diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 210c47a90..000000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -Dockerfile diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1003e5853..000000000 --- a/.gitattributes +++ /dev/null @@ -1,8 +0,0 @@ -# This file has been retrieved from angular repository. - -# Auto detect text files and perform LF normalization -* text=auto - -# JS and TS files must always use LF for tools to work -*.js eol=lf -*.ts eol=lf diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 14e5e7e4f..000000000 --- a/.gitignore +++ /dev/null @@ -1,49 +0,0 @@ -# Specifies intentionally untracked files to ignore when using Git -# http://git-scm.com/docs/gitignore - -*~ -*.sw[mnpcod] -*.log -*.tmp -*.tmp.* -log.txt -*.sublime-project -*.sublime-workspace -.vscode/ -npm-debug.log* - -.idea/ -.sourcemaps/ -.sass-cache/ -.tmp/ -.versions/ -coverage/ -dist/ -node_modules/ -tmp/ -temp/ -platforms/ -/plugins/ -/plugins/android.json -/plugins/ios.json -resources/android/icon -resources/android/splash -resources/ios/icon -resources/ios/splash -resources/windows/icon -resources/windows/splash -config.xml -www/ -!www/README.md -$RECYCLE.BIN/ - -.DS_Store -Thumbs.db -UserInterfaceState.xcuserstate - -e2e/build -/desktop/* -!/desktop/assets/ -!/desktop/electron.js -src/configconstants.ts -.moodleapp-dev-config diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 4fd021952..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dde8041a3..000000000 --- a/.travis.yml +++ /dev/null @@ -1,125 +0,0 @@ -os: linux -dist: bionic -group: edge -language: node_js -node_js: 11 -php: 7.1 - -android: - components: - - tools - - platform-tools - - build-tools-29.0.3 - - android-28 - - extra-google-google_play_services - - extra-google-m2repository - - extra-android-m2repository - -git: - depth: 3 - -before_cache: - - rm -rf $HOME/.cache/electron-builder/wine - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - -cache: - directories: - - $HOME/.npm - - $HOME/.cache/electron - - $HOME/.cache/electron-builder - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - - $HOME/.android/build-cache - -before_script: - - if [ "$TRAVIS_OS_NAME" != 'windows' ] ; then npm install npm@latest -g ; fi - - gulp - -jobs: - include: - - stage: check - if: NOT branch =~ /(master|integration|desktop)$/ AND env(DEPLOY) IS blank - os: linux - script: npm run build --bailOnLintError true --typeCheckOnLint true - - stage: mirror - if: branch IN (master, integration, desktop) AND repo = moodlehq/moodleapp AND type != cron - os: linux - script: scripts/mirror.sh - - stage: prepare - if: branch =~ /(master|^integration)$/ AND env(PREPARE) IS NOT blank AND env(PREPARE) = 1 AND type != cron - os: linux - script: scripts/aot.sh - - stage: build - name: "Build Android" - if: env(DEPLOY) IS NOT blank AND ((env(DEPLOY) = 1 AND branch != desktop) OR (env(DEPLOY) IN (2,3) AND tag IS NOT blank)) - os: linux - dist: trusty - group: edge - language: android - env: - - BUILD_PLATFORM='android' - before_install: - - nvm install 11 - - node --version - - npm --version - - nvm --version - - npm ci - - npm install -g gulp - script: scripts/aot.sh - - stage: build - name: "Build iOS" - if: env(DEPLOY) IS NOT blank AND ((env(DEPLOY) = 1 AND branch != desktop) OR (env(DEPLOY) IN (2,3) AND tag IS NOT blank)) - os: osx - osx_image: xcode12u - env: - - BUILD_PLATFORM='ios' - script: scripts/aot.sh - - stage: build - name: "Build Linux" - if: env(DEPLOY) IS NOT blank AND ((env(DEPLOY) = 1 AND branch = desktop) OR (env(DEPLOY) = 3 AND tag IS NOT blank)) - os: linux - env: - - ELECTRON_CACHE=$HOME/.cache/electron - - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder - - BUILD_PLATFORM='linux' - script: scripts/aot.sh - - stage: build - name: "Build MacOS" - if: env(DEPLOY) IS NOT blank AND ((env(DEPLOY) = 1 AND branch = desktop) OR (env(DEPLOY) = 3 AND tag IS NOT blank)) - os: osx - osx_image: xcode12u - env: - - ELECTRON_CACHE=$HOME/.cache/electron - - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder - - BUILD_PLATFORM='osx' - script: scripts/aot.sh - - stage: build - name: "Build Windows" - if: env(DEPLOY) IS NOT blank AND ((env(DEPLOY) = 1 AND branch = desktop) OR (env(DEPLOY) = 3 AND tag IS NOT blank)) - os: windows - env: - - ELECTRON_CACHE=$HOME/.cache/electron - - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder - - ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=true - - DEBUG=electron-windows-store - - BUILD_PLATFORM='windows' - script: scripts/aot.sh - - stage: test - name: "End to end tests (mod_forum, mod_messages and mod_comments)" - services: - - docker - if: type = cron - script: scripts/test_e2e.sh "@app&&@mod_forum" "@app&&@mod_messages" "@app&&@mod_comments" - - stage: test - name: "End to end tests (mod_data, mod_survey, mod_course, core_course and mod_courses)" - services: - - docker - if: type = cron - script: scripts/test_e2e.sh "@app&&@mod_data" "@app&&@mod_survey" "@app&&@mod_course" "@app&&@core_course" "@app&&@mod_courses" - - stage: test - name: "End to end tests (others)" - services: - - docker - if: type = cron - script: scripts/test_e2e.sh "@app&&~@mod_forum&&~@mod_messages&&~@mod_comments&&~@mod_data&&~@mod_survey&&~@mod_course&&~@core_course&&~@mod_courses" diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 991bd3f7f..000000000 --- a/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# This image is based on the fat node 11 image. -# We require fat images as neither alpine, or slim, include git binaries. -FROM node:11 - -# Port 8100 for ionic dev server. -EXPOSE 8100 - -# Port 35729 is the live-reload server. -EXPOSE 35729 - -# Port 53703 is the Chrome dev logger port. -EXPOSE 53703 - -WORKDIR /app - -COPY . /app - -# 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 ["npm", "run", "ionic:serve"] diff --git a/GoogleService-Info.plist b/GoogleService-Info.plist deleted file mode 100644 index f6d1bd44c..000000000 --- a/GoogleService-Info.plist +++ /dev/null @@ -1,40 +0,0 @@ - - - - - AD_UNIT_ID_FOR_BANNER_TEST - - AD_UNIT_ID_FOR_INTERSTITIAL_TEST - - CLIENT_ID - - REVERSED_CLIENT_ID - - API_KEY - - GCM_SENDER_ID - - PLIST_VERSION - 1 - BUNDLE_ID - com.moodle.moodlemobile - PROJECT_ID - moodlemobile-push - STORAGE_BUCKET - - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - - DATABASE_URL - - - \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 7a4a3ea24..000000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/MainActivity.java b/MainActivity.java deleted file mode 100755 index 92a34c3c0..000000000 --- a/MainActivity.java +++ /dev/null @@ -1,56 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Based on the template node_modules/cordova-android/bin/templates/project/Activity.java - -package com.moodle.moodlemobile; - -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import org.apache.cordova.*; - -public class MainActivity extends CordovaActivity -{ - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - // enable Cordova apps to be started in the background - Bundle extras = getIntent().getExtras(); - if (extras != null && extras.getBoolean("cdvStartInBackground", false)) { - moveTaskToBack(true); - } - - // Set by in config.xml - loadUrl(launchUrl); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - // Forward back key events to the web view. - if (this.appView != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - View webview = this.appView.getView(); - - if (webview != null) { - webview.dispatchKeyEvent(event); - } - - return true; - } - - return super.dispatchKeyEvent(event); - } -} diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 64a4824f2..000000000 --- a/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -(C) Copyright 2015 Moodle Pty Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/PACKAGE_PROBLEMS.md b/PACKAGE_PROBLEMS.md deleted file mode 100644 index 03663b6af..000000000 --- a/PACKAGE_PROBLEMS.md +++ /dev/null @@ -1,17 +0,0 @@ -Package updates known problems -================= - -@ionic/app-scripts 3.2.3 shows error Cannot find type definition file for '@types'. on ngc build. - -com-darryncampbell-cordova-plugin-intent 2.0.0 onwards needs Android X Support. Unsupported on PGB. - -typescript is needed to be less than 2.7 for @angular/compiler-cli - -cordova-plugin-ionic-keyboard has problems on greater versions than 2.1.3 - -jszip has problems with "lie" dependency on greater versions than 3.1 - -promise.prototype.finally has problems on greater versions than 3.1 - -cordova-ios: should remain on 5.1 because of: https://github.com/apache/cordova-ios/pull/801 - diff --git a/config.xml b/config.xml deleted file mode 100644 index b5c78097b..000000000 --- a/config.xml +++ /dev/null @@ -1,648 +0,0 @@ - - - Moodle - Moodle official app - Moodle Mobile team - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - We need your location so you can attach it as part of your submissions. - - - We need your location so you can attach it as part of your submissions. - - - We need camera access to take pictures so you can attach them as part of your submissions. - - - We need microphone access to record sounds so you can attach them as part of your submissions. - - - We need photo library access to get pictures from there so you can attach them as part of your submissions. - - - - - - 3.9.3-dev - - - YES - - - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - - - - - - - CFBundleTypeName - Unknown File - LSHandlerRank - Alternate - LSItemContentTypes - - public.calendar-event - public.database - public.executable - public.data - public.content - public.item - - - - CFBundleTypeName - Video - LSHandlerRank - Alternate - LSItemContentTypes - - public.video - - - - CFBundleTypeName - Image - LSHandlerRank - Alternate - LSItemContentTypes - - public.image - - - - CFBundleTypeName - Web Archive - LSHandlerRank - Alternate - LSItemContentTypes - - com.apple.webarchive - - - - CFBundleTypeName - iWork Keynote - LSHandlerRank - Alternate - LSItemContentTypes - - com.apple.keynote.key - com.apple.iwork.keynote.key - com.apple.iwork.keynote.kth - - - - CFBundleTypeName - iWork Numbers - LSHandlerRank - Alternate - LSItemContentTypes - - com.apple.numbers.numbers - com.apple.iwork.numbers.numbers - com.apple.iwork.numbers.template - - - - CFBundleTypeName - iWork Pages - LSHandlerRank - Alternate - LSItemContentTypes - - com.apple.page.pages - com.apple.iwork.pages.pages - com.apple.iwork.pages.template - - - - CFBundleTypeName - OpenDocument Spreadsheet - LSHandlerRank - Alternate - LSItemContentTypes - - org.oasis.opendocument.spreadsheet - - - - CFBundleTypeName - OpenDocument Presentation - LSHandlerRank - Alternate - LSItemContentTypes - - org.oasis.opendocument.presentation - - - - CFBundleTypeName - OpenDocument Text - LSHandlerRank - Alternate - LSItemContentTypes - - org.oasis.opendocument.text - - - - CFBundleTypeName - Folder - LSHandlerRank - Alternate - LSItemContentTypes - - public.folder - - - - CFBundleTypeName - Audio - LSHandlerRank - Alternate - LSItemContentTypes - - public.audio - public.mp3 - public.mpeg-4-audio - com.apple.protected-​mpeg-4-audio - public.aifc-audio - com.apple.coreaudio-​format - public.aiff-audio - - - - CFBundleTypeName - Movie - LSHandlerRank - Alternate - LSItemContentTypes - - public.movie - public.3gpp2 - public.3gpp - public.mpeg - com.apple.quicktime-movie - public.mpeg-4 - - - - CFBundleTypeName - GIF image - LSHandlerRank - Alternate - LSItemContentTypes - - com.compuserve.gif - - - - CFBundleTypeName - PNG image - LSHandlerRank - Alternate - LSItemContentTypes - - public.png - - - - CFBundleTypeName - TIFF image - LSHandlerRank - Alternate - LSItemContentTypes - - public.tiff - - - - CFBundleTypeName - JPEG image - LSHandlerRank - Alternate - LSItemContentTypes - - public.jpeg - - - - CFBundleTypeName - XML - LSHandlerRank - Alternate - LSItemContentTypes - - public.xml - - - - CFBundleTypeName - HTML - LSHandlerRank - Alternate - LSItemContentTypes - - public.html - public.xhtml - - - - CFBundleTypeName - Rich Text - LSHandlerRank - Alternate - LSItemContentTypes - - public.rtf - com.apple.rtfd - com.apple.flat-rtfd - - - - CFBundleTypeName - Text - LSHandlerRank - Alternate - LSItemContentTypes - - public.text - public.plain-text - public.utf8-plain-text - public.utf16-external-plain-​text - public.utf16-plain-text - com.apple.traditional-mac-​plain-text - public.source-code - public.c-source - public.objective-c-source - public.c-plus-plus-source - public.objective-c-plus-​plus-source - public.c-header - public.c-plus-plus-header - com.sun.java-source - public.script - public.shell-script - - - - CFBundleTypeExtensions - - zip - zipx - - CFBundleTypeName - Zip archive - LSHandlerRank - Alternate - LSItemContentTypes - - public.zip-archive - public.archive - com.pkware.zip-archive - com.pkware.zipx-archive - - - - CFBundleTypeExtensions - - rar - RAR - - CFBundleTypeName - Rar archive - LSHandlerRank - Alternate - LSItemContentTypes - - com.rarlab.rar-archive - public.archive - - - - CFBundleTypeExtensions - - 7z - 7Z - - CFBundleTypeName - 7z archive - LSHandlerRank - Alternate - LSItemContentTypes - - org.7-zip.7-zip-archive - public.archive - - - - CFBundleTypeName - Waveform audio - LSHandlerRank - Alternate - LSItemContentTypes - - com.microsoft.waveform-​audio - - - - CFBundleTypeName - Windows icon image - LSHandlerRank - Alternate - LSItemContentTypes - - com.microsoft.ico - com.apple.icns - - - - CFBundleTypeName - Windows bitmap image - LSHandlerRank - Alternate - LSItemContentTypes - - com.microsoft.bmp - - - - CFBundleTypeName - Microsoft PowerPoint - LSHandlerRank - Alternate - LSItemContentTypes - - com.microsoft.powerpoint.​ppt - org.openxmlformats.presentationml.presentation - - - - CFBundleTypeName - Microsoft Excel - LSHandlerRank - Alternate - LSItemContentTypes - - org.openxmlformats.spreadsheetml.sheet - com.microsoft.excel.xls - - - - CFBundleTypeName - Microsoft Word - LSHandlerRank - Alternate - LSItemContentTypes - - com.microsoft.word.doc - com.microsoft.word.wordml - org.openxmlformats.wordprocessingml.document - - - - CFBundleTypeName - PDF - LSHandlerRank - Alternate - LSItemContentTypes - - com.adobe.pdf - - - - - - diff --git a/config/copy.config.js b/config/copy.config.js deleted file mode 100644 index cf81edd35..000000000 --- a/config/copy.config.js +++ /dev/null @@ -1,48 +0,0 @@ -// New copy task for font files and config.json. -module.exports = { - // Override Ionic copyFonts task to exclude Roboto and Noto fonts. - copyFonts: { - src: ['{{ROOT}}/node_modules/ionicons/dist/fonts/**/*'], - dest: '{{WWW}}/assets/fonts' - }, - copyFontAwesome: { - src: ['{{ROOT}}/node_modules/font-awesome/fonts/**/*'], - dest: '{{WWW}}/assets/fonts' - }, - copyConfig: { - src: ['{{ROOT}}/src/config.json'], - dest: '{{WWW}}/' - }, - copyMathJaxMain: { - src: ['{{ROOT}}/node_modules/mathjax/MathJax.js'], - dest: '{{WWW}}/lib/mathjax' - }, - copyMathJaxExtensions: { - src: ['{{ROOT}}/node_modules/mathjax/extensions/**/*'], - dest: '{{WWW}}/lib/mathjax/extensions' - }, - copyMathJaxElement: { - src: ['{{ROOT}}/node_modules/mathjax/jax/element/**/*'], - dest: '{{WWW}}/lib/mathjax/jax/element' - }, - copyMathJaxInput: { - src: ['{{ROOT}}/node_modules/mathjax/jax/input/**/*'], - dest: '{{WWW}}/lib/mathjax/jax/input' - }, - copyMathJaxSVGOutput: { - src: ['{{ROOT}}/node_modules/mathjax/jax/output/SVG/**/*'], - dest: '{{WWW}}/lib/mathjax/jax/output/SVG' - }, - copyMathJaxPreviewHTMLOutput: { - src: ['{{ROOT}}/node_modules/mathjax/jax/output/PreviewHTML/**/*'], - dest: '{{WWW}}/lib/mathjax/jax/output/PreviewHTML' - }, - copyMathJaxLocalization: { - src: ['{{ROOT}}/node_modules/mathjax/localization/**/*'], - dest: '{{WWW}}/lib/mathjax/localization' - }, - copyH5P: { - src: ['{{ROOT}}/src/core/h5p/assets/**/*'], - dest: '{{WWW}}/h5p/' - }, -}; diff --git a/config/sass.config.js b/config/sass.config.js deleted file mode 100644 index e13f6ff2e..000000000 --- a/config/sass.config.js +++ /dev/null @@ -1,15 +0,0 @@ -// Adding Font Awesome to includePaths -module.exports = { - includePaths: [ - 'node_modules/ionic-angular/themes', - 'node_modules/ionicons/dist/scss', - 'node_modules/ionic-angular/fonts', - 'node_modules/font-awesome/scss' - ], - includeFiles: [ - /\.(s(c|a)ss)$/i - ], - excludeFiles: [ - /\.(wp).(scss)$/i - ] -}; \ No newline at end of file diff --git a/config/uglifyjs.config.js b/config/uglifyjs.config.js deleted file mode 100644 index 38a263a29..000000000 --- a/config/uglifyjs.config.js +++ /dev/null @@ -1,19 +0,0 @@ -// Check https://github.com/mishoo/UglifyJS2/tree/harmony#minify-options-structure -module.exports = { - /** - * mangle: uglify 2's mangle option - */ - mangle: { - keep_classnames: true, - keep_fnames: true - }, - /** - * compress: uglify 2's compress option - */ - compress: { - toplevel: true, - pure_getters: true - }, - keep_classnames: true, - keep_fnames: true -} \ No newline at end of file diff --git a/config/webpack.config.js b/config/webpack.config.js deleted file mode 100644 index 1c6ae7ca5..000000000 --- a/config/webpack.config.js +++ /dev/null @@ -1,43 +0,0 @@ -const { resolve } = require('path'); -const webpackMerge = require('webpack-merge'); -const { dev, prod } = require('@ionic/app-scripts/config/webpack.config'); - -const customConfig = { - resolve: { - alias: { - '@addon': resolve('./src/addon'), - '@app': resolve('./src/app'), - '@classes': resolve('./src/classes'), - '@core': resolve('./src/core'), - '@providers': resolve('./src/providers'), - '@components': resolve('./src/components'), - '@directives': resolve('./src/directives'), - '@pipes': resolve('./src/pipes'), - '@singletons': resolve('./src/singletons'), - } - }, - externals: [ - (function () { - var IGNORES = ["fs","child_process","electron","path","assert","cluster","crypto","dns","domain","events","http","https","net","os","process","punycode","querystring","readline","repl","stream","string_decoder","tls","tty","dgram","url","util","v8","vm","zlib"]; - return function (context, request, callback) { - if (IGNORES.indexOf(request) >= 0) { - return callback(null, "require('" + request + "')"); - } - return callback(); - }; - })() - ], - module: { - loaders: [ - { - test: /\.node$/, - use: 'node-loader' - } - ] - } -}; - -module.exports = { - dev: webpackMerge(dev, customConfig), - prod: webpackMerge(prod, customConfig), -} diff --git a/desktop/assets/mac/child.plist b/desktop/assets/mac/child.plist deleted file mode 100644 index ab867b6fc..000000000 --- a/desktop/assets/mac/child.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.inherit - - - \ No newline at end of file diff --git a/desktop/assets/mac/loginhelper.plist b/desktop/assets/mac/loginhelper.plist deleted file mode 100644 index 8e31f755a..000000000 --- a/desktop/assets/mac/loginhelper.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/desktop/assets/mac/parent.plist b/desktop/assets/mac/parent.plist deleted file mode 100644 index 9e39db337..000000000 --- a/desktop/assets/mac/parent.plist +++ /dev/null @@ -1,20 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.application-groups - - 2NU57U5PAW.com.moodle.moodledesktop - - com.apple.security.network.client - - com.apple.security.device.camera - - com.apple.security.files.user-selected.read-only - - com.apple.security.device.audio-input - - - \ No newline at end of file diff --git a/desktop/assets/mac/sign.sh b/desktop/assets/mac/sign.sh deleted file mode 100755 index ae1523be2..000000000 --- a/desktop/assets/mac/sign.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# -# Script to sign macOSX pkg. -# https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide -# - -# Name of your app. -APP="Moodle Desktop" -# The name of certificates you requested. -APP_KEY="3rd Party Mac Developer Application: Moodle Pty Ltd (2NU57U5PAW)" -INSTALLER_KEY="3rd Party Mac Developer Installer: Moodle Pty Ltd (2NU57U5PAW)" - - -BASEPATH="desktop/dist/mas" -# The path of your app to sign. -APP_PATH="${BASEPATH}/${APP}.app" -# The path to the location you want to put the signed package. -RESULT_PATH="${BASEPATH}/${APP}.pkg" - -# The path of your plist files. -CHILD_PLIST="desktop/assets/mac/child.plist" -PARENT_PLIST="desktop/assets/mac/parent.plist" -LOGINHELPER_PLIST="desktop/assets/mac/loginhelper.plist" - -FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks" - -codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework" -codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib" -codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libnode.dylib" -codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework" -codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/Contents/MacOS/$APP Helper" -codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/" -codesign -s "$APP_KEY" -f --entitlements "$LOGINHELPER_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/Contents/MacOS/$APP Login Helper" -codesign -s "$APP_KEY" -f --entitlements "$LOGINHELPER_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/" -codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$APP_PATH/Contents/MacOS/$APP" -codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH" - -productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH" diff --git a/desktop/assets/windows/AppXManifest.xml b/desktop/assets/windows/AppXManifest.xml deleted file mode 100644 index c74afc756..000000000 --- a/desktop/assets/windows/AppXManifest.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Moodle Desktop - Moodle Pty Ltd. - The official app for Moodle. - assets\StoreLogo.png - - - - - - - - - - - - - - - - - - - Moodle Mobile URI Scheme - - - - - - diff --git a/desktop/electron.js b/desktop/electron.js deleted file mode 100644 index a526fa466..000000000 --- a/desktop/electron.js +++ /dev/null @@ -1,265 +0,0 @@ - -// dialog isn't used, but not requiring it throws an error. -const {app, BrowserWindow, ipcMain, shell, dialog, Menu} = require('electron'); -const path = require('path'); -const url = require('url'); -const fs = require('fs'); -const os = require('os'); -const userAgent = 'MoodleMobile'; -const isMac = os.platform().indexOf('darwin') != -1; - -// Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the JavaScript object is garbage collected. -let mainWindow, - appName = 'Moodle Desktop', // Default value. - isReady = false, - configRead = false; - -function createWindow() { - // Create the browser window. - var width = 800, - height = 600; - - const screen = require('electron').screen; - if (screen) { - const display = screen.getPrimaryDisplay(); - if (display && display.workArea) { - width = display.workArea.width || width; - height = display.workArea.height || height; - } - } - - const options = { - width: width, - height: height, - minWidth: 400, - minHeight: 400, - textAreasAreResizable: false, - plugins: true, - show: false // Don't show it until it's ready to prevent showing a blank screen. - }; - - if (os.platform().indexOf('linux') === 0) { - options.icon = path.join(__dirname, '/../www/assets/icon/icon.png'); - } - - mainWindow = new BrowserWindow(options); - - // And load the index.html of the app. - mainWindow.loadURL(url.format({ - pathname: path.join(__dirname, '/../www/index.html'), - protocol: 'file:', - slashes: true - })); - - mainWindow.once('ready-to-show', () => { - mainWindow.show(); - mainWindow.maximize(); - }); - - // Emitted when the window is closed. - mainWindow.on('closed', () => { - // Dereference the window object. - mainWindow = null - }); - - mainWindow.on('focus', () => { - mainWindow.webContents.send('coreAppFocused'); // Send an event to the main window. - }); - - // Append some text to the user agent. - mainWindow.webContents.setUserAgent(mainWindow.webContents.getUserAgent() + ' ' + userAgent); - - // Add shortcut to open dev tools: Cmd + Option + I in MacOS, Ctrl + Shift + I in Windows/Linux. - mainWindow.webContents.on('before-input-event', function(e, input) { - if (input.type == 'keyDown' && !input.isAutoRepeat && input.code == 'KeyI' && - ((isMac && input.alt && input.meta) || (!isMac && input.shift && input.control))) { - mainWindow.webContents.toggleDevTools(); - } - }, true) -} - -// Make sure that only a single instance of the app is running. -// For some reason, gotTheLock is always false in signed Mac apps so we should ingore it. -// See https://github.com/electron/electron/issues/15958 -var gotTheLock = app.requestSingleInstanceLock(); - -if (!gotTheLock && !isMac) { - // It's not the main instance of the app, kill it. - app.exit(); - return; -} - -app.on('second-instance', (event, commandLine, workingDirectory) => { - // Another instance was launched. If it was launched with a URL, it should be in the second param. - if (commandLine && commandLine[1]) { - appLaunched(commandLine[1]); - } else { - focusApp(); - } -}); - -// This method will be called when Electron has finished initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.on('ready', function() { - isReady = true; - - createWindow(); - - if (configRead) { - setAppMenu(); - } -}); - -// Quit when all windows are closed. -app.on('window-all-closed', () => { - app.exit(); -}); - -app.on('activate', () => { - // On macOS it's common to re-create a window in the app when the dock icon is clicked and there are no other windows open. - if (mainWindow === null) { - createWindow(); - } -}); - -// Read the config.json file. -fs.readFile(path.join(__dirname, 'config.json'), 'utf8', (err, data) => { - configRead = true; - - // Default values. - var ssoScheme = 'moodlemobile', - appId = 'com.moodle.moodlemobile'; - - if (!err) { - try { - data = JSON.parse(data); - ssoScheme = data.customurlscheme; - appName = data.desktopappname; - appId = data.app_id; - } catch(ex) {} - } - - // Set default protocol (custom URL scheme). - app.setAsDefaultProtocolClient(ssoScheme); - - // Fix notifications in Windows. - app.setAppUserModelId(appId); - - if (isReady) { - setAppMenu(); - } -}); - -// Listen for open-url events (Mac OS only). -app.on('open-url', (event, url) => { - event.preventDefault(); - appLaunched(url); -}); - -function appLaunched(url) { - // App was launched again with a URL. Focus the main window and send an event to treat the URL. - if (mainWindow) { - focusApp(); - mainWindow.webContents.send('coreAppLaunched', url); // Send an event to the main window. - } -} - -function focusApp() { - if (mainWindow) { - if (mainWindow.isMinimized()) { - mainWindow.restore(); - } - mainWindow.focus(); - } -} - -// Listen for events sent by the renderer processes (windows). -ipcMain.on('openItem', (event, path) => { - var result; - - // Add file:// protocol if it isn't there. - if (path.indexOf('file://') == -1) { - path = 'file://' + path; - } - - if (os.platform().indexOf('darwin') > -1) { - // Use openExternal in MacOS because openItem doesn't work in sandboxed apps. - // https://github.com/electron/electron/issues/9005 - result = shell.openExternal(path); - } else { - result = shell.openItem(path); - } - - if (!result) { - // Cannot open file, probably no app to handle it. Open the folder. - result = shell.showItemInFolder(path.replace('file://', '')); - } - - event.returnValue = result; -}); - -ipcMain.on('closeSecondaryWindows', () => { - const windows = BrowserWindow.getAllWindows(); - for (let i = 0; i < windows.length; i++) { - const win = windows[i]; - if (!win.isDestroyed() && (!mainWindow || win.id != mainWindow.id)) { - win.close(); - } - } -}); - -ipcMain.on('focusApp', focusApp); - -// Configure the app's menu. -function setAppMenu() { - let menuTemplate = [ - { - label: appName, - role: 'window', - submenu: [ - { - label: 'Quit', - accelerator: 'CmdorCtrl+Q', - role: 'close' - } - ] - }, - { - label: 'Edit', - submenu: [ - { - label: 'Cut', - role: 'cut' - }, - { - label: 'Copy', - role: 'copy' - }, - { - label: 'Paste', - role: 'paste' - }, - { - label: 'Select All', - role: 'selectall' - } - ] - }, - { - label: 'Help', - role: 'help', - submenu: [ - { - label: 'Docs', - accelerator: 'CmdOrCtrl+H', - click() { - shell.openExternal('https://docs.moodle.org/en/Moodle_Mobile'); - } - } - ] - } - ]; - - Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate)); -} diff --git a/google-services.json b/google-services.json deleted file mode 100644 index c1c3ad8bf..000000000 --- a/google-services.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "project_info": { - "project_number": "", - "firebase_url": "", - "project_id": "", - "storage_bucket": "" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:111111111111:android:1111111111111111", - "android_client_info": { - "package_name": "com.moodle.moodlemobile" - } - }, - "oauth_client": [ - { - "client_id": "", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "" - }, - { - "current_key": "" - } - ], - "services": { - "analytics_service": { - "status": 1 - }, - "appinvite_service": { - "status": 1, - "other_platform_oauth_client": [] - }, - "ads_service": { - "status": 2 - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/gulp/dev-config.js b/gulp/dev-config.js deleted file mode 100644 index b270bf51f..000000000 --- a/gulp/dev-config.js +++ /dev/null @@ -1,69 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const fs = require('fs'); - -const DEV_CONFIG_FILE = '.moodleapp-dev-config'; - -/** - * Class to read and write dev-config data from a file. - */ -class DevConfig { - - constructor() { - this.loadFileData(); - } - - /** - * Get a setting. - * - * @param name Name of the setting to get. - * @param defaultValue Value to use if not found. - */ - get(name, defaultValue) { - return typeof this.config[name] != 'undefined' ? this.config[name] : defaultValue; - } - - /** - * Load file data to memory. - */ - loadFileData() { - if (!fs.existsSync(DEV_CONFIG_FILE)) { - this.config = {}; - - return; - } - - try { - this.config = JSON.parse(fs.readFileSync(DEV_CONFIG_FILE)); - } catch (error) { - console.error('Error reading dev config file.', error); - this.config = {}; - } - } - - /** - * Save some settings. - * - * @param settings Object with the settings to save. - */ - save(settings) { - this.config = Object.assign(this.config, settings); - - // Save the data in the dev file. - fs.writeFileSync(DEV_CONFIG_FILE, JSON.stringify(this.config, null, 4)); - } -} - -module.exports = new DevConfig(); diff --git a/gulp/git.js b/gulp/git.js deleted file mode 100644 index f3f66298a..000000000 --- a/gulp/git.js +++ /dev/null @@ -1,237 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const exec = require('child_process').exec; -const fs = require('fs'); -const DevConfig = require('./dev-config'); -const Utils = require('./utils'); - -/** - * Class to run git commands. - */ -class Git { - - /** - * Create a patch. - * - * @param range Show only commits in the specified revision range. - * @param saveTo Path to the file to save the patch to. If not defined, the patch contents will be returned. - * @return Promise resolved when done. If saveTo not provided, it will return the patch contents. - */ - createPatch(range, saveTo) { - return new Promise((resolve, reject) => { - exec(`git format-patch ${range} --stdout`, (err, result) => { - if (err) { - reject(err || 'Cannot create patch.'); - return; - } - - if (!saveTo) { - resolve(result); - return; - } - - // Save it to a file. - const directory = saveTo.substring(0, saveTo.lastIndexOf('/')); - if (directory && directory != '.' && directory != '..' && !fs.existsSync(directory)) { - fs.mkdirSync(directory); - } - fs.writeFileSync(saveTo, result); - - resolve(); - }); - }); - } - - /** - * Get current branch. - * - * @return Promise resolved with the branch name. - */ - getCurrentBranch() { - return new Promise((resolve, reject) => { - exec('git branch --show-current', (err, branch) => { - if (branch) { - resolve(branch.replace('\n', '')); - } else { - reject (err || 'Current branch not found.'); - } - }); - }); - } - - /** - * Get the HEAD commit for a certain branch. - * - * @param branch Name of the branch. - * @param branchData Parsed branch data. If not provided it will be calculated. - * @return HEAD commit. - */ - async getHeadCommit(branch, branchData) { - if (!branchData) { - // Parse the branch to get the project and issue number. - branchData = Utils.parseBranch(branch); - } - - // Loop over the last commits to find the first commit messages that doesn't belong to the issue. - const commitsString = await this.log(50, branch, '%s_____%H'); - const commits = commitsString.split('\n'); - commits.pop(); // Remove last element, it's an empty string. - - for (let i = 0; i < commits.length; i++) { - const commit = commits[i]; - const match = Utils.getIssueFromCommitMessage(commit) == branchData.issue; - - if (i === 0 && !match) { - // Most recent commit doesn't belong to the issue. Stop looking. - break; - } - - if (!match) { - // The commit does not match any more, we found it! - return commit.split('_____')[1]; - } - } - - // Couldn't find the commit using the commit names, get the last commit in the integration branch. - const remote = DevConfig.get('upstreamRemote', 'origin'); - console.log(`Head commit not found using commit messages. Get last commit from ${remote}/integration`); - const hashes = await this.hashes(1, `${remote}/integration`); - - return hashes[0]; - } - - /** - * Get the URL of a certain remote. - * - * @param remote Remote name. - * @return Promise resolved with the remote URL. - */ - getRemoteUrl(remote) { - return new Promise((resolve, reject) => { - exec(`git remote get-url ${remote}`, (err, url) => { - if (url) { - resolve(url.replace('\n', '')); - } else { - reject (err || 'Remote not found.'); - } - }); - }); - } - - /** - * Return the latest hashes from git log. - * - * @param count Number of commits to display. - * @param range Show only commits in the specified revision range. - * @param format Pretty-print the contents of the commit logs in a given format. - * @return Promise resolved with the list of hashes. - */ - async hashes(count, range, format) { - format = format || '%H'; - - const hashList = await this.log(count, range, format); - - const hashes = hashList.split('\n'); - hashes.pop(); // Remove last element, it's an empty string. - - return hashes; - } - - /** - * Calls the log command and returns the raw output. - * - * @param count Number of commits to display. - * @param range Show only commits in the specified revision range. - * @param format Pretty-print the contents of the commit logs in a given format. - * @param path Show only commits that are enough to explain how the files that match the specified paths came to be. - * @return Promise resolved with the result. - */ - log(count, range, format, path) { - if (typeof count == 'undefined') { - count = 10; - } - - let command = 'git log'; - - if (count > 0) { - command += ` -n ${count} `; - } - if (format) { - command += ` --format=${format} `; - } - if (range){ - command += ` ${range} `; - } - if (path) { - command += ` -- ${path}`; - } - - return new Promise((resolve, reject) => { - exec(command, (err, result, stderr) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - }); - } - - /** - * Return the latest titles of the commit messages. - * - * @param count Number of commits to display. - * @param range Show only commits in the specified revision range. - * @param path Show only commits that are enough to explain how the files that match the specified paths came to be. - * @return Promise resolved with the list of titles. - */ - async messages(count, range, path) { - count = typeof count != 'undefined' ? count : 10; - - const messageList = await this.log(count, range, '%s', path); - - const messages = messageList.split('\n'); - messages.pop(); // Remove last element, it's an empty string. - - return messages; - } - - /** - * Push a branch. - * - * @param remote Remote to use. - * @param branch Branch to push. - * @param force Whether to force the push. - * @return Promise resolved when done. - */ - push(remote, branch, force) { - return new Promise((resolve, reject) => { - let command = `git push ${remote} ${branch}`; - if (force) { - command += ' -f'; - } - - exec(command, (err, result, stderr) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - } -} - -module.exports = new Git(); diff --git a/gulp/jira.js b/gulp/jira.js deleted file mode 100644 index 90aeaa1c7..000000000 --- a/gulp/jira.js +++ /dev/null @@ -1,475 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const exec = require('child_process').exec; -const https = require('https'); -const inquirer = require('inquirer'); -const fs = require('fs'); -const request = require('request'); // This lib is deprecated, but it was the only way I found to make upload files work. -const DevConfig = require('./dev-config'); -const Git = require('./git'); -const Url = require('./url'); -const Utils = require('./utils'); - -const apiVersion = 2; - -/** - * Class to interact with Jira. - */ -class Jira { - - /** - * Ask the password to the user. - * - * @return Promise resolved with the password. - */ - async askPassword() { - const data = await inquirer.prompt([ - { - type: 'password', - name: 'password', - message: `Please enter the password for the username ${this.username}.`, - }, - ]); - - return data.password; - } - - /** - * Ask the user the tracker data. - * - * @return Promise resolved with the data, rejected if cannot get. - */ - async askTrackerData() { - const data = await inquirer.prompt([ - { - type: 'input', - name: 'url', - message: 'Please enter the tracker URL.', - default: 'https://tracker.moodle.org/', - }, - { - type: 'input', - name: 'username', - message: 'Please enter your tracker username.', - }, - ]); - - DevConfig.save({ - 'tracker.url': data.url, - 'tracker.username': data.username, - }); - - return data; - } - - /** - * Build URL to perform requests to Jira. - * - * @param uri URI to add the the Jira URL. - * @return URL. - */ - buildRequestUrl(uri) { - return Utils.concatenatePaths([this.url, this.uri, '/rest/api/', apiVersion, uri]); - } - - /** - * Delete an attachment. - * - * @param attachmentId Attachment ID. - * @return Promise resolved when done. - */ - async deleteAttachment(attachmentId) { - const response = await this.request(`attachment/${attachmentId}`, 'DELETE'); - - if (response.status != 204) { - throw new Error('Could not delete the attachment'); - } - } - - /** - * Load the issue info from jira server using a REST API call. - * - * @param key Key to identify the issue. E.g. MOBILE-1234. - * @param fields Fields to get. - * @return Promise resolved with the issue data. - */ - async getIssue(key, fields) { - fields = fields || '*all,-comment'; - - await this.init(); // Initialize data if needed. - - const response = await this.request(`issue/${key}`, 'GET', {'fields': fields, 'expand': 'names'}); - - if (response.status == 404) { - throw new Error('Issue could not be found.'); - } else if (response.status != 200) { - throw new Error('The tracker is not available.') - } - - const issue = response.data; - issue.named = {}; - - // Populate the named fields in a separate key. Allows us to easily find them without knowing the field ID. - const nameList = issue.names || {}; - for (const fieldKey in issue.fields) { - if (nameList[fieldKey]) { - issue.named[nameList[fieldKey]] = issue.fields[fieldKey]; - } - } - - return issue - } - - /** - * Load the version info from the jira server using a rest api call. - * - * @return Promise resolved when done. - */ - async getServerInfo() { - const response = await this.request('serverInfo'); - - if (response.status != 200) { - throw new Error(`Unexpected response code: ${response.status}`, response); - } - - this.version = response.data; - } - - /** - * Get tracker data to push an issue. - * - * @return Promise resolved with the data. - */ - async getTrackerData() { - // Check dev-config file first. - let data = this.getTrackerDataFromDevConfig(); - - if (data) { - console.log('Using tracker data from dev-config file'); - return data; - } - - // Try to use mdk now. - try { - data = await this.getTrackerDataFromMdk(); - - console.log('Using tracker data from mdk'); - - return data; - } catch (error) { - // MDK not available or not configured. Ask for the data. - const data = await this.askTrackerData(); - - data.fromInput = true; - - return data; - } - } - - /** - * Get tracker data from dev config file. - * - * @return Data, undefined if cannot get. - */ - getTrackerDataFromDevConfig() { - const url = DevConfig.get('tracker.url'); - const username = DevConfig.get('tracker.username'); - - if (url && username) { - return { - url, - username, - }; - } - } - - /** - * Get tracker URL and username from mdk. - * - * @return Promise resolved with the data, rejected if cannot get. - */ - getTrackerDataFromMdk() { - return new Promise((resolve, reject) => { - exec('mdk config show tracker.url', (err, url) => { - if (!url) { - reject(err || 'URL not found.'); - return; - } - - exec('mdk config show tracker.username', (err, username) => { - if (username) { - resolve({ - url: url.replace('\n', ''), - username: username.replace('\n', ''), - }); - } else { - reject(err | 'Username not found.'); - } - }); - }); - }); - } - - /** - * Initialize some data. - * - * @return Promise resolved when done. - */ - async init() { - if (this.initialized) { - // Already initialized. - return; - } - - // Get tracker URL and username. - const trackerData = await this.getTrackerData(); - - this.url = trackerData.url; - this.username = trackerData.username; - - const parsed = Url.parse(this.url); - this.ssl = parsed.protocol == 'https'; - this.host = parsed.domain; - this.uri = parsed.path; - - // Get the password. - const keytar = require('keytar'); - - this.password = await keytar.getPassword('mdk-jira-password', this.username); // Use same service name as mdk. - - if (!this.password) { - // Ask the user. - this.password = await this.askPassword(); - } - - while (!this.initialized) { - try { - await this.getServerInfo(); - - this.initialized = true; - keytar.setPassword('mdk-jira-password', this.username, this.password); - } catch (error) { - console.log('Error connecting to the server. Please make sure you entered the data correctly.', error); - if (trackerData.fromInput) { - // User entered the data manually, ask him again. - trackerData = await this.askTrackerData(); - - this.url = trackerData.url; - this.username = trackerData.username; - } - - this.password = await this.askPassword(); - } - } - } - - /** - * Check if a certain issue could be a security issue. - * - * @param key Key to identify the issue. E.g. MOBILE-1234. - * @return Promise resolved with boolean: whether it's a security issue. - */ - async isSecurityIssue(key) { - const issue = await this.getIssue(key, 'security'); - - return issue.fields && !!issue.fields.security; - } - - /** - * Sends a request to the server and returns the data. - * - * @param uri URI to add the the Jira URL. - * @param method Method to use. Defaults to 'GET'. - * @param params Params to send as GET params (in the URL). - * @param data JSON string with the data to send as POST/PUT params. - * @param headers Headers to send. - * @return Promise resolved with the result. - */ - request(uri, method, params, data, headers) { - uri = uri || ''; - method = (method || 'GET').toUpperCase(); - data = data || ''; - params = params || {}; - headers = headers || {}; - headers['Content-Type'] = 'application/json'; - - return new Promise((resolve, reject) => { - - // Build the request URL. - const url = Url.addParamsToUrl(this.buildRequestUrl(uri), params); - - // Initialize the request. - const options = { - method: method, - auth: `${this.username}:${this.password}`, - headers: headers, - }; - const request = https.request(url, options); - - // Add data. - if (data) { - request.write(data); - } - - // Treat response. - request.on('response', (response) => { - // Read the result. - let result = ''; - response.on('data', (chunk) => { - result += chunk; - }); - response.on('end', () => { - try { - result = JSON.parse(result); - } catch (error) { - // Leave it as text. - } - - resolve({ - status: response.statusCode, - data: result, - }); - }); - }); - - request.on('error', (e) => { - reject(e); - }); - - // Send the request. - request.end(); - }); - } - - /** - * Sets a set of fields for a certain issue in Jira. - * - * @param key Key to identify the issue. E.g. MOBILE-1234. - * @param updates Object with the fields to update. - * @return Promise resolved when done. - */ - async setCustomFields(key, updates) { - const issue = await this.getIssue(key); - const update = {'fields': {}}; - - // Detect which fields have changed. - for (const updateName in updates) { - const updateValue = updates[updateName]; - const remoteValue = issue.named[updateName]; - - if (!remoteValue || remoteValue != updateValue) { - // Map the label of the field with the field code. - let fieldKey; - for (const key in issue.names) { - if (issue.names[key] == updateName) { - fieldKey = key; - break; - } - } - - if (!fieldKey) { - throw new Error(`Could not find the field named ${updateName}.`); - } - - update.fields[fieldKey] = updateValue; - } - } - - if (!Object.keys(update.fields).length) { - // No fields to update. - console.log('No updates required.') - return; - } - - const response = await this.request(`issue/${key}`, 'PUT', null, JSON.stringify(update)); - - if (response.status != 204) { - throw new Error(`Issue was not updated: ${response.status}`, response.data); - } - - console.log('Issue updated successfully.'); - } - - /** - * Upload a new attachment to an issue. - * - * @param key Key to identify the issue. E.g. MOBILE-1234. - * @param filePath Path to the file to upload. - * @return Promise resolved when done. - */ - async upload(key, filePath) { - - const uri = `issue/${key}/attachments`; - const headers = { - 'X-Atlassian-Token': 'nocheck', - } - - const response = await this.uploadFile(uri, 'file', filePath, headers); - - if (response.status != 200) { - throw new Error('Could not upload file to Jira issue'); - } - - console.log('File successfully uploaded.') - } - - /** - * Upload a file to Jira. - * - * @param uri URI to add the the Jira URL. - * @param fieldName Name of the form field where to put the file. - * @param filePath Path to the file. - * @param headers Headers. - * @return Promise resolved with the result. - */ - async uploadFile(uri, fieldName, filePath, headers) { - uri = uri || ''; - headers = headers || {}; - headers['Content-Type'] = 'multipart/form-data'; - - return new Promise((resolve, reject) => { - // Add the file to the form data. - const formData = {}; - formData[fieldName] = { - value: fs.createReadStream(filePath), - options: { - filename: filePath.substr(filePath.lastIndexOf('/') + 1), - contentType: 'multipart/form-data', - }, - }; - - // Perform the request. - const options = { - url: this.buildRequestUrl(uri), - method: 'POST', - headers: headers, - auth: { - user: this.username, - pass: this.password, - }, - formData: formData, - }; - - request(options, (err, httpResponse, body) => { - resolve({ - status: httpResponse.statusCode, - data: body, - }); - }); - }); - } -} - -module.exports = new Jira(); diff --git a/gulp/task-build-config.js b/gulp/task-build-config.js deleted file mode 100644 index 69bf71aa3..000000000 --- a/gulp/task-build-config.js +++ /dev/null @@ -1,138 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const gulp = require('gulp'); -const through = require('through'); -const bufferFrom = require('buffer-from'); -const rename = require('gulp-rename'); -const exec = require('child_process').exec; - -const LICENSE = '' + - '// (C) Copyright 2015 Moodle Pty Ltd.\n' + - '//\n' + - '// Licensed under the Apache License, Version 2.0 (the "License");\n' + - '// you may not use this file except in compliance with the License.\n' + - '// You may obtain a copy of the License at\n' + - '//\n' + - '// http://www.apache.org/licenses/LICENSE-2.0\n' + - '//\n' + - '// Unless required by applicable law or agreed to in writing, software\n' + - '// distributed under the License is distributed on an "AS IS" BASIS,\n' + - '// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' + - '// See the License for the specific language governing permissions and\n' + - '// limitations under the License.\n\n'; - -/** - * Task to convert config.json into a TypeScript class. - */ -class BuildConfigTask { - - /** - * Run the task. - * - * @param path Path to the config file. - * @param done Function to call when done. - */ - run(path, done) { - const self = this; - - // Get the last commit. - exec('git log -1 --pretty=format:"%H"', (err, commit, stderr) => { - if (err) { - console.error('An error occurred while getting the last commit: ' + err); - } else if (stderr) { - console.error('An error occurred while getting the last commit: ' + stderr); - } - - gulp.src(path) - .pipe(through(function(file) { - // Convert the contents of the file into a TypeScript class. - // Disable the rule variable-name in the file. - const config = JSON.parse(file.contents.toString()); - let contents = LICENSE + '// tslint:disable: variable-name\n' + 'export class CoreConfigConstants {\n'; - - for (let key in config) { - let value = self.transformValue(config[key]); - - if (typeof config[key] != 'number' && typeof config[key] != 'boolean' && typeof config[key] != 'string') { - key = key + ': any'; - } - - // If key has quotation marks, remove them. - if (key[0] == '"') { - key = key.substr(1, key.length - 2); - } - contents += ' static ' + key + ' = ' + value + ';\n'; - } - - // Add compilation info. - contents += ' static compilationtime = ' + Date.now() + ';\n'; - contents += ' static lastcommit = \'' + commit + '\';\n'; - - contents += '}\n'; - - file.contents = bufferFrom(contents); - - this.emit('data', file); - })) - .pipe(rename('configconstants.ts')) - .pipe(gulp.dest('./src')) - .on('end', done); - }); - } - - - /** - * Recursively transform a config value into personalized TS. - * - * @param value Value to convert - * @return Converted value. - */ - transformValue(value) { - if (typeof value == 'string') { - // Wrap the string in ' and escape them. - return "'" + value.replace(/([^\\])'/g, "$1\\'") + "'"; - } - - if (typeof value != 'number' && typeof value != 'boolean') { - const isArray = Array.isArray(value); - let contents = ''; - - let quoteKeys = false; - if (!isArray) { - for (let key in value) { - if (key.indexOf('-') >= 0) { - quoteKeys = true; - break; - } - } - } - - for (let key in value) { - value[key] = this.transformValue(value[key]); - - const quotedKey = quoteKeys ? "'" + key + "'" : key; - contents += ' ' + (isArray ? '' : quotedKey + ': ') + value[key] + ",\n"; - } - - contents += (isArray ? ']' : '}'); - - return (isArray ? '[' : '{') + "\n" + contents.replace(/^/gm, ' '); - } - - return value; - } -} - -module.exports = BuildConfigTask; diff --git a/gulp/task-build-lang.js b/gulp/task-build-lang.js deleted file mode 100644 index daa5d3126..000000000 --- a/gulp/task-build-lang.js +++ /dev/null @@ -1,176 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const gulp = require('gulp'); -const slash = require('gulp-slash'); -const clipEmptyFiles = require('gulp-clip-empty-files'); -const through = require('through'); -const bufferFrom = require('buffer-from'); -const File = require('vinyl'); -const pathLib = require('path'); - -/** - * Task to build the language files into a single file per language. - */ -class BuildLangTask { - - /** - * Copy a property from one object to another, adding a prefix to the key if needed. - * - * @param target Object to copy the properties to. - * @param source Object to copy the properties from. - * @param prefix Prefix to add to the keys. - */ - addProperties(target, source, prefix) { - for (let property in source) { - target[prefix + property] = source[property]; - } - } - - /** - * Run the task. - * - * @param language Language to treat. - * @param langPaths Paths to the possible language files. - * @param done Function to call when done. - */ - run(language, langPaths, done) { - const filename = language + '.json'; - const data = {}; - let firstFile = null; - const self = this; - - const paths = langPaths.map((path) => { - if (path.slice(-1) != '/') { - path = path + '/'; - } - - return path + language + '.json'; - }); - - gulp.src(paths, { allowEmpty: true }) - .pipe(slash()) - .pipe(clipEmptyFiles()) - .pipe(through(function(file) { - if (!firstFile) { - firstFile = file; - } - - return self.treatFile(file, data); - }, function() { - /* This implementation is based on gulp-jsoncombine module. - * https://github.com/reflog/gulp-jsoncombine */ - if (firstFile) { - const joinedPath = pathLib.join(firstFile.base, language + '.json'); - - const joinedFile = new File({ - cwd: firstFile.cwd, - base: firstFile.base, - path: joinedPath, - contents: self.treatMergedData(data), - }); - - this.emit('data', joinedFile); - } - - this.emit('end'); - })) - .pipe(gulp.dest(pathLib.join('./src/assets', 'lang'))) - .on('end', done); - } - - /** - * Treats a file to merge JSONs. This function is based on gulp-jsoncombine module. - * https://github.com/reflog/gulp-jsoncombine - * - * @param file File treated. - * @param data Object where to store the data. - */ - treatFile(file, data) { - if (file.isNull() || file.isStream()) { - return; // ignore - } - - try { - let srcPos = file.path.lastIndexOf('/src/'); - if (srcPos == -1) { - // It's probably a Windows environment. - srcPos = file.path.lastIndexOf('\\src\\'); - } - - const path = file.path.substr(srcPos + 5); - data[path] = JSON.parse(file.contents.toString()); - } catch (err) { - console.log('Error parsing JSON: ' + err); - } - } - - /** - * Treats the merged JSON data, adding prefixes depending on the component. - * - * @param data Merged data. - * @return Buffer with the treated data. - */ - treatMergedData(data) { - const merged = {}; - const mergedOrdered = {}; - - for (let filepath in data) { - const pathSplit = filepath.split(/[\/\\]/); - let prefix; - - pathSplit.pop(); - - switch (pathSplit[0]) { - case 'lang': - prefix = 'core'; - break; - case 'core': - if (pathSplit[1] == 'lang') { - // Not used right now. - prefix = 'core'; - } else { - prefix = 'core.' + pathSplit[1]; - } - break; - case 'addon': - // Remove final item 'lang'. - pathSplit.pop(); - // Remove first item 'addon'. - pathSplit.shift(); - - // For subplugins. We'll use plugin_subfolder_subfolder2_... - // E.g. 'mod_assign_feedback_comments'. - prefix = 'addon.' + pathSplit.join('_'); - break; - case 'assets': - prefix = 'assets.' + pathSplit[1]; - break; - } - - if (prefix) { - this.addProperties(merged, data[filepath], prefix + '.'); - } - } - - // Force ordering by string key. - Object.keys(merged).sort().forEach((key) => { - mergedOrdered[key] = merged[key]; - }); - - return bufferFrom(JSON.stringify(mergedOrdered, null, 4)); - } -} - -module.exports = BuildLangTask; diff --git a/gulp/task-combine-scss.js b/gulp/task-combine-scss.js deleted file mode 100644 index 0f0a28003..000000000 --- a/gulp/task-combine-scss.js +++ /dev/null @@ -1,164 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const gulp = require('gulp'); -const through = require('through'); -const bufferFrom = require('buffer-from'); -const concat = require('gulp-concat'); -const pathLib = require('path'); -const fs = require('fs'); - -/** - * Task to combine scss into a single file. - */ -class CombineScssTask { - - /** - * Finds the file and returns its content. - * - * @param capture Import file path. - * @param baseDir Directory where the file was found. - * @param paths Alternative paths where to find the imports. - * @param parsedFiles Already parsed files to reduce size of the result. - * @return Partially combined scss. - */ - getReplace(capture, baseDir, paths, parsedFiles) { - let parse = pathLib.parse(pathLib.resolve(baseDir, capture + '.scss')); - let file = parse.dir + '/' + parse.name; - - if (file.slice(-3) === '.wp') { - console.log('Windows Phone not supported "' + capture); - // File was already parsed, leave the import commented. - return '// @import "' + capture + '";'; - } - - if (!fs.existsSync(file + '.scss')) { - // File not found, might be a partial file. - file = parse.dir + '/_' + parse.name; - } - - // If file still not found, try to find the file in the alternative paths. - let x = 0; - while (!fs.existsSync(file + '.scss') && paths.length > x) { - parse = pathLib.parse(pathLib.resolve(paths[x], capture + '.scss')); - file = parse.dir + '/' + parse.name; - - x++; - } - - file = file + '.scss'; - - if (!fs.existsSync(file)) { - // File not found. Leave the import there. - console.log('File "' + capture + '" not found'); - return '@import "' + capture + '";'; - } - - if (parsedFiles.indexOf(file) >= 0) { - console.log('File "' + capture + '" already parsed'); - // File was already parsed, leave the import commented. - return '// @import "' + capture + '";'; - } - - parsedFiles.push(file); - const text = fs.readFileSync(file); - - // Recursive call. - return this.scssCombine(text, parse.dir, paths, parsedFiles); - } - - /** - * Run the task. - * - * @param done Function to call when done. - */ - run(done) { - const paths = [ - 'node_modules/ionic-angular/themes/', - 'node_modules/font-awesome/scss/', - 'node_modules/ionicons/dist/scss/' - ]; - const parsedFiles = []; - const self = this; - - gulp.src([ - './src/theme/variables.scss', - './node_modules/ionic-angular/themes/ionic.globals.*.scss', - './node_modules/ionic-angular/themes/ionic.components.scss', - './src/**/*.scss', - ]).pipe(through(function(file) { // Combine them based on @import and save it to stream. - if (file.isNull()) { - return; - } - - parsedFiles.push(file); - file.contents = bufferFrom(self.scssCombine( - file.contents, pathLib.dirname(file.path), paths, parsedFiles)); - - this.emit('data', file); - })).pipe(concat('combined.scss')) // Concat the stream output in single file. - .pipe(gulp.dest('.')) // Save file to destination. - .on('end', done); - } - - /** - * Combine scss files with its imports - * - * @param content Scss string to treat. - * @param baseDir Directory where the file was found. - * @param paths Alternative paths where to find the imports. - * @param parsedFiles Already parsed files to reduce size of the result. - * @return Scss string with the replaces done. - */ - scssCombine(content, baseDir, paths, parsedFiles) { - // Content is a Buffer, convert to string. - if (typeof content != "string") { - content = content.toString(); - } - - // Search of single imports. - let regex = /@import[ ]*['"](.*)['"][ ]*;/g; - - if (regex.test(content)) { - return content.replace(regex, (m, capture) => { - if (capture == "bmma") { - return m; - } - - return this.getReplace(capture, baseDir, paths, parsedFiles); - }); - } - - // Search of multiple imports. - regex = /@import(?:[ \n]+['"](.*)['"][,]?[ \n]*)+;/gm; - if (regex.test(content)) { - return content.replace(regex, (m, capture) => { - let text = ''; - - // Divide the import into multiple files. - const captures = m.match(/['"]([^'"]*)['"]/g); - - for (let x in captures) { - text += this.getReplace(captures[x].replace(/['"]+/g, ''), baseDir, paths, parsedFiles) + '\n'; - } - - return text; - }); - } - - return content; - } -} - -module.exports = CombineScssTask; diff --git a/gulp/task-copy-component-templates.js b/gulp/task-copy-component-templates.js deleted file mode 100644 index 2773a07b7..000000000 --- a/gulp/task-copy-component-templates.js +++ /dev/null @@ -1,79 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const fs = require('fs'); -const gulp = require('gulp'); -const flatten = require('gulp-flatten'); -const htmlmin = require('gulp-htmlmin'); -const pathLib = require('path'); - -const TEMPLATES_SRC = [ - './src/components/**/*.html', - './src/core/**/components/**/*.html', - './src/core/**/component/**/*.html', - // Copy all addon components because any component can be injected using extraImports. - './src/addon/**/components/**/*.html', - './src/addon/**/component/**/*.html' -]; -const TEMPLATES_DEST = './www/templates'; - -/** - * Task to copy component templates to www to make compile-html work in AOT. - */ -class CopyComponentTemplatesTask { - - /** - * Delete a folder and all its contents. - * - * @param path [description] - * @return {[type]} [description] - */ - deleteFolderRecursive(path) { - if (fs.existsSync(path)) { - fs.readdirSync(path).forEach((file) => { - var curPath = pathLib.join(path, file); - - if (fs.lstatSync(curPath).isDirectory()) { - this.deleteFolderRecursive(curPath); - } else { - fs.unlinkSync(curPath); - } - }); - - fs.rmdirSync(path); - } - } - - /** - * Run the task. - * - * @param done Callback to call once done. - */ - run(done) { - this.deleteFolderRecursive(TEMPLATES_DEST); - - gulp.src(TEMPLATES_SRC, { allowEmpty: true }) - .pipe(flatten()) - // Check options here: https://github.com/kangax/html-minifier - .pipe(htmlmin({ - collapseWhitespace: true, - removeComments: true, - caseSensitive: true - })) - .pipe(gulp.dest(TEMPLATES_DEST)) - .on('end', done); - } -} - -module.exports = CopyComponentTemplatesTask; diff --git a/gulp/task-push.js b/gulp/task-push.js deleted file mode 100644 index 54d151dde..000000000 --- a/gulp/task-push.js +++ /dev/null @@ -1,280 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const gulp = require('gulp'); -const inquirer = require('inquirer'); -const DevConfig = require('./dev-config'); -const Git = require('./git'); -const Jira = require('./jira'); -const Utils = require('./utils'); - -/** - * Task to push a git branch and update tracker issue. - */ -class PushTask { - - /** - * Ask the user whether he wants to continue. - * - * @return Promise resolved with boolean: true if he wants to continue. - */ - async askConfirmContinue() { - const answer = await inquirer.prompt([ - { - type: 'input', - name: 'confirm', - message: 'Are you sure you want to continue?', - default: 'n', - }, - ]); - - return answer.confirm == 'y'; - } - - /** - * Push a patch to the tracker and remove the previous one. - * - * @param branch Branch name. - * @param branchData Parsed branch data. - * @param remote Remote used. - * @return Promise resolved when done. - */ - async pushPatch(branch, branchData, remote) { - const headCommit = await Git.getHeadCommit(branch, branchData); - - if (!headCommit) { - throw new Error('Head commit not resolved, abort pushing patch.'); - } - - // Create the patch file. - const fileName = branch + '.patch'; - const tmpPatchPath = `./tmp/${fileName}`; - - await Git.createPatch(`${headCommit}...${branch}`, tmpPatchPath); - console.log('Git patch created'); - - // Check if there is an attachment with same name in the issue. - const issue = await Jira.getIssue(branchData.issue, 'attachment'); - - let existingAttachmentId; - const attachments = (issue.fields && issue.fields.attachment) || []; - for (const i in attachments) { - if (attachments[i].filename == fileName) { - // Found an existing attachment with the same name, we keep track of it. - existingAttachmentId = attachments[i].id; - break - } - } - - // Push the patch to the tracker. - console.log(`Uploading patch ${fileName} to the tracker...`); - await Jira.upload(branchData.issue, tmpPatchPath); - - if (existingAttachmentId) { - // On success, deleting file that was there before. - try { - console.log('Deleting older patch...') - await Jira.deleteAttachment(existingAttachmentId); - } catch (error) { - console.log('Could not delete older attachment.'); - } - } - } - - /** - * Run the task. - * - * @param args Command line arguments. - * @param done Function to call when done. - */ - async run(args, done) { - try { - const remote = args.remote || DevConfig.get('upstreamRemote', 'origin'); - let branch = args.branch; - const force = !!args.force; - - if (!branch) { - branch = await Git.getCurrentBranch(); - } - - if (!branch) { - throw new Error('Cannot determine the current branch. Please make sure youu aren\'t in detached HEAD state'); - } else if (branch == 'HEAD') { - throw new Error('Cannot push HEAD branch'); - } - - // Parse the branch to get the project and issue number. - const branchData = Utils.parseBranch(branch); - const keepRunning = await this.validateCommitMessages(branchData); - - if (!keepRunning) { - // Last commit not valid, stop. - console.log('Exiting...'); - done(); - return; - } - - if (!args.patch) { - // Check if it's a security issue to force patch mode. - try { - args.patch = await Jira.isSecurityIssue(branchData.issue); - - if (args.patch) { - console.log(`${branchData.issue} appears to be a security issue, switching to patch mode...`); - } - } catch (error) { - console.log(`Could not check if ${branchData.issue} is a security issue.`); - } - } - - if (args.patch) { - // Create and upload a patch file. - await this.pushPatch(branch, branchData, remote); - } else { - // Push the branch. - console.log(`Pushing branch ${branch} to remote ${remote}...`); - await Git.push(remote, branch, force); - - // Update tracker info. - console.log(`Branch pushed, update tracker info...`); - await this.updateTrackerGitInfo(branch, branchData, remote); - } - } catch (error) { - console.error(error); - } - - done(); - } - - /** - * Update git info in the tracker issue. - * - * @param branch Branch name. - * @param branchData Parsed branch data. - * @param remote Remote used. - * @return Promise resolved when done. - */ - async updateTrackerGitInfo(branch, branchData, remote) { - // Get the repository data for the project. - let repositoryUrl = DevConfig.get(branchData.project + '.repositoryUrl'); - let diffUrlTemplate = DevConfig.get(branchData.project + '.diffUrlTemplate', ''); - - if (!repositoryUrl) { - // Calculate the repositoryUrl based on the remote URL. - repositoryUrl = await Git.getRemoteUrl(remote); - } - - // Make sure the repository URL uses the regular format. - repositoryUrl = repositoryUrl.replace(/^(git@|git:\/\/)/, 'https://') - .replace(/\.git$/, '') - .replace('github.com:', 'github.com/'); - - if (!diffUrlTemplate) { - diffUrlTemplate = Utils.concatenatePaths([repositoryUrl, 'compare/%headcommit%...%branch%']); - } - - // Now create the git URL for the repository. - const repositoryGitUrl = repositoryUrl.replace(/^https?:\/\//, 'git://') + '.git'; - - // Search HEAD commit to put in the diff URL. - console.log ('Searching for head commit...'); - let headCommit = await Git.getHeadCommit(branch, branchData); - - if (!headCommit) { - throw new Error('Head commit not resolved, aborting update of tracker fields'); - } - - headCommit = headCommit.substr(0, 10); - console.log(`Head commit resolved to ${headCommit}`); - - // Calculate last properties needed. - const diffUrl = diffUrlTemplate.replace('%branch%', branch).replace('%headcommit%', headCommit); - const fieldRepositoryUrl = DevConfig.get('tracker.fieldnames.repositoryurl', 'Pull from Repository'); - const fieldBranch = DevConfig.get('tracker.fieldnames.branch', 'Pull Master Branch'); - const fieldDiffUrl = DevConfig.get('tracker.fieldnames.diffurl', 'Pull Master Diff URL'); - - // Update tracker fields. - const updates = {}; - updates[fieldRepositoryUrl] = repositoryGitUrl; - updates[fieldBranch] = branch; - updates[fieldDiffUrl] = diffUrl; - - console.log('Setting tracker fields...'); - await Jira.setCustomFields(branchData.issue, updates); - } - - /** - * Validate commit messages comparing them with the branch name. - * - * @param branchData Parsed branch data. - * @return True if value is ok or the user wants to continue anyway, false to stop. - */ - async validateCommitMessages(branchData) { - const messages = await Git.messages(30); - - let numConsecutive = 0; - let wrongCommitCandidate = null; - - for (let i = 0; i < messages.length; i++) { - const message = messages[i]; - const issue = Utils.getIssueFromCommitMessage(message); - - if (!issue || issue != branchData.issue) { - if (i === 0) { - // Last commit is wrong, it shouldn't happen. Ask the user if he wants to continue. - if (!issue) { - console.log('The issue number could not be found in the last commit message.'); - console.log(`Commit: ${message}`); - } else if (issue != branchData.issue) { - console.log('The issue number in the last commit does not match the branch being pushed to.'); - console.log(`Branch: ${branchData.issue} vs. commit: ${issue}`); - } - - return this.askConfirmContinue(); - } - - numConsecutive++; - if (numConsecutive > 2) { - // 3 consecutive commits with different branch, probably the branch commits are over. Everything OK. - return true; - - // Don't treat a merge pull request commit as a wrong commit between right commits. - // The current push could be a quick fix after a merge. - } else if (!wrongCommitCandidate && message.indexOf('Merge pull request') == -1) { - wrongCommitCandidate = { - message: message, - issue: issue, - index: i, - }; - } - } else if (wrongCommitCandidate) { - // We've found a commit with the branch name after a commit with a different branch. Probably wrong commit. - if (!wrongCommitCandidate.issue) { - console.log('The issue number could not be found in one of the commit messages.'); - console.log(`Commit: ${wrongCommitCandidate.message}`); - } else { - console.log('The issue number in a certain commit does not match the branch being pushed to.'); - console.log(`Branch: ${branchData.issue} vs. commit: ${wrongCommitCandidate.issue}`); - console.log(`Commit message: ${wrongCommitCandidate.message}`); - } - - return this.askConfirmContinue(); - } - } - - return true; - } -} - -module.exports = PushTask; diff --git a/gulp/url.js b/gulp/url.js deleted file mode 100644 index 8b46409f6..000000000 --- a/gulp/url.js +++ /dev/null @@ -1,79 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * Class with helper functions for urls. - */ -class Url { - - /** - * Add params to a URL. - * - * @param url URL to add the params to. - * @param params Object with the params to add. - * @return URL with params. - */ - static addParamsToUrl(url, params) { - let separator = url.indexOf('?') != -1 ? '&' : '?'; - - for (const key in params) { - let value = params[key]; - - // Ignore objects. - if (typeof value != 'object') { - url += separator + key + '=' + value; - separator = '&'; - } - } - - return url; - } - - /** - * Parse parts of a url, using an implicit protocol if it is missing from the url. - * - * @param url Url. - * @return Url parts. - */ - static parse(url) { - // Parse url with regular expression taken from RFC 3986: https://tools.ietf.org/html/rfc3986#appendix-B. - const match = url.trim().match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/); - - if (!match) { - return null; - } - - const host = match[4] || ''; - - // Get the credentials and the port from the host. - const [domainAndPort, credentials] = host.split('@').reverse(); - const [domain, port] = domainAndPort.split(':'); - const [username, password] = credentials ? credentials.split(':') : []; - - // Prepare parts replacing empty strings with undefined. - return { - protocol: match[2] || undefined, - domain: domain || undefined, - port: port || undefined, - credentials: credentials || undefined, - username: username || undefined, - password: password || undefined, - path: match[5] || undefined, - query: match[7] || undefined, - fragment: match[9] || undefined, - }; - } -} - -module.exports = Url; diff --git a/gulp/utils.js b/gulp/utils.js deleted file mode 100644 index 0ca11fa74..000000000 --- a/gulp/utils.js +++ /dev/null @@ -1,119 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const DevConfig = require('./dev-config'); -const DEFAULT_ISSUE_REGEX = '^(MOBILE)[-_]([0-9]+)'; - -/** - * Class with some utility functions. - */ -class Utils { - /** - * Concatenate several paths, adding a slash between them if needed. - * - * @param paths List of paths. - * @return Concatenated path. - */ - static concatenatePaths(paths) { - if (!paths.length) { - return ''; - } - - // Remove all slashes between paths. - for (let i = 0; i < paths.length; i++) { - if (!paths[i]) { - continue; - } - - if (i === 0) { - paths[i] = String(paths[i]).replace(/\/+$/g, ''); - } else if (i === paths.length - 1) { - paths[i] = String(paths[i]).replace(/^\/+/g, ''); - } else { - paths[i] = String(paths[i]).replace(/^\/+|\/+$/g, ''); - } - } - - // Remove empty paths. - paths = paths.filter(path => !!path); - - return paths.join('/'); - } - - /** - * Get command line arguments. - * - * @return Object with command line arguments. - */ - static getCommandLineArguments() { - - let args = {}, opt, thisOpt, curOpt; - for (let a = 0; a < process.argv.length; a++) { - - thisOpt = process.argv[a].trim(); - opt = thisOpt.replace(/^\-+/, ''); - - if (opt === thisOpt) { - // argument value - if (curOpt) { - args[curOpt] = opt; - } - curOpt = null; - } - else { - // Argument name. - curOpt = opt; - args[curOpt] = true; - } - } - - return args; - } - - /** - * Given a commit message, return the issue name (e.g. MOBILE-1234). - * - * @param commit Commit message. - * @return Issue name. - */ - static getIssueFromCommitMessage(commit) { - const regex = new RegExp(DevConfig.get('wording.branchRegex', DEFAULT_ISSUE_REGEX), 'i'); - const matches = commit.match(regex); - - return matches && matches[0]; - } - - /** - * Parse a branch name to extract some data. - * - * @param branch Branch name to parse. - * @return Data. - */ - static parseBranch(branch) { - const regex = new RegExp(DevConfig.get('wording.branchRegex', DEFAULT_ISSUE_REGEX), 'i'); - - const matches = branch.match(regex); - if (!matches || matches.length < 3) { - throw new Error(`Error parsing branch ${branch}`); - } - - return { - issue: matches[0], - project: matches[1], - issueNumber: matches[2], - }; - } -} - -module.exports = Utils; diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index dcc2afde8..000000000 --- a/gulpfile.js +++ /dev/null @@ -1,68 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const BuildConfigTask = require('./gulp/task-build-config'); -const BuildLangTask = require('./gulp/task-build-lang'); -const CombineScssTask = require('./gulp/task-combine-scss'); -const CopyComponentTemplatesTask = require('./gulp/task-copy-component-templates'); -const PushTask = require('./gulp/task-push'); -const Utils = require('./gulp/utils'); -const gulp = require('gulp'); -const pathLib = require('path'); - -const paths = { - lang: [ - './src/lang/', - './src/core/**/lang/', - './src/addon/**/lang/', - './src/assets/countries/', - './src/assets/mimetypes/' - ], - config: './src/config.json', -}; - -const args = Utils.getCommandLineArguments(); - -// Build the language files into a single file per language. -gulp.task('lang', (done) => { - new BuildLangTask().run('en', paths.lang, done); -}); - -// Convert config.json into a TypeScript class. -gulp.task('config', (done) => { - new BuildConfigTask().run(paths.config, done); -}); - -// Copy component templates to www to make compile-html work in AOT. -gulp.task('copy-component-templates', (done) => { - new CopyComponentTemplatesTask().run(done); -}); - -// Combine SCSS files. -gulp.task('combine-scss', (done) => { - new CombineScssTask().run(done); -}); - -gulp.task('push', (done) => { - new PushTask().run(args, done); -}); - -gulp.task('default', gulp.parallel('lang', 'config')); - -gulp.task('watch', () => { - const langsPaths = paths.lang.map(path => path + 'en.json'); - - gulp.watch(langsPaths, { interval: 500 }, gulp.parallel('lang')); - gulp.watch(paths.config, { interval: 500 }, gulp.parallel('config')); -}); diff --git a/hooks/post_push b/hooks/post_push deleted file mode 100644 index 047dc366e..000000000 --- a/hooks/post_push +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -e - -if [ "integration" != "${SOURCE_BRANCH}" ] -then - # A space-separated list of additional tags to place on this image. - additionalTags=(latest) - - # Tag and push image for each additional tag - for tag in ${additionalTags[@]}; do - echo "Tagging {$IMAGE_NAME} as ${DOCKER_REPO}:${tag}" - docker tag $IMAGE_NAME ${DOCKER_REPO}:${tag} - - echo "Pushing ${DOCKER_REPO}:${tag}" - docker push ${DOCKER_REPO}:${tag} - done -fi diff --git a/ionic.config.json b/ionic.config.json deleted file mode 100644 index 52b63fde9..000000000 --- a/ionic.config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "moodlemobile", - "integrations": { - "cordova": {}, - "gulp": {} - }, - "type": "ionic-angular", - "watchPatterns": [], - "id": "com.moodle.moodlemobile" -} diff --git a/licenses.json b/licenses.json deleted file mode 100644 index fc6609a85..000000000 --- a/licenses.json +++ /dev/null @@ -1,4496 +0,0 @@ -{ - "@angular/animations@5.2.11": { - "licenses": "MIT", - "repository": "https://github.com/angular/angular", - "publisher": "angular", - "licenseFile": "README.md" - }, - "@angular/common@5.2.11": { - "licenses": "MIT", - "repository": "https://github.com/angular/angular", - "publisher": "angular", - "licenseFile": "README.md" - }, - "@angular/compiler-cli@5.2.11": { - "licenses": "MIT", - "repository": "https://github.com/angular/angular", - "licenseFile": "README.md" - }, - "@angular/compiler@5.2.11": { - "licenses": "MIT", - "repository": "https://github.com/angular/angular", - "publisher": "angular", - "licenseFile": "README.md" - }, - "@angular/core@5.2.11": { - "licenses": "MIT", - "repository": "https://github.com/angular/angular", - "publisher": "angular", - "licenseFile": "README.md" - }, - "@angular/forms@5.2.11": { - "licenses": "MIT", - "repository": "https://github.com/angular/angular", - "publisher": "angular", - "licenseFile": "README.md" - }, - "@angular/platform-browser-dynamic@5.2.11": { - "licenses": "MIT", - "repository": "https://github.com/angular/angular", - "publisher": "angular", - "licenseFile": "README.md" - }, - "@angular/platform-browser@5.2.11": { - "licenses": "MIT", - "repository": "https://github.com/angular/angular", - "publisher": "angular", - "licenseFile": "README.md" - }, - "@ionic-native/badge@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/camera@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/chooser@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/clipboard@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/core@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/device@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/file-opener@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/file-transfer@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/file@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/geolocation@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/globalization@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/http@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/in-app-browser@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/keyboard@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/local-notifications@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/media-capture@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/network@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/push@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/qr-scanner@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/screen-orientation@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/splash-screen@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/sqlite@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/status-bar@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/web-intent@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@ionic-native/zip@4.20.0": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic-native", - "publisher": "ionic", - "licenseFile": "README.md" - }, - "@mrmlnc/readdir-enhanced@2.2.1": { - "licenses": "MIT", - "repository": "https://github.com/bigstickcarpet/readdir-enhanced", - "publisher": "James Messinger", - "url": "http://bigstickcarpet.com", - "licenseFile": "LICENSE" - }, - "@ngx-translate/core@8.0.0": { - "licenses": "MIT", - "repository": "https://github.com/ngx-translate/core", - "publisher": "Olivier Combe", - "licenseFile": "LICENSE" - }, - "@ngx-translate/http-loader@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/ngx-translate/http-loader", - "publisher": "Olivier Combe", - "licenseFile": "LICENSE" - }, - "@nodelib/fs.stat@1.1.3": { - "licenses": "MIT", - "repository": "https://github.com/nodelib/nodelib/tree/master/packages/fs/fs.stat", - "licenseFile": "README.md" - }, - "@types/events@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "licenseFile": "LICENSE" - }, - "@types/glob@7.1.1": { - "licenses": "MIT", - "repository": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "licenseFile": "LICENSE" - }, - "@types/minimatch@3.0.3": { - "licenses": "MIT", - "repository": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "licenseFile": "LICENSE" - }, - "@types/node@8.10.59": { - "licenses": "MIT", - "repository": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "licenseFile": "LICENSE" - }, - "abbrev@1.1.1": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/abbrev-js", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "licenseFile": "LICENSE" - }, - "accepts@1.3.5": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/accepts", - "licenseFile": "LICENSE" - }, - "ajv@6.11.0": { - "licenses": "MIT", - "repository": "https://github.com/epoberezkin/ajv", - "publisher": "Evgeny Poberezkin", - "licenseFile": "LICENSE" - }, - "ajv@6.9.1": { - "licenses": "MIT", - "repository": "https://github.com/epoberezkin/ajv", - "publisher": "Evgeny Poberezkin", - "licenseFile": "LICENSE" - }, - "android-versions@1.5.0": { - "licenses": "MIT", - "repository": "https://github.com/dvoiss/android-versions", - "publisher": "dvoiss", - "licenseFile": "README.md" - }, - "ansi-align@2.0.0": { - "licenses": "ISC", - "repository": "https://github.com/nexdrew/ansi-align", - "publisher": "nexdrew", - "licenseFile": "LICENSE" - }, - "ansi-escapes@3.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/ansi-escapes", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "ansi-regex@2.1.1": { - "licenses": "MIT", - "repository": "https://github.com/chalk/ansi-regex", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "ansi-regex@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/chalk/ansi-regex", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "ansi-regex@4.1.0": { - "licenses": "MIT", - "repository": "https://github.com/chalk/ansi-regex", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "ansi-styles@3.2.1": { - "licenses": "MIT", - "repository": "https://github.com/chalk/ansi-styles", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "ansi@0.3.1": { - "licenses": "MIT", - "repository": "https://github.com/TooTallNate/ansi.js", - "publisher": "Nathan Rajlich", - "email": "nathan@tootallnate.net", - "url": "http://tootallnate.net", - "licenseFile": "LICENSE" - }, - "anymatch@1.3.2": { - "licenses": "ISC", - "repository": "https://github.com/es128/anymatch", - "publisher": "Elan Shanker", - "url": "http://github.com/es128", - "licenseFile": "LICENSE" - }, - "aproba@1.2.0": { - "licenses": "ISC", - "repository": "https://github.com/iarna/aproba", - "publisher": "Rebecca Turner", - "email": "me@re-becca.org", - "licenseFile": "LICENSE" - }, - "are-we-there-yet@1.1.5": { - "licenses": "ISC", - "repository": "https://github.com/iarna/are-we-there-yet", - "publisher": "Rebecca Turner", - "url": "http://re-becca.org", - "licenseFile": "LICENSE" - }, - "arr-diff@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/arr-diff", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "arr-diff@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/arr-diff", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "arr-flatten@1.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/arr-flatten", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "arr-union@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/arr-union", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "array-find-index@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/array-find-index", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "array-flatten@1.1.1": { - "licenses": "MIT", - "repository": "https://github.com/blakeembrey/array-flatten", - "publisher": "Blake Embrey", - "email": "hello@blakeembrey.com", - "url": "http://blakeembrey.me", - "licenseFile": "LICENSE" - }, - "array-ify@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/stevemao/array-ify", - "publisher": "Steve Mao", - "email": "maochenyan@gmail.com", - "url": "https://github.com/stevemao", - "licenseFile": "README.md" - }, - "array-union@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/array-union", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "array-uniq@1.0.3": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/array-uniq", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "array-unique@0.2.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/array-unique", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "array-unique@0.3.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/array-unique", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "asn1@0.2.3": { - "licenses": "MIT", - "repository": "https://github.com/mcavage/node-asn1", - "publisher": "Mark Cavage", - "email": "mcavage@gmail.com", - "licenseFile": "LICENSE" - }, - "assert-plus@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/mcavage/node-assert-plus", - "publisher": "Mark Cavage", - "email": "mcavage@gmail.com", - "licenseFile": "README.md" - }, - "assign-symbols@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/assign-symbols", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "async-each@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/paulmillr/async-each", - "publisher": "Paul Miller", - "url": "http://paulmillr.com/", - "licenseFile": "README.md" - }, - "async@2.6.2": { - "licenses": "MIT", - "repository": "https://github.com/caolan/async", - "publisher": "Caolan McMahon", - "licenseFile": "LICENSE" - }, - "asynckit@0.4.0": { - "licenses": "MIT", - "repository": "https://github.com/alexindigo/asynckit", - "publisher": "Alex Indigo", - "email": "iam@alexindigo.com", - "licenseFile": "LICENSE" - }, - "atob@2.1.1": { - "licenses": "(MIT OR Apache-2.0)", - "repository": "git://git.coolaj86.com/coolaj86/atob.js", - "publisher": "AJ ONeal", - "email": "coolaj86@gmail.com", - "url": "https://coolaj86.com", - "licenseFile": "LICENSE" - }, - "aws-sign2@0.7.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/mikeal/aws-sign", - "publisher": "Mikeal Rogers", - "email": "mikeal.rogers@gmail.com", - "url": "http://www.futurealoof.com", - "licenseFile": "LICENSE" - }, - "aws4@1.8.0": { - "licenses": "MIT", - "repository": "https://github.com/mhart/aws4", - "publisher": "Michael Hart", - "email": "michael.hart.au@gmail.com", - "url": "http://github.com/mhart", - "licenseFile": "LICENSE" - }, - "babel-plugin-add-header-comment@1.0.3": { - "licenses": "MIT", - "repository": "https://github.com/shopify/babel-plugin-add-header-comment", - "publisher": "Shopify Inc.", - "licenseFile": "LICENSE.md" - }, - "balanced-match@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/juliangruber/balanced-match", - "publisher": "Julian Gruber", - "email": "mail@juliangruber.com", - "url": "http://juliangruber.com", - "licenseFile": "LICENSE.md" - }, - "base64-js@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/beatgammit/base64-js", - "publisher": "T. Jameson Little", - "email": "t.jameson.little@gmail.com", - "licenseFile": "LICENSE" - }, - "base@0.11.2": { - "licenses": "MIT", - "repository": "https://github.com/node-base/base", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "bcrypt-pbkdf@1.0.1": { - "licenses": "BSD-3-Clause", - "licenseFile": "README.md" - }, - "big-integer@1.6.44": { - "licenses": "Unlicense", - "repository": "https://github.com/peterolson/BigInteger.js", - "publisher": "Peter Olson", - "email": "peter.e.c.olson+npm@gmail.com", - "licenseFile": "LICENSE" - }, - "binary-extensions@1.11.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/binary-extensions", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "bindings@1.5.0": { - "licenses": "MIT", - "repository": "https://github.com/TooTallNate/node-bindings", - "publisher": "Nathan Rajlich", - "email": "nathan@tootallnate.net", - "url": "http://tootallnate.net", - "licenseFile": "LICENSE.md" - }, - "body-parser@1.18.3": { - "licenses": "MIT", - "repository": "https://github.com/expressjs/body-parser", - "licenseFile": "LICENSE" - }, - "boxen@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/boxen", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "bplist-creator@0.0.7": { - "licenses": "MIT", - "repository": "https://github.com/nearinfinity/node-bplist-creator", - "publisher": "https://github.com/nearinfinity/node-bplist-parser.git", - "licenseFile": "README.md" - }, - "bplist-parser@0.0.6": { - "licenses": "MIT", - "repository": "https://github.com/nearinfinity/node-bplist-parser", - "publisher": "Joe Ferner", - "email": "joe.ferner@nearinfinity.com", - "licenseFile": "README.md" - }, - "bplist-parser@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/nearinfinity/node-bplist-parser", - "publisher": "Joe Ferner", - "email": "joe.ferner@nearinfinity.com", - "licenseFile": "README.md" - }, - "brace-expansion@1.1.11": { - "licenses": "MIT", - "repository": "https://github.com/juliangruber/brace-expansion", - "publisher": "Julian Gruber", - "email": "mail@juliangruber.com", - "url": "http://juliangruber.com", - "licenseFile": "LICENSE" - }, - "braces@1.8.5": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/braces", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "braces@2.3.2": { - "licenses": "MIT", - "repository": "https://github.com/micromatch/braces", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "buffer-from@1.1.0": { - "licenses": "MIT", - "repository": "https://github.com/LinusU/buffer-from", - "licenseFile": "readme.md" - }, - "builtin-modules@1.1.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/builtin-modules", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "builtins@1.0.3": { - "licenses": "MIT", - "repository": "https://github.com/juliangruber/builtins", - "licenseFile": "License" - }, - "bytes@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/visionmedia/bytes.js", - "publisher": "TJ Holowaychuk", - "email": "tj@vision-media.ca", - "url": "http://tjholowaychuk.com", - "licenseFile": "LICENSE" - }, - "cache-base@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/cache-base", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "call-me-maybe@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/limulus/call-me-maybe", - "publisher": "Eric McCarthy", - "email": "eric@limulus.net", - "url": "http://www.limulus.net/", - "licenseFile": "LICENSE" - }, - "callsites@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/callsites", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "camelcase@4.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/camelcase", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "capture-stack-trace@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/floatdrop/capture-stack-trace", - "publisher": "Vsevolod Strukchinsky", - "email": "floatdrop@gmail.com", - "url": "github.com/floatdrop", - "licenseFile": "license" - }, - "caseless@0.12.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/mikeal/caseless", - "publisher": "Mikeal Rogers", - "email": "mikeal.rogers@gmail.com", - "licenseFile": "LICENSE" - }, - "chalk@2.4.1": { - "licenses": "MIT", - "repository": "https://github.com/chalk/chalk", - "licenseFile": "license" - }, - "chalk@2.4.2": { - "licenses": "MIT", - "repository": "https://github.com/chalk/chalk", - "licenseFile": "license" - }, - "chardet@0.7.0": { - "licenses": "MIT", - "repository": "https://github.com/runk/node-chardet", - "publisher": "Dmitry Shirokov", - "email": "deadrunk@gmail.com", - "licenseFile": "LICENSE" - }, - "chart.js@2.9.3": { - "licenses": "MIT", - "repository": "https://github.com/chartjs/Chart.js", - "licenseFile": "LICENSE.md" - }, - "chartjs-color-string@0.5.0": { - "licenses": "MIT", - "repository": "https://github.com/chartjs/chartjs-color-string", - "publisher": "Heather Arthur", - "email": "fayearthur@gmail.com", - "licenseFile": "LICENSE" - }, - "chartjs-color@2.2.0": { - "licenses": "MIT", - "repository": "https://github.com/chartjs/chartjs-color", - "licenseFile": "LICENSE" - }, - "chokidar@1.7.0": { - "licenses": "MIT", - "repository": "https://github.com/paulmillr/chokidar", - "publisher": "Paul Miller", - "url": "http://paulmillr.com", - "licenseFile": "README.md" - }, - "chownr@1.1.1": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/chownr", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "ci-info@1.1.3": { - "licenses": "MIT", - "repository": "https://github.com/watson/ci-info", - "publisher": "Thomas Watson Steen", - "email": "w@tson.dk", - "url": "https://twitter.com/wa7son", - "licenseFile": "LICENSE" - }, - "class-utils@0.3.6": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/class-utils", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "cli-boxes@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/cli-boxes", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "cli-cursor@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/cli-cursor", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "cli-width@2.2.0": { - "licenses": "ISC", - "repository": "https://github.com/knownasilya/cli-width", - "publisher": "Ilya Radchenko", - "email": "ilya@burstcreations.com", - "licenseFile": "LICENSE" - }, - "code-point-at@1.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/code-point-at", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "collection-visit@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/collection-visit", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "color-convert@0.5.3": { - "licenses": "MIT*", - "repository": "https://github.com/harthur/color-convert", - "publisher": "Heather Arthur", - "email": "fayearthur@gmail.com", - "licenseFile": "LICENSE" - }, - "color-convert@1.9.2": { - "licenses": "MIT", - "repository": "https://github.com/Qix-/color-convert", - "publisher": "Heather Arthur", - "email": "fayearthur@gmail.com", - "licenseFile": "LICENSE" - }, - "color-name@1.1.1": { - "licenses": "MIT", - "repository": "https://github.com/dfcreative/color-name", - "publisher": "DY", - "email": "dfcreative@gmail.com", - "licenseFile": "LICENSE" - }, - "color-name@1.1.3": { - "licenses": "MIT", - "repository": "https://github.com/dfcreative/color-name", - "publisher": "DY", - "email": "dfcreative@gmail.com", - "licenseFile": "LICENSE" - }, - "com-darryncampbell-cordova-plugin-intent@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/darryncampbell/darryncampbell-cordova-plugin-intent", - "publisher": "Darryn Campbell", - "licenseFile": "LICENSE" - }, - "combined-stream@1.0.6": { - "licenses": "MIT", - "repository": "https://github.com/felixge/node-combined-stream", - "publisher": "Felix Geisendörfer", - "email": "felix@debuggable.com", - "url": "http://debuggable.com/", - "licenseFile": "License" - }, - "compare-func@1.3.2": { - "licenses": "MIT", - "repository": "https://github.com/stevemao/compare-func", - "publisher": "Steve Mao", - "email": "maochenyan@gmail.com", - "url": "https://github.com/stevemao", - "licenseFile": "README.md" - }, - "component-emitter@1.2.1": { - "licenses": "MIT", - "repository": "https://github.com/component/emitter", - "licenseFile": "LICENSE" - }, - "compressible@2.0.17": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/compressible", - "licenseFile": "LICENSE" - }, - "compression@1.7.4": { - "licenses": "MIT", - "repository": "https://github.com/expressjs/compression", - "licenseFile": "LICENSE" - }, - "concat-map@0.0.1": { - "licenses": "MIT", - "repository": "https://github.com/substack/node-concat-map", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "conf@1.4.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/conf", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "configstore@3.1.2": { - "licenses": "BSD-2-Clause", - "repository": "https://github.com/yeoman/configstore", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "configstore@4.0.0": { - "licenses": "BSD-2-Clause", - "repository": "https://github.com/yeoman/configstore", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "console-control-strings@1.1.0": { - "licenses": "ISC", - "repository": "https://github.com/iarna/console-control-strings", - "publisher": "Rebecca Turner", - "email": "me@re-becca.org", - "url": "http://re-becca.org/", - "licenseFile": "LICENSE" - }, - "content-disposition@0.5.2": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/content-disposition", - "licenseFile": "LICENSE" - }, - "content-type@1.0.4": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/content-type", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "cookie-signature@1.0.6": { - "licenses": "MIT", - "repository": "https://github.com/visionmedia/node-cookie-signature", - "publisher": "TJ Holowaychuk", - "email": "tj@learnboost.com", - "licenseFile": "Readme.md" - }, - "cookie@0.3.1": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/cookie", - "publisher": "Roman Shtylman", - "email": "shtylman@gmail.com", - "licenseFile": "LICENSE" - }, - "copy-descriptor@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/copy-descriptor", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "cordova-android-support-gradle-release@3.0.1": { - "licenses": "MIT", - "publisher": "Dave Alden", - "licenseFile": "README.md" - }, - "cordova-android@8.1.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-android", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-android/NOTICE" - }, - "cordova-app-hello-world@4.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-app-hello-world", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-app-hello-world/NOTICE" - }, - "cordova-clipboard@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/ihadeed/cordova-clipboard", - "publisher": "Ibrahim Hadeed", - "licenseFile": "LICENSE" - }, - "cordova-common@3.2.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-common", - "publisher": "Apache Software Foundation", - "licenseFile": "README.md" - }, - "cordova-create@2.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-create", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE" - }, - "cordova-fetch@2.0.1": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-fetch", - "publisher": "Apache Software Foundation", - "licenseFile": "README.md" - }, - "cordova-ios@5.1.1": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-ios", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-ios/NOTICE" - }, - "cordova-lib@9.0.1": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-lib", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-lib/NOTICE" - }, - "cordova-plugin-advanced-http@2.4.1": { - "licenses": "MIT", - "repository": "https://github.com/silkimen/cordova-plugin-advanced-http", - "publisher": "Wymsee", - "licenseFile": "LICENSE" - }, - "cordova-plugin-badge@0.8.8": { - "licenses": "Apache*", - "repository": "https://github.com/katzer/cordova-plugin-badge", - "publisher": "Sebastián Katzer", - "licenseFile": "LICENSE" - }, - "cordova-plugin-camera@4.1.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-camera", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-camera/NOTICE" - }, - "cordova-plugin-chooser@1.3.1": { - "licenses": "Apache-2.0", - "repository": "https://github.com/cyph/cordova-plugin-chooser", - "publisher": "Cyph, Inc.", - "email": "cordova-plugin-chooser@cyph.com", - "url": "https://github.com/cyph", - "licenseFile": "LICENSE" - }, - "cordova-plugin-customurlscheme@5.0.0": { - "licenses": "MIT", - "repository": "https://github.com/EddyVerbruggen/Custom-URL-scheme", - "publisher": "Eddy Verbruggen", - "email": "eddyverbruggen@gmail.com", - "url": "https://github.com/EddyVerbruggen", - "licenseFile": "README.md" - }, - "cordova-plugin-device@2.0.3": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-device", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-device/NOTICE" - }, - "cordova-plugin-file-opener2@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/pwlin/cordova-plugin-file-opener2", - "publisher": "pwlin05@gmail.com", - "licenseFile": "LICENSE" - }, - "cordova-plugin-file-transfer@1.7.1": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-file-transfer", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-file-transfer/NOTICE" - }, - "cordova-plugin-file@6.0.2": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-file", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-file/NOTICE" - }, - "cordova-plugin-geolocation@4.0.3-dev": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-geolocation", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-geolocation/NOTICE" - }, - "cordova-plugin-globalization@1.11.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-globalization", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-globalization/NOTICE" - }, - "cordova-plugin-inappbrowser@4.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-inappbrowser", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-inappbrowser/NOTICE" - }, - "cordova-plugin-ionic-keyboard@2.1.3": { - "licenses": "MIT*", - "repository": "https://github.com/ionic-team/cordova-plugin-ionic-keyboard", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE" - }, - "cordova-plugin-ionic-webview@4.1.3": { - "licenses": "Apache-2.0", - "repository": "https://github.com/ionic-team/cordova-plugin-ionic-webview", - "publisher": "Ionic Team", - "licenseFile": "LICENSE" - }, - "cordova-plugin-local-notification@0.9.0-beta.3": { - "licenses": "Apache*", - "repository": "https://github.com/katzer/cordova-plugin-local-notifications", - "publisher": "Sebastián Katzer", - "licenseFile": "LICENSE" - }, - "cordova-plugin-media-capture@3.0.3": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-media-capture", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-media-capture/NOTICE" - }, - "cordova-plugin-network-information@2.0.2": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-network-information", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-network-information/NOTICE" - }, - "cordova-plugin-qrscanner@3.0.1": { - "licenses": "MIT", - "repository": "https://github.com/bitpay/cordova-plugin-qrscanner", - "publisher": "Jason Dreyzehner", - "licenseFile": "LICENSE" - }, - "cordova-plugin-screen-orientation@3.0.2": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-screen-orientation", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE" - }, - "cordova-plugin-splashscreen@5.0.3": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-splashscreen", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-splashscreen/NOTICE" - }, - "cordova-plugin-statusbar@2.4.3": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-statusbar", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-statusbar/NOTICE" - }, - "cordova-plugin-whitelist@1.3.4": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-plugin-whitelist", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-plugin-whitelist/NOTICE" - }, - "cordova-plugin-wkuserscript@1.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/moodlemobile/cordova-plugin-wkuserscript", - "publisher": "Moodle Pty Ltd", - "licenseFile": "LICENSE" - }, - "cordova-plugin-wkwebview-cookies@1.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies", - "publisher": "Moodle Pty Ltd", - "licenseFile": "LICENSE" - }, - "cordova-plugin-zip@3.1.0": { - "licenses": "BSD*", - "repository": "https://github.com/MobileChromeApps/cordova-plugin-zip", - "licenseFile": "LICENSE" - }, - "cordova-serve@3.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-serve", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova-serve/NOTICE" - }, - "cordova-sqlite-storage-dependencies@2.1.1": { - "licenses": "Unlicense", - "repository": "https://github.com/litehelpers/Cordova-sqlite-storage-dependencies", - "publisher": "Christopher J. Brody", - "licenseFile": "README.md" - }, - "cordova-sqlite-storage@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/xpbrew/cordova-sqlite-storage", - "publisher": "various", - "licenseFile": "LICENSE.md" - }, - "cordova-support-google-services@1.3.2": { - "licenses": "MIT", - "repository": "https://github.com/chemerisuk/cordova-support-google-services", - "publisher": "Maksim Chemerisuk", - "email": "chemerisuk@gmail.com", - "url": "https://github.com/chemerisuk", - "licenseFile": "LICENSE" - }, - "cordova@9.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-cli", - "publisher": "Anis Kadri", - "licenseFile": "LICENSE", - "noticeFile": "node_modules/cordova/NOTICE" - }, - "core-js@2.3.0": { - "licenses": "MIT", - "repository": "https://github.com/zloirock/core-js", - "licenseFile": "LICENSE" - }, - "core-util-is@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/isaacs/core-util-is", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "create-error-class@3.0.2": { - "licenses": "MIT", - "repository": "https://github.com/floatdrop/create-error-class", - "publisher": "Vsevolod Strukchinsky", - "email": "floatdrop@gmail.com", - "url": "github.com/floatdrop", - "licenseFile": "license" - }, - "cross-spawn@5.1.0": { - "licenses": "MIT", - "repository": "https://github.com/IndigoUnited/node-cross-spawn", - "publisher": "IndigoUnited", - "email": "hello@indigounited.com", - "url": "http://indigounited.com", - "licenseFile": "LICENSE" - }, - "cross-spawn@6.0.5": { - "licenses": "MIT", - "repository": "https://github.com/moxystudio/node-cross-spawn", - "publisher": "André Cruz", - "email": "andre@moxy.studio", - "licenseFile": "LICENSE" - }, - "crypto-random-string@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/crypto-random-string", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "currently-unhandled@0.4.1": { - "licenses": "MIT", - "repository": "https://github.com/jamestalmage/currently-unhandled", - "publisher": "James Talmage", - "email": "james@talmage.io", - "url": "github.com/jamestalmage", - "licenseFile": "license" - }, - "dashdash@1.14.1": { - "licenses": "MIT", - "repository": "https://github.com/trentm/node-dashdash", - "publisher": "Trent Mick", - "email": "trentm@gmail.com", - "url": "http://trentm.com", - "licenseFile": "LICENSE.txt" - }, - "debug@2.6.9": { - "licenses": "MIT", - "repository": "https://github.com/visionmedia/debug", - "publisher": "TJ Holowaychuk", - "email": "tj@vision-media.ca", - "licenseFile": "LICENSE" - }, - "debug@4.1.1": { - "licenses": "MIT", - "repository": "https://github.com/visionmedia/debug", - "publisher": "TJ Holowaychuk", - "email": "tj@vision-media.ca", - "licenseFile": "LICENSE" - }, - "decode-uri-component@0.2.0": { - "licenses": "MIT", - "repository": "https://github.com/SamVerschueren/decode-uri-component", - "publisher": "Sam Verschueren", - "email": "sam.verschueren@gmail.com", - "url": "github.com/SamVerschueren", - "licenseFile": "license" - }, - "dedent@0.7.0": { - "licenses": "MIT", - "repository": "https://github.com/dmnd/dedent", - "publisher": "Desmond Brand", - "email": "dmnd@desmondbrand.com", - "url": "http://desmondbrand.com", - "licenseFile": "LICENSE" - }, - "deep-equal@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/substack/node-deep-equal", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "deep-extend@0.6.0": { - "licenses": "MIT", - "repository": "https://github.com/unclechu/node-deep-extend", - "publisher": "Viacheslav Lotsmanov", - "email": "lotsmanov89@gmail.com", - "licenseFile": "LICENSE" - }, - "define-properties@1.1.2": { - "licenses": "MIT", - "repository": "https://github.com/ljharb/define-properties", - "publisher": "Jordan Harband", - "licenseFile": "LICENSE" - }, - "define-property@0.2.5": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/define-property", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "define-property@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/define-property", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "define-property@2.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/define-property", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "defined@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/substack/defined", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "delayed-stream@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/felixge/node-delayed-stream", - "publisher": "Felix Geisendörfer", - "email": "felix@debuggable.com", - "url": "http://debuggable.com/", - "licenseFile": "License" - }, - "delegates@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/visionmedia/node-delegates", - "licenseFile": "License" - }, - "dep-graph@1.1.0": { - "licenses": "MIT*", - "repository": "https://github.com/TrevorBurnham/dep-graph", - "publisher": "Trevor Burnham", - "url": "http://trevorburnham.com", - "licenseFile": "README.mdown" - }, - "depd@1.1.2": { - "licenses": "MIT", - "repository": "https://github.com/dougwilson/nodejs-depd", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "destroy@1.0.4": { - "licenses": "MIT", - "repository": "https://github.com/stream-utils/destroy", - "publisher": "Jonathan Ong", - "email": "me@jongleberry.com", - "url": "http://jongleberry.com", - "licenseFile": "LICENSE" - }, - "detect-indent@5.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/detect-indent", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "detect-libc@1.0.3": { - "licenses": "Apache-2.0", - "repository": "https://github.com/lovell/detect-libc", - "publisher": "Lovell Fuller", - "email": "npm@lovell.info", - "licenseFile": "LICENSE" - }, - "dir-glob@2.2.2": { - "licenses": "MIT", - "repository": "https://github.com/kevva/dir-glob", - "publisher": "Kevin Mårtensson", - "email": "kevinmartensson@gmail.com", - "url": "github.com/kevva", - "licenseFile": "license" - }, - "dot-prop@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/dot-prop", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "dot-prop@4.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/dot-prop", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "duplexer3@0.1.4": { - "licenses": "BSD-3-Clause", - "repository": "https://github.com/floatdrop/duplexer3", - "publisher": "Conrad Pankoff", - "email": "deoxxa@fknsrs.biz", - "url": "http://www.fknsrs.biz/", - "licenseFile": "LICENSE.md" - }, - "ecc-jsbn@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/quartzjer/ecc-jsbn", - "publisher": "Jeremie Miller", - "email": "jeremie@jabber.org", - "url": "http://jeremie.com/", - "licenseFile": "LICENSE" - }, - "editor@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/substack/node-editor", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "ee-first@1.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonathanong/ee-first", - "publisher": "Jonathan Ong", - "email": "me@jongleberry.com", - "url": "http://jongleberry.com", - "licenseFile": "LICENSE" - }, - "elementtree@0.1.7": { - "licenses": "Apache-2.0", - "repository": "https://github.com/racker/node-elementtree", - "publisher": "Rackspace US, Inc.", - "licenseFile": "LICENSE.txt", - "noticeFile": "node_modules/elementtree/NOTICE" - }, - "encodeurl@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/pillarjs/encodeurl", - "licenseFile": "LICENSE" - }, - "end-of-stream@1.4.1": { - "licenses": "MIT", - "repository": "https://github.com/mafintosh/end-of-stream", - "publisher": "Mathias Buus", - "email": "mathiasbuus@gmail.com", - "licenseFile": "LICENSE" - }, - "endent@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/ZhouHansen/endent", - "publisher": "zhouhancheng", - "email": "z308114274@gmail.com", - "licenseFile": "LICENSE" - }, - "env-paths@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/env-paths", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "es-abstract@1.12.0": { - "licenses": "MIT", - "repository": "https://github.com/ljharb/es-abstract", - "publisher": "Jordan Harband", - "email": "ljharb@gmail.com", - "url": "http://ljharb.codes", - "licenseFile": "LICENSE" - }, - "es-to-primitive@1.1.1": { - "licenses": "MIT", - "repository": "https://github.com/ljharb/es-to-primitive", - "publisher": "Jordan Harband", - "licenseFile": "LICENSE" - }, - "es6-promise-plugin@4.2.2": { - "licenses": "MIT", - "repository": "https://github.com/vstirbu/PromisesPlugin", - "licenseFile": "LICENSE" - }, - "es6-promise@3.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jakearchibald/ES6-Promises", - "publisher": "Yehuda Katz, Tom Dale, Stefan Penner and contributors", - "url": "Conversion to ES6 API by Jake Archibald", - "licenseFile": "LICENSE" - }, - "escape-html@1.0.3": { - "licenses": "MIT", - "repository": "https://github.com/component/escape-html", - "licenseFile": "LICENSE" - }, - "escape-string-regexp@1.0.5": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/escape-string-regexp", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "etag@1.8.1": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/etag", - "licenseFile": "LICENSE" - }, - "execa@0.7.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/execa", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "execa@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/execa", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "expand-brackets@0.1.5": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/expand-brackets", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "expand-brackets@2.1.4": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/expand-brackets", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "expand-range@1.8.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/expand-range", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "express@4.16.4": { - "licenses": "MIT", - "repository": "https://github.com/expressjs/express", - "publisher": "TJ Holowaychuk", - "email": "tj@vision-media.ca", - "licenseFile": "LICENSE" - }, - "extend-shallow@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/extend-shallow", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "extend-shallow@3.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/extend-shallow", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "extend@3.0.2": { - "licenses": "MIT", - "repository": "https://github.com/justmoon/node-extend", - "publisher": "Stefan Thomas", - "email": "justmoon@members.fsf.org", - "url": "http://www.justmoon.net", - "licenseFile": "LICENSE" - }, - "external-editor@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/mrkmg/node-external-editor", - "publisher": "Kevin Gravier", - "email": "kevin@mrkmg.com", - "url": "https://mrkmg.com", - "licenseFile": "LICENSE" - }, - "extglob@0.3.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/extglob", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "extglob@2.0.4": { - "licenses": "MIT", - "repository": "https://github.com/micromatch/extglob", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "extsprintf@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/davepacheco/node-extsprintf", - "licenseFile": "LICENSE" - }, - "fast-deep-equal@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/epoberezkin/fast-deep-equal", - "publisher": "Evgeny Poberezkin", - "licenseFile": "LICENSE" - }, - "fast-deep-equal@3.1.1": { - "licenses": "MIT", - "repository": "https://github.com/epoberezkin/fast-deep-equal", - "publisher": "Evgeny Poberezkin", - "licenseFile": "LICENSE" - }, - "fast-glob@2.2.7": { - "licenses": "MIT", - "repository": "https://github.com/mrmlnc/fast-glob", - "publisher": "Denis Malinochkin", - "url": "canonium.com", - "licenseFile": "LICENSE" - }, - "fast-json-parse@1.0.3": { - "licenses": "MIT", - "repository": "https://github.com/mcollina/fast-json-parse", - "publisher": "Matteo Collina", - "email": "hello@matteocollina.com", - "licenseFile": "LICENSE" - }, - "fast-json-stable-stringify@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/epoberezkin/fast-json-stable-stringify", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "figures@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/figures", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "file-uri-to-path@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/TooTallNate/file-uri-to-path", - "publisher": "Nathan Rajlich", - "email": "nathan@tootallnate.net", - "url": "http://n8.io/", - "licenseFile": "LICENSE" - }, - "filename-regex@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/regexhq/filename-regex", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "fill-range@2.2.4": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/fill-range", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "fill-range@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/fill-range", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "finalhandler@1.1.1": { - "licenses": "MIT", - "repository": "https://github.com/pillarjs/finalhandler", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "find-up@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/find-up", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "font-awesome@4.7.0": { - "licenses": "(OFL-1.1 AND MIT)", - "repository": "https://github.com/FortAwesome/Font-Awesome", - "publisher": "Dave Gandy", - "email": "dave@fontawesome.io", - "url": "http://twitter.com/davegandy", - "licenseFile": "README.md" - }, - "for-each@0.3.3": { - "licenses": "MIT", - "repository": "https://github.com/Raynos/for-each", - "publisher": "Raynos", - "email": "raynos2@gmail.com", - "licenseFile": "LICENSE" - }, - "for-in@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/for-in", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "for-own@0.1.5": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/for-own", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "foreach@2.0.5": { - "licenses": "MIT", - "repository": "https://github.com/manuelstofer/foreach", - "publisher": "Manuel Stofer", - "email": "manuel@takimata.ch", - "licenseFile": "LICENSE" - }, - "forever-agent@0.6.1": { - "licenses": "Apache-2.0", - "repository": "https://github.com/mikeal/forever-agent", - "publisher": "Mikeal Rogers", - "email": "mikeal.rogers@gmail.com", - "url": "http://www.futurealoof.com", - "licenseFile": "LICENSE" - }, - "form-data@2.3.3": { - "licenses": "MIT", - "repository": "https://github.com/form-data/form-data", - "publisher": "Felix Geisendörfer", - "email": "felix@debuggable.com", - "url": "http://debuggable.com/", - "licenseFile": "License" - }, - "forwarded@0.1.2": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/forwarded", - "licenseFile": "LICENSE" - }, - "fragment-cache@0.2.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/fragment-cache", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "fresh@0.5.2": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/fresh", - "publisher": "TJ Holowaychuk", - "email": "tj@vision-media.ca", - "url": "http://tjholowaychuk.com", - "licenseFile": "LICENSE" - }, - "fs-extra@7.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jprichardson/node-fs-extra", - "publisher": "JP Richardson", - "email": "jprichardson@gmail.com", - "licenseFile": "LICENSE" - }, - "fs-extra@8.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jprichardson/node-fs-extra", - "publisher": "JP Richardson", - "email": "jprichardson@gmail.com", - "licenseFile": "LICENSE" - }, - "fs-minipass@1.2.5": { - "licenses": "ISC", - "repository": "https://github.com/npm/fs-minipass", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "fs.realpath@1.0.0": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/fs.realpath", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "fsevents@1.2.12": { - "licenses": "MIT", - "repository": "https://github.com/strongloop/fsevents", - "publisher": "Philipp Dunkel", - "email": "pip@pipobscure.com", - "licenseFile": "LICENSE" - }, - "function-bind@1.1.1": { - "licenses": "MIT", - "repository": "https://github.com/Raynos/function-bind", - "publisher": "Raynos", - "email": "raynos2@gmail.com", - "licenseFile": "LICENSE" - }, - "gauge@2.7.4": { - "licenses": "ISC", - "repository": "https://github.com/iarna/gauge", - "publisher": "Rebecca Turner", - "email": "me@re-becca.org", - "licenseFile": "LICENSE" - }, - "get-stream@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/get-stream", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "get-stream@4.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/get-stream", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "get-value@2.0.6": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/get-value", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "getpass@0.1.7": { - "licenses": "MIT", - "repository": "https://github.com/arekinath/node-getpass", - "publisher": "Alex Wilson", - "email": "alex.wilson@joyent.com", - "licenseFile": "LICENSE" - }, - "glob-base@0.3.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/glob-base", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "glob-parent@2.0.0": { - "licenses": "ISC", - "repository": "https://github.com/es128/glob-parent", - "publisher": "Elan Shanker", - "licenseFile": "LICENSE" - }, - "glob-parent@3.1.0": { - "licenses": "ISC", - "repository": "https://github.com/es128/glob-parent", - "publisher": "Elan Shanker", - "url": "https://github.com/es128", - "licenseFile": "LICENSE" - }, - "glob-to-regexp@0.3.0": { - "licenses": "BSD*", - "repository": "https://github.com/fitzgen/glob-to-regexp", - "publisher": "Nick Fitzgerald", - "email": "fitzgen@gmail.com", - "licenseFile": "README.md" - }, - "glob@7.1.2": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/node-glob", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "glob@7.1.3": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/node-glob", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "glob@7.1.4": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/node-glob", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "global-dirs@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/global-dirs", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "globby@9.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/globby", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "got@6.7.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/got", - "licenseFile": "license" - }, - "graceful-fs@4.1.11": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/node-graceful-fs", - "licenseFile": "LICENSE" - }, - "graceful-fs@4.2.2": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/node-graceful-fs", - "licenseFile": "LICENSE" - }, - "har-schema@2.0.0": { - "licenses": "ISC", - "repository": "https://github.com/ahmadnassri/har-schema", - "publisher": "Ahmad Nassri", - "email": "ahmad@ahmadnassri.com", - "url": "https://www.ahmadnassri.com/", - "licenseFile": "LICENSE" - }, - "har-validator@5.1.3": { - "licenses": "MIT", - "repository": "https://github.com/ahmadnassri/node-har-validator", - "publisher": "Ahmad Nassri", - "email": "ahmad@ahmadnassri.com", - "url": "https://www.ahmadnassri.com/", - "licenseFile": "LICENSE" - }, - "has-flag@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/has-flag", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "has-unicode@2.0.1": { - "licenses": "ISC", - "repository": "https://github.com/iarna/has-unicode", - "publisher": "Rebecca Turner", - "email": "me@re-becca.org", - "licenseFile": "LICENSE" - }, - "has-value@0.3.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/has-value", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "has-value@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/has-value", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "has-values@0.1.4": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/has-values", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "has-values@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/has-values", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "has@1.0.3": { - "licenses": "MIT", - "repository": "https://github.com/tarruda/has", - "publisher": "Thiago de Arruda", - "email": "tpadilha84@gmail.com", - "licenseFile": "LICENSE-MIT" - }, - "hosted-git-info@2.6.0": { - "licenses": "ISC", - "repository": "https://github.com/npm/hosted-git-info", - "publisher": "Rebecca Turner", - "email": "me@re-becca.org", - "url": "http://re-becca.org", - "licenseFile": "LICENSE" - }, - "hosted-git-info@2.8.4": { - "licenses": "ISC", - "repository": "https://github.com/npm/hosted-git-info", - "publisher": "Rebecca Turner", - "email": "me@re-becca.org", - "url": "http://re-becca.org", - "licenseFile": "LICENSE" - }, - "http-errors@1.6.3": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/http-errors", - "publisher": "Jonathan Ong", - "email": "me@jongleberry.com", - "url": "http://jongleberry.com", - "licenseFile": "LICENSE" - }, - "http-signature@1.2.0": { - "licenses": "MIT", - "repository": "https://github.com/joyent/node-http-signature", - "publisher": "Joyent, Inc", - "licenseFile": "LICENSE" - }, - "iconv-lite@0.4.23": { - "licenses": "MIT", - "repository": "https://github.com/ashtuchkin/iconv-lite", - "publisher": "Alexander Shtuchkin", - "email": "ashtuchkin@gmail.com", - "licenseFile": "LICENSE" - }, - "iconv-lite@0.4.24": { - "licenses": "MIT", - "repository": "https://github.com/ashtuchkin/iconv-lite", - "publisher": "Alexander Shtuchkin", - "email": "ashtuchkin@gmail.com", - "licenseFile": "LICENSE" - }, - "ignore-walk@3.0.1": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/ignore-walk", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "ignore@4.0.6": { - "licenses": "MIT", - "repository": "https://github.com/kaelzhang/node-ignore", - "publisher": "kael", - "licenseFile": "LICENSE-MIT" - }, - "immediate@3.0.6": { - "licenses": "MIT", - "repository": "https://github.com/calvinmetcalf/immediate", - "licenseFile": "LICENSE.txt" - }, - "import-fresh@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/import-fresh", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "import-lazy@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/import-lazy", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "imurmurhash@0.1.4": { - "licenses": "MIT", - "repository": "https://github.com/jensyt/imurmurhash-js", - "publisher": "Jens Taylor", - "email": "jensyt@gmail.com", - "url": "https://github.com/homebrewing", - "licenseFile": "README.md" - }, - "indent-string@3.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/indent-string", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "inflight@1.0.6": { - "licenses": "ISC", - "repository": "https://github.com/npm/inflight", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "inherits@2.0.3": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/inherits", - "licenseFile": "LICENSE" - }, - "inherits@2.0.4": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/inherits", - "licenseFile": "LICENSE" - }, - "ini@1.3.5": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/ini", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "init-package-json@1.10.3": { - "licenses": "ISC", - "repository": "https://github.com/npm/init-package-json", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "inquirer@6.5.2": { - "licenses": "MIT", - "repository": "https://github.com/SBoudrias/Inquirer.js", - "publisher": "Simon Boudrias", - "email": "admin@simonboudrias.com", - "licenseFile": "LICENSE" - }, - "insight@0.10.3": { - "licenses": "BSD-2-Clause", - "repository": "https://github.com/yeoman/insight", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "install@0.8.9": { - "licenses": "MIT", - "repository": "https://github.com/benjamn/install", - "publisher": "Ben Newman", - "email": "bn@cs.stanford.edu", - "licenseFile": "LICENSE" - }, - "ionic-angular@3.9.9": { - "licenses": "MIT", - "repository": "https://github.com/ionic-team/ionic", - "licenseFile": "README.md" - }, - "ionicons@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/driftyco/ionicons", - "publisher": "Ben Sperry", - "url": "https://twitter.com/bensperry", - "licenseFile": "LICENSE" - }, - "ios-sim@8.0.2": { - "licenses": "MIT", - "repository": "https://github.com/ios-control/ios-sim", - "publisher": "Shazron Abdullah", - "licenseFile": "LICENSE" - }, - "ip-regex@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/ip-regex", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "ipaddr.js@1.8.0": { - "licenses": "MIT", - "repository": "https://github.com/whitequark/ipaddr.js", - "publisher": "whitequark", - "email": "whitequark@whitequark.org", - "licenseFile": "README.md" - }, - "is-accessor-descriptor@0.1.6": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-accessor-descriptor", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-accessor-descriptor@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-accessor-descriptor", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-binary-path@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-binary-path", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-buffer@1.1.6": { - "licenses": "MIT", - "repository": "https://github.com/feross/is-buffer", - "publisher": "Feross Aboukhadijeh", - "email": "feross@feross.org", - "url": "http://feross.org/", - "licenseFile": "LICENSE" - }, - "is-builtin-module@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-builtin-module", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-callable@1.1.3": { - "licenses": "MIT", - "repository": "https://github.com/ljharb/is-callable", - "publisher": "Jordan Harband", - "email": "ljharb@gmail.com", - "url": "http://ljharb.codes", - "licenseFile": "LICENSE" - }, - "is-ci@1.1.0": { - "licenses": "MIT", - "repository": "https://github.com/watson/is-ci", - "publisher": "Thomas Watson Steen", - "email": "w@tson.dk", - "url": "https://twitter.com/wa7son", - "licenseFile": "LICENSE" - }, - "is-data-descriptor@0.1.4": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-data-descriptor", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-data-descriptor@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-data-descriptor", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-date-object@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/ljharb/is-date-object", - "publisher": "Jordan Harband", - "licenseFile": "LICENSE" - }, - "is-descriptor@0.1.6": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-descriptor", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-descriptor@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-descriptor", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-dotfile@1.0.3": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-dotfile", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-equal-shallow@0.1.3": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-equal-shallow", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-extendable@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-extendable", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-extendable@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-extendable", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-extglob@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-extglob", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-extglob@2.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-extglob", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-fullwidth-code-point@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-fullwidth-code-point", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-fullwidth-code-point@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-fullwidth-code-point", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-glob@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-glob", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-glob@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-glob", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-glob@4.0.1": { - "licenses": "MIT", - "repository": "https://github.com/micromatch/is-glob", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-installed-globally@0.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-installed-globally", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-npm@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-npm", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "http://sindresorhus.com", - "licenseFile": "readme.md" - }, - "is-number@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-number", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-number@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-number", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-number@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-number", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-obj@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-obj", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-odd@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-odd", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-path-inside@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-path-inside", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-plain-object@2.0.4": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-plain-object", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-posix-bracket@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-posix-bracket", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-primitive@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-primitive", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-promise@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/then/is-promise", - "publisher": "ForbesLindesay", - "licenseFile": "LICENSE" - }, - "is-redirect@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-redirect", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-regex@1.0.4": { - "licenses": "MIT", - "repository": "https://github.com/ljharb/is-regex", - "publisher": "Jordan Harband", - "licenseFile": "LICENSE" - }, - "is-retry-allowed@1.2.0": { - "licenses": "MIT", - "repository": "https://github.com/floatdrop/is-retry-allowed", - "publisher": "Vsevolod Strukchinsky", - "email": "floatdrop@gmail.com", - "url": "github.com/floatdrop", - "licenseFile": "license" - }, - "is-stream@1.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-stream", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "is-symbol@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/ljharb/is-symbol", - "publisher": "Jordan Harband", - "licenseFile": "LICENSE" - }, - "is-typedarray@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/hughsk/is-typedarray", - "publisher": "Hugh Kennedy", - "email": "hughskennedy@gmail.com", - "url": "http://hughsk.io/", - "licenseFile": "LICENSE.md" - }, - "is-url@1.2.4": { - "licenses": "MIT", - "repository": "https://github.com/segmentio/is-url", - "licenseFile": "LICENSE-MIT" - }, - "is-windows@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/is-windows", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "is-wsl@1.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/is-wsl", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "isarray@0.0.1": { - "licenses": "MIT", - "repository": "https://github.com/juliangruber/isarray", - "publisher": "Julian Gruber", - "email": "mail@juliangruber.com", - "url": "http://juliangruber.com", - "licenseFile": "README.md" - }, - "isarray@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/juliangruber/isarray", - "publisher": "Julian Gruber", - "email": "mail@juliangruber.com", - "url": "http://juliangruber.com", - "licenseFile": "README.md" - }, - "isexe@2.0.0": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/isexe", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "isobject@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/isobject", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "isobject@3.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/isobject", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "isstream@0.1.2": { - "licenses": "MIT", - "repository": "https://github.com/rvagg/isstream", - "publisher": "Rod Vagg", - "email": "rod@vagg.org", - "licenseFile": "LICENSE.md" - }, - "jsbn@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/andyperlitch/jsbn", - "publisher": "Tom Wu", - "licenseFile": "LICENSE" - }, - "json-parse-better-errors@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/zkat/json-parse-better-errors", - "publisher": "Kat Marchán", - "email": "kzm@zkat.tech", - "licenseFile": "LICENSE.md" - }, - "json-schema-traverse@0.4.1": { - "licenses": "MIT", - "repository": "https://github.com/epoberezkin/json-schema-traverse", - "publisher": "Evgeny Poberezkin", - "licenseFile": "LICENSE" - }, - "json-schema@0.2.3": { - "licenses": [ - "AFLv2.1", - "BSD" - ], - "repository": "https://github.com/kriszyp/json-schema", - "publisher": "Kris Zyp", - "licenseFile": "README.md" - }, - "json-stringify-safe@5.0.1": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/json-stringify-safe", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me", - "licenseFile": "LICENSE" - }, - "jsonfile@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jprichardson/node-jsonfile", - "publisher": "JP Richardson", - "email": "jprichardson@gmail.com", - "licenseFile": "LICENSE" - }, - "jsprim@1.4.1": { - "licenses": "MIT", - "repository": "https://github.com/joyent/node-jsprim", - "licenseFile": "LICENSE" - }, - "jszip@3.1.5": { - "licenses": "(MIT OR GPL-3.0)", - "repository": "https://github.com/Stuk/jszip", - "publisher": "Stuart Knightley", - "email": "stuart@stuartk.com", - "licenseFile": "LICENSE.markdown" - }, - "kind-of@3.2.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/kind-of", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "kind-of@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/kind-of", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "kind-of@5.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/kind-of", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "kind-of@6.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/kind-of", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "kind-of@6.0.3": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/kind-of", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "latest-version@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/latest-version", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "lie@3.1.1": { - "licenses": "MIT", - "repository": "https://github.com/calvinmetcalf/lie", - "licenseFile": "license.md" - }, - "locate-path@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/locate-path", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "lodash.debounce@4.0.8": { - "licenses": "MIT", - "repository": "https://github.com/lodash/lodash", - "publisher": "John-David Dalton", - "email": "john.david.dalton@gmail.com", - "url": "http://allyoucanleet.com/", - "licenseFile": "LICENSE" - }, - "lodash@4.17.15": { - "licenses": "MIT", - "repository": "https://github.com/lodash/lodash", - "publisher": "John-David Dalton", - "email": "john.david.dalton@gmail.com", - "licenseFile": "LICENSE" - }, - "loud-rejection@2.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/loud-rejection", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "lowercase-keys@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/lowercase-keys", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "lru-cache@4.1.5": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/node-lru-cache", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "licenseFile": "LICENSE" - }, - "macos-release@2.3.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/macos-release", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "make-dir@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/make-dir", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "map-cache@0.2.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/map-cache", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "map-visit@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/map-visit", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "math-random@1.0.4": { - "licenses": "MIT", - "repository": "https://github.com/michaelrhodes/math-random", - "publisher": "Michael Rhodes", - "licenseFile": "readme.md" - }, - "mathjax@2.7.7": { - "licenses": "Apache-2.0", - "repository": "https://github.com/mathjax/MathJax", - "licenseFile": "LICENSE" - }, - "md5-file@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/roryrjb/md5-file", - "publisher": "Rory Bradford", - "email": "rory@dysfunctionalprogramming.com", - "licenseFile": "LICENSE.md" - }, - "media-typer@0.3.0": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/media-typer", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "merge-descriptors@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/component/merge-descriptors", - "publisher": "Jonathan Ong", - "email": "me@jongleberry.com", - "url": "http://jongleberry.com", - "licenseFile": "LICENSE" - }, - "merge2@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/teambition/merge2", - "licenseFile": "LICENSE" - }, - "methods@1.1.2": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/methods", - "licenseFile": "LICENSE" - }, - "micromatch@2.3.11": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/micromatch", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "micromatch@3.1.10": { - "licenses": "MIT", - "repository": "https://github.com/micromatch/micromatch", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "mime-db@1.38.0": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/mime-db", - "licenseFile": "LICENSE" - }, - "mime-db@1.42.0": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/mime-db", - "licenseFile": "LICENSE" - }, - "mime-types@2.1.22": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/mime-types", - "licenseFile": "LICENSE" - }, - "mime@1.4.1": { - "licenses": "MIT", - "repository": "https://github.com/broofa/node-mime", - "publisher": "Robert Kieffer", - "email": "robert@broofa.com", - "url": "http://github.com/broofa", - "licenseFile": "LICENSE" - }, - "mimic-fn@1.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/mimic-fn", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "minimatch@3.0.4": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/minimatch", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me", - "licenseFile": "LICENSE" - }, - "minimist@0.0.8": { - "licenses": "MIT", - "repository": "https://github.com/substack/minimist", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "minimist@1.2.5": { - "licenses": "MIT", - "repository": "https://github.com/substack/minimist", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "minipass@2.9.0": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/minipass", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "minizlib@1.2.1": { - "licenses": "MIT", - "repository": "https://github.com/isaacs/minizlib", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "mixin-deep@1.3.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/mixin-deep", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "mkdirp@0.5.1": { - "licenses": "MIT", - "repository": "https://github.com/substack/node-mkdirp", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "mkdirp@0.5.5": { - "licenses": "MIT", - "repository": "https://github.com/substack/node-mkdirp", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "moment@2.24.0": { - "licenses": "MIT", - "repository": "https://github.com/moment/moment", - "publisher": "Iskren Ivov Chernev", - "email": "iskren.chernev@gmail.com", - "url": "https://github.com/ichernev", - "licenseFile": "LICENSE" - }, - "moodlemobile@3.9.2": { - "licenses": "Apache-2.0", - "repository": "https://github.com/moodlehq/moodlemobile2", - "publisher": "Moodle Pty Ltd.", - "email": "mobile@moodle.com", - "licenseFile": "LICENSE", - "noticeFile": "NOTICE" - }, - "ms@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/zeit/ms", - "licenseFile": "license.md" - }, - "ms@2.1.2": { - "licenses": "MIT", - "repository": "https://github.com/zeit/ms", - "licenseFile": "license.md" - }, - "mute-stream@0.0.7": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/mute-stream", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "mute-stream@0.0.8": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/mute-stream", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "nan@2.14.0": { - "licenses": "MIT", - "repository": "https://github.com/nodejs/nan", - "licenseFile": "LICENSE.md" - }, - "nanomatch@1.2.9": { - "licenses": "MIT", - "repository": "https://github.com/micromatch/nanomatch", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "needle@2.3.0": { - "licenses": "MIT", - "repository": "https://github.com/tomas/needle", - "publisher": "Tomás Pollak", - "email": "tomas@forkhq.com", - "licenseFile": "license.txt" - }, - "negotiator@0.6.1": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/negotiator", - "licenseFile": "LICENSE" - }, - "nice-try@1.0.5": { - "licenses": "MIT", - "repository": "https://github.com/electerious/nice-try", - "licenseFile": "LICENSE" - }, - "nl.kingsquare.cordova.background-audio@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/AubreyHewes/cordova-background-audio", - "licenseFile": "LICENSE" - }, - "node-pre-gyp@0.12.0": { - "licenses": "BSD-3-Clause", - "repository": "https://github.com/mapbox/node-pre-gyp", - "publisher": "Dane Springmeyer", - "email": "dane@mapbox.com", - "licenseFile": "LICENSE" - }, - "nopt@1.0.9": { - "licenses": "MIT", - "repository": "https://github.com/isaacs/nopt", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "nopt@4.0.1": { - "licenses": "ISC", - "repository": "https://github.com/npm/nopt", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "nopt@4.0.3": { - "licenses": "ISC", - "repository": "https://github.com/npm/nopt", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "normalize-package-data@2.4.0": { - "licenses": "BSD-2-Clause", - "repository": "https://github.com/npm/normalize-package-data", - "publisher": "Meryn Stol", - "email": "merynstol@gmail.com", - "licenseFile": "LICENSE" - }, - "normalize-path@2.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/normalize-path", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "npm-bundled@1.0.6": { - "licenses": "ISC", - "repository": "https://github.com/npm/npm-bundled", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "npm-package-arg@6.1.1": { - "licenses": "ISC", - "repository": "https://github.com/npm/npm-package-arg", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "npm-packlist@1.4.1": { - "licenses": "ISC", - "repository": "https://github.com/npm/npm-packlist", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "npm-run-path@2.0.2": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/npm-run-path", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "npmlog@4.1.2": { - "licenses": "ISC", - "repository": "https://github.com/npm/npmlog", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "number-is-nan@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/number-is-nan", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "oauth-sign@0.9.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/mikeal/oauth-sign", - "publisher": "Mikeal Rogers", - "email": "mikeal.rogers@gmail.com", - "url": "http://www.futurealoof.com", - "licenseFile": "LICENSE" - }, - "object-assign@4.1.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/object-assign", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "object-copy@0.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/object-copy", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "object-inspect@1.6.0": { - "licenses": "MIT", - "repository": "https://github.com/substack/object-inspect", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "object-keys@1.0.11": { - "licenses": "MIT", - "repository": "https://github.com/ljharb/object-keys", - "publisher": "Jordan Harband", - "email": "ljharb@gmail.com", - "url": "http://ljharb.codes", - "licenseFile": "LICENSE" - }, - "object-visit@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/object-visit", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "object.omit@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/object.omit", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "object.pick@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/object.pick", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "objectorarray@1.0.3": { - "licenses": "ISC", - "repository": "https://github.com/ZhouHansen/objectnotnull", - "publisher": "zhouhancheng", - "email": "z308114274@gmail.com", - "licenseFile": "LICENSE" - }, - "on-finished@2.3.0": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/on-finished", - "licenseFile": "LICENSE" - }, - "on-headers@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/on-headers", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "once@1.4.0": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/once", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "onetime@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/onetime", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "opn@5.5.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/opn", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "os-homedir@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/os-homedir", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "os-name@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/os-name", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "os-tmpdir@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/os-tmpdir", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "osenv@0.1.5": { - "licenses": "ISC", - "repository": "https://github.com/npm/osenv", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "p-finally@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/p-finally", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "p-limit@1.3.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/p-limit", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "p-locate@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/p-locate", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "p-try@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/p-try", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "p-try@2.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/p-try", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "package-json@4.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/package-json", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "pako@1.0.6": { - "licenses": "(MIT AND Zlib)", - "repository": "https://github.com/nodeca/pako", - "licenseFile": "LICENSE" - }, - "parent-module@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/parent-module", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "parse-glob@3.0.4": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/parse-glob", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "parseurl@1.3.2": { - "licenses": "MIT", - "repository": "https://github.com/pillarjs/parseurl", - "licenseFile": "LICENSE" - }, - "pascalcase@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/pascalcase", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "path-dirname@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/es128/path-dirname", - "publisher": "Elan Shanker", - "licenseFile": "license" - }, - "path-exists@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/path-exists", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "path-is-absolute@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/path-is-absolute", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "path-is-inside@1.0.2": { - "licenses": "(WTFPL OR MIT)", - "repository": "https://github.com/domenic/path-is-inside", - "publisher": "Domenic Denicola", - "email": "d@domenic.me", - "url": "https://domenic.me", - "licenseFile": "LICENSE.txt" - }, - "path-key@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/path-key", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "path-parse@1.0.6": { - "licenses": "MIT", - "repository": "https://github.com/jbgutierrez/path-parse", - "publisher": "Javier Blanco", - "email": "http://jbgutierrez.info", - "licenseFile": "LICENSE" - }, - "path-to-regexp@0.1.7": { - "licenses": "MIT", - "repository": "https://github.com/component/path-to-regexp", - "licenseFile": "LICENSE" - }, - "path-to-regexp@1.7.0": { - "licenses": "MIT", - "repository": "https://github.com/pillarjs/path-to-regexp", - "licenseFile": "LICENSE" - }, - "path-type@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/path-type", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "performance-now@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/braveg1rl/performance-now", - "publisher": "Braveg1rl", - "email": "braveg1rl@outlook.com", - "licenseFile": "license.txt" - }, - "phonegap-plugin-multidex@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/phonegap/phonegap-plugin-multidex", - "publisher": "Adobe PhoneGap Team", - "licenseFile": "README.md" - }, - "phonegap-plugin-push@2.2.3": { - "licenses": "MIT", - "repository": "https://github.com/phonegap/phonegap-plugin-push", - "publisher": "Adobe PhoneGap Team", - "licenseFile": "README.md" - }, - "pify@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/pify", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "pify@4.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/pify", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "pkg-up@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/pkg-up", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "plist@3.0.1": { - "licenses": "MIT", - "repository": "https://github.com/TooTallNate/node-plist", - "publisher": "Nathan Rajlich", - "email": "nathan@tootallnate.net", - "licenseFile": "LICENSE" - }, - "posix-character-classes@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/posix-character-classes", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "prepend-http@1.0.4": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/prepend-http", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "preserve@0.2.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/preserve", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "process-nextick-args@1.0.7": { - "licenses": "MIT", - "repository": "https://github.com/calvinmetcalf/process-nextick-args", - "licenseFile": "license.md" - }, - "process-nextick-args@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/calvinmetcalf/process-nextick-args", - "licenseFile": "license.md" - }, - "promise.prototype.finally@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/es-shims/Promise.prototype.finally", - "publisher": "Jordan Harband", - "licenseFile": "LICENSE" - }, - "promzard@0.3.0": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/promzard", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "properties-parser@0.3.1": { - "licenses": "MIT", - "repository": "https://github.com/xavi-/node-properties-parser", - "licenseFile": "README.markdown" - }, - "proxy-addr@2.0.4": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/proxy-addr", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "pseudomap@1.0.2": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/pseudomap", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "psl@1.1.29": { - "licenses": "MIT", - "repository": "https://github.com/wrangr/psl", - "publisher": "Lupo Montero", - "licenseFile": "README.md" - }, - "pump@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/mafintosh/pump", - "publisher": "Mathias Buus Madsen", - "email": "mathiasbuus@gmail.com", - "licenseFile": "LICENSE" - }, - "punycode@1.4.1": { - "licenses": "MIT", - "repository": "https://github.com/bestiejs/punycode.js", - "publisher": "Mathias Bynens", - "url": "https://mathiasbynens.be/", - "licenseFile": "LICENSE-MIT.txt" - }, - "punycode@2.1.1": { - "licenses": "MIT", - "repository": "https://github.com/bestiejs/punycode.js", - "publisher": "Mathias Bynens", - "url": "https://mathiasbynens.be/", - "licenseFile": "LICENSE-MIT.txt" - }, - "q@1.5.1": { - "licenses": "MIT", - "repository": "https://github.com/kriskowal/q", - "publisher": "Kris Kowal", - "email": "kris@cixar.com", - "url": "https://github.com/kriskowal", - "licenseFile": "LICENSE" - }, - "qrcode-reader@1.0.4": { - "licenses": "Apache-2.0", - "repository": "https://github.com/edi9999/jsqrcode", - "licenseFile": "COPYING" - }, - "qs@6.5.2": { - "licenses": "BSD-3-Clause", - "repository": "https://github.com/ljharb/qs", - "licenseFile": "LICENSE" - }, - "randomatic@3.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/randomatic", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "range-parser@1.2.0": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/range-parser", - "publisher": "TJ Holowaychuk", - "email": "tj@vision-media.ca", - "url": "http://tjholowaychuk.com", - "licenseFile": "LICENSE" - }, - "raw-body@2.3.3": { - "licenses": "MIT", - "repository": "https://github.com/stream-utils/raw-body", - "publisher": "Jonathan Ong", - "email": "me@jongleberry.com", - "url": "http://jongleberry.com", - "licenseFile": "LICENSE" - }, - "rc@1.2.8": { - "licenses": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "repository": "https://github.com/dominictarr/rc", - "publisher": "Dominic Tarr", - "email": "dominic.tarr@gmail.com", - "url": "dominictarr.com", - "licenseFile": "LICENSE.APACHE2" - }, - "read-chunk@3.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/read-chunk", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "read-package-json@2.1.0": { - "licenses": "ISC", - "repository": "https://github.com/npm/read-package-json", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "read@1.0.7": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/read", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "readable-stream@2.0.6": { - "licenses": "MIT", - "repository": "https://github.com/nodejs/readable-stream", - "licenseFile": "LICENSE" - }, - "readable-stream@2.3.6": { - "licenses": "MIT", - "repository": "https://github.com/nodejs/readable-stream", - "licenseFile": "LICENSE" - }, - "readdirp@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/thlorenz/readdirp", - "publisher": "Thorsten Lorenz", - "email": "thlorenz@gmx.de", - "url": "thlorenz.com", - "licenseFile": "LICENSE" - }, - "reflect-metadata@0.1.12": { - "licenses": "Apache-2.0", - "repository": "https://github.com/rbuckton/reflect-metadata", - "publisher": "Ron Buckton", - "email": "ron.buckton@microsoft.com", - "url": "http://github.com/rbuckton", - "licenseFile": "LICENSE" - }, - "regex-cache@0.4.4": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/regex-cache", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "regex-not@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/regex-not", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "registry-auth-token@3.4.0": { - "licenses": "MIT", - "repository": "https://github.com/rexxars/registry-auth-token", - "publisher": "Espen Hovlandsdal", - "email": "espen@hovlandsdal.com", - "licenseFile": "LICENSE" - }, - "registry-url@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/registry-url", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "remove-trailing-separator@1.1.0": { - "licenses": "ISC", - "repository": "https://github.com/darsain/remove-trailing-separator", - "publisher": "darsain", - "licenseFile": "license" - }, - "repeat-element@1.1.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/repeat-element", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "repeat-string@1.6.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/repeat-string", - "publisher": "Jon Schlinkert", - "url": "http://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "request@2.88.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/request/request", - "publisher": "Mikeal Rogers", - "email": "mikeal.rogers@gmail.com", - "licenseFile": "LICENSE" - }, - "resolve-from@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/resolve-from", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "resolve-url@0.2.1": { - "licenses": "MIT", - "repository": "https://github.com/lydell/resolve-url", - "publisher": "Simon Lydell", - "licenseFile": "LICENSE" - }, - "resolve@1.11.1": { - "licenses": "MIT", - "repository": "https://github.com/browserify/resolve", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "resolve@1.12.0": { - "licenses": "MIT", - "repository": "https://github.com/browserify/resolve", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "restore-cursor@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/restore-cursor", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "resumer@0.0.0": { - "licenses": "MIT", - "repository": "https://github.com/substack/resumer", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "ret@0.1.15": { - "licenses": "MIT", - "repository": "https://github.com/fent/ret.js", - "publisher": "Roly Fentanes", - "url": "https://github.com/fent", - "licenseFile": "LICENSE" - }, - "rimraf@2.6.3": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/rimraf", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "run-async@2.3.0": { - "licenses": "MIT", - "repository": "https://github.com/SBoudrias/run-async", - "publisher": "Simon Boudrias", - "email": "admin@simonboudrias.com", - "licenseFile": "LICENSE" - }, - "rxjs@5.5.12": { - "licenses": "Apache-2.0", - "repository": "https://github.com/ReactiveX/RxJS", - "publisher": "Ben Lesh", - "email": "ben@benlesh.com", - "licenseFile": "LICENSE.txt" - }, - "rxjs@6.5.3": { - "licenses": "Apache-2.0", - "repository": "https://github.com/reactivex/rxjs", - "publisher": "Ben Lesh", - "email": "ben@benlesh.com", - "licenseFile": "LICENSE.txt" - }, - "safe-buffer@5.1.2": { - "licenses": "MIT", - "repository": "https://github.com/feross/safe-buffer", - "publisher": "Feross Aboukhadijeh", - "email": "feross@feross.org", - "url": "http://feross.org", - "licenseFile": "LICENSE" - }, - "safe-regex@1.1.0": { - "licenses": "MIT", - "repository": "https://github.com/substack/safe-regex", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "safer-buffer@2.1.2": { - "licenses": "MIT", - "repository": "https://github.com/ChALkeR/safer-buffer", - "publisher": "Nikita Skovoroda", - "email": "chalkerx@gmail.com", - "url": "https://github.com/ChALkeR", - "licenseFile": "LICENSE" - }, - "sax@1.1.4": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/sax-js", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "sax@1.2.4": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/sax-js", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "sdp@1.5.4": { - "licenses": "MIT", - "repository": "https://github.com/fippo/sdp", - "publisher": "Philipp Hancke", - "licenseFile": "LICENSE" - }, - "semver-diff@2.1.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/semver-diff", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "http://sindresorhus.com", - "licenseFile": "license" - }, - "semver@5.5.0": { - "licenses": "ISC", - "repository": "https://github.com/npm/node-semver", - "licenseFile": "LICENSE" - }, - "semver@5.6.0": { - "licenses": "ISC", - "repository": "https://github.com/npm/node-semver", - "licenseFile": "LICENSE" - }, - "semver@5.7.0": { - "licenses": "ISC", - "repository": "https://github.com/npm/node-semver", - "licenseFile": "LICENSE" - }, - "semver@5.7.1": { - "licenses": "ISC", - "repository": "https://github.com/npm/node-semver", - "licenseFile": "LICENSE" - }, - "semver@6.3.0": { - "licenses": "ISC", - "repository": "https://github.com/npm/node-semver", - "licenseFile": "LICENSE" - }, - "send@0.16.2": { - "licenses": "MIT", - "repository": "https://github.com/pillarjs/send", - "publisher": "TJ Holowaychuk", - "email": "tj@vision-media.ca", - "licenseFile": "LICENSE" - }, - "serve-static@1.13.2": { - "licenses": "MIT", - "repository": "https://github.com/expressjs/serve-static", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "serviceworker-cache-polyfill@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/coonsta/cache-polyfill", - "licenseFile": "LICENSE" - }, - "set-blocking@2.0.0": { - "licenses": "ISC", - "repository": "https://github.com/yargs/set-blocking", - "publisher": "Ben Coe", - "email": "ben@npmjs.com", - "licenseFile": "LICENSE.txt" - }, - "set-immediate-shim@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/set-immediate-shim", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "readme.md" - }, - "set-value@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/set-value", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "setprototypeof@1.1.0": { - "licenses": "ISC", - "repository": "https://github.com/wesleytodd/setprototypeof", - "publisher": "Wes Todd", - "licenseFile": "LICENSE" - }, - "shebang-command@1.2.0": { - "licenses": "MIT", - "repository": "https://github.com/kevva/shebang-command", - "publisher": "Kevin Martensson", - "email": "kevinmartensson@gmail.com", - "url": "github.com/kevva", - "licenseFile": "license" - }, - "shebang-regex@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/shebang-regex", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "shelljs@0.2.6": { - "licenses": "BSD*", - "repository": "https://github.com/arturadib/shelljs", - "publisher": "Artur Adib", - "email": "aadib@mozilla.com", - "licenseFile": "LICENSE" - }, - "shelljs@0.5.3": { - "licenses": "BSD*", - "repository": "https://github.com/arturadib/shelljs", - "publisher": "Artur Adib", - "email": "arturadib@gmail.com", - "licenseFile": "LICENSE" - }, - "signal-exit@3.0.2": { - "licenses": "ISC", - "repository": "https://github.com/tapjs/signal-exit", - "publisher": "Ben Coe", - "email": "ben@npmjs.com", - "licenseFile": "LICENSE.txt" - }, - "simctl@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/ios-control/simctl", - "publisher": "Shazron Abdullah", - "licenseFile": "LICENSE" - }, - "simple-plist@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/wollardj/node-simple-plist", - "publisher": "Joe Wollard", - "licenseFile": "LICENSE" - }, - "slash@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/slash", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "http://sindresorhus.com", - "licenseFile": "readme.md" - }, - "slash@2.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/slash", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "snapdragon-node@2.1.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/snapdragon-node", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "snapdragon-util@3.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/snapdragon-util", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "snapdragon@0.8.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/snapdragon", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "source-map-resolve@0.5.2": { - "licenses": "MIT", - "repository": "https://github.com/lydell/source-map-resolve", - "publisher": "Simon Lydell", - "licenseFile": "LICENSE" - }, - "source-map-support@0.5.6": { - "licenses": "MIT", - "repository": "https://github.com/evanw/node-source-map-support", - "licenseFile": "LICENSE.md" - }, - "source-map-url@0.4.0": { - "licenses": "MIT", - "repository": "https://github.com/lydell/source-map-url", - "publisher": "Simon Lydell", - "licenseFile": "LICENSE" - }, - "source-map@0.5.7": { - "licenses": "BSD-3-Clause", - "repository": "https://github.com/mozilla/source-map", - "publisher": "Nick Fitzgerald", - "email": "nfitzgerald@mozilla.com", - "licenseFile": "LICENSE" - }, - "source-map@0.6.1": { - "licenses": "BSD-3-Clause", - "repository": "https://github.com/mozilla/source-map", - "publisher": "Nick Fitzgerald", - "email": "nfitzgerald@mozilla.com", - "licenseFile": "LICENSE" - }, - "spdx-correct@3.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/jslicense/spdx-correct.js", - "publisher": "Kyle E. Mitchell", - "email": "kyle@kemitchell.com", - "url": "https://kemitchell.com", - "licenseFile": "LICENSE" - }, - "spdx-exceptions@2.1.0": { - "licenses": "CC-BY-3.0", - "repository": "https://github.com/kemitchell/spdx-exceptions.json", - "publisher": "The Linux Foundation", - "licenseFile": "README.md" - }, - "spdx-expression-parse@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jslicense/spdx-expression-parse.js", - "publisher": "Kyle E. Mitchell", - "email": "kyle@kemitchell.com", - "url": "http://kemitchell.com", - "licenseFile": "LICENSE" - }, - "spdx-license-ids@3.0.0": { - "licenses": "CC0-1.0", - "repository": "https://github.com/shinnn/spdx-license-ids", - "publisher": "Shinnosuke Watanabe", - "url": "https://github.com/shinnn", - "licenseFile": "README.md" - }, - "split-string@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/split-string", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "sshpk@1.14.2": { - "licenses": "MIT", - "repository": "https://github.com/arekinath/node-sshpk", - "publisher": "Joyent, Inc", - "licenseFile": "LICENSE" - }, - "static-extend@0.1.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/static-extend", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "statuses@1.4.0": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/statuses", - "licenseFile": "LICENSE" - }, - "stream-buffers@2.2.0": { - "licenses": "Unlicense", - "repository": "https://github.com/samcday/node-stream-buffer", - "publisher": "Sam Day", - "email": "me@samcday.com.au", - "licenseFile": "README.md" - }, - "string-width@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/string-width", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "string-width@2.1.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/string-width", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "string.prototype.codepointat@0.2.1": { - "licenses": "MIT", - "repository": "https://github.com/mathiasbynens/String.prototype.codePointAt", - "publisher": "Mathias Bynens", - "url": "https://mathiasbynens.be/", - "licenseFile": "LICENSE-MIT.txt" - }, - "string.prototype.trim@1.1.2": { - "licenses": "MIT", - "repository": "https://github.com/es-shims/String.prototype.trim", - "publisher": "Jordan Harband", - "email": "ljharb@gmail.com", - "url": "http://ljharb.codes", - "licenseFile": "LICENSE" - }, - "string_decoder@0.10.31": { - "licenses": "MIT", - "repository": "https://github.com/rvagg/string_decoder", - "licenseFile": "LICENSE" - }, - "string_decoder@1.1.1": { - "licenses": "MIT", - "repository": "https://github.com/nodejs/string_decoder", - "licenseFile": "LICENSE" - }, - "strip-ansi@3.0.1": { - "licenses": "MIT", - "repository": "https://github.com/chalk/strip-ansi", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "strip-ansi@4.0.0": { - "licenses": "MIT", - "repository": "https://github.com/chalk/strip-ansi", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "strip-ansi@5.2.0": { - "licenses": "MIT", - "repository": "https://github.com/chalk/strip-ansi", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "strip-bom@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/strip-bom", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "strip-eof@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/strip-eof", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "strip-json-comments@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/strip-json-comments", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "supports-color@5.4.0": { - "licenses": "MIT", - "repository": "https://github.com/chalk/supports-color", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "sw-toolbox@3.6.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/GoogleChrome/sw-toolbox", - "licenseFile": "LICENSE" - }, - "symbol-observable@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/blesh/symbol-observable", - "publisher": "Ben Lesh", - "email": "ben@benlesh.com", - "licenseFile": "license" - }, - "tail@0.4.0": { - "licenses": "MIT*", - "repository": "https://github.com/lucagrulla/node-tail", - "publisher": "Luca Grulla", - "licenseFile": "README.md" - }, - "tape@4.11.0": { - "licenses": "MIT", - "repository": "https://github.com/substack/tape", - "publisher": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net", - "licenseFile": "LICENSE" - }, - "tar@4.4.8": { - "licenses": "ISC", - "repository": "https://github.com/npm/node-tar", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "term-size@1.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/term-size", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "through@2.3.8": { - "licenses": "MIT", - "repository": "https://github.com/dominictarr/through", - "publisher": "Dominic Tarr", - "email": "dominic.tarr@gmail.com", - "url": "dominictarr.com", - "licenseFile": "LICENSE.APACHE2" - }, - "timed-out@4.0.1": { - "licenses": "MIT", - "repository": "https://github.com/floatdrop/timed-out", - "publisher": "Vsevolod Strukchinsky", - "email": "floatdrop@gmail.com", - "licenseFile": "license" - }, - "tmp@0.0.33": { - "licenses": "MIT", - "repository": "https://github.com/raszi/node-tmp", - "publisher": "KARASZI István", - "email": "github@spam.raszi.hu", - "url": "http://raszi.hu/", - "licenseFile": "LICENSE" - }, - "to-object-path@0.3.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/to-object-path", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "to-regex-range@2.1.1": { - "licenses": "MIT", - "repository": "https://github.com/micromatch/to-regex-range", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "to-regex@3.0.2": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/to-regex", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "tough-cookie@2.4.3": { - "licenses": "BSD-3-Clause", - "repository": "https://github.com/salesforce/tough-cookie", - "publisher": "Jeremy Stashewsky", - "email": "jstash@gmail.com", - "licenseFile": "LICENSE" - }, - "tough-cookie@3.0.1": { - "licenses": "BSD-3-Clause", - "repository": "https://github.com/salesforce/tough-cookie", - "publisher": "Jeremy Stashewsky", - "email": "jstash@gmail.com", - "licenseFile": "LICENSE" - }, - "ts-md5@1.2.7": { - "licenses": "MIT", - "repository": "https://github.com/cotag/ts-md5", - "licenseFile": "README.md" - }, - "tsickle@0.27.5": { - "licenses": "MIT", - "repository": "https://github.com/angular/tsickle", - "licenseFile": "LICENSE" - }, - "tslib@1.9.2": { - "licenses": "Apache-2.0", - "repository": "https://github.com/Microsoft/tslib", - "publisher": "Microsoft Corp.", - "licenseFile": "LICENSE.txt" - }, - "tunnel-agent@0.6.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/mikeal/tunnel-agent", - "publisher": "Mikeal Rogers", - "email": "mikeal.rogers@gmail.com", - "url": "http://www.futurealoof.com", - "licenseFile": "LICENSE" - }, - "tweetnacl@0.14.5": { - "licenses": "Unlicense", - "repository": "https://github.com/dchest/tweetnacl-js", - "publisher": "TweetNaCl-js contributors", - "licenseFile": "LICENSE" - }, - "type-is@1.6.16": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/type-is", - "licenseFile": "LICENSE" - }, - "typescript@2.6.2": { - "licenses": "Apache-2.0", - "repository": "https://github.com/Microsoft/TypeScript", - "publisher": "Microsoft Corp.", - "licenseFile": "LICENSE.txt" - }, - "underscore@1.2.1": { - "licenses": "MIT*", - "repository": "https://github.com/documentcloud/underscore", - "publisher": "Jeremy Ashkenas", - "email": "jeremy@documentcloud.org", - "licenseFile": "LICENSE" - }, - "underscore@1.9.1": { - "licenses": "MIT", - "repository": "https://github.com/jashkenas/underscore", - "publisher": "Jeremy Ashkenas", - "email": "jeremy@documentcloud.org", - "licenseFile": "LICENSE" - }, - "union-value@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/union-value", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "unique-string@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/unique-string", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "universalify@0.1.1": { - "licenses": "MIT", - "repository": "https://github.com/RyanZim/universalify", - "publisher": "Ryan Zimmerman", - "email": "opensrc@ryanzim.com", - "licenseFile": "LICENSE" - }, - "unorm@1.6.0": { - "licenses": "MIT*", - "repository": "https://github.com/walling/unorm", - "publisher": "Bjarke Walling", - "email": "bwp@bwp.dk", - "licenseFile": "LICENSE.md" - }, - "unpipe@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/stream-utils/unpipe", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "unset-value@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/unset-value", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "unzip-response@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/unzip-response", - "licenseFile": "license" - }, - "update-notifier@2.5.0": { - "licenses": "BSD-2-Clause", - "repository": "https://github.com/yeoman/update-notifier", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "https://sindresorhus.com", - "licenseFile": "license" - }, - "uri-js@4.2.2": { - "licenses": "BSD-2-Clause", - "repository": "https://github.com/garycourt/uri-js", - "publisher": "Gary Court", - "email": "gary.court@gmail.com", - "licenseFile": "README.md" - }, - "urix@0.1.0": { - "licenses": "MIT", - "repository": "https://github.com/lydell/urix", - "publisher": "Simon Lydell", - "licenseFile": "LICENSE" - }, - "url-parse-lax@1.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/url-parse-lax", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "use@3.1.0": { - "licenses": "MIT", - "repository": "https://github.com/jonschlinkert/use", - "publisher": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert", - "licenseFile": "LICENSE" - }, - "util-deprecate@1.0.2": { - "licenses": "MIT", - "repository": "https://github.com/TooTallNate/util-deprecate", - "publisher": "Nathan Rajlich", - "email": "nathan@tootallnate.net", - "url": "http://n8.io/", - "licenseFile": "LICENSE" - }, - "utils-merge@1.0.1": { - "licenses": "MIT", - "repository": "https://github.com/jaredhanson/utils-merge", - "publisher": "Jared Hanson", - "email": "jaredhanson@gmail.com", - "url": "http://www.jaredhanson.net/", - "licenseFile": "LICENSE" - }, - "uuid@3.3.2": { - "licenses": "MIT", - "repository": "https://github.com/kelektiv/node-uuid", - "licenseFile": "LICENSE.md" - }, - "valid-identifier@0.0.2": { - "licenses": "Apache-2.0", - "repository": "https://github.com/purplecabbage/valid-identifier", - "publisher": "Jesse MacFadyen", - "licenseFile": "LICENSE" - }, - "validate-npm-package-license@3.0.3": { - "licenses": "Apache-2.0", - "repository": "https://github.com/kemitchell/validate-npm-package-license.js", - "publisher": "Kyle E. Mitchell", - "email": "kyle@kemitchell.com", - "url": "https://kemitchell.com", - "licenseFile": "LICENSE" - }, - "validate-npm-package-name@3.0.0": { - "licenses": "ISC", - "repository": "https://github.com/npm/validate-npm-package-name", - "publisher": "zeke", - "licenseFile": "LICENSE" - }, - "vary@1.1.2": { - "licenses": "MIT", - "repository": "https://github.com/jshttp/vary", - "publisher": "Douglas Christopher Wilson", - "email": "doug@somethingdoug.com", - "licenseFile": "LICENSE" - }, - "verror@1.10.0": { - "licenses": "MIT", - "repository": "https://github.com/davepacheco/node-verror", - "licenseFile": "LICENSE" - }, - "web-animations-js@2.3.2": { - "licenses": "Apache-2.0", - "repository": "https://github.com/web-animations/web-animations-js", - "licenseFile": "README.md" - }, - "webrtc-adapter@3.4.3": { - "licenses": "BSD-3-Clause", - "repository": "https://github.com/webrtc/adapter", - "licenseFile": "LICENSE.md" - }, - "which@1.3.1": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/node-which", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me", - "licenseFile": "LICENSE" - }, - "wide-align@1.1.3": { - "licenses": "ISC", - "repository": "https://github.com/iarna/wide-align", - "publisher": "Rebecca Turner", - "email": "me@re-becca.org", - "url": "http://re-becca.org/", - "licenseFile": "LICENSE" - }, - "widest-line@2.0.1": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/widest-line", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "windows-release@3.2.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/windows-release", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "with-open-file@0.1.6": { - "licenses": "MIT", - "repository": "https://github.com/raphinesse/with-open-file", - "publisher": "Raphael von der Grün", - "email": "raphinesse@gmail.com", - "licenseFile": "LICENSE" - }, - "wrappy@1.0.2": { - "licenses": "ISC", - "repository": "https://github.com/npm/wrappy", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "write-file-atomic@2.4.3": { - "licenses": "ISC", - "repository": "https://github.com/iarna/write-file-atomic", - "publisher": "Rebecca Turner", - "email": "me@re-becca.org", - "url": "http://re-becca.org", - "licenseFile": "LICENSE" - }, - "xcode@2.0.0": { - "licenses": "Apache-2.0", - "repository": "https://github.com/apache/cordova-node-xcode", - "publisher": "Apache Software Foundation", - "licenseFile": "LICENSE" - }, - "xdg-basedir@3.0.0": { - "licenses": "MIT", - "repository": "https://github.com/sindresorhus/xdg-basedir", - "publisher": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com", - "licenseFile": "license" - }, - "xml-escape@1.1.0": { - "licenses": "MIT*", - "repository": "https://github.com/miketheprogrammer/xml-escape", - "publisher": "Michael Hernandez - michael.hernandez1988@gmail.com", - "licenseFile": "LICENSE" - }, - "xmlbuilder@9.0.7": { - "licenses": "MIT", - "repository": "https://github.com/oozcitak/xmlbuilder-js", - "publisher": "Ozgur Ozcitak", - "email": "oozcitak@gmail.com", - "licenseFile": "LICENSE" - }, - "xmldom@0.1.27": { - "licenses": "LGPL", - "repository": "https://github.com/jindw/xmldom", - "publisher": "jindw", - "email": "jindw@xidea.org", - "url": "http://www.xidea.org", - "licenseFile": "LICENSE" - }, - "yallist@2.1.2": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/yallist", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "yallist@3.0.3": { - "licenses": "ISC", - "repository": "https://github.com/isaacs/yallist", - "publisher": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/", - "licenseFile": "LICENSE" - }, - "zone.js@0.8.29": { - "licenses": "MIT", - "repository": "https://github.com/angular/zone.js", - "publisher": "Brian Ford", - "licenseFile": "LICENSE" - } -} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index c44b655e4..000000000 --- a/package-lock.json +++ /dev/null @@ -1,17411 +0,0 @@ -{ - "name": "moodlemobile", - "version": "3.9.3", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "7zip-bin": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-4.0.2.tgz", - "integrity": "sha512-XtGk+IF57pr852UK1AhQJXqmm1WmSgS5uISL+LPs0z/iAxXouMvdlLJrHPeukP6gd7yR2rDTMSMkHNODgwIq7A==", - "dev": true - }, - "@angular-devkit/build-optimizer": { - "version": "0.0.35", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.0.35.tgz", - "integrity": "sha512-7JxZZAYFSCc0tP6+NrRn3b2Cd1b9d+a3+OfwVNyNsNd2unelqUMko2hm0KLbC8BXcXt/OILg1E/ZgLAXSS47nw==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "source-map": "^0.5.6", - "typescript": "~2.6.1", - "webpack-sources": "^1.0.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@angular/animations": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-5.2.11.tgz", - "integrity": "sha512-J7wKHkFn3wV28/Y1Qm4yjGXVCwXzj1JR5DRjGDTFnxTRacUFx7Nj0ApGhN0b2+V0NOvgxQOvEW415Y22kGoblw==", - "requires": { - "tslib": "^1.7.1" - } - }, - "@angular/common": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-5.2.11.tgz", - "integrity": "sha512-LniJjGAeftUJDJh+2+LEjltcGen08C/VMxQ/eUYmesytKy1sN+MWzh3GbpKfEWtWmyUsYTG9lAAJNo3L3jPwsw==", - "requires": { - "tslib": "^1.7.1" - } - }, - "@angular/compiler": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.2.11.tgz", - "integrity": "sha512-ICvB1ud1mxaXUYLb8vhJqiLhGBVocAZGxoHTglv6hMkbrRYcnlB3FZJFOzBvtj+krkd1jamoYLI43UAmesqQ6Q==", - "requires": { - "tslib": "^1.7.1" - } - }, - "@angular/compiler-cli": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-5.2.11.tgz", - "integrity": "sha512-dwrQ0yxoCM/XzKzlm7pTsyg4/6ECjT9emZufGj8t12bLMO8NDn1IJOsqXJA1+onEgQKhlr0Ziwi+96TvDTb1Cg==", - "requires": { - "chokidar": "^1.4.2", - "minimist": "^1.2.0", - "reflect-metadata": "^0.1.2", - "tsickle": "^0.27.2" - } - }, - "@angular/core": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-5.2.11.tgz", - "integrity": "sha512-h2vpvXNAdOqKzbVaZcHnHGMT5A8uDnizk6FgGq6SPyw9s3d+/VxZ9LJaPjUk3g2lICA7og1tUel+2YfF971MlQ==", - "requires": { - "tslib": "^1.7.1" - } - }, - "@angular/forms": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.11.tgz", - "integrity": "sha512-wBllFlIubPclAFRXUc84Kc7TMeKOftzrQraVZ7ooTNeFLLa/FZLN2K8HGyRde8X/XDsMu1XAmjNfkz++spwTzA==", - "requires": { - "tslib": "^1.7.1" - } - }, - "@angular/platform-browser": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.11.tgz", - "integrity": "sha512-6YZ4IpBFqXx88vEzBZG2WWnaSYXbFWDgG0iT+bZPHAfwsbmqbcMcs7Ogu+XZ4VmK02dTqbrFh7U4P2W+sqrzow==", - "requires": { - "tslib": "^1.7.1" - } - }, - "@angular/platform-browser-dynamic": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.11.tgz", - "integrity": "sha512-5kKPNULcXNwkyBjpHfF+pq+Yxi8Zl866YSOK9t8txoiQ9Ctw97kMkEJcTetk6MJgBp/NP3YyjtoTAm8oXLerug==", - "requires": { - "tslib": "^1.7.1" - } - }, - "@ionic-native/badge": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/badge/-/badge-4.20.0.tgz", - "integrity": "sha512-5IGGFH6Gl5sXtKg7YeyXtVBDDmD0oU/bilfGQ3MzgMsk8iochx1d+RbmnaVe4oCRJhIc47rSteYulmm+5M5hyg==" - }, - "@ionic-native/camera": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/camera/-/camera-4.20.0.tgz", - "integrity": "sha512-WnfQq8RV+7ezOqpCyNx9Xgpy7Y8TZehGLSxZXnCqCbFZ72CpC70Q5AV/eTIRGiKkotx2U6nUopYF+gTj1cunFA==" - }, - "@ionic-native/chooser": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/chooser/-/chooser-4.20.0.tgz", - "integrity": "sha512-3Qn6TqWBzs2EQV0zp0uoTRDVPPdC/EgkdzIQVnryTZg1p1NZ3L8hKuIOHms/WqFwOGMeA87dMImxSgHjw9JXJQ==" - }, - "@ionic-native/clipboard": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/clipboard/-/clipboard-4.20.0.tgz", - "integrity": "sha512-2rYK41wnxY1UnBAHyANb1hyGyM/hdfR5qg8QhTCCe8kQSpt7XApE5QVlxmytdtoYWNqF6/yNiXzYKrJjUpsn0A==" - }, - "@ionic-native/core": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-4.20.0.tgz", - "integrity": "sha512-8ppRT4zXKwtalv9HJomLQjDnMfPAiKdNUQSSKpwZePmI+8TpYRL7UNr0o0hmjiTXx1oGcKQFzHpDc1M2yeR3BA==" - }, - "@ionic-native/device": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/device/-/device-4.20.0.tgz", - "integrity": "sha512-ogHZwlC1GLbj2sL/eRp+RDs7bWc1AuwKNhgtDLE3yjXey09I5ErkADLydugMTEYoU/Wja9+YjXdZGymuaHwgNg==" - }, - "@ionic-native/diagnostic": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@ionic-native/diagnostic/-/diagnostic-4.2.0.tgz", - "integrity": "sha512-hCRYVseQrsbuA4EKgvmwJB/nweHcrBK+avE2+GyYcUFoNNhM5yz9i7mlw4J0Vw5mr2udAVhPdLyvJV5p1AkU4g==" - }, - "@ionic-native/file": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/file/-/file-4.20.0.tgz", - "integrity": "sha512-iIq/wXTw1vLMB/19dhDQEHCB3SwfiwU9X8HivM6pEvW5eFr2BkR371/1KF2LcmSDNjctG4HvrjTnRpH1uKfUfQ==" - }, - "@ionic-native/file-opener": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/file-opener/-/file-opener-4.20.0.tgz", - "integrity": "sha512-LN7aCikXQ4Bbikmn2vRjCdnNMu3cp7Nf8wlDP/qy/qN7SChTB7lvstoeCZKNYj4EFmTkE75179WCsv1CexGOpA==" - }, - "@ionic-native/file-transfer": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/file-transfer/-/file-transfer-4.20.0.tgz", - "integrity": "sha512-eZymJgIXQ4Vh8YaoHRKBJYM/1ynGYeQvLOvQks+yvlwp2tzVh4q1IVjvvA2O50zGRVFHGgdyuG4YDMWMZEZzdw==" - }, - "@ionic-native/geolocation": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/geolocation/-/geolocation-4.20.0.tgz", - "integrity": "sha512-2w/KpWuXVDqGMSDLhBut0Domy3yGolpPTvff/8xP8HGxxf1bcEKQLfoY68viOSuTPt7sg33rWWAKHf2/YOI6Lg==" - }, - "@ionic-native/globalization": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/globalization/-/globalization-4.20.0.tgz", - "integrity": "sha512-zyxaW+vZb1OHeDgGbrZHQe3hy30K4YeKjGr8KNGcwq+k2ZHkfqo/H6XIwf2m/UlFTgacvdR9XZtfP+6N0suybg==" - }, - "@ionic-native/http": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/http/-/http-4.20.0.tgz", - "integrity": "sha512-DF+Y1oYoHTv9Y22a2jLgniOmj9Twba+9j8rzHA4xboVT2HpB6bsBSWOktdAXDVjoajXiLsA/u7fh6YD8//NVGg==" - }, - "@ionic-native/in-app-browser": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/in-app-browser/-/in-app-browser-4.20.0.tgz", - "integrity": "sha512-y04sEK2ssb0ipeZTB3HPjVpGTYJUkPupjrAPx9hvbzWB0g1CHRWcsnQHezxItGz/jCQzUAKOjxQZ23oy8Qf4Bg==" - }, - "@ionic-native/keyboard": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/keyboard/-/keyboard-4.20.0.tgz", - "integrity": "sha512-GA720A+sFvYVZ/84PaZQqm+zcUu7gS/DRKNU9UGwhBdjQUmUn15EaFF+vuyxLigUfJ0YRxmAOcBDBEiIL9pDaQ==" - }, - "@ionic-native/local-notifications": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/local-notifications/-/local-notifications-4.20.0.tgz", - "integrity": "sha512-Ht/0zau8/2+G/bH/okXXhhWB6YrkCNL2QxVJHQ2dophXFGxQPOZAN3CKWhuQSjfbr76fa2nvQXF6jsXLpIR/ng==" - }, - "@ionic-native/media": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/media/-/media-4.20.0.tgz", - "integrity": "sha512-uhuTvy7MT6zFMSTDX/0aIrGu8IeRGi2FWJbWE+6o5wttAeVA6hNISSbtj4OQZhL3sUXYNCczDayV1VsOcXbdUg==" - }, - "@ionic-native/media-capture": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/media-capture/-/media-capture-4.20.0.tgz", - "integrity": "sha512-2jNNYwlk1JVJyMFb2p5xVDKc9pxI7o5WpkYWbac4+bZnOW/ZYtRI0at85JgQY6RZ6S0HOh/KyFEYeRXfeL86vA==" - }, - "@ionic-native/network": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/network/-/network-4.20.0.tgz", - "integrity": "sha512-BBSeaJJEd9KpzJmgkZwti0pzWeDO3bIVyl6/+c/Ivi1ytmJI2kL0bQcukLuABv02rCeKczVuzUgL0D4dGWtLqA==" - }, - "@ionic-native/push": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/push/-/push-4.20.0.tgz", - "integrity": "sha512-IgzaZd8KSPLwyLX1emRijlQ0Vfa3RlPPBx370lVH32c8zG3DFH1xfQQbb39KF3qmX5b6so0pGGA2holSUwVm2w==" - }, - "@ionic-native/qr-scanner": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/qr-scanner/-/qr-scanner-4.20.0.tgz", - "integrity": "sha512-eLeJQq49/x5bdCVLotuMHZZ3YGEpSzuEnuX2vno2ugdGSygBm+wxIVSa9Nuz8HozYwC6oyii+zH/pg4SZ+4V9Q==" - }, - "@ionic-native/screen-orientation": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/screen-orientation/-/screen-orientation-4.20.0.tgz", - "integrity": "sha512-vQaADwj93OFoRwDOJd/67pP/ESaXtfVfL8vinFpsBtlkBvouSTrjEzEht7ZrVfkTuKuDGFzvbFqB8DxLNf6mUg==" - }, - "@ionic-native/splash-screen": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/splash-screen/-/splash-screen-4.20.0.tgz", - "integrity": "sha512-Fubxp7mexLrkfW3BC0433IXQELKhRMB/fETPtG8BKcMiETp03Xu0xNSGpZceBhs2aDjZQhE57tcwwEzM+oHLTA==" - }, - "@ionic-native/sqlite": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/sqlite/-/sqlite-4.20.0.tgz", - "integrity": "sha512-kxQ+AQ+8yZitIjxoQ/Ke2dCeaprlDf/G8zV0uj3pN4c/F68d6OW2v2eHpuY6Ae2QGI0WSmEdQRgsjm3hjaHy2A==" - }, - "@ionic-native/status-bar": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/status-bar/-/status-bar-4.20.0.tgz", - "integrity": "sha512-y/Sqo4c0rQcLVhF/mj72wcn2ReKOW5SxBOD5I7YILNfXvEpDlUgWUxkTM5gNZn3ecCbYFrypNXIn2tsxZBAd+w==" - }, - "@ionic-native/web-intent": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/web-intent/-/web-intent-4.20.0.tgz", - "integrity": "sha512-oufDpyHdgfZYH7+xevDLfORV81Cd58ld0wnaZpUGSPKSwGkrQoqBccy7K6dX6k+iTK0o159ubaKys7gxpiArBg==" - }, - "@ionic-native/zip": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@ionic-native/zip/-/zip-4.20.0.tgz", - "integrity": "sha512-k21k+EcRPU/jpv4U55qP+aaDVIr/1Ta5/TU4KPuOxvWrSdqKHAObtkN3CfCNd3GoQEjvIE9T3nnXmnolq9yCTg==" - }, - "@ionic/app-scripts": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@ionic/app-scripts/-/app-scripts-3.2.3.tgz", - "integrity": "sha512-t1kL9NqY/sVLfAIVKfN4VJigPqMWs5euKxig3sjsrakSQ9z4njmbDghzAC4taefIpYxLGMOeWgKwrrxOiLkdWQ==", - "dev": true, - "requires": { - "@angular-devkit/build-optimizer": "0.0.35", - "autoprefixer": "^7.2.6", - "chalk": "^2.4.0", - "chokidar": "^2.0.4", - "clean-css": "^4.1.11", - "cross-spawn": "^5.1.0", - "dotenv-webpack": "^1.5.7", - "express": "^4.16.3", - "fs-extra": "^4.0.2", - "glob": "^7.1.2", - "json-loader": "^0.5.7", - "node-sass": "^4.10.0", - "os-name": "^2.0.1", - "postcss": "^6.0.21", - "proxy-middleware": "^0.15.0", - "reflect-metadata": "^0.1.10", - "rollup": "0.50.0", - "rollup-plugin-commonjs": "8.2.6", - "rollup-plugin-node-resolve": "3.0.0", - "source-map": "^0.6.1", - "tiny-lr": "^1.1.1", - "tslint": "^5.8.0", - "tslint-eslint-rules": "^4.1.1", - "uglify-es": "3.2.2", - "webpack": "3.12.0", - "ws": "3.3.2", - "xml2js": "^0.4.19" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - } - } - }, - "@ionic/cli": { - "version": "6.11.8", - "resolved": "https://registry.npmjs.org/@ionic/cli/-/cli-6.11.8.tgz", - "integrity": "sha512-AccCEMRLxzBBhpN/id6DvTAhU5NkNo/tSbuTTIbEgEZgyH6n+TlN9e7iFzmOhLKkwwwwDy7H4iYSR+xT38YaFw==", - "dev": true, - "requires": { - "@ionic/cli-framework": "5.0.4", - "@ionic/cli-framework-output": "2.2.0", - "@ionic/cli-framework-prompts": "2.1.6", - "@ionic/utils-array": "2.1.5", - "@ionic/utils-fs": "3.1.5", - "@ionic/utils-network": "2.1.5", - "@ionic/utils-process": "2.1.6", - "@ionic/utils-stream": "3.1.5", - "@ionic/utils-subprocess": "2.1.6", - "@ionic/utils-terminal": "2.2.1", - "chalk": "^4.0.0", - "debug": "^4.0.0", - "diff": "^4.0.1", - "elementtree": "^0.1.7", - "leek": "0.0.24", - "lodash": "^4.17.5", - "open": "^7.0.4", - "os-name": "^4.0.0", - "semver": "^7.1.1", - "split2": "^3.0.0", - "ssh-config": "^1.1.1", - "stream-combiner2": "^1.1.1", - "superagent": "^5.2.1", - "superagent-proxy": "^2.0.0", - "tar": "^6.0.1", - "tslib": "^2.0.1" - }, - "dependencies": { - "@ionic/utils-fs": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.5.tgz", - "integrity": "sha512-a41bY2dHqWSEQQ/80CpbXSs8McyiCFf2DnIWWLukrhYWf46h4qi6M/8dxcMKrofRiqI/3F+cL3S2mOm9Zz/o2Q==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "fs-extra": "^9.0.0", - "tslib": "^2.0.1" - } - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "macos-release": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz", - "integrity": "sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==", - "dev": true - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "os-name": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.0.tgz", - "integrity": "sha512-caABzDdJMbtykt7GmSogEat3faTKQhmZf0BS5l/pZGmP0vPWQjXWqOhbLyK+b6j2/DQPmEvYdzLXJXXLJNVDNg==", - "dev": true, - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^4.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tar": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", - "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", - "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", - "dev": true, - "requires": { - "execa": "^4.0.2" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@ionic/cli-framework": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@ionic/cli-framework/-/cli-framework-5.0.4.tgz", - "integrity": "sha512-8sO+OjvKxW/ofBngRzydWRq1ATDwHPgQ6occTVEBcSNI9wCiKfLA9GKuKRYfmVvCDX84/g3BAWr6IdErRqODHw==", - "dev": true, - "requires": { - "@ionic/cli-framework-output": "2.2.0", - "@ionic/utils-array": "2.1.5", - "@ionic/utils-fs": "3.1.5", - "@ionic/utils-object": "2.1.5", - "@ionic/utils-process": "2.1.6", - "@ionic/utils-stream": "3.1.5", - "@ionic/utils-subprocess": "2.1.6", - "@ionic/utils-terminal": "2.2.1", - "chalk": "^4.0.0", - "debug": "^4.0.0", - "lodash": "^4.17.5", - "minimist": "^1.2.0", - "rimraf": "^3.0.0", - "tslib": "^2.0.1", - "untildify": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "@ionic/utils-fs": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.5.tgz", - "integrity": "sha512-a41bY2dHqWSEQQ/80CpbXSs8McyiCFf2DnIWWLukrhYWf46h4qi6M/8dxcMKrofRiqI/3F+cL3S2mOm9Zz/o2Q==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "fs-extra": "^9.0.0", - "tslib": "^2.0.1" - } - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "dev": true - } - } - }, - "@ionic/cli-framework-output": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.0.tgz", - "integrity": "sha512-WIWLTlkG8mGOyLPxwmR1Ybwtb0FnAKfw0tl77S4Br6xj/WlKNDD0rgMZfgcn7sMOF98IqkeQgDM9rvPJrgiq4Q==", - "dev": true, - "requires": { - "@ionic/utils-terminal": "2.2.1", - "debug": "^4.0.0", - "signal-exit": "^3.0.3", - "slice-ansi": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "tslib": "^2.0.1", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "@ionic/cli-framework-prompts": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@ionic/cli-framework-prompts/-/cli-framework-prompts-2.1.6.tgz", - "integrity": "sha512-hKyXyBlOA4KBHGKSIX1uHiaviZ/V7cGTlB3ZmmfXu7xqEmSuT8NXwP3VTazIm0tXuVXMXlgKEewikaH6P9VrVA==", - "dev": true, - "requires": { - "@ionic/utils-terminal": "2.2.1", - "debug": "^4.0.0", - "inquirer": "^7.0.0", - "tslib": "^2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - } - } - }, - "@ionic/utils-array": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.5.tgz", - "integrity": "sha512-HD72a71IQVBmQckDwmA8RxNVMTbxnaLbgFOl+dO5tbvW9CkkSFCv41h6fUuNsSEVgngfkn0i98HDuZC8mk+lTA==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - } - } - }, - "@ionic/utils-fs": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.3.tgz", - "integrity": "sha512-xMZhlB1XgZQchvKZFQt6NcKgLsCdSTt3lmU0Gl0HIWdGHjwI5QSyLCYPTvfa8Wm4vCn7XYiMy05bWxWtY0+FxQ==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "fs-extra": "^9.0.0", - "through2": "^3.0.0", - "tslib": "1.11.2" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "fs-extra": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", - "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", - "dev": true, - "requires": { - "readable-stream": "2 || 3" - } - }, - "tslib": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.2.tgz", - "integrity": "sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg==", - "dev": true - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "dev": true - } - } - }, - "@ionic/utils-network": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@ionic/utils-network/-/utils-network-2.1.5.tgz", - "integrity": "sha512-HUQ1Ec4Mh2MXzzKdbbbDS6xYKwpFJ2XRY7SYXbaZT8+jiNahfHbsOfe62/p8bk41Yil7E9EagzGC2JvIFJh01w==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - } - } - }, - "@ionic/utils-object": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.5.tgz", - "integrity": "sha512-XnYNSwfewUqxq+yjER1hxTKggftpNjFLJH0s37jcrNDwbzmbpFTQTVAp4ikNK4rd9DOebX/jbeZb8jfD86IYxw==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - } - } - }, - "@ionic/utils-process": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.6.tgz", - "integrity": "sha512-HuMTJp6lkzMZAI+FUEqLoOx6wxd29ysmk0Q9dCHABbIEP4hM5zAXTIuv4qUYelzT6lBywS/TAdfz+XB1TM6umg==", - "dev": true, - "requires": { - "@ionic/utils-object": "2.1.5", - "@ionic/utils-terminal": "2.2.1", - "debug": "^4.0.0", - "signal-exit": "^3.0.3", - "tree-kill": "^1.2.2", - "tslib": "^2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - } - } - }, - "@ionic/utils-stream": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.5.tgz", - "integrity": "sha512-hkm46uHvEC05X/8PHgdJi4l4zv9VQDELZTM+Kz69odtO9zZYfnt8DkfXHJqJ+PxmtiE5mk/ehJWLnn/XAczTUw==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - } - } - }, - "@ionic/utils-subprocess": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.6.tgz", - "integrity": "sha512-osM2CaD+bxf4WXfQEjkPXHPqzg4xUqg7Rm9FK/DKddIuL6fHPoeLOwP7t0L3J4417dYsvtssTjxbSQnm/V5aWw==", - "dev": true, - "requires": { - "@ionic/utils-array": "2.1.5", - "@ionic/utils-fs": "3.1.5", - "@ionic/utils-process": "2.1.6", - "@ionic/utils-stream": "3.1.5", - "@ionic/utils-terminal": "2.2.1", - "cross-spawn": "^7.0.0", - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, - "dependencies": { - "@ionic/utils-fs": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.5.tgz", - "integrity": "sha512-a41bY2dHqWSEQQ/80CpbXSs8McyiCFf2DnIWWLukrhYWf46h4qi6M/8dxcMKrofRiqI/3F+cL3S2mOm9Zz/o2Q==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "fs-extra": "^9.0.0", - "tslib": "^2.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "@ionic/utils-terminal": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.2.1.tgz", - "integrity": "sha512-YBXSpK5rHu3XlQCF01S23Y4Rz1msBRqNBgGDo8lXekeRmI2WgeCxMHFZfKTEh30DQNYibnkwaeLacHp6ohd+8g==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - } - } - }, - "@netflix/nerror": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@netflix/nerror/-/nerror-1.1.3.tgz", - "integrity": "sha512-b+MGNyP9/LXkapreJzNUzcvuzZslj/RGgdVVJ16P2wSlYatfLycPObImqVJSmNAdyeShvNeM/pl3sVZsObFueg==", - "requires": { - "assert-plus": "^1.0.0", - "extsprintf": "^1.4.0", - "lodash": "^4.17.15" - }, - "dependencies": { - "extsprintf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", - "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=" - } - } - }, - "@ngx-translate/core": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-8.0.0.tgz", - "integrity": "sha1-dR/WtRLYDzp0jS3o38lt/vopr+A=" - }, - "@ngx-translate/http-loader": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-2.0.1.tgz", - "integrity": "sha1-qmd4jmS/qGUmkad7Ais7QDEgkRM=" - }, - "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", - "requires": { - "@nodelib/fs.stat": "2.0.3", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==" - }, - "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", - "requires": { - "@nodelib/fs.scandir": "2.1.3", - "fastq": "^1.6.0" - } - }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, - "@types/cordova": { - "version": "0.0.34", - "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz", - "integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=", - "dev": true - }, - "@types/cordova-plugin-file": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@types/cordova-plugin-file/-/cordova-plugin-file-4.3.2.tgz", - "integrity": "sha512-kCvVcLxo2M6NxBCngW8JuFlS3YpsqE6VcH9PKnOdWwtpCBTi7WXhQU9kb0h0/TVX5Bp8ab+qLz0mm9wSkcXdXg==", - "dev": true, - "requires": { - "cordova-plugin-file": "*" - } - }, - "@types/cordova-plugin-file-transfer": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/cordova-plugin-file-transfer/-/cordova-plugin-file-transfer-0.0.3.tgz", - "integrity": "sha1-u6d+jQTQejlRR5eiA8JXxbECNoA=", - "dev": true, - "requires": { - "@types/cordova-plugin-file": "*" - } - }, - "@types/cordova-plugin-globalization": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/cordova-plugin-globalization/-/cordova-plugin-globalization-0.0.3.tgz", - "integrity": "sha1-ySA6HENtPS0DBXiffJwrq6i6KK0=", - "dev": true - }, - "@types/cordova-plugin-network-information": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/cordova-plugin-network-information/-/cordova-plugin-network-information-0.0.3.tgz", - "integrity": "sha1-+iycaufkxX8Tt39pXaTtuzr6oBY=", - "dev": true - }, - "@types/node": { - "version": "8.10.59", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz", - "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==", - "dev": true - }, - "@types/promise.prototype.finally": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/promise.prototype.finally/-/promise.prototype.finally-2.0.4.tgz", - "integrity": "sha512-bkzsMz11tGI5r/ZhJIVI1+QSWKKBcX/Llv0zoM2ufOH+o/3sLXwE5xCW0OUX81xPa4EGOyPKFlNrH9GH+IF4lg==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "^4.0.3" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "dependencies": { - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" - } - } - }, - "ajv-keywords": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", - "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "android-versions": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.5.0.tgz", - "integrity": "sha512-/GWUAqa2OJNlDF5VGSe3SR1QMHEPXxx54Ur56r0qQC0H9FlBr7kyBF2SgVEhzFCPbrW4UcYgVuWrq/2Ty3QvXg==", - "requires": { - "semver": "^5.4.1" - } - }, - "ansi": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", - "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=" - }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "requires": { - "ansi-wrap": "^0.1.0" - } - }, - "ansi-cyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", - "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", - "dev": true, - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "requires": { - "type-fest": "^0.11.0" - } - }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", - "dev": true, - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-red": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", - "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", - "dev": true, - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - }, - "dependencies": { - "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", - "requires": { - "color-name": "1.1.1" - } - }, - "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" - } - } - }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", - "dev": true, - "requires": { - "buffer-equal": "^1.0.0" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "arr-filter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", - "dev": true, - "requires": { - "make-iterator": "^1.0.0" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", - "dev": true, - "requires": { - "make-iterator": "^1.0.0" - } - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=" - }, - "array-initial": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", - "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", - "dev": true, - "requires": { - "array-slice": "^1.0.0", - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "array-last": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", - "dev": true, - "requires": { - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true - }, - "array-sort": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", - "dev": true, - "requires": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "ast-types": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.1.tgz", - "integrity": "sha512-pfSiukbt23P1qMhNnsozLzhMLBs7EEeXqPyvPmnuZM+RMfwfqwDbSVKYflgGuVI7/VehR4oMks0igzdNAg4VeQ==", - "dev": true, - "requires": { - "tslib": "^2.0.1" - }, - "dependencies": { - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true - } - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "requires": { - "lodash": "^4.17.11" - } - }, - "async-done": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.2", - "process-nextick-args": "^2.0.0", - "stream-exhaust": "^1.0.1" - } - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" - }, - "async-exit-hook": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "async-settle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", - "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", - "dev": true, - "requires": { - "async-done": "^1.2.2" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", - "dev": true - }, - "autoprefixer": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.6.tgz", - "integrity": "sha512-Iq8TRIB+/9eQ8rbGhcP7ct5cYb/3qjNYAR2SnzLCEcwF6rvVOax8+9+fccgXk4bEhQGjOZd5TLhsksmAdsbGqQ==", - "dev": true, - "requires": { - "browserslist": "^2.11.3", - "caniuse-lite": "^1.0.30000805", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^6.0.17", - "postcss-value-parser": "^3.2.3" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-plugin-add-header-comment": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/babel-plugin-add-header-comment/-/babel-plugin-add-header-comment-1.0.3.tgz", - "integrity": "sha1-URxJAQYmQNWkgLSsPt1pRBlYUOw=" - }, - "bach": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", - "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", - "dev": true, - "requires": { - "arr-filter": "^1.1.1", - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "array-each": "^1.0.0", - "array-initial": "^1.0.0", - "array-last": "^1.1.1", - "async-done": "^1.2.2", - "async-settle": "^1.0.0", - "now-and-later": "^2.0.0" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "big-integer": { - "version": "1.6.44", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.44.tgz", - "integrity": "sha512-7MzElZPTyJ2fNvBkPxtFQ2fWIkVmuzw41+BZHSzpEq3ymB2MfeKp1+yXl/tS75xCx+WnyV+yb0kp+K1C3UNwmQ==" - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "optional": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - }, - "dependencies": { - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "optional": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "optional": true - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "optional": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "~2.0.0" - } - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", - "dev": true - }, - "bluebird-lst": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.5.tgz", - "integrity": "sha512-Ey0bDNys5qpYPhZ/oQ9vOEvD0TYQDTILMXWP2iGfvMg7rSDde+oV4aQQgqRH+CvBFNz2BSDQnPGMUl6LKBUUQA==", - "dev": true, - "requires": { - "bluebird": "^3.5.1" - } - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", - "dev": true, - "requires": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" - }, - "dependencies": { - "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", - "dev": true - }, - "raw-body": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", - "dev": true, - "requires": { - "bytes": "1", - "string_decoder": "0.10" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - } - }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - } - } - }, - "bplist-creator": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz", - "integrity": "sha1-N98VNgkoJLh8QvlXsBNEEXNyrkU=", - "requires": { - "stream-buffers": "~2.2.0" - } - }, - "bplist-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz", - "integrity": "sha1-1g1dzCDLptx+HymbNdPh+V2vuuY=", - "requires": { - "big-integer": "^1.6.7" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000792", - "electron-to-chromium": "^1.3.30" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, - "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", - "dev": true - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true - }, - "buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==" - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builder-util": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-5.17.0.tgz", - "integrity": "sha512-F4s0uU/H0ZBes4CXkICDvUoC3MS9eW4rv5r+wkzcijJBqDfYNV7LLXxnN50P1bOVpFXa8Cw/NUHnsq95Xf478g==", - "dev": true, - "requires": { - "7zip-bin": "~4.0.2", - "app-builder-bin": "1.11.4", - "bluebird-lst": "^1.0.5", - "builder-util-runtime": "^4.4.1", - "chalk": "^2.4.1", - "debug": "^3.1.0", - "fs-extra-p": "^4.6.1", - "is-ci": "^1.1.0", - "js-yaml": "^3.12.0", - "lazy-val": "^1.0.3", - "semver": "^5.5.0", - "source-map-support": "^0.5.6", - "stat-mode": "^0.2.2", - "temp-file": "^3.1.3" - }, - "dependencies": { - "app-builder-bin": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-1.11.4.tgz", - "integrity": "sha512-04sgoFSz6q5pbAxAXcxfUFPl16gJsay5b8dudFXUwQbFfS7ox2uGgbOO5LGHF0t7sM7q/N82ztGePuuCSkKZHQ==", - "dev": true - }, - "builder-util-runtime": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.4.1.tgz", - "integrity": "sha512-8L2pbL6D3VdI1f8OMknlZJpw0c7KK15BRz3cY77AOUElc4XlCv2UhVV01jJM7+6Lx7henaQh80ALULp64eFYAQ==", - "dev": true, - "requires": { - "bluebird-lst": "^1.0.5", - "debug": "^3.1.0", - "fs-extra-p": "^4.6.1", - "sax": "^1.2.4" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - } - }, - "caniuse-lite": { - "version": "1.0.30000938", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz", - "integrity": "sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "chart.js": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz", - "integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==", - "requires": { - "chartjs-color": "^2.1.0", - "moment": "^2.10.2" - } - }, - "chartjs-color": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", - "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", - "requires": { - "chartjs-color-string": "^0.5.0", - "color-convert": "^0.5.3" - } - }, - "chartjs-color-string": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", - "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==", - "requires": { - "color-name": "^1.0.0" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, - "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" - }, - "chromium-pickle-js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", - "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=", - "dev": true - }, - "ci-info": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", - "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - } - }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==" - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - }, - "dependencies": { - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - } - } - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "collection-map": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", - "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", - "dev": true, - "requires": { - "arr-map": "^2.0.2", - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - } - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", - "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "com-darryncampbell-cordova-plugin-intent": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/com-darryncampbell-cordova-plugin-intent/-/com-darryncampbell-cordova-plugin-intent-1.3.0.tgz", - "integrity": "sha512-JXslndd4UiRHmirGZrwrHZHczoZ5sxM7zAylm4bPX7ZDwD4FdCHhILgDA8AeaG8wc11e0A7OEAFo0Esgc0M4yA==" - }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true - }, - "compare-func": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.4.tgz", - "integrity": "sha512-sq2sWtrqKPkEXAC8tEJA1+BqAH9GbFkGBtUOqrUX57VSfwp8xyktctk+uLoRy5eccTdxzDcVIztlYDpKs3Jv1Q==", - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^3.0.0" - }, - "dependencies": { - "dot-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", - "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", - "requires": { - "is-obj": "^1.0.0" - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - } - } - }, - "compare-version": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", - "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "requires": { - "mime-db": ">= 1.43.0 < 2" - }, - "dependencies": { - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - } - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "concat-with-sourcemaps": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", - "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - } - }, - "conf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-1.4.0.tgz", - "integrity": "sha512-bzlVWS2THbMetHqXKB8ypsXN4DQ/1qopGwNJi1eYbpwesJcd86FBjFciCQX/YwAhp9bM7NVnPFqZ5LpV7gP0Dg==", - "requires": { - "dot-prop": "^4.1.0", - "env-paths": "^1.0.0", - "make-dir": "^1.0.0", - "pkg-up": "^2.0.0", - "write-file-atomic": "^2.3.0" - }, - "dependencies": { - "dot-prop": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", - "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", - "requires": { - "is-obj": "^1.0.0" - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - } - } - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", - "dev": true - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", - "dev": true - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-props": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", - "dev": true, - "requires": { - "each-props": "^1.3.0", - "is-plain-object": "^2.0.1" - } - }, - "cordova": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/cordova/-/cordova-10.0.0.tgz", - "integrity": "sha512-00wMcj3X9ILhKtvRG2iEwO2qly4B+vgXFhH4WhVepWg2UVbD1opl1q9jSZ+j2AaI/vsBWW8e6M2M5FAHasnuWw==", - "requires": { - "configstore": "^5.0.1", - "cordova-common": "^4.0.2", - "cordova-create": "^3.0.0", - "cordova-lib": "^10.0.0", - "editor": "^1.0.0", - "execa": "^4.0.3", - "fs-extra": "^9.0.1", - "insight": "^0.10.3", - "loud-rejection": "^2.2.0", - "nopt": "^4.0.3", - "semver": "^7.3.2", - "systeminformation": "^4.26.10", - "update-notifier": "^4.1.0" - }, - "dependencies": { - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "requires": { - "big-integer": "^1.6.44" - } - }, - "cordova-common": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-4.0.2.tgz", - "integrity": "sha512-od7aNShyuBajzPY83mUEO8tERwwWdFklXETHiXP5Ft87CWeo/tSuwNPFztyTy8XYc74yXdogXKPTJeUHuVzB8Q==", - "requires": { - "@netflix/nerror": "^1.1.3", - "ansi": "^0.3.1", - "bplist-parser": "^0.2.0", - "cross-spawn": "^7.0.1", - "elementtree": "^0.1.7", - "endent": "^1.4.1", - "fast-glob": "^3.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "plist": "^3.0.1", - "q": "^1.5.1", - "read-chunk": "^3.2.0", - "strip-bom": "^4.0.0", - "underscore": "^1.9.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "endent": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/endent/-/endent-1.4.1.tgz", - "integrity": "sha512-buHTb5c8AC9NshtP6dgmNLYkiT+olskbq1z6cEGvfGCF3Qphbu/1zz5Xu+yjTDln8RbxNhPoUyJ5H8MSrp1olQ==", - "requires": { - "dedent": "^0.7.0", - "fast-json-parse": "^1.0.3", - "objectorarray": "^1.0.4" - } - }, - "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "loud-rejection": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-2.2.0.tgz", - "integrity": "sha512-S0FayMXku80toa5sZ6Ro4C+s+EtFDCsyJNG/AzFMfX3AxD5Si4dZsgzm/kKnbOxHl5Cv8jBlno8+3XYIh2pNjQ==", - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.2" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "objectorarray": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.4.tgz", - "integrity": "sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w==" - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" - }, - "underscore": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", - "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==" - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "cordova-android": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/cordova-android/-/cordova-android-8.1.0.tgz", - "integrity": "sha512-eAY6g9q3raJ4P03wNdSWC5MOW1EfxoomWNXsPhi7T6Q9yAqmxqn0sLEUjLL1Ib0LCH3nKQWBXdxapQ5LgbHu+g==", - "requires": { - "android-versions": "^1.4.0", - "compare-func": "^1.3.2", - "cordova-common": "^3.2.0", - "nopt": "^4.0.1", - "properties-parser": "^0.3.1", - "q": "^1.5.1", - "shelljs": "^0.5.3" - }, - "dependencies": { - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - } - } - }, - "cordova-android-support-gradle-release": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cordova-android-support-gradle-release/-/cordova-android-support-gradle-release-3.0.1.tgz", - "integrity": "sha512-RSW55DkSckmqhX/kjj+a1YeVdy7s/AtlZn6Qa5XMQmmA4Iogq+IF2jvInZqzCF19DbI5YE95AP7VDbRk+DdDRw==", - "requires": { - "q": "^1.4.1", - "semver": "5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" - } - } - }, - "cordova-app-hello-world": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cordova-app-hello-world/-/cordova-app-hello-world-5.0.0.tgz", - "integrity": "sha512-5My01wsYoeYwS0f/t5Ck52xPm0+2zYJ0SlvxG9vUsndDGtgiP6t/G8upPgWcyDRRz7Rs/50yZuOntmHqmJxccQ==" - }, - "cordova-clipboard": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/cordova-clipboard/-/cordova-clipboard-1.3.0.tgz", - "integrity": "sha512-IGk4LZm/DJ0Xk/jgakHm4wa+A/lrRP3QfzMAHDG7oWLJS4ISOpfI32Wez4ndnENItRslGyBVyJyKD83CxELCAw==" - }, - "cordova-common": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-3.2.0.tgz", - "integrity": "sha512-EvlQ6PirfR65hGDoQvsluW00uSS2MTVIRKQ3c1Xvsddx7D5T5JgF3fHWkGik/Y/8yNcpI0zI2NcJyie2z/ak2A==", - "requires": { - "ansi": "^0.3.1", - "bplist-parser": "^0.1.0", - "cross-spawn": "^6.0.5", - "elementtree": "0.1.7", - "endent": "^1.1.1", - "fs-extra": "^8.0.0", - "glob": "^7.1.2", - "minimatch": "^3.0.0", - "plist": "^3.0.1", - "q": "^1.4.1", - "strip-bom": "^3.0.0", - "underscore": "^1.8.3", - "which": "^1.3.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" - } - } - }, - "cordova-create": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cordova-create/-/cordova-create-3.0.0.tgz", - "integrity": "sha512-WxZRTnt5RHxSAB9urnHFUtVBcIe1YjR4sfwHLsxakNoKkFhcie3HrV5QmNBgRQ5DkxmanRN3VSx4OrPVsNmAaQ==", - "requires": { - "cordova-app-hello-world": "^5.0.0", - "cordova-common": "^4.0.1", - "cordova-fetch": "^3.0.0", - "fs-extra": "^9.0.0", - "globby": "^11.0.0", - "import-fresh": "^3.2.1", - "isobject": "^4.0.0", - "npm-package-arg": "^8.0.1", - "path-is-inside": "^1.0.2", - "tmp": "^0.2.1", - "valid-identifier": "0.0.2" - }, - "dependencies": { - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "requires": { - "big-integer": "^1.6.44" - } - }, - "cordova-common": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-4.0.2.tgz", - "integrity": "sha512-od7aNShyuBajzPY83mUEO8tERwwWdFklXETHiXP5Ft87CWeo/tSuwNPFztyTy8XYc74yXdogXKPTJeUHuVzB8Q==", - "requires": { - "@netflix/nerror": "^1.1.3", - "ansi": "^0.3.1", - "bplist-parser": "^0.2.0", - "cross-spawn": "^7.0.1", - "elementtree": "^0.1.7", - "endent": "^1.4.1", - "fast-glob": "^3.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "plist": "^3.0.1", - "q": "^1.5.1", - "read-chunk": "^3.2.0", - "strip-bom": "^4.0.0", - "underscore": "^1.9.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "endent": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/endent/-/endent-1.4.1.tgz", - "integrity": "sha512-buHTb5c8AC9NshtP6dgmNLYkiT+olskbq1z6cEGvfGCF3Qphbu/1zz5Xu+yjTDln8RbxNhPoUyJ5H8MSrp1olQ==", - "requires": { - "dedent": "^0.7.0", - "fast-json-parse": "^1.0.3", - "objectorarray": "^1.0.4" - } - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==" - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "objectorarray": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.4.tgz", - "integrity": "sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "requires": { - "rimraf": "^3.0.0" - } - }, - "underscore": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", - "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==" - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "cordova-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cordova-fetch/-/cordova-fetch-3.0.0.tgz", - "integrity": "sha512-N6mB/1GD8BNclxnfO85E4/s46nEJjIxYeJYHRGi6MjofhigJ3NlGwTCslbTcq8IOYEh0RdoA0mS4W2jA5UcWeQ==", - "requires": { - "cordova-common": "^4.0.0", - "fs-extra": "^9.0.0", - "npm-package-arg": "^8.0.1", - "pify": "^5.0.0", - "resolve": "^1.15.1", - "semver": "^7.1.3", - "which": "^2.0.2" - }, - "dependencies": { - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "requires": { - "big-integer": "^1.6.44" - } - }, - "cordova-common": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-4.0.2.tgz", - "integrity": "sha512-od7aNShyuBajzPY83mUEO8tERwwWdFklXETHiXP5Ft87CWeo/tSuwNPFztyTy8XYc74yXdogXKPTJeUHuVzB8Q==", - "requires": { - "@netflix/nerror": "^1.1.3", - "ansi": "^0.3.1", - "bplist-parser": "^0.2.0", - "cross-spawn": "^7.0.1", - "elementtree": "^0.1.7", - "endent": "^1.4.1", - "fast-glob": "^3.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "plist": "^3.0.1", - "q": "^1.5.1", - "read-chunk": "^3.2.0", - "strip-bom": "^4.0.0", - "underscore": "^1.9.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "endent": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/endent/-/endent-1.4.1.tgz", - "integrity": "sha512-buHTb5c8AC9NshtP6dgmNLYkiT+olskbq1z6cEGvfGCF3Qphbu/1zz5Xu+yjTDln8RbxNhPoUyJ5H8MSrp1olQ==", - "requires": { - "dedent": "^0.7.0", - "fast-json-parse": "^1.0.3", - "objectorarray": "^1.0.4" - } - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "objectorarray": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.4.tgz", - "integrity": "sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "pify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", - "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==" - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "requires": { - "path-parse": "^1.0.6" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" - }, - "underscore": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", - "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==" - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "cordova-ios": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/cordova-ios/-/cordova-ios-5.1.1.tgz", - "integrity": "sha512-asZMCj44JMe/PMrDIRC97GStPCH+GpaMNVe8hdmu8WWXJzMzRNRRJ339YYU89jitWf9ZKMdyBgrnSnQi5bJ/ZQ==", - "requires": { - "cordova-common": "^3.1.0", - "ios-sim": "^8.0.1", - "nopt": "^4.0.1", - "plist": "^3.0.1", - "q": "^1.5.1", - "semver": "^6.3.0", - "shelljs": "^0.5.3", - "unorm": "^1.4.1", - "xcode": "^2.0.0", - "xml-escape": "^1.1.0" - }, - "dependencies": { - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "cordova-lib": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/cordova-lib/-/cordova-lib-10.0.0.tgz", - "integrity": "sha512-azU/WH0x/3fQg33tU5bKCtj+Weh/bHelz9FWCVdXqVOHXmjzbi3p6p61z5Si967Tfh3TkmHRrodNxS0ovZ7iFQ==", - "requires": { - "cordova-common": "^4.0.2", - "cordova-fetch": "^3.0.0", - "cordova-serve": "^4.0.0", - "dep-graph": "^1.1.0", - "detect-indent": "^6.0.0", - "detect-newline": "^3.1.0", - "elementtree": "^0.1.7", - "execa": "^4.0.3", - "fs-extra": "^9.0.1", - "globby": "^11.0.1", - "init-package-json": "^1.10.3", - "md5-file": "^5.0.0", - "pify": "^5.0.0", - "semver": "^7.3.2", - "stringify-package": "^1.0.1", - "write-file-atomic": "^3.0.3" - }, - "dependencies": { - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "requires": { - "big-integer": "^1.6.44" - } - }, - "cordova-common": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-4.0.2.tgz", - "integrity": "sha512-od7aNShyuBajzPY83mUEO8tERwwWdFklXETHiXP5Ft87CWeo/tSuwNPFztyTy8XYc74yXdogXKPTJeUHuVzB8Q==", - "requires": { - "@netflix/nerror": "^1.1.3", - "ansi": "^0.3.1", - "bplist-parser": "^0.2.0", - "cross-spawn": "^7.0.1", - "elementtree": "^0.1.7", - "endent": "^1.4.1", - "fast-glob": "^3.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "plist": "^3.0.1", - "q": "^1.5.1", - "read-chunk": "^3.2.0", - "strip-bom": "^4.0.0", - "underscore": "^1.9.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "endent": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/endent/-/endent-1.4.1.tgz", - "integrity": "sha512-buHTb5c8AC9NshtP6dgmNLYkiT+olskbq1z6cEGvfGCF3Qphbu/1zz5Xu+yjTDln8RbxNhPoUyJ5H8MSrp1olQ==", - "requires": { - "dedent": "^0.7.0", - "fast-json-parse": "^1.0.3", - "objectorarray": "^1.0.4" - } - }, - "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "objectorarray": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.4.tgz", - "integrity": "sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w==" - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "pify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", - "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" - }, - "underscore": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", - "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==" - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "cordova-plugin-advanced-http": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/cordova-plugin-advanced-http/-/cordova-plugin-advanced-http-2.4.1.tgz", - "integrity": "sha512-6G8MTy/d02jE6n3Y9CVyCtD5hZGiBb+/dR2AIzhKN1RGGz38g1D2C8yE4MqHRvnmry6k/KHQWT1MsHNXrjouXQ==" - }, - "cordova-plugin-badge": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/cordova-plugin-badge/-/cordova-plugin-badge-0.8.8.tgz", - "integrity": "sha512-RhIBtd5xhD/iLnxjt35jvOae28oNW/wtMZBOmQR3Rf0y4wirvA1bpAZEhBoFqL+rZGhsd6ddOdQXdex1T0DRyQ==" - }, - "cordova-plugin-camera": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cordova-plugin-camera/-/cordova-plugin-camera-4.1.0.tgz", - "integrity": "sha512-fCLhWjWYn49q3X5xaypAPgTz6MAWSKFFQvD2Gpi5SuVlrRPRphtX2jIqR2zCBuDTBR082QVnlc+yUDXt65Mjgw==" - }, - "cordova-plugin-chooser": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/cordova-plugin-chooser/-/cordova-plugin-chooser-1.3.2.tgz", - "integrity": "sha512-GfAibvrPdWe/ri+h3e3xkmq5bietY6yJRBIZawYDE7w600j2mtRsxgat7siWZtjRRhJuVsVwUG6H86Hyp3WKvA==" - }, - "cordova-plugin-customurlscheme": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cordova-plugin-customurlscheme/-/cordova-plugin-customurlscheme-5.0.1.tgz", - "integrity": "sha512-Nn3+MUrEGfBSFzkC9s5izzOcmpVy8Pya5oYF+CkcdqAlsqL7EqpUan3Q0Eold4EWFisVG5jRCg0XjyxL4uHGfw==" - }, - "cordova-plugin-device": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cordova-plugin-device/-/cordova-plugin-device-2.0.3.tgz", - "integrity": "sha512-Jb3V72btxf3XHpkPQsGdyc8N6tVBYn1vsxSFj43fIz9vonJDUThYPCJJHqk6PX6N4dJw6I4FjxkpfCR4LDYMlw==" - }, - "cordova-plugin-file": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/cordova-plugin-file/-/cordova-plugin-file-6.0.2.tgz", - "integrity": "sha512-m7cughw327CjONN/qjzsTpSesLaeybksQh420/gRuSXJX5Zt9NfgsSbqqKDon6jnQ9Mm7h7imgyO2uJ34XMBtA==" - }, - "cordova-plugin-file-opener2": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/cordova-plugin-file-opener2/-/cordova-plugin-file-opener2-3.0.4.tgz", - "integrity": "sha512-bd1aCx62X2RwpC+KUiuB7quoxL/8RnPMEJU7x38Tvs+cUGLWBvsmR9+/LqGBsSns2CIqgnJ34TW0Vazoqu7Ieg==" - }, - "cordova-plugin-file-transfer": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/cordova-plugin-file-transfer/-/cordova-plugin-file-transfer-1.7.1.tgz", - "integrity": "sha1-p12L4uvDu5sjxbG70ZkhTsJnWGs=" - }, - "cordova-plugin-geolocation": { - "version": "git+https://github.com/apache/cordova-plugin-geolocation.git#89cf51d222e8f225bdfb661965b3007d669c40ff", - "from": "git+https://github.com/apache/cordova-plugin-geolocation.git#89cf51d222e8f225bdfb661965b3007d669c40ff" - }, - "cordova-plugin-globalization": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/cordova-plugin-globalization/-/cordova-plugin-globalization-1.11.0.tgz", - "integrity": "sha1-6sMVgQAphJOvowvolA5pj2HvvP4=" - }, - "cordova-plugin-inappbrowser": { - "version": "git+https://github.com/moodlemobile/cordova-plugin-inappbrowser.git#715c858975cc1cb5d140afaa7973938511d38509", - "from": "git+https://github.com/moodlemobile/cordova-plugin-inappbrowser.git#moodle" - }, - "cordova-plugin-ionic-keyboard": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.1.3.tgz", - "integrity": "sha512-6ucQ6FdlLdBm8kJfFnzozmBTjru/0xekHP/dAhjoCZggkGRlgs8TsUJFkxa/bV+qi7Dlo50JjmpE4UMWAO+aOQ==" - }, - "cordova-plugin-ionic-webview": { - "version": "git+https://github.com/moodlemobile/cordova-plugin-ionic-webview.git#ac90a8ac88e2c0512d6b250249b1f673f2fbcb68", - "from": "git+https://github.com/moodlemobile/cordova-plugin-ionic-webview.git#500-moodle" - }, - "cordova-plugin-local-notification": { - "version": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#0bb96b757fb484553ceabf35a59802f7983a2836", - "from": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle" - }, - "cordova-plugin-media": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/cordova-plugin-media/-/cordova-plugin-media-5.0.3.tgz", - "integrity": "sha512-UQPFlpk1zL4BY44zGi8RVmYCvcKBCN4Dyf8ovxqGYCC8zR1yhbTRWYDdO9vJdERwbfgWV7+z7FMWiSUfqWm9bQ==" - }, - "cordova-plugin-media-capture": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/cordova-plugin-media-capture/-/cordova-plugin-media-capture-3.0.3.tgz", - "integrity": "sha512-pVQOrNM7VAuVUMXibAlMGIArrftHPrRs4dUCoE+e2HEFUp3LmN3Yj539LjdUxcWmz/A/cHC65m9E3DS56YJhcg==" - }, - "cordova-plugin-network-information": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cordova-plugin-network-information/-/cordova-plugin-network-information-2.0.2.tgz", - "integrity": "sha512-NwO3qDBNL/vJxUxBTPNOA1HvkDf9eTeGH8JSZiwy1jq2W2mJKQEDBwqWkaEQS19Yd/MQTiw0cykxg5D7u4J6cQ==" - }, - "cordova-plugin-qrscanner": { - "version": "git+https://github.com/moodlemobile/cordova-plugin-qrscanner.git#857efee3a7a49104faabd108ff1f00a57d3aca94", - "from": "git+https://github.com/moodlemobile/cordova-plugin-qrscanner.git#dist", - "requires": { - "qrcode-reader": "^1.0.4", - "webrtc-adapter": "^3.1.4" - } - }, - "cordova-plugin-screen-orientation": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/cordova-plugin-screen-orientation/-/cordova-plugin-screen-orientation-3.0.2.tgz", - "integrity": "sha512-2w6CMC+HGvbhogJetalwGurL2Fx8DQCCPy3wlSZHN1/W7WoQ5n9ujVozcoKrY4VaagK6bxrPFih+ElkO8Uqfzg==" - }, - "cordova-plugin-splashscreen": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-6.0.0.tgz", - "integrity": "sha512-pm4ZtJKQY4bCGXVeIInbGrXilryTevYSKgfvoQJpW9UClOWKAxSsYf2/4G2u1vcn492svOSL42OSa2MhujBWEQ==" - }, - "cordova-plugin-statusbar": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/cordova-plugin-statusbar/-/cordova-plugin-statusbar-2.4.3.tgz", - "integrity": "sha512-ThmXzl6QIKWFXf4wWw7Q/zpB+VKkz3VM958+5A0sXD4jmR++u7KnGttLksXshVwWr6lvGwUebLYtIyXwS4Ovcg==" - }, - "cordova-plugin-whitelist": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/cordova-plugin-whitelist/-/cordova-plugin-whitelist-1.3.4.tgz", - "integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ==" - }, - "cordova-plugin-wkuserscript": { - "version": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git#6413f4bb3c2565f353e690b5c1450b69ad9e860e", - "from": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git" - }, - "cordova-plugin-wkwebview-cookies": { - "version": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git#8c3a289e29b33edecff15f470c1630baf4ec3e88", - "from": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git" - }, - "cordova-plugin-zip": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cordova-plugin-zip/-/cordova-plugin-zip-3.1.0.tgz", - "integrity": "sha1-F2yCSOog058c+VnvXmFWrMqWshc=" - }, - "cordova-serve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cordova-serve/-/cordova-serve-4.0.0.tgz", - "integrity": "sha512-gzTLeBQzNP8aM/nG0/7sSfICfNazUgwvEU2kiDaybbYXmxwioo2v96h4tzE0XOyA64beyYwAyRYEEqWA4AMZjw==", - "requires": { - "chalk": "^3.0.0", - "compression": "^1.7.4", - "express": "^4.17.1", - "open": "^7.0.3", - "which": "^2.0.2" - }, - "dependencies": { - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "cordova-sqlite-storage": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cordova-sqlite-storage/-/cordova-sqlite-storage-4.0.0.tgz", - "integrity": "sha512-/n5KT3TyRAC7QRe9A4Sn7bMpdsBJ6aMmHat2PsMxFZBot45SOxbAEgfGmXtq0e7OEdVzk573sIn42bLS6lNLjQ==", - "requires": { - "cordova-sqlite-storage-dependencies": "2.1.1" - }, - "dependencies": { - "cordova-sqlite-storage-dependencies": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cordova-sqlite-storage-dependencies/-/cordova-sqlite-storage-dependencies-2.1.1.tgz", - "integrity": "sha512-1lV5Pg1FttjBmGO8z4gxtuA4BbPKtgTfUEh1Vx4boa41inizyxaowRyTeaaqEhi5gmYAaX8sRTABm9U/XckRFg==" - } - } - }, - "cordova-support-google-services": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/cordova-support-google-services/-/cordova-support-google-services-1.3.2.tgz", - "integrity": "sha512-RtEWzULreUX662MFWopGhFispLiHX7gUf2GijPOC2mY2oCNuUobj2mO4tl5q7PYbOreSxq+PrSekhmS6TAAWdw==" - }, - "cordova.plugins.diagnostic": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/cordova.plugins.diagnostic/-/cordova.plugins.diagnostic-5.0.2.tgz", - "integrity": "sha512-H59o7YxJ2/COzvg+jyTpUqX8QoDcvti9dluJ9a+pHumE8lf3meWemwCl0QFa9GH+xgVd6X1Ikj/6P3+DKWd9eg==", - "dev": true, - "requires": { - "colors": "^1.1.2", - "elementtree": "^0.1.6", - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "core-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "requires": { - "array-find-index": "^1.0.1" - } - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "^0.10.9" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "data-uri-to-buffer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", - "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", - "dev": true - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "optional": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=" - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", - "dev": true, - "requires": { - "kind-of": "^5.0.2" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "default-resolution": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", - "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", - "dev": true - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - }, - "dependencies": { - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - } - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" - }, - "degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "dev": true, - "requires": { - "ast-types": "0.x.x", - "escodegen": "1.x.x", - "esprima": "3.x.x" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "dep-graph": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dep-graph/-/dep-graph-1.1.0.tgz", - "integrity": "sha1-+t6GqSeZqBPptCURzfPfpsyNvv4=", - "requires": { - "underscore": "1.2.1" - }, - "dependencies": { - "underscore": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.2.1.tgz", - "integrity": "sha1-/FxrB2VnPZKi1KyLTcCqiHAuK9Q=" - } - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "detect-indent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz", - "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "requires": { - "path-type": "^4.0.0" - }, - "dependencies": { - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - } - } - }, - "doctrine": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", - "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", - "dev": true, - "requires": { - "esutils": "^1.1.6", - "isarray": "0.0.1" - }, - "dependencies": { - "esutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", - "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "dotenv": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", - "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==", - "dev": true - }, - "dotenv-defaults": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-1.0.2.tgz", - "integrity": "sha512-iXFvHtXl/hZPiFj++1hBg4lbKwGM+t/GlvELDnRtOFdjXyWP7mubkVr+eZGWG62kdsbulXAef6v/j6kiWc/xGA==", - "dev": true, - "requires": { - "dotenv": "^6.2.0" - } - }, - "dotenv-expand": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-4.2.0.tgz", - "integrity": "sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU=", - "dev": true - }, - "dotenv-webpack": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-1.7.0.tgz", - "integrity": "sha512-wwNtOBW/6gLQSkb8p43y0Wts970A3xtNiG/mpwj9MLUhtPCQG6i+/DSXXoNN7fbPCU/vQ7JjwGmgOeGZSSZnsw==", - "dev": true, - "requires": { - "dotenv-defaults": "^1.0.2" - } - }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "each-props": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.1", - "object.defaults": "^1.1.0" - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, - "editor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz", - "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", - "dev": true - }, - "electron-builder-lib": { - "version": "20.23.1", - "resolved": "https://registry.npmjs.org/electron-builder-lib/-/electron-builder-lib-20.23.1.tgz", - "integrity": "sha512-9bYeANVqFPpSmswPwXv8efu9voPE1Q8hw/jNwiWGICjPeYjHyKwa4ao+Vd1beY6ZhUDVhxxXIdlJWnmvH7Mcxw==", - "dev": true, - "requires": { - "7zip-bin": "~4.0.2", - "app-builder-bin": "1.11.4", - "async-exit-hook": "^2.0.1", - "bluebird-lst": "^1.0.5", - "builder-util": "5.17.0", - "builder-util-runtime": "4.4.1", - "chromium-pickle-js": "^0.2.0", - "debug": "^3.1.0", - "ejs": "^2.6.1", - "electron-osx-sign": "0.4.10", - "electron-publish": "20.23.1", - "env-paths": "^1.0.0", - "fs-extra-p": "^4.6.1", - "hosted-git-info": "^2.7.1", - "is-ci": "^1.1.0", - "isbinaryfile": "^3.0.2", - "js-yaml": "^3.12.0", - "lazy-val": "^1.0.3", - "minimatch": "^3.0.4", - "normalize-package-data": "^2.4.0", - "plist": "^3.0.1", - "read-config-file": "3.1.0", - "sanitize-filename": "^1.6.1", - "semver": "^5.5.0", - "sumchecker": "^2.0.2", - "temp-file": "^3.1.3" - }, - "dependencies": { - "app-builder-bin": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-1.11.4.tgz", - "integrity": "sha512-04sgoFSz6q5pbAxAXcxfUFPl16gJsay5b8dudFXUwQbFfS7ox2uGgbOO5LGHF0t7sM7q/N82ztGePuuCSkKZHQ==", - "dev": true - }, - "builder-util-runtime": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.4.1.tgz", - "integrity": "sha512-8L2pbL6D3VdI1f8OMknlZJpw0c7KK15BRz3cY77AOUElc4XlCv2UhVV01jJM7+6Lx7henaQh80ALULp64eFYAQ==", - "dev": true, - "requires": { - "bluebird-lst": "^1.0.5", - "debug": "^3.1.0", - "fs-extra-p": "^4.6.1", - "sax": "^1.2.4" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "electron-osx-sign": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.10.tgz", - "integrity": "sha1-vk87ibKnWh3F8eckkIGrKSnKOiY=", - "dev": true, - "requires": { - "bluebird": "^3.5.0", - "compare-version": "^0.1.2", - "debug": "^2.6.8", - "isbinaryfile": "^3.0.2", - "minimist": "^1.2.0", - "plist": "^2.1.0" - }, - "dependencies": { - "base64-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", - "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=", - "dev": true - }, - "plist": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz", - "integrity": "sha1-V8zbeggh3yGDEhejytVOPhRqECU=", - "dev": true, - "requires": { - "base64-js": "1.2.0", - "xmlbuilder": "8.2.2", - "xmldom": "0.1.x" - } - }, - "xmlbuilder": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", - "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=", - "dev": true - } - } - }, - "electron-publish": { - "version": "20.23.1", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-20.23.1.tgz", - "integrity": "sha512-FsNL5bY4/Uab5YGYWE+/7wL8znkghEPwJvDyD0985Obw0Eg9JZP1MpUbt4jUxrK8z5wTLVMFdSv7ymV8yq6ypA==", - "dev": true, - "requires": { - "bluebird-lst": "^1.0.5", - "builder-util": "~5.17.0", - "builder-util-runtime": "^4.4.1", - "chalk": "^2.4.1", - "fs-extra-p": "^4.6.1", - "lazy-val": "^1.0.3", - "mime": "^2.3.1" - }, - "dependencies": { - "builder-util-runtime": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.4.1.tgz", - "integrity": "sha512-8L2pbL6D3VdI1f8OMknlZJpw0c7KK15BRz3cY77AOUElc4XlCv2UhVV01jJM7+6Lx7henaQh80ALULp64eFYAQ==", - "dev": true, - "requires": { - "bluebird-lst": "^1.0.5", - "debug": "^3.1.0", - "fs-extra-p": "^4.6.1", - "sax": "^1.2.4" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "electron-rebuild": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-1.10.0.tgz", - "integrity": "sha512-n10i30GJg7JH8yZL3ZY3x80YtKmSYuuN8cl+3Feljm+sQDU4rUW1jbnYGu0eUHlK3kPOiNWPtW7srGcwZ9p1zQ==", - "dev": true, - "requires": { - "colors": "^1.3.3", - "debug": "^4.1.1", - "detect-libc": "^1.0.3", - "fs-extra": "^8.1.0", - "node-abi": "^2.11.0", - "node-gyp": "^6.0.1", - "ora": "^3.4.0", - "spawn-rx": "^3.0.0", - "yargs": "^14.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cli-spinners": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz", - "integrity": "sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "env-paths": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node-abi": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.14.0.tgz", - "integrity": "sha512-y54KGgEOHnRHlGQi7E5UiryRkH8bmksmQLj/9iLAjoje743YS+KaKB/sDYXgqtT0J16JT3c3AYJZNI98aU/kYg==", - "dev": true, - "requires": { - "semver": "^5.4.1" - } - }, - "node-gyp": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-6.1.0.tgz", - "integrity": "sha512-h4A2zDlOujeeaaTx06r4Vy+8MZ1679lU+wbCKDS4ZtvY2A37DESo37oejIw0mtmR3+rvNwts5B6Kpt1KrNYdNw==", - "dev": true, - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "npmlog": "^4.1.2", - "request": "^2.88.0", - "rimraf": "^2.6.3", - "semver": "^5.7.1", - "tar": "^4.4.12", - "which": "^1.3.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "ora": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", - "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-spinners": "^2.0.0", - "log-symbols": "^2.2.0", - "strip-ansi": "^5.2.0", - "wcwidth": "^1.0.1" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "spawn-rx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-3.0.0.tgz", - "integrity": "sha512-dw4Ryg/KMNfkKa5ezAR5aZe9wNwPdKlnHEXtHOjVnyEDSPQyOpIPPRtcIiu7127SmtHhaCjw21yC43HliW0iIg==", - "dev": true, - "requires": { - "debug": "^2.5.1", - "lodash.assign": "^4.2.0", - "rxjs": "^6.3.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yargs": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.2.tgz", - "integrity": "sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.0" - } - }, - "yargs-parser": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "electron-to-chromium": { - "version": "1.3.113", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", - "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", - "dev": true - }, - "elementtree": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", - "integrity": "sha1-mskb5uUvtuYkTE5UpKw+2K6OKcA=", - "requires": { - "sax": "1.1.4" - }, - "dependencies": { - "sax": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", - "integrity": "sha1-dLbTPJrh4AFRDxeakRaFiPGu2qk=" - } - } - }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "^1.4.0" - } - }, - "endent": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/endent/-/endent-1.3.0.tgz", - "integrity": "sha512-C8AryqPPwtydqcpO5AF6k9Bd1EpFkQtvsefJqS3y3n8TG13Jy63MascDxTOULZYqrUde+dK6BjNc6LIMr3iI2A==", - "requires": { - "dedent": "^0.7.0", - "fast-json-parse": "^1.0.3", - "objectorarray": "^1.0.3" - } - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" - } - }, - "env-paths": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", - "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=" - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", - "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", - "dev": true, - "requires": { - "string-template": "~0.2.1", - "xtend": "~4.0.0" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" - } - }, - "es5-ext": { - "version": "0.10.45", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", - "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", - "dev": true, - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "1" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=" - }, - "es6-promise-plugin": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/es6-promise-plugin/-/es6-promise-plugin-4.2.2.tgz", - "integrity": "sha512-uoA4aVplXI9oqUYJFBAVRwAqIN9/n9JgrTAUGX3qPbnSZVE5yY1+6/MsoN5f4xsaPO62WjPHOdtts6okMN6tNA==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - }, - "dependencies": { - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - } - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" - } - }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "estree-walker": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz", - "integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", - "dev": true - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "requires": { - "fill-range": "^2.1.0" - } - }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "optional": true - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "requires": { - "is-extglob": "^1.0.0" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fancy-log": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", - "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", - "dev": true, - "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "time-stamp": "^1.0.0" - } - }, - "fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "fast-json-parse": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", - "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", - "dev": true - }, - "fastq": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", - "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" - }, - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, - "fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - } - }, - "flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "font-awesome": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", - "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "requires": { - "is-callable": "^1.1.3" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "requires": { - "for-in": "^1.0.1" - } - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "dependencies": { - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - } - } - }, - "formidable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", - "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", - "dev": true - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "optional": true - }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-extra-p": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.6.1.tgz", - "integrity": "sha512-IsTMbUS0svZKZTvqF4vDS9c/L7Mw9n8nZQWWeSzAGacOSe+8CzowhUN0tdZEZFIJNP5HC7L9j3MMikz/G4hDeQ==", - "dev": true, - "requires": { - "bluebird-lst": "^1.0.5", - "fs-extra": "^6.0.1" - }, - "dependencies": { - "fs-extra": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", - "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - } - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "fs-mkdirp-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", - "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "through2": "^2.0.3" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", - "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true - }, - "aproba": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": false, - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": false, - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "optional": true - }, - "debug": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==" - }, - "deep-extend": { - "version": "0.6.0", - "resolved": false, - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true - }, - "delegates": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "optional": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "optional": true - }, - "gauge": { - "version": "2.7.4", - "resolved": false, - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": false, - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": false, - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "optional": true - }, - "ini": { - "version": "1.3.5", - "resolved": false, - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": false, - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": false, - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "minipass": { - "version": "2.3.5", - "resolved": false, - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "optional": true, - "requires": { - "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "optional": true - } - } - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "optional": true - }, - "needle": { - "version": "2.3.0", - "resolved": false, - "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "optional": true, - "requires": { - "ms": "^2.1.1" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - } - } - } - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "resolved": false, - "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": false, - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "resolved": false, - "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": false, - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": false, - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "optional": true - }, - "osenv": { - "version": "0.1.5", - "resolved": false, - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "optional": true - }, - "rc": { - "version": "1.2.8", - "resolved": false, - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": false, - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": false, - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": false, - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true - }, - "sax": { - "version": "1.2.4", - "resolved": false, - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "optional": true - }, - "semver": { - "version": "5.7.0", - "resolved": false, - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "optional": true - }, - "string-width": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "optional": true - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": false, - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "optional": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "dev": true, - "requires": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "requires": { - "globule": "^1.0.0" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-uri": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.4.tgz", - "integrity": "sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q==", - "dev": true, - "requires": { - "data-uri-to-buffer": "1", - "debug": "2", - "extend": "~3.0.2", - "file-uri-to-path": "1", - "ftp": "~0.3.10", - "readable-stream": "2" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", - "optional": true - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "requires": { - "is-glob": "^2.0.0" - } - }, - "glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", - "dev": true, - "requires": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "glob-watcher": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", - "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-done": "^1.2.0", - "chokidar": "^2.0.0", - "is-negated-glob": "^1.0.0", - "just-debounce": "^1.0.0", - "object.defaults": "^1.1.0" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": false, - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": false, - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": false, - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "resolved": false, - "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "resolved": false, - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": false, - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": false, - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "resolved": false, - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": false, - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": false, - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "resolved": false, - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "resolved": false, - "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "resolved": false, - "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "resolved": false, - "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": false, - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "resolved": false, - "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": false, - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": false, - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "resolved": false, - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "resolved": false, - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": false, - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": false, - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": false, - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "resolved": false, - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "resolved": false, - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", - "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": false, - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "resolved": false, - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - } - } - }, - "global-dirs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", - "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", - "requires": { - "ini": "^1.3.5" - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - } - } - }, - "globule": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", - "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", - "dev": true, - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" - } - }, - "glogg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", - "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", - "dev": true, - "requires": { - "sparkles": "^1.0.0" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "gulp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", - "dev": true, - "requires": { - "glob-watcher": "^5.0.3", - "gulp-cli": "^2.2.0", - "undertaker": "^1.2.1", - "vinyl-fs": "^3.0.0" - }, - "dependencies": { - "gulp-cli": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz", - "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==", - "dev": true, - "requires": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.1.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.0.1", - "yargs": "^7.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "gulp-clip-empty-files": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/gulp-clip-empty-files/-/gulp-clip-empty-files-0.1.2.tgz", - "integrity": "sha1-vumATiU7vaVc+8Em6NuuZDPtzMg=", - "dev": true, - "requires": { - "through2": "~2.0.1" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "gulp-concat": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", - "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", - "dev": true, - "requires": { - "concat-with-sourcemaps": "^1.0.0", - "through2": "^2.0.0", - "vinyl": "^2.0.0" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "gulp-flatten": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/gulp-flatten/-/gulp-flatten-0.4.0.tgz", - "integrity": "sha512-eg4spVTAiv1xXmugyaCxWne1oPtNG0UHEtABx5W8ScLiqAYceyYm6GYA36x0Qh8KOIXmAZV97L2aYGnKREG3Sg==", - "dev": true, - "requires": { - "plugin-error": "^0.1.2", - "through2": "^2.0.0" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "gulp-htmlmin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/gulp-htmlmin/-/gulp-htmlmin-5.0.1.tgz", - "integrity": "sha512-ASlyDPZOSKjHYUifYV0rf9JPDflN9IRIb8lw2vRqtYMC4ljU3zAmnnaVXwFQ3H+CfXxZSUesZ2x7jrnPJu93jA==", - "dev": true, - "requires": { - "html-minifier": "^3.5.20", - "plugin-error": "^1.0.1", - "through2": "^2.0.3" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dev": true, - "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "gulp-rename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", - "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", - "dev": true - }, - "gulp-slash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gulp-slash/-/gulp-slash-1.1.3.tgz", - "integrity": "sha1-8VUhrCOxeNtE5VHjDi/Lykv2/h0=", - "dev": true, - "requires": { - "slash": "~0.1.3", - "through2": "~0.5.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "through2": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", - "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", - "dev": true, - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" - } - }, - "xtend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", - "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", - "dev": true - } - } - }, - "gulplog": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", - "dev": true, - "requires": { - "glogg": "^1.0.0" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hosted-git-info": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", - "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==" - }, - "html-minifier": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", - "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", - "dev": true, - "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.2.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - }, - "uglify-js": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", - "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", - "dev": true, - "requires": { - "commander": "~2.19.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true - } - } - } - } - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "http-parser-js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", - "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", - "dev": true - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", - "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "in-publish": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", - "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "init-package-json": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz", - "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", - "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "1 || 2", - "semver": "2.x || 3.x || 4 || 5", - "validate-npm-package-license": "^3.0.1", - "validate-npm-package-name": "^3.0.0" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" - }, - "npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", - "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - } - } - }, - "inquirer": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.2.tgz", - "integrity": "sha512-DF4osh1FM6l0RJc5YWYhSDB6TawiBRlbV9Cox8MWlidU218Tb7fm3lQTULyUJDfJ0tjbzl0W4q651mrCCEM55w==", - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.16", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "rxjs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", - "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", - "requires": { - "tslib": "^1.9.0" - } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "insight": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/insight/-/insight-0.10.3.tgz", - "integrity": "sha512-YOncxSN6Omh+1Oqxt+OJAvJVMDKw7l6IEG0wT2cTMGxjsTcroOGW4IR926QDzxg/uZHcFZ2cZbckDWdZhc2pZw==", - "requires": { - "async": "^2.6.2", - "chalk": "^2.4.2", - "conf": "^1.4.0", - "inquirer": "^6.3.1", - "lodash.debounce": "^4.0.8", - "os-name": "^3.1.0", - "request": "^2.88.0", - "tough-cookie": "^3.0.1", - "uuid": "^3.3.2" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "macos-release": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz", - "integrity": "sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==" - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" - }, - "os-name": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", - "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^3.1.0" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - } - } - }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, - "install": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/install/-/install-0.8.9.tgz", - "integrity": "sha1-n0tcDRhR74cunfheT3Fi1OXc2+0=" - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ionic-angular": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/ionic-angular/-/ionic-angular-3.9.9.tgz", - "integrity": "sha512-XJGWbBrLEPRZDAPDOv0mFUHuAkqld/YqepPEBi9j6z235gbfX1CJL5A4xciCq0UMh5UDPlTt/CvRy8cRt9XCgQ==" - }, - "ionicons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-3.0.0.tgz", - "integrity": "sha1-QLja9P16MRUL0AIWD2ZJbiKpjDw=" - }, - "ios-sim": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/ios-sim/-/ios-sim-8.0.2.tgz", - "integrity": "sha512-P7nEG771bfd+JoMRjnis1gpZOkjTUUxu+4Ek1Z+eoaEEoT9byllU9pxfQ8Df7hL3gSkIQxNwTSLhos2I8tWUQA==", - "requires": { - "bplist-parser": "^0.0.6", - "nopt": "1.0.9", - "plist": "^3.0.1", - "simctl": "^2" - }, - "dependencies": { - "bplist-parser": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.0.6.tgz", - "integrity": "sha1-ONo0cYF9+dRKs4kuJ3B7u9daEbk=" - }, - "nopt": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.9.tgz", - "integrity": "sha1-O8DXy6e/sNWmdtvtfA6+SKT9RU4=", - "requires": { - "abbrev": "1" - } - } - } - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - }, - "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", - "dev": true - }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" - }, - "is-ci": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", - "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", - "dev": true, - "requires": { - "ci-info": "^1.0.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==" - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "requires": { - "is-primitive": "^2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "is-negated-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", - "dev": true - }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "dev": true, - "requires": { - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, - "requires": { - "is-unc-path": "^1.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dev": true, - "requires": { - "unc-path-regex": "^0.1.2" - } - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-valid-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", - "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "requires": { - "buffer-alloc": "^1.2.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "js-base64": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.3.tgz", - "integrity": "sha512-fiUvdfCaAXoQTHdKMgTvg6IkecXDcVz6V5rlftUTclF9IKBjMizvSdQaCl/z/6TApDeby5NL+axYou3i0mu1Pg==", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "jszip": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", - "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", - "requires": { - "core-js": "~2.3.0", - "es6-promise": "~3.0.2", - "lie": "~3.1.0", - "pako": "~1.0.2", - "readable-stream": "~2.0.6" - }, - "dependencies": { - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "just-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", - "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", - "dev": true - }, - "keytar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/keytar/-/keytar-6.0.1.tgz", - "integrity": "sha512-1Ihpf2tdM3sLwGMkYHXYhVC/hx5BDR7CWFL4IrBA3IDZo0xHhS2nM+tU9Y+u/U7okNfbVkwmKsieLkcWRMh93g==", - "optional": true, - "requires": { - "node-addon-api": "^3.0.0", - "prebuild-install": "5.3.4" - } - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "last-run": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", - "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", - "dev": true, - "requires": { - "default-resolution": "^2.0.0", - "es6-weak-map": "^2.0.1" - } - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "requires": { - "package-json": "^6.3.0" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - }, - "lazy-val": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz", - "integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==", - "dev": true - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true, - "requires": { - "readable-stream": "^2.0.5" - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "^1.0.0" - } - }, - "lead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", - "dev": true, - "requires": { - "flush-write-stream": "^1.0.2" - } - }, - "leek": { - "version": "0.0.24", - "resolved": "https://registry.npmjs.org/leek/-/leek-0.0.24.tgz", - "integrity": "sha1-5ADlfw5g2O8r1NBo3EKKVDRdvNo=", - "dev": true, - "requires": { - "debug": "^2.1.0", - "lodash.assign": "^3.2.0", - "rsvp": "^3.0.21" - }, - "dependencies": { - "lodash.assign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-3.2.0.tgz", - "integrity": "sha1-POnwI0tLIiPilrj6CsH+6OvKZPo=", - "dev": true, - "requires": { - "lodash._baseassign": "^3.0.0", - "lodash._createassigner": "^3.0.0", - "lodash.keys": "^3.0.0" - } - } - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", - "requires": { - "immediate": "~3.0.5" - } - }, - "liftoff": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", - "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", - "dev": true, - "requires": { - "extend": "^3.0.0", - "findup-sync": "^3.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" - } - }, - "livereload-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", - "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", - "dev": true - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - } - } - }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, - "requires": { - "lodash._basecopy": "^3.0.0", - "lodash.keys": "^3.0.0" - } - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._bindcallback": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", - "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", - "dev": true - }, - "lodash._createassigner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz", - "integrity": "sha1-g4pbri/aymOsIt7o4Z+k5taXCxE=", - "dev": true, - "requires": { - "lodash._bindcallback": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash.restparam": "^3.0.0" - } - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - }, - "lodash.restparam": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", - "dev": true - }, - "lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "macos-release": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-1.1.0.tgz", - "integrity": "sha512-mmLbumEYMi5nXReB9js3WGsB8UE6cDBWyIO62Z4DNx6GbRhDxHNjA1MlzSpJ2S2KM1wyiPRA0d19uHWYYvMHjA==", - "dev": true - }, - "magic-string": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", - "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", - "dev": true, - "requires": { - "vlq": "^0.2.2" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "matchdep": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", - "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", - "dev": true, - "requires": { - "findup-sync": "^2.0.0", - "micromatch": "^3.0.4", - "resolve": "^1.4.0", - "stack-trace": "0.0.10" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, - "math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==" - }, - "mathjax": { - "version": "2.7.7", - "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-2.7.7.tgz", - "integrity": "sha512-OOl0B2/0tSJAtAZarXnQuLDBLgTNRqiI9VqHTQzPsxf4okT2iIpDrvaklK9x2QEMD1sDj4yRn11Ygci41DxMAQ==" - }, - "md5-file": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz", - "integrity": "sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==" - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" - }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "requires": { - "mime-db": "~1.38.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "optional": true - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - }, - "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "optional": true - }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "mute-stdout": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", - "dev": true - }, - "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "optional": true - }, - "native-run": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/native-run/-/native-run-1.0.0.tgz", - "integrity": "sha512-BKHQM9oXRY7mIlOHex1EAeTJeg1Gy6EJEKvt1bWyrb387dcFYCIVGdCqKmbQXg4OjHrJw2caDrHoN9y7U9y2+A==", - "dev": true, - "requires": { - "@ionic/utils-fs": "^3.0.0", - "debug": "^4.1.1", - "elementtree": "^0.1.7", - "ini": "^1.3.5", - "node-ioslib": "0.0.9", - "split2": "^3.1.0", - "through2": "^3.0.0", - "tslib": "^1.9.3", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", - "dev": true, - "requires": { - "readable-stream": "2 || 3" - } - }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - } - } - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", - "dev": true - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "nl.kingsquare.cordova.background-audio": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nl.kingsquare.cordova.background-audio/-/nl.kingsquare.cordova.background-audio-1.0.1.tgz", - "integrity": "sha1-Gx1NzaijXAx/x5UzN7FzsUtPmGA=" - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "^1.1.1" - } - }, - "node-abi": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.18.0.tgz", - "integrity": "sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==", - "optional": true, - "requires": { - "semver": "^5.4.1" - } - }, - "node-addon-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz", - "integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==", - "optional": true - }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "dev": true, - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - }, - "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - } - } - }, - "node-ioslib": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/node-ioslib/-/node-ioslib-0.0.9.tgz", - "integrity": "sha512-rHRbH1glQ+mmC8EuBLl89OO/WjicAUszW3OKRzzHdedVyWZ/yD3eg3GzHkaNgWtTwFl/0x29OyC4aQp7LdYlYA==", - "dev": true, - "requires": { - "bplist-parser": "^0.1.1", - "debug": "^4.1.1", - "plist": "^3.0.1", - "tslib": "^1.9.3" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - } - } - }, - "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "0.0.4" - } - }, - "node-loader": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/node-loader/-/node-loader-0.6.0.tgz", - "integrity": "sha1-x5fvUQle1YWZArFX9jhPY2HgWug=", - "dev": true - }, - "node-sass": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", - "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", - "dev": true, - "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash": "^4.17.15", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.13.2", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "2.2.5", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "noop-logger": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", - "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", - "optional": true - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" - }, - "now-and-later": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", - "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", - "dev": true, - "requires": { - "once": "^1.3.2" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-package-arg": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.0.1.tgz", - "integrity": "sha512-/h5Fm6a/exByzFSTm7jAyHbgOqErl9qSNJDQF32Si/ZzgwT2TERVxRxn3Jurw1wflgyVVAxnFR4fRHPM7y1ClQ==", - "requires": { - "hosted-git-info": "^3.0.2", - "semver": "^7.0.0", - "validate-npm-package-name": "^3.0.0" - }, - "dependencies": { - "hosted-git-info": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.5.tgz", - "integrity": "sha512-i4dpK6xj9BIpVOTboXIlKG9+8HMKggcrMX7WA24xZtKwX0TPelq/rbaS5rCKeNX8sJXZJGdSxpnEGtta+wismQ==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" - }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", - "dev": true, - "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", - "dev": true, - "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - } - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.reduce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", - "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", - "dev": true, - "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - } - } - }, - "objectorarray": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.3.tgz", - "integrity": "sha512-kPoflSYkAf/Onvjr4ZLaq37vDuOXjVzfwLCRuORRzYGdXkHa/vacPT0RgR+KmtkwOYFcxTMM62BRrZk8GGKHjw==", - "requires": { - "tape": "^4.8.0" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "open": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/open/-/open-7.0.4.tgz", - "integrity": "sha512-brSA+/yq+b08Hsr4c8fsEW2CRzk1BmfN3SAK/5VCHQ9bdoZJ4qa/+AfR0xHjlbbZUyPkUHs1b8x1RqdyZdkVqQ==", - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "dependencies": { - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "requires": { - "is-docker": "^2.0.0" - } - } - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "ordered-read-streams": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, - "os-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-2.0.1.tgz", - "integrity": "sha1-uaOGNhwXrjohc27wWZQFyajF3F4=", - "dev": true, - "requires": { - "macos-release": "^1.0.0", - "win-release": "^1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" - }, - "pac-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz", - "integrity": "sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ==", - "dev": true, - "requires": { - "agent-base": "^4.2.0", - "debug": "^4.1.1", - "get-uri": "^2.0.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^3.0.0", - "pac-resolver": "^3.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "^4.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "pac-resolver": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", - "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", - "dev": true, - "requires": { - "co": "^4.6.0", - "degenerator": "^1.0.4", - "ip": "^1.1.5", - "netmask": "^1.0.6", - "thunkify": "^2.1.2" - } - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "^2.2.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-asn1": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", - "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", - "dev": true, - "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "dev": true, - "requires": { - "path-root-regex": "^0.1.0" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true - }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - } - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "phonegap-plugin-multidex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/phonegap-plugin-multidex/-/phonegap-plugin-multidex-1.0.0.tgz", - "integrity": "sha512-1wvc3iQOQpEBaQbXgLxA2JUiLSQ2azdF/bF29ghXDiQJWSpQ1BF8gSuqttM8WZoj081Ps8OKL0gYxdDBkFNPqA==" - }, - "phonegap-plugin-push": { - "version": "git+https://github.com/moodlemobile/phonegap-plugin-push.git#cb332d5ec8a1a0d43916fc6d35455fe6bea7a7f9", - "from": "git+https://github.com/moodlemobile/phonegap-plugin-push.git#moodle-v3", - "requires": { - "babel-plugin-add-header-comment": "^1.0.3", - "install": "^0.8.2" - } - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "^2.0.0" - } - } - } - }, - "plist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz", - "integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==", - "requires": { - "base64-js": "^1.2.3", - "xmlbuilder": "^9.0.7", - "xmldom": "0.1.x" - } - }, - "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", - "dev": true, - "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" - }, - "dependencies": { - "arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" - } - }, - "arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", - "dev": true - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, - "extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", - "dev": true, - "requires": { - "kind-of": "^1.1.0" - } - }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", - "dev": true - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "prebuild-install": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.4.tgz", - "integrity": "sha512-AkKN+pf4fSEihjapLEEj8n85YIw/tN6BQqkhzbDc0RvEZGdkpJBGMUYx66AAMcPG2KzmPQS7Cm16an4HVBRRMA==", - "optional": true, - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp": "^0.5.1", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.7.0", - "noop-logger": "^0.1.1", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0", - "which-pm-runs": "^1.0.0" - }, - "dependencies": { - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "optional": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" - }, - "pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" - }, - "promise.prototype.finally": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/promise.prototype.finally/-/promise.prototype.finally-3.1.0.tgz", - "integrity": "sha512-7p/K2f6dI+dM8yjRQEGrTQs5hTQixUAdOGpMEA3+pVxpX5oHKRSKAXyLw9Q9HUWDTdwtoo39dSHGQtN90HcEwQ==", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.9.0", - "function-bind": "^1.1.1" - } - }, - "promzard": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz", - "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", - "requires": { - "read": "1" - } - }, - "properties-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/properties-parser/-/properties-parser-0.3.1.tgz", - "integrity": "sha1-ExbpU5/7/ZOEXjabIRAiq9R4dxo=", - "requires": { - "string.prototype.codepointat": "^0.2.0" - } - }, - "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" - } - }, - "proxy-agent": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.1.1.tgz", - "integrity": "sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw==", - "dev": true, - "requires": { - "agent-base": "^4.2.0", - "debug": "4", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^3.0.0", - "lru-cache": "^5.1.1", - "pac-proxy-agent": "^3.0.1", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^4.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "proxy-middleware": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz", - "integrity": "sha1-o/3xvvtzD5UZZYcqwvYHTGFHelY=", - "dev": true - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "pupa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", - "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", - "requires": { - "escape-goat": "^2.0.0" - } - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" - }, - "qrcode-reader": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/qrcode-reader/-/qrcode-reader-1.0.4.tgz", - "integrity": "sha512-rRjALGNh9zVqvweg1j5OKIQKNsw3bLC+7qwlnead5K/9cb1cEIAGkwikt/09U0K+2IDWGD9CC6SP7tHAjUeqvQ==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - } - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", - "requires": { - "mute-stream": "~0.0.4" - } - }, - "read-chunk": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-3.2.0.tgz", - "integrity": "sha512-CEjy9LCzhmD7nUpJ1oVOE6s/hBkejlcJEgLQHVnQznOSilOPb+kpKktlLfFDK3/WP43+F80xkUTM2VOkYoSYvQ==", - "requires": { - "pify": "^4.0.1", - "with-open-file": "^0.1.6" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - } - } - }, - "read-config-file": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-3.1.0.tgz", - "integrity": "sha512-z3VTrR9fgFu+Ll6MhTdtxbPFBKNGKgzYYnRjOcZvQeE/zwJTjPYVrps0ATgaSWU2/BnucUg3knP+Oz4zo9vEoA==", - "dev": true, - "requires": { - "ajv": "^6.5.2", - "ajv-keywords": "^3.2.0", - "bluebird-lst": "^1.0.5", - "dotenv": "^6.0.0", - "dotenv-expand": "^4.2.0", - "fs-extra-p": "^4.6.1", - "js-yaml": "^3.12.0", - "json5": "^1.0.1", - "lazy-val": "^1.0.3" - }, - "dependencies": { - "ajv": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz", - "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "read-package-json": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", - "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", - "requires": { - "glob": "^7.1.1", - "json-parse-even-better-errors": "^2.3.0", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, - "reflect-metadata": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz", - "integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==" - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "registry-auth-token": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", - "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "requires": { - "rc": "^1.2.8" - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" - } - }, - "remove-bom-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", - "dev": true, - "requires": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, - "replace-homedir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", - "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1", - "is-absolute": "^1.0.0", - "remove-trailing-separator": "^1.1.0" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", - "dev": true, - "requires": { - "path-parse": "^1.0.5" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "resolve-options": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", - "dev": true, - "requires": { - "value-or-function": "^3.0.0" - } - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "resumer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", - "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", - "requires": { - "through": "~2.3.4" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "^0.1.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rollup": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.50.0.tgz", - "integrity": "sha512-7RqCBQ9iwsOBPkjYgoIaeUij606mSkDMExP0NT7QDI3bqkHYQHrQ83uoNIXwPcQm/vP2VbsUz3kiyZZ1qPlLTQ==", - "dev": true - }, - "rollup-plugin-commonjs": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.2.6.tgz", - "integrity": "sha512-qK0+uhktmnAgZkHkqFuajNmPw93fjrO7+CysDaxWE5jrUR9XSlSvuao5ZJP+XizxA8weakhgYYBtbVz9SGBpjA==", - "dev": true, - "requires": { - "acorn": "^5.2.1", - "estree-walker": "^0.5.0", - "magic-string": "^0.22.4", - "resolve": "^1.4.0", - "rollup-pluginutils": "^2.0.1" - } - }, - "rollup-plugin-node-resolve": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.0.0.tgz", - "integrity": "sha1-i4l8TDAw1QASd7BRSyXSygloPuA=", - "dev": true, - "requires": { - "browser-resolve": "^1.11.0", - "builtin-modules": "^1.1.0", - "is-module": "^1.0.0", - "resolve": "^1.1.6" - } - }, - "rollup-pluginutils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.4.1.tgz", - "integrity": "sha512-wesMQ9/172IJDIW/lYWm0vW0LiKe5Ekjws481R7z9WTRtmO59cqyM/2uUlxvf6yzm/fElFmHUobeQOYz46dZJw==", - "dev": true, - "requires": { - "estree-walker": "^0.6.0", - "micromatch": "^3.1.10" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "estree-walker": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.0.tgz", - "integrity": "sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw==", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, - "rsvp": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", - "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", - "dev": true - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" - }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==" - }, - "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "requires": { - "symbol-observable": "1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-json-parse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sanitize-filename": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.1.tgz", - "integrity": "sha1-YS2hyWRz+gLczaktzVtKsWSmdyo=", - "dev": true, - "requires": { - "truncate-utf8-bytes": "^1.0.0" - } - }, - "sass-graph": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", - "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^13.3.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "dev": true, - "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "sdp": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/sdp/-/sdp-1.5.4.tgz", - "integrity": "sha1-jgOPbdsUvXZa4fS1IW4SCUUR4NA=" - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "semver-greatest-satisfied-range": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", - "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", - "dev": true, - "requires": { - "sver-compat": "^1.5.0" - } - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } - }, - "serviceworker-cache-polyfill": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz", - "integrity": "sha1-3hnuc77yGrPAdAo3sz22JGS6ves=" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "shelljs": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", - "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "simctl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simctl/-/simctl-2.0.0.tgz", - "integrity": "sha512-5rB7rN4N3b0z0nFdy9eczVssXqrv2aAgdVRksPVqVoiDtvXmfzNvebp3EMdId2sAUzXIflarQlx4P0hjVQEzKQ==", - "requires": { - "shelljs": "^0.2.6", - "tail": "^0.4.0" - }, - "dependencies": { - "shelljs": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.2.6.tgz", - "integrity": "sha1-kEktcv/MgVmXa6umL7D2iE8MM3g=" - } - } - }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "optional": true - }, - "simple-get": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", - "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", - "optional": true, - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "simple-plist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.0.0.tgz", - "integrity": "sha512-043L2rO80LVF7zfZ+fqhsEkoJFvW8o59rt/l4ctx1TJWoTx7/jkiS1R5TatD15Z1oYnuLJytzE7gcnnBuIPL2g==", - "requires": { - "bplist-creator": "0.0.7", - "bplist-parser": "0.1.1", - "plist": "^3.0.1" - } - }, - "slash": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/slash/-/slash-0.1.3.tgz", - "integrity": "sha1-qnEMjvULjh0YetbP9G84xla6Dlc=", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - } - } - }, - "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "socks": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", - "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", - "dev": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "^4.1.0" - } - }, - "socks-proxy-agent": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", - "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", - "dev": true, - "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - }, - "dependencies": { - "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - } - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "sparkles": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", - "dev": true - }, - "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "split2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz", - "integrity": "sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==", - "dev": true, - "requires": { - "readable-stream": "^3.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "ssh-config": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/ssh-config/-/ssh-config-1.1.6.tgz", - "integrity": "sha512-ZPO9rECxzs5JIQ6G/2EfL1I9ho/BVZkx9HRKn8+0af7QgwAmumQ7XBFP1ggMyPMo+/tUbmv0HFdv4qifdO/9JA==", - "dev": true - }, - "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "dev": true - }, - "stat-mode": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", - "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-buffers": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", - "integrity": "sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=" - }, - "stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", - "dev": true, - "requires": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "stream-exhaust": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", - "dev": true - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string.prototype.codepointat": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", - "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" - }, - "string.prototype.trim": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", - "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.0", - "function-bind": "^1.0.2" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringify-package": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", - "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "sumchecker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-2.0.2.tgz", - "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=", - "dev": true, - "requires": { - "debug": "^2.2.0" - } - }, - "superagent": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz", - "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==", - "dev": true, - "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.7", - "form-data": "^3.0.0", - "formidable": "^1.2.2", - "methods": "^1.1.2", - "mime": "^2.4.6", - "qs": "^6.9.4", - "readable-stream": "^3.6.0", - "semver": "^7.3.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", - "dev": true - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - } - } - }, - "superagent-proxy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/superagent-proxy/-/superagent-proxy-2.0.0.tgz", - "integrity": "sha512-TktJma5jPdiH1BNN+reF/RMW3b8aBTCV7KlLFV0uYcREgNf3pvo7Rdt564OcFHwkGb3mYEhHuWPBhSbOwiNaYw==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "proxy-agent": "3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "sver-compat": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", - "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", - "dev": true, - "requires": { - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" - } - }, - "sw-toolbox": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", - "integrity": "sha1-Jt8dHHA0hljk3qKIQxkUm3sxg7U=", - "requires": { - "path-to-regexp": "^1.0.1", - "serviceworker-cache-polyfill": "^4.0.0" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" - }, - "systeminformation": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.27.3.tgz", - "integrity": "sha512-0Nc8AYEK818h7FI+bbe/kj7xXsMD5zOHvO9alUqQH/G4MHXu5tHQfWqC/bzWOk4JtoQPhnyLgxMYncDA2eeSBw==" - }, - "tail": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/tail/-/tail-0.4.0.tgz", - "integrity": "sha1-0p3nJ1DMmdseBTr/E8NZ7PtxMAI=" - }, - "tapable": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", - "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", - "dev": true - }, - "tape": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/tape/-/tape-4.11.0.tgz", - "integrity": "sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA==", - "requires": { - "deep-equal": "~1.0.1", - "defined": "~1.0.0", - "for-each": "~0.3.3", - "function-bind": "~1.1.1", - "glob": "~7.1.4", - "has": "~1.0.3", - "inherits": "~2.0.4", - "minimist": "~1.2.0", - "object-inspect": "~1.6.0", - "resolve": "~1.11.1", - "resumer": "~0.0.0", - "string.prototype.trim": "~1.1.2", - "through": "~2.3.8" - }, - "dependencies": { - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "dev": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - }, - "tar-fs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", - "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", - "optional": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "optional": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "tar-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz", - "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==", - "optional": true, - "requires": { - "bl": "^4.0.1", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "optional": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "temp-file": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.1.3.tgz", - "integrity": "sha512-oz2J77loDE9sGrlRTqBzwbsUvoBD2BpyXeaRPKyGwBIwaamSs2jdqAfhutw7Tch9llr1u8E2ruoug09rNPa3PA==", - "dev": true, - "requires": { - "async-exit-hook": "^2.0.1", - "bluebird-lst": "^1.0.5", - "fs-extra-p": "^4.6.1", - "lazy-val": "^1.0.3" - } - }, - "term-size": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", - "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", - "dev": true, - "requires": { - "through2": "~2.0.0", - "xtend": "~4.0.0" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", - "dev": true - }, - "time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "tiny-lr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", - "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", - "dev": true, - "requires": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", - "dev": true, - "requires": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" - } - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - } - } - }, - "to-through": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", - "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", - "dev": true, - "requires": { - "through2": "^2.0.3" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - } - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "dev": true, - "requires": { - "glob": "^7.1.2" - } - }, - "truncate-utf8-bytes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", - "dev": true, - "requires": { - "utf8-byte-length": "^1.0.1" - } - }, - "ts-md5": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.2.7.tgz", - "integrity": "sha512-emODogvKGWi1KO1l9c6YxLMBn6CEH3VrH5mVPIyOtxBG52BvV4jP3GWz6bOZCz61nLgBc3ffQYE4+EHfCD+V7w==" - }, - "tsickle": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.27.5.tgz", - "integrity": "sha512-NP+CjM1EXza/M8mOXBLH3vkFEJiu1zfEAlC5WdJxHPn8l96QPz5eooP6uAgYtw1CcKfuSyIiheNUdKxtDWCNeg==", - "requires": { - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map": "^0.6.0", - "source-map-support": "^0.5.0" - } - }, - "tslib": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", - "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==" - }, - "tslint": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz", - "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==", - "dev": true, - "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.27.2" - } - }, - "tslint-eslint-rules": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz", - "integrity": "sha1-fDDniC8mvCdr/5HSOEl1xp2viLo=", - "dev": true, - "requires": { - "doctrine": "^0.7.2", - "tslib": "^1.0.0", - "tsutils": "^1.4.0" - }, - "dependencies": { - "tsutils": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz", - "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=", - "dev": true - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", - "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", - "dev": true - }, - "uglify-es": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.2.2.tgz", - "integrity": "sha512-l+s5VLzFwGJfS+fbqaGf/Dfwo1MF13jLOF2ekL0PytzqEqQ6cVppvHf4jquqFok+35USMpKjqkYxy6pQyUcuug==", - "dev": true, - "requires": { - "commander": "~2.12.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", - "dev": true - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "dev": true, - "requires": { - "source-map": "^0.5.6", - "uglify-js": "^2.8.29", - "webpack-sources": "^1.0.1" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - } - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } - } - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true - }, - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" - }, - "undertaker": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", - "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "bach": "^1.0.0", - "collection-map": "^1.0.0", - "es6-weak-map": "^2.0.1", - "last-run": "^1.1.0", - "object.defaults": "^1.0.0", - "object.reduce": "^1.0.0", - "undertaker-registry": "^1.0.0" - } - }, - "undertaker-registry": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unique-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", - "dev": true, - "requires": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" - }, - "unorm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", - "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "optional": true - }, - "update-notifier": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.1.tgz", - "integrity": "sha512-9y+Kds0+LoLG6yN802wVXoIfxYEwh3FlZwzMwpCZp62S2i1/Jzeqb9Eeeju3NSHccGGasfGlK5/vEHbAifYRDg==", - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - } - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } - }, - "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "utf8-byte-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", - "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", - "dev": true - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, - "v8flags": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", - "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "valid-identifier": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/valid-identifier/-/valid-identifier-0.0.2.tgz", - "integrity": "sha512-zaSmOW6ykXwrkX0YTuFUSoALNEKGaQHpxBJQLb3TXspRNDpBwbfrIQCZqAQ0LKBlKuyn2YOq7NNd6415hvZ33g==" - }, - "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "requires": { - "builtins": "^1.0.3" - } - }, - "value-or-function": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", - "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", - "dev": true - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", - "dev": true, - "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - } - }, - "vinyl-fs": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", - "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", - "dev": true, - "requires": { - "fs-mkdirp-stream": "^1.0.0", - "glob-stream": "^6.1.0", - "graceful-fs": "^4.0.0", - "is-valid-glob": "^1.0.0", - "lazystream": "^1.0.0", - "lead": "^1.0.0", - "object.assign": "^4.0.4", - "pumpify": "^1.3.5", - "readable-stream": "^2.3.3", - "remove-bom-buffer": "^3.0.0", - "remove-bom-stream": "^1.2.0", - "resolve-options": "^1.1.0", - "through2": "^2.0.0", - "to-through": "^2.0.0", - "value-or-function": "^3.0.0", - "vinyl": "^2.0.0", - "vinyl-sourcemap": "^1.1.0" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "vinyl-sourcemap": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", - "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", - "dev": true, - "requires": { - "append-buffer": "^1.0.2", - "convert-source-map": "^1.5.0", - "graceful-fs": "^4.1.6", - "normalize-path": "^2.1.1", - "now-and-later": "^2.0.0", - "remove-bom-buffer": "^3.0.0", - "vinyl": "^2.0.0" - } - }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "dev": true - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "watchpack": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", - "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", - "dev": true, - "requires": { - "chokidar": "^3.4.1", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" - }, - "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true, - "optional": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "optional": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz", - "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "optional": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "optional": true - }, - "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "optional": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "watchpack-chokidar2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", - "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", - "dev": true, - "optional": true, - "requires": { - "chokidar": "^2.1.8" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "optional": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "optional": true - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - } - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "web-animations-js": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz", - "integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA==" - }, - "webpack": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", - "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", - "dev": true, - "requires": { - "acorn": "^5.0.0", - "acorn-dynamic-import": "^2.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "async": "^2.1.2", - "enhanced-resolve": "^3.4.0", - "escope": "^3.6.0", - "interpret": "^1.0.0", - "json-loader": "^0.5.4", - "json5": "^0.5.1", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "mkdirp": "~0.5.0", - "node-libs-browser": "^2.0.0", - "source-map": "^0.5.3", - "supports-color": "^4.2.1", - "tapable": "^0.2.7", - "uglifyjs-webpack-plugin": "^0.4.6", - "watchpack": "^1.4.0", - "webpack-sources": "^1.0.1", - "yargs": "^8.0.2" - }, - "dependencies": { - "ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } - } - }, - "webpack-merge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", - "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, - "webpack-sources": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", - "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "webrtc-adapter": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-3.4.3.tgz", - "integrity": "sha1-tjYGLu6abvFYrNDYUBtnhDS1bxY=", - "requires": { - "sdp": "^1.5.0" - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "win-release": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz", - "integrity": "sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=", - "dev": true, - "requires": { - "semver": "^5.0.1" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "windows-release": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.3.tgz", - "integrity": "sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg==", - "requires": { - "execa": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "with-open-file": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", - "integrity": "sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA==", - "requires": { - "p-finally": "^1.0.0", - "p-try": "^2.1.0", - "pify": "^4.0.1" - }, - "dependencies": { - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.2.tgz", - "integrity": "sha512-t+WGpsNxhMR4v6EClXS8r8km5ZljKJzyGhJf7goJz9k5Ye3+b5Bvno5rjqPuIBn5mnn5GBb7o8IrIWHxX1qOLQ==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - }, - "xcode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xcode/-/xcode-2.0.0.tgz", - "integrity": "sha512-5xF6RCjAdDEiEsbbZaS/gBRt3jZ/177otZcpoLCjGN/u1LrfgH7/Sgeeavpr/jELpyDqN2im3AKosl2G2W8hfw==", - "requires": { - "simple-plist": "^1.0.0", - "uuid": "^3.3.2" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" - }, - "xml-escape": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz", - "integrity": "sha1-OQTBQ/qOs6ADDsZG0pAqLxtwbEQ=" - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" - }, - "xmldom": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", - "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" - }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "dev": true, - "requires": { - "camelcase": "^3.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - } - } - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "zone.js": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz", - "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 75c1227a1..000000000 --- a/package.json +++ /dev/null @@ -1,292 +0,0 @@ -{ - "name": "moodlemobile", - "version": "3.9.3", - "description": "The official app for Moodle.", - "author": { - "name": "Moodle Pty Ltd.", - "email": "mobile@moodle.com" - }, - "config": { - "ionic_webpack": "./config/webpack.config.js", - "ionic_copy": "./config/copy.config.js", - "ionic_uglifyjs": "./config/uglifyjs.config.js", - "ionic_sass": "./config/sass.config.js" - }, - "repository": { - "type": "git", - "url": "https://github.com/moodlehq/moodlemobile2.git" - }, - "license": "Apache-2.0", - "licenses": [ - { - "type": "Apache-2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0" - } - ], - "scripts": { - "start": "npm run dev", - "dev": "ionic serve", - "dev:android": "ionic cordova run android --livereload", - "dev:ios": "ionic cordova run ios --livereload", - "prod:android": "ionic cordova run android --aot", - "prod:ios": "ionic cordova run ios --aot", - "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": "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.11", - "@angular/common": "^5.2.11", - "@angular/compiler": "^5.2.11", - "@angular/compiler-cli": "^5.2.11", - "@angular/core": "^5.2.11", - "@angular/forms": "^5.2.11", - "@angular/platform-browser": "^5.2.11", - "@angular/platform-browser-dynamic": "^5.2.11", - "@ionic-native/badge": "^4.20.0", - "@ionic-native/camera": "^4.20.0", - "@ionic-native/chooser": "^4.20.0", - "@ionic-native/clipboard": "^4.20.0", - "@ionic-native/core": "^4.20.0", - "@ionic-native/device": "^4.20.0", - "@ionic-native/diagnostic": "^4.2.0", - "@ionic-native/file": "^4.20.0", - "@ionic-native/file-opener": "^4.20.0", - "@ionic-native/file-transfer": "^4.20.0", - "@ionic-native/geolocation": "^4.20.0", - "@ionic-native/globalization": "^4.20.0", - "@ionic-native/http": "^4.20.0", - "@ionic-native/in-app-browser": "^4.20.0", - "@ionic-native/keyboard": "^4.20.0", - "@ionic-native/local-notifications": "^4.20.0", - "@ionic-native/media": "^4.20.0", - "@ionic-native/media-capture": "^4.20.0", - "@ionic-native/network": "^4.20.0", - "@ionic-native/push": "^4.20.0", - "@ionic-native/qr-scanner": "^4.20.0", - "@ionic-native/screen-orientation": "^4.20.0", - "@ionic-native/splash-screen": "^4.20.0", - "@ionic-native/sqlite": "^4.20.0", - "@ionic-native/status-bar": "^4.20.0", - "@ionic-native/web-intent": "^4.20.0", - "@ionic-native/zip": "^4.20.0", - "@ngx-translate/core": "^8.0.0", - "@ngx-translate/http-loader": "^2.0.1", - "ajv": "^6.11.0", - "chart.js": "^2.9.3", - "com-darryncampbell-cordova-plugin-intent": "^1.3.0", - "cordova": "^10.0.0", - "cordova-android": "^8.1.0", - "cordova-android-support-gradle-release": "^3.0.1", - "cordova-clipboard": "^1.3.0", - "cordova-ios": "^5.1.1", - "cordova-plugin-advanced-http": "^2.4.1", - "cordova-plugin-badge": "^0.8.8", - "cordova-plugin-camera": "^4.1.0", - "cordova-plugin-chooser": "^1.3.2", - "cordova-plugin-customurlscheme": "^5.0.1", - "cordova-plugin-device": "^2.0.3", - "cordova-plugin-file": "^6.0.2", - "cordova-plugin-file-opener2": "^3.0.4", - "cordova-plugin-file-transfer": "^1.7.1", - "cordova-plugin-geolocation": "git+https://github.com/apache/cordova-plugin-geolocation.git#89cf51d222e8f225bdfb661965b3007d669c40ff", - "cordova-plugin-globalization": "^1.11.0", - "cordova-plugin-inappbrowser": "git+https://github.com/moodlemobile/cordova-plugin-inappbrowser.git#moodle", - "cordova-plugin-ionic-keyboard": "2.1.3", - "cordova-plugin-ionic-webview": "git+https://github.com/moodlemobile/cordova-plugin-ionic-webview.git#500-moodle", - "cordova-plugin-local-notification": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle", - "cordova-plugin-media": "^5.0.3", - "cordova-plugin-media-capture": "^3.0.3", - "cordova-plugin-network-information": "^2.0.2", - "cordova-plugin-qrscanner": "git+https://github.com/moodlemobile/cordova-plugin-qrscanner.git#dist", - "cordova-plugin-screen-orientation": "^3.0.2", - "cordova-plugin-splashscreen": "^6.0.0", - "cordova-plugin-statusbar": "^2.4.3", - "cordova-plugin-whitelist": "^1.3.4", - "cordova-plugin-wkuserscript": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git", - "cordova-plugin-wkwebview-cookies": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git", - "cordova-plugin-zip": "^3.1.0", - "cordova-sqlite-storage": "^4.0.0", - "cordova-support-google-services": "^1.3.2", - "es6-promise-plugin": "^4.2.2", - "font-awesome": "^4.7.0", - "inquirer": "^7.3.2", - "ionic-angular": "3.9.9", - "ionicons": "3.0.0", - "jszip": "^3.1.5", - "mathjax": "2.7.7", - "moment": "^2.24.0", - "nl.kingsquare.cordova.background-audio": "^1.0.1", - "phonegap-plugin-multidex": "^1.0.0", - "phonegap-plugin-push": "git+https://github.com/moodlemobile/phonegap-plugin-push.git#moodle-v3", - "promise.prototype.finally": "3.1.0", - "rxjs": "^5.5.12", - "sw-toolbox": "^3.6.0", - "ts-md5": "^1.2.7", - "web-animations-js": "^2.3.2", - "zone.js": "^0.8.29" - }, - "devDependencies": { - "@ionic/app-scripts": "3.2.3", - "@ionic/cli": "^6.11.7", - "@types/cordova": "^0.0.34", - "@types/cordova-plugin-file-transfer": "^0.0.3", - "@types/cordova-plugin-globalization": "^0.0.3", - "@types/cordova-plugin-network-information": "^0.0.3", - "@types/node": "^8.10.59", - "@types/promise.prototype.finally": "^2.0.4", - "acorn": "^5.7.4", - "cordova.plugins.diagnostic": "^5.0.2", - "electron-builder-lib": "^20.23.1", - "electron-rebuild": "^1.10.0", - "gulp": "4.0.2", - "gulp-clip-empty-files": "^0.1.2", - "gulp-concat": "^2.6.1", - "gulp-flatten": "^0.4.0", - "gulp-htmlmin": "^5.0.1", - "gulp-rename": "^2.0.0", - "gulp-slash": "^1.1.3", - "lodash.template": "^4.5.0", - "minimist": "^1.2.5", - "native-run": "^1.0.0", - "node-loader": "^0.6.0", - "request": "^2.88.2", - "through": "^2.3.8", - "typescript": "~2.6.2", - "vinyl": "^2.2.0", - "webpack-merge": "^4.2.2" - }, - "optionalDependencies": { - "keytar": "^6.0.1" - }, - "browser": { - "electron": false - }, - "cordova": { - "platforms": [ - "android", - "ios" - ], - "plugins": { - "com-darryncampbell-cordova-plugin-intent": {}, - "cordova-android-support-gradle-release": { - "ANDROID_SUPPORT_VERSION": "27.1.0" - }, - "cordova-clipboard": {}, - "cordova-plugin-badge": {}, - "cordova-plugin-camera": {}, - "cordova-plugin-customurlscheme": { - "URL_SCHEME": "moodlemobile" - }, - "cordova-plugin-device": {}, - "cordova-plugin-file": {}, - "cordova-plugin-file-opener2": {}, - "cordova-plugin-file-transfer": {}, - "cordova-plugin-globalization": {}, - "cordova-plugin-inappbrowser": {}, - "cordova-plugin-ionic-keyboard": {}, - "cordova-plugin-local-notification": {}, - "cordova-plugin-media-capture": {}, - "cordova-plugin-network-information": {}, - "cordova-plugin-screen-orientation": {}, - "cordova-plugin-splashscreen": {}, - "cordova-plugin-statusbar": {}, - "cordova-plugin-whitelist": {}, - "cordova-plugin-zip": {}, - "cordova-sqlite-storage": {}, - "nl.kingsquare.cordova.background-audio": {}, - "phonegap-plugin-push": { - "ANDROID_SUPPORT_V13_VERSION": "27.+", - "FCM_VERSION": "17.5.+" - }, - "cordova-plugin-geolocation": { - "GEOLOCATION_USAGE_DESCRIPTION": "We need your location so you can attach it as part of your submissions.", - "GPS_REQUIRED": "false" - }, - "cordova-plugin-ionic-webview": {}, - "cordova-plugin-advanced-http": { - "OKHTTP_VERSION": "3.10.0" - }, - "cordova-plugin-wkwebview-cookies": {}, - "cordova-plugin-qrscanner": {}, - "cordova-plugin-chooser": {}, - "cordova-plugin-wkuserscript": {}, - "cordova-plugin-media": { - "KEEP_AVAUDIOSESSION_ALWAYS_ACTIVE": "NO" - }, - "cordova.plugins.diagnostic": {} - } - }, - "main": "desktop/electron.js", - "build": { - "appId": "com.moodle.moodledesktop", - "productName": "Moodle Desktop", - "files": [ - "desktop/electron.js", - "www/**/*", - "!config", - "!desktop/assets", - "!desktop/dist", - "!node_modules", - "!**/e2e", - "!hooks", - "!platforms", - "!plugins", - "!resources", - "!src", - "!**/*.scss" - ], - "directories": { - "output": "desktop/dist" - }, - "protocols": [ - { - "name": "Moodle Mobile URL", - "schemes": [ - "moodlemobile" - ], - "role": "Viewer" - } - ], - "compression": "maximum", - "electronVersion": "4.2.5", - "mac": { - "category": "public.app-category.education", - "icon": "resources/desktop/icon.icns", - "target": "mas", - "bundleVersion": "3.9.3", - "extendInfo": { - "ElectronTeamID": "2NU57U5PAW" - } - }, - "win": { - "target": "appx", - "icon": "resources/desktop/icon.ico" - }, - "linux": { - "category": "Education", - "target": "AppImage" - }, - "snap": { - "confinement": "classic" - }, - "nsis": { - "deleteAppDataOnUninstall": true - } - }, - "engines": { - "node": ">=11.x" - } -} diff --git a/resources/android/icon-foreground.png b/resources/android/icon-foreground.png deleted file mode 100644 index cdadeb07f..000000000 Binary files a/resources/android/icon-foreground.png and /dev/null differ diff --git a/resources/android/icon/drawable-hdpi-smallicon.png b/resources/android/icon/drawable-hdpi-smallicon.png deleted file mode 100644 index 5262dcb0f..000000000 Binary files a/resources/android/icon/drawable-hdpi-smallicon.png and /dev/null differ diff --git a/resources/android/icon/drawable-ldpi-smallicon.png b/resources/android/icon/drawable-ldpi-smallicon.png deleted file mode 100644 index 3360a685b..000000000 Binary files a/resources/android/icon/drawable-ldpi-smallicon.png and /dev/null differ diff --git a/resources/android/icon/drawable-mdpi-smallicon.png b/resources/android/icon/drawable-mdpi-smallicon.png deleted file mode 100644 index c8df8f94d..000000000 Binary files a/resources/android/icon/drawable-mdpi-smallicon.png and /dev/null differ diff --git a/resources/android/icon/drawable-xhdpi-smallicon.png b/resources/android/icon/drawable-xhdpi-smallicon.png deleted file mode 100644 index 28081d204..000000000 Binary files a/resources/android/icon/drawable-xhdpi-smallicon.png and /dev/null differ diff --git a/resources/desktop/Square150x150Logo.png b/resources/desktop/Square150x150Logo.png deleted file mode 100644 index 95de043c2..000000000 Binary files a/resources/desktop/Square150x150Logo.png and /dev/null differ diff --git a/resources/desktop/Square44x44Logo.png b/resources/desktop/Square44x44Logo.png deleted file mode 100644 index df2f21b2d..000000000 Binary files a/resources/desktop/Square44x44Logo.png and /dev/null differ diff --git a/resources/desktop/StoreLogo.png b/resources/desktop/StoreLogo.png deleted file mode 100644 index f41b1f803..000000000 Binary files a/resources/desktop/StoreLogo.png and /dev/null differ diff --git a/resources/desktop/Wide310x150Logo.png b/resources/desktop/Wide310x150Logo.png deleted file mode 100644 index 05b810bed..000000000 Binary files a/resources/desktop/Wide310x150Logo.png and /dev/null differ diff --git a/resources/desktop/icon.icns b/resources/desktop/icon.icns deleted file mode 100644 index 2799114b2..000000000 Binary files a/resources/desktop/icon.icns and /dev/null differ diff --git a/resources/desktop/icon.ico b/resources/desktop/icon.ico deleted file mode 100644 index 483c9647c..000000000 Binary files a/resources/desktop/icon.ico and /dev/null differ diff --git a/resources/icon.png b/resources/icon.png deleted file mode 100644 index 7abfc0a4f..000000000 Binary files a/resources/icon.png and /dev/null differ diff --git a/resources/splash.png b/resources/splash.png deleted file mode 100644 index e7889ccf9..000000000 Binary files a/resources/splash.png and /dev/null differ diff --git a/resources/values/colors.xml b/resources/values/colors.xml deleted file mode 100644 index 841ad9a24..000000000 --- a/resources/values/colors.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFF - diff --git a/scripts/aot.sh b/scripts/aot.sh deleted file mode 100755 index 4ebb56b0d..000000000 --- a/scripts/aot.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -source "scripts/functions.sh" - -if [ ! -z $GIT_ORG_PRIVATE ] && [ ! -z $GIT_TOKEN ] ; then - print_title "Run scripts" - git clone --depth 1 https://$GIT_TOKEN@github.com/$GIT_ORG_PRIVATE/apps-scripts.git ../scripts - cp ../scripts/*.sh scripts/ - - if [ $TRAVIS_BUILD_STAGE_NAME == 'prepare' ] && [ -f scripts/prepare.sh ] ; then - print_title 'Prepare Build' - ./scripts/prepare.sh - - if [ $? -ne 0 ]; then - exit 1 - fi - elif [ $TRAVIS_BUILD_STAGE_NAME != 'prepare' ] && [ -f scripts/platform.sh ]; then - print_title 'Platform Build' - ./scripts/platform.sh - - if [ $? -ne 0 ]; then - exit 1 - fi - fi -else - print_title "AOT Compilation" - # Dynamic template loading without errors. - sed -ie $'s~throw new Error("No ResourceLoader.*~url = "templates/" + url;\\\nvar resolve;\\\nvar reject;\\\nvar promise = new Promise(function (res, rej) {\\\nresolve = res;\\\nreject = rej;\\\n});\\\nvar xhr = new XMLHttpRequest();\\\nxhr.open("GET", url, true);\\\nxhr.responseType = "text";\\\nxhr.onload = function () {\\\nvar response = xhr.response || xhr.responseText;\\\nvar status = xhr.status === 1223 ? 204 : xhr.status;\\\nif (status === 0) {\\\nstatus = response ? 200 : 0;\\\n}\\\nif (200 <= status \&\& status <= 300) {\\\nresolve(response);\\\n}\\\nelse {\\\nreject("Failed to load " + url);\\\n}\\\n};\\\nxhr.onerror = function () { reject("Failed to load " + url); };\\\nxhr.send();\\\nreturn promise;\\\n~g' node_modules/@angular/platform-browser-dynamic/esm5/platform-browser-dynamic.js - # Do not run JS optimizations to avoid problems with site plugins. - sed -ie "s/context\.isProd || hasArg('--optimizeJs')/false/g" node_modules/@ionic/app-scripts/dist/util/config.js - npm run ionic:build -- --prod -fi - diff --git a/scripts/create_langindex.sh b/scripts/create_langindex.sh deleted file mode 100755 index 9a82b64e0..000000000 --- a/scripts/create_langindex.sh +++ /dev/null @@ -1,327 +0,0 @@ -#!/bin/bash -source "functions.sh" - -#Saves or updates a key on langindex_old.json -function save_key { - local key=$1 - local found=$2 - - print_ok "$key=$found" - echo "{\"$key\": \"$found\"}" > langindex_old.json - jq -s '.[0] + .[1]' langindex.json langindex_old.json > langindex_new.json - mv langindex_new.json langindex.json -} - -#Removes a key on langindex_old.json -function remove_key { - local key=$1 - - cat langindex.json | jq 'del(."'$key'")' > langindex_new.json - mv langindex_new.json langindex.json - print_ok "Deleted unused key $key" -} - -#Check if and i exists in php file -function exists_in_file { - local file=$1 - local id=$2 - - file=`echo $file | sed s/^mod_workshop_assessment/workshopform/1` - file=`echo $file | sed s/^mod_assign_/assign/1` - file=`echo $file | sed s/^mod_//1` - - completeFile="$LANGPACKSFOLDER/en/$file.php" - if [ -f "$completeFile" ]; then - coincidence=`grep "string\[\'$id\'\]" $completeFile` - if [ ! -z "$coincidence" ]; then - found=$file - return - fi - fi - found=0 -} - -#Checks if a key exists on the original local_moodlemobileapp.php -function exists_in_mobile { - local file='local_moodlemobileapp' - exists_in_file $file $key -} - -function do_match { - match=$1 - filematch="" - - coincidence=`grep "$match" $LANGPACKSFOLDER/en/*.php | wc -l` - if [ $coincidence -eq 1 ]; then - filematch=`grep "$match" $LANGPACKSFOLDER/en/*.php | cut -d'/' -f5 | cut -d'.' -f1` - exists_in_file $filematch $plainid - elif [ $coincidence -gt 0 ] && [ "$#" -gt 1 ]; then - print_message $2 - tput setaf 6 - grep "$match" $LANGPACKSFOLDER/en/*.php - fi -} - -#Find if the id or the value can be found on files to help providing a solution. -function find_matches { - do_match "string\[\'$plainid\'\] = \'$value\'" "Found EXACT match for $key in the following paths" - if [ $coincidence -gt 0 ]; then - case=1 - return - fi - - do_match " = \'$value\'" "Found some string VALUES for $key in the following paths" - if [ $coincidence -gt 0 ]; then - case=2 - return - fi - - do_match "string\[\'$plainid\'\]" "Found some string KEYS for $key in the following paths, value $value" - if [ $coincidence -gt 0 ]; then - case=3 - return - fi - - print_message "No match found for $key add it to local_moodlemobileapp" - save_key $key "local_moodlemobileapp" -} - -function find_single_matches { - do_match "string\[\'$plainid\'\] = \'$value\'" - if [ ! -z $filematch ] && [ $found != 0 ]; then - case=1 - return - fi - - do_match " = \'$value\'" - if [ ! -z $filematch ] && [ $filematch != 'local_moodlemobileapp' ]; then - case=2 - print_message "Found some string VALUES for $key in the following paths $filematch" - tput setaf 6 - grep "$match" $LANGPACKSFOLDER/en/*.php - return - fi - - do_match "string\[\'$plainid\'\]" - if [ ! -z $filematch ] && [ $found != 0 ]; then - case=3 - return - fi -} - - -#Tries to gues the file where the id will be found. -function guess_file { - local key=$1 - local value=$2 - - local type=`echo $key | cut -d'.' -f1` - local component=`echo $key | cut -d'.' -f2` - local plainid=`echo $key | cut -d'.' -f3-` - - if [ -z "$plainid" ]; then - plainid=$component - component='moodle' - fi - - exists_in_file $component $plainid - - if [ $found == 0 ]; then - tempid=`echo $plainid | sed s/^mod_//1` - if [ $component == 'moodle' ] && [ "$tempid" != "$plainid" ]; then - exists_in_file $plainid pluginname - - if [ $found != 0 ]; then - found=$found/pluginname - fi - fi - fi - - # Not found in file, try in local_moodlemobileapp - if [ $found == 0 ]; then - exists_in_mobile - fi - - # Still not found, if only found in one file, use it. - if [ $found == 0 ]; then - find_single_matches - fi - - # Last fallback. - if [ $found == 0 ]; then - exists_in_file 'moodle' $plainid - fi - - if [ $found == 0 ]; then - find_matches - else - save_key $key $found - fi -} - -function current_translation_exists { - local key=$1 - local current=$2 - local file=$3 - - plainid=`echo $key | cut -d'.' -f3-` - - if [ -z "$plainid" ]; then - plainid=`echo $key | cut -d'.' -f2` - fi - - local currentFile=`echo $current | cut -d'/' -f1` - local currentStr=`echo $current | cut -d'/' -f2-` - if [ $currentFile == $current ]; then - currentStr=$plainid - fi - - exists_in_file $currentFile $currentStr - if [ $found == 0 ]; then - # Translation not found. - exec="jq -r .\"$key\" $file" - value=`$exec` - - print_error "Translation of '$currentStr' not found in '$currentFile'" - - guess_file $key "$value" - fi -} - -#Finds if there's a better file where to get the id from. -function find_better_file { - local key=$1 - local value=$2 - local current=$3 - - local type=`echo $key | cut -d'.' -f1` - local component=`echo $key | cut -d'.' -f2` - local plainid=`echo $key | cut -d'.' -f3-` - - if [ -z "$plainid" ]; then - plainid=$component - component='moodle' - fi - - local currentFile=`echo $current | cut -d'/' -f1` - local currentStr=`echo $current | cut -d'/' -f2-` - if [ $currentFile == $current ]; then - currentStr=$plainid - fi - - exists_in_file $component $plainid - if [ $found != 0 ] && [ $currentStr == $plainid ]; then - if [ $found != $currentFile ]; then - print_ok "Key '$key' found in component, no need to replace old '$current'" - fi - - return - fi - - # Still not found, if only found in one file, use it. - if [ $found == 0 ]; then - find_single_matches - fi - - if [ $found != 0 ] && [ $found != $currentFile ] && [ $case -lt 3 ]; then - print_message "Indexed string '$key' found in '$found' better than '$current'" - return - fi - - if [ $currentFile == 'local_moodlemobileapp' ]; then - exists_in_mobile - else - exists_in_file $currentFile $currentStr - fi - - if [ $found == 0 ]; then - print_error "Indexed string '$key' not found on current place '$current'" - if [ $currentFile != 'local_moodlemobileapp' ]; then - print_error "Execute this on AMOS - CPY [$currentStr,$currentFile],[$key,local_moodlemobileapp]" - save_key $key "local_moodlemobileapp" - fi - fi -} - -# Parses the file. -function parse_file { - findbetter=$2 - keys=`jq -r 'keys[]' $1` - for key in $keys; do - # Check if already parsed. - exec="jq -r .\"$key\" langindex.json" - found=`$exec` - - if [ -z "$found" ] || [ "$found" == 'null' ]; then - exec="jq -r .\"$key\" $1" - value=`$exec` - guess_file $key "$value" - else - if [ "$found" == 'donottranslate' ]; then - # Do nothing since is not translatable. - continue - elif [ ! -z "$findbetter" ]; then - exec="jq -r .\"$key\" $1" - value=`$exec` - find_better_file "$key" "$value" "$found" - elif [ "$found" != 'local_moodlemobileapp' ]; then - current_translation_exists "$key" "$found" "$1" - fi - fi - done - - # Do some cleanup - langkeys=`jq -r 'keys[]' langindex.json` - findkeys="${keys[@]}" - for key in $langkeys; do - # Check if already used. - array_contains "$key" "$findkeys" - - if [ -z "$found" ] || [ "$found" == 'null' ]; then - remove_key $key - fi - done -} - -# Checks if an array contains an string. -function array_contains { - local hayjack=$2 - local needle=$1 - found='' - for i in $hayjack; do - if [ "$i" == "$needle" ] ; then - found=$i - return - fi - done -} - -print_title 'Generating language from code...' -gulp lang - -print_title 'Getting languages' -git clone https://git.in.moodle.com/moodle/moodle-langpacks.git $LANGPACKSFOLDER -pushd $LANGPACKSFOLDER -BRANCHES=($(git branch -r --format="%(refname:lstrip=3)" --sort="refname" | grep MOODLE_)) -BRANCH=${BRANCHES[${#BRANCHES[@]}-1]} -git checkout $BRANCH -git pull -popd - -print_title 'Processing file' -#Create langindex.json if not exists. -if [ ! -f 'langindex.json' ]; then - echo "{}" > langindex.json -fi - -findbetter=$1 -parse_file '../src/assets/lang/en.json' $findbetter - -echo - -jq -S --indent 2 -s '.[0]' langindex.json > langindex_new.json -mv langindex_new.json langindex.json -rm langindex_old.json - -print_ok 'All done!' \ No newline at end of file diff --git a/scripts/functions.sh b/scripts/functions.sh deleted file mode 100644 index f90399189..000000000 --- a/scripts/functions.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash - -LANGPACKSFOLDER='../../moodle-langpacks' - -function check_success_exit { - if [ $? -ne 0 ]; then - print_error "$1" - exit 1 - elif [ "$#" -gt 1 ]; then - print_ok "$2" - fi -} - -function check_success { - if [ $? -ne 0 ]; then - print_error "$1" - elif [ "$#" -gt 1 ]; then - print_ok "$2" - fi -} - -function print_success { - if [ $? -ne 0 ]; then - print_message "$1" - $3=0 - else - print_ok "$2" - fi -} - -function print_error { - tput setaf 1; echo " ERROR: $1"; tput sgr0 -} - -function print_ok { - tput setaf 2; echo " OK: $1"; tput sgr0 - echo -} - -function print_message { - tput setaf 3; echo "-------- $1"; tput sgr0 - echo -} - -function print_title { - stepnumber=$(($stepnumber + 1)) - echo - tput setaf 5; echo "$stepnumber $1"; tput sgr0 - tput setaf 5; echo '=================='; tput sgr0 -} - -function telegram_notify { - if [ ! -z $TELEGRAM_APIKEY ] && [ ! -z $TELEGRAM_CHATID ] ; then - MESSAGE="Travis error: $1%0ABranch: $TRAVIS_BRANCH%0ARepo: $TRAVIS_REPO_SLUG" - URL="https://api.telegram.org/bot$TELEGRAM_APIKEY/sendMessage" - - curl -s -X POST $URL -d chat_id=$TELEGRAM_CHATID -d text="$MESSAGE" - fi -} - -function notify_on_error_exit { - if [ $? -ne 0 ]; then - print_error "$1" - telegram_notify "$1" - exit 1 - fi -} diff --git a/scripts/get_ws_changes.php b/scripts/get_ws_changes.php deleted file mode 100644 index fff119f1b..000000000 --- a/scripts/get_ws_changes.php +++ /dev/null @@ -1,99 +0,0 @@ -. - -/** - * Script for detecting changes in a WS params or return data, version by version. - * - * The first parameter (required) is the path to the Moodle installation to use. - * The second parameter (required) is the name to the WS to convert. - * The third parameter (optional) is a number: 1 to convert the params structure, - * 0 to convert the returns structure. Defaults to 0. - */ - -if (!isset($argv[1])) { - echo "ERROR: Please pass the path to the folder containing the Moodle installations as the first parameter.\n"; - die(); -} - - -if (!isset($argv[2])) { - echo "ERROR: Please pass the WS name as the second parameter.\n"; - die(); -} - -define('CLI_SCRIPT', true); -require_once('ws_to_ts_functions.php'); - -$versions = array('master', '38', '37', '36', '35', '34', '33', '32', '31'); - -$moodlespath = $argv[1]; -$wsname = $argv[2]; -$useparams = !!(isset($argv[3]) && $argv[3]); -$pathseparator = '/'; - -// Get the path to the script. -$index = strrpos(__FILE__, $pathseparator); -if ($index === false) { - $pathseparator = '\\'; - $index = strrpos(__FILE__, $pathseparator); -} -$scriptfolder = substr(__FILE__, 0, $index); -$scriptpath = concatenate_paths($scriptfolder, 'get_ws_structure.php', $pathseparator); - -$previousstructure = null; -$previousversion = null; -$libsloaded = false; - -foreach ($versions as $version) { - $moodlepath = concatenate_paths($moodlespath, 'stable_' . $version, $pathseparator); - - if (!$libsloaded) { - $libsloaded = true; - - require($moodlepath . '/config.php'); - require($CFG->dirroot . '/webservice/lib.php'); - } - - // Get the structure in this Moodle version. - $structure = shell_exec("php $scriptpath $moodlepath $wsname " . ($useparams ? 'true' : '')); - - if (strpos($structure, 'ERROR:') === 0) { - echo "WS not found in version $version. Stop.\n"; - break; - } - - $structure = unserialize($structure); - - if ($previousstructure != null) { - echo "*** Check changes from version $version to $previousversion ***\n"; - - $messages = detect_ws_changes($previousstructure, $structure); - - if (count($messages) > 0) { - $haschanged = true; - - foreach($messages as $message) { - echo "$message\n"; - } - } else { - echo "No changes found.\n"; - } - echo "\n"; - } - - $previousstructure = $structure; - $previousversion = $version; -} diff --git a/scripts/get_ws_structure.php b/scripts/get_ws_structure.php deleted file mode 100644 index bfa975ee9..000000000 --- a/scripts/get_ws_structure.php +++ /dev/null @@ -1,55 +0,0 @@ -. - -/** - * Script for getting the PHP structure of a WS returns or params. - * - * The first parameter (required) is the path to the Moodle installation to use. - * The second parameter (required) is the name to the WS to convert. - * The third parameter (optional) is a number: 1 to convert the params structure, - * 0 to convert the returns structure. Defaults to 0. - */ - -if (!isset($argv[1])) { - echo "ERROR: Please pass the Moodle path as the first parameter.\n"; - die(); -} - - -if (!isset($argv[2])) { - echo "ERROR: Please pass the WS name as the second parameter.\n"; - die(); -} - -$moodlepath = $argv[1]; -$wsname = $argv[2]; -$useparams = !!(isset($argv[3]) && $argv[3]); - -define('CLI_SCRIPT', true); - -require($moodlepath . '/config.php'); -require($CFG->dirroot . '/webservice/lib.php'); -require_once('ws_to_ts_functions.php'); - -$structure = get_ws_structure($wsname, $useparams); - -if ($structure === false) { - echo "ERROR: The WS wasn't found in this Moodle installation.\n"; - die(); -} - -remove_default_closures($structure); -echo serialize($structure); diff --git a/scripts/get_ws_ts.php b/scripts/get_ws_ts.php deleted file mode 100644 index 900a153b8..000000000 --- a/scripts/get_ws_ts.php +++ /dev/null @@ -1,62 +0,0 @@ -. - -/** - * Script for converting a PHP WS structure to a TS type. - * - * The first parameter (required) is the path to the Moodle installation to use. - * The second parameter (required) is the name to the WS to convert. - * The third parameter (optional) is the name to put to the TS type. Defaults to "TypeName". - * The fourth parameter (optional) is a number: 1 to convert the params structure, - * 0 to convert the returns structure. Defaults to 0. - */ - -if (!isset($argv[1])) { - echo "ERROR: Please pass the Moodle path as the first parameter.\n"; - die(); -} - - -if (!isset($argv[2])) { - echo "ERROR: Please pass the WS name as the second parameter.\n"; - die(); -} - -$moodlepath = $argv[1]; -$wsname = $argv[2]; -$typename = isset($argv[3]) ? $argv[3] : 'TypeName'; -$useparams = !!(isset($argv[4]) && $argv[4]); - -define('CLI_SCRIPT', true); - -require($moodlepath . '/config.php'); -require($CFG->dirroot . '/webservice/lib.php'); -require_once('ws_to_ts_functions.php'); - -$structure = get_ws_structure($wsname, $useparams); - -if ($structure === false) { - echo "ERROR: The WS wasn't found in this Moodle installation.\n"; - die(); -} - -if ($useparams) { - $description = "Params of WS $wsname."; -} else { - $description = "Result of WS $wsname."; -} - -echo get_ts_doc(null, $description, '') . "export type $typename = " . convert_to_ts(null, $structure, $useparams) . ";\n"; diff --git a/scripts/lang_functions.php b/scripts/lang_functions.php deleted file mode 100644 index 668070a60..000000000 --- a/scripts/lang_functions.php +++ /dev/null @@ -1,474 +0,0 @@ -. - -/** - * 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() { - $local = 0; - // 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; - $local++; - } 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 ($local local)\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')) { - 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) { - echo "Cannot translate $lang, folder not found"; - - return false; - } - - if (OVERRIDE_LANG_SUFIX) { - $override_langfolder = get_langfolder($lang.OVERRIDE_LANG_SUFIX); - } else { - $override_langfolder = false; - } - - $total = count($keys); - $local = 0; - - $langparts = explode('-', $lang, 2); - $parentname = $langparts[0] ? $langparts[0] : ""; - $parent = ""; - - echo "Processing $lang"; - // Check parent language exists. - if ($parentname != $lang && get_langfolder($parentname)) { - echo " ($parentname)"; - $parent = $parentname; - } - - $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 ($value->file == 'donottranslate') { - // Restore it form the json. - if ($langFile && is_array($langFile) && isset($langFile[$key])) { - $translations[$key] = $langFile[$key]; - } else { - // If not present, do not count it in the total. - $total--; - } - - continue; - } - - if (TOTRANSLATE) { - echo "\n\t\tTo 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]; - - if ($value->file == 'local_moodlemobileapp') { - $local++; - } - } - if (TOTRANSLATE && !isset($string[$value->string])) { - 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 { - // @TODO: Remove that line when core.cannotconnect and core.login.invalidmoodleversion are completelly changed to use $a - if (($key == 'core.cannotconnect' || $key == 'core.login.invalidmoodleversion') && strpos($text, '2.4') != false) { - $text = str_replace('2.4', '{{$a}}', $text); - } - $local++; - } - - $translations[$key] = html_entity_decode($text); - } - - if (!empty($parent)) { - $translations['core.parentlanguage'] = $parent; - } else if (isset($translations['core.parentlanguage'])) { - unset($translations['core.parentlanguage']); - } - - // 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); - $bar = progressbar($percentage); - if (strlen($lang) <= 2 && !$parent) { - echo "\t"; - } - echo "\t\t$success of $total -> $percentage% $bar ($local local)\n"; - - if ($lang == 'en') { - generate_local_moodlemobileapp($keys, $translations); - override_component_lang_files($keys, $translations); - } - - return true; -} - -function progressbar($percentage) { - $done = floor($percentage/10); - return "\t".str_repeat('=', $done) . str_repeat('-', 10-$done); -} - -function detect_lang($lang, $keys) { - $langfoldername = get_langfolder($lang); - if (!$langfoldername) { - echo "Cannot translate $lang, folder not found"; - - 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; - } - - $title = $lang; - if ($parent != "" && $parent != $lang) { - $title .= " ($parent)"; - } - $langname = $string['thislanguage']; - $title .= " ".$langname." -D"; - - // Add the translation to the array. - foreach ($keys as $key => $value) { - $string = get_translation_strings($langfoldername, $value->file); - // Apply translations. - if (!$string) { - // Do not count non translatable in the totals. - if ($value->file == 'donottranslate') { - $total--; - } - continue; - } - - if (!isset($string[$value->string])) { - continue; - } else { - $text = $string[$value->string]; - } - - if ($value->file == 'local_moodlemobileapp') { - $local++; - } - - $success++; - } - - $percentage = floor($success/$total * 100); - $bar = progressbar($percentage); - - echo "Checking ".$title.str_repeat("\t", 7 - floor(mb_strlen($title, 'UTF-8')/8)); - echo "\t$success of $total -> $percentage% $bar ($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 = '. - -/** - * Version details. - * - * @package local - * @subpackage moodlemobileapp - * @copyright 2014 Juan Leyva - * @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"); -} diff --git a/scripts/langindex.json b/scripts/langindex.json deleted file mode 100644 index 2c1b0fd02..000000000 --- a/scripts/langindex.json +++ /dev/null @@ -1,2191 +0,0 @@ -{ - "addon.badges.alignment": "badges", - "addon.badges.badgedetails": "badges", - "addon.badges.badges": "badges", - "addon.badges.bendorsement": "badges", - "addon.badges.claimcomment": "badges", - "addon.badges.claimid": "badges", - "addon.badges.contact": "badges", - "addon.badges.dateawarded": "badges", - "addon.badges.expired": "badges", - "addon.badges.expirydate": "badges", - "addon.badges.imageauthoremail": "badges", - "addon.badges.imageauthorname": "badges", - "addon.badges.imageauthorurl": "badges", - "addon.badges.imagecaption": "badges", - "addon.badges.issuancedetails": "badges", - "addon.badges.issuerdetails": "badges", - "addon.badges.issueremail": "badges", - "addon.badges.issuername": "badges", - "addon.badges.issuerurl": "badges", - "addon.badges.language": "badges", - "addon.badges.noalignment": "badges", - "addon.badges.nobadges": "badges", - "addon.badges.norelated": "badges", - "addon.badges.recipientdetails": "badges", - "addon.badges.relatedbages": "badges", - "addon.badges.version": "badges", - "addon.badges.warnexpired": "badges", - "addon.block_activitymodules.pluginname": "block_activity_modules", - "addon.block_activityresults.pluginname": "block_activity_results", - "addon.block_badges.pluginname": "block_badges", - "addon.block_blogmenu.pluginname": "block_blog_menu", - "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.allincludinghidden": "block_myoverview", - "addon.block_myoverview.favourites": "block_myoverview", - "addon.block_myoverview.future": "block_myoverview", - "addon.block_myoverview.hiddencourses": "block_myoverview", - "addon.block_myoverview.inprogress": "block_myoverview", - "addon.block_myoverview.lastaccessed": "block_myoverview", - "addon.block_myoverview.nocourses": "block_myoverview", - "addon.block_myoverview.past": "block_myoverview", - "addon.block_myoverview.pluginname": "block_myoverview", - "addon.block_myoverview.shortname": "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", - "addon.block_timeline.next6months": "block_timeline", - "addon.block_timeline.next7days": "block_timeline", - "addon.block_timeline.nocoursesinprogress": "block_timeline", - "addon.block_timeline.noevents": "block_timeline", - "addon.block_timeline.overdue": "block_timeline", - "addon.block_timeline.pluginname": "block_timeline", - "addon.block_timeline.sortbycourses": "block_timeline", - "addon.block_timeline.sortbydates": "block_timeline", - "addon.blog.blog": "blog", - "addon.blog.blogentries": "blog", - "addon.blog.errorloadentries": "local_moodlemobileapp", - "addon.blog.linktooriginalentry": "blog", - "addon.blog.noentriesyet": "blog", - "addon.blog.publishtonoone": "blog", - "addon.blog.publishtosite": "blog", - "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.categoryevents": "calendar", - "addon.calendar.confirmeventdelete": "calendar", - "addon.calendar.confirmeventseriesdelete": "calendar", - "addon.calendar.courseevents": "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.groupevents": "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.siteevents": "calendar", - "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", - "addon.calendar.typedue": "calendar", - "addon.calendar.typegradingdue": "calendar", - "addon.calendar.typegroup": "calendar", - "addon.calendar.typeopen": "calendar", - "addon.calendar.typesite": "calendar", - "addon.calendar.typeuser": "calendar", - "addon.calendar.upcomingevents": "calendar", - "addon.calendar.userevents": "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", - "addon.competency.coursecompetencies": "tool_lp", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "tool_lp", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "tool_lp", - "addon.competency.crossreferencedcompetencies": "tool_lp", - "addon.competency.duedate": "tool_lp", - "addon.competency.errornocompetenciesfound": "local_moodlemobileapp", - "addon.competency.evidence": "tool_lp", - "addon.competency.evidence_competencyrule": "competency", - "addon.competency.evidence_coursecompleted": "competency", - "addon.competency.evidence_coursemodulecompleted": "competency", - "addon.competency.evidence_courserestored": "competency", - "addon.competency.evidence_evidenceofpriorlearninglinked": "competency", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "competency", - "addon.competency.evidence_manualoverride": "competency", - "addon.competency.evidence_manualoverrideincourse": "competency", - "addon.competency.evidence_manualoverrideinplan": "competency", - "addon.competency.learningplancompetencies": "tool_lp", - "addon.competency.learningplans": "tool_lp", - "addon.competency.myplans": "tool_lp", - "addon.competency.noactivities": "tool_lp", - "addon.competency.nocompetencies": "local_moodlemobileapp", - "addon.competency.nocompetenciesincourse": "tool_lp", - "addon.competency.nocrossreferencedcompetencies": "tool_lp", - "addon.competency.noevidence": "tool_lp", - "addon.competency.noplanswerecreated": "tool_lp", - "addon.competency.nouserplanswithcompetency": "competency", - "addon.competency.path": "tool_lp", - "addon.competency.planstatusactive": "competency", - "addon.competency.planstatuscomplete": "competency", - "addon.competency.planstatusdraft": "competency", - "addon.competency.planstatusinreview": "competency", - "addon.competency.planstatuswaitingforreview": "competency", - "addon.competency.proficient": "tool_lp", - "addon.competency.progress": "tool_lp", - "addon.competency.rating": "tool_lp", - "addon.competency.reviewstatus": "tool_lp", - "addon.competency.status": "tool_lp", - "addon.competency.template": "tool_lp", - "addon.competency.uponcoursecompletion": "tool_lp", - "addon.competency.usercompetencystatus_idle": "competency", - "addon.competency.usercompetencystatus_inreview": "competency", - "addon.competency.usercompetencystatus_waitingforreview": "competency", - "addon.competency.userplans": "competency", - "addon.competency.xcompetenciesproficientoutofy": "tool_lp", - "addon.competency.xcompetenciesproficientoutofyincourse": "tool_lp", - "addon.coursecompletion.complete": "local_moodlemobileapp", - "addon.coursecompletion.completecourse": "block_selfcompletion", - "addon.coursecompletion.completed": "completion", - "addon.coursecompletion.completiondate": "report_completion", - "addon.coursecompletion.completionmenuitem": "completion", - "addon.coursecompletion.couldnotloadreport": "local_moodlemobileapp", - "addon.coursecompletion.coursecompletion": "completion", - "addon.coursecompletion.criteria": "completion", - "addon.coursecompletion.criteriagroup": "completion", - "addon.coursecompletion.criteriarequiredall": "completion", - "addon.coursecompletion.criteriarequiredany": "completion", - "addon.coursecompletion.inprogress": "completion", - "addon.coursecompletion.manualselfcompletion": "completion", - "addon.coursecompletion.nottracked": "completion", - "addon.coursecompletion.notyetstarted": "completion", - "addon.coursecompletion.pending": "completion", - "addon.coursecompletion.required": "moodle", - "addon.coursecompletion.requiredcriteria": "completion", - "addon.coursecompletion.requirement": "block_completionstatus", - "addon.coursecompletion.status": "moodle", - "addon.coursecompletion.viewcoursereport": "completion", - "addon.files.couldnotloadfiles": "local_moodlemobileapp", - "addon.files.emptyfilelist": "local_moodlemobileapp", - "addon.files.erroruploadnotworking": "local_moodlemobileapp", - "addon.files.files": "moodle", - "addon.files.privatefiles": "moodle", - "addon.files.sitefiles": "moodle", - "addon.messageoutput_airnotifier.processorsettingsdesc": "local_moodlemobileapp", - "addon.messages.acceptandaddcontact": "message", - "addon.messages.addcontact": "message", - "addon.messages.addcontactconfirm": "message", - "addon.messages.addtofavourites": "message", - "addon.messages.addtoyourcontacts": "message", - "addon.messages.blocknoncontacts": "message", - "addon.messages.blockuser": "message", - "addon.messages.blockuserconfirm": "message", - "addon.messages.contactableprivacy": "message", - "addon.messages.contactableprivacy_coursemember": "message", - "addon.messages.contactableprivacy_onlycontacts": "message", - "addon.messages.contactableprivacy_site": "message", - "addon.messages.contactblocked": "message", - "addon.messages.contactlistempty": "local_moodlemobileapp", - "addon.messages.contactname": "local_moodlemobileapp", - "addon.messages.contactrequestsent": "message", - "addon.messages.contacts": "message", - "addon.messages.conversationactions": "message", - "addon.messages.decline": "message", - "addon.messages.deleteallconfirm": "message", - "addon.messages.deleteallselfconfirm": "message", - "addon.messages.deleteconversation": "message", - "addon.messages.deleteforeveryone": "message", - "addon.messages.deletemessage": "local_moodlemobileapp", - "addon.messages.deletemessageconfirmation": "local_moodlemobileapp", - "addon.messages.errordeletemessage": "local_moodlemobileapp", - "addon.messages.errorwhileretrievingcontacts": "local_moodlemobileapp", - "addon.messages.errorwhileretrievingdiscussions": "local_moodlemobileapp", - "addon.messages.errorwhileretrievingmessages": "local_moodlemobileapp", - "addon.messages.errorwhileretrievingusers": "local_moodlemobileapp", - "addon.messages.groupconversations": "message", - "addon.messages.groupinfo": "message", - "addon.messages.individualconversations": "message", - "addon.messages.info": "message", - "addon.messages.isnotinyourcontacts": "message", - "addon.messages.message": "message", - "addon.messages.messagenotsent": "local_moodlemobileapp", - "addon.messages.messagepreferences": "message", - "addon.messages.messages": "message", - "addon.messages.muteconversation": "message", - "addon.messages.mutedconversation": "message", - "addon.messages.newmessage": "message", - "addon.messages.newmessages": "local_moodlemobileapp", - "addon.messages.nocontactrequests": "message", - "addon.messages.nocontactsgetstarted": "message", - "addon.messages.nofavourites": "message", - "addon.messages.nogroupconversations": "message", - "addon.messages.noindividualconversations": "message", - "addon.messages.nomessagesfound": "message", - "addon.messages.noncontacts": "message", - "addon.messages.nousersfound": "local_moodlemobileapp", - "addon.messages.numparticipants": "message", - "addon.messages.removecontact": "message", - "addon.messages.removecontactconfirm": "message", - "addon.messages.removefromfavourites": "message", - "addon.messages.removefromyourcontacts": "message", - "addon.messages.requests": "moodle", - "addon.messages.requirecontacttomessage": "message", - "addon.messages.searchcombined": "message", - "addon.messages.selfconversation": "message", - "addon.messages.selfconversationdefaultmessage": "message", - "addon.messages.sendcontactrequest": "message", - "addon.messages.showdeletemessages": "local_moodlemobileapp", - "addon.messages.type_blocked": "local_moodlemobileapp", - "addon.messages.type_offline": "local_moodlemobileapp", - "addon.messages.type_online": "local_moodlemobileapp", - "addon.messages.type_search": "local_moodlemobileapp", - "addon.messages.type_strangers": "local_moodlemobileapp", - "addon.messages.unabletomessage": "message", - "addon.messages.unblockuser": "message", - "addon.messages.unblockuserconfirm": "message", - "addon.messages.unmuteconversation": "message", - "addon.messages.useentertosend": "message", - "addon.messages.useentertosenddescdesktop": "local_moodlemobileapp", - "addon.messages.useentertosenddescmac": "local_moodlemobileapp", - "addon.messages.userwouldliketocontactyou": "message", - "addon.messages.warningconversationmessagenotsent": "local_moodlemobileapp", - "addon.messages.warningmessagenotsent": "local_moodlemobileapp", - "addon.messages.wouldliketocontactyou": "message", - "addon.messages.you": "message", - "addon.messages.youhaveblockeduser": "message", - "addon.messages.yourcontactrequestpending": "message", - "addon.mod_assign.acceptsubmissionstatement": "local_moodlemobileapp", - "addon.mod_assign.addattempt": "assign", - "addon.mod_assign.addnewattempt": "assign", - "addon.mod_assign.addnewattemptfromprevious": "assign", - "addon.mod_assign.addsubmission": "assign", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "assign", - "addon.mod_assign.allowsubmissionsfromdate": "assign", - "addon.mod_assign.allowsubmissionsfromdatesummary": "assign", - "addon.mod_assign.applytoteam": "assign", - "addon.mod_assign.assignmentisdue": "assign", - "addon.mod_assign.attemptnumber": "assign", - "addon.mod_assign.attemptreopenmethod": "assign", - "addon.mod_assign.attemptreopenmethod_manual": "assign", - "addon.mod_assign.attemptreopenmethod_untilpass": "assign", - "addon.mod_assign.attemptsettings": "assign", - "addon.mod_assign.cannoteditduetostatementsubmission": "local_moodlemobileapp", - "addon.mod_assign.cannotgradefromapp": "local_moodlemobileapp", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "local_moodlemobileapp", - "addon.mod_assign.confirmsubmission": "assign", - "addon.mod_assign.currentattempt": "assign", - "addon.mod_assign.currentattemptof": "assign", - "addon.mod_assign.currentgrade": "assign", - "addon.mod_assign.cutoffdate": "assign", - "addon.mod_assign.defaultteam": "assign", - "addon.mod_assign.duedate": "assign", - "addon.mod_assign.duedateno": "assign", - "addon.mod_assign.duedatereached": "assign", - "addon.mod_assign.editingstatus": "assign", - "addon.mod_assign.editsubmission": "assign", - "addon.mod_assign.erroreditpluginsnotsupported": "local_moodlemobileapp", - "addon.mod_assign.errorshowinginformation": "local_moodlemobileapp", - "addon.mod_assign.extensionduedate": "assign", - "addon.mod_assign.feedbacknotsupported": "local_moodlemobileapp", - "addon.mod_assign.grade": "grades", - "addon.mod_assign.graded": "assign", - "addon.mod_assign.gradedby": "assign", - "addon.mod_assign.gradedfollowupsubmit": "assign", - "addon.mod_assign.gradedon": "assign", - "addon.mod_assign.gradelocked": "assign", - "addon.mod_assign.gradenotsynced": "local_moodlemobileapp", - "addon.mod_assign.gradeoutof": "assign", - "addon.mod_assign.gradingstatus": "assign", - "addon.mod_assign.groupsubmissionsettings": "assign", - "addon.mod_assign.hiddenuser": "assign", - "addon.mod_assign.latesubmissions": "assign", - "addon.mod_assign.latesubmissionsaccepted": "assign", - "addon.mod_assign.markingworkflowstate": "assign", - "addon.mod_assign.markingworkflowstateinmarking": "assign", - "addon.mod_assign.markingworkflowstateinreview": "assign", - "addon.mod_assign.markingworkflowstatenotmarked": "assign", - "addon.mod_assign.markingworkflowstatereadyforrelease": "assign", - "addon.mod_assign.markingworkflowstatereadyforreview": "assign", - "addon.mod_assign.markingworkflowstatereleased": "assign", - "addon.mod_assign.modulenameplural": "assign", - "addon.mod_assign.multipleteams": "assign", - "addon.mod_assign.multipleteams_desc": "assign", - "addon.mod_assign.noattempt": "assign", - "addon.mod_assign.nomoresubmissionsaccepted": "assign", - "addon.mod_assign.noonlinesubmissions": "assign", - "addon.mod_assign.nosubmission": "assign", - "addon.mod_assign.notallparticipantsareshown": "local_moodlemobileapp", - "addon.mod_assign.noteam": "assign", - "addon.mod_assign.noteam_desc": "assign", - "addon.mod_assign.notgraded": "assign", - "addon.mod_assign.numberofdraftsubmissions": "assign", - "addon.mod_assign.numberofparticipants": "assign", - "addon.mod_assign.numberofsubmissionsneedgrading": "assign", - "addon.mod_assign.numberofsubmittedassignments": "assign", - "addon.mod_assign.numberofteams": "assign", - "addon.mod_assign.numwords": "moodle", - "addon.mod_assign.outof": "assign", - "addon.mod_assign.overdue": "assign", - "addon.mod_assign.submission": "assign", - "addon.mod_assign.submissioneditable": "assign", - "addon.mod_assign.submissionnoteditable": "assign", - "addon.mod_assign.submissionnotsupported": "local_moodlemobileapp", - "addon.mod_assign.submissionslocked": "assign", - "addon.mod_assign.submissionstatus": "assign", - "addon.mod_assign.submissionstatus_": "assign", - "addon.mod_assign.submissionstatus_draft": "assign", - "addon.mod_assign.submissionstatus_marked": "assign", - "addon.mod_assign.submissionstatus_new": "assign", - "addon.mod_assign.submissionstatus_reopened": "assign", - "addon.mod_assign.submissionstatus_submitted": "assign", - "addon.mod_assign.submissionstatusheading": "assign", - "addon.mod_assign.submissionteam": "assign", - "addon.mod_assign.submitassignment": "assign", - "addon.mod_assign.submitassignment_help": "assign", - "addon.mod_assign.submittedearly": "assign", - "addon.mod_assign.submittedlate": "assign", - "addon.mod_assign.syncblockedusercomponent": "local_moodlemobileapp", - "addon.mod_assign.timemodified": "assign", - "addon.mod_assign.timeremaining": "assign", - "addon.mod_assign.ungroupedusers": "assign", - "addon.mod_assign.ungroupedusersoptional": "assign", - "addon.mod_assign.unlimitedattempts": "assign", - "addon.mod_assign.userswhoneedtosubmit": "assign", - "addon.mod_assign.userwithid": "local_moodlemobileapp", - "addon.mod_assign.viewsubmission": "assign", - "addon.mod_assign.warningsubmissiongrademodified": "local_moodlemobileapp", - "addon.mod_assign.warningsubmissionmodified": "local_moodlemobileapp", - "addon.mod_assign.wordlimit": "assignsubmission_onlinetext", - "addon.mod_assign_feedback_comments.pluginname": "assignfeedback_comments", - "addon.mod_assign_feedback_editpdf.pluginname": "assignfeedback_editpdf", - "addon.mod_assign_feedback_file.pluginname": "assignfeedback_file", - "addon.mod_assign_submission_comments.pluginname": "assignsubmission_comments", - "addon.mod_assign_submission_file.pluginname": "assignsubmission_file", - "addon.mod_assign_submission_onlinetext.pluginname": "assignsubmission_onlinetext", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "assignsubmission_onlinetext", - "addon.mod_book.errorchapter": "book", - "addon.mod_book.modulenameplural": "book", - "addon.mod_book.navnexttitle": "book", - "addon.mod_book.navprevtitle": "book", - "addon.mod_book.tagarea_book_chapters": "book", - "addon.mod_book.toc": "book", - "addon.mod_chat.beep": "chat", - "addon.mod_chat.chatreport": "chat", - "addon.mod_chat.currentusers": "chat", - "addon.mod_chat.enterchat": "chat", - "addon.mod_chat.entermessage": "chat", - "addon.mod_chat.errorwhileconnecting": "local_moodlemobileapp", - "addon.mod_chat.errorwhilegettingchatdata": "local_moodlemobileapp", - "addon.mod_chat.errorwhilegettingchatusers": "local_moodlemobileapp", - "addon.mod_chat.errorwhileretrievingmessages": "local_moodlemobileapp", - "addon.mod_chat.errorwhilesendingmessage": "local_moodlemobileapp", - "addon.mod_chat.messagebeepseveryone": "chat", - "addon.mod_chat.messagebeepsyou": "chat", - "addon.mod_chat.messageenter": "chat", - "addon.mod_chat.messageexit": "chat", - "addon.mod_chat.messages": "chat", - "addon.mod_chat.messageyoubeep": "chat", - "addon.mod_chat.modulenameplural": "chat", - "addon.mod_chat.mustbeonlinetosendmessages": "local_moodlemobileapp", - "addon.mod_chat.nomessages": "chat", - "addon.mod_chat.nosessionsfound": "local_moodlemobileapp", - "addon.mod_chat.saidto": "chat", - "addon.mod_chat.send": "chat", - "addon.mod_chat.sessionstart": "chat", - "addon.mod_chat.showincompletesessions": "local_moodlemobileapp", - "addon.mod_chat.talk": "chat", - "addon.mod_chat.viewreport": "chat", - "addon.mod_choice.cannotsubmit": "choice", - "addon.mod_choice.choiceoptions": "choice", - "addon.mod_choice.errorgetchoice": "local_moodlemobileapp", - "addon.mod_choice.expired": "choice", - "addon.mod_choice.full": "choice", - "addon.mod_choice.limita": "choice", - "addon.mod_choice.modulenameplural": "choice", - "addon.mod_choice.noresultsviewable": "choice", - "addon.mod_choice.notopenyet": "choice", - "addon.mod_choice.numberofuser": "choice", - "addon.mod_choice.numberofuserinpercentage": "choice", - "addon.mod_choice.previewonly": "choice", - "addon.mod_choice.publishinfoanonafter": "choice", - "addon.mod_choice.publishinfoanonclose": "choice", - "addon.mod_choice.publishinfofullafter": "choice", - "addon.mod_choice.publishinfofullclose": "choice", - "addon.mod_choice.publishinfonever": "choice", - "addon.mod_choice.removemychoice": "choice", - "addon.mod_choice.responses": "choice", - "addon.mod_choice.responsesa": "choice", - "addon.mod_choice.responsesresultgraphdescription": "local_moodlemobileapp", - "addon.mod_choice.responsesresultgraphheader": "choice", - "addon.mod_choice.resultsnotsynced": "local_moodlemobileapp", - "addon.mod_choice.savemychoice": "choice", - "addon.mod_choice.userchoosethisoption": "choice", - "addon.mod_choice.yourselection": "choice", - "addon.mod_data.addentries": "data", - "addon.mod_data.advancedsearch": "data", - "addon.mod_data.alttext": "data", - "addon.mod_data.approve": "data", - "addon.mod_data.approved": "data", - "addon.mod_data.ascending": "data", - "addon.mod_data.authorfirstname": "data", - "addon.mod_data.authorlastname": "data", - "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", - "addon.mod_data.errorapproving": "local_moodlemobileapp", - "addon.mod_data.errordeleting": "local_moodlemobileapp", - "addon.mod_data.errormustsupplyvalue": "data", - "addon.mod_data.expired": "data", - "addon.mod_data.fields": "data", - "addon.mod_data.foundrecords": "data", - "addon.mod_data.gettinglocation": "local_moodlemobileapp", - "addon.mod_data.latlongboth": "data", - "addon.mod_data.locationpermissiondenied": "local_moodlemobileapp", - "addon.mod_data.menuchoose": "data", - "addon.mod_data.modulenameplural": "data", - "addon.mod_data.more": "data", - "addon.mod_data.mylocation": "local_moodlemobileapp", - "addon.mod_data.nomatch": "data", - "addon.mod_data.norecords": "data", - "addon.mod_data.notapproved": "data", - "addon.mod_data.notopenyet": "data", - "addon.mod_data.numrecords": "data", - "addon.mod_data.other": "data", - "addon.mod_data.recordapproved": "data", - "addon.mod_data.recorddeleted": "data", - "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", - "addon.mod_feedback.analysis": "feedback", - "addon.mod_feedback.anonymous": "feedback", - "addon.mod_feedback.anonymous_entries": "feedback", - "addon.mod_feedback.average": "feedback", - "addon.mod_feedback.captchaofflinewarning": "local_moodlemobileapp", - "addon.mod_feedback.complete_the_form": "feedback", - "addon.mod_feedback.completed_feedbacks": "feedback", - "addon.mod_feedback.continue_the_form": "feedback", - "addon.mod_feedback.feedback_is_not_open": "feedback", - "addon.mod_feedback.feedback_submitted_offline": "local_moodlemobileapp", - "addon.mod_feedback.feedbackclose": "feedback", - "addon.mod_feedback.feedbackopen": "feedback", - "addon.mod_feedback.mapcourses": "feedback", - "addon.mod_feedback.maximal": "feedback", - "addon.mod_feedback.minimal": "feedback", - "addon.mod_feedback.mode": "feedback", - "addon.mod_feedback.modulenameplural": "feedback", - "addon.mod_feedback.next_page": "feedback", - "addon.mod_feedback.non_anonymous": "feedback", - "addon.mod_feedback.non_anonymous_entries": "feedback", - "addon.mod_feedback.non_respondents_students": "feedback", - "addon.mod_feedback.not_selected": "feedback", - "addon.mod_feedback.not_started": "feedback", - "addon.mod_feedback.numberoutofrange": "feedback", - "addon.mod_feedback.overview": "feedback", - "addon.mod_feedback.page_after_submit": "feedback", - "addon.mod_feedback.preview": "moodle", - "addon.mod_feedback.previous_page": "feedback", - "addon.mod_feedback.questions": "feedback", - "addon.mod_feedback.response_nr": "feedback", - "addon.mod_feedback.responses": "feedback", - "addon.mod_feedback.save_entries": "feedback", - "addon.mod_feedback.show_entries": "feedback", - "addon.mod_feedback.show_nonrespondents": "feedback", - "addon.mod_feedback.started": "feedback", - "addon.mod_feedback.this_feedback_is_already_submitted": "feedback", - "addon.mod_folder.emptyfilelist": "local_moodlemobileapp", - "addon.mod_folder.modulenameplural": "folder", - "addon.mod_forum.addanewdiscussion": "forum", - "addon.mod_forum.addanewquestion": "forum", - "addon.mod_forum.addanewtopic": "forum", - "addon.mod_forum.addtofavourites": "forum", - "addon.mod_forum.advanced": "moodle", - "addon.mod_forum.cannotadddiscussion": "forum", - "addon.mod_forum.cannotadddiscussionall": "forum", - "addon.mod_forum.cannotcreatediscussion": "forum", - "addon.mod_forum.couldnotadd": "forum", - "addon.mod_forum.couldnotupdate": "forum", - "addon.mod_forum.cutoffdatereached": "forum", - "addon.mod_forum.delete": "forum", - "addon.mod_forum.deletedpost": "forum", - "addon.mod_forum.deletesure": "forum", - "addon.mod_forum.discussion": "forum", - "addon.mod_forum.discussionlistsortbycreatedasc": "forum", - "addon.mod_forum.discussionlistsortbycreateddesc": "forum", - "addon.mod_forum.discussionlistsortbylastpostasc": "forum", - "addon.mod_forum.discussionlistsortbylastpostdesc": "forum", - "addon.mod_forum.discussionlistsortbyrepliesasc": "forum", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "forum", - "addon.mod_forum.discussionlocked": "forum", - "addon.mod_forum.discussionpinned": "forum", - "addon.mod_forum.discussionsubscription": "forum", - "addon.mod_forum.edit": "forum", - "addon.mod_forum.erroremptymessage": "forum", - "addon.mod_forum.erroremptysubject": "forum", - "addon.mod_forum.errorgetforum": "local_moodlemobileapp", - "addon.mod_forum.errorgetgroups": "local_moodlemobileapp", - "addon.mod_forum.errorposttoallgroups": "local_moodlemobileapp", - "addon.mod_forum.favouriteupdated": "forum", - "addon.mod_forum.forumnodiscussionsyet": "local_moodlemobileapp", - "addon.mod_forum.group": "local_moodlemobileapp", - "addon.mod_forum.lastpost": "forum", - "addon.mod_forum.lockdiscussion": "forum", - "addon.mod_forum.lockupdated": "forum", - "addon.mod_forum.message": "forum", - "addon.mod_forum.modeflatnewestfirst": "forum", - "addon.mod_forum.modeflatoldestfirst": "forum", - "addon.mod_forum.modenested": "forum", - "addon.mod_forum.modulenameplural": "forum", - "addon.mod_forum.numdiscussions": "local_moodlemobileapp", - "addon.mod_forum.numreplies": "local_moodlemobileapp", - "addon.mod_forum.pindiscussion": "forum", - "addon.mod_forum.pinupdated": "forum", - "addon.mod_forum.postisprivatereply": "forum", - "addon.mod_forum.posttoforum": "forum", - "addon.mod_forum.posttomygroups": "forum", - "addon.mod_forum.privatereply": "forum", - "addon.mod_forum.re": "forum", - "addon.mod_forum.refreshdiscussions": "local_moodlemobileapp", - "addon.mod_forum.refreshposts": "local_moodlemobileapp", - "addon.mod_forum.removefromfavourites": "forum", - "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", - "addon.mod_forum.unpindiscussion": "forum", - "addon.mod_forum.unread": "forum", - "addon.mod_forum.unreadpostsnumber": "forum", - "addon.mod_forum.yourreply": "forum", - "addon.mod_glossary.addentry": "glossary", - "addon.mod_glossary.aliases": "glossary", - "addon.mod_glossary.attachment": "glossary", - "addon.mod_glossary.browsemode": "local_moodlemobileapp", - "addon.mod_glossary.byalphabet": "local_moodlemobileapp", - "addon.mod_glossary.byauthor": "local_moodlemobileapp", - "addon.mod_glossary.bycategory": "local_moodlemobileapp", - "addon.mod_glossary.bynewestfirst": "local_moodlemobileapp", - "addon.mod_glossary.byrecentlyupdated": "local_moodlemobileapp", - "addon.mod_glossary.bysearch": "local_moodlemobileapp", - "addon.mod_glossary.cannoteditentry": "local_moodlemobileapp", - "addon.mod_glossary.casesensitive": "glossary", - "addon.mod_glossary.categories": "glossary", - "addon.mod_glossary.concept": "glossary", - "addon.mod_glossary.definition": "glossary", - "addon.mod_glossary.entriestobesynced": "local_moodlemobileapp", - "addon.mod_glossary.entrypendingapproval": "local_moodlemobileapp", - "addon.mod_glossary.entryusedynalink": "glossary", - "addon.mod_glossary.errconceptalreadyexists": "glossary", - "addon.mod_glossary.errorloadingentries": "local_moodlemobileapp", - "addon.mod_glossary.errorloadingentry": "local_moodlemobileapp", - "addon.mod_glossary.errorloadingglossary": "local_moodlemobileapp", - "addon.mod_glossary.fillfields": "glossary", - "addon.mod_glossary.fullmatch": "glossary", - "addon.mod_glossary.linking": "glossary", - "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_h5pactivity.all_attempts": "h5pactivity", - "addon.mod_h5pactivity.answer_checked": "h5pactivity", - "addon.mod_h5pactivity.answer_correct": "h5pactivity", - "addon.mod_h5pactivity.answer_fail": "h5pactivity", - "addon.mod_h5pactivity.answer_incorrect": "h5pactivity", - "addon.mod_h5pactivity.answer_pass": "h5pactivity", - "addon.mod_h5pactivity.attempt": "h5pactivity", - "addon.mod_h5pactivity.attempt_completion_no": "h5pactivity", - "addon.mod_h5pactivity.attempt_completion_yes": "h5pactivity", - "addon.mod_h5pactivity.attempt_success_fail": "h5pactivity", - "addon.mod_h5pactivity.attempt_success_pass": "h5pactivity", - "addon.mod_h5pactivity.attempt_success_unknown": "h5pactivity", - "addon.mod_h5pactivity.attempts_none": "h5pactivity", - "addon.mod_h5pactivity.completion": "h5pactivity", - "addon.mod_h5pactivity.downloadh5pfile": "local_moodlemobileapp", - "addon.mod_h5pactivity.duration": "h5pactivity", - "addon.mod_h5pactivity.errorgetactivity": "local_moodlemobileapp", - "addon.mod_h5pactivity.filestatenotdownloaded": "local_moodlemobileapp", - "addon.mod_h5pactivity.filestateoutdated": "local_moodlemobileapp", - "addon.mod_h5pactivity.maxscore": "h5pactivity", - "addon.mod_h5pactivity.modulenameplural": "h5pactivity", - "addon.mod_h5pactivity.myattempts": "h5pactivity", - "addon.mod_h5pactivity.no_compatible_track": "h5pactivity", - "addon.mod_h5pactivity.offlinedisabledwarning": "local_moodlemobileapp", - "addon.mod_h5pactivity.outcome": "h5pactivity", - "addon.mod_h5pactivity.previewmode": "h5pactivity", - "addon.mod_h5pactivity.result_fill-in": "h5pactivity", - "addon.mod_h5pactivity.result_other": "h5pactivity", - "addon.mod_h5pactivity.review_my_attempts": "h5pactivity", - "addon.mod_h5pactivity.score": "h5pactivity", - "addon.mod_h5pactivity.score_out_of": "h5pactivity", - "addon.mod_h5pactivity.startdate": "h5pactivity", - "addon.mod_h5pactivity.totalscore": "h5pactivity", - "addon.mod_h5pactivity.viewattempt": "local_moodlemobileapp", - "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", - "addon.mod_lesson.attemptsremaining": "lesson", - "addon.mod_lesson.averagescore": "lesson", - "addon.mod_lesson.averagetime": "lesson", - "addon.mod_lesson.branchtable": "lesson", - "addon.mod_lesson.cannotfindattempt": "lesson", - "addon.mod_lesson.cannotfinduser": "lesson", - "addon.mod_lesson.clusterjump": "lesson", - "addon.mod_lesson.completed": "lesson", - "addon.mod_lesson.congratulations": "lesson", - "addon.mod_lesson.continue": "lesson", - "addon.mod_lesson.continuetonextpage": "lesson", - "addon.mod_lesson.defaultessayresponse": "lesson", - "addon.mod_lesson.detailedstats": "lesson", - "addon.mod_lesson.didnotanswerquestion": "lesson", - "addon.mod_lesson.displayofgrade": "lesson", - "addon.mod_lesson.displayscorewithessays": "lesson", - "addon.mod_lesson.displayscorewithoutessays": "lesson", - "addon.mod_lesson.emptypassword": "lesson", - "addon.mod_lesson.enterpassword": "lesson", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "lesson", - "addon.mod_lesson.errorprefetchrandombranch": "local_moodlemobileapp", - "addon.mod_lesson.errorreviewretakenotlast": "local_moodlemobileapp", - "addon.mod_lesson.finish": "lesson", - "addon.mod_lesson.finishretakeoffline": "local_moodlemobileapp", - "addon.mod_lesson.firstwrong": "lesson", - "addon.mod_lesson.gotoendoflesson": "lesson", - "addon.mod_lesson.grade": "lesson", - "addon.mod_lesson.highscore": "lesson", - "addon.mod_lesson.hightime": "lesson", - "addon.mod_lesson.leftduringtimed": "lesson", - "addon.mod_lesson.leftduringtimednoretake": "lesson", - "addon.mod_lesson.lessonmenu": "lesson", - "addon.mod_lesson.lessonstats": "lesson", - "addon.mod_lesson.linkedmedia": "lesson", - "addon.mod_lesson.loginfail": "lesson", - "addon.mod_lesson.lowscore": "lesson", - "addon.mod_lesson.lowtime": "lesson", - "addon.mod_lesson.maximumnumberofattemptsreached": "lesson", - "addon.mod_lesson.modattemptsnoteacher": "lesson", - "addon.mod_lesson.modulenameplural": "lesson", - "addon.mod_lesson.noanswer": "lesson", - "addon.mod_lesson.nolessonattempts": "lesson", - "addon.mod_lesson.nolessonattemptsgroup": "lesson", - "addon.mod_lesson.notcompleted": "lesson", - "addon.mod_lesson.numberofcorrectanswers": "lesson", - "addon.mod_lesson.numberofpagesviewed": "lesson", - "addon.mod_lesson.numberofpagesviewednotice": "lesson", - "addon.mod_lesson.ongoingcustom": "lesson", - "addon.mod_lesson.ongoingnormal": "lesson", - "addon.mod_lesson.or": "lesson", - "addon.mod_lesson.overview": "lesson", - "addon.mod_lesson.preview": "lesson", - "addon.mod_lesson.progressbarteacherwarning2": "lesson", - "addon.mod_lesson.progresscompleted": "lesson", - "addon.mod_lesson.question": "lesson", - "addon.mod_lesson.rawgrade": "lesson", - "addon.mod_lesson.reports": "lesson", - "addon.mod_lesson.response": "lesson", - "addon.mod_lesson.retakefinishedinsync": "local_moodlemobileapp", - "addon.mod_lesson.retakelabelfull": "local_moodlemobileapp", - "addon.mod_lesson.retakelabelshort": "local_moodlemobileapp", - "addon.mod_lesson.review": "lesson", - "addon.mod_lesson.reviewlesson": "lesson", - "addon.mod_lesson.reviewquestionback": "lesson", - "addon.mod_lesson.reviewquestioncontinue": "lesson", - "addon.mod_lesson.secondpluswrong": "lesson", - "addon.mod_lesson.submit": "lesson", - "addon.mod_lesson.teacherjumpwarning": "lesson", - "addon.mod_lesson.teacherongoingwarning": "lesson", - "addon.mod_lesson.teachertimerwarning": "lesson", - "addon.mod_lesson.thatsthecorrectanswer": "lesson", - "addon.mod_lesson.thatsthewronganswer": "lesson", - "addon.mod_lesson.timeremaining": "lesson", - "addon.mod_lesson.timetaken": "lesson", - "addon.mod_lesson.unseenpageinbranch": "lesson", - "addon.mod_lesson.warningretakefinished": "local_moodlemobileapp", - "addon.mod_lesson.welldone": "lesson", - "addon.mod_lesson.youhaveseen": "lesson", - "addon.mod_lesson.youranswer": "lesson", - "addon.mod_lesson.yourcurrentgradeisoutof": "lesson", - "addon.mod_lesson.youshouldview": "lesson", - "addon.mod_lti.errorgetlti": "local_moodlemobileapp", - "addon.mod_lti.errorinvalidlaunchurl": "local_moodlemobileapp", - "addon.mod_lti.launchactivity": "local_moodlemobileapp", - "addon.mod_lti.modulenameplural": "lti", - "addon.mod_page.errorwhileloadingthepage": "local_moodlemobileapp", - "addon.mod_page.modulenameplural": "page", - "addon.mod_quiz.answercolon": "qtype_numerical", - "addon.mod_quiz.attemptfirst": "quiz", - "addon.mod_quiz.attemptlast": "quiz", - "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", - "addon.mod_quiz.completedon": "quiz", - "addon.mod_quiz.confirmclose": "quiz", - "addon.mod_quiz.confirmcontinueoffline": "local_moodlemobileapp", - "addon.mod_quiz.confirmleavequizonerror": "local_moodlemobileapp", - "addon.mod_quiz.confirmstart": "quizaccess_timelimit", - "addon.mod_quiz.confirmstartheader": "quizaccess_timelimit", - "addon.mod_quiz.connectionerror": "quiz", - "addon.mod_quiz.continueattemptquiz": "quiz", - "addon.mod_quiz.continuepreview": "quiz", - "addon.mod_quiz.errorbehaviournotsupported": "local_moodlemobileapp", - "addon.mod_quiz.errordownloading": "local_moodlemobileapp", - "addon.mod_quiz.errorgetattempt": "local_moodlemobileapp", - "addon.mod_quiz.errorgetquestions": "local_moodlemobileapp", - "addon.mod_quiz.errorgetquiz": "local_moodlemobileapp", - "addon.mod_quiz.errorparsequestions": "local_moodlemobileapp", - "addon.mod_quiz.errorquestionsnotsupported": "local_moodlemobileapp", - "addon.mod_quiz.errorrulesnotsupported": "local_moodlemobileapp", - "addon.mod_quiz.errorsaveattempt": "local_moodlemobileapp", - "addon.mod_quiz.feedback": "quiz", - "addon.mod_quiz.finishattemptdots": "quiz", - "addon.mod_quiz.finishnotsynced": "local_moodlemobileapp", - "addon.mod_quiz.grade": "quiz", - "addon.mod_quiz.gradeaverage": "quiz", - "addon.mod_quiz.gradehighest": "quiz", - "addon.mod_quiz.grademethod": "quiz", - "addon.mod_quiz.gradesofar": "quiz", - "addon.mod_quiz.marks": "quiz", - "addon.mod_quiz.modulenameplural": "quiz", - "addon.mod_quiz.mustbesubmittedby": "quiz", - "addon.mod_quiz.noquestions": "quiz", - "addon.mod_quiz.noreviewattempt": "quiz", - "addon.mod_quiz.notyetgraded": "quiz", - "addon.mod_quiz.opentoc": "local_moodlemobileapp", - "addon.mod_quiz.outof": "quiz", - "addon.mod_quiz.outofpercent": "quiz", - "addon.mod_quiz.outofshort": "quiz", - "addon.mod_quiz.overallfeedback": "quiz", - "addon.mod_quiz.overdue": "quiz", - "addon.mod_quiz.overduemustbesubmittedby": "quiz", - "addon.mod_quiz.preview": "quiz", - "addon.mod_quiz.previewquiznow": "quiz", - "addon.mod_quiz.question": "quiz", - "addon.mod_quiz.quiznavigation": "quiz", - "addon.mod_quiz.quizpassword": "quizaccess_password", - "addon.mod_quiz.reattemptquiz": "quiz", - "addon.mod_quiz.requirepasswordmessage": "quizaccess_password", - "addon.mod_quiz.returnattempt": "quiz", - "addon.mod_quiz.review": "quiz", - "addon.mod_quiz.reviewofattempt": "quiz", - "addon.mod_quiz.reviewofpreview": "quiz", - "addon.mod_quiz.showall": "quiz", - "addon.mod_quiz.showeachpage": "quiz", - "addon.mod_quiz.startattempt": "quiz", - "addon.mod_quiz.startedon": "quiz", - "addon.mod_quiz.stateabandoned": "quiz", - "addon.mod_quiz.statefinished": "quiz", - "addon.mod_quiz.statefinisheddetails": "quiz", - "addon.mod_quiz.stateinprogress": "quiz", - "addon.mod_quiz.stateoverdue": "quiz", - "addon.mod_quiz.stateoverduedetails": "quiz", - "addon.mod_quiz.status": "quiz", - "addon.mod_quiz.submitallandfinish": "quiz", - "addon.mod_quiz.summaryofattempt": "quiz", - "addon.mod_quiz.summaryofattempts": "quiz", - "addon.mod_quiz.timeleft": "quiz", - "addon.mod_quiz.timetaken": "quiz", - "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", - "addon.mod_resource.modulenameplural": "resource", - "addon.mod_resource.openthefile": "local_moodlemobileapp", - "addon.mod_resource.uploadeddate": "resource", - "addon.mod_scorm.asset": "scorm", - "addon.mod_scorm.assetlaunched": "scorm", - "addon.mod_scorm.attempts": "scorm", - "addon.mod_scorm.averageattempt": "scorm", - "addon.mod_scorm.browse": "scorm", - "addon.mod_scorm.browsed": "scorm", - "addon.mod_scorm.browsemode": "scorm", - "addon.mod_scorm.cannotcalculategrade": "local_moodlemobileapp", - "addon.mod_scorm.completed": "scorm", - "addon.mod_scorm.contents": "scorm", - "addon.mod_scorm.dataattemptshown": "local_moodlemobileapp", - "addon.mod_scorm.enter": "scorm", - "addon.mod_scorm.errorcreateofflineattempt": "local_moodlemobileapp", - "addon.mod_scorm.errordownloadscorm": "local_moodlemobileapp", - "addon.mod_scorm.errorgetscorm": "local_moodlemobileapp", - "addon.mod_scorm.errorinvalidversion": "local_moodlemobileapp", - "addon.mod_scorm.errornotdownloadable": "local_moodlemobileapp", - "addon.mod_scorm.errornovalidsco": "local_moodlemobileapp", - "addon.mod_scorm.errorpackagefile": "local_moodlemobileapp", - "addon.mod_scorm.errorsyncscorm": "local_moodlemobileapp", - "addon.mod_scorm.exceededmaxattempts": "scorm", - "addon.mod_scorm.failed": "scorm", - "addon.mod_scorm.firstattempt": "scorm", - "addon.mod_scorm.gradeaverage": "scorm", - "addon.mod_scorm.gradeforattempt": "scorm", - "addon.mod_scorm.gradehighest": "scorm", - "addon.mod_scorm.grademethod": "scorm", - "addon.mod_scorm.gradereported": "scorm", - "addon.mod_scorm.gradescoes": "scorm", - "addon.mod_scorm.gradesum": "scorm", - "addon.mod_scorm.highestattempt": "scorm", - "addon.mod_scorm.incomplete": "scorm", - "addon.mod_scorm.lastattempt": "scorm", - "addon.mod_scorm.modulenameplural": "scorm", - "addon.mod_scorm.newattempt": "scorm", - "addon.mod_scorm.noattemptsallowed": "scorm", - "addon.mod_scorm.noattemptsmade": "scorm", - "addon.mod_scorm.notattempted": "scorm", - "addon.mod_scorm.offlineattemptnote": "local_moodlemobileapp", - "addon.mod_scorm.offlineattemptovermax": "local_moodlemobileapp", - "addon.mod_scorm.organizations": "scorm", - "addon.mod_scorm.passed": "scorm", - "addon.mod_scorm.reviewmode": "scorm", - "addon.mod_scorm.score": "scorm", - "addon.mod_scorm.scormstatusnotdownloaded": "local_moodlemobileapp", - "addon.mod_scorm.scormstatusoutdated": "local_moodlemobileapp", - "addon.mod_scorm.suspended": "scorm", - "addon.mod_scorm.toc": "scorm", - "addon.mod_scorm.warningofflinedatadeleted": "local_moodlemobileapp", - "addon.mod_scorm.warningsynconlineincomplete": "local_moodlemobileapp", - "addon.mod_survey.cannotsubmitsurvey": "local_moodlemobileapp", - "addon.mod_survey.errorgetsurvey": "local_moodlemobileapp", - "addon.mod_survey.ifoundthat": "survey", - "addon.mod_survey.ipreferthat": "survey", - "addon.mod_survey.modulenameplural": "survey", - "addon.mod_survey.responses": "survey", - "addon.mod_survey.results": "local_moodlemobileapp", - "addon.mod_survey.surveycompletednograph": "survey", - "addon.mod_url.accessurl": "local_moodlemobileapp", - "addon.mod_url.modulenameplural": "url", - "addon.mod_url.pointingtourl": "local_moodlemobileapp", - "addon.mod_wiki.cannoteditpage": "wiki", - "addon.mod_wiki.createpage": "wiki", - "addon.mod_wiki.editingpage": "wiki", - "addon.mod_wiki.errorloadingpage": "local_moodlemobileapp", - "addon.mod_wiki.errornowikiavailable": "local_moodlemobileapp", - "addon.mod_wiki.gowikihome": "local_moodlemobileapp", - "addon.mod_wiki.map": "wiki", - "addon.mod_wiki.modulenameplural": "wiki", - "addon.mod_wiki.newpagehdr": "wiki", - "addon.mod_wiki.newpagetitle": "wiki", - "addon.mod_wiki.nocontent": "wiki", - "addon.mod_wiki.notingroup": "wiki", - "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", - "addon.mod_wiki.wrongversionlock": "wiki", - "addon.mod_workshop.alreadygraded": "workshop", - "addon.mod_workshop.areainstructauthors": "workshop", - "addon.mod_workshop.areainstructreviewers": "workshop", - "addon.mod_workshop.assess": "workshop", - "addon.mod_workshop.assessedsubmission": "workshop", - "addon.mod_workshop.assessmentform": "workshop", - "addon.mod_workshop.assessmentsettings": "workshop", - "addon.mod_workshop.assessmentstrategynotsupported": "local_moodlemobileapp", - "addon.mod_workshop.assessmentweight": "workshop", - "addon.mod_workshop.assignedassessments": "workshop", - "addon.mod_workshop.assignedassessmentsnone": "workshop", - "addon.mod_workshop.conclusion": "workshop", - "addon.mod_workshop.createsubmission": "workshop", - "addon.mod_workshop.deletesubmission": "workshop", - "addon.mod_workshop.editsubmission": "workshop", - "addon.mod_workshop.feedbackauthor": "workshop", - "addon.mod_workshop.feedbackby": "workshop", - "addon.mod_workshop.feedbackreviewer": "workshop", - "addon.mod_workshop.givengrades": "workshop", - "addon.mod_workshop.gradecalculated": "workshop", - "addon.mod_workshop.gradeinfo": "workshop", - "addon.mod_workshop.gradeover": "workshop", - "addon.mod_workshop.gradesreport": "workshop", - "addon.mod_workshop.gradinggrade": "workshop", - "addon.mod_workshop.gradinggradecalculated": "workshop", - "addon.mod_workshop.gradinggradeof": "workshop", - "addon.mod_workshop.gradinggradeover": "workshop", - "addon.mod_workshop.modulenameplural": "workshop", - "addon.mod_workshop.nogradeyet": "workshop", - "addon.mod_workshop.notassessed": "workshop", - "addon.mod_workshop.notoverridden": "workshop", - "addon.mod_workshop.noyoursubmission": "workshop", - "addon.mod_workshop.overallfeedback": "workshop", - "addon.mod_workshop.publishedsubmissions": "workshop", - "addon.mod_workshop.publishsubmission": "workshop", - "addon.mod_workshop.publishsubmission_help": "workshop", - "addon.mod_workshop.reassess": "workshop", - "addon.mod_workshop.receivedgrades": "workshop", - "addon.mod_workshop.submissionattachment": "workshop", - "addon.mod_workshop.submissioncontent": "workshop", - "addon.mod_workshop.submissiondeleteconfirm": "workshop", - "addon.mod_workshop.submissiongrade": "workshop", - "addon.mod_workshop.submissiongradeof": "workshop", - "addon.mod_workshop.submissionrequiredcontent": "workshop", - "addon.mod_workshop.submissionrequiredtitle": "local_moodlemobileapp", - "addon.mod_workshop.submissionsreport": "workshop", - "addon.mod_workshop.submissiontitle": "workshop", - "addon.mod_workshop.switchphase10": "workshop", - "addon.mod_workshop.switchphase20": "workshop", - "addon.mod_workshop.switchphase30": "workshop", - "addon.mod_workshop.switchphase40": "workshop", - "addon.mod_workshop.switchphase50": "workshop", - "addon.mod_workshop.userplan": "workshop", - "addon.mod_workshop.userplancurrentphase": "workshop", - "addon.mod_workshop.warningassessmentmodified": "local_moodlemobileapp", - "addon.mod_workshop.warningsubmissionmodified": "local_moodlemobileapp", - "addon.mod_workshop.weightinfo": "workshop", - "addon.mod_workshop.yourassessment": "workshop/assessmentbyyourself", - "addon.mod_workshop.yourassessmentfor": "workshop", - "addon.mod_workshop.yourgrades": "workshop", - "addon.mod_workshop.yoursubmission": "workshop", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "workshopform_accumulative", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "workshopform_accumulative", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "workshopform_accumulative", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "workshopform_accumulative", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "workshopform_comments", - "addon.mod_workshop_assessment_comments.dimensionnumber": "workshopform_comments", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "workshopform_numerrors", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "workshopform_accumulative", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "workshopform_numerrors", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "workshopform_rubric", - "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", - "addon.notes.personalnotes": "notes", - "addon.notes.publishstate": "notes", - "addon.notes.sitenotes": "notes", - "addon.notes.userwithid": "local_moodlemobileapp", - "addon.notes.warningnotenotsent": "local_moodlemobileapp", - "addon.notifications.errorgetnotifications": "local_moodlemobileapp", - "addon.notifications.markallread": "moodle", - "addon.notifications.notificationpreferences": "message", - "addon.notifications.notifications": "local_moodlemobileapp", - "addon.notifications.playsound": "local_moodlemobileapp", - "addon.notifications.therearentnotificationsyet": "local_moodlemobileapp", - "addon.storagemanager.deletecourse": "local_moodlemobileapp", - "addon.storagemanager.deletecourses": "local_moodlemobileapp", - "addon.storagemanager.deletedatafrom": "local_moodlemobileapp", - "addon.storagemanager.info": "local_moodlemobileapp", - "addon.storagemanager.managestorage": "local_moodlemobileapp", - "addon.storagemanager.storageused": "local_moodlemobileapp", - "assets.countries.AD": "countries", - "assets.countries.AE": "countries", - "assets.countries.AF": "countries", - "assets.countries.AG": "countries", - "assets.countries.AI": "countries", - "assets.countries.AL": "countries", - "assets.countries.AM": "countries", - "assets.countries.AO": "countries", - "assets.countries.AQ": "countries", - "assets.countries.AR": "countries", - "assets.countries.AS": "countries", - "assets.countries.AT": "countries", - "assets.countries.AU": "countries", - "assets.countries.AW": "countries", - "assets.countries.AX": "countries", - "assets.countries.AZ": "countries", - "assets.countries.BA": "countries", - "assets.countries.BB": "countries", - "assets.countries.BD": "countries", - "assets.countries.BE": "countries", - "assets.countries.BF": "countries", - "assets.countries.BG": "countries", - "assets.countries.BH": "countries", - "assets.countries.BI": "countries", - "assets.countries.BJ": "countries", - "assets.countries.BL": "countries", - "assets.countries.BM": "countries", - "assets.countries.BN": "countries", - "assets.countries.BO": "countries", - "assets.countries.BQ": "countries", - "assets.countries.BR": "countries", - "assets.countries.BS": "countries", - "assets.countries.BT": "countries", - "assets.countries.BV": "countries", - "assets.countries.BW": "countries", - "assets.countries.BY": "countries", - "assets.countries.BZ": "countries", - "assets.countries.CA": "countries", - "assets.countries.CC": "countries", - "assets.countries.CD": "countries", - "assets.countries.CF": "countries", - "assets.countries.CG": "countries", - "assets.countries.CH": "countries", - "assets.countries.CI": "countries", - "assets.countries.CK": "countries", - "assets.countries.CL": "countries", - "assets.countries.CM": "countries", - "assets.countries.CN": "countries", - "assets.countries.CO": "countries", - "assets.countries.CR": "countries", - "assets.countries.CU": "countries", - "assets.countries.CV": "countries", - "assets.countries.CW": "countries", - "assets.countries.CX": "countries", - "assets.countries.CY": "countries", - "assets.countries.CZ": "countries", - "assets.countries.DE": "countries", - "assets.countries.DJ": "countries", - "assets.countries.DK": "countries", - "assets.countries.DM": "countries", - "assets.countries.DO": "countries", - "assets.countries.DZ": "countries", - "assets.countries.EC": "countries", - "assets.countries.EE": "countries", - "assets.countries.EG": "countries", - "assets.countries.EH": "countries", - "assets.countries.ER": "countries", - "assets.countries.ES": "countries", - "assets.countries.ET": "countries", - "assets.countries.FI": "countries", - "assets.countries.FJ": "countries", - "assets.countries.FK": "countries", - "assets.countries.FM": "countries", - "assets.countries.FO": "countries", - "assets.countries.FR": "countries", - "assets.countries.GA": "countries", - "assets.countries.GB": "countries", - "assets.countries.GD": "countries", - "assets.countries.GE": "countries", - "assets.countries.GF": "countries", - "assets.countries.GG": "countries", - "assets.countries.GH": "countries", - "assets.countries.GI": "countries", - "assets.countries.GL": "countries", - "assets.countries.GM": "countries", - "assets.countries.GN": "countries", - "assets.countries.GP": "countries", - "assets.countries.GQ": "countries", - "assets.countries.GR": "countries", - "assets.countries.GS": "countries", - "assets.countries.GT": "countries", - "assets.countries.GU": "countries", - "assets.countries.GW": "countries", - "assets.countries.GY": "countries", - "assets.countries.HK": "countries", - "assets.countries.HM": "countries", - "assets.countries.HN": "countries", - "assets.countries.HR": "countries", - "assets.countries.HT": "countries", - "assets.countries.HU": "countries", - "assets.countries.ID": "countries", - "assets.countries.IE": "countries", - "assets.countries.IL": "countries", - "assets.countries.IM": "countries", - "assets.countries.IN": "countries", - "assets.countries.IO": "countries", - "assets.countries.IQ": "countries", - "assets.countries.IR": "countries", - "assets.countries.IS": "countries", - "assets.countries.IT": "countries", - "assets.countries.JE": "countries", - "assets.countries.JM": "countries", - "assets.countries.JO": "countries", - "assets.countries.JP": "countries", - "assets.countries.KE": "countries", - "assets.countries.KG": "countries", - "assets.countries.KH": "countries", - "assets.countries.KI": "countries", - "assets.countries.KM": "countries", - "assets.countries.KN": "countries", - "assets.countries.KP": "countries", - "assets.countries.KR": "countries", - "assets.countries.KW": "countries", - "assets.countries.KY": "countries", - "assets.countries.KZ": "countries", - "assets.countries.LA": "countries", - "assets.countries.LB": "countries", - "assets.countries.LC": "countries", - "assets.countries.LI": "countries", - "assets.countries.LK": "countries", - "assets.countries.LR": "countries", - "assets.countries.LS": "countries", - "assets.countries.LT": "countries", - "assets.countries.LU": "countries", - "assets.countries.LV": "countries", - "assets.countries.LY": "countries", - "assets.countries.MA": "countries", - "assets.countries.MC": "countries", - "assets.countries.MD": "countries", - "assets.countries.ME": "countries", - "assets.countries.MF": "countries", - "assets.countries.MG": "countries", - "assets.countries.MH": "countries", - "assets.countries.MK": "countries", - "assets.countries.ML": "countries", - "assets.countries.MM": "countries", - "assets.countries.MN": "countries", - "assets.countries.MO": "countries", - "assets.countries.MP": "countries", - "assets.countries.MQ": "countries", - "assets.countries.MR": "countries", - "assets.countries.MS": "countries", - "assets.countries.MT": "countries", - "assets.countries.MU": "countries", - "assets.countries.MV": "countries", - "assets.countries.MW": "countries", - "assets.countries.MX": "countries", - "assets.countries.MY": "countries", - "assets.countries.MZ": "countries", - "assets.countries.NA": "countries", - "assets.countries.NC": "countries", - "assets.countries.NE": "countries", - "assets.countries.NF": "countries", - "assets.countries.NG": "countries", - "assets.countries.NI": "countries", - "assets.countries.NL": "countries", - "assets.countries.NO": "countries", - "assets.countries.NP": "countries", - "assets.countries.NR": "countries", - "assets.countries.NU": "countries", - "assets.countries.NZ": "countries", - "assets.countries.OM": "countries", - "assets.countries.PA": "countries", - "assets.countries.PE": "countries", - "assets.countries.PF": "countries", - "assets.countries.PG": "countries", - "assets.countries.PH": "countries", - "assets.countries.PK": "countries", - "assets.countries.PL": "countries", - "assets.countries.PM": "countries", - "assets.countries.PN": "countries", - "assets.countries.PR": "countries", - "assets.countries.PS": "countries", - "assets.countries.PT": "countries", - "assets.countries.PW": "countries", - "assets.countries.PY": "countries", - "assets.countries.QA": "countries", - "assets.countries.RE": "countries", - "assets.countries.RO": "countries", - "assets.countries.RS": "countries", - "assets.countries.RU": "countries", - "assets.countries.RW": "countries", - "assets.countries.SA": "countries", - "assets.countries.SB": "countries", - "assets.countries.SC": "countries", - "assets.countries.SD": "countries", - "assets.countries.SE": "countries", - "assets.countries.SG": "countries", - "assets.countries.SH": "countries", - "assets.countries.SI": "countries", - "assets.countries.SJ": "countries", - "assets.countries.SK": "countries", - "assets.countries.SL": "countries", - "assets.countries.SM": "countries", - "assets.countries.SN": "countries", - "assets.countries.SO": "countries", - "assets.countries.SR": "countries", - "assets.countries.SS": "countries", - "assets.countries.ST": "countries", - "assets.countries.SV": "countries", - "assets.countries.SX": "countries", - "assets.countries.SY": "countries", - "assets.countries.SZ": "countries", - "assets.countries.TC": "countries", - "assets.countries.TD": "countries", - "assets.countries.TF": "countries", - "assets.countries.TG": "countries", - "assets.countries.TH": "countries", - "assets.countries.TJ": "countries", - "assets.countries.TK": "countries", - "assets.countries.TL": "countries", - "assets.countries.TM": "countries", - "assets.countries.TN": "countries", - "assets.countries.TO": "countries", - "assets.countries.TR": "countries", - "assets.countries.TT": "countries", - "assets.countries.TV": "countries", - "assets.countries.TW": "countries", - "assets.countries.TZ": "countries", - "assets.countries.UA": "countries", - "assets.countries.UG": "countries", - "assets.countries.UM": "countries", - "assets.countries.US": "countries", - "assets.countries.UY": "countries", - "assets.countries.UZ": "countries", - "assets.countries.VA": "countries", - "assets.countries.VC": "countries", - "assets.countries.VE": "countries", - "assets.countries.VG": "countries", - "assets.countries.VI": "countries", - "assets.countries.VN": "countries", - "assets.countries.VU": "countries", - "assets.countries.WF": "countries", - "assets.countries.WS": "countries", - "assets.countries.YE": "countries", - "assets.countries.YT": "countries", - "assets.countries.ZA": "countries", - "assets.countries.ZM": "countries", - "assets.countries.ZW": "countries", - "assets.mimetypes.application/epub_zip": "mimetypes", - "assets.mimetypes.application/msword": "mimetypes", - "assets.mimetypes.application/pdf": "mimetypes", - "assets.mimetypes.application/vnd.moodle.backup": "mimetypes", - "assets.mimetypes.application/vnd.ms-excel": "mimetypes", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "mimetypes", - "assets.mimetypes.application/vnd.ms-powerpoint": "mimetypes", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "mimetypes", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "mimetypes", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "mimetypes", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "mimetypes", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "mimetypes", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "mimetypes", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "mimetypes", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "mimetypes", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "mimetypes", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "mimetypes", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "mimetypes", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "mimetypes", - "assets.mimetypes.application/x-iwork-pages-sffpages": "mimetypes", - "assets.mimetypes.application/x-javascript": "mimetypes", - "assets.mimetypes.application/x-mspublisher": "mimetypes", - "assets.mimetypes.application/x-shockwave-flash": "mimetypes", - "assets.mimetypes.application/xhtml_xml": "mimetypes", - "assets.mimetypes.archive": "mimetypes", - "assets.mimetypes.audio": "mimetypes", - "assets.mimetypes.default": "mimetypes", - "assets.mimetypes.document/unknown": "mimetypes", - "assets.mimetypes.group:archive": "mimetypes", - "assets.mimetypes.group:audio": "mimetypes", - "assets.mimetypes.group:document": "mimetypes", - "assets.mimetypes.group:html_audio": "mimetypes", - "assets.mimetypes.group:html_track": "mimetypes", - "assets.mimetypes.group:html_video": "mimetypes", - "assets.mimetypes.group:image": "mimetypes", - "assets.mimetypes.group:presentation": "mimetypes", - "assets.mimetypes.group:sourcecode": "mimetypes", - "assets.mimetypes.group:spreadsheet": "mimetypes", - "assets.mimetypes.group:video": "mimetypes", - "assets.mimetypes.group:web_audio": "mimetypes", - "assets.mimetypes.group:web_file": "mimetypes", - "assets.mimetypes.group:web_image": "mimetypes", - "assets.mimetypes.group:web_video": "mimetypes", - "assets.mimetypes.image": "mimetypes", - "assets.mimetypes.image/vnd.microsoft.icon": "mimetypes", - "assets.mimetypes.text/css": "mimetypes", - "assets.mimetypes.text/csv": "mimetypes", - "assets.mimetypes.text/html": "mimetypes", - "assets.mimetypes.text/plain": "mimetypes", - "assets.mimetypes.text/rtf": "mimetypes", - "assets.mimetypes.text/vtt": "mimetypes", - "assets.mimetypes.video": "mimetypes", - "core.accounts": "admin", - "core.add": "moodle", - "core.agelocationverification": "moodle", - "core.ago": "message", - "core.all": "moodle", - "core.allgroups": "moodle", - "core.allparticipants": "moodle", - "core.answer": "moodle", - "core.answered": "quiz", - "core.areyousure": "moodle", - "core.back": "moodle", - "core.block.blocks": "moodle", - "core.browser": "local_moodlemobileapp", - "core.cancel": "moodle", - "core.cannotconnect": "local_moodlemobileapp", - "core.cannotconnecttrouble": "local_moodlemobileapp", - "core.cannotconnectverify": "local_moodlemobileapp", - "core.cannotdownloadfiles": "local_moodlemobileapp", - "core.cannotopeninapp": "local_moodlemobileapp", - "core.cannotopeninappdownload": "local_moodlemobileapp", - "core.captureaudio": "local_moodlemobileapp", - "core.capturedimage": "local_moodlemobileapp", - "core.captureimage": "local_moodlemobileapp", - "core.capturevideo": "local_moodlemobileapp", - "core.category": "moodle", - "core.choose": "moodle", - "core.choosedots": "moodle", - "core.clearsearch": "local_moodlemobileapp", - "core.clearstoreddata": "local_moodlemobileapp", - "core.clicktohideshow": "moodle", - "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.completion-alt-auto-fail": "completion", - "core.completion-alt-auto-n": "completion", - "core.completion-alt-auto-n-override": "completion", - "core.completion-alt-auto-pass": "completion", - "core.completion-alt-auto-y": "completion", - "core.completion-alt-auto-y-override": "completion", - "core.completion-alt-manual-n": "completion", - "core.completion-alt-manual-n-override": "completion", - "core.completion-alt-manual-y": "completion", - "core.completion-alt-manual-y-override": "completion", - "core.confirmcanceledit": "local_moodlemobileapp", - "core.confirmdeletefile": "repository", - "core.confirmgotabroot": "local_moodlemobileapp", - "core.confirmgotabrootdefault": "local_moodlemobileapp", - "core.confirmleaveunknownchanges": "local_moodlemobileapp", - "core.confirmloss": "local_moodlemobileapp", - "core.confirmopeninbrowser": "local_moodlemobileapp", - "core.considereddigitalminor": "moodle", - "core.content": "moodle", - "core.contenteditingsynced": "local_moodlemobileapp", - "core.contentlinks.chooseaccount": "local_moodlemobileapp", - "core.contentlinks.chooseaccounttoopenlink": "local_moodlemobileapp", - "core.contentlinks.confirmurlothersite": "local_moodlemobileapp", - "core.contentlinks.errornoactions": "local_moodlemobileapp", - "core.contentlinks.errornosites": "local_moodlemobileapp", - "core.contentlinks.errorredirectothersite": "local_moodlemobileapp", - "core.continue": "moodle", - "core.copiedtoclipboard": "local_moodlemobileapp", - "core.copytoclipboard": "local_moodlemobileapp", - "core.course": "moodle", - "core.course.activitydisabled": "local_moodlemobileapp", - "core.course.activitynotyetviewableremoteaddon": "local_moodlemobileapp", - "core.course.activitynotyetviewablesiteupgradeneeded": "local_moodlemobileapp", - "core.course.allsections": "local_moodlemobileapp", - "core.course.askadmintosupport": "local_moodlemobileapp", - "core.course.availablespace": "local_moodlemobileapp", - "core.course.cannotdeletewhiledownloading": "local_moodlemobileapp", - "core.course.confirmdeletemodulefiles": "local_moodlemobileapp", - "core.course.confirmdeletestoreddata": "local_moodlemobileapp", - "core.course.confirmdownload": "local_moodlemobileapp", - "core.course.confirmdownloadunknownsize": "local_moodlemobileapp", - "core.course.confirmdownloadzerosize": "local_moodlemobileapp", - "core.course.confirmlimiteddownload": "local_moodlemobileapp", - "core.course.confirmpartialdownloadsize": "local_moodlemobileapp", - "core.course.contents": "local_moodlemobileapp", - "core.course.couldnotloadsectioncontent": "local_moodlemobileapp", - "core.course.couldnotloadsections": "local_moodlemobileapp", - "core.course.coursesummary": "moodle", - "core.course.downloadcourse": "tool_mobile", - "core.course.errordownloadingcourse": "local_moodlemobileapp", - "core.course.errordownloadingsection": "local_moodlemobileapp", - "core.course.errorgetmodule": "local_moodlemobileapp", - "core.course.hiddenfromstudents": "moodle", - "core.course.hiddenoncoursepage": "moodle", - "core.course.insufficientavailablequota": "local_moodlemobileapp", - "core.course.insufficientavailablespace": "local_moodlemobileapp", - "core.course.manualcompletionnotsynced": "local_moodlemobileapp", - "core.course.nocontentavailable": "local_moodlemobileapp", - "core.course.overriddennotice": "grades", - "core.course.refreshcourse": "local_moodlemobileapp", - "core.course.sections": "moodle", - "core.course.useactivityonbrowser": "local_moodlemobileapp", - "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", - "core.courses.cannotretrievemorecategories": "local_moodlemobileapp", - "core.courses.categories": "moodle", - "core.courses.confirmselfenrol": "local_moodlemobileapp", - "core.courses.courses": "moodle", - "core.courses.downloadcourses": "local_moodlemobileapp", - "core.courses.enrolme": "local_moodlemobileapp", - "core.courses.errorloadcategories": "local_moodlemobileapp", - "core.courses.errorloadcourses": "local_moodlemobileapp", - "core.courses.errorloadplugins": "local_moodlemobileapp", - "core.courses.errorsearching": "local_moodlemobileapp", - "core.courses.errorselfenrol": "local_moodlemobileapp", - "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", - "core.courses.nocoursesyet": "moodle", - "core.courses.nosearchresults": "wiki", - "core.courses.notenroled": "completion", - "core.courses.notenrollable": "local_moodlemobileapp", - "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", - "core.courses.searchcoursesadvice": "local_moodlemobileapp", - "core.courses.selfenrolment": "local_moodlemobileapp", - "core.courses.sendpaymentbutton": "enrol_paypal", - "core.courses.show": "block_myoverview", - "core.courses.totalcoursesearchresults": "local_moodlemobileapp", - "core.currentdevice": "local_moodlemobileapp", - "core.datastoredoffline": "local_moodlemobileapp", - "core.date": "moodle", - "core.day": "moodle", - "core.days": "moodle", - "core.decsep": "langconfig", - "core.defaultvalue": "tool_usertours", - "core.delete": "moodle", - "core.deletedoffline": "local_moodlemobileapp", - "core.deleteduser": "bulkusers", - "core.deleting": "local_moodlemobileapp", - "core.description": "moodle", - "core.desktop": "local_moodlemobileapp", - "core.dfdaymonthyear": "local_moodlemobileapp", - "core.dfdayweekmonth": "local_moodlemobileapp", - "core.dffulldate": "local_moodlemobileapp", - "core.dflastweekdate": "local_moodlemobileapp", - "core.dfmediumdate": "local_moodlemobileapp", - "core.dftimedate": "local_moodlemobileapp", - "core.digitalminor": "moodle", - "core.digitalminor_desc": "moodle", - "core.discard": "local_moodlemobileapp", - "core.dismiss": "local_moodlemobileapp", - "core.displayoptions": "atto_media", - "core.done": "survey", - "core.download": "moodle", - "core.downloaded": "local_moodlemobileapp", - "core.downloadfile": "moodle", - "core.downloading": "local_moodlemobileapp", - "core.edit": "moodle", - "core.editor.autosavesucceeded": "editor_atto", - "core.editor.bold": "atto_bold/pluginname", - "core.editor.clear": "atto_clear/pluginname", - "core.editor.h3": "atto_title", - "core.editor.h4": "atto_title", - "core.editor.h5": "atto_title", - "core.editor.hidetoolbar": "local_moodlemobileapp", - "core.editor.italic": "atto_italic/pluginname", - "core.editor.orderedlist": "atto_orderedlist/pluginname", - "core.editor.p": "atto_title", - "core.editor.strike": "atto_strike/pluginname", - "core.editor.textrecovered": "editor_atto", - "core.editor.toggle": "local_moodlemobileapp", - "core.editor.underline": "atto_underline/pluginname", - "core.editor.unorderedlist": "atto_unorderedlist/pluginname", - "core.emptysplit": "local_moodlemobileapp", - "core.error": "moodle", - "core.errorchangecompletion": "local_moodlemobileapp", - "core.errordeletefile": "local_moodlemobileapp", - "core.errordownloading": "local_moodlemobileapp", - "core.errordownloadingsomefiles": "local_moodlemobileapp", - "core.errorfileexistssamename": "local_moodlemobileapp", - "core.errorinvalidform": "local_moodlemobileapp", - "core.errorinvalidresponse": "local_moodlemobileapp", - "core.errorloadingcontent": "local_moodlemobileapp", - "core.errorofflinedisabled": "local_moodlemobileapp", - "core.erroropenfilenoapp": "local_moodlemobileapp", - "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.errorurlschemeinvalidscheme": "local_moodlemobileapp", - "core.errorurlschemeinvalidsite": "local_moodlemobileapp", - "core.explanationdigitalminor": "moodle", - "core.favourites": "moodle", - "core.filename": "repository", - "core.filenameexist": "local_moodlemobileapp", - "core.filenotfound": "resource", - "core.fileuploader.addfiletext": "repository", - "core.fileuploader.audio": "local_moodlemobileapp", - "core.fileuploader.camera": "local_moodlemobileapp", - "core.fileuploader.confirmuploadfile": "local_moodlemobileapp", - "core.fileuploader.confirmuploadunknownsize": "local_moodlemobileapp", - "core.fileuploader.errorcapturingaudio": "local_moodlemobileapp", - "core.fileuploader.errorcapturingimage": "local_moodlemobileapp", - "core.fileuploader.errorcapturingvideo": "local_moodlemobileapp", - "core.fileuploader.errorgettingimagealbum": "local_moodlemobileapp", - "core.fileuploader.errormustbeonlinetoupload": "local_moodlemobileapp", - "core.fileuploader.errornoapp": "local_moodlemobileapp", - "core.fileuploader.errorreadingfile": "local_moodlemobileapp", - "core.fileuploader.errorwhileuploading": "local_moodlemobileapp", - "core.fileuploader.file": "local_moodlemobileapp", - "core.fileuploader.filesofthesetypes": "form", - "core.fileuploader.fileuploaded": "local_moodlemobileapp", - "core.fileuploader.invalidfiletype": "repository", - "core.fileuploader.maxbytesfile": "local_moodlemobileapp", - "core.fileuploader.more": "data", - "core.fileuploader.photoalbums": "local_moodlemobileapp", - "core.fileuploader.readingfile": "local_moodlemobileapp", - "core.fileuploader.readingfileperc": "local_moodlemobileapp", - "core.fileuploader.selectafile": "local_moodlemobileapp", - "core.fileuploader.uploadafile": "local_moodlemobileapp", - "core.fileuploader.uploading": "local_moodlemobileapp", - "core.fileuploader.uploadingperc": "local_moodlemobileapp", - "core.fileuploader.video": "local_moodlemobileapp", - "core.filter": "moodle", - "core.folder": "moodle", - "core.forcepasswordchangenotice": "moodle", - "core.fulllistofcourses": "moodle", - "core.fullnameandsitename": "local_moodlemobileapp", - "core.grades.average": "grades", - "core.grades.badgrade": "grades", - "core.grades.contributiontocoursetotal": "grades", - "core.grades.feedback": "grades", - "core.grades.grade": "grades", - "core.grades.gradeitem": "grades", - "core.grades.grades": "grades", - "core.grades.lettergrade": "grades", - "core.grades.nogradesreturned": "grades", - "core.grades.nooutcome": "grades", - "core.grades.percentage": "grades", - "core.grades.range": "grades", - "core.grades.rank": "grades", - "core.grades.weight": "grades", - "core.group": "moodle", - "core.groupsseparate": "moodle", - "core.groupsvisible": "moodle", - "core.h5p.additionallicenseinfo": "h5p", - "core.h5p.author": "h5p", - "core.h5p.authorcomments": "h5p", - "core.h5p.authorcommentsdescription": "h5p", - "core.h5p.authorname": "h5p", - "core.h5p.authorrole": "h5p", - "core.h5p.by": "h5p", - "core.h5p.cancellabel": "h5p", - "core.h5p.ccattribution": "h5p", - "core.h5p.ccattributionnc": "h5p", - "core.h5p.ccattributionncnd": "h5p", - "core.h5p.ccattributionncsa": "h5p", - "core.h5p.ccattributionnd": "h5p", - "core.h5p.ccattributionsa": "h5p", - "core.h5p.ccpdd": "h5p", - "core.h5p.changedby": "h5p", - "core.h5p.changedescription": "h5p", - "core.h5p.changelog": "h5p", - "core.h5p.changeplaceholder": "h5p", - "core.h5p.close": "h5p", - "core.h5p.confirmdialogbody": "h5p", - "core.h5p.confirmdialogheader": "h5p", - "core.h5p.confirmlabel": "h5p", - "core.h5p.connectionLost": "h5p", - "core.h5p.connectionReestablished": "h5p", - "core.h5p.contentCopied": "h5p", - "core.h5p.contentchanged": "h5p", - "core.h5p.contenttype": "h5p", - "core.h5p.copyright": "h5p", - "core.h5p.copyrightinfo": "h5p", - "core.h5p.copyrightstring": "h5p", - "core.h5p.copyrighttitle": "h5p", - "core.h5p.creativecommons": "h5p", - "core.h5p.date": "h5p", - "core.h5p.disablefullscreen": "h5p", - "core.h5p.download": "h5p", - "core.h5p.downloadtitle": "h5p", - "core.h5p.editor": "h5p", - "core.h5p.embed": "h5p", - "core.h5p.embedtitle": "h5p", - "core.h5p.errorgetemail": "local_moodlemobileapp", - "core.h5p.fullscreen": "h5p", - "core.h5p.gpl": "h5p", - "core.h5p.h5ptitle": "h5p", - "core.h5p.hideadvanced": "h5p", - "core.h5p.license": "h5p", - "core.h5p.licenseCC010": "h5p", - "core.h5p.licenseCC010U": "h5p", - "core.h5p.licenseCC10": "h5p", - "core.h5p.licenseCC20": "h5p", - "core.h5p.licenseCC25": "h5p", - "core.h5p.licenseCC30": "h5p", - "core.h5p.licenseCC40": "h5p", - "core.h5p.licenseGPL": "h5p", - "core.h5p.licenseV1": "h5p", - "core.h5p.licenseV2": "h5p", - "core.h5p.licenseV3": "h5p", - "core.h5p.licensee": "h5p", - "core.h5p.licenseextras": "h5p", - "core.h5p.licenseversion": "h5p", - "core.h5p.nocopyright": "h5p", - "core.h5p.offlineDialogBody": "h5p", - "core.h5p.offlineDialogHeader": "h5p", - "core.h5p.offlineDialogRetryButtonLabel": "h5p", - "core.h5p.offlineDialogRetryMessage": "h5p", - "core.h5p.offlineSuccessfulSubmit": "h5p", - "core.h5p.offlinedisabled": "local_moodlemobileapp", - "core.h5p.originator": "h5p", - "core.h5p.pd": "h5p", - "core.h5p.pddl": "h5p", - "core.h5p.pdm": "h5p", - "core.h5p.play": "local_moodlemobileapp", - "core.h5p.resizescript": "h5p", - "core.h5p.resubmitScores": "h5p", - "core.h5p.reuse": "h5p", - "core.h5p.reuseContent": "h5p", - "core.h5p.reuseDescription": "h5p", - "core.h5p.showadvanced": "h5p", - "core.h5p.showless": "h5p", - "core.h5p.showmore": "h5p", - "core.h5p.size": "h5p", - "core.h5p.source": "h5p", - "core.h5p.startingover": "h5p", - "core.h5p.sublevel": "h5p", - "core.h5p.thumbnail": "h5p", - "core.h5p.title": "h5p", - "core.h5p.undisclosed": "h5p", - "core.h5p.year": "h5p", - "core.h5p.years": "h5p", - "core.h5p.yearsfrom": "h5p", - "core.h5p.yearsto": "h5p", - "core.hasdatatosync": "local_moodlemobileapp", - "core.help": "moodle", - "core.hide": "moodle", - "core.hour": "moodle", - "core.hours": "moodle", - "core.humanreadablesize": "local_moodlemobileapp", - "core.image": "local_moodlemobileapp", - "core.imageviewer": "local_moodlemobileapp", - "core.info": "moodle", - "core.invalidformdata": "error", - "core.labelsep": "langconfig", - "core.lastaccess": "moodle", - "core.lastdownloaded": "local_moodlemobileapp", - "core.lastmodified": "moodle", - "core.lastsync": "local_moodlemobileapp", - "core.layoutgrid": "workshopform_rubric", - "core.list": "moodle", - "core.listsep": "langconfig", - "core.loading": "moodle", - "core.loadmore": "local_moodlemobileapp", - "core.location": "moodle", - "core.login.auth_email": "auth_email/pluginname", - "core.login.authenticating": "local_moodlemobileapp", - "core.login.cancel": "moodle", - "core.login.changepassword": "moodle", - "core.login.changepasswordbutton": "local_moodlemobileapp", - "core.login.changepasswordhelp": "local_moodlemobileapp", - "core.login.changepasswordinstructions": "local_moodlemobileapp", - "core.login.changepasswordlogoutinstructions": "local_moodlemobileapp", - "core.login.changepasswordreconnectinstructions": "local_moodlemobileapp", - "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", - "core.login.createuserandpass": "moodle", - "core.login.credentialsdescription": "local_moodlemobileapp", - "core.login.emailconfirmsent": "moodle", - "core.login.emailconfirmsentnoemail": "local_moodlemobileapp", - "core.login.emailconfirmsentsuccess": "moodle", - "core.login.emailnotmatch": "local_moodlemobileapp", - "core.login.erroraccesscontrolalloworigin": "local_moodlemobileapp", - "core.login.errordeletesite": "local_moodlemobileapp", - "core.login.errorexampleurl": "local_moodlemobileapp", - "core.login.errorqrnoscheme": "local_moodlemobileapp", - "core.login.errorupdatesite": "local_moodlemobileapp", - "core.login.faqcannotconnectanswer": "local_moodlemobileapp", - "core.login.faqcannotconnectquestion": "local_moodlemobileapp", - "core.login.faqcannotfindmysiteanswer": "local_moodlemobileapp", - "core.login.faqcannotfindmysitequestion": "local_moodlemobileapp", - "core.login.faqsetupsiteanswer": "local_moodlemobileapp", - "core.login.faqsetupsitelinktitle": "local_moodlemobileapp", - "core.login.faqsetupsitequestion": "local_moodlemobileapp", - "core.login.faqtestappanswer": "local_moodlemobileapp", - "core.login.faqtestappquestion": "local_moodlemobileapp", - "core.login.faqwhatisurlanswer": "local_moodlemobileapp", - "core.login.faqwhatisurlquestion": "local_moodlemobileapp", - "core.login.faqwhereisqrcode": "local_moodlemobileapp", - "core.login.faqwhereisqrcodeanswer": "local_moodlemobileapp", - "core.login.findyoursite": "local_moodlemobileapp", - "core.login.firsttime": "moodle", - "core.login.forcepasswordchangenotice": "moodle", - "core.login.forgotten": "moodle", - "core.login.help": "moodle", - "core.login.helpmelogin": "local_moodlemobileapp", - "core.login.instructions": "auth", - "core.login.invalidaccount": "local_moodlemobileapp", - "core.login.invaliddate": "calendar/errorinvaliddate", - "core.login.invalidemail": "moodle", - "core.login.invalidmoodleversion": "local_moodlemobileapp", - "core.login.invalidsite": "local_moodlemobileapp", - "core.login.invalidtime": "local_moodlemobileapp", - "core.login.invalidurl": "scorm", - "core.login.invalidvaluemax": "local_moodlemobileapp", - "core.login.invalidvaluemin": "local_moodlemobileapp", - "core.login.localmobileunexpectedresponse": "local_moodlemobileapp", - "core.login.loggedoutssodescription": "local_moodlemobileapp", - "core.login.login": "moodle", - "core.login.loginbutton": "local_moodlemobileapp", - "core.login.logininsiterequired": "local_moodlemobileapp", - "core.login.loginsteps": "moodle", - "core.login.missingemail": "moodle", - "core.login.missingfirstname": "moodle", - "core.login.missinglastname": "moodle", - "core.login.mobileservicesnotenabled": "local_moodlemobileapp", - "core.login.mustconfirm": "moodle", - "core.login.newaccount": "moodle", - "core.login.notloggedin": "local_moodlemobileapp", - "core.login.onboardingcreatemanagecourses": "local_moodlemobileapp", - "core.login.onboardingenrolmanagestudents": "local_moodlemobileapp", - "core.login.onboardinggetstarted": "local_moodlemobileapp", - "core.login.onboardingialreadyhaveasite": "local_moodlemobileapp", - "core.login.onboardingimalearner": "local_moodlemobileapp", - "core.login.onboardingimaneducator": "local_moodlemobileapp", - "core.login.onboardingineedasite": "local_moodlemobileapp", - "core.login.onboardingprovidefeedback": "local_moodlemobileapp", - "core.login.onboardingtoconnect": "local_moodlemobileapp", - "core.login.onboardingwelcome": "local_moodlemobileapp", - "core.login.or": "local_moodlemobileapp", - "core.login.password": "moodle", - "core.login.passwordforgotten": "moodle", - "core.login.passwordforgotteninstructions2": "moodle", - "core.login.passwordrequired": "local_moodlemobileapp", - "core.login.policyaccept": "moodle", - "core.login.policyagree": "moodle", - "core.login.policyagreement": "moodle", - "core.login.policyagreementclick": "moodle", - "core.login.potentialidps": "auth", - "core.login.profileinvaliddata": "admin", - "core.login.recaptchachallengeimage": "local_moodlemobileapp", - "core.login.recaptchaexpired": "local_moodlemobileapp", - "core.login.recaptchaincorrect": "local_moodlemobileapp", - "core.login.reconnect": "local_moodlemobileapp", - "core.login.reconnectdescription": "local_moodlemobileapp", - "core.login.reconnectssodescription": "local_moodlemobileapp", - "core.login.resendemail": "moodle", - "core.login.searchby": "local_moodlemobileapp", - "core.login.security_question": "auth", - "core.login.selectacountry": "moodle", - "core.login.selectsite": "local_moodlemobileapp", - "core.login.signupplugindisabled": "local_moodlemobileapp", - "core.login.signuprequiredfieldnotsupported": "local_moodlemobileapp", - "core.login.siteaddress": "local_moodlemobileapp", - "core.login.siteaddressplaceholder": "donottranslate", - "core.login.sitehasredirect": "local_moodlemobileapp", - "core.login.siteinmaintenance": "local_moodlemobileapp", - "core.login.sitepolicynotagreederror": "local_moodlemobileapp", - "core.login.siteurl": "local_moodlemobileapp", - "core.login.siteurlrequired": "local_moodlemobileapp", - "core.login.startsignup": "moodle", - "core.login.stillcantconnect": "local_moodlemobileapp", - "core.login.supplyinfo": "moodle", - "core.login.username": "moodle", - "core.login.usernameoremail": "moodle", - "core.login.usernamerequired": "local_moodlemobileapp", - "core.login.usernotaddederror": "error", - "core.login.visitchangepassword": "local_moodlemobileapp", - "core.login.webservicesnotenabled": "local_moodlemobileapp", - "core.login.youcanstillconnectwithcredentials": "local_moodlemobileapp", - "core.login.yourenteredsite": "local_moodlemobileapp", - "core.lostconnection": "local_moodlemobileapp", - "core.mainmenu.changesite": "local_moodlemobileapp", - "core.mainmenu.help": "moodle", - "core.mainmenu.logout": "moodle", - "core.mainmenu.website": "local_moodlemobileapp", - "core.maxsizeandattachments": "moodle", - "core.min": "moodle", - "core.mins": "moodle", - "core.misc": "admin", - "core.mod_assign": "assign/pluginname", - "core.mod_assignment": "assignment/pluginname", - "core.mod_book": "book/pluginname", - "core.mod_chat": "chat/pluginname", - "core.mod_choice": "choice/pluginname", - "core.mod_data": "data/pluginname", - "core.mod_database": "data/pluginname", - "core.mod_external-tool": "lti/pluginname", - "core.mod_feedback": "feedback/pluginname", - "core.mod_file": "moodle/file", - "core.mod_folder": "folder/pluginname", - "core.mod_forum": "forum/pluginname", - "core.mod_glossary": "glossary/pluginname", - "core.mod_h5pactivity": "h5pactivity/pluginname", - "core.mod_ims": "imscp/pluginname", - "core.mod_imscp": "imscp/pluginname", - "core.mod_label": "label/pluginname", - "core.mod_lesson": "lesson/pluginname", - "core.mod_lti": "lti/pluginname", - "core.mod_page": "page/pluginname", - "core.mod_quiz": "quiz/pluginname", - "core.mod_resource": "resource/pluginname", - "core.mod_scorm": "scorm/pluginname", - "core.mod_survey": "survey/pluginname", - "core.mod_url": "url/pluginname", - "core.mod_wiki": "wiki/pluginname", - "core.mod_workshop": "workshop/pluginname", - "core.moduleintro": "moodle", - "core.more": "moodle", - "core.mygroups": "group", - "core.name": "moodle", - "core.needhelp": "local_moodlemobileapp", - "core.networkerroriframemsg": "local_moodlemobileapp", - "core.networkerrormsg": "local_moodlemobileapp", - "core.never": "moodle", - "core.next": "moodle", - "core.no": "moodle", - "core.nocomments": "moodle", - "core.nograde": "moodle", - "core.none": "moodle", - "core.nooptionavailable": "local_moodlemobileapp", - "core.nopasswordchangeforced": "local_moodlemobileapp", - "core.nopermissionerror": "local_moodlemobileapp", - "core.nopermissions": "error", - "core.noresults": "moodle", - "core.noselection": "form", - "core.notapplicable": "local_moodlemobileapp", - "core.notavailable": "moodle", - "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", - "core.online": "message", - "core.openfile": "local_moodlemobileapp", - "core.openfullimage": "local_moodlemobileapp", - "core.openinbrowser": "local_moodlemobileapp", - "core.openmodinbrowser": "local_moodlemobileapp", - "core.othergroups": "group", - "core.pagea": "moodle", - "core.parentlanguage": "langconfig", - "core.paymentinstant": "moodle", - "core.percentagenumber": "local_moodlemobileapp", - "core.phone": "moodle", - "core.pictureof": "moodle", - "core.previous": "moodle", - "core.proceed": "moodle", - "core.pulltorefresh": "local_moodlemobileapp", - "core.qrscanner": "local_moodlemobileapp", - "core.question.answer": "question", - "core.question.answersaved": "question", - "core.question.cannotdeterminestatus": "local_moodlemobileapp", - "core.question.certainty": "qbehaviour_deferredcbm", - "core.question.complete": "question", - "core.question.correct": "question", - "core.question.errorattachmentsnotsupported": "local_moodlemobileapp", - "core.question.errorinlinefilesnotsupported": "local_moodlemobileapp", - "core.question.errorquestionnotsupported": "local_moodlemobileapp", - "core.question.feedback": "question", - "core.question.howtodraganddrop": "local_moodlemobileapp", - "core.question.incorrect": "question", - "core.question.information": "question", - "core.question.invalidanswer": "question", - "core.question.notanswered": "question", - "core.question.notyetanswered": "question", - "core.question.partiallycorrect": "question", - "core.question.questionmessage": "local_moodlemobileapp", - "core.question.questionno": "question", - "core.question.requiresgrading": "question", - "core.quotausage": "moodle", - "core.rating.aggregateavg": "rating", - "core.rating.aggregatecount": "rating", - "core.rating.aggregatemax": "rating", - "core.rating.aggregatemin": "rating", - "core.rating.aggregatesum": "rating", - "core.rating.noratings": "rating", - "core.rating.rating": "rating", - "core.rating.ratings": "rating", - "core.redirectingtosite": "local_moodlemobileapp", - "core.refresh": "moodle", - "core.remove": "moodle", - "core.removefiles": "local_moodlemobileapp", - "core.required": "moodle", - "core.requireduserdatamissing": "local_moodlemobileapp", - "core.resourcedisplayopen": "moodle", - "core.resources": "moodle", - "core.restore": "moodle", - "core.restricted": "moodle", - "core.retry": "local_moodlemobileapp", - "core.save": "moodle", - "core.savechanges": "assign", - "core.scanqr": "local_moodlemobileapp", - "core.search": "moodle", - "core.searching": "local_moodlemobileapp", - "core.searchresults": "moodle", - "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", - "core.settings.about": "local_moodlemobileapp", - "core.settings.appsettings": "local_moodlemobileapp", - "core.settings.appversion": "local_moodlemobileapp", - "core.settings.cannotsyncloggedout": "local_moodlemobileapp", - "core.settings.cannotsyncoffline": "local_moodlemobileapp", - "core.settings.cannotsyncwithoutwifi": "local_moodlemobileapp", - "core.settings.colorscheme": "local_moodlemobileapp", - "core.settings.colorscheme-auto": "local_moodlemobileapp", - "core.settings.colorscheme-dark": "local_moodlemobileapp", - "core.settings.colorscheme-light": "local_moodlemobileapp", - "core.settings.compilationinfo": "local_moodlemobileapp", - "core.settings.copyinfo": "local_moodlemobileapp", - "core.settings.cordovadevicemodel": "local_moodlemobileapp", - "core.settings.cordovadeviceosversion": "local_moodlemobileapp", - "core.settings.cordovadeviceplatform": "local_moodlemobileapp", - "core.settings.cordovadeviceuuid": "local_moodlemobileapp", - "core.settings.cordovaversion": "local_moodlemobileapp", - "core.settings.currentlanguage": "moodle", - "core.settings.debugdisplay": "admin", - "core.settings.debugdisplaydescription": "local_moodlemobileapp", - "core.settings.deletesitefiles": "local_moodlemobileapp", - "core.settings.deletesitefilestitle": "local_moodlemobileapp", - "core.settings.deviceinfo": "local_moodlemobileapp", - "core.settings.deviceos": "local_moodlemobileapp", - "core.settings.disableall": "message", - "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", - "core.settings.entriesincache": "local_moodlemobileapp", - "core.settings.errordeletesitefiles": "local_moodlemobileapp", - "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.forcedsetting": "local_moodlemobileapp", - "core.settings.general": "moodle", - "core.settings.language": "moodle", - "core.settings.license": "moodle", - "core.settings.localnotifavailable": "local_moodlemobileapp", - "core.settings.locationhref": "local_moodlemobileapp", - "core.settings.locked": "admin", - "core.settings.loggedin": "message", - "core.settings.loggedoff": "message", - "core.settings.navigatorlanguage": "local_moodlemobileapp", - "core.settings.navigatoruseragent": "local_moodlemobileapp", - "core.settings.networkstatus": "local_moodlemobileapp", - "core.settings.opensourcelicenses": "local_moodlemobileapp", - "core.settings.preferences": "moodle", - "core.settings.privacypolicy": "local_moodlemobileapp", - "core.settings.publisher": "local_moodlemobileapp", - "core.settings.pushid": "local_moodlemobileapp", - "core.settings.reportinbackground": "local_moodlemobileapp", - "core.settings.screen": "local_moodlemobileapp", - "core.settings.settings": "moodle", - "core.settings.showdownloadoptions": "local_moodlemobileapp", - "core.settings.siteinfo": "local_moodlemobileapp", - "core.settings.sites": "moodle", - "core.settings.spaceusage": "local_moodlemobileapp", - "core.settings.spaceusagehelp": "local_moodlemobileapp", - "core.settings.synchronization": "local_moodlemobileapp", - "core.settings.synchronizenow": "local_moodlemobileapp", - "core.settings.synchronizenowhelp": "local_moodlemobileapp", - "core.settings.syncsettings": "local_moodlemobileapp", - "core.settings.total": "moodle", - "core.settings.wificonnection": "local_moodlemobileapp", - "core.sharedfiles.chooseaccountstorefile": "local_moodlemobileapp", - "core.sharedfiles.chooseactionrepeatedfile": "local_moodlemobileapp", - "core.sharedfiles.errorreceivefilenosites": "local_moodlemobileapp", - "core.sharedfiles.nosharedfiles": "local_moodlemobileapp", - "core.sharedfiles.nosharedfilestoupload": "local_moodlemobileapp", - "core.sharedfiles.rename": "local_moodlemobileapp", - "core.sharedfiles.replace": "local_moodlemobileapp", - "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", - "core.sitehome.sitenews": "moodle", - "core.sitemaintenance": "admin", - "core.sizeb": "moodle", - "core.sizegb": "moodle", - "core.sizekb": "moodle", - "core.sizemb": "moodle", - "core.sizetb": "local_moodlemobileapp", - "core.skip": "tool_usertours", - "core.sorry": "local_moodlemobileapp", - "core.sort": "moodle", - "core.sortby": "moodle", - "core.start": "grouptool", - "core.storingfiles": "local_moodlemobileapp", - "core.strftimedate": "langconfig", - "core.strftimedatefullshort": "langconfig", - "core.strftimedateshort": "langconfig", - "core.strftimedatetime": "langconfig", - "core.strftimedatetimeshort": "langconfig", - "core.strftimedaydate": "langconfig", - "core.strftimedaydatetime": "langconfig", - "core.strftimedayshort": "langconfig", - "core.strftimedaytime": "langconfig", - "core.strftimemonthyear": "langconfig", - "core.strftimerecent": "langconfig", - "core.strftimerecentfull": "langconfig", - "core.strftimetime": "langconfig", - "core.strftimetime12": "langconfig", - "core.strftimetime24": "langconfig", - "core.submit": "moodle", - "core.success": "moodle", - "core.tablet": "local_moodlemobileapp", - "core.tag.defautltagcoll": "tag", - "core.tag.errorareanotsupported": "local_moodlemobileapp", - "core.tag.inalltagcoll": "tag", - "core.tag.itemstaggedwith": "tag", - "core.tag.noresultsfor": "tag", - "core.tag.notagsfound": "tag", - "core.tag.searchtags": "tag", - "core.tag.showingfirsttags": "tag", - "core.tag.tag": "moodle", - "core.tag.tagarea_course": "tag", - "core.tag.tagarea_course_modules": "tag", - "core.tag.tagarea_post": "tag", - "core.tag.tagarea_user": "tag", - "core.tag.tags": "moodle", - "core.tag.warningareasnotsupported": "local_moodlemobileapp", - "core.teachers": "moodle", - "core.thereisdatatosync": "local_moodlemobileapp", - "core.thisdirection": "langconfig", - "core.time": "moodle", - "core.timesup": "quiz", - "core.today": "moodle", - "core.tryagain": "local_moodlemobileapp", - "core.twoparagraphs": "local_moodlemobileapp", - "core.uhoh": "local_moodlemobileapp", - "core.unexpectederror": "local_moodlemobileapp", - "core.unicodenotsupported": "local_moodlemobileapp", - "core.unicodenotsupportedcleanerror": "local_moodlemobileapp", - "core.unknown": "local_moodlemobileapp", - "core.unlimited": "moodle", - "core.unzipping": "local_moodlemobileapp", - "core.updaterequired": "local_moodlemobileapp", - "core.updaterequireddesc": "local_moodlemobileapp", - "core.upgraderunning": "error", - "core.user": "moodle", - "core.user.address": "moodle", - "core.user.city": "moodle", - "core.user.contact": "local_moodlemobileapp", - "core.user.country": "moodle", - "core.user.description": "moodle", - "core.user.details": "report_security", - "core.user.detailsnotavailable": "local_moodlemobileapp", - "core.user.editingteacher": "moodle/defaultcourseteacher", - "core.user.email": "moodle", - "core.user.emailagain": "moodle", - "core.user.errorloaduser": "local_moodlemobileapp", - "core.user.firstname": "moodle", - "core.user.interests": "moodle", - "core.user.lastname": "moodle", - "core.user.manager": "role", - "core.user.newpicture": "moodle", - "core.user.noparticipants": "error", - "core.user.participants": "moodle", - "core.user.phone1": "moodle", - "core.user.phone2": "moodle", - "core.user.roles": "moodle", - "core.user.sendemail": "local_moodlemobileapp", - "core.user.student": "moodle/defaultcoursestudent", - "core.user.teacher": "moodle/noneditingteacher", - "core.user.webpage": "moodle", - "core.userdeleted": "moodle", - "core.userdetails": "moodle", - "core.usernotfullysetup": "error", - "core.users": "moodle", - "core.view": "moodle", - "core.viewcode": "local_moodlemobileapp", - "core.vieweditor": "local_moodlemobileapp", - "core.viewembeddedcontent": "local_moodlemobileapp", - "core.viewprofile": "moodle", - "core.warningofflinedatadeleted": "local_moodlemobileapp", - "core.whatisyourage": "moodle", - "core.wheredoyoulive": "moodle", - "core.whoissiteadmin": "local_moodlemobileapp", - "core.whoops": "local_moodlemobileapp", - "core.whyisthishappening": "local_moodlemobileapp", - "core.whyisthisrequired": "moodle", - "core.wsfunctionnotavailable": "local_moodlemobileapp", - "core.year": "moodle", - "core.years": "moodle", - "core.yes": "moodle", - "core.youreoffline": "local_moodlemobileapp", - "core.youreonline": "local_moodlemobileapp" -} diff --git a/scripts/mirror.sh b/scripts/mirror.sh deleted file mode 100755 index 32e4efa4a..000000000 --- a/scripts/mirror.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -source "scripts/functions.sh" - -npm run build --bailOnLintError true --typeCheckOnLint true -if [ $? -ne 0 ]; then - exit 1 -fi - -if [ -z $GIT_ORG_PRIVATE ] || [ -z $GIT_TOKEN ]; then - print_error "Env vars not correctly defined" - exit 1 -fi - -# List first level of installed libraries so we can check the installed versions. -print_title "NPM packages list" -npm list --depth=0 - -if [ "$TRAVIS_BRANCH" == 'master' ]; then - print_title "Update langpacks" - cd scripts - ./update_lang.sh - cd .. - - print_title "Update generated lang files" - git remote set-url origin https://$GIT_TOKEN@github.com/$TRAVIS_REPO_SLUG.git - git fetch -q origin - git add -A src/assets/lang - git add */en.json - git add src/config.json - git commit -m 'Update lang files [ci skip]' - - print_title "Update Licenses" - npm install -g license-checker - - jq --version - license-checker --json --production --relativeLicensePath > licenses.json - jq 'del(.[].path)' licenses.json > licenses_old.json - mv licenses_old.json licenses.json - licenses=`jq -r 'keys[]' licenses.json` - echo "{" > licensesurl.json - first=1 - for license in $licenses; do - obj=`jq --arg lic $license '.[$lic]' licenses.json` - licensePath=`echo $obj | jq -r '.licenseFile'` - file="" - if [[ ! -z "$licensePath" ]] || [[ "$licensePath" != "null" ]]; then - file=$(basename $licensePath) - if [ $first -eq 1 ] ; then - first=0 - echo "\"$license\" : { \"licenseFile\" : \"$file\"}" >> licensesurl.json - else - echo ",\"$license\" : { \"licenseFile\" : \"$file\"}" >> licensesurl.json - fi - fi - done - echo "}" >> licensesurl.json - - jq -s '.[0] * .[1]' licenses.json licensesurl.json > licenses_old.json - mv licenses_old.json licenses.json - rm licensesurl.json - - git add licenses.json - git commit -m 'Update licenses [ci skip]' - - git push origin HEAD:$TRAVIS_BRANCH - notify_on_error_exit "MIRROR: Unsuccessful push, stopping..." -fi - -if [ "$TRAVIS_BRANCH" == 'integration' ] || [ "$TRAVIS_BRANCH" == 'master' ] || [ "$TRAVIS_BRANCH" == 'desktop' ] ; then - print_title "Mirror repository" - git remote add mirror https://$GIT_TOKEN@github.com/$GIT_ORG_PRIVATE/moodleapp.git - git fetch -q --unshallow mirror - notify_on_error_exit "MIRROR: Unsuccessful fetch of mirror, stopping..." - git fetch -q origin --depth=100 - notify_on_error_exit "MIRROR: Unsuccessful fetch of origin, stopping..." - git push -f mirror HEAD:$TRAVIS_BRANCH - notify_on_error_exit "MIRROR: Unsuccessful mirror, stopping..." - git push -f mirror --tags - notify_on_error_exit "MIRROR: Unsuccessful mirror tags, stopping..." -fi diff --git a/scripts/moodle_to_json.php b/scripts/moodle_to_json.php deleted file mode 100644 index e82eb9523..000000000 --- a/scripts/moodle_to_json.php +++ /dev/null @@ -1,62 +0,0 @@ -. - -/** - * Script for converting moodle strings to json. - */ - -// Check we are in CLI. -if (isset($_SERVER['REMOTE_ADDR'])) { - exit(1); -} -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); -$config_langs = array_keys(get_object_vars($config['languages'])); - -// Set languages to do. If script is called using a language it will be used as unique. -if (isset($argv[1]) && !empty($argv[1])) { - $forcedetect = false; - define('TOTRANSLATE', true); - $languages = explode(',', $argv[1]); -} else { - $forcedetect = true; - define('TOTRANSLATE', false); - $languages = $config_langs; -} - -$keys = get_langindex_keys(); - -$added_langs = build_languages($languages, $keys); - -if ($forcedetect) { - $new_langs = detect_languages($languages, $keys); - - if (!empty($new_langs)) { - echo "\n\n\nThe following languages are going to be added\n\n\n"; - $added_langs = build_languages($new_langs, $keys, $added_langs); - } -} - -add_langs_to_config($added_langs, $config); diff --git a/scripts/test_e2e.sh b/scripts/test_e2e.sh deleted file mode 100755 index 50908f91a..000000000 --- a/scripts/test_e2e.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -source "scripts/functions.sh" - -# Prepare variables -basedir="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )" -dockerscripts="$HOME/moodle-docker/bin/" -dockercompose="$dockerscripts/moodle-docker-compose" - -export MOODLE_DOCKER_DB=pgsql -export MOODLE_DOCKER_BROWSER=chrome -export MOODLE_DOCKER_WWWROOT="$HOME/moodle" -export MOODLE_DOCKER_PHP_VERSION=7.3 -export MOODLE_DOCKER_APP_PATH=$basedir - -# Prepare dependencies -print_title "Preparing dependencies" -git clone --branch master --depth 1 git://github.com/moodle/moodle $HOME/moodle -git clone --branch master --depth 1 git://github.com/moodlehq/moodle-local_moodlemobileapp $HOME/moodle/local/moodlemobileapp -git clone --branch master --depth 1 git://github.com/moodlehq/moodle-docker $HOME/moodle-docker - -cp $HOME/moodle-docker/config.docker-template.php $HOME/moodle/config.php - -# Build app -print_title "Building app" -npm install -npm run setup - -# Start containers -print_title "Starting containers" -$dockercompose pull -$dockercompose up -d -$dockerscripts/moodle-docker-wait-for-db -$dockerscripts/moodle-docker-wait-for-app - -$dockercompose exec -T webserver sh -c "php admin/tool/behat/cli/init.php" -notify_on_error_exit "e2e failed initializing behat" - -print_title "Running e2e tests" - -# Run tests -for tags in "$@" -do - $dockercompose exec -T webserver sh -c "php admin/tool/behat/cli/run.php --tags=\"$tags\" --auto-rerun" - notify_on_error_exit "Some e2e tests are failing, please review" -done - -# Clean up -$dockercompose down diff --git a/scripts/update_lang.sh b/scripts/update_lang.sh deleted file mode 100755 index dac901789..000000000 --- a/scripts/update_lang.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -source "functions.sh" -forceLang=$1 - -print_title 'Getting languages' -git clone --depth 1 --no-single-branch https://git.in.moodle.com/moodle/moodle-langpacks.git $LANGPACKSFOLDER -pushd $LANGPACKSFOLDER -BRANCHES=($(git branch -r --format="%(refname:lstrip=3)" --sort="refname" | grep MOODLE_)) -BRANCH=${BRANCHES[${#BRANCHES[@]}-1]} -git checkout $BRANCH -git pull -popd - -print_title 'Getting local mobile langs' -git clone --depth 1 https://github.com/moodlehq/moodle-local_moodlemobileapp.git ../../moodle-local_moodlemobileapp - -if [ -z $forceLang ]; then - php -f moodle_to_json.php -else - php -f moodle_to_json.php "$forceLang" -fi - -print_ok 'All done!' \ No newline at end of file diff --git a/scripts/ws_to_ts_functions.php b/scripts/ws_to_ts_functions.php deleted file mode 100644 index ca05d2082..000000000 --- a/scripts/ws_to_ts_functions.php +++ /dev/null @@ -1,244 +0,0 @@ -. - -/** - * Helper functions for converting a Moodle WS structure to a TS type. - */ - -/** - * Get the structure of a WS params or returns. - */ -function get_ws_structure($wsname, $useparams) { - global $DB; - - // get all the function descriptions - $functions = $DB->get_records('external_functions', array(), 'name'); - $functiondescs = array(); - foreach ($functions as $function) { - $functiondescs[$function->name] = external_api::external_function_info($function); - } - - if (!isset($functiondescs[$wsname])) { - return false; - } else if ($useparams) { - return $functiondescs[$wsname]->parameters_desc; - } else { - return $functiondescs[$wsname]->returns_desc; - } -} - -/** - * Fix a comment: make sure first letter is uppercase and add a dot at the end if needed. - */ -function fix_comment($desc) { - $desc = trim($desc); - $desc = ucfirst($desc); - - if (substr($desc, -1) !== '.') { - $desc .= '.'; - } - - return $desc; -} - -/** - * Get an inline comment based on a certain text. - */ -function get_inline_comment($desc) { - if (empty($desc)) { - return ''; - } - - return ' // ' . fix_comment($desc); -} - -/** - * Add the TS documentation of a certain element. - */ -function get_ts_doc($type, $desc, $indentation) { - if (empty($desc)) { - // If no key, it's probably in an array. We only document object properties. - return ''; - } - - return $indentation . "/**\n" . - $indentation . " * " . fix_comment($desc) . "\n" . - (!empty($type) ? ($indentation . " * @type {" . $type . "}\n") : '') . - $indentation . " */\n"; -} - -/** - * Specify a certain type, with or without a key. - */ -function convert_key_type($key, $type, $required, $indentation) { - if ($key) { - // It has a key, it's inside an object. - return $indentation . "$key" . ($required == VALUE_OPTIONAL ? '?' : '') . ": $type"; - } else { - // No key, it's probably in an array. Just include the type. - return $type; - } -} - -/** - * Convert a certain element into a TS structure. - */ -function convert_to_ts($key, $value, $boolisnumber = false, $indentation = '', $arraydesc = '') { - if ($value instanceof external_value || $value instanceof external_warnings || $value instanceof external_files) { - // It's a basic field or a pre-defined type like warnings. - $type = 'string'; - - if ($value instanceof external_warnings) { - $type = 'CoreWSExternalWarning[]'; - } else if ($value instanceof external_files) { - $type = 'CoreWSExternalFile[]'; - } else if ($value->type == PARAM_BOOL && !$boolisnumber) { - $type = 'boolean'; - } else if (($value->type == PARAM_BOOL && $boolisnumber) || $value->type == PARAM_INT || $value->type == PARAM_FLOAT || - $value->type == PARAM_LOCALISEDFLOAT || $value->type == PARAM_PERMISSION || $value->type == PARAM_INTEGER || - $value->type == PARAM_NUMBER) { - $type = 'number'; - } - - $result = convert_key_type($key, $type, $value->required, $indentation); - - return $result; - - } else if ($value instanceof external_single_structure) { - // It's an object. - $result = convert_key_type($key, '{', $value->required, $indentation); - - if ($arraydesc) { - // It's an array of objects. Print the array description now. - $result .= get_inline_comment($arraydesc); - } - - $result .= "\n"; - - foreach ($value->keys as $key => $value) { - $result .= convert_to_ts($key, $value, $boolisnumber, $indentation . ' ') . ';'; - - if (!$value instanceof external_multiple_structure || !$value->content instanceof external_single_structure) { - // Add inline comments after the field, except for arrays of objects where it's added at the start. - $result .= get_inline_comment($value->desc); - } - - $result .= "\n"; - } - - $result .= "$indentation}"; - - return $result; - - } else if ($value instanceof external_multiple_structure) { - // It's an array. - $result = convert_key_type($key, '', $value->required, $indentation); - - $result .= convert_to_ts(null, $value->content, $boolisnumber, $indentation, $value->desc); - - $result .= "[]"; - - return $result; - } else { - echo "WARNING: Unknown structure: $key " . get_class($value) . " \n"; - - return ""; - } -} - -/** - * Concatenate two paths. - */ -function concatenate_paths($left, $right, $separator = '/') { - if (!is_string($left) || $left == '') { - return $right; - } else if (!is_string($right) || $right == '') { - return $left; - } - - $lastCharLeft = substr($left, -1); - $firstCharRight = $right[0]; - - if ($lastCharLeft === $separator && $firstCharRight === $separator) { - return $left . substr($right, 1); - } else if ($lastCharLeft !== $separator && $firstCharRight !== '/') { - return $left . '/' . $right; - } else { - return $left . $right; - } -} - -/** - * Detect changes between 2 WS structures. We only detect fields that have been added or modified, not removed fields. - */ -function detect_ws_changes($new, $old, $key = '', $path = '') { - $messages = []; - - if (gettype($new) != gettype($old)) { - // The type has changed. - $messages[] = "Property '$key' has changed type, from '" . gettype($old) . "' to '" . gettype($new) . - ($path != '' ? "' inside $path." : "'."); - - } else if ($new instanceof external_value && $new->type != $old->type) { - // The type has changed. - $messages[] = "Property '$key' has changed type, from '" . $old->type . "' to '" . $new->type . - ($path != '' ? "' inside $path." : "'."); - - } else if ($new instanceof external_warnings || $new instanceof external_files) { - // Ignore these types. - - } else if ($new instanceof external_single_structure) { - // Check each subproperty. - $newpath = ($path != '' ? "$path." : '') . $key; - - foreach ($new->keys as $subkey => $value) { - if (!isset($old->keys[$subkey])) { - // New property. - $messages[] = "New property '$subkey' found" . ($newpath != '' ? " inside '$newpath'." : '.'); - } else { - $messages = array_merge($messages, detect_ws_changes($value, $old->keys[$subkey], $subkey, $newpath)); - } - } - } else if ($new instanceof external_multiple_structure) { - // Recursive call with the content. - $messages = array_merge($messages, detect_ws_changes($new->content, $old->content, $key, $path)); - } - - return $messages; -} - -/** - * Remove all closures (anonymous functions) in the default values so the object can be serialized. - */ -function remove_default_closures($value) { - if ($value instanceof external_warnings || $value instanceof external_files) { - // Ignore these types. - - } else if ($value instanceof external_value) { - if ($value->default instanceof Closure) { - $value->default = null; - } - - } else if ($value instanceof external_single_structure) { - - foreach ($value->keys as $key => $subvalue) { - remove_default_closures($subvalue); - } - - } else if ($value instanceof external_multiple_structure) { - remove_default_closures($value->content); - } -} diff --git a/src/addon/badges/badges.module.ts b/src/addon/badges/badges.module.ts deleted file mode 100644 index a85fb49f5..000000000 --- a/src/addon/badges/badges.module.ts +++ /dev/null @@ -1,54 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonBadgesProvider } from './providers/badges'; -import { AddonBadgesUserHandler } from './providers/user-handler'; -import { AddonBadgesMyBadgesLinkHandler } from './providers/mybadges-link-handler'; -import { AddonBadgesBadgeLinkHandler } from './providers/badge-link-handler'; -import { AddonBadgesPushClickHandler } from './providers/push-click-handler'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreUserDelegate } from '@core/user/providers/user-delegate'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; - -// List of providers (without handlers). -export const ADDON_BADGES_PROVIDERS: any[] = [ - AddonBadgesProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - ], - providers: [ - AddonBadgesProvider, - AddonBadgesUserHandler, - AddonBadgesMyBadgesLinkHandler, - AddonBadgesBadgeLinkHandler, - AddonBadgesPushClickHandler - ] -}) -export class AddonBadgesModule { - constructor(userDelegate: CoreUserDelegate, userHandler: AddonBadgesUserHandler, - contentLinksDelegate: CoreContentLinksDelegate, myBadgesLinkHandler: AddonBadgesMyBadgesLinkHandler, - badgeLinkHandler: AddonBadgesBadgeLinkHandler, - pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonBadgesPushClickHandler) { - - userDelegate.registerHandler(userHandler); - contentLinksDelegate.registerHandler(myBadgesLinkHandler); - contentLinksDelegate.registerHandler(badgeLinkHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - } -} diff --git a/src/addon/badges/lang/en.json b/src/addon/badges/lang/en.json deleted file mode 100644 index 6c8a2b856..000000000 --- a/src/addon/badges/lang/en.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "alignment": "Alignment", - "badgedetails": "Badge details", - "badges": "Badges", - "bendorsement": "Endorsement", - "claimcomment": "Endorsement comment", - "claimid": "Claim URL", - "contact": "Contact", - "dateawarded": "Date issued", - "expired": "Expired", - "expirydate": "Expiry date", - "imageauthoremail": "Image author's email", - "imageauthorname": "Image author's name", - "imageauthorurl": "Image author's URL", - "imagecaption": "Image caption", - "issuancedetails": "Badge expiry", - "issuerdetails": "Issuer details", - "issueremail": "Email", - "issuername": "Issuer name", - "issuerurl": "Issuer URL", - "language": "Language", - "noalignment": "This badge does not have any external skills or standards specified.", - "nobadges": "There are no badges available.", - "norelated": "This badge does not have any related badges.", - "recipientdetails": "Recipient details", - "relatedbages": "Related badges", - "version": "Version", - "warnexpired": "(This badge has expired!)" -} \ No newline at end of file diff --git a/src/addon/badges/pages/issued-badge/issued-badge.html b/src/addon/badges/pages/issued-badge/issued-badge.html deleted file mode 100644 index ec4dc0e4f..000000000 --- a/src/addon/badges/pages/issued-badge/issued-badge.html +++ /dev/null @@ -1,179 +0,0 @@ - - - {{badge && badge.name}} - - - - - - - - - - - - - {{ 'addon.badges.expired' | translate }} - - - - - - -

{{ 'addon.badges.recipientdetails' | translate}}

-
- -

{{ 'core.name' | translate}}

-

{{ user.fullname }}

-
-
- - - -

{{ 'addon.badges.issuerdetails' | translate}}

-
- -

{{ 'addon.badges.issuername' | translate}}

-

{{ badge.issuername }}

-
- -

{{ 'addon.badges.contact' | translate}}

-

- {{ badge.issuercontact }} -

-
-
- - - -

{{ 'addon.badges.badgedetails' | translate}}

-
- -

{{ 'core.name' | translate}}

-

{{ badge.name }}

-
- -

{{ 'addon.badges.version' | translate}}

-

{{ badge.version }}

-
- -

{{ 'addon.badges.language' | translate}}

-

{{ badge.language }}

-
- -

{{ 'core.description' | translate}}

-

{{ badge.description }}

-
- -

{{ 'addon.badges.imageauthorname' | translate}}

-

{{ badge.imageauthorname }}

-
- -

{{ 'addon.badges.imageauthoremail' | translate}}

-

- {{ badge.imageauthoremail }} -

-
- -

{{ 'addon.badges.imageauthorurl' | translate}}

-

- {{ badge.imageauthorurl }} -

-
- -

{{ 'addon.badges.imagecaption' | translate}}

-

{{ badge.imagecaption }}

-
- -

{{ 'core.course' | translate}}

-

- -

-
- -
- - - -

{{ 'addon.badges.issuancedetails' | translate}}

-
- -

{{ 'addon.badges.dateawarded' | translate}}

-

{{badge.dateissued * 1000 | coreFormatDate }}

-
- -

{{ 'addon.badges.expirydate' | translate}}

-

- {{ badge.dateexpire * 1000 | coreFormatDate }} - - {{ 'addon.badges.warnexpired' | translate }} - -

-
- -
- - - - -

{{ 'addon.badges.bendorsement' | translate}}

-
- -

{{ 'addon.badges.issuername' | translate}}

-

{{ badge.endorsement.issuername }}

-
- -

{{ 'addon.badges.issueremail' | translate}}

-

- {{ badge.endorsement.issueremail }} -

-
- -

{{ 'addon.badges.issuerurl' | translate}}

-

- {{ badge.endorsement.issuerurl }} -

-
- -

{{ 'addon.badges.dateawarded' | translate}}

-

{{ badge.endorsement.dateissued * 1000 | coreFormatDate }}

-
- -

{{ 'addon.badges.claimid' | translate}}

-

- {{ badge.endorsement.claimid }} -

-
- -

{{ 'addon.badges.claimcomment' | translate}}

-

{{ badge.endorsement.claimcomment }}

-
-
- - - - -

{{ 'addon.badges.relatedbages' | translate}}

-
- -

{{ relatedBadge.name }}

-
- -

{{ 'addon.badges.norelated' | translate}}

-
-
- - - - -

{{ 'addon.badges.alignment' | translate}}

-
- -

{{ alignment.targetname }}

-
- -

{{ 'addon.badges.noalignment' | translate}}

-
-
-
-
diff --git a/src/addon/badges/pages/issued-badge/issued-badge.module.ts b/src/addon/badges/pages/issued-badge/issued-badge.module.ts deleted file mode 100644 index a44bd34ef..000000000 --- a/src/addon/badges/pages/issued-badge/issued-badge.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonBadgesIssuedBadgePage } from './issued-badge'; - -@NgModule({ - declarations: [ - AddonBadgesIssuedBadgePage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonBadgesIssuedBadgePage), - TranslateModule.forChild() - ], -}) -export class AddonBadgesIssuedBadgePageModule {} diff --git a/src/addon/badges/pages/issued-badge/issued-badge.ts b/src/addon/badges/pages/issued-badge/issued-badge.ts deleted file mode 100644 index 46977fd45..000000000 --- a/src/addon/badges/pages/issued-badge/issued-badge.ts +++ /dev/null @@ -1,113 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, Content, NavParams } from 'ionic-angular'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { AddonBadgesProvider, AddonBadgesUserBadge } from '../../providers/badges'; - -/** - * Page that displays the list of calendar events. - */ -@IonicPage({ segment: 'addon-badges-issued-badge' }) -@Component({ - selector: 'page-addon-badges-issued-badge', - templateUrl: 'issued-badge.html', -}) -export class AddonBadgesIssuedBadgePage { - @ViewChild(Content) content: Content; - - protected badgeHash: string; - protected userId: number; - protected courseId: number; - - user: any = {}; - course: any = {}; - badge: AddonBadgesUserBadge; - - badgeLoaded = false; - currentTime = 0; - - constructor(private badgesProvider: AddonBadgesProvider, navParams: NavParams, sitesProvider: CoreSitesProvider, - private domUtils: CoreDomUtilsProvider, private timeUtils: CoreTimeUtilsProvider, - private userProvider: CoreUserProvider, private coursesProvider: CoreCoursesProvider) { - - this.courseId = navParams.get('courseId') || 0; // Use 0 for site badges. - this.userId = navParams.get('userId') || sitesProvider.getCurrentSite().getUserId(); - this.badgeHash = navParams.get('badgeHash'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - - this.fetchIssuedBadge().finally(() => { - this.badgeLoaded = true; - }); - } - - /** - * Fetch the issued badge required for the view. - * - * @return Promise resolved when done. - */ - fetchIssuedBadge(): Promise { - const promises = []; - - this.currentTime = this.timeUtils.timestamp(); - promises.push(this.userProvider.getProfile(this.userId, this.courseId, true).then((user) => { - this.user = user; - })); - - promises.push(this.badgesProvider.getUserBadges(this.courseId, this.userId).then((badges) => { - const badge = badges.find((badge) => { - return this.badgeHash == badge.uniquehash; - }); - - if (badge) { - this.badge = badge; - if (badge.courseid) { - return this.coursesProvider.getUserCourse(badge.courseid, true).then((course) => { - this.course = course; - }).catch(() => { - // Maybe an old deleted course. - this.course = null; - }); - } - } - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error getting badge data.'); - })); - - return Promise.all(promises); - } - - /** - * Refresh the badges. - * - * @param refresher Refresher. - */ - refreshBadges(refresher: any): void { - this.badgesProvider.invalidateUserBadges(this.courseId, this.userId).finally(() => { - this.fetchIssuedBadge().finally(() => { - refresher.complete(); - }); - }); - } -} diff --git a/src/addon/badges/pages/user-badges/user-badges.html b/src/addon/badges/pages/user-badges/user-badges.html deleted file mode 100644 index 315e9af08..000000000 --- a/src/addon/badges/pages/user-badges/user-badges.html +++ /dev/null @@ -1,29 +0,0 @@ - - - {{ 'addon.badges.badges' | translate }} - - - - - - - - - - - - - - - - -

{{ badge.name }}

-

{{ badge.dateissued * 1000 | coreFormatDate :'strftimedatetimeshort' }}

- - {{ 'addon.badges.expired' | translate }} - -
-
-
-
-
\ No newline at end of file diff --git a/src/addon/badges/pages/user-badges/user-badges.module.ts b/src/addon/badges/pages/user-badges/user-badges.module.ts deleted file mode 100644 index 03c8ecbe8..000000000 --- a/src/addon/badges/pages/user-badges/user-badges.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonBadgesUserBadgesPage } from './user-badges'; - -@NgModule({ - declarations: [ - AddonBadgesUserBadgesPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonBadgesUserBadgesPage), - TranslateModule.forChild() - ], -}) -export class AddonBadgesUserBadgesPageModule {} diff --git a/src/addon/badges/pages/user-badges/user-badges.ts b/src/addon/badges/pages/user-badges/user-badges.ts deleted file mode 100644 index 70c7f04ed..000000000 --- a/src/addon/badges/pages/user-badges/user-badges.ts +++ /dev/null @@ -1,102 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, Content, NavParams } from 'ionic-angular'; -import { AddonBadgesProvider, AddonBadgesUserBadge } from '../../providers/badges'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; - -/** - * Page that displays the list of calendar events. - */ -@IonicPage({ segment: 'addon-badges-user-badges' }) -@Component({ - selector: 'page-addon-badges-user-badges', - templateUrl: 'user-badges.html', -}) -export class AddonBadgesUserBadgesPage { - @ViewChild(Content) content: Content; - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - courseId: number; - userId: number; - - badgesLoaded = false; - badges: AddonBadgesUserBadge[] = []; - currentTime = 0; - badgeHash: string; - - constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, private badgesProvider: AddonBadgesProvider, - private domUtils: CoreDomUtilsProvider, private timeUtils: CoreTimeUtilsProvider) { - - this.courseId = navParams.get('courseId') || 0; // Use 0 for site badges. - this.userId = navParams.get('userId') || sitesProvider.getCurrentSite().getUserId(); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - - this.fetchBadges().finally(() => { - if (!this.badgeHash && this.splitviewCtrl.isOn() && this.badges.length > 0) { - // Take first and load it. - this.loadIssuedBadge(this.badges[0].uniquehash); - } - this.badgesLoaded = true; - }); - } - - /** - * Fetch all the badges required for the view. - * - * @return Promise resolved when done. - */ - fetchBadges(): Promise { - this.currentTime = this.timeUtils.timestamp(); - - return this.badgesProvider.getUserBadges(this.courseId, this.userId).then((badges) => { - this.badges = badges; - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error getting badges data.'); - }); - } - - /** - * Refresh the badges. - * - * @param refresher Refresher. - */ - refreshBadges(refresher: any): void { - this.badgesProvider.invalidateUserBadges(this.courseId, this.userId).finally(() => { - this.fetchBadges().finally(() => { - refresher.complete(); - }); - }); - } - - /** - * Navigate to a particular badge. - * - * @param badgeHash Badge to load. - */ - loadIssuedBadge(badgeHash: string): void { - this.badgeHash = badgeHash; - const params = {courseId: this.courseId, userId: this.userId, badgeHash: badgeHash}; - this.splitviewCtrl.push('AddonBadgesIssuedBadgePage', params); - } -} diff --git a/src/addon/badges/providers/badge-link-handler.ts b/src/addon/badges/providers/badge-link-handler.ts deleted file mode 100644 index 21fd59ab7..000000000 --- a/src/addon/badges/providers/badge-link-handler.ts +++ /dev/null @@ -1,66 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonBadgesProvider } from './badges'; - -/** - * Handler to treat links to user participants page. - */ -@Injectable() -export class AddonBadgesBadgeLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonBadgesBadgeLinkHandler'; - pattern = /\/badges\/badge\.php.*([\?\&]hash=)/; - - constructor(private badgesProvider: AddonBadgesProvider, private linkHelper: CoreContentLinksHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - return [{ - action: (siteId, navCtrl?): void => { - this.linkHelper.goInSite(navCtrl, 'AddonBadgesIssuedBadgePage', {courseId: 0, badgeHash: params.hash}, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - - return this.badgesProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/badges/providers/badges.ts b/src/addon/badges/providers/badges.ts deleted file mode 100644 index bce298dc4..000000000 --- a/src/addon/badges/providers/badges.ts +++ /dev/null @@ -1,206 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreWSExternalWarning } from '@providers/ws'; -import { CoreSite } from '@classes/site'; - -/** - * Service to handle badges. - */ -@Injectable() -export class AddonBadgesProvider { - protected logger; - protected ROOT_CACHE_KEY = 'mmaBadges:'; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) { - this.logger = logger.getInstance('AddonBadgesProvider'); - } - - /** - * Returns whether or not the badge plugin is enabled for a certain site. - * - * This method is called quite often and thus should only perform a quick - * check, we should not be calling WS from here. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if enabled, false otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - if (!site.canUseAdvancedFeature('enablebadges')) { - return false; - } else if (!site.wsAvailable('core_course_get_user_navigation_options')) { - return false; - } - - return true; - }); - } - - /** - * Get the cache key for the get badges call. - * - * @param courseId ID of the course to get the badges from. - * @param userId ID of the user to get the badges from. - * @return Cache key. - */ - protected getBadgesCacheKey(courseId: number, userId: number): string { - return this.ROOT_CACHE_KEY + 'badges:' + courseId + ':' + userId; - } - - /** - * Get issued badges for a certain user in a course. - * - * @param courseId ID of the course to get the badges from. - * @param userId ID of the user to get the badges from. - * @param siteId Site ID. If not defined, current site. - * @return Promise to be resolved when the badges are retrieved. - */ - getUserBadges(courseId: number, userId: number, siteId?: string): Promise { - - this.logger.debug('Get badges for course ' + courseId); - - return this.sitesProvider.getSite(siteId).then((site) => { - - const data = { - courseid : courseId, - userid : userId - }, - preSets = { - cacheKey: this.getBadgesCacheKey(courseId, userId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - return site.read('core_badges_get_user_badges', data, preSets).then((response: AddonBadgesGetUserBadgesResult): any => { - if (response && response.badges) { - // In 3.7, competencies was renamed to alignment. Rename the property in 3.6 too. - response.badges.forEach((badge) => { - badge.alignment = badge.alignment || badge.competencies; - - // Check that the alignment is valid, they were broken in 3.7. - if (badge.alignment && badge.alignment[0] && typeof badge.alignment[0].targetname == 'undefined') { - // If any badge lacks targetname it means they are affected by the Moodle bug, don't display them. - delete badge.alignment; - } - }); - - return response.badges; - } else { - return Promise.reject(null); - } - }); - }); - } - - /** - * Invalidate get badges WS call. - * - * @param courseId Course ID. - * @param userId ID of the user to get the badges from. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateUserBadges(courseId: number, userId: number, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getBadgesCacheKey(courseId, userId)); - }); - } -} - -/** - * Result of WS core_badges_get_user_badges. - */ -export type AddonBadgesGetUserBadgesResult = { - badges: AddonBadgesUserBadge[]; // List of badges. - warnings?: CoreWSExternalWarning[]; // List of warnings. -}; - -/** - * Badge data returned by WS core_badges_get_user_badges. - */ -export type AddonBadgesUserBadge = { - id?: number; // Badge id. - name: string; // Badge name. - description: string; // Badge description. - timecreated?: number; // Time created. - timemodified?: number; // Time modified. - usercreated?: number; // User created. - usermodified?: number; // User modified. - issuername: string; // Issuer name. - issuerurl: string; // Issuer URL. - issuercontact: string; // Issuer contact. - expiredate?: number; // Expire date. - expireperiod?: number; // Expire period. - type?: number; // Type. - courseid?: number; // Course id. - message?: string; // Message. - messagesubject?: string; // Message subject. - attachment?: number; // Attachment. - notification?: number; // @since 3.6. Whether to notify when badge is awarded. - nextcron?: number; // @since 3.6. Next cron. - status?: number; // Status. - issuedid?: number; // Issued id. - uniquehash: string; // Unique hash. - dateissued: number; // Date issued. - dateexpire: number; // Date expire. - visible?: number; // Visible. - email?: string; // @since 3.6. User email. - version?: string; // @since 3.6. Version. - language?: string; // @since 3.6. Language. - imageauthorname?: string; // @since 3.6. Name of the image author. - imageauthoremail?: string; // @since 3.6. Email of the image author. - imageauthorurl?: string; // @since 3.6. URL of the image author. - imagecaption?: string; // @since 3.6. Caption of the image. - badgeurl: string; // Badge URL. - endorsement?: { // @since 3.6. - id: number; // Endorsement id. - badgeid: number; // Badge id. - issuername: string; // Endorsement issuer name. - issuerurl: string; // Endorsement issuer URL. - issueremail: string; // Endorsement issuer email. - claimid: string; // Claim URL. - claimcomment: string; // Claim comment. - dateissued: number; // Date issued. - }; - alignment?: { // @since 3.7. Calculated by the app for 3.6 sites. Badge alignments. - id?: number; // Alignment id. - badgeid?: number; // Badge id. - targetname?: string; // Target name. - targeturl?: string; // Target URL. - targetdescription?: string; // Target description. - targetframework?: string; // Target framework. - targetcode?: string; // Target code. - }[]; - competencies?: { // @deprecated from 3.7. @since 3.6. In 3.7 it was renamed to alignment. - id?: number; // Alignment id. - badgeid?: number; // Badge id. - targetname?: string; // Target name. - targeturl?: string; // Target URL. - targetdescription?: string; // Target description. - targetframework?: string; // Target framework. - targetcode?: string; // Target code. - }[]; - relatedbadges?: { // @since 3.6. Related badges. - id: number; // Badge id. - name: string; // Badge name. - version?: string; // Version. - language?: string; // Language. - type?: number; // Type. - }[]; -}; diff --git a/src/addon/badges/providers/mybadges-link-handler.ts b/src/addon/badges/providers/mybadges-link-handler.ts deleted file mode 100644 index 7a3db5562..000000000 --- a/src/addon/badges/providers/mybadges-link-handler.ts +++ /dev/null @@ -1,67 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonBadgesProvider } from './badges'; - -/** - * Handler to treat links to user badges page. - */ -@Injectable() -export class AddonBadgesMyBadgesLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonBadgesMyBadgesLinkHandler'; - featureName = 'CoreUserDelegate_AddonBadges'; - pattern = /\/badges\/mybadges\.php/; - - constructor(private badgesProvider: AddonBadgesProvider, private linkHelper: CoreContentLinksHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - return [{ - action: (siteId, navCtrl?): void => { - this.linkHelper.goInSite(navCtrl, 'AddonBadgesUserBadgesPage', {}, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - - return this.badgesProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/badges/providers/push-click-handler.ts b/src/addon/badges/providers/push-click-handler.ts deleted file mode 100644 index 816a5cc47..000000000 --- a/src/addon/badges/providers/push-click-handler.ts +++ /dev/null @@ -1,71 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreLoginHelperProvider } from '@core/login/providers/helper'; -import { AddonBadgesProvider } from './badges'; - -/** - * Handler for badges push notifications clicks. - */ -@Injectable() -export class AddonBadgesPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonBadgesPushClickHandler'; - priority = 200; - featureName = 'CoreUserDelegate_AddonBadges'; - - constructor(private utils: CoreUtilsProvider, private badgesProvider: AddonBadgesProvider, - private loginHelper: CoreLoginHelperProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - const data = notification.customdata || {}; - - if (this.utils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'moodle' && - (notification.name == 'badgerecipientnotice' || (notification.name == 'badgecreatornotice' && data.hash))) { - return this.badgesProvider.isPluginEnabled(notification.site); - } - - return false; - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - handleClick(notification: any): Promise { - const data = notification.customdata || {}; - - if (data.hash) { - // We have the hash, open the badge directly. - return this.loginHelper.redirect('AddonBadgesIssuedBadgePage', {courseId: 0, badgeHash: data.hash}, notification.site); - } - - // No hash, open the list of user badges. - return this.badgesProvider.invalidateUserBadges(0, Number(notification.usertoid), notification.site).catch(() => { - // Ignore errors. - }).then(() => { - return this.loginHelper.redirect('AddonBadgesUserBadgesPage', {}, notification.site); - }); - } -} diff --git a/src/addon/badges/providers/user-handler.ts b/src/addon/badges/providers/user-handler.ts deleted file mode 100644 index 22c7daa58..000000000 --- a/src/addon/badges/providers/user-handler.ts +++ /dev/null @@ -1,75 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; -import { AddonBadgesProvider } from './badges'; - -/** - * Profile badges handler. - */ -@Injectable() -export class AddonBadgesUserHandler implements CoreUserProfileHandler { - name = 'AddonBadges'; - priority = 50; - type = CoreUserDelegate.TYPE_NEW_PAGE; - - constructor(protected badgesProvider: AddonBadgesProvider) { } - - /** - * Check if handler is enabled. - * - * @return Always enabled. - */ - isEnabled(): Promise { - return this.badgesProvider.isPluginEnabled(); - } - - /** - * Check if handler is enabled for this user in this context. - * - * @param user User to check. - * @param courseId Course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return True if enabled, false otherwise. - */ - isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean { - - if (navOptions && typeof navOptions.badges != 'undefined') { - return navOptions.badges; - } - - // If we reach here, it means we are opening the user site profile. - return true; - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { - return { - icon: 'trophy', - title: 'addon.badges.badges', - class: '', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - navCtrl.push('AddonBadgesUserBadgesPage', {courseId: courseId, userId: user.id }); - } - }; - } -} diff --git a/src/addon/block/activitymodules/activitymodules.module.ts b/src/addon/block/activitymodules/activitymodules.module.ts deleted file mode 100644 index 0a05e1cdc..000000000 --- a/src/addon/block/activitymodules/activitymodules.module.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonBlockActivityModulesComponentsModule } from './components/components.module'; -import { CoreBlockDelegate } from '@core/block/providers/delegate'; -import { AddonBlockActivityModulesHandler } from './providers/block-handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule, - CoreComponentsModule, - CoreDirectivesModule, - AddonBlockActivityModulesComponentsModule, - TranslateModule.forChild() - ], - exports: [ - ], - providers: [ - AddonBlockActivityModulesHandler - ] -}) -export class AddonBlockActivityModulesModule { - constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockActivityModulesHandler) { - blockDelegate.registerHandler(blockHandler); - } -} diff --git a/src/addon/block/activitymodules/components/activitymodules/activitymodules.scss b/src/addon/block/activitymodules/components/activitymodules/activitymodules.scss deleted file mode 100644 index 83a108a27..000000000 --- a/src/addon/block/activitymodules/components/activitymodules/activitymodules.scss +++ /dev/null @@ -1,17 +0,0 @@ -ion-app.app-root.md addon-block-activitymodules { - .core-module-icon { - margin-top: $label-md-margin-top; - margin-bottom: $label-md-margin-bottom; - width: 24px; - height: 24px; - } -} - -ion-app.app-root.ios addon-block-activitymodules { - .core-module-icon { - margin-top: $label-ios-margin-top; - margin-bottom: $label-ios-margin-bottom; - width: 24px; - height: 24px; - } -} diff --git a/src/addon/block/activitymodules/components/activitymodules/activitymodules.ts b/src/addon/block/activitymodules/components/activitymodules/activitymodules.ts deleted file mode 100644 index b46a9b366..000000000 --- a/src/addon/block/activitymodules/components/activitymodules/activitymodules.ts +++ /dev/null @@ -1,140 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector, Input } from '@angular/core'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component'; -import { CoreConstants, ContextLevel } from '@core/constants'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; - -/** - * Component to render an "activity modules" block. - */ -@Component({ - selector: 'addon-block-activitymodules', - templateUrl: 'addon-block-activitymodules.html' -}) -export class AddonBlockActivityModulesComponent extends CoreBlockBaseComponent implements OnInit { - @Input() block: any; // The block to render. - @Input() contextLevel: ContextLevel; // The context where the block will be used. - @Input() instanceId: number; // The instance ID associated with the context level. - - entries: any[] = []; - - protected fetchContentDefaultError = 'Error getting activity modules data.'; - - constructor(injector: Injector, protected courseProvider: CoreCourseProvider, - protected translate: TranslateService, protected moduleDelegate: CoreCourseModuleDelegate, - protected sitesProvider: CoreSitesProvider) { - - super(injector, 'AddonBlockActivityModulesComponent'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return this.courseProvider.invalidateSections(this.instanceId); - } - - /** - * Fetch the data to render the block. - * - * @return Promise resolved when done. - */ - protected fetchContent(): Promise { - return this.courseProvider.getSections(this.getCourseId(), false, true).then((sections) => { - this.entries = []; - - const archetypes = {}, - modIcons = {}; - let modFullNames = {}; - - sections.forEach((section) => { - if (!section.modules) { - return; - } - - section.modules.forEach((mod) => { - if (mod.uservisible === false || !this.courseProvider.moduleHasView(mod) || - typeof modFullNames[mod.modname] != 'undefined') { - // Ignore this module. - return; - } - - // Get the archetype of the module type. - if (typeof archetypes[mod.modname] == 'undefined') { - archetypes[mod.modname] = this.moduleDelegate.supportsFeature(mod.modname, - CoreConstants.FEATURE_MOD_ARCHETYPE, CoreConstants.MOD_ARCHETYPE_OTHER); - } - - // Get the full name of the module type. - if (archetypes[mod.modname] == CoreConstants.MOD_ARCHETYPE_RESOURCE) { - // All resources are gathered in a single "Resources" option. - if (!modFullNames['resources']) { - modFullNames['resources'] = this.translate.instant('core.resources'); - } - } else { - modFullNames[mod.modname] = mod.modplural; - } - modIcons[mod.modname] = mod.modicon; - }); - }); - - // Sort the modnames alphabetically. - modFullNames = this.utils.sortValues(modFullNames); - - for (const modName in modFullNames) { - let icon; - - if (modName === 'resources') { - icon = this.courseProvider.getModuleIconSrc('page', modIcons['page']); - } else { - icon = this.moduleDelegate.getModuleIconSrc(modName, modIcons[modName]); - } - - this.entries.push({ - icon: icon, - name: modFullNames[modName], - modName: modName - }); - } - }); - } - - /** - * Obtain the appropiate course id for the block. - * - * @return Course id. - */ - protected getCourseId(): number { - switch (this.contextLevel) { - case ContextLevel.COURSE: - return this.instanceId; - default: - return this.sitesProvider.getCurrentSiteHomeId(); - } - } -} diff --git a/src/addon/block/activitymodules/components/activitymodules/addon-block-activitymodules.html b/src/addon/block/activitymodules/components/activitymodules/addon-block-activitymodules.html deleted file mode 100644 index fc407eb4b..000000000 --- a/src/addon/block/activitymodules/components/activitymodules/addon-block-activitymodules.html +++ /dev/null @@ -1,9 +0,0 @@ - -

{{ 'addon.block_activitymodules.pluginname' | translate }}

-
- - - - {{ entry.name }} - - diff --git a/src/addon/block/activitymodules/components/components.module.ts b/src/addon/block/activitymodules/components/components.module.ts deleted file mode 100644 index a5d1651b3..000000000 --- a/src/addon/block/activitymodules/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonBlockActivityModulesComponent } from './activitymodules/activitymodules'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; - -@NgModule({ - declarations: [ - AddonBlockActivityModulesComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonBlockActivityModulesComponent - ], - entryComponents: [ - AddonBlockActivityModulesComponent - ] -}) -export class AddonBlockActivityModulesComponentsModule {} diff --git a/src/addon/block/activitymodules/lang/en.json b/src/addon/block/activitymodules/lang/en.json deleted file mode 100644 index 7f1c7ab21..000000000 --- a/src/addon/block/activitymodules/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Activities" -} diff --git a/src/addon/block/activitymodules/providers/block-handler.ts b/src/addon/block/activitymodules/providers/block-handler.ts deleted file mode 100644 index 53310d351..000000000 --- a/src/addon/block/activitymodules/providers/block-handler.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreBlockHandlerData } from '@core/block/providers/delegate'; -import { AddonBlockActivityModulesComponent } from '../components/activitymodules/activitymodules'; -import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler'; - -/** - * Block handler. - */ -@Injectable() -export class AddonBlockActivityModulesHandler extends CoreBlockBaseHandler { - name = 'AddonBlockActivityModules'; - blockName = 'activity_modules'; - - constructor() { - super(); - } - - /** - * Returns the data needed to render the block. - * - * @param injector Injector. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_activitymodules.pluginname', - class: 'addon-block-activitymodules', - component: AddonBlockActivityModulesComponent - }; - } -} diff --git a/src/addon/block/activityresults/activityresults.module.ts b/src/addon/block/activityresults/activityresults.module.ts deleted file mode 100644 index 3c0ee74d3..000000000 --- a/src/addon/block/activityresults/activityresults.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreBlockDelegate } from '@core/block/providers/delegate'; -import { AddonBlockActivityResultsHandler } from './providers/block-handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule, - TranslateModule.forChild() - ], - exports: [ - ], - providers: [ - AddonBlockActivityResultsHandler - ] -}) -export class AddonBlockActivityResultsModule { - constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockActivityResultsHandler) { - blockDelegate.registerHandler(blockHandler); - } -} diff --git a/src/addon/block/activityresults/activityresults.scss b/src/addon/block/activityresults/activityresults.scss deleted file mode 100644 index b76cb85a9..000000000 --- a/src/addon/block/activityresults/activityresults.scss +++ /dev/null @@ -1,31 +0,0 @@ -.addon-block-activity-results core-block-pre-rendered { - ion-item.core-block-content { - table.grades { - @include text-align('start'); - width: 100%; - - .number { - @include text-align('start'); - width: 10%; - } - - .name { - @include text-align('start'); - width: 77%; - } - - .grade { - @include text-align('end'); - } - - caption { - @include text-align('start'); - padding-top: .75rem; - padding-bottom: .75rem; - color: $gray-darker; - font-weight: bold; - font-size: 18px; - } - } - } -} \ No newline at end of file diff --git a/src/addon/block/activityresults/lang/en.json b/src/addon/block/activityresults/lang/en.json deleted file mode 100644 index 4dd6abbb7..000000000 --- a/src/addon/block/activityresults/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Activity results" -} \ No newline at end of file diff --git a/src/addon/block/activityresults/providers/block-handler.ts b/src/addon/block/activityresults/providers/block-handler.ts deleted file mode 100644 index 8553cb3a4..000000000 --- a/src/addon/block/activityresults/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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 AddonBlockActivityResultsHandler extends CoreBlockBaseHandler { - name = 'AddonBlockActivityResults'; - blockName = 'activity_results'; - - constructor() { - super(); - } - - /** - * Returns the data needed to render the block. - * - * @param injector Injector. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_activityresults.pluginname', - class: 'addon-block-activity-results', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/badges/badges.module.ts b/src/addon/block/badges/badges.module.ts deleted file mode 100644 index 7b4a5ad38..000000000 --- a/src/addon/block/badges/badges.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/badges/badges.scss b/src/addon/block/badges/badges.scss deleted file mode 100644 index 3f9d26d8b..000000000 --- a/src/addon/block/badges/badges.scss +++ /dev/null @@ -1,23 +0,0 @@ -.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; - } - } - } - } -} \ No newline at end of file diff --git a/src/addon/block/badges/lang/en.json b/src/addon/block/badges/lang/en.json deleted file mode 100644 index dd957321f..000000000 --- a/src/addon/block/badges/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Latest badges" -} \ No newline at end of file diff --git a/src/addon/block/badges/providers/block-handler.ts b/src/addon/block/badges/providers/block-handler.ts deleted file mode 100644 index defc559c6..000000000 --- a/src/addon/block/badges/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_badges.pluginname', - class: 'addon-block-badges', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/blogmenu/blogmenu.module.ts b/src/addon/block/blogmenu/blogmenu.module.ts deleted file mode 100644 index 6c3b51dc8..000000000 --- a/src/addon/block/blogmenu/blogmenu.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/blogmenu/blogmenu.scss b/src/addon/block/blogmenu/blogmenu.scss deleted file mode 100644 index c649986ed..000000000 --- a/src/addon/block/blogmenu/blogmenu.scss +++ /dev/null @@ -1,16 +0,0 @@ -.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; - } -} \ No newline at end of file diff --git a/src/addon/block/blogmenu/lang/en.json b/src/addon/block/blogmenu/lang/en.json deleted file mode 100644 index 23541f7a0..000000000 --- a/src/addon/block/blogmenu/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Blog menu" -} \ No newline at end of file diff --git a/src/addon/block/blogmenu/providers/block-handler.ts b/src/addon/block/blogmenu/providers/block-handler.ts deleted file mode 100644 index b8d445b0f..000000000 --- a/src/addon/block/blogmenu/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_blogmenu.pluginname', - class: 'addon-block-blog-menu', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/blogrecent/blogrecent.module.ts b/src/addon/block/blogrecent/blogrecent.module.ts deleted file mode 100644 index 0f09c7e4b..000000000 --- a/src/addon/block/blogrecent/blogrecent.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/blogrecent/blogrecent.scss b/src/addon/block/blogrecent/blogrecent.scss deleted file mode 100644 index 81a03b227..000000000 --- a/src/addon/block/blogrecent/blogrecent.scss +++ /dev/null @@ -1,13 +0,0 @@ -.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; - } - } - } -} \ No newline at end of file diff --git a/src/addon/block/blogrecent/lang/en.json b/src/addon/block/blogrecent/lang/en.json deleted file mode 100644 index a92c0cce5..000000000 --- a/src/addon/block/blogrecent/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Recent blog entries" -} \ No newline at end of file diff --git a/src/addon/block/blogrecent/providers/block-handler.ts b/src/addon/block/blogrecent/providers/block-handler.ts deleted file mode 100644 index 7c3887c5b..000000000 --- a/src/addon/block/blogrecent/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_blogrecent.pluginname', - class: 'addon-block-blog-recent', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/blogtags/blogtags.module.ts b/src/addon/block/blogtags/blogtags.module.ts deleted file mode 100644 index 7a36e6f4e..000000000 --- a/src/addon/block/blogtags/blogtags.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/blogtags/blogtags.scss b/src/addon/block/blogtags/blogtags.scss deleted file mode 100644 index ef3a6a4c8..000000000 --- a/src/addon/block/blogtags/blogtags.scss +++ /dev/null @@ -1,89 +0,0 @@ -.addon-block-blog-tags core-block-pre-rendered { - .core-block-content { - ul.inline-list { - font-size: 80%; - 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; - } - } - } - } -} \ No newline at end of file diff --git a/src/addon/block/blogtags/lang/en.json b/src/addon/block/blogtags/lang/en.json deleted file mode 100644 index 683c3aa90..000000000 --- a/src/addon/block/blogtags/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Blog tags" -} \ No newline at end of file diff --git a/src/addon/block/blogtags/providers/block-handler.ts b/src/addon/block/blogtags/providers/block-handler.ts deleted file mode 100644 index 0c2becf40..000000000 --- a/src/addon/block/blogtags/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_blogtags.pluginname', - class: 'addon-block-blog-tags', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/calendarmonth/calendarmonth.module.ts b/src/addon/block/calendarmonth/calendarmonth.module.ts deleted file mode 100644 index 8388cc464..000000000 --- a/src/addon/block/calendarmonth/calendarmonth.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/calendarmonth/lang/en.json b/src/addon/block/calendarmonth/lang/en.json deleted file mode 100644 index 86a476c29..000000000 --- a/src/addon/block/calendarmonth/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Calendar" -} \ No newline at end of file diff --git a/src/addon/block/calendarmonth/providers/block-handler.ts b/src/addon/block/calendarmonth/providers/block-handler.ts deleted file mode 100644 index 1f4c46084..000000000 --- a/src/addon/block/calendarmonth/providers/block-handler.ts +++ /dev/null @@ -1,60 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - 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 - }; - } -} diff --git a/src/addon/block/calendarupcoming/calendarupcoming.module.ts b/src/addon/block/calendarupcoming/calendarupcoming.module.ts deleted file mode 100644 index cc9d804f7..000000000 --- a/src/addon/block/calendarupcoming/calendarupcoming.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/calendarupcoming/lang/en.json b/src/addon/block/calendarupcoming/lang/en.json deleted file mode 100644 index 4d73d8f67..000000000 --- a/src/addon/block/calendarupcoming/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Upcoming events" -} \ No newline at end of file diff --git a/src/addon/block/calendarupcoming/providers/block-handler.ts b/src/addon/block/calendarupcoming/providers/block-handler.ts deleted file mode 100644 index 4adc5dfb5..000000000 --- a/src/addon/block/calendarupcoming/providers/block-handler.ts +++ /dev/null @@ -1,61 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - 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 - }; - } -} diff --git a/src/addon/block/comments/comments.module.ts b/src/addon/block/comments/comments.module.ts deleted file mode 100644 index ab09d9ac1..000000000 --- a/src/addon/block/comments/comments.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/comments/lang/en.json b/src/addon/block/comments/lang/en.json deleted file mode 100644 index adcbcabae..000000000 --- a/src/addon/block/comments/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Comments" -} \ No newline at end of file diff --git a/src/addon/block/comments/providers/block-handler.ts b/src/addon/block/comments/providers/block-handler.ts deleted file mode 100644 index 526cc9d79..000000000 --- a/src/addon/block/comments/providers/block-handler.ts +++ /dev/null @@ -1,53 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - 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 } - }; - } -} diff --git a/src/addon/block/completionstatus/completionstatus.module.ts b/src/addon/block/completionstatus/completionstatus.module.ts deleted file mode 100644 index c84115d9d..000000000 --- a/src/addon/block/completionstatus/completionstatus.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/completionstatus/lang/en.json b/src/addon/block/completionstatus/lang/en.json deleted file mode 100644 index fe57356da..000000000 --- a/src/addon/block/completionstatus/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Course completion status" -} \ No newline at end of file diff --git a/src/addon/block/completionstatus/providers/block-handler.ts b/src/addon/block/completionstatus/providers/block-handler.ts deleted file mode 100644 index d64fee217..000000000 --- a/src/addon/block/completionstatus/providers/block-handler.ts +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_completionstatus.pluginname', - class: 'addon-block-completion-status', - component: CoreBlockOnlyTitleComponent, - link: 'AddonCourseCompletionReportPage', - linkParams: { courseId: instanceId } - }; - } -} diff --git a/src/addon/block/glossaryrandom/glossaryrandom.module.ts b/src/addon/block/glossaryrandom/glossaryrandom.module.ts deleted file mode 100644 index df316f008..000000000 --- a/src/addon/block/glossaryrandom/glossaryrandom.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/glossaryrandom/lang/en.json b/src/addon/block/glossaryrandom/lang/en.json deleted file mode 100644 index 1ae4de38c..000000000 --- a/src/addon/block/glossaryrandom/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Random glossary entry" -} \ No newline at end of file diff --git a/src/addon/block/glossaryrandom/providers/block-handler.ts b/src/addon/block/glossaryrandom/providers/block-handler.ts deleted file mode 100644 index 259b4616b..000000000 --- a/src/addon/block/glossaryrandom/providers/block-handler.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - return { - title: block.contents.title || 'addon.block_glossaryrandom.pluginname', - class: 'addon-block-glossary-random', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/html/html.module.ts b/src/addon/block/html/html.module.ts deleted file mode 100644 index 18cd3925d..000000000 --- a/src/addon/block/html/html.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/html/providers/block-handler.ts b/src/addon/block/html/providers/block-handler.ts deleted file mode 100644 index 5b5dda4f0..000000000 --- a/src/addon/block/html/providers/block-handler.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: block.contents.title, - class: 'addon-block-html', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/learningplans/lang/en.json b/src/addon/block/learningplans/lang/en.json deleted file mode 100644 index 0a7f81e22..000000000 --- a/src/addon/block/learningplans/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Learning plans" -} \ No newline at end of file diff --git a/src/addon/block/learningplans/learningplans.module.ts b/src/addon/block/learningplans/learningplans.module.ts deleted file mode 100644 index 936aa8edb..000000000 --- a/src/addon/block/learningplans/learningplans.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/learningplans/providers/block-handler.ts b/src/addon/block/learningplans/providers/block-handler.ts deleted file mode 100644 index cc1396a78..000000000 --- a/src/addon/block/learningplans/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_learningplans.pluginname', - class: 'addon-block-learning-plans', - component: CoreBlockOnlyTitleComponent, - link: 'AddonCompetencyPlanListPage' - }; - } -} diff --git a/src/addon/block/myoverview/components/components.module.ts b/src/addon/block/myoverview/components/components.module.ts deleted file mode 100644 index d345b508c..000000000 --- a/src/addon/block/myoverview/components/components.module.ts +++ /dev/null @@ -1,47 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreCoursesComponentsModule } from '@core/courses/components/components.module'; -import { AddonBlockMyOverviewComponent } from './myoverview/myoverview'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; - -@NgModule({ - declarations: [ - AddonBlockMyOverviewComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCoursesComponentsModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonBlockMyOverviewComponent - ], - entryComponents: [ - AddonBlockMyOverviewComponent - ] -}) -export class AddonBlockMyOverviewComponentsModule {} diff --git a/src/addon/block/myoverview/components/myoverview/addon-block-myoverview.html b/src/addon/block/myoverview/components/myoverview/addon-block-myoverview.html deleted file mode 100644 index 1cb51f1e2..000000000 --- a/src/addon/block/myoverview/components/myoverview/addon-block-myoverview.html +++ /dev/null @@ -1,51 +0,0 @@ - -

{{ 'addon.block_myoverview.pluginname' | translate }}

- -
- - {{prefetchCoursesData[selectedFilter].badge}} - -
- - - - - - -
- -
- - - {{ 'addon.block_myoverview.allincludinghidden' | translate }} - {{ 'addon.block_myoverview.all' | translate }} - {{ 'addon.block_myoverview.inprogress' | translate }} - {{ 'addon.block_myoverview.future' | translate }} - {{ 'addon.block_myoverview.past' | translate }} - - - {{ customOption.name }} - - - {{ 'addon.block_myoverview.favourites' | translate }} - {{ 'addon.block_myoverview.hiddencourses' | translate }} - -
- - - - - - -
- - - - - - - -
-
diff --git a/src/addon/block/myoverview/components/myoverview/myoverview.ts b/src/addon/block/myoverview/components/myoverview/myoverview.ts deleted file mode 100644 index 7fd24084c..000000000 --- a/src/addon/block/myoverview/components/myoverview/myoverview.ts +++ /dev/null @@ -1,523 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Input, OnDestroy, ViewChild, Injector, OnChanges, SimpleChange } from '@angular/core'; -import { Searchbar } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData } from '@core/courses/providers/courses'; -import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; -import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers/coursecompletion'; -import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component'; - -/** - * Component to render a my overview block. - */ -@Component({ - selector: 'addon-block-myoverview', - templateUrl: 'addon-block-myoverview.html' -}) -export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implements OnInit, OnChanges, OnDestroy { - @ViewChild('searchbar') searchbar: Searchbar; - @Input() downloadEnabled: boolean; - - courses = { - filter: '', - all: [], - allincludinghidden: [], - past: [], - inprogress: [], - future: [], - favourite: [], - hidden: [], - custom: [], // Leave it empty to avoid download all those courses. - }; - customFilter: any[] = []; - selectedFilter = 'inprogress'; - sort = 'fullname'; - currentSite: any; - filteredCourses: any[] = []; - prefetchCoursesData = { - all: {}, - allincludinghidden: {}, - inprogress: {}, - past: {}, - future: {}, - favourite: {}, - hidden: {}, - custom: {}, // Leave it empty to avoid download all those courses. - }; - showFilters = { // Options are show, disabled, hidden. - all: 'show', - allincludinghidden: 'show', - past: 'show', - inprogress: 'show', - future: 'show', - favourite: 'show', - hidden: 'show', - custom: 'hidden', - }; - showFilter = false; - showSelectorFilter = false; - showSortFilter = false; - downloadCourseEnabled: boolean; - downloadCoursesEnabled: boolean; - - protected FILTER_PRIORITY = ['all', 'allincludinghidden', 'inprogress', 'future', 'past', 'favourite', 'hidden', 'custom']; - protected prefetchIconsInitialized = false; - protected isDestroyed; - protected coursesObserver; - protected updateSiteObserver; - protected courseIds = []; - protected fetchContentDefaultError = 'Error getting my overview data.'; - protected showSortByShortName = false; - - constructor(injector: Injector, - protected coursesProvider: CoreCoursesProvider, - protected courseCompletionProvider: AddonCourseCompletionProvider, - protected eventsProvider: CoreEventsProvider, - protected courseHelper: CoreCourseHelperProvider, - protected courseOptionsDelegate: CoreCourseOptionsDelegate, - protected coursesHelper: CoreCoursesHelperProvider, - protected sitesProvider: CoreSitesProvider, - protected timeUtils: CoreTimeUtilsProvider) { - - super(injector, 'AddonBlockMyOverviewComponent'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Refresh the enabled flags if enabled. - this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite(); - this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); - - // Refresh the enabled flags if site is updated. - this.updateSiteObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => { - this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite(); - this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); - - }, this.sitesProvider.getCurrentSiteId()); - - this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, - (data: CoreCoursesMyCoursesUpdatedEventData) => { - - if (data.action == CoreCoursesProvider.ACTION_ENROL || data.action == CoreCoursesProvider.ACTION_STATE_CHANGED) { - this.refreshCourseList(); - } - }, this.sitesProvider.getCurrentSiteId()); - - this.currentSite = this.sitesProvider.getCurrentSite(); - - const promises = []; - promises.push(this.currentSite.getLocalSiteConfig('AddonBlockMyOverviewSort', this.sort).then((value) => { - this.sort = value; - })); - promises.push(this.currentSite.getLocalSiteConfig('AddonBlockMyOverviewFilter', this.selectedFilter).then((value) => { - this.selectedFilter = value; - })); - - Promise.all(promises).finally(() => { - 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. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.coursesProvider.invalidateUserCourses().finally(() => { - // Invalidate course completion data. - return this.utils.allPromises(this.courseIds.map((courseId) => { - return this.courseCompletionProvider.invalidateCourseCompletion(courseId); - })); - })); - - promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); - if (this.courseIds.length > 0) { - promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds.join(','))); - } - - return this.utils.allPromises(promises).finally(() => { - this.prefetchIconsInitialized = false; - }); - } - - /** - * Fetch the courses for my overview. - * - * @return Promise resolved when done. - */ - protected fetchContent(): Promise { - const config = this.block.configs; - - const showCategories = config && config.displaycategories && config.displaycategories.value == '1'; - - return this.coursesHelper.getUserCoursesWithOptions(this.sort, null, null, showCategories).then((courses) => { - // Check to show sort by short name only if the text is visible. - if (courses.length > 0) { - const sampleCourse = courses[0]; - this.showSortByShortName = sampleCourse.displayname && sampleCourse.shortname && - sampleCourse.fullname != sampleCourse.displayname; - } - - // Rollback to sort by full name if user is sorting by short name then Moodle web change the config. - if (!this.showSortByShortName && this.sort === 'shortname') { - this.switchSort('fullname'); - } - - this.courseIds = courses.map((course) => { - return course.id; - }); - - this.showSortFilter = courses.length > 0 && typeof courses[0].lastaccess != 'undefined'; - - this.initCourseFilters(courses); - - this.courses.filter = ''; - this.showFilter = false; - - this.showFilters.all = this.getShowFilterValue(!config || config.displaygroupingall.value == '1', - this.courses.all.length === 0); - // Do not show allincludinghiddenif config it's not present (before 3.8). - this.showFilters.allincludinghidden = - this.getShowFilterValue(config && config.displaygroupingallincludinghidden.value == '1', - this.courses.allincludinghidden.length === 0); - - this.showFilters.inprogress = this.getShowFilterValue(!config || config.displaygroupinginprogress.value == '1', - this.courses.inprogress.length === 0); - this.showFilters.past = this.getShowFilterValue(!config || config.displaygroupingpast.value == '1', - this.courses.past.length === 0); - this.showFilters.future = this.getShowFilterValue(!config || config.displaygroupingfuture.value == '1', - 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'); - - this.showFilters.hidden = this.getShowFilterValue( - this.showSelectorFilter && typeof courses[0].hidden != 'undefined' && - (!config || config.displaygroupinghidden.value == '1'), - this.courses.hidden.length === 0); - - this.showFilters.favourite = this.getShowFilterValue( - this.showSelectorFilter && typeof courses[0].isfavourite != 'undefined' && - (!config || (config.displaygroupingstarred && config.displaygroupingstarred.value == '1') || - (config.displaygroupingfavourites && config.displaygroupingfavourites.value == '1')), - this.courses.favourite.length === 0); - - this.showFilters.custom = this.getShowFilterValue(this.showSelectorFilter && config && - config.displaygroupingcustomfield.value == '1' && config.customfieldsexport && config.customfieldsexport.value, - false); - if (this.showFilters.custom == 'show') { - this.customFilter = this.textUtils.parseJSON(config.customfieldsexport.value); - } else { - this.customFilter = []; - } - - if (this.showSelectorFilter) { - // Check if any selector is shown and not disabled. - this.showSelectorFilter = Object.keys(this.showFilters).some((key) => { - return this.showFilters[key] == 'show'; - }); - - if (!this.showSelectorFilter) { - // All filters disabled, display all the courses. - this.showFilters.all = 'show'; - } - } - - if (!this.showSelectorFilter) { - // No selector, display all the courses. - this.selectedFilter = 'all'; - } - this.setCourseFilter(this.selectedFilter); - - this.initPrefetchCoursesIcons(); - }); - } - - /** - * Helper function to help with filter values. - * - * @param showCondition If true, filter will be shown. - * @param disabledCondition If true, and showCondition is also met, it will be shown as disabled. - * @return show / disabled / hidden value. - */ - protected getShowFilterValue(showCondition: boolean, disabledCondition: boolean): string { - return showCondition ? (disabledCondition ? 'disabled' : 'show') : 'hidden'; - } - - /** - * The filter has changed. - * - * @param Received Event. - */ - filterChanged(event: any): void { - const newValue = event.target.value && event.target.value.trim().toLowerCase(); - if (!newValue || this.courses.allincludinghidden.length <= 0) { - this.filteredCourses = this.courses.allincludinghidden; - } else { - // Use displayname if avalaible, or fullname if not. - if (this.courses.allincludinghidden.length > 0 && - typeof this.courses.allincludinghidden[0].displayname != 'undefined') { - this.filteredCourses = this.courses.allincludinghidden.filter((course) => { - return course.displayname.toLowerCase().indexOf(newValue) > -1; - }); - } else { - this.filteredCourses = this.courses.allincludinghidden.filter((course) => { - return course.fullname.toLowerCase().indexOf(newValue) > -1; - }); - } - } - } - - /** - * Initialize the prefetch icon for selected courses. - */ - protected initPrefetchCoursesIcons(): void { - if (this.prefetchIconsInitialized || !this.downloadEnabled) { - // Already initialized. - return; - } - - this.prefetchIconsInitialized = true; - - Object.keys(this.prefetchCoursesData).forEach((filter) => { - this.courseHelper.initPrefetchCoursesIcons(this.courses[filter], this.prefetchCoursesData[filter]).then((prefetch) => { - this.prefetchCoursesData[filter] = prefetch; - }); - }); - } - - /** - * Prefetch all the shown courses. - * - * @return Promise resolved when done. - */ - prefetchCourses(): Promise { - const selected = this.selectedFilter, - initialIcon = this.prefetchCoursesData[selected].icon; - - return this.courseHelper.prefetchCourses(this.courses[selected], this.prefetchCoursesData[selected]).catch((error) => { - if (!this.isDestroyed) { - this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); - this.prefetchCoursesData[selected].icon = initialIcon; - } - }); - } - - /** - * Refresh the list of courses. - * - * @return Promise resolved when done. - */ - protected async refreshCourseList(): Promise { - this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED); - - try { - await this.coursesProvider.invalidateUserCourses(); - } catch (error) { - // Ignore errors. - } - - await this.loadContent(true); - } - - /** - * The selected courses filter have changed. - */ - selectedChanged(): void { - this.setCourseFilter(this.selectedFilter); - } - - /** - * Set selected courses filter. - * - * @param filter Filter name to set. - */ - protected setCourseFilter(filter: string): void { - this.selectedFilter = filter; - - if (this.showFilters.custom == 'show' && filter.startsWith('custom-') && - typeof this.customFilter[filter.substr(7)] != 'undefined') { - const filterName = this.block.configs.customfiltergrouping.value, - filterValue = this.customFilter[filter.substr(7)].value; - - this.loaded = false; - - this.coursesProvider.getEnrolledCoursesByCustomField(filterName, filterValue).then((courses) => { - // Get the courses information from allincludinghidden to get the max info about the course. - const courseIds = courses.map((course) => course.id); - this.filteredCourses = this.courses.allincludinghidden.filter((allCourse) => - courseIds.indexOf(allCourse.id) !== -1); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, this.fetchContentDefaultError); - }).finally(() => { - this.loaded = true; - }); - } else { - // Only save the filter if not a custom one. - this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewFilter', filter); - - if (this.showFilters[filter] == 'show') { - this.filteredCourses = this.courses[filter]; - } else { - const activeFilter = this.FILTER_PRIORITY.find((name) => { - return this.showFilters[name] == 'show'; - }); - - if (activeFilter) { - this.setCourseFilter(activeFilter); - } - } - } - } - - /** - * Init courses filters. - * - * @param courses Courses to filter. - */ - initCourseFilters(courses: any[]): void { - this.courses.allincludinghidden = courses; - - if (this.showSortFilter) { - if (this.sort == 'lastaccess') { - courses.sort((a, b) => { - return b.lastaccess - a.lastaccess; - }); - } else if (this.sort == 'fullname') { - courses.sort((a, b) => { - const compareA = a.fullname.toLowerCase(), - compareB = b.fullname.toLowerCase(); - - return compareA.localeCompare(compareB); - }); - } else if (this.sort == 'shortname') { - courses.sort((a, b) => { - const compareA = a.shortname.toLowerCase(), - compareB = b.shortname.toLowerCase(); - compareA.localeCompare(); - - return compareA.localeCompare(compareB); - }); - } - } - - this.courses.all = []; - this.courses.past = []; - this.courses.inprogress = []; - this.courses.future = []; - this.courses.favourite = []; - this.courses.hidden = []; - - const today = this.timeUtils.timestamp(); - courses.forEach((course) => { - if (course.hidden) { - this.courses.hidden.push(course); - } else { - this.courses.all.push(course); - - if ((course.enddate && course.enddate < today) || course.completed) { - // Courses that have already ended. - this.courses.past.push(course); - } else if (course.startdate > today) { - // Courses that have not started yet. - this.courses.future.push(course); - } else { - // Courses still in progress. - this.courses.inprogress.push(course); - } - - if (course.isfavourite) { - this.courses.favourite.push(course); - } - } - }); - - this.setCourseFilter(this.selectedFilter); - } - - /** - * The selected courses sort filter have changed. - * - * @param sort New sorting. - */ - switchSort(sort: string): void { - this.sort = sort; - this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewSort', this.sort); - this.initCourseFilters(this.courses.allincludinghidden); - } - - /** - * Show or hide the filter. - */ - switchFilter(): void { - this.showFilter = !this.showFilter; - this.courses.filter = ''; - - if (this.showFilter) { - this.filteredCourses = this.courses.allincludinghidden; - } else { - this.setCourseFilter(this.selectedFilter); - } - } - - /** - * Popover closed after clicking switch filter. - */ - switchFilterClosed(): void { - if (this.showFilter) { - setTimeout(() => { - this.searchbar.setFocus(); - }); - } - } - - /** - * If switch button that enables the filter input is shown or not. - * - * @return If switch button that enables the filter input is shown or not. - */ - showFilterSwitchButton(): boolean { - return this.loaded && this.courses.allincludinghidden && this.courses.allincludinghidden.length > 5; - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.isDestroyed = true; - this.coursesObserver && this.coursesObserver.off(); - this.updateSiteObserver && this.updateSiteObserver.off(); - } -} diff --git a/src/addon/block/myoverview/lang/en.json b/src/addon/block/myoverview/lang/en.json deleted file mode 100644 index 7bca82636..000000000 --- a/src/addon/block/myoverview/lang/en.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "all": "All (except removed from view)", - "allincludinghidden": "All", - "favourites": "Starred", - "future": "Future", - "hiddencourses": "Removed from view", - "inprogress": "In progress", - "lastaccessed": "Last accessed", - "nocourses": "No courses", - "past": "Past", - "pluginname": "Course overview", - "shortname": "Short name", - "title": "Course name" -} diff --git a/src/addon/block/myoverview/myoverview.module.ts b/src/addon/block/myoverview/myoverview.module.ts deleted file mode 100644 index 6b26273e2..000000000 --- a/src/addon/block/myoverview/myoverview.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreBlockDelegate } from '@core/block/providers/delegate'; -import { AddonBlockMyOverviewComponentsModule } from './components/components.module'; -import { AddonBlockMyOverviewHandler } from './providers/block-handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule, - AddonBlockMyOverviewComponentsModule, - TranslateModule.forChild() - ], - exports: [ - ], - providers: [ - AddonBlockMyOverviewHandler - ] -}) -export class AddonBlockMyOverviewModule { - constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockMyOverviewHandler) { - blockDelegate.registerHandler(blockHandler); - } -} diff --git a/src/addon/block/myoverview/providers/block-handler.ts b/src/addon/block/myoverview/providers/block-handler.ts deleted file mode 100644 index 089bfb058..000000000 --- a/src/addon/block/myoverview/providers/block-handler.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreBlockHandlerData } from '@core/block/providers/delegate'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { AddonBlockMyOverviewComponent } from '../components/myoverview/myoverview'; -import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler'; - -/** - * Block handler. - */ -@Injectable() -export class AddonBlockMyOverviewHandler extends CoreBlockBaseHandler { - name = 'AddonBlockMyOverview'; - blockName = 'myoverview'; - - constructor(private coursesProvider: CoreCoursesProvider, private sitesProvider: CoreSitesProvider) { - super(); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return (this.sitesProvider.getCurrentSite() && this.sitesProvider.getCurrentSite().isVersionGreaterEqualThan('3.6')) || - !this.coursesProvider.isMyCoursesDisabledInSite(); - } - - /** - * Returns the data needed to render the block. - * - * @param injector Injector. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_myoverview.pluginname', - class: 'addon-block-myoverview', - component: AddonBlockMyOverviewComponent - }; - } -} diff --git a/src/addon/block/newsitems/lang/en.json b/src/addon/block/newsitems/lang/en.json deleted file mode 100644 index 83b981297..000000000 --- a/src/addon/block/newsitems/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Latest announcements" -} \ No newline at end of file diff --git a/src/addon/block/newsitems/newsitems.module.ts b/src/addon/block/newsitems/newsitems.module.ts deleted file mode 100644 index 11af484ba..000000000 --- a/src/addon/block/newsitems/newsitems.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/newsitems/newsitems.scss b/src/addon/block/newsitems/newsitems.scss deleted file mode 100644 index 8b0c46287..000000000 --- a/src/addon/block/newsitems/newsitems.scss +++ /dev/null @@ -1,26 +0,0 @@ -.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; - } - } -} \ No newline at end of file diff --git a/src/addon/block/newsitems/providers/block-handler.ts b/src/addon/block/newsitems/providers/block-handler.ts deleted file mode 100644 index 48e1692c3..000000000 --- a/src/addon/block/newsitems/providers/block-handler.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - return { - title: 'addon.block_newsitems.pluginname', - class: 'addon-block-news-items', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/onlineusers/lang/en.json b/src/addon/block/onlineusers/lang/en.json deleted file mode 100644 index 4bc6cd412..000000000 --- a/src/addon/block/onlineusers/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Online users" -} \ No newline at end of file diff --git a/src/addon/block/onlineusers/onlineusers.module.ts b/src/addon/block/onlineusers/onlineusers.module.ts deleted file mode 100644 index b243c7e8a..000000000 --- a/src/addon/block/onlineusers/onlineusers.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/onlineusers/onlineusers.scss b/src/addon/block/onlineusers/onlineusers.scss deleted file mode 100644 index 009a347b8..000000000 --- a/src/addon/block/onlineusers/onlineusers.scss +++ /dev/null @@ -1,48 +0,0 @@ -.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; - } - -} \ No newline at end of file diff --git a/src/addon/block/onlineusers/providers/block-handler.ts b/src/addon/block/onlineusers/providers/block-handler.ts deleted file mode 100644 index a1bfc4e61..000000000 --- a/src/addon/block/onlineusers/providers/block-handler.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - return { - title: 'addon.block_onlineusers.pluginname', - class: 'addon-block-online-users', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/privatefiles/lang/en.json b/src/addon/block/privatefiles/lang/en.json deleted file mode 100644 index bba9d4bc0..000000000 --- a/src/addon/block/privatefiles/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Private files" -} \ No newline at end of file diff --git a/src/addon/block/privatefiles/privatefiles.module.ts b/src/addon/block/privatefiles/privatefiles.module.ts deleted file mode 100644 index 81c2a7f5f..000000000 --- a/src/addon/block/privatefiles/privatefiles.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/privatefiles/providers/block-handler.ts b/src/addon/block/privatefiles/providers/block-handler.ts deleted file mode 100644 index 7632f770b..000000000 --- a/src/addon/block/privatefiles/providers/block-handler.ts +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_privatefiles.pluginname', - class: 'addon-block-private-files', - component: CoreBlockOnlyTitleComponent, - link: 'AddonFilesListPage', - linkParams: {root: 'my'} - }; - } -} diff --git a/src/addon/block/recentactivity/lang/en.json b/src/addon/block/recentactivity/lang/en.json deleted file mode 100644 index 29f7996e2..000000000 --- a/src/addon/block/recentactivity/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Recent activity" -} \ No newline at end of file diff --git a/src/addon/block/recentactivity/providers/block-handler.ts b/src/addon/block/recentactivity/providers/block-handler.ts deleted file mode 100644 index 832fdbcbc..000000000 --- a/src/addon/block/recentactivity/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_recentactivity.pluginname', - class: 'addon-block-recent-activity', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/recentactivity/recentactivity.module.ts b/src/addon/block/recentactivity/recentactivity.module.ts deleted file mode 100644 index 4d20d2114..000000000 --- a/src/addon/block/recentactivity/recentactivity.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/recentactivity/recentactivity.scss b/src/addon/block/recentactivity/recentactivity.scss deleted file mode 100644 index 0eb73e102..000000000 --- a/src/addon/block/recentactivity/recentactivity.scss +++ /dev/null @@ -1,20 +0,0 @@ -.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); - } - } - } - } -} \ No newline at end of file diff --git a/src/addon/block/recentlyaccessedcourses/components/components.module.ts b/src/addon/block/recentlyaccessedcourses/components/components.module.ts deleted file mode 100644 index 39e3c11a1..000000000 --- a/src/addon/block/recentlyaccessedcourses/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonBlockRecentlyAccessedCoursesComponent } from './recentlyaccessedcourses/recentlyaccessedcourses'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCoursesComponentsModule } from '@core/courses/components/components.module'; - -@NgModule({ - declarations: [ - AddonBlockRecentlyAccessedCoursesComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCoursesComponentsModule - ], - providers: [ - ], - exports: [ - AddonBlockRecentlyAccessedCoursesComponent - ], - entryComponents: [ - AddonBlockRecentlyAccessedCoursesComponent - ] -}) -export class AddonBlockRecentlyAccessedCoursesComponentsModule {} diff --git a/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html b/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html deleted file mode 100644 index aede44831..000000000 --- a/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html +++ /dev/null @@ -1,19 +0,0 @@ - -

{{ 'addon.block_recentlyaccessedcourses.pluginname' | translate }}

-
- - {{prefetchCoursesData.badge}} - -
-
- - - -
- - - -
-
diff --git a/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts b/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts deleted file mode 100644 index a1eb249ce..000000000 --- a/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts +++ /dev/null @@ -1,237 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, Injector, Input, OnChanges, SimpleChange } from '@angular/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData } from '@core/courses/providers/courses'; -import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; -import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers/coursecompletion'; -import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component'; - -/** - * Component to render a recent courses block. - */ -@Component({ - selector: 'addon-block-recentlyaccessedcourses', - templateUrl: 'addon-block-recentlyaccessedcourses.html' -}) -export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseComponent implements OnInit, OnChanges, OnDestroy { - @Input() downloadEnabled: boolean; - - courses = []; - prefetchCoursesData = { - icon: '', - badge: '' - }; - downloadCourseEnabled: boolean; - downloadCoursesEnabled: boolean; - - protected prefetchIconsInitialized = false; - protected isDestroyed; - protected coursesObserver; - protected updateSiteObserver; - protected courseIds = []; - protected fetchContentDefaultError = 'Error getting recent courses data.'; - - constructor(injector: Injector, private coursesProvider: CoreCoursesProvider, - private courseCompletionProvider: AddonCourseCompletionProvider, private eventsProvider: CoreEventsProvider, - private courseHelper: CoreCourseHelperProvider, - private courseOptionsDelegate: CoreCourseOptionsDelegate, private coursesHelper: CoreCoursesHelperProvider, - private sitesProvider: CoreSitesProvider) { - - super(injector, 'AddonBlockRecentlyAccessedCoursesComponent'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - - // Refresh the enabled flags if enabled. - this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite(); - this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); - - // Refresh the enabled flags if site is updated. - this.updateSiteObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => { - this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite(); - this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); - - }, this.sitesProvider.getCurrentSiteId()); - - this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, - (data: CoreCoursesMyCoursesUpdatedEventData) => { - - if (this.shouldRefreshOnUpdatedEvent(data)) { - this.refreshCourseList(); - } - }, this.sitesProvider.getCurrentSiteId()); - - 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. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.coursesProvider.invalidateUserCourses().finally(() => { - // Invalidate course completion data. - return this.utils.allPromises(this.courseIds.map((courseId) => { - return this.courseCompletionProvider.invalidateCourseCompletion(courseId); - })); - })); - - promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); - if (this.courseIds.length > 0) { - promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds.join(','))); - } - - return this.utils.allPromises(promises).finally(() => { - this.prefetchIconsInitialized = false; - }); - } - - /** - * Fetch the courses for recent courses. - * - * @return Promise resolved when done. - */ - protected fetchContent(): Promise { - const showCategories = this.block.configs && this.block.configs.displaycategories && - this.block.configs.displaycategories.value == '1'; - - return this.coursesHelper.getUserCoursesWithOptions('lastaccess', 10, null, showCategories).then((courses) => { - this.courses = courses; - - this.initPrefetchCoursesIcons(); - }); - } - - /** - * Refresh the list of courses. - * - * @return Promise resolved when done. - */ - protected async refreshCourseList(): Promise { - this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED); - - try { - await this.coursesProvider.invalidateUserCourses(); - } catch (error) { - // Ignore errors. - } - - await this.loadContent(true); - } - - /** - * Initialize the prefetch icon for selected courses. - */ - protected initPrefetchCoursesIcons(): void { - if (this.prefetchIconsInitialized || !this.downloadEnabled) { - // Already initialized. - return; - } - - this.prefetchIconsInitialized = true; - - this.courseHelper.initPrefetchCoursesIcons(this.courses, this.prefetchCoursesData).then((prefetch) => { - this.prefetchCoursesData = prefetch; - }); - } - - /** - * Whether list should be refreshed based on a EVENT_MY_COURSES_UPDATED event. - * - * @param data Event data. - * @return Whether to refresh. - */ - protected shouldRefreshOnUpdatedEvent(data: CoreCoursesMyCoursesUpdatedEventData): boolean { - if (data.action == CoreCoursesProvider.ACTION_ENROL) { - // Always update if user enrolled in a course. - return true; - } - - if (data.action == CoreCoursesProvider.ACTION_VIEW && data.courseId != this.sitesProvider.getCurrentSiteHomeId() && - this.courses[0] && data.courseId != this.courses[0].id) { - // Update list if user viewed a course that isn't the most recent one and isn't site home. - return true; - } - - if (data.action == CoreCoursesProvider.ACTION_STATE_CHANGED && data.state == CoreCoursesProvider.STATE_FAVOURITE && - this.hasCourse(data.courseId)) { - // Update list if a visible course is now favourite or unfavourite. - return true; - } - - return false; - } - - /** - * Check if a certain course is in the list of courses. - * - * @param courseId Course ID to search. - * @return Whether it's in the list. - */ - protected hasCourse(courseId: number): boolean { - if (!this.courses) { - return false; - } - - return !!this.courses.find((course) => { - return course.id == courseId; - }); - } - - /** - * Prefetch all the shown courses. - * - * @return Promise resolved when done. - */ - prefetchCourses(): Promise { - const initialIcon = this.prefetchCoursesData.icon; - - return this.courseHelper.prefetchCourses(this.courses, this.prefetchCoursesData).catch((error) => { - if (!this.isDestroyed) { - this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); - this.prefetchCoursesData.icon = initialIcon; - } - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.isDestroyed = true; - this.coursesObserver && this.coursesObserver.off(); - this.updateSiteObserver && this.updateSiteObserver.off(); - } -} diff --git a/src/addon/block/recentlyaccessedcourses/lang/en.json b/src/addon/block/recentlyaccessedcourses/lang/en.json deleted file mode 100644 index e87006df7..000000000 --- a/src/addon/block/recentlyaccessedcourses/lang/en.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "nocourses": "No recent courses", - "pluginname": "Recently accessed courses" -} diff --git a/src/addon/block/recentlyaccessedcourses/providers/block-handler.ts b/src/addon/block/recentlyaccessedcourses/providers/block-handler.ts deleted file mode 100644 index d562effe8..000000000 --- a/src/addon/block/recentlyaccessedcourses/providers/block-handler.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreBlockHandlerData } from '@core/block/providers/delegate'; -import { AddonBlockRecentlyAccessedCoursesComponent } from '../components/recentlyaccessedcourses/recentlyaccessedcourses'; -import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler'; - -/** - * Block handler. - */ -@Injectable() -export class AddonBlockRecentlyAccessedCoursesHandler extends CoreBlockBaseHandler { - name = 'AddonBlockRecentlyAccessedCourses'; - blockName = 'recentlyaccessedcourses'; - - constructor() { - super(); - } - - /** - * Returns the data needed to render the block. - * - * @param injector Injector. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_recentlyaccessedcourses.pluginname', - class: 'addon-block-recentlyaccessedcourses', - component: AddonBlockRecentlyAccessedCoursesComponent - }; - } -} diff --git a/src/addon/block/recentlyaccessedcourses/recentlyaccessedcourses.module.ts b/src/addon/block/recentlyaccessedcourses/recentlyaccessedcourses.module.ts deleted file mode 100644 index 53ade7657..000000000 --- a/src/addon/block/recentlyaccessedcourses/recentlyaccessedcourses.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreBlockDelegate } from '@core/block/providers/delegate'; -import { AddonBlockRecentlyAccessedCoursesComponentsModule } from './components/components.module'; -import { AddonBlockRecentlyAccessedCoursesHandler } from './providers/block-handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule, - CoreComponentsModule, - AddonBlockRecentlyAccessedCoursesComponentsModule, - TranslateModule.forChild() - ], - providers: [ - AddonBlockRecentlyAccessedCoursesHandler - ] -}) -export class AddonBlockRecentlyAccessedCoursesModule { - constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockRecentlyAccessedCoursesHandler) { - blockDelegate.registerHandler(blockHandler); - } -} diff --git a/src/addon/block/recentlyaccesseditems/components/components.module.ts b/src/addon/block/recentlyaccesseditems/components/components.module.ts deleted file mode 100644 index bede51164..000000000 --- a/src/addon/block/recentlyaccesseditems/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonBlockRecentlyAccessedItemsComponent } from './recentlyaccesseditems/recentlyaccesseditems'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; - -@NgModule({ - declarations: [ - AddonBlockRecentlyAccessedItemsComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonBlockRecentlyAccessedItemsComponent - ], - entryComponents: [ - AddonBlockRecentlyAccessedItemsComponent - ] -}) -export class AddonBlockRecentlyAccessedItemsComponentsModule {} diff --git a/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html b/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html deleted file mode 100644 index cfa2892d6..000000000 --- a/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html +++ /dev/null @@ -1,19 +0,0 @@ - -

{{ 'addon.block_recentlyaccesseditems.pluginname' | translate }}

-
- -
-
- - - -

-

-
-
-
-
- - - -
diff --git a/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.scss b/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.scss deleted file mode 100644 index 83c2695ce..000000000 --- a/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.scss +++ /dev/null @@ -1,3 +0,0 @@ -ion-app.app-root addon-block-recentlyaccesseditems .core-horizontal-scroll > div { - @include horizontal_scroll_item(80%, 250px, 300px); -} \ No newline at end of file diff --git a/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts b/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts deleted file mode 100644 index 60041df84..000000000 --- a/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts +++ /dev/null @@ -1,91 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector, Optional } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component'; -import { - AddonBlockRecentlyAccessedItemsProvider, AddonBlockRecentlyAccessedItemsItem -} from '../../providers/recentlyaccesseditems'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; - -/** - * Component to render a recently accessed items block. - */ -@Component({ - selector: 'addon-block-recentlyaccesseditems', - templateUrl: 'addon-block-recentlyaccesseditems.html' -}) -export class AddonBlockRecentlyAccessedItemsComponent extends CoreBlockBaseComponent implements OnInit { - items: AddonBlockRecentlyAccessedItemsItem[] = []; - - protected fetchContentDefaultError = 'Error getting recently accessed items data.'; - - constructor(injector: Injector, @Optional() private navCtrl: NavController, - private sitesProvider: CoreSitesProvider, - private recentItemsProvider: AddonBlockRecentlyAccessedItemsProvider, - private contentLinksHelper: CoreContentLinksHelperProvider) { - - super(injector, 'AddonBlockRecentlyAccessedItemsComponent'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return this.recentItemsProvider.invalidateRecentItems(); - } - - /** - * Fetch the data to render the block. - * - * @return Promise resolved when done. - */ - protected fetchContent(): Promise { - return this.recentItemsProvider.getRecentItems().then((items) => { - this.items = items; - }); - } - - /** - * Event clicked. - * - * @param e Click event. - * @param item Activity item info. - */ - action(e: Event, item: any): void { - e.preventDefault(); - e.stopPropagation(); - - const url = this.textUtils.decodeHTMLEntities(item.viewurl); - const modal = this.domUtils.showModalLoading(); - this.contentLinksHelper.handleLink(url, undefined, this.navCtrl).then((treated) => { - if (!treated) { - return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); - } - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/block/recentlyaccesseditems/lang/en.json b/src/addon/block/recentlyaccesseditems/lang/en.json deleted file mode 100644 index 47311d281..000000000 --- a/src/addon/block/recentlyaccesseditems/lang/en.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "noitems": "No recent items", - "pluginname": "Recently accessed items" -} diff --git a/src/addon/block/recentlyaccesseditems/providers/block-handler.ts b/src/addon/block/recentlyaccesseditems/providers/block-handler.ts deleted file mode 100644 index 5ba0425c9..000000000 --- a/src/addon/block/recentlyaccesseditems/providers/block-handler.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreBlockHandlerData } from '@core/block/providers/delegate'; -import { AddonBlockRecentlyAccessedItemsComponent } from '../components/recentlyaccesseditems/recentlyaccesseditems'; -import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler'; - -/** - * Block handler. - */ -@Injectable() -export class AddonBlockRecentlyAccessedItemsHandler extends CoreBlockBaseHandler { - name = 'AddonBlockRecentlyAccessedItems'; - blockName = 'recentlyaccesseditems'; - - constructor() { - super(); - } - - /** - * Returns the data needed to render the block. - * - * @param injector Injector. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_recentlyaccesseditems.pluginname', - class: 'addon-block-recentlyaccesseditems', - component: AddonBlockRecentlyAccessedItemsComponent - }; - } -} diff --git a/src/addon/block/recentlyaccesseditems/providers/recentlyaccesseditems.ts b/src/addon/block/recentlyaccesseditems/providers/recentlyaccesseditems.ts deleted file mode 100644 index 3eb38e75b..000000000 --- a/src/addon/block/recentlyaccesseditems/providers/recentlyaccesseditems.ts +++ /dev/null @@ -1,100 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Service that provides some features regarding recently accessed items. - */ -@Injectable() -export class AddonBlockRecentlyAccessedItemsProvider { - protected ROOT_CACHE_KEY = 'AddonBlockRecentlyAccessedItems:'; - - constructor(private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, - private domUtils: CoreDomUtilsProvider) { } - - /** - * Get cache key for get last accessed items value WS call. - * - * @return Cache key. - */ - protected getRecentItemsCacheKey(): string { - return this.ROOT_CACHE_KEY + ':recentitems'; - } - - /** - * Get last accessed items. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved when the info is retrieved. - */ - getRecentItems(siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const preSets = { - cacheKey: this.getRecentItemsCacheKey() - }; - - return site.read('block_recentlyaccesseditems_get_recent_items', undefined, preSets) - .then((items: AddonBlockRecentlyAccessedItemsItem[]) => { - - return items.map((item) => { - const modicon = item.icon && this.domUtils.getHTMLElementAttribute(item.icon, 'src'); - item.iconUrl = this.courseProvider.getModuleIconSrc(item.modname, modicon); - - return item; - }); - }); - }); - } - - /** - * Invalidates get last accessed items WS call. - * - * @param siteId Site ID to invalidate. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateRecentItems(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getRecentItemsCacheKey()); - }); - } -} - -/** - * Result of WS block_recentlyaccesseditems_get_recent_items. - */ -export type AddonBlockRecentlyAccessedItemsItem = { - id: number; // Id. - courseid: number; // Courseid. - cmid: number; // Cmid. - userid: number; // Userid. - modname: string; // Modname. - name: string; // Name. - coursename: string; // Coursename. - timeaccess: number; // Timeaccess. - viewurl: string; // Viewurl. - courseviewurl: string; // Courseviewurl. - icon: string; // Icon. -} & AddonBlockRecentlyAccessedItemsItemCalculatedData; - -/** - * Calculated data for recently accessed item. - */ -export type AddonBlockRecentlyAccessedItemsItemCalculatedData = { - iconUrl: string; // Icon URL. Calculated by the app. -}; diff --git a/src/addon/block/recentlyaccesseditems/recentlyaccesseditems.module.ts b/src/addon/block/recentlyaccesseditems/recentlyaccesseditems.module.ts deleted file mode 100644 index 3a9229176..000000000 --- a/src/addon/block/recentlyaccesseditems/recentlyaccesseditems.module.ts +++ /dev/null @@ -1,42 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonBlockRecentlyAccessedItemsComponentsModule } from './components/components.module'; -import { CoreBlockDelegate } from '@core/block/providers/delegate'; -import { AddonBlockRecentlyAccessedItemsHandler } from './providers/block-handler'; -import { AddonBlockRecentlyAccessedItemsProvider } from './providers/recentlyaccesseditems'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule, - AddonBlockRecentlyAccessedItemsComponentsModule, - TranslateModule.forChild() - ], - exports: [ - ], - providers: [ - AddonBlockRecentlyAccessedItemsHandler, - AddonBlockRecentlyAccessedItemsProvider - ] -}) -export class AddonBlockRecentlyAccessedItemsModule { - constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockRecentlyAccessedItemsHandler) { - blockDelegate.registerHandler(blockHandler); - } -} diff --git a/src/addon/block/rssclient/lang/en.json b/src/addon/block/rssclient/lang/en.json deleted file mode 100644 index 18282971b..000000000 --- a/src/addon/block/rssclient/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Remote RSS feeds" -} \ No newline at end of file diff --git a/src/addon/block/rssclient/providers/block-handler.ts b/src/addon/block/rssclient/providers/block-handler.ts deleted file mode 100644 index e7ad835e3..000000000 --- a/src/addon/block/rssclient/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: block.contents.title || 'addon.block_rssclient.pluginname', - class: 'addon-block-rss-client', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/rssclient/rssclient.module.ts b/src/addon/block/rssclient/rssclient.module.ts deleted file mode 100644 index 402bd00ac..000000000 --- a/src/addon/block/rssclient/rssclient.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/rssclient/rssclient.scss b/src/addon/block/rssclient/rssclient.scss deleted file mode 100644 index 33b37b0e7..000000000 --- a/src/addon/block/rssclient/rssclient.scss +++ /dev/null @@ -1,19 +0,0 @@ -.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; - } - } - } -} \ No newline at end of file diff --git a/src/addon/block/selfcompletion/lang/en.json b/src/addon/block/selfcompletion/lang/en.json deleted file mode 100644 index 32521695a..000000000 --- a/src/addon/block/selfcompletion/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Self completion" -} \ No newline at end of file diff --git a/src/addon/block/selfcompletion/providers/block-handler.ts b/src/addon/block/selfcompletion/providers/block-handler.ts deleted file mode 100644 index b9309609d..000000000 --- a/src/addon/block/selfcompletion/providers/block-handler.ts +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_selfcompletion.pluginname', - class: 'addon-block-self-completion', - component: CoreBlockOnlyTitleComponent, - link: 'AddonCourseCompletionReportPage', - linkParams: { courseId: instanceId } - }; - } -} diff --git a/src/addon/block/selfcompletion/selfcompletion.module.ts b/src/addon/block/selfcompletion/selfcompletion.module.ts deleted file mode 100644 index 8330faa9d..000000000 --- a/src/addon/block/selfcompletion/selfcompletion.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/sitemainmenu/components/components.module.ts b/src/addon/block/sitemainmenu/components/components.module.ts deleted file mode 100644 index 9ca18bf78..000000000 --- a/src/addon/block/sitemainmenu/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonBlockSiteMainMenuComponent } from './sitemainmenu/sitemainmenu'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; - -@NgModule({ - declarations: [ - AddonBlockSiteMainMenuComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonBlockSiteMainMenuComponent - ], - entryComponents: [ - AddonBlockSiteMainMenuComponent - ] -}) -export class AddonBlockSiteMainMenuComponentsModule {} diff --git a/src/addon/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html b/src/addon/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html deleted file mode 100644 index 49dfb8748..000000000 --- a/src/addon/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html +++ /dev/null @@ -1,12 +0,0 @@ - -

{{ 'addon.block_sitemainmenu.pluginname' | translate }}

-
- - - - - - - - - diff --git a/src/addon/block/sitemainmenu/components/sitemainmenu/sitemainmenu.ts b/src/addon/block/sitemainmenu/components/sitemainmenu/sitemainmenu.ts deleted file mode 100644 index 968af16b8..000000000 --- a/src/addon/block/sitemainmenu/components/sitemainmenu/sitemainmenu.ts +++ /dev/null @@ -1,117 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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'; -import { CoreSiteHomeProvider } from '@core/sitehome/providers/sitehome'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component'; - -/** - * Component to render a site main menu block. - */ -@Component({ - selector: 'addon-block-sitemainmenu', - templateUrl: 'addon-block-sitemainmenu.html' -}) -export class AddonBlockSiteMainMenuComponent extends CoreBlockBaseComponent implements OnInit { - @Input() downloadEnabled: boolean; - - component = 'AddonBlockSiteMainMenu'; - mainMenuBlock: any; - siteHomeId: number; - - protected fetchContentDefaultError = 'Error getting main menu data.'; - - constructor(injector: Injector, protected sitesProvider: CoreSitesProvider, protected courseProvider: CoreCourseProvider, - protected courseHelper: CoreCourseHelperProvider, protected siteHomeProvider: CoreSiteHomeProvider, - protected prefetchDelegate: CoreCourseModulePrefetchDelegate) { - - super(injector, 'AddonBlockSiteMainMenuComponent'); - - this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.courseProvider.invalidateSections(this.siteHomeId)); - promises.push(this.siteHomeProvider.invalidateNewsForum(this.siteHomeId)); - - if (this.mainMenuBlock && this.mainMenuBlock.modules) { - // Invalidate modules prefetch data. - promises.push(this.prefetchDelegate.invalidateModules(this.mainMenuBlock.modules, this.siteHomeId)); - } - - return Promise.all(promises); - } - - /** - * Fetch the data to render the block. - * - * @return Promise resolved when done. - */ - protected fetchContent(): Promise { - return this.courseProvider.getSections(this.siteHomeId, false, true).then((sections) => { - this.mainMenuBlock = sections.find((section) => section.section == 0); - - if (this.mainMenuBlock) { - this.mainMenuBlock.hasContent = this.courseHelper.sectionHasContent(this.mainMenuBlock); - this.courseHelper.addHandlerDataForModules([this.mainMenuBlock], this.siteHomeId, undefined, undefined, true); - - // Check if Site Home displays announcements. If so, remove it from the main menu block. - const currentSite = this.sitesProvider.getCurrentSite(), - config = currentSite ? currentSite.getStoredConfig() || {} : {}; - let hasNewsItem = false; - - if (config.frontpageloggedin) { - const items = config.frontpageloggedin.split(','); - - hasNewsItem = items.find((item) => { return item == '0'; }); - } - - 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.mainMenuBlock.modules.length; i++) { - const module = this.mainMenuBlock.modules[i]; - - if (module.modname == 'forum' && module.instance == forum.id) { - this.mainMenuBlock.modules.splice(i, 1); - break; - } - } - }).catch(() => { - // Ignore errors. - }); - } - } - }); - } -} diff --git a/src/addon/block/sitemainmenu/lang/en.json b/src/addon/block/sitemainmenu/lang/en.json deleted file mode 100644 index 0f3aca2ff..000000000 --- a/src/addon/block/sitemainmenu/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Main menu" -} diff --git a/src/addon/block/sitemainmenu/providers/block-handler.ts b/src/addon/block/sitemainmenu/providers/block-handler.ts deleted file mode 100644 index 305a46eb0..000000000 --- a/src/addon/block/sitemainmenu/providers/block-handler.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreBlockHandlerData } from '@core/block/providers/delegate'; -import { AddonBlockSiteMainMenuComponent } from '../components/sitemainmenu/sitemainmenu'; -import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler'; - -/** - * Block handler. - */ -@Injectable() -export class AddonBlockSiteMainMenuHandler extends CoreBlockBaseHandler { - name = 'AddonBlockSiteMainMenu'; - blockName = 'site_main_menu'; - - constructor() { - super(); - } - - /** - * Returns the data needed to render the block. - * - * @param injector Injector. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_sitemainmenu.pluginname', - class: 'addon-block-sitemainmenu', - component: AddonBlockSiteMainMenuComponent - }; - } -} diff --git a/src/addon/block/sitemainmenu/sitemainmenu.module.ts b/src/addon/block/sitemainmenu/sitemainmenu.module.ts deleted file mode 100644 index 5c475da1d..000000000 --- a/src/addon/block/sitemainmenu/sitemainmenu.module.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonBlockSiteMainMenuComponentsModule } from './components/components.module'; -import { CoreBlockDelegate } from '@core/block/providers/delegate'; -import { AddonBlockSiteMainMenuHandler } from './providers/block-handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule, - CoreComponentsModule, - CoreDirectivesModule, - AddonBlockSiteMainMenuComponentsModule, - TranslateModule.forChild() - ], - exports: [ - ], - providers: [ - AddonBlockSiteMainMenuHandler - ] -}) -export class AddonBlockSiteMainMenuModule { - constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockSiteMainMenuHandler) { - blockDelegate.registerHandler(blockHandler); - } -} diff --git a/src/addon/block/starredcourses/components/components.module.ts b/src/addon/block/starredcourses/components/components.module.ts deleted file mode 100644 index 5cd67b4b6..000000000 --- a/src/addon/block/starredcourses/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonBlockStarredCoursesComponent } from './starredcourses/starredcourses'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCoursesComponentsModule } from '@core/courses/components/components.module'; - -@NgModule({ - declarations: [ - AddonBlockStarredCoursesComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCoursesComponentsModule - ], - providers: [ - ], - exports: [ - AddonBlockStarredCoursesComponent - ], - entryComponents: [ - AddonBlockStarredCoursesComponent - ] -}) -export class AddonBlockStarredCoursesComponentsModule {} diff --git a/src/addon/block/starredcourses/components/starredcourses/addon-block-starredcourses.html b/src/addon/block/starredcourses/components/starredcourses/addon-block-starredcourses.html deleted file mode 100644 index af38133a9..000000000 --- a/src/addon/block/starredcourses/components/starredcourses/addon-block-starredcourses.html +++ /dev/null @@ -1,19 +0,0 @@ - -

{{ 'addon.block_starredcourses.pluginname' | translate }}

-
- - {{prefetchCoursesData.badge}} - -
-
- - - -
- - - -
-
diff --git a/src/addon/block/starredcourses/components/starredcourses/starredcourses.ts b/src/addon/block/starredcourses/components/starredcourses/starredcourses.ts deleted file mode 100644 index 0fc513209..000000000 --- a/src/addon/block/starredcourses/components/starredcourses/starredcourses.ts +++ /dev/null @@ -1,216 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, Injector, Input, OnChanges, SimpleChange } from '@angular/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData } from '@core/courses/providers/courses'; -import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; -import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers/coursecompletion'; -import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component'; - -/** - * Component to render a starred courses block. - */ -@Component({ - selector: 'addon-block-starredcourses', - templateUrl: 'addon-block-starredcourses.html' -}) -export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent implements OnInit, OnChanges, OnDestroy { - @Input() downloadEnabled: boolean; - - courses = []; - prefetchCoursesData = { - icon: '', - badge: '' - }; - downloadCourseEnabled: boolean; - downloadCoursesEnabled: boolean; - - protected prefetchIconsInitialized = false; - protected isDestroyed; - protected coursesObserver; - protected updateSiteObserver; - protected courseIds = []; - protected fetchContentDefaultError = 'Error getting starred courses data.'; - - constructor(injector: Injector, private coursesProvider: CoreCoursesProvider, - private courseCompletionProvider: AddonCourseCompletionProvider, private eventsProvider: CoreEventsProvider, - private courseHelper: CoreCourseHelperProvider, - private courseOptionsDelegate: CoreCourseOptionsDelegate, private coursesHelper: CoreCoursesHelperProvider, - private sitesProvider: CoreSitesProvider) { - - super(injector, 'AddonBlockStarredCoursesComponent'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - - // Refresh the enabled flags if enabled. - this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite(); - this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); - - // Refresh the enabled flags if site is updated. - this.updateSiteObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => { - this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite(); - this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); - - }, this.sitesProvider.getCurrentSiteId()); - - this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, - (data: CoreCoursesMyCoursesUpdatedEventData) => { - - if (this.shouldRefreshOnUpdatedEvent(data)) { - this.refreshCourseList(); - } - this.refreshContent(); - }, this.sitesProvider.getCurrentSiteId()); - - 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. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.coursesProvider.invalidateUserCourses().finally(() => { - // Invalidate course completion data. - return this.utils.allPromises(this.courseIds.map((courseId) => { - return this.courseCompletionProvider.invalidateCourseCompletion(courseId); - })); - })); - - promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); - if (this.courseIds.length > 0) { - promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds.join(','))); - } - - return this.utils.allPromises(promises).finally(() => { - this.prefetchIconsInitialized = false; - }); - } - - /** - * Fetch the courses. - * - * @return Promise resolved when done. - */ - protected fetchContent(): Promise { - const showCategories = this.block.configs && this.block.configs.displaycategories && - this.block.configs.displaycategories.value == '1'; - - return this.coursesHelper.getUserCoursesWithOptions('timemodified', 0, 'isfavourite', showCategories).then((courses) => { - this.courses = courses; - - this.initPrefetchCoursesIcons(); - }); - } - - /** - * Refresh the list of courses. - * - * @return Promise resolved when done. - */ - protected async refreshCourseList(): Promise { - this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED); - - try { - await this.coursesProvider.invalidateUserCourses(); - } catch (error) { - // Ignore errors. - } - - await this.loadContent(true); - } - - /** - * Whether list should be refreshed based on a EVENT_MY_COURSES_UPDATED event. - * - * @param data Event data. - * @return Whether to refresh. - */ - protected shouldRefreshOnUpdatedEvent(data: CoreCoursesMyCoursesUpdatedEventData): boolean { - if (data.action == CoreCoursesProvider.ACTION_ENROL) { - // Always update if user enrolled in a course. - // New courses shouldn't be favourite by default, but just in case. - return true; - } - - if (data.action == CoreCoursesProvider.ACTION_STATE_CHANGED && data.state == CoreCoursesProvider.STATE_FAVOURITE) { - // Update list when making a course favourite or not. - return true; - } - - return false; - } - - /** - * Initialize the prefetch icon for selected courses. - */ - protected initPrefetchCoursesIcons(): void { - if (this.prefetchIconsInitialized || !this.downloadEnabled) { - // Already initialized. - return; - } - - this.prefetchIconsInitialized = true; - - this.courseHelper.initPrefetchCoursesIcons(this.courses, this.prefetchCoursesData).then((prefetch) => { - this.prefetchCoursesData = prefetch; - }); - } - - /** - * Prefetch all the shown courses. - * - * @return Promise resolved when done. - */ - prefetchCourses(): Promise { - const initialIcon = this.prefetchCoursesData.icon; - - return this.courseHelper.prefetchCourses(this.courses, this.prefetchCoursesData).catch((error) => { - if (!this.isDestroyed) { - this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); - this.prefetchCoursesData.icon = initialIcon; - } - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.isDestroyed = true; - this.coursesObserver && this.coursesObserver.off(); - this.updateSiteObserver && this.updateSiteObserver.off(); - } -} diff --git a/src/addon/block/starredcourses/lang/en.json b/src/addon/block/starredcourses/lang/en.json deleted file mode 100644 index cc3dd03c7..000000000 --- a/src/addon/block/starredcourses/lang/en.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "nocourses": "No starred courses", - "pluginname": "Starred courses" -} diff --git a/src/addon/block/starredcourses/providers/block-handler.ts b/src/addon/block/starredcourses/providers/block-handler.ts deleted file mode 100644 index a9bbc579e..000000000 --- a/src/addon/block/starredcourses/providers/block-handler.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreBlockHandlerData } from '@core/block/providers/delegate'; -import { AddonBlockStarredCoursesComponent } from '../components/starredcourses/starredcourses'; -import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler'; - -/** - * Block handler. - */ -@Injectable() -export class AddonBlockStarredCoursesHandler extends CoreBlockBaseHandler { - name = 'AddonBlockStarredCourses'; - blockName = 'starredcourses'; - - constructor() { - super(); - } - - /** - * Returns the data needed to render the block. - * - * @param injector Injector. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.starredcourses.pluginname', - class: 'addon-block-starredcourses', - component: AddonBlockStarredCoursesComponent - }; - } -} diff --git a/src/addon/block/starredcourses/starredcourses.module.ts b/src/addon/block/starredcourses/starredcourses.module.ts deleted file mode 100644 index 96b973ef7..000000000 --- a/src/addon/block/starredcourses/starredcourses.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreBlockDelegate } from '@core/block/providers/delegate'; -import { AddonBlockStarredCoursesComponentsModule } from './components/components.module'; -import { AddonBlockStarredCoursesHandler } from './providers/block-handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule, - CoreComponentsModule, - AddonBlockStarredCoursesComponentsModule, - TranslateModule.forChild() - ], - providers: [ - AddonBlockStarredCoursesHandler - ] -}) -export class AddonBlockStarredCoursesModule { - constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockStarredCoursesHandler) { - blockDelegate.registerHandler(blockHandler); - } -} diff --git a/src/addon/block/tags/lang/en.json b/src/addon/block/tags/lang/en.json deleted file mode 100644 index a4080dd78..000000000 --- a/src/addon/block/tags/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Tags" -} \ No newline at end of file diff --git a/src/addon/block/tags/providers/block-handler.ts b/src/addon/block/tags/providers/block-handler.ts deleted file mode 100644 index 566bd6bfa..000000000 --- a/src/addon/block/tags/providers/block-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_tags.pluginname', - class: 'addon-block-tags', - component: CoreBlockPreRenderedComponent - }; - } -} diff --git a/src/addon/block/tags/tags.module.ts b/src/addon/block/tags/tags.module.ts deleted file mode 100644 index 7367edbdc..000000000 --- a/src/addon/block/tags/tags.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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); - } -} diff --git a/src/addon/block/tags/tags.scss b/src/addon/block/tags/tags.scss deleted file mode 100644 index c6704e962..000000000 --- a/src/addon/block/tags/tags.scss +++ /dev/null @@ -1,107 +0,0 @@ -.addon-block-tags core-block-pre-rendered { - .core-block-content { - .tag_cloud { - font-size: 80%; - 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; - } - } - } - } - } -} \ No newline at end of file diff --git a/src/addon/block/timeline/components/components.module.ts b/src/addon/block/timeline/components/components.module.ts deleted file mode 100644 index 465a66858..000000000 --- a/src/addon/block/timeline/components/components.module.ts +++ /dev/null @@ -1,53 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreCoursesComponentsModule } from '@core/courses/components/components.module'; -import { AddonBlockTimelineComponent } from './timeline/timeline'; -import { AddonBlockTimelineEventsComponent } from './events/events'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; - -@NgModule({ - declarations: [ - AddonBlockTimelineComponent, - AddonBlockTimelineEventsComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreCoursesComponentsModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonBlockTimelineComponent, - AddonBlockTimelineEventsComponent - ], - entryComponents: [ - AddonBlockTimelineComponent, - AddonBlockTimelineEventsComponent - ] -}) -export class AddonBlockTimelineComponentsModule {} diff --git a/src/addon/block/timeline/components/events/addon-block-timeline-events.html b/src/addon/block/timeline/components/events/addon-block-timeline-events.html deleted file mode 100644 index 6b983e107..000000000 --- a/src/addon/block/timeline/components/events/addon-block-timeline-events.html +++ /dev/null @@ -1,36 +0,0 @@ - - -

{{ dayEvents.dayTimestamp * 1000 | coreFormatDate:"strftimedayshort" }}

-
- - - -

-

- -

- - - - {{event.timesort * 1000 | coreFormatDate:"strftimetime24" }} - - -
-
-
- -
- - - -
- - \ No newline at end of file diff --git a/src/addon/block/timeline/components/events/events.scss b/src/addon/block/timeline/components/events/events.scss deleted file mode 100644 index e284f0d24..000000000 --- a/src/addon/block/timeline/components/events/events.scss +++ /dev/null @@ -1,45 +0,0 @@ -ion-app.app-root core-courses-course-progress { - - .core-course-module-handler.item { - @include core-items(); - } - - .core-course-module-handler.item .item-heading:first-child { - margin-top: 0; - } -} - -ion-app.app-root addon-block-timeline-events { - a button { - pointer-events: auto; - } -} - -ion-app.app-root core-courses-course-progress addon-block-timeline-events { - @include media-breakpoint-up(md) { - .hidden-tablet { - display: block !important; - opacity: 1 !important; - &.button[disabled] { - opacity: .4 !important; - } - } - .hidden-phone { - display: none !important; - opacity: 0 !important; - } - } - @include media-breakpoint-up(lg) { - .hidden-tablet { - display: none !important; - opacity: 0 !important; - } - .hidden-phone { - display: block !important; - opacity: 1 !important; - &.button[disabled] { - opacity: .4 !important; - } - } - } -} diff --git a/src/addon/block/timeline/components/events/events.ts b/src/addon/block/timeline/components/events/events.ts deleted file mode 100644 index 74424a614..000000000 --- a/src/addon/block/timeline/components/events/events.ts +++ /dev/null @@ -1,143 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, OnChanges, EventEmitter, SimpleChange, Optional } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import * as moment from 'moment'; - -/** - * Directive to render a list of events in course overview. - */ -@Component({ - selector: 'addon-block-timeline-events', - templateUrl: 'addon-block-timeline-events.html' -}) -export class AddonBlockTimelineEventsComponent implements OnChanges { - @Input() events = []; // The events to render. - @Input() showCourse?: boolean | string; // Whether to show the course name. - @Input() from: number; // Number of days from today to offset the events. - @Input() to?: number; // Number of days from today to limit the events to. If not defined, no limit. - @Input() canLoadMore?: boolean; // Whether more events can be loaded. - @Output() loadMore: EventEmitter; // Notify that more events should be loaded. - - empty: boolean; - loadingMore: boolean; - filteredEvents = []; - - constructor(@Optional() private navCtrl: NavController, private utils: CoreUtilsProvider, - private textUtils: CoreTextUtilsProvider, private domUtils: CoreDomUtilsProvider, - private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, - private contentLinksHelper: CoreContentLinksHelperProvider, private timeUtils: CoreTimeUtilsProvider) { - this.loadMore = new EventEmitter(); - } - - /** - * Detect changes on input properties. - */ - ngOnChanges(changes: {[name: string]: SimpleChange}): void { - this.showCourse = this.utils.isTrueOrOne(this.showCourse); - - if (changes.events || changes.from || changes.to) { - if (this.events && this.events.length > 0) { - const filteredEvents = this.filterEventsByTime(this.from, this.to); - this.empty = !filteredEvents || filteredEvents.length <= 0; - - const eventsByDay = {}; - filteredEvents.forEach((event) => { - const dayTimestamp = this.timeUtils.getMidnightForTimestamp(event.timesort); - if (eventsByDay[dayTimestamp]) { - eventsByDay[dayTimestamp].push(event); - } else { - eventsByDay[dayTimestamp] = [event]; - } - }); - - const todaysMidnight = this.timeUtils.getMidnightForTimestamp(); - this.filteredEvents = []; - Object.keys(eventsByDay).forEach((key) => { - const dayTimestamp = parseInt(key); - this.filteredEvents.push({ - color: dayTimestamp < todaysMidnight ? 'danger' : 'light', - dayTimestamp: dayTimestamp, - events: eventsByDay[dayTimestamp] - }); - }); - } else { - this.empty = true; - } - } - } - - /** - * Filter the events by time. - * - * @param start Number of days to start getting events from today. E.g. -1 will get events from yesterday. - * @param end Number of days after the start. - * @return Filtered events. - */ - protected filterEventsByTime(start: number, end?: number): any[] { - start = moment().add(start, 'days').startOf('day').unix(); - end = typeof end != 'undefined' ? moment().add(end, 'days').startOf('day').unix() : end; - - return this.events.filter((event) => { - if (end) { - return start <= event.timesort && event.timesort < end; - } - - return start <= event.timesort; - }).map((event) => { - event.iconUrl = this.courseProvider.getModuleIconSrc(event.icon.component); - - return event; - }); - } - - /** - * Load more events clicked. - */ - loadMoreEvents(): void { - this.loadingMore = true; - this.loadMore.emit(); - } - - /** - * Action clicked. - * - * @param e Click event. - * @param url Url of the action. - */ - action(e: Event, url: string): void { - e.preventDefault(); - e.stopPropagation(); - - // Fix URL format. - url = this.textUtils.decodeHTMLEntities(url); - - const modal = this.domUtils.showModalLoading(); - this.contentLinksHelper.handleLink(url, undefined, this.navCtrl).then((treated) => { - if (!treated) { - return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); - } - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/block/timeline/components/timeline/addon-block-timeline.html b/src/addon/block/timeline/components/timeline/addon-block-timeline.html deleted file mode 100644 index 62afe16e1..000000000 --- a/src/addon/block/timeline/components/timeline/addon-block-timeline.html +++ /dev/null @@ -1,35 +0,0 @@ - -

{{ 'addon.block_timeline.pluginname' | translate }}

- - - - -
- -
- - {{ 'core.all' | translate }} - {{ 'addon.block_timeline.overdue' | translate }} - {{ 'addon.block_timeline.duedate' | translate }} - {{ 'addon.block_timeline.next7days' | translate }} - {{ 'addon.block_timeline.next30days' | translate }} - {{ 'addon.block_timeline.next3months' | translate }} - {{ 'addon.block_timeline.next6months' | translate }} - -
- - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/src/addon/block/timeline/components/timeline/timeline.ts b/src/addon/block/timeline/components/timeline/timeline.ts deleted file mode 100644 index b585ede3e..000000000 --- a/src/addon/block/timeline/components/timeline/timeline.ts +++ /dev/null @@ -1,224 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector } from '@angular/core'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; -import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; -import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component'; -import { AddonBlockTimelineProvider } from '../../providers/timeline'; -import { AddonCalendarEvent } from '@addon/calendar/providers/calendar'; - -/** - * Component to render a timeline block. - */ -@Component({ - selector: 'addon-block-timeline', - templateUrl: 'addon-block-timeline.html' -}) -export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implements OnInit { - sort = 'sortbydates'; - filter = 'next30days'; - currentSite: any; - timeline = { - events: [], - loaded: false, - canLoadMore: undefined - }; - timelineCourses = { - courses: [], - loaded: false, - canLoadMore: false - }; - dataFrom: number; - dataTo: number; - - protected courseIds = []; - protected fetchContentDefaultError = 'Error getting timeline data.'; - - constructor(injector: Injector, private coursesProvider: CoreCoursesProvider, - private timelineProvider: AddonBlockTimelineProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, - private coursesHelper: CoreCoursesHelperProvider, private sitesProvider: CoreSitesProvider, - private timeUtils: CoreTimeUtilsProvider) { - - super(injector, 'AddonBlockTimelineComponent'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.currentSite = this.sitesProvider.getCurrentSite(); - this.currentSite.getLocalSiteConfig('AddonBlockTimelineFilter', this.filter).then((value) => { - this.filter = value; - this.switchFilter(); - }); - this.currentSite.getLocalSiteConfig('AddonBlockTimelineSort', this.sort).then((value) => { - this.sort = value; - super.ngOnInit(); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.timelineProvider.invalidateActionEventsByTimesort()); - promises.push(this.timelineProvider.invalidateActionEventsByCourses()); - promises.push(this.coursesProvider.invalidateUserCourses()); - promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); - if (this.courseIds.length > 0) { - promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds.join(','))); - } - - return this.utils.allPromises(promises); - } - - /** - * Fetch the courses for my overview. - * - * @return Promise resolved when done. - */ - protected fetchContent(): Promise { - if (this.sort == 'sortbydates') { - return this.fetchMyOverviewTimeline().finally(() => { - this.timeline.loaded = true; - }); - } else if (this.sort == 'sortbycourses') { - return this.fetchMyOverviewTimelineByCourses().finally(() => { - this.timelineCourses.loaded = true; - }); - } - } - - /** - * Load more events. - */ - loadMoreTimeline(): Promise { - return this.fetchMyOverviewTimeline(this.timeline.canLoadMore).catch((error) => { - this.domUtils.showErrorModalDefault(error, this.fetchContentDefaultError); - }); - } - - /** - * Load more events. - * - * @param course Course. - * @return Promise resolved when done. - */ - loadMoreCourse(course: any): Promise { - return this.timelineProvider.getActionEventsByCourse(course.id, course.canLoadMore).then((courseEvents) => { - course.events = course.events.concat(courseEvents.events); - course.canLoadMore = courseEvents.canLoadMore; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, this.fetchContentDefaultError); - }); - } - - /** - * Fetch the timeline. - * - * @param afterEventId The last event id. - * @return Promise resolved when done. - */ - protected fetchMyOverviewTimeline(afterEventId?: number): Promise { - return this.timelineProvider.getActionEventsByTimesort(afterEventId).then((events) => { - this.timeline.events = events.events; - this.timeline.canLoadMore = events.canLoadMore; - }); - } - - /** - * Fetch the timeline by courses. - * - * @return Promise resolved when done. - */ - protected fetchMyOverviewTimelineByCourses(): Promise { - return this.coursesHelper.getUserCoursesWithOptions().then((courses) => { - const today = this.timeUtils.timestamp(); - courses = courses.filter((course) => { - return course.startdate <= today && (!course.enddate || course.enddate >= today); - }); - - this.timelineCourses.courses = courses; - if (courses.length > 0) { - this.courseIds = courses.map((course) => { - return course.id; - }); - - return this.timelineProvider.getActionEventsByCourses(this.courseIds).then((courseEvents) => { - this.timelineCourses.courses.forEach((course) => { - course.events = courseEvents[course.id].events; - course.canLoadMore = courseEvents[course.id].canLoadMore; - }); - }); - } - }); - } - - /** - * Change timeline filter being viewed. - */ - switchFilter(): void { - this.currentSite.setLocalSiteConfig('AddonBlockTimelineFilter', this.filter); - switch (this.filter) { - case 'overdue': - this.dataFrom = -14; - this.dataTo = 0; - break; - case 'next7days': - this.dataFrom = 0; - this.dataTo = 7; - break; - case 'next30days': - this.dataFrom = 0; - this.dataTo = 30; - break; - case 'next3months': - this.dataFrom = 0; - this.dataTo = 90; - break; - case 'next6months': - this.dataFrom = 0; - this.dataTo = 180; - break; - default: - case 'all': - this.dataFrom = -14; - this.dataTo = undefined; - break; - } - } - - /** - * Change timeline sort being viewed. - * - * @param sort New sorting. - */ - switchSort(sort: string): void { - this.sort = sort; - this.currentSite.setLocalSiteConfig('AddonBlockTimelineSort', this.sort); - if (!this.timeline.loaded && this.sort == 'sortbydates') { - this.fetchContent(); - } else if (!this.timelineCourses.loaded && this.sort == 'sortbycourses') { - this.fetchContent(); - } - } -} diff --git a/src/addon/block/timeline/lang/en.json b/src/addon/block/timeline/lang/en.json deleted file mode 100644 index fbd482f47..000000000 --- a/src/addon/block/timeline/lang/en.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "duedate": "Due date", - "next30days": "Next 30 days", - "next3months": "Next 3 months", - "next6months": "Next 6 months", - "next7days": "Next 7 days", - "nocoursesinprogress": "No in-progress courses", - "noevents": "No upcoming activities due", - "overdue": "Overdue", - "pluginname": "Timeline", - "sortbycourses": "Sort by courses", - "sortbydates": "Sort by dates" -} \ No newline at end of file diff --git a/src/addon/block/timeline/providers/block-handler.ts b/src/addon/block/timeline/providers/block-handler.ts deleted file mode 100644 index 55b201bc3..000000000 --- a/src/addon/block/timeline/providers/block-handler.ts +++ /dev/null @@ -1,69 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreBlockHandlerData } from '@core/block/providers/delegate'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { AddonBlockTimelineProvider } from '@addon/block/timeline/providers/timeline'; -import { AddonBlockTimelineComponent } from '../components/timeline/timeline'; -import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler'; - -/** - * Block handler. - */ -@Injectable() -export class AddonBlockTimelineHandler extends CoreBlockBaseHandler { - name = 'AddonBlockTimeline'; - blockName = 'timeline'; - - constructor(private timelineProvider: AddonBlockTimelineProvider, private coursesProvider: CoreCoursesProvider, - private sitesProvider: CoreSitesProvider) { - - super(); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.timelineProvider.isAvailable().then((enabled) => { - const currentSite = this.sitesProvider.getCurrentSite(); - - return enabled && ((currentSite && currentSite.isVersionGreaterEqualThan('3.6')) || - !this.coursesProvider.isMyCoursesDisabledInSite()); - }); - } - - /** - * Returns the data needed to render the block. - * - * @param injector Injector. - * @param block The block to render. - * @param contextLevel The context where the block will be used. - * @param instanceId The instance ID associated with the context level. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number) - : CoreBlockHandlerData | Promise { - - return { - title: 'addon.block_timeline.pluginname', - class: 'addon-block-timeline', - component: AddonBlockTimelineComponent - }; - } -} diff --git a/src/addon/block/timeline/providers/timeline.ts b/src/addon/block/timeline/providers/timeline.ts deleted file mode 100644 index 95148ce26..000000000 --- a/src/addon/block/timeline/providers/timeline.ts +++ /dev/null @@ -1,265 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesDashboardProvider } from '@core/courses/providers/dashboard'; -import { AddonCalendarEvents, AddonCalendarEventsGroupedByCourse, AddonCalendarEvent } from '@addon/calendar/providers/calendar'; -import * as moment from 'moment'; - -/** - * Service that provides some features regarding course overview. - */ -@Injectable() -export class AddonBlockTimelineProvider { - static EVENTS_LIMIT = 20; - static EVENTS_LIMIT_PER_COURSE = 10; - // Cache key was maintained when moving the functions to this file. It comes from core myoverview. - protected ROOT_CACHE_KEY = 'myoverview:'; - - constructor(private sitesProvider: CoreSitesProvider, private dashboardProvider: CoreCoursesDashboardProvider) { } - - /** - * Get calendar action events for the given course. - * - * @param courseId Only events in this course. - * @param afterEventId The last seen event id. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved when the info is retrieved. - */ - getActionEventsByCourse(courseId: number, afterEventId?: number, siteId?: string): - Promise<{ events: AddonCalendarEvent[], canLoadMore: number }> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const time = moment().subtract(14, 'days').unix(), // Check two weeks ago. - data: any = { - timesortfrom: time, - courseid: courseId, - limitnum: AddonBlockTimelineProvider.EVENTS_LIMIT_PER_COURSE - }, - preSets = { - cacheKey: this.getActionEventsByCourseCacheKey(courseId) - }; - - if (afterEventId) { - data.aftereventid = afterEventId; - } - - return site.read('core_calendar_get_action_events_by_course', data, preSets) - .then((courseEvents: AddonCalendarEvents): any => { - - if (courseEvents && courseEvents.events) { - return this.treatCourseEvents(courseEvents, time); - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get calendar action events for the given course value WS call. - * - * @param courseId Only events in this course. - * @return Cache key. - */ - protected getActionEventsByCourseCacheKey(courseId: number): string { - return this.getActionEventsByCoursesCacheKey() + ':' + courseId; - } - - /** - * Get calendar action events for a given list of courses. - * - * @param courseIds Course IDs. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved when the info is retrieved. - */ - getActionEventsByCourses(courseIds: number[], siteId?: string): Promise<{ [courseId: string]: - { events: AddonCalendarEvent[], canLoadMore: number } }> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const time = moment().subtract(14, 'days').unix(), // Check two weeks ago. - data = { - timesortfrom: time, - courseids: courseIds, - limitnum: AddonBlockTimelineProvider.EVENTS_LIMIT_PER_COURSE - }, - preSets = { - cacheKey: this.getActionEventsByCoursesCacheKey() - }; - - return site.read('core_calendar_get_action_events_by_courses', data, preSets) - .then((events: AddonCalendarEventsGroupedByCourse): any => { - - if (events && events.groupedbycourse) { - const courseEvents = {}; - - events.groupedbycourse.forEach((course) => { - courseEvents[course.courseid] = this.treatCourseEvents(course, time); - }); - - return courseEvents; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get calendar action events for a given list of courses value WS call. - * - * @return Cache key. - */ - protected getActionEventsByCoursesCacheKey(): string { - return this.ROOT_CACHE_KEY + 'bycourse'; - } - - /** - * Get calendar action events based on the timesort value. - * - * @param afterEventId The last seen event id. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved when the info is retrieved. - */ - getActionEventsByTimesort(afterEventId: number, siteId?: string): - Promise<{ events: AddonCalendarEvent[], canLoadMore: number }> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const time = moment().subtract(14, 'days').unix(), // Check two weeks ago. - data: any = { - timesortfrom: time, - limitnum: AddonBlockTimelineProvider.EVENTS_LIMIT - }, - preSets = { - cacheKey: this.getActionEventsByTimesortCacheKey(afterEventId, data.limitnum), - getCacheUsingCacheKey: true, - uniqueCacheKey: true - }; - - if (afterEventId) { - data.aftereventid = afterEventId; - } - - return site.read('core_calendar_get_action_events_by_timesort', data, preSets) - .then((result: AddonCalendarEvents): any => { - - if (result && result.events) { - const canLoadMore = result.events.length >= data.limitnum ? result.lastid : undefined; - - // Filter events by time in case it uses cache. - const events = result.events.filter((element) => { - return element.timesort >= time; - }); - - return { - events: events, - canLoadMore: canLoadMore - }; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get prefix cache key for calendar action events based on the timesort value WS calls. - * - * @return Cache key. - */ - protected getActionEventsByTimesortPrefixCacheKey(): string { - return this.ROOT_CACHE_KEY + 'bytimesort:'; - } - - /** - * Get cache key for get calendar action events based on the timesort value WS call. - * - * @param afterEventId The last seen event id. - * @param limit Limit num of the call. - * @return Cache key. - */ - protected getActionEventsByTimesortCacheKey(afterEventId?: number, limit?: number): string { - afterEventId = afterEventId || 0; - limit = limit || 0; - - return this.getActionEventsByTimesortPrefixCacheKey() + afterEventId + ':' + limit; - } - - /** - * Invalidates get calendar action events for a given list of courses WS call. - * - * @param siteId Site ID to invalidate. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateActionEventsByCourses(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getActionEventsByCoursesCacheKey()); - }); - } - - /** - * Invalidates get calendar action events based on the timesort value WS call. - * - * @param siteId Site ID to invalidate. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateActionEventsByTimesort(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getActionEventsByTimesortPrefixCacheKey()); - }); - } - - /** - * Returns whether or not My Overview is available for a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if available, resolved with false or rejected otherwise. - */ - isAvailable(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - // First check if dashboard is disabled. - if (this.dashboardProvider.isDisabledInSite(site)) { - return false; - } - - return site.wsAvailable('core_calendar_get_action_events_by_courses') && - site.wsAvailable('core_calendar_get_action_events_by_timesort'); - }); - } - - /** - * Handles course events, filtering and treating if more can be loaded. - * - * @param course Object containing response course events info. - * @param timeFrom Current time to filter events from. - * @return Object with course events and last loaded event id if more can be loaded. - */ - protected treatCourseEvents(course: AddonCalendarEvents, timeFrom: number): - { events: AddonCalendarEvent[], canLoadMore: number } { - - const canLoadMore: number = - course.events.length >= AddonBlockTimelineProvider.EVENTS_LIMIT_PER_COURSE ? course.lastid : undefined; - - // Filter events by time in case it uses cache. - course.events = course.events.filter((element) => { - return element.timesort >= timeFrom; - }); - - return { - events: course.events, - canLoadMore: canLoadMore - }; - } -} diff --git a/src/addon/block/timeline/timeline.module.ts b/src/addon/block/timeline/timeline.module.ts deleted file mode 100644 index 084de8980..000000000 --- a/src/addon/block/timeline/timeline.module.ts +++ /dev/null @@ -1,42 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreBlockDelegate } from '@core/block/providers/delegate'; -import { AddonBlockTimelineComponentsModule } from './components/components.module'; -import { AddonBlockTimelineProvider } from './providers/timeline'; -import { AddonBlockTimelineHandler } from './providers/block-handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule, - AddonBlockTimelineComponentsModule, - TranslateModule.forChild() - ], - exports: [ - ], - providers: [ - AddonBlockTimelineProvider, - AddonBlockTimelineHandler - ] -}) -export class AddonBlockTimelineModule { - constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockTimelineHandler) { - blockDelegate.registerHandler(blockHandler); - } -} diff --git a/src/addon/blog/blog.module.ts b/src/addon/blog/blog.module.ts deleted file mode 100644 index 0d642e012..000000000 --- a/src/addon/blog/blog.module.ts +++ /dev/null @@ -1,56 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -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: [ - ], - imports: [ - AddonBlogComponentsModule - ], - providers: [ - AddonBlogProvider, - AddonBlogMainMenuHandler, - AddonBlogUserHandler, - AddonBlogCourseOptionHandler, - AddonBlogIndexLinkHandler, - AddonBlogTagAreaHandler - ] -}) -export class AddonBlogModule { - constructor(mainMenuDelegate: CoreMainMenuDelegate, menuHandler: AddonBlogMainMenuHandler, - userHandler: AddonBlogUserHandler, userDelegate: CoreUserDelegate, - courseOptionHandler: AddonBlogCourseOptionHandler, courseOptionsDelegate: CoreCourseOptionsDelegate, - linkHandler: AddonBlogIndexLinkHandler, contentLinksDelegate: CoreContentLinksDelegate, - tagAreaDelegate: CoreTagAreaDelegate, tagAreaHandler: AddonBlogTagAreaHandler) { - mainMenuDelegate.registerHandler(menuHandler); - userDelegate.registerHandler(userHandler); - courseOptionsDelegate.registerHandler(courseOptionHandler); - contentLinksDelegate.registerHandler(linkHandler); - tagAreaDelegate.registerHandler(tagAreaHandler); - } -} diff --git a/src/addon/blog/components/components.module.ts b/src/addon/blog/components/components.module.ts deleted file mode 100644 index f8deceac2..000000000 --- a/src/addon/blog/components/components.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -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({ - declarations: [ - AddonBlogEntriesComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreCommentsComponentsModule, - CoreTagComponentsModule - ], - providers: [ - ], - exports: [ - AddonBlogEntriesComponent - ], - entryComponents: [ - AddonBlogEntriesComponent - ] -}) -export class AddonBlogComponentsModule {} diff --git a/src/addon/blog/components/entries/addon-blog-entries.html b/src/addon/blog/components/entries/addon-blog-entries.html deleted file mode 100644 index a77b45b2f..000000000 --- a/src/addon/blog/components/entries/addon-blog-entries.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - {{ 'addon.blog.showonlyyourentries' | translate }} - - - - - - - -

- - - {{ 'addon.blog.' + entry.publishTranslated | translate}} - -

-

- - {{entry.created | coreDateDayOrTime}} - - {{entry.user && entry.user.fullname}} -

-
- - - - - -
{{ 'core.tag.tags' | translate }}:
- -
- - - - - {{ 'addon.blog.linktooriginalentry' | translate }} -
- - - - {{entry.lastmodified | coreTimeAgo}} - - - -
-
- -
-
diff --git a/src/addon/blog/components/entries/entries.ts b/src/addon/blog/components/entries/entries.ts deleted file mode 100644 index 50b71f10c..000000000 --- a/src/addon/blog/components/entries/entries.ts +++ /dev/null @@ -1,281 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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 { CoreTextUtils } from '@providers/utils/text'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonBlogProvider, AddonBlogPost } from '../../providers/blog'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { CoreTagProvider } from '@core/tag/providers/tag'; - -/** - * Component that displays the blog entries. - */ -@Component({ - selector: 'addon-blog-entries', - templateUrl: 'addon-blog-entries.html', -}) -export class AddonBlogEntriesComponent implements OnInit { - @Input() userId?: number; - @Input() courseId?: number; - @Input() cmId?: number; - @Input() entryId?: number; - @Input() groupId?: number; - @Input() tagId?: number; - - protected filter = {}; - protected pageLoaded = 0; - protected userPageLoaded = 0; - protected canLoadMoreEntries = false; - protected canLoadMoreUserEntries = true; - protected siteHomeId: number; - - @ViewChild(Content) content: Content; - - loaded = false; - canLoadMore = false; - loadMoreError = false; - entries: AddonBlogPostFormatted[] = []; - currentUserId: number; - showMyEntriesToggle = false; - onlyMyEntries = false; - component = AddonBlogProvider.COMPONENT; - commentsEnabled: boolean; - tagsEnabled: boolean; - contextLevel: string; - contextInstanceId: number; - - constructor(protected blogProvider: AddonBlogProvider, protected domUtils: CoreDomUtilsProvider, - protected userProvider: CoreUserProvider, sitesProvider: CoreSitesProvider, protected utils: CoreUtilsProvider, - protected commentsProvider: CoreCommentsProvider, private tagProvider: CoreTagProvider) { - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - this.siteHomeId = sitesProvider.getCurrentSiteHomeId(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (this.userId) { - this.filter['userid'] = this.userId; - } - this.showMyEntriesToggle = !this.userId; - - if (this.courseId) { - this.filter['courseid'] = this.courseId; - } - - if (this.cmId) { - this.filter['cmid'] = this.cmId; - } - - if (this.entryId) { - this.filter['entryid'] = this.entryId; - } - - if (this.groupId) { - this.filter['groupid'] = this.groupId; - } - - if (this.tagId) { - this.filter['tagid'] = this.tagId; - } - - // Calculate the context level. - if (this.userId && !this.courseId && !this.cmId) { - this.contextLevel = 'user'; - this.contextInstanceId = this.userId; - } else if (this.courseId && this.courseId != this.siteHomeId) { - this.contextLevel = 'course'; - this.contextInstanceId = this.courseId; - } else { - this.contextLevel = 'system'; - this.contextInstanceId = 0; - } - - this.commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(); - this.tagsEnabled = this.tagProvider.areTagsAvailableInSite(); - - this.fetchEntries().then(() => { - this.blogProvider.logView(this.filter).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Fetch blog entries. - * - * @param refresh Empty events array first. - * @return Promise with the entries. - */ - private fetchEntries(refresh: boolean = false): Promise { - this.loadMoreError = false; - - if (refresh) { - this.pageLoaded = 0; - this.userPageLoaded = 0; - } - - const loadPage = this.onlyMyEntries ? this.userPageLoaded : this.pageLoaded; - - return this.blogProvider.getEntries(this.filter, loadPage).then((result) => { - const promises = result.entries.map((entry: AddonBlogPostFormatted) => { - switch (entry.publishstate) { - case 'draft': - entry.publishTranslated = 'publishtonoone'; - break; - case 'site': - entry.publishTranslated = 'publishtosite'; - break; - case 'public': - entry.publishTranslated = 'publishtoworld'; - break; - default: - entry.publishTranslated = 'privacy:unknown'; - break; - } - - // Calculate the context. This code was inspired by calendar events, Moodle doesn't do this for blogs. - if (entry.moduleid || entry.coursemoduleid) { - entry.contextLevel = 'module'; - entry.contextInstanceId = entry.moduleid || entry.coursemoduleid; - } else if (entry.courseid) { - entry.contextLevel = 'course'; - entry.contextInstanceId = entry.courseid; - } else { - entry.contextLevel = 'user'; - entry.contextInstanceId = entry.userid; - } - - entry.summary = CoreTextUtils.instance.replacePluginfileUrls(entry.summary, entry.summaryfiles || []); - - return this.userProvider.getProfile(entry.userid, entry.courseid, true).then((user) => { - entry.user = user; - }).catch(() => { - // Ignore errors. - }); - }); - - if (refresh) { - this.entries = result.entries; - } else { - this.entries = this.utils.uniqueArray(this.entries.concat(result.entries), 'id').sort((a, b) => { - return b.created - a.created; - }); - } - - 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) => { - this.domUtils.showErrorModalDefault(message, 'addon.blog.errorloadentries', true); - this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading. - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Toggle between showing only my entries or not. - * - * @param 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. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Resolved when done. - */ - loadMore(infiniteComplete?: any): Promise { - return this.fetchEntries().finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Refresh blog entries on PTR. - * - * @param refresher Refresher instance. - */ - refresh(refresher?: any): void { - 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(); - } - }); - }); - } -} - -/** - * Blog post with some calculated data. - */ -type AddonBlogPostFormatted = AddonBlogPost & { - publishTranslated?: string; // Calculated in the app. Key of the string to translate the publish state of the post. - user?: any; // Calculated in the app. Data of the user that wrote the post. - contextLevel?: string; // Calculated in the app. The context level of the entry. - contextInstanceId?: number; // Calculated in the app. The context instance id. -}; diff --git a/src/addon/blog/lang/en.json b/src/addon/blog/lang/en.json deleted file mode 100644 index 6e183232f..000000000 --- a/src/addon/blog/lang/en.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "blog": "Blog", - "blogentries": "Blog entries", - "errorloadentries": "Error loading blog entries.", - "linktooriginalentry": "Link to original blog entry", - "noentriesyet": "No visible entries here", - "publishtonoone": "Yourself (draft)", - "publishtosite": "Anyone on this site", - "publishtoworld": "Anyone in the world", - "showonlyyourentries": "Show only your entries", - "siteblogheading": "Site blog" -} \ No newline at end of file diff --git a/src/addon/blog/pages/entries/entries.html b/src/addon/blog/pages/entries/entries.html deleted file mode 100644 index e5a12aba8..000000000 --- a/src/addon/blog/pages/entries/entries.html +++ /dev/null @@ -1,7 +0,0 @@ - - - {{ title | translate }} - - - - diff --git a/src/addon/blog/pages/entries/entries.module.ts b/src/addon/blog/pages/entries/entries.module.ts deleted file mode 100644 index c63779472..000000000 --- a/src/addon/blog/pages/entries/entries.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonBlogEntriesPage } from './entries'; -import { AddonBlogComponentsModule } from '../../components/components.module'; - -@NgModule({ - declarations: [ - AddonBlogEntriesPage, - ], - imports: [ - CoreDirectivesModule, - AddonBlogComponentsModule, - IonicPageModule.forChild(AddonBlogEntriesPage), - TranslateModule.forChild() - ], -}) -export class AddonBlogEntriesPageModule {} diff --git a/src/addon/blog/pages/entries/entries.ts b/src/addon/blog/pages/entries/entries.ts deleted file mode 100644 index cab44eb7f..000000000 --- a/src/addon/blog/pages/entries/entries.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; - -/** - * Page that displays the list of blog entries. - */ -@IonicPage({ segment: 'addon-blog-entries' }) -@Component({ - selector: 'page-addon-blog-entries', - templateUrl: 'entries.html', -}) -export class AddonBlogEntriesPage { - userId: number; - courseId: number; - cmId: number; - entryId: number; - groupId: number; - tagId: number; - title: string; - - constructor(params: NavParams) { - this.userId = params.get('userId'); - this.courseId = params.get('courseId'); - this.cmId = params.get('cmId'); - this.entryId = params.get('entryId'); - this.groupId = params.get('groupId'); - this.tagId = params.get('tagId'); - - if (!this.userId && !this.courseId && !this.cmId && !this.entryId && !this.groupId && !this.tagId) { - this.title = 'addon.blog.siteblogheading'; - } else { - this.title = 'addon.blog.blogentries'; - } - } -} diff --git a/src/addon/blog/providers/blog.ts b/src/addon/blog/providers/blog.ts deleted file mode 100644 index c57da1044..000000000 --- a/src/addon/blog/providers/blog.ts +++ /dev/null @@ -1,166 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; -import { CoreTagItem } from '@core/tag/providers/tag'; - -/** - * Service to handle blog entries. - */ -@Injectable() -export class AddonBlogProvider { - static ENTRIES_PER_PAGE = 10; - static COMPONENT = 'blog'; - protected ROOT_CACHE_KEY = 'addonBlog:'; - protected logger; - - constructor(logger: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, protected utils: CoreUtilsProvider, - protected pushNotificationsProvider: CorePushNotificationsProvider) { - this.logger = logger.getInstance('AddonBlogProvider'); - } - - /** - * Returns whether or not the blog plugin is enabled for a certain site. - * - * This method is called quite often and thus should only perform a quick - * check, we should not be calling WS from here. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if enabled, resolved with false or rejected otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('core_blog_get_entries') && - site.canUseAdvancedFeature('enableblogs'); - }); - } - - /** - * Get the cache key for the blog entries. - * - * @param filter Filter to apply on search. - * @return Cache key. - */ - getEntriesCacheKey(filter: any = {}): string { - return this.ROOT_CACHE_KEY + this.utils.sortAndStringify(filter); - } - - /** - * Get blog entries. - * - * @param filter Filter to apply on search. - * @param page Page of the blog entries to fetch. - * @param siteId Site ID. If not defined, current site. - * @return Promise to be resolved when the entries are retrieved. - */ - getEntries(filter: any = {}, page: number = 0, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - filters: this.utils.objectToArrayOfObjects(filter, 'name', 'value'), - page: page, - perpage: AddonBlogProvider.ENTRIES_PER_PAGE - }; - - const preSets = { - cacheKey: this.getEntriesCacheKey(filter), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - return site.read('core_blog_get_entries', data, preSets); - }); - } - - /** - * Invalidate blog entries WS call. - * - * @param filter Filter to apply on search - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateEntries(filter: any = {}, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getEntriesCacheKey(filter)); - }); - } - - /** - * Trigger the blog_entries_viewed event. - * - * @param filter Filter to apply on search. - * @param siteId Site ID. If not defined, current site. - * @return Promise to be resolved when done. - */ - logView(filter: any = {}, siteId?: string): Promise { - this.pushNotificationsProvider.logViewListEvent('blog', 'core_blog_view_entries', filter, siteId); - - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - filters: this.utils.objectToArrayOfObjects(filter, 'name', 'value') - }; - - return site.write('core_blog_view_entries', data); - }); - } -} - -/** - * Data returned by blog's post_exporter. - */ -export type AddonBlogPost = { - id: number; // Post/entry id. - module: string; // Where it was published the post (blog, blog_external...). - userid: number; // Post author. - courseid: number; // Course where the post was created. - groupid: number; // Group post was created for. - moduleid: number; // Module id where the post was created (not used anymore). - coursemoduleid: number; // Course module id where the post was created. - subject: string; // Post subject. - summary: string; // Post summary. - summaryformat: number; // Summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - content: string; // Post content. - uniquehash: string; // Post unique hash. - rating: number; // Post rating. - format: number; // Post content format. - attachment: string; // Post atachment. - publishstate: string; // Post publish state. - lastmodified: number; // When it was last modified. - created: number; // When it was created. - usermodified: number; // User that updated the post. - summaryfiles: CoreWSExternalFile[]; // Summaryfiles. - attachmentfiles?: CoreWSExternalFile[]; // Attachmentfiles. - tags?: CoreTagItem[]; // @since 3.7. Tags. -}; - -/** - * Result of WS core_blog_get_entries. - */ -export type AddonBlogGetEntriesResult = { - entries: AddonBlogPost[]; - totalentries: number; // The total number of entries found. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS core_blog_view_entries. - */ -export type AddonBlogViewEntriesResult = { - status: boolean; // Status: true if success. - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/blog/providers/course-option-handler.ts b/src/addon/blog/providers/course-option-handler.ts deleted file mode 100644 index 7632c8df7..000000000 --- a/src/addon/blog/providers/course-option-handler.ts +++ /dev/null @@ -1,121 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonBlogEntriesComponent } from '../components/entries/entries'; -import { AddonBlogProvider } from './blog'; -import { CoreWSExternalFile } from '@providers/ws'; - -/** - * Course nav handler. - */ -@Injectable() -export class AddonBlogCourseOptionHandler implements CoreCourseOptionsHandler { - name = 'AddonBlog'; - priority = 100; - - constructor(protected coursesProvider: CoreCoursesProvider, protected blogProvider: AddonBlogProvider, - protected courseHelper: CoreCourseHelperProvider, protected courseProvider: CoreCourseProvider, - protected sitesProvider: CoreSitesProvider, protected filepoolProvider: CoreFilepoolProvider) {} - - /** - * Should invalidate the data to determine if the handler is enabled for a certain course. - * - * @param courseId The course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved when done. - */ - invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise { - return this.courseProvider.invalidateCourseBlocks(courseId); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.blogProvider.isPluginEnabled(); - } - - /** - * Whether or not the handler is enabled for a certain course. - * - * @param courseId The course ID. - * @param accessData Access type and data. Default, guest, ... - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return True or promise resolved with true if enabled. - */ - isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { - return this.courseHelper.hasABlockNamed(courseId, 'blog_menu').then((enabled) => { - if (enabled && navOptions && typeof navOptions.blogs != 'undefined') { - return navOptions.blogs; - } - - return enabled; - }); - } - - /** - * Returns the data needed to render the handler. - * - * @param injector Injector. - * @param course The course. - * @return Data or promise resolved with the data. - */ - getDisplayData(injector: Injector, course: any): CoreCourseOptionsHandlerData | Promise { - return { - title: 'addon.blog.blog', - class: 'addon-blog-handler', - component: AddonBlogEntriesComponent - }; - } - - /** - * Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline. - * - * @param course The course. - * @return Promise resolved when done. - */ - prefetch(course: any): Promise { - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.blogProvider.getEntries({courseid: course.id}).then((result) => { - return result.entries.map((entry) => { - let files: CoreWSExternalFile[] = []; - - if (entry.attachmentfiles && entry.attachmentfiles.length) { - files = entry.attachmentfiles; - } - if (entry.summaryfiles && entry.summaryfiles.length) { - files = files.concat(entry.summaryfiles); - } - - if (files.length > 0) { - return this.filepoolProvider.addFilesToQueue(siteId, files, entry.module, entry.id); - } - - return Promise.resolve(); - }); - }); - } -} diff --git a/src/addon/blog/providers/index-link-handler.ts b/src/addon/blog/providers/index-link-handler.ts deleted file mode 100644 index dd613b72e..000000000 --- a/src/addon/blog/providers/index-link-handler.ts +++ /dev/null @@ -1,75 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonBlogProvider } from './blog'; - -/** - * Handler to treat links to blog page. - */ -@Injectable() -export class AddonBlogIndexLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonBlogIndexLinkHandler'; - featureName = 'CoreUserDelegate_AddonBlog:blogs'; - pattern = /\/blog\/index\.php/; - - constructor(private blogProvider: AddonBlogProvider, private linkHelper: CoreContentLinksHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - const pageParams: any = {}; - - params.userid ? pageParams['userId'] = parseInt(params.userid, 10) : null; - params.modid ? pageParams['cmId'] = parseInt(params.modid, 10) : null; - params.courseid ? pageParams['courseId'] = parseInt(params.courseid, 10) : null; - params.entryid ? pageParams['entryId'] = parseInt(params.entryid, 10) : null; - params.groupid ? pageParams['groupId'] = parseInt(params.groupid, 10) : null; - params.tagid ? pageParams['tagId'] = parseInt(params.tagid, 10) : null; - - return [{ - action: (siteId, navCtrl?): void => { - this.linkHelper.goInSite(navCtrl, 'AddonBlogEntriesPage', pageParams, siteId, !Object.keys(pageParams).length); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - - return this.blogProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/blog/providers/mainmenu-handler.ts b/src/addon/blog/providers/mainmenu-handler.ts deleted file mode 100644 index 622adf650..000000000 --- a/src/addon/blog/providers/mainmenu-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonBlogProvider } from './blog'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; - -/** - * Handler to inject an option into main menu. - */ -@Injectable() -export class AddonBlogMainMenuHandler implements CoreMainMenuHandler { - name = 'AddonBlog'; - priority = 450; - - constructor(private blogProvider: AddonBlogProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.blogProvider.isPluginEnabled(); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreMainMenuHandlerData { - return { - icon: 'fa-newspaper-o', - title: 'addon.blog.siteblogheading', - page: 'AddonBlogEntriesPage', - class: 'addon-blog-handler' - }; - } -} diff --git a/src/addon/blog/providers/tag-area-handler.ts b/src/addon/blog/providers/tag-area-handler.ts deleted file mode 100644 index 0800db2f8..000000000 --- a/src/addon/blog/providers/tag-area-handler.ts +++ /dev/null @@ -1,58 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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 Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.blogProvider.isPluginEnabled(); - } - - /** - * Parses the rendered content of a tag index and returns the items. - * - * @param content Rendered content. - * @return Area items (or promise resolved with the items). - */ - parseContent(content: string): any[] | Promise { - return this.tagHelper.parseFeedContent(content); - } - - /** - * Get the component to use to display items. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return CoreTagFeedComponent; - } -} diff --git a/src/addon/blog/providers/user-handler.ts b/src/addon/blog/providers/user-handler.ts deleted file mode 100644 index b676c96b8..000000000 --- a/src/addon/blog/providers/user-handler.ts +++ /dev/null @@ -1,71 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonBlogProvider } from './blog'; - -/** - * Profile item handler. - */ -@Injectable() -export class AddonBlogUserHandler implements CoreUserProfileHandler { - name = 'AddonBlog:blogs'; - priority = 300; - type = CoreUserDelegate.TYPE_NEW_PAGE; - - constructor(protected linkHelper: CoreContentLinksHelperProvider, protected blogProvider: AddonBlogProvider) { - } - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.blogProvider.isPluginEnabled(); - } - - /** - * Check if handler is enabled for this user in this context. - * - * @param user User to check. - * @param courseId Course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved with true if enabled, resolved with false otherwise. - */ - isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { - return true; - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { - return { - icon: 'fa-newspaper-o', - title: 'addon.blog.blogentries', - class: 'addon-blog-handler', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - - this.linkHelper.goInSite(navCtrl, 'AddonBlogEntriesPage', { userId: user.id, courseId: courseId }); - } - }; - } -} diff --git a/src/addon/calendar/calendar.module.ts b/src/addon/calendar/calendar.module.ts deleted file mode 100644 index b0c7ecd7b..000000000 --- a/src/addon/calendar/calendar.module.ts +++ /dev/null @@ -1,91 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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 { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { AddonCalendarComponentsModule } from './components/components.module'; - -// List of providers (without handlers). -export const ADDON_CALENDAR_PROVIDERS: any[] = [ - AddonCalendarProvider, - AddonCalendarOfflineProvider, - AddonCalendarHelperProvider, - AddonCalendarSyncProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonCalendarComponentsModule, - ], - providers: [ - AddonCalendarProvider, - AddonCalendarOfflineProvider, - AddonCalendarHelperProvider, - AddonCalendarSyncProvider, - AddonCalendarMainMenuHandler, - AddonCalendarSyncCronHandler, - AddonCalendarViewLinkHandler - ] -}) -export class AddonCalendarModule { - constructor(mainMenuDelegate: CoreMainMenuDelegate, calendarHandler: AddonCalendarMainMenuHandler, - initDelegate: CoreInitDelegate, calendarProvider: AddonCalendarProvider, loginHelper: CoreLoginHelperProvider, - localNotificationsProvider: CoreLocalNotificationsProvider, - cronDelegate: CoreCronDelegate, syncHandler: AddonCalendarSyncCronHandler, - contentLinksDelegate: CoreContentLinksDelegate, viewLinkHandler: AddonCalendarViewLinkHandler) { - - mainMenuDelegate.registerHandler(calendarHandler); - cronDelegate.register(syncHandler); - contentLinksDelegate.registerHandler(viewLinkHandler); - - initDelegate.ready().then(() => { - calendarProvider.scheduleAllSitesEventsNotifications(); - }); - - localNotificationsProvider.registerClick(AddonCalendarProvider.COMPONENT, (data) => { - if (data.eventid) { - initDelegate.ready().then(() => { - calendarProvider.isDisabled(data.siteId).then((disabled) => { - if (disabled) { - // The calendar is disabled in the site, don't open it. - return; - } - - // 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); - }); - - }); - }); - } - }); - } -} diff --git a/src/addon/calendar/components/calendar/addon-calendar-calendar.html b/src/addon/calendar/components/calendar/addon-calendar-calendar.html deleted file mode 100644 index 460072acb..000000000 --- a/src/addon/calendar/components/calendar/addon-calendar-calendar.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - -

{{ periodName }}

-
- - - - - -
-
- - - - - - - {{ day.shortname | translate }} - {{ day.fullname | translate }} - - - - - - - -

{{ day.mday }}

- - -

- - -
- -

- - - - {{ event.timestart * 1000 | coreFormatDate: timeFormat }} - - {{event.name}} -

-
-

{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}

-
-
- -
-
- -
diff --git a/src/addon/calendar/components/calendar/calendar.scss b/src/addon/calendar/components/calendar/calendar.scss deleted file mode 100644 index 436e2d210..000000000 --- a/src/addon/calendar/components/calendar/calendar.scss +++ /dev/null @@ -1,225 +0,0 @@ - -$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, -ion-app.app-root addon-calendar-filter-popover { - - .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-filter-popover .item.addon-calendar-event { - ion-icon[item-start] + .item-inner { - @include margin-horizontal(8px, 0); - } - - > .icon { - width: 28px; - height: 28px; - line-height: 28px; - font-size: 20px; - - &.fa { - font-size: 16px; - &::before { - width: 1.8em; - } - } - } -} - -ion-app.app-root addon-calendar-calendar { - - .addon-calendar-navigation { - @include padding(5px, 10px, null, 10px); - } - - .addon-calendar-months { - background-color: white; - padding: 0; - @include darkmode() { - background-color: $gray-darker; - } - } - - .addon-calendar-day { - border-bottom: 1px solid $calendar-border-color; - @include border-end(1px, solid, $calendar-border-color); - overflow: hidden; - min-height: 60px; - cursor: pointer; - - &: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 { - cursor: auto; - background-color: $gray-lighter; - @include darkmode() { - background-color: $black; - } - } - - .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; - } -} \ No newline at end of file diff --git a/src/addon/calendar/components/calendar/calendar.ts b/src/addon/calendar/components/calendar/calendar.ts deleted file mode 100644 index 1881b860e..000000000 --- a/src/addon/calendar/components/calendar/calendar.ts +++ /dev/null @@ -1,515 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, OnInit, Input, OnChanges, DoCheck, SimpleChange, Output, EventEmitter, - KeyValueDiffers } 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, AddonCalendarWeek } from '../../providers/calendar'; -import { AddonCalendarHelperProvider, AddonCalendarFilter } 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, DoCheck, OnDestroy { - @Input() initialYear: number | string; // Initial year to load. - @Input() initialMonth: number | string; // Initial month to load. - @Input() filter: AddonCalendarFilter; // Filter to apply. - @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(); - @Output() onDayClicked = new EventEmitter<{day: number, month: number, year: number}>(); - - periodName: string; - weekDays: any[]; - weeks: AddonCalendarWeek[]; - 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; - protected differ: any; // To detect changes in the data input. - - // Observers. - protected undeleteEventObserver: any; - protected obsDefaultTimeChange: any; - - constructor(eventsProvider: CoreEventsProvider, - sitesProvider: CoreSitesProvider, - localNotificationsProvider: CoreLocalNotificationsProvider, - differs: KeyValueDiffers, - 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); - - this.differ = differs.find([]).create(); - } - - /** - * 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); - } - - /** - * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays). - */ - ngDoCheck(): void { - if (this.weeks) { - // Check if there's any change in the filter object. - const changes = this.differ.diff(this.filter); - if (changes) { - this.filterEvents(); - } - } - } - - /** - * Fetch contacts. - * - * @param refresh True if we are refreshing events. - * @return Promise resolved when done. - */ - fetchData(refresh: boolean = false): Promise { - 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 resolved when done. - */ - fetchEvents(): Promise { - // 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.forEach((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); - }); - } - }); - }); - } - - // 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 resolved when done. - */ - protected loadCategories(): Promise { - 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 based on the filter popover. - */ - filterEvents(): void { - this.weeks.forEach((week) => { - week.days.forEach((day) => { - day.filteredEvents = this.calendarHelper.getFilteredEvents(day.events, this.filter, this.categories); - - // Re-calculate some properties. - this.calendarHelper.calculateDayData(day, day.filteredEvents); - }); - }); - } - - /** - * Refresh events. - * - * @param afterChange Whether the refresh is done after an event has changed or has been synced. - * @return Promise resolved when done. - */ - refreshData(afterChange?: boolean): Promise { - 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 calendarEvent Calendar event.. - * @param event Mouse event. - */ - eventClicked(calendarEvent: any, event: MouseEvent): void { - this.onEventClicked.emit(calendarEvent.id); - event.stopPropagation(); - } - - /** - * A day was clicked. - * - * @param 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 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 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 event Event object. - * @return 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(); - } -} diff --git a/src/addon/calendar/components/components.module.ts b/src/addon/calendar/components/components.module.ts deleted file mode 100644 index dacc846b9..000000000 --- a/src/addon/calendar/components/components.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCalendarCalendarComponent } from '../components/calendar/calendar'; -import { AddonCalendarUpcomingEventsComponent } from '../components/upcoming-events/upcoming-events'; -import { AddonCalendarFilterPopoverComponent } from '../components/filter/filter'; - -@NgModule({ - declarations: [ - AddonCalendarCalendarComponent, - AddonCalendarUpcomingEventsComponent, - AddonCalendarFilterPopoverComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule - ], - providers: [ - ], - exports: [ - AddonCalendarCalendarComponent, - AddonCalendarUpcomingEventsComponent, - AddonCalendarFilterPopoverComponent - ], - entryComponents: [ - AddonCalendarFilterPopoverComponent - ] -}) -export class AddonCalendarComponentsModule {} diff --git a/src/addon/calendar/components/filter/addon-calendar-filter-popover.html b/src/addon/calendar/components/filter/addon-calendar-filter-popover.html deleted file mode 100644 index 50ad865e1..000000000 --- a/src/addon/calendar/components/filter/addon-calendar-filter-popover.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - {{ 'addon.calendar.' + type + 'events' | translate}} - - - - - - - - - - diff --git a/src/addon/calendar/components/filter/filter.ts b/src/addon/calendar/components/filter/filter.ts deleted file mode 100644 index 8d0d5ee0e..000000000 --- a/src/addon/calendar/components/filter/filter.ts +++ /dev/null @@ -1,78 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { NavParams } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { AddonCalendarProvider } from '../../providers/calendar'; -import { AddonCalendarHelperProvider, AddonCalendarFilter } from '../../providers/helper'; - -/** - * Component to display the events filter that includes events types and a list of courses. - */ -@Component({ - selector: 'addon-calendar-filter-popover', - templateUrl: 'addon-calendar-filter-popover.html' -}) -export class AddonCalendarFilterPopoverComponent { - courses: any[]; - courseId = -1; - - types = {}; - typeIcons = {}; - typeKeys = []; - - constructor(navParams: NavParams, protected eventsProvider: CoreEventsProvider) { - this.courses = navParams.get('courses') || []; - const filter: AddonCalendarFilter = navParams.get('filter') || { - filtered: false, - courseId: null, - categoryId: null, - course: true, - group: true, - site: true, - user: true, - category: true - }; - this.courseId = filter.courseId || -1; - - this.typeKeys = AddonCalendarProvider.ALL_TYPES.map((name) => { - this.types[name] = filter[name]; - this.typeIcons[name] = AddonCalendarHelperProvider.EVENTICONS[name]; - - return name; - }); - } - - /** - * Function called when an item is clicked. - * - * @param event Click event. - */ - onChange(event: Event): void { - const filter = this.types; - if (this.courseId > 0) { - const course = this.courses.find((course) => this.courseId == course.id); - filter['courseId'] = course && course.id; - filter['categoryId'] = course && course.category; - } else { - filter['courseId'] = false; - filter['categoryId'] = false; - } - - filter['filtered'] = filter['courseId'] || AddonCalendarProvider.ALL_TYPES.some((name) => !this.types[name]); - - this.eventsProvider.trigger(AddonCalendarProvider.FILTER_CHANGED_EVENT, filter); - } -} diff --git a/src/addon/calendar/components/upcoming-events/addon-calendar-upcoming-events.html b/src/addon/calendar/components/upcoming-events/addon-calendar-upcoming-events.html deleted file mode 100644 index 9650b070c..000000000 --- a/src/addon/calendar/components/upcoming-events/addon-calendar-upcoming-events.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - -

-

- - - {{ 'core.notsent' | translate }} - - - - {{ 'core.deletedoffline' | translate }} - -
-
-
- -
diff --git a/src/addon/calendar/components/upcoming-events/upcoming-events.scss b/src/addon/calendar/components/upcoming-events/upcoming-events.scss deleted file mode 100644 index b8c06c58f..000000000 --- a/src/addon/calendar/components/upcoming-events/upcoming-events.scss +++ /dev/null @@ -1,5 +0,0 @@ -ion-app.app-root addon-calendar-upcoming-events { - .addon-calendar-event { - cursor: pointer; - } -} diff --git a/src/addon/calendar/components/upcoming-events/upcoming-events.ts b/src/addon/calendar/components/upcoming-events/upcoming-events.ts deleted file mode 100644 index cf0b64527..000000000 --- a/src/addon/calendar/components/upcoming-events/upcoming-events.ts +++ /dev/null @@ -1,335 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, OnInit, Input, DoCheck, Output, EventEmitter, KeyValueDiffers } 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 { AddonCalendarProvider, AddonCalendarCalendarEvent } from '../../providers/calendar'; -import { AddonCalendarHelperProvider, AddonCalendarFilter } from '../../providers/helper'; -import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreConstants } from '@core/constants'; - -/** - * Component that displays upcoming events. - */ -@Component({ - selector: 'addon-calendar-upcoming-events', - templateUrl: 'addon-calendar-upcoming-events.html', -}) -export class AddonCalendarUpcomingEventsComponent implements OnInit, DoCheck, OnDestroy { - @Input() filter: AddonCalendarFilter; // Filter to apply. - @Output() onEventClicked = new EventEmitter(); - - filteredEvents = []; - loaded = false; - - protected year: number; - protected month: number; - protected categoriesRetrieved = false; - protected categories = {}; - protected currentSiteId: string; - protected events: AddonCalendarCalendarEvent[] = []; // Events (both online and offline). - protected onlineEvents: AddonCalendarCalendarEvent[] = []; - protected offlineEvents = []; // Offline events. - protected deletedEvents = []; // Events deleted in offline. - protected lookAhead: number; - protected timeFormat: string; - protected differ: any; // To detect changes in the data input. - - // Observers. - protected undeleteEventObserver: any; - protected obsDefaultTimeChange: any; - - constructor(eventsProvider: CoreEventsProvider, - sitesProvider: CoreSitesProvider, - localNotificationsProvider: CoreLocalNotificationsProvider, - differs: KeyValueDiffers, - private calendarProvider: AddonCalendarProvider, - private calendarHelper: AddonCalendarHelperProvider, - private calendarOffline: AddonCalendarOfflineProvider, - private domUtils: CoreDomUtilsProvider, - private coursesProvider: CoreCoursesProvider) { - - this.currentSiteId = sitesProvider.getCurrentSiteId(); - - if (localNotificationsProvider.isAvailable()) { - // Re-schedule events if default time changes. - this.obsDefaultTimeChange = eventsProvider.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => { - calendarProvider.scheduleEventsNotifications(this.onlineEvents); - }, 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); - - this.differ = differs.find([]).create(); - } - - /** - * Component loaded. - */ - ngOnInit(): void { - this.fetchData(); - } - - /** - * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays). - */ - ngDoCheck(): void { - // Check if there's any change in the filter object. - const changes = this.differ.diff(this.filter); - if (changes) { - this.filterEvents(); - } - } - - /** - * Fetch data. - * - * @param refresh True if we are refreshing events. - * @return Promise resolved when done. - */ - fetchData(refresh: boolean = false): Promise { - 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); - }); - - this.offlineEvents = this.sortEvents(events); - })); - - // Get events deleted in offline. - promises.push(this.calendarOffline.getAllDeletedEventsIds().then((ids) => { - this.deletedEvents = ids; - })); - - // Get user preferences. - promises.push(this.calendarProvider.getCalendarLookAhead().then((value) => { - this.lookAhead = value; - })); - - 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 upcoming events. - * - * @return Promise resolved when done. - */ - fetchEvents(): Promise { - // Don't pass courseId and categoryId, we'll filter them locally. - return this.calendarProvider.getUpcomingEvents().then((result) => { - const promises = []; - - this.onlineEvents = result.events; - - this.onlineEvents.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); - - // Schedule notifications for the events retrieved. - this.calendarProvider.scheduleEventsNotifications(this.onlineEvents); - - // Merge the online events with offline data. - this.events = this.mergeEvents(); - - // Filter events by course. - this.filterEvents(); - - // Re-calculate the formatted time so it uses the device date. - this.events.forEach((event) => { - promises.push(this.calendarProvider.formatEventTime(event, this.timeFormat).then((time) => { - event.formattedtime = time; - })); - }); - - return Promise.all(promises); - }); - } - - /** - * Load categories to be able to filter events. - * - * @return Promise resolved when done. - */ - protected loadCategories(): Promise { - 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 based on the filter popover. - */ - protected filterEvents(): void { - this.filteredEvents = this.calendarHelper.getFilteredEvents(this.events, this.filter, this.categories); - } - - /** - * Refresh events. - * - * @param afterChange Whether the refresh is done after an event has changed or has been synced. - * @return Promise resolved when done. - */ - refreshData(afterChange?: boolean): Promise { - const promises = []; - - // Don't invalidate upcoming events after a change, it has already been handled. - if (!afterChange) { - promises.push(this.calendarProvider.invalidateAllUpcomingEvents()); - } - promises.push(this.coursesProvider.invalidateCategories(0, true)); - promises.push(this.calendarProvider.invalidateLookAhead()); - promises.push(this.calendarProvider.invalidateTimeFormat()); - - this.categoriesRetrieved = false; // Get categories again. - - return Promise.all(promises).then(() => { - return this.fetchData(true); - }); - } - - /** - * An event was clicked. - * - * @param event Event. - */ - eventClicked(event: any): void { - this.onEventClicked.emit(event.id); - } - - /** - * Merge online events with the offline events of that period. - * - * @return Merged events. - */ - protected mergeEvents(): any[] { - if (!this.offlineEvents.length && !this.deletedEvents.length) { - // No offline events, nothing to merge. - return this.onlineEvents; - } - - const start = Date.now() / 1000, - end = start + (CoreConstants.SECONDS_DAY * this.lookAhead); - let result = this.onlineEvents; - - if (this.deletedEvents.length) { - // Mark as deleted the events that were deleted in offline. - result.forEach((event) => { - event.deleted = this.deletedEvents.indexOf(event.id) != -1; - }); - } - - if (this.offlineEvents.length) { - // Remove the online events that were modified in offline. - result = result.filter((event) => { - const offlineEvent = this.offlineEvents.find((ev) => { - return ev.id == event.id; - }); - - return !offlineEvent; - }); - } - - // Now get the offline events that belong to this period. - const periodOfflineEvents = this.offlineEvents.filter((event) => { - return (event.timestart >= start || event.timestart + event.timeduration >= start) && event.timestart <= end; - }); - - // Merge both arrays and sort them. - result = result.concat(periodOfflineEvents); - - return this.sortEvents(result); - } - - /** - * Sort events by timestart. - * - * @param 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 eventId Event ID. - */ - protected undeleteEvent(eventId: number): void { - const event = this.onlineEvents.find((event) => { - return event.id == eventId; - }); - - if (event) { - event.deleted = false; - } - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.undeleteEventObserver && this.undeleteEventObserver.off(); - this.obsDefaultTimeChange && this.obsDefaultTimeChange.off(); - } -} diff --git a/src/addon/calendar/lang/en.json b/src/addon/calendar/lang/en.json deleted file mode 100644 index 8b1748f6b..000000000 --- a/src/addon/calendar/lang/en.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "allday": "All day", - "calendar": "Calendar", - "calendarevent": "Calendar event", - "calendarevents": "Calendar events", - "calendarreminders": "Calendar reminders", - "categoryevents": "Category events", - "confirmeventdelete": "Are you sure you want to delete the \"{{$a}}\" event?", - "confirmeventseriesdelete": "The \"{{$a.name}}\" event is part of a series. Do you want to delete just this event, or all {{$a.count}} events in the series?", - "courseevents": "Course events", - "currentmonth": "Current Month", - "daynext": "Next day", - "dayprev": "Previous day", - "defaultnotificationtime": "Default notification time", - "deleteallevents": "Delete all events", - "deleteevent": "Delete event", - "deleteoneevent": "Delete this event", - "durationminutes": "Duration in minutes", - "durationnone": "Without duration", - "durationuntil": "Until", - "editevent": "Editing event", - "errorloadevent": "Error loading event.", - "errorloadevents": "Error loading events.", - "eventcalendareventdeleted": "Calendar event deleted", - "eventduration": "Duration", - "eventendtime": "End time", - "eventkind": "Type of event", - "eventname": "Event title", - "eventstarttime": "Start time", - "eventtype": "Event type", - "fri": "Fri", - "friday": "Friday", - "gotoactivity": "Go to activity", - "groupevents": "Group events", - "invalidtimedurationminutes": "The duration in minutes you have entered is invalid. Please enter the duration in minutes greater than 0 or select no duration.", - "invalidtimedurationuntil": "The date and time you selected for duration until is before the start time of the event. Please correct this before proceeding.", - "mon": "Mon", - "monday": "Monday", - "monthlyview": "Monthly view", - "newevent": "New event", - "noevents": "There are no events", - "nopermissiontoupdatecalendar": "Sorry, but you do not have permission to update the calendar event.", - "reminders": "Reminders", - "repeatedevents": "Repeated events", - "repeateditall": "Also apply changes to the other {{$a}} events in this repeat series", - "repeateditthis": "Apply changes to this event only", - "repeatevent": "Repeat this event", - "repeatweeksl": "Repeat weekly, creating altogether", - "sat": "Sat", - "saturday": "Saturday", - "setnewreminder": "Set a new reminder", - "siteevents": "Site events", - "sun": "Sun", - "sunday": "Sunday", - "thu": "Thu", - "thursday": "Thursday", - "today": "Today", - "tomorrow": "Tomorrow", - "tue": "Tue", - "tuesday": "Tuesday", - "typecategory": "Category event", - "typeclose": "Close event", - "typecourse": "Course event", - "typedue": "Due event", - "typegradingdue": "Grading due event", - "typegroup": "Group event", - "typeopen": "Open event", - "typesite": "Site event", - "typeuser": "User event", - "upcomingevents": "Upcoming events", - "userevents": "User events", - "wed": "Wed", - "wednesday": "Wednesday", - "when": "When", - "yesterday": "Yesterday" -} \ No newline at end of file diff --git a/src/addon/calendar/pages/day/day.html b/src/addon/calendar/pages/day/day.html deleted file mode 100644 index 1f36db633..000000000 --- a/src/addon/calendar/pages/day/day.html +++ /dev/null @@ -1,75 +0,0 @@ - - - {{ 'addon.calendar.calendarevents' | translate }} - - - - - - - - - - - - - - - - - - - - - - - -

{{ periodName }}

-
- - - - - -
-
- - - - - {{ 'core.hasdatatosync' | translate:{$a: 'core.day' | translate} }} - - - - - - - - - - -

-

- - - {{ 'core.notsent' | translate }} - - - - {{ 'core.deletedoffline' | translate }} - -
-
-
-
- - - - - -
diff --git a/src/addon/calendar/pages/day/day.module.ts b/src/addon/calendar/pages/day/day.module.ts deleted file mode 100644 index 7a672b6c5..000000000 --- a/src/addon/calendar/pages/day/day.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCalendarDayPage } from './day'; - -@NgModule({ - declarations: [ - AddonCalendarDayPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonCalendarDayPage), - TranslateModule.forChild() - ], -}) -export class AddonCalendarDayPageModule {} diff --git a/src/addon/calendar/pages/day/day.scss b/src/addon/calendar/pages/day/day.scss deleted file mode 100644 index 4918c1312..000000000 --- a/src/addon/calendar/pages/day/day.scss +++ /dev/null @@ -1,9 +0,0 @@ -page-addon-calendar-day { - .addon-calendar-period { - flex-grow: 3; - h3 { - margin-top: 10px; - font-size: 1.6rem; - } - } -} \ No newline at end of file diff --git a/src/addon/calendar/pages/day/day.ts b/src/addon/calendar/pages/day/day.ts deleted file mode 100644 index 92abebece..000000000 --- a/src/addon/calendar/pages/day/day.ts +++ /dev/null @@ -1,721 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OFx ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, NgZone } from '@angular/core'; -import { IonicPage, NavParams, NavController, PopoverController } from 'ionic-angular'; -import { CoreAppProvider } from '@providers/app'; -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 { AddonCalendarProvider, AddonCalendarCalendarEvent } from '../../providers/calendar'; -import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; -import { AddonCalendarHelperProvider, AddonCalendarFilter } from '../../providers/helper'; -import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; -import { AddonCalendarFilterPopoverComponent } from '../../components/filter/filter'; -import { Network } from '@ionic-native/network'; -import * as moment from 'moment'; - -/** - * Page that displays the calendar events for a certain day. - */ -@IonicPage({ segment: 'addon-calendar-day' }) -@Component({ - selector: 'page-addon-calendar-day', - templateUrl: 'day.html', -}) -export class AddonCalendarDayPage implements OnInit, OnDestroy { - - protected currentSiteId: string; - protected year: number; - protected month: number; - protected day: number; - protected categories = {}; - protected events = []; // Events (both online and offline). - protected onlineEvents: AddonCalendarCalendarEvent[] = []; - protected offlineEvents = {}; // Offline events. - protected offlineEditedEventsIds = []; // IDs of events edited in offline. - protected deletedEvents = []; // Events deleted in offline. - protected timeFormat: string; - protected currentMoment: moment.Moment; - protected currentTime: number; - - // Observers. - protected newEventObserver: any; - protected discardedObserver: any; - protected editEventObserver: any; - protected deleteEventObserver: any; - protected undeleteEventObserver: any; - protected syncObserver: any; - protected manualSyncObserver: any; - protected onlineObserver: any; - protected obsDefaultTimeChange: any; - protected filterChangedObserver: any; - - periodName: string; - filteredEvents = []; - canCreate = false; - courses: any[]; - loaded = false; - hasOffline = false; - isOnline = false; - syncIcon: string; - isCurrentDay: boolean; - isPastDay: boolean; - filter: AddonCalendarFilter = { - filtered: false, - courseId: null, - categoryId: null, - course: true, - group: true, - site: true, - user: true, - category: true - }; - - constructor(localNotificationsProvider: CoreLocalNotificationsProvider, - navParams: NavParams, - network: Network, - zone: NgZone, - sitesProvider: CoreSitesProvider, - private navCtrl: NavController, - private domUtils: CoreDomUtilsProvider, - private timeUtils: CoreTimeUtilsProvider, - private calendarProvider: AddonCalendarProvider, - private calendarOffline: AddonCalendarOfflineProvider, - private calendarHelper: AddonCalendarHelperProvider, - private calendarSync: AddonCalendarSyncProvider, - private eventsProvider: CoreEventsProvider, - private coursesProvider: CoreCoursesProvider, - private coursesHelper: CoreCoursesHelperProvider, - private appProvider: CoreAppProvider, - private popoverCtrl: PopoverController) { - - const now = new Date(); - - AddonCalendarProvider.ALL_TYPES.forEach((name) => { - this.filter[name] = navParams.get(name); - this.filter[name] = typeof this.filter[name] == 'undefined' ? true : this.filter[name]; - }); - this.filter.courseId = navParams.get('courseId'); - this.filter.categoryId = navParams.get('categoryId'); - - this.filter.filtered = !!this.filter.courseId || AddonCalendarProvider.ALL_TYPES.some((name) => !this.filter[name]); - - this.year = navParams.get('year') || now.getFullYear(); - this.month = navParams.get('month') || (now.getMonth() + 1); - this.day = navParams.get('day') || now.getDate(); - this.currentSiteId = sitesProvider.getCurrentSiteId(); - - if (localNotificationsProvider.isAvailable()) { - // Re-schedule events if default time changes. - this.obsDefaultTimeChange = eventsProvider.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => { - calendarProvider.scheduleEventsNotifications(this.onlineEvents); - }, this.currentSiteId); - } - - // Listen for events added. When an event is added, reload the data. - this.newEventObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => { - if (data && data.event) { - this.loaded = false; - this.refreshData(true, false, true); - } - }, this.currentSiteId); - - // Listen for new event discarded event. When it does, reload the data. - this.discardedObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => { - this.loaded = false; - this.refreshData(true, false, true); - }, this.currentSiteId); - - // Listen for events edited. When an event is edited, reload the data. - this.editEventObserver = eventsProvider.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => { - if (data && data.event) { - this.loaded = false; - this.refreshData(true, false, true); - } - }, this.currentSiteId); - - // Refresh data if calendar events are synchronized automatically. - this.syncObserver = eventsProvider.on(AddonCalendarSyncProvider.AUTO_SYNCED, (data) => { - this.loaded = false; - this.refreshData(false, false, true); - }, this.currentSiteId); - - // Refresh data if calendar events are synchronized manually but not by this page. - this.manualSyncObserver = eventsProvider.on(AddonCalendarSyncProvider.MANUAL_SYNCED, (data) => { - if (data && (data.source != 'day' || data.year != this.year || data.month != this.month || data.day != this.day)) { - this.loaded = false; - this.refreshData(false, false, true); - } - }, this.currentSiteId); - - // Update the events when an event is deleted. - this.deleteEventObserver = eventsProvider.on(AddonCalendarProvider.DELETED_EVENT_EVENT, (data) => { - if (data && !data.sent) { - // Event was deleted in offline. Just mark it as deleted, no need to refresh. - this.hasOffline = this.markAsDeleted(data.eventId, true) || this.hasOffline; - this.deletedEvents.push(data.eventId); - } else { - this.loaded = false; - this.refreshData(false, false, true); - } - }, 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. - const found = this.markAsDeleted(data.eventId, false); - - // 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); - } - - if (found) { - // The deleted event belongs to current list. Re-calculate "hasOffline". - this.hasOffline = false; - - if (this.events.length != this.onlineEvents.length) { - this.hasOffline = true; - } else { - const event = this.events.find((event) => { - return event.deleted || event.offline; - }); - - this.hasOffline = !!event; - } - } - } - }, this.currentSiteId); - - this.filterChangedObserver = this.eventsProvider.on(AddonCalendarProvider.FILTER_CHANGED_EVENT, (data) => { - this.filter = data; - - // Course viewed has changed, check if the user can create events for this course calendar. - this.calendarHelper.canEditEvents(this.filter['courseId']).then((canEdit) => { - this.canCreate = canEdit; - }); - - this.filterEvents(); - }); - - // Refresh online status when changes. - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.isOnline = this.appProvider.isOnline(); - }); - }); - } - - /** - * View loaded. - */ - ngOnInit(): void { - this.calculateCurrentMoment(); - this.calculateIsCurrentDay(); - - this.fetchData(true, false); - } - - /** - * Fetch all the data required for the view. - * - * @param sync Whether it should try to synchronize offline events. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - fetchData(sync?: boolean, showErrors?: boolean): Promise { - - this.syncIcon = 'spinner'; - this.isOnline = this.appProvider.isOnline(); - - const promise = sync ? this.sync() : Promise.resolve(); - - return promise.then(() => { - const promises = []; - - // Load courses for the popover. - promises.push(this.coursesHelper.getCoursesForPopover(this.filter['courseId']).then((data) => { - this.courses = data.courses; - })); - - // Get categories. - 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 & day. - 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; - })); - - // Check if user can create events. - promises.push(this.calendarHelper.canEditEvents(this.filter['courseId']).then((canEdit) => { - this.canCreate = canEdit; - })); - - // Get user preferences. - 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; - this.syncIcon = 'sync'; - }); - } - - /** - * Fetch the events for current day. - * - * @return Promise resolved when done. - */ - fetchEvents(): Promise { - // Don't pass courseId and categoryId, we'll filter them locally. - return this.calendarProvider.getDayEvents(this.year, this.month, this.day).catch((error) => { - if (!this.appProvider.isOnline()) { - // Allow navigating to non-cached days in offline (behave as if using emergency cache). - return Promise.resolve({ events: [] }); - } else { - return Promise.reject(error); - } - }).then((result) => { - const promises = []; - - // 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, this.day).getTime(), - 'core.strftimedaydate'); - - this.onlineEvents = result.events; - this.onlineEvents.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); - - // Schedule notifications for the events retrieved (only future events will be scheduled). - this.calendarProvider.scheduleEventsNotifications(this.onlineEvents); - - // Merge the online events with offline data. - this.events = this.mergeEvents(); - - // Filter events by course. - this.filterEvents(); - - this.calculateIsCurrentDay(); - - // Re-calculate the formatted time so it uses the device date. - const dayTime = this.currentMoment.unix() * 1000; - this.events.forEach((event) => { - event.ispast = this.isPastDay || (this.isCurrentDay && this.isEventPast(event)); - promises.push(this.calendarProvider.formatEventTime(event, this.timeFormat, true, dayTime).then((time) => { - event.formattedtime = time; - })); - }); - - return Promise.all(promises); - }); - } - - /** - * Merge online events with the offline events of that period. - * - * @return Merged events. - */ - protected mergeEvents(): any[] { - this.hasOffline = false; - - if (!Object.keys(this.offlineEvents).length && !this.deletedEvents.length) { - // No offline events, nothing to merge. - return this.onlineEvents; - } - - const monthOfflineEvents = this.offlineEvents[this.calendarHelper.getMonthId(this.year, this.month)], - dayOfflineEvents = monthOfflineEvents && monthOfflineEvents[this.day]; - let result = this.onlineEvents; - - if (this.deletedEvents.length) { - // Mark as deleted the events that were deleted in offline. - result.forEach((event) => { - event.deleted = this.deletedEvents.indexOf(event.id) != -1; - - if (event.deleted) { - this.hasOffline = true; - } - }); - } - - if (this.offlineEditedEventsIds.length) { - // Remove the online events that were modified in offline. - result = result.filter((event) => { - return this.offlineEditedEventsIds.indexOf(event.id) == -1; - }); - - if (result.length != this.onlineEvents.length) { - this.hasOffline = true; - } - } - - if (dayOfflineEvents && dayOfflineEvents.length) { - // Add the offline events (either new or edited). - this.hasOffline = true; - result = this.sortEvents(result.concat(dayOfflineEvents)); - } - - return result; - } - - /** - * Filter events based on the filter popover. - */ - protected filterEvents(): void { - this.filteredEvents = this.calendarHelper.getFilteredEvents(this.events, this.filter, this.categories); - } - - /** - * Sort events by timestart. - * - * @param 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; - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @param done Function to call when done. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - doRefresh(refresher?: any, done?: () => void, showErrors?: boolean): Promise { - if (this.loaded) { - return this.refreshData(true, showErrors).finally(() => { - refresher && refresher.complete(); - done && done(); - }); - } - - return Promise.resolve(); - } - - /** - * Refresh the data. - * - * @param sync Whether it should try to synchronize offline events. - * @param showErrors Whether to show sync errors to the user. - * @param afterChange Whether the refresh is done after an event has changed or has been synced. - * @return Promise resolved when done. - */ - refreshData(sync?: boolean, showErrors?: boolean, afterChange?: boolean): Promise { - this.syncIcon = 'spinner'; - - const promises = []; - - // Don't invalidate day events after a change, it has already been handled. - if (!afterChange) { - promises.push(this.calendarProvider.invalidateDayEvents(this.year, this.month, this.day)); - } - promises.push(this.calendarProvider.invalidateAllowedEventTypes()); - promises.push(this.coursesProvider.invalidateCategories(0, true)); - promises.push(this.calendarProvider.invalidateTimeFormat()); - - return Promise.all(promises).finally(() => { - return this.fetchData(sync, showErrors); - }); - } - - /** - * Load categories to be able to filter events. - * - * @return Promise resolved when done. - */ - protected loadCategories(): Promise { - return this.coursesProvider.getCategories(0, true).then((cats) => { - this.categories = {}; - - // Index categories by ID. - cats.forEach((category) => { - this.categories[category.id] = category; - }); - }).catch(() => { - // Ignore errors. - }); - } - - /** - * Try to synchronize offline events. - * - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - protected sync(showErrors?: boolean): Promise { - return this.calendarSync.syncEvents().then((result) => { - if (result.warnings && result.warnings.length) { - this.domUtils.showErrorModal(result.warnings[0]); - } - - if (result.updated) { - // Trigger a manual sync event. - result.source = 'day'; - result.day = this.day; - result.month = this.month; - result.year = this.year; - - this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId); - } - }).catch((error) => { - if (showErrors) { - this.domUtils.showErrorModalDefault(error, 'core.errorsync', true); - } - }); - } - - /** - * Navigate to a particular event. - * - * @param eventId Event to load. - */ - gotoEvent(eventId: number): void { - if (eventId < 0) { - // It's an offline event, go to the edit page. - this.openEdit(eventId); - } else { - this.navCtrl.push('AddonCalendarEventPage', { - id: eventId - }); - } - } - - /** - * Show the context menu. - * - * @param event Event. - */ - openFilter(event: MouseEvent): void { - const popover = this.popoverCtrl.create(AddonCalendarFilterPopoverComponent, { - courses: this.courses, - filter: this.filter - }); - - popover.present({ - ev: event - }); - } - - /** - * Open page to create/edit an event. - * - * @param eventId Event ID to edit. - */ - openEdit(eventId?: number): void { - const params: any = {}; - - if (eventId) { - params.eventId = eventId; - } else { - // It's a new event, set the time. - params.timestamp = moment().year(this.year).month(this.month - 1).date(this.day).unix() * 1000; - } - - if (this.filter['courseId']) { - params.courseId = this.filter['courseId']; - } - - this.navCtrl.push('AddonCalendarEditEventPage', params); - } - - /** - * Calculate current moment. - */ - calculateCurrentMoment(): void { - this.currentMoment = moment().year(this.year).month(this.month - 1).date(this.day); - } - - /** - * Check if user is viewing the current day. - */ - calculateIsCurrentDay(): void { - const now = new Date(); - - this.currentTime = this.timeUtils.timestamp(); - - this.isCurrentDay = this.year == now.getFullYear() && this.month == now.getMonth() + 1 && this.day == now.getDate(); - this.isPastDay = this.year < now.getFullYear() || (this.year == now.getFullYear() && this.month < now.getMonth()) || - (this.year == now.getFullYear() && this.month == now.getMonth() + 1 && this.day < now.getDate()); - } - - /** - * Go to current day. - */ - goToCurrentDay(): void { - const now = new Date(), - initialDay = this.day, - initialMonth = this.month, - initialYear = this.year; - - this.day = now.getDate(); - this.month = now.getMonth() + 1; - this.year = now.getFullYear(); - this.calculateCurrentMoment(); - - this.loaded = false; - - this.fetchEvents().then(() => { - this.isCurrentDay = true; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); - - this.year = initialYear; - this.month = initialMonth; - this.day = initialDay; - this.calculateCurrentMoment(); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Load next month. - */ - loadNext(): void { - this.increaseDay(); - - this.loaded = false; - - this.fetchEvents().catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); - this.decreaseDay(); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Load previous month. - */ - loadPrevious(): void { - this.decreaseDay(); - - this.loaded = false; - - this.fetchEvents().catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); - this.increaseDay(); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Decrease the current day. - */ - protected decreaseDay(): void { - this.currentMoment.subtract(1, 'day'); - - this.year = this.currentMoment.year(); - this.month = this.currentMoment.month() + 1; - this.day = this.currentMoment.date(); - } - - /** - * Increase the current day. - */ - protected increaseDay(): void { - this.currentMoment.add(1, 'day'); - - this.year = this.currentMoment.year(); - this.month = this.currentMoment.month() + 1; - this.day = this.currentMoment.date(); - } - - /** - * Find an event and mark it as deleted. - * - * @param eventId Event ID. - * @param deleted Whether to mark it as deleted or not. - * @return Whether the event was found. - */ - protected markAsDeleted(eventId: number, deleted: boolean): boolean { - const event = this.onlineEvents.find((event) => { - return event.id == eventId; - }); - - if (event) { - event.deleted = deleted; - - return true; - } - - return false; - } - - /** - * Returns if the event is in the past or not. - * @param event Event object. - * @return True if it's in the past. - */ - isEventPast(event: any): boolean { - return (event.timestart + event.timeduration) < this.currentTime; - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.newEventObserver && this.newEventObserver.off(); - this.discardedObserver && this.discardedObserver.off(); - this.editEventObserver && this.editEventObserver.off(); - this.deleteEventObserver && this.deleteEventObserver.off(); - this.undeleteEventObserver && this.undeleteEventObserver.off(); - this.syncObserver && this.syncObserver.off(); - this.manualSyncObserver && this.manualSyncObserver.off(); - this.onlineObserver && this.onlineObserver.unsubscribe(); - this.filterChangedObserver && this.filterChangedObserver.off(); - this.obsDefaultTimeChange && this.obsDefaultTimeChange.off(); - } -} diff --git a/src/addon/calendar/pages/edit-event/edit-event.html b/src/addon/calendar/pages/edit-event/edit-event.html deleted file mode 100644 index 49c0bb32f..000000000 --- a/src/addon/calendar/pages/edit-event/edit-event.html +++ /dev/null @@ -1,159 +0,0 @@ - - - {{ title | translate }} - - - - - - - - -
- - -

{{ 'addon.calendar.eventname' | translate }}

- - -
- - - -

{{ 'core.date' | translate }}

- - -
- - - -

{{ 'addon.calendar.eventkind' | translate }}

- - {{ type.name | translate }} - -
- - - -

{{ 'core.category' | translate }}

- - {{ category.name }} - -
- - - -

{{ 'core.course' | translate }}

- - {{ course.fullname }} - -
- - - - - -

{{ 'core.course' | translate }}

- - {{ course.fullname }} - -
- - -

{{ 'core.coursenogroups' | translate }}

-
- - -

{{ 'core.group' | translate }}

- - {{ group.name }} - -
- - - - -
- - - - - {{ 'core.showmore' | translate }} - - {{ 'core.showless' | translate }} - - - - - -

{{ 'core.description' | translate }}

- -
- - - -

{{ 'core.location' | translate }}

- -
- - -
-

{{ 'addon.calendar.eventduration' | translate }}

- - {{ 'addon.calendar.durationnone' | translate }} - - - - {{ 'addon.calendar.durationuntil' | translate }} - - - - - - - {{ 'addon.calendar.durationminutes' | translate }} - - - - - -
- - - - -

{{ 'addon.calendar.repeatevent' | translate }}

- -
- -

{{ 'addon.calendar.repeatweeksl' | translate }}

- -
-
- - -
-

{{ 'addon.calendar.repeatedevents' | translate }}

- - {{ 'addon.calendar.repeateditall' | translate:{$a: otherEventsCount} }} - - - - {{ 'addon.calendar.repeateditthis' | translate }} - - -
-
- - - - - - - - - - - -
-
-
diff --git a/src/addon/calendar/pages/edit-event/edit-event.module.ts b/src/addon/calendar/pages/edit-event/edit-event.module.ts deleted file mode 100644 index 5821de8a0..000000000 --- a/src/addon/calendar/pages/edit-event/edit-event.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; -import { AddonCalendarEditEventPage } from './edit-event'; - -@NgModule({ - declarations: [ - AddonCalendarEditEventPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonCalendarEditEventPage), - TranslateModule.forChild() - ], -}) -export class AddonCalendarEditEventPageModule {} diff --git a/src/addon/calendar/pages/edit-event/edit-event.scss b/src/addon/calendar/pages/edit-event/edit-event.scss deleted file mode 100644 index 055fc5f3e..000000000 --- a/src/addon/calendar/pages/edit-event/edit-event.scss +++ /dev/null @@ -1,28 +0,0 @@ -ion-app.app-root page-addon-calendar-edit-event { - .addon-calendar-radio-container ion-item:not(.addon-calendar-radio-title) { - &.item-ios { - @include padding-horizontal($item-ios-padding-start * 2, null); - - ion-input { - @include padding-horizontal($datetime-ios-padding-start - $text-input-ios-margin-start, null); - } - } - &.item-md { - @include padding-horizontal($item-md-padding-start * 2, null); - - ion-input { - @include padding-horizontal($datetime-md-padding-start - $text-input-md-margin-start, null); - } - } - } - - .addon-calendar-eventtype-container.item-select-disabled { - ion-label, ion-select { - opacity: 1; - } - - .select-icon { - display: none; - } - } -} \ No newline at end of file diff --git a/src/addon/calendar/pages/edit-event/edit-event.ts b/src/addon/calendar/pages/edit-event/edit-event.ts deleted file mode 100644 index e68d4b362..000000000 --- a/src/addon/calendar/pages/edit-event/edit-event.ts +++ /dev/null @@ -1,606 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, Optional, ViewChild, ElementRef } from '@angular/core'; -import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreEditorRichTextEditorComponent } from '@core/editor/components/rich-text-editor/rich-text-editor.ts'; -import { AddonCalendarProvider, AddonCalendarGetAccessInfoResult, AddonCalendarEvent } from '../../providers/calendar'; -import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; -import { AddonCalendarHelperProvider } from '../../providers/helper'; -import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; -import { CoreSite } from '@classes/site'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; - -/** - * Page that displays a form to create/edit an event. - */ -@IonicPage({ segment: 'addon-calendar-edit-event' }) -@Component({ - selector: 'page-addon-calendar-edit-event', - templateUrl: 'edit-event.html', -}) -export class AddonCalendarEditEventPage implements OnInit, OnDestroy { - - @ViewChild(CoreEditorRichTextEditorComponent) descriptionEditor: CoreEditorRichTextEditorComponent; - @ViewChild('editEventForm') formElement: ElementRef; - - title: string; - dateFormat: string; - component = AddonCalendarProvider.COMPONENT; - loaded = false; - hasOffline = false; - eventTypes = []; - categories = []; - courses = []; - groups = []; - loadingGroups = false; - courseGroupSet = false; - advanced = false; - errors: any; - event: AddonCalendarEvent; // The event object (when editing an event). - otherEventsCount: number; - - // Form variables. - eventForm: FormGroup; - eventTypeControl: FormControl; - groupControl: FormControl; - descriptionControl: FormControl; - - protected eventId: number; - protected courseId: number; - protected originalData: any; - protected currentSite: CoreSite; - protected types: {[name: string]: boolean}; // Object with the supported types. - protected showAll: boolean; - protected isDestroyed = false; - protected error = false; - protected gotEventData = false; - - constructor(navParams: NavParams, - private navCtrl: NavController, - private translate: TranslateService, - private domUtils: CoreDomUtilsProvider, - private timeUtils: CoreTimeUtilsProvider, - private eventsProvider: CoreEventsProvider, - private groupsProvider: CoreGroupsProvider, - sitesProvider: CoreSitesProvider, - private coursesProvider: CoreCoursesProvider, - private utils: CoreUtilsProvider, - private calendarProvider: AddonCalendarProvider, - private calendarOffline: AddonCalendarOfflineProvider, - private calendarHelper: AddonCalendarHelperProvider, - private calendarSync: AddonCalendarSyncProvider, - private fb: FormBuilder, - private syncProvider: CoreSyncProvider, - private filterHelper: CoreFilterHelperProvider, - @Optional() private svComponent: CoreSplitViewComponent) { - - this.eventId = navParams.get('eventId'); - this.courseId = navParams.get('courseId'); - this.title = this.eventId ? 'addon.calendar.editevent' : 'addon.calendar.newevent'; - - const timestamp = navParams.get('timestamp'); - - this.currentSite = sitesProvider.getCurrentSite(); - this.errors = { - required: this.translate.instant('core.required') - }; - - // Calculate format to use. ion-datetime doesn't support escaping characters ([]), so we remove them. - this.dateFormat = this.timeUtils.convertPHPToMoment(this.translate.instant('core.strftimedatetimeshort')) - .replace(/[\[\]]/g, ''); - - // Initialize form variables. - this.eventForm = new FormGroup({}); - this.eventTypeControl = this.fb.control('', Validators.required); - this.groupControl = this.fb.control(''); - this.descriptionControl = this.fb.control(''); - - const currentDate = this.timeUtils.toDatetimeFormat(timestamp); - - this.eventForm.addControl('name', this.fb.control('', Validators.required)); - this.eventForm.addControl('timestart', this.fb.control(currentDate, Validators.required)); - this.eventForm.addControl('eventtype', this.eventTypeControl); - this.eventForm.addControl('categoryid', this.fb.control('')); - this.eventForm.addControl('courseid', this.fb.control(this.courseId)); - this.eventForm.addControl('groupcourseid', this.fb.control('')); - this.eventForm.addControl('groupid', this.groupControl); - this.eventForm.addControl('description', this.descriptionControl); - this.eventForm.addControl('location', this.fb.control('')); - this.eventForm.addControl('duration', this.fb.control(0)); - this.eventForm.addControl('timedurationuntil', this.fb.control(currentDate)); - this.eventForm.addControl('timedurationminutes', this.fb.control('')); - this.eventForm.addControl('repeat', this.fb.control(false)); - this.eventForm.addControl('repeats', this.fb.control('1')); - this.eventForm.addControl('repeateditall', this.fb.control(1)); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchData().finally(() => { - this.originalData = this.utils.clone(this.eventForm.value); - this.loaded = true; - }); - } - - /** - * Fetch the data needed to render the form. - * - * @param refresh Whether it's refreshing data. - * @return Promise resolved when done. - */ - protected fetchData(refresh?: boolean): Promise { - let accessInfo: AddonCalendarGetAccessInfoResult; - - this.error = false; - - // Get access info. - return this.calendarProvider.getAccessInformation(this.courseId).then((info) => { - accessInfo = info; - - return this.calendarProvider.getAllowedEventTypes(this.courseId); - }).then((types) => { - this.types = types; - - const promises = [], - eventTypes = this.calendarHelper.getEventTypeOptions(types); - - if (!eventTypes.length) { - return Promise.reject(this.translate.instant('addon.calendar.nopermissiontoupdatecalendar')); - } - - if (this.eventId && !this.gotEventData) { - // Editing an event, get the event data. Wait for sync first. - - promises.push(this.calendarSync.waitForSync(AddonCalendarSyncProvider.SYNC_ID).then(() => { - // Do not block if the scope is already destroyed. - if (!this.isDestroyed) { - this.syncProvider.blockOperation(AddonCalendarProvider.COMPONENT, this.eventId); - } - - const promises = []; - - // Get the event offline data if there's any. - promises.push(this.calendarOffline.getEvent(this.eventId).then((event) => { - this.hasOffline = true; - - return event; - }).catch(() => { - // No offline data. - this.hasOffline = false; - })); - - if (this.eventId > 0) { - // It's an online event. get its data from server. - promises.push(this.calendarProvider.getEventById(this.eventId).then((event) => { - this.event = event; - if (event && event.repeatid) { - this.otherEventsCount = event.eventcount ? event.eventcount - 1 : 0; - } - - return event; - })); - } - - return Promise.all(promises).then((result) => { - this.gotEventData = true; - - const event = result[0] || result[1]; // Use offline data first. - - if (event) { - // Load the data in the form. - return this.loadEventData(event, !!result[0]); - } - }); - })); - } - - if (types.category) { - // Get the categories. - promises.push(this.coursesProvider.getCategories(0, true).then((cats) => { - this.categories = cats; - })); - } - - this.showAll = this.utils.isTrueOrOne(this.currentSite.getStoredConfig('calendar_adminseesall')) && - accessInfo.canmanageentries; - - if (types.course || types.groups) { - // Get the courses. - const promise = this.showAll ? this.coursesProvider.getCoursesByField() : this.coursesProvider.getUserCourses(); - - promises.push(promise.then((courses) => { - if (this.showAll) { - // Remove site home from the list of courses. - const siteHomeId = this.currentSite.getSiteHomeId(); - courses = courses.filter((course) => { - return course.id != siteHomeId; - }); - } - - // Format the name of the courses. - const subPromises = []; - courses.forEach((course) => { - subPromises.push(this.filterHelper.getFiltersAndFormatText(course.fullname, 'course', course.id) - .then((result) => { - course.fullname = result.text; - }).catch(() => { - // Ignore errors. - })); - }); - - return Promise.all(subPromises).then(() => { - // Sort courses by name. - this.courses = courses.sort((a, b) => { - const compareA = a.fullname.toLowerCase(), - compareB = b.fullname.toLowerCase(); - - return compareA.localeCompare(compareB); - }); - }); - })); - } - - return Promise.all(promises).then(() => { - if (!this.eventTypeControl.value) { - // Initialize event type value. If course is allowed, select it first. - if (types.course) { - this.eventTypeControl.setValue(AddonCalendarProvider.TYPE_COURSE); - } else { - this.eventTypeControl.setValue(eventTypes[0].value); - } - } - - this.eventTypes = eventTypes; - }); - - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting data.'); - this.error = true; - - if (!this.svComponent || !this.svComponent.isOn()) { - this.originalData = null; // Avoid asking for confirmation. - this.navCtrl.pop(); - } - }); - } - - /** - * Load an event data into the form. - * - * @param event Event data. - * @param isOffline Whether the data is from offline or not. - * @return Promise resolved when done. - */ - protected loadEventData(event: any, isOffline: boolean): Promise { - const courseId = event.course ? event.course.id : event.courseid; - - this.eventForm.controls.name.setValue(event.name); - this.eventForm.controls.timestart.setValue(this.timeUtils.toDatetimeFormat(event.timestart * 1000)); - this.eventForm.controls.eventtype.setValue(event.eventtype); - this.eventForm.controls.categoryid.setValue(event.categoryid || ''); - this.eventForm.controls.courseid.setValue(courseId || ''); - this.eventForm.controls.groupcourseid.setValue(event.groupcourseid || courseId || ''); - this.eventForm.controls.groupid.setValue(event.groupid || ''); - this.eventForm.controls.description.setValue(event.description); - this.eventForm.controls.location.setValue(event.location); - - if (isOffline) { - // It's an offline event, use the data as it is. - this.eventForm.controls.duration.setValue(event.duration); - this.eventForm.controls.timedurationuntil.setValue( - this.timeUtils.toDatetimeFormat((event.timedurationuntil * 1000) || Date.now())); - this.eventForm.controls.timedurationminutes.setValue(event.timedurationminutes || ''); - this.eventForm.controls.repeat.setValue(!!event.repeat); - this.eventForm.controls.repeats.setValue(event.repeats || '1'); - this.eventForm.controls.repeateditall.setValue(event.repeateditall || 1); - } else { - // Online event, we'll have to calculate the data. - - if (event.timeduration > 0) { - this.eventForm.controls.duration.setValue(1); - this.eventForm.controls.timedurationuntil.setValue(this.timeUtils.toDatetimeFormat( - (event.timestart + event.timeduration) * 1000)); - } else { - // No duration. - this.eventForm.controls.duration.setValue(0); - this.eventForm.controls.timedurationuntil.setValue(this.timeUtils.toDatetimeFormat()); - } - - this.eventForm.controls.timedurationminutes.setValue(''); - this.eventForm.controls.repeat.setValue(!!event.repeatid); - this.eventForm.controls.repeats.setValue(event.eventcount || '1'); - this.eventForm.controls.repeateditall.setValue(1); - } - - if (event.eventtype == 'group' && courseId) { - return this.loadGroups(courseId); - } - - return Promise.resolve(); - } - - /** - * Pull to refresh. - * - * @param refresher Refresher. - */ - refreshData(refresher: any): void { - const promises = [ - this.calendarProvider.invalidateAccessInformation(this.courseId), - this.calendarProvider.invalidateAllowedEventTypes(this.courseId) - ]; - - if (this.types) { - if (this.types.category) { - promises.push(this.coursesProvider.invalidateCategories(0, true)); - } - if (this.types.course || this.types.groups) { - if (this.showAll) { - promises.push(this.coursesProvider.invalidateCoursesByField()); - } else { - promises.push(this.coursesProvider.invalidateUserCourses()); - } - } - } - - Promise.all(promises).finally(() => { - this.fetchData(true).finally(() => { - refresher.complete(); - }); - }); - } - - /** - * A course was selected, get its groups. - * - * @param courseId Course ID. - */ - groupCourseSelected(courseId: number): void { - if (!courseId) { - return; - } - - const modal = this.domUtils.showModalLoading(); - - this.loadGroups(courseId).then(() => { - this.groupControl.setValue(''); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting data.'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Load groups of a certain course. - * - * @param courseId Course ID. - * @return Promise resolved when done. - */ - protected loadGroups(courseId: number): Promise { - this.loadingGroups = true; - - return this.groupsProvider.getUserGroupsInCourse(courseId).then((groups) => { - this.groups = groups; - this.courseGroupSet = true; - }).finally(() => { - this.loadingGroups = false; - }); - } - - /** - * Show or hide advanced form fields. - */ - toggleAdvanced(): void { - this.advanced = !this.advanced; - } - - /** - * Create the event. - */ - submit(): void { - // Validate data. - const formData = this.eventForm.value, - timeStartDate = this.timeUtils.convertToTimestamp(formData.timestart), - timeUntilDate = this.timeUtils.convertToTimestamp(formData.timedurationuntil), - timeDurationMinutes = parseInt(formData.timedurationminutes || '', 10); - let error; - - if (formData.eventtype == AddonCalendarProvider.TYPE_COURSE && !formData.courseid) { - error = 'core.selectacourse'; - } else if (formData.eventtype == AddonCalendarProvider.TYPE_GROUP && !formData.groupcourseid) { - error = 'core.selectacourse'; - } else if (formData.eventtype == AddonCalendarProvider.TYPE_GROUP && !formData.groupid) { - error = 'core.selectagroup'; - } else if (formData.eventtype == AddonCalendarProvider.TYPE_CATEGORY && !formData.categoryid) { - error = 'core.selectacategory'; - } else if (formData.duration == 1 && timeStartDate > timeUntilDate) { - error = 'addon.calendar.invalidtimedurationuntil'; - } else if (formData.duration == 2 && (isNaN(timeDurationMinutes) || timeDurationMinutes < 1)) { - error = 'addon.calendar.invalidtimedurationminutes'; - } - - if (error) { - // Show error and stop. - this.domUtils.showErrorModal(this.translate.instant(error)); - - return; - } - - // Format the data to send. - const data: any = { - name: formData.name, - eventtype: formData.eventtype, - timestart: timeStartDate, - description: { - text: formData.description || '', - format: 1 - }, - location: formData.location, - duration: formData.duration, - repeat: formData.repeat - }; - - if (formData.eventtype == AddonCalendarProvider.TYPE_COURSE) { - data.courseid = formData.courseid; - } else if (formData.eventtype == AddonCalendarProvider.TYPE_GROUP) { - data.groupcourseid = formData.groupcourseid; - data.groupid = formData.groupid; - } else if (formData.eventtype == AddonCalendarProvider.TYPE_CATEGORY) { - data.categoryid = formData.categoryid; - } - - if (formData.duration == 1) { - data.timedurationuntil = timeUntilDate; - } else if (formData.duration == 2) { - data.timedurationminutes = formData.timedurationminutes; - } - - if (formData.repeat) { - data.repeats = Number(formData.repeats); - } - - if (this.event && this.event.repeatid) { - data.repeatid = this.event.repeatid; - data.repeateditall = formData.repeateditall; - } - - // Send the data. - const modal = this.domUtils.showModalLoading('core.sending', true); - let event: AddonCalendarEvent; - - this.calendarProvider.submitEvent(this.eventId, data).then((result) => { - event = result.event; - - this.domUtils.triggerFormSubmittedEvent(this.formElement, result.sent, this.currentSite.getId()); - - if (result.sent) { - // Event created or edited, invalidate right days & months. - const numberOfRepetitions = formData.repeat ? formData.repeats : - (data.repeateditall && this.otherEventsCount ? this.otherEventsCount + 1 : 1); - - return this.calendarHelper.refreshAfterChangeEvent(result.event, numberOfRepetitions).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - this.returnToList(event); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error sending data.'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Convenience function to update or return to event list depending on device. - * - * @param event Event. - */ - protected returnToList(event?: any): void { - // Unblock the sync because the view will be destroyed and the sync process could be triggered before ngOnDestroy. - this.unblockSync(); - - if (this.eventId > 0) { - // Editing an event. - const data: any = { - event: event - }; - this.eventsProvider.trigger(AddonCalendarProvider.EDIT_EVENT_EVENT, data, this.currentSite.getId()); - } else { - if (event) { - const data: any = { - event: event - }; - this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_EVENT, data, this.currentSite.getId()); - } else { - this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, {}, this.currentSite.getId()); - } - } - - if (this.svComponent && this.svComponent.isOn()) { - // Empty form. - this.hasOffline = false; - this.eventForm.reset(this.originalData); - this.originalData = this.utils.clone(this.eventForm.value); - } else { - this.originalData = null; // Avoid asking for confirmation. - this.navCtrl.pop(); - } - } - - /** - * Discard an offline saved discussion. - */ - discard(): void { - this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => { - this.calendarOffline.deleteEvent(this.eventId).then(() => { - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.currentSite.getId()); - - this.returnToList(); - }).catch(() => { - // Shouldn't happen. - this.domUtils.showErrorModal('Error discarding event.'); - }); - }).catch(() => { - // Cancelled. - }); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.calendarHelper.hasEventDataChanged(this.eventForm.value, this.originalData)) { - // Show confirmation if some data has been modified. - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.currentSite.getId()); - } - - /** - * Unblock sync. - */ - protected unblockSync(): void { - if (this.eventId) { - this.syncProvider.unblockOperation(AddonCalendarProvider.COMPONENT, this.eventId); - } - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.unblockSync(); - this.isDestroyed = true; - } -} diff --git a/src/addon/calendar/pages/event/event.html b/src/addon/calendar/pages/event/event.html deleted file mode 100644 index 63f53e1e6..000000000 --- a/src/addon/calendar/pages/event/event.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendarevent' | translate} }} - - - - - - - -

{{ 'addon.calendar.eventname' | translate }}

-

- - {{ 'core.deletedoffline' | translate }} - -
- -

{{ 'addon.calendar.when' | translate }}

-

- - {{ 'core.deletedoffline' | translate }} - -
- -

{{ 'addon.calendar.eventtype' | translate }}

-

{{ 'addon.calendar.type' + event.formattedType | translate }}

-
- -

{{ 'core.course' | translate}}

-

-
- -

{{ 'core.group' | translate}}

-

{{ groupName }}

-
- -

{{ 'core.category' | translate}}

-

-
- -

{{ 'core.description' | translate}}

-

- -

-
- -

{{ 'core.location' | translate}}

-

- - - -

-
- - {{ 'addon.calendar.gotoactivity' | translate }} - -
-
- - - -

{{ 'addon.calendar.reminders' | translate }}

-
- - -

{{ 'core.defaultvalue' | translate :{$a: ((event.timestart - defaultTime) * 1000) | coreFormatDate } }}

-

{{ reminder.time * 1000 | coreFormatDate }}

- -
-
- - - - - - - -
-
-
diff --git a/src/addon/calendar/pages/event/event.module.ts b/src/addon/calendar/pages/event/event.module.ts deleted file mode 100644 index 54d4d5e31..000000000 --- a/src/addon/calendar/pages/event/event.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCalendarEventPage } from './event'; - -@NgModule({ - declarations: [ - AddonCalendarEventPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonCalendarEventPage), - TranslateModule.forChild() - ], -}) -export class AddonCalendarEventPageModule {} diff --git a/src/addon/calendar/pages/event/event.scss b/src/addon/calendar/pages/event/event.scss deleted file mode 100644 index 6a9913737..000000000 --- a/src/addon/calendar/pages/event/event.scss +++ /dev/null @@ -1,5 +0,0 @@ -ion-app.app-root page-addon-calendar-event { - .card ion-note { - font-size: 1.6rem; - } -} diff --git a/src/addon/calendar/pages/event/event.ts b/src/addon/calendar/pages/event/event.ts deleted file mode 100644 index 4eae82b39..000000000 --- a/src/addon/calendar/pages/event/event.ts +++ /dev/null @@ -1,557 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, Optional, OnDestroy, NgZone } from '@angular/core'; -import { IonicPage, Content, NavParams, NavController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonCalendarProvider } from '../../providers/calendar'; -import { AddonCalendarHelperProvider } from '../../providers/helper'; -import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; -import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { Network } from '@ionic-native/network'; - -/** - * Page that displays a single calendar event. - */ -@IonicPage({ segment: 'addon-calendar-event' }) -@Component({ - selector: 'page-addon-calendar-event', - templateUrl: 'event.html', -}) -export class AddonCalendarEventPage implements OnDestroy { - @ViewChild(Content) content: Content; - - protected eventId; - protected siteHomeId: number; - protected editEventObserver: any; - protected syncObserver: any; - protected manualSyncObserver: any; - protected onlineObserver: any; - protected currentSiteId: string; - - eventLoaded: boolean; - notificationFormat: string; - notificationMin: string; - notificationMax: string; - notificationTimeText: string; - event: any = {}; - courseId: number; - courseName: string; - groupName: string; - courseUrl = ''; - notificationsEnabled = false; - moduleUrl = ''; - categoryPath = ''; - currentTime: number; - defaultTime: number; - reminders: any[]; - canEdit = false; - canDelete = false; - hasOffline = false; - isOnline = false; - syncIcon: string; // Sync icon. - isSplitViewOn = false; - - constructor(private translate: TranslateService, private calendarProvider: AddonCalendarProvider, navParams: NavParams, - private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider, - private calendarHelper: AddonCalendarHelperProvider, private sitesProvider: CoreSitesProvider, - localNotificationsProvider: CoreLocalNotificationsProvider, private courseProvider: CoreCourseProvider, - private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider, - private groupsProvider: CoreGroupsProvider, @Optional() private svComponent: CoreSplitViewComponent, - private navCtrl: NavController, private eventsProvider: CoreEventsProvider, network: Network, zone: NgZone, - private calendarSync: AddonCalendarSyncProvider, private appProvider: CoreAppProvider, - private calendarOffline: AddonCalendarOfflineProvider) { - - this.eventId = navParams.get('id'); - this.notificationsEnabled = localNotificationsProvider.isAvailable(); - this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); - this.currentSiteId = sitesProvider.getCurrentSiteId(); - this.isSplitViewOn = this.svComponent && this.svComponent.isOn(); - - // Check if site supports editing and deleting. No need to check allowed types, event.canedit already does it. - this.canEdit = this.calendarProvider.canEditEventsInSite(); - this.canDelete = this.calendarProvider.canDeleteEventsInSite(); - - if (this.notificationsEnabled) { - this.calendarProvider.getEventReminders(this.eventId).then((reminders) => { - this.reminders = reminders; - }); - - this.calendarProvider.getDefaultNotificationTime().then((defaultTime) => { - this.defaultTime = defaultTime * 60; - }); - - // Calculate format to use. - this.notificationFormat = this.timeUtils.fixFormatForDatetime(this.timeUtils.convertPHPToMoment( - this.translate.instant('core.strftimedatetime'))); - } - - // Listen for event edited. If current event is edited, reload the data. - this.editEventObserver = eventsProvider.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => { - if (data && data.event && data.event.id == this.eventId) { - this.eventLoaded = false; - this.refreshEvent(true, false); - } - }, this.currentSiteId); - - // Refresh data if this calendar event is synchronized automatically. - this.syncObserver = eventsProvider.on(AddonCalendarSyncProvider.AUTO_SYNCED, this.checkSyncResult.bind(this, false), - this.currentSiteId); - - // Refresh data if calendar events are synchronized manually but not by this page. - this.manualSyncObserver = eventsProvider.on(AddonCalendarSyncProvider.MANUAL_SYNCED, this.checkSyncResult.bind(this, true), - this.currentSiteId); - - // Refresh online status when changes. - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.isOnline = this.appProvider.isOnline(); - }); - }); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.syncIcon = 'spinner'; - - this.fetchEvent(); - } - - /** - * Fetches the event and updates the view. - * - * @param sync Whether it should try to synchronize offline events. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - fetchEvent(sync?: boolean, showErrors?: boolean): Promise { - const currentSite = this.sitesProvider.getCurrentSite(), - canGetById = this.calendarProvider.isGetEventByIdAvailableInSite(); - let promise, - deleted = false; - - this.isOnline = this.appProvider.isOnline(); - - if (sync) { - // Try to synchronize offline events. - promise = this.calendarSync.syncEvents().then((result) => { - if (result.warnings && result.warnings.length) { - this.domUtils.showErrorModal(result.warnings[0]); - } - - if (result.deleted && result.deleted.indexOf(this.eventId) != -1) { - // This event was deleted during the sync. - deleted = true; - } - - if (result.updated) { - // Trigger a manual sync event. - result.source = 'event'; - - this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId); - } - }).catch((error) => { - if (showErrors) { - this.domUtils.showErrorModalDefault(error, 'core.errorsync', true); - } - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - if (deleted) { - return; - } - - const promises = []; - - // Get the event data. - if (canGetById) { - promises.push(this.calendarProvider.getEventById(this.eventId)); - } else { - promises.push(this.calendarProvider.getEvent(this.eventId)); - } - - // Get offline data. - promises.push(this.calendarOffline.getEvent(this.eventId).catch(() => { - // No offline data. - })); - - return Promise.all(promises).then((results) => { - if (results[1]) { - // There is offline data, apply it. - this.hasOffline = true; - Object.assign(results[0], results[1]); - } else { - this.hasOffline = false; - } - - return results[0]; - }); - - }).then((event) => { - if (deleted) { - return; - } - - const promises = []; - - this.calendarHelper.formatEventData(event); - this.event = event; - - this.currentTime = this.timeUtils.timestamp(); - this.notificationMin = this.timeUtils.userDate(this.currentTime * 1000, 'YYYY-MM-DDTHH:mm', false); - this.notificationMax = this.timeUtils.userDate((event.timestart + event.timeduration) * 1000, - 'YYYY-MM-DDTHH:mm', false); - - // Reset some of the calculated data. - this.categoryPath = ''; - this.courseName = ''; - this.courseUrl = ''; - this.moduleUrl = ''; - - if (event.moduleIcon) { - // It's a module event, translate the module name to the current language. - const name = this.courseProvider.translateModuleName(event.modulename); - if (name.indexOf('core.mod_') === -1) { - event.moduleName = name; - } - - // Get the module URL. - if (canGetById) { - this.moduleUrl = event.url; - } - } - - // If the event belongs to a course, get the course name and the URL to view it. - if (canGetById && event.course && event.course.id != this.siteHomeId) { - this.courseId = event.course.id; - this.courseName = event.course.fullname; - this.courseUrl = event.course.viewurl; - } else if (event.courseid && event.courseid != this.siteHomeId) { - // Retrieve the course. - promises.push(this.coursesProvider.getUserCourse(event.courseid, true).then((course) => { - this.courseId = course.id; - this.courseName = course.fullname; - this.courseUrl = currentSite ? this.textUtils.concatenatePaths(currentSite.siteUrl, - '/course/view.php?id=' + event.courseid) : ''; - }).catch(() => { - // Error getting course, just don't show the course name. - })); - } - - // If it's a group event, get the name of the group. - const courseId = canGetById && event.course ? event.course.id : event.courseid; - if (courseId && event.groupid) { - promises.push(this.groupsProvider.getUserGroupsInCourse(event.courseid).then((groups) => { - const group = groups.find((group) => { - return group.id == event.groupid; - }); - - this.groupName = group ? group.name : ''; - }).catch(() => { - // Error getting groups, just don't show the group name. - this.groupName = ''; - })); - } - - if (canGetById && event.iscategoryevent && event.category) { - this.categoryPath = event.category.nestedname; - } - - if (event.location) { - // Build a link to open the address in maps. - event.location = this.textUtils.decodeHTML(event.location); - event.encodedLocation = this.textUtils.buildAddressURL(event.location); - } - - // Check if event was deleted in offine. - promises.push(this.calendarOffline.isEventDeleted(this.eventId).then((deleted) => { - event.deleted = deleted; - })); - - // Re-calculate the formatted time so it uses the device date. - promises.push(this.calendarProvider.getCalendarTimeFormat().then((timeFormat) => { - this.calendarProvider.formatEventTime(event, timeFormat).then((time) => { - event.formattedtime = time; - }); - })); - - return Promise.all(promises); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevent', true); - }).finally(() => { - this.eventLoaded = true; - this.syncIcon = 'sync'; - }); - } - - /** - * Add a reminder for this event. - */ - addNotificationTime(): void { - if (this.notificationTimeText && this.event && this.event.id) { - let notificationTime = this.timeUtils.convertToTimestamp(this.notificationTimeText); - - const currentTime = this.timeUtils.timestamp(), - minute = Math.floor(currentTime / 60) * 60; - - // Check if the notification time is in the same minute as we are, so the notification is triggered. - if (notificationTime >= minute && notificationTime < minute + 60) { - notificationTime = currentTime + 1; - } - - this.calendarProvider.addEventReminder(this.event, notificationTime).then(() => { - this.calendarProvider.getEventReminders(this.eventId).then((reminders) => { - this.reminders = reminders; - }); - - this.notificationTimeText = null; - }); - } - } - - /** - * Cancel the selected notification. - * - * @param id Reminder ID. - * @param e Click event. - */ - cancelNotification(id: number, e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - this.domUtils.showDeleteConfirm().then(() => { - const modal = this.domUtils.showModalLoading('core.deleting', true); - this.calendarProvider.deleteEventReminder(id).then(() => { - this.calendarProvider.getEventReminders(this.eventId).then((reminders) => { - this.reminders = reminders; - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error deleting reminder'); - }).finally(() => { - modal.dismiss(); - }); - }).catch(() => { - // Cancelled. - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @param done Function to call when done. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - doRefresh(refresher?: any, done?: () => void, showErrors?: boolean): Promise { - if (this.eventLoaded) { - return this.refreshEvent(true, showErrors).finally(() => { - refresher && refresher.complete(); - done && done(); - }); - } - - return Promise.resolve(); - } - - /** - * Refresh the event. - * - * @param sync Whether it should try to synchronize offline events. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - refreshEvent(sync?: boolean, showErrors?: boolean): Promise { - this.syncIcon = 'spinner'; - - const promises = []; - - promises.push(this.calendarProvider.invalidateEvent(this.eventId)); - promises.push(this.calendarProvider.invalidateTimeFormat()); - - return Promise.all(promises).catch(() => { - // Ignore errors. - }).then(() => { - return this.fetchEvent(sync, showErrors); - }); - } - - /** - * Open the page to edit the event. - */ - openEdit(): void { - // Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav. - const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - navCtrl.push('AddonCalendarEditEventPage', {eventId: this.eventId}); - } - - /** - * Delete the event. - */ - deleteEvent(): void { - const title = this.translate.instant('addon.calendar.deleteevent'), - options: any = {}; - let message: string; - - if (this.event.eventcount > 1) { - // It's a repeated event. - message = this.translate.instant('addon.calendar.confirmeventseriesdelete', - {$a: {name: this.event.name, count: this.event.eventcount}}); - - options.inputs = [ - { - type: 'radio', - name: 'deleteall', - checked: true, - value: false, - label: this.translate.instant('addon.calendar.deleteoneevent') - }, - { - type: 'radio', - name: 'deleteall', - checked: false, - value: true, - label: this.translate.instant('addon.calendar.deleteallevents') - } - ]; - } else { - // Not repeated, display a simple confirm. - message = this.translate.instant('addon.calendar.confirmeventdelete', {$a: this.event.name}); - } - - this.domUtils.showConfirm(message, title, undefined, undefined, options).then((deleteAll) => { - - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.calendarProvider.deleteEvent(this.event.id, this.event.name, deleteAll).then((sent) => { - let promise; - - if (sent) { - // Event deleted, invalidate right days & months. - promise = this.calendarHelper.refreshAfterChangeEvent(this.event, deleteAll ? this.event.eventcount : 1) - .catch(() => { - // Ignore errors. - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - // Trigger an event. - this.eventsProvider.trigger(AddonCalendarProvider.DELETED_EVENT_EVENT, { - eventId: this.eventId, - sent: sent - }, this.sitesProvider.getCurrentSiteId()); - - if (sent) { - this.domUtils.showToast('addon.calendar.eventcalendareventdeleted', true, 3000, undefined, false); - - // Event deleted, close the view. - if (!this.svComponent || !this.svComponent.isOn()) { - this.navCtrl.pop(); - } - } else { - // Event deleted in offline, just mark it as deleted. - this.event.deleted = true; - } - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error deleting event.'); - }).finally(() => { - modal.dismiss(); - }); - }, () => { - // User canceled. - }); - } - - /** - * Undo delete the event. - */ - undoDelete(): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.calendarOffline.unmarkDeleted(this.event.id).then(() => { - - // Trigger an event. - this.eventsProvider.trigger(AddonCalendarProvider.UNDELETED_EVENT_EVENT, { - eventId: this.eventId - }, this.sitesProvider.getCurrentSiteId()); - - this.event.deleted = false; - - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error undeleting event.'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Check the result of an automatic sync or a manual sync not done by this page. - * - * @param isManual Whether it's a manual sync. - * @param data Sync result. - */ - protected checkSyncResult(isManual: boolean, data: any): void { - if (!data) { - return; - } - - if (data.deleted && data.deleted.indexOf(this.eventId) != -1) { - this.domUtils.showToast('addon.calendar.eventcalendareventdeleted', true, 3000, undefined, false); - - // Event was deleted, close the view. - if (!this.svComponent || !this.svComponent.isOn()) { - this.navCtrl.pop(); - } - } else if (data.events && (!isManual || data.source != 'event')) { - const event = data.events.find((ev) => { - return ev.id == this.eventId; - }); - - if (event) { - this.eventLoaded = false; - this.refreshEvent(); - } - } - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.editEventObserver && this.editEventObserver.off(); - this.syncObserver && this.syncObserver.off(); - this.manualSyncObserver && this.manualSyncObserver.off(); - this.onlineObserver && this.onlineObserver.unsubscribe(); - } -} diff --git a/src/addon/calendar/pages/index/index.html b/src/addon/calendar/pages/index/index.html deleted file mode 100644 index 005d68f25..000000000 --- a/src/addon/calendar/pages/index/index.html +++ /dev/null @@ -1,38 +0,0 @@ - - - {{ (showCalendar ? 'addon.calendar.calendarevents' : 'addon.calendar.upcomingevents') | translate }} - - - - - - - - - - - - - - - - - - - {{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendar' | translate} }} - - - - - - - - - - - diff --git a/src/addon/calendar/pages/index/index.module.ts b/src/addon/calendar/pages/index/index.module.ts deleted file mode 100644 index 91a657304..000000000 --- a/src/addon/calendar/pages/index/index.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCalendarComponentsModule } from '../../components/components.module'; -import { AddonCalendarIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonCalendarIndexPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - AddonCalendarComponentsModule, - IonicPageModule.forChild(AddonCalendarIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonCalendarIndexPageModule {} diff --git a/src/addon/calendar/pages/index/index.ts b/src/addon/calendar/pages/index/index.ts deleted file mode 100644 index 6cdf432b2..000000000 --- a/src/addon/calendar/pages/index/index.ts +++ /dev/null @@ -1,400 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OFx ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild, NgZone } from '@angular/core'; -import { IonicPage, NavParams, NavController, PopoverController } from 'ionic-angular'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonCalendarProvider } from '../../providers/calendar'; -import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; -import { AddonCalendarHelperProvider, AddonCalendarFilter } from '../../providers/helper'; -import { AddonCalendarCalendarComponent } from '../../components/calendar/calendar'; -import { AddonCalendarUpcomingEventsComponent } from '../../components/upcoming-events/upcoming-events'; -import { AddonCalendarFilterPopoverComponent } from '../../components/filter/filter'; -import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; -import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; -import { Network } from '@ionic-native/network'; - -/** - * Page that displays the calendar events. - */ -@IonicPage({ segment: 'addon-calendar-index' }) -@Component({ - selector: 'page-addon-calendar-index', - templateUrl: 'index.html', -}) -export class AddonCalendarIndexPage implements OnInit, OnDestroy { - @ViewChild(AddonCalendarCalendarComponent) calendarComponent: AddonCalendarCalendarComponent; - @ViewChild(AddonCalendarUpcomingEventsComponent) upcomingEventsComponent: AddonCalendarUpcomingEventsComponent; - - protected eventId: number; - protected currentSiteId: string; - - // Observers. - protected newEventObserver: any; - protected discardedObserver: any; - protected editEventObserver: any; - protected deleteEventObserver: any; - protected undeleteEventObserver: any; - protected syncObserver: any; - protected manualSyncObserver: any; - protected onlineObserver: any; - protected filterChangedObserver: any; - - year: number; - month: number; - canCreate = false; - courses: any[]; - notificationsEnabled = false; - loaded = false; - hasOffline = false; - isOnline = false; - syncIcon: string; - showCalendar = true; - loadUpcoming = false; - filter: AddonCalendarFilter = { - filtered: false, - courseId: null, - categoryId: null, - course: true, - group: true, - site: true, - user: true, - category: true - }; - - constructor(localNotificationsProvider: CoreLocalNotificationsProvider, - navParams: NavParams, - network: Network, - zone: NgZone, - sitesProvider: CoreSitesProvider, - private navCtrl: NavController, - private domUtils: CoreDomUtilsProvider, - private calendarProvider: AddonCalendarProvider, - private calendarOffline: AddonCalendarOfflineProvider, - private calendarHelper: AddonCalendarHelperProvider, - private calendarSync: AddonCalendarSyncProvider, - private eventsProvider: CoreEventsProvider, - private coursesHelper: CoreCoursesHelperProvider, - private appProvider: CoreAppProvider, - private popoverCtrl: PopoverController) { - - this.eventId = navParams.get('eventId') || false; - this.year = navParams.get('year'); - this.month = navParams.get('month'); - this.notificationsEnabled = localNotificationsProvider.isAvailable(); - this.currentSiteId = sitesProvider.getCurrentSiteId(); - this.loadUpcoming = !!navParams.get('upcoming'); - this.showCalendar = !this.loadUpcoming; - - AddonCalendarProvider.ALL_TYPES.forEach((name) => { - this.filter[name] = true; - }); - this.filter.courseId = navParams.get('courseId'); - - // Listen for events added. When an event is added, reload the data. - this.newEventObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => { - if (data && data.event) { - this.loaded = false; - this.refreshData(true, false, true); - } - }, this.currentSiteId); - - // Listen for new event discarded event. When it does, reload the data. - this.discardedObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => { - this.loaded = false; - this.refreshData(true, false, true); - }, this.currentSiteId); - - // Listen for events edited. When an event is edited, reload the data. - this.editEventObserver = eventsProvider.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => { - if (data && data.event) { - this.loaded = false; - this.refreshData(true, false, true); - } - }, this.currentSiteId); - - // Refresh data if calendar events are synchronized automatically. - this.syncObserver = eventsProvider.on(AddonCalendarSyncProvider.AUTO_SYNCED, (data) => { - this.loaded = false; - this.refreshData(false, false, true); - }, this.currentSiteId); - - // Refresh data if calendar events are synchronized manually but not by this page. - this.manualSyncObserver = eventsProvider.on(AddonCalendarSyncProvider.MANUAL_SYNCED, (data) => { - if (data && data.source != 'index') { - this.loaded = false; - this.refreshData(false, false, true); - } - }, this.currentSiteId); - - // Update the events when an event is deleted. - this.deleteEventObserver = eventsProvider.on(AddonCalendarProvider.DELETED_EVENT_EVENT, (data) => { - this.loaded = false; - this.refreshData(false, false, true); - }, this.currentSiteId); - - // Update the "hasOffline" property if an event deleted in offline is restored. - this.undeleteEventObserver = eventsProvider.on(AddonCalendarProvider.UNDELETED_EVENT_EVENT, (data) => { - this.calendarOffline.hasOfflineData().then((hasOffline) => { - this.hasOffline = hasOffline; - }); - }, this.currentSiteId); - - this.filterChangedObserver = this.eventsProvider.on(AddonCalendarProvider.FILTER_CHANGED_EVENT, (data) => { - this.filter = data; - - // Course viewed has changed, check if the user can create events for this course calendar. - this.calendarHelper.canEditEvents(this.filter['courseId']).then((canEdit) => { - this.canCreate = canEdit; - }); - }); - - // Refresh online status when changes. - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.isOnline = this.appProvider.isOnline(); - }); - }); - } - - /** - * View loaded. - */ - ngOnInit(): void { - if (this.eventId) { - // There is an event to load, open the event in a new state. - this.gotoEvent(this.eventId); - } - - this.fetchData(true, false); - } - - /** - * Fetch all the data required for the view. - * - * @param sync Whether it should try to synchronize offline events. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - fetchData(sync?: boolean, showErrors?: boolean): Promise { - - this.syncIcon = 'spinner'; - this.isOnline = this.appProvider.isOnline(); - - let promise; - - if (sync) { - // Try to synchronize offline events. - promise = this.calendarSync.syncEvents().then((result) => { - if (result.warnings && result.warnings.length) { - this.domUtils.showErrorModal(result.warnings[0]); - } - - if (result.updated) { - // Trigger a manual sync event. - result.source = 'index'; - - this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId); - } - }).catch((error) => { - if (showErrors) { - this.domUtils.showErrorModalDefault(error, 'core.errorsync', true); - } - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - const promises = []; - - this.hasOffline = false; - - // Load courses for the popover. - promises.push(this.coursesHelper.getCoursesForPopover(this.filter.courseId).then((data) => { - this.courses = data.courses; - })); - - // Check if user can create events. - promises.push(this.calendarHelper.canEditEvents(this.filter.courseId).then((canEdit) => { - this.canCreate = canEdit; - })); - - // Check if there is offline data. - promises.push(this.calendarOffline.hasOfflineData().then((hasOffline) => { - this.hasOffline = hasOffline; - })); - - return Promise.all(promises); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); - }).finally(() => { - this.loaded = true; - this.syncIcon = 'sync'; - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @param done Function to call when done. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - doRefresh(refresher?: any, done?: () => void, showErrors?: boolean): Promise { - if (this.loaded) { - return this.refreshData(true, showErrors).finally(() => { - refresher && refresher.complete(); - done && done(); - }); - } - - return Promise.resolve(); - } - - /** - * Refresh the data. - * - * @param sync Whether it should try to synchronize offline events. - * @param showErrors Whether to show sync errors to the user. - * @param afterChange Whether the refresh is done after an event has changed or has been synced. - * @return Promise resolved when done. - */ - refreshData(sync?: boolean, showErrors?: boolean, afterChange?: boolean): Promise { - this.syncIcon = 'spinner'; - - const promises = []; - - promises.push(this.calendarProvider.invalidateAllowedEventTypes()); - - // Refresh the sub-component. - if (this.showCalendar && this.calendarComponent) { - promises.push(this.calendarComponent.refreshData(afterChange)); - } else if (!this.showCalendar && this.upcomingEventsComponent) { - promises.push(this.upcomingEventsComponent.refreshData(afterChange)); - } - - return Promise.all(promises).finally(() => { - return this.fetchData(sync, showErrors); - }); - } - - /** - * Navigate to a particular event. - * - * @param eventId Event to load. - */ - gotoEvent(eventId: number): void { - if (eventId < 0) { - // It's an offline event, go to the edit page. - this.openEdit(eventId); - } else { - this.navCtrl.push('AddonCalendarEventPage', { - id: eventId - }); - } - } - - /** - * View a certain day. - * - * @param data Data with the year, month and day. - */ - gotoDay(data: any): void { - const params: any = { - day: data.day, - month: data.month, - year: data.year - }; - - Object.keys(this.filter).forEach((key) => { - params[key] = this.filter[key]; - }); - - this.navCtrl.push('AddonCalendarDayPage', params); - } - - /** - * Show the context menu. - * - * @param event Event. - */ - openFilter(event: MouseEvent): void { - const popover = this.popoverCtrl.create(AddonCalendarFilterPopoverComponent, { - courses: this.courses, - filter: this.filter - }); - - popover.present({ - ev: event - }); - } - - /** - * Open page to create/edit an event. - * - * @param eventId Event ID to edit. - */ - openEdit(eventId?: number): void { - const params: any = {}; - - if (eventId) { - params.eventId = eventId; - } - if (this.filter.courseId) { - params.courseId = this.filter.courseId; - } - - this.navCtrl.push('AddonCalendarEditEventPage', params); - } - - /** - * Open calendar events settings. - */ - openSettings(): void { - this.navCtrl.push('AddonCalendarSettingsPage'); - } - - /** - * Toogle display: monthly view or upcoming events. - */ - toggleDisplay(): void { - this.showCalendar = !this.showCalendar; - - if (!this.showCalendar) { - this.loadUpcoming = true; - } - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.newEventObserver && this.newEventObserver.off(); - this.discardedObserver && this.discardedObserver.off(); - this.editEventObserver && this.editEventObserver.off(); - this.deleteEventObserver && this.deleteEventObserver.off(); - this.undeleteEventObserver && this.undeleteEventObserver.off(); - this.syncObserver && this.syncObserver.off(); - this.manualSyncObserver && this.manualSyncObserver.off(); - this.filterChangedObserver && this.filterChangedObserver.off(); - this.onlineObserver && this.onlineObserver.unsubscribe(); - } -} diff --git a/src/addon/calendar/pages/list/list.html b/src/addon/calendar/pages/list/list.html deleted file mode 100644 index 9f5e4178f..000000000 --- a/src/addon/calendar/pages/list/list.html +++ /dev/null @@ -1,66 +0,0 @@ - - - {{ 'addon.calendar.calendarevents' | translate }} - - - - - - - - - - - - - - - - - - {{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendar' | translate} }} - - - - - - - - - {{ event.timestart * 1000 | coreFormatDate: "strftimedayshort" }} - - - - -

-

- {{ event.timestart * 1000 | coreFormatDate: "strftimetime" }} - - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimetime" }} - - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimedatetimeshort" }} -

- - - {{ 'core.notsent' | translate }} - - - - {{ 'core.deletedoffline' | translate }} - -
-
-
- - -
- - - - - -
-
\ No newline at end of file diff --git a/src/addon/calendar/pages/list/list.module.ts b/src/addon/calendar/pages/list/list.module.ts deleted file mode 100644 index b5e7d1f99..000000000 --- a/src/addon/calendar/pages/list/list.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCalendarListPage } from './list'; - -@NgModule({ - declarations: [ - AddonCalendarListPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonCalendarListPage), - TranslateModule.forChild() - ], -}) -export class AddonCalendarListPageModule {} diff --git a/src/addon/calendar/pages/list/list.scss b/src/addon/calendar/pages/list/list.scss deleted file mode 100644 index 9f40d9746..000000000 --- a/src/addon/calendar/pages/list/list.scss +++ /dev/null @@ -1,5 +0,0 @@ -ion-app.app-root page-addon-calendar-list { - ion-note { - max-width: 30%; - } -} diff --git a/src/addon/calendar/pages/list/list.ts b/src/addon/calendar/pages/list/list.ts deleted file mode 100644 index 250c9054b..000000000 --- a/src/addon/calendar/pages/list/list.ts +++ /dev/null @@ -1,714 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, OnDestroy, NgZone } from '@angular/core'; -import { IonicPage, Content, NavParams, NavController, PopoverController } from 'ionic-angular'; -import { AddonCalendarProvider, AddonCalendarGetEventsEvent } from '../../providers/calendar'; -import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; -import { AddonCalendarHelperProvider, AddonCalendarFilter } from '../../providers/helper'; -import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreAppProvider } from '@providers/app'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import * as moment from 'moment'; -import { Network } from '@ionic-native/network'; -import { CoreConstants } from '@core/constants'; -import { AddonCalendarFilterPopoverComponent } from '../../components/filter/filter'; - -/** - * Page that displays the list of calendar events. - */ -@IonicPage({ segment: 'addon-calendar-list' }) -@Component({ - selector: 'page-addon-calendar-list', - templateUrl: 'list.html', -}) -export class AddonCalendarListPage implements OnDestroy { - @ViewChild(Content) content: Content; - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - protected initialTime = 0; - protected daysLoaded = 0; - protected emptyEventsTimes = 0; // Variable to identify consecutive calls returning 0 events. - protected categoriesRetrieved = false; - protected getCategories = false; - protected categories = {}; - protected siteHomeId: number; - protected eventId: number; - protected currentSiteId: string; - protected onlineEvents: AddonCalendarGetEventsEvent[] = []; - protected offlineEvents = []; - protected deletedEvents = []; - - // Observers. - protected obsDefaultTimeChange: any; - protected newEventObserver: any; - protected discardedObserver: any; - protected editEventObserver: any; - protected deleteEventObserver: any; - protected undeleteEventObserver: any; - protected syncObserver: any; - protected manualSyncObserver: any; - protected onlineObserver: any; - protected filterChangedObserver: any; - - courses: any[]; - eventsLoaded = false; - events = []; // Events (both online and offline). - notificationsEnabled = false; - filteredEvents: AddonCalendarGetEventsEvent[] = []; - canLoadMore = false; - loadMoreError = false; - canCreate = false; - hasOffline = false; - isOnline = false; - syncIcon: string; // Sync icon. - filter: AddonCalendarFilter = { - filtered: false, - courseId: null, - categoryId: null, - course: false, - group: false, - site: false, - user: false, - category: false - }; - - constructor( - navParams: NavParams, - sitesProvider: CoreSitesProvider, - network: Network, - zone: NgZone, - localNotificationsProvider: CoreLocalNotificationsProvider, - private calendarProvider: AddonCalendarProvider, - private domUtils: CoreDomUtilsProvider, - private coursesProvider: CoreCoursesProvider, - private utils: CoreUtilsProvider, - private calendarHelper: AddonCalendarHelperProvider, - private coursesHelper: CoreCoursesHelperProvider, - private eventsProvider: CoreEventsProvider, - private navCtrl: NavController, - private appProvider: CoreAppProvider, - private calendarOffline: AddonCalendarOfflineProvider, - private calendarSync: AddonCalendarSyncProvider, - private timeUtils: CoreTimeUtilsProvider, - private popoverCtrl: PopoverController) { - - this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); - this.notificationsEnabled = localNotificationsProvider.isAvailable(); - this.currentSiteId = sitesProvider.getCurrentSiteId(); - - if (this.notificationsEnabled) { - // Re-schedule events if default time changes. - this.obsDefaultTimeChange = eventsProvider.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => { - calendarProvider.scheduleEventsNotifications(this.onlineEvents); - }, this.currentSiteId); - } - - this.eventId = navParams.get('eventId') || false; - - AddonCalendarProvider.ALL_TYPES.forEach((name) => { - this.filter[name] = true; - }); - this.filter['courseId'] = navParams.get('courseId'); - - // Listen for events added. When an event is added, reload the data. - this.newEventObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => { - if (data && data.event) { - if (this.splitviewCtrl.isOn()) { - // Discussion added, clear details page. - this.splitviewCtrl.emptyDetails(); - } - - this.eventsLoaded = false; - this.refreshEvents(true, false).finally(() => { - - // In tablet mode try to open the event (only if it's an online event). - if (this.splitviewCtrl.isOn() && data.event.id > 0) { - this.gotoEvent(data.event.id); - } - }); - } - }, this.currentSiteId); - - // Listen for new event discarded event. When it does, reload the data. - this.discardedObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => { - if (this.splitviewCtrl.isOn()) { - // Discussion added, clear details page. - this.splitviewCtrl.emptyDetails(); - } - - this.eventsLoaded = false; - this.refreshEvents(true, false); - }, this.currentSiteId); - - // Listen for events edited. When an event is edited, reload the data. - this.editEventObserver = eventsProvider.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => { - if (data && data.event) { - this.eventsLoaded = false; - this.refreshEvents(true, false); - } - }, this.currentSiteId); - - // Refresh data if calendar events are synchronized automatically. - this.syncObserver = eventsProvider.on(AddonCalendarSyncProvider.AUTO_SYNCED, (data) => { - this.eventsLoaded = false; - this.refreshEvents(); - - if (this.splitviewCtrl.isOn() && this.eventId && data && data.deleted && data.deleted.indexOf(this.eventId) != -1) { - // Current selected event was deleted. Clear details. - this.splitviewCtrl.emptyDetails(); - } - }, this.currentSiteId); - - // Refresh data if calendar events are synchronized manually but not by this page. - this.manualSyncObserver = eventsProvider.on(AddonCalendarSyncProvider.MANUAL_SYNCED, (data) => { - if (data && data.source != 'list') { - this.eventsLoaded = false; - this.refreshEvents(); - } - - if (this.splitviewCtrl.isOn() && this.eventId && data && data.deleted && data.deleted.indexOf(this.eventId) != -1) { - // Current selected event was deleted. Clear details. - this.splitviewCtrl.emptyDetails(); - } - }, this.currentSiteId); - - // Update the list when an event is deleted. - this.deleteEventObserver = eventsProvider.on(AddonCalendarProvider.DELETED_EVENT_EVENT, (data) => { - if (data && !data.sent) { - // Event was deleted in offline. Just mark it as deleted, no need to refresh. - this.markAsDeleted(data.eventId, true); - this.deletedEvents.push(data.eventId); - this.hasOffline = true; - } else { - // Event deleted, clear the details if needed and refresh the view. - if (this.splitviewCtrl.isOn()) { - this.splitviewCtrl.emptyDetails(); - } - - this.eventsLoaded = false; - this.refreshEvents(); - } - }, 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.markAsDeleted(data.eventId, false); - - // 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.hasOffline = !!this.offlineEvents.length || !!this.deletedEvents.length; - } - }, this.currentSiteId); - - this.filterChangedObserver = this.eventsProvider.on(AddonCalendarProvider.FILTER_CHANGED_EVENT, (data) => { - this.filter = data; - - // Course viewed has changed, check if the user can create events for this course calendar. - this.calendarHelper.canEditEvents(this.filter['courseId']).then((canEdit) => { - this.canCreate = canEdit; - }); - - this.filterEvents(); - - this.domUtils.scrollToTop(this.content); - }); - - // Refresh online status when changes. - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.isOnline = this.appProvider.isOnline(); - }); - }); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - if (this.eventId) { - // There is an event to load, open the event in a new state. - this.gotoEvent(this.eventId); - } - - this.syncIcon = 'spinner'; - - this.fetchData(false, true, false).then(() => { - if (!this.eventId && this.splitviewCtrl.isOn() && this.events.length > 0) { - // Take first online event and load it. If no online event, load the first offline. - if (this.onlineEvents[0]) { - this.gotoEvent(this.onlineEvents[0].id); - } else { - this.gotoEvent(this.offlineEvents[0].id); - } - } - }); - } - - /** - * Fetch all the data required for the view. - * - * @param refresh Empty events array first. - * @param sync Whether it should try to synchronize offline events. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - fetchData(refresh?: boolean, sync?: boolean, showErrors?: boolean): Promise { - this.initialTime = this.timeUtils.timestamp(); - this.daysLoaded = 0; - this.emptyEventsTimes = 0; - this.isOnline = this.appProvider.isOnline(); - - let promise; - - if (sync) { - // Try to synchronize offline events. - promise = this.calendarSync.syncEvents().then((result) => { - if (result.warnings && result.warnings.length) { - this.domUtils.showErrorModal(result.warnings[0]); - } - - if (result.updated) { - // Trigger a manual sync event. - result.source = 'list'; - - this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId); - } - }).catch((error) => { - if (showErrors) { - this.domUtils.showErrorModalDefault(error, 'core.errorsync', true); - } - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - - const promises = []; - - this.hasOffline = false; - - promises.push(this.calendarHelper.canEditEvents(this.filter['courseId']).then((canEdit) => { - this.canCreate = canEdit; - })); - - // Load courses for the popover. - promises.push(this.coursesHelper.getCoursesForPopover(this.filter['courseId']).then((result) => { - this.courses = result.courses; - - return this.fetchEvents(refresh); - })); - - // Get offline events. - promises.push(this.calendarOffline.getAllEditedEvents().then((events) => { - this.hasOffline = this.hasOffline || !!events.length; - - // Format data and sort by timestart. - events.forEach((event) => { - event.offline = true; - this.calendarHelper.formatEventData(event); - }); - this.offlineEvents = this.sortEvents(events); - })); - - // Get events deleted in offline. - promises.push(this.calendarOffline.getAllDeletedEventsIds().then((ids) => { - this.hasOffline = this.hasOffline || !!ids.length; - this.deletedEvents = ids; - })); - - return Promise.all(promises); - }).finally(() => { - this.eventsLoaded = true; - this.syncIcon = 'sync'; - }); - } - - /** - * Fetches the events and updates the view. - * - * @param refresh Empty events array first. - * @return Promise resolved when done. - */ - fetchEvents(refresh?: boolean): Promise { - this.loadMoreError = false; - - return this.calendarProvider.getEventsList(this.initialTime, this.daysLoaded, AddonCalendarProvider.DAYS_INTERVAL) - .then((onlineEvents) => { - - if (onlineEvents.length === 0) { - this.emptyEventsTimes++; - if (this.emptyEventsTimes > 5) { // Stop execution if we retrieve empty list 6 consecutive times. - this.canLoadMore = false; - if (refresh) { - this.onlineEvents = []; - this.filteredEvents = []; - this.events = this.offlineEvents; - } - } else { - // No events returned, load next events. - this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL; - - return this.fetchEvents(); - } - } else { - onlineEvents.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); - - // Get the merged events of this period. - const events = this.mergeEvents(onlineEvents); - - this.getCategories = this.shouldLoadCategories(onlineEvents); - - if (refresh) { - this.onlineEvents = onlineEvents; - this.events = events; - } else { - // Filter events with same ID. Repeated events are returned once per WS call, show them only once. - this.onlineEvents = this.utils.mergeArraysWithoutDuplicates(this.onlineEvents, onlineEvents, 'id'); - this.events = this.utils.mergeArraysWithoutDuplicates(this.events, events, 'id'); - } - this.filterEvents(); - - // Calculate which evemts need to display the date. - this.filteredEvents.forEach((event, index): any => { - event.showDate = this.showDate(event, this.filteredEvents[index - 1]); - event.endsSameDay = this.endsSameDay(event); - }); - this.canLoadMore = true; - - // Schedule notifications for the events retrieved (might have new events). - this.calendarProvider.scheduleEventsNotifications(this.onlineEvents); - - this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL; - } - - // Resize the content so infinite loading is able to calculate if it should load more items or not. - // @todo: Infinite loading is not working if content is not high enough. - this.content.resize(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); - this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading. - }).then(() => { - // Success retrieving events. Get categories if needed. - if (this.getCategories) { - this.getCategories = false; - - return this.loadCategories(); - } - }); - } - - /** - * Function to load more events. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Resolved when done. - */ - loadMoreEvents(infiniteComplete?: any): Promise { - return this.fetchEvents().finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - protected filterEvents(): void { - this.filteredEvents = this.calendarHelper.getFilteredEvents(this.events, this.filter, this.categories); - } - - /** - * Returns if the current state should load categories or not. - * @param events Events to parse. - * @return True if categories should be loaded. - */ - protected shouldLoadCategories(events: any[]): boolean { - if (this.categoriesRetrieved || this.getCategories) { - // Use previous value - return this.getCategories; - } - - // Categories not loaded yet. We should get them if there's any category event. - const found = events.some((event) => event.categoryid != 'undefined' && event.categoryid > 0); - - return found || this.getCategories; - } - - /** - * Load categories to be able to filter events. - * - * @return Promise resolved when done. - */ - protected loadCategories(): Promise { - 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. - }); - } - - /** - * Merge a period of online events with the offline events of that period. - * - * @param onlineEvents Online events. - * @return Merged events. - */ - protected mergeEvents(onlineEvents: any[]): any[] { - if (!this.offlineEvents.length && !this.deletedEvents.length) { - // No offline events, nothing to merge. - return onlineEvents; - } - - const start = this.initialTime + (CoreConstants.SECONDS_DAY * this.daysLoaded), - end = start + (CoreConstants.SECONDS_DAY * AddonCalendarProvider.DAYS_INTERVAL) - 1; - let result = onlineEvents; - - if (this.deletedEvents.length) { - // Mark as deleted the events that were deleted in offline. - result.forEach((event) => { - event.deleted = this.deletedEvents.indexOf(event.id) != -1; - }); - } - - if (this.offlineEvents.length) { - // Remove the online events that were modified in offline. - result = result.filter((event) => { - const offlineEvent = this.offlineEvents.find((ev) => { - return ev.id == event.id; - }); - - return !offlineEvent; - }); - } - - // Now get the offline events that belong to this period. - const periodOfflineEvents = this.offlineEvents.filter((event) => { - if (this.daysLoaded == 0 && event.timestart < start) { - // Display offline events that are previous to current time to allow editing them. - return true; - } - - return (event.timestart >= start || event.timestart + event.timeduration >= start) && event.timestart <= end; - }); - - // Merge both arrays and sort them. - result = result.concat(periodOfflineEvents); - - return this.sortEvents(result); - } - - /** - * Sort events by timestart. - * - * @param 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; - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @param done Function to call when done. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - doRefresh(refresher?: any, done?: () => void, showErrors?: boolean): Promise { - if (this.eventsLoaded) { - return this.refreshEvents(true, showErrors).finally(() => { - refresher && refresher.complete(); - done && done(); - }); - } - - return Promise.resolve(); - } - - /** - * Refresh the events. - * - * @param sync Whether it should try to synchronize offline events. - * @param showErrors Whether to show sync errors to the user. - * @return Promise resolved when done. - */ - refreshEvents(sync?: boolean, showErrors?: boolean): Promise { - this.syncIcon = 'spinner'; - - const promises = []; - - promises.push(this.calendarProvider.invalidateEventsList()); - promises.push(this.calendarProvider.invalidateAllowedEventTypes()); - - if (this.categoriesRetrieved) { - promises.push(this.coursesProvider.invalidateCategories(0, true)); - this.categoriesRetrieved = false; - } - - return Promise.all(promises).finally(() => { - return this.fetchData(true, sync, showErrors); - }); - } - - /** - * Check date should be shown on event list for the current event. - * If date has changed from previous to current event it should be shown. - * - * @param event Current event where to show the date. - * @param prevEvent Previous event where to compare the date with. - * @return If date has changed and should be shown. - */ - protected showDate(event: any, prevEvent?: any): boolean { - if (!prevEvent) { - // First event, show it. - return true; - } - - // Check if day has changed. - return !moment(event.timestart * 1000).isSame(prevEvent.timestart * 1000, 'day'); - } - - /** - * Check if event ends the same date or not. - * - * @param event Event info. - * @return If date has changed and should be shown. - */ - protected endsSameDay(event: AddonCalendarGetEventsEvent): boolean { - if (!event.timeduration) { - // No duration. - return true; - } - - // Check if day has changed. - return moment(event.timestart * 1000).isSame((event.timestart + event.timeduration) * 1000, 'day'); - } - - /** - * Show the context menu. - * - * @param event Event. - */ - openFilter(event: MouseEvent): void { - const popover = this.popoverCtrl.create(AddonCalendarFilterPopoverComponent, { - courses: this.courses, - filter: this.filter - }); - - popover.present({ - ev: event - }); - } - - /** - * Open page to create/edit an event. - * - * @param eventId Event ID to edit. - */ - openEdit(eventId?: number): void { - this.eventId = undefined; - - const params: any = {}; - - if (eventId) { - params.eventId = eventId; - } - if (this.filter['courseId']) { - params.courseId = this.filter['courseId']; - } - - this.splitviewCtrl.push('AddonCalendarEditEventPage', params); - } - - /** - * Open calendar events settings. - */ - openSettings(): void { - this.navCtrl.push('AddonCalendarSettingsPage'); - } - - /** - * Navigate to a particular event. - * - * @param eventId Event to load. - */ - gotoEvent(eventId: number): void { - this.eventId = eventId; - - if (eventId < 0) { - // It's an offline event, go to the edit page. - this.openEdit(eventId); - } else { - this.splitviewCtrl.push('AddonCalendarEventPage', { - id: eventId - }); - } - } - - /** - * Find an event and mark it as deleted. - * - * @param eventId Event ID. - * @param deleted Whether to mark it as deleted or not. - */ - protected markAsDeleted(eventId: number, deleted: boolean): void { - const event = this.onlineEvents.find((event) => { - return event.id == eventId; - }); - - if (event) { - event.deleted = deleted; - } - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.obsDefaultTimeChange && this.obsDefaultTimeChange.off(); - this.newEventObserver && this.newEventObserver.off(); - this.discardedObserver && this.discardedObserver.off(); - this.editEventObserver && this.editEventObserver.off(); - this.deleteEventObserver && this.deleteEventObserver.off(); - this.undeleteEventObserver && this.undeleteEventObserver.off(); - this.syncObserver && this.syncObserver.off(); - this.manualSyncObserver && this.manualSyncObserver.off(); - this.filterChangedObserver && this.filterChangedObserver.off(); - this.onlineObserver && this.onlineObserver.unsubscribe(); - } -} diff --git a/src/addon/calendar/pages/settings/settings.html b/src/addon/calendar/pages/settings/settings.html deleted file mode 100644 index 095708be3..000000000 --- a/src/addon/calendar/pages/settings/settings.html +++ /dev/null @@ -1,22 +0,0 @@ - - - {{ 'core.settings.settings' | translate }} - - - - - - {{ 'addon.calendar.defaultnotificationtime' | translate }} - - {{ 'core.settings.disabled' | translate }} - {{ 600 | coreDuration }} - {{ 1800 | coreDuration }} - {{ 3600 | coreDuration }} - {{ 7200 | coreDuration }} - {{ 21600 | coreDuration }} - {{ 43200 | coreDuration }} - {{ 86400 | coreDuration }} - - - - diff --git a/src/addon/calendar/pages/settings/settings.module.ts b/src/addon/calendar/pages/settings/settings.module.ts deleted file mode 100644 index d6abe8a15..000000000 --- a/src/addon/calendar/pages/settings/settings.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonCalendarSettingsPage } from './settings'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; - -@NgModule({ - declarations: [ - AddonCalendarSettingsPage, - ], - imports: [ - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonCalendarSettingsPage), - TranslateModule.forChild() - ], -}) -export class AddonCalendarSettingsPageModule {} diff --git a/src/addon/calendar/pages/settings/settings.ts b/src/addon/calendar/pages/settings/settings.ts deleted file mode 100644 index cfd83d501..000000000 --- a/src/addon/calendar/pages/settings/settings.ts +++ /dev/null @@ -1,55 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage } from 'ionic-angular'; -import { AddonCalendarProvider } from '../../providers/calendar'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; - -/** - * Page that displays the calendar settings. - */ -@IonicPage({ segment: 'addon-calendar-settings' }) -@Component({ - selector: 'page-addon-calendar-settings', - templateUrl: 'settings.html', -}) -export class AddonCalendarSettingsPage { - - defaultTime = 0; - - constructor(private calendarProvider: AddonCalendarProvider, private eventsProvider: CoreEventsProvider, - private sitesProvider: CoreSitesProvider) { } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.calendarProvider.getDefaultNotificationTime().then((time) => { - this.defaultTime = time; - }); - } - - /** - * Update default time. - * - * @param newTime New time. - */ - updateDefaultTime(newTime: number): void { - this.calendarProvider.setDefaultNotificationTime(newTime); - this.eventsProvider.trigger(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, { time: newTime }, - this.sitesProvider.getCurrentSiteId()); - } -} diff --git a/src/addon/calendar/providers/calendar-offline.ts b/src/addon/calendar/providers/calendar-offline.ts deleted file mode 100644 index 072f579dc..000000000 --- a/src/addon/calendar/providers/calendar-offline.ts +++ /dev/null @@ -1,389 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Service to handle offline calendar events. - */ -@Injectable() -export class AddonCalendarOfflineProvider { - - // Variables for database. - static EVENTS_TABLE = 'addon_calendar_offline_events'; - static DELETED_EVENTS_TABLE = 'addon_calendar_deleted_events'; - - protected siteSchema: CoreSiteSchema = { - name: 'AddonCalendarOfflineProvider', - version: 1, - tables: [ - { - name: AddonCalendarOfflineProvider.EVENTS_TABLE, - columns: [ - { - name: 'id', // Negative for offline entries. - type: 'INTEGER', - primaryKey: true - }, - { - name: 'name', - type: 'TEXT', - notNull: true - }, - { - name: 'timestart', - type: 'INTEGER', - notNull: true - }, - { - name: 'eventtype', - type: 'TEXT', - notNull: true - }, - { - name: 'categoryid', - type: 'INTEGER', - }, - { - name: 'courseid', - type: 'INTEGER', - }, - { - name: 'groupcourseid', - type: 'INTEGER', - }, - { - name: 'groupid', - type: 'INTEGER', - }, - { - name: 'description', - type: 'TEXT', - }, - { - name: 'location', - type: 'TEXT', - }, - { - name: 'duration', - type: 'INTEGER', - }, - { - name: 'timedurationuntil', - type: 'INTEGER', - }, - { - name: 'timedurationminutes', - type: 'INTEGER', - }, - { - name: 'repeat', - type: 'INTEGER', - }, - { - name: 'repeats', - type: 'INTEGER', - }, - { - name: 'repeatid', - type: 'INTEGER', - }, - { - name: 'repeateditall', - type: 'INTEGER', - }, - { - name: 'userid', - type: 'INTEGER', - }, - { - name: 'timecreated', - type: 'INTEGER', - } - ] - }, - { - name: AddonCalendarOfflineProvider.DELETED_EVENTS_TABLE, - columns: [ - { - name: 'id', - type: 'INTEGER', - primaryKey: true - }, - { - name: 'name', // Save the name to be able to notify the user. - type: 'TEXT', - notNull: true - }, - { - name: 'repeat', - type: 'INTEGER' - }, - { - name: 'timemodified', - type: 'INTEGER', - } - ] - } - ] - }; - - constructor(private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider) { - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete an offline event. - * - * @param eventId Event ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteEvent(eventId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions: any = { - id: eventId - }; - - return site.getDb().deleteRecords(AddonCalendarOfflineProvider.EVENTS_TABLE, conditions); - }); - } - - /** - * Get the IDs of all the events created/edited/deleted in offline. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the IDs. - */ - getAllEventsIds(siteId?: string): Promise { - const promises = []; - - promises.push(this.getAllDeletedEventsIds(siteId)); - promises.push(this.getAllEditedEventsIds(siteId)); - - return Promise.all(promises).then((result) => { - return this.utils.mergeArraysWithoutDuplicates(result[0], result[1]); - }); - } - - /** - * Get all the events deleted in offline. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with all the events deleted in offline. - */ - getAllDeletedEvents(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonCalendarOfflineProvider.DELETED_EVENTS_TABLE); - }); - } - - /** - * Get the IDs of all the events deleted in offline. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the IDs of all the events deleted in offline. - */ - getAllDeletedEventsIds(siteId?: string): Promise { - return this.getAllDeletedEvents(siteId).then((events) => { - return events.map((event) => { - return event.id; - }); - }); - } - - /** - * Get all the events created/edited in offline. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with events. - */ - getAllEditedEvents(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonCalendarOfflineProvider.EVENTS_TABLE); - }); - } - - /** - * Get the IDs of all the events created/edited in offline. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with events IDs. - */ - getAllEditedEventsIds(siteId?: string): Promise { - return this.getAllEditedEvents(siteId).then((events) => { - return events.map((event) => { - return event.id; - }); - }); - } - - /** - * Get an event deleted in offline. - * - * @param eventId Event ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the deleted event. - */ - getDeletedEvent(eventId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions: any = { - id: eventId - }; - - return site.getDb().getRecord(AddonCalendarOfflineProvider.DELETED_EVENTS_TABLE, conditions); - }); - } - - /** - * Get an offline event. - * - * @param eventId Event ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the event. - */ - getEvent(eventId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions: any = { - id: eventId - }; - - return site.getDb().getRecord(AddonCalendarOfflineProvider.EVENTS_TABLE, conditions); - }); - } - - /** - * Check if there are offline events to send. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline events, false otherwise. - */ - hasEditedEvents(siteId?: string): Promise { - return this.getAllEditedEvents(siteId).then((events) => { - return !!events.length; - }).catch(() => { - // No offline data found, return false. - return false; - }); - } - - /** - * Check whether there's offline data for a site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline data, false otherwise. - */ - hasOfflineData(siteId?: string): Promise { - return this.getAllEventsIds(siteId).then((ids) => { - return ids.length > 0; - }); - } - - /** - * Check if an event is deleted. - * - * @param eventId Event ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether the event is deleted. - */ - isEventDeleted(eventId: number, siteId?: string): Promise { - return this.getDeletedEvent(eventId, siteId).then((event) => { - return !!event; - }).catch(() => { - return false; - }); - } - - /** - * Mark an event as deleted. - * - * @param eventId Event ID to delete. - * @param name Name of the event to delete. - * @param deleteAll If it's a repeated event. whether to delete all events of the series. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - markDeleted(eventId: number, name: string, deleteAll?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const event = { - id: eventId, - name: name || '', - repeat: deleteAll ? 1 : 0, - timemodified: Date.now() - }; - - return site.getDb().insertRecord(AddonCalendarOfflineProvider.DELETED_EVENTS_TABLE, event); - }); - } - - /** - * Offline version for adding a new discussion to a forum. - * - * @param eventId Event ID. If it's a new event, set it to undefined/null. - * @param data Event data. - * @param timeCreated The time the event was created. If not defined, current time. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the stored event. - */ - saveEvent(eventId: number, data: any, timeCreated?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - timeCreated = timeCreated || Date.now(); - - const event = { - id: eventId || -timeCreated, - name: data.name, - timestart: data.timestart, - eventtype: data.eventtype, - categoryid: data.categoryid || null, - courseid: data.courseid || null, - groupcourseid: data.groupcourseid || null, - groupid: data.groupid || null, - description: data.description && data.description.text, - location: data.location, - duration: data.duration, - timedurationuntil: data.timedurationuntil, - timedurationminutes: data.timedurationminutes, - repeat: data.repeat ? 1 : 0, - repeats: data.repeats, - repeatid: data.repeatid, - repeateditall: data.repeateditall ? 1 : 0, - timecreated: timeCreated, - userid: site.getUserId() - }; - - return site.getDb().insertRecord(AddonCalendarOfflineProvider.EVENTS_TABLE, event).then(() => { - return event; - }); - }); - } - - /** - * Unmark an event as deleted. - * - * @param eventId Event ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - unmarkDeleted(eventId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions: any = { - id: eventId - }; - - return site.getDb().deleteRecords(AddonCalendarOfflineProvider.DELETED_EVENTS_TABLE, conditions); - }); - } -} diff --git a/src/addon/calendar/providers/calendar-sync.ts b/src/addon/calendar/providers/calendar-sync.ts deleted file mode 100644 index 3dd0e6f16..000000000 --- a/src/addon/calendar/providers/calendar-sync.ts +++ /dev/null @@ -1,300 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSyncBaseProvider } from '@classes/base-sync'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreAppProvider } from '@providers/app'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonCalendarProvider } from './calendar'; -import { AddonCalendarOfflineProvider } from './calendar-offline'; -import { AddonCalendarHelperProvider } from './helper'; - -/** - * Service to sync calendar. - */ -@Injectable() -export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_calendar_autom_synced'; - static MANUAL_SYNCED = 'addon_calendar_manual_synced'; - static SYNC_ID = 'calendar'; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - courseProvider: CoreCourseProvider, - private eventsProvider: CoreEventsProvider, - loggerProvider: CoreLoggerProvider, - sitesProvider: CoreSitesProvider, - syncProvider: CoreSyncProvider, - textUtils: CoreTextUtilsProvider, - timeUtils: CoreTimeUtilsProvider, - private utils: CoreUtilsProvider, - private calendarProvider: AddonCalendarProvider, - private calendarOffline: AddonCalendarOfflineProvider, - private calendarHelper: AddonCalendarHelperProvider) { - - super('AddonCalendarSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils); - } - - /** - * Try to synchronize all events in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllEvents(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all calendar events', this.syncAllEventsFunc.bind(this), [force], siteId); - } - - /** - * Sync all events on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllEventsFunc(siteId: string, force?: boolean): Promise { - - const promise = force ? this.syncEvents(siteId) : this.syncEventsIfNeeded(siteId); - - return promise.then((result) => { - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonCalendarSyncProvider.AUTO_SYNCED, { - warnings: result.warnings, - events: result.events, - deleted: result.deleted - }, siteId); - } - }); - } - - /** - * Sync a site events only if a certain time has passed since the last time. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the events are synced or if it doesn't need to be synced. - */ - syncEventsIfNeeded(siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.isSyncNeeded(AddonCalendarSyncProvider.SYNC_ID, siteId).then((needed) => { - if (needed) { - return this.syncEvents(siteId); - } - }); - } - - /** - * Synchronize all offline events of a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncEvents(siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (this.isSyncing(AddonCalendarSyncProvider.SYNC_ID, siteId)) { - // There's already a sync ongoing for this site, return the promise. - return this.getOngoingSync(AddonCalendarSyncProvider.SYNC_ID, siteId); - } - - this.logger.debug('Try to sync calendar events for site ' + siteId); - - const result = { - warnings: [], - events: [], - deleted: [], - toinvalidate: [], - updated: false - }; - let offlineEventIds: number[]; - - // Get offline events. - const syncPromise = this.calendarOffline.getAllEventsIds(siteId).catch(() => { - // No offline data found, return empty list. - return []; - }).then((eventIds) => { - offlineEventIds = eventIds; - - if (!eventIds.length) { - // Nothing to sync. - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - const promises = []; - - offlineEventIds.forEach((eventId) => { - promises.push(this.syncOfflineEvent(eventId, result, siteId)); - }); - - return this.utils.allPromises(promises); - }).then(() => { - if (result.updated) { - - // Data has been sent to server. Now invalidate the WS calls. - const promises = [ - this.calendarProvider.invalidateEventsList(siteId), - this.calendarHelper.refreshAfterChangeEvents(result.toinvalidate, siteId) - ]; - - return Promise.all(promises).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(AddonCalendarSyncProvider.SYNC_ID, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // All done, return the result. - return result; - }); - - return this.addOngoingSync(AddonCalendarSyncProvider.SYNC_ID, syncPromise, siteId); - } - - /** - * Synchronize an offline event. - * - * @param eventId The event ID to sync. - * @param result Object where to store the result of the sync. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - protected syncOfflineEvent(eventId: number, result: any, siteId?: string): Promise { - - // Verify that event isn't blocked. - if (this.syncProvider.isBlocked(AddonCalendarProvider.COMPONENT, eventId, siteId)) { - this.logger.debug('Cannot sync event ' + eventId + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', - {$a: this.translate.instant('addon.calendar.calendarevent')})); - } - - // First of all, check if the event has been deleted. - return this.calendarOffline.getDeletedEvent(eventId, siteId).then((data) => { - // Delete the event. - return this.calendarProvider.deleteEventOnline(data.id, data.repeat, siteId).then(() => { - result.updated = true; - result.deleted.push(eventId); - - // Event sent, delete the offline data. - const promises = []; - - promises.push(this.calendarOffline.unmarkDeleted(eventId, siteId)); - promises.push(this.calendarOffline.deleteEvent(eventId, siteId).catch(() => { - // Ignore errors, maybe there was no edit data. - })); - - // We need the event data to invalidate it. Get it from local DB. - promises.push(this.calendarProvider.getEventFromLocalDb(eventId, siteId).then((event) => { - result.toinvalidate.push({ - event: event, - repeated: data.repeat ? event.eventcount : 1 - }); - }).catch(() => { - // Ignore errors. - })); - - return Promise.all(promises); - }).catch((error) => { - - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that the event cannot be created. Delete it. - result.updated = true; - - const promises = []; - - promises.push(this.calendarOffline.unmarkDeleted(eventId, siteId)); - promises.push(this.calendarOffline.deleteEvent(eventId, siteId).catch(() => { - // Ignore errors, maybe there was no edit data. - })); - - return Promise.all(promises).then(() => { - // Event deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.translate.instant('addon.calendar.calendarevent'), - name: data.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } - - // Local error, reject. - return Promise.reject(error); - }); - }, () => { - - // Not deleted. Now get the event data. - return this.calendarOffline.getEvent(eventId, siteId).then((event) => { - // Try to send the data. - const data = this.utils.clone(event); // Clone the object because it will be modified in the submit function. - - data.description = { - text: data.description, - format: 1 - }; - - return this.calendarProvider.submitEventOnline(eventId > 0 ? eventId : undefined, data, siteId).then((newEvent) => { - result.updated = true; - result.events.push(newEvent); - - // Add data to invalidate. - const numberOfRepetitions = data.repeat ? data.repeats : - (data.repeateditall && newEvent.repeatid ? newEvent.eventcount : 1); - - result.toinvalidate.push({ - event: newEvent, - repeated: numberOfRepetitions - }); - - // Event sent, delete the offline data. - return this.calendarOffline.deleteEvent(event.id, siteId); - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that the event cannot be created. Delete it. - result.updated = true; - - return this.calendarOffline.deleteEvent(event.id, siteId).then(() => { - // Event deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.translate.instant('addon.calendar.calendarevent'), - name: event.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } - - // Local error, reject. - return Promise.reject(error); - }); - }); - }); - } -} diff --git a/src/addon/calendar/providers/calendar.ts b/src/addon/calendar/providers/calendar.ts deleted file mode 100644 index a37528e3e..000000000 --- a/src/addon/calendar/providers/calendar.ts +++ /dev/null @@ -1,2210 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreAppProvider } from '@providers/app'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreConstants } from '@core/constants'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreConfigProvider } from '@providers/config'; -import { ILocalNotification } from '@ionic-native/local-notifications'; -import { SQLiteDB } from '@classes/sqlitedb'; -import { AddonCalendarOfflineProvider } from './calendar-offline'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreWSExternalWarning, CoreWSDate } from '@providers/ws'; -import * as moment from 'moment'; - -/** - * Service to handle calendar events. - */ -@Injectable() -export class AddonCalendarProvider { - static DAYS_INTERVAL = 30; - static COMPONENT = 'AddonCalendarEvents'; - static DEFAULT_NOTIFICATION_TIME_CHANGED = 'AddonCalendarDefaultNotificationTimeChangedEvent'; - static DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime'; - static DEFAULT_NOTIFICATION_TIME = 60; - static STARTING_WEEK_DAY = 'addon_calendar_starting_week_day'; - static NEW_EVENT_EVENT = 'addon_calendar_new_event'; - static NEW_EVENT_DISCARDED_EVENT = 'addon_calendar_new_event_discarded'; - static EDIT_EVENT_EVENT = 'addon_calendar_edit_event'; - static DELETED_EVENT_EVENT = 'addon_calendar_deleted_event'; - static UNDELETED_EVENT_EVENT = 'addon_calendar_undeleted_event'; - static FILTER_CHANGED_EVENT = 'addon_calendar_filter_changed_event'; - static TYPE_CATEGORY = 'category'; - static TYPE_COURSE = 'course'; - static TYPE_GROUP = 'group'; - static TYPE_SITE = 'site'; - static TYPE_USER = 'user'; - static ALL_TYPES = [ AddonCalendarProvider.TYPE_SITE, - AddonCalendarProvider.TYPE_CATEGORY, - AddonCalendarProvider.TYPE_COURSE, - AddonCalendarProvider.TYPE_GROUP, - AddonCalendarProvider.TYPE_USER ]; - - static CALENDAR_TF_24 = '%H:%M'; // Calendar time in 24 hours format. - static CALENDAR_TF_12 = '%I:%M %p'; // Calendar time in 12 hours format. - - protected ROOT_CACHE_KEY = 'mmaCalendar:'; - - protected weekDays = [ - { - shortname: 'addon.calendar.sun', - fullname: 'addon.calendar.sunday' - }, - { - shortname: 'addon.calendar.mon', - fullname: 'addon.calendar.monday' - }, - { - shortname: 'addon.calendar.tue', - fullname: 'addon.calendar.tuesday' - }, - { - shortname: 'addon.calendar.wed', - fullname: 'addon.calendar.wednesday' - }, - { - shortname: 'addon.calendar.thu', - fullname: 'addon.calendar.thursday' - }, - { - shortname: 'addon.calendar.fri', - fullname: 'addon.calendar.friday' - }, - { - shortname: 'addon.calendar.sat', - fullname: 'addon.calendar.saturday' - } - ]; - - // Variables for database. - static EVENTS_TABLE = 'addon_calendar_events_3'; - static REMINDERS_TABLE = 'addon_calendar_reminders'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonCalendarProvider', - version: 3, - canBeCleared: [ AddonCalendarProvider.EVENTS_TABLE ], - tables: [ - { - name: AddonCalendarProvider.EVENTS_TABLE, - columns: [ - { - name: 'id', - type: 'INTEGER', - primaryKey: true - }, - { - name: 'name', - type: 'TEXT', - notNull: true - }, - { - name: 'description', - type: 'TEXT' - }, - { - name: 'format', - type: 'INTEGER' - }, - { - name: 'eventtype', - type: 'TEXT' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'timestart', - type: 'INTEGER' - }, - { - name: 'timeduration', - type: 'INTEGER' - }, - { - name: 'categoryid', - type: 'INTEGER' - }, - { - name: 'groupid', - type: 'INTEGER' - }, - { - name: 'userid', - type: 'INTEGER' - }, - { - name: 'instance', - type: 'INTEGER' - }, - { - name: 'modulename', - type: 'TEXT' - }, - { - name: 'timemodified', - type: 'INTEGER' - }, - { - name: 'repeatid', - type: 'INTEGER' - }, - { - name: 'visible', - type: 'INTEGER' - }, - { - name: 'uuid', - type: 'TEXT' - }, - { - name: 'sequence', - type: 'INTEGER' - }, - { - name: 'subscriptionid', - type: 'INTEGER' - }, - { - name: 'location', - type: 'TEXT' - }, - { - name: 'eventcount', - type: 'INTEGER' - }, - { - name: 'timesort', - type: 'INTEGER' - }, - { - name: 'category', - type: 'TEXT' - }, - { - name: 'course', - type: 'TEXT' - }, - { - name: 'subscription', - type: 'TEXT' - }, - { - name: 'canedit', - type: 'INTEGER' - }, - { - name: 'candelete', - type: 'INTEGER' - }, - { - name: 'deleteurl', - type: 'TEXT' - }, - { - name: 'editurl', - type: 'TEXT' - }, - { - name: 'viewurl', - type: 'TEXT' - }, - { - name: 'formattedtime', - type: 'TEXT' - }, - { - name: 'isactionevent', - type: 'INTEGER' - }, - { - name: 'url', - type: 'TEXT' - }, - { - name: 'islastday', - type: 'INTEGER' - }, - { - name: 'popupname', - type: 'TEXT' - }, - { - name: 'mindaytimestamp', - type: 'INTEGER' - }, - { - name: 'maxdaytimestamp', - type: 'INTEGER' - }, - { - name: 'draggable', - type: 'INTEGER' - } - ] - }, - { - name: AddonCalendarProvider.REMINDERS_TABLE, - columns: [ - { - name: 'id', - type: 'INTEGER', - primaryKey: true - }, - { - name: 'eventid', - type: 'INTEGER' - }, - { - name: 'time', - type: 'INTEGER' - } - ], - uniqueKeys: [ - ['eventid', 'time'] - ] - } - ], - migrate(db: SQLiteDB, oldVersion: number, siteId: string): Promise | void { - if (oldVersion < 3) { - const newTable = AddonCalendarProvider.EVENTS_TABLE; - let oldTable = 'addon_calendar_events_2'; - - return db.tableExists(oldTable).catch(() => { - // The v2 table doesn't exist, try with v1. - oldTable = 'addon_calendar_events'; - - return db.tableExists(oldTable); - }).then(() => { - // Move the records from the old table. - // Move the records from the old table. - return db.getAllRecords(oldTable).then((events) => { - const promises = []; - - events.forEach((event) => { - promises.push(db.insertRecord(newTable, event)); - }); - - return Promise.all(promises); - }); - }).then(() => { - return db.dropTable(oldTable); - }).catch(() => { - // Old table does not exist, ignore. - }); - } - }, - }; - - protected logger; - - constructor(logger: CoreLoggerProvider, - private sitesProvider: CoreSitesProvider, - private groupsProvider: CoreGroupsProvider, - private coursesProvider: CoreCoursesProvider, - private textUtils: CoreTextUtilsProvider, - private timeUtils: CoreTimeUtilsProvider, - private urlUtils: CoreUrlUtilsProvider, - private localNotificationsProvider: CoreLocalNotificationsProvider, - private configProvider: CoreConfigProvider, - private utils: CoreUtilsProvider, - private calendarOffline: AddonCalendarOfflineProvider, - private appProvider: CoreAppProvider, - private translate: TranslateService, - private userProvider: CoreUserProvider) { - - this.logger = logger.getInstance('AddonCalendarProvider'); - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Check if a certain site allows deleting events. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if can delete. - * @since 3.3 - */ - canDeleteEvents(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.canDeleteEventsInSite(site); - }).catch(() => { - return false; - }); - } - - /** - * Check if a certain site allows deleting events. - * - * @param site Site. If not defined, use current site. - * @return Whether events can be deleted. - * @since 3.3 - */ - canDeleteEventsInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.wsAvailable('core_calendar_delete_calendar_events'); - } - - /** - * Check if a certain site allows creating and editing events. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if can create/edit. - * @since 3.7.1 - */ - canEditEvents(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.canEditEventsInSite(site); - }).catch(() => { - return false; - }); - } - - /** - * Check if a certain site allows creating and editing events. - * - * @param site Site. If not defined, use current site. - * @return Whether events can be created and edited. - * @since 3.7.1 - */ - canEditEventsInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - // The WS to create/edit events requires a fix that was integrated in 3.7.1. - return site && site.isVersionGreaterEqualThan('3.7.1'); - } - - /** - * Check if a certain site allows viewing events in monthly view. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if monthly view is supported. - * @since 3.4 - */ - canViewMonth(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.canViewMonthInSite(site); - }).catch(() => { - return false; - }); - } - - /** - * Check if a certain site allows viewing events in monthly view. - * - * @param site Site. If not defined, use current site. - * @return Whether monthly view is supported. - * @since 3.4 - */ - canViewMonthInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.wsAvailable('core_calendar_get_calendar_monthly_view'); - } - - /** - * Removes expired events from local DB. - * - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved when done. - */ - cleanExpiredEvents(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - if (this.canViewMonthInSite(site)) { - // Site supports monthly view, don't clean expired events because user can see past events. - return; - } - - return site.getDb().getRecordsSelect(AddonCalendarProvider.EVENTS_TABLE, 'timestart + timeduration < ?', - [this.timeUtils.timestamp()]).then((events) => { - return Promise.all(events.map((event) => { - return this.deleteLocalEvent(event.id, siteId); - })); - }); - }); - } - - /** - * Delete an event. - * - * @param eventId Event ID to delete. - * @param name Name of the event to delete. - * @param deleteAll If it's a repeated event. whether to delete all events of the series. - * @param forceOffline True to always save it in offline. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - deleteEvent(eventId: number, name: string, deleteAll?: boolean, forceOffline?: boolean, siteId?: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Function to store the submission to be synchronized later. - const storeOffline = (): Promise => { - return this.calendarOffline.markDeleted(eventId, name, deleteAll, siteId).then(() => { - return false; - }); - }; - - if (forceOffline || !this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - // If the event is already stored, discard it first. - return this.calendarOffline.unmarkDeleted(eventId, siteId).then(() => { - return this.deleteEventOnline(eventId, deleteAll, siteId).then(() => { - return true; - }).catch((error) => { - if (error && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Delete an event. It will fail if offline or cannot connect. - * - * @param eventId Event ID to delete. - * @param deleteAll If it's a repeated event. whether to delete all events of the series. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - deleteEventOnline(eventId: number, deleteAll?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - - const params = { - events: [ - { - eventid: eventId, - repeat: deleteAll ? 1 : 0 - } - ] - }, - preSets = { - responseExpected: false - }; - - return site.write('core_calendar_delete_calendar_events', params, preSets); - }); - } - - /** - * Delete a locally stored event cancelling all the reminders and notifications. - * - * @param eventId Event ID. - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Resolved when done. - */ - protected deleteLocalEvent(eventId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - siteId = site.getId(); - - const promises = []; - - promises.push(site.getDb().deleteRecords(AddonCalendarProvider.EVENTS_TABLE, {id: eventId})); - - promises.push(site.getDb().getRecords(AddonCalendarProvider.REMINDERS_TABLE, {eventid: eventId}).then((reminders) => { - return Promise.all(reminders.map((reminder) => { - return this.deleteEventReminder(reminder.id, siteId); - })); - })); - - return Promise.all(promises).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Format event time. Similar to calendar_format_event_time. - * - * @param event Event to format. - * @param format Calendar time format (from getCalendarTimeFormat). - * @param useCommonWords Whether to use common words like "Today", "Yesterday", etc. - * @param seenDay Timestamp of day currently seen. If set, the function will not add links to this day. - * @param showTime Determine the show time GMT timestamp. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the formatted event time. - */ - formatEventTime(event: AddonCalendarAnyEvent, format: string, useCommonWords: boolean = true, seenDay?: number, - showTime: number = 0, siteId?: string): Promise { - - const start = event.timestart * 1000, - end = (event.timestart + event.timeduration) * 1000; - let time; - - if (event.timeduration) { - - if (moment(start).isSame(end, 'day')) { - // Event starts and ends the same day. - if (event.timeduration == CoreConstants.SECONDS_DAY) { - time = this.translate.instant('addon.calendar.allday'); - } else { - time = this.timeUtils.userDate(start, format) + ' » ' + - this.timeUtils.userDate(end, format); - } - - } else { - // Event lasts more than one day. - const timeStart = this.timeUtils.userDate(start, format), - timeEnd = this.timeUtils.userDate(end, format), - promises = []; - - // Don't use common words when the event lasts more than one day. - let dayStart = this.getDayRepresentation(start, false) + ', ', - dayEnd = this.getDayRepresentation(end, false) + ', '; - - // Add links to the days if needed. - if (dayStart && (!seenDay || !moment(seenDay).isSame(start, 'day'))) { - promises.push(this.getViewUrl('day', event.timestart, undefined, siteId).then((url) => { - dayStart = this.urlUtils.buildLink(url, dayStart); - })); - } - if (dayEnd && (!seenDay || !moment(seenDay).isSame(end, 'day'))) { - promises.push(this.getViewUrl('day', end / 1000, undefined, siteId).then((url) => { - dayEnd = this.urlUtils.buildLink(url, dayEnd); - })); - } - - return Promise.all(promises).then(() => { - return dayStart + timeStart + ' » ' + dayEnd + timeEnd; - }); - } - } else { - // There is no time duration. - time = this.timeUtils.userDate(start, format); - } - - if (!showTime) { - // Display day + time. - if (seenDay && moment(seenDay).isSame(start, 'day')) { - // This day is currently being displayed, don't add an link. - return Promise.resolve(this.getDayRepresentation(start, useCommonWords) + ', ' + time); - } else { - // Add link to view the day. - return this.getViewUrl('day', event.timestart, undefined, siteId).then((url) => { - return this.urlUtils.buildLink(url, this.getDayRepresentation(start, useCommonWords)) + ', ' + time; - }); - } - } else { - return Promise.resolve(time); - } - } - - /** - * Get access information for a calendar (either course calendar or site calendar). - * - * @param courseId Course ID. If not defined, site calendar. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with object with access information. - * @since 3.7 - */ - getAccessInformation(courseId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params: any = {}, - preSets = { - cacheKey: this.getAccessInformationCacheKey(courseId) - }; - - if (courseId) { - params.courseid = courseId; - } - - return site.read('core_calendar_get_calendar_access_information', params, preSets); - }); - } - - /** - * Get cache key for calendar access information WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getAccessInformationCacheKey(courseId?: number): string { - return this.ROOT_CACHE_KEY + 'accessInformation:' + (courseId || 0); - } - - /** - * Get all calendar events from local Db. - * - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved with all the events. - */ - getAllEventsFromLocalDb(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getAllRecords(AddonCalendarProvider.EVENTS_TABLE); - }); - } - - /** - * Get the type of events a user can create (either course calendar or site calendar). - * - * @param courseId Course ID. If not defined, site calendar. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with an object indicating the types. - * @since 3.7 - */ - getAllowedEventTypes(courseId?: number, siteId?: string): Promise<{[name: string]: boolean}> { - return this.sitesProvider.getSite(siteId).then((site) => { - const params: any = {}, - preSets = { - cacheKey: this.getAllowedEventTypesCacheKey(courseId) - }; - - if (courseId) { - params.courseid = courseId; - } - - return site.read('core_calendar_get_allowed_event_types', params, preSets) - .then((response: AddonCalendarGetAllowedEventTypesResult) => { - // Convert the array to an object. - const result = {}; - - if (response.allowedeventtypes) { - response.allowedeventtypes.map((type) => { - result[type] = true; - }); - } - - return result; - }); - }); - } - - /** - * Get cache key for calendar allowed event types WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getAllowedEventTypesCacheKey(courseId?: number): string { - return this.ROOT_CACHE_KEY + 'allowedEventTypes:' + (courseId || 0); - } - - /** - * Get the "look ahead" for a certain user. - * - * @param siteId ID of the site. If not defined, use current site. - * @return Promise resolved with the look ahead (number of days). - */ - getCalendarLookAhead(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.userProvider.getUserPreference('calendar_lookahead').catch((error) => { - // Ignore errors. - }).then((value): any => { - if (value != null) { - return value; - } - - return site.getStoredConfig('calendar_lookahead'); - }); - }); - } - - /** - * Get the time format to use in calendar. - * - * @param siteId ID of the site. If not defined, use current site. - * @return Promise resolved with the format. - */ - getCalendarTimeFormat(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.userProvider.getUserPreference('calendar_timeformat').catch((error) => { - // Ignore errors. - }).then((format) => { - - if (!format || format === '0') { - format = site.getStoredConfig('calendar_site_timeformat'); - } - - if (format === AddonCalendarProvider.CALENDAR_TF_12) { - format = this.translate.instant('core.strftimetime12'); - } else if (format === AddonCalendarProvider.CALENDAR_TF_24) { - format = this.translate.instant('core.strftimetime24'); - } - - return format && format !== '0' ? format : this.translate.instant('core.strftimetime'); - }); - }); - } - - /** - * Return the representation day. Equivalent to Moodle's calendar_day_representation. - * - * @param time Timestamp to get the day from. - * @param useCommonWords Whether to use common words like "Today", "Yesterday", etc. - * @return The formatted date/time. - */ - getDayRepresentation(time: number, useCommonWords: boolean = true): string { - - if (!useCommonWords) { - // We don't want words, just a date. - return this.timeUtils.userDate(time, 'core.strftimedayshort'); - } - - const date = moment(time), - today = moment(); - - if (date.isSame(today, 'day')) { - return this.translate.instant('addon.calendar.today'); - - } else if (date.isSame(today.clone().subtract(1, 'days'), 'day')) { - return this.translate.instant('addon.calendar.yesterday'); - - } else if (date.isSame(today.clone().add(1, 'days'), 'day')) { - return this.translate.instant('addon.calendar.tomorrow'); - - } else { - return this.timeUtils.userDate(time, 'core.strftimedayshort'); - } - } - - /** - * Get the configured default notification time. - * - * @param siteId ID of the site. If not defined, use current site. - * @return Promise resolved with the default time. - */ - getDefaultNotificationTime(siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const key = AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId; - - return this.configProvider.get(key, AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME); - } - - /** - * Get a calendar event. If the server request fails and data is not cached, try to get it from local DB. - * - * @param id Event ID. - * @param siteId ID of the site. If not defined, use current site. - * @return Promise resolved when the event data is retrieved. - */ - getEvent(id: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const preSets = { - cacheKey: this.getEventCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_RARELY - }, - data = { - options: { - userevents: 0, - siteevents: 0, - }, - events: { - eventids: [ - id - ] - } - }; - - return site.read('core_calendar_get_calendar_events', data, preSets) - .then((response: AddonCalendarGetEventsResult) => { - // The WebService returns all category events. Check the response to search for the event we want. - const event = response.events.find((e) => { return e.id == id; }); - - return event || this.getEventFromLocalDb(id); - }).catch(() => { - return this.getEventFromLocalDb(id); - }); - }); - } - - /** - * Get a calendar event by ID. This function returns more data than getEvent, but it isn't available in all Moodles. - * - * @param id Event ID. - * @param siteId ID of the site. If not defined, use current site. - * @return Promise resolved when the event data is retrieved. - * @since 3.4 - */ - getEventById(id: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const preSets = { - cacheKey: this.getEventCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_RARELY - }, - data = { - eventid: id - }; - - return site.read('core_calendar_get_calendar_event_by_id', data, preSets) - .then((response: AddonCalendarGetEventByIdResult) => { - return response.event; - }).catch((error) => { - return this.getEventFromLocalDb(id).catch(() => { - return Promise.reject(error); - }); - }); - }); - } - - /** - * Get cache key for a single event WS call. - * - * @param id Event ID. - * @return Cache key. - */ - protected getEventCacheKey(id: number): string { - return this.ROOT_CACHE_KEY + 'events:' + id; - } - - /** - * Get a calendar event from local Db. - * - * @param id Event ID. - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved when the event data is retrieved. - */ - getEventFromLocalDb(id: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecord(AddonCalendarProvider.EVENTS_TABLE, { id: id }).then((event) => { - if (this.isGetEventByIdAvailableInSite(site)) { - // Calculate data to match the new WS. - event.descriptionformat = event.format; - event.iscourseevent = event.eventtype == AddonCalendarProvider.TYPE_COURSE; - event.iscategoryevent = event.eventtype == AddonCalendarProvider.TYPE_CATEGORY; - event.normalisedeventtype = this.getEventType(event); - event.category = this.textUtils.parseJSON(event.category, null); - event.course = this.textUtils.parseJSON(event.course, null); - event.subscription = this.textUtils.parseJSON(event.subscription, null); - } - - return event; - }); - }); - } - - /** - * Adds an event reminder and schedule a new notification. - * - * @param event Event to update its notification time. - * @param time New notification setting timestamp. - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved when the notification is updated. - */ - addEventReminder(event: AddonCalendarAnyEvent, time: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const reminder = { - eventid: event.id, - time: time - }; - - return site.getDb().insertRecord(AddonCalendarProvider.REMINDERS_TABLE, reminder).then((reminderId) => { - return this.scheduleEventNotification(event, reminderId, time, site.getId()); - }); - }); - } - - /** - * Return the normalised event type. - * Activity events are normalised to be course events. - * - * @param event The event to get its type. - * @return Event type. - */ - getEventType(event: any): string { - if (event.modulename) { - return 'course'; - } - - return event.eventtype; - } - - /** - * Remove an event reminder and cancel the notification. - * - * @param id Reminder ID. - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved when the notification is updated. - */ - deleteEventReminder(id: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - if (this.localNotificationsProvider.isAvailable()) { - this.localNotificationsProvider.cancel(id, AddonCalendarProvider.COMPONENT, site.getId()); - } - - return site.getDb().deleteRecords(AddonCalendarProvider.REMINDERS_TABLE, {id: id}); - }); - } - - /** - * Get calendar events for a certain day. - * - * @param year Year to get. - * @param month Month to get. - * @param day Day to get. - * @param courseId Course to get. - * @param categoryId Category to get. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the response. - */ - getDayEvents(year: number, month: number, day: number, courseId?: number, categoryId?: number, ignoreCache?: boolean, - siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - const data: any = { - year: year, - month: month, - day: day - }; - - if (courseId) { - data.courseid = courseId; - } - if (categoryId) { - data.categoryid = categoryId; - } - - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getDayEventsCacheKey(year, month, day, courseId, categoryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - return site.read('core_calendar_get_calendar_day_view', data, preSets).then((response: AddonCalendarCalendarDay) => { - this.storeEventsInLocalDB(response.events, siteId); - - return response; - }); - }); - } - - /** - * Get prefix cache key for day events WS calls. - * - * @return Prefix Cache key. - */ - protected getDayEventsPrefixCacheKey(): string { - return this.ROOT_CACHE_KEY + 'day:'; - } - - /** - * Get prefix cache key for a certain day for day events WS calls. - * - * @param year Year to get. - * @param month Month to get. - * @param day Day to get. - * @return Prefix Cache key. - */ - protected getDayEventsDayPrefixCacheKey(year: number, month: number, day: number): string { - return this.getDayEventsPrefixCacheKey() + year + ':' + month + ':' + day + ':'; - } - - /** - * Get cache key for day events WS calls. - * - * @param year Year to get. - * @param month Month to get. - * @param day Day to get. - * @param courseId Course to get. - * @param categoryId Category to get. - * @return Cache key. - */ - protected getDayEventsCacheKey(year: number, month: number, day: number, courseId?: number, categoryId?: number): string { - return this.getDayEventsDayPrefixCacheKey(year, month, day) + (courseId ? courseId : '') + ':' + - (categoryId ? categoryId : ''); - } - - /** - * Get a calendar reminders from local Db. - * - * @param id Event ID. - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved when the event data is retrieved. - */ - getEventReminders(id: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonCalendarProvider.REMINDERS_TABLE, {eventid: id}, 'time ASC'); - }); - } - - /** - * Get the events in a certain period. The period is calculated like this: - * start time: now + daysToStart - * end time: start time + daysInterval - * E.g. using provider.getEventsList(undefined, 30, 30) is going to get the events starting after 30 days from now - * and ending before 60 days from now. - * - * @param initialTime Timestamp when the first fetch was done. If not defined, current time. - * @param daysToStart Number of days from now to start getting events. - * @param daysInterval Number of days between timestart and timeend. - * @param siteId Site to get the events from. If not defined, use current site. - * @return Promise to be resolved when the events are retrieved. - */ - getEventsList(initialTime?: number, daysToStart: number = 0, daysInterval: number = AddonCalendarProvider.DAYS_INTERVAL, - siteId?: string): Promise { - - initialTime = initialTime || this.timeUtils.timestamp(); - - return this.sitesProvider.getSite(siteId).then((site) => { - siteId = site.getId(); - const promises = []; - let courses, groups; - - promises.push(this.coursesProvider.getUserCourses(false, siteId).then((data) => { - courses = data; - courses.push({ id: site.getSiteHomeId() }); // Add front page. - })); - promises.push(this.groupsProvider.getAllUserGroups(siteId).then((data) => { - groups = data; - })); - - return Promise.all(promises).then(() => { - const start = initialTime + (CoreConstants.SECONDS_DAY * daysToStart), - end = start + (CoreConstants.SECONDS_DAY * daysInterval) - 1, - data = { - options: { - userevents: 1, - siteevents: 1, - timestart: start, - timeend: end - }, - events: { - courseids: [], - groupids: [] - } - }; - - data.events.courseids = courses.map((course) => { - return course.id; - }); - data.events.groupids = groups.map((group) => { - return group.id; - }); - - // We need to retrieve cached data using cache key because we have timestamp in the params. - const preSets = { - cacheKey: this.getEventsListCacheKey(daysToStart, daysInterval), - getCacheUsingCacheKey: true, - uniqueCacheKey: true, - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - return site.read('core_calendar_get_calendar_events', data, preSets) - .then((response: AddonCalendarGetEventsResult) => { - - if (!this.canViewMonthInSite(site)) { - // Store events only in 3.1-3.3. In 3.4+ we'll use the new WS that return more info. - this.storeEventsInLocalDB(response.events, siteId); - } - - return response.events; - }); - }); - }); - } - - /** - * Get prefix cache key for events list WS calls. - * - * @return Prefix Cache key. - */ - protected getEventsListPrefixCacheKey(): string { - return this.ROOT_CACHE_KEY + 'events:'; - } - - /** - * Get cache key for events list WS calls. - * - * @param daysToStart Number of days from now to start getting events. - * @param daysInterval Number of days between timestart and timeend. - * @return Cache key. - */ - protected getEventsListCacheKey(daysToStart: number, daysInterval: number): string { - return this.getEventsListPrefixCacheKey() + daysToStart + ':' + daysInterval; - } - - /** - * Get calendar events from local Db that have the same repeatid. - * - * @param repeatId Repeat Id of the event. - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved with all the events. - */ - getLocalEventsByRepeatIdFromLocalDb(repeatId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonCalendarProvider.EVENTS_TABLE, {repeatid: repeatId}); - }); - } - /** - * Get monthly calendar events. - * - * @param year Year to get. - * @param month Month to get. - * @param courseId Course to get. - * @param categoryId Category to get. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the response. - */ - getMonthlyEvents(year: number, month: number, courseId?: number, categoryId?: number, ignoreCache?: boolean, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - const data: any = { - year: year, - month: month - }; - - // This parameter requires Moodle 3.5. - if (site.isVersionGreaterEqualThan('3.5')) { - // Set mini to 1 to prevent returning the course selector HTML. - data.mini = 1; - } - - if (courseId) { - data.courseid = courseId; - } - if (categoryId) { - data.categoryid = categoryId; - } - - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getMonthlyEventsCacheKey(year, month, courseId, categoryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - return site.read('core_calendar_get_calendar_monthly_view', data, preSets) - .then((response: AddonCalendarMonth) => { - - response.weeks.forEach((week) => { - week.days.forEach((day) => { - this.storeEventsInLocalDB(day.events, siteId); - }); - }); - - // Store starting week day preference, we need it in offline to show months that are not in cache. - if (this.appProvider.isOnline()) { - this.configProvider.set(AddonCalendarProvider.STARTING_WEEK_DAY, response.daynames[0].dayno); - } - - return response; - }); - }); - } - - /** - * Get prefix cache key for monthly events WS calls. - * - * @return Prefix Cache key. - */ - protected getMonthlyEventsPrefixCacheKey(): string { - return this.ROOT_CACHE_KEY + 'monthly:'; - } - - /** - * Get prefix cache key for a certain month for monthly events WS calls. - * - * @param year Year to get. - * @param month Month to get. - * @return Prefix Cache key. - */ - protected getMonthlyEventsMonthPrefixCacheKey(year: number, month: number): string { - return this.getMonthlyEventsPrefixCacheKey() + year + ':' + month + ':'; - } - - /** - * Get cache key for monthly events WS calls. - * - * @param year Year to get. - * @param month Month to get. - * @param courseId Course to get. - * @param categoryId Category to get. - * @return Cache key. - */ - protected getMonthlyEventsCacheKey(year: number, month: number, courseId?: number, categoryId?: number): string { - return this.getMonthlyEventsMonthPrefixCacheKey(year, month) + (courseId ? courseId : '') + ':' + - (categoryId ? categoryId : ''); - } - - /** - * Get upcoming calendar events. - * - * @param courseId Course to get. - * @param categoryId Category to get. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the response. - */ - getUpcomingEvents(courseId?: number, categoryId?: number, ignoreCache?: boolean, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - const data: any = {}; - - if (courseId) { - data.courseid = courseId; - } - if (categoryId) { - data.categoryid = categoryId; - } - - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getUpcomingEventsCacheKey(courseId, categoryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - return site.read('core_calendar_get_calendar_upcoming_view', data, preSets).then((response: AddonCalendarUpcoming) => { - this.storeEventsInLocalDB(response.events, siteId); - - return response; - }); - }); - } - - /** - * Get prefix cache key for upcoming events WS calls. - * - * @return Prefix Cache key. - */ - protected getUpcomingEventsPrefixCacheKey(): string { - return this.ROOT_CACHE_KEY + 'upcoming:'; - } - - /** - * Get cache key for upcoming events WS calls. - * - * @param courseId Course to get. - * @param categoryId Category to get. - * @return Cache key. - */ - protected getUpcomingEventsCacheKey(courseId?: number, categoryId?: number): string { - return this.getUpcomingEventsPrefixCacheKey() + (courseId ? courseId : '') + ':' + (categoryId ? categoryId : ''); - } - - /** - * Get URL to view a calendar. - * - * @param view The view to load: 'month', 'day', 'upcoming', etc. - * @param time Time to load. If not defined, current time. - * @param courseId Course to load. If not defined, all courses. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the URL.x - */ - getViewUrl(view: string, time?: number, courseId?: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - let url = this.textUtils.concatenatePaths(site.getURL(), 'calendar/view.php?view=' + view); - - if (time) { - url += '&time=' + time; - } - - if (courseId) { - url += '&course=' + courseId; - } - - return url; - }); - } - - /** - * Get the week days, already ordered according to a specified starting day. - * - * @param startingDay Starting day. 0=Sunday, 1=Monday, ... - * @return Week days. - */ - getWeekDays(startingDay?: number): any[] { - startingDay = startingDay || 0; - - return this.weekDays.slice(startingDay).concat(this.weekDays.slice(0, startingDay)); - } - - /** - * Invalidates access information. - * - * @param courseId Course ID. If not defined, site calendar. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAccessInformation(courseId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(courseId)); - }); - } - - /** - * Invalidates allowed event types. - * - * @param courseId Course ID. If not defined, site calendar. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllowedEventTypes(courseId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAllowedEventTypesCacheKey(courseId)); - }); - } - - /** - * Invalidates day events for all days. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllDayEvents(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getDayEventsPrefixCacheKey()); - }); - } - - /** - * Invalidates day events for a certain day. - * - * @param year Year. - * @param month Month. - * @param day Day. - * @return Promise resolved when the data is invalidated. - */ - invalidateDayEvents(year: number, month: number, day: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getDayEventsDayPrefixCacheKey(year, month, day)); - }); - } - - /** - * Invalidates events list and all the single events and related info. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved when the list is invalidated. - */ - invalidateEventsList(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - siteId = site.getId(); - - const promises = []; - - promises.push(this.coursesProvider.invalidateUserCourses(siteId)); - promises.push(this.groupsProvider.invalidateAllUserGroups(siteId)); - promises.push(site.invalidateWsCacheForKeyStartingWith(this.getEventsListPrefixCacheKey())); - - return Promise.all(promises); - }); - } - - /** - * Invalidates a single event. - * - * @param eventId List of courses or course ids. - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved when the list is invalidated. - */ - invalidateEvent(eventId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getEventCacheKey(eventId)); - }); - } - - /** - * Invalidates monthly events for all months. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllMonthlyEvents(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getMonthlyEventsPrefixCacheKey()); - }); - } - - /** - * Invalidates monthly events for a certain months. - * - * @param year Year. - * @param month Month. - * @return Promise resolved when the data is invalidated. - */ - invalidateMonthlyEvents(year: number, month: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getMonthlyEventsMonthPrefixCacheKey(year, month)); - }); - } - - /** - * Invalidates upcoming events for all courses and categories. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllUpcomingEvents(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getUpcomingEventsPrefixCacheKey()); - }); - } - - /** - * Invalidates upcoming events for a certain course or category. - * - * @param courseId Course ID. - * @param categoryId Category ID. - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateUpcomingEvents(courseId?: number, categoryId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getUpcomingEventsCacheKey(courseId, categoryId)); - }); - } - - /** - * Invalidates look ahead setting. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateLookAhead(siteId?: string): Promise { - return this.userProvider.invalidateUserPreference('calendar_lookahead', siteId); - } - - /** - * Invalidates time format setting. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateTimeFormat(siteId?: string): Promise { - return this.userProvider.invalidateUserPreference('calendar_timeformat', siteId); - } - - /** - * Check if Calendar is disabled in a certain site. - * - * @param site Site. If not defined, use current site. - * @return Whether it's disabled. - */ - isCalendarDisabledInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.isFeatureDisabled('CoreMainMenuDelegate_AddonCalendar'); - } - - /** - * Check if Calendar is disabled in a certain site. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if disabled, rejected or resolved with false otherwise. - */ - isDisabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.isCalendarDisabledInSite(site); - }); - } - - /** - * Check if the get event by ID WS is available. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if available. - * @since 3.4 - */ - isGetEventByIdAvailable(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.isGetEventByIdAvailableInSite(site); - }).catch(() => { - return false; - }); - } - - /** - * Check if the get event by ID WS is available in a certain site. - * - * @param site Site. If not defined, use current site. - * @return Whether it's available. - * @since 3.4 - */ - isGetEventByIdAvailableInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.wsAvailable('core_calendar_get_calendar_event_by_id'); - } - - /** - * Get the next events for all the sites and schedules their notifications. - * If an event notification time is 0, cancel its scheduled notification (if any). - * If local notification plugin is not enabled, resolve the promise. - * - * @return Promise resolved when all the notifications have been scheduled. - */ - scheduleAllSitesEventsNotifications(): Promise { - const notificationsEnabled = this.localNotificationsProvider.isAvailable(); - - return this.sitesProvider.getSitesIds().then((siteIds) => { - const promises = []; - - siteIds.forEach((siteId) => { - promises.push(this.cleanExpiredEvents(siteId).then(() => { - if (notificationsEnabled) { - // Check if calendar is disabled for the site. - return this.isDisabled(siteId).then((disabled) => { - if (!disabled) { - // Get first events. - return this.getEventsList(undefined, undefined, undefined, siteId).then((events) => { - return this.scheduleEventsNotifications(events, siteId); - }); - } - }); - } - })); - }); - - return Promise.all(promises); - }); - } - - /** - * Schedules an event notification. If time is 0, cancel scheduled notification if any. - * If local notification plugin is not enabled, resolve the promise. - * - * @param event Event to schedule. - * @param reminderId The reminder ID. - * @param time Notification setting time (in minutes). E.g. 10 means "notificate 10 minutes before start". - * @param siteId Site ID the event belongs to. If not defined, use current site. - * @return Promise resolved when the notification is scheduled. - */ - protected async scheduleEventNotification(event: AddonCalendarAnyEvent, reminderId: number, time: number, siteId?: string) - : Promise { - - if (!this.localNotificationsProvider.isAvailable()) { - return; - } - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (time === 0) { - // Cancel if it was scheduled. - return this.localNotificationsProvider.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId); - } - - if (time == -1) { - // If time is -1, get event default time to calculate the notification time. - time = await this.getDefaultNotificationTime(siteId); - - if (time == 0) { - // Default notification time is disabled, do not show. - return this.localNotificationsProvider.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId); - } - - time = event.timestart - (time * 60); - } - - time = time * 1000; - - if (time <= Date.now()) { - // This reminder is over, don't schedule. Cancel if it was scheduled. - return this.localNotificationsProvider.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId); - } - - const notification: ILocalNotification = { - id: reminderId, - title: event.name, - text: this.timeUtils.userDate(event.timestart * 1000, 'core.strftimedaydatetime', true), - icon: 'file://assets/img/icons/calendar.png', - trigger: { - at: new Date(time) - }, - data: { - eventid: event.id, - reminderid: reminderId, - siteid: siteId - } - }; - - return this.localNotificationsProvider.schedule(notification, AddonCalendarProvider.COMPONENT, siteId); - } - - /** - * Schedules the notifications for a list of events. - * If an event notification time is 0, cancel its scheduled notification (if any). - * If local notification plugin is not enabled, resolve the promise. - * - * @param events Events to schedule. - * @param siteId ID of the site the events belong to. If not defined, use current site. - * @return Promise resolved when all the notifications have been scheduled. - */ - scheduleEventsNotifications(events: AddonCalendarAnyEvent[], siteId?: string): Promise { - - if (this.localNotificationsProvider.isAvailable()) { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return Promise.all(events.map((event) => { - const timeEnd = (event.timestart + event.timeduration) * 1000; - - if (timeEnd <= new Date().getTime()) { - // The event has finished already, don't schedule it. - return this.deleteLocalEvent(event.id, siteId); - } - - return this.getEventReminders(event.id, siteId).then((reminders) => { - return Promise.all(reminders.map((reminder) => { - return this.scheduleEventNotification(event, reminder.id, reminder.time, siteId); - })); - }); - })); - } - - return Promise.resolve([]); - } - - /** - * Set the default notification time. - * - * @param time New default time. - * @param siteId ID of the site. If not defined, use current site. - * @return Promise resolved when stored. - */ - setDefaultNotificationTime(time: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const key = AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId; - - return this.configProvider.set(key, time); - } - - /** - * Store an event in local DB as it is. - * - * @param event Event to store. - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved when stored. - */ - storeEventInLocalDb(event: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - siteId = site.getId(); - - // If event does not exist on the DB, schedule the reminder. - return this.getEventFromLocalDb(event.id, site.id).catch(() => { - // Event does not exist. Check if any reminder exists first. - return this.getEventReminders(event.id, siteId).then((reminders) => { - if (reminders.length == 0) { - this.addEventReminder(event, -1, siteId); - } - }); - }).then(() => { - // Don't store data that can be calculated like formattedtime, iscategoryevent, etc. - const eventRecord = { - id: event.id, - name: event.name, - description: event.description, - format: event.descriptionformat || event.format, - eventtype: event.eventtype, - courseid: event.courseid, - timestart: event.timestart, - timeduration: event.timeduration, - categoryid: event.categoryid, - groupid: event.groupid, - userid: event.userid, - instance: event.instance, - modulename: event.modulename, - timemodified: event.timemodified, - repeatid: event.repeatid, - visible: event.visible, - uuid: event.uuid, - sequence: event.sequence, - subscriptionid: event.subscriptionid, - location: event.location, - eventcount: event.eventcount, - timesort: event.timesort, - category: event.category ? JSON.stringify(event.category) : undefined, - course: event.course ? JSON.stringify(event.course) : undefined, - subscription: event.subscription ? JSON.stringify(event.subscription) : undefined, - canedit: event.canedit ? 1 : 0, - candelete: event.candelete ? 1 : 0, - deleteurl: event.deleteurl, - editurl: event.editurl, - viewurl: event.viewurl, - isactionevent: event.isactionevent ? 1 : 0, - url: event.url, - islastday: event.islastday ? 1 : 0, - popupname: event.popupname, - mindaytimestamp: event.mindaytimestamp, - maxdaytimestamp: event.maxdaytimestamp, - draggable: event.draggable, - }; - - return site.getDb().insertRecord(AddonCalendarProvider.EVENTS_TABLE, eventRecord); - }); - }); - } - - /** - * Store events in local DB. - * - * @param events Events to store. - * @param siteId ID of the site the event belongs to. If not defined, use current site. - * @return Promise resolved when the events are stored. - */ - protected storeEventsInLocalDB(events: any[], siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - siteId = site.getId(); - - return Promise.all(events.map((event) => { - // If event does not exist on the DB, schedule the reminder. - return this.storeEventInLocalDb(event, siteId); - })); - }); - } - - /** - * Submit a calendar event. - * - * @param eventId ID of the event. If undefined/null, create a new event. - * @param formData Form data. - * @param timeCreated The time the event was created. Only if modifying a new offline event. - * @param forceOffline True to always save it in offline. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the event and a boolean indicating if data was sent to server or stored in offline. - */ - submitEvent(eventId: number, formData: any, timeCreated?: number, forceOffline?: boolean, siteId?: string): - Promise<{sent: boolean, event: AddonCalendarEvent}> { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Function to store the event to be synchronized later. - const storeOffline = (): Promise<{sent: boolean, event: any}> => { - return this.calendarOffline.saveEvent(eventId, formData, timeCreated, siteId).then((event) => { - return {sent: false, event: event}; - }); - }; - - if (forceOffline || !this.appProvider.isOnline()) { - // App is offline, store the event. - return storeOffline(); - } - - // If the event is already stored, discard it first. - return this.calendarOffline.deleteEvent(eventId, siteId).then(() => { - return this.submitEventOnline(eventId, formData, siteId).then((event) => { - return {sent: true, event: event}; - }).catch((error) => { - if (error && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Submit an event, either to create it or to edit it. It will fail if offline or cannot connect. - * - * @param eventId ID of the event. If undefined/null, create a new event. - * @param formData Form data. - * @param siteId Site ID. If not provided, current site. - * @return Promise resolved when done. - */ - submitEventOnline(eventId: number, formData: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - // Add data that is "hidden" in web. - formData.id = eventId || 0; - formData.userid = site.getUserId(); - formData.visible = 1; - formData.instance = 0; - - if (eventId > 0) { - formData['_qf__core_calendar_local_event_forms_update'] = 1; - } else { - formData['_qf__core_calendar_local_event_forms_create'] = 1; - } - - const params = { - formdata: this.utils.objectToGetParams(formData) - }; - - return site.write('core_calendar_submit_create_update_form', params) - .then((result: AddonCalendarSubmitCreateUpdateFormResult): AddonCalendarEvent => { - - if (result.validationerror) { - // Simulate a WS error. - return Promise.reject({ - message: this.translate.instant('core.invalidformdata'), - errorcode: 'validationerror' - }); - } - - return result.event; - }); - }); - } -} - -/** - * Data returned by calendar's events_exporter. - */ -export type AddonCalendarEvents = { - events: AddonCalendarEvent[]; // Events. - firstid: number; // Firstid. - lastid: number; // Lastid. -}; - -/** - * Data returned by calendar's events_grouped_by_course_exporter. - */ -export type AddonCalendarEventsGroupedByCourse = { - groupedbycourse: AddonCalendarEventsSameCourse[]; // Groupped by course. -}; - -/** - * Data returned by calendar's events_same_course_exporter. - */ -export type AddonCalendarEventsSameCourse = AddonCalendarEvents & { - courseid: number; // Courseid. -}; - -/** - * Data returned by calendar's event_exporter_base. - */ -export type AddonCalendarEventBase = { - id: number; // Id. - name: string; // Name. - description?: string; // Description. - descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - location?: string; // @since 3.6. Location. - categoryid?: number; // Categoryid. - groupid?: number; // Groupid. - userid?: number; // Userid. - repeatid?: number; // Repeatid. - eventcount?: number; // Eventcount. - modulename?: string; // Modulename. - instance?: number; // Instance. - eventtype: string; // Eventtype. - timestart: number; // Timestart. - timeduration: number; // Timeduration. - timesort: number; // Timesort. - visible: number; // Visible. - timemodified: number; // Timemodified. - icon: { - key: string; // Key. - component: string; // Component. - alttext: string; // Alttext. - }; - category?: { - id: number; // Id. - name: string; // Name. - idnumber: string; // Idnumber. - description?: string; // Description. - parent: number; // Parent. - coursecount: number; // Coursecount. - visible: number; // Visible. - timemodified: number; // Timemodified. - depth: number; // Depth. - nestedname: string; // Nestedname. - url: string; // Url. - }; - course?: { - id: number; // Id. - fullname: string; // Fullname. - shortname: string; // Shortname. - idnumber: string; // Idnumber. - summary: string; // Summary. - summaryformat: number; // Summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - startdate: number; // Startdate. - enddate: number; // Enddate. - visible: boolean; // @since 3.8. Visible. - fullnamedisplay: string; // Fullnamedisplay. - viewurl: string; // Viewurl. - courseimage: string; // @since 3.6. Courseimage. - progress?: number; // @since 3.6. Progress. - hasprogress: boolean; // @since 3.6. Hasprogress. - isfavourite: boolean; // @since 3.6. Isfavourite. - hidden: boolean; // @since 3.6. Hidden. - timeaccess?: number; // @since 3.6. Timeaccess. - showshortname: boolean; // @since 3.6. Showshortname. - coursecategory: string; // @since 3.7. Coursecategory. - }; - subscription?: { - displayeventsource: boolean; // Displayeventsource. - subscriptionname?: string; // Subscriptionname. - subscriptionurl?: string; // Subscriptionurl. - }; - canedit: boolean; // Canedit. - candelete: boolean; // Candelete. - deleteurl: string; // Deleteurl. - editurl: string; // Editurl. - viewurl: string; // Viewurl. - formattedtime: string; // Formattedtime. - isactionevent: boolean; // Isactionevent. - iscourseevent: boolean; // Iscourseevent. - iscategoryevent: boolean; // Iscategoryevent. - groupname?: string; // Groupname. - normalisedeventtype: string; // @since 3.7. Normalisedeventtype. - normalisedeventtypetext: string; // @since 3.7. Normalisedeventtypetext. -}; - -/** - * Data returned by calendar's event_exporter. Don't confuse it with AddonCalendarCalendarEvent. - */ -export type AddonCalendarEvent = AddonCalendarEventBase & { - url: string; // Url. - action?: { - name: string; // Name. - url: string; // Url. - itemcount: number; // Itemcount. - actionable: boolean; // Actionable. - showitemcount: boolean; // Showitemcount. - }; -}; - -/** - * Data returned by calendar's calendar_event_exporter. Don't confuse it with AddonCalendarEvent. - */ -export type AddonCalendarCalendarEvent = AddonCalendarEventBase & { - url: string; // Url. - islastday: boolean; // Islastday. - popupname: string; // Popupname. - mindaytimestamp?: number; // Mindaytimestamp. - mindayerror?: string; // Mindayerror. - maxdaytimestamp?: number; // Maxdaytimestamp. - maxdayerror?: string; // Maxdayerror. - draggable: boolean; // Draggable. -} & AddonCalendarCalendarEventCalculatedData; - -/** - * Any of the possible types of events. - */ -export type AddonCalendarAnyEvent = AddonCalendarGetEventsEvent | AddonCalendarEvent | AddonCalendarCalendarEvent; - -/** - * Data returned by calendar's calendar_day_exporter. Don't confuse it with AddonCalendarDay. - */ -export type AddonCalendarCalendarDay = { - events: AddonCalendarCalendarEvent[]; // Events. - defaulteventcontext: number; // Defaulteventcontext. - filter_selector: string; // Filter_selector. - courseid: number; // Courseid. - categoryid?: number; // Categoryid. - neweventtimestamp: number; // Neweventtimestamp. - date: CoreWSDate; - periodname: string; // Periodname. - previousperiod: CoreWSDate; - previousperiodlink: string; // Previousperiodlink. - previousperiodname: string; // Previousperiodname. - nextperiod: CoreWSDate; - nextperiodname: string; // Nextperiodname. - nextperiodlink: string; // Nextperiodlink. - larrow: string; // Larrow. - rarrow: string; // Rarrow. -}; - -/** - * Data returned by calendar's month_exporter. - */ -export type AddonCalendarMonth = { - url: string; // Url. - courseid: number; // Courseid. - categoryid?: number; // Categoryid. - filter_selector?: string; // Filter_selector. - weeks: AddonCalendarWeek[]; // Weeks. - daynames: AddonCalendarDayName[]; // Daynames. - view: string; // View. - date: CoreWSDate; - periodname: string; // Periodname. - includenavigation: boolean; // Includenavigation. - initialeventsloaded: boolean; // @since 3.5. Initialeventsloaded. - previousperiod: CoreWSDate; - previousperiodlink: string; // Previousperiodlink. - previousperiodname: string; // Previousperiodname. - nextperiod: CoreWSDate; - nextperiodname: string; // Nextperiodname. - nextperiodlink: string; // Nextperiodlink. - larrow: string; // Larrow. - rarrow: string; // Rarrow. - defaulteventcontext: number; // Defaulteventcontext. -}; - -/** - * Data returned by calendar's week_exporter. - */ -export type AddonCalendarWeek = { - prepadding: number[]; // Prepadding. - postpadding: number[]; // Postpadding. - days: AddonCalendarWeekDay[]; // Days. -}; - -/** - * Data returned by calendar's week_day_exporter. - */ -export type AddonCalendarWeekDay = AddonCalendarDay & { - istoday: boolean; // Istoday. - isweekend: boolean; // Isweekend. - popovertitle: string; // Popovertitle. - ispast?: boolean; // Calculated in the app. Whether the day is in the past. - filteredEvents?: AddonCalendarCalendarEvent[]; // Calculated in the app. Filtered events. -}; - -/** - * Data returned by calendar's day_exporter. Don't confuse it with AddonCalendarCalendarDay. - */ -export type AddonCalendarDay = { - seconds: number; // Seconds. - minutes: number; // Minutes. - hours: number; // Hours. - mday: number; // Mday. - wday: number; // Wday. - year: number; // Year. - yday: number; // Yday. - timestamp: number; // Timestamp. - neweventtimestamp: number; // Neweventtimestamp. - viewdaylink?: string; // Viewdaylink. - events: AddonCalendarCalendarEvent[]; // Events. - hasevents: boolean; // Hasevents. - calendareventtypes: string[]; // Calendareventtypes. - previousperiod: number; // Previousperiod. - nextperiod: number; // Nextperiod. - navigation: string; // Navigation. - haslastdayofevent: boolean; // Haslastdayofevent. -}; - -/** - * Data returned by calendar's day_name_exporter. - */ -export type AddonCalendarDayName = { - dayno: number; // Dayno. - shortname: string; // Shortname. - fullname: string; // Fullname. -}; - -/** - * Data returned by calendar's calendar_upcoming_exporter. - */ -export type AddonCalendarUpcoming = { - events: AddonCalendarCalendarEvent[]; // Events. - defaulteventcontext: number; // Defaulteventcontext. - filter_selector: string; // Filter_selector. - courseid: number; // Courseid. - categoryid?: number; // Categoryid. - isloggedin: boolean; // Isloggedin. - date: CoreWSDate; // @since 3.8. Date. -}; - -/** - * Result of WS core_calendar_get_calendar_access_information. - */ -export type AddonCalendarGetAccessInfoResult = { - canmanageentries: boolean; // Whether the user can manage entries. - canmanageownentries: boolean; // Whether the user can manage its own entries. - canmanagegroupentries: boolean; // Whether the user can manage group entries. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS core_calendar_get_allowed_event_types. - */ -export type AddonCalendarGetAllowedEventTypesResult = { - allowedeventtypes: string[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS core_calendar_get_calendar_events. - */ -export type AddonCalendarGetEventsResult = { - events: AddonCalendarGetEventsEvent[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Event data returned by WS core_calendar_get_calendar_events. - */ -export type AddonCalendarGetEventsEvent = { - id: number; // Event id. - name: string; // Event name. - description?: string; // Description. - format: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - courseid: number; // Course id. - categoryid?: number; // @since 3.4. Category id (only for category events). - groupid: number; // Group id. - userid: number; // User id. - repeatid: number; // Repeat id. - modulename?: string; // Module name. - instance: number; // Instance id. - eventtype: string; // Event type. - timestart: number; // Timestart. - timeduration: number; // Time duration. - visible: number; // Visible. - uuid?: string; // Unique id of ical events. - sequence: number; // Sequence. - timemodified: number; // Time modified. - subscriptionid?: number; // Subscription id. - showDate?: boolean; // Calculated in the app. Whether date should be shown before this event. - endsSameDay?: boolean; // Calculated in the app. Whether the event finishes the same day it starts. - deleted?: boolean; // Calculated in the app. Whether it has been deleted in offline. -}; - -/** - * Result of WS core_calendar_get_calendar_event_by_id. - */ -export type AddonCalendarGetEventByIdResult = { - event: AddonCalendarEvent; // Event. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS core_calendar_submit_create_update_form. - */ -export type AddonCalendarSubmitCreateUpdateFormResult = { - event?: AddonCalendarEvent; // Event. - validationerror: boolean; // Invalid form data. -}; - -/** - * Calculated data for AddonCalendarCalendarEvent. - */ -export type AddonCalendarCalendarEventCalculatedData = { - eventIcon?: string; // Calculated in the app. Event icon. - moduleIcon?: string; // Calculated in the app. Module icon. - formattedType?: string; // Calculated in the app. Formatted type. - duration?: number; // Calculated in the app. Duration of offline event. - format?: number; // Calculated in the app. Format of offline event. - timedurationuntil?: number; // Calculated in the app. Time duration until of offline event. - timedurationminutes?: number; // Calculated in the app. Time duration in minutes of offline event. - deleted?: boolean; // Calculated in the app. Whether it has been deleted in offline. - ispast?: boolean; // Calculated in the app. Whether the event is in the past. -}; diff --git a/src/addon/calendar/providers/helper.ts b/src/addon/calendar/providers/helper.ts deleted file mode 100644 index a6bed59b5..000000000 --- a/src/addon/calendar/providers/helper.ts +++ /dev/null @@ -1,531 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonCalendarProvider } from './calendar'; -import { CoreConstants } from '@core/constants'; -import { CoreConfigProvider } from '@providers/config'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import * as moment from 'moment'; - -/** - * Service that provides some features regarding lists of courses and categories. - */ -@Injectable() -export class AddonCalendarHelperProvider { - protected logger; - - static EVENTICONS = { - course: 'fa-graduation-cap', - group: 'people', - site: 'globe', - user: 'person', - category: 'fa-cubes' - }; - - constructor(logger: CoreLoggerProvider, - private courseProvider: CoreCourseProvider, - private sitesProvider: CoreSitesProvider, - private calendarProvider: AddonCalendarProvider, - private configProvider: CoreConfigProvider, - private utils: CoreUtilsProvider) { - this.logger = logger.getInstance('AddonCalendarHelperProvider'); - } - - /** - * Calculate some day data based on a list of events for that day. - * - * @param day Day. - * @param events Events. - */ - calculateDayData(day: any, events: any[]): void { - day.hasevents = events.length > 0; - day.haslastdayofevent = false; - - const types = {}; - events.forEach((event) => { - types[event.formattedType || event.eventtype] = true; - - if (event.islastday) { - day.haslastdayofevent = true; - } - }); - - day.calendareventtypes = Object.keys(types); - } - - /** - * Check if current user can create/edit events. - * - * @param courseId Course ID. If not defined, site calendar. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether the user can create events. - */ - canEditEvents(courseId?: number, siteId?: string): Promise { - return this.calendarProvider.canEditEvents(siteId).then((canEdit) => { - if (!canEdit) { - return false; - } - - // Site allows creating events. Check if the user has permissions to do so. - return this.calendarProvider.getAllowedEventTypes(courseId, siteId).then((types) => { - return Object.keys(types).length > 0; - }); - }).catch(() => { - return false; - }); - } - - /** - * Classify events into their respective months and days. If an event duration covers more than one day, - * it will be included in all the days it lasts. - * - * @param events Events to classify. - * @return Object with the classified events. - */ - classifyIntoMonths(events: any[]): {[monthId: string]: {[day: number]: any[]}} { - - const result = {}; - - events.forEach((event) => { - const treatedDay = moment(new Date(event.timestart * 1000)), - endDay = moment(new Date((event.timestart + (event.timeduration || 0)) * 1000)); - - // Add the event to all the days it lasts. - while (!treatedDay.isAfter(endDay, 'day')) { - const monthId = this.getMonthId(treatedDay.year(), treatedDay.month() + 1), - day = treatedDay.date(); - - if (!result[monthId]) { - result[monthId] = {}; - } - if (!result[monthId][day]) { - result[monthId][day] = []; - } - result[monthId][day].push(event); - - treatedDay.add(1, 'day'); // Treat next day. - } - }); - - return result; - } - - /** - * Convenience function to format some event data to be rendered. - * - * @param e Event to format. - */ - formatEventData(e: any): void { - e.eventIcon = AddonCalendarHelperProvider.EVENTICONS[e.eventtype] || ''; - if (!e.eventIcon) { - e.eventIcon = this.courseProvider.getModuleIconSrc(e.modulename); - e.moduleIcon = e.eventIcon; - } - - e.formattedType = this.calendarProvider.getEventType(e); - - // Calculate context. - const categoryId = e.category ? e.category.id : e.categoryid, - courseId = e.course ? e.course.id : e.courseid; - - if (categoryId > 0) { - e.contextLevel = 'coursecat'; - e.contextInstanceId = categoryId; - } else if (courseId > 0) { - e.contextLevel = 'course'; - e.contextInstanceId = courseId; - } else { - e.contextLevel = 'user'; - e.contextInstanceId = e.userid; - } - - if (typeof e.duration != 'undefined') { - // It's an offline event, add some calculated data. - e.format = 1; - e.visible = 1; - - if (e.duration == 1) { - e.timeduration = e.timedurationuntil - e.timestart; - } else if (e.duration == 2) { - e.timeduration = e.timedurationminutes * CoreConstants.SECONDS_MINUTE; - } else { - e.timeduration = 0; - } - } - } - - /** - * Get options (name & value) for each allowed event type. - * - * @param eventTypes Result of getAllowedEventTypes. - * @return Options. - */ - getEventTypeOptions(eventTypes: {[name: string]: boolean}): {name: string, value: string}[] { - const options = []; - - if (eventTypes.user) { - options.push({name: 'core.user', value: AddonCalendarProvider.TYPE_USER}); - } - if (eventTypes.group) { - options.push({name: 'core.group', value: AddonCalendarProvider.TYPE_GROUP}); - } - if (eventTypes.course) { - options.push({name: 'core.course', value: AddonCalendarProvider.TYPE_COURSE}); - } - if (eventTypes.category) { - options.push({name: 'core.category', value: AddonCalendarProvider.TYPE_CATEGORY}); - } - if (eventTypes.site) { - options.push({name: 'core.site', value: AddonCalendarProvider.TYPE_SITE}); - } - - return options; - } - - /** - * Get the month "id" (year + month). - * - * @param year Year. - * @param month Month. - * @return The "id". - */ - getMonthId(year: number, month: number): string { - return year + '#' + month; - } - - /** - * Get weeks of a month in offline (with no events). - * - * The result has the same structure than getMonthlyEvents, but it only contains fields that are actually used by the app. - * - * @param year Year to get. - * @param month Month to get. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the response. - */ - getOfflineMonthWeeks(year: number, month: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - // Get starting week day user preference, fallback to site configuration. - const startWeekDay = site.getStoredConfig('calendar_startwday'); - - return this.configProvider.get(AddonCalendarProvider.STARTING_WEEK_DAY, startWeekDay); - }).then((startWeekDay) => { - const today = moment(); - const isCurrentMonth = today.year() == year && today.month() == month - 1; - const weeks = []; - - let date = moment({year, month: month - 1, date: 1}); - for (let mday = 1; mday <= date.daysInMonth(); mday++) { - date = moment({year, month: month - 1, date: mday}); - - // Add new week and calculate prepadding. - if (!weeks.length || date.day() == startWeekDay) { - const prepaddingLength = (date.day() - startWeekDay + 7) % 7; - const prepadding = []; - for (let i = 0; i < prepaddingLength; i++) { - prepadding.push(i); - } - weeks.push({ prepadding, postpadding: [], days: []}); - } - - // Calculate postpadding of last week. - if (mday == date.daysInMonth()) { - const postpaddingLength = (startWeekDay - date.day() + 6) % 7; - const postpadding = []; - for (let i = 0; i < postpaddingLength; i++) { - postpadding.push(i); - } - weeks[weeks.length - 1].postpadding = postpadding; - } - - // Add day to current week. - weeks[weeks.length - 1].days.push({ - events: [], - hasevents: false, - mday: date.date(), - isweekend: date.day() == 0 || date.day() == 6, - istoday: isCurrentMonth && today.date() == date.date(), - calendareventtypes: [], - }); - } - - return {weeks, daynames: [{dayno: startWeekDay}]}; - }); - } - - /** - * Check if the data of an event has changed. - * - * @param data Current data. - * @param original Original data. - * @return True if data has changed, false otherwise. - */ - hasEventDataChanged(data: any, original?: any): boolean { - if (!original) { - // There is no original data, assume it hasn't changed. - return false; - } - - // Check the fields that don't depend on any other. - if (data.name != original.name || data.timestart != original.timestart || data.eventtype != original.eventtype || - data.description != original.description || data.location != original.location || - data.duration != original.duration || data.repeat != original.repeat) { - return true; - } - - // Check data that depends on eventtype. - if ((data.eventtype == AddonCalendarProvider.TYPE_CATEGORY && data.categoryid != original.categoryid) || - (data.eventtype == AddonCalendarProvider.TYPE_COURSE && data.courseid != original.courseid) || - (data.eventtype == AddonCalendarProvider.TYPE_GROUP && data.groupcourseid != original.groupcourseid && - data.groupid != original.groupid)) { - return true; - } - - // Check data that depends on duration. - if ((data.duration == 1 && data.timedurationuntil != original.timedurationuntil) || - (data.duration == 2 && data.timedurationminutes != original.timedurationminutes)) { - return true; - } - - if (data.repeat && data.repeats != original.repeats) { - return true; - } - - return false; - } - - /** - * Filter events to be shown on the events list. - * - * @param events Events without filtering. - * @param filter Filter from popover. - * @param categories Categories indexed by ID. - * @return Filtered events. - */ - getFilteredEvents(events: any[], filter: AddonCalendarFilter, categories: any): any[] { - // Do not filter. - if (!filter.filtered) { - return events; - } - - const courseId = filter.courseId ? Number(filter.courseId) : undefined; - - if (!courseId || courseId < 0) { - // Filter only by type. - return events.filter((event) => { - return filter[event.formattedType]; - }); - } - - const categoryId = filter.categoryId ? Number(filter.categoryId) : undefined; - - return events.filter((event) => { - return filter[event.formattedType] && - this.shouldDisplayEvent(event, courseId, categoryId, categories); - }); - } - - /** - * Check if an event should be displayed based on the filter. - * - * @param event Event object. - * @param courseId Course ID to filter. - * @param categoryId Category ID the course belongs to. - * @param categories Categories indexed by ID. - * @return Whether it should be displayed. - */ - shouldDisplayEvent(event: any, courseId: number, categoryId: number, categories: any): boolean { - if (event.eventtype == 'user' || event.eventtype == 'site') { - // User or site event, display it. - return true; - } - - if (event.eventtype == 'category') { - if (!event.categoryid || !Object.keys(categories).length) { - // We can't tell if the course belongs to the category, display them all. - return true; - } - - if (event.categoryid == categoryId) { - // The event is in the same category as the course, display it. - return true; - } - - // Check parent categories. - let category = categories[categoryId]; - while (category) { - if (!category.parent) { - // Category doesn't have parent, stop. - break; - } - - if (event.categoryid == category.parent) { - return true; - } - category = categories[category.parent]; - } - - return false; - } - - const eventCourse = (event.course && event.course.id) || event.courseid; - - // Show the event if it is from site home or if it matches the selected course. - return eventCourse && (eventCourse == this.sitesProvider.getCurrentSiteHomeId() || eventCourse == courseId); - } - - /** - * Refresh the month & day for several created/edited/deleted events, and invalidate the months & days - * for their repeated events if needed. - * - * @param events Events that have been touched and number of times each event is repeated. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - refreshAfterChangeEvents(events: {event: any, repeated: number}[], siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const fetchTimestarts = [], - invalidateTimestarts = []; - - // Always fetch upcoming events. - const upcomingPromise = this.calendarProvider.getUpcomingEvents(undefined, undefined, true, site.id).catch(() => { - // Ignore errors. - }); - - // Invalidate the events and get the timestarts so we can invalidate months & days. - return this.utils.allPromises([upcomingPromise].concat(events.map((eventData) => { - - if (eventData.repeated > 1) { - if (eventData.event.repeatid) { - // Being edited or deleted. - // We need to calculate the days to invalidate because the event date could have changed. - // We don't know if the repeated events are before or after this one, invalidate them all. - fetchTimestarts.push(eventData.event.timestart); - - for (let i = 1; i < eventData.repeated; i++) { - invalidateTimestarts.push(eventData.event.timestart + CoreConstants.SECONDS_DAY * 7 * i); - invalidateTimestarts.push(eventData.event.timestart - CoreConstants.SECONDS_DAY * 7 * i); - } - - // Get the repeated events to invalidate them. - return this.calendarProvider.getLocalEventsByRepeatIdFromLocalDb(eventData.event.repeatid, site.id) - .then((events) => { - - return this.utils.allPromises(events.map((event) => { - return this.calendarProvider.invalidateEvent(event.id); - })); - }); - } else { - // Being added. - let time = eventData.event.timestart; - fetchTimestarts.push(time); - - while (eventData.repeated > 1) { - time += CoreConstants.SECONDS_DAY * 7; - eventData.repeated--; - invalidateTimestarts.push(time); - } - - return Promise.resolve(); - } - } else { - // Not repeated. - fetchTimestarts.push(eventData.event.timestart); - - return this.calendarProvider.invalidateEvent(eventData.event.id); - } - - }))).finally(() => { - const treatedMonths = {}, - treatedDays = {}; - - return this.utils.allPromises([ - this.calendarProvider.invalidateAllUpcomingEvents(), - - // Fetch or invalidate months and days. - this.utils.allPromises(fetchTimestarts.concat(invalidateTimestarts).map((time, index) => { - const promises = [], - day = moment(new Date(time * 1000)), - monthId = this.getMonthId(day.year(), day.month() + 1), - dayId = monthId + '#' + day.date(); - - if (!treatedMonths[monthId]) { - // Month not treated already, do it now. - treatedMonths[monthId] = monthId; - - if (index < fetchTimestarts.length) { - promises.push(this.calendarProvider.getMonthlyEvents(day.year(), day.month() + 1, undefined, - undefined, true, site.id).catch(() => { - - // Ignore errors. - })); - } else { - promises.push(this.calendarProvider.invalidateMonthlyEvents(day.year(), day.month() + 1, site.id)); - } - } - - if (!treatedDays[dayId]) { - // Day not invalidated already, do it now. - treatedDays[dayId] = dayId; - - if (index < fetchTimestarts.length) { - promises.push(this.calendarProvider.getDayEvents(day.year(), day.month() + 1, day.date(), - undefined, undefined, true, site.id).catch(() => { - - // Ignore errors. - })); - } else { - promises.push(this.calendarProvider.invalidateDayEvents(day.year(), day.month() + 1, day.date(), - site.id)); - } - } - - return this.utils.allPromises(promises); - })) - ]); - }); - }); - } - - /** - * Refresh the month & day for a created/edited/deleted event, and invalidate the months & days - * for their repeated events if needed. - * - * @param event Event that has been touched. - * @param repeated Number of times the event is repeated. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - refreshAfterChangeEvent(event: any, repeated: number, siteId?: string): Promise { - return this.refreshAfterChangeEvents([{event: event, repeated: repeated}], siteId); - } -} - -/** - * Calculated data for Calendar filtering. - */ -export type AddonCalendarFilter = { - filtered: boolean; // If filter enabled (some filters applied). - courseId: number; // Course Id to filter. - categoryId: string; // Category Id to filter. - course: boolean, // Filter to show course events. - group: boolean, // Filter to show group events. - site: boolean, // Filter to show show site events. - user: boolean, // Filter to show user events. - category: boolean // Filter to show category events. -}; diff --git a/src/addon/calendar/providers/mainmenu-handler.ts b/src/addon/calendar/providers/mainmenu-handler.ts deleted file mode 100644 index 47374f033..000000000 --- a/src/addon/calendar/providers/mainmenu-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonCalendarProvider } from './calendar'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; - -/** - * Handler to inject an option into main menu. - */ -@Injectable() -export class AddonCalendarMainMenuHandler implements CoreMainMenuHandler { - name = 'AddonCalendar'; - priority = 900; - - constructor(private calendarProvider: AddonCalendarProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return !this.calendarProvider.isCalendarDisabledInSite(); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreMainMenuHandlerData { - return { - icon: 'calendar', - title: 'addon.calendar.calendar', - page: this.calendarProvider.canViewMonthInSite() ? 'AddonCalendarIndexPage' : 'AddonCalendarListPage', - class: 'addon-calendar-handler' - }; - } -} diff --git a/src/addon/calendar/providers/sync-cron-handler.ts b/src/addon/calendar/providers/sync-cron-handler.ts deleted file mode 100644 index 90e8b4c16..000000000 --- a/src/addon/calendar/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonCalendarSyncProvider } from './calendar-sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonCalendarSyncCronHandler implements CoreCronHandler { - name = 'AddonCalendarSyncCronHandler'; - - constructor(private calendarSync: AddonCalendarSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.calendarSync.syncAllEvents(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.calendarSync.syncInterval; - } -} diff --git a/src/addon/calendar/providers/view-link-handler.ts b/src/addon/calendar/providers/view-link-handler.ts deleted file mode 100644 index 15b1ce78f..000000000 --- a/src/addon/calendar/providers/view-link-handler.ts +++ /dev/null @@ -1,113 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonCalendarProvider } from './calendar'; - -/** - * Content links handler for calendar view page. - */ -@Injectable() -export class AddonCalendarViewLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonCalendarViewLinkHandler'; - pattern = /\/calendar\/view\.php/; - - protected SUPPORTED_VIEWS = ['month', 'mini', 'minithree', 'day', 'upcoming', 'upcoming_mini']; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private calendarProvider: AddonCalendarProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - if (!params.view || params.view == 'month' || params.view == 'mini' || params.view == 'minithree') { - // Monthly view, open the calendar tab. - const stateParams: any = { - courseId: params.course - }, - timestamp = params.time ? params.time * 1000 : Date.now(); - - const date = new Date(timestamp); - stateParams.year = date.getFullYear(); - stateParams.month = date.getMonth() + 1; - - this.linkHelper.goInSite(navCtrl, 'AddonCalendarIndexPage', stateParams, siteId); // @todo: Add checkMenu param. - - } else if (params.view == 'day') { - // Daily view, open the page. - const stateParams: any = { - courseId: params.course - }, - timestamp = params.time ? params.time * 1000 : Date.now(); - - const date = new Date(timestamp); - stateParams.year = date.getFullYear(); - stateParams.month = date.getMonth() + 1; - stateParams.day = date.getDate(); - - this.linkHelper.goInSite(navCtrl, 'AddonCalendarDayPage', stateParams, siteId); - - } else if (params.view == 'upcoming' || params.view == 'upcoming_mini') { - // Upcoming view, open the calendar tab. - const stateParams: any = { - courseId: params.course, - upcoming: true, - }; - - this.linkHelper.goInSite(navCtrl, 'AddonCalendarIndexPage', stateParams, siteId); // @todo: Add checkMenu param. - - } - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - if (params.view && this.SUPPORTED_VIEWS.indexOf(params.view) == -1) { - // This type of view isn't supported in the app. - return false; - } - - return this.calendarProvider.isDisabled(siteId).then((disabled) => { - if (disabled) { - return false; - } - - return this.calendarProvider.canViewMonth(siteId); - }); - } -} diff --git a/src/addon/competency/competency.module.ts b/src/addon/competency/competency.module.ts deleted file mode 100644 index 1d05db647..000000000 --- a/src/addon/competency/competency.module.ts +++ /dev/null @@ -1,76 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonCompetencyProvider } from './providers/competency'; -import { AddonCompetencyHelperProvider } from './providers/helper'; -import { AddonCompetencyCourseOptionHandler } from './providers/course-option-handler'; -import { AddonCompetencyMainMenuHandler } from './providers/mainmenu-handler'; -import { AddonCompetencyUserHandler } from './providers/user-handler'; -import { AddonCompetencyCompetencyLinkHandler } from './providers/competency-link-handler'; -import { AddonCompetencyPlanLinkHandler } from './providers/plan-link-handler'; -import { AddonCompetencyPlansLinkHandler } from './providers/plans-link-handler'; -import { AddonCompetencyUserCompetencyLinkHandler } from './providers/user-competency-link-handler'; -import { AddonCompetencyPushClickHandler } from './providers/push-click-handler'; -import { AddonCompetencyComponentsModule } from './components/components.module'; -import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; -import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; -import { CoreUserDelegate } from '@core/user/providers/user-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; - -// List of providers (without handlers). -export const ADDON_COMPETENCY_PROVIDERS: any[] = [ - AddonCompetencyProvider, - AddonCompetencyHelperProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonCompetencyComponentsModule - ], - providers: [ - AddonCompetencyProvider, - AddonCompetencyHelperProvider, - AddonCompetencyCourseOptionHandler, - AddonCompetencyMainMenuHandler, - AddonCompetencyUserHandler, - AddonCompetencyCompetencyLinkHandler, - AddonCompetencyPlanLinkHandler, - AddonCompetencyPlansLinkHandler, - AddonCompetencyUserCompetencyLinkHandler, - AddonCompetencyPushClickHandler - ] -}) -export class AddonCompetencyModule { - constructor(mainMenuDelegate: CoreMainMenuDelegate, mainMenuHandler: AddonCompetencyMainMenuHandler, - courseOptionsDelegate: CoreCourseOptionsDelegate, courseOptionHandler: AddonCompetencyCourseOptionHandler, - userDelegate: CoreUserDelegate, userHandler: AddonCompetencyUserHandler, - contentLinksDelegate: CoreContentLinksDelegate, competencyLinkHandler: AddonCompetencyCompetencyLinkHandler, - planLinkHandler: AddonCompetencyPlanLinkHandler, plansLinkHandler: AddonCompetencyPlansLinkHandler, - userComptencyLinkHandler: AddonCompetencyUserCompetencyLinkHandler, - pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonCompetencyPushClickHandler) { - - mainMenuDelegate.registerHandler(mainMenuHandler); - courseOptionsDelegate.registerHandler(courseOptionHandler); - userDelegate.registerHandler(userHandler); - contentLinksDelegate.registerHandler(competencyLinkHandler); - contentLinksDelegate.registerHandler(planLinkHandler); - contentLinksDelegate.registerHandler(plansLinkHandler); - contentLinksDelegate.registerHandler(userComptencyLinkHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - } -} diff --git a/src/addon/competency/components/components.module.ts b/src/addon/competency/components/components.module.ts deleted file mode 100644 index 8a1559965..000000000 --- a/src/addon/competency/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCompetencyCourseComponent } from './course/course'; - -@NgModule({ - declarations: [ - AddonCompetencyCourseComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule - ], - providers: [ - ], - exports: [ - AddonCompetencyCourseComponent - ], - entryComponents: [ - AddonCompetencyCourseComponent - ] -}) -export class AddonCompetencyComponentsModule {} diff --git a/src/addon/competency/components/course/addon-competency-course.html b/src/addon/competency/components/course/addon-competency-course.html deleted file mode 100644 index 74722c707..000000000 --- a/src/addon/competency/components/course/addon-competency-course.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - {{ 'addon.competency.coursecompetencyratingsarepushedtouserplans' | translate }} - - - {{ 'addon.competency.coursecompetencyratingsarenotpushedtouserplans' | translate }} - - - - {{ 'addon.competency.xcompetenciesproficientoutofyincourse' | translate:{$a: {x: competencies.statistics.proficientcompetencycount, y: competencies.statistics.competencycount} } }} - - - - {{ 'addon.competency.competenciesmostoftennotproficientincourse' | translate }}: -

- - {{ comp.shortname }} - {{ comp.idnumber }} - -

-
-
- -

{{ 'addon.competency.coursecompetencies' | translate }}

- - - -

{{ user.fullname }}

-
-
- - -
- - -

{{competency.competency.shortname}} {{competency.competency.idnumber}}

- {{ competency.usercompetencycourse.gradename }} -
- -

- -

-
- {{ 'addon.competency.path' | translate }} - {{ competency.comppath.framework.name }} - {{ competency.comppath.framework.name }} -  /  - - {{ ancestor.name }} - {{ ancestor.name }} -  /  - -
-
- {{ 'addon.competency.uponcoursecompletion' | translate }} - - {{ ruleoutcome.text }} - -
-
- {{ 'addon.competency.activities' | translate }} -

- {{ 'addon.competency.noactivities' | translate }} -

- - - - -
-
- {{ 'addon.competency.userplans' | translate }} -

- {{ 'addon.competency.nouserplanswithcompetency' | translate }} -

- - - -
-
-
-
-
-
diff --git a/src/addon/competency/components/course/course.ts b/src/addon/competency/components/course/course.ts deleted file mode 100644 index ba92de5a5..000000000 --- a/src/addon/competency/components/course/course.ts +++ /dev/null @@ -1,108 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, Input } from '@angular/core'; -import { Content, NavController } from 'ionic-angular'; -import { CoreAppProvider } from '@providers/app'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonCompetencyProvider, AddonCompetencyDataForCourseCompetenciesPageResult } from '../../providers/competency'; -import { AddonCompetencyHelperProvider } from '../../providers/helper'; - -/** - * Component that displays the competencies of a course. - */ -@Component({ - selector: 'addon-competency-course', - templateUrl: 'addon-competency-course.html', -}) -export class AddonCompetencyCourseComponent { - @ViewChild(Content) content: Content; - - @Input() courseId: number; - @Input() userId: number; - - competenciesLoaded = false; - competencies: AddonCompetencyDataForCourseCompetenciesPageResult; - user: any; - - constructor(private navCtrl: NavController, private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider, - private competencyProvider: AddonCompetencyProvider, private helperProvider: AddonCompetencyHelperProvider) { - } - - /** - * View loaded. - */ - ngOnInit(): void { - this.fetchCourseCompetencies().finally(() => { - this.competenciesLoaded = true; - }); - } - - /** - * Fetches the competencies and updates the view. - * - * @return Promise resolved when done. - */ - protected fetchCourseCompetencies(): Promise { - return this.competencyProvider.getCourseCompetencies(this.courseId, this.userId).then((competencies) => { - this.competencies = competencies; - - // Get the user profile image. - this.helperProvider.getProfile(this.userId).then((user) => { - this.user = user; - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error getting course competencies data.'); - }); - } - - /** - * Opens a competency. - * - * @param competencyId - */ - openCompetency(competencyId: number): void { - if (this.appProvider.isWide()) { - this.navCtrl.push('AddonCompetencyCompetenciesPage', {competencyId, courseId: this.courseId, userId: this.userId}); - } else { - this.navCtrl.push('AddonCompetencyCompetencyPage', {competencyId, courseId: this.courseId, userId: this.userId}); - } - } - - /** - * Opens the summary of a competency. - * - * @param competencyId - */ - openCompetencySummary(competencyId: number): void { - this.navCtrl.push('AddonCompetencyCompetencySummaryPage', { - competencyId, - contextLevel: 'course', - contextInstanceId: this.courseId - }); - } - - /** - * Refreshes the competencies. - * - * @param refresher Refresher. - */ - refreshCourseCompetencies(refresher: any): void { - this.competencyProvider.invalidateCourseCompetencies(this.courseId, this.userId).finally(() => { - this.fetchCourseCompetencies().finally(() => { - refresher.complete(); - }); - }); - } -} diff --git a/src/addon/competency/lang/en.json b/src/addon/competency/lang/en.json deleted file mode 100644 index e09256cb0..000000000 --- a/src/addon/competency/lang/en.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "activities": "Activities", - "competencies": "Competencies", - "competenciesmostoftennotproficientincourse": "Competencies most often not proficient in this course", - "coursecompetencies": "Course competencies", - "coursecompetencyratingsarenotpushedtouserplans": "Competency ratings in this course do not affect learning plans.", - "coursecompetencyratingsarepushedtouserplans": "Competency ratings in this course are updated immediately in learning plans.", - "crossreferencedcompetencies": "Cross-referenced competencies", - "duedate": "Due date", - "errornocompetenciesfound": "No competencies found", - "evidence": "Evidence", - "evidence_competencyrule": "The rule of the competency was met.", - "evidence_coursecompleted": "The course '{{$a}}' was completed.", - "evidence_coursemodulecompleted": "The activity '{{$a}}' was completed.", - "evidence_courserestored": "The rating was restored along with the course '{{$a}}'.", - "evidence_evidenceofpriorlearninglinked": "The evidence of prior learning '{{$a}}' was linked.", - "evidence_evidenceofpriorlearningunlinked": "The evidence of prior learning '{{$a}}' was unlinked.", - "evidence_manualoverride": "The competency rating was manually set.", - "evidence_manualoverrideincourse": "The competency rating was manually set in the course '{{$a}}'.", - "evidence_manualoverrideinplan": "The competency rating was manually set in the learning plan '{{$a}}'.", - "learningplancompetencies": "Learning plan competencies", - "learningplans": "Learning plans", - "myplans": "My learning plans", - "noactivities": "No activities", - "nocompetencies": "No competencies", - "nocompetenciesincourse": "No competencies have been linked to this course.", - "nocrossreferencedcompetencies": "No other competencies have been cross-referenced to this competency.", - "noevidence": "No evidence", - "noplanswerecreated": "No learning plans were created.", - "nouserplanswithcompetency": "No learning plans contain this competency.", - "path": "Path:", - "planstatusactive": "Active", - "planstatuscomplete": "Complete", - "planstatusdraft": "Draft", - "planstatusinreview": "In review", - "planstatuswaitingforreview": "Waiting for review", - "proficient": "Proficient", - "progress": "Progress", - "rating": "Rating", - "reviewstatus": "Review status", - "status": "Status", - "template": "Learning plan template", - "uponcoursecompletion": "Upon course completion:", - "usercompetencystatus_idle": "Idle", - "usercompetencystatus_inreview": "In review", - "usercompetencystatus_waitingforreview": "Waiting for review", - "userplans": "Learning plans", - "xcompetenciesproficientoutofy": "{{$a.x}} out of {{$a.y}} competencies are proficient", - "xcompetenciesproficientoutofyincourse": "You are proficient in {{$a.x}} out of {{$a.y}} competencies in this course." -} \ No newline at end of file diff --git a/src/addon/competency/pages/competencies/competencies.html b/src/addon/competency/pages/competencies/competencies.html deleted file mode 100644 index b89d3a0e0..000000000 --- a/src/addon/competency/pages/competencies/competencies.html +++ /dev/null @@ -1,21 +0,0 @@ - - - {{ title }} - - - - - - - - - - -

{{ competency.competency.shortname }} {{competency.competency.idnumber}}

- {{ competency.usercompetency.gradename }} - {{ competency.usercompetencycourse.gradename }} -
-
-
-
-
\ No newline at end of file diff --git a/src/addon/competency/pages/competencies/competencies.module.ts b/src/addon/competency/pages/competencies/competencies.module.ts deleted file mode 100644 index 8e5f1fca5..000000000 --- a/src/addon/competency/pages/competencies/competencies.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonCompetencyCompetenciesPage } from './competencies'; - -@NgModule({ - declarations: [ - AddonCompetencyCompetenciesPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonCompetencyCompetenciesPage), - TranslateModule.forChild() - ], -}) -export class AddonCompetencyCompetenciesPageModule {} diff --git a/src/addon/competency/pages/competencies/competencies.ts b/src/addon/competency/pages/competencies/competencies.ts deleted file mode 100644 index 0a103f867..000000000 --- a/src/addon/competency/pages/competencies/competencies.ts +++ /dev/null @@ -1,143 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { - AddonCompetencyProvider, AddonCompetencyDataForCourseCompetenciesPageResult, AddonCompetencyDataForPlanPageResult, - AddonCompetencyDataForPlanPageCompetency, AddonCompetencyDataForCourseCompetenciesPageCompetency -} from '../../providers/competency'; - -/** - * Page that displays the list of competencies of a learning plan. - */ -@IonicPage({ segment: 'addon-competency-competencies' }) -@Component({ - selector: 'page-addon-competency-competencies', - templateUrl: 'competencies.html', -}) -export class AddonCompetencyCompetenciesPage { - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - protected planId: number; - protected courseId: number; - protected competencyId: number; - protected userId: number; - - competenciesLoaded = false; - competencies: AddonCompetencyDataForPlanPageCompetency[] | AddonCompetencyDataForCourseCompetenciesPageCompetency[] = []; - title: string; - - constructor(navParams: NavParams, private translate: TranslateService, private domUtils: CoreDomUtilsProvider, - private competencyProvider: AddonCompetencyProvider) { - this.planId = navParams.get('planId'); - this.courseId = navParams.get('courseId'); - this.competencyId = navParams.get('competencyId'); - this.userId = navParams.get('userId'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - if (this.competencyId) { - // There is a competency to load. - this.openCompetency(this.competencyId); - } - - this.fetchCompetencies().then(() => { - if (!this.competencyId && this.splitviewCtrl.isOn() && this.competencies.length > 0) { - // Take first and load it. - this.openCompetency(this.competencies[0].competency.id); - } - }).finally(() => { - this.competenciesLoaded = true; - }); - } - - /** - * Fetches the competencies and updates the view. - * - * @return Promise resolved when done. - */ - protected fetchCompetencies(): Promise { - let promise: Promise; - - if (this.planId) { - promise = this.competencyProvider.getLearningPlan(this.planId); - } else if (this.courseId) { - promise = this.competencyProvider.getCourseCompetencies(this.courseId, this.userId); - } else { - promise = Promise.reject(null); - } - - return promise.then((response) => { - - if (this.planId) { - const resp = response; - - if (resp.competencycount <= 0) { - return Promise.reject(this.translate.instant('addon.competency.errornocompetenciesfound')); - } - - this.title = resp.plan.name; - this.userId = resp.plan.userid; - } else { - this.title = this.translate.instant('addon.competency.coursecompetencies'); - } - this.competencies = response.competencies; - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error getting competencies data.'); - }); - } - - /** - * Opens a competency. - * - * @param competencyId - */ - openCompetency(competencyId: number): void { - this.competencyId = competencyId; - let params; - if (this.planId) { - params = {competencyId, planId: this.planId}; - } else { - params = {competencyId, courseId: this.courseId, userId: this.userId}; - } - this.splitviewCtrl.push('AddonCompetencyCompetencyPage', params); - } - - /** - * Refreshes the competencies. - * - * @param refresher Refresher. - */ - refreshCompetencies(refresher: any): void { - let promise; - if (this.planId) { - promise = this.competencyProvider.invalidateLearningPlan(this.planId); - } else { - promise = this.competencyProvider.invalidateCourseCompetencies(this.courseId, this.userId); - } - - return promise.finally(() => { - this.fetchCompetencies().finally(() => { - refresher.complete(); - }); - }); - } -} diff --git a/src/addon/competency/pages/competency/competency.html b/src/addon/competency/pages/competency/competency.html deleted file mode 100644 index e3cd3ef36..000000000 --- a/src/addon/competency/pages/competency/competency.html +++ /dev/null @@ -1,92 +0,0 @@ - - - {{ competency.competency.competency.shortname }} {{ competency.competency.competency.idnumber }} - - - - - - - - - - -

{{ user.fullname }}

-
-
- - - - - - - {{ 'addon.competency.path' | translate }} - {{ competency.competency.comppath.framework.name }} - {{ competency.competency.comppath.framework.name }} -  /  - - {{ ancestor.name }} - {{ ancestor.name }} -  /  - - - - {{ 'addon.competency.crossreferencedcompetencies' | translate }}: -
{{ 'addon.competency.nocrossreferencedcompetencies' | translate }}
- -
- - {{ 'addon.competency.activities' | translate }} -

- {{ 'addon.competency.noactivities' | translate }} -

- - - - -
- - {{ 'addon.competency.reviewstatus' | translate }} - {{ userCompetency.statusname }} - - - {{ 'addon.competency.proficient' | translate }} - - {{ 'core.yes' | translate }} - - - {{ 'core.no' | translate }} - - - - {{ 'addon.competency.rating' | translate }} - {{ userCompetency.gradename }} - -
- -
-

{{ 'addon.competency.evidence' | translate }}

-

- {{ 'addon.competency.noevidence' | translate }} -

- - - -

{{ evidence.actionuser.fullname }}

-

{{ evidence.timemodified * 1000 | coreFormatDate }}

-
- -

{{ evidence.gradename }}

-

{{ evidence.description }}

-
{{ evidence.note }}
-
-
-
-
-
diff --git a/src/addon/competency/pages/competency/competency.module.ts b/src/addon/competency/pages/competency/competency.module.ts deleted file mode 100644 index ebc8079cd..000000000 --- a/src/addon/competency/pages/competency/competency.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCompetencyCompetencyPage } from './competency'; - -@NgModule({ - declarations: [ - AddonCompetencyCompetencyPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonCompetencyCompetencyPage), - TranslateModule.forChild() - ], -}) -export class AddonCompetencyCompetencyPageModule {} diff --git a/src/addon/competency/pages/competency/competency.ts b/src/addon/competency/pages/competency/competency.ts deleted file mode 100644 index e125c51bc..000000000 --- a/src/addon/competency/pages/competency/competency.ts +++ /dev/null @@ -1,175 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { - AddonCompetencyProvider, AddonCompetencyUserCompetencySummary, AddonCompetencyUserCompetencySummaryInPlan, - AddonCompetencyUserCompetencySummaryInCourse, AddonCompetencyUserCompetencyPlan, - AddonCompetencyUserCompetency, AddonCompetencyUserCompetencyCourse -} from '../../providers/competency'; -import { AddonCompetencyHelperProvider } from '../../providers/helper'; -import { CoreUserSummary } from '@core/user/providers/user'; -import { CoreCourseModuleSummary } from '@core/course/providers/course'; - -/** - * Page that displays a learning plan. - */ -@IonicPage({ segment: 'addon-competency-competency' }) -@Component({ - selector: 'page-addon-competency-competency', - templateUrl: 'competency.html', -}) -export class AddonCompetencyCompetencyPage { - competencyLoaded = false; - competencyId: number; - planId: number; - courseId: number; - userId: number; - planStatus: number; - coursemodules: CoreCourseModuleSummary[]; - user: CoreUserSummary; - competency: AddonCompetencyUserCompetencySummary; - userCompetency: AddonCompetencyUserCompetencyPlan | AddonCompetencyUserCompetency | AddonCompetencyUserCompetencyCourse; - contextLevel: string; - contextInstanceId: number; - - constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService, - private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider, - @Optional() private svComponent: CoreSplitViewComponent, private competencyProvider: AddonCompetencyProvider, - private competencyHelperProvider: AddonCompetencyHelperProvider) { - this.competencyId = navParams.get('competencyId'); - this.planId = navParams.get('planId'); - this.courseId = navParams.get('courseId'); - this.userId = navParams.get('userId'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchCompetency().then(() => { - const name = this.competency && this.competency.competency && this.competency.competency.competency && - this.competency.competency.competency.shortname; - - if (this.planId) { - this.competencyProvider.logCompetencyInPlanView(this.planId, this.competencyId, this.planStatus, name, - this.userId).catch(() => { - // Ignore errors. - }); - } else { - this.competencyProvider.logCompetencyInCourseView(this.courseId, this.competencyId, name, this.userId).catch(() => { - // Ignore errors. - }); - } - }).finally(() => { - this.competencyLoaded = true; - }); - } - - /** - * Fetches the competency and updates the view. - * - * @return Promise resolved when done. - */ - protected fetchCompetency(): Promise { - let promise: Promise; - - if (this.planId) { - this.planStatus = null; - promise = this.competencyProvider.getCompetencyInPlan(this.planId, this.competencyId); - } else if (this.courseId) { - promise = this.competencyProvider.getCompetencyInCourse(this.courseId, this.competencyId, this.userId); - } else { - promise = Promise.reject(null); - } - - return promise.then((competency) => { - - // Calculate the context. - if (this.courseId) { - this.contextLevel = 'course'; - this.contextInstanceId = this.courseId; - } else { - this.contextLevel = 'user'; - this.contextInstanceId = this.userId || competency.usercompetencysummary.user.id; - } - - this.competency = competency.usercompetencysummary; - this.userCompetency = this.competency.usercompetencyplan || this.competency.usercompetency; - - if (this.planId) { - this.planStatus = ( competency).plan.status; - this.competency.usercompetency.statusname = - this.competencyHelperProvider.getCompetencyStatusName(this.competency.usercompetency.status); - } else { - this.userCompetency = this.competency.usercompetencycourse; - this.coursemodules = ( competency).coursemodules; - } - - if (this.competency.user.id != this.sitesProvider.getCurrentSiteUserId()) { - // Get the user profile from the returned object. - this.user = this.competency.user; - } - - this.competency.evidence.forEach((evidence) => { - if (evidence.descidentifier) { - const key = 'addon.competency.' + evidence.descidentifier; - evidence.description = this.translate.instant(key, {$a: evidence.desca}); - } - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error getting competency data.'); - }); - } - - /** - * Refreshes the competency. - * - * @param refresher Refresher. - */ - refreshCompetency(refresher: any): void { - let promise; - if (this.planId) { - promise = this.competencyProvider.invalidateCompetencyInPlan(this.planId, this.competencyId); - } else { - promise = this.competencyProvider.invalidateCompetencyInCourse(this.courseId, this.competencyId); - } - - return promise.finally(() => { - this.fetchCompetency().finally(() => { - refresher.complete(); - }); - }); - } - - /** - * Opens the summary of a competency. - * - * @param competencyId - */ - openCompetencySummary(competencyId: number): void { - // Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav. - const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - navCtrl.push('AddonCompetencyCompetencySummaryPage', { - competencyId, - contextLevel: this.contextLevel, - contextInstanceId: this.contextInstanceId - }); - } -} diff --git a/src/addon/competency/pages/competencysummary/competencysummary.html b/src/addon/competency/pages/competencysummary/competencysummary.html deleted file mode 100644 index d00b4ba50..000000000 --- a/src/addon/competency/pages/competencysummary/competencysummary.html +++ /dev/null @@ -1,24 +0,0 @@ - - - {{ competency.competency.shortname }} {{ competency.competency.idnumber }} - - - - - - - - - - - - - {{ 'addon.competency.path' | translate }} - {{ competency.comppath.framework.name }} - -  / {{ ancestor.name }} - - - - - diff --git a/src/addon/competency/pages/competencysummary/competencysummary.module.ts b/src/addon/competency/pages/competencysummary/competencysummary.module.ts deleted file mode 100644 index 8be154249..000000000 --- a/src/addon/competency/pages/competencysummary/competencysummary.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonCompetencyCompetencySummaryPage } from './competencysummary'; - -@NgModule({ - declarations: [ - AddonCompetencyCompetencySummaryPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonCompetencyCompetencySummaryPage), - TranslateModule.forChild() - ], -}) -export class AddonCompetencyCompetencySummaryPageModule {} diff --git a/src/addon/competency/pages/competencysummary/competencysummary.ts b/src/addon/competency/pages/competencysummary/competencysummary.ts deleted file mode 100644 index 085854ce5..000000000 --- a/src/addon/competency/pages/competencysummary/competencysummary.ts +++ /dev/null @@ -1,104 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { AddonCompetencyProvider, AddonCompetencySummary } from '../../providers/competency'; - -/** - * Page that displays a learning plan. - */ -@IonicPage({ segment: 'addon-competency-competency-summary' }) -@Component({ - selector: 'page-addon-competency-competency-summary', - templateUrl: 'competencysummary.html', -}) -export class AddonCompetencyCompetencySummaryPage { - competencyLoaded = false; - competencyId: number; - competency: AddonCompetencySummary; - contextLevel: string; - contextInstanceId: number; - - constructor(private navCtrl: NavController, navParams: NavParams, private domUtils: CoreDomUtilsProvider, - @Optional() private svComponent: CoreSplitViewComponent, private competencyProvider: AddonCompetencyProvider) { - this.competencyId = navParams.get('competencyId'); - this.contextLevel = navParams.get('contextLevel'); - this.contextInstanceId = navParams.get('contextInstanceId'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchCompetency().then(() => { - const name = this.competency.competency && this.competency.competency.shortname; - - this.competencyProvider.logCompetencyView(this.competencyId, name).catch(() => { - // Ignore errors. - }); - }).finally(() => { - this.competencyLoaded = true; - }); - } - - /** - * Fetches the competency summary and updates the view. - * - * @return Promise resolved when done. - */ - protected fetchCompetency(): Promise { - return this.competencyProvider.getCompetencySummary(this.competencyId).then((result) => { - if (!this.contextLevel || typeof this.contextInstanceId == 'undefined') { - // Context not specified, use user context. - this.contextLevel = 'user'; - this.contextInstanceId = result.usercompetency.userid; - } - - this.competency = result.competency; - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error getting competency summary data.'); - }); - } - - /** - * Refreshes the competency summary. - * - * @param refresher Refresher. - */ - refreshCompetency(refresher: any): void { - this.competencyProvider.invalidateCompetencySummary(this.competencyId).finally(() => { - this.fetchCompetency().finally(() => { - refresher.complete(); - }); - }); - } - - /** - * Opens the summary of a competency. - * - * @param competencyId - */ - openCompetencySummary(competencyId: number): void { - // Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav. - const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - navCtrl.push('AddonCompetencyCompetencySummaryPage', { - competencyId, - contextLevel: this.contextLevel, - contextInstanceId: this.contextInstanceId - }); - } -} diff --git a/src/addon/competency/pages/coursecompetencies/coursecompetencies.html b/src/addon/competency/pages/coursecompetencies/coursecompetencies.html deleted file mode 100644 index ed3a00ea6..000000000 --- a/src/addon/competency/pages/coursecompetencies/coursecompetencies.html +++ /dev/null @@ -1,6 +0,0 @@ - - - {{ 'addon.competency.coursecompetencies' | translate }} - - - \ No newline at end of file diff --git a/src/addon/competency/pages/coursecompetencies/coursecompetencies.module.ts b/src/addon/competency/pages/coursecompetencies/coursecompetencies.module.ts deleted file mode 100644 index 0dd7b7598..000000000 --- a/src/addon/competency/pages/coursecompetencies/coursecompetencies.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonCompetencyComponentsModule } from '../../components/components.module'; -import { AddonCompetencyCourseCompetenciesPage } from './coursecompetencies'; - -@NgModule({ - declarations: [ - AddonCompetencyCourseCompetenciesPage, - ], - imports: [ - CoreDirectivesModule, - IonicPageModule.forChild(AddonCompetencyCourseCompetenciesPage), - TranslateModule.forChild(), - AddonCompetencyComponentsModule - ], -}) -export class AddonCompetencyCourseCompetenciesPageModule {} diff --git a/src/addon/competency/pages/coursecompetencies/coursecompetencies.ts b/src/addon/competency/pages/coursecompetencies/coursecompetencies.ts deleted file mode 100644 index 836d01a45..000000000 --- a/src/addon/competency/pages/coursecompetencies/coursecompetencies.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; - -/** - * Page that displays the list of competencies of a course. - */ -@IonicPage({ segment: 'addon-competency-coursecompetencies' }) -@Component({ - selector: 'page-addon-competency-coursecompetencies', - templateUrl: 'coursecompetencies.html', -}) -export class AddonCompetencyCourseCompetenciesPage { - - protected courseId: number; - protected userId: number; - - constructor(navParams: NavParams) { - this.courseId = navParams.get('courseId'); - this.userId = navParams.get('userId'); - } -} diff --git a/src/addon/competency/pages/plan/plan.html b/src/addon/competency/pages/plan/plan.html deleted file mode 100644 index f45818688..000000000 --- a/src/addon/competency/pages/plan/plan.html +++ /dev/null @@ -1,55 +0,0 @@ - - - {{plan.plan.name}} - - - - - - - - - - -

{{ user.fullname }}

-
-
- - - - - - - {{ 'addon.competency.status' | translate }}: - {{ plan.plan.statusname }} - - - {{ 'addon.competency.duedate' | translate }}: - {{ plan.plan.duedate * 1000 | coreFormatDate }} - - - {{ 'addon.competency.template' | translate }}: - {{ plan.plan.template.shortname }} - - - {{ 'addon.competency.progress' | translate }}: - {{ 'addon.competency.xcompetenciesproficientoutofy' | translate: {$a: {x: plan.proficientcompetencycount, y: plan.competencycount} } }} - - - - - -

{{ 'addon.competency.learningplancompetencies' | translate }}

- - - {{ 'addon.competency.nocompetencies' | translate }} - - -

{{competency.competency.shortname}} {{competency.competency.idnumber}}

- {{ competency.usercompetencyplan.gradename }} - {{ competency.usercompetency.gradename }} -
-
-
-
-
diff --git a/src/addon/competency/pages/plan/plan.module.ts b/src/addon/competency/pages/plan/plan.module.ts deleted file mode 100644 index 92b4a0cae..000000000 --- a/src/addon/competency/pages/plan/plan.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCompetencyPlanPage } from './plan'; - -@NgModule({ - declarations: [ - AddonCompetencyPlanPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonCompetencyPlanPage), - TranslateModule.forChild() - ], -}) -export class AddonCompetencyPlanPageModule {} diff --git a/src/addon/competency/pages/plan/plan.ts b/src/addon/competency/pages/plan/plan.ts deleted file mode 100644 index ba4e53f20..000000000 --- a/src/addon/competency/pages/plan/plan.ts +++ /dev/null @@ -1,97 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { CoreAppProvider } from '@providers/app'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { AddonCompetencyProvider, AddonCompetencyDataForPlanPageResult } from '../../providers/competency'; -import { AddonCompetencyHelperProvider } from '../../providers/helper'; - -/** - * Page that displays a learning plan. - */ -@IonicPage({ segment: 'addon-competency-plan' }) -@Component({ - selector: 'page-addon-competency-plan', - templateUrl: 'plan.html', -}) -export class AddonCompetencyPlanPage { - protected planId: number; - planLoaded = false; - plan: AddonCompetencyDataForPlanPageResult; - user: any; - - constructor(private navCtrl: NavController, navParams: NavParams, private appProvider: CoreAppProvider, - private domUtils: CoreDomUtilsProvider, @Optional() private svComponent: CoreSplitViewComponent, - private competencyProvider: AddonCompetencyProvider, private competencyHelperProvider: AddonCompetencyHelperProvider) { - this.planId = navParams.get('planId'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchLearningPlan().finally(() => { - this.planLoaded = true; - }); - } - - /** - * Fetches the learning plan and updates the view. - * - * @return Promise resolved when done. - */ - protected fetchLearningPlan(): Promise { - return this.competencyProvider.getLearningPlan(this.planId).then((plan) => { - plan.plan.statusname = this.competencyHelperProvider.getPlanStatusName(plan.plan.status); - // Get the user profile image. - this.competencyHelperProvider.getProfile(plan.plan.userid).then((user) => { - this.user = user; - }); - - this.plan = plan; - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error getting learning plan data.'); - }); - } - - /** - * Navigates to a particular competency. - * - * @param competencyId - */ - openCompetency(competencyId: number): void { - const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - if (this.appProvider.isWide()) { - navCtrl.push('AddonCompetencyCompetenciesPage', {competencyId, planId: this.planId}); - } else { - navCtrl.push('AddonCompetencyCompetencyPage', {competencyId, planId: this.planId}); - } - } - - /** - * Refreshes the learning plan. - * - * @param refresher Refresher. - */ - refreshLearningPlan(refresher: any): void { - this.competencyProvider.invalidateLearningPlan(this.planId).finally(() => { - this.fetchLearningPlan().finally(() => { - refresher.complete(); - }); - }); - } -} diff --git a/src/addon/competency/pages/planlist/planlist.html b/src/addon/competency/pages/planlist/planlist.html deleted file mode 100644 index 485ce2a45..000000000 --- a/src/addon/competency/pages/planlist/planlist.html +++ /dev/null @@ -1,23 +0,0 @@ - - - {{ 'addon.competency.userplans' | translate }} - - - - - - - - - - - - -

{{ plan.name }}

-

{{ 'addon.competency.duedate' | translate }}: {{ plan.duedate * 1000 | coreFormatDate :'strftimedatetimeshort' }}

- {{ plan.statusname }} -
-
-
-
-
diff --git a/src/addon/competency/pages/planlist/planlist.module.ts b/src/addon/competency/pages/planlist/planlist.module.ts deleted file mode 100644 index 81c77d96c..000000000 --- a/src/addon/competency/pages/planlist/planlist.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCompetencyPlanListPage } from './planlist'; - -@NgModule({ - declarations: [ - AddonCompetencyPlanListPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonCompetencyPlanListPage), - TranslateModule.forChild() - ], -}) -export class AddonCompetencyPlanListPageModule {} diff --git a/src/addon/competency/pages/planlist/planlist.ts b/src/addon/competency/pages/planlist/planlist.ts deleted file mode 100644 index 3802a02ec..000000000 --- a/src/addon/competency/pages/planlist/planlist.ts +++ /dev/null @@ -1,118 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { AddonCompetencyProvider, AddonCompetencyPlan } from '../../providers/competency'; -import { AddonCompetencyHelperProvider } from '../../providers/helper'; - -/** - * Page that displays the list of learning plans. - */ -@IonicPage({ segment: 'addon-competency-planlist' }) -@Component({ - selector: 'page-addon-competency-planlist', - templateUrl: 'planlist.html', -}) -export class AddonCompetencyPlanListPage { - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - protected userId: number; - protected planId: number; - plansLoaded = false; - plans: AddonCompetencyPlan[] = []; - - constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private competencyProvider: AddonCompetencyProvider, - private competencyHelperProvider: AddonCompetencyHelperProvider) { - this.userId = navParams.get('userId'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - if (this.planId) { - // There is a learning plan to load. - this.openPlan(this.planId); - } - - this.fetchLearningPlans().then(() => { - if (!this.planId && this.splitviewCtrl.isOn() && this.plans.length > 0) { - // Take first and load it. - this.openPlan(this.plans[0].id); - } - }).finally(() => { - this.plansLoaded = true; - }); - } - - /** - * Fetches the learning plans and updates the view. - * - * @return Promise resolved when done. - */ - protected fetchLearningPlans(): Promise { - return this.competencyProvider.getLearningPlans(this.userId).then((plans) => { - plans.forEach((plan: AddonCompetencyPlanFormatted) => { - plan.statusname = this.competencyHelperProvider.getPlanStatusName(plan.status); - switch (plan.status) { - case AddonCompetencyProvider.STATUS_ACTIVE: - plan.statuscolor = 'success'; - break; - case AddonCompetencyProvider.STATUS_COMPLETE: - plan.statuscolor = 'danger'; - break; - default: - plan.statuscolor = 'warning'; - break; - } - }); - this.plans = plans; - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error getting learning plans data.'); - }); - } - - /** - * Refreshes the learning plans. - * - * @param refresher Refresher. - */ - refreshLearningPlans(refresher: any): void { - this.competencyProvider.invalidateLearningPlans(this.userId).finally(() => { - this.fetchLearningPlans().finally(() => { - refresher.complete(); - }); - }); - } - - /** - * Opens a learning plan. - * - * @param planId Learning plan to load. - */ - openPlan(planId: number): void { - this.planId = planId; - this.splitviewCtrl.push('AddonCompetencyPlanPage', { planId }); - } -} - -/** - * Competency plan with some calculated data. - */ -type AddonCompetencyPlanFormatted = AddonCompetencyPlan & { - statuscolor?: string; // Calculated in the app. Color of the plan's status. -}; diff --git a/src/addon/competency/providers/competency-link-handler.ts b/src/addon/competency/providers/competency-link-handler.ts deleted file mode 100644 index 95c8362a6..000000000 --- a/src/addon/competency/providers/competency-link-handler.ts +++ /dev/null @@ -1,74 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonCompetencyProvider } from './competency'; - -/** - * Handler to treat links to a competency in a plan or in a course. - */ -@Injectable() -export class AddonCompetencyCompetencyLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonCompetencyCompetencyLinkHandler'; - pattern = /\/admin\/tool\/lp\/(user_competency_in_course|user_competency_in_plan)\.php/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private competencyProvider: AddonCompetencyProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - courseId = courseId || params.courseid || params.cid; - - return [{ - action: (siteId, navCtrl?): void => { - this.linkHelper.goInSite(navCtrl, 'AddonCompetencyCompetencyPage', { - planId: params.planid, - competencyId: params.competencyid, - courseId: courseId, - userId: params.userid - }, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - // Handler is disabled if all competency features are disabled. - return this.competencyProvider.allCompetenciesDisabled(siteId).then((disabled) => { - return !disabled; - }); - } -} diff --git a/src/addon/competency/providers/competency.ts b/src/addon/competency/providers/competency.ts deleted file mode 100644 index 7d2704da9..000000000 --- a/src/addon/competency/providers/competency.ts +++ /dev/null @@ -1,1007 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; -import { CoreSite } from '@classes/site'; -import { CoreCommentsArea } from '@core/comments/providers/comments'; -import { CoreUserSummary } from '@core/user/providers/user'; -import { CoreCourseSummary, CoreCourseModuleSummary } from '@core/course/providers/course'; - -/** - * Service to handle caompetency learning plans. - */ -@Injectable() -export class AddonCompetencyProvider { - - // Learning plan status. - static STATUS_DRAFT = 0; - static STATUS_ACTIVE = 1; - static STATUS_COMPLETE = 2; - static STATUS_WAITING_FOR_REVIEW = 3; - static STATUS_IN_REVIEW = 4; - - // Competency status. - static REVIEW_STATUS_IDLE = 0; - static REVIEW_STATUS_WAITING_FOR_REVIEW = 1; - static REVIEW_STATUS_IN_REVIEW = 2; - - protected ROOT_CACHE_KEY = 'mmaCompetency:'; - - protected logger; - - constructor(loggerProvider: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, - protected pushNotificationsProvider: CorePushNotificationsProvider) { - this.logger = loggerProvider.getInstance('AddonCompetencyProvider'); - } - - /** - * Check if all competencies features are disabled. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether all competency features are disabled. - */ - allCompetenciesDisabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.isFeatureDisabled('CoreMainMenuDelegate_AddonCompetency') && - site.isFeatureDisabled('CoreCourseOptionsDelegate_AddonCompetency') && - site.isFeatureDisabled('CoreUserDelegate_AddonCompetency'); - }); - } - - /** - * Get cache key for user learning plans data WS calls. - * - * @param userId User ID. - * @return Cache key. - */ - protected getLearningPlansCacheKey(userId: number): string { - return this.ROOT_CACHE_KEY + 'userplans:' + userId; - } - - /** - * Get cache key for learning plan data WS calls. - * - * @param planId Plan ID. - * @return Cache key. - */ - protected getLearningPlanCacheKey(planId: number): string { - return this.ROOT_CACHE_KEY + 'learningplan:' + planId; - } - - /** - * Get cache key for competency in plan data WS calls. - * - * @param planId Plan ID. - * @param competencyId Competency ID. - * @return Cache key. - */ - protected getCompetencyInPlanCacheKey(planId: number, competencyId: number): string { - return this.ROOT_CACHE_KEY + 'plancompetency:' + planId + ':' + competencyId; - } - - /** - * Get cache key for competency in course data WS calls. - * - * @param courseId Course ID. - * @param competencyId Competency ID. - * @param userId User ID. - * @return Cache key. - */ - protected getCompetencyInCourseCacheKey(courseId: number, competencyId: number, userId: number): string { - return this.ROOT_CACHE_KEY + 'coursecompetency:' + userId + ':' + courseId + ':' + competencyId; - } - - /** - * Get cache key for competency summary data WS calls. - * - * @param competencyId Competency ID. - * @param userId User ID. - * @return Cache key. - */ - protected getCompetencySummaryCacheKey(competencyId: number, userId: number): string { - return this.ROOT_CACHE_KEY + 'competencysummary:' + userId + ':' + competencyId; - } - - /** - * Get cache key for course competencies data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getCourseCompetenciesCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'coursecompetencies:' + courseId; - } - - /** - * Returns whether competencies are enabled. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return competencies if enabled for the given course, false otherwise. - */ - isPluginForCourseEnabled(courseId: number, siteId?: string): Promise { - if (!this.sitesProvider.isLoggedIn()) { - return Promise.resolve(false); - } - - return this.getCourseCompetencies(courseId, 0, siteId).catch(() => { - return false; - }); - } - - /** - * Get plans for a certain user. - * - * @param userId ID of the user. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise to be resolved when the plans are retrieved. - */ - getLearningPlans(userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - this.logger.debug('Get plans for user ' + userId); - - const params = { - userid: userId - }, - preSets = { - cacheKey: this.getLearningPlansCacheKey(userId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - return site.read('tool_lp_data_for_plans_page', params, preSets) - .then((response: AddonCompetencyDataForPlansPageResult): any => { - - if (response.plans) { - return response.plans; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a certain plan. - * - * @param planId ID of the plan. - * @param siteId Site ID. If not defined, current site. - * @return Promise to be resolved when the plan is retrieved. - */ - getLearningPlan(planId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - - this.logger.debug('Get plan ' + planId); - - const params = { - planid: planId - }, - preSets = { - cacheKey: this.getLearningPlanCacheKey(planId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - return site.read('tool_lp_data_for_plan_page', params, preSets) - .then((response: AddonCompetencyDataForPlanPageResult): any => { - - if (response.plan) { - return response; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a certain competency in a plan. - * - * @param planId ID of the plan. - * @param competencyId ID of the competency. - * @param siteId Site ID. If not defined, current site. - * @return Promise to be resolved when the competency is retrieved. - */ - getCompetencyInPlan(planId: number, competencyId: number, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - this.logger.debug('Get competency ' + competencyId + ' in plan ' + planId); - - const params = { - planid: planId, - competencyid: competencyId - }, - preSets = { - cacheKey: this.getCompetencyInPlanCacheKey(planId, competencyId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - return site.read('tool_lp_data_for_user_competency_summary_in_plan', params, preSets) - .then((response: AddonCompetencyUserCompetencySummaryInPlan): any => { - - if (response.usercompetencysummary) { - return response; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a certain competency in a course. - * - * @param courseId ID of the course. - * @param competencyId ID of the competency. - * @param userId ID of the user. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @return Promise to be resolved when the competency is retrieved. - */ - getCompetencyInCourse(courseId: number, competencyId: number, userId?: number, siteId?: string, ignoreCache?: boolean) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - this.logger.debug('Get competency ' + competencyId + ' in course ' + courseId); - - const params = { - courseid: courseId, - competencyid: competencyId, - userid: userId - }, - preSets: any = { - cacheKey: this.getCompetencyInCourseCacheKey(courseId, competencyId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - return site.read('tool_lp_data_for_user_competency_summary_in_course', params, preSets) - .then((response: AddonCompetencyUserCompetencySummaryInCourse): any => { - - if (response.usercompetencysummary) { - return response; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a certain competency summary. - * - * @param competencyId ID of the competency. - * @param userId ID of the user. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @return Promise to be resolved when the competency summary is retrieved. - */ - getCompetencySummary(competencyId: number, userId?: number, siteId?: string, ignoreCache?: boolean) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - this.logger.debug('Get competency ' + competencyId + ' summary for user' + userId); - - const params = { - competencyid: competencyId, - userid: userId - }, - preSets: any = { - cacheKey: this.getCompetencySummaryCacheKey(competencyId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - return site.read('tool_lp_data_for_user_competency_summary', params, preSets) - .then((response: AddonCompetencyUserCompetencySummary): any => { - - if (response.competency) { - return response; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get all competencies in a course. - * - * @param courseId ID of the course. - * @param userId ID of the user. - * @param siteId Site ID. If not defined, current site. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @return Promise to be resolved when the course competencies are retrieved. - */ - getCourseCompetencies(courseId: number, userId?: number, siteId?: string, ignoreCache?: boolean) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - this.logger.debug('Get course competencies for course ' + courseId); - - const params = { - courseid: courseId - }, - preSets: any = { - cacheKey: this.getCourseCompetenciesCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - return site.read('tool_lp_data_for_course_competencies_page', params, preSets) - .then((response: AddonCompetencyDataForCourseCompetenciesPageResult): any => { - - if (response.competencies) { - return response; - } - - return Promise.reject(null); - }); - - }).then((response) => { - - if (!userId || userId == this.sitesProvider.getCurrentSiteUserId()) { - return response; - } - - let promises: Promise[]; - - promises = response.competencies.map((competency) => - this.getCompetencyInCourse(courseId, competency.competency.id, userId, siteId) - ); - - return Promise.all(promises).then((responses: AddonCompetencyUserCompetencySummaryInCourse[]) => { - responses.forEach((resp, index) => { - response.competencies[index].usercompetencycourse = resp.usercompetencysummary.usercompetencycourse; - }); - - return response; - }); - }); - } - - /** - * Invalidates User Learning Plans data. - * - * @param userId ID of the user. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateLearningPlans(userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getLearningPlansCacheKey(userId)); - }); - } - - /** - * Invalidates Learning Plan data. - * - * @param planId ID of the plan. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateLearningPlan(planId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getLearningPlanCacheKey(planId)); - }); - } - - /** - * Invalidates Competency in Plan data. - * - * @param planId ID of the plan. - * @param competencyId ID of the competency. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateCompetencyInPlan(planId: number, competencyId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCompetencyInPlanCacheKey(planId, competencyId)); - }); - } - - /** - * Invalidates Competency in Course data. - * - * @param courseId ID of the course. - * @param competencyId ID of the competency. - * @param userId ID of the user. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateCompetencyInCourse(courseId: number, competencyId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCompetencyInCourseCacheKey(courseId, competencyId, userId)); - }); - } - - /** - * Invalidates Competency Summary data. - * - * @param competencyId ID of the competency. - * @param userId ID of the user. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateCompetencySummary(competencyId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCompetencySummaryCacheKey(competencyId, userId)); - }); - } - - /** - * Invalidates Course Competencies data. - * - * @param courseId ID of the course. - * @param userId ID of the user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateCourseCompetencies(courseId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCourseCompetenciesCacheKey(courseId)); - }).then(() => { - if (!userId || userId == this.sitesProvider.getCurrentSiteUserId()) { - return; - } - - /* Competencies for other users are fetched with getCompetencyInCourse (and saved in their own cache). - We need to fecth the list of competencies to know which ones to invalidate. We can pass 0 as userId - to getCourseCompetencies, we just need the competency IDs and this way we avid extra WS calls. */ - return this.getCourseCompetencies(courseId, 0, siteId).then((competencies) => { - const promises = competencies.competencies.map((competency) => { - return this.invalidateCompetencyInCourse(courseId, competency.competency.id, userId, siteId); - }); - - return Promise.all(promises); - }); - }); - } - - /** - * Report the competency as being viewed in plan. - * - * @param planId ID of the plan. - * @param competencyId ID of the competency. - * @param planStatus Current plan Status to decide what action should be logged. - * @param name Name of the competency. - * @param userId User ID. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, name?: string, userId?: number, - siteId?: string): Promise { - if (planId && competencyId) { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const params = { - planid: planId, - competencyid: competencyId, - userid: userId - }, - preSets = { - typeExpected: 'boolean' - }, - wsName = planStatus == AddonCompetencyProvider.STATUS_COMPLETE ? - 'core_competency_user_competency_plan_viewed' : 'core_competency_user_competency_viewed_in_plan'; - - this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, { - planid: planId, - planstatus: planStatus, - userid: userId - }, siteId); - - return site.write(wsName, params, preSets).then((success: boolean) => { - if (!success) { - return Promise.reject(null); - } - }); - }); - } - - return Promise.reject(null); - } - - /** - * Report the competency as being viewed in course. - * - * @param courseId ID of the course. - * @param competencyId ID of the competency. - * @param name Name of the competency. - * @param userId User ID. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logCompetencyInCourseView(courseId: number, competencyId: number, name?: string, userId?: number, siteId?: string) - : Promise { - - if (courseId && competencyId) { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const params = { - courseid: courseId, - competencyid: competencyId, - userid: userId - }; - const preSets = { - typeExpected: 'boolean' - }; - const wsName = 'core_competency_user_competency_viewed_in_course'; - - this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, { - courseid: courseId, - userid: userId - }, siteId); - - return site.write(wsName, params, preSets).then((success: boolean) => { - if (!success) { - return Promise.reject(null); - } - }); - }); - } - - return Promise.reject(null); - } - - /** - * Report the competency as being viewed. - * - * @param competencyId ID of the competency. - * @param name Name of the competency. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logCompetencyView(competencyId: number, name?: string, siteId?: string): Promise { - if (competencyId) { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - id: competencyId, - }; - const preSets = { - typeExpected: 'boolean' - }; - const wsName = 'core_competency_competency_viewed'; - - this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, {}, siteId); - - return site.write(wsName, params, preSets).then((success: boolean) => { - if (!success) { - return Promise.reject(null); - } - }); - }); - } - - return Promise.reject(null); - } -} - -/** - * Data returned by competency's plan_exporter. - */ -export type AddonCompetencyPlan = { - name: string; // Name. - description: string; // Description. - descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - userid: number; // Userid. - templateid: number; // Templateid. - origtemplateid: number; // Origtemplateid. - status: number; // Status. - duedate: number; // Duedate. - reviewerid: number; // Reviewerid. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. - statusname: string; // Statusname. - isbasedontemplate: boolean; // Isbasedontemplate. - canmanage: boolean; // Canmanage. - canrequestreview: boolean; // Canrequestreview. - canreview: boolean; // Canreview. - canbeedited: boolean; // Canbeedited. - isactive: boolean; // Isactive. - isdraft: boolean; // Isdraft. - iscompleted: boolean; // Iscompleted. - isinreview: boolean; // Isinreview. - iswaitingforreview: boolean; // Iswaitingforreview. - isreopenallowed: boolean; // Isreopenallowed. - iscompleteallowed: boolean; // Iscompleteallowed. - isunlinkallowed: boolean; // Isunlinkallowed. - isrequestreviewallowed: boolean; // Isrequestreviewallowed. - iscancelreviewrequestallowed: boolean; // Iscancelreviewrequestallowed. - isstartreviewallowed: boolean; // Isstartreviewallowed. - isstopreviewallowed: boolean; // Isstopreviewallowed. - isapproveallowed: boolean; // Isapproveallowed. - isunapproveallowed: boolean; // Isunapproveallowed. - duedateformatted: string; // Duedateformatted. - commentarea: CoreCommentsArea; - reviewer?: CoreUserSummary; - template?: AddonCompetencyTemplate; - url: string; // Url. -}; - -/** - * Data returned by competency's template_exporter. - */ -export type AddonCompetencyTemplate = { - shortname: string; // Shortname. - description: string; // Description. - descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - duedate: number; // Duedate. - visible: boolean; // Visible. - contextid: number; // Contextid. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. - duedateformatted: string; // Duedateformatted. - cohortscount: number; // Cohortscount. - planscount: number; // Planscount. - canmanage: boolean; // Canmanage. - canread: boolean; // Canread. - contextname: string; // Contextname. - contextnamenoprefix: string; // Contextnamenoprefix. -}; - -/** - * Data returned by competency's competency_exporter. - */ -export type AddonCompetencyCompetency = { - shortname: string; // Shortname. - idnumber: string; // Idnumber. - description: string; // Description. - descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - sortorder: number; // Sortorder. - parentid: number; // Parentid. - path: string; // Path. - ruleoutcome: number; // Ruleoutcome. - ruletype: string; // Ruletype. - ruleconfig: string; // Ruleconfig. - scaleid: number; // Scaleid. - scaleconfiguration: string; // Scaleconfiguration. - competencyframeworkid: number; // Competencyframeworkid. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. -}; - -/** - * Data returned by competency's competency_path_exporter. - */ -export type AddonCompetencyPath = { - ancestors: AddonCompetencyPathNode[]; // Ancestors. - framework: AddonCompetencyPathNode; - pluginbaseurl: string; // Pluginbaseurl. - pagecontextid: number; // Pagecontextid. - showlinks: boolean; // @since 3.7. Showlinks. -}; - -/** - * Data returned by competency's path_node_exporter. - */ -export type AddonCompetencyPathNode = { - id: number; // Id. - name: string; // Name. - first: boolean; // First. - last: boolean; // Last. - position: number; // Position. -}; - -/** - * Data returned by competency's user_competency_exporter. - */ -export type AddonCompetencyUserCompetency = { - userid: number; // Userid. - competencyid: number; // Competencyid. - status: number; // Status. - reviewerid: number; // Reviewerid. - proficiency: boolean; // Proficiency. - grade: number; // Grade. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. - canrequestreview: boolean; // Canrequestreview. - canreview: boolean; // Canreview. - gradename: string; // Gradename. - isrequestreviewallowed: boolean; // Isrequestreviewallowed. - iscancelreviewrequestallowed: boolean; // Iscancelreviewrequestallowed. - isstartreviewallowed: boolean; // Isstartreviewallowed. - isstopreviewallowed: boolean; // Isstopreviewallowed. - isstatusidle: boolean; // Isstatusidle. - isstatusinreview: boolean; // Isstatusinreview. - isstatuswaitingforreview: boolean; // Isstatuswaitingforreview. - proficiencyname: string; // Proficiencyname. - reviewer?: CoreUserSummary; - statusname: string; // Statusname. - url: string; // Url. -}; - -/** - * Data returned by competency's user_competency_plan_exporter. - */ -export type AddonCompetencyUserCompetencyPlan = { - userid: number; // Userid. - competencyid: number; // Competencyid. - proficiency: boolean; // Proficiency. - grade: number; // Grade. - planid: number; // Planid. - sortorder: number; // Sortorder. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. - gradename: string; // Gradename. - proficiencyname: string; // Proficiencyname. -}; - -/** - * Data returned by competency's user_competency_summary_in_plan_exporter. - */ -export type AddonCompetencyUserCompetencySummaryInPlan = { - usercompetencysummary: AddonCompetencyUserCompetencySummary; - plan: AddonCompetencyPlan; -}; - -/** - * Data returned by competency's user_competency_summary_exporter. - */ -export type AddonCompetencyUserCompetencySummary = { - showrelatedcompetencies: boolean; // Showrelatedcompetencies. - cangrade: boolean; // Cangrade. - competency: AddonCompetencySummary; - user: CoreUserSummary; - usercompetency?: AddonCompetencyUserCompetency; - usercompetencyplan?: AddonCompetencyUserCompetencyPlan; - usercompetencycourse?: AddonCompetencyUserCompetencyCourse; - evidence: AddonCompetencyEvidence[]; // Evidence. - commentarea?: CoreCommentsArea; -}; - -/** - * Data returned by competency's competency_summary_exporter. - */ -export type AddonCompetencySummary = { - linkedcourses: CoreCourseSummary; // Linkedcourses. - relatedcompetencies: AddonCompetencyCompetency[]; // Relatedcompetencies. - competency: AddonCompetencyCompetency; - framework: AddonCompetencyFramework; - hascourses: boolean; // Hascourses. - hasrelatedcompetencies: boolean; // Hasrelatedcompetencies. - scaleid: number; // Scaleid. - scaleconfiguration: string; // Scaleconfiguration. - taxonomyterm: string; // Taxonomyterm. - comppath: AddonCompetencyPath; - pluginbaseurl: string; // @since 3.7. Pluginbaseurl. -}; - -/** - * Data returned by competency's competency_framework_exporter. - */ -export type AddonCompetencyFramework = { - shortname: string; // Shortname. - idnumber: string; // Idnumber. - description: string; // Description. - descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - visible: boolean; // Visible. - scaleid: number; // Scaleid. - scaleconfiguration: string; // Scaleconfiguration. - contextid: number; // Contextid. - taxonomies: string; // Taxonomies. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. - canmanage: boolean; // Canmanage. - competenciescount: number; // Competenciescount. - contextname: string; // Contextname. - contextnamenoprefix: string; // Contextnamenoprefix. -}; - -/** - * Data returned by competency's user_competency_course_exporter. - */ -export type AddonCompetencyUserCompetencyCourse = { - userid: number; // Userid. - courseid: number; // Courseid. - competencyid: number; // Competencyid. - proficiency: boolean; // Proficiency. - grade: number; // Grade. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. - gradename: string; // Gradename. - proficiencyname: string; // Proficiencyname. -}; - -/** - * Data returned by competency's evidence_exporter. - */ -export type AddonCompetencyEvidence = { - usercompetencyid: number; // Usercompetencyid. - contextid: number; // Contextid. - action: number; // Action. - actionuserid: number; // Actionuserid. - descidentifier: string; // Descidentifier. - desccomponent: string; // Desccomponent. - desca: string; // Desca. - url: string; // Url. - grade: number; // Grade. - note: string; // Note. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. - actionuser?: CoreUserSummary; - description: string; // Description. - gradename: string; // Gradename. - userdate: string; // Userdate. - candelete: boolean; // Candelete. -}; - -/** - * Data returned by competency's user_competency_summary_in_course_exporter. - */ -export type AddonCompetencyUserCompetencySummaryInCourse = { - usercompetencysummary: AddonCompetencyUserCompetencySummary; - course: CoreCourseSummary; - coursemodules: CoreCourseModuleSummary[]; // Coursemodules. - plans: AddonCompetencyPlan[]; // @since 3.7. Plans. - pluginbaseurl: string; // @since 3.7. Pluginbaseurl. -}; - -/** - * Data returned by competency's course_competency_settings_exporter. - */ -export type AddonCompetencyCourseCompetencySettings = { - courseid: number; // Courseid. - pushratingstouserplans: boolean; // Pushratingstouserplans. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. -}; - -/** - * Data returned by competency's course_competency_statistics_exporter. - */ -export type AddonCompetencyCourseCompetencyStatistics = { - competencycount: number; // Competencycount. - proficientcompetencycount: number; // Proficientcompetencycount. - proficientcompetencypercentage: number; // Proficientcompetencypercentage. - proficientcompetencypercentageformatted: string; // Proficientcompetencypercentageformatted. - leastproficient: AddonCompetencyCompetency[]; // Leastproficient. - leastproficientcount: number; // Leastproficientcount. - canbegradedincourse: boolean; // Canbegradedincourse. - canmanagecoursecompetencies: boolean; // Canmanagecoursecompetencies. -}; - -/** - * Data returned by competency's course_competency_exporter. - */ -export type AddonCompetencyCourseCompetency = { - courseid: number; // Courseid. - competencyid: number; // Competencyid. - sortorder: number; // Sortorder. - ruleoutcome: number; // Ruleoutcome. - id: number; // Id. - timecreated: number; // Timecreated. - timemodified: number; // Timemodified. - usermodified: number; // Usermodified. -}; - -/** - * Result of WS tool_lp_data_for_plans_page. - */ -export type AddonCompetencyDataForPlansPageResult = { - userid: number; // The learning plan user id. - plans: AddonCompetencyPlan[]; - pluginbaseurl: string; // Url to the tool_lp plugin folder on this Moodle site. - navigation: string[]; - canreaduserevidence: boolean; // Can the current user view the user's evidence. - canmanageuserplans: boolean; // Can the current user manage the user's plans. -}; - -/** - * Result of WS tool_lp_data_for_plan_page. - */ -export type AddonCompetencyDataForPlanPageResult = { - plan: AddonCompetencyPlan; - contextid: number; // Context ID. - pluginbaseurl: string; // Plugin base URL. - competencies: AddonCompetencyDataForPlanPageCompetency[]; - competencycount: number; // Count of competencies. - proficientcompetencycount: number; // Count of proficientcompetencies. - proficientcompetencypercentage: number; // Percentage of competencies proficient. - proficientcompetencypercentageformatted: string; // Displayable percentage. -}; - -/** - * Competency data returned by tool_lp_data_for_plan_page. - */ -export type AddonCompetencyDataForPlanPageCompetency = { - competency: AddonCompetencyCompetency; - comppath: AddonCompetencyPath; - usercompetency?: AddonCompetencyUserCompetency; - usercompetencyplan?: AddonCompetencyUserCompetencyPlan; -}; - -/** - * Result of WS tool_lp_data_for_course_competencies_page. - */ -export type AddonCompetencyDataForCourseCompetenciesPageResult = { - courseid: number; // The current course id. - pagecontextid: number; // The current page context ID. - gradableuserid?: number; // Current user id, if the user is a gradable user. - canmanagecompetencyframeworks: boolean; // User can manage competency frameworks. - canmanagecoursecompetencies: boolean; // User can manage linked course competencies. - canconfigurecoursecompetencies: boolean; // User can configure course competency settings. - cangradecompetencies: boolean; // User can grade competencies. - settings: AddonCompetencyCourseCompetencySettings; - statistics: AddonCompetencyCourseCompetencyStatistics; - competencies: AddonCompetencyDataForCourseCompetenciesPageCompetency[]; - manageurl: string; // Url to the manage competencies page. - pluginbaseurl: string; // @since 3.6. Url to the course competencies page. -}; - -/** - * Competency data returned by tool_lp_data_for_course_competencies_page. - */ -export type AddonCompetencyDataForCourseCompetenciesPageCompetency = { - competency: AddonCompetencyCompetency; - coursecompetency: AddonCompetencyCourseCompetency; - coursemodules: CoreCourseModuleSummary[]; - usercompetencycourse?: AddonCompetencyUserCompetencyCourse; - ruleoutcomeoptions: { - value: number; // The option value. - text: string; // The name of the option. - selected: boolean; // If this is the currently selected option. - }[]; - comppath: AddonCompetencyPath; - plans: AddonCompetencyPlan[]; // @since 3.7. -}; diff --git a/src/addon/competency/providers/course-option-handler.ts b/src/addon/competency/providers/course-option-handler.ts deleted file mode 100644 index c38c2fd2a..000000000 --- a/src/addon/competency/providers/course-option-handler.ts +++ /dev/null @@ -1,132 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonCompetencyCourseComponent } from '../components/course/course'; -import { AddonCompetencyProvider } from '../providers/competency'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; - -/** - * Course nav handler. - */ -@Injectable() -export class AddonCompetencyCourseOptionHandler implements CoreCourseOptionsHandler { - name = 'AddonCompetency'; - priority = 300; - - constructor(private competencyProvider: AddonCompetencyProvider, protected filterHelper: CoreFilterHelperProvider) {} - - /** - * Whether or not the handler is enabled ona site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether or not the handler is enabled for a certain course. - * - * @param courseId The course ID. - * @param accessData Access type and data. Default, guest, ... - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return True or promise resolved with true if enabled. - */ - isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { - if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) { - return false; // Not enabled for guests. - } - - if (navOptions && typeof navOptions.competencies != 'undefined') { - return navOptions.competencies; - } - - return this.competencyProvider.isPluginForCourseEnabled(courseId).then((competencies) => { - return competencies ? !competencies.canmanagecoursecompetencies : false; - }); - } - - /** - * Returns the data needed to render the handler. - * - * @param injector Injector. - * @param course The course. - * @return Data or promise resolved with the data. - */ - getDisplayData?(injector: Injector, course: any): CoreCourseOptionsHandlerData | Promise { - return { - title: 'addon.competency.competencies', - class: 'addon-competency-course-handler', - component: AddonCompetencyCourseComponent - }; - } - - /** - * Should invalidate the data to determine if the handler is enabled for a certain course. - * - * @param courseId The course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved when done. - */ - invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise { - if (navOptions && typeof navOptions.competencies != 'undefined') { - // No need to invalidate anything. - return Promise.resolve(); - } - - return this.competencyProvider.invalidateCourseCompetencies(courseId); - } - - /** - * Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline. - * - * @param course The course. - * @return Promise resolved when done. - */ - prefetch(course: any): Promise { - // Get the competencies in the course. - return this.competencyProvider.getCourseCompetencies(course.id, undefined, undefined, true).then((competencies) => { - const promises = []; - - // Prefetch all the competencies. - if (competencies && competencies.competencies) { - competencies.competencies.forEach((competency) => { - promises.push(this.competencyProvider.getCompetencyInCourse(course.id, competency.competency.id, undefined, - undefined, true)); - - promises.push(this.competencyProvider.getCompetencySummary(competency.competency.id, undefined, undefined, - true)); - - if (competency.coursemodules) { - competency.coursemodules.forEach((module) => { - promises.push(this.filterHelper.getFilters('module', module.id, {courseId: course.id})); - }); - } - - if (competency.plans) { - competency.plans.forEach((plan) => { - promises.push(this.filterHelper.getFilters('user', plan.userid)); - }); - } - }); - } - - return Promise.all(promises); - }); - } -} diff --git a/src/addon/competency/providers/helper.ts b/src/addon/competency/providers/helper.ts deleted file mode 100644 index b956578d5..000000000 --- a/src/addon/competency/providers/helper.ts +++ /dev/null @@ -1,105 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonCompetencyProvider } from './competency'; - -/** - * Service that provides some features regarding learning plans. - */ -@Injectable() -export class AddonCompetencyHelperProvider { - - constructor(private sitesProvider: CoreSitesProvider, private userProvider: CoreUserProvider, - private translate: TranslateService) { - } - - /** - * Convenient helper to get the user profile image. - * - * @param userId User Id - * @return User profile Image URL or true if default icon. - */ - getProfile(userId: number): Promise { - if (!userId || userId == this.sitesProvider.getCurrentSiteUserId()) { - return Promise.resolve(false); - } - - // Get the user profile to retrieve the user image. - return this.userProvider.getProfile(userId, null, true).then((user) => { - user.profileimageurl = user.profileimageurl || true; - - return user; - }); - } - - /** - * Get the review status name translated. - * - * @param status - */ - getCompetencyStatusName(status: number): string { - let statusTranslateName; - switch (status) { - case AddonCompetencyProvider.REVIEW_STATUS_IDLE: - statusTranslateName = 'idle'; - break; - case AddonCompetencyProvider.REVIEW_STATUS_IN_REVIEW: - statusTranslateName = 'inreview'; - break; - case AddonCompetencyProvider.REVIEW_STATUS_WAITING_FOR_REVIEW: - statusTranslateName = 'waitingforreview'; - break; - default: - // We can use the current status name. - return String(status); - } - - return this.translate.instant('addon.competency.usercompetencystatus_' + statusTranslateName); - } - - /** - * Get the status name translated. - * - * @param status - */ - getPlanStatusName(status: number): string { - let statusTranslateName; - switch (status) { - case AddonCompetencyProvider.STATUS_DRAFT: - statusTranslateName = 'draft'; - break; - case AddonCompetencyProvider.STATUS_ACTIVE: - statusTranslateName = 'active'; - break; - case AddonCompetencyProvider.STATUS_COMPLETE: - statusTranslateName = 'complete'; - break; - case AddonCompetencyProvider.STATUS_WAITING_FOR_REVIEW: - statusTranslateName = 'waitingforreview'; - break; - case AddonCompetencyProvider.STATUS_IN_REVIEW: - statusTranslateName = 'inreview'; - break; - default: - // We can use the current status name. - return String(status); - } - - return this.translate.instant('addon.competency.planstatus' + statusTranslateName); - } -} diff --git a/src/addon/competency/providers/mainmenu-handler.ts b/src/addon/competency/providers/mainmenu-handler.ts deleted file mode 100644 index f3040968c..000000000 --- a/src/addon/competency/providers/mainmenu-handler.ts +++ /dev/null @@ -1,54 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonCompetencyProvider } from './competency'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; - -/** - * Handler to inject an option into main menu. - */ -@Injectable() -export class AddonCompetencyMainMenuHandler implements CoreMainMenuHandler { - name = 'AddonCompetency'; - priority = 500; - - constructor(private competencyProvider: AddonCompetencyProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - // Check the user has at least one learn plan available. - return this.competencyProvider.getLearningPlans().then((plans) => { - return plans.length > 0; - }); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreMainMenuHandlerData { - return { - icon: 'map', - title: 'addon.competency.myplans', - page: 'AddonCompetencyPlanListPage', - class: 'addon-competency-handler' - }; - } -} diff --git a/src/addon/competency/providers/plan-link-handler.ts b/src/addon/competency/providers/plan-link-handler.ts deleted file mode 100644 index d03060f3b..000000000 --- a/src/addon/competency/providers/plan-link-handler.ts +++ /dev/null @@ -1,68 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonCompetencyProvider } from './competency'; - -/** - * Handler to treat links to a plan. - */ -@Injectable() -export class AddonCompetencyPlanLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonCompetencyPlanLinkHandler'; - pattern = /\/admin\/tool\/lp\/plan\.php.*([\?\&]id=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private competencyProvider: AddonCompetencyProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - return [{ - action: (siteId, navCtrl?): void => { - this.linkHelper.goInSite(navCtrl, 'AddonCompetencyPlanPage', { planId: params.id }, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - // Handler is disabled if all competency features are disabled. - return this.competencyProvider.allCompetenciesDisabled(siteId).then((disabled) => { - return !disabled; - }); - } -} diff --git a/src/addon/competency/providers/plans-link-handler.ts b/src/addon/competency/providers/plans-link-handler.ts deleted file mode 100644 index a423c1fc7..000000000 --- a/src/addon/competency/providers/plans-link-handler.ts +++ /dev/null @@ -1,69 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonCompetencyProvider } from './competency'; - -/** - * Handler to treat links to user plans. - */ -@Injectable() -export class AddonCompetencyPlansLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonCompetencyPlansLinkHandler'; - pattern = /\/admin\/tool\/lp\/plans\.php/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private competencyProvider: AddonCompetencyProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - return [{ - action: (siteId, navCtrl?): void => { - this.linkHelper.goInSite(navCtrl, 'AddonCompetencyPlanListPage', { userId: params.userid }, siteId, - typeof params.userid == 'undefined'); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - // Handler is disabled if all competency features are disabled. - return this.competencyProvider.allCompetenciesDisabled(siteId).then((disabled) => { - return !disabled; - }); - } -} diff --git a/src/addon/competency/providers/push-click-handler.ts b/src/addon/competency/providers/push-click-handler.ts deleted file mode 100644 index c40509fec..000000000 --- a/src/addon/competency/providers/push-click-handler.ts +++ /dev/null @@ -1,100 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreLoginHelperProvider } from '@core/login/providers/helper'; -import { AddonCompetencyProvider } from './competency'; - -/** - * Handler for competencies push notifications clicks. - */ -@Injectable() -export class AddonCompetencyPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonCompetencyPushClickHandler'; - priority = 200; - - constructor(private utils: CoreUtilsProvider, private urlUtils: CoreUrlUtilsProvider, - private competencyProvider: AddonCompetencyProvider, private loginHelper: CoreLoginHelperProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - if (this.utils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'moodle' && - (notification.name == 'competencyplancomment' || notification.name == 'competencyusercompcomment')) { - // If all competency features are disabled, don't handle the click. - return this.competencyProvider.allCompetenciesDisabled(notification.site).then((disabled) => { - return !disabled; - }); - } - - return false; - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - handleClick(notification: any): Promise { - const contextUrlParams = this.urlUtils.extractUrlParams(notification.contexturl); - - if (notification.name == 'competencyplancomment') { - // Open the learning plan. - const planId = Number(contextUrlParams.id); - - return this.competencyProvider.invalidateLearningPlan(planId, notification.site).catch(() => { - // Ignore errors. - }).then(() => { - return this.loginHelper.redirect('AddonCompetencyPlanPage', { planId: planId }, notification.site); - }); - } else { - - if (notification.contexturl && notification.contexturl.indexOf('user_competency_in_plan.php') != -1) { - // Open the competency. - const courseId = Number(notification.course), - competencyId = Number(contextUrlParams.competencyid), - planId = Number(contextUrlParams.planid), - userId = Number(contextUrlParams.userid); - - return this.competencyProvider.invalidateCompetencyInPlan(planId, competencyId, notification.site).catch(() => { - // Ignore errors. - }).then(() => { - return this.loginHelper.redirect('AddonCompetencyCompetencyPage', { - planId: planId, - competencyId: competencyId, - courseId: courseId, - userId: userId - }, notification.site); - }); - } else { - // Open the list of plans. - const userId = Number(contextUrlParams.userid); - - return this.competencyProvider.invalidateLearningPlans(userId, notification.site).catch(() => { - // Ignore errors. - }).then(() => { - return this.loginHelper.redirect('AddonCompetencyPlanListPage', { userId: userId }, notification.site); - }); - } - } - } -} diff --git a/src/addon/competency/providers/user-competency-link-handler.ts b/src/addon/competency/providers/user-competency-link-handler.ts deleted file mode 100644 index 3d1eafe35..000000000 --- a/src/addon/competency/providers/user-competency-link-handler.ts +++ /dev/null @@ -1,68 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonCompetencyProvider } from './competency'; - -/** - * Handler to treat links to a usr competency. - */ -@Injectable() -export class AddonCompetencyUserCompetencyLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonCompetencyUserCompetencyLinkHandler'; - pattern = /\/admin\/tool\/lp\/user_competency\.php.*([\?\&]id=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private competencyProvider: AddonCompetencyProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - return [{ - action: (siteId, navCtrl?): void => { - this.linkHelper.goInSite(navCtrl, 'AddonCompetencyCompetencySummaryPage', { competencyId: params.id }, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - // Handler is disabled if all competency features are disabled. - return this.competencyProvider.allCompetenciesDisabled(siteId).then((disabled) => { - return !disabled; - }); - } -} diff --git a/src/addon/competency/providers/user-handler.ts b/src/addon/competency/providers/user-handler.ts deleted file mode 100644 index b2c07ebc0..000000000 --- a/src/addon/competency/providers/user-handler.ts +++ /dev/null @@ -1,132 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonCompetencyProvider } from './competency'; - -/** - * Profile competencies handler. - */ -@Injectable() -export class AddonCompetencyUserHandler implements CoreUserProfileHandler { - name = 'AddonCompetency:learningPlan'; - priority = 900; - type = CoreUserDelegate.TYPE_NEW_PAGE; - participantsNavEnabledCache = {}; - usersNavEnabledCache = {}; - - constructor(private linkHelper: CoreContentLinksHelperProvider, protected sitesProvider: CoreSitesProvider, - private competencyProvider: AddonCompetencyProvider, eventsProvider: CoreEventsProvider) { - eventsProvider.on(CoreEventsProvider.LOGOUT, this.clearUsersNavCache.bind(this)); - eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, this.clearUsersNavCache.bind(this)); - } - - /** - * Clear users nav cache. - */ - private clearUsersNavCache(): void { - this.participantsNavEnabledCache = {}; - this.usersNavEnabledCache = {}; - } - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if handler is enabled for this user in this context. - * - * @param user User to check. - * @param courseId Course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved with true if enabled, resolved with false otherwise. - */ - isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { - if (courseId) { - const cacheKey = courseId + '.' + user.id; - - // Link on a user course profile. - if (typeof this.participantsNavEnabledCache[cacheKey] != 'undefined') { - return this.participantsNavEnabledCache[cacheKey]; - } - - return this.competencyProvider.getCourseCompetencies(courseId, user.id).then((response) => { - const enabled = response.competencies.length > 0; - this.participantsNavEnabledCache[cacheKey] = enabled; - - return enabled; - }).catch((message) => { - this.participantsNavEnabledCache[cacheKey] = false; - - return false; - }); - } else { - // Link on a user site profile. - if (typeof this.usersNavEnabledCache[user.id] != 'undefined') { - return this.usersNavEnabledCache[user.id]; - } - - return this.competencyProvider.getLearningPlans(user.id).then((plans) => { - // Check the user has at least one learn plan available. - const enabled = plans.length > 0; - this.usersNavEnabledCache[user.id] = enabled; - - return enabled; - }); - } - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { - if (courseId) { - return { - icon: 'ribbon', - title: 'addon.competency.competencies', - class: 'addon-competency-handler', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - - this.linkHelper.goInSite(navCtrl, 'AddonCompetencyCourseCompetenciesPage', {courseId, userId: user.id}); - } - }; - } else { - return { - icon: 'map', - title: 'addon.competency.learningplans', - class: 'addon-competency-handler', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - - this.linkHelper.goInSite(navCtrl, 'AddonCompetencyPlanListPage', {userId: user.id}); - } - }; - } - } -} diff --git a/src/addon/coursecompletion/components/components.module.ts b/src/addon/coursecompletion/components/components.module.ts deleted file mode 100644 index 5ed07218c..000000000 --- a/src/addon/coursecompletion/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonCourseCompletionReportComponent } from './report/report'; - -@NgModule({ - declarations: [ - AddonCourseCompletionReportComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule - ], - providers: [ - ], - exports: [ - AddonCourseCompletionReportComponent - ], - entryComponents: [ - AddonCourseCompletionReportComponent - ] -}) -export class AddonCourseCompletionComponentsModule {} diff --git a/src/addon/coursecompletion/components/report/addon-course-completion-report.html b/src/addon/coursecompletion/components/report/addon-course-completion-report.html deleted file mode 100644 index 954447487..000000000 --- a/src/addon/coursecompletion/components/report/addon-course-completion-report.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - -

{{ 'addon.coursecompletion.status' | translate }}

-

{{ statusText | translate }}

-
- -

{{ 'addon.coursecompletion.required' | translate }}

-

{{ 'addon.coursecompletion.criteriarequiredall' | translate }}

-

{{ 'addon.coursecompletion.criteriarequiredany' | translate }}

-
-
- - {{ 'addon.coursecompletion.requiredcriteria' | translate }} - -

-

- {{ criteria.status }} -
- - - {{ 'addon.coursecompletion.criteriagroup' | translate }} - {{ 'addon.coursecompletion.criteria' | translate }} - {{ 'addon.coursecompletion.requirement' | translate }} - {{ 'addon.coursecompletion.status' | translate }} - {{ 'addon.coursecompletion.complete' | translate }} - {{ 'addon.coursecompletion.completiondate' | translate }} - - - - - - - {{ criteria.status }} - {{ criteria.timecompleted * 1000 | coreFormatDate :'strftimedatetimeshort' }} - - - -
- - {{ 'addon.coursecompletion.manualselfcompletion' | translate }} - - - - - -
- - {{ 'addon.coursecompletion.nottracked' | translate }} -
-
-
diff --git a/src/addon/coursecompletion/components/report/report.ts b/src/addon/coursecompletion/components/report/report.ts deleted file mode 100644 index 2f3532f0a..000000000 --- a/src/addon/coursecompletion/components/report/report.ts +++ /dev/null @@ -1,104 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonCourseCompletionProvider, AddonCourseCompletionCourseCompletionStatus } from '../../providers/coursecompletion'; - -/** - * Component that displays the course completion report. - */ -@Component({ - selector: 'addon-course-completion-report', - templateUrl: 'addon-course-completion-report.html', -}) -export class AddonCourseCompletionReportComponent implements OnInit { - @Input() courseId: number; - @Input() userId: number; - - completionLoaded = false; - completion: AddonCourseCompletionCourseCompletionStatus; - showSelfComplete: boolean; - tracked = true; // Whether completion is tracked. - statusText: string; - - constructor( - private sitesProvider: CoreSitesProvider, - private domUtils: CoreDomUtilsProvider, - private courseCompletionProvider: AddonCourseCompletionProvider) {} - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.userId) { - this.userId = this.sitesProvider.getCurrentSiteUserId(); - } - - this.fetchCompletion().finally(() => { - this.completionLoaded = true; - }); - } - - /** - * Fetch compleiton data. - * - * @return Promise resolved when done. - */ - protected fetchCompletion(): Promise { - return this.courseCompletionProvider.getCompletion(this.courseId, this.userId).then((completion) => { - - this.statusText = this.courseCompletionProvider.getCompletedStatusText(completion); - - this.completion = completion; - this.showSelfComplete = this.courseCompletionProvider.canMarkSelfCompleted(this.userId, completion); - this.tracked = true; - }).catch((error) => { - if (error && error.errorcode == 'notenroled') { - // Not enrolled error, probably a teacher. - this.tracked = false; - } else { - this.domUtils.showErrorModalDefault(error, 'addon.coursecompletion.couldnotloadreport', true); - } - }); - } - - /** - * Refresh completion data on PTR. - * - * @param refresher Refresher instance. - */ - refreshCompletion(refresher?: any): void { - this.courseCompletionProvider.invalidateCourseCompletion(this.courseId, this.userId).finally(() => { - this.fetchCompletion().finally(() => { - refresher && refresher.complete(); - }); - }); - } - - /** - * Mark course as completed. - */ - completeCourse(): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - this.courseCompletionProvider.markCourseAsSelfCompleted(this.courseId).then(() => { - return this.refreshCompletion(); - }).catch((message) => { - this.domUtils.showErrorModal(message); - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/coursecompletion/coursecompletion.module.ts b/src/addon/coursecompletion/coursecompletion.module.ts deleted file mode 100644 index 6ce5ebbfd..000000000 --- a/src/addon/coursecompletion/coursecompletion.module.ts +++ /dev/null @@ -1,42 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonCourseCompletionProvider } from './providers/coursecompletion'; -import { AddonCourseCompletionCourseOptionHandler } from './providers/course-option-handler'; -import { AddonCourseCompletionUserHandler } from './providers/user-handler'; -import { AddonCourseCompletionComponentsModule } from './components/components.module'; -import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; -import { CoreUserDelegate } from '@core/user/providers/user-delegate'; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonCourseCompletionComponentsModule - ], - providers: [ - AddonCourseCompletionProvider, - AddonCourseCompletionCourseOptionHandler, - AddonCourseCompletionUserHandler - ] -}) -export class AddonCourseCompletionModule { - constructor(courseOptionsDelegate: CoreCourseOptionsDelegate, courseOptionHandler: AddonCourseCompletionCourseOptionHandler, - userDelegate: CoreUserDelegate, userHandler: AddonCourseCompletionUserHandler) { - // Register handlers. - courseOptionsDelegate.registerHandler(courseOptionHandler); - userDelegate.registerHandler(userHandler); - } -} diff --git a/src/addon/coursecompletion/lang/en.json b/src/addon/coursecompletion/lang/en.json deleted file mode 100644 index 81ef0272e..000000000 --- a/src/addon/coursecompletion/lang/en.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "complete": "Complete", - "completecourse": "Complete course", - "completed": "Completed", - "completiondate": "Completion date", - "completionmenuitem": "Completion", - "couldnotloadreport": "Could not load the course completion report. Please try again later.", - "coursecompletion": "Course completion", - "criteria": "Criteria", - "criteriagroup": "Criteria group", - "criteriarequiredall": "All criteria below are required", - "criteriarequiredany": "Any criteria below are required", - "inprogress": "In progress", - "manualselfcompletion": "Manual self completion", - "nottracked": "You are currently not being tracked by completion in this course", - "notyetstarted": "Not yet started", - "pending": "Pending", - "required": "Required", - "requiredcriteria": "Required criteria", - "requirement": "Requirement", - "status": "Status", - "viewcoursereport": "View course report" -} \ No newline at end of file diff --git a/src/addon/coursecompletion/pages/report/report.html b/src/addon/coursecompletion/pages/report/report.html deleted file mode 100644 index 6bea93b55..000000000 --- a/src/addon/coursecompletion/pages/report/report.html +++ /dev/null @@ -1,6 +0,0 @@ - - - {{ 'addon.coursecompletion.coursecompletion' | translate }} - - - diff --git a/src/addon/coursecompletion/pages/report/report.module.ts b/src/addon/coursecompletion/pages/report/report.module.ts deleted file mode 100644 index 0bb1284d5..000000000 --- a/src/addon/coursecompletion/pages/report/report.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonCourseCompletionComponentsModule } from '../../components/components.module'; -import { AddonCourseCompletionReportPage } from './report'; - -@NgModule({ - declarations: [ - AddonCourseCompletionReportPage, - ], - imports: [ - CoreDirectivesModule, - AddonCourseCompletionComponentsModule, - IonicPageModule.forChild(AddonCourseCompletionReportPage), - TranslateModule.forChild() - ], -}) -export class AddonModFolderIndexPageModule {} diff --git a/src/addon/coursecompletion/pages/report/report.ts b/src/addon/coursecompletion/pages/report/report.ts deleted file mode 100644 index eec7e95a8..000000000 --- a/src/addon/coursecompletion/pages/report/report.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; - -/** - * Page that displays the course completion report. - */ -@IonicPage({ segment: 'addon-course-completion-report' }) -@Component({ - selector: 'page-addon-course-completion-report', - templateUrl: 'report.html', -}) -export class AddonCourseCompletionReportPage { - - courseId: number; - userId: number; - - constructor(navParams: NavParams) { - this.courseId = navParams.get('courseId'); - this.userId = navParams.get('userId'); - } -} diff --git a/src/addon/coursecompletion/providers/course-option-handler.ts b/src/addon/coursecompletion/providers/course-option-handler.ts deleted file mode 100644 index 0a7671657..000000000 --- a/src/addon/coursecompletion/providers/course-option-handler.ts +++ /dev/null @@ -1,108 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { AddonCourseCompletionProvider } from './coursecompletion'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate'; -import { AddonCourseCompletionReportComponent } from '../components/report/report'; - -/** - * Handler to inject an option into the course main menu. - */ -@Injectable() -export class AddonCourseCompletionCourseOptionHandler implements CoreCourseOptionsHandler { - name = 'AddonCourseCompletion'; - priority = 200; - - constructor(private courseCompletionProvider: AddonCourseCompletionProvider) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.courseCompletionProvider.isPluginViewEnabled(); - } - - /** - * Whether or not the handler is enabled for a certain course. - * - * @param courseId The course ID. - * @param accessData Access type and data. Default, guest, ... - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return True or promise resolved with true if enabled. - */ - isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { - if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) { - return false; // Not enabled for guests. - } - - return this.courseCompletionProvider.isPluginViewEnabledForCourse(courseId).then((courseEnabled) => { - // If is not enabled in the course, is not enabled for the user. - if (!courseEnabled) { - return false; - } - - // Check if the user can see his own report, teachers can't. - return this.courseCompletionProvider.isPluginViewEnabledForUser(courseId); - }); - } - - /** - * Returns the data needed to render the handler. - * - * @param courseId The course ID. - * @return Data. - */ - getDisplayData?(injector: Injector, courseId: number): CoreCourseOptionsHandlerData { - return { - title: 'addon.coursecompletion.completionmenuitem', - class: 'addon-coursecompletion-course-handler', - component: AddonCourseCompletionReportComponent, - }; - } - - /** - * Should invalidate the data to determine if the handler is enabled for a certain course. - * - * @param courseId The course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved when done. - */ - invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise { - return this.courseCompletionProvider.invalidateCourseCompletion(courseId); - } - - /** - * Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline. - * - * @param course The course. - * @return Promise resolved when done. - */ - prefetch(course: any): Promise { - return this.courseCompletionProvider.getCompletion(course.id, undefined, { - getFromCache: false, - emergencyCache: false - }).catch((error) => { - if (error && error.errorcode == 'notenroled') { - // Not enrolled error, probably a teacher. Ignore error. - } else { - return Promise.reject(error); - } - }); - } -} diff --git a/src/addon/coursecompletion/providers/coursecompletion.ts b/src/addon/coursecompletion/providers/coursecompletion.ts deleted file mode 100644 index 85ab726fd..000000000 --- a/src/addon/coursecompletion/providers/coursecompletion.ts +++ /dev/null @@ -1,303 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning } from '@providers/ws'; - -/** - * Service to handle course completion. - */ -@Injectable() -export class AddonCourseCompletionProvider { - - protected ROOT_CACHE_KEY = 'mmaCourseCompletion:'; - protected logger; - - constructor(logger: CoreLoggerProvider, - private sitesProvider: CoreSitesProvider, - private coursesProvider: CoreCoursesProvider, - private utils: CoreUtilsProvider) { - this.logger = logger.getInstance('AddonCourseCompletionProvider'); - } - - /** - * Returns whether or not the user can mark a course as self completed. - * It can if it's configured in the course and it hasn't been completed yet. - * - * @param userId User ID. - * @param completion Course completion. - * @return True if user can mark course as self completed, false otherwise. - */ - canMarkSelfCompleted(userId: number, completion: AddonCourseCompletionCourseCompletionStatus): boolean { - let selfCompletionActive = false, - alreadyMarked = false; - - if (this.sitesProvider.getCurrentSiteUserId() != userId) { - return false; - } - - completion.completions.forEach((criteria) => { - if (criteria.type === 1) { - // Self completion criteria found. - selfCompletionActive = true; - alreadyMarked = criteria.complete; - } - }); - - return selfCompletionActive && !alreadyMarked; - } - - /** - * Get completed status text. The language code returned is meant to be translated. - * - * @param completion Course completion. - * @return Language code of the text to show. - */ - getCompletedStatusText(completion: AddonCourseCompletionCourseCompletionStatus): string { - if (completion.completed) { - return 'addon.coursecompletion.completed'; - } else { - // Let's calculate status. - let hasStarted = false; - completion.completions.forEach((criteria) => { - if (criteria.timecompleted || criteria.complete) { - hasStarted = true; - } - }); - if (hasStarted) { - return 'addon.coursecompletion.inprogress'; - } else { - return 'addon.coursecompletion.notyetstarted'; - } - } - } - - /** - * Get course completion status for a certain course and user. - * - * @param courseId Course ID. - * @param userId User ID. If not defined, use current user. - * @param preSets Presets to use when calling the WebService. - * @param siteId Site ID. If not defined, use current site. - * @return Promise to be resolved when the completion is retrieved. - */ - getCompletion(courseId: number, userId?: number, preSets?: any, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - preSets = preSets || {}; - - this.logger.debug('Get completion for course ' + courseId + ' and user ' + userId); - - const data = { - courseid: courseId, - userid: userId - }; - - preSets.cacheKey = this.getCompletionCacheKey(courseId, userId); - preSets.updateFrequency = preSets.updateFrequency || CoreSite.FREQUENCY_SOMETIMES; - preSets.cacheErrors = ['notenroled']; - - return site.read('core_completion_get_course_completion_status', data, preSets) - .then((data: AddonCourseCompletionGetCourseCompletionStatusResult): any => { - - if (data.completionstatus) { - return data.completionstatus; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get completion WS calls. - * - * @param courseId Course ID. - * @param useIid User ID. - * @return Cache key. - */ - protected getCompletionCacheKey(courseId: number, userId: number): string { - return this.ROOT_CACHE_KEY + 'view:' + courseId + ':' + userId; - } - - /** - * Invalidates view course completion WS call. - * - * @param courseId Course ID. - * @param userId User ID. If not defined, use current user. - * @return Promise resolved when the list is invalidated. - */ - invalidateCourseCompletion(courseId: number, userId?: number): Promise { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - - return this.sitesProvider.getCurrentSite().invalidateWsCacheForKey(this.getCompletionCacheKey(courseId, userId)); - } - - /** - * Returns whether or not the view course completion plugin is enabled for the current site. - * - * @return True if plugin enabled, false otherwise. - */ - isPluginViewEnabled(): boolean { - return this.sitesProvider.isLoggedIn(); - } - - /** - * Returns whether or not the view course completion plugin is enabled for a certain course. - * - * @param courseId Course ID. - * @param preferCache True if shouldn't call WS if data is cached, false otherwise. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - */ - isPluginViewEnabledForCourse(courseId: number, preferCache: boolean = true): Promise { - if (!courseId) { - return Promise.reject(null); - } - - return this.coursesProvider.getUserCourse(courseId, preferCache).then((course) => { - if (course) { - if (typeof course.enablecompletion != 'undefined' && course.enablecompletion == 0) { - // Completion not enabled for the course. - return false; - } - - if (typeof course.completionhascriteria != 'undefined' && course.completionhascriteria == 0) { - // No criteria, cannot view completion. - return false; - } - } - - return true; - }); - } - - /** - * Returns whether or not the view course completion plugin is enabled for a certain user. - * - * @param courseId Course ID. - * @param userId User ID. If not defined, use current user. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - */ - isPluginViewEnabledForUser(courseId: number, userId?: number): Promise { - // Check if user wants to view his own completion. - const currentUserId = this.sitesProvider.getCurrentSiteUserId(); - let promise; - - if (!userId || userId == currentUserId) { - // Viewing own completion. Get the course to check if it has completion criteria. - promise = this.coursesProvider.getUserCourse(courseId, true).then((course): any => { - // If the site is returning the completionhascriteria then the user can view his own completion. - // We already checked the value in isPluginViewEnabledForCourse. - if (course && typeof course.completionhascriteria != 'undefined') { - return true; - } - - return Promise.reject(null); - }); - } else { - promise = Promise.reject(null); - } - - return promise.catch(() => { - // User not viewing own completion or the site doesn't tell us if the course has criteria. - // The only way to know if completion can be viewed is to call the WS. - // Disable emergency cache to be able to detect that the plugin has been disabled (WS will fail). - const preSets: any = { - emergencyCache: 0 - }; - - return this.getCompletion(courseId, userId, preSets).then(() => { - return true; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WS returned an error, plugin is not enabled. - return false; - } else { - // Not a WS error. Check if we have a cached value. - preSets.omitExpires = true; - - return this.getCompletion(courseId, userId, preSets).then(() => { - return true; - }).catch(() => { - return false; - }); - } - }); - }); - } - - /** - * Mark a course as self completed. - * - * @param courseId Course ID. - * @return Promise resolved on success. - */ - markCourseAsSelfCompleted(courseId: number): Promise { - const params = { - courseid: courseId - }; - - return this.sitesProvider.getCurrentSite().write('core_completion_mark_course_self_completed', params) - .then((response: AddonCourseCompletionMarkCourseSelfCompletedResult) => { - - if (!response.status) { - return Promise.reject(null); - } - }); - } -} - -/** - * Completion status returned by core_completion_get_course_completion_status. - */ -export type AddonCourseCompletionCourseCompletionStatus = { - completed: boolean; // True if the course is complete, false otherwise. - aggregation: number; // Aggregation method 1 means all, 2 means any. - completions: { - type: number; // Completion criteria type. - title: string; // Completion criteria Title. - status: string; // Completion status (Yes/No) a % or number. - complete: boolean; // Completion status (true/false). - timecompleted: number; // Timestamp for criteria completetion. - details: { - type: string; // Type description. - criteria: string; // Criteria description. - requirement: string; // Requirement description. - status: string; // Status description, can be anything. - }; // Details. - }[]; -}; - -/** - * Result of WS core_completion_get_course_completion_status. - */ -export type AddonCourseCompletionGetCourseCompletionStatusResult = { - completionstatus: AddonCourseCompletionCourseCompletionStatus; // Course status. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS core_completion_mark_course_self_completed. - */ -export type AddonCourseCompletionMarkCourseSelfCompletedResult = { - status: boolean; // Status, true if success. - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/coursecompletion/providers/user-handler.ts b/src/addon/coursecompletion/providers/user-handler.ts deleted file mode 100644 index 51f826b1a..000000000 --- a/src/addon/coursecompletion/providers/user-handler.ts +++ /dev/null @@ -1,101 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonCourseCompletionProvider } from './coursecompletion'; - -/** - * Profile course completion handler. - */ -@Injectable() -export class AddonCourseCompletionUserHandler implements CoreUserProfileHandler { - name = 'AddonCourseCompletion'; - type = CoreUserDelegate.TYPE_NEW_PAGE; - priority = 200; - - protected enabledCache = {}; - - constructor(eventsProvider: CoreEventsProvider, private courseCompletionProvider: AddonCourseCompletionProvider) { - eventsProvider.on(CoreEventsProvider.LOGOUT, () => { - this.enabledCache = {}; - }); - eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, (data) => { - const cacheKey = data.userId + '-' + data.courseId; - - delete this.enabledCache[cacheKey]; - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.courseCompletionProvider.isPluginViewEnabled(); - } - - /** - * Check if handler is enabled for this user in this context. - * - * @param user User to check. - * @param courseId Course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved with true if enabled, resolved with false otherwise. - */ - isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { - if (!courseId) { - return false; - } - - return this.courseCompletionProvider.isPluginViewEnabledForCourse(courseId).then((courseEnabled) => { - // If is not enabled in the course, is not enabled for the user. - if (!courseEnabled) { - return false; - } - - const cacheKey = user.id + '-' + courseId; - if (typeof this.enabledCache[cacheKey] !== 'undefined') { - return this.enabledCache[cacheKey]; - } - - return this.courseCompletionProvider.isPluginViewEnabledForUser(courseId, user.id).then((enabled) => { - this.enabledCache[cacheKey] = enabled; - - return enabled; - }); - }); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { - return { - icon: 'checkbox-outline', - title: 'addon.coursecompletion.coursecompletion', - class: 'addon-coursecompletion-handler', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - navCtrl.push('AddonCourseCompletionReportPage', {courseId: courseId, userId: user.id }); - } - }; - } -} diff --git a/src/addon/files/files.module.ts b/src/addon/files/files.module.ts deleted file mode 100644 index b1233d7b3..000000000 --- a/src/addon/files/files.module.ts +++ /dev/null @@ -1,42 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonFilesProvider } from './providers/files'; -import { AddonFilesHelperProvider } from './providers/helper'; -import { AddonFilesMainMenuHandler } from './providers/mainmenu-handler'; -import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; - -// List of providers (without handlers). -export const ADDON_FILES_PROVIDERS: any[] = [ - AddonFilesProvider, - AddonFilesHelperProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - ], - providers: [ - AddonFilesProvider, - AddonFilesHelperProvider, - AddonFilesMainMenuHandler - ] -}) -export class AddonFilesModule { - constructor(mainMenuDelegate: CoreMainMenuDelegate, filesHandler: AddonFilesMainMenuHandler) { - mainMenuDelegate.registerHandler(filesHandler); - } -} diff --git a/src/addon/files/lang/en.json b/src/addon/files/lang/en.json deleted file mode 100644 index b923f6141..000000000 --- a/src/addon/files/lang/en.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "couldnotloadfiles": "The list of files could not be loaded.", - "emptyfilelist": "There are no files to show.", - "erroruploadnotworking": "Unfortunately it is currently not possible to upload files to your site.", - "files": "Files", - "privatefiles": "Private files", - "sitefiles": "Site files" -} \ No newline at end of file diff --git a/src/addon/files/pages/list/list.html b/src/addon/files/pages/list/list.html deleted file mode 100644 index 3b9207168..000000000 --- a/src/addon/files/pages/list/list.html +++ /dev/null @@ -1,44 +0,0 @@ - - - {{ title }} - - - - - - - - - -
- - {{ 'addon.files.privatefiles' | translate }} - {{ 'addon.files.sitefiles' | translate }} - -
- - -

{{ 'core.quotausage' | translate:{$a: {used: spaceUsed, total: userQuotaReadable} } }}

- - - - - - -

{{file.filename}}

-
- -
-
- - - -
- - - - - -
\ No newline at end of file diff --git a/src/addon/files/pages/list/list.module.ts b/src/addon/files/pages/list/list.module.ts deleted file mode 100644 index ad8bf4652..000000000 --- a/src/addon/files/pages/list/list.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonFilesListPage } from './list'; - -@NgModule({ - declarations: [ - AddonFilesListPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonFilesListPage), - TranslateModule.forChild() - ], -}) -export class AddonFilesListPageModule {} diff --git a/src/addon/files/pages/list/list.ts b/src/addon/files/pages/list/list.ts deleted file mode 100644 index 99b58c774..000000000 --- a/src/addon/files/pages/list/list.ts +++ /dev/null @@ -1,215 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { AddonFilesProvider, AddonFilesFile, AddonFilesGetUserPrivateFilesInfoResult } from '../../providers/files'; -import { AddonFilesHelperProvider } from '../../providers/helper'; - -/** - * Page that displays the list of files. - */ -@IonicPage({ segment: 'addon-files-list' }) -@Component({ - selector: 'page-addon-files-list', - templateUrl: 'list.html', -}) -export class AddonFilesListPage implements OnDestroy { - - title: string; // Page title. - showPrivateFiles: boolean; // Whether the user can view private files. - showSiteFiles: boolean; // Whether the user can view site files. - showUpload: boolean; // Whether the user can upload files. - root: string; // The root of the files loaded: 'my' or 'site'. - path: string; // The path of the directory being loaded. If empty path it means the root is being loaded. - userQuota: number; // The user quota (in bytes). - filesInfo: AddonFilesGetUserPrivateFilesInfoResult; // Info about private files (size, number of files, etc.). - spaceUsed: string; // Space used in a readable format. - userQuotaReadable: string; // User quota in a readable format. - files: AddonFilesFile[]; // List of files. - component: string; // Component to link the file downloads to. - filesLoaded: boolean; // Whether the files are loaded. - - protected updateSiteObserver; - - constructor(navParams: NavParams, eventsProvider: CoreEventsProvider, private sitesProvider: CoreSitesProvider, - private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private appProvider: CoreAppProvider, - private filesProvider: AddonFilesProvider, private filesHelper: AddonFilesHelperProvider, - private textUtils: CoreTextUtilsProvider) { - this.title = navParams.get('title') || this.translate.instant('addon.files.files'); - this.root = navParams.get('root'); - this.path = navParams.get('path'); - - // Update visibility if current site info is updated. - this.updateSiteObserver = eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => { - this.setVisibility(); - }, sitesProvider.getCurrentSiteId()); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.setVisibility(); - this.userQuota = this.sitesProvider.getCurrentSite().getInfo().userquota; - - if (!this.root) { - // Load private files by default. - if (this.showPrivateFiles) { - this.root = 'my'; - } else if (this.showSiteFiles) { - this.root = 'site'; - } - } - - if (this.root) { - this.rootChanged(); - } else { - this.filesLoaded = true; - } - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - */ - refreshData(refresher: any): void { - this.refreshFiles().finally(() => { - refresher.complete(); - }); - } - - /** - * Function called when the root has changed. - */ - rootChanged(): void { - this.filesLoaded = false; - this.component = this.root == 'my' ? AddonFilesProvider.PRIVATE_FILES_COMPONENT : AddonFilesProvider.SITE_FILES_COMPONENT; - - this.fetchFiles().finally(() => { - this.filesLoaded = true; - }); - } - - /** - * Upload a new file. - */ - uploadFile(): void { - this.filesProvider.versionCanUploadFiles().then((canUpload) => { - if (!canUpload) { - this.domUtils.showAlertTranslated('core.notice', 'addon.files.erroruploadnotworking'); - } else if (!this.appProvider.isOnline()) { - this.domUtils.showErrorModal('core.fileuploader.errormustbeonlinetoupload', true); - } else { - this.filesHelper.uploadPrivateFile(this.filesInfo).then(() => { - // File uploaded, refresh the list. - this.filesLoaded = false; - this.refreshFiles().finally(() => { - this.filesLoaded = true; - }); - }).catch(() => { - // Ignore errors, they're handled inside the function. - }); - } - }); - } - - /** - * Set visibility of some items based on site data. - */ - protected setVisibility(): void { - this.showPrivateFiles = this.filesProvider.canViewPrivateFiles(); - this.showSiteFiles = this.filesProvider.canViewSiteFiles(); - this.showUpload = this.filesProvider.canUploadFiles(); - } - - /** - * Fetch the files. - * - * @return Promise resolved when done. - */ - protected fetchFiles(): Promise { - let promise: Promise; - - if (!this.path) { - // The path is unknown, the user must be requesting a root. - if (this.root == 'site') { - this.title = this.translate.instant('addon.files.sitefiles'); - promise = this.filesProvider.getSiteFiles(); - } else if (this.root == 'my') { - this.title = this.translate.instant('addon.files.files'); - - promise = this.filesProvider.getPrivateFiles().then((files) => { - if (this.showUpload && this.filesProvider.canGetPrivateFilesInfo() && this.userQuota > 0) { - // Get the info to calculate the available size. - return this.filesProvider.getPrivateFilesInfo().then((info) => { - this.filesInfo = info; - this.spaceUsed = this.textUtils.bytesToSize(info.filesizewithoutreferences, 1); - this.userQuotaReadable = this.textUtils.bytesToSize(this.userQuota, 1); - - return files; - }); - } else { - // User quota isn't useful, delete it. - delete this.userQuota; - } - - return files; - }); - } else { - // Unknown root. - promise = Promise.reject(null); - } - } else { - // Path is set, serve the files the user requested. - promise = this.filesProvider.getFiles(this.path); - } - - return promise.then((files) => { - this.files = files; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.files.couldnotloadfiles', true); - }); - } - - /** - * Refresh the displayed files. - * - * @return Promise resolved when done. - */ - protected refreshFiles(): Promise { - const promises = []; - - promises.push(this.filesProvider.invalidateDirectory(this.root, this.path)); - promises.push(this.filesProvider.invalidatePrivateFilesInfoForUser()); - - return Promise.all(promises).finally(() => { - return this.fetchFiles(); - }); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.updateSiteObserver && this.updateSiteObserver.off(); - } -} diff --git a/src/addon/files/providers/files.ts b/src/addon/files/providers/files.ts deleted file mode 100644 index bae3eaf51..000000000 --- a/src/addon/files/providers/files.ts +++ /dev/null @@ -1,477 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning } from '@providers/ws'; - -/** - * Service to handle my files and site files. - */ -@Injectable() -export class AddonFilesProvider { - protected ROOT_CACHE_KEY = 'mmaFiles:'; - static PRIVATE_FILES_COMPONENT = 'mmaFilesMy'; - static SITE_FILES_COMPONENT = 'mmaFilesSite'; - - constructor(private sitesProvider: CoreSitesProvider, private mimeUtils: CoreMimetypeUtilsProvider) { } - - /** - * Check if core_user_get_private_files_info WS call is available. - * - * @return Whether the WS is available, false otherwise. - */ - canGetPrivateFilesInfo(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('core_user_get_private_files_info'); - } - - /** - * Check if user can view his private files. - * - * @return Whether the user can view his private files. - */ - canViewPrivateFiles(): boolean { - return this.sitesProvider.getCurrentSite().canAccessMyFiles() && !this.isPrivateFilesDisabledInSite(); - } - - /** - * Check if user can view site files. - * - * @return Whether the user can view site files. - */ - canViewSiteFiles(): boolean { - return !this.isSiteFilesDisabledInSite(); - } - - /** - * Check if user can upload private files. - * - * @return Whether the user can upload private files. - */ - canUploadFiles(): boolean { - const currentSite = this.sitesProvider.getCurrentSite(); - - return currentSite.canAccessMyFiles() && currentSite.canUploadFiles() && !this.isUploadDisabledInSite(); - } - - /** - * Get the list of files. - * - * @param params A list of parameters accepted by the Web service. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getFiles(params: any, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const preSets = { - cacheKey: this.getFilesListCacheKey(params), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - return site.read('core_files_get_files', params, preSets); - }).then((result: AddonFilesGetFilesResult) => { - const entries: AddonFilesFile[] = []; - - if (result.files) { - result.files.forEach((entry) => { - if (entry.isdir) { - // Create a "link" to load the folder. - entry.link = { - contextid: entry.contextid || null, - component: entry.component || '', - filearea: entry.filearea || '', - itemid: entry.itemid || 0, - filepath: entry.filepath || '', - filename: entry.filename || '' - }; - - if (entry.component) { - // Delete unused elements that may break the request. - entry.link.filename = ''; - } - } - - if (entry.isdir) { - entry.imgPath = this.mimeUtils.getFolderIcon(); - } else { - entry.imgPath = this.mimeUtils.getFileIcon(entry.filename); - } - - entries.push(entry); - }); - } - - return entries; - }); - } - - /** - * Get cache key for file list WS calls. - * - * @param params Params of the WS. - * @return Cache key. - */ - protected getFilesListCacheKey(params: any): string { - const root = !params.component ? 'site' : 'my'; - - return this.ROOT_CACHE_KEY + 'list:' + root + ':' + params.contextid + ':' + params.filepath; - } - - /** - * Get the private files of the current user. - * - * @return Promise resolved with the files. - */ - getPrivateFiles(): Promise { - return this.getFiles(this.getPrivateFilesRootParams()); - } - - /** - * Get params to get root private files directory. - * - * @return Params. - */ - protected getPrivateFilesRootParams(): any { - return { - contextid: -1, - component: 'user', - filearea: 'private', - contextlevel: 'user', - instanceid: this.sitesProvider.getCurrentSite().getUserId(), - itemid: 0, - filepath: '', - filename: '' - }; - } - - /** - * Get private files info. - * - * @param userId User ID. If not defined, current user in the site. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the info. - */ - getPrivateFilesInfo(userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const params = { - userid: userId - }, - preSets = { - cacheKey: this.getPrivateFilesInfoCacheKey(userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - return site.read('core_user_get_private_files_info', params, preSets); - }); - } - - /** - * Get the cache key for private files info WS calls. - * - * @param userId User ID. - * @return Cache key. - */ - protected getPrivateFilesInfoCacheKey(userId: number): string { - return this.getPrivateFilesInfoCommonCacheKey() + ':' + userId; - } - - /** - * Get the common part of the cache keys for private files info WS calls. - * - * @return Cache key. - */ - protected getPrivateFilesInfoCommonCacheKey(): string { - return this.ROOT_CACHE_KEY + 'privateInfo'; - } - - /** - * Get the site files. - * - * @return Promise resolved with the files. - */ - getSiteFiles(): Promise { - return this.getFiles(this.getSiteFilesRootParams()); - } - - /** - * Get params to get root site files directory. - * - * @return Params. - */ - protected getSiteFilesRootParams(): any { - return { - contextid: 0, - component: '', - filearea: '', - itemid: 0, - filepath: '', - filename: '' - }; - } - - /** - * Invalidates list of files in a certain directory. - * - * @param root Root of the directory ('my' for private files, 'site' for site files). - * @param path Path to the directory. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateDirectory(root: string, path: string, siteId?: string): Promise { - let params; - if (!path) { - if (root === 'site') { - params = this.getSiteFilesRootParams(); - } else if (root === 'my') { - params = this.getPrivateFilesRootParams(); - } - } else { - params = path; - } - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getFilesListCacheKey(params)); - }); - } - - /** - * Invalidates private files info for all users. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidatePrivateFilesInfo(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getPrivateFilesInfoCommonCacheKey()); - }); - } - - /** - * Invalidates private files info for a certain user. - * - * @param userId User ID. If not defined, current user in the site. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved when the data is invalidated. - */ - invalidatePrivateFilesInfoForUser(userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getPrivateFilesInfoCacheKey(userId)); - }); - } - - /** - * Check if Files is disabled in a certain site. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if disabled, rejected or resolved with false otherwise. - */ - isDisabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.isDisabledInSite(site); - }); - } - - /** - * Check if Files is disabled in a certain site. - * - * @param site Site. If not defined, use current site. - * @return Whether it's disabled. - */ - isDisabledInSite(site: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.isFeatureDisabled('CoreMainMenuDelegate_AddonFiles'); - } - - /** - * Return whether or not the plugin is enabled. - * - * @return True if enabled, false otherwise. - */ - isPluginEnabled(): boolean { - return this.canViewPrivateFiles() || this.canViewSiteFiles() || this.canUploadFiles(); - } - - /** - * Check if private files is disabled in a certain site. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if disabled, rejected or resolved with false otherwise. - */ - isPrivateFilesDisabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.isPrivateFilesDisabledInSite(site); - }); - } - - /** - * Check if private files is disabled in a certain site. - * - * @param site Site. If not defined, use current site. - * @return Whether it's disabled. - */ - isPrivateFilesDisabledInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.isFeatureDisabled('AddonFilesPrivateFiles'); - } - - /** - * Check if site files is disabled in a certain site. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if disabled, rejected or resolved with false otherwise. - */ - isSiteFilesDisabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.isSiteFilesDisabledInSite(site); - }); - } - - /** - * Check if site files is disabled in a certain site. - * - * @param site Site. If not defined, use current site. - * @return Whether it's disabled. - */ - isSiteFilesDisabledInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.isFeatureDisabled('AddonFilesSiteFiles'); - } - - /** - * Check if upload files is disabled in a certain site. - * - * @param siteId Site Id. If not defined, use current site. - * @return Promise resolved with true if disabled, rejected or resolved with false otherwise. - */ - isUploadDisabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.isUploadDisabledInSite(site); - }); - } - - /** - * Check if upload files is disabled in a certain site. - * - * @param site Site. If not defined, use current site. - * @return Whether it's disabled. - */ - isUploadDisabledInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.isFeatureDisabled('AddonFilesUpload'); - } - - /** - * Move a file from draft area to private files. - * - * @param draftId The draft area ID of the file. - * @param siteid ID of the site. If not defined, use current site. - * @return Promise resolved in success, rejected otherwise. - */ - moveFromDraftToPrivate(draftId: number, siteId?: string): Promise { - const params = { - draftid: draftId - }, - preSets = { - responseExpected: false - }; - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.write('core_user_add_user_private_files', params, preSets); - }); - } - - /** - * Check the Moodle version in order to check if upload files is working. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with true if WS is working, false otherwise. - */ - versionCanUploadFiles(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - // Upload private files doesn't work for Moodle 3.1.0 due to a bug. - return site.isVersionGreaterEqualThan('3.1.1'); - }); - } -} - -/** - * File data returned by core_files_get_files. - */ -export type AddonFilesFile = { - contextid: number; - component: string; - filearea: string; - itemid: number; - filepath: string; - filename: string; - isdir: boolean; - url: string; - timemodified: number; - timecreated?: number; // Time created. - filesize?: number; // File size. - author?: string; // File owner. - license?: string; // File license. -} & AddonFilesFileCalculatedData; - -/** - * Result of WS core_files_get_files. - */ -export type AddonFilesGetFilesResult = { - parents: { - contextid: number; - component: string; - filearea: string; - itemid: number; - filepath: string; - filename: string; - }[]; - files: AddonFilesFile[]; -}; - -/** - * Result of WS core_user_get_private_files_info. - */ -export type AddonFilesGetUserPrivateFilesInfoResult = { - filecount: number; // Number of files in the area. - foldercount: number; // Number of folders in the area. - filesize: number; // Total size of the files in the area. - filesizewithoutreferences: number; // Total size of the area excluding file references. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Calculated data for AddonFilesFile. - */ -export type AddonFilesFileCalculatedData = { - link?: { // Calculated in the app. A link to open the folder. - contextid?: number; // Folder's contextid. - component?: string; // Folder's component. - filearea?: string; // Folder's filearea. - itemid?: number; // Folder's itemid. - filepath?: string; // Folder's filepath. - filename?: string; // Folder's filename. - }; - imgPath?: string; // Path to file icon's image. -}; diff --git a/src/addon/files/providers/helper.ts b/src/addon/files/providers/helper.ts deleted file mode 100644 index d1d0cf6fd..000000000 --- a/src/addon/files/providers/helper.ts +++ /dev/null @@ -1,74 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper'; -import { AddonFilesProvider } from './files'; - -/** - * Service that provides some features regarding private and site files. - */ -@Injectable() -export class AddonFilesHelperProvider { - - constructor(private sitesProvider: CoreSitesProvider, private fileUploaderHelper: CoreFileUploaderHelperProvider, - private filesProvider: AddonFilesProvider, private domUtils: CoreDomUtilsProvider) { } - - /** - * Select a file, upload it and move it to private files. - * - * @param info Private files info. See AddonFilesProvider.getPrivateFilesInfo. - * @return Promise resolved when a file is uploaded, rejected otherwise. - */ - uploadPrivateFile(info?: any): Promise { - // Calculate the max size. - const currentSite = this.sitesProvider.getCurrentSite(); - let maxSize = currentSite.getInfo().usermaxuploadfilesize, - userQuota = currentSite.getInfo().userquota; - - if (userQuota === 0) { - // 0 means ignore user quota. In the app it is -1. - userQuota = -1; - } else if (userQuota > 0 && typeof info != 'undefined') { - userQuota = userQuota - info.filesizewithoutreferences; - } - - if (typeof userQuota != 'undefined') { - // Use the minimum value. - maxSize = Math.min(maxSize, userQuota); - } - - // Select and upload the file. - return this.fileUploaderHelper.selectAndUploadFile(maxSize).then((result) => { - if (!result) { - return Promise.reject(null); - } - - // File uploaded. Move it to private files. - const modal = this.domUtils.showModalLoading('core.fileuploader.uploading', true); - - return this.filesProvider.moveFromDraftToPrivate(result.itemid).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.fileuploader.errorwhileuploading', true); - - return Promise.reject(null); - }).finally(() => { - modal.dismiss(); - }); - }).then(() => { - this.domUtils.showToast('core.fileuploader.fileuploaded', true, undefined, 'core-toast-success'); - }); - } -} diff --git a/src/addon/files/providers/mainmenu-handler.ts b/src/addon/files/providers/mainmenu-handler.ts deleted file mode 100644 index acf2c811e..000000000 --- a/src/addon/files/providers/mainmenu-handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonFilesProvider } from './files'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; - -/** - * Handler to inject an option into main menu. - */ -@Injectable() -export class AddonFilesMainMenuHandler implements CoreMainMenuHandler { - name = 'AddonFiles'; - priority = 400; - - constructor(private filesProvider: AddonFilesProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.filesProvider.isPluginEnabled(); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreMainMenuHandlerData { - return { - icon: 'folder', - title: 'addon.files.files', - page: 'AddonFilesListPage', - class: 'addon-files-handler' - }; - } -} diff --git a/src/addon/filter/activitynames/activitynames.module.ts b/src/addon/filter/activitynames/activitynames.module.ts deleted file mode 100644 index 91d9e6f40..000000000 --- a/src/addon/filter/activitynames/activitynames.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterActivityNamesHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterActivityNamesHandler - ] -}) -export class AddonFilterActivityNamesModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterActivityNamesHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/activitynames/providers/handler.ts b/src/addon/filter/activitynames/providers/handler.ts deleted file mode 100644 index e82644bc1..000000000 --- a/src/addon/filter/activitynames/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the Activity names filter. - */ -@Injectable() -export class AddonFilterActivityNamesHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterActivityNamesHandler'; - filterName = 'activitynames'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/algebra/algebra.module.ts b/src/addon/filter/algebra/algebra.module.ts deleted file mode 100644 index 8b34d7afb..000000000 --- a/src/addon/filter/algebra/algebra.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterAlgebraHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterAlgebraHandler - ] -}) -export class AddonFilterAlgebraModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterAlgebraHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/algebra/providers/handler.ts b/src/addon/filter/algebra/providers/handler.ts deleted file mode 100644 index 5079ff659..000000000 --- a/src/addon/filter/algebra/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the Algebra notation filter. - */ -@Injectable() -export class AddonFilterAlgebraHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterAlgebraHandler'; - filterName = 'algebra'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/censor/censor.module.ts b/src/addon/filter/censor/censor.module.ts deleted file mode 100644 index 6d6fc5521..000000000 --- a/src/addon/filter/censor/censor.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterCensorHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterCensorHandler - ] -}) -export class AddonFilterCensorModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterCensorHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/censor/providers/handler.ts b/src/addon/filter/censor/providers/handler.ts deleted file mode 100644 index b9ce9649f..000000000 --- a/src/addon/filter/censor/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the Word censorship filter. - */ -@Injectable() -export class AddonFilterCensorHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterCensorHandler'; - filterName = 'censor'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/data/data.module.ts b/src/addon/filter/data/data.module.ts deleted file mode 100644 index e76812085..000000000 --- a/src/addon/filter/data/data.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterDataHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterDataHandler - ] -}) -export class AddonFilterDataModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterDataHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/data/providers/handler.ts b/src/addon/filter/data/providers/handler.ts deleted file mode 100644 index b0dfc9c7a..000000000 --- a/src/addon/filter/data/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the Database auto-link filter. - */ -@Injectable() -export class AddonFilterDataHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterDataHandler'; - filterName = 'data'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/displayh5p/displayh5p.module.ts b/src/addon/filter/displayh5p/displayh5p.module.ts deleted file mode 100644 index 8a7197c71..000000000 --- a/src/addon/filter/displayh5p/displayh5p.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterDisplayH5PHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterDisplayH5PHandler - ] -}) -export class AddonFilterDisplayH5PModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterDisplayH5PHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/displayh5p/providers/handler.ts b/src/addon/filter/displayh5p/providers/handler.ts deleted file mode 100644 index abbfdb957..000000000 --- a/src/addon/filter/displayh5p/providers/handler.ts +++ /dev/null @@ -1,98 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, ViewContainerRef, ComponentFactoryResolver } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFilter, CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreH5PPlayerComponent } from '@core/h5p/components/h5p-player/h5p-player'; - -/** - * Handler to support the Display H5P filter. - */ -@Injectable() -export class AddonFilterDisplayH5PHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterDisplayH5PHandler'; - filterName = 'displayh5p'; - - protected template = document.createElement('template'); // A template element to convert HTML to element. - - constructor(protected factoryResolver: ComponentFactoryResolver) { - super(); - } - - /** - * Filter some text. - * - * @param text The text to filter. - * @param filter The filter. - * @param options Options passed to the filters. - * @param siteId Site ID. If not defined, current site. - * @return Filtered text (or promise resolved with the filtered text). - */ - filter(text: string, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, siteId?: string) - : string | Promise { - - this.template.innerHTML = text; - - const h5pIframes = Array.from(this.template.content.querySelectorAll('iframe.h5p-iframe')); - - // Replace all iframes with an empty div that will be treated in handleHtml. - h5pIframes.forEach((iframe) => { - const placeholder = document.createElement('div'); - - placeholder.classList.add('core-h5p-tmp-placeholder'); - placeholder.setAttribute('data-player-src', iframe.src); - - iframe.parentElement.replaceChild(placeholder, iframe); - }); - - return this.template.innerHTML; - } - - /** - * Handle HTML. This function is called after "filter", and it will receive an HTMLElement containing the text that was - * filtered. - * - * @param container The HTML container to handle. - * @param filter The filter. - * @param options Options passed to the filters. - * @param viewContainerRef The ViewContainerRef where the container is. - * @param component Component. - * @param componentId Component ID. - * @param siteId Site ID. If not defined, current site. - * @return If async, promise resolved when done. - */ - handleHtml(container: HTMLElement, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, - viewContainerRef: ViewContainerRef, component?: string, componentId?: string | number, siteId?: string) - : void | Promise { - - const placeholders = Array.from(container.querySelectorAll('div.core-h5p-tmp-placeholder')); - - placeholders.forEach((placeholder) => { - const url = placeholder.getAttribute('data-player-src'); - - // Create the component to display the player. - const factory = this.factoryResolver.resolveComponentFactory(CoreH5PPlayerComponent), - componentRef = viewContainerRef.createComponent(factory); - - componentRef.instance.src = url; - componentRef.instance.component = component; - componentRef.instance.componentId = componentId; - - // Move the component to its right position. - placeholder.parentElement.replaceChild(componentRef.instance.elementRef.nativeElement, placeholder); - }); - } -} diff --git a/src/addon/filter/emailprotect/emailprotect.module.ts b/src/addon/filter/emailprotect/emailprotect.module.ts deleted file mode 100644 index a722980be..000000000 --- a/src/addon/filter/emailprotect/emailprotect.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterEmailProtectHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterEmailProtectHandler - ] -}) -export class AddonFilterEmailProtectModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterEmailProtectHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/emailprotect/providers/handler.ts b/src/addon/filter/emailprotect/providers/handler.ts deleted file mode 100644 index 77b431bde..000000000 --- a/src/addon/filter/emailprotect/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the Email protection filter. - */ -@Injectable() -export class AddonFilterEmailProtectHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterEmailProtectHandler'; - filterName = 'emailprotect'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/emoticon/emoticon.module.ts b/src/addon/filter/emoticon/emoticon.module.ts deleted file mode 100644 index 9b975cea4..000000000 --- a/src/addon/filter/emoticon/emoticon.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterEmoticonHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterEmoticonHandler - ] -}) -export class AddonFilterEmoticonModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterEmoticonHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/emoticon/providers/handler.ts b/src/addon/filter/emoticon/providers/handler.ts deleted file mode 100644 index 7b403231f..000000000 --- a/src/addon/filter/emoticon/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the Emoticon filter. - */ -@Injectable() -export class AddonFilterEmoticonHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterEmoticonHandler'; - filterName = 'emoticon'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/filter.module.ts b/src/addon/filter/filter.module.ts deleted file mode 100644 index 29ab01243..000000000 --- a/src/addon/filter/filter.module.ts +++ /dev/null @@ -1,53 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonFilterActivityNamesModule } from './activitynames/activitynames.module'; -import { AddonFilterAlgebraModule } from './algebra/algebra.module'; -import { AddonFilterCensorModule } from './censor/censor.module'; -import { AddonFilterDataModule } from './data/data.module'; -import { AddonFilterDisplayH5PModule } from './displayh5p/displayh5p.module'; -import { AddonFilterEmailProtectModule } from './emailprotect/emailprotect.module'; -import { AddonFilterEmoticonModule } from './emoticon/emoticon.module'; -import { AddonFilterGlossaryModule } from './glossary/glossary.module'; -import { AddonFilterMathJaxLoaderModule } from './mathjaxloader/mathjaxloader.module'; -import { AddonFilterMediaPluginModule } from './mediaplugin/mediaplugin.module'; -import { AddonFilterMultilangModule } from './multilang/multilang.module'; -import { AddonFilterTexModule } from './tex/tex.module'; -import { AddonFilterTidyModule } from './tidy/tidy.module'; -import { AddonFilterUrlToLinkModule } from './urltolink/urltolink.module'; - -@NgModule({ - declarations: [], - imports: [ - AddonFilterActivityNamesModule, - AddonFilterAlgebraModule, - AddonFilterCensorModule, - AddonFilterDataModule, - AddonFilterDisplayH5PModule, - AddonFilterEmailProtectModule, - AddonFilterEmoticonModule, - AddonFilterGlossaryModule, - AddonFilterMathJaxLoaderModule, - AddonFilterMediaPluginModule, - AddonFilterMultilangModule, - AddonFilterTexModule, - AddonFilterTidyModule, - AddonFilterUrlToLinkModule - ], - providers: [ - ], - exports: [] -}) -export class AddonFilterModule { } diff --git a/src/addon/filter/glossary/glossary.module.ts b/src/addon/filter/glossary/glossary.module.ts deleted file mode 100644 index a5bae30c8..000000000 --- a/src/addon/filter/glossary/glossary.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterGlossaryHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterGlossaryHandler - ] -}) -export class AddonFilterGlossaryModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterGlossaryHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/glossary/providers/handler.ts b/src/addon/filter/glossary/providers/handler.ts deleted file mode 100644 index c89489b12..000000000 --- a/src/addon/filter/glossary/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the Glossary auto-link filter. - */ -@Injectable() -export class AddonFilterGlossaryHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterGlossaryHandler'; - filterName = 'glossary'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/mathjaxloader/mathjaxloader.module.ts b/src/addon/filter/mathjaxloader/mathjaxloader.module.ts deleted file mode 100644 index 5d78641ca..000000000 --- a/src/addon/filter/mathjaxloader/mathjaxloader.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterMathJaxLoaderHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterMathJaxLoaderHandler - ] -}) -export class AddonFilterMathJaxLoaderModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterMathJaxLoaderHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/mathjaxloader/providers/handler.ts b/src/addon/filter/mathjaxloader/providers/handler.ts deleted file mode 100644 index beb3a1cb0..000000000 --- a/src/addon/filter/mathjaxloader/providers/handler.ts +++ /dev/null @@ -1,394 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, ViewContainerRef } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFilter, CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLangProvider } from '@providers/lang'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the MathJax filter. - */ -@Injectable() -export class AddonFilterMathJaxLoaderHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterMathJaxLoaderHandler'; - filterName = 'mathjaxloader'; - - // Default values for MathJax config for sites where we cannot retrieve it. - protected DEFAULT_URL = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js'; - protected DEFAULT_CONFIG = ` - MathJax.Hub.Config({ - extensions: [ - "Safe.js", - "tex2jax.js", - "mml2jax.js", - "MathEvents.js", - "MathZoom.js", - "MathMenu.js", - "toMathML.js", - "TeX/noErrors.js", - "TeX/noUndefined.js", - "TeX/AMSmath.js", - "TeX/AMSsymbols.js", - "fast-preview.js", - "AssistiveMML.js", - "[a11y]/accessibility-menu.js" - ], - jax: ["input/TeX","input/MathML","output/SVG"], - showMathMenu: false, - errorSettings: { message: ["!"] }, - skipStartupTypeset: true, - messageStyle: "none" - }); - `; - - // List of language codes found in the MathJax/localization/ directory. - protected MATHJAX_LANG_CODES = [ - 'ar', 'ast', 'bcc', 'bg', 'br', 'ca', 'cdo', 'ce', 'cs', 'cy', 'da', 'de', 'diq', 'en', 'eo', 'es', 'fa', - 'fi', 'fr', 'gl', 'he', 'ia', 'it', 'ja', 'kn', 'ko', 'lb', 'lki', 'lt', 'mk', 'nl', 'oc', 'pl', 'pt', - 'pt-br', 'qqq', 'ru', 'scn', 'sco', 'sk', 'sl', 'sv', 'th', 'tr', 'uk', 'vi', 'zh-hans', 'zh-hant' - ]; - - // List of explicit mappings and known exceptions (moodle => mathjax). - protected EXPLICIT_MAPPING = { - 'zh-tw': 'zh-hant', - 'zh-cn': 'zh-hans', - }; - - protected window: any = window; // Convert the window to to be able to use non-standard properties like MathJax. - - constructor(eventsProvider: CoreEventsProvider, - private langProvider: CoreLangProvider, - private sitesProvider: CoreSitesProvider, - private textUtils: CoreTextUtilsProvider, - private utils: CoreUtilsProvider) { - super(); - - // Load the JS. - this.loadJS(); - - // Get the current language. - this.langProvider.getCurrentLanguage().then((lang) => { - lang = this.mapLanguageCode(lang); - - // Now call the configure function. - this.window.M.filter_mathjaxloader.configure({ - mathjaxconfig: this.DEFAULT_CONFIG, - lang: lang - }); - }); - - // Update MathJax locale if app language changes. - eventsProvider.on(CoreEventsProvider.LANGUAGE_CHANGED, (lang) => { - if (typeof this.window.MathJax != 'undefined') { - lang = this.mapLanguageCode(lang); - - this.window.MathJax.Hub.Queue(() => { - this.window.MathJax.Localization.setLocale(lang); - }); - } - }); - } - - /** - * Filter some text. - * - * @param text The text to filter. - * @param filter The filter. - * @param options Options passed to the filters. - * @param siteId Site ID. If not defined, current site. - * @return Filtered text (or promise resolved with the filtered text). - */ - filter(text: string, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, siteId?: string) - : string | Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - // Don't apply this filter if Moodle is 3.7 or higher and the WS already filtered the content. - if (!options.wsNotFiltered && site.isVersionGreaterEqualThan('3.7')) { - return text; - } - - if (text.indexOf('class="filter_mathjaxloader_equation"') != -1) { - // The content seems to have treated mathjax already, don't do it. - return text; - } - - // We cannot get the filter settings, so we cannot know if it can be used as a replacement for the TeX filter. - // Assume it cannot (default value). - let hasDisplayOrInline = false; - if (text.match(/\\[\[\(]/) || text.match(/\$\$/)) { - // Only parse the text if there are mathjax symbols in it. - // The recognized math environments are \[ \] and $$ $$ for display mathematics and \( \) for inline mathematics. - // Wrap display and inline math environments in nolink spans. - const result = this.wrapMathInNoLink(text); - text = result.text; - hasDisplayOrInline = result.changed; - } - - if (hasDisplayOrInline) { - return '' + text + ''; - } - - return text; - }); - } - - /** - * Handle HTML. This function is called after "filter", and it will receive an HTMLElement containing the text that was - * filtered. - * - * @param container The HTML container to handle. - * @param filter The filter. - * @param options Options passed to the filters. - * @param viewContainerRef The ViewContainerRef where the container is. - * @param component Component. - * @param componentId Component ID. - * @param siteId Site ID. If not defined, current site. - * @return If async, promise resolved when done. - */ - handleHtml(container: HTMLElement, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, - viewContainerRef: ViewContainerRef, component?: string, componentId?: string | number, siteId?: string) - : void | Promise { - - return this.waitForReady().then(() => { - this.window.M.filter_mathjaxloader.typeset(container); - }); - } - - /** - * Wrap a portion of the $text inside a no link span. The whole text is then returned. - * - * @param text The text to modify. - * @param start The start index of the substring in text that should be wrapped in the span. - * @param end The end index of the substring in text that should be wrapped in the span. - * @return The whole text with the span inserted around the defined substring. - */ - protected insertSpan(text: string, start: number, end: number): string { - return this.textUtils.substrReplace(text, - '' + text.substr(start, end - start + 1) + '', - start, - end - start + 1); - } - - /** - * Check if the JS library has been loaded. - * - * @return Whether the library has been loaded. - */ - protected jsLoaded(): boolean { - return this.window.M && this.window.M.filter_mathjaxloader; - } - - /** - * Load the JS to make MathJax work in the app. The JS loaded is extracted from Moodle filter's loader JS file. - */ - protected loadJS(): void { - // tslint:disable: no-this-assignment - const that = this; - - this.window.M = this.window.M || {}; - this.window.M.filter_mathjaxloader = this.window.M.filter_mathjaxloader || { - _lang: '', - _configured: false, - // Add the configuration to the head and set the lang. - configure: function (params: any): void { - // Add a js configuration object to the head. - const script = document.createElement('script'); - script.type = 'text/x-mathjax-config'; - script.text = params.mathjaxconfig; - document.head.appendChild(script); - - // Save the lang config until MathJax is actually loaded. - this._lang = params.lang; - }, - // Set the correct language for the MathJax menus. - _setLocale: function (): void { - if (!this._configured) { - const lang = this._lang; - - if (typeof that.window.MathJax != 'undefined') { - that.window.MathJax.Hub.Queue(() => { - that.window.MathJax.Localization.setLocale(lang); - }); - that.window.MathJax.Hub.Configured(); - this._configured = true; - } - } - }, - // Called by the filter when an equation is found while rendering the page. - typeset: function (container: HTMLElement): void { - if (!this._configured) { - this._setLocale(); - } - - if (typeof that.window.MathJax != 'undefined') { - const processDelay = that.window.MathJax.Hub.processSectionDelay; - // Set the process section delay to 0 when updating the formula. - that.window.MathJax.Hub.processSectionDelay = 0; - - const equations = Array.from(container.querySelectorAll('.filter_mathjaxloader_equation')); - equations.forEach((node) => { - that.window.MathJax.Hub.Queue(['Typeset', that.window.MathJax.Hub, node]); - }); - - // Set the delay back to normal after processing. - that.window.MathJax.Hub.processSectionDelay = processDelay; - } - } - }; - } - - /** - * Perform a mapping of the app language code to the equivalent for MathJax. - * - * @param langCode The app language code. - * @return The MathJax language code. - */ - protected mapLanguageCode(langCode: string): string { - - // If defined, explicit mapping takes the highest precedence. - if (this.EXPLICIT_MAPPING[langCode]) { - return this.EXPLICIT_MAPPING[langCode]; - } - - // If there is exact match, it will be probably right. - if (this.MATHJAX_LANG_CODES.indexOf(langCode) != -1) { - return langCode; - } - - // Finally try to find the best matching mathjax pack. - const parts = langCode.split('-'); - if (this.MATHJAX_LANG_CODES.indexOf(parts[0]) != -1) { - return parts[0]; - } - - // No more guessing, use default language. - return this.langProvider.getDefaultLanguage(); - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - // Only apply the filter if logged in and we're filtering current site. - return site && site.getId() == this.sitesProvider.getCurrentSiteId(); - } - - /** - * Wait for the MathJax library and our JS object to be loaded. - * - * @param retries Number of times this has been retried. - * @return Promise resolved when ready or if it took too long to load. - */ - protected waitForReady(retries: number = 0): Promise { - if ((this.window.MathJax && this.jsLoaded()) || retries >= 20) { - // Loaded or too many retries, stop. - return Promise.resolve(); - } - - const deferred = this.utils.promiseDefer(); - - setTimeout(() => { - return this.waitForReady(retries + 1).finally(() => { - deferred.resolve(); - }); - }, 250); - - return deferred.promise; - } - - /** - * Find math environments in the $text and wrap them in no link spans - * (). If math environments are nested, only - * the outer environment is wrapped in the span. - * - * The recognized math environments are \[ \] and $$ $$ for display - * mathematics and \( \) for inline mathematics. - * - * @param text The text to filter. - * @return Object containing the potentially modified text and a boolean that is true if any changes were made to the text. - */ - protected wrapMathInNoLink(text: string): {text: string, changed: boolean} { - let len = text.length, - i = 1, - displayStart = -1, - displayBracket = false, - displayDollar = false, - inlineStart = -1, - changesDone = false; - - // Loop over the $text once. - while (i < len) { - if (displayStart === -1) { - // No display math has started yet. - if (text[i - 1] === '\\') { - - if (text[i] === '[') { - // Display mode \[ begins. - displayStart = i - 1; - displayBracket = true; - } else if (text[i] === '(') { - // Inline math \( begins, not nested inside display math. - inlineStart = i - 1; - } else if (text[i] === ')' && inlineStart > -1) { - // Inline math ends, not nested inside display math. Wrap the span around it. - text = this.insertSpan(text, inlineStart, i); - - inlineStart = -1; // Reset. - i += 28; // The text length changed due to the . - len += 28; - changesDone = true; - } - - } else if (text[i - 1] === '$' && text[i] === '$') { - // Display mode $$ begins. - displayStart = i - 1; - displayDollar = true; - } - - } else { - // Display math open. - if ((text[i - 1] === '\\' && text[i] === ']' && displayBracket) || - (text[i - 1] === '$' && text[i] === '$' && displayDollar)) { - // Display math ends, wrap the span around it. - text = this.insertSpan(text, displayStart, i); - - displayStart = -1; // Reset. - displayBracket = false; - displayDollar = false; - i += 28; // The text length changed due to the . - len += 28; - changesDone = true; - } - } - - i++; - } - - return { - text: text, - changed: changesDone - }; - } -} diff --git a/src/addon/filter/mediaplugin/mediaplugin.module.ts b/src/addon/filter/mediaplugin/mediaplugin.module.ts deleted file mode 100644 index c29931c91..000000000 --- a/src/addon/filter/mediaplugin/mediaplugin.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterMediaPluginHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterMediaPluginHandler - ] -}) -export class AddonFilterMediaPluginModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterMediaPluginHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/mediaplugin/providers/handler.ts b/src/addon/filter/mediaplugin/providers/handler.ts deleted file mode 100644 index e1c5a93fc..000000000 --- a/src/addon/filter/mediaplugin/providers/handler.ts +++ /dev/null @@ -1,91 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFilter, CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; - -/** - * Handler to support the Multimedia filter. - */ -@Injectable() -export class AddonFilterMediaPluginHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterMediaPluginHandler'; - filterName = 'mediaplugin'; - - protected template = document.createElement('template'); // A template element to convert HTML to element. - - constructor(private textUtils: CoreTextUtilsProvider, - private urlUtils: CoreUrlUtilsProvider) { - super(); - } - - /** - * Filter some text. - * - * @param text The text to filter. - * @param filter The filter. - * @param options Options passed to the filters. - * @param siteId Site ID. If not defined, current site. - * @return Filtered text (or promise resolved with the filtered text). - */ - filter(text: string, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, siteId?: string) - : string | Promise { - - this.template.innerHTML = text; - - const videos = Array.from(this.template.content.querySelectorAll('video')); - - videos.forEach((video) => { - this.treatVideoFilters(video); - }); - - return this.template.innerHTML; - } - - /** - * Treat video filters. Currently only treating youtube video using video JS. - * - * @param el Video element. - * @param navCtrl NavController to use. - */ - protected treatVideoFilters(video: HTMLElement): void { - // Treat Video JS Youtube video links and translate them to iframes. - if (!video.classList.contains('video-js')) { - return; - } - - const data = this.textUtils.parseJSON(video.getAttribute('data-setup') || video.getAttribute('data-setup-lazy') || '{}'), - youtubeUrl = data.techOrder && data.techOrder[0] && data.techOrder[0] == 'youtube' && - this.urlUtils.getYoutubeEmbedUrl(data.sources && data.sources[0] && data.sources[0].src); - - if (!youtubeUrl) { - return; - } - - const iframe = document.createElement('iframe'); - iframe.id = video.id; - iframe.src = youtubeUrl; - iframe.setAttribute('frameborder', '0'); - iframe.setAttribute('allowfullscreen', '1'); - iframe.width = '100%'; - iframe.height = '300'; - - // Replace video tag by the iframe. - video.parentNode.replaceChild(iframe, video); - } -} diff --git a/src/addon/filter/multilang/multilang.module.ts b/src/addon/filter/multilang/multilang.module.ts deleted file mode 100644 index db8328d2b..000000000 --- a/src/addon/filter/multilang/multilang.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterMultilangHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterMultilangHandler - ] -}) -export class AddonFilterMultilangModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterMultilangHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/multilang/providers/handler.ts b/src/addon/filter/multilang/providers/handler.ts deleted file mode 100644 index 229182791..000000000 --- a/src/addon/filter/multilang/providers/handler.ts +++ /dev/null @@ -1,88 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFilter, CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreLangProvider } from '@providers/lang'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the Multilang filter. - */ -@Injectable() -export class AddonFilterMultilangHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterMultilangHandler'; - filterName = 'multilang'; - - constructor(private langProvider: CoreLangProvider, - private sitesProvider: CoreSitesProvider) { - super(); - } - - /** - * Filter some text. - * - * @param text The text to filter. - * @param filter The filter. - * @param options Options passed to the filters. - * @param siteId Site ID. If not defined, current site. - * @return Filtered text (or promise resolved with the filtered text). - */ - filter(text: string, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, siteId?: string) - : string | Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - return this.langProvider.getCurrentLanguage().then((language) => { - // Match the current language. - const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g; - let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g'); - - if (!text.match(currentLangRegEx)) { - // Current lang not found. Try to find the first language. - const matches = text.match(anyLangRegEx); - if (matches && matches[0]) { - language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)[1]; - currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', - 'g'); - } else { - // No multi-lang tag found, stop. - return text; - } - } - // Extract contents of current language. - text = text.replace(currentLangRegEx, '$1'); - // Delete the rest of languages - text = text.replace(anyLangRegEx, ''); - - return text; - }); - }); - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - // The filter should be applied if site is older than 3.7 or the WS didn't filter the text. - return options.wsNotFiltered || (site && !site.isVersionGreaterEqualThan('3.7')); - } -} diff --git a/src/addon/filter/tex/providers/handler.ts b/src/addon/filter/tex/providers/handler.ts deleted file mode 100644 index 313f13a9f..000000000 --- a/src/addon/filter/tex/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the TeX notation filter. - */ -@Injectable() -export class AddonFilterTexHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterTexHandler'; - filterName = 'tex'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/tex/tex.module.ts b/src/addon/filter/tex/tex.module.ts deleted file mode 100644 index 2fecf37d7..000000000 --- a/src/addon/filter/tex/tex.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterTexHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterTexHandler - ] -}) -export class AddonFilterTexModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterTexHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/tidy/providers/handler.ts b/src/addon/filter/tidy/providers/handler.ts deleted file mode 100644 index d1a3f608a..000000000 --- a/src/addon/filter/tidy/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the HTML tidy filter. - */ -@Injectable() -export class AddonFilterTidyHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterTidyHandler'; - filterName = 'tidy'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/tidy/tidy.module.ts b/src/addon/filter/tidy/tidy.module.ts deleted file mode 100644 index 54782656c..000000000 --- a/src/addon/filter/tidy/tidy.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterTidyHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterTidyHandler - ] -}) -export class AddonFilterTidyModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterTidyHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/filter/urltolink/providers/handler.ts b/src/addon/filter/urltolink/providers/handler.ts deleted file mode 100644 index cc1aa05cc..000000000 --- a/src/addon/filter/urltolink/providers/handler.ts +++ /dev/null @@ -1,45 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter'; -import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter'; -import { CoreSite } from '@classes/site'; - -/** - * Handler to support the URL to link and images filter. - */ -@Injectable() -export class AddonFilterUrlToLinkHandler extends CoreFilterDefaultHandler { - name = 'AddonFilterUrlToLinkHandler'; - filterName = 'urltolink'; - - constructor() { - super(); - - // This filter is handled by Moodle, nothing to do in the app. - } - - /** - * Check if the filter should be applied in a certain site based on some filter options. - * - * @param options Options. - * @param site Site. - * @return Whether filter should be applied. - */ - shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean { - return false; - } -} diff --git a/src/addon/filter/urltolink/urltolink.module.ts b/src/addon/filter/urltolink/urltolink.module.ts deleted file mode 100644 index 7316a849a..000000000 --- a/src/addon/filter/urltolink/urltolink.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreFilterDelegate } from '@core/filter/providers/delegate'; -import { AddonFilterUrlToLinkHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - IonicModule - ], - providers: [ - AddonFilterUrlToLinkHandler - ] -}) -export class AddonFilterUrlToLinkModule { - constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterUrlToLinkHandler) { - filterDelegate.registerHandler(handler); - } -} diff --git a/src/addon/messageoutput/airnotifier/airnotifier.module.ts b/src/addon/messageoutput/airnotifier/airnotifier.module.ts deleted file mode 100644 index b8a3b3ca6..000000000 --- a/src/addon/messageoutput/airnotifier/airnotifier.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonMessageOutputDelegate } from '@addon/messageoutput/providers/delegate'; -import { AddonMessageOutputAirnotifierProvider } from './providers/airnotifier'; -import { AddonMessageOutputAirnotifierHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - imports: [ - ], - providers: [ - AddonMessageOutputAirnotifierProvider, - AddonMessageOutputAirnotifierHandler, - ] -}) -export class AddonMessageOutputAirnotifierModule { - constructor(messageOutputDelegate: AddonMessageOutputDelegate, airnotifierHandler: AddonMessageOutputAirnotifierHandler) { - messageOutputDelegate.registerHandler(airnotifierHandler); - } -} diff --git a/src/addon/messageoutput/airnotifier/lang/en.json b/src/addon/messageoutput/airnotifier/lang/en.json deleted file mode 100644 index a6f460bbb..000000000 --- a/src/addon/messageoutput/airnotifier/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "processorsettingsdesc": "Configure devices" -} \ No newline at end of file diff --git a/src/addon/messageoutput/airnotifier/pages/devices/devices.html b/src/addon/messageoutput/airnotifier/pages/devices/devices.html deleted file mode 100644 index 24434141b..000000000 --- a/src/addon/messageoutput/airnotifier/pages/devices/devices.html +++ /dev/null @@ -1,22 +0,0 @@ - - - {{ 'addon.messageoutput_airnotifier.processorsettingsdesc' | translate }} - - - - - - - - - - - {{ device.name }} {{ device.model }} {{ device.platform }} {{ device.version }} - ({{ 'core.currentdevice' | translate }}) - - - - - - - diff --git a/src/addon/messageoutput/airnotifier/pages/devices/devices.module.ts b/src/addon/messageoutput/airnotifier/pages/devices/devices.module.ts deleted file mode 100644 index 4f8239e39..000000000 --- a/src/addon/messageoutput/airnotifier/pages/devices/devices.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonMessageOutputAirnotifierDevicesPage } from './devices'; - -@NgModule({ - declarations: [ - AddonMessageOutputAirnotifierDevicesPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonMessageOutputAirnotifierDevicesPage), - TranslateModule.forChild() - ], -}) -export class AddonMessageOutputAirnotifierDevicesPageModule {} diff --git a/src/addon/messageoutput/airnotifier/pages/devices/devices.ts b/src/addon/messageoutput/airnotifier/pages/devices/devices.ts deleted file mode 100644 index 11b4f4cc6..000000000 --- a/src/addon/messageoutput/airnotifier/pages/devices/devices.ts +++ /dev/null @@ -1,146 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy } from '@angular/core'; -import { IonicPage } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; -import { AddonMessageOutputAirnotifierProvider, AddonMessageOutputAirnotifierDevice } from '../../providers/airnotifier'; - -/** - * Page that displays the list of devices. - */ -@IonicPage({ segment: 'addon-message-output-airnotifier-devices' }) -@Component({ - selector: 'page-addon-message-output-airnotifier-devices', - templateUrl: 'devices.html', -}) -export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy { - - devices: AddonMessageOutputAirnotifierDeviceFormatted[] = []; - devicesLoaded = false; - - protected updateTimeout: any; - - constructor(private domUtils: CoreDomUtilsProvider, private airnotifierProivder: AddonMessageOutputAirnotifierProvider, - private pushNotificationsProvider: CorePushNotificationsProvider ) { - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchDevices(); - } - - /** - * Fetches the list of devices. - * - * @return Promise resolved when done. - */ - protected fetchDevices(): Promise { - return this.airnotifierProivder.getUserDevices().then((devices) => { - const pushId = this.pushNotificationsProvider.getPushId(); - - // Convert enabled to boolean and search current device. - devices.forEach((device: AddonMessageOutputAirnotifierDeviceFormatted) => { - device.enable = !!device.enable; - device.current = pushId && pushId == device.pushid; - }); - - this.devices = devices; - }).catch((message) => { - this.domUtils.showErrorModal(message); - }).finally(() => { - this.devicesLoaded = true; - }); - } - - /** - * Update list of devices after a certain time. The purpose is to store the updated data, it won't be reflected in the view. - */ - protected updateDevicesAfterDelay(): void { - // Cancel pending updates. - if (this.updateTimeout) { - clearTimeout(this.updateTimeout); - } - - this.updateTimeout = setTimeout(() => { - this.updateTimeout = null; - this.updateDevices(); - }, 5000); - } - - /** - * Fetch devices. The purpose is to store the updated data, it won't be reflected in the view. - */ - protected updateDevices(): void { - this.airnotifierProivder.invalidateUserDevices().finally(() => { - this.airnotifierProivder.getUserDevices(); - }); - } - - /** - * Refresh the list of devices. - * - * @param refresher Refresher. - */ - refreshDevices(refresher: any): void { - this.airnotifierProivder.invalidateUserDevices().finally(() => { - this.fetchDevices().finally(() => { - refresher.complete(); - }); - }); - } - - /** - * Enable or disable a certain device. - * - * @param device The device object. - * @param enable True to enable the device, false to disable it. - */ - enableDevice(device: AddonMessageOutputAirnotifierDeviceFormatted, enable: boolean): void { - device.updating = true; - - this.airnotifierProivder.enableDevice(device.id, enable).then(() => { - // Update the list of devices since it was modified. - this.updateDevicesAfterDelay(); - }).catch((message) => { - // Show error and revert change. - this.domUtils.showErrorModal(message); - device.enable = !device.enable; - }).finally(() => { - device.updating = false; - }); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - // If there is a pending action to update devices, execute it right now. - if (this.updateTimeout) { - clearTimeout(this.updateTimeout); - this.updateDevices(); - } - } -} - -/** - * User device with some calculated data. - */ -type AddonMessageOutputAirnotifierDeviceFormatted = AddonMessageOutputAirnotifierDevice & { - current?: boolean; // Calculated in the app. Whether it's the current device. - updating?: boolean; // Calculated in the app. Whether the device enable is being updated right now. -}; diff --git a/src/addon/messageoutput/airnotifier/providers/airnotifier.ts b/src/addon/messageoutput/airnotifier/providers/airnotifier.ts deleted file mode 100644 index 01d429774..000000000 --- a/src/addon/messageoutput/airnotifier/providers/airnotifier.ts +++ /dev/null @@ -1,154 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreConfigConstants } from '../../../../configconstants'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning } from '@providers/ws'; - -/** - * Service to handle Airnotifier message output. - */ -@Injectable() -export class AddonMessageOutputAirnotifierProvider { - - protected ROOT_CACHE_KEY = 'mmaMessageOutputAirnotifier:'; - protected logger: any; - - constructor(loggerProvider: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) { - this.logger = loggerProvider.getInstance('AddonMessageOutputAirnotifier'); - } - - /** - * Enables or disables a device. - * - * @param deviceId Device ID. - * @param enable True to enable, false to disable. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success. - */ - enableDevice(deviceId: number, enable: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - deviceid: deviceId, - enable: enable ? 1 : 0 - }; - - return site.write('message_airnotifier_enable_device', data) - .then((result: AddonMessageOutputAirnotifierEnableDeviceResult) => { - - if (!result.success) { - // Fail. Reject with warning message if any. - if (result.warnings && result.warnings.length) { - return Promise.reject(result.warnings[0].message); - } - - return Promise.reject(null); - } - }); - }); - } - - /** - * Get the cache key for the get user devices call. - * - * @return Cache key. - */ - protected getUserDevicesCacheKey(): string { - return this.ROOT_CACHE_KEY + 'userDevices'; - } - - /** - * Get user devices. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the devices. - */ - getUserDevices(siteId?: string): Promise { - this.logger.debug('Get user devices'); - - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - appid: CoreConfigConstants.app_id - }; - const preSets = { - cacheKey: this.getUserDevicesCacheKey(), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - return site.read('message_airnotifier_get_user_devices', data, preSets) - .then((data: AddonMessageOutputAirnotifierGetUserDevicesResult) => { - return data.devices; - }); - }); - } - - /** - * Invalidate get user devices. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateUserDevices(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getUserDevicesCacheKey()); - }); - } - - /** - * Returns whether or not the plugin is enabled for the current site. - * - * @return True if enabled, false otherwise. - * @since 3.2 - */ - isEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('message_airnotifier_enable_device') && - this.sitesProvider.wsAvailableInCurrentSite('message_airnotifier_get_user_devices'); - } -} - -/** - * Device data returned by WS message_airnotifier_get_user_devices. - */ -export type AddonMessageOutputAirnotifierDevice = { - id: number; // Device id (in the message_airnotifier table). - appid: string; // The app id, something like com.moodle.moodlemobile. - name: string; // The device name, 'occam' or 'iPhone' etc. - model: string; // The device model 'Nexus4' or 'iPad1,1' etc. - platform: string; // The device platform 'iOS' or 'Android' etc. - version: string; // The device version '6.1.2' or '4.2.2' etc. - pushid: string; // The device PUSH token/key/identifier/registration id. - uuid: string; // The device UUID. - enable: number | boolean; // Whether the device is enabled or not. - timecreated: number; // Time created. - timemodified: number; // Time modified. -}; - -/** - * Result of WS message_airnotifier_enable_device. - */ -export type AddonMessageOutputAirnotifierEnableDeviceResult = { - success: boolean; // True if success. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS message_airnotifier_get_user_devices. - */ -export type AddonMessageOutputAirnotifierGetUserDevicesResult = { - devices: AddonMessageOutputAirnotifierDevice[]; // List of devices. - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/messageoutput/airnotifier/providers/handler.ts b/src/addon/messageoutput/airnotifier/providers/handler.ts deleted file mode 100644 index 7d8d10007..000000000 --- a/src/addon/messageoutput/airnotifier/providers/handler.ts +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonMessageOutputHandler, AddonMessageOutputHandlerData } from '@addon/messageoutput/providers/delegate'; -import { AddonMessageOutputAirnotifierProvider } from './airnotifier'; - -/** - * Airnotifier message output handler. - */ -@Injectable() -export class AddonMessageOutputAirnotifierHandler implements AddonMessageOutputHandler { - name = 'AddonMessageOutputAirnotifier'; - processorName = 'airnotifier'; - - constructor(private airnotifierProvider: AddonMessageOutputAirnotifierProvider) {} - - /** - * Whether or not the module is enabled for the site. - * - * @return True if enabled, false otherwise. - */ - isEnabled(): boolean { - return this.airnotifierProvider.isEnabled(); - } - - /** - * Returns the data needed to render the handler. - * - * @param processor The processor object. - * @return Data. - */ - getDisplayData(processor: any): AddonMessageOutputHandlerData { - return { - priority: 600, - label: 'addon.messageoutput_airnotifier.processorsettingsdesc', - icon: 'settings', - page: 'AddonMessageOutputAirnotifierDevicesPage', - }; - } -} diff --git a/src/addon/messageoutput/messageoutput.module.ts b/src/addon/messageoutput/messageoutput.module.ts deleted file mode 100644 index a2cc6e96f..000000000 --- a/src/addon/messageoutput/messageoutput.module.ts +++ /dev/null @@ -1,32 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonMessageOutputDelegate } from './providers/delegate'; - -// List of providers (without handlers). -export const ADDON_MESSAGEOUTPUT_PROVIDERS: any[] = [ - AddonMessageOutputDelegate -]; - -@NgModule({ - declarations: [ - ], - imports: [ - ], - providers: [ - AddonMessageOutputDelegate - ] -}) -export class AddonMessageOutputModule {} diff --git a/src/addon/messageoutput/providers/delegate.ts b/src/addon/messageoutput/providers/delegate.ts deleted file mode 100644 index d7251448d..000000000 --- a/src/addon/messageoutput/providers/delegate.ts +++ /dev/null @@ -1,91 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; - -/** - * Interface that all message output handlers must implement. - */ -export interface AddonMessageOutputHandler extends CoreDelegateHandler { - /** - * The name of the processor. E.g. 'airnotifier'. - */ - processorName: string; - - /** - * Returns the data needed to render the handler. - * - * @param processor The processor object. - * @return Data. - */ - getDisplayData(processor: any): AddonMessageOutputHandlerData; -} - -/** - * Data needed to render a message output handler. It's returned by the handler. - */ -export interface AddonMessageOutputHandlerData { - /** - * Handler's priority. - */ - priority: number; - - /** - * Name of the page to load for the handler. - */ - page: string; - - /** - * Label to display for the handler. - */ - label: string; - - /** - * Name of the icon to display for the handler. - */ - icon: string; - - /** - * Params to pass to the page. - */ - pageParams?: any; -} - -/** - * Delegate to register processors (message/output) to be used in places like notification preferences. - */ - @Injectable() - export class AddonMessageOutputDelegate extends CoreDelegate { - - protected handlerNameProperty = 'processorName'; - - constructor(protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, - protected eventsProvider: CoreEventsProvider) { - super('AddonMessageOutputDelegate', loggerProvider, sitesProvider, eventsProvider); - } - - /** - * Get the display data of the handler. - * - * @param processor The processor object. - * @return Data. - */ - getDisplayData(processor: any): AddonMessageOutputHandlerData { - return this.executeFunctionOnEnabled(processor.name, 'getDisplayData', processor); - } -} diff --git a/src/addon/messages/components/components.module.ts b/src/addon/messages/components/components.module.ts deleted file mode 100644 index 3e27e38bb..000000000 --- a/src/addon/messages/components/components.module.ts +++ /dev/null @@ -1,53 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreSearchComponentsModule } from '@core/search/components/components.module'; -import { AddonMessagesDiscussionsComponent } from '../components/discussions/discussions'; -import { AddonMessagesConfirmedContactsComponent } from '../components/confirmed-contacts/confirmed-contacts'; -import { AddonMessagesContactRequestsComponent } from '../components/contact-requests/contact-requests'; -import { AddonMessagesContactsComponent } from '../components/contacts/contacts'; - -@NgModule({ - declarations: [ - AddonMessagesDiscussionsComponent, - AddonMessagesConfirmedContactsComponent, - AddonMessagesContactRequestsComponent, - AddonMessagesContactsComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreSearchComponentsModule, - ], - providers: [ - ], - exports: [ - AddonMessagesDiscussionsComponent, - AddonMessagesConfirmedContactsComponent, - AddonMessagesContactRequestsComponent, - AddonMessagesContactsComponent - ] -}) -export class AddonMessagesComponentsModule {} diff --git a/src/addon/messages/components/confirmed-contacts/addon-messages-confirmed-contacts.html b/src/addon/messages/components/confirmed-contacts/addon-messages-confirmed-contacts.html deleted file mode 100644 index 37e628570..000000000 --- a/src/addon/messages/components/confirmed-contacts/addon-messages-confirmed-contacts.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - -

- {{ contact.fullname }} - -

-
-
- - -
-
diff --git a/src/addon/messages/components/confirmed-contacts/confirmed-contacts.ts b/src/addon/messages/components/confirmed-contacts/confirmed-contacts.ts deleted file mode 100644 index 251d46e32..000000000 --- a/src/addon/messages/components/confirmed-contacts/confirmed-contacts.ts +++ /dev/null @@ -1,154 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; -import { Content } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonMessagesProvider, AddonMessagesConversationMember } from '../../providers/messages'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Component that displays the list of confirmed contacts. - */ -@Component({ - selector: 'addon-messages-confirmed-contacts', - templateUrl: 'addon-messages-confirmed-contacts.html', -}) -export class AddonMessagesConfirmedContactsComponent implements OnInit, OnDestroy { - @Output() onUserSelected = new EventEmitter<{userId: number, onInit?: boolean}>(); - @ViewChild(Content) content: Content; - - loaded = false; - canLoadMore = false; - loadMoreError = false; - contacts: AddonMessagesConversationMember[] = []; - selectedUserId: number; - - protected memberInfoObserver; - - constructor(private domUtils: CoreDomUtilsProvider, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, - private messagesProvider: AddonMessagesProvider) { - - this.onUserSelected = new EventEmitter(); - - // Update block status of a user. - this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => { - if (data.userBlocked || data.userUnblocked) { - const user = this.contacts.find((user) => user.id == data.userId); - if (user) { - user.isblocked = data.userBlocked; - } - } else if (data.contactRemoved) { - const index = this.contacts.findIndex((contact) => contact.id == data.userId); - if (index >= 0) { - this.contacts.splice(index, 1); - } - } else if (data.contactRequestConfirmed) { - this.refreshData(); - } - }, sitesProvider.getCurrentSiteId()); - } - - /** - * Component loaded. - */ - ngOnInit(): void { - this.fetchData().then(() => { - if (this.contacts.length) { - this.selectUser(this.contacts[0].id, true); - } - }).finally(() => { - this.loaded = true; - }); - - // Workaround for infinite scrolling. - this.content.resize(); - } - - /** - * Fetch contacts. - * - * @param refresh True if we are refreshing contacts, false if we are loading more. - * @return Promise resolved when done. - */ - fetchData(refresh: boolean = false): Promise { - this.loadMoreError = false; - - const limitFrom = refresh ? 0 : this.contacts.length; - let promise; - - if (limitFrom === 0) { - // Always try to get latest data from server. - promise = this.messagesProvider.invalidateUserContacts().catch(() => { - // Shouldn't happen. - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - return this.messagesProvider.getUserContacts(limitFrom); - }).then((result) => { - this.contacts = refresh ? result.contacts : this.contacts.concat(result.contacts); - this.canLoadMore = result.canLoadMore; - }).catch((error) => { - this.loadMoreError = true; - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); - }); - } - - /** - * Refresh contacts. - * - * @param refresher Refresher. - * @return Promise resolved when done. - */ - refreshData(refresher?: any): Promise { - // No need to invalidate contacts, we always try to get the latest. - return this.fetchData(true).finally(() => { - refresher && refresher.complete(); - }); - } - - /** - * Load more contacts. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Resolved when done. - */ - loadMore(infiniteComplete?: any): Promise { - return this.fetchData().finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Notify that a contact has been selected. - * - * @param userId User id. - * @param onInit Whether the contact is selected on initial load. - */ - selectUser(userId: number, onInit: boolean = false): void { - this.selectedUserId = userId; - this.onUserSelected.emit({userId, onInit}); - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.memberInfoObserver && this.memberInfoObserver.off(); - } -} diff --git a/src/addon/messages/components/contact-requests/addon-messages-contact-requests.html b/src/addon/messages/components/contact-requests/addon-messages-contact-requests.html deleted file mode 100644 index c37ccea7e..000000000 --- a/src/addon/messages/components/contact-requests/addon-messages-contact-requests.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - -

{{ request.fullname }}

-

{{ 'addon.messages.wouldliketocontactyou' | translate }}

-
-
- - -
-
diff --git a/src/addon/messages/components/contact-requests/contact-requests.ts b/src/addon/messages/components/contact-requests/contact-requests.ts deleted file mode 100644 index 6c889935d..000000000 --- a/src/addon/messages/components/contact-requests/contact-requests.ts +++ /dev/null @@ -1,148 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; -import { Content } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonMessagesProvider, AddonMessagesConversationMember } from '../../providers/messages'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Component that displays the list of contact requests. - */ -@Component({ - selector: 'addon-messages-contact-requests', - templateUrl: 'addon-messages-contact-requests.html', -}) -export class AddonMessagesContactRequestsComponent implements OnInit, OnDestroy { - @Output() onUserSelected = new EventEmitter<{userId: number, onInit?: boolean}>(); - @ViewChild(Content) content: Content; - - loaded = false; - canLoadMore = false; - loadMoreError = false; - requests: AddonMessagesConversationMember[] = []; - selectedUserId: number; - - protected memberInfoObserver; - - constructor(private domUtils: CoreDomUtilsProvider, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, - private messagesProvider: AddonMessagesProvider) { - - // Hide the "Would like to contact you" message when a contact request is confirmed. - this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => { - if (data.contactRequestConfirmed || data.contactRequestDeclined) { - const index = this.requests.findIndex((request) => request.id == data.userId); - if (index >= 0) { - this.requests.splice(index, 1); - } - } - }, sitesProvider.getCurrentSiteId()); - } - - /** - * Component loaded. - */ - ngOnInit(): void { - this.fetchData().then(() => { - if (this.requests.length) { - this.selectUser(this.requests[0].id, true); - } - }).finally(() => { - this.loaded = true; - }); - - // Workaround for infinite scrolling. - this.content.resize(); - } - - /** - * Fetch contact requests. - * - * @param refresh True if we are refreshing contact requests, false if we are loading more. - * @return Promise resolved when done. - */ - fetchData(refresh: boolean = false): Promise { - this.loadMoreError = false; - - const limitFrom = refresh ? 0 : this.requests.length; - let promise; - - if (limitFrom === 0) { - // Always try to get latest data from server. - promise = this.messagesProvider.invalidateContactRequestsCache().catch(() => { - // Shouldn't happen. - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - return this.messagesProvider.getContactRequests(limitFrom); - }).then((result) => { - this.requests = refresh ? result.requests : this.requests.concat(result.requests); - this.canLoadMore = result.canLoadMore; - }).catch((error) => { - this.loadMoreError = true; - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); - }); - } - - /** - * Refresh contact requests. - * - * @param refresher Refresher. - * @return Promise resolved when done. - */ - refreshData(refresher?: any): Promise { - // Refresh the number of contacts requests to update badges. - this.messagesProvider.refreshContactRequestsCount(); - - // No need to invalidate contact requests, we always try to get the latest. - return this.fetchData(true).finally(() => { - refresher && refresher.complete(); - }); - } - - /** - * Load more contact requests. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Resolved when done. - */ - loadMore(infiniteComplete?: any): Promise { - return this.fetchData().finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Notify that a contact has been selected. - * - * @param userId User id. - * @param onInit Whether the contact is selected on initial load. - */ - selectUser(userId: number, onInit: boolean = false): void { - this.selectedUserId = userId; - this.onUserSelected.emit({userId, onInit}); - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.memberInfoObserver && this.memberInfoObserver.off(); - } -} diff --git a/src/addon/messages/components/contacts/addon-messages-contacts.html b/src/addon/messages/components/contacts/addon-messages-contacts.html deleted file mode 100644 index 0b4167b04..000000000 --- a/src/addon/messages/components/contacts/addon-messages-contacts.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - -

{{ 'addon.messages.type_' + contactType | translate }}

- {{ contacts[contactType].length }} -
- - - - -

{{ contact.fullname }}

-
-
-
-
-
-
diff --git a/src/addon/messages/components/contacts/contacts.ts b/src/addon/messages/components/contacts/contacts.ts deleted file mode 100644 index 2ba88635a..000000000 --- a/src/addon/messages/components/contacts/contacts.ts +++ /dev/null @@ -1,248 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { - AddonMessagesProvider, AddonMessagesGetContactsResult, AddonMessagesSearchContactsContact -} from '../../providers/messages'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; - -/** - * Component that displays the list of contacts. - */ -@Component({ - selector: 'addon-messages-contacts', - templateUrl: 'addon-messages-contacts.html', -}) -export class AddonMessagesContactsComponent { - - protected currentUserId: number; - protected searchingMessages: string; - protected loadingMessages: string; - protected siteId: string; - protected noSearchTypes = ['online', 'offline', 'blocked', 'strangers']; - - loaded = false; - discussionUserId: number; - contactTypes = this.noSearchTypes; - searchType = 'search'; - loadingMessage = ''; - hasContacts = false; - contacts: AddonMessagesGetContactsFormatted = { - online: [], - offline: [], - strangers: [], - search: [] - }; - searchString = ''; - - protected memberInfoObserver; - - constructor(sitesProvider: CoreSitesProvider, translate: TranslateService, private appProvider: CoreAppProvider, - private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams, - private eventsProvider: CoreEventsProvider) { - - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - this.siteId = sitesProvider.getCurrentSiteId(); - this.searchingMessages = translate.instant('core.searching'); - this.loadingMessages = translate.instant('core.loading'); - this.loadingMessage = this.loadingMessages; - - this.discussionUserId = navParams.get('discussionUserId') || false; - - // Refresh the list when a contact request is confirmed. - this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => { - if (data.contactRequestConfirmed) { - this.refreshData(); - } - }, sitesProvider.getCurrentSiteId()); - } - - /** - * Component loaded. - */ - ngOnInit(): void { - if (this.discussionUserId) { - // There is a discussion to load, open the discussion in a new state. - this.gotoDiscussion(this.discussionUserId); - } - - this.fetchData().then(() => { - if (!this.discussionUserId && this.hasContacts) { - let contact; - for (const x in this.contacts) { - if (this.contacts[x].length > 0) { - contact = this.contacts[x][0]; - break; - } - } - - if (contact) { - // Take first and load it. - this.gotoDiscussion(contact.id, true); - } - } - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @return Promise resolved when done. - */ - refreshData(refresher?: any): Promise { - let promise; - - if (this.searchString) { - // User has searched, update the search. - promise = this.performSearch(this.searchString); - } else { - // Update contacts. - promise = this.messagesProvider.invalidateAllContactsCache(this.currentUserId).then(() => { - return this.fetchData(); - }); - } - - return promise.finally(() => { - refresher.complete(); - }); - } - - /** - * Fetch contacts. - * - * @return Promise resolved when done. - */ - protected fetchData(): Promise { - this.loadingMessage = this.loadingMessages; - - return this.messagesProvider.getAllContacts().then((contacts) => { - for (const x in contacts) { - if (contacts[x].length > 0) { - this.contacts[x] = this.sortUsers(contacts[x]); - } else { - this.contacts[x] = []; - } - } - - this.clearSearch(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); - }); - } - - /** - * Sort user list by fullname - * @param list List to sort. - * @return Sorted list. - */ - protected sortUsers(list: any[]): any[] { - return list.sort((a, b) => { - const compareA = a.fullname.toLowerCase(), - compareB = b.fullname.toLowerCase(); - - return compareA.localeCompare(compareB); - }); - } - - /** - * Clear search and show all contacts again. - */ - clearSearch(): void { - this.searchString = ''; // Reset searched string. - this.contactTypes = this.noSearchTypes; - - this.hasContacts = false; - for (const x in this.contacts) { - if (this.contacts[x].length > 0) { - this.hasContacts = true; - - return; - } - } - } - - /** - * Search users from the UI. - * - * @param query Text to search for. - * @return Resolved when done. - */ - search(query: string): Promise { - this.appProvider.closeKeyboard(); - - this.loaded = false; - this.loadingMessage = this.searchingMessages; - - return this.performSearch(query).finally(() => { - this.loaded = true; - }); - } - - /** - * Perform the search of users. - * - * @param query Text to search for. - * @return Resolved when done. - */ - protected performSearch(query: string): Promise { - return this.messagesProvider.searchContacts(query).then((result) => { - this.hasContacts = result.length > 0; - this.searchString = query; - this.contactTypes = ['search']; - - this.contacts.search = this.sortUsers(result); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); - }); - } - - /** - * Navigate to a particular discussion. - * - * @param discussionUserId Discussion Id to load. - * @param onlyWithSplitView Only go to Discussion if split view is on. - */ - gotoDiscussion(discussionUserId: number, onlyWithSplitView: boolean = false): void { - this.discussionUserId = discussionUserId; - - const params = { - discussion: discussionUserId, - onlyWithSplitView: onlyWithSplitView - }; - this.eventsProvider.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_EVENT, params, this.siteId); - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.memberInfoObserver && this.memberInfoObserver.off(); - } -} - -/** - * Contacts with some calculated data. - */ -export type AddonMessagesGetContactsFormatted = AddonMessagesGetContactsResult & { - search?: AddonMessagesSearchContactsContact[]; // Calculated in the app. Result of searching users. -}; diff --git a/src/addon/messages/components/discussions/addon-messages-discussions.html b/src/addon/messages/components/discussions/addon-messages-discussions.html deleted file mode 100644 index 87154cbfe..000000000 --- a/src/addon/messages/components/discussions/addon-messages-discussions.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - -

{{ 'core.searchresults' | translate }}

- {{ search.results.length }} -
- - -

{{ result.fullname }}

-

-
-
- - - - -

{{ discussion.fullname }}

- - - {{discussion.message.timecreated / 1000 | coreDateDayOrTime}} - -

-
-
- - - - -
-
diff --git a/src/addon/messages/components/discussions/discussions.ts b/src/addon/messages/components/discussions/discussions.ts deleted file mode 100644 index 629446b1a..000000000 --- a/src/addon/messages/components/discussions/discussions.ts +++ /dev/null @@ -1,259 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy } from '@angular/core'; -import { Platform, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonMessagesProvider } from '../../providers/messages'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreAppProvider } from '@providers/app'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; - -/** - * Component that displays the list of discussions. - */ -@Component({ - selector: 'addon-messages-discussions', - templateUrl: 'addon-messages-discussions.html', -}) -export class AddonMessagesDiscussionsComponent implements OnDestroy { - protected newMessagesObserver: any; - protected readChangedObserver: any; - protected cronObserver: any; - protected appResumeSubscription: any; - protected loadingMessages: string; - protected siteId: string; - - loaded = false; - loadingMessage: string; - discussions: any; - discussionUserId: number; - pushObserver: any; - search = { - enabled: false, - showResults: false, - results: [], - loading: '', - text: '' - }; - - constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService, - private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams, - private appProvider: CoreAppProvider, platform: Platform, private utils: CoreUtilsProvider, - pushNotificationsDelegate: CorePushNotificationsDelegate) { - - this.search.loading = translate.instant('core.searching'); - this.loadingMessages = translate.instant('core.loading'); - this.siteId = sitesProvider.getCurrentSiteId(); - - // Update discussions when new message is received. - this.newMessagesObserver = eventsProvider.on(AddonMessagesProvider.NEW_MESSAGE_EVENT, (data) => { - if (data.userId && this.discussions) { - const discussion = this.discussions.find((disc) => { - return disc.message.user == data.userId; - }); - - if (typeof discussion == 'undefined') { - this.loaded = false; - this.refreshData().finally(() => { - this.loaded = true; - }); - } else { - // An existing discussion has a new message, update the last message. - discussion.message.message = data.message; - discussion.message.timecreated = data.timecreated; - } - } - }, this.siteId); - - // Update discussions when a message is read. - this.readChangedObserver = eventsProvider.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data) => { - if (data.userId && this.discussions) { - const discussion = this.discussions.find((disc) => { - return disc.message.user == data.userId; - }); - - if (typeof discussion != 'undefined') { - // A discussion has been read reset counter. - discussion.unread = false; - - // Conversations changed, invalidate them and refresh unread counts. - this.messagesProvider.invalidateConversations(this.siteId); - this.messagesProvider.refreshUnreadConversationCounts(this.siteId); - } - } - }, this.siteId); - - // Refresh the view when the app is resumed. - this.appResumeSubscription = platform.resume.subscribe(() => { - if (!this.loaded) { - return; - } - this.loaded = false; - this.refreshData(); - }); - - this.discussionUserId = navParams.get('discussionUserId') || false; - - // If a message push notification is received, refresh the view. - this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => { - // New message received. If it's from current site, refresh the data. - if (utils.isFalseOrZero(notification.notif) && notification.site == this.siteId) { - // Don't refresh unread counts, it's refreshed from the main menu handler in this case. - this.refreshData(null, false); - } - }); - } - - /** - * Component loaded. - */ - ngOnInit(): void { - if (this.discussionUserId) { - // There is a discussion to load, open the discussion in a new state. - this.gotoDiscussion(this.discussionUserId); - } - - this.fetchData().then(() => { - if (!this.discussionUserId && this.discussions.length > 0) { - // Take first and load it. - this.gotoDiscussion(this.discussions[0].message.user, undefined, true); - } - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @param refreshUnreadCounts Whteher to refresh unread counts. - * @return Promise resolved when done. - */ - refreshData(refresher?: any, refreshUnreadCounts: boolean = true): Promise { - const promises = []; - promises.push(this.messagesProvider.invalidateDiscussionsCache(this.siteId)); - - if (refreshUnreadCounts) { - promises.push(this.messagesProvider.invalidateUnreadConversationCounts(this.siteId)); - } - - return this.utils.allPromises(promises).finally(() => { - return this.fetchData().finally(() => { - if (refresher) { - refresher.complete(); - } - }); - }); - } - - /** - * Fetch discussions. - * - * @return Promise resolved when done. - */ - protected fetchData(): Promise { - this.loadingMessage = this.loadingMessages; - this.search.enabled = this.messagesProvider.isSearchMessagesEnabled(); - - const promises = []; - - promises.push(this.messagesProvider.getDiscussions(this.siteId).then((discussions) => { - // Convert to an array for sorting. - const discussionsSorted = []; - for (const userId in discussions) { - discussions[userId].unread = !!discussions[userId].unread; - discussionsSorted.push(discussions[userId]); - } - - this.discussions = discussionsSorted.sort((a, b) => { - return b.message.timecreated - a.message.timecreated; - }); - })); - - promises.push(this.messagesProvider.getUnreadConversationCounts(this.siteId)); - - return Promise.all(promises).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Clear search and show discussions again. - */ - clearSearch(): void { - this.loaded = false; - this.search.showResults = false; - this.search.text = ''; // Reset searched string. - this.fetchData().finally(() => { - this.loaded = true; - }); - } - - /** - * Search messages cotaining text. - * - * @param query Text to search for. - * @return Resolved when done. - */ - searchMessage(query: string): Promise { - this.appProvider.closeKeyboard(); - this.loaded = false; - this.loadingMessage = this.search.loading; - - return this.messagesProvider.searchMessages(query, undefined, undefined, undefined, this.siteId).then((searchResults) => { - this.search.showResults = true; - this.search.results = searchResults.messages; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Navigate to a particular discussion. - * - * @param discussionUserId Discussion Id to load. - * @param messageId Message to scroll after loading the discussion. Used when searching. - * @param onlyWithSplitView Only go to Discussion if split view is on. - */ - gotoDiscussion(discussionUserId: number, messageId?: number, onlyWithSplitView: boolean = false): void { - this.discussionUserId = discussionUserId; - - const params = { - discussion: discussionUserId, - onlyWithSplitView: onlyWithSplitView - }; - if (messageId) { - params['message'] = messageId; - } - this.eventsProvider.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_EVENT, params, this.siteId); - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.newMessagesObserver && this.newMessagesObserver.off(); - this.readChangedObserver && this.readChangedObserver.off(); - this.cronObserver && this.cronObserver.off(); - this.appResumeSubscription && this.appResumeSubscription.unsubscribe(); - this.pushObserver && this.pushObserver.unsubscribe(); - } -} diff --git a/src/addon/messages/lang/en.json b/src/addon/messages/lang/en.json deleted file mode 100644 index 0b728d3e9..000000000 --- a/src/addon/messages/lang/en.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "acceptandaddcontact": "Accept and add to contacts", - "addcontact": "Add contact", - "addcontactconfirm": "Are you sure you want to add {{$a}} to your contacts?", - "addtofavourites": "Star conversation", - "addtoyourcontacts": "Add to contacts", - "blocknoncontacts": "Prevent non-contacts from messaging me", - "blockuser": "Block user", - "blockuserconfirm": "Are you sure you want to block {{$a}}?", - "contactableprivacy": "Accept messages from:", - "contactableprivacy_coursemember": "My contacts and anyone in my courses", - "contactableprivacy_onlycontacts": "My contacts only", - "contactableprivacy_site": "Anyone on the site", - "contactblocked": "Contact blocked", - "contactlistempty": "The contact list is empty", - "contactname": "Contact name", - "contactrequestsent": "Contact request sent", - "contacts": "Contacts", - "conversationactions": "Conversation actions menu", - "decline": "Decline", - "deleteallconfirm": "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.", - "deleteallselfconfirm": "Are you sure you would like to delete this entire personal conversation?", - "deleteconversation": "Delete conversation", - "deleteforeveryone": "Delete for me and for everyone else", - "deletemessage": "Delete message", - "deletemessageconfirmation": "Are you sure you want to delete this message? It will only be deleted from your messaging history and will still be viewable by the user who sent or received the message.", - "errordeletemessage": "Error while deleting the message.", - "errorwhileretrievingcontacts": "Error while retrieving contacts from the server.", - "errorwhileretrievingdiscussions": "Error while retrieving discussions from the server.", - "errorwhileretrievingmessages": "Error while retrieving messages from the server.", - "errorwhileretrievingusers": "Error while retrieving users from the server.", - "groupconversations": "Group", - "groupinfo": "Group info", - "individualconversations": "Private", - "info": "User info", - "isnotinyourcontacts": "{{$a}} is not in your contacts", - "message": "Message", - "messagenotsent": "The message was not sent. Please try again later.", - "messagepreferences": "Message preferences", - "messages": "Messages", - "muteconversation": "Mute", - "mutedconversation": "Muted conversation", - "newmessage": "New message", - "newmessages": "New messages", - "nocontactrequests": "No contact requests", - "nocontactsgetstarted": "No contacts", - "nofavourites": "No starred conversations", - "nogroupconversations": "No group conversations", - "noindividualconversations": "No private conversations", - "nomessagesfound": "No messages were found", - "noncontacts": "Non-contacts", - "nousersfound": "No users found", - "numparticipants": "{{$a}} participants", - "removecontact": "Remove contact", - "removecontactconfirm": "Are you sure you want to remove {{$a}} from your contacts?", - "removefromfavourites": "Unstar conversation", - "removefromyourcontacts": "Remove from contacts", - "requests": "Requests", - "requirecontacttomessage": "You need to request {{$a}} to add you as a contact to be able to message them.", - "searchcombined": "Search people and messages", - "selfconversation": "Personal space", - "selfconversationdefaultmessage": "Save draft messages, links, notes etc. to access later.", - "sendcontactrequest": "Send contact request", - "showdeletemessages": "Show delete messages", - "type_blocked": "Blocked", - "type_offline": "Offline", - "type_online": "Online", - "type_search": "Search results", - "type_strangers": "Others", - "unabletomessage": "You are unable to message this user", - "unblockuser": "Unblock user", - "unblockuserconfirm": "Are you sure you want to unblock {{$a}}?", - "unmuteconversation": "Unmute", - "useentertosend": "Use enter to send", - "useentertosenddescdesktop": "If disabled, you can use Ctrl+Enter to send the message.", - "useentertosenddescmac": "If disabled, you can use Cmd+Enter to send the message.", - "userwouldliketocontactyou": "{{$a}} would like to contact you", - "warningconversationmessagenotsent": "Couldn't send message(s) to conversation {{conversation}}. {{error}}", - "warningmessagenotsent": "Couldn't send message(s) to user {{user}}. {{error}}", - "wouldliketocontactyou": "Would like to contact you", - "you": "You:", - "youhaveblockeduser": "You have blocked this user.", - "yourcontactrequestpending": "Your contact request is pending with {{$a}}" -} \ No newline at end of file diff --git a/src/addon/messages/messages.module.ts b/src/addon/messages/messages.module.ts deleted file mode 100644 index 69b1b0860..000000000 --- a/src/addon/messages/messages.module.ts +++ /dev/null @@ -1,139 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule, NgZone } from '@angular/core'; -import { Network } from '@ionic-native/network'; -import { AddonMessagesProvider } from './providers/messages'; -import { AddonMessagesOfflineProvider } from './providers/messages-offline'; -import { AddonMessagesSyncProvider } from './providers/sync'; -import { AddonMessagesMainMenuHandler } from './providers/mainmenu-handler'; -import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreUserDelegate } from '@core/user/providers/user-delegate'; -import { CoreCronDelegate } from '@providers/cron'; -import { AddonMessagesSendMessageUserHandler } from './providers/user-send-message-handler'; -import { AddonMessagesAddContactUserHandler } from './providers/user-add-contact-handler'; -import { AddonMessagesBlockContactUserHandler } from './providers/user-block-contact-handler'; -import { AddonMessagesContactRequestLinkHandler } from './providers/contact-request-link-handler'; -import { AddonMessagesDiscussionLinkHandler } from './providers/discussion-link-handler'; -import { AddonMessagesIndexLinkHandler } from './providers/index-link-handler'; -import { AddonMessagesSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonMessagesPushClickHandler } from './providers/push-click-handler'; -import { CoreAppProvider } from '@providers/app'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreSettingsDelegate } from '@core/settings/providers/delegate'; -import { AddonMessagesSettingsHandler } from './providers/settings-handler'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -// List of providers (without handlers). -export const ADDON_MESSAGES_PROVIDERS: any[] = [ - AddonMessagesProvider, - AddonMessagesOfflineProvider, - AddonMessagesSyncProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - ], - providers: [ - AddonMessagesProvider, - AddonMessagesOfflineProvider, - AddonMessagesSyncProvider, - AddonMessagesMainMenuHandler, - AddonMessagesSendMessageUserHandler, - AddonMessagesAddContactUserHandler, - AddonMessagesBlockContactUserHandler, - AddonMessagesContactRequestLinkHandler, - AddonMessagesDiscussionLinkHandler, - AddonMessagesIndexLinkHandler, - AddonMessagesSyncCronHandler, - AddonMessagesSettingsHandler, - AddonMessagesPushClickHandler - ] -}) -export class AddonMessagesModule { - constructor(mainMenuDelegate: CoreMainMenuDelegate, mainmenuHandler: AddonMessagesMainMenuHandler, - contentLinksDelegate: CoreContentLinksDelegate, indexLinkHandler: AddonMessagesIndexLinkHandler, - discussionLinkHandler: AddonMessagesDiscussionLinkHandler, sendMessageHandler: AddonMessagesSendMessageUserHandler, - userDelegate: CoreUserDelegate, cronDelegate: CoreCronDelegate, syncHandler: AddonMessagesSyncCronHandler, - network: Network, zone: NgZone, messagesSync: AddonMessagesSyncProvider, appProvider: CoreAppProvider, - localNotifications: CoreLocalNotificationsProvider, messagesProvider: AddonMessagesProvider, - sitesProvider: CoreSitesProvider, linkHelper: CoreContentLinksHelperProvider, - settingsHandler: AddonMessagesSettingsHandler, settingsDelegate: CoreSettingsDelegate, - pushNotificationsDelegate: CorePushNotificationsDelegate, utils: CoreUtilsProvider, - addContactHandler: AddonMessagesAddContactUserHandler, blockContactHandler: AddonMessagesBlockContactUserHandler, - contactRequestLinkHandler: AddonMessagesContactRequestLinkHandler, pushClickHandler: AddonMessagesPushClickHandler) { - // Register handlers. - mainMenuDelegate.registerHandler(mainmenuHandler); - contentLinksDelegate.registerHandler(indexLinkHandler); - contentLinksDelegate.registerHandler(discussionLinkHandler); - contentLinksDelegate.registerHandler(contactRequestLinkHandler); - userDelegate.registerHandler(sendMessageHandler); - userDelegate.registerHandler(addContactHandler); - userDelegate.registerHandler(blockContactHandler); - cronDelegate.register(syncHandler); - cronDelegate.register(mainmenuHandler); - settingsDelegate.registerHandler(settingsHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - - // Sync some discussions when device goes online. - network.onConnect().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - messagesSync.syncAllDiscussions(undefined, true); - }); - }); - - const notificationClicked = (notification: any): void => { - messagesProvider.isMessagingEnabledForSite(notification.site).then(() => { - sitesProvider.isFeatureDisabled('CoreMainMenuDelegate_AddonMessages', notification.site).then((disabled) => { - if (disabled) { - // Messages are disabled, stop. - return; - } - - messagesProvider.invalidateDiscussionsCache(notification.site).finally(() => { - // Check if group messaging is enabled, to determine which page should be loaded. - messagesProvider.isGroupMessagingEnabledInSite(notification.site).then((enabled) => { - const pageParams: any = {}; - let pageName = 'AddonMessagesIndexPage'; - if (enabled) { - pageName = 'AddonMessagesGroupConversationsPage'; - } - - // Check if we have enough information to open the conversation. - if (notification.convid && enabled) { - pageParams.conversationId = Number(notification.convid); - } else if (notification.userfromid || notification.useridfrom) { - pageParams.discussionUserId = Number(notification.userfromid || notification.useridfrom); - } - - linkHelper.goInSite(undefined, pageName, pageParams, notification.site); - }); - }); - }); - }); - }; - - if (appProvider.isDesktop()) { - // Listen for clicks in simulated push notifications. - localNotifications.registerClick(AddonMessagesProvider.PUSH_SIMULATION_COMPONENT, notificationClicked); - } - } -} diff --git a/src/addon/messages/pages/contacts/contacts.html b/src/addon/messages/pages/contacts/contacts.html deleted file mode 100644 index 1316bd027..000000000 --- a/src/addon/messages/pages/contacts/contacts.html +++ /dev/null @@ -1,28 +0,0 @@ - - - {{ 'addon.messages.contacts' | translate }} - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/addon/messages/pages/contacts/contacts.module.ts b/src/addon/messages/pages/contacts/contacts.module.ts deleted file mode 100644 index e85d44606..000000000 --- a/src/addon/messages/pages/contacts/contacts.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonMessagesContactsPage } from './contacts'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonMessagesComponentsModule } from '../../components/components.module'; - -@NgModule({ - declarations: [ - AddonMessagesContactsPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - AddonMessagesComponentsModule, - IonicPageModule.forChild(AddonMessagesContactsPage), - TranslateModule.forChild() - ], -}) -export class AddonMessagesContactsPageModule {} diff --git a/src/addon/messages/pages/contacts/contacts.ts b/src/addon/messages/pages/contacts/contacts.ts deleted file mode 100644 index 5ee6b6ff4..000000000 --- a/src/addon/messages/pages/contacts/contacts.ts +++ /dev/null @@ -1,122 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, ViewChild } from '@angular/core'; -import { IonicPage, NavController } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonMessagesProvider } from '../../providers/messages'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreTabsComponent } from '@components/tabs/tabs'; - -/** - * Page that displays contacts and contact requests. - */ -@IonicPage({ segment: 'addon-messages-contacts' }) -@Component({ - selector: 'page-addon-messages-contacts', - templateUrl: 'contacts.html', -}) -export class AddonMessagesContactsPage implements OnDestroy { - - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - @ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent; - - contactRequestsCount = 0; - - protected loadSplitViewObserver: any; - protected siteId: string; - protected contactRequestsCountObserver: any; - protected conversationUserId: number; // User id of the conversation opened in the split view. - protected selectedUserId = { - contacts: null, // User id of the selected user in the confirmed contacts tab. - requests: null, // User id of the selected user in the contact requests tab. - }; - - constructor(eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, - private navCtrl: NavController, private messagesProvider: AddonMessagesProvider) { - - this.siteId = sitesProvider.getCurrentSiteId(); - - // Update the contact requests badge. - this.contactRequestsCountObserver = eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { - this.contactRequestsCount = data.count; - }, this.siteId); - } - - /** - * Page being initialized. - */ - ngOnInit(): void { - this.messagesProvider.getContactRequestsCount(this.siteId); // Badge already updated by the observer. - } - - /** - * Navigate to the search page. - */ - gotoSearch(): void { - this.navCtrl.push('AddonMessagesSearchPage'); - } - - /** - * User entered the page. - */ - ionViewDidEnter(): void { - if (!this.splitviewCtrl.isOn()) { - this.selectedUserId.contacts = null; - this.selectedUserId.requests = null; - } - - this.tabsComponent && this.tabsComponent.ionViewDidEnter(); - } - - /** - * User left the page. - */ - ionViewDidLeave(): void { - this.tabsComponent && this.tabsComponent.ionViewDidLeave(); - } - - /** - * Set the selected user and open the conversation in the split view if needed. - * - * @param tab Active tab: "contacts" or "requests". - * @param userId Id of the selected user, undefined to use the last selected user in the tab. - * @param onInit Whether the contact was selected on initial load. - */ - selectUser(tab: string, userId?: number, onInit: boolean = false): void { - userId = userId || this.selectedUserId[tab]; - - if (!userId || userId == this.conversationUserId && this.splitviewCtrl.isOn()) { - // No user conversation to open or it is already opened. - return; - } - - if (onInit && !this.splitviewCtrl.isOn()) { - // Do not open a conversation by default when split view is not visible. - return; - } - - this.conversationUserId = userId; - this.selectedUserId[tab] = userId; - this.splitviewCtrl.push('AddonMessagesDiscussionPage', { userId }); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.contactRequestsCountObserver && this.contactRequestsCountObserver.off(); - } -} diff --git a/src/addon/messages/pages/conversation-info/conversation-info.html b/src/addon/messages/pages/conversation-info/conversation-info.html deleted file mode 100644 index b5885ddd1..000000000 --- a/src/addon/messages/pages/conversation-info/conversation-info.html +++ /dev/null @@ -1,36 +0,0 @@ - - - {{ 'addon.messages.groupinfo' | translate }} - - - - - - - - - - - - -
- -
-

-

-

{{ 'addon.messages.numparticipants' | translate:{$a: conversation.membercount} }}

-
- - - -

- {{ member.fullname }} - -

-
- - -
-
diff --git a/src/addon/messages/pages/conversation-info/conversation-info.module.ts b/src/addon/messages/pages/conversation-info/conversation-info.module.ts deleted file mode 100644 index df49d4eed..000000000 --- a/src/addon/messages/pages/conversation-info/conversation-info.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonMessagesConversationInfoPage } from './conversation-info'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonMessagesConversationInfoPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonMessagesConversationInfoPage), - TranslateModule.forChild() - ], -}) -export class AddonMessagesConversationInfoPageModule {} diff --git a/src/addon/messages/pages/conversation-info/conversation-info.ts b/src/addon/messages/pages/conversation-info/conversation-info.ts deleted file mode 100644 index 5dfeb450d..000000000 --- a/src/addon/messages/pages/conversation-info/conversation-info.ts +++ /dev/null @@ -1,136 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; -import { - AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMember -} from '../../providers/messages'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSitesProvider } from '@providers/sites'; - -/** - * Page that displays the list of conversations, including group conversations. - */ -@IonicPage({ segment: 'addon-messages-conversation-info' }) -@Component({ - selector: 'page-addon-messages-conversation-info', - templateUrl: 'conversation-info.html', -}) -export class AddonMessagesConversationInfoPage implements OnInit { - - loaded = false; - conversation: AddonMessagesConversationFormatted; - members: AddonMessagesConversationMember[] = []; - canLoadMore = false; - loadMoreError = false; - - protected conversationId: number; - - constructor(private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams, - protected viewCtrl: ViewController, sitesProvider: CoreSitesProvider) { - this.conversationId = navParams.get('conversationId'); - } - - /** - * Component loaded. - */ - ngOnInit(): void { - this.fetchData().finally(() => { - this.loaded = true; - }); - } - - /** - * Fetch the required data. - * - * @return Promise resolved when done. - */ - protected fetchData(): Promise { - // Get the conversation data first. - return this.messagesProvider.getConversation(this.conversationId, false, true, 0, 0).then((conversation) => { - this.conversation = conversation; - - // Now get the members. - return this.fetchMembers(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting members.'); - }); - } - - /** - * Get conversation members. - * - * @param loadingMore Whether we are loading more data or just the first ones. - * @return Promise resolved when done. - */ - protected fetchMembers(loadingMore?: boolean): Promise { - this.loadMoreError = false; - - const limitFrom = loadingMore ? this.members.length : 0; - - return this.messagesProvider.getConversationMembers(this.conversationId, limitFrom).then((data) => { - if (loadingMore) { - this.members = this.members.concat(data.members); - } else { - this.members = data.members; - } - - this.canLoadMore = data.canLoadMore; - }); - } - - /** - * Function to load more members. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Resolved when done. - */ - loadMoreMembers(infiniteComplete?: any): Promise { - return this.fetchMembers(true).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting members.'); - this.loadMoreError = true; - }).finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @return Promise resolved when done. - */ - refreshData(refresher?: any): Promise { - const promises = []; - - promises.push(this.messagesProvider.invalidateConversation(this.conversationId)); - promises.push(this.messagesProvider.invalidateConversationMembers(this.conversationId)); - - return Promise.all(promises).then(() => { - return this.fetchData().finally(() => { - refresher && refresher.complete(); - }); - }); - } - - /** - * Close modal. - * - * @param userId User conversation to load. - */ - closeModal(userId?: number): void { - this.viewCtrl.dismiss(userId); - } -} diff --git a/src/addon/messages/pages/discussion/discussion.html b/src/addon/messages/pages/discussion/discussion.html deleted file mode 100644 index fbe0c89b3..000000000 --- a/src/addon/messages/pages/discussion/discussion.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

{{ 'addon.messages.selfconversation' | translate }}

-

{{ 'addon.messages.selfconversationdefaultmessage' | translate }}

-
- - - -
- {{ message.timecreated | coreFormatDate: "strftimedayshort" }} -
- - - {{ 'addon.messages.newmessages' | translate }} - - - - - -

- - -
{{ members[message.useridfrom].fullname }}
- - {{ message.timecreated | coreFormatDate: "strftimetime" }} - -

- - -

- -

- - -
-
-
-
- - - - - -
-
- - -

{{ 'addon.messages.unabletomessage' | translate }}

-
-

{{ 'addon.messages.youhaveblockeduser' | translate }}

- -
-
-

{{ 'addon.messages.isnotinyourcontacts' | translate: {$a: otherMember.fullname} }}

-

{{ 'addon.messages.requirecontacttomessage' | translate: {$a: otherMember.fullname} }}

- -
-
-

{{ 'addon.messages.userwouldliketocontactyou' | translate: {$a: otherMember.fullname} }}

- - -
-
-

{{ 'addon.messages.contactrequestsent' | translate }}

-

{{ 'addon.messages.yourcontactrequestpending' | translate: {$a: otherMember.fullname} }}

-
- -
-
diff --git a/src/addon/messages/pages/discussion/discussion.module.ts b/src/addon/messages/pages/discussion/discussion.module.ts deleted file mode 100644 index a3e020e3f..000000000 --- a/src/addon/messages/pages/discussion/discussion.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonMessagesDiscussionPage } from './discussion'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; - -@NgModule({ - declarations: [ - AddonMessagesDiscussionPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonMessagesDiscussionPage), - TranslateModule.forChild() - ], -}) -export class AddonMessagesDiscussionPageModule {} diff --git a/src/addon/messages/pages/discussion/discussion.scss b/src/addon/messages/pages/discussion/discussion.scss deleted file mode 100644 index 4b323f98f..000000000 --- a/src/addon/messages/pages/discussion/discussion.scss +++ /dev/null @@ -1,260 +0,0 @@ -// Messages. -$item-message-bg: $white !default; -$item-message-note-text: $gray-dark !default; -$item-message-note-font-size: 75% !default; -$item-message-mine-bg: $gray-light !default; - -$core-discussion-messages-badge: $core-color !default; -$core-discussion-messages-badge-text: $white !default; - -@mixin message-page { - ion-content { - background-color: $gray-lighter !important; - @include darkmode() { - background-color: $gray-darker !important; - } - } - - .addon-messages-discussion-container { - display: flex; - flex-direction: column; - padding-bottom: 15px; - } - - .addon-messages-date { - font-weight: normal; - font-size: 1.4rem; - } - - .addon-messages-unreadfrom { - color: $core-color; - background-color: transparent; - margin-top: 6px; - ion-icon { - color: $core-color; - background-color: transparent; - } - } - - // Message item. - .item.item-block.addon-message { - border: 0; - border-radius: 4px; - padding: 8px; - @include margin(8px, 8px, 0, 8px); - background-color: $item-message-bg; - align-self: flex-start; - width: 90%; - max-width: 90%; - min-height: 0; - position: relative; - @include core-transition(width); - // This is needed to display bubble tails. - overflow: visible; - - core-format-text > p:only-child { - display: inline; - } - - - .addon-message-user { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin-bottom: .5rem; - margin-top: 0; - color: $text-color; - - ion-avatar { - display: block; - min-width: 30px; - min-height: 30px; - margin: 0; - img { - width: 30px; - height: 30px; - } - } - - div { - font-weight: 500; - flex-grow: 1; - @include padding-horizontal(.5rem); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .note { - @include text-align('end'); - color: $item-message-note-text; - font-size: $item-message-note-font-size; - } - } - - &.addon-message-no-user .addon-message-user .note { - width: 100%; - } - - &.activated { - background-color: darken($item-message-bg, 10%); - } - - .item-inner { - border-bottom: 0; - padding: 0; - margin: 0; - } - - .label { - margin: 0; - padding: 0; - } - - .addon-message-text { - display: inline-flex; - * { - color: $text-color; - } - } - - .addon-messages-delete-button { - min-height: initial; - line-height: initial; - @include margin(0, 0, 0, 10px); - height: 1.6em !important; - -webkit-align-self: flex-end; - -ms-flex-item-align: end; - align-self: flex-end; - vertical-align: middle; - @include float('end'); - - .icon { - font-size: 1.4em; - line-height: initial; - color: $danger; - } - } - - .tail { - content: ''; - width: 0; - height: 0; - border: 0.5rem solid transparent; - position: absolute; - touch-action: none; - } - - // Defines when an item-message is the user's. - &.addon-message-mine { - background-color: $item-message-mine-bg; - align-self: flex-end; - - &.activated { - background-color: darken($item-message-mine-bg, 10%); - } - - .spinner { - @include float(end); - @include margin(2px, -3px, -2px, 5px); - - svg { - width: 16px; - height: 16px; - } - } - - .tail { - @include position(null, 0, 0, null); - @include margin-horizontal(null, -0.5rem); - border-bottom-color: $item-message-mine-bg; - } - - &.activated .tail { - border-bottom-color: darken($item-message-mine-bg, 10%); - } - } - - &.addon-message-not-mine .tail { - @include position(null, null, 0, 0); - @include margin-horizontal(-0.5rem, null); - border-bottom-color: $item-message-bg; - } - - &.addon-message-not-mine.activated .tail { - border-bottom-color: darken($item-message-bg, 10%); - } - } - - .item.addon-message.addon-message-mine + .item.addon-message.addon-message-no-user.addon-message-mine, - .item.addon-message.addon-message-not-mine + .item.addon-message.addon-message-no-user.addon-message-not-mine { - h2 { - margin-bottom: 0; - } - margin-top: -4px; - padding-top: 0; - border-top-right-radius: 0; - border-top-left-radius: 0; - } - - .has-fab .scroll-content { - padding-bottom: 0; - } - ion-fab button { - overflow: visible; - position: relative; - box-shadow: $fab-md-box-shadow; - - .core-discussion-messages-badge { - position: absolute; - border-radius: 50%; - color: $core-discussion-messages-badge-text; - background-color: $core-discussion-messages-badge; - display: block; - line-height: 20px; - height: 20px; - width: 20px; - right: -6px; - top: -6px; - - } - } - -} - - -ion-app.app-root page-addon-messages-discussion.ion-page { - - @include message-page(); - - .toolbar-title { - padding: 0; - - img { - @include margin-horizontal(null, 6px); - } - display: flex; - align-items: center; - - core-format-text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex-shrink: 1; - } - - ion-icon { - @include margin-horizontal(6px, null); - } - - &.toolbar-title-ios { - justify-content: center; - } - } -} - -ion-app.app-root.ios page-addon-messages-discussion ion-footer .toolbar:last-child { - padding-bottom: 4px; - min-height: 0; -} diff --git a/src/addon/messages/pages/discussion/discussion.ts b/src/addon/messages/pages/discussion/discussion.ts deleted file mode 100644 index 11a4046a1..000000000 --- a/src/addon/messages/pages/discussion/discussion.ts +++ /dev/null @@ -1,1563 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, ViewChild, Optional } from '@angular/core'; -import { IonicPage, NavParams, NavController, Content, ModalController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { - AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMember, AddonMessagesConversationMessage, - AddonMessagesGetMessagesMessage -} from '../../providers/messages'; -import { AddonMessagesOfflineProvider } from '../../providers/messages-offline'; -import { AddonMessagesSyncProvider } from '../../providers/sync'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreAppProvider } from '@providers/app'; -import { coreSlideInOut } from '@classes/animations'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreInfiniteLoadingComponent } from '@components/infinite-loading/infinite-loading'; -import { Md5 } from 'ts-md5/dist/md5'; -import * as moment from 'moment'; - -/** - * Page that displays a message discussion page. - */ -@IonicPage({ segment: 'addon-messages-discussion' }) -@Component({ - selector: 'page-addon-messages-discussion', - templateUrl: 'discussion.html', - animations: [coreSlideInOut] -}) -export class AddonMessagesDiscussionPage implements OnDestroy { - @ViewChild(Content) content: Content; - @ViewChild(CoreInfiniteLoadingComponent) infinite: CoreInfiniteLoadingComponent; - - siteId: string; - protected fetching: boolean; - protected polling; - protected logger; - - protected unreadMessageFrom = 0; - protected messagesBeingSent = 0; - protected pagesLoaded = 1; - protected lastMessage = {text: '', timecreated: 0}; - protected keepMessageMap: {[hash: string]: boolean} = {}; - protected syncObserver: any; - protected oldContentHeight = 0; - protected keyboardObserver: any; - protected scrollBottom = true; - protected viewDestroyed = false; - protected memberInfoObserver: any; - protected showLoadingModal = false; // Whether to show a loading modal while fetching data. - protected scrollListener; - - conversationId: number; // Conversation ID. Undefined if it's a new individual conversation. - conversation: AddonMessagesConversationFormatted; // The conversation object (if it exists). - userId: number; // User ID you're talking to (only if group messaging not enabled or it's a new individual conversation). - currentUserId: number; - title: string; - showInfo: boolean; - conversationImage: string; - loaded = false; - showKeyboard = false; - canLoadMore = false; - loadMoreError = false; - messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[] = []; - showDelete = false; - canDelete = false; - groupMessagingEnabled: boolean; - isGroup = false; - members: {[id: number]: AddonMessagesConversationMember} = {}; // Members that wrote a message, indexed by ID. - favouriteIcon = 'fa-star'; - favouriteIconSlash = false; - deleteIcon = 'trash'; - blockIcon = 'close-circle'; - addRemoveIcon = 'person'; - otherMember: AddonMessagesConversationMember; // Other member information (individual conversations only). - footerType: 'message' | 'blocked' | 'requiresContact' | 'requestSent' | 'requestReceived' | 'unable'; - requestContactSent = false; - requestContactReceived = false; - isSelf = false; - muteEnabled = false; - muteIcon = 'volume-off'; - newMessages = 0; - - constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, navParams: NavParams, - private userProvider: CoreUserProvider, private navCtrl: NavController, private messagesSync: AddonMessagesSyncProvider, - private domUtils: CoreDomUtilsProvider, private messagesProvider: AddonMessagesProvider, logger: CoreLoggerProvider, - private utils: CoreUtilsProvider, private appProvider: CoreAppProvider, private translate: TranslateService, - @Optional() private svComponent: CoreSplitViewComponent, private messagesOffline: AddonMessagesOfflineProvider, - private modalCtrl: ModalController, private textUtils: CoreTextUtilsProvider) { - - this.siteId = sitesProvider.getCurrentSiteId(); - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - this.groupMessagingEnabled = this.messagesProvider.isGroupMessagingEnabled(); - this.muteEnabled = this.messagesProvider.isMuteConversationEnabled(); - - this.logger = logger.getInstance('AddonMessagesDiscussionPage'); - - this.conversationId = navParams.get('conversationId'); - this.userId = navParams.get('userId'); - this.showKeyboard = navParams.get('showKeyboard'); - - // Refresh data if this discussion is synchronized automatically. - this.syncObserver = eventsProvider.on(AddonMessagesSyncProvider.AUTO_SYNCED, (data) => { - if ((data.userId && data.userId == this.userId) || - (data.conversationId && data.conversationId == this.conversationId)) { - // Fetch messages. - this.fetchMessages(); - - // Show first warning if any. - if (data.warnings && data.warnings[0]) { - this.domUtils.showErrorModal(data.warnings[0]); - } - } - }, this.siteId); - - // Refresh data if info of a mamber of the conversation have changed. - this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => { - if (data.userId && (this.members[data.userId] || this.otherMember && data.userId == this.otherMember.id)) { - this.fetchData(); - } - }, this.siteId); - - this.scrollListener = this.scrollListenerFunction.bind(this); - } - - /** - * Adds a new message to the message list. - * - * @param message Message to be added. - * @param keep If set the keep flag or not. - * @return If message is not mine and was recently added. - */ - protected addMessage(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted, - keep: boolean = true): boolean { - - /* Create a hash to identify the message. The text of online messages isn't reliable because it can have random data - like VideoJS ID. Try to use id and fallback to text for offline messages. */ - message.hash = Md5.hashAsciiStr(String(message.id || message.text || '')) + '#' + message.timecreated + '#' + - message.useridfrom; - - let added = false; - if (typeof this.keepMessageMap[message.hash] === 'undefined') { - // Message not added to the list. Add it now. - this.messages.push(message); - added = message.useridfrom != this.currentUserId; - } - // Message needs to be kept in the list. - this.keepMessageMap[message.hash] = keep; - - return added; - } - - /** - * Remove a message if it shouldn't be in the list anymore. - * - * @param hash Hash of the message to be removed. - */ - protected removeMessage(hash: string): void { - if (this.keepMessageMap[hash]) { - // Selected to keep it, clear the flag. - this.keepMessageMap[hash] = false; - - return; - } - - delete this.keepMessageMap[hash]; - - const position = this.messages.findIndex((message) => { - return message.hash == hash; - }); - if (position >= 0) { - this.messages.splice(position, 1); - } - } - - /** - * Runs when the page has loaded. This event only happens once per page being created. - * If a page leaves but is cached, then this event will not fire again on a subsequent viewing. - * Setup code for the page. - */ - ionViewDidLoad(): void { - // Disable the profile button if we're already coming from a profile. - const backViewPage = this.navCtrl.getPrevious() && this.navCtrl.getPrevious().component.name; - this.showInfo = !backViewPage || backViewPage !== 'CoreUserProfilePage'; - - // Recalculate footer position when keyboard is shown or hidden. - this.keyboardObserver = this.eventsProvider.on(CoreEventsProvider.KEYBOARD_CHANGE, (kbHeight) => { - this.content.resize(); - }); - - this.fetchData(); - } - - /** - * Convenience function to fetch the conversation data. - * - * @return Resolved when done. - */ - protected fetchData(): Promise { - let loader; - if (this.showLoadingModal) { - loader = this.domUtils.showModalLoading(); - } - - if (!this.groupMessagingEnabled && this.userId) { - // Get the user profile to retrieve the user fullname and image. - this.userProvider.getProfile(this.userId, undefined, true).then((user) => { - if (!this.title) { - this.title = user.fullname; - } - this.conversationImage = user.profileimageurl; - }); - } - - // Synchronize messages if needed. - return this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => { - // Ignore errors. - }).then((warnings): Promise => { - if (warnings && warnings[0]) { - this.domUtils.showErrorModal(warnings[0]); - } - - if (this.groupMessagingEnabled) { - // Get the conversation ID if it exists and we don't have it yet. - return this.getConversation(this.conversationId, this.userId).then((exists) => { - const promises = []; - - if (exists) { - // Fetch the messages for the first time. - promises.push(this.fetchMessages()); - } - - if (this.userId) { - // Get the member info. Invalidate first to make sure we get the latest status. - promises.push(this.messagesProvider.invalidateMemberInfo(this.userId).catch(() => { - // Shouldn't happen. - }).then(() => { - return this.messagesProvider.getMemberInfo(this.userId); - }).then((member) => { - this.otherMember = member; - if (!exists && member) { - this.conversationImage = member.profileimageurl; - this.title = member.fullname; - } - this.blockIcon = this.otherMember && this.otherMember.isblocked ? 'checkmark-circle' : 'close-circle'; - })); - } else { - this.otherMember = null; - } - - return Promise.all(promises); - }); - } else { - this.otherMember = null; - - // Fetch the messages for the first time. - return this.fetchMessages().then(() => { - if (!this.title && this.messages.length) { - // Didn't receive the fullname via argument. Try to get it from messages. - // It's possible that name cannot be resolved when no messages were yet exchanged. - const firstMessage = this.messages[0]; - if (firstMessage.useridto != this.currentUserId) { - this.title = firstMessage.usertofullname || ''; - } else { - this.title = firstMessage.userfromfullname || ''; - } - } - }); - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true); - }).finally(() => { - this.checkCanDelete(); - this.resizeContent(); - this.loaded = true; - this.setPolling(); // Make sure we're polling messages. - this.setContactRequestInfo(); - this.setFooterType(); - loader && loader.dismiss(); - }); - } - - /** - * Runs when the page has fully entered and is now the active page. - * This event will fire, whether it was the first load or a cached page. - */ - ionViewDidEnter(): void { - this.setPolling(); - } - - /** - * Runs when the page is about to leave and no longer be the active page. - */ - ionViewWillLeave(): void { - this.unsetPolling(); - } - - /** - * Convenience function to fetch messages. - * - * @param messagesAreNew If messages loaded are new messages. - * @return Resolved when done. - */ - protected fetchMessages(messagesAreNew: boolean = true): Promise { - this.loadMoreError = false; - - if (this.messagesBeingSent > 0) { - // We do not poll while a message is being sent or we could confuse the user. - // Otherwise, his message would disappear from the list, and he'd have to wait for the interval to check for messages. - return Promise.reject(null); - } else if (this.fetching) { - // Already fetching. - return Promise.reject(null); - } else if (this.groupMessagingEnabled && !this.conversationId) { - // Don't have enough data to fetch messages. - return Promise.reject(null); - } - - if (this.conversationId) { - this.logger.debug(`Polling new messages for conversation '${this.conversationId}'`); - } else { - this.logger.debug(`Polling new messages for discussion with user '${this.userId}'`); - } - - this.fetching = true; - - // Wait for synchronization process to finish. - return this.messagesSync.waitForSyncConversation(this.conversationId, this.userId).then(() => { - // Fetch messages. Invalidate the cache before fetching. - if (this.groupMessagingEnabled) { - return this.messagesProvider.invalidateConversationMessages(this.conversationId).catch(() => { - // Ignore errors. - }).then(() => { - return this.getConversationMessages(this.pagesLoaded); - }); - } else { - return this.messagesProvider.invalidateDiscussionCache(this.userId).catch(() => { - // Ignore errors. - }).then(() => { - return this.getDiscussionMessages(this.pagesLoaded); - }); - } - }).then((messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[]) => { - this.loadMessages(messages, messagesAreNew); - }).finally(() => { - this.fetching = false; - }); - } - - /** - * Format and load a list of messages into the view. - * - * @param messagesAreNew If messages loaded are new messages. - * @param messages Messages to load. - */ - protected loadMessages(messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[], - messagesAreNew: boolean = true): void { - - if (this.viewDestroyed) { - return; - } - - // Don't use domUtils.getScrollHeight because it gives an outdated value after receiving a new message. - const scrollHeight = this.content && this.content.getScrollElement() ? this.content.getScrollElement().scrollHeight : 0; - - // Check if we are at the bottom to scroll it after render. - // Use a 5px error margin because in iOS there is 1px difference for some reason. - this.scrollBottom = Math.abs(scrollHeight - this.domUtils.getScrollTop(this.content) - - this.domUtils.getContentHeight(this.content)) < 5; - - if (this.messagesBeingSent > 0) { - // Ignore polling due to a race condition. - return; - } - - // Add new messages to the list and mark the messages that should still be displayed. - const newMessages = messages.reduce((val, message) => { - return val + (this.addMessage(message) ? 1 : 0); - }, 0); - - // Set the new badges message if we're loading new messages. - if (messagesAreNew) { - this.setNewMessagesBadge(this.newMessages + newMessages); - } - - // Remove messages that shouldn't be in the list anymore. - for (const hash in this.keepMessageMap) { - this.removeMessage(hash); - } - - // Sort the messages. - this.messagesProvider.sortMessages(this.messages); - - // Calculate which messages need to display the date or user data. - this.messages.forEach((message, index) => { - message.showDate = this.showDate(message, this.messages[index - 1]); - message.showUserData = this.showUserData(message, this.messages[index - 1]); - message.showTail = this.showTail(message, this.messages[index + 1]); - }); - - // Call resize to recalculate the dimensions. - this.content && this.content.resize(); - - // If we received a new message while using group messaging, force mark messages as read. - const last = this.messages[this.messages.length - 1], - forceMark = this.groupMessagingEnabled && last && last.useridfrom != this.currentUserId && this.lastMessage.text != '' - && (last.text !== this.lastMessage.text || last.timecreated !== this.lastMessage.timecreated); - - // Notify that there can be a new message. - this.notifyNewMessage(); - - // Mark retrieved messages as read if they are not. - this.markMessagesAsRead(forceMark); - } - - /** - * Set the new message badge number and set scroll listener if needed. - * - * @param addMessages NUmber of messages still to be read. - */ - protected setNewMessagesBadge(addMessages: number): void { - if (this.newMessages == 0 && addMessages > 0) { - // Setup scrolling. - this.content.getScrollElement().addEventListener('scroll', this.scrollListener); - - this.scrollListenerFunction(); - } else if (this.newMessages > 0 && addMessages == 0) { - // Remove scrolling. - this.content.getScrollElement().removeEventListener('scroll', this.scrollListener); - } - - this.newMessages = addMessages; - } - - /** - * The scroll was moved. Update new messages count. - */ - protected scrollListenerFunction(): void { - if (this.newMessages > 0) { - const scrollBottom = this.domUtils.getScrollTop(this.content) + this.domUtils.getContentHeight(this.content); - const scrollHeight = this.domUtils.getScrollHeight(this.content); - if (scrollBottom > scrollHeight - 40) { - // At the bottom, reset. - this.setNewMessagesBadge(0); - - return; - } - - const scrollElRect = this.content.getScrollElement().getBoundingClientRect(); - const scrollBottomPos = (scrollElRect && scrollElRect.bottom) || 0; - - if (scrollBottomPos == 0) { - return; - } - - const messages = Array.from(document.querySelectorAll('.addon-message-not-mine')).slice(-this.newMessages).reverse(); - - const newMessagesUnread = messages.findIndex((message, index) => { - const elementRect = message.getBoundingClientRect(); - if (!elementRect) { - return false; - } - - return elementRect.bottom <= scrollBottomPos; - }); - - if (newMessagesUnread > 0 && newMessagesUnread < this.newMessages) { - this.setNewMessagesBadge(newMessagesUnread); - } - } - } - - /** - * Get the conversation. - * - * @param conversationId Conversation ID. - * @param userId User ID. - * @return Promise resolved with a boolean: whether the conversation exists or not. - */ - protected getConversation(conversationId: number, userId: number): Promise { - let promise: Promise, - fallbackConversation: AddonMessagesConversationFormatted; - - // Try to get the conversationId if we don't have it. - if (conversationId) { - promise = Promise.resolve(conversationId); - } else { - let subPromise: Promise; - - if (userId == this.currentUserId && this.messagesProvider.isSelfConversationEnabled()) { - subPromise = this.messagesProvider.getSelfConversation(); - } else { - subPromise = this.messagesProvider.getConversationBetweenUsers(userId, undefined, true); - } - - promise = subPromise.then((conversation) => { - fallbackConversation = conversation; - - return conversation.id; - }); - } - - return promise.then((conversationId) => { - // Retrieve the conversation. Invalidate data first to get the right unreadcount. - return this.messagesProvider.invalidateConversation(conversationId).catch(() => { - // Ignore errors. - }).then(() => { - return this.messagesProvider.getConversation(conversationId, undefined, true); - }).catch((error): any => { - // Get conversation failed, use the fallback one if we have it. - if (fallbackConversation) { - return fallbackConversation; - } - - return Promise.reject(error); - }).then((conversation: AddonMessagesConversationFormatted) => { - this.conversation = conversation; - - if (conversation) { - this.conversationId = conversation.id; - this.title = conversation.name; - this.conversationImage = conversation.imageurl; - this.isGroup = conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP; - this.favouriteIcon = 'fa-star'; - this.favouriteIconSlash = conversation.isfavourite; - this.muteIcon = conversation.ismuted ? 'volume-up' : 'volume-off'; - if (!this.isGroup) { - this.userId = conversation.userid; - } - this.isSelf = conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_SELF; - - return true; - } else { - return false; - } - }); - }, (error) => { - // Probably conversation does not exist or user is offline. Try to load offline messages. - this.isSelf = userId == this.currentUserId; - - return this.messagesOffline.getMessages(userId).then((messages): any => { - if (messages && messages.length) { - // We have offline messages, this probably means that the conversation didn't exist. Don't display error. - messages.forEach((message) => { - message.pending = true; - message.text = message.smallmessage; - }); - - this.loadMessages(messages); - } else if (error.errorcode != 'errorconversationdoesnotexist') { - // Display the error. - return Promise.reject(error); - } - - return false; - }); - }); - } - - /** - * Get the messages of the conversation. Used if group messaging is supported. - * - * @param pagesToLoad Number of "pages" to load. - * @param offset Offset for message list. - * @return Promise resolved with the list of messages. - */ - protected async getConversationMessages(pagesToLoad: number, offset: number = 0) - : Promise { - - const excludePending = offset > 0; - - const result = await this.messagesProvider.getConversationMessages(this.conversationId, { - excludePending: excludePending, - limitFrom: offset, - }); - - pagesToLoad--; - - // Treat members. Don't use CoreUtilsProvider.arrayToObject because we don't want to override the existing object. - if (result.members) { - result.members.forEach((member) => { - this.members[member.id] = member; - }); - } - - if (pagesToLoad > 0 && result.canLoadMore) { - offset += AddonMessagesProvider.LIMIT_MESSAGES; - - // Get more messages. - const nextMessages = await this.getConversationMessages(pagesToLoad, offset); - - return result.messages.concat(nextMessages); - } else { - // No more messages to load, return them. - this.canLoadMore = result.canLoadMore; - - return result.messages; - } - } - - /** - * Get a discussion. Can load several "pages". - * - * @param pagesToLoad Number of pages to load. - * @param lfReceivedUnread Number of unread received messages already fetched, so fetch will be done from this. - * @param lfReceivedRead Number of read received messages already fetched, so fetch will be done from this. - * @param lfSentUnread Number of unread sent messages already fetched, so fetch will be done from this. - * @param lfSentRead Number of read sent messages already fetched, so fetch will be done from this. - * @return Resolved when done. - */ - protected getDiscussionMessages(pagesToLoad: number, lfReceivedUnread: number = 0, lfReceivedRead: number = 0, - lfSentUnread: number = 0, lfSentRead: number = 0): Promise { - - // Only get offline messages if we're loading the first "page". - const excludePending = lfReceivedUnread > 0 || lfReceivedRead > 0 || lfSentUnread > 0 || lfSentRead > 0; - - // Get next messages. - return this.messagesProvider.getDiscussion(this.userId, excludePending, lfReceivedUnread, lfReceivedRead, lfSentUnread, - lfSentRead).then((result) => { - - pagesToLoad--; - if (pagesToLoad > 0 && result.canLoadMore) { - // More pages to load. Calculate new limit froms. - result.messages.forEach((message: AddonMessagesGetMessagesMessageFormatted) => { - if (!message.pending) { - if (message.useridfrom == this.userId) { - if (message.read) { - lfReceivedRead++; - } else { - lfReceivedUnread++; - } - } else { - if (message.read) { - lfSentRead++; - } else { - lfSentUnread++; - } - } - } - }); - - // Get next messages. - return this.getDiscussionMessages(pagesToLoad, lfReceivedUnread, lfReceivedRead, lfSentUnread, lfSentRead) - .then((nextMessages) => { - return result.messages.concat(nextMessages); - }); - } else { - // No more messages to load, return them. - this.canLoadMore = result.canLoadMore; - - return result.messages; - } - }); - } - - /** - * Mark messages as read. - */ - protected markMessagesAsRead(forceMark: boolean): void { - let readChanged = false; - const promises = []; - - if (this.messagesProvider.isMarkAllMessagesReadEnabled()) { - let messageUnreadFound = false; - - // Mark all messages at a time if there is any unread message. - if (forceMark) { - messageUnreadFound = true; - } else if (this.groupMessagingEnabled) { - messageUnreadFound = this.conversation && this.conversation.unreadcount > 0 && this.conversationId > 0; - } else { - for (const x in this.messages) { - const message = this.messages[x]; - // If an unread message is found, mark all messages as read. - if (message.useridfrom != this.currentUserId && - ( message).read == 0) { - messageUnreadFound = true; - break; - } - } - } - - if (messageUnreadFound) { - this.setUnreadLabelPosition(); - - let promise; - - if (this.groupMessagingEnabled) { - promise = this.messagesProvider.markAllConversationMessagesRead(this.conversationId); - } else { - promise = this.messagesProvider.markAllMessagesRead(this.userId).then(() => { - // Mark all messages as read. - this.messages.forEach((message) => { - ( message).read = 1; - }); - }); - } - - promises.push(promise.then(() => { - readChanged = true; - })); - } - } else { - this.setUnreadLabelPosition(); - // Mark each message as read one by one. - this.messages.forEach((message) => { - // If the message is unread, call this.messagesProvider.markMessageRead. - if (message.useridfrom != this.currentUserId && ( message).read == 0) { - promises.push(this.messagesProvider.markMessageRead(message.id).then(() => { - readChanged = true; - ( message).read = 1; - })); - } - }); - } - - Promise.all(promises).finally(() => { - if (readChanged) { - this.eventsProvider.trigger(AddonMessagesProvider.READ_CHANGED_EVENT, { - conversationId: this.conversationId, - userId: this.userId - }, this.siteId); - } - }); - } - - /** - * Notify the last message found so discussions list controller can tell if last message should be updated. - */ - protected notifyNewMessage(): void { - const last = this.messages[this.messages.length - 1]; - - let trigger = false; - - if (!last) { - this.lastMessage = {text: '', timecreated: 0}; - trigger = true; - } else if (last.text !== this.lastMessage.text || last.timecreated !== this.lastMessage.timecreated) { - this.lastMessage = {text: last.text, timecreated: last.timecreated}; - trigger = true; - } - - if (trigger) { - // Update discussions last message. - this.eventsProvider.trigger(AddonMessagesProvider.NEW_MESSAGE_EVENT, { - conversationId: this.conversationId, - userId: this.userId, - message: this.lastMessage.text, - timecreated: this.lastMessage.timecreated, - isfavourite: this.conversation && this.conversation.isfavourite, - type: this.conversation && this.conversation.type - }, this.siteId); - - // Update navBar links and buttons. - const newCanDelete = (last && last.id && this.messages.length == 1) || this.messages.length > 1; - if (this.canDelete != newCanDelete) { - this.checkCanDelete(); - } - } - } - - /** - * Set the place where the unread label position has to be. - */ - protected setUnreadLabelPosition(): void { - if (this.unreadMessageFrom != 0) { - return; - } - - if (this.groupMessagingEnabled) { - // Use the unreadcount from the conversation to calculate where should the label be placed. - if (this.conversation && this.conversation.unreadcount > 0 && this.messages) { - // Iterate over messages to find the right message using the unreadcount. Skip offline messages and own messages. - let found = 0; - - for (let i = this.messages.length - 1; i >= 0; i--) { - const message = this.messages[i]; - if (!message.pending && message.useridfrom != this.currentUserId) { - found++; - if (found == this.conversation.unreadcount) { - this.unreadMessageFrom = Number(message.id); - break; - } - } - } - } - } else { - let previousMessageRead = false; - - for (const x in this.messages) { - const message = this.messages[x]; - if (message.useridfrom != this.currentUserId) { - const unreadFrom = message.read == 0 && previousMessageRead; - - if (unreadFrom) { - // Save where the label is placed. - this.unreadMessageFrom = Number(message.id); - break; - } - - previousMessageRead = message.read != 0; - } - } - } - - // Do not update the message unread from label on next refresh. - if (this.unreadMessageFrom == 0) { - // Using negative to indicate the label is not placed but should not be placed. - this.unreadMessageFrom = -1; - } - } - - /** - * Check if there's any message in the list that can be deleted. - */ - protected checkCanDelete(): void { - // All messages being sent should be at the end of the list. - const first = this.messages[0]; - this.canDelete = first && !first.sending; - } - - /** - * Hide unread label when sending messages. - */ - protected hideUnreadLabel(): void { - if (this.unreadMessageFrom > 0) { - this.unreadMessageFrom = -1; - } - } - - /** - * Wait until fetching is false. - * @return Resolved when done. - */ - protected waitForFetch(): Promise { - if (!this.fetching) { - return Promise.resolve(); - } - - const deferred = this.utils.promiseDefer(); - - setTimeout(() => { - return this.waitForFetch().finally(() => { - deferred.resolve(); - }); - }, 400); - - return deferred.promise; - } - - /** - * Set a polling to get new messages every certain time. - */ - protected setPolling(): void { - if (this.groupMessagingEnabled && !this.conversationId) { - // Don't have enough data to poll messages. - return; - } - - if (!this.polling) { - // Start polling. - this.polling = setInterval(() => { - this.fetchMessages().catch(() => { - // Ignore errors. - }); - }, AddonMessagesProvider.POLL_INTERVAL); - } - } - - /** - * Unset polling. - */ - protected unsetPolling(): void { - if (this.polling) { - this.logger.debug(`Cancelling polling for conversation with user '${this.userId}'`); - clearInterval(this.polling); - this.polling = undefined; - } - } - - /** - * Copy message to clipboard. - * - * @param message Message to be copied. - */ - copyMessage(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): void { - const text = this.textUtils.decodeHTMLEntities( - ( message).smallmessage || message.text || ''); - this.utils.copyToClipboard(text); - } - - /** - * Function to delete a message. - * - * @param message Message object to delete. - * @param index Index where the message is to delete it from the view. - */ - deleteMessage(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted, index: number) - : void { - - const canDeleteAll = this.conversation && this.conversation.candeletemessagesforallusers, - langKey = message.pending || canDeleteAll || this.isSelf ? 'core.areyousure' : - 'addon.messages.deletemessageconfirmation', - options: any = {}; - - if (canDeleteAll && !message.pending) { - // Show delete for all checkbox. - options.inputs = [{ - type: 'checkbox', - name: 'deleteforall', - checked: false, - value: true, - label: this.translate.instant('addon.messages.deleteforeveryone') - }]; - } - - this.domUtils.showConfirm(this.translate.instant(langKey), undefined, undefined, undefined, options).then((data) => { - const modal = this.domUtils.showModalLoading('core.deleting', true); - - return this.messagesProvider.deleteMessage(message, data && data[0]).then(() => { - // Remove message from the list without having to wait for re-fetch. - this.messages.splice(index, 1); - this.removeMessage(message.hash); - this.notifyNewMessage(); - - this.fetchMessages(); // Re-fetch messages to update cached data. - }).finally(() => { - modal.dismiss(); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errordeletemessage', true); - }); - } - - /** - * Function to load previous messages. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Resolved when done. - */ - loadPrevious(infiniteComplete?: any): Promise { - let infiniteHeight = this.infinite ? this.infinite.getHeight() : 0; - const scrollHeight = this.domUtils.getScrollHeight(this.content); - - // If there is an ongoing fetch, wait for it to finish. - return this.waitForFetch().finally(() => { - this.pagesLoaded++; - - this.fetchMessages(false).then(() => { - - // Try to keep the scroll position. - const scrollBottom = scrollHeight - this.domUtils.getScrollTop(this.content); - - if (this.canLoadMore && infiniteHeight && this.infinite) { - // The height of the infinite is different while spinner is shown. Add that difference. - infiniteHeight = infiniteHeight - this.infinite.getHeight(); - } else if (!this.canLoadMore) { - // Can't load more, take into account the full height of the infinite loading since it will disappear now. - infiniteHeight = infiniteHeight || (this.infinite ? this.infinite.getHeight() : 0); - } - - this.keepScroll(scrollHeight, scrollBottom, infiniteHeight); - }).catch((error) => { - this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading. - this.pagesLoaded--; - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true); - }).finally(() => { - infiniteComplete && infiniteComplete(); - }); - }); - } - - /** - * Keep scroll position after loading previous messages. - * We don't use resizeContent because the approach used is different and it isn't easy to calculate these positions. - */ - protected keepScroll(oldScrollHeight: number, oldScrollBottom: number, infiniteHeight: number, retries?: number): void { - retries = retries || 0; - - setTimeout(() => { - const newScrollHeight = this.domUtils.getScrollHeight(this.content); - - if (newScrollHeight == oldScrollHeight) { - // Height hasn't changed yet. Retry if max retries haven't been reached. - if (retries <= 10) { - this.keepScroll(oldScrollHeight, oldScrollBottom, infiniteHeight, retries + 1); - } - - return; - } - - const scrollTo = newScrollHeight - oldScrollBottom + infiniteHeight; - - this.domUtils.scrollTo(this.content, 0, scrollTo, 0); - }, 30); - } - - /** - * Content or scroll has been resized. For content, only call it if it's been added on top. - */ - resizeContent(): void { - let top = this.content.getContentDimensions().scrollTop; - this.content.resize(); - - // Wait for new content height to be calculated. - setTimeout(() => { - // Visible content size changed, maintain the bottom position. - if (!this.viewDestroyed && this.content && this.domUtils.getContentHeight(this.content) != this.oldContentHeight) { - if (!top) { - top = this.content.getContentDimensions().scrollTop; - } - - top += this.oldContentHeight - this.domUtils.getContentHeight(this.content); - this.oldContentHeight = this.domUtils.getContentHeight(this.content); - - this.domUtils.scrollTo(this.content, 0, top, 0); - } - }); - } - - /** - * Scroll bottom when render has finished. - */ - scrollToBottom(): void { - // Check if scroll is at bottom. If so, scroll bottom after rendering since there might be something new. - if (this.scrollBottom) { - // Need a timeout to leave time to the view to be rendered. - setTimeout(() => { - if (!this.viewDestroyed) { - this.domUtils.scrollToBottom(this.content, 0); - } - }); - this.scrollBottom = false; - - // Reset the badge. - this.setNewMessagesBadge(0); - } - } - - /** - * Scroll to the first new unread message. - */ - scrollToFirstUnreadMessage(): void { - if (this.newMessages > 0) { - const messages = Array.from(document.querySelectorAll('.addon-message-not-mine')); - - this.domUtils.scrollToElement(this.content, messages[messages.length - this.newMessages]); - } - } - - /** - * Sends a message to the server. - * - * @param text Message text. - */ - sendMessage(text: string): void { - let message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted; - - this.hideUnreadLabel(); - - this.showDelete = false; - this.scrollBottom = true; - this.setNewMessagesBadge(0); - - message = { - id: null, - pending: true, - sending: true, - useridfrom: this.currentUserId, - smallmessage: text, - text: text, - timecreated: new Date().getTime() - }; - message.showDate = this.showDate(message, this.messages[this.messages.length - 1]); - this.addMessage(message, false); - - this.messagesBeingSent++; - - // If there is an ongoing fetch, wait for it to finish. - // Otherwise, if a message is sent while fetching it could disappear until the next fetch. - this.waitForFetch().finally(() => { - let promise: Promise<{sent: boolean, message: any}>; - - if (this.conversationId) { - promise = this.messagesProvider.sendMessageToConversation(this.conversation, text); - } else { - promise = this.messagesProvider.sendMessage(this.userId, text); - } - - promise.then((data) => { - let promise; - - this.messagesBeingSent--; - - if (data.sent) { - if (!this.conversationId && data.message && data.message.conversationid) { - // Message sent to a new conversation, try to load the conversation. - promise = this.getConversation(data.message.conversationid, this.userId).then(() => { - // Now fetch messages. - return this.fetchMessages(); - }).finally(() => { - // Start polling messages now that the conversation exists. - this.setPolling(); - }); - } else { - // Message was sent, fetch messages right now. - promise = this.fetchMessages(); - } - } else { - promise = Promise.reject(null); - } - - promise.catch(() => { - // Fetch failed or is offline message, mark the message as sent. - // If fetch is successful there's no need to mark it because the fetch will already show the message received. - message.sending = false; - if (data.sent) { - // Message sent to server, not pending anymore. - message.pending = false; - } else if (data.message) { - message.timecreated = data.message.timecreated; - } - - this.notifyNewMessage(); - }); - }).catch((error) => { - this.messagesBeingSent--; - - // Only close the keyboard if an error happens. - // We want the user to be able to send multiple messages without the keyboard being closed. - this.appProvider.closeKeyboard(); - - this.domUtils.showErrorModalDefault(error, 'addon.messages.messagenotsent', true); - this.removeMessage(message.hash); - }); - }); - } - - /** - * Check date should be shown on message list for the current message. - * If date has changed from previous to current message it should be shown. - * - * @param message Current message where to show the date. - * @param prevMessage Previous message where to compare the date with. - * @return If date has changed and should be shown. - */ - showDate(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted, - prevMessage?: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): boolean { - - if (!prevMessage) { - // First message, show it. - return true; - } - - // Check if day has changed. - return !moment(message.timecreated).isSame(prevMessage.timecreated, 'day'); - } - - /** - * Check if the user info should be displayed for the current message. - * User data is only displayed for group conversations if the previous message was from another user. - * - * @param message Current message where to show the user info. - * @param prevMessage Previous message. - * @return Whether user data should be shown. - */ - showUserData(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted, - prevMessage?: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): boolean { - - return this.isGroup && message.useridfrom != this.currentUserId && this.members[message.useridfrom] && - (!prevMessage || prevMessage.useridfrom != message.useridfrom || message.showDate); - } - - /** - * Check if a css tail should be shown. - * - * @param message Current message where to show the user info. - * @param nextMessage Next message. - * @return Whether user data should be shown. - */ - showTail(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted, - nextMessage?: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): boolean { - return !nextMessage || nextMessage.useridfrom != message.useridfrom || nextMessage.showDate; - } - - /** - * Toggles delete state. - */ - toggleDelete(): void { - this.showDelete = !this.showDelete; - } - - /** - * View info. If it's an individual conversation, go to the user profile. - * If it's a group conversation, view info about the group. - */ - viewInfo(): void { - if (this.isGroup) { - // Display the group information. - const modal = this.modalCtrl.create('AddonMessagesConversationInfoPage', { - conversationId: this.conversationId - }); - - modal.present(); - modal.onDidDismiss((userId) => { - if (typeof userId != 'undefined') { - // Open user conversation. - if (this.svComponent) { - // Notify the left pane to load it, this way the right conversation will be highlighted. - this.eventsProvider.trigger(AddonMessagesProvider.OPEN_CONVERSATION_EVENT, {userId: userId}, this.siteId); - } else { - // Open the discussion in a new view. - this.navCtrl.push('AddonMessagesDiscussionPage', {userId: userId}); - } - } - }); - } else { - // Open the user profile. - const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - navCtrl.push('CoreUserProfilePage', { userId: this.userId }); - } - } - - /** - * Change the favourite state of the current conversation. - * - * @param done Function to call when done. - */ - changeFavourite(done?: () => void): void { - this.favouriteIcon = 'spinner'; - - this.messagesProvider.setFavouriteConversation(this.conversation.id, !this.conversation.isfavourite).then(() => { - this.conversation.isfavourite = !this.conversation.isfavourite; - - // Get the conversation data so it's cached. Don't block the user for this. - this.messagesProvider.getConversation(this.conversation.id, undefined, true); - - this.eventsProvider.trigger(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, { - conversationId: this.conversation.id, - action: 'favourite', - value: this.conversation.isfavourite - }, this.siteId); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error changing favourite state.'); - }).finally(() => { - this.favouriteIcon = 'fa-star'; - this.favouriteIconSlash = this.conversation.isfavourite; - done && done(); - }); - } - - /** - * Change the mute state of the current conversation. - * - * @param done Function to call when done. - */ - changeMute(done?: () => void): void { - this.muteIcon = 'spinner'; - - this.messagesProvider.muteConversation(this.conversation.id, !this.conversation.ismuted).then(() => { - this.conversation.ismuted = !this.conversation.ismuted; - - // Get the conversation data so it's cached. Don't block the user for this. - this.messagesProvider.getConversation(this.conversation.id, undefined, true); - - this.eventsProvider.trigger(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, { - conversationId: this.conversation.id, - action: 'mute', - value: this.conversation.ismuted - }, this.siteId); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error changing muted state.'); - }).finally(() => { - this.muteIcon = this.conversation.ismuted ? 'volume-up' : 'volume-off'; - done && done(); - }); - } - - /** - * Calculate whether there are pending contact requests. - */ - protected setContactRequestInfo(): void { - this.requestContactSent = false; - this.requestContactReceived = false; - if (this.otherMember && !this.otherMember.iscontact) { - this.requestContactSent = this.otherMember.contactrequests.some((request) => { - return request.userid == this.currentUserId && request.requesteduserid == this.otherMember.id; - }); - this.requestContactReceived = this.otherMember.contactrequests.some((request) => { - return request.userid == this.otherMember.id && request.requesteduserid == this.currentUserId; - }); - } - } - - /** - * Calculate what to display in the footer. - */ - protected setFooterType(): void { - if (!this.otherMember) { - // Group conversation or group messaging not available. - this.footerType = 'message'; - } else if (this.otherMember.isblocked) { - this.footerType = 'blocked'; - } else if (this.requestContactReceived) { - this.footerType = 'requestReceived'; - } else if (this.otherMember.canmessage) { - this.footerType = 'message'; - } else if (this.requestContactSent) { - this.footerType = 'requestSent'; - } else if (this.otherMember.requirescontact) { - this.footerType = 'requiresContact'; - } else { - this.footerType = 'unable'; - } - } - - /** - * Displays a confirmation modal to block the user of the individual conversation. - * - * @return Promise resolved when user is blocked or dialog is cancelled. - */ - blockUser(): Promise { - if (!this.otherMember) { - // Should never happen. - return Promise.reject(null); - } - - const template = this.translate.instant('addon.messages.blockuserconfirm', {$a: this.otherMember.fullname}); - const okText = this.translate.instant('addon.messages.blockuser'); - - return this.domUtils.showConfirm(template, undefined, okText).then(() => { - this.blockIcon = 'spinner'; - - const modal = this.domUtils.showModalLoading('core.sending', true); - this.showLoadingModal = true; - - return this.messagesProvider.blockContact(this.otherMember.id).finally(() => { - modal.dismiss(); - this.showLoadingModal = false; - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }).finally(() => { - this.blockIcon = this.otherMember.isblocked ? 'close-circle' : 'checkmark-circle'; - }); - } - - /** - * Delete the conversation. - * - * @param done Function to call when done. - */ - deleteConversation(done?: () => void): void { - const confirmMessage = 'addon.messages.' + (this.isSelf ? 'deleteallselfconfirm' : 'deleteallconfirm'); - - this.domUtils.showDeleteConfirm(confirmMessage).then(() => { - this.deleteIcon = 'spinner'; - - return this.messagesProvider.deleteConversation(this.conversation.id).then(() => { - this.eventsProvider.trigger(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, { - conversationId: this.conversation.id, - action: 'delete' - }, this.siteId); - - this.messages = []; - }).finally(() => { - this.deleteIcon = 'trash'; - done && done(); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error deleting conversation.'); - }); - } - - /** - * Displays a confirmation modal to unblock the user of the individual conversation. - * - * @return Promise resolved when user is unblocked or dialog is cancelled. - */ - unblockUser(): Promise { - if (!this.otherMember) { - // Should never happen. - return Promise.reject(null); - } - - const template = this.translate.instant('addon.messages.unblockuserconfirm', {$a: this.otherMember.fullname}); - const okText = this.translate.instant('addon.messages.unblockuser'); - - return this.domUtils.showConfirm(template, undefined, okText).then(() => { - this.blockIcon = 'spinner'; - - const modal = this.domUtils.showModalLoading('core.sending', true); - this.showLoadingModal = true; - - return this.messagesProvider.unblockContact(this.otherMember.id).finally(() => { - modal.dismiss(); - this.showLoadingModal = false; - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }).finally(() => { - this.blockIcon = this.otherMember.isblocked ? 'close-circle' : 'checkmark-circle'; - }); - } - - /** - * Displays a confirmation modal to send a contact request to the other user of the individual conversation. - * - * @return Promise resolved when the request is sent or the dialog is cancelled. - */ - createContactRequest(): Promise { - if (!this.otherMember) { - // Should never happen. - return Promise.reject(null); - } - - const template = this.translate.instant('addon.messages.addcontactconfirm', { $a: this.otherMember.fullname }); - const okText = this.translate.instant('core.add'); - - return this.domUtils.showConfirm(template, undefined, okText).then(() => { - this.addRemoveIcon = 'spinner'; - - const modal = this.domUtils.showModalLoading('core.sending', true); - this.showLoadingModal = true; - - return this.messagesProvider.createContactRequest(this.otherMember.id).finally(() => { - modal.dismiss(); - this.showLoadingModal = false; - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }).finally(() => { - this.addRemoveIcon = 'person'; - }); - } - - /** - * Confirms the contact request of the other user of the individual conversation. - * - * @return Promise resolved when the request is confirmed. - */ - confirmContactRequest(): Promise { - if (!this.otherMember) { - // Should never happen. - return Promise.reject(null); - } - - const modal = this.domUtils.showModalLoading('core.sending', true); - this.showLoadingModal = true; - - return this.messagesProvider.confirmContactRequest(this.otherMember.id).finally(() => { - modal.dismiss(); - this.showLoadingModal = false; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }); - } - - /** - * Declines the contact request of the other user of the individual conversation. - * - * @return Promise resolved when the request is confirmed. - */ - declineContactRequest(): Promise { - if (!this.otherMember) { - // Should never happen. - return Promise.reject(null); - } - - const modal = this.domUtils.showModalLoading('core.sending', true); - this.showLoadingModal = true; - - return this.messagesProvider.declineContactRequest(this.otherMember.id).finally(() => { - modal.dismiss(); - this.showLoadingModal = false; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }); - } - - /** - * Displays a confirmation modal to remove the other user of the conversation from contacts. - * - * @return Promise resolved when the request is sent or the dialog is cancelled. - */ - removeContact(): Promise { - if (!this.otherMember) { - // Should never happen. - return Promise.reject(null); - } - - const template = this.translate.instant('addon.messages.removecontactconfirm', { $a: this.otherMember.fullname }); - const okText = this.translate.instant('core.remove'); - - return this.domUtils.showConfirm(template, undefined, okText).then(() => { - this.addRemoveIcon = 'spinner'; - - const modal = this.domUtils.showModalLoading('core.sending', true); - this.showLoadingModal = true; - - return this.messagesProvider.removeContact(this.otherMember.id).finally(() => { - modal.dismiss(); - this.showLoadingModal = false; - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }).finally(() => { - this.addRemoveIcon = 'person'; - }); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - // Unset again, just in case. - this.unsetPolling(); - this.syncObserver && this.syncObserver.off(); - this.keyboardObserver && this.keyboardObserver.off(); - this.memberInfoObserver && this.memberInfoObserver.off(); - this.viewDestroyed = true; - } -} - -/** - * Conversation message with some calculated data. - */ -type AddonMessagesConversationMessageFormatted = AddonMessagesConversationMessage & { - pending?: boolean; // Calculated in the app. Whether the message is pending to be sent. - sending?: boolean; // Calculated in the app. Whether the message is being sent right now. - hash?: string; // Calculated in the app. A hash to identify the message. - showDate?: boolean; // Calculated in the app. Whether to show the date before the message. - showUserData?: boolean; // Calculated in the app. Whether to show the user data in the message. - showTail?: boolean; // Calculated in the app. Whether to show a "tail" in the message. -}; - -/** - * Message with some calculated data. - */ -type AddonMessagesGetMessagesMessageFormatted = AddonMessagesGetMessagesMessage & { - sending?: boolean; // Calculated in the app. Whether the message is being sent right now. - hash?: string; // Calculated in the app. A hash to identify the message. - showDate?: boolean; // Calculated in the app. Whether to show the date before the message. - showUserData?: boolean; // Calculated in the app. Whether to show the user data in the message. - showTail?: boolean; // Calculated in the app. Whether to show a "tail" in the message. -}; diff --git a/src/addon/messages/pages/group-conversations/group-conversations.html b/src/addon/messages/pages/group-conversations/group-conversations.html deleted file mode 100644 index 4f2405bb4..000000000 --- a/src/addon/messages/pages/group-conversations/group-conversations.html +++ /dev/null @@ -1,117 +0,0 @@ - - - {{ 'addon.messages.messages' | translate }} - - - - - - - - - - - - - - - - - - -

{{ 'addon.messages.contacts' | translate }}

- {{contactRequestsCount}} -
- - - - - {{ 'core.favourites' | translate }} ({{ favourites.count }}) - {{ favourites.unread }} - -
- - - - -

{{ 'addon.messages.nofavourites' | translate }}

-
-
- - - - - - - - - {{ 'addon.messages.groupconversations' | translate }} ({{ group.count }}) - {{ group.unread }} - -
- - - - -

{{ 'addon.messages.nogroupconversations' | translate }}

-
-
- - - - - - - - {{ 'addon.messages.individualconversations' | translate }} ({{ individual.count }}) - {{ individual.unread }} - -
- - - - -

{{ 'addon.messages.noindividualconversations' | translate }}

-
-
- - - - -
-
-
-
- - - - - - - - - - - - -

- - - -

- - {{ conversation.unreadcount }} - {{conversation.lastmessagedate | coreDateDayOrTime}} - -

-

- {{ 'addon.messages.you' | translate }} - {{ conversation.members[0].fullname + ':' }} - -

-
-
diff --git a/src/addon/messages/pages/group-conversations/group-conversations.module.ts b/src/addon/messages/pages/group-conversations/group-conversations.module.ts deleted file mode 100644 index ba801e991..000000000 --- a/src/addon/messages/pages/group-conversations/group-conversations.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonMessagesGroupConversationsPage } from './group-conversations'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; - -@NgModule({ - declarations: [ - AddonMessagesGroupConversationsPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonMessagesGroupConversationsPage), - TranslateModule.forChild() - ], -}) -export class AddonMessagesGroupConversationsPageModule {} diff --git a/src/addon/messages/pages/group-conversations/group-conversations.scss b/src/addon/messages/pages/group-conversations/group-conversations.scss deleted file mode 100644 index 517363205..000000000 --- a/src/addon/messages/pages/group-conversations/group-conversations.scss +++ /dev/null @@ -1,48 +0,0 @@ -ion-app.app-root .addon-messages-conversation-item, -ion-app.app-root .addon-message-discussion { - h2 { - core-format-text { - font-weight: bold; - } - - ion-icon { - @include margin(null, null, null, 2px); - } - } - - .note { - position: absolute; - @include position(0, 0, null, null); - margin: 4px 8px; - font-size: 1.3rem; - } - - .addon-message-last-message { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - } - - .addon-message-last-message-user { - white-space: nowrap; - color: $black; - @include margin(null, 2px, null, null); - @include darkmode() { - color: $white; - } - } - - .addon-message-last-message-text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex-shrink: 1; - } -} - -ion-app.app-root .addon-message-discussion { - h2 { - margin-top: 10px; - } -} \ No newline at end of file diff --git a/src/addon/messages/pages/group-conversations/group-conversations.ts b/src/addon/messages/pages/group-conversations/group-conversations.ts deleted file mode 100644 index ececc1ac2..000000000 --- a/src/addon/messages/pages/group-conversations/group-conversations.ts +++ /dev/null @@ -1,743 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, Platform, NavController, NavParams, Content } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { - AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMessage -} from '../../providers/messages'; -import { AddonMessagesOfflineProvider } from '../../providers/messages-offline'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreUserProvider } from '@core/user/providers/user'; - -/** - * Page that displays the list of conversations, including group conversations. - */ -@IonicPage({ segment: 'addon-messages-group-conversations' }) -@Component({ - selector: 'page-addon-messages-group-conversations', - templateUrl: 'group-conversations.html', -}) -export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - @ViewChild(Content) content: Content; - @ViewChild('favlist') favListEl: ElementRef; - @ViewChild('grouplist') groupListEl: ElementRef; - @ViewChild('indlist') indListEl: ElementRef; - - loaded = false; - loadingMessage: string; - selectedConversationId: number; - selectedUserId: number; - contactRequestsCount = 0; - favourites: AddonMessagesGroupConversationOption = { - type: null, - favourites: true, - count: 0, - unread: 0, - }; - group: AddonMessagesGroupConversationOption = { - type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP, - favourites: false, - count: 0, - unread: 0 - }; - individual: AddonMessagesGroupConversationOption = { - type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, - favourites: false, - count: 0, - unread: 0 - }; - typeGroup = AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP; - currentListEl: HTMLElement; - - protected loadingString: string; - protected siteId: string; - protected currentUserId: number; - protected conversationId: number; - protected discussionUserId: number; - protected newMessagesObserver: any; - protected pushObserver: any; - protected appResumeSubscription: any; - protected readChangedObserver: any; - protected cronObserver: any; - protected openConversationObserver: any; - protected updateConversationListObserver: any; - protected contactRequestsCountObserver: any; - protected memberInfoObserver: any; - - constructor(eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService, - private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams, - private navCtrl: NavController, platform: Platform, private utils: CoreUtilsProvider, - pushNotificationsDelegate: CorePushNotificationsDelegate, private messagesOffline: AddonMessagesOfflineProvider, - private userProvider: CoreUserProvider) { - - this.loadingString = translate.instant('core.loading'); - this.siteId = sitesProvider.getCurrentSiteId(); - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - // Conversation to load. - this.conversationId = navParams.get('conversationId') || false; - this.discussionUserId = !this.conversationId && (navParams.get('discussionUserId') || false); - - // Update conversations when new message is received. - this.newMessagesObserver = eventsProvider.on(AddonMessagesProvider.NEW_MESSAGE_EVENT, (data) => { - // Check if the new message belongs to the option that is currently expanded. - const expandedOption = this.getExpandedOption(), - messageOption = this.getConversationOption(data); - - if (expandedOption != messageOption) { - return; // Message doesn't belong to current list, stop. - } - - // Search the conversation to update. - const conversation = this.findConversation(data.conversationId, data.userId, expandedOption); - - if (typeof conversation == 'undefined') { - // Probably a new conversation, refresh the list. - this.loaded = false; - this.refreshData().finally(() => { - this.loaded = true; - }); - } else if (conversation.lastmessage != data.message || conversation.lastmessagedate != data.timecreated / 1000) { - const isNewer = data.timecreated / 1000 > conversation.lastmessagedate; - - // An existing conversation has a new message, update the last message. - conversation.lastmessage = data.message; - conversation.lastmessagedate = data.timecreated / 1000; - - // Sort the affected list. - const option = this.getConversationOption(conversation); - option.conversations = this.messagesProvider.sortConversations(option.conversations); - - if (isNewer) { - // The last message is newer than the previous one, scroll to top to keep viewing the conversation. - this.domUtils.scrollToTop(this.content); - } - } - }, this.siteId); - - // Update conversations when a message is read. - this.readChangedObserver = eventsProvider.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data) => { - if (data.conversationId) { - const conversation = this.findConversation(data.conversationId); - - if (typeof conversation != 'undefined') { - // A conversation has been read reset counter. - conversation.unreadcount = 0; - - // Conversations changed, invalidate them and refresh unread counts. - this.messagesProvider.invalidateConversations(this.siteId); - this.messagesProvider.refreshUnreadConversationCounts(this.siteId); - } - } - }, this.siteId); - - // Load a discussion if we receive an event to do so. - this.openConversationObserver = eventsProvider.on(AddonMessagesProvider.OPEN_CONVERSATION_EVENT, (data) => { - if (data.conversationId || data.userId) { - this.gotoConversation(data.conversationId, data.userId); - } - }, this.siteId); - - // Refresh the view when the app is resumed. - this.appResumeSubscription = platform.resume.subscribe(() => { - if (!this.loaded) { - return; - } - this.loaded = false; - this.refreshData().finally(() => { - this.loaded = true; - }); - }); - - // Update conversations if we receive an event to do so. - this.updateConversationListObserver = eventsProvider.on(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, (data) => { - if (data && data.action == 'mute') { - // If the conversation is displayed, change its muted value. - const expandedOption = this.getExpandedOption(); - - if (expandedOption && expandedOption.conversations) { - const conversation = this.findConversation(data.conversationId, undefined, expandedOption); - if (conversation) { - conversation.ismuted = data.value; - } - } - - return; - } - - this.refreshData(); - - }, this.siteId); - - // If a message push notification is received, refresh the view. - this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => { - // New message received. If it's from current site, refresh the data. - if (utils.isFalseOrZero(notification.notif) && notification.site == this.siteId) { - // Don't refresh unread counts, it's refreshed from the main menu handler in this case. - this.refreshData(null, false); - } - }); - - // Update unread conversation counts. - this.cronObserver = eventsProvider.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, (data) => { - this.favourites.unread = data.favourites; - this.individual.unread = data.individual + data.self; // Self is only returned if it's not favourite. - this.group.unread = data.group; - }, this.siteId); - - // Update the contact requests badge. - this.contactRequestsCountObserver = eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { - this.contactRequestsCount = data.count; - }, this.siteId); - - // Update block status of a user. - this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => { - if (!data.userBlocked && !data.userUnblocked) { - // The block status has not changed, ignore. - return; - } - - const expandedOption = this.getExpandedOption(); - if (expandedOption == this.individual || expandedOption == this.favourites) { - if (!expandedOption.conversations || expandedOption.conversations.length <= 0) { - return; - } - - const conversation = this.findConversation(undefined, data.userId, expandedOption); - if (conversation) { - conversation.isblocked = data.userBlocked; - } - } - }, this.siteId); - } - - /** - * Component loaded. - */ - ngOnInit(): void { - if (this.conversationId || this.discussionUserId) { - // There is a discussion to load, open the discussion in a new state. - this.gotoConversation(this.conversationId, this.discussionUserId); - } - - this.fetchData().then(() => { - if (!this.conversationId && !this.discussionUserId && this.splitviewCtrl.isOn()) { - // Load the first conversation. - let conversation; - const expandedOption = this.getExpandedOption(); - - if (expandedOption) { - conversation = expandedOption.conversations[0]; - } - - if (conversation) { - this.gotoConversation(conversation.id); - } - } - }); - } - - /** - * Fetch conversations. - * - * @param refreshUnreadCounts Whether to refresh unread counts. - * @return Promise resolved when done. - */ - protected fetchData(refreshUnreadCounts: boolean = true): Promise { - this.loadingMessage = this.loadingString; - - // Load the amount of conversations and contact requests. - const promises = []; - - promises.push(this.fetchConversationCounts()); - - // View updated by the events observers. - promises.push(this.messagesProvider.getContactRequestsCount(this.siteId)); - if (refreshUnreadCounts) { - promises.push(this.messagesProvider.refreshUnreadConversationCounts(this.siteId)); - } - - return Promise.all(promises).then(() => { - if (typeof this.favourites.expanded == 'undefined') { - // The expanded status hasn't been initialized. Do it now. - if (this.conversationId || this.discussionUserId) { - // A certain conversation should be opened. - // We don't know which option it belongs to, so we need to fetch the data for all of them. - const promises = []; - - promises.push(this.fetchDataForOption(this.favourites, false)); - promises.push(this.fetchDataForOption(this.group, false)); - promises.push(this.fetchDataForOption(this.individual, false)); - - return Promise.all(promises).then(() => { - // All conversations have been loaded, find the one we need to load and expand its option. - const conversation = this.findConversation(this.conversationId, this.discussionUserId); - if (conversation) { - const option = this.getConversationOption(conversation); - - return this.expandOption(option); - } else { - // Conversation not found, just open the default option. - this.calculateExpandedStatus(); - - // Now load the data for the expanded option. - return this.fetchDataForExpandedOption(); - } - }); - } - - // No conversation specified or not found, determine which one should be expanded. - this.calculateExpandedStatus(); - } - - // Now load the data for the expanded option. - return this.fetchDataForExpandedOption(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Calculate which option should be expanded initially. - */ - protected calculateExpandedStatus(): void { - this.favourites.expanded = this.favourites.count != 0 && !this.group.unread && !this.individual.unread; - this.group.expanded = !this.favourites.expanded && this.group.count != 0 && !this.individual.unread; - this.individual.expanded = !this.favourites.expanded && !this.group.expanded; - - this.loadCurrentListElement(); - } - - /** - * Fetch data for the expanded option. - * - * @return Promise resolved when done. - */ - protected fetchDataForExpandedOption(): Promise { - const expandedOption = this.getExpandedOption(); - - if (expandedOption) { - return this.fetchDataForOption(expandedOption, false); - } - - return Promise.resolve(); - } - - /** - * Fetch data for a certain option. - * - * @param option The option to fetch data for. - * @param loadingMore Whether we are loading more data or just the first ones. - * @param getCounts Whether to get counts data. - * @return Promise resolved when done. - */ - fetchDataForOption(option: AddonMessagesGroupConversationOption, loadingMore?: boolean, getCounts?: boolean): Promise { - option.loadMoreError = false; - - const limitFrom = loadingMore ? option.conversations.length : 0, - promises = []; - let data: {conversations: AddonMessagesConversationForList[], canLoadMore: boolean}, - offlineMessages; - - // Get the conversations and, if needed, the offline messages. Always try to get the latest data. - promises.push(this.messagesProvider.invalidateConversations(this.siteId).catch(() => { - // Shouldn't happen. - }).then(() => { - return this.messagesProvider.getConversations(option.type, option.favourites, limitFrom, this.siteId); - }).then((result) => { - data = result; - })); - - if (!loadingMore) { - promises.push(this.messagesOffline.getAllMessages().then((data) => { - offlineMessages = data; - })); - } - - if (getCounts) { - promises.push(this.fetchConversationCounts()); - promises.push(this.messagesProvider.refreshUnreadConversationCounts(this.siteId)); - } - - return Promise.all(promises).then(() => { - if (loadingMore) { - option.conversations = option.conversations.concat(data.conversations); - option.canLoadMore = data.canLoadMore; - } else { - option.conversations = data.conversations; - option.canLoadMore = data.canLoadMore; - - if (offlineMessages && offlineMessages.length) { - return this.loadOfflineMessages(option, offlineMessages).then(() => { - // Sort the conversations, the offline messages could affect the order. - option.conversations = this.messagesProvider.sortConversations(option.conversations); - }); - } - } - - }); - } - - /** - * Fetch conversation counts. - * - * @return Promise resolved when done. - */ - protected fetchConversationCounts(): Promise { - // Always try to get the latest data. - return this.messagesProvider.invalidateConversationCounts(this.siteId).catch(() => { - // Shouldn't happen. - }).then(() => { - return this.messagesProvider.getConversationCounts(this.siteId); - }).then((counts) => { - this.favourites.count = counts.favourites; - this.individual.count = counts.individual + counts.self; // Self is only returned if it's not favourite. - this.group.count = counts.group; - }); - } - - /** - * Find a conversation in the list of loaded conversations. - * - * @param conversationId The conversation ID to search. - * @param userId User ID to search (if no conversationId). - * @param option The option to search in. If not defined, search in all options. - * @return Conversation. - */ - protected findConversation(conversationId: number, userId?: number, option?: AddonMessagesGroupConversationOption) - : AddonMessagesConversationForList { - - if (conversationId) { - const conversations = option ? (option.conversations || []) : ((this.favourites.conversations || []) - .concat(this.group.conversations || []).concat(this.individual.conversations || [])); - - return conversations.find((conv) => { - return conv.id == conversationId; - }); - } - - const conversations = option ? (option.conversations || []) : - ((this.favourites.conversations || []).concat(this.individual.conversations || [])); - - return conversations.find((conv) => { - return conv.userid == userId; - }); - } - - /** - * Get the option that is currently expanded, undefined if they are all collapsed. - * - * @return Option currently expanded. - */ - protected getExpandedOption(): AddonMessagesGroupConversationOption { - if (this.favourites.expanded) { - return this.favourites; - } else if (this.group.expanded) { - return this.group; - } else if (this.individual.expanded) { - return this.individual; - } - } - - /** - * Navigate to contacts view. - */ - gotoContacts(): void { - this.splitviewCtrl.getMasterNav().push('AddonMessagesContactsPage'); - } - - /** - * Navigate to a particular conversation. - * - * @param conversationId Conversation Id to load. - * @param userId User of the conversation. Only if there is no conversationId. - * @param messageId Message to scroll after loading the discussion. Used when searching. - */ - gotoConversation(conversationId: number, userId?: number, messageId?: number): void { - this.selectedConversationId = conversationId; - this.selectedUserId = userId; - - const params = { - conversationId: conversationId, - userId: userId - }; - if (messageId) { - params['message'] = messageId; - } - this.splitviewCtrl.push('AddonMessagesDiscussionPage', params); - } - - /** - * Navigate to message settings. - */ - gotoSettings(): void { - this.splitviewCtrl.push('AddonMessagesSettingsPage'); - } - - /** - * Function to load more conversations. - * - * @param option The option to fetch data for. - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Promise resolved when done. - */ - loadMoreConversations(option: AddonMessagesGroupConversationOption, infiniteComplete?: any): Promise { - return this.fetchDataForOption(option, true).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true); - option.loadMoreError = true; - }).finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Load offline messages into the conversations. - * - * @param option The option where the messages should be loaded. - * @param messages Offline messages. - * @return Promise resolved when done. - */ - protected loadOfflineMessages(option: AddonMessagesGroupConversationOption, messages: any[]): Promise { - const promises = []; - - messages.forEach((message) => { - if (message.conversationid) { - // It's an existing conversation. Search it in the current option. - let conversation = this.findConversation(message.conversationid, undefined, option); - - if (conversation) { - // Check if it's the last message. Offline messages are considered more recent than sent messages. - if (typeof conversation.lastmessage === 'undefined' || conversation.lastmessage === null || - !conversation.lastmessagepending || conversation.lastmessagedate <= message.timecreated / 1000) { - - this.addLastOfflineMessage(conversation, message); - } - } else { - // Conversation not found, it could be an old one or the message could belong to another option. - conversation = message.conversation || {}; - conversation.id = message.conversationid; - - if (this.getConversationOption(conversation) == option) { - // Message belongs to current option, add the conversation. - this.addLastOfflineMessage(conversation, message); - this.addOfflineConversation(conversation); - } - } - } else if (option == this.individual) { - // It's a new conversation. Check if we already created it (there is more than one message for the same user). - const conversation = this.findConversation(undefined, message.touserid, option); - - message.text = message.smallmessage; - - if (conversation) { - // Check if it's the last message. Offline messages are considered more recent than sent messages. - if (conversation.lastmessagedate <= message.timecreated / 1000) { - this.addLastOfflineMessage(conversation, message); - } - } else { - // Get the user data and create a new conversation if it belongs to the current option. - promises.push(this.userProvider.getProfile(message.touserid, undefined, true).catch(() => { - // User not found. - }).then((user) => { - const conversation = { - userid: message.touserid, - name: user ? user.fullname : String(message.touserid), - imageurl: user ? user.profileimageurl : '', - type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL - }; - - this.addLastOfflineMessage(conversation, message); - this.addOfflineConversation(conversation); - })); - } - } - }); - - return Promise.all(promises); - } - - /** - * Add an offline conversation into the right list of conversations. - * - * @param conversation Offline conversation to add. - */ - protected addOfflineConversation(conversation: any): void { - const option = this.getConversationOption(conversation); - option.conversations.unshift(conversation); - } - - /** - * Add a last offline message into a conversation. - * - * @param conversation Conversation where to put the last message. - * @param message Offline message to add. - */ - protected addLastOfflineMessage(conversation: any, message: AddonMessagesConversationMessage): void { - conversation.lastmessage = message.text; - conversation.lastmessagedate = message.timecreated / 1000; - conversation.lastmessagepending = true; - conversation.sentfromcurrentuser = true; - } - - /** - * Given a conversation, return its option (favourites, group, individual). - * - * @param conversation Conversation to check. - * @return Option object. - */ - protected getConversationOption(conversation: AddonMessagesConversationForList): AddonMessagesGroupConversationOption { - if (conversation.isfavourite) { - return this.favourites; - } else if (conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) { - return this.group; - } else { - return this.individual; - } - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @param refreshUnreadCounts Whether to refresh unread counts. - * @return Promise resolved when done. - */ - refreshData(refresher?: any, refreshUnreadCounts: boolean = true): Promise { - // Don't invalidate conversations and so, they always try to get latest data. - const promises = [ - this.messagesProvider.invalidateContactRequestsCountCache(this.siteId) - ]; - - return this.utils.allPromises(promises).finally(() => { - return this.fetchData(refreshUnreadCounts).finally(() => { - if (refresher) { - refresher.complete(); - } - }); - }); - } - - /** - * Toogle the visibility of an option (expand/collapse). - * - * @param option The option to expand/collapse. - */ - toggle(option: AddonMessagesGroupConversationOption): void { - if (option.expanded) { - // Already expanded, close it. - option.expanded = false; - this.loadCurrentListElement(); - } else { - // Pass getCounts=true to update the counts everytime the user expands an option. - this.expandOption(option, true).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true); - }); - } - } - - /** - * Expand a certain option. - * - * @param option The option to expand. - * @param getCounts Whether to get counts data. - * @return Promise resolved when done. - */ - protected expandOption(option: AddonMessagesGroupConversationOption, getCounts?: boolean): Promise { - // Collapse all and expand the right one. - this.favourites.expanded = false; - this.group.expanded = false; - this.individual.expanded = false; - - option.expanded = true; - option.loading = true; - - return this.fetchDataForOption(option, false, getCounts).then(() => { - this.loadCurrentListElement(); - }).catch((error) => { - option.expanded = false; - - return Promise.reject(error); - }).finally(() => { - option.loading = false; - }); - } - - /** - * Load the current list element based on the expanded list. - */ - protected loadCurrentListElement(): void { - if (this.favourites.expanded) { - this.currentListEl = this.favListEl && this.favListEl.nativeElement; - } else if (this.group.expanded) { - this.currentListEl = this.groupListEl && this.groupListEl.nativeElement; - } else if (this.individual.expanded) { - this.currentListEl = this.indListEl && this.indListEl.nativeElement; - } else { - this.currentListEl = undefined; - } - } - - /** - * Navigate to the search page. - */ - gotoSearch(): void { - this.navCtrl.push('AddonMessagesSearchPage'); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.newMessagesObserver && this.newMessagesObserver.off(); - this.appResumeSubscription && this.appResumeSubscription.unsubscribe(); - this.pushObserver && this.pushObserver.unsubscribe(); - this.readChangedObserver && this.readChangedObserver.off(); - this.cronObserver && this.cronObserver.off(); - this.openConversationObserver && this.openConversationObserver.off(); - this.updateConversationListObserver && this.updateConversationListObserver.off(); - this.contactRequestsCountObserver && this.contactRequestsCountObserver.off(); - this.memberInfoObserver && this.memberInfoObserver.off(); - } -} - -/** - * Conversation options. - */ -export type AddonMessagesGroupConversationOption = { - type: number; // Option type. - favourites: boolean; // Whether it contains favourites conversations. - count: number; // Number of conversations. - unread?: number; // Number of unread conversations. - expanded?: boolean; // Whether the option is currently expanded. - loading?: boolean; // Whether the option is being loaded. - canLoadMore?: boolean; // Whether it can load more data. - loadMoreError?: boolean; // Whether there was an error loading more conversations. - conversations?: AddonMessagesConversationForList[]; // List of conversations. -}; - -/** - * Formatted conversation with some calculated data for the list. - */ -export type AddonMessagesConversationForList = AddonMessagesConversationFormatted & { - lastmessagepending?: boolean; // Calculated in the app. Whether last message is pending to be sent. -}; diff --git a/src/addon/messages/pages/index/index.html b/src/addon/messages/pages/index/index.html deleted file mode 100644 index 29a434166..000000000 --- a/src/addon/messages/pages/index/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - {{ 'addon.messages.messages' | translate }} - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/addon/messages/pages/index/index.module.ts b/src/addon/messages/pages/index/index.module.ts deleted file mode 100644 index 42d5cd30a..000000000 --- a/src/addon/messages/pages/index/index.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonMessagesIndexPage } from './index'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonMessagesComponentsModule } from '../../components/components.module'; - -@NgModule({ - declarations: [ - AddonMessagesIndexPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - AddonMessagesComponentsModule, - IonicPageModule.forChild(AddonMessagesIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonMessagesIndexPageModule {} diff --git a/src/addon/messages/pages/index/index.ts b/src/addon/messages/pages/index/index.ts deleted file mode 100644 index db1d3ebbd..000000000 --- a/src/addon/messages/pages/index/index.ts +++ /dev/null @@ -1,86 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, ViewChild } from '@angular/core'; -import { IonicPage } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonMessagesProvider } from '../../providers/messages'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreTabsComponent } from '@components/tabs/tabs'; - -/** - * Page that displays the messages index page. - */ -@IonicPage({ segment: 'addon-messages-index' }) -@Component({ - selector: 'page-addon-messages-index', - templateUrl: 'index.html', -}) -export class AddonMessagesIndexPage implements OnDestroy { - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - @ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent; - - protected loadSplitViewObserver: any; - protected siteId: string; - - constructor(eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider) { - - this.siteId = sitesProvider.getCurrentSiteId(); - - // Update split view or navigate. - this.loadSplitViewObserver = eventsProvider.on(AddonMessagesProvider.SPLIT_VIEW_LOAD_EVENT, (data) => { - if (data.discussion && (this.splitviewCtrl.isOn() || !data.onlyWithSplitView)) { - this.gotoDiscussion(data.discussion, data.message); - } - }, this.siteId); - } - - /** - * Navigate to a particular discussion. - * - * @param discussionUserId Discussion Id to load. - * @param messageId Message to scroll after loading the discussion. Used when searching. - */ - gotoDiscussion(discussionUserId: number, messageId?: number): void { - const params = { - userId: discussionUserId - }; - if (messageId) { - params['message'] = messageId; - } - this.splitviewCtrl.push('AddonMessagesDiscussionPage', params); - } - - /** - * User entered the page. - */ - ionViewDidEnter(): void { - this.tabsComponent && this.tabsComponent.ionViewDidEnter(); - } - - /** - * User left the page. - */ - ionViewDidLeave(): void { - this.tabsComponent && this.tabsComponent.ionViewDidLeave(); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.loadSplitViewObserver && this.loadSplitViewObserver.off(); - } -} diff --git a/src/addon/messages/pages/search/search.html b/src/addon/messages/pages/search/search.html deleted file mode 100644 index 8f21234e0..000000000 --- a/src/addon/messages/pages/search/search.html +++ /dev/null @@ -1,60 +0,0 @@ - - - {{ 'addon.messages.searchcombined' | translate }} - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ item.titleString | translate }} - - - - -

- - -

- - {{result.lastmessagedate | coreDateDayOrTime}} - -

- {{ 'addon.messages.you' | translate }} - -

-
- - - -
- -
-
- -
-
-
-
\ No newline at end of file diff --git a/src/addon/messages/pages/search/search.module.ts b/src/addon/messages/pages/search/search.module.ts deleted file mode 100644 index ba6796a0e..000000000 --- a/src/addon/messages/pages/search/search.module.ts +++ /dev/null @@ -1,39 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonMessagesSearchPage } from './search'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreSearchComponentsModule } from '@core/search/components/components.module'; -import { AddonMessagesComponentsModule } from '../../components/components.module'; - -@NgModule({ - declarations: [ - AddonMessagesSearchPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreSearchComponentsModule, - AddonMessagesComponentsModule, - IonicPageModule.forChild(AddonMessagesSearchPage), - TranslateModule.forChild(), - ], -}) -export class AddonMessagesSearchPageModule {} diff --git a/src/addon/messages/pages/search/search.ts b/src/addon/messages/pages/search/search.ts deleted file mode 100644 index a02203faf..000000000 --- a/src/addon/messages/pages/search/search.ts +++ /dev/null @@ -1,263 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, ViewChild } from '@angular/core'; -import { IonicPage } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonMessagesProvider, AddonMessagesConversationMember, AddonMessagesMessageAreaContact } from '../../providers/messages'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreAppProvider } from '@providers/app'; - -/** - * Page for searching users. - */ -@IonicPage({ segment: 'addon-messages-search' }) -@Component({ - selector: 'page-addon-messages-search', - templateUrl: 'search.html', -}) -export class AddonMessagesSearchPage implements OnDestroy { - - disableSearch = false; - displaySearching = false; - displayResults = false; - query = ''; - contacts = { - type: 'contacts', - titleString: 'addon.messages.contacts', - results: [], - canLoadMore: false, - loadingMore: false - }; - nonContacts = { - type: 'noncontacts', - titleString: 'addon.messages.noncontacts', - results: [], - canLoadMore: false, - loadingMore: false - }; - messages = { - type: 'messages', - titleString: 'addon.messages.messages', - results: [], - canLoadMore: false, - loadingMore: false, - loadMoreError: false - }; - selectedResult = null; - - protected memberInfoObserver; - - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - constructor(private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider, eventsProvider: CoreEventsProvider, - sitesProvider: CoreSitesProvider, private messagesProvider: AddonMessagesProvider) { - - // Update block status of a user. - this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => { - if (!data.userBlocked && !data.userUnblocked) { - // The block status has not changed, ignore. - return; - } - - const contact = this.contacts.results.find((user) => user.id == data.userId); - if (contact) { - contact.isblocked = data.userBlocked; - } else { - const nonContact = this.nonContacts.results.find((user) => user.id == data.userId); - if (nonContact) { - nonContact.isblocked = data.userBlocked; - } - } - - this.messages.results.forEach((message: any): void => { - if (message.userid == data.userId) { - message.isblocked = data.userBlocked; - } - }); - }, sitesProvider.getCurrentSiteId()); - } - - /** - * Clear search. - */ - clearSearch(): void { - this.query = ''; - this.displayResults = false; - this.splitviewCtrl.emptyDetails(); - } - - /** - * Start a new search or load more results. - * - * @param query Text to search for. - * @param loadMore Load more contacts, noncontacts or messages. If undefined, start a new search. - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Resolved when done. - */ - search(query: string, loadMore?: 'contacts' | 'noncontacts' | 'messages', infiniteComplete?: any): Promise { - this.appProvider.closeKeyboard(); - - this.query = query; - this.disableSearch = true; - this.displaySearching = !loadMore; - - const promises = []; - let newContacts: AddonMessagesConversationMember[] = []; - let newNonContacts: AddonMessagesConversationMember[] = []; - let newMessages: AddonMessagesMessageAreaContact[] = []; - let canLoadMoreContacts = false; - let canLoadMoreNonContacts = false; - let canLoadMoreMessages = false; - - if (!loadMore || loadMore == 'contacts' || loadMore == 'noncontacts') { - const limitNum = loadMore ? AddonMessagesProvider.LIMIT_SEARCH : AddonMessagesProvider.LIMIT_INITIAL_USER_SEARCH; - let limitFrom = 0; - if (loadMore == 'contacts') { - limitFrom = this.contacts.results.length; - this.contacts.loadingMore = true; - } else if (loadMore == 'noncontacts') { - limitFrom = this.nonContacts.results.length; - this.nonContacts.loadingMore = true; - } - - promises.push( - this.messagesProvider.searchUsers(query, limitFrom, limitNum).then((result) => { - if (!loadMore || loadMore == 'contacts') { - newContacts = result.contacts; - canLoadMoreContacts = result.canLoadMoreContacts; - } - if (!loadMore || loadMore == 'noncontacts') { - newNonContacts = result.nonContacts; - canLoadMoreNonContacts = result.canLoadMoreNonContacts; - } - }) - ); - } - - if (!loadMore || loadMore == 'messages') { - let limitFrom = 0; - if (loadMore == 'messages') { - limitFrom = this.messages.results.length; - this.messages.loadingMore = true; - } - - promises.push( - this.messagesProvider.searchMessages(query, undefined, limitFrom).then((result) => { - newMessages = result.messages; - canLoadMoreMessages = result.canLoadMore; - }) - ); - } - - return Promise.all(promises).then(() => { - if (!loadMore) { - this.contacts.results = []; - this.nonContacts.results = []; - this.messages.results = []; - } - - this.displayResults = true; - - if (!loadMore || loadMore == 'contacts') { - this.contacts.results.push(...newContacts); - this.contacts.canLoadMore = canLoadMoreContacts; - this.setHighlight(newContacts, true); - } - - if (!loadMore || loadMore == 'noncontacts') { - this.nonContacts.results.push(...newNonContacts); - this.nonContacts.canLoadMore = canLoadMoreNonContacts; - this.setHighlight(newNonContacts, true); - } - - if (!loadMore || loadMore == 'messages') { - this.messages.results.push(...newMessages); - this.messages.canLoadMore = canLoadMoreMessages; - this.messages.loadMoreError = false; - this.setHighlight(newMessages, false); - } - - if (!loadMore) { - if (this.contacts.results.length > 0) { - this.openConversation(this.contacts.results[0], true); - } else if (this.nonContacts.results.length > 0) { - this.openConversation(this.nonContacts.results[0], true); - } else if (this.messages.results.length > 0) { - this.openConversation(this.messages.results[0], true); - } - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingusers', true); - - if (loadMore == 'messages') { - this.messages.loadMoreError = true; - } - }).finally(() => { - this.disableSearch = false; - this.displaySearching = false; - - if (loadMore == 'contacts') { - this.contacts.loadingMore = false; - } else if (loadMore == 'noncontacts') { - this.nonContacts.loadingMore = false; - } else if (loadMore == 'messages') { - this.messages.loadingMore = false; - } - - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Open a conversation in the split view. - * - * @param result User or message. - * @param onInit Whether the tser was selected on initial load. - */ - openConversation(result: any, onInit: boolean = false): void { - if (!onInit || this.splitviewCtrl.isOn()) { - this.selectedResult = result; - const params: any = {}; - if (result.conversationid) { - params.conversationId = result.conversationid; - } else { - params.userId = result.id; - } - this.splitviewCtrl.push('AddonMessagesDiscussionPage', params); - } - } - - /** - * Set the highlight values for each entry. - * - * @param results Results to highlight. - * @param isUser Whether the results are from a user search or from a message search. - */ - setHighlight(results: any[], isUser: boolean): void { - results.forEach((result) => { - result.highlightName = isUser ? this.query : undefined; - result.highlightMessage = !isUser ? this.query : undefined; - }); - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.memberInfoObserver && this.memberInfoObserver.off(); - } -} diff --git a/src/addon/messages/pages/settings/settings.html b/src/addon/messages/pages/settings/settings.html deleted file mode 100644 index c955efb2e..000000000 --- a/src/addon/messages/pages/settings/settings.html +++ /dev/null @@ -1,110 +0,0 @@ - - - {{ 'addon.messages.messages' | translate }} - - - - - - - - - - - {{ 'addon.messages.blocknoncontacts' | translate }} - - - - - {{ 'addon.messages.contactableprivacy' | translate }} - - {{ 'addon.messages.contactableprivacy_onlycontacts' | translate }} - - - - {{ 'addon.messages.contactableprivacy_coursemember' | translate }} - - - - {{ 'addon.messages.contactableprivacy_site' | translate }} - - - - - - - -
- - - - - {{ notification.displayname }} - {{ 'core.settings.loggedin' | translate }} - {{ 'core.settings.loggedoff' | translate }} - - - {{ 'addon.notifications.notificationpreferences' | translate }} - - - - - - - - {{ processor.displayname }} - - - - {{ processor.lockedmessage }} - {{ 'core.settings.disabled' | translate }} - - - - - - - {{ processor.displayname }} - - - - - -
{{'core.settings.locked' | translate }}
- - {{ 'core.settings.disabled' | translate }} -
-
- - {{ processor.displayname }} - - - {{ 'core.settings.' + state | translate }} - - - - {{'core.settings.locked' | translate }} - {{ 'core.settings.disabled' | translate }} - -
-
-
-
-
- - - - - {{ 'core.settings.general' | translate }} - - -

{{ 'addon.messages.useentertosend' | translate }}

-

{{ 'addon.messages.useentertosenddescdesktop' | translate }}

-

{{ 'addon.messages.useentertosenddescmac' | translate }}

-
- -
-
-
-
-
diff --git a/src/addon/messages/pages/settings/settings.module.ts b/src/addon/messages/pages/settings/settings.module.ts deleted file mode 100644 index 70bd2b59e..000000000 --- a/src/addon/messages/pages/settings/settings.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonMessagesSettingsPage } from './settings'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonMessagesComponentsModule } from '../../components/components.module'; - -@NgModule({ - declarations: [ - AddonMessagesSettingsPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - AddonMessagesComponentsModule, - IonicPageModule.forChild(AddonMessagesSettingsPage), - TranslateModule.forChild() - ], -}) -export class AddonMessagesSettingsPageModule {} diff --git a/src/addon/messages/pages/settings/settings.scss b/src/addon/messages/pages/settings/settings.scss deleted file mode 100644 index 138395573..000000000 --- a/src/addon/messages/pages/settings/settings.scss +++ /dev/null @@ -1,10 +0,0 @@ -ion-app.app-root page-addon-messages-settings { - .list-header { - margin-bottom: 0; - border-top: 0; - } - - .toggle { - display: inline-block; - } -} \ No newline at end of file diff --git a/src/addon/messages/pages/settings/settings.ts b/src/addon/messages/pages/settings/settings.ts deleted file mode 100644 index c8b07e34e..000000000 --- a/src/addon/messages/pages/settings/settings.ts +++ /dev/null @@ -1,290 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy } from '@angular/core'; -import { IonicPage } from 'ionic-angular'; -import { - AddonMessagesProvider, AddonMessagesMessagePreferences, AddonMessagesMessagePreferencesNotification, - AddonMessagesMessagePreferencesNotificationProcessor -} from '../../providers/messages'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreAppProvider } from '@providers/app'; -import { CoreConfigProvider } from '@providers/config'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreConstants } from '@core/constants'; - -/** - * Page that displays the messages settings page. - */ -@IonicPage({ segment: 'addon-messages-settings' }) -@Component({ - selector: 'page-addon-messages-settings', - templateUrl: 'settings.html', -}) -export class AddonMessagesSettingsPage implements OnDestroy { - protected updateTimeout: any; - - preferences: AddonMessagesMessagePreferences; - preferencesLoaded: boolean; - contactablePrivacy: number | boolean; - advancedContactable = false; // Whether the site supports "advanced" contactable privacy. - allowSiteMessaging = false; - onlyContactsValue = AddonMessagesProvider.MESSAGE_PRIVACY_ONLYCONTACTS; - courseMemberValue = AddonMessagesProvider.MESSAGE_PRIVACY_COURSEMEMBER; - siteValue = AddonMessagesProvider.MESSAGE_PRIVACY_SITE; - groupMessagingEnabled: boolean; - sendOnEnter: boolean; - isDesktop: boolean; - isMac: boolean; - - protected previousContactableValue: number | boolean; - - constructor(private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, - private userProvider: CoreUserProvider, private sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, - private configProvider: CoreConfigProvider, private eventsProvider: CoreEventsProvider) { - - const currentSite = sitesProvider.getCurrentSite(); - this.advancedContactable = currentSite && currentSite.isVersionGreaterEqualThan('3.6'); - this.allowSiteMessaging = currentSite && currentSite.canUseAdvancedFeature('messagingallusers'); - this.groupMessagingEnabled = this.messagesProvider.isGroupMessagingEnabled(); - - this.configProvider.get(CoreConstants.SETTINGS_SEND_ON_ENTER, !appProvider.isMobile()).then((sendOnEnter) => { - this.sendOnEnter = !!sendOnEnter; - }); - - this.isDesktop = !appProvider.isMobile(); - this.isMac = appProvider.isMac(); - } - - /** - * Runs when the page has loaded. This event only happens once per page being created. - * If a page leaves but is cached, then this event will not fire again on a subsequent viewing. - * Setup code for the page. - */ - ionViewDidLoad(): void { - this.fetchPreferences(); - } - - /** - * Fetches preference data. - * - * @return Promise resolved when done. - */ - protected fetchPreferences(): Promise { - return this.messagesProvider.getMessagePreferences().then((preferences) => { - if (this.groupMessagingEnabled) { - // Simplify the preferences. - for (const component of preferences.components) { - // Only display get the notification preferences. - component.notifications = component.notifications.filter((notification) => { - return notification.preferencekey == AddonMessagesProvider.NOTIFICATION_PREFERENCES_KEY; - }); - - component.notifications.forEach((notification) => { - notification.processors.forEach( - (processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted) => { - processor.checked = processor.loggedin.checked || processor.loggedoff.checked; - }); - }); - } - } - - this.preferences = preferences; - this.contactablePrivacy = preferences.blocknoncontacts; - this.previousContactableValue = this.contactablePrivacy; - }).catch((message) => { - this.domUtils.showErrorModal(message); - }).finally(() => { - this.preferencesLoaded = true; - }); - } - - /** - * Update preferences. The purpose is to store the updated data, it won't be reflected in the view. - */ - protected updatePreferences(): void { - this.messagesProvider.invalidateMessagePreferences().finally(() => { - this.fetchPreferences(); - }); - } - - /** - * Update preferences after a certain time. The purpose is to store the updated data, it won't be reflected in the view. - */ - protected updatePreferencesAfterDelay(): void { - // Cancel pending updates. - clearTimeout(this.updateTimeout); - - this.updateTimeout = setTimeout(() => { - this.updateTimeout = null; - this.updatePreferences(); - }, 5000); - } - - /** - * Save the contactable privacy setting.. - * - * @param value The value to set. - */ - saveContactablePrivacy(value: number | boolean): void { - if (this.contactablePrivacy == this.previousContactableValue) { - // Value hasn't changed from previous, it probably means that we just fetched the value from the server. - return; - } - - const modal = this.domUtils.showModalLoading('core.sending', true); - - if (!this.advancedContactable) { - // Convert from boolean to number. - value = value ? 1 : 0; - } - - this.userProvider.updateUserPreference('message_blocknoncontacts', value).then(() => { - // Update the preferences since they were modified. - this.updatePreferencesAfterDelay(); - this.previousContactableValue = this.contactablePrivacy; - }).catch((message) => { - // Show error and revert change. - this.domUtils.showErrorModal(message); - this.contactablePrivacy = this.previousContactableValue; - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Change the value of a certain preference. - * - * @param notification Notification object. - * @param state State name, ['loggedin', 'loggedoff']. - * @param processor Notification processor. - */ - changePreference(notification: AddonMessagesMessagePreferencesNotificationFormatted, state: string, - processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted): void { - - if (this.groupMessagingEnabled) { - // Update both states at the same time. - const valueArray = [], - promises = []; - let value = 'none'; - - notification.processors.forEach((processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted) => { - if (processor.checked) { - valueArray.push(processor.name); - } - }); - - if (value.length > 0) { - value = valueArray.join(','); - } - - notification.updating = true; - - promises.push(this.userProvider.updateUserPreference(notification.preferencekey + '_loggedin', value)); - promises.push(this.userProvider.updateUserPreference(notification.preferencekey + '_loggedoff', value)); - - Promise.all(promises).then(() => { - // Update the preferences since they were modified. - this.updatePreferencesAfterDelay(); - }).catch((error) => { - // Show error and revert change. - this.domUtils.showErrorModal(error); - processor.checked = !processor.checked; - }).finally(() => { - notification.updating = false; - }); - } else { - // Update only the specified state. - const processorState = processor[state], - preferenceName = notification.preferencekey + '_' + processorState.name, - valueArray = []; - let value = 'none'; - - notification.processors.forEach((processor) => { - if (processor[state].checked) { - valueArray.push(processor.name); - } - }); - - if (value.length > 0) { - value = valueArray.join(','); - } - - if (!notification.updating) { - notification.updating = {}; - } - - notification.updating[state] = true; - this.userProvider.updateUserPreference(preferenceName, value).then(() => { - // Update the preferences since they were modified. - this.updatePreferencesAfterDelay(); - }).catch((message) => { - // Show error and revert change. - this.domUtils.showErrorModal(message); - processorState.checked = !processorState.checked; - }).finally(() => { - notification.updating[state] = false; - }); - } - } - - /** - * Refresh the list of preferences. - * - * @param refresher Refresher. - */ - refreshPreferences(refresher: any): void { - this.messagesProvider.invalidateMessagePreferences().finally(() => { - this.fetchPreferences().finally(() => { - refresher.complete(); - }); - }); - } - - sendOnEnterChanged(): void { - // Save the value. - this.configProvider.set(CoreConstants.SETTINGS_SEND_ON_ENTER, this.sendOnEnter ? 1 : 0); - - // Notify the app. - this.eventsProvider.trigger(CoreEventsProvider.SEND_ON_ENTER_CHANGED, {sendOnEnter: !!this.sendOnEnter}, - this.sitesProvider.getCurrentSiteId()); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - // If there is a pending action to update preferences, execute it right now. - if (this.updateTimeout) { - clearTimeout(this.updateTimeout); - this.updatePreferences(); - } - } -} - -/** - * Message preferences notification with some caclulated data. - */ -type AddonMessagesMessagePreferencesNotificationFormatted = AddonMessagesMessagePreferencesNotification & { - updating?: boolean | {[state: string]: boolean}; // Calculated in the app. Whether the notification is being updated. -}; - -/** - * Message preferences notification processor with some caclulated data. - */ -type AddonMessagesMessagePreferencesNotificationProcessorFormatted = AddonMessagesMessagePreferencesNotificationProcessor & { - checked?: boolean; // Calculated in the app. Whether the processor is checked either for loggedin or loggedoff. -}; diff --git a/src/addon/messages/providers/contact-request-link-handler.ts b/src/addon/messages/providers/contact-request-link-handler.ts deleted file mode 100644 index ba08f3a97..000000000 --- a/src/addon/messages/providers/contact-request-link-handler.ts +++ /dev/null @@ -1,70 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonMessagesProvider } from './messages'; - -/** - * Content links handler for a contact requests. - */ -@Injectable() -export class AddonMessagesContactRequestLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonMessagesContactRequestLinkHandler'; - pattern = /\/message\/pendingcontactrequests\.php/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private messagesProvider: AddonMessagesProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - this.linkHelper.goInSite(navCtrl, 'AddonMessagesContactsPage', {}, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.messagesProvider.isPluginEnabled(siteId).then((enabled) => { - if (!enabled) { - return false; - } - - return this.messagesProvider.isGroupMessagingEnabled(); - }); - } -} diff --git a/src/addon/messages/providers/discussion-link-handler.ts b/src/addon/messages/providers/discussion-link-handler.ts deleted file mode 100644 index 87462c9b6..000000000 --- a/src/addon/messages/providers/discussion-link-handler.ts +++ /dev/null @@ -1,88 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonMessagesProvider } from './messages'; -import { CoreSitesProvider } from '@providers/sites'; - -/** - * Content links handler for a discussion. - * Match message index URL with params id, user1 or user2. - */ -@Injectable() -export class AddonMessagesDiscussionLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonMessagesDiscussionLinkHandler'; - pattern = /\/message\/index\.php.*([\?\&](id|user1|user2)=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private messagesProvider: AddonMessagesProvider, - private sitesProvider: CoreSitesProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const stateParams = { - userId: parseInt(params.id || params.user2, 10) - }; - this.linkHelper.goInSite(navCtrl, 'AddonMessagesDiscussionPage', stateParams, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.messagesProvider.isPluginEnabled(siteId).then((enabled) => { - if (!enabled) { - return false; - } - - if (typeof params.id == 'undefined' && typeof params.user2 == 'undefined') { - // Other user not defined, cannot treat the URL. - return false; - } - - if (typeof params.user1 != 'undefined') { - // Check if user1 is the current user, since the app only supports current user. - return this.sitesProvider.getSite(siteId).then((site) => { - return parseInt(params.user1, 10) == site.getUserId(); - }); - } - - return true; - }); - } -} diff --git a/src/addon/messages/providers/index-link-handler.ts b/src/addon/messages/providers/index-link-handler.ts deleted file mode 100644 index 215338116..000000000 --- a/src/addon/messages/providers/index-link-handler.ts +++ /dev/null @@ -1,70 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonMessagesProvider } from './messages'; - -/** - * Content links handler for messaging index. - * Match message index URL without params id, user1 or user2. - */ -@Injectable() -export class AddonMessagesIndexLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonMessagesIndexLinkHandler'; - pattern = /\/message\/index\.php((?![\?\&](id|user1|user2)=\d+).)*$/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private messagesProvider: AddonMessagesProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - let pageName = 'AddonMessagesIndexPage'; - if (this.messagesProvider.isGroupMessagingEnabled()) { - pageName = 'AddonMessagesGroupConversationsPage'; - } - - this.linkHelper.goInSite(navCtrl, pageName, undefined, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.messagesProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/messages/providers/mainmenu-handler.ts b/src/addon/messages/providers/mainmenu-handler.ts deleted file mode 100644 index 42b720139..000000000 --- a/src/addon/messages/providers/mainmenu-handler.ts +++ /dev/null @@ -1,308 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonMessagesProvider } from './messages'; -import { CoreMainMenuHandler, CoreMainMenuHandlerToDisplay } from '@core/mainmenu/providers/delegate'; -import { CoreCronHandler } from '@providers/cron'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreAppProvider } from '@providers/app'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; -import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; - -/** - * Handler to inject an option into main menu. - */ -@Injectable() -export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCronHandler { - name = 'AddonMessages'; - priority = 800; - protected handler: CoreMainMenuHandlerToDisplay = { - icon: 'chatbubbles', - title: 'addon.messages.messages', - page: 'AddonMessagesIndexPage', - class: 'addon-messages-handler', - showBadge: true, // Do not check isMessageCountEnabled because we'll use fallback it not enabled. - badge: '', - loading: true - }; - - protected unreadCount = 0; - protected contactRequestsCount = 0; - protected orMore = false; - - constructor(private messagesProvider: AddonMessagesProvider, private sitesProvider: CoreSitesProvider, - eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider, - private localNotificationsProvider: CoreLocalNotificationsProvider, private filterHelper: CoreFilterHelperProvider, - private pushNotificationsProvider: CorePushNotificationsProvider, utils: CoreUtilsProvider, - pushNotificationsDelegate: CorePushNotificationsDelegate, private emulatorHelper: CoreEmulatorHelperProvider) { - - eventsProvider.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, (data) => { - this.unreadCount = data.favourites + data.individual + data.group + data.self; - this.orMore = data.orMore; - this.updateBadge(data.siteId); - }); - - eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { - this.contactRequestsCount = data.count; - this.updateBadge(data.siteId); - }); - - // Reset info on logout. - eventsProvider.on(CoreEventsProvider.LOGOUT, (data) => { - this.unreadCount = 0; - this.contactRequestsCount = 0; - this.orMore = false; - this.handler.badge = ''; - this.handler.loading = true; - }); - - // If a message push notification is received, refresh the count. - pushNotificationsDelegate.on('receive').subscribe((notification) => { - // New message received. If it's from current site, refresh the data. - const isMessage = utils.isFalseOrZero(notification.notif) || notification.name == 'messagecontactrequests'; - if (isMessage && this.sitesProvider.isCurrentSite(notification.site)) { - this.refreshBadge(notification.site); - } - }); - - // Register Badge counter. - pushNotificationsDelegate.registerCounterHandler('AddonMessages'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.messagesProvider.isPluginEnabled(); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreMainMenuHandlerToDisplay { - this.handler.page = this.messagesProvider.isGroupMessagingEnabled() ? - 'AddonMessagesGroupConversationsPage' : 'AddonMessagesIndexPage'; - - if (this.handler.loading) { - this.refreshBadge(); - } - - return this.handler; - } - - /** - * Refreshes badge number. - * - * @param siteId Site ID or current Site if undefined. - * @param unreadOnly If true only the unread conversations count is refreshed. - * @return Resolve when done. - */ - refreshBadge(siteId?: string, unreadOnly?: boolean): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - if (!siteId) { - return; - } - - const promises = []; - - promises.push(this.messagesProvider.refreshUnreadConversationCounts(siteId).catch(() => { - this.unreadCount = 0; - this.orMore = false; - })); - - // Refresh the number of contact requests in 3.6+ sites. - if (!unreadOnly && this.messagesProvider.isGroupMessagingEnabled()) { - promises.push(this.messagesProvider.refreshContactRequestsCount(siteId).catch(() => { - this.contactRequestsCount = 0; - })); - } - - return Promise.all(promises).finally(() => { - this.updateBadge(siteId); - this.handler.loading = false; - }); - } - - /** - * Update badge number and push notifications counter from loaded data. - * - * @param siteId Site ID. - */ - updateBadge(siteId: string): void { - const totalCount = this.unreadCount + (this.contactRequestsCount || 0); - if (totalCount > 0) { - this.handler.badge = totalCount + (this.orMore ? '+' : ''); - } else { - this.handler.badge = ''; - } - - // Update push notifications badge. - this.pushNotificationsProvider.updateAddonCounter('AddonMessages', totalCount, siteId); - } - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - if (this.sitesProvider.isCurrentSite(siteId)) { - this.refreshBadge(); - } - - if (this.appProvider.isDesktop() && this.localNotificationsProvider.isAvailable()) { - this.emulatorHelper.checkNewNotifications( - AddonMessagesProvider.PUSH_SIMULATION_COMPONENT, - this.fetchMessages.bind(this), this.getTitleAndText.bind(this), siteId); - } - - return Promise.resolve(); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - if (this.appProvider.isDesktop()) { - return 60000; // Desktop usually has a WiFi connection, check it every minute. - } else if (this.messagesProvider.isGroupMessagingEnabled() || this.messagesProvider.isMessageCountEnabled()) { - return 300000; // We have a WS to check the number, check it every 5 minutes. - } else { - return 600000; // Check it every 10 minutes. - } - } - - /** - * Whether it's a synchronization process or not. - * - * @return True if is a sync process, false otherwise. - */ - isSync(): boolean { - // This is done to use only wifi if using the fallback function. - - if (this.appProvider.isDesktop()) { - // In desktop it is always sync, since it fetches messages to see if there's a new one. - return true; - } - - return !this.messagesProvider.isMessageCountEnabled() && !this.messagesProvider.isGroupMessagingEnabled(); - } - - /** - * Whether the process should be executed during a manual sync. - * - * @return True if is a manual sync process, false otherwise. - */ - canManualSync(): boolean { - return true; - } - - /** - * Get the latest unread received messages from a site. - * - * @param siteId Site ID. Default current. - * @return Promise resolved with the notifications. - */ - protected fetchMessages(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - if (site.isVersionGreaterEqualThan('3.7')) { - - // Use get conversations WS to be able to get group conversations messages. - return this.messagesProvider.getConversations(undefined, undefined, 0, site.id, undefined, false, true) - .then((result) => { - - // Find the first unmuted conversation. - const conv = result.conversations.find((conversation) => { - return !conversation.ismuted; - }); - - if (conv.isread) { - // The conversation is read, no unread messages. - return []; - } - - const currentUserId = site.getUserId(), - message: any = conv.messages[0]; // Treat only the last message, is the one we're interested. - - if (!message || message.useridfrom == currentUserId) { - // No last message or not from current user. Return empty list. - return []; - } - - // Add some calculated data. - message.contexturl = ''; - message.contexturlname = ''; - message.convid = conv.id; - message.fullmessage = message.text; - message.fullmessageformat = 0; - message.fullmessagehtml = ''; - message.notification = 0; - message.read = 0; - message.smallmessage = message.smallmessage || message.text; - message.subject = conv.name; - message.timecreated = message.timecreated * 1000; - message.timeread = 0; - message.useridto = currentUserId; - message.usertofullname = site.getInfo().fullname; - - const userFrom = conv.members.find((member) => { - return member.id == message.useridfrom; - }); - message.userfromfullname = userFrom && userFrom.fullname; - - return [message]; - }); - } else { - return this.messagesProvider.getUnreadReceivedMessages(true, false, true, siteId).then((response) => { - return response.messages; - }); - } - }); - } - - /** - * Given a message, return the title and the text for the message. - * - * @param message Message. - * @return Promise resolved with an object with title and text. - */ - protected getTitleAndText(message: any): Promise { - const data = { - title: message.name || message.userfromfullname, - }; - - return this.filterHelper.getFiltersAndFormatText(message.text, 'system', 0, {clean: true, singleLine: true}).catch(() => { - return {text: message.text}; - }).then((result) => { - data['text'] = result.text; - - return data; - }); - } -} diff --git a/src/addon/messages/providers/messages-offline.ts b/src/addon/messages/providers/messages-offline.ts deleted file mode 100644 index f9909bc0b..000000000 --- a/src/addon/messages/providers/messages-offline.ts +++ /dev/null @@ -1,349 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreAppProvider } from '@providers/app'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Service to handle Offline messages. - */ -@Injectable() -export class AddonMessagesOfflineProvider { - - protected logger; - - // Variables for database. - static MESSAGES_TABLE = 'addon_messages_offline_messages'; // When group messaging isn't available or a new conversation starts. - static CONVERSATION_MESSAGES_TABLE = 'addon_messages_offline_conversation_messages'; // Conversation messages. - protected siteSchema: CoreSiteSchema = { - name: 'AddonMessagesOfflineProvider', - version: 1, - tables: [ - { - name: AddonMessagesOfflineProvider.MESSAGES_TABLE, - columns: [ - { - name: 'touserid', - type: 'INTEGER' - }, - { - name: 'useridfrom', - type: 'INTEGER' - }, - { - name: 'smallmessage', - type: 'TEXT' - }, - { - name: 'timecreated', - type: 'INTEGER' - }, - { - name: 'deviceoffline', // If message was stored because device was offline. - type: 'INTEGER' - } - ], - primaryKeys: ['touserid', 'smallmessage', 'timecreated'] - }, - { - name: AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, - columns: [ - { - name: 'conversationid', - type: 'INTEGER' - }, - { - name: 'text', - type: 'TEXT' - }, - { - name: 'timecreated', - type: 'INTEGER' - }, - { - name: 'deviceoffline', // If message was stored because device was offline. - type: 'INTEGER' - }, - { - name: 'conversation', // Data about the conversation. - type: 'TEXT' - } - ], - primaryKeys: ['conversationid', 'text', 'timecreated'] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, - private textUtils: CoreTextUtilsProvider) { - this.logger = logger.getInstance('AddonMessagesOfflineProvider'); - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete a message. - * - * @param conversationId Conversation ID. - * @param message The message. - * @param timeCreated The time the message was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteConversationMessage(conversationId: number, message: string, timeCreated: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, { - conversationid: conversationId, - text: message, - timecreated: timeCreated - }); - }); - } - - /** - * Delete all the messages in a conversation. - * - * @param conversationId Conversation ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteConversationMessages(conversationId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, { - conversationid: conversationId - }); - }); - } - - /** - * Delete a message. - * - * @param toUserId User ID to send the message to. - * @param message The message. - * @param timeCreated The time the message was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteMessage(toUserId: number, message: string, timeCreated: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonMessagesOfflineProvider.MESSAGES_TABLE, { - touserid: toUserId, - smallmessage: message, - timecreated: timeCreated - }); - }); - } - - /** - * Get all messages where deviceoffline is set to 1. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with messages. - */ - getAllDeviceOfflineMessages(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const promises = []; - - promises.push(site.getDb().getRecords(AddonMessagesOfflineProvider.MESSAGES_TABLE, {deviceoffline: 1})); - promises.push(site.getDb().getRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, {deviceoffline: 1})); - - return Promise.all(promises).then((results) => { - results[1] = this.parseConversationMessages(results[1]); - - return results[0].concat(results[1]); - }); - }); - } - - /** - * Get all offline messages. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with messages. - */ - getAllMessages(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const promises = []; - - promises.push(site.getDb().getAllRecords(AddonMessagesOfflineProvider.MESSAGES_TABLE)); - promises.push(site.getDb().getAllRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE)); - - return Promise.all(promises).then((results) => { - results[1] = this.parseConversationMessages(results[1]); - - return results[0].concat(results[1]); - }); - }); - } - - /** - * Get offline messages to send to a certain user. - * - * @param conversationId Conversation ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with messages. - */ - getConversationMessages(conversationId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, - {conversationid: conversationId}).then((messages) => { - - return this.parseConversationMessages(messages); - }); - }); - } - - /** - * Get offline messages to send to a certain user. - * - * @param toUserId User ID to get messages to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with messages. - */ - getMessages(toUserId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonMessagesOfflineProvider.MESSAGES_TABLE, {touserid: toUserId}); - }); - } - - /** - * Check if there are offline messages to send to a conversation. - * - * @param conversationId Conversation ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline messages, false otherwise. - */ - hasConversationMessages(conversationId: number, siteId?: string): Promise { - return this.getConversationMessages(conversationId, siteId).then((messages) => { - return !!messages.length; - }); - } - - /** - * Check if there are offline messages to send to a certain user. - * - * @param toUserId User ID to check. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline messages, false otherwise. - */ - hasMessages(toUserId: number, siteId?: string): Promise { - return this.getMessages(toUserId, siteId).then((messages) => { - return !!messages.length; - }); - } - - /** - * Parse some fields of each offline conversation messages. - * - * @param messages List of messages to parse. - * @return Parsed messages. - */ - protected parseConversationMessages(messages: any[]): any[] { - if (!messages) { - return []; - } - - messages.forEach((message) => { - if (message.conversation) { - message.conversation = this.textUtils.parseJSON(message.conversation, {}); - } - }); - - return messages; - } - - /** - * Save a conversation message to be sent later. - * - * @param conversation Conversation. - * @param message The message to send. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - saveConversationMessage(conversation: any, message: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - conversationid: conversation.id, - text: message, - timecreated: Date.now(), - deviceoffline: this.appProvider.isOnline() ? 0 : 1, - conversation: JSON.stringify({ - name: conversation.name || '', - subname: conversation.subname || '', - imageurl: conversation.imageurl || '', - isfavourite: conversation.isfavourite ? 1 : 0, - type: conversation.type - }) - }; - - return site.getDb().insertRecord(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, entry).then(() => { - return entry; - }); - }); - } - - /** - * Save a message to be sent later. - * - * @param toUserId User ID recipient of the message. - * @param message The message to send. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - saveMessage(toUserId: number, message: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - touserid: toUserId, - useridfrom: site.getUserId(), - smallmessage: message, - timecreated: new Date().getTime(), - deviceoffline: this.appProvider.isOnline() ? 0 : 1 - }; - - return site.getDb().insertRecord(AddonMessagesOfflineProvider.MESSAGES_TABLE, entry).then(() => { - return entry; - }); - }); - } - - /** - * Set deviceoffline for a group of messages. - * - * @param messages Messages to update. Should be the same entry as retrieved from the DB. - * @param value Value to set. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - setMessagesDeviceOffline(messages: any, value: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const db = site.getDb(), - promises = [], - data = { deviceoffline: value ? 1 : 0 }; - - messages.forEach((message) => { - if (message.conversationid) { - promises.push(db.updateRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, data, - {conversationid: message.conversationid, text: message.text, timecreated: message.timecreated})); - } else { - promises.push(db.updateRecords(AddonMessagesOfflineProvider.MESSAGES_TABLE, data, - {touserid: message.touserid, smallmessage: message.smallmessage, timecreated: message.timecreated})); - } - }); - - return Promise.all(promises); - }); - } -} diff --git a/src/addon/messages/providers/messages.ts b/src/addon/messages/providers/messages.ts deleted file mode 100644 index 6033ad8fd..000000000 --- a/src/addon/messages/providers/messages.ts +++ /dev/null @@ -1,3177 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreAppProvider } from '@providers/app'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonMessagesOfflineProvider } from './messages-offline'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; -import { CoreWSExternalWarning } from '@providers/ws'; - -/** - * Service to handle messages. - */ -@Injectable() -export class AddonMessagesProvider { - protected ROOT_CACHE_KEY = 'mmaMessages:'; - protected LIMIT_MESSAGES = AddonMessagesProvider.LIMIT_MESSAGES; - static NEW_MESSAGE_EVENT = 'addon_messages_new_message_event'; - static READ_CHANGED_EVENT = 'addon_messages_read_changed_event'; - static OPEN_CONVERSATION_EVENT = 'addon_messages_open_conversation_event'; // Notify that a conversation should be opened. - static SPLIT_VIEW_LOAD_EVENT = 'addon_messages_split_view_load_event'; - static UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event'; - static MEMBER_INFO_CHANGED_EVENT = 'addon_messages_member_changed_event'; - static UNREAD_CONVERSATION_COUNTS_EVENT = 'addon_messages_unread_conversation_counts_event'; - static CONTACT_REQUESTS_COUNT_EVENT = 'addon_messages_contact_requests_count_event'; - static POLL_INTERVAL = 10000; - static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation'; - - static MESSAGE_PRIVACY_COURSEMEMBER = 0; // Privacy setting for being messaged by anyone within courses user is member of. - static MESSAGE_PRIVACY_ONLYCONTACTS = 1; // Privacy setting for being messaged only by contacts. - static MESSAGE_PRIVACY_SITE = 2; // Privacy setting for being messaged by anyone on the site. - static MESSAGE_CONVERSATION_TYPE_INDIVIDUAL = 1; // An individual conversation. - static MESSAGE_CONVERSATION_TYPE_GROUP = 2; // A group conversation. - static MESSAGE_CONVERSATION_TYPE_SELF = 3; // A self conversation. - static LIMIT_CONTACTS = 50; - static LIMIT_MESSAGES = 50; - static LIMIT_INITIAL_USER_SEARCH = 3; - static LIMIT_SEARCH = 50; - - static NOTIFICATION_PREFERENCES_KEY = 'message_provider_moodle_instantmessage'; - - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, - private userProvider: CoreUserProvider, private messagesOffline: AddonMessagesOfflineProvider, - private utils: CoreUtilsProvider, private timeUtils: CoreTimeUtilsProvider, - private emulatorHelper: CoreEmulatorHelperProvider, private eventsProvider: CoreEventsProvider) { - this.logger = logger.getInstance('AddonMessagesProvider'); - } - - /** - * Add a contact. - * - * @param userId User ID of the person to add. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved when done. - * @deprecated since Moodle 3.6 - */ - addContact(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - userids: [ userId ] - }; - - return site.write('core_message_create_contacts', params).then(() => { - return this.invalidateAllContactsCache(site.getUserId(), site.getId()); - }); - }); - } - - /** - * Block a user. - * - * @param userId User ID of the person to block. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved when done. - */ - blockContact(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - let promise; - if (site.wsAvailable('core_message_block_user')) { - // Since Moodle 3.6 - const params = { - userid: site.getUserId(), - blockeduserid: userId, - }; - promise = site.write('core_message_block_user', params); - } else { - const params = { - userids: [userId] - }; - promise = site.write('core_message_block_contacts', params); - } - - return promise.then(() => { - return this.invalidateAllMemberInfo(userId, site).finally(() => { - const data = { userId, userBlocked: true }; - this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); - }); - }); - }); - } - - /** - * Confirm a contact request from another user. - * - * @param userId ID of the user who made the contact request. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved when done. - * @since 3.6 - */ - confirmContactRequest(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - userid: userId, - requesteduserid: site.getUserId(), - }; - - return site.write('core_message_confirm_contact_request', params).then(() => { - return this.utils.allPromises([ - this.invalidateAllMemberInfo(userId, site), - this.invalidateContactsCache(site.id), - this.invalidateUserContacts(site.id), - this.refreshContactRequestsCount(site.id), - ]).finally(() => { - const data = { userId, contactRequestConfirmed: true }; - this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); - }); - }); - }); - } - - /** - * Send a contact request to another user. - * - * @param userId ID of the receiver of the contact request. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved when done. - * @since 3.6 - */ - createContactRequest(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - userid: site.getUserId(), - requesteduserid: userId, - }; - - return site.write('core_message_create_contact_request', params).then(() => { - return this.invalidateAllMemberInfo(userId, site).finally(() => { - const data = { userId, contactRequestCreated: true }; - this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); - }); - }); - }); - } - - /** - * Decline a contact request from another user. - * - * @param userId ID of the user who made the contact request. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved when done. - * @since 3.6 - */ - declineContactRequest(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - userid: userId, - requesteduserid: site.getUserId(), - }; - - return site.write('core_message_decline_contact_request', params).then(() => { - return this.utils.allPromises([ - this.invalidateAllMemberInfo(userId, site), - this.refreshContactRequestsCount(site.id), - ]).finally(() => { - const data = { userId, contactRequestDeclined: true }; - this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); - }); - }); - }); - } - - /** - * Delete a conversation. - * - * @param conversationId Conversation to delete. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @return Promise resolved when the conversation has been deleted. - */ - deleteConversation(conversationId: number, siteId?: string, userId?: number): Promise { - return this.deleteConversations([conversationId], siteId, userId); - } - - /** - * Delete several conversations. - * - * @param conversationIds Conversations to delete. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @return Promise resolved when the conversations have been deleted. - */ - deleteConversations(conversationIds: number[], siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const params = { - userid: userId, - conversationids: conversationIds - }; - - return site.write('core_message_delete_conversations_by_id', params).then(() => { - const promises = []; - - conversationIds.forEach((conversationId) => { - promises.push(this.messagesOffline.deleteConversationMessages(conversationId, site.getId()).catch(() => { - // Ignore errors (shouldn't happen). - })); - }); - - return Promise.all(promises); - }); - }); - } - - /** - * Delete a message (online or offline). - * - * @param message Message to delete. - * @param deleteForAll Whether the message should be deleted for all users. - * @return Promise resolved when the message has been deleted. - */ - deleteMessage(message: any, deleteForAll?: boolean): Promise { - if (message.id) { - // Message has ID, it means it has been sent to the server. - if (deleteForAll) { - return this.deleteMessageForAllOnline(message.id); - } else { - return this.deleteMessageOnline(message.id, message.read); - } - } - - // It's an offline message. - if (message.conversationid) { - return this.messagesOffline.deleteConversationMessage(message.conversationid, message.text, message.timecreated); - } else { - return this.messagesOffline.deleteMessage(message.touserid, message.smallmessage, message.timecreated); - } - } - - /** - * Delete a message from the server. - * - * @param id Message ID. - * @param read 1 if message is read, 0 otherwise. - * @param userId User we want to delete the message for. If not defined, use current user. - * @return Promise resolved when the message has been deleted. - */ - deleteMessageOnline(id: number, read: number, userId?: number): Promise { - const params: any = { - messageid: id, - userid: userId || this.sitesProvider.getCurrentSiteUserId() - }; - - if (typeof read != 'undefined') { - params.read = read; - } - - return this.sitesProvider.getCurrentSite().write('core_message_delete_message', params).then(() => { - return this.invalidateDiscussionCache(userId); - }); - } - - /** - * Delete a message for all users. - * - * @param id Message ID. - * @param userId User we want to delete the message for. If not defined, use current user. - * @return Promise resolved when the message has been deleted. - */ - deleteMessageForAllOnline(id: number, userId?: number): Promise { - const params: any = { - messageid: id, - userid: userId || this.sitesProvider.getCurrentSiteUserId() - }; - - return this.sitesProvider.getCurrentSite().write('core_message_delete_message_for_all_users', params).then(() => { - return this.invalidateDiscussionCache(userId); - }); - } - - /** - * Format a conversation. - * - * @param conversation Conversation to format. - * @param userId User ID viewing the conversation. - * @return Formatted conversation. - */ - protected formatConversation(conversation: AddonMessagesConversationFormatted, userId: number) - : AddonMessagesConversationFormatted { - - const numMessages = conversation.messages.length, - lastMessage = numMessages ? conversation.messages[numMessages - 1] : null; - - conversation.lastmessage = lastMessage ? lastMessage.text : null; - conversation.lastmessagedate = lastMessage ? lastMessage.timecreated : null; - conversation.sentfromcurrentuser = lastMessage ? lastMessage.useridfrom == userId : null; - - if (conversation.type != AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) { - const isIndividual = conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, - otherUser = conversation.members.reduce((carry, member) => { - if (!carry && ((isIndividual && member.id != userId) || (!isIndividual && member.id == userId))) { - carry = member; - } - - return carry; - }, null); - - conversation.name = conversation.name ? conversation.name : otherUser.fullname; - conversation.imageurl = conversation.imageurl ? conversation.imageurl : otherUser.profileimageurl; - conversation.userid = otherUser.id; - conversation.showonlinestatus = otherUser.showonlinestatus; - conversation.isonline = otherUser.isonline; - conversation.isblocked = otherUser.isblocked; - conversation.otherUser = otherUser; - } - - return conversation; - } - - /** - * Get the cache key for blocked contacts. - * - * @param userId The user who's contacts we're looking for. - * @return Cache key. - */ - protected getCacheKeyForBlockedContacts(userId: number): string { - return this.ROOT_CACHE_KEY + 'blockedContacts:' + userId; - } - - /** - * Get the cache key for contacts. - * - * @return Cache key. - */ - protected getCacheKeyForContacts(): string { - return this.ROOT_CACHE_KEY + 'contacts'; - } - - /** - * Get the cache key for comfirmed contacts. - * - * @return Cache key. - */ - protected getCacheKeyForUserContacts(): string { - return this.ROOT_CACHE_KEY + 'userContacts'; - } - - /** - * Get the cache key for contact requests. - * - * @return Cache key. - */ - protected getCacheKeyForContactRequests(): string { - return this.ROOT_CACHE_KEY + 'contactRequests'; - } - - /** - * Get the cache key for contact requests count. - * - * @return Cache key. - */ - protected getCacheKeyForContactRequestsCount(): string { - return this.ROOT_CACHE_KEY + 'contactRequestsCount'; - } - - /** - * Get the cache key for a discussion. - * - * @param userId The other person with whom the current user is having the discussion. - * @return Cache key. - */ - getCacheKeyForDiscussion(userId: number): string { - return this.ROOT_CACHE_KEY + 'discussion:' + userId; - } - - /** - * Get the cache key for the message count. - * - * @param userId User ID. - * @return Cache key. - */ - protected getCacheKeyForMessageCount(userId: number): string { - return this.ROOT_CACHE_KEY + 'count:' + userId; - } - - /** - * Get the cache key for unread conversation counts. - * - * @return Cache key. - */ - protected getCacheKeyForUnreadConversationCounts(): string { - return this.ROOT_CACHE_KEY + 'unreadConversationCounts'; - } - - /** - * Get the cache key for the list of discussions. - * - * @return Cache key. - */ - protected getCacheKeyForDiscussions(): string { - return this.ROOT_CACHE_KEY + 'discussions'; - } - - /** - * Get cache key for get conversations. - * - * @param userId User ID. - * @param conversationId Conversation ID. - * @return Cache key. - */ - protected getCacheKeyForConversation(userId: number, conversationId: number): string { - return this.ROOT_CACHE_KEY + 'conversation:' + userId + ':' + conversationId; - } - - /** - * Get cache key for get conversations between users. - * - * @param userId User ID. - * @param otherUserId Other user ID. - * @return Cache key. - */ - protected getCacheKeyForConversationBetweenUsers(userId: number, otherUserId: number): string { - return this.ROOT_CACHE_KEY + 'conversationBetweenUsers:' + userId + ':' + otherUserId; - } - - /** - * Get cache key for get conversation members. - * - * @param userId User ID. - * @param conversationId Conversation ID. - * @return Cache key. - */ - protected getCacheKeyForConversationMembers(userId: number, conversationId: number): string { - return this.ROOT_CACHE_KEY + 'conversationMembers:' + userId + ':' + conversationId; - } - - /** - * Get cache key for get conversation messages. - * - * @param userId User ID. - * @param conversationId Conversation ID. - * @return Cache key. - */ - protected getCacheKeyForConversationMessages(userId: number, conversationId: number): string { - return this.ROOT_CACHE_KEY + 'conversationMessages:' + userId + ':' + conversationId; - } - - /** - * Get cache key for get conversations. - * - * @param userId User ID. - * @param type Filter by type. - * @param favourites Filter favourites. - * @return Cache key. - */ - protected getCacheKeyForConversations(userId: number, type?: number, favourites?: boolean): string { - return this.getCommonCacheKeyForUserConversations(userId) + ':' + type + ':' + favourites; - } - - /** - * Get cache key for conversation counts. - * - * @return Cache key. - */ - protected getCacheKeyForConversationCounts(): string { - return this.ROOT_CACHE_KEY + 'conversationCounts'; - } - - /** - * Get cache key for member info. - * - * @param userId User ID. - * @param otherUserId The other user ID. - * @return Cache key. - */ - protected getCacheKeyForMemberInfo(userId: number, otherUserId: number): string { - return this.ROOT_CACHE_KEY + 'memberInfo:' + userId + ':' + otherUserId; - } - - /** - * Get cache key for get self conversation. - * - * @param userId User ID. - * @return Cache key. - */ - protected getCacheKeyForSelfConversation(userId: number): string { - return this.ROOT_CACHE_KEY + 'selfconversation:' + userId; - } - - /** - * Get common cache key for get user conversations. - * - * @param userId User ID. - * @return Cache key. - */ - protected getCommonCacheKeyForUserConversations(userId: number): string { - return this.getRootCacheKeyForConversations() + userId; - } - - /** - * Get root cache key for get conversations. - * - * @return Cache key. - */ - protected getRootCacheKeyForConversations(): string { - return this.ROOT_CACHE_KEY + 'conversations:'; - } - - /** - * Get all the contacts of the current user. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the WS data. - * @deprecated since Moodle 3.6 - */ - getAllContacts(siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.getContacts(siteId).then((contacts) => { - return this.getBlockedContacts(siteId).then((blocked) => { - contacts.blocked = blocked.users; - this.storeUsersFromAllContacts(contacts); - - return contacts; - }).catch(() => { - // The WS for blocked contacts might fail, but we still want the contacts. - contacts.blocked = []; - this.storeUsersFromAllContacts(contacts); - - return contacts; - }); - }); - } - - /** - * Get all the users blocked by the current user. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the WS data. - */ - getBlockedContacts(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const userId = site.getUserId(), - params = { - userid: userId - }, - preSets = { - cacheKey: this.getCacheKeyForBlockedContacts(userId), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }; - - return site.read('core_message_get_blocked_users', params, preSets); - }); - } - - /** - * Get the contacts of the current user. - * - * This excludes the blocked users. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the WS data. - * @deprecated since Moodle 3.6 - */ - getContacts(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const preSets = { - cacheKey: this.getCacheKeyForContacts(), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }; - - return site.read('core_message_get_contacts', undefined, preSets).then((contacts: AddonMessagesGetContactsResult) => { - // Filter contacts with negative ID, they are notifications. - const validContacts: AddonMessagesGetContactsResult = { - online: [], - offline: [], - strangers: [] - }; - - for (const typeName in contacts) { - if (!validContacts[typeName]) { - validContacts[typeName] = []; - } - - contacts[typeName].forEach((contact) => { - if (contact.id > 0) { - validContacts[typeName].push(contact); - } - }); - } - - return validContacts; - }); - }); - } - - /** - * Get the list of user contacts. - * - * @param limitFrom Position of the first contact to fetch. - * @param limitNum Number of contacts to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the list of user contacts. - * @since 3.6 - */ - getUserContacts(limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS , siteId?: string): - Promise<{contacts: AddonMessagesConversationMember[], canLoadMore: boolean}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - userid: site.getUserId(), - limitfrom: limitFrom, - limitnum: limitNum <= 0 ? 0 : limitNum + 1 - }; - const preSets = { - cacheKey: this.getCacheKeyForUserContacts(), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }; - - return site.read('core_message_get_user_contacts', params, preSets) - .then((contacts: AddonMessagesConversationMember[]) => { - - if (!contacts || !contacts.length) { - return { contacts: [], canLoadMore: false }; - } - - this.userProvider.storeUsers(contacts, site.id); - - if (limitNum <= 0) { - return { contacts, canLoadMore: false }; - } - - return { - contacts: contacts.slice(0, limitNum), - canLoadMore: contacts.length > limitNum - }; - }); - }); - } - - /** - * Get the contact request sent to the current user. - * - * @param limitFrom Position of the first contact request to fetch. - * @param limitNum Number of contact requests to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the list of contact requests. - * @since 3.6 - */ - getContactRequests(limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS, siteId?: string): - Promise<{requests: AddonMessagesConversationMember[], canLoadMore: boolean}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - userid: site.getUserId(), - limitfrom: limitFrom, - limitnum: limitNum <= 0 ? 0 : limitNum + 1 - }; - const preSets = { - cacheKey: this.getCacheKeyForContactRequests(), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }; - - return site.read('core_message_get_contact_requests', data, preSets) - .then((requests: AddonMessagesConversationMember[]) => { - - if (!requests || !requests.length) { - return { requests: [], canLoadMore: false }; - } - - this.userProvider.storeUsers(requests, site.id); - - if (limitNum <= 0) { - return { requests, canLoadMore: false }; - } - - return { - requests: requests.slice(0, limitNum), - canLoadMore: requests.length > limitNum - }; - }); - }); - } - - /** - * Get the number of contact requests sent to the current user. - * - * @param siteId Site ID. If not defined, use current site. - * @return Resolved with the number of contact requests. - * @since 3.6 - */ - getContactRequestsCount(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - userid: site.getUserId(), - }; - const preSets = { - cacheKey: this.getCacheKeyForContactRequestsCount(), - typeExpected: 'number' - }; - - return site.read('core_message_get_received_contact_requests_count', data, preSets).then((count: number) => { - // Notify the new count so all badges are updated. - this.eventsProvider.trigger(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, { count }, site.id); - - return count; - }); - }); - } - - /** - * Get a conversation by the conversation ID. - * - * @param conversationId Conversation ID to fetch. - * @param includeContactRequests Include contact requests. - * @param includePrivacyInfo Include privacy info. - * @param messageOffset Offset for messages list. - * @param messageLimit Limit of messages. Defaults to 1 (last message). - * We recommend getConversationMessages to get them. - * @param memberOffset Offset for members list. - * @param memberLimit Limit of members. Defaults to 2 (to be able to know the other user in individual ones). - * We recommend getConversationMembers to get them. - * @param newestFirst Whether to order messages by newest first. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @return Promise resolved with the response. - * @since 3.6 - */ - getConversation(conversationId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean, - messageOffset: number = 0, messageLimit: number = 1, memberOffset: number = 0, memberLimit: number = 2, - newestFirst: boolean = true, siteId?: string, userId?: number): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const preSets = { - cacheKey: this.getCacheKeyForConversation(userId, conversationId) - }, - params: any = { - userid: userId, - conversationid: conversationId, - includecontactrequests: includeContactRequests ? 1 : 0, - includeprivacyinfo: includePrivacyInfo ? 1 : 0, - messageoffset: messageOffset, - messagelimit: messageLimit, - memberoffset: memberOffset, - memberlimit: memberLimit, - newestmessagesfirst: newestFirst ? 1 : 0 - }; - - return site.read('core_message_get_conversation', params, preSets).then((conversation: AddonMessagesConversation) => { - return this.formatConversation(conversation, userId); - }); - }); - } - - /** - * Get a conversation between two users. - * - * @param otherUserId The other user ID. - * @param includeContactRequests Include contact requests. - * @param includePrivacyInfo Include privacy info. - * @param messageOffset Offset for messages list. - * @param messageLimit Limit of messages. Defaults to 1 (last message). - * We recommend getConversationMessages to get them. - * @param memberOffset Offset for members list. - * @param memberLimit Limit of members. Defaults to 2 (to be able to know the other user in individual ones). - * We recommend getConversationMembers to get them. - * @param newestFirst Whether to order messages by newest first. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @param preferCache True if shouldn't call WS if data is cached, false otherwise. - * @return Promise resolved with the response. - * @since 3.6 - */ - getConversationBetweenUsers(otherUserId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean, - messageOffset: number = 0, messageLimit: number = 1, memberOffset: number = 0, memberLimit: number = 2, - newestFirst: boolean = true, siteId?: string, userId?: number, preferCache?: boolean) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const preSets = { - cacheKey: this.getCacheKeyForConversationBetweenUsers(userId, otherUserId), - omitExpires: !!preferCache, - }, - params: any = { - userid: userId, - otheruserid: otherUserId, - includecontactrequests: includeContactRequests ? 1 : 0, - includeprivacyinfo: includePrivacyInfo ? 1 : 0, - messageoffset: messageOffset, - messagelimit: messageLimit, - memberoffset: memberOffset, - memberlimit: memberLimit, - newestmessagesfirst: newestFirst ? 1 : 0 - }; - - return site.read('core_message_get_conversation_between_users', params, preSets) - .then((conversation: AddonMessagesConversation) => { - return this.formatConversation(conversation, userId); - }); - }); - } - - /** - * Get a conversation members. - * - * @param conversationId Conversation ID to fetch. - * @param limitFrom Offset for members list. - * @param limitTo Limit of members. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in - * @since 3.6 - */ - getConversationMembers(conversationId: number, limitFrom: number = 0, limitTo?: number, includeContactRequests?: boolean, - siteId?: string, userId?: number): Promise<{members: AddonMessagesConversationMember[], canLoadMore: boolean}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - if (typeof limitTo == 'undefined' || limitTo === null) { - limitTo = this.LIMIT_MESSAGES; - } - - const preSets = { - cacheKey: this.getCacheKeyForConversationMembers(userId, conversationId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }, - params: any = { - userid: userId, - conversationid: conversationId, - limitfrom: limitFrom, - limitnum: limitTo < 1 ? limitTo : limitTo + 1, // If there is a limit, get 1 more than requested. - includecontactrequests: includeContactRequests ? 1 : 0, - includeprivacyinfo: 1, - }; - - return site.read('core_message_get_conversation_members', params, preSets) - .then((members: AddonMessagesConversationMember[]) => { - - if (limitTo < 1) { - return { - canLoadMore: false, - members: members - }; - } else { - return { - canLoadMore: members.length > limitTo, - members: members.slice(0, limitTo) - }; - } - - }); - }); - } - - /** - * Get a conversation by the conversation ID. - * - * @param conversationId Conversation ID to fetch. - * @param options Options. - * @return Promise resolved with the response. - * @since 3.6 - */ - async getConversationMessages(conversationId: number, options?: AddonMessagesGetConversationMessagesOptions) - : Promise { - - options = options || {}; - - const site = await this.sitesProvider.getSite(options.siteId); - - options.userId = options.userId || site.getUserId(); - options.limitFrom = options.limitFrom || 0; - options.limitTo = options.limitTo === undefined || options.limitTo === null ? this.LIMIT_MESSAGES : options.limitTo; - options.timeFrom = options.timeFrom || 0; - options.newestFirst = options.newestFirst === undefined || options.newestFirst === null ? true : options.newestFirst; - - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getCacheKeyForConversationMessages(options.userId, conversationId), - }; - const params = { - currentuserid: options.userId, - convid: conversationId, - limitfrom: options.limitFrom, - limitnum: options.limitTo < 1 ? options.limitTo : options.limitTo + 1, // If there's a limit, get 1 more than requested. - newest: options.newestFirst ? 1 : 0, - timefrom: options.timeFrom, - }; - - if (options.limitFrom > 0) { - // Do not use cache when retrieving older messages. - // This is to prevent storing too much data and to prevent inconsistencies between "pages" loaded. - preSets.getFromCache = false; - preSets.saveToCache = false; - preSets.emergencyCache = false; - } else if (options.forceCache) { - preSets.omitExpires = true; - } else if (options.ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - const result: AddonMessagesGetConversationMessagesResult = - await site.read('core_message_get_conversation_messages', params, preSets); - - if (options.limitTo < 1) { - result.canLoadMore = false; - result.messages = result.messages; - } else { - result.canLoadMore = result.messages.length > options.limitTo; - result.messages = result.messages.slice(0, options.limitTo); - } - - let lastReceived; - - result.messages.forEach((message) => { - // Convert time to milliseconds. - message.timecreated = message.timecreated ? message.timecreated * 1000 : 0; - - if (!lastReceived && message.useridfrom != options.userId) { - lastReceived = message; - } - }); - - if (this.appProvider.isDesktop() && options.limitFrom === 0 && lastReceived) { - // Store the last received message (we cannot know if it's unread or not). Don't block the user for this. - this.storeLastReceivedMessageIfNeeded(conversationId, lastReceived, site.getId()); - } - - if (options.excludePending) { - // No need to get offline messages, return the ones we have. - return result; - } - - // Get offline messages. - const offlineMessages = await this.messagesOffline.getConversationMessages(conversationId); - - // Mark offline messages as pending. - offlineMessages.forEach((message) => { - message.pending = true; - message.useridfrom = options.userId; - }); - - result.messages = result.messages.concat(offlineMessages); - - return result; - } - - /** - * Get the discussions of a certain user. This function is used in Moodle sites higher than 3.6. - * If the site is older than 3.6, please use getDiscussions. - * - * @param type Filter by type. - * @param favourites Whether to restrict the results to contain NO favourite conversations (false), ONLY favourite - * conversation (true), or ignore any restriction altogether (undefined or null). - * @param limitFrom The offset to start at. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @param forceCache True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @return Promise resolved with the conversations. - * @since 3.6 - */ - getConversations(type?: number, favourites?: boolean, limitFrom: number = 0, siteId?: string, userId?: number, - forceCache?: boolean, ignoreCache?: boolean) - : Promise<{conversations: AddonMessagesConversationFormatted[], canLoadMore: boolean}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const preSets = { - cacheKey: this.getCacheKeyForConversations(userId, type, favourites) - }, - params: any = { - userid: userId, - limitfrom: limitFrom, - limitnum: this.LIMIT_MESSAGES + 1, - }; - - if (forceCache) { - preSets['omitExpires'] = true; - } else if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; - } - - if (typeof type != 'undefined' && type != null) { - params.type = type; - } - if (typeof favourites != 'undefined' && favourites != null) { - params.favourites = favourites ? 1 : 0; - } - - if (site.isVersionGreaterEqualThan('3.7') && type != AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) { - // Add self conversation to the list. - params.mergeself = 1; - } - - return site.read('core_message_get_conversations', params, preSets).catch((error) => { - if (params.mergeself) { - // Try again without the new param. Maybe the user is offline and he has a previous request cached. - delete params.mergeself; - - return site.read('core_message_get_conversations', params, preSets); - } - - return Promise.reject(error); - }).then((response: AddonMessagesGetConversationsResult) => { - // Format the conversations, adding some calculated fields. - const conversations = response.conversations.slice(0, this.LIMIT_MESSAGES).map((conversation) => { - return this.formatConversation(conversation, userId); - }), - conv = conversations[0], - lastMessage = conv && conv.messages[0]; - - if (this.appProvider.isDesktop() && limitFrom === 0 && lastMessage && !conv.sentfromcurrentuser) { - // Store the last received message (we cannot know if it's unread or not). Don't block the user for this. - this.storeLastReceivedMessageIfNeeded(conv.id, lastMessage, site.getId()); - } - - return { - conversations: conversations, - canLoadMore: response.conversations.length > this.LIMIT_MESSAGES - }; - }); - }); - } - - /** - * Get conversation counts by type. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with favourite, - * individual, group and self conversation counts. - * @since 3.6 - */ - getConversationCounts(siteId?: string): Promise<{favourites: number, individual: number, group: number, self: number}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const preSets = { - cacheKey: this.getCacheKeyForConversationCounts() - }; - - return site.read('core_message_get_conversation_counts', {}, preSets) - .then((result: AddonMessagesGetConversationCountsResult) => { - - const counts = { - favourites: result.favourites, - individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL], - group: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP], - self: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_SELF] || 0 - }; - - return counts; - }); - }); - } - - /** - * Return the current user's discussion with another user. - * - * @param userId The ID of the other user. - * @param excludePending True to exclude messages pending to be sent. - * @param lfReceivedUnread Number of unread received messages already fetched, so fetch will be done from this. - * @param lfReceivedRead Number of read received messages already fetched, so fetch will be done from this. - * @param lfSentUnread Number of unread sent messages already fetched, so fetch will be done from this. - * @param lfSentRead Number of read sent messages already fetched, so fetch will be done from this. - * @param toDisplay True if messages will be displayed to the user, either in view or in a notification. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with messages and a boolean telling if can load more messages. - */ - getDiscussion(userId: number, excludePending: boolean, lfReceivedUnread: number = 0, lfReceivedRead: number = 0, - lfSentUnread: number = 0, lfSentRead: number = 0, toDisplay: boolean = true, siteId?: string) - : Promise<{messages: AddonMessagesGetMessagesMessage[], canLoadMore: boolean}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const result = { - messages: [], - canLoadMore: false - }, - preSets = { - cacheKey: this.getCacheKeyForDiscussion(userId) - }, - params = { - useridto: site.getUserId(), - useridfrom: userId, - limitnum: this.LIMIT_MESSAGES - }; - - let hasReceived, - hasSent; - - if (lfReceivedUnread > 0 || lfReceivedRead > 0 || lfSentUnread > 0 || lfSentRead > 0) { - // Do not use cache when retrieving older messages. - // This is to prevent storing too much data and to prevent inconsistencies between "pages" loaded. - preSets['getFromCache'] = false; - preSets['saveToCache'] = false; - preSets['emergencyCache'] = false; - } - - // Get message received by current user. - return this.getRecentMessages(params, preSets, lfReceivedUnread, lfReceivedRead, toDisplay, site.getId()) - .then((response) => { - result.messages = response; - params.useridto = userId; - params.useridfrom = site.getUserId(); - hasReceived = response.length > 0; - - // Get message sent by current user. - return this.getRecentMessages(params, preSets, lfSentUnread, lfSentRead, toDisplay, siteId); - }).then((response) => { - result.messages = result.messages.concat(response); - hasSent = response.length > 0; - - if (result.messages.length > this.LIMIT_MESSAGES) { - // Sort messages and get the more recent ones. - result.canLoadMore = true; - result.messages = this.sortMessages(result['messages']); - result.messages = result.messages.slice(-this.LIMIT_MESSAGES); - } else { - result.canLoadMore = result.messages.length == this.LIMIT_MESSAGES && (!hasReceived || !hasSent); - } - - if (excludePending) { - // No need to get offline messages, return the ones we have. - return result; - } - - // Get offline messages. - return this.messagesOffline.getMessages(userId).then((offlineMessages) => { - // Mark offline messages as pending. - offlineMessages.forEach((message) => { - message.pending = true; - message.text = message.smallmessage; - }); - - result.messages = result.messages.concat(offlineMessages); - - return result; - }); - }); - }); - } - - /** - * Get the discussions of the current user. This function is used in Moodle sites older than 3.6. - * If the site is 3.6 or higher, please use getConversations. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with an object where the keys are the user ID of the other user. - */ - getDiscussions(siteId?: string): Promise<{[userId: number]: AddonMessagesDiscussion}> { - return this.sitesProvider.getSite(siteId).then((site) => { - const discussions: {[userId: number]: AddonMessagesDiscussion} = {}, - currentUserId = site.getUserId(), - params = { - useridto: currentUserId, - useridfrom: 0, - limitnum: this.LIMIT_MESSAGES - }, - preSets = { - cacheKey: this.getCacheKeyForDiscussions() - }; - - /** - * Convenience function to treat a recent message, adding it to discussions list if needed. - */ - const treatRecentMessage = (message: AddonMessagesGetMessagesMessage, userId: number, userFullname: string): void => { - if (typeof discussions[userId] === 'undefined') { - discussions[userId] = { - fullname: userFullname, - profileimageurl: '' - }; - - if (!message.timeread && !message.pending && message.useridfrom != currentUserId) { - discussions[userId].unread = true; - } - } - - // Extract the most recent message. Pending messages are considered more recent than messages already sent. - const discMessage = discussions[userId].message; - if (typeof discMessage === 'undefined' || (!discMessage.pending && message.pending) || - (discMessage.pending == message.pending && (discMessage.timecreated < message.timecreated || - (discMessage.timecreated == message.timecreated && discMessage.id < message.id)))) { - - discussions[userId].message = { - id: message.id, - user: userId, - message: message.text, - timecreated: message.timecreated, - pending: !!message.pending - }; - } - }; - - // Get recent messages sent to current user. - return this.getRecentMessages(params, preSets, undefined, undefined, undefined, site.getId()).then((messages) => { - - // Extract the discussions by filtering same senders. - messages.forEach((message) => { - treatRecentMessage(message, message.useridfrom, message.userfromfullname); - }); - - // Now get the last messages sent by the current user. - params.useridfrom = params.useridto; - params.useridto = 0; - - return this.getRecentMessages(params, preSets); - }).then((messages) => { - - // Extract the discussions by filtering same senders. - messages.forEach((message) => { - treatRecentMessage(message, message.useridto, message.usertofullname); - }); - - // Now get unsent messages. - return this.messagesOffline.getAllMessages(site.getId()); - }).then((offlineMessages) => { - offlineMessages.forEach((message) => { - message.pending = true; - message.text = message.smallmessage; - treatRecentMessage(message, message.touserid, ''); - }); - - return this.getDiscussionsUserImg(discussions, site.getId()).then((discussions) => { - this.storeUsersFromDiscussions(discussions); - - return discussions; - }); - }); - }); - } - - /** - * Get user images for all the discussions that don't have one already. - * - * @param discussions List of discussions. - * @param siteId Site ID. If not defined, current site. - * @return Promise always resolved. Resolve param is the formatted discussions. - */ - protected getDiscussionsUserImg(discussions: any, siteId?: string): Promise { - const promises = []; - - for (const userId in discussions) { - if (!discussions[userId].profileimageurl) { - // We don't have the user image. Try to retrieve it. - promises.push(this.userProvider.getProfile(discussions[userId].message.user, 0, true, siteId).then((user) => { - discussions[userId].profileimageurl = user.profileimageurl; - }).catch(() => { - // Error getting profile, resolve promise without adding any extra data. - })); - } - } - - return Promise.all(promises).then(() => { - return discussions; - }); - } - - /** - * Get conversation member info by user id, works even if no conversation betwen the users exists. - * - * @param otherUserId The other user ID. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @return Promise resolved with the member info. - * @since 3.6 - */ - getMemberInfo(otherUserId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const preSets = { - cacheKey: this.getCacheKeyForMemberInfo(userId, otherUserId), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }, - params: any = { - referenceuserid: userId, - userids: [otherUserId], - includecontactrequests: 1, - includeprivacyinfo: 1, - }; - - return site.read('core_message_get_member_info', params, preSets) - .then((members: AddonMessagesConversationMember[]): any => { - - if (!members || members.length < 1) { - // Should never happen. - return Promise.reject(null); - } - - return members[0]; - }); - }); - } - - /** - * Get the cache key for the get message preferences call. - * - * @return Cache key. - */ - protected getMessagePreferencesCacheKey(): string { - return this.ROOT_CACHE_KEY + 'messagePreferences'; - } - - /** - * Get message preferences. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the message preferences. - */ - getMessagePreferences(siteId?: string): Promise { - this.logger.debug('Get message preferences'); - - return this.sitesProvider.getSite(siteId).then((site) => { - const preSets = { - cacheKey: this.getMessagePreferencesCacheKey(), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - return site.read('core_message_get_user_message_preferences', {}, preSets) - .then((data: AddonMessagesGetUserMessagePreferencesResult): any => { - - if (data.preferences) { - data.preferences.blocknoncontacts = data.blocknoncontacts; - - return data.preferences; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get messages according to the params. - * - * @param params Parameters to pass to the WS. - * @param preSets Set of presets for the WS. - * @param toDisplay True if messages will be displayed to the user, either in view or in a notification. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the data. - */ - protected getMessages(params: any, preSets: any, toDisplay: boolean = true, siteId?: string) - : Promise { - - params['type'] = 'conversations'; - params['newestfirst'] = 1; - - return this.sitesProvider.getSite(siteId).then((site) => { - const userId = site.getUserId(); - - return site.read('core_message_get_messages', params, preSets).then((response: AddonMessagesGetMessagesResult) => { - response.messages.forEach((message) => { - message.read = params.read == 0 ? 0 : 1; - // Convert times to milliseconds. - message.timecreated = message.timecreated ? message.timecreated * 1000 : 0; - message.timeread = message.timeread ? message.timeread * 1000 : 0; - }); - - if (toDisplay && this.appProvider.isDesktop() && !params.read && params.useridto == userId && - params.limitfrom === 0) { - // Store the last unread received messages. Don't block the user for this. - this.storeLastReceivedMessageIfNeeded(params.useridfrom, response.messages[0], site.getId()); - } - - return response; - }); - }); - } - - /** - * Get the most recent messages. - * - * @param params Parameters to pass to the WS. - * @param preSets Set of presets for the WS. - * @param limitFromUnread Number of read messages already fetched, so fetch will be done from this number. - * @param limitFromRead Number of unread messages already fetched, so fetch will be done from this number. - * @param toDisplay True if messages will be displayed to the user, either in view or in a notification. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the data. - */ - getRecentMessages(params: any, preSets: any, limitFromUnread: number = 0, limitFromRead: number = 0, - toDisplay: boolean = true, siteId?: string): Promise { - limitFromUnread = limitFromUnread || 0; - limitFromRead = limitFromRead || 0; - - params['read'] = 0; - params['limitfrom'] = limitFromUnread; - - return this.getMessages(params, preSets, toDisplay, siteId).then((response) => { - let messages = response.messages; - if (messages) { - if (messages.length >= params.limitnum) { - return messages; - } - - // We need to fetch more messages. - params.limitnum = params.limitnum - messages.length; - params.read = 1; - params.limitfrom = limitFromRead; - - return this.getMessages(params, preSets, toDisplay, siteId).then((response) => { - if (response.messages) { - messages = messages.concat(response.messages); - } - - return messages; - }).catch(() => { - return messages; - }); - - } else { - return Promise.reject(null); - } - }); - } - - /** - * Get a self conversation. - * - * @param messageOffset Offset for messages list. - * @param messageLimit Limit of messages. Defaults to 1 (last message). - * We recommend getConversationMessages to get them. - * @param newestFirst Whether to order messages by newest first. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID to get the self conversation for. If not defined, current user in the site. - * @return Promise resolved with the response. - * @since 3.7 - */ - getSelfConversation(messageOffset: number = 0, messageLimit: number = 1, newestFirst: boolean = true, siteId?: string, - userId?: number): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const preSets = { - cacheKey: this.getCacheKeyForSelfConversation(userId) - }, - params: any = { - userid: userId, - messageoffset: messageOffset, - messagelimit: messageLimit, - newestmessagesfirst: newestFirst ? 1 : 0 - }; - - return site.read('core_message_get_self_conversation', params, preSets) - .then((conversation: AddonMessagesConversation) => { - return this.formatConversation(conversation, userId); - }); - }); - } - - /** - * Get unread conversation counts by type. - * - * @param siteId Site ID. If not defined, use current site. - * @return Resolved with the unread favourite, individual and group conversation counts. - */ - getUnreadConversationCounts(siteId?: string): - Promise<{favourites: number, individual: number, group: number, self: number, orMore?: boolean}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - let promise: Promise<{favourites: number, individual: number, group: number, self: number, orMore?: boolean}>; - - if (this.isGroupMessagingEnabled()) { - // @since 3.6 - const preSets = { - cacheKey: this.getCacheKeyForUnreadConversationCounts() - }; - - promise = site.read('core_message_get_unread_conversation_counts', {}, preSets) - .then((result: AddonMessagesGetUnreadConversationCountsResult) => { - return { - favourites: result.favourites, - individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL], - group: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP], - self: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_SELF] || 0 - }; - }); - - } else if (this.isMessageCountEnabled()) { - // @since 3.2 - const params = { - useridto: site.getUserId(), - }, - preSets = { - cacheKey: this.getCacheKeyForMessageCount(site.getUserId()), - typeExpected: 'number' - }; - - promise = site.read('core_message_get_unread_conversations_count', params, preSets).then((count: number) => { - return { favourites: 0, individual: count, group: 0, self: 0 }; - }); - } else { - // Fallback call. - const params = { - read: 0, - limitfrom: 0, - limitnum: this.LIMIT_MESSAGES + 1, - useridto: site.getUserId(), - useridfrom: 0, - }; - - promise = this.getMessages(params, undefined, false, siteId).then((response) => { - // Count the discussions by filtering same senders. - const discussions = {}; - response.messages.forEach((message) => { - discussions[message.useridto] = 1; - }); - - const count = Object.keys(discussions).length; - - return { - favourites: 0, - individual: count, - group: 0, - self: 0, - orMore: count > this.LIMIT_MESSAGES - }; - }); - } - - return promise.then((counts) => { - // Notify the new counts so all views are updated. - this.eventsProvider.trigger(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, counts, site.id); - - return counts; - }); - }); - } - - /** - * Get the latest unread received messages. - * - * @param toDisplay True if messages will be displayed to the user, either in view or in a notification. - * @param forceCache True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the message unread count. - */ - getUnreadReceivedMessages(toDisplay: boolean = true, forceCache: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - read: 0, - limitfrom: 0, - limitnum: this.LIMIT_MESSAGES, - useridto: site.getUserId(), - useridfrom: 0 - }, - preSets = {}; - - if (forceCache) { - preSets['omitExpires'] = true; - } else if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; - } - - return this.getMessages(params, preSets, toDisplay, siteId); - }); - } - - /** - * Invalidate all contacts cache. - * - * @param userId The user ID. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateAllContactsCache(userId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.invalidateContactsCache(siteId).then(() => { - return this.invalidateBlockedContactsCache(userId, siteId); - }); - } - - /** - * Invalidate blocked contacts cache. - * - * @param userId The user ID. - * @param siteId Site ID. If not defined, current site. - */ - invalidateBlockedContactsCache(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCacheKeyForBlockedContacts(userId)); - }); - } - - /** - * Invalidate contacts cache. - * - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateContactsCache(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCacheKeyForContacts()); - }); - } - - /** - * Invalidate user contacts cache. - * - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateUserContacts(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCacheKeyForUserContacts()); - }); - } - - /** - * Invalidate contact requests cache. - * - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateContactRequestsCache(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCacheKeyForContactRequests()); - }); - } - - /** - * Invalidate contact requests count cache. - * - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateContactRequestsCountCache(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCacheKeyForContactRequestsCount()); - }); - } - - /** - * Invalidate conversation. - * - * @param conversationId Conversation ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - invalidateConversation(conversationId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCacheKeyForConversation(userId, conversationId)); - }); - } - - /** - * Invalidate conversation between users. - * - * @param otherUserId Other user ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - invalidateConversationBetweenUsers(otherUserId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCacheKeyForConversationBetweenUsers(userId, otherUserId)); - }); - } - - /** - * Invalidate conversation members cache. - * - * @param conversationId Conversation ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - invalidateConversationMembers(conversationId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCacheKeyForConversationMembers(userId, conversationId)); - }); - } - - /** - * Invalidate conversation messages cache. - * - * @param conversationId Conversation ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - invalidateConversationMessages(conversationId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCacheKeyForConversationMessages(userId, conversationId)); - }); - } - - /** - * Invalidate conversations cache. - * - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - invalidateConversations(siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKeyStartingWith(this.getCommonCacheKeyForUserConversations(userId)); - }); - } - - /** - * Invalidate conversation counts cache. - * - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateConversationCounts(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCacheKeyForConversationCounts()); - }); - } - - /** - * Invalidate discussion cache. - * - * @param userId The user ID with whom the current user is having the discussion. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateDiscussionCache(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCacheKeyForDiscussion(userId)); - }); - } - - /** - * Invalidate discussions cache. - * - * Note that {@link this.getDiscussions} uses the contacts, so we need to invalidate contacts too. - * - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateDiscussionsCache(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const promises = []; - promises.push(site.invalidateWsCacheForKey(this.getCacheKeyForDiscussions())); - promises.push(this.invalidateContactsCache(site.getId())); - - return Promise.all(promises); - }); - } - - /** - * Invalidate member info cache. - * - * @param otherUserId The other user ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - invalidateMemberInfo(otherUserId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCacheKeyForMemberInfo(userId, otherUserId)); - }); - } - - /** - * Invalidate get message preferences. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateMessagePreferences(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getMessagePreferencesCacheKey()); - }); - } - - /** - * Invalidate all cache entries with member info. - * - * @param userId Id of the user to invalidate. - * @param site Site object. - * @return Promise resolved when done. - */ - protected invalidateAllMemberInfo(userId: number, site: CoreSite): Promise { - return this.utils.allPromises([ - this.invalidateMemberInfo(userId, site.id), - this.invalidateUserContacts(site.id), - this.invalidateContactRequestsCache(site.id), - this.invalidateConversations(site.id), - this.getConversationBetweenUsers(userId, undefined, undefined, undefined, undefined, undefined, undefined, undefined, - site.id, undefined, true).then((conversation) => { - return this.utils.allPromises([ - this.invalidateConversation(conversation.id), - this.invalidateConversationMembers(conversation.id, site.id), - ]); - }).catch(() => { - // The conversation does not exist or we can't fetch it now, ignore it. - }) - ]); - } - - /** - * Invalidate a self conversation. - * - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - invalidateSelfConversation(siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCacheKeyForSelfConversation(userId)); - }); - } - - /** - * Invalidate unread conversation counts cache. - * - * @param siteId Site ID. If not defined, current site. - * @return Resolved when done. - */ - invalidateUnreadConversationCounts(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - if (this.isGroupMessagingEnabled()) { - // @since 3.6 - return site.invalidateWsCacheForKey(this.getCacheKeyForUnreadConversationCounts()); - - } else if (this.isMessageCountEnabled()) { - // @since 3.2 - return site.invalidateWsCacheForKey(this.getCacheKeyForMessageCount(site.getUserId())); - } - }); - } - - /** - * Checks if the a user is blocked by the current user. - * - * @param userId The user ID to check against. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved with boolean, rejected when we do not know. - */ - isBlocked(userId: number, siteId?: string): Promise { - if (this.isGroupMessagingEnabled()) { - return this.getMemberInfo(userId, siteId).then((member) => { - return member.isblocked; - }); - } - - return this.getBlockedContacts(siteId).then((blockedContacts) => { - if (!blockedContacts.users || blockedContacts.users.length < 1) { - return false; - } - - return blockedContacts.users.some((user) => { - return userId == user.id; - }); - }); - } - - /** - * Checks if the a user is a contact of the current user. - * - * @param userId The user ID to check against. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved with boolean, rejected when we do not know. - */ - isContact(userId: number, siteId?: string): Promise { - if (this.isGroupMessagingEnabled()) { - return this.getMemberInfo(userId, siteId).then((member) => { - return member.iscontact; - }); - } - - return this.getContacts(siteId).then((contacts) => { - return ['online', 'offline'].some((type) => { - if (contacts[type] && contacts[type].length > 0) { - return contacts[type].some((user) => { - return userId == user.id; - }); - } - - return false; - }); - }); - } - - /** - * Returns whether or not group messaging is supported. - * - * @return If related WS is available on current site. - * @since 3.6 - */ - isGroupMessagingEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('core_message_get_conversations'); - } - - /** - * Returns whether or not group messaging is supported in a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether related WS is available on a certain site. - * @since 3.6 - */ - isGroupMessagingEnabledInSite(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('core_message_get_conversations'); - }).catch(() => { - return false; - }); - } - - /** - * Returns whether or not we can mark all messages as read. - * - * @return If related WS is available on current site. - * @since 3.2 - */ - isMarkAllMessagesReadEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('core_message_mark_all_conversation_messages_as_read') || - this.sitesProvider.wsAvailableInCurrentSite('core_message_mark_all_messages_as_read'); - } - - /** - * Returns whether or not we can count unread messages. - * - * @return True if enabled, false otherwise. - * @since 3.2 - */ - isMessageCountEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('core_message_get_unread_conversations_count'); - } - - /** - * Returns whether or not the message preferences are enabled for the current site. - * - * @return True if enabled, false otherwise. - * @since 3.2 - */ - isMessagePreferencesEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('core_message_get_user_message_preferences'); - } - - /** - * Returns whether or not messaging is enabled for a certain site. - * - * This could call a WS so do not abuse this method. - * - * @param siteId Site ID. If not defined, current site. - * @return Resolved when enabled, otherwise rejected. - */ - isMessagingEnabledForSite(siteId?: string): Promise { - return this.isPluginEnabled(siteId).then((enabled) => { - if (!enabled) { - return Promise.reject(null); - } - }); - } - - /** - * Returns whether or not a site supports muting or unmuting a conversation. - * - * @param site The site to check, undefined for current site. - * @return If related WS is available on current site. - * @since 3.7 - */ - isMuteConversationEnabled(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.wsAvailable('core_message_mute_conversations'); - } - - /** - * Returns whether or not a site supports muting or unmuting a conversation. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether related WS is available on a certain site. - * @since 3.7 - */ - isMuteConversationEnabledInSite(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.isMuteConversationEnabled(site); - }).catch(() => { - return false; - }); - } - - /** - * Returns whether or not the plugin is enabled in a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if enabled, rejected or resolved with false otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.canUseAdvancedFeature('messaging'); - }); - } - - /** - * Returns whether or not we can search messages. - * - * @since 3.2 - */ - isSearchMessagesEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('core_message_data_for_messagearea_search_messages'); - } - - /** - * Returns whether or not self conversation is supported in a certain site. - * - * @param site Site. If not defined, current site. - * @return If related WS is available on the site. - * @since 3.7 - */ - isSelfConversationEnabled(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.wsAvailable('core_message_get_self_conversation'); - } - - /** - * Returns whether or not self conversation is supported in a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether related WS is available on a certain site. - * @since 3.7 - */ - isSelfConversationEnabledInSite(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.isSelfConversationEnabled(site); - }).catch(() => { - return false; - }); - } - - /** - * Mark message as read. - * - * @param messageId ID of message to mark as read - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean marking success or not. - */ - markMessageRead(messageId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - messageid: messageId, - timeread: this.timeUtils.timestamp() - }; - - return site.write('core_message_mark_message_read', params); - }); - } - - /** - * Mark all messages of a conversation as read. - * - * @param conversationId Conversation ID. - * @return Promise resolved if success. - * @since 3.6 - */ - markAllConversationMessagesRead(conversationId?: number): Promise { - const params = { - userid: this.sitesProvider.getCurrentSiteUserId(), - conversationid: conversationId - }, - preSets = { - responseExpected: false - }; - - return this.sitesProvider.getCurrentSite().write('core_message_mark_all_conversation_messages_as_read', params, preSets); - } - - /** - * Mark all messages of a discussion as read. - * - * @param userIdFrom User Id for the sender. - * @return Promise resolved with boolean marking success or not. - */ - markAllMessagesRead(userIdFrom?: number): Promise { - const params = { - useridto: this.sitesProvider.getCurrentSiteUserId(), - useridfrom: userIdFrom - }, - preSets = { - typeExpected: 'boolean' - }; - - return this.sitesProvider.getCurrentSite().write('core_message_mark_all_messages_as_read', params, preSets); - } - - /** - * Mute or unmute a conversation. - * - * @param conversationId Conversation ID. - * @param set Whether to mute or unmute. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - muteConversation(conversationId: number, set: boolean, siteId?: string, userId?: number): Promise { - return this.muteConversations([conversationId], set, siteId, userId); - } - - /** - * Mute or unmute some conversations. - * - * @param conversations Conversation IDs. - * @param set Whether to mute or unmute. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - muteConversations(conversations: number[], set: boolean, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const params = { - userid: userId, - conversationids: conversations - }, - wsName = set ? 'core_message_mute_conversations' : 'core_message_unmute_conversations'; - - return site.write(wsName, params).then(() => { - // Invalidate the conversations data. - const promises = []; - - conversations.forEach((conversationId) => { - promises.push(this.invalidateConversation(conversationId, site.getId(), userId)); - }); - - return Promise.all(promises).catch(() => { - // Ignore errors. - }); - }); - }); - } - - /** - * Refresh the number of contact requests sent to the current user. - * - * @param siteId Site ID. If not defined, use current site. - * @return Resolved with the number of contact requests. - * @since 3.6 - */ - refreshContactRequestsCount(siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.invalidateContactRequestsCountCache(siteId).then(() => { - return this.getContactRequestsCount(siteId); - }); - } - - /** - * Refresh unread conversation counts and trigger event. - * - * @param siteId Site ID. If not defined, use current site. - * @return Resolved with the unread favourite, individual and group conversation counts. - */ - refreshUnreadConversationCounts(siteId?: string): - Promise<{favourites: number, individual: number, group: number, orMore?: boolean}> { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.invalidateUnreadConversationCounts(siteId).then(() => { - return this.getUnreadConversationCounts(siteId); - }); - } - - /** - * Remove a contact. - * - * @param userId User ID of the person to remove. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved when done. - */ - removeContact(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - userids: [ userId ] - }, - preSets = { - responseExpected: false - }; - - return site.write('core_message_delete_contacts', params, preSets).then(() => { - if (this.isGroupMessagingEnabled()) { - return this.utils.allPromises([ - this.invalidateUserContacts(site.id), - this.invalidateAllMemberInfo(userId, site), - ]).then(() => { - const data = { userId, contactRemoved: true }; - this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); - }); - } else { - return this.invalidateContactsCache(site.id); - } - }); - }); - } - - /** - * Search for contacts. - * - * By default this only returns the first 100 contacts, but note that the WS can return thousands - * of results which would take a while to process. The limit here is just a convenience to - * prevent viewed to crash because too many DOM elements are created. - * - * @param query The query string. - * @param limit The number of results to return, 0 for none. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the contacts. - */ - searchContacts(query: string, limit: number = 100, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - searchtext: query, - onlymycourses: 0 - }, - preSets = { - getFromCache: false // Always try to get updated data. If it fails, it will get it from cache. - }; - - return site.read('core_message_search_contacts', data, preSets) - .then((contacts: AddonMessagesSearchContactsContact[]) => { - - if (limit && contacts.length > limit) { - contacts = contacts.splice(0, limit); - } - this.userProvider.storeUsers(contacts); - - return contacts; - }); - }); - } - - /** - * Search for all the messges with a specific text. - * - * @param query The query string. - * @param userId The user ID. If not defined, current user. - * @param limitFrom Position of the first result to get. Defaults to 0. - * @param limitNum Number of results to get. Defaults to AddonMessagesProvider.LIMIT_SEARCH. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the results. - */ - searchMessages(query: string, userId?: number, limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_SEARCH, - siteId?: string): Promise<{messages: AddonMessagesMessageAreaContact[], canLoadMore: boolean}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - userid: userId || site.getUserId(), - search: query, - limitfrom: limitFrom, - limitnum: limitNum <= 0 ? 0 : limitNum + 1 - }, - preSets = { - getFromCache: false // Always try to get updated data. If it fails, it will get it from cache. - }; - - return site.read('core_message_data_for_messagearea_search_messages', params, preSets) - .then((result: AddonMessagesDataForMessageAreaSearchMessagesResult) => { - - if (!result.contacts || !result.contacts.length) { - return { messages: [], canLoadMore: false }; - } - - result.contacts.forEach((contact) => { - contact.id = contact.userid; - }); - - this.userProvider.storeUsers(result.contacts, site.id); - - if (limitNum <= 0) { - return { messages: result.contacts, canLoadMore: false }; - } - - return { - messages: result.contacts.slice(0, limitNum), - canLoadMore: result.contacts.length > limitNum - }; - }); - }); - } - - /** - * Search for users. - * - * @param query Text to search for. - * @param limitFrom Position of the first found user to fetch. - * @param limitNum Number of found users to fetch. Defaults to AddonMessagesProvider.LIMIT_SEARCH. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved with two lists of found users: contacts and non-contacts. - * @since 3.6 - */ - searchUsers(query: string, limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_SEARCH, siteId?: string): - Promise<{contacts: AddonMessagesConversationMember[], nonContacts: AddonMessagesConversationMember[], - canLoadMoreContacts: boolean, canLoadMoreNonContacts: boolean}> { - - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - userid: site.getUserId(), - search: query, - limitfrom: limitFrom, - limitnum: limitNum <= 0 ? 0 : limitNum + 1 - }, - preSets = { - getFromCache: false // Always try to get updated data. If it fails, it will get it from cache. - }; - - return site.read('core_message_message_search_users', data, preSets).then((result: AddonMessagesSearchUsersResult) => { - const contacts = result.contacts || []; - const nonContacts = result.noncontacts || []; - - this.userProvider.storeUsers(contacts, site.id); - this.userProvider.storeUsers(nonContacts, site.id); - - if (limitNum <= 0) { - return { contacts, nonContacts, canLoadMoreContacts: false, canLoadMoreNonContacts: false }; - } - - return { - contacts: contacts.slice(0, limitNum), - nonContacts: nonContacts.slice(0, limitNum), - canLoadMoreContacts: contacts.length > limitNum, - canLoadMoreNonContacts: nonContacts.length > limitNum - }; - }); - }); - } - - /** - * Send a message to someone. - * - * @param userIdTo User ID to send the message to. - * @param message The message to send - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with: - * - sent (Boolean) True if message was sent to server, false if stored in device. - * - message (Object) If sent=false, contains the stored message. - */ - sendMessage(toUserId: number, message: string, siteId?: string) - : Promise<{sent: boolean, message: AddonMessagesSendInstantMessagesMessage}> { - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.messagesOffline.saveMessage(toUserId, message, siteId).then((entry) => { - return { - sent: false, - message: entry - }; - }); - }; - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (!this.appProvider.isOnline()) { - // App is offline, store the message. - return storeOffline(); - } - - // Check if this conversation already has offline messages. - // If so, store this message since they need to be sent in order. - return this.messagesOffline.hasMessages(toUserId, siteId).catch(() => { - // Error, it's safer to assume it has messages. - return true; - }).then((hasStoredMessages) => { - if (hasStoredMessages) { - return storeOffline(); - } - - // Online and no messages stored. Send it to server. - return this.sendMessageOnline(toUserId, message).then((result) => { - return { - sent: true, - message: result - }; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // It's a WebService error, the user cannot send the message so don't store it. - return Promise.reject(error); - } - - // Error sending message, store it to retry later. - return storeOffline(); - }); - }); - } - - /** - * Send a message to someone. It will fail if offline or cannot connect. - * - * @param toUserId User ID to send the message to. - * @param message The message to send - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected if failure. - */ - sendMessageOnline(toUserId: number, message: string, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const messages = [ - { - touserid: toUserId, - text: message, - textformat: 1 - } - ]; - - return this.sendMessagesOnline(messages, siteId).then((response) => { - if (response && response[0] && response[0].msgid === -1) { - // There was an error, and it should be translated already. - return Promise.reject(this.utils.createFakeWSError(response[0].errormessage)); - } - - return this.invalidateDiscussionCache(toUserId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - return response[0]; - }); - }); - } - - /** - * Send some messages. It will fail if offline or cannot connect. - * IMPORTANT: Sending several messages at once for the same discussions can cause problems with display order, - * since messages with same timecreated aren't ordered by ID. - * - * @param messages Messages to send. Each message must contain touserid, text and textformat. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected if failure. Promise resolved doesn't mean that messages - * have been sent, the resolve param can contain errors for messages not sent. - */ - sendMessagesOnline(messages: any[], siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - messages: messages - }; - - return site.write('core_message_send_instant_messages', data); - }); - } - - /** - * Send a message to a conversation. - * - * @param conversation Conversation. - * @param message The message to send. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with: - * - sent (boolean) True if message was sent to server, false if stored in device. - * - message (any) If sent=false, contains the stored message. - * @since 3.6 - */ - sendMessageToConversation(conversation: any, message: string, siteId?: string) - : Promise<{sent: boolean, message: AddonMessagesSendMessagesToConversationMessage}> { - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.messagesOffline.saveConversationMessage(conversation, message, siteId).then((entry) => { - return { - sent: false, - message: entry - }; - }); - }; - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (!this.appProvider.isOnline()) { - // App is offline, store the message. - return storeOffline(); - } - - // Check if this conversation already has offline messages. - // If so, store this message since they need to be sent in order. - return this.messagesOffline.hasConversationMessages(conversation.id, siteId).catch(() => { - // Error, it's safer to assume it has messages. - return true; - }).then((hasStoredMessages) => { - if (hasStoredMessages) { - return storeOffline(); - } - - // Online and no messages stored. Send it to server. - return this.sendMessageToConversationOnline(conversation.id, message).then((result) => { - return { - sent: true, - message: result - }; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // It's a WebService error, the user cannot send the message so don't store it. - return Promise.reject(error); - } - - // Error sending message, store it to retry later. - return storeOffline(); - }); - }); - } - - /** - * Send a message to a conversation. It will fail if offline or cannot connect. - * - * @param conversationId Conversation ID. - * @param message The message to send - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected if failure. - * @since 3.6 - */ - sendMessageToConversationOnline(conversationId: number, message: string, siteId?: string) - : Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const messages = [ - { - text: message, - textformat: 1 - } - ]; - - return this.sendMessagesToConversationOnline(conversationId, messages, siteId).then((response) => { - return this.invalidateConversationMessages(conversationId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - return response[0]; - }); - }); - } - - /** - * Send some messages to a conversation. It will fail if offline or cannot connect. - * - * @param conversationId Conversation ID. - * @param messages Messages to send. Each message must contain text and, optionally, textformat. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected if failure. - * @since 3.6 - */ - sendMessagesToConversationOnline(conversationId: number, messages: any[], siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - conversationid: conversationId, - messages: messages.map((message) => { - return { - text: message.text, - textformat: typeof message.textformat != 'undefined' ? message.textformat : 1 - }; - }) - }; - - return site.write('core_message_send_messages_to_conversation', params); - }); - } - - /** - * Set or unset a conversation as favourite. - * - * @param conversationId Conversation ID. - * @param set Whether to set or unset it as favourite. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - setFavouriteConversation(conversationId: number, set: boolean, siteId?: string, userId?: number): Promise { - return this.setFavouriteConversations([conversationId], set, siteId, userId); - } - - /** - * Set or unset some conversations as favourites. - * - * @param conversations Conversation IDs. - * @param set Whether to set or unset them as favourites. - * @param siteId Site ID. If not defined, use current site. - * @param userId User ID. If not defined, current user in the site. - * @return Resolved when done. - */ - setFavouriteConversations(conversations: number[], set: boolean, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const params = { - userid: userId, - conversations: conversations - }, - wsName = set ? 'core_message_set_favourite_conversations' : 'core_message_unset_favourite_conversations'; - - return site.write(wsName, params).then(() => { - // Invalidate the conversations data. - const promises = []; - - conversations.forEach((conversationId) => { - promises.push(this.invalidateConversation(conversationId, site.getId(), userId)); - }); - - return Promise.all(promises).catch(() => { - // Ignore errors. - }); - }); - }); - } - - /** - * Helper method to sort conversations by last message time. - * - * @param conversations Array of conversations. - * @return Conversations sorted with most recent last. - */ - sortConversations(conversations: AddonMessagesConversationFormatted[]): AddonMessagesConversationFormatted[] { - return conversations.sort((a, b) => { - const timeA = Number(a.lastmessagedate), - timeB = Number(b.lastmessagedate); - - if (timeA == timeB && a.id) { - // Same time, sort by ID. - return a.id <= b.id ? 1 : -1; - } - - return timeA <= timeB ? 1 : -1; - }); - } - - /** - * Helper method to sort messages by time. - * - * @param messages Array of messages containing the key 'timecreated'. - * @return Messages sorted with most recent last. - */ - sortMessages(messages: any[]): any[] { - return messages.sort((a, b) => { - // Pending messages last. - if (a.pending && !b.pending) { - return 1; - } else if (!a.pending && b.pending) { - return -1; - } - - const timecreatedA = parseInt(a.timecreated, 10), - timecreatedB = parseInt(b.timecreated, 10); - if (timecreatedA == timecreatedB && a.id) { - // Same time, sort by ID. - return a.id >= b.id ? 1 : -1; - } - - return timecreatedA >= timecreatedB ? 1 : -1; - }); - } - - /** - * Store the last received message if it's newer than the last stored. - * - * @param convIdOrUserIdFrom Conversation ID (3.6+) or ID of the useridfrom retrieved (3.5-), 0 for all users. - * @param message Last message received. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - protected storeLastReceivedMessageIfNeeded(convIdOrUserIdFrom: number, - message: AddonMessagesGetMessagesMessage | AddonMessagesConversationMessage, siteId?: string): Promise { - - const component = AddonMessagesProvider.PUSH_SIMULATION_COMPONENT; - - // Get the last received message. - return this.emulatorHelper.getLastReceivedNotification(component, siteId).then((lastMessage) => { - if (convIdOrUserIdFrom > 0 && (!message || !lastMessage)) { - // Seeing a single discussion. No received message or cannot know if it really is the last received message. Stop. - return; - } - - if (message && lastMessage && message.timecreated <= lastMessage.timecreated) { - // The message isn't newer than the stored message, don't store it. - return; - } - - return this.emulatorHelper.storeLastReceivedNotification(component, message, siteId); - }); - } - - /** - * Store user data from contacts in local DB. - * - * @param contactTypes List of contacts grouped in types. - */ - protected storeUsersFromAllContacts(contactTypes: AddonMessagesGetContactsResult): void { - for (const x in contactTypes) { - this.userProvider.storeUsers(contactTypes[x]); - } - } - - /** - * Store user data from discussions in local DB. - * - * @param discussions List of discussions. - * @param siteId Site ID. If not defined, current site. - */ - protected storeUsersFromDiscussions(discussions: any, siteId?: string): void { - const users = []; - for (const userId in discussions) { - users.push({ - id: userId, - fullname: discussions[userId].fullname, - profileimageurl: discussions[userId].profileimageurl - }); - } - this.userProvider.storeUsers(users, siteId); - } - - /** - * Unblock a user. - * - * @param userId User ID of the person to unblock. - * @param siteId Site ID. If not defined, use current site. - * @return Resolved when done. - */ - unblockContact(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - let promise; - if (site.wsAvailable('core_message_unblock_user')) { - // Since Moodle 3.6 - const params = { - userid: site.getUserId(), - unblockeduserid: userId, - }; - promise = site.write('core_message_unblock_user', params); - } else { - const params = { - userids: [userId] - }; - const preSets = { - responseExpected: false - }; - promise = site.write('core_message_unblock_contacts', params, preSets); - } - - return promise.then(() => { - return this.invalidateAllMemberInfo(userId, site).finally(() => { - const data = { userId, userUnblocked: true }; - this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id); - }); - }); - }); - } -} - -/** - * Options to pass to getConversationMessages. - */ -export type AddonMessagesGetConversationMessagesOptions = { - excludePending?: boolean; // True to exclude messages pending to be sent. - limitFrom?: number; // Offset for messages list. Defaults to 0. - limitTo?: number; // Limit of messages. - newestFirst?: boolean; // Whether to order messages by newest first. - timeFrom?: number; // The timestamp from which the messages were created (in seconds). Defaults to 0. - siteId?: string; // Site ID. If not defined, use current site. - userId?: number; // User ID. If not defined, current user in the site. - forceCache?: boolean; // True if it should return cached data. Has priority over ignoreCache. - ignoreCache?: boolean; // True if it should ignore cached data (it will always fail in offline or server down). -}; - -/** - * Conversation. - */ -export type AddonMessagesConversation = { - id: number; // The conversation id. - name: string; // The conversation name, if set. - subname: string; // A subtitle for the conversation name, if set. - imageurl: string; // A link to the conversation picture, if set. - type: number; // The type of the conversation (1=individual,2=group,3=self). - membercount: number; // Total number of conversation members. - ismuted: boolean; // @since 3.7. If the user muted this conversation. - isfavourite: boolean; // If the user marked this conversation as a favourite. - isread: boolean; // If the user has read all messages in the conversation. - unreadcount: number; // The number of unread messages in this conversation. - members: AddonMessagesConversationMember[]; - messages: AddonMessagesConversationMessage[]; - candeletemessagesforallusers: boolean; // @since 3.7. If the user can delete messages in the conversation for all users. -}; - -/** - * Conversation with some calculated data. - */ -export type AddonMessagesConversationFormatted = AddonMessagesConversation & { - lastmessage?: string; // Calculated in the app. Last message. - lastmessagedate?: number; // Calculated in the app. Date the last message was sent. - sentfromcurrentuser?: boolean; // Calculated in the app. Whether last message was sent by the current user. - name?: string; // Calculated in the app. If private conversation, name of the other user. - userid?: number; // Calculated in the app. URL. If private conversation, ID of the other user. - showonlinestatus?: boolean; // Calculated in the app. If private conversation, whether to show online status of the other user. - isonline?: boolean; // Calculated in the app. If private conversation, whether the other user is online. - isblocked?: boolean; // Calculated in the app. If private conversation, whether the other user is blocked. - otherUser?: AddonMessagesConversationMember; // Calculated in the app. Other user in the conversation. -}; - -/** - * Conversation member. - */ -export type AddonMessagesConversationMember = { - id: number; // The user id. - fullname: string; // The user's name. - profileurl: string; // The link to the user's profile page. - profileimageurl: string; // User picture URL. - profileimageurlsmall: string; // Small user picture URL. - isonline: boolean; // The user's online status. - showonlinestatus: boolean; // Show the user's online status?. - isblocked: boolean; // If the user has been blocked. - iscontact: boolean; // Is the user a contact?. - isdeleted: boolean; // Is the user deleted?. - canmessageevenifblocked: boolean; // @since 3.8. If the user can still message even if they get blocked. - canmessage: boolean; // If the user can be messaged. - requirescontact: boolean; // If the user requires to be contacts. - contactrequests?: { // The contact requests. - id: number; // The id of the contact request. - userid: number; // The id of the user who created the contact request. - requesteduserid: number; // The id of the user confirming the request. - timecreated: number; // The timecreated timestamp for the contact request. - }[]; - conversations?: { // Conversations between users. - id: number; // Conversations id. - type: number; // Conversation type: private or public. - name: string; // Multilang compatible conversation name2. - timecreated: number; // The timecreated timestamp for the conversation. - }[]; -}; - -/** - * Conversation message. - */ -export type AddonMessagesConversationMessage = { - id: number; // The id of the message. - useridfrom: number; // The id of the user who sent the message. - text: string; // The text of the message. - timecreated: number; // The timecreated timestamp for the message. -}; - -/** - * Message preferences. - */ -export type AddonMessagesMessagePreferences = { - userid: number; // User id. - disableall: number; // Whether all the preferences are disabled. - processors: { // Config form values. - displayname: string; // Display name. - name: string; // Processor name. - hassettings: boolean; // Whether has settings. - contextid: number; // Context id. - userconfigured: number; // Whether is configured by the user. - }[]; - components: { // Available components. - displayname: string; // Display name. - notifications: AddonMessagesMessagePreferencesNotification[]; // List of notificaitons for the component. - }[]; -} & AddonMessagesMessagePreferencesCalculatedData; - -/** - * Notification processor in message preferences. - */ -export type AddonMessagesMessagePreferencesNotification = { - displayname: string; // Display name. - preferencekey: string; // Preference key. - processors: AddonMessagesMessagePreferencesNotificationProcessor[]; // Processors values for this notification. -}; - -/** - * Notification processor in message preferences. - */ -export type AddonMessagesMessagePreferencesNotificationProcessor = { - displayname: string; // Display name. - name: string; // Processor name. - locked: boolean; // Is locked by admin?. - lockedmessage?: string; // @since 3.6. Text to display if locked. - userconfigured: number; // Is configured?. - loggedin: { - name: string; // Name. - displayname: string; // Display name. - checked: boolean; // Is checked?. - }; - loggedoff: { - name: string; // Name. - displayname: string; // Display name. - checked: boolean; // Is checked?. - }; -}; - -/** - * Message discussion (before 3.6). - */ -export type AddonMessagesDiscussion = { - fullname: string; // Full name of the other user in the discussion. - profileimageurl: string; // Profile image of the other user in the discussion. - message?: { // Last message. - id: number; // Message ID. - user: number; // User ID that sent the message. - message: string; // Text of the message. - timecreated: number; // Time the message was sent. - pending?: boolean; // Whether the message is pending to be sent. - }; - unread?: boolean; // Whether the discussion has unread messages. -}; - -/** - * Contact for message area. - */ -export type AddonMessagesMessageAreaContact = { - userid: number; // The user's id. - fullname: string; // The user's name. - profileimageurl: string; // User picture URL. - profileimageurlsmall: string; // Small user picture URL. - ismessaging: boolean; // If we are messaging the user. - sentfromcurrentuser: boolean; // Was the last message sent from the current user?. - lastmessage: string; // The user's last message. - lastmessagedate: number; // @since 3.6. Timestamp for last message. - messageid: number; // The unique search message id. - showonlinestatus: boolean; // Show the user's online status?. - isonline: boolean; // The user's online status. - isread: boolean; // If the user has read the message. - isblocked: boolean; // If the user has been blocked. - unreadcount: number; // The number of unread messages in this conversation. - conversationid: number; // @since 3.6. The id of the conversation. -} & AddonMessagesMessageAreaContactCalculatedData; - -/** - * Result of WS core_message_get_blocked_users. - */ -export type AddonMessagesGetBlockedUsersResult = { - users: AddonMessagesBlockedUser[]; // List of blocked users. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * User data returned by core_message_get_blocked_users. - */ -export type AddonMessagesBlockedUser = { - id: number; // User ID. - fullname: string; // User full name. - profileimageurl?: string; // User picture URL. -}; - -/** - * Result of WS core_message_get_contacts. - */ -export type AddonMessagesGetContactsResult = { - online: AddonMessagesGetContactsContact[]; // List of online contacts. - offline: AddonMessagesGetContactsContact[]; // List of offline contacts. - strangers: AddonMessagesGetContactsContact[]; // List of users that are not in the user's contact list but have sent a message. -} & AddonMessagesGetContactsCalculatedData; - -/** - * User data returned by core_message_get_contacts. - */ -export type AddonMessagesGetContactsContact = { - id: number; // User ID. - fullname: string; // User full name. - profileimageurl?: string; // User picture URL. - profileimageurlsmall?: string; // Small user picture URL. - unread: number; // Unread message count. -}; - -/** - * User data returned by core_message_search_contacts. - */ -export type AddonMessagesSearchContactsContact = { - id: number; // User ID. - fullname: string; // User full name. - profileimageurl?: string; // User picture URL. - profileimageurlsmall?: string; // Small user picture URL. -}; - -/** - * Result of WS core_message_get_conversation_messages. - */ -export type AddonMessagesGetConversationMessagesResult = { - id: number; // The conversation id. - members: AddonMessagesConversationMember[]; - messages: AddonMessagesConversationMessage[]; -} & AddonMessagesGetConversationMessagesCalculatedData; - -/** - * Result of WS core_message_get_conversations. - */ -export type AddonMessagesGetConversationsResult = { - conversations: AddonMessagesConversation[]; -}; - -/** - * Result of WS core_message_get_conversation_counts. - */ -export type AddonMessagesGetConversationCountsResult = { - favourites: number; // Total number of favourite conversations. - types: { - 1: number; // Total number of individual conversations. - 2: number; // Total number of group conversations. - 3: number; // @since 3.7. Total number of self conversations. - }; -}; - -/** - * Result of WS core_message_get_unread_conversation_counts. - */ -export type AddonMessagesGetUnreadConversationCountsResult = { - favourites: number; // Total number of unread favourite conversations. - types: { - 1: number; // Total number of unread individual conversations. - 2: number; // Total number of unread group conversations. - 3: number; // @since 3.7. Total number of unread self conversations. - }; -}; - -/** - * Result of WS core_message_get_user_message_preferences. - */ -export type AddonMessagesGetUserMessagePreferencesResult = { - preferences: AddonMessagesMessagePreferences; - blocknoncontacts: number; // Privacy messaging setting to define who can message you. - entertosend: boolean; // @since 3.6. User preference for using enter to send messages. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS core_message_get_messages. - */ -export type AddonMessagesGetMessagesResult = { - messages: AddonMessagesGetMessagesMessage[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Message data returned by core_message_get_messages. - */ -export type AddonMessagesGetMessagesMessage = { - id: number; // Message id. - useridfrom: number; // User from id. - useridto: number; // User to id. - subject: string; // The message subject. - text: string; // The message text formated. - fullmessage: string; // The message. - fullmessageformat: number; // Fullmessage format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - fullmessagehtml: string; // The message in html. - smallmessage: string; // The shorten message. - notification: number; // Is a notification?. - contexturl: string; // Context URL. - contexturlname: string; // Context URL link name. - timecreated: number; // Time created. - timeread: number; // Time read. - usertofullname: string; // User to full name. - userfromfullname: string; // User from full name. - component?: string; // @since 3.7. The component that generated the notification. - eventtype?: string; // @since 3.7. The type of notification. - customdata?: string; // @since 3.7. Custom data to be passed to the message processor. -} & AddonMessagesGetMessagesMessageCalculatedData; - -/** - * Result of WS core_message_data_for_messagearea_search_messages. - */ -export type AddonMessagesDataForMessageAreaSearchMessagesResult = { - contacts: AddonMessagesMessageAreaContact[]; -}; - -/** - * Result of WS core_message_message_search_users. - */ -export type AddonMessagesSearchUsersResult = { - contacts: AddonMessagesConversationMember[]; - noncontacts: AddonMessagesConversationMember[]; -}; - -/** - * Result of WS core_message_mark_message_read. - */ -export type AddonMessagesMarkMessageReadResult = { - messageid: number; // The id of the message in the messages table. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS core_message_send_instant_messages. - */ -export type AddonMessagesSendInstantMessagesMessage = { - msgid: number; // Test this to know if it succeeds: id of the created message if it succeeded, -1 when failed. - clientmsgid?: string; // Your own id for the message. - errormessage?: string; // Error message - if it failed. - text?: string; // @since 3.6. The text of the message. - timecreated?: number; // @since 3.6. The timecreated timestamp for the message. - conversationid?: number; // @since 3.6. The conversation id for this message. - useridfrom?: number; // @since 3.6. The user id who sent the message. - candeletemessagesforallusers: boolean; // @since 3.7. If the user can delete messages in the conversation for all users. -}; - -/** - * Result of WS core_message_send_messages_to_conversation. - */ -export type AddonMessagesSendMessagesToConversationMessage = { - id: number; // The id of the message. - useridfrom: number; // The id of the user who sent the message. - text: string; // The text of the message. - timecreated: number; // The timecreated timestamp for the message. -}; - -/** - * Calculated data for core_message_get_contacts. - */ -export type AddonMessagesGetContactsCalculatedData = { - blocked?: AddonMessagesBlockedUser[]; // Calculated in the app. List of blocked users. -}; - -/** - * Calculated data for core_message_get_conversation_messages. - */ -export type AddonMessagesGetConversationMessagesCalculatedData = { - canLoadMore?: boolean; // Calculated in the app. Whether more messages can be loaded. -}; - -/** - * Calculated data for message preferences. - */ -export type AddonMessagesMessagePreferencesCalculatedData = { - blocknoncontacts?: number; // Calculated in the app. Based on the result of core_message_get_user_message_preferences. -}; - -/** - * Calculated data for messages returned by core_message_get_messages. - */ -export type AddonMessagesGetMessagesMessageCalculatedData = { - pending?: boolean; // Calculated in the app. Whether the message is pending to be sent. - read?: number; // Calculated in the app. Whether the message has been read. -}; - -/** - * Calculated data for contact for message area. - */ -export type AddonMessagesMessageAreaContactCalculatedData = { - id?: number; // Calculated in the app. User ID. -}; diff --git a/src/addon/messages/providers/push-click-handler.ts b/src/addon/messages/providers/push-click-handler.ts deleted file mode 100644 index 56e686a41..000000000 --- a/src/addon/messages/providers/push-click-handler.ts +++ /dev/null @@ -1,77 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreLoginHelperProvider } from '@core/login/providers/helper'; -import { AddonMessagesProvider } from './messages'; - -/** - * Handler for messaging push notifications clicks. - */ -@Injectable() -export class AddonMessagesPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonMessagesPushClickHandler'; - priority = 200; - featureName = 'CoreMainMenuDelegate_AddonMessages'; - - constructor(private utils: CoreUtilsProvider, private messagesProvider: AddonMessagesProvider, - private loginHelper: CoreLoginHelperProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - if (this.utils.isTrueOrOne(notification.notif) && notification.name != 'messagecontactrequests') { - return false; - } - - // Check that messaging is enabled. - return this.messagesProvider.isPluginEnabled(notification.site); - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - handleClick(notification: any): Promise { - return this.messagesProvider.invalidateDiscussionsCache(notification.site).catch(() => { - // Ignore errors. - }).then(() => { - // Check if group messaging is enabled, to determine which page should be loaded. - return this.messagesProvider.isGroupMessagingEnabledInSite(notification.site).then((enabled) => { - const pageParams: any = {}; - let pageName = 'AddonMessagesIndexPage'; - if (enabled) { - pageName = 'AddonMessagesGroupConversationsPage'; - } - - // Check if we have enough information to open the conversation. - if (notification.convid && enabled) { - pageParams.conversationId = Number(notification.convid); - } else if (notification.userfromid) { - pageParams.discussionUserId = Number(notification.userfromid); - } - - return this.loginHelper.redirect(pageName, pageParams, notification.site); - }); - }); - } -} diff --git a/src/addon/messages/providers/settings-handler.ts b/src/addon/messages/providers/settings-handler.ts deleted file mode 100644 index cbee96b44..000000000 --- a/src/addon/messages/providers/settings-handler.ts +++ /dev/null @@ -1,55 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonMessagesProvider } from './messages'; -import { CoreSettingsHandler, CoreSettingsHandlerData } from '@core/settings/providers/delegate'; - -/** - * Message settings handler. - */ -@Injectable() -export class AddonMessagesSettingsHandler implements CoreSettingsHandler { - name = 'AddonMessages'; - priority = 600; - - constructor(private messagesProvider: AddonMessagesProvider) { - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - async isEnabled(): Promise { - const messagingEnabled = await this.messagesProvider.isPluginEnabled(); - - return messagingEnabled && this.messagesProvider.isMessagePreferencesEnabled(); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreSettingsHandlerData { - return { - icon: 'chatbubbles', - title: 'addon.messages.messages', - page: 'AddonMessagesSettingsPage', - class: 'addon-messages-settings-handler', - }; - } - -} diff --git a/src/addon/messages/providers/sync-cron-handler.ts b/src/addon/messages/providers/sync-cron-handler.ts deleted file mode 100644 index 6fbafe9dc..000000000 --- a/src/addon/messages/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonMessagesSyncProvider } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonMessagesSyncCronHandler implements CoreCronHandler { - name = 'AddonMessagesSyncCronHandler'; - - constructor(private messagesSync: AddonMessagesSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.messagesSync.syncAllDiscussions(siteId); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return 300000; // 5 minutes. - } -} diff --git a/src/addon/messages/providers/sync.ts b/src/addon/messages/providers/sync.ts deleted file mode 100644 index fe7e83e71..000000000 --- a/src/addon/messages/providers/sync.ts +++ /dev/null @@ -1,356 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncBaseProvider } from '@classes/base-sync'; -import { CoreAppProvider } from '@providers/app'; -import { AddonMessagesOfflineProvider } from './messages-offline'; -import { AddonMessagesProvider, AddonMessagesConversationFormatted } from './messages'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreConstants } from '@core/constants'; - -/** - * Service to sync messages. - */ -@Injectable() -export class AddonMessagesSyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_messages_autom_synced'; - - constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, - translate: TranslateService, syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, - private messagesOffline: AddonMessagesOfflineProvider, private eventsProvider: CoreEventsProvider, - private messagesProvider: AddonMessagesProvider, private userProvider: CoreUserProvider, - private utils: CoreUtilsProvider, timeUtils: CoreTimeUtilsProvider) { - super('AddonMessagesSync', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils); - } - - /** - * Get the ID of a discussion sync. - * - * @param conversationId Conversation ID. - * @param userId User ID talking to (if no conversation ID). - * @return Sync ID. - */ - protected getSyncId(conversationId: number, userId: number): string { - if (conversationId) { - return 'conversationid:' + conversationId; - } else { - return 'userid:' + userId; - } - } - - /** - * Try to synchronize all the discussions in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param onlyDeviceOffline True to only sync discussions that failed because device was offline, - * false to sync all. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllDiscussions(siteId?: string, onlyDeviceOffline: boolean = false): Promise { - const syncFunctionLog = 'all discussions' + (onlyDeviceOffline ? ' (Only offline)' : ''); - - return this.syncOnSites(syncFunctionLog, this.syncAllDiscussionsFunc.bind(this), [onlyDeviceOffline], siteId); - } - - /** - * Get all messages pending to be sent in the site. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param onlyDeviceOffline True to only sync discussions that failed because device was offline. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllDiscussionsFunc(siteId?: string, onlyDeviceOffline: boolean = false): Promise { - const promise = onlyDeviceOffline ? - this.messagesOffline.getAllDeviceOfflineMessages(siteId) : - this.messagesOffline.getAllMessages(siteId); - - return promise.then((messages) => { - const userIds = [], - conversationIds = [], - promises = []; - - // Get all the conversations to be synced. - messages.forEach((message) => { - if (message.conversationid) { - if (conversationIds.indexOf(message.conversationid) == -1) { - conversationIds.push(message.conversationid); - } - } else if (userIds.indexOf(message.touserid) == -1) { - userIds.push(message.touserid); - } - }); - - // Sync all conversations. - conversationIds.forEach((conversationId) => { - promises.push(this.syncDiscussion(conversationId, undefined, siteId).then((warnings) => { - if (typeof warnings != 'undefined') { - // Sync successful, send event. - this.eventsProvider.trigger(AddonMessagesSyncProvider.AUTO_SYNCED, { - conversationId: conversationId, - warnings: warnings - }, siteId); - } - })); - }); - - userIds.forEach((userId) => { - promises.push(this.syncDiscussion(undefined, userId, siteId).then((warnings) => { - if (typeof warnings != 'undefined') { - // Sync successful, send event. - this.eventsProvider.trigger(AddonMessagesSyncProvider.AUTO_SYNCED, { - userId: userId, - warnings: warnings - }, siteId); - } - })); - }); - - return Promise.all(promises); - }); - } - - /** - * Synchronize a discussion. - * - * @param conversationId Conversation ID. - * @param userId User ID talking to (if no conversation ID). - * @param siteId Site ID. - * @return Promise resolved with the list of warnings if sync is successful, rejected otherwise. - */ - syncDiscussion(conversationId: number, userId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = this.getSyncId(conversationId, userId); - - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this conversation, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - return this.addOngoingSync(syncId, this.performSyncDiscussion(conversationId, userId, siteId), siteId); - } - - /** - * Perform the synchronization of a discussion. - * - * @param conversationId Conversation ID. - * @param userId User ID talking to (if no conversation ID). - * @param siteId Site ID. - * @return Promise resolved with the list of warnings if sync is successful, rejected otherwise. - */ - protected async performSyncDiscussion(conversationId: number, userId: number, siteId: string): Promise { - const groupMessagingEnabled = this.messagesProvider.isGroupMessagingEnabled(); - let messages: any[]; - const errors = []; - const warnings: string[] = []; - - if (conversationId) { - this.logger.debug(`Try to sync conversation '${conversationId}'`); - messages = await this.messagesOffline.getConversationMessages(conversationId, siteId); - } else { - this.logger.debug(`Try to sync discussion with user '${userId}'`); - messages = await this.messagesOffline.getMessages(userId, siteId); - } - - if (!messages.length) { - // Nothing to sync. - return []; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. Mark messages as device offline. - this.messagesOffline.setMessagesDeviceOffline(messages, true); - - return Promise.reject(null); - } - - // Order message by timecreated. - messages = this.messagesProvider.sortMessages(messages); - - // Get messages sent by the user after the first offline message was sent. - // We subtract some time because the message could've been saved in server before it was in the app. - const timeFrom = Math.floor((messages[0].timecreated - CoreConstants.WS_TIMEOUT - 1000) / 1000); - const onlineMessages = await this.getMessagesSentAfter(timeFrom, conversationId, userId, siteId); - - // Send the messages. Send them 1 by 1 to simulate web's behaviour and to make sure we know which message has failed. - for (let i = 0; i < messages.length; i++) { - const message = messages[i]; - const textFieldName = conversationId ? 'text' : 'smallmessage'; - const wrappedText = message[textFieldName][0] != '<' ? '

' + message[textFieldName] + '

' : message[textFieldName]; - - try { - if (onlineMessages.indexOf(wrappedText) != -1) { - // Message already sent, ignore it to prevent duplicates. - } else if (conversationId) { - await this.messagesProvider.sendMessageToConversationOnline(conversationId, message.text, siteId); - } else { - await this.messagesProvider.sendMessageOnline(userId, message.smallmessage, siteId); - } - } catch (error) { - if (!this.utils.isWebServiceError(error)) { - // Error sending, stop execution. - if (this.appProvider.isOnline()) { - // App is online, unmark deviceoffline if marked. - this.messagesOffline.setMessagesDeviceOffline(messages, false); - } - - throw error; - } - - // Error returned by WS. Store the error to show a warning but keep sending messages. - if (errors.indexOf(error) == -1) { - errors.push(error); - } - } - - // Message was sent, delete it from local DB. - if (conversationId) { - await this.messagesOffline.deleteConversationMessage(conversationId, message.text, message.timecreated, siteId); - } else { - await this.messagesOffline.deleteMessage(userId, message.smallmessage, message.timecreated, siteId); - } - - // In some Moodle versions, wait 1 second to make sure timecreated is different. - // This is because there was a bug where messages with the same timecreated had a wrong order. - if (!groupMessagingEnabled && i < messages.length - 1) { - await this.utils.wait(1000); - } - } - - await this.handleSyncErrors(conversationId, userId, errors, warnings); - - // All done, return the warnings. - return warnings; - } - - /** - * Get messages sent by current user after a certain time. - * - * @param time Time in seconds. - * @param conversationId Conversation ID. - * @param userId User ID talking to (if no conversation ID). - * @param siteId Site ID. - * @return Promise resolved with the messages texts. - */ - protected async getMessagesSentAfter(time: number, conversationId: number, userId: number, siteId: string): Promise { - const site = await this.sitesProvider.getSite(siteId); - - const siteCurrentUserId = site.getUserId(); - - if (conversationId) { - try { - const result = await this.messagesProvider.getConversationMessages(conversationId, { - excludePending: true, - ignoreCache: true, - timeFrom: time, - }); - - const sentMessages = result.messages.filter((message) => message.useridfrom == siteCurrentUserId); - - return sentMessages.map((message) => message.text); - } catch (error) { - if (error && error.errorcode == 'invalidresponse') { - // There's a bug in Moodle that causes this error if there are no new messages. Return empty array. - return []; - } - - throw error; - } - } else { - - const params = { - useridto: userId, - useridfrom: siteCurrentUserId, - limitnum: AddonMessagesProvider.LIMIT_MESSAGES, - }; - const preSets = { - cacheKey: this.messagesProvider.getCacheKeyForDiscussion(userId), - ignoreCache: true, - }; - - const messages = await this.messagesProvider.getRecentMessages(params, preSets, 0, 0, false, siteId); - - time = time * 1000; // Convert to milliseconds. - const messagesAfterTime = messages.filter((message) => message.timecreated >= time); - - return messagesAfterTime.map((message) => message.text); - } - } - - /** - * Handle sync errors. - * - * @param conversationId Conversation ID. - * @param userId User ID talking to (if no conversation ID). - * @param errors List of errors. - * @param warnings Array where to place the warnings. - * @return Promise resolved when done. - */ - protected handleSyncErrors(conversationId: number, userId: number, errors: any[], warnings: string[]): Promise { - if (errors && errors.length) { - if (conversationId) { - - // Get conversation name and add errors to warnings array. - return this.messagesProvider.getConversation(conversationId, false, false).catch(() => { - // Ignore errors. - return {}; - }).then((conversation) => { - errors.forEach((error) => { - warnings.push(this.translate.instant('addon.messages.warningconversationmessagenotsent', { - conversation: conversation.name ? conversation.name : conversationId, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - }); - } else { - - // Get user full name and add errors to warnings array. - return this.userProvider.getProfile(userId, undefined, true).catch(() => { - // Ignore errors. - return {}; - }).then((user) => { - errors.forEach((error) => { - warnings.push(this.translate.instant('addon.messages.warningmessagenotsent', { - user: user.fullname ? user.fullname : userId, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - }); - } - } - } - - /** - * If there's an ongoing sync for a certain conversation, wait for it to end. - * If there's no sync ongoing the promise will be resolved right away. - * - * @param conversationId Conversation ID. - * @param userId User ID talking to (if no conversation ID). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when there's no sync going on for the identifier. - */ - waitForSyncConversation(conversationId: number, userId: number, siteId?: string): Promise { - const syncId = this.getSyncId(conversationId, userId); - - return this.waitForSync(syncId, siteId); - } -} diff --git a/src/addon/messages/providers/user-add-contact-handler.ts b/src/addon/messages/providers/user-add-contact-handler.ts deleted file mode 100644 index 7c885960e..000000000 --- a/src/addon/messages/providers/user-add-contact-handler.ts +++ /dev/null @@ -1,206 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, OnDestroy } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonMessagesProvider } from './messages'; -import { AddonMessagesBlockContactUserHandler } from './user-block-contact-handler'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Profile add/remove contact handler. - */ -@Injectable() -export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandler, OnDestroy { - /** - * Update handler information event. - */ - static UPDATED_EVENT = 'AddonMessagesAddContactUserHandler_updated_event'; - - name = 'AddonMessages:addContact'; - priority = 800; - type = CoreUserDelegate.TYPE_ACTION; - - protected disabled = false; - protected updateObs: any; - - constructor(protected sitesProvider: CoreSitesProvider, - private messagesProvider: AddonMessagesProvider, protected eventsProvider: CoreEventsProvider, - private domUtils: CoreDomUtilsProvider, private translate: TranslateService) { - - this.updateObs = eventsProvider.on(AddonMessagesBlockContactUserHandler.UPDATED_EVENT, (data) => { - this.checkButton(data.userId); - }); - } - - /** - * Check if handler is enabled. - * - * @return Promise resolved with true if enabled, rejected or resolved with false otherwise. - */ - isEnabled(): Promise { - return this.messagesProvider.isPluginEnabled(); - } - - /** - * Check if handler is enabled for this user in this context. - * - * @param user User to check. - * @param courseId Course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved with true if enabled, resolved with false otherwise. - */ - isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { - return user.id != this.sitesProvider.getCurrentSiteUserId(); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { - this.checkButton(user.id); - - return { - icon: '', - title: '', - spinner: false, - class: '', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - - if (this.disabled) { - return; - } - this.disabled = true; - this.updateButton(user.id, {spinner: true}); - - this.messagesProvider.isContact(user.id).then((isContact) => { - if (isContact) { - const message = this.translate.instant('addon.messages.removecontactconfirm', {$a: user.fullname}); - const okText = this.translate.instant('core.remove'); - - return this.domUtils.showConfirm(message, undefined, okText).then(() => { - return this.messagesProvider.removeContact(user.id); - }); - } else { - return this.addContact(user); - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }).finally(() => { - this.eventsProvider.trigger(AddonMessagesAddContactUserHandler.UPDATED_EVENT, {userId: user.id}); - this.checkButton(user.id).finally(() => { - this.disabled = false; - }); - }); - - } - }; - } - - /** - * Update Button with avalaible data. - * @param userId User Id to update. - * @return Promise resolved when done. - */ - protected checkButton(userId: number): Promise { - this.updateButton(userId, {spinner: true}); - - const groupMessagingEnabled = this.messagesProvider.isGroupMessagingEnabled(); - - return this.messagesProvider.isContact(userId).then((isContact) => { - if (isContact) { - this.updateButton(userId, { - title: groupMessagingEnabled ? 'addon.messages.removefromyourcontacts' : 'addon.messages.removecontact', - class: 'addon-messages-removecontact-handler', - icon: 'remove', - hidden: false, - spinner: false - }); - } else { - this.updateButton(userId, { - title: groupMessagingEnabled ? 'addon.messages.addtoyourcontacts' : 'addon.messages.addcontact', - class: 'addon-messages-addcontact-handler', - icon: 'add', - hidden: false, - spinner: false - }); - } - }).catch(() => { - // This fails for some reason, let's just hide the button. - this.updateButton(userId, {hidden: true}); - }); - } - - /** - * Triggers the event to update the handler information. - * - * @param userId The user ID the handler belongs to. - * @param data Data that should be updated. - */ - protected updateButton(userId: number, data: any): void { - // This fails for some reason, let's just hide the button. - this.eventsProvider.trigger(CoreUserDelegate.UPDATE_HANDLER_EVENT, { handler: this.name, data: data, userId: userId }); - } - - /** - * Add a contact or send a contact request if group messaging is enabled. - * - * @param user User to add as contact. - * @return Promise resolved when done. - */ - protected addContact(user: any): Promise { - if (!this.messagesProvider.isGroupMessagingEnabled()) { - return this.messagesProvider.addContact(user.id); - } - - return this.messagesProvider.getMemberInfo(user.id).then((member) => { - const currentUserId = this.sitesProvider.getCurrentSiteUserId(); - const requestSent = member.contactrequests.some((request) => { - return request.userid == currentUserId && request.requesteduserid == user.id; - }); - - if (requestSent) { - const message = this.translate.instant('addon.messages.yourcontactrequestpending', {$a: user.fullname}); - - return this.domUtils.showAlert(null, message); - } - - const message = this.translate.instant('addon.messages.addcontactconfirm', {$a: user.fullname}); - const okText = this.translate.instant('core.add'); - - return this.domUtils.showConfirm(message, undefined, okText).then(() => { - return this.messagesProvider.createContactRequest(user.id); - }).then(() => { - const message = this.translate.instant('addon.messages.contactrequestsent'); - - return this.domUtils.showAlert(null, message); - }); - }); - } - - /** - * Destroyed method. - */ - ngOnDestroy(): void { - this.updateObs && this.updateObs.off(); - } -} diff --git a/src/addon/messages/providers/user-block-contact-handler.ts b/src/addon/messages/providers/user-block-contact-handler.ts deleted file mode 100644 index 55912ac99..000000000 --- a/src/addon/messages/providers/user-block-contact-handler.ts +++ /dev/null @@ -1,174 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, OnDestroy } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonMessagesProvider } from './messages'; -import { AddonMessagesAddContactUserHandler } from './user-add-contact-handler'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Profile block/unblock contact handler. - */ -@Injectable() -export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHandler, OnDestroy { - /** - * Update handler information event. - */ - static UPDATED_EVENT = 'AddonMessagesBlockContactUserHandler_updated_event'; - - name = 'AddonMessages:blockContact'; - priority = 600; - type = CoreUserDelegate.TYPE_ACTION; - - protected disabled = false; - protected updateObs: any; - - constructor(protected sitesProvider: CoreSitesProvider, private messagesProvider: AddonMessagesProvider, - protected eventsProvider: CoreEventsProvider, private domUtils: CoreDomUtilsProvider, - private translate: TranslateService) { - - this.updateObs = eventsProvider.on(AddonMessagesAddContactUserHandler.UPDATED_EVENT, (data) => { - this.checkButton(data.userId); - }); - } - - /** - * Check if handler is enabled. - * - * @return Promise resolved with true if enabled, rejected or resolved with false otherwise. - */ - isEnabled(): Promise { - return this.messagesProvider.isPluginEnabled(); - } - - /** - * Check if handler is enabled for this user in this context. - * - * @param user User to check. - * @param courseId Course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved with true if enabled, resolved with false otherwise. - */ - isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { - return user.id != this.sitesProvider.getCurrentSiteUserId(); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { - - this.checkButton(user.id); - - return { - icon: '', - title: '', - spinner: false, - class: '', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - - if (this.disabled) { - return; - } - this.disabled = true; - this.updateButton(user.id, {spinner: true}); - - this.messagesProvider.isBlocked(user.id).then((isBlocked) => { - if (isBlocked) { - const template = this.translate.instant('addon.messages.unblockuserconfirm', {$a: user.fullname}); - const okText = this.translate.instant('addon.messages.unblockuser'); - - return this.domUtils.showConfirm(template, undefined, okText).then(() => { - return this.messagesProvider.unblockContact(user.id); - }); - } else { - const template = this.translate.instant('addon.messages.blockuserconfirm', {$a: user.fullname}); - const okText = this.translate.instant('addon.messages.blockuser'); - - return this.domUtils.showConfirm(template, undefined, okText).then(() => { - return this.messagesProvider.blockContact(user.id); - }); - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }).finally(() => { - this.eventsProvider.trigger(AddonMessagesBlockContactUserHandler.UPDATED_EVENT, {userId: user.id}); - this.checkButton(user.id).finally(() => { - this.disabled = false; - }); - }); - - } - }; - } - - /** - * Update Button with avalaible data. - * @param userId User Id to update. - * @return Promise resolved when done. - */ - protected checkButton(userId: number): Promise { - this.updateButton(userId, {spinner: true}); - - return this.messagesProvider.isBlocked(userId).then((isBlocked) => { - if (isBlocked) { - this.updateButton(userId, { - title: 'addon.messages.unblockuser', - class: 'addon-messages-unblockcontact-handler', - icon: 'checkmark-circle', - hidden: false, - spinner: false - }); - } else { - this.updateButton(userId, { - title: 'addon.messages.blockuser', - class: 'addon-messages-blockcontact-handler', - icon: 'close-circle', - hidden: false, - spinner: false - }); - } - }).catch(() => { - // This fails for some reason, let's just hide the button. - this.updateButton(userId, {hidden: true}); - }); - } - - /** - * Triggers the event to update the handler information. - * - * @param userId The user ID the handler belongs to. - * @param data Data that should be updated. - */ - protected updateButton(userId: number, data: any): void { - // This fails for some reason, let's just hide the button. - this.eventsProvider.trigger(CoreUserDelegate.UPDATE_HANDLER_EVENT, { handler: this.name, data: data, userId: userId }); - } - - /** - * Destroyed method. - */ - ngOnDestroy(): void { - this.updateObs && this.updateObs.off(); - } -} diff --git a/src/addon/messages/providers/user-send-message-handler.ts b/src/addon/messages/providers/user-send-message-handler.ts deleted file mode 100644 index 1824a7d71..000000000 --- a/src/addon/messages/providers/user-send-message-handler.ts +++ /dev/null @@ -1,83 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonMessagesProvider } from './messages'; - -/** - * Profile send message handler. - */ -@Injectable() -export class AddonMessagesSendMessageUserHandler implements CoreUserProfileHandler { - name = 'AddonMessages:sendMessage'; - priority = 1000; - type = CoreUserDelegate.TYPE_COMMUNICATION; - - constructor(private linkHelper: CoreContentLinksHelperProvider, protected sitesProvider: CoreSitesProvider, - private messagesProvider: AddonMessagesProvider) { } - - /** - * Check if handler is enabled. - * - * @return Promise resolved with true if enabled, rejected or resolved with false otherwise. - */ - isEnabled(): Promise { - return this.messagesProvider.isPluginEnabled(); - } - - /** - * Check if handler is enabled for this user in this context. - * - * @param user User to check. - * @param courseId Course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved with true if enabled, resolved with false otherwise. - */ - isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { - const currentSite = this.sitesProvider.getCurrentSite(); - - if (!currentSite) { - return false; - } - - // From 3.7 you can send messages to yourself. - return user.id != currentSite.getUserId() || currentSite.isVersionGreaterEqualThan('3.7'); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { - return { - icon: 'send', - title: 'addon.messages.message', - class: 'addon-messages-send-message-handler', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - const pageParams = { - showKeyboard: true, - userId: user.id - }; - this.linkHelper.goInSite(navCtrl, 'AddonMessagesDiscussionPage', pageParams); - } - }; - } -} diff --git a/src/addon/mod/assign/assign.module.ts b/src/addon/mod/assign/assign.module.ts deleted file mode 100644 index fcea1f157..000000000 --- a/src/addon/mod/assign/assign.module.ts +++ /dev/null @@ -1,87 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; -import { AddonModAssignProvider } from './providers/assign'; -import { AddonModAssignOfflineProvider } from './providers/assign-offline'; -import { AddonModAssignSyncProvider } from './providers/assign-sync'; -import { AddonModAssignHelperProvider } from './providers/helper'; -import { AddonModAssignFeedbackDelegate } from './providers/feedback-delegate'; -import { AddonModAssignSubmissionDelegate } from './providers/submission-delegate'; -import { AddonModAssignDefaultFeedbackHandler } from './providers/default-feedback-handler'; -import { AddonModAssignDefaultSubmissionHandler } from './providers/default-submission-handler'; -import { AddonModAssignModuleHandler } from './providers/module-handler'; -import { AddonModAssignPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModAssignSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModAssignIndexLinkHandler } from './providers/index-link-handler'; -import { AddonModAssignListLinkHandler } from './providers/list-link-handler'; -import { AddonModAssignPushClickHandler } from './providers/push-click-handler'; -import { AddonModAssignSubmissionModule } from './submission/submission.module'; -import { AddonModAssignFeedbackModule } from './feedback/feedback.module'; - -// List of providers (without handlers). -export const ADDON_MOD_ASSIGN_PROVIDERS: any[] = [ - AddonModAssignProvider, - AddonModAssignOfflineProvider, - AddonModAssignSyncProvider, - AddonModAssignHelperProvider, - AddonModAssignFeedbackDelegate, - AddonModAssignSubmissionDelegate -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModAssignSubmissionModule, - AddonModAssignFeedbackModule - ], - providers: [ - AddonModAssignProvider, - AddonModAssignOfflineProvider, - AddonModAssignSyncProvider, - AddonModAssignHelperProvider, - AddonModAssignFeedbackDelegate, - AddonModAssignSubmissionDelegate, - AddonModAssignDefaultFeedbackHandler, - AddonModAssignDefaultSubmissionHandler, - AddonModAssignModuleHandler, - AddonModAssignPrefetchHandler, - AddonModAssignSyncCronHandler, - AddonModAssignIndexLinkHandler, - AddonModAssignListLinkHandler, - AddonModAssignPushClickHandler - ] -}) -export class AddonModAssignModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModAssignModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModAssignPrefetchHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModAssignSyncCronHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModAssignIndexLinkHandler, - listLinkHandler: AddonModAssignListLinkHandler, pushNotificationsDelegate: CorePushNotificationsDelegate, - pushClickHandler: AddonModAssignPushClickHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - cronDelegate.register(syncHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - } -} diff --git a/src/addon/mod/assign/classes/base-feedback-handler.ts b/src/addon/mod/assign/classes/base-feedback-handler.ts deleted file mode 100644 index 56591e989..000000000 --- a/src/addon/mod/assign/classes/base-feedback-handler.ts +++ /dev/null @@ -1,184 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModAssignFeedbackHandler } from '../providers/feedback-delegate'; -import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign'; - -/** - * Base handler for feedback plugins. - * - * This class is needed because parent classes cannot have @Injectable in Angular v6, so the default handler cannot be a - * parent class. - */ -export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedbackHandler { - name = 'AddonModAssignBaseFeedbackHandler'; - type = 'base'; - - constructor(protected translate: TranslateService) { } - - /** - * Discard the draft data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - discardDraft(assignId: number, userId: number, siteId?: string): void | Promise { - // Nothing to do. - } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: AddonModAssignPlugin): any | Promise { - // Nothing to do. - } - - /** - * Return the draft saved data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return Data (or promise resolved with the data). - */ - getDraft(assignId: number, userId: number, siteId?: string): any | Promise { - // Nothing to do. - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise { - return []; - } - - /** - * Get a readable name to use for the plugin. - * - * @param plugin The plugin object. - * @return The plugin name. - */ - getPluginName(plugin: AddonModAssignPlugin): string { - // Check if there's a translated string for the plugin. - const translationId = 'addon.mod_assign_feedback_' + plugin.type + '.pluginname', - translation = this.translate.instant(translationId); - - if (translationId != translation) { - // Translation found, use it. - return translation; - } - - // Fallback to WS string. - if (plugin.name) { - return plugin.name; - } - } - - /** - * Check if the feedback data has changed for this plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the feedback. - * @return Boolean (or promise resolved with boolean): whether the data has changed. - */ - hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, userId: number): boolean | Promise { - return false; - } - - /** - * Check whether the plugin has draft data stored. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return Boolean or promise resolved with boolean: whether the plugin has draft data. - */ - hasDraftData(assignId: number, userId: number, siteId?: string): boolean | Promise { - return false; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Prefetch any required data for the plugin. - * This should NOT prefetch files. Files to be prefetched should be returned by the getPluginFiles function. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetch(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): Promise { - return Promise.resolve(); - } - - /** - * Prepare and add to pluginData the data to send to the server based on the draft data saved. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareFeedbackData(assignId: number, userId: number, plugin: AddonModAssignPlugin, pluginData: any, - siteId?: string): void | Promise { - // Nothing to do. - } - - /** - * Save draft data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param data The data to save. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - saveDraft(assignId: number, userId: number, plugin: AddonModAssignPlugin, data: any, siteId?: string) - : void | Promise { - // Nothing to do. - } -} diff --git a/src/addon/mod/assign/classes/base-submission-handler.ts b/src/addon/mod/assign/classes/base-submission-handler.ts deleted file mode 100644 index 6d147d882..000000000 --- a/src/addon/mod/assign/classes/base-submission-handler.ts +++ /dev/null @@ -1,257 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModAssignSubmissionHandler } from '../providers/submission-delegate'; -import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign'; - -/** - * Base handler for submission plugins. - * - * This class is needed because parent classes cannot have @Injectable in Angular v6, so the default handler cannot be a - * parent class. - */ -export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmissionHandler { - name = 'AddonModAssignBaseSubmissionHandler'; - type = 'base'; - - constructor(protected translate: TranslateService) { } - - /** - * Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the - * plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit - * unfiltered data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @return Boolean or promise resolved with boolean: whether it can be edited in offline. - */ - canEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin): boolean | Promise { - return false; - } - - /** - * Check if a plugin has no data. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return Whether the plugin is empty. - */ - isEmpty(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): boolean { - return true; - } - - /** - * Should clear temporary data for a cancelled submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - */ - clearTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): void { - // Nothing to do. - } - - /** - * This function will be called when the user wants to create a new submission based on the previous one. - * It should add to pluginData the data to send to server based in the data in plugin (previous attempt). - * - * @param assign The assignment. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - copySubmissionData(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any, - userId?: number, siteId?: string): void | Promise { - // Nothing to do. - } - - /** - * Delete any stored data for the plugin and submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - deleteOfflineData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, siteId?: string): void | Promise { - // Nothing to do. - } - - /** - * Return the Component to use to display the plugin data, either in read or in edit mode. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @param edit Whether the user is editing. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise { - // Nothing to do. - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise { - return []; - } - - /** - * Get a readable name to use for the plugin. - * - * @param plugin The plugin object. - * @return The plugin name. - */ - getPluginName(plugin: AddonModAssignPlugin): string { - // Check if there's a translated string for the plugin. - const translationId = 'addon.mod_assign_submission_' + plugin.type + '.pluginname', - translation = this.translate.instant(translationId); - - if (translationId != translation) { - // Translation found, use it. - return translation; - } - - // Fallback to WS string. - if (plugin.name) { - return plugin.name; - } - } - - /** - * Get the size of data (in bytes) this plugin will send to copy a previous submission. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return The size (or promise resolved with size). - */ - getSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): number | Promise { - return 0; - } - - /** - * Get the size of data (in bytes) this plugin will send to add or edit a submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @return The size (or promise resolved with size). - */ - getSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): number | Promise { - return 0; - } - - /** - * Check if the submission data has changed for this plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return Boolean (or promise resolved with boolean): whether the data has changed. - */ - hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): boolean | Promise { - return false; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether or not the handler is enabled for edit on a site level. - * @return Whether or not the handler is enabled for edit on a site level. - */ - isEnabledForEdit(): boolean | Promise { - return false; - } - - /** - * Prefetch any required data for the plugin. - * This should NOT prefetch files. Files to be prefetched should be returned by the getPluginFiles function. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetch?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): Promise { - return Promise.resolve(); - } - - /** - * Prepare and add to pluginData the data to send to the server based on the input data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @param pluginData Object where to store the data to send. - * @param offline Whether the user is editing in offline. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean, - userId?: number, siteId?: string): void | Promise { - // Nothing to do. - } - - /** - * Prepare and add to pluginData the data to send to the server based on the offline data stored. - * This will be used when performing a synchronization. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise { - // Nothing to do. - } -} diff --git a/src/addon/mod/assign/classes/feedback-plugin-component.ts b/src/addon/mod/assign/classes/feedback-plugin-component.ts deleted file mode 100644 index 991219c07..000000000 --- a/src/addon/mod/assign/classes/feedback-plugin-component.ts +++ /dev/null @@ -1,71 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Input } from '@angular/core'; -import { ModalController } from 'ionic-angular'; -import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign'; - -/** - * Base class for component to render a feedback plugin. - */ -export class AddonModAssignFeedbackPluginComponentBase { - @Input() assign: AddonModAssignAssign; // The assignment. - @Input() submission: AddonModAssignSubmission; // The submission. - @Input() plugin: AddonModAssignPlugin; // The plugin object. - @Input() userId: number; // The user ID of the submission. - @Input() configs: any; // The configs for the plugin. - @Input() canEdit: boolean; // Whether the user can edit. - @Input() edit: boolean; // Whether the user is editing. - - constructor(protected modalCtrl: ModalController) { } - - /** - * Open a modal to edit the feedback plugin. - * - * @return Promise resolved with the input data, rejected if cancelled. - */ - editFeedback(): Promise { - if (this.canEdit) { - return new Promise((resolve, reject): void => { - // Create the navigation modal. - const modal = this.modalCtrl.create('AddonModAssignEditFeedbackModalPage', { - assign: this.assign, - submission: this.submission, - plugin: this.plugin, - userId: this.userId - }); - - modal.present(); - modal.onDidDismiss((data) => { - if (typeof data == 'undefined') { - reject(); - } else { - resolve(data); - } - }); - }); - } else { - return Promise.reject(null); - } - } - - /** - * Invalidate the data. - * - * @return Promise resolved when done. - */ - invalidate(): Promise { - return Promise.resolve(); - } -} diff --git a/src/addon/mod/assign/classes/submission-plugin-component.ts b/src/addon/mod/assign/classes/submission-plugin-component.ts deleted file mode 100644 index 3a466b675..000000000 --- a/src/addon/mod/assign/classes/submission-plugin-component.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Input } from '@angular/core'; -import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign'; - -/** - * Base class for component to render a submission plugin. - */ -export class AddonModAssignSubmissionPluginComponent { - @Input() assign: AddonModAssignAssign; // The assignment. - @Input() submission: AddonModAssignSubmission; // The submission. - @Input() plugin: AddonModAssignPlugin; // The plugin object. - @Input() configs: {[name: string]: string}; // The configs for the plugin. - @Input() edit: boolean; // Whether the user is editing. - @Input() allowOffline: boolean; // Whether to allow offline. - - constructor() { - // Nothing to do. - } - - /** - * Invalidate the data. - * - * @return Promise resolved when done. - */ - invalidate(): Promise { - return Promise.resolve(); - } -} diff --git a/src/addon/mod/assign/components/components.module.ts b/src/addon/mod/assign/components/components.module.ts deleted file mode 100644 index de3fbba1c..000000000 --- a/src/addon/mod/assign/components/components.module.ts +++ /dev/null @@ -1,56 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModAssignIndexComponent } from './index/index'; -import { AddonModAssignSubmissionComponent } from './submission/submission'; -import { AddonModAssignSubmissionPluginComponent } from './submission-plugin/submission-plugin'; -import { AddonModAssignFeedbackPluginComponent } from './feedback-plugin/feedback-plugin'; - -@NgModule({ - declarations: [ - AddonModAssignIndexComponent, - AddonModAssignSubmissionComponent, - AddonModAssignSubmissionPluginComponent, - AddonModAssignFeedbackPluginComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModAssignIndexComponent, - AddonModAssignSubmissionComponent, - AddonModAssignSubmissionPluginComponent, - AddonModAssignFeedbackPluginComponent - ], - entryComponents: [ - AddonModAssignIndexComponent - ] -}) -export class AddonModAssignComponentsModule {} diff --git a/src/addon/mod/assign/components/feedback-plugin/addon-mod-assign-feedback-plugin.html b/src/addon/mod/assign/components/feedback-plugin/addon-mod-assign-feedback-plugin.html deleted file mode 100644 index daa72e7d8..000000000 --- a/src/addon/mod/assign/components/feedback-plugin/addon-mod-assign-feedback-plugin.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - -

{{ plugin.name }}

- - {{ 'addon.mod_assign.feedbacknotsupported' | translate }} - -

- -

- -
-
-
diff --git a/src/addon/mod/assign/components/feedback-plugin/feedback-plugin.ts b/src/addon/mod/assign/components/feedback-plugin/feedback-plugin.ts deleted file mode 100644 index c08c5c96f..000000000 --- a/src/addon/mod/assign/components/feedback-plugin/feedback-plugin.ts +++ /dev/null @@ -1,106 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, Injector, ViewChild } from '@angular/core'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../providers/assign'; -import { AddonModAssignHelperProvider } from '../../providers/helper'; -import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate'; -import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; - -/** - * Component that displays an assignment feedback plugin. - */ -@Component({ - selector: 'addon-mod-assign-feedback-plugin', - templateUrl: 'addon-mod-assign-feedback-plugin.html', -}) -export class AddonModAssignFeedbackPluginComponent implements OnInit { - @ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent; - - @Input() assign: AddonModAssignAssign; // The assignment. - @Input() submission: AddonModAssignSubmission; // The submission. - @Input() plugin: AddonModAssignPlugin; // The plugin object. - @Input() userId: number; // The user ID of the submission. - @Input() canEdit: boolean | string; // Whether the user can edit. - @Input() edit: boolean | string; // Whether the user is editing. - - pluginComponent: any; // Component to render the plugin. - data: any; // Data to pass to the component. - - // Data to render the plugin if it isn't supported. - component = AddonModAssignProvider.COMPONENT; - text = ''; - files = []; - notSupported: boolean; - pluginLoaded: boolean; - - constructor(protected injector: Injector, protected feedbackDelegate: AddonModAssignFeedbackDelegate, - protected assignProvider: AddonModAssignProvider, protected assignHelper: AddonModAssignHelperProvider) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.plugin) { - this.pluginLoaded = true; - - return; - } - - this.plugin.name = this.feedbackDelegate.getPluginName(this.plugin); - if (!this.plugin.name) { - this.pluginLoaded = true; - - return; - } - - this.edit = this.edit && this.edit !== 'false'; - this.canEdit = this.canEdit && this.canEdit !== 'false'; - - // Check if the plugin has defined its own component to render itself. - this.feedbackDelegate.getComponentForPlugin(this.injector, this.plugin).then((component) => { - this.pluginComponent = component; - - if (component) { - // Prepare the data to pass to the component. - this.data = { - assign: this.assign, - submission: this.submission, - plugin: this.plugin, - userId: this.userId, - configs: this.assignHelper.getPluginConfig(this.assign, 'assignfeedback', this.plugin.type), - edit: this.edit, - canEdit: this.canEdit - }; - } else { - // Data to render the plugin. - this.text = this.assignProvider.getSubmissionPluginText(this.plugin); - this.files = this.assignProvider.getSubmissionPluginAttachments(this.plugin); - this.notSupported = this.feedbackDelegate.isPluginSupported(this.plugin.type); - this.pluginLoaded = true; - } - }); - } - - /** - * Invalidate the plugin data. - * - * @return Promise resolved when done. - */ - invalidate(): Promise { - return Promise.resolve(this.dynamicComponent && this.dynamicComponent.callComponentFunction('invalidate', [])); - } -} diff --git a/src/addon/mod/assign/components/index/addon-mod-assign-index.html b/src/addon/mod/assign/components/index/addon-mod-assign-index.html deleted file mode 100644 index 18284862e..000000000 --- a/src/addon/mod/assign/components/index/addon-mod-assign-index.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - {{ 'core.hasdatatosync' | translate:{$a: moduleName} }} -
- - - - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - - -

{{ 'addon.mod_assign.timeremaining' | translate }}

-

{{ timeRemaining }}

-
- -

{{ 'addon.mod_assign.latesubmissions' | translate }}

-

{{ lateSubmissions }}

-
- - - -

{{ 'addon.mod_assign.numberofteams' | translate }}

-

{{ 'addon.mod_assign.numberofparticipants' | translate }}

- - {{ summary.participantcount }} - -
- - - -

{{ 'addon.mod_assign.numberofdraftsubmissions' | translate }}

- - {{ summary.submissiondraftscount }} - -
- - - -

{{ 'addon.mod_assign.numberofsubmittedassignments' | translate }}

- - {{ summary.submissionssubmittedcount }} - -
- - - -

{{ 'addon.mod_assign.numberofsubmissionsneedgrading' | translate }}

- - {{ summary.submissionsneedgradingcount }} - -
- - -
- - {{ 'addon.mod_assign.'+summary.warnofungroupedusers | translate }} -
-
- - - - -
diff --git a/src/addon/mod/assign/components/index/index.ts b/src/addon/mod/assign/components/index/index.ts deleted file mode 100644 index 6f872d3c6..000000000 --- a/src/addon/mod/assign/components/index/index.ts +++ /dev/null @@ -1,390 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector, ViewChild } from '@angular/core'; -import { Content, NavController } from 'ionic-angular'; -import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmissionGradingSummary } from '../../providers/assign'; -import { AddonModAssignHelperProvider } from '../../providers/helper'; -import { AddonModAssignOfflineProvider } from '../../providers/assign-offline'; -import { AddonModAssignSyncProvider } from '../../providers/assign-sync'; -import { AddonModAssignSubmissionComponent } from '../submission/submission'; - -/** - * Component that displays an assignment. - */ -@Component({ - selector: 'addon-mod-assign-index', - templateUrl: 'addon-mod-assign-index.html', -}) -export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityComponent { - @ViewChild(AddonModAssignSubmissionComponent) submissionComponent: AddonModAssignSubmissionComponent; - - component = AddonModAssignProvider.COMPONENT; - moduleName = 'assign'; - - assign: AddonModAssignAssign; // The assign object. - canViewAllSubmissions: boolean; // Whether the user can view all submissions. - canViewOwnSubmission: boolean; // Whether the user can view their own submission. - timeRemaining: string; // Message about time remaining to submit. - lateSubmissions: string; // Message about late submissions. - showNumbers = true; // Whether to show number of submissions with each status. - summary: AddonModAssignSubmissionGradingSummary; // The grading summary. - needsGradingAvalaible: boolean; // Whether we can see the submissions that need grading. - - groupInfo: CoreGroupInfo = { - groups: [], - separateGroups: false, - visibleGroups: false - }; - - // Status. - submissionStatusSubmitted = AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED; - submissionStatusDraft = AddonModAssignProvider.SUBMISSION_STATUS_DRAFT; - needGrading = AddonModAssignProvider.NEED_GRADING; - - protected userId: number; // Current user ID. - protected syncEventName = AddonModAssignSyncProvider.AUTO_SYNCED; - - // Observers. - protected savedObserver; - protected submittedObserver; - protected gradedObserver; - - constructor(injector: Injector, protected assignProvider: AddonModAssignProvider, @Optional() content: Content, - protected assignHelper: AddonModAssignHelperProvider, protected assignOffline: AddonModAssignOfflineProvider, - protected syncProvider: AddonModAssignSyncProvider, protected timeUtils: CoreTimeUtilsProvider, - protected groupsProvider: CoreGroupsProvider, protected navCtrl: NavController) { - super(injector, content); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.userId = this.sitesProvider.getCurrentSiteUserId(); - - this.loadContent(false, true).then(() => { - this.assignProvider.logView(this.assign.id, this.assign.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - - if (this.canViewAllSubmissions) { - // User can see all submissions, log grading view. - this.assignProvider.logGradingView(this.assign.id, this.assign.name).catch(() => { - // Ignore errors. - }); - } else if (this.canViewOwnSubmission) { - // User can only see their own submission, log view the user submission. - this.assignProvider.logSubmissionView(this.assign.id, this.assign.name).catch(() => { - // Ignore errors. - }); - } - }); - - // Listen to events. - this.savedObserver = this.eventsProvider.on(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, (data) => { - if (this.assign && data.assignmentId == this.assign.id && data.userId == this.userId) { - // Assignment submission saved, refresh data. - this.showLoadingAndRefresh(true, false); - } - }, this.siteId); - - this.submittedObserver = this.eventsProvider.on(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, (data) => { - if (this.assign && data.assignmentId == this.assign.id && data.userId == this.userId) { - // Assignment submitted, check completion. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - - // Reload data since it can have offline data now. - this.showLoadingAndRefresh(true, false); - } - }, this.siteId); - - this.gradedObserver = this.eventsProvider.on(AddonModAssignProvider.GRADED_EVENT, (data) => { - if (this.assign && data.assignmentId == this.assign.id && data.userId == this.userId) { - // Assignment graded, refresh data. - this.showLoadingAndRefresh(true, false); - } - }, this.siteId); - } - - /** - * Expand the description. - */ - expandDescription(ev?: Event): void { - ev && ev.preventDefault(); - ev && ev.stopPropagation(); - - if (this.assign && (this.description || this.assign.introattachments)) { - this.textUtils.viewText(this.translate.instant('core.description'), this.description, { - component: this.component, - componentId: this.module.id, - files: this.assign.introattachments, - filter: true, - contextLevel: 'module', - instanceId: this.module.id, - courseId: this.courseId, - }); - } - } - - /** - * Get assignment data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - - // Get assignment data. - return this.assignProvider.getAssignment(this.courseId, this.module.id).then((assignData) => { - this.assign = assignData; - - this.dataRetrieved.emit(this.assign); - this.description = this.assign.intro; - - if (sync) { - // Try to synchronize the assign. - return this.syncActivity(showErrors).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Check if there's any offline data for this assign. - return this.assignOffline.hasAssignOfflineData(this.assign.id); - }).then((hasOffline) => { - this.hasOffline = hasOffline; - - // Get assignment submissions. - return this.assignProvider.getSubmissions(this.assign.id, {cmId: this.module.id}).then((data) => { - const time = this.timeUtils.timestamp(); - - this.canViewAllSubmissions = data.canviewsubmissions; - - if (data.canviewsubmissions) { - - // Calculate the messages to display about time remaining and late submissions. - if (this.assign.duedate > 0) { - if (this.assign.duedate - time <= 0) { - this.timeRemaining = this.translate.instant('addon.mod_assign.assignmentisdue'); - } else { - this.timeRemaining = this.timeUtils.formatDuration(this.assign.duedate - time, 3); - - if (this.assign.cutoffdate) { - if (this.assign.cutoffdate > time) { - this.lateSubmissions = this.translate.instant('addon.mod_assign.latesubmissionsaccepted', - {$a: this.timeUtils.userDate(this.assign.cutoffdate * 1000)}); - } else { - this.lateSubmissions = this.translate.instant('addon.mod_assign.nomoresubmissionsaccepted'); - } - } else { - this.lateSubmissions = ''; - } - } - } else { - this.timeRemaining = ''; - this.lateSubmissions = ''; - } - - // Check if groupmode is enabled to avoid showing wrong numbers. - return this.groupsProvider.getActivityGroupInfo(this.assign.cmid, false).then((groupInfo) => { - const currentSite = this.sitesProvider.getCurrentSite(); - this.groupInfo = groupInfo; - this.showNumbers = groupInfo.groups.length == 0 || - (currentSite && currentSite.isVersionGreaterEqualThan('3.5')); - - return this.setGroup(this.groupsProvider.validateGroupId(this.group, groupInfo)); - }); - } - - // Check if the user can view their own submission. - return this.assignProvider.getSubmissionStatus(this.assign.id, {cmId: this.module.id}).then(() => { - this.canViewOwnSubmission = true; - }).catch((error) => { - this.canViewOwnSubmission = false; - - if (error.errorcode !== 'nopermission') { - return Promise.reject(error); - } - }); - }); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Set group to see the summary. - * - * @param groupId Group ID. - * @return Resolved when done. - */ - setGroup(groupId: number): Promise { - this.group = groupId; - - return this.assignProvider.getSubmissionStatus(this.assign.id, { - groupId: this.group, - cmId: this.module.id, - }).then((response) => { - this.summary = response.gradingsummary; - if (typeof this.summary.warnofungroupedusers == 'boolean' && this.summary.warnofungroupedusers) { - this.summary.warnofungroupedusers = 'ungroupedusers'; - } else { - switch (this.summary.warnofungroupedusers) { - case AddonModAssignProvider.WARN_GROUPS_REQUIRED: - this.summary.warnofungroupedusers = 'ungroupedusers'; - break; - case AddonModAssignProvider.WARN_GROUPS_OPTIONAL: - this.summary.warnofungroupedusers = 'ungroupedusersoptional'; - break; - default: - this.summary.warnofungroupedusers = ''; - break; - } - } - - const currentSite = this.sitesProvider.getCurrentSite(); - - this.needsGradingAvalaible = response.gradingsummary && response.gradingsummary.submissionsneedgradingcount > 0 && - currentSite && currentSite.isVersionGreaterEqualThan('3.2'); - }); - } - - /** - * Go to view a list of submissions. - * - * @param status Status to see. - * @param count Number of submissions with the status. - */ - goToSubmissionList(status: string, count: number): void { - if (typeof status == 'undefined') { - this.navCtrl.push('AddonModAssignSubmissionListPage', { - courseId: this.courseId, - groupId: this.group || 0, - moduleId: this.module.id, - moduleName: this.moduleName - }); - } else if (count || !this.showNumbers) { - this.navCtrl.push('AddonModAssignSubmissionListPage', { - status: status, - courseId: this.courseId, - groupId: this.group || 0, - moduleId: this.module.id, - moduleName: this.moduleName - }); - } - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned by the sync function. - * @return If succeed or not. - */ - protected hasSyncSucceed(result: any): boolean { - if (result.updated) { - this.submissionComponent && this.submissionComponent.invalidateAndRefresh(false); - } - - return result.updated; - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.assignProvider.invalidateAssignmentData(this.courseId)); - - if (this.assign) { - promises.push(this.assignProvider.invalidateAllSubmissionData(this.assign.id)); - - if (this.canViewAllSubmissions) { - promises.push(this.assignProvider.invalidateSubmissionStatusData(this.assign.id, undefined, this.group)); - } - } - - return Promise.all(promises).finally(() => { - this.submissionComponent && this.submissionComponent.invalidateAndRefresh(true); - }); - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - super.ionViewDidEnter(); - - this.submissionComponent && this.submissionComponent.ionViewDidEnter(); - } - - /** - * User left the page that contains the component. - */ - ionViewDidLeave(): void { - super.ionViewDidLeave(); - - this.submissionComponent && this.submissionComponent.ionViewDidLeave(); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (this.assign && syncEventData.assignId == this.assign.id) { - if (syncEventData.warnings && syncEventData.warnings.length) { - // Show warnings. - this.domUtils.showErrorModal(syncEventData.warnings[0]); - } - - return true; - } - - return false; - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.syncProvider.syncAssign(this.assign.id); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - - this.savedObserver && this.savedObserver.off(); - this.submittedObserver && this.submittedObserver.off(); - this.gradedObserver && this.gradedObserver.off(); - } -} diff --git a/src/addon/mod/assign/components/submission-plugin/addon-mod-assign-submission-plugin.html b/src/addon/mod/assign/components/submission-plugin/addon-mod-assign-submission-plugin.html deleted file mode 100644 index d7223bfc7..000000000 --- a/src/addon/mod/assign/components/submission-plugin/addon-mod-assign-submission-plugin.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - -

{{ plugin.name }}

- - {{ 'addon.mod_assign.submissionnotsupported' | translate }} - -

- -

- -
-
-
diff --git a/src/addon/mod/assign/components/submission-plugin/submission-plugin.ts b/src/addon/mod/assign/components/submission-plugin/submission-plugin.ts deleted file mode 100644 index ae939d9be..000000000 --- a/src/addon/mod/assign/components/submission-plugin/submission-plugin.ts +++ /dev/null @@ -1,100 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, Injector, ViewChild } from '@angular/core'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../providers/assign'; -import { AddonModAssignHelperProvider } from '../../providers/helper'; -import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate'; -import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; - -/** - * Component that displays an assignment submission plugin. - */ -@Component({ - selector: 'addon-mod-assign-submission-plugin', - templateUrl: 'addon-mod-assign-submission-plugin.html', -}) -export class AddonModAssignSubmissionPluginComponent implements OnInit { - @ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent; - - @Input() assign: AddonModAssignAssign; // The assignment. - @Input() submission: AddonModAssignSubmission; // The submission. - @Input() plugin: AddonModAssignPlugin; // The plugin object. - @Input() edit: boolean | string; // Whether the user is editing. - @Input() allowOffline: boolean | string; // Whether to allow offline. - - pluginComponent: any; // Component to render the plugin. - data: any; // Data to pass to the component. - - // Data to render the plugin if it isn't supported. - component = AddonModAssignProvider.COMPONENT; - text = ''; - files = []; - notSupported: boolean; - pluginLoaded: boolean; - - constructor(protected injector: Injector, protected submissionDelegate: AddonModAssignSubmissionDelegate, - protected assignProvider: AddonModAssignProvider, protected assignHelper: AddonModAssignHelperProvider) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.plugin) { - return; - } - - this.plugin.name = this.submissionDelegate.getPluginName(this.plugin); - if (!this.plugin.name) { - return; - } - - this.edit = this.edit && this.edit !== 'false'; - this.allowOffline = this.allowOffline && this.allowOffline !== 'false'; - - // Check if the plugin has defined its own component to render itself. - this.submissionDelegate.getComponentForPlugin(this.injector, this.plugin, this.edit).then((component) => { - this.pluginComponent = component; - - if (component) { - // Prepare the data to pass to the component. - this.data = { - assign: this.assign, - submission: this.submission, - plugin: this.plugin, - configs: this.assignHelper.getPluginConfig(this.assign, 'assignsubmission', this.plugin.type), - edit: this.edit, - allowOffline: this.allowOffline - }; - } else { - // Data to render the plugin. - this.text = this.assignProvider.getSubmissionPluginText(this.plugin); - this.files = this.assignProvider.getSubmissionPluginAttachments(this.plugin); - this.notSupported = this.submissionDelegate.isPluginSupported(this.plugin.type); - this.pluginLoaded = true; - } - }); - } - - /** - * Invalidate the plugin data. - * - * @return Promise resolved when done. - */ - invalidate(): Promise { - return Promise.resolve(this.dynamicComponent && this.dynamicComponent.callComponentFunction('invalidate', [])); - } -} diff --git a/src/addon/mod/assign/components/submission/addon-mod-assign-submission.html b/src/addon/mod/assign/components/submission/addon-mod-assign-submission.html deleted file mode 100644 index 2164fd42d..000000000 --- a/src/addon/mod/assign/components/submission/addon-mod-assign-submission.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - - -

{{ user.fullname }}

- -
- - - -

{{ 'addon.mod_assign.hiddenuser' | translate }} {{blindId}}

- -
- - - -

{{ 'addon.mod_assign.submissionstatus' | translate }}

- -
- - - - - - - - - - -

{{ 'addon.mod_assign.timemodified' | translate }}

-

{{ userSubmission.timemodified * 1000 | coreFormatDate }}

-
- - -

{{ 'addon.mod_assign.timeremaining' | translate }}

-

-
- - -

-

-
- - -

{{ 'addon.mod_assign.duedate' | translate }}

-

{{ assign.duedate * 1000 | coreFormatDate }}

-

{{ 'addon.mod_assign.duedateno' | translate }}

-
- - -

{{ 'addon.mod_assign.cutoffdate' | translate }}

-

{{ assign.cutoffdate * 1000 | coreFormatDate }}

-
- - -

{{ 'addon.mod_assign.extensionduedate' | translate }}

-

{{ lastAttempt.extensionduedate * 1000 | coreFormatDate }}

-
- - -

{{ 'addon.mod_assign.attemptnumber' | translate }}

-

{{ 'addon.mod_assign.outof' | translate : {'$a': {'current': currentAttempt, 'total': maxAttemptsText} } }}

-

{{ 'addon.mod_assign.outof' | translate : {'$a': {'current': currentAttempt, 'total': assign.maxattempts} } }}

-
- - - - -
-

{{ 'addon.mod_assign.erroreditpluginsnotsupported' | translate }}

-

{{ name }}

-
-
-

{{ 'addon.mod_assign.cannoteditduetostatementsubmission' | translate }}

-
-
- - -
- - - - - - - - {{ 'addon.mod_assign.submitassignment' | translate }} -

{{ 'addon.mod_assign.submitassignment_help' | translate }}

-
- - -

{{ 'addon.mod_assign.cannotsubmitduetostatementsubmission' | translate }}

-
-
- - - -

{{ 'addon.mod_assign.userswhoneedtosubmit' | translate: {$a: ''} }}

-
- - -

{{ user.fullname }}

-
- - {{ 'addon.mod_assign.hiddenuser' | translate }} {{ user }} - -
-
- - - -

{{ 'addon.mod_assign.submissionslocked' | translate }}

-
- - - -

{{ 'addon.mod_assign.editingstatus' | translate }}

-

{{ 'addon.mod_assign.submissioneditable' | translate }}

-

{{ 'addon.mod_assign.submissionnoteditable' | translate }}

-
-
-
- - - - - - -

{{ 'addon.mod_assign.currentgrade' | translate }}

-

- - - -
- - - -

{{ 'addon.mod_assign.gradeoutof' | translate: {$a: gradeInfo.grade} }}

- -

{{ 'addon.mod_assign.gradelocked' | translate }}

-
- - - -

{{ 'addon.mod_assign.grade' | translate }}

- - {{grade.label}} - -
- - - -

{{ outcome.name }}

- - {{grade.label}} - -

{{ outcome.selected }}

-
- - - -

{{ 'addon.mod_assign.currentgrade' | translate }}

-

{{ grade.gradebookGrade }}

-

{{ grade.scale[grade.gradebookGrade].label }}

-

-

-
- - - - - -

{{ 'addon.mod_assign.markingworkflowstate' | translate }}

-

{{ workflowStatusTranslationId | translate }}

-
- - - -

{{ 'addon.mod_assign.groupsubmissionsettings' | translate }}

- {{ 'addon.mod_assign.applytoteam' | translate }} - -
- - - - -

{{ 'addon.mod_assign.attemptsettings' | translate }}

-

{{ 'addon.mod_assign.outof' | translate : {'$a': {'current': currentAttempt, 'total': maxAttemptsText} } }}

-

{{ 'addon.mod_assign.outof' | translate : {'$a': {'current': currentAttempt, 'total': assign.maxattempts} } }}

-

{{ 'addon.mod_assign.attemptreopenmethod' | translate }}: {{ 'addon.mod_assign.attemptreopenmethod_' + assign.attemptreopenmethod | translate }}

-
- - {{ 'addon.mod_assign.addattempt' | translate }} - - -
- - - - -

{{ 'addon.mod_assign.gradedby' | translate }}

-

{{ grader.fullname }}

-

{{ feedback.gradeddate * 1000 | coreFormatDate }}

-
- - - -

{{ 'addon.mod_assign.gradedon' | translate }}

-

{{ feedback.gradeddate * 1000 | coreFormatDate }}

-
- - -
- -

{{ 'addon.mod_assign.cannotgradefromapp' | translate:{$a: moduleName} }}

- - {{ 'core.openinbrowser' | translate }} - - -
-
-
-
-
- - - - -

{{lastAttempt.submissiongroupname}}

- -

{{ 'addon.mod_assign.noteam' | translate }}

-

{{ 'addon.mod_assign.noteam_desc' | translate }}

-
- -

{{ 'addon.mod_assign.multipleteams' | translate }}

-

{{ 'addon.mod_assign.multipleteams_desc' | translate }}

-
-

- {{ 'addon.mod_assign.defaultteam' | translate }} -

-
- - {{ statusTranslated }} - - - {{ gradingStatusTranslationId | translate }} - -
diff --git a/src/addon/mod/assign/components/submission/submission.scss b/src/addon/mod/assign/components/submission/submission.scss deleted file mode 100644 index 8d3f9a458..000000000 --- a/src/addon/mod/assign/components/submission/submission.scss +++ /dev/null @@ -1,263 +0,0 @@ -ion-app.app-root addon-mod-assign-submission { - div.latesubmission, - div.overdue { - // @extend .core-danger-item; - } - - div.earlysubmission { - // @extend .core-success-item; - } - - div.submissioneditable p { - color: $red; - @include darkmode() { - color: $red-light; - } - } - - .core-grading-summary .advancedgrade { - display: none; - } -} - -core-format-text { - - .gradingform_rubric_editform .status { - font-weight: normal; - text-transform: uppercase; - font-size: 60%; - padding: 0.25em; - border: 1px solid $gray-light; - } - - .gradingform_rubric_editform .status.ready { - background-color: $green-light; - border-color: $green; - } - - .gradingform_rubric_editform .status.draft { - background-color: $yellow-light; - border-color: $yellow; - } - - .gradingform_rubric { - overflow: auto; - padding-bottom: 1.5em; - max-width: 720px; - position: relative; - margin: 0 auto; - tbody { - background: $white; - color: $text-color; - } - } - - // Do not display remark column. - .gradingform_rubric .criterion .remark { - display: none; - } - - .gradingform_rubric.editor .criterion .controls, - .gradingform_rubric .criterion .description, - .gradingform_rubric .criterion .levels, - .gradingform_rubric.editor .criterion .addlevel, - .gradingform_rubric .criterion .remark, - .gradingform_rubric .criterion .levels .level { - vertical-align: top; - } - - .gradingform_rubric.editor .criterion .controls, - .gradingform_rubric .criterion .description, - .gradingform_rubric.editor .criterion .addlevel, - .gradingform_rubric .criterion .remark, - .gradingform_rubric .criterion .levels .level { - padding: 3px; - } - - .gradingform_rubric .criteria { - height: 100%; - } - - .gradingform_rubric .criterion { - border: 1px solid $gray; - overflow: hidden; - } - - .gradingform_rubric .criterion.even { - background: $gray-lighter; - } - - .gradingform_rubric .criterion .description { - width: 150px; - font-weight: bold; - } - - .gradingform_rubric .criterion .levels table { - width: 100%; - height: 100%; - } - - .gradingform_rubric .criterion .levels, - .gradingform_rubric .criterion .levels table, - .gradingform_rubric .criterion .levels table tbody { - padding: 0; - margin: 0; - } - - .gradingform_rubric .criterion .levels .level { - border-left: 1px solid $gray; - max-width: 150px; - } - - .gradingform_rubric .criterion .levels .level .level-wrapper { - position: relative; - } - - .gradingform_rubric .criterion .levels .level.last { - border-right: 1px solid $gray; - } - - .gradingform_rubric .plainvalue.empty { - font-style: italic; - color: $gray-dark; - } - - .gradingform_rubric.editor .criterion .levels .level .delete { - position: absolute; - right: 0; - } - - .gradingform_rubric .criterion .levels .level .score { - font-style: italic; - color: $green; - font-weight: bold; - margin-top: 5px; - white-space: nowrap; - } - - .gradingform_rubric .criterion .levels .level .score .scorevalue { - padding-right: 5px; - } - - /* Make invisible the buttons 'Move up' for the first criterion and - 'Move down' for the last, because those buttons will make no change */ - .gradingform_rubric.editor .criterion.first .controls .moveup input, - .gradingform_rubric.editor .criterion.last .controls .movedown input { - display: none; - } - - /* evaluation */ - .gradingform_rubric .criterion .levels .level.currentchecked { - background: #fff0f0; - } - - .gradingform_rubric .criterion .levels .level.checked { - background: $green-light; - border: 1px solid $gray-darker; - } - - .gradingform_rubric .options .optionsheading { - font-weight: bold; - font-size: 1.1em; - padding-bottom: 5px; - } - - .gradingform_rubric .options .option { - padding-bottom: 2px; - } - - .gradingform_rubric .options .option label { - margin-left: 5px; - } - - .gradingform_rubric .options .option .value { - margin-left: 5px; - font-weight: bold; - } - - .gradingform_rubric .criterion .levels.error { - border: 1px solid $red; - } - - .gradingform_rubric .criterion .description.error, - .gradingform_rubric .criterion .levels .level .definition.error, - .gradingform_rubric .criterion .levels .level .score.error { - background: $gray-lighter; - } - - .gradingform_rubric-regrade { - padding: 10px; - background: $gray-lighter; - border: 1px solid $red-light; - margin-bottom: 10px; - } - - .gradingform_rubric-restored { - padding: 10px; - background: $yellow-light; - border: 1px solid $yellow; - margin-bottom: 10px; - } - - .gradingform_rubric-error { - color: $red; - font-weight: bold; - } - - /* special classes for elements created by rubriceditor.js */ - .gradingform_rubric.editor .hiddenelement { - display: none; - } - - .gradingform_rubric.editor .pseudotablink { - background-color: transparent; - border: 0 solid; - height: 1px; - width: 1px; - color: transparent; - padding: 0; - margin: 0; - position: relative; - float: right; - } - - .gradingform_rubric { - padding-bottom: 0; - max-width: none; - } - - .gradingform_rubric .criterion .description { - font-weight: 500; - min-width: 150px; - } - - .gradingform_rubric .criterion .levels { - background-color: $white; - } - - .gradingform_rubric .criterion, - .gradingform_rubric .criterion.even { - background-color: transparent; - } - - .gradingform_rubric.evaluate .criterion .levels .level:hover { - background-color: $green-light; - } - - .gradingform_rubric .criterion .levels .level.checked { - background-color: $green-light; - border: none; - border-left: 1px solid $gray; - } - - .gradingform_rubric .criterion .levels .level .score { - color: $green; - font-weight: 500; - font-style: normal; - margin-top: 20px; - } - - .gradingform_rubric .criterion .remark textarea { - margin-bottom: 0; - } -} diff --git a/src/addon/mod/assign/components/submission/submission.ts b/src/addon/mod/assign/components/submission/submission.ts deleted file mode 100644 index 92cf4167b..000000000 --- a/src/addon/mod/assign/components/submission/submission.ts +++ /dev/null @@ -1,1083 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, OnDestroy, ViewChild, Optional, ViewChildren, QueryList } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider, CoreEventObserver } from '@providers/events'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreLangProvider } from '@providers/lang'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper'; -import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmissionFeedback, AddonModAssignSubmission, - AddonModAssignSubmissionAttempt, AddonModAssignSubmissionPreviousAttempt, AddonModAssignPlugin -} from '../../providers/assign'; -import { AddonModAssignHelperProvider } from '../../providers/helper'; -import { AddonModAssignOfflineProvider } from '../../providers/assign-offline'; -import { AddonModAssignSync, AddonModAssignSyncProvider } from '../../providers/assign-sync'; -import { CoreTabsComponent } from '@components/tabs/tabs'; -import { CoreTabComponent } from '@components/tabs/tab'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { AddonModAssignSubmissionPluginComponent } from '../submission-plugin/submission-plugin'; - -/** - * Component that displays an assignment submission. - */ -@Component({ - selector: 'addon-mod-assign-submission', - templateUrl: 'addon-mod-assign-submission.html', -}) -export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy { - @ViewChild(CoreTabsComponent) tabs: CoreTabsComponent; - @ViewChildren(AddonModAssignSubmissionPluginComponent) submissionComponents: QueryList; - - @Input() courseId: number; // Course ID the submission belongs to. - @Input() moduleId: number; // Module ID the submission belongs to. - @Input() submitId: number; // User that did the submission. - @Input() blindId: number; // Blinded user ID (if it's blinded). - @Input() showGrade: boolean | string; // Whether to display the grade tab at start. - - loaded: boolean; // Whether data has been loaded. - selectedTab: number; // Tab selected on start. - assign: AddonModAssignAssign; // The assignment the submission belongs to. - userSubmission: AddonModAssignSubmission; // The submission object. - isSubmittedForGrading: boolean; // Whether the submission has been submitted for grading. - submitModel: any = {}; // Model where to store the data to submit (for grading). - feedback: AddonModAssignSubmissionFeedbackFormatted; // The feedback. - hasOffline: boolean; // Whether there is offline data. - submittedOffline: boolean; // Whether it was submitted in offline. - fromDate: string; // Readable date when the assign started accepting submissions. - currentAttempt: number; // The current attempt number. - maxAttemptsText: string; // The text for maximum attempts. - blindMarking: boolean; // Whether blind marking is enabled. - user: any; // The user. - lastAttempt: AddonModAssignSubmissionAttemptFormatted; // The last attempt. - membersToSubmit: any[]; // Team members that need to submit the assignment. - canSubmit: boolean; // Whether the user can submit for grading. - canEdit: boolean; // Whether the user can edit the submission. - submissionStatement: string; // The submission statement. - showErrorStatementEdit: boolean; // Whether to show an error in edit due to submission statement. - showErrorStatementSubmit: boolean; // Whether to show an error in submit due to submission statement. - gradingStatusTranslationId: string; // Key of the text to display for the grading status. - gradingColor: string; // Color to apply to the grading status. - workflowStatusTranslationId: string; // Key of the text to display for the workflow status. - submissionPlugins: AddonModAssignPlugin[]; // List of submission plugins. - timeRemaining: string; // Message about time remaining. - timeRemainingClass: string; // Class to apply to time remaining message. - statusTranslated: string; // Status. - statusColor: string; // Color to apply to the status. - unsupportedEditPlugins: string[]; // List of submission plugins that don't support edit. - grade: any; // Data about the grade. - grader: any; // Profile of the teacher that graded the submission. - gradeInfo: any; // Grade data for the assignment, retrieved from the server. - isGrading: boolean; // Whether the user is grading. - canSaveGrades: boolean; // Whether the user can save the grades. - allowAddAttempt: boolean; // Allow adding a new attempt when grading. - gradeUrl: string; // URL to grade in browser. - - // Some constants. - statusNew = AddonModAssignProvider.SUBMISSION_STATUS_NEW; - statusReopened = AddonModAssignProvider.SUBMISSION_STATUS_REOPENED; - attemptReopenMethodNone = AddonModAssignProvider.ATTEMPT_REOPEN_METHOD_NONE; - unlimitedAttempts = AddonModAssignProvider.UNLIMITED_ATTEMPTS; - - protected siteId: string; // Current site ID. - protected currentUserId: number; // Current user ID. - protected previousAttempt: AddonModAssignSubmissionPreviousAttempt; // The previous attempt. - protected isPreviousAttemptEmpty: boolean; // Whether the previous attempt contains an empty submission. - protected submissionStatusAvailable: boolean; // Whether we were able to retrieve the submission status. - protected originalGrades: any = {}; // Object with the original grade data, to check for changes. - protected isDestroyed: boolean; // Whether the component has been destroyed. - protected syncObserver: CoreEventObserver; - protected hasOfflineGrade = false; - - constructor(protected navCtrl: NavController, protected appProvider: CoreAppProvider, protected domUtils: CoreDomUtilsProvider, - sitesProvider: CoreSitesProvider, protected syncProvider: CoreSyncProvider, protected timeUtils: CoreTimeUtilsProvider, - protected textUtils: CoreTextUtilsProvider, protected translate: TranslateService, protected utils: CoreUtilsProvider, - protected eventsProvider: CoreEventsProvider, protected courseProvider: CoreCourseProvider, - protected fileUploaderHelper: CoreFileUploaderHelperProvider, protected gradesHelper: CoreGradesHelperProvider, - protected userProvider: CoreUserProvider, protected groupsProvider: CoreGroupsProvider, - protected langProvider: CoreLangProvider, protected assignProvider: AddonModAssignProvider, - protected assignHelper: AddonModAssignHelperProvider, protected assignOfflineProvider: AddonModAssignOfflineProvider, - @Optional() protected splitviewCtrl: CoreSplitViewComponent) { - - this.siteId = sitesProvider.getCurrentSiteId(); - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.selectedTab = this.showGrade && this.showGrade !== 'false' ? 1 : 0; - this.isSubmittedForGrading = !!this.submitId; - - this.loadData(true); - - // Refresh data if this assign is synchronized and it's grading. - const events = [AddonModAssignSyncProvider.AUTO_SYNCED, AddonModAssignSyncProvider.MANUAL_SYNCED]; - - this.syncObserver = this.eventsProvider.onMultiple(events, async (data) => { - // Check that user is grading and this grade wasn't blocked when sync was performed. - if (!this.loaded || !this.isGrading || data.gradesBlocked.indexOf(this.submitId) != -1) { - return; - } - - if (data.context == 'submission' && data.submitId == this.submitId) { - // Manual sync triggered by this same submission, ignore it. - return; - } - - // Don't refresh if the user has modified some data. - const hasDataToSave = await this.hasDataToSave(); - - if (!hasDataToSave) { - this.invalidateAndRefresh(false); - } - }, this.siteId); - } - - /** - * Calculate the time remaining message and class. - * - * @param response Response of get submission status. - */ - protected calculateTimeRemaining(response: any): void { - if (this.assign.duedate > 0) { - const time = this.timeUtils.timestamp(), - dueDate = response.lastattempt && response.lastattempt.extensionduedate ? - response.lastattempt.extensionduedate : this.assign.duedate, - timeRemaining = dueDate - time; - - if (timeRemaining <= 0) { - if (!this.userSubmission || this.userSubmission.status != AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) { - - if ((response.lastattempt && response.lastattempt.submissionsenabled) || - (response.gradingsummary && response.gradingsummary.submissionsenabled)) { - this.timeRemaining = this.translate.instant('addon.mod_assign.overdue', - {$a: this.timeUtils.formatDuration(-timeRemaining, 3) }); - this.timeRemainingClass = 'overdue'; - } else { - this.timeRemaining = this.translate.instant('addon.mod_assign.duedatereached'); - this.timeRemainingClass = ''; - } - } else { - - const timeSubmittedDiff = this.userSubmission.timemodified - dueDate; - if (timeSubmittedDiff > 0) { - this.timeRemaining = this.translate.instant('addon.mod_assign.submittedlate', - {$a: this.timeUtils.formatDuration(timeSubmittedDiff, 2) }); - this.timeRemainingClass = 'latesubmission'; - } else { - this.timeRemaining = this.translate.instant('addon.mod_assign.submittedearly', - {$a: this.timeUtils.formatDuration(-timeSubmittedDiff, 2) }); - this.timeRemainingClass = 'earlysubmission'; - } - } - } else { - this.timeRemaining = this.timeUtils.formatDuration(timeRemaining, 3); - this.timeRemainingClass = ''; - } - } else { - this.timeRemaining = ''; - this.timeRemainingClass = ''; - } - } - - /** - * Check if the user can leave the view. If there are changes to be saved, it will ask for confirm. - * - * @return Promise resolved if can leave the view, rejected otherwise. - */ - canLeave(): Promise { - // Check if there is data to save. - return this.hasDataToSave().then((modified) => { - if (modified) { - // Modified, confirm user wants to go back. - return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')).then(() => { - return this.discardDrafts().catch(() => { - // Ignore errors. - }); - }); - } - }); - } - - /** - * Copy a previous attempt and then go to edit. - */ - copyPrevious(): void { - if (!this.appProvider.isOnline()) { - this.domUtils.showErrorModal('core.networkerrormsg', true); - - return; - } - - if (!this.previousAttempt) { - // Cannot access previous attempts, just go to edit. - return this.goToEdit(); - } - - const previousSubmission = this.previousAttempt.submission; - let modal = this.domUtils.showModalLoading(); - - this.assignHelper.getSubmissionSizeForCopy(this.assign, previousSubmission).catch(() => { - // Error calculating size, return -1. - return -1; - }).then((size) => { - modal.dismiss(); - - // Confirm action. - return this.fileUploaderHelper.confirmUploadFile(size, true); - }).then(() => { - // User confirmed, copy the attempt. - modal = this.domUtils.showModalLoading('core.sending', true); - - this.assignHelper.copyPreviousAttempt(this.assign, previousSubmission).then(() => { - // Now go to edit. - this.goToEdit(); - - if (!this.assign.submissiondrafts) { - // No drafts allowed, so it was submitted. Trigger event. - this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, { - assignmentId: this.assign.id, - submissionId: this.userSubmission.id, - userId: this.currentUserId - }, this.siteId); - } else { - // Invalidate and refresh data to update this view. - this.invalidateAndRefresh(true); - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }).finally(() => { - modal.dismiss(); - }); - }).catch(() => { - // Cancelled. - }); - } - - /** - * Discard feedback drafts. - * - * @return Promise resolved when done. - */ - protected discardDrafts(): Promise { - if (this.feedback && this.feedback.plugins) { - return this.assignHelper.discardFeedbackPluginData(this.assign.id, this.submitId, this.feedback); - } - - return Promise.resolve(); - } - - /** - * Go to the page to add or edit submission. - */ - goToEdit(): void { - this.navCtrl.push('AddonModAssignEditPage', { - moduleId: this.moduleId, - courseId: this.courseId, - userId: this.submitId, - blindId: this.blindId - }); - } - - /** - * Check if there's data to save (grade). - * - * @param isSubmit Whether the user is about to submit the grade. - * @return Promise resolved with boolean: whether there's data to save. - */ - protected async hasDataToSave(isSubmit?: boolean): Promise { - if (!this.canSaveGrades || !this.loaded) { - return false; - } - - if (isSubmit && this.hasOfflineGrade) { - // Always allow sending if the grade is saved in offline. - return true; - } - - // Check if numeric grade and toggles changed. - if (this.originalGrades.grade != this.grade.grade || this.originalGrades.addAttempt != this.grade.addAttempt || - this.originalGrades.applyToAll != this.grade.applyToAll) { - return true; - } - - // Check if outcomes changed. - if (this.gradeInfo && this.gradeInfo.outcomes) { - for (const x in this.gradeInfo.outcomes) { - const outcome = this.gradeInfo.outcomes[x]; - - if (this.originalGrades.outcomes[outcome.id] == 'undefined' || - this.originalGrades.outcomes[outcome.id] != outcome.selectedId) { - return true; - } - } - } - - if (this.feedback && this.feedback.plugins) { - try { - return this.assignHelper.hasFeedbackDataChanged(this.assign, this.userSubmission, this.feedback, this.submitId); - } catch (error) { - // Error ocurred, consider there are no changes. - return false; - } - } - - return false; - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - this.tabs && this.tabs.ionViewDidEnter(); - } - - /** - * User left the page that contains the component. - */ - ionViewDidLeave(): void { - this.tabs && this.tabs.ionViewDidLeave(); - } - - /** - * Invalidate and refresh data. - * - * @param sync Whether to try to synchronize data. - * @return Promise resolved when done. - */ - invalidateAndRefresh(sync?: boolean): Promise { - this.loaded = false; - - const promises = []; - - promises.push(this.assignProvider.invalidateAssignmentData(this.courseId)); - if (this.assign) { - promises.push(this.assignProvider.invalidateSubmissionStatusData(this.assign.id, this.submitId, undefined, - !!this.blindId)); - promises.push(this.assignProvider.invalidateAssignmentUserMappingsData(this.assign.id)); - promises.push(this.assignProvider.invalidateListParticipantsData(this.assign.id)); - } - promises.push(this.gradesHelper.invalidateGradeModuleItems(this.courseId, this.submitId)); - promises.push(this.courseProvider.invalidateModule(this.moduleId)); - - // Invalidate plugins. - if (this.submissionComponents && this.submissionComponents.length) { - this.submissionComponents.forEach((component) => { - promises.push(component.invalidate()); - }); - } - - return Promise.all(promises).catch(() => { - // Ignore errors. - }).then(() => { - return this.loadData(sync); - }); - } - - /** - * Load the data to render the submission. - * - * @param sync Whether to try to synchronize data. - * @return Promise resolved when done. - */ - protected async loadData(sync?: boolean): Promise { - let isBlind = !!this.blindId; - - this.previousAttempt = undefined; - this.isPreviousAttemptEmpty = true; - - if (!this.submitId) { - this.submitId = this.currentUserId; - isBlind = false; - } - - try { - // Get the assignment. - this.assign = await this.assignProvider.getAssignment(this.courseId, this.moduleId); - - if (this.submitId != this.currentUserId && sync) { - // Teacher viewing a student submission. Try to sync the assign, there could be offline grades stored. - try { - const result = await AddonModAssignSync.instance.syncAssign(this.assign.id); - - if (result && result.updated) { - this.eventsProvider.trigger(AddonModAssignSyncProvider.MANUAL_SYNCED, { - assignId: this.assign.id, - warnings: result.warnings, - gradesBlocked: result.gradesBlocked, - context: 'submission', - submitId: this.submitId, - }, this.siteId); - } - } catch (error) { - // Ignore errors, probably user is offline or sync is blocked. - } - } - - const time = this.timeUtils.timestamp(); - let promises = []; - - if (this.assign.allowsubmissionsfromdate && this.assign.allowsubmissionsfromdate >= time) { - this.fromDate = this.timeUtils.userDate(this.assign.allowsubmissionsfromdate * 1000); - } - - this.currentAttempt = 0; - this.maxAttemptsText = this.translate.instant('addon.mod_assign.unlimitedattempts'); - this.blindMarking = this.isSubmittedForGrading && this.assign.blindmarking && !this.assign.revealidentities; - - if (!this.blindMarking && this.submitId != this.currentUserId) { - promises.push(this.loadSubmissionUserProfile()); - } - - // Check if there's any offline data for this submission. - promises.push(this.loadSubmissionOfflineData()); - - await Promise.all(promises); - - // Get submission status. - const response = await this.assignProvider.getSubmissionStatusWithRetry(this.assign, {userId: this.submitId, isBlind}); - - promises = []; - - this.submissionStatusAvailable = true; - this.lastAttempt = response.lastattempt; - this.membersToSubmit = []; - - // Search the previous attempt. - if (response.previousattempts && response.previousattempts.length > 0) { - const previousAttempts = response.previousattempts.sort((a, b) => { - return a.attemptnumber - b.attemptnumber; - }); - this.previousAttempt = previousAttempts[previousAttempts.length - 1]; - this.isPreviousAttemptEmpty = this.assignHelper.isSubmissionEmpty(this.assign, this.previousAttempt.submission); - } - - // Treat last attempt. - this.treatLastAttempt(response, promises); - - // Calculate the time remaining. - this.calculateTimeRemaining(response); - - // Load the feedback. - promises.push(this.loadFeedback(response.feedback)); - - // Check if there's any unsupported plugin for editing. - if (!this.userSubmission || !this.userSubmission.plugins) { - // Submission not created yet, we have to use assign configs to detect the plugins used. - this.userSubmission = this.assignHelper.createEmptySubmission(); - this.userSubmission.plugins = this.assignHelper.getPluginsEnabled(this.assign, 'assignsubmission'); - } - - // Get the submission plugins that don't support editing. - promises.push(this.loadUnsupportedPlugins()); - - await Promise.all(promises); - } catch (error) { - this.domUtils.showErrorModalDefault(error, 'Error getting assigment data.'); - } finally { - this.loaded = true; - } - } - - /** - * Load profile of submission's user. - * - * @return Promise resolved when done. - */ - protected async loadSubmissionUserProfile(): Promise { - this.user = await this.userProvider.getProfile(this.submitId, this.courseId); - } - - /** - * Load offline data for the submission (not the submission grade). - * - * @return Promise resolved when done. - */ - protected async loadSubmissionOfflineData(): Promise { - try { - const data = await this.assignOfflineProvider.getSubmission(this.assign.id, this.submitId); - - this.hasOffline = data && data.plugindata && Object.keys(data.plugindata).length > 0; - this.submittedOffline = data && data.submitted; - } catch (error) { - // No offline data found. - this.hasOffline = false; - this.submittedOffline = false; - } - } - - /** - * Load the data to render the feedback and grade. - * - * @param feedback The feedback data from the submission status. - * @return Promise resolved when done. - */ - protected loadFeedback(feedback: AddonModAssignSubmissionFeedback): Promise { - this.grade = { - method: false, - grade: false, - gradebookGrade: false, - modified: 0, - gradingStatus: false, - addAttempt : false, - applyToAll: false, - scale: false, - lang: false, - disabled: false - }; - - this.originalGrades = { - grade: false, - addAttempt: false, - applyToAll: false, - outcomes: {} - }; - - if (feedback) { - this.feedback = feedback; - - // If we have data about the grader, get its profile. - if (feedback.grade && feedback.grade.grader > 0) { - this.userProvider.getProfile(feedback.grade.grader, this.courseId).then((profile) => { - this.grader = profile; - }).catch(() => { - // Ignore errors. - }); - } else { - delete this.grader; - } - - // Check if the grade uses advanced grading. - if (feedback.gradefordisplay) { - const position = feedback.gradefordisplay.indexOf('class="advancedgrade"'); - if (position > -1) { - this.feedback.advancedgrade = true; - } - } - - // Do not override already loaded grade. - if (feedback.grade && feedback.grade.grade && !this.grade.grade) { - const parsedGrade = parseFloat(feedback.grade.grade); - this.grade.grade = parsedGrade >= 0 ? parsedGrade : null; - this.grade.gradebookGrade = this.utils.formatFloat(this.grade.grade); - this.originalGrades.grade = this.grade.grade; - } - } else { - // If no feedback, always show Submission. - this.selectedTab = 0; - this.tabs.selectTab(0); - } - - this.grade.gradingStatus = this.lastAttempt && this.lastAttempt.gradingstatus; - - // Get the grade for the assign. - return this.courseProvider.getModuleBasicGradeInfo(this.moduleId).then((gradeInfo) => { - this.gradeInfo = gradeInfo; - - if (!gradeInfo) { - return; - } - - // Make sure outcomes is an array. - gradeInfo.outcomes = gradeInfo.outcomes || []; - - // Treat the grade info. - return this.treatGradeInfo(); - }).then(() => { - if (!this.isGrading) { - return; - } - - const isManual = this.assign.attemptreopenmethod == AddonModAssignProvider.ATTEMPT_REOPEN_METHOD_MANUAL, - isUnlimited = this.assign.maxattempts == AddonModAssignProvider.UNLIMITED_ATTEMPTS, - isLessThanMaxAttempts = this.userSubmission && (this.userSubmission.attemptnumber < (this.assign.maxattempts - 1)); - - this.allowAddAttempt = isManual && (!this.userSubmission || isUnlimited || isLessThanMaxAttempts); - - if (this.assign.teamsubmission) { - this.grade.applyToAll = true; - this.originalGrades.applyToAll = true; - } - if (this.assign.markingworkflow && this.grade.gradingStatus) { - this.workflowStatusTranslationId = - this.assignProvider.getSubmissionGradingStatusTranslationId(this.grade.gradingStatus); - } - - if (this.isGrading && this.lastAttempt.gradingstatus == 'graded' && !this.assign.markingworkflow) { - if (this.feedback.gradeddate < this.lastAttempt.submission.timemodified) { - this.lastAttempt.gradingstatus = AddonModAssignProvider.GRADED_FOLLOWUP_SUBMIT; - - // Get grading text and color. - this.gradingStatusTranslationId = this.assignProvider.getSubmissionGradingStatusTranslationId( - this.lastAttempt.gradingstatus); - this.gradingColor = this.assignProvider.getSubmissionGradingStatusColor(this.lastAttempt.gradingstatus); - - } - } - - if (!this.feedback || !this.feedback.plugins) { - // Feedback plugins not present, we have to use assign configs to detect the plugins used. - this.feedback = this.assignHelper.createEmptyFeedback(); - this.feedback.plugins = this.assignHelper.getPluginsEnabled(this.assign, 'assignfeedback'); - } - - // Check if there's any offline data for this submission. - if (this.canSaveGrades) { - // Submission grades aren't identified by attempt number so it can retrieve the feedback for a previous attempt. - // The app will not treat that as an special case. - return this.assignOfflineProvider.getSubmissionGrade(this.assign.id, this.submitId).catch(() => { - // Grade not found. - }).then((data) => { - this.hasOfflineGrade = false; - - // Load offline grades. - if (data && (!feedback || !feedback.gradeddate || feedback.gradeddate < data.timemodified)) { - // If grade has been modified from gradebook, do not use offline. - if (this.grade.modified < data.timemodified) { - this.hasOfflineGrade = true; - this.grade.grade = !this.grade.scale ? this.utils.formatFloat(data.grade) : data.grade; - this.gradingStatusTranslationId = 'addon.mod_assign.gradenotsynced'; - this.gradingColor = ''; - this.originalGrades.grade = this.grade.grade; - } - - this.grade.applyToAll = data.applytoall; - this.grade.addAttempt = data.addattempt; - this.originalGrades.applyToAll = this.grade.applyToAll; - this.originalGrades.addAttempt = this.grade.addAttempt; - - if (data.outcomes && Object.keys(data.outcomes).length) { - this.gradeInfo.outcomes.forEach((outcome) => { - if (typeof data.outcomes[outcome.itemNumber] != 'undefined') { - // If outcome has been modified from gradebook, do not use offline. - if (outcome.modified < data.timemodified) { - outcome.selectedId = data.outcomes[outcome.itemNumber]; - this.originalGrades.outcomes[outcome.id] = outcome.selectedId; - } - } - }); - } - } - }); - } else { - // User cannot save grades in the app. Load the URL to grade it in browser. - return this.courseProvider.getModule(this.moduleId, this.courseId, undefined, true).then((mod) => { - this.gradeUrl = mod.url + '&action=grader&userid=' + this.submitId; - }); - } - }); - } - - /** - * Get the submission plugins that don't support editing. - * - * @return Promise resolved when done. - */ - protected async loadUnsupportedPlugins(): Promise { - this.unsupportedEditPlugins = await this.assignProvider.getUnsupportedEditPlugins(this.userSubmission.plugins); - } - - /** - * Set the submission status name and class. - * - * @param status Submission status. - */ - protected setStatusNameAndClass(status: any): void { - if (this.hasOffline || this.submittedOffline) { - // Offline data. - this.statusTranslated = this.translate.instant('core.notsent'); - this.statusColor = 'warning'; - } else if (!this.assign.teamsubmission) { - - // Single submission. - if (this.userSubmission && this.userSubmission.status != this.statusNew) { - this.statusTranslated = this.translate.instant('addon.mod_assign.submissionstatus_' + this.userSubmission.status); - this.statusColor = this.assignProvider.getSubmissionStatusColor(this.userSubmission.status); - } else { - if (!status.lastattempt.submissionsenabled) { - this.statusTranslated = this.translate.instant('addon.mod_assign.noonlinesubmissions'); - this.statusColor = this.assignProvider.getSubmissionStatusColor('noonlinesubmissions'); - } else { - this.statusTranslated = this.translate.instant('addon.mod_assign.noattempt'); - this.statusColor = this.assignProvider.getSubmissionStatusColor('noattempt'); - } - } - } else { - - // Team submission. - if (!status.lastattempt.submissiongroup && this.assign.preventsubmissionnotingroup) { - this.statusTranslated = this.translate.instant('addon.mod_assign.nosubmission'); - this.statusColor = this.assignProvider.getSubmissionStatusColor('nosubmission'); - } else if (this.userSubmission && this.userSubmission.status != this.statusNew) { - this.statusTranslated = this.translate.instant('addon.mod_assign.submissionstatus_' + this.userSubmission.status); - this.statusColor = this.assignProvider.getSubmissionStatusColor(this.userSubmission.status); - } else { - if (!status.lastattempt.submissionsenabled) { - this.statusTranslated = this.translate.instant('addon.mod_assign.noonlinesubmissions'); - this.statusColor = this.assignProvider.getSubmissionStatusColor('noonlinesubmissions'); - } else { - this.statusTranslated = this.translate.instant('addon.mod_assign.nosubmission'); - this.statusColor = this.assignProvider.getSubmissionStatusColor('nosubmission'); - } - } - } - } - - /** - * Show advanced grade. - */ - showAdvancedGrade(): void { - if (this.feedback && this.feedback.advancedgrade) { - this.textUtils.viewText(this.translate.instant('core.grades.grade'), this.feedback.gradefordisplay, { - component: AddonModAssignProvider.COMPONENT, - componentId: this.moduleId, - }); - } - } - - /** - * Submit for grading. - * - * @param acceptStatement Whether the statement has been accepted. - */ - submitForGrading(acceptStatement: boolean): void { - if (this.assign.requiresubmissionstatement && !acceptStatement) { - this.domUtils.showErrorModal('addon.mod_assign.acceptsubmissionstatement', true); - - return; - } - - // Ask for confirmation. @todo plugin precheck_submission - this.domUtils.showConfirm(this.translate.instant('addon.mod_assign.confirmsubmission')).then(() => { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.assignProvider.submitForGrading(this.assign.id, this.courseId, acceptStatement, this.userSubmission.timemodified, - this.hasOffline).then(() => { - - // Submitted, trigger event. - this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, { - assignmentId: this.assign.id, - submissionId: this.userSubmission.id, - userId: this.currentUserId - }, this.siteId); - }).finally(() => { - modal.dismiss(); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }); - } - - /** - * Submit a grade and feedback. - * - * @return Promise resolved when done. - */ - submitGrade(): Promise { - // Check if there's something to be saved. - return this.hasDataToSave(true).then((modified) => { - if (!modified) { - return; - } - - const attemptNumber = this.userSubmission ? this.userSubmission.attemptnumber : -1, - outcomes = {}, - // Scale "no grade" uses -1 instead of 0. - grade = this.grade.scale && this.grade.grade == 0 ? -1 : this.utils.unformatFloat(this.grade.grade, true); - - if (grade === false) { - // Grade is invalid. - return Promise.reject(this.translate.instant('core.grades.badgrade')); - } - - const modal = this.domUtils.showModalLoading('core.sending', true); - let pluginPromise; - - this.gradeInfo.outcomes.forEach((outcome) => { - if (outcome.itemNumber) { - outcomes[outcome.itemNumber] = outcome.selectedId; - } - }); - - if (this.feedback && this.feedback.plugins) { - pluginPromise = this.assignHelper.prepareFeedbackPluginData(this.assign.id, this.submitId, this.feedback); - } else { - pluginPromise = Promise.resolve({}); - } - - return pluginPromise.then((pluginData) => { - // We have all the data, now send it. - return this.assignProvider.submitGradingForm(this.assign.id, this.submitId, this.courseId, grade, attemptNumber, - this.grade.addAttempt, this.grade.gradingStatus, this.grade.applyToAll, outcomes, pluginData).then(() => { - - // Data sent, discard draft. - return this.discardDrafts(); - }).finally(() => { - // Invalidate and refresh data. - this.invalidateAndRefresh(true); - - this.eventsProvider.trigger(AddonModAssignProvider.GRADED_EVENT, { - assignmentId: this.assign.id, - submissionId: this.submitId, - userId: this.currentUserId - }, this.siteId); - }); - }).finally(() => { - // Select submission view. - this.tabs.selectTab(0); - modal.dismiss(); - }); - }); - } - - /** - * Treat the grade info. - * - * @return Promise resolved when done. - */ - protected treatGradeInfo(): Promise { - // Check if grading method is simple or not. - if (this.gradeInfo.advancedgrading && this.gradeInfo.advancedgrading[0] && - typeof this.gradeInfo.advancedgrading[0].method != 'undefined') { - this.grade.method = this.gradeInfo.advancedgrading[0].method || 'simple'; - } else { - this.grade.method = 'simple'; - } - - this.isGrading = true; - this.canSaveGrades = this.grade.method == 'simple'; // Grades can be saved if simple grading. - - if (this.gradeInfo.scale) { - this.grade.scale = this.utils.makeMenuFromList(this.gradeInfo.scale, this.translate.instant('core.nograde')); - } else { - // Format the grade. - this.grade.grade = this.utils.formatFloat(this.grade.grade); - this.originalGrades.grade = this.grade.grade; - - // Get current language to format grade input field. - this.langProvider.getCurrentLanguage().then((lang) => { - this.grade.lang = lang; - }); - } - - // Treat outcomes. - if (this.assignProvider.isOutcomesEditEnabled()) { - this.gradeInfo.outcomes.forEach((outcome) => { - if (outcome.scale) { - outcome.options = - this.utils.makeMenuFromList(outcome.scale, this.translate.instant('core.grades.nooutcome')); - } - outcome.selectedId = 0; - this.originalGrades.outcomes[outcome.id] = outcome.selectedId; - }); - } - - // Get grade items. - return this.gradesHelper.getGradeModuleItems(this.courseId, this.moduleId, this.submitId).then((grades) => { - const outcomes = []; - - grades.forEach((grade) => { - if (!grade.outcomeid && !grade.scaleid) { - - // Not using outcomes or scale, get the numeric grade. - if (this.grade.scale) { - this.grade.gradebookGrade = this.utils.formatFloat(this.gradesHelper.getGradeValueFromLabel( - this.grade.scale, grade.gradeformatted)); - } else { - const parsedGrade = parseFloat(grade.gradeformatted); - this.grade.gradebookGrade = parsedGrade || parsedGrade == 0 ? this.utils.formatFloat(parsedGrade) : null; - } - - this.grade.disabled = grade.gradeislocked || grade.gradeisoverridden; - this.grade.modified = grade.gradedategraded; - } else if (grade.outcomeid) { - - // Only show outcomes with info on it, outcomeid could be null if outcomes are disabled on site. - this.gradeInfo.outcomes.forEach((outcome) => { - if (outcome.id == grade.outcomeid) { - outcome.selected = grade.gradeformatted; - outcome.modified = grade.gradedategraded; - if (outcome.options) { - outcome.selectedId = this.gradesHelper.getGradeValueFromLabel(outcome.options, outcome.selected); - this.originalGrades.outcomes[outcome.id] = outcome.selectedId; - outcome.itemNumber = grade.itemnumber; - } - outcomes.push(outcome); - } - }); - this.gradeInfo.disabled = grade.gradeislocked || grade.gradeisoverridden; - } - }); - - this.gradeInfo.outcomes = outcomes; - }); - } - - /** - * Treat the last attempt. - * - * @param response Response of get submission status. - * @param promises List where to add the promises. - */ - protected treatLastAttempt(response: any, promises: any[]): void { - if (!response.lastattempt) { - return; - } - - const submissionStatementMissing = this.assign.requiresubmissionstatement && - typeof this.assign.submissionstatement == 'undefined'; - - this.canSubmit = !this.isSubmittedForGrading && !this.submittedOffline && (response.lastattempt.cansubmit || - (this.hasOffline && this.assignProvider.canSubmitOffline(this.assign, response))); - this.canEdit = !this.isSubmittedForGrading && response.lastattempt.canedit && - (!this.submittedOffline || !this.assign.submissiondrafts); - - // Get submission statement if needed. - if (this.assign.requiresubmissionstatement && this.assign.submissiondrafts && this.submitId == this.currentUserId) { - this.submissionStatement = this.assign.submissionstatement; - this.submitModel.submissionStatement = false; - } else { - this.submissionStatement = undefined; - this.submitModel.submissionStatement = true; // No submission statement, so it's accepted. - } - - // Show error if submission statement should be shown but it couldn't be retrieved. - this.showErrorStatementEdit = submissionStatementMissing && !this.assign.submissiondrafts && - this.submitId == this.currentUserId; - this.showErrorStatementSubmit = submissionStatementMissing && !!this.assign.submissiondrafts; - - this.userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, response.lastattempt); - - if (this.assign.attemptreopenmethod != this.attemptReopenMethodNone && this.userSubmission) { - this.currentAttempt = this.userSubmission.attemptnumber + 1; - } - - this.setStatusNameAndClass(response); - - if (this.assign.teamsubmission) { - if (response.lastattempt.submissiongroup) { - // Get the name of the group. - promises.push(this.groupsProvider.getActivityAllowedGroups(this.assign.cmid).then((result) => { - result.groups.forEach((group) => { - if (group.id == response.lastattempt.submissiongroup) { - this.lastAttempt.submissiongroupname = group.name; - } - }); - })); - } - - // Get the members that need to submit. - if (this.userSubmission && this.userSubmission.status != this.statusNew) { - response.lastattempt.submissiongroupmemberswhoneedtosubmit.forEach((member) => { - if (this.blindMarking) { - // Users not blinded! (Moodle < 3.1.1, 3.2). - promises.push(this.assignProvider.getAssignmentUserMappings(this.assign.id, member, { - cmId: this.moduleId, - }).then((blindId) => { - this.membersToSubmit.push(blindId); - })); - } else { - promises.push(this.userProvider.getProfile(member, this.courseId).then((profile) => { - this.membersToSubmit.push(profile); - })); - } - }); - } - } - - // Get grading text and color. - this.gradingStatusTranslationId = this.assignProvider.getSubmissionGradingStatusTranslationId( - response.lastattempt.gradingstatus); - this.gradingColor = this.assignProvider.getSubmissionGradingStatusColor(response.lastattempt.gradingstatus); - - // Get the submission plugins. - if (this.userSubmission) { - if (!this.assign.teamsubmission || !response.lastattempt.submissiongroup || !this.assign.preventsubmissionnotingroup) { - if (this.previousAttempt && this.previousAttempt.submission.plugins && - this.userSubmission.status == this.statusReopened) { - // Get latest attempt if avalaible. - this.submissionPlugins = this.previousAttempt.submission.plugins; - } else { - this.submissionPlugins = this.userSubmission.plugins; - } - } - } - } - - /** - * Block or unblock the automatic sync of the user grade. - * - * @param block Whether to block or unblock. - */ - protected setGradeSyncBlocked(block?: boolean): void { - if (this.isDestroyed || !this.assign || !this.isGrading) { - return; - } - - const syncId = AddonModAssignSync.instance.getGradeSyncId(this.assign.id, this.submitId); - - if (block) { - this.syncProvider.blockOperation(AddonModAssignProvider.COMPONENT, syncId); - } else { - this.syncProvider.unblockOperation(AddonModAssignProvider.COMPONENT, syncId); - } - } - - /** - * A certain tab has been selected, either manually or automatically. - * - * @param tab The tab that was selected. - */ - tabSelected(tab: CoreTabComponent): void { - // Block sync when selecting grade tab, unblock when leaving it. - this.setGradeSyncBlocked(this.tabs.getIndex(tab) === 1); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.setGradeSyncBlocked(false); - this.isDestroyed = true; - this.syncObserver && this.syncObserver.off(); - } -} - -/** - * Submission attempt with some calculated data. - */ -type AddonModAssignSubmissionAttemptFormatted = AddonModAssignSubmissionAttempt & { - submissiongroupname?: string; // Calculated in the app. Group name the attempt belongs to. -}; - -/** - * Feedback of an assign submission with some calculated data. - */ -type AddonModAssignSubmissionFeedbackFormatted = AddonModAssignSubmissionFeedback & { - advancedgrade?: boolean; // Calculated in the app. Whether it uses advanced grading. -}; diff --git a/src/addon/mod/assign/feedback/comments/comments.module.ts b/src/addon/mod/assign/feedback/comments/comments.module.ts deleted file mode 100644 index cb23c6983..000000000 --- a/src/addon/mod/assign/feedback/comments/comments.module.ts +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModAssignFeedbackCommentsHandler } from './providers/handler'; -import { AddonModAssignFeedbackCommentsComponent } from './component/comments'; -import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModAssignFeedbackCommentsComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - ], - providers: [ - AddonModAssignFeedbackCommentsHandler - ], - exports: [ - AddonModAssignFeedbackCommentsComponent - ], - entryComponents: [ - AddonModAssignFeedbackCommentsComponent - ] -}) -export class AddonModAssignFeedbackCommentsModule { - constructor(feedbackDelegate: AddonModAssignFeedbackDelegate, handler: AddonModAssignFeedbackCommentsHandler) { - feedbackDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html b/src/addon/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html deleted file mode 100644 index 75892386b..000000000 --- a/src/addon/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html +++ /dev/null @@ -1,24 +0,0 @@ - - -

{{ plugin.name }}

-

- -

-
-
- -
- - - {{ 'core.notsent' | translate }} - -
-
- - - - - - diff --git a/src/addon/mod/assign/feedback/comments/component/comments.ts b/src/addon/mod/assign/feedback/comments/component/comments.ts deleted file mode 100644 index 1207e8f33..000000000 --- a/src/addon/mod/assign/feedback/comments/component/comments.ts +++ /dev/null @@ -1,151 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, ElementRef } from '@angular/core'; -import { ModalController } from 'ionic-angular'; -import { FormBuilder, FormControl } from '@angular/forms'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { AddonModAssignProvider } from '../../../providers/assign'; -import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline'; -import { AddonModAssignFeedbackDelegate } from '../../../providers/feedback-delegate'; -import { AddonModAssignFeedbackPluginComponentBase } from '../../../classes/feedback-plugin-component'; -import { AddonModAssignFeedbackCommentsHandler } from '../providers/handler'; - -/** - * Component to render a comments feedback plugin. - */ -@Component({ - selector: 'addon-mod-assign-feedback-comments', - templateUrl: 'addon-mod-assign-feedback-comments.html' -}) -export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedbackPluginComponentBase implements OnInit { - - control: FormControl; - component = AddonModAssignProvider.COMPONENT; - text: string; - isSent: boolean; - loaded: boolean; - - protected element: HTMLElement; - - constructor(modalCtrl: ModalController, element: ElementRef, protected domUtils: CoreDomUtilsProvider, - protected textUtils: CoreTextUtilsProvider, protected assignOfflineProvider: AddonModAssignOfflineProvider, - protected assignProvider: AddonModAssignProvider, protected fb: FormBuilder, - protected feedbackDelegate: AddonModAssignFeedbackDelegate) { - super(modalCtrl); - - this.element = element.nativeElement; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.getText().then((text) => { - - this.text = text; - - if (!this.canEdit && !this.edit) { - // User cannot edit the comment. Show it full when clicked. - this.element.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - if (this.text) { - // Open a new state with the text. - this.textUtils.viewText(this.plugin.name, this.text, { - component: this.component, - componentId: this.assign.cmid, - filter: true, - contextLevel: 'module', - instanceId: this.assign.cmid, - courseId: this.assign.course, - }); - } - }); - } else if (this.edit) { - this.control = this.fb.control(text); - } - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Edit the comment. - */ - editComment(): void { - this.editFeedback().then((inputData) => { - const text = AddonModAssignFeedbackCommentsHandler.getTextFromInputData(this.textUtils, this.plugin, inputData); - - // Update the text and save it as draft. - this.isSent = false; - this.text = this.replacePluginfileUrls(text); - this.feedbackDelegate.saveFeedbackDraft(this.assign.id, this.userId, this.plugin, { - text: text, - format: 1 - }); - }).catch(() => { - // User cancelled, nothing to do. - }); - } - - /** - * Get the text for the plugin. - * - * @return Promise resolved with the text. - */ - protected getText(): Promise { - // Check if the user already modified the comment. - return this.feedbackDelegate.getPluginDraftData(this.assign.id, this.userId, this.plugin).then((draft) => { - if (draft) { - this.isSent = false; - - return this.replacePluginfileUrls(draft.text); - } else { - // There is no draft saved. Check if we have anything offline. - return this.assignOfflineProvider.getSubmissionGrade(this.assign.id, this.userId).catch(() => { - // No offline data found. - }).then((offlineData) => { - if (offlineData && offlineData.plugindata && offlineData.plugindata.assignfeedbackcomments_editor) { - // Save offline as draft. - this.isSent = false; - this.feedbackDelegate.saveFeedbackDraft(this.assign.id, this.userId, this.plugin, - offlineData.plugindata.assignfeedbackcomments_editor); - - return this.replacePluginfileUrls(offlineData.plugindata.assignfeedbackcomments_editor.text); - } - - // No offline data found, return online text. - this.isSent = true; - - return this.assignProvider.getSubmissionPluginText(this.plugin); - }); - } - }); - } - - /** - * Replace @@PLUGINFILE@@ wildcards with the real URL of embedded files. - * - * @param Text to treat. - * @return Treated text. - */ - replacePluginfileUrls(text: string): string { - const files = this.plugin.fileareas && this.plugin.fileareas[0] && this.plugin.fileareas[0].files; - - return this.textUtils.replacePluginfileUrls(text, files || []); - } -} diff --git a/src/addon/mod/assign/feedback/comments/lang/en.json b/src/addon/mod/assign/feedback/comments/lang/en.json deleted file mode 100644 index 637363859..000000000 --- a/src/addon/mod/assign/feedback/comments/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Feedback comments" -} \ No newline at end of file diff --git a/src/addon/mod/assign/feedback/comments/providers/handler.ts b/src/addon/mod/assign/feedback/comments/providers/handler.ts deleted file mode 100644 index 6898521e6..000000000 --- a/src/addon/mod/assign/feedback/comments/providers/handler.ts +++ /dev/null @@ -1,232 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../../providers/assign'; -import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline'; -import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate'; -import { AddonModAssignFeedbackCommentsComponent } from '../component/comments'; - -/** - * Handler for comments feedback plugin. - */ -@Injectable() -export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeedbackHandler { - name = 'AddonModAssignFeedbackCommentsHandler'; - type = 'comments'; - - protected drafts = {}; // Store the data in this service so it isn't lost if the user performs a PTR in the page. - - constructor(private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider, - private assignProvider: AddonModAssignProvider, private assignOfflineProvider: AddonModAssignOfflineProvider) { } - - /** - * Discard the draft data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - discardDraft(assignId: number, userId: number, siteId?: string): void | Promise { - const id = this.getDraftId(assignId, userId, siteId); - if (typeof this.drafts[id] != 'undefined') { - delete this.drafts[id]; - } - } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: AddonModAssignPlugin): any | Promise { - return AddonModAssignFeedbackCommentsComponent; - } - - /** - * Return the draft saved data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return Data (or promise resolved with the data). - */ - getDraft(assignId: number, userId: number, siteId?: string): any | Promise { - const id = this.getDraftId(assignId, userId, siteId); - - if (typeof this.drafts[id] != 'undefined') { - return this.drafts[id]; - } - } - - /** - * Get a draft ID. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return Draft ID. - */ - protected getDraftId(assignId: number, userId: number, siteId?: string): string { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return siteId + '#' + assignId + '#' + userId; - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise { - return this.assignProvider.getSubmissionPluginAttachments(plugin); - } - - /** - * Get the text to submit. - * - * @param textUtils Text utils instance. - * @param plugin Plugin. - * @param inputData Data entered in the feedback edit form. - * @return Text to submit. - */ - static getTextFromInputData(textUtils: CoreTextUtilsProvider, plugin: any, inputData: any): string { - const files = plugin.fileareas && plugin.fileareas[0] ? plugin.fileareas[0].files : []; - let text = inputData.assignfeedbackcomments_editor; - - // The input data can have a string or an object with text and format. Get the text. - if (text && text.text) { - text = text.text; - } - - return textUtils.restorePluginfileUrls(text, files); - } - - /** - * Check if the feedback data has changed for this plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the feedback. - * @param userId User ID of the submission. - * @return Boolean (or promise resolved with boolean): whether the data has changed. - */ - hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, userId: number): boolean | Promise { - - // Get it from plugin or offline. - return this.assignOfflineProvider.getSubmissionGrade(assign.id, userId).catch(() => { - // No offline data found. - }).then((data) => { - if (data && data.plugindata && data.plugindata.assignfeedbackcomments_editor) { - return data.plugindata.assignfeedbackcomments_editor.text; - } - - // No offline data found, get text from plugin. - return this.assignProvider.getSubmissionPluginText(plugin); - }).then((initialText) => { - const newText = AddonModAssignFeedbackCommentsHandler.getTextFromInputData(this.textUtils, plugin, inputData); - - if (typeof newText == 'undefined') { - return false; - } - - // Check if text has changed. - return initialText != newText; - }); - } - - /** - * Check whether the plugin has draft data stored. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return Boolean or promise resolved with boolean: whether the plugin has draft data. - */ - hasDraftData(assignId: number, userId: number, siteId?: string): boolean | Promise { - const draft = this.getDraft(assignId, userId, siteId); - - return !!draft; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - // In here we should check if comments is not disabled in site. - // But due to this is not a common comments place and it can be disabled separately into Moodle (disabling the plugin). - // We are leaving it always enabled. It's also a teacher's feature. - return true; - } - - /** - * Prepare and add to pluginData the data to send to the server based on the draft data saved. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareFeedbackData(assignId: number, userId: number, plugin: AddonModAssignPlugin, pluginData: any, - siteId?: string): void | Promise { - - const draft = this.getDraft(assignId, userId, siteId); - - if (draft) { - // Add some HTML to the text if needed. - draft.text = this.textUtils.formatHtmlLines(draft.text); - - pluginData.assignfeedbackcomments_editor = draft; - } - } - - /** - * Save draft data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param data The data to save. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - saveDraft(assignId: number, userId: number, plugin: AddonModAssignPlugin, data: any, siteId?: string) - : void | Promise { - - if (data) { - this.drafts[this.getDraftId(assignId, userId, siteId)] = data; - } - } -} diff --git a/src/addon/mod/assign/feedback/editpdf/component/addon-mod-assign-feedback-editpdf.html b/src/addon/mod/assign/feedback/editpdf/component/addon-mod-assign-feedback-editpdf.html deleted file mode 100644 index fc9633296..000000000 --- a/src/addon/mod/assign/feedback/editpdf/component/addon-mod-assign-feedback-editpdf.html +++ /dev/null @@ -1,7 +0,0 @@ - - -

{{plugin.name}}

-
- -
-
diff --git a/src/addon/mod/assign/feedback/editpdf/component/editpdf.ts b/src/addon/mod/assign/feedback/editpdf/component/editpdf.ts deleted file mode 100644 index 38ef466aa..000000000 --- a/src/addon/mod/assign/feedback/editpdf/component/editpdf.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { ModalController } from 'ionic-angular'; -import { AddonModAssignProvider } from '../../../providers/assign'; -import { AddonModAssignFeedbackPluginComponentBase } from '../../../classes/feedback-plugin-component'; - -/** - * Component to render a edit pdf feedback plugin. - */ -@Component({ - selector: 'addon-mod-assign-feedback-edit-pdf', - templateUrl: 'addon-mod-assign-feedback-editpdf.html' -}) -export class AddonModAssignFeedbackEditPdfComponent extends AddonModAssignFeedbackPluginComponentBase implements OnInit { - - component = AddonModAssignProvider.COMPONENT; - files: any[]; - - constructor(modalCtrl: ModalController, protected assignProvider: AddonModAssignProvider) { - super(modalCtrl); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (this.plugin) { - this.files = this.assignProvider.getSubmissionPluginAttachments(this.plugin); - } - } -} diff --git a/src/addon/mod/assign/feedback/editpdf/editpdf.module.ts b/src/addon/mod/assign/feedback/editpdf/editpdf.module.ts deleted file mode 100644 index c003cd837..000000000 --- a/src/addon/mod/assign/feedback/editpdf/editpdf.module.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModAssignFeedbackEditPdfHandler } from './providers/handler'; -import { AddonModAssignFeedbackEditPdfComponent } from './component/editpdf'; -import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModAssignFeedbackEditPdfComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModAssignFeedbackEditPdfHandler - ], - exports: [ - AddonModAssignFeedbackEditPdfComponent - ], - entryComponents: [ - AddonModAssignFeedbackEditPdfComponent - ] -}) -export class AddonModAssignFeedbackEditPdfModule { - constructor(feedbackDelegate: AddonModAssignFeedbackDelegate, handler: AddonModAssignFeedbackEditPdfHandler) { - feedbackDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/assign/feedback/editpdf/lang/en.json b/src/addon/mod/assign/feedback/editpdf/lang/en.json deleted file mode 100644 index a98c70fd9..000000000 --- a/src/addon/mod/assign/feedback/editpdf/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Annotate PDF" -} \ No newline at end of file diff --git a/src/addon/mod/assign/feedback/editpdf/providers/handler.ts b/src/addon/mod/assign/feedback/editpdf/providers/handler.ts deleted file mode 100644 index c04319db8..000000000 --- a/src/addon/mod/assign/feedback/editpdf/providers/handler.ts +++ /dev/null @@ -1,68 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../../providers/assign'; -import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate'; -import { AddonModAssignFeedbackEditPdfComponent } from '../component/editpdf'; - -/** - * Handler for edit pdf feedback plugin. - */ -@Injectable() -export class AddonModAssignFeedbackEditPdfHandler implements AddonModAssignFeedbackHandler { - name = 'AddonModAssignFeedbackEditPdfHandler'; - type = 'editpdf'; - - constructor(private assignProvider: AddonModAssignProvider) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: AddonModAssignPlugin): any | Promise { - return AddonModAssignFeedbackEditPdfComponent; - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise { - return this.assignProvider.getSubmissionPluginAttachments(plugin); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/assign/feedback/feedback.module.ts b/src/addon/mod/assign/feedback/feedback.module.ts deleted file mode 100644 index fe4a39ab8..000000000 --- a/src/addon/mod/assign/feedback/feedback.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModAssignFeedbackCommentsModule } from './comments/comments.module'; -import { AddonModAssignFeedbackEditPdfModule } from './editpdf/editpdf.module'; -import { AddonModAssignFeedbackFileModule } from './file/file.module'; - -@NgModule({ - declarations: [], - imports: [ - AddonModAssignFeedbackCommentsModule, - AddonModAssignFeedbackEditPdfModule, - AddonModAssignFeedbackFileModule - ], - providers: [ - ], - exports: [] -}) -export class AddonModAssignFeedbackModule { } diff --git a/src/addon/mod/assign/feedback/file/component/addon-mod-assign-feedback-file.html b/src/addon/mod/assign/feedback/file/component/addon-mod-assign-feedback-file.html deleted file mode 100644 index fc9633296..000000000 --- a/src/addon/mod/assign/feedback/file/component/addon-mod-assign-feedback-file.html +++ /dev/null @@ -1,7 +0,0 @@ - - -

{{plugin.name}}

-
- -
-
diff --git a/src/addon/mod/assign/feedback/file/component/file.ts b/src/addon/mod/assign/feedback/file/component/file.ts deleted file mode 100644 index e9e036194..000000000 --- a/src/addon/mod/assign/feedback/file/component/file.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { ModalController } from 'ionic-angular'; -import { AddonModAssignProvider } from '../../../providers/assign'; -import { AddonModAssignFeedbackPluginComponentBase } from '../../../classes/feedback-plugin-component'; - -/** - * Component to render a file feedback plugin. - */ -@Component({ - selector: 'addon-mod-assign-feedback-file', - templateUrl: 'addon-mod-assign-feedback-file.html' -}) -export class AddonModAssignFeedbackFileComponent extends AddonModAssignFeedbackPluginComponentBase implements OnInit { - - component = AddonModAssignProvider.COMPONENT; - files: any[]; - - constructor(modalCtrl: ModalController, protected assignProvider: AddonModAssignProvider) { - super(modalCtrl); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (this.plugin) { - this.files = this.assignProvider.getSubmissionPluginAttachments(this.plugin); - } - } -} diff --git a/src/addon/mod/assign/feedback/file/file.module.ts b/src/addon/mod/assign/feedback/file/file.module.ts deleted file mode 100644 index 1024e5994..000000000 --- a/src/addon/mod/assign/feedback/file/file.module.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModAssignFeedbackFileHandler } from './providers/handler'; -import { AddonModAssignFeedbackFileComponent } from './component/file'; -import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModAssignFeedbackFileComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModAssignFeedbackFileHandler - ], - exports: [ - AddonModAssignFeedbackFileComponent - ], - entryComponents: [ - AddonModAssignFeedbackFileComponent - ] -}) -export class AddonModAssignFeedbackFileModule { - constructor(feedbackDelegate: AddonModAssignFeedbackDelegate, handler: AddonModAssignFeedbackFileHandler) { - feedbackDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/assign/feedback/file/lang/en.json b/src/addon/mod/assign/feedback/file/lang/en.json deleted file mode 100644 index e5e6aeb98..000000000 --- a/src/addon/mod/assign/feedback/file/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "File feedback" -} \ No newline at end of file diff --git a/src/addon/mod/assign/feedback/file/providers/handler.ts b/src/addon/mod/assign/feedback/file/providers/handler.ts deleted file mode 100644 index 89a5efdd7..000000000 --- a/src/addon/mod/assign/feedback/file/providers/handler.ts +++ /dev/null @@ -1,68 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../../providers/assign'; -import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate'; -import { AddonModAssignFeedbackFileComponent } from '../component/file'; - -/** - * Handler for file feedback plugin. - */ -@Injectable() -export class AddonModAssignFeedbackFileHandler implements AddonModAssignFeedbackHandler { - name = 'AddonModAssignFeedbackFileHandler'; - type = 'file'; - - constructor(private assignProvider: AddonModAssignProvider) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: AddonModAssignPlugin): any | Promise { - return AddonModAssignFeedbackFileComponent; - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise { - return this.assignProvider.getSubmissionPluginAttachments(plugin); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/assign/lang/en.json b/src/addon/mod/assign/lang/en.json deleted file mode 100644 index 5311cdf8a..000000000 --- a/src/addon/mod/assign/lang/en.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "acceptsubmissionstatement": "Please accept the submission statement.", - "addattempt": "Allow another attempt", - "addnewattempt": "Add a new attempt", - "addnewattemptfromprevious": "Add a new attempt based on previous submission", - "addsubmission": "Add submission", - "allowsubmissionsfromdate": "Allow submissions from", - "allowsubmissionsfromdatesummary": "This assignment will accept submissions from {{$a}}", - "allowsubmissionsanddescriptionfromdatesummary": "The assignment details and submission form will be available from {{$a}}", - "applytoteam": "Apply grades and feedback to entire group", - "assignmentisdue": "Assignment is due", - "attemptnumber": "Attempt number", - "attemptreopenmethod": "Attempts reopened", - "attemptreopenmethod_manual": "Manually", - "attemptreopenmethod_untilpass": "Automatically until pass", - "attemptsettings": "Attempt settings", - "cannotgradefromapp": "Certain grading methods are not yet supported by the app and cannot be modified.", - "cannoteditduetostatementsubmission": "You can't add or edit a submission in the app because the submission statement could not be retrieved from the site.", - "cannotsubmitduetostatementsubmission": "You can't make a submission in the app because the submission statement could not be retrieved from the site.", - "confirmsubmission": "Are you sure you want to submit your work for grading? You will not be able to make any more changes.", - "currentgrade": "Current grade in gradebook", - "cutoffdate": "Cut-off date", - "currentattempt": "This is attempt {{$a}}.", - "currentattemptof": "This is attempt {{$a.attemptnumber}} ( {{$a.maxattempts}} attempts allowed ).", - "defaultteam": "Default group", - "duedate": "Due date", - "duedateno": "No due date", - "duedatereached": "The due date for this assignment has now passed", - "editingstatus": "Editing status", - "editsubmission": "Edit submission", - "erroreditpluginsnotsupported": "You can't add or edit a submission in the app because certain plugins are not yet supported for editing.", - "errorshowinginformation": "Submission information cannot be displayed.", - "extensionduedate": "Extension due date", - "feedbacknotsupported": "This feedback is not supported by the app and may not contain all the information.", - "grade": "Grade", - "graded": "Graded", - "gradedby": "Graded by", - "gradedfollowupsubmit": "Graded - follow up submission received", - "gradenotsynced": "Grade not synced", - "gradedon": "Graded on", - "gradelocked": "This grade is locked or overridden in the gradebook.", - "gradeoutof": "Grade out of {{$a}}", - "gradingstatus": "Grading status", - "groupsubmissionsettings": "Group submission settings", - "hiddenuser": "Participant", - "latesubmissions": "Late submissions", - "latesubmissionsaccepted": "Allowed until {{$a}}", - "markingworkflowstate": "Marking workflow state", - "markingworkflowstateinmarking": "In marking", - "markingworkflowstateinreview": "In review", - "markingworkflowstatenotmarked": "Not marked", - "markingworkflowstatereadyforreview": "Marking completed", - "markingworkflowstatereadyforrelease": "Ready for release", - "markingworkflowstatereleased": "Released", - "modulenameplural": "Assignments", - "multipleteams": "Member of more than one group", - "multipleteams_desc": "The assignment requires submission in groups. You are a member of more than one group. To be able to submit you must be a member of only one group. Please contact your teacher to change your group membership.", - "noattempt": "No attempt", - "nomoresubmissionsaccepted": "Only allowed for participants who have been granted an extension", - "noonlinesubmissions": "This assignment does not require you to submit anything online", - "nosubmission": "Nothing has been submitted for this assignment", - "notallparticipantsareshown": "Participants who have not made a submission are not shown.", - "noteam": "Not a member of any group", - "noteam_desc": "This assignment requires submission in groups. You are not a member of any group, so you cannot create a submission. Please contact your teacher to be added to a group.", - "notgraded": "Not graded", - "numberofdraftsubmissions": "Drafts", - "numberofparticipants": "Participants", - "numberofsubmittedassignments": "Submitted", - "numberofsubmissionsneedgrading": "Needs grading", - "numberofteams": "Groups", - "numwords": "{{$a}} words", - "outof": "{{$a.current}} out of {{$a.total}}", - "overdue": "Assignment is overdue by: {{$a}}", - "submissioneditable": "Student can edit this submission", - "submissionnoteditable": "Student cannot edit this submission", - "submissionnotsupported": "This submission is not supported by the app and may not contain all the information.", - "submission": "Submission", - "submissionslocked": "This assignment is not accepting submissions", - "submissionstatus_draft": "Draft (not submitted)", - "submissionstatusheading": "Submission status", - "submissionstatus_marked": "Graded", - "submissionstatus_new": "No submission", - "submissionstatus_reopened": "Reopened", - "submissionstatus_submitted": "Submitted for grading", - "submissionstatus_": "No submission", - "submissionstatus": "Submission status", - "submissionteam": "Group", - "submitassignment_help": "Once this assignment is submitted you will not be able to make any more changes.", - "submitassignment": "Submit assignment", - "submittedearly": "Assignment was submitted {{$a}} early", - "submittedlate": "Assignment was submitted {{$a}} late", - "syncblockedusercomponent": "user grade", - "timemodified": "Last modified", - "timeremaining": "Time remaining", - "ungroupedusers": "The setting 'Require group to make submission' is enabled and some users are either not a member of any group, or are a member of more than one group, so are unable to make submissions.", - "ungroupedusersoptional": "The setting 'Students submit in groups' is enabled and some users are either not a member of any group, or are a member of more than one group. Please be aware that these students will submit as members of the 'Default group'.", - "unlimitedattempts": "Unlimited", - "userwithid": "User with ID {{id}}", - "userswhoneedtosubmit": "Users who need to submit: {{$a}}", - "viewsubmission": "View submission", - "warningsubmissionmodified": "The user submission was modified on the site.", - "warningsubmissiongrademodified": "The submission grade was modified on the site.", - "wordlimit": "Word limit" -} \ No newline at end of file diff --git a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.html b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.html deleted file mode 100644 index 381d0ff43..000000000 --- a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.html +++ /dev/null @@ -1,16 +0,0 @@ - - - {{ plugin.name }} - - - - - - -
- - -
-
diff --git a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.module.ts b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.module.ts deleted file mode 100644 index e0b440478..000000000 --- a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { AddonModAssignEditFeedbackModalPage } from './edit-feedback-modal'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModAssignComponentsModule } from '../../components/components.module'; - -@NgModule({ - declarations: [ - AddonModAssignEditFeedbackModalPage - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - AddonModAssignComponentsModule, - IonicPageModule.forChild(AddonModAssignEditFeedbackModalPage), - TranslateModule.forChild() - ] -}) -export class AddonModAssignEditFeedbackModalPageModule {} diff --git a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts deleted file mode 100644 index 2ebb374f6..000000000 --- a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts +++ /dev/null @@ -1,123 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, ViewController, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate'; -import { - AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../providers/assign'; - -/** - * Modal that allows editing a feedback plugin. - */ -@IonicPage({ segment: 'addon-mod-assign-edit-feedback-modal' }) -@Component({ - selector: 'page-addon-mod-assign-edit-feedback-modal', - templateUrl: 'edit-feedback-modal.html', -}) -export class AddonModAssignEditFeedbackModalPage { - - @Input() assign: AddonModAssignAssign; // The assignment. - @Input() submission: AddonModAssignSubmission; // The submission. - @Input() plugin: AddonModAssignPlugin; // The plugin object. - @Input() userId: number; // The user ID of the submission. - - @ViewChild('editFeedbackForm') formElement: ElementRef; - - protected forceLeave = false; // To allow leaving the page without checking for changes. - - constructor(params: NavParams, - protected viewCtrl: ViewController, - protected domUtils: CoreDomUtilsProvider, - protected translate: TranslateService, - protected feedbackDelegate: AddonModAssignFeedbackDelegate, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider) { - - this.assign = params.get('assign'); - this.submission = params.get('submission'); - this.plugin = params.get('plugin'); - this.userId = params.get('userId'); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave) { - return; - } - - const changed = await this.hasDataChanged(); - if (changed) { - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } - - /** - * Close modal. - * - * @param data Data to return to the page. - */ - closeModal(data: any): void { - this.viewCtrl.dismiss(data); - } - - /** - * Done editing. - * - * @param e Click event. - */ - done(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId()); - - // Close the modal, sending the input data. - this.forceLeave = true; - this.closeModal(this.getInputData()); - } - - /** - * Get the input data. - * - * @return Object with the data. - */ - protected getInputData(): any { - return this.domUtils.getDataFromForm(document.forms['addon-mod_assign-edit-feedback-form']); - } - - /** - * Check if data has changed. - * - * @return Promise resolved with boolean: whether the data has changed. - */ - protected hasDataChanged(): Promise { - return this.feedbackDelegate.hasPluginDataChanged(this.assign, this.submission, this.plugin, this.getInputData(), - this.userId).catch(() => { - // Ignore errors. - return true; - }); - } -} diff --git a/src/addon/mod/assign/pages/edit/edit.html b/src/addon/mod/assign/pages/edit/edit.html deleted file mode 100644 index f849d0390..000000000 --- a/src/addon/mod/assign/pages/edit/edit.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - -
- - - - - - - - - - -
-
-
-
diff --git a/src/addon/mod/assign/pages/edit/edit.module.ts b/src/addon/mod/assign/pages/edit/edit.module.ts deleted file mode 100644 index 0125765e6..000000000 --- a/src/addon/mod/assign/pages/edit/edit.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModAssignComponentsModule } from '../../components/components.module'; -import { AddonModAssignEditPage } from './edit'; - -@NgModule({ - declarations: [ - AddonModAssignEditPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - AddonModAssignComponentsModule, - IonicPageModule.forChild(AddonModAssignEditPage), - TranslateModule.forChild() - ], -}) -export class AddonModAssignEditPageModule {} diff --git a/src/addon/mod/assign/pages/edit/edit.ts b/src/addon/mod/assign/pages/edit/edit.ts deleted file mode 100644 index d080ffd23..000000000 --- a/src/addon/mod/assign/pages/edit/edit.ts +++ /dev/null @@ -1,366 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper'; -import { AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission } from '../../providers/assign'; -import { AddonModAssignOfflineProvider } from '../../providers/assign-offline'; -import { AddonModAssignSyncProvider } from '../../providers/assign-sync'; -import { AddonModAssignHelperProvider } from '../../providers/helper'; - -/** - * Page that allows adding or editing an assigment submission. - */ -@IonicPage({ segment: 'addon-mod-assign-edit' }) -@Component({ - selector: 'page-addon-mod-assign-edit', - templateUrl: 'edit.html', -}) -export class AddonModAssignEditPage implements OnInit, OnDestroy { - - @ViewChild('editSubmissionForm') formElement: ElementRef; - - title: string; // Title to display. - assign: AddonModAssignAssign; // Assignment. - courseId: number; // Course ID the assignment belongs to. - userSubmission: AddonModAssignSubmission; // The user submission. - allowOffline: boolean; // Whether offline is allowed. - submissionStatement: string; // The submission statement. - submissionStatementAccepted: boolean; // Whether submission statement is accepted. - loaded: boolean; // Whether data has been loaded. - - protected moduleId: number; // Module ID the submission belongs to. - protected userId: number; // User doing the submission. - protected isBlind: boolean; // Whether blind is used. - protected editText: string; // "Edit submission" translated text. - protected saveOffline = false; // Whether to save data in offline. - protected hasOffline = false; // Whether the assignment has offline data. - protected isDestroyed = false; // Whether the component has been destroyed. - protected forceLeave = false; // To allow leaving the page without checking for changes. - - constructor(navParams: NavParams, protected navCtrl: NavController, protected sitesProvider: CoreSitesProvider, - protected syncProvider: CoreSyncProvider, protected domUtils: CoreDomUtilsProvider, - protected translate: TranslateService, protected fileUploaderHelper: CoreFileUploaderHelperProvider, - protected eventsProvider: CoreEventsProvider, protected assignProvider: AddonModAssignProvider, - protected assignOfflineProvider: AddonModAssignOfflineProvider, protected assignHelper: AddonModAssignHelperProvider, - protected assignSyncProvider: AddonModAssignSyncProvider) { - - this.moduleId = navParams.get('moduleId'); - this.courseId = navParams.get('courseId'); - this.userId = sitesProvider.getCurrentSiteUserId(); // Right now we can only edit current user's submissions. - this.isBlind = !!navParams.get('blindId'); - - this.editText = translate.instant('addon.mod_assign.editsubmission'); - this.title = this.editText; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchAssignment().finally(() => { - this.loaded = true; - }); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave) { - return; - } - - // Check if data has changed. - const changed = await this.hasDataChanged(); - if (changed) { - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - // Nothing has changed or user confirmed to leave. Clear temporary data from plugins. - this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, this.getInputData()); - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } - - /** - * Fetch assignment data. - * - * @return Promise resolved when done. - */ - protected fetchAssignment(): Promise { - const currentUserId = this.sitesProvider.getCurrentSiteUserId(); - - // Get assignment data. - return this.assignProvider.getAssignment(this.courseId, this.moduleId).then((assign) => { - this.assign = assign; - this.title = assign.name || this.title; - - if (!this.isDestroyed) { - // Block the assignment. - this.syncProvider.blockOperation(AddonModAssignProvider.COMPONENT, assign.id); - } - - // Wait for sync to be over (if any). - return this.assignSyncProvider.waitForSync(assign.id); - }).then(() => { - - // Get submission status. Ignore cache to get the latest data. - const options = { - userId: this.userId, - isBlind: this.isBlind, - cmId: this.assign.cmid, - filter: false, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - }; - - return this.assignProvider.getSubmissionStatus(this.assign.id, options).catch((err) => { - // Cannot connect. Get cached data. - options.filter = true; - options.readingStrategy = CoreSitesReadingStrategy.PreferCache; - - return this.assignProvider.getSubmissionStatus(this.assign.id, options).then((response) => { - const userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, response.lastattempt); - - // Check if the user can edit it in offline. - return this.assignHelper.canEditSubmissionOffline(this.assign, userSubmission).then((canEditOffline): any => { - if (canEditOffline) { - return response; - } - - // Submission cannot be edited in offline, reject. - this.allowOffline = false; - - return Promise.reject(err); - }); - }); - }).then((response) => { - if (!response.lastattempt.canedit) { - // Can't edit. Reject. - return Promise.reject(this.translate.instant('core.nopermissions', {$a: this.editText})); - } - - this.userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, response.lastattempt); - this.allowOffline = true; // If offline isn't allowed we shouldn't have reached this point. - - // Only show submission statement if we are editing our own submission. - if (this.assign.requiresubmissionstatement && !this.assign.submissiondrafts && this.userId == currentUserId) { - this.submissionStatement = this.assign.submissionstatement; - } else { - this.submissionStatement = undefined; - } - - // Check if there's any offline data for this submission. - return this.assignOfflineProvider.getSubmission(this.assign.id, this.userId).then((data) => { - this.hasOffline = data && data.plugindata && Object.keys(data.plugindata).length > 0; - }).catch(() => { - // No offline data found. - this.hasOffline = false; - }); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting assigment data.'); - - // Leave the player. - this.leaveWithoutCheck(); - }); - } - - /** - * Get the input data. - * - * @return Input data. - */ - protected getInputData(): any { - return this.domUtils.getDataFromForm(document.forms['addon-mod_assign-edit-form']); - } - - /** - * Check if data has changed. - * - * @return Promise resolved with boolean: whether data has changed. - */ - protected hasDataChanged(): Promise { - // Usually the hasSubmissionDataChanged call will be resolved inmediately, causing the modal to be shown just an instant. - // We'll wait a bit before showing it to prevent this "blink". - let modal, - showModal = true; - - setTimeout(() => { - if (showModal) { - modal = this.domUtils.showModalLoading(); - } - }, 100); - - const data = this.getInputData(); - - return this.assignHelper.hasSubmissionDataChanged(this.assign, this.userSubmission, data).finally(() => { - if (modal) { - modal.dismiss(); - } else { - showModal = false; - } - }); - } - - /** - * Leave the view without checking for changes. - */ - protected leaveWithoutCheck(): void { - this.forceLeave = true; - this.navCtrl.pop(); - } - - /** - * Get data to submit based on the input data. - * - * @param inputData The input data. - * @return Promise resolved with the data to submit. - */ - protected prepareSubmissionData(inputData: any): Promise { - // If there's offline data, always save it in offline. - this.saveOffline = this.hasOffline; - - return this.assignHelper.prepareSubmissionPluginData(this.assign, this.userSubmission, inputData, this.hasOffline) - .catch((error) => { - - if (this.allowOffline && !this.saveOffline) { - // Cannot submit in online, prepare for offline usage. - this.saveOffline = true; - - return this.assignHelper.prepareSubmissionPluginData(this.assign, this.userSubmission, inputData, true); - } - - return Promise.reject(error); - }); - } - - /** - * Save the submission. - */ - save(): void { - // Check if data has changed. - this.hasDataChanged().then((changed) => { - if (changed) { - this.saveSubmission().then(() => { - this.leaveWithoutCheck(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error saving submission.'); - }); - } else { - // Nothing to save, just go back. - this.leaveWithoutCheck(); - } - }); - } - - /** - * Save the submission. - * - * @return Promise resolved when done. - */ - protected async saveSubmission(): Promise { - const inputData = this.getInputData(); - - if (this.submissionStatement && (!inputData.submissionstatement || inputData.submissionstatement === 'false')) { - throw this.translate.instant('addon.mod_assign.acceptsubmissionstatement'); - } - - let modal = this.domUtils.showModalLoading(); - let size; - - // Get size to ask for confirmation. - try { - size = await this.assignHelper.getSubmissionSizeForEdit(this.assign, this.userSubmission, inputData); - } catch (error) { - // Error calculating size, return -1. - size = -1; - } - - modal.dismiss(); - - try { - // Confirm action. - await this.fileUploaderHelper.confirmUploadFile(size, true, this.allowOffline); - - modal = this.domUtils.showModalLoading('core.sending', true); - - const pluginData = await this.prepareSubmissionData(inputData); - if (!Object.keys(pluginData).length) { - // Nothing to save. - return; - } - - let sent: boolean; - - if (this.saveOffline) { - // Save submission in offline. - sent = false; - await this.assignOfflineProvider.saveSubmission(this.assign.id, this.courseId, pluginData, - this.userSubmission.timemodified, !this.assign.submissiondrafts, this.userId); - } else { - // Try to send it to server. - sent = await this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline, - this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId); - } - - // Clear temporary data from plugins. - await this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData); - - if (sent) { - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'assign' }); - } - - // Submission saved, trigger events. - this.domUtils.triggerFormSubmittedEvent(this.formElement, sent, this.sitesProvider.getCurrentSiteId()); - - const params = { - assignmentId: this.assign.id, - submissionId: this.userSubmission.id, - userId: this.userId, - }; - - this.eventsProvider.trigger(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, params, - this.sitesProvider.getCurrentSiteId()); - - if (!this.assign.submissiondrafts) { - // No drafts allowed, so it was submitted. Trigger event. - this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, params, - this.sitesProvider.getCurrentSiteId()); - } - } finally { - modal.dismiss(); - } - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.isDestroyed = true; - - // Unblock the assignment. - if (this.assign) { - this.syncProvider.unblockOperation(AddonModAssignProvider.COMPONENT, this.assign.id); - } - } -} diff --git a/src/addon/mod/assign/pages/index/index.html b/src/addon/mod/assign/pages/index/index.html deleted file mode 100644 index 3b40f57c1..000000000 --- a/src/addon/mod/assign/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/assign/pages/index/index.module.ts b/src/addon/mod/assign/pages/index/index.module.ts deleted file mode 100644 index 8b06a4404..000000000 --- a/src/addon/mod/assign/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModAssignComponentsModule } from '../../components/components.module'; -import { AddonModAssignIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModAssignIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModAssignComponentsModule, - IonicPageModule.forChild(AddonModAssignIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModAssignIndexPageModule {} diff --git a/src/addon/mod/assign/pages/index/index.ts b/src/addon/mod/assign/pages/index/index.ts deleted file mode 100644 index a4b3413b4..000000000 --- a/src/addon/mod/assign/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModAssignIndexComponent } from '../../components/index/index'; - -/** - * Page that displays an assign. - */ -@IonicPage({ segment: 'addon-mod-assign-index' }) -@Component({ - selector: 'page-addon-mod-assign-index', - templateUrl: 'index.html', -}) -export class AddonModAssignIndexPage { - @ViewChild(AddonModAssignIndexComponent) assignComponent: AddonModAssignIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the assign instance. - * - * @param assign Assign instance. - */ - updateData(assign: any): void { - this.title = assign.name || this.title; - } -} diff --git a/src/addon/mod/assign/pages/submission-list/submission-list.html b/src/addon/mod/assign/pages/submission-list/submission-list.html deleted file mode 100644 index c11241dbb..000000000 --- a/src/addon/mod/assign/pages/submission-list/submission-list.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - - - - -

{{submission.userfullname}}

-

{{ 'addon.mod_assign.hiddenuser' | translate }}{{submission.blindid}}

-

- {{submission.groupname}} - {{ 'addon.mod_assign.noteam' | translate }} - {{ 'addon.mod_assign.multipleteams' | translate }} - {{ 'addon.mod_assign.defaultteam' | translate }} -

- - {{ submission.statusTranslated }} - - - {{ submission.gradingStatusTranslationId | translate }} - -
-
- - - - {{ 'addon.mod_assign.notallparticipantsareshown' | translate }} - -
-
-
-
diff --git a/src/addon/mod/assign/pages/submission-list/submission-list.module.ts b/src/addon/mod/assign/pages/submission-list/submission-list.module.ts deleted file mode 100644 index 6e791da75..000000000 --- a/src/addon/mod/assign/pages/submission-list/submission-list.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModAssignSubmissionListPage } from './submission-list'; - -@NgModule({ - declarations: [ - AddonModAssignSubmissionListPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonModAssignSubmissionListPage), - TranslateModule.forChild() - ], -}) -export class AddonModAssignSubmissionListPageModule {} diff --git a/src/addon/mod/assign/pages/submission-list/submission-list.ts b/src/addon/mod/assign/pages/submission-list/submission-list.ts deleted file mode 100644 index b0d6d2eca..000000000 --- a/src/addon/mod/assign/pages/submission-list/submission-list.ts +++ /dev/null @@ -1,348 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider, CoreEventObserver } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignGrade, AddonModAssignSubmission -} from '../../providers/assign'; -import { AddonModAssignOfflineProvider } from '../../providers/assign-offline'; -import { AddonModAssignSyncProvider, AddonModAssignSync } from '../../providers/assign-sync'; -import { AddonModAssignHelperProvider, AddonModAssignSubmissionFormatted } from '../../providers/helper'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; - -/** - * Page that displays a list of submissions of an assignment. - */ -@IonicPage({ segment: 'addon-mod-assign-submission-list' }) -@Component({ - selector: 'page-addon-mod-assign-submission-list', - templateUrl: 'submission-list.html', -}) -export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy { - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - title: string; // Title to display. - assign: AddonModAssignAssign; // Assignment. - submissions: any[]; // List of submissions - loaded: boolean; // Whether data has been loaded. - haveAllParticipants: boolean; // Whether all participants have been loaded. - selectedSubmissionId: number; // Selected submission ID. - groupId = 0; // Group ID to show. - - groupInfo: CoreGroupInfo = { - groups: [], - separateGroups: false, - visibleGroups: false - }; - - protected moduleId: number; // Module ID the submission belongs to. - protected courseId: number; // Course ID the assignment belongs to. - protected selectedStatus: string; // The status to see. - protected gradedObserver: CoreEventObserver; // Observer to refresh data when a grade changes. - protected syncObserver: CoreEventObserver; // OObserver to refresh data when the async is synchronized. - protected submissionsData: {canviewsubmissions: boolean, submissions?: AddonModAssignSubmission[]}; - - constructor(navParams: NavParams, protected sitesProvider: CoreSitesProvider, protected eventsProvider: CoreEventsProvider, - protected domUtils: CoreDomUtilsProvider, protected translate: TranslateService, - protected assignProvider: AddonModAssignProvider, protected assignOfflineProvider: AddonModAssignOfflineProvider, - protected assignHelper: AddonModAssignHelperProvider, protected groupsProvider: CoreGroupsProvider) { - - this.moduleId = navParams.get('moduleId'); - this.courseId = navParams.get('courseId'); - this.groupId = navParams.get('groupId'); - this.selectedStatus = navParams.get('status'); - - if (this.selectedStatus) { - if (this.selectedStatus == AddonModAssignProvider.NEED_GRADING) { - this.title = this.translate.instant('addon.mod_assign.numberofsubmissionsneedgrading'); - } else { - this.title = this.translate.instant('addon.mod_assign.submissionstatus_' + this.selectedStatus); - } - } else { - this.title = this.translate.instant('addon.mod_assign.numberofparticipants'); - } - - // Update data if some grade changes. - this.gradedObserver = eventsProvider.on(AddonModAssignProvider.GRADED_EVENT, (data) => { - if (this.loaded && this.assign && data.assignmentId == this.assign.id && - data.userId == sitesProvider.getCurrentSiteUserId()) { - // Grade changed, refresh the data. - this.loaded = false; - - this.refreshAllData(true).finally(() => { - this.loaded = true; - }); - } - }, sitesProvider.getCurrentSiteId()); - - // Refresh data if this assign is synchronized. - const events = [AddonModAssignSyncProvider.AUTO_SYNCED, AddonModAssignSyncProvider.MANUAL_SYNCED]; - this.syncObserver = eventsProvider.onMultiple(events, (data) => { - if (!this.loaded || data.context == 'submission-list') { - return; - } - - this.loaded = false; - - this.refreshAllData(false).finally(() => { - this.loaded = true; - }); - }, this.sitesProvider.getCurrentSiteId()); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchAssignment(true).finally(() => { - if (!this.selectedSubmissionId && this.splitviewCtrl.isOn() && this.submissions.length > 0) { - // Take first and load it. - this.loadSubmission(this.submissions[0]); - } - - this.loaded = true; - }); - } - - /** - * Fetch assignment data. - * - * @param sync Whether to try to synchronize data. - * @return Promise resolved when done. - */ - protected async fetchAssignment(sync?: boolean): Promise { - try { - // Get assignment data. - this.assign = await this.assignProvider.getAssignment(this.courseId, this.moduleId); - - this.title = this.assign.name || this.title; - - if (sync) { - try { - // Try to synchronize data. - const result = await AddonModAssignSync.instance.syncAssign(this.assign.id); - - if (result && result.updated) { - this.eventsProvider.trigger(AddonModAssignSyncProvider.MANUAL_SYNCED, { - assignId: this.assign.id, - warnings: result.warnings, - gradesBlocked: result.gradesBlocked, - context: 'submission-list', - }, this.sitesProvider.getCurrentSiteId()); - } - } catch (error) { - // Ignore errors, probably user is offline or sync is blocked. - } - } - - // Get assignment submissions. - this.submissionsData = await this.assignProvider.getSubmissions(this.assign.id, {cmId: this.assign.cmid}); - - if (!this.submissionsData.canviewsubmissions) { - // User shouldn't be able to reach here. - throw new Error('Cannot view submissions.'); - } - - // Check if groupmode is enabled to avoid showing wrong numbers. - this.groupInfo = await this.groupsProvider.getActivityGroupInfo(this.assign.cmid, false); - - await this.setGroup(this.groupsProvider.validateGroupId(this.groupId, this.groupInfo)); - } catch (error) { - this.domUtils.showErrorModalDefault(error, 'Error getting assigment data.'); - } - } - - /** - * Set group to see the summary. - * - * @param groupId Group ID. - * @return Resolved when done. - */ - setGroup(groupId: number): Promise { - this.groupId = groupId; - - this.haveAllParticipants = true; - - if (!this.sitesProvider.getCurrentSite().wsAvailable('mod_assign_list_participants')) { - // Submissions are not displayed in Moodle 3.1 without the local plugin, see MOBILE-2968. - this.haveAllParticipants = false; - this.submissions = []; - - return Promise.resolve(); - } - - // Fetch submissions and grades. - const promises = [ - this.assignHelper.getSubmissionsUserData(this.assign, this.submissionsData.submissions, this.groupId), - // Get assignment grades only if workflow is not enabled to check grading date. - !this.assign.markingworkflow ? this.assignProvider.getAssignmentGrades(this.assign.id, {cmId: this.assign.cmid}) : - Promise.resolve(null), - ]; - - return Promise.all(promises).then(([submissions, grades]: [AddonModAssignSubmissionFormatted[], AddonModAssignGrade[]]) => { - // Filter the submissions to get only the ones with the right status and add some extra data. - const getNeedGrading = this.selectedStatus == AddonModAssignProvider.NEED_GRADING, - searchStatus = getNeedGrading ? AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED : this.selectedStatus, - promises = [], - showSubmissions = []; - - submissions.forEach((submission: AddonModAssignSubmissionForList) => { - if (!searchStatus || searchStatus == submission.status) { - promises.push(this.assignOfflineProvider.getSubmissionGrade(this.assign.id, submission.userid).catch(() => { - // Ignore errors. - }).then((data) => { - let promise, - notSynced = false; - - // Load offline grades. - if (data && submission.timemodified < data.timemodified) { - notSynced = true; - } - - if (getNeedGrading) { - // Only show the submissions that need to be graded. - promise = this.assignProvider.needsSubmissionToBeGraded(submission, this.assign.id); - } else { - promise = Promise.resolve(true); - } - - return promise.then((add) => { - if (!add) { - return; - } - - if (submission.gradingstatus == 'graded' && !this.assign.markingworkflow) { - // Get the last grade of the submission. - const grade = grades.filter((grade) => { - return grade.userid == submission.userid; - }).reduce((a, b) => { - return ( a.timemodified > b.timemodified ? a : b ); - }); - - if (grade && grade.timemodified < submission.timemodified) { - submission.gradingstatus = AddonModAssignProvider.GRADED_FOLLOWUP_SUBMIT; - } - } - submission.statusColor = this.assignProvider.getSubmissionStatusColor(submission.status); - submission.gradingColor = this.assignProvider.getSubmissionGradingStatusColor(submission.gradingstatus); - - // Show submission status if not submitted for grading. - if (submission.statusColor != 'success' || !submission.gradingstatus) { - submission.statusTranslated = this.translate.instant('addon.mod_assign.submissionstatus_' + - submission.status); - } else { - submission.statusTranslated = ''; - } - - if (notSynced) { - submission.gradingStatusTranslationId = 'addon.mod_assign.gradenotsynced'; - submission.gradingColor = ''; - } else if (submission.statusColor != 'danger' || submission.gradingColor != 'danger') { - // Show grading status if one of the statuses is not done. - submission.gradingStatusTranslationId = - this.assignProvider.getSubmissionGradingStatusTranslationId(submission.gradingstatus); - } else { - submission.gradingStatusTranslationId = ''; - } - - showSubmissions.push(submission); - }); - })); - } - }); - - return Promise.all(promises).then(() => { - this.submissions = showSubmissions; - }); - }); - } - - /** - * Load a certain submission. - * - * @param submission The submission to load. - */ - loadSubmission(submission: any): void { - if (this.selectedSubmissionId === submission.submitid && this.splitviewCtrl.isOn()) { - // Already selected. - return; - } - - this.selectedSubmissionId = submission.submitid; - - this.splitviewCtrl.push('AddonModAssignSubmissionReviewPage', { - courseId: this.courseId, - moduleId: this.moduleId, - submitId: submission.submitid, - blindId: submission.blindid - }); - } - - /** - * Refresh all the data. - * - * @param sync Whether to try to synchronize data. - * @return Promise resolved when done. - */ - protected refreshAllData(sync?: boolean): Promise { - const promises = []; - - promises.push(this.assignProvider.invalidateAssignmentData(this.courseId)); - if (this.assign) { - promises.push(this.assignProvider.invalidateAllSubmissionData(this.assign.id)); - promises.push(this.assignProvider.invalidateAssignmentUserMappingsData(this.assign.id)); - promises.push(this.assignProvider.invalidateAssignmentGradesData(this.assign.id)); - promises.push(this.assignProvider.invalidateListParticipantsData(this.assign.id)); - } - - return Promise.all(promises).finally(() => { - return this.fetchAssignment(sync); - }); - } - - /** - * Refresh the list. - * - * @param refresher Refresher. - */ - refreshList(refresher: any): void { - this.refreshAllData(true).finally(() => { - refresher.complete(); - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.gradedObserver && this.gradedObserver.off(); - this.syncObserver && this.syncObserver.off(); - } -} - -/** - * Calculated data for an assign submission. - */ -type AddonModAssignSubmissionForList = AddonModAssignSubmissionFormatted & { - statusColor?: string; // Calculated in the app. Color of the submission status. - gradingColor?: string; // Calculated in the app. Color of the submission grading status. - statusTranslated?: string; // Calculated in the app. Translated text of the submission status. - gradingStatusTranslationId?: string; // Calculated in the app. Key of the text of the submission grading status. -}; diff --git a/src/addon/mod/assign/pages/submission-review/submission-review.html b/src/addon/mod/assign/pages/submission-review/submission-review.html deleted file mode 100644 index 47e05acad..000000000 --- a/src/addon/mod/assign/pages/submission-review/submission-review.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/assign/pages/submission-review/submission-review.module.ts b/src/addon/mod/assign/pages/submission-review/submission-review.module.ts deleted file mode 100644 index d22b9920e..000000000 --- a/src/addon/mod/assign/pages/submission-review/submission-review.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModAssignComponentsModule } from '../../components/components.module'; -import { AddonModAssignSubmissionReviewPage } from './submission-review'; - -@NgModule({ - declarations: [ - AddonModAssignSubmissionReviewPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - AddonModAssignComponentsModule, - IonicPageModule.forChild(AddonModAssignSubmissionReviewPage), - TranslateModule.forChild() - ], -}) -export class AddonModAssignSubmissionReviewPageModule {} diff --git a/src/addon/mod/assign/pages/submission-review/submission-review.ts b/src/addon/mod/assign/pages/submission-review/submission-review.ts deleted file mode 100644 index d3d2ed5ba..000000000 --- a/src/addon/mod/assign/pages/submission-review/submission-review.ts +++ /dev/null @@ -1,173 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, ViewChild } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { CoreAppProvider } from '@providers/app'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModAssignProvider, AddonModAssignAssign } from '../../providers/assign'; -import { AddonModAssignSubmissionComponent } from '../../components/submission/submission'; - -/** - * Page that displays a submission. - */ -@IonicPage({ segment: 'addon-mod-assign-submission-review' }) -@Component({ - selector: 'page-addon-mod-assign-submission-review', - templateUrl: 'submission-review.html', -}) -export class AddonModAssignSubmissionReviewPage implements OnInit { - @ViewChild(AddonModAssignSubmissionComponent) submissionComponent: AddonModAssignSubmissionComponent; - - title: string; // Title to display. - moduleId: number; // Module ID the submission belongs to. - courseId: number; // Course ID the assignment belongs to. - submitId: number; // User that did the submission. - blindId: number; // Blinded user ID (if it's blinded). - showGrade: boolean; // Whether to display the grade at start. - loaded: boolean; // Whether data has been loaded. - canSaveGrades: boolean; // Whether the user can save grades. - - protected assign: AddonModAssignAssign; // The assignment the submission belongs to. - protected blindMarking: boolean; // Whether it uses blind marking. - protected forceLeave = false; // To allow leaving the page without checking for changes. - - constructor(navParams: NavParams, protected navCtrl: NavController, protected courseProvider: CoreCourseProvider, - protected appProvider: CoreAppProvider, protected assignProvider: AddonModAssignProvider, - protected domUtils: CoreDomUtilsProvider) { - - this.moduleId = navParams.get('moduleId'); - this.courseId = navParams.get('courseId'); - this.submitId = navParams.get('submitId'); - this.blindId = navParams.get('blindId'); - this.showGrade = !!navParams.get('showGrade'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchSubmission().finally(() => { - this.loaded = true; - }); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - ionViewCanLeave(): boolean | Promise { - if (!this.submissionComponent || this.forceLeave) { - return true; - } - - // Check if data has changed. - return this.submissionComponent.canLeave(); - } - - /** - * User entered the page. - */ - ionViewDidEnter(): void { - this.submissionComponent && this.submissionComponent.ionViewDidEnter(); - } - - /** - * User left the page. - */ - ionViewDidLeave(): void { - this.submissionComponent && this.submissionComponent.ionViewDidLeave(); - } - - /** - * Get the submission. - * - * @return Promise resolved when done. - */ - protected fetchSubmission(): Promise { - return this.assignProvider.getAssignment(this.courseId, this.moduleId).then((assignment) => { - this.assign = assignment; - this.title = this.assign.name; - - this.blindMarking = this.assign.blindmarking && !this.assign.revealidentities; - - return this.courseProvider.getModuleBasicGradeInfo(this.moduleId).then((gradeInfo) => { - if (gradeInfo) { - // Grades can be saved if simple grading. - if (gradeInfo.advancedgrading && gradeInfo.advancedgrading[0] && - typeof gradeInfo.advancedgrading[0].method != 'undefined') { - - const method = gradeInfo.advancedgrading[0].method || 'simple'; - this.canSaveGrades = method == 'simple'; - } else { - this.canSaveGrades = true; - } - } - }); - }); - } - - /** - * Refresh all the data. - * - * @return Promise resolved when done. - */ - protected refreshAllData(): Promise { - const promises = []; - - promises.push(this.assignProvider.invalidateAssignmentData(this.courseId)); - if (this.assign) { - promises.push(this.assignProvider.invalidateSubmissionData(this.assign.id)); - promises.push(this.assignProvider.invalidateAssignmentUserMappingsData(this.assign.id)); - promises.push(this.assignProvider.invalidateSubmissionStatusData(this.assign.id, this.submitId, undefined, - this.blindMarking)); - } - - return Promise.all(promises).finally(() => { - this.submissionComponent && this.submissionComponent.invalidateAndRefresh(true); - - return this.fetchSubmission(); - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - */ - refreshSubmission(refresher: any): void { - this.refreshAllData().finally(() => { - refresher.complete(); - }); - } - - /** - * Submit a grade and feedback. - */ - submitGrade(): void { - if (this.submissionComponent) { - this.submissionComponent.submitGrade().then(() => { - // Grade submitted, leave the view if not in tablet. - if (!this.appProvider.isWide()) { - this.forceLeave = true; - this.navCtrl.pop(); - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }); - } - } -} diff --git a/src/addon/mod/assign/providers/assign-offline.ts b/src/addon/mod/assign/providers/assign-offline.ts deleted file mode 100644 index 29923f37f..000000000 --- a/src/addon/mod/assign/providers/assign-offline.ts +++ /dev/null @@ -1,510 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFileProvider } from '@providers/file'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; - -/** - * Service to handle offline assign. - */ -@Injectable() -export class AddonModAssignOfflineProvider { - - protected logger; - - // Variables for database. - static SUBMISSIONS_TABLE = 'addon_mod_assign_submissions'; - static SUBMISSIONS_GRADES_TABLE = 'addon_mod_assign_submissions_grading'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModAssignOfflineProvider', - version: 1, - tables: [ - { - name: AddonModAssignOfflineProvider.SUBMISSIONS_TABLE, - columns: [ - { - name: 'assignid', - type: 'INTEGER' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'userid', - type: 'INTEGER' - }, - { - name: 'plugindata', - type: 'TEXT' - }, - { - name: 'onlinetimemodified', - type: 'INTEGER' - }, - { - name: 'timecreated', - type: 'INTEGER' - }, - { - name: 'timemodified', - type: 'INTEGER' - }, - { - name: 'submitted', - type: 'INTEGER' - }, - { - name: 'submissionstatement', - type: 'INTEGER' - } - ], - primaryKeys: ['assignid', 'userid'] - }, - { - name: AddonModAssignOfflineProvider.SUBMISSIONS_GRADES_TABLE, - columns: [ - { - name: 'assignid', - type: 'INTEGER' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'userid', - type: 'INTEGER' - }, - { - name: 'grade', - type: 'REAL' - }, - { - name: 'attemptnumber', - type: 'INTEGER' - }, - { - name: 'addattempt', - type: 'INTEGER' - }, - { - name: 'workflowstate', - type: 'TEXT' - }, - { - name: 'applytoall', - type: 'INTEGER' - }, - { - name: 'outcomes', - type: 'TEXT' - }, - { - name: 'plugindata', - type: 'TEXT' - }, - { - name: 'timemodified', - type: 'INTEGER' - } - ], - primaryKeys: ['assignid', 'userid'] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider, - private fileProvider: CoreFileProvider, private timeUtils: CoreTimeUtilsProvider) { - this.logger = logger.getInstance('AddonModAssignOfflineProvider'); - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete a submission. - * - * @param assignId Assignment ID. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteSubmission(assignId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().deleteRecords(AddonModAssignOfflineProvider.SUBMISSIONS_TABLE, - {assignid: assignId, userid: userId}); - }); - } - - /** - * Delete a submission grade. - * - * @param assignId Assignment ID. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteSubmissionGrade(assignId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().deleteRecords(AddonModAssignOfflineProvider.SUBMISSIONS_GRADES_TABLE, - {assignid: assignId, userid: userId}); - }); - } - - /** - * Get all the assignments ids that have something to be synced. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with assignments id that have something to be synced. - */ - getAllAssigns(siteId?: string): Promise { - const promises = []; - - promises.push(this.getAllSubmissions(siteId)); - promises.push(this.getAllSubmissionsGrade(siteId)); - - return Promise.all(promises).then((results) => { - // Flatten array. - results = [].concat.apply([], results); - - // Get assign id. - results = results.map((object) => { - return object.assignid; - }); - - // Get unique values. - results = results.filter((id, pos) => { - return results.indexOf(id) == pos; - }); - - return results; - }); - } - - /** - * Get all the stored submissions from all the assignments. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with submissions. - */ - protected getAllSubmissions(siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getAllRecords(AddonModAssignOfflineProvider.SUBMISSIONS_TABLE); - }).then((submissions) => { - - // Parse the plugin data. - submissions.forEach((submission) => { - submission.plugindata = this.textUtils.parseJSON(submission.plugindata, {}); - }); - - return submissions; - }); - } - - /** - * Get all the stored submissions grades from all the assignments. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with submissions grades. - */ - protected getAllSubmissionsGrade(siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getAllRecords(AddonModAssignOfflineProvider.SUBMISSIONS_GRADES_TABLE); - }).then((submissions) => { - - // Parse the plugin data and outcomes. - submissions.forEach((submission) => { - submission.outcomes = this.textUtils.parseJSON(submission.outcomes, {}); - submission.plugindata = this.textUtils.parseJSON(submission.plugindata, {}); - }); - - return submissions; - }); - } - - /** - * Get all the stored submissions for a certain assignment. - * - * @param assignId Assignment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with submissions. - */ - getAssignSubmissions(assignId: number, siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getRecords(AddonModAssignOfflineProvider.SUBMISSIONS_TABLE, {assignid: assignId}); - }).then((submissions) => { - - // Parse the plugin data. - submissions.forEach((submission) => { - submission.plugindata = this.textUtils.parseJSON(submission.plugindata, {}); - }); - - return submissions; - }); - } - - /** - * Get all the stored submissions grades for a certain assignment. - * - * @param assignId Assignment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with submissions grades. - */ - getAssignSubmissionsGrade(assignId: number, siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getRecords(AddonModAssignOfflineProvider.SUBMISSIONS_GRADES_TABLE, {assignid: assignId}); - }).then((submissions) => { - - // Parse the plugin data and outcomes. - submissions.forEach((submission) => { - submission.outcomes = this.textUtils.parseJSON(submission.outcomes, {}); - submission.plugindata = this.textUtils.parseJSON(submission.plugindata, {}); - }); - - return submissions; - }); - } - - /** - * Get a stored submission. - * - * @param assignId Assignment ID. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with submission. - */ - getSubmission(assignId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().getRecord(AddonModAssignOfflineProvider.SUBMISSIONS_TABLE, {assignid: assignId, userid: userId}); - }).then((submission) => { - - // Parse the plugin data. - submission.plugindata = this.textUtils.parseJSON(submission.plugindata, {}); - - return submission; - }); - } - - /** - * Get the path to the folder where to store files for an offline submission. - * - * @param assignId Assignment ID. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getSubmissionFolder(assignId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const siteFolderPath = this.fileProvider.getSiteFolder(site.getId()), - submissionFolderPath = 'offlineassign/' + assignId + '/' + userId; - - return this.textUtils.concatenatePaths(siteFolderPath, submissionFolderPath); - }); - } - - /** - * Get a stored submission grade. - * Submission grades are not identified using attempt number so it can retrieve the feedback for a previous attempt. - * - * @param assignId Assignment ID. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with submission grade. - */ - getSubmissionGrade(assignId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().getRecord(AddonModAssignOfflineProvider.SUBMISSIONS_GRADES_TABLE, - {assignid: assignId, userid: userId}); - }).then((submission) => { - - // Parse the plugin data and outcomes. - submission.outcomes = this.textUtils.parseJSON(submission.outcomes, {}); - submission.plugindata = this.textUtils.parseJSON(submission.plugindata, {}); - - return submission; - }); - } - - /** - * Get the path to the folder where to store files for a certain plugin in an offline submission. - * - * @param assignId Assignment ID. - * @param pluginName Name of the plugin. Must be unique (both in submission and feedback plugins). - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getSubmissionPluginFolder(assignId: number, pluginName: string, userId?: number, siteId?: string): Promise { - return this.getSubmissionFolder(assignId, userId, siteId).then((folderPath) => { - return this.textUtils.concatenatePaths(folderPath, pluginName); - }); - } - - /** - * Check if the assignment has something to be synced. - * - * @param assignId Assignment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether the assignment has something to be synced. - */ - hasAssignOfflineData(assignId: number, siteId?: string): Promise { - const promises = []; - - promises.push(this.getAssignSubmissions(assignId, siteId)); - promises.push(this.getAssignSubmissionsGrade(assignId, siteId)); - - return Promise.all(promises).then((results) => { - for (let i = 0; i < results.length; i++) { - const result = results[i]; - - if (result && result.length) { - return true; - } - } - - return false; - }).catch(() => { - // No offline data found. - return false; - }); - } - - /** - * Mark/Unmark a submission as being submitted. - * - * @param assignId Assignment ID. - * @param courseId Course ID the assign belongs to. - * @param submitted True to mark as submitted, false to mark as not submitted. - * @param acceptStatement True to accept the submission statement, false otherwise. - * @param timemodified The time the submission was last modified in online. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if marked, rejected if failure. - */ - markSubmitted(assignId: number, courseId: number, submitted: boolean, acceptStatement: boolean, timemodified: number, - userId?: number, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - // Check if there's a submission stored. - return this.getSubmission(assignId, userId, site.getId()).catch(() => { - // No submission, create an empty one. - const now = this.timeUtils.timestamp(); - - return { - assignid: assignId, - courseid: courseId, - userid: userId, - onlinetimemodified: timemodified, - timecreated: now, - timemodified: now - }; - }).then((submission) => { - // Mark the submission. - submission.submitted = submitted ? 1 : 0; - submission.submissionstatement = acceptStatement ? 1 : 0; - submission.plugindata = submission.plugindata ? JSON.stringify(submission.plugindata) : '{}'; - - return site.getDb().insertRecord(AddonModAssignOfflineProvider.SUBMISSIONS_TABLE, submission); - }); - }); - } - - /** - * Save a submission to be sent later. - * - * @param assignId Assignment ID. - * @param courseId Course ID the assign belongs to. - * @param pluginData Data to save. - * @param timemodified The time the submission was last modified in online. - * @param submitted True if submission has been submitted, false otherwise. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - saveSubmission(assignId: number, courseId: number, pluginData: any, timemodified: number, submitted: boolean, userId?: number, - siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const now = this.timeUtils.timestamp(), - entry = { - assignid: assignId, - courseid: courseId, - plugindata: pluginData ? JSON.stringify(pluginData) : '{}', - userid: userId, - submitted: submitted ? 1 : 0, - timecreated: now, - timemodified: now, - onlinetimemodified: timemodified - }; - - return site.getDb().insertRecord(AddonModAssignOfflineProvider.SUBMISSIONS_TABLE, entry); - }); - } - - /** - * Save a grading to be sent later. - * - * @param assignId Assign ID. - * @param userId User ID. - * @param courseId Course ID the assign belongs to. - * @param grade Grade to submit. - * @param attemptNumber Number of the attempt being graded. - * @param addAttempt Admit the user to attempt again. - * @param workflowState Next workflow State. - * @param applyToAll If it's a team submission, whether the grade applies to all group members. - * @param outcomes Object including all outcomes values. If empty, any of them will be sent. - * @param pluginData Plugin data to save. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - submitGradingForm(assignId: number, userId: number, courseId: number, grade: number, attemptNumber: number, addAttempt: boolean, - workflowState: string, applyToAll: boolean, outcomes: any, pluginData: any, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const now = this.timeUtils.timestamp(), - entry = { - assignid: assignId, - userid: userId, - courseid: courseId, - grade: grade, - attemptnumber: attemptNumber, - addattempt: addAttempt ? 1 : 0, - workflowstate: workflowState, - applytoall: applyToAll ? 1 : 0, - outcomes: outcomes ? JSON.stringify(outcomes) : '{}', - plugindata: pluginData ? JSON.stringify(pluginData) : '{}', - timemodified: now - }; - - return site.getDb().insertRecord(AddonModAssignOfflineProvider.SUBMISSIONS_GRADES_TABLE, entry); - }); - } -} diff --git a/src/addon/mod/assign/providers/assign-sync.ts b/src/addon/mod/assign/providers/assign-sync.ts deleted file mode 100644 index 3cc10f7f6..000000000 --- a/src/addon/mod/assign/providers/assign-sync.ts +++ /dev/null @@ -1,517 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; -import { CoreSyncBaseProvider, CoreSyncBlockedError } from '@classes/base-sync'; -import { AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission } from './assign'; -import { AddonModAssignOfflineProvider } from './assign-offline'; -import { AddonModAssignSubmissionDelegate } from './submission-delegate'; -import { AddonModAssignFeedbackDelegate } from './feedback-delegate'; - -import { makeSingleton } from '@singletons/core.singletons'; - -/** - * Data returned by an assign sync. - */ -export interface AddonModAssignSyncResult { - /** - * List of warnings. - */ - warnings: string[]; - - /** - * Whether data was updated in the site. - */ - updated: boolean; - - /** - * Whether some grade couldn't be synced because it was blocked. - */ - gradesBlocked: number[]; -} - -/** - * Service to sync assigns. - */ -@Injectable() -export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_assign_autom_synced'; - static MANUAL_SYNCED = 'addon_mod_assign_manual_synced'; - - protected componentTranslate: string; - - constructor(loggerProvider: CoreLoggerProvider, - sitesProvider: CoreSitesProvider, - appProvider: CoreAppProvider, - syncProvider: CoreSyncProvider, - textUtils: CoreTextUtilsProvider, - translate: TranslateService, - timeUtils: CoreTimeUtilsProvider, - protected courseProvider: CoreCourseProvider, - protected eventsProvider: CoreEventsProvider, - protected assignProvider: AddonModAssignProvider, - protected assignOfflineProvider: AddonModAssignOfflineProvider, - protected utils: CoreUtilsProvider, - protected submissionDelegate: AddonModAssignSubmissionDelegate, - protected feedbackDelegate: AddonModAssignFeedbackDelegate, - protected gradesHelper: CoreGradesHelperProvider, - protected logHelper: CoreCourseLogHelperProvider) { - - super('AddonModAssignSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils); - - this.componentTranslate = courseProvider.translateModuleName('assign'); - } - - /** - * Get the sync ID for a certain user grade. - * - * @param assignId Assign ID. - * @param userId User the grade belongs to. - * @return Sync ID. - */ - getGradeSyncId(assignId: number, userId: number): string { - return 'assignGrade#' + assignId + '#' + userId; - } - - /** - * Convenience function to get scale selected option. - * - * @param options Possible options. - * @param selected Selected option to search. - * @return Index of the selected option. - */ - protected getSelectedScaleId(options: string, selected: string): number { - let optionsList = options.split(','); - - optionsList = optionsList.map((value) => { - return value.trim(); - }); - - optionsList.unshift(''); - - const index = options.indexOf(selected) || 0; - if (index < 0) { - return 0; - } - - return index; - } - - /** - * Check if an assignment has data to synchronize. - * - * @param assignId Assign ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether it has data to sync. - */ - hasDataToSync(assignId: number, siteId?: string): Promise { - return this.assignOfflineProvider.hasAssignOfflineData(assignId, siteId); - } - - /** - * Try to synchronize all the assignments in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllAssignments(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all assignments', this.syncAllAssignmentsFunc.bind(this), [force], siteId); - } - - /** - * Sync all assignments on a site. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected async syncAllAssignmentsFunc(siteId?: string, force?: boolean): Promise { - // Get all assignments that have offline data. - const assignIds = await this.assignOfflineProvider.getAllAssigns(siteId); - - // Try to sync all assignments. - await Promise.all(assignIds.map(async (assignId) => { - const data = force ? await this.syncAssign(assignId, siteId) : await this.syncAssignIfNeeded(assignId, siteId); - - if (!data || !data.updated) { - // Not updated. - return; - } - - this.eventsProvider.trigger(AddonModAssignSyncProvider.AUTO_SYNCED, { - assignId: assignId, - warnings: data.warnings, - gradesBlocked: data.gradesBlocked, - }, siteId); - })); - } - - /** - * Sync an assignment only if a certain time has passed since the last time. - * - * @param assignId Assign ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the assign is synced or it doesn't need to be synced. - */ - async syncAssignIfNeeded(assignId: number, siteId?: string): Promise { - const needed = await this.isSyncNeeded(assignId, siteId); - - if (needed) { - return this.syncAssign(assignId, siteId); - } - } - - /** - * Try to synchronize an assign. - * - * @param assignId Assign ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success. - */ - async syncAssign(assignId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (this.isSyncing(assignId, siteId)) { - // There's already a sync ongoing for this assign, return the promise. - return this.getOngoingSync(assignId, siteId); - } - - // Verify that assign isn't blocked. - if (this.syncProvider.isBlocked(AddonModAssignProvider.COMPONENT, assignId, siteId)) { - this.logger.error('Cannot sync assign ' + assignId + ' because it is blocked.'); - - throw new CoreSyncBlockedError(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - return this.addOngoingSync(assignId, this.performSyncAssign(assignId, siteId), siteId); - } - - /** - * Perform the assign submission. - * - * @param assignId Assign ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success. - */ - protected async performSyncAssign(assignId: number, siteId?: string): Promise { - - this.logger.debug('Try to sync assign ' + assignId + ' in site ' + siteId); - - const result: AddonModAssignSyncResult = { - warnings: [], - updated: false, - gradesBlocked: [], - }; - - // Load offline data and sync offline logs. - const promisesResults = await Promise.all([ - this.getOfflineSubmissions(assignId, siteId), - this.getOfflineGrades(assignId, siteId), - this.logHelper.syncIfNeeded(AddonModAssignProvider.COMPONENT, assignId, siteId), - ]); - - const submissions = promisesResults[0]; - const grades = promisesResults[1]; - - if (!submissions.length && !grades.length) { - // Nothing to sync. - await this.utils.ignoreErrors(this.setSyncTime(assignId, siteId)); - - return result; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - throw new Error(this.translate.instant('core.cannotconnect')); - } - - const courseId = submissions.length > 0 ? submissions[0].courseid : grades[0].courseid; - - const assign = await this.assignProvider.getAssignmentById(courseId, assignId, {siteId}); - - let promises = []; - - promises = promises.concat(submissions.map(async (submission) => { - await this.syncSubmission(assign, submission, result.warnings, siteId); - - result.updated = true; - })); - - promises = promises.concat(grades.map(async (grade) => { - try { - await this.syncSubmissionGrade(assign, grade, result.warnings, courseId, siteId); - - result.updated = true; - } catch (error) { - if (error instanceof CoreSyncBlockedError) { - // Grade blocked, but allow finish the sync. - result.gradesBlocked.push(grade.userid); - } else { - throw error; - } - } - })); - - await Promise.all(promises); - - if (result.updated) { - // Data has been sent to server. Now invalidate the WS calls. - await this.utils.ignoreErrors(this.assignProvider.invalidateContent(assign.cmid, courseId, siteId)); - } - - // Sync finished, set sync time. - await this.utils.ignoreErrors(this.setSyncTime(assignId, siteId)); - - // All done, return the result. - return result; - } - - /** - * Get offline grades to be sent. - * - * @param assignId Assign ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise with grades. - */ - protected async getOfflineGrades(assignId: number, siteId: string): Promise { - try { - const submissions = await this.assignOfflineProvider.getAssignSubmissionsGrade(assignId, siteId); - - return submissions; - } catch (error) { - // No offline data found, return empty array. - return []; - } - } - - /** - * Get offline submissions to be sent. - * - * @param assignId Assign ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise with submissions. - */ - protected async getOfflineSubmissions(assignId: number, siteId: string): Promise { - try { - const submissions = await this.assignOfflineProvider.getAssignSubmissions(assignId, siteId); - - return submissions; - } catch (error) { - // No offline data found, return empty array. - return []; - } - } - - /** - * Synchronize a submission. - * - * @param assign Assignment. - * @param offlineData Submission offline data. - * @param warnings List of warnings. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - protected async syncSubmission(assign: AddonModAssignAssign, offlineData: any, warnings: string[], siteId?: string) - : Promise { - - const userId = offlineData.userid; - const pluginData = {}; - const options = { - userId, - cmId: assign.cmid, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - const status = await this.assignProvider.getSubmissionStatus(assign.id, options); - - const submission = this.assignProvider.getSubmissionObjectFromAttempt(assign, status.lastattempt); - - if (submission.timemodified != offlineData.onlinetimemodified) { - // The submission was modified in Moodle, discard the submission. - this.addOfflineDataDeletedWarning(warnings, this.componentTranslate, assign.name, - this.translate.instant('addon.mod_assign.warningsubmissionmodified')); - - return this.deleteSubmissionData(assign, submission, offlineData, siteId); - } - - try { - // Prepare plugins data. - await Promise.all(submission.plugins.map(async (plugin) => { - await this.submissionDelegate.preparePluginSyncData(assign, submission, plugin, offlineData, pluginData, siteId); - })); - - // Now save the submission. - if (Object.keys(pluginData).length > 0) { - await this.assignProvider.saveSubmissionOnline(assign.id, pluginData, siteId); - } - - if (assign.submissiondrafts && offlineData.submitted) { - // The user submitted the assign manually. Submit it for grading. - await this.assignProvider.submitForGradingOnline(assign.id, offlineData.submissionstatement, siteId); - } - - // Submission data sent, update cached data. No need to block the user for this. - this.assignProvider.getSubmissionStatus(assign.id, options); - } catch (error) { - if (!error || !this.utils.isWebServiceError(error)) { - // Local error, reject. - throw error; - } - - // A WebService has thrown an error, this means it cannot be submitted. Discard the submission. - this.addOfflineDataDeletedWarning(warnings, this.componentTranslate, assign.name, - this.textUtils.getErrorMessageFromError(error)); - } - - // Delete the offline data. - await this.deleteSubmissionData(assign, submission, offlineData, siteId); - } - - /** - * Delete the submission offline data (not grades). - * - * @param assign Assign. - * @param submission Submission. - * @param offlineData Offline data. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected async deleteSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, offlineData: any, - siteId?: string): Promise { - - // Delete the offline data. - await this.assignOfflineProvider.deleteSubmission(assign.id, offlineData.userid, siteId); - - // Delete plugins data. - await Promise.all(submission.plugins.map(async (plugin) => { - await this.submissionDelegate.deletePluginOfflineData(assign, submission, plugin, offlineData, siteId); - })); - } - - /** - * Synchronize a submission grade. - * - * @param assign Assignment. - * @param offlineData Submission grade offline data. - * @param warnings List of warnings. - * @param courseId Course Id. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - protected async syncSubmissionGrade(assign: AddonModAssignAssign, offlineData: any, warnings: string[], courseId: number, - siteId?: string): Promise { - - const userId = offlineData.userid; - const syncId = this.getGradeSyncId(assign.id, userId); - const options = { - userId, - cmId: assign.cmid, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - // Check if this grade sync is blocked. - if (this.syncProvider.isBlocked(AddonModAssignProvider.COMPONENT, syncId, siteId)) { - this.logger.error(`Cannot sync grade for assign ${assign.id} and user ${userId} because it is blocked.!!!!`); - - throw new CoreSyncBlockedError(this.translate.instant('core.errorsyncblocked', - {$a: this.translate.instant('addon.mod_assign.syncblockedusercomponent')})); - } - - const status = await this.assignProvider.getSubmissionStatus(assign.id, options); - - const timemodified = status.feedback && (status.feedback.gradeddate || status.feedback.grade.timemodified); - - if (timemodified > offlineData.timemodified) { - // The submission grade was modified in Moodle, discard it. - this.addOfflineDataDeletedWarning(warnings, this.componentTranslate, assign.name, - this.translate.instant('addon.mod_assign.warningsubmissiongrademodified')); - - return this.assignOfflineProvider.deleteSubmissionGrade(assign.id, userId, siteId); - } - - // If grade has been modified from gradebook, do not use offline. - const grades = await this.gradesHelper.getGradeModuleItems(courseId, assign.cmid, userId, undefined, siteId, true); - - const gradeInfo = await this.courseProvider.getModuleBasicGradeInfo(assign.cmid, siteId); - - // Override offline grade and outcomes based on the gradebook data. - grades.forEach((grade) => { - if (grade.gradedategraded >= offlineData.timemodified) { - if (!grade.outcomeid && !grade.scaleid) { - if (gradeInfo && gradeInfo.scale) { - offlineData.grade = this.getSelectedScaleId(gradeInfo.scale, grade.gradeformatted); - } else { - offlineData.grade = parseFloat(grade.gradeformatted) || null; - } - } else if (grade.outcomeid && this.assignProvider.isOutcomesEditEnabled() && gradeInfo.outcomes) { - gradeInfo.outcomes.forEach((outcome, index) => { - if (outcome.scale && grade.itemnumber == index) { - offlineData.outcomes[grade.itemnumber] = this.getSelectedScaleId(outcome.scale, - outcome.selected); - } - }); - } - } - }); - - try { - // Now submit the grade. - await this.assignProvider.submitGradingFormOnline(assign.id, userId, offlineData.grade, offlineData.attemptnumber, - offlineData.addattempt, offlineData.workflowstate, offlineData.applytoall, offlineData.outcomes, - offlineData.plugindata, siteId); - - // Grades sent. Discard grades drafts. - const promises = []; - if (status.feedback && status.feedback.plugins) { - status.feedback.plugins.forEach((plugin) => { - promises.push(this.feedbackDelegate.discardPluginFeedbackData(assign.id, userId, plugin, siteId)); - }); - } - - // Update cached data. - promises.push(this.assignProvider.getSubmissionStatus(assign.id, options)); - - await Promise.all(promises); - } catch (error) { - if (!error || !this.utils.isWebServiceError(error)) { - // Local error, reject. - throw error; - } - - // A WebService has thrown an error, this means it cannot be submitted. Discard the submission. - this.addOfflineDataDeletedWarning(warnings, this.componentTranslate, assign.name, - this.textUtils.getErrorMessageFromError(error)); - } - - // Delete the offline data. - await this.assignOfflineProvider.deleteSubmissionGrade(assign.id, userId, siteId); - } -} - -export class AddonModAssignSync extends makeSingleton(AddonModAssignSyncProvider) {} diff --git a/src/addon/mod/assign/providers/assign.ts b/src/addon/mod/assign/providers/assign.ts deleted file mode 100644 index cf570beb7..000000000 --- a/src/addon/mod/assign/providers/assign.ts +++ /dev/null @@ -1,1580 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { CoreGradesProvider } from '@core/grades/providers/grades'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModAssignSubmissionDelegate } from './submission-delegate'; -import { AddonModAssignOfflineProvider } from './assign-offline'; -import { CoreSite } from '@classes/site'; -import { CoreInterceptor } from '@classes/interceptor'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some functions for assign. - */ -@Injectable() -export class AddonModAssignProvider { - static COMPONENT = 'mmaModAssign'; - static SUBMISSION_COMPONENT = 'mmaModAssignSubmission'; - static UNLIMITED_ATTEMPTS = -1; - - // Submission status. - static SUBMISSION_STATUS_NEW = 'new'; - static SUBMISSION_STATUS_REOPENED = 'reopened'; - static SUBMISSION_STATUS_DRAFT = 'draft'; - static SUBMISSION_STATUS_SUBMITTED = 'submitted'; - - // "Re-open" methods (to retry the assign). - static ATTEMPT_REOPEN_METHOD_NONE = 'none'; - static ATTEMPT_REOPEN_METHOD_MANUAL = 'manual'; - - // Grading status. - static GRADING_STATUS_GRADED = 'graded'; - static GRADING_STATUS_NOT_GRADED = 'notgraded'; - static MARKING_WORKFLOW_STATE_RELEASED = 'released'; - static NEED_GRADING = 'needgrading'; - static GRADED_FOLLOWUP_SUBMIT = 'gradedfollowupsubmit'; - - // Group submissions warnings. - static WARN_GROUPS_REQUIRED = 'warnrequired'; - static WARN_GROUPS_OPTIONAL = 'warnoptional'; - - // Events. - static SUBMISSION_SAVED_EVENT = 'addon_mod_assign_submission_saved'; - static SUBMITTED_FOR_GRADING_EVENT = 'addon_mod_assign_submitted_for_grading'; - static GRADED_EVENT = 'addon_mod_assign_graded'; - - protected ROOT_CACHE_KEY = 'mmaModAssign:'; - - protected logger; - protected gradingOfflineEnabled: {[siteId: string]: boolean} = {}; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider, - private timeUtils: CoreTimeUtilsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, - private submissionDelegate: AddonModAssignSubmissionDelegate, - private gradesProvider: CoreGradesProvider, private filepoolProvider: CoreFilepoolProvider, - private assignOffline: AddonModAssignOfflineProvider, private commentsProvider: CoreCommentsProvider, - private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModAssignProvider'); - } - - /** - * Check if the user can submit in offline. This should only be used if submissionStatus.lastattempt.cansubmit cannot - * be used (offline usage). - * This function doesn't check if the submission is empty, it should be checked before calling this function. - * - * @param assign Assignment instance. - * @param submissionStatus Submission status returned by getSubmissionStatus. - * @return Whether it can submit. - */ - canSubmitOffline(assign: any, submissionStatus: any): boolean { - if (!this.isSubmissionOpen(assign, submissionStatus)) { - return false; - } - - const userSubmission = submissionStatus.lastattempt.submission, - teamSubmission = submissionStatus.lastattempt.teamsubmission; - - if (teamSubmission) { - if (teamSubmission.status === AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) { - // The assignment submission has been completed. - return false; - } else if (userSubmission && userSubmission.status === AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) { - // The user has already clicked the submit button on the team submission. - return false; - } else if (assign.preventsubmissionnotingroup && !submissionStatus.lastattempt.submissiongroup) { - return false; - } - } else if (userSubmission) { - if (userSubmission.status === AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) { - // The assignment submission has been completed. - return false; - } - } else { - // No valid submission or team submission. - return false; - } - - // Last check is that this instance allows drafts. - return assign.submissiondrafts; - } - - /** - * Fix some submission status params. - * - * @param site Site to use. - * @param userId User Id (empty for current user). - * @param groupId Group Id (empty for all participants). - * @param isBlind If blind marking is enabled or not. - * @return Object with fixed params. - */ - protected fixSubmissionStatusParams(site: CoreSite, userId?: number, groupId?: number, isBlind?: boolean) - : {userId: number, groupId: number, isBlind: boolean} { - - return { - isBlind: !userId ? false : !!isBlind, - groupId: site.isVersionGreaterEqualThan('3.5') ? groupId || 0 : 0, - userId: userId || site.getUserId(), - }; - } - - /** - * Get an assignment by course module ID. - * - * @param courseId Course ID the assignment belongs to. - * @param cmId Assignment module ID. - * @param options Other options. - * @return Promise resolved with the assignment. - */ - getAssignment(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getAssignmentByField(courseId, 'cmid', cmId, options); - } - - /** - * Get an assigment with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the assignment is retrieved. - */ - protected getAssignmentByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - includenotenrolledcourses: 1, - }; - const preSets = { - cacheKey: this.getAssignmentCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModAssignProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_assign_get_assignments', params, preSets).catch(() => { - // In 3.6 we added a new parameter includenotenrolledcourses that could cause offline data not to be found. - // Retry again without the param to check if the request is already cached. - delete params.includenotenrolledcourses; - - return site.read('mod_assign_get_assignments', params, preSets); - }).then((response: AddonModAssignGetAssignmentsResult): any => { - // Search the assignment to return. - if (response.courses && response.courses.length) { - const assignments = response.courses[0].assignments; - - for (let i = 0; i < assignments.length; i++) { - if (assignments[i][key] == value) { - return assignments[i]; - } - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get an assignment by instance ID. - * - * @param courseId Course ID the assignment belongs to. - * @param id Assignment instance ID. - * @param options Other options. - * @return Promise resolved with the assignment. - */ - getAssignmentById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getAssignmentByField(courseId, 'id', id, options); - } - - /** - * Get cache key for assignment data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getAssignmentCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'assignment:' + courseId; - } - - /** - * Get an assignment user mapping for blind marking. - * - * @param assignId Assignment Id. - * @param userId User Id to be blinded. - * @param options Other options. - * @return Promise resolved with the user blind id. - */ - getAssignmentUserMappings(assignId: number, userId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - assignmentids: [assignId], - }; - const preSets = { - cacheKey: this.getAssignmentUserMappingsCacheKey(assignId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModAssignProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_assign_get_user_mappings', params, preSets) - .then((response: AddonModAssignGetUserMappingsResult): any => { - - // Search the user. - if (response.assignments && response.assignments.length) { - if (!userId || userId < 0) { - // User not valid, stop. - return -1; - } - - const assignment = response.assignments[0]; - - if (assignment.assignmentid == assignId) { - const mappings = assignment.mappings; - - for (let i = 0; i < mappings.length; i++) { - if (mappings[i].userid == userId) { - return mappings[i].id; - } - } - } - } else if (response.warnings && response.warnings.length) { - return Promise.reject(response.warnings[0]); - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for assignment user mappings data WS calls. - * - * @param assignId Assignment ID. - * @return Cache key. - */ - protected getAssignmentUserMappingsCacheKey(assignId: number): string { - return this.ROOT_CACHE_KEY + 'usermappings:' + assignId; - } - - /** - * Returns grade information from assign_grades for the requested assignment id - * - * @param assignId Assignment Id. - * @param options Other options. - * @return Resolved with requested info when done. - */ - getAssignmentGrades(assignId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - assignmentids: [assignId], - }; - const preSets = { - cacheKey: this.getAssignmentGradesCacheKey(assignId), - component: AddonModAssignProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_assign_get_grades', params, preSets).then((response: AddonModAssignGetGradesResult): any => { - // Search the assignment. - if (response.assignments && response.assignments.length) { - const assignment = response.assignments[0]; - - if (assignment.assignmentid == assignId) { - return assignment.grades; - } - } else if (response.warnings && response.warnings.length) { - if (response.warnings[0].warningcode == '3') { - // No grades found. - return []; - } - - return Promise.reject(response.warnings[0]); - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for assignment grades data WS calls. - * - * @param assignId Assignment ID. - * @return Cache key. - */ - protected getAssignmentGradesCacheKey(assignId: number): string { - return this.ROOT_CACHE_KEY + 'assigngrades:' + assignId; - } - - /** - * Returns the color name for a given grading status name. - * - * @param status Grading status name - * @return The color name. - */ - getSubmissionGradingStatusColor(status: string): string { - if (!status) { - return ''; - } - - if (status == AddonModAssignProvider.GRADING_STATUS_GRADED || - status == AddonModAssignProvider.MARKING_WORKFLOW_STATE_RELEASED) { - return 'success'; - } - - return 'danger'; - } - - /** - * Returns the translation id for a given grading status name. - * - * @param status Grading Status name - * @return The status translation identifier. - */ - getSubmissionGradingStatusTranslationId(status: string): string { - if (!status) { - return; - } - - if (status == AddonModAssignProvider.GRADING_STATUS_GRADED || status == AddonModAssignProvider.GRADING_STATUS_NOT_GRADED - || status == AddonModAssignProvider.GRADED_FOLLOWUP_SUBMIT) { - return 'addon.mod_assign.' + status; - } - - return 'addon.mod_assign.markingworkflowstate' + status; - } - - /** - * Get the submission object from an attempt. - * - * @param assign Assign. - * @param attempt Attempt. - * @return Submission object or null. - */ - getSubmissionObjectFromAttempt(assign: AddonModAssignAssign, attempt: AddonModAssignSubmissionAttempt) - : AddonModAssignSubmission { - - if (!attempt) { - return null; - } - - return assign.teamsubmission ? attempt.teamsubmission : attempt.submission; - } - - /** - * Get attachments of a submission plugin. - * - * @param submissionPlugin Submission plugin. - * @return Submission plugin attachments. - */ - getSubmissionPluginAttachments(submissionPlugin: any): any[] { - const files = []; - - if (submissionPlugin.fileareas) { - submissionPlugin.fileareas.forEach((filearea) => { - if (!filearea || !filearea.files) { - // No files to get. - return; - } - - filearea.files.forEach((file) => { - if (!file.filename) { - // We don't have filename, extract it from the path. - file.filename = file.filepath[0] == '/' ? file.filepath.substr(1) : file.filepath; - } - - files.push(file); - }); - }); - } - - return files; - } - - /** - * Get text of a submission plugin. - * - * @param submissionPlugin Submission plugin. - * @param keepUrls True if it should keep original URLs, false if they should be replaced. - * @return Submission text. - */ - getSubmissionPluginText(submissionPlugin: any, keepUrls?: boolean): string { - let text = ''; - - if (submissionPlugin.editorfields) { - submissionPlugin.editorfields.forEach((field) => { - text += field.text; - }); - - if (!keepUrls && submissionPlugin.fileareas && submissionPlugin.fileareas[0]) { - text = this.textUtils.replacePluginfileUrls(text, submissionPlugin.fileareas[0].files); - } - } - - return text; - } - - /** - * Get an assignment submissions. - * - * @param assignId Assignment id. - * @param options Other options. - * @return Promise resolved when done. - */ - getSubmissions(assignId: number, options: CoreCourseCommonModWSOptions = {}) - : Promise<{canviewsubmissions: boolean, submissions?: AddonModAssignSubmission[]}> { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - assignmentids: [assignId], - }; - const preSets = { - cacheKey: this.getSubmissionsCacheKey(assignId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModAssignProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_assign_get_submissions', params, preSets) - .then((response: AddonModAssignGetSubmissionsResult): any => { - - // Check if we can view submissions, with enough permissions. - if (response.warnings.length > 0 && response.warnings[0].warningcode == '1') { - return {canviewsubmissions: false}; - } - - if (response.assignments && response.assignments.length) { - return { - canviewsubmissions: true, - submissions: response.assignments[0].submissions - }; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for assignment submissions data WS calls. - * - * @param assignId Assignment id. - * @return Cache key. - */ - protected getSubmissionsCacheKey(assignId: number): string { - return this.ROOT_CACHE_KEY + 'submissions:' + assignId; - } - - /** - * Get information about an assignment submission status for a given user. - * - * @param assignId Assignment instance id. - * @param options Other options. - * @return Promise always resolved with the user submission status. - */ - getSubmissionStatus(assignId: number, options: AddonModAssignSubmissionStatusOptions = {}) - : Promise { - - if (options.filter === undefined || options.filter === null) { - options.filter = true; - } - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const fixedParams = this.fixSubmissionStatusParams(site, options.userId, options.groupId, options.isBlind); - - const params = { - assignid: assignId, - userid: fixedParams.userId, - }; - if (fixedParams.groupId) { - params['groupid'] = fixedParams.groupId; - } - - const preSets = { - cacheKey: this.getSubmissionStatusCacheKey(assignId, fixedParams.userId, fixedParams.groupId, - fixedParams.isBlind), - getCacheUsingCacheKey: true, // We use the cache key to take isBlind into account. - filter: options.filter, - rewriteurls: options.filter, - component: AddonModAssignProvider.COMPONENT, - componentId: options.cmId, - // Don't cache when getting text without filters. - // @todo Change this to support offline editing. - saveToCache: options.filter, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_assign_get_submission_status', params, preSets); - }); - } - - /** - * Get information about an assignment submission status for a given user. - * If the data doesn't include the user submission, retry ignoring cache. - * - * @param assign Assignment. - * @param options Other options. - * @return Promise always resolved with the user submission status. - */ - getSubmissionStatusWithRetry(assign: any, options: AddonModAssignSubmissionStatusOptions = {}) - : Promise { - options.cmId = options.cmId || assign.cmid; - - return this.getSubmissionStatus(assign.id, options).then((response) => { - const userSubmission = this.getSubmissionObjectFromAttempt(assign, response.lastattempt); - - if (!userSubmission) { - // Try again, ignoring cache. - const newOptions = { - ...options, // Include all the original options. - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - }; - - return this.getSubmissionStatus(assign.id, newOptions).catch(() => { - // Error, return the first result even if it doesn't have the user submission. - return response; - }); - } - - return response; - }); - } - - /** - * Get cache key for get submission status data WS calls. - * - * @param assignId Assignment instance id. - * @param userId User id (empty for current user). - * @param groupId Group Id (empty for all participants). - * @param isBlind If blind marking is enabled or not. - * @return Cache key. - */ - protected getSubmissionStatusCacheKey(assignId: number, userId: number, groupId?: number, isBlind?: boolean): string { - return this.getSubmissionsCacheKey(assignId) + ':' + userId + ':' + (isBlind ? 1 : 0) + ':' + groupId; - } - - /** - * Returns the color name for a given status name. - * - * @param status Status name - * @return The color name. - */ - getSubmissionStatusColor(status: string): string { - switch (status) { - case 'submitted': - return 'success'; - case 'draft': - return 'info'; - case 'new': - case 'noattempt': - case 'noonlinesubmissions': - case 'nosubmission': - case 'gradedfollowupsubmit': - return 'danger'; - default: - return 'light'; - } - } - - /** - * Given a list of plugins, returns the plugin names that aren't supported for editing. - * - * @param plugins Plugins to check. - * @return Promise resolved with unsupported plugin names. - */ - getUnsupportedEditPlugins(plugins: any[]): Promise { - const notSupported = [], - promises = []; - - plugins.forEach((plugin) => { - promises.push(this.submissionDelegate.isPluginSupportedForEdit(plugin.type).then((enabled) => { - if (!enabled) { - notSupported.push(plugin.name); - } - })); - }); - - return Promise.all(promises).then(() => { - return notSupported; - }); - } - - /** - * List the participants for a single assignment, with some summary info about their submissions. - * - * @param assignId Assignment id. - * @param groupId Group id. If not defined, 0. - * @param options Other options. - * @return Promise resolved with the list of participants and summary of submissions. - */ - listParticipants(assignId: number, groupId?: number, options: CoreCourseCommonModWSOptions = {}) - : Promise { - - groupId = groupId || 0; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - if (!site.wsAvailable('mod_assign_list_participants')) { - // Silently fail if is not available. (needs Moodle version >= 3.2) - return Promise.reject(null); - } - - const params = { - assignid: assignId, - groupid: groupId, - filter: '', - }; - const preSets = { - cacheKey: this.listParticipantsCacheKey(assignId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModAssignProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_assign_list_participants', params, preSets); - }); - } - - /** - * Get cache key for assignment list participants data WS calls. - * - * @param assignId Assignment id. - * @param groupId Group id. - * @return Cache key. - */ - protected listParticipantsCacheKey(assignId: number, groupId: number): string { - return this.listParticipantsPrefixCacheKey(assignId) + ':' + groupId; - } - - /** - * Get prefix cache key for assignment list participants data WS calls. - * - * @param assignId Assignment id. - * @return Cache key. - */ - protected listParticipantsPrefixCacheKey(assignId: number): string { - return this.ROOT_CACHE_KEY + 'participants:' + assignId; - } - - /** - * Invalidates all submission status data. - * - * @param assignId Assignment instance id. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllSubmissionData(assignId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getSubmissionsCacheKey(assignId)); - }); - } - - /** - * Invalidates assignment data WS calls. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAssignmentData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAssignmentCacheKey(courseId)); - }); - } - - /** - * Invalidates assignment user mappings data WS calls. - * - * @param assignId Assignment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAssignmentUserMappingsData(assignId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAssignmentUserMappingsCacheKey(assignId)); - }); - } - - /** - * Invalidates assignment grades data WS calls. - * - * @param assignId Assignment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAssignmentGradesData(assignId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAssignmentGradesCacheKey(assignId)); - }); - } - - /** - * Invalidate the prefetched content except files. - * To invalidate files, use AddonModAssignProvider.invalidateFiles. - * - * @param moduleId The module ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.getAssignment(courseId, moduleId, {siteId}).then((assign) => { - const promises = []; - - // Do not invalidate assignment data before getting assignment info, we need it! - promises.push(this.invalidateAllSubmissionData(assign.id, siteId)); - promises.push(this.invalidateAssignmentUserMappingsData(assign.id, siteId)); - promises.push(this.invalidateAssignmentGradesData(assign.id, siteId)); - promises.push(this.invalidateListParticipantsData(assign.id, siteId)); - promises.push(this.commentsProvider.invalidateCommentsByInstance('module', assign.id, siteId)); - promises.push(this.invalidateAssignmentData(courseId, siteId)); - promises.push(this.gradesProvider.invalidateAllCourseGradesData(courseId)); - - return Promise.all(promises); - }); - } - - /** - * Invalidate the prefetched files. - * - * @param moduleId The module ID. - * @return Promise resolved when the files are invalidated. - */ - invalidateFiles(moduleId: number): Promise { - return this.filepoolProvider.invalidateFilesByComponent(this.sitesProvider.getCurrentSiteId(), - AddonModAssignProvider.COMPONENT, moduleId); - } - - /** - * Invalidates assignment submissions data WS calls. - * - * @param assignId Assignment instance id. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSubmissionData(assignId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getSubmissionsCacheKey(assignId)); - }); - } - - /** - * Invalidates submission status data. - * - * @param assignId Assignment instance id. - * @param userId User id (empty for current user). - * @param groupId Group Id (empty for all participants). - * @param isBlind Whether blind marking is enabled or not. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSubmissionStatusData(assignId: number, userId?: number, groupId?: number, isBlind?: boolean, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const fixedParams = this.fixSubmissionStatusParams(site, userId, groupId, isBlind); - - return site.invalidateWsCacheForKey(this.getSubmissionStatusCacheKey(assignId, fixedParams.userId, - fixedParams.groupId, fixedParams.isBlind)); - }); - } - - /** - * Invalidates assignment participants data. - * - * @param assignId Assignment instance id. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateListParticipantsData(assignId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.listParticipantsPrefixCacheKey(assignId)); - }); - } - - /** - * Convenience function to check if grading offline is enabled. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether grading offline is enabled. - */ - protected isGradingOfflineEnabled(siteId?: string): Promise { - if (typeof this.gradingOfflineEnabled[siteId] != 'undefined') { - return Promise.resolve(this.gradingOfflineEnabled[siteId]); - } - - return this.gradesProvider.isGradeItemsAvalaible(siteId).then((enabled) => { - this.gradingOfflineEnabled[siteId] = enabled; - - return enabled; - }); - } - - /** - * Outcomes only can be edited if mod_assign_submit_grading_form is avalaible. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if outcomes edit is enabled, rejected or resolved with false otherwise. - * @since 3.2 - */ - isOutcomesEditEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('mod_assign_submit_grading_form'); - }); - } - - /** - * Check if assignments plugin is enabled in a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Whether the plugin is enabled. - */ - isPluginEnabled(siteId?: string): boolean { - return true; - } - - /** - * Check if a submission is open. This function is based on Moodle's submissions_open. - * - * @param assign Assignment instance. - * @param submissionStatus Submission status returned by getSubmissionStatus. - * @return Whether submission is open. - */ - isSubmissionOpen(assign: any, submissionStatus: any): boolean { - if (!assign || !submissionStatus) { - return false; - } - - const time = this.timeUtils.timestamp(), - lastAttempt = submissionStatus.lastattempt, - submission = this.getSubmissionObjectFromAttempt(assign, lastAttempt); - - let dateOpen = true, - finalDate; - - if (assign.cutoffdate) { - finalDate = assign.cutoffdate; - } - - if (lastAttempt && lastAttempt.locked) { - return false; - } - - // User extensions. - if (finalDate) { - if (lastAttempt && lastAttempt.extensionduedate) { - // Extension can be before cut off date. - if (lastAttempt.extensionduedate > finalDate) { - finalDate = lastAttempt.extensionduedate; - } - } - } - - if (finalDate) { - dateOpen = assign.allowsubmissionsfromdate <= time && time <= finalDate; - } else { - dateOpen = assign.allowsubmissionsfromdate <= time; - } - - if (!dateOpen) { - return false; - } - - if (submission) { - if (assign.submissiondrafts && submission.status == AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) { - // Drafts are tracked and the student has submitted the assignment. - return false; - } - } - - return true; - } - - /** - * Report an assignment submission as being viewed. - * - * @param assignId Assignment ID. - * @param name Name of the assign. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logSubmissionView(assignId: number, name?: string, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - assignid: assignId - }; - - return this.logHelper.logSingle('mod_assign_view_submission_status', params, AddonModAssignProvider.COMPONENT, - assignId, name, 'assign', {}, siteId); - }); - } - - /** - * Report an assignment grading table is being viewed. - * - * @param assignId Assignment ID. - * @param name Name of the assign. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logGradingView(assignId: number, name?: string, siteId?: string): Promise { - const params = { - assignid: assignId - }; - - return this.logHelper.logSingle('mod_assign_view_grading_table', params, AddonModAssignProvider.COMPONENT, assignId, - name, 'assign', {}, siteId); - } - - /** - * Report an assign as being viewed. - * - * @param assignId Assignment ID. - * @param name Name of the assign. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(assignId: number, name?: string, siteId?: string): Promise { - const params = { - assignid: assignId - }; - - return this.logHelper.logSingle('mod_assign_view_assign', params, AddonModAssignProvider.COMPONENT, assignId, name, - 'assign', {}, siteId); - } - - /** - * Returns if a submissions needs to be graded. - * - * @param submission Submission. - * @param assignId Assignment ID. - * @return Promise resolved with boolean: whether it needs to be graded or not. - */ - needsSubmissionToBeGraded(submission: any, assignId: number): Promise { - if (!submission.gradingstatus) { - // This should not happen, but it's better to show rather than not showing any of the submissions. - return Promise.resolve(true); - } - - if (submission.gradingstatus != AddonModAssignProvider.GRADING_STATUS_GRADED && - submission.gradingstatus != AddonModAssignProvider.MARKING_WORKFLOW_STATE_RELEASED) { - // Not graded. - return Promise.resolve(true); - } - - // We need more data to decide that. - return this.getSubmissionStatus(assignId, { - userId: submission.submitid, - isBlind: !!submission.blindid, - }).then((response) => { - if (!response.feedback || !response.feedback.gradeddate) { - // Not graded. - return true; - } - - // Submitted after grading? - return response.feedback.gradeddate < submission.timemodified; - }); - } - - /** - * Save current user submission for a certain assignment. - * - * @param assignId Assign ID. - * @param courseId Course ID the assign belongs to. - * @param pluginData Data to save. - * @param allowOffline Whether to allow offline usage. - * @param timemodified The time the submission was last modified in online. - * @param allowsDrafts Whether the assignment allows submission drafts. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if sent to server, resolved with false if stored in offline. - */ - saveSubmission(assignId: number, courseId: number, pluginData: any, allowOffline: boolean, timemodified: number, - allowsDrafts?: boolean, userId?: number, siteId?: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Function to store the submission to be synchronized later. - const storeOffline = (): Promise => { - return this.assignOffline.saveSubmission(assignId, courseId, pluginData, timemodified, !allowsDrafts, userId, siteId) - .then(() => { - return false; - }); - }; - - if (allowOffline && !this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - // If there's already a submission to be sent to the server, discard it first. - return this.assignOffline.deleteSubmission(assignId, userId, siteId).then(() => { - return this.saveSubmissionOnline(assignId, pluginData, siteId).then(() => { - return true; - }).catch((error) => { - if (allowOffline && error && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Save current user submission for a certain assignment. It will fail if offline or cannot connect. - * - * @param assignId Assign ID. - * @param pluginData Data to save. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when saved, rejected otherwise. - */ - saveSubmissionOnline(assignId: number, pluginData: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - assignmentid: assignId, - plugindata: pluginData - }; - - return site.write('mod_assign_save_submission', params).then((warnings: CoreWSExternalWarning[]) => { - if (warnings && warnings.length) { - // The WebService returned warnings, reject. - return Promise.reject(warnings[0]); - } - }); - }); - } - - /** - * Submit the current user assignment for grading. - * - * @param assignId Assign ID. - * @param courseId Course ID the assign belongs to. - * @param acceptStatement True if submission statement is accepted, false otherwise. - * @param timemodified The time the submission was last modified in online. - * @param forceOffline True to always mark it in offline. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if sent to server, resolved with false if stored in offline. - */ - submitForGrading(assignId: number, courseId: number, acceptStatement: boolean, timemodified: number, forceOffline?: boolean, - siteId?: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Function to store the submission to be synchronized later. - const storeOffline = (): Promise => { - return this.assignOffline.markSubmitted(assignId, courseId, true, acceptStatement, timemodified, undefined, siteId) - .then(() => { - return false; - }); - }; - - if (forceOffline || !this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - // If there's already a submission to be sent to the server, discard it first. - return this.assignOffline.deleteSubmission(assignId, undefined, siteId).then(() => { - return this.submitForGradingOnline(assignId, acceptStatement, siteId).then(() => { - return true; - }).catch((error) => { - if (error && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Submit the current user assignment for grading. It will fail if offline or cannot connect. - * - * @param assignId Assign ID. - * @param acceptStatement True if submission statement is accepted, false otherwise. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when submitted, rejected otherwise. - */ - submitForGradingOnline(assignId: number, acceptStatement: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - assignmentid: assignId, - acceptsubmissionstatement: acceptStatement ? 1 : 0 - }; - - return site.write('mod_assign_submit_for_grading', params).then((warnings: CoreWSExternalWarning[]) => { - if (warnings && warnings.length) { - // The WebService returned warnings, reject. - return Promise.reject(warnings[0]); - } - }); - }); - } - - /** - * Submit the grading for the current user and assignment. It will use old or new WS depending on availability. - * - * @param assignId Assign ID. - * @param userId User ID. - * @param courseId Course ID the assign belongs to. - * @param grade Grade to submit. - * @param attemptNumber Number of the attempt being graded. - * @param addAttempt Admit the user to attempt again. - * @param workflowState Next workflow State. - * @param applyToAll If it's a team submission, whether the grade applies to all group members. - * @param outcomes Object including all outcomes values. If empty, any of them will be sent. - * @param pluginData Feedback plugin data to save. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if sent to server, resolved with false if stored offline. - */ - submitGradingForm(assignId: number, userId: number, courseId: number, grade: number, attemptNumber: number, addAttempt: boolean, - workflowState: string, applyToAll: boolean, outcomes: any, pluginData: any, siteId?: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Function to store the grading to be synchronized later. - const storeOffline = (): Promise => { - return this.assignOffline.submitGradingForm(assignId, userId, courseId, grade, attemptNumber, addAttempt, workflowState, - applyToAll, outcomes, pluginData, siteId).then(() => { - return false; - }); - }; - - // Grading offline is only allowed if WS of grade items is enabled to avoid inconsistency. - return this.isGradingOfflineEnabled(siteId).then((enabled) => { - if (!enabled) { - return this.submitGradingFormOnline(assignId, userId, grade, attemptNumber, addAttempt, workflowState, - applyToAll, outcomes, pluginData, siteId).then(() => { - - return true; - }); - } - - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - // If there's already a grade to be sent to the server, discard it first. - return this.assignOffline.deleteSubmissionGrade(assignId, userId, siteId).then(() => { - return this.submitGradingFormOnline(assignId, userId, grade, attemptNumber, addAttempt, workflowState, applyToAll, - outcomes, pluginData, siteId).then(() => { - return true; - }).catch((error) => { - if (error && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error, reject. - return Promise.reject(error); - } - }); - }); - }); - } - - /** - * Submit the grading for the current user and assignment. It will use old or new WS depending on availability. - * It will fail if offline or cannot connect. - * - * @param assignId Assign ID. - * @param userId User ID. - * @param grade Grade to submit. - * @param attemptNumber Number of the attempt being graded. - * @param addAttempt Allow the user to attempt again. - * @param workflowState Next workflow State. - * @param applyToAll If it's a team submission, if the grade applies to all group members. - * @param outcomes Object including all outcomes values. If empty, any of them will be sent. - * @param pluginData Feedback plugin data to save. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when submitted, rejected otherwise. - */ - submitGradingFormOnline(assignId: number, userId: number, grade: number, attemptNumber: number, addAttempt: boolean, - workflowState: string, applyToAll: boolean, outcomes: any, pluginData: any, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - if (site.wsAvailable('mod_assign_submit_grading_form')) { - // WS available @since 3.2. - - const jsonData = { - grade: grade, - attemptnumber: attemptNumber, - addattempt: addAttempt ? 1 : 0, - workflowstate: workflowState, - applytoall: applyToAll ? 1 : 0 - }; - - for (const index in outcomes) { - jsonData['outcome_' + index + '[' + userId + ']'] = outcomes[index]; - } - - for (const index in pluginData) { - jsonData[index] = pluginData[index]; - } - - const serialized = CoreInterceptor.serialize(jsonData, true), - params = { - assignmentid: assignId, - userid: userId, - jsonformdata: JSON.stringify(serialized) - }; - - return site.write('mod_assign_submit_grading_form', params).then((warnings: CoreWSExternalWarning[]) => { - if (warnings && warnings.length) { - // The WebService returned warnings, reject. - return Promise.reject(warnings[0]); - } - }); - } else { - // WS not available, fallback to save_grade. - - const params = { - assignmentid: assignId, - userid: userId, - grade: grade, - attemptnumber: attemptNumber, - addattempt: addAttempt ? 1 : 0, - workflowstate: workflowState, - applytoall: applyToAll ? 1 : 0, - plugindata: pluginData - }, - preSets = { - responseExpected: false - }; - - return site.write('mod_assign_save_grade', params, preSets); - } - }); - } -} - -/** - * Options to pass to get submission status. - */ -export type AddonModAssignSubmissionStatusOptions = CoreCourseCommonModWSOptions & { - userId?: number; // User Id (empty for current user). - groupId?: number; // Group Id (empty for all participants). - isBlind?: boolean; // If blind marking is enabled or not. - filter?: boolean; // True to filter WS response and rewrite URLs, false otherwise. Defaults to true. -}; - -/** - * Assign data returned by mod_assign_get_assignments. - */ -export type AddonModAssignAssign = { - id: number; // Assignment id. - cmid: number; // Course module id. - course: number; // Course id. - name: string; // Assignment name. - nosubmissions: number; // No submissions. - submissiondrafts: number; // Submissions drafts. - sendnotifications: number; // Send notifications. - sendlatenotifications: number; // Send notifications. - sendstudentnotifications: number; // Send student notifications (default). - duedate: number; // Assignment due date. - allowsubmissionsfromdate: number; // Allow submissions from date. - grade: number; // Grade type. - timemodified: number; // Last time assignment was modified. - completionsubmit: number; // If enabled, set activity as complete following submission. - cutoffdate: number; // Date after which submission is not accepted without an extension. - gradingduedate?: number; // @since 3.3. The expected date for marking the submissions. - teamsubmission: number; // If enabled, students submit as a team. - requireallteammemberssubmit: number; // If enabled, all team members must submit. - teamsubmissiongroupingid: number; // The grouping id for the team submission groups. - blindmarking: number; // If enabled, hide identities until reveal identities actioned. - hidegrader?: number; // @since 3.7. If enabled, hide grader to student. - revealidentities: number; // Show identities for a blind marking assignment. - attemptreopenmethod: string; // Method used to control opening new attempts. - maxattempts: number; // Maximum number of attempts allowed. - markingworkflow: number; // Enable marking workflow. - markingallocation: number; // Enable marking allocation. - requiresubmissionstatement: number; // Student must accept submission statement. - preventsubmissionnotingroup?: number; // @since 3.2. Prevent submission not in group. - submissionstatement?: string; // @since 3.2. Submission statement formatted. - submissionstatementformat?: number; // @since 3.2. Submissionstatement format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - configs: AddonModAssignConfig[]; // Configuration settings. - intro?: string; // Assignment intro, not allways returned because it deppends on the activity configuration. - introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles?: CoreWSExternalFile[]; // @since 3.2. - introattachments?: CoreWSExternalFile[]; -}; - -/** - * Config setting in an assign. - */ -export type AddonModAssignConfig = { - id?: number; // Assign_plugin_config id. - assignment?: number; // Assignment id. - plugin: string; // Plugin. - subtype: string; // Subtype. - name: string; // Name. - value: string; // Value. -}; - -/** - * Grade of an assign, returned by mod_assign_get_grades. - */ -export type AddonModAssignGrade = { - id: number; // Grade id. - assignment?: number; // Assignment id. - userid: number; // Student id. - attemptnumber: number; // Attempt number. - timecreated: number; // Grade creation time. - timemodified: number; // Grade last modified time. - grader: number; // Grader, -1 if grader is hidden. - grade: string; // Grade. - gradefordisplay?: string; // Grade rendered into a format suitable for display. -}; - -/** - * Assign submission returned by mod_assign_get_submissions. - */ -export type AddonModAssignSubmission = { - id: number; // Submission id. - userid: number; // Student id. - attemptnumber: number; // Attempt number. - timecreated: number; // Submission creation time. - timemodified: number; // Submission last modified time. - status: string; // Submission status. - groupid: number; // Group id. - assignment?: number; // Assignment id. - latest?: number; // Latest attempt. - plugins?: AddonModAssignPlugin[]; // Plugins. - gradingstatus?: string; // @since 3.2. Grading status. -}; - -/** - * Assign plugin. - */ -export type AddonModAssignPlugin = { - type: string; // Submission plugin type. - name: string; // Submission plugin name. - fileareas?: { // Fileareas. - area: string; // File area. - files?: CoreWSExternalFile[]; - }[]; - editorfields?: { // Editorfields. - name: string; // Field name. - description: string; // Field description. - text: string; // Field value. - format: number; // Text format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - }[]; -}; - -/** - * Grading summary of an assign submission. - */ -export type AddonModAssignSubmissionGradingSummary = { - participantcount: number; // Number of users who can submit. - submissiondraftscount: number; // Number of submissions in draft status. - submissionsenabled: boolean; // Whether submissions are enabled or not. - submissionssubmittedcount: number; // Number of submissions in submitted status. - submissionsneedgradingcount: number; // Number of submissions that need grading. - warnofungroupedusers: string | boolean; // Whether we need to warn people about groups. -}; - -/** - * Attempt of an assign submission. - */ -export type AddonModAssignSubmissionAttempt = { - submission?: AddonModAssignSubmission; // Submission info. - teamsubmission?: AddonModAssignSubmission; // Submission info. - submissiongroup?: number; // The submission group id (for group submissions only). - submissiongroupmemberswhoneedtosubmit?: number[]; // List of users who still need to submit (for group submissions only). - submissionsenabled: boolean; // Whether submissions are enabled or not. - locked: boolean; // Whether new submissions are locked. - graded: boolean; // Whether the submission is graded. - canedit: boolean; // Whether the user can edit the current submission. - caneditowner?: boolean; // @since 3.2. Whether the owner of the submission can edit it. - cansubmit: boolean; // Whether the user can submit. - extensionduedate: number; // Extension due date. - blindmarking: boolean; // Whether blind marking is enabled. - gradingstatus: string; // Grading status. - usergroups: number[]; // User groups in the course. -}; - -/** - * Previous attempt of an assign submission. - */ -export type AddonModAssignSubmissionPreviousAttempt = { - attemptnumber: number; // Attempt number. - submission?: AddonModAssignSubmission; // Submission info. - grade?: AddonModAssignGrade; // Grade information. - feedbackplugins?: AddonModAssignPlugin[]; // Feedback info. -}; - -/** - * Feedback of an assign submission. - */ -export type AddonModAssignSubmissionFeedback = { - grade: AddonModAssignGrade; // Grade information. - gradefordisplay: string; // Grade rendered into a format suitable for display. - gradeddate: number; // The date the user was graded. - plugins?: AddonModAssignPlugin[]; // Plugins info. -}; - -/** - * Participant returned by mod_assign_list_participants. - */ -export type AddonModAssignParticipant = { - id: number; // ID of the user. - username?: string; // The username. - firstname?: string; // The first name(s) of the user. - lastname?: string; // The family name of the user. - fullname: string; // The fullname of the user. - email?: string; // Email address. - address?: string; // Postal address. - phone1?: string; // Phone 1. - phone2?: string; // Phone 2. - icq?: string; // Icq number. - skype?: string; // Skype id. - yahoo?: string; // Yahoo id. - aim?: string; // Aim id. - msn?: string; // Msn number. - department?: string; // Department. - institution?: string; // Institution. - idnumber?: string; // The idnumber of the user. - interests?: string; // User interests (separated by commas). - firstaccess?: number; // First access to the site (0 if never). - lastaccess?: number; // Last access to the site (0 if never). - suspended?: boolean; // @since 3.2. Suspend user account, either false to enable user login or true to disable it. - description?: string; // User profile description. - descriptionformat?: number; // Int format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - city?: string; // Home city of the user. - url?: string; // URL of the user. - country?: string; // Home country code of the user, such as AU or CZ. - profileimageurlsmall?: string; // User image profile URL - small version. - profileimageurl?: string; // User image profile URL - big version. - customfields?: { // User custom fields (also known as user profile fields). - type: string; // The type of the custom field - text field, checkbox... - value: string; // The value of the custom field. - name: string; // The name of the custom field. - shortname: string; // The shortname of the custom field - to be able to build the field class in the code. - }[]; - preferences?: { // Users preferences. - name: string; // The name of the preferences. - value: string; // The value of the preference. - }[]; - recordid?: number; // @since 3.7. Record id. - groups?: { // User groups. - id: number; // Group id. - name: string; // Group name. - description: string; // Group description. - }[]; - roles?: { // User roles. - roleid: number; // Role id. - name: string; // Role name. - shortname: string; // Role shortname. - sortorder: number; // Role sortorder. - }[]; - enrolledcourses?: { // Courses where the user is enrolled - limited by which courses the user is able to see. - id: number; // Id of the course. - fullname: string; // Fullname of the course. - shortname: string; // Shortname of the course. - }[]; - submitted: boolean; // Have they submitted their assignment. - requiregrading: boolean; // Is their submission waiting for grading. - grantedextension?: boolean; // @since 3.3. Have they been granted an extension. - groupid?: number; // For group assignments this is the group id. - groupname?: string; // For group assignments this is the group name. -}; - -/** - * Result of WS mod_assign_get_assignments. - */ -export type AddonModAssignGetAssignmentsResult = { - courses: { // List of courses. - id: number; // Course id. - fullname: string; // Course full name. - shortname: string; // Course short name. - timemodified: number; // Last time modified. - assignments: AddonModAssignAssign[]; // Assignment info. - }[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_assign_get_user_mappings. - */ -export type AddonModAssignGetUserMappingsResult = { - assignments: { // List of assign user mapping data. - assignmentid: number; // Assignment id. - mappings: { - id: number; // User mapping id. - userid: number; // Student id. - }[]; - }[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_assign_get_grades. - */ -export type AddonModAssignGetGradesResult = { - assignments: { // List of assignment grade information. - assignmentid: number; // Assignment id. - grades: AddonModAssignGrade[]; - }[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_assign_get_submissions. - */ -export type AddonModAssignGetSubmissionsResult = { - assignments: { // Assignment submissions. - assignmentid: number; // Assignment id. - submissions: AddonModAssignSubmission[]; - }[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_assign_get_submission_status. - */ -export type AddonModAssignGetSubmissionStatusResult = { - gradingsummary?: AddonModAssignSubmissionGradingSummary; // Grading information. - lastattempt?: AddonModAssignSubmissionAttempt; // Last attempt information. - feedback?: AddonModAssignSubmissionFeedback; // Feedback for the last attempt. - previousattempts?: AddonModAssignSubmissionPreviousAttempt[]; // List all the previous attempts did by the user. - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/assign/providers/default-feedback-handler.ts b/src/addon/mod/assign/providers/default-feedback-handler.ts deleted file mode 100644 index 28b34b4cf..000000000 --- a/src/addon/mod/assign/providers/default-feedback-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModAssignBaseFeedbackHandler } from '../classes/base-feedback-handler'; - -/** - * Default handler used when a feedback plugin doesn't have a specific implementation. - */ -@Injectable() -export class AddonModAssignDefaultFeedbackHandler extends AddonModAssignBaseFeedbackHandler { - name = 'AddonModAssignDefaultFeedbackHandler'; - type = 'default'; - - constructor(protected translate: TranslateService) { - super(translate); - } -} diff --git a/src/addon/mod/assign/providers/default-submission-handler.ts b/src/addon/mod/assign/providers/default-submission-handler.ts deleted file mode 100644 index 2e3ea3059..000000000 --- a/src/addon/mod/assign/providers/default-submission-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModAssignBaseSubmissionHandler } from '../classes/base-submission-handler'; - -/** - * Default handler used when a submission plugin doesn't have a specific implementation. - */ -@Injectable() -export class AddonModAssignDefaultSubmissionHandler extends AddonModAssignBaseSubmissionHandler { - name = 'AddonModAssignDefaultSubmissionHandler'; - type = 'default'; - - constructor(protected translate: TranslateService) { - super(translate); - } // Nothing to do. -} diff --git a/src/addon/mod/assign/providers/feedback-delegate.ts b/src/addon/mod/assign/providers/feedback-delegate.ts deleted file mode 100644 index a548038c1..000000000 --- a/src/addon/mod/assign/providers/feedback-delegate.ts +++ /dev/null @@ -1,310 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; -import { AddonModAssignDefaultFeedbackHandler } from './default-feedback-handler'; -import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from './assign'; - -/** - * Interface that all feedback handlers must implement. - */ -export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler { - - /** - * Name of the type of feedback the handler supports. E.g. 'file'. - */ - type: string; - - /** - * Discard the draft data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - discardDraft?(assignId: number, userId: number, siteId?: string): void | Promise; - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent?(injector: Injector, plugin: AddonModAssignPlugin): any | Promise; - - /** - * Return the draft saved data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return Data (or promise resolved with the data). - */ - getDraft?(assignId: number, userId: number, siteId?: string): any | Promise; - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise; - - /** - * Get a readable name to use for the plugin. - * - * @param plugin The plugin object. - * @return The plugin name. - */ - getPluginName?(plugin: AddonModAssignPlugin): string; - - /** - * Check if the feedback data has changed for this plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the feedback. - * @param userId User ID of the submission. - * @return Boolean (or promise resolved with boolean): whether the data has changed. - */ - hasDataChanged?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, userId: number): boolean | Promise; - - /** - * Check whether the plugin has draft data stored. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return Boolean or promise resolved with boolean: whether the plugin has draft data. - */ - hasDraftData?(assignId: number, userId: number, siteId?: string): boolean | Promise; - - /** - * Prefetch any required data for the plugin. - * This should NOT prefetch files. Files to be prefetched should be returned by the getPluginFiles function. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetch?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): Promise; - - /** - * Prepare and add to pluginData the data to send to the server based on the draft data saved. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareFeedbackData?(assignId: number, userId: number, plugin: AddonModAssignPlugin, pluginData: any, - siteId?: string): void | Promise; - - /** - * Save draft data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param data The data to save. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - saveDraft?(assignId: number, userId: number, plugin: AddonModAssignPlugin, data: any, siteId?: string) - : void | Promise; -} - -/** - * Delegate to register plugins for assign feedback. - */ -@Injectable() -export class AddonModAssignFeedbackDelegate extends CoreDelegate { - - protected handlerNameProperty = 'type'; - - constructor(logger: CoreLoggerProvider, sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, - protected defaultHandler: AddonModAssignDefaultFeedbackHandler) { - super('AddonModAssignFeedbackDelegate', logger, sitesProvider, eventsProvider); - } - - /** - * Discard the draft data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - discardPluginFeedbackData(assignId: number, userId: number, plugin: AddonModAssignPlugin, siteId?: string) - : Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'discardDraft', [assignId, userId, siteId])); - } - - /** - * Get the component to use for a certain feedback plugin. - * - * @param injector Injector. - * @param plugin The plugin object. - * @return Promise resolved with the component to use, undefined if not found. - */ - getComponentForPlugin(injector: Injector, plugin: AddonModAssignPlugin): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getComponent', [injector, plugin])); - } - - /** - * Return the draft saved data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the draft data. - */ - getPluginDraftData(assignId: number, userId: number, plugin: AddonModAssignPlugin, siteId?: string) - : Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getDraft', [assignId, userId, siteId])); - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getPluginFiles', [assign, submission, plugin, siteId])); - } - - /** - * Get a readable name to use for a certain feedback plugin. - * - * @param plugin Plugin to get the name for. - * @return Human readable name. - */ - getPluginName(plugin: AddonModAssignPlugin): string { - return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]); - } - - /** - * Check if the feedback data has changed for a certain plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the feedback. - * @param userId User ID of the submission. - * @return Promise resolved with true if data has changed, resolved with false otherwise. - */ - hasPluginDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, userId: number): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDataChanged', - [assign, submission, plugin, inputData, userId])); - } - - /** - * Check whether the plugin has draft data stored. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if it has draft data. - */ - hasPluginDraftData(assignId: number, userId: number, plugin: AddonModAssignPlugin, siteId?: string) - : Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDraftData', [assignId, userId, siteId])); - } - - /** - * Check if a feedback plugin is supported. - * - * @param pluginType Type of the plugin. - * @return Whether it's supported. - */ - isPluginSupported(pluginType: string): boolean { - return this.hasHandler(pluginType, true); - } - - /** - * Prefetch any required data for a feedback plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetch(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, plugin: AddonModAssignPlugin, - siteId?: string): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prefetch', [assign, submission, plugin, siteId])); - } - - /** - * Prepare and add to pluginData the data to submit for a certain feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data has been gathered. - */ - preparePluginFeedbackData(assignId: number, userId: number, plugin: AddonModAssignPlugin, pluginData: any, - siteId?: string): Promise { - - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareFeedbackData', - [assignId, userId, plugin, pluginData, siteId])); - } - - /** - * Save draft data of the feedback plugin. - * - * @param assignId The assignment ID. - * @param userId User ID. - * @param plugin The plugin object. - * @param inputData Data to save. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data has been saved. - */ - saveFeedbackDraft(assignId: number, userId: number, plugin: AddonModAssignPlugin, inputData: any, - siteId?: string): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'saveDraft', - [assignId, userId, plugin, inputData, siteId])); - } -} diff --git a/src/addon/mod/assign/providers/helper.ts b/src/addon/mod/assign/providers/helper.ts deleted file mode 100644 index 4e7cc288e..000000000 --- a/src/addon/mod/assign/providers/helper.ts +++ /dev/null @@ -1,639 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFileProvider } from '@providers/file'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { AddonModAssignFeedbackDelegate } from './feedback-delegate'; -import { AddonModAssignSubmissionDelegate } from './submission-delegate'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignParticipant, - AddonModAssignSubmissionFeedback -} from './assign'; -import { AddonModAssignOfflineProvider } from './assign-offline'; - -/** - * Service that provides some helper functions for assign. - */ -@Injectable() -export class AddonModAssignHelperProvider { - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private fileProvider: CoreFileProvider, - private assignProvider: AddonModAssignProvider, private utils: CoreUtilsProvider, - private assignOffline: AddonModAssignOfflineProvider, private feedbackDelegate: AddonModAssignFeedbackDelegate, - private submissionDelegate: AddonModAssignSubmissionDelegate, private fileUploaderProvider: CoreFileUploaderProvider, - private groupsProvider: CoreGroupsProvider) { - this.logger = logger.getInstance('AddonModAssignHelperProvider'); - } - - /** - * Check if a submission can be edited in offline. - * - * @param assign Assignment. - * @param submission Submission. - * @return Whether it can be edited offline. - */ - canEditSubmissionOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission): Promise { - if (!submission) { - return Promise.resolve(false); - } - - if (submission.status == AddonModAssignProvider.SUBMISSION_STATUS_NEW || - submission.status == AddonModAssignProvider.SUBMISSION_STATUS_REOPENED) { - // It's a new submission, allow creating it in offline. - return Promise.resolve(true); - } - - const promises = []; - let canEdit = true; - - for (let i = 0; i < submission.plugins.length; i++) { - const plugin = submission.plugins[i]; - promises.push(this.submissionDelegate.canPluginEditOffline(assign, submission, plugin).then((canEditPlugin) => { - if (!canEditPlugin) { - canEdit = false; - } - })); - } - - return Promise.all(promises).then(() => { - return canEdit; - }); - } - - /** - * Clear plugins temporary data because a submission was cancelled. - * - * @param assign Assignment. - * @param submission Submission to clear the data for. - * @param inputData Data entered in the submission form. - */ - clearSubmissionPluginTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any): void { - submission.plugins.forEach((plugin) => { - this.submissionDelegate.clearTmpData(assign, submission, plugin, inputData); - }); - } - - /** - * Copy the data from last submitted attempt to the current submission. - * Since we don't have any WS for that we'll have to re-submit everything manually. - * - * @param assign Assignment. - * @param previousSubmission Submission to copy. - * @return Promise resolved when done. - */ - copyPreviousAttempt(assign: AddonModAssignAssign, previousSubmission: AddonModAssignSubmission): Promise { - const pluginData = {}, - promises = []; - - previousSubmission.plugins.forEach((plugin) => { - promises.push(this.submissionDelegate.copyPluginSubmissionData(assign, plugin, pluginData)); - }); - - return Promise.all(promises).then(() => { - // We got the plugin data. Now we need to submit it. - if (Object.keys(pluginData).length) { - // There's something to save. - return this.assignProvider.saveSubmissionOnline(assign.id, pluginData); - } - }); - } - - /** - * Create an empty feedback object. - * - * @return Feedback. - */ - createEmptyFeedback(): AddonModAssignSubmissionFeedback { - return { - grade: undefined, - gradefordisplay: undefined, - gradeddate: undefined - }; - } - - /** - * Create an empty submission object. - * - * @return Submission. - */ - createEmptySubmission(): AddonModAssignSubmissionFormatted { - return { - id: undefined, - userid: undefined, - attemptnumber: undefined, - timecreated: undefined, - timemodified: undefined, - status: undefined, - groupid: undefined - }; - } - - /** - * Delete stored submission files for a plugin. See storeSubmissionFiles. - * - * @param assignId Assignment ID. - * @param folderName Name of the plugin folder. Must be unique (both in submission and feedback plugins). - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - deleteStoredSubmissionFiles(assignId: number, folderName: string, userId?: number, siteId?: string): Promise { - return this.assignOffline.getSubmissionPluginFolder(assignId, folderName, userId, siteId).then((folderPath) => { - return this.fileProvider.removeDir(folderPath); - }); - } - - /** - * Delete all drafts of the feedback plugin data. - * - * @param assignId Assignment Id. - * @param userId User Id. - * @param feedback Feedback data. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - discardFeedbackPluginData(assignId: number, userId: number, feedback: AddonModAssignSubmissionFeedback, - siteId?: string): Promise { - - const promises = []; - - feedback.plugins.forEach((plugin) => { - promises.push(this.feedbackDelegate.discardPluginFeedbackData(assignId, userId, plugin, siteId)); - }); - - return Promise.all(promises); - } - - /** - * Check if a submission has no content. - * - * @param assign Assignment object. - * @param submission Submission to inspect. - * @return Whether the submission is empty. - */ - isSubmissionEmpty(assign: AddonModAssignAssign, submission?: AddonModAssignSubmission): boolean { - if (!submission) { - return true; - } - - for (const plugin of submission.plugins) { - // If any plugin is not empty, we consider that the submission is not empty either. - if (!this.submissionDelegate.isPluginEmpty(assign, plugin)) { - return false; - } - } - - // If all the plugins were empty (or there were no plugins), we consider the submission to be empty. - return true; - } - - /** - * List the participants for a single assignment, with some summary info about their submissions. - * - * @param assign Assignment object. - * @param groupId Group Id. - * @param options Other options. - * @return Promise resolved with the list of participants and summary of submissions. - */ - getParticipants(assign: AddonModAssignAssign, groupId?: number, options: CoreSitesCommonWSOptions = {}) - : Promise { - - groupId = groupId || 0; - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - const modOptions = {cmId: assign.cmid, ...options}; // Create new options including all existing ones. - - return this.assignProvider.listParticipants(assign.id, groupId, modOptions).then((participants) => { - if (groupId || participants && participants.length > 0) { - return participants; - } - - // If no participants returned and all groups specified, get participants by groups. - return this.groupsProvider.getActivityGroupInfo(assign.cmid, false, undefined, modOptions.siteId).then((info) => { - const promises = [], - participants: {[id: number]: AddonModAssignParticipant} = {}; - - info.groups.forEach((userGroup) => { - promises.push(this.assignProvider.listParticipants(assign.id, userGroup.id, modOptions).then((parts) => { - // Do not get repeated users. - parts.forEach((participant) => { - participants[participant.id] = participant; - }); - })); - }); - - return Promise.all(promises).then(() => { - return this.utils.objectToArray(participants); - }); - }); - }); - } - - /** - * Get plugin config from assignment config. - * - * @param assign Assignment object including all config. - * @param subtype Subtype name (assignsubmission or assignfeedback) - * @param type Name of the subplugin. - * @return Object containing all configurations of the subplugin selected. - */ - getPluginConfig(assign: AddonModAssignAssign, subtype: string, type: string): {[name: string]: string} { - const configs: {[name: string]: string} = {}; - - assign.configs.forEach((config) => { - if (config.subtype == subtype && config.plugin == type) { - configs[config.name] = config.value; - } - }); - - return configs; - } - - /** - * Get enabled subplugins. - * - * @param assign Assignment object including all config. - * @param subtype Subtype name (assignsubmission or assignfeedback) - * @return List of enabled plugins for the assign. - */ - getPluginsEnabled(assign: AddonModAssignAssign, subtype: string): any[] { - const enabled = []; - - assign.configs.forEach((config) => { - if (config.subtype == subtype && config.name == 'enabled' && parseInt(config.value, 10) === 1) { - // Format the plugin objects. - enabled.push({ - type: config.plugin - }); - } - }); - - return enabled; - } - - /** - * Get a list of stored submission files. See storeSubmissionFiles. - * - * @param assignId Assignment ID. - * @param folderName Name of the plugin folder. Must be unique (both in submission and feedback plugins). - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getStoredSubmissionFiles(assignId: number, folderName: string, userId?: number, siteId?: string): Promise { - return this.assignOffline.getSubmissionPluginFolder(assignId, folderName, userId, siteId).then((folderPath) => { - return this.fileProvider.getDirectoryContents(folderPath); - }); - } - - /** - * Get the size that will be uploaded to perform an attempt copy. - * - * @param assign Assignment. - * @param previousSubmission Submission to copy. - * @return Promise resolved with the size. - */ - getSubmissionSizeForCopy(assign: AddonModAssignAssign, previousSubmission: AddonModAssignSubmission): Promise { - const promises = []; - let totalSize = 0; - - previousSubmission.plugins.forEach((plugin) => { - promises.push(this.submissionDelegate.getPluginSizeForCopy(assign, plugin).then((size) => { - totalSize += size; - })); - }); - - return Promise.all(promises).then(() => { - return totalSize; - }); - } - - /** - * Get the size that will be uploaded to save a submission. - * - * @param assign Assignment. - * @param submission Submission to check data. - * @param inputData Data entered in the submission form. - * @return Promise resolved with the size. - */ - getSubmissionSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any): Promise { - - const promises = []; - let totalSize = 0; - - submission.plugins.forEach((plugin) => { - promises.push(this.submissionDelegate.getPluginSizeForEdit(assign, submission, plugin, inputData).then((size) => { - totalSize += size; - })); - }); - - return Promise.all(promises).then(() => { - return totalSize; - }); - } - - /** - * Get user data for submissions since they only have userid. - * - * @param assign Assignment object. - * @param submissions Submissions to get the data for. - * @param groupId Group Id. - * @param options Other options. - * @return Promise always resolved. Resolve param is the formatted submissions. - */ - getSubmissionsUserData(assign: AddonModAssignAssign, submissions: AddonModAssignSubmissionFormatted[], groupId?: number, - options: CoreSitesCommonWSOptions = {}): Promise { - - const modOptions = {cmId: assign.cmid, ...options}; // Create new options including all existing ones. - - return this.getParticipants(assign, groupId, modOptions).then((parts) => { - const blind = assign.blindmarking && !assign.revealidentities; - const promises = []; - const result: AddonModAssignSubmissionFormatted[] = []; - const participants: {[id: number]: AddonModAssignParticipant} = this.utils.arrayToObject(parts, 'id'); - - submissions.forEach((submission) => { - submission.submitid = submission.userid > 0 ? submission.userid : submission.blindid; - if (submission.submitid <= 0) { - return; - } - - const participant = participants[submission.submitid]; - if (participant) { - delete participants[submission.submitid]; - } else { - // Avoid permission denied error. Participant not found on list. - return; - } - - if (!blind) { - submission.userfullname = participant.fullname; - submission.userprofileimageurl = participant.profileimageurl; - } - - submission.manyGroups = !!participant.groups && participant.groups.length > 1; - submission.noGroups = !!participant.groups && participant.groups.length == 0; - if (participant.groupname) { - submission.groupid = participant.groupid; - submission.groupname = participant.groupname; - } - - let promise; - if (submission.userid > 0 && blind) { - // Blind but not blinded! (Moodle < 3.1.1, 3.2). - delete submission.userid; - - promise = this.assignProvider.getAssignmentUserMappings(assign.id, submission.submitid, modOptions) - .then((blindId) => { - submission.blindid = blindId; - }); - } - - promise = promise || Promise.resolve(); - - promises.push(promise.then(() => { - // Add to the list. - if (submission.userfullname || submission.blindid) { - result.push(submission); - } - })); - }); - - return Promise.all(promises).then(() => { - // Create a submission for each participant left in the list (the participants already treated were removed). - this.utils.objectToArray(participants).forEach((participant: AddonModAssignParticipant) => { - const submission = this.createEmptySubmission(); - - submission.submitid = participant.id; - - if (!blind) { - submission.userid = participant.id; - submission.userfullname = participant.fullname; - submission.userprofileimageurl = participant.profileimageurl; - } else { - submission.blindid = participant.id; - } - - submission.manyGroups = !!participant.groups && participant.groups.length > 1; - submission.noGroups = !!participant.groups && participant.groups.length == 0; - if (participant.groupname) { - submission.groupid = participant.groupid; - submission.groupname = participant.groupname; - } - submission.status = participant.submitted ? AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED : - AddonModAssignProvider.SUBMISSION_STATUS_NEW; - - result.push(submission); - }); - - return result; - }); - }); - } - - /** - * Check if the feedback data has changed for a certain submission and assign. - * - * @param assign Assignment. - * @param submission The submission. - * @param feedback Feedback data. - * @param userId The user ID. - * @return Promise resolved with true if data has changed, resolved with false otherwise. - */ - hasFeedbackDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - feedback: AddonModAssignSubmissionFeedback, userId: number): Promise { - - const promises = []; - let hasChanged = false; - - feedback.plugins.forEach((plugin) => { - promises.push(this.prepareFeedbackPluginData(assign.id, userId, feedback).then((inputData) => { - return this.feedbackDelegate.hasPluginDataChanged(assign, submission, plugin, inputData, userId).then((changed) => { - if (changed) { - hasChanged = true; - } - }); - }).catch(() => { - // Ignore errors. - })); - }); - - return this.utils.allPromises(promises).then(() => { - return hasChanged; - }); - } - - /** - * Check if the submission data has changed for a certain submission and assign. - * - * @param assign Assignment. - * @param submission Submission to check data. - * @param inputData Data entered in the submission form. - * @return Promise resolved with true if data has changed, resolved with false otherwise. - */ - hasSubmissionDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any) - : Promise { - - const promises = []; - let hasChanged = false; - - submission.plugins.forEach((plugin) => { - promises.push(this.submissionDelegate.hasPluginDataChanged(assign, submission, plugin, inputData).then((changed) => { - if (changed) { - hasChanged = true; - } - }).catch(() => { - // Ignore errors. - })); - }); - - return this.utils.allPromises(promises).then(() => { - return hasChanged; - }); - } - - /** - * Prepare and return the plugin data to send for a certain feedback and assign. - * - * @param assignId Assignment Id. - * @param userId User Id. - * @param feedback Feedback data. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with plugin data to send to server. - */ - prepareFeedbackPluginData(assignId: number, userId: number, feedback: AddonModAssignSubmissionFeedback, siteId?: string) - : Promise { - - const pluginData = {}, - promises = []; - - feedback.plugins.forEach((plugin) => { - promises.push(this.feedbackDelegate.preparePluginFeedbackData(assignId, userId, plugin, pluginData, siteId)); - }); - - return Promise.all(promises).then(() => { - return pluginData; - }); - } - - /** - * Prepare and return the plugin data to send for a certain submission and assign. - * - * @param assign Assignment. - * @param submission Submission to check data. - * @param inputData Data entered in the submission form. - * @param offline True to prepare the data for an offline submission, false otherwise. - * @return Promise resolved with plugin data to send to server. - */ - prepareSubmissionPluginData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any, - offline?: boolean): Promise { - - const pluginData = {}, - promises = []; - - submission.plugins.forEach((plugin) => { - promises.push(this.submissionDelegate.preparePluginSubmissionData(assign, submission, plugin, inputData, pluginData, - offline)); - }); - - return Promise.all(promises).then(() => { - return pluginData; - }); - } - - /** - * Given a list of files (either online files or local files), store the local files in a local folder - * to be submitted later. - * - * @param assignId Assignment ID. - * @param folderName Name of the plugin folder. Must be unique (both in submission and feedback plugins). - * @param files List of files. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - storeSubmissionFiles(assignId: number, folderName: string, files: any[], userId?: number, siteId?: string): Promise { - // Get the folder where to store the files. - return this.assignOffline.getSubmissionPluginFolder(assignId, folderName, userId, siteId).then((folderPath) => { - return this.fileUploaderProvider.storeFilesToUpload(folderPath, files); - }); - } - - /** - * Upload a file to a draft area. If the file is an online file it will be downloaded and then re-uploaded. - * - * @param assignId Assignment ID. - * @param file Online file or local FileEntry. - * @param itemId Draft ID to use. Undefined or 0 to create a new draft ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the itemId. - */ - uploadFile(assignId: number, file: any, itemId?: number, siteId?: string): Promise { - return this.fileUploaderProvider.uploadOrReuploadFile(file, itemId, AddonModAssignProvider.COMPONENT, assignId, siteId); - } - - /** - * Given a list of files (either online files or local files), upload them to a draft area and return the draft ID. - * Online files will be downloaded and then re-uploaded. - * If there are no files to upload it will return a fake draft ID (1). - * - * @param assignId Assignment ID. - * @param files List of files. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the itemId. - */ - uploadFiles(assignId: number, files: any[], siteId?: string): Promise { - return this.fileUploaderProvider.uploadOrReuploadFiles(files, AddonModAssignProvider.COMPONENT, assignId, siteId); - } - - /** - * Upload or store some files, depending if the user is offline or not. - * - * @param assignId Assignment ID. - * @param folderName Name of the plugin folder. Must be unique (both in submission and feedback plugins). - * @param files List of files. - * @param offline True if files sould be stored for offline, false to upload them. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - uploadOrStoreFiles(assignId: number, folderName: string, files: any[], offline?: boolean, userId?: number, siteId?: string) - : Promise { - - if (offline) { - return this.storeSubmissionFiles(assignId, folderName, files, userId, siteId); - } else { - return this.uploadFiles(assignId, files, siteId); - } - } -} - -/** - * Assign submission with some calculated data. - */ -export type AddonModAssignSubmissionFormatted = AddonModAssignSubmission & { - blindid?: number; // Calculated in the app. Blindid of the user that did the submission. - submitid?: number; // Calculated in the app. Userid or blindid of the user that did the submission. - userfullname?: string; // Calculated in the app. Full name of the user that did the submission. - userprofileimageurl?: string; // Calculated in the app. Avatar of the user that did the submission. - manyGroups?: boolean; // Calculated in the app. Whether the user belongs to more than 1 group. - noGroups?: boolean; // Calculated in the app. Whether the user doesn't belong to any group. - groupname?: string; // Calculated in the app. Name of the group the submission belongs to. -}; diff --git a/src/addon/mod/assign/providers/index-link-handler.ts b/src/addon/mod/assign/providers/index-link-handler.ts deleted file mode 100644 index 014ef82fd..000000000 --- a/src/addon/mod/assign/providers/index-link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to assign index page. - */ -@Injectable() -export class AddonModAssignIndexLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModAssignIndexLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModAssign', 'assign'); - } -} diff --git a/src/addon/mod/assign/providers/list-link-handler.ts b/src/addon/mod/assign/providers/list-link-handler.ts deleted file mode 100644 index ff80c18d6..000000000 --- a/src/addon/mod/assign/providers/list-link-handler.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModAssignProvider } from './assign'; - -/** - * Handler to treat links to assign list page. - */ -@Injectable() -export class AddonModAssignListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModAssignListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected assignProvider: AddonModAssignProvider) { - super(linkHelper, translate, 'AddonModAssign', 'assign'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return this.assignProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/assign/providers/module-handler.ts b/src/addon/mod/assign/providers/module-handler.ts deleted file mode 100644 index 9928356e6..000000000 --- a/src/addon/mod/assign/providers/module-handler.ts +++ /dev/null @@ -1,92 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModAssignProvider } from './assign'; -import { AddonModAssignIndexComponent } from '../components/index/index'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support assign modules. - */ -@Injectable() -export class AddonModAssignModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModAssign'; - modName = 'assign'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, - [CoreConstants.FEATURE_ADVANCED_GRADING]: true, - [CoreConstants.FEATURE_PLAGIARISM]: true, - [CoreConstants.FEATURE_COMMENT]: true - }; - - constructor(private courseProvider: CoreCourseProvider, private assignProvider: AddonModAssignProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return this.assignProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_assign-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModAssignIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModAssignIndexComponent; - } -} diff --git a/src/addon/mod/assign/providers/prefetch-handler.ts b/src/addon/mod/assign/providers/prefetch-handler.ts deleted file mode 100644 index 26e2a11c4..000000000 --- a/src/addon/mod/assign/providers/prefetch-handler.ts +++ /dev/null @@ -1,516 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModAssignProvider, AddonModAssignGetSubmissionStatusResult, AddonModAssignSubmission } from './assign'; -import { AddonModAssignHelperProvider, AddonModAssignSubmissionFormatted } from './helper'; -import { AddonModAssignSyncProvider } from './assign-sync'; -import { AddonModAssignFeedbackDelegate } from './feedback-delegate'; -import { AddonModAssignSubmissionDelegate } from './submission-delegate'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch assigns. - */ -@Injectable() -export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModAssign'; - modName = 'assign'; - component = AddonModAssignProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^submissions$|^grades$|^gradeitems$|^outcomes$|^comments$/; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected assignProvider: AddonModAssignProvider, - protected textUtils: CoreTextUtilsProvider, - protected feedbackDelegate: AddonModAssignFeedbackDelegate, - protected submissionDelegate: AddonModAssignSubmissionDelegate, - protected courseHelper: CoreCourseHelperProvider, - protected groupsProvider: CoreGroupsProvider, - protected gradesHelper: CoreGradesHelperProvider, - protected userProvider: CoreUserProvider, - protected assignHelper: AddonModAssignHelperProvider, - protected syncProvider: AddonModAssignSyncProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Check if a certain module can use core_course_check_updates to check if it has updates. - * If not defined, it will assume all modules can be checked. - * The modules that return false will always be shown as outdated when they're downloaded. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Whether the module can use check_updates. The promise should never be rejected. - */ - canUseCheckUpdates(module: any, courseId: number): boolean | Promise { - // Teachers cannot use the WS because it doesn't check student submissions. - return this.assignProvider.getAssignment(courseId, module.id).then((assign) => { - return this.assignProvider.getSubmissions(assign.id, {cmId: module.id}).then((data) => { - if (data.canviewsubmissions) { - return false; - } - - // Check if the user can view their own submission. - return this.assignProvider.getSubmissionStatus(assign.id, {cmId: module.id}).then(() => { - return true; - }); - }); - }).catch(() => { - return false; - }); - } - - /** - * Get list of files. If not defined, we'll assume they're in module.contents. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the list of files. - */ - getFiles(module: any, courseId: number, single?: boolean, siteId?: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.assignProvider.getAssignment(courseId, module.id, {siteId}).then((assign) => { - // Get intro files and attachments. - let files = assign.introattachments || []; - files = files.concat(this.getIntroFilesFromInstance(module, assign)); - - // Now get the files in the submissions. - return this.assignProvider.getSubmissions(assign.id, {cmId: module.id, siteId}).then((data) => { - const blindMarking = assign.blindmarking && !assign.revealidentities; - - if (data.canviewsubmissions) { - // Teacher, get all submissions. - return this.assignHelper.getSubmissionsUserData(assign, data.submissions, 0, {siteId}) - .then((submissions: AddonModAssignSubmissionFormatted[]) => { - - const promises = []; - - // Get all the files in the submissions. - submissions.forEach((submission) => { - promises.push(this.getSubmissionFiles(assign, submission.submitid, !!submission.blindid, siteId) - .then((submissionFiles) => { - files = files.concat(submissionFiles); - }).catch((error) => { - if (error && error.errorcode == 'nopermission') { - // The user does not have persmission to view this submission, ignore it. - return Promise.resolve(); - } - - return Promise.reject(error); - })); - }); - - return Promise.all(promises).then(() => { - return files; - }); - }); - } else { - // Student, get only his/her submissions. - const userId = this.sitesProvider.getCurrentSiteUserId(); - - return this.getSubmissionFiles(assign, userId, blindMarking, siteId).then((submissionFiles) => { - files = files.concat(submissionFiles); - - return files; - }); - } - }); - }).catch(() => { - // Error getting data, return empty list. - return []; - }); - } - - /** - * Get submission files. - * - * @param assign Assign. - * @param submitId User ID of the submission to get. - * @param blindMarking True if blind marking, false otherwise. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with array of files. - */ - protected getSubmissionFiles(assign: any, submitId: number, blindMarking: boolean, siteId?: string) - : Promise { - - return this.assignProvider.getSubmissionStatusWithRetry(assign, { - userId: submitId, - isBlind: blindMarking, - siteId, - }).then((response) => { - const promises = []; - let userSubmission: AddonModAssignSubmission; - - if (response.lastattempt) { - userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(assign, response.lastattempt); - if (userSubmission && userSubmission.plugins) { - // Add submission plugin files. - userSubmission.plugins.forEach((plugin) => { - promises.push(this.submissionDelegate.getPluginFiles(assign, userSubmission, plugin, siteId)); - }); - } - } - - if (response.feedback && response.feedback.plugins) { - // Add feedback plugin files. - response.feedback.plugins.forEach((plugin) => { - promises.push(this.feedbackDelegate.getPluginFiles(assign, userSubmission, plugin, siteId)); - }); - } - - return Promise.all(promises); - - }).then((filesLists) => { - let files = []; - - filesLists.forEach((filesList) => { - files = files.concat(filesList); - }); - - return files; - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.assignProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - return this.assignProvider.invalidateAssignmentData(courseId); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.assignProvider.isPluginEnabled(); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchAssign.bind(this)); - } - - /** - * Prefetch an assignment. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchAssign(module: any, courseId: number, single: boolean, siteId: string): Promise { - const userId = this.sitesProvider.getCurrentSiteUserId(); - const promises = []; - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const options = { - cmId: module.id, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - // Get assignment to retrieve all its submissions. - promises.push(this.assignProvider.getAssignment(courseId, module.id, options).then((assign) => { - const subPromises = [], - blindMarking = assign.blindmarking && !assign.revealidentities; - - if (blindMarking) { - subPromises.push(this.utils.ignoreErrors(this.assignProvider.getAssignmentUserMappings(assign.id, -1, options))); - } - - subPromises.push(this.prefetchSubmissions(assign, courseId, module.id, userId, siteId)); - - subPromises.push(this.courseHelper.getModuleCourseIdByInstance(assign.id, 'assign', siteId)); - - // Download intro files and attachments. Do not call getFiles because it'd call some WS twice. - let files = assign.introattachments || []; - files = files.concat(this.getIntroFilesFromInstance(module, assign)); - - subPromises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id)); - - return Promise.all(subPromises); - })); - - return Promise.all(promises); - } - - /** - * Prefetch assign submissions. - * - * @param assign Assign. - * @param courseId Course ID. - * @param moduleId Module ID. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when prefetched, rejected otherwise. - */ - protected prefetchSubmissions(assign: any, courseId: number, moduleId: number, userId: number, siteId: string): Promise { - const options = { - cmId: moduleId, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - // Get submissions. - return this.assignProvider.getSubmissions(assign.id, options).then((data) => { - const promises = []; - - promises.push(this.groupsProvider.getActivityGroupInfo(assign.cmid, false, undefined, siteId).then((groupInfo) => { - if (data.canviewsubmissions) { - // Teacher, prefetch all submissions. - const groupProms = []; - if (!groupInfo.groups || groupInfo.groups.length == 0) { - groupInfo.groups = [{id: 0}]; - } - - groupInfo.groups.forEach((group) => { - groupProms.push(this.assignHelper.getSubmissionsUserData(assign, data.submissions, group.id, options) - .then((submissions: AddonModAssignSubmissionFormatted[]) => { - - const subPromises = []; - - submissions.forEach((submission) => { - const submissionOptions = { - userId: submission.submitid, - groupId: group.id, - isBlind: !!submission.blindid, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - subPromises.push(this.assignProvider.getSubmissionStatusWithRetry(assign, submissionOptions) - .then((subm) => { - return this.prefetchSubmission(assign, courseId, moduleId, subm, submission.submitid, siteId); - }).catch((error) => { - if (error && error.errorcode == 'nopermission') { - // The user does not have persmission to view this submission, ignore it. - return Promise.resolve(); - } - - return Promise.reject(error); - })); - }); - - if (!assign.markingworkflow) { - // Get assignment grades only if workflow is not enabled to check grading date. - subPromises.push(this.assignProvider.getAssignmentGrades(assign.id, options)); - } - - // Prefetch the submission of the current user even if it does not exist, this will be create it. - if (!data.submissions || - !data.submissions.find((subm: AddonModAssignSubmissionFormatted) => subm.submitid == userId)) { - const submissionOptions = { - userId, - groupId: group.id, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - subPromises.push(this.assignProvider.getSubmissionStatusWithRetry(assign, submissionOptions) - .then((subm) => { - return this.prefetchSubmission(assign, courseId, moduleId, subm, userId, siteId); - })); - } - - return Promise.all(subPromises); - }).then(() => { - // Participiants already fetched, we don't need to ignore cache now. - return this.assignHelper.getParticipants(assign, group.id, {siteId}).then((participants) => { - return this.userProvider.prefetchUserAvatars(participants, 'profileimageurl', siteId); - }).catch(() => { - // Fail silently (Moodle < 3.2). - }); - })); - }); - - return Promise.all(groupProms); - } - })); - - // Prefetch own submission, we need to do this for teachers too so the response with error is cached. - promises.push( - this.assignProvider.getSubmissionStatusWithRetry(assign, { - userId, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((subm) => { - return this.prefetchSubmission(assign, courseId, moduleId, subm, userId, siteId); - }).catch((error) => { - // Ignore if the user can't view their own submission. - if (error.errorcode != 'nopermission') { - return Promise.reject(error); - } - }) - ); - - return Promise.all(promises); - }); - } - - /** - * Prefetch a submission. - * - * @param assign Assign. - * @param courseId Course ID. - * @param moduleId Module ID. - * @param submission Data returned by AddonModAssignProvider.getSubmissionStatus. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when prefetched, rejected otherwise. - */ - protected prefetchSubmission(assign: any, courseId: number, moduleId: number, - submission: AddonModAssignGetSubmissionStatusResult, userId?: number, siteId?: string): Promise { - - const promises = [], - blindMarking = assign.blindmarking && !assign.revealidentities; - let userIds = [], - userSubmission: AddonModAssignSubmission; - - if (submission.lastattempt) { - userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(assign, submission.lastattempt); - - // Get IDs of the members who need to submit. - if (!blindMarking && submission.lastattempt.submissiongroupmemberswhoneedtosubmit) { - userIds = userIds.concat(submission.lastattempt.submissiongroupmemberswhoneedtosubmit); - } - - if (userSubmission && userSubmission.id) { - // Prefetch submission plugins data. - if (userSubmission.plugins) { - userSubmission.plugins.forEach((plugin) => { - // Prefetch the plugin WS data. - promises.push(this.submissionDelegate.prefetch(assign, userSubmission, plugin, siteId)); - - // Prefetch the plugin files. - promises.push(this.submissionDelegate.getPluginFiles(assign, userSubmission, plugin, siteId) - .then((files) => { - return this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id); - }).catch(() => { - // Ignore errors. - })); - }); - } - - // Get ID of the user who did the submission. - if (userSubmission.userid) { - userIds.push(userSubmission.userid); - } - } - } - - // Prefetch grade items. - if (userId) { - promises.push(this.courseProvider.getModuleBasicGradeInfo(moduleId, siteId).then((gradeInfo) => { - if (gradeInfo) { - promises.push(this.gradesHelper.getGradeModuleItems(courseId, moduleId, userId, undefined, siteId, true)); - } - })); - } - - // Prefetch feedback. - if (submission.feedback) { - // Get profile and image of the grader. - if (submission.feedback.grade && submission.feedback.grade.grader > 0) { - userIds.push(submission.feedback.grade.grader); - } - - // Prefetch feedback plugins data. - if (submission.feedback.plugins) { - submission.feedback.plugins.forEach((plugin) => { - // Prefetch the plugin WS data. - promises.push(this.feedbackDelegate.prefetch(assign, userSubmission, plugin, siteId)); - - // Prefetch the plugin files. - promises.push(this.feedbackDelegate.getPluginFiles(assign, userSubmission, plugin, siteId).then((files) => { - return this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id); - }).catch(() => { - // Ignore errors. - })); - }); - } - } - - // Prefetch user profiles. - promises.push(this.userProvider.prefetchProfiles(userIds, courseId, siteId)); - - return Promise.all(promises); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - return this.syncProvider.syncAssign(module.instance, siteId); - } -} diff --git a/src/addon/mod/assign/providers/push-click-handler.ts b/src/addon/mod/assign/providers/push-click-handler.ts deleted file mode 100644 index 9dea85284..000000000 --- a/src/addon/mod/assign/providers/push-click-handler.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModAssignProvider } from './assign'; - -/** - * Handler for assign push notifications clicks. - */ -@Injectable() -export class AddonModAssignPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonModAssignPushClickHandler'; - priority = 200; - featureName = 'CoreCourseModuleDelegate_AddonModAssign'; - - constructor(private utils: CoreUtilsProvider, private assignProvider: AddonModAssignProvider, - private urlUtils: CoreUrlUtilsProvider, private courseHelper: CoreCourseHelperProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - return this.utils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_assign' && - notification.name == 'assign_notification'; - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - handleClick(notification: any): Promise { - const contextUrlParams = this.urlUtils.extractUrlParams(notification.contexturl), - courseId = Number(notification.courseid), - moduleId = Number(contextUrlParams.id); - - return this.assignProvider.invalidateContent(moduleId, courseId, notification.site).catch(() => { - // Ignore errors. - }).then(() => { - return this.courseHelper.navigateToModule(moduleId, notification.site, courseId); - }); - } -} diff --git a/src/addon/mod/assign/providers/submission-delegate.ts b/src/addon/mod/assign/providers/submission-delegate.ts deleted file mode 100644 index 89e29e5cf..000000000 --- a/src/addon/mod/assign/providers/submission-delegate.ts +++ /dev/null @@ -1,443 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; -import { AddonModAssignDefaultSubmissionHandler } from './default-submission-handler'; -import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from './assign'; - -/** - * Interface that all submission handlers must implement. - */ -export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler { - - /** - * Name of the type of submission the handler supports. E.g. 'file'. - */ - type: string; - - /** - * Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the - * plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit - * unfiltered data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @return Boolean or promise resolved with boolean: whether it can be edited in offline. - */ - canEditOffline?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin): boolean | Promise; - - /** - * Check if a plugin has no data. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return Whether the plugin is empty. - */ - isEmpty?(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): boolean; - - /** - * Should clear temporary data for a cancelled submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - */ - clearTmpData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): void; - - /** - * This function will be called when the user wants to create a new submission based on the previous one. - * It should add to pluginData the data to send to server based in the data in plugin (previous attempt). - * - * @param assign The assignment. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - copySubmissionData?(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any, - userId?: number, siteId?: string): void | Promise; - - /** - * Delete any stored data for the plugin and submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - deleteOfflineData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, siteId?: string): void | Promise; - - /** - * Return the Component to use to display the plugin data, either in read or in edit mode. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @param edit Whether the user is editing. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent?(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise; - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise; - - /** - * Get a readable name to use for the plugin. - * - * @param plugin The plugin object. - * @return The plugin name. - */ - getPluginName?(plugin: AddonModAssignPlugin): string; - - /** - * Get the size of data (in bytes) this plugin will send to copy a previous submission. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return The size (or promise resolved with size). - */ - getSizeForCopy?(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): number | Promise; - - /** - * Get the size of data (in bytes) this plugin will send to add or edit a submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return The size (or promise resolved with size). - */ - getSizeForEdit?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): number | Promise; - - /** - * Check if the submission data has changed for this plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return Boolean (or promise resolved with boolean): whether the data has changed. - */ - hasDataChanged?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): boolean | Promise; - - /** - * Whether or not the handler is enabled for edit on a site level. - * - * @return Whether or not the handler is enabled for edit on a site level. - */ - isEnabledForEdit?(): boolean | Promise; - - /** - * Prefetch any required data for the plugin. - * This should NOT prefetch files. Files to be prefetched should be returned by the getPluginFiles function. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetch?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): Promise; - - /** - * Prepare and add to pluginData the data to send to the server based on the input data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @param pluginData Object where to store the data to send. - * @param offline Whether the user is editing in offline. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareSubmissionData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean, - userId?: number, siteId?: string): void | Promise; - - /** - * Prepare and add to pluginData the data to send to the server based on the offline data stored. - * This will be used when performing a synchronization. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareSyncData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise; -} - -/** - * Delegate to register plugins for assign submission. - */ -@Injectable() -export class AddonModAssignSubmissionDelegate extends CoreDelegate { - - protected handlerNameProperty = 'type'; - - constructor(logger: CoreLoggerProvider, sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, - protected defaultHandler: AddonModAssignDefaultSubmissionHandler) { - super('AddonModAssignSubmissionDelegate', logger, sitesProvider, eventsProvider); - } - - /** - * Whether the plugin can be edited in offline for existing submissions. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @return Promise resolved with boolean: whether it can be edited in offline. - */ - canPluginEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'canEditOffline', [assign, submission, plugin])); - } - - /** - * Clear some temporary data for a certain plugin because a submission was cancelled. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - */ - clearTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): void { - return this.executeFunctionOnEnabled(plugin.type, 'clearTmpData', [assign, submission, plugin, inputData]); - } - - /** - * Copy the data from last submitted attempt to the current submission for a certain plugin. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data has been copied. - */ - copyPluginSubmissionData(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any, - userId?: number, siteId?: string): void | Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'copySubmissionData', - [assign, plugin, pluginData, userId, siteId])); - } - - /** - * Delete offline data stored for a certain submission and plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - deletePluginOfflineData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, siteId?: string): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'deleteOfflineData', - [assign, submission, plugin, offlineData, siteId])); - } - - /** - * Get the component to use for a certain submission plugin. - * - * @param injector Injector. - * @param plugin The plugin object. - * @param edit Whether the user is editing. - * @return Promise resolved with the component to use, undefined if not found. - */ - getComponentForPlugin(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getComponent', [injector, plugin, edit])); - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getPluginFiles', [assign, submission, plugin, siteId])); - } - - /** - * Get a readable name to use for a certain submission plugin. - * - * @param plugin Plugin to get the name for. - * @return Human readable name. - */ - getPluginName(plugin: AddonModAssignPlugin): string { - return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]); - } - - /** - * Get the size of data (in bytes) this plugin will send to copy a previous submission. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return Promise resolved with size. - */ - getPluginSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getSizeForCopy', [assign, plugin])); - } - - /** - * Get the size of data (in bytes) this plugin will send to add or edit a submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return Promise resolved with size. - */ - getPluginSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getSizeForEdit', - [assign, submission, plugin, inputData])); - } - - /** - * Check if the submission data has changed for a certain plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return Promise resolved with true if data has changed, resolved with false otherwise. - */ - hasPluginDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDataChanged', - [assign, submission, plugin, inputData])); - } - - /** - * Check if a submission plugin is supported. - * - * @param pluginType Type of the plugin. - * @return Whether it's supported. - */ - isPluginSupported(pluginType: string): boolean { - return this.hasHandler(pluginType, true); - } - - /** - * Check if a submission plugin is supported for edit. - * - * @param pluginType Type of the plugin. - * @return Whether it's supported for edit. - */ - isPluginSupportedForEdit(pluginType: string): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(pluginType, 'isEnabledForEdit')); - } - - /** - * Check if a plugin has no data. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return Whether the plugin is empty. - */ - isPluginEmpty(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): boolean { - return this.executeFunctionOnEnabled(plugin.type, 'isEmpty', [assign, plugin]); - } - - /** - * Prefetch any required data for a submission plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetch(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, plugin: AddonModAssignPlugin, - siteId?: string): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prefetch', [assign, submission, plugin, siteId])); - } - - /** - * Prepare and add to pluginData the data to submit for a certain submission plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @param pluginData Object where to store the data to send. - * @param offline Whether the user is editing in offline. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data has been gathered. - */ - preparePluginSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean, userId?: number, - siteId?: string): Promise { - - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareSubmissionData', - [assign, submission, plugin, inputData, pluginData, offline, userId, siteId])); - } - - /** - * Prepare and add to pluginData the data to send to server to synchronize an offline submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data has been gathered. - */ - preparePluginSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): Promise { - - return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareSyncData', - [assign, submission, plugin, offlineData, pluginData, siteId])); - } -} diff --git a/src/addon/mod/assign/providers/sync-cron-handler.ts b/src/addon/mod/assign/providers/sync-cron-handler.ts deleted file mode 100644 index 593cd4c45..000000000 --- a/src/addon/mod/assign/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModAssignSyncProvider } from './assign-sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModAssignSyncCronHandler implements CoreCronHandler { - name = 'AddonModAssignSyncCronHandler'; - - constructor(private assignSync: AddonModAssignSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.assignSync.syncAllAssignments(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.assignSync.syncInterval; - } -} diff --git a/src/addon/mod/assign/submission/comments/comments.module.ts b/src/addon/mod/assign/submission/comments/comments.module.ts deleted file mode 100644 index 769f3b819..000000000 --- a/src/addon/mod/assign/submission/comments/comments.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModAssignSubmissionCommentsHandler } from './providers/handler'; -import { AddonModAssignSubmissionCommentsComponent } from './component/comments'; -import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate'; -import { CoreCommentsComponentsModule } from '@core/comments/components/components.module'; - -@NgModule({ - declarations: [ - AddonModAssignSubmissionCommentsComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreCommentsComponentsModule - ], - providers: [ - AddonModAssignSubmissionCommentsHandler - ], - exports: [ - AddonModAssignSubmissionCommentsComponent - ], - entryComponents: [ - AddonModAssignSubmissionCommentsComponent - ] -}) -export class AddonModAssignSubmissionCommentsModule { - constructor(submissionDelegate: AddonModAssignSubmissionDelegate, handler: AddonModAssignSubmissionCommentsHandler) { - submissionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/assign/submission/comments/component/addon-mod-assign-submission-comments.html b/src/addon/mod/assign/submission/comments/component/addon-mod-assign-submission-comments.html deleted file mode 100644 index f264d3162..000000000 --- a/src/addon/mod/assign/submission/comments/component/addon-mod-assign-submission-comments.html +++ /dev/null @@ -1,4 +0,0 @@ - -

{{plugin.name}}

- -
diff --git a/src/addon/mod/assign/submission/comments/component/comments.ts b/src/addon/mod/assign/submission/comments/component/comments.ts deleted file mode 100644 index 6e6d6bb01..000000000 --- a/src/addon/mod/assign/submission/comments/component/comments.ts +++ /dev/null @@ -1,54 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { CoreCommentsCommentsComponent } from '@core/comments/components/comments/comments'; -import { AddonModAssignSubmissionPluginComponent } from '../../../classes/submission-plugin-component'; - -/** - * Component to render a comments submission plugin. - */ -@Component({ - selector: 'addon-mod-assign-submission-comments', - templateUrl: 'addon-mod-assign-submission-comments.html' -}) -export class AddonModAssignSubmissionCommentsComponent extends AddonModAssignSubmissionPluginComponent { - @ViewChild(CoreCommentsCommentsComponent) commentsComponent: CoreCommentsCommentsComponent; - - commentsEnabled: boolean; - - constructor(protected commentsProvider: CoreCommentsProvider) { - super(); - - this.commentsEnabled = !commentsProvider.areCommentsDisabledInSite(); - } - - /** - * Invalidate the data. - * - * @return Promise resolved when done. - */ - invalidate(): Promise { - return this.commentsProvider.invalidateCommentsData('module', this.assign.cmid, 'assignsubmission_comments', - this.submission.id, 'submission_comments'); - } - - /** - * Show the comments. - */ - showComments(e?: Event): void { - this.commentsComponent && this.commentsComponent.openComments(e); - } -} diff --git a/src/addon/mod/assign/submission/comments/lang/en.json b/src/addon/mod/assign/submission/comments/lang/en.json deleted file mode 100644 index c69c732aa..000000000 --- a/src/addon/mod/assign/submission/comments/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "Submission comments" -} \ No newline at end of file diff --git a/src/addon/mod/assign/submission/comments/providers/handler.ts b/src/addon/mod/assign/submission/comments/providers/handler.ts deleted file mode 100644 index 98dcca088..000000000 --- a/src/addon/mod/assign/submission/comments/providers/handler.ts +++ /dev/null @@ -1,99 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate'; -import { AddonModAssignSubmissionCommentsComponent } from '../component/comments'; -import { - AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../../providers/assign'; - -/** - * Handler for comments submission plugin. - */ -@Injectable() -export class AddonModAssignSubmissionCommentsHandler implements AddonModAssignSubmissionHandler { - name = 'AddonModAssignSubmissionCommentsHandler'; - type = 'comments'; - - constructor(private commentsProvider: CoreCommentsProvider) { } - - /** - * Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the - * plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit - * unfiltered data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @return Boolean or promise resolved with boolean: whether it can be edited in offline. - */ - canEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin): boolean | Promise { - // This plugin is read only, but return true to prevent blocking the edition. - return true; - } - - /** - * Return the Component to use to display the plugin data, either in read or in edit mode. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @param edit Whether the user is editing. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise { - return edit ? undefined : AddonModAssignSubmissionCommentsComponent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether or not the handler is enabled for edit on a site level. - * - * @return Whether or not the handler is enabled for edit on a site level. - */ - isEnabledForEdit(): boolean | Promise { - return true; - } - - /** - * Prefetch any required data for the plugin. - * This should NOT prefetch files. Files to be prefetched should be returned by the getPluginFiles function. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetch(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): Promise { - - return this.commentsProvider.getComments('module', assign.cmid, 'assignsubmission_comments', submission.id, - 'submission_comments', 0, siteId).catch(() => { - // Fail silently (Moodle < 3.1.1, 3.2) - }); - } -} diff --git a/src/addon/mod/assign/submission/file/component/addon-mod-assign-submission-file.html b/src/addon/mod/assign/submission/file/component/addon-mod-assign-submission-file.html deleted file mode 100644 index bd4fd9d01..000000000 --- a/src/addon/mod/assign/submission/file/component/addon-mod-assign-submission-file.html +++ /dev/null @@ -1,13 +0,0 @@ - - -

{{plugin.name}}

-
- -
-
- - -
- {{plugin.name}} - -
diff --git a/src/addon/mod/assign/submission/file/component/file.ts b/src/addon/mod/assign/submission/file/component/file.ts deleted file mode 100644 index 137a3d902..000000000 --- a/src/addon/mod/assign/submission/file/component/file.ts +++ /dev/null @@ -1,74 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { AddonModAssignProvider } from '../../../providers/assign'; -import { AddonModAssignHelperProvider } from '../../../providers/helper'; -import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline'; -import { AddonModAssignSubmissionFileHandler } from '../providers/handler'; -import { AddonModAssignSubmissionPluginComponent } from '../../../classes/submission-plugin-component'; - -/** - * Component to render a file submission plugin. - */ -@Component({ - selector: 'addon-mod-assign-submission-file', - templateUrl: 'addon-mod-assign-submission-file.html' -}) -export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmissionPluginComponent implements OnInit { - - component = AddonModAssignProvider.COMPONENT; - files: any[]; - - constructor(protected fileSessionprovider: CoreFileSessionProvider, protected assignProvider: AddonModAssignProvider, - protected assignOfflineProvider: AddonModAssignOfflineProvider, protected assignHelper: AddonModAssignHelperProvider, - protected fileUploaderProvider: CoreFileUploaderProvider) { - super(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Get the offline data. - this.assignOfflineProvider.getSubmission(this.assign.id).catch(() => { - // Error getting data, assume there's no offline submission. - }).then((offlineData) => { - if (offlineData && offlineData.plugindata && offlineData.plugindata.files_filemanager) { - // It has offline data. - let promise; - if (offlineData.plugindata.files_filemanager.offline) { - promise = this.assignHelper.getStoredSubmissionFiles(this.assign.id, - AddonModAssignSubmissionFileHandler.FOLDER_NAME); - } else { - promise = Promise.resolve([]); - } - - return promise.then((offlineFiles) => { - const onlineFiles = offlineData.plugindata.files_filemanager.online || []; - offlineFiles = this.fileUploaderProvider.markOfflineFiles(offlineFiles); - - this.files = onlineFiles.concat(offlineFiles); - }); - } else { - // No offline data, get the online files. - this.files = this.assignProvider.getSubmissionPluginAttachments(this.plugin); - } - }).finally(() => { - this.fileSessionprovider.setFiles(this.component, this.assign.id, this.files); - }); - } -} diff --git a/src/addon/mod/assign/submission/file/file.module.ts b/src/addon/mod/assign/submission/file/file.module.ts deleted file mode 100644 index e92dd77cd..000000000 --- a/src/addon/mod/assign/submission/file/file.module.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModAssignSubmissionFileHandler } from './providers/handler'; -import { AddonModAssignSubmissionFileComponent } from './component/file'; -import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModAssignSubmissionFileComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModAssignSubmissionFileHandler - ], - exports: [ - AddonModAssignSubmissionFileComponent - ], - entryComponents: [ - AddonModAssignSubmissionFileComponent - ] -}) -export class AddonModAssignSubmissionFileModule { - constructor(submissionDelegate: AddonModAssignSubmissionDelegate, handler: AddonModAssignSubmissionFileHandler) { - submissionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/assign/submission/file/lang/en.json b/src/addon/mod/assign/submission/file/lang/en.json deleted file mode 100644 index 7262ba217..000000000 --- a/src/addon/mod/assign/submission/file/lang/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pluginname": "File submissions" -} \ No newline at end of file diff --git a/src/addon/mod/assign/submission/file/providers/handler.ts b/src/addon/mod/assign/submission/file/providers/handler.ts deleted file mode 100644 index 7e3eeb4e4..000000000 --- a/src/addon/mod/assign/submission/file/providers/handler.ts +++ /dev/null @@ -1,334 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { CoreFileHelperProvider } from '@providers/file-helper'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../../providers/assign'; -import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline'; -import { AddonModAssignHelperProvider } from '../../../providers/helper'; -import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate'; -import { AddonModAssignSubmissionFileComponent } from '../component/file'; - -/** - * Handler for file submission plugin. - */ -@Injectable() -export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmissionHandler { - static FOLDER_NAME = 'submission_file'; - - name = 'AddonModAssignSubmissionFileHandler'; - type = 'file'; - - constructor(private assignProvider: AddonModAssignProvider, private assignOfflineProvider: AddonModAssignOfflineProvider, - private assignHelper: AddonModAssignHelperProvider, private fileSessionProvider: CoreFileSessionProvider, - private fileUploaderProvider: CoreFileUploaderProvider, private fileHelper: CoreFileHelperProvider, - private utils: CoreUtilsProvider) { } - - /** - * Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the - * plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit - * unfiltered data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @return Boolean or promise resolved with boolean: whether it can be edited in offline. - */ - canEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin): boolean | Promise { - // This plugin doesn't use Moodle filters, it can be edited in offline. - return true; - } - - /** - * Check if a plugin has no data. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return Whether the plugin is empty. - */ - isEmpty(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): boolean { - const files = this.assignProvider.getSubmissionPluginAttachments(plugin); - - return files.length === 0; - } - - /** - * Should clear temporary data for a cancelled submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - */ - clearTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): void { - const files = this.fileSessionProvider.getFiles(AddonModAssignProvider.COMPONENT, assign.id); - - // Clear the files in session for this assign. - this.fileSessionProvider.clearFiles(AddonModAssignProvider.COMPONENT, assign.id); - - // Now delete the local files from the tmp folder. - this.fileUploaderProvider.clearTmpFiles(files); - } - - /** - * This function will be called when the user wants to create a new submission based on the previous one. - * It should add to pluginData the data to send to server based in the data in plugin (previous attempt). - * - * @param assign The assignment. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - copySubmissionData(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any, - userId?: number, siteId?: string): void | Promise { - - // We need to re-upload all the existing files. - const files = this.assignProvider.getSubmissionPluginAttachments(plugin); - - return this.assignHelper.uploadFiles(assign.id, files).then((itemId) => { - pluginData.files_filemanager = itemId; - }); - } - - /** - * Return the Component to use to display the plugin data, either in read or in edit mode. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @param edit Whether the user is editing. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise { - return AddonModAssignSubmissionFileComponent; - } - - /** - * Delete any stored data for the plugin and submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - deleteOfflineData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, siteId?: string): void | Promise { - - return this.assignHelper.deleteStoredSubmissionFiles(assign.id, AddonModAssignSubmissionFileHandler.FOLDER_NAME, - submission.userid, siteId).catch(() => { - // Ignore errors, maybe the folder doesn't exist. - }); - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise { - return this.assignProvider.getSubmissionPluginAttachments(plugin); - } - - /** - * Get the size of data (in bytes) this plugin will send to copy a previous submission. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return The size (or promise resolved with size). - */ - getSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): number | Promise { - const files = this.assignProvider.getSubmissionPluginAttachments(plugin); - - return this.fileHelper.getTotalFilesSize(files); - } - - /** - * Get the size of data (in bytes) this plugin will send to add or edit a submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return The size (or promise resolved with size). - */ - getSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): number | Promise { - // Check if there's any change. - if (this.hasDataChanged(assign, submission, plugin, inputData)) { - const files = this.fileSessionProvider.getFiles(AddonModAssignProvider.COMPONENT, assign.id); - - return this.fileHelper.getTotalFilesSize(files); - } else { - // Nothing has changed, we won't upload any file. - return 0; - } - } - - /** - * Check if the submission data has changed for this plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return Boolean (or promise resolved with boolean): whether the data has changed. - */ - hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): boolean | Promise { - - // Check if there's any offline data. - return this.assignOfflineProvider.getSubmission(assign.id, submission.userid).catch(() => { - // No offline data found. - }).then((offlineData) => { - if (offlineData && offlineData.plugindata && offlineData.plugindata.files_filemanager) { - // Has offline data, return the number of files. - return offlineData.plugindata.files_filemanager.offline + offlineData.plugindata.files_filemanager.online.length; - } - - // No offline data, return the number of online files. - const pluginFiles = this.assignProvider.getSubmissionPluginAttachments(plugin); - - return pluginFiles && pluginFiles.length; - }).then((numFiles) => { - const currentFiles = this.fileSessionProvider.getFiles(AddonModAssignProvider.COMPONENT, assign.id); - - if (currentFiles.length != numFiles) { - // Number of files has changed. - return true; - } - - // Search if there is any local file added. - for (let i = 0; i < currentFiles.length; i++) { - const file = currentFiles[i]; - if (!file.filename && typeof file.name != 'undefined' && !file.offline) { - // There's a local file added, list has changed. - return true; - } - } - - // No local files and list length is the same, this means the list hasn't changed. - return false; - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether or not the handler is enabled for edit on a site level. - * - * @return Whether or not the handler is enabled for edit on a site level. - */ - isEnabledForEdit(): boolean | Promise { - return true; - } - - /** - * Prepare and add to pluginData the data to send to the server based on the input data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @param pluginData Object where to store the data to send. - * @param offline Whether the user is editing in offline. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean, - userId?: number, siteId?: string): void | Promise { - - if (this.hasDataChanged(assign, submission, plugin, inputData)) { - // Data has changed, we need to upload new files and re-upload all the existing files. - const currentFiles = this.fileSessionProvider.getFiles(AddonModAssignProvider.COMPONENT, assign.id), - error = this.utils.hasRepeatedFilenames(currentFiles); - - if (error) { - return Promise.reject(error); - } - - return this.assignHelper.uploadOrStoreFiles(assign.id, AddonModAssignSubmissionFileHandler.FOLDER_NAME, - currentFiles, offline, userId, siteId).then((result) => { - pluginData.files_filemanager = result; - }); - } - } - - /** - * Prepare and add to pluginData the data to send to the server based on the offline data stored. - * This will be used when performing a synchronization. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise { - - const filesData = offlineData && offlineData.plugindata && offlineData.plugindata.files_filemanager; - if (filesData) { - // Has some data to sync. - let files = filesData.online || [], - promise; - - if (filesData.offline) { - // Has offline files, get them and add them to the list. - promise = this.assignHelper.getStoredSubmissionFiles(assign.id, AddonModAssignSubmissionFileHandler.FOLDER_NAME, - submission.userid, siteId).then((result) => { - files = files.concat(result); - }).catch(() => { - // Folder not found, no files to add. - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - return this.assignHelper.uploadFiles(assign.id, files, siteId).then((itemId) => { - pluginData.files_filemanager = itemId; - }); - }); - } - } -} diff --git a/src/addon/mod/assign/submission/onlinetext/component/addon-mod-assign-submission-onlinetext.html b/src/addon/mod/assign/submission/onlinetext/component/addon-mod-assign-submission-onlinetext.html deleted file mode 100644 index a659af7a9..000000000 --- a/src/addon/mod/assign/submission/onlinetext/component/addon-mod-assign-submission-onlinetext.html +++ /dev/null @@ -1,20 +0,0 @@ - - -

{{ plugin.name }}

-

{{ 'addon.mod_assign.numwords' | translate: {'$a': words} }}

-

- -

-
- - -
- {{ plugin.name }} - -

{{ 'addon.mod_assign.wordlimit' | translate }}

-

{{ 'core.numwords' | translate: {'$a': words + ' / ' + configs.wordlimit} }}

-
- - - -
diff --git a/src/addon/mod/assign/submission/onlinetext/component/onlinetext.ts b/src/addon/mod/assign/submission/onlinetext/component/onlinetext.ts deleted file mode 100644 index ccfb0d3cd..000000000 --- a/src/addon/mod/assign/submission/onlinetext/component/onlinetext.ts +++ /dev/null @@ -1,128 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, ElementRef } from '@angular/core'; -import { FormBuilder, FormControl } from '@angular/forms'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { AddonModAssignProvider } from '../../../providers/assign'; -import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline'; -import { AddonModAssignSubmissionPluginComponent } from '../../../classes/submission-plugin-component'; - -/** - * Component to render an onlinetext submission plugin. - */ -@Component({ - selector: 'addon-mod-assign-submission-online-text', - templateUrl: 'addon-mod-assign-submission-onlinetext.html' -}) -export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignSubmissionPluginComponent implements OnInit { - - control: FormControl; - words: number; - component = AddonModAssignProvider.COMPONENT; - text: string; - loaded: boolean; - wordLimitEnabled: boolean; - currentUserId: number; - - protected wordCountTimeout: any; - protected element: HTMLElement; - - constructor( - protected fb: FormBuilder, - protected domUtils: CoreDomUtilsProvider, - protected textUtils: CoreTextUtilsProvider, - protected assignProvider: AddonModAssignProvider, - protected assignOfflineProvider: AddonModAssignOfflineProvider, - element: ElementRef, - sitesProvider: CoreSitesProvider) { - - super(); - this.element = element.nativeElement; - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Get the text. Check if we have anything offline. - this.assignOfflineProvider.getSubmission(this.assign.id).catch(() => { - // No offline data found. - }).then((offlineData) => { - if (offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor) { - return offlineData.plugindata.onlinetext_editor.text; - } - - // No offline data found, return online text. - return this.assignProvider.getSubmissionPluginText(this.plugin); - }).then((text) => { - this.wordLimitEnabled = !!parseInt(this.configs.wordlimitenabled, 10); - - // Set the text. - this.text = text; - - if (!this.edit) { - // Not editing, see full text when clicked. - this.element.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - if (text) { - // Open a new state with the interpolated contents. - this.textUtils.viewText(this.plugin.name, text, { - component: this.component, - componentId: this.assign.cmid, - filter: true, - contextLevel: 'module', - instanceId: this.assign.cmid, - courseId: this.assign.course, - }); - } - }); - } else { - // Create and add the control. - this.control = this.fb.control(text); - } - - // Calculate initial words. - if (this.wordLimitEnabled) { - this.words = this.textUtils.countWords(text); - } - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Text changed. - * - * @param text The new text. - */ - onChange(text: string): void { - // Count words if needed. - if (this.wordLimitEnabled) { - // Cancel previous wait. - clearTimeout(this.wordCountTimeout); - - // Wait before calculating, if the user keeps inputing we won't calculate. - // This is to prevent slowing down devices, this calculation can be slow if the text is long. - this.wordCountTimeout = setTimeout(() => { - this.words = this.textUtils.countWords(text); - }, 1500); - } - } -} diff --git a/src/addon/mod/assign/submission/onlinetext/lang/en.json b/src/addon/mod/assign/submission/onlinetext/lang/en.json deleted file mode 100644 index e49362133..000000000 --- a/src/addon/mod/assign/submission/onlinetext/lang/en.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "pluginname": "Online text submissions", - "wordlimitexceeded": "The word limit for this assignment is {{$a.limit}} words and you are attempting to submit {{$a.count}} words. Please review your submission and try again." -} \ No newline at end of file diff --git a/src/addon/mod/assign/submission/onlinetext/onlinetext.module.ts b/src/addon/mod/assign/submission/onlinetext/onlinetext.module.ts deleted file mode 100644 index 5ac1e16f8..000000000 --- a/src/addon/mod/assign/submission/onlinetext/onlinetext.module.ts +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModAssignSubmissionOnlineTextHandler } from './providers/handler'; -import { AddonModAssignSubmissionOnlineTextComponent } from './component/onlinetext'; -import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModAssignSubmissionOnlineTextComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - ], - providers: [ - AddonModAssignSubmissionOnlineTextHandler - ], - exports: [ - AddonModAssignSubmissionOnlineTextComponent - ], - entryComponents: [ - AddonModAssignSubmissionOnlineTextComponent - ] -}) -export class AddonModAssignSubmissionOnlineTextModule { - constructor(submissionDelegate: AddonModAssignSubmissionDelegate, handler: AddonModAssignSubmissionOnlineTextHandler) { - submissionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/assign/submission/onlinetext/providers/handler.ts b/src/addon/mod/assign/submission/onlinetext/providers/handler.ts deleted file mode 100644 index 572d61735..000000000 --- a/src/addon/mod/assign/submission/onlinetext/providers/handler.ts +++ /dev/null @@ -1,294 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreFileHelperProvider } from '@providers/file-helper'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { - AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin -} from '../../../providers/assign'; -import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline'; -import { AddonModAssignHelperProvider } from '../../../providers/helper'; -import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate'; -import { AddonModAssignSubmissionOnlineTextComponent } from '../component/onlinetext'; - -/** - * Handler for online text submission plugin. - */ -@Injectable() -export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssignSubmissionHandler { - name = 'AddonModAssignSubmissionOnlineTextHandler'; - type = 'onlinetext'; - - constructor(private translate: TranslateService, private sitesProvider: CoreSitesProvider, - private fileHelper: CoreFileHelperProvider, private textUtils: CoreTextUtilsProvider, - private assignProvider: AddonModAssignProvider, private assignOfflineProvider: AddonModAssignOfflineProvider, - private assignHelper: AddonModAssignHelperProvider) { } - - /** - * Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the - * plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit - * unfiltered data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @return Boolean or promise resolved with boolean: whether it can be edited in offline. - */ - canEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin): boolean | Promise { - // This plugin uses Moodle filters, it cannot be edited in offline. - return false; - } - - /** - * Check if a plugin has no data. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return Whether the plugin is empty. - */ - isEmpty(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): boolean { - const text = this.assignProvider.getSubmissionPluginText(plugin, true); - - // If the text is empty, we can ignore files because they won't be visible anyways. - return text.trim().length === 0; - } - - /** - * This function will be called when the user wants to create a new submission based on the previous one. - * It should add to pluginData the data to send to server based in the data in plugin (previous attempt). - * - * @param assign The assignment. - * @param plugin The plugin object. - * @param pluginData Object where to store the data to send. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - copySubmissionData(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any, - userId?: number, siteId?: string): void | Promise { - - const text = this.assignProvider.getSubmissionPluginText(plugin, true), - files = this.assignProvider.getSubmissionPluginAttachments(plugin); - let promise; - - if (!files.length) { - // No files to copy, no item ID. - promise = Promise.resolve(0); - } else { - // Re-upload the files. - promise = this.assignHelper.uploadFiles(assign.id, files, siteId); - } - - return promise.then((itemId) => { - pluginData.onlinetext_editor = { - text: text, - format: 1, - itemid: itemId - }; - }); - } - - /** - * Return the Component to use to display the plugin data, either in read or in edit mode. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param plugin The plugin object. - * @param edit Whether the user is editing. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise { - return AddonModAssignSubmissionOnlineTextComponent; - } - - /** - * Get files used by this plugin. - * The files returned by this function will be prefetched when the user prefetches the assign. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param siteId Site ID. If not defined, current site. - * @return The files (or promise resolved with the files). - */ - getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise { - return this.assignProvider.getSubmissionPluginAttachments(plugin); - } - - /** - * Get the size of data (in bytes) this plugin will send to copy a previous submission. - * - * @param assign The assignment. - * @param plugin The plugin object. - * @return The size (or promise resolved with size). - */ - async getSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): Promise { - const text = this.assignProvider.getSubmissionPluginText(plugin, true), - files = this.assignProvider.getSubmissionPluginAttachments(plugin); - - const filesSize = await this.fileHelper.getTotalFilesSize(files); - - return text.length + filesSize; - } - - /** - * Get the size of data (in bytes) this plugin will send to add or edit a submission. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return The size (or promise resolved with size). - */ - getSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): number | Promise { - const text = this.assignProvider.getSubmissionPluginText(plugin, true); - - return text.length; - } - - /** - * Get the text to submit. - * - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return Text to submit. - */ - protected getTextToSubmit(plugin: any, inputData: any): string { - const text = inputData.onlinetext_editor_text, - files = plugin.fileareas && plugin.fileareas[0] ? plugin.fileareas[0].files : []; - - return this.textUtils.restorePluginfileUrls(text, files); - } - - /** - * Check if the submission data has changed for this plugin. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @return Boolean (or promise resolved with boolean): whether the data has changed. - */ - hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any): boolean | Promise { - - // Get the original text from plugin or offline. - return this.assignOfflineProvider.getSubmission(assign.id, submission.userid).catch(() => { - // No offline data found. - }).then((data) => { - if (data && data.plugindata && data.plugindata.onlinetext_editor) { - return data.plugindata.onlinetext_editor.text; - } - - // No offline data found, get text from plugin. - return plugin.editorfields && plugin.editorfields[0] ? plugin.editorfields[0].text : ''; - }).then((initialText) => { - // Check if text has changed. - return initialText != this.getTextToSubmit(plugin, inputData); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether or not the handler is enabled for edit on a site level. - * - * @return Whether or not the handler is enabled for edit on a site level. - */ - isEnabledForEdit(): boolean | Promise { - // There's a bug in Moodle 3.1.0 that doesn't allow submitting HTML, so we'll disable this plugin in that case. - // Bug was fixed in 3.1.1 minor release and in 3.2. - const currentSite = this.sitesProvider.getCurrentSite(); - - return currentSite && (currentSite.isVersionGreaterEqualThan('3.1.1') || currentSite.checkIfAppUsesLocalMobile()); - } - - /** - * Prepare and add to pluginData the data to send to the server based on the input data. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param inputData Data entered by the user for the submission. - * @param pluginData Object where to store the data to send. - * @param offline Whether the user is editing in offline. - * @param userId User ID. If not defined, site's current user. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean, - userId?: number, siteId?: string): void | Promise { - - let text = this.getTextToSubmit(plugin, inputData); - - // Check word limit. - const configs = this.assignHelper.getPluginConfig(assign, 'assignsubmission', plugin.type); - if (parseInt(configs.wordlimitenabled, 10)) { - const words = this.textUtils.countWords(text); - const wordlimit = parseInt(configs.wordlimit, 10); - if (words > wordlimit) { - const params = {$a: {count: words, limit: wordlimit}}; - const message = this.translate.instant('addon.mod_assign_submission_onlinetext.wordlimitexceeded', params); - - return Promise.reject(message); - } - } - - // Add some HTML to the text if needed. - text = this.textUtils.formatHtmlLines(text); - - pluginData.onlinetext_editor = { - text: text, - format: 1, - itemid: 0 // Can't add new files yet, so we use a fake itemid. - }; - } - - /** - * Prepare and add to pluginData the data to send to the server based on the offline data stored. - * This will be used when performing a synchronization. - * - * @param assign The assignment. - * @param submission The submission. - * @param plugin The plugin object. - * @param offlineData Offline data stored. - * @param pluginData Object where to store the data to send. - * @param siteId Site ID. If not defined, current site. - * @return If the function is async, it should return a Promise resolved when done. - */ - prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, - plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise { - - const textData = offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor; - if (textData) { - // Has some data to sync. - pluginData.onlinetext_editor = textData; - } - } -} diff --git a/src/addon/mod/assign/submission/submission.module.ts b/src/addon/mod/assign/submission/submission.module.ts deleted file mode 100644 index 026731a5a..000000000 --- a/src/addon/mod/assign/submission/submission.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModAssignSubmissionCommentsModule } from './comments/comments.module'; -import { AddonModAssignSubmissionFileModule } from './file/file.module'; -import { AddonModAssignSubmissionOnlineTextModule } from './onlinetext/onlinetext.module'; - -@NgModule({ - declarations: [], - imports: [ - AddonModAssignSubmissionCommentsModule, - AddonModAssignSubmissionFileModule, - AddonModAssignSubmissionOnlineTextModule - ], - providers: [ - ], - exports: [] -}) -export class AddonModAssignSubmissionModule { } diff --git a/src/addon/mod/book/book.module.ts b/src/addon/mod/book/book.module.ts deleted file mode 100644 index 71c086dfd..000000000 --- a/src/addon/mod/book/book.module.ts +++ /dev/null @@ -1,61 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModBookComponentsModule } from './components/components.module'; -import { AddonModBookProvider } from './providers/book'; -import { AddonModBookModuleHandler } from './providers/module-handler'; -import { AddonModBookLinkHandler } from './providers/link-handler'; -import { AddonModBookListLinkHandler } from './providers/list-link-handler'; -import { AddonModBookPrefetchHandler } from './providers/prefetch-handler'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate'; -import { AddonModBookTagAreaHandler } from './providers/tag-area-handler'; - -// List of providers (without handlers). -export const ADDON_MOD_BOOK_PROVIDERS: any[] = [ - AddonModBookProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModBookComponentsModule - ], - providers: [ - AddonModBookProvider, - AddonModBookModuleHandler, - AddonModBookLinkHandler, - AddonModBookListLinkHandler, - AddonModBookPrefetchHandler, - AddonModBookTagAreaHandler - ] -}) -export class AddonModBookModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModBookModuleHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModBookLinkHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModBookPrefetchHandler, - listLinkHandler: AddonModBookListLinkHandler, tagAreaDelegate: CoreTagAreaDelegate, - tagAreaHandler: AddonModBookTagAreaHandler) { - - moduleDelegate.registerHandler(moduleHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - prefetchDelegate.registerHandler(prefetchHandler); - tagAreaDelegate.registerHandler(tagAreaHandler); - } -} diff --git a/src/addon/mod/book/components/components.module.ts b/src/addon/mod/book/components/components.module.ts deleted file mode 100644 index 805a25124..000000000 --- a/src/addon/mod/book/components/components.module.ts +++ /dev/null @@ -1,47 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModBookIndexComponent } from './index/index'; -import { CoreTagComponentsModule } from '@core/tag/components/components.module'; - -@NgModule({ - declarations: [ - AddonModBookIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule, - CoreTagComponentsModule - ], - providers: [ - ], - exports: [ - AddonModBookIndexComponent - ], - entryComponents: [ - AddonModBookIndexComponent - ] -}) -export class AddonModBookComponentsModule {} diff --git a/src/addon/mod/book/components/index/addon-mod-book-index.html b/src/addon/mod/book/components/index/addon-mod-book-index.html deleted file mode 100644 index 74ad51a52..000000000 --- a/src/addon/mod/book/components/index/addon-mod-book-index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - -
- - -
- {{ 'core.tag.tags' | translate }}: - -
- -
- -
diff --git a/src/addon/mod/book/components/index/index.ts b/src/addon/mod/book/components/index/index.ts deleted file mode 100644 index b52ed71cb..000000000 --- a/src/addon/mod/book/components/index/index.ts +++ /dev/null @@ -1,219 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector, Input } from '@angular/core'; -import { Content, ModalController } from 'ionic-angular'; -import { - CoreCourseModuleMainResourceComponent, CoreCourseResourceDownloadResult -} from '@core/course/classes/main-resource-component'; -import { - AddonModBookProvider, AddonModBookContentsMap, AddonModBookTocChapter, AddonModBookBook, AddonModBookNavStyle -} from '../../providers/book'; -import { CoreTagProvider } from '@core/tag/providers/tag'; - -/** - * Component that displays a book. - */ -@Component({ - selector: 'addon-mod-book-index', - templateUrl: 'addon-mod-book-index.html', -}) -export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComponent { - @Input() initialChapterId: string; // The initial chapter ID to load. - - component = AddonModBookProvider.COMPONENT; - chapterContent: string; - previousChapter: AddonModBookTocChapter; - nextChapter: AddonModBookTocChapter; - tagsEnabled: boolean; - displayNavBar = true; - previousNavBarTitle: string; - nextNavBarTitle: string; - warning: string; - - protected chapters: AddonModBookTocChapter[]; - protected currentChapter: string; - protected contentsMap: AddonModBookContentsMap; - protected book: AddonModBookBook; - protected displayTitlesInNavBar = false; - - constructor(injector: Injector, - protected bookProvider: AddonModBookProvider, - protected modalCtrl: ModalController, - protected tagProvider: CoreTagProvider, - @Optional() protected content: Content) { - super(injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.tagsEnabled = this.tagProvider.areTagsAvailableInSite(); - - this.loadContent(); - } - - /** - * Show the TOC. - * - * @param event Event. - */ - showToc(event: MouseEvent): void { - // Create the toc modal. - const modal = this.modalCtrl.create('AddonModBookTocPage', { - moduleId: this.module.id, - chapters: this.chapters, - selected: this.currentChapter, - courseId: this.courseId, - book: this.book, - }, { cssClass: 'core-modal-lateral', - showBackdrop: true, - enableBackdropDismiss: true, - enterAnimation: 'core-modal-lateral-transition', - leaveAnimation: 'core-modal-lateral-transition' }); - - modal.onDidDismiss((chapterId) => { - if (chapterId) { - this.changeChapter(chapterId); - } - }); - - modal.present({ - ev: event - }); - } - - /** - * Change the current chapter. - * - * @param chapterId Chapter to load. - * @return Promise resolved when done. - */ - changeChapter(chapterId: string): void { - if (chapterId && chapterId != this.currentChapter) { - this.loaded = false; - this.refreshIcon = 'spinner'; - this.loadChapter(chapterId, true); - } - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return this.bookProvider.invalidateContent(this.module.id, this.courseId); - } - - /** - * Download book contents and load the current chapter. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchContent(refresh?: boolean): Promise { - const promises = []; - let downloadResult: CoreCourseResourceDownloadResult; - - // Try to get the book data. - promises.push(this.bookProvider.getBook(this.courseId, this.module.id).then((book) => { - this.book = book; - this.dataRetrieved.emit(book); - this.description = book.intro; - this.displayNavBar = book.navstyle != AddonModBookNavStyle.TOC_ONLY; - this.displayTitlesInNavBar = book.navstyle == AddonModBookNavStyle.TEXT; - }).catch(() => { - // Ignore errors since this WS isn't available in some Moodle versions. - })); - - // Get module status to determine if it needs to be downloaded. - promises.push(this.downloadResourceIfNeeded(refresh).then((result) => { - downloadResult = result; - })); - - return Promise.all(promises).then(() => { - this.contentsMap = this.bookProvider.getContentsMap(this.module.contents); - this.chapters = this.bookProvider.getTocList(this.module.contents); - - if (typeof this.currentChapter == 'undefined' && typeof this.initialChapterId != 'undefined' && this.chapters) { - // Initial chapter set. Validate that the chapter exists. - const chapter = this.chapters.find((chapter) => { - return chapter.id == this.initialChapterId; - }); - - if (chapter) { - this.currentChapter = this.initialChapterId; - } - } - - if (typeof this.currentChapter == 'undefined') { - // Load the first chapter. - this.currentChapter = this.bookProvider.getFirstChapter(this.chapters); - } - - // Show chapter. - return this.loadChapter(this.currentChapter, refresh).then(() => { - this.warning = downloadResult.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error) : ''; - }).catch(() => { - // Ignore errors, they're handled inside the loadChapter function. - }); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Load a book chapter. - * - * @param chapterId Chapter to load. - * @param logChapterId Whether chapter ID should be passed to the log view function. - * @return Promise resolved when done. - */ - protected loadChapter(chapterId: string, logChapterId: boolean): Promise { - this.currentChapter = chapterId; - this.domUtils.scrollToTop(this.content); - - return this.bookProvider.getChapterContent(this.contentsMap, chapterId, this.module.id).then((content) => { - this.chapterContent = content; - this.previousChapter = this.bookProvider.getPreviousChapter(this.chapters, chapterId); - this.nextChapter = this.bookProvider.getNextChapter(this.chapters, chapterId); - - this.previousNavBarTitle = this.previousChapter && this.displayTitlesInNavBar ? - this.translate.instant('addon.mod_book.navprevtitle', {$a: this.previousChapter.title}) : ''; - this.nextNavBarTitle = this.nextChapter && this.displayTitlesInNavBar ? - this.translate.instant('addon.mod_book.navnexttitle', {$a: this.nextChapter.title}) : ''; - - // Chapter loaded, log view. We don't return the promise because we don't want to block the user for this. - this.bookProvider.logView(this.module.instance, logChapterId ? chapterId : undefined, this.module.name).then(() => { - // Module is completed when last chapter is viewed, so we only check completion if the last is reached. - if (!this.nextChapter) { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - }).catch(() => { - // Ignore errors. - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_book.errorchapter', true); - - return Promise.reject(null); - }).finally(() => { - this.loaded = true; - this.refreshIcon = 'refresh'; - }); - } -} diff --git a/src/addon/mod/book/lang/en.json b/src/addon/mod/book/lang/en.json deleted file mode 100644 index 200e96ce1..000000000 --- a/src/addon/mod/book/lang/en.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "errorchapter": "Error reading chapter of book.", - "modulenameplural": "Books", - "navnexttitle": "Next: {{$a}}", - "navprevtitle": "Previous: {{$a}}", - "tagarea_book_chapters": "Book chapters", - "toc": "Table of contents" -} \ No newline at end of file diff --git a/src/addon/mod/book/pages/index/index.html b/src/addon/mod/book/pages/index/index.html deleted file mode 100644 index 9e468397a..000000000 --- a/src/addon/mod/book/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/book/pages/index/index.module.ts b/src/addon/mod/book/pages/index/index.module.ts deleted file mode 100644 index 88032b387..000000000 --- a/src/addon/mod/book/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModBookComponentsModule } from '../../components/components.module'; -import { AddonModBookIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModBookIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModBookComponentsModule, - IonicPageModule.forChild(AddonModBookIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModBookIndexPageModule {} diff --git a/src/addon/mod/book/pages/index/index.ts b/src/addon/mod/book/pages/index/index.ts deleted file mode 100644 index f9f53e160..000000000 --- a/src/addon/mod/book/pages/index/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModBookIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a book. - */ -@IonicPage({ segment: 'addon-mod-book-index' }) -@Component({ - selector: 'page-addon-mod-book-index', - templateUrl: 'index.html', -}) -export class AddonModBookIndexPage { - @ViewChild(AddonModBookIndexComponent) bookComponent: AddonModBookIndexComponent; - - title: string; - module: any; - courseId: number; - chapterId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.chapterId = navParams.get('chapterId'); - this.title = this.module.name; - } - - /** - * Update some data based on the book instance. - * - * @param book Book instance. - */ - updateData(book: any): void { - this.title = book.name || this.title; - } -} diff --git a/src/addon/mod/book/pages/toc/toc.html b/src/addon/mod/book/pages/toc/toc.html deleted file mode 100644 index 5e6ec0247..000000000 --- a/src/addon/mod/book/pages/toc/toc.html +++ /dev/null @@ -1,23 +0,0 @@ - - - {{ 'addon.mod_book.toc' | translate }} - - - - - - - - diff --git a/src/addon/mod/book/pages/toc/toc.module.ts b/src/addon/mod/book/pages/toc/toc.module.ts deleted file mode 100644 index a96ddf55b..000000000 --- a/src/addon/mod/book/pages/toc/toc.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModBookTocPage } from './toc'; - -@NgModule({ - declarations: [ - AddonModBookTocPage, - ], - imports: [ - CoreDirectivesModule, - IonicPageModule.forChild(AddonModBookTocPage), - TranslateModule.forChild() - ], -}) -export class AddonModBookTocPageModule {} diff --git a/src/addon/mod/book/pages/toc/toc.scss b/src/addon/mod/book/pages/toc/toc.scss deleted file mode 100644 index 2ed350b72..000000000 --- a/src/addon/mod/book/pages/toc/toc.scss +++ /dev/null @@ -1,7 +0,0 @@ -ion-app.app-root page-addon-mod-book-toc { - .addon-mod-book-bullet { - font-weight: bold; - font-size: 1.5em; - margin-right: 3px; - } -} diff --git a/src/addon/mod/book/pages/toc/toc.ts b/src/addon/mod/book/pages/toc/toc.ts deleted file mode 100644 index d6dc1b3f8..000000000 --- a/src/addon/mod/book/pages/toc/toc.ts +++ /dev/null @@ -1,67 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; -import { AddonModBookTocChapter, AddonModBookBook, AddonModBookNumbering } from '../../providers/book'; - -/** - * Modal to display the TOC of a book. - */ -@IonicPage({ segment: 'addon-mod-book-toc-modal' }) -@Component({ - selector: 'page-addon-mod-book-toc', - templateUrl: 'toc.html' -}) -export class AddonModBookTocPage { - moduleId: number; - chapters: AddonModBookTocChapter[]; - selected: number; - courseId: number; - showNumbers = true; - addPadding = true; - showBullets = false; - - protected book: AddonModBookBook; - - constructor(navParams: NavParams, private viewCtrl: ViewController) { - this.moduleId = navParams.get('moduleId'); - this.chapters = navParams.get('chapters') || []; - this.selected = navParams.get('selected'); - this.courseId = navParams.get('courseId'); - this.book = navParams.get('book'); - - if (this.book) { - this.showNumbers = this.book.numbering == AddonModBookNumbering.NUMBERS; - this.showBullets = this.book.numbering == AddonModBookNumbering.BULLETS; - this.addPadding = this.book.numbering != AddonModBookNumbering.NONE; - } - } - - /** - * Function called when a course is clicked. - * - * @param id ID of the clicked chapter. - */ - loadChapter(id: string): void { - this.viewCtrl.dismiss(id); - } - - /** - * Close modal. - */ - closeModal(): void { - this.viewCtrl.dismiss(); - } -} diff --git a/src/addon/mod/book/providers/book.ts b/src/addon/mod/book/providers/book.ts deleted file mode 100644 index a9e4f143b..000000000 --- a/src/addon/mod/book/providers/book.ts +++ /dev/null @@ -1,485 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFileProvider } from '@providers/file'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreSite } from '@classes/site'; -import { CoreTagItem } from '@core/tag/providers/tag'; -import { CoreWSProvider, CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; - -/** - * Constants to define how the chapters and subchapters of a book should be displayed in that table of contents. - */ -export const enum AddonModBookNumbering { - NONE = 0, - NUMBERS = 1, - BULLETS = 2, - INDENTED = 3, -} - -/** - * Constants to define the navigation style used within a book. - */ -export const enum AddonModBookNavStyle { - TOC_ONLY = 0, - IMAGE = 1, - TEXT = 2, -} - -/** - * Service that provides some features for books. - */ -@Injectable() -export class AddonModBookProvider { - static COMPONENT = 'mmaModBook'; - - protected ROOT_CACHE_KEY = 'mmaModBook:'; - protected logger; - - constructor(logger: CoreLoggerProvider, - protected sitesProvider: CoreSitesProvider, - protected textUtils: CoreTextUtilsProvider, - protected fileProvider: CoreFileProvider, - protected filepoolProvider: CoreFilepoolProvider, - protected wsProvider: CoreWSProvider, - protected utils: CoreUtilsProvider, - protected courseProvider: CoreCourseProvider, - protected domUtils: CoreDomUtilsProvider, - protected logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModBookProvider'); - } - - /** - * Get a book by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the book is retrieved. - */ - getBook(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getBookByField(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a book with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the book is retrieved. - */ - protected getBookByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId] - }; - const preSets = { - cacheKey: this.getBookDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModBookProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_book_get_books_by_courses', params, preSets) - .then((response: AddonModBookGetBooksByCoursesResult): any => { - - // Search the book. - if (response && response.books) { - const book = response.books.find((book) => book[key] == value); - if (book) { - return book; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get book data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getBookDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'book:' + courseId; - } - - /** - * Gets a chapter contents. - * - * @param contentsMap Contents map returned by getContentsMap. - * @param chapterId Chapter to retrieve. - * @param moduleId The module ID. - * @return Promise resolved with the contents. - */ - getChapterContent(contentsMap: AddonModBookContentsMap, chapterId: string, moduleId: number): Promise { - const indexUrl = contentsMap[chapterId] ? contentsMap[chapterId].indexUrl : undefined, - siteId = this.sitesProvider.getCurrentSiteId(); - let promise; - - if (!indexUrl) { - // It shouldn't happen. - this.logger.debug('Could not locate the index chapter'); - - return Promise.reject(null); - } - - if (this.fileProvider.isAvailable()) { - promise = this.filepoolProvider.downloadUrl(siteId, indexUrl, false, AddonModBookProvider.COMPONENT, moduleId); - } else { - // We return the live URL. - return this.sitesProvider.getCurrentSite().checkAndFixPluginfileURL(indexUrl); - } - - return promise.then(async (url) => { - const content = await this.wsProvider.getText(url); - - // Now that we have the content, we update the SRC to point back to the external resource. - return this.domUtils.restoreSourcesInHtml(content, contentsMap[chapterId].paths); - }); - } - - /** - * Convert an array of book contents into an object where contents are organized in chapters. - * Each chapter has an indexUrl and the list of contents in that chapter. - * - * @param contents The module contents. - * @return Contents map. - */ - getContentsMap(contents: any[]): AddonModBookContentsMap { - const map: AddonModBookContentsMap = {}; - - if (!contents) { - return map; - } - - contents.forEach((content) => { - if (this.isFileDownloadable(content)) { - let chapter, - matches, - split, - filepathIsChapter, - key; - - // Search the chapter number in the filepath. - matches = content.filepath.match(/\/(\d+)\//); - if (matches && matches[1]) { - chapter = matches[1]; - filepathIsChapter = content.filepath == '/' + chapter + '/'; - - // Init the chapter if it's not defined yet. - map[chapter] = map[chapter] || { paths: {} }; - - if (content.filename == 'index.html' && filepathIsChapter) { - // Index of the chapter, set indexUrl and tags of the chapter. - map[chapter].indexUrl = content.fileurl; - map[chapter].tags = content.tags; - } else { - if (filepathIsChapter) { - // It's a file in the root folder OR the WS isn't returning the filepath as it should (MDL-53671). - // Try to get the path to the file from the URL. - split = content.fileurl.split('mod_book/chapter' + content.filepath); - key = split[1] || content.filename; // Use filename if we couldn't find the path. - } else { - // Remove the chapter folder from the path and add the filename. - key = content.filepath.replace('/' + chapter + '/', '') + content.filename; - } - - map[chapter].paths[this.textUtils.decodeURIComponent(key)] = content.fileurl; - } - } - } - }); - - return map; - } - - /** - * Get the first chapter of a book. - * - * @param chapters The chapters list. - * @return The chapter id. - */ - getFirstChapter(chapters: AddonModBookTocChapter[]): string { - if (!chapters || !chapters.length) { - return; - } - - return chapters[0].id; - } - - /** - * Get the next chapter to the given one. - * - * @param chapters The chapters list. - * @param chapterId The current chapter. - * @return The next chapter. - */ - getNextChapter(chapters: AddonModBookTocChapter[], chapterId: string): AddonModBookTocChapter { - let next: AddonModBookTocChapter; - - for (let i = 0; i < chapters.length; i++) { - if (chapters[i].id == chapterId) { - if (typeof chapters[i + 1] != 'undefined') { - next = chapters[i + 1]; - break; - } - } - } - - return next; - } - - /** - * Get the previous chapter to the given one. - * - * @param chapters The chapters list. - * @param chapterId The current chapter. - * @return The next chapter. - */ - getPreviousChapter(chapters: AddonModBookTocChapter[], chapterId: string): AddonModBookTocChapter { - let previous: AddonModBookTocChapter; - - for (let i = 0; i < chapters.length; i++) { - if (chapters[i].id == chapterId) { - break; - } - previous = chapters[i]; - } - - return previous; - } - - /** - * Get the book toc as an array. - * - * @param contents The module contents. - * @return The toc. - */ - getToc(contents: any[]): any[] { - if (!contents || !contents.length) { - return []; - } - - return this.textUtils.parseJSON(contents[0].content, []); - } - - /** - * Get the book toc as an array of chapters (not nested). - * - * @param contents The module contents. - * @return The toc as a list. - */ - getTocList(contents: any[]): AddonModBookTocChapter[] { - // Convenience function to get chapter info. - const getChapterInfo = (chapter: any, chapterNumber: number, previousNumber: string = ''): AddonModBookTocChapter => { - chapter.hidden = !!parseInt(chapter.hidden, 10); - - const fullChapterNumber = previousNumber + (chapter.hidden ? 'x.' : chapterNumber + '.'); - - return { - id: chapter.href.replace('/index.html', ''), - title: chapter.title, - level: chapter.level, - number: fullChapterNumber, - hidden: chapter.hidden - }; - }; - - const chapters = [], - toc = this.getToc(contents); - - let chapterNumber = 1; - toc.forEach((chapter) => { - const tocChapter = getChapterInfo(chapter, chapterNumber); - - // Add the chapter to the list. - chapters.push(tocChapter); - - if (chapter.subitems) { - let subChapterNumber = 1; - // Add all the subchapters to the list. - chapter.subitems.forEach((subChapter) => { - chapters.push(getChapterInfo(subChapter, subChapterNumber, tocChapter.number)); - subChapterNumber++; - }); - } - - chapterNumber++; - }); - - return chapters; - } - - /** - * Invalidates book data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateBookData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getBookDataCacheKey(courseId)); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.invalidateBookData(courseId, siteId)); - promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModBookProvider.COMPONENT, moduleId)); - promises.push(this.courseProvider.invalidateModule(moduleId, siteId)); - - return this.utils.allPromises(promises); - } - - /** - * Check if a file is downloadable. The file param must have a 'type' attribute like in core_course_get_contents response. - * - * @param file File to check. - * @return Whether it's downloadable. - */ - isFileDownloadable(file: any): boolean { - return file.type === 'file'; - } - - /** - * Return whether or not the plugin is enabled. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.canDownloadFiles(); - }); - } - - /** - * Report a book as being viewed. - * - * @param id Module ID. - * @param chapterId Chapter ID. - * @param name Name of the book. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, chapterId: string, name?: string, siteId?: string): Promise { - const params = { - bookid: id, - chapterid: chapterId - }; - - return this.logHelper.logSingle('mod_book_view_book', params, AddonModBookProvider.COMPONENT, id, name, 'book', - {chapterid: chapterId}, siteId); - } -} - -/** - * A book chapter inside the toc list. - */ -export interface AddonModBookTocChapter { - /** - * ID to identify the chapter. - */ - id: string; - - /** - * Chapter's title. - */ - title: string; - - /** - * The chapter's level. - */ - level: number; - - /** - * The chapter is hidden. - */ - hidden: boolean; - - /** - * The chapter's number'. - */ - number: string; -} - -/** - * Map of book contents. For each chapter it has its index URL and the list of paths of the files the chapter has. Each path - * is identified by the relative path in the book, and the value is the URL of the file. - */ -export type AddonModBookContentsMap = { - [chapter: string]: { - indexUrl?: string, - paths: {[path: string]: string}, - tags?: CoreTagItem[] - } -}; - -/** - * Book returned by mod_book_get_books_by_courses. - */ -export type AddonModBookBook = { - id: number; // Book id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Book name. - intro: string; // The Book intro. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles?: CoreWSExternalFile[]; // @since 3.2. - numbering: number; // Book numbering configuration. - navstyle: number; // Book navigation style configuration. - customtitles: number; // Book custom titles type. - revision?: number; // Book revision. - timecreated?: number; // Time of creation. - timemodified?: number; // Time of last modification. - section?: number; // Course section id. - visible?: boolean; // Visible. - groupmode?: number; // Group mode. - groupingid?: number; // Group id. -}; - -/** - * Result of WS mod_book_get_books_by_courses. - */ -export type AddonModBookGetBooksByCoursesResult = { - books: AddonModBookBook[]; - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/book/providers/link-handler.ts b/src/addon/mod/book/providers/link-handler.ts deleted file mode 100644 index 051d08429..000000000 --- a/src/addon/mod/book/providers/link-handler.ts +++ /dev/null @@ -1,56 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModBookProvider } from './book'; - -/** - * Handler to treat links to book. - */ -@Injectable() -export class AddonModBookLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModBookLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, - protected bookProvider: AddonModBookProvider) { - super(courseHelper, 'AddonModBook', 'book', 'b'); - } - - /** - * Get the mod params necessary to open an activity. - * - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of params to pass to navigateToModule / navigateToModuleByInstance. - */ - getPageParams(url: string, params: any, courseId?: number): any { - return params.chapterid ? {chapterId: parseInt(params.chapterid, 10)} : undefined; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.bookProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/book/providers/list-link-handler.ts b/src/addon/mod/book/providers/list-link-handler.ts deleted file mode 100644 index 00d694c34..000000000 --- a/src/addon/mod/book/providers/list-link-handler.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModBookProvider } from './book'; - -/** - * Handler to treat links to book list page. - */ -@Injectable() -export class AddonModBookListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModBookListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected bookProvider: AddonModBookProvider) { - super(linkHelper, translate, 'AddonModBook', 'book'); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.bookProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/book/providers/module-handler.ts b/src/addon/mod/book/providers/module-handler.ts deleted file mode 100644 index 01fa9a1c5..000000000 --- a/src/addon/mod/book/providers/module-handler.ts +++ /dev/null @@ -1,91 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModBookProvider } from './book'; -import { AddonModBookIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support book modules. - */ -@Injectable() -export class AddonModBookModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModBook'; - modName = 'book'; - - supportedFeatures = { - [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(protected bookProvider: AddonModBookProvider, private courseProvider: CoreCourseProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.bookProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_book-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModBookIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param course The course object. - * @param module The module object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getMainComponent(injector: Injector, course: any, module: any): any | Promise { - return AddonModBookIndexComponent; - } -} diff --git a/src/addon/mod/book/providers/prefetch-handler.ts b/src/addon/mod/book/providers/prefetch-handler.ts deleted file mode 100644 index d4eb5903a..000000000 --- a/src/addon/mod/book/providers/prefetch-handler.ts +++ /dev/null @@ -1,109 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; -import { AddonModBookProvider } from './book'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch books. - */ -@Injectable() -export class AddonModBookPrefetchHandler extends CoreCourseResourcePrefetchHandlerBase { - name = 'AddonModBook'; - modName = 'book'; - component = AddonModBookProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^entries$/; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected bookProvider: AddonModBookProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Download or prefetch the content. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param prefetch True to prefetch, false to download right away. - * @param dirPath Path of the directory where to store all the content files. This is to keep the files - * relative paths and make the package work in an iframe. Undefined to download the files - * in the filepool root folder. - * @return Promise resolved when all content is downloaded. Data returned is not reliable. - */ - downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise { - const promises = []; - - promises.push(super.downloadOrPrefetch(module, courseId, prefetch)); - promises.push(this.bookProvider.getBook(courseId, module.id).catch(() => { - // Ignore errors since this WS isn't available in some Moodle versions. - })); - - return Promise.all(promises); - } - - /** - * Returns module intro files. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @return Promise resolved with list of intro files. - */ - getIntroFiles(module: any, courseId: number): Promise { - return this.bookProvider.getBook(courseId, module.id).catch(() => { - // Not found, return undefined so module description is used. - }).then((book) => { - return this.getIntroFilesFromInstance(module, book); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.bookProvider.invalidateContent(moduleId, courseId); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.bookProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/book/providers/tag-area-handler.ts b/src/addon/mod/book/providers/tag-area-handler.ts deleted file mode 100644 index 8f226ff94..000000000 --- a/src/addon/mod/book/providers/tag-area-handler.ts +++ /dev/null @@ -1,75 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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 { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { AddonModBookProvider } from './book'; - -/** - * Handler to support tags. - */ -@Injectable() -export class AddonModBookTagAreaHandler implements CoreTagAreaHandler { - name = 'AddonModBookTagAreaHandler'; - type = 'mod_book/book_chapters'; - - constructor(private tagHelper: CoreTagHelperProvider, private bookProvider: AddonModBookProvider, - private courseProvider: CoreCourseProvider, private urlUtils: CoreUrlUtilsProvider) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.bookProvider.isPluginEnabled(); - } - - /** - * Parses the rendered content of a tag index and returns the items. - * - * @param content Rendered content. - * @return Area items (or promise resolved with the items). - */ - parseContent(content: string): any[] | Promise { - const items = this.tagHelper.parseFeedContent(content); - - // Find module ids of the returned books, they are needed by the link delegate. - return Promise.all(items.map((item) => { - const params = this.urlUtils.extractUrlParams(item.url); - if (params.b && !params.id) { - const bookId = parseInt(params.b, 10); - - return this.courseProvider.getModuleBasicInfoByInstance(bookId, 'book').then((module) => { - item.url += '&id=' + module.id; - }); - } - })).then(() => { - return items; - }); - } - - /** - * Get the component to use to display items. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return CoreTagFeedComponent; - } -} diff --git a/src/addon/mod/chat/chat.module.ts b/src/addon/mod/chat/chat.module.ts deleted file mode 100644 index 45934fd7b..000000000 --- a/src/addon/mod/chat/chat.module.ts +++ /dev/null @@ -1,58 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { AddonModChatComponentsModule } from './components/components.module'; -import { AddonModChatProvider } from './providers/chat'; -import { AddonModChatHelperProvider } from './providers/helper'; -import { AddonModChatLinkHandler } from './providers/link-handler'; -import { AddonModChatListLinkHandler } from './providers/list-link-handler'; -import { AddonModChatModuleHandler } from './providers/module-handler'; -import { AddonModChatPrefetchHandler } from './providers/prefetch-handler'; - -// List of providers (without handlers). -export const ADDON_MOD_CHAT_PROVIDERS: any[] = [ - AddonModChatProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModChatComponentsModule - ], - providers: [ - AddonModChatProvider, - AddonModChatHelperProvider, - AddonModChatLinkHandler, - AddonModChatListLinkHandler, - AddonModChatModuleHandler, - AddonModChatPrefetchHandler - ] -}) -export class AddonModChatModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModChatModuleHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModChatLinkHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModChatPrefetchHandler, - listLinkHandler: AddonModChatListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - prefetchDelegate.registerHandler(prefetchHandler); - } -} diff --git a/src/addon/mod/chat/components/components.module.ts b/src/addon/mod/chat/components/components.module.ts deleted file mode 100644 index 6dfb975cc..000000000 --- a/src/addon/mod/chat/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModChatIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModChatIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModChatIndexComponent - ], - entryComponents: [ - AddonModChatIndexComponent - ] -}) -export class AddonModChatComponentsModule {} diff --git a/src/addon/mod/chat/components/index/addon-mod-chat-index.html b/src/addon/mod/chat/components/index/addon-mod-chat-index.html deleted file mode 100644 index 85ae32c07..000000000 --- a/src/addon/mod/chat/components/index/addon-mod-chat-index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - {{ 'addon.mod_chat.sessionstart' | translate:{$a: chatInfo} }} - - - - diff --git a/src/addon/mod/chat/components/index/index.ts b/src/addon/mod/chat/components/index/index.ts deleted file mode 100644 index a61d51f3c..000000000 --- a/src/addon/mod/chat/components/index/index.ts +++ /dev/null @@ -1,112 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Injector } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { AddonModChatProvider, AddonModChatChat } from '../../providers/chat'; - -/** - * Component that displays a chat. - */ -@Component({ - selector: 'addon-mod-chat-index', - templateUrl: 'addon-mod-chat-index.html', -}) -export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComponent { - component = AddonModChatProvider.COMPONENT; - moduleName = 'chat'; - - chat: AddonModChatChat; - chatInfo: any; - - protected title: string; - protected sessionsAvailable = false; - - constructor(injector: Injector, private chatProvider: AddonModChatProvider, private timeUtils: CoreTimeUtilsProvider, - protected navCtrl: NavController) { - super(injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.loadContent().then(() => { - this.chatProvider.logView(this.chat.id, this.chat.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Download chat. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - return this.chatProvider.getChat(this.courseId, this.module.id).then((chat) => { - this.chat = chat; - this.description = chat.intro; - - const now = this.timeUtils.timestamp(); - const span = chat.chattime - now; - - if (chat.chattime && chat.schedule > 0 && span > 0) { - this.chatInfo = { - date: this.timeUtils.userDate(chat.chattime * 1000), - fromnow: this.timeUtils.formatTime(span) - }; - } else { - this.chatInfo = false; - } - - this.dataRetrieved.emit(chat); - - return this.chatProvider.areSessionsAvailable().then((available) => { - this.sessionsAvailable = available; - }); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Enter the chat. - */ - enterChat(): void { - const title = this.chat.name || this.moduleName; - this.navCtrl.push('AddonModChatChatPage', { - chatId: this.chat.id, - courseId: this.courseId, - title: title, - cmId: this.module.id - }); - } - - /** - * View past sessions. - */ - viewSessions(): void { - this.navCtrl.push('AddonModChatSessionsPage', {courseId: this.courseId, chatId: this.chat.id, cmId: this.module.id}); - } -} diff --git a/src/addon/mod/chat/lang/en.json b/src/addon/mod/chat/lang/en.json deleted file mode 100644 index ad8f86ce8..000000000 --- a/src/addon/mod/chat/lang/en.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "beep": "Beep", - "chatreport": "Chat sessions", - "currentusers": "Current users", - "enterchat": "Click here to enter the chat now", - "entermessage": "Enter your message", - "errorwhileconnecting": "Error while connecting to the chat.", - "errorwhilegettingchatdata": "Error while getting chat data.", - "errorwhilegettingchatusers": "Error while getting chat users.", - "errorwhileretrievingmessages": "Error while retrieving messages from the server.", - "errorwhilesendingmessage": "Error while sending the message.", - "messagebeepseveryone": "{{$a}} beeps everyone!", - "messagebeepsyou": "{{$a}} has just beeped you!", - "messageenter": "{{$a}} has just entered this chat", - "messageexit": "{{$a}} has left this chat", - "messages": "Messages", - "messageyoubeep": "You beeped {{$a}}", - "modulenameplural": "Chats", - "mustbeonlinetosendmessages": "You must be online to send messages.", - "nomessages": "No messages yet", - "nosessionsfound": "No sessions found", - "saidto": "said to", - "send": "Send", - "sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)", - "showincompletesessions": "Show incomplete sessions", - "talk": "Talk", - "viewreport": "View past chat sessions" -} \ No newline at end of file diff --git a/src/addon/mod/chat/pages/chat/chat.html b/src/addon/mod/chat/pages/chat/chat.html deleted file mode 100644 index 2784a7732..000000000 --- a/src/addon/mod/chat/pages/chat/chat.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - -
- {{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }} -
- -
- - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} - {{ 'addon.mod_chat.messagebeepseveryone' | translate:{$a: message.userfullname} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} - {{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} - {{ 'addon.mod_chat.messageyoubeep' | translate:{$a: message.beepWho} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} - {{ message.userfullname }} - -
- - - -

- -
{{ message.userfullname }}
- {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }} -

- -

- -

-
-
-
- -
- -
-
- - -

{{ 'addon.mod_chat.mustbeonlinetosendmessages' | translate }}

- - -
-
diff --git a/src/addon/mod/chat/pages/chat/chat.module.ts b/src/addon/mod/chat/pages/chat/chat.module.ts deleted file mode 100644 index 7b26e45da..000000000 --- a/src/addon/mod/chat/pages/chat/chat.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModChatChatPage } from './chat'; - -@NgModule({ - declarations: [ - AddonModChatChatPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonModChatChatPage), - TranslateModule.forChild() - ], -}) -export class AddonModChatChatPageModule {} diff --git a/src/addon/mod/chat/pages/chat/chat.scss b/src/addon/mod/chat/pages/chat/chat.scss deleted file mode 100644 index e9eb2ef73..000000000 --- a/src/addon/mod/chat/pages/chat/chat.scss +++ /dev/null @@ -1,8 +0,0 @@ -ion-app.app-root page-addon-mod-chat-chat.ion-page { - @include message-page(); - - .addon-mod-chat-notice { - margin-top: 10px; - margin-bottom: 10px; - } -} diff --git a/src/addon/mod/chat/pages/chat/chat.ts b/src/addon/mod/chat/pages/chat/chat.ts deleted file mode 100644 index f5f50d7ba..000000000 --- a/src/addon/mod/chat/pages/chat/chat.ts +++ /dev/null @@ -1,387 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, NgZone } from '@angular/core'; -import { Content, IonicPage, ModalController, NavController, NavParams } from 'ionic-angular'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModChatProvider } from '../../providers/chat'; -import { AddonModChatHelperProvider, AddonModChatMessageForView } from '../../providers/helper'; -import { Network } from '@ionic-native/network'; -import { coreSlideInOut } from '@classes/animations'; -import { CoreSendMessageFormComponent } from '@components/send-message-form/send-message-form'; - -/** - * Page that displays a chat session. - */ -@IonicPage({ segment: 'addon-mod-chat-chat' }) -@Component({ - selector: 'page-addon-mod-chat-chat', - templateUrl: 'chat.html', - animations: [coreSlideInOut] -}) -export class AddonModChatChatPage { - @ViewChild(Content) content: Content; - @ViewChild(CoreSendMessageFormComponent) sendMessageForm: CoreSendMessageFormComponent; - - loaded = false; - title: string; - messages: AddonModChatMessageForView[] = []; - newMessage: string; - polling: any; - isOnline: boolean; - currentUserId: number; - sending: boolean; - cmId: number; - - protected logger; - protected courseId: number; - protected chatId: number; - protected sessionId: string; - protected lastTime = 0; - protected oldContentHeight = 0; - protected onlineObserver: any; - protected keyboardObserver: any; - protected viewDestroyed = false; - protected pollingRunning = false; - protected users = []; - - constructor(navParams: NavParams, logger: CoreLoggerProvider, network: Network, zone: NgZone, private navCtrl: NavController, - private chatProvider: AddonModChatProvider, private appProvider: CoreAppProvider, sitesProvider: CoreSitesProvider, - private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider, - private eventsProvider: CoreEventsProvider, private chatHelper: AddonModChatHelperProvider) { - - this.chatId = navParams.get('chatId'); - this.courseId = navParams.get('courseId'); - this.title = navParams.get('title'); - this.cmId = navParams.get('cmId'); - - this.logger = logger.getInstance('AddonModChoiceChoicePage'); - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - this.isOnline = this.appProvider.isOnline(); - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.isOnline = this.appProvider.isOnline(); - }); - }); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.loginUser().then(() => { - return this.fetchMessages().then(() => { - this.startPolling(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhileretrievingmessages', true); - this.navCtrl.pop(); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhileconnecting', true); - this.navCtrl.pop(); - }).finally(() => { - this.loaded = true; - }); - - // Recalculate footer position when keyboard is shown or hidden. - this.keyboardObserver = this.eventsProvider.on(CoreEventsProvider.KEYBOARD_CHANGE, (kbHeight) => { - this.content.resize(); - }); - } - - /** - * Runs when the page has fully entered and is now the active page. - * This event will fire, whether it was the first load or a cached page. - */ - ionViewDidEnter(): void { - this.startPolling(); - } - - /** - * Runs when the page is about to leave and no longer be the active page. - */ - ionViewWillLeave(): void { - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'chat' }); - this.stopPolling(); - } - - /** - * Display the chat users modal. - */ - showChatUsers(): void { - // Create the toc modal. - const modal = this.modalCtrl.create('AddonModChatUsersPage', { - sessionId: this.sessionId, - cmId: this.cmId, - }, { cssClass: 'core-modal-lateral', - showBackdrop: true, - enableBackdropDismiss: true, - enterAnimation: 'core-modal-lateral-transition', - leaveAnimation: 'core-modal-lateral-transition' }); - - modal.onDidDismiss((data) => { - if (data && data.talkTo) { - this.newMessage = `To ${data.talkTo}: ` + (this.sendMessageForm.message || ''); - } - if (data && data.beepTo) { - this.sendMessage('', data.beepTo); - } - if (data && data.users) { - this.users = data.users; - } - }); - - modal.present({ - ev: event - }); - } - - /** - * Get the user fullname for a beep. - * - * @param id User Id before parsing. - * @return User fullname. - */ - protected getUserFullname(id: string): Promise { - if (isNaN(parseInt(id, 10))) { - return Promise.resolve(id); - } - - const user = this.users.find((user) => user.id == id); - - if (user) { - return Promise.resolve(user.fullname); - } - - return this.chatProvider.getChatUsers(this.sessionId, {cmId: this.cmId}).then((data) => { - this.users = data.users; - const user = this.users.find((user) => user.id == id); - - if (user) { - return user.fullname; - } - - return id; - }).catch((error) => { - // Ignore errors. - return id; - }); - } - - /** - * Convenience function to login the user. - * - * @return Promise resolved when done. - */ - protected loginUser(): Promise { - return this.chatProvider.loginUser(this.chatId).then((sessionId) => { - this.sessionId = sessionId; - }); - } - - /** - * Convenience function to fetch chat messages. - * - * @return Promise resolved when done. - */ - protected fetchMessages(): Promise { - return this.chatProvider.getLatestMessages(this.sessionId, this.lastTime).then((messagesInfo) => { - this.lastTime = messagesInfo.chatnewlasttime || 0; - - return this.chatProvider.getMessagesUserData(messagesInfo.messages, this.courseId).then((messages) => { - if (messages.length) { - const previousLength = this.messages.length; - this.messages = this.messages.concat( messages); - - // Calculate which messages need to display the date or user data. - for (let index = previousLength ; index < this.messages.length; index++) { - const message = this.messages[index]; - const prevMessage = index > 0 ? this.messages[index - 1] : null; - - this.chatHelper.formatMessage(this.currentUserId, message, prevMessage); - - if (message.beep && message.beep != this.currentUserId + '') { - this.getUserFullname(message.beep).then((fullname) => { - message.beepWho = fullname; - }); - } - } - - this.messages[this.messages.length - 1].showTail = true; - - // New messages or beeps, scroll to bottom. - setTimeout(() => this.scrollToBottom()); - } - }); - }); - } - - /** - * Start the polling to get chat messages periodically. - */ - protected startPolling(): void { - // We already have the polling in place. - if (this.polling) { - return; - } - - // Start polling. - this.polling = setInterval(() => { - this.fetchMessagesInterval().catch(() => { - // Ignore errors. - }); - }, AddonModChatProvider.POLL_INTERVAL); - } - - /** - * Stop polling for messages. - */ - protected stopPolling(): void { - if (this.polling) { - this.logger.debug('Cancelling polling for messages'); - clearInterval(this.polling); - } - } - - /** - * Convenience function to be called every certain time to fetch chat messages. - * - * @return Promise resolved when done. - */ - protected fetchMessagesInterval(): Promise { - this.logger.debug('Polling for messages'); - if (!this.isOnline || this.pollingRunning) { - // Obviously we cannot check for new messages when the app is offline. - return Promise.reject(null); - } - - this.pollingRunning = true; - - return this.fetchMessages().catch(() => { - // Try to login, it might have failed because the session expired. - return this.loginUser().then(() => { - return this.fetchMessages(); - }).catch((error) => { - // Fail again. Stop polling if needed. - if (this.polling) { - clearInterval(this.polling); - this.polling = undefined; - } - this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhileretrievingmessages', true); - - return Promise.reject(null); - }); - }).finally(() => { - this.pollingRunning = false; - }); - } - - /** - * Send a message to the chat. - * - * @param text Text of the nessage. - * @param beep ID of the user to beep. - */ - sendMessage(text: string, beep: number = 0): void { - if (!this.isOnline) { - // Silent error, the view should prevent this. - return; - } else if (beep === 0 && !text.trim()) { - // Silent error. - return; - } - - this.sending = true; - this.chatProvider.sendMessage(this.sessionId, text, beep).then(() => { - // Update messages to show the sent message. - this.fetchMessagesInterval().catch(() => { - // Ignore errors. - }); - }).catch((error) => { - /* Only close the keyboard if an error happens, we want the user to be able to send multiple - messages without the keyboard being closed. */ - this.appProvider.closeKeyboard(); - - this.newMessage = text; - - this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilesendingmessage', true); - }).finally(() => { - this.sending = false; - }); - } - - reconnect(): Promise { - const modal = this.domUtils.showModalLoading(); - - // Call startPolling would take a while for the first execution, so we'll execute it manually to check if it works now. - return this.fetchMessagesInterval().then(() => { - // It works, start the polling again. - this.startPolling(); - }).catch(() => { - // Ignore errors. - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Scroll bottom when render has finished. - */ - scrollToBottom(): void { - // Need a timeout to leave time to the view to be rendered. - setTimeout(() => { - if (!this.viewDestroyed) { - this.domUtils.scrollToBottom(this.content, 0); - } - }); - } - - /** - * Content or scroll has been resized. For content, only call it if it's been added on top. - */ - resizeContent(): void { - let top = this.content.getContentDimensions().scrollTop; - this.content.resize(); - - // Wait for new content height to be calculated. - setTimeout(() => { - // Visible content size changed, maintain the bottom position. - if (!this.viewDestroyed && this.content && this.domUtils.getContentHeight(this.content) != this.oldContentHeight) { - if (!top) { - top = this.content.getContentDimensions().scrollTop; - } - - top += this.oldContentHeight - this.domUtils.getContentHeight(this.content); - this.oldContentHeight = this.domUtils.getContentHeight(this.content); - - this.content.scrollTo(0, top, 0); - } - }); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.onlineObserver && this.onlineObserver.unsubscribe(); - this.keyboardObserver && this.keyboardObserver.off(); - this.stopPolling(); - this.viewDestroyed = true; - } -} diff --git a/src/addon/mod/chat/pages/index/index.html b/src/addon/mod/chat/pages/index/index.html deleted file mode 100644 index 52c0b1f3c..000000000 --- a/src/addon/mod/chat/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/chat/pages/index/index.module.ts b/src/addon/mod/chat/pages/index/index.module.ts deleted file mode 100644 index 9ee30df8f..000000000 --- a/src/addon/mod/chat/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModChatComponentsModule } from '../../components/components.module'; -import { AddonModChatIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModChatIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModChatComponentsModule, - IonicPageModule.forChild(AddonModChatIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModChatIndexPageModule {} diff --git a/src/addon/mod/chat/pages/index/index.ts b/src/addon/mod/chat/pages/index/index.ts deleted file mode 100644 index b95eaab42..000000000 --- a/src/addon/mod/chat/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModChatIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a chat. - */ -@IonicPage({ segment: 'addon-mod-chat-index' }) -@Component({ - selector: 'page-addon-mod-chat-index', - templateUrl: 'index.html', -}) -export class AddonModChatIndexPage { - @ViewChild(AddonModChatIndexComponent) chatComponent: AddonModChatIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the chat instance. - * - * @param chat Chat instance. - */ - updateData(chat: any): void { - this.title = chat.name || this.title; - } -} diff --git a/src/addon/mod/chat/pages/session-messages/session-messages.html b/src/addon/mod/chat/pages/session-messages/session-messages.html deleted file mode 100644 index 0b662db80..000000000 --- a/src/addon/mod/chat/pages/session-messages/session-messages.html +++ /dev/null @@ -1,63 +0,0 @@ - - - {{ 'addon.mod_chat.messages' | translate }} - - - - - - - - - -
- {{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }} -
- -
- - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} - {{ 'addon.mod_chat.messagebeepseveryone' | translate:{$a: message.userfullname} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} - {{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} - {{ 'addon.mod_chat.messageyoubeep' | translate:{$a: message.beepWho} }} - - - - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} - {{ message.userfullname }} - -
- - - -

- -
{{ message.userfullname }}
- {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }} -

- -

- -

-
-
-
-
-
-
diff --git a/src/addon/mod/chat/pages/session-messages/session-messages.module.ts b/src/addon/mod/chat/pages/session-messages/session-messages.module.ts deleted file mode 100644 index d5c1c8835..000000000 --- a/src/addon/mod/chat/pages/session-messages/session-messages.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModChatComponentsModule } from '../../components/components.module'; -import { AddonModChatSessionMessagesPage } from './session-messages'; - -@NgModule({ - declarations: [ - AddonModChatSessionMessagesPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - AddonModChatComponentsModule, - IonicPageModule.forChild(AddonModChatSessionMessagesPage), - TranslateModule.forChild() - ], -}) -export class AddonModChatSessionMessagesPageModule {} diff --git a/src/addon/mod/chat/pages/session-messages/session-messages.scss b/src/addon/mod/chat/pages/session-messages/session-messages.scss deleted file mode 100644 index ef01df6e2..000000000 --- a/src/addon/mod/chat/pages/session-messages/session-messages.scss +++ /dev/null @@ -1,8 +0,0 @@ -ion-app.app-root page-addon-mod-chat-session-messages.ion-page { - @include message-page(); - - .addon-mod-chat-notice { - margin-top: 10px; - margin-bottom: 10px; - } -} diff --git a/src/addon/mod/chat/pages/session-messages/session-messages.ts b/src/addon/mod/chat/pages/session-messages/session-messages.ts deleted file mode 100644 index aa894c218..000000000 --- a/src/addon/mod/chat/pages/session-messages/session-messages.ts +++ /dev/null @@ -1,125 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModChatProvider } from '../../providers/chat'; -import { AddonModChatHelperProvider, AddonModChatSessionMessageForView } from '../../providers/helper'; - -/** - * Page that displays list of chat session messages. - */ -@IonicPage({ segment: 'addon-mod-chat-session-messages' }) -@Component({ - selector: 'page-addon-mod-chat-session-messages', - templateUrl: 'session-messages.html', -}) -export class AddonModChatSessionMessagesPage { - - currentUserId: number; - cmId: number; - messages: AddonModChatSessionMessageForView[] = []; - loaded = false; - - protected courseId: number; - protected chatId: number; - protected sessionStart: number; - protected sessionEnd: number; - protected groupId: number; - - constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider, - sitesProvider: CoreSitesProvider, private chatHelper: AddonModChatHelperProvider, private userProvider: CoreUserProvider) { - this.courseId = navParams.get('courseId'); - this.chatId = navParams.get('chatId'); - this.groupId = navParams.get('groupId'); - this.sessionStart = navParams.get('sessionStart'); - this.sessionEnd = navParams.get('sessionEnd'); - this.cmId = navParams.get('cmId'); - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - - this.fetchMessages(); - } - - /** - * Fetch session messages. - * - * @return Promise resolved when done. - */ - protected fetchMessages(): Promise { - return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId, - {cmId: this.cmId}).then((messages) => { - return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => { - this.messages = messages; - - if (messages.length) { - // Calculate which messages need to display the date or user data. - for (let index = 0 ; index < this.messages.length; index++) { - const message = this.messages[index]; - const prevMessage = index > 0 ? this.messages[index - 1] : null; - - this.chatHelper.formatMessage(this.currentUserId, message, prevMessage); - - if (message.beep && message.beep != this.currentUserId + '') { - this.getUserFullname(message.beep).then((fullname) => { - message.beepWho = fullname; - }); - } - } - - this.messages[this.messages.length - 1].showTail = true; - } - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Get the user fullname for a beep. - * - * @param id User Id before parsing. - * @return User fullname. - */ - protected getUserFullname(id: string): Promise { - if (isNaN(parseInt(id, 10))) { - return Promise.resolve(id); - } - - return this.userProvider.getProfile(parseInt(id, 10), this.courseId, true).then((user) => { - return user.fullname; - }).catch(() => { - // Error getting profile. - return id; - }); - } - - /** - * Refresh session messages. - * - * @param refresher Refresher. - */ - refreshMessages(refresher: any): void { - this.chatProvider.invalidateSessionMessages(this.chatId, this.sessionStart, this.groupId).finally(() => { - this.fetchMessages().finally(() => { - refresher.complete(); - }); - }); - } - -} diff --git a/src/addon/mod/chat/pages/sessions/sessions.html b/src/addon/mod/chat/pages/sessions/sessions.html deleted file mode 100644 index b77fe3601..000000000 --- a/src/addon/mod/chat/pages/sessions/sessions.html +++ /dev/null @@ -1,45 +0,0 @@ - - - {{ 'addon.mod_chat.chatreport' | translate }} - - - - - - - - - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - - {{ 'addon.mod_chat.showincompletesessions' | translate }} - - - - -

{{ session.sessionstart * 1000 | coreFormatDate }}

-

{{ session.duration | coreDuration }}

-
- - - {{ user.userfullname }} ({{ user.messagecount }}) - - -
- -
-
- - -
-
-
diff --git a/src/addon/mod/chat/pages/sessions/sessions.module.ts b/src/addon/mod/chat/pages/sessions/sessions.module.ts deleted file mode 100644 index 1c8a8767d..000000000 --- a/src/addon/mod/chat/pages/sessions/sessions.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModChatComponentsModule } from '../../components/components.module'; -import { AddonModChatSessionsPage } from './sessions'; - -@NgModule({ - declarations: [ - AddonModChatSessionsPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - AddonModChatComponentsModule, - IonicPageModule.forChild(AddonModChatSessionsPage), - TranslateModule.forChild() - ], -}) -export class AddonModChatSessionsPageModule {} diff --git a/src/addon/mod/chat/pages/sessions/sessions.scss b/src/addon/mod/chat/pages/sessions/sessions.scss deleted file mode 100644 index 066605cdc..000000000 --- a/src/addon/mod/chat/pages/sessions/sessions.scss +++ /dev/null @@ -1,8 +0,0 @@ -ion-app.app-root page-addon-mod-chat-sessions { - .addon-mod-chat-session-show-more .card-content{ - padding-bottom: 0; - } - .addon-mod-chat-session-selected { - border-top: 5px solid $core-splitview-selected; - } -} diff --git a/src/addon/mod/chat/pages/sessions/sessions.ts b/src/addon/mod/chat/pages/sessions/sessions.ts deleted file mode 100644 index 2b62d4ad9..000000000 --- a/src/addon/mod/chat/pages/sessions/sessions.ts +++ /dev/null @@ -1,174 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModChatProvider, AddonModChatSession, AddonModChatSessionUser } from '../../providers/chat'; - -/** - * Page that displays list of chat sessions. - */ -@IonicPage({ segment: 'addon-mod-chat-sessions' }) -@Component({ - selector: 'page-addon-mod-chat-sessions', - templateUrl: 'sessions.html', -}) -export class AddonModChatSessionsPage { - - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - protected courseId: number; - protected cmId: number; - protected chatId: number; - protected loaded = false; - protected showAll = false; - protected groupId = 0; - protected groupInfo: CoreGroupInfo; - protected sessions = []; - protected selectedSessionStart: number; - protected selectedSessionGroupId: number; - - constructor(navParams: NavParams, private chatProvider: AddonModChatProvider, private domUtils: CoreDomUtilsProvider, - private userProvider: CoreUserProvider, private groupsProvider: CoreGroupsProvider, - private translate: TranslateService, private utils: CoreUtilsProvider) { - this.courseId = navParams.get('courseId'); - this.cmId = navParams.get('cmId'); - this.chatId = navParams.get('chatId'); - - this.fetchSessions().then(() => { - if (this.splitviewCtrl.isOn() && this.sessions.length > 0) { - this.openSession(this.sessions[0]); - } - }); - } - - /** - * Fetch chat sessions. - * - * @param showLoading Display a loading modal. - * @return Promise resolved when done. - */ - fetchSessions(showLoading?: boolean): Promise { - const modal = showLoading ? this.domUtils.showModalLoading() : null; - - return this.groupsProvider.getActivityGroupInfo(this.cmId, false).then((groupInfo) => { - this.groupInfo = groupInfo; - this.groupId = this.groupsProvider.validateGroupId(this.groupId, groupInfo); - - return this.chatProvider.getSessions(this.chatId, this.groupId, this.showAll, {cmId: this.cmId}); - }).then((sessions: AddonModChatSessionFormatted[]) => { - // Fetch user profiles. - const promises = []; - - sessions.forEach((session) => { - session.duration = session.sessionend - session.sessionstart; - session.sessionusers.forEach((sessionUser: AddonModChatUserSessionFormatted) => { - if (!sessionUser.userfullname) { - // The WS does not return the user name, fetch user profile. - promises.push(this.userProvider.getProfile(sessionUser.userid, this.courseId, true).then((user) => { - sessionUser.userfullname = user.fullname; - }).catch(() => { - // Error getting profile, most probably the user is deleted. - sessionUser.userfullname = this.translate.instant('core.deleteduser') + ' ' + sessionUser.userid; - })); - } - }); - - // If session has more than 4 users we display a "Show more" link. - session.allsessionusers = session.sessionusers; - if (session.sessionusers.length > 4) { - session.sessionusers = session.allsessionusers.slice(0, 3); - } - }); - - return Promise.all(promises).then(() => { - this.sessions = sessions; - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); - }).finally(() => { - this.loaded = true; - modal && modal.dismiss(); - }); - } - - /** - * Refresh chat sessions. - * - * @param refresher Refresher. - */ - refreshSessions(refresher: any): void { - const promises = [ - this.groupsProvider.invalidateActivityGroupInfo(this.cmId), - this.chatProvider.invalidateSessions(this.chatId, this.groupId, this.showAll) - ]; - - this.utils.allPromises(promises).finally(() => { - this.fetchSessions().finally(() => { - refresher.complete(); - }); - }); - } - - /** - * Navigate to a session. - * - * @param session Chat session. - */ - openSession(session: any): void { - this.selectedSessionStart = session.sessionstart; - this.selectedSessionGroupId = this.groupId; - const params = { - courseId: this.courseId, - chatId: this.chatId, - groupId: this.groupId, - sessionStart: session.sessionstart, - sessionEnd: session.sessionend, - cmId: this.cmId - }; - this.splitviewCtrl.push('AddonModChatSessionMessagesPage', params); - } - - /** - * Show more session users. - * - * @param session Chat session. - * @param $event The event. - */ - showMoreUsers(session: any, $event: Event): void { - session.sessionusers = session.allsessionusers; - $event.stopPropagation(); - } -} - -/** - * Fields added to chat session in this view. - */ -type AddonModChatSessionFormatted = AddonModChatSession & { - duration?: number; // Session duration. - allsessionusers?: AddonModChatUserSessionFormatted[]; // All session users. -}; - -/** - * Fields added to user session in this view. - */ -type AddonModChatUserSessionFormatted = AddonModChatSessionUser & { - userfullname?: string; // User full name. -}; diff --git a/src/addon/mod/chat/pages/users/users.html b/src/addon/mod/chat/pages/users/users.html deleted file mode 100644 index 3e5f9dfc6..000000000 --- a/src/addon/mod/chat/pages/users/users.html +++ /dev/null @@ -1,28 +0,0 @@ - - - {{ 'addon.mod_chat.currentusers' | translate }} - - - - - - - - - -

{{ user.fullname }}

- - - - -
-
-
diff --git a/src/addon/mod/chat/pages/users/users.module.ts b/src/addon/mod/chat/pages/users/users.module.ts deleted file mode 100644 index 4400f39eb..000000000 --- a/src/addon/mod/chat/pages/users/users.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModChatUsersPage } from './users'; - -@NgModule({ - declarations: [ - AddonModChatUsersPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonModChatUsersPage), - TranslateModule.forChild() - ], -}) -export class AddonModChatUsersPageModule {} diff --git a/src/addon/mod/chat/pages/users/users.scss b/src/addon/mod/chat/pages/users/users.scss deleted file mode 100644 index 43dc18a2c..000000000 --- a/src/addon/mod/chat/pages/users/users.scss +++ /dev/null @@ -1,5 +0,0 @@ -ion-app.app-root page-addon-mod-chat-users { - .addon-mod-chat-user ion-label { - margin-bottom: 0; - } -} diff --git a/src/addon/mod/chat/pages/users/users.ts b/src/addon/mod/chat/pages/users/users.ts deleted file mode 100644 index 6879416e9..000000000 --- a/src/addon/mod/chat/pages/users/users.ts +++ /dev/null @@ -1,100 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, NgZone } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; -import { CoreAppProvider } from '@providers/app'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModChatProvider, AddonModChatUser } from '../../providers/chat'; -import { Network } from '@ionic-native/network'; - -/** - * Page that displays the chat session users. - */ -@IonicPage({ segment: 'addon-mod-chat-users' }) -@Component({ - selector: 'page-addon-mod-chat-users', - templateUrl: 'users.html', -}) -export class AddonModChatUsersPage { - - users: AddonModChatUser[] = []; - usersLoaded = false; - currentUserId: number; - isOnline: boolean; - - protected sessionId: string; - protected cmId: number; - protected onlineObserver: any; - - constructor(navParams: NavParams, network: Network, zone: NgZone, private appProvider: CoreAppProvider, - private sitesProvider: CoreSitesProvider, private viewCtrl: ViewController, - private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider) { - this.sessionId = navParams.get('sessionId'); - this.isOnline = this.appProvider.isOnline(); - this.currentUserId = this.sitesProvider.getCurrentSiteUserId(); - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.isOnline = this.appProvider.isOnline(); - }); - }); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.chatProvider.getChatUsers(this.sessionId, {cmId: this.cmId}).then((data) => { - this.users = data.users; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilegettingchatusers', true); - }).finally(() => { - this.usersLoaded = true; - }); - } - - /** - * Close the chat users modal. - */ - closeModal(): void { - this.viewCtrl.dismiss({users: this.users}); - } - - /** - * Add "To user:". - * - * @param user User object. - */ - talkTo(user: AddonModChatUser): void { - this.viewCtrl.dismiss({talkTo: user.fullname, users: this.users}); - } - - /** - * Beep a user. - * - * @param user User object. - */ - beepTo(user: AddonModChatUser): void { - this.viewCtrl.dismiss({beepTo: user.id, users: this.users}); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.onlineObserver && this.onlineObserver.unsubscribe(); - } -} diff --git a/src/addon/mod/chat/providers/chat.ts b/src/addon/mod/chat/providers/chat.ts deleted file mode 100644 index 688c051fe..000000000 --- a/src/addon/mod/chat/providers/chat.ts +++ /dev/null @@ -1,562 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; -import { AddonModChatMessageForView, AddonModChatSessionMessageForView } from './helper'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some features for chats. - */ -@Injectable() -export class AddonModChatProvider { - static COMPONENT = 'mmaModChat'; - static POLL_INTERVAL = 4000; - - protected ROOT_CACHE_KEY = 'AddonModChat:'; - - constructor(private sitesProvider: CoreSitesProvider, private userProvider: CoreUserProvider, - private logHelper: CoreCourseLogHelperProvider, protected utils: CoreUtilsProvider, private translate: TranslateService) {} - - /** - * Get a chat. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the chat is retrieved. - */ - getChat(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId] - }; - const preSets = { - cacheKey: this.getChatsCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModChatProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_chat_get_chats_by_courses', params, preSets) - .then((response: AddonModChatGetChatsByCoursesResult): any => { - - if (response.chats) { - const chat = response.chats.find((chat) => chat.coursemodule == cmId); - if (chat) { - return chat; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Log the user into a chat room. - * - * @param chatId Chat instance ID. - * @return Promise resolved when the WS is executed. - */ - loginUser(chatId: number): Promise { - const params = { - chatid: chatId - }; - - return this.sitesProvider.getCurrentSite().write('mod_chat_login_user', params) - .then((response: AddonModChatLoginUserResult): any => { - - if (response.chatsid) { - return response.chatsid; - } - - return Promise.reject(null); - }); - } - - /** - * Report a chat as being viewed. - * - * @param id Chat instance ID. - * @param name Name of the chat. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - chatid: id - }; - - return this.logHelper.logSingle('mod_chat_view_chat', params, AddonModChatProvider.COMPONENT, id, name, 'chat', {}, siteId); - } - - /** - * Send a message to a chat. - * - * @param sessionId Chat sessiond ID. - * @param message Message text. - * @param beepUserId Beep user ID. - * @return Promise resolved when the WS is executed. - */ - sendMessage(sessionId: string, message: string, beepUserId: number): Promise { - const params = { - chatsid: sessionId, - messagetext: message, - beepid: beepUserId - }; - - return this.sitesProvider.getCurrentSite().write('mod_chat_send_chat_message', params) - .then((response: AddonModChatSendChatMessageResult): any => { - - if (response.messageid) { - return response.messageid; - } - - return Promise.reject(null); - }); - } - - /** - * Get the latest messages from a chat session. - * - * @param sessionId Chat sessiond ID. - * @param lastTime Last time when messages were retrieved. - * @return Promise resolved when the WS is executed. - */ - getLatestMessages(sessionId: string, lastTime: number): Promise { - const params = { - chatsid: sessionId, - chatlasttime: lastTime - }; - - /* We use write to not use cache. It doesn't make sense to store the messages in cache - because we won't be able to retireve them if AddonModChatProvider.loginUser fails. */ - return this.sitesProvider.getCurrentSite().write('mod_chat_get_chat_latest_messages', params); - } - - /** - * Get user data for messages since they only have userid. - * - * @param messages Messages to get the user data for. - * @param courseId ID of the course the messages belong to. - * @return Promise always resolved with the formatted messages. - */ - getMessagesUserData(messages: (AddonModChatMessage | AddonModChatSessionMessage)[], courseId: number) - : Promise<(AddonModChatMessageForView | AddonModChatSessionMessageForView)[]> { - - const promises = messages.map((message: AddonModChatMessageForView | AddonModChatSessionMessageForView) => { - return this.userProvider.getProfile(message.userid, courseId, true).then((user) => { - message.userfullname = user.fullname; - message.userprofileimageurl = user.profileimageurl; - }).catch(() => { - // Error getting profile, most probably the user is deleted. - message.userfullname = this.translate.instant('core.deleteduser') + ' ' + message.userid; - }); - }); - - return Promise.all(promises).then(() => { - return messages; - }); - } - - /** - * Get the actives users of a current chat. - * - * @param sessionId Chat sessiond ID. - * @param options Other options. - * @return Promise resolved when the WS is executed. - */ - getChatUsers(sessionId: string, options: CoreCourseCommonModWSOptions = {}): Promise { - // By default, always try to get the latest data. - options.readingStrategy = options.readingStrategy || CoreSitesReadingStrategy.PreferNetwork; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - chatsid: sessionId, - }; - const preSets = { - component: AddonModChatProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_chat_get_chat_users', params, preSets); - }); - } - - /** - * Return whether WS for passed sessions are available. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with a boolean. - */ - areSessionsAvailable(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('mod_chat_get_sessions') && site.wsAvailable('mod_chat_get_session_messages'); - }); - } - - /** - * Get chat sessions. - * - * @param chatId Chat ID. - * @param groupId Group ID, 0 means that the function will determine the user group. - * @param showAll Whether to include incomplete sessions or not. - * @param options Other options. - * @return Promise resolved with the list of sessions. - * @since 3.5 - */ - getSessions(chatId: number, groupId: number = 0, showAll: boolean = false, options: CoreCourseCommonModWSOptions = {}): - Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - chatid: chatId, - groupid: groupId, - showall: showAll ? 1 : 0, - }; - const preSets = { - cacheKey: this.getSessionsCacheKey(chatId, groupId, showAll), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModChatProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_chat_get_sessions', params, preSets).then((response: AddonModChatGetSessionsResult): any => { - if (!response || !response.sessions) { - return Promise.reject(null); - } - - return response.sessions; - }); - }); - } - - /** - * Get chat session messages. - * - * @param chatId Chat ID. - * @param sessionStart Session start time. - * @param sessionEnd Session end time. - * @param groupId Group ID, 0 means that the function will determine the user group. - * @param options Other options. - * @return Promise resolved with the list of messages. - * @since 3.5 - */ - getSessionMessages(chatId: number, sessionStart: number, sessionEnd: number, groupId: number = 0, - options: CoreCourseCommonModWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - chatid: chatId, - sessionstart: sessionStart, - sessionend: sessionEnd, - groupid: groupId, - }; - const preSets = { - cacheKey: this.getSessionMessagesCacheKey(chatId, sessionStart, groupId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModChatProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_chat_get_session_messages', params, preSets) - .then((response: AddonModChatGetSessionMessagesResult): any => { - - if (!response || !response.messages) { - return Promise.reject(null); - } - - return response.messages; - }); - }); - } - - /** - * Invalidate chats. - * - * @param courseId Course ID. - * @return Promise resolved when the data is invalidated. - */ - invalidateChats(courseId: number): Promise { - const site = this.sitesProvider.getCurrentSite(); - - return site.invalidateWsCacheForKey(this.getChatsCacheKey(courseId)); - } - - /** - * Invalidate chat sessions. - * - * @param chatId Chat ID. - * @param groupId Group ID, 0 means that the function will determine the user group. - * @param showAll Whether to include incomplete sessions or not. - * @return Promise resolved when the data is invalidated. - */ - invalidateSessions(chatId: number, groupId: number = 0, showAll: boolean = false): Promise { - const site = this.sitesProvider.getCurrentSite(); - - return site.invalidateWsCacheForKey(this.getSessionsCacheKey(chatId, groupId, showAll)); - } - - /** - * Invalidate all chat sessions. - * - * @param chatId Chat ID. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllSessions(chatId: number): Promise { - const site = this.sitesProvider.getCurrentSite(); - - return site.invalidateWsCacheForKeyStartingWith(this.getSessionsCacheKeyPrefix(chatId)); - } - - /** - * Invalidate chat session messages. - * - * @param chatId Chat ID. - * @param sessionStart Session start time. - * @param groupId Group ID, 0 means that the function will determine the user group. - * @return Promise resolved when the data is invalidated. - */ - invalidateSessionMessages(chatId: number, sessionStart: number, groupId: number = 0): Promise { - const site = this.sitesProvider.getCurrentSite(); - - return site.invalidateWsCacheForKey(this.getSessionMessagesCacheKey(chatId, sessionStart, groupId)); - } - - /** - * Invalidate all chat session messages. - * - * @param chatId Chat ID. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllSessionMessages(chatId: number): Promise { - const site = this.sitesProvider.getCurrentSite(); - - return site.invalidateWsCacheForKeyStartingWith(this.getSessionMessagesCacheKeyPrefix(chatId)); - } - - /** - * Get cache key for chats WS call. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getChatsCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'chats:' + courseId; - } - - /** - * Get cache key for sessions WS call. - * - * @param chatId Chat ID. - * @param groupId Goup ID, 0 means that the function will determine the user group. - * @param showAll Whether to include incomplete sessions or not. - * @return Cache key. - */ - protected getSessionsCacheKey(chatId: number, groupId: number, showAll: boolean): string { - return this.getSessionsCacheKeyPrefix(chatId) + groupId + ':' + (showAll ? 1 : 0); - } - - /** - * Get cache key prefix for sessions WS call. - * - * @param chatId Chat ID. - * @return Cache key prefix. - */ - protected getSessionsCacheKeyPrefix(chatId: number): string { - return this.ROOT_CACHE_KEY + 'sessions:' + chatId + ':'; - } - - /** - * Get cache key for session messages WS call. - * - * @param chatId Chat ID. - * @param sessionStart Session start time. - * @param groupId Group ID, 0 means that the function will determine the user group. - * @return Cache key. - */ - protected getSessionMessagesCacheKey(chatId: number, sessionStart: number, groupId: number): string { - return this.getSessionMessagesCacheKeyPrefix(chatId) + sessionStart + ':' + groupId; - } - - /** - * Get cache key prefix for session messages WS call. - * - * @param chatId Chat ID. - * @return Cache key prefix. - */ - protected getSessionMessagesCacheKeyPrefix(chatId: number): string { - return this.ROOT_CACHE_KEY + 'sessionsMessages:' + chatId + ':'; - } -} - -/** - * Chat returned by mod_chat_get_chats_by_courses. - */ -export type AddonModChatChat = { - id: number; // Chat id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Chat name. - intro: string; // The Chat intro. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles?: CoreWSExternalFile[]; // @since 3.2. - chatmethod?: string; // Chat method (sockets, ajax, header_js). - keepdays?: number; // Keep days. - studentlogs?: number; // Student logs visible to everyone. - chattime?: number; // Chat time. - schedule?: number; // Schedule type. - timemodified?: number; // Time of last modification. - section?: number; // Course section id. - visible?: boolean; // Visible. - groupmode?: number; // Group mode. - groupingid?: number; // Group id. -}; - -/** - * Chat user returned by mod_chat_get_chat_users. - */ -export type AddonModChatUser = { - id: number; // User id. - fullname: string; // User full name. - profileimageurl: string; // User picture URL. -}; - -/** - * Meessage returned by mod_chat_get_chat_latest_messages. - */ -export type AddonModChatMessage = { - id: number; // Message id. - userid: number; // User id. - system: boolean; // True if is a system message (like user joined). - message: string; // Message text. - timestamp: number; // Timestamp for the message. -}; - -/** - * Message with user data. - */ -export type AddonModChatMessageWithUserData = AddonModChatMessage & AddonModChatMessageUserData; - -/** - * Chat session. - */ -export type AddonModChatSession = { - sessionstart: number; // Session start time. - sessionend: number; // Session end time. - sessionusers: AddonModChatSessionUser[]; // Session users. - iscomplete: boolean; // Whether the session is completed or not. -}; - -/** - * Chat user returned by mod_chat_get_sessions. - */ -export type AddonModChatSessionUser = { - userid: number; // User id. - messagecount: number; // Number of messages in the session. -}; - -/** - * Message returned by mod_chat_get_session_messages. - */ -export type AddonModChatSessionMessage = { - id: number; // The message record id. - chatid: number; // The chat id. - userid: number; // The user who wrote the message. - groupid: number; // The group this message belongs to. - issystem: boolean; // Whether is a system message or not. - message: string; // The message text. - timestamp: number; // The message timestamp (indicates when the message was sent). -}; - -/** - * Session message with user data. - */ -export type AddonModChatSessionMessageWithUserData = AddonModChatSessionMessage & AddonModChatMessageUserData; - -/** - * Result of WS mod_chat_get_chats_by_courses. - */ -export type AddonModChatGetChatsByCoursesResult = { - chats: AddonModChatChat[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_chat_get_chat_users. - */ -export type AddonModChatGetChatUsersResult = { - users: AddonModChatUser[]; // List of users. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_chat_get_sessions. - */ -export type AddonModChatGetSessionsResult = { - sessions: AddonModChatSession[]; // List of sessions. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_chat_get_session_messages. - */ -export type AddonModChatGetSessionMessagesResult = { - messages: AddonModChatSessionMessage[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_chat_send_chat_message. - */ -export type AddonModChatSendChatMessageResult = { - messageid: number; // Message sent id. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_chat_get_chat_latest_messages. - */ -export type AddonModChatGetChatLatestMessagesResult = { - messages: AddonModChatMessage[]; // List of messages. - chatnewlasttime: number; // New last time. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_chat_login_user. - */ -export type AddonModChatLoginUserResult = { - chatsid: string; // Unique chat session id. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * User data added to messages. - */ -type AddonModChatMessageUserData = { - userfullname?: string; // Calculated in the app. Full name of the user who wrote the message. - userprofileimageurl?: string; // Calculated in the app. Full name of the user who wrote the message. -}; diff --git a/src/addon/mod/chat/providers/helper.ts b/src/addon/mod/chat/providers/helper.ts deleted file mode 100644 index a7b0ecd07..000000000 --- a/src/addon/mod/chat/providers/helper.ts +++ /dev/null @@ -1,132 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import * as moment from 'moment'; -import { AddonModChatMessageWithUserData, AddonModChatSessionMessageWithUserData } from './chat'; - -/** - * Helper service that provides some features for chat. - */ -@Injectable() -export class AddonModChatHelperProvider { - - static patternto = new RegExp(/^To\s([^:]+):(.*)/); - - constructor(protected translate: TranslateService, - protected textUtils: CoreTextUtilsProvider) { - - } - - /** - * Give some format info about messages. - * - * @param currentUserId User Id. - * @param message Message in a discussion. - * @param prevMessage Previous Message in a discussion (if any). - * @return Message with additional info. - */ - formatMessage(currentUserId: number, message: AddonModChatMessageForView | AddonModChatSessionMessageForView, - prevMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): any { - message.message = message.message.trim(); - - message.showDate = this.showDate(message, prevMessage); - message.beep = message.message.substr(0, 5) == 'beep ' && message.message.substr(5).trim(); - - message.special = ( message).issystem || ( message).system || - !!message.beep; - - if (message.message.substr(0, 4) == '/me ') { - message.special = true; - message.message = message.message.substr(4).trim(); - } - - if (!message.special && message.message.match(AddonModChatHelperProvider.patternto)) { - const matches = message.message.match(AddonModChatHelperProvider.patternto); - message.message = '' + this.translate.instant('addon.mod_chat.saidto') + - ' ' + matches[1] + ': ' + matches[2]; - } - - message.showUserData = this.showUserData(currentUserId, message, prevMessage); - prevMessage ? - prevMessage.showTail = this.showTail(prevMessage, message) : null; - } - - /** - * Check if the user info should be displayed for the current message. - * User data is only displayed if the previous message was from another user. - * - * @param message Current message where to show the user info. - * @param prevMessage Previous message. - * @return Whether user data should be shown. - */ - protected showUserData(currentUserId: number, message: AddonModChatMessageForView | AddonModChatSessionMessageForView, - prevMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): boolean { - return message.userid != currentUserId && - (!prevMessage || prevMessage.userid != message.userid || message.showDate || prevMessage.special); - } - - /** - * Check if a css tail should be shown. - * - * @param message Current message where to show the user info. - * @param nextMessage Next message. - * @return Whether user data should be shown. - */ - protected showTail(message: AddonModChatMessageForView | AddonModChatSessionMessageForView, - nextMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): boolean { - return !nextMessage || nextMessage.userid != message.userid || nextMessage.showDate || nextMessage.special; - } - - /** - * Check if the date should be displayed between messages (when the day changes at midnight for example). - * - * @param message New message object. - * @param prevMessage Previous message object. - * @return True if messages are from diferent days, false othetwise. - */ - protected showDate(message: AddonModChatMessageForView | AddonModChatSessionMessageForView, - prevMessage: AddonModChatMessageForView | AddonModChatSessionMessageForView): boolean { - if (!prevMessage) { - return true; - } - - // Check if day has changed. - return !moment(message.timestamp * 1000).isSame(prevMessage.timestamp * 1000, 'day'); - } -} - -/** - * Special info for view usage. - */ -type AddonModChatInfoForView = { - showDate?: boolean; // If date should be displayed before the message. - beep?: string; // User id of the beeped user or 'all'. - special?: boolean; // True if is an special message (system, beep or command). - showUserData?: boolean; // If user data should be displayed. - showTail?: boolean; // If tail should be displayed (decoration). - beepWho?: string; // Fullname of the beeped user. -}; - -/** - * Message with data for view usage. - */ -export type AddonModChatMessageForView = AddonModChatMessageWithUserData & AddonModChatInfoForView; - -/** - * Session message with data for view usage. - */ -export type AddonModChatSessionMessageForView = AddonModChatSessionMessageWithUserData & AddonModChatInfoForView; diff --git a/src/addon/mod/chat/providers/link-handler.ts b/src/addon/mod/chat/providers/link-handler.ts deleted file mode 100644 index a2fb3035f..000000000 --- a/src/addon/mod/chat/providers/link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to chat. - */ -@Injectable() -export class AddonModChatLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModChatLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModChat', 'chat', 'c'); - } -} diff --git a/src/addon/mod/chat/providers/list-link-handler.ts b/src/addon/mod/chat/providers/list-link-handler.ts deleted file mode 100644 index f56210b84..000000000 --- a/src/addon/mod/chat/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to chat list page. - */ -@Injectable() -export class AddonModChatListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModChatListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModChat', 'chat'); - } -} diff --git a/src/addon/mod/chat/providers/module-handler.ts b/src/addon/mod/chat/providers/module-handler.ts deleted file mode 100644 index 1befb4aff..000000000 --- a/src/addon/mod/chat/providers/module-handler.ts +++ /dev/null @@ -1,93 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModChatIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; -import { AddonModChatProvider } from './chat'; - -/** - * Handler to support chat modules. - */ -@Injectable() -export class AddonModChatModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModChat'; - modName = 'chat'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider, private chatProvider: AddonModChatProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - const data: CoreCourseModuleHandlerData = { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_chat-handler', - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModChatIndexPage', pageParams, options); - } - }; - - this.chatProvider.areSessionsAvailable().then((available) => { - data.showDownloadButton = available; - }); - - return data; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModChatIndexComponent; - } -} diff --git a/src/addon/mod/chat/providers/prefetch-handler.ts b/src/addon/mod/chat/providers/prefetch-handler.ts deleted file mode 100644 index 62821a577..000000000 --- a/src/addon/mod/chat/providers/prefetch-handler.ts +++ /dev/null @@ -1,198 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModChatProvider, AddonModChatChat } from './chat'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch chats. - */ -@Injectable() -export class AddonModChatPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModChat'; - modName = 'chat'; - component = AddonModChatProvider.COMPONENT; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - private groupsProvider: CoreGroupsProvider, - private userProvider: CoreUserProvider, - private chatProvider: AddonModChatProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.chatProvider.areSessionsAvailable(); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.chatProvider.getChat(courseId, moduleId).then((chat) => { - const promises = [ - this.chatProvider.invalidateAllSessions(chat.id), - this.chatProvider.invalidateAllSessionMessages(chat.id) - ]; - - return this.utils.allPromises(promises); - }); - } - - /** - * Invalidate WS calls needed to determine module status (usually, to check if module is downloadable). - * It doesn't need to invalidate check updates. It should NOT invalidate files nor all the prefetched data. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const promises = [ - this.chatProvider.invalidateChats(courseId), - this.courseProvider.invalidateModule(module.id) - ]; - - return this.utils.allPromises(promises); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchChat.bind(this)); - } - - /** - * Prefetch a chat. - * - * @param module The module object returned by WS. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchChat(module: any, courseId: number, single: boolean, siteId: string): Promise { - // Prefetch chat and group info. - const promises: Promise[] = [ - this.chatProvider.getChat(courseId, module.id, {readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, siteId}), - this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, siteId) - ]; - const options = { - cmId: module.id, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - return Promise.all(promises).then(([chat, groupInfo]: [AddonModChatChat, CoreGroupInfo]) => { - const promises = []; - - let groupIds = [0]; - if (groupInfo.groups && groupInfo.groups.length > 0) { - groupIds = groupInfo.groups.map((group) => group.id); - } - - groupIds.forEach((groupId) => { - // Prefetch complete sessions. - promises.push(this.chatProvider.getSessions(chat.id, groupId, false, options).catch((error) => { - // Ignore group error. - if (error.errorcode != 'notingroup') { - return Promise.reject(error); - } - })); - - // Prefetch all sessions. - promises.push(this.chatProvider.getSessions(chat.id, groupId, true, options).then((sessions) => { - const promises = sessions.map((session) => this.prefetchSession(chat.id, session, 0, courseId, module.id, - siteId)); - - return Promise.all(promises); - }).catch((error) => { - // Ignore group error. - if (error.errorcode != 'notingroup') { - return Promise.reject(error); - } - })); - }); - - return Promise.all(promises); - }); - } - - /** - * Prefetch chat session messages and user profiles. - * - * @param chatId Chat ID. - * @param session Session object. - * @param groupId Group ID. - * @param courseId Course ID the module belongs to. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchSession(chatId: number, session: any, groupId: number, courseId: number, cmId: number, siteId: string) - : Promise { - return this.chatProvider.getSessionMessages(chatId, session.sessionstart, session.sessionend, groupId, { - cmId, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((messages) => { - const users = {}; - session.sessionusers.forEach((user) => { - users[user.userid] = true; - }); - messages.forEach((message) => { - users[message.userid] = true; - }); - const userIds = Object.keys(users).map(Number); - - return this.userProvider.prefetchProfiles(userIds, courseId, siteId); - }); - } -} diff --git a/src/addon/mod/choice/choice.module.ts b/src/addon/mod/choice/choice.module.ts deleted file mode 100644 index 5e00daa85..000000000 --- a/src/addon/mod/choice/choice.module.ts +++ /dev/null @@ -1,67 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { AddonModChoiceComponentsModule } from './components/components.module'; -import { AddonModChoiceModuleHandler } from './providers/module-handler'; -import { AddonModChoiceProvider } from './providers/choice'; -import { AddonModChoiceLinkHandler } from './providers/link-handler'; -import { AddonModChoiceListLinkHandler } from './providers/list-link-handler'; -import { AddonModChoicePrefetchHandler } from './providers/prefetch-handler'; -import { AddonModChoiceSyncProvider } from './providers/sync'; -import { AddonModChoiceSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModChoiceOfflineProvider } from './providers/offline'; - -// List of providers (without handlers). -export const ADDON_MOD_CHOICE_PROVIDERS: any[] = [ - AddonModChoiceProvider, - AddonModChoiceSyncProvider, - AddonModChoiceOfflineProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModChoiceComponentsModule - ], - providers: [ - AddonModChoiceProvider, - AddonModChoiceSyncProvider, - AddonModChoiceOfflineProvider, - AddonModChoiceModuleHandler, - AddonModChoicePrefetchHandler, - AddonModChoiceLinkHandler, - AddonModChoiceListLinkHandler, - AddonModChoiceSyncCronHandler - ] -}) -export class AddonModChoiceModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModChoiceModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModChoicePrefetchHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModChoiceLinkHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModChoiceSyncCronHandler, - listLinkHandler: AddonModChoiceListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - cronDelegate.register(syncHandler); - } -} diff --git a/src/addon/mod/choice/components/components.module.ts b/src/addon/mod/choice/components/components.module.ts deleted file mode 100644 index 866f3587f..000000000 --- a/src/addon/mod/choice/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModChoiceIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModChoiceIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModChoiceIndexComponent - ], - entryComponents: [ - AddonModChoiceIndexComponent - ] -}) -export class AddonModChoiceComponentsModule {} diff --git a/src/addon/mod/choice/components/index/addon-mod-choice-index.html b/src/addon/mod/choice/components/index/addon-mod-choice-index.html deleted file mode 100644 index bdadfd1f2..000000000 --- a/src/addon/mod/choice/components/index/addon-mod-choice-index.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -

{{ 'addon.mod_choice.previewonly' | translate:{$a: openTimeReadable} }}

-

{{ 'addon.mod_choice.notopenyet' | translate:{$a: openTimeReadable} }}

-
- - -

{{ 'addon.mod_choice.yourselection' | translate }}

-

{{ 'addon.mod_choice.expired' | translate:{$a: closeTimeReadable} }}

-
- - - - {{ 'core.hasdatatosync' | translate:{$a: moduleName} }} - - - -
- - {{ publishInfo | translate }} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - {{ 'addon.mod_choice.responses' | translate }} - - - - - - {{ 'addon.mod_choice.resultsnotsynced' | translate }} - - - - - - - - -

-

{{ 'addon.mod_choice.numberofuser' | translate }}: {{ result.numberofuser }} ({{ 'core.percentagenumber' | translate: {$a: result.percentageamountfixed} }})

-

{{ 'addon.mod_choice.limita' | translate:{$a: result.maxanswer} }}

-
- - -

{{user.fullname}}

-
-
-
-
-
-
- -

{{ 'addon.mod_choice.noresultsviewable' | translate }}

-
-
- - - -

- {{ 'addon.mod_choice.full' | translate }} -

- -

{{ 'addon.mod_choice.responsesa' | translate:{$a: option.countanswers} }}

-

{{ 'addon.mod_choice.limita' | translate:{$a: option.maxanswers} }}

-
-
diff --git a/src/addon/mod/choice/components/index/index.ts b/src/addon/mod/choice/components/index/index.ts deleted file mode 100644 index 89dbcc0dc..000000000 --- a/src/addon/mod/choice/components/index/index.ts +++ /dev/null @@ -1,449 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector } from '@angular/core'; -import { Content } from 'ionic-angular'; -import { CoreEvents, CoreEventsProvider } from '@providers/events'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { AddonModChoiceProvider, AddonModChoiceChoice, AddonModChoiceOption, AddonModChoiceResult } from '../../providers/choice'; -import { AddonModChoiceOfflineProvider } from '../../providers/offline'; -import { AddonModChoiceSyncProvider } from '../../providers/sync'; - -/** - * Component that displays a choice. - */ -@Component({ - selector: 'addon-mod-choice-index', - templateUrl: 'addon-mod-choice-index.html', -}) -export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityComponent { - component = AddonModChoiceProvider.COMPONENT; - moduleName = 'choice'; - - choice: AddonModChoiceChoice; - options: AddonModChoiceOption[] = []; - selectedOption: {id: number}; - choiceNotOpenYet = false; - choiceClosed = false; - canEdit = false; - canDelete = false; - canSeeResults = false; - data = []; - labels = []; - results = []; - publishInfo: string; // Message explaining the user what will happen with his choices. - openTimeReadable: string; - closeTimeReadable: string; - - protected userId: number; - protected syncEventName = AddonModChoiceSyncProvider.AUTO_SYNCED; - protected hasAnsweredOnline = false; - protected now: number; - - constructor( - injector: Injector, - protected choiceProvider: AddonModChoiceProvider, - @Optional() content: Content, - protected choiceOffline: AddonModChoiceOfflineProvider, - protected choiceSync: AddonModChoiceSyncProvider, - protected timeUtils: CoreTimeUtilsProvider, - ) { - super(injector, content); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.userId = this.sitesProvider.getCurrentSiteUserId(); - - this.loadContent(false, true).then(() => { - if (!this.choice) { - return; - } - this.choiceProvider.logView(this.choice.id, this.choice.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch((error) => { - // Ignore errors. - }); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.choiceProvider.invalidateChoiceData(this.courseId)); - - if (this.choice) { - promises.push(this.choiceProvider.invalidateOptions(this.choice.id)); - promises.push(this.choiceProvider.invalidateResults(this.choice.id)); - } - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (this.choice && syncEventData.choiceId == this.choice.id && syncEventData.userId == this.userId) { - this.domUtils.scrollToTop(this.content); - - return true; - } - - return false; - } - - /** - * Download choice contents. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - this.now = new Date().getTime(); - - return this.choiceProvider.getChoice(this.courseId, this.module.id).then((choice) => { - this.choice = choice; - this.choice.timeopen = choice.timeopen * 1000; - this.choice.timeclose = choice.timeclose * 1000; - this.openTimeReadable = this.timeUtils.userDate(choice.timeopen); - this.closeTimeReadable = this.timeUtils.userDate(choice.timeclose); - - this.description = choice.intro; - this.choiceNotOpenYet = choice.timeopen && choice.timeopen > this.now; - this.choiceClosed = choice.timeclose && choice.timeclose <= this.now; - - this.dataRetrieved.emit(choice); - - if (sync) { - // Try to synchronize the choice. - return this.syncActivity(showErrors).then((updated) => { - if (updated) { - // Responses were sent, update the choice. - return this.choiceProvider.getChoice(this.courseId, this.module.id).then((choice) => { - this.choice = choice; - }); - } - }); - } - }).then(() => { - // Check if there are responses stored in offline. - return this.choiceOffline.hasResponse(this.choice.id); - }).then((hasOffline) => { - this.hasOffline = hasOffline; - - // We need fetchOptions to finish before calling fetchResults because it needs hasAnsweredOnline variable. - return this.fetchOptions(hasOffline).then(() => { - return this.fetchResults(); - }); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Convenience function to get choice options. - * - * @param hasOffline True if there are responses stored offline. - * @return Promise resolved when done. - */ - protected fetchOptions(hasOffline: boolean): Promise { - return this.choiceProvider.getOptions(this.choice.id, {cmId: this.module.id}).then((options) => { - let promise; - - // Check if the user has answered (synced) to allow show results. - this.hasAnsweredOnline = options.some((option) => option.checked); - - if (hasOffline) { - promise = this.choiceOffline.getResponse(this.choice.id).then((response) => { - const optionsKeys: {[id: number]: AddonModChoiceOption} = {}; - options.forEach((option) => { - optionsKeys[option.id] = option; - }); - // Update options with the offline data. - if (response.deleting) { - // Uncheck selected options. - if (response.responses.length > 0) { - // Uncheck all options selected in responses. - response.responses.forEach((selected) => { - if (optionsKeys[selected] && optionsKeys[selected].checked) { - optionsKeys[selected].checked = false; - optionsKeys[selected].countanswers--; - } - }); - } else { - // On empty responses, uncheck all selected. - Object.keys(optionsKeys).forEach((key) => { - if (optionsKeys[key].checked) { - optionsKeys[key].checked = false; - optionsKeys[key].countanswers--; - } - }); - } - } else { - // Uncheck all options to check again the offlines'. - Object.keys(optionsKeys).forEach((key) => { - if (optionsKeys[key].checked) { - optionsKeys[key].checked = false; - optionsKeys[key].countanswers--; - } - }); - // Then check selected ones. - response.responses.forEach((selected) => { - if (optionsKeys[selected]) { - optionsKeys[selected].checked = true; - optionsKeys[selected].countanswers++; - } - }); - } - - // Convert it again to array. - return Object.keys(optionsKeys).map((key) => optionsKeys[key]); - }); - } else { - promise = Promise.resolve(options); - } - - promise.then((options: AddonModChoiceOption[]) => { - const isOpen = this.isChoiceOpen(); - - let hasAnswered = false; - this.selectedOption = {id: -1}; // Single choice model. - options.forEach((option) => { - if (option.checked) { - hasAnswered = true; - if (!this.choice.allowmultiple) { - this.selectedOption.id = option.id; - } - } - }); - - this.canEdit = isOpen && (this.choice.allowupdate || !hasAnswered); - this.canDelete = isOpen && this.choice.allowupdate && hasAnswered; - this.options = options; - - if (this.canEdit) { - - // Calculate the publish info message. - switch (this.choice.showresults) { - case AddonModChoiceProvider.RESULTS_NOT: - this.publishInfo = 'addon.mod_choice.publishinfonever'; - break; - - case AddonModChoiceProvider.RESULTS_AFTER_ANSWER: - if (this.choice.publish == AddonModChoiceProvider.PUBLISH_ANONYMOUS) { - this.publishInfo = 'addon.mod_choice.publishinfoanonafter'; - } else { - this.publishInfo = 'addon.mod_choice.publishinfofullafter'; - } - break; - - case AddonModChoiceProvider.RESULTS_AFTER_CLOSE: - if (this.choice.publish == AddonModChoiceProvider.PUBLISH_ANONYMOUS) { - this.publishInfo = 'addon.mod_choice.publishinfoanonclose'; - } else { - this.publishInfo = 'addon.mod_choice.publishinfofullclose'; - } - break; - - default: - // No need to inform the user since it's obvious that the results are being published. - this.publishInfo = ''; - } - } - }); - }); - } - - /** - * Convenience function to get choice results. - * - * @return Resolved when done. - */ - protected fetchResults(): Promise { - if (this.choiceNotOpenYet) { - // Cannot see results yet. - this.canSeeResults = false; - - return Promise.resolve(); - } - - return this.choiceProvider.getResults(this.choice.id, {cmId: this.module.id}).then((results) => { - let hasVotes = false; - this.data = []; - this.labels = []; - results.forEach((result: AddonModChoiceResultFormatted) => { - if (result.numberofuser > 0) { - hasVotes = true; - } - result.percentageamountfixed = result.percentageamount.toFixed(1); - this.data.push(result.numberofuser); - this.labels.push(result.text); - }); - this.canSeeResults = hasVotes || this.choiceProvider.canStudentSeeResults(this.choice, this.hasAnsweredOnline); - this.results = results; - }); - } - - /** - * Check if a choice is open. - * - * @return True if choice is open, false otherwise. - */ - protected isChoiceOpen(): boolean { - return (this.choice.timeopen === 0 || this.choice.timeopen <= this.now) && - (this.choice.timeclose === 0 || this.choice.timeclose > this.now); - } - - /** - * Return true if the user has selected at least one option. - * - * @return True if the user has responded. - */ - canSave(): boolean { - if (this.choice.allowmultiple) { - return this.options.some((option) => option.checked); - } else { - return this.selectedOption.id !== -1; - } - } - - /** - * Save options selected. - */ - save(): void { - // Only show confirm if choice doesn't allow update. - let promise; - if (this.choice.allowupdate) { - promise = Promise.resolve(); - } else { - promise = this.domUtils.showConfirm(this.translate.instant('core.areyousure')); - } - - promise.then(() => { - const responses = []; - if (this.choice.allowmultiple) { - this.options.forEach((option) => { - if (option.checked) { - responses.push(option.id); - } - }); - } else { - responses.push(this.selectedOption.id); - } - - const modal = this.domUtils.showModalLoading('core.sending', true); - this.choiceProvider.submitResponse(this.choice.id, this.choice.name, this.courseId, responses).then((online) => { - // Success! - // Check completion since it could be configured to complete once the user answers the choice. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - this.domUtils.scrollToTop(this.content); - - if (online) { - CoreEvents.instance.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: this.moduleName }); - } - - return this.dataUpdated(online); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_choice.cannotsubmit', true); - }).finally(() => { - modal.dismiss(); - }); - }); - } - - /** - * Delete options selected. - */ - delete(): void { - this.domUtils.showDeleteConfirm().then(() => { - const modal = this.domUtils.showModalLoading('core.sending', true); - this.choiceProvider.deleteResponses(this.choice.id, this.choice.name, this.courseId).then(() => { - this.domUtils.scrollToTop(this.content); - - // Refresh the data. Don't call dataUpdated because deleting an answer doesn't mark the choice as outdated. - return this.refreshContent(false); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_choice.cannotsubmit', true); - }).finally(() => { - modal.dismiss(); - }); - }).catch(() => { - // Ingore cancelled modal. - }); - } - - /** - * Function to call when some data has changed. It will refresh/prefetch data. - * - * @param online Whether the data was sent to server or stored in offline. - * @return Promise resolved when done. - */ - protected dataUpdated(online: boolean): Promise { - if (online && this.isPrefetched()) { - // The choice is downloaded, update the data. - return this.choiceSync.prefetchAfterUpdate(this.module, this.courseId).then(() => { - // Update the view. - this.showLoadingAndFetch(false, false); - }).catch(() => { - // Prefetch failed, refresh the data. - return this.refreshContent(false); - }); - } else { - // Not downloaded, refresh the data. - return this.refreshContent(false); - } - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.choiceSync.syncChoice(this.choice.id, this.userId); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return Whether it succeed or not. - */ - protected hasSyncSucceed(result: any): boolean { - return result.updated; - } -} - -/** - * Choice result with some calculated data. - */ -export type AddonModChoiceResultFormatted = AddonModChoiceResult & { - percentageamountfixed: string; // Percentage of users answers with fixed decimals. -}; diff --git a/src/addon/mod/choice/lang/en.json b/src/addon/mod/choice/lang/en.json deleted file mode 100644 index 7adee8f2e..000000000 --- a/src/addon/mod/choice/lang/en.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "cannotsubmit": "Sorry, there was a problem submitting your choice. Please try again.", - "choiceoptions": "Choice options", - "errorgetchoice": "Error getting choice data.", - "expired": "This activity closed on {{$a}}.", - "full": "(Full)", - "limita": "Limit: {{$a}}", - "modulenameplural": "Choices", - "noresultsviewable": "The results are not currently viewable.", - "notopenyet": "This activity is not available until {{$a}}.", - "numberofuser": "Number of responses", - "numberofuserinpercentage": "Percentage of responses", - "previewonly": "This is just a preview of the available options for this activity. You will not be able to submit your choice until {{$a}}.", - "publishinfoanonafter": "Anonymous results will be published after you answer.", - "publishinfoanonclose": "Anonymous results will be published after the activity is closed.", - "publishinfofullafter": "Full results, showing everyone's choices, will be published after you answer.", - "publishinfofullclose": "Full results, showing everyone's choices, will be published after the activity is closed.", - "publishinfonever": "The results of this activity will not be published after you answer.", - "removemychoice": "Remove my choice", - "responses": "Responses", - "responsesa": "Responses: {{$a}}", - "responsesresultgraphdescription": "{{number}}% of the users chose the option: {{text}}.", - "responsesresultgraphheader": "Graph display", - "resultsnotsynced": "Your last response must be synchronised before it is included in the results.", - "savemychoice": "Save my choice", - "userchoosethisoption": "Users who chose this option", - "yourselection": "Your selection" -} \ No newline at end of file diff --git a/src/addon/mod/choice/pages/index/index.html b/src/addon/mod/choice/pages/index/index.html deleted file mode 100644 index 3feab085c..000000000 --- a/src/addon/mod/choice/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/choice/pages/index/index.module.ts b/src/addon/mod/choice/pages/index/index.module.ts deleted file mode 100644 index 8dd0f81fb..000000000 --- a/src/addon/mod/choice/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModChoiceComponentsModule } from '../../components/components.module'; -import { AddonModChoiceIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModChoiceIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModChoiceComponentsModule, - IonicPageModule.forChild(AddonModChoiceIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModChoiceIndexPageModule {} diff --git a/src/addon/mod/choice/pages/index/index.ts b/src/addon/mod/choice/pages/index/index.ts deleted file mode 100644 index 38005c282..000000000 --- a/src/addon/mod/choice/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModChoiceIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a choice. - */ -@IonicPage({ segment: 'addon-mod-choice-index' }) -@Component({ - selector: 'page-addon-mod-choice-index', - templateUrl: 'index.html', -}) -export class AddonModChoiceIndexPage { - @ViewChild(AddonModChoiceIndexComponent) choiceComponent: AddonModChoiceIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the choice instance. - * - * @param choice Choice instance. - */ - updateData(choice: any): void { - this.title = choice.name || this.title; - } -} diff --git a/src/addon/mod/choice/providers/choice.ts b/src/addon/mod/choice/providers/choice.ts deleted file mode 100644 index 3e74cfb66..000000000 --- a/src/addon/mod/choice/providers/choice.ts +++ /dev/null @@ -1,543 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModChoiceOfflineProvider } from './offline'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some features for choices. - */ -@Injectable() -export class AddonModChoiceProvider { - static COMPONENT = 'mmaModChoice'; - - static RESULTS_NOT = 0; - static RESULTS_AFTER_ANSWER = 1; - static RESULTS_AFTER_CLOSE = 2; - static RESULTS_ALWAYS = 3; - - static PUBLISH_ANONYMOUS = false; - static PUBLISH_NAMES = true; - - protected ROOT_CACHE_KEY = 'mmaModChoice:'; - - constructor(private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, - private filepoolProvider: CoreFilepoolProvider, private utils: CoreUtilsProvider, - private choiceOffline: AddonModChoiceOfflineProvider, private logHelper: CoreCourseLogHelperProvider) {} - - /** - * Check if results can be seen by a student. The student can see the results if: - * - they're always published, OR - * - they're published after the choice is closed and it's closed, OR - * - they're published after answering and the user has answered. - * - * @param choice Choice to check. - * @param hasAnswered True if user has answered the choice, false otherwise. - * @return True if the students can see the results. - */ - canStudentSeeResults(choice: any, hasAnswered: boolean): boolean { - const now = new Date().getTime(); - - return choice.showresults === AddonModChoiceProvider.RESULTS_ALWAYS || - choice.showresults === AddonModChoiceProvider.RESULTS_AFTER_CLOSE && - choice.timeclose !== 0 && choice.timeclose <= now || - choice.showresults === AddonModChoiceProvider.RESULTS_AFTER_ANSWER && hasAnswered; - } - - /** - * Delete responses from a choice. - * - * @param choiceId Choice ID. - * @param name Choice name. - * @param courseId Course ID the choice belongs to. - * @param responses IDs of the answers. If not defined, delete all the answers of the current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if response was sent to server, false if stored in device. - */ - deleteResponses(choiceId: number, name: string, courseId: number, responses?: number[], siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - responses = responses || []; - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.choiceOffline.saveResponse(choiceId, name, courseId, responses, true, siteId).then(() => { - return false; - }); - }; - - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - // If there's already a response to be sent to the server, discard it first. - return this.choiceOffline.deleteResponse(choiceId, siteId).then(() => { - return this.deleteResponsesOnline(choiceId, responses, siteId).then(() => { - return true; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. - return Promise.reject(error); - } - - // Couldn't connect to server, store in offline. - return storeOffline(); - }); - }); - } - - /** - * Delete responses from a choice. It will fail if offline or cannot connect. - * - * @param choiceId Choice ID. - * @param responses IDs of the answers. If not defined, delete all the answers of the current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when responses are successfully deleted. - */ - deleteResponsesOnline(choiceId: number, responses?: number[], siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - choiceid: choiceId, - responses: responses - }; - - return site.write('mod_choice_delete_choice_responses', params) - .then((response: AddonModChoiceDeleteChoiceResponsesResult) => { - - // Other errors ocurring. - if (!response || response.status === false) { - return Promise.reject(this.utils.createFakeWSError('')); - } - - // Invalidate related data. - const promises = [ - this.invalidateOptions(choiceId, site.id), - this.invalidateResults(choiceId, site.id) - ]; - - return Promise.all(promises).catch(() => { - // Ignore errors. - }); - }); - }); - } - - /** - * Get cache key for choice data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getChoiceDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'choice:' + courseId; - } - - /** - * Get cache key for choice options WS calls. - * - * @param choiceId Choice ID. - * @return Cache key. - */ - protected getChoiceOptionsCacheKey(choiceId: number): string { - return this.ROOT_CACHE_KEY + 'options:' + choiceId; - } - - /** - * Get cache key for choice results WS calls. - * - * @param choiceId Choice ID. - * @return Cache key. - */ - protected getChoiceResultsCacheKey(choiceId: number): string { - return this.ROOT_CACHE_KEY + 'results:' + choiceId; - } - - /** - * Get a choice with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the choice is retrieved. - */ - protected getChoiceByDataKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId] - }; - const preSets = { - cacheKey: this.getChoiceDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModChoiceProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_choice_get_choices_by_courses', params, preSets) - .then((response: AddonModChoiceGetChoicesByCoursesResult): any => { - - if (response && response.choices) { - const currentChoice = response.choices.find((choice) => choice[key] == value); - if (currentChoice) { - return currentChoice; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a choice by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the choice is retrieved. - */ - getChoice(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getChoiceByDataKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a choice by ID. - * - * @param courseId Course ID. - * @param choiceId Choice ID. - * @param options Other options. - * @return Promise resolved when the choice is retrieved. - */ - getChoiceById(courseId: number, choiceId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getChoiceByDataKey(courseId, 'id', choiceId, options); - } - - /** - * Get choice options. - * - * @param choiceId Choice ID. - * @param options Other options. - * @return Promise resolved with choice options. - */ - getOptions(choiceId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - choiceid: choiceId - }; - const preSets = { - cacheKey: this.getChoiceOptionsCacheKey(choiceId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModChoiceProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_choice_get_choice_options', params, preSets) - .then((response: AddonModChoiceGetChoiceOptionsResult): any => { - - if (response.options) { - return response.options; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get choice results. - * - * @param choiceId Choice ID. - * @param options Other options. - * @return Promise resolved with choice results. - */ - getResults(choiceId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - choiceid: choiceId - }; - const preSets = { - cacheKey: this.getChoiceOptionsCacheKey(choiceId), - component: AddonModChoiceProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_choice_get_choice_results', params, preSets) - .then((response: AddonModChoiceGetChoiceResults): any => { - - if (response.options) { - return response.options; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Invalidate choice data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateChoiceData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(null).then((site) => { - return site.invalidateWsCacheForKey(this.getChoiceDataCacheKey(courseId)); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.getChoice(courseId, moduleId).then((choice) => { - return Promise.all([ - this.invalidateChoiceData(courseId), - this.invalidateOptions(choice.id), - this.invalidateResults(choice.id), - ]); - })); - - promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModChoiceProvider.COMPONENT, moduleId)); - - return this.utils.allPromises(promises); - } - - /** - * Invalidate choice options. - * - * @param choiceId Choice ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateOptions(choiceId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getChoiceOptionsCacheKey(choiceId)); - }); - } - - /** - * Invalidate choice results. - * - * @param choiceId Choice ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateResults(choiceId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getChoiceResultsCacheKey(choiceId)); - }); - } - - /** - * Report the choice as being viewed. - * - * @param id Choice ID. - * @param name Name of the choice. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - choiceid: id - }; - - return this.logHelper.logSingle('mod_choice_view_choice', params, AddonModChoiceProvider.COMPONENT, id, name, 'choice', - {}, siteId); - } - - /** - * Send a response to a choice to Moodle. - * - * @param choiceId Choice ID. - * @param name Choice name. - * @param courseId Course ID the choice belongs to. - * @param responses IDs of selected options. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if response was sent to server, false if stored in device. - */ - submitResponse(choiceId: number, name: string, courseId: number, responses: number[], siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.choiceOffline.saveResponse(choiceId, name, courseId, responses, false, siteId).then(() => { - return false; - }); - }; - - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - // If there's already a response to be sent to the server, discard it first. - return this.choiceOffline.deleteResponse(choiceId, siteId).then(() => { - return this.submitResponseOnline(choiceId, responses, siteId).then(() => { - return true; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. - return Promise.reject(error); - } else { - // Couldn't connect to server, store it offline. - return storeOffline(); - } - }); - }); - } - - /** - * Send a response to a choice to Moodle. It will fail if offline or cannot connect. - * - * @param choiceId Choice ID. - * @param responses IDs of selected options. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when responses are successfully submitted. - */ - submitResponseOnline(choiceId: number, responses: number[], siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - choiceid: choiceId, - responses: responses - }; - - return site.write('mod_choice_submit_choice_response', params).then(() => { - // Invalidate related data. - const promises = [ - this.invalidateOptions(choiceId, siteId), - this.invalidateResults(choiceId, siteId) - ]; - - return Promise.all(promises).catch(() => { - // Ignore errors. - }); - }); - }); - } -} - -/** - * Choice returned by mod_choice_get_choices_by_courses. - */ -export type AddonModChoiceChoice = { - id: number; // Choice instance id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Choice name. - intro: string; // The choice intro. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles?: CoreWSExternalFile[]; // @since 3.2. - publish?: boolean; // If choice is published. - showresults?: number; // 0 never, 1 after answer, 2 after close, 3 always. - display?: number; // Display mode (vertical, horizontal). - allowupdate?: boolean; // Allow update. - allowmultiple?: boolean; // Allow multiple choices. - showunanswered?: boolean; // Show users who not answered yet. - includeinactive?: boolean; // Include inactive users. - limitanswers?: boolean; // Limit unswers. - timeopen?: number; // Date of opening validity. - timeclose?: number; // Date of closing validity. - showpreview?: boolean; // Show preview before timeopen. - timemodified?: number; // Time of last modification. - completionsubmit?: boolean; // Completion on user submission. - section?: number; // Course section id. - visible?: boolean; // Visible. - groupmode?: number; // Group mode. - groupingid?: number; // Group id. -}; - -/** - * Option returned by mod_choice_get_choice_options. - */ -export type AddonModChoiceOption = { - id: number; // Option id. - text: string; // Text of the choice. - maxanswers: number; // Maximum number of answers. - displaylayout: boolean; // True for orizontal, otherwise vertical. - countanswers: number; // Number of answers. - checked: boolean; // We already answered. - disabled: boolean; // Option disabled. -}; - -/** - * Result returned by mod_choice_get_choice_results. - */ -export type AddonModChoiceResult = { - id: number; // Choice instance id. - text: string; // Text of the choice. - maxanswer: number; // Maximum number of answers. - userresponses: { - userid: number; // User id. - fullname: string; // User full name. - profileimageurl: string; // Profile user image url. - answerid?: number; // Answer id. - timemodified?: number; // Time of modification. - }[]; - numberofuser: number; // Number of users answers. - percentageamount: number; // Percentage of users answers. -}; - -/** - * Result of WS mod_choice_get_choices_by_courses. - */ -export type AddonModChoiceGetChoicesByCoursesResult = { - choices: AddonModChoiceChoice[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_choice_get_choice_options. - */ -export type AddonModChoiceGetChoiceOptionsResult = { - options: AddonModChoiceOption[]; // Options. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_choice_get_choice_results. - */ -export type AddonModChoiceGetChoiceResults = { - options: AddonModChoiceResult[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_choice_delete_choice_responses. - */ -export type AddonModChoiceDeleteChoiceResponsesResult = { - status: boolean; // Status, true if everything went right. - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/choice/providers/link-handler.ts b/src/addon/mod/choice/providers/link-handler.ts deleted file mode 100644 index 482d92ef5..000000000 --- a/src/addon/mod/choice/providers/link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to choice. - */ -@Injectable() -export class AddonModChoiceLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModChoiceLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModChoice', 'choice'); - } -} diff --git a/src/addon/mod/choice/providers/list-link-handler.ts b/src/addon/mod/choice/providers/list-link-handler.ts deleted file mode 100644 index df74bdcad..000000000 --- a/src/addon/mod/choice/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to choice list page. - */ -@Injectable() -export class AddonModChoiceListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModChoiceListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModChoice', 'choice'); - } -} diff --git a/src/addon/mod/choice/providers/module-handler.ts b/src/addon/mod/choice/providers/module-handler.ts deleted file mode 100644 index 0d6f4473b..000000000 --- a/src/addon/mod/choice/providers/module-handler.ts +++ /dev/null @@ -1,88 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModChoiceIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support choice modules. - */ -@Injectable() -export class AddonModChoiceModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModChoice'; - modName = 'choice'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_choice-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModChoiceIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModChoiceIndexComponent; - } -} diff --git a/src/addon/mod/choice/providers/offline.ts b/src/addon/mod/choice/providers/offline.ts deleted file mode 100644 index ef7a15391..000000000 --- a/src/addon/mod/choice/providers/offline.ts +++ /dev/null @@ -1,172 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; - -/** - * Service to handle offline choices. - */ -@Injectable() -export class AddonModChoiceOfflineProvider { - - // Variables for database. - static CHOICE_TABLE = 'addon_mod_choice_responses'; - - protected siteSchema: CoreSiteSchema = { - name: 'AddonModChoiceOfflineProvider', - version: 1, - tables: [ - { - name: AddonModChoiceOfflineProvider.CHOICE_TABLE, - columns: [ - { - name: 'choiceid', - type: 'INTEGER' - }, - { - name: 'name', - type: 'TEXT' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'userid', - type: 'INTEGER' - }, - { - name: 'responses', - type: 'TEXT' - }, - { - name: 'deleting', - type: 'INTEGER' - }, - { - name: 'timecreated', - type: 'INTEGER' - } - ], - primaryKeys: ['choiceid', 'userid'] - } - ] - }; - - constructor(private sitesProvider: CoreSitesProvider) { - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete a response. - * - * @param choiceId Choice ID to remove. - * @param siteId Site ID. If not defined, current site. - * @param userId User the responses belong to. If not defined, current user in site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteResponse(choiceId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().deleteRecords(AddonModChoiceOfflineProvider.CHOICE_TABLE, {choiceid: choiceId, userid: userId}); - }); - } - - /** - * Get all offline responses. - * - * @param siteId Site ID. If not defined, current site. - * @return Promi[se resolved with responses. - */ - getResponses(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModChoiceOfflineProvider.CHOICE_TABLE).then((records) => { - records.forEach((record) => { - record.responses = JSON.parse(record.responses); - }); - - return records; - }); - }); - } - - /** - * Check if there are offline responses to send. - * - * @param choiceId Choice ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User the responses belong to. If not defined, current user in site. - * @return Promise resolved with boolean: true if has offline answers, false otherwise. - */ - hasResponse(choiceId: number, siteId?: string, userId?: number): Promise { - return this.getResponse(choiceId, siteId, userId).then((response) => { - return !!response.choiceid; - }).catch((error) => { - // No offline data found, return false. - return false; - }); - } - - /** - * Get response to be synced. - * - * @param choiceId Choice ID to get. - * @param siteId Site ID. If not defined, current site. - * @param userId User the responses belong to. If not defined, current user in site. - * @return Promise resolved with the object to be synced. - */ - getResponse(choiceId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().getRecord(AddonModChoiceOfflineProvider.CHOICE_TABLE, {choiceid: choiceId, userid: userId}) - .then((record) => { - record.responses = JSON.parse(record.responses); - - return record; - }); - }); - } - - /** - * Offline version for sending a response to a choice to Moodle. - * - * @param choiceId Choice ID. - * @param name Choice name. - * @param courseId Course ID the choice belongs to. - * @param responses IDs of selected options. - * @param deleting If true, the user is deleting responses, if false, submitting. - * @param siteId Site ID. If not defined, current site. - * @param userId User the responses belong to. If not defined, current user in site. - * @return Promise resolved when results are successfully submitted. - */ - saveResponse(choiceId: number, name: string, courseId: number, responses: number[], deleting: boolean, - siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - choiceid: choiceId, - name: name, - courseid: courseId, - userid: userId || site.getUserId(), - responses: JSON.stringify(responses), - deleting: deleting ? 1 : 0, - timecreated: new Date().getTime() - }; - - return site.getDb().insertRecord(AddonModChoiceOfflineProvider.CHOICE_TABLE, entry); - }); - } -} diff --git a/src/addon/mod/choice/providers/prefetch-handler.ts b/src/addon/mod/choice/providers/prefetch-handler.ts deleted file mode 100644 index f319c6044..000000000 --- a/src/addon/mod/choice/providers/prefetch-handler.ts +++ /dev/null @@ -1,175 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModChoiceSyncProvider } from './sync'; -import { AddonModChoiceProvider } from './choice'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch choices. - */ -@Injectable() -export class AddonModChoicePrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModChoice'; - modName = 'choice'; - component = AddonModChoiceProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^answers$/; - - protected syncProvider: AddonModChoiceSyncProvider; // It will be injected later to prevent circular dependencies. - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected choiceProvider: AddonModChoiceProvider, - protected userProvider: CoreUserProvider, - protected injector: Injector) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchChoice.bind(this)); - } - - /** - * Prefetch a choice. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchChoice(module: any, courseId: number, single: boolean, siteId: string): Promise { - const commonOptions = { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - const modOptions = { - cmId: module.id, - ...commonOptions, // Include all common options. - }; - - return this.choiceProvider.getChoice(courseId, module.id, commonOptions).then((choice) => { - const promises = []; - - // Get the options and results. - promises.push(this.choiceProvider.getOptions(choice.id, modOptions)); - promises.push(this.choiceProvider.getResults(choice.id, modOptions).then((options) => { - // If we can see the users that answered, prefetch their profile and avatar. - const subPromises = []; - options.forEach((option) => { - option.userresponses.forEach((response) => { - if (response.userid) { - subPromises.push(this.userProvider.getProfile(response.userid, courseId, false, siteId)); - } - if (response.profileimageurl) { - subPromises.push(this.filepoolProvider.addToQueueByUrl(siteId, response.profileimageurl).catch(() => { - // Ignore failures. - })); - } - }); - }); - - return Promise.all(subPromises); - })); - - // Get the intro files. - const introFiles = this.getIntroFilesFromInstance(module, choice); - promises.push(this.filepoolProvider.addFilesToQueue(siteId, introFiles, AddonModChoiceProvider.COMPONENT, module.id)); - - return Promise.all(promises); - }); - } - - /** - * Returns choice intro files. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @return Promise resolved with list of intro files. - */ - getIntroFiles(module: any, courseId: number): Promise { - return this.choiceProvider.getChoice(courseId, module.id).catch(() => { - // Not found, return undefined so module description is used. - }).then((choice) => { - return this.getIntroFilesFromInstance(module, choice); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.choiceProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - return this.choiceProvider.invalidateChoiceData(courseId); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - if (!this.syncProvider) { - this.syncProvider = this.injector.get(AddonModChoiceSyncProvider); - } - - return this.syncProvider.syncChoice(module.instance, undefined, siteId); - } -} diff --git a/src/addon/mod/choice/providers/sync-cron-handler.ts b/src/addon/mod/choice/providers/sync-cron-handler.ts deleted file mode 100644 index 986f3f09a..000000000 --- a/src/addon/mod/choice/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModChoiceSyncProvider } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModChoiceSyncCronHandler implements CoreCronHandler { - name = 'AddonModChoiceSyncCronHandler'; - - constructor(private choiceSync: AddonModChoiceSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.choiceSync.syncAllChoices(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.choiceSync.syncInterval; - } -} diff --git a/src/addon/mod/choice/providers/sync.ts b/src/addon/mod/choice/providers/sync.ts deleted file mode 100644 index e5367ac1f..000000000 --- a/src/addon/mod/choice/providers/sync.ts +++ /dev/null @@ -1,227 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreAppProvider } from '@providers/app'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { AddonModChoiceOfflineProvider } from './offline'; -import { AddonModChoiceProvider } from './choice'; -import { CoreEventsProvider } from '@providers/events'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreCourseActivitySyncBaseProvider } from '@core/course/classes/activity-sync'; -import { CoreSyncProvider } from '@providers/sync'; -import { AddonModChoicePrefetchHandler } from './prefetch-handler'; - -/** - * Service to sync choices. - */ -@Injectable() -export class AddonModChoiceSyncProvider extends CoreCourseActivitySyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_choice_autom_synced'; - protected componentTranslate: string; - - constructor(protected sitesProvider: CoreSitesProvider, loggerProvider: CoreLoggerProvider, - protected appProvider: CoreAppProvider, private choiceOffline: AddonModChoiceOfflineProvider, - private eventsProvider: CoreEventsProvider, private choiceProvider: AddonModChoiceProvider, - translate: TranslateService, private utils: CoreUtilsProvider, protected textUtils: CoreTextUtilsProvider, - private courseProvider: CoreCourseProvider, syncProvider: CoreSyncProvider, timeUtils: CoreTimeUtilsProvider, - private logHelper: CoreCourseLogHelperProvider, prefetchHandler: AddonModChoicePrefetchHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate) { - - super('AddonModChoiceSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils, prefetchDelegate, prefetchHandler); - - this.componentTranslate = courseProvider.translateModuleName('choice'); - } - - /** - * Get the ID of a choice sync. - * - * @param choiceId Choice ID. - * @param userId User the responses belong to. - * @return Sync ID. - */ - protected getSyncId(choiceId: number, userId: number): string { - return choiceId + '#' + userId; - } - - /** - * Try to synchronize all the choices in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllChoices(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('choices', this.syncAllChoicesFunc.bind(this), [force], siteId); - } - - /** - * Sync all pending choices on a site. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllChoicesFunc(siteId?: string, force?: boolean): Promise { - return this.choiceOffline.getResponses(siteId).then((responses) => { - // Sync all responses. - const promises = responses.map((response) => { - const promise = force ? this.syncChoice(response.choiceid, response.userid, siteId) : - this.syncChoiceIfNeeded(response.choiceid, response.userid, siteId); - - return promise.then((result) => { - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModChoiceSyncProvider.AUTO_SYNCED, { - choiceId: response.choiceid, - userId: response.userid, - warnings: result.warnings - }, siteId); - } - }); - }); - - return Promise.all(promises); - }); - } - - /** - * Sync an choice only if a certain time has passed since the last time. - * - * @param choiceId Choice ID to be synced. - * @param userId User the answers belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the choice is synced or it doesn't need to be synced. - */ - syncChoiceIfNeeded(choiceId: number, userId: number, siteId?: string): Promise { - const syncId = this.getSyncId(choiceId, userId); - - return this.isSyncNeeded(syncId, siteId).then((needed) => { - if (needed) { - return this.syncChoice(choiceId, userId, siteId); - } - }); - } - - /** - * Synchronize a choice. - * - * @param choiceId Choice ID to be synced. - * @param userId User the answers belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncChoice(choiceId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - siteId = site.getId(); - - const syncId = this.getSyncId(choiceId, userId); - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this discussion, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - this.logger.debug(`Try to sync choice '${choiceId}' for user '${userId}'`); - - let courseId; - const result = { - warnings: [], - updated: false - }; - - // Sync offline logs. - const syncPromise = this.logHelper.syncIfNeeded(AddonModChoiceProvider.COMPONENT, choiceId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - return this.choiceOffline.getResponse(choiceId, siteId, userId).catch(() => { - // No offline data found, return empty object. - return {}; - }); - }).then((data) => { - if (!data.choiceid) { - // Nothing to sync. - return; - } - - if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - courseId = data.courseid; - - // Send the responses. - let promise; - - if (data.deleting) { - // A user has deleted some responses. - promise = this.choiceProvider.deleteResponsesOnline(choiceId, data.responses, siteId); - } else { - // A user has added some responses. - promise = this.choiceProvider.submitResponseOnline(choiceId, data.responses, siteId); - } - - return promise.then(() => { - result.updated = true; - - return this.choiceOffline.deleteResponse(choiceId, siteId, userId); - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. Delete them. - result.updated = true; - - return this.choiceOffline.deleteResponse(choiceId, siteId, userId).then(() => { - // Responses deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } - - // Couldn't connect to server, reject. - return Promise.reject(error); - }); - }).then(() => { - if (courseId) { - // Data has been sent to server, prefetch choice if needed. - return this.courseProvider.getModuleBasicInfoByInstance(choiceId, 'choice', siteId).then((module) => { - return this.prefetchAfterUpdate(module, courseId, undefined, siteId); - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(syncId, siteId); - }).then(() => { - // All done, return the warnings. - return result; - }); - - return this.addOngoingSync(syncId, syncPromise, siteId); - }); - } -} diff --git a/src/addon/mod/data/classes/field-plugin-component.ts b/src/addon/mod/data/classes/field-plugin-component.ts deleted file mode 100644 index ac622a505..000000000 --- a/src/addon/mod/data/classes/field-plugin-component.ts +++ /dev/null @@ -1,92 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Input, Output, OnInit, OnChanges, SimpleChange, EventEmitter } from '@angular/core'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; - -/** - * Base class for component to render a field. - */ -export class AddonModDataFieldPluginComponent implements OnInit, OnChanges { - @Input() mode: string; // The render mode. - @Input() field: any; // The field to render. - @Input() value?: any; // The value of the field. - @Input() database?: any; // Database object. - @Input() error?: string; // Error when editing. - @Output() gotoEntry?: EventEmitter; // Action to perform. - @Input() form?: FormGroup; // Form where to add the form control. Just required for edit and search modes. - @Input() search?: any; // The search value of all fields. - - constructor(protected fb: FormBuilder) { - this.gotoEntry = new EventEmitter(); - } - - /** - * Add the form control for the search mode. - * - * @param fieldName Control field name. - * @param value Initial set value. - */ - protected addControl(fieldName: string, value?: any): void { - if (!this.form) { - return; - } - - if (this.mode == 'search') { - this.form.addControl(fieldName, this.fb.control(this.search[fieldName] || null)); - } - - if (this.mode == 'edit') { - this.form.addControl(fieldName, this.fb.control(value, this.field.required ? Validators.required : null)); - } - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.init(); - } - - /** - * Initialize field. - */ - protected init(): void { - return; - } - - /** - * Return if is shown or list mode. - * - * @return True if mode is show or list. - */ - isShowOrListMode(): boolean { - return this.mode == 'list' || this.mode == 'show'; - } - - /** - * Component being changed. - */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if (this.isShowOrListMode() && changes.value) { - this.updateValue(changes.value.currentValue); - } - } - - /** - * Update value being shown. - */ - protected updateValue(value: any): void { - this.value = value; - } -} diff --git a/src/addon/mod/data/components/action/action.ts b/src/addon/mod/data/components/action/action.ts deleted file mode 100644 index 81051183d..000000000 --- a/src/addon/mod/data/components/action/action.ts +++ /dev/null @@ -1,133 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component, Input, OnInit, Injector } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { AddonModDataProvider } from '../../providers/data'; -import { AddonModDataHelperProvider } from '../../providers/helper'; -import { AddonModDataOfflineProvider } from '../../providers/offline'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreTagProvider } from '@core/tag/providers/tag'; - -/** - * Component that displays a database action. - */ -@Component({ - selector: 'addon-mod-data-action', - templateUrl: 'addon-mod-data-action.html', -}) -export class AddonModDataActionComponent implements OnInit { - @Input() mode: string; // The render mode. - @Input() action: string; // The field to render. - @Input() entry?: any; // The value of the field. - @Input() database: any; // Database object. - @Input() module: any; // Module object. - @Input() group: number; // Module object. - @Input() offset?: number; // Offset of the entry. - - siteId: string; - rootUrl: string; - url: string; - userPicture: string; - tagsEnabled: boolean; - - constructor(protected injector: Injector, protected dataProvider: AddonModDataProvider, - protected dataOffline: AddonModDataOfflineProvider, protected eventsProvider: CoreEventsProvider, - sitesProvider: CoreSitesProvider, protected userProvider: CoreUserProvider, private navCtrl: NavController, - protected linkHelper: CoreContentLinksHelperProvider, private dataHelper: AddonModDataHelperProvider, - private tagProvider: CoreTagProvider) { - this.rootUrl = sitesProvider.getCurrentSite().getURL(); - this.siteId = sitesProvider.getCurrentSiteId(); - this.tagsEnabled = this.tagProvider.areTagsAvailableInSite(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (this.action == 'userpicture') { - this.userProvider.getProfile(this.entry.userid, this.database.courseid).then((profile) => { - this.userPicture = profile.profileimageurl; - }); - } - } - - /** - * Approve the entry. - */ - approveEntry(): void { - this.dataHelper.approveOrDisapproveEntry(this.database.id, this.entry.id, true, this.database.courseid); - } - - /** - * Show confirmation modal for deleting the entry. - */ - deleteEntry(): void { - this.dataHelper.showDeleteEntryModal(this.database.id, this.entry.id, this.database.courseid); - } - - /** - * Disapprove the entry. - */ - disapproveEntry(): void { - this.dataHelper.approveOrDisapproveEntry(this.database.id, this.entry.id, false, this.database.courseid); - } - - /** - * Go to the edit page of the entry. - */ - editEntry(): void { - const pageParams = { - courseId: this.database.course, - module: this.module, - entryId: this.entry.id - }; - - this.linkHelper.goInSite(this.navCtrl, 'AddonModDataEditPage', pageParams); - } - - /** - * Go to the view page of the entry. - */ - viewEntry(): void { - const pageParams: any = { - courseId: this.database.course, - module: this.module, - entryId: this.entry.id, - group: this.group, - offset: this.offset - }; - - this.linkHelper.goInSite(this.navCtrl, 'AddonModDataEntryPage', pageParams); - } - - /** - * Undo delete action. - * - * @return Solved when done. - */ - undoDelete(): Promise { - const dataId = this.database.id, - entryId = this.entry.id; - - return this.dataOffline.getEntry(dataId, entryId, 'delete', this.siteId).then(() => { - // Found. Just delete the action. - return this.dataOffline.deleteEntry(dataId, entryId, 'delete', this.siteId); - }).then(() => { - this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId}, this.siteId); - }); - } -} diff --git a/src/addon/mod/data/components/action/addon-mod-data-action.html b/src/addon/mod/data/components/action/addon-mod-data-action.html deleted file mode 100644 index 74d2afd56..000000000 --- a/src/addon/mod/data/components/action/addon-mod-data-action.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -{{ entry.timecreated * 1000 | coreFormatDate }} -{{ entry.timemodified * 1000 | coreFormatDate }} - - - - - -{{entry.fullname}} - - diff --git a/src/addon/mod/data/components/components.module.ts b/src/addon/mod/data/components/components.module.ts deleted file mode 100644 index c824304ce..000000000 --- a/src/addon/mod/data/components/components.module.ts +++ /dev/null @@ -1,59 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModDataIndexComponent } from './index/index'; -import { AddonModDataFieldPluginComponent } from './field-plugin/field-plugin'; -import { AddonModDataActionComponent } from './action/action'; -import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module'; -import { CoreCommentsComponentsModule } from '@core/comments/components/components.module'; -import { CoreTagComponentsModule } from '@core/tag/components/components.module'; - -@NgModule({ - declarations: [ - AddonModDataIndexComponent, - AddonModDataFieldPluginComponent, - AddonModDataActionComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreCourseComponentsModule, - CoreCompileHtmlComponentModule, - CoreCommentsComponentsModule, - CoreTagComponentsModule - ], - providers: [ - ], - exports: [ - AddonModDataIndexComponent, - AddonModDataFieldPluginComponent, - AddonModDataActionComponent - ], - entryComponents: [ - AddonModDataIndexComponent - ] -}) -export class AddonModDataComponentsModule {} diff --git a/src/addon/mod/data/components/field-plugin/addon-mod-data-field-plugin.html b/src/addon/mod/data/components/field-plugin/addon-mod-data-field-plugin.html deleted file mode 100644 index 489bbda21..000000000 --- a/src/addon/mod/data/components/field-plugin/addon-mod-data-field-plugin.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/addon/mod/data/components/field-plugin/field-plugin.ts b/src/addon/mod/data/components/field-plugin/field-plugin.ts deleted file mode 100644 index a1be01a3b..000000000 --- a/src/addon/mod/data/components/field-plugin/field-plugin.ts +++ /dev/null @@ -1,93 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component, Input, Output, OnInit, Injector, ViewChild, OnChanges, SimpleChange, EventEmitter } from '@angular/core'; -import { FormGroup } from '@angular/forms'; -import { AddonModDataProvider } from '../../providers/data'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; - -/** - * Component that displays a database field plugin. - */ -@Component({ - selector: 'addon-mod-data-field-plugin', - templateUrl: 'addon-mod-data-field-plugin.html', -}) -export class AddonModDataFieldPluginComponent implements OnInit, OnChanges { - @ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent; - - @Input() mode: string; // The render mode. - @Input() field: any; // The field to render. - @Input() value?: any; // The value of the field. - @Input() database?: any; // Database object. - @Input() error?: string; // Error when editing. - @Output() gotoEntry: EventEmitter; // Action to perform. - @Input() form?: FormGroup; // Form where to add the form control. Just required for edit and search modes. - @Input() search?: any; // The search value of all fields. - - fieldComponent: any; // Component to render the plugin. - data: any; // Data to pass to the component. - fieldLoaded: boolean; - - constructor(protected injector: Injector, protected dataDelegate: AddonModDataFieldsDelegate, - protected dataProvider: AddonModDataProvider) { - this.gotoEntry = new EventEmitter(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.field) { - this.fieldLoaded = true; - - return; - } - - // Check if the plugin has defined its own component to render itself. - this.dataDelegate.getComponentForField(this.injector, this.field).then((component) => { - this.fieldComponent = component; - - if (component) { - // Prepare the data to pass to the component. - this.data = { - mode: this.mode, - field: this.field, - value: this.value, - database: this.database, - error: this.error, - gotoEntry: this.gotoEntry, - form: this.form, - search: this.search - }; - } - }).finally(() => { - this.fieldLoaded = true; - }); - } - - /** - * Component being changed. - */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if (this.fieldLoaded && this.data) { - if (this.mode == 'edit' && changes.error) { - this.data.error = changes.error.currentValue; - } - if ((this.mode == 'show' || this.mode == 'list') && changes.value) { - this.data.value = changes.value.currentValue; - } - } - } -} diff --git a/src/addon/mod/data/components/index/addon-mod-data-index.html b/src/addon/mod/data/components/index/addon-mod-data-index.html deleted file mode 100644 index c0a92a4ad..000000000 --- a/src/addon/mod/data/components/index/addon-mod-data-index.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - -
- - {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} -
- - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - -
- - {{ 'addon.mod_data.notopenyet' | translate:{$a: timeAvailableFromReadable} }} -
- -
- - {{ 'addon.mod_data.expired' | translate:{$a: timeAvailableToReadable} }} -
- -
- - {{ 'addon.mod_data.entrieslefttoaddtoview' | translate:{$a: {entrieslefttoview: access.entrieslefttoview} } }} -
- -
- - {{ 'addon.mod_data.entrieslefttoadd' | translate:{$a: {entriesleft: access.entrieslefttoadd} } }} -
- - - - - {{ 'addon.mod_data.resetsettings' | translate}} - - -
-

-
-
- -
- - - -
- - - - - - - - - - - - - - - - - {{ 'addon.mod_data.resetsettings' | translate}} - - -
- - - - diff --git a/src/addon/mod/data/components/index/index.ts b/src/addon/mod/data/components/index/index.ts deleted file mode 100644 index 34feeef8f..000000000 --- a/src/addon/mod/data/components/index/index.ts +++ /dev/null @@ -1,491 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector } from '@angular/core'; -import { Content, ModalController, NavController } from 'ionic-angular'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { CoreRatingProvider } from '@core/rating/providers/rating'; -import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; -import { AddonModDataProvider } from '../../providers/data'; -import { AddonModDataHelperProvider } from '../../providers/helper'; -import { AddonModDataSyncProvider } from '../../providers/sync'; -import { AddonModDataComponentsModule } from '../components.module'; -import { AddonModDataPrefetchHandler } from '../../providers/prefetch-handler'; - -/** - * Component that displays a data index page. - */ -@Component({ - selector: 'addon-mod-data-index', - templateUrl: 'addon-mod-data-index.html', -}) -export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComponent { - - component = AddonModDataProvider.COMPONENT; - moduleName = 'data'; - - access: any = {}; - data: any = {}; - fields: any; - selectedGroup: number; - timeAvailableFrom: number | boolean; - timeAvailableFromReadable: string | boolean; - timeAvailableTo: number | boolean; - timeAvailableToReadable: string | boolean; - isEmpty = true; - groupInfo: CoreGroupInfo; - entries = []; - firstEntry = false; - canAdd = false; - canSearch = false; - search = { - sortBy: '0', - sortDirection: 'DESC', - page: 0, - text: '', - searching: false, - searchingAdvanced: false, - advanced: [] - }; - hasNextPage = false; - entriesRendered = ''; - extraImports = [AddonModDataComponentsModule]; - jsData; - foundRecordsData; - - protected syncEventName = AddonModDataSyncProvider.AUTO_SYNCED; - protected entryChangedObserver: any; - protected hasComments = false; - protected fieldsArray: any; - - hasOfflineRatings: boolean; - protected ratingOfflineObserver: any; - protected ratingSyncObserver: any; - - constructor( - injector: Injector, - @Optional() content: Content, - private dataProvider: AddonModDataProvider, - private dataHelper: AddonModDataHelperProvider, - private prefetchHandler: AddonModDataPrefetchHandler, - private timeUtils: CoreTimeUtilsProvider, - private groupsProvider: CoreGroupsProvider, - private modalCtrl: ModalController, - private utils: CoreUtilsProvider, - protected navCtrl: NavController) { - - super(injector, content); - - // Refresh entries on change. - this.entryChangedObserver = this.eventsProvider.on(AddonModDataProvider.ENTRY_CHANGED, (eventData) => { - if (this.data.id == eventData.dataId) { - this.loaded = false; - - return this.loadContent(true); - } - }, this.siteId); - - // Listen for offline ratings saved and synced. - this.ratingOfflineObserver = this.eventsProvider.on(CoreRatingProvider.RATING_SAVED_EVENT, (data) => { - if (this.data && data.component == 'mod_data' && data.ratingArea == 'entry' && data.contextLevel == 'module' - && data.instanceId == this.data.coursemodule) { - this.hasOfflineRatings = true; - } - }); - this.ratingSyncObserver = this.eventsProvider.on(CoreRatingSyncProvider.SYNCED_EVENT, (data) => { - if (this.data && data.component == 'mod_data' && data.ratingArea == 'entry' && data.contextLevel == 'module' - && data.instanceId == this.data.coursemodule) { - this.hasOfflineRatings = false; - } - }); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.selectedGroup = this.group || 0; - - this.loadContent(false, true).then(() => { - return this.logView(true); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.dataProvider.invalidateDatabaseData(this.courseId)); - if (this.data) { - promises.push(this.dataProvider.invalidateDatabaseAccessInformationData(this.data.id)); - promises.push(this.groupsProvider.invalidateActivityGroupInfo(this.data.coursemodule)); - promises.push(this.dataProvider.invalidateEntriesData(this.data.id)); - promises.push(this.dataProvider.invalidateFieldsData(this.data.id)); - - if (this.hasComments) { - this.eventsProvider.trigger(CoreCommentsProvider.REFRESH_COMMENTS_EVENT, { - contextLevel: 'module', - instanceId: this.data.coursemodule - }, this.sitesProvider.getCurrentSiteId()); - } - } - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (this.data && syncEventData.dataId == this.data.id && typeof syncEventData.entryId == 'undefined') { - this.loaded = false; - // Refresh the data. - this.domUtils.scrollToTop(this.content); - - return true; - } - - return false; - } - - /** - * Download data contents. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - let canAdd = false, - canSearch = false; - - return this.dataProvider.getDatabase(this.courseId, this.module.id).then((data) => { - this.data = data; - this.hasComments = data.comments; - - this.description = data.intro || data.description; - this.dataRetrieved.emit(data); - - if (sync) { - // Try to synchronize the data. - return this.syncActivity(showErrors).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - return this.dataProvider.getDatabaseAccessInformation(this.data.id, {cmId: this.module.id}); - }).then((accessData) => { - this.access = accessData; - - if (!accessData.timeavailable) { - const time = this.timeUtils.timestamp(); - - this.timeAvailableFrom = this.data.timeavailablefrom && time < this.data.timeavailablefrom ? - parseInt(this.data.timeavailablefrom, 10) * 1000 : false; - this.timeAvailableFromReadable = this.timeAvailableFrom ? this.timeUtils.userDate(this.timeAvailableFrom) : false; - this.timeAvailableTo = this.data.timeavailableto && time > this.data.timeavailableto ? - parseInt(this.data.timeavailableto, 10) * 1000 : false; - this.timeAvailableToReadable = this.timeAvailableTo ? this.timeUtils.userDate(this.timeAvailableTo) : false; - - this.isEmpty = true; - this.groupInfo = null; - - return; - } - - canSearch = true; - canAdd = accessData.canaddentry; - - return this.groupsProvider.getActivityGroupInfo(this.data.coursemodule).then((groupInfo) => { - this.groupInfo = groupInfo; - this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, groupInfo); - }); - }).then(() => { - return this.dataProvider.getFields(this.data.id, {cmId: this.module.id}).then((fields) => { - if (fields.length == 0) { - canSearch = false; - canAdd = false; - } - this.search.advanced = []; - - this.fields = this.utils.arrayToObject(fields, 'id'); - this.fieldsArray = this.utils.objectToArray(this.fields); - - return this.fetchEntriesData(); - }); - }).finally(() => { - this.canAdd = canAdd; - this.canSearch = canSearch; - this.fillContextMenu(refresh); - }); - } - - /** - * Fetch current database entries. - * - * @return Resolved then done. - */ - protected fetchEntriesData(): Promise { - - return this.dataProvider.getDatabaseAccessInformation(this.data.id, { - groupId: this.selectedGroup, - cmId: this.module.id, - }).then((accessData) => { - // Update values for current group. - this.access.canaddentry = accessData.canaddentry; - - const search = this.search.searching && !this.search.searchingAdvanced ? this.search.text : undefined; - const advSearch = this.search.searching && this.search.searchingAdvanced ? this.search.advanced : undefined; - - return this.dataHelper.fetchEntries(this.data, this.fieldsArray, { - groupId: this.selectedGroup, - search, - advSearch, - sort: Number(this.search.sortBy), - order: this.search.sortDirection, - page: this.search.page, - }); - }).then((entries) => { - const numEntries = entries.entries.length; - const numOfflineEntries = entries.offlineEntries.length; - this.isEmpty = !numEntries && !entries.offlineEntries.length; - this.hasNextPage = numEntries >= AddonModDataProvider.PER_PAGE && ((this.search.page + 1) * - AddonModDataProvider.PER_PAGE) < entries.totalcount; - this.hasOffline = entries.hasOfflineActions; - this.hasOfflineRatings = entries.hasOfflineRatings; - this.entriesRendered = ''; - - if (typeof entries.maxcount != 'undefined') { - this.foundRecordsData = { - num: entries.totalcount, - max: entries.maxcount, - reseturl: '#' - }; - } else { - this.foundRecordsData = undefined; - } - - if (!this.isEmpty) { - this.entries = entries.offlineEntries.concat(entries.entries); - - let entriesHTML = this.dataHelper.getTemplate(this.data, 'listtemplateheader', this.fieldsArray); - - // Get first entry from the whole list. - if (!this.search.searching || !this.firstEntry) { - this.firstEntry = this.entries[0].id; - } - - const template = this.dataHelper.getTemplate(this.data, 'listtemplate', this.fieldsArray); - - const entriesById = {}; - this.entries.forEach((entry, index) => { - entriesById[entry.id] = entry; - - const actions = this.dataHelper.getActions(this.data, this.access, entry); - const offset = this.search.searching ? undefined : - this.search.page * AddonModDataProvider.PER_PAGE + index - numOfflineEntries; - - entriesHTML += this.dataHelper.displayShowFields(template, this.fieldsArray, entry, offset, 'list', actions); - }); - entriesHTML += this.dataHelper.getTemplate(this.data, 'listtemplatefooter', this.fieldsArray); - - this.entriesRendered = this.domUtils.fixHtml(entriesHTML); - - // Pass the input data to the component. - this.jsData = { - fields: this.fields, - entries: entriesById, - data: this.data, - module: this.module, - group: this.selectedGroup, - gotoEntry: this.gotoEntry.bind(this) - }; - } else if (!this.search.searching) { - // Empty and no searching. - this.canSearch = false; - } - this.firstEntry = false; - }); - } - - /** - * Display the chat users modal. - */ - showSearch(): void { - const modal = this.modalCtrl.create('AddonModDataSearchPage', { - search: this.search, - fields: this.fields, - data: this.data}); - modal.onDidDismiss((data) => { - // Add data to search object. - if (data) { - this.search = data; - this.searchEntries(0); - } - }); - modal.present(); - } - - /** - * Performs the search and closes the modal. - * - * @param page Page number. - * @return Resolved when done. - */ - searchEntries(page: number): Promise { - this.loaded = false; - this.search.page = page; - - return this.fetchEntriesData().then(() => { - // Log activity view for coherence with Moodle web. - return this.logView(); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Reset all search filters and closes the modal. - */ - searchReset(): void { - this.search.sortBy = '0'; - this.search.sortDirection = 'DESC'; - this.search.text = ''; - this.search.advanced = []; - this.search.searchingAdvanced = false; - this.search.searching = false; - this.searchEntries(0); - } - - /** - * Set group to see the database. - * - * @param groupId Group ID. - * @return Resolved when new group is selected or rejected if not. - */ - setGroup(groupId: number): Promise { - this.selectedGroup = groupId; - this.search.page = 0; - - return this.fetchEntriesData().then(() => { - // Log activity view for coherence with Moodle web. - return this.logView(); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - - return Promise.reject(null); - }); - } - - /** - * Opens add entries form. - */ - gotoAddEntries(): void { - const params = { - module: this.module, - courseId: this.courseId, - group: this.selectedGroup - }; - - this.navCtrl.push('AddonModDataEditPage', params); - } - - /** - * Goto the selected entry. - * - * @param entryId Entry ID. - */ - gotoEntry(entryId: number): void { - const params = { - module: this.module, - courseId: this.courseId, - entryId: entryId, - group: this.selectedGroup, - offset: null - }; - - // Try to find page number and offset of the entry. - const pageXOffset = this.entries.findIndex((entry) => entry.id == entryId); - if (pageXOffset >= 0) { - params.offset = this.search.page * AddonModDataProvider.PER_PAGE + pageXOffset; - } - - this.navCtrl.push('AddonModDataEntryPage', params); - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.prefetchHandler.sync(this.module, this.courseId); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return If suceed or not. - */ - protected hasSyncSucceed(result: any): boolean { - return result.updated; - } - - /** - * Log viewing the activity. - * - * @param checkCompletion Whether to check completion. - * @return Promise resolved when done. - */ - protected logView(checkCompletion?: boolean): Promise { - if (!this.data || !this.data.id) { - return Promise.resolve(); - } - - return this.dataProvider.logView(this.data.id, this.data.name).then(() => { - if (checkCompletion) { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - }).catch(() => { - // Ignore errors, the user could be offline. - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - this.entryChangedObserver && this.entryChangedObserver.off(); - this.ratingOfflineObserver && this.ratingOfflineObserver.off(); - this.ratingSyncObserver && this.ratingSyncObserver.off(); - } -} diff --git a/src/addon/mod/data/data.module.ts b/src/addon/mod/data/data.module.ts deleted file mode 100644 index f3b8c0f7b..000000000 --- a/src/addon/mod/data/data.module.ts +++ /dev/null @@ -1,96 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { AddonModDataComponentsModule } from './components/components.module'; -import { AddonModDataModuleHandler } from './providers/module-handler'; -import { AddonModDataProvider } from './providers/data'; -import { AddonModDataLinkHandler } from './providers/link-handler'; -import { AddonModDataApproveLinkHandler } from './providers/approve-link-handler'; -import { AddonModDataDeleteLinkHandler } from './providers/delete-link-handler'; -import { AddonModDataShowLinkHandler } from './providers/show-link-handler'; -import { AddonModDataEditLinkHandler } from './providers/edit-link-handler'; -import { AddonModDataListLinkHandler } from './providers/list-link-handler'; -import { AddonModDataHelperProvider } from './providers/helper'; -import { AddonModDataPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModDataSyncProvider } from './providers/sync'; -import { AddonModDataSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModDataOfflineProvider } from './providers/offline'; -import { AddonModDataFieldsDelegate } from './providers/fields-delegate'; -import { AddonModDataDefaultFieldHandler } from './providers/default-field-handler'; -import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate'; -import { AddonModDataTagAreaHandler } from './providers/tag-area-handler'; -import { AddonModDataFieldModule } from './fields/field.module'; - -// List of providers (without handlers). -export const ADDON_MOD_DATA_PROVIDERS: any[] = [ - AddonModDataProvider, - AddonModDataHelperProvider, - AddonModDataSyncProvider, - AddonModDataOfflineProvider, - AddonModDataFieldsDelegate -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModDataComponentsModule, - AddonModDataFieldModule - ], - providers: [ - AddonModDataProvider, - AddonModDataHelperProvider, - AddonModDataSyncProvider, - AddonModDataOfflineProvider, - AddonModDataFieldsDelegate, - AddonModDataModuleHandler, - AddonModDataPrefetchHandler, - AddonModDataLinkHandler, - AddonModDataApproveLinkHandler, - AddonModDataDeleteLinkHandler, - AddonModDataShowLinkHandler, - AddonModDataEditLinkHandler, - AddonModDataListLinkHandler, - AddonModDataSyncCronHandler, - AddonModDataDefaultFieldHandler, - AddonModDataTagAreaHandler - ] -}) -export class AddonModDataModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModDataModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModDataPrefetchHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModDataLinkHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModDataSyncCronHandler, - approveLinkHandler: AddonModDataApproveLinkHandler, deleteLinkHandler: AddonModDataDeleteLinkHandler, - showLinkHandler: AddonModDataShowLinkHandler, editLinkHandler: AddonModDataEditLinkHandler, - listLinkHandler: AddonModDataListLinkHandler, tagAreaDelegate: CoreTagAreaDelegate, - tagAreaHandler: AddonModDataTagAreaHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(approveLinkHandler); - contentLinksDelegate.registerHandler(deleteLinkHandler); - contentLinksDelegate.registerHandler(showLinkHandler); - contentLinksDelegate.registerHandler(editLinkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - cronDelegate.register(syncHandler); - tagAreaDelegate.registerHandler(tagAreaHandler); - } -} diff --git a/src/addon/mod/data/data.scss b/src/addon/mod/data/data.scss deleted file mode 100644 index 78160c7d2..000000000 --- a/src/addon/mod/data/data.scss +++ /dev/null @@ -1,120 +0,0 @@ -.addon-data-contents { - overflow: visible; - white-space: normal; - word-break: break-word; - padding: $content-padding; - @include safe-area-padding-horizontal($content-padding !important, $content-padding !important); - background-color: $white; - border-top-width: 1px; - border-bottom-width: 1px; - border-right-width: 0; - border-left-width: 0; - border-style: solid; - border-color: $list-border-color; - - @include darkmode { - background-color: $core-dark-item-bg-color; - } - - table, tbody { - display: block; - } - - tr { - @extend .row; - padding: 0; - @include media-breakpoint-down(sm) { - flex-direction: column; - } - } - - td, th { - @extend .col; - min-height: auto; - } - - // Do not let block elements to define widths or heights. - address, article, aside, blockquote, canvas, dd, div, dl, dt, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, - header, hr, li, main, nav, noscript, ol, p, pre, section, table, tfoot, ul, video { - width: auto !important; - height: auto !important; - min-width: auto !important; - min-height: auto !important; - // Avoid having one entry over another. - max-height: none !important; - - } -} - -page-addon-mod-data-search, -page-addon-mod-data-edit { - table { - width: 100%; - } - td { - vertical-align: top; - } - - .addon-data-lantlong { - display: flex; - } - - .addon-data-contents form, - form .addon-data-advanced-search { - background-color: $list-background-color; - - @include darkmode() { - background-color: $core-dark-item-bg-color; - color: $core-dark-text-color; - } - - .item.item-input .item-block .item-inner ion-input, - .item.item-input.item-input-has-focus .item-inner ion-input, - .item.item-input.input-has-focus .item-inner ion-input { - border: 0 !important; - box-shadow: none; - } - - .core-mark-required { - @include float(end); - - + ion-input, - + ion-select { - @include padding(null, 20px, null, null); - } - } - - @if ($text-input-md-show-focus-highlight) { - .input-md input:focus { - @include md-input-highlight($text-input-md-highlight-color); - } - } - - .input-md input { - @include padding-horizontal(null, ($item-md-padding-end / 2)); - border-bottom: 1px solid $list-md-border-color; - &:focus { - @include md-input-highlight($text-input-md-highlight-color); - } - } - - .input-ios input { - @include padding-horizontal(null, $item-ios-padding-end / 2); - @include safe-area-padding-horizontal(null, $item-ios-padding-end / 2); - border-bottom: $hairlines-width solid $list-ios-border-color; - &:focus { - @include ios-input-highlight($text-input-ios-highlight-color); - } - } - - ion-select { - width: 100%; - @include position(null, null, null, 0); - max-width: none; - } - - .core-item-has-rich-text-editor { - @include margin(null, 1px, null, null); - } - } -} \ No newline at end of file diff --git a/src/addon/mod/data/fields/checkbox/checkbox.module.ts b/src/addon/mod/data/fields/checkbox/checkbox.module.ts deleted file mode 100644 index ddf3be9d2..000000000 --- a/src/addon/mod/data/fields/checkbox/checkbox.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldCheckboxHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldCheckboxComponent } from './component/checkbox'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldCheckboxComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModDataFieldCheckboxHandler - ], - exports: [ - AddonModDataFieldCheckboxComponent - ], - entryComponents: [ - AddonModDataFieldCheckboxComponent - ] -}) -export class AddonModDataFieldCheckboxModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldCheckboxHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/checkbox/component/addon-mod-data-field-checkbox.html b/src/addon/mod/data/fields/checkbox/component/addon-mod-data-field-checkbox.html deleted file mode 100644 index c4459caae..000000000 --- a/src/addon/mod/data/fields/checkbox/component/addon-mod-data-field-checkbox.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - {{option.key}} - - - - - {{ 'addon.mod_data.selectedrequired' | translate }} - - - - - - \ No newline at end of file diff --git a/src/addon/mod/data/fields/checkbox/component/checkbox.ts b/src/addon/mod/data/fields/checkbox/component/checkbox.ts deleted file mode 100644 index a84d7f6d5..000000000 --- a/src/addon/mod/data/fields/checkbox/component/checkbox.ts +++ /dev/null @@ -1,73 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data checkbox field. - */ -@Component({ - selector: 'addon-mod-data-field-checkbox', - templateUrl: 'addon-mod-data-field-checkbox.html' -}) -export class AddonModDataFieldCheckboxComponent extends AddonModDataFieldPluginComponent { - - options = []; - - constructor(protected fb: FormBuilder) { - super(fb); - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.isShowOrListMode()) { - this.updateValue(this.value); - - return; - } - - this.options = this.field.param1.split(/\r?\n/).map((option) => { - return { key: option, value: option }; - }); - - const values = []; - if (this.mode == 'edit' && this.value && this.value.content) { - this.value.content.split('##').forEach((value) => { - const x = this.options.findIndex((option) => value == option.key); - if (x >= 0) { - values.push(value); - } - }); - } - - if (this.mode == 'search') { - this.addControl('f_' + this.field.id + '_allreq'); - } - - this.addControl('f_' + this.field.id, values); - } - - /** - * Update value being shown. - * - * @param value New value to be set. - */ - protected updateValue(value: any): void { - this.value = value || {}; - this.value.content = value && value.content && value.content.split('##').join('
'); - } -} diff --git a/src/addon/mod/data/fields/checkbox/providers/handler.ts b/src/addon/mod/data/fields/checkbox/providers/handler.ts deleted file mode 100644 index f28908f0c..000000000 --- a/src/addon/mod/data/fields/checkbox/providers/handler.ts +++ /dev/null @@ -1,142 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataFieldCheckboxComponent } from '../component/checkbox'; - -/** - * Handler for checkbox data field plugin. - */ -@Injectable() -export class AddonModDataFieldCheckboxHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldCheckboxHandler'; - type = 'checkbox'; - - constructor(private translate: TranslateService) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldCheckboxComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id, - reqName = 'f_' + field.id + '_allreq'; - - const values = []; - - if (inputData[fieldName] && inputData[fieldName].length > 0) { - values.push({ - name: fieldName, - value: inputData[fieldName] - }); - - if (inputData[reqName]) { - values.push({ - name: reqName, - value: true - }); - } - - return values; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - - return [{ - fieldid: field.id, - value: inputData[fieldName] || [] - }]; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id; - - originalFieldData = (originalFieldData && originalFieldData.content) || ''; - - return inputData[fieldName].join('##') != originalFieldData; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - originalContent.content = (offlineContent[''] && offlineContent[''].join('##')) || ''; - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/date/component/addon-mod-data-field-date.html b/src/addon/mod/data/fields/date/component/addon-mod-data-field-date.html deleted file mode 100644 index d225289e6..000000000 --- a/src/addon/mod/data/fields/date/component/addon-mod-data-field-date.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - {{ 'addon.mod_data.usedate' | translate }} - - - - - -{{ value.content * 1000 | coreFormatDate:'strftimedate' }} - diff --git a/src/addon/mod/data/fields/date/component/date.ts b/src/addon/mod/data/fields/date/component/date.ts deleted file mode 100644 index 0e4a3aa40..000000000 --- a/src/addon/mod/data/fields/date/component/date.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data date field. - */ -@Component({ - selector: 'addon-mod-data-field-date', - templateUrl: 'addon-mod-data-field-date.html' -}) -export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginComponent { - - format: string; - - constructor(protected fb: FormBuilder, protected timeUtils: CoreTimeUtilsProvider, protected translate: TranslateService) { - super(fb); - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.isShowOrListMode()) { - return; - } - - let val; - - // Calculate format to use. - this.format = this.timeUtils.fixFormatForDatetime(this.timeUtils.convertPHPToMoment( - this.translate.instant('core.strftimedate'))); - - if (this.mode == 'search') { - this.addControl('f_' + this.field.id + '_z'); - val = this.search['f_' + this.field.id + '_y'] ? new Date(this.search['f_' + this.field.id + '_y'] + '-' + - this.search['f_' + this.field.id + '_m'] + '-' + this.search['f_' + this.field.id + '_d']) : new Date(); - - this.search['f_' + this.field.id] = this.timeUtils.toDatetimeFormat(val.getTime()); - } else { - val = this.value && this.value.content ? new Date(parseInt(this.value.content, 10) * 1000) : new Date(); - val = this.timeUtils.toDatetimeFormat(val.getTime()); - } - - this.addControl('f_' + this.field.id, val); - } -} diff --git a/src/addon/mod/data/fields/date/date.module.ts b/src/addon/mod/data/fields/date/date.module.ts deleted file mode 100644 index 62936608c..000000000 --- a/src/addon/mod/data/fields/date/date.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldDateHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldDateComponent } from './component/date'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldDateComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule - ], - providers: [ - AddonModDataFieldDateHandler - ], - exports: [ - AddonModDataFieldDateComponent - ], - entryComponents: [ - AddonModDataFieldDateComponent - ] -}) -export class AddonModDataFieldDateModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldDateHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/date/providers/handler.ts b/src/addon/mod/data/fields/date/providers/handler.ts deleted file mode 100644 index be8a84350..000000000 --- a/src/addon/mod/data/fields/date/providers/handler.ts +++ /dev/null @@ -1,181 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataFieldDateComponent } from '../component/date'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; - -/** - * Handler for date data field plugin. - */ -@Injectable() -export class AddonModDataFieldDateHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldDateHandler'; - type = 'date'; - - constructor(private translate: TranslateService, private timeUtils: CoreTimeUtilsProvider) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldDateComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id, - enabledName = 'f_' + field.id + '_z'; - - if (inputData[enabledName] && typeof inputData[fieldName] == 'string') { - const values = [], - date = inputData[fieldName].substr(0, 10).split('-'), - year = date[0], - month = date[1], - day = date[2]; - values.push({ - name: fieldName + '_y', - value: year - }); - values.push({ - name: fieldName + '_m', - value: month - }); - values.push({ - name: fieldName + '_d', - value: day - }); - values.push({ - name: enabledName, - value: 1 - }); - - return values; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - - if (typeof inputData[fieldName] == 'string') { - const values = [], - date = inputData[fieldName].substr(0, 10).split('-'), - year = date[0], - month = date[1], - day = date[2]; - values.push({ - fieldid: field.id, - subfield: 'year', - value: year - }); - values.push({ - fieldid: field.id, - subfield: 'month', - value: month - }); - values.push({ - fieldid: field.id, - subfield: 'day', - value: day - }); - - return values; - } - - return false; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id, - input = inputData[fieldName] && inputData[fieldName].substr(0, 10) || ''; - - originalFieldData = (originalFieldData && originalFieldData.content && - this.timeUtils.toDatetimeFormat(originalFieldData.content * 1000).substr(0, 10)) || ''; - - return input != originalFieldData; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && - (!inputData || inputData.length < 2 || !inputData[0].value || !inputData[1].value || !inputData[2].value)) { - - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - let date = Date.UTC(offlineContent['year'] || '', offlineContent['month'] ? offlineContent['month'] - 1 : null, - offlineContent['day'] || null); - date = Math.floor(date / 1000); - - originalContent.content = date || ''; - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/field.module.ts b/src/addon/mod/data/fields/field.module.ts deleted file mode 100644 index 9a850d4f6..000000000 --- a/src/addon/mod/data/fields/field.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModDataFieldCheckboxModule } from './checkbox/checkbox.module'; -import { AddonModDataFieldDateModule } from './date/date.module'; -import { AddonModDataFieldFileModule } from './file/file.module'; -import { AddonModDataFieldLatlongModule } from './latlong/latlong.module'; -import { AddonModDataFieldMenuModule } from './menu/menu.module'; -import { AddonModDataFieldMultimenuModule } from './multimenu/multimenu.module'; -import { AddonModDataFieldNumberModule } from './number/number.module'; -import { AddonModDataFieldPictureModule } from './picture/picture.module'; -import { AddonModDataFieldRadiobuttonModule } from './radiobutton/radiobutton.module'; -import { AddonModDataFieldTextModule } from './text/text.module'; -import { AddonModDataFieldTextareaModule } from './textarea/textarea.module'; -import { AddonModDataFieldUrlModule } from './url/url.module'; - -@NgModule({ - declarations: [], - imports: [ - AddonModDataFieldCheckboxModule, - AddonModDataFieldDateModule, - AddonModDataFieldFileModule, - AddonModDataFieldLatlongModule, - AddonModDataFieldMenuModule, - AddonModDataFieldMultimenuModule, - AddonModDataFieldNumberModule, - AddonModDataFieldPictureModule, - AddonModDataFieldRadiobuttonModule, - AddonModDataFieldTextModule, - AddonModDataFieldTextareaModule, - AddonModDataFieldUrlModule, - ], - providers: [ - ], - exports: [] -}) -export class AddonModDataFieldModule { } diff --git a/src/addon/mod/data/fields/file/component/addon-mod-data-field-file.html b/src/addon/mod/data/fields/file/component/addon-mod-data-field-file.html deleted file mode 100644 index bb71fc2cd..000000000 --- a/src/addon/mod/data/fields/file/component/addon-mod-data-field-file.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - -
- -
-
diff --git a/src/addon/mod/data/fields/file/component/file.ts b/src/addon/mod/data/fields/file/component/file.ts deleted file mode 100644 index b223de280..000000000 --- a/src/addon/mod/data/fields/file/component/file.ts +++ /dev/null @@ -1,83 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { AddonModDataProvider } from '../../../providers/data'; - -/** - * Component to render data file field. - */ -@Component({ - selector: 'addon-mod-data-field-file', - templateUrl: 'addon-mod-data-field-file.html' -}) -export class AddonModDataFieldFileComponent extends AddonModDataFieldPluginComponent { - - files = []; - component: string; - componentId: number; - maxSizeBytes: number; - - constructor(protected fb: FormBuilder, private fileSessionprovider: CoreFileSessionProvider) { - super(fb); - } - - /** - * Get the files from the input value. - * - * @param value Input value. - * @return List of files. - */ - protected getFiles(value: any): any { - let files = (value && value.files) || []; - - // Reduce to first element. - if (files.length > 0) { - files = [files[0]]; - } - - return files; - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.mode != 'search') { - this.component = AddonModDataProvider.COMPONENT; - this.componentId = this.database.coursemodule; - - this.updateValue(this.value); - - if (this.mode == 'edit') { - this.maxSizeBytes = parseInt(this.field.param3, 10); - this.fileSessionprovider.setFiles(this.component, this.database.id + '_' + this.field.id, this.files); - } - } else { - this.addControl('f_' + this.field.id); - } - } - - /** - * Update value being shown. - * - * @param value New value to be set. - */ - protected updateValue(value: any): void { - this.value = value; - this.files = this.getFiles(value); - } -} diff --git a/src/addon/mod/data/fields/file/file.module.ts b/src/addon/mod/data/fields/file/file.module.ts deleted file mode 100644 index 4c2d0fdf3..000000000 --- a/src/addon/mod/data/fields/file/file.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldFileHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldFileComponent } from './component/file'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldFileComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - ], - providers: [ - AddonModDataFieldFileHandler - ], - exports: [ - AddonModDataFieldFileComponent - ], - entryComponents: [ - AddonModDataFieldFileComponent - ] -}) -export class AddonModDataFieldFileModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldFileHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/file/providers/handler.ts b/src/addon/mod/data/fields/file/providers/handler.ts deleted file mode 100644 index cd0c3f0f8..000000000 --- a/src/addon/mod/data/fields/file/providers/handler.ts +++ /dev/null @@ -1,154 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataProvider } from '../../../providers/data'; -import { AddonModDataFieldFileComponent } from '../component/file'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; - -/** - * Handler for file data field plugin. - */ -@Injectable() -export class AddonModDataFieldFileHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldFileHandler'; - type = 'file'; - - constructor(private translate: TranslateService, private fileSessionprovider: CoreFileSessionProvider, - private fileUploaderProvider: CoreFileUploaderProvider) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldFileComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id; - - if (inputData[fieldName]) { - return [{ - name: fieldName, - value: inputData[fieldName] - }]; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const files = this.getFieldEditFiles(field); - - return [{ - fieldid: field.id, - subfield: 'file', - files: files - }]; - } - - /** - * Get field edit files in the input data. - * - * @param field Defines the field.. - * @return With name and value of the data to be sent. - */ - getFieldEditFiles(field: any): any { - return this.fileSessionprovider.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id); - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const files = this.fileSessionprovider.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id) || []; - let originalFiles = (originalFieldData && originalFieldData.files) || []; - - if (originalFiles.length) { - originalFiles = [originalFiles[0]]; - } - - return this.fileUploaderProvider.areFileListDifferent(files, originalFiles); - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - if (offlineContent && offlineContent.file && offlineContent.file.offline > 0 && offlineFiles && offlineFiles.length > 0) { - originalContent.content = offlineFiles[0].filename; - originalContent.files = [offlineFiles[0]]; - } else if (offlineContent && offlineContent.file && offlineContent.file.online && offlineContent.file.online.length > 0) { - originalContent.content = offlineContent.file.online[0].filename; - originalContent.files = [offlineContent.file.online[0]]; - } - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/latlong/component/addon-mod-data-field-latlong.html b/src/addon/mod/data/fields/latlong/component/addon-mod-data-field-latlong.html deleted file mode 100644 index ba9a05723..000000000 --- a/src/addon/mod/data/fields/latlong/component/addon-mod-data-field-latlong.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - -
- - °N -
-
- - °E -
-
- -
- -
- - - - {{ formatLatLong(north, east) }} - \ No newline at end of file diff --git a/src/addon/mod/data/fields/latlong/component/latlong.ts b/src/addon/mod/data/fields/latlong/component/latlong.ts deleted file mode 100644 index f34e13102..000000000 --- a/src/addon/mod/data/fields/latlong/component/latlong.ts +++ /dev/null @@ -1,163 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; -import { CoreApp, CoreAppProvider } from '@providers/app'; -import { CoreGeolocation, CoreGeolocationError, CoreGeolocationErrorReason } from '@providers/geolocation'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Component to render data latlong field. - */ -@Component({ - selector: 'addon-mod-data-field-latlong', - templateUrl: 'addon-mod-data-field-latlong.html' -}) -export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginComponent { - - north: number; - east: number; - showGeolocation: boolean; - - constructor( - protected fb: FormBuilder, - protected domUtils: CoreDomUtilsProvider, - protected sanitizer: DomSanitizer, - appProvider: CoreAppProvider) { - super(fb); - - this.showGeolocation = !appProvider.isDesktop(); - } - - /** - * Format latitude and longitude in a simple text. - * - * @param north Degrees north. - * @param east Degrees East. - * @return Readable Latitude and logitude. - */ - formatLatLong(north: number, east: number): string { - if (north !== null || east !== null) { - const northFixed = north ? Math.abs(north).toFixed(4) : '0.0000', - eastFixed = east ? Math.abs(east).toFixed(4) : '0.0000'; - - return northFixed + (north < 0 ? '°S' : '°N') + ' ' + eastFixed + (east < 0 ? '°W' : '°E'); - } - } - - /** - * Get link to maps from latitude and longitude. - * - * @param north Degrees north. - * @param east Degrees East. - * @return Link to maps depending on platform. - */ - getLatLongLink(north: number, east: number): SafeUrl { - if (north !== null || east !== null) { - const northFixed = north ? north.toFixed(4) : '0.0000'; - const eastFixed = east ? east.toFixed(4) : '0.0000'; - let url; - - if (CoreApp.instance.isIOS()) { - url = 'http://maps.apple.com/?ll=' + northFixed + ',' + eastFixed + '&near=' + northFixed + ',' + eastFixed; - } else { - url = 'geo:' + northFixed + ',' + eastFixed; - } - - return this.sanitizer.bypassSecurityTrustUrl(url); - } - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.value) { - this.updateValue(this.value); - } - - if (this.mode == 'edit') { - this.addControl('f_' + this.field.id + '_0', this.north); - this.addControl('f_' + this.field.id + '_1', this.east); - } else if (this.mode == 'search') { - this.addControl('f_' + this.field.id); - } - } - - /** - * Update value being shown. - * - * @param value New value to be set. - */ - protected updateValue(value: any): void { - this.value = value; - this.north = (value && parseFloat(value.content)) || null; - this.east = (value && parseFloat(value.content1)) || null; - } - - /** - * Get user location. - * - * @param $event The event. - */ - async getLocation(event: Event): Promise { - event.preventDefault(); - - const modal = this.domUtils.showModalLoading('addon.mod_data.gettinglocation', true); - - try { - const coordinates = await CoreGeolocation.instance.getCoordinates(); - - this.form.controls['f_' + this.field.id + '_0'].setValue(coordinates.latitude); - this.form.controls['f_' + this.field.id + '_1'].setValue(coordinates.longitude); - } catch (error) { - this.showLocationErrorModal(error); - } - - modal.dismiss(); - } - - /** - * Show the appropriate error modal for the given error getting the location. - * - * @param error Location error. - */ - protected showLocationErrorModal(error: any): void { - if (error instanceof CoreGeolocationError) { - this.domUtils.showErrorModal(this.getGeolocationErrorMessage(error), true); - - return; - } - - this.domUtils.showErrorModalDefault(error, 'Error getting location'); - } - - /** - * Get error message from a geolocation error. - * - * @param error Geolocation error. - */ - protected getGeolocationErrorMessage(error: CoreGeolocationError): string { - // tslint:disable-next-line: switch-default - switch (error.reason) { - case CoreGeolocationErrorReason.PermissionDenied: - return 'addon.mod_data.locationpermissiondenied'; - case CoreGeolocationErrorReason.LocationNotEnabled: - return 'addon.mod_data.locationnotenabled'; - } - } - -} diff --git a/src/addon/mod/data/fields/latlong/latlong.module.ts b/src/addon/mod/data/fields/latlong/latlong.module.ts deleted file mode 100644 index cfc2af9f6..000000000 --- a/src/addon/mod/data/fields/latlong/latlong.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldLatlongHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldLatlongComponent } from './component/latlong'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldLatlongComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModDataFieldLatlongHandler - ], - exports: [ - AddonModDataFieldLatlongComponent - ], - entryComponents: [ - AddonModDataFieldLatlongComponent - ] -}) -export class AddonModDataFieldLatlongModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldLatlongHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/latlong/providers/handler.ts b/src/addon/mod/data/fields/latlong/providers/handler.ts deleted file mode 100644 index fb67ca9c6..000000000 --- a/src/addon/mod/data/fields/latlong/providers/handler.ts +++ /dev/null @@ -1,153 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataFieldLatlongComponent } from '../component/latlong'; - -/** - * Handler for latlong data field plugin. - */ -@Injectable() -export class AddonModDataFieldLatlongHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldLatlongHandler'; - type = 'latlong'; - - constructor(private translate: TranslateService) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldLatlongComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id; - - if (inputData[fieldName]) { - return [{ - name: fieldName, - value: inputData[fieldName] - }]; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - - return [ - { - fieldid: field.id, - subfield: '0', - value: inputData[fieldName + '_0'] || '' - }, - { - fieldid: field.id, - subfield: '1', - value: inputData[fieldName + '_1'] || '' - } - ]; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id, - lat = inputData[fieldName + '_0'] || '', - long = inputData[fieldName + '_1'] || '', - originalLat = (originalFieldData && originalFieldData.content) || '', - originalLong = (originalFieldData && originalFieldData.content1) || ''; - - return lat != originalLat || long != originalLong; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - let valueCount = 0; - - // The lat long class has two values that need to be checked. - inputData.forEach((value) => { - if (typeof value.value != 'undefined' && value.value != '') { - valueCount++; - } - }); - - // If we get here then only one field has been filled in. - if (valueCount == 1) { - return this.translate.instant('addon.mod_data.latlongboth'); - } else if (field.required && valueCount == 0) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - originalContent.content = offlineContent[0] || ''; - originalContent.content1 = offlineContent[1] || ''; - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/menu/component/addon-mod-data-field-menu.html b/src/addon/mod/data/fields/menu/component/addon-mod-data-field-menu.html deleted file mode 100644 index eb8fbe013..000000000 --- a/src/addon/mod/data/fields/menu/component/addon-mod-data-field-menu.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - {{ 'addon.mod_data.menuchoose' | translate }} - {{option}} - - - - -{{ value.content }} \ No newline at end of file diff --git a/src/addon/mod/data/fields/menu/component/menu.ts b/src/addon/mod/data/fields/menu/component/menu.ts deleted file mode 100644 index 462163521..000000000 --- a/src/addon/mod/data/fields/menu/component/menu.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data menu field. - */ -@Component({ - selector: 'addon-mod-data-field-menu', - templateUrl: 'addon-mod-data-field-menu.html' -}) -export class AddonModDataFieldMenuComponent extends AddonModDataFieldPluginComponent { - - options = []; - - constructor(protected fb: FormBuilder) { - super(fb); - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.isShowOrListMode()) { - return; - } - - this.options = this.field.param1.split('\n'); - - let val; - if (this.mode == 'edit' && this.value) { - val = this.value.content; - } - - this.addControl('f_' + this.field.id, val); - } -} diff --git a/src/addon/mod/data/fields/menu/menu.module.ts b/src/addon/mod/data/fields/menu/menu.module.ts deleted file mode 100644 index f2a839e03..000000000 --- a/src/addon/mod/data/fields/menu/menu.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldMenuHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldMenuComponent } from './component/menu'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldMenuComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModDataFieldMenuHandler - ], - exports: [ - AddonModDataFieldMenuComponent - ], - entryComponents: [ - AddonModDataFieldMenuComponent - ] -}) -export class AddonModDataFieldMenuModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldMenuHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/menu/providers/handler.ts b/src/addon/mod/data/fields/menu/providers/handler.ts deleted file mode 100644 index 104559f66..000000000 --- a/src/addon/mod/data/fields/menu/providers/handler.ts +++ /dev/null @@ -1,133 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataFieldMenuComponent } from '../component/menu'; - -/** - * Handler for menu data field plugin. - */ -@Injectable() -export class AddonModDataFieldMenuHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldMenuHandler'; - type = 'menu'; - - constructor(private translate: TranslateService) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldMenuComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id; - if (inputData[fieldName]) { - return [{ - name: fieldName, - value: inputData[fieldName] - }]; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - - if (inputData[fieldName]) { - return [{ - fieldid: field.id, - value: inputData[fieldName] - }]; - } - - return false; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id, - input = inputData[fieldName] || ''; - originalFieldData = (originalFieldData && originalFieldData.content) || ''; - - return input != originalFieldData; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - originalContent.content = offlineContent[''] || ''; - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/multimenu/component/addon-mod-data-field-multimenu.html b/src/addon/mod/data/fields/multimenu/component/addon-mod-data-field-multimenu.html deleted file mode 100644 index ea3cd13df..000000000 --- a/src/addon/mod/data/fields/multimenu/component/addon-mod-data-field-multimenu.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - {{option.key}} - - - - - - {{ 'addon.mod_data.selectedrequired' | translate }} - - - - - - \ No newline at end of file diff --git a/src/addon/mod/data/fields/multimenu/component/multimenu.ts b/src/addon/mod/data/fields/multimenu/component/multimenu.ts deleted file mode 100644 index 1373015b0..000000000 --- a/src/addon/mod/data/fields/multimenu/component/multimenu.ts +++ /dev/null @@ -1,73 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data multimenu field. - */ -@Component({ - selector: 'addon-mod-data-field-multimenu', - templateUrl: 'addon-mod-data-field-multimenu.html' -}) -export class AddonModDataFieldMultimenuComponent extends AddonModDataFieldPluginComponent { - - options = []; - - constructor(protected fb: FormBuilder) { - super(fb); - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.isShowOrListMode()) { - this.updateValue(this.value); - - return; - } - - this.options = this.field.param1.split(/\r?\n/).map((option) => { - return { key: option, value: option }; - }); - - const values = []; - if (this.mode == 'edit' && this.value && this.value.content) { - this.value.content.split('##').forEach((value) => { - const x = this.options.findIndex((option) => value == option.key); - if (x >= 0) { - values.push(value); - } - }); - } - - if (this.mode == 'search') { - this.addControl('f_' + this.field.id + '_allreq'); - } - - this.addControl('f_' + this.field.id, values); - } - - /** - * Update value being shown. - * - * @param value New value to be set. - */ - protected updateValue(value: any): void { - this.value = value || {}; - this.value.content = value && value.content && value.content.split('##').join('
'); - } -} diff --git a/src/addon/mod/data/fields/multimenu/multimenu.module.ts b/src/addon/mod/data/fields/multimenu/multimenu.module.ts deleted file mode 100644 index e2bdf031e..000000000 --- a/src/addon/mod/data/fields/multimenu/multimenu.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldMultimenuHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldMultimenuComponent } from './component/multimenu'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldMultimenuComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModDataFieldMultimenuHandler - ], - exports: [ - AddonModDataFieldMultimenuComponent - ], - entryComponents: [ - AddonModDataFieldMultimenuComponent - ] -}) -export class AddonModDataFieldMultimenuModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldMultimenuHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/multimenu/providers/handler.ts b/src/addon/mod/data/fields/multimenu/providers/handler.ts deleted file mode 100644 index 1a7e1ce82..000000000 --- a/src/addon/mod/data/fields/multimenu/providers/handler.ts +++ /dev/null @@ -1,142 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataFieldMultimenuComponent } from '../component/multimenu'; - -/** - * Handler for multimenu data field plugin. - */ -@Injectable() -export class AddonModDataFieldMultimenuHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldMultimenuHandler'; - type = 'multimenu'; - - constructor(private translate: TranslateService) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldMultimenuComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id, - reqName = 'f_' + field.id + '_allreq'; - - if (inputData[fieldName] && inputData[fieldName].length > 0) { - const values = []; - - values.push({ - name: fieldName, - value: inputData[fieldName] - }); - - if (inputData[reqName]) { - values.push({ - name: reqName, - value: true - }); - } - - return values; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - - return [{ - fieldid: field.id, - value: inputData[fieldName] || [] - }]; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id; - - originalFieldData = (originalFieldData && originalFieldData.content) || ''; - - return inputData[fieldName].join('##') != originalFieldData; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - originalContent.content = (offlineContent[''] && offlineContent[''].join('##')) || ''; - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/number/component/addon-mod-data-field-number.html b/src/addon/mod/data/fields/number/component/addon-mod-data-field-number.html deleted file mode 100644 index 2e02d9fd5..000000000 --- a/src/addon/mod/data/fields/number/component/addon-mod-data-field-number.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - -{{ value.content }} diff --git a/src/addon/mod/data/fields/number/component/number.ts b/src/addon/mod/data/fields/number/component/number.ts deleted file mode 100644 index e43c3f43e..000000000 --- a/src/addon/mod/data/fields/number/component/number.ts +++ /dev/null @@ -1,47 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data number field. - */ -@Component({ - selector: 'addon-mod-data-field-number', - templateUrl: 'addon-mod-data-field-number.html' -}) -export class AddonModDataFieldNumberComponent extends AddonModDataFieldPluginComponent{ - - constructor(protected fb: FormBuilder) { - super(fb); - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.isShowOrListMode()) { - return; - } - - let value; - if (this.mode == 'edit' && this.value) { - const v = parseFloat(this.value.content); - value = isNaN(v) ? '' : v; - } - - this.addControl('f_' + this.field.id, value); - } -} diff --git a/src/addon/mod/data/fields/number/number.module.ts b/src/addon/mod/data/fields/number/number.module.ts deleted file mode 100644 index 69fa463d7..000000000 --- a/src/addon/mod/data/fields/number/number.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldNumberHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldNumberComponent } from './component/number'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldNumberComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModDataFieldNumberHandler - ], - exports: [ - AddonModDataFieldNumberComponent - ], - entryComponents: [ - AddonModDataFieldNumberComponent - ] -}) -export class AddonModDataFieldNumberModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldNumberHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/number/providers/handler.ts b/src/addon/mod/data/fields/number/providers/handler.ts deleted file mode 100644 index a9a57d217..000000000 --- a/src/addon/mod/data/fields/number/providers/handler.ts +++ /dev/null @@ -1,73 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldTextHandler } from '../../text/providers/handler'; -import { AddonModDataFieldNumberComponent } from '../component/number'; - -/** - * Handler for number data field plugin. - */ -@Injectable() -export class AddonModDataFieldNumberHandler extends AddonModDataFieldTextHandler { - name = 'AddonModDataFieldNumberHandler'; - type = 'number'; - - constructor(protected translate: TranslateService) { - super(translate); - } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldNumberComponent; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id; - const input = inputData[fieldName] || ''; - originalFieldData = originalFieldData && originalFieldData.content || ''; - - return input != originalFieldData; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && (!inputData || !inputData.length || inputData[0].value == '')) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } -} diff --git a/src/addon/mod/data/fields/picture/component/addon-mod-data-field-picture.html b/src/addon/mod/data/fields/picture/component/addon-mod-data-field-picture.html deleted file mode 100644 index c5a4a560e..000000000 --- a/src/addon/mod/data/fields/picture/component/addon-mod-data-field-picture.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - {{ 'addon.mod_data.alttext' | translate }} - - - - - - - - - - diff --git a/src/addon/mod/data/fields/picture/component/picture.ts b/src/addon/mod/data/fields/picture/component/picture.ts deleted file mode 100644 index 570a706dc..000000000 --- a/src/addon/mod/data/fields/picture/component/picture.ts +++ /dev/null @@ -1,137 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; -import { AddonModDataProvider } from '../../../providers/data'; - -/** - * Component to render data picture field. - */ -@Component({ - selector: 'addon-mod-data-field-picture', - templateUrl: 'addon-mod-data-field-picture.html' -}) -export class AddonModDataFieldPictureComponent extends AddonModDataFieldPluginComponent { - - files = []; - component: string; - componentId: number; - maxSizeBytes: number; - - image: any; - entryId: number; - imageUrl: string; - title: string; - width: string; - height: string; - - constructor(protected fb: FormBuilder, private fileSessionprovider: CoreFileSessionProvider, - private domUtils: CoreDomUtilsProvider) { - super(fb); - } - - /** - * Get the files from the input value. - * - * @param value Input value. - * @return List of files. - */ - protected getFiles(value: any): any { - let files = (value && value.files) || []; - - // Reduce to first element. - if (files.length > 0) { - files = [files[0]]; - } - - return files; - } - - /** - * Find file in a list. - * - * @param files File list where to search. - * @param filenameSeek Filename to search. - * @return File found or false. - */ - protected findFile(files: any[], filenameSeek: string): any { - return files.find((file) => file.filename == filenameSeek) || false; - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.mode != 'search') { - this.component = AddonModDataProvider.COMPONENT; - this.componentId = this.database.coursemodule; - - this.updateValue(this.value); - - if (this.mode == 'edit') { - this.maxSizeBytes = parseInt(this.field.param3, 10); - this.fileSessionprovider.setFiles(this.component, this.database.id + '_' + this.field.id, this.files); - - const alttext = (this.value && this.value.content1) || ''; - this.addControl('f_' + this.field.id + '_alttext', alttext); - } - } else { - this.addControl('f_' + this.field.id); - } - } - - /** - * Update value being shown. - * - * @param value New value to be set. - */ - protected updateValue(value: any): void { - this.value = value; - - // Edit mode, the list shouldn't change so there is no need to watch it. - const files = value && value.files || []; - - // Get image or thumb. - if (files.length > 0) { - const filenameSeek = this.mode == 'list' ? 'thumb_' + value.content : value.content; - this.image = this.findFile(files, filenameSeek); - - if (!this.image && this.mode == 'list') { - this.image = this.findFile(files, value.content); - } - - this.files = [this.image]; - } else { - this.image = false; - this.files = []; - } - - if (this.mode != 'edit') { - this.entryId = (value && value.recordid) || null; - this.title = (value && value.content1) || ''; - this.imageUrl = null; - setTimeout(() => { - if (this.image) { - this.imageUrl = this.image.offline ? this.image.toURL() : this.image.fileurl; - } - }, 1); - - this.width = this.domUtils.formatPixelsSize(this.field.param1); - this.height = this.domUtils.formatPixelsSize(this.field.param2); - } - } -} diff --git a/src/addon/mod/data/fields/picture/picture.module.ts b/src/addon/mod/data/fields/picture/picture.module.ts deleted file mode 100644 index ba85a3350..000000000 --- a/src/addon/mod/data/fields/picture/picture.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldPictureHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldPictureComponent } from './component/picture'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldPictureComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - ], - providers: [ - AddonModDataFieldPictureHandler - ], - exports: [ - AddonModDataFieldPictureComponent - ], - entryComponents: [ - AddonModDataFieldPictureComponent - ] -}) -export class AddonModDataFieldPictureModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldPictureHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/picture/providers/handler.ts b/src/addon/mod/data/fields/picture/providers/handler.ts deleted file mode 100644 index 1ccbbd46c..000000000 --- a/src/addon/mod/data/fields/picture/providers/handler.ts +++ /dev/null @@ -1,188 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataProvider } from '../../../providers/data'; -import { AddonModDataFieldPictureComponent } from '../component/picture'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; - -/** - * Handler for picture data field plugin. - */ -@Injectable() -export class AddonModDataFieldPictureHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldPictureHandler'; - type = 'picture'; - - constructor(private translate: TranslateService, private fileSessionprovider: CoreFileSessionProvider, - private fileUploaderProvider: CoreFileUploaderProvider) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldPictureComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id; - - if (inputData[fieldName]) { - return [{ - name: fieldName, - value: inputData[fieldName] - }]; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const files = this.getFieldEditFiles(field); - const fieldName = 'f_' + field.id + '_alttext'; - - return [ - { - fieldid: field.id, - subfield: 'file', - files: files - }, - { - fieldid: field.id, - subfield: 'alttext', - value: inputData[fieldName] - } - ]; - } - - /** - * Get field edit files in the input data. - * - * @param field Defines the field.. - * @return With name and value of the data to be sent. - */ - getFieldEditFiles(field: any): any { - return this.fileSessionprovider.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id); - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id + '_alttext', - altText = inputData[fieldName] || '', - originalAltText = (originalFieldData && originalFieldData.content1) || '', - files = this.getFieldEditFiles(field) || []; - let originalFiles = (originalFieldData && originalFieldData.files) || []; - - // Get image. - if (originalFiles.length > 0) { - const filenameSeek = (originalFieldData && originalFieldData.content) || '', - file = originalFiles.find((file) => file.filename == filenameSeek); - if (file) { - originalFiles = [file]; - } - } else { - originalFiles = []; - } - - return altText != originalAltText || this.fileUploaderProvider.areFileListDifferent(files, originalFiles); - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required) { - if (!inputData || !inputData.length) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - const found = inputData.some((input) => { - if (typeof input.subfield != 'undefined' && input.subfield == 'file') { - return !!input.value; - } - - return false; - }); - - if (!found) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - if (offlineContent && offlineContent.file && offlineContent.file.offline > 0 && offlineFiles && offlineFiles.length > 0) { - originalContent.content = offlineFiles[0].filename; - originalContent.files = [offlineFiles[0]]; - } else if (offlineContent && offlineContent.file && offlineContent.file.online && offlineContent.file.online.length > 0) { - originalContent.content = offlineContent.file.online[0].filename; - originalContent.files = [offlineContent.file.online[0]]; - } - - originalContent.content1 = offlineContent.alttext || ''; - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/radiobutton/component/addon-mod-data-field-radiobutton.html b/src/addon/mod/data/fields/radiobutton/component/addon-mod-data-field-radiobutton.html deleted file mode 100644 index eb8fbe013..000000000 --- a/src/addon/mod/data/fields/radiobutton/component/addon-mod-data-field-radiobutton.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - {{ 'addon.mod_data.menuchoose' | translate }} - {{option}} - - - - -{{ value.content }} \ No newline at end of file diff --git a/src/addon/mod/data/fields/radiobutton/component/radiobutton.ts b/src/addon/mod/data/fields/radiobutton/component/radiobutton.ts deleted file mode 100644 index d4be0615d..000000000 --- a/src/addon/mod/data/fields/radiobutton/component/radiobutton.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data radiobutton field. - */ -@Component({ - selector: 'addon-mod-data-field-radiobutton', - templateUrl: 'addon-mod-data-field-radiobutton.html' -}) -export class AddonModDataFieldRadiobuttonComponent extends AddonModDataFieldPluginComponent { - - options = []; - - constructor(protected fb: FormBuilder) { - super(fb); - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.isShowOrListMode()) { - return; - } - - this.options = this.field.param1.split('\n'); - - let val; - if (this.mode == 'edit' && this.value) { - val = this.value.content; - } - - this.addControl('f_' + this.field.id, val); - } -} diff --git a/src/addon/mod/data/fields/radiobutton/providers/handler.ts b/src/addon/mod/data/fields/radiobutton/providers/handler.ts deleted file mode 100644 index 1f9fc210a..000000000 --- a/src/addon/mod/data/fields/radiobutton/providers/handler.ts +++ /dev/null @@ -1,129 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataFieldRadiobuttonComponent } from '../component/radiobutton'; - -/** - * Handler for checkbox data field plugin. - */ -@Injectable() -export class AddonModDataFieldRadiobuttonHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldRadiobuttonHandler'; - type = 'radiobutton'; - - constructor(private translate: TranslateService) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldRadiobuttonComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id; - if (inputData[fieldName]) { - return [{ - name: fieldName, - value: inputData[fieldName] - }]; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - - return [{ - fieldid: field.id, - value: inputData[fieldName] || '' - }]; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id, - input = inputData[fieldName] || ''; - originalFieldData = (originalFieldData && originalFieldData.content) || ''; - - return input != originalFieldData; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - originalContent.content = offlineContent[''] || ''; - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/radiobutton/radiobutton.module.ts b/src/addon/mod/data/fields/radiobutton/radiobutton.module.ts deleted file mode 100644 index 877e490f5..000000000 --- a/src/addon/mod/data/fields/radiobutton/radiobutton.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldRadiobuttonHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldRadiobuttonComponent } from './component/radiobutton'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldRadiobuttonComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModDataFieldRadiobuttonHandler - ], - exports: [ - AddonModDataFieldRadiobuttonComponent - ], - entryComponents: [ - AddonModDataFieldRadiobuttonComponent - ] -}) -export class AddonModDataFieldRadiobuttonModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldRadiobuttonHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/text/component/addon-mod-data-field-text.html b/src/addon/mod/data/fields/text/component/addon-mod-data-field-text.html deleted file mode 100644 index 9af142d7c..000000000 --- a/src/addon/mod/data/fields/text/component/addon-mod-data-field-text.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - -{{ value.content }} \ No newline at end of file diff --git a/src/addon/mod/data/fields/text/component/text.ts b/src/addon/mod/data/fields/text/component/text.ts deleted file mode 100644 index 960759409..000000000 --- a/src/addon/mod/data/fields/text/component/text.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data text field. - */ -@Component({ - selector: 'addon-mod-data-field-text', - templateUrl: 'addon-mod-data-field-text.html' -}) -export class AddonModDataFieldTextComponent extends AddonModDataFieldPluginComponent { - - constructor(protected fb: FormBuilder) { - super(fb); - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.isShowOrListMode()) { - return; - } - - let value; - if (this.mode == 'edit' && this.value) { - value = this.value.content; - } - - this.addControl('f_' + this.field.id, value); - } -} diff --git a/src/addon/mod/data/fields/text/providers/handler.ts b/src/addon/mod/data/fields/text/providers/handler.ts deleted file mode 100644 index 5e54570fd..000000000 --- a/src/addon/mod/data/fields/text/providers/handler.ts +++ /dev/null @@ -1,130 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; -import { AddonModDataFieldTextComponent } from '../component/text'; - -/** - * Handler for number data field plugin. - */ -@Injectable() -export class AddonModDataFieldTextHandler implements AddonModDataFieldHandler { - name = 'AddonModDataFieldTextHandler'; - type = 'text'; - - constructor(protected translate: TranslateService) { } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldTextComponent; - } - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - const fieldName = 'f_' + field.id; - - if (inputData[fieldName]) { - return [{ - name: fieldName, - value: inputData[fieldName] - }]; - } - - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - - return [{ - fieldid: field.id, - value: inputData[fieldName] || '' - }]; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - const fieldName = 'f_' + field.id, - input = inputData[fieldName] || ''; - originalFieldData = (originalFieldData && originalFieldData.content) || ''; - - return input != originalFieldData; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - originalContent.content = offlineContent[''] || ''; - - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/fields/text/text.module.ts b/src/addon/mod/data/fields/text/text.module.ts deleted file mode 100644 index 9f288d343..000000000 --- a/src/addon/mod/data/fields/text/text.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldTextHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldTextComponent } from './component/text'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldTextComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModDataFieldTextHandler - ], - exports: [ - AddonModDataFieldTextComponent - ], - entryComponents: [ - AddonModDataFieldTextComponent - ] -}) -export class AddonModDataFieldTextModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldTextHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html b/src/addon/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html deleted file mode 100644 index 680b92f89..000000000 --- a/src/addon/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/addon/mod/data/fields/textarea/component/textarea.ts b/src/addon/mod/data/fields/textarea/component/textarea.ts deleted file mode 100644 index d0d03359a..000000000 --- a/src/addon/mod/data/fields/textarea/component/textarea.ts +++ /dev/null @@ -1,68 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { AddonModDataProvider } from '../../../providers/data'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data number field. - */ -@Component({ - selector: 'addon-mod-data-field-textarea', - templateUrl: 'addon-mod-data-field-textarea.html' -}) -export class AddonModDataFieldTextareaComponent extends AddonModDataFieldPluginComponent { - - component: string; - componentId: number; - - constructor(protected fb: FormBuilder, protected textUtils: CoreTextUtilsProvider) { - super(fb); - } - - /** - * Format value to be shown. Replacing plugin file Urls. - * - * @param value Value to replace. - * @return Replaced string to be rendered. - */ - format(value: any): string { - const files = (value && value.files) || []; - - return value ? this.textUtils.replacePluginfileUrls(value.content, files) : ''; - } - - /** - * Initialize field. - */ - protected init(): void { - this.component = AddonModDataProvider.COMPONENT; - this.componentId = this.database.coursemodule; - - if (this.isShowOrListMode()) { - return; - } - - let text; - // Check if rich text editor is enabled. - if (this.mode == 'edit') { - const files = (this.value && this.value.files) || []; - text = this.value ? this.textUtils.replacePluginfileUrls(this.value.content, files) : ''; - } - - this.addControl('f_' + this.field.id, text); - } -} diff --git a/src/addon/mod/data/fields/textarea/providers/handler.ts b/src/addon/mod/data/fields/textarea/providers/handler.ts deleted file mode 100644 index 206a31b27..000000000 --- a/src/addon/mod/data/fields/textarea/providers/handler.ts +++ /dev/null @@ -1,133 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldTextHandler } from '../../text/providers/handler'; -import { AddonModDataFieldTextareaComponent } from '../component/textarea'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Handler for textarea data field plugin. - */ -@Injectable() -export class AddonModDataFieldTextareaHandler extends AddonModDataFieldTextHandler { - name = 'AddonModDataFieldTextareaHandler'; - type = 'textarea'; - - constructor(protected translate: TranslateService, private textUtils: CoreTextUtilsProvider) { - super(translate); - } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldTextareaComponent; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - const files = this.getFieldEditFiles(field, inputData, originalFieldData); - let text = this.textUtils.restorePluginfileUrls(inputData[fieldName] || '', files); - // Add some HTML to the text if needed. - text = this.textUtils.formatHtmlLines(text); - - // WS does not properly check if HTML content is blank when the field is required. - if (this.textUtils.htmlIsBlank(text)) { - text = ''; - } - - return [ - { - fieldid: field.id, - value: text - }, - { - fieldid: field.id, - subfield: 'content1', - value: 1 - }, - { - fieldid: field.id, - subfield: 'itemid', - files: files - } - ]; - } - - /** - * Get field edit files in the input data. - * - * @param field Defines the field.. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return With name and value of the data to be sent. - */ - getFieldEditFiles(field: any, inputData: any, originalFieldData: any): any { - return (originalFieldData && originalFieldData.files) || []; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required) { - if (!inputData || !inputData.length) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - const value = inputData.find((value) => value.subfield == ''); - - if (!value || this.textUtils.htmlIsBlank(value.value)) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - } - - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - originalContent.content = offlineContent[''] || ''; - if (originalContent.content.length > 0 && originalContent.files && originalContent.files.length > 0) { - // Take the original files since we cannot edit them on the app. - originalContent.content = this.textUtils.replacePluginfileUrls(originalContent.content, originalContent.files); - } - - return originalContent; - } -} diff --git a/src/addon/mod/data/fields/textarea/textarea.module.ts b/src/addon/mod/data/fields/textarea/textarea.module.ts deleted file mode 100644 index ead1184b6..000000000 --- a/src/addon/mod/data/fields/textarea/textarea.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldTextareaHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldTextareaComponent } from './component/textarea'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldTextareaComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - ], - providers: [ - AddonModDataFieldTextareaHandler - ], - exports: [ - AddonModDataFieldTextareaComponent - ], - entryComponents: [ - AddonModDataFieldTextareaComponent - ] -}) -export class AddonModDataFieldTextareaModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldTextareaHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/fields/url/component/addon-mod-data-field-url.html b/src/addon/mod/data/fields/url/component/addon-mod-data-field-url.html deleted file mode 100644 index 7d2c37bec..000000000 --- a/src/addon/mod/data/fields/url/component/addon-mod-data-field-url.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - {{ displayValue }} - {{ displayValue }} - \ No newline at end of file diff --git a/src/addon/mod/data/fields/url/component/url.ts b/src/addon/mod/data/fields/url/component/url.ts deleted file mode 100644 index 175f12096..000000000 --- a/src/addon/mod/data/fields/url/component/url.ts +++ /dev/null @@ -1,81 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Component } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; - -/** - * Component to render data url field. - */ -@Component({ - selector: 'addon-mod-data-field-url', - templateUrl: 'addon-mod-data-field-url.html' -}) -export class AddonModDataFieldUrlComponent extends AddonModDataFieldPluginComponent { - - protected autoLink = false; - protected displayValue = ''; - - constructor(protected fb: FormBuilder) { - super(fb); - } - - /** - * Initialize field. - */ - protected init(): void { - if (this.isShowOrListMode()) { - return; - } - - let value; - if (this.mode == 'edit' && this.value) { - value = this.value.content; - } - - this.addControl('f_' + this.field.id, value); - } - - /** - * Calculate data for show or list mode. - */ - protected calculateShowListData(): void { - if (!this.value || !this.value.content) { - return; - } - - const url = this.value.content; - const text = this.field.param2 || this.value.content1; // Param2 forces the text to display. - - this.autoLink = parseInt(this.field.param1, 10) === 1; - - if (this.autoLink) { - this.displayValue = text || url; - } else { - // No auto link, always display the URL. - this.displayValue = url; - } - } - - /** - * Update value being shown. - */ - protected updateValue(value: any): void { - super.updateValue(value); - - if (this.isShowOrListMode()) { - this.calculateShowListData(); - } - } -} diff --git a/src/addon/mod/data/fields/url/providers/handler.ts b/src/addon/mod/data/fields/url/providers/handler.ts deleted file mode 100644 index d6da62ad0..000000000 --- a/src/addon/mod/data/fields/url/providers/handler.ts +++ /dev/null @@ -1,76 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injector, Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataFieldTextHandler } from '../../text/providers/handler'; -import { AddonModDataFieldUrlComponent } from '../component/url'; - -/** - * Handler for url data field plugin. - */ -@Injectable() -export class AddonModDataFieldUrlHandler extends AddonModDataFieldTextHandler { - name = 'AddonModDataFieldUrlHandler'; - type = 'url'; - - constructor(protected translate: TranslateService) { - super(translate); - } - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, plugin: any): any | Promise { - return AddonModDataFieldUrlComponent; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - const fieldName = 'f_' + field.id; - - return [ - { - fieldid: field.id, - subfield: '0', - value: (inputData[fieldName] && inputData[fieldName].trim()) || '' - } - ]; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { - return this.translate.instant('addon.mod_data.errormustsupplyvalue'); - } - - return false; - } -} diff --git a/src/addon/mod/data/fields/url/url.module.ts b/src/addon/mod/data/fields/url/url.module.ts deleted file mode 100644 index e92cf2fb5..000000000 --- a/src/addon/mod/data/fields/url/url.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModDataFieldUrlHandler } from './providers/handler'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataFieldUrlComponent } from './component/url'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModDataFieldUrlComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModDataFieldUrlHandler - ], - exports: [ - AddonModDataFieldUrlComponent - ], - entryComponents: [ - AddonModDataFieldUrlComponent - ] -}) -export class AddonModDataFieldUrlModule { - constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldUrlHandler) { - fieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/data/lang/en.json b/src/addon/mod/data/lang/en.json deleted file mode 100644 index 106896f4f..000000000 --- a/src/addon/mod/data/lang/en.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "addentries": "Add entries", - "advancedsearch": "Advanced search", - "alttext": "Alternative text", - "approve": "Approve", - "approved": "Approved", - "ascending": "Ascending", - "authorfirstname": "Author first name", - "authorlastname": "Author surname", - "confirmdeleterecord": "Are you sure you want to delete this entry?", - "descending": "Descending", - "disapprove": "Undo approval", - "edittagsnotsupported": "Sorry, editing tags is not supported by the app.", - "emptyaddform": "You did not fill out any fields!", - "entrieslefttoadd": "You must add {{$a.entriesleft}} more entry/entries in order to complete this activity", - "entrieslefttoaddtoview": "You must add {{$a.entrieslefttoview}} more entry/entries before you can view other participants' entries.", - "errorapproving": "Error approving or unapproving entry.", - "errordeleting": "Error deleting entry.", - "errormustsupplyvalue": "You must supply a value here.", - "expired": "Sorry, this activity closed on {{$a}} and is no longer available", - "fields": "Fields", - "foundrecords": "Found records: {{$a.num}}/{{$a.max}} (Reset filters)", - "gettinglocation": "Getting location", - "latlongboth": "Both latitude and longitude are required.", - "locationpermissiondenied": "Permission to access your location has been denied.", - "locationnotenabled": "Location is not enabled", - "menuchoose": "Choose...", - "modulenameplural": "Databases", - "more": "More", - "mylocation": "My location", - "nomatch": "No matching entries found!", - "norecords": "No entries in database", - "notapproved": "Entry is not approved yet.", - "notopenyet": "Sorry, this activity is not available until {{$a}}", - "numrecords": "{{$a}} entries", - "other": "Other", - "recordapproved": "Entry approved", - "recorddeleted": "Entry deleted", - "recorddisapproved": "Entry unapproved", - "resetsettings": "Reset filters", - "search": "Search", - "searchbytagsnotsupported": "Sorry, searching by tags is not supported by the app.", - "selectedrequired": "All selected required", - "single": "View single", - "tagarea_data_records": "Data records", - "timeadded": "Time added", - "timemodified": "Time modified", - "usedate": "Include in search." -} \ No newline at end of file diff --git a/src/addon/mod/data/pages/edit/edit.html b/src/addon/mod/data/pages/edit/edit.html deleted file mode 100644 index 7b5418da5..000000000 --- a/src/addon/mod/data/pages/edit/edit.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - -
- - -
- -
-
-
-
diff --git a/src/addon/mod/data/pages/edit/edit.module.ts b/src/addon/mod/data/pages/edit/edit.module.ts deleted file mode 100644 index 1cb6313f3..000000000 --- a/src/addon/mod/data/pages/edit/edit.module.ts +++ /dev/null @@ -1,39 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreCommentsComponentsModule } from '@core/comments/components/components.module'; -import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module'; -import { AddonModDataComponentsModule } from '../../components/components.module'; -import { AddonModDataEditPage } from './edit'; - -@NgModule({ - declarations: [ - AddonModDataEditPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - AddonModDataComponentsModule, - CoreCompileHtmlComponentModule, - CoreCommentsComponentsModule, - IonicPageModule.forChild(AddonModDataEditPage), - TranslateModule.forChild() - ], -}) -export class AddonModDataEditPageModule {} diff --git a/src/addon/mod/data/pages/edit/edit.ts b/src/addon/mod/data/pages/edit/edit.ts deleted file mode 100644 index b3542eebf..000000000 --- a/src/addon/mod/data/pages/edit/edit.ts +++ /dev/null @@ -1,346 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, ElementRef } from '@angular/core'; -import { Content, IonicPage, NavParams, NavController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { FormGroup } from '@angular/forms'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModDataProvider } from '../../providers/data'; -import { AddonModDataHelperProvider } from '../../providers/helper'; -import { AddonModDataOfflineProvider } from '../../providers/offline'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataComponentsModule } from '../../components/components.module'; -import { CoreTagProvider } from '@core/tag/providers/tag'; - -/** - * Page that displays the view edit page. - */ -@IonicPage({ segment: 'addon-mod-data-edit' }) -@Component({ - selector: 'page-addon-mod-data-edit', - templateUrl: 'edit.html', -}) -export class AddonModDataEditPage { - @ViewChild(Content) content: Content; - @ViewChild('editFormEl') formElement: ElementRef; - - protected module: any; - protected courseId: number; - protected data: any; - protected entryId: number; - protected entry: any; - protected fields = {}; - protected fieldsArray = []; - protected siteId: string; - protected offline: boolean; - protected forceLeave = false; // To allow leaving the page without checking for changes. - - title = ''; - component = AddonModDataProvider.COMPONENT; - loaded = false; - selectedGroup = 0; - cssClass = ''; - groupInfo: any; - editFormRender = ''; - editForm: FormGroup; - extraImports = [AddonModDataComponentsModule]; - jsData: any; - errors = {}; - - constructor(params: NavParams, protected utils: CoreUtilsProvider, protected groupsProvider: CoreGroupsProvider, - protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate, - protected courseProvider: CoreCourseProvider, protected dataProvider: AddonModDataProvider, - protected dataOffline: AddonModDataOfflineProvider, protected dataHelper: AddonModDataHelperProvider, - sitesProvider: CoreSitesProvider, protected navCtrl: NavController, protected translate: TranslateService, - protected eventsProvider: CoreEventsProvider, protected fileUploaderProvider: CoreFileUploaderProvider, - private tagProvider: CoreTagProvider) { - this.module = params.get('module') || {}; - this.entryId = params.get('entryId') || null; - this.courseId = params.get('courseId'); - this.selectedGroup = params.get('group') || 0; - - this.siteId = sitesProvider.getCurrentSiteId(); - - this.title = this.module.name; - - this.editForm = new FormGroup({}); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchEntryData(); - } - - /** - * Check if we can leave the page or not and ask to confirm the lost of data. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave || !this.entry) { - return; - } - - const inputData = this.editForm.value; - - const changed = await this.dataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.data.id, this.entry.contents); - - if (changed) { - // Show confirmation if some data has been modified. - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - // Delete the local files from the tmp folder. - const files = await this.dataHelper.getEditTmpFiles(inputData, this.fieldsArray, this.data.id, this.entry.contents); - this.fileUploaderProvider.clearTmpFiles(files); - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.siteId); - } - - /** - * Fetch the entry data. - * - * @return Resolved when done. - */ - protected fetchEntryData(): Promise { - return this.dataProvider.getDatabase(this.courseId, this.module.id).then((data) => { - this.title = data.name || this.title; - this.data = data; - this.cssClass = 'addon-data-entries-' + data.id; - - return this.dataProvider.getDatabaseAccessInformation(data.id, {cmId: this.module.id}); - }).then((accessData) => { - if (this.entryId) { - return this.groupsProvider.getActivityGroupInfo(this.data.coursemodule).then((groupInfo) => { - this.groupInfo = groupInfo; - this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, groupInfo); - }); - } - }).then(() => { - return this.dataProvider.getFields(this.data.id, {cmId: this.module.id}); - }).then((fieldsData) => { - this.fieldsArray = fieldsData; - this.fields = this.utils.arrayToObject(fieldsData, 'id'); - - return this.dataHelper.fetchEntry(this.data, fieldsData, this.entryId); - }).then((entry) => { - this.entry = entry.entry; - - this.editFormRender = this.displayEditFields(); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Saves data. - * - * @param e Event. - * @return Resolved when done. - */ - save(e: Event): Promise { - e.preventDefault(); - e.stopPropagation(); - - const inputData = this.editForm.value; - - return this.dataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.data.id, - this.entry.contents).then((changed) => { - - if (!changed) { - if (this.entryId) { - return this.returnToEntryList(); - } - - // New entry, no changes means no field filled, warn the user. - return Promise.reject('addon.mod_data.emptyaddform'); - } - - const modal = this.domUtils.showModalLoading('core.sending', true); - - // Create an ID to assign files. - const entryTemp = this.entryId ? this.entryId : - (new Date().getTime()); - - return this.dataHelper.getEditDataFromForm(inputData, this.fieldsArray, this.data.id, entryTemp, this.entry.contents, - this.offline).catch((e) => { - if (!this.offline) { - // Cannot submit in online, prepare for offline usage. - this.offline = true; - - return this.dataHelper.getEditDataFromForm(inputData, this.fieldsArray, this.data.id, entryTemp, - this.entry.contents, this.offline); - } - - return Promise.reject(e); - }).then((editData) => { - if (editData.length > 0) { - if (this.entryId) { - return this.dataProvider.editEntry(this.data.id, this.entryId, this.courseId, editData, this.fields, - undefined, this.offline); - } - - return this.dataProvider.addEntry(this.data.id, entryTemp, this.courseId, editData, this.selectedGroup, - this.fields, undefined, this.offline); - } - - return false; - }).then((result: any) => { - if (!result) { - // No field filled, warn the user. - return Promise.reject('addon.mod_data.emptyaddform'); - } - - // This is done if entry is updated when editing or creating if not. - if ((this.entryId && result.updated) || (!this.entryId && result.newentryid)) { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, result.sent, this.siteId); - - if (result.sent) { - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'data' }); - } - - const promises = []; - - this.entryId = this.entryId || result.newentryid; - - promises.push(this.dataProvider.invalidateEntryData(this.data.id, this.entryId, this.siteId)); - promises.push(this.dataProvider.invalidateEntriesData(this.data.id, this.siteId)); - - return Promise.all(promises).then(() => { - this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, - { dataId: this.data.id, entryId: this.entryId } , this.siteId); - }).finally(() => { - return this.returnToEntryList(); - }); - } else { - this.errors = {}; - if (result.fieldnotifications) { - result.fieldnotifications.forEach((fieldNotif) => { - const field = this.fieldsArray.find((field) => field.name == fieldNotif.fieldname); - if (field) { - this.errors[field.id] = fieldNotif.notification; - } - }); - } - this.jsData['errors'] = this.errors; - - setTimeout(() => { - this.scrollToFirstError(); - }); - } - }).finally(() => { - modal.dismiss(); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Cannot edit entry', true); - }); - } - - /** - * Set group to see the database. - * - * @param groupId Group identifier to set. - * @return Resolved when done. - */ - setGroup(groupId: number): Promise { - this.selectedGroup = groupId; - this.loaded = false; - - return this.fetchEntryData(); - } - - /** - * Displays Edit Search Fields. - * - * @return Generated HTML. - */ - protected displayEditFields(): string { - this.jsData = { - fields: this.fields, - contents: this.utils.clone(this.entry.contents), - form: this.editForm, - data: this.data, - errors: this.errors - }; - - let replace, - render, - template = this.dataHelper.getTemplate(this.data, 'addtemplate', this.fieldsArray); - - // Replace the fields found on template. - this.fieldsArray.forEach((field) => { - replace = '[[' + field.name + ']]'; - replace = replace.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); - replace = new RegExp(replace, 'gi'); - - // Replace field by a generic directive. - render = '\ - '; - template = template.replace(replace, render); - - // Replace the field id tag. - replace = '[[' + field.name + '#id]]'; - replace = replace.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); - replace = new RegExp(replace, 'gi'); - - template = template.replace(replace, 'field_' + field.id); - }); - - // Editing tags is not supported. - replace = new RegExp('##tags##', 'gi'); - const message = '

{{ \'addon.mod_data.edittagsnotsupported\' | translate }}

'; - template = template.replace(replace, this.tagProvider.areTagsAvailableInSite() ? message : ''); - - return template; - } - - /** - * Return to the entry list (previous page) discarding temp data. - * - * @return Resolved when done. - */ - protected returnToEntryList(): Promise { - const inputData = this.editForm.value; - - return this.dataHelper.getEditTmpFiles(inputData, this.fieldsArray, this.data.id, - this.entry.contents).then((files) => { - this.fileUploaderProvider.clearTmpFiles(files); - }).finally(() => { - // Go back to entry list. - this.forceLeave = true; - this.navCtrl.pop(); - }); - } - - /** - * Scroll to first error or to the top if not found. - */ - protected scrollToFirstError(): void { - if (!this.domUtils.scrollToElementBySelector(this.content, '.addon-data-error')) { - this.domUtils.scrollToTop(this.content); - } - } -} diff --git a/src/addon/mod/data/pages/entry/entry.html b/src/addon/mod/data/pages/entry/entry.html deleted file mode 100644 index 835f1fa4b..000000000 --- a/src/addon/mod/data/pages/entry/entry.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - -
- - {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} -
- - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - -
- - - -
- - - - - - - - - - - - - - - - - - -
-
diff --git a/src/addon/mod/data/pages/entry/entry.module.ts b/src/addon/mod/data/pages/entry/entry.module.ts deleted file mode 100644 index 326028728..000000000 --- a/src/addon/mod/data/pages/entry/entry.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreCommentsComponentsModule } from '@core/comments/components/components.module'; -import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module'; -import { CoreRatingComponentsModule } from '@core/rating/components/components.module'; -import { AddonModDataComponentsModule } from '../../components/components.module'; -import { AddonModDataEntryPage } from './entry'; - -@NgModule({ - declarations: [ - AddonModDataEntryPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - AddonModDataComponentsModule, - CoreCompileHtmlComponentModule, - CoreCommentsComponentsModule, - IonicPageModule.forChild(AddonModDataEntryPage), - TranslateModule.forChild(), - CoreRatingComponentsModule - ], -}) -export class AddonModDataEntryPageModule {} diff --git a/src/addon/mod/data/pages/entry/entry.ts b/src/addon/mod/data/pages/entry/entry.ts deleted file mode 100644 index 7a10b347a..000000000 --- a/src/addon/mod/data/pages/entry/entry.ts +++ /dev/null @@ -1,402 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { ChangeDetectorRef, Component, ViewChild, OnDestroy } from '@angular/core'; -import { Content, IonicPage, NavParams, NavController } from 'ionic-angular'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreRatingInfo } from '@core/rating/providers/rating'; -import { AddonModDataProvider } from '../../providers/data'; -import { AddonModDataHelperProvider } from '../../providers/helper'; -import { AddonModDataSyncProvider } from '../../providers/sync'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataComponentsModule } from '../../components/components.module'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { CoreCommentsCommentsComponent } from '@core/comments/components/comments/comments'; - -/** - * Page that displays the view entry page. - */ -@IonicPage({ segment: 'addon-mod-data-entry' }) -@Component({ - selector: 'page-addon-mod-data-entry', - templateUrl: 'entry.html', -}) -export class AddonModDataEntryPage implements OnDestroy { - @ViewChild(Content) content: Content; - @ViewChild(CoreCommentsCommentsComponent) comments: CoreCommentsCommentsComponent; - - protected module: any; - protected entryId: number; - protected courseId: number; - protected offset: number; - protected syncObserver: any; // It will observe the sync auto event. - protected entryChangedObserver: any; // It will observe the changed entry event. - protected fields = {}; - protected fieldsArray = []; - - title = ''; - moduleName = 'data'; - component = AddonModDataProvider.COMPONENT; - entryLoaded = false; - renderingEntry = false; - loadingComments = false; - loadingRating = false; - selectedGroup = 0; - entry: any; - previousOffset: number; - nextOffset: number; - access: any; - data: any; - groupInfo: any; - showComments: any; - entryHtml = ''; - siteId: string; - extraImports = [AddonModDataComponentsModule]; - jsData; - ratingInfo: CoreRatingInfo; - isPullingToRefresh = false; // Whether the last fetching of data was started by a pull-to-refresh action - commentsEnabled: boolean; - - constructor(params: NavParams, protected utils: CoreUtilsProvider, protected groupsProvider: CoreGroupsProvider, - protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate, - protected courseProvider: CoreCourseProvider, protected dataProvider: AddonModDataProvider, - protected dataHelper: AddonModDataHelperProvider, - sitesProvider: CoreSitesProvider, protected navCtrl: NavController, protected eventsProvider: CoreEventsProvider, - private cdr: ChangeDetectorRef, protected commentsProvider: CoreCommentsProvider) { - this.module = params.get('module') || {}; - this.entryId = params.get('entryId') || null; - this.courseId = params.get('courseId'); - this.selectedGroup = params.get('group') || 0; - this.offset = params.get('offset'); - - this.siteId = sitesProvider.getCurrentSiteId(); - - this.title = this.module.name; - this.moduleName = this.courseProvider.translateModuleName('data'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(); - this.fetchEntryData().then(() => { - this.logView(); - }); - - // Refresh data if this discussion is synchronized automatically. - this.syncObserver = this.eventsProvider.on(AddonModDataSyncProvider.AUTO_SYNCED, (data) => { - if ((data.entryId == this.entryId || data.offlineEntryId == this.entryId) && this.data.id == data.dataId) { - if (data.deleted) { - // If deleted, go back. - this.navCtrl.pop(); - } else { - this.entryId = data.entryid; - this.entryLoaded = false; - this.fetchEntryData(true); - } - } - }, this.siteId); - - // Refresh entry on change. - this.entryChangedObserver = this.eventsProvider.on(AddonModDataProvider.ENTRY_CHANGED, (data) => { - if (data.entryId == this.entryId && this.data.id == data.dataId) { - if (data.deleted) { - // If deleted, go back. - this.navCtrl.pop(); - } else { - this.entryLoaded = false; - this.fetchEntryData(true); - } - } - }, this.siteId); - } - - /** - * Fetch the entry data. - * - * @param refresh Whether to refresh the current data or not. - * @param isPtr Whether is a pull to refresh action. - * @return Resolved when done. - */ - protected fetchEntryData(refresh?: boolean, isPtr?: boolean): Promise { - this.isPullingToRefresh = isPtr; - - return this.dataProvider.getDatabase(this.courseId, this.module.id).then((data) => { - this.title = data.name || this.title; - this.data = data; - - return this.dataProvider.getFields(this.data.id, {cmId: this.module.id}).then((fieldsData) => { - this.fields = this.utils.arrayToObject(fieldsData, 'id'); - this.fieldsArray = fieldsData; - }); - }).then(() => { - return this.setEntryFromOffset().then(() => { - return this.dataProvider.getDatabaseAccessInformation(this.data.id, {cmId: this.module.id}); - }); - }).then((accessData) => { - this.access = accessData; - - return this.groupsProvider.getActivityGroupInfo(this.data.coursemodule).then((groupInfo) => { - this.groupInfo = groupInfo; - this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, groupInfo); - }); - }).then(() => { - const actions = this.dataHelper.getActions(this.data, this.access, this.entry); - - const template = this.dataHelper.getTemplate(this.data, 'singletemplate', this.fieldsArray); - this.entryHtml = this.dataHelper.displayShowFields(template, this.fieldsArray, this.entry, this.offset, 'show', - actions); - this.showComments = actions.comments; - - const entries = {}; - entries[this.entryId] = this.entry; - - // Pass the input data to the component. - this.jsData = { - fields: this.fields, - entries: entries, - data: this.data, - module: this.module, - group: this.selectedGroup - }; - }).catch((message) => { - if (!refresh) { - // Some call failed, retry without using cache since it might be a new activity. - return this.refreshAllData(isPtr); - } - - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - }).finally(() => { - this.domUtils.scrollToTop(this.content); - this.entryLoaded = true; - }); - } - - /** - * Go to selected entry without changing state. - * - * @param offset Entry offset. - * @return Resolved when done. - */ - gotoEntry(offset: number): Promise { - this.offset = offset; - this.entryId = null; - this.entry = null; - this.entryLoaded = false; - - return this.fetchEntryData().then(() => { - this.logView(); - }); - } - - /** - * Refresh all the data. - * - * @param isPtr Whether is a pull to refresh action. - * @return Promise resolved when done. - */ - protected refreshAllData(isPtr?: boolean): Promise { - const promises = []; - - promises.push(this.dataProvider.invalidateDatabaseData(this.courseId)); - if (this.data) { - promises.push(this.dataProvider.invalidateEntryData(this.data.id, this.entryId)); - promises.push(this.groupsProvider.invalidateActivityGroupInfo(this.data.coursemodule)); - promises.push(this.dataProvider.invalidateEntriesData(this.data.id)); - promises.push(this.dataProvider.invalidateFieldsData(this.data.id)); - - if (this.data.comments && this.entry && this.entry.id > 0 && this.commentsEnabled && this.comments) { - // Refresh comments. Don't add it to promises because we don't want the comments fetch to block the entry fetch. - this.comments.doRefresh().catch(() => { - // Ignore errors. - }); - } - } - - return Promise.all(promises).finally(() => { - return this.fetchEntryData(true, isPtr); - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @return Promise resolved when done. - */ - refreshDatabase(refresher?: any): Promise { - if (this.entryLoaded) { - return this.refreshAllData(true).finally(() => { - refresher && refresher.complete(); - }); - } - } - - /** - * Set group to see the database. - * - * @param groupId Group identifier to set. - * @return Resolved when done. - */ - setGroup(groupId: number): Promise { - this.selectedGroup = groupId; - this.offset = null; - this.entry = null; - this.entryId = null; - this.entryLoaded = false; - - return this.fetchEntryData().then(() => { - this.logView(); - }); - } - - /** - * Convenience function to fetch the entry and set next/previous entries. - * - * @return Resolved when done. - */ - protected setEntryFromOffset(): Promise { - const emptyOffset = typeof this.offset != 'number'; - - if (emptyOffset && typeof this.entryId == 'number') { - // Entry id passed as navigation parameter instead of the offset. - // We don't display next/previous buttons in this case. - this.nextOffset = null; - this.previousOffset = null; - - return this.dataHelper.fetchEntry(this.data, this.fieldsArray, this.entryId).then((entry) => { - this.entry = entry.entry; - this.ratingInfo = entry.ratinginfo; - }); - } - - const perPage = AddonModDataProvider.PER_PAGE; - const page = !emptyOffset && this.offset >= 0 ? Math.floor(this.offset / perPage) : 0; - - return this.dataHelper.fetchEntries(this.data, this.fieldsArray, { - groupId: this.selectedGroup, - sort: 0, - order: 'DESC', - page, - perPage, - }).then((entries) => { - - const pageEntries = entries.offlineEntries.concat(entries.entries); - let pageIndex; // Index of the entry when concatenating offline and online page entries. - if (emptyOffset) { - // No offset passed, display the first entry. - pageIndex = 0; - } else if (this.offset > 0) { - // Online entry. - pageIndex = this.offset % perPage + entries.offlineEntries.length; - } else { - // Offline entry. - pageIndex = this.offset + entries.offlineEntries.length; - } - - this.entry = pageEntries[pageIndex]; - this.entryId = this.entry.id; - - this.previousOffset = page > 0 || pageIndex > 0 ? this.offset - 1 : null; - - let promise; - - if (pageIndex + 1 < pageEntries.length) { - // Not the last entry on the page; - this.nextOffset = this.offset + 1; - } else if (pageEntries.length < perPage) { - // Last entry of the last page. - this.nextOffset = null; - } else { - // Last entry of the page, check if there are more pages. - promise = this.dataProvider.getEntries(this.data.id, { - groupId: this.selectedGroup, - page: page + 1, - perPage: perPage, - }).then((entries) => { - this.nextOffset = entries && entries.entries && entries.entries.length > 0 ? this.offset + 1 : null; - }); - } - - return Promise.resolve(promise).then(() => { - if (this.entryId > 0) { - // Online entry, we need to fetch the the rating info. - return this.dataProvider.getEntry(this.data.id, this.entryId, {cmId: this.module.id}).then((entry) => { - this.ratingInfo = entry.ratinginfo; - }); - } - }); - }); - } - - /** - * Function called when entry is being rendered. - */ - setRenderingEntry(rendering: boolean): void { - this.renderingEntry = rendering; - this.cdr.detectChanges(); - } - - /** - * Function called when comments component is loading data. - */ - setLoadingComments(loading: boolean): void { - this.loadingComments = loading; - this.cdr.detectChanges(); - } - - /** - * Function called when rate component is loading data. - */ - setLoadingRating(loading: boolean): void { - this.loadingRating = loading; - this.cdr.detectChanges(); - } - - /** - * Function called when rating is updated online. - */ - ratingUpdated(): void { - this.dataProvider.invalidateEntryData(this.data.id, this.entryId); - } - - /** - * Log viewing the activity. - * - * @return Promise resolved when done. - */ - protected logView(): Promise { - if (!this.data || !this.data.id) { - return Promise.resolve(); - } - - return this.dataProvider.logView(this.data.id, this.data.name).catch(() => { - // Ignore errors, the user could be offline. - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.syncObserver && this.syncObserver.off(); - this.entryChangedObserver && this.entryChangedObserver.off(); - } -} diff --git a/src/addon/mod/data/pages/index/index.html b/src/addon/mod/data/pages/index/index.html deleted file mode 100644 index c870c6fcc..000000000 --- a/src/addon/mod/data/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/data/pages/index/index.module.ts b/src/addon/mod/data/pages/index/index.module.ts deleted file mode 100644 index 5b2501654..000000000 --- a/src/addon/mod/data/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModDataComponentsModule } from '../../components/components.module'; -import { AddonModDataIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModDataIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModDataComponentsModule, - IonicPageModule.forChild(AddonModDataIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModDataIndexPageModule {} diff --git a/src/addon/mod/data/pages/index/index.ts b/src/addon/mod/data/pages/index/index.ts deleted file mode 100644 index 039891590..000000000 --- a/src/addon/mod/data/pages/index/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModDataIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a data. - */ -@IonicPage({ segment: 'addon-mod-data-index' }) -@Component({ - selector: 'page-addon-mod-data-index', - templateUrl: 'index.html', -}) -export class AddonModDataIndexPage { - @ViewChild(AddonModDataIndexComponent) dataComponent: AddonModDataIndexComponent; - - title: string; - module: any; - courseId: number; - group: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.group = navParams.get('group') || 0; - this.title = this.module.name; - } - - /** - * Update some data based on the data instance. - * - * @param data Data instance. - */ - updateData(data: any): void { - this.title = data.name || this.title; - } -} diff --git a/src/addon/mod/data/pages/search/search.html b/src/addon/mod/data/pages/search/search.html deleted file mode 100644 index 6dc36d529..000000000 --- a/src/addon/mod/data/pages/search/search.html +++ /dev/null @@ -1,57 +0,0 @@ - - - {{ 'addon.mod_data.search' | translate }} - - - - - - - -
- - - - - - {{ 'core.sortby' | translate }} - - - {{field.name}} - - - {{ 'addon.mod_data.timeadded' | translate }} - {{ 'addon.mod_data.timemodified' | translate }} - {{ 'addon.mod_data.authorfirstname' | translate }} - {{ 'addon.mod_data.authorlastname' | translate }} - {{ 'addon.mod_data.approved' | translate }} - - - - - - {{ 'addon.mod_data.ascending' | translate }} - - - - {{ 'addon.mod_data.descending' | translate }} - - - - - -
- -
-
-
diff --git a/src/addon/mod/data/pages/search/search.module.ts b/src/addon/mod/data/pages/search/search.module.ts deleted file mode 100644 index b083cc1cc..000000000 --- a/src/addon/mod/data/pages/search/search.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModDataComponentsModule } from '../../components/components.module'; -import { AddonModDataSearchPage } from './search'; -import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module'; - -@NgModule({ - declarations: [ - AddonModDataSearchPage, - ], - imports: [ - CoreDirectivesModule, - AddonModDataComponentsModule, - CoreCompileHtmlComponentModule, - IonicPageModule.forChild(AddonModDataSearchPage), - TranslateModule.forChild() - ], -}) -export class AddonModDataSearchPageModule {} diff --git a/src/addon/mod/data/pages/search/search.ts b/src/addon/mod/data/pages/search/search.ts deleted file mode 100644 index 3b9ab3953..000000000 --- a/src/addon/mod/data/pages/search/search.ts +++ /dev/null @@ -1,231 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { AddonModDataComponentsModule } from '../../components/components.module'; -import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; -import { AddonModDataHelperProvider } from '../../providers/helper'; -import { CoreTagProvider } from '@core/tag/providers/tag'; - -/** - * Page that displays the search modal. - */ -@IonicPage({ segment: 'addon-mod-data-search' }) -@Component({ - selector: 'page-addon-mod-data-search', - templateUrl: 'search.html', -}) -export class AddonModDataSearchPage { - @ViewChild('searchFormEl') formElement: ElementRef; - - search: any; - fields: any; - data: any; - advancedSearch: any; - extraImports = [AddonModDataComponentsModule]; - searchForm: FormGroup; - jsData: any; - fieldsArray: any; - - constructor(params: NavParams, - protected viewCtrl: ViewController, - fb: FormBuilder, - protected utils: CoreUtilsProvider, - protected domUtils: CoreDomUtilsProvider, - protected fieldsDelegate: AddonModDataFieldsDelegate, - protected textUtils: CoreTextUtilsProvider, - protected dataHelper: AddonModDataHelperProvider, - protected tagProvider: CoreTagProvider, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider) { - this.search = params.get('search'); - this.fields = params.get('fields'); - this.data = params.get('data'); - - const advanced = {}; - if (typeof this.search.advanced == 'object') { - Object.keys(this.search.advanced).forEach((index) => { - if (typeof this.search.advanced[index] != 'undefined' && typeof this.search.advanced[index].name != 'undefined') { - advanced[this.search.advanced[index].name] = this.search.advanced[index].value ? - this.textUtils.parseJSON(this.search.advanced[index].value) : ''; - } else { - advanced[index] = this.search.advanced[index] ? - this.textUtils.parseJSON(this.search.advanced[index]) : ''; - } - }); - } else { - this.search.advanced.forEach((field) => { - advanced[field.name] = field.value ? this.textUtils.parseJSON(field.value) : ''; - }); - } - this.search.advanced = advanced; - - this.searchForm = fb.group({ - text: [this.search.text], - sortBy: [this.search.sortBy || '0'], - sortDirection: [this.search.sortDirection || 'DESC'], - firstname: [this.search.advanced['firstname'] || ''], - lastname: [this.search.advanced['lastname'] || ''] - }); - - this.fieldsArray = this.utils.objectToArray(this.fields); - this.advancedSearch = this.renderAdvancedSearchFields(); - } - - /** - * Displays Advanced Search Fields. - * - * @return Generated HTML. - */ - protected renderAdvancedSearchFields(): string { - this.jsData = { - fields: this.fields, - form: this.searchForm, - search: this.search.advanced - }; - - let template = this.dataHelper.getTemplate(this.data, 'asearchtemplate', this.fieldsArray), - replace, render; - - // Replace the fields found on template. - this.fieldsArray.forEach((field) => { - replace = '[[' + field.name + ']]'; - replace = replace.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); - replace = new RegExp(replace, 'gi'); - - // Replace field by a generic directive. - render = ''; - template = template.replace(replace, render); - }); - - // Not pluginable other search elements. - // Replace firstname field by the text input. - replace = new RegExp('##firstname##', 'gi'); - render = ''; - template = template.replace(replace, render); - - // Replace lastname field by the text input. - replace = new RegExp('##lastname##', 'gi'); - render = ''; - template = template.replace(replace, render); - - // Searching by tags is not supported. - replace = new RegExp('##tags##', 'gi'); - const message = '

{{ \'addon.mod_data.searchbytagsnotsupported\' | translate }}

'; - template = template.replace(replace, this.tagProvider.areTagsAvailableInSite() ? message : ''); - - return template; - } - - /** - * Retrieve the entered data in search in a form. - * - * @param searchedData Array with the entered form values. - * @return Array with the answers. - */ - getSearchDataFromForm(searchedData: any): any[] { - const advancedSearch = []; - - // Filter and translate fields to each field plugin. - this.fieldsArray.forEach((field) => { - const fieldData = this.fieldsDelegate.getFieldSearchData(field, searchedData); - - if (fieldData) { - fieldData.forEach((data) => { - data.value = JSON.stringify(data.value); - // WS wants values in Json format. - advancedSearch.push(data); - }); - } - }); - - // Not pluginable other search elements. - if (searchedData['firstname']) { - // WS wants values in Json format. - advancedSearch.push({ - name: 'firstname', - value: JSON.stringify(searchedData['firstname']) - }); - } - - if (searchedData['lastname']) { - // WS wants values in Json format. - advancedSearch.push({ - name: 'lastname', - value: JSON.stringify(searchedData['lastname']) - }); - } - - return advancedSearch; - } - - /** - * Close modal. - * - * @param data Data to return to the page. - */ - closeModal(data?: any): void { - if (typeof data == 'undefined') { - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } else { - this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId()); - } - - this.viewCtrl.dismiss(data); - } - - /** - * Toggles between advanced to normal search. - * - * @param advanced True for advanced, false for basic. - */ - changeAdvanced(advanced: boolean): void { - this.search.searchingAdvanced = advanced; - } - - /** - * Done editing. - * - * @param e Event. - */ - searchEntries(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - const searchedData = this.searchForm.value; - - if (this.search.searchingAdvanced) { - this.search.advanced = this.getSearchDataFromForm(searchedData); - this.search.searching = this.search.advanced.length > 0; - } else { - this.search.text = searchedData.text; - this.search.searching = this.search.text.length > 0; - } - - this.search.sortBy = searchedData.sortBy; - this.search.sortDirection = searchedData.sortDirection; - - this.closeModal(this.search); - } -} diff --git a/src/addon/mod/data/providers/approve-link-handler.ts b/src/addon/mod/data/providers/approve-link-handler.ts deleted file mode 100644 index 7426ad39f..000000000 --- a/src/addon/mod/data/providers/approve-link-handler.ts +++ /dev/null @@ -1,76 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { AddonModDataProvider } from './data'; -import { AddonModDataHelperProvider } from './helper'; - -/** - * Content links handler for database approve/disapprove entry. - * Match mod/data/view.php?d=6&approve=5 with a valid data id and entryid. - */ -@Injectable() -export class AddonModDataApproveLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModDataApproveLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModData'; - pattern = /\/mod\/data\/view\.php.*([\?\&](d|approve|disapprove)=\d+)/; - priority = 50; // Higher priority than the default link handler for view.php. - - constructor(private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const dataId = parseInt(params.d, 10), - entryId = parseInt(params.approve, 10) || parseInt(params.disapprove, 10), - approve = parseInt(params.approve, 10) ? true : false; - - this.dataHelper.approveOrDisapproveEntry(dataId, entryId, approve, courseId, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - if (typeof params.d == 'undefined' || (typeof params.approve == 'undefined' && typeof params.disapprove == 'undefined')) { - // Required fields not defined. Cannot treat the URL. - return false; - } - - return this.dataProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/mod/data/providers/data.ts b/src/addon/mod/data/providers/data.ts deleted file mode 100644 index cde86d294..000000000 --- a/src/addon/mod/data/providers/data.ts +++ /dev/null @@ -1,1028 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModDataOfflineProvider } from './offline'; -import { AddonModDataFieldsDelegate } from './fields-delegate'; -import { CoreRatingInfo } from '@core/rating/providers/rating'; -import { CoreSite } from '@classes/site'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Database entry (online or offline). - */ -export interface AddonModDataEntry { - id: number; // Negative for offline entries. - userid: number; - groupid: number; - dataid: number; - timecreated: number; - timemodified: number; - approved: boolean; - canmanageentry: boolean; - fullname: string; - contents: AddonModDataEntryFields; - deleted?: boolean; // Entry is deleted offline. - hasOffline?: boolean; // Entry has offline actions. -} - -/** - * Entry field content. - */ -export interface AddonModDataEntryField { - fieldid: number; - content: string; - content1: string; - content2: string; - content3: string; - content4: string; - files: any[]; -} - -/** - * Entry contents indexed by field id. - */ -export interface AddonModDataEntryFields { - [fieldid: number]: AddonModDataEntryField; -} - -/** - * List of entries returned by web service and helper functions. - */ -export interface AddonModDataEntries { - entries: AddonModDataEntry[]; // Online entries. - totalcount: number; // Total count of online entries or found entries. - maxcount?: number; // Total count of online entries. Only returned when searching. - offlineEntries?: AddonModDataEntry[]; // Offline entries. - hasOfflineActions?: boolean; // Whether the database has offline data. - hasOfflineRatings?: boolean; // Whether the database has offline ratings. -} - -/** - * Subfield form data. - */ -export interface AddonModDataSubfieldData { - fieldid: number; - subfield?: string; - value?: string; // Value encoded in JSON. - files?: any[]; -} - -/** - * Service that provides some features for databases. - */ -@Injectable() -export class AddonModDataProvider { - static COMPONENT = 'mmaModData'; - static PER_PAGE = 25; - static ENTRY_CHANGED = 'addon_mod_data_entry_changed'; - - protected ROOT_CACHE_KEY = AddonModDataProvider.COMPONENT + ':'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, - private filepoolProvider: CoreFilepoolProvider, private dataOffline: AddonModDataOfflineProvider, - private appProvider: CoreAppProvider, private fieldsDelegate: AddonModDataFieldsDelegate, - private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModDataProvider'); - } - - /** - * Adds a new entry to a database. - * - * @param dataId Data instance ID. - * @param entryId EntryId or provisional entry ID when offline. - * @param courseId Course ID. - * @param contents The fields data to be created. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param fields The fields that define the contents. - * @param siteId Site ID. If not defined, current site. - * @param forceOffline Force editing entry in offline. - * @return Promise resolved when the action is done. - */ - addEntry(dataId: number, entryId: number, courseId: number, contents: AddonModDataSubfieldData[], groupId: number = 0, - fields: any, siteId?: string, forceOffline: boolean = false): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a data to be synchronized later. - const storeOffline = (): Promise => { - return this.dataOffline.saveEntry(dataId, entryId, 'add', courseId, groupId, contents, undefined, siteId) - .then((entry) => { - return { - // Return provissional entry Id. - newentryid: entry, - sent: false, - }; - }); - }; - - if (!this.appProvider.isOnline() || forceOffline) { - const notifications = this.checkFields(fields, contents); - if (notifications) { - return Promise.resolve({ - fieldnotifications: notifications - }); - } - - return storeOffline(); - } - - return this.addEntryOnline(dataId, contents, groupId, siteId).then((result) => { - result.sent = true; - - return result; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. - return Promise.reject(error); - } - - // Couldn't connect to server, store in offline. - return storeOffline(); - }); - } - - /** - * Adds a new entry to a database. It does not cache calls. It will fail if offline or cannot connect. - * - * @param dataId Database ID. - * @param data The fields data to be created. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the action is done. - */ - addEntryOnline(dataId: number, data: AddonModDataSubfieldData[], groupId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - databaseid: dataId, - data: data - }; - - if (typeof groupId !== 'undefined') { - params['groupid'] = groupId; - } - - return site.write('mod_data_add_entry', params); - }); - } - - /** - * Approves or unapproves an entry. - * - * @param dataId Database ID. - * @param entryId Entry ID. - * @param approve Whether to approve (true) or unapprove the entry. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the action is done. - */ - approveEntry(dataId: number, entryId: number, approve: boolean, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a data to be synchronized later. - const storeOffline = (): Promise => { - const action = approve ? 'approve' : 'disapprove'; - - return this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId) - .then(() => { - return { - sent: false, - }; - }); - }; - - // Get if the opposite action is not synced. - const oppositeAction = approve ? 'disapprove' : 'approve'; - - return this.dataOffline.getEntry(dataId, entryId, oppositeAction, siteId).then(() => { - // Found. Just delete the action. - return this.dataOffline.deleteEntry(dataId, entryId, oppositeAction, siteId); - }).catch(() => { - - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - return this.approveEntryOnline(entryId, approve, siteId).then(() => { - return { - sent: true, - }; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. - return Promise.reject(error); - } - - // Couldn't connect to server, store in offline. - return storeOffline(); - }); - }); - } - - /** - * Approves or unapproves an entry. It does not cache calls. It will fail if offline or cannot connect. - * - * @param entryId Entry ID. - * @param approve Whether to approve (true) or unapprove the entry. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the action is done. - */ - approveEntryOnline(entryId: number, approve: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - entryid: entryId, - approve: approve ? 1 : 0 - }; - - return site.write('mod_data_approve_entry', params); - }); - } - - /** - * Convenience function to check fields requeriments here named "notifications". - * - * @param fields The fields that define the contents. - * @param contents The contents data of the fields. - * @return Array of notifications if any or false. - */ - protected checkFields(fields: any, contents: AddonModDataSubfieldData[]): any[] | false { - const notifications = [], - contentsIndexed = {}; - - contents.forEach((content) => { - if (typeof contentsIndexed[content.fieldid] == 'undefined') { - contentsIndexed[content.fieldid] = []; - } - contentsIndexed[content.fieldid].push(content); - }); - - // App is offline, check required fields. - Object.keys(fields).forEach((key) => { - const field = fields[key], - notification = this.fieldsDelegate.getFieldsNotifications(field, contentsIndexed[field.id]); - if (notification) { - notifications.push({ - fieldname: field.name, - notification: notification - }); - } - }); - - return notifications.length ? notifications : false; - } - - /** - * Deletes an entry. - * - * @param dataId Database ID. - * @param entryId Entry ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the action is done. - */ - deleteEntry(dataId: number, entryId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a data to be synchronized later. - const storeOffline = (): Promise => { - return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId) - .then(() => { - return { - sent: false, - }; - }); - }; - - let justAdded = false; - - // Check if the opposite action is not synced and just delete it. - return this.dataOffline.getEntryActions(dataId, entryId, siteId).then((entries) => { - if (entries && entries.length) { - // Found. Delete other actions first. - const proms = entries.map((entry) => { - if (entry.action == 'add') { - justAdded = true; - } - - return this.dataOffline.deleteEntry(dataId, entryId, entry.action, siteId); - }); - - return Promise.all(proms); - } - }).then(() => { - if (justAdded) { - // The field was added offline, delete and stop. - return; - } - - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - return this.deleteEntryOnline(entryId, siteId).then(() => { - return { - sent: true, - }; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. - return Promise.reject(error); - } - - // Couldn't connect to server, store in offline. - return storeOffline(); - }); - }); - } - - /** - * Deletes an entry. It does not cache calls. It will fail if offline or cannot connect. - * - * @param entryId Entry ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the action is done. - */ - deleteEntryOnline(entryId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - entryid: entryId - }; - - return site.write('mod_data_delete_entry', params); - }); - } - - /** - * Updates an existing entry. - * - * @param dataId Database ID. - * @param entryId Entry ID. - * @param courseId Course ID. - * @param contents The contents data to be updated. - * @param fields The fields that define the contents. - * @param siteId Site ID. If not defined, current site. - * @param forceOffline Force editing entry in offline. - * @return Promise resolved when the action is done. - */ - editEntry(dataId: number, entryId: number, courseId: number, contents: AddonModDataSubfieldData[], fields: any, siteId?: string, - forceOffline: boolean = false): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a data to be synchronized later. - const storeOffline = (): Promise => { - return this.dataOffline.saveEntry(dataId, entryId, 'edit', courseId, undefined, contents, undefined, siteId) - .then(() => { - return { - updated: true, - sent: false, - }; - }); - }; - - let justAdded = false, - groupId; - - if (!this.appProvider.isOnline() || forceOffline) { - const notifications = this.checkFields(fields, contents); - if (notifications) { - return Promise.resolve({ - fieldnotifications: notifications - }); - } - } - - // Get other not not synced actions. - return this.dataOffline.getEntryActions(dataId, entryId, siteId).then((entries) => { - if (entries && entries.length) { - // Found. Delete add and edit actions first. - const proms = []; - entries.forEach((entry) => { - if (entry.action == 'add') { - justAdded = true; - groupId = entry.groupid; - proms.push(this.dataOffline.deleteEntry(dataId, entryId, entry.action, siteId)); - } else if (entry.action == 'edit') { - proms.push(this.dataOffline.deleteEntry(dataId, entryId, entry.action, siteId)); - } - }); - - return Promise.all(proms); - } - }).then(() => { - if (justAdded) { - // The field was added offline, add again and stop. - return this.addEntry(dataId, entryId, courseId, contents, groupId, fields, siteId, forceOffline) - .then((result) => { - result.updated = true; - result.sent = true; - - return result; - }); - } - - if (!this.appProvider.isOnline() || forceOffline) { - // App is offline, store the action. - return storeOffline(); - } - - return this.editEntryOnline(entryId, contents, siteId).then((result) => { - result.sent = true; - - return result; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. - return Promise.reject(error); - } - - // Couldn't connect to server, store in offline. - return storeOffline(); - }); - }); - } - - /** - * Updates an existing entry. It does not cache calls. It will fail if offline or cannot connect. - * - * @param entryId Entry ID. - * @param data The fields data to be updated. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the action is done. - */ - editEntryOnline(entryId: number, data: AddonModDataSubfieldData[], siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - entryid: entryId, - data: data - }; - - return site.write('mod_data_update_entry', params); - }); - } - - /** - * Performs the whole fetch of the entries in the database. - * - * @param dataId Data ID. - * @param options Other options. - * @return Promise resolved when done. - */ - fetchAllEntries(dataId: number, options: AddonModDataGetEntriesOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - options.page = 0; - - return this.fetchEntriesRecursive(dataId, [], options); - } - - /** - * Recursive call on fetch all entries. - * - * @param dataId Data ID. - * @param entries Entries already fetch (just to concatenate them). - * @param options Other options. - * @return Promise resolved when done. - */ - protected fetchEntriesRecursive(dataId: number, entries: any, options: AddonModDataGetEntriesOptions = {}) - : Promise { - return this.getEntries(dataId, options).then((result) => { - entries = entries.concat(result.entries); - - const canLoadMore = options.perPage > 0 && ((options.page + 1) * options.perPage) < result.totalcount; - if (canLoadMore) { - options.page++; - - return this.fetchEntriesRecursive(dataId, entries, options); - } - - return entries; - }); - } - - /** - * Get cache key for data data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getDatabaseDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'data:' + courseId; - } - - /** - * Get prefix cache key for all database activity data WS calls. - * - * @param dataId Data ID. - * @return Cache key. - */ - protected getDatabaseDataPrefixCacheKey(dataId: number): string { - return this.ROOT_CACHE_KEY + dataId; - } - - /** - * Get a database data. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the data is retrieved. - */ - protected getDatabaseByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): - Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getDatabaseDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModDataProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_data_get_databases_by_courses', params, preSets).then((response) => { - if (response && response.databases) { - const currentData = response.databases.find((data) => data[key] == value); - if (currentData) { - return currentData; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a data by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the data is retrieved. - */ - getDatabase(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getDatabaseByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a data by ID. - * - * @param courseId Course ID. - * @param id Data ID. - * @param options Other options. - * @return Promise resolved when the data is retrieved. - */ - getDatabaseById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getDatabaseByKey(courseId, 'id', id, options); - } - - /** - * Get prefix cache key for all database access information data WS calls. - * - * @param dataId Data ID. - * @return Cache key. - */ - protected getDatabaseAccessInformationDataPrefixCacheKey(dataId: number): string { - return this.getDatabaseDataPrefixCacheKey(dataId) + ':access:'; - } - - /** - * Get cache key for database access information data WS calls. - * - * @param dataId Data ID. - * @param groupId Group ID. - * @return Cache key. - */ - protected getDatabaseAccessInformationDataCacheKey(dataId: number, groupId: number = 0): string { - return this.getDatabaseAccessInformationDataPrefixCacheKey(dataId) + groupId; - } - - /** - * Get access information for a given database. - * - * @param dataId Data ID. - * @param options Other options. - * @return Promise resolved when the database is retrieved. - */ - getDatabaseAccessInformation(dataId: number, options: AddonModDataAccessInfoOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - databaseid: dataId, - }; - const preSets = { - cacheKey: this.getDatabaseAccessInformationDataCacheKey(dataId, options.groupId), - component: AddonModDataProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - if (typeof options.groupId !== 'undefined') { - params['groupid'] = options.groupId; - } - - return site.read('mod_data_get_data_access_information', params, preSets); - }); - } - - /** - * Get entries for a specific database and group. - * - * @param dataId Data ID. - * @param options Other options. - * @return Promise resolved when the database is retrieved. - */ - getEntries(dataId: number, options: AddonModDataGetEntriesOptions = {}): Promise { - options.groupId = options.groupId || 0; - options.sort = options.sort || 0; - options.order || options.order || 'DESC'; - options.page = options.page || 0; - options.perPage = options.perPage || AddonModDataProvider.PER_PAGE; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - // Always use sort and order params to improve cache usage (entries are identified by params). - const params = { - databaseid: dataId, - returncontents: 1, - page: options.page, - perpage: options.perPage, - groupid: options.groupId, - sort: options.sort, - order: options.order, - }; - const preSets = { - cacheKey: this.getEntriesCacheKey(dataId, options.groupId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModDataProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_data_get_entries', params, preSets).then((response) => { - response.entries.forEach((entry) => { - entry.contents = this.utils.arrayToObject(entry.contents, 'fieldid'); - }); - - return response; - }); - }); - } - - /** - * Get cache key for database entries data WS calls. - * - * @param dataId Data ID. - * @param groupId Group ID. - * @return Cache key. - */ - protected getEntriesCacheKey(dataId: number, groupId: number = 0): string { - return this.getEntriesPrefixCacheKey(dataId) + groupId; - } - - /** - * Get prefix cache key for database all entries data WS calls. - * - * @param dataId Data ID. - * @return Cache key. - */ - protected getEntriesPrefixCacheKey(dataId: number): string { - return this.getDatabaseDataPrefixCacheKey(dataId) + ':entries:'; - } - - /** - * Get an entry of the database activity. - * - * @param dataId Data ID for caching purposes. - * @param entryId Entry ID. - * @param options Other options. - * @return Promise resolved when the entry is retrieved. - */ - getEntry(dataId: number, entryId: number, options: CoreCourseCommonModWSOptions = {}): - Promise<{entry: AddonModDataEntry, ratinginfo: CoreRatingInfo}> { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - entryid: entryId, - returncontents: 1, - }; - const preSets = { - cacheKey: this.getEntryCacheKey(dataId, entryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModDataProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_data_get_entry', params, preSets).then((response) => { - response.entry.contents = this.utils.arrayToObject(response.entry.contents, 'fieldid'); - - return response; - }); - }); - } - - /** - * Get cache key for database entry data WS calls. - * - * @param dataId Data ID for caching purposes. - * @param entryId Entry ID. - * @return Cache key. - */ - protected getEntryCacheKey(dataId: number, entryId: number): string { - return this.getDatabaseDataPrefixCacheKey(dataId) + ':entry:' + entryId; - } - - /** - * Get the list of configured fields for the given database. - * - * @param dataId Data ID. - * @param options Other options. - * @return Promise resolved when the fields are retrieved. - */ - getFields(dataId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - databaseid: dataId, - }; - const preSets = { - cacheKey: this.getFieldsCacheKey(dataId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModDataProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_data_get_fields', params, preSets).then((response) => { - if (response && response.fields) { - return response.fields; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for database fields data WS calls. - * - * @param dataId Data ID. - * @return Cache key. - */ - protected getFieldsCacheKey(dataId: number): string { - return this.getDatabaseDataPrefixCacheKey(dataId) + ':fields'; - } - - /** - * Invalidate the prefetched content. - * To invalidate files, use AddonModDataProvider#invalidateFiles. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.getDatabase(courseId, moduleId).then((data) => { - const ps = []; - - // Do not invalidate module data before getting module info, we need it! - ps.push(this.invalidateDatabaseData(courseId, siteId)); - ps.push(this.invalidateDatabaseWSData(data.id, siteId)); - ps.push(this.invalidateFieldsData(data.id, siteId)); - - return Promise.all(ps); - })); - - promises.push(this.invalidateFiles(moduleId, siteId)); - - return this.utils.allPromises(promises); - } - - /** - * Invalidates database access information data. - * - * @param dataId Data ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateDatabaseAccessInformationData(dataId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getDatabaseAccessInformationDataPrefixCacheKey(dataId)); - }); - } - - /** - * Invalidates database entries data. - * - * @param dataId Data ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateEntriesData(dataId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getEntriesPrefixCacheKey(dataId)); - }); - } - - /** - * Invalidates database fields data. - * - * @param dataId Data ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateFieldsData(dataId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getFieldsCacheKey(dataId)); - }); - } - - /** - * Invalidate the prefetched files. - * - * @param moduleId The module ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the files are invalidated. - */ - invalidateFiles(moduleId: number, siteId?: string): Promise { - return this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModDataProvider.COMPONENT, moduleId); - } - - /** - * Invalidates database data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateDatabaseData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getDatabaseDataCacheKey(courseId)); - }); - } - - /** - * Invalidates database data except files and module info. - * - * @param databaseId Data ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateDatabaseWSData(databaseId: number, siteId: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getDatabaseDataPrefixCacheKey(databaseId)); - }); - } - - /** - * Invalidates database entry data. - * - * @param dataId Data ID for caching purposes. - * @param entryId Entry ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateEntryData(dataId: number, entryId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getEntryCacheKey(dataId, entryId)); - }); - } - - /** - * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the database WS are available. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - * @since 3.3 - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('mod_data_get_data_access_information'); - }); - } - - /** - * Report the database as being viewed. - * - * @param id Module ID. - * @param name Name of the data. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - databaseid: id - }; - - return this.logHelper.logSingle('mod_data_view_database', params, AddonModDataProvider.COMPONENT, id, name, 'data', {}, - siteId); - } - - /** - * Performs search over a database. - * - * @param dataId The data instance id. - * @param options Other options. - * @return Promise resolved when the action is done. - */ - searchEntries(dataId: number, options?: AddonModDataSearchEntriesOptions): Promise { - options.groupId = options.groupId || 0; - options.sort = options.sort || 0; - options.order || options.order || 'DESC'; - options.page = options.page || 0; - options.perPage = options.perPage || AddonModDataProvider.PER_PAGE; - options.readingStrategy = options.readingStrategy || CoreSitesReadingStrategy.PreferNetwork; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - databaseid: dataId, - groupid: options.groupId, - returncontents: 1, - page: options.page, - perpage: options.perPage, - }; - const preSets = { - component: AddonModDataProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - if (typeof options.sort != 'undefined') { - params['sort'] = options.sort; - } - - if (typeof options.order !== 'undefined') { - params['order'] = options.order; - } - - if (typeof options.search !== 'undefined') { - params['search'] = options.search; - } - - if (typeof options.advSearch !== 'undefined') { - params['advsearch'] = options.advSearch; - } - - return site.read('mod_data_search_entries', params, preSets).then((response) => { - response.entries.forEach((entry) => { - entry.contents = this.utils.arrayToObject(entry.contents, 'fieldid'); - }); - - return response; - }); - }); - } -} - -/** - * Options to pass to get access info. - */ -export type AddonModDataAccessInfoOptions = CoreCourseCommonModWSOptions & { - groupId?: number; // Group Id. -}; - -/** - * Options to pass to get entries. - */ -export type AddonModDataGetEntriesOptions = CoreCourseCommonModWSOptions & { - groupId?: number; // Group Id. - sort?: number; // Sort the records by this field id, defaults to 0. Reserved ids are: - // 0: timeadded - // -1: firstname - // -2: lastname - // -3: approved - // -4: timemodified - order?: string; // The direction of the sorting: 'ASC' or 'DESC'. Defaults to 'DESC'. - page?: number; // Page of records to return. Defaults to 0. - perPage?: number; // Records per page to return. Defaults to AddonModDataProvider.PER_PAGE. -}; - -/** - * Options to pass to search entries. - */ -export type AddonModDataSearchEntriesOptions = AddonModDataGetEntriesOptions & { - search?: string; // Search text. It will be used if advSearch is not defined. - advSearch?: any; // Advanced search data. -}; diff --git a/src/addon/mod/data/providers/default-field-handler.ts b/src/addon/mod/data/providers/default-field-handler.ts deleted file mode 100644 index bc9beba91..000000000 --- a/src/addon/mod/data/providers/default-field-handler.ts +++ /dev/null @@ -1,100 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injectable } from '@angular/core'; -import { AddonModDataFieldHandler } from './fields-delegate'; - -/** - * Default handler used when a field plugin doesn't have a specific implementation. - */ -@Injectable() -export class AddonModDataDefaultFieldHandler implements AddonModDataFieldHandler { - name = 'AddonModDataDefaultFieldHandler'; - type = 'default'; - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData(field: any, inputData: any): any { - return false; - } - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - return false; - } - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { - return false; - } - - /** - * Get field edit files in the input data. - * - * @param field Defines the field.. - * @return With name and value of the data to be sent. - */ - getFieldEditFiles(field: any, inputData: any, originalFieldData: any): any { - return []; - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string | false { - return false; - } - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { - return originalContent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/data/providers/delete-link-handler.ts b/src/addon/mod/data/providers/delete-link-handler.ts deleted file mode 100644 index a4252719e..000000000 --- a/src/addon/mod/data/providers/delete-link-handler.ts +++ /dev/null @@ -1,74 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { AddonModDataProvider } from './data'; -import { AddonModDataHelperProvider } from './helper'; - -/** - * Content links handler for database delete entry. - * Match mod/data/view.php?d=6&delete=5 with a valid data id and entryid. - */ -@Injectable() -export class AddonModDataDeleteLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModDataDeleteLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModData'; - pattern = /\/mod\/data\/view\.php.*([\?\&](d|delete)=\d+)/; - - constructor(private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const dataId = parseInt(params.d, 10); - const entryId = parseInt(params.delete, 10); - - this.dataHelper.showDeleteEntryModal(dataId, entryId, courseId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - if (typeof params.d == 'undefined' || typeof params.delete == 'undefined') { - // Required fields not defined. Cannot treat the URL. - return false; - } - - return this.dataProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/mod/data/providers/edit-link-handler.ts b/src/addon/mod/data/providers/edit-link-handler.ts deleted file mode 100644 index 44b2727c6..000000000 --- a/src/addon/mod/data/providers/edit-link-handler.ts +++ /dev/null @@ -1,92 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonModDataProvider } from './data'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Content links handler for database add or edit entry. - * Match mod/data/edit.php?d=6&rid=6 with a valid data and optional record id. - */ -@Injectable() -export class AddonModDataEditLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModDataEditLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModData'; - pattern = /\/mod\/data\/edit\.php.*([\?\&](d|rid)=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private dataProvider: AddonModDataProvider, - private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - dataId = parseInt(params.d, 10), - rId = parseInt(params.rid, 10) || false; - - this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => { - const pageParams = { - module: module, - courseId: module.course - }; - - if (rId) { - pageParams['entryId'] = rId; - } - - return this.linkHelper.goInSite(navCtrl, 'AddonModDataEditPage', pageParams, siteId); - }).finally(() => { - // Just in case. In fact we need to dismiss the modal before showing a toast or error message. - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - if (typeof params.d == 'undefined') { - // Id not defined. Cannot treat the URL. - return false; - } - - return this.dataProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/mod/data/providers/fields-delegate.ts b/src/addon/mod/data/providers/fields-delegate.ts deleted file mode 100644 index 9c668affa..000000000 --- a/src/addon/mod/data/providers/fields-delegate.ts +++ /dev/null @@ -1,223 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injector, Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; -import { AddonModDataDefaultFieldHandler } from './default-field-handler'; - -/** - * Interface that all fields handlers must implement. - */ -export interface AddonModDataFieldHandler extends CoreDelegateHandler { - - /** - * Name of the type of data field the handler supports. E.g. 'checkbox'. - */ - type: string; - - /** - * Return the Component to use to display the plugin data. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param field The field object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent?(injector: Injector, plugin: any): any | Promise; - - /** - * Get field search data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return With name and value of the data to be sent. - */ - getFieldSearchData?(field: any, inputData: any): any; - - /** - * Get field edit data in the input data. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return With name and value of the data to be sent. - */ - getFieldEditData?(field: any, inputData: any, originalFieldData: any): any; - - /** - * Get field data in changed. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @param originalFieldData Original field entered data. - * @return If the field has changes. - */ - hasFieldDataChanged?(field: any, inputData: any, originalFieldData: any): Promise | boolean; - - /** - * Get field edit files in the input data. - * - * @param field Defines the field.. - * @return With name and value of the data to be sent. - */ - getFieldEditFiles?(field: any, inputData: any, originalFieldData: any): any; - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications?(field: any, inputData: any): string | false; - - /** - * Override field content data with offline submission. - * - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData?(originalContent: any, offlineContent: any, offlineFiles?: any): any; -} - -/** - * Delegate to register database fields handlers. - */ -@Injectable() -export class AddonModDataFieldsDelegate extends CoreDelegate { - - protected handlerNameProperty = 'type'; - - constructor(logger: CoreLoggerProvider, sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, - protected utils: CoreUtilsProvider, protected defaultHandler: AddonModDataDefaultFieldHandler) { - super('AddonModDataFieldsDelegate', logger, sitesProvider, eventsProvider); - } - - /** - * Get the component to use for a certain field field. - * - * @param injector Injector. - * @param field The field object. - * @return Promise resolved with the component to use, undefined if not found. - */ - getComponentForField(injector: Injector, field: any): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(field.type, 'getComponent', [injector, field])); - } - - /** - * Get database data in the input data to search. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @return Name and data field. - */ - getFieldSearchData(field: any, inputData: any): any { - return this.executeFunctionOnEnabled(field.type, 'getFieldSearchData', [field, inputData]); - } - - /** - * Get database data in the input data to add or update entry. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @param originalFieldData Original field entered data. - * @return Name and data field. - */ - getFieldEditData(field: any, inputData: any, originalFieldData: any): any { - return this.executeFunctionOnEnabled(field.type, 'getFieldEditData', [field, inputData, originalFieldData]); - } - - /** - * Get database data in the input files to add or update entry. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @param originalFieldData Original field entered data. - * @return Name and data field. - */ - getFieldEditFiles(field: any, inputData: any, originalFieldData: any): any { - return this.executeFunctionOnEnabled(field.type, 'getFieldEditFiles', [field, inputData, originalFieldData]); - } - - /** - * Check and get field requeriments. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the edit form. - * @return String with the notification or false. - */ - getFieldsNotifications(field: any, inputData: any): string { - return this.executeFunctionOnEnabled(field.type, 'getFieldsNotifications', [field, inputData]); - } - - /** - * Check if field type manage files or not. - * - * @param field Defines the field to be checked. - * @return If the field type manages files. - */ - hasFiles(field: any): boolean { - return this.hasFunction(field.type, 'getFieldEditFiles'); - } - - /** - * Check if the data has changed for a certain field. - * - * @param field Defines the field to be rendered. - * @param inputData Data entered in the search form. - * @param originalFieldData Original field entered data. - * @return Promise rejected if has changed, resolved if no changes. - */ - hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(field.type, 'hasFieldDataChanged', - [field, inputData, originalFieldData])).then((result) => { - return result ? Promise.reject(null) : Promise.resolve(); - }); - } - - /** - * Check if a field plugin is supported. - * - * @param pluginType Type of the plugin. - * @return True if supported, false otherwise. - */ - isPluginSupported(pluginType: string): boolean { - return this.hasHandler(pluginType, true); - } - - /** - * Override field content data with offline submission. - * - * @param field Defines the field to be rendered. - * @param originalContent Original data to be overriden. - * @param offlineContent Array with all the offline data to override. - * @param offlineFiles Array with all the offline files in the field. - * @return Data overriden - */ - overrideData(field: any, originalContent: any, offlineContent: any, offlineFiles?: any): any { - originalContent = originalContent || {}; - - if (!offlineContent) { - return originalContent; - } - - return this.executeFunctionOnEnabled(field.type, 'overrideData', [originalContent, offlineContent, offlineFiles]); - } - -} diff --git a/src/addon/mod/data/providers/helper.ts b/src/addon/mod/data/providers/helper.ts deleted file mode 100644 index ea686e422..000000000 --- a/src/addon/mod/data/providers/helper.ts +++ /dev/null @@ -1,708 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { AddonModDataFieldsDelegate } from './fields-delegate'; -import { AddonModDataOfflineProvider, AddonModDataOfflineAction } from './offline'; -import { - AddonModDataProvider, AddonModDataEntry, AddonModDataEntryFields, AddonModDataEntries, AddonModDataSearchEntriesOptions -} from './data'; -import { CoreRatingInfo } from '@core/rating/providers/rating'; -import { CoreRatingOfflineProvider } from '@core/rating/providers/offline'; - -/** - * Service that provides helper functions for datas. - */ -@Injectable() -export class AddonModDataHelperProvider { - - constructor(private sitesProvider: CoreSitesProvider, protected dataProvider: AddonModDataProvider, - private translate: TranslateService, private fieldsDelegate: AddonModDataFieldsDelegate, - private dataOffline: AddonModDataOfflineProvider, private fileUploaderProvider: CoreFileUploaderProvider, - private textUtils: CoreTextUtilsProvider, private eventsProvider: CoreEventsProvider, private utils: CoreUtilsProvider, - private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider, - private ratingOffline: CoreRatingOfflineProvider) {} - - /** - * Returns the record with the offline actions applied. - * - * @param record Entry to modify. - * @param offlineActions Offline data with the actions done. - * @param fields Entry defined fields indexed by fieldid. - * @return Promise resolved when done. - */ - applyOfflineActions(record: AddonModDataEntry, offlineActions: AddonModDataOfflineAction[], fields: any[]): - Promise { - const promises = []; - - offlineActions.forEach((action) => { - record.timemodified = action.timemodified; - record.hasOffline = true; - - switch (action.action) { - case 'approve': - record.approved = true; - break; - case 'disapprove': - record.approved = false; - break; - case 'delete': - record.deleted = true; - break; - case 'add': - case 'edit': - record.groupid = action.groupid; - - const offlineContents = {}; - - action.fields.forEach((offlineContent) => { - if (typeof offlineContents[offlineContent.fieldid] == 'undefined') { - offlineContents[offlineContent.fieldid] = {}; - } - - if (offlineContent.subfield) { - offlineContents[offlineContent.fieldid][offlineContent.subfield] = - this.textUtils.parseJSON(offlineContent.value); - } else { - offlineContents[offlineContent.fieldid][''] = this.textUtils.parseJSON(offlineContent.value); - } - }); - - // Override field contents. - fields.forEach((field) => { - if (this.fieldsDelegate.hasFiles(field)) { - promises.push(this.getStoredFiles(record.dataid, record.id, field.id).then((offlineFiles) => { - record.contents[field.id] = this.fieldsDelegate.overrideData(field, record.contents[field.id], - offlineContents[field.id], offlineFiles); - record.contents[field.id].fieldid = field.id; - })); - } else { - record.contents[field.id] = this.fieldsDelegate.overrideData(field, record.contents[field.id], - offlineContents[field.id]); - record.contents[field.id].fieldid = field.id; - } - }); - break; - default: - break; - } - }); - - return Promise.all(promises).then(() => { - return record; - }); - } - - /** - * Approve or disapprove a database entry. - * - * @param dataId Database ID. - * @param entryId Entry ID. - * @param approve True to approve, false to disapprove. - * @param courseId Course ID. It not defined, it will be fetched. - * @param siteId Site ID. If not defined, current site. - */ - approveOrDisapproveEntry(dataId: number, entryId: number, approve: boolean, courseId?: number, siteId?: string): void { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.getActivityCourseIdIfNotSet(dataId, courseId, siteId).then((courseId) => { - // Approve/disapprove entry. - return this.dataProvider.approveEntry(dataId, entryId, approve, courseId, siteId).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errorapproving', true); - - return Promise.reject(null); - }); - }).then(() => { - const promises = []; - promises.push(this.dataProvider.invalidateEntryData(dataId, entryId, siteId)); - promises.push(this.dataProvider.invalidateEntriesData(dataId, siteId)); - - return Promise.all(promises).catch(() => { - // Ignore errors. - }); - }).then(() => { - this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId}, siteId); - - this.domUtils.showToast(approve ? 'addon.mod_data.recordapproved' : 'addon.mod_data.recorddisapproved', true, 3000); - }).catch(() => { - // Ignore error, it was already displayed. - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Displays fields for being shown. - * - * @param template Template HMTL. - * @param fields Fields that defines every content in the entry. - * @param entry Entry. - * @param offset Entry offset. - * @param mode Mode list or show. - * @param actions Actions that can be performed to the record. - * @return Generated HTML. - */ - displayShowFields(template: string, fields: any[], entry: any, offset: number, mode: string, - actions: {[name: string]: boolean}): string { - - if (!template) { - return ''; - } - - let replace, render; - - // Replace the fields found on template. - fields.forEach((field) => { - replace = '[[' + field.name + ']]'; - replace = replace.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); - replace = new RegExp(replace, 'gi'); - - // Replace field by a generic directive. - render = ''; - template = template.replace(replace, render); - }); - - for (const action in actions) { - replace = new RegExp('##' + action + '##', 'gi'); - // Is enabled? - if (actions[action]) { - if (action == 'moreurl') { - // Render more url directly because it can be part of an HTML attribute. - render = this.sitesProvider.getCurrentSite().getURL() + '/mod/data/view.php?d={{data.id}}&rid=' + entry.id; - } else if (action == 'approvalstatus') { - render = this.translate.instant('addon.mod_data.' + (entry.approved ? 'approved' : 'notapproved')); - } else { - render = ''; - } - template = template.replace(replace, render); - } else { - template = template.replace(replace, ''); - } - } - - return template; - } - - /** - * Get online and offline entries, or search entries. - * - * @param data Database object. - * @param fields The fields that define the contents. - * @param options Other options. - * @return Promise resolved when the database is retrieved. - */ - fetchEntries(data: any, fields: any[], options: AddonModDataSearchEntriesOptions = {}): Promise { - options.groupId = options.groupId || 0; - options.page = options.page || 0; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const offlineActions = {}; - const result: AddonModDataEntries = { - entries: [], - totalcount: 0, - offlineEntries: [] - }; - options.siteId = site.id; - - const offlinePromise = this.dataOffline.getDatabaseEntries(data.id, site.id).then((actions) => { - result.hasOfflineActions = !!actions.length; - - actions.forEach((action) => { - if (typeof offlineActions[action.entryid] == 'undefined') { - offlineActions[action.entryid] = []; - } - offlineActions[action.entryid].push(action); - - // We only display new entries in the first page when not searching. - if (action.action == 'add' && options.page == 0 && !options.search && !options.advSearch && - (!action.groupid || !options.groupId || action.groupid == options.groupId)) { - result.offlineEntries.push({ - id: action.entryid, - canmanageentry: true, - approved: !data.approval || data.manageapproved, - dataid: data.id, - groupid: action.groupid, - timecreated: -action.entryid, - timemodified: -action.entryid, - userid: site.getUserId(), - fullname: site.getInfo().fullname, - contents: {} - }); - } - }); - - // Sort offline entries by creation time. - result.offlineEntries.sort((entry1, entry2) => entry2.timecreated - entry1.timecreated); - }); - - const ratingsPromise = this.ratingOffline.hasRatings('mod_data', 'entry', 'module', data.coursemodule) - .then((hasRatings) => { - result.hasOfflineRatings = hasRatings; - }); - - let fetchPromise: Promise; - if (options.search || options.advSearch) { - fetchPromise = this.dataProvider.searchEntries(data.id, options).then((fetchResult) => { - result.entries = fetchResult.entries; - result.totalcount = fetchResult.totalcount; - result.maxcount = fetchResult.maxcount; - }); - } else { - fetchPromise = this.dataProvider.getEntries(data.id, options).then((fetchResult) => { - result.entries = fetchResult.entries; - result.totalcount = fetchResult.totalcount; - }); - } - - return Promise.all([offlinePromise, ratingsPromise, fetchPromise]).then(() => { - // Apply offline actions to online and offline entries. - const promises = []; - result.entries.forEach((entry) => { - promises.push(this.applyOfflineActions(entry, offlineActions[entry.id] || [], fields)); - }); - result.offlineEntries.forEach((entry) => { - promises.push(this.applyOfflineActions(entry, offlineActions[entry.id] || [], fields)); - }); - - return Promise.all(promises); - }).then(() => { - return result; - }); - }); - } - - /** - * Fetch an online or offline entry. - * - * @param data Database. - * @param fields List of database fields. - * @param entryId Entry ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the entry. - */ - fetchEntry(data: any, fields: any[], entryId: number, siteId?: string): - Promise<{entry: AddonModDataEntry, ratinginfo?: CoreRatingInfo}> { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.dataOffline.getEntryActions(data.id, entryId, site.id).then((offlineActions) => { - let promise: Promise<{entry: AddonModDataEntry, ratinginfo?: CoreRatingInfo}>; - - if (entryId > 0) { - // Online entry. - promise = this.dataProvider.getEntry(data.id, entryId, {cmId: data.coursemodule, siteId: site.id}); - } else { - // Offline entry or new entry. - promise = Promise.resolve({ - entry: { - id: entryId, - userid: site.getUserId(), - groupid: 0, - dataid: data.id, - timecreated: -entryId, - timemodified: -entryId, - approved: !data.approval || data.manageapproved, - canmanageentry: true, - fullname: site.getInfo().fullname, - contents: {}, - } - }); - } - - return promise.then((response) => { - return this.applyOfflineActions(response.entry, offlineActions, fields).then(() => { - return response; - }); - }); - }); - }); - } - - /** - * Returns an object with all the actions that the user can do over the record. - * - * @param database Database activity. - * @param accessInfo Access info to the activity. - * @param record Entry or record where the actions will be performed. - * @return Keyed with the action names and boolean to evalute if it can or cannot be done. - */ - getActions(database: any, accessInfo: any, record: any): {[name: string]: boolean} { - return { - more: true, - moreurl: true, - user: true, - userpicture: true, - timeadded: true, - timemodified: true, - tags: true, - - edit: record.canmanageentry && !record.deleted, // This already checks capabilities and readonly period. - delete: record.canmanageentry, - approve: database.approval && accessInfo.canapprove && !record.approved && !record.deleted, - disapprove: database.approval && accessInfo.canapprove && record.approved && !record.deleted, - - approvalstatus: database.approval, - comments: database.comments, - - // Unsupported actions. - delcheck: false, - export: false - }; - } - - /** - * Convenience function to get the course id of the database. - * - * @param dataId Database id. - * @param courseId Course id, if known. - * @param siteId Site id, if not set, current site will be used. - * @return Resolved with course Id when done. - */ - protected getActivityCourseIdIfNotSet(dataId: number, courseId?: number, siteId?: string): Promise { - if (courseId) { - return Promise.resolve(courseId); - } - - return this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => { - return module.course; - }); - } - - /** - * Returns the default template of a certain type. - * - * Based on Moodle function data_generate_default_template. - * - * @param type Type of template. - * @param fields List of database fields. - * @return Template HTML. - */ - getDefaultTemplate(type: string, fields: any[]): string { - if (type == 'listtemplateheader' || type == 'listtemplatefooter') { - return ''; - } - - const html = []; - - if (type == 'listtemplate') { - html.push('##delcheck##
'); - } - - html.push( - '
', - '', - '' - ); - - fields.forEach((field) => { - html.push( - '', - '', - '', - '' - ); - }); - - if (type == 'listtemplate') { - html.push( - '', - '', - '' - ); - } else if (type == 'singletemplate') { - html.push( - '', - '', - '' - ); - } else if (type == 'asearchtemplate') { - html.push( - '', - '', - '', - '', - '', - '', - '', - '' - ); - } - - html.push( - '', - '
', field.name, ': [[', field.name, ']]
', - '##edit## ##more## ##delete## ##approve## ##disapprove## ##export##', - '
', - '##edit## ##delete## ##approve## ##disapprove## ##export##', - '
Author first name: ##firstname##
Author surname: ##lastname##
', - '
' - ); - - if (type == 'listtemplate') { - html.push('
'); - } - - return html.join(''); - } - - /** - * Retrieve the entered data in the edit form. - * We don't use ng-model because it doesn't detect changes done by JavaScript. - * - * @param inputData Array with the entered form values. - * @param fields Fields that defines every content in the entry. - * @param dataId Database Id. If set, files will be uploaded and itemId set. - * @param entryId Entry Id. - * @param entryContents Original entry contents. - * @param offline True to prepare the data for an offline uploading, false otherwise. - * @param siteId Site ID. If not defined, current site. - * @return That contains object with the answers. - */ - getEditDataFromForm(inputData: any, fields: any, dataId: number, entryId: number, entryContents: AddonModDataEntryFields, - offline: boolean = false, siteId?: string): Promise { - if (!inputData) { - return Promise.resolve({}); - } - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Filter and translate fields to each field plugin. - const edit = [], - promises = []; - fields.forEach((field) => { - promises.push(Promise.resolve(this.fieldsDelegate.getFieldEditData(field, inputData, entryContents[field.id])) - .then((fieldData) => { - if (fieldData) { - const proms = []; - - fieldData.forEach((data) => { - let dataProm; - - // Upload Files if asked. - if (dataId && data.files) { - dataProm = this.uploadOrStoreFiles(dataId, 0, entryId, data.fieldid, data.files, offline, siteId) - .then((filesResult) => { - delete data.files; - data.value = filesResult; - }); - } else { - dataProm = Promise.resolve(); - } - - proms.push(dataProm.then(() => { - if (data.value) { - data.value = JSON.stringify(data.value); - } - if (typeof data.subfield == 'undefined') { - data.subfield = ''; - } - - // WS wants values in Json format. - edit.push(data); - })); - }); - - return Promise.all(proms); - } - })); - }); - - return Promise.all(promises).then(() => { - return edit; - }); - } - - /** - * Retrieve the temp files to be updated. - * - * @param inputData Array with the entered form values. - * @param fields Fields that defines every content in the entry. - * @param dataId Database Id. If set, fils will be uploaded and itemId set. - * @param entryContents Original entry contents indexed by field id. - * @return That contains object with the files. - */ - getEditTmpFiles(inputData: any, fields: any[], dataId: number, entryContents: AddonModDataEntryFields): Promise { - if (!inputData) { - return Promise.resolve([]); - } - - // Filter and translate fields to each field plugin. - const promises = fields.map((field) => { - return Promise.resolve(this.fieldsDelegate.getFieldEditFiles(field, inputData, entryContents[field.id])); - }); - - return Promise.all(promises).then((fieldsFiles) => { - return fieldsFiles.reduce((files: any[], fieldFiles: any) => files.concat(fieldFiles), []); - }); - } - - /** - * Get a list of stored attachment files for a new entry. See $mmaModDataHelper#storeFiles. - * - * @param dataId Database ID. - * @param entryId Entry ID or, if creating, timemodified. - * @param fieldId Field ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getStoredFiles(dataId: number, entryId: number, fieldId: number, siteId?: string): Promise { - return this.dataOffline.getEntryFieldFolder(dataId, entryId, fieldId, siteId).then((folderPath) => { - return this.fileUploaderProvider.getStoredFiles(folderPath).catch(() => { - // Ignore not found files. - return []; - }); - }); - } - - /** - * Returns the template of a certain type. - * - * @param data Database object. - * @param type Type of template. - * @param fields List of database fields. - * @return Template HTML. - */ - getTemplate(data: any, type: string, fields: any[]): string { - let template = data[type] || this.getDefaultTemplate(type, fields); - - if (type != 'listtemplateheader' && type != 'listtemplatefooter') { - // Try to fix syntax errors so the template can be parsed by Angular. - template = this.domUtils.fixHtml(template); - } - - // Add core-link directive to links. - template = template.replace(/]*href="[^>]*)>/ig, (match, attributes) => { - return ''; - }); - - return template; - } - - /** - * Check if data has been changed by the user. - * - * @param inputData Object with the entered form values. - * @param fields Fields that defines every content in the entry. - * @param dataId Database Id. If set, fils will be uploaded and itemId set. - * @param entryContents Original entry contents indexed by field id. - * @return True if changed, false if not. - */ - hasEditDataChanged(inputData: any, fields: any[], dataId: number, entryContents: AddonModDataEntryFields): Promise { - const promises = fields.map((field) => { - return this.fieldsDelegate.hasFieldDataChanged(field, inputData, entryContents[field.id]); - }); - - // Will reject on first change detected. - return Promise.all(promises).then(() => { - // No changes. - return false; - }).catch(() => { - // Has changes. - return true; - }); - } - - /** - * Displays a confirmation modal for deleting an entry. - * - * @param dataId Database ID. - * @param entryId Entry ID. - * @param courseId Course ID. It not defined, it will be fetched. - * @param siteId Site ID. If not defined, current site. - */ - showDeleteEntryModal(dataId: number, entryId: number, courseId?: number, siteId?: string): void { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - this.domUtils.showDeleteConfirm('addon.mod_data.confirmdeleterecord').then(() => { - const modal = this.domUtils.showModalLoading(); - - return this.getActivityCourseIdIfNotSet(dataId, courseId, siteId).then((courseId) => { - return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errordeleting', true); - - return Promise.reject(null); - }).then(() => { - return this.utils.allPromises([ - this.dataProvider.invalidateEntryData(dataId, entryId, siteId), - this.dataProvider.invalidateEntriesData(dataId, siteId) - ]).catch(() => { - // Ignore errors. - }); - }).then(() => { - this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId, entryId, deleted: true}, siteId); - - this.domUtils.showToast('addon.mod_data.recorddeleted', true, 3000); - }).finally(() => { - modal.dismiss(); - }); - }).catch(() => { - // Ignore error, it was already displayed. - }); - } - - /** - * Given a list of files (either online files or local files), store the local files in a local folder - * to be submitted later. - * - * @param dataId Database ID. - * @param entryId Entry ID or, if creating, timemodified. - * @param fieldId Field ID. - * @param files List of files. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - storeFiles(dataId: number, entryId: number, fieldId: number, files: any[], siteId?: string): Promise { - // Get the folder where to store the files. - return this.dataOffline.getEntryFieldFolder(dataId, entryId, fieldId, siteId).then((folderPath) => { - return this.fileUploaderProvider.storeFilesToUpload(folderPath, files); - }); - } - - /** - * Upload or store some files, depending if the user is offline or not. - * - * @param dataId Database ID. - * @param itemId Draft ID to use. Undefined or 0 to create a new draft ID. - * @param entryId Entry ID or, if creating, timemodified. - * @param fieldId Field ID. - * @param files List of files. - * @param offline True if files sould be stored for offline, false to upload them. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success. - */ - uploadOrStoreFiles(dataId: number, itemId: number = 0, entryId: number, fieldId: number, files: any[], offline: boolean, - siteId?: string): Promise { - if (files.length) { - if (offline) { - return this.storeFiles(dataId, entryId, fieldId, files, siteId); - } - - return this.fileUploaderProvider.uploadOrReuploadFiles(files, AddonModDataProvider.COMPONENT, itemId, siteId); - } - - return Promise.resolve(0); - } -} diff --git a/src/addon/mod/data/providers/link-handler.ts b/src/addon/mod/data/providers/link-handler.ts deleted file mode 100644 index 30879d5ec..000000000 --- a/src/addon/mod/data/providers/link-handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModDataProvider } from './data'; - -/** - * Handler to treat links to data. - */ -@Injectable() -export class AddonModDataLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModDataLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, - protected dataProvider: AddonModDataProvider) { - super(courseHelper, 'AddonModData', 'data', 'd'); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.dataProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/data/providers/list-link-handler.ts b/src/addon/mod/data/providers/list-link-handler.ts deleted file mode 100644 index 5fb1a21c7..000000000 --- a/src/addon/mod/data/providers/list-link-handler.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModDataProvider } from './data'; - -/** - * Handler to treat links to data list page. - */ -@Injectable() -export class AddonModDataListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModDataListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected dataProvider: AddonModDataProvider) { - super(linkHelper, translate, 'AddonModData', 'data'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return this.dataProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/data/providers/module-handler.ts b/src/addon/mod/data/providers/module-handler.ts deleted file mode 100644 index b158e1828..000000000 --- a/src/addon/mod/data/providers/module-handler.ts +++ /dev/null @@ -1,91 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModDataIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModDataProvider } from './data'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support data modules. - */ -@Injectable() -export class AddonModDataModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModData'; - modName = 'data'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, - [CoreConstants.FEATURE_RATE]: true, - [CoreConstants.FEATURE_COMMENT]: true - }; - - constructor(private courseProvider: CoreCourseProvider, private dataProvider: AddonModDataProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return this.dataProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_data-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModDataIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModDataIndexComponent; - } -} diff --git a/src/addon/mod/data/providers/offline.ts b/src/addon/mod/data/providers/offline.ts deleted file mode 100644 index d58794859..000000000 --- a/src/addon/mod/data/providers/offline.ts +++ /dev/null @@ -1,346 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreFileProvider } from '@providers/file'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { SQLiteDB } from '@classes/sqlitedb'; -import { AddonModDataSubfieldData } from './data'; - -/** - * Entry action stored offline. - */ -export interface AddonModDataOfflineAction { - dataid: number; - courseid: number; - groupid: number; - action: string; - entryid: number; // Negative for offline entries. - fields: AddonModDataSubfieldData[]; - timemodified: number; -} - -/** - * Service to handle Offline data. - */ -@Injectable() -export class AddonModDataOfflineProvider { - - protected logger; - - // Variables for database. - static DATA_ENTRY_TABLE = 'addon_mod_data_entry_1'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModDataOfflineProvider', - version: 1, - tables: [ - { - name: AddonModDataOfflineProvider.DATA_ENTRY_TABLE, - columns: [ - { - name: 'dataid', - type: 'INTEGER' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'groupid', - type: 'INTEGER' - }, - { - name: 'action', - type: 'TEXT' - }, - { - name: 'entryid', - type: 'INTEGER' - }, - { - name: 'fields', - type: 'TEXT' - }, - { - name: 'timemodified', - type: 'INTEGER' - } - ], - primaryKeys: ['dataid', 'entryid', 'action'] - } - ], - migrate(db: SQLiteDB, oldVersion: number, siteId: string): Promise | void { - if (oldVersion == 0) { - // Move the records from the old table. - const newTable = AddonModDataOfflineProvider.DATA_ENTRY_TABLE; - const oldTable = 'addon_mod_data_entry'; - - return db.tableExists(oldTable).then(() => { - return db.insertRecordsFrom(newTable, oldTable).then(() => { - return db.dropTable(oldTable); - }); - }).catch(() => { - // Old table does not exist, ignore. - }); - } - } - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider, - private fileProvider: CoreFileProvider, private fileUploaderProvider: CoreFileUploaderProvider, - private utils: CoreUtilsProvider) { - this.logger = logger.getInstance('AddonModDataOfflineProvider'); - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete all the actions of an entry. - * - * @param dataId Database ID. - * @param entryId Database entry ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteAllEntryActions(dataId: number, entryId: number, siteId?: string): Promise { - return this.getEntryActions(dataId, entryId, siteId).then((actions) => { - const promises = []; - - actions.forEach((action) => { - promises.push(this.deleteEntry(dataId, entryId, action.action, siteId)); - }); - - return Promise.all(promises); - }); - } - - /** - * Delete an stored entry. - * - * @param dataId Database ID. - * @param entryId Database entry Id. - * @param action Action to be done - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteEntry(dataId: number, entryId: number, action: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.deleteEntryFiles(dataId, entryId, action, site.id).then(() => { - return site.getDb().deleteRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId, - action: action}); - }); - }); - } - - /** - * Delete entry offline files. - * - * @param dataId Database ID. - * @param entryId Database entry ID. - * @param action Action to be done. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - protected deleteEntryFiles(dataId: number, entryId: number, action: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.getEntry(dataId, entryId, action, site.id).then((entry) => { - if (!entry.fields) { - return; - } - - const promises = []; - - entry.fields.forEach((field) => { - const value = this.textUtils.parseJSON(field.value); - if (!value.offline) { - return; - } - - const promise = this.getEntryFieldFolder(dataId, entryId, field.fieldid, site.id).then((folderPath) => { - return this.fileUploaderProvider.getStoredFiles(folderPath); - }).then((files) => { - return this.fileUploaderProvider.clearTmpFiles(files); - }).catch(() => { - // Files not found, ignore. - }); - - promises.push(promise); - }); - - return Promise.all(promises); - }).catch(() => { - // Entry not found, ignore. - }); - }); - } - - /** - * Get all the stored entry data from all the databases. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with entries. - */ - getAllEntries(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getAllRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE); - }).then((entries) => { - return entries.map(this.parseRecord.bind(this)); - }); - } - - /** - * Get all the stored entry actions from a certain database, sorted by modification time. - * - * @param dataId Database ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with entries. - */ - getDatabaseEntries(dataId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId}, 'timemodified'); - }).then((entries) => { - return entries.map(this.parseRecord.bind(this)); - }); - } - - /** - * Get an stored entry data. - * - * @param dataId Database ID. - * @param entryId Database entry Id. - * @param action Action to be done - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with entry. - */ - getEntry(dataId: number, entryId: number, action: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecord(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId, - action: action}); - }).then((entry) => { - return this.parseRecord(entry); - }); - } - - /** - * Get an all stored entry actions data. - * - * @param dataId Database ID. - * @param entryId Database entry Id. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with entry actions. - */ - getEntryActions(dataId: number, entryId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId}); - }).then((entries) => { - return entries.map(this.parseRecord.bind(this)); - }); - } - - /** - * Check if there are offline entries to send. - * - * @param dataId Database ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline answers, false otherwise. - */ - hasOfflineData(dataId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.utils.promiseWorks( - site.getDb().recordExists(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, {dataid: dataId}) - ); - }); - } - - /** - * Get the path to the folder where to store files for offline files in a database. - * - * @param dataId Database ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - protected getDatabaseFolder(dataId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - - const siteFolderPath = this.fileProvider.getSiteFolder(site.getId()), - folderPath = 'offlinedatabase/' + dataId; - - return this.textUtils.concatenatePaths(siteFolderPath, folderPath); - }); - } - - /** - * Get the path to the folder where to store files for a new offline entry. - * - * @param dataId Database ID. - * @param entryId The ID of the entry. - * @param fieldId Field ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getEntryFieldFolder(dataId: number, entryId: number, fieldId: number, siteId?: string): Promise { - return this.getDatabaseFolder(dataId, siteId).then((folderPath) => { - return this.textUtils.concatenatePaths(folderPath, entryId + '_' + fieldId); - }); - } - - /** - * Parse "fields" of an offline record. - * - * @param record Record object - * @return Record object with columns parsed. - */ - protected parseRecord(record: any): AddonModDataOfflineAction { - record.fields = this.textUtils.parseJSON(record.fields); - - return record; - } - - /** - * Save an entry data to be sent later. - * - * @param dataId Database ID. - * @param entryId Database entry Id. If action is add entryId should be 0 and -timemodified will be used. - * @param action Action to be done to the entry: [add, edit, delete, approve, disapprove] - * @param courseId Course ID of the database. - * @param groupId Group ID. Only provided when adding. - * @param fields Array of field data of the entry if needed. - * @param timemodified The time the entry was modified. If not defined, current time. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - saveEntry(dataId: number, entryId: number, action: string, courseId: number, groupId?: number, - fields?: AddonModDataSubfieldData[], timemodified?: number, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - timemodified = timemodified || new Date().getTime(); - entryId = typeof entryId == 'undefined' || entryId === null ? -timemodified : entryId; - const entry = { - dataid: dataId, - courseid: courseId, - groupid: groupId, - action: action, - entryid: entryId, - fields: JSON.stringify(fields || []), - timemodified: timemodified - }; - - return site.getDb().insertRecord(AddonModDataOfflineProvider.DATA_ENTRY_TABLE, entry); - }); - } - -} diff --git a/src/addon/mod/data/providers/prefetch-handler.ts b/src/addon/mod/data/providers/prefetch-handler.ts deleted file mode 100644 index 4355a8fe6..000000000 --- a/src/addon/mod/data/providers/prefetch-handler.ts +++ /dev/null @@ -1,345 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { AddonModDataProvider, AddonModDataEntry } from './data'; -import { AddonModDataSyncProvider } from './sync'; -import { AddonModDataHelperProvider } from './helper'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch databases. - */ -@Injectable() -export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModData'; - modName = 'data'; - component = AddonModDataProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^entries$|^gradeitems$|^outcomes$|^comments$|^ratings/; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected dataProvider: AddonModDataProvider, - protected timeUtils: CoreTimeUtilsProvider, - protected dataHelper: AddonModDataHelperProvider, - protected groupsProvider: CoreGroupsProvider, - protected commentsProvider: CoreCommentsProvider, - protected syncProvider: AddonModDataSyncProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Retrieves all the entries for all the groups and then returns only unique entries. - * - * @param dataId Database Id. - * @param groups Array of groups in the activity. - * @param options Other options. - * @return All unique entries. - */ - protected getAllUniqueEntries(dataId: number, groups: any[], options: CoreSitesCommonWSOptions = {}) - : Promise { - - const promises = groups.map((group) => { - return this.dataProvider.fetchAllEntries(dataId, { - groupId: group.id, - ...options, // Include all options. - }); - }); - - return Promise.all(promises).then((responses) => { - const uniqueEntries = {}; - - responses.forEach((groupEntries) => { - groupEntries.forEach((entry) => { - uniqueEntries[entry.id] = entry; - }); - }); - - return this.utils.objectToArray(uniqueEntries); - }); - } - - /** - * Helper function to get all database info just once. - * - * @param module Module to get the files. - * @param courseId Course ID the module belongs to. - * @param omitFail True to always return even if fails. Default false. - * @param options Other options. - * @return Promise resolved with the info fetched. - */ - protected getDatabaseInfoHelper(module: any, courseId: number, omitFail: boolean, options: CoreSitesCommonWSOptions = {}) - : Promise { - let database, - groups = [], - entries = [], - files = []; - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - return this.dataProvider.getDatabase(courseId, module.id, options).then((data) => { - files = this.getIntroFilesFromInstance(module, data); - database = data; - - return this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, options.siteId).then((groupInfo) => { - if (!groupInfo.groups || groupInfo.groups.length == 0) { - groupInfo.groups = [{id: 0}]; - } - groups = groupInfo.groups; - - return this.getAllUniqueEntries(database.id, groups, options); - }); - }).then((uniqueEntries) => { - entries = uniqueEntries; - files = files.concat(this.getEntriesFiles(entries)); - - return { - database: database, - groups: groups, - entries: entries, - files: files - }; - }).catch((message): any => { - if (omitFail) { - // Any error, return the info we have. - return { - database: database, - groups: groups, - entries: entries, - files: files - }; - } - - return Promise.reject(message); - }); - } - - /** - * Returns the file contained in the entries. - * - * @param entries List of entries to get files from. - * @return List of files. - */ - protected getEntriesFiles(entries: AddonModDataEntry[]): any[] { - let files = []; - - entries.forEach((entry) => { - this.utils.objectToArray(entry.contents).forEach((content) => { - files = files.concat(content.files); - }); - }); - - return files; - } - - /** - * Get the list of downloadable files. - * - * @param module Module to get the files. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the list of files. - */ - getFiles(module: any, courseId: number, single?: boolean): Promise { - return this.getDatabaseInfoHelper(module, courseId, true).then((info) => { - return info.files; - }); - } - - /** - * Returns data intro files. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @return Promise resolved with list of intro files. - */ - getIntroFiles(module: any, courseId: number): Promise { - return this.dataProvider.getDatabase(courseId, module.id).catch(() => { - // Not found, return undefined so module description is used. - }).then((data) => { - return this.getIntroFilesFromInstance(module, data); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.dataProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const promises = []; - promises.push(this.dataProvider.invalidateDatabaseData(courseId)); - promises.push(this.dataProvider.invalidateDatabaseAccessInformationData(module.instance)); - - return Promise.all(promises); - } - - /** - * Check if a database is downloadable. - * A database isn't downloadable if it's not open yet. - * - * @param module Module to check. - * @param courseId Course ID the module belongs to. - * @return Promise resolved with true if downloadable, resolved with false otherwise. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - return this.dataProvider.getDatabase(courseId, module.id, { - readingStrategy: CoreSitesReadingStrategy.PreferCache, - }).then((database) => { - return this.dataProvider.getDatabaseAccessInformation(database.id, {cmId: module.id}).then((accessData) => { - // Check if database is restricted by time. - if (!accessData.timeavailable) { - const time = this.timeUtils.timestamp(); - - // It is restricted, checking times. - if (database.timeavailablefrom && time < database.timeavailablefrom) { - return false; - } - if (database.timeavailableto && time > database.timeavailableto) { - return false; - } - } - - return true; - }); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.dataProvider.isPluginEnabled(); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchDatabase.bind(this)); - } - - /** - * Prefetch a database. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchDatabase(module: any, courseId: number, single: boolean, siteId: string): Promise { - const options = { - cmId: module.id, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - return this.getDatabaseInfoHelper(module, courseId, false, options).then((info) => { - // Prefetch the database data. - const database = info.database, - commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(), - promises = []; - - promises.push(this.dataProvider.getFields(database.id, options)); - - promises.push(this.filepoolProvider.addFilesToQueue(siteId, info.files, this.component, module.id)); - - info.groups.forEach((group) => { - promises.push(this.dataProvider.getDatabaseAccessInformation(database.id, { - groupId: group.id, - ...options, // Include all options. - })); - }); - - info.entries.forEach((entry) => { - promises.push(this.dataProvider.getEntry(database.id, entry.id, options)); - - if (commentsEnabled && database.comments) { - promises.push(this.commentsProvider.getComments('module', database.coursemodule, 'mod_data', entry.id, - 'database_entry', 0, siteId)); - } - }); - - // Add Basic Info to manage links. - promises.push(this.courseProvider.getModuleBasicInfoByInstance(database.id, 'data', siteId)); - - return Promise.all(promises); - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - const promises = [ - this.syncProvider.syncDatabase(module.instance, siteId), - this.syncProvider.syncRatings(module.id, true, siteId) - ]; - - return Promise.all(promises).then((results) => { - return results.reduce((a, b) => ({ - updated: a.updated || b.updated, - warnings: (a.warnings || []).concat(b.warnings || []), - }), {updated: false}); - }); - } -} diff --git a/src/addon/mod/data/providers/show-link-handler.ts b/src/addon/mod/data/providers/show-link-handler.ts deleted file mode 100644 index 5bb471762..000000000 --- a/src/addon/mod/data/providers/show-link-handler.ts +++ /dev/null @@ -1,105 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonModDataProvider } from './data'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Content links handler for database show entry. - * Match mod/data/view.php?d=6&rid=5 with a valid data id and entryid. - */ -@Injectable() -export class AddonModDataShowLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModDataShowLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModData'; - pattern = /\/mod\/data\/view\.php.*([\?\&](d|rid|page|group|mode)=\d+)/; - priority = 50; // Higher priority than the default link handler for view.php. - - constructor(private linkHelper: CoreContentLinksHelperProvider, private dataProvider: AddonModDataProvider, - private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - dataId = parseInt(params.d, 10), - rId = parseInt(params.rid, 10) || false, - group = parseInt(params.group, 10) || false, - page = parseInt(params.page, 10) || false; - - this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => { - const pageParams = { - module: module, - courseId: module.course - }; - - if (group) { - pageParams['group'] = group; - } - - if (params.mode && params.mode == 'single') { - pageParams['offset'] = page || 0; - } else if (rId) { - pageParams['entryId'] = rId; - } - - return this.linkHelper.goInSite(navCtrl, 'AddonModDataEntryPage', pageParams, siteId); - }).finally(() => { - // Just in case. In fact we need to dismiss the modal before showing a toast or error message. - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - if (typeof params.d == 'undefined') { - // Id not defined. Cannot treat the URL. - return false; - } - - if ((!params.mode || params.mode != 'single') && typeof params.rid == 'undefined') { - return false; - } - - return this.dataProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/mod/data/providers/sync-cron-handler.ts b/src/addon/mod/data/providers/sync-cron-handler.ts deleted file mode 100644 index 1cc7f19c2..000000000 --- a/src/addon/mod/data/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModDataSyncProvider } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModDataSyncCronHandler implements CoreCronHandler { - name = 'AddonModDataSyncCronHandler'; - - constructor(private dataSync: AddonModDataSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.dataSync.syncAllDatabases(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.dataSync.syncInterval; - } -} diff --git a/src/addon/mod/data/providers/sync.ts b/src/addon/mod/data/providers/sync.ts deleted file mode 100644 index dbc3792dd..000000000 --- a/src/addon/mod/data/providers/sync.ts +++ /dev/null @@ -1,442 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncBaseProvider } from '@classes/base-sync'; -import { CoreAppProvider } from '@providers/app'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { AddonModDataOfflineProvider, AddonModDataOfflineAction } from './offline'; -import { AddonModDataProvider } from './data'; -import { AddonModDataHelperProvider } from './helper'; -import { CoreEventsProvider } from '@providers/events'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; - -/** - * Service to sync databases. - */ -@Injectable() -export class AddonModDataSyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_data_autom_synced'; - protected componentTranslate: string; - - constructor(protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider, - protected appProvider: CoreAppProvider, private dataOffline: AddonModDataOfflineProvider, - private eventsProvider: CoreEventsProvider, private dataProvider: AddonModDataProvider, - protected translate: TranslateService, private utils: CoreUtilsProvider, courseProvider: CoreCourseProvider, - syncProvider: CoreSyncProvider, protected textUtils: CoreTextUtilsProvider, timeUtils: CoreTimeUtilsProvider, - private dataHelper: AddonModDataHelperProvider, private logHelper: CoreCourseLogHelperProvider, - private ratingSync: CoreRatingSyncProvider) { - super('AddonModDataSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils); - - this.componentTranslate = courseProvider.translateModuleName('data'); - } - - /** - * Check if a database has data to synchronize. - * - * @param dataId Database ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has data to sync, false otherwise. - */ - hasDataToSync(dataId: number, siteId?: string): Promise { - return this.dataOffline.hasOfflineData(dataId, siteId); - } - - /** - * Try to synchronize all the databases in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllDatabases(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all databases', this.syncAllDatabasesFunc.bind(this), [force], siteId); - } - - /** - * Sync all pending databases on a site. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllDatabasesFunc(siteId?: string, force?: boolean): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - // Get all data answers pending to be sent in the site. - promises.push(this.dataOffline.getAllEntries(siteId).then((offlineActions) => { - const promises = {}; - - // Do not sync same database twice. - offlineActions.forEach((action) => { - if (typeof promises[action.dataid] != 'undefined') { - return; - } - - promises[action.dataid] = force ? this.syncDatabase(action.dataid, siteId) : - this.syncDatabaseIfNeeded(action.dataid, siteId); - - promises[action.dataid].then((result) => { - if (result && result.updated) { - // Sync done. Send event. - this.eventsProvider.trigger(AddonModDataSyncProvider.AUTO_SYNCED, { - dataId: action.dataid, - warnings: result.warnings - }, siteId); - } - }); - }); - - // Promises will be an object so, convert to an array first; - return Promise.all(this.utils.objectToArray(promises)); - })); - - promises.push(this.syncRatings(undefined, force, siteId)); - - return Promise.all(promises); - } - - /** - * Sync a database only if a certain time has passed since the last time. - * - * @param dataId Database ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is synced or if it doesn't need to be synced. - */ - syncDatabaseIfNeeded(dataId: number, siteId?: string): Promise { - return this.isSyncNeeded(dataId, siteId).then((needed) => { - if (needed) { - return this.syncDatabase(dataId, siteId); - } - }); - } - - /** - * Synchronize a data. - * - * @param dataId Data ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncDatabase(dataId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (this.isSyncing(dataId, siteId)) { - // There's already a sync ongoing for this data and user, return the promise. - return this.getOngoingSync(dataId, siteId); - } - - // Verify that data isn't blocked. - if (this.syncProvider.isBlocked(AddonModDataProvider.COMPONENT, dataId, siteId)) { - this.logger.debug(`Cannot sync database '${dataId}' because it is blocked.`); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug(`Try to sync data '${dataId}' in site ${siteId}'`); - - let courseId, - data; - const result = { - warnings: [], - updated: false - }; - - // Sync offline logs. - const syncPromise = this.logHelper.syncIfNeeded(AddonModDataProvider.COMPONENT, dataId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Get answers to be sent. - return this.dataOffline.getDatabaseEntries(dataId, siteId).catch(() => { - // No offline data found, return empty object. - return []; - }); - }).then((offlineActions: AddonModDataOfflineAction[]) => { - if (!offlineActions.length) { - // Nothing to sync. - return; - } - - if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - courseId = offlineActions[0].courseid; - - // Send the answers. - return this.dataProvider.getDatabaseById(courseId, dataId, {siteId}).then((database) => { - data = database; - - const offlineEntries = {}; - - offlineActions.forEach((entry) => { - if (typeof offlineEntries[entry.entryid] == 'undefined') { - offlineEntries[entry.entryid] = []; - } - offlineEntries[entry.entryid].push(entry); - }); - - const promises = this.utils.objectToArray(offlineEntries).map((entryActions) => { - return this.syncEntry(data, entryActions, result, siteId); - }); - - return Promise.all(promises); - }).then(() => { - if (result.updated) { - // Data has been sent to server. Now invalidate the WS calls. - return this.dataProvider.invalidateContent(data.cmid, courseId, siteId).catch(() => { - // Ignore errors. - }); - } - }); - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(dataId, siteId); - }).then(() => { - return result; - }); - - return this.addOngoingSync(dataId, syncPromise, siteId); - } - - /** - * Synchronize an entry. - * - * @param data Database. - * @param entryActions Entry actions. - * @param result Object with the result of the sync. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - protected syncEntry(data: any, entryActions: AddonModDataOfflineAction[], result: any, siteId?: string): Promise { - let discardError; - let timePromise; - let entryId = entryActions[0].entryid; - let offlineId; - let deleted = false; - - const editAction = entryActions.find((action) => action.action == 'add' || action.action == 'edit'); - const approveAction = entryActions.find((action) => action.action == 'approve' || action.action == 'disapprove'); - const deleteAction = entryActions.find((action) => action.action == 'delete'); - const options = { - cmId: data.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - if (entryId > 0) { - timePromise = this.dataProvider.getEntry(data.id, entryId, options).then((entry) => { - return entry.entry.timemodified; - }).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means the entry has been deleted. - return Promise.resolve(-1); - } - - return Promise.reject(error); - }); - } else if (editAction) { - // New entry. - offlineId = entryId; - timePromise = Promise.resolve(0); - } else { - // New entry but the add action is missing, discard. - timePromise = Promise.resolve(-1); - } - - return timePromise.then((timemodified) => { - if (timemodified < 0 || timemodified >= entryActions[0].timemodified) { - // The entry was not found in Moodle or the entry has been modified, discard the action. - result.updated = true; - discardError = this.translate.instant('addon.mod_data.warningsubmissionmodified'); - - return this.dataOffline.deleteAllEntryActions(data.id, entryId, siteId); - } - - if (deleteAction) { - return this.dataProvider.deleteEntryOnline(entryId, siteId).then(() => { - deleted = true; - }).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means it cannot be performed. Discard. - discardError = this.textUtils.getErrorMessageFromError(error); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }).then(() => { - // Delete the offline data. - result.updated = true; - - return this.dataOffline.deleteAllEntryActions(deleteAction.dataid, deleteAction.entryid, siteId); - }); - } - - let editPromise; - - if (editAction) { - editPromise = Promise.all(editAction.fields.map((field) => { - // Upload Files if asked. - const value = this.textUtils.parseJSON(field.value); - if (value.online || value.offline) { - let files = value.online || []; - const fileProm = value.offline ? - this.dataHelper.getStoredFiles(editAction.dataid, entryId, field.fieldid) : - Promise.resolve([]); - - return fileProm.then((offlineFiles) => { - files = files.concat(offlineFiles); - - return this.dataHelper.uploadOrStoreFiles(editAction.dataid, 0, entryId, field.fieldid, files, - false, siteId).then((filesResult) => { - field.value = JSON.stringify(filesResult); - }); - }); - } - })).then(() => { - if (editAction.action == 'add') { - return this.dataProvider.addEntryOnline(editAction.dataid, editAction.fields, editAction.groupid, siteId) - .then((result) => { - entryId = result.newentryid; - }); - } else { - return this.dataProvider.editEntryOnline(entryId, editAction.fields, siteId); - } - }).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means it cannot be performed. Discard. - discardError = this.textUtils.getErrorMessageFromError(error); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }).then(() => { - // Delete the offline data. - result.updated = true; - - return this.dataOffline.deleteEntry(editAction.dataid, editAction.entryid, editAction.action, siteId); - }); - } else { - editPromise = Promise.resolve(); - } - - if (approveAction) { - editPromise = editPromise.then(() => { - return this.dataProvider.approveEntryOnline(entryId, approveAction.action == 'approve', siteId); - }).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means it cannot be performed. Discard. - discardError = this.textUtils.getErrorMessageFromError(error); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }).then(() => { - // Delete the offline data. - result.updated = true; - - return this.dataOffline.deleteEntry(approveAction.dataid, approveAction.entryid, approveAction.action, siteId); - }); - } - - return editPromise; - - }).then(() => { - if (discardError) { - // Submission was discarded, add a warning. - const message = this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.name, - error: discardError - }); - - if (result.warnings.indexOf(message) == -1) { - result.warnings.push(message); - } - } - - // Sync done. Send event. - this.eventsProvider.trigger(AddonModDataSyncProvider.AUTO_SYNCED, { - dataId: data.id, - entryId: entryId, - offlineEntryId: offlineId, - warnings: result.warnings, - deleted: deleted - }, siteId); - }); - } - - /** - * Synchronize offline ratings. - * - * @param cmId Course module to be synced. If not defined, sync all databases. - * @param force Wether to force sync not depending on last execution. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncRatings(cmId?: number, force?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.ratingSync.syncRatings('mod_data', 'entry', 'module', cmId, 0, force, siteId).then((results) => { - let updated = false; - const warnings = []; - const promises = []; - - results.forEach((result) => { - promises.push(this.dataProvider.getDatabase(result.itemSet.courseId, result.itemSet.instanceId, {siteId}) - .then((data) => { - const promises = []; - - if (result.updated.length) { - updated = true; - - // Invalidate entry of updated ratings. - result.updated.forEach((itemId) => { - promises.push(this.dataProvider.invalidateEntryData(data.id, itemId, siteId)); - }); - } - - if (result.warnings.length) { - result.warnings.forEach((warning) => { - warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.name, - error: warning - })); - }); - } - - return this.utils.allPromises(promises); - })); - }); - - return Promise.all(promises).then(() => { - return { updated, warnings }; - }); - }); - } -} diff --git a/src/addon/mod/data/providers/tag-area-handler.ts b/src/addon/mod/data/providers/tag-area-handler.ts deleted file mode 100644 index 1668554c1..000000000 --- a/src/addon/mod/data/providers/tag-area-handler.ts +++ /dev/null @@ -1,58 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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 { AddonModDataProvider } from './data'; - -/** - * Handler to support tags. - */ -@Injectable() -export class AddonModDataTagAreaHandler implements CoreTagAreaHandler { - name = 'AddonModDataTagAreaHandler'; - type = 'mod_data/data_records'; - - constructor(private tagHelper: CoreTagHelperProvider, private dataProvider: AddonModDataProvider) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.dataProvider.isPluginEnabled(); - } - - /** - * Parses the rendered content of a tag index and returns the items. - * - * @param content Rendered content. - * @return Area items (or promise resolved with the items). - */ - parseContent(content: string): any[] | Promise { - return this.tagHelper.parseFeedContent(content); - } - - /** - * Get the component to use to display items. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return CoreTagFeedComponent; - } -} diff --git a/src/addon/mod/feedback/components/components.module.ts b/src/addon/mod/feedback/components/components.module.ts deleted file mode 100644 index d7d016bc9..000000000 --- a/src/addon/mod/feedback/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModFeedbackIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModFeedbackIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModFeedbackIndexComponent - ], - entryComponents: [ - AddonModFeedbackIndexComponent - ] -}) -export class AddonModFeedbackComponentsModule {} diff --git a/src/addon/mod/feedback/components/index/addon-mod-feedback-index.html b/src/addon/mod/feedback/components/index/addon-mod-feedback-index.html deleted file mode 100644 index d4f10a815..000000000 --- a/src/addon/mod/feedback/components/index/addon-mod-feedback-index.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - -

{{ 'addon.mod_feedback.completed_feedbacks' | translate }}

- {{feedback.completedCount}} -
- -

{{ 'addon.mod_feedback.show_nonrespondents' | translate }}

-
- -

{{ 'addon.mod_feedback.questions' | translate }}

- {{feedback.itemsCount}} -
- - - - - - - - - -
- - {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} -
- -
- - {{ 'addon.mod_feedback.feedback_is_not_open' | translate }} -
- -
- - {{ 'addon.mod_feedback.this_feedback_is_already_submitted' | translate }} -
- - - -

{{ 'addon.mod_feedback.feedbackopen' | translate }}

-

{{overview.openTimeReadable}}

-
- -

{{ 'addon.mod_feedback.feedbackclose' | translate }}

-

{{overview.closeTimeReadable}}

-
- -

{{ 'addon.mod_feedback.page_after_submit' | translate }}

- -
- - -

{{ 'addon.mod_feedback.mode' | translate }}

-

{{ 'addon.mod_feedback.anonymous' | translate }}

-

{{ 'addon.mod_feedback.non_anonymous' | translate }}

-
- - - - - - - - - - - - -
-
-
-
- - - - - - - -
- - {{ warning }} -
- - - -

{{item.number}}.

-

- - -
    -
  • {{ data }}
  • -
-

{{ 'addon.mod_feedback.average' | translate }}: {{item.average | number : '1.2-2'}}

-
- -
    - -
  • {{ data }}
  • -
    -
-
- - -

{{ 'addon.mod_feedback.average' | translate }}: {{item.average | number : '1.2-2'}}

-
-
-
-
-
-
-
diff --git a/src/addon/mod/feedback/components/index/index.ts b/src/addon/mod/feedback/components/index/index.ts deleted file mode 100644 index 0f58c27d7..000000000 --- a/src/addon/mod/feedback/components/index/index.ts +++ /dev/null @@ -1,482 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Optional, Injector, ViewChild } from '@angular/core'; -import { Content, NavController } from 'ionic-angular'; -import { CoreGroupInfo, CoreGroupsProvider } from '@providers/groups'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { AddonModFeedbackProvider } from '../../providers/feedback'; -import { AddonModFeedbackHelperProvider } from '../../providers/helper'; -import { AddonModFeedbackOfflineProvider } from '../../providers/offline'; -import { AddonModFeedbackSyncProvider } from '../../providers/sync'; -import { CoreTabsComponent } from '@components/tabs/tabs'; - -/** - * Component that displays a feedback index page. - */ -@Component({ - selector: 'addon-mod-feedback-index', - templateUrl: 'addon-mod-feedback-index.html', -}) -export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivityComponent { - @ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent; - - @Input() tab = 'overview'; - @Input() group = 0; - - component = AddonModFeedbackProvider.COMPONENT; - moduleName = 'feedback'; - - access = { - canviewreports: false, - canviewanalysis: false, - isempty: true - }; - feedback: any; - goPage: number; - groupInfo: CoreGroupInfo = { - groups: [], - separateGroups: false, - visibleGroups: false - }; - items: any[]; - overview = { - timeopen: 0, - openTimeReadable: '', - timeclose: 0, - closeTimeReadable: '' - }; - warning = ''; - tabsLoaded = { - overview: false, - analysis: false - }; - showTabs = false; - tabsReady = false; - firstSelectedTab: number; - - protected submitObserver: any; - protected syncEventName = AddonModFeedbackSyncProvider.AUTO_SYNCED; - - constructor(injector: Injector, private feedbackProvider: AddonModFeedbackProvider, @Optional() content: Content, - private feedbackOffline: AddonModFeedbackOfflineProvider, private groupsProvider: CoreGroupsProvider, - private feedbackSync: AddonModFeedbackSyncProvider, protected navCtrl: NavController, - private feedbackHelper: AddonModFeedbackHelperProvider, private timeUtils: CoreTimeUtilsProvider) { - super(injector, content); - - // Listen for form submit events. - this.submitObserver = this.eventsProvider.on(AddonModFeedbackProvider.FORM_SUBMITTED, (data) => { - if (this.feedback && data.feedbackId == this.feedback.id) { - this.tabsLoaded['analysis'] = false; - this.tabsLoaded['overview'] = false; - this.loaded = false; - - let promise; - - // Prefetch data if needed. - if (!data.offline && this.isPrefetched()) { - promise = this.feedbackSync.prefetchAfterUpdate(this.module, this.courseId).catch(() => { - // Ignore errors. - }); - } else { - promise = Promise.resolve(); - } - - promise.then(() => { - // Load the right tab. - if (data.tab != this.tab) { - this.tabChanged(data.tab); - } else { - this.loadContent(true); - } - }); - } - }, this.siteId); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.loadContent(false, true).then(() => { - if (this.feedback) { - this.feedbackProvider.logView(this.feedback.id, this.feedback.name).catch(() => { - // Ignore errors. - }); - } - }).finally(() => { - this.tabsReady = true; - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.feedbackProvider.invalidateFeedbackData(this.courseId)); - if (this.feedback) { - promises.push(this.feedbackProvider.invalidateFeedbackAccessInformationData(this.feedback.id)); - promises.push(this.feedbackProvider.invalidateAnalysisData(this.feedback.id)); - promises.push(this.groupsProvider.invalidateActivityAllowedGroups(this.feedback.coursemodule)); - promises.push(this.groupsProvider.invalidateActivityGroupMode(this.feedback.coursemodule)); - promises.push(this.feedbackProvider.invalidateResumePageData(this.feedback.id)); - } - - this.tabsLoaded['analysis'] = false; - this.tabsLoaded['overview'] = false; - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (this.feedback && syncEventData.feedbackId == this.feedback.id) { - // Refresh the data. - this.domUtils.scrollToTop(this.content); - - return true; - } - - return false; - } - - /** - * Download feedback contents. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - return this.feedbackProvider.getFeedback(this.courseId, this.module.id).then((feedback) => { - this.feedback = feedback; - - this.description = feedback.intro || feedback.description; - this.dataRetrieved.emit(feedback); - - if (sync) { - // Try to synchronize the feedback. - return this.syncActivity(showErrors); - } - }).then(() => { - // Check if there are answers stored in offline. - return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, {cmId: this.module.id}); - }).then((accessData) => { - this.access = accessData; - this.showTabs = (accessData.canviewreports || accessData.canviewanalysis) && !accessData.isempty; - - this.firstSelectedTab = 0; - if (this.tab == 'analysis') { - this.firstSelectedTab = 1; - - return this.fetchFeedbackAnalysisData(this.access); - } - - return this.fetchFeedbackOverviewData(this.access); - }).finally(() => { - // Now fill the context menu. - this.fillContextMenu(refresh); - - if (this.feedback) { - // Check if there are responses stored in offline. - return this.feedbackOffline.hasFeedbackOfflineData(this.feedback.id).then((hasOffline) => { - this.hasOffline = hasOffline; - }); - } - }); - } - - /** - * Convenience function to get feedback overview data. - * - * @param accessData Retrieved access data. - * @return Resolved when done. - */ - protected fetchFeedbackOverviewData(accessData: any): Promise { - const promises = []; - - if (accessData.cancomplete && accessData.cansubmit && accessData.isopen) { - promises.push(this.feedbackProvider.getResumePage(this.feedback.id, {cmId: this.module.id}).then((goPage) => { - this.goPage = goPage > 0 ? goPage : false; - })); - } - - if (accessData.canedititems) { - this.overview.timeopen = parseInt(this.feedback.timeopen) * 1000 || 0; - this.overview.openTimeReadable = this.overview.timeopen ? this.timeUtils.userDate(this.overview.timeopen) : ''; - this.overview.timeclose = parseInt(this.feedback.timeclose) * 1000 || 0; - this.overview.closeTimeReadable = this.overview.timeclose ? this.timeUtils.userDate(this.overview.timeclose) : ''; - } - if (accessData.canviewanalysis) { - // Get groups (only for teachers). - promises.push(this.fetchGroupInfo(this.feedback.coursemodule)); - } - - return Promise.all(promises).finally(() => { - this.tabsLoaded['overview'] = true; - }); - } - - /** - * Convenience function to get feedback analysis data. - * - * @param accessData Retrieved access data. - * @return Resolved when done. - */ - protected fetchFeedbackAnalysisData(accessData: any): Promise { - let promise; - - if (accessData.canviewanalysis) { - // Get groups (only for teachers). - promise = this.fetchGroupInfo(this.feedback.coursemodule); - } else { - this.tabChanged('overview'); - promise = Promise.resolve(); - } - - return promise.finally(() => { - this.tabsLoaded['analysis'] = true; - }); - } - - /** - * Fetch Group info data. - * - * @param cmId Course module ID. - * @return Resolved when done. - */ - protected fetchGroupInfo(cmId: number): Promise { - return this.groupsProvider.getActivityGroupInfo(cmId).then((groupInfo) => { - this.groupInfo = groupInfo; - - return this.setGroup(this.groupsProvider.validateGroupId(this.group, groupInfo)); - }); - } - - /** - * Parse the analysis info to show the info correctly formatted. - * - * @param item Item to parse. - * @return Parsed item. - */ - protected parseAnalysisInfo(item: any): any { - switch (item.typ) { - case 'numeric': - item.average = item.data.reduce((prev, current) => { - return prev + parseInt(current, 10); - }, 0) / item.data.length; - item.template = 'numeric'; - break; - - case 'info': - item.data = item.data.map((dataItem) => { - dataItem = this.textUtils.parseJSON(dataItem); - - return typeof dataItem.show != 'undefined' ? dataItem.show : false; - }).filter((dataItem) => { - // Filter false entries. - return dataItem; - }); - - case 'textfield': - case 'textarea': - item.template = 'list'; - break; - - case 'multichoicerated': - case 'multichoice': - item.data = item.data.map((dataItem) => { - dataItem = this.textUtils.parseJSON(dataItem); - - return typeof dataItem.answertext != 'undefined' ? dataItem : false; - }).filter((dataItem) => { - // Filter false entries. - return dataItem; - }); - - // Format labels. - item.labels = item.data.map((dataItem) => { - dataItem.quotient = (dataItem.quotient * 100).toFixed(2); - let label = ''; - - if (typeof dataItem.value != 'undefined') { - label = '(' + dataItem.value + ') '; - } - label += dataItem.answertext; - label += dataItem.quotient > 0 ? ' (' + dataItem.quotient + '%)' : ''; - - return label; - }); - - item.chartData = item.data.map((dataItem) => { - return dataItem.answercount; - }); - - if (item.typ == 'multichoicerated') { - item.average = item.data.reduce((prev, current) => { - return prev + parseFloat(current.avg); - }, 0.0); - } - - const subtype = item.presentation.charAt(0); - - const single = subtype != 'c'; - item.chartType = single ? 'doughnut' : 'bar'; - - item.template = 'chart'; - break; - - default: - break; - } - - return item; - } - - /** - * Function to go to the questions form. - * - * @param preview Preview or edit the form. - */ - gotoAnswerQuestions(preview: boolean = false): void { - const stateParams = { - module: this.module, - moduleId: this.module.id, - courseId: this.courseId, - preview: preview - }; - this.navCtrl.push('AddonModFeedbackFormPage', stateParams); - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - super.ionViewDidEnter(); - - this.tabsComponent && this.tabsComponent.ionViewDidEnter(); - } - - /** - * User left the page that contains the component. - */ - ionViewDidLeave(): void { - super.ionViewDidLeave(); - - this.tabsComponent && this.tabsComponent.ionViewDidLeave(); - } - - /** - * Function to link implemented features. - * - * @param feature Feature to navigate. - */ - openFeature(feature: string): void { - this.feedbackHelper.openFeature(feature, this.navCtrl, this.module, this.courseId, this.group); - } - - /** - * Tab changed, fetch content again. - * - * @param tabName New tab name. - */ - tabChanged(tabName: string): void { - this.tab = tabName; - - if (!this.tabsLoaded[this.tab]) { - this.loadContent(false, false, true); - } - } - - /** - * Set group to see the analysis. - * - * @param groupId Group ID. - * @return Resolved when done. - */ - setGroup(groupId: number): Promise { - this.group = groupId; - - return this.feedbackProvider.getAnalysis(this.feedback.id, {groupId, cmId: this.module.id}).then((analysis) => { - this.feedback.completedCount = analysis.completedcount; - this.feedback.itemsCount = analysis.itemscount; - - if (this.tab == 'analysis') { - let num = 1; - - this.items = analysis.itemsdata.map((item) => { - // Move data inside item. - item.item.data = item.data; - item = item.item; - item.number = num++; - if (item.data && item.data.length) { - return this.parseAnalysisInfo(item); - } - - return false; - }).filter((item) => { - return item; - }); - - this.warning = ''; - if (analysis.warnings.length) { - const warning = analysis.warnings.find((warning) => { - return warning.warningcode == 'insufficientresponsesforthisgroup'; - }); - this.warning = warning && warning.message; - } - } - }); - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.feedbackSync.syncFeedback(this.feedback.id); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return If suceed or not. - */ - protected hasSyncSucceed(result: any): boolean { - return result.updated; - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - this.submitObserver && this.submitObserver.off(); - } -} diff --git a/src/addon/mod/feedback/feedback.module.ts b/src/addon/mod/feedback/feedback.module.ts deleted file mode 100644 index d816ab94a..000000000 --- a/src/addon/mod/feedback/feedback.module.ts +++ /dev/null @@ -1,94 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; -import { AddonModFeedbackComponentsModule } from './components/components.module'; -import { AddonModFeedbackModuleHandler } from './providers/module-handler'; -import { AddonModFeedbackProvider } from './providers/feedback'; -import { AddonModFeedbackLinkHandler } from './providers/link-handler'; -import { AddonModFeedbackAnalysisLinkHandler } from './providers/analysis-link-handler'; -import { AddonModFeedbackShowEntriesLinkHandler } from './providers/show-entries-link-handler'; -import { AddonModFeedbackShowNonRespondentsLinkHandler } from './providers/show-non-respondents-link-handler'; -import { AddonModFeedbackCompleteLinkHandler } from './providers/complete-link-handler'; -import { AddonModFeedbackPrintLinkHandler } from './providers/print-link-handler'; -import { AddonModFeedbackListLinkHandler } from './providers/list-link-handler'; -import { AddonModFeedbackHelperProvider } from './providers/helper'; -import { AddonModFeedbackPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModFeedbackPushClickHandler } from './providers/push-click-handler'; -import { AddonModFeedbackSyncProvider } from './providers/sync'; -import { AddonModFeedbackSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModFeedbackOfflineProvider } from './providers/offline'; - -// List of providers (without handlers). -export const ADDON_MOD_FEEDBACK_PROVIDERS: any[] = [ - AddonModFeedbackProvider, - AddonModFeedbackHelperProvider, - AddonModFeedbackSyncProvider, - AddonModFeedbackOfflineProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModFeedbackComponentsModule - ], - providers: [ - AddonModFeedbackProvider, - AddonModFeedbackHelperProvider, - AddonModFeedbackSyncProvider, - AddonModFeedbackOfflineProvider, - AddonModFeedbackModuleHandler, - AddonModFeedbackPrefetchHandler, - AddonModFeedbackLinkHandler, - AddonModFeedbackAnalysisLinkHandler, - AddonModFeedbackShowEntriesLinkHandler, - AddonModFeedbackShowNonRespondentsLinkHandler, - AddonModFeedbackCompleteLinkHandler, - AddonModFeedbackPrintLinkHandler, - AddonModFeedbackListLinkHandler, - AddonModFeedbackSyncCronHandler, - AddonModFeedbackPushClickHandler - ] -}) -export class AddonModFeedbackModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModFeedbackModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModFeedbackPrefetchHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModFeedbackLinkHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModFeedbackSyncCronHandler, - analysisLinkHandler: AddonModFeedbackAnalysisLinkHandler, - showEntriesLinkHandler: AddonModFeedbackShowEntriesLinkHandler, - showNonRespondentsLinkHandler: AddonModFeedbackShowNonRespondentsLinkHandler, - completeLinkHandler: AddonModFeedbackCompleteLinkHandler, - printLinkHandler: AddonModFeedbackPrintLinkHandler, listLinkHandler: AddonModFeedbackListLinkHandler, - pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModFeedbackPushClickHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(analysisLinkHandler); - contentLinksDelegate.registerHandler(showEntriesLinkHandler); - contentLinksDelegate.registerHandler(showNonRespondentsLinkHandler); - contentLinksDelegate.registerHandler(completeLinkHandler); - contentLinksDelegate.registerHandler(printLinkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - cronDelegate.register(syncHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - } -} diff --git a/src/addon/mod/feedback/lang/en.json b/src/addon/mod/feedback/lang/en.json deleted file mode 100644 index 5e6381eb3..000000000 --- a/src/addon/mod/feedback/lang/en.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "analysis": "Analysis", - "anonymous": "Anonymous", - "anonymous_entries": "Anonymous entries ({{$a}})", - "average": "Average", - "captchaofflinewarning": "Feedback with CAPTCHA cannot be completed offline, or if not configured, or if the server is down.", - "complete_the_form": "Answer the questions", - "completed_feedbacks": "Submitted answers", - "continue_the_form": "Continue answering the questions", - "feedback_is_not_open": "The feedback is not open", - "feedback_submitted_offline": "This feedback has been saved to be submitted later.", - "feedbackclose": "Allow answers to", - "feedbackopen": "Allow answers from", - "mapcourses": "Map feedback to courses", - "maximal": "Maximum", - "minimal": "Minimum", - "mode": "Mode", - "modulenameplural": "Feedback", - "next_page": "Next page", - "non_anonymous": "User's name will be logged and shown with answers", - "non_anonymous_entries": "Non anonymous entries ({{$a}})", - "non_respondents_students": "Non-respondent students ({{$a}})", - "not_selected": "Not selected", - "not_started": "Not started", - "numberoutofrange": "Number out of range", - "overview": "Overview", - "page_after_submit": "Completion message", - "preview": "Preview", - "previous_page": "Previous page", - "questions": "Questions", - "response_nr": "Response number", - "responses": "Responses", - "save_entries": "Submit your answers", - "show_entries": "Show responses", - "show_nonrespondents": "Show non-respondents", - "started": "Started", - "this_feedback_is_already_submitted": "You've already completed this activity." -} \ No newline at end of file diff --git a/src/addon/mod/feedback/pages/attempt/attempt.html b/src/addon/mod/feedback/pages/attempt/attempt.html deleted file mode 100644 index a872de399..000000000 --- a/src/addon/mod/feedback/pages/attempt/attempt.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - {{ attempt.fullname }} - {{ 'addon.mod_feedback.response_nr' |translate }}: {{attempt.number}} - - - - - - - - -

{{attempt.fullname}}

-

{{attempt.timemodified * 1000 | coreFormatDate }}

-
- - -

{{ 'addon.mod_feedback.response_nr' |translate }}: {{attempt.number}} ({{ 'addon.mod_feedback.anonymous' |translate }})

-

{{attempt.timemodified * 1000 | coreFormatDate }}

-
- - - - -

- {{item.itemnumber}}. -

-

-
-
-
-
-
-
diff --git a/src/addon/mod/feedback/pages/attempt/attempt.module.ts b/src/addon/mod/feedback/pages/attempt/attempt.module.ts deleted file mode 100644 index d440ae183..000000000 --- a/src/addon/mod/feedback/pages/attempt/attempt.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModFeedbackComponentsModule } from '../../components/components.module'; -import { AddonModFeedbackAttemptPage } from './attempt'; - -@NgModule({ - declarations: [ - AddonModFeedbackAttemptPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - CorePipesModule, - AddonModFeedbackComponentsModule, - IonicPageModule.forChild(AddonModFeedbackAttemptPage), - TranslateModule.forChild() - ], -}) -export class AddonModFeedbackAttemptPageModule {} diff --git a/src/addon/mod/feedback/pages/attempt/attempt.ts b/src/addon/mod/feedback/pages/attempt/attempt.ts deleted file mode 100644 index d6ba346e9..000000000 --- a/src/addon/mod/feedback/pages/attempt/attempt.ts +++ /dev/null @@ -1,96 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams, NavController } from 'ionic-angular'; -import { AddonModFeedbackProvider } from '../../providers/feedback'; -import { AddonModFeedbackHelperProvider } from '../../providers/helper'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Page that displays a feedback attempt review. - */ -@IonicPage({ segment: 'addon-mod-feedback-attempt' }) -@Component({ - selector: 'page-addon-mod-feedback-attempt', - templateUrl: 'attempt.html', -}) -export class AddonModFeedbackAttemptPage { - - protected feedbackId: number; - protected courseId: number; - - feedback: any; - attempt: any; - items: any; - componentId: number; - component = AddonModFeedbackProvider.COMPONENT; - feedbackLoaded = false; - - constructor(navParams: NavParams, protected feedbackProvider: AddonModFeedbackProvider, protected navCtrl: NavController, - protected domUtils: CoreDomUtilsProvider, protected feedbackHelper: AddonModFeedbackHelperProvider, - protected textUtils: CoreTextUtilsProvider) { - this.feedbackId = navParams.get('feedbackId') || 0; - this.courseId = navParams.get('courseId'); - this.attempt = navParams.get('attempt') || false; - this.componentId = navParams.get('moduleId'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchData(); - } - - /** - * Fetch all the data required for the view. - * - * @return Promise resolved when done. - */ - fetchData(): Promise { - // Get the feedback to be able to now if questions should be autonumbered. - return this.feedbackProvider.getFeedbackById(this.courseId, this.feedbackId).then((feedback) => { - this.feedback = feedback; - - return this.feedbackProvider.getItems(this.feedbackId, {cmId: this.feedback.coursemodule}); - }).then((items) => { - // Add responses and format items. - this.items = items.items.map((item) => { - if (item.typ == 'label') { - item.submittedValue = this.textUtils.replacePluginfileUrls(item.presentation, item.itemfiles); - } else { - for (const x in this.attempt.responses) { - if (this.attempt.responses[x].id == item.id) { - item.submittedValue = this.attempt.responses[x].printval; - break; - } - } - } - - return this.feedbackHelper.getItemForm(item, true); - }); - - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - // Some call failed on first fetch, go back. - this.navCtrl.pop(); - - return Promise.reject(null); - }).finally(() => { - this.feedbackLoaded = true; - }); - } -} diff --git a/src/addon/mod/feedback/pages/form/form.html b/src/addon/mod/feedback/pages/form/form.html deleted file mode 100644 index 99b805762..000000000 --- a/src/addon/mod/feedback/pages/form/form.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - -

{{ 'addon.mod_feedback.mode' | translate }}

-

{{ 'addon.mod_feedback.anonymous' | translate }}

-

{{ 'addon.mod_feedback.non_anonymous' | translate }}

-
- - - - - {{item.itemnumber}}. - - {{item.postfix}} - -
- - -

-
- - - - - -

{{ 'addon.mod_feedback.numberoutofrange' | translate }} [{{item.rangefrom}}, {{item.rangeto}}]

-
- - - - - - - - - - - - - - - - - - - - - - - - -
- - {{ 'addon.mod_feedback.captchaofflinewarning' | translate }} -
-
-
-
-
-
- - - - - - - - - - - - - -
-
- -
- -

{{ 'addon.mod_feedback.this_feedback_is_already_submitted' | translate }}

-

{{ 'addon.mod_feedback.feedback_submitted_offline' | translate }}

-

-
- - - - - - - - - - - -
-
diff --git a/src/addon/mod/feedback/pages/form/form.module.ts b/src/addon/mod/feedback/pages/form/form.module.ts deleted file mode 100644 index 99d0bdce6..000000000 --- a/src/addon/mod/feedback/pages/form/form.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { AddonModFeedbackComponentsModule } from '../../components/components.module'; -import { AddonModFeedbackFormPage } from './form'; - -@NgModule({ - declarations: [ - AddonModFeedbackFormPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - AddonModFeedbackComponentsModule, - IonicPageModule.forChild(AddonModFeedbackFormPage), - TranslateModule.forChild() - ], -}) -export class AddonModFeedbackFormPageModule {} diff --git a/src/addon/mod/feedback/pages/form/form.scss b/src/addon/mod/feedback/pages/form/form.scss deleted file mode 100644 index 496e55b0a..000000000 --- a/src/addon/mod/feedback/pages/form/form.scss +++ /dev/null @@ -1,15 +0,0 @@ -ion-app.app-root page-addon-mod-feedback-form { - .addon-mod_feedback-form-content { - align-self: self-start; - width: 100%; - } - .item-md .addon-mod_feedback-form-content { - @include margin($item-md-padding-media-top, ($item-md-padding-end / 2), $item-md-padding-media-bottom, 0); - } - .item-ios .addon-mod_feedback-form-content { - @include margin($item-ios-padding-media-top, $item-ios-padding-start, $item-ios-padding-media-bottom, 0); - } - .addon-mod_feedback-postfix { - font-size: 1.4rem; - } -} \ No newline at end of file diff --git a/src/addon/mod/feedback/pages/form/form.ts b/src/addon/mod/feedback/pages/form/form.ts deleted file mode 100644 index 601b1a416..000000000 --- a/src/addon/mod/feedback/pages/form/form.ts +++ /dev/null @@ -1,370 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, Optional, NgZone } from '@angular/core'; -import { IonicPage, NavParams, NavController, Content } from 'ionic-angular'; -import { Network } from '@ionic-native/network'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModFeedbackProvider } from '../../providers/feedback'; -import { AddonModFeedbackHelperProvider } from '../../providers/helper'; -import { AddonModFeedbackSyncProvider } from '../../providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreLoginHelperProvider } from '@core/login/providers/helper'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; - -/** - * Page that displays feedback form. - */ -@IonicPage({ segment: 'addon-mod-feedback-form' }) -@Component({ - selector: 'page-addon-mod-feedback-form', - templateUrl: 'form.html', -}) -export class AddonModFeedbackFormPage implements OnDestroy { - - protected module: any; - protected currentPage: number; - protected submitted: any; - protected feedback; - protected siteAfterSubmit; - protected onlineObserver; - protected originalData; - protected currentSite; - protected forceLeave = false; - - title: string; - preview = false; - courseId: number; - componentId: number; - completionPageContents: string; - component = AddonModFeedbackProvider.COMPONENT; - offline = false; - feedbackLoaded = false; - access: any; - items = []; - hasPrevPage = false; - hasNextPage = false; - completed = false; - completedOffline = false; - - constructor(navParams: NavParams, protected feedbackProvider: AddonModFeedbackProvider, protected appProvider: CoreAppProvider, - protected utils: CoreUtilsProvider, protected domUtils: CoreDomUtilsProvider, protected navCtrl: NavController, - protected feedbackHelper: AddonModFeedbackHelperProvider, protected courseProvider: CoreCourseProvider, - protected eventsProvider: CoreEventsProvider, protected feedbackSync: AddonModFeedbackSyncProvider, network: Network, - protected translate: TranslateService, protected loginHelper: CoreLoginHelperProvider, - protected linkHelper: CoreContentLinksHelperProvider, sitesProvider: CoreSitesProvider, - @Optional() private content: Content, zone: NgZone, protected courseHelper: CoreCourseHelperProvider) { - - this.module = navParams.get('module'); - this.courseId = navParams.get('courseId'); - this.currentPage = navParams.get('page'); - this.title = navParams.get('title'); - this.preview = !!navParams.get('preview'); - this.componentId = navParams.get('moduleId') || this.module.id; - - this.currentSite = sitesProvider.getCurrentSite(); - - // Refresh online status when changes. - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.offline = !this.appProvider.isOnline(); - }); - }); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchData().then(() => { - this.feedbackProvider.logView(this.feedback.id, this.feedback.name, true).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * View entered. - */ - ionViewDidEnter(): void { - this.forceLeave = false; - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - ionViewCanLeave(): boolean | Promise { - if (this.forceLeave) { - return true; - } - - if (!this.preview) { - const responses = this.feedbackHelper.getPageItemsResponses(this.items); - - if (this.items && !this.completed && this.originalData) { - // Form submitted. Check if there is any change. - if (!this.utils.basicLeftCompare(responses, this.originalData, 3)) { - return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - } - } - - return Promise.resolve(); - } - - /** - * Fetch all the data required for the view. - * - * @return Promise resolved when done. - */ - protected fetchData(): Promise { - this.offline = !this.appProvider.isOnline(); - const options = { - cmId: this.module.id, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }; - - return this.feedbackProvider.getFeedback(this.courseId, this.module.id).then((feedbackData) => { - this.feedback = feedbackData; - - this.title = this.feedback.name || this.title; - - return this.fetchAccessData(); - }).then((accessData) => { - if (!this.preview && accessData.cansubmit && !accessData.isempty) { - return typeof this.currentPage == 'undefined' ? - this.feedbackProvider.getResumePage(this.feedback.id, options) : Promise.resolve(this.currentPage); - } else { - this.preview = true; - - return Promise.resolve(0); - } - }).catch((error) => { - if (!this.offline && !this.utils.isWebServiceError(error)) { - // If it fails, go offline. - this.offline = true; - options.readingStrategy = CoreSitesReadingStrategy.PreferCache; - - return this.feedbackProvider.getResumePage(this.feedback.id, options); - } - - return Promise.reject(error); - }).then((page) => { - return this.fetchFeedbackPageData(page || 0); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - this.forceLeave = true; - this.navCtrl.pop(); - - return Promise.reject(null); - }).finally(() => { - this.feedbackLoaded = true; - }); - } - - /** - * Fetch access information. - * - * @return Promise resolved when done. - */ - protected fetchAccessData(): Promise { - const options = { - cmId: this.module.id, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }; - - return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, options).catch((error) => { - if (!this.offline && !this.utils.isWebServiceError(error)) { - // If it fails, go offline. - this.offline = true; - options.readingStrategy = CoreSitesReadingStrategy.PreferCache; - - return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, options); - } - - return Promise.reject(error); - }).then((accessData) => { - this.access = accessData; - - return accessData; - }); - } - - protected fetchFeedbackPageData(page: number = 0): Promise { - const options = { - cmId: this.module.id, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }; - let promise; - this.items = []; - - if (this.preview) { - promise = this.feedbackProvider.getItems(this.feedback.id, {cmId: this.module.id}); - } else { - this.currentPage = page; - - promise = this.feedbackProvider.getPageItemsWithValues(this.feedback.id, page, options).catch((error) => { - if (!this.offline && !this.utils.isWebServiceError(error)) { - // If it fails, go offline. - this.offline = true; - options.readingStrategy = CoreSitesReadingStrategy.PreferCache; - - return this.feedbackProvider.getPageItemsWithValues(this.feedback.id, page, options); - } - - return Promise.reject(error); - }).then((response) => { - this.hasPrevPage = !!response.hasprevpage; - this.hasNextPage = !!response.hasnextpage; - - return response; - }); - } - - return promise.then((response) => { - this.items = response.items.map((itemData) => { - return this.feedbackHelper.getItemForm(itemData, this.preview); - }).filter((itemData) => { - // Filter items with errors. - return itemData; - }); - - if (!this.preview) { - const itemsCopy = this.utils.clone(this.items); // Copy the array to avoid modifications. - this.originalData = this.feedbackHelper.getPageItemsResponses(itemsCopy); - } - }); - } - - /** - * Function to allow page navigation through the questions form. - * - * @param goPrevious If true it will go back to the previous page, if false, it will go forward. - * @return Resolved when done. - */ - gotoPage(goPrevious: boolean): Promise { - this.domUtils.scrollToTop(this.content); - this.feedbackLoaded = false; - - const responses = this.feedbackHelper.getPageItemsResponses(this.items), - formHasErrors = this.items.some((item) => { - return item.isEmpty || item.hasError; - }); - - // Sync other pages first. - return this.feedbackSync.syncFeedback(this.feedback.id).catch(() => { - // Ignore errors. - }).then(() => { - return this.feedbackProvider.processPage(this.feedback.id, this.currentPage, responses, { - goPrevious, - formHasErrors, - courseId: this.courseId, - cmId: this.module.id, - }).then((response) => { - const jumpTo = parseInt(response.jumpto, 10); - - if (response.completed) { - // Form is completed, show completion message and buttons. - this.items = []; - this.completed = true; - this.completedOffline = !!response.offline; - this.completionPageContents = response.completionpagecontents; - this.siteAfterSubmit = response.siteaftersubmit; - this.submitted = true; - - // Invalidate access information so user will see home page updated (continue form or completion messages). - const promises = []; - promises.push(this.feedbackProvider.invalidateFeedbackAccessInformationData(this.feedback.id)); - promises.push(this.feedbackProvider.invalidateResumePageData(this.feedback.id)); - - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'feedback' }); - - return Promise.all(promises).then(() => { - return this.fetchAccessData(); - }); - } else if (isNaN(jumpTo) || jumpTo == this.currentPage) { - // Errors on questions, stay in page. - return Promise.resolve(); - } else { - this.submitted = true; - // Invalidate access information so user will see home page updated (continue form). - this.feedbackProvider.invalidateResumePageData(this.feedback.id); - - // Fetch the new page. - return this.fetchFeedbackPageData(jumpTo); - } - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - - return Promise.reject(null); - }).finally(() => { - this.feedbackLoaded = true; - }); - } - - /** - * Function to link implemented features. - */ - showAnalysis(): void { - this.submitted = 'analysis'; - this.feedbackHelper.openFeature('analysis', this.navCtrl, this.module, this.courseId); - } - - /** - * Function to go to the page after submit. - */ - continue(): void { - if (this.siteAfterSubmit) { - const modal = this.domUtils.showModalLoading(); - this.linkHelper.handleLink(this.siteAfterSubmit).then((treated) => { - if (!treated) { - return this.currentSite.openInBrowserWithAutoLoginIfSameSite(this.siteAfterSubmit); - } - }).finally(() => { - modal.dismiss(); - }); - } else { - this.courseHelper.getAndOpenCourse(undefined, this.courseId, {}, this.currentSite.getId()); - } - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - if (this.submitted) { - const tab = this.submitted == 'analysis' ? 'analysis' : 'overview'; - - // If form has been submitted, the info has been already invalidated but we should update index view. - this.eventsProvider.trigger(AddonModFeedbackProvider.FORM_SUBMITTED, { - feedbackId: this.feedback.id, - tab: tab, - offline: this.completedOffline - }); - } - this.onlineObserver && this.onlineObserver.unsubscribe(); - } -} diff --git a/src/addon/mod/feedback/pages/index/index.html b/src/addon/mod/feedback/pages/index/index.html deleted file mode 100644 index 401746668..000000000 --- a/src/addon/mod/feedback/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/feedback/pages/index/index.module.ts b/src/addon/mod/feedback/pages/index/index.module.ts deleted file mode 100644 index 118271d59..000000000 --- a/src/addon/mod/feedback/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModFeedbackComponentsModule } from '../../components/components.module'; -import { AddonModFeedbackIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModFeedbackIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModFeedbackComponentsModule, - IonicPageModule.forChild(AddonModFeedbackIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModFeedbackIndexPageModule {} diff --git a/src/addon/mod/feedback/pages/index/index.ts b/src/addon/mod/feedback/pages/index/index.ts deleted file mode 100644 index b11341126..000000000 --- a/src/addon/mod/feedback/pages/index/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModFeedbackIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a feedback. - */ -@IonicPage({ segment: 'addon-mod-feedback-index' }) -@Component({ - selector: 'page-addon-mod-feedback-index', - templateUrl: 'index.html', -}) -export class AddonModFeedbackIndexPage { - @ViewChild(AddonModFeedbackIndexComponent) feedbackComponent: AddonModFeedbackIndexComponent; - - title: string; - module: any; - courseId: number; - selectedTab: string; - selectedGroup: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.selectedGroup = navParams.get('group') || 0; - this.selectedTab = navParams.get('tab') || 'overview'; - this.title = this.module.name; - } - - /** - * Update some data based on the feedback instance. - * - * @param feedback Feedback instance. - */ - updateData(feedback: any): void { - this.title = feedback.name || this.title; - } -} diff --git a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html deleted file mode 100644 index 86aa3d0da..000000000 --- a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html +++ /dev/null @@ -1,43 +0,0 @@ - - - {{ 'addon.mod_feedback.responses' |translate }} - - - - - - - - - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - - {{ 'addon.mod_feedback.non_respondents_students' | translate : {$a: total } }} - - - - -

{{ user.fullname }}

-

- - {{ 'addon.mod_feedback.started' | translate}} - - - {{ 'addon.mod_feedback.not_started' | translate}} - -

-
-
- - - - - -
-
-
diff --git a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.module.ts b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.module.ts deleted file mode 100644 index c1dc531f4..000000000 --- a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { AddonModFeedbackComponentsModule } from '../../components/components.module'; -import { AddonModFeedbackNonRespondentsPage } from './nonrespondents'; - -@NgModule({ - declarations: [ - AddonModFeedbackNonRespondentsPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - AddonModFeedbackComponentsModule, - IonicPageModule.forChild(AddonModFeedbackNonRespondentsPage), - TranslateModule.forChild() - ], -}) -export class AddonModFeedbackNonRespondentsPageModule {} diff --git a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts deleted file mode 100644 index 35425ea36..000000000 --- a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts +++ /dev/null @@ -1,164 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams, NavController } from 'ionic-angular'; -import { AddonModFeedbackProvider } from '../../providers/feedback'; -import { AddonModFeedbackHelperProvider } from '../../providers/helper'; -import { CoreGroupInfo, CoreGroupsProvider } from '@providers/groups'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Page that displays feedback non respondents. - */ -@IonicPage({ segment: 'addon-mod-feedback-nonrespondents' }) -@Component({ - selector: 'page-addon-mod-feedback-nonrespondents', - templateUrl: 'nonrespondents.html', -}) -export class AddonModFeedbackNonRespondentsPage { - - protected moduleId: number; - protected feedbackId: number; - protected courseId: number; - protected page = 0; - - selectedGroup: number; - groupInfo: CoreGroupInfo = { - groups: [], - separateGroups: false, - visibleGroups: false - }; - - users = []; - total = 0; - canLoadMore = false; - - feedbackLoaded = false; - loadingMore = false; - - constructor(navParams: NavParams, protected feedbackProvider: AddonModFeedbackProvider, - protected groupsProvider: CoreGroupsProvider, protected domUtils: CoreDomUtilsProvider, - protected feedbackHelper: AddonModFeedbackHelperProvider, protected navCtrl: NavController) { - const module = navParams.get('module'); - this.moduleId = module.id; - this.feedbackId = module.instance; - this.courseId = navParams.get('courseId'); - this.selectedGroup = navParams.get('group') || 0; - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchData(); - } - - /** - * Fetch all the data required for the view. - * - * @param refresh Empty events array first. - * @return Promise resolved when done. - */ - fetchData(refresh: boolean = false): Promise { - this.page = 0; - this.total = 0; - this.users = []; - - return this.groupsProvider.getActivityGroupInfo(this.moduleId).then((groupInfo) => { - this.groupInfo = groupInfo; - this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, groupInfo); - - return this.loadGroupUsers(this.selectedGroup); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - - if (!refresh) { - // Some call failed on first fetch, go back. - this.navCtrl.pop(); - } - - return Promise.reject(null); - }); - } - - /** - * Load Group responses. - * - * @param groupId If defined it will change group if not, it will load more users for the same group. - * @return Resolved with the attempts loaded. - */ - protected loadGroupUsers(groupId?: number): Promise { - if (typeof groupId == 'undefined') { - this.page++; - this.loadingMore = true; - } else { - this.selectedGroup = groupId; - this.page = 0; - this.total = 0; - this.users = []; - this.feedbackLoaded = false; - } - - return this.feedbackHelper.getNonRespondents(this.feedbackId, { - groupId: this.selectedGroup, - page: this.page, - cmId: this.moduleId, - }).then((response) => { - this.total = response.total; - - if (this.users.length < response.total) { - this.users = this.users.concat(response.users); - } - - this.canLoadMore = this.users.length < response.total; - - return response; - }).finally(() => { - this.loadingMore = false; - this.feedbackLoaded = true; - }); - } - - /** - * Change selected group or load more users. - * - * @param groupId Group ID selected. If not defined, it will load more users. - */ - loadAttempts(groupId?: number): void { - this.loadGroupUsers(groupId).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - }); - } - - /** - * Refresh the attempts. - * - * @param refresher Refresher. - */ - refreshFeedback(refresher: any): void { - if (this.feedbackLoaded) { - const promises = []; - - promises.push(this.feedbackProvider.invalidateNonRespondentsData(this.feedbackId)); - promises.push(this.groupsProvider.invalidateActivityGroupInfo(this.moduleId)); - - Promise.all(promises).finally(() => { - return this.fetchData(true); - }).finally(() => { - refresher.complete(); - }); - } - } -} diff --git a/src/addon/mod/feedback/pages/respondents/respondents.html b/src/addon/mod/feedback/pages/respondents/respondents.html deleted file mode 100644 index dcd466f6a..000000000 --- a/src/addon/mod/feedback/pages/respondents/respondents.html +++ /dev/null @@ -1,51 +0,0 @@ - - - {{ 'addon.mod_feedback.responses' |translate }} - - - - - - - - - - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - - - {{ 'addon.mod_feedback.non_anonymous_entries' | translate : {$a: responses.total } }} - - - -

{{ attempt.fullname }}

-

{{attempt.timemodified * 1000 | coreFormatDate }}

-
- - - - - -
- - - {{ 'addon.mod_feedback.anonymous_entries' |translate : {$a: anonResponses.total } }} - - -

{{ 'addon.mod_feedback.response_nr' |translate }}: {{attempt.number}}

-
- - - - - -
-
-
-
-
diff --git a/src/addon/mod/feedback/pages/respondents/respondents.module.ts b/src/addon/mod/feedback/pages/respondents/respondents.module.ts deleted file mode 100644 index c126decc7..000000000 --- a/src/addon/mod/feedback/pages/respondents/respondents.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModFeedbackComponentsModule } from '../../components/components.module'; -import { AddonModFeedbackRespondentsPage } from './respondents'; - -@NgModule({ - declarations: [ - AddonModFeedbackRespondentsPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - CorePipesModule, - AddonModFeedbackComponentsModule, - IonicPageModule.forChild(AddonModFeedbackRespondentsPage), - TranslateModule.forChild() - ], -}) -export class AddonModFeedbackRespondentsPageModule {} diff --git a/src/addon/mod/feedback/pages/respondents/respondents.ts b/src/addon/mod/feedback/pages/respondents/respondents.ts deleted file mode 100644 index ca21c2325..000000000 --- a/src/addon/mod/feedback/pages/respondents/respondents.ts +++ /dev/null @@ -1,208 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams, NavController } from 'ionic-angular'; -import { AddonModFeedbackProvider } from '../../providers/feedback'; -import { AddonModFeedbackHelperProvider } from '../../providers/helper'; -import { CoreGroupInfo, CoreGroupsProvider } from '@providers/groups'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; - -/** - * Page that displays feedback respondents. - */ -@IonicPage({ segment: 'addon-mod-feedback-respondents' }) -@Component({ - selector: 'page-addon-mod-feedback-respondents', - templateUrl: 'respondents.html', -}) -export class AddonModFeedbackRespondentsPage { - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - protected moduleId: number; - protected feedbackId: number; - protected courseId: number; - protected page = 0; - - selectedGroup: number; - groupInfo: CoreGroupInfo = { - groups: [], - separateGroups: false, - visibleGroups: false - }; - - responses = { - attempts: [], - total: 0, - canLoadMore: false - }; - anonResponses = { - attempts: [], - total: 0, - canLoadMore: false - }; - feedbackLoaded = false; - loadingMore = false; - attemptId: number; - - constructor(navParams: NavParams, protected feedbackProvider: AddonModFeedbackProvider, - protected groupsProvider: CoreGroupsProvider, protected domUtils: CoreDomUtilsProvider, - protected feedbackHelper: AddonModFeedbackHelperProvider, protected navCtrl: NavController) { - const module = navParams.get('module'); - this.moduleId = module.id; - this.feedbackId = module.instance; - this.courseId = navParams.get('courseId'); - this.selectedGroup = navParams.get('group') || 0; - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchData().then(() => { - if (this.splitviewCtrl.isOn()) { - if (this.responses.attempts.length > 0) { - // Take first and load it. - this.gotoAttempt(this.responses.attempts[0]); - } else if (this.anonResponses.attempts.length > 0) { - // Take first and load it. - this.gotoAttempt(this.anonResponses.attempts[0]); - } - } - }); - } - - /** - * Fetch all the data required for the view. - * - * @param refresh Empty events array first. - * @return Promise resolved when done. - */ - fetchData(refresh: boolean = false): Promise { - this.page = 0; - this.responses.total = 0; - this.responses.attempts = []; - this.anonResponses.total = 0; - this.anonResponses.attempts = []; - - return this.groupsProvider.getActivityGroupInfo(this.moduleId).then((groupInfo) => { - this.groupInfo = groupInfo; - this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, groupInfo); - - return this.loadGroupAttempts(this.selectedGroup); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - - if (!refresh) { - // Some call failed on first fetch, go back. - this.navCtrl.pop(); - } - - return Promise.reject(null); - }); - } - - /** - * Load Group attempts. - * - * @param groupId If defined it will change group if not, it will load more attempts for the same group. - * @return Resolved with the attempts loaded. - */ - protected loadGroupAttempts(groupId?: number): Promise { - if (typeof groupId == 'undefined') { - this.page++; - this.loadingMore = true; - } else { - this.selectedGroup = groupId; - this.page = 0; - this.responses.total = 0; - this.responses.attempts = []; - this.anonResponses.total = 0; - this.anonResponses.attempts = []; - this.feedbackLoaded = false; - } - - return this.feedbackHelper.getResponsesAnalysis(this.feedbackId, { - groupId: this.selectedGroup, - page: this.page, - cmId: this.moduleId, - }).then((responses) => { - this.responses.total = responses.totalattempts; - this.anonResponses.total = responses.totalanonattempts; - - if (this.anonResponses.attempts.length < responses.totalanonattempts) { - this.anonResponses.attempts = this.anonResponses.attempts.concat(responses.anonattempts); - } - if (this.responses.attempts.length < responses.totalattempts) { - this.responses.attempts = this.responses.attempts.concat(responses.attempts); - } - - this.anonResponses.canLoadMore = this.anonResponses.attempts.length < responses.totalanonattempts; - this.responses.canLoadMore = this.responses.attempts.length < responses.totalattempts; - - return responses; - }).finally(() => { - this.loadingMore = false; - this.feedbackLoaded = true; - }); - } - - /** - * Navigate to a particular attempt. - * - * @param attempt Attempt object to load. - */ - gotoAttempt(attempt: any): void { - this.attemptId = attempt.id; - this.splitviewCtrl.push('AddonModFeedbackAttemptPage', { - attemptId: attempt.id, - attempt: attempt, - feedbackId: this.feedbackId, - moduleId: this.moduleId, - courseId: this.courseId - }); - } - - /** - * Change selected group or load more attempts. - * - * @param groupId Group ID selected. If not defined, it will load more attempts. - */ - loadAttempts(groupId?: number): void { - this.loadGroupAttempts(groupId).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - }); - } - - /** - * Refresh the attempts. - * - * @param refresher Refresher. - */ - refreshFeedback(refresher: any): void { - if (this.feedbackLoaded) { - const promises = []; - - promises.push(this.feedbackProvider.invalidateResponsesAnalysisData(this.feedbackId)); - promises.push(this.groupsProvider.invalidateActivityGroupInfo(this.moduleId)); - - Promise.all(promises).finally(() => { - return this.fetchData(true); - }).finally(() => { - refresher.complete(); - }); - } - } -} diff --git a/src/addon/mod/feedback/providers/analysis-link-handler.ts b/src/addon/mod/feedback/providers/analysis-link-handler.ts deleted file mode 100644 index 4e75348b2..000000000 --- a/src/addon/mod/feedback/providers/analysis-link-handler.ts +++ /dev/null @@ -1,93 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonModFeedbackProvider } from './feedback'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Content links handler for a feedback analysis. - * Match mod/feedback/analysis.php with a valid feedback id. - */ -@Injectable() -export class AddonModFeedbackAnalysisLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModFeedbackAnalysisLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModFeedback'; - pattern = /\/mod\/feedback\/analysis\.php.*([\&\?]id=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private feedbackProvider: AddonModFeedbackProvider, - private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - moduleId = params.id; - - this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => { - const stateParams = { - module: module, - courseId: module.course, - tab: 'analysis' - }; - - return this.linkHelper.goInSite(navCtrl, 'AddonModFeedbackIndexPage', stateParams, siteId); - }).finally(() => { - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.feedbackProvider.isPluginEnabled(siteId).then((enabled) => { - if (!enabled) { - return false; - } - - if (typeof params.id == 'undefined') { - // Cannot treat the URL. - return false; - } - - return true; - }); - } -} diff --git a/src/addon/mod/feedback/providers/complete-link-handler.ts b/src/addon/mod/feedback/providers/complete-link-handler.ts deleted file mode 100644 index b8071a119..000000000 --- a/src/addon/mod/feedback/providers/complete-link-handler.ts +++ /dev/null @@ -1,89 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonModFeedbackProvider } from './feedback'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Content links handler for feedback complete questions. - * Match mod/feedback/complete.php with a valid feedback id. - */ -@Injectable() -export class AddonModFeedbackCompleteLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModFeedbackCompleteLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModFeedback'; - pattern = /\/mod\/feedback\/complete\.php.*([\?\&](id|gopage)=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private feedbackProvider: AddonModFeedbackProvider, - private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - moduleId = params.id; - - this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => { - const stateParams = { - module: module, - moduleId: module.id, - courseId: module.course - }; - if (typeof params.gopage == 'undefined') { - stateParams['page'] = parseInt(params.gopage, 10); - } - - return this.linkHelper.goInSite(navCtrl, 'AddonModFeedbackFormPage', stateParams, siteId); - }).finally(() => { - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - if (typeof params.id == 'undefined') { - return false; - } - - return this.feedbackProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/mod/feedback/providers/feedback.ts b/src/addon/mod/feedback/providers/feedback.ts deleted file mode 100644 index 19e39fab9..000000000 --- a/src/addon/mod/feedback/providers/feedback.ts +++ /dev/null @@ -1,1239 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreAppProvider } from '@providers/app'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModFeedbackOfflineProvider } from './offline'; -import { CoreSite } from '@classes/site'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some features for feedbacks. - */ -@Injectable() -export class AddonModFeedbackProvider { - static COMPONENT = 'mmaModFeedback'; - static FORM_SUBMITTED = 'addon_mod_feedback_form_submitted'; - static LINE_SEP = '|'; - static MULTICHOICE_TYPE_SEP = '>>>>>'; - static MULTICHOICE_ADJUST_SEP = '<<<<<'; - static MULTICHOICE_HIDENOSELECT = 'h'; - static MULTICHOICERATED_VALUE_SEP = '####'; - - protected ROOT_CACHE_KEY = ''; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, - private filepoolProvider: CoreFilepoolProvider, private feedbackOffline: AddonModFeedbackOfflineProvider, - private appProvider: CoreAppProvider, private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModFeedbackProvider'); - } - - /** - * Check dependency of a question item. - * - * @param items All question items to check dependency. - * @param item Item to check. - * @return Return true if dependency is acomplished and it can be shown. False, otherwise. - */ - protected checkDependencyItem(items: any[], item: any): boolean { - const depend = items.find((itemFind) => { - return itemFind.id == item.dependitem; - }); - - // Item not found, looks like dependent item has been removed or is in the same or following pages. - if (!depend) { - return true; - } - - switch (depend.typ) { - case 'label': - return false; - case 'multichoice': - case 'multichoicerated': - return this.compareDependItemMultichoice(depend, item.dependvalue); - default: - break; - } - - return item.dependvalue == depend.rawValue; - } - - /** - * Check dependency item of type Multichoice. - * - * @param item Item to check. - * @param dependValue Value to compare. - * @return Return true if dependency is acomplished and it can be shown. False, otherwise. - */ - protected compareDependItemMultichoice(item: any, dependValue: string): boolean { - let values, choices; - const parts = item.presentation.split(AddonModFeedbackProvider.MULTICHOICE_TYPE_SEP) || [], - subtype = parts.length > 0 && parts[0] ? parts[0] : 'r'; - - choices = parts[1] || ''; - choices = choices.split(AddonModFeedbackProvider.MULTICHOICE_ADJUST_SEP)[0] || ''; - choices = choices.split(AddonModFeedbackProvider.LINE_SEP) || []; - - if (subtype === 'c') { - if (typeof item.rawValue == 'undefined') { - values = ['']; - } else { - item.rawValue = '' + item.rawValue; - values = item.rawValue.split(AddonModFeedbackProvider.LINE_SEP); - } - } else { - values = [item.rawValue]; - } - - for (let index = 0; index < choices.length; index++) { - for (const x in values) { - if (values[x] == index + 1) { - let value = choices[index]; - - if (item.typ == 'multichoicerated') { - value = value.split(AddonModFeedbackProvider.MULTICHOICERATED_VALUE_SEP)[1] || ''; - } - - if (value.trim() == dependValue) { - return true; - } - - // We can finish checking if only searching on one value and we found it. - if (values.length == 1) { - return false; - } - } - } - } - - return false; - } - - /** - * Fill values of item questions. - * - * @param feedbackId Feedback ID. - * @param items Item to fill the value. - * @param options Other options. - * @return Resolved with values when done. - */ - protected fillValues(feedbackId: number, items: any[], options: CoreCourseCommonModWSOptions = {}): Promise { - return this.getCurrentValues(feedbackId, options).then((valuesArray) => { - const values = {}; - - valuesArray.forEach((value) => { - values[value.item] = value.value; - }); - - items.forEach((itemData) => { - if (itemData.hasvalue && typeof values[itemData.id] != 'undefined') { - itemData.rawValue = values[itemData.id]; - } - }); - }).catch(() => { - // Ignore errors. - }).then(() => { - // Merge with offline data. - return this.feedbackOffline.getFeedbackResponses(feedbackId, options.siteId).then((offlineValuesArray) => { - const offlineValues = {}; - - // Merge all values into one array. - offlineValuesArray = offlineValuesArray.reduce((a, b) => { - const responses = this.utils.objectToArrayOfObjects(b.responses, 'id', 'value'); - - return a.concat(responses); - }, []).map((a) => { - const parts = a.id.split('_'); - a.typ = parts[0]; - a.item = parseInt(parts[1], 0); - - return a; - }); - - offlineValuesArray.forEach((value) => { - if (typeof offlineValues[value.item] == 'undefined') { - offlineValues[value.item] = []; - } - offlineValues[value.item].push(value.value); - }); - - items.forEach((itemData) => { - if (itemData.hasvalue && typeof offlineValues[itemData.id] != 'undefined') { - // Treat multichoice checkboxes. - if (itemData.typ == 'multichoice' && - itemData.presentation.split(AddonModFeedbackProvider.MULTICHOICE_TYPE_SEP)[0] == 'c') { - - offlineValues[itemData.id] = offlineValues[itemData.id].filter((value) => { - return value > 0; - }); - itemData.rawValue = offlineValues[itemData.id].join(AddonModFeedbackProvider.LINE_SEP); - } else { - itemData.rawValue = offlineValues[itemData.id][0]; - } - } - }); - - return items; - }); - }).catch(() => { - // Ignore errors. - return items; - }); - } - - /** - * Returns all the feedback non respondents users. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @param previous Only for recurrent use. Object with the previous fetched info. - * @return Promise resolved when the info is retrieved. - */ - getAllNonRespondents(feedbackId: number, options: AddonModFeedbackGroupOptions = {}, previous?: any): Promise { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - previous = previous || { - page: 0, - users: [] - }; - - return this.getNonRespondents(feedbackId, { - page: previous.page, - ...options, // Include all options. - }).then((response) => { - if (previous.users.length < response.total) { - previous.users = previous.users.concat(response.users); - } - - if (previous.users.length < response.total) { - // Can load more. - previous.page++; - - return this.getAllNonRespondents(feedbackId, options, previous); - } - previous.total = response.total; - - return previous; - }); - } - - /** - * Returns all the feedback user responses. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @param previous Only for recurrent use. Object with the previous fetched info. - * @return Promise resolved when the info is retrieved. - */ - getAllResponsesAnalysis(feedbackId: number, options: AddonModFeedbackGroupOptions = {}, previous?: any): Promise { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - previous = previous || { - page: 0, - attempts: [], - anonattempts: [] - }; - - return this.getResponsesAnalysis(feedbackId, { - page: previous.page, - ...options, // Include all options. - }).then((responses) => { - if (previous.anonattempts.length < responses.totalanonattempts) { - previous.anonattempts = previous.anonattempts.concat(responses.anonattempts); - } - - if (previous.attempts.length < responses.totalattempts) { - previous.attempts = previous.attempts.concat(responses.attempts); - } - - if (previous.anonattempts.length < responses.totalanonattempts || previous.attempts.length < responses.totalattempts) { - // Can load more. - previous.page++; - - return this.getAllResponsesAnalysis(feedbackId, options, previous); - } - - previous.totalattempts = responses.totalattempts; - previous.totalanonattempts = responses.totalanonattempts; - - return previous; - }); - } - - /** - * Get analysis information for a given feedback. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the feedback is retrieved. - */ - getAnalysis(feedbackId: number, options: AddonModFeedbackGroupOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - }; - const preSets = { - cacheKey: this.getAnalysisDataCacheKey(feedbackId, options.groupId), - component: AddonModFeedbackProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - if (options.groupId) { - params['groupid'] = options.groupId; - } - - return site.read('mod_feedback_get_analysis', params, preSets); - }); - } - - /** - * Get cache key for feedback analysis data WS calls. - * - * @param feedbackId Feedback ID. - * @param groupId Group ID. - * @return Cache key. - */ - protected getAnalysisDataCacheKey(feedbackId: number, groupId: number = 0): string { - return this.getAnalysisDataPrefixCacheKey(feedbackId) + groupId; - } - - /** - * Get prefix cache key for feedback analysis data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getAnalysisDataPrefixCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':analysis:'; - } - - /** - * Find an attempt in all responses analysis. - * - * @param feedbackId Feedback ID. - * @param attemptId Attempt id to find. - * @param options Other options. - * @param previous Only for recurrent use. Object with the previous fetched info. - * @return Promise resolved when the info is retrieved. - */ - getAttempt(feedbackId: number, attemptId: number, options: CoreCourseCommonModWSOptions = {}, previous?: any): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - previous = previous || { - page: 0, - attemptsLoaded: 0, - anonAttemptsLoaded: 0 - }; - - return this.getResponsesAnalysis(feedbackId, { - page: previous.page, - groupId: 0, - ...options, // Include all options. - }).then((responses) => { - let attempt; - - attempt = responses.attempts.find((attempt) => { - return attemptId == attempt.id; - }); - - if (attempt) { - return attempt; - } - - attempt = responses.anonattempts.find((attempt) => { - return attemptId == attempt.id; - }); - - if (attempt) { - return attempt; - } - - if (previous.anonAttemptsLoaded < responses.totalanonattempts) { - previous.anonAttemptsLoaded += responses.anonattempts.length; - } - - if (previous.attemptsLoaded < responses.totalattempts) { - previous.attemptsLoaded += responses.attempts.length; - } - - if (previous.anonAttemptsLoaded < responses.totalanonattempts || previous.attemptsLoaded < responses.totalattempts) { - // Can load more. Check there. - previous.page++; - - return this.getAttempt(feedbackId, attemptId, options, previous); - } - - // Not found and all loaded. Reject. - return Promise.reject(null); - }); - } - - /** - * Get prefix cache key for feedback completion data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getCompletedDataCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':completed:'; - } - - /** - * Returns the temporary completion timemodified for the current user. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getCurrentCompletedTimeModified(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - }; - const preSets = { - cacheKey: this.getCurrentCompletedTimeModifiedDataCacheKey(feedbackId), - component: AddonModFeedbackProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_feedback_get_current_completed_tmp', params, preSets).then((response) => { - if (response && typeof response.feedback != 'undefined' && typeof response.feedback.timemodified != 'undefined') { - return response.feedback.timemodified; - } - - return 0; - }).catch(() => { - // Ignore errors. - return 0; - }); - }); - } - - /** - * Get prefix cache key for feedback current completed temp data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getCurrentCompletedTimeModifiedDataCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':completedtime:'; - } - - /** - * Returns the temporary responses or responses of the last submission for the current user. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getCurrentValues(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - }; - const preSets = { - cacheKey: this.getCurrentValuesDataCacheKey(feedbackId), - component: AddonModFeedbackProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_feedback_get_unfinished_responses', params, preSets).then((response) => { - if (!response || typeof response.responses == 'undefined') { - return Promise.reject(null); - } - - if (response.responses.length == 0) { - // No unfinished responses, fetch responses of the last submission. - return site.read('mod_feedback_get_finished_responses', params, preSets).then((response) => { - if (!response || typeof response.responses == 'undefined') { - return Promise.reject(null); - } - - return response.responses; - }); - } - - return response.responses; - }); - }); - } - - /** - * Get cache key for get current values feedback data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getCurrentValuesDataCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':currentvalues'; - } - - /** - * Get access information for a given feedback. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the feedback is retrieved. - */ - getFeedbackAccessInformation(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - }; - const preSets = { - cacheKey: this.getFeedbackAccessInformationDataCacheKey(feedbackId), - component: AddonModFeedbackProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_feedback_get_feedback_access_information', params, preSets); - }); - } - - /** - * Get cache key for feedback access information data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getFeedbackAccessInformationDataCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':access'; - } - - /** - * Get cache key for feedback data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getFeedbackCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'feedback:' + courseId; - } - - /** - * Get prefix cache key for all feedback activity data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getFeedbackDataPrefixCacheKey(feedbackId: number): string { - return this.ROOT_CACHE_KEY + feedbackId; - } - - /** - * Get a feedback with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the feedback is retrieved. - */ - protected getFeedbackDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getFeedbackCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModFeedbackProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_feedback_get_feedbacks_by_courses', params, preSets).then((response) => { - if (response && response.feedbacks) { - const currentFeedback = response.feedbacks.find((feedback) => { - return feedback[key] == value; - }); - if (currentFeedback) { - return currentFeedback; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a feedback by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the feedback is retrieved. - */ - getFeedback(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getFeedbackDataByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a feedback by ID. - * - * @param courseId Course ID. - * @param id Feedback ID. - * @param options Other options. - * @return Promise resolved when the feedback is retrieved. - */ - getFeedbackById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getFeedbackDataByKey(courseId, 'id', id, options); - } - - /** - * Returns the items (questions) in the given feedback. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getItems(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - }; - const preSets = { - cacheKey: this.getItemsDataCacheKey(feedbackId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModFeedbackProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_feedback_get_items', params, preSets); - }); - } - - /** - * Get cache key for get items feedback data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getItemsDataCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':items'; - } - - /** - * Retrieves a list of students who didn't submit the feedback. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getNonRespondents(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise { - options.groupId = options.groupId || 0; - options.page = options.page || 0; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - groupid: options.groupId, - page: options.page, - }; - const preSets = { - cacheKey: this.getNonRespondentsDataCacheKey(feedbackId, options.groupId), - component: AddonModFeedbackProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_feedback_get_non_respondents', params, preSets); - }); - } - - /** - * Get cache key for non respondents feedback data WS calls. - * - * @param feedbackId Feedback ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @return Cache key. - */ - protected getNonRespondentsDataCacheKey(feedbackId: number, groupId: number = 0): string { - return this.getNonRespondentsDataPrefixCacheKey(feedbackId) + groupId; - } - - /** - * Get prefix cache key for feedback non respondents data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getNonRespondentsDataPrefixCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':nonrespondents:'; - } - - /** - * Get a single feedback page items. This function is not cached, use AddonModFeedbackHelperProvider#getPageItems instead. - * - * @param feedbackId Feedback ID. - * @param page The page to get. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the info is retrieved. - */ - getPageItems(feedbackId: number, page: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - feedbackid: feedbackId, - page: page - }; - - return site.write('mod_feedback_get_page_items', params); - }); - } - - /** - * Get a single feedback page items. If offline or server down it will use getItems to calculate dependencies. - * - * @param feedbackId Feedback ID. - * @param page The page to get. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getPageItemsWithValues(feedbackId: number, page: number, options: CoreCourseCommonModWSOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - return this.getPageItems(feedbackId, page, options.siteId).then((response) => { - return this.fillValues(feedbackId, response.items, options).then((items) => { - response.items = items; - - return response; - }); - }).catch(() => { - // If getPageItems fail we should calculate it using getItems. - return this.getItems(feedbackId, options).then((response) => { - return this.fillValues(feedbackId, response.items, options).then((items) => { - // Separate items by pages. - let currentPage = 0; - const previousPageItems = []; - - const pageItems = items.filter((item) => { - // Greater page, discard all entries. - if (currentPage > page) { - return false; - } - - if (item.typ == 'pagebreak') { - currentPage++; - - return false; - } - - // Save items on previous page to check dependencies and discard entry. - if (currentPage < page) { - previousPageItems.push(item); - - return false; - } - - // Filter depending items. - if (item && item.dependitem > 0 && previousPageItems.length > 0) { - return this.checkDependencyItem(previousPageItems, item); - } - - // Filter items with errors. - return item; - }); - - // Check if there are more pages. - response.hasprevpage = page > 0; - response.hasnextpage = currentPage > page; - response.items = pageItems; - - return response; - }); - }); - }); - } - - /** - * Convenience function to get the page we can jump. - * - * @param feedbackId Feedback ID. - * @param page Page where we want to jump. - * @param changePage If page change is forward (1) or backward (-1). - * @param options Other options. - * @return Page number where to jump. Or false if completed or first page. - */ - protected getPageJumpTo(feedbackId: number, page: number, changePage: number, options: {cmId?: number, siteId?: string}) - : Promise { - - return this.getPageItemsWithValues(feedbackId, page, { - cmId: options.cmId, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId: options.siteId, - }).then((resp) => { - // The page we are going has items. - if (resp.items.length > 0) { - return page; - } - - // Check we can jump futher. - if ((changePage == 1 && resp.hasnextpage) || (changePage == -1 && resp.hasprevpage)) { - return this.getPageJumpTo(feedbackId, page + changePage, changePage, options); - } - - // Completed or first page. - return false; - }); - } - - /** - * Returns the feedback user responses. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getResponsesAnalysis(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise { - options.groupId = options.groupId || 0; - options.page = options.page || 0; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - groupid: options.groupId, - page: options.page, - }; - const preSets = { - cacheKey: this.getResponsesAnalysisDataCacheKey(feedbackId, options.groupId), - component: AddonModFeedbackProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_feedback_get_responses_analysis', params, preSets); - }); - } - - /** - * Get cache key for responses analysis feedback data WS calls. - * - * @param feedbackId Feedback ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @return Cache key. - */ - protected getResponsesAnalysisDataCacheKey(feedbackId: number, groupId: number = 0): string { - return this.getResponsesAnalysisDataPrefixCacheKey(feedbackId) + groupId; - } - - /** - * Get prefix cache key for feedback responses analysis data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getResponsesAnalysisDataPrefixCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':responsesanalysis:'; - } - - /** - * Gets the resume page information. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getResumePage(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - }; - const preSets = { - cacheKey: this.getResumePageDataCacheKey(feedbackId), - component: AddonModFeedbackProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_feedback_launch_feedback', params, preSets).then((response) => { - if (response && typeof response.gopage != 'undefined') { - // WS will return -1 for last page but the user need to start again. - return response.gopage > 0 ? response.gopage : 0; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get prefix cache key for resume feedback page data WS calls. - * - * @param feedbackId Feedback ID. - * @return Cache key. - */ - protected getResumePageDataCacheKey(feedbackId: number): string { - return this.getFeedbackDataPrefixCacheKey(feedbackId) + ':launch'; - } - - /** - * Invalidates feedback data except files and module info. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllFeedbackData(feedbackId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getFeedbackDataPrefixCacheKey(feedbackId)); - }); - } - - /** - * Invalidates feedback analysis data. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAnalysisData(feedbackId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getAnalysisDataPrefixCacheKey(feedbackId)); - }); - } - - /** - * Invalidate the prefetched content. - * To invalidate files, use AddonModFeedbackProvider#invalidateFiles. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.getFeedback(courseId, moduleId, {siteId}).then((feedback) => { - const ps = []; - - // Do not invalidate module data before getting module info, we need it! - ps.push(this.invalidateFeedbackData(courseId, siteId)); - ps.push(this.invalidateAllFeedbackData(feedback.id, siteId)); - - return Promise.all(ps); - })); - - promises.push(this.invalidateFiles(moduleId, siteId)); - - return this.utils.allPromises(promises); - } - - /** - * Invalidates temporary completion record data. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateCurrentValuesData(feedbackId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCurrentValuesDataCacheKey(feedbackId)); - }); - } - - /** - * Invalidates feedback access information data. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateFeedbackAccessInformationData(feedbackId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getFeedbackAccessInformationDataCacheKey(feedbackId)); - }); - } - - /** - * Invalidates feedback data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateFeedbackData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getFeedbackCacheKey(courseId)); - }); - } - - /** - * Invalidate the prefetched files. - * - * @param moduleId The module ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the files are invalidated. - */ - invalidateFiles(moduleId: number, siteId?: string): Promise { - return this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModFeedbackProvider.COMPONENT, moduleId); - } - - /** - * Invalidates feedback non respondents record data. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateNonRespondentsData(feedbackId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getNonRespondentsDataPrefixCacheKey(feedbackId)); - - }); - } - - /** - * Invalidates feedback user responses record data. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateResponsesAnalysisData(feedbackId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getResponsesAnalysisDataPrefixCacheKey(feedbackId)); - - }); - } - - /** - * Invalidates launch feedback data. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateResumePageData(feedbackId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getResumePageDataCacheKey(feedbackId)); - }); - } - - /** - * Returns if feedback has been completed - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - isCompleted(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - feedbackid: feedbackId, - }; - const preSets = { - cacheKey: this.getCompletedDataCacheKey(feedbackId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModFeedbackProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return this.utils.promiseWorks(site.read('mod_feedback_get_last_completed', params, preSets)); - }); - } - - /** - * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the feedback WS are available. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - * @since 3.3 - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('mod_feedback_get_feedbacks_by_courses') && - site.wsAvailable('mod_feedback_get_feedback_access_information'); - }); - } - - /** - * Report the feedback as being viewed. - * - * @param id Module ID. - * @param name Name of the feedback. - * @param formViewed True if form was viewed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, formViewed: boolean = false, siteId?: string): Promise { - const params = { - feedbackid: id, - moduleviewed: formViewed ? 1 : 0 - }; - - return this.logHelper.logSingle('mod_feedback_view_feedback', params, AddonModFeedbackProvider.COMPONENT, id, name, - 'feedback', {moduleviewed: params.moduleviewed}, siteId); - } - - /** - * Process a jump between pages. - * - * @param feedbackId Feedback ID. - * @param page The page being processed. - * @param responses The data to be processed the key is the field name (usually type[index]_id). - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - processPage(feedbackId: number, page: number, responses: any, options: AddonModFeedbackProcessPageOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.feedbackOffline.saveResponses(feedbackId, page, responses, options.courseId, options.siteId).then(() => { - // Simulate process_page response. - const response = { - jumpto: page, - completed: false, - offline: true - }; - let changePage = 0; - - if (options.goPrevious) { - if (page > 0) { - changePage = -1; - } - } else if (!options.formHasErrors) { - // We can only go next if it has no errors. - changePage = 1; - } - - if (changePage === 0) { - return response; - } - - return this.getPageItemsWithValues(feedbackId, page, { - cmId: options.cmId, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId: options.siteId, - }).then((resp) => { - // Check completion. - if (changePage == 1 && !resp.hasnextpage) { - response.completed = true; - - return response; - } - - return this.getPageJumpTo(feedbackId, page + changePage, changePage, options).then((loadPage) => { - if (loadPage === false) { - // Completed or first page. - if (changePage == -1) { - // First page. - response.jumpto = 0; - } else { - // Completed. - response.completed = true; - } - } else { - response.jumpto = loadPage; - } - - return response; - }); - }); - }); - }; - - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - // If there's already a response to be sent to the server, discard it first. - return this.feedbackOffline.deleteFeedbackPageResponses(feedbackId, page, options.siteId).then(() => { - return this.processPageOnline(feedbackId, page, responses, options.goPrevious, options.siteId).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. - return Promise.reject(error); - } - - // Couldn't connect to server, store in offline. - return storeOffline(); - }); - }); - } - - /** - * Process a jump between pages. - * - * @param feedbackId Feedback ID. - * @param page The page being processed. - * @param responses The data to be processed the key is the field name (usually type[index]_id). - * @param goPrevious Whether we want to jump to previous page. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the info is retrieved. - */ - processPageOnline(feedbackId: number, page: number, responses: any, goPrevious: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - feedbackid: feedbackId, - page: page, - responses: this.utils.objectToArrayOfObjects(responses, 'name', 'value'), - goprevious: goPrevious ? 1 : 0 - }; - - return site.write('mod_feedback_process_page', params).catch((error) => { - return Promise.reject(this.utils.createFakeWSError(error)); - }).then((response) => { - // Invalidate and update current values because they will change. - return this.invalidateCurrentValuesData(feedbackId, site.getId()).then(() => { - return this.getCurrentValues(feedbackId, {siteId: site.getId()}); - }).catch(() => { - // Ignore errors. - }).then(() => { - return response; - }); - }); - }); - } -} - -/** - * Common options with a group ID. - */ -export type AddonModFeedbackGroupOptions = CoreCourseCommonModWSOptions & { - groupId?: number; // Group id, 0 means that the function will determine the user group. Defaults to 0. -}; - -/** - * Common options with a group ID and page. - */ -export type AddonModFeedbackGroupPaginatedOptions = AddonModFeedbackGroupOptions & { - page?: number; // The page of records to return. The page of records to return. -}; - -/** - * Common options with a group ID and page. - */ -export type AddonModFeedbackProcessPageOptions = { - goPrevious?: boolean; // Whether we want to jump to previous page. - formHasErrors?: boolean; // Whether the form we sent has required but empty fields (only used in offline). - cmId?: number; // Module ID. - courseId?: number; // Course ID the feedback belongs to. - siteId?: string; // Site ID. If not defined, current site.; -}; diff --git a/src/addon/mod/feedback/providers/helper.ts b/src/addon/mod/feedback/providers/helper.ts deleted file mode 100644 index f136c2456..000000000 --- a/src/addon/mod/feedback/providers/helper.ts +++ /dev/null @@ -1,526 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, ViewController } from 'ionic-angular'; -import { AddonModFeedbackProvider, AddonModFeedbackGroupPaginatedOptions } from './feedback'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Service that provides helper functions for feedbacks. - */ -@Injectable() -export class AddonModFeedbackHelperProvider { - - protected MODE_RESPONSETIME = 1; - protected MODE_COURSE = 2; - protected MODE_CATEGORY = 3; - - constructor(protected feedbackProvider: AddonModFeedbackProvider, protected userProvider: CoreUserProvider, - protected textUtils: CoreTextUtilsProvider, protected translate: TranslateService, - protected timeUtils: CoreTimeUtilsProvider, protected domUtils: CoreDomUtilsProvider, - protected courseProvider: CoreCourseProvider, protected linkHelper: CoreContentLinksHelperProvider, - protected sitesProvider: CoreSitesProvider, protected utils: CoreUtilsProvider) { - } - - /** - * Check if the page we are going to open is in the history and returns the view controller in the stack to go back. - * - * @param pageName Name of the page we want to navigate. - * @param instance Activity instance Id. I.e FeedbackId. - * @param paramName Param name where to find the instance number. - * @param prefix Prefix to check if we are out of the activity context. - * @param navCtrl Nav Controller of the view. - * @return Returns view controller found or null. - */ - protected getPageView(pageName: string, instance: number, paramName: string, prefix: string, - navCtrl: NavController): ViewController { - let historyInstance, params, - view = navCtrl.getActive(); - - while (!view.isFirst()) { - if (!view.name.startsWith(prefix)) { - break; - } - - params = view.getNavParams(); - - historyInstance = params.get(paramName) ? params.get(paramName) : params.get('module').instance; - - // Check we are not changing to another activity. - if (!historyInstance || historyInstance != instance) { - break; - } - - // Page found. - if (view.name == pageName) { - return view; - } - - view = navCtrl.getPrevious(view); - } - - return null; - } - - /** - * Retrieves a list of students who didn't submit the feedback with extra info. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getNonRespondents(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise { - return this.feedbackProvider.getNonRespondents(feedbackId, options).then((responses) => { - return this.addImageProfileToAttempts(responses.users).then((users) => { - responses.users = users; - - return responses; - }); - }); - } - - /** - * Get page items responses to be sent. - * - * @param items Items where the values are. - * @return Responses object to be sent. - */ - getPageItemsResponses(items: any[]): any { - const responses = {}; - - items.forEach((itemData) => { - let answered = false; - - itemData.hasError = false; - - if (itemData.typ == 'captcha') { - const value = itemData.value || '', - name = itemData.typ + '_' + itemData.id; - - answered = !!value; - responses[name] = 1; - responses['g-recaptcha-response'] = value; - responses['recaptcha_element'] = 'dummyvalue'; - - if (itemData.required && !answered) { - // Check if it has any value. - itemData.isEmpty = true; - } else { - itemData.isEmpty = false; - } - } else if (itemData.hasvalue) { - let name, value; - const nameTemp = itemData.typ + '_' + itemData.id; - - if (itemData.typ == 'multichoice' && itemData.subtype == 'c') { - name = nameTemp + '[0]'; - responses[name] = 0; - itemData.choices.forEach((choice, index) => { - name = nameTemp + '[' + (index + 1) + ']'; - value = choice.checked ? choice.value : 0; - if (!answered && value) { - answered = true; - } - responses[name] = value; - }); - } else { - if (itemData.typ == 'multichoice' && itemData.subtype != 'r') { - name = nameTemp + '[0]'; - } else { - name = nameTemp; - } - - if (itemData.typ == 'multichoice' || itemData.typ == 'multichoicerated') { - value = itemData.value || 0; - } else if (itemData.typ == 'numeric') { - value = itemData.value || itemData.value == 0 ? itemData.value : ''; - - if (value != '') { - if ((itemData.rangefrom != '' && value < itemData.rangefrom) || - (itemData.rangeto != '' && value > itemData.rangeto)) { - itemData.hasError = true; - } - } - } else { - value = itemData.value || itemData.value == 0 ? itemData.value : ''; - } - - answered = !!value; - responses[name] = value; - } - - if (itemData.required && !answered) { - // Check if it has any value. - itemData.isEmpty = true; - } else { - itemData.isEmpty = false; - } - } - }); - - return responses; - } - - /** - * Returns the feedback user responses with extra info. - * - * @param feedbackId Feedback ID. - * @param options Other options. - * @return Promise resolved when the info is retrieved. - */ - getResponsesAnalysis(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise { - return this.feedbackProvider.getResponsesAnalysis(feedbackId, options).then((responses) => { - return this.addImageProfileToAttempts(responses.attempts).then((attempts) => { - responses.attempts = attempts; - - return responses; - }); - }); - } - - /** - * Handle a show entries link. - * - * @param navCtrl Nav controller to use to navigate. Can be undefined/null. - * @param params URL params. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - handleShowEntriesLink(navCtrl: NavController, params: any, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const modal = this.domUtils.showModalLoading(), - moduleId = params.id; - - return this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => { - let stateParams; - - if (typeof params.showcompleted == 'undefined') { - // Param showcompleted not defined. Show entry list. - stateParams = { - module: module, - courseId: module.course - }; - - return this.linkHelper.goInSite(navCtrl, 'AddonModFeedbackRespondentsPage', stateParams, siteId); - } - - return this.feedbackProvider.getAttempt(module.instance, params.showcompleted, { - cmId: moduleId, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((attempt) => { - stateParams = { - moduleId: module.id, - attempt: attempt, - feedbackId: module.instance, - courseId: module.course - }; - - return this.linkHelper.goInSite(navCtrl, 'AddonModFeedbackAttemptPage', stateParams, siteId); - }); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Add Image profile url field on attempts - * - * @param attempts Attempts array to get profile from. - * @return Returns the same array with the profileimageurl added if found. - */ - protected addImageProfileToAttempts(attempts: any): Promise { - const promises = attempts.map((attempt) => { - return this.userProvider.getProfile(attempt.userid, attempt.courseid, true).then((user) => { - attempt.profileimageurl = user.profileimageurl; - }).catch(() => { - // Error getting profile, resolve promise without adding any extra data. - }); - }); - - return Promise.all(promises).then(() => { - return attempts; - }); - } - - /** - * Helper function to open a feature in the app. - * - * @param feature Name of the feature to open. - * @param navCtrl NavController. - * @param module Course module activity object. - * @param courseId Course Id. - * @param group Course module activity object. - * @return Resolved when navigation animation is done. - */ - openFeature(feature: string, navCtrl: NavController, module: any, courseId: number, group: number = 0): Promise { - const pageName = feature && feature != 'analysis' ? 'AddonModFeedback' + feature + 'Page' : 'AddonModFeedbackIndexPage', - stateParams = { - module: module, - moduleId: module.id, - courseId: courseId, - feedbackId: module.instance, - group: group - }; - - // Only check history if navigating through tabs. - if (pageName == 'AddonModFeedbackIndexPage') { - stateParams['tab'] = feature == 'analysis' ? 'analysis' : 'overview'; - const view = this.getPageView(pageName, module.instance, 'feedbackId', 'AddonModFeedback', navCtrl); - - if (view) { - // Go back to the found page. - return navCtrl.popTo(view); - } - } - - // Not found, open new state. - return navCtrl.push(pageName, stateParams); - } - - /** - * Helper funtion for item type Label. - * - * @param item Item to process. - * @return Item processed to show form. - */ - protected getItemFormLabel(item: any): any { - item.template = 'label'; - item.name = ''; - item.presentation = this.textUtils.replacePluginfileUrls(item.presentation, item.itemfiles); - - return item; - } - - /** - * Helper funtion for item type Info. - * - * @param item Item to process. - * @return Item processed to show form. - */ - protected getItemFormInfo(item: any): any { - item.template = 'label'; - - const type = parseInt(item.presentation, 10); - - if (type == this.MODE_COURSE || type == this.MODE_CATEGORY) { - item.presentation = item.otherdata; - item.value = typeof item.rawValue != 'undefined' ? item.rawValue : item.otherdata; - } else if (type == this.MODE_RESPONSETIME) { - item.value = '__CURRENT__TIMESTAMP__'; - const tempValue = typeof item.rawValue != 'undefined' ? item.rawValue * 1000 : new Date().getTime(); - item.presentation = this.timeUtils.userDate(tempValue); - } else { - // Errors on item, return false. - return false; - } - - return item; - } - - /** - * Helper funtion for item type Numeric. - * - * @param item Item to process. - * @return Item processed to show form. - */ - protected getItemFormNumeric(item: any): any { - item.template = 'numeric'; - - const range = item.presentation.split(AddonModFeedbackProvider.LINE_SEP) || []; - range[0] = range.length > 0 ? parseInt(range[0], 10) : undefined; - range[1] = range.length > 1 ? parseInt(range[1], 10) : undefined; - - item.rangefrom = typeof range[0] == 'number' && !isNaN(range[0]) ? range[0] : ''; - item.rangeto = typeof range[1] == 'number' && !isNaN(range[1]) ? range[1] : ''; - item.value = typeof item.rawValue != 'undefined' ? parseFloat(item.rawValue) : ''; - item.postfix = this.getNumericBoundariesForDisplay(item.rangefrom, item.rangeto); - - return item; - } - - /** - * Helper funtion for item type Text field. - * - * @param item Item to process. - * @return Item processed to show form. - */ - protected getItemFormTextfield(item: any): any { - item.template = 'textfield'; - item.length = item.presentation.split(AddonModFeedbackProvider.LINE_SEP)[1] || 255; - item.value = typeof item.rawValue != 'undefined' ? item.rawValue : ''; - - return item; - } - - /** - * Helper funtion for item type Textarea. - * - * @param item Item to process. - * @return Item processed to show form. - */ - protected getItemFormTextarea(item: any): any { - item.template = 'textarea'; - item.value = typeof item.rawValue != 'undefined' ? item.rawValue : ''; - - return item; - } - - /** - * Helper funtion for item type Multichoice. - * - * @param item Item to process. - * @return Item processed to show form. - */ - protected getItemFormMultichoice(item: any): any { - let parts = item.presentation.split(AddonModFeedbackProvider.MULTICHOICE_TYPE_SEP) || []; - item.subtype = parts.length > 0 && parts[0] ? parts[0] : 'r'; - item.template = 'multichoice-' + item.subtype; - - item.presentation = parts.length > 1 ? parts[1] : ''; - if (item.subtype != 'd') { - parts = item.presentation.split(AddonModFeedbackProvider.MULTICHOICE_ADJUST_SEP) || []; - item.presentation = parts.length > 0 ? parts[0] : ''; - // Horizontal are not supported right now. item.horizontal = parts.length > 1 && !!parts[1]; - } - - item.choices = item.presentation.split(AddonModFeedbackProvider.LINE_SEP) || []; - item.choices = item.choices.map((choice, index) => { - const weightValue = choice.split(AddonModFeedbackProvider.MULTICHOICERATED_VALUE_SEP) || ['']; - choice = weightValue.length == 1 ? weightValue[0] : '(' + weightValue[0] + ') ' + weightValue[1]; - - return {value: index + 1, label: choice}; - }); - - if (item.subtype === 'r' && item.options.search(AddonModFeedbackProvider.MULTICHOICE_HIDENOSELECT) == -1) { - item.choices.unshift({value: 0, label: this.translate.instant('addon.mod_feedback.not_selected')}); - item.value = typeof item.rawValue != 'undefined' ? parseInt(item.rawValue, 10) : 0; - } else if (item.subtype === 'd') { - item.choices.unshift({value: 0, label: ''}); - item.value = typeof item.rawValue != 'undefined' ? parseInt(item.rawValue, 10) : 0; - } else if (item.subtype === 'c') { - if (typeof item.rawValue == 'undefined') { - item.value = ''; - } else { - item.rawValue = '' + item.rawValue; - const values = item.rawValue.split(AddonModFeedbackProvider.LINE_SEP); - item.choices.forEach((choice) => { - for (const x in values) { - if (choice.value == values[x]) { - choice.checked = true; - - return; - } - } - }); - } - } else { - item.value = typeof item.rawValue != 'undefined' ? parseInt(item.rawValue, 10) : ''; - } - - return item; - } - - /** - * Helper funtion for item type Captcha. - * - * @param item Item to process. - * @return Item processed to show form. - */ - protected getItemFormCaptcha(item: any): any { - const data = this.textUtils.parseJSON(item.otherdata); - if (data && data.length > 3) { - item.captcha = { - recaptchapublickey: data[3] - }; - } - item.template = 'captcha'; - item.value = ''; - - return item; - } - - /** - * Process and returns item to print form. - * - * @param item Item to process. - * @param preview Previewing options. - * @return Item processed to show form. - */ - getItemForm(item: any, preview: boolean): any { - switch (item.typ) { - case 'label': - return this.getItemFormLabel(item); - case 'info': - return this.getItemFormInfo(item); - case 'numeric': - return this.getItemFormNumeric(item); - case 'textfield': - return this.getItemFormTextfield(item); - case 'textarea': - return this.getItemFormTextarea(item); - case 'multichoice': - return this.getItemFormMultichoice(item); - case 'multichoicerated': - return this.getItemFormMultichoice(item); - case 'pagebreak': - if (!preview) { - // Pagebreaks are only used on preview. - return false; - } - break; - case 'captcha': - // Captcha is not supported right now. However label will be shown. - return this.getItemFormCaptcha(item); - default: - return false; - } - - return item; - } - - /** - * Returns human-readable boundaries (min - max). - * Based on Moodle's get_boundaries_for_display. - * - * @param rangeFrom Range from. - * @param rangeTo Range to. - * @return Human-readable boundaries. - */ - protected getNumericBoundariesForDisplay(rangeFrom: number, rangeTo: number): string { - const rangeFromSet = typeof rangeFrom == 'number', - rangeToSet = typeof rangeTo == 'number'; - - if (!rangeFromSet && rangeToSet) { - return ' (' + this.translate.instant('addon.mod_feedback.maximal') + ': ' + this.utils.formatFloat(rangeTo) + ')'; - } else if (rangeFromSet && !rangeToSet) { - return ' (' + this.translate.instant('addon.mod_feedback.minimal') + ': ' + this.utils.formatFloat(rangeFrom) + ')'; - } else if (!rangeFromSet && !rangeToSet) { - return ''; - } - - return ' (' + this.utils.formatFloat(rangeFrom) + ' - ' + this.utils.formatFloat(rangeTo) + ')'; - } - -} diff --git a/src/addon/mod/feedback/providers/link-handler.ts b/src/addon/mod/feedback/providers/link-handler.ts deleted file mode 100644 index a6021911e..000000000 --- a/src/addon/mod/feedback/providers/link-handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModFeedbackProvider } from './feedback'; - -/** - * Handler to treat links to feedback. - */ -@Injectable() -export class AddonModFeedbackLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModFeedbackLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, - protected feedbackProvider: AddonModFeedbackProvider) { - super(courseHelper, 'AddonModFeedback', 'feedback'); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.feedbackProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/feedback/providers/list-link-handler.ts b/src/addon/mod/feedback/providers/list-link-handler.ts deleted file mode 100644 index 655efa066..000000000 --- a/src/addon/mod/feedback/providers/list-link-handler.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModFeedbackProvider } from './feedback'; - -/** - * Handler to treat links to feedback list page. - */ -@Injectable() -export class AddonModFeedbackListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModFeedbackListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected feedbackProvider: AddonModFeedbackProvider) { - super(linkHelper, translate, 'AddonModFeedback', 'feedback'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return this.feedbackProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/feedback/providers/module-handler.ts b/src/addon/mod/feedback/providers/module-handler.ts deleted file mode 100644 index 6dc99f339..000000000 --- a/src/addon/mod/feedback/providers/module-handler.ts +++ /dev/null @@ -1,89 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModFeedbackIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModFeedbackProvider } from './feedback'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support feedback modules. - */ -@Injectable() -export class AddonModFeedbackModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModFeedback'; - modName = 'feedback'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider, private feedbackProvider: AddonModFeedbackProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return this.feedbackProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_feedback-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModFeedbackIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModFeedbackIndexComponent; - } -} diff --git a/src/addon/mod/feedback/providers/offline.ts b/src/addon/mod/feedback/providers/offline.ts deleted file mode 100644 index 32c63b125..000000000 --- a/src/addon/mod/feedback/providers/offline.ts +++ /dev/null @@ -1,175 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; - -/** - * Service to handle Offline feedback. - */ -@Injectable() -export class AddonModFeedbackOfflineProvider { - - protected logger; - - // Variables for database. - static FEEDBACK_TABLE = 'addon_mod_feedback_answers'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModFeedbackOfflineProvider', - version: 1, - tables: [ - { - name: AddonModFeedbackOfflineProvider.FEEDBACK_TABLE, - columns: [ - { - name: 'feedbackid', - type: 'INTEGER' - }, - { - name: 'page', - type: 'INTEGER' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'responses', - type: 'TEXT' - }, - { - name: 'timemodified', - type: 'INTEGER' - } - ], - primaryKeys: ['feedbackid', 'page'] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, - private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider) { - this.logger = logger.getInstance('AddonModFeedbackOfflineProvider'); - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete the stored for a certain feedback page. - * - * @param feedbackId Feedback ID. - * @param page Page of the form to delete responses from. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteFeedbackPageResponses(feedbackId: number, page: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModFeedbackOfflineProvider.FEEDBACK_TABLE, {feedbackid: feedbackId, page: page}); - }); - } - - /** - * Get all the stored feedback responses data from all the feedback. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with entries. - */ - getAllFeedbackResponses(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getAllRecords(AddonModFeedbackOfflineProvider.FEEDBACK_TABLE).then((entries) => { - entries.forEach((entry) => { - entry.responses = this.textUtils.parseJSON(entry.responses); - }); - - return entries; - }); - }); - } - - /** - * Get all the stored responses from a certain feedback. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with responses. - */ - getFeedbackResponses(feedbackId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModFeedbackOfflineProvider.FEEDBACK_TABLE, {feedbackid: feedbackId}); - }).then((entries) => { - entries.forEach((entry) => { - entry.responses = this.textUtils.parseJSON(entry.responses); - }); - - return entries; - }); - } - - /** - * Get the stored responses for a certain feedback page. - * - * @param feedbackId Feedback ID. - * @param page Page of the form to get responses from. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with responses. - */ - getFeedbackPageResponses(feedbackId: number, page: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecord(AddonModFeedbackOfflineProvider.FEEDBACK_TABLE, {feedbackid: feedbackId, page: page}); - }).then((entry) => { - entry.responses = this.textUtils.parseJSON(entry.responses); - - return entry; - }); - } - - /** - * Get if the feedback have something to be synced. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if the feedback have something to be synced. - */ - hasFeedbackOfflineData(feedbackId: number, siteId?: string): Promise { - return this.getFeedbackResponses(feedbackId, siteId).then((responses) => { - return !!responses.length; - }); - } - - /** - * Save page responses to be sent later. - * - * @param feedbackId Feedback ID. - * @param page The page being processed. - * @param responses The data to be processed the key is the field name (usually type[index]_id) - * @param courseId Course ID the feedback belongs to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - saveResponses(feedbackId: number, page: number, responses: any, courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - feedbackid: feedbackId, - page: page, - courseid: courseId, - responses: JSON.stringify(responses), - timemodified: this.timeUtils.timestamp() - }; - - return site.getDb().insertRecord(AddonModFeedbackOfflineProvider.FEEDBACK_TABLE, entry); - }); - } -} diff --git a/src/addon/mod/feedback/providers/prefetch-handler.ts b/src/addon/mod/feedback/providers/prefetch-handler.ts deleted file mode 100644 index 375e6a049..000000000 --- a/src/addon/mod/feedback/providers/prefetch-handler.ts +++ /dev/null @@ -1,280 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { AddonModFeedbackProvider } from './feedback'; -import { AddonModFeedbackHelperProvider } from './helper'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreGroupsProvider } from '@providers/groups'; -import { AddonModFeedbackSyncProvider } from './sync'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; -import { CoreWSExternalFile } from '@providers/ws'; - -/** - * Handler to prefetch feedbacks. - */ -@Injectable() -export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModFeedback'; - modName = 'feedback'; - component = AddonModFeedbackProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^attemptsfinished|^attemptsunfinished$/; - - protected syncProvider: AddonModFeedbackSyncProvider; // It will be injected later to prevent circular dependencies. - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected feedbackProvider: AddonModFeedbackProvider, - protected feedbackHelper: AddonModFeedbackHelperProvider, - protected timeUtils: CoreTimeUtilsProvider, - protected groupsProvider: CoreGroupsProvider, - protected injector: Injector) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Get the list of downloadable files. - * - * @param module Module to get the files. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the list of files. - */ - async getFiles(module: any, courseId: number, single?: boolean): Promise { - let files = []; - - const feedback = await this.feedbackProvider.getFeedback(courseId, module.id); - - // Get intro files and page after submit files. - files = feedback.pageaftersubmitfiles || []; - files = files.concat(this.getIntroFilesFromInstance(module, feedback)); - - try { - const response = await this.feedbackProvider.getItems(feedback.id); - - response.items.forEach((item) => { - files = files.concat(item.itemfiles.map((file) => { - file.fileurl = file.fileurl || file.url; - - return file; - })); - }); - - } catch (e) { - // Ignore errors. - } - - return files; - } - - /** - * Returns feedback intro files. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @return Promise resolved with list of intro files. - */ - getIntroFiles(module: any, courseId: number): Promise { - return this.feedbackProvider.getFeedback(courseId, module.id).catch(() => { - // Not found, return undefined so module description is used. - }).then((feedback) => { - return this.getIntroFilesFromInstance(module, feedback); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.feedbackProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - return this.feedbackProvider.invalidateFeedbackData(courseId); - } - - /** - * Check if a feedback is downloadable. - * A feedback isn't downloadable if it's not open yet. - * Closed feedback are downloadable because teachers can always see the results. - * - * @param module Module to check. - * @param courseId Course ID the module belongs to. - * @return Promise resolved with true if downloadable, resolved with false otherwise. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - return this.feedbackProvider.getFeedback(courseId, module.id, { - readingStrategy: CoreSitesReadingStrategy.PreferCache, - }).then((feedback) => { - const now = this.timeUtils.timestamp(); - - // Check time first if available. - if (feedback.timeopen && feedback.timeopen > now) { - return false; - } - if (feedback.timeclose && feedback.timeclose < now) { - return false; - } - - return this.feedbackProvider.getFeedbackAccessInformation(feedback.id, {cmId: module.id}).then((accessData) => { - return accessData.isopen; - }); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.feedbackProvider.isPluginEnabled(); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchFeedback.bind(this)); - } - - /** - * Prefetch a feedback. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchFeedback(module: any, courseId: number, single: boolean, siteId: string): Promise { - const commonOptions = { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - const modOptions = { - cmId: module.id, - ...commonOptions, // Include all common options. - }; - - // Prefetch the feedback data. - return this.feedbackProvider.getFeedback(courseId, module.id, commonOptions).then((feedback) => { - let files = (feedback.pageaftersubmitfiles || []).concat(this.getIntroFilesFromInstance(module, feedback)); - - return this.feedbackProvider.getFeedbackAccessInformation(feedback.id, modOptions).then((accessData) => { - const p2 = []; - if (accessData.canedititems || accessData.canviewreports) { - // Get all groups analysis. - p2.push(this.feedbackProvider.getAnalysis(feedback.id, modOptions)); - p2.push(this.groupsProvider.getActivityGroupInfo(feedback.coursemodule, true, undefined, siteId, true) - .then((groupInfo) => { - const p3 = []; - - if (!groupInfo.groups || groupInfo.groups.length == 0) { - groupInfo.groups = [{id: 0}]; - } - groupInfo.groups.forEach((group) => { - const groupOptions = { - groupId: group.id, - ...modOptions, // Include all mod options. - }; - - p3.push(this.feedbackProvider.getAnalysis(feedback.id, groupOptions)); - p3.push(this.feedbackProvider.getAllResponsesAnalysis(feedback.id, groupOptions)); - - if (!accessData.isanonymous) { - p3.push(this.feedbackProvider.getAllNonRespondents(feedback.id, groupOptions)); - } - }); - - return Promise.all(p3); - })); - } - - p2.push(this.feedbackProvider.getItems(feedback.id, commonOptions).then((response) => { - response.items.forEach((item) => { - files = files.concat(item.itemfiles); - }); - - return this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id); - })); - - if (accessData.cancomplete && accessData.cansubmit && !accessData.isempty) { - // Send empty data, so it will recover last completed feedback attempt values. - p2.push(this.feedbackProvider.processPageOnline(feedback.id, 0, {}, undefined, siteId).finally(() => { - const p4 = []; - - p4.push(this.feedbackProvider.getCurrentValues(feedback.id, modOptions)); - p4.push(this.feedbackProvider.getResumePage(feedback.id, modOptions)); - - return Promise.all(p4); - })); - } - - return Promise.all(p2); - }); - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - if (!this.syncProvider) { - this.syncProvider = this.injector.get(AddonModFeedbackSyncProvider); - } - - return this.syncProvider.syncFeedback(module.instance, siteId); - } -} diff --git a/src/addon/mod/feedback/providers/print-link-handler.ts b/src/addon/mod/feedback/providers/print-link-handler.ts deleted file mode 100644 index 92ba6e803..000000000 --- a/src/addon/mod/feedback/providers/print-link-handler.ts +++ /dev/null @@ -1,87 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonModFeedbackProvider } from './feedback'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Content links handler for feedback print questions. - * Match mod/feedback/print.php with a valid feedback id. - */ -@Injectable() -export class AddonModFeedbackPrintLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModFeedbackPrintLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModFeedback'; - pattern = /\/mod\/feedback\/print\.php.*([\?\&](id)=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private feedbackProvider: AddonModFeedbackProvider, - private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - moduleId = params.id; - - this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => { - const stateParams = { - module: module, - moduleId: module.id, - courseId: module.course, - preview: true - }; - - return this.linkHelper.goInSite(navCtrl, 'AddonModFeedbackFormPage', stateParams, siteId); - }).finally(() => { - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - if (typeof params.id == 'undefined') { - return false; - } - - return this.feedbackProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/mod/feedback/providers/push-click-handler.ts b/src/addon/mod/feedback/providers/push-click-handler.ts deleted file mode 100644 index 44753d590..000000000 --- a/src/addon/mod/feedback/providers/push-click-handler.ts +++ /dev/null @@ -1,69 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModFeedbackProvider } from './feedback'; -import { AddonModFeedbackHelperProvider } from './helper'; - -/** - * Handler for feedback push notifications clicks. - */ -@Injectable() -export class AddonModFeedbackPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonModFeedbackPushClickHandler'; - priority = 200; - featureName = 'CoreCourseModuleDelegate_AddonModFeedback'; - - constructor(private utils: CoreUtilsProvider, private feedbackHelper: AddonModFeedbackHelperProvider, - private urlUtils: CoreUrlUtilsProvider, private courseHelper: CoreCourseHelperProvider, - private feedbackProvider: AddonModFeedbackProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - if (this.utils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_feedback' && - (notification.name == 'submission' || notification.name == 'message')) { - - return this.feedbackProvider.isPluginEnabled(notification.site); - } - - return false; - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - handleClick(notification: any): Promise { - const contextUrlParams = this.urlUtils.extractUrlParams(notification.contexturl), - courseId = Number(notification.courseid), - moduleId = Number(contextUrlParams.id); - - if (notification.name == 'submission') { - return this.feedbackHelper.handleShowEntriesLink(undefined, contextUrlParams, notification.site); - } else { - return this.courseHelper.navigateToModule(moduleId, notification.site, courseId); - } - } -} diff --git a/src/addon/mod/feedback/providers/show-entries-link-handler.ts b/src/addon/mod/feedback/providers/show-entries-link-handler.ts deleted file mode 100644 index 981e02b7d..000000000 --- a/src/addon/mod/feedback/providers/show-entries-link-handler.ts +++ /dev/null @@ -1,77 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { AddonModFeedbackProvider } from './feedback'; -import { AddonModFeedbackHelperProvider } from './helper'; - -/** - * Content links handler for feedback show entries questions. - * Match mod/feedback/show_entries.php with a valid feedback id. - */ -@Injectable() -export class AddonModFeedbackShowEntriesLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModFeedbackShowEntriesLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModFeedback'; - pattern = /\/mod\/feedback\/show_entries\.php.*([\?\&](id|showcompleted)=\d+)/; - - constructor(private feedbackProvider: AddonModFeedbackProvider, private feedbackHelper: AddonModFeedbackHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - this.feedbackHelper.handleShowEntriesLink(navCtrl, params, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.feedbackProvider.isPluginEnabled(siteId).then((enabled) => { - if (!enabled) { - return false; - } - - if (typeof params.id == 'undefined') { - // Cannot treat the URL. - return false; - } - - return true; - }); - } -} diff --git a/src/addon/mod/feedback/providers/show-non-respondents-link-handler.ts b/src/addon/mod/feedback/providers/show-non-respondents-link-handler.ts deleted file mode 100644 index 8b19b76a8..000000000 --- a/src/addon/mod/feedback/providers/show-non-respondents-link-handler.ts +++ /dev/null @@ -1,93 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonModFeedbackProvider } from './feedback'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Content links handler for feedback show non respondents. - * Match mod/feedback/show_nonrespondents.php with a valid feedback id. - */ -@Injectable() -export class AddonModFeedbackShowNonRespondentsLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModFeedbackShowNonRespondentsLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModFeedback'; - pattern = /\/mod\/feedback\/show_nonrespondents\.php.*([\?\&](id)=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private feedbackProvider: AddonModFeedbackProvider, - private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - moduleId = params.id; - - this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => { - const stateParams = { - module: module, - moduleId: module.id, - courseId: module.course - }; - - return this.linkHelper.goInSite(navCtrl, 'AddonModFeedbackNonRespondentsPage', stateParams, siteId); - }).finally(() => { - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.feedbackProvider.isPluginEnabled(siteId).then((enabled) => { - if (!enabled) { - return false; - } - - if (typeof params.id == 'undefined') { - // Cannot treat the URL. - return false; - } - - return true; - }); - } -} diff --git a/src/addon/mod/feedback/providers/sync-cron-handler.ts b/src/addon/mod/feedback/providers/sync-cron-handler.ts deleted file mode 100644 index f9da19369..000000000 --- a/src/addon/mod/feedback/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModFeedbackSyncProvider } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModFeedbackSyncCronHandler implements CoreCronHandler { - name = 'AddonModFeedbackSyncCronHandler'; - - constructor(private feedbackSync: AddonModFeedbackSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.feedbackSync.syncAllFeedbacks(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.feedbackSync.syncInterval; - } -} diff --git a/src/addon/mod/feedback/providers/sync.ts b/src/addon/mod/feedback/providers/sync.ts deleted file mode 100644 index f4716e7b5..000000000 --- a/src/addon/mod/feedback/providers/sync.ts +++ /dev/null @@ -1,302 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreAppProvider } from '@providers/app'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { AddonModFeedbackOfflineProvider } from './offline'; -import { AddonModFeedbackProvider } from './feedback'; -import { CoreEventsProvider } from '@providers/events'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivitySyncBaseProvider } from '@core/course/classes/activity-sync'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreSyncProvider } from '@providers/sync'; -import { AddonModFeedbackPrefetchHandler } from './prefetch-handler'; - -/** - * Service to sync feedbacks. - */ -@Injectable() -export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_feedback_autom_synced'; - protected componentTranslate: string; - - constructor(protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider, - protected appProvider: CoreAppProvider, private feedbackOffline: AddonModFeedbackOfflineProvider, - private eventsProvider: CoreEventsProvider, private feedbackProvider: AddonModFeedbackProvider, - protected translate: TranslateService, private utils: CoreUtilsProvider, protected textUtils: CoreTextUtilsProvider, - private courseProvider: CoreCourseProvider, syncProvider: CoreSyncProvider, timeUtils: CoreTimeUtilsProvider, - private logHelper: CoreCourseLogHelperProvider, prefetchDelegate: CoreCourseModulePrefetchDelegate, - prefetchHandler: AddonModFeedbackPrefetchHandler) { - - super('AddonModFeedbackSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils, prefetchDelegate, prefetchHandler); - - this.componentTranslate = courseProvider.translateModuleName('feedback'); - } - - /** - * Conveniece function to prefetch data after an update. - * - * @param module Module. - * @param courseId Course ID. - * @param regex If regex matches, don't download the data. Defaults to check files and timers. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetchAfterUpdate(module: any, courseId: number, regex?: RegExp, siteId?: string): Promise { - regex = regex || /^.*files$|^timers/; - - return super.prefetchAfterUpdate(module, courseId, regex, siteId); - } - - /** - * Try to synchronize all the feedbacks in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllFeedbacks(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all feedbacks', this.syncAllFeedbacksFunc.bind(this), [force], siteId); - } - - /** - * Sync all pending feedbacks on a site. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllFeedbacksFunc(siteId?: string, force?: boolean): Promise { - // Sync all new responses. - return this.feedbackOffline.getAllFeedbackResponses(siteId).then((responses) => { - const promises = {}; - - // Do not sync same feedback twice. - for (const i in responses) { - const response = responses[i]; - - if (typeof promises[response.feedbackid] != 'undefined') { - continue; - } - - promises[response.feedbackid] = force ? this.syncFeedback(response.feedbackid, siteId) : - this.syncFeedbackIfNeeded(response.feedbackid, siteId); - - promises[response.feedbackid].then((result) => { - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModFeedbackSyncProvider.AUTO_SYNCED, { - feedbackId: response.feedbackid, - userId: response.userid, - warnings: result.warnings - }, siteId); - } - }); - } - - // Promises will be an object so, convert to an array first; - return Promise.all(this.utils.objectToArray(promises)); - }); - } - - /** - * Sync a feedback only if a certain time has passed since the last time. - * - * @param feedbackId Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the feedback is synced or if it doesn't need to be synced. - */ - syncFeedbackIfNeeded(feedbackId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.isSyncNeeded(feedbackId, siteId).then((needed) => { - if (needed) { - return this.syncFeedback(feedbackId, siteId); - } - }); - } - - /** - * Synchronize all offline responses of a feedback. - * - * @param feedbackId Feedback ID to be synced. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncFeedback(feedbackId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = feedbackId; - - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this feedback, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - // Verify that feedback isn't blocked. - if (this.syncProvider.isBlocked(AddonModFeedbackProvider.COMPONENT, syncId, siteId)) { - this.logger.debug(`Cannot sync feedback '${syncId}' because it is blocked.`); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - const result = { - warnings: [], - updated: false - }; - - let courseId, - feedback; - - this.logger.debug(`Try to sync feedback '${feedbackId}' in site ${siteId}'`); - - // Sync offline logs. - const syncPromise = this.logHelper.syncIfNeeded(AddonModFeedbackProvider.COMPONENT, feedbackId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Get offline responses to be sent. - return this.feedbackOffline.getFeedbackResponses(feedbackId, siteId).catch(() => { - // No offline data found, return empty array. - return []; - }); - }).then((responses) => { - if (!responses.length) { - // Nothing to sync. - return; - } - - if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - courseId = responses[0].courseid; - - return this.feedbackProvider.getFeedbackById(courseId, feedbackId, {siteId}).then((feedbackData) => { - feedback = feedbackData; - - if (!feedback.multiple_submit) { - // If it does not admit multiple submits, check if it is completed to know if we can submit. - return this.feedbackProvider.isCompleted(feedbackId, {cmId: feedback.coursemodule, siteId}); - } else { - return false; - } - }).then((isCompleted) => { - if (isCompleted) { - // Cannot submit again, delete resposes. - const promises = []; - - responses.forEach((data) => { - promises.push(this.feedbackOffline.deleteFeedbackPageResponses(feedbackId, data.page, siteId)); - }); - - result.updated = true; - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: feedback.name, - error: this.translate.instant('addon.mod_feedback.this_feedback_is_already_submitted') - })); - - return Promise.all(promises); - } - - return this.feedbackProvider.getCurrentCompletedTimeModified(feedbackId, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((timemodified) => { - // Sort by page. - responses.sort((a, b) => { - return a.page - b.page; - }); - - responses = responses.map((data) => { - return { - func: this.processPage.bind(this), - params: [feedback, data, siteId, timemodified, result], - blocking: true - }; - }); - - // Execute all the processes in order to solve dependencies. - return this.utils.executeOrderedPromises(responses); - }); - }); - }).then(() => { - if (result.updated) { - // Data has been sent to server, update data. - return this.courseProvider.getModuleBasicInfoByInstance(feedbackId, 'feedback', siteId).then((module) => { - return this.prefetchAfterUpdate(module, courseId, undefined, siteId); - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(syncId, siteId); - }).then(() => { - return result; - }); - - return this.addOngoingSync(syncId, syncPromise, siteId); - } - - /** - * Convenience function to sync process page calls. - * - * @param feedback Feedback object. - * @param data Response data. - * @param siteId Site Id. - * @param timemodified Current completed modification time. - * @param result Result object to be modified. - * @return Resolve when done or rejected with error. - */ - protected processPage(feedback: any, data: any, siteId: string, timemodified: number, result: any): Promise { - // Delete all pages that are submitted before changing website. - if (timemodified > data.timemodified) { - return this.feedbackOffline.deleteFeedbackPageResponses(feedback.id, data.page, siteId); - } - - return this.feedbackProvider.processPageOnline(feedback.id, data.page, data.responses, false, siteId).then(() => { - result.updated = true; - - return this.feedbackOffline.deleteFeedbackPageResponses(feedback.id, data.page, siteId); - }).catch((error) => { - if (error && error.wserror) { - // The WebService has thrown an error, this means that responses cannot be submitted. Delete them. - result.updated = true; - - return this.feedbackOffline.deleteFeedbackPageResponses(feedback.id, data.page, siteId).then(() => { - // Responses deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: feedback.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }); - } -} diff --git a/src/addon/mod/folder/components/components.module.ts b/src/addon/mod/folder/components/components.module.ts deleted file mode 100644 index cb4c5988e..000000000 --- a/src/addon/mod/folder/components/components.module.ts +++ /dev/null @@ -1,43 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModFolderIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModFolderIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModFolderIndexComponent - ], - entryComponents: [ - AddonModFolderIndexComponent - ] -}) -export class AddonModFolderComponentsModule {} diff --git a/src/addon/mod/folder/components/index/addon-mod-folder-index.html b/src/addon/mod/folder/components/index/addon-mod-folder-index.html deleted file mode 100644 index e56091080..000000000 --- a/src/addon/mod/folder/components/index/addon-mod-folder-index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -

{{file.name}}

-
- -
-
- - - -
diff --git a/src/addon/mod/folder/components/index/index.scss b/src/addon/mod/folder/components/index/index.scss deleted file mode 100644 index f2fb207d2..000000000 --- a/src/addon/mod/folder/components/index/index.scss +++ /dev/null @@ -1,6 +0,0 @@ -ion-app.app-root addon-mod-folder-index { - .item-media > img:first-child { - width: 24px; - height: 24px; - } -} \ No newline at end of file diff --git a/src/addon/mod/folder/components/index/index.ts b/src/addon/mod/folder/components/index/index.ts deleted file mode 100644 index 805210106..000000000 --- a/src/addon/mod/folder/components/index/index.ts +++ /dev/null @@ -1,136 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Injector } from '@angular/core'; -import { CoreCourseModuleMainResourceComponent } from '@core/course/classes/main-resource-component'; -import { AddonModFolderProvider } from '../../providers/folder'; -import { AddonModFolderHelperProvider } from '../../providers/helper'; - -/** - * Component that displays a folder. - * @todo Adding a new file in a folder updates the revision of all the files, so they're all shown as outdated. - * To ignore revision in folders we'll have to modify CoreCourseModulePrefetchDelegate, core-file and CoreFilepoolProvider. - */ -@Component({ - selector: 'addon-mod-folder-index', - templateUrl: 'addon-mod-folder-index.html', -}) -export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceComponent { - @Input() folderInstance?: any; // The mod_folder instance. - @Input() subfolder?: any; // Subfolder to show. - - component = AddonModFolderProvider.COMPONENT; - canGetFolder: boolean; - contents: any; - - constructor(injector: Injector, - protected folderProvider: AddonModFolderProvider, - protected folderHelper: AddonModFolderHelperProvider) { - super(injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.canGetFolder = this.folderProvider.isGetFolderWSAvailable(); - - if (this.subfolder) { - // Subfolder. Use module param. - this.showModuleData(this.subfolder.contents); - this.loaded = true; - this.refreshIcon = 'refresh'; - } else { - this.loadContent().then(() => { - this.folderProvider.logView(this.module.instance, this.module.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }).finally(() => { - this.loaded = true; - this.refreshIcon = 'refresh'; - }); - } - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return this.folderProvider.invalidateContent(this.module.id, this.courseId); - } - - /** - * Convenience function to set data to display. - * - * @param folderContents Contents to show. - */ - protected showModuleData(folderContents: any): void { - this.description = this.folderInstance ? this.folderInstance.intro : this.module.description; - - if (this.subfolder) { - // Subfolder. - this.contents = folderContents; - } else { - this.contents = this.folderHelper.formatContents(folderContents); - } - } - - /** - * Download folder contents. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchContent(refresh?: boolean): Promise { - let promise, - folderContents = this.module.contents; - - if (this.canGetFolder) { - promise = this.folderProvider.getFolder(this.courseId, this.module.id).then((folder) => { - return this.courseProvider.loadModuleContents(this.module, this.courseId, undefined, false, refresh).then(() => { - folderContents = this.module.contents; - this.folderInstance = folder; - - return folder; - }); - }); - } else { - promise = this.courseProvider.getModule(this.module.id, this.courseId).then((module) => { - if (!module.contents.length && this.module.contents.length && !this.appProvider.isOnline()) { - // The contents might be empty due to a cached data. Use the old ones. - module.contents = this.module.contents; - } - this.module = module; - folderContents = module.contents; - - return module; - }); - } - - return promise.then(() => { - - this.dataRetrieved.emit(this.folderInstance || this.module); - - this.showModuleData(folderContents); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } -} diff --git a/src/addon/mod/folder/folder.module.ts b/src/addon/mod/folder/folder.module.ts deleted file mode 100644 index fd60a4ff4..000000000 --- a/src/addon/mod/folder/folder.module.ts +++ /dev/null @@ -1,64 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModFolderProvider } from './providers/folder'; -import { AddonModFolderHelperProvider } from './providers/helper'; -import { AddonModFolderModuleHandler } from './providers/module-handler'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { AddonModFolderComponentsModule } from './components/components.module'; -import { AddonModFolderPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModFolderLinkHandler } from './providers/link-handler'; -import { AddonModFolderListLinkHandler } from './providers/list-link-handler'; -import { AddonModFolderPluginFileHandler } from './providers/pluginfile-handler'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -// List of providers (without handlers). -export const ADDON_MOD_FOLDER_PROVIDERS: any[] = [ - AddonModFolderProvider, - AddonModFolderHelperProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModFolderComponentsModule - ], - providers: [ - AddonModFolderProvider, - AddonModFolderHelperProvider, - AddonModFolderModuleHandler, - AddonModFolderPrefetchHandler, - AddonModFolderLinkHandler, - AddonModFolderListLinkHandler, - AddonModFolderPluginFileHandler - ] -}) -export class AddonModFolderModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModFolderModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModFolderPrefetchHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModFolderLinkHandler, - pluginfileDelegate: CorePluginFileDelegate, pluginfileHandler: AddonModFolderPluginFileHandler, - listLinkHandler: AddonModFolderListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - pluginfileDelegate.registerHandler(pluginfileHandler); - } -} diff --git a/src/addon/mod/folder/lang/en.json b/src/addon/mod/folder/lang/en.json deleted file mode 100644 index 40bcdb36a..000000000 --- a/src/addon/mod/folder/lang/en.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "emptyfilelist": "There are no files to show.", - "modulenameplural": "Folders" -} \ No newline at end of file diff --git a/src/addon/mod/folder/pages/index/index.html b/src/addon/mod/folder/pages/index/index.html deleted file mode 100644 index 27f9f1509..000000000 --- a/src/addon/mod/folder/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/folder/pages/index/index.module.ts b/src/addon/mod/folder/pages/index/index.module.ts deleted file mode 100644 index e3f60150b..000000000 --- a/src/addon/mod/folder/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModFolderComponentsModule } from '../../components/components.module'; -import { AddonModFolderIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModFolderIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModFolderComponentsModule, - IonicPageModule.forChild(AddonModFolderIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModFolderIndexPageModule {} diff --git a/src/addon/mod/folder/pages/index/index.ts b/src/addon/mod/folder/pages/index/index.ts deleted file mode 100644 index a64d7b499..000000000 --- a/src/addon/mod/folder/pages/index/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModFolderIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a folder. - */ -@IonicPage({ segment: 'addon-mod-folder-index' }) -@Component({ - selector: 'page-addon-mod-folder-index', - templateUrl: 'index.html', -}) -export class AddonModFolderIndexPage { - @ViewChild(AddonModFolderIndexComponent) folderComponent: AddonModFolderIndexComponent; - - title: string; - module: any; - courseId: number; - folderInstance: any; - subfolder: any; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.folderInstance = navParams.get('folderInstance'); - this.subfolder = navParams.get('subfolder'); - this.title = this.subfolder ? this.subfolder.name : this.module.name; - } - - /** - * Update some data based on the folder instance. - * - * @param folder Folder instance. - */ - updateData(folder: any): void { - this.title = folder.name || this.title; - } -} diff --git a/src/addon/mod/folder/providers/folder.ts b/src/addon/mod/folder/providers/folder.ts deleted file mode 100644 index a75520c16..000000000 --- a/src/addon/mod/folder/providers/folder.ts +++ /dev/null @@ -1,185 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; - -/** - * Service that provides some features for folder. - */ -@Injectable() -export class AddonModFolderProvider { - static COMPONENT = 'mmaModFolder'; - - protected ROOT_CACHE_KEY = 'mmaModFolder:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, - private utils: CoreUtilsProvider, private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModFolderProvider'); - } - - /** - * Get a folder by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the book is retrieved. - */ - getFolder(courseId: number, cmId: number, options?: CoreSitesCommonWSOptions): Promise { - return this.getFolderByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a folder. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the book is retrieved. - */ - protected getFolderByKey(courseId: number, key: string, value: any, options?: CoreSitesCommonWSOptions) - : Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getFolderCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModFolderProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_folder_get_folders_by_courses', params, preSets) - .then((response: AddonModFolderGetFoldersByCoursesResult): any => { - - if (response && response.folders) { - const currentFolder = response.folders.find((folder) => { - return folder[key] == value; - }); - if (currentFolder) { - return currentFolder; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for folder data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getFolderCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'folder:' + courseId; - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - const promises = []; - - promises.push(this.invalidateFolderData(courseId, siteId)); - promises.push(this.courseProvider.invalidateModule(moduleId, siteId)); - - return this.utils.allPromises(promises); - } - - /** - * Invalidates folder data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateFolderData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getFolderCacheKey(courseId)); - }); - } - - /** - * Returns whether or not getFolder WS available or not. - * - * @return If WS is avalaible. - * @since 3.3 - */ - isGetFolderWSAvailable(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('mod_folder_get_folders_by_courses'); - } - - /** - * Report a folder as being viewed. - * - * @param id Module ID. - * @param name Name of the folder. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - folderid: id - }; - - return this.logHelper.logSingle('mod_folder_view_folder', params, AddonModFolderProvider.COMPONENT, id, name, 'folder', - {}, siteId); - } -} - -/** - * Folder returned by mod_folder_get_folders_by_courses. - */ -export type AddonModFolderFolder = { - id: number; // Module id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Page name. - intro: string; // Summary. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles: CoreWSExternalFile[]; - revision: number; // Incremented when after each file changes, to avoid cache. - timemodified: number; // Last time the folder was modified. - display: number; // Display type of folder contents on a separate page or inline. - showexpanded: number; // 1 = expanded, 0 = collapsed for sub-folders. - showdownloadfolder: number; // Whether to show the download folder button. - section: number; // Course section id. - visible: number; // Module visibility. - groupmode: number; // Group mode. - groupingid: number; // Grouping id. -}; - -/** - * Result of WS mod_folder_get_folders_by_courses. - */ -export type AddonModFolderGetFoldersByCoursesResult = { - folders: AddonModFolderFolder[]; - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/folder/providers/helper.ts b/src/addon/mod/folder/providers/helper.ts deleted file mode 100644 index 643e8e300..000000000 --- a/src/addon/mod/folder/providers/helper.ts +++ /dev/null @@ -1,89 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCourseProvider } from '@core/course/providers/course'; - -/** - * Service that provides some features for folder. - */ -@Injectable() -export class AddonModFolderHelperProvider { - - constructor(private courseProvider: CoreCourseProvider) { - } - - /** - * Format folder contents, creating directory structure. - * Folders found in filepaths are added to the array. Each folder has the properties: name, fileicon, - * type (folder), filepath and contents (array with files and subfolders). - * - * @param contents Folder contents. - * @return Formatted contents. - */ - formatContents(contents: any[]): any[] { - const files = [], - folders = [], - folderIcon = this.courseProvider.getModuleIconSrc('folder'); - - contents.forEach((entry) => { - if (entry.filepath !== '/') { - // It's a file in a subfolder. Lets treat the path to add the subfolders to the array. - let directories, - currentList = folders, // Start at root level. - path = entry.filepath, - subpath = ''; - - // Remove first and last slash if needed. - if (path.substr(0, 1) === '/') { - path = path.substr(1); - } - if (path.substr(path.length - 1) === '/') { - path = path.slice(0, -1); - } - - directories = path.split('/'); - - directories.forEach((directory) => { - subpath = subpath + '/' + directory; - // Search if the directory is already stored in folders array. - const foundList = currentList.find((list) => { - return list.name === directory; - }); - - if (foundList) { - currentList = foundList.contents; - } else { - // Directory not found. Add it to the array. - const newFolder = { - name: directory, - fileicon: folderIcon, - contents: [], - filepath: subpath, - type: 'folder' - }; - currentList.push(newFolder); - currentList = newFolder.contents; - } - }); - - currentList.push(entry); - } else { - files.push(entry); - } - }); - - return folders.concat(files); - } -} diff --git a/src/addon/mod/folder/providers/link-handler.ts b/src/addon/mod/folder/providers/link-handler.ts deleted file mode 100644 index cba59acfb..000000000 --- a/src/addon/mod/folder/providers/link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to resource. - */ -@Injectable() -export class AddonModFolderLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModFolderLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModFolder', 'folder', 'f'); - } -} diff --git a/src/addon/mod/folder/providers/list-link-handler.ts b/src/addon/mod/folder/providers/list-link-handler.ts deleted file mode 100644 index 5d64b422c..000000000 --- a/src/addon/mod/folder/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to folder list page. - */ -@Injectable() -export class AddonModFolderListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModFolderListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModFolder', 'folder'); - } -} diff --git a/src/addon/mod/folder/providers/module-handler.ts b/src/addon/mod/folder/providers/module-handler.ts deleted file mode 100644 index 9cc5c3268..000000000 --- a/src/addon/mod/folder/providers/module-handler.ts +++ /dev/null @@ -1,88 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModFolderIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support folder modules. - */ -@Injectable() -export class AddonModFolderModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModFolder'; - modName = 'folder'; - - supportedFeatures = { - [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_folder-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModFolderIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModFolderIndexComponent; - } -} diff --git a/src/addon/mod/folder/providers/pluginfile-handler.ts b/src/addon/mod/folder/providers/pluginfile-handler.ts deleted file mode 100644 index 35e816d94..000000000 --- a/src/addon/mod/folder/providers/pluginfile-handler.ts +++ /dev/null @@ -1,59 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CorePluginFileHandler } from '@providers/plugin-file-delegate'; - -/** - * Handler to treat links to folder. - */ -@Injectable() -export class AddonModFolderPluginFileHandler implements CorePluginFileHandler { - name = 'AddonModFolderPluginFileHandler'; - component = 'mod_folder'; - - /** - * Return the RegExp to match the revision on pluginfile URLs. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return RegExp to match the revision on pluginfile URLs. - */ - getComponentRevisionRegExp(args: string[]): RegExp { - // Check filearea. - if (args[2] == 'content') { - // Component + Filearea + Revision - return new RegExp('/mod_folder/content/([0-9]+)/'); - } - } - - /** - * Should return the string to remove the revision on pluginfile url. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return String to remove the revision on pluginfile url. - */ - getComponentRevisionReplace(args: string[]): string { - // Component + Filearea + Revision - return '/mod_folder/content/0/'; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/folder/providers/prefetch-handler.ts b/src/addon/mod/folder/providers/prefetch-handler.ts deleted file mode 100644 index 9a5dfc307..000000000 --- a/src/addon/mod/folder/providers/prefetch-handler.ts +++ /dev/null @@ -1,101 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; -import { AddonModFolderProvider } from './folder'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch folders. - */ -@Injectable() -export class AddonModFolderPrefetchHandler extends CoreCourseResourcePrefetchHandlerBase { - name = 'AddonModFolder'; - modName = 'folder'; - component = AddonModFolderProvider.COMPONENT; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected folderProvider: AddonModFolderProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Download or prefetch the content. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param prefetch True to prefetch, false to download right away. - * @param dirPath Path of the directory where to store all the content files. This is to keep the files - * relative paths and make the package work in an iframe. Undefined to download the files - * in the filepool root folder. - * @return Promise resolved when all content is downloaded. Data returned is not reliable. - */ - downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise { - const promises = []; - - promises.push(super.downloadOrPrefetch(module, courseId, prefetch)); - - if (this.folderProvider.isGetFolderWSAvailable()) { - promises.push(this.folderProvider.getFolder(courseId, module.id)); - } - - return Promise.all(promises); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.folderProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const promises = []; - - promises.push(this.folderProvider.invalidateFolderData(courseId)); - promises.push(this.courseProvider.invalidateModule(module.id)); - - return Promise.all(promises); - } -} diff --git a/src/addon/mod/forum/components/components.module.ts b/src/addon/mod/forum/components/components.module.ts deleted file mode 100644 index e9cdcba14..000000000 --- a/src/addon/mod/forum/components/components.module.ts +++ /dev/null @@ -1,64 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { CoreRatingComponentsModule } from '@core/rating/components/components.module'; -import { CoreTagComponentsModule } from '@core/tag/components/components.module'; -import { AddonModForumIndexComponent } from './index/index'; -import { AddonModForumPostComponent } from './post/post'; -import { AddonForumDiscussionOptionsMenuComponent } from './discussion-options-menu/discussion-options-menu'; -import { AddonForumPostOptionsMenuComponent } from './post-options-menu/post-options-menu'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModForumIndexComponent, - AddonModForumPostComponent, - AddonForumDiscussionOptionsMenuComponent, - AddonForumPostOptionsMenuComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreCourseComponentsModule, - CoreRatingComponentsModule, - CoreTagComponentsModule, - CoreEditorComponentsModule, - ], - providers: [ - ], - exports: [ - AddonModForumIndexComponent, - AddonModForumPostComponent, - AddonForumDiscussionOptionsMenuComponent, - AddonForumPostOptionsMenuComponent - ], - entryComponents: [ - AddonModForumIndexComponent, - AddonForumDiscussionOptionsMenuComponent, - AddonForumPostOptionsMenuComponent - ] -}) -export class AddonModForumComponentsModule {} diff --git a/src/addon/mod/forum/components/discussion-options-menu/addon-forum-discussion-options-menu.html b/src/addon/mod/forum/components/discussion-options-menu/addon-forum-discussion-options-menu.html deleted file mode 100644 index ba53fa1cc..000000000 --- a/src/addon/mod/forum/components/discussion-options-menu/addon-forum-discussion-options-menu.html +++ /dev/null @@ -1,24 +0,0 @@ - - -

{{ 'addon.mod_forum.lockdiscussion' | translate }}

-
- - -

{{ 'addon.mod_forum.unlockdiscussion' | translate }}

-
- - -

{{ 'addon.mod_forum.pindiscussion' | translate }}

-
- - -

{{ 'addon.mod_forum.unpindiscussion' | translate }}

-
- - -

{{ 'addon.mod_forum.addtofavourites' | translate }}

-
- - -

{{ 'addon.mod_forum.removefromfavourites' | translate }}

-
\ No newline at end of file diff --git a/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts b/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts deleted file mode 100644 index 754ecff79..000000000 --- a/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts +++ /dev/null @@ -1,145 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { NavParams, ViewController } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonModForumProvider } from '../../providers/forum'; - -/** - * This component is meant to display a popover with the discussion options. - */ -@Component({ - selector: 'addon-forum-discussion-options-menu', - templateUrl: 'addon-forum-discussion-options-menu.html' -}) -export class AddonForumDiscussionOptionsMenuComponent implements OnInit { - discussion: any; // The discussion. - forumId: number; // The forum Id. - cmId: number; // The component module Id. - canPin = false; - - constructor(navParams: NavParams, - protected viewCtrl: ViewController, - protected forumProvider: AddonModForumProvider, - protected domUtils: CoreDomUtilsProvider, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider) { - this.discussion = navParams.get('discussion'); - this.forumId = navParams.get('forumId'); - this.cmId = navParams.get('cmId'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (this.forumProvider.isSetPinStateAvailableForSite()) { - // Use the canAddDiscussion WS to check if the user can pin discussions. - this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).then((response) => { - this.canPin = !!response.canpindiscussions; - }).catch(() => { - this.canPin = false; - }); - } else { - this.canPin = false; - } - } - - /** - * Lock or unlock the discussion. - * - * @param locked True to lock the discussion, false to unlock. - */ - setLockState(locked: boolean): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.forumProvider.setLockState(this.forumId, this.discussion.discussion, locked).then((response) => { - this.viewCtrl.dismiss({action: 'lock', value: locked}); - - const data = { - forumId: this.forumId, - discussionId: this.discussion.discussion, - cmId: this.cmId, - locked: response.locked - }; - this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - this.domUtils.showToast('addon.mod_forum.lockupdated', true); - }).catch((error) => { - this.domUtils.showErrorModal(error); - this.viewCtrl.dismiss(); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Pin or unpin the discussion. - * - * @param pinned True to pin the discussion, false to unpin it. - */ - setPinState(pinned: boolean): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.forumProvider.setPinState(this.discussion.discussion, pinned).then(() => { - this.viewCtrl.dismiss({action: 'pin', value: pinned}); - - const data = { - forumId: this.forumId, - discussionId: this.discussion.discussion, - cmId: this.cmId, - pinned: pinned - }; - this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - this.domUtils.showToast('addon.mod_forum.pinupdated', true); - }).catch((error) => { - this.domUtils.showErrorModal(error); - this.viewCtrl.dismiss(); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Star or unstar the discussion. - * - * @param starred True to star the discussion, false to unstar it. - */ - toggleFavouriteState(starred: boolean): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.forumProvider.toggleFavouriteState(this.discussion.discussion, starred).then(() => { - this.viewCtrl.dismiss({action: 'star', value: starred}); - - const data = { - forumId: this.forumId, - discussionId: this.discussion.discussion, - cmId: this.cmId, - starred: starred - }; - this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - this.domUtils.showToast('addon.mod_forum.favouriteupdated', true); - }).catch((error) => { - this.domUtils.showErrorModal(error); - this.viewCtrl.dismiss(); - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/mod/forum/components/index/addon-mod-forum-index.html b/src/addon/mod/forum/components/index/addon-mod-forum-index.html deleted file mode 100644 index 4f00d77b0..000000000 --- a/src/addon/mod/forum/components/index/addon-mod-forum-index.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - {{ 'core.hasdatatosync' | translate:{$a: moduleName} }} - - - - - {{ availabilityMessage }} - - - - - - -
- -
- - - -
-

- -

-
-
- -
-

{{discussion.userfullname}}

-

{{ discussion.groupname }}

-

{{ 'core.notsent' | translate }}

-
-
-
-
- - -
-

- - - -

- -
-
- -
-

{{discussion.userfullname}}

-

{{ discussion.groupname }}

-

{{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}}

-
-
- - - - {{ 'addon.mod_forum.lastpost' | translate }} - {{discussion.timemodified | coreTimeAgo}} - {{discussion.created | coreTimeAgo}} - - - - - {{ 'addon.mod_forum.numreplies' | translate:{numreplies: discussion.numreplies} }} - {{ discussion.numunread }} - - - -
-
- - -
-
- - - - -
-
diff --git a/src/addon/mod/forum/components/index/index.scss b/src/addon/mod/forum/components/index/index.scss deleted file mode 100644 index 911c5d809..000000000 --- a/src/addon/mod/forum/components/index/index.scss +++ /dev/null @@ -1,54 +0,0 @@ -$addon-forum-avatar-size: 28px; - -ion-app.app-root addon-mod-forum-index { - .addon-forum-star { - color: $core-star-color; - } - - .addon-mod-forum-discussion.item { - .label { - margin-top: 4px; - - h2 { - margin-top: 8px; - margin-bottom: 8px; - font-weight: bold; - ion-icon { - @include margin(0, 6px, 0, 0); - } - } - h3 { - font-size: 1.6rem; - } - } - - ion-avatar { - width: $addon-forum-avatar-size; - height: $addon-forum-avatar-size; - min-width: $addon-forum-avatar-size; - min-height: $addon-forum-avatar-size; - &[item-start] { - @include margin(0, 8px, 0, 0); - } - img { - width: $addon-forum-avatar-size; - height: $addon-forum-avatar-size; - } - } - - .addon-mod-forum-discussion-title, - .addon-mod-forum-discussion-info { - display: flex; - align-items: center; - } - .addon-mod-forum-discussion-title h2, - .addon-mod-forum-discussion-info .addon-mod-forum-discussion-author { - flex-grow: 1; - } - - .addon-mod-forum-discussion-more-info { - font-size: 1.4rem; - clear: both; - } - } -} diff --git a/src/addon/mod/forum/components/index/index.ts b/src/addon/mod/forum/components/index/index.ts deleted file mode 100644 index 9642aaddf..000000000 --- a/src/addon/mod/forum/components/index/index.ts +++ /dev/null @@ -1,662 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector, ViewChild } from '@angular/core'; -import { Content, ModalController, PopoverController } from 'ionic-angular'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreRatingProvider } from '@core/rating/providers/rating'; -import { CoreRatingOfflineProvider } from '@core/rating/providers/offline'; -import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; -import { AddonModForumProvider } from '../../providers/forum'; -import { AddonModForumHelperProvider } from '../../providers/helper'; -import { AddonModForumOfflineProvider } from '../../providers/offline'; -import { AddonModForumSyncProvider } from '../../providers/sync'; -import { AddonModForumPrefetchHandler } from '../../providers/prefetch-handler'; -import { AddonForumDiscussionOptionsMenuComponent } from '../discussion-options-menu/discussion-options-menu'; - -/** - * Component that displays a forum entry page. - */ -@Component({ - selector: 'addon-mod-forum-index', - templateUrl: 'addon-mod-forum-index.html', -}) -export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityComponent { - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - - component = AddonModForumProvider.COMPONENT; - moduleName = 'forum'; - - descriptionNote: string; - forum: any; - canLoadMore = false; - loadMoreError = false; - discussions = []; - offlineDiscussions = []; - selectedDiscussion = 0; // Disucssion ID or negative timecreated if it's an offline discussion. - canAddDiscussion = false; - addDiscussionText = this.translate.instant('addon.mod_forum.addanewdiscussion'); - availabilityMessage: string; - - sortingAvailable: boolean; - sortOrders = []; - selectedSortOrder = null; - sortOrderSelectorExpanded = false; - - protected syncEventName = AddonModForumSyncProvider.AUTO_SYNCED; - protected page = 0; - protected trackPosts = false; - protected usesGroups = false; - protected canPin = false; - protected syncManualObserver: any; // It will observe the sync manual event. - protected replyObserver: any; - protected newDiscObserver: any; - protected viewDiscObserver: any; - protected changeDiscObserver: any; - - hasOfflineRatings: boolean; - protected ratingOfflineObserver: any; - protected ratingSyncObserver: any; - - constructor(injector: Injector, - @Optional() protected content: Content, - protected modalCtrl: ModalController, - protected groupsProvider: CoreGroupsProvider, - protected userProvider: CoreUserProvider, - protected forumProvider: AddonModForumProvider, - protected forumHelper: AddonModForumHelperProvider, - protected forumOffline: AddonModForumOfflineProvider, - protected forumSync: AddonModForumSyncProvider, - protected prefetchDelegate: CoreCourseModulePrefetchDelegate, - protected prefetchHandler: AddonModForumPrefetchHandler, - protected ratingOffline: CoreRatingOfflineProvider, - protected popoverCtrl: PopoverController) { - super(injector); - - this.sortingAvailable = this.forumProvider.isDiscussionListSortingAvailable(); - this.sortOrders = this.forumProvider.getAvailableSortOrders(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - // Refresh data if this forum discussion is synchronized from discussions list. - this.syncManualObserver = this.eventsProvider.on(AddonModForumSyncProvider.MANUAL_SYNCED, (data) => { - this.autoSyncEventReceived(data); - }, this.siteId); - - // Listen for discussions added. When a discussion is added, we reload the data. - this.newDiscObserver = this.eventsProvider.on(AddonModForumProvider.NEW_DISCUSSION_EVENT, - this.eventReceived.bind(this, true)); - this.replyObserver = this.eventsProvider.on(AddonModForumProvider.REPLY_DISCUSSION_EVENT, - this.eventReceived.bind(this, false)); - this.changeDiscObserver = this.eventsProvider.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, (data) => { - if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module.id) { - this.forumProvider.invalidateDiscussionsList(this.forum.id).finally(() => { - if (data.discussionId) { - // Discussion changed, search it in the list of discussions. - const discussion = this.discussions.find((disc) => { - return data.discussionId == disc.discussion; - }); - - if (discussion) { - if (typeof data.locked != 'undefined') { - discussion.locked = data.locked; - } - if (typeof data.pinned != 'undefined') { - discussion.pinned = data.pinned; - } - if (typeof data.starred != 'undefined') { - discussion.starred = data.starred; - } - - this.showLoadingAndRefresh(false); - } - } - - if (typeof data.deleted != 'undefined' && data.deleted) { - if (data.post.parentid == 0 && this.splitviewCtrl && this.splitviewCtrl.isOn()) { - // Discussion deleted, clear details page. - this.splitviewCtrl.emptyDetails(); - } - this.showLoadingAndRefresh(false); - } - }); - } - }); - - // Select the current opened discussion. - this.viewDiscObserver = this.eventsProvider.on(AddonModForumProvider.VIEW_DISCUSSION_EVENT, (data) => { - if (this.forum && this.forum.id == data.forumId) { - this.selectedDiscussion = this.splitviewCtrl.isOn() ? data.discussion : 0; - - // Invalidate discussion list if it was not read. - const discussion = this.discussions.find((disc) => disc.discussion == data.discussion); - if (discussion && discussion.numunread > 0) { - this.forumProvider.invalidateDiscussionsList(this.forum.id); - } - } - }, this.sitesProvider.getCurrentSiteId()); - - // Listen for offline ratings saved and synced. - this.ratingOfflineObserver = this.eventsProvider.on(CoreRatingProvider.RATING_SAVED_EVENT, (data) => { - if (this.forum && data.component == 'mod_forum' && data.ratingArea == 'post' && - data.contextLevel == 'module' && data.instanceId == this.forum.cmid) { - this.hasOfflineRatings = true; - } - }); - this.ratingSyncObserver = this.eventsProvider.on(CoreRatingSyncProvider.SYNCED_EVENT, (data) => { - if (this.forum && data.component == 'mod_forum' && data.ratingArea == 'post' && - data.contextLevel == 'module' && data.instanceId == this.forum.cmid) { - this.ratingOffline.hasRatings('mod_forum', 'post', 'module', this.forum.cmid).then((hasRatings) => { - this.hasOfflineRatings = hasRatings; - }); - } - }); - - this.loadContent(false, true).then(() => { - if (!this.forum) { - return; - } - - if (this.splitviewCtrl.isOn()) { - // Load the first discussion. - if (this.offlineDiscussions.length > 0) { - this.openNewDiscussion(this.offlineDiscussions[0].timecreated); - } else if (this.discussions.length > 0) { - this.openDiscussion(this.discussions[0]); - } - } - - this.forumProvider.logView(this.forum.id, this.forum.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch((error) => { - // Ignore errors. - }); - }); - } - - /** - * Download the component contents. - * - * @param refresh Whether we're refreshing data. - * @param sync If the refresh needs syncing. - * @param showErrors Wether to show errors to the user or hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - this.loadMoreError = false; - - const promises = []; - - promises.push(this.forumProvider.getForum(this.courseId, this.module.id).then((forum) => { - this.forum = forum; - - this.description = forum.intro || this.description; - this.descriptionNote = this.translate.instant('addon.mod_forum.numdiscussions', {numdiscussions: forum.numdiscussions}); - if (typeof forum.istracked != 'undefined') { - this.trackPosts = forum.istracked; - } - this.availabilityMessage = this.forumHelper.getAvailabilityMessage(forum); - - this.dataRetrieved.emit(forum); - - switch (forum.type) { - case 'news': - case 'blog': - this.addDiscussionText = this.translate.instant('addon.mod_forum.addanewtopic'); - break; - case 'qanda': - this.addDiscussionText = this.translate.instant('addon.mod_forum.addanewquestion'); - break; - default: - this.addDiscussionText = this.translate.instant('addon.mod_forum.addanewdiscussion'); - } - - if (sync) { - // Try to synchronize the forum. - return this.syncActivity(showErrors).then((updated) => { - if (updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModForumSyncProvider.MANUAL_SYNCED, { - forumId: forum.id, - userId: this.sitesProvider.getCurrentSiteUserId(), - source: 'index', - }, this.sitesProvider.getCurrentSiteId()); - } - }); - } - }).then(() => { - const promises = []; - // Check if the activity uses groups. - promises.push(this.groupsProvider.getActivityGroupMode(this.forum.cmid).then((mode) => { - this.usesGroups = (mode === CoreGroupsProvider.SEPARATEGROUPS || mode === CoreGroupsProvider.VISIBLEGROUPS); - })); - promises.push(this.forumProvider.getAccessInformation(this.forum.id, {cmId: this.module.id}).then((accessInfo) => { - // Disallow adding discussions if cut-off date is reached and the user has not the capability to override it. - // Just in case the forum was fetched from WS when the cut-off date was not reached but it is now. - const cutoffDateReached = this.forumHelper.isCutoffDateReached(this.forum) && !accessInfo.cancanoverridecutoff; - this.canAddDiscussion = this.forum.cancreatediscussions && !cutoffDateReached; - })); - - if (this.forumProvider.isSetPinStateAvailableForSite()) { - // Use the canAddDiscussion WS to check if the user can pin discussions. - promises.push(this.forumProvider.canAddDiscussionToAll(this.forum.id, {cmId: this.module.id}).then((response) => { - this.canPin = !!response.canpindiscussions; - }).catch(() => { - this.canPin = false; - })); - } else { - this.canPin = false; - } - - return Promise.all(promises); - })); - - promises.push(this.fetchSortOrderPreference()); - - return Promise.all(promises).then(() => { - return Promise.all([ - this.fetchOfflineDiscussion(), - this.fetchDiscussions(refresh), - this.ratingOffline.hasRatings('mod_forum', 'post', 'module', this.forum.cmid).then((hasRatings) => { - this.hasOfflineRatings = hasRatings; - }) - ]); - }).catch((message) => { - if (!refresh) { - // Get forum failed, retry without using cache since it might be a new activity. - return this.refreshContent(sync); - } - - this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.errorgetforum', true); - - this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading. - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Convenience function to fetch offline discussions. - * - * @return Promise resolved when done. - */ - protected fetchOfflineDiscussion(): Promise { - return this.forumOffline.getNewDiscussions(this.forum.id).then((offlineDiscussions) => { - this.hasOffline = !!offlineDiscussions.length; - - if (this.hasOffline) { - let promise; - if (this.usesGroups) { - promise = this.forumProvider.formatDiscussionsGroups(this.forum.cmid, offlineDiscussions); - } else { - promise = Promise.resolve(offlineDiscussions); - } - - return promise.then((offlineDiscussions) => { - // Fill user data for Offline discussions (should be already cached). - const userPromises = []; - offlineDiscussions.forEach((discussion) => { - if (discussion.parent != 0 || this.forum.type != 'single') { - // Do not show author for first post and type single. - userPromises.push(this.userProvider.getProfile(discussion.userid, this.courseId, true) - .then((user) => { - discussion.userfullname = user.fullname; - discussion.userpictureurl = user.profileimageurl; - }).catch(() => { - // Ignore errors. - })); - } - }); - - return Promise.all(userPromises).then(() => { - // Sort discussion by time (newer first). - offlineDiscussions.sort((a, b) => b.timecreated - a.timecreated); - - this.offlineDiscussions = offlineDiscussions; - }); - }); - } else { - this.offlineDiscussions = []; - } - }); - } - - /** - * Convenience function to get forum discussions. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchDiscussions(refresh: boolean): Promise { - this.loadMoreError = false; - - if (refresh) { - this.page = 0; - } - - return this.forumProvider.getDiscussions(this.forum.id, { - cmId: this.forum.cmid, - sortOrder: this.selectedSortOrder.value, - page: this.page, - }).then((response) => { - let promise; - if (this.usesGroups) { - promise = this.forumProvider.formatDiscussionsGroups(this.forum.cmid, response.discussions); - } else { - promise = Promise.resolve(response.discussions); - } - - return promise.then((discussions) => { - if (this.forum.type == 'single') { - // Hide author for first post and type single. - for (const x in discussions) { - if (discussions[x].userfullname && discussions[x].parent == 0) { - discussions[x].userfullname = false; - break; - } - } - } - - if (typeof this.forum.istracked == 'undefined' && !this.trackPosts) { - // If any discussion has unread posts, the whole forum is being tracked. - for (const y in discussions) { - if (discussions[y].numunread > 0) { - this.trackPosts = true; - break; - } - } - } - - if (this.page == 0) { - this.discussions = discussions; - } else { - this.discussions = this.discussions.concat(discussions); - } - - this.canLoadMore = response.canLoadMore; - this.page++; - - // Check if there are replies for discussions stored in offline. - return this.forumOffline.hasForumReplies(this.forum.id).then((hasOffline) => { - const offlinePromises = []; - this.hasOffline = this.hasOffline || hasOffline; - - if (hasOffline) { - // Only update new fetched discussions. - discussions.forEach((discussion) => { - // Get offline discussions. - offlinePromises.push(this.forumOffline.getDiscussionReplies(discussion.discussion).then((replies) => { - discussion.numreplies = parseInt(discussion.numreplies, 10) + replies.length; - })); - }); - } - - return Promise.all(offlinePromises); - }); - }); - }); - } - - /** - * Convenience function to load more forum discussions. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Promise resolved when done. - */ - fetchMoreDiscussions(infiniteComplete?: any): Promise { - return this.fetchDiscussions(false).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.errorgetforum', true); - - this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading. - }).finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Convenience function to fetch the sort order preference. - * - * @return Promise resolved when done. - */ - protected fetchSortOrderPreference(): Promise { - let promise; - if (this.sortingAvailable) { - promise = this.userProvider.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER).then((value) => { - return value ? parseInt(value, 10) : null; - }); - } else { - // Use default. - promise = Promise.resolve(null); - } - - return promise.then((value) => { - this.selectedSortOrder = this.sortOrders.find((sortOrder) => sortOrder.value === value) || this.sortOrders[0]; - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.forumProvider.invalidateForumData(this.courseId)); - - if (this.forum) { - promises.push(this.forumProvider.invalidateDiscussionsList(this.forum.id)); - promises.push(this.groupsProvider.invalidateActivityGroupMode(this.forum.cmid)); - promises.push(this.forumProvider.invalidateAccessInformation(this.forum.id)); - } - - if (this.sortingAvailable) { - promises.push(this.userProvider.invalidateUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER)); - } - - return Promise.all(promises); - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.prefetchHandler.sync(this.module, this.courseId); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return Whether it succeed or not. - */ - protected hasSyncSucceed(result: any): boolean { - return result.updated; - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - return this.forum && syncEventData.source != 'index' && syncEventData.forumId == this.forum.id && - syncEventData.userId == this.sitesProvider.getCurrentSiteUserId(); - } - - /** - * Function called when we receive an event of new discussion or reply to discussion. - * - * @param isNewDiscussion Whether it's a new discussion event. - * @param data Event data. - */ - protected eventReceived(isNewDiscussion: boolean, data: any): void { - if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module.id) { - if (isNewDiscussion && this.splitviewCtrl.isOn()) { - // Discussion added, clear details page. - this.splitviewCtrl.emptyDetails(); - } - - this.showLoadingAndRefresh(false).finally(() => { - // If it's a new discussion in tablet mode, try to open it. - if (isNewDiscussion && this.splitviewCtrl.isOn()) { - - if (data.discussionIds) { - // Discussion sent to server, search it in the list of discussions. - const discussion = this.discussions.find((disc) => { - return data.discussionIds.indexOf(disc.discussion) >= 0; - }); - if (discussion) { - this.openDiscussion(discussion); - } - - } else if (data.discTimecreated) { - // It's an offline discussion, open it. - this.openNewDiscussion(data.discTimecreated); - } - } - }); - - // Check completion since it could be configured to complete once the user adds a new discussion or replies. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - } - - /** - * Opens a discussion. - * - * @param discussion Discussion object. - */ - openDiscussion(discussion: any): void { - const params = { - courseId: this.courseId, - cmId: this.module.id, - forumId: this.forum.id, - discussion: discussion, - trackPosts: this.trackPosts, - }; - this.splitviewCtrl.push('AddonModForumDiscussionPage', params); - } - - /** - * Opens the new discussion form. - * - * @param timeCreated Creation time of the offline discussion. - */ - openNewDiscussion(timeCreated: number = 0): void { - const params = { - courseId: this.courseId, - cmId: this.module.id, - forumId: this.forum.id, - timeCreated: timeCreated, - }; - this.splitviewCtrl.push('AddonModForumNewDiscussionPage', params); - - this.selectedDiscussion = 0; - } - - /** - * Display the sort order selector modal. - * - * @param event Event. - */ - showSortOrderSelector(event: MouseEvent): void { - if (!this.sortingAvailable) { - return; - } - - const params = { sortOrders: this.sortOrders, selected: this.selectedSortOrder.value }; - const modal = this.modalCtrl.create('AddonModForumSortOrderSelectorPage', params); - modal.onDidDismiss((sortOrder) => { - this.sortOrderSelectorExpanded = false; - - if (sortOrder && sortOrder.value != this.selectedSortOrder.value) { - this.selectedSortOrder = sortOrder; - this.page = 0; - this.userProvider.setUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER, sortOrder.value.toFixed(0)) - .then(() => { - this.showLoadingAndFetch(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error updating preference.'); - }); - } - }); - - modal.present({ev: event}); - this.sortOrderSelectorExpanded = true; - } - - /** - * Show the context menu. - * - * @param e Click Event. - */ - showOptionsMenu(e: Event, discussion: any): void { - e.preventDefault(); - e.stopPropagation(); - - const popover = this.popoverCtrl.create(AddonForumDiscussionOptionsMenuComponent, { - discussion: discussion, - forumId: this.forum.id, - cmId: this.module.id - }); - popover.onDidDismiss((data) => { - if (data && data.action) { - switch (data.action) { - case 'lock': - discussion.locked = data.value; - break; - case 'pin': - discussion.pinned = data.value; - break; - case 'star': - discussion.starred = data.value; - break; - default: - break; - } - } - }); - popover.present({ - ev: e - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - - this.syncManualObserver && this.syncManualObserver.off(); - this.newDiscObserver && this.newDiscObserver.off(); - this.replyObserver && this.replyObserver.off(); - this.viewDiscObserver && this.viewDiscObserver.off(); - this.changeDiscObserver && this.changeDiscObserver.off(); - this.ratingOfflineObserver && this.ratingOfflineObserver.off(); - this.ratingSyncObserver && this.ratingSyncObserver.off(); - } -} diff --git a/src/addon/mod/forum/components/post-options-menu/addon-forum-post-options-menu.html b/src/addon/mod/forum/components/post-options-menu/addon-forum-post-options-menu.html deleted file mode 100644 index d66529bc8..000000000 --- a/src/addon/mod/forum/components/post-options-menu/addon-forum-post-options-menu.html +++ /dev/null @@ -1,18 +0,0 @@ - - - -

{{ 'addon.mod_forum.edit' | translate }}

-
- - -

{{ 'addon.mod_forum.delete' | translate }}

-

{{ 'core.discard' | translate }}

-
- -

{{ 'core.numwords' | translate: {'$a': wordCount} }}

-
- - -

{{ 'core.openinbrowser' | translate }}

-
-
diff --git a/src/addon/mod/forum/components/post-options-menu/post-options-menu.scss b/src/addon/mod/forum/components/post-options-menu/post-options-menu.scss deleted file mode 100644 index f790d9f8d..000000000 --- a/src/addon/mod/forum/components/post-options-menu/post-options-menu.scss +++ /dev/null @@ -1,11 +0,0 @@ -addon-forum-post-options-menu { - core-loading:not(.core-loading-loaded) > .core-loading-container { - position: relative !important; - padding-top: 10px !important; - padding-bottom: 10px !important; - overflow: hidden; - } - core-loading > .core-loading-container .core-loading-message { - display: none; - } -} \ No newline at end of file diff --git a/src/addon/mod/forum/components/post-options-menu/post-options-menu.ts b/src/addon/mod/forum/components/post-options-menu/post-options-menu.ts deleted file mode 100644 index 8b9f2c69c..000000000 --- a/src/addon/mod/forum/components/post-options-menu/post-options-menu.ts +++ /dev/null @@ -1,140 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, NgZone } from '@angular/core'; -import { NavParams, ViewController } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSite } from '@classes/site'; -import { AddonModForumProvider } from '../../providers/forum'; -import { CoreApp } from '@providers/app'; -import { Network } from '@ionic-native/network'; - -/** - * This component is meant to display a popover with the post options. - */ -@Component({ - selector: 'addon-forum-post-options-menu', - templateUrl: 'addon-forum-post-options-menu.html' -}) -export class AddonForumPostOptionsMenuComponent implements OnInit { - post: any; // The post. - forumId: number; // The forum Id. - wordCount: number; // Number of words when available. - canEdit = false; - canDelete = false; - loaded = false; - url: string; - isOnline: boolean; - offlinePost: boolean; - - protected cmId: number; - protected onlineObserver: any; - - constructor(navParams: NavParams, - network: Network, - zone: NgZone, - protected viewCtrl: ViewController, - protected domUtils: CoreDomUtilsProvider, - protected forumProvider: AddonModForumProvider, - protected sitesProvider: CoreSitesProvider) { - this.post = navParams.get('post'); - this.forumId = navParams.get('forumId'); - this.cmId = navParams.get('cmId'); - - this.isOnline = CoreApp.instance.isOnline(); - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.isOnline = CoreApp.instance.isOnline(); - }); - }); - } - - /** - * Component being initialized. - */ - async ngOnInit(): Promise { - if (this.post.id > 0) { - const site: CoreSite = this.sitesProvider.getCurrentSite(); - this.url = site.createSiteUrl('/mod/forum/discuss.php', {d: this.post.discussionid}, 'p' + this.post.id); - this.offlinePost = false; - } else { - // Offline post, you can edit or discard the post. - this.loaded = true; - this.offlinePost = true; - - return; - } - - if (typeof this.post.capabilities.delete == 'undefined') { - if (this.forumId) { - try { - this.post = - await this.forumProvider.getDiscussionPost(this.forumId, this.post.discussionid, this.post.id, { - cmId: this.cmId, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - }); - } catch (error) { - this.domUtils.showErrorModalDefault(error, 'Error getting discussion post.'); - } - } else { - this.loaded = true; - - return; - } - } - - this.canDelete = this.post.capabilities.delete && this.forumProvider.isDeletePostAvailable(); - this.canEdit = this.post.capabilities.edit && this.forumProvider.isUpdatePostAvailable(); - this.wordCount = this.post.haswordcount && this.post.wordcount; - this.loaded = true; - } - - /** - * Close the popover. - */ - dismiss(): void { - this.viewCtrl.dismiss(); - } - - /** - * Delete a post. - */ - deletePost(): void { - if (!this.offlinePost) { - this.viewCtrl.dismiss({action: 'delete'}); - } else { - this.viewCtrl.dismiss({action: 'deleteoffline'}); - } - } - - /** - * Edit a post. - */ - editPost(): void { - if (!this.offlinePost) { - this.viewCtrl.dismiss({action: 'edit'}); - } else { - this.viewCtrl.dismiss({action: 'editoffline'}); - } - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.onlineObserver && this.onlineObserver.unsubscribe(); - } -} diff --git a/src/addon/mod/forum/components/post/addon-mod-forum-post.html b/src/addon/mod/forum/components/post/addon-mod-forum-post.html deleted file mode 100644 index 6a620c98e..000000000 --- a/src/addon/mod/forum/components/post/addon-mod-forum-post.html +++ /dev/null @@ -1,94 +0,0 @@ -
- - -
-

- - - -

- - - - -
- -
-
- -
- {{ 'addon.mod_forum.postisprivatereply' | translate }} -
- -
- -
-
-
- -
{{ 'core.tag.tags' | translate }}:
- -
- - - - - - -
- -
- - {{ 'addon.mod_forum.subject' | translate }} - - - - {{ 'addon.mod_forum.message' | translate }} - - - - {{ 'addon.mod_forum.privatereply' | translate }} - - - - - - - {{ 'addon.mod_forum.advanced' | translate }} - - - - - - - - - - - - - - - -
-
diff --git a/src/addon/mod/forum/components/post/post.scss b/src/addon/mod/forum/components/post/post.scss deleted file mode 100644 index 96f1b39dd..000000000 --- a/src/addon/mod/forum/components/post/post.scss +++ /dev/null @@ -1,72 +0,0 @@ -ion-app.app-root addon-mod-forum-post .addon-mod_forum-post { - background-color: $white; - border-bottom: 1px solid $list-md-border-color; - - @include darkmode() { - background-color: $core-dark-item-bg-color; - } - - .addon-forum-star { - color: $core-star-color; - } - - .card-header .item { - .label { - margin-top: 4px; - - h2 { - margin-top: 8px; - margin-bottom: 8px; - font-weight: bold; - ion-icon { - @include margin(0, 6px, 0, 0); - } - } - h3 { - font-size: 1.6rem; - } - } - - ion-avatar { - width: $addon-forum-avatar-size; - height: $addon-forum-avatar-size; - min-width: $addon-forum-avatar-size; - min-height: $addon-forum-avatar-size; - &[item-start] { - @include margin(0, 8px, 0, 0); - } - img { - width: $addon-forum-avatar-size; - height: $addon-forum-avatar-size; - } - } - - .addon-mod-forum-post-title, - .addon-mod-forum-post-info { - display: flex; - align-items: center; - } - - .addon-mod-forum-post-info { - margin-top: 8px; - } - - .addon-mod-forum-post-title + .addon-mod-forum-post-info { - margin-top: 0px; - } - - .addon-mod-forum-post-title h2, - .addon-mod-forum-post-info .addon-mod-forum-post-author { - flex-grow: 1; - } - } - - .item .item-inner { - border-bottom: 0; - } - - .addon-mod-forum-post-more-info div { - font-size: 1.4rem; - } -} - diff --git a/src/addon/mod/forum/components/post/post.ts b/src/addon/mod/forum/components/post/post.ts deleted file mode 100644 index f9402ece9..000000000 --- a/src/addon/mod/forum/components/post/post.ts +++ /dev/null @@ -1,523 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { - Component, Input, Output, Optional, EventEmitter, OnInit, OnDestroy, ViewChild, ElementRef, OnChanges, SimpleChange -} from '@angular/core'; -import { FormControl } from '@angular/forms'; -import { Content, PopoverController, ModalController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonModForumProvider } from '../../providers/forum'; -import { AddonModForumHelperProvider } from '../../providers/helper'; -import { AddonModForumOfflineProvider } from '../../providers/offline'; -import { AddonModForumSyncProvider } from '../../providers/sync'; -import { CoreRatingInfo } from '@core/rating/providers/rating'; -import { CoreTagProvider } from '@core/tag/providers/tag'; -import { AddonForumPostOptionsMenuComponent } from '../post-options-menu/post-options-menu'; - -/** - * Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.). - */ -@Component({ - selector: 'addon-mod-forum-post', - templateUrl: 'addon-mod-forum-post.html', -}) -export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges { - @Input() post: any; // Post. - @Input() courseId: number; // Post's course ID. - @Input() discussionId: number; // Post's' discussion ID. - @Input() discussion?: any; // Post's' discussion, only for starting posts. - @Input() component: string; // Component this post belong to. - @Input() componentId: number; // Component ID. - @Input() replyData: any; // Object with the new post data. Usually shared between posts. - @Input() originalData: any; // Object with the original post data. Usually shared between posts. - @Input() trackPosts: boolean; // True if post is being tracked. - @Input() forum: any; // The forum the post belongs to. Required for attachments and offline posts. - @Input() accessInfo: any; // Forum access information. - @Input() parentSubject?: string; // Subject of parent post. - @Input() ratingInfo?: CoreRatingInfo; // Rating info item. - @Input() leavingPage?: boolean; // Whether the page that contains this post is being left and will be destroyed. - @Output() onPostChange: EventEmitter; // Event emitted when a reply is posted or modified. - - @ViewChild('replyFormEl') formElement: ElementRef; - - messageControl = new FormControl(); - - uniqueId: string; - defaultReplySubject: string; - advanced = false; // Display all form fields. - tagsEnabled: boolean; - displaySubject = true; - optionsMenuEnabled = false; - - protected syncId: string; - - constructor( - private uploaderProvider: CoreFileUploaderProvider, - private syncProvider: CoreSyncProvider, - private domUtils: CoreDomUtilsProvider, - private textUtils: CoreTextUtilsProvider, - private translate: TranslateService, - private forumProvider: AddonModForumProvider, - private forumHelper: AddonModForumHelperProvider, - private forumOffline: AddonModForumOfflineProvider, - private forumSync: AddonModForumSyncProvider, - private tagProvider: CoreTagProvider, - @Optional() private content: Content, - protected popoverCtrl: PopoverController, - protected modalCtrl: ModalController, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider) { - this.onPostChange = new EventEmitter(); - this.tagsEnabled = this.tagProvider.areTagsAvailableInSite(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.uniqueId = this.post.id > 0 ? 'reply' + this.post.id : 'edit' + this.post.parentid; - - const reTranslated = this.translate.instant('addon.mod_forum.re'); - this.displaySubject = !this.parentSubject || - (this.post.subject != this.parentSubject && this.post.subject != `Re: ${this.parentSubject}` && - this.post.subject != `${reTranslated} ${this.parentSubject}`); - this.defaultReplySubject = this.post.replysubject || ((this.post.subject.startsWith('Re: ') || - this.post.subject.startsWith(reTranslated)) ? this.post.subject : `${reTranslated} ${this.post.subject}`); - - this.optionsMenuEnabled = this.post.id < 0 || (this.forumProvider.isGetDiscussionPostAvailable() && - (this.forumProvider.isDeletePostAvailable() || this.forumProvider.isUpdatePostAvailable())); - } - - /** - * Detect changes on input properties. - */ - ngOnChanges(changes: {[name: string]: SimpleChange}): void { - if (changes.leavingPage && this.leavingPage) { - // Download all courses is enabled now, initialize it. - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } - } - - /** - * Deletes an online post. - */ - deletePost(): void { - this.domUtils.showDeleteConfirm('addon.mod_forum.deletesure').then(() => { - const modal = this.domUtils.showModalLoading('core.deleting', true); - - this.forumProvider.deletePost(this.post.id).then((response) => { - - const data = { - forumId: this.forum.id, - discussionId: this.discussionId, - cmId: this.forum.cmid, - deleted: response.status, - post: this.post - }; - - this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, - this.sitesProvider.getCurrentSiteId()); - - this.domUtils.showToast('addon.mod_forum.deletedpost', true); - }).catch((error) => { - this.domUtils.showErrorModal(error); - }).finally(() => { - modal.dismiss(); - }); - }).catch((error) => { - // Do nothing. - }); - } - - /** - * Set data to new reply post, clearing temporary files and updating original data. - * - * @param replyingTo Id of post beeing replied. - * @param isEditing True it's an offline reply beeing edited, false otherwise. - * @param subject Subject of the reply. - * @param message Message of the reply. - * @param isPrivate True if it's private reply. - * @param files Reply attachments. - */ - protected setReplyFormData(replyingTo?: number, isEditing?: boolean, subject?: string, message?: string, files?: any[], - isPrivate?: boolean): void { - // Delete the local files from the tmp folder if any. - this.uploaderProvider.clearTmpFiles(this.replyData.files); - - this.replyData.replyingTo = replyingTo || 0; - this.replyData.isEditing = !!isEditing; - this.replyData.subject = subject || this.defaultReplySubject || ''; - this.replyData.message = message || null; - this.replyData.files = files || []; - this.replyData.isprivatereply = !!isPrivate; - - // Update rich text editor. - this.messageControl.setValue(this.replyData.message); - - // Update original data. - this.originalData.subject = this.replyData.subject; - this.originalData.message = this.replyData.message; - this.originalData.files = this.replyData.files.slice(); - this.originalData.isprivatereply = this.replyData.isprivatereply; - - // Show advanced fields if any of them has not the default value. - this.advanced = this.replyData.files.length > 0; - } - - /** - * Show the context menu. - * - * @param e Click Event. - */ - showOptionsMenu(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - const popover = this.popoverCtrl.create(AddonForumPostOptionsMenuComponent, { - post: this.post, - forumId: this.forum.id, - cmId: this.forum.cmid, - }); - popover.onDidDismiss((data) => { - if (data && data.action) { - switch (data.action) { - case 'edit': - this.editPost(); - break; - case 'editoffline': - this.editOfflineReply(); - break; - case 'delete': - this.deletePost(); - break; - case 'deleteoffline': - this.discardOfflineReply(); - break; - default: - break; - } - } - }); - popover.present({ - ev: e - }); - } - - /** - * Shows a form modal to edit an online post. - */ - editPost(): void { - const modal = this.modalCtrl.create('AddonModForumEditPostPage', { - post: this.post, - component: this.component, - componentId: this.componentId, - forum: this.forum - }); - - modal.present(); - modal.onDidDismiss((data) => { - if (typeof data != 'undefined') { - // Add some HTML to the message if needed. - const message = this.textUtils.formatHtmlLines(data.message); - const files = data.files || []; - const sendingModal = this.domUtils.showModalLoading('core.sending', true); - let promise; - - // Upload attachments first if any. - if (files.length) { - promise = this.forumHelper.uploadOrStoreReplyFiles(this.forum.id, this.post.id, files, false); - } else { - promise = Promise.resolve(); - } - - promise.then((attach) => { - const options: any = {}; - - if (attach) { - options.attachmentsid = attach; - } - - // Try to send it to server. - return this.forumProvider.updatePost(this.post.id, data.subject, message, options); - }).then((sent) => { - if (sent && this.forum.id) { - // Data sent to server, delete stored files (if any). - this.forumHelper.deleteReplyStoredFiles(this.forum.id, this.post.id); - - this.onPostChange.emit(); - this.post.subject = data.subject; - this.post.message = message; - this.post.attachments = data.files; - } - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.couldnotupdate', true); - }).finally(() => { - sendingModal.dismiss(); - }); - } - }); - } - - /** - * Set this post as being replied to. - */ - showReplyForm(): void { - if (this.replyData.isEditing) { - // User is editing a post, data needs to be resetted. Ask confirm if there is unsaved data. - this.confirmDiscard().then(() => { - this.setReplyFormData(this.post.id); - - if (this.content) { - setTimeout(() => { - this.content.resize(); - this.domUtils.scrollToElementBySelector(this.content, '#addon-forum-reply-edit-form-' + this.uniqueId); - }); - } - }).catch(() => { - // Cancelled. - }); - - return; - } else if (!this.replyData.replyingTo) { - // User isn't replying, it's a brand new reply. Initialize the data. - this.setReplyFormData(this.post.id); - } else { - // The post being replied has changed but the data will be kept. - this.replyData.replyingTo = this.post.id; - - if (this.replyData.subject == this.originalData.subject) { - // Update subject only if it hadn't been modified - this.replyData.subject = this.defaultReplySubject; - this.originalData.subject = this.defaultReplySubject; - } - - this.messageControl.setValue(this.replyData.message); - } - - if (this.content) { - setTimeout(() => { - this.content.resize(); - this.domUtils.scrollToElementBySelector(this.content, '#addon-forum-reply-edit-form-' + this.uniqueId); - }); - } - - } - - /** - * Set this post as being edited to. - */ - editOfflineReply(): void { - // Ask confirm if there is unsaved data. - this.confirmDiscard().then(() => { - this.syncId = this.forumSync.getDiscussionSyncId(this.discussionId); - this.syncProvider.blockOperation(AddonModForumProvider.COMPONENT, this.syncId); - - this.setReplyFormData(this.post.parentid, true, this.post.subject, this.post.message, this.post.attachments, - this.post.isprivatereply); - }).catch(() => { - // Cancelled. - }); - } - - /** - * Message changed. - * - * @param text The new text. - */ - onMessageChange(text: string): void { - this.replyData.message = text; - } - - /** - * Reply to this post. - */ - reply(): void { - if (!this.replyData.subject) { - this.domUtils.showErrorModal('addon.mod_forum.erroremptysubject', true); - - return; - } - - if (!this.replyData.message) { - this.domUtils.showErrorModal('addon.mod_forum.erroremptymessage', true); - - return; - } - - let saveOffline = false; - let message = this.replyData.message; - const subject = this.replyData.subject; - const replyingTo = this.replyData.replyingTo; - const files = this.replyData.files || []; - const options: any = {}; - const modal = this.domUtils.showModalLoading('core.sending', true); - let promise; - - // Add some HTML to the message if needed. - message = this.textUtils.formatHtmlLines(message); - - // Set private option if checked. - if (this.replyData.isprivatereply) { - options.private = true; - } - - // Upload attachments first if any. - if (files.length) { - promise = this.forumHelper.uploadOrStoreReplyFiles(this.forum.id, replyingTo, files, false).catch((error) => { - // Cannot upload them in online, save them in offline. - if (!this.forum.id) { - // Cannot store them in offline without the forum ID. Reject. - return Promise.reject(error); - } - - saveOffline = true; - - return this.forumHelper.uploadOrStoreReplyFiles(this.forum.id, replyingTo, files, true); - }); - } else { - promise = Promise.resolve(); - } - - promise.then((attach) => { - if (attach) { - options.attachmentsid = attach; - } - - if (saveOffline) { - // Save post in offline. - return this.forumOffline.replyPost(replyingTo, this.discussionId, this.forum.id, this.forum.name, - this.courseId, subject, message, options).then(() => { - // Return false since it wasn't sent to server. - return false; - }); - } else { - // Try to send it to server. - // Don't allow offline if there are attachments since they were uploaded fine. - return this.forumProvider.replyPost(replyingTo, this.discussionId, this.forum.id, this.forum.name, - this.courseId, subject, message, options, undefined, !files.length); - } - }).then((sent) => { - if (sent && this.forum.id) { - // Data sent to server, delete stored files (if any). - this.forumHelper.deleteReplyStoredFiles(this.forum.id, replyingTo); - } - - // Reset data. - this.setReplyFormData(); - - this.onPostChange.emit(); - - this.domUtils.triggerFormSubmittedEvent(this.formElement, sent, this.sitesProvider.getCurrentSiteId()); - - if (this.syncId) { - this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.couldnotadd', true); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Cancel reply. - */ - cancel(): void { - this.confirmDiscard().then(() => { - // Reset data. - this.setReplyFormData(); - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - - if (this.syncId) { - this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } - }).catch(() => { - // Cancelled. - }); - } - - /** - * Discard offline reply. - */ - discardOfflineReply(): void { - this.domUtils.showDeleteConfirm().then(() => { - const promises = []; - - promises.push(this.forumOffline.deleteReply(this.post.parentid)); - if (this.forum.id) { - promises.push(this.forumHelper.deleteReplyStoredFiles(this.forum.id, this.post.parentid).catch(() => { - // Ignore errors, maybe there are no files. - })); - } - - return Promise.all(promises).finally(() => { - // Reset data. - this.setReplyFormData(); - - this.onPostChange.emit(); - - if (this.syncId) { - this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } - }); - }).catch(() => { - // Cancelled. - }); - } - - /** - * Function called when rating is updated online. - */ - ratingUpdated(): void { - this.forumProvider.invalidateDiscussionPosts(this.discussionId, this.forum.id); - } - - /** - * Show or hide advanced form fields. - */ - toggleAdvanced(): void { - this.advanced = !this.advanced; - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - if (this.syncId) { - this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } - } - - /** - * Confirm discard changes if any. - * - * @return Promise resolved if the user confirms or data was not changed and rejected otherwise. - */ - protected confirmDiscard(): Promise { - if (this.forumHelper.hasPostDataChanged(this.replyData, this.originalData)) { - // Show confirmation if some data has been modified. - return this.domUtils.showConfirm(this.translate.instant('core.confirmloss')); - } else { - return Promise.resolve(); - } - } -} diff --git a/src/addon/mod/forum/forum.module.ts b/src/addon/mod/forum/forum.module.ts deleted file mode 100644 index a41da53ae..000000000 --- a/src/addon/mod/forum/forum.module.ts +++ /dev/null @@ -1,87 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; -import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate'; -import { AddonModForumProvider } from './providers/forum'; -import { AddonModForumOfflineProvider } from './providers/offline'; -import { AddonModForumHelperProvider } from './providers/helper'; -import { AddonModForumSyncProvider } from './providers/sync'; -import { AddonModForumModuleHandler } from './providers/module-handler'; -import { AddonModForumPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModForumSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModForumIndexLinkHandler } from './providers/index-link-handler'; -import { AddonModForumDiscussionLinkHandler } from './providers/discussion-link-handler'; -import { AddonModForumListLinkHandler } from './providers/list-link-handler'; -import { AddonModForumPostLinkHandler } from './providers/post-link-handler'; -import { AddonModForumPushClickHandler } from './providers/push-click-handler'; -import { AddonModForumTagAreaHandler } from './providers/tag-area-handler'; -import { AddonModForumComponentsModule } from './components/components.module'; - -// List of providers (without handlers). -export const ADDON_MOD_FORUM_PROVIDERS: any[] = [ - AddonModForumProvider, - AddonModForumOfflineProvider, - AddonModForumHelperProvider, - AddonModForumSyncProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModForumComponentsModule, - ], - providers: [ - AddonModForumProvider, - AddonModForumOfflineProvider, - AddonModForumHelperProvider, - AddonModForumSyncProvider, - AddonModForumModuleHandler, - AddonModForumPrefetchHandler, - AddonModForumSyncCronHandler, - AddonModForumIndexLinkHandler, - AddonModForumListLinkHandler, - AddonModForumPostLinkHandler, - AddonModForumDiscussionLinkHandler, - AddonModForumPushClickHandler, - AddonModForumTagAreaHandler - ] -}) -export class AddonModForumModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModForumModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModForumPrefetchHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModForumSyncCronHandler, linksDelegate: CoreContentLinksDelegate, - indexHandler: AddonModForumIndexLinkHandler, discussionHandler: AddonModForumDiscussionLinkHandler, - listLinkHandler: AddonModForumListLinkHandler, - pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModForumPushClickHandler, - postLinkHandler: AddonModForumPostLinkHandler, tagAreaDelegate: CoreTagAreaDelegate, - tagAreaHandler: AddonModForumTagAreaHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - cronDelegate.register(syncHandler); - linksDelegate.registerHandler(indexHandler); - linksDelegate.registerHandler(discussionHandler); - linksDelegate.registerHandler(listLinkHandler); - linksDelegate.registerHandler(postLinkHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - tagAreaDelegate.registerHandler(tagAreaHandler); - } -} diff --git a/src/addon/mod/forum/lang/en.json b/src/addon/mod/forum/lang/en.json deleted file mode 100644 index ba17e36b8..000000000 --- a/src/addon/mod/forum/lang/en.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "addanewdiscussion": "Add a new discussion topic", - "addanewquestion": "Add a new question", - "addanewtopic": "Add a new topic", - "addtofavourites": "Star this discussion", - "advanced": "Advanced", - "cannotadddiscussion": "Adding discussions to this forum requires group membership.", - "cannotadddiscussionall": "You do not have permission to add a new discussion topic for all participants.", - "cannotcreatediscussion": "Could not create new discussion", - "couldnotadd": "Could not add your post due to an unknown error", - "couldnotupdate": "Could not update your post due to an unknown error", - "cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.", - "delete": "Delete", - "deletedpost": "The post has been deleted", - "deletesure": "Are you sure you want to delete this post?", - "discussion": "Discussion", - "discussionlistsortbycreatedasc": "Sort by creation date in ascending order", - "discussionlistsortbycreateddesc": "Sort by creation date in descending order", - "discussionlistsortbylastpostasc": "Sort by last post creation date in ascending order", - "discussionlistsortbylastpostdesc": "Sort by last post creation date in descending order", - "discussionlistsortbyrepliesasc": "Sort by number of replies in ascending order", - "discussionlistsortbyrepliesdesc": "Sort by number of replies in descending order", - "discussionlocked": "This discussion has been locked so you can no longer reply to it.", - "discussionpinned": "Pinned", - "discussionsubscription": "Discussion subscription", - "edit": "Edit", - "erroremptymessage": "Post message cannot be empty", - "erroremptysubject": "Post subject cannot be empty.", - "errorgetforum": "Error getting forum data.", - "errorgetgroups": "Error getting group settings.", - "errorposttoallgroups": "Could not create new discussion in all groups.", - "favouriteupdated": "Your star option has been updated.", - "forumnodiscussionsyet": "There are no discussions yet in this forum.", - "group": "Group", - "lastpost": "Last post", - "lockdiscussion": "Lock this discussion", - "lockupdated": "The lock option has been updated.", - "message": "Message", - "modeflatnewestfirst": "Display replies flat, with newest first", - "modeflatoldestfirst": "Display replies flat, with oldest first", - "modenested": "Display replies in nested form", - "modulenameplural": "Forums", - "numdiscussions": "{{numdiscussions}} discussions", - "numreplies": "{{numreplies}} replies", - "pindiscussion": "Pin this discussion", - "pinupdated": "The pin option has been updated.", - "postisprivatereply": "This is a private reply. It is not visible to other participants.", - "posttoforum": "Post to forum", - "posttomygroups": "Post a copy to all groups", - "privatereply": "Reply privately", - "re": "Re:", - "refreshdiscussions": "Refresh discussions", - "refreshposts": "Refresh posts", - "removefromfavourites": "Unstar this discussion", - "reply": "Reply", - "replyplaceholder": "Write your reply...", - "subject": "Subject", - "tagarea_forum_posts": "Forum posts", - "thisforumhasduedate": "The due date for posting to this forum is {{$a}}.", - "thisforumisdue": "The due date for posting to this forum was {{$a}}.", - "unlockdiscussion": "Unlock this discussion", - "unpindiscussion": "Unpin this discussion", - "unread": "Unread", - "unreadpostsnumber": "{{$a}} unread posts", - "yourreply": "Your reply" -} \ No newline at end of file diff --git a/src/addon/mod/forum/pages/discussion/discussion.html b/src/addon/mod/forum/pages/discussion/discussion.html deleted file mode 100644 index bc403cdd9..000000000 --- a/src/addon/mod/forum/pages/discussion/discussion.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ 'core.hasdatatosync' | translate:{$a: discussionStr} }} - - - - - {{ availabilityMessage }} - - - - {{ 'addon.mod_forum.discussionlocked' | translate }} - - -
- -
- - - - - - - - - - - - - - - - - - -
- - - -
-
-
-
diff --git a/src/addon/mod/forum/pages/discussion/discussion.module.ts b/src/addon/mod/forum/pages/discussion/discussion.module.ts deleted file mode 100644 index 90b84af3d..000000000 --- a/src/addon/mod/forum/pages/discussion/discussion.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModForumComponentsModule } from '../../components/components.module'; -import { AddonModForumDiscussionPage } from './discussion'; - -@NgModule({ - declarations: [ - AddonModForumDiscussionPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - AddonModForumComponentsModule, - IonicPageModule.forChild(AddonModForumDiscussionPage), - TranslateModule.forChild() - ], -}) -export class AddonModForumDiscussionPageModule {} diff --git a/src/addon/mod/forum/pages/discussion/discussion.scss b/src/addon/mod/forum/pages/discussion/discussion.scss deleted file mode 100644 index fcc1213fd..000000000 --- a/src/addon/mod/forum/pages/discussion/discussion.scss +++ /dev/null @@ -1,12 +0,0 @@ -ion-app.app-root page-addon-mod-forum-discussion { - .highlight .card-header .item { - background-color: $gray-lighter; - @include darkmode() { - background-color: $gray-dark; - } - } - - .addon-forum-reply-button .label { - margin: 0; - } -} \ No newline at end of file diff --git a/src/addon/mod/forum/pages/discussion/discussion.ts b/src/addon/mod/forum/pages/discussion/discussion.ts deleted file mode 100644 index 14d83c207..000000000 --- a/src/addon/mod/forum/pages/discussion/discussion.ts +++ /dev/null @@ -1,751 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, OnDestroy, ViewChild, NgZone } from '@angular/core'; -import { IonicPage, NavParams, Content, NavController } from 'ionic-angular'; -import { Network } from '@ionic-native/network'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreRatingProvider, CoreRatingInfo } from '@core/rating/providers/rating'; -import { CoreRatingOfflineProvider } from '@core/rating/providers/offline'; -import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; -import { AddonModForumProvider } from '../../providers/forum'; -import { AddonModForumOfflineProvider } from '../../providers/offline'; -import { AddonModForumHelperProvider } from '../../providers/helper'; -import { AddonModForumSyncProvider } from '../../providers/sync'; - -type SortType = 'flat-newest' | 'flat-oldest' | 'nested'; - -type Post = any & { children?: Post[]; }; - -/** - * Page that displays a forum discussion. - */ -@IonicPage({ segment: 'addon-mod-forum-discussion' }) -@Component({ - selector: 'page-addon-mod-forum-discussion', - templateUrl: 'discussion.html', -}) -export class AddonModForumDiscussionPage implements OnDestroy { - @ViewChild(Content) content: Content; - - courseId: number; - discussionId: number; - forum: any = {}; - accessInfo: any = {}; - discussion: any; - startingPost: any; - posts: any[]; - discussionLoaded = false; - postSubjects: { [id: string]: string }; - isOnline: boolean; - isSplitViewOn: boolean; - postHasOffline: boolean; - sort: SortType = 'nested'; - trackPosts: boolean; - replyData = { - replyingTo: 0, - isEditing: false, - subject: '', - message: null, // Null means empty or just white space. - files: [], - isprivatereply: false, - }; - originalData = { - subject: null, // Null means original data is not set. - message: null, // Null means empty or just white space. - files: [], - isprivatereply: false, - }; - refreshIcon = 'spinner'; - syncIcon = 'spinner'; - discussionStr = ''; - component = AddonModForumProvider.COMPONENT; - cmId: number; - canPin = false; - availabilityMessage: string; - leavingPage = false; - - protected forumId: number; - protected postId: number; - protected parent: number; - protected onlineObserver: any; - protected syncObserver: any; - protected syncManualObserver: any; - - ratingInfo?: CoreRatingInfo; - hasOfflineRatings: boolean; - protected ratingOfflineObserver: any; - protected ratingSyncObserver: any; - protected changeDiscObserver: any; - - constructor(navParams: NavParams, - network: Network, - zone: NgZone, - protected appProvider: CoreAppProvider, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider, - protected domUtils: CoreDomUtilsProvider, - protected utils: CoreUtilsProvider, - protected translate: TranslateService, - protected uploaderProvider: CoreFileUploaderProvider, - protected forumProvider: AddonModForumProvider, - protected forumOffline: AddonModForumOfflineProvider, - protected forumHelper: AddonModForumHelperProvider, - protected forumSync: AddonModForumSyncProvider, - protected ratingOffline: CoreRatingOfflineProvider, - protected userProvider: CoreUserProvider, - @Optional() protected svComponent: CoreSplitViewComponent, - protected navCtrl: NavController) { - this.courseId = navParams.get('courseId'); - this.cmId = navParams.get('cmId'); - this.forumId = navParams.get('forumId'); - this.discussion = navParams.get('discussion'); - this.discussionId = this.discussion ? this.discussion.discussion : navParams.get('discussionId'); - this.trackPosts = navParams.get('trackPosts'); - this.postId = navParams.get('postId'); - this.parent = navParams.get('parent'); - - this.isOnline = this.appProvider.isOnline(); - this.onlineObserver = network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - this.isOnline = this.appProvider.isOnline(); - }); - }); - this.isSplitViewOn = this.svComponent && this.svComponent.isOn(); - - this.discussionStr = translate.instant('addon.mod_forum.discussion'); - } - - /** - * View loaded. - */ - async ionViewDidLoad(): Promise { - if (this.parent) { - this.sort = 'nested'; // Force nested order. - } else { - this.sort = await this.getUserSort(); - } - - await this.fetchPosts(true, false, true); - - const scrollTo = this.postId || this.parent; - if (scrollTo) { - // Scroll to the post. - setTimeout(() => { - this.domUtils.scrollToElementBySelector(this.content, '#addon-mod_forum-post-' + scrollTo); - }); - } - } - - /** - * Get sort type configured by the current user. - * - * @return Promise resolved with the sort type. - */ - protected async getUserSort(): Promise { - try { - const value = await this.sitesProvider.getCurrentSite().getLocalSiteConfig('AddonModForumDiscussionSort'); - - return value; - } catch (error) { - try { - const value = await this.userProvider.getUserPreference('forum_displaymode'); - - switch (Number(value)) { - case 1: - return 'flat-oldest'; - case -1: - return 'flat-newest'; - case 3: - return 'nested'; - case 2: // Threaded not implemented. - default: - // Not set, use default sort. - // @TODO add fallback to $CFG->forum_displaymode. - } - } catch (error) { - // Ignore errors. - } - } - - return 'flat-oldest'; - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - if (this.syncObserver) { - // Already setup. - return; - } - - // Refresh data if this discussion is synchronized automatically. - this.syncObserver = this.eventsProvider.on(AddonModForumSyncProvider.AUTO_SYNCED, (data) => { - if (data.forumId == this.forumId && this.discussionId == data.discussionId - && data.userId == this.sitesProvider.getCurrentSiteUserId()) { - // Refresh the data. - this.discussionLoaded = false; - this.refreshPosts(); - } - }, this.sitesProvider.getCurrentSiteId()); - - // Refresh data if this forum discussion is synchronized from discussions list. - this.syncManualObserver = this.eventsProvider.on(AddonModForumSyncProvider.MANUAL_SYNCED, (data) => { - if (data.source != 'discussion' && data.forumId == this.forumId && - data.userId == this.sitesProvider.getCurrentSiteUserId()) { - // Refresh the data. - this.discussionLoaded = false; - this.refreshPosts(); - } - }, this.sitesProvider.getCurrentSiteId()); - - // Trigger view event, to highlight the current opened discussion in the split view. - this.eventsProvider.trigger(AddonModForumProvider.VIEW_DISCUSSION_EVENT, { - forumId: this.forumId, - discussion: this.discussionId - }, this.sitesProvider.getCurrentSiteId()); - - // Listen for offline ratings saved and synced. - this.ratingOfflineObserver = this.eventsProvider.on(CoreRatingProvider.RATING_SAVED_EVENT, (data) => { - if (data.component == 'mod_forum' && data.ratingArea == 'post' && data.contextLevel == 'module' && - data.instanceId == this.cmId && data.itemSetId == this.discussionId) { - this.hasOfflineRatings = true; - } - }); - this.ratingSyncObserver = this.eventsProvider.on(CoreRatingSyncProvider.SYNCED_EVENT, (data) => { - if (data.component == 'mod_forum' && data.ratingArea == 'post' && data.contextLevel == 'module' && - data.instanceId == this.cmId && data.itemSetId == this.discussionId) { - this.hasOfflineRatings = false; - } - }); - - this.changeDiscObserver = this.eventsProvider.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, (data) => { - if ((this.forumId && this.forumId === data.forumId) || data.cmId === this.cmId) { - this.forumProvider.invalidateDiscussionsList(this.forumId).finally(() => { - if (typeof data.locked != 'undefined') { - this.discussion.locked = data.locked; - } - if (typeof data.pinned != 'undefined') { - this.discussion.pinned = data.pinned; - } - if (typeof data.starred != 'undefined') { - this.discussion.starred = data.starred; - } - - if (typeof data.deleted != 'undefined' && data.deleted) { - if (!data.post.parentid) { - if (this.svComponent && this.svComponent.isOn()) { - this.svComponent.emptyDetails(); - } else { - this.navCtrl.pop(); - } - } else { - this.discussionLoaded = false; - this.refreshPosts(); - } - } - }); - } - }); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - - if (this.forumHelper.hasPostDataChanged(this.replyData, this.originalData)) { - // Show confirmation if some data has been modified. - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - // Delete the local files from the tmp folder. - this.uploaderProvider.clearTmpFiles(this.replyData.files); - - this.leavingPage = true; - } - - /** - * Convenience function to get the forum. - * - * @return Promise resolved with the forum. - */ - protected fetchForum(): Promise { - if (this.courseId && this.cmId) { - return this.forumProvider.getForum(this.courseId, this.cmId); - } else if (this.courseId && this.forumId) { - return this.forumProvider.getForumById(this.courseId, this.forumId); - } else { - // Cannot get the forum. - return Promise.reject(null); - } - } - - /** - * Convenience function to get the posts. - * - * @param sync Whether to try to synchronize the discussion. - * @param showErrors Whether to show errors in a modal. - * @param forceMarkAsRead Whether to mark all posts as read. - * @return Promise resolved when done. - */ - protected fetchPosts(sync?: boolean, showErrors?: boolean, forceMarkAsRead?: boolean): Promise { - let syncPromise; - if (sync) { - // Try to synchronize the forum. - syncPromise = this.syncDiscussion(showErrors).catch(() => { - // Ignore errors. - }); - } else { - syncPromise = Promise.resolve(); - } - - let onlinePosts = []; - const offlineReplies = []; - let hasUnreadPosts = false; - let ratingInfo; - - return syncPromise.then(() => { - return this.forumProvider.getDiscussionPosts(this.discussionId, {cmId: this.cmId}).then((response) => { - onlinePosts = response.posts; - ratingInfo = response.ratinginfo; - this.courseId = response.courseid || this.courseId; - this.forumId = response.forumid || this.forumId; - }).then(() => { - // Check if there are responses stored in offline. - return this.forumOffline.getDiscussionReplies(this.discussionId).then((replies) => { - this.postHasOffline = !!replies.length; - const convertPromises = []; - - // Index posts to allow quick access. Also check unread field. - const posts = {}; - onlinePosts.forEach((post) => { - posts[post.id] = post; - hasUnreadPosts = hasUnreadPosts || !!post.unread; - }); - - replies.forEach((offlineReply) => { - // If we don't have forumId and courseId, get it from the post. - if (!this.forumId) { - this.forumId = offlineReply.forumid; - } - if (!this.courseId) { - this.courseId = offlineReply.courseid; - } - - convertPromises.push(this.forumHelper.convertOfflineReplyToOnline(offlineReply).then((reply) => { - offlineReplies.push(reply); - - // Disable reply of the parent. Reply in offline to the same post is not allowed, edit instead. - posts[reply.parentid].capabilities.reply = false; - })); - }); - - return Promise.all(convertPromises).then(() => { - // Convert back to array. - onlinePosts = this.utils.objectToArray(posts); - }); - }); - }); - }).then(() => { - let posts = offlineReplies.concat(onlinePosts); - - this.startingPost = this.forumProvider.extractStartingPost(posts); - - // If sort type is nested, normal sorting is disabled and nested posts will be displayed. - if (this.sort == 'nested') { - // Sort first by creation date to make format tree work. - this.forumProvider.sortDiscussionPosts(posts, 'ASC'); - - const rootId = this.startingPost ? this.startingPost.id : (this.discussion ? this.discussion.id : 0); - posts = this.utils.formatTree(posts, 'parentid', 'id', rootId); - } else { - // Set default reply subject. - const direction = this.sort == 'flat-newest' ? 'DESC' : 'ASC'; - this.forumProvider.sortDiscussionPosts(posts, direction); - } - - // Now try to get the forum. - return this.fetchForum().then((forum) => { - // "forum.istracked" is more reliable than "trackPosts". - if (typeof forum.istracked != 'undefined') { - this.trackPosts = forum.istracked; - } - - this.forumId = forum.id; - this.cmId = forum.cmid; - this.courseId = forum.course; - this.forum = forum; - this.availabilityMessage = this.forumHelper.getAvailabilityMessage(forum); - - const promises = []; - - promises.push(this.forumProvider.getAccessInformation(this.forumId, {cmId: this.cmId}).then((accessInfo) => { - this.accessInfo = accessInfo; - - // Disallow replying if cut-off date is reached and the user has not the capability to override it. - // Just in case the posts were fetched from WS when the cut-off date was not reached but it is now. - if (this.forumHelper.isCutoffDateReached(forum) && !accessInfo.cancanoverridecutoff) { - posts.forEach((post) => { - post.capabilities.reply = false; - }); - } - })); - - // The discussion object was not passed as parameter and there is no starting post. Should not happen. - if (!this.discussion) { - promises.push(this.loadDiscussion(this.forumId, this.cmId, this.discussionId)); - } - - return Promise.all(promises); - }).catch(() => { - // Ignore errors. - }).then(() => { - if (!this.discussion && !this.startingPost) { - // The discussion object was not passed as parameter and there is no starting post. Should not happen. - return Promise.reject('Invalid forum discussion.'); - } - - if (this.startingPost.author && this.forum.type == 'single') { - // Hide author and groups for first post and type single. - this.startingPost.author.fullname = null; - this.startingPost.author.groups = null; - - } - - this.posts = posts; - this.ratingInfo = ratingInfo; - - this.postSubjects = this.getAllPosts().reduce((postSubjects, post) => { - postSubjects[post.id] = post.subject; - - return postSubjects; - }, { [this.startingPost.id]: this.startingPost.subject }); - }); - }).then(() => { - if (this.forumProvider.isSetPinStateAvailableForSite()) { - // Use the canAddDiscussion WS to check if the user can pin discussions. - return this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).then((response) => { - this.canPin = !!response.canpindiscussions; - }).catch(() => { - this.canPin = false; - }); - } else { - this.canPin = false; - } - }).then(() => { - return this.ratingOffline.hasRatings('mod_forum', 'post', 'module', this.cmId, this.discussionId).then((hasRatings) => { - this.hasOfflineRatings = hasRatings; - }); - }).catch((message) => { - this.domUtils.showErrorModal(message); - }).finally(() => { - this.discussionLoaded = true; - this.refreshIcon = 'refresh'; - this.syncIcon = 'sync'; - - if (forceMarkAsRead || (hasUnreadPosts && this.trackPosts)) { - // // Add log in Moodle and mark unread posts as readed. - this.forumProvider.logDiscussionView(this.discussionId, this.forumId || -1, this.forum.name).catch(() => { - // Ignore errors. - }).finally(() => { - // Trigger mark read posts. - this.eventsProvider.trigger(AddonModForumProvider.MARK_READ_EVENT, { - courseId: this.courseId, - moduleId: this.cmId - }, this.sitesProvider.getCurrentSiteId()); - }); - } - }); - } - - /** - * Convenience function to load discussion. - * - * @param forumId Forum ID. - * @param cmId Forum cmid. - * @param discussionId Discussion ID. - * @return Promise resolved when done. - */ - protected loadDiscussion(forumId: number, cmId: number, discussionId: number): Promise { - // Fetch the discussion if not passed as parameter. - if (!this.discussion && forumId) { - return this.forumHelper.getDiscussionById(forumId, cmId, discussionId).then((discussion) => { - this.discussion = discussion; - this.discussionId = this.discussion.discussion; - }).catch(() => { - // Ignore errors. - }); - } - - return Promise.resolve(); - } - - /** - * Tries to synchronize the posts discussion. - * - * @param showErrors Whether to show errors in a modal. - * @return Promise resolved when done. - */ - protected syncDiscussion(showErrors: boolean): Promise { - const promises = []; - - promises.push(this.forumSync.syncDiscussionReplies(this.discussionId).then((result) => { - if (result.warnings && result.warnings.length) { - this.domUtils.showErrorModal(result.warnings[0]); - } - - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModForumSyncProvider.MANUAL_SYNCED, { - forumId: this.forumId, - userId: this.sitesProvider.getCurrentSiteUserId(), - source: 'discussion' - }, this.sitesProvider.getCurrentSiteId()); - } - - return result.updated; - })); - - promises.push(this.forumSync.syncRatings(this.cmId, this.discussionId).then((result) => { - if (result.warnings && result.warnings.length) { - this.domUtils.showErrorModal(result.warnings[0]); - } - })); - - return Promise.all(promises).catch((error) => { - if (showErrors) { - this.domUtils.showErrorModalDefault(error, 'core.errorsync', true); - } - - return Promise.reject(null); - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @param done Function to call when done. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - doRefresh(refresher?: any, done?: () => void, showErrors: boolean = false): Promise { - if (this.discussionLoaded) { - return this.refreshPosts(true, showErrors).finally(() => { - refresher && refresher.complete(); - done && done(); - }); - } - - return Promise.resolve(); - } - - /** - * Refresh posts. - * - * @param sync Whether to try to synchronize the discussion. - * @param showErrors Whether to show errors in a modal. - * @return Promise resolved when done. - */ - refreshPosts(sync?: boolean, showErrors?: boolean): Promise { - this.domUtils.scrollToTop(this.content); - this.refreshIcon = 'spinner'; - this.syncIcon = 'spinner'; - - const promises = [ - this.forumProvider.invalidateForumData(this.courseId), - this.forumProvider.invalidateDiscussionPosts(this.discussionId, this.forumId), - this.forumProvider.invalidateAccessInformation(this.forumId), - this.forumProvider.invalidateCanAddDiscussion(this.forumId) - ]; - - return this.utils.allPromises(promises).catch(() => { - // Ignore errors. - }).then(() => { - return this.fetchPosts(sync, showErrors); - }); - } - - /** - * Function to change posts sorting - * - * @param type Sort type. - * @return Promised resolved when done. - */ - changeSort(type: SortType): Promise { - this.discussionLoaded = false; - this.sort = type; - this.sitesProvider.getCurrentSite().setLocalSiteConfig('AddonModForumDiscussionSort', this.sort); - this.domUtils.scrollToTop(this.content); - - return this.fetchPosts(); - } - - /** - * Lock or unlock the discussion. - * - * @param locked True to lock the discussion, false to unlock. - */ - setLockState(locked: boolean): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.forumProvider.setLockState(this.forumId, this.discussionId, locked).then((response) => { - this.discussion.locked = response.locked; - - const data = { - forumId: this.forumId, - discussionId: this.discussionId, - cmId: this.cmId, - locked: this.discussion.locked - }; - this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - this.domUtils.showToast('addon.mod_forum.lockupdated', true); - }).catch((error) => { - this.domUtils.showErrorModal(error); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Pin or unpin the discussion. - * - * @param pinned True to pin the discussion, false to unpin it. - */ - setPinState(pinned: boolean): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.forumProvider.setPinState(this.discussionId, pinned).then(() => { - this.discussion.pinned = pinned; - - const data = { - forumId: this.forumId, - discussionId: this.discussionId, - cmId: this.cmId, - pinned: this.discussion.pinned - }; - this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - this.domUtils.showToast('addon.mod_forum.pinupdated', true); - }).catch((error) => { - this.domUtils.showErrorModal(error); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Star or unstar the discussion. - * - * @param starred True to star the discussion, false to unstar it. - */ - toggleFavouriteState(starred: boolean): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.forumProvider.toggleFavouriteState(this.discussionId, starred).then(() => { - this.discussion.starred = starred; - - const data = { - forumId: this.forumId, - discussionId: this.discussionId, - cmId: this.cmId, - starred: this.discussion.starred - }; - this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - this.domUtils.showToast('addon.mod_forum.favouriteupdated', true); - }).catch((error) => { - this.domUtils.showErrorModal(error); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * New post added. - */ - postListChanged(): void { - // Trigger an event to notify a new reply. - const data = { - forumId: this.forumId, - discussionId: this.discussionId, - cmId: this.cmId - }; - this.eventsProvider.trigger(AddonModForumProvider.REPLY_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - this.discussionLoaded = false; - this.refreshPosts().finally(() => { - this.discussionLoaded = true; - }); - } - - /** - * Runs when the page is about to leave and no longer be the active page. - */ - ionViewWillLeave(): void { - this.syncObserver && this.syncObserver.off(); - this.syncManualObserver && this.syncManualObserver.off(); - this.ratingOfflineObserver && this.ratingOfflineObserver.off(); - this.ratingSyncObserver && this.ratingSyncObserver.off(); - this.changeDiscObserver && this.changeDiscObserver.off(); - delete this.syncObserver; - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.onlineObserver && this.onlineObserver.unsubscribe(); - } - - /** - * Get all the posts contained in the discussion. - * - * @return Array containing all the posts of the discussion. - */ - protected getAllPosts(): Post[] { - return [].concat(...this.posts.map(this.flattenPostHierarchy.bind(this))); - } - - /** - * Flatten a post's hierarchy into an array. - * - * @param parent Parent post. - * @return Array containing all the posts within the hierarchy (including the parent). - */ - protected flattenPostHierarchy(parent: Post): Post[] { - const posts = [parent]; - const children = parent.children || []; - - for (const child of children) { - posts.push(...this.flattenPostHierarchy(child)); - } - - return posts; - } -} diff --git a/src/addon/mod/forum/pages/edit-post/addon-mod-forum-edit-post.html b/src/addon/mod/forum/pages/edit-post/addon-mod-forum-edit-post.html deleted file mode 100644 index 4ffd4c50e..000000000 --- a/src/addon/mod/forum/pages/edit-post/addon-mod-forum-edit-post.html +++ /dev/null @@ -1,40 +0,0 @@ - - - {{ 'addon.mod_forum.yourreply' | translate }} - - - - - - -
- - {{ 'addon.mod_forum.subject' | translate }} - - - - {{ 'addon.mod_forum.message' | translate }} - - - - - - {{ 'addon.mod_forum.advanced' | translate }} - - - - - - - - - - - - - - -
-
\ No newline at end of file diff --git a/src/addon/mod/forum/pages/edit-post/edit-post.module.ts b/src/addon/mod/forum/pages/edit-post/edit-post.module.ts deleted file mode 100644 index 89cf9ad4d..000000000 --- a/src/addon/mod/forum/pages/edit-post/edit-post.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModForumComponentsModule } from '../../components/components.module'; -import { AddonModForumEditPostPage } from './edit-post'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModForumEditPostPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - AddonModForumComponentsModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonModForumEditPostPage), - TranslateModule.forChild() - ], -}) -export class AddonModForumEditPostPageModule {} diff --git a/src/addon/mod/forum/pages/edit-post/edit-post.ts b/src/addon/mod/forum/pages/edit-post/edit-post.ts deleted file mode 100644 index 152d15077..000000000 --- a/src/addon/mod/forum/pages/edit-post/edit-post.ts +++ /dev/null @@ -1,151 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, ElementRef } from '@angular/core'; -import { FormControl } from '@angular/forms'; -import { IonicPage, ViewController, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModForumProvider } from '../../providers/forum'; -import { AddonModForumHelperProvider } from '../../providers/helper'; - -/** - * Page that displays a form to edit discussion post. - */ -@IonicPage({ segment: 'addon-mod-edit-post' }) -@Component({ - selector: 'addon-mod-forum-edit-post', - templateUrl: 'addon-mod-forum-edit-post.html', -}) -export class AddonModForumEditPostPage { - @ViewChild('editFormEl') formElement: ElementRef; - - component: string; // Component this post belong to. - componentId: number; // Component ID. - forum: any; // The forum the post belongs to. Required for attachments and offline posts. - - messageControl = new FormControl(); - advanced = false; // Display all form fields. - replyData: any = {}; - originalData: any = {}; // Object with the original post data. Usually shared between posts. - - protected forceLeave = false; // To allow leaving the page without checking for changes. - - constructor( - params: NavParams, - protected forumProvider: AddonModForumProvider, - protected viewCtrl: ViewController, - protected domUtils: CoreDomUtilsProvider, - protected uploaderProvider: CoreFileUploaderProvider, - protected forumHelper: AddonModForumHelperProvider, - protected translate: TranslateService, - protected sitesProvider: CoreSitesProvider) { - - const post = params.get('post'); - this.component = params.get('component'); - this.componentId = params.get('componentId'); - this.forum = params.get('forum'); - - this.replyData.id = post.id; - this.replyData.subject = post.subject; - this.replyData.message = post.message; - this.replyData.files = post.attachments || []; - - // Delete the local files from the tmp folder if any. - this.uploaderProvider.clearTmpFiles(this.replyData.files); - - // Update rich text editor. - this.messageControl.setValue(this.replyData.message); - - // Update original data. - this.originalData.subject = this.replyData.subject; - this.originalData.message = this.replyData.message; - this.originalData.files = this.replyData.files.slice(); - - // Show advanced fields if any of them has not the default value. - this.advanced = this.replyData.files.length > 0; - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - ionViewCanLeave(): boolean | Promise { - if (this.forceLeave) { - return true; - } - - let promise: any; - - if (this.forumHelper.hasPostDataChanged(this.replyData, this.originalData)) { - // Show confirmation if some data has been modified. - promise = this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - // Delete the local files from the tmp folder. - this.uploaderProvider.clearTmpFiles(this.replyData.files); - }); - } - - /** - * Message changed. - * - * @param text The new text. - */ - onMessageChange(text: string): void { - this.replyData.message = text; - } - - /** - * Close modal. - * - * @param data Data to return to the page. - */ - closeModal(data: any, ): void { - if (data) { - this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId()); - } else { - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } - - this.viewCtrl.dismiss(data); - } - - /** - * Reply to this post. - * - * @param e Click event. - */ - reply(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - // Close the modal, sending the input data. - this.forceLeave = true; - this.closeModal(this.replyData); - } - - /** - * Show or hide advanced form fields. - */ - toggleAdvanced(): void { - this.advanced = !this.advanced; - } -} diff --git a/src/addon/mod/forum/pages/index/index.html b/src/addon/mod/forum/pages/index/index.html deleted file mode 100644 index fc621ed20..000000000 --- a/src/addon/mod/forum/pages/index/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/src/addon/mod/forum/pages/index/index.module.ts b/src/addon/mod/forum/pages/index/index.module.ts deleted file mode 100644 index b59f992bf..000000000 --- a/src/addon/mod/forum/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModForumComponentsModule } from '../../components/components.module'; -import { AddonModForumIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModForumIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModForumComponentsModule, - IonicPageModule.forChild(AddonModForumIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModForumIndexPageModule {} diff --git a/src/addon/mod/forum/pages/index/index.ts b/src/addon/mod/forum/pages/index/index.ts deleted file mode 100644 index be2971ccd..000000000 --- a/src/addon/mod/forum/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModForumIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a forum. - */ -@IonicPage({ segment: 'addon-mod-forum-index' }) -@Component({ - selector: 'page-addon-mod-forum-index', - templateUrl: 'index.html', -}) -export class AddonModForumIndexPage { - @ViewChild(AddonModForumIndexComponent) forumComponent: AddonModForumIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the forum instance. - * - * @param forum Forum instance. - */ - updateData(forum: any): void { - this.title = forum.name || this.title; - } -} diff --git a/src/addon/mod/forum/pages/new-discussion/new-discussion.html b/src/addon/mod/forum/pages/new-discussion/new-discussion.html deleted file mode 100644 index dfed2ccbe..000000000 --- a/src/addon/mod/forum/pages/new-discussion/new-discussion.html +++ /dev/null @@ -1,62 +0,0 @@ - - - {{ 'addon.mod_forum.addanewdiscussion' | translate }} - - - - - - - - - - - -
- - {{ 'addon.mod_forum.subject' | translate }} - - - - {{ 'addon.mod_forum.message' | translate }} - - - - - - {{ 'addon.mod_forum.advanced' | translate }} - - - - {{ 'addon.mod_forum.posttomygroups' | translate }} - - - - {{ 'addon.mod_forum.group' | translate }} - - {{ group.name }} - - - - {{ 'addon.mod_forum.discussionsubscription' | translate }} - - - - {{ 'addon.mod_forum.discussionpinned' | translate }} - - - - - - - - - - - - - - -
-
-
diff --git a/src/addon/mod/forum/pages/new-discussion/new-discussion.module.ts b/src/addon/mod/forum/pages/new-discussion/new-discussion.module.ts deleted file mode 100644 index 0bb9e4038..000000000 --- a/src/addon/mod/forum/pages/new-discussion/new-discussion.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModForumNewDiscussionPage } from './new-discussion'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModForumNewDiscussionPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonModForumNewDiscussionPage), - TranslateModule.forChild() - ], -}) -export class AddonModForumNewDiscussionPageModule {} diff --git a/src/addon/mod/forum/pages/new-discussion/new-discussion.ts b/src/addon/mod/forum/pages/new-discussion/new-discussion.ts deleted file mode 100644 index bde109ba0..000000000 --- a/src/addon/mod/forum/pages/new-discussion/new-discussion.ts +++ /dev/null @@ -1,569 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, Optional, ViewChild, ElementRef } from '@angular/core'; -import { FormControl } from '@angular/forms'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreEditorRichTextEditorComponent } from '@core/editor/components/rich-text-editor/rich-text-editor.ts'; -import { AddonModForumProvider } from '../../providers/forum'; -import { AddonModForumOfflineProvider } from '../../providers/offline'; -import { AddonModForumHelperProvider } from '../../providers/helper'; -import { AddonModForumSyncProvider } from '../../providers/sync'; - -/** - * Page that displays the new discussion form. - */ -@IonicPage({ segment: 'addon-mod-forum-new-discussion' }) -@Component({ - selector: 'page-addon-mod-forum-new-discussion', - templateUrl: 'new-discussion.html', -}) -export class AddonModForumNewDiscussionPage implements OnDestroy { - - @ViewChild('newDiscFormEl') formElement: ElementRef; - @ViewChild(CoreEditorRichTextEditorComponent) messageEditor: CoreEditorRichTextEditorComponent; - - component = AddonModForumProvider.COMPONENT; - messageControl = new FormControl(); - groupsLoaded = false; - showGroups = false; - hasOffline = false; - canCreateAttachments = true; // Assume we can by default. - canPin = false; - forum: any; - showForm = false; - groups = []; - groupIds = []; - newDiscussion = { - subject: '', - message: null, // Null means empty or just white space. - postToAllGroups: false, - groupId: 0, - subscribe: true, - pin: false, - files: [] - }; - advanced = false; // Display all form fields. - accessInfo: any = {}; - - protected courseId: number; - protected cmId: number; - protected forumId: number; - protected timeCreated: number; - protected syncId: string; - protected syncObserver: any; - protected isDestroyed = false; - protected originalData: any; - protected forceLeave = false; - - constructor(navParams: NavParams, - private navCtrl: NavController, - private translate: TranslateService, - private domUtils: CoreDomUtilsProvider, - private eventsProvider: CoreEventsProvider, - private groupsProvider: CoreGroupsProvider, - private sitesProvider: CoreSitesProvider, - private syncProvider: CoreSyncProvider, - private uploaderProvider: CoreFileUploaderProvider, - private textUtils: CoreTextUtilsProvider, - private utils: CoreUtilsProvider, - private forumProvider: AddonModForumProvider, - private forumOffline: AddonModForumOfflineProvider, - private forumSync: AddonModForumSyncProvider, - private forumHelper: AddonModForumHelperProvider, - @Optional() private svComponent: CoreSplitViewComponent) { - this.courseId = navParams.get('courseId'); - this.cmId = navParams.get('cmId'); - this.forumId = navParams.get('forumId'); - this.timeCreated = navParams.get('timeCreated'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchDiscussionData().finally(() => { - this.groupsLoaded = true; - }); - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - if (this.syncObserver) { - // Already setup. - return; - } - - // Refresh data if this discussion is synchronized automatically. - this.syncObserver = this.eventsProvider.on(AddonModForumSyncProvider.AUTO_SYNCED, (data) => { - if (data.forumId == this.forumId && data.userId == this.sitesProvider.getCurrentSiteUserId()) { - this.domUtils.showAlertTranslated('core.notice', 'core.contenteditingsynced'); - this.returnToDiscussions(); - } - }, this.sitesProvider.getCurrentSiteId()); - - // Trigger view event, to highlight the current opened discussion in the split view. - this.eventsProvider.trigger(AddonModForumProvider.VIEW_DISCUSSION_EVENT, { - forumId: this.forumId, - discussion: -this.timeCreated - }, this.sitesProvider.getCurrentSiteId()); - } - - /** - * Fetch if forum uses groups and the groups it uses. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchDiscussionData(refresh?: boolean): Promise { - return this.groupsProvider.getActivityGroupMode(this.cmId).then((mode) => { - const promises = []; - - if (mode === CoreGroupsProvider.SEPARATEGROUPS || mode === CoreGroupsProvider.VISIBLEGROUPS) { - promises.push(this.groupsProvider.getActivityAllowedGroups(this.cmId).then((result) => { - let promise; - if (mode === CoreGroupsProvider.VISIBLEGROUPS) { - // We need to check which of the returned groups the user can post to. - promise = this.validateVisibleGroups(result.groups); - } else { - // WS already filters groups, no need to do it ourselves. Add "All participants" if needed. - promise = this.addAllParticipantsOption(result.groups, true); - } - - return promise.then((forumGroups) => { - if (forumGroups.length > 0) { - this.groups = forumGroups; - this.groupIds = forumGroups.map((group) => group.id).filter((id) => id > 0); - // Do not override group id. - this.newDiscussion.groupId = this.newDiscussion.groupId || forumGroups[0].id; - this.showGroups = true; - if (this.groupIds.length <= 1) { - this.newDiscussion.postToAllGroups = false; - } - } else { - const message = mode === CoreGroupsProvider.SEPARATEGROUPS ? - 'addon.mod_forum.cannotadddiscussionall' : 'addon.mod_forum.cannotadddiscussion'; - - return Promise.reject(this.translate.instant(message)); - } - }); - })); - } else { - this.showGroups = false; - this.newDiscussion.postToAllGroups = false; - - // Use the canAddDiscussion WS to check if the user can add attachments and pin discussions. - promises.push(this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).then((response) => { - this.canPin = !!response.canpindiscussions; - this.canCreateAttachments = !!response.cancreateattachment; - }).catch(() => { - // Ignore errors, use default values. - })); - } - - // Get forum. - promises.push(this.forumProvider.getForum(this.courseId, this.cmId).then((forum) => { - this.forum = forum; - })); - - // Get access information. - promises.push(this.forumProvider.getAccessInformation(this.forumId, {cmId: this.cmId}).then((accessInfo) => { - this.accessInfo = accessInfo; - })); - - return Promise.all(promises); - }).then(() => { - // If editing a discussion, get offline data. - if (this.timeCreated && !refresh) { - this.syncId = this.forumSync.getForumSyncId(this.forumId); - - return this.forumSync.waitForSync(this.syncId).then(() => { - // Do not block if the scope is already destroyed. - if (!this.isDestroyed) { - this.syncProvider.blockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } - - return this.forumOffline.getNewDiscussion(this.forumId, this.timeCreated).then((discussion) => { - this.hasOffline = true; - discussion.options = discussion.options || {}; - if (discussion.groupid == AddonModForumProvider.ALL_GROUPS) { - this.newDiscussion.groupId = this.groups[0].id; - this.newDiscussion.postToAllGroups = true; - } else { - this.newDiscussion.groupId = discussion.groupid; - this.newDiscussion.postToAllGroups = false; - } - this.newDiscussion.subject = discussion.subject; - this.newDiscussion.message = discussion.message; - this.newDiscussion.subscribe = discussion.options.discussionsubscribe; - this.newDiscussion.pin = discussion.options.discussionpinned; - this.messageControl.setValue(discussion.message); - - // Treat offline attachments if any. - let promise; - if (discussion.options.attachmentsid && discussion.options.attachmentsid.offline) { - promise = this.forumHelper.getNewDiscussionStoredFiles(this.forumId, this.timeCreated).then((files) => { - this.newDiscussion.files = files; - }); - } - - return Promise.resolve(promise).then(() => { - // Show advanced fields by default if any of them has not the default value. - if (!this.newDiscussion.subscribe || this.newDiscussion.pin || this.newDiscussion.files.length || - this.groups.length > 0 && this.newDiscussion.groupId != this.groups[0].id || - this.newDiscussion.postToAllGroups) { - this.advanced = true; - } - }); - }); - }); - } - }).then(() => { - if (!this.originalData) { - // Initialize original data. - this.originalData = { - subject: this.newDiscussion.subject, - message: this.newDiscussion.message, - files: this.newDiscussion.files.slice(), - }; - } - this.showForm = true; - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.errorgetgroups', true); - this.showForm = false; - }); - } - - /** - * Validate which of the groups returned by getActivityAllowedGroups in visible groups should be shown to post to. - * - * @param forumGroups Forum groups. - * @return Promise resolved with the list of groups. - */ - protected validateVisibleGroups(forumGroups: any[]): Promise { - // We first check if the user can post to all the groups. - return this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).catch(() => { - // The call failed, let's assume he can't. - return { - status: false, - canpindiscussions: false, - cancreateattachment: true - }; - }).then((response) => { - this.canPin = !!response.canpindiscussions; - this.canCreateAttachments = !!response.cancreateattachment; - - if (response.status) { - // The user can post to all groups, add the "All participants" option and return them all. - return this.addAllParticipantsOption(forumGroups, false); - } else { - // The user can't post to all groups, let's check which groups he can post to. - const promises = []; - const filtered = []; - - forumGroups.forEach((group) => { - promises.push(this.forumProvider.canAddDiscussion(this.forumId, group.id, {cmId: this.cmId}).catch(() => { - /* The call failed, let's return true so the group is shown. If the user can't post to - it an error will be shown when he tries to add the discussion. */ - return { - status: true - }; - }).then((response) => { - if (response.status) { - filtered.push(group); - } - })); - }); - - return Promise.all(promises).then(() => { - return filtered; - }); - } - }); - } - - /** - * Filter forum groups, returning only those that are inside user groups. - * - * @param forumGroups Forum groups. - * @param userGroups User groups. - * @return Filtered groups. - */ - protected filterGroups(forumGroups: any[], userGroups: any[]): any[] { - const filtered = []; - const userGroupsIds = userGroups.map((g) => g.id); - - forumGroups.forEach((fg) => { - if (userGroupsIds.indexOf(fg.id) > -1) { - filtered.push(fg); - } - }); - - return filtered; - } - - /** - * Add the "All participants" option to a list of groups if the user can add a discussion to all participants. - * - * @param groups Groups. - * @param check True to check if the user can add a discussion to all participants. - * @return Promise resolved with the list of groups. - */ - protected addAllParticipantsOption(groups: any[], check: boolean): Promise { - if (!this.forumProvider.isAllParticipantsFixed()) { - // All participants has a bug, don't add it. - return Promise.resolve(groups); - } - - let promise; - - if (check) { - // We need to check if the user can add a discussion to all participants. - promise = this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).then((response) => { - this.canPin = !!response.canpindiscussions; - this.canCreateAttachments = !!response.cancreateattachment; - - return response.status; - }).catch(() => { - // The call failed, let's assume he can't. - return false; - }); - } else { - // No need to check, assume the user can. - promise = Promise.resolve(true); - } - - return promise.then((canAdd) => { - if (canAdd) { - groups.unshift({ - courseid: this.courseId, - id: AddonModForumProvider.ALL_PARTICIPANTS, - name: this.translate.instant('core.allparticipants') - }); - } - - return groups; - }); - } - - /** - * Pull to refresh. - * - * @param refresher Refresher. - */ - refreshGroups(refresher: any): void { - const promises = [ - this.groupsProvider.invalidateActivityGroupMode(this.cmId), - this.groupsProvider.invalidateActivityAllowedGroups(this.cmId), - this.forumProvider.invalidateCanAddDiscussion(this.forumId), - ]; - - Promise.all(promises).finally(() => { - this.fetchDiscussionData(true).finally(() => { - refresher.complete(); - }); - }); - } - - /** - * Convenience function to update or return to discussions depending on device. - * - * @param discussionIds Ids of the new discussions. - * @param discTimecreated The time created of the discussion (if offline). - */ - protected returnToDiscussions(discussionIds?: number[], discTimecreated?: number): void { - const data: any = { - forumId: this.forumId, - cmId: this.cmId, - discussionIds: discussionIds, - discTimecreated: discTimecreated - }; - this.eventsProvider.trigger(AddonModForumProvider.NEW_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - // Delete the local files from the tmp folder. - this.uploaderProvider.clearTmpFiles(this.newDiscussion.files); - - if (this.svComponent && this.svComponent.isOn()) { - // Empty form. - this.hasOffline = false; - this.newDiscussion.subject = ''; - this.newDiscussion.message = null; - this.newDiscussion.files = []; - this.newDiscussion.postToAllGroups = false; - this.messageEditor.clearText(); - this.originalData = this.utils.clone(this.newDiscussion); - this.forceLeave = true; // Avoid asking for confirmation. - - // Trigger view event, to highlight the current opened discussion in the split view. - this.eventsProvider.trigger(AddonModForumProvider.VIEW_DISCUSSION_EVENT, { - forumId: this.forumId, - discussion: 0 - }, this.sitesProvider.getCurrentSiteId()); - } else { - this.forceLeave = true; // Avoid asking for confirmation. - this.navCtrl.pop(); - } - } - - /** - * Message changed. - * - * @param text The new text. - */ - onMessageChange(text: string): void { - this.newDiscussion.message = text; - } - - /** - * Add a new discussion. - */ - add(): void { - const forumName = this.forum.name; - const subject = this.newDiscussion.subject; - let message = this.newDiscussion.message; - const pin = this.newDiscussion.pin; - const attachments = this.newDiscussion.files; - const discTimecreated = this.timeCreated || Date.now(); - const options: any = { - discussionsubscribe: !!this.newDiscussion.subscribe - }; - - if (!subject) { - this.domUtils.showErrorModal('addon.mod_forum.erroremptysubject', true); - - return; - } - if (!message) { - this.domUtils.showErrorModal('addon.mod_forum.erroremptymessage', true); - - return; - } - - const modal = this.domUtils.showModalLoading('core.sending', true); - - // Add some HTML to the message if needed. - message = this.textUtils.formatHtmlLines(message); - - if (pin) { - options.discussionpinned = true; - } - - const groupIds = this.newDiscussion.postToAllGroups ? this.groupIds : [this.newDiscussion.groupId]; - - this.forumHelper.addNewDiscussion(this.forumId, forumName, this.courseId, subject, message, attachments, options, groupIds, - discTimecreated).then((discussionIds) => { - if (discussionIds) { - // Data sent to server, delete stored files (if any). - this.forumHelper.deleteNewDiscussionStoredFiles(this.forumId, discTimecreated); - - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'forum' }); - } - - if (discussionIds && discussionIds.length < groupIds.length) { - // Some discussions could not be created. - this.domUtils.showErrorModalDefault(null, 'addon.mod_forum.errorposttoallgroups', true); - } - - this.domUtils.triggerFormSubmittedEvent(this.formElement, !!discussionIds, this.sitesProvider.getCurrentSiteId()); - - this.returnToDiscussions(discussionIds, discTimecreated); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.cannotcreatediscussion', true); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Discard an offline saved discussion. - */ - discard(): void { - this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => { - const promises = []; - - promises.push(this.forumOffline.deleteNewDiscussion(this.forumId, this.timeCreated)); - promises.push(this.forumHelper.deleteNewDiscussionStoredFiles(this.forumId, this.timeCreated).catch(() => { - // Ignore errors, maybe there are no files. - })); - - return Promise.all(promises).then(() => { - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - - this.returnToDiscussions(); - }); - }).catch(() => { - // Cancelled. - }); - } - - /** - * Show or hide advanced form fields. - */ - toggleAdvanced(): void { - this.advanced = !this.advanced; - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave) { - return; - } - - if (this.forumHelper.hasPostDataChanged(this.newDiscussion, this.originalData)) { - // Show confirmation if some data has been modified. - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - // Delete the local files from the tmp folder. - this.uploaderProvider.clearTmpFiles(this.newDiscussion.files); - - if (this.formElement) { - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } - } - - /** - * Runs when the page is about to leave and no longer be the active page. - */ - ionViewWillLeave(): void { - this.syncObserver && this.syncObserver.off(); - delete this.syncObserver; - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - if (this.syncId) { - this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } - this.isDestroyed = true; - } -} diff --git a/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.html b/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.html deleted file mode 100644 index f5c8520c3..000000000 --- a/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.html +++ /dev/null @@ -1,19 +0,0 @@ - - - {{ 'core.sort' | translate }} - - - - - - - - - -

{{ sortOrder.label | translate }}

-
-
-
-
diff --git a/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.module.ts b/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.module.ts deleted file mode 100644 index 8cba267f7..000000000 --- a/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModForumSortOrderSelectorPage } from './sort-order-selector'; - -@NgModule({ - declarations: [ - AddonModForumSortOrderSelectorPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonModForumSortOrderSelectorPage), - TranslateModule.forChild() - ], -}) -export class AddonModForumSortOrderSelectorPagePageModule {} diff --git a/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.ts b/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.ts deleted file mode 100644 index dcf10ab0f..000000000 --- a/src/addon/mod/forum/pages/sort-order-selector/sort-order-selector.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; - -/** - * Page that displays the sort selector. - */ -@IonicPage({ segment: 'addon-mod-forum-sort-order-selector' }) -@Component({ - selector: 'page-addon-mod-forum-sort-order-selector', - templateUrl: 'sort-order-selector.html', -}) -export class AddonModForumSortOrderSelectorPage { - - sortOrders = []; - selected: number; - - constructor(navParams: NavParams, private viewCtrl: ViewController) { - this.sortOrders = navParams.get('sortOrders'); - this.selected = navParams.get('selected'); - } - - /** - * Close the modal. - */ - closeModal(): void { - this.viewCtrl.dismiss(); - } - - /** - * Select a sort order. - * - * @param sortOrder Selected sort order. - */ - selectSortOrder(sortOrder: any): void { - this.viewCtrl.dismiss(sortOrder); - } -} diff --git a/src/addon/mod/forum/providers/discussion-link-handler.ts b/src/addon/mod/forum/providers/discussion-link-handler.ts deleted file mode 100644 index cb6866dd7..000000000 --- a/src/addon/mod/forum/providers/discussion-link-handler.ts +++ /dev/null @@ -1,85 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; - -/** - * Handler to treat links to forum review. - */ -@Injectable() -export class AddonModForumDiscussionLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModForumDiscussionLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModForum'; - pattern = /\/mod\/forum\/discuss\.php.*([\&\?]d=\d+)/; - - constructor(protected domUtils: CoreDomUtilsProvider, protected linkHelper: CoreContentLinksHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @param data Extra data to handle the URL. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number, data?: any): - CoreContentLinksAction[] | Promise { - data = data || {}; - - // On 3.6 downwards, it will open the discussion but without knowing the lock status of the discussion. - // However canreply will be false. - - return [{ - action: (siteId, navCtrl?): void => { - const pageParams: any = { - courseId: courseId || parseInt(params.courseid, 10) || parseInt(params.cid, 10) || undefined, - discussionId: parseInt(params.d, 10), - cmId: data.cmid && parseInt(data.cmid, 10), - forumId: data.instance && parseInt(data.instance, 10) - }; - - if (data.postid || params.urlHash) { - pageParams.postId = parseInt(data.postid || params.urlHash.replace('p', '')); - } - if (params.parent) { - pageParams.parent = parseInt(params.parent); - } - - this.linkHelper.goInSite(navCtrl, 'AddonModForumDiscussionPage', pageParams, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/forum/providers/forum.ts b/src/addon/mod/forum/providers/forum.ts deleted file mode 100644 index 4118db0f1..000000000 --- a/src/addon/mod/forum/providers/forum.ts +++ /dev/null @@ -1,1185 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSite } from '@classes/site'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModForumOfflineProvider } from './offline'; -import { CoreRatingInfo } from '@core/rating/providers/rating'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some features for forums. - */ -@Injectable() -export class AddonModForumProvider { - static COMPONENT = 'mmaModForum'; - static DISCUSSIONS_PER_PAGE = 10; // Max of discussions per page. - static NEW_DISCUSSION_EVENT = 'addon_mod_forum_new_discussion'; - static REPLY_DISCUSSION_EVENT = 'addon_mod_forum_reply_discussion'; - static VIEW_DISCUSSION_EVENT = 'addon_mod_forum_view_discussion'; - static CHANGE_DISCUSSION_EVENT = 'addon_mod_forum_change_discussion_status'; - static MARK_READ_EVENT = 'addon_mod_forum_mark_read'; - static LEAVING_POSTS_PAGE = 'addon_mod_forum_leaving_posts_page'; - - static PREFERENCE_SORTORDER = 'forum_discussionlistsortorder'; - static SORTORDER_LASTPOST_DESC = 1; - static SORTORDER_LASTPOST_ASC = 2; - static SORTORDER_CREATED_DESC = 3; - static SORTORDER_CREATED_ASC = 4; - static SORTORDER_REPLIES_DESC = 5; - static SORTORDER_REPLIES_ASC = 6; - - static ALL_PARTICIPANTS = -1; - static ALL_GROUPS = -2; - - protected ROOT_CACHE_KEY = 'mmaModForum:'; - - constructor(private appProvider: CoreAppProvider, - private sitesProvider: CoreSitesProvider, - private groupsProvider: CoreGroupsProvider, - private filepoolProvider: CoreFilepoolProvider, - private userProvider: CoreUserProvider, - private translate: TranslateService, - private utils: CoreUtilsProvider, - private forumOffline: AddonModForumOfflineProvider, - private logHelper: CoreCourseLogHelperProvider) {} - - /** - * Get cache key for can add discussion WS calls. - * - * @param forumId Forum ID. - * @param groupId Group ID. - * @return Cache key. - */ - protected getCanAddDiscussionCacheKey(forumId: number, groupId: number): string { - return this.getCommonCanAddDiscussionCacheKey(forumId) + groupId; - } - - /** - * Get common part of cache key for can add discussion WS calls. - * TODO: Use getForumDataCacheKey as a prefix. - * - * @param forumId Forum ID. - * @return Cache key. - */ - protected getCommonCanAddDiscussionCacheKey(forumId: number): string { - return this.ROOT_CACHE_KEY + 'canadddiscussion:' + forumId + ':'; - } - - /** - * Get prefix cache key for all forum activity data WS calls. - * - * @param forumId Forum ID. - * @return Cache key. - */ - protected getForumDataPrefixCacheKey(forumId: number): string { - return this.ROOT_CACHE_KEY + forumId; - } - - /** - * Get cache key for discussion post data WS calls. - * - * @param forumId Forum ID. - * @param discussionId Discussion ID. - * @param postId Course ID. - * @return Cache key. - */ - protected getDiscussionPostDataCacheKey(forumId: number, discussionId: number, postId: number): string { - return this.getForumDiscussionDataCacheKey(forumId, discussionId) + ':post:' + postId; - } - - /** - * Get cache key for forum data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getForumDiscussionDataCacheKey(forumId: number, discussionId: number): string { - return this.getForumDataPrefixCacheKey(forumId) + ':discussion:' + discussionId; - } - - /** - * Get cache key for forum data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getForumDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'forum:' + courseId; - } - - /** - * Get cache key for forum access information WS calls. - * TODO: Use getForumDataCacheKey as a prefix. - * - * @param forumId Forum ID. - * @return Cache key. - */ - protected getAccessInformationCacheKey(forumId: number): string { - return this.ROOT_CACHE_KEY + 'accessInformation:' + forumId; - } - - /** - * Get cache key for forum discussion posts WS calls. - * TODO: Use getForumDiscussionDataCacheKey instead. - * - * @param discussionId Discussion ID. - * @return Cache key. - */ - protected getDiscussionPostsCacheKey(discussionId: number): string { - return this.ROOT_CACHE_KEY + 'discussion:' + discussionId; - } - - /** - * Get cache key for forum discussions list WS calls. - * - * @param forumId Forum ID. - * @param sortOrder Sort order. - * @return Cache key. - */ - protected getDiscussionsListCacheKey(forumId: number, sortOrder: number): string { - let key = this.ROOT_CACHE_KEY + 'discussions:' + forumId; - - if (sortOrder != AddonModForumProvider.SORTORDER_LASTPOST_DESC) { - key += ':' + sortOrder; - } - - return key; - } - - /** - * Add a new discussion. It will fail if offline or cannot connect. - * - * @param forumId Forum ID. - * @param subject New discussion's subject. - * @param message New discussion's message. - * @param options Options (subscribe, pin, ...). - * @param groupId Group this discussion belongs to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the discussion is created. - */ - addNewDiscussionOnline(forumId: number, subject: string, message: string, options?: any, groupId?: number, siteId?: string) - : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params: any = { - forumid: forumId, - subject: subject, - message: message, - options: this.utils.objectToArrayOfObjects(options, 'name', 'value') - }; - - if (groupId) { - params.groupid = groupId; - } - - return site.write('mod_forum_add_discussion', params).then((response) => { - // Other errors ocurring. - if (!response || !response.discussionid) { - return Promise.reject(this.utils.createFakeWSError('')); - } else { - return response.discussionid; - } - }); - }); - } - - /** - * Check if a user can post to a certain group. - * - * @param forumId Forum ID. - * @param groupId Group ID. - * @param options Other options. - * @return Promise resolved with an object with the following properties: - * - status (boolean) - * - canpindiscussions (boolean) - * - cancreateattachment (boolean) - */ - canAddDiscussion(forumId: number, groupId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - const params = { - forumid: forumId, - groupid: groupId, - }; - const preSets = { - cacheKey: this.getCanAddDiscussionCacheKey(forumId, groupId), - component: AddonModForumProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - return site.read('mod_forum_can_add_discussion', params, preSets).then((result) => { - if (result) { - if (typeof result.canpindiscussions == 'undefined') { - // WS doesn't support it yet, default it to false to prevent students from seeing the option. - result.canpindiscussions = false; - } - if (typeof result.cancreateattachment == 'undefined') { - // WS doesn't support it yet, default it to true since usually the users will be able to create them. - result.cancreateattachment = true; - } - - return result; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Check if a user can post to all groups. - * - * @param forumId Forum ID. - * @param options Other options. - * @return Promise resolved with an object with the following properties: - * - status (boolean) - * - canpindiscussions (boolean) - * - cancreateattachment (boolean) - */ - canAddDiscussionToAll(forumId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.canAddDiscussion(forumId, AddonModForumProvider.ALL_PARTICIPANTS, options); - } - - /** - * Delete a post. - * - * @param postId Post id. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - * @since 3.8 - */ - deletePost(postId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - postid: postId - }; - - return site.write('mod_forum_delete_post', params); - }); - } - - /** - * Extract the starting post of a discussion from a list of posts. The post is removed from the array passed as a parameter. - * - * @param posts Posts to search. - * @return Starting post or undefined if not found. - */ - extractStartingPost(posts: any[]): any { - const index = posts.findIndex((post) => !post.parentid); - - return index >= 0 ? posts.splice(index, 1).pop() : undefined; - } - - /** - * There was a bug adding new discussions to All Participants (see MDL-57962). Check if it's fixed. - * - * @return True if fixed, false otherwise. - */ - isAllParticipantsFixed(): boolean { - return this.sitesProvider.getCurrentSite() && - this.sitesProvider.getCurrentSite().isVersionGreaterEqualThan(['3.1.5', '3.2.2']); - } - - /** - * Returns whether or not getDiscussionPost WS available or not. - * - * @return If WS is avalaible. - * @since 3.8 - */ - isGetDiscussionPostAvailable(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('mod_forum_get_discussion_post'); - } - - /** - * Returns whether or not getDiscussionPost WS available or not. - * - * @param site Site. If not defined, current site. - * @return If WS is avalaible. - * @since 3.7 - */ - isGetDiscussionPostsAvailable(site?: CoreSite): boolean { - return site ? site.wsAvailable('mod_forum_get_discussion_posts') : - this.sitesProvider.wsAvailableInCurrentSite('mod_forum_get_discussion_posts'); - } - - /** - * Returns whether or not deletePost WS available or not. - * - * @return If WS is avalaible. - * @since 3.8 - */ - isDeletePostAvailable(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('mod_forum_delete_post'); - } - - /** - * Returns whether or not updatePost WS available or not. - * - * @return If WS is avalaible. - * @since 3.8 - */ - isUpdatePostAvailable(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('mod_forum_update_discussion_post'); - } - - /** - * Format discussions, setting groupname if the discussion group is valid. - * - * @param cmId Forum cmid. - * @param discussions List of discussions to format. - * @return Promise resolved with the formatted discussions. - */ - formatDiscussionsGroups(cmId: number, discussions: any[]): Promise { - discussions = this.utils.clone(discussions); - - return this.groupsProvider.getActivityAllowedGroups(cmId).then((result) => { - const strAllParts = this.translate.instant('core.allparticipants'); - const strAllGroups = this.translate.instant('core.allgroups'); - - // Turn groups into an object where each group is identified by id. - const groups = {}; - result.groups.forEach((fg) => { - groups[fg.id] = fg; - }); - - // Format discussions. - discussions.forEach((disc) => { - if (disc.groupid == AddonModForumProvider.ALL_PARTICIPANTS) { - disc.groupname = strAllParts; - } else if (disc.groupid == AddonModForumProvider.ALL_GROUPS) { - // Offline discussions only. - disc.groupname = strAllGroups; - } else { - const group = groups[disc.groupid]; - if (group) { - disc.groupname = group.name; - } - } - }); - - return discussions; - }).catch(() => { - return discussions; - }); - } - - /** - * Get all course forums. - * - * @param courseId Course ID. - * @param options Other options. - * @return Promise resolved when the forums are retrieved. - */ - getCourseForums(courseId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getForumDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModForumProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_forum_get_forums_by_courses', params, preSets); - }); - } - - /** - * Get a particular discussion post. - * - * @param forumId Forum ID. - * @param discussionId Discussion ID. - * @param postId Post ID. - * @param options Other options. - * @return Promise resolved when the post is retrieved. - */ - getDiscussionPost(forumId: number, discussionId: number, postId: number, options: CoreCourseCommonModWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - postid: postId, - }; - const preSets = { - cacheKey: this.getDiscussionPostDataCacheKey(forumId, discussionId, postId), - updateFrequency: CoreSite.FREQUENCY_USUALLY, - component: AddonModForumProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_forum_get_discussion_post', params, preSets).then((response) => { - if (response.post) { - return response.post; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a forum by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the forum is retrieved. - */ - getForum(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getCourseForums(courseId, options).then((forums) => { - const forum = forums.find((forum) => forum.cmid == cmId); - if (forum) { - return forum; - } - - return Promise.reject(null); - }); - } - - /** - * Get a forum by forum ID. - * - * @param courseId Course ID. - * @param forumId Forum ID. - * @param options Other options. - * @return Promise resolved when the forum is retrieved. - */ - getForumById(courseId: number, forumId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getCourseForums(courseId, options).then((forums) => { - const forum = forums.find((forum) => forum.id == forumId); - if (forum) { - return forum; - } - - return Promise.reject(null); - }); - } - - /** - * Get access information for a given forum. - * - * @param forumId Forum ID. - * @param options Other options. - * @return Object with access information. - * @since 3.7 - */ - getAccessInformation(forumId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - if (!site.wsAvailable('mod_forum_get_forum_access_information')) { - // Access information not available for 3.6 or older sites. - return Promise.resolve({}); - } - - const params = { - forumid: forumId, - }; - const preSets = { - cacheKey: this.getAccessInformationCacheKey(forumId), - component: AddonModForumProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_forum_get_forum_access_information', params, preSets); - }); - } - - /** - * Get forum discussion posts. - * - * @param discussionId Discussion ID. - * @param options Other options. - * @return Promise resolved with forum posts and rating info. - */ - getDiscussionPosts(discussionId: number, options: CoreCourseCommonModWSOptions = {}): Promise<{posts: any[], courseid?: number, - forumid?: number, ratinginfo?: CoreRatingInfo}> { - - // Convenience function to translate legacy data to new format. - const translateLegacyPostsFormat = (posts: any[]): any[] => { - return posts.map((post) => { - const newPost = { - id: post.id , - discussionid: post.discussion, - parentid: post.parent, - hasparent: !!post.parent, - author: { - id: post.userid, - fullname: post.userfullname, - urls: { profileimage: post.userpictureurl }, - }, - timecreated: post.created, - subject: post.subject, - message: post.message, - attachments : post.attachments, - capabilities: { - reply: !!post.canreply, - }, - - unread: !post.postread, - isprivatereply: !!post.isprivatereply, - tags: post.tags - }; - - if (post.groupname) { - newPost.author['groups'] = [{name: post.groupname}]; - } - - return newPost; - }); - }; - - const params = { - discussionid: discussionId, - }; - const preSets = { - cacheKey: this.getDiscussionPostsCacheKey(discussionId), - component: AddonModForumProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const wsName = this.isGetDiscussionPostsAvailable(site) ? 'mod_forum_get_discussion_posts' : - 'mod_forum_get_forum_discussion_posts'; - - return site.read(wsName, params, preSets).then((response) => { - if (response) { - - if (wsName == 'mod_forum_get_forum_discussion_posts') { - response.posts = translateLegacyPostsFormat(response.posts); - } - this.storeUserData(response.posts); - - return response; - } else { - return Promise.reject(null); - } - }); - }); - } - - /** - * Sort forum discussion posts by an specified field. - * - * @param posts Discussion posts to be sorted in place. - * @param direction Direction of the sorting (ASC / DESC). - */ - sortDiscussionPosts(posts: any[], direction: string): void { - // @todo: Check children when sorting. - posts.sort((a, b) => { - a = parseInt(a.timecreated, 10) || 0; - b = parseInt(b.timecreated, 10) || 0; - if (a == 0 || b == 0) { - // Leave 0 at the end. - return b - a; - } - - if (direction == 'ASC') { - return a - b; - } else { - return b - a; - } - }); - } - - /** - * Return whether discussion lists can be sorted. - * - * @param site Site. If not defined, current site. - * @return True if discussion lists can be sorted. - */ - isDiscussionListSortingAvailable(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site && site.isVersionGreaterEqualThan('3.7'); - } - - /** - * Return the list of available sort orders. - * - * @return List of sort orders. - */ - getAvailableSortOrders(): {label: string, value: number}[] { - const sortOrders = [ - { - label: 'addon.mod_forum.discussionlistsortbylastpostdesc', - value: AddonModForumProvider.SORTORDER_LASTPOST_DESC - }, - ]; - - if (this.isDiscussionListSortingAvailable()) { - sortOrders.push( - { - label: 'addon.mod_forum.discussionlistsortbylastpostasc', - value: AddonModForumProvider.SORTORDER_LASTPOST_ASC - }, - { - label: 'addon.mod_forum.discussionlistsortbycreateddesc', - value: AddonModForumProvider.SORTORDER_CREATED_DESC - }, - { - label: 'addon.mod_forum.discussionlistsortbycreatedasc', - value: AddonModForumProvider.SORTORDER_CREATED_ASC - }, - { - label: 'addon.mod_forum.discussionlistsortbyrepliesdesc', - value: AddonModForumProvider.SORTORDER_REPLIES_DESC - }, - { - label: 'addon.mod_forum.discussionlistsortbyrepliesasc', - value: AddonModForumProvider.SORTORDER_REPLIES_ASC - } - ); - } - - return sortOrders; - } - - /** - * Get forum discussions. - * - * @param forumId Forum ID. - * @param options Other options. - * @return Promise resolved with an object with: - * - discussions: List of discussions. Note that for every discussion in the list discussion.id is the main post ID but - * discussion ID is discussion.discussion. - * - canLoadMore: True if there may be more discussions to load. - */ - getDiscussions(forumId: number, options: AddonModForumGetDiscussionsOptions = {}): Promise { - options.sortOrder = options.sortOrder || AddonModForumProvider.SORTORDER_LASTPOST_DESC; - options.page = options.page || 0; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - let method = 'mod_forum_get_forum_discussions_paginated'; - const params: any = { - forumid: forumId, - page: options.page, - perpage: AddonModForumProvider.DISCUSSIONS_PER_PAGE, - }; - - if (site.wsAvailable('mod_forum_get_forum_discussions')) { - // Since Moodle 3.7. - method = 'mod_forum_get_forum_discussions'; - params.sortorder = options.sortOrder; - } else { - if (options.sortOrder == AddonModForumProvider.SORTORDER_LASTPOST_DESC) { - params.sortby = 'timemodified'; - params.sortdirection = 'DESC'; - } else { - // Sorting not supported with the old WS method. - return Promise.reject(null); - } - } - - const preSets = { - cacheKey: this.getDiscussionsListCacheKey(forumId, options.sortOrder), - component: AddonModForumProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read(method, params, preSets).catch((error) => { - // Try to get the data from cache stored with the old WS method. - if (!this.appProvider.isOnline() && method == 'mod_forum_get_forum_discussion' && - options.sortOrder == AddonModForumProvider.SORTORDER_LASTPOST_DESC) { - - const params = { - forumid: forumId, - page: options.page, - perpage: AddonModForumProvider.DISCUSSIONS_PER_PAGE, - sortby: 'timemodified', - sortdirection: 'DESC' - }; - Object.assign(preSets, this.sitesProvider.getReadingStrategyPreSets(CoreSitesReadingStrategy.PreferCache)); - - return site.read('mod_forum_get_forum_discussions_paginated', params, preSets); - } - - return Promise.reject(error); - }).then((response) => { - if (response) { - this.storeUserData(response.discussions); - - return Promise.resolve({ - discussions: response.discussions, - canLoadMore: response.discussions.length >= AddonModForumProvider.DISCUSSIONS_PER_PAGE, - }); - } else { - return Promise.reject(null); - } - }); - }); - } - - /** - * Get forum discussions in several pages. - * If a page fails, the discussions until that page will be returned along with a flag indicating an error occurred. - * - * @param forumId Forum ID. - * @param cmId Forum cmid. - * @param sortOrder Sort order. - * @param forceCache True to always get the value from cache, false otherwise. - * @param numPages Number of pages to get. If not defined, all pages. - * @param startPage Page to start. If not defined, first page. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with an object with: - * - discussions: List of discussions. - * - error: True if an error occurred, false otherwise. - */ - getDiscussionsInPages(forumId: number, options: AddonModForumGetDiscussionsInPagesOptions = {}): Promise { - options.page = options.page || 0; - - const result = { - discussions: [], - error: false - }; - let numPages = typeof options.numPages == 'undefined' ? -1 : options.numPages; - - if (!numPages) { - return Promise.resolve(result); - } - - const getPage = (page: number): Promise => { - // Get page discussions. - return this.getDiscussions(forumId, options).then((response) => { - result.discussions = result.discussions.concat(response.discussions); - numPages--; - - if (response.canLoadMore && numPages !== 0) { - return getPage(page + 1); // Get next page. - } else { - return result; - } - }).catch(() => { - // Error getting a page. - result.error = true; - - return result; - }); - }; - - return getPage(options.page); - } - - /** - * Invalidates can add discussion WS calls. - * - * @param forumId Forum ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateCanAddDiscussion(forumId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getCommonCanAddDiscussionCacheKey(forumId)); - }); - } - - /** - * Invalidate the prefetched content except files. - * To invalidate files, use AddonModForum#invalidateFiles. - * - * @param moduleId The module ID. - * @param courseId Course ID. - * @return Promise resolved when data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - // Get the forum first, we need the forum ID. - return this.getForum(courseId, moduleId).then((forum) => { - const promises = []; - - promises.push(this.invalidateForumData(courseId)); - promises.push(this.invalidateDiscussionsList(forum.id)); - promises.push(this.invalidateCanAddDiscussion(forum.id)); - promises.push(this.invalidateAccessInformation(forum.id)); - - this.getAvailableSortOrders().forEach((sortOrder) => { - // We need to get the list of discussions to be able to invalidate their posts. - promises.push(this.getDiscussionsInPages(forum.id, { - cmId: forum.cmid, - sortOrder: sortOrder.value, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - }).then((response) => { - // Now invalidate the WS calls. - const promises = []; - - response.discussions.forEach((discussion) => { - promises.push(this.invalidateDiscussionPosts(discussion.discussion, forum.id)); - }); - - return this.utils.allPromises(promises); - })); - }); - - if (this.isDiscussionListSortingAvailable()) { - promises.push(this.userProvider.invalidateUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER)); - } - - return this.utils.allPromises(promises); - }); - } - - /** - * Invalidates access information. - * - * @param forumId Forum ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAccessInformation(forumId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(forumId)); - }); - } - - /** - * Invalidates forum discussion posts. - * - * @param discussionId Discussion ID. - * @param forumId Forum ID. If not set, we can't invalidate individual post information. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateDiscussionPosts(discussionId: number, forumId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const promises = [site.invalidateWsCacheForKey(this.getDiscussionPostsCacheKey(discussionId))]; - - if (forumId) { - promises.push(site.invalidateWsCacheForKeyStartingWith(this.getForumDiscussionDataCacheKey(forumId, discussionId))); - } - - return this.utils.allPromises(promises); - }); - } - - /** - * Invalidates discussion list. - * - * @param forumId Forum ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateDiscussionsList(forumId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.utils.allPromises(this.getAvailableSortOrders().map((sortOrder) => { - return site.invalidateWsCacheForKey(this.getDiscussionsListCacheKey(forumId, sortOrder.value)); - })); - }); - } - - /** - * Invalidate the prefetched files. - * - * @param moduleId The module ID. - * @return Promise resolved when the files are invalidated. - */ - invalidateFiles(moduleId: number): Promise { - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModForumProvider.COMPONENT, moduleId); - } - - /** - * Invalidates forum data. - * - * @param courseId Course ID. - * @return Promise resolved when the data is invalidated. - */ - invalidateForumData(courseId: number): Promise { - return this.sitesProvider.getCurrentSite().invalidateWsCacheForKey(this.getForumDataCacheKey(courseId)); - } - - /** - * Report a forum as being viewed. - * - * @param id Module ID. - * @param name Name of the forum. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - forumid: id - }; - - return this.logHelper.logSingle('mod_forum_view_forum', params, AddonModForumProvider.COMPONENT, id, name, 'forum', {}, - siteId); - } - - /** - * Report a forum discussion as being viewed. - * - * @param id Discussion ID. - * @param forumId Forum ID. - * @param name Name of the forum. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logDiscussionView(id: number, forumId: number, name?: string, siteId?: string): Promise { - const params = { - discussionid: id - }; - - return this.logHelper.logSingle('mod_forum_view_forum_discussion', params, AddonModForumProvider.COMPONENT, forumId, name, - 'forum', params, siteId); - } - - /** - * Reply to a certain post. - * - * @param postId ID of the post being replied. - * @param discussionId ID of the discussion the user is replying to. - * @param forumId ID of the forum the user is replying to. - * @param name Forum name. - * @param courseId Course ID the forum belongs to. - * @param subject New post's subject. - * @param message New post's message. - * @param options Options (subscribe, attachments, ...). - * @param siteId Site ID. If not defined, current site. - * @param allowOffline True if it can be stored in offline, false otherwise. - * @return Promise resolved with post ID if sent online, resolved with false if stored offline. - */ - replyPost(postId: number, discussionId: number, forumId: number, name: string, courseId: number, subject: string, - message: string, options?: any, siteId?: string, allowOffline?: boolean): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - if (!forumId) { - // Not enough data to store in offline, reject. - return Promise.reject(this.translate.instant('core.networkerrormsg')); - } - - return this.forumOffline.replyPost(postId, discussionId, forumId, name, courseId, subject, message, options, siteId) - .then(() => { - return false; - }); - }; - - if (!this.appProvider.isOnline() && allowOffline) { - // App is offline, store the action. - return storeOffline(); - } - - // If there's already a reply to be sent to the server, discard it first. - return this.forumOffline.deleteReply(postId, siteId).then(() => { - - return this.replyPostOnline(postId, subject, message, options, siteId).then(() => { - return true; - }).catch((error) => { - if (allowOffline && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Reply to a certain post. It will fail if offline or cannot connect. - * - * @param postId ID of the post being replied. - * @param subject New post's subject. - * @param message New post's message. - * @param options Options (subscribe, attachments, ...). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the created post id. - */ - replyPostOnline(postId: number, subject: string, message: string, options?: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - postid: postId, - subject: subject, - message: message, - options: this.utils.objectToArrayOfObjects(options, 'name', 'value') - }; - - return site.write('mod_forum_add_discussion_post', params).then((response) => { - if (!response || !response.postid) { - return Promise.reject(this.utils.createFakeWSError('')); - } else { - return response.postid; - } - }); - }); - } - - /** - * Lock or unlock a discussion. - * - * @param forumId Forum id. - * @param discussionId DIscussion id. - * @param locked True to lock, false to unlock. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - * @since 3.7 - */ - setLockState(forumId: number, discussionId: number, locked: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - forumid: forumId, - discussionid: discussionId, - targetstate: locked ? 0 : 1 - }; - - return site.write('mod_forum_set_lock_state', params); - }); - } - - /** - * Returns whether the set pin state WS is available. - * - * @param site Site. If not defined, current site. - * @return Whether it's available. - * @since 3.7 - */ - isSetPinStateAvailableForSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return this.sitesProvider.wsAvailableInCurrentSite('mod_forum_set_pin_state'); - } - - /** - * Pin or unpin a discussion. - * - * @param discussionId Discussion id. - * @param locked True to pin, false to unpin. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - * @since 3.7 - */ - setPinState(discussionId: number, pinned: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - discussionid: discussionId, - targetstate: pinned ? 1 : 0 - }; - - return site.write('mod_forum_set_pin_state', params); - }); - } - - /** - * Star or unstar a discussion. - * - * @param discussionId Discussion id. - * @param starred True to star, false to unstar. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - * @since 3.7 - */ - toggleFavouriteState(discussionId: number, starred: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - discussionid: discussionId, - targetstate: starred ? 1 : 0 - }; - - return site.write('mod_forum_toggle_favourite_state', params); - }); - } - - /** - * Store the users data from a discussions/posts list. - * - * @param list Array of posts or discussions. - */ - protected storeUserData(list: any[]): void { - const users = {}; - - list.forEach((entry) => { - if (entry.author) { - const authorId = parseInt(entry.author.id); - if (!isNaN(authorId) && !users[authorId]) { - users[authorId] = { - id: entry.author.id, - fullname: entry.author.fullname, - profileimageurl: entry.author.urls.profileimage - }; - } - } - const userId = parseInt(entry.userid); - if (!isNaN(userId) && !users[userId]) { - users[userId] = { - id: userId, - fullname: entry.userfullname, - profileimageurl: entry.userpictureurl - }; - } - const userModified = parseInt(entry.usermodified); - if (!isNaN(userModified) && !users[userModified]) { - users[userModified] = { - id: userModified, - fullname: entry.usermodifiedfullname, - profileimageurl: entry.usermodifiedpictureurl - }; - } - }); - - this.userProvider.storeUsers(this.utils.objectToArray(users)); - } - - /** - * Update a certain post. - * - * @param postId ID of the post being edited. - * @param subject New post's subject. - * @param message New post's message. - * @param options Options (subscribe, attachments, ...). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with success boolean when done. - */ - updatePost(postId: number, subject: string, message: string, options?: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - postid: postId, - subject: subject, - message: message, - options: this.utils.objectToArrayOfObjects(options, 'name', 'value') - }; - - return site.write('mod_forum_update_discussion_post', params).then((response) => { - return response && response.status; - }); - }); - } -} - -/** - * Options to pass to get discussions. - */ -export type AddonModForumGetDiscussionsOptions = CoreCourseCommonModWSOptions & { - sortOrder?: number; // Sort order. - page?: number; // Page. Defaults to 0. -}; - -/** - * Options to pass to get discussions in pages. - */ -export type AddonModForumGetDiscussionsInPagesOptions = AddonModForumGetDiscussionsOptions & { - numPages?: number; // Number of pages to get. If not defined, all pages. -}; diff --git a/src/addon/mod/forum/providers/helper.ts b/src/addon/mod/forum/providers/helper.ts deleted file mode 100644 index 63aa04019..000000000 --- a/src/addon/mod/forum/providers/helper.ts +++ /dev/null @@ -1,452 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFileProvider } from '@providers/file'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModForumProvider } from './forum'; -import { AddonModForumOfflineProvider } from './offline'; - -/** - * Service that provides some features for forums. - */ -@Injectable() -export class AddonModForumHelperProvider { - constructor(private translate: TranslateService, - private fileProvider: CoreFileProvider, - private sitesProvider: CoreSitesProvider, - private uploaderProvider: CoreFileUploaderProvider, - private timeUtils: CoreTimeUtilsProvider, - private userProvider: CoreUserProvider, - private appProvider: CoreAppProvider, - private utils: CoreUtilsProvider, - private forumProvider: AddonModForumProvider, - private forumOffline: AddonModForumOfflineProvider) {} - - /** - * Add a new discussion. - * - * @param forumId Forum ID. - * @param name Forum name. - * @param courseId Course ID the forum belongs to. - * @param subject New discussion's subject. - * @param message New discussion's message. - * @param attachments New discussion's attachments. - * @param options Options (subscribe, pin, ...). - * @param groupIds Groups this discussion belongs to. - * @param timeCreated The time the discussion was created. Only used when editing discussion. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with ids of the created discussions or null if stored offline - */ - addNewDiscussion(forumId: number, name: string, courseId: number, subject: string, message: string, attachments?: any[], - options?: any, groupIds?: number[], timeCreated?: number, siteId?: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - groupIds = groupIds && groupIds.length > 0 ? groupIds : [0]; - - let saveOffline = false; - const attachmentsIds = []; - let offlineAttachments: any; - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - // Multiple groups, the discussion is being posted to all groups. - const groupId = groupIds.length > 1 ? AddonModForumProvider.ALL_GROUPS : groupIds[0]; - - if (offlineAttachments) { - options.attachmentsid = offlineAttachments; - } - - return this.forumOffline.addNewDiscussion(forumId, name, courseId, subject, message, options, - groupId, timeCreated, siteId).then(() => { - return null; - }); - }; - - // First try to upload attachments, once per group. - let promise; - if (attachments && attachments.length > 0) { - const promises = groupIds.map(() => { - return this.uploadOrStoreNewDiscussionFiles(forumId, timeCreated, attachments, false).then((attach) => { - attachmentsIds.push(attach); - }); - }); - - promise = Promise.all(promises).catch(() => { - // Cannot upload them in online, save them in offline. - saveOffline = true; - - return this.uploadOrStoreNewDiscussionFiles(forumId, timeCreated, attachments, true).then((attach) => { - offlineAttachments = attach; - }); - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - // If we are editing an offline discussion, discard previous first. - let discardPromise; - if (timeCreated) { - discardPromise = this.forumOffline.deleteNewDiscussion(forumId, timeCreated, siteId); - } else { - discardPromise = Promise.resolve(); - } - - return discardPromise.then(() => { - if (saveOffline || !this.appProvider.isOnline()) { - return storeOffline(); - } - - const errors = []; - const discussionIds = []; - - const promises = groupIds.map((groupId, index) => { - const grouOptions = this.utils.clone(options); - if (attachmentsIds[index]) { - grouOptions.attachmentsid = attachmentsIds[index]; - } - - return this.forumProvider.addNewDiscussionOnline(forumId, subject, message, grouOptions, groupId, siteId) - .then((discussionId) => { - discussionIds.push(discussionId); - }).catch((error) => { - errors.push(error); - }); - }); - - return Promise.all(promises).then(() => { - if (errors.length == groupIds.length) { - // All requests have failed. - for (let i = 0; i < errors.length; i++) { - if (this.utils.isWebServiceError(errors[i]) || attachments.length > 0) { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(errors[i]); - } - } - - // Couldn't connect to server, store offline. - return storeOffline(); - } - - return discussionIds; - }); - }); - }); - } - - /** - * Convert offline reply to online format in order to be compatible with them. - * - * @param offlineReply Offline version of the reply. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object converted to Online. - */ - convertOfflineReplyToOnline(offlineReply: any, siteId?: string): Promise { - const reply: any = { - id: -offlineReply.timecreated, - discussionid: offlineReply.discussionid, - parentid: offlineReply.postid, - hasparent: !!offlineReply.postid, - author: { - id: offlineReply.userid, - }, - timecreated: false, - subject: offlineReply.subject, - message: offlineReply.message, - attachments: [], - capabilities: { - reply: false, - }, - unread: false, - isprivatereply: offlineReply.options && offlineReply.options.private, - tags: null - }, - promises = []; - - // Treat attachments if any. - if (offlineReply.options && offlineReply.options.attachmentsid) { - reply.attachments = offlineReply.options.attachmentsid.online || []; - - if (offlineReply.options.attachmentsid.offline) { - promises.push(this.getReplyStoredFiles(offlineReply.forumid, reply.parentid, siteId, offlineReply.userid) - .then((files) => { - reply.attachments = reply.attachments.concat(files); - })); - } - } - - // Get user data. - promises.push(this.userProvider.getProfile(offlineReply.userid, offlineReply.courseid, true).then((user) => { - reply.author.fullname = user.fullname; - reply.author.urls = { profileimage: user.profileimageurl }; - }).catch(() => { - // Ignore errors. - })); - - return Promise.all(promises).then(() => { - reply.attachment = reply.attachments.length > 0 ? 1 : 0; - - return reply; - }); - } - - /** - * Delete stored attachment files for a new discussion. - * - * @param forumId Forum ID. - * @param timecreated The time the discussion was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when deleted. - */ - deleteNewDiscussionStoredFiles(forumId: number, timecreated: number, siteId?: string): Promise { - return this.forumOffline.getNewDiscussionFolder(forumId, timecreated, siteId).then((folderPath) => { - return this.fileProvider.removeDir(folderPath).catch(() => { - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists. - }); - }); - } - - /** - * Delete stored attachment files for a reply. - * - * @param forumId Forum ID. - * @param postId ID of the post being replied. - * @param siteId Site ID. If not defined, current site. - * @param userId User the reply belongs to. If not defined, current user in site. - * @return Promise resolved when deleted. - */ - deleteReplyStoredFiles(forumId: number, postId: number, siteId?: string, userId?: number): Promise { - return this.forumOffline.getReplyFolder(forumId, postId, siteId, userId).then((folderPath) => { - return this.fileProvider.removeDir(folderPath).catch(() => { - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists. - }); - }); - } - - /** - * Returns the availability message of the given forum. - * - * @param forum Forum instance. - * @return Message or null if the forum has no cut-off or due date. - */ - getAvailabilityMessage(forum: any): string { - if (this.isCutoffDateReached(forum)) { - return this.translate.instant('addon.mod_forum.cutoffdatereached'); - } else if (this.isDueDateReached(forum)) { - const dueDate = this.timeUtils.userDate(forum.duedate * 1000); - - return this.translate.instant('addon.mod_forum.thisforumisdue', {$a: dueDate}); - } else if (forum.duedate > 0) { - const dueDate = this.timeUtils.userDate(forum.duedate * 1000); - - return this.translate.instant('addon.mod_forum.thisforumhasduedate', {$a: dueDate}); - } else { - return null; - } - } - - /** - * Get a forum discussion by id. - * - * This function is inefficient because it needs to fetch all discussion pages in the worst case. - * - * @param forumId Forum ID. - * @param cmId Forum cmid - * @param discussionId Discussion ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the discussion data. - */ - getDiscussionById(forumId: number, cmId: number, discussionId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const findDiscussion = (page: number): Promise => { - return this.forumProvider.getDiscussions(forumId, { - cmId, - page, - siteId, - }).then((response) => { - if (response.discussions && response.discussions.length > 0) { - // Note that discussion.id is the main post ID but discussion ID is discussion.discussion. - const discussion = response.discussions.find((discussion) => discussion.discussion == discussionId); - if (discussion) { - return discussion; - } - if (response.canLoadMore) { - return findDiscussion(page + 1); - } - } - - return Promise.reject(null); - }); - }; - - return findDiscussion(0); - } - - /** - * Get a list of stored attachment files for a new discussion. See AddonModForumHelper#storeNewDiscussionFiles. - * - * @param forumId Forum ID. - * @param timecreated The time the discussion was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getNewDiscussionStoredFiles(forumId: number, timecreated: number, siteId?: string): Promise { - return this.forumOffline.getNewDiscussionFolder(forumId, timecreated, siteId).then((folderPath) => { - return this.uploaderProvider.getStoredFiles(folderPath); - }); - } - - /** - * Get a list of stored attachment files for a reply. See AddonModForumHelper#storeReplyFiles. - * - * @param forumId Forum ID. - * @param postId ID of the post being replied. - * @param siteId Site ID. If not defined, current site. - * @param userId User the reply belongs to. If not defined, current user in site. - * @return Promise resolved with the files. - */ - getReplyStoredFiles(forumId: number, postId: number, siteId?: string, userId?: number): Promise { - return this.forumOffline.getReplyFolder(forumId, postId, siteId, userId).then((folderPath) => { - return this.uploaderProvider.getStoredFiles(folderPath); - }); - } - - /** - * Check if the data of a post/discussion has changed. - * - * @param post Current data. - * @param original Original ata. - * @return True if data has changed, false otherwise. - */ - hasPostDataChanged(post: any, original?: any): boolean { - if (!original || original.subject == null) { - // There is no original data, assume it hasn't changed. - return false; - } - - if (post.subject != original.subject || post.message != original.message) { - return true; - } - - if (post.isprivatereply != original.isprivatereply) { - return true; - } - - return this.uploaderProvider.areFileListDifferent(post.files, original.files); - } - - /** - * Is the cutoff date for the forum reached? - * - * @param forum Forum instance. - */ - isCutoffDateReached(forum: any): boolean { - const now = Date.now() / 1000; - - return forum.cutoffdate > 0 && forum.cutoffdate < now; - } - - /** - * Is the due date for the forum reached? - * - * @param forum Forum instance. - */ - isDueDateReached(forum: any): boolean { - const now = Date.now() / 1000; - - return forum.duedate > 0 && forum.duedate < now; - } - - /** - * Given a list of files (either online files or local files), store the local files in a local folder - * to be submitted later. - * - * @param forumId Forum ID. - * @param timecreated The time the discussion was created. - * @param files List of files. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - storeNewDiscussionFiles(forumId: number, timecreated: number, files: any[], siteId?: string): Promise { - // Get the folder where to store the files. - return this.forumOffline.getNewDiscussionFolder(forumId, timecreated, siteId).then((folderPath) => { - return this.uploaderProvider.storeFilesToUpload(folderPath, files); - }); - } - - /** - * Given a list of files (either online files or local files), store the local files in a local folder - * to be submitted later. - * - * @param forumId Forum ID. - * @param postId ID of the post being replied. - * @param files List of files. - * @param siteId Site ID. If not defined, current site. - * @param userId User the reply belongs to. If not defined, current user in site. - * @return Promise resolved if success, rejected otherwise. - */ - storeReplyFiles(forumId: number, postId: number, files: any[], siteId?: string, userId?: number): Promise { - // Get the folder where to store the files. - return this.forumOffline.getReplyFolder(forumId, postId, siteId, userId).then((folderPath) => { - return this.uploaderProvider.storeFilesToUpload(folderPath, files); - }); - } - - /** - * Upload or store some files for a new discussion, depending if the user is offline or not. - * - * @param forumId Forum ID. - * @param timecreated The time the discussion was created. - * @param files List of files. - * @param offline True if files sould be stored for offline, false to upload them. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success. - */ - uploadOrStoreNewDiscussionFiles(forumId: number, timecreated: number, files: any[], offline: boolean, siteId?: string) - : Promise { - if (offline) { - return this.storeNewDiscussionFiles(forumId, timecreated, files, siteId); - } else { - return this.uploaderProvider.uploadOrReuploadFiles(files, AddonModForumProvider.COMPONENT, forumId, siteId); - } - } - - /** - * Upload or store some files for a reply, depending if the user is offline or not. - * - * @param forumId Forum ID. - * @param postId ID of the post being replied. - * @param files List of files. - * @param offline True if files sould be stored for offline, false to upload them. - * @param siteId Site ID. If not defined, current site. - * @param userId User the reply belongs to. If not defined, current user in site. - * @return Promise resolved if success. - */ - uploadOrStoreReplyFiles(forumId: number, postId: number, files: any[], offline: boolean, siteId?: string, userId?: number) - : Promise { - if (offline) { - return this.storeReplyFiles(forumId, postId, files, siteId, userId); - } else { - return this.uploaderProvider.uploadOrReuploadFiles(files, AddonModForumProvider.COMPONENT, forumId, siteId); - } - } -} diff --git a/src/addon/mod/forum/providers/index-link-handler.ts b/src/addon/mod/forum/providers/index-link-handler.ts deleted file mode 100644 index 9e059e491..000000000 --- a/src/addon/mod/forum/providers/index-link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to forum index. - */ -@Injectable() -export class AddonModForumIndexLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModForumIndexLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModForum', 'forum', 'f'); - } -} diff --git a/src/addon/mod/forum/providers/list-link-handler.ts b/src/addon/mod/forum/providers/list-link-handler.ts deleted file mode 100644 index 1f0093d11..000000000 --- a/src/addon/mod/forum/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to forum list page. - */ -@Injectable() -export class AddonModForumListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModForumListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModForum', 'forum'); - } -} diff --git a/src/addon/mod/forum/providers/module-handler.ts b/src/addon/mod/forum/providers/module-handler.ts deleted file mode 100644 index f79aa5a75..000000000 --- a/src/addon/mod/forum/providers/module-handler.ts +++ /dev/null @@ -1,157 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModForumProvider } from './forum'; -import { AddonModForumIndexComponent } from '../components/index/index'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support forum modules. - */ -@Injectable() -export class AddonModForumModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModForum'; - modName = 'forum'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, - [CoreConstants.FEATURE_RATE]: true, - [CoreConstants.FEATURE_PLAGIARISM]: true - }; - - constructor(private courseProvider: CoreCourseProvider, private forumProvider: AddonModForumProvider, - private translate: TranslateService, private eventsProvider: CoreEventsProvider, - private sitesProvider: CoreSitesProvider) {} - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - const data: CoreCourseModuleHandlerData = { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_forum-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModForumIndexPage', pageParams, options); - } - }; - - if (typeof module.afterlink != 'undefined') { - data.extraBadgeColor = ''; - const match = />(\d+)[^<]+/.exec(module.afterlink); - data.extraBadge = match ? this.translate.instant('addon.mod_forum.unreadpostsnumber', {$a : match[1] }) : ''; - } else { - this.updateExtraBadge(data, courseId, module.id); - } - - const event = this.eventsProvider.on(AddonModForumProvider.MARK_READ_EVENT, (eventData) => { - if (eventData.courseId == courseId && eventData.moduleId == module.id) { - this.updateExtraBadge(data, eventData.courseId, eventData.moduleId, eventData.siteId); - } - }, this.sitesProvider.getCurrentSiteId()); - - data.onDestroy = (): void => { - event && event.off(); - }; - - return data; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModForumIndexComponent; - } - - /** - * Whether to display the course refresher in single activity course format. If it returns false, a refresher must be - * included in the template that calls the doRefresh method of the component. Defaults to true. - * - * @return Whether the refresher should be displayed. - */ - displayRefresherInSingleActivity(): boolean { - return false; - } - - /** - * Triggers an update for the extra badge text. - * - * @param data Course Module Handler data. - * @param courseId Course ID. - * @param moduleId Course module ID. - * @param siteId Site ID. If not defined, current site. - */ - updateExtraBadge(data: CoreCourseModuleHandlerData, courseId: number, moduleId: number, siteId?: string): void { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - if (!siteId) { - return; - } - - data.extraBadge = this.translate.instant('core.loading'); - data.extraBadgeColor = 'light'; - - this.forumProvider.invalidateForumData(courseId).finally(() => { - // Handle unread posts. - this.forumProvider.getForum(courseId, moduleId, {siteId}).then((forumData) => { - data.extraBadgeColor = ''; - data.extraBadge = forumData.unreadpostscount ? this.translate.instant('addon.mod_forum.unreadpostsnumber', - {$a : forumData.unreadpostscount }) : ''; - }).catch(() => { - data.extraBadgeColor = ''; - data.extraBadge = ''; - // Ignore errors. - }); - }); - } -} diff --git a/src/addon/mod/forum/providers/offline.ts b/src/addon/mod/forum/providers/offline.ts deleted file mode 100644 index 4679b8a7b..000000000 --- a/src/addon/mod/forum/providers/offline.ts +++ /dev/null @@ -1,462 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFileProvider } from '@providers/file'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { AddonModForumProvider } from './forum'; - -/** - * Service to handle offline forum. - */ -@Injectable() -export class AddonModForumOfflineProvider { - - // Variables for database. - static DISCUSSIONS_TABLE = 'addon_mod_forum_discussions'; - static REPLIES_TABLE = 'addon_mod_forum_replies'; - - protected siteSchema: CoreSiteSchema = { - name: 'AddonModForumOfflineProvider', - version: 1, - tables: [ - { - name: AddonModForumOfflineProvider.DISCUSSIONS_TABLE, - columns: [ - { - name: 'forumid', - type: 'INTEGER', - }, - { - name: 'name', - type: 'TEXT', - }, - { - name: 'courseid', - type: 'INTEGER', - }, - { - name: 'subject', - type: 'TEXT', - }, - { - name: 'message', - type: 'TEXT', - }, - { - name: 'options', - type: 'TEXT', - }, - { - name: 'groupid', - type: 'INTEGER', - }, - { - name: 'userid', - type: 'INTEGER', - }, - { - name: 'timecreated', - type: 'INTEGER', - } - ], - primaryKeys: ['forumid', 'userid', 'timecreated'] - }, - { - name: AddonModForumOfflineProvider.REPLIES_TABLE, - columns: [ - { - name: 'postid', - type: 'INTEGER', - }, - { - name: 'discussionid', - type: 'INTEGER', - }, - { - name: 'forumid', - type: 'INTEGER', - }, - { - name: 'name', - type: 'TEXT', - }, - { - name: 'courseid', - type: 'INTEGER', - }, - { - name: 'subject', - type: 'TEXT', - }, - { - name: 'message', - type: 'TEXT', - }, - { - name: 'options', - type: 'TEXT', - }, - { - name: 'userid', - type: 'INTEGER', - }, - { - name: 'timecreated', - type: 'INTEGER', - } - ], - primaryKeys: ['postid', 'userid'] - } - ] - }; - - constructor(private fileProvider: CoreFileProvider, - private sitesProvider: CoreSitesProvider, - private textUtils: CoreTextUtilsProvider) { - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete a forum offline discussion. - * - * @param forumId Forum ID. - * @param timeCreated The time the discussion was created. - * @param siteId Site ID. If not defined, current site. - * @param userId User the discussion belongs to. If not defined, current user in site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteNewDiscussion(forumId: number, timeCreated: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - forumid: forumId, - userid: userId || site.getUserId(), - timecreated: timeCreated, - }; - - return site.getDb().deleteRecords(AddonModForumOfflineProvider.DISCUSSIONS_TABLE, conditions); - }); - } - - /** - * Get a forum offline discussion. - * - * @param forumId Forum ID. - * @param timeCreated The time the discussion was created. - * @param siteId Site ID. If not defined, current site. - * @param userId User the discussion belongs to. If not defined, current user in site. - * @return Promise resolved if stored, rejected if failure. - */ - getNewDiscussion(forumId: number, timeCreated: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - forumid: forumId, - userid: userId || site.getUserId(), - timecreated: timeCreated, - }; - - return site.getDb().getRecord(AddonModForumOfflineProvider.DISCUSSIONS_TABLE, conditions).then((record) => { - record.options = this.textUtils.parseJSON(record.options); - - return record; - }); - }); - } - - /** - * Get all offline new discussions. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with discussions. - */ - getAllNewDiscussions(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModForumOfflineProvider.DISCUSSIONS_TABLE).then(this.parseRecordOptions.bind(this)); - }); - } - - /** - * Check if there are offline new discussions to send. - * - * @param forumId Forum ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User the discussions belong to. If not defined, current user in site. - * @return Promise resolved with boolean: true if has offline answers, false otherwise. - */ - hasNewDiscussions(forumId: number, siteId?: string, userId?: number): Promise { - return this.getNewDiscussions(forumId, siteId, userId).then((discussions) => { - return !!discussions.length; - }).catch(() => { - // No offline data found, return false. - return false; - }); - } - - /** - * Get new discussions to be synced. - * - * @param forumId Forum ID to get. - * @param siteId Site ID. If not defined, current site. - * @param userId User the discussions belong to. If not defined, current user in site. - * @return Promise resolved with the object to be synced. - */ - getNewDiscussions(forumId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - forumid: forumId, - userid: userId || site.getUserId(), - }; - - return site.getDb().getRecords(AddonModForumOfflineProvider.DISCUSSIONS_TABLE, conditions) - .then(this.parseRecordOptions.bind(this)); - }); - } - - /** - * Offline version for adding a new discussion to a forum. - * - * @param forumId Forum ID. - * @param name Forum name. - * @param courseId Course ID the forum belongs to. - * @param subject New discussion's subject. - * @param message New discussion's message. - * @param options Options (subscribe, pin, ...). - * @param groupId Group this discussion belongs to. - * @param timeCreated The time the discussion was created. If not defined, current time. - * @param siteId Site ID. If not defined, current site. - * @param userId User the discussion belong to. If not defined, current user in site. - * @return Promise resolved when new discussion is successfully saved. - */ - addNewDiscussion(forumId: number, name: string, courseId: number, subject: string, message: string, options?: any, - groupId?: number, timeCreated?: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - forumid: forumId, - name: name, - courseid: courseId, - subject: subject, - message: message, - options: JSON.stringify(options || {}), - groupid: groupId || AddonModForumProvider.ALL_PARTICIPANTS, - userid: userId || site.getUserId(), - timecreated: timeCreated || new Date().getTime() - }; - - return site.getDb().insertRecord(AddonModForumOfflineProvider.DISCUSSIONS_TABLE, data); - }); - } - - /** - * Delete forum offline replies. - * - * @param postId ID of the post being replied. - * @param siteId Site ID. If not defined, current site. - * @param userId User the reply belongs to. If not defined, current user in site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteReply(postId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - postid: postId, - userid: userId || site.getUserId(), - }; - - return site.getDb().deleteRecords(AddonModForumOfflineProvider.REPLIES_TABLE, conditions); - }); - } - - /** - * Get all offline replies. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with replies. - */ - getAllReplies(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModForumOfflineProvider.REPLIES_TABLE).then(this.parseRecordOptions.bind(this)); - }); - } - - /** - * Check if there is an offline reply for a forum to be synced. - * - * @param forumId ID of the forum being replied. - * @param siteId Site ID. If not defined, current site. - * @param userId User the replies belong to. If not defined, current user in site. - * @return Promise resolved with boolean: true if has offline answers, false otherwise. - */ - hasForumReplies(forumId: number, siteId?: string, userId?: number): Promise { - return this.getForumReplies(forumId, siteId, userId).then((replies) => { - return !!replies.length; - }).catch(() => { - // No offline data found, return false. - return false; - }); - } - - /** - * Get the replies of a forum to be synced. - * - * @param forumId ID of the forum being replied. - * @param siteId Site ID. If not defined, current site. - * @param userId User the replies belong to. If not defined, current user in site. - * @return Promise resolved with replies. - */ - getForumReplies(forumId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - forumid: forumId, - userid: userId || site.getUserId(), - }; - - return site.getDb().getRecords(AddonModForumOfflineProvider.REPLIES_TABLE, conditions) - .then(this.parseRecordOptions.bind(this)); - }); - } - - /** - * Check if there is an offline reply to be synced. - * - * @param discussionId ID of the discussion the user is replying to. - * @param siteId Site ID. If not defined, current site. - * @param userId User the replies belong to. If not defined, current user in site. - * @return Promise resolved with boolean: true if has offline answers, false otherwise. - */ - hasDiscussionReplies(discussionId: number, siteId?: string, userId?: number): Promise { - return this.getDiscussionReplies(discussionId, siteId, userId).then((replies) => { - return !!replies.length; - }).catch(() => { - // No offline data found, return false. - return false; - }); - } - - /** - * Get the replies of a discussion to be synced. - * - * @param discussionId ID of the discussion the user is replying to. - * @param siteId Site ID. If not defined, current site. - * @param userId User the replies belong to. If not defined, current user in site. - * @return Promise resolved with discussions. - */ - getDiscussionReplies(discussionId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - discussionid: discussionId, - userid: userId || site.getUserId(), - }; - - return site.getDb().getRecords(AddonModForumOfflineProvider.REPLIES_TABLE, conditions) - .then(this.parseRecordOptions.bind(this)); - }); - } - - /** - * Offline version for replying to a certain post. - * - * @param postId ID of the post being replied. - * @param discussionId ID of the discussion the user is replying to. - * @param forumId ID of the forum the user is replying to. - * @param name Forum name. - * @param courseId Course ID the forum belongs to. - * @param subject New post's subject. - * @param message New post's message. - * @param options Options (subscribe, attachments, ...). - * @param siteId Site ID. If not defined, current site. - * @param userId User the post belong to. If not defined, current user in site. - * @return Promise resolved when the post is created. - */ - replyPost(postId: number, discussionId: number, forumId: number, name: string, courseId: number, subject: string, - message: string, options?: any, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - postid: postId, - discussionid: discussionId, - forumid: forumId, - name: name, - courseid: courseId, - subject: subject, - message: message, - options: JSON.stringify(options || {}), - userid: userId || site.getUserId(), - timecreated: new Date().getTime() - }; - - return site.getDb().insertRecord(AddonModForumOfflineProvider.REPLIES_TABLE, data); - }); - } - - /** - * Get the path to the folder where to store files for offline attachments in a forum. - * - * @param forumId Forum ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getForumFolder(forumId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const siteFolderPath = this.fileProvider.getSiteFolder(site.getId()); - - return this.textUtils.concatenatePaths(siteFolderPath, 'offlineforum/' + forumId); - }); - } - - /** - * Get the path to the folder where to store files for a new offline discussion. - * - * @param forumId Forum ID. - * @param timeCreated The time the discussion was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getNewDiscussionFolder(forumId: number, timeCreated: number, siteId?: string): Promise { - return this.getForumFolder(forumId, siteId).then((folderPath) => { - return this.textUtils.concatenatePaths(folderPath, 'newdisc_' + timeCreated); - }); - } - - /** - * Get the path to the folder where to store files for a new offline reply. - * - * @param forumId Forum ID. - * @param postId ID of the post being replied. - * @param siteId Site ID. If not defined, current site. - * @param userId User the replies belong to. If not defined, current user in site. - * @return Promise resolved with the path. - */ - getReplyFolder(forumId: number, postId: number, siteId?: string, userId?: number): Promise { - return this.getForumFolder(forumId, siteId).then((folderPath) => { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return this.textUtils.concatenatePaths(folderPath, 'reply_' + postId + '_' + userId); - }); - }); - } - - /** - * Parse "options" column of fetched records. - * - * @param records List of records. - * @return List of records with options parsed. - */ - protected parseRecordOptions(records: any[]): any[] { - records.forEach((record) => { - record.options = this.textUtils.parseJSON(record.options); - }); - - return records; - } -} diff --git a/src/addon/mod/forum/providers/post-link-handler.ts b/src/addon/mod/forum/providers/post-link-handler.ts deleted file mode 100644 index afe072aea..000000000 --- a/src/addon/mod/forum/providers/post-link-handler.ts +++ /dev/null @@ -1,84 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Content links handler for forum new discussion. - * Match mod/forum/post.php?forum=6 with a valid data. - */ -@Injectable() -export class AddonModForumPostLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModForumPostLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModForum'; - pattern = /\/mod\/forum\/post\.php.*([\?\&](forum)=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private courseProvider: CoreCourseProvider, - private domUtils: CoreDomUtilsProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - forumId = parseInt(params.forum, 10); - - this.courseProvider.getModuleBasicInfoByInstance(forumId, 'forum', siteId).then((module) => { - const pageParams = { - courseId: module.course, - cmId: module.id, - forumId: module.instance, - timeCreated: 0, - }; - - return this.linkHelper.goInSite(navCtrl, 'AddonModForumNewDiscussionPage', pageParams, siteId); - }).finally(() => { - // Just in case. In fact we need to dismiss the modal before showing a toast or error message. - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return typeof params.forum != 'undefined'; - } -} diff --git a/src/addon/mod/forum/providers/prefetch-handler.ts b/src/addon/mod/forum/providers/prefetch-handler.ts deleted file mode 100644 index fd09775d1..000000000 --- a/src/addon/mod/forum/providers/prefetch-handler.ts +++ /dev/null @@ -1,339 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider, CoreCourseCommonModWSOptions } from '@core/course/providers/course'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { CoreGroupsProvider } from '@providers/groups'; -import { AddonModForumProvider } from './forum'; -import { AddonModForumSyncProvider } from './sync'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch forums. - */ -@Injectable() -export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModForum'; - modName = 'forum'; - component = AddonModForumProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^discussions$/; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - private userProvider: CoreUserProvider, - private groupsProvider: CoreGroupsProvider, - private forumProvider: AddonModForumProvider, - private syncProvider: AddonModForumSyncProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Get list of files. If not defined, we'll assume they're in module.contents. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the list of files. - */ - getFiles(module: any, courseId: number, single?: boolean): Promise { - return this.forumProvider.getForum(courseId, module.id).then((forum) => { - const files = this.getIntroFilesFromInstance(module, forum); - - // Get posts. - return this.getPostsForPrefetch(forum, {cmId: module.id}).then((posts) => { - // Add posts attachments and embedded files. - return files.concat(this.getPostsFiles(posts)); - }); - }).catch(() => { - // Forum not found, return empty list. - return []; - }); - } - - /** - * Given a list of forum posts, return a list with all the files (attachments and embedded files). - * - * @param posts Forum posts. - * @return Files. - */ - protected getPostsFiles(posts: any[]): any[] { - let files = []; - const getInlineFiles = this.sitesProvider.getCurrentSite() && - this.sitesProvider.getCurrentSite().isVersionGreaterEqualThan('3.2'); - - posts.forEach((post) => { - if (post.attachments && post.attachments.length) { - files = files.concat(post.attachments); - } - if (getInlineFiles && post.messageinlinefiles && post.messageinlinefiles.length) { - files = files.concat(post.messageinlinefiles); - } else if (post.message && !getInlineFiles) { - files = files.concat(this.filepoolProvider.extractDownloadableFilesFromHtmlAsFakeFileObjects(post.message)); - } - }); - - return files; - } - - /** - * Get the posts to be prefetched. - * - * @param forum Forum instance. - * @param options Other options. - * @return Promise resolved with array of posts. - */ - protected getPostsForPrefetch(forum: any, options: CoreCourseCommonModWSOptions = {}): Promise { - const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => { - // Get discussions in first 2 pages. - const discussionsOptions = { - sortOrder: sortOrder.value, - numPages: 2, - ...options, // Include all options. - }; - - return this.forumProvider.getDiscussionsInPages(forum.id, discussionsOptions).then((response) => { - if (response.error) { - return Promise.reject(null); - } - - const promises = []; - - response.discussions.forEach((discussion) => { - promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion, options)); - }); - - return Promise.all(promises); - }); - }); - - return Promise.all(promises).then((results) => { - // Each order has returned its own list of posts. Merge all the lists, preventing duplicates. - const posts = [], - postIds = {}; // To make the array unique. - - results.forEach((orderResults) => { - orderResults.forEach((orderResult) => { - orderResult.posts.forEach((post) => { - if (!postIds[post.id]) { - postIds[post.id] = true; - posts.push(post); - } - }); - }); - }); - - return posts; - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.forumProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status (usually, to check if module is downloadable). - * It doesn't need to invalidate check updates. It should NOT invalidate files nor all the prefetched data. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - // Invalidate forum data to recalculate unread message count badge. - const promises = []; - - promises.push(this.forumProvider.invalidateForumData(courseId)); - promises.push(this.courseProvider.invalidateModule(module.id)); - - return Promise.all(promises); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchForum.bind(this)); - } - - /** - * Prefetch a forum. - * - * @param module The module object returned by WS. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchForum(module: any, courseId: number, single: boolean, siteId: string): Promise { - const commonOptions = { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - const modOptions = { - cmId: module.id, - ...commonOptions, // Include all common options. - }; - - // Get the forum data. - return this.forumProvider.getForum(courseId, module.id, commonOptions).then((forum) => { - const promises = []; - - // Prefetch the posts. - promises.push(this.getPostsForPrefetch(forum, modOptions).then((posts) => { - const promises = []; - - const files = this.getIntroFilesFromInstance(module, forum).concat(this.getPostsFiles(posts)); - promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id)); - - // Prefetch groups data. - promises.push(this.prefetchGroupsInfo(forum, courseId, forum.cancreatediscussions, siteId)); - - // Prefetch avatars. - promises.push(this.userProvider.prefetchUserAvatars(posts, 'userpictureurl', siteId)); - - return Promise.all(promises); - })); - - // Prefetch access information. - promises.push(this.forumProvider.getAccessInformation(forum.id, modOptions)); - - // Prefetch sort order preference. - if (this.forumProvider.isDiscussionListSortingAvailable()) { - promises.push(this.userProvider.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER, siteId)); - } - - return Promise.all(promises); - }); - } - - /** - * Prefetch groups info for a forum. - * - * @param module The module object returned by WS. - * @param courseI Course ID the module belongs to. - * @param canCreateDiscussions Whether the user can create discussions in the forum. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when group data has been prefetched. - */ - protected prefetchGroupsInfo(forum: any, courseId: number, canCreateDiscussions: boolean, siteId?: string): any { - const options = { - cmId: forum.cmid, - siteId, - }; - - // Check group mode. - return this.groupsProvider.getActivityGroupMode(forum.cmid, siteId).then((mode) => { - if (mode !== CoreGroupsProvider.SEPARATEGROUPS && mode !== CoreGroupsProvider.VISIBLEGROUPS) { - // Activity doesn't use groups. Prefetch canAddDiscussionToAll to determine if user can pin/attach. - return this.forumProvider.canAddDiscussionToAll(forum.id, options).catch(() => { - // Ignore errors. - }); - } - - // Activity uses groups, prefetch allowed groups. - return this.groupsProvider.getActivityAllowedGroups(forum.cmid, undefined, siteId).then((result) => { - if (mode === CoreGroupsProvider.SEPARATEGROUPS) { - // Groups are already filtered by WS. Prefetch canAddDiscussionToAll to determine if user can pin/attach. - return this.forumProvider.canAddDiscussionToAll(forum.id, options).catch(() => { - // Ignore errors. - }); - } - - if (canCreateDiscussions) { - // Prefetch data to check the visible groups when creating discussions. - return this.forumProvider.canAddDiscussionToAll(forum.id, options).catch(() => { - // The call failed, let's assume he can't. - return { - status: false - }; - }).then((response) => { - if (response.status) { - // User can post to all groups, nothing else to prefetch. - return; - } - - // The user can't post to all groups, let's check which groups he can post to. - const groupPromises = []; - result.groups.forEach((group) => { - groupPromises.push(this.forumProvider.canAddDiscussion(forum.id, group.id, options).catch(() => { - // Ignore errors. - })); - }); - - return Promise.all(groupPromises); - }); - } - }); - }).catch((error) => { - // Ignore errors if cannot create discussions. - if (canCreateDiscussions) { - return Promise.reject(error); - } - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - const promises = []; - - promises.push(this.syncProvider.syncForumDiscussions(module.instance, undefined, siteId)); - promises.push(this.syncProvider.syncForumReplies(module.instance, undefined, siteId)); - promises.push(this.syncProvider.syncRatings(module.id, undefined, true, siteId)); - - return Promise.all(promises).then((results) => { - return results.reduce((a, b) => ({ - updated: a.updated || b.updated, - warnings: (a.warnings || []).concat(b.warnings || []), - }), {updated: false}); - }); - } -} diff --git a/src/addon/mod/forum/providers/push-click-handler.ts b/src/addon/mod/forum/providers/push-click-handler.ts deleted file mode 100644 index 5d3891d81..000000000 --- a/src/addon/mod/forum/providers/push-click-handler.ts +++ /dev/null @@ -1,71 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonModForumProvider } from './forum'; - -/** - * Handler for forum push notifications clicks. - */ -@Injectable() -export class AddonModForumPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonModForumPushClickHandler'; - priority = 200; - featureName = 'CoreCourseModuleDelegate_AddonModForum'; - - constructor(private utils: CoreUtilsProvider, private forumProvider: AddonModForumProvider, - private urlUtils: CoreUrlUtilsProvider, private linkHelper: CoreContentLinksHelperProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - return this.utils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_forum' && - notification.name == 'posts'; - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - handleClick(notification: any): Promise { - const contextUrlParams = this.urlUtils.extractUrlParams(notification.contexturl), - data = notification.customdata || {}, - pageParams: any = { - courseId: Number(notification.courseid), - discussionId: Number(contextUrlParams.d || data.discussionid), - cmId: Number(data.cmid), - forumId: Number(data.instance) - }; - - if (data.postid || contextUrlParams.urlHash) { - pageParams.postId = Number(data.postid || contextUrlParams.urlHash.replace('p', '')); - } - - return this.forumProvider.invalidateDiscussionPosts(pageParams.discussionId, undefined, notification.site).catch(() => { - // Ignore errors. - }).then(() => { - return this.linkHelper.goInSite(undefined, 'AddonModForumDiscussionPage', pageParams, notification.site); - }); - } -} diff --git a/src/addon/mod/forum/providers/sync-cron-handler.ts b/src/addon/mod/forum/providers/sync-cron-handler.ts deleted file mode 100644 index 440d29068..000000000 --- a/src/addon/mod/forum/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModForumSyncProvider } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModForumSyncCronHandler implements CoreCronHandler { - name = 'AddonModForumSyncCronHandler'; - - constructor(private forumSync: AddonModForumSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.forumSync.syncAllForums(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.forumSync.syncInterval; - } -} diff --git a/src/addon/mod/forum/providers/sync.ts b/src/addon/mod/forum/providers/sync.ts deleted file mode 100644 index 9ca5c5a18..000000000 --- a/src/addon/mod/forum/providers/sync.ts +++ /dev/null @@ -1,632 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSyncBaseProvider } from '@classes/base-sync'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreAppProvider } from '@providers/app'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModForumProvider } from './forum'; -import { AddonModForumHelperProvider } from './helper'; -import { AddonModForumOfflineProvider } from './offline'; -import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; - -/** - * Service to sync forums. - */ -@Injectable() -export class AddonModForumSyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_forum_autom_synced'; - static MANUAL_SYNCED = 'addon_mod_forum_manual_synced'; - - protected componentTranslate: string; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - courseProvider: CoreCourseProvider, - private eventsProvider: CoreEventsProvider, - private groupsProvider: CoreGroupsProvider, - loggerProvider: CoreLoggerProvider, - sitesProvider: CoreSitesProvider, - syncProvider: CoreSyncProvider, - textUtils: CoreTextUtilsProvider, - timeUtils: CoreTimeUtilsProvider, - private uploaderProvider: CoreFileUploaderProvider, - private utils: CoreUtilsProvider, - private forumProvider: AddonModForumProvider, - private forumHelper: AddonModForumHelperProvider, - private forumOffline: AddonModForumOfflineProvider, - private logHelper: CoreCourseLogHelperProvider, - private ratingSync: CoreRatingSyncProvider) { - - super('AddonModForumSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils); - - this.componentTranslate = courseProvider.translateModuleName('forum'); - } - - /** - * Try to synchronize all the forums in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllForums(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all forums', this.syncAllForumsFunc.bind(this), [force], siteId); - } - - /** - * Sync all forums on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllForumsFunc(siteId: string, force?: boolean): Promise { - const sitePromises = []; - - // Sync all new discussions. - sitePromises.push(this.forumOffline.getAllNewDiscussions(siteId).then((discussions) => { - const promises = {}; - - // Do not sync same forum twice. - discussions.forEach((discussion) => { - if (typeof promises[discussion.forumid] != 'undefined') { - return; - } - - promises[discussion.forumid] = force ? this.syncForumDiscussions(discussion.forumid, discussion.userid, siteId) : - this.syncForumDiscussionsIfNeeded(discussion.forumid, discussion.userid, siteId); - - promises[discussion.forumid].then((result) => { - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModForumSyncProvider.AUTO_SYNCED, { - forumId: discussion.forumid, - userId: discussion.userid, - warnings: result.warnings - }, siteId); - } - }); - }); - - return Promise.all(this.utils.objectToArray(promises)); - })); - - // Sync all discussion replies. - sitePromises.push(this.forumOffline.getAllReplies(siteId).then((replies) => { - const promises = {}; - - // Do not sync same discussion twice. - replies.forEach((reply) => { - if (typeof promises[reply.discussionid] != 'undefined') { - return; - } - - promises[reply.discussionid] = force ? this.syncDiscussionReplies(reply.discussionid, reply.userid, siteId) : - this.syncDiscussionRepliesIfNeeded(reply.discussionid, reply.userid, siteId); - - promises[reply.discussionid].then((result) => { - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModForumSyncProvider.AUTO_SYNCED, { - forumId: reply.forumid, - discussionId: reply.discussionid, - userId: reply.userid, - warnings: result.warnings - }, siteId); - } - }); - }); - - return Promise.all(this.utils.objectToArray(promises)); - })); - - sitePromises.push(this.syncRatings(undefined, undefined, force, siteId)); - - return Promise.all(sitePromises); - } - - /** - * Sync a forum only if a certain time has passed since the last time. - * - * @param forumId Forum ID. - * @param userId User the discussion belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the forum is synced or if it doesn't need to be synced. - */ - syncForumDiscussionsIfNeeded(forumId: number, userId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = this.getForumSyncId(forumId, userId); - - return this.isSyncNeeded(syncId, siteId).then((needed) => { - if (needed) { - return this.syncForumDiscussions(forumId, userId, siteId); - } - }); - } - - /** - * Synchronize all offline discussions of a forum. - * - * @param forumId Forum ID to be synced. - * @param userId User the discussions belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncForumDiscussions(forumId: number, userId?: number, siteId?: string): Promise { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = this.getForumSyncId(forumId, userId); - - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this discussion, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - // Verify that forum isn't blocked. - if (this.syncProvider.isBlocked(AddonModForumProvider.COMPONENT, syncId, siteId)) { - this.logger.debug('Cannot sync forum ' + forumId + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug('Try to sync forum ' + forumId + ' for user ' + userId); - - const result = { - warnings: [], - updated: false - }; - - // Sync offline logs. - const syncPromise = this.logHelper.syncIfNeeded(AddonModForumProvider.COMPONENT, forumId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Get offline responses to be sent. - return this.forumOffline.getNewDiscussions(forumId, siteId, userId).catch(() => { - // No offline data found, return empty object. - return []; - }); - }).then((discussions) => { - if (!discussions.length) { - // Nothing to sync. - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - const promises = []; - - discussions.forEach((data) => { - let groupsPromise; - if (data.groupid == AddonModForumProvider.ALL_GROUPS) { - // Fetch all group ids. - groupsPromise = this.forumProvider.getForumById(data.courseid, data.forumid, {siteId}).then((forum) => { - return this.groupsProvider.getActivityAllowedGroups(forum.cmid).then((result) => { - return result.groups.map((group) => group.id); - }); - }); - } else { - groupsPromise = Promise.resolve([data.groupid]); - } - - promises.push(groupsPromise.then((groupIds) => { - const errors = []; - - return Promise.all(groupIds.map((groupId) => { - // First of all upload the attachments (if any). - return this.uploadAttachments(forumId, data, true, siteId, userId).then((itemId) => { - // Now try to add the discussion. - const options = this.utils.clone(data.options || {}); - options.attachmentsid = itemId; - - return this.forumProvider.addNewDiscussionOnline(forumId, data.subject, data.message, options, - groupId, siteId); - }).catch((error) => { - errors.push(error); - }); - })).then(() => { - if (errors.length == groupIds.length) { - // All requests have failed, reject if errors were not returned by WS. - for (let i = 0; i < errors.length; i++) { - if (!this.utils.isWebServiceError(errors[i])) { - return Promise.reject(errors[i]); - } - } - } - - // All requests succeeded, some failed or all failed with a WS error. - result.updated = true; - - return this.deleteNewDiscussion(forumId, data.timecreated, siteId, userId).then(() => { - if (errors.length == groupIds.length) { - // All requests failed with WS error. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.name, - error: this.textUtils.getErrorMessageFromError(errors[0]) - })); - } - }); - }); - })); - }); - - return Promise.all(promises); - }).then(() => { - if (result.updated) { - // Data has been sent to server. Now invalidate the WS calls. - const promises = [ - this.forumProvider.invalidateDiscussionsList(forumId, siteId), - this.forumProvider.invalidateCanAddDiscussion(forumId, siteId), - ]; - - return Promise.all(promises).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(syncId, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // All done, return the warnings. - return result; - }); - - return this.addOngoingSync(syncId, syncPromise, siteId); - } - - /** - * Synchronize forum offline ratings. - * - * @param cmId Course module to be synced. If not defined, sync all forums. - * @param discussionId Discussion id to be synced. If not defined, sync all discussions. - * @param force Wether to force sync not depending on last execution. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncRatings(cmId?: number, discussionId?: number, force?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.ratingSync.syncRatings('mod_forum', 'post', 'module', cmId, discussionId, force, siteId).then((results) => { - let updated = false; - const warnings = []; - const promises = []; - - results.forEach((result) => { - if (result.updated.length) { - updated = true; - - // Invalidate discussions of updated ratings. - promises.push(this.forumProvider.invalidateDiscussionPosts(result.itemSet.itemSetId, undefined, siteId)); - } - if (result.warnings.length) { - // Fetch forum to construct the warning message. - promises.push(this.forumProvider.getForum(result.itemSet.courseId, result.itemSet.instanceId, {siteId}) - .then((forum) => { - result.warnings.forEach((warning) => { - warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: forum.name, - error: warning - })); - }); - })); - } - }); - - return this.utils.allPromises(promises).then(() => { - return { updated, warnings }; - }); - }); - } - - /** - * Synchronize all offline discussion replies of a forum. - * - * @param forumId Forum ID to be synced. - * @param userId User the discussions belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncForumReplies(forumId: number, userId?: number, siteId?: string): Promise { - // Get offline forum replies to be sent. - return this.forumOffline.getForumReplies(forumId, siteId, userId).catch(() => { - // No offline data found, return empty list. - return []; - }).then((replies) => { - if (!replies.length) { - // Nothing to sync. - return { warnings: [], updated: false }; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - const promises = {}; - - // Do not sync same discussion twice. - replies.forEach((reply) => { - if (typeof promises[reply.discussionid] != 'undefined') { - return; - } - promises[reply.discussionid] = this.syncDiscussionReplies(reply.discussionid, userId, siteId); - }); - - return Promise.all(this.utils.objectToArray(promises)).then((results) => { - return results.reduce((a, b) => ({ - warnings: a.warnings.concat(b.warnings), - updated: a.updated || b.updated, - }), { warnings: [], updated: false }); - }); - }); - } - - /** - * Sync a forum discussion replies only if a certain time has passed since the last time. - * - * @param discussionId Discussion ID to be synced. - * @param userId User the posts belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the forum discussion is synced or if it doesn't need to be synced. - */ - syncDiscussionRepliesIfNeeded(discussionId: number, userId?: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = this.getDiscussionSyncId(discussionId, userId); - - return this.isSyncNeeded(syncId, siteId).then((needed) => { - if (needed) { - return this.syncDiscussionReplies(discussionId, userId, siteId); - } - }); - } - - /** - * Synchronize all offline replies from a discussion. - * - * @param discussionId Discussion ID to be synced. - * @param userId User the posts belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncDiscussionReplies(discussionId: number, userId?: number, siteId?: string): Promise { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = this.getDiscussionSyncId(discussionId, userId); - - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this discussion, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - // Verify that forum isn't blocked. - if (this.syncProvider.isBlocked(AddonModForumProvider.COMPONENT, syncId, siteId)) { - this.logger.debug('Cannot sync forum discussion ' + discussionId + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug('Try to sync forum discussion ' + discussionId + ' for user ' + userId); - - let forumId; - const result = { - warnings: [], - updated: false - }; - - // Get offline responses to be sent. - const syncPromise = this.forumOffline.getDiscussionReplies(discussionId, siteId, userId).catch(() => { - // No offline data found, return empty object. - return []; - }).then((replies) => { - if (!replies.length) { - // Nothing to sync. - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - const promises = []; - - replies.forEach((data) => { - forumId = data.forumid; - data.options = data.options || {}; - - // First of all upload the attachments (if any). - const promise = this.uploadAttachments(forumId, data, false, siteId, userId).then((itemId) => { - // Now try to send the reply. - data.options.attachmentsid = itemId; - - return this.forumProvider.replyPostOnline(data.postid, data.subject, data.message, data.options, siteId); - }); - - promises.push(promise.then(() => { - result.updated = true; - - return this.deleteReply(forumId, data.postid, siteId, userId); - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. Delete them. - result.updated = true; - - return this.deleteReply(forumId, data.postid, siteId, userId).then(() => { - // Responses deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - })); - }); - - return Promise.all(promises); - }).then(() => { - // Data has been sent to server. Now invalidate the WS calls. - const promises = []; - if (forumId) { - promises.push(this.forumProvider.invalidateDiscussionsList(forumId, siteId)); - } - promises.push(this.forumProvider.invalidateDiscussionPosts(discussionId, forumId, siteId)); - - return this.utils.allPromises(promises).catch(() => { - // Ignore errors. - }); - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(syncId, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // All done, return the warnings. - return result; - }); - - return this.addOngoingSync(syncId, syncPromise, siteId); - } - - /** - * Delete a new discussion. - * - * @param forumId Forum ID the discussion belongs to. - * @param timecreated The timecreated of the discussion. - * @param siteId Site ID. If not defined, current site. - * @param userId User the discussion belongs to. If not defined, current user in site. - * @return Promise resolved when deleted. - */ - protected deleteNewDiscussion(forumId: number, timecreated: number, siteId?: string, userId?: number): Promise { - const promises = []; - - promises.push(this.forumOffline.deleteNewDiscussion(forumId, timecreated, siteId, userId)); - promises.push(this.forumHelper.deleteNewDiscussionStoredFiles(forumId, timecreated, siteId).catch(() => { - // Ignore errors, maybe there are no files. - })); - - return Promise.all(promises); - } - - /** - * Delete a new discussion. - * - * @param forumId Forum ID the discussion belongs to. - * @param postId ID of the post being replied. - * @param siteId Site ID. If not defined, current site. - * @param userId User the discussion belongs to. If not defined, current user in site. - * @return Promise resolved when deleted. - */ - protected deleteReply(forumId: number, postId: number, siteId?: string, userId?: number): Promise { - const promises = []; - - promises.push(this.forumOffline.deleteReply(postId, siteId, userId)); - promises.push(this.forumHelper.deleteReplyStoredFiles(forumId, postId, siteId, userId).catch(() => { - // Ignore errors, maybe there are no files. - })); - - return Promise.all(promises); - } - - /** - * Upload attachments of an offline post/discussion. - * - * @param forumId Forum ID the post belongs to. - * @param post Offline post or discussion. - * @param isDisc True if it's a new discussion, false if it's a reply. - * @param siteId Site ID. If not defined, current site. - * @param userId User the reply belongs to. If not defined, current user in site. - * @return Promise resolved with draftid if uploaded, resolved with undefined if nothing to upload. - */ - protected uploadAttachments(forumId: number, post: any, isDisc: boolean, siteId?: string, userId?: number): Promise { - const attachments = post && post.options && post.options.attachmentsid; - - if (attachments) { - // Has some attachments to sync. - let files = attachments.online || []; - let promise; - - if (attachments.offline) { - // Has offline files. - if (isDisc) { - promise = this.forumHelper.getNewDiscussionStoredFiles(forumId, post.timecreated, siteId); - } else { - promise = this.forumHelper.getReplyStoredFiles(forumId, post.postid, siteId, userId); - } - - promise.then((atts) => { - files = files.concat(atts); - }).catch(() => { - // Folder not found, no files to add. - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - return this.uploaderProvider.uploadOrReuploadFiles(files, AddonModForumProvider.COMPONENT, forumId, siteId); - }); - } - - // No attachments, resolve. - return Promise.resolve(); - } - - /** - * Get the ID of a forum sync. - * - * @param forumId Forum ID. - * @param userId User the responses belong to.. If not defined, current user. - * @return Sync ID. - */ - getForumSyncId(forumId: number, userId?: number): string { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - - return 'forum#' + forumId + '#' + userId; - } - - /** - * Get the ID of a discussion sync. - * - * @param discussionId Discussion ID. - * @param userId User the responses belong to.. If not defined, current user. - * @return Sync ID. - */ - getDiscussionSyncId(discussionId: number, userId?: number): string { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - - return 'discussion#' + discussionId + '#' + userId; - } -} diff --git a/src/addon/mod/forum/providers/tag-area-handler.ts b/src/addon/mod/forum/providers/tag-area-handler.ts deleted file mode 100644 index abeb5c680..000000000 --- a/src/addon/mod/forum/providers/tag-area-handler.ts +++ /dev/null @@ -1,57 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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'; - -/** - * Handler to support tags. - */ -@Injectable() -export class AddonModForumTagAreaHandler implements CoreTagAreaHandler { - name = 'AddonModForumTagAreaHandler'; - type = 'mod_forum/forum_posts'; - - constructor(private tagHelper: CoreTagHelperProvider) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Parses the rendered content of a tag index and returns the items. - * - * @param content Rendered content. - * @return Area items (or promise resolved with the items). - */ - parseContent(content: string): any[] | Promise { - return this.tagHelper.parseFeedContent(content); - } - - /** - * Get the component to use to display items. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return CoreTagFeedComponent; - } -} diff --git a/src/addon/mod/glossary/components/components.module.ts b/src/addon/mod/glossary/components/components.module.ts deleted file mode 100644 index ca3ffdcce..000000000 --- a/src/addon/mod/glossary/components/components.module.ts +++ /dev/null @@ -1,53 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { CoreSearchComponentsModule } from '@core/search/components/components.module'; -import { AddonModGlossaryIndexComponent } from './index/index'; -import { AddonModGlossaryModePickerPopoverComponent } from './mode-picker/mode-picker'; - -@NgModule({ - declarations: [ - AddonModGlossaryIndexComponent, - AddonModGlossaryModePickerPopoverComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreCourseComponentsModule, - CoreSearchComponentsModule, - ], - providers: [ - ], - exports: [ - AddonModGlossaryIndexComponent, - AddonModGlossaryModePickerPopoverComponent - ], - entryComponents: [ - AddonModGlossaryIndexComponent, - AddonModGlossaryModePickerPopoverComponent - ] -}) -export class AddonModGlossaryComponentsModule {} diff --git a/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html deleted file mode 100644 index 475d5e969..000000000 --- a/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ 'core.hasdatatosync' | translate:{$a: moduleName} }} - - - - - - - - - {{ 'addon.mod_glossary.entriestobesynced' | translate }} - - -

-
-
- - - - - - {{getDivider(entry)}} - - - -

-
-
-
- - - - -
- - - - -
-
diff --git a/src/addon/mod/glossary/components/index/index.ts b/src/addon/mod/glossary/components/index/index.ts deleted file mode 100644 index 3b759ea5e..000000000 --- a/src/addon/mod/glossary/components/index/index.ts +++ /dev/null @@ -1,479 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Injector, ViewChild } from '@angular/core'; -import { Content, PopoverController } from 'ionic-angular'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreRatingProvider } from '@core/rating/providers/rating'; -import { CoreRatingOfflineProvider } from '@core/rating/providers/offline'; -import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; -import { AddonModGlossaryProvider } from '../../providers/glossary'; -import { AddonModGlossaryOfflineProvider } from '../../providers/offline'; -import { AddonModGlossarySyncProvider } from '../../providers/sync'; -import { AddonModGlossaryModePickerPopoverComponent } from '../mode-picker/mode-picker'; -import { AddonModGlossaryPrefetchHandler } from '../../providers/prefetch-handler'; - -type FetchMode = 'author_all' | 'cat_all' | 'newest_first' | 'recently_updated' | 'letter_all'; - -/** - * Component that displays a glossary entry page. - */ -@Component({ - selector: 'addon-mod-glossary-index', - templateUrl: 'addon-mod-glossary-index.html', -}) -export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivityComponent { - @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - @ViewChild(Content) content: Content; - - component = AddonModGlossaryProvider.COMPONENT; - moduleName = 'glossary'; - - isSearch = false; - entries = []; - offlineEntries = []; - canAdd = false; - canLoadMore = false; - loadMoreError = false; - loadingMessage = this.translate.instant('core.loading'); - selectedEntry: number; - - protected syncEventName = AddonModGlossarySyncProvider.AUTO_SYNCED; - protected glossary: any; - protected fetchFunction: Function; - protected fetchInvalidate: Function; - protected fetchArguments: any[]; - protected showDivider: (entry: any, previous?: any) => boolean; - protected getDivider: (entry: any) => string; - protected addEntryObserver: any; - protected fetchMode: FetchMode; - protected viewMode: string; - protected fetchedEntriesCanLoadMore = false; - protected fetchedEntries = []; - - hasOfflineRatings: boolean; - protected ratingOfflineObserver: any; - protected ratingSyncObserver: any; - - constructor(injector: Injector, - private popoverCtrl: PopoverController, - private glossaryProvider: AddonModGlossaryProvider, - private glossaryOffline: AddonModGlossaryOfflineProvider, - private prefetchHandler: AddonModGlossaryPrefetchHandler, - private ratingOffline: CoreRatingOfflineProvider) { - super(injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - // When an entry is added, we reload the data. - this.addEntryObserver = this.eventsProvider.on(AddonModGlossaryProvider.ADD_ENTRY_EVENT, this.eventReceived.bind(this)); - - // Listen for offline ratings saved and synced. - this.ratingOfflineObserver = this.eventsProvider.on(CoreRatingProvider.RATING_SAVED_EVENT, (data) => { - if (this.glossary && data.component == 'mod_glossary' && data.ratingArea == 'entry' && data.contextLevel == 'module' - && data.instanceId == this.glossary.coursemodule) { - this.hasOfflineRatings = true; - } - }); - this.ratingSyncObserver = this.eventsProvider.on(CoreRatingSyncProvider.SYNCED_EVENT, (data) => { - if (this.glossary && data.component == 'mod_glossary' && data.ratingArea == 'entry' && data.contextLevel == 'module' - && data.instanceId == this.glossary.coursemodule) { - this.hasOfflineRatings = false; - } - }); - - this.loadContent(false, true).then(() => { - if (!this.glossary) { - return; - } - - if (this.splitviewCtrl.isOn()) { - // Load the first entry. - if (this.entries.length > 0) { - this.openEntry(this.entries[0].id); - } - } - - this.glossaryProvider.logView(this.glossary.id, this.viewMode, this.glossary.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch((error) => { - // Ignore errors. - }); - }); - } - - /** - * Download the component contents. - * - * @param refresh Whether we're refreshing data. - * @param sync If the refresh needs syncing. - * @param showErrors Wether to show errors to the user or hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - return this.glossaryProvider.getGlossary(this.courseId, this.module.id).then((glossary) => { - this.glossary = glossary; - - this.description = glossary.intro || this.description; - this.canAdd = (this.glossaryProvider.isPluginEnabledForEditing() && glossary.canaddentry) || false; - - this.dataRetrieved.emit(this.glossary); - - if (!this.fetchMode) { - this.switchMode('letter_all'); - } - - if (sync) { - // Try to synchronize the glossary. - return this.syncActivity(showErrors); - } - }).then(() => { - const promises = []; - - promises.push(this.fetchEntries().then(() => { - // Check if there are responses stored in offline. - return this.glossaryOffline.getGlossaryNewEntries(this.glossary.id).then((offlineEntries) => { - offlineEntries.sort((a, b) => a.concept.localeCompare(b.fullname)); - this.hasOffline = !!offlineEntries.length; - this.offlineEntries = offlineEntries || []; - }); - })); - - promises.push(this.ratingOffline.hasRatings('mod_glossary', 'entry', 'module', this.glossary.coursemodule) - .then((hasRatings) => { - this.hasOfflineRatings = hasRatings; - })); - - return Promise.all(promises); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Convenience function to fetch entries. - * - * @param append True if fetched entries are appended to exsiting ones. - * @return Promise resolved when done. - */ - protected fetchEntries(append: boolean = false): Promise { - this.loadMoreError = false; - - if (!this.fetchFunction || !this.fetchArguments) { - // This happens in search mode with an empty query. - return Promise.resolve({entries: [], count: 0}); - } - - return this.glossaryProvider.fetchEntries(this.fetchFunction, this.fetchArguments, { - from: append ? this.entries.length : 0, - cmId: this.module.id, - }).then((result) => { - if (append) { - Array.prototype.push.apply(this.entries, result.entries); - } else { - this.entries = result.entries; - - if (this.splitviewCtrl.isOn()) { - // Load the first entry. - if (this.entries.length > 0) { - const found = this.selectedEntry && this.entries.some((entry) => entry.id == this.selectedEntry); - - // The current selected entry is not found in the current list, open first item. - if (!found) { - this.openEntry(this.entries[0].id); - } - } else { - this.selectedEntry = null; - this.splitviewCtrl.emptyDetails(); - } - } - } - this.canLoadMore = this.entries.length < result.count; - }).catch((error) => { - return Promise.reject(error); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - if (this.fetchInvalidate && this.fetchArguments) { - promises.push(this.fetchInvalidate.apply(this.glossaryProvider, this.fetchArguments)); - } - - promises.push(this.glossaryProvider.invalidateCourseGlossaries(this.courseId)); - - if (this.glossary && this.glossary.id) { - promises.push(this.glossaryProvider.invalidateCategories(this.glossary.id)); - } - - return Promise.all(promises); - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.prefetchHandler.sync(this.module, this.courseId); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return Whether it succeed or not. - */ - protected hasSyncSucceed(result: any): boolean { - return result.updated; - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - return this.glossary && syncEventData.glossaryId == this.glossary.id && - syncEventData.userId == this.sitesProvider.getCurrentSiteUserId(); - } - - /** - * Change fetch mode. - * - * @param mode New mode. - */ - protected switchMode(mode: FetchMode): void { - this.fetchMode = mode; - this.isSearch = false; - - switch (mode) { - case 'author_all': - // Browse by author. - this.viewMode = 'author'; - this.fetchFunction = this.glossaryProvider.getEntriesByAuthor; - this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByAuthor; - this.fetchArguments = [this.glossary.id, 'ALL', 'LASTNAME', 'ASC']; - this.getDivider = (entry: any): string => entry.userfullname; - this.showDivider = (entry: any, previous?: any): boolean => { - return typeof previous === 'undefined' || entry.userid != previous.userid; - }; - break; - case 'cat_all': - // Browse by category. - this.viewMode = 'cat'; - this.fetchFunction = this.glossaryProvider.getEntriesByCategory; - this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByCategory; - this.fetchArguments = [this.glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATEGORIES]; - this.getDivider = (entry: any): string => entry.categoryname; - this.showDivider = (entry?: any, previous?: any): boolean => { - return !previous || this.getDivider(entry) != this.getDivider(previous); - }; - break; - case 'newest_first': - // Newest first. - this.viewMode = 'date'; - this.fetchFunction = this.glossaryProvider.getEntriesByDate; - this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByDate; - this.fetchArguments = [this.glossary.id, 'CREATION', 'DESC']; - this.getDivider = null; - this.showDivider = (): boolean => false; - break; - case 'recently_updated': - // Recently updated. - this.viewMode = 'date'; - this.fetchFunction = this.glossaryProvider.getEntriesByDate; - this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByDate; - this.fetchArguments = [this.glossary.id, 'UPDATE', 'DESC']; - this.getDivider = null; - this.showDivider = (): boolean => false; - break; - case 'letter_all': - default: - // Consider it is 'letter_all'. - this.viewMode = 'letter'; - this.fetchMode = 'letter_all'; - this.fetchFunction = this.glossaryProvider.getEntriesByLetter; - this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByLetter; - this.fetchArguments = [this.glossary.id, 'ALL']; - this.getDivider = (entry: any): string => { - // Try to get the first letter without HTML tags. - const noTags = this.textUtils.cleanTags(entry.concept); - - return (noTags || entry.concept).substr(0, 1).toUpperCase(); - }; - this.showDivider = (entry?: any, previous?: any): boolean => { - return !previous || this.getDivider(entry) != this.getDivider(previous); - }; - break; - } - } - - /** - * Convenience function to load more forum discussions. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - * @return Promise resolved when done. - */ - loadMoreEntries(infiniteComplete?: any): Promise { - return this.fetchEntries(true).catch((error) => { - this.loadMoreError = true; - this.domUtils.showErrorModalDefault(error, 'addon.mod_glossary.errorloadingentries', true); - }).finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Show the mode picker menu. - * - * @param event Event. - */ - openModePicker(event: MouseEvent): void { - const popover = this.popoverCtrl.create(AddonModGlossaryModePickerPopoverComponent, { - browsemodes: this.glossary.browsemodes, - selectedMode: this.isSearch ? '' : this.fetchMode - }); - - popover.onDidDismiss((mode: FetchMode) => { - if (mode !== this.fetchMode) { - this.changeFetchMode(mode); - } else if (this.isSearch) { - this.toggleSearch(); - } - }); - - popover.present({ - ev: event - }); - } - - /** - * Toggles between search and fetch mode. - */ - toggleSearch(): void { - if (this.isSearch) { - this.isSearch = false; - this.entries = this.fetchedEntries; - this.canLoadMore = this.fetchedEntriesCanLoadMore; - this.switchMode(this.fetchMode); - } else { - // Search for entries. - this.fetchFunction = this.glossaryProvider.getEntriesBySearch; - this.fetchInvalidate = this.glossaryProvider.invalidateEntriesBySearch; - this.fetchArguments = null; // Dynamically set later. - this.getDivider = null; - this.showDivider = (): boolean => false; - this.isSearch = true; - - this.fetchedEntries = this.entries; - this.fetchedEntriesCanLoadMore = this.canLoadMore; - this.canLoadMore = false; - this.entries = []; - } - } - - /** - * Change fetch mode - * @param {FetchMode} mode [description] - */ - changeFetchMode(mode: FetchMode): void { - this.isSearch = false; - this.loadingMessage = this.translate.instant('core.loading'); - this.domUtils.scrollToTop(this.content); - this.switchMode(mode); - this.loaded = false; - this.loadContent(); - } - - /** - * Opens an entry. - * - * @param entryId Entry id. - */ - openEntry(entryId: number): void { - const params = { - courseId: this.courseId, - entryId: entryId - }; - this.splitviewCtrl.push('AddonModGlossaryEntryPage', params); - this.selectedEntry = entryId; - } - - /** - * Opens new entry editor. - * - * @param entry Offline entry to edit. - */ - openNewEntry(entry?: any): void { - const params = { - courseId: this.courseId, - module: this.module, - glossary: this.glossary, - entry: entry, - }; - this.splitviewCtrl.getMasterNav().push('AddonModGlossaryEditPage', params); - this.selectedEntry = 0; - } - - /** - * Search entries. - * - * @param query Text entered on the search box. - */ - search(query: string): void { - this.loadingMessage = this.translate.instant('core.searching'); - this.fetchArguments = [this.glossary.id, query, 1, 'CONCEPT', 'ASC']; - this.loaded = false; - this.loadContent(); - } - - /** - * Function called when we receive an event of new entry. - * - * @param data Event data. - */ - protected eventReceived(data: any): void { - if (this.glossary && this.glossary.id === data.glossaryId) { - this.showLoadingAndRefresh(false); - - // Check completion since it could be configured to complete once the user adds a new discussion or replies. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - - this.addEntryObserver && this.addEntryObserver.off(); - this.ratingOfflineObserver && this.ratingOfflineObserver.off(); - this.ratingSyncObserver && this.ratingSyncObserver.off(); - } -} diff --git a/src/addon/mod/glossary/components/mode-picker/addon-mod-glossary-mode-picker.html b/src/addon/mod/glossary/components/mode-picker/addon-mod-glossary-mode-picker.html deleted file mode 100644 index df7d9e525..000000000 --- a/src/addon/mod/glossary/components/mode-picker/addon-mod-glossary-mode-picker.html +++ /dev/null @@ -1,6 +0,0 @@ - - - {{mode.langkey | translate}} - - - diff --git a/src/addon/mod/glossary/components/mode-picker/mode-picker.ts b/src/addon/mod/glossary/components/mode-picker/mode-picker.ts deleted file mode 100644 index baac2a4f4..000000000 --- a/src/addon/mod/glossary/components/mode-picker/mode-picker.ts +++ /dev/null @@ -1,65 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { NavParams, ViewController } from 'ionic-angular'; - -/** - * Component to display the mode picker. - */ -@Component({ - selector: 'addon-mod-glossary-mode-picker-popover', - templateUrl: 'addon-mod-glossary-mode-picker.html' -}) -export class AddonModGlossaryModePickerPopoverComponent { - modes = []; - selectedMode: string; - - constructor(navParams: NavParams, private viewCtrl: ViewController) { - this.selectedMode = navParams.get('selectedMode') || ''; - const browsemodes = navParams.get('browsemodes'); - - browsemodes.forEach((mode) => { - switch (mode) { - case 'letter' : - this.modes.push({key: 'letter_all', langkey: 'addon.mod_glossary.byalphabet'}); - break; - case 'cat' : - this.modes.push({key: 'cat_all', langkey: 'addon.mod_glossary.bycategory'}); - break; - case 'date' : - this.modes.push({key: 'newest_first', langkey: 'addon.mod_glossary.bynewestfirst'}); - this.modes.push({key: 'recently_updated', langkey: 'addon.mod_glossary.byrecentlyupdated'}); - break; - case 'author' : - this.modes.push({key: 'author_all', langkey: 'addon.mod_glossary.byauthor'}); - break; - default: - } - }); - } - - /** - * Function called when a mode is clicked. - * - * @param event Click event. - * @param key Clicked mode key. - * @return Return true if success, false if error. - */ - modePicked(event: Event, key: string): boolean { - this.viewCtrl.dismiss(key); - - return true; - } -} diff --git a/src/addon/mod/glossary/glossary.module.ts b/src/addon/mod/glossary/glossary.module.ts deleted file mode 100644 index 06ba0a136..000000000 --- a/src/addon/mod/glossary/glossary.module.ts +++ /dev/null @@ -1,82 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate'; -import { AddonModGlossaryProvider } from './providers/glossary'; -import { AddonModGlossaryOfflineProvider } from './providers/offline'; -import { AddonModGlossaryHelperProvider } from './providers/helper'; -import { AddonModGlossarySyncProvider } from './providers/sync'; -import { AddonModGlossaryModuleHandler } from './providers/module-handler'; -import { AddonModGlossaryPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModGlossarySyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModGlossaryIndexLinkHandler } from './providers/index-link-handler'; -import { AddonModGlossaryEntryLinkHandler } from './providers/entry-link-handler'; -import { AddonModGlossaryListLinkHandler } from './providers/list-link-handler'; -import { AddonModGlossaryEditLinkHandler } from './providers/edit-link-handler'; -import { AddonModGlossaryTagAreaHandler } from './providers/tag-area-handler'; -import { AddonModGlossaryComponentsModule } from './components/components.module'; - -// List of providers (without handlers). -export const ADDON_MOD_GLOSSARY_PROVIDERS: any[] = [ - AddonModGlossaryProvider, - AddonModGlossaryOfflineProvider, - AddonModGlossaryHelperProvider, - AddonModGlossarySyncProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModGlossaryComponentsModule, - ], - providers: [ - AddonModGlossaryProvider, - AddonModGlossaryOfflineProvider, - AddonModGlossaryHelperProvider, - AddonModGlossarySyncProvider, - AddonModGlossaryModuleHandler, - AddonModGlossaryPrefetchHandler, - AddonModGlossarySyncCronHandler, - AddonModGlossaryIndexLinkHandler, - AddonModGlossaryEntryLinkHandler, - AddonModGlossaryListLinkHandler, - AddonModGlossaryEditLinkHandler, - AddonModGlossaryTagAreaHandler - ] -}) -export class AddonModGlossaryModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModGlossaryModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModGlossaryPrefetchHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModGlossarySyncCronHandler, linksDelegate: CoreContentLinksDelegate, - indexHandler: AddonModGlossaryIndexLinkHandler, discussionHandler: AddonModGlossaryEntryLinkHandler, - listLinkHandler: AddonModGlossaryListLinkHandler, - editLinkHandler: AddonModGlossaryEditLinkHandler, tagAreaDelegate: CoreTagAreaDelegate, - tagAreaHandler: AddonModGlossaryTagAreaHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - cronDelegate.register(syncHandler); - linksDelegate.registerHandler(indexHandler); - linksDelegate.registerHandler(discussionHandler); - linksDelegate.registerHandler(listLinkHandler); - linksDelegate.registerHandler(editLinkHandler); - tagAreaDelegate.registerHandler(tagAreaHandler); - } -} diff --git a/src/addon/mod/glossary/lang/en.json b/src/addon/mod/glossary/lang/en.json deleted file mode 100644 index ba4329f33..000000000 --- a/src/addon/mod/glossary/lang/en.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "addentry": "Add a new entry", - "aliases": "Keyword(s)", - "attachment": "Attachment", - "browsemode": "Browse entries", - "byalphabet": "Alphabetically", - "byauthor": "Group by author", - "bycategory": "Group by category", - "bynewestfirst": "Newest first", - "byrecentlyupdated": "Recently updated", - "bysearch": "Search", - "cannoteditentry": "Cannot edit entry", - "casesensitive": "This entry is case sensitive", - "categories": "Categories", - "concept": "Concept", - "definition": "Definition", - "entriestobesynced": "Entries to be synced", - "entrypendingapproval": "This entry is pending approval.", - "entryusedynalink": "This entry should be automatically linked", - "errconceptalreadyexists": "This concept already exists. No duplicates allowed in this glossary.", - "errorloadingentries": "An error occurred while loading entries.", - "errorloadingentry": "An error occurred while loading the entry.", - "errorloadingglossary": "An error occurred while loading the glossary.", - "fillfields": "Concept and definition are mandatory fields.", - "fullmatch": "Match whole words only", - "linking": "Auto-linking", - "modulenameplural": "Glossaries", - "noentriesfound": "No entries were found.", - "searchquery": "Search query", - "tagarea_glossary_entries": "Glossary entries" -} diff --git a/src/addon/mod/glossary/pages/edit/edit.html b/src/addon/mod/glossary/pages/edit/edit.html deleted file mode 100644 index 98bf66757..000000000 --- a/src/addon/mod/glossary/pages/edit/edit.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - -
- - {{ 'addon.mod_glossary.concept' | translate }} - - - - {{ 'addon.mod_glossary.definition' | translate }} - - - - {{ 'addon.mod_glossary.categories' | translate }} - - {{ category.name }} - - - - {{ 'addon.mod_glossary.aliases' | translate }} - - - {{ 'addon.mod_glossary.attachment' | translate }} - - - {{ 'addon.mod_glossary.linking' | translate }} - - {{ 'addon.mod_glossary.entryusedynalink' | translate }} - - - - {{ 'addon.mod_glossary.casesensitive' | translate }} - - - - {{ 'addon.mod_glossary.fullmatch' | translate }} - - - -
-
-
diff --git a/src/addon/mod/glossary/pages/edit/edit.module.ts b/src/addon/mod/glossary/pages/edit/edit.module.ts deleted file mode 100644 index 926f32ad6..000000000 --- a/src/addon/mod/glossary/pages/edit/edit.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModGlossaryEditPage } from './edit'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModGlossaryEditPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonModGlossaryEditPage), - TranslateModule.forChild() - ], -}) -export class AddonModGlossaryNewDiscussionPageModule {} diff --git a/src/addon/mod/glossary/pages/edit/edit.ts b/src/addon/mod/glossary/pages/edit/edit.ts deleted file mode 100644 index 498568084..000000000 --- a/src/addon/mod/glossary/pages/edit/edit.ts +++ /dev/null @@ -1,276 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; -import { FormControl } from '@angular/forms'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { AddonModGlossaryProvider } from '../../providers/glossary'; -import { AddonModGlossaryOfflineProvider } from '../../providers/offline'; -import { AddonModGlossaryHelperProvider } from '../../providers/helper'; - -/** - * Page that displays the edit form. - */ -@IonicPage({ segment: 'addon-mod-glossary-edit' }) -@Component({ - selector: 'page-addon-mod-glossary-edit', - templateUrl: 'edit.html', -}) -export class AddonModGlossaryEditPage implements OnInit { - @ViewChild('editFormEl') formElement: ElementRef; - - component = AddonModGlossaryProvider.COMPONENT; - loaded = false; - entry = { - concept: '', - definition: '', - timecreated: 0, - }; - options = { - categories: [], - aliases: '', - usedynalink: false, - casesensitive: false, - fullmatch: false - }; - attachments = []; - definitionControl = new FormControl(); - categories = []; - editorExtraParams: {[name: string]: any} = {}; - - protected courseId: number; - protected module: any; - protected glossary: any; - protected syncId: string; - protected syncObserver: any; - protected isDestroyed = false; - protected originalData: any; - protected saved = false; - - constructor(private navParams: NavParams, - private navCtrl: NavController, - private translate: TranslateService, - private domUtils: CoreDomUtilsProvider, - private eventsProvider: CoreEventsProvider, - private sitesProvider: CoreSitesProvider, - private uploaderProvider: CoreFileUploaderProvider, - private textUtils: CoreTextUtilsProvider, - private glossaryProvider: AddonModGlossaryProvider, - private glossaryOffline: AddonModGlossaryOfflineProvider, - private glossaryHelper: AddonModGlossaryHelperProvider) { - this.courseId = navParams.get('courseId'); - this.module = navParams.get('module'); - this.glossary = navParams.get('glossary'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const entry = this.navParams.get('entry'); - - let promise; - - if (entry) { - this.entry.concept = entry.concept || ''; - this.entry.definition = entry.definition || ''; - this.entry.timecreated = entry.timecreated || 0; - - this.originalData = { - concept: this.entry.concept, - definition: this.entry.definition, - files: [], - }; - - if (entry.options) { - this.options.categories = entry.options.categories || []; - this.options.aliases = entry.options.aliases || ''; - this.options.usedynalink = !!entry.options.usedynalink; - if (this.options.usedynalink) { - this.options.casesensitive = !!entry.options.casesensitive; - this.options.fullmatch = !!entry.options.fullmatch; - } - } - - // Treat offline attachments if any. - if (entry.attachments && entry.attachments.offline) { - promise = this.glossaryHelper.getStoredFiles(this.glossary.id, entry.concept, entry.timecreated).then((files) => { - this.attachments = files; - this.originalData.files = files.slice(); - }); - } - - if (entry.id) { - this.editorExtraParams.id = entry.id; - } - } - - this.definitionControl.setValue(this.entry.definition); - - Promise.resolve(promise).then(() => { - this.glossaryProvider.getAllCategories(this.glossary.id, { - cmId: this.module.id, - }).then((categories) => { - this.categories = categories; - }).finally(() => { - this.loaded = true; - }); - }); - } - - /** - * Definition changed. - * - * @param text The new text. - */ - onDefinitionChange(text: string): void { - this.entry.definition = text; - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.saved) { - return; - } - - if (this.glossaryHelper.hasEntryDataChanged(this.entry, this.attachments, this.originalData)) { - // Show confirmation if some data has been modified. - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - // Delete the local files from the tmp folder. - this.uploaderProvider.clearTmpFiles(this.attachments); - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } - - /** - * Save the entry. - */ - save(): void { - let definition = this.entry.definition, - saveOffline = false, - promise; - const timecreated = this.entry.timecreated || Date.now(); - - if (!this.entry.concept || !definition) { - this.domUtils.showErrorModal('addon.mod_glossary.fillfields', true); - - return; - } - - const modal = this.domUtils.showModalLoading('core.sending', true); - - // Add some HTML to the definition if needed. - definition = this.textUtils.formatHtmlLines(definition); - - // Upload attachments first if any. - if (this.attachments.length > 0) { - promise = this.glossaryHelper.uploadOrStoreFiles(this.glossary.id, this.entry.concept, timecreated, this.attachments, - false).catch(() => { - // Cannot upload them in online, save them in offline. - saveOffline = true; - - return this.glossaryHelper.uploadOrStoreFiles(this.glossary.id, this.entry.concept, timecreated, - this.attachments, true); - }); - } else { - promise = Promise.resolve(); - } - - promise.then((attach) => { - const options: any = { - aliases: this.options.aliases, - categories: this.options.categories.join(',') - }; - - if (this.glossary.usedynalink) { - options.usedynalink = this.options.usedynalink ? 1 : 0; - if (this.options.usedynalink) { - options.casesensitive = this.options.casesensitive ? 1 : 0; - options.fullmatch = this.options.fullmatch ? 1 : 0; - } - } - - if (saveOffline) { - let promise; - if (this.entry && !this.glossary.allowduplicatedentries) { - // Check if the entry is duplicated in online or offline mode. - promise = this.glossaryProvider.isConceptUsed(this.glossary.id, this.entry.concept, { - timeCreated: this.entry.timecreated, - cmId: this.module.id, - }).then((used) => { - if (used) { - // There's a entry with same name, reject with error message. - return Promise.reject(this.translate.instant('addon.mod_glossary.errconceptalreadyexists')); - } - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - // Save entry in offline. - return this.glossaryOffline.addNewEntry(this.glossary.id, this.entry.concept, definition, this.courseId, - options, attach, timecreated, undefined, undefined, this.entry).then(() => { - // Don't return anything. - }); - }); - } else { - // Try to send it to server. - // Don't allow offline if there are attachments since they were uploaded fine. - return this.glossaryProvider.addEntry(this.glossary.id, this.entry.concept, definition, this.courseId, options, - attach, { - timeCreated: timecreated, - discardEntry: this.entry, - allowOffline: !this.attachments.length, - checkDuplicates: !this.glossary.allowduplicatedentries, - }); - } - }).then((entryId) => { - // Delete the local files from the tmp folder. - this.uploaderProvider.clearTmpFiles(this.attachments); - - if (entryId) { - // Data sent to server, delete stored files (if any). - this.glossaryHelper.deleteStoredFiles(this.glossary.id, this.entry.concept, timecreated); - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'glossary' }); - } - - const data = { - glossaryId: this.glossary.id, - }; - this.eventsProvider.trigger(AddonModGlossaryProvider.ADD_ENTRY_EVENT, data, this.sitesProvider.getCurrentSiteId()); - - this.domUtils.triggerFormSubmittedEvent(this.formElement, !!entryId, this.sitesProvider.getCurrentSiteId()); - - this.saved = true; - this.navCtrl.pop(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_glossary.cannoteditentry', true); - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/mod/glossary/pages/entry/entry.html b/src/addon/mod/glossary/pages/entry/entry.html deleted file mode 100644 index fc67df806..000000000 --- a/src/addon/mod/glossary/pages/entry/entry.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - -

- {{ entry.timemodified | coreDateDayOrTime }} -

{{ entry.userfullname }}

-
- -

- {{ entry.timemodified | coreDateDayOrTime }} -
- - - - -
- -
-
- -
{{ 'core.tag.tags' | translate }}:
- -
- -

{{ 'addon.mod_glossary.entrypendingapproval' | translate }}

-
- - - - - -
- - - - {{ 'addon.mod_glossary.errorloadingentry' | translate }} - - -
-
diff --git a/src/addon/mod/glossary/pages/entry/entry.module.ts b/src/addon/mod/glossary/pages/entry/entry.module.ts deleted file mode 100644 index c045e4040..000000000 --- a/src/addon/mod/glossary/pages/entry/entry.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -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 { CoreRatingComponentsModule } from '@core/rating/components/components.module'; -import { CoreTagComponentsModule } from '@core/tag/components/components.module'; -import { AddonModGlossaryEntryPage } from './entry'; - -@NgModule({ - declarations: [ - AddonModGlossaryEntryPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonModGlossaryEntryPage), - TranslateModule.forChild(), - CoreCommentsComponentsModule, - CoreRatingComponentsModule, - CoreTagComponentsModule - ], -}) -export class AddonModForumDiscussionPageModule {} diff --git a/src/addon/mod/glossary/pages/entry/entry.ts b/src/addon/mod/glossary/pages/entry/entry.ts deleted file mode 100644 index d1e22e722..000000000 --- a/src/addon/mod/glossary/pages/entry/entry.ts +++ /dev/null @@ -1,144 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreRatingInfo } from '@core/rating/providers/rating'; -import { CoreTagProvider } from '@core/tag/providers/tag'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { CoreCommentsCommentsComponent } from '@core/comments/components/comments/comments'; -import { AddonModGlossaryProvider } from '../../providers/glossary'; - -/** - * Page that displays a glossary entry. - */ -@IonicPage({ segment: 'addon-mod-glossary-entry' }) -@Component({ - selector: 'page-addon-mod-glossary-entry', - templateUrl: 'entry.html', -}) -export class AddonModGlossaryEntryPage { - @ViewChild(CoreCommentsCommentsComponent) comments: CoreCommentsCommentsComponent; - - component = AddonModGlossaryProvider.COMPONENT; - componentId: number; - entry: any; - glossary: any; - loaded = false; - showAuthor = false; - showDate = false; - ratingInfo: CoreRatingInfo; - tagsEnabled: boolean; - commentsEnabled: boolean; - - protected courseId: number; - protected entryId: number; - - constructor(navParams: NavParams, - protected domUtils: CoreDomUtilsProvider, - protected glossaryProvider: AddonModGlossaryProvider, - protected tagProvider: CoreTagProvider, - protected commentsProvider: CoreCommentsProvider) { - this.courseId = navParams.get('courseId'); - this.entryId = navParams.get('entryId'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.tagsEnabled = this.tagProvider.areTagsAvailableInSite(); - this.commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(); - - this.fetchEntry().then(() => { - this.glossaryProvider.logEntryView(this.entry.id, this.componentId, this.glossary.name).catch(() => { - // Ignore errors. - }); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - * @return Promise resolved when done. - */ - doRefresh(refresher?: any): Promise { - if (this.glossary && this.glossary.allowcomments && this.entry && this.entry.id > 0 && this.commentsEnabled && - this.comments) { - // Refresh comments. Don't add it to promises because we don't want the comments fetch to block the entry fetch. - this.comments.doRefresh().catch(() => { - // Ignore errors. - }); - } - - return this.glossaryProvider.invalidateEntry(this.entry.id).catch(() => { - // Ignore errors. - }).then(() => { - return this.fetchEntry(true); - }).finally(() => { - refresher && refresher.complete(); - }); - } - - /** - * Convenience function to get the glossary entry. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchEntry(refresh?: boolean): Promise { - return this.glossaryProvider.getEntry(this.entryId).then((result) => { - this.entry = result.entry; - this.ratingInfo = result.ratinginfo; - - if (!refresh) { - // Load the glossary. - return this.glossaryProvider.getGlossaryById(this.courseId, this.entry.glossaryid).then((glossary) => { - this.glossary = glossary; - this.componentId = glossary.coursemodule; - - switch (glossary.displayformat) { - case 'fullwithauthor': - case 'encyclopedia': - this.showAuthor = true; - this.showDate = true; - break; - case 'fullwithoutauthor': - this.showAuthor = false; - this.showDate = true; - break; - default: // Default, and faq, simple, entrylist, continuous. - this.showAuthor = false; - this.showDate = false; - } - }); - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_glossary.errorloadingentry', true); - - return Promise.reject(null); - }); - } - - /** - * Function called when rating is updated online. - */ - ratingUpdated(): void { - this.glossaryProvider.invalidateEntry(this.entryId); - } -} diff --git a/src/addon/mod/glossary/pages/index/index.html b/src/addon/mod/glossary/pages/index/index.html deleted file mode 100644 index bd651f76a..000000000 --- a/src/addon/mod/glossary/pages/index/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/src/addon/mod/glossary/pages/index/index.module.ts b/src/addon/mod/glossary/pages/index/index.module.ts deleted file mode 100644 index 4395a0c55..000000000 --- a/src/addon/mod/glossary/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModGlossaryComponentsModule } from '../../components/components.module'; -import { AddonModGlossaryIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModGlossaryIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModGlossaryComponentsModule, - IonicPageModule.forChild(AddonModGlossaryIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModGlossaryIndexPageModule {} diff --git a/src/addon/mod/glossary/pages/index/index.ts b/src/addon/mod/glossary/pages/index/index.ts deleted file mode 100644 index 673aa39b1..000000000 --- a/src/addon/mod/glossary/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModGlossaryIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a glossary. - */ -@IonicPage({ segment: 'addon-mod-glossary-index' }) -@Component({ - selector: 'page-addon-mod-glossary-index', - templateUrl: 'index.html', -}) -export class AddonModGlossaryIndexPage { - @ViewChild(AddonModGlossaryIndexComponent) glossaryComponent: AddonModGlossaryIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the glossary instance. - * - * @param glossary Glossary instance. - */ - updateData(glossary: any): void { - this.title = glossary.name || this.title; - } -} diff --git a/src/addon/mod/glossary/providers/edit-link-handler.ts b/src/addon/mod/glossary/providers/edit-link-handler.ts deleted file mode 100644 index 5dd28d5e4..000000000 --- a/src/addon/mod/glossary/providers/edit-link-handler.ts +++ /dev/null @@ -1,88 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModGlossaryProvider } from './glossary'; - -/** - * Content links handler for glossary new entry. - * Match mod/glossary/edit.php?cmid=6 with a valid data. - * Currently it only supports new entry. - */ -@Injectable() -export class AddonModGlossaryEditLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModGlossaryEditLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModGlossary'; - pattern = /\/mod\/glossary\/edit\.php.*([\?\&](cmid)=\d+)/; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private courseProvider: CoreCourseProvider, - private domUtils: CoreDomUtilsProvider, private glossaryProvider: AddonModGlossaryProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - cmId = parseInt(params.cmid, 10); - - this.courseProvider.getModuleBasicInfo(cmId, siteId).then((module) => { - return this.glossaryProvider.getGlossary(module.course, module.id).then((glossary) => { - const pageParams = { - courseId: module.course, - module: module, - glossary: glossary, - entry: null // It does not support entry editing. - }; - - this.linkHelper.goInSite(navCtrl, 'AddonModGlossaryEditPage', pageParams, siteId); - }); - }).finally(() => { - // Just in case. In fact we need to dismiss the modal before showing a toast or error message. - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return typeof params.cmid != 'undefined'; - } -} diff --git a/src/addon/mod/glossary/providers/entry-link-handler.ts b/src/addon/mod/glossary/providers/entry-link-handler.ts deleted file mode 100644 index 4c7d5a6fd..000000000 --- a/src/addon/mod/glossary/providers/entry-link-handler.ts +++ /dev/null @@ -1,83 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModGlossaryProvider } from './glossary'; - -/** - * Handler to treat links to glossary entries. - */ -@Injectable() -export class AddonModGlossaryEntryLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModGlossaryEntryLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModGlossary'; - pattern = /\/mod\/glossary\/(showentry|view)\.php.*([\&\?](eid|g|mode|hook)=\d+)/; - - constructor( - private domUtils: CoreDomUtilsProvider, - private linkHelper: CoreContentLinksHelperProvider, - private glossaryProvider: AddonModGlossaryProvider, - private courseHelper: CoreCourseHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(); - let entryId; - if (params.mode == 'entry') { - entryId = parseInt(params.hook, 10); - } else { - entryId = parseInt(params.eid, 10); - } - - let promise; - - if (courseId) { - promise = Promise.resolve(courseId); - } else { - promise = this.glossaryProvider.getEntry(entryId, {siteId}).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_glossary.errorloadingentry', true); - - return Promise.reject(null); - }).then((response) => { - return this.courseHelper.getModuleCourseIdByInstance(response.entry.glossaryid, 'glossary', siteId); - }); - } - - return promise.then((courseId) => { - this.linkHelper.goInSite(navCtrl, 'AddonModGlossaryEntryPage', {courseId, entryId}, siteId); - }).finally(() => { - modal.dismiss(); - }); - } - }]; - } -} diff --git a/src/addon/mod/glossary/providers/glossary.ts b/src/addon/mod/glossary/providers/glossary.ts deleted file mode 100644 index 79f9887ad..000000000 --- a/src/addon/mod/glossary/providers/glossary.ts +++ /dev/null @@ -1,1115 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSite } from '@classes/site'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSiteSchema, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreRatingInfo } from '@core/rating/providers/rating'; -import { AddonModGlossaryOfflineProvider } from './offline'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some features for glossaries. - */ -@Injectable() -export class AddonModGlossaryProvider { - static COMPONENT = 'mmaModGlossary'; - static LIMIT_ENTRIES = 25; - static LIMIT_CATEGORIES = 10; - static SHOW_ALL_CATEGORIES = 0; - static SHOW_NOT_CATEGORISED = -1; - - static ADD_ENTRY_EVENT = 'addon_mod_glossary_add_entry'; - - protected ROOT_CACHE_KEY = 'mmaModGlossary:'; - - // Variables for database. - static ENTRIES_TABLE = 'addon_mod_glossary_entry_glossaryid'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModGlossaryProvider', - version: 1, - tables: [ - { - name: AddonModGlossaryProvider.ENTRIES_TABLE, - columns: [ - { - name: 'entryid', - type: 'INTEGER', - primaryKey: true - }, - { - name: 'glossaryid', - type: 'INTEGER', - }, - { - name: 'pagefrom', - type: 'INTEGER', - } - ] - } - ] - }; - - constructor(private appProvider: CoreAppProvider, - private sitesProvider: CoreSitesProvider, - private filepoolProvider: CoreFilepoolProvider, - private translate: TranslateService, - private textUtils: CoreTextUtilsProvider, - private utils: CoreUtilsProvider, - private glossaryOffline: AddonModGlossaryOfflineProvider, - private logHelper: CoreCourseLogHelperProvider) { - - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Get the course glossary cache key. - * - * @param courseId Course Id. - * @return Cache key. - */ - protected getCourseGlossariesCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'courseGlossaries:' + courseId; - } - - /** - * Get all the glossaries in a course. - * - * @param courseId Course Id. - * @param options Other options. - * @return Resolved with the glossaries. - */ - getCourseGlossaries(courseId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getCourseGlossariesCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModGlossaryProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_glossary_get_glossaries_by_courses', params, preSets).then((result) => { - return result.glossaries; - }); - }); - } - - /** - * Invalidate all glossaries in a course. - * - * @param courseId Course Id. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when data is invalidated. - */ - invalidateCourseGlossaries(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const key = this.getCourseGlossariesCacheKey(courseId); - - return site.invalidateWsCacheForKey(key); - }); - } - - /** - * Get the entries by author cache key. - * - * @param glossaryId Glossary Id. - * @param letter First letter of firstname or lastname, or either keywords: ALL or SPECIAL. - * @param field Search and order using: FIRSTNAME or LASTNAME - * @param sort The direction of the order: ASC or DESC - * @return Cache key. - */ - protected getEntriesByAuthorCacheKey(glossaryId: number, letter: string, field: string, sort: string): string { - return this.ROOT_CACHE_KEY + 'entriesByAuthor:' + glossaryId + ':' + letter + ':' + field + ':' + sort; - } - - /** - * Get entries by author. - * - * @param glossaryId Glossary Id. - * @param letter First letter of firstname or lastname, or either keywords: ALL or SPECIAL. - * @param field Search and order using: FIRSTNAME or LASTNAME - * @param sort The direction of the order: ASC or DESC - * @param options Other options. - * @return Resolved with the entries. - */ - getEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string, - options: AddonModGlossaryGetEntriesOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - id: glossaryId, - letter: letter, - field: field, - sort: sort, - from: options.from || 0, - limit: options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES, - }; - const preSets = { - cacheKey: this.getEntriesByAuthorCacheKey(glossaryId, letter, field, sort), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModGlossaryProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_glossary_get_entries_by_author', params, preSets); - }); - } - - /** - * Invalidate cache of entries by author. - * - * @param glossaryId Glossary Id. - * @param letter First letter of firstname or lastname, or either keywords: ALL or SPECIAL. - * @param field Search and order using: FIRSTNAME or LASTNAME - * @param sort The direction of the order: ASC or DESC - * @param siteId Site ID. If not defined, current site. - * @return Resolved when data is invalidated. - */ - invalidateEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const key = this.getEntriesByAuthorCacheKey(glossaryId, letter, field, sort); - - return site.invalidateWsCacheForKey(key); - }); - } - - /** - * Get entries by category. - * - * @param glossaryId Glossary Id. - * @param categoryId The category ID. Use constant SHOW_ALL_CATEGORIES for all categories, or - * constant SHOW_NOT_CATEGORISED for uncategorised entries. - * @param options Other options. - * @return Resolved with the entries. - */ - getEntriesByCategory(glossaryId: number, categoryId: number, options: AddonModGlossaryGetEntriesOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - id: glossaryId, - categoryid: categoryId, - from: options.from || 0, - limit: options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES, - }; - const preSets = { - cacheKey: this.getEntriesByCategoryCacheKey(glossaryId, categoryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModGlossaryProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_glossary_get_entries_by_category', params, preSets); - }); - } - - /** - * Invalidate cache of entries by category. - * - * @param glossaryId Glossary Id. - * @param categoryId The category ID. Use constant SHOW_ALL_CATEGORIES for all categories, or - * constant SHOW_NOT_CATEGORISED for uncategorised entries. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when data is invalidated. - */ - invalidateEntriesByCategory(glossaryId: number, categoryId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const key = this.getEntriesByCategoryCacheKey(glossaryId, categoryId); - - return site.invalidateWsCacheForKey(key); - }); - } - - /** - * Get the entries by category cache key. - * - * @param glossaryId Glossary Id. - * @param categoryId The category ID. Use constant SHOW_ALL_CATEGORIES for all categories, or - * constant SHOW_NOT_CATEGORISED for uncategorised entries. - * @return Cache key. - */ - getEntriesByCategoryCacheKey(glossaryId: number, categoryId: number): string { - return this.ROOT_CACHE_KEY + 'entriesByCategory:' + glossaryId + ':' + categoryId; - } - - /** - * Get the entries by date cache key. - * - * @param glossaryId Glossary Id. - * @param order The way to order the records. - * @param sort The direction of the order. - * @return Cache key. - */ - getEntriesByDateCacheKey(glossaryId: number, order: string, sort: string): string { - return this.ROOT_CACHE_KEY + 'entriesByDate:' + glossaryId + ':' + order + ':' + sort; - } - - /** - * Get entries by date. - * - * @param glossaryId Glossary Id. - * @param order The way to order the records. - * @param sort The direction of the order. - * @param options Other options. - * @return Resolved with the entries. - */ - getEntriesByDate(glossaryId: number, order: string, sort: string, options: AddonModGlossaryGetEntriesOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - id: glossaryId, - order: order, - sort: sort, - from: options.from || 0, - limit: options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES, - }; - const preSets = { - cacheKey: this.getEntriesByDateCacheKey(glossaryId, order, sort), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModGlossaryProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_glossary_get_entries_by_date', params, preSets); - }); - } - - /** - * Invalidate cache of entries by date. - * - * @param glossaryId Glossary Id. - * @param order The way to order the records. - * @param sort The direction of the order. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when data is invalidated. - */ - invalidateEntriesByDate(glossaryId: number, order: string, sort: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const key = this.getEntriesByDateCacheKey(glossaryId, order, sort); - - return site.invalidateWsCacheForKey(key); - }); - } - - /** - * Get the entries by letter cache key. - * - * @param glossaryId Glossary Id. - * @param letter A letter, or a special keyword. - * @return Cache key. - */ - protected getEntriesByLetterCacheKey(glossaryId: number, letter: string): string { - return this.ROOT_CACHE_KEY + 'entriesByLetter:' + glossaryId + ':' + letter; - } - - /** - * Get entries by letter. - * - * @param glossaryId Glossary Id. - * @param letter A letter, or a special keyword. - * @param options Other options. - * @return Resolved with the entries. - */ - getEntriesByLetter(glossaryId: number, letter: string, options: AddonModGlossaryGetEntriesOptions = {}): Promise { - options.from = options.from || 0; - options.limit = options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - id: glossaryId, - letter: letter, - from: options.from, - limit: options.limit, - }; - const preSets = { - cacheKey: this.getEntriesByLetterCacheKey(glossaryId, letter), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModGlossaryProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_glossary_get_entries_by_letter', params, preSets).then((result) => { - - if (options.limit == AddonModGlossaryProvider.LIMIT_ENTRIES) { - // Store entries in background, don't block the user for this. - this.storeEntries(glossaryId, result.entries, options.from, site.getId()).catch(() => { - // Ignore errors. - }); - } - - return result; - }); - }); - } - - /** - * Invalidate cache of entries by letter. - * - * @param glossaryId Glossary Id. - * @param letter A letter, or a special keyword. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when data is invalidated. - */ - invalidateEntriesByLetter(glossaryId: number, letter: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const key = this.getEntriesByLetterCacheKey(glossaryId, letter); - - return site.invalidateWsCacheForKey(key); - }); - } - - /** - * Get the entries by search cache key. - * - * @param glossaryId Glossary Id. - * @param query The search query. - * @param fullSearch Whether or not full search is required. - * @param order The way to order the results. - * @param sort The direction of the order. - * @return Cache key. - */ - protected getEntriesBySearchCacheKey(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string): - string { - return this.ROOT_CACHE_KEY + 'entriesBySearch:' + glossaryId + ':' + fullSearch + ':' + order + ':' + sort + ':' + query; - } - - /** - * Get entries by search. - * - * @param glossaryId Glossary Id. - * @param query The search query. - * @param fullSearch Whether or not full search is required. - * @param order The way to order the results. - * @param sort The direction of the order. - * @param from Start returning records from here. - * @param limit Number of records to return. - * @param omitExpires True to always get the value from cache. If data isn't cached, it will call the WS. - * @param forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS. - * @param siteId Site ID. If not defined, current site. - * @return Resolved with the entries. - */ - getEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string, - options: AddonModGlossaryGetEntriesOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - id: glossaryId, - query: query, - fullsearch: fullSearch, - order: order, - sort: sort, - from: options.from || 0, - limit: options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES, - }; - const preSets = { - cacheKey: this.getEntriesBySearchCacheKey(glossaryId, query, fullSearch, order, sort), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModGlossaryProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_glossary_get_entries_by_search', params, preSets); - }); - } - - /** - * Invalidate cache of entries by search. - * - * @param glossaryId Glossary Id. - * @param query The search query. - * @param fullSearch Whether or not full search is required. - * @param order The way to order the results. - * @param sort The direction of the order. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when data is invalidated. - */ - invalidateEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const key = this.getEntriesBySearchCacheKey(glossaryId, query, fullSearch, order, sort); - - return site.invalidateWsCacheForKey(key); - }); - } - - /** - * Get the glossary categories cache key. - * - * @param glossaryId Glossary Id. - * @return The cache key. - */ - protected getCategoriesCacheKey(glossaryId: number): string { - return this.ROOT_CACHE_KEY + 'categories:' + glossaryId; - } - - /** - * Get all the categories related to the glossary. - * - * @param glossaryId Glossary Id. - * @param options Other options. - * @return Promise resolved with the categories if supported or empty array if not. - */ - getAllCategories(glossaryId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - return this.getCategories(glossaryId, [], site, options); - }); - } - - /** - * Get the categories related to the glossary by sections. It's a recursive function see initial call values. - * - * @param glossaryId Glossary Id. - * @param categories Already fetched categories where to append the fetch. - * @param site Site object. - * @param options Other options. - * @return Promise resolved with the categories. - */ - protected getCategories(glossaryId: number, categories: any[], site: CoreSite, - options: AddonModGlossaryGetCategoriesOptions = {}): Promise { - - options.from = options.from || 0; - options.limit = options.limit || AddonModGlossaryProvider.LIMIT_CATEGORIES; - - const params = { - id: glossaryId, - from: options.from, - limit: options.limit, - }; - const preSets = { - cacheKey: this.getCategoriesCacheKey(glossaryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModGlossaryProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_glossary_get_categories', params, preSets).then((response) => { - categories = categories.concat(response.categories); - const canLoadMore = (options.from + options.limit) < response.count; - if (canLoadMore) { - options.from += options.limit; - - return this.getCategories(glossaryId, categories, site, options); - } - - return categories; - }); - } - - /** - * Invalidate cache of categories by glossary id. - * - * @param glossaryId Glossary Id. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when categories data has been invalidated, - */ - invalidateCategories(glossaryId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCategoriesCacheKey(glossaryId)); - }); - } - - /** - * Get an entry by ID cache key. - * - * @param entryId Entry Id. - * @return Cache key. - */ - protected getEntryCacheKey(entryId: number): string { - return this.ROOT_CACHE_KEY + 'getEntry:' + entryId; - } - - /** - * Get one entry by ID. - * - * @param entryId Entry ID. - * @param options Other options. - * @return Promise resolved with the entry. - */ - getEntry(entryId: number, options: CoreCourseCommonModWSOptions = {}) - : Promise<{entry: any, ratinginfo: CoreRatingInfo, from?: number}> { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - id: entryId, - }; - const preSets = { - cacheKey: this.getEntryCacheKey(entryId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModGlossaryProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_glossary_get_entry_by_id', params, preSets).then((response) => { - if (response && response.entry) { - return response; - } else { - return Promise.reject(null); - } - }).catch((error) => { - // Entry not found. Search it in the list of entries. - let glossaryId; - - const searchEntry = (from: number, loadNext: boolean): Promise => { - // Get the entries from this "page" and check if the entry we're looking for is in it. - return this.getEntriesByLetter(glossaryId, 'ALL', { - from: from, - readingStrategy: CoreSitesReadingStrategy.OnlyCache, - cmId: options.cmId, - siteId: options.siteId, - }).then((result) => { - - for (let i = 0; i < result.entries.length; i++) { - const entry = result.entries[i]; - if (entry.id == entryId) { - // Entry found, return it. - return { - entry: entry, - from: from - }; - } - } - - const nextFrom = from + result.entries.length; - if (nextFrom < result.count && loadNext) { - // Get the next "page". - return searchEntry(nextFrom, true); - } - - // No more pages and the entry wasn't found. Reject. - return Promise.reject(null); - }); - }; - - return this.getStoredDataForEntry(entryId, site.getId()).then((data) => { - glossaryId = data.glossaryId; - - if (typeof data.from != 'undefined') { - return searchEntry(data.from, false).catch(() => { - // Entry not found in that page. Search all pages. - return searchEntry(0, true); - }); - } - - // Page not specified, search all pages. - return searchEntry(0, true); - }).catch(() => { - return Promise.reject(error); - }); - }); - }); - } - - /** - * Get a glossary ID and the "from" of a given entry. - * - * @param entryId Entry ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the glossary ID and the "from". - */ - getStoredDataForEntry(entryId: number, siteId?: string): Promise<{glossaryId: number, from: number}> { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - entryid: entryId - }; - - return site.getDb().getRecord(AddonModGlossaryProvider.ENTRIES_TABLE, conditions).then((record) => { - return { - glossaryId: record.glossaryid, - from: record.pagefrom - }; - }); - }); - } - - /** - * Performs the fetch of the entries using the propper function and arguments. - * - * @param fetchFunction Function to fetch. - * @param fetchArguments Arguments to call the fetching. - * @param options Other options. - * @return Promise resolved with the response. - */ - fetchEntries(fetchFunction: Function, fetchArguments: any[], options: AddonModGlossaryGetEntriesOptions = {}): Promise { - const args = fetchArguments.slice(); - args.push(options); - - return fetchFunction.apply(this, args); - } - - /** - * Performs the whole fetch of the entries using the proper function and arguments. - * - * @param fetchFunction Function to fetch. - * @param fetchArguments Arguments to call the fetching. - * @param options Other options. - * @return Promise resolved with all entrries. - */ - fetchAllEntries(fetchFunction: Function, fetchArguments: any[], options: CoreCourseCommonModWSOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - const entries = []; - - const fetchMoreEntries = (): Promise => { - return this.fetchEntries(fetchFunction, fetchArguments, { - from: entries.length, - ...options, // Include all options. - }).then((result) => { - Array.prototype.push.apply(entries, result.entries); - - return entries.length < result.count ? fetchMoreEntries() : entries; - }); - }; - - return fetchMoreEntries(); - } - - /** - * Invalidate cache of entry by ID. - * - * @param entryId Entry Id. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when data is invalidated. - */ - invalidateEntry(entryId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getEntryCacheKey(entryId)); - }); - } - - /** - * Invalidate cache of all entries in the array. - * - * @param entries Entry objects to invalidate. - * @param siteId Site ID. If not defined, current site. - * @return Resolved when data is invalidated. - */ - protected invalidateEntries(entries: any[], siteId?: string): Promise { - const keys = []; - entries.forEach((entry) => { - keys.push(this.getEntryCacheKey(entry.id)); - }); - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateMultipleWsCacheForKey(keys); - }); - } - - /** - * Invalidate the prefetched content except files. - * To invalidate files, use AddonModGlossary#invalidateFiles. - * - * @param moduleId The module ID. - * @param courseId Course ID. - * @return Promise resolved when data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.getGlossary(courseId, moduleId).then((glossary) => { - return this.invalidateGlossaryEntries(glossary).finally(() => { - return this.utils.allPromises([ - this.invalidateCourseGlossaries(courseId), - this.invalidateCategories(glossary.id) - ]); - }); - }); - } - - /** - * Invalidate the prefetched content for a given glossary, except files. - * To invalidate files, use AddonModGlossaryProvider#invalidateFiles. - * - * @param glossary The glossary object. - * @param onlyEntriesList If true, entries won't be invalidated. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateGlossaryEntries(glossary: any, onlyEntriesList?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - if (!onlyEntriesList) { - promises.push(this.fetchAllEntries(this.getEntriesByLetter, [glossary.id, 'ALL'], { - cmId: glossary.coursemodule, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId, - }).then((entries) => { - return this.invalidateEntries(entries, siteId); - })); - } - - glossary.browsemodes.forEach((mode) => { - switch (mode) { - case 'letter': - promises.push(this.invalidateEntriesByLetter(glossary.id, 'ALL', siteId)); - break; - case 'cat': - promises.push(this.invalidateEntriesByCategory(glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATEGORIES, - siteId)); - break; - case 'date': - promises.push(this.invalidateEntriesByDate(glossary.id, 'CREATION', 'DESC', siteId)); - promises.push(this.invalidateEntriesByDate(glossary.id, 'UPDATE', 'DESC', siteId)); - break; - case 'author': - promises.push(this.invalidateEntriesByAuthor(glossary.id, 'ALL', 'LASTNAME', 'ASC', siteId)); - break; - default: - } - }); - - return this.utils.allPromises(promises); - } - - /** - * Invalidate the prefetched files. - * - * @param moduleId The module ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the files are invalidated. - */ - protected invalidateFiles(moduleId: number, siteId?: string): Promise { - return this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModGlossaryProvider.COMPONENT, moduleId); - } - - /** - * Get one glossary by cmid. - * - * @param courseId Course Id. - * @param cmId Course Module Id. - * @param options Other options. - * @return Promise resolved with the glossary. - */ - getGlossary(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getCourseGlossaries(courseId, options).then((glossaries) => { - const glossary = glossaries.find((glossary) => glossary.coursemodule == cmId); - - if (glossary) { - return glossary; - } - - return Promise.reject(null); - }); - } - - /** - * Get one glossary by glossary ID. - * - * @param courseId Course Id. - * @param glossaryId Glossary Id. - * @param options Other options. - * @return Promise resolved with the glossary. - */ - getGlossaryById(courseId: number, glossaryId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getCourseGlossaries(courseId, options).then((glossaries) => { - const glossary = glossaries.find((glossary) => glossary.id == glossaryId); - - if (glossary) { - return glossary; - } - - return Promise.reject(null); - }); - } - - /** - * Create a new entry on a glossary - * - * @param glossaryId Glossary ID. - * @param concept Glossary entry concept. - * @param definition Glossary entry concept definition. - * @param courseId Course ID of the glossary. - * @param entryOptions Array of options for the entry. - * @param attach Attachments ID if sending online, result of CoreFileUploaderProvider#storeFilesToUpload - * otherwise. - * @param otherOptions Other options. - * @return Promise resolved with entry ID if entry was created in server, false if stored in device. - */ - addEntry(glossaryId: number, concept: string, definition: string, courseId: number, entryOptions: any, attach: any, - otherOptions: AddonModGlossaryAddEntryOptions = {}): Promise { - otherOptions.siteId = otherOptions.siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a new entry to be synchronized later. - const storeOffline = (): Promise => { - const discardTime = otherOptions.discardEntry && otherOptions.discardEntry.timecreated; - - let duplicatesPromise; - if (otherOptions.checkDuplicates) { - duplicatesPromise = this.isConceptUsed(glossaryId, concept, { - cmId: otherOptions.cmId, - timeCreated: discardTime, - siteId: otherOptions.siteId, - }); - } else { - duplicatesPromise = Promise.resolve(false); - } - - // Check if the entry is duplicated in online or offline mode. - return duplicatesPromise.then((used) => { - if (used) { - return Promise.reject(this.translate.instant('addon.mod_glossary.errconceptalreadyexists')); - } - - return this.glossaryOffline.addNewEntry(glossaryId, concept, definition, courseId, attach, entryOptions, - otherOptions.timeCreated, otherOptions.siteId, undefined, otherOptions.discardEntry).then(() => { - return false; - }); - }); - }; - - if (!this.appProvider.isOnline() && otherOptions.allowOffline) { - // App is offline, store the action. - return storeOffline(); - } - - // If we are editing an offline entry, discard previous first. - let discardPromise; - if (otherOptions.discardEntry) { - discardPromise = this.glossaryOffline.deleteNewEntry( - glossaryId, otherOptions.discardEntry.concept, otherOptions.discardEntry.timecreated, otherOptions.siteId); - } else { - discardPromise = Promise.resolve(); - } - - return discardPromise.then(() => { - // Try to add it in online. - return this.addEntryOnline(glossaryId, concept, definition, entryOptions, attach, otherOptions.siteId) - .then((entryId) => { - return entryId; - }).catch((error) => { - if (otherOptions.allowOffline && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Create a new entry on a glossary. It does not cache calls. It will fail if offline or cannot connect. - * - * @param glossaryId Glossary ID. - * @param concept Glossary entry concept. - * @param definition Glossary entry concept definition. - * @param options Array of options for the entry. - * @param attachId Attachments ID (if any attachment). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the entry ID if created, rejected otherwise. - */ - addEntryOnline(glossaryId: number, concept: string, definition: string, options?: any, attachId?: number, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - glossaryid: glossaryId, - concept: concept, - definition: definition, - definitionformat: 1, - options: this.utils.objectToArrayOfObjects(options || {}, 'name', 'value') - }; - - if (attachId) { - params.options.push({ - name: 'attachmentsid', - value: attachId - }); - } - - // Workaround for bug MDL-57737. - if (!site.isVersionGreaterEqualThan('3.2.2')) { - params.definition = this.textUtils.cleanTags(params.definition); - } - - return site.write('mod_glossary_add_entry', params).then((response) => { - if (response && response.entryid) { - return response.entryid; - } - - return Promise.reject(this.utils.createFakeWSError('')); - }); - }); - } - - /** - * Check if a entry concept is already used. - * - * @param glossaryId Glossary ID. - * @param concept Concept to check. - * @param options Other options. - * @return Promise resolved with true if used, resolved with false if not used or error. - */ - isConceptUsed(glossaryId: number, concept: string, options: AddonModGlossaryIsConceptUsedOptions = {}): Promise { - // Check offline first. - return this.glossaryOffline.isConceptUsed(glossaryId, concept, options.timeCreated, options.siteId).then((exists) => { - if (exists) { - return true; - } - - // If we get here, there's no offline entry with this name, check online. - // Get entries from the cache. - return this.fetchAllEntries(this.getEntriesByLetter, [glossaryId, 'ALL'], { - cmId: options.cmId, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId: options.siteId, - }).then((entries) => { - // Check if there's any entry with the same concept. - return entries.some((entry) => entry.concept == concept); - }); - }).catch(() => { - // Error, assume not used. - return false; - }); - } - - /** - * Return whether or not the plugin is enabled for editing in the current site. Plugin is enabled if the glossary WS are - * available. - * - * @return Whether the glossary editing is available or not. - */ - isPluginEnabledForEditing(): boolean { - return this.sitesProvider.getCurrentSite().wsAvailable('mod_glossary_add_entry'); - } - - /** - * Report a glossary as being viewed. - * - * @param glossaryId Glossary ID. - * @param mode The mode in which the glossary was viewed. - * @param name Name of the glossary. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(glossaryId: number, mode: string, name?: string, siteId?: string): Promise { - const params = { - id: glossaryId, - mode: mode - }; - - return this.logHelper.logSingle('mod_glossary_view_glossary', params, AddonModGlossaryProvider.COMPONENT, glossaryId, name, - 'glossary', {mode: mode}, siteId); - } - - /** - * Report a glossary entry as being viewed. - * - * @param entryId Entry ID. - * @param glossaryId Glossary ID. - * @param name Name of the glossary. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logEntryView(entryId: number, glossaryId: number, name?: string, siteId?: string): Promise { - const params = { - id: entryId - }; - - return this.logHelper.logSingle('mod_glossary_view_entry', params, AddonModGlossaryProvider.COMPONENT, glossaryId, name, - 'glossary', {entryid: entryId}, siteId); - } - - /** - * Store several entries so we can determine their glossaryId in offline. - * - * @param glossaryId Glossary ID the entries belongs to. - * @param entries Entries. - * @param from The "page" the entries belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - protected storeEntries(glossaryId: number, entries: any[], from: number, siteId?: string): Promise { - const promises = []; - - entries.forEach((entry) => { - promises.push(this.storeEntryId(glossaryId, entry.id, from, siteId)); - }); - - return Promise.all(promises); - } - - /** - * Store an entry so we can determine its glossaryId in offline. - * - * @param glossaryId Glossary ID the entry belongs to. - * @param entryId Entry ID. - * @param from The "page" the entry belongs to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - protected storeEntryId(glossaryId: number, entryId: number, from: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - entryid: entryId, - glossaryid: glossaryId, - pagefrom: from - }; - - return site.getDb().insertRecord(AddonModGlossaryProvider.ENTRIES_TABLE, entry); - }); - } -} - -/** - * Options to pass to add entry. - */ -export type AddonModGlossaryAddEntryOptions = { - timeCreated?: number; // The time the entry was created. If not defined, current time. - discardEntry?: any; // The entry provided will be discarded if found. - allowOffline?: boolean; // True if it can be stored in offline, false otherwise. - checkDuplicates?: boolean; // Check for duplicates before storing offline. Only used if allowOffline is true. - cmId?: number; // Module ID. - siteId?: string; // Site ID. If not defined, current site. -}; - -/** - * Options to pass to the different get entries functions. - */ -export type AddonModGlossaryGetEntriesOptions = CoreCourseCommonModWSOptions & { - from?: number; // Start returning records from here. Defaults to 0. - limit?: number; // Number of records to return. Defaults to AddonModGlossaryProvider.LIMIT_ENTRIES. -}; - -/** - * Options to pass to get categories. - */ -export type AddonModGlossaryGetCategoriesOptions = CoreCourseCommonModWSOptions & { - from?: number; // Start returning records from here. Defaults to 0. - limit?: number; // Number of records to return. Defaults to AddonModGlossaryProvider.LIMIT_CATEGORIES. -}; - -/** - * Options to pass to is concept used. - */ -export type AddonModGlossaryIsConceptUsedOptions = { - cmId?: number; // Module ID. - timeCreated?: number; // Timecreated to check that is not the timecreated we are editing. - siteId?: string; // Site ID. If not defined, current site. -}; diff --git a/src/addon/mod/glossary/providers/helper.ts b/src/addon/mod/glossary/providers/helper.ts deleted file mode 100644 index cd5227e50..000000000 --- a/src/addon/mod/glossary/providers/helper.ts +++ /dev/null @@ -1,121 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreFileProvider } from '@providers/file'; -import { AddonModGlossaryProvider } from './glossary'; -import { AddonModGlossaryOfflineProvider } from './offline'; - -/** - * Helper to gather some common functions for glossary. - */ -@Injectable() -export class AddonModGlossaryHelperProvider { - - constructor(private fileProvider: CoreFileProvider, - private uploaderProvider: CoreFileUploaderProvider, - private glossaryOffline: AddonModGlossaryOfflineProvider) {} - - /** - * Delete stored attachment files for a new entry. - * - * @param glossaryId Glossary ID. - * @param entryName The name of the entry. - * @param timeCreated The time the entry was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when deleted. - */ - deleteStoredFiles(glossaryId: number, entryName: string, timeCreated: number, siteId?: string): Promise { - return this.glossaryOffline.getEntryFolder(glossaryId, entryName, timeCreated, siteId).then((folderPath) => { - return this.fileProvider.removeDir(folderPath).catch(() => { - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists. - }); - }); - } - - /** - * Get a list of stored attachment files for a new entry. See AddonModGlossaryHelperProvider#storeFiles. - * - * @param glossaryId lossary ID. - * @param entryName The name of the entry. - * @param timeCreated The time the entry was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getStoredFiles(glossaryId: number, entryName: string, timeCreated: number, siteId?: string): Promise { - return this.glossaryOffline.getEntryFolder(glossaryId, entryName, timeCreated, siteId).then((folderPath) => { - return this.uploaderProvider.getStoredFiles(folderPath); - }); - } - - /** - * Check if the data of an entry has changed. - * - * @param entry Current data. - * @param files Files attached. - * @param original Original content. - * @return True if data has changed, false otherwise. - */ - hasEntryDataChanged(entry: any, files: any[], original: any): boolean { - if (!original || typeof original.concept == 'undefined') { - // There is no original data. - return entry.definition || entry.concept || files.length > 0; - } - - if (original.definition != entry.definition || original.concept != entry.concept) { - return true; - } - - return this.uploaderProvider.areFileListDifferent(files, original.files); - } - - /** - * Given a list of files (either online files or local files), store the local files in a local folder - * to be submitted later. - * - * @param glossaryId Glossary ID. - * @param entryName The name of the entry. - * @param timeCreated The time the entry was created. - * @param files List of files. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - storeFiles(glossaryId: number, entryName: string, timeCreated: number, files: any[], siteId?: string): Promise { - // Get the folder where to store the files. - return this.glossaryOffline.getEntryFolder(glossaryId, entryName, timeCreated, siteId).then((folderPath) => { - return this.uploaderProvider.storeFilesToUpload(folderPath, files); - }); - } - - /** - * Upload or store some files, depending if the user is offline or not. - * - * @param glossaryId Glossary ID. - * @param entryName The name of the entry. - * @param timeCreated The time the entry was created. - * @param files List of files. - * @param offline True if files sould be stored for offline, false to upload them. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success. - */ - uploadOrStoreFiles(glossaryId: number, entryName: string, timeCreated: number, files: any[], offline: boolean, - siteId?: string): Promise { - if (offline) { - return this.storeFiles(glossaryId, entryName, timeCreated, files, siteId); - } else { - return this.uploaderProvider.uploadOrReuploadFiles(files, AddonModGlossaryProvider.COMPONENT, glossaryId, siteId); - } - } -} diff --git a/src/addon/mod/glossary/providers/index-link-handler.ts b/src/addon/mod/glossary/providers/index-link-handler.ts deleted file mode 100644 index a4e78aa5e..000000000 --- a/src/addon/mod/glossary/providers/index-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModGlossaryProvider } from './glossary'; - -/** - * Handler to treat links to glossary index. - */ -@Injectable() -export class AddonModGlossaryIndexLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModGlossaryIndexLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, protected glossaryProvider: AddonModGlossaryProvider) { - super(courseHelper, 'AddonModGlossary', 'glossary', 'g'); - } -} diff --git a/src/addon/mod/glossary/providers/list-link-handler.ts b/src/addon/mod/glossary/providers/list-link-handler.ts deleted file mode 100644 index b6a4ead66..000000000 --- a/src/addon/mod/glossary/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to glossary list page. - */ -@Injectable() -export class AddonModGlossaryListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModGlossaryListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModGlossary', 'glossary'); - } -} diff --git a/src/addon/mod/glossary/providers/module-handler.ts b/src/addon/mod/glossary/providers/module-handler.ts deleted file mode 100644 index ea9cb06cb..000000000 --- a/src/addon/mod/glossary/providers/module-handler.ts +++ /dev/null @@ -1,100 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModGlossaryIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support glossary modules. - */ -@Injectable() -export class AddonModGlossaryModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModGlossary'; - modName = 'glossary'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, - [CoreConstants.FEATURE_RATE]: true, - [CoreConstants.FEATURE_PLAGIARISM]: true - }; - - constructor(private courseProvider: CoreCourseProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_glossary-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModGlossaryIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModGlossaryIndexComponent; - } - - /** - * Whether to display the course refresher in single activity course format. If it returns false, a refresher must be - * included in the template that calls the doRefresh method of the component. Defaults to true. - * - * @return Whether the refresher should be displayed. - */ - displayRefresherInSingleActivity(): boolean { - return false; - } -} diff --git a/src/addon/mod/glossary/providers/offline.ts b/src/addon/mod/glossary/providers/offline.ts deleted file mode 100644 index 181e239d8..000000000 --- a/src/addon/mod/glossary/providers/offline.ts +++ /dev/null @@ -1,285 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFileProvider } from '@providers/file'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Service to handle offline glossary. - */ -@Injectable() -export class AddonModGlossaryOfflineProvider { - - // Variables for database. - static ENTRIES_TABLE = 'addon_mod_glossary_entrues'; - - protected siteSchema: CoreSiteSchema = { - name: 'AddonModGlossaryOfflineProvider', - version: 1, - tables: [ - { - name: AddonModGlossaryOfflineProvider.ENTRIES_TABLE, - columns: [ - { - name: 'glossaryid', - type: 'INTEGER', - }, - { - name: 'courseid', - type: 'INTEGER', - }, - { - name: 'concept', - type: 'TEXT', - }, - { - name: 'definition', - type: 'TEXT', - }, - { - name: 'definitionformat', - type: 'TEXT', - }, - { - name: 'userid', - type: 'INTEGER', - }, - { - name: 'timecreated', - type: 'INTEGER', - }, - { - name: 'options', - type: 'TEXT', - }, - { - name: 'attachments', - type: 'TEXT', - }, - ], - primaryKeys: ['glossaryid', 'concept', 'timecreated'] - } - ] - }; - - constructor(private fileProvider: CoreFileProvider, - private sitesProvider: CoreSitesProvider, - private textUtils: CoreTextUtilsProvider, - private utils: CoreUtilsProvider) { - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete a new entry. - * - * @param glossaryId Glossary ID. - * @param concept Glossary entry concept. - * @param timeCreated The time the entry was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteNewEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - glossaryid: glossaryId, - concept: concept, - timecreated: timeCreated, - }; - - return site.getDb().deleteRecords(AddonModGlossaryOfflineProvider.ENTRIES_TABLE, conditions); - }); - } - - /** - * Get all the stored new entries from all the glossaries. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with entries. - */ - getAllNewEntries(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModGlossaryOfflineProvider.ENTRIES_TABLE).then((records: any[]) => { - return records.map(this.parseRecord.bind(this)); - }); - }); - } - - /** - * Get a stored new entry. - * - * @param glossaryId Glossary ID. - * @param concept Glossary entry concept. - * @param timeCreated The time the entry was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with entry. - */ - getNewEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - glossaryid: glossaryId, - concept: concept, - timecreated: timeCreated, - }; - - return site.getDb().getRecord(AddonModGlossaryOfflineProvider.ENTRIES_TABLE, conditions) - .then(this.parseRecord.bind(this)); - }); - } - - /** - * Get all the stored add entry data from a certain glossary. - * - * @param glossaryId Glossary ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User the entries belong to. If not defined, current user in site. - * @return Promise resolved with entries. - */ - getGlossaryNewEntries(glossaryId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - glossaryid: glossaryId, - userId: userId || site.getUserId(), - }; - - return site.getDb().getRecords(AddonModGlossaryOfflineProvider.ENTRIES_TABLE, conditions).then((records: any[]) => { - return records.map(this.parseRecord.bind(this)); - }); - }); - } - - /** - * Check if a concept is used offline. - * - * @param glossaryId Glossary ID. - * @param concept Concept to check. - * @param timeCreated Time of the entry we are editing. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if concept is found, false otherwise. - */ - isConceptUsed(glossaryId: number, concept: string, timeCreated?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - glossaryid: glossaryId, - concept: concept, - }; - - return site.getDb().getRecords(AddonModGlossaryOfflineProvider.ENTRIES_TABLE, conditions).then((entries) => { - if (!entries.length) { - return false; - } - - if (entries.length > 1 || !timeCreated) { - return true; - } - - // If there's only one entry, check that is not the one we are editing. - return this.utils.promiseFails(this.getNewEntry(glossaryId, concept, timeCreated, siteId)); - }); - }).catch(() => { - // No offline data found, return false. - return false; - }); - } - - /** - * Save a new entry to be sent later. - * - * @param glossaryId Glossary ID. - * @param concept Glossary entry concept. - * @param definition Glossary entry concept definition. - * @param courseId Course ID of the glossary. - * @param options Options for the entry. - * @param attachments Result of CoreFileUploaderProvider#storeFilesToUpload for attachments. - * @param timeCreated The time the entry was created. If not defined, current time. - * @param siteId Site ID. If not defined, current site. - * @param userId User the entry belong to. If not defined, current user in site. - * @param discardEntry The entry provided will be discarded if found. - * @return Promise resolved if stored, rejected if failure. - */ - addNewEntry(glossaryId: number, concept: string, definition: string, courseId: number, options?: any, attachments?: any, - timeCreated?: number, siteId?: string, userId?: number, discardEntry?: any): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - glossaryid: glossaryId, - courseid: courseId, - concept: concept, - definition: definition, - definitionformat: 'html', - options: JSON.stringify(options), - attachments: JSON.stringify(attachments), - userid: userId || site.getUserId(), - timecreated: timeCreated || new Date().getTime() - }; - - // If editing an offline entry, delete previous first. - let discardPromise; - if (discardEntry) { - discardPromise = this.deleteNewEntry(glossaryId, discardEntry.concept, discardEntry.timecreated, site.getId()); - } else { - discardPromise = Promise.resolve(); - } - - return discardPromise.then(() => { - return site.getDb().insertRecord(AddonModGlossaryOfflineProvider.ENTRIES_TABLE, entry).then(() => false); - }); - }); - } - - /** - * Get the path to the folder where to store files for offline attachments in a glossary. - * - * @param glossaryId Glossary ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getGlossaryFolder(glossaryId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const siteFolderPath = this.fileProvider.getSiteFolder(site.getId()); - const folderPath = 'offlineglossary/' + glossaryId; - - return this.textUtils.concatenatePaths(siteFolderPath, folderPath); - }); - } - - /** - * Get the path to the folder where to store files for a new offline entry. - * - * @param glossaryId Glossary ID. - * @param concept The name of the entry. - * @param timeCreated Time to allow duplicated entries. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getEntryFolder(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise { - return this.getGlossaryFolder(glossaryId, siteId).then((folderPath) => { - return this.textUtils.concatenatePaths(folderPath, 'newentry_' + concept + '_' + timeCreated); - }); - } - - /** - * Parse "options" and "attachments" columns of a fetched record. - * - * @param records Record object - * @return Record object with columns parsed. - */ - protected parseRecord(record: any): any { - record.options = this.textUtils.parseJSON(record.options); - record.attachments = this.textUtils.parseJSON(record.attachments); - - return record; - } -} diff --git a/src/addon/mod/glossary/providers/prefetch-handler.ts b/src/addon/mod/glossary/providers/prefetch-handler.ts deleted file mode 100644 index cfb5c4785..000000000 --- a/src/addon/mod/glossary/providers/prefetch-handler.ts +++ /dev/null @@ -1,231 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCommentsProvider } from '@core/comments/providers/comments'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { AddonModGlossaryProvider } from './glossary'; -import { AddonModGlossarySyncProvider } from './sync'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; -import { CoreUserProvider } from '@core/user/providers/user'; - -/** - * Handler to prefetch forums. - */ -@Injectable() -export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModGlossary'; - modName = 'glossary'; - component = AddonModGlossaryProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^entries$/; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected glossaryProvider: AddonModGlossaryProvider, - protected commentsProvider: CoreCommentsProvider, - protected syncProvider: AddonModGlossarySyncProvider, - protected userProvider: CoreUserProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Get list of files. If not defined, we'll assume they're in module.contents. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the list of files. - */ - getFiles(module: any, courseId: number, single?: boolean): Promise { - return this.glossaryProvider.getGlossary(courseId, module.id).then((glossary) => { - return this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter, [glossary.id, 'ALL'], { - cmId: module.id, - }).then((entries) => { - return this.getFilesFromGlossaryAndEntries(module, glossary, entries); - }); - }).catch(() => { - // Glossary not found, return empty list. - return []; - }); - } - - /** - * Get the list of downloadable files. It includes entry embedded files. - * - * @param module Module to get the files. - * @param glossary Glossary - * @param entries Entries of the Glossary. - * @return List of Files. - */ - protected getFilesFromGlossaryAndEntries(module: any, glossary: any, entries: any[]): any[] { - let files = this.getIntroFilesFromInstance(module, glossary); - const getInlineFiles = this.sitesProvider.getCurrentSite() && - this.sitesProvider.getCurrentSite().isVersionGreaterEqualThan('3.2'); - - // Get entries files. - entries.forEach((entry) => { - files = files.concat(entry.attachments); - - if (getInlineFiles && entry.definitioninlinefiles && entry.definitioninlinefiles.length) { - files = files.concat(entry.definitioninlinefiles); - } else if (entry.definition && !getInlineFiles) { - files = files.concat(this.filepoolProvider.extractDownloadableFilesFromHtmlAsFakeFileObjects(entry.definition)); - } - }); - - return files; - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.glossaryProvider.invalidateContent(moduleId, courseId); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchGlossary.bind(this)); - } - - /** - * Prefetch a glossary. - * - * @param module The module object returned by WS. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchGlossary(module: any, courseId: number, single: boolean, siteId: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const options = { - cmId: module.id, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - // Prefetch the glossary data. - return this.glossaryProvider.getGlossary(courseId, module.id, {siteId}).then((glossary) => { - const promises = []; - - glossary.browsemodes.forEach((mode) => { - switch (mode) { - case 'letter': // Always done. Look bellow. - break; - case 'cat': // Not implemented. - promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByCategory, - [glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATEGORIES], options)); - break; - case 'date': - promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByDate, - [glossary.id, 'CREATION', 'DESC'], options)); - promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByDate, - [glossary.id, 'UPDATE', 'DESC'], options)); - break; - case 'author': - promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByAuthor, - [glossary.id, 'ALL', 'LASTNAME', 'ASC'], options)); - break; - default: - } - }); - - // Fetch all entries to get information from. - promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter, [glossary.id, 'ALL'], - options).then((entries) => { - const promises = []; - const commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(); - - entries.forEach((entry) => { - // Don't fetch individual entries, it's too many WS calls. - - if (glossary.allowcomments && commentsEnabled) { - promises.push(this.commentsProvider.getComments('module', glossary.coursemodule, 'mod_glossary', entry.id, - 'glossary_entry', 0, siteId)); - } - }); - - const files = this.getFilesFromGlossaryAndEntries(module, glossary, entries); - promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id)); - - // Prefetch user avatars. - promises.push(this.userProvider.prefetchUserAvatars(entries, 'userpictureurl', siteId)); - - return Promise.all(promises); - })); - - // Get all categories. - promises.push(this.glossaryProvider.getAllCategories(glossary.id, options)); - - // Prefetch data for link handlers. - promises.push(this.courseProvider.getModuleBasicInfo(module.id, siteId)); - promises.push(this.courseProvider.getModuleBasicInfoByInstance(glossary.id, 'glossary', siteId)); - - return Promise.all(promises); - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - const promises = [ - this.syncProvider.syncGlossaryEntries(module.instance, undefined, siteId), - this.syncProvider.syncRatings(module.id, undefined, siteId) - ]; - - return Promise.all(promises).then((results) => { - return results.reduce((a, b) => ({ - updated: a.updated || b.updated, - warnings: (a.warnings || []).concat(b.warnings || []), - }), {updated: false}); - }); - } -} diff --git a/src/addon/mod/glossary/providers/sync-cron-handler.ts b/src/addon/mod/glossary/providers/sync-cron-handler.ts deleted file mode 100644 index 0eb156fe1..000000000 --- a/src/addon/mod/glossary/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModGlossarySyncProvider } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModGlossarySyncCronHandler implements CoreCronHandler { - name = 'AddonModGlossarySyncCronHandler'; - - constructor(private glossarySync: AddonModGlossarySyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.glossarySync.syncAllGlossaries(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.glossarySync.syncInterval; - } -} diff --git a/src/addon/mod/glossary/providers/sync.ts b/src/addon/mod/glossary/providers/sync.ts deleted file mode 100644 index 87ea9ba5c..000000000 --- a/src/addon/mod/glossary/providers/sync.ts +++ /dev/null @@ -1,369 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSyncBaseProvider } from '@classes/base-sync'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreAppProvider } from '@providers/app'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModGlossaryProvider } from './glossary'; -import { AddonModGlossaryHelperProvider } from './helper'; -import { AddonModGlossaryOfflineProvider } from './offline'; -import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; - -/** - * Service to sync glossaries. - */ -@Injectable() -export class AddonModGlossarySyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_glossary_autom_synced'; - - protected componentTranslate: string; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - courseProvider: CoreCourseProvider, - private eventsProvider: CoreEventsProvider, - loggerProvider: CoreLoggerProvider, - sitesProvider: CoreSitesProvider, - syncProvider: CoreSyncProvider, - textUtils: CoreTextUtilsProvider, - timeUtils: CoreTimeUtilsProvider, - private uploaderProvider: CoreFileUploaderProvider, - private utils: CoreUtilsProvider, - private glossaryProvider: AddonModGlossaryProvider, - private glossaryHelper: AddonModGlossaryHelperProvider, - private glossaryOffline: AddonModGlossaryOfflineProvider, - private logHelper: CoreCourseLogHelperProvider, - private ratingSync: CoreRatingSyncProvider) { - - super('AddonModGlossarySyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils); - - this.componentTranslate = courseProvider.translateModuleName('glossary'); - } - - /** - * Try to synchronize all the glossaries in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllGlossaries(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all glossaries', this.syncAllGlossariesFunc.bind(this), [force], siteId); - } - - /** - * Sync all glossaries on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllGlossariesFunc(siteId: string, force?: boolean): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - // Sync all new entries - promises.push(this.glossaryOffline.getAllNewEntries(siteId).then((entries) => { - const promises = {}; - - // Do not sync same glossary twice. - for (const i in entries) { - const entry = entries[i]; - - if (typeof promises[entry.glossaryid] != 'undefined') { - continue; - } - - promises[entry.glossaryid] = force ? this.syncGlossaryEntries(entry.glossaryid, entry.userid, siteId) : - this.syncGlossaryEntriesIfNeeded(entry.glossaryid, entry.userid, siteId); - - promises[entry.glossaryid].then((result) => { - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModGlossarySyncProvider.AUTO_SYNCED, { - glossaryId: entry.glossaryid, - userId: entry.userid, - warnings: result.warnings - }, siteId); - } - }); - } - - // Promises will be an object so, convert to an array first; - return Promise.all(this.utils.objectToArray(promises)); - })); - - promises.push(this.syncRatings(undefined, force, siteId)); - - return Promise.all(promises); - } - - /** - * Sync a glossary only if a certain time has passed since the last time. - * - * @param glossaryId Glossary ID. - * @param userId User the entry belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the glossary is synced or if it doesn't need to be synced. - */ - syncGlossaryEntriesIfNeeded(glossaryId: number, userId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = this.getGlossarySyncId(glossaryId, userId); - - return this.isSyncNeeded(syncId, siteId).then((needed) => { - if (needed) { - return this.syncGlossaryEntries(glossaryId, userId, siteId); - } - }); - } - - /** - * Synchronize all offline entries of a glossary. - * - * @param glossaryId Glossary ID to be synced. - * @param userId User the entries belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncGlossaryEntries(glossaryId: number, userId?: number, siteId?: string): Promise { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = this.getGlossarySyncId(glossaryId, userId); - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this glossary, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - // Verify that glossary isn't blocked. - if (this.syncProvider.isBlocked(AddonModGlossaryProvider.COMPONENT, syncId, siteId)) { - this.logger.debug('Cannot sync glossary ' + glossaryId + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug('Try to sync glossary ' + glossaryId + ' for user ' + userId); - - let courseId; - const result = { - warnings: [], - updated: false - }; - - // Sync offline logs. - const syncPromise = this.logHelper.syncIfNeeded(AddonModGlossaryProvider.COMPONENT, glossaryId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Get offline responses to be sent. - return this.glossaryOffline.getGlossaryNewEntries(glossaryId, siteId, userId).catch(() => { - // No offline data found, return empty object. - return []; - }); - }).then((entries) => { - if (!entries.length) { - // Nothing to sync. - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - const promises = []; - - entries.forEach((data) => { - let promise; - - courseId = data.courseid; - - // First of all upload the attachments (if any). - promise = this.uploadAttachments(glossaryId, data, siteId).then((itemId) => { - // Now try to add the entry. - return this.glossaryProvider.addEntryOnline( - glossaryId, data.concept, data.definition, data.options, itemId, siteId); - }); - - promises.push(promise.then(() => { - result.updated = true; - - return this.deleteAddEntry(glossaryId, data.concept, data.timecreated, siteId); - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. Delete them. - result.updated = true; - - return this.deleteAddEntry(glossaryId, data.concept, data.timecreated, siteId).then(() => { - // Responses deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.concept, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - })); - }); - - return Promise.all(promises); - }).then(() => { - if (result.updated && courseId) { - // Data has been sent to server. Now invalidate the WS calls. - return this.glossaryProvider.getGlossaryById(courseId, glossaryId).then((glossary) => { - return this.glossaryProvider.invalidateGlossaryEntries(glossary, true); - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(syncId, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // All done, return the warnings. - return result; - }); - - return this.addOngoingSync(syncId, syncPromise, siteId); - } - - /** - * Synchronize offline ratings. - * - * @param cmId Course module to be synced. If not defined, sync all glossaries. - * @param force Wether to force sync not depending on last execution. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncRatings(cmId?: number, force?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.ratingSync.syncRatings('mod_glossary', 'entry', 'module', cmId, 0, force, siteId).then((results) => { - let updated = false; - const warnings = []; - const promises = []; - - results.forEach((result) => { - if (result.updated.length) { - updated = true; - - // Invalidate entry of updated ratings. - result.updated.forEach((itemId) => { - promises.push(this.glossaryProvider.invalidateEntry(itemId, siteId)); - }); - } - if (result.warnings.length) { - promises.push(this.glossaryProvider.getGlossary(result.itemSet.courseId, result.itemSet.instanceId, {siteId}) - .then((glossary) => { - result.warnings.forEach((warning) => { - warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: glossary.name, - error: warning - })); - }); - })); - } - }); - - return this.utils.allPromises(promises).then(() => { - return { updated, warnings }; - }); - }); - } - - /** - * Delete a new entry. - * - * @param glossaryId Glossary ID. - * @param concept Glossary entry concept. - * @param timeCreated Time to allow duplicated entries. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when deleted. - */ - protected deleteAddEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise { - const promises = []; - - promises.push(this.glossaryOffline.deleteNewEntry(glossaryId, concept, timeCreated, siteId)); - promises.push(this.glossaryHelper.deleteStoredFiles(glossaryId, concept, timeCreated, siteId).catch(() => { - // Ignore errors, maybe there are no files. - })); - - return Promise.all(promises); - } - - /** - * Upload attachments of an offline entry. - * - * @param glossaryId Glossary ID. - * @param entry Offline entry. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with draftid if uploaded, resolved with 0 if nothing to upload. - */ - protected uploadAttachments(glossaryId: number, entry: any, siteId?: string): Promise { - if (entry.attachments) { - // Has some attachments to sync. - let files = entry.attachments.online || []; - let promise; - - if (entry.attachments.offline) { - // Has offline files. - promise = this.glossaryHelper.getStoredFiles(glossaryId, entry.concept, entry.timecreated, siteId).then((atts) => { - files = files.concat(atts); - }).catch(() => { - // Folder not found, no files to add. - }); - } else { - promise = Promise.resolve(0); - } - - return promise.then(() => { - return this.uploaderProvider.uploadOrReuploadFiles(files, AddonModGlossaryProvider.COMPONENT, glossaryId, siteId); - }); - } - - // No attachments, resolve. - return Promise.resolve(0); - } - - /** - * Get the ID of a glossary sync. - * - * @param glossaryId Glossary ID. - * @param userId User the entries belong to.. If not defined, current user. - * @return Sync ID. - */ - protected getGlossarySyncId(glossaryId: number, userId?: number): string { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - - return 'glossary#' + glossaryId + '#' + userId; - } -} diff --git a/src/addon/mod/glossary/providers/tag-area-handler.ts b/src/addon/mod/glossary/providers/tag-area-handler.ts deleted file mode 100644 index a7323de00..000000000 --- a/src/addon/mod/glossary/providers/tag-area-handler.ts +++ /dev/null @@ -1,57 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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'; - -/** - * Handler to support tags. - */ -@Injectable() -export class AddonModGlossaryTagAreaHandler implements CoreTagAreaHandler { - name = 'AddonModGlossaryTagAreaHandler'; - type = 'mod_glossary/glossary_entries'; - - constructor(private tagHelper: CoreTagHelperProvider) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Parses the rendered content of a tag index and returns the items. - * - * @param content Rendered content. - * @return Area items (or promise resolved with the items). - */ - parseContent(content: string): any[] | Promise { - return this.tagHelper.parseFeedContent(content); - } - - /** - * Get the component to use to display items. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return CoreTagFeedComponent; - } -} diff --git a/src/addon/mod/h5pactivity/components/components.module.ts b/src/addon/mod/h5pactivity/components/components.module.ts deleted file mode 100644 index 67ba634d8..000000000 --- a/src/addon/mod/h5pactivity/components/components.module.ts +++ /dev/null @@ -1,47 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { CoreH5PComponentsModule } from '@core/h5p/components/components.module'; -import { AddonModH5PActivityIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModH5PActivityIndexComponent, - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule, - CoreH5PComponentsModule, - ], - providers: [ - ], - exports: [ - AddonModH5PActivityIndexComponent, - ], - entryComponents: [ - AddonModH5PActivityIndexComponent, - ] -}) -export class AddonModH5PActivityComponentsModule {} diff --git a/src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html b/src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html deleted file mode 100644 index cd0c7f9e8..000000000 --- a/src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - {{ 'core.hasdatatosync' | translate:{$a: moduleName} }} - - - - - {{ 'core.h5p.offlinedisabled' | translate }} {{ 'addon.mod_h5pactivity.offlinedisabledwarning' | translate }} - - - - - {{ 'addon.mod_h5pactivity.previewmode' | translate }} - - - - -

{{ stateMessage | translate }}

-
- - - - {{ 'addon.mod_h5pactivity.downloadh5pfile' | translate }} - - - - - -

{{ progressMessage | translate }}

- -
-
- - -
diff --git a/src/addon/mod/h5pactivity/components/index/index.ts b/src/addon/mod/h5pactivity/components/index/index.ts deleted file mode 100644 index c0f1077e7..000000000 --- a/src/addon/mod/h5pactivity/components/index/index.ts +++ /dev/null @@ -1,475 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector } from '@angular/core'; -import { Content } from 'ionic-angular'; - -import { CoreApp } from '@providers/app'; -import { CoreEvents } from '@providers/events'; -import { CoreFilepool } from '@providers/filepool'; -import { CoreWSExternalFile } from '@providers/ws'; -import { CoreDomUtils } from '@providers/utils/dom'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreH5P } from '@core/h5p/providers/h5p'; -import { CoreH5PDisplayOptions } from '@core/h5p/classes/core'; -import { CoreH5PHelper } from '@core/h5p/classes/helper'; -import { CoreXAPI } from '@core/xapi/providers/xapi'; -import { CoreXAPIOffline } from '@core/xapi/providers/offline'; -import { CoreConstants } from '@core/constants'; -import { CoreSite } from '@classes/site'; - -import { - AddonModH5PActivity, AddonModH5PActivityProvider, AddonModH5PActivityData, AddonModH5PActivityAccessInfo -} from '../../providers/h5pactivity'; -import { AddonModH5PActivitySyncProvider, AddonModH5PActivitySync } from '../../providers/sync'; - -/** - * Component that displays an H5P activity entry page. - */ -@Component({ - selector: 'addon-mod-h5pactivity-index', - templateUrl: 'addon-mod-h5pactivity-index.html', -}) -export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActivityComponent { - component = AddonModH5PActivityProvider.COMPONENT; - moduleName = 'h5pactivity'; - - h5pActivity: AddonModH5PActivityData; // The H5P activity object. - accessInfo: AddonModH5PActivityAccessInfo; // Info about the user capabilities. - deployedFile: CoreWSExternalFile; // The H5P deployed file. - - stateMessage: string; // Message about the file state. - downloading: boolean; // Whether the H5P file is being downloaded. - needsDownload: boolean; // Whether the file needs to be downloaded. - percentage: string; // Download/unzip percentage. - progressMessage: string; // Message about download/unzip. - playing: boolean; // Whether the package is being played. - displayOptions: CoreH5PDisplayOptions; // Display options for the package. - onlinePlayerUrl: string; // URL to play the package in online. - fileUrl: string; // The fileUrl to use to play the package. - state: string; // State of the file. - siteCanDownload: boolean; - trackComponent: string; // Component for tracking. - hasOffline: boolean; - isOpeningPage: boolean; - - protected fetchContentDefaultError = 'addon.mod_h5pactivity.errorgetactivity'; - protected syncEventName = AddonModH5PActivitySyncProvider.AUTO_SYNCED; - protected site: CoreSite; - protected observer; - protected messageListenerFunction: (event: MessageEvent) => Promise; - - constructor(injector: Injector, - @Optional() protected content: Content) { - super(injector, content); - - this.site = this.sitesProvider.getCurrentSite(); - this.siteCanDownload = this.site.canDownloadFiles() && !CoreH5P.instance.isOfflineDisabledInSite(); - - // Listen for messages from the iframe. - this.messageListenerFunction = this.onIframeMessage.bind(this); - window.addEventListener('message', this.messageListenerFunction); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.loadContent(); - } - - /** - * Check the completion. - */ - protected checkCompletion(): void { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - - /** - * Get the activity data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - try { - this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(this.courseId, this.module.id, { - siteId: this.siteId, - }); - - this.dataRetrieved.emit(this.h5pActivity); - this.description = this.h5pActivity.intro; - this.displayOptions = CoreH5PHelper.decodeDisplayOptions(this.h5pActivity.displayoptions); - - if (sync) { - await this.syncActivity(showErrors); - } - - await Promise.all([ - this.checkHasOffline(), - this.fetchAccessInfo(), - this.fetchDeployedFileData(), - ]); - - this.trackComponent = this.accessInfo.cansubmit ? AddonModH5PActivityProvider.TRACK_COMPONENT : ''; - - if (this.h5pActivity.package && this.h5pActivity.package[0]) { - // The online player should use the original file, not the trusted one. - this.onlinePlayerUrl = CoreH5P.instance.h5pPlayer.calculateOnlinePlayerUrl( - this.site.getURL(), this.h5pActivity.package[0].fileurl, this.displayOptions, this.trackComponent); - } - - if (!this.siteCanDownload || this.state == CoreConstants.DOWNLOADED) { - // Cannot download the file or already downloaded, play the package directly. - this.play(); - - } else if ((this.state == CoreConstants.NOT_DOWNLOADED || this.state == CoreConstants.OUTDATED) && - CoreFilepool.instance.shouldDownload(this.deployedFile.filesize) && CoreApp.instance.isOnline()) { - // Package is small, download it automatically. Don't block this function for this. - this.downloadAutomatically(); - } - } finally { - this.fillContextMenu(refresh); - } - } - - /** - * Fetch the access info and store it in the right variables. - * - * @return Promise resolved when done. - */ - protected async checkHasOffline(): Promise { - this.hasOffline = await CoreXAPIOffline.instance.contextHasStatements(this.h5pActivity.context, this.siteId); - } - - /** - * Fetch the access info and store it in the right variables. - * - * @return Promise resolved when done. - */ - protected async fetchAccessInfo(): Promise { - this.accessInfo = await AddonModH5PActivity.instance.getAccessInformation(this.h5pActivity.id, { - cmId: this.module.id, - siteId: this.siteId, - }); - } - - /** - * Fetch the deployed file data if needed and store it in the right variables. - * - * @return Promise resolved when done. - */ - protected async fetchDeployedFileData(): Promise { - if (!this.siteCanDownload) { - // Cannot download the file, no need to fetch the file data. - return; - } - - this.deployedFile = await AddonModH5PActivity.instance.getDeployedFile(this.h5pActivity, { - displayOptions: this.displayOptions, - siteId: this.siteId, - }); - - this.fileUrl = this.deployedFile.fileurl; - - // Listen for changes in the state. - const eventName = await CoreFilepool.instance.getFileEventNameByUrl(this.siteId, this.deployedFile.fileurl); - - if (!this.observer) { - this.observer = CoreEvents.instance.on(eventName, () => { - this.calculateFileState(); - }); - } - - await this.calculateFileState(); - } - - /** - * Calculate the state of the deployed file. - * - * @return Promise resolved when done. - */ - protected async calculateFileState(): Promise { - this.state = await CoreFilepool.instance.getFileStateByUrl(this.siteId, this.deployedFile.fileurl, - this.deployedFile.timemodified); - - this.showFileState(); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return AddonModH5PActivity.instance.invalidateActivityData(this.courseId); - } - - /** - * Displays some data based on the state of the main file. - */ - protected showFileState(): void { - - if (this.state == CoreConstants.OUTDATED) { - this.stateMessage = 'addon.mod_h5pactivity.filestateoutdated'; - this.needsDownload = true; - } else if (this.state == CoreConstants.NOT_DOWNLOADED) { - this.stateMessage = 'addon.mod_h5pactivity.filestatenotdownloaded'; - this.needsDownload = true; - } else if (this.state == CoreConstants.DOWNLOADING) { - this.stateMessage = ''; - - if (!this.downloading) { - // It's being downloaded right now but the view isn't tracking it. "Restore" the download. - this.downloadDeployedFile().then(() => { - this.play(); - }); - } - } else { - this.stateMessage = ''; - this.needsDownload = false; - } - } - - /** - * Download the file and play it. - * - * @param e Click event. - * @return Promise resolved when done. - */ - async downloadAndPlay(e: MouseEvent): Promise { - e && e.preventDefault(); - e && e.stopPropagation(); - - if (!CoreApp.instance.isOnline()) { - CoreDomUtils.instance.showErrorModal('core.networkerrormsg', true); - - return; - } - - try { - // Confirm the download if needed. - await CoreDomUtils.instance.confirmDownloadSize({ size: this.deployedFile.filesize, total: true }); - - await this.downloadDeployedFile(); - - if (!this.isDestroyed) { - this.play(); - } - - } catch (error) { - if (CoreDomUtils.instance.isCanceledError(error) || this.isDestroyed) { - // User cancelled or view destroyed, stop. - return; - } - - CoreDomUtils.instance.showErrorModalDefault(error, 'core.errordownloading', true); - } - } - - /** - * Download the file automatically. - * - * @return Promise resolved when done. - */ - protected async downloadAutomatically(): Promise { - try { - await this.downloadDeployedFile(); - - if (!this.isDestroyed) { - this.play(); - } - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, 'core.errordownloading', true); - } - } - - /** - * Download athe H5P deployed file or restores an ongoing download. - * - * @return Promise resolved when done. - */ - protected async downloadDeployedFile(): Promise { - this.downloading = true; - this.progressMessage = 'core.downloading'; - - try { - await CoreFilepool.instance.downloadUrl(this.siteId, this.deployedFile.fileurl, false, this.component, this.componentId, - this.deployedFile.timemodified, (data) => { - - if (!data) { - return; - } - - if (data.message) { - // Show a message. - this.progressMessage = data.message; - this.percentage = undefined; - } else if (typeof data.loaded != 'undefined') { - if (this.progressMessage == 'core.downloading') { - // Downloading package. - this.percentage = (Number(data.loaded / this.deployedFile.filesize) * 100).toFixed(1); - } else if (typeof data.total != 'undefined') { - // Unzipping package. - this.percentage = (Number(data.loaded / data.total) * 100).toFixed(1); - } else { - this.percentage = undefined; - } - } else { - this.percentage = undefined; - } - }); - - } finally { - this.progressMessage = undefined; - this.percentage = undefined; - this.downloading = false; - } - } - - /** - * Play the package. - */ - play(): void { - this.playing = true; - - // Mark the activity as viewed. - AddonModH5PActivity.instance.logView(this.h5pActivity.id, this.h5pActivity.name, this.siteId); - } - - /** - * Go to view user events. - */ - async viewMyAttempts(): Promise { - this.isOpeningPage = true; - - try { - await this.navCtrl.push('AddonModH5PActivityUserAttemptsPage', { - courseId: this.courseId, - h5pActivityId: this.h5pActivity.id, - }); - } finally { - this.isOpeningPage = false; - } - } - - /** - * Treat an iframe message event. - * - * @param event Event. - * @return Promise resolved when done. - */ - protected async onIframeMessage(event: MessageEvent): Promise { - if (!event.data || !CoreXAPI.instance.canPostStatementsInSite(this.site) || !this.isCurrentXAPIPost(event.data)) { - return; - } - - try { - const options = { - offline: this.hasOffline, - courseId: this.courseId, - extra: this.h5pActivity.name, - siteId: this.site.getId(), - }; - - const sent = await CoreXAPI.instance.postStatements(this.h5pActivity.context, event.data.component, - JSON.stringify(event.data.statements), options); - - this.hasOffline = !sent; - - if (sent) { - try { - // Invalidate attempts. - await AddonModH5PActivity.instance.invalidateUserAttempts(this.h5pActivity.id, undefined, this.siteId); - } catch (error) { - // Ignore errors. - } - } - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, 'Error sending tracking data.'); - } - } - - /** - * Check if an event is an XAPI post statement of the current activity. - * - * @param data Event data. - * @return Whether it's an XAPI post statement of the current activity. - */ - protected isCurrentXAPIPost(data: any): boolean { - if (data.environment != 'moodleapp' || data.context != 'h5p' || data.action != 'xapi_post_statement' || !data.statements) { - return false; - } - - // Check the event belongs to this activity. - const trackingUrl = data.statements[0] && data.statements[0].object && data.statements[0].object.id; - if (!trackingUrl) { - return false; - } - - if (!this.site.containsUrl(trackingUrl)) { - // The event belongs to another site, weird scenario. Maybe some JS running in background. - return false; - } - - const match = trackingUrl.match(/xapi\/activity\/(\d+)/); - - return match && match[1] == this.h5pActivity.context; - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return AddonModH5PActivitySync.instance.syncActivity(this.h5pActivity.context, this.site.getId()); - } - - /** - * An autosync event has been received. - * - * @param syncEventData Data receiven on sync observer. - */ - protected autoSyncEventReceived(syncEventData: any): void { - this.checkHasOffline(); - } - - /** - * Go to blog posts. - * - * @param event Event. - */ - async gotoBlog(event: any): Promise { - this.isOpeningPage = true; - - try { - await super.gotoBlog(event); - } finally { - this.isOpeningPage = false; - } - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.observer && this.observer.off(); - window.removeEventListener('message', this.messageListenerFunction); - } -} diff --git a/src/addon/mod/h5pactivity/h5pactivity.module.ts b/src/addon/mod/h5pactivity/h5pactivity.module.ts deleted file mode 100644 index 85cce71a2..000000000 --- a/src/addon/mod/h5pactivity/h5pactivity.module.ts +++ /dev/null @@ -1,70 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; - -import { CoreCronDelegate } from '@providers/cron'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; - -import { AddonModH5PActivityComponentsModule } from './components/components.module'; -import { AddonModH5PActivityModuleHandler } from './providers/module-handler'; -import { AddonModH5PActivityProvider } from './providers/h5pactivity'; -import { AddonModH5PActivitySyncProvider } from './providers/sync'; -import { AddonModH5PActivityPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModH5PActivityIndexLinkHandler } from './providers/index-link-handler'; -import { AddonModH5PActivityReportLinkHandler } from './providers/report-link-handler'; -import { AddonModH5PActivitySyncCronHandler } from './providers/sync-cron-handler'; - -// List of providers (without handlers). -export const ADDON_MOD_H5P_ACTIVITY_PROVIDERS: any[] = [ - AddonModH5PActivityProvider, - AddonModH5PActivitySyncProvider, -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModH5PActivityComponentsModule - ], - providers: [ - AddonModH5PActivityProvider, - AddonModH5PActivitySyncProvider, - AddonModH5PActivityModuleHandler, - AddonModH5PActivityPrefetchHandler, - AddonModH5PActivityIndexLinkHandler, - AddonModH5PActivityReportLinkHandler, - AddonModH5PActivitySyncCronHandler, - ] -}) -export class AddonModH5PActivityModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, - moduleHandler: AddonModH5PActivityModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, - prefetchHandler: AddonModH5PActivityPrefetchHandler, - linksDelegate: CoreContentLinksDelegate, - indexHandler: AddonModH5PActivityIndexLinkHandler, - reportLinkHandler: AddonModH5PActivityReportLinkHandler, - cronDelegate: CoreCronDelegate, - syncHandler: AddonModH5PActivitySyncCronHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - linksDelegate.registerHandler(indexHandler); - linksDelegate.registerHandler(reportLinkHandler); - cronDelegate.register(syncHandler); - } -} diff --git a/src/addon/mod/h5pactivity/lang/en.json b/src/addon/mod/h5pactivity/lang/en.json deleted file mode 100644 index 3ac109635..000000000 --- a/src/addon/mod/h5pactivity/lang/en.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "all_attempts": "All user attempts", - "answer_checked": "Answer checked", - "answer_correct": "Your answer is correct", - "answer_fail": "Incorrect answer", - "answer_incorrect": "Your answer is incorrect", - "answer_pass": "Correct answer", - "attempt": "Attempt", - "attempt_completion_no": "This attempt is not marked as completed", - "attempt_completion_yes": "This attempt is completed", - "attempt_success_fail": "Fail", - "attempt_success_pass": "Pass", - "attempt_success_unknown": "Not reported", - "attempts_none": "This user has no attempts to display.", - "completion": "Completion", - "downloadh5pfile": "Download H5P file", - "duration": "Duration", - "errorgetactivity": "Error getting H5P activity data.", - "filestatenotdownloaded": "The H5P package is not downloaded. You need to download it to be able to use it.", - "filestateoutdated": "The H5P package has been modified since the last download. You need to download it again to be able to use it.", - "maxscore": "Max score", - "modulenameplural": "H5P", - "myattempts": "My attempts", - "no_compatible_track": "This interaction ({{$a}}) does not provide tracking information or the tracking\n provided is not compatible with the current activity version.", - "offlinedisabledwarning": "You need to be online to view the H5P package.", - "outcome": "Outcome", - "previewmode": "This content is displayed in preview mode. No attempt tracking will be stored.", - "result_fill-in": "Fill-in text", - "result_other": "Unknown interaction type", - "review_my_attempts": "View my attempts", - "score": "Score", - "score_out_of": "{{$a.rawscore}} out of {{$a.maxscore}}", - "startdate": "Start date", - "totalscore": "Total score", - "viewattempt": "View attempt {{$a}}" -} \ No newline at end of file diff --git a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.html b/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.html deleted file mode 100644 index 4ff03aa34..000000000 --- a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - -

{{ 'addon.mod_h5pactivity.attempt' | translate }} #{{attempt.attempt}}: {{user.fullname}}

-
- - -

{{ 'addon.mod_h5pactivity.attempt' | translate }} #{{attempt.attempt}}

-
- - - - - -

{{ 'addon.mod_h5pactivity.startdate' | translate }}

-

{{ attempt.timecreated | coreFormatDate:'strftimedatetime' }}

-
- -

{{ 'addon.mod_h5pactivity.completion' | translate }}

-

- - {{ 'addon.mod_h5pactivity.attempt_completion_yes' | translate }} -

-

- - {{ 'addon.mod_h5pactivity.attempt_completion_no' | translate }} -

-
- -

{{ 'addon.mod_h5pactivity.duration' | translate }}

-

{{ attempt.durationReadable }}

-
- -

{{ 'addon.mod_h5pactivity.outcome' | translate }}

-

- - {{ 'addon.mod_h5pactivity.attempt_success_pass' | translate }} -

-

- - {{ 'addon.mod_h5pactivity.attempt_success_fail' | translate }} -

-

- {{ 'addon.mod_h5pactivity.attempt_success_unknown' | translate }} -

-
- -

{{ 'addon.mod_h5pactivity.totalscore' | translate }}

-

{{ 'addon.mod_h5pactivity.score_out_of' | translate:{$a: attempt} }}

-
-
-
- - - - - - - - - - - - - - - - {{ result.optionslabel }} - {{ result.correctlabel }} - {{ result.answerlabel }} - - - - - - - - - - - - - - - - - - - - - - -

{{ 'addon.mod_h5pactivity.score' | translate }}: {{ 'addon.mod_h5pactivity.score_out_of' | translate:{$a: result} }}

-
-
- - - - {{ 'addon.mod_h5pactivity.no_compatible_track' | translate:{$a: result.interactiontype} }} - -
-
-
-
-
- - - -

- - {{ answer.answer }} -

-

- - {{ answer.answer }} -

-

- {{ answer.answer }} -

-

- -

-

- -

-

- -

-
diff --git a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.module.ts b/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.module.ts deleted file mode 100644 index a692f5b14..000000000 --- a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModH5PActivityAttemptResultsPage } from './attempt-results'; - -@NgModule({ - declarations: [ - AddonModH5PActivityAttemptResultsPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonModH5PActivityAttemptResultsPage), - TranslateModule.forChild(), - ], -}) -export class AddonModH5PActivityAttemptResultsPageModule {} diff --git a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.scss b/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.scss deleted file mode 100644 index 04b50c7d4..000000000 --- a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.scss +++ /dev/null @@ -1,55 +0,0 @@ -ion-app.app-root page-addon-mod-h5pactivity-attempt-results { - - .addon-mod_h5pactivity-attempt-result-summary { - img { - width: 16px; - height: 16px; - display: inline; - @include margin-horizontal(0, 4px); - } - - .icon { - font-size: 1.4em; - } - } - - .addon-mod_h5pactivity-result-table-header .item-inner { - font-size: 0.9em; - font-weight: bold; - - .col[text-center] { - @include padding-horizontal(0); - } - } - - .addon-mod_h5pactivity-result-table-header, .addon-mod_h5pactivity-result-table-row { - - .item-inner ion-label { - @include margin(null, 0, null, null); - } - - .item { - @include padding(null, null, null, 0); - } - - .label { - margin-top: 0; - margin-bottom: 0; - } - - .icon { - font-size: 1.2em; - } - } - - .addon-mod_h5pactivity-result-table-row.item:nth-child(even) { - background-color: $gray-lighter; - @include darkmode() { - background-color: $core-dark-item-divider-bg-color; - } - } - - .addon-mod_h5pactivity-result-score { - border-top: 1px solid black; - } -} diff --git a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.ts b/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.ts deleted file mode 100644 index 996ddb0cf..000000000 --- a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.ts +++ /dev/null @@ -1,120 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreDomUtils } from '@providers/utils/dom'; -import { CoreUser } from '@core/user/providers/user'; -import { - AddonModH5PActivity, AddonModH5PActivityProvider, AddonModH5PActivityData, AddonModH5PActivityAttemptResults -} from '../../providers/h5pactivity'; - -/** - * Page that displays results of an attempt. - */ -@IonicPage({ segment: 'addon-mod-h5pactivity-attempt-results' }) -@Component({ - selector: 'page-addon-mod-h5pactivity-attempt-results', - templateUrl: 'attempt-results.html', -}) -export class AddonModH5PActivityAttemptResultsPage implements OnInit { - loaded: boolean; - h5pActivity: AddonModH5PActivityData; - attempt: AddonModH5PActivityAttemptResults; - user: any; - component = AddonModH5PActivityProvider.COMPONENT; - - protected courseId: number; - protected h5pActivityId: number; - protected attemptId: number; - - constructor(navParams: NavParams) { - this.courseId = navParams.get('courseId'); - this.h5pActivityId = navParams.get('h5pActivityId'); - this.attemptId = navParams.get('attemptId'); - } - - /** - * Component being initialized. - * - * @return Promise resolved when done. - */ - async ngOnInit(): Promise { - try { - await this.fetchData(); - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading attempt.'); - } finally { - this.loaded = true; - } - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - */ - doRefresh(refresher: any): void { - this.refreshData().finally(() => { - refresher.complete(); - }); - } - - /** - * Get quiz data and attempt data. - * - * @return Promise resolved when done. - */ - protected async fetchData(): Promise { - this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId); - - this.attempt = await AddonModH5PActivity.instance.getAttemptResults(this.h5pActivityId, this.attemptId, { - cmId: this.h5pActivity.coursemodule, - }); - - await this.fetchUserProfile(); - } - - /** - * Get user profile. - * - * @return Promise resolved when done. - */ - protected async fetchUserProfile(): Promise { - try { - this.user = await CoreUser.instance.getProfile(this.attempt.userid, this.courseId, true); - } catch (error) { - // Ignore errors. - } - } - - /** - * Refresh the data. - * - * @return Promise resolved when done. - */ - protected async refreshData(): Promise { - - try { - await Promise.all([ - AddonModH5PActivity.instance.invalidateActivityData(this.courseId), - AddonModH5PActivity.instance.invalidateAttemptResults(this.h5pActivityId, this.attemptId), - ]); - } catch (error) { - // Ignore errors. - } - - await this.fetchData(); - } -} diff --git a/src/addon/mod/h5pactivity/pages/index/index.html b/src/addon/mod/h5pactivity/pages/index/index.html deleted file mode 100644 index 420138c97..000000000 --- a/src/addon/mod/h5pactivity/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/h5pactivity/pages/index/index.module.ts b/src/addon/mod/h5pactivity/pages/index/index.module.ts deleted file mode 100644 index b75f46b1c..000000000 --- a/src/addon/mod/h5pactivity/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModH5PActivityComponentsModule } from '../../components/components.module'; -import { AddonModH5PActivityIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModH5PActivityIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModH5PActivityComponentsModule, - IonicPageModule.forChild(AddonModH5PActivityIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModH5PActivityIndexPageModule {} diff --git a/src/addon/mod/h5pactivity/pages/index/index.ts b/src/addon/mod/h5pactivity/pages/index/index.ts deleted file mode 100644 index e3bc7e304..000000000 --- a/src/addon/mod/h5pactivity/pages/index/index.ts +++ /dev/null @@ -1,65 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreDomUtils } from '@providers/utils/dom'; -import { AddonModH5PActivityIndexComponent } from '../../components/index/index'; -import { AddonModH5PActivityData } from '../../providers/h5pactivity'; - -import { Translate } from '@singletons/core.singletons'; - -/** - * Page that displays an H5P activity. - */ -@IonicPage({ segment: 'addon-mod-h5pactivity-index' }) -@Component({ - selector: 'page-addon-mod-h5pactivity-index', - templateUrl: 'index.html', -}) -export class AddonModH5PActivityIndexPage { - @ViewChild(AddonModH5PActivityIndexComponent) h5pComponent: AddonModH5PActivityIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the H5P activity instance. - * - * @param h5p H5P activity instance. - */ - updateData(h5p: AddonModH5PActivityData): void { - this.title = h5p.name || this.title; - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - ionViewCanLeave(): Promise { - if (!this.h5pComponent.playing || this.h5pComponent.isOpeningPage) { - return; - } - - return CoreDomUtils.instance.showConfirm(Translate.instance.instant('core.confirmleaveunknownchanges')); - } -} diff --git a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.html b/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.html deleted file mode 100644 index a7dc50709..000000000 --- a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - -

{{ user.fullname }}

-
- - -

{{ 'addon.mod_h5pactivity.myattempts' | translate }}

-
- - - - - -

{{ attemptsData.scored.title }}

-
- -
- - - - -

{{ 'addon.mod_h5pactivity.all_attempts' | translate }}

-
- -
-
- - - - -
-
- - - - - - - # - {{ 'core.date' | translate }} - {{ 'addon.mod_h5pactivity.score' | translate }} - {{ 'addon.mod_h5pactivity.maxscore' | translate }} - {{ 'addon.mod_h5pactivity.duration' | translate }} - {{ 'addon.mod_h5pactivity.completion' | translate }} - {{ 'core.success' | translate }} - - - - - - - {{ attempt.attempt }} - {{ attempt.timemodified | coreFormatDate:'strftimedatetimeshort' }} - - {{ attempt.rawscore }} / {{ attempt.maxscore }} - - {{ attempt.maxscore }} - {{ attempt.durationReadable }} - - - - - - - - - - - - \ No newline at end of file diff --git a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.module.ts b/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.module.ts deleted file mode 100644 index 8532f1bb4..000000000 --- a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModH5PActivityUserAttemptsPage } from './user-attempts'; - -@NgModule({ - declarations: [ - AddonModH5PActivityUserAttemptsPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonModH5PActivityUserAttemptsPage), - TranslateModule.forChild(), - ], -}) -export class AddonModH5PActivityUserAttemptsPageModule {} diff --git a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.scss b/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.scss deleted file mode 100644 index f6a9381b0..000000000 --- a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.scss +++ /dev/null @@ -1,37 +0,0 @@ -ion-app.app-root page-addon-mod-h5pactivity-user-attempts { - - .item.addon-mod_h5pactivity-table-header[detail-push] .item-inner { - background-image: none; - } - - .item.addon-mod_h5pactivity-table-header .item-inner { - font-size: 0.9em; - font-weight: bold; - - .col[text-center] { - @include padding-horizontal(0); - } - } - - .addon-mod_h5pactivity-table-header, .addon-mod_h5pactivity-table-row { - - .item-inner ion-label { - @include margin(null, 0, null, null); - } - - .item { - @include padding(null, null, null, 0); - } - - .label { - margin-top: 0; - margin-bottom: 0; - } - } - - .addon-mod_h5pactivity-table-row { - .addon-mod_h5pactivity-table-success-col { - font-size: 1.4em; - } - } -} diff --git a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.ts b/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.ts deleted file mode 100644 index bffee1f21..000000000 --- a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.ts +++ /dev/null @@ -1,133 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreSites } from '@providers/sites'; -import { CoreDomUtils } from '@providers/utils/dom'; -import { CoreUser } from '@core/user/providers/user'; -import { - AddonModH5PActivity, AddonModH5PActivityData, AddonModH5PActivityUserAttempts -} from '../../providers/h5pactivity'; - -/** - * Page that displays user attempts of a certain user. - */ -@IonicPage({ segment: 'addon-mod-h5pactivity-user-attempts' }) -@Component({ - selector: 'page-addon-mod-h5pactivity-user-attempts', - templateUrl: 'user-attempts.html', -}) -export class AddonModH5PActivityUserAttemptsPage implements OnInit { - loaded: boolean; - courseId: number; - h5pActivityId: number; - h5pActivity: AddonModH5PActivityData; - attemptsData: AddonModH5PActivityUserAttempts; - user: any; - isCurrentUser: boolean; - - protected userId: number; - - constructor(navParams: NavParams) { - this.courseId = navParams.get('courseId'); - this.h5pActivityId = navParams.get('h5pActivityId'); - this.userId = navParams.get('userId') || CoreSites.instance.getCurrentSiteUserId(); - this.isCurrentUser = this.userId == CoreSites.instance.getCurrentSiteUserId(); - } - - /** - * Component being initialized. - * - * @return Promise resolved when done. - */ - async ngOnInit(): Promise { - try { - await this.fetchData(); - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading attempts.'); - } finally { - this.loaded = true; - } - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - */ - doRefresh(refresher: any): void { - this.refreshData().finally(() => { - refresher.complete(); - }); - } - - /** - * Get quiz data and attempt data. - * - * @return Promise resolved when done. - */ - protected async fetchData(): Promise { - this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId); - - await Promise.all([ - this.fetchAttempts(), - this.fetchUserProfile(), - ]); - } - - /** - * Get attempts. - * - * @return Promise resolved when done. - */ - protected async fetchAttempts(): Promise { - this.attemptsData = await AddonModH5PActivity.instance.getUserAttempts(this.h5pActivityId, { - cmId: this.h5pActivity.coursemodule, - userId: this.userId, - }); - } - - /** - * Get user profile. - * - * @return Promise resolved when done. - */ - protected async fetchUserProfile(): Promise { - try { - this.user = await CoreUser.instance.getProfile(this.userId, this.courseId, true); - } catch (error) { - // Ignore errors. - } - } - - /** - * Refresh the data. - * - * @return Promise resolved when done. - */ - protected async refreshData(): Promise { - - try { - await Promise.all([ - AddonModH5PActivity.instance.invalidateActivityData(this.courseId), - AddonModH5PActivity.instance.invalidateUserAttempts(this.h5pActivityId, this.userId), - ]); - } catch (error) { - // Ignore errors. - } - - await this.fetchData(); - } -} diff --git a/src/addon/mod/h5pactivity/providers/h5pactivity.ts b/src/addon/mod/h5pactivity/providers/h5pactivity.ts deleted file mode 100644 index 1cf09d1c8..000000000 --- a/src/addon/mod/h5pactivity/providers/h5pactivity.ts +++ /dev/null @@ -1,785 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; - -import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; -import { CoreTimeUtils } from '@providers/utils/time'; -import { CoreUtils } from '@providers/utils/utils'; -import { CoreSite } from '@classes/site'; -import { CoreCourseLogHelper } from '@core/course/providers/log-helper'; -import { CoreH5P } from '@core/h5p/providers/h5p'; -import { CoreH5PDisplayOptions } from '@core/h5p/classes/core'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -import { makeSingleton, Translate } from '@singletons/core.singletons'; - -/** - * Service that provides some features for H5P activity. - */ -@Injectable() -export class AddonModH5PActivityProvider { - static COMPONENT = 'mmaModH5PActivity'; - static TRACK_COMPONENT = 'mod_h5pactivity'; // Component for tracking. - - protected ROOT_CACHE_KEY = 'mmaModH5PActivity:'; - - /** - * Format an attempt's data. - * - * @param attempt Attempt to format. - */ - protected formatAttempt(attempt: AddonModH5PActivityWSAttempt): AddonModH5PActivityAttempt { - const formattedAttempt: AddonModH5PActivityAttempt = attempt; - - formattedAttempt.timecreated = attempt.timecreated * 1000; // Convert to milliseconds. - formattedAttempt.timemodified = attempt.timemodified * 1000; // Convert to milliseconds. - formattedAttempt.success = typeof formattedAttempt.success == 'undefined' ? null : formattedAttempt.success; - - if (!attempt.duration) { - formattedAttempt.durationReadable = '-'; - formattedAttempt.durationCompact = '-'; - } else { - formattedAttempt.durationReadable = CoreTimeUtils.instance.formatTime(attempt.duration); - formattedAttempt.durationCompact = CoreTimeUtils.instance.formatDurationShort(attempt.duration); - } - - return formattedAttempt; - } - - /** - * Format attempt data and results. - * - * @param attempt Attempt and results to format. - */ - protected formatAttemptResults(attempt: AddonModH5PActivityWSAttemptResults): AddonModH5PActivityAttemptResults { - const formattedAttempt: AddonModH5PActivityAttemptResults = this.formatAttempt(attempt); - - formattedAttempt.results = formattedAttempt.results.map((result) => { - return this.formatResult(result); - }); - - return formattedAttempt; - } - - /** - * Format the attempts of a user. - * - * @param data Data to format. - * @return Formatted data. - */ - protected formatUserAttempts(data: AddonModH5PActivityWSUserAttempts): AddonModH5PActivityUserAttempts { - const formatted: AddonModH5PActivityUserAttempts = data; - - formatted.attempts = formatted.attempts.map((attempt) => { - return this.formatAttempt(attempt); - }); - - if (formatted.scored) { - - formatted.scored.attempts = formatted.scored.attempts.map((attempt) => { - return this.formatAttempt(attempt); - }); - } - - return formatted; - } - - /** - * Format an attempt's result. - * - * @param result Result to format. - */ - protected formatResult(result: AddonModH5PActivityWSResult): AddonModH5PActivityWSResult { - result.timecreated = result.timecreated * 1000; // Convert to milliseconds. - - return result; - } - - /** - * Get cache key for access information WS calls. - * - * @param id H5P activity ID. - * @return Cache key. - */ - protected getAccessInformationCacheKey(id: number): string { - return this.ROOT_CACHE_KEY + 'accessInfo:' + id; - } - - /** - * Get access information for a given H5P activity. - * - * @param id H5P activity ID. - * @param options Other options. - * @return Promise resolved with the data. - */ - async getAccessInformation(id: number, options: CoreCourseCommonModWSOptions = {}): Promise { - - const site = await CoreSites.instance.getSite(options.siteId); - - const params = { - h5pactivityid: id, - }; - const preSets = { - cacheKey: this.getAccessInformationCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModH5PActivityProvider.COMPONENT, - componentId: options.cmId, - ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_h5pactivity_get_h5pactivity_access_information', params, preSets); - } - - /** - * Get attempt results for all user attempts. - * - * @param id Activity ID. - * @param options Other options. - * @return Promise resolved with the results of the attempt. - */ - async getAllAttemptsResults(id: number, options?: AddonModH5PActivityGetAttemptResultsOptions) - : Promise { - - const userAttempts = await AddonModH5PActivity.instance.getUserAttempts(id, options); - - const attemptIds = userAttempts.attempts.map((attempt) => { - return attempt.id; - }); - - if (attemptIds.length) { - // Get all the attempts with a single call. - return AddonModH5PActivity.instance.getAttemptsResults(id, attemptIds, options); - } else { - // No attempts. - return { - activityid: id, - attempts: [], - warnings: [], - }; - } - } - - /** - * Get cache key for results WS calls. - * - * @param id Instance ID. - * @param attemptsIds Attempts IDs. - * @return Cache key. - */ - protected getAttemptResultsCacheKey(id: number, attemptsIds: number[]): string { - return this.getAttemptResultsCommonCacheKey(id) + ':' + JSON.stringify(attemptsIds); - } - - /** - * Get common cache key for results WS calls. - * - * @param id Instance ID. - * @return Cache key. - */ - protected getAttemptResultsCommonCacheKey(id: number): string { - return this.ROOT_CACHE_KEY + 'results:' + id; - } - - /** - * Get attempt results. - * - * @param id Activity ID. - * @param attemptId Attempt ID. - * @param options Other options. - * @return Promise resolved with the results of the attempt. - */ - async getAttemptResults(id: number, attemptId: number, options?: AddonModH5PActivityGetAttemptResultsOptions) - : Promise { - - options = options || {}; - - const site = await CoreSites.instance.getSite(options.siteId); - - const params = { - h5pactivityid: id, - attemptids: [attemptId], - }; - const preSets = { - cacheKey: this.getAttemptResultsCacheKey(id, params.attemptids), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModH5PActivityProvider.COMPONENT, - componentId: options.cmId, - ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - try { - const response: AddonModH5PActivityGetResultsResult = await site.read('mod_h5pactivity_get_results', params, preSets); - - if (response.warnings[0]) { - throw response.warnings[0]; // Cannot view attempt. - } - - return this.formatAttemptResults(response.attempts[0]); - } catch (error) { - if (CoreUtils.instance.isWebServiceError(error)) { - throw error; - } - - // Check if the full list of results is cached. If so, get the results from there. - const cacheOptions = { - ...options, // Include all the original options. - readingStrategy: CoreSitesReadingStrategy.OnlyCache, - }; - - const attemptsResults = await AddonModH5PActivity.instance.getAllAttemptsResults(id, cacheOptions); - - const attempt = attemptsResults.attempts.find((attempt) => { - return attempt.id == attemptId; - }); - - if (!attempt) { - throw error; - } - - return attempt; - } - } - - /** - * Get attempts results. - * - * @param id Activity ID. - * @param attemptsIds Attempts IDs. - * @param options Other options. - * @return Promise resolved with all the attempts. - */ - async getAttemptsResults(id: number, attemptsIds: number[], options?: AddonModH5PActivityGetAttemptResultsOptions) - : Promise { - - options = options || {}; - - const site = await CoreSites.instance.getSite(options.siteId); - - const params = { - h5pactivityid: id, - attemptids: attemptsIds, - }; - const preSets = { - cacheKey: this.getAttemptResultsCommonCacheKey(id), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModH5PActivityProvider.COMPONENT, - componentId: options.cmId, - ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - const response: AddonModH5PActivityGetResultsResult = await site.read('mod_h5pactivity_get_results', params, preSets); - - response.attempts = response.attempts.map((attempt) => { - return this.formatAttemptResults(attempt); - }); - - return response; - } - - /** - * Get deployed file from an H5P activity instance. - * - * @param h5pActivity Activity instance. - * @param options Options - * @return Promise resolved with the file. - */ - async getDeployedFile(h5pActivity: AddonModH5PActivityData, options?: AddonModH5PActivityGetDeployedFileOptions) - : Promise { - - if (h5pActivity.deployedfile) { - // File already deployed and still valid, use this one. - return h5pActivity.deployedfile; - } else { - if (!h5pActivity.package || !h5pActivity.package[0]) { - // Shouldn't happen. - throw 'No H5P package found.'; - } - - options = options || {}; - - // Deploy the file in the server. - return CoreH5P.instance.getTrustedH5PFile(h5pActivity.package[0].fileurl, options.displayOptions, - options.ignoreCache, options.siteId); - } - } - - /** - * Get cache key for H5P activity data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getH5PActivityDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'h5pactivity:' + courseId; - } - - /** - * Get an H5P activity with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved with the activity data. - */ - protected async getH5PActivityByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - const site = await CoreSites.instance.getSite(options.siteId); - - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getH5PActivityDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModH5PActivityProvider.COMPONENT, - ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - const response: AddonModH5PActivityGetByCoursesResult = - await site.read('mod_h5pactivity_get_h5pactivities_by_courses', params, preSets); - - if (response && response.h5pactivities) { - const currentActivity = response.h5pactivities.find((h5pActivity) => { - return h5pActivity[key] == value; - }); - - if (currentActivity) { - return currentActivity; - } - } - - throw Translate.instance.instant('addon.mod_h5pactivity.errorgetactivity'); - } - - /** - * Get an H5P activity by module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved with the activity data. - */ - getH5PActivity(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getH5PActivityByField(courseId, 'coursemodule', cmId, options); - } - - /** - * Get an H5P activity by context ID. - * - * @param courseId Course ID. - * @param contextId Context ID. - * @param options Other options. - * @return Promise resolved with the activity data. - */ - getH5PActivityByContextId(courseId: number, contextId: number, options: CoreSitesCommonWSOptions = {}) - : Promise { - return this.getH5PActivityByField(courseId, 'context', contextId, options); - } - - /** - * Get an H5P activity by instance ID. - * - * @param courseId Course ID. - * @param id Instance ID. - * @param options Other options. - * @return Promise resolved with the activity data. - */ - getH5PActivityById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getH5PActivityByField(courseId, 'id', id, options); - } - - /** - * Get cache key for attemps WS calls. - * - * @param id Instance ID. - * @param userIds User IDs. - * @return Cache key. - */ - protected getUserAttemptsCacheKey(id: number, userIds: number[]): string { - return this.getUserAttemptsCommonCacheKey(id) + ':' + JSON.stringify(userIds); - } - - /** - * Get common cache key for attempts WS calls. - * - * @param id Instance ID. - * @return Cache key. - */ - protected getUserAttemptsCommonCacheKey(id: number): string { - return this.ROOT_CACHE_KEY + 'attempts:' + id; - } - - /** - * Get attempts of a certain user. - * - * @param id Activity ID. - * @param options Other options. - * @return Promise resolved with the attempts of the user. - */ - async getUserAttempts(id: number, options: AddonModH5PActivityGetAttemptsOptions = {}) - : Promise { - - const site = await CoreSites.instance.getSite(options.siteId); - - const params = { - h5pactivityid: id, - userids: [options.userId || site.getUserId()], - }; - const preSets = { - cacheKey: this.getUserAttemptsCacheKey(id, params.userids), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModH5PActivityProvider.COMPONENT, - componentId: options.cmId, - ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - const response: AddonModH5PActivityGetAttemptsResult = await site.read('mod_h5pactivity_get_attempts', params, preSets); - - if (response.warnings[0]) { - throw response.warnings[0]; // Cannot view user attempts. - } - - return this.formatUserAttempts(response.usersattempts[0]); - } - - /** - * Invalidates access information. - * - * @param id H5P activity ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - async invalidateAccessInformation(id: number, siteId?: string): Promise { - - const site = await CoreSites.instance.getSite(siteId); - - await site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(id)); - } - - /** - * Invalidates H5P activity data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - async invalidateActivityData(courseId: number, siteId?: string): Promise { - const site = await CoreSites.instance.getSite(siteId); - - await site.invalidateWsCacheForKey(this.getH5PActivityDataCacheKey(courseId)); - } - - /** - * Invalidates all attempts results for H5P activity. - * - * @param id Activity ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - async invalidateAllResults(id: number, siteId?: string): Promise { - const site = await CoreSites.instance.getSite(siteId); - - await site.invalidateWsCacheForKey(this.getAttemptResultsCommonCacheKey(id)); - } - - /** - * Invalidates results of a certain attempt for H5P activity. - * - * @param id Activity ID. - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - async invalidateAttemptResults(id: number, attemptId: number, siteId?: string): Promise { - const site = await CoreSites.instance.getSite(siteId); - - await site.invalidateWsCacheForKey(this.getAttemptResultsCacheKey(id, [attemptId])); - } - - /** - * Invalidates all users attempts for H5P activity. - * - * @param id Activity ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - async invalidateAllUserAttempts(id: number, siteId?: string): Promise { - const site = await CoreSites.instance.getSite(siteId); - - await site.invalidateWsCacheForKey(this.getUserAttemptsCommonCacheKey(id)); - } - - /** - * Invalidates attempts of a certain user for H5P activity. - * - * @param id Activity ID. - * @param userId User ID. If not defined, current user in the site. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - async invalidateUserAttempts(id: number, userId?: number, siteId?: string): Promise { - const site = await CoreSites.instance.getSite(siteId); - - userId = userId || site.getUserId(); - - await site.invalidateWsCacheForKey(this.getUserAttemptsCacheKey(id, [userId])); - } - - /** - * Delete launcher. - * - * @return Promise resolved when the launcher file is deleted. - */ - async isPluginEnabled(siteId?: string): Promise { - const site = await CoreSites.instance.getSite(siteId); - - return site.wsAvailable('mod_h5pactivity_get_h5pactivities_by_courses'); - } - - /** - * Report an H5P activity as being viewed. - * - * @param id H5P activity ID. - * @param name Name of the activity. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - h5pactivityid: id, - }; - - return CoreCourseLogHelper.instance.logSingle( - 'mod_h5pactivity_view_h5pactivity', - params, - AddonModH5PActivityProvider.COMPONENT, - id, - name, - 'h5pactivity', - {}, - siteId - ); - } -} - -export class AddonModH5PActivity extends makeSingleton(AddonModH5PActivityProvider) {} - -/** - * Basic data for an H5P activity, exported by Moodle class h5pactivity_summary_exporter. - */ -export type AddonModH5PActivityData = { - id: number; // The primary key of the record. - course: number; // Course id this h5p activity is part of. - name: string; // The name of the activity module instance. - timecreated?: number; // Timestamp of when the instance was added to the course. - timemodified?: number; // Timestamp of when the instance was last modified. - intro: string; // H5P activity description. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - grade?: number; // The maximum grade for submission. - displayoptions: number; // H5P Button display options. - enabletracking: number; // Enable xAPI tracking. - grademethod: number; // Which H5P attempt is used for grading. - contenthash?: string; // Sha1 hash of file content. - coursemodule: number; // Coursemodule. - context: number; // Context ID. - introfiles: CoreWSExternalFile[]; - package: CoreWSExternalFile[]; - deployedfile?: { - filename?: string; // File name. - filepath?: string; // File path. - filesize?: number; // File size. - fileurl?: string; // Downloadable file url. - timemodified?: number; // Time modified. - mimetype?: string; // File mime type. - }; -}; - -/** - * Result of WS mod_h5pactivity_get_h5pactivities_by_courses. - */ -export type AddonModH5PActivityGetByCoursesResult = { - h5pactivities: AddonModH5PActivityData[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_h5pactivity_get_h5pactivity_access_information. - */ -export type AddonModH5PActivityAccessInfo = { - warnings?: CoreWSExternalWarning[]; - canview?: boolean; // Whether the user has the capability mod/h5pactivity:view allowed. - canaddinstance?: boolean; // Whether the user has the capability mod/h5pactivity:addinstance allowed. - cansubmit?: boolean; // Whether the user has the capability mod/h5pactivity:submit allowed. - canreviewattempts?: boolean; // Whether the user has the capability mod/h5pactivity:reviewattempts allowed. -}; - -/** - * Result of WS mod_h5pactivity_get_attempts. - */ -export type AddonModH5PActivityGetAttemptsResult = { - activityid: number; // Activity course module ID. - usersattempts: AddonModH5PActivityWSUserAttempts[]; // The complete users attempts list. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_h5pactivity_get_results. - */ -export type AddonModH5PActivityGetResultsResult = { - activityid: number; // Activity course module ID. - attempts: AddonModH5PActivityWSAttemptResults[]; // The complete attempts list. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Attempts data for a user as returned by the WS mod_h5pactivity_get_attempts. - */ -export type AddonModH5PActivityWSUserAttempts = { - userid: number; // The user id. - attempts: AddonModH5PActivityWSAttempt[]; // The complete attempts list. - scored?: { // Attempts used to grade the activity. - title: string; // Scored attempts title. - grademethod: string; // Scored attempts title. - attempts: AddonModH5PActivityWSAttempt[]; // List of the grading attempts. - }; -}; - -/** - * Attempt data as returned by the WS mod_h5pactivity_get_attempts. - */ -export type AddonModH5PActivityWSAttempt = { - id: number; // ID of the context. - h5pactivityid: number; // ID of the H5P activity. - userid: number; // ID of the user. - timecreated: number; // Attempt creation. - timemodified: number; // Attempt modified. - attempt: number; // Attempt number. - rawscore: number; // Attempt score value. - maxscore: number; // Attempt max score. - duration: number; // Attempt duration in seconds. - completion?: number; // Attempt completion. - success?: number; // Attempt success. - scaled: number; // Attempt scaled. -}; - -/** - * Attempt and results data as returned by the WS mod_h5pactivity_get_results. - */ -export type AddonModH5PActivityWSAttemptResults = AddonModH5PActivityWSAttempt & { - results?: AddonModH5PActivityWSResult[]; // The results of the attempt. -}; - -/** - * Attempt result data as returned by the WS mod_h5pactivity_get_results. - */ -export type AddonModH5PActivityWSResult = { - id: number; // ID of the context. - attemptid: number; // ID of the H5P attempt. - subcontent: string; // Subcontent identifier. - timecreated: number; // Result creation. - interactiontype: string; // Interaction type. - description: string; // Result description. - content?: string; // Result extra content. - rawscore: number; // Result score value. - maxscore: number; // Result max score. - duration?: number; // Result duration in seconds. - completion?: number; // Result completion. - success?: number; // Result success. - optionslabel?: string; // Label used for result options. - correctlabel?: string; // Label used for correct answers. - answerlabel?: string; // Label used for user answers. - track?: boolean; // If the result has valid track information. - options?: { // The statement options. - description: string; // Option description. - id: number; // Option identifier. - correctanswer: AddonModH5PActivityWSResultAnswer; // The option correct answer. - useranswer: AddonModH5PActivityWSResultAnswer; // The option user answer. - }[]; -}; - -/** - * Result answer as returned by the WS mod_h5pactivity_get_results. - */ -export type AddonModH5PActivityWSResultAnswer = { - answer?: string; // Option text value. - correct?: boolean; // If has to be displayed as correct. - incorrect?: boolean; // If has to be displayed as incorrect. - text?: boolean; // If has to be displayed as simple text. - checked?: boolean; // If has to be displayed as a checked option. - unchecked?: boolean; // If has to be displayed as a unchecked option. - pass?: boolean; // If has to be displayed as passed. - fail?: boolean; // If has to be displayed as failed. -}; - -/** - * User attempts data with some calculated data. - */ -export type AddonModH5PActivityUserAttempts = { - userid: number; // The user id. - attempts: AddonModH5PActivityAttempt[]; // The complete attempts list. - scored?: { // Attempts used to grade the activity. - title: string; // Scored attempts title. - grademethod: string; // Scored attempts title. - attempts: AddonModH5PActivityAttempt[]; // List of the grading attempts. - }; -}; - -/** - * Attempts results with some calculated data. - */ -export type AddonModH5PActivityAttemptsResults = { - activityid: number; // Activity course module ID. - attempts: AddonModH5PActivityAttemptResults[]; // The complete attempts list. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Attempt with some calculated data. - */ -export type AddonModH5PActivityAttempt = AddonModH5PActivityWSAttempt & { - durationReadable?: string; // Duration in a human readable format. - durationCompact?: string; // Duration in a "short" human readable format. -}; - -/** - * Attempt and results data with some calculated data. - */ -export type AddonModH5PActivityAttemptResults = AddonModH5PActivityAttempt & { - results?: AddonModH5PActivityWSResult[]; // The results of the attempt. -}; - -/** - * Options to pass to getDeployedFile function. - */ -export type AddonModH5PActivityGetDeployedFileOptions = { - displayOptions?: CoreH5PDisplayOptions; // Display options - ignoreCache?: boolean; // Whether to ignore cache. Will fail if offline or server down. - siteId?: string; // Site ID. If not defined, current site. -}; - -/** - * Options to pass to getAttemptResults function. - */ -export type AddonModH5PActivityGetAttemptResultsOptions = CoreCourseCommonModWSOptions & { - userId?: number; // User ID. If not defined, user of the site. -}; - -/** - * Options to pass to getAttempts function. - */ -export type AddonModH5PActivityGetAttemptsOptions = AddonModH5PActivityGetAttemptResultsOptions; diff --git a/src/addon/mod/h5pactivity/providers/index-link-handler.ts b/src/addon/mod/h5pactivity/providers/index-link-handler.ts deleted file mode 100644 index a72c66593..000000000 --- a/src/addon/mod/h5pactivity/providers/index-link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to H5P activity index. - */ -@Injectable() -export class AddonModH5PActivityIndexLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModH5PActivityIndexLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModH5PActivity', 'h5pactivity'); - } -} diff --git a/src/addon/mod/h5pactivity/providers/module-handler.ts b/src/addon/mod/h5pactivity/providers/module-handler.ts deleted file mode 100644 index 6f544a16c..000000000 --- a/src/addon/mod/h5pactivity/providers/module-handler.ts +++ /dev/null @@ -1,90 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; - -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourse } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -import { AddonModH5PActivity } from './h5pactivity'; -import { AddonModH5PActivityIndexComponent } from '../components/index/index'; - -/** - * Handler to support H5P activities. - */ -@Injectable() -export class AddonModH5PActivityModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModH5PActivity'; - modName = 'h5pactivity'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_MODEDIT_DEFAULT_COMPLETION]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - }; - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return AddonModH5PActivity.instance.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - - return { - icon: CoreCourse.instance.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_h5pactivity-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModH5PActivityIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModH5PActivityIndexComponent; - } -} diff --git a/src/addon/mod/h5pactivity/providers/prefetch-handler.ts b/src/addon/mod/h5pactivity/providers/prefetch-handler.ts deleted file mode 100644 index fd000532a..000000000 --- a/src/addon/mod/h5pactivity/providers/prefetch-handler.ts +++ /dev/null @@ -1,199 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreWSExternalFile } from '@providers/ws'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CoreH5PHelper } from '@core/h5p/classes/helper'; -import { CoreH5P } from '@core/h5p/providers/h5p'; -import { CoreUser } from '@core/user/providers/user'; -import { AddonModH5PActivity, AddonModH5PActivityProvider, AddonModH5PActivityData } from './h5pactivity'; - -/** - * Handler to prefetch h5p activity. - */ -@Injectable() -export class AddonModH5PActivityPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModH5PActivity'; - modName = 'h5pactivity'; - component = AddonModH5PActivityProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^tracks$|^usertracks$/; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Get list of files. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the list of files. - */ - async getFiles(module: any, courseId: number, single?: boolean): Promise { - - const h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(courseId, module.id); - - const displayOptions = CoreH5PHelper.decodeDisplayOptions(h5pActivity.displayoptions); - - const deployedFile = await AddonModH5PActivity.instance.getDeployedFile(h5pActivity, { - displayOptions: displayOptions, - }); - - return [deployedFile].concat(this.getIntroFilesFromInstance(module, h5pActivity)); - } - - /** - * Invalidate WS calls needed to determine module status (usually, to check if module is downloadable). - * It doesn't need to invalidate check updates. It should NOT invalidate files nor all the prefetched data. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - async invalidateModule(module: any, courseId: number): Promise { - // No need to invalidate anything. - } - - /** - * Check if a module can be downloaded. If the function is not defined, we assume that all modules are downloadable. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Whether the module can be downloaded. The promise should never be rejected. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - return this.sitesProvider.getCurrentSite().canDownloadFiles() && !CoreH5P.instance.isOfflineDisabledInSite(); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return AddonModH5PActivity.instance.isPluginEnabled(); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchActivity.bind(this)); - } - - /** - * Prefetch an H5P activity. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected async prefetchActivity(module: any, courseId: number, single: boolean, siteId: string): Promise { - - const h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(courseId, module.id, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }); - - const introFiles = this.getIntroFilesFromInstance(module, h5pActivity); - - await Promise.all([ - this.prefetchWSData(h5pActivity, siteId), - this.filepoolProvider.addFilesToQueue(siteId, introFiles, AddonModH5PActivityProvider.COMPONENT, module.id), - this.prefetchMainFile(module, h5pActivity, siteId), - ]); - } - - /** - * Prefetch the deployed file of the activity. - * - * @param module Module. - * @param h5pActivity Activity instance. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected async prefetchMainFile(module: any, h5pActivity: AddonModH5PActivityData, siteId: string): Promise { - - const displayOptions = CoreH5PHelper.decodeDisplayOptions(h5pActivity.displayoptions); - - const deployedFile = await AddonModH5PActivity.instance.getDeployedFile(h5pActivity, { - displayOptions: displayOptions, - ignoreCache: true, - siteId: siteId, - }); - - await this.filepoolProvider.addFilesToQueue(siteId, [deployedFile], AddonModH5PActivityProvider.COMPONENT, module.id); - } - - /** - * Prefetch all the WebService data. - * - * @param h5pActivity Activity instance. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected async prefetchWSData(h5pActivity: AddonModH5PActivityData, siteId: string): Promise { - - const accessInfo = await AddonModH5PActivity.instance.getAccessInformation(h5pActivity.id, { - cmId: h5pActivity.coursemodule, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId, - }); - - if (!accessInfo.canreviewattempts) { - // Not a teacher, prefetch user attempts and the current user profile. - const site = await this.sitesProvider.getSite(siteId); - - const options = { - cmId: h5pActivity.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId: siteId, - }; - - await Promise.all([ - AddonModH5PActivity.instance.getAllAttemptsResults(h5pActivity.id, options), - CoreUser.instance.prefetchProfiles([site.getUserId()], h5pActivity.course, siteId), - ]); - } - } -} diff --git a/src/addon/mod/h5pactivity/providers/report-link-handler.ts b/src/addon/mod/h5pactivity/providers/report-link-handler.ts deleted file mode 100644 index 615b151a7..000000000 --- a/src/addon/mod/h5pactivity/providers/report-link-handler.ts +++ /dev/null @@ -1,147 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreDomUtils } from '@providers/utils/dom'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelper } from '@core/contentlinks/providers/helper'; -import { CoreCourse } from '@core/course/providers/course'; -import { AddonModH5PActivity } from './h5pactivity'; - -/** - * Handler to treat links to H5P activity report. - */ -@Injectable() -export class AddonModH5PActivityReportLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModH5PActivityReportLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModH5PActivity'; - pattern = /\/mod\/h5pactivity\/report\.php.*([\&\?]a=\d+)/; - - constructor() { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - courseId = courseId || params.courseid || params.cid; - - return [{ - action: async (siteId, navCtrl?): Promise => { - try { - const id = Number(params.a); - - if (!courseId) { - courseId = await this.getCourseId(id, siteId); - } - - if (typeof params.attemptid != 'undefined') { - this.openAttemptResults(id, Number(params.attemptid), courseId, siteId, navCtrl); - } else { - const userId = params.userid ? Number(params.userid) : undefined; - - this.openUserAttempts(id, courseId, siteId, userId, navCtrl); - } - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, 'Error processing link.'); - } - } - }]; - } - - /** - * Get course Id for an activity. - * - * @param id Activity ID. - * @param siteId Site ID. - * @return Promise resolved with course ID. - */ - protected async getCourseId(id: number, siteId: string): Promise { - const modal = CoreDomUtils.instance.showModalLoading(); - - try { - const module = await CoreCourse.instance.getModuleBasicInfoByInstance(id, 'h5pactivity', siteId); - - return module.course; - } finally { - modal.dismiss(); - } - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - async isEnabled(siteId: string, url: string, params: any, courseId?: number): Promise { - return AddonModH5PActivity.instance.isPluginEnabled(); - } - - /** - * Open attempt results. - * - * @param id Activity ID. - * @param attemptId Attempt ID. - * @param courseId Course ID. - * @param siteId Site ID. - * @param navCtrl The NavController to use to navigate. - * @return Promise resolved when done. - */ - protected openAttemptResults(id: number, attemptId: number, courseId: number, siteId: string, navCtrl?: NavController): void { - - const pageParams = { - courseId: courseId, - h5pActivityId: id, - attemptId: attemptId, - }; - - CoreContentLinksHelper.instance.goInSite(navCtrl, 'AddonModH5PActivityAttemptResultsPage', pageParams, siteId); - } - - /** - * Open user attempts. - * - * @param id Activity ID. - * @param courseId Course ID. - * @param siteId Site ID. - * @param userId User ID. If not defined, current user in site. - * @param navCtrl The NavController to use to navigate. - * @return Promise resolved when done. - */ - protected openUserAttempts(id: number, courseId: number, siteId: string, userId?: number, navCtrl?: NavController): void { - - const pageParams = { - courseId: courseId, - h5pActivityId: id, - userId: userId, - }; - - CoreContentLinksHelper.instance.goInSite(navCtrl, 'AddonModH5PActivityUserAttemptsPage', pageParams, siteId); - } -} diff --git a/src/addon/mod/h5pactivity/providers/sync-cron-handler.ts b/src/addon/mod/h5pactivity/providers/sync-cron-handler.ts deleted file mode 100644 index 5ffccb86b..000000000 --- a/src/addon/mod/h5pactivity/providers/sync-cron-handler.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModH5PActivitySync } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModH5PActivitySyncCronHandler implements CoreCronHandler { - name = 'AddonModH5PActivitySyncCronHandler'; - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return AddonModH5PActivitySync.instance.syncAllActivities(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return AddonModH5PActivitySync.instance.syncInterval; - } -} diff --git a/src/addon/mod/h5pactivity/providers/sync.ts b/src/addon/mod/h5pactivity/providers/sync.ts deleted file mode 100644 index 407f86d91..000000000 --- a/src/addon/mod/h5pactivity/providers/sync.ts +++ /dev/null @@ -1,223 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEvents } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreUtils } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreCourse } from '@core/course/providers/course'; -import { CoreCourseLogHelper } from '@core/course/providers/log-helper'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreCourseActivitySyncBaseProvider } from '@core/course/classes/activity-sync'; -import { CoreXAPI } from '@core/xapi/providers/xapi'; -import { CoreXAPIOffline } from '@core/xapi/providers/offline'; -import { AddonModH5PActivity, AddonModH5PActivityProvider } from './h5pactivity'; -import { AddonModH5PActivityPrefetchHandler } from './prefetch-handler'; - -import { makeSingleton } from '@singletons/core.singletons'; - -/** - * Service to sync H5P activities. - */ -@Injectable() -export class AddonModH5PActivitySyncProvider extends CoreCourseActivitySyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_h5pactivity_autom_synced'; - protected componentTranslate: string; - - constructor(sitesProvider: CoreSitesProvider, - loggerProvider: CoreLoggerProvider, - appProvider: CoreAppProvider, - translate: TranslateService, - textUtils: CoreTextUtilsProvider, - syncProvider: CoreSyncProvider, - timeUtils: CoreTimeUtilsProvider, - prefetchHandler: AddonModH5PActivityPrefetchHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate) { - - super('AddonModH5PActivitySyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils, prefetchDelegate, prefetchHandler); - - this.componentTranslate = CoreCourse.instance.translateModuleName('h5pactivity'); - } - - /** - * Try to synchronize all the H5P activities in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllActivities(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('H5P activities', this.syncAllActivitiesFunc.bind(this), [force], siteId); - } - - /** - * Sync all H5P activities on a site. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - protected async syncAllActivitiesFunc(siteId?: string, force?: boolean): Promise { - const entries = await CoreXAPIOffline.instance.getAllStatements(siteId); - - // Sync all responses. - const promises = entries.map((response) => { - const promise = force ? this.syncActivity(response.contextid, siteId) : - this.syncActivityIfNeeded(response.contextid, siteId); - - return promise.then((result) => { - if (result && result.updated) { - // Sync successful, send event. - CoreEvents.instance.trigger(AddonModH5PActivitySyncProvider.AUTO_SYNCED, { - contextId: response.contextid, - warnings: result.warnings, - }, siteId); - } - }); - }); - - await Promise.all(promises); - } - - /** - * Sync an H5P activity only if a certain time has passed since the last time. - * - * @param contextId Context ID of the activity. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the activity is synced or it doesn't need to be synced. - */ - async syncActivityIfNeeded(contextId: number, siteId?: string): Promise { - const needed = await this.isSyncNeeded(contextId, siteId); - - if (needed) { - return this.syncActivity(contextId, siteId); - } - } - - /** - * Synchronize an H5P activity. If it's already being synced it will reuse the same promise. - * - * @param contextId Context ID of the activity. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncActivity(contextId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - throw this.translate.instant('core.networkerrormsg'); - } - - if (this.isSyncing(contextId, siteId)) { - // There's already a sync ongoing for this discussion, return the promise. - return this.getOngoingSync(contextId, siteId); - } - - return this.addOngoingSync(contextId, this.syncActivityData(contextId, siteId), siteId); - } - - /** - * Synchronize an H5P activity. - * - * @param contextId Context ID of the activity. - * @param siteId Site ID. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - protected async syncActivityData(contextId: number, siteId: string): Promise<{warnings: string[], updated: boolean}> { - - this.logger.debug(`Try to sync H5P activity with context ID '${contextId}'`); - - const result = { - warnings: [], - updated: false, - }; - - // Get all the statements stored for the activity. - const entries = await CoreXAPIOffline.instance.getContextStatements(contextId, siteId); - - if (!entries || !entries.length) { - // Nothing to sync. - await this.setSyncTime(contextId, siteId); - - return result; - } - - // Get the activity instance. - const courseId = entries[0].courseid; - - const h5pActivity = await AddonModH5PActivity.instance.getH5PActivityByContextId(courseId, contextId, {siteId}); - - // Sync offline logs. - try { - await CoreCourseLogHelper.instance.syncIfNeeded(AddonModH5PActivityProvider.COMPONENT, h5pActivity.id, siteId); - } catch (error) { - // Ignore errors. - } - - // Send the statements in order. - for (let i = 0; i < entries.length; i++) { - const entry = entries[i]; - - try { - await CoreXAPI.instance.postStatementsOnline(entry.component, entry.statements, siteId); - - result.updated = true; - - await CoreXAPIOffline.instance.deleteStatements(entry.id, siteId); - } catch (error) { - if (CoreUtils.instance.isWebServiceError(error)) { - // The WebService has thrown an error, this means that statements cannot be submitted. Delete them. - result.updated = true; - - await CoreXAPIOffline.instance.deleteStatements(entry.id, siteId); - - // Responses deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: entry.extra, - error: this.textUtils.getErrorMessageFromError(error), - })); - } else { - // Stop synchronizing. - throw error; - } - } - } - - if (result.updated) { - try { - // Data has been sent to server, invalidate attempts. - await AddonModH5PActivity.instance.invalidateUserAttempts(h5pActivity.id, undefined, siteId); - } catch (error) { - // Ignore errors. - } - } - - // Sync finished, set sync time. - await this.setSyncTime(contextId, siteId); - - return result; - } -} - -export class AddonModH5PActivitySync extends makeSingleton(AddonModH5PActivitySyncProvider) {} diff --git a/src/addon/mod/imscp/components/components.module.ts b/src/addon/mod/imscp/components/components.module.ts deleted file mode 100644 index 9ff28b872..000000000 --- a/src/addon/mod/imscp/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModImscpIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModImscpIndexComponent, - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModImscpIndexComponent - ], - entryComponents: [ - AddonModImscpIndexComponent - ] -}) -export class AddonModImscpComponentsModule {} diff --git a/src/addon/mod/imscp/components/index/addon-mod-imscp-index.html b/src/addon/mod/imscp/components/index/addon-mod-imscp-index.html deleted file mode 100644 index 7d14a8a56..000000000 --- a/src/addon/mod/imscp/components/index/addon-mod-imscp-index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -
- - -
-
diff --git a/src/addon/mod/imscp/components/index/index.scss b/src/addon/mod/imscp/components/index/index.scss deleted file mode 100644 index 036f1c632..000000000 --- a/src/addon/mod/imscp/components/index/index.scss +++ /dev/null @@ -1,12 +0,0 @@ -ion-app.app-root addon-mod-imscp-index { - .addon-mod-imscp-container { - position: absolute; - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - } - core-iframe { - flex-basis: 100%; - } -} diff --git a/src/addon/mod/imscp/components/index/index.ts b/src/addon/mod/imscp/components/index/index.ts deleted file mode 100644 index 246721aab..000000000 --- a/src/addon/mod/imscp/components/index/index.ts +++ /dev/null @@ -1,159 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Injector } from '@angular/core'; -import { ModalController } from 'ionic-angular'; -import { - CoreCourseModuleMainResourceComponent, CoreCourseResourceDownloadResult -} from '@core/course/classes/main-resource-component'; -import { AddonModImscpProvider } from '../../providers/imscp'; - -/** - * Component that displays a IMSCP. - */ -@Component({ - selector: 'addon-mod-imscp-index', - templateUrl: 'addon-mod-imscp-index.html', -}) -export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceComponent { - component = AddonModImscpProvider.COMPONENT; - - items = []; - currentItem: string; - src = ''; - warning: string; - - // Initialize empty previous/next to prevent showing arrows for an instant before they're hidden. - previousItem = ''; - nextItem = ''; - - constructor(injector: Injector, - protected imscpProvider: AddonModImscpProvider, - protected modalCtrl: ModalController) { - super(injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.loadContent().then(() => { - this.imscpProvider.logView(this.module.instance, this.module.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return this.imscpProvider.invalidateContent(this.module.id, this.courseId); - } - - /** - * Download imscp contents. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchContent(refresh?: boolean): Promise { - let downloadResult: CoreCourseResourceDownloadResult; - const promises = []; - - promises.push(this.imscpProvider.getImscp(this.courseId, this.module.id).then((imscp) => { - this.description = imscp.intro; - this.dataRetrieved.emit(imscp); - })); - - promises.push(this.downloadResourceIfNeeded(refresh).then((result) => { - downloadResult = result; - })); - - return Promise.all(promises).then(() => { - this.items = this.imscpProvider.createItemList(this.module.contents); - if (this.items.length && typeof this.currentItem == 'undefined') { - this.currentItem = this.items[0].href; - } - - return this.loadItem(this.currentItem).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_imscp.deploymenterror', true); - - return Promise.reject(null); - }); - }).then(() => { - this.warning = downloadResult.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error) : ''; - - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Loads an item. - * - * @param itemId Item ID. - * @return Promise resolved when done. - */ - loadItem(itemId: string): Promise { - return this.imscpProvider.getIframeSrc(this.module, itemId).then((src) => { - this.currentItem = itemId; - this.previousItem = this.imscpProvider.getPreviousItem(this.items, itemId); - this.nextItem = this.imscpProvider.getNextItem(this.items, itemId); - - if (this.src && src == this.src) { - // Re-loading same page. Set it to empty and then re-set the src in the next digest so it detects it has changed. - this.src = ''; - setTimeout(() => { - this.src = src; - }); - } else { - this.src = src; - } - }); - } - - /** - * Show the TOC. - * - * @param event Event. - */ - showToc(event: MouseEvent): void { - // Create the toc modal. - const modal = this.modalCtrl.create('AddonModImscpTocPage', { - items: this.items, - selected: this.currentItem - }, { cssClass: 'core-modal-lateral', - showBackdrop: true, - enableBackdropDismiss: true, - enterAnimation: 'core-modal-lateral-transition', - leaveAnimation: 'core-modal-lateral-transition' }); - - modal.onDidDismiss((itemId) => { - if (itemId) { - this.loadItem(itemId); - } - }); - - modal.present({ - ev: event - }); - } -} diff --git a/src/addon/mod/imscp/imscp.module.ts b/src/addon/mod/imscp/imscp.module.ts deleted file mode 100644 index bcafdaae5..000000000 --- a/src/addon/mod/imscp/imscp.module.ts +++ /dev/null @@ -1,61 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModImscpComponentsModule } from './components/components.module'; -import { AddonModImscpModuleHandler } from './providers/module-handler'; -import { AddonModImscpProvider } from './providers/imscp'; -import { AddonModImscpPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModImscpLinkHandler } from './providers/link-handler'; -import { AddonModImscpListLinkHandler } from './providers/list-link-handler'; -import { AddonModImscpPluginFileHandler } from './providers/pluginfile-handler'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -// List of providers (without handlers). -export const ADDON_MOD_IMSCP_PROVIDERS: any[] = [ - AddonModImscpProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModImscpComponentsModule - ], - providers: [ - AddonModImscpProvider, - AddonModImscpModuleHandler, - AddonModImscpPrefetchHandler, - AddonModImscpLinkHandler, - AddonModImscpListLinkHandler, - AddonModImscpPluginFileHandler - ] -}) -export class AddonModImscpModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModImscpModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModImscpPrefetchHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModImscpLinkHandler, - pluginfileDelegate: CorePluginFileDelegate, pluginfileHandler: AddonModImscpPluginFileHandler, - listLinkHandler: AddonModImscpListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - pluginfileDelegate.registerHandler(pluginfileHandler); - } -} diff --git a/src/addon/mod/imscp/lang/en.json b/src/addon/mod/imscp/lang/en.json deleted file mode 100644 index 441eea598..000000000 --- a/src/addon/mod/imscp/lang/en.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "deploymenterror": "Content package error!", - "modulenameplural": "IMS content packages", - "showmoduledescription": "Show description", - "toc": "TOC" -} \ No newline at end of file diff --git a/src/addon/mod/imscp/pages/index/index.html b/src/addon/mod/imscp/pages/index/index.html deleted file mode 100644 index b99777991..000000000 --- a/src/addon/mod/imscp/pages/index/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/addon/mod/imscp/pages/index/index.module.ts b/src/addon/mod/imscp/pages/index/index.module.ts deleted file mode 100644 index 7aad119ef..000000000 --- a/src/addon/mod/imscp/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModImscpComponentsModule } from '../../components/components.module'; -import { AddonModImscpIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModImscpIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModImscpComponentsModule, - IonicPageModule.forChild(AddonModImscpIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModImscpIndexPageModule {} diff --git a/src/addon/mod/imscp/pages/index/index.ts b/src/addon/mod/imscp/pages/index/index.ts deleted file mode 100644 index 56d80259e..000000000 --- a/src/addon/mod/imscp/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModImscpIndexComponent } from '../../components/index/index'; - -/** - * Imscp that displays a IMSCP. - */ -@IonicPage({ segment: 'addon-mod-imscp-index' }) -@Component({ - selector: 'page-addon-mod-imscp-index', - templateUrl: 'index.html', -}) -export class AddonModImscpIndexPage { - @ViewChild(AddonModImscpIndexComponent) imscpComponent: AddonModImscpIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the imscp instance. - * - * @param imscp Imscp instance. - */ - updateData(imscp: any): void { - this.title = imscp.name || this.title; - } -} diff --git a/src/addon/mod/imscp/pages/toc/toc.html b/src/addon/mod/imscp/pages/toc/toc.html deleted file mode 100644 index 035a5d6a0..000000000 --- a/src/addon/mod/imscp/pages/toc/toc.html +++ /dev/null @@ -1,19 +0,0 @@ - - - {{ 'addon.mod_imscp.toc' | translate }} - - - - - - - - diff --git a/src/addon/mod/imscp/pages/toc/toc.module.ts b/src/addon/mod/imscp/pages/toc/toc.module.ts deleted file mode 100644 index d28469b58..000000000 --- a/src/addon/mod/imscp/pages/toc/toc.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModImscpTocPage } from './toc'; - -@NgModule({ - declarations: [ - AddonModImscpTocPage, - ], - imports: [ - CoreDirectivesModule, - IonicPageModule.forChild(AddonModImscpTocPage), - TranslateModule.forChild() - ], -}) -export class AddonModImscpTocPageModule {} diff --git a/src/addon/mod/imscp/pages/toc/toc.ts b/src/addon/mod/imscp/pages/toc/toc.ts deleted file mode 100644 index fe27fb416..000000000 --- a/src/addon/mod/imscp/pages/toc/toc.ts +++ /dev/null @@ -1,60 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; - -/** - * Modal to display the TOC of a imscp. - */ -@IonicPage({ segment: 'addon-mod-imscp-toc-modal' }) -@Component({ - selector: 'page-addon-mod-imscp-toc', - templateUrl: 'toc.html' -}) -export class AddonModImscpTocPage { - items = []; - selected: string; - - constructor(navParams: NavParams, private viewCtrl: ViewController) { - this.items = navParams.get('items') || []; - this.selected = navParams.get('selected'); - } - - /** - * Function called when an item is clicked. - * - * @param id ID of the clicked item. - */ - loadItem(id: string): void { - this.viewCtrl.dismiss(id); - } - - /** - * Get dummy array for padding. - * - * @param n Array length. - * @return Dummy array with n elements. - */ - getNumberForPadding(n: number): number[] { - return new Array(n); - } - - /** - * Close modal. - */ - closeModal(): void { - this.viewCtrl.dismiss(); - } -} diff --git a/src/addon/mod/imscp/providers/imscp.ts b/src/addon/mod/imscp/providers/imscp.ts deleted file mode 100644 index 3a0bd3c52..000000000 --- a/src/addon/mod/imscp/providers/imscp.ts +++ /dev/null @@ -1,362 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; - -/** - * Service that provides some features for IMSCP. - */ -@Injectable() -export class AddonModImscpProvider { - static COMPONENT = 'mmaModImscp'; - - protected ROOT_CACHE_KEY = 'mmaModImscp:'; - - constructor(private appProvider: CoreAppProvider, private courseProvider: CoreCourseProvider, - private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider, - private textUtils: CoreTextUtilsProvider, private utils: CoreUtilsProvider, - private logHelper: CoreCourseLogHelperProvider) {} - - /** - * Get the IMSCP toc as an array. - * - * @param contents The module contents. - * @return The toc. - */ - protected getToc(contents: any[]): any { - if (!contents || !contents.length) { - return []; - } - - return JSON.parse(contents[0].content); - } - - /** - * Get the imscp toc as an array of items (not nested) to build the navigation tree. - * - * @param contents The module contents. - * @return The toc as a list. - */ - createItemList(contents: any[]): any[] { - const items = []; - - this.getToc(contents).forEach((el) => { - items.push({href: el.href, title: el.title, level: el.level}); - el.subitems.forEach((sel) => { - items.push({href: sel.href, title: sel.title, level: sel.level}); - }); - }); - - return items; - } - - /** - * Get the previous item to the given one. - * - * @param items The items list. - * @param itemId The current item. - * @return The previous item id. - */ - getPreviousItem(items: any[], itemId: string): string { - const position = this.getItemPosition(items, itemId); - - if (position != -1) { - for (let i = position - 1; i >= 0; i--) { - if (items[i] && items[i].href) { - return items[i].href; - } - } - } - - return ''; - } - - /** - * Get the next item to the given one. - * - * @param items The items list. - * @param itemId The current item. - * @return The next item id. - */ - getNextItem(items: any[], itemId: string): string { - const position = this.getItemPosition(items, itemId); - - if (position != -1) { - for (let i = position + 1; i < items.length; i++) { - if (items[i] && items[i].href) { - return items[i].href; - } - } - } - - return ''; - } - - /** - * Get the position of a item. - * - * @param items The items list. - * @param itemId The item to search. - * @return The item position. - */ - protected getItemPosition(items: any[], itemId: string): number { - for (let i = 0; i < items.length; i++) { - if (items[i].href == itemId) { - return i; - } - } - - return -1; - } - - /** - * Check if we should ommit the file download. - * - * @param fileName The file name - * @return True if we should ommit the file. - */ - protected checkSpecialFiles(fileName: string): boolean { - return fileName == 'imsmanifest.xml'; - } - - /** - * Get cache key for imscp data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getImscpDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'imscp:' + courseId; - } - - /** - * Get a imscp with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the imscp is retrieved. - */ - protected getImscpByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getImscpDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModImscpProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_imscp_get_imscps_by_courses', params, preSets) - .then((response: AddonModImscpGetImscpsByCoursesResult): any => { - - if (response && response.imscps) { - const currentImscp = response.imscps.find((imscp) => imscp[key] == value); - if (currentImscp) { - return currentImscp; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a imscp by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the imscp is retrieved. - */ - getImscp(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getImscpByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Given a filepath, get a certain fileurl from module contents. - * - * @param contents Module contents. - * @param targetFilePath Path of the searched file. - * @return File URL. - */ - protected getFileUrlFromContents(contents: any[], targetFilePath: string): string { - let indexUrl; - contents.forEach((content) => { - if (content.type == 'file' && !indexUrl) { - const filePath = this.textUtils.concatenatePaths(content.filepath, content.filename); - const filePathAlt = filePath.charAt(0) === '/' ? filePath.substr(1) : '/' + filePath; - // Check if it's main file. - if (filePath === targetFilePath || filePathAlt === targetFilePath) { - indexUrl = content.fileurl; - } - } - }); - - return indexUrl; - } - - /** - * Get src of a imscp item. - * - * @param module The module object. - * @param itemHref Href of item to get. If not defined, gets src of main item. - * @return Promise resolved with the item src. - */ - getIframeSrc(module: any, itemHref?: string): Promise { - if (!itemHref) { - const toc = this.getToc(module.contents); - if (!toc.length) { - return Promise.reject(null); - } - itemHref = toc[0].href; - } - - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.filepoolProvider.getPackageDirUrlByUrl(siteId, module.url).then((dirPath) => { - return this.textUtils.concatenatePaths(dirPath, itemHref); - }).catch(() => { - // Error getting directory, there was an error downloading or we're in browser. Return online URL if connected. - if (this.appProvider.isOnline()) { - const indexUrl = this.getFileUrlFromContents(module.contents, itemHref); - - if (indexUrl) { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.checkAndFixPluginfileURL(indexUrl); - }); - } - } - - return Promise.reject(null); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the content is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.invalidateImscpData(courseId, siteId)); - promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModImscpProvider.COMPONENT, moduleId)); - promises.push(this.courseProvider.invalidateModule(moduleId, siteId)); - - return this.utils.allPromises(promises); - } - - /** - * Invalidates imscp data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateImscpData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getImscpDataCacheKey(courseId)); - }); - } - - /** - * Check if a file is downloadable. The file param must have 'type' and 'filename' attributes - * like in core_course_get_contents response. - * - * @param file File to check. - * @return True if downloadable, false otherwise. - */ - isFileDownloadable(file: any): boolean { - return file.type === 'file' && !this.checkSpecialFiles(file.filename); - } - - /** - * Return whether or not the plugin is enabled in a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.canDownloadFiles(); - }); - } - - /** - * Report a IMSCP as being viewed. - * - * @param id Module ID. - * @param name Name of the imscp. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - imscpid: id - }; - - return this.logHelper.logSingle('mod_imscp_view_imscp', params, AddonModImscpProvider.COMPONENT, id, name, 'imscp', {}, - siteId); - } -} - -/** - * IMSCP returned by mod_imscp_get_imscps_by_courses. - */ -export type AddonModImscpImscp = { - id: number; // IMSCP id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Activity name. - intro?: string; // The IMSCP intro. - introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles?: CoreWSExternalFile[]; // @since 3.2. - revision?: number; // Revision. - keepold?: number; // Number of old IMSCP to keep. - structure?: string; // IMSCP structure. - timemodified?: string; // Time of last modification. - section?: number; // Course section id. - visible?: boolean; // If visible. - groupmode?: number; // Group mode. - groupingid?: number; // Group id. -}; - -/** - * Result of WS mod_imscp_get_imscps_by_courses. - */ -export type AddonModImscpGetImscpsByCoursesResult = { - imscps: AddonModImscpImscp[]; - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/imscp/providers/link-handler.ts b/src/addon/mod/imscp/providers/link-handler.ts deleted file mode 100644 index f7e4c14bd..000000000 --- a/src/addon/mod/imscp/providers/link-handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModImscpProvider } from './imscp'; - -/** - * Handler to treat links to IMSCP. - */ -@Injectable() -export class AddonModImscpLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModImscpLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, - protected imscpProvider: AddonModImscpProvider) { - super(courseHelper, 'AddonModImscp', 'imscp', 'i'); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.imscpProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/imscp/providers/list-link-handler.ts b/src/addon/mod/imscp/providers/list-link-handler.ts deleted file mode 100644 index e973aadf8..000000000 --- a/src/addon/mod/imscp/providers/list-link-handler.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModImscpProvider } from './imscp'; - -/** - * Handler to treat links to IMSCP list page. - */ -@Injectable() -export class AddonModImscpListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModImscpListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected imscpProvider: AddonModImscpProvider) { - super(linkHelper, translate, 'AddonModImscp', 'imscp'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.imscpProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/imscp/providers/module-handler.ts b/src/addon/mod/imscp/providers/module-handler.ts deleted file mode 100644 index 577e7bfbe..000000000 --- a/src/addon/mod/imscp/providers/module-handler.ts +++ /dev/null @@ -1,89 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModImscpIndexComponent } from '../components/index/index'; -import { AddonModImscpProvider } from './imscp'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support IMSCP modules. - */ -@Injectable() -export class AddonModImscpModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModImscp'; - modName = 'imscp'; - - supportedFeatures = { - [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider, protected imscpProvider: AddonModImscpProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.imscpProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_imscp-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModImscpIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModImscpIndexComponent; - } -} diff --git a/src/addon/mod/imscp/providers/pluginfile-handler.ts b/src/addon/mod/imscp/providers/pluginfile-handler.ts deleted file mode 100644 index 4d283a90e..000000000 --- a/src/addon/mod/imscp/providers/pluginfile-handler.ts +++ /dev/null @@ -1,66 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CorePluginFileHandler } from '@providers/plugin-file-delegate'; - -/** - * Handler to treat links to IMSCP. - */ -@Injectable() -export class AddonModImscpPluginFileHandler implements CorePluginFileHandler { - name = 'AddonModImscpPluginFileHandler'; - component = 'mod_imscp'; - - /** - * Return the RegExp to match the revision on pluginfile URLs. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return RegExp to match the revision on pluginfile URLs. - */ - getComponentRevisionRegExp(args: string[]): RegExp { - // Check filearea. - if (args[2] == 'content') { - // Component + Filearea + Revision - return new RegExp('/mod_imscp/content/([0-9]+)/'); - } - - if (args[2] == 'backup') { - // Component + Filearea + Revision - return new RegExp('/mod_imscp/backup/([0-9]+)/'); - } - - return null; - } - - /** - * Should return the string to remove the revision on pluginfile url. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return String to remove the revision on pluginfile url. - */ - getComponentRevisionReplace(args: string[]): string { - // Component + Filearea + Revision - return '/mod_imscp/' + args[2] + '/0/'; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/imscp/providers/prefetch-handler.ts b/src/addon/mod/imscp/providers/prefetch-handler.ts deleted file mode 100644 index 899fdbe09..000000000 --- a/src/addon/mod/imscp/providers/prefetch-handler.ts +++ /dev/null @@ -1,139 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; -import { AddonModImscpProvider } from './imscp'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch IMSCPs. - */ -@Injectable() -export class AddonModImscpPrefetchHandler extends CoreCourseResourcePrefetchHandlerBase { - name = 'AddonModImscp'; - modName = 'imscp'; - component = AddonModImscpProvider.COMPONENT; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected imscpProvider: AddonModImscpProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Download or prefetch the content. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param prefetch True to prefetch, false to download right away. - * @param dirPath Path of the directory where to store all the content files. This is to keep the files - * relative paths and make the package work in an iframe. Undefined to download the files - * in the filepool root folder. - * @return Promise resolved when all content is downloaded. Data returned is not reliable. - */ - downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise { - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.filepoolProvider.getPackageDirPathByUrl(siteId, module.url).then((dirPath) => { - const promises = []; - - promises.push(super.downloadOrPrefetch(module, courseId, prefetch, dirPath)); - promises.push(this.imscpProvider.getImscp(courseId, module.id, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - })); - - return Promise.all(promises); - }); - } - - /** - * Returns module intro files. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @return Promise resolved with list of intro files. - */ - getIntroFiles(module: any, courseId: number): Promise { - return this.imscpProvider.getImscp(courseId, module.id).catch(() => { - // Not found, return undefined so module description is used. - }).then((imscp) => { - return this.getIntroFilesFromInstance(module, imscp); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.imscpProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const promises = []; - - promises.push(this.imscpProvider.invalidateImscpData(courseId)); - promises.push(this.courseProvider.invalidateModule(module.id)); - - return Promise.all(promises); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.imscpProvider.isPluginEnabled(); - } - - /** - * Check if a file is downloadable. - * - * @param file File to check. - * @return Whether the file is downloadable. - */ - isFileDownloadable(file: any): boolean { - return this.imscpProvider.isFileDownloadable(file); - } -} diff --git a/src/addon/mod/label/label.module.ts b/src/addon/mod/label/label.module.ts deleted file mode 100644 index 696652a49..000000000 --- a/src/addon/mod/label/label.module.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModLabelProvider } from './providers/label'; -import { AddonModLabelModuleHandler } from './providers/module-handler'; -import { AddonModLabelLinkHandler } from './providers/link-handler'; -import { AddonModLabelPrefetchHandler } from './providers/prefetch-handler'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; - -@NgModule({ - declarations: [ - ], - imports: [ - ], - providers: [ - AddonModLabelProvider, - AddonModLabelModuleHandler, - AddonModLabelLinkHandler, - AddonModLabelPrefetchHandler - ] -}) -export class AddonModLabelModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModLabelModuleHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModLabelLinkHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModLabelPrefetchHandler) { - moduleDelegate.registerHandler(moduleHandler); - contentLinksDelegate.registerHandler(linkHandler); - prefetchDelegate.registerHandler(prefetchHandler); - } -} diff --git a/src/addon/mod/label/label.scss b/src/addon/mod/label/label.scss deleted file mode 100644 index 111c3c1d5..000000000 --- a/src/addon/mod/label/label.scss +++ /dev/null @@ -1,16 +0,0 @@ -ion-app.app-root .item.core-course-module-handler.addon-mod-label-handler { - align-items: center; - cursor: auto; - - &:hover { - opacity: 1; - } -} - -.md .item.core-course-module-handler.addon-mod-label-handler .item-inner { - padding-bottom: $item-md-padding-bottom; -} - -.ios .item.core-course-module-handler.addon-mod-label-handler .item-inner { - padding-bottom: $item-ios-padding-bottom; -} diff --git a/src/addon/mod/label/providers/label.ts b/src/addon/mod/label/providers/label.ts deleted file mode 100644 index b16a304fe..000000000 --- a/src/addon/mod/label/providers/label.ts +++ /dev/null @@ -1,190 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; - -/** - * Service that provides some features for labels. - */ -@Injectable() -export class AddonModLabelProvider { - static COMPONENT = 'mmaModLabel'; - - protected ROOT_CACHE_KEY = 'mmaModLabel:'; - - constructor(private sitesProvider: CoreSitesProvider, private filepoolProvider: CoreFilepoolProvider, - private utils: CoreUtilsProvider) {} - - /** - * Get cache key for label data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getLabelDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'label:' + courseId; - } - - /** - * Get a label with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the label is retrieved. - */ - protected getLabelByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getLabelDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModLabelProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_label_get_labels_by_courses', params, preSets) - .then((response: AddonModLabelGetLabelsByCoursesResult): any => { - - if (response && response.labels) { - const currentLabel = response.labels.find((label) => label[key] == value); - if (currentLabel) { - return currentLabel; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a label by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the label is retrieved. - */ - getLabel(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getLabelByField(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a label by ID. - * - * @param courseId Course ID. - * @param labelId Label ID. - * @param options Other options. - * @return Promise resolved when the label is retrieved. - */ - getLabelById(courseId: number, labelId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getLabelByField(courseId, 'id', labelId, options); - } - - /** - * Invalidate label data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateLabelData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(null).then((site) => { - return site.invalidateWsCacheForKey(this.getLabelDataCacheKey(courseId)); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.invalidateLabelData(courseId, siteId)); - - promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModLabelProvider.COMPONENT, moduleId, true)); - - return this.utils.allPromises(promises); - } - - /** - * Check if the site has the WS to get label data. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether it's available. - * @since 3.3 - */ - isGetLabelAvailable(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('mod_label_get_labels_by_courses'); - }); - } - - /** - * Check if the site has the WS to get label data. - * - * @param site Site. If not defined, current site. - * @return Whether it's available. - * @since 3.3 - */ - isGetLabelAvailableForSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.wsAvailable('mod_label_get_labels_by_courses'); - } -} - -/** - * Label returned by mod_label_get_labels_by_courses. - */ -export type AddonModLabelLabel = { - id: number; // Module id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Label name. - intro: string; // Label contents. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles: CoreWSExternalFile[]; - timemodified: number; // Last time the label was modified. - section: number; // Course section id. - visible: number; // Module visibility. - groupmode: number; // Group mode. - groupingid: number; // Grouping id. -}; - -/** - * Result of WS mod_label_get_labels_by_courses. - */ -export type AddonModLabelGetLabelsByCoursesResult = { - labels: AddonModLabelLabel[]; - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/label/providers/link-handler.ts b/src/addon/mod/label/providers/link-handler.ts deleted file mode 100644 index 75bb57f31..000000000 --- a/src/addon/mod/label/providers/link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to label. - */ -@Injectable() -export class AddonModLabelLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModLabelLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModLabel', 'label', 'l'); - } -} diff --git a/src/addon/mod/label/providers/module-handler.ts b/src/addon/mod/label/providers/module-handler.ts deleted file mode 100644 index 06bfed785..000000000 --- a/src/addon/mod/label/providers/module-handler.ts +++ /dev/null @@ -1,87 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support label modules. - */ -@Injectable() -export class AddonModLabelModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModLabel'; - modName = 'label'; - - supportedFeatures = { - [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, - [CoreConstants.FEATURE_IDNUMBER]: true, - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: false, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor() { - // Nothing to do. - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - // Remove the description from the module so it isn't rendered twice. - const title = module.description; - module.description = ''; - - return { - icon: '', - title: title, - a11yTitle: '', - class: 'addon-mod-label-handler' - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param course The course object. - * @param module The module object. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getMainComponent(injector: Injector, course: any, module: any): any | Promise { - // There's no need to implement this because label cannot be used in singleactivity course format. - } -} diff --git a/src/addon/mod/label/providers/prefetch-handler.ts b/src/addon/mod/label/providers/prefetch-handler.ts deleted file mode 100644 index b920ac7ba..000000000 --- a/src/addon/mod/label/providers/prefetch-handler.ts +++ /dev/null @@ -1,104 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; -import { AddonModLabelProvider } from './label'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch labels. - */ -@Injectable() -export class AddonModLabelPrefetchHandler extends CoreCourseResourcePrefetchHandlerBase { - name = 'AddonModLabel'; - modName = 'label'; - component = AddonModLabelProvider.COMPONENT; - updatesNames = /^.*files$/; - skipListStatus = true; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected labelProvider: AddonModLabelProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Returns module intro files. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @return Promise resolved with list of intro files. - */ - getIntroFiles(module: any, courseId: number, ignoreCache?: boolean): Promise { - let promise; - - if (this.labelProvider.isGetLabelAvailableForSite()) { - promise = this.labelProvider.getLabel(courseId, module.id, { - readingStrategy: ignoreCache ? CoreSitesReadingStrategy.OnlyNetwork : undefined - }); - } else { - promise = Promise.resolve(); - } - - return promise.then((label) => { - return this.getIntroFilesFromInstance(module, label); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.labelProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const promises = []; - - promises.push(this.labelProvider.invalidateLabelData(courseId)); - promises.push(this.courseProvider.invalidateModule(module.id)); - - return this.utils.allPromises(promises); - } -} diff --git a/src/addon/mod/lesson/components/components.module.ts b/src/addon/mod/lesson/components/components.module.ts deleted file mode 100644 index a3ec028aa..000000000 --- a/src/addon/mod/lesson/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModLessonIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModLessonIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModLessonIndexComponent - ], - entryComponents: [ - AddonModLessonIndexComponent - ] -}) -export class AddonModLessonComponentsModule {} diff --git a/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html deleted file mode 100644 index ebe107144..000000000 --- a/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- - -
- - {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} -
- - - -
- - {{ 'addon.mod_lesson.enterpassword' | translate }} - - - - - - - -
-
- - - - - - -

{{ 'addon.mod_lesson.retakefinishedinsync' | translate }}

- {{ 'addon.mod_lesson.review' | translate }} -
- - - - -

- - - - {{ 'core.no' | translate }} - - - {{ 'core.yes' | translate }} - - - -
- - - -

- - {{ 'addon.mod_lesson.continue' | translate }} - - -
- - - -

-
- - - - - {{ 'core.start' | translate }} - - - - {{ 'addon.mod_lesson.preview' | translate }} - - - - - - - - {{ 'addon.mod_lesson.continue' | translate }} - - - -
-
-
-
-
- - - - - - - - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - - - - - - - - - - - {{ 'addon.mod_lesson.lessonstats' | translate }} - - - - - - - -

{{ 'addon.mod_lesson.averagescore' | translate }}

-

{{ 'core.percentagenumber' | translate:{$a: overview.avescore} }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
- - -

{{ 'addon.mod_lesson.highscore' | translate }}

-

{{ 'core.percentagenumber' | translate:{$a: overview.highscore} }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
- - -

{{ 'addon.mod_lesson.lowscore' | translate }}

-

{{ 'core.percentagenumber' | translate:{$a: overview.lowscore} }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
-
-
- - - - -

{{ 'addon.mod_lesson.averagetime' | translate }}

-

{{ overview.avetimeReadable }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
- - -

{{ 'addon.mod_lesson.hightime' | translate }}

-

{{ overview.hightimeReadable }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
- - -

{{ 'addon.mod_lesson.lowtime' | translate }}

-

{{ overview.lowtimeReadable }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
-
-
-
- - - - - - -

{{ 'addon.mod_lesson.averagescore' | translate }}

-

{{ 'core.percentagenumber' | translate:{$a: overview.avescore} }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
- - -

{{ 'addon.mod_lesson.averagetime' | translate }}

-

{{ overview.avetimeReadable }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
-
-
- - - - -

{{ 'addon.mod_lesson.highscore' | translate }}

-

{{ 'core.percentagenumber' | translate:{$a: overview.highscore} }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
- - -

{{ 'addon.mod_lesson.hightime' | translate }}

-

{{ overview.hightimeReadable }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
-
-
- - - - -

{{ 'addon.mod_lesson.lowscore' | translate }}

-

{{ 'core.percentagenumber' | translate:{$a: overview.lowscore} }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
- - -

{{ 'addon.mod_lesson.lowtime' | translate }}

-

{{ overview.lowtimeReadable }}

-

{{ 'addon.mod_lesson.notcompleted' | translate }}

-
-
-
-
-
- - - - - {{ 'addon.mod_lesson.overview' | translate }} - - - - -

{{ student.fullname }}

- -
-
-
-
-
-
-
diff --git a/src/addon/mod/lesson/components/index/index.ts b/src/addon/mod/lesson/components/index/index.ts deleted file mode 100644 index 4df72774b..000000000 --- a/src/addon/mod/lesson/components/index/index.ts +++ /dev/null @@ -1,644 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector, Input, ViewChild, ElementRef } from '@angular/core'; -import { Content, NavController } from 'ionic-angular'; -import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModLessonProvider } from '../../providers/lesson'; -import { AddonModLessonOfflineProvider } from '../../providers/lesson-offline'; -import { AddonModLessonSyncProvider } from '../../providers/lesson-sync'; -import { AddonModLessonPrefetchHandler } from '../../providers/prefetch-handler'; -import { CoreConstants } from '@core/constants'; -import { CoreTabsComponent } from '@components/tabs/tabs'; - -/** - * Component that displays a lesson entry page. - */ -@Component({ - selector: 'addon-mod-lesson-index', - templateUrl: 'addon-mod-lesson-index.html', -}) -export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityComponent { - @ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent; - @ViewChild('passwordForm') formElement: ElementRef; - - @Input() group: number; // The group to display. - @Input() action: string; // The "action" to display first. - - component = AddonModLessonProvider.COMPONENT; - moduleName = 'lesson'; - - lesson: any; // The lesson. - selectedTab: number; // The initial selected tab. - askPassword: boolean; // Whether to ask the password. - canManage: boolean; // Whether the user can manage the lesson. - canViewReports: boolean; // Whether the user can view the lesson reports. - showSpinner: boolean; // Whether to display a spinner. - hasOffline: boolean; // Whether there's offline data. - retakeToReview: any; // A retake to review. - preventMessages: string[]; // List of messages that prevent the lesson from being seen. - leftDuringTimed: boolean; // Whether the user has started and left a retake. - groupInfo: CoreGroupInfo; // The group info. - reportLoaded: boolean; // Whether the report data has been loaded. - selectedGroupName: string; // The name of the selected group. - overview: any; // Reports overview data. - finishedOffline: boolean; // Whether a retake was finished in offline. - - protected syncEventName = AddonModLessonSyncProvider.AUTO_SYNCED; - protected accessInfo: any; // Lesson access info. - protected password: string; // The password for the lesson. - protected hasPlayed: boolean; // Whether the user has gone to the lesson player (attempted). - protected dataSentObserver; // To detect data sent to server. - protected dataSent = false; // Whether some data was sent to server while playing the lesson. - - constructor(injector: Injector, protected lessonProvider: AddonModLessonProvider, @Optional() content: Content, - protected groupsProvider: CoreGroupsProvider, protected lessonOffline: AddonModLessonOfflineProvider, - protected lessonSync: AddonModLessonSyncProvider, protected utils: CoreUtilsProvider, - protected prefetchHandler: AddonModLessonPrefetchHandler, protected navCtrl: NavController, - protected timeUtils: CoreTimeUtilsProvider, protected userProvider: CoreUserProvider) { - super(injector, content); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.selectedTab = this.action == 'report' ? 1 : 0; - - this.loadContent(false, true).then(() => { - if (!this.lesson || (this.preventMessages && this.preventMessages.length)) { - return; - } - - this.logView(); - }); - } - - /** - * Change the group displayed. - * - * @param groupId Group ID to display. - */ - changeGroup(groupId: number): void { - this.reportLoaded = false; - - this.setGroup(groupId).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting report.'); - }).finally(() => { - this.reportLoaded = true; - }); - } - - /** - * Get the lesson data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - - let lessonReady = true; - this.askPassword = false; - const options = {cmId: this.module.id}; - - return this.lessonProvider.getLesson(this.courseId, this.module.id).then((lessonData) => { - this.lesson = lessonData; - - this.dataRetrieved.emit(this.lesson); - this.description = this.lesson.intro; // Show description only if intro is present. - - if (sync) { - // Try to synchronize the lesson. - return this.syncActivity(showErrors); - } - }).then(() => { - return this.lessonProvider.getAccessInformation(this.lesson.id, options); - }).then((info) => { - const promises = []; - - this.accessInfo = info; - this.canManage = info.canmanage; - this.canViewReports = info.canviewreports; - this.preventMessages = []; - - if (this.lessonProvider.isLessonOffline(this.lesson)) { - // Handle status. - this.setStatusListener(); - - // Check if there is offline data. - promises.push(this.lessonSync.hasDataToSync(this.lesson.id, info.attemptscount).then((hasOffline) => { - this.hasOffline = hasOffline; - })); - - // Check if there is a retake finished in a synchronization. - promises.push(this.lessonSync.getRetakeFinishedInSync(this.lesson.id).then((retake) => { - if (retake && retake.retake == info.attemptscount - 1) { - // The retake finished is still the last retake. Allow reviewing it. - this.retakeToReview = retake; - } else { - this.retakeToReview = undefined; - if (retake) { - this.lessonSync.deleteRetakeFinishedInSync(this.lesson.id); - } - } - })); - - // Check if the ser has a finished retake in offline. - promises.push(this.lessonOffline.hasFinishedRetake(this.lesson.id).then((finished) => { - this.finishedOffline = finished; - })); - - // Update the list of content pages viewed and question attempts. - promises.push(this.lessonProvider.getContentPagesViewedOnline(this.lesson.id, info.attemptscount, options)); - promises.push(this.lessonProvider.getQuestionsAttemptsOnline(this.lesson.id, info.attemptscount, options)); - } - - if (info.preventaccessreasons && info.preventaccessreasons.length) { - let preventReason = this.lessonProvider.getPreventAccessReason(info, false); - const askPassword = preventReason.reason == 'passwordprotectedlesson'; - - if (askPassword) { - // The lesson requires a password. Check if there is one in memory or DB. - const promise = this.password ? Promise.resolve(this.password) : - this.lessonProvider.getStoredPassword(this.lesson.id); - - promises.push(promise.then((password) => { - return this.validatePassword(password); - }).then(() => { - // Now that we have the password, get the access reason again ignoring the password. - preventReason = this.lessonProvider.getPreventAccessReason(info, true); - if (preventReason) { - this.preventMessages = [preventReason]; - } - }).catch(() => { - // No password or the validation failed. Show password form. - this.askPassword = true; - this.preventMessages = [preventReason]; - lessonReady = false; - })); - } else { - // Lesson cannot be started. - this.preventMessages = [preventReason]; - lessonReady = false; - } - } - - if (this.selectedTab == 1 && this.canViewReports) { - // Only fetch the report data if the tab is selected. - promises.push(this.fetchReportData()); - } - - return Promise.all(promises).then(() => { - if (lessonReady) { - // Lesson can be started, don't ask the password and don't show prevent messages. - this.lessonReady(refresh); - } - }); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Fetch the reports data. - * - * @return Promise resolved when done. - */ - protected fetchReportData(): Promise { - return this.groupsProvider.getActivityGroupInfo(this.module.id).then((groupInfo) => { - this.groupInfo = groupInfo; - - return this.setGroup(this.groupsProvider.validateGroupId(this.group, groupInfo)); - }).finally(() => { - this.reportLoaded = true; - }); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return If suceed or not. - */ - protected hasSyncSucceed(result: any): boolean { - if (result.updated || this.dataSent) { - // Check completion status if something was sent. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - - this.dataSent = false; - - return result.updated; - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - super.ionViewDidEnter(); - - this.tabsComponent && this.tabsComponent.ionViewDidEnter(); - - // Update data when we come back from the player since the status could have changed. - if (this.hasPlayed) { - this.hasPlayed = false; - - this.dataSentObserver && this.dataSentObserver.off(); // Stop listening for changes. - this.dataSentObserver = undefined; - - // Refresh data. - this.showLoadingAndRefresh(true, false); - } - } - - /** - * User left the page that contains the component. - */ - ionViewDidLeave(): void { - super.ionViewDidLeave(); - - this.tabsComponent && this.tabsComponent.ionViewDidLeave(); - - if (this.navCtrl.getActive().component.name == 'AddonModLessonPlayerPage') { - this.hasPlayed = true; - - // Detect if anything was sent to server. - this.dataSentObserver && this.dataSentObserver.off(); - - this.dataSentObserver = this.eventsProvider.on(AddonModLessonProvider.DATA_SENT_EVENT, (data) => { - // Ignore launch sending because it only affects timers. - if (data.lessonId === this.lesson.id && data.type != 'launch') { - this.dataSent = true; - } - }, this.siteId); - } - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.lessonProvider.invalidateLessonData(this.courseId)); - - if (this.lesson) { - promises.push(this.lessonProvider.invalidateAccessInformation(this.lesson.id)); - promises.push(this.lessonProvider.invalidatePages(this.lesson.id)); - promises.push(this.lessonProvider.invalidateLessonWithPassword(this.lesson.id)); - promises.push(this.lessonProvider.invalidateTimers(this.lesson.id)); - promises.push(this.lessonProvider.invalidateContentPagesViewed(this.lesson.id)); - promises.push(this.lessonProvider.invalidateQuestionsAttempts(this.lesson.id)); - promises.push(this.lessonProvider.invalidateRetakesOverview(this.lesson.id)); - promises.push(this.groupsProvider.invalidateActivityGroupInfo(this.module.id)); - } - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - return this.lesson && syncEventData.lessonId == this.lesson.id; - } - - /** - * Function called when the lesson is ready to be seen (no pending prevent access reasons). - * - * @param refresh If it's refreshing content. - */ - protected lessonReady(refresh?: boolean): void { - this.askPassword = false; - this.leftDuringTimed = this.hasOffline || this.lessonProvider.leftDuringTimed(this.accessInfo); - - if (this.password) { - // Store the password in DB. - this.lessonProvider.storePassword(this.lesson.id, this.password); - } - } - - /** - * Log viewing the lesson. - */ - protected logView(): void { - this.lessonProvider.logViewLesson(this.lesson.id, this.password, this.lesson.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch((error) => { - // Ignore errors. - }); - } - - /** - * Open the lesson player. - * - * @param continueLast Whether to continue the last retake. - * @return Promise resolved when done. - */ - protected playLesson(continueLast: boolean): Promise { - // Calculate the pageId to load. If there is timelimit, lesson is always restarted from the start. - let promise; - - if (this.hasOffline) { - if (continueLast) { - promise = this.lessonProvider.getLastPageSeen(this.lesson.id, this.accessInfo.attemptscount, { - cmId: this.module.id, - }); - } else { - promise = Promise.resolve(this.accessInfo.firstpageid); - } - } else if (this.leftDuringTimed && !this.lesson.timelimit) { - promise = Promise.resolve(continueLast ? this.accessInfo.lastpageseen : this.accessInfo.firstpageid); - } else { - promise = Promise.resolve(); - } - - return promise.then((pageId) => { - this.navCtrl.push('AddonModLessonPlayerPage', { - courseId: this.courseId, - lessonId: this.lesson.id, - pageId: pageId, - password: this.password - }); - }); - } - - /** - * First tab selected. - */ - indexSelected(): void { - this.selectedTab = 0; - } - - /** - * Reports tab selected. - */ - reportsSelected(): void { - this.selectedTab = 1; - - if (!this.groupInfo) { - this.fetchReportData().catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting report.'); - }); - } - } - - /** - * Review the lesson. - */ - review(): void { - if (!this.retakeToReview) { - // No retake to review, stop. - return; - } - - this.navCtrl.push('AddonModLessonPlayerPage', { - courseId: this.courseId, - lessonId: this.lesson.id, - pageId: this.retakeToReview.pageid, - password: this.password, - review: true, - retake: this.retakeToReview.retake - }); - } - - /** - * Set a group to view the reports. - * - * @param groupId Group ID. - * @return Promise resolved when done. - */ - protected setGroup(groupId: number): Promise { - this.group = groupId; - this.selectedGroupName = ''; - - // Search the name of the group if it isn't all participants. - if (groupId && this.groupInfo && this.groupInfo.groups) { - for (let i = 0; i < this.groupInfo.groups.length; i++) { - const group = this.groupInfo.groups[i]; - if (groupId == group.id) { - this.selectedGroupName = group.name; - break; - } - } - } - - // Get the overview of retakes for the group. - return this.lessonProvider.getRetakesOverview(this.lesson.id, { - groupId, - cmId: this.lesson.coursemodule, - }).then((data) => { - const promises = []; - - // Format times and grades. - if (data && data.avetime != null && data.numofattempts) { - data.avetime = Math.floor(data.avetime / data.numofattempts); - data.avetimeReadable = this.timeUtils.formatTime(data.avetime); - } - - if (data && data.hightime != null) { - data.hightimeReadable = this.timeUtils.formatTime(data.hightime); - } - - if (data && data.lowtime != null) { - data.lowtimeReadable = this.timeUtils.formatTime(data.lowtime); - } - - if (data && data.lessonscored) { - if (data.numofattempts) { - data.avescore = this.textUtils.roundToDecimals(data.avescore, 2); - } - if (data.highscore != null) { - data.highscore = this.textUtils.roundToDecimals(data.highscore, 2); - } - if (data.lowscore != null) { - data.lowscore = this.textUtils.roundToDecimals(data.lowscore, 2); - } - } - - if (data && data.students) { - // Get the user data for each student returned. - data.students.forEach((student) => { - student.bestgrade = this.textUtils.roundToDecimals(student.bestgrade, 2); - - promises.push(this.userProvider.getProfile(student.id, this.courseId, true).then((user) => { - student.profileimageurl = user.profileimageurl; - }).catch(() => { - // Error getting profile, resolve promise without adding any extra data. - })); - }); - } - - return this.utils.allPromises(promises).catch(() => { - // Shouldn't happen. - }).then(() => { - this.overview = data; - }); - }); - } - - /** - * Displays some data based on the current status. - * - * @param status The current status. - * @param previousStatus The previous status. If not defined, there is no previous status. - */ - protected showStatus(status: string, previousStatus?: string): void { - this.showSpinner = status == CoreConstants.DOWNLOADING; - } - - /** - * Start the lesson. - * - * @param continueLast Whether to continue the last attempt. - */ - start(continueLast?: boolean): void { - if (this.showSpinner) { - // Lesson is being downloaded, abort. - return; - } - - if (this.lessonProvider.isLessonOffline(this.lesson)) { - // Lesson supports offline, check if it needs to be downloaded. - if (this.currentStatus != CoreConstants.DOWNLOADED) { - // Prefetch the lesson. - this.showSpinner = true; - - this.prefetchHandler.prefetch(this.module, this.courseId, true).then(() => { - // Success downloading, open lesson. - this.playLesson(continueLast); - }).catch((error) => { - if (this.hasOffline) { - // Error downloading but there is something offline, allow continuing it. - this.playLesson(continueLast); - } else { - this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); - } - }).finally(() => { - this.showSpinner = false; - }); - } else { - // Already downloaded, open it. - this.playLesson(continueLast); - } - } else { - this.playLesson(continueLast); - } - } - - /** - * Submit password for password protected lessons. - * - * @param e Event. - * @param passwordEl The password input. - */ - submitPassword(e: Event, passwordEl: HTMLInputElement): void { - e.preventDefault(); - e.stopPropagation(); - - const password = passwordEl && passwordEl.value; - if (!password) { - this.domUtils.showErrorModal('addon.mod_lesson.emptypassword', true); - - return; - } - - this.loaded = false; - this.refreshIcon = 'spinner'; - this.syncIcon = 'spinner'; - - this.validatePassword(password).then(() => { - // Password validated. - this.lessonReady(false); - - // Now that we have the password, get the access reason again ignoring the password. - const preventReason = this.lessonProvider.getPreventAccessReason(this.accessInfo, true); - if (preventReason) { - this.preventMessages = [preventReason]; - } else { - this.preventMessages = []; - } - - // Log view now that we have the password. - this.logView(); - }).catch((error) => { - this.domUtils.showErrorModal(error); - }).finally(() => { - this.loaded = true; - this.refreshIcon = 'refresh'; - this.syncIcon = 'sync'; - - this.domUtils.triggerFormSubmittedEvent(this.formElement, true, this.siteId); - }); - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.lessonSync.syncLesson(this.lesson.id, true).then((result) => { - if (!result.updated && this.dataSent && this.isPrefetched()) { - // The user sent data to server, but not in the sync process. Check if we need to fetch data. - return this.lessonSync.prefetchAfterUpdate(this.module, this.courseId).catch(() => { - // Ignore errors. - }).then(() => { - return result; - }); - } - - return result; - }); - } - - /** - * Validate a password and retrieve extra data. - * - * @param password The password to validate. - * @return Promise resolved when done. - */ - protected validatePassword(password: string): Promise { - return this.lessonProvider.getLessonWithPassword(this.lesson.id, {password, cmId: this.module.id}).then((lessonData) => { - this.lesson = lessonData; - this.password = password; - }).catch((error) => { - this.password = ''; - - return Promise.reject(error); - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - - this.dataSentObserver && this.dataSentObserver.off(); - } -} diff --git a/src/addon/mod/lesson/lang/en.json b/src/addon/mod/lesson/lang/en.json deleted file mode 100644 index 3c310fca4..000000000 --- a/src/addon/mod/lesson/lang/en.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "answer": "Answer", - "attempt": "Attempt: {{$a}}", - "attemptheader": "Attempt", - "attemptsremaining": "You have {{$a}} attempt(s) remaining", - "averagescore": "Average score", - "averagetime": "Average time", - "branchtable": "Content", - "cannotfindattempt": "Error: could not find attempt", - "cannotfinduser": "Error: could not find users", - "clusterjump": "Unseen question within a cluster", - "completed": "Completed", - "congratulations": "Congratulations - end of lesson reached", - "continue": "Continue", - "continuetonextpage": "Continue to next page.", - "defaultessayresponse": "Your essay will be graded by your teacher.", - "detailedstats": "Detailed statistics", - "didnotanswerquestion": "Did not answer this question.", - "displayofgrade": "Display of grade (for students only)", - "displayscorewithessays": "

You earned {{$a.score}} out of {{$a.tempmaxgrade}} for the automatically graded questions.

\n

Your {{$a.essayquestions}} essay question(s) will be graded and added into your final score at a later date.

\n

Your current grade without the essay question(s) is {{$a.score}} out of {{$a.grade}}.

", - "displayscorewithoutessays": "Your score is {{$a.score}} (out of {{$a.grade}}).", - "emptypassword": "Password cannot be empty", - "enterpassword": "Please enter the password:", - "eolstudentoutoftimenoanswers": "You did not answer any questions. You have received a 0 for this lesson.", - "errorprefetchrandombranch": "This lesson contains a jump to a random content page. It can't be attempted in the app until it has been started in a web browser.", - "errorreviewretakenotlast": "This attempt can no longer be reviewed because another attempt has been finished.", - "finish": "Finish", - "finishretakeoffline": "This attempt was finished offline.", - "firstwrong": "You have answered incorrectly. Would you like to attempt the question again? (If you now answer the question correctly, it will not count towards your final score.)", - "gotoendoflesson": "Go to the end of the lesson", - "grade": "Grade", - "highscore": "High score", - "hightime": "High time", - "leftduringtimed": "You have left during a timed lesson.
Please click on Continue to restart the lesson.", - "leftduringtimednoretake": "You have left during a timed lesson and you are
not allowed to retake or continue the lesson.", - "lessonmenu": "Lesson menu", - "lessonstats": "Lesson statistics", - "linkedmedia": "Linked media", - "loginfail": "Login failed, please try again...", - "lowscore": "Low score", - "lowtime": "Low time", - "maximumnumberofattemptsreached": "Maximum number of attempts reached - Moving to next page", - "modattemptsnoteacher": "Student review only works for students.", - "modulenameplural": "Lessons", - "noanswer": "One or more questions have no answer given. Please go back and submit an answer.", - "nolessonattempts": "No attempts have been made on this lesson.", - "nolessonattemptsgroup": "No attempts have been made by {{$a}} group members on this lesson.", - "notcompleted": "Not completed", - "numberofcorrectanswers": "Number of correct answers: {{$a}}", - "numberofpagesviewed": "Number of questions answered: {{$a}}", - "numberofpagesviewednotice": "Number of questions answered: {{$a.nquestions}} (You should answer at least {{$a.minquestions}})", - "ongoingcustom": "You have earned {{$a.score}} point(s) out of {{$a.currenthigh}} point(s) thus far.", - "ongoingnormal": "You have answered {{$a.correct}} correctly out of {{$a.viewed}} attempts.", - "or": "OR", - "overview": "Overview", - "preview": "Preview", - "progressbarteacherwarning2": "You will not see the progress bar because you can edit this lesson", - "progresscompleted": "You have completed {{$a}}% of the lesson", - "question": "Question", - "rawgrade": "Raw grade", - "reports": "Reports", - "response": "Response", - "retakefinishedinsync": "An offline attempt was synchronised. Do you want to review it?", - "retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "review": "Review", - "reviewlesson": "Review lesson", - "reviewquestionback": "Yes, I'd like to try again", - "reviewquestioncontinue": "No, I just want to go on to the next question", - "secondpluswrong": "Not quite. Would you like to try again?", - "submit": "Submit", - "teacherjumpwarning": "A {{$a.cluster}} jump or an {{$a.unseen}} jump is being used in this lesson. The next page jump will be used instead. Log in as a student to test these jumps.", - "teacherongoingwarning": "The ongoing score is only displayed for the student. Log in as a student to test the ongoing score.", - "teachertimerwarning": "Timer only works for students. Test the timer by logging in as a student.", - "thatsthecorrectanswer": "That's the correct answer", - "thatsthewronganswer": "That's the wrong answer", - "timeremaining": "Time remaining", - "timetaken": "Time taken", - "unseenpageinbranch": "Unseen question within a content page", - "warningretakefinished": "The attempt was finished on the site.", - "welldone": "Well done!", - "youhaveseen": "You have seen more than one page of this lesson already.
Do you want to start at the last page you saw?", - "youranswer": "Your answer", - "yourcurrentgradeisoutof": "Your current grade is {{$a.grade}} out of {{$a.total}}", - "youshouldview": "You should answer at least: {{$a}}" -} \ No newline at end of file diff --git a/src/addon/mod/lesson/lesson.module.ts b/src/addon/mod/lesson/lesson.module.ts deleted file mode 100644 index 29d874b01..000000000 --- a/src/addon/mod/lesson/lesson.module.ts +++ /dev/null @@ -1,82 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; -import { AddonModLessonComponentsModule } from './components/components.module'; -import { AddonModLessonProvider } from './providers/lesson'; -import { AddonModLessonOfflineProvider } from './providers/lesson-offline'; -import { AddonModLessonSyncProvider } from './providers/lesson-sync'; -import { AddonModLessonHelperProvider } from './providers/helper'; -import { AddonModLessonModuleHandler } from './providers/module-handler'; -import { AddonModLessonPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModLessonSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModLessonIndexLinkHandler } from './providers/index-link-handler'; -import { AddonModLessonGradeLinkHandler } from './providers/grade-link-handler'; -import { AddonModLessonReportLinkHandler } from './providers/report-link-handler'; -import { AddonModLessonListLinkHandler } from './providers/list-link-handler'; -import { AddonModLessonPushClickHandler } from './providers/push-click-handler'; - -// List of providers (without handlers). -export const ADDON_MOD_LESSON_PROVIDERS: any[] = [ - AddonModLessonProvider, - AddonModLessonOfflineProvider, - AddonModLessonSyncProvider, - AddonModLessonHelperProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModLessonComponentsModule - ], - providers: [ - AddonModLessonProvider, - AddonModLessonOfflineProvider, - AddonModLessonSyncProvider, - AddonModLessonHelperProvider, - AddonModLessonModuleHandler, - AddonModLessonPrefetchHandler, - AddonModLessonSyncCronHandler, - AddonModLessonIndexLinkHandler, - AddonModLessonGradeLinkHandler, - AddonModLessonReportLinkHandler, - AddonModLessonListLinkHandler, - AddonModLessonPushClickHandler - ] -}) -export class AddonModLessonModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModLessonModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModLessonPrefetchHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModLessonSyncCronHandler, linksDelegate: CoreContentLinksDelegate, - indexHandler: AddonModLessonIndexLinkHandler, gradeHandler: AddonModLessonGradeLinkHandler, - reportHandler: AddonModLessonReportLinkHandler, - listLinkHandler: AddonModLessonListLinkHandler, pushNotificationsDelegate: CorePushNotificationsDelegate, - pushClickHandler: AddonModLessonPushClickHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - cronDelegate.register(syncHandler); - linksDelegate.registerHandler(indexHandler); - linksDelegate.registerHandler(gradeHandler); - linksDelegate.registerHandler(reportHandler); - linksDelegate.registerHandler(listLinkHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - } -} diff --git a/src/addon/mod/lesson/pages/index/index.html b/src/addon/mod/lesson/pages/index/index.html deleted file mode 100644 index 691bb7d19..000000000 --- a/src/addon/mod/lesson/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/lesson/pages/index/index.module.ts b/src/addon/mod/lesson/pages/index/index.module.ts deleted file mode 100644 index 609d9a349..000000000 --- a/src/addon/mod/lesson/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModLessonComponentsModule } from '../../components/components.module'; -import { AddonModLessonIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModLessonIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModLessonComponentsModule, - IonicPageModule.forChild(AddonModLessonIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModLessonIndexPageModule {} diff --git a/src/addon/mod/lesson/pages/index/index.ts b/src/addon/mod/lesson/pages/index/index.ts deleted file mode 100644 index a48787281..000000000 --- a/src/addon/mod/lesson/pages/index/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModLessonIndexComponent } from '../../components/index/index'; - -/** - * Page that displays the lesson entry page. - */ -@IonicPage({ segment: 'addon-mod-lesson-index' }) -@Component({ - selector: 'page-addon-mod-lesson-index', - templateUrl: 'index.html', -}) -export class AddonModLessonIndexPage { - @ViewChild(AddonModLessonIndexComponent) lessonComponent: AddonModLessonIndexComponent; - - title: string; - module: any; - courseId: number; - group: number; // The group to display. - action: string; // The "action" to display first. - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.group = navParams.get('group'); - this.action = navParams.get('action'); - this.title = this.module.name; - } - - /** - * Update some data based on the lesson instance. - * - * @param lesson Lesson instance. - */ - updateData(lesson: any): void { - this.title = lesson.name || this.title; - } - - /** - * User entered the page. - */ - ionViewDidEnter(): void { - this.lessonComponent.ionViewDidEnter(); - } - - /** - * User left the page. - */ - ionViewDidLeave(): void { - this.lessonComponent.ionViewDidLeave(); - } -} diff --git a/src/addon/mod/lesson/pages/menu-modal/menu-modal.html b/src/addon/mod/lesson/pages/menu-modal/menu-modal.html deleted file mode 100644 index a3fe189bc..000000000 --- a/src/addon/mod/lesson/pages/menu-modal/menu-modal.html +++ /dev/null @@ -1,36 +0,0 @@ - - - {{ pageInstance.lesson.name }} - - - - - - - - diff --git a/src/addon/mod/lesson/pages/menu-modal/menu-modal.module.ts b/src/addon/mod/lesson/pages/menu-modal/menu-modal.module.ts deleted file mode 100644 index 28f4a1f40..000000000 --- a/src/addon/mod/lesson/pages/menu-modal/menu-modal.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModLessonMenuModalPage } from './menu-modal'; -import { TranslateModule } from '@ngx-translate/core'; - -@NgModule({ - declarations: [ - AddonModLessonMenuModalPage - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonModLessonMenuModalPage), - TranslateModule.forChild() - ] -}) -export class AddonModLessonMenuModalPageModule {} diff --git a/src/addon/mod/lesson/pages/menu-modal/menu-modal.scss b/src/addon/mod/lesson/pages/menu-modal/menu-modal.scss deleted file mode 100644 index 8a686fb9c..000000000 --- a/src/addon/mod/lesson/pages/menu-modal/menu-modal.scss +++ /dev/null @@ -1,5 +0,0 @@ -ion-app.app-root page-addon-mod-lesson-menu-modal { - .addon-mod_lesson-selected, .item.addon-mod_lesson-selected { - background: $blue-light; - } -} diff --git a/src/addon/mod/lesson/pages/menu-modal/menu-modal.ts b/src/addon/mod/lesson/pages/menu-modal/menu-modal.ts deleted file mode 100644 index dfcece01b..000000000 --- a/src/addon/mod/lesson/pages/menu-modal/menu-modal.ts +++ /dev/null @@ -1,57 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, ViewController, NavParams } from 'ionic-angular'; - -/** - * Modal that renders the lesson menu and media file. - */ -@IonicPage({ segment: 'addon-mod-lesson-menu-modal' }) -@Component({ - selector: 'page-addon-mod-lesson-menu-modal', - templateUrl: 'menu-modal.html', -}) -export class AddonModLessonMenuModalPage { - - /** - * The instance of the page that opened the modal. We use the instance instead of the needed attributes for these reasons: - * - We want the user to be able to see the media file while the menu is being loaded, so we need to be able to update - * the menu dynamically based on the data retrieved by the page that opened the modal. - * - The onDidDismiss function takes a while to be called, making the app seem slow. This way we can directly call - * the functions we need without having to wait for the modal to be dismissed. - */ - pageInstance: any; - - constructor(params: NavParams, protected viewCtrl: ViewController) { - this.pageInstance = params.get('page'); - } - - /** - * Close modal. - */ - closeModal(): void { - this.viewCtrl.dismiss(); - } - - /** - * Load a certain page. - * - * @param pageId The page ID to load. - */ - loadPage(pageId: number): void { - this.pageInstance.changePage && this.pageInstance.changePage(pageId); - this.closeModal(); - } -} diff --git a/src/addon/mod/lesson/pages/password-modal/password-modal.html b/src/addon/mod/lesson/pages/password-modal/password-modal.html deleted file mode 100644 index 3c35a3dcc..000000000 --- a/src/addon/mod/lesson/pages/password-modal/password-modal.html +++ /dev/null @@ -1,26 +0,0 @@ - - - {{ 'core.login.password' | translate }} - - - - - - -
- - - {{ 'addon.mod_lesson.enterpassword' | translate }} - - - - - - -
-
diff --git a/src/addon/mod/lesson/pages/password-modal/password-modal.module.ts b/src/addon/mod/lesson/pages/password-modal/password-modal.module.ts deleted file mode 100644 index ea9b63c5c..000000000 --- a/src/addon/mod/lesson/pages/password-modal/password-modal.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { AddonModLessonPasswordModalPage } from './password-modal'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModLessonPasswordModalPage - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonModLessonPasswordModalPage), - TranslateModule.forChild() - ] -}) -export class AddonModLessonPasswordModalPageModule {} diff --git a/src/addon/mod/lesson/pages/password-modal/password-modal.ts b/src/addon/mod/lesson/pages/password-modal/password-modal.ts deleted file mode 100644 index 03278e4c0..000000000 --- a/src/addon/mod/lesson/pages/password-modal/password-modal.ts +++ /dev/null @@ -1,60 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, ViewController } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Modal that asks the password for a lesson. - */ -@IonicPage({ segment: 'addon-mod-lesson-password-modal' }) -@Component({ - selector: 'page-addon-mod-lesson-password-modal', - templateUrl: 'password-modal.html', -}) -export class AddonModLessonPasswordModalPage { - @ViewChild('passwordForm') formElement: ElementRef; - - constructor(protected viewCtrl: ViewController, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider, - protected domUtils: CoreDomUtilsProvider) { } - - /** - * Send the password back. - * - * @param e Event. - * @param password The input element. - */ - submitPassword(e: Event, password: HTMLInputElement): void { - e.preventDefault(); - e.stopPropagation(); - - this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId()); - - this.viewCtrl.dismiss(password.value); - } - - /** - * Close modal. - */ - closeModal(): void { - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - - this.viewCtrl.dismiss(); - } -} diff --git a/src/addon/mod/lesson/pages/player/player.html b/src/addon/mod/lesson/pages/player/player.html deleted file mode 100644 index dfe4c3e86..000000000 --- a/src/addon/mod/lesson/pages/player/player.html +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - - - - - - - -
- - {{ messages[0].message }} -
- -
- - - - - -

{{ 'addon.mod_lesson.attempt' | translate:{$a: retake} }}

-
- - {{ pageData.ongoingscore }} - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -

{{ 'addon.mod_lesson.youranswer' | translate }}

-

-
-
- - - - -
- - - - - - -
- - - - - - - - - - -
- - - - - - - -

-
- - - {{option.label}} - - -
-
-
-
-
- - - - -
-
- - - - - - - {{ button.content }} - - - - -

{{ 'addon.mod_lesson.progresscompleted' | translate:{$a: pageData.progress} }}

- -
-
- - {{ 'addon.mod_lesson.progressbarteacherwarning2' | translate }} -
-
- - - -
- - {{ 'addon.mod_lesson.finishretakeoffline' | translate }} -
- -

{{ 'addon.mod_lesson.congratulations' | translate }}

- - <{{ eolData.notenoughtimespent.message }} - - - {{ eolData.numberofpagesviewed.message }} - - - {{ eolData.youshouldview.message }} - - - {{ eolData.numberofcorrectanswers.message }} - - - - - - {{ eolData.displayscorewithoutessays.message }} - - - {{ eolData.yourcurrentgradeisoutof.message }} - - - {{ eolData.eolstudentoutoftimenoanswers.message }} - - - {{ eolData.welldone.message }} - - -

{{ 'addon.mod_lesson.progresscompleted' | translate:{$a: eolData.progresscompleted.value} }}

- -
- - {{ eolData.displayofgrade.message }} - - - - {{ 'addon.mod_lesson.reviewlesson' | translate }} - - - - {{ eolData.modattemptsnoteacher.message }} - - - - - - - - - - - - - -
- - - - - {{ processData.ongoingscore }} - - -

- -

-
-

{{ 'addon.mod_lesson.gotoendoflesson' | translate }}

-

{{ 'addon.mod_lesson.or' | translate }}

-

{{ 'addon.mod_lesson.continuetonextpage' | translate }}

-
-
- - {{ 'addon.mod_lesson.finish' | translate }} - {{ button.label | translate }} - -
-
-
-
diff --git a/src/addon/mod/lesson/pages/player/player.module.ts b/src/addon/mod/lesson/pages/player/player.module.ts deleted file mode 100644 index c6dd35097..000000000 --- a/src/addon/mod/lesson/pages/player/player.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModLessonPlayerPage } from './player'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModLessonPlayerPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonModLessonPlayerPage), - TranslateModule.forChild() - ], -}) -export class AddonModLessonPlayerPageModule {} diff --git a/src/addon/mod/lesson/pages/player/player.scss b/src/addon/mod/lesson/pages/player/player.scss deleted file mode 100644 index b31e8cab1..000000000 --- a/src/addon/mod/lesson/pages/player/player.scss +++ /dev/null @@ -1,67 +0,0 @@ -ion-app.app-root page-addon-mod-lesson-player { - .addon-mod_lesson-slideshow { - max-width: 100%; - max-height: 100%; - margin: 0 auto; - } - - ion-input[padding-left] input[padding-left] { - // Applying padding-left to the ion-input applies it twice since it's replicated in the inner input. - @include padding(null, null, null, 0); - } - - .addon-mod_lesson-pagebuttons .button-block { - height: 100%; - display: flex; - flex-direction: column; - - .button-inner { - flex-grow: 1; - } - } - - table { - width: 100%; - margin-top: 1.5rem; - - tr:nth-child(odd) { - background-color: $gray-lighter; - @include darkmode() { - background-color: $core-dark-item-divider-bg-color; - } - } - - tr:last-child td { - border-bottom: 0; - } - - td { - padding: 5px; - line-height: 1.5; - } - } - - .item-ios table td { - border-bottom: $hairlines-width solid $list-ios-border-color; - } - - .item-md table td { - border-bottom: 1px solid $list-md-border-color; - } - - .item-ios table { - @extend .card-ios; - @include darkmode() { - color: $white; - background-color: $core-dark-item-bg-color; - } - } - - .item-md table { - @extend .card-md; - @include darkmode() { - color: $white; - background-color: $core-dark-item-bg-color; - } - } -} diff --git a/src/addon/mod/lesson/pages/player/player.ts b/src/addon/mod/lesson/pages/player/player.ts deleted file mode 100644 index b2a69cd5e..000000000 --- a/src/addon/mod/lesson/pages/player/player.ts +++ /dev/null @@ -1,716 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, ElementRef } from '@angular/core'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { IonicPage, NavParams, Content, PopoverController, ModalController, Modal, NavController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { MoodleMobileApp } from '../../../../../app/app.component'; -import { AddonModLessonProvider } from '../../providers/lesson'; -import { AddonModLessonOfflineProvider } from '../../providers/lesson-offline'; -import { AddonModLessonSyncProvider } from '../../providers/lesson-sync'; -import { AddonModLessonHelperProvider } from '../../providers/helper'; - -/** - * Page that allows attempting and reviewing a lesson. - */ -@IonicPage({ segment: 'addon-mod-lesson-player' }) -@Component({ - selector: 'page-addon-mod-lesson-player', - templateUrl: 'player.html', -}) -export class AddonModLessonPlayerPage implements OnInit, OnDestroy { - @ViewChild(Content) content: Content; - @ViewChild('questionFormEl') formElement: ElementRef; - - component = AddonModLessonProvider.COMPONENT; - LESSON_EOL = AddonModLessonProvider.LESSON_EOL; - questionForm: FormGroup; // The FormGroup for question pages. - title: string; // The page title. - lesson: any; // The lesson object. - currentPage: number; // Current page being viewed. - review: boolean; // Whether the user is reviewing. - messages: any[]; // Messages to display to the user. - menuModal: Modal; // Modal to navigate through the pages. - canManage: boolean; // Whether the user can manage the lesson. - retake: number; // Current retake number. - showRetake: boolean; // Whether the retake number needs to be displayed. - lessonWidth: string; // Width of the lesson (if slideshow mode). - lessonHeight: string; // Height of the lesson (if slideshow mode). - endTime: number; // End time of the lesson if it's timed. - pageData: any; // Current page data. - pageContent: string; // Current page contents. - pageButtons: any[]; // List of buttons of the current page. - question: any; // Question of the current page (if it's a question page). - eolData: any; // Data for EOL page (if current page is EOL). - processData: any; // Data to display after processing a page. - loaded: boolean; // Whether data has been loaded. - displayMenu: boolean; // Whether the lesson menu should be displayed. - originalData: any; // Original question data. It is used to check if data has changed. - - protected courseId: number; // The course ID the lesson belongs to. - protected lessonId: number; // Lesson ID. - protected password: string; // Lesson password (if any). - protected forceLeave = false; // If true, don't perform any check when leaving the view. - protected offline: boolean; // Whether we are in offline mode. - protected accessInfo: any; // Lesson access info. - protected jumps: any; // All possible jumps. - protected mediaFile: any; // Media file of the lesson. - protected firstPageLoaded: boolean; // Whether the first page has been loaded. - protected loadingMenu: boolean; // Whether the lesson menu is being loaded. - protected lessonPages: any[]; // Lesson pages (for the lesson menu). - - constructor(protected navParams: NavParams, protected translate: TranslateService, - protected eventsProvider: CoreEventsProvider, protected sitesProvider: CoreSitesProvider, - protected syncProvider: CoreSyncProvider, protected domUtils: CoreDomUtilsProvider, popoverCtrl: PopoverController, - protected timeUtils: CoreTimeUtilsProvider, protected lessonProvider: AddonModLessonProvider, - protected lessonHelper: AddonModLessonHelperProvider, protected lessonSync: AddonModLessonSyncProvider, - protected lessonOfflineProvider: AddonModLessonOfflineProvider, protected cdr: ChangeDetectorRef, - modalCtrl: ModalController, protected navCtrl: NavController, protected appProvider: CoreAppProvider, - protected utils: CoreUtilsProvider, protected urlUtils: CoreUrlUtilsProvider, protected fb: FormBuilder, - protected mmApp: MoodleMobileApp) { - - this.lessonId = navParams.get('lessonId'); - this.courseId = navParams.get('courseId'); - this.password = navParams.get('password'); - this.review = !!navParams.get('review'); - this.currentPage = navParams.get('pageId'); - - // Block the lesson so it cannot be synced. - this.syncProvider.blockOperation(this.component, this.lessonId); - - // Create the navigation modal. - this.menuModal = modalCtrl.create('AddonModLessonMenuModalPage', { - page: this - }, { cssClass: 'core-modal-lateral', - showBackdrop: true, - enableBackdropDismiss: true, - enterAnimation: 'core-modal-lateral-transition', - leaveAnimation: 'core-modal-lateral-transition' }); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Fetch the Lesson data. - this.fetchLessonData().then((success) => { - if (success) { - // Review data loaded or new retake started, remove any retake being finished in sync. - this.lessonSync.deleteRetakeFinishedInSync(this.lessonId); - } - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - // Unblock the lesson so it can be synced. - this.syncProvider.unblockOperation(this.component, this.lessonId); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave) { - return; - } - - if (this.question && !this.eolData && !this.processData && this.originalData) { - // Question shown. Check if there is any change. - if (!this.utils.basicLeftCompare(this.questionForm.getRawValue(), this.originalData, 3)) { - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - } - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } - - /** - * Runs when the page is about to leave and no longer be the active page. - */ - ionViewWillLeave(): void { - this.mmApp.closeModal(); - } - - /** - * A button was clicked. - * - * @param data Button data. - */ - buttonClicked(data: any): void { - this.processPage(data); - } - - /** - * Call a function and go offline if allowed and the call fails. - * - * @param func Function to call. - * @param args Arguments to pass to the function. - * @param options Options passed to the function (also included in args). - * @return Promise resolved in success, rejected otherwise. - */ - protected callFunction(func: Function, args: any[], options: any): Promise { - return func.apply(func, args).catch((error) => { - if (!this.offline && !this.review && this.lessonProvider.isLessonOffline(this.lesson) && - !this.utils.isWebServiceError(error)) { - // If it fails, go offline. - this.offline = true; - - // Get the possible jumps now. - return this.lessonProvider.getPagesPossibleJumps(this.lesson.id, { - cmId: this.lesson.coursemodule, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - }).then((jumpList) => { - this.jumps = jumpList; - - // Call the function again with offline mode and the new jumps. - options.readingStrategy = CoreSitesReadingStrategy.PreferCache; - options.jumps = this.jumps; - options.offline = true; - - return func.apply(func, args); - }); - } - - return Promise.reject(error); - }); - } - - /** - * Change the page from menu or when continuing from a feedback page. - * - * @param pageId Page to load. - * @param ignoreCurrent If true, allow loading current page. - */ - changePage(pageId: number, ignoreCurrent?: boolean): void { - if (!ignoreCurrent && !this.eolData && this.currentPage == pageId) { - // Page already loaded, stop. - return; - } - - this.loaded = true; - this.messages = []; - - this.loadPage(pageId).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error loading page'); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Get the lesson data and load the page. - * - * @return Promise resolved with true if success, resolved with false otherwise. - */ - protected fetchLessonData(): Promise { - // Wait for any ongoing sync to finish. We won't sync a lesson while it's being played. - return this.lessonSync.waitForSync(this.lessonId).then(() => { - return this.lessonProvider.getLessonById(this.courseId, this.lessonId); - }).then((lessonData) => { - this.lesson = lessonData; - this.title = this.lesson.name; // Temporary title. - - // If lesson has offline data already, use offline mode. - return this.lessonOfflineProvider.hasOfflineData(this.lessonId); - }).then((offlineMode) => { - this.offline = offlineMode; - - if (!offlineMode && !this.appProvider.isOnline() && this.lessonProvider.isLessonOffline(this.lesson) && !this.review) { - // Lesson doesn't have offline data, but it allows offline and the device is offline. Use offline mode. - this.offline = true; - } - - const options = { - cmId: this.lesson.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }; - - return this.callFunction(this.lessonProvider.getAccessInformation.bind(this.lessonProvider), - [this.lesson.id, options], options); - }).then((info) => { - const promises = []; - - this.accessInfo = info; - this.canManage = info.canmanage; - this.retake = info.attemptscount; - this.showRetake = !this.currentPage && this.retake > 0; // Only show it in first page if it isn't the first retake. - - if (info.preventaccessreasons && info.preventaccessreasons.length) { - // If it's a password protected lesson and we have the password, allow playing it. - const preventReason = this.lessonProvider.getPreventAccessReason(info, !!this.password, this.review); - if (preventReason) { - // Lesson cannot be played, show message and go back. - return Promise.reject(preventReason.message); - } - } - - if (this.review && this.navParams.get('retake') != info.attemptscount - 1) { - // Reviewing a retake that isn't the last one. Error. - return Promise.reject(this.translate.instant('addon.mod_lesson.errorreviewretakenotlast')); - } - - if (this.password) { - // Lesson uses password, get the whole lesson object. - const options = { - password: this.password, - cmId: this.lesson.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }; - promises.push(this.callFunction(this.lessonProvider.getLessonWithPassword.bind(this.lessonProvider), - [this.lesson.id, options], options).then((lesson) => { - this.lesson = lesson; - })); - } - - if (this.offline) { - // Offline mode, get the list of possible jumps to allow navigation. - promises.push(this.lessonProvider.getPagesPossibleJumps(this.lesson.id, { - cmId: this.lesson.coursemodule, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - }).then((jumpList) => { - this.jumps = jumpList; - })); - } - - return Promise.all(promises); - }).then(() => { - this.mediaFile = this.lesson.mediafiles && this.lesson.mediafiles[0]; - - this.lessonWidth = this.lesson.slideshow ? this.domUtils.formatPixelsSize(this.lesson.mediawidth) : ''; - this.lessonHeight = this.lesson.slideshow ? this.domUtils.formatPixelsSize(this.lesson.mediaheight) : ''; - - return this.launchRetake(this.currentPage); - }).then(() => { - return true; - }).catch((error) => { - // An error occurred. - let promise; - - if (this.review && this.navParams.get('retake') && this.utils.isWebServiceError(error)) { - // The user cannot review the retake. Unmark the retake as being finished in sync. - promise = this.lessonSync.deleteRetakeFinishedInSync(this.lessonId); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true); - this.forceLeave = true; - this.navCtrl.pop(); - - return false; - }); - }); - } - - /** - * Finish the retake. - * - * @param outOfTime Whether the retake is finished because the user ran out of time. - * @return Promise resolved when done. - */ - protected finishRetake(outOfTime?: boolean): Promise { - let promise; - - this.messages = []; - - if (this.offline && this.appProvider.isOnline()) { - // Offline mode but the app is online. Try to sync the data. - promise = this.lessonSync.syncLesson(this.lesson.id, true, true).then((result) => { - if (result.warnings && result.warnings.length) { - const error = result.warnings[0]; - - // Some data was deleted. Check if the retake has changed. - return this.lessonProvider.getAccessInformation(this.lesson.id, { - cmId: this.lesson.coursemodule, - }).then((info) => { - if (info.attemptscount != this.accessInfo.attemptscount) { - // The retake has changed. Leave the view and show the error. - this.forceLeave = true; - this.navCtrl.pop(); - - return Promise.reject(error); - } - - // Retake hasn't changed, show the warning and finish the retake in offline. - this.offline = false; - this.domUtils.showErrorModal(error); - }); - } - - this.offline = false; - }, () => { - // Ignore errors. - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - // Now finish the retake. - const options = { - password: this.password, - outOfTime, - review: this.review, - offline: this.offline, - accessInfo: this.accessInfo, - }; - const args = [this.lesson, this.courseId, options]; - - return this.callFunction(this.lessonProvider.finishRetake.bind(this.lessonProvider), args, options); - }).then((data) => { - this.title = this.lesson.name; - this.eolData = data.data; - this.messages = this.messages.concat(data.messages); - this.processData = undefined; - - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'lesson' }); - - // Format activity link if present. - if (this.eolData && this.eolData.activitylink) { - this.eolData.activitylink.value = this.lessonHelper.formatActivityLink(this.eolData.activitylink.value); - } - - // Format review lesson if present. - if (this.eolData && this.eolData.reviewlesson) { - const params = this.urlUtils.extractUrlParams(this.eolData.reviewlesson.value); - - if (!params || !params.pageid) { - // No pageid in the URL, the user cannot review (probably didn't answer any question). - delete this.eolData.reviewlesson; - } else { - this.eolData.reviewlesson.pageid = params.pageid; - } - } - }); - } - - /** - * Jump to a certain page after performing an action. - * - * @param pageId The page to load. - * @return Promise resolved when done. - */ - protected jumpToPage(pageId: number): Promise { - if (pageId === 0) { - // Not a valid page, return to entry view. - // This happens, for example, when the user clicks to go to previous page and there is no previous page. - this.forceLeave = true; - this.navCtrl.pop(); - - return Promise.resolve(); - } else if (pageId == AddonModLessonProvider.LESSON_EOL) { - // End of lesson reached. - return this.finishRetake(); - } - - // Load new page. - this.messages = []; - - return this.loadPage(pageId); - } - - /** - * Start or continue a retake. - * - * @param pageId The page to load. - * @return Promise resolved when done. - */ - protected launchRetake(pageId: number): Promise { - let promise; - - if (this.review) { - // Review mode, no need to launch the retake. - promise = Promise.resolve({}); - } else if (!this.offline) { - // Not in offline mode, launch the retake. - promise = this.lessonProvider.launchRetake(this.lesson.id, this.password, pageId); - } else { - // Check if there is a finished offline retake. - promise = this.lessonOfflineProvider.hasFinishedRetake(this.lesson.id).then((finished) => { - if (finished) { - // Always show EOL page. - pageId = AddonModLessonProvider.LESSON_EOL; - } - - return {}; - }); - } - - return promise.then((data) => { - this.currentPage = pageId || this.accessInfo.firstpageid; - this.messages = data.messages || []; - - if (this.lesson.timelimit && !this.accessInfo.canmanage) { - // Get the last lesson timer. - return this.lessonProvider.getTimers(this.lesson.id, { - cmId: this.lesson.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - }).then((timers) => { - this.endTime = timers[timers.length - 1].starttime + this.lesson.timelimit; - }); - } - }).then(() => { - return this.loadPage(this.currentPage); - }); - } - - /** - * Load the lesson menu. - * - * @return Promise resolved when done. - */ - protected loadMenu(): Promise { - if (this.loadingMenu) { - // Already loading. - return; - } - - this.loadingMenu = true; - - const options = { - password: this.password, - cmId: this.lesson.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }; - const args = [this.lessonId, options]; - - return this.callFunction(this.lessonProvider.getPages.bind(this.lessonProvider), args, options).then((pages) => { - this.lessonPages = pages.map((entry) => { - return entry.page; - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error loading menu.'); - }).finally(() => { - this.loadingMenu = false; - }); - } - - /** - * Load a certain page. - * - * @param pageId The page to load. - * @return Promise resolved when done. - */ - protected loadPage(pageId: number): Promise { - if (pageId == AddonModLessonProvider.LESSON_EOL) { - // End of lesson reached. - return this.finishRetake(); - } - - const options = { - password: this.password, - review: this.review, - inludeContents: true, - cmId: this.lesson.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - accessInfo: this.accessInfo, - jumps: this.jumps, - }; - const args = [this.lesson, pageId, options]; - - return this.callFunction(this.lessonProvider.getPageData.bind(this.lessonProvider), args, options).then((data) => { - if (data.newpageid == AddonModLessonProvider.LESSON_EOL) { - // End of lesson reached. - return this.finishRetake(); - } - - this.pageData = data; - this.title = data.page.title; - this.pageContent = this.lessonHelper.getPageContentsFromPageData(data); - this.loaded = true; - this.currentPage = pageId; - this.messages = this.messages.concat(data.messages); - - // Page loaded, hide EOL and feedback data if shown. - this.eolData = this.processData = undefined; - - if (this.lessonProvider.isQuestionPage(data.page.type)) { - // Create an empty FormGroup without controls, they will be added in getQuestionFromPageData. - this.questionForm = this.fb.group({}); - this.pageButtons = []; - this.question = this.lessonHelper.getQuestionFromPageData(this.questionForm, data); - this.originalData = this.questionForm.getRawValue(); // Use getRawValue to include disabled values. - } else { - this.pageButtons = this.lessonHelper.getPageButtonsFromHtml(data.pagecontent); - this.question = undefined; - this.originalData = undefined; - } - - if (data.displaymenu && !this.displayMenu) { - // Load the menu. - this.loadMenu(); - } - this.displayMenu = !!data.displaymenu; - - if (!this.firstPageLoaded) { - this.firstPageLoaded = true; - } else { - this.showRetake = false; - } - }); - } - - /** - * Process a page, sending some data. - * - * @param data The data to send. - * @param formSubmitted Whether a form was submitted. - * @return Promise resolved when done. - */ - protected processPage(data: any, formSubmitted?: boolean): Promise { - this.loaded = false; - - const options = { - password: this.password, - review: this.review, - offline: this.offline, - accessInfo: this.accessInfo, - jumps: this.jumps, - }; - const args = [this.lesson, this.courseId, this.pageData, data, options]; - - return this.callFunction(this.lessonProvider.processPage.bind(this.lessonProvider), args, options).then((result) => { - if (formSubmitted) { - this.domUtils.triggerFormSubmittedEvent(this.formElement, result.sent, this.sitesProvider.getCurrentSiteId()); - } - - if (!this.offline && !this.review && this.lessonProvider.isLessonOffline(this.lesson)) { - // Lesson allows offline and the user changed some data in server. Update cached data. - const retake = this.accessInfo.attemptscount; - const options = { - cmId: this.lesson.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - }; - - if (this.lessonProvider.isQuestionPage(this.pageData.page.type)) { - this.lessonProvider.getQuestionsAttemptsOnline(this.lessonId, retake, options); - } else { - this.lessonProvider.getContentPagesViewedOnline(this.lessonId, retake, options); - } - } - - if (result.nodefaultresponse || result.inmediatejump) { - // Don't display feedback or force a redirect to a new page. Load the new page. - return this.jumpToPage(result.newpageid); - } else { - - // Not inmediate jump, show the feedback. - result.feedback = this.lessonHelper.removeQuestionFromFeedback(result.feedback); - this.messages = result.messages; - this.processData = result; - this.processData.buttons = []; - - if (this.lesson.review && !result.correctanswer && !result.noanswer && !result.isessayquestion && - !result.maxattemptsreached && !result.reviewmode) { - // User can try again, show button to do so. - this.processData.buttons.push({ - label: 'addon.mod_lesson.reviewquestionback', - pageId: this.currentPage - }); - } - - // Button to continue. - if (this.lesson.review && !result.correctanswer && !result.noanswer && !result.isessayquestion && - !result.maxattemptsreached) { - /* If both the "Yes, I'd like to try again" and "No, I just want to go on to the next question" point to the - same page then don't show the "No, I just want to go on to the next question" button. It's confusing. */ - if (this.pageData.page.id != result.newpageid) { - // Button to continue the lesson (the page to go is configured by the teacher). - this.processData.buttons.push({ - label: 'addon.mod_lesson.reviewquestioncontinue', - pageId: result.newpageid - }); - } - } else { - this.processData.buttons.push({ - label: 'addon.mod_lesson.continue', - pageId: result.newpageid - }); - } - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error processing page'); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Review the lesson. - * - * @param pageId Page to load. - */ - reviewLesson(pageId: number): void { - this.loaded = false; - this.review = true; - this.offline = false; // Don't allow offline mode in review. - - this.loadPage(pageId).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error loading page'); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Submit a question. - * - * @param e Event. - */ - submitQuestion(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - this.loaded = false; - - // Use getRawValue to include disabled values. - const data = this.lessonHelper.prepareQuestionData(this.question, this.questionForm.getRawValue()); - - this.processPage(data, true).finally(() => { - this.loaded = true; - }); - } - - /** - * Time up. - */ - timeUp(): void { - // Time up called, hide the timer. - this.endTime = undefined; - this.loaded = false; - - this.finishRetake(true).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error finishing attempt'); - }).finally(() => { - this.loaded = true; - }); - } -} diff --git a/src/addon/mod/lesson/pages/user-retake/user-retake.html b/src/addon/mod/lesson/pages/user-retake/user-retake.html deleted file mode 100644 index dea9016d9..000000000 --- a/src/addon/mod/lesson/pages/user-retake/user-retake.html +++ /dev/null @@ -1,157 +0,0 @@ - - - {{ 'addon.mod_lesson.detailedstats' | translate }} - - - - - - - - - - - - -

{{student.fullname}}

- -
- - - - {{ 'addon.mod_lesson.attemptheader' | translate }} - - {{retake.label}} - - - - -
- - - -

{{ 'addon.mod_lesson.grade' | translate }}

-

{{ 'core.percentagenumber' | translate:{$a: retake.userstats.grade} }}

-
- - -

{{ 'addon.mod_lesson.rawgrade' | translate }}

-

{{ retake.userstats.gradeinfo.earned }} / {{ retake.userstats.gradeinfo.total }}

-
-
-
- -

{{ 'addon.mod_lesson.timetaken' | translate }}

-

{{ retake.userstats.timetakenReadable }}

-
- -

{{ 'addon.mod_lesson.completed' | translate }}

-

{{ retake.userstats.completed * 1000 | coreFormatDate }}

-
-
- - - - {{ 'addon.mod_lesson.notcompleted' | translate }} - - - - - - - -

{{page.qtype}}: {{page.title}}

-
- -

{{ 'addon.mod_lesson.question' | translate }}

-

-
- -

{{ 'addon.mod_lesson.answer' | translate }}

-
- -

{{ 'addon.mod_lesson.didnotanswerquestion' | translate }}

-
-
-
- - - - - - - -

-
-
-
- -
- - - - - -

- - - -
- - -
- - - -

{{ answer[0].value }}

- - - -
- - - - - -

-
- -

{{answer[0].value}}

- - - -
-
-
- - - -

- - - -
-
- - - -

- - - -
-
- - -

{{ 'addon.mod_lesson.response' | translate }}

-

-
- -

{{page.answerdata.score}}

-
-
-
-
-
-
-
diff --git a/src/addon/mod/lesson/pages/user-retake/user-retake.module.ts b/src/addon/mod/lesson/pages/user-retake/user-retake.module.ts deleted file mode 100644 index 4a625fcc8..000000000 --- a/src/addon/mod/lesson/pages/user-retake/user-retake.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonModLessonUserRetakePage } from './user-retake'; - -@NgModule({ - declarations: [ - AddonModLessonUserRetakePage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonModLessonUserRetakePage), - TranslateModule.forChild() - ], -}) -export class AddonModLessonUserRetakePageModule {} diff --git a/src/addon/mod/lesson/pages/user-retake/user-retake.scss b/src/addon/mod/lesson/pages/user-retake/user-retake.scss deleted file mode 100644 index 96cbd5ee3..000000000 --- a/src/addon/mod/lesson/pages/user-retake/user-retake.scss +++ /dev/null @@ -1,8 +0,0 @@ -ion-app.app-root page-addon-mod-lesson-user-retake { - .addon-mod_lesson-highlight { - background: $blue-light; - .label, .label p { - color: $blue-dark; - } - } -} diff --git a/src/addon/mod/lesson/pages/user-retake/user-retake.ts b/src/addon/mod/lesson/pages/user-retake/user-retake.ts deleted file mode 100644 index 3d6418495..000000000 --- a/src/addon/mod/lesson/pages/user-retake/user-retake.ts +++ /dev/null @@ -1,238 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModLessonProvider } from '../../providers/lesson'; -import { AddonModLessonHelperProvider } from '../../providers/helper'; - -/** - * Page that displays a retake made by a certain user. - */ -@IonicPage({ segment: 'addon-mod-lesson-user-retake' }) -@Component({ - selector: 'page-addon-mod-lesson-user-retake', - templateUrl: 'user-retake.html', -}) -export class AddonModLessonUserRetakePage implements OnInit { - - component = AddonModLessonProvider.COMPONENT; - lesson: any; // The lesson the retake belongs to. - courseId: number; // Course ID the lesson belongs to. - selectedRetake: number; // The retake to see. - student: any; // Data about the student and his retakes. - retake: any; // Data about the retake. - loaded: boolean; // Whether the data has been loaded. - - protected lessonId: number; // The lesson ID the retake belongs to. - protected userId: number; // User ID to see the retakes. - protected retakeNumber: number; // Number of the initial retake to see. - protected previousSelectedRetake: number; // To be able to detect the previous selected retake when it has changed. - - constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, protected textUtils: CoreTextUtilsProvider, - protected translate: TranslateService, protected domUtils: CoreDomUtilsProvider, - protected userProvider: CoreUserProvider, protected timeUtils: CoreTimeUtilsProvider, - protected lessonProvider: AddonModLessonProvider, protected lessonHelper: AddonModLessonHelperProvider, - protected utils: CoreUtilsProvider) { - - this.lessonId = navParams.get('lessonId'); - this.courseId = navParams.get('courseId'); - this.userId = navParams.get('userId') || sitesProvider.getCurrentSiteUserId(); - this.retakeNumber = navParams.get('retake'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Fetch the data. - this.fetchData().finally(() => { - this.loaded = true; - }); - } - - /** - * Change the retake displayed. - * - * @param retakeNumber The new retake number. - */ - changeRetake(retakeNumber: number): void { - this.loaded = false; - - this.setRetake(retakeNumber).catch((error) => { - this.selectedRetake = this.previousSelectedRetake; - this.domUtils.showErrorModal(this.utils.addDataNotDownloadedError(error, 'Error getting attempt.')); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Pull to refresh. - * - * @param refresher Refresher. - */ - doRefresh(refresher: any): void { - this.refreshData().finally(() => { - refresher.complete(); - }); - } - - /** - * Get lesson and retake data. - * - * @return Promise resolved when done. - */ - protected fetchData(): Promise { - return this.lessonProvider.getLessonById(this.courseId, this.lessonId).then((lessonData) => { - this.lesson = lessonData; - - // Get the retakes overview for all participants. - return this.lessonProvider.getRetakesOverview(this.lesson.id, { - cmId: this.lesson.coursemodule, - }); - }).then((data) => { - // Search the student. - let student; - - if (data && data.students) { - for (let i = 0; i < data.students.length; i++) { - if (data.students[i].id == this.userId) { - student = data.students[i]; - break; - } - } - } - - if (!student) { - // Student not found. - return Promise.reject(this.translate.instant('addon.mod_lesson.cannotfinduser')); - } - - if (!student.attempts || !student.attempts.length) { - // No retakes. - return Promise.reject(this.translate.instant('addon.mod_lesson.cannotfindattempt')); - } - - student.bestgrade = this.textUtils.roundToDecimals(student.bestgrade, 2); - student.attempts.forEach((retake) => { - if (!this.selectedRetake && this.retakeNumber == retake.try) { - // The retake specified as parameter exists. Use it. - this.selectedRetake = this.retakeNumber; - } - - retake.label = this.lessonHelper.getRetakeLabel(retake); - }); - - if (!this.selectedRetake) { - // Retake number not specified or not valid, use the last retake. - this.selectedRetake = student.attempts[student.attempts.length - 1].try; - } - - // Get the profile image of the user. - return this.userProvider.getProfile(student.id, this.courseId, true).then((user) => { - student.profileimageurl = user.profileimageurl; - - return student; - }).catch(() => { - // Error getting profile, resolve promise without adding any extra data. - return student; - }); - }).then((student) => { - this.student = student; - - return this.setRetake(this.selectedRetake); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting data.', true); - }); - } - - /** - * Refreshes data. - * - * @return Promise resolved when done. - */ - protected refreshData(): Promise { - const promises = []; - - promises.push(this.lessonProvider.invalidateLessonData(this.courseId)); - if (this.lesson) { - promises.push(this.lessonProvider.invalidateRetakesOverview(this.lesson.id)); - promises.push(this.lessonProvider.invalidateUserRetakesForUser(this.lesson.id, this.userId)); - } - - return Promise.all(promises).catch(() => { - // Ignore errors. - }).then(() => { - return this.fetchData(); - }); - } - - /** - * Set the retake to view and load its data. - * - * @param retakeNumber Retake number to set. - * @return Promise resolved when done. - */ - protected setRetake(retakeNumber: number): Promise { - this.selectedRetake = retakeNumber; - - return this.lessonProvider.getUserRetake(this.lessonId, retakeNumber, { - cmId: this.lesson.coursemodule, - userId: this.userId, - }).then((data) => { - - if (data && data.completed != -1) { - // Completed. - data.userstats.grade = this.textUtils.roundToDecimals(data.userstats.grade, 2); - data.userstats.timetakenReadable = this.timeUtils.formatTime(data.userstats.timetotake); - } - - if (data && data.answerpages) { - // Format pages data. - data.answerpages.forEach((page) => { - if (this.lessonProvider.answerPageIsContent(page)) { - page.isContent = true; - - if (page.answerdata && page.answerdata.answers) { - page.answerdata.answers.forEach((answer) => { - // Content pages only have 1 valid field in the answer array. - answer[0] = this.lessonHelper.getContentPageAnswerDataFromHtml(answer[0]); - }); - } - } else if (this.lessonProvider.answerPageIsQuestion(page)) { - page.isQuestion = true; - - if (page.answerdata && page.answerdata.answers) { - page.answerdata.answers.forEach((answer) => { - // Only the first field of the answer array requires to be parsed. - answer[0] = this.lessonHelper.getQuestionPageAnswerDataFromHtml(answer[0]); - }); - } - } - }); - } - - this.retake = data; - this.previousSelectedRetake = this.selectedRetake; - }); - } -} diff --git a/src/addon/mod/lesson/providers/grade-link-handler.ts b/src/addon/mod/lesson/providers/grade-link-handler.ts deleted file mode 100644 index 7c0cc6c34..000000000 --- a/src/addon/mod/lesson/providers/grade-link-handler.ts +++ /dev/null @@ -1,95 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksModuleGradeHandler } from '@core/contentlinks/classes/module-grade-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModLessonProvider } from './lesson'; - -/** - * Handler to treat links to lesson grade. - */ -@Injectable() -export class AddonModLessonGradeLinkHandler extends CoreContentLinksModuleGradeHandler { - name = 'AddonModLessonGradeLinkHandler'; - canReview = true; - - constructor(courseHelper: CoreCourseHelperProvider, domUtils: CoreDomUtilsProvider, sitesProvider: CoreSitesProvider, - protected lessonProvider: AddonModLessonProvider, protected courseProvider: CoreCourseProvider, - protected linkHelper: CoreContentLinksHelperProvider) { - super(courseHelper, domUtils, sitesProvider, 'AddonModLesson', 'lesson'); - } - - /** - * Go to the page to review. - * - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. - * @param siteId Site to use. - * @param navCtrl Nav Controller to use to navigate. - * @return Promise resolved when done. - */ - protected goToReview(url: string, params: any, courseId: number, siteId: string, navCtrl?: NavController): Promise { - - const moduleId = parseInt(params.id, 10), - modal = this.domUtils.showModalLoading(); - let module; - - return this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((mod) => { - module = mod; - courseId = module.course || courseId || params.courseid || params.cid; - - // Check if the user can see the user reports in the lesson. - return this.lessonProvider.getAccessInformation(module.instance, {cmId: module.id, siteId}); - }).then((info) => { - if (info.canviewreports) { - // User can view reports, go to view the report. - const pageParams = { - courseId: Number(courseId), - lessonId: module.instance, - userId: parseInt(params.userid, 10) - }; - - this.linkHelper.goInSite(navCtrl, 'AddonModLessonUserRetakePage', pageParams, siteId); - } else { - // User cannot view the report, go to lesson index. - this.courseHelper.navigateToModule(moduleId, siteId, courseId, module.section, undefined, undefined, navCtrl); - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.lessonProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/lesson/providers/helper.ts b/src/addon/mod/lesson/providers/helper.ts deleted file mode 100644 index 099ace894..000000000 --- a/src/addon/mod/lesson/providers/helper.ts +++ /dev/null @@ -1,492 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { AddonModLessonProvider } from './lesson'; - -/** - * Helper service that provides some features for quiz. - */ -@Injectable() -export class AddonModLessonHelperProvider { - - constructor(private domUtils: CoreDomUtilsProvider, private fb: FormBuilder, private translate: TranslateService, - private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider, - private lessonProvider: AddonModLessonProvider) { } - - /** - * Given the HTML of next activity link, format it to extract the href and the text. - * - * @param activityLink HTML of the activity link. - * @return Formatted data. - */ - formatActivityLink(activityLink: string): {formatted: boolean, label: string, href: string} { - const element = this.domUtils.convertToElement(activityLink), - anchor = element.querySelector('a'); - - if (!anchor) { - // Anchor not found, return the original HTML. - return { - formatted: false, - label: activityLink, - href: '' - }; - } - - return { - formatted: true, - label: anchor.innerHTML, - href: anchor.href - }; - } - - /** - * Given the HTML of an answer from a content page, extract the data to render the answer. - * - * @param html Answer's HTML. - * @return Data to render the answer. - */ - getContentPageAnswerDataFromHtml(html: string): {buttonText: string, content: string} { - const data = { - buttonText: '', - content: '' - }, - element = this.domUtils.convertToElement(html); - - // Search the input button. - const button = element.querySelector('input[type="button"]'); - - if (button) { - // Extract the button content and remove it from the HTML. - data.buttonText = button.value; - button.remove(); - } - - data.content = element.innerHTML.trim(); - - return data; - } - - /** - * Get the buttons to change pages. - * - * @param html Page's HTML. - * @return List of buttons. - */ - getPageButtonsFromHtml(html: string): any[] { - const buttons = [], - element = this.domUtils.convertToElement(html); - - // Get the container of the buttons if it exists. - let buttonsContainer = element.querySelector('.branchbuttoncontainer'); - - if (!buttonsContainer) { - // Button container not found, might be a legacy lesson (from 1.9). - if (!element.querySelector('form input[type="submit"]')) { - // No buttons found. - return buttons; - } - buttonsContainer = element; - } - - const forms = Array.from(buttonsContainer.querySelectorAll('form')); - forms.forEach((form) => { - const buttonSelector = 'input[type="submit"], button[type="submit"]', - buttonEl = form.querySelector(buttonSelector), - inputs = Array.from(form.querySelectorAll('input')); - - if (!buttonEl || !inputs || !inputs.length) { - // Button not found or no inputs, ignore it. - return; - } - - const button = { - id: buttonEl.id, - title: buttonEl.title || buttonEl.value, - content: buttonEl.tagName == 'INPUT' ? buttonEl.value : buttonEl.innerHTML.trim(), - data: {} - }; - - inputs.forEach((input) => { - if (input.type != 'submit') { - button.data[input.name] = input.value; - } - }); - - buttons.push(button); - }); - - return buttons; - } - - /** - * Given a page data (result of getPageData), get the page contents. - * - * @param data Page data. - * @return Page contents. - */ - getPageContentsFromPageData(data: any): string { - // Search the page contents inside the whole page HTML. Use data.pagecontent because it's filtered. - const element = this.domUtils.convertToElement(data.pagecontent), - contents = element.querySelector('.contents'); - - if (contents) { - return contents.innerHTML.trim(); - } - - // Cannot find contents element. - if (this.lessonProvider.isQuestionPage(data.page.type) || - data.page.qtype == AddonModLessonProvider.LESSON_PAGE_BRANCHTABLE) { - // Return page.contents to prevent having duplicated elements (some elements like videos might not work). - return data.page.contents; - } else { - // It's an end of cluster, end of branch, etc. Return the whole pagecontent to match what's displayed in web. - return data.pagecontent; - } - } - - /** - * Get a question and all the data required to render it from the page data (result of AddonModLessonProvider.getPageData). - * - * @param questionForm The form group where to add the controls. - * @param pageData Page data (result of $mmaModLesson#getPageData). - * @return Question data. - */ - getQuestionFromPageData(questionForm: FormGroup, pageData: any): any { - const question: any = {}, - element = this.domUtils.convertToElement(pageData.pagecontent); - - // Get the container of the question answers if it exists. - const fieldContainer = element.querySelector('.fcontainer'); - - // Get hidden inputs and add their data to the form group. - const hiddenInputs = Array.from(element.querySelectorAll('input[type="hidden"]')); - hiddenInputs.forEach((input) => { - questionForm.addControl(input.name, this.fb.control(input.value)); - }); - - // Get the submit button and extract its value. - const submitButton = element.querySelector('input[type="submit"]'); - question.submitLabel = submitButton ? submitButton.value : this.translate.instant('addon.mod_lesson.submit'); - - if (!fieldContainer) { - // Element not found, return. - return question; - } - - let type; - - switch (pageData.page.qtype) { - case AddonModLessonProvider.LESSON_PAGE_TRUEFALSE: - case AddonModLessonProvider.LESSON_PAGE_MULTICHOICE: - question.template = 'multichoice'; - question.options = []; - - // Get all the inputs. Search radio first. - let inputs = Array.from(fieldContainer.querySelectorAll('input[type="radio"]')); - if (!inputs || !inputs.length) { - // Radio buttons not found, it might be a multi answer. Search for checkbox. - question.multi = true; - inputs = Array.from(fieldContainer.querySelectorAll('input[type="checkbox"]')); - - if (!inputs || !inputs.length) { - // No checkbox found either. Stop. - return question; - } - } - - let controlAdded = false; - inputs.forEach((input) => { - const option: any = { - id: input.id, - name: input.name, - value: input.value, - checked: !!input.checked, - disabled: !!input.disabled - }, - parent = input.parentElement; - - if (option.checked || question.multi) { - // Add the control. - const value = question.multi ? {value: option.checked, disabled: option.disabled} : option.value; - questionForm.addControl(option.name, this.fb.control(value)); - controlAdded = true; - } - - // Remove the input and use the rest of the parent contents as the label. - input.remove(); - option.text = parent.innerHTML.trim(); - - question.options.push(option); - }); - - if (!question.multi) { - question.controlName = inputs[0].name; // All option have the same name in single choice. - - if (!controlAdded) { - // No checked option for single choice, add the control with an empty value. - questionForm.addControl(question.controlName, this.fb.control('')); - } - } - - break; - - case AddonModLessonProvider.LESSON_PAGE_NUMERICAL: - type = 'number'; - case AddonModLessonProvider.LESSON_PAGE_SHORTANSWER: - question.template = 'shortanswer'; - - // Get the input. - const input = fieldContainer.querySelector('input[type="text"], input[type="number"]'); - if (!input) { - return question; - } - - question.input = { - id: input.id, - name: input.name, - maxlength: input.maxLength, - type: type || 'text' - }; - - // Init the control. - questionForm.addControl(input.name, this.fb.control({value: input.value, disabled: input.readOnly})); - break; - - case AddonModLessonProvider.LESSON_PAGE_ESSAY: - question.template = 'essay'; - - // Get the textarea. - const textarea = fieldContainer.querySelector('textarea'); - - if (!textarea) { - // Textarea not found, probably review mode. - const answerEl = fieldContainer.querySelector('.reviewessay'); - if (!answerEl) { - // Answer not found, stop. - return question; - } - question.useranswer = answerEl.innerHTML; - - } else { - question.textarea = { - id: textarea.id, - name: textarea.name || 'answer[text]' - }; - - // Init the control. - question.control = this.fb.control(''); - questionForm.addControl(question.textarea.name, question.control); - } - - break; - - case AddonModLessonProvider.LESSON_PAGE_MATCHING: - question.template = 'matching'; - - const rows = Array.from(fieldContainer.querySelectorAll('.answeroption')); - question.rows = []; - - rows.forEach((row) => { - const label = row.querySelector('label'), - select = row.querySelector('select'), - options = Array.from(row.querySelectorAll('option')), - rowData: any = {}; - - if (!label || !select || !options || !options.length) { - return; - } - - // Get the row's text (label). - rowData.text = label.innerHTML.trim(); - rowData.id = select.id; - rowData.name = select.name; - rowData.options = []; - - // Treat each option. - let controlAdded = false; - options.forEach((option) => { - if (typeof option.value == 'undefined') { - // Option not valid, ignore it. - return; - } - - const opt = { - value: option.value, - label: option.innerHTML.trim(), - selected: option.selected - }; - - if (opt.selected) { - controlAdded = true; - questionForm.addControl(rowData.name, this.fb.control({value: opt.value, disabled: !!select.disabled})); - } - - rowData.options.push(opt); - }); - - if (!controlAdded) { - // No selected option, add the control with an empty value. - questionForm.addControl(rowData.name, this.fb.control({value: '', disabled: !!select.disabled})); - } - - question.rows.push(rowData); - }); - break; - default: - // Nothing to do. - } - - return question; - } - - /** - * Given the HTML of an answer from a question page, extract the data to render the answer. - * - * @param html Answer's HTML. - * @return Object with the data to render the answer. If the answer doesn't require any parsing, return a string with - * the HTML. - */ - getQuestionPageAnswerDataFromHtml(html: string): any { - const data: any = {}, - element = this.domUtils.convertToElement(html); - - // Check if it has a checkbox. - let input = element.querySelector('input[type="checkbox"][name*="answer"]'); - - if (input) { - // Truefalse or multichoice. - data.isCheckbox = true; - data.checked = !!input.checked; - data.name = input.name; - data.highlight = !!element.querySelector('.highlight'); - - input.remove(); - data.content = element.innerHTML.trim(); - - return data; - } - - // Check if it has an input text or number. - input = element.querySelector('input[type="number"],input[type="text"]'); - if (input) { - // Short answer or numeric. - data.isText = true; - data.value = input.value; - - return data; - } - - // Check if it has a select. - const select = element.querySelector('select'); - if (select && select.options) { - // Matching. - const selectedOption = select.options[select.selectedIndex]; - data.isSelect = true; - data.id = select.id; - if (selectedOption) { - data.value = selectedOption.value; - } else { - data.value = ''; - } - - select.remove(); - data.content = element.innerHTML.trim(); - - return data; - } - - // The answer doesn't need any parsing, return the HTML as it is. - return html; - } - - /** - * Get a label to identify a retake (lesson attempt). - * - * @param retake Retake object. - * @param includeDuration Whether to include the duration of the retake. - * @return Retake label. - */ - getRetakeLabel(retake: any, includeDuration?: boolean): string { - const data = { - retake: retake.try + 1, - grade: '', - timestart: '', - duration: '' - }, - hasGrade = retake.grade != null; - - if (hasGrade || retake.end) { - // Retake finished with or without grade (if the lesson only has content pages, it has no grade). - if (hasGrade) { - data.grade = this.translate.instant('core.percentagenumber', {$a: retake.grade}); - } - data.timestart = this.timeUtils.userDate(retake.timestart * 1000); - if (includeDuration) { - data.duration = this.timeUtils.formatTime(retake.timeend - retake.timestart); - } - } else { - // The user has not completed the retake. - data.grade = this.translate.instant('addon.mod_lesson.notcompleted'); - if (retake.timestart) { - data.timestart = this.timeUtils.userDate(retake.timestart * 1000); - } - } - - return this.translate.instant('addon.mod_lesson.retakelabel' + (includeDuration ? 'full' : 'short'), data); - } - - /** - * Prepare the question data to be sent to server. - * - * @param question Question to prepare. - * @param data Data to prepare. - * @return Data to send. - */ - prepareQuestionData(question: any, data: any): any { - if (question.template == 'essay' && question.textarea) { - // Add some HTML to the answer if needed. - data[question.textarea.name] = this.textUtils.formatHtmlLines(data[question.textarea.name]); - } else if (question.template == 'multichoice' && question.multi) { - // Only send the options with value set to true. - for (const name in data) { - if (name.match(/answer\[\d+\]/) && data[name] == false) { - delete data[name]; - } - } - } - - return data; - } - - /** - * Given the feedback of a process page in HTML, remove the question text. - * - * @param html Feedback's HTML. - * @return Feedback without the question text. - */ - removeQuestionFromFeedback(html: string): string { - const element = this.domUtils.convertToElement(html); - - // Remove the question text. - this.domUtils.removeElement(element, '.generalbox:not(.feedback):not(.correctanswer)'); - - return element.innerHTML.trim(); - } -} diff --git a/src/addon/mod/lesson/providers/index-link-handler.ts b/src/addon/mod/lesson/providers/index-link-handler.ts deleted file mode 100644 index 62ffb8311..000000000 --- a/src/addon/mod/lesson/providers/index-link-handler.ts +++ /dev/null @@ -1,110 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModLessonProvider } from './lesson'; -import { NavController } from 'ionic-angular'; - -/** - * Handler to treat links to lesson index. - */ -@Injectable() -export class AddonModLessonIndexLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModLessonIndexLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, protected lessonProvider: AddonModLessonProvider, - protected domUtils: CoreDomUtilsProvider, protected courseProvider: CoreCourseProvider) { - super(courseHelper, 'AddonModLesson', 'lesson'); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - courseId = courseId || params.courseid || params.cid; - - return [{ - action: (siteId, navCtrl?): void => { - /* Ignore the pageid param. If we open the lesson player with a certain page and the user hasn't started - the lesson, an error is thrown: could not find lesson_timer records. */ - if (params.userpassword) { - this.navigateToModuleWithPassword(parseInt(params.id, 10), courseId, params.userpassword, siteId, navCtrl); - } else { - this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId, - undefined, undefined, undefined, navCtrl); - } - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.lessonProvider.isPluginEnabled(); - } - - /** - * Navigate to a lesson module (index page) with a fixed password. - * - * @param moduleId Module ID. - * @param courseId Course ID. - * @param password Password. - * @param siteId Site ID. - * @param navCtrl Navigation controller. - * @return Promise resolved when navigated. - */ - protected navigateToModuleWithPassword(moduleId: number, courseId: number, password: string, siteId: string, - navCtrl?: NavController): Promise { - const modal = this.domUtils.showModalLoading(); - - // Get the module. - return this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => { - courseId = courseId || module.course; - - // Store the password so it's automatically used. - return this.lessonProvider.storePassword(parseInt(module.instance, 10), password, siteId).catch(() => { - // Ignore errors. - }).then(() => { - return this.courseHelper.navigateToModule(moduleId, siteId, courseId, module.section, - undefined, undefined, navCtrl); - }); - }).catch(() => { - // Error, go to index page. - return this.courseHelper.navigateToModule(moduleId, siteId, courseId, undefined, undefined, undefined, navCtrl); - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/mod/lesson/providers/lesson-offline.ts b/src/addon/mod/lesson/providers/lesson-offline.ts deleted file mode 100644 index 53938e50b..000000000 --- a/src/addon/mod/lesson/providers/lesson-offline.ts +++ /dev/null @@ -1,606 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModLessonProvider } from './lesson'; - -/** - * Service to handle offline lesson. - */ -@Injectable() -export class AddonModLessonOfflineProvider { - - protected logger; - - // Variables for database. We use lowercase in the names to match the WS responses. - static RETAKES_TABLE = 'addon_mod_lesson_retakes'; - static PAGE_ATTEMPTS_TABLE = 'addon_mod_lesson_page_attempts'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModLessonOfflineProvider', - version: 1, - tables: [ - { - name: AddonModLessonOfflineProvider.RETAKES_TABLE, - columns: [ - { - name: 'lessonid', - type: 'INTEGER', - primaryKey: true // Only 1 offline retake per lesson. - }, - { - name: 'retake', // Retake number. - type: 'INTEGER', - notNull: true - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'finished', - type: 'INTEGER' - }, - { - name: 'outoftime', - type: 'INTEGER' - }, - { - name: 'timemodified', - type: 'INTEGER' - }, - { - name: 'lastquestionpage', - type: 'INTEGER' - }, - ] - }, - { - name: AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE, - columns: [ - { - name: 'lessonid', - type: 'INTEGER', - notNull: true - }, - { - name: 'retake', // Retake number. - type: 'INTEGER', - notNull: true - }, - { - name: 'pageid', - type: 'INTEGER', - notNull: true - }, - { - name: 'timemodified', - type: 'INTEGER', - notNull: true - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'data', - type: 'TEXT' - }, - { - name: 'type', - type: 'INTEGER' - }, - { - name: 'newpageid', - type: 'INTEGER' - }, - { - name: 'correct', - type: 'INTEGER' - }, - { - name: 'answerid', - type: 'INTEGER' - }, - { - name: 'useranswer', - type: 'TEXT' - }, - ], - // A user can attempt several times per page and retake. - primaryKeys: ['lessonid', 'retake', 'pageid', 'timemodified'] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private timeUtils: CoreTimeUtilsProvider, - private textUtils: CoreTextUtilsProvider, private utils: CoreUtilsProvider) { - this.logger = logger.getInstance('AddonModLessonOfflineProvider'); - - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete an offline attempt. - * - * @param lessonId Lesson ID. - * @param retake Lesson retake number. - * @param pageId Page ID. - * @param timemodified The timemodified of the attempt. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - deleteAttempt(lessonId: number, retake: number, pageId: number, timemodified: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE, { - lessonid: lessonId, - retake: retake, - pageid: pageId, - timemodified: timemodified - }); - }); - } - - /** - * Delete offline lesson retake. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - deleteRetake(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModLessonOfflineProvider.RETAKES_TABLE, {lessonid: lessonId}); - }); - } - - /** - * Delete offline attempts for a retake and page. - * - * @param lessonId Lesson ID. - * @param retake Lesson retake number. - * @param pageId Page ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - deleteRetakeAttemptsForPage(lessonId: number, retake: number, pageId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE, {lessonid: lessonId, - retake: retake, pageid: pageId}); - }); - } - - /** - * Mark a retake as finished. - * - * @param lessonId Lesson ID. - * @param courseId Course ID the lesson belongs to. - * @param retake Retake number. - * @param finished Whether retake is finished. - * @param outOfTime If the user ran out of time. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - finishRetake(lessonId: number, courseId: number, retake: number, finished?: boolean, outOfTime?: boolean, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - // Get current stored retake (if any). If not found, it will create a new one. - return this.getRetakeWithFallback(lessonId, courseId, retake, site.id).then((entry) => { - entry.finished = finished ? 1 : 0; - entry.outoftime = outOfTime ? 1 : 0; - entry.timemodified = this.timeUtils.timestamp(); - - return site.getDb().insertRecord(AddonModLessonOfflineProvider.RETAKES_TABLE, entry); - }); - }); - } - - /** - * Get all the offline page attempts in a certain site. - * - * @param siteId Site ID. If not set, use current site. - * @return Promise resolved when the offline attempts are retrieved. - */ - getAllAttempts(siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getAllRecords(AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE); - }).then((attempts) => { - return this.parsePageAttempts(attempts); - }); - } - - /** - * Get all the lessons that have offline data in a certain site. - * - * @param siteId Site ID. If not set, use current site. - * @return Promise resolved with an object containing the lessons. - */ - getAllLessonsWithData(siteId?: string): Promise { - const promises = [], - lessons = {}; - - // Get the lessons from page attempts. - promises.push(this.getAllAttempts(siteId).then((entries) => { - this.getLessonsFromEntries(lessons, entries); - }).catch(() => { - // Ignore errors. - })); - - // Get the lessons from retakes. - promises.push(this.getAllRetakes(siteId).then((entries) => { - this.getLessonsFromEntries(lessons, entries); - }).catch(() => { - // Ignore errors. - })); - - return Promise.all(promises).then(() => { - return this.utils.objectToArray(lessons); - }); - } - - /** - * Get all the offline retakes in a certain site. - * - * @param siteId Site ID. If not set, use current site. - * @return Promise resolved when the offline retakes are retrieved. - */ - getAllRetakes(siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getAllRecords(AddonModLessonOfflineProvider.RETAKES_TABLE); - }); - } - - /** - * Retrieve the last offline attempt stored in a retake. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the attempt (undefined if no attempts). - */ - getLastQuestionPageAttempt(lessonId: number, retake: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.getRetakeWithFallback(lessonId, 0, retake, siteId).then((retakeData) => { - if (!retakeData.lastquestionpage) { - // No question page attempted. - return; - } - - return this.getRetakeAttemptsForPage(lessonId, retake, retakeData.lastquestionpage, siteId).then((attempts) => { - // Return the attempt with highest timemodified. - return attempts.reduce((a, b) => { - return a.timemodified > b.timemodified ? a : b; - }); - }); - }).catch(() => { - // Error, return undefined. - }); - } - - /** - * Retrieve all offline attempts for a lesson. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the attempts. - */ - getLessonAttempts(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE, {lessonid: lessonId}); - }).then((attempts) => { - return this.parsePageAttempts(attempts); - }); - } - - /** - * Given a list of DB entries (either retakes or page attempts), get the list of lessons. - * - * @param lessons Object where to store the lessons. - * @param entries List of DB entries. - */ - protected getLessonsFromEntries(lessons: any, entries: any[]): void { - entries.forEach((entry) => { - if (!lessons[entry.lessonid]) { - lessons[entry.lessonid] = { - id: entry.lessonid, - courseId: entry.courseid - }; - } - }); - } - - /** - * Get attempts for question pages and retake in a lesson. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param correct True to only fetch correct attempts, false to get them all. - * @param pageId If defined, only get attempts on this page. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the attempts. - */ - getQuestionsAttempts(lessonId: number, retake: number, correct?: boolean, pageId?: number, siteId?: string): Promise { - let promise; - - if (pageId) { - // Page ID is set, only get the attempts for that page. - promise = this.getRetakeAttemptsForPage(lessonId, retake, pageId, siteId); - } else { - // Page ID not specified, get all the attempts. - promise = this.getRetakeAttemptsForType(lessonId, retake, AddonModLessonProvider.TYPE_QUESTION, siteId); - } - - return promise.then((attempts) => { - if (correct) { - return attempts.filter((attempt) => { - return !!attempt.correct; - }); - } - - return attempts; - }); - } - - /** - * Retrieve a retake from site DB. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the retake. - */ - getRetake(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecord(AddonModLessonOfflineProvider.RETAKES_TABLE, {lessonid: lessonId}); - }); - } - - /** - * Retrieve all offline attempts for a retake. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the retake attempts. - */ - getRetakeAttempts(lessonId: number, retake: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE, {lessonid: lessonId, retake: retake}); - }).then((attempts) => { - return this.parsePageAttempts(attempts); - }); - } - - /** - * Retrieve offline attempts for a retake and page. - * - * @param lessonId Lesson ID. - * @param retake Lesson retake number. - * @param pageId Page ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the retake attempts. - */ - getRetakeAttemptsForPage(lessonId: number, retake: number, pageId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE, {lessonid: lessonId, retake: retake, - pageid: pageId}); - }).then((attempts) => { - return this.parsePageAttempts(attempts); - }); - } - - /** - * Retrieve offline attempts for certain pages for a retake. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param type Type of the pages to get: TYPE_QUESTION or TYPE_STRUCTURE. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the retake attempts. - */ - getRetakeAttemptsForType(lessonId: number, retake: number, type: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE, {lessonid: lessonId, retake: retake, - type: type}); - }).then((attempts) => { - return this.parsePageAttempts(attempts); - }); - } - - /** - * Get stored retake. If not found or doesn't match the retake number, return a new one. - * - * @param lessonId Lesson ID. - * @param courseId Course ID the lesson belongs to. - * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the retake. - */ - protected getRetakeWithFallback(lessonId: number, courseId: number, retake: number, siteId?: string): Promise { - // Get current stored retake. - return this.getRetake(lessonId, siteId).then((retakeData) => { - if (retakeData.retake != retake) { - // The stored retake doesn't match the retake number, create a new one. - return Promise.reject(null); - } - - return retakeData; - }).catch(() => { - // No retake, create a new one. - return { - lessonid: lessonId, - retake: retake, - courseid: courseId, - finished: 0 - }; - }); - } - - /** - * Check if there is a finished retake for a certain lesson. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean. - */ - hasFinishedRetake(lessonId: number, siteId?: string): Promise { - return this.getRetake(lessonId, siteId).then((retake) => { - return !!retake.finished; - }).catch(() => { - return false; - }); - } - - /** - * Check if a lesson has offline data. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean. - */ - hasOfflineData(lessonId: number, siteId?: string): Promise { - const promises = []; - let hasData = false; - - promises.push(this.getRetake(lessonId, siteId).then(() => { - hasData = true; - }).catch(() => { - // Ignore errors. - })); - - promises.push(this.getLessonAttempts(lessonId, siteId).then((attempts) => { - hasData = hasData || !!attempts.length; - }).catch(() => { - // Ignore errors. - })); - - return Promise.all(promises).then(() => { - return hasData; - }); - } - - /** - * Check if there are offline attempts for a retake. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with a boolean. - */ - hasRetakeAttempts(lessonId: number, retake: number, siteId?: string): Promise { - return this.getRetakeAttempts(lessonId, retake, siteId).then((list) => { - return !!list.length; - }).catch(() => { - return false; - }); - } - - /** - * Parse some properties of a page attempt. - * - * @param attempt The attempt to treat. - * @return The treated attempt. - */ - protected parsePageAttempt(attempt: any): any { - attempt.data = this.textUtils.parseJSON(attempt.data); - attempt.useranswer = this.textUtils.parseJSON(attempt.useranswer); - - return attempt; - } - - /** - * Parse some properties of some page attempts. - * - * @param attempts The attempts to treat. - * @return The treated attempts. - */ - protected parsePageAttempts(attempts: any[]): any[] { - attempts.forEach((attempt) => { - this.parsePageAttempt(attempt); - }); - - return attempts; - } - - /** - * Process a lesson page, saving its data. - * - * @param lessonId Lesson ID. - * @param courseId Course ID the lesson belongs to. - * @param retake Retake number. - * @param page Page. - * @param data Data to save. - * @param newPageId New page ID (calculated). - * @param answerId The answer ID that the user answered. - * @param correct If answer is correct. Only for question pages. - * @param userAnswer The user's answer (userresponse from checkAnswer). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - processPage(lessonId: number, courseId: number, retake: number, page: any, data: any, newPageId: number, answerId?: number, - correct?: boolean, userAnswer?: any, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - lessonid: lessonId, - retake: retake, - pageid: page.id, - timemodified: this.timeUtils.timestamp(), - courseid: courseId, - data: data ? JSON.stringify(data) : null, - type: page.type, - newpageid: newPageId, - correct: correct ? 1 : 0, - answerid: Number(answerId), - useranswer: userAnswer ? JSON.stringify(userAnswer) : null, - }; - - return site.getDb().insertRecord(AddonModLessonOfflineProvider.PAGE_ATTEMPTS_TABLE, entry); - }).then(() => { - if (page.type == AddonModLessonProvider.TYPE_QUESTION) { - // It's a question page, set it as last question page attempted. - return this.setLastQuestionPageAttempted(lessonId, courseId, retake, page.id, siteId); - } - }); - } - - /** - * Set the last question page attempted in a retake. - * - * @param lessonId Lesson ID. - * @param courseId Course ID the lesson belongs to. - * @param retake Retake number. - * @param lastPage ID of the last question page attempted. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - setLastQuestionPageAttempted(lessonId: number, courseId: number, retake: number, lastPage: number, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - // Get current stored retake (if any). If not found, it will create a new one. - return this.getRetakeWithFallback(lessonId, courseId, retake, site.id).then((entry) => { - entry.lastquestionpage = lastPage; - entry.timemodified = this.timeUtils.timestamp(); - - return site.getDb().insertRecord(AddonModLessonOfflineProvider.RETAKES_TABLE, entry); - }); - }); - } -} diff --git a/src/addon/mod/lesson/providers/lesson-sync.ts b/src/addon/mod/lesson/providers/lesson-sync.ts deleted file mode 100644 index a5375d352..000000000 --- a/src/addon/mod/lesson/providers/lesson-sync.ts +++ /dev/null @@ -1,506 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreCourseActivitySyncBaseProvider } from '@core/course/classes/activity-sync'; -import { AddonModLessonProvider } from './lesson'; -import { AddonModLessonOfflineProvider } from './lesson-offline'; -import { AddonModLessonPrefetchHandler } from './prefetch-handler'; - -/** - * Data returned by a lesson sync. - */ -export interface AddonModLessonSyncResult { - /** - * List of warnings. - */ - warnings: string[]; - - /** - * Whether some data was sent to the server or offline data was updated. - */ - updated: boolean; -} - -/** - * Service to sync lesson. - */ -@Injectable() -export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_lesson_autom_synced'; - - protected componentTranslate: string; - - // Variables for database. - static RETAKES_FINISHED_TABLE = 'addon_mod_lesson_retakes_finished_sync'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModLessonSyncProvider', - version: 1, - tables: [ - { - name: AddonModLessonSyncProvider.RETAKES_FINISHED_TABLE, - columns: [ - { - name: 'lessonid', - type: 'INTEGER', - primaryKey: true - }, - { - name: 'retake', - type: 'INTEGER' - }, - { - name: 'pageid', - type: 'INTEGER' - }, - { - name: 'timefinished', - type: 'INTEGER' - } - ] - } - ] - }; - - constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, - syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService, - private courseProvider: CoreCourseProvider, private eventsProvider: CoreEventsProvider, - private lessonProvider: AddonModLessonProvider, private lessonOfflineProvider: AddonModLessonOfflineProvider, - protected prefetchHandler: AddonModLessonPrefetchHandler, timeUtils: CoreTimeUtilsProvider, - private utils: CoreUtilsProvider, private urlUtils: CoreUrlUtilsProvider, - private logHelper: CoreCourseLogHelperProvider, prefetchDelegate: CoreCourseModulePrefetchDelegate) { - - super('AddonModLessonSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils, prefetchDelegate, prefetchHandler); - - this.componentTranslate = courseProvider.translateModuleName('lesson'); - - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Unmark a retake as finished in a synchronization. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - deleteRetakeFinishedInSync(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModLessonSyncProvider.RETAKES_FINISHED_TABLE, {lessonid: lessonId}); - }).catch(() => { - // Ignore errors, maybe there is none. - }); - } - - /** - * Get a retake finished in a synchronization for a certain lesson (if any). - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the retake entry (undefined if no retake). - */ - getRetakeFinishedInSync(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecord(AddonModLessonSyncProvider.RETAKES_FINISHED_TABLE, {lessonid: lessonId}); - }).catch(() => { - // Ignore errors, return undefined. - }); - } - - /** - * Check if a lesson has data to synchronize. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether it has data to sync. - */ - hasDataToSync(lessonId: number, retake: number, siteId?: string): Promise { - const promises = []; - let hasDataToSync = false; - - promises.push(this.lessonOfflineProvider.hasRetakeAttempts(lessonId, retake, siteId).then((hasAttempts) => { - hasDataToSync = hasDataToSync || hasAttempts; - }).catch(() => { - // Ignore errors. - })); - - promises.push(this.lessonOfflineProvider.hasFinishedRetake(lessonId, siteId).then((hasFinished) => { - hasDataToSync = hasDataToSync || hasFinished; - })); - - return Promise.all(promises).then(() => { - return hasDataToSync; - }); - } - - /** - * Mark a retake as finished in a synchronization. - * - * @param lessonId Lesson ID. - * @param retake The retake number. - * @param pageId The page ID to start reviewing from. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - setRetakeFinishedInSync(lessonId: number, retake: number, pageId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().insertRecord(AddonModLessonSyncProvider.RETAKES_FINISHED_TABLE, { - lessonid: lessonId, - retake: Number(retake), - pageid: Number(pageId), - timefinished: this.timeUtils.timestamp() - }); - }); - } - - /** - * Try to synchronize all the lessons in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllLessons(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all lessons', this.syncAllLessonsFunc.bind(this), [force], siteId); - } - - /** - * Sync all lessons on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllLessonsFunc(siteId: string, force?: boolean): Promise { - // Get all the lessons that have something to be synchronized. - return this.lessonOfflineProvider.getAllLessonsWithData(siteId).then((lessons) => { - // Sync all lessons that haven't been synced for a while. - const promises = []; - - lessons.map((lesson) => { - const promise = force ? this.syncLesson(lesson.id, false, false, siteId) : - this.syncLessonIfNeeded(lesson.id, false, siteId); - - return promise.then((result) => { - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModLessonSyncProvider.AUTO_SYNCED, { - lessonId: lesson.id, - warnings: result.warnings - }, siteId); - } - }); - }); - - return Promise.all(promises); - }); - } - - /** - * Sync a lesson only if a certain time has passed since the last time. - * - * @param lessonId Lesson ID. - * @param askPreflight Whether we should ask for password if needed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the lesson is synced or if it doesn't need to be synced. - */ - syncLessonIfNeeded(lessonId: number, askPassword?: boolean, siteId?: string): Promise { - return this.isSyncNeeded(lessonId, siteId).then((needed) => { - if (needed) { - return this.syncLesson(lessonId, askPassword, false, siteId); - } - }); - } - - /** - * Try to synchronize a lesson. - * - * @param lessonId Lesson ID. - * @param askPassword True if we should ask for password if needed, false otherwise. - * @param ignoreBlock True to ignore the sync block setting. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success. - */ - syncLesson(lessonId: number, askPassword?: boolean, ignoreBlock?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const result: AddonModLessonSyncResult = { - warnings: [], - updated: false - }; - let syncPromise, - lesson, - courseId, - password, - accessInfo; - - if (this.isSyncing(lessonId, siteId)) { - // There's already a sync ongoing for this lesson, return the promise. - return this.getOngoingSync(lessonId, siteId); - } - - // Verify that lesson isn't blocked. - if (!ignoreBlock && this.syncProvider.isBlocked(AddonModLessonProvider.COMPONENT, lessonId, siteId)) { - this.logger.debug('Cannot sync lesson ' + lessonId + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug('Try to sync lesson ' + lessonId + ' in site ' + siteId); - - // Sync offline logs. - syncPromise = this.logHelper.syncIfNeeded(AddonModLessonProvider.COMPONENT, lessonId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Try to synchronize the attempts first. - return this.lessonOfflineProvider.getLessonAttempts(lessonId, siteId); - }).then((attempts) => { - if (!attempts.length) { - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - courseId = attempts[0].courseid; - - // Get the info, access info and the lesson password if needed. - return this.lessonProvider.getLessonById(courseId, lessonId, {siteId}).then((lessonData) => { - lesson = lessonData; - - return this.prefetchHandler.getLessonPassword(lessonId, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - askPassword, - siteId, - }); - }).then((data) => { - const attemptsLength = attempts.length, - promises = []; - - accessInfo = data.accessInfo; - password = data.password; - lesson = data.lesson || lesson; - - // Filter the attempts, get only the ones that belong to the current retake. - attempts = attempts.filter((attempt) => { - if (attempt.retake != accessInfo.attemptscount) { - // Attempt doesn't belong to current retake, delete. - promises.push(this.lessonOfflineProvider.deleteAttempt(lesson.id, attempt.retake, attempt.pageid, - attempt.timemodified, siteId).catch(() => { - // Ignore errors. - })); - - return false; - } - - return true; - }); - - if (attempts.length != attemptsLength) { - // Some attempts won't be sent, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: lesson.name, - error: this.translate.instant('addon.mod_lesson.warningretakefinished') - })); - } - - return Promise.all(promises); - }).then(() => { - if (!attempts.length) { - return; - } - - // Send the attempts in the same order they were answered. - attempts.sort((a, b) => { - return a.timemodified - b.timemodified; - }); - - attempts = attempts.map((attempt) => { - return { - func: this.sendAttempt.bind(this), - params: [lesson, password, attempt, result, siteId], - blocking: true - }; - }); - - return this.utils.executeOrderedPromises(attempts); - }); - }).then(() => { - // Attempts sent or there was none. If there is a finished retake, send it. - return this.lessonOfflineProvider.getRetake(lessonId, siteId).then((retake) => { - if (!retake.finished) { - // The retake isn't marked as finished, nothing to send. Delete the retake. - return this.lessonOfflineProvider.deleteRetake(lessonId, siteId); - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - let promise; - - courseId = retake.courseid || courseId; - - if (lesson) { - // Data already retrieved when syncing attempts. - promise = Promise.resolve(); - } else { - promise = this.lessonProvider.getLessonById(courseId, lessonId, {siteId}).then((lessonData) => { - lesson = lessonData; - - return this.prefetchHandler.getLessonPassword(lessonId, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - askPassword, - siteId, - }); - }).then((data) => { - accessInfo = data.accessInfo; - password = data.password; - lesson = data.lesson || lesson; - }); - } - - return promise.then(() => { - if (retake.retake != accessInfo.attemptscount) { - // The retake changed, add a warning if it isn't there already. - if (!result.warnings.length) { - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: lesson.name, - error: this.translate.instant('addon.mod_lesson.warningretakefinished') - })); - } - - return this.lessonOfflineProvider.deleteRetake(lessonId, siteId); - } - - // All good, finish the retake. - return this.lessonProvider.finishRetakeOnline(lessonId, {password, siteId}).then((response) => { - result.updated = true; - - if (!ignoreBlock) { - // Mark the retake as finished in a sync if it can be reviewed. - if (response.data && response.data.reviewlesson) { - const params = this.urlUtils.extractUrlParams(response.data.reviewlesson.value); - if (params && params.pageid) { - // The retake can be reviewed, mark it as finished. Don't block the user for this. - this.setRetakeFinishedInSync(lessonId, retake.retake, params.pageid, siteId); - } - } - } - - return this.lessonOfflineProvider.deleteRetake(lessonId, siteId); - }).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. Delete them. - result.updated = true; - - return this.lessonOfflineProvider.deleteRetake(lessonId, siteId).then(() => { - // Retake deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: lesson.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }); - }); - }, () => { - // No retake stored, nothing to do. - }); - }).then(() => { - if (result.updated && courseId) { - // Data has been sent to server, update data. - return this.courseProvider.getModuleBasicInfoByInstance(lessonId, 'lesson', siteId).then((module) => { - return this.prefetchAfterUpdate(module, courseId, undefined, siteId); - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(lessonId, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // All done, return the result. - return result; - }); - - return this.addOngoingSync(lessonId, syncPromise, siteId); - } - - /** - * Send an attempt to the site and delete it afterwards. - * - * @param lesson Lesson. - * @param password Password (if any). - * @param attempt Attempt to send. - * @param result Result where to store the data. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - protected sendAttempt(lesson: any, password: string, attempt: any, result: AddonModLessonSyncResult, siteId?: string) - : Promise { - - return this.lessonProvider.processPageOnline(lesson.id, attempt.pageid, attempt.data, { - password, - siteId, - }).then(() => { - result.updated = true; - - return this.lessonOfflineProvider.deleteAttempt(lesson.id, attempt.retake, attempt.pageid, attempt.timemodified, - siteId); - }).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that the attempt cannot be submitted. Delete it. - result.updated = true; - - return this.lessonOfflineProvider.deleteAttempt(lesson.id, attempt.retake, attempt.pageid, attempt.timemodified, - siteId).then(() => { - - // Attempt deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: lesson.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }); - } -} diff --git a/src/addon/mod/lesson/providers/lesson.ts b/src/addon/mod/lesson/providers/lesson.ts deleted file mode 100644 index 148ad611c..000000000 --- a/src/addon/mod/lesson/providers/lesson.ts +++ /dev/null @@ -1,3462 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreGradesProvider } from '@core/grades/providers/grades'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreSite } from '@classes/site'; -import { AddonModLessonOfflineProvider } from './lesson-offline'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Result of check answer. - */ -export interface AddonModLessonCheckAnswerResult { - answerid?: number; - noanswer?: boolean; - correctanswer?: boolean; - isessayquestion?: boolean; - response?: string; - newpageid?: number; - studentanswer?: any; - userresponse?: any; - feedback?: string; - nodefaultresponse?: boolean; - inmediatejump?: boolean; - studentanswerformat?: number; - useranswer?: any; -} - -/** - * Result of record attempt. - */ -export interface AddonModLessonRecordAttemptResult extends AddonModLessonCheckAnswerResult { - attemptsremaining?: number; - maxattemptsreached?: boolean; -} - -/** - * Result of lesson grade. - */ -export interface AddonModLessonGrade { - /** - * Number of questions answered. - */ - nquestions: number; - - /** - * Number of question attempts. - */ - attempts: number; - - /** - * Max points possible. - */ - total: number; - - /** - * Points earned by the student. - */ - earned: number; - - /** - * Calculated percentage grade. - */ - grade: number; - - /** - * Numer of manually graded questions. - */ - nmanual: number; - - /** - * Point value for manually graded questions. - */ - manualpoints: number; -} - -/** - * Service that provides some features for lesson. - * - * Lesson terminology is a bit confusing and ambiguous in Moodle. For that reason, in the app it has been decided to use - * the following terminology: - * - Retake: An attempt in a lesson. In Moodle it's sometimes called "attempt", "try" or "retry". - * - Attempt: An attempt in a page inside a retake. In the app, this includes content pages. - * - Content page: A page with only content (no question). In Moodle it's sometimes called "branch table". - * - Page answers: List of possible answers for a page (configured by the teacher). NOT the student answer for the page. - * - * This terminology sometimes won't match with WebServices names, params or responses. - */ -@Injectable() -export class AddonModLessonProvider { - static COMPONENT = 'mmaModLesson'; - static DATA_SENT_EVENT = 'addon_mod_lesson_data_sent'; - - // This page. - static LESSON_THISPAGE = 0; - // Next page -> any page not seen before. - static LESSON_UNSEENPAGE = 1; - // Next page -> any page not answered correctly. - static LESSON_UNANSWEREDPAGE = 2; - // Jump to Next Page. - static LESSON_NEXTPAGE = -1; - // End of Lesson. - static LESSON_EOL = -9; - // Jump to an unseen page within a branch and end of branch or end of lesson. - static LESSON_UNSEENBRANCHPAGE = -50; - // Jump to a random page within a branch and end of branch or end of lesson. - static LESSON_RANDOMPAGE = -60; - // Jump to a random Branch. - static LESSON_RANDOMBRANCH = -70; - // Cluster Jump. - static LESSON_CLUSTERJUMP = -80; - - // Type of page: question or structure (content). - static TYPE_QUESTION = 0; - static TYPE_STRUCTURE = 1; - - // Type of question pages. - static LESSON_PAGE_SHORTANSWER = 1; - static LESSON_PAGE_TRUEFALSE = 2; - static LESSON_PAGE_MULTICHOICE = 3; - static LESSON_PAGE_MATCHING = 5; - static LESSON_PAGE_NUMERICAL = 8; - static LESSON_PAGE_ESSAY = 10; - static LESSON_PAGE_BRANCHTABLE = 20; // Content page. - static LESSON_PAGE_ENDOFBRANCH = 21; - static LESSON_PAGE_CLUSTER = 30; - static LESSON_PAGE_ENDOFCLUSTER = 31; - - /** - * Constant used as a delimiter when parsing multianswer questions - */ - static MULTIANSWER_DELIMITER = '@^#|'; - - static LESSON_OTHER_ANSWERS = '@#wronganswer#@'; - - // Variables for database. - static PASSWORD_TABLE = 'addon_mod_lesson_password'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModLessonProvider', - version: 1, - tables: [ - { - name: AddonModLessonProvider.PASSWORD_TABLE, - columns: [ - { - name: 'lessonid', - type: 'INTEGER', - primaryKey: true - }, - { - name: 'password', - type: 'TEXT' - }, - { - name: 'timemodified', - type: 'INTEGER' - } - ] - } - ] - }; - - protected ROOT_CACHE_KEY = 'mmaModLesson:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, - private translate: TranslateService, private textUtils: CoreTextUtilsProvider, private domUtils: CoreDomUtilsProvider, - private lessonOfflineProvider: AddonModLessonOfflineProvider, private logHelper: CoreCourseLogHelperProvider, - private eventsProvider: CoreEventsProvider) { - this.logger = logger.getInstance('AddonModLessonProvider'); - - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Add an answer and its response to a feedback string (HTML). - * - * @param feedback The current feedback. - * @param answer Student answer. - * @param answerFormat Answer format. - * @param response Response. - * @param className Class to add to the response. - * @return New feedback. - */ - protected addAnswerAndResponseToFeedback(feedback: string, answer: string, answerFormat: number, response: string, - className: string): string { - - // Add a table row containing the answer. - feedback += '' + (answerFormat ? answer : this.textUtils.cleanTags(answer)) + - ''; - - // If the response exists, add a table row containing the response. If not, add en empty row. - if (response && response.trim()) { - feedback += '' + - this.translate.instant('addon.mod_lesson.response') + ':
' + - response + ''; - } else { - feedback += ''; - } - - return feedback; - } - - /** - * Add a message to a list of messages, following the format of the messages returned by WS. - * - * @param messages List of messages where to add the message. - * @param stringName The ID of the message to be translated. E.g. 'addon.mod_lesson.numberofpagesviewednotice'. - * @param stringParams The params of the message (if any). - */ - protected addMessage(messages: any[], stringName: string, stringParams?: any): void { - messages.push({ - message: this.translate.instant(stringName, stringParams) - }); - } - - /** - * Add a property to the result of the "process EOL page" simulation in offline. - * - * @param result Result where to add the value. - * @param name Name of the property. - * @param value Value to add. - * @param addMessage Whether to add a message related to the value. - */ - protected addResultValueEolPage(result: any, name: string, value: any, addMessage?: boolean): void { - let message = ''; - - if (addMessage) { - const params = typeof value != 'boolean' ? {$a: value} : undefined; - message = this.translate.instant('addon.mod_lesson.' + name, params); - } - - result.data[name] = { - name: name, - value: value, - message: message - }; - } - - /** - * Check if an answer page (from getUserRetake) is a content page. - * - * @param page Answer page. - * @return Whether it's a content page. - */ - answerPageIsContent(page: any): boolean { - // The page doesn't have any reliable field to use for checking this. Check qtype first (translated string). - if (page.qtype == this.translate.instant('addon.mod_lesson.branchtable')) { - return true; - } - - // The qtype doesn't match, but that doesn't mean it's not a content page, maybe the language is different. - // Check it's not a question page. - if (page.answerdata && !this.answerPageIsQuestion(page)) { - // It isn't a question page, but it can be an end of branch, etc. Check if the first answer has a button. - if (page.answerdata.answers && page.answerdata.answers[0]) { - const element = this.domUtils.convertToElement(page.answerdata.answers[0][0]); - - return !!element.querySelector('input[type="button"]'); - } - } - - return false; - } - - /** - * Check if an answer page (from getUserRetake) is a question page. - * - * @param page Answer page. - * @return Whether it's a question page. - */ - answerPageIsQuestion(page: any): boolean { - if (!page.answerdata) { - return false; - } - - if (page.answerdata.score) { - // Only question pages have a score. - return true; - } - - if (page.answerdata.answers) { - for (let i = 0; i < page.answerdata.answers.length; i++) { - const answer = page.answerdata.answers[i]; - if (answer[1]) { - // Only question pages have a statistic. - return true; - } - } - } - - return false; - } - - /** - * Calculate some offline data like progress and ongoingscore. - * - * @param lesson Lesson. - * @param options Other options. - * @return Promise resolved with the data. - */ - protected calculateOfflineData(lesson: any, options: AddonModLessonCalculateOfflineDataOptions = {}) - : Promise<{reviewmode: boolean, progress: number, ongoingscore: string}> { - - const accessInfo = options.accessInfo || {}; - const reviewMode = options.review || accessInfo.reviewmode, - promises = []; - let ongoingMessage = '', - progress: number; - - if (!accessInfo.canmanage) { - if (lesson.ongoing && !reviewMode) { - promises.push(this.getOngoingScoreMessage(lesson, accessInfo, options).then((message) => { - ongoingMessage = message; - })); - } - if (lesson.progressbar) { - const modOptions = { - cmId: lesson.coursemodule, - ...options, // Include all options. - }; - - promises.push(this.calculateProgress(lesson.id, accessInfo, modOptions).then((p) => { - progress = p; - })); - } - } - - return Promise.all(promises).then(() => { - return { - reviewmode: reviewMode, - progress: progress, - ongoingscore: ongoingMessage - }; - }); - } - - /** - * Calculate the progress of the current user in the lesson. - * Based on Moodle's calculate_progress. - * - * @param lessonId Lesson ID. - * @param accessInfo Result of get access info. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param pageIndex Object containing all the pages indexed by ID. If not defined, it will be calculated. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with a number: the progress (scale 0-100). - */ - calculateProgress(lessonId: number, accessInfo: any, options: AddonModLessonCalculateProgressOptions = {}): Promise { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - // Check if the user is reviewing the attempt. - if (options.review) { - return Promise.resolve(100); - } - - const retake = accessInfo.attemptscount; - const commonOptions = { - cmId: options.cmId, - siteId: options.siteId, - }; - let viewedPagesIds; - let promise; - - if (options.pageIndex) { - promise = Promise.resolve(); - } else { - // Retrieve the index. - promise = this.getPages(lessonId, { - cmId: options.cmId, - password: options.password, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId: options.siteId, - }).then((pages) => { - options.pageIndex = this.createPagesIndex(pages); - }); - } - - return promise.then(() => { - // Get the list of question pages attempted. - return this.getPagesIdsWithQuestionAttempts(lessonId, retake, commonOptions); - }).then((ids) => { - viewedPagesIds = ids; - - // Get the list of viewed content pages. - return this.getContentPagesViewedIds(lessonId, retake, commonOptions); - }).then((viewedContentPagesIds) => { - const validPages = {}; - let pageId = accessInfo.firstpageid; - - viewedPagesIds = this.utils.mergeArraysWithoutDuplicates(viewedPagesIds, viewedContentPagesIds); - - // Filter out the following pages: - // - End of Cluster - // - End of Branch - // - Pages found inside of Clusters - // Do not filter out Cluster Page(s) because we count a cluster as one. - // By keeping the cluster page, we get our 1. - while (pageId) { - pageId = this.validPageAndView(options.pageIndex, options.pageIndex[pageId], validPages, viewedPagesIds); - } - - // Progress calculation as a percent. - return this.textUtils.roundToDecimals(viewedPagesIds.length / Object.keys(validPages).length, 2) * 100; - }); - } - - /** - * Check if the answer provided by the user is correct or not and return the result object. - * This method is based on the check_answer implementation of all page types (Moodle). - * - * @param lesson Lesson. - * @param pageData Result of getPageData for the page to process. - * @param data Data containing the user answer. - * @param jumps Result of get pages possible jumps. - * @param pageIndex Object containing all the pages indexed by ID. - * @return Result. - */ - protected checkAnswer(lesson: any, pageData: any, data: any, jumps: any, pageIndex: any): AddonModLessonCheckAnswerResult { - // Default result. - const result: AddonModLessonCheckAnswerResult = { - answerid: 0, - noanswer: false, - correctanswer: false, - isessayquestion: false, - response: '', - newpageid: 0, - studentanswer: '', - userresponse: null, - feedback: '', - nodefaultresponse: false, - inmediatejump: false - }; - - switch (pageData.page.qtype) { - case AddonModLessonProvider.LESSON_PAGE_BRANCHTABLE: - // Load the new page immediately. - result.inmediatejump = true; - result.newpageid = this.getNewPageId(pageData.page.id, data.jumpto, jumps); - break; - - case AddonModLessonProvider.LESSON_PAGE_ESSAY: - this.checkAnswerEssay(pageData, data, result); - break; - - case AddonModLessonProvider.LESSON_PAGE_MATCHING: - this.checkAnswerMatching(pageData, data, result); - break; - - case AddonModLessonProvider.LESSON_PAGE_MULTICHOICE: - this.checkAnswerMultichoice(lesson, pageData, data, pageIndex, result); - break; - - case AddonModLessonProvider.LESSON_PAGE_NUMERICAL: - this.checkAnswerNumerical(lesson, pageData, data, pageIndex, result); - break; - - case AddonModLessonProvider.LESSON_PAGE_SHORTANSWER: - this.checkAnswerShort(lesson, pageData, data, pageIndex, result); - break; - - case AddonModLessonProvider.LESSON_PAGE_TRUEFALSE: - this.checkAnswerTruefalse(lesson, pageData, data, pageIndex, result); - break; - default: - // Nothing to do. - } - - return result; - } - - /** - * Check an essay answer. - * - * @param pageData Result of getPageData for the page to process. - * @param data Data containing the user answer. - * @param result Object where to store the result. - */ - protected checkAnswerEssay(pageData: any, data: any, result: AddonModLessonCheckAnswerResult): void { - let studentAnswer; - - result.isessayquestion = true; - - if (!data) { - result.inmediatejump = true; - result.newpageid = pageData.page.id; - - return; - } - - // The name was changed to "answer_editor" in 3.7. Before it was just "answer". Support both cases. - if (typeof data['answer_editor[text]'] != 'undefined') { - studentAnswer = data['answer_editor[text]']; - } else if (typeof data.answer_editor == 'object') { - studentAnswer = data.answer_editor.text; - } else if (typeof data['answer[text]'] != 'undefined') { - studentAnswer = data['answer[text]']; - } else if (typeof data.answer == 'object') { - studentAnswer = data.answer.text; - } else { - studentAnswer = data.answer; - } - - if (!studentAnswer || studentAnswer.trim() === '') { - result.noanswer = true; - - return; - } - - // Essay pages should only have 1 possible answer. - pageData.answers.forEach((answer) => { - result.answerid = answer.id; - result.newpageid = answer.jumpto; - }); - - result.userresponse = { - sent: 0, - graded: 0, - score: 0, - answer: studentAnswer, - answerformat: 1, - response: '', - responseformat: 1 - }; - result.studentanswerformat = 1; - result.studentanswer = studentAnswer; - } - - /** - * Check a matching answer. - * - * @param pageData Result of getPageData for the page to process. - * @param data Data containing the user answer. - * @param result Object where to store the result. - */ - protected checkAnswerMatching(pageData: any, data: any, result: AddonModLessonCheckAnswerResult): void { - if (!data) { - result.inmediatejump = true; - result.newpageid = pageData.page.id; - - return; - } - - const response = this.getUserResponseMatching(data), - getAnswers = this.utils.clone(pageData.answers), - correct = getAnswers.shift(), - wrong = getAnswers.shift(), - answers = {}; - - getAnswers.forEach((answer) => { - if (answer.answer !== '' || answer.response !== '') { - answers[answer.id] = answer; - } - }); - - // Get the user's exact responses for record keeping. - const userResponse = []; - let hits = 0; - - result.studentanswer = ''; - result.studentanswerformat = 1; - - for (const id in response) { - let value = response[id]; - - if (!value) { - result.noanswer = true; - - return; - } - - value = this.textUtils.decodeHTML(value); - userResponse.push(value); - - if (typeof answers[id] != 'undefined') { - const answer = answers[id]; - - result.studentanswer += '
' + answer.answer + ' = ' + value; - if (answer.response && answer.response.trim() == value.trim()) { - hits++; - } - } - } - - result.userresponse = userResponse.join(','); - - if (hits == Object.keys(answers).length) { - result.correctanswer = true; - result.response = correct.answer; - result.answerid = correct.id; - result.newpageid = correct.jumpto; - } else { - result.correctanswer = false; - result.response = wrong.answer; - result.answerid = wrong.id; - result.newpageid = wrong.jumpto; - } - } - - /** - * Check a multichoice answer. - * - * @param lesson Lesson. - * @param pageData Result of getPageData for the page to process. - * @param data Data containing the user answer. - * @param pageIndex Object containing all the pages indexed by ID. - * @param result Object where to store the result. - */ - protected checkAnswerMultichoice(lesson: any, pageData: any, data: any, pageIndex: any, - result: AddonModLessonCheckAnswerResult): void { - - if (!data) { - result.inmediatejump = true; - result.newpageid = pageData.page.id; - - return; - } - - const answers = this.getUsedAnswersMultichoice(pageData); - - if (pageData.page.qoption) { - // Multianswer allowed, user's answer is an array. - const studentAnswers = this.getUserResponseMultichoice(data); - - if (!studentAnswers || !Array.isArray(studentAnswers)) { - result.noanswer = true; - - return; - } - - // Get what the user answered. - result.userresponse = studentAnswers.join(','); - - // Get the answers in a set order, the id order. - const studentAswersArray = [], - responses = []; - let nHits = 0, - nCorrect = 0, - correctAnswerId = 0, - wrongAnswerId = 0, - correctPageId, - wrongPageId; - - // Store student's answers for displaying on feedback page. - result.studentanswer = ''; - result.studentanswerformat = 1; - answers.forEach((answer) => { - for (const i in studentAnswers) { - const answerId = studentAnswers[i]; - - if (answerId == answer.id) { - studentAswersArray.push(answer.answer); - responses.push(answer.response); - break; - } - } - }); - result.studentanswer = studentAswersArray.join(AddonModLessonProvider.MULTIANSWER_DELIMITER); - - // Iterate over all the possible answers. - answers.forEach((answer) => { - const correctAnswer = this.isAnswerCorrect(lesson, pageData.page.id, answer, pageIndex); - - // Iterate over all the student answers to check if he selected the current possible answer. - studentAnswers.forEach((answerId) => { - if (answerId == answer.id) { - if (correctAnswer) { - nHits++; - } else { - // Always use the first student wrong answer. - if (typeof wrongPageId == 'undefined') { - wrongPageId = answer.jumpto; - } - // Save the answer id for scoring. - if (!wrongAnswerId) { - wrongAnswerId = answer.id; - } - } - } - }); - - if (correctAnswer) { - nCorrect++; - - // Save the first jumpto. - if (typeof correctPageId == 'undefined') { - correctPageId = answer.jumpto; - } - // Save the answer id for scoring. - if (!correctAnswerId) { - correctAnswerId = answer.id; - } - } - }); - - if (studentAnswers.length == nCorrect && nHits == nCorrect) { - result.correctanswer = true; - result.response = responses.join(AddonModLessonProvider.MULTIANSWER_DELIMITER); - result.newpageid = correctPageId; - result.answerid = correctAnswerId; - } else { - result.correctanswer = false; - result.response = responses.join(AddonModLessonProvider.MULTIANSWER_DELIMITER); - result.newpageid = wrongPageId; - result.answerid = wrongAnswerId; - } - } else { - // Only one answer allowed. - if (typeof data.answerid == 'undefined' || (!data.answerid && Number(data.answerid) !== 0)) { - result.noanswer = true; - - return; - } - - result.answerid = data.answerid; - - // Search the answer. - for (const i in pageData.answers) { - const answer = pageData.answers[i]; - if (answer.id == data.answerid) { - result.correctanswer = this.isAnswerCorrect(lesson, pageData.page.id, answer, pageIndex); - result.newpageid = answer.jumpto; - result.response = answer.response; - result.userresponse = result.studentanswer = answer.answer; - break; - } - } - } - } - - /** - * Check a numerical answer. - * - * @param lesson Lesson. - * @param pageData Result of getPageData for the page to process. - * @param data Data containing the user answer. - * @param pageIndex Object containing all the pages indexed by ID. - * @param result Object where to store the result. - */ - protected checkAnswerNumerical(lesson: any, pageData: any, data: any, pageIndex: any, result: AddonModLessonCheckAnswerResult) - : void { - - const parsedAnswer = parseFloat(data.answer); - - // Set defaults. - result.response = ''; - result.newpageid = 0; - - if (!data.answer || isNaN(parsedAnswer)) { - result.noanswer = true; - - return; - } else { - result.useranswer = parsedAnswer; - } - - result.studentanswer = result.userresponse = result.useranswer; - - // Find the answer. - for (const i in pageData.answers) { - const answer = pageData.answers[i]; - let max, min; - - if (answer.answer && answer.answer.indexOf(':') != -1) { - // There's a pair of values. - const split = answer.answer.split(':'); - min = parseFloat(split[0]); - max = parseFloat(split[1]); - } else { - // Only one value. - min = parseFloat(answer.answer); - max = min; - } - - if (result.useranswer >= min && result.useranswer <= max) { - result.newpageid = answer.jumpto; - result.response = answer.response; - result.correctanswer = this.isAnswerCorrect(lesson, pageData.page.id, answer, pageIndex); - result.answerid = answer.id; - break; - } - } - - this.checkOtherAnswers(lesson, pageData, result); - } - - /** - * Check a short answer. - * - * @param lesson Lesson. - * @param pageData Result of getPageData for the page to process. - * @param data Data containing the user answer. - * @param pageIndex Object containing all the pages indexed by ID. - * @param result Object where to store the result. - */ - protected checkAnswerShort(lesson: any, pageData: any, data: any, pageIndex: any, result: AddonModLessonCheckAnswerResult) - : void { - - let studentAnswer = data.answer && data.answer.trim ? data.answer.trim() : false; - if (!studentAnswer) { - result.noanswer = true; - - return; - } - - // Search the answer in the list of possible answers. - for (const i in pageData.answers) { - const answer = pageData.answers[i], - useRegExp = pageData.page.qoption; - let expectedAnswer = answer.answer, - isMatch = false, - ignoreCase; - - if (useRegExp) { - ignoreCase = ''; - if (expectedAnswer.substr(-2) == '/i') { - expectedAnswer = expectedAnswer.substr(0, expectedAnswer.length - 2); - ignoreCase = 'i'; - } - } else { - expectedAnswer = expectedAnswer.replace('*', '#####'); - expectedAnswer = this.textUtils.escapeForRegex(expectedAnswer); - expectedAnswer = expectedAnswer.replace('#####', '.*'); - } - - // See if user typed in any of the correct answers. - if (this.isAnswerCorrect(lesson, pageData.page.id, answer, pageIndex)) { - if (!useRegExp) { // We are using 'normal analysis', which ignores case. - if (studentAnswer.match(new RegExp('^' + expectedAnswer + '$', 'i'))) { - isMatch = true; - } - } else { - if (studentAnswer.match(new RegExp('^' + expectedAnswer + '$', ignoreCase))) { - isMatch = true; - } - } - if (isMatch) { - result.correctanswer = true; - } - } else { - if (!useRegExp) { // We are using 'normal analysis'. - // See if user typed in any of the wrong answers; don't worry about case. - if (studentAnswer.match(new RegExp('^' + expectedAnswer + '$', 'i'))) { - isMatch = true; - } - } else { // We are using regular expressions analysis. - const startCode = expectedAnswer.substr(0, 2); - - switch (startCode){ - // 1- Check for absence of required string in studentAnswer (coded by initial '--'). - case '--': - expectedAnswer = expectedAnswer.substr(2); - if (!studentAnswer.match(new RegExp('^' + expectedAnswer + '$', ignoreCase))) { - isMatch = true; - } - break; - - // 2- Check for code for marking wrong strings (coded by initial '++'). - case '++': - expectedAnswer = expectedAnswer.substr(2); - - // Check for one or several matches. - const matches = studentAnswer.match(new RegExp(expectedAnswer, 'g' + ignoreCase)); - if (matches) { - isMatch = true; - const nb = matches[0].length, - original = [], - marked = []; - - for (let j = 0; j < nb; j++) { - original.push(matches[0][j]); - marked.push('' + matches[0][j] + ''); - } - - studentAnswer = studentAnswer.replace(original, marked); - } - break; - - // 3- Check for wrong answers belonging neither to -- nor to ++ categories. - default: - if (studentAnswer.match(new RegExp('^' + expectedAnswer + '$', ignoreCase))) { - isMatch = true; - } - break; - } - - result.correctanswer = false; - } - } - - if (isMatch) { - result.newpageid = answer.jumpto; - result.response = answer.response; - result.answerid = answer.id; - break; // Quit answer analysis immediately after a match has been found. - } - } - - this.checkOtherAnswers(lesson, pageData, result); - - result.userresponse = studentAnswer; - result.studentanswer = this.textUtils.s(studentAnswer); // Clean student answer as it goes to output. - } - - /** - * Check a truefalse answer. - * - * @param lesson Lesson. - * @param pageData Result of getPageData for the page to process. - * @param data Data containing the user answer. - * @param pageIndex Object containing all the pages indexed by ID. - * @param result Object where to store the result. - */ - protected checkAnswerTruefalse(lesson: any, pageData: any, data: any, pageIndex: any, result: AddonModLessonCheckAnswerResult) - : void { - - if (!data.answerid) { - result.noanswer = true; - - return; - } - - result.answerid = data.answerid; - - // Get the answer. - for (const i in pageData.answers) { - const answer = pageData.answers[i]; - if (answer.id == data.answerid) { - // Answer found. - result.correctanswer = this.isAnswerCorrect(lesson, pageData.page.id, answer, pageIndex); - result.newpageid = answer.jumpto; - result.response = answer.response; - result.studentanswer = result.userresponse = answer.answer; - break; - } - } - } - - /** - * Check the "other answers" value. - * - * @param lesson Lesson. - * @param pageData Result of getPageData for the page to process. - * @param result Object where to store the result. - */ - protected checkOtherAnswers(lesson: any, pageData: any, result: AddonModLessonCheckAnswerResult): void { - // We could check here to see if we have a wrong answer jump to use. - if (result.answerid == 0) { - // Use the all other answers jump details if it is set up. - const lastAnswer = pageData.answers[pageData.answers.length - 1] || {}; - - // Double check that this is the OTHER_ANSWERS answer. - if (typeof lastAnswer.answer == 'string' && - lastAnswer.answer.indexOf(AddonModLessonProvider.LESSON_OTHER_ANSWERS) != -1) { - result.newpageid = lastAnswer.jumpto; - result.response = lastAnswer.response; - - if (lesson.custom) { - result.correctanswer = lastAnswer.score > 0; - } - result.answerid = lastAnswer.id; - } - } - } - - /** - * Create a list of pages indexed by page ID based on a list of pages. - * - * @param pageList Result of get pages. - * @return Pages index. - */ - protected createPagesIndex(pageList: any[]): any { - // Index the pages by page ID. - const pages = {}; - - pageList.forEach((pageData) => { - pages[pageData.page.id] = pageData.page; - }); - - return pages; - } - - /** - * Finishes a retake. - * - * @param lesson Lesson. - * @param courseId Course ID the lesson belongs to. - * @param options Other options. - * @return Promise resolved in success, rejected otherwise. - */ - finishRetake(lesson: any, courseId: number, options: AddonModLessonFinishRetakeOptions = {}): Promise { - - if (options.offline) { - const retake = options.accessInfo.attemptscount; - const newOptions = { - cmId: lesson.coursemodule, - password: options.password, - review: options.review, - siteId: options.siteId, - }; - - return this.lessonOfflineProvider.finishRetake(lesson.id, courseId, retake, true, options.outOfTime, options.siteId) - .then(() => { - // Get the lesson grade. - return this.lessonGrade(lesson, retake, newOptions).catch(() => { - // Ignore errors. - return {}; - }); - }).then((gradeInfo: AddonModLessonGrade) => { - // Retake marked, now return the response. We won't return all the possible data. - // This code is based in Moodle's process_eol_page. - const result = { - data: {}, - messages: [], - warnings: [] - }, - promises = []; - let gradeLesson = true, - messageParams, - entryData; - - this.addResultValueEolPage(result, 'offline', true); // Mark the result as offline. - this.addResultValueEolPage(result, 'gradeinfo', gradeInfo); - - if (lesson.custom && !options.accessInfo.canmanage) { - /* Before we calculate the custom score make sure they answered the minimum number of questions. - We only need to do this for custom scoring as we can not get the miniumum score the user should achieve. - If we are not using custom scoring (so all questions are valued as 1) then we simply check if they - answered more than the minimum questions, if not, we mark it out of the number specified in the minimum - questions setting - which is done in lesson_grade(). */ - - // Get the number of answers given. - if (gradeInfo.nquestions < lesson.minquestions) { - gradeLesson = false; - messageParams = { - nquestions: gradeInfo.nquestions, - minquestions: lesson.minquestions - }; - this.addMessage(result.messages, 'addon.mod_lesson.numberofpagesviewednotice', {$a: messageParams}); - } - } - - if (!options.accessInfo.canmanage) { - if (gradeLesson) { - promises.push(this.calculateProgress(lesson.id, options.accessInfo, newOptions).then((progress) => { - this.addResultValueEolPage(result, 'progresscompleted', progress); - })); - - if (gradeInfo.attempts) { - // User has answered questions. - if (!lesson.custom) { - this.addResultValueEolPage(result, 'numberofpagesviewed', gradeInfo.nquestions, true); - if (lesson.minquestions) { - if (gradeInfo.nquestions < lesson.minquestions) { - this.addResultValueEolPage(result, 'youshouldview', lesson.minquestions, true); - } - } - this.addResultValueEolPage(result, 'numberofcorrectanswers', gradeInfo.earned, true); - } - - entryData = { - score: gradeInfo.earned, - grade: gradeInfo.total - }; - if (gradeInfo.nmanual) { - entryData.tempmaxgrade = gradeInfo.total - gradeInfo.manualpoints; - entryData.essayquestions = gradeInfo.nmanual; - this.addResultValueEolPage(result, 'displayscorewithessays', entryData, true); - } else { - this.addResultValueEolPage(result, 'displayscorewithoutessays', entryData, true); - } - - if (lesson.grade != CoreGradesProvider.TYPE_NONE) { - entryData = { - grade: this.textUtils.roundToDecimals(gradeInfo.grade * lesson.grade / 100, 1), - total: lesson.grade - }; - this.addResultValueEolPage(result, 'yourcurrentgradeisoutof', entryData, true); - } - - } else { - // User hasn't answered any question, only content pages. - if (lesson.timelimit) { - if (options.outOfTime) { - this.addResultValueEolPage(result, 'eolstudentoutoftimenoanswers', true, true); - } - } else { - this.addResultValueEolPage(result, 'welldone', true, true); - } - } - } - } else { - // Display for teacher. - if (lesson.grade != CoreGradesProvider.TYPE_NONE) { - this.addResultValueEolPage(result, 'displayofgrade', true, true); - } - } - - if (lesson.modattempts && options.accessInfo.canmanage) { - this.addResultValueEolPage(result, 'modattemptsnoteacher', true, true); - } - - if (gradeLesson) { - this.addResultValueEolPage(result, 'gradelesson', 1); - } - - return result; - }); - } - - return this.finishRetakeOnline(lesson.id, options).then((response) => { - this.eventsProvider.trigger(AddonModLessonProvider.DATA_SENT_EVENT, { - lessonId: lesson.id, - type: 'finish', - courseId: courseId, - outOfTime: options.outOfTime, - review: options.review, - }, this.sitesProvider.getCurrentSiteId()); - - return response; - }); - } - - /** - * Finishes a retake. It will fail if offline or cannot connect. - * - * @param lessonId Lesson ID. - * @param options Other options. - * @return Promise resolved in success, rejected otherwise. - */ - finishRetakeOnline(lessonId: number, options: AddonModLessonFinishRetakeOnlineOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params: any = { - lessonid: lessonId, - outoftime: options.outOfTime ? 1 : 0, - review: options.review ? 1 : 0, - }; - - if (typeof options.password == 'string') { - params.password = options.password; - } - - return site.write('mod_lesson_finish_attempt', params).then((response) => { - // Convert the data array into an object and decode the values. - const map = {}; - - response.data.forEach((entry) => { - if (entry.value && typeof entry.value == 'string' && entry.value !== '1') { - // It's a JSON encoded object. Try to decode it. - entry.value = this.textUtils.parseJSON(entry.value); - } - - map[entry.name] = entry; - }); - response.data = map; - - return response; - }); - }); - } - - /** - * Get the access information of a certain lesson. - * - * @param lessonId Lesson ID. - * @param options Other options. - * @return Promise resolved with the access information. - */ - getAccessInformation(lessonId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - lessonid: lessonId, - }; - const preSets = { - cacheKey: this.getAccessInformationCacheKey(lessonId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_lesson_get_lesson_access_information', params, preSets); - }); - } - - /** - * Get cache key for access information WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getAccessInformationCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'accessInfo:' + lessonId; - } - - /** - * Get content pages viewed in online and offline. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with an object with the online and offline viewed pages. - */ - getContentPagesViewed(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}) - : Promise<{online: any[], offline: any[]}> { - const promises = [], - type = AddonModLessonProvider.TYPE_STRUCTURE, - result = { - online: [], - offline: [] - }; - - // Get the online pages. - promises.push(this.getContentPagesViewedOnline(lessonId, retake, options).then((pages) => { - result.online = pages; - })); - - // Get the offline pages. - promises.push(this.lessonOfflineProvider.getRetakeAttemptsForType(lessonId, retake, type, options.siteId).catch(() => { - return []; - }).then((pages) => { - result.offline = pages; - })); - - return Promise.all(promises).then(() => { - return result; - }); - } - - /** - * Get cache key for get content pages viewed WS calls. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @return Cache key. - */ - protected getContentPagesViewedCacheKey(lessonId: number, retake: number): string { - return this.getContentPagesViewedCommonCacheKey(lessonId) + ':' + retake; - } - - /** - * Get common cache key for get content pages viewed WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getContentPagesViewedCommonCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'contentPagesViewed:' + lessonId; - } - - /** - * Get IDS of content pages viewed in online and offline. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with list of IDs. - */ - getContentPagesViewedIds(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.getContentPagesViewed(lessonId, retake, options).then((result) => { - const ids = {}, - pages = result.online.concat(result.offline); - - pages.forEach((page) => { - if (!ids[page.pageid]) { - ids[page.pageid] = true; - } - }); - - return Object.keys(ids).map((id) => { - return Number(id); - }); - }); - } - - /** - * Get the list of content pages viewed in the site for a certain retake. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with the viewed pages. - */ - getContentPagesViewedOnline(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - lessonid: lessonId, - lessonattempt: retake, - }; - const preSets = { - cacheKey: this.getContentPagesViewedCacheKey(lessonId, retake), - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_lesson_get_content_pages_viewed', params, preSets).then((result) => { - return result.pages; - }); - }); - } - - /** - * Get the last content page viewed. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with the last content page viewed. - */ - getLastContentPageViewed(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.getContentPagesViewed(lessonId, retake, options).then((data) => { - let lastPage, - maxTime = 0; - - data.online.forEach((page) => { - if (page.timeseen > maxTime) { - lastPage = page; - maxTime = page.timeseen; - } - }); - - data.offline.forEach((page) => { - if (page.timemodified > maxTime) { - lastPage = page; - maxTime = page.timemodified; - } - }); - - return lastPage; - }).catch(() => { - // Error getting last page, don't return anything. - }); - } - - /** - * Get the last page seen. - * Based on Moodle's get_last_page_seen. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with the last page seen. - */ - getLastPageSeen(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - let lastPageSeen: number; - - // Get the last question answered. - return this.lessonOfflineProvider.getLastQuestionPageAttempt(lessonId, retake, options.siteId).then((answer) => { - if (answer) { - lastPageSeen = answer.newpageid; - } - - // Now get the last content page viewed. - return this.getLastContentPageViewed(lessonId, retake, options).then((page) => { - if (page) { - if (answer) { - if (page.timemodified > answer.timemodified) { - // This content page was viewed more recently than the question page. - lastPageSeen = page.newpageid || page.pageid; - } - } else { - // Has not answered any questions but has viewed a content page. - lastPageSeen = page.newpageid || page.pageid; - } - } - - return lastPageSeen; - }); - }); - } - - /** - * Get a Lesson by module ID. - * - * @param courseId Course ID. - * @param cmid Course module ID. - * @param options Other options. - * @return Promise resolved when the lesson is retrieved. - */ - getLesson(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getLessonByField(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a Lesson with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the lesson is retrieved. - */ - protected getLessonByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getLessonDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModLessonProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_lesson_get_lessons_by_courses', params, preSets).then((response) => { - if (response && response.lessons) { - const currentLesson = response.lessons.find((lesson) => { - return lesson[key] == value; - }); - - if (currentLesson) { - return currentLesson; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a Lesson by lesson ID. - * - * @param courseId Course ID. - * @param id Lesson ID. - * @param options Other options. - * @return Promise resolved when the lesson is retrieved. - */ - getLessonById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getLessonByField(courseId, 'id', id, options); - } - - /** - * Get cache key for Lesson data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getLessonDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'lesson:' + courseId; - } - - /** - * Get a lesson protected with password. - * - * @param lessonId Lesson ID. - * @param options Other options. - * @return Promise resolved with the lesson. - */ - getLessonWithPassword(lessonId: number, options: AddonModLessonGetWithPasswordOptions = {}): Promise { - const validatePassword = typeof options.validatePassword == 'undefined' ? true : options.validatePassword; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params: any = { - lessonid: lessonId, - }; - const preSets = { - cacheKey: this.getLessonWithPasswordCacheKey(lessonId), - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - if (typeof options.password == 'string') { - params.password = options.password; - } - - return site.read('mod_lesson_get_lesson', params, preSets).then((response) => { - if (typeof response.lesson.ongoing == 'undefined') { - // Basic data not received, password is wrong. Remove stored password. - this.removeStoredPassword(lessonId, site.id); - - if (validatePassword) { - // Invalidate the data and reject. - return this.invalidateLessonWithPassword(lessonId, site.id).catch(() => { - // Shouldn't happen. - }).then(() => { - return Promise.reject(this.translate.instant('addon.mod_lesson.loginfail')); - }); - } - } - - return response.lesson; - }); - }); - } - - /** - * Get cache key for get lesson with password WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getLessonWithPasswordCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'lessonWithPswrd:' + lessonId; - } - - /** - * Given a page ID, a jumpto and all the possible jumps, calcualate the new page ID. - * - * @param pageId Current page ID. - * @param jumpTo The jumpto. - * @param jumps Result of get pages possible jumps. - * @return New page ID. - */ - protected getNewPageId(pageId: number, jumpTo: number, jumps: any): number { - // If jump not found, return current jumpTo. - if (jumps && jumps[pageId] && jumps[pageId][jumpTo]) { - return jumps[pageId][jumpTo].calculatedjump; - } else if (!jumpTo) { - // Return current page. - return pageId; - } - - return jumpTo; - } - - /** - * Get the ongoing score message for the user (depending on the user permission and lesson settings). - * - * @param lesson Lesson. - * @param accessInfo Result of get access info. - * @param options Other options. - * @return Promise resolved with the ongoing score message. - */ - getOngoingScoreMessage(lesson: any, accessInfo: any, options: AddonModLessonGradeOptions = {}): Promise { - - if (accessInfo.canmanage) { - return Promise.resolve(this.translate.instant('addon.mod_lesson.teacherongoingwarning')); - } else { - let retake = accessInfo.attemptscount; - if (options.review) { - retake--; - } - - return this.lessonGrade(lesson, retake, options).then((gradeInfo) => { - const data: any = {}; - - if (lesson.custom) { - data.score = gradeInfo.earned; - data.currenthigh = gradeInfo.total; - - return this.translate.instant('addon.mod_lesson.ongoingcustom', {$a: data}); - } else { - data.correct = gradeInfo.earned; - data.viewed = gradeInfo.attempts; - - return this.translate.instant('addon.mod_lesson.ongoingnormal', {$a: data}); - } - }); - } - } - - /** - * Get the possible answers from a page. - * - * @param lesson Lesson. - * @param pageId Page ID. - * @param options Other options. - * @return Promise resolved with the list of possible answers. - */ - protected getPageAnswers(lesson: any, pageId: number, options: AddonModLessonPwdReviewOptions = {}): Promise { - return this.getPageData(lesson, pageId, { - includeContents: true, - ...options, // Include all options. - readingStrategy: options.readingStrategy || CoreSitesReadingStrategy.PreferCache, - }).then((data) => { - return data.answers; - }); - } - - /** - * Get all the possible answers from a list of pages, indexed by answerId. - * - * @param lesson Lesson. - * @param pageIds List of page IDs. - * @param options Other options. - * @return Promise resolved with an object containing the answers. - */ - protected getPagesAnswers(lesson: any, pageIds: number[], options: AddonModLessonPwdReviewOptions = {}): Promise { - - const answers = {}, - promises = []; - - pageIds.forEach((pageId) => { - promises.push(this.getPageAnswers(lesson, pageId, options).then((pageAnswers) => { - pageAnswers.forEach((answer) => { - // Include the pageid in each answer and add them to the final list. - answer.pageid = pageId; - answers[answer.id] = answer; - }); - })); - }); - - return Promise.all(promises).then(() => { - return answers; - }); - } - - /** - * Get page data. - * - * @param lesson Lesson. - * @param pageId Page ID. - * @param options Other options. - * @return Promise resolved with the page data. - */ - getPageData(lesson: any, pageId: number, options: AddonModLessonGetPageDataOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params: any = { - lessonid: lesson.id, - pageid: Number(pageId), - review: options.review ? 1 : 0, - returncontents: options.includeContents ? 1 : 0, - }; - const preSets = { - cacheKey: this.getPageDataCacheKey(lesson.id, pageId), - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - if (typeof options.password == 'string') { - params.password = options.password; - } - - if (options.review) { - // Force online mode in review. - preSets.getFromCache = false; - preSets.saveToCache = false; - preSets.emergencyCache = false; - } - - return site.read('mod_lesson_get_page_data', params, preSets).then((data) => { - if (preSets.omitExpires && options.accessInfo && data.page) { - // Offline mode and valid page. Calculate the data that might be affected. - return this.calculateOfflineData(lesson, options).then((calcData) => { - Object.assign(data, calcData); - - return this.getPageViewMessages(lesson, options.accessInfo, data.page, options.jumps, { - password: options.password, - siteId: options.siteId, - }); - }).then((messages) => { - data.messages = messages; - - return data; - }); - } - - return data; - }); - }); - } - - /** - * Get cache key for get page data WS calls. - * - * @param lessonId Lesson ID. - * @param pageId Page ID. - * @return Cache key. - */ - protected getPageDataCacheKey(lessonId: number, pageId: number): string { - return this.getPageDataCommonCacheKey(lessonId) + ':' + pageId; - } - - /** - * Get common cache key for get page data WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getPageDataCommonCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'pageData:' + lessonId; - } - - /** - * Get lesson pages. - * - * @param lessonId Lesson ID. - * @param options Other options. - * @return Promise resolved with the pages. - */ - getPages(lessonId: number, options: AddonModLessonPwdReviewOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params: any = { - lessonid: lessonId, - }; - const preSets = { - cacheKey: this.getPagesCacheKey(lessonId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - if (typeof options.password == 'string') { - params.password = options.password; - } - - return site.read('mod_lesson_get_pages', params, preSets).then((response) => { - return response.pages; - }); - }); - } - - /** - * Get cache key for get pages WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getPagesCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'pages:' + lessonId; - } - - /** - * Get possible jumps for a lesson. - * - * @param lessonId Lesson ID. - * @param options Other options. - * @return Promise resolved with the jumps. - */ - getPagesPossibleJumps(lessonId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - lessonid: lessonId, - }; - const preSets = { - cacheKey: this.getPagesPossibleJumpsCacheKey(lessonId), - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_lesson_get_pages_possible_jumps', params, preSets).then((response) => { - // Index the jumps by page and jumpto. - if (response.jumps) { - const jumps = {}; - - response.jumps.forEach((jump) => { - if (typeof jumps[jump.pageid] == 'undefined') { - jumps[jump.pageid] = {}; - } - jumps[jump.pageid][jump.jumpto] = jump; - }); - - return jumps; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get pages possible jumps WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getPagesPossibleJumpsCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'pagesJumps:' + lessonId; - } - - /** - * Get different informative messages when processing a lesson page. - * Please try to use WS response messages instead of this function if possible. - * Based on Moodle's add_messages_on_page_process. - * - * @param lesson Lesson. - * @param accessInfo Result of get access info. - * @param result Result of process page. - * @param review If the user wants to review just after finishing (1 hour margin). - * @param jumps Result of get pages possible jumps. - * @return Array with the messages. - */ - getPageProcessMessages(lesson: any, accessInfo: any, result: any, review: boolean, jumps: any): any[] { - const messages = []; - - if (accessInfo.canmanage) { - // Warning for teachers to inform them that cluster and unseen does not work while logged in as a teacher. - if (this.lessonDisplayTeacherWarning(jumps)) { - this.addMessage(messages, 'addon.mod_lesson.teacherjumpwarning', {$a: { - cluster: this.translate.instant('addon.mod_lesson.clusterjump'), - unseen: this.translate.instant('addon.mod_lesson.unseenpageinbranch') - }}); - } - - // Inform teacher that s/he will not see the timer. - if (lesson.timelimit) { - this.addMessage(messages, 'addon.mod_lesson.teachertimerwarning'); - } - } - // Report attempts remaining. - if (result.attemptsremaining > 0 && lesson.review && !review) { - this.addMessage(messages, 'addon.mod_lesson.attemptsremaining', {$a: result.attemptsremaining}); - } - - return messages; - } - - /** - * Get the IDs of all the pages that have at least 1 question attempt. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with the IDs. - */ - getPagesIdsWithQuestionAttempts(lessonId: number, retake: number, options: AddonModLessonGetPagesIdsWithAttemptsOptions = {}) - : Promise { - - return this.getQuestionsAttempts(lessonId, retake, options).then((result) => { - const ids = {}, - attempts = result.online.concat(result.offline); - - attempts.forEach((attempt) => { - if (!ids[attempt.pageid]) { - ids[attempt.pageid] = true; - } - }); - - return Object.keys(ids).map((id) => { - return Number(id); - }); - }); - } - - /** - * Get different informative messages when viewing a lesson page. - * Please try to use WS response messages instead of this function if possible. - * Based on Moodle's add_messages_on_page_view. - * - * @param lesson Lesson. - * @param accessInfo Result of get access info. Required if offline is true. - * @param page Page loaded. - * @param jumps Result of get pages possible jumps. - * @param options Other options. - * @return Promise resolved with the list of messages. - */ - getPageViewMessages(lesson: any, accessInfo: any, page: any, jumps: any, options: AddonModLessonGetPageViewMessagesOptions = {}) - : Promise { - - const messages = []; - let promise = Promise.resolve(); - - if (!accessInfo.canmanage) { - if (page.qtype == AddonModLessonProvider.LESSON_PAGE_BRANCHTABLE && lesson.minquestions) { - // Tell student how many questions they have seen, how many are required and their grade. - const retake = accessInfo.attemptscount; - - promise = this.lessonGrade(lesson, retake, options).then((gradeInfo) => { - if (gradeInfo.attempts) { - if (gradeInfo.nquestions < lesson.minquestions) { - this.addMessage(messages, 'addon.mod_lesson.numberofpagesviewednotice', {$a: { - nquestions: gradeInfo.nquestions, - minquestions: lesson.minquestions - }}); - } - - if (!options.review && !lesson.retake) { - this.addMessage(messages, 'addon.mod_lesson.numberofcorrectanswers', {$a: gradeInfo.earned}); - - if (lesson.grade != CoreGradesProvider.TYPE_NONE) { - this.addMessage(messages, 'addon.mod_lesson.yourcurrentgradeisoutof', {$a: { - grade: this.textUtils.roundToDecimals(gradeInfo.grade * lesson.grade / 100, 1), - total: lesson.grade - }}); - } - } - } - }).catch(() => { - // Ignore errors. - }); - } - } else { - if (lesson.timelimit) { - this.addMessage(messages, 'addon.mod_lesson.teachertimerwarning'); - } - - if (this.lessonDisplayTeacherWarning(jumps)) { - // Warning for teachers to inform them that cluster and unseen does not work while logged in as a teacher. - this.addMessage(messages, 'addon.mod_lesson.teacherjumpwarning', {$a: { - cluster: this.translate.instant('addon.mod_lesson.clusterjump'), - unseen: this.translate.instant('addon.mod_lesson.unseenpageinbranch') - }}); - } - } - - return promise.then(() => { - return messages; - }); - } - - /** - * Get questions attempts, including offline attempts. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with the questions attempts. - */ - getQuestionsAttempts(lessonId: number, retake: number, options: AddonModLessonGetQuestionsAttemptsOptions = {}) - : Promise<{online: any[], offline: any[]}> { - - const promises = [], - result = { - online: [], - offline: [] - }; - - promises.push(this.getQuestionsAttemptsOnline(lessonId, retake, options).then((attempts) => { - result.online = attempts; - })); - - promises.push(this.lessonOfflineProvider.getQuestionsAttempts(lessonId, retake, options.correct, options.pageId, - options.siteId).catch(() => { - // Error, assume no attempts. - return []; - }).then((attempts) => { - result.offline = attempts; - })); - - return Promise.all(promises).then(() => { - return result; - }); - } - - /** - * Get cache key for get questions attempts WS calls. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param userId User ID. - * @return Cache key. - */ - protected getQuestionsAttemptsCacheKey(lessonId: number, retake: number, userId: number): string { - return this.getQuestionsAttemptsCommonCacheKey(lessonId) + ':' + userId + ':' + retake; - } - - /** - * Get common cache key for get questions attempts WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getQuestionsAttemptsCommonCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'questionsAttempts:' + lessonId; - } - - /** - * Get questions attempts from the site. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with the questions attempts. - */ - getQuestionsAttemptsOnline(lessonId: number, retake: number, options: AddonModLessonGetQuestionsAttemptsOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = options.userId || site.getUserId(); - - // Don't pass "pageId" and "correct" params, they will be filtered locally. - const params = { - lessonid: lessonId, - attempt: retake, - userid: userId, - }; - const preSets = { - cacheKey: this.getQuestionsAttemptsCacheKey(lessonId, retake, userId), - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_lesson_get_questions_attempts', params, preSets).then((response) => { - if (options.pageId || options.correct) { - // Filter the attempts. - return response.attempts.filter((attempt) => { - if (options.correct && !attempt.correct) { - return false; - } - - if (options.pageId && attempt.pageid != options.pageId) { - return false; - } - - return true; - }); - } - - return response.attempts; - }); - }); - } - - /** - * Get the overview of retakes in a lesson (named "attempts overview" in Moodle). - * - * @param lessonId Lesson ID. - * @param options Other options. - * @return Promise resolved with the retakes overview. - */ - getRetakesOverview(lessonId: number, options: AddonModLessonGroupOptions = {}): Promise { - - const groupId = options.groupId || 0; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - lessonid: lessonId, - groupid: groupId, - }; - const preSets = { - cacheKey: this.getRetakesOverviewCacheKey(lessonId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_lesson_get_attempts_overview', params, preSets).then((response) => { - return response.data; - }); - }); - } - - /** - * Get cache key for get retakes overview WS calls. - * - * @param lessonId Lesson ID. - * @param groupId Group ID. - * @return Cache key. - */ - protected getRetakesOverviewCacheKey(lessonId: number, groupId: number): string { - return this.getRetakesOverviewCommonCacheKey(lessonId) + ':' + groupId; - } - - /** - * Get common cache key for get retakes overview WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getRetakesOverviewCommonCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'retakesOverview:' + lessonId; - } - - /** - * Get a password stored in DB. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with password on success, rejected otherwise. - */ - getStoredPassword(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecord(AddonModLessonProvider.PASSWORD_TABLE, {lessonid: lessonId}).then((entry) => { - return entry.password; - }); - }); - } - - /** - * Finds all pages that appear to be a subtype of the provided pageId until an end point specified within "ends" is - * encountered or no more pages exist. - * Based on Moodle's get_sub_pages_of. - * - * @param pages Index of lesson pages, indexed by page ID. See createPagesIndex. - * @param pageId Page ID to get subpages of. - * @param end An array of LESSON_PAGE_* types that signify an end of the subtype. - * @return List of subpages. - */ - getSubpagesOf(pages: any, pageId: number, ends: number[]): any[] { - const subPages = []; - - pageId = pages[pageId].nextpageid; // Move to the first page after the given page. - ends = ends || []; - - while (true) { - if (!pageId || ends.indexOf(pages[pageId].qtype) != -1) { - // No more pages or it reached a page of the searched types. Stop. - break; - } - - subPages.push(pages[pageId]); - pageId = pages[pageId].nextpageid; - } - - return subPages; - } - - /** - * Get lesson timers. - * - * @param lessonId Lesson ID. - * @param options Other options. - * @return Promise resolved with the pages. - */ - getTimers(lessonId: number, options: AddonModLessonUserOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = options.userId || site.getUserId(); - - const params = { - lessonid: lessonId, - userid: userId, - }; - const preSets = { - cacheKey: this.getTimersCacheKey(lessonId, userId), - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_lesson_get_user_timers', params, preSets).then((response) => { - return response.timers; - }); - }); - } - - /** - * Get cache key for get timers WS calls. - * - * @param lessonId Lesson ID. - * @param userId User ID. - * @return Cache key. - */ - protected getTimersCacheKey(lessonId: number, userId: number): string { - return this.getTimersCommonCacheKey(lessonId) + ':' + userId; - } - - /** - * Get common cache key for get timers WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getTimersCommonCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'timers:' + lessonId; - } - - /** - * Get the list of used answers (with valid answer) in a multichoice question page. - * - * @param pageData Result of getPageData for the page to process. - * @return List of used answers. - */ - protected getUsedAnswersMultichoice(pageData: any): any[] { - const answers = this.utils.clone(pageData.answers); - - return answers.filter((entry) => { - return entry.answer !== ''; - }); - } - - /** - * Get the user's response in a matching question page. - * - * @param data Data containing the user answer. - * @return User response. - */ - protected getUserResponseMatching(data: any): any { - if (data.response) { - // The data is already stored as expected. Return it. - return data.response; - } - - // Data is stored in properties like 'response[379]'. Recreate the response object. - const response = {}; - - for (const key in data) { - const match = key.match(/^response\[(\d+)\]/); - - if (match && match.length > 1) { - response[match[1]] = data[key]; - } - } - - return response; - } - - /** - * Get the user's response in a multichoice page if multiple answers are allowed. - * - * @param data Data containing the user answer. - * @return User response. - */ - protected getUserResponseMultichoice(data: any): any[] { - if (data.answer) { - // The data is already stored as expected. If it's valid, parse the values to int. - if (Array.isArray(data.answer)) { - return data.answer.map((value) => { - return parseInt(value, 10); - }); - } - - return data.answer; - } - - // Data is stored in properties like 'answer[379]'. Recreate the answer array. - const answer = []; - for (const key in data) { - const match = key.match(/^answer\[(\d+)\]/); - if (match && match.length > 1) { - answer.push(parseInt(match[1], 10)); - } - } - - return answer; - } - - /** - * Get a user's retake. - * - * @param lessonId Lesson ID. - * @param retake Retake number - * @param options Other options. - * @return Promise resolved with the retake data. - */ - getUserRetake(lessonId: number, retake: number, options: AddonModLessonUserOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = options.userId || site.getUserId(); - - const params = { - lessonid: lessonId, - userid: userId, - lessonattempt: retake, - }; - const preSets = { - cacheKey: this.getUserRetakeCacheKey(lessonId, userId, retake), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModLessonProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_lesson_get_user_attempt', params, preSets); - }); - } - - /** - * Get cache key for get user retake WS calls. - * - * @param lessonId Lesson ID. - * @param userId User ID. - * @param retake Retake number - * @return Cache key. - */ - protected getUserRetakeCacheKey(lessonId: number, userId: number, retake: number): string { - return this.getUserRetakeUserCacheKey(lessonId, userId) + ':' + retake; - } - - /** - * Get user cache key for get user retake WS calls. - * - * @param lessonId Lesson ID. - * @param userId User ID. - * @return Cache key. - */ - protected getUserRetakeUserCacheKey(lessonId: number, userId: number): string { - return this.getUserRetakeLessonCacheKey(lessonId) + ':' + userId; - } - - /** - * Get lesson cache key for get user retake WS calls. - * - * @param lessonId Lesson ID. - * @return Cache key. - */ - protected getUserRetakeLessonCacheKey(lessonId: number): string { - return this.ROOT_CACHE_KEY + 'userRetake:' + lessonId; - } - - /** - * Get the prevent access reason to display for a certain lesson. - * - * @param info Lesson access info. - * @param ignorePassword Whether password protected reason should be ignored (user already entered the password). - * @param isReview Whether user is reviewing a retake. - * @return Prevent access reason. - */ - getPreventAccessReason(info: any, ignorePassword?: boolean, isReview?: boolean): any { - let result; - - if (info && info.preventaccessreasons) { - for (let i = 0; i < info.preventaccessreasons.length; i++) { - const entry = info.preventaccessreasons[i]; - - if (entry.reason == 'lessonopen' || entry.reason == 'lessonclosed') { - // Time restrictions are the most prioritary, return it. - return entry; - } else if (entry.reason == 'passwordprotectedlesson') { - if (!ignorePassword) { - // Treat password before all other reasons. - result = entry; - } - } else if (entry.reason == 'noretake' && isReview) { - // Ignore noretake error when reviewing. - } else if (!result) { - // Rest of cases, just return any of them. - result = entry; - } - } - } - - return result; - } - - /** - * Check if a jump is correct. - * Based in Moodle's jumpto_is_correct. - * - * @param pageId ID of the page from which you are jumping from. - * @param jumpTo The jumpto number. - * @param pageIndex Object containing all the pages indexed by ID. See createPagesIndex. - * @return Whether jump is correct. - */ - jumptoIsCorrect(pageId: number, jumpTo: number, pageIndex: any): boolean { - // First test the special values. - if (!jumpTo) { - // Same page - return false; - } else if (jumpTo == AddonModLessonProvider.LESSON_NEXTPAGE) { - return true; - } else if (jumpTo == AddonModLessonProvider.LESSON_UNSEENBRANCHPAGE) { - return true; - } else if (jumpTo == AddonModLessonProvider.LESSON_RANDOMPAGE) { - return true; - } else if (jumpTo == AddonModLessonProvider.LESSON_CLUSTERJUMP) { - return true; - } else if (jumpTo == AddonModLessonProvider.LESSON_EOL) { - return true; - } - - let aPageId = pageIndex[pageId].nextpageid; - while (aPageId) { - if (jumpTo == aPageId) { - return true; - } - - aPageId = pageIndex[aPageId].nextpageid; - } - - return false; - } - - /** - * Invalidates Lesson data. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAccessInformation(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(lessonId)); - }); - } - - /** - * Invalidates content pages viewed for all retakes. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContentPagesViewed(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getContentPagesViewedCommonCacheKey(lessonId)); - }); - } - - /** - * Invalidates content pages viewed for a certain retake. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContentPagesViewedForRetake(lessonId: number, retake: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getContentPagesViewedCacheKey(lessonId, retake)); - }); - } - - /** - * Invalidates Lesson data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateLessonData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getLessonDataCacheKey(courseId)); - }); - } - - /** - * Invalidates lesson with password. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateLessonWithPassword(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getLessonWithPasswordCacheKey(lessonId)); - }); - } - - /** - * Invalidates page data for all pages. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidatePageData(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getPageDataCommonCacheKey(lessonId)); - }); - } - - /** - * Invalidates page data for a certain page. - * - * @param lessonId Lesson ID. - * @param pageId Page ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidatePageDataForPage(lessonId: number, pageId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getPageDataCacheKey(lessonId, pageId)); - }); - } - - /** - * Invalidates pages. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidatePages(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getPagesCacheKey(lessonId)); - }); - } - - /** - * Invalidates pages possible jumps. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidatePagesPossibleJumps(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getPagesPossibleJumpsCacheKey(lessonId)); - }); - } - - /** - * Invalidates questions attempts for all retakes. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateQuestionsAttempts(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getQuestionsAttemptsCommonCacheKey(lessonId)); - }); - } - - /** - * Invalidates question attempts for a certain retake and user. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param siteId Site ID. If not defined, current site.. - * @param userId User ID. If not defined, site's user. - * @return Promise resolved when the data is invalidated. - */ - invalidateQuestionsAttemptsForRetake(lessonId: number, retake: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getQuestionsAttemptsCacheKey(lessonId, retake, userId)); - }); - } - - /** - * Invalidates retakes overview for all groups in a lesson. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateRetakesOverview(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getRetakesOverviewCommonCacheKey(lessonId)); - }); - } - - /** - * Invalidates retakes overview for a certain group in a lesson. - * - * @param lessonId Lesson ID. - * @param groupId Group ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateRetakesOverviewForGroup(lessonId: number, groupId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getRetakesOverviewCacheKey(lessonId, groupId)); - }); - } - - /** - * Invalidates timers for all users in a lesson. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateTimers(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getTimersCommonCacheKey(lessonId)); - }); - } - - /** - * Invalidates timers for a certain user. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateTimersForUser(lessonId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getTimersCacheKey(lessonId, userId)); - }); - } - - /** - * Invalidates a certain retake for a certain user. - * - * @param lessonId Lesson ID. - * @param retake Retake number. - * @param userId User ID. Undefined for current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateUserRetake(lessonId: number, retake: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getUserRetakeCacheKey(lessonId, userId, retake)); - }); - } - - /** - * Invalidates all retakes for all users in a lesson. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateUserRetakesForLesson(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getUserRetakeLessonCacheKey(lessonId)); - }); - } - - /** - * Invalidates all retakes for a certain user in a lesson. - * - * @param lessonId Lesson ID. - * @param userId User ID. Undefined for current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateUserRetakesForUser(lessonId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKeyStartingWith(this.getUserRetakeUserCacheKey(lessonId, userId)); - }); - } - - /** - * Check if a page answer is correct. - * - * @param lesson Lesson. - * @param pageId The page ID. - * @param answer The answer to check. - * @param pageIndex Object containing all the pages indexed by ID. - * @return Whether the answer is correct. - */ - protected isAnswerCorrect(lesson: any, pageId: number, answer: any, pageIndex: any): boolean { - if (lesson.custom) { - // Custom scores. If score on answer is positive, it is correct. - return answer.score > 0; - } else { - return this.jumptoIsCorrect(pageId, answer.jumpto, pageIndex); - } - } - - /** - * Check if a lesson is enabled to be used in offline. - * - * @param lesson Lesson. - * @return Whether offline is enabled. - */ - isLessonOffline(lesson: any): boolean { - return !!lesson.allowofflineattempts; - } - - /** - * Check if a lesson is password protected based in the access info. - * - * @param info Lesson access info. - * @return Whether the lesson is password protected. - */ - isPasswordProtected(info: any): boolean { - if (info && info.preventaccessreasons) { - for (let i = 0; i < info.preventaccessreasons.length; i++) { - const entry = info.preventaccessreasons[i]; - - if (entry.reason == 'passwordprotectedlesson') { - return true; - } - } - } - - return false; - } - - /** - * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the lesson WS are available. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - // All WS were introduced at the same time so checking one is enough. - return site.wsAvailable('mod_lesson_get_lesson_access_information'); - }); - } - - /** - * Check if a page is a question page or a content page. - * - * @param type Type of the page. - * @return True if question page, false if content page. - */ - isQuestionPage(type: number): boolean { - return type == AddonModLessonProvider.TYPE_QUESTION; - } - - /** - * Start or continue a retake. - * - * @param id Lesson ID. - * @param password Lesson password (if any). - * @param pageId Page id to continue from (only when continuing a retake). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - launchRetake(id: number, password?: string, pageId?: number, review?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params: any = { - lessonid: id, - review: review ? 1 : 0 - }; - - if (typeof password == 'string') { - params.password = password; - } - if (typeof pageId == 'number') { - params.pageid = pageId; - } - - return site.write('mod_lesson_launch_attempt', params).then((response) => { - this.eventsProvider.trigger(AddonModLessonProvider.DATA_SENT_EVENT, { - lessonId: id, - type: 'launch' - }, this.sitesProvider.getCurrentSiteId()); - - return response; - }); - }); - } - - /** - * Check if the user left during a timed session. - * - * @param info Lesson access info. - * @return True if left during timed, false otherwise. - */ - leftDuringTimed(info: any): boolean { - return info && info.lastpageseen && info.lastpageseen != AddonModLessonProvider.LESSON_EOL && info.leftduringtimedsession; - } - - /** - * Checks to see if a LESSON_CLUSTERJUMP or a LESSON_UNSEENBRANCHPAGE is used in a lesson. - * Based on Moodle's lesson_display_teacher_warning. - * - * @param jumps Result of get pages possible jumps. - * @return Whether the lesson uses one of those jumps. - */ - lessonDisplayTeacherWarning(jumps: any): boolean { - if (!jumps) { - return false; - } - - // Check if any jump is to cluster or unseen content page. - for (const pageId in jumps) { - for (const jumpto in jumps[pageId]) { - const jumptoNum = Number(jumpto); - - if (jumptoNum == AddonModLessonProvider.LESSON_CLUSTERJUMP || - jumptoNum == AddonModLessonProvider.LESSON_UNSEENBRANCHPAGE) { - return true; - } - } - } - - return false; - } - - /** - * Calculates a user's grade for a lesson. - * Based on Moodle's lesson_grade. - * - * @param lesson Lesson. - * @param retake Retake number. - * @param options Other options. - * @return Promise resolved with the grade data. - */ - lessonGrade(lesson: any, retake: number, options: AddonModLessonGradeOptions = {}): Promise { - - // Initialize all variables. - let nViewed = 0, - nManual = 0, - manualPoints = 0, - theGrade = 0, - nQuestions = 0, - total = 0, - earned = 0; - - // Get the questions attempts for the user. - return this.getQuestionsAttempts(lesson.id, retake, { - cmId: lesson.coursemodule, - siteId: options.siteId, - userId: options.userId, - }).then((attemptsData) => { - const attempts = attemptsData.online.concat(attemptsData.offline); - - if (!attempts.length) { - // No attempts. - return; - } - - const attemptSet = {}; - let promise; - - // Create the pageIndex if it isn't provided. - if (!options.pageIndex) { - promise = this.getPages(lesson.id, { - password: options.password, - cmId: lesson.coursemodule, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId: options.siteId, - }).then((pages) => { - options.pageIndex = this.createPagesIndex(pages); - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - const pageIds = []; - - // Group each try with its page. - attempts.forEach((attempt) => { - if (!attemptSet[attempt.pageid]) { - attemptSet[attempt.pageid] = []; - pageIds.push(attempt.pageid); - } - attemptSet[attempt.pageid].push(attempt); - }); - - // Drop all attempts that go beyond max attempts for the lesson. - for (const pageId in attemptSet) { - // Sort the list by time in ascending order. - const attempts = attemptSet[pageId].sort((a, b) => { - return (a.timeseen || a.timemodified) - (b.timeseen || b.timemodified); - }); - - attemptSet[pageId] = attempts.slice(0, lesson.maxattempts); - } - - // Get all the answers from the pages the user answered. - return this.getPagesAnswers(lesson, pageIds, options); - }).then((answers) => { - // Number of pages answered. - nQuestions = Object.keys(attemptSet).length; - - for (const pageId in attemptSet) { - const attempts = attemptSet[pageId], - lastAttempt = attempts[attempts.length - 1]; - - if (lesson.custom) { - // If essay question, handle it, otherwise add to score. - if (options.pageIndex[lastAttempt.pageid].qtype == AddonModLessonProvider.LESSON_PAGE_ESSAY) { - if (lastAttempt.useranswer && typeof lastAttempt.useranswer.score != 'undefined') { - earned += lastAttempt.useranswer.score; - } - nManual++; - manualPoints += answers[lastAttempt.answerid].score; - } else if (lastAttempt.answerid) { - earned += answers[lastAttempt.answerid].score; - } - } else { - attempts.forEach((attempt) => { - earned += attempt.correct ? 1 : 0; - }); - - // If essay question, increase numbers. - if (options.pageIndex[lastAttempt.pageid].qtype == AddonModLessonProvider.LESSON_PAGE_ESSAY) { - nManual++; - manualPoints++; - } - } - - // Number of times answered. - nViewed += attempts.length; - } - - if (lesson.custom) { - const bestScores = {}; - - // Find the highest possible score per page to get our total. - for (const answerId in answers) { - const answer = answers[answerId]; - - if (typeof bestScores[answer.pageid] == 'undefined') { - bestScores[answer.pageid] = answer.score; - } else if (bestScores[answer.pageid] < answer.score) { - bestScores[answer.pageid] = answer.score; - } - } - - // Sum all the scores. - for (const pageId in bestScores) { - total += bestScores[pageId]; - } - } else { - // Check to make sure the student has answered the minimum questions. - if (lesson.minquestions && nQuestions < lesson.minquestions) { - // Nope, increase number viewed by the amount of unanswered questions. - total = nViewed + (lesson.minquestions - nQuestions); - } else { - total = nViewed; - } - } - }); - }).then(() => { - if (total) { // Not zero. - theGrade = this.textUtils.roundToDecimals(earned * 100 / total, 5); - } - - return { - nquestions: nQuestions, - attempts: nViewed, - total: total, - earned: earned, - grade: theGrade, - nmanual: nManual, - manualpoints: manualPoints - }; - }); - } - - /** - * Report a lesson as being viewed. - * - * @param id Module ID. - * @param password Lesson password (if any). - * @param name Name of the assign. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logViewLesson(id: number, password?: string, name?: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params: any = { - lessonid: id - }; - - if (typeof password == 'string') { - params.password = password; - } - - return this.logHelper.logSingle('mod_lesson_view_lesson', params, AddonModLessonProvider.COMPONENT, id, name, - 'lesson', {}, siteId); - }); - - } - - /** - * Process a lesson page, saving its data. - * - * @param lesson Lesson. - * @param courseId Course ID the lesson belongs to. - * @param pageData Result of getPageData for the page to process. - * @param data Data to save. - * @param options Other options. - * @return Promise resolved when done. - */ - processPage(lesson: any, courseId: number, pageData: any, data: any, options: AddonModLessonProcessPageOptions = {}) - : Promise { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - const page = pageData.page, - pageId = page.id; - let result, - pageIndex; - - if (options.offline) { - // Get the list of pages of the lesson. - return this.getPages(lesson.id, { - cmId: lesson.coursemodule, - password: options.password, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId: options.siteId, - }).then((pages) => { - pageIndex = this.createPagesIndex(pages); - - if (pageData.answers.length) { - return this.recordAttempt(lesson, courseId, pageData, data, options.review, options.accessInfo, options.jumps, - pageIndex, options.siteId); - } else { - // The page has no answers so we will just progress to the next page (as set by newpageid). - return { - nodefaultresponse: true, - newpageid: data.newpageid - }; - } - }).then((res) => { - result = res; - result.newpageid = this.getNewPageId(pageData.page.id, result.newpageid, options.jumps); - - // Calculate some needed offline data. - return this.calculateOfflineData(lesson, { - accessInfo: options.accessInfo, - password: options.password, - review: options.review, - pageIndex, - siteId: options.siteId, - }); - }).then((calculatedData) => { - // Add some default data to match the WS response. - result.warnings = []; - result.displaymenu = pageData.displaymenu; // Keep the same value since we can't calculate it in offline. - result.messages = this.getPageProcessMessages(lesson, options.accessInfo, result, options.review, options.jumps); - result.sent = false; - Object.assign(result, calculatedData); - - return result; - }); - } - - return this.processPageOnline(lesson.id, pageId, data, options).then((response) => { - this.eventsProvider.trigger(AddonModLessonProvider.DATA_SENT_EVENT, { - lessonId: lesson.id, - type: 'process', - courseId: courseId, - pageId: pageId, - review: options.review, - }, this.sitesProvider.getCurrentSiteId()); - - response.sent = true; - - return response; - }); - } - - /** - * Process a lesson page, saving its data. It will fail if offline or cannot connect. - * - * @param lessonId Lesson ID. - * @param pageId Page ID. - * @param data Data to save. - * @param options Other options. - * @return Promise resolved in success, rejected otherwise. - */ - processPageOnline(lessonId: number, pageId: number, data: any, options: AddonModLessonProcessPageOnlineOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params: any = { - lessonid: lessonId, - pageid: pageId, - data: this.utils.objectToArrayOfObjects(data, 'name', 'value', true), - review: options.review ? 1 : 0, - }; - - if (typeof options.password == 'string') { - params.password = options.password; - } - - return site.write('mod_lesson_process_page', params); - }); - } - - /** - * Records an attempt on a certain page. - * Based on Moodle's record_attempt. - * - * @param lesson Lesson. - * @param courseId Course ID the lesson belongs to. - * @param pageData Result of getPageData for the page to process. - * @param data Data to save. - * @param review If the user wants to review just after finishing (1 hour margin). - * @param accessInfo Result of get access info. - * @param jumps Result of get pages possible jumps. - * @param pageIndex Object containing all the pages indexed by ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the result. - */ - protected recordAttempt(lesson: any, courseId: number, pageData: any, data: any, review: boolean, accessInfo: any, jumps: any, - pageIndex: any, siteId?: string): Promise { - - // Check the user answer. Each page type has its own implementation. - const result: AddonModLessonRecordAttemptResult = this.checkAnswer(lesson, pageData, data, jumps, pageIndex), - retake = accessInfo.attemptscount; - - // Processes inmediate jumps. - if (result.inmediatejump) { - if (pageData.page.qtype == AddonModLessonProvider.LESSON_PAGE_BRANCHTABLE) { - // Store the content page data. In Moodle this is stored in a separate table, during checkAnswer. - return this.lessonOfflineProvider.processPage(lesson.id, courseId, retake, pageData.page, data, - result.newpageid, result.answerid, false, result.userresponse, siteId).then(() => { - return result; - }); - } - - return Promise.resolve(result); - } - - let promise = Promise.resolve(), - stop = false, - nAttempts; - - result.attemptsremaining = 0; - result.maxattemptsreached = false; - - if (result.noanswer) { - result.newpageid = pageData.page.id; // Display same page again. - result.feedback = this.translate.instant('addon.mod_lesson.noanswer'); - } else { - if (!accessInfo.canmanage) { - // Get the number of attempts that have been made on this question for this student and retake. - promise = this.getQuestionsAttempts(lesson.id, retake, { - cmId: lesson.coursemodule, - pageId: pageData.page.id, - siteId, - }).then((attempts) => { - nAttempts = attempts.online.length + attempts.offline.length; - - // Check if they have reached (or exceeded) the maximum number of attempts allowed. - if (nAttempts >= lesson.maxattempts) { - result.maxattemptsreached = true; - result.feedback = this.translate.instant('addon.mod_lesson.maximumnumberofattemptsreached'); - result.newpageid = AddonModLessonProvider.LESSON_NEXTPAGE; - stop = true; // Set stop to true to prevent further calculations. - - return; - } - - let subPromise; - - // Only insert a record if we are not reviewing the lesson. - if (!review) { - if (lesson.retake || (!lesson.retake && !retake)) { - // Store the student's attempt and increase the number of attempts made. - // Calculate and store the new page ID to prevent having to recalculate it later. - const newPageId = this.getNewPageId(pageData.page.id, result.newpageid, jumps); - subPromise = this.lessonOfflineProvider.processPage(lesson.id, courseId, retake, pageData.page, data, - newPageId, result.answerid, result.correctanswer, result.userresponse, siteId); - nAttempts++; - } - } - - // Check if "number of attempts remaining" message is needed. - if (!result.correctanswer && !result.newpageid) { - // Retreive the number of attempts left counter. - if (nAttempts >= lesson.maxattempts) { - if (lesson.maxattempts > 1) { // Don't bother with message if only one attempt. - result.maxattemptsreached = true; - } - result.newpageid = AddonModLessonProvider.LESSON_NEXTPAGE; - } else if (lesson.maxattempts > 1) { // Don't bother with message if only one attempt - result.attemptsremaining = lesson.maxattempts - nAttempts; - } - } - - return subPromise; - }); - } - - promise = promise.then(() => { - if (stop) { - return; - } - - // Determine default feedback if necessary. - if (!result.response) { - if (!lesson.feedback && !result.noanswer && - !(lesson.review && !result.correctanswer && !result.isessayquestion)) { - // These conditions have been met: - // 1. The lesson manager has not supplied feedback to the student. - // 2. Not displaying default feedback. - // 3. The user did provide an answer. - // 4. We are not reviewing with an incorrect answer (and not reviewing an essay question). - - result.nodefaultresponse = true; - } else if (result.isessayquestion) { - result.response = this.translate.instant('addon.mod_lesson.defaultessayresponse'); - } else if (result.correctanswer) { - result.response = this.translate.instant('addon.mod_lesson.thatsthecorrectanswer'); - } else { - result.response = this.translate.instant('addon.mod_lesson.thatsthewronganswer'); - } - } - - if (result.response) { - let subPromise; - - if (lesson.review && !result.correctanswer && !result.isessayquestion) { - // Calculate the number of question attempt in the page if it isn't calculated already. - if (typeof nAttempts == 'undefined') { - subPromise = this.getQuestionsAttempts(lesson.id, retake, { - cmId: lesson.coursemodule, - pageId: pageData.page.id, - siteId, - }).then((result) => { - nAttempts = result.online.length + result.offline.length; - }); - } else { - subPromise = Promise.resolve(); - } - - subPromise.then(() => { - const messageId = nAttempts == 1 ? 'firstwrong' : 'secondpluswrong'; - - result.feedback = ''; - }); - } else { - result.feedback = ''; - subPromise = Promise.resolve(); - } - - let className = 'response'; - if (result.correctanswer) { - className += ' correct'; - } else if (!result.isessayquestion) { - className += ' incorrect'; - } - - return subPromise.then(() => { - result.feedback += '
' + pageData.page.contents + '
'; - result.feedback += '
' + - this.translate.instant('addon.mod_lesson.youranswer') + ' : ' + - '
'; - - // Create a table containing the answers and responses. - if (pageData.page.qoption) { - // Multianswer allowed. - const studentAnswerArray = result.studentanswer ? - result.studentanswer.split(AddonModLessonProvider.MULTIANSWER_DELIMITER) : [], - responseArray = result.response ? - result.response.split(AddonModLessonProvider.MULTIANSWER_DELIMITER) : []; - - // Add answers and responses to the table. - for (let i = 0; i < studentAnswerArray.length; i++) { - result.feedback = this.addAnswerAndResponseToFeedback(result.feedback, studentAnswerArray[i], - result.studentanswerformat, responseArray[i], className); - } - } else { - // Only 1 answer, add it to the table. - result.feedback = this.addAnswerAndResponseToFeedback(result.feedback, result.studentanswer, - result.studentanswerformat, result.response, className); - } - - result.feedback += '
'; - }); - } - }); - } - - return promise.then(() => { - return result; - }); - } - - /** - * Remove a password stored in DB. - * - * @param lessonId Lesson ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when removed. - */ - removeStoredPassword(lessonId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModLessonProvider.PASSWORD_TABLE, {lessonid: lessonId}); - }); - } - - /** - * Store a password in DB. - * - * @param lessonId Lesson ID. - * @param password Password to store. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when stored. - */ - storePassword(lessonId: number, password: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - lessonid: lessonId, - password: password, - timemodified: Date.now() - }; - - return site.getDb().insertRecord(AddonModLessonProvider.PASSWORD_TABLE, entry); - }); - } - - /** - * Function to determine if a page is a valid page. It will add the page to validPages if valid. It can also - * modify the list of viewedPagesIds for cluster pages. - * Based on Moodle's valid_page_and_view. - * - * @param pages Index of lesson pages, indexed by page ID. See createPagesIndex. - * @param page Page to check. - * @param validPages Valid pages, indexed by page ID. - * @param viewedPagesIds List of viewed pages IDs. - * @return Next page ID. - */ - validPageAndView(pages: any, page: any, validPages: any, viewedPagesIds: number[]): number { - - if (page.qtype != AddonModLessonProvider.LESSON_PAGE_ENDOFCLUSTER && - page.qtype != AddonModLessonProvider.LESSON_PAGE_ENDOFBRANCH) { - // Add this page as a valid page. - validPages[page.id] = 1; - } - - if (page.qtype == AddonModLessonProvider.LESSON_PAGE_CLUSTER) { - // Get list of pages in the cluster. - const subPages = this.getSubpagesOf(pages, page.id, [AddonModLessonProvider.LESSON_PAGE_ENDOFCLUSTER]); - - subPages.forEach((subPage) => { - const position = viewedPagesIds.indexOf(subPage.id); - - if (position != -1) { - delete viewedPagesIds[position]; // Remove it. - - // Since the user did see one page in the cluster, add the cluster pageid to the viewedPagesIds. - if (viewedPagesIds.indexOf(page.id) == -1) { - viewedPagesIds.push(page.id); - } - } - }); - } - - return page.nextpageid; - } -} - -/** - * Common options including a group ID. - */ -export type AddonModLessonGroupOptions = CoreCourseCommonModWSOptions & { - groupId?: number; // The group to get. If not defined, all participants. -}; - -/** - * Common options including a group ID. - */ -export type AddonModLessonUserOptions = CoreCourseCommonModWSOptions & { - userId?: number; // User ID. If not defined, site's current user. -}; - -/** - * Common options including a password. - */ -export type AddonModLessonPasswordOptions = CoreCourseCommonModWSOptions & { - password?: string; // Lesson password (if any). -}; - -/** - * Common options including password and review. - */ -export type AddonModLessonPwdReviewOptions = AddonModLessonPasswordOptions & { - review?: boolean; // If the user wants to review just after finishing (1 hour margin). -}; - -/** - * Options to pass to get lesson with password. - */ -export type AddonModLessonGetWithPasswordOptions = AddonModLessonPasswordOptions & { - validatePassword?: boolean; // Defauls to true. If true, the function will fail if the password is wrong. - // If false, it will return a lesson with the basic data if password is wrong. -}; - -/** - * Options to pass to calculateProgress. - */ -export type AddonModLessonCalculateProgressBasicOptions = { - password?: string; // Lesson password (if any). - review?: boolean; // If the user wants to review just after finishing (1 hour margin). - pageIndex?: any; // Object containing all the pages indexed by ID. If not provided, it will be calculated. - siteId?: string; // Site ID. If not defined, current site. -}; - -/** - * Options to pass to calculateProgress. - */ -export type AddonModLessonCalculateProgressOptions = AddonModLessonCalculateProgressBasicOptions & { - cmId?: number; // Module ID. -}; - -/** - * Options to pass to lessonGrade. - */ -export type AddonModLessonGradeOptions = AddonModLessonCalculateProgressBasicOptions & { - userId?: number; // User ID. If not defined, site's user. -}; - -/** - * Options to pass to calculateOfflineData. - */ -export type AddonModLessonCalculateOfflineDataOptions = AddonModLessonCalculateProgressBasicOptions & { - accessInfo?: any; // Result of get access info. -}; - -/** - * Options to pass to get page data. - */ -export type AddonModLessonGetPageDataOptions = AddonModLessonPwdReviewOptions & { - includeContents?: boolean; // Include the page rendered contents. - accessInfo?: any; // Result of get access info. Required if offline is true. - jumps?: any; // Result of get pages possible jumps. Required if offline is true. -}; - -/** - * Options to pass to get page data. - */ -export type AddonModLessonGetPageViewMessagesOptions = { - password?: string; // Lesson password (if any). - review?: boolean; // If the user wants to review just after finishing (1 hour margin). - siteId?: string; // Site ID. If not defined, current site. -}; - -/** - * Options to pass to get questions attempts. - */ -export type AddonModLessonGetQuestionsAttemptsOptions = CoreCourseCommonModWSOptions & { - correct?: boolean; // True to only fetch correct attempts, false to get them all. - pageId?: number; // If defined, only get attempts on this page. - userId?: number; // User ID. If not defined, site's user. -}; - -/** - * Options to pass to getPagesIdsWithQuestionAttempts. - */ -export type AddonModLessonGetPagesIdsWithAttemptsOptions = CoreCourseCommonModWSOptions & { - correct?: boolean; // True to only fetch correct attempts, false to get them all. - userId?: number; // User ID. If not defined, site's user. -}; - -/** - * Options to pass to processPageOnline. - */ -export type AddonModLessonProcessPageOnlineOptions = { - password?: string; // Lesson password (if any). - review?: boolean; // If the user wants to review just after finishing (1 hour margin). - siteId?: string; // Site ID. If not defined, current site. -}; - -/** - * Options to pass to processPage. - */ -export type AddonModLessonProcessPageOptions = AddonModLessonProcessPageOnlineOptions & { - offline?: boolean; // Whether it's offline mode. - accessInfo?: any; // Result of get access info. Required if offline is true. - jumps?: any; // Result of get pages possible jumps. Required if offline is true. -}; - -/** - * Options to pass to finishRetakeOnline. - */ -export type AddonModLessonFinishRetakeOnlineOptions = { - password?: string; // Lesson password (if any). - outOfTime?: boolean; // Whether the user ran out of time. - review?: boolean; // If the user wants to review just after finishing (1 hour margin). - siteId?: string; // Site ID. If not defined, current site. -}; - -/** - * Options to pass to finishRetake. - */ -export type AddonModLessonFinishRetakeOptions = AddonModLessonFinishRetakeOnlineOptions & { - offline?: boolean; // Whether it's offline mode. - accessInfo?: any; // Result of get access info. Required if offline is true. -}; diff --git a/src/addon/mod/lesson/providers/list-link-handler.ts b/src/addon/mod/lesson/providers/list-link-handler.ts deleted file mode 100644 index 1906cc07d..000000000 --- a/src/addon/mod/lesson/providers/list-link-handler.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModLessonProvider } from './lesson'; - -/** - * Handler to treat links to lesson list page. - */ -@Injectable() -export class AddonModLessonListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModLessonListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected lessonProvider: AddonModLessonProvider) { - super(linkHelper, translate, 'AddonModLesson', 'lesson'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Promise resolved with boolean: whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return this.lessonProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/lesson/providers/module-handler.ts b/src/addon/mod/lesson/providers/module-handler.ts deleted file mode 100644 index 426d6b0e9..000000000 --- a/src/addon/mod/lesson/providers/module-handler.ts +++ /dev/null @@ -1,89 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModLessonIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModLessonProvider } from './lesson'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support quiz modules. - */ -@Injectable() -export class AddonModLessonModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModLesson'; - modName = 'lesson'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider, private lessonProvider: AddonModLessonProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Promise resolved with boolean: whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return this.lessonProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_lesson-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModLessonIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModLessonIndexComponent; - } -} diff --git a/src/addon/mod/lesson/providers/prefetch-handler.ts b/src/addon/mod/lesson/providers/prefetch-handler.ts deleted file mode 100644 index 1550e4143..000000000 --- a/src/addon/mod/lesson/providers/prefetch-handler.ts +++ /dev/null @@ -1,525 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { ModalController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider, CoreCourseCommonModWSOptions } from '@core/course/providers/course'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { AddonModLessonProvider } from './lesson'; -import { AddonModLessonSyncProvider } from './lesson-sync'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch lessons. - */ -@Injectable() -export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModLesson'; - modName = 'lesson'; - component = AddonModLessonProvider.COMPONENT; - // Don't check timers to decrease positives. If a user performs some action it will be reflected in other items. - updatesNames = /^configuration$|^.*files$|^grades$|^gradeitems$|^pages$|^answers$|^questionattempts$|^pagesviewed$/; - - protected syncProvider: AddonModLessonSyncProvider; // It will be injected later to prevent circular dependencies. - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected modalCtrl: ModalController, - protected groupsProvider: CoreGroupsProvider, - protected lessonProvider: AddonModLessonProvider, - protected injector: Injector) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Ask password. - * - * @param info Lesson access info. - * @return Promise resolved with the password. - */ - protected askUserPassword(info: any): Promise { - // Create and show the modal. - const modal = this.modalCtrl.create('AddonModLessonPasswordModalPage'); - - modal.present(); - - // Wait for modal to be dismissed. - return new Promise((resolve, reject): void => { - modal.onDidDismiss((password) => { - if (typeof password != 'undefined') { - resolve(password); - } else { - reject(this.domUtils.createCanceledError()); - } - }); - }); - } - - /** - * Get the download size of a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the size and a boolean indicating if it was able - * to calculate the total size. - */ - getDownloadSize(module: any, courseId: any, single?: boolean): Promise<{ size: number, total: boolean }> { - const siteId = this.sitesProvider.getCurrentSiteId(); - let lesson, - password, - result; - - return this.lessonProvider.getLesson(courseId, module.id, {siteId}).then((lessonData) => { - lesson = lessonData; - - // Get the lesson password if it's needed. - return this.getLessonPassword(lesson.id, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - askPassword: single, - siteId, - }); - }).then((data) => { - password = data.password; - lesson = data.lesson || lesson; - - // Get intro files and media files. - let files = lesson.mediafiles || []; - files = files.concat(this.getIntroFilesFromInstance(module, lesson)); - - return this.pluginFileDelegate.getFilesDownloadSize(files); - }).then((res) => { - result = res; - - // Get the pages to calculate the size. - return this.lessonProvider.getPages(lesson.id, { - cmId: module.id, - password, - siteId, - }); - }).then((pages) => { - pages.forEach((page) => { - result.size += page.filessizetotal; - }); - - return result; - }); - } - - /** - * Get the lesson password if needed. If not stored, it can ask the user to enter it. - * - * @param lessonId Lesson ID. - * @param options Other options. - * @return Promise resolved when done. - */ - getLessonPassword(lessonId: number, options: AddonModLessonGetPasswordOptions = {}) - : Promise<{password?: string, lesson?: any, accessInfo: any}> { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - // Get access information to check if password is needed. - return this.lessonProvider.getAccessInformation(lessonId, options).then((info): any => { - if (info.preventaccessreasons && info.preventaccessreasons.length) { - const passwordNeeded = info.preventaccessreasons.length == 1 && this.lessonProvider.isPasswordProtected(info); - if (passwordNeeded) { - - // The lesson requires a password. Check if there is one in DB. - return this.lessonProvider.getStoredPassword(lessonId).catch(() => { - // No password found. - }).then((password) => { - if (password) { - return this.validatePassword(lessonId, info, password, options); - } else { - return Promise.reject(null); - } - }).catch(() => { - // No password or error validating it. Ask for it if allowed. - if (options.askPassword) { - return this.askUserPassword(info).then((password) => { - return this.validatePassword(lessonId, info, password, options); - }); - } - - // Cannot ask for password, reject. - return Promise.reject(info.preventaccessreasons[0].message); - }); - } else { - // Lesson cannot be played, reject. - return Promise.reject(info.preventaccessreasons[0].message); - } - } - - // Password not needed. - return { accessInfo: info }; - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - // Only invalidate the data that doesn't ignore cache when prefetching. - const promises = []; - - promises.push(this.lessonProvider.invalidateLessonData(courseId)); - promises.push(this.courseProvider.invalidateModule(moduleId)); - promises.push(this.groupsProvider.invalidateActivityAllowedGroups(moduleId)); - - return Promise.all(promises); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const siteId = this.sitesProvider.getCurrentSiteId(); - - // Invalidate data to determine if module is downloadable. - return this.lessonProvider.getLesson(courseId, module.id, { - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId, - }).then((lesson) => { - const promises = []; - - promises.push(this.lessonProvider.invalidateLessonData(courseId, siteId)); - promises.push(this.lessonProvider.invalidateAccessInformation(lesson.id, siteId)); - - return Promise.all(promises); - }); - } - - /** - * Check if a module can be downloaded. If the function is not defined, we assume that all modules are downloadable. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Whether the module can be downloaded. The promise should never be rejected. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.lessonProvider.getLesson(courseId, module.id, {siteId}).then((lesson) => { - // Check if there is any prevent access reason. - return this.lessonProvider.getAccessInformation(lesson.id, {cmId: module.id, siteId}).then((info) => { - if (!info.canviewreports && !this.lessonProvider.isLessonOffline(lesson)) { - return false; - } - - // It's downloadable if there are no prevent access reasons or there is just 1 and it's password. - return !info.preventaccessreasons || !info.preventaccessreasons.length || - (info.preventaccessreasons.length == 1 && this.lessonProvider.isPasswordProtected(info)); - }); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.lessonProvider.isPluginEnabled(); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchLesson.bind(this)); - } - - /** - * Prefetch a lesson. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchLesson(module: any, courseId: number, single: boolean, siteId: string): Promise { - let lesson; - let password; - let accessInfo; - - const commonOptions = { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - const modOptions = { - cmId: module.id, - ...commonOptions, // Include all common options. - }; - - return this.lessonProvider.getLesson(courseId, module.id, commonOptions).then((lessonData) => { - lesson = lessonData; - - // Get the lesson password if it's needed. - return this.getLessonPassword(lesson.id, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - askPassword: single, - siteId, - }); - }).then((data) => { - password = data.password; - lesson = data.lesson || lesson; - accessInfo = data.accessInfo; - - if (this.lessonProvider.isLessonOffline(lesson) && !this.lessonProvider.leftDuringTimed(accessInfo)) { - // The user didn't left during a timed session. Call launch retake to make sure there is a started retake. - return this.lessonProvider.launchRetake(lesson.id, password, undefined, false, siteId).then(() => { - const promises = []; - - // New data generated, update the download time and refresh the access info. - promises.push(this.filepoolProvider.updatePackageDownloadTime(siteId, this.component, module.id).catch(() => { - // Ignore errors. - })); - - promises.push(this.lessonProvider.getAccessInformation(lesson.id, modOptions).then((info) => { - accessInfo = info; - })); - - return Promise.all(promises); - }); - } - }).then(() => { - const promises = [], - retake = accessInfo.attemptscount; - - // Download intro files and media files. - let files = lesson.mediafiles || []; - files = files.concat(this.getIntroFilesFromInstance(module, lesson)); - - promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id)); - - // Get the list of pages. - if (this.lessonProvider.isLessonOffline(lesson)) { - const passwordOptions = { - password, - ...modOptions, // Include all mod options. - }; - - promises.push(this.lessonProvider.getPages(lesson.id, passwordOptions).then((pages) => { - const subPromises = []; - let hasRandomBranch = false; - - // Get the data for each page. - pages.forEach((data) => { - // Check if any page has a RANDOMBRANCH jump. - if (!hasRandomBranch) { - for (let i = 0; i < data.jumps.length; i++) { - if (data.jumps[i] == AddonModLessonProvider.LESSON_RANDOMBRANCH) { - hasRandomBranch = true; - break; - } - } - } - - // Get the page data. We don't pass accessInfo because we don't need to calculate the offline data. - subPromises.push(this.lessonProvider.getPageData(lesson, data.page.id, { - includeContents: true, - ...passwordOptions, // Include all options. - }).then((pageData) => { - - // Download the page files. - let pageFiles = pageData.contentfiles || []; - - pageData.answers.forEach((answer) => { - if (answer.answerfiles && answer.answerfiles.length) { - pageFiles = pageFiles.concat(answer.answerfiles); - } - if (answer.responsefiles && answer.responsefiles.length) { - pageFiles = pageFiles.concat(answer.responsefiles); - } - }); - - return this.filepoolProvider.addFilesToQueue(siteId, pageFiles, this.component, module.id); - })); - }); - - // Prefetch the list of possible jumps for offline navigation. Do it here because we know hasRandomBranch. - subPromises.push(this.lessonProvider.getPagesPossibleJumps(lesson.id, modOptions).catch((error) => { - if (hasRandomBranch) { - // The WebSevice probably failed because RANDOMBRANCH aren't supported if the user hasn't seen any page. - return Promise.reject(this.translate.instant('addon.mod_lesson.errorprefetchrandombranch')); - } else { - return Promise.reject(error); - } - })); - - return Promise.all(subPromises); - })); - - // Prefetch user timers to be able to calculate timemodified in offline. - promises.push(this.lessonProvider.getTimers(lesson.id, modOptions).catch(() => { - // Ignore errors. - })); - - // Prefetch viewed pages in last retake to calculate progress. - promises.push(this.lessonProvider.getContentPagesViewedOnline(lesson.id, retake, modOptions)); - - // Prefetch question attempts in last retake for offline calculations. - promises.push(this.lessonProvider.getQuestionsAttemptsOnline(lesson.id, retake, modOptions)); - } - - if (accessInfo.canviewreports) { - // Prefetch reports data. - promises.push(this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, siteId, true).then((info) => { - const subPromises = []; - - info.groups.forEach((group) => { - subPromises.push(this.lessonProvider.getRetakesOverview(lesson.id, { - groupId: group.id, - ...modOptions, // Include all options. - })); - }); - - // Always get all participants, even if there are no groups. - subPromises.push(this.lessonProvider.getRetakesOverview(lesson.id, modOptions).then((data) => { - if (!data || !data.students) { - return; - } - - // Prefetch the last retake for each user. - const retakePromises = []; - - data.students.forEach((student) => { - if (!student.attempts || !student.attempts.length) { - return; - } - - const lastRetake = student.attempts[student.attempts.length - 1]; - if (!lastRetake) { - return; - } - - retakePromises.push(this.lessonProvider.getUserRetake(lesson.id, lastRetake.try, { - userId: student.id, - ...modOptions, // Include all options. - }).then((attempt) => { - if (!attempt || !attempt.answerpages) { - return; - } - - // Download embedded files in essays. - const files = []; - attempt.answerpages.forEach((answerPage) => { - if (answerPage.page.qtype != AddonModLessonProvider.LESSON_PAGE_ESSAY) { - return; - } - answerPage.answerdata.answers.forEach((answer) => { - files.push(...this.filepoolProvider.extractDownloadableFilesFromHtmlAsFakeFileObjects( - answer[0])); - }); - }); - - return this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id); - })); - }); - - return Promise.all(retakePromises); - })); - - return Promise.all(subPromises); - })); - } - - return Promise.all(promises); - }); - } - - /** - * Validate the password. - * - * @param lessonId Lesson ID. - * @param info Lesson access info. - * @param pwd Password to check. - * @param options Other options. - * @return Promise resolved when done. - */ - protected validatePassword(lessonId: number, info: any, pwd: string, options: CoreCourseCommonModWSOptions = {}) - : Promise<{password: string, lesson: any, accessInfo: any}> { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - return this.lessonProvider.getLessonWithPassword(lessonId, { - password: pwd, - ...options, // Include all options. - }).then((lesson) => { - // Password is ok, store it and return the data. - return this.lessonProvider.storePassword(lesson.id, pwd, options.siteId).then(() => { - return { - password: pwd, - lesson: lesson, - accessInfo: info - }; - }); - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - if (!this.syncProvider) { - this.syncProvider = this.injector.get(AddonModLessonSyncProvider); - } - - return this.syncProvider.syncLesson(module.instance, false, false, siteId); - } -} - -/** - * Options to pass to get lesson password. - */ -export type AddonModLessonGetPasswordOptions = CoreCourseCommonModWSOptions & { - askPassword?: boolean; // True if we should ask for password if needed, false otherwise. -}; diff --git a/src/addon/mod/lesson/providers/push-click-handler.ts b/src/addon/mod/lesson/providers/push-click-handler.ts deleted file mode 100644 index 85378bbf2..000000000 --- a/src/addon/mod/lesson/providers/push-click-handler.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreGradesProvider } from '@core/grades/providers/grades'; -import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; - -/** - * Handler for lesson push notifications clicks. - */ -@Injectable() -export class AddonModLessonPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonModLessonPushClickHandler'; - priority = 200; - featureName = 'CoreCourseModuleDelegate_AddonModLesson'; - - constructor(private utils: CoreUtilsProvider, private gradesHelper: CoreGradesHelperProvider, - private gradesProvider: CoreGradesProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - if (this.utils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_lesson' && - notification.name == 'graded_essay') { - - return this.gradesProvider.isPluginEnabledForCourse(Number(notification.courseid), notification.site); - } - - return false; - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - handleClick(notification: any): Promise { - const data = notification.customdata || {}, - courseId = Number(notification.courseid), - moduleId = Number(data.cmid); - - return this.gradesHelper.goToGrades(courseId, undefined, moduleId, undefined, notification.site); - } -} diff --git a/src/addon/mod/lesson/providers/report-link-handler.ts b/src/addon/mod/lesson/providers/report-link-handler.ts deleted file mode 100644 index bb6d8739c..000000000 --- a/src/addon/mod/lesson/providers/report-link-handler.ts +++ /dev/null @@ -1,154 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModLessonProvider } from './lesson'; - -/** - * Handler to treat links to lesson report. - */ -@Injectable() -export class AddonModLessonReportLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModLessonReportLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModLesson'; - pattern = /\/mod\/lesson\/report\.php.*([\&\?]id=\d+)/; - - constructor(protected domUtils: CoreDomUtilsProvider, protected lessonProvider: AddonModLessonProvider, - protected courseHelper: CoreCourseHelperProvider, protected linkHelper: CoreContentLinksHelperProvider, - protected courseProvider: CoreCourseProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - courseId = courseId || params.courseid || params.cid; - - return [{ - action: (siteId, navCtrl?): void => { - if (!params.action || params.action == 'reportoverview') { - // Go to overview. - this.openReportOverview(parseInt(params.id, 10), courseId, parseInt(params.group, 10), siteId, navCtrl); - } else if (params.action == 'reportdetail') { - this.openUserRetake(parseInt(params.id, 10), parseInt(params.userid, 10), courseId, parseInt(params.try, 10), - siteId, navCtrl); - } - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - if (params.action == 'reportdetail' && !params.userid) { - // Individual details are only available if the teacher is seeing a certain user. - return false; - } - - return this.lessonProvider.isPluginEnabled(); - } - - /** - * Open report overview. - * - * @param moduleId Module ID. - * @param courseId Course ID. - * @param groupId Group ID. - * @param siteId Site ID. - * @param navCtrl The NavController to use to navigate. - * @return Promise resolved when done. - */ - protected openReportOverview(moduleId: number, courseId?: number, groupId?: number, siteId?: string, navCtrl?: NavController) - : Promise { - - const modal = this.domUtils.showModalLoading(); - - // Get the module object. - return this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => { - courseId = courseId || module.course; - - const pageParams = { - module: module, - courseId: Number(courseId), - action: 'report', - group: isNaN(groupId) ? null : groupId - }; - - this.linkHelper.goInSite(navCtrl, 'AddonModLessonIndexPage', pageParams, siteId); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error processing link.'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Open a user's retake. - * - * @param moduleId Module ID. - * @param userId User ID. - * @param courseId Course ID. - * @param retake Retake to open. - * @param groupId Group ID. - * @param siteId Site ID. - * @param navCtrl The NavController to use to navigate. - * @return Promise resolved when done. - */ - protected openUserRetake(moduleId: number, userId: number, courseId: number, retake: number, siteId: string, - navCtrl?: NavController): Promise { - - const modal = this.domUtils.showModalLoading(); - - // Get the module object. - return this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => { - courseId = courseId || module.course; - - const pageParams = { - lessonId: module.instance, - courseId: Number(courseId), - userId: userId, - retake: retake || 0 - }; - - this.linkHelper.goInSite(navCtrl, 'AddonModLessonUserRetakePage', pageParams, siteId); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error processing link.'); - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/mod/lesson/providers/sync-cron-handler.ts b/src/addon/mod/lesson/providers/sync-cron-handler.ts deleted file mode 100644 index 8dab6edd0..000000000 --- a/src/addon/mod/lesson/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModLessonSyncProvider } from './lesson-sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModLessonSyncCronHandler implements CoreCronHandler { - name = 'AddonModLessonSyncCronHandler'; - - constructor(private lessonSync: AddonModLessonSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.lessonSync.syncAllLessons(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.lessonSync.syncInterval; - } -} diff --git a/src/addon/mod/lti/components/components.module.ts b/src/addon/mod/lti/components/components.module.ts deleted file mode 100644 index a0ae72b9e..000000000 --- a/src/addon/mod/lti/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModLtiIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModLtiIndexComponent, - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModLtiIndexComponent, - ], - entryComponents: [ - AddonModLtiIndexComponent, - ] -}) -export class AddonModLtiComponentsModule {} diff --git a/src/addon/mod/lti/components/index/addon-mod-lti-index.html b/src/addon/mod/lti/components/index/addon-mod-lti-index.html deleted file mode 100644 index 334ef2e7a..000000000 --- a/src/addon/mod/lti/components/index/addon-mod-lti-index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - -
- -
-
diff --git a/src/addon/mod/lti/components/index/index.ts b/src/addon/mod/lti/components/index/index.ts deleted file mode 100644 index f078e2070..000000000 --- a/src/addon/mod/lti/components/index/index.ts +++ /dev/null @@ -1,98 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector } from '@angular/core'; -import { Content } from 'ionic-angular'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { AddonModLtiProvider, AddonModLtiLti } from '../../providers/lti'; -import { AddonModLtiHelper } from '../../providers/helper'; - -/** - * Component that displays an LTI entry page. - */ -@Component({ - selector: 'addon-mod-lti-index', - templateUrl: 'addon-mod-lti-index.html', -}) -export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityComponent { - component = AddonModLtiProvider.COMPONENT; - moduleName = 'lti'; - - lti: AddonModLtiLti; // The LTI object. - - protected fetchContentDefaultError = 'addon.mod_lti.errorgetlti'; - - constructor(injector: Injector, - @Optional() protected content: Content, - private ltiProvider: AddonModLtiProvider) { - super(injector, content); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.loadContent(); - } - - /** - * Check the completion. - */ - protected checkCompletion(): void { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - - /** - * Get the LTI data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - return this.ltiProvider.getLti(this.courseId, this.module.id).then((ltiData) => { - this.lti = ltiData; - this.description = this.lti.intro; - this.dataRetrieved.emit(this.lti); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.ltiProvider.invalidateLti(this.courseId)); - if (this.lti) { - promises.push(this.ltiProvider.invalidateLtiLaunchData(this.lti.id)); - } - - return Promise.all(promises); - } - - /** - * Launch the LTI. - */ - launch(): void { - AddonModLtiHelper.instance.getDataAndLaunch(this.courseId, this.module, this.lti); - } -} diff --git a/src/addon/mod/lti/lang/en.json b/src/addon/mod/lti/lang/en.json deleted file mode 100644 index 7a70ea4e7..000000000 --- a/src/addon/mod/lti/lang/en.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "errorgetlti": "Error getting module data.", - "errorinvalidlaunchurl": "The launch URL is not valid.", - "launchactivity": "Launch the activity", - "modulenameplural": "External tools" -} \ No newline at end of file diff --git a/src/addon/mod/lti/lti.module.ts b/src/addon/mod/lti/lti.module.ts deleted file mode 100644 index 20649b794..000000000 --- a/src/addon/mod/lti/lti.module.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModLtiComponentsModule } from './components/components.module'; -import { AddonModLtiModuleHandler } from './providers/module-handler'; -import { AddonModLtiProvider } from './providers/lti'; -import { AddonModLtiHelperProvider } from './providers/helper'; -import { AddonModLtiLinkHandler } from './providers/link-handler'; -import { AddonModLtiListLinkHandler } from './providers/list-link-handler'; -import { AddonModLtiPrefetchHandler } from './providers/prefetch-handler'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; - -// List of providers (without handlers). -export const ADDON_MOD_LTI_PROVIDERS: any[] = [ - AddonModLtiProvider, - AddonModLtiHelperProvider, -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModLtiComponentsModule - ], - providers: [ - AddonModLtiProvider, - AddonModLtiHelperProvider, - AddonModLtiModuleHandler, - AddonModLtiLinkHandler, - AddonModLtiListLinkHandler, - AddonModLtiPrefetchHandler, - ] -}) -export class AddonModLtiModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, - moduleHandler: AddonModLtiModuleHandler, - contentLinksDelegate: CoreContentLinksDelegate, - linkHandler: AddonModLtiLinkHandler, - listLinkHandler: AddonModLtiListLinkHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, - prefetchHandler: AddonModLtiPrefetchHandler) { - - moduleDelegate.registerHandler(moduleHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - prefetchDelegate.registerHandler(prefetchHandler); - } -} diff --git a/src/addon/mod/lti/pages/index/index.html b/src/addon/mod/lti/pages/index/index.html deleted file mode 100644 index 68e70bb3c..000000000 --- a/src/addon/mod/lti/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/lti/pages/index/index.module.ts b/src/addon/mod/lti/pages/index/index.module.ts deleted file mode 100644 index 1300091a8..000000000 --- a/src/addon/mod/lti/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModLtiComponentsModule } from '../../components/components.module'; -import { AddonModLtiIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModLtiIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModLtiComponentsModule, - IonicPageModule.forChild(AddonModLtiIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModLtiIndexPageModule {} diff --git a/src/addon/mod/lti/pages/index/index.ts b/src/addon/mod/lti/pages/index/index.ts deleted file mode 100644 index 697d93d81..000000000 --- a/src/addon/mod/lti/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModLtiIndexComponent } from '../../components/index/index'; - -/** - * Page that displays an LTI. - */ -@IonicPage({ segment: 'addon-mod-lti-index' }) -@Component({ - selector: 'page-addon-mod-lti-index', - templateUrl: 'index.html', -}) -export class AddonModLtiIndexPage { - @ViewChild(AddonModLtiIndexComponent) ltiComponent: AddonModLtiIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the LTI instance. - * - * @param lti LTI instance. - */ - updateData(lti: any): void { - this.title = lti.name || this.title; - } -} diff --git a/src/addon/mod/lti/providers/helper.ts b/src/addon/mod/lti/providers/helper.ts deleted file mode 100644 index 24a771c69..000000000 --- a/src/addon/mod/lti/providers/helper.ts +++ /dev/null @@ -1,119 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { Platform } from 'ionic-angular'; -import { CoreEvents, CoreEventsProvider } from '@providers/events'; -import { CoreSites } from '@providers/sites'; -import { CoreDomUtils } from '@providers/utils/dom'; -import { CoreCourse } from '@core/course/providers/course'; -import { AddonModLti, AddonModLtiLti } from './lti'; - -import { makeSingleton } from '@singletons/core.singletons'; - -/** - * Service that provides some helper functions for LTI. - */ -@Injectable() -export class AddonModLtiHelperProvider { - - protected pendingCheckCompletion: {[moduleId: string]: {courseId: number, module: any}} = {}; - - constructor(platform: Platform) { - - platform.resume.subscribe(() => { - // User went back to the app, check pending completions. - for (const moduleId in this.pendingCheckCompletion) { - const data = this.pendingCheckCompletion[moduleId]; - - CoreCourse.instance.checkModuleCompletion(data.courseId, data.module.completiondata); - } - }); - - // Clear pending completion on logout. - CoreEvents.instance.on(CoreEventsProvider.LOGOUT, () => { - this.pendingCheckCompletion = {}; - }); - } - - /** - * Get needed data and launch the LTI. - * - * @param courseId Course ID. - * @param module Module. - * @param lti LTI instance. If not provided it will be obtained. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - async getDataAndLaunch(courseId: number, module: any, lti?: AddonModLtiLti, siteId?: string): Promise { - siteId = siteId || CoreSites.instance.getCurrentSiteId(); - - const modal = CoreDomUtils.instance.showModalLoading(); - - try { - const openInBrowser = await AddonModLti.instance.isOpenInAppBrowserDisabled(siteId); - - if (openInBrowser) { - const site = await CoreSites.instance.getSite(siteId); - - // The view event is triggered by the browser, mark the module as pending to check completion. - this.pendingCheckCompletion[module.id] = { - courseId, - module, - }; - - await site.openInBrowserWithAutoLogin(module.url); - } else { - // Open in app. - if (!lti) { - lti = await AddonModLti.instance.getLti(courseId, module.id); - } - - const launchData = await AddonModLti.instance.getLtiLaunchData(lti.id); - - // "View" LTI without blocking the UI. - this.logViewAndCheckCompletion(courseId, module, lti.id, lti.name, siteId); - - // Launch LTI. - return AddonModLti.instance.launch(launchData.endpoint, launchData.parameters); - } - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_lti.errorgetlti', true); - } finally { - modal.dismiss(); - } - } - - /** - * Report the LTI as being viewed and check completion. - * - * @param courseId Course ID. - * @param module Module. - * @param ltiId LTI id. - * @param name Name of the lti. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - async logViewAndCheckCompletion(courseId: number, module: any, ltiId: number, name?: string, siteId?: string): Promise { - try { - await AddonModLti.instance.logView(ltiId, name); - - CoreCourse.instance.checkModuleCompletion(courseId, module.completiondata); - } catch (error) { - // Ignore errors. - } - } -} - -export class AddonModLtiHelper extends makeSingleton(AddonModLtiHelperProvider) {} diff --git a/src/addon/mod/lti/providers/link-handler.ts b/src/addon/mod/lti/providers/link-handler.ts deleted file mode 100644 index 2a174688c..000000000 --- a/src/addon/mod/lti/providers/link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to LTI. - */ -@Injectable() -export class AddonModLtiLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModLtiLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModLti', 'lti', 'l'); - } -} diff --git a/src/addon/mod/lti/providers/list-link-handler.ts b/src/addon/mod/lti/providers/list-link-handler.ts deleted file mode 100644 index 5a474395d..000000000 --- a/src/addon/mod/lti/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to LTI list page. - */ -@Injectable() -export class AddonModLtiListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModLtiListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModLti', 'lti'); - } -} diff --git a/src/addon/mod/lti/providers/lti.ts b/src/addon/mod/lti/providers/lti.ts deleted file mode 100644 index b2d301078..000000000 --- a/src/addon/mod/lti/providers/lti.ts +++ /dev/null @@ -1,328 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFileProvider } from '@providers/file'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; - -import { makeSingleton } from '@singletons/core.singletons'; - -/** - * Service that provides some features for LTI. - */ -@Injectable() -export class AddonModLtiProvider { - static COMPONENT = 'mmaModLti'; - - protected ROOT_CACHE_KEY = 'mmaModLti:'; - protected LAUNCHER_FILE_NAME = 'lti_launcher.html'; - - constructor(private fileProvider: CoreFileProvider, - private sitesProvider: CoreSitesProvider, - private textUtils: CoreTextUtilsProvider, - private urlUtils: CoreUrlUtilsProvider, - private utils: CoreUtilsProvider, - private translate: TranslateService, - private appProvider: CoreAppProvider, - private logHelper: CoreCourseLogHelperProvider) {} - - /** - * Delete launcher. - * - * @return Promise resolved when the launcher file is deleted. - */ - deleteLauncher(): Promise { - return this.fileProvider.removeFile(this.LAUNCHER_FILE_NAME); - } - - /** - * Generates a launcher file. - * - * @param url Launch URL. - * @param params Launch params. - * @return Promise resolved with the file URL. - */ - async generateLauncher(url: string, params: AddonModLtiParam[]): Promise { - if (!this.fileProvider.isAvailable()) { - return url; - } - - // Generate a form with the params. - let text = '
\n'; - params.forEach((p) => { - if (p.name == 'ext_submit') { - text += ' \n'; - }); - text += '
\n'; - - // Add an in-line script to automatically submit the form. - text += ' \n'; - - const entry = await this.fileProvider.writeFile(this.LAUNCHER_FILE_NAME, text); - - if (this.appProvider.isDesktop()) { - return entry.toInternalURL(); - } else { - return entry.toURL(); - } - } - - /** - * Get a LTI. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the LTI is retrieved. - */ - async getLti(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - const params: any = { - courseids: [courseId] - }; - const preSets = { - cacheKey: this.getLtiCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModLtiProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - const site = await this.sitesProvider.getSite(options.siteId); - - const response: AddonModLtiGetLtisByCoursesResult = await site.read('mod_lti_get_ltis_by_courses', params, preSets); - - if (response.ltis) { - const currentLti = response.ltis.find((lti) => lti.coursemodule == cmId); - if (currentLti) { - return currentLti; - } - } - - throw new Error('Activity not found.'); - } - - /** - * Get cache key for LTI data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getLtiCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'lti:' + courseId; - } - - /** - * Get a LTI launch data. - * - * @param id LTI id. - * @return Promise resolved when the launch data is retrieved. - */ - getLtiLaunchData(id: number): Promise { - const params = { - toolid: id - }; - - // Try to avoid using cache since the "nonce" parameter is set to a timestamp. - const preSets = { - getFromCache: false, - saveToCache: true, - emergencyCache: true, - cacheKey: this.getLtiLaunchDataCacheKey(id) - }; - - return this.sitesProvider.getCurrentSite().read('mod_lti_get_tool_launch_data', params, preSets) - .then((response: AddonModLtiGetToolLaunchDataResult): any => { - - if (response.endpoint) { - return response; - } - - return Promise.reject(null); - }); - } - - /** - * Get cache key for LTI launch data WS calls. - * - * @param id LTI id. - * @return Cache key. - */ - protected getLtiLaunchDataCacheKey(id: number): string { - return this.ROOT_CACHE_KEY + 'launch:' + id; - } - - /** - * Invalidates LTI data. - * - * @param courseId Course ID. - * @return Promise resolved when the data is invalidated. - */ - invalidateLti(courseId: number): Promise { - return this.sitesProvider.getCurrentSite().invalidateWsCacheForKey(this.getLtiCacheKey(courseId)); - } - - /** - * Invalidates options. - * - * @param id LTI id. - * @return Promise resolved when the data is invalidated. - */ - invalidateLtiLaunchData(id: number): Promise { - return this.sitesProvider.getCurrentSite().invalidateWsCacheForKey(this.getLtiLaunchDataCacheKey(id)); - } - - /** - * Check if open in InAppBrowser is disabled. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether it's disabled. - */ - async isOpenInAppBrowserDisabled(siteId?: string): Promise { - const site = await this.sitesProvider.getSite(siteId); - - return this.isOpenInAppBrowserDisabledInSite(site); - } - - /** - * Check if open in InAppBrowser is disabled. - * - * @param site Site. If not defined, current site. - * @return Whether it's disabled. - */ - isOpenInAppBrowserDisabledInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.isFeatureDisabled('CoreCourseModuleDelegate_AddonModLti:openInAppBrowser'); - } - - /** - * Launch LTI. - * - * @param url Launch URL. - * @param params Launch params. - * @return Promise resolved when the WS call is successful. - */ - async launch(url: string, params: AddonModLtiParam[]): Promise { - if (!this.urlUtils.isHttpURL(url)) { - throw this.translate.instant('addon.mod_lti.errorinvalidlaunchurl'); - } - - // Generate launcher and open it. - const launcherUrl = await this.generateLauncher(url, params); - - if (this.appProvider.isMobile()) { - this.utils.openInApp(launcherUrl); - } else { - // In desktop open in browser, we found some cases where inapp caused JS issues. - this.utils.openInBrowser(launcherUrl); - } - } - - /** - * Report the LTI as being viewed. - * - * @param id LTI id. - * @param name Name of the lti. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params: any = { - ltiid: id - }; - - return this.logHelper.logSingle('mod_lti_view_lti', params, AddonModLtiProvider.COMPONENT, id, name, 'lti', {}, siteId); - } -} - -export class AddonModLti extends makeSingleton(AddonModLtiProvider) {} - -/** - * LTI returned by mod_lti_get_ltis_by_courses. - */ -export type AddonModLtiLti = { - id: number; // External tool id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // LTI name. - intro?: string; // The LTI intro. - introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles?: CoreWSExternalFile[]; // @since 3.2. - timecreated?: number; // Time of creation. - timemodified?: number; // Time of last modification. - typeid?: number; // Type id. - toolurl?: string; // Tool url. - securetoolurl?: string; // Secure tool url. - instructorchoicesendname?: string; // Instructor choice send name. - instructorchoicesendemailaddr?: number; // Instructor choice send mail address. - instructorchoiceallowroster?: number; // Instructor choice allow roster. - instructorchoiceallowsetting?: number; // Instructor choice allow setting. - instructorcustomparameters?: string; // Instructor custom parameters. - instructorchoiceacceptgrades?: number; // Instructor choice accept grades. - grade?: number; // Enable grades. - launchcontainer?: number; // Launch container mode. - resourcekey?: string; // Resource key. - password?: string; // Shared secret. - debuglaunch?: number; // Debug launch. - showtitlelaunch?: number; // Show title launch. - showdescriptionlaunch?: number; // Show description launch. - servicesalt?: string; // Service salt. - icon?: string; // Alternative icon URL. - secureicon?: string; // Secure icon URL. - section?: number; // Course section id. - visible?: number; // Visible. - groupmode?: number; // Group mode. - groupingid?: number; // Group id. -}; - -/** - * Param to send to the LTI. - */ -export type AddonModLtiParam = { - name: string; // Parameter name. - value: string; // Parameter value. -}; - -/** - * Result of WS mod_lti_get_ltis_by_courses. - */ -export type AddonModLtiGetLtisByCoursesResult = { - ltis: AddonModLtiLti[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_lti_get_tool_launch_data. - */ -export type AddonModLtiGetToolLaunchDataResult = { - endpoint: string; // Endpoint URL. - parameters: AddonModLtiParam[]; - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/lti/providers/module-handler.ts b/src/addon/mod/lti/providers/module-handler.ts deleted file mode 100644 index eba92ce10..000000000 --- a/src/addon/mod/lti/providers/module-handler.ts +++ /dev/null @@ -1,128 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { DomSanitizer } from '@angular/platform-browser'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreAppProvider } from '@providers/app'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonModLtiIndexComponent } from '../components/index/index'; -import { AddonModLtiProvider } from './lti'; -import { AddonModLtiHelper } from './helper'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support LTI modules. - */ -@Injectable() -export class AddonModLtiModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModLti'; - modName = 'lti'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private appProvider: CoreAppProvider, - private courseProvider: CoreCourseProvider, - private filepoolProvider: CoreFilepoolProvider, - private sitesProvider: CoreSitesProvider, - private ltiProvider: AddonModLtiProvider, - private sanitizer: DomSanitizer) {} - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - const data: CoreCourseModuleHandlerData = { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_lti-handler', - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModLtiIndexPage', pageParams, options); - }, - buttons: [{ - icon: 'link', - label: 'addon.mod_lti.launchactivity', - action: (event: Event, navCtrl: NavController, module: any, courseId: number): void => { - // Launch the LTI. - AddonModLtiHelper.instance.getDataAndLaunch(courseId, module); - } - }] - }; - - // Handle custom icons. - this.ltiProvider.getLti(courseId, module.id).then((ltiData) => { - const icon = ltiData.secureicon || ltiData.icon; - if (icon) { - const siteId = this.sitesProvider.getCurrentSiteId(); - this.filepoolProvider.downloadUrl(siteId, icon, false, AddonModLtiProvider.COMPONENT, module.id).then(() => { - // Get the internal URL. - return this.filepoolProvider.getSrcByUrl(siteId, icon, AddonModLtiProvider.COMPONENT, module.id); - }).then((url) => { - data.icon = this.sanitizer.bypassSecurityTrustUrl(url); - }).catch(() => { - // Error downloading. If we're online we'll set the online url. - if (this.appProvider.isOnline()) { - data.icon = this.sanitizer.bypassSecurityTrustUrl(icon); - } - }); - } - }).catch(() => { - // Ignore errors. - }); - - return data; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModLtiIndexComponent; - } -} diff --git a/src/addon/mod/lti/providers/prefetch-handler.ts b/src/addon/mod/lti/providers/prefetch-handler.ts deleted file mode 100644 index d2b02d014..000000000 --- a/src/addon/mod/lti/providers/prefetch-handler.ts +++ /dev/null @@ -1,99 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { AddonModLtiProvider } from './lti'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch LTIs. LTIs cannot be prefetched, but the handler will be used to invalidate some data on course PTR. - */ -@Injectable() -export class AddonModLtiPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModLti'; - modName = 'lti'; - component = AddonModLtiProvider.COMPONENT; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected ltiProvider: AddonModLtiProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Download the module. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when all content is downloaded. - */ - download(module: any, courseId: number, dirPath?: string): Promise { - return Promise.resolve(); - } - - /** - * Invalidate WS calls needed to determine module status (usually, to check if module is downloadable). - * It doesn't need to invalidate check updates. It should NOT invalidate files nor all the prefetched data. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - return this.ltiProvider.invalidateLti(courseId); - } - - /** - * Check if a module can be downloaded. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Whether the module can be downloaded. The promise should never be rejected. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - return false; // LTIs aren't downloadable. - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return Promise.resolve(); - } -} diff --git a/src/addon/mod/page/components/components.module.ts b/src/addon/mod/page/components/components.module.ts deleted file mode 100644 index d36a28733..000000000 --- a/src/addon/mod/page/components/components.module.ts +++ /dev/null @@ -1,47 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModPageIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModPageIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModPageIndexComponent - ], - entryComponents: [ - AddonModPageIndexComponent - ] -}) -export class AddonModPageComponentsModule {} diff --git a/src/addon/mod/page/components/index/addon-mod-page-index.html b/src/addon/mod/page/components/index/addon-mod-page-index.html deleted file mode 100644 index 783080214..000000000 --- a/src/addon/mod/page/components/index/addon-mod-page-index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -
- -

- {{ 'core.lastmodified' | translate}}: {{ page.timemodified * 1000 | coreFormatDate }} -

-
- -
diff --git a/src/addon/mod/page/components/index/index.scss b/src/addon/mod/page/components/index/index.scss deleted file mode 100644 index e1837f5ea..000000000 --- a/src/addon/mod/page/components/index/index.scss +++ /dev/null @@ -1,10 +0,0 @@ -addon-mod-page-index { - /* Solves iframe height */ - .core-loading-content > div[padding] { - height: 100%; - } - - core-format-text > .no-overflow { - display: inline; - } -} \ No newline at end of file diff --git a/src/addon/mod/page/components/index/index.ts b/src/addon/mod/page/components/index/index.ts deleted file mode 100644 index 27f84b9b9..000000000 --- a/src/addon/mod/page/components/index/index.ts +++ /dev/null @@ -1,135 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Injector } from '@angular/core'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { - CoreCourseModuleMainResourceComponent, CoreCourseResourceDownloadResult -} from '@core/course/classes/main-resource-component'; -import { AddonModPageProvider, AddonModPagePage } from '../../providers/page'; -import { AddonModPageHelperProvider } from '../../providers/helper'; - -/** - * Component that displays a page. - */ -@Component({ - selector: 'addon-mod-page-index', - templateUrl: 'addon-mod-page-index.html', -}) -export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComponent { - component = AddonModPageProvider.COMPONENT; - canGetPage: boolean; - contents: any; - displayDescription = true; - displayTimemodified = true; - page: AddonModPagePage; - warning: string; - - protected fetchContentDefaultError = 'addon.mod_page.errorwhileloadingthepage'; - - constructor(injector: Injector, - protected pageProvider: AddonModPageProvider, - protected pageHelper: AddonModPageHelperProvider, - protected utils: CoreUtilsProvider) { - super(injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.canGetPage = this.pageProvider.isGetPageWSAvailable(); - - this.loadContent().then(() => { - this.pageProvider.logView(this.module.instance, this.module.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return this.pageProvider.invalidateContent(this.module.id, this.courseId); - } - - /** - * Download page contents. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchContent(refresh?: boolean): Promise { - let downloadResult: CoreCourseResourceDownloadResult; - - // Download the resource if it needs to be downloaded. - return this.downloadResourceIfNeeded(refresh).then((result) => { - downloadResult = result; - }).then(() => { - const promises = []; - - let getPagePromise; - - // Get the module to get the latest title and description. Data should've been updated in download. - if (this.canGetPage) { - getPagePromise = this.pageProvider.getPageData(this.courseId, this.module.id); - } else { - getPagePromise = this.courseProvider.getModule(this.module.id, this.courseId); - } - - promises.push(getPagePromise.then((page) => { - if (page) { - this.description = page.intro || page.description; - this.dataRetrieved.emit(page); - - if (this.canGetPage) { - this.page = page; - - // Check if description and timemodified should be displayed. - if (this.page.displayoptions) { - const options = this.textUtils.unserialize(this.page.displayoptions) || {}; - this.displayDescription = typeof options.printintro == 'undefined' || - this.utils.isTrueOrOne(options.printintro); - this.displayTimemodified = typeof options.printlastmodified == 'undefined' || - this.utils.isTrueOrOne(options.printlastmodified); - } else { - this.displayDescription = true; - this.displayTimemodified = true; - } - } - } - }).catch(() => { - // Ignore errors. - })); - - // Get the page HTML. - promises.push(this.pageHelper.getPageHtml(this.module.contents, this.module.id).then((content) => { - - this.contents = content; - this.warning = downloadResult.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error) : ''; - })); - - return Promise.all(promises); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } -} diff --git a/src/addon/mod/page/lang/en.json b/src/addon/mod/page/lang/en.json deleted file mode 100644 index 34bd9817d..000000000 --- a/src/addon/mod/page/lang/en.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "errorwhileloadingthepage": "Error while loading the page content.", - "modulenameplural": "Pages" -} \ No newline at end of file diff --git a/src/addon/mod/page/page.module.ts b/src/addon/mod/page/page.module.ts deleted file mode 100644 index fc357fec1..000000000 --- a/src/addon/mod/page/page.module.ts +++ /dev/null @@ -1,64 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModPageComponentsModule } from './components/components.module'; -import { AddonModPageModuleHandler } from './providers/module-handler'; -import { AddonModPageProvider } from './providers/page'; -import { AddonModPagePrefetchHandler } from './providers/prefetch-handler'; -import { AddonModPageLinkHandler } from './providers/link-handler'; -import { AddonModPageListLinkHandler } from './providers/list-link-handler'; -import { AddonModPagePluginFileHandler } from './providers/pluginfile-handler'; -import { AddonModPageHelperProvider } from './providers/helper'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -// List of providers (without handlers). -export const ADDON_MOD_PAGE_PROVIDERS: any[] = [ - AddonModPageProvider, - AddonModPageHelperProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModPageComponentsModule - ], - providers: [ - AddonModPageProvider, - AddonModPageHelperProvider, - AddonModPageModuleHandler, - AddonModPagePrefetchHandler, - AddonModPageLinkHandler, - AddonModPageListLinkHandler, - AddonModPagePluginFileHandler - ] -}) -export class AddonModPageModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModPageModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModPagePrefetchHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModPageLinkHandler, - pluginfileDelegate: CorePluginFileDelegate, pluginfileHandler: AddonModPagePluginFileHandler, - listLinkHandler: AddonModPageListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - pluginfileDelegate.registerHandler(pluginfileHandler); - } -} diff --git a/src/addon/mod/page/pages/index/index.html b/src/addon/mod/page/pages/index/index.html deleted file mode 100644 index 65f10989b..000000000 --- a/src/addon/mod/page/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/page/pages/index/index.module.ts b/src/addon/mod/page/pages/index/index.module.ts deleted file mode 100644 index 7cb2aea16..000000000 --- a/src/addon/mod/page/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModPageComponentsModule } from '../../components/components.module'; -import { AddonModPageIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModPageIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModPageComponentsModule, - IonicPageModule.forChild(AddonModPageIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModPageIndexPageModule {} diff --git a/src/addon/mod/page/pages/index/index.ts b/src/addon/mod/page/pages/index/index.ts deleted file mode 100644 index 7de352e1b..000000000 --- a/src/addon/mod/page/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModPageIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a page. - */ -@IonicPage({ segment: 'addon-mod-page-index' }) -@Component({ - selector: 'page-addon-mod-page-index', - templateUrl: 'index.html', -}) -export class AddonModPageIndexPage { - @ViewChild(AddonModPageIndexComponent) pageComponent: AddonModPageIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the page instance. - * - * @param page Page instance. - */ - updateData(page: any): void { - this.title = page.name || this.title; - } -} diff --git a/src/addon/mod/page/providers/helper.ts b/src/addon/mod/page/providers/helper.ts deleted file mode 100644 index d95ff5a3b..000000000 --- a/src/addon/mod/page/providers/helper.ts +++ /dev/null @@ -1,109 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { AddonModPageProvider } from './page'; -import { CoreFileProvider } from '@providers/file'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreWSProvider } from '@providers/ws'; - -/** - * Service that provides some features for page. - */ -@Injectable() -export class AddonModPageHelperProvider { - - protected logger; - - constructor(logger: CoreLoggerProvider, - protected domUtils: CoreDomUtilsProvider, - protected filepoolProvider: CoreFilepoolProvider, - protected fileProvider: CoreFileProvider, - protected textUtils: CoreTextUtilsProvider, - protected wsProvider: CoreWSProvider, - protected sitesProvider: CoreSitesProvider) { - this.logger = logger.getInstance('AddonModPageHelperProvider'); - } - - /** - * Gets the page HTML. - * - * @param contents The module contents. - * @param moduleId The module ID. - * @return The HTML of the page. - */ - getPageHtml(contents: any, moduleId: number): Promise { - let indexUrl, - promise; - const paths = {}; - - // Extract the information about paths from the module contents. - contents.forEach((content) => { - const url = content.fileurl; - - if (this.isMainPage(content)) { - // This seems to be the most reliable way to spot the index page. - indexUrl = url; - } else { - let key = content.filename; - if (content.filepath !== '/') { - // Add the folders without the leading slash. - key = content.filepath.substr(1) + key; - } - paths[this.textUtils.decodeURIComponent(key)] = url; - } - }); - - // Promise handling when we are in a browser. - if (!indexUrl) { - // If ever that happens. - this.logger.debug('Could not locate the index page'); - promise = Promise.reject(null); - } else if (this.fileProvider.isAvailable()) { - // The file system is available. - promise = this.filepoolProvider.downloadUrl(this.sitesProvider.getCurrentSiteId(), indexUrl, false, - AddonModPageProvider.COMPONENT, moduleId); - } else { - // We return the live URL. - promise = this.sitesProvider.getCurrentSite().checkAndFixPluginfileURL(indexUrl); - } - - return promise.then(async (url) => { - const content = await this.wsProvider.getText(url); - - // Now that we have the content, we update the SRC to point back to the external resource. - // That will be caught by core-format-text. - return this.domUtils.restoreSourcesInHtml(content, paths); - }); - } - - /** - * Returns whether the file is the main page of the module. - * - * @param file An object returned from WS containing file info. - * @return Whether the file is the main page or not. - */ - protected isMainPage(file: any): boolean { - const filename = file.filename || '', - fileurl = file.fileurl || '', - url = '/mod_page/content/index.html', - encodedUrl = encodeURIComponent(url); - - return (filename === 'index.html' && (fileurl.indexOf(url) > 0 || fileurl.indexOf(encodedUrl) > 0 )); - } -} diff --git a/src/addon/mod/page/providers/link-handler.ts b/src/addon/mod/page/providers/link-handler.ts deleted file mode 100644 index 6374fb2d1..000000000 --- a/src/addon/mod/page/providers/link-handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModPageProvider } from './page'; - -/** - * Handler to treat links to resource. - */ -@Injectable() -export class AddonModPageLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModPageLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, - protected pageProvider: AddonModPageProvider) { - super(courseHelper, 'AddonModPage', 'page', 'p'); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.pageProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/page/providers/list-link-handler.ts b/src/addon/mod/page/providers/list-link-handler.ts deleted file mode 100644 index 0fcd97384..000000000 --- a/src/addon/mod/page/providers/list-link-handler.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModPageProvider } from './page'; - -/** - * Handler to treat links to page list page. - */ -@Injectable() -export class AddonModPageListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModPageListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected pageProvider: AddonModPageProvider) { - super(linkHelper, translate, 'AddonModPage', 'page'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.pageProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/page/providers/module-handler.ts b/src/addon/mod/page/providers/module-handler.ts deleted file mode 100644 index 39aff31f3..000000000 --- a/src/addon/mod/page/providers/module-handler.ts +++ /dev/null @@ -1,89 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModPageProvider } from './page'; -import { AddonModPageIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support page modules. - */ -@Injectable() -export class AddonModPageModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModPage'; - modName = 'page'; - - supportedFeatures = { - [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider, protected pageProvider: AddonModPageProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.pageProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_page-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModPageIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModPageIndexComponent; - } -} diff --git a/src/addon/mod/page/providers/page.ts b/src/addon/mod/page/providers/page.ts deleted file mode 100644 index 2fab4bcce..000000000 --- a/src/addon/mod/page/providers/page.ts +++ /dev/null @@ -1,203 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; - -/** - * Service that provides some features for page. - */ -@Injectable() -export class AddonModPageProvider { - static COMPONENT = 'mmaModPage'; - - protected ROOT_CACHE_KEY = 'mmaModPage:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, - private utils: CoreUtilsProvider, private filepoolProvider: CoreFilepoolProvider, - private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModPageProvider'); - } - - /** - * Get a page by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the page is retrieved. - */ - getPageData(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getPageByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a page. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the page is retrieved. - */ - protected getPageByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getPageCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModPageProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_page_get_pages_by_courses', params, preSets) - .then((response: AddonModPageGetPagesByCoursesResult): any => { - - if (response && response.pages) { - const currentPage = response.pages.find((page) => { - return page[key] == value; - }); - if (currentPage) { - return currentPage; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for page data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getPageCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'page:' + courseId; - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - const promises = []; - - promises.push(this.invalidatePageData(courseId, siteId)); - promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModPageProvider.COMPONENT, moduleId)); - promises.push(this.courseProvider.invalidateModule(moduleId, siteId)); - - return this.utils.allPromises(promises); - } - - /** - * Invalidates page data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidatePageData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getPageCacheKey(courseId)); - }); - } - - /** - * Returns whether or not getPage WS available or not. - * - * @return If WS is avalaible. - * @since 3.3 - */ - isGetPageWSAvailable(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('mod_page_get_pages_by_courses'); - } - - /** - * Return whether or not the plugin is enabled. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.canDownloadFiles(); - }); - } - - /** - * Report a page as being viewed. - * - * @param id Module ID. - * @param name Name of the page. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - pageid: id - }; - - return this.logHelper.logSingle('mod_page_view_page', params, AddonModPageProvider.COMPONENT, id, name, 'page', {}, siteId); - } -} - -/** - * Page returned by mod_page_get_pages_by_courses. - */ -export type AddonModPagePage = { - id: number; // Module id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Page name. - intro: string; // Summary. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles: CoreWSExternalFile[]; - content: string; // Page content. - contentformat: number; // Content format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - contentfiles: CoreWSExternalFile[]; - legacyfiles: number; // Legacy files flag. - legacyfileslast: number; // Legacy files last control flag. - display: number; // How to display the page. - displayoptions: string; // Display options (width, height). - revision: number; // Incremented when after each file changes, to avoid cache. - timemodified: number; // Last time the page was modified. - section: number; // Course section id. - visible: number; // Module visibility. - groupmode: number; // Group mode. - groupingid: number; // Grouping id. -}; - -/** - * Result of WS mod_page_get_pages_by_courses. - */ -export type AddonModPageGetPagesByCoursesResult = { - pages: AddonModPagePage[]; - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/page/providers/pluginfile-handler.ts b/src/addon/mod/page/providers/pluginfile-handler.ts deleted file mode 100644 index 1c93e3e21..000000000 --- a/src/addon/mod/page/providers/pluginfile-handler.ts +++ /dev/null @@ -1,59 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CorePluginFileHandler } from '@providers/plugin-file-delegate'; - -/** - * Handler to treat links to page. - */ -@Injectable() -export class AddonModPagePluginFileHandler implements CorePluginFileHandler { - name = 'AddonModPagePluginFileHandler'; - component = 'mod_page'; - - /** - * Return the RegExp to match the revision on pluginfile URLs. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return RegExp to match the revision on pluginfile URLs. - */ - getComponentRevisionRegExp(args: string[]): RegExp { - // Check filearea. - if (args[2] == 'content') { - // Component + Filearea + Revision - return new RegExp('/mod_page/content/([0-9]+)/'); - } - } - - /** - * Should return the string to remove the revision on pluginfile url. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return String to remove the revision on pluginfile url. - */ - getComponentRevisionReplace(args: string[]): string { - // Component + Filearea + Revision - return '/mod_page/content/0/'; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/page/providers/prefetch-handler.ts b/src/addon/mod/page/providers/prefetch-handler.ts deleted file mode 100644 index 5f72b882f..000000000 --- a/src/addon/mod/page/providers/prefetch-handler.ts +++ /dev/null @@ -1,104 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; -import { AddonModPageProvider } from './page'; -import { AddonModPageHelperProvider } from './helper'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch pages. - */ -@Injectable() -export class AddonModPagePrefetchHandler extends CoreCourseResourcePrefetchHandlerBase { - name = 'AddonModPage'; - modName = 'page'; - component = AddonModPageProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$/; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected pageProvider: AddonModPageProvider, - protected pageHelper: AddonModPageHelperProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Download or prefetch the content. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param prefetch True to prefetch, false to download right away. - * @param dirPath Path of the directory where to store all the content files. This is to keep the files - * relative paths and make the package work in an iframe. Undefined to download the files - * in the filepool root page. - * @return Promise resolved when all content is downloaded. Data returned is not reliable. - */ - downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise { - const promises = []; - - promises.push(super.downloadOrPrefetch(module, courseId, prefetch)); - - if (this.pageProvider.isGetPageWSAvailable()) { - promises.push(this.pageProvider.getPageData(courseId, module.id)); - } - - return Promise.all(promises); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.pageProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const promises = []; - - promises.push(this.pageProvider.invalidatePageData(courseId)); - promises.push(this.courseProvider.invalidateModule(module.id)); - - return this.utils.allPromises(promises); - } -} diff --git a/src/addon/mod/quiz/accessrules/delaybetweenattempts/delaybetweenattempts.module.ts b/src/addon/mod/quiz/accessrules/delaybetweenattempts/delaybetweenattempts.module.ts deleted file mode 100644 index 6f408fc11..000000000 --- a/src/addon/mod/quiz/accessrules/delaybetweenattempts/delaybetweenattempts.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModQuizAccessDelayBetweenAttemptsHandler } from './providers/handler'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonModQuizAccessDelayBetweenAttemptsHandler - ] -}) -export class AddonModQuizAccessDelayBetweenAttemptsModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessDelayBetweenAttemptsHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/accessrules/delaybetweenattempts/providers/handler.ts b/src/addon/mod/quiz/accessrules/delaybetweenattempts/providers/handler.ts deleted file mode 100644 index c605d358c..000000000 --- a/src/addon/mod/quiz/accessrules/delaybetweenattempts/providers/handler.ts +++ /dev/null @@ -1,52 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; - -/** - * Handler to support delay between attempts access rule. - */ -@Injectable() -export class AddonModQuizAccessDelayBetweenAttemptsHandler implements AddonModQuizAccessRuleHandler { - name = 'AddonModQuizAccessDelayBetweenAttempts'; - ruleName = 'quizaccess_delaybetweenattempts'; - - constructor() { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - return false; - } -} diff --git a/src/addon/mod/quiz/accessrules/ipaddress/ipaddress.module.ts b/src/addon/mod/quiz/accessrules/ipaddress/ipaddress.module.ts deleted file mode 100644 index 3b58baf47..000000000 --- a/src/addon/mod/quiz/accessrules/ipaddress/ipaddress.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModQuizAccessIpAddressHandler } from './providers/handler'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonModQuizAccessIpAddressHandler - ] -}) -export class AddonModQuizAccessIpAddressModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessIpAddressHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/accessrules/ipaddress/providers/handler.ts b/src/addon/mod/quiz/accessrules/ipaddress/providers/handler.ts deleted file mode 100644 index 6f763e4e7..000000000 --- a/src/addon/mod/quiz/accessrules/ipaddress/providers/handler.ts +++ /dev/null @@ -1,52 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; - -/** - * Handler to support IP address access rule. - */ -@Injectable() -export class AddonModQuizAccessIpAddressHandler implements AddonModQuizAccessRuleHandler { - name = 'AddonModQuizAccessIpAddress'; - ruleName = 'quizaccess_ipaddress'; - - constructor() { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - return false; - } -} diff --git a/src/addon/mod/quiz/accessrules/numattempts/numattempts.module.ts b/src/addon/mod/quiz/accessrules/numattempts/numattempts.module.ts deleted file mode 100644 index 9be84f679..000000000 --- a/src/addon/mod/quiz/accessrules/numattempts/numattempts.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModQuizAccessNumAttemptsHandler } from './providers/handler'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonModQuizAccessNumAttemptsHandler - ] -}) -export class AddonModQuizAccessNumAttemptsModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessNumAttemptsHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/accessrules/numattempts/providers/handler.ts b/src/addon/mod/quiz/accessrules/numattempts/providers/handler.ts deleted file mode 100644 index 00da772ed..000000000 --- a/src/addon/mod/quiz/accessrules/numattempts/providers/handler.ts +++ /dev/null @@ -1,52 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; - -/** - * Handler to support num attempts access rule. - */ -@Injectable() -export class AddonModQuizAccessNumAttemptsHandler implements AddonModQuizAccessRuleHandler { - name = 'AddonModQuizAccessNumAttempts'; - ruleName = 'quizaccess_numattempts'; - - constructor() { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - return false; - } -} diff --git a/src/addon/mod/quiz/accessrules/offlineattempts/component/addon-mod-quiz-access-offline-attempts.html b/src/addon/mod/quiz/accessrules/offlineattempts/component/addon-mod-quiz-access-offline-attempts.html deleted file mode 100644 index fad58a104..000000000 --- a/src/addon/mod/quiz/accessrules/offlineattempts/component/addon-mod-quiz-access-offline-attempts.html +++ /dev/null @@ -1,4 +0,0 @@ - -

{{ 'core.settings.synchronization' | translate }}

-

{{ 'addon.mod_quiz.confirmcontinueoffline' | translate:{$a: quiz.syncTimeReadable} }}

-
diff --git a/src/addon/mod/quiz/accessrules/offlineattempts/component/offlineattempts.ts b/src/addon/mod/quiz/accessrules/offlineattempts/component/offlineattempts.ts deleted file mode 100644 index 6c26cda9e..000000000 --- a/src/addon/mod/quiz/accessrules/offlineattempts/component/offlineattempts.ts +++ /dev/null @@ -1,43 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Input } from '@angular/core'; -import { FormGroup, FormBuilder } from '@angular/forms'; - -/** - * Component to render the preflight for offline attempts. - */ -@Component({ - selector: 'addon-mod-quiz-access-offline-attempts', - templateUrl: 'addon-mod-quiz-access-offline-attempts.html' -}) -export class AddonModQuizAccessOfflineAttemptsComponent implements OnInit { - - @Input() rule: string; // The name of the rule. - @Input() quiz: any; // The quiz the rule belongs to. - @Input() attempt: any; // The attempt being started/continued. - @Input() prefetch: boolean; // Whether the user is prefetching the quiz. - @Input() siteId: string; // Site ID. - @Input() form: FormGroup; // Form where to add the form control. - - constructor(private fb: FormBuilder) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Always set confirmdatasaved to 1. Sending the data means the user accepted. - this.form.addControl('confirmdatasaved', this.fb.control(1)); - } -} diff --git a/src/addon/mod/quiz/accessrules/offlineattempts/offlineattempts.module.ts b/src/addon/mod/quiz/accessrules/offlineattempts/offlineattempts.module.ts deleted file mode 100644 index 0c94830b2..000000000 --- a/src/addon/mod/quiz/accessrules/offlineattempts/offlineattempts.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModQuizAccessOfflineAttemptsHandler } from './providers/handler'; -import { AddonModQuizAccessOfflineAttemptsComponent } from './component/offlineattempts'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - AddonModQuizAccessOfflineAttemptsComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - ], - providers: [ - AddonModQuizAccessOfflineAttemptsHandler - ], - exports: [ - AddonModQuizAccessOfflineAttemptsComponent - ], - entryComponents: [ - AddonModQuizAccessOfflineAttemptsComponent - ] -}) -export class AddonModQuizAccessOfflineAttemptsModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessOfflineAttemptsHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/accessrules/offlineattempts/providers/handler.ts b/src/addon/mod/quiz/accessrules/offlineattempts/providers/handler.ts deleted file mode 100644 index ff8a0c6c3..000000000 --- a/src/addon/mod/quiz/accessrules/offlineattempts/providers/handler.ts +++ /dev/null @@ -1,91 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; -import { AddonModQuizSyncProvider } from '../../../providers/quiz-sync'; -import { AddonModQuizAccessOfflineAttemptsComponent } from '../component/offlineattempts'; - -/** - * Handler to support offline attempts access rule. - */ -@Injectable() -export class AddonModQuizAccessOfflineAttemptsHandler implements AddonModQuizAccessRuleHandler { - name = 'AddonModQuizAccessOfflineAttempts'; - ruleName = 'quizaccess_offlineattempts'; - - constructor(protected quizSync: AddonModQuizSyncProvider) { - // Nothing to do. - } - - /** - * Add preflight data that doesn't require user interaction. The data should be added to the preflightData param. - * - * @param quiz The quiz the rule belongs to. - * @param preflightData Object where to add the preflight data. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done if async, void if it's synchronous. - */ - getFixedPreflightData(quiz: any, preflightData: any, attempt?: any, prefetch?: boolean, siteId?: string): void | Promise { - preflightData.confirmdatasaved = 1; - } - - /** - * Return the Component to use to display the access rule preflight. - * Implement this if your access rule requires a preflight check with user interaction. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getPreflightComponent(injector: Injector): any | Promise { - return AddonModQuizAccessOfflineAttemptsComponent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - if (prefetch) { - // Don't show the warning if the user is prefetching. - return false; - } - - if (!attempt) { - // User is starting a new attempt, show the warning. - return true; - } - - // Show warning if last sync was a while ago. - return Date.now() - this.quizSync.syncInterval > quiz.syncTime; - } -} diff --git a/src/addon/mod/quiz/accessrules/openclosedate/openclosedate.module.ts b/src/addon/mod/quiz/accessrules/openclosedate/openclosedate.module.ts deleted file mode 100644 index 7e944dffd..000000000 --- a/src/addon/mod/quiz/accessrules/openclosedate/openclosedate.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModQuizAccessOpenCloseDateHandler } from './providers/handler'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonModQuizAccessOpenCloseDateHandler - ] -}) -export class AddonModQuizAccessOpenCloseDateModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessOpenCloseDateHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/accessrules/openclosedate/providers/handler.ts b/src/addon/mod/quiz/accessrules/openclosedate/providers/handler.ts deleted file mode 100644 index a7c1ca4f2..000000000 --- a/src/addon/mod/quiz/accessrules/openclosedate/providers/handler.ts +++ /dev/null @@ -1,75 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; -import { AddonModQuizProvider } from '../../../providers/quiz'; - -/** - * Handler to support open/close date access rule. - */ -@Injectable() -export class AddonModQuizAccessOpenCloseDateHandler implements AddonModQuizAccessRuleHandler { - name = 'AddonModQuizAccessOpenCloseDate'; - ruleName = 'quizaccess_openclosedate'; - - constructor() { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - return false; - } - - /** - * Whether or not the time left of an attempt should be displayed. - * - * @param attempt The attempt. - * @param endTime The attempt end time (in seconds). - * @param timeNow The current time in seconds. - * @return Whether it should be displayed. - */ - shouldShowTimeLeft(attempt: any, endTime: number, timeNow: number): boolean { - // If this is a teacher preview after the close date, do not show the time. - if (attempt.preview && timeNow > endTime) { - return false; - } - - // Show the time left only if it's less than QUIZ_SHOW_TIME_BEFORE_DEADLINE. - if (timeNow > endTime - AddonModQuizProvider.QUIZ_SHOW_TIME_BEFORE_DEADLINE) { - return true; - } - - return false; - } -} diff --git a/src/addon/mod/quiz/accessrules/password/component/addon-mod-quiz-access-password.html b/src/addon/mod/quiz/accessrules/password/component/addon-mod-quiz-access-password.html deleted file mode 100644 index c32ae2776..000000000 --- a/src/addon/mod/quiz/accessrules/password/component/addon-mod-quiz-access-password.html +++ /dev/null @@ -1,9 +0,0 @@ - -

{{ 'addon.mod_quiz.quizpassword' | translate }}

-

{{ 'addon.mod_quiz.requirepasswordmessage' | translate}}

-
- - - - - diff --git a/src/addon/mod/quiz/accessrules/password/component/password.ts b/src/addon/mod/quiz/accessrules/password/component/password.ts deleted file mode 100644 index da3a959b8..000000000 --- a/src/addon/mod/quiz/accessrules/password/component/password.ts +++ /dev/null @@ -1,43 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Input } from '@angular/core'; -import { FormGroup, FormBuilder } from '@angular/forms'; - -/** - * Component to render the preflight for password. - */ -@Component({ - selector: 'addon-mod-quiz-access-password', - templateUrl: 'addon-mod-quiz-access-password.html' -}) -export class AddonModQuizAccessPasswordComponent implements OnInit { - - @Input() rule: string; // The name of the rule. - @Input() quiz: any; // The quiz the rule belongs to. - @Input() attempt: any; // The attempt being started/continued. - @Input() prefetch: boolean; // Whether the user is prefetching the quiz. - @Input() siteId: string; // Site ID. - @Input() form: FormGroup; // Form where to add the form control. - - constructor(private fb: FormBuilder) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Add the control for the password. - this.form.addControl('quizpassword', this.fb.control('')); - } -} diff --git a/src/addon/mod/quiz/accessrules/password/password.module.ts b/src/addon/mod/quiz/accessrules/password/password.module.ts deleted file mode 100644 index 941af9fed..000000000 --- a/src/addon/mod/quiz/accessrules/password/password.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { AddonModQuizAccessPasswordHandler } from './providers/handler'; -import { AddonModQuizAccessPasswordComponent } from './component/password'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - AddonModQuizAccessPasswordComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule - ], - providers: [ - AddonModQuizAccessPasswordHandler - ], - exports: [ - AddonModQuizAccessPasswordComponent - ], - entryComponents: [ - AddonModQuizAccessPasswordComponent - ] -}) -export class AddonModQuizAccessPasswordModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessPasswordHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/accessrules/password/providers/handler.ts b/src/addon/mod/quiz/accessrules/password/providers/handler.ts deleted file mode 100644 index 9195f5603..000000000 --- a/src/addon/mod/quiz/accessrules/password/providers/handler.ts +++ /dev/null @@ -1,207 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; -import { AddonModQuizAccessPasswordComponent } from '../component/password'; - -/** - * Handler to support password access rule. - */ -@Injectable() -export class AddonModQuizAccessPasswordHandler implements AddonModQuizAccessRuleHandler { - // Variables for database. - static PASSWORD_TABLE = 'addon_mod_quiz_access_password'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModQuizAccessPasswordHandler', - version: 1, - tables: [ - { - name: AddonModQuizAccessPasswordHandler.PASSWORD_TABLE, - columns: [ - { - name: 'id', - type: 'INTEGER', - primaryKey: true - }, - { - name: 'password', - type: 'TEXT' - }, - { - name: 'timemodified', - type: 'INTEGER' - } - ] - } - ] - }; - - name = 'AddonModQuizAccessPassword'; - ruleName = 'quizaccess_password'; - - constructor(private sitesProvider: CoreSitesProvider) { - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Add preflight data that doesn't require user interaction. The data should be added to the preflightData param. - * - * @param quiz The quiz the rule belongs to. - * @param preflightData Object where to add the preflight data. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done if async, void if it's synchronous. - */ - getFixedPreflightData(quiz: any, preflightData: any, attempt?: any, prefetch?: boolean, siteId?: string): void | Promise { - if (quiz && quiz.id && typeof preflightData.quizpassword == 'undefined') { - // Try to get a password stored. If it's found, use it. - return this.getPasswordEntry(quiz.id, siteId).then((entry) => { - preflightData.quizpassword = entry.password; - }).catch(() => { - // Don't reject. - }); - } - } - - /** - * Get a password stored in DB. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the DB entry on success. - */ - protected getPasswordEntry(quizId: number, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecord(AddonModQuizAccessPasswordHandler.PASSWORD_TABLE, {id: quizId}); - }); - } - - /** - * Return the Component to use to display the access rule preflight. - * Implement this if your access rule requires a preflight check with user interaction. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getPreflightComponent(injector: Injector): any | Promise { - return AddonModQuizAccessPasswordComponent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - // If there's a password stored don't require the preflight since we'll use the stored one. - return this.getPasswordEntry(quiz.id, siteId).then(() => { - return false; - }).catch(() => { - // Not stored. - return true; - }); - } - - /** - * Function called when the preflight check has passed. This is a chance to record that fact in some way. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. - * @param preflightData Preflight data gathered. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done if async, void if it's synchronous. - */ - notifyPreflightCheckPassed(quiz: any, attempt: any, preflightData: any, prefetch?: boolean, siteId?: string) - : void | Promise { - - // The password is right, store it to use it automatically in following executions. - if (quiz && quiz.id && typeof preflightData.quizpassword != 'undefined') { - return this.storePassword(quiz.id, preflightData.quizpassword, siteId); - } - } - - /** - * Function called when the preflight check fails. This is a chance to record that fact in some way. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. - * @param preflightData Preflight data gathered. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done if async, void if it's synchronous. - */ - notifyPreflightCheckFailed?(quiz: any, attempt: any, preflightData: any, prefetch?: boolean, siteId?: string) - : void | Promise { - - // The password is wrong, remove it from DB if it's there. - if (quiz && quiz.id) { - return this.removePassword(quiz.id, siteId); - } - } - - /** - * Remove a password from DB. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - protected removePassword(quizId: number, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModQuizAccessPasswordHandler.PASSWORD_TABLE, {id: quizId}); - }); - } - - /** - * Store a password in DB. - * - * @param quizId Quiz ID. - * @param password Password. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - protected storePassword(quizId: number, password: string, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { - id: quizId, - password: password, - timemodified: Date.now() - }; - - return site.getDb().insertRecord(AddonModQuizAccessPasswordHandler.PASSWORD_TABLE, entry); - }); - } -} diff --git a/src/addon/mod/quiz/accessrules/safebrowser/providers/handler.ts b/src/addon/mod/quiz/accessrules/safebrowser/providers/handler.ts deleted file mode 100644 index 6f50fa179..000000000 --- a/src/addon/mod/quiz/accessrules/safebrowser/providers/handler.ts +++ /dev/null @@ -1,52 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; - -/** - * Handler to support safe address access rule. - */ -@Injectable() -export class AddonModQuizAccessSafeBrowserHandler implements AddonModQuizAccessRuleHandler { - name = 'AddonModQuizAccessSafeBrowser'; - ruleName = 'quizaccess_safebrowser'; - - constructor() { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - return false; - } -} diff --git a/src/addon/mod/quiz/accessrules/safebrowser/safebrowser.module.ts b/src/addon/mod/quiz/accessrules/safebrowser/safebrowser.module.ts deleted file mode 100644 index 9552bc5bd..000000000 --- a/src/addon/mod/quiz/accessrules/safebrowser/safebrowser.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModQuizAccessSafeBrowserHandler } from './providers/handler'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonModQuizAccessSafeBrowserHandler - ] -}) -export class AddonModQuizAccessSafeBrowserModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessSafeBrowserHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/accessrules/securewindow/providers/handler.ts b/src/addon/mod/quiz/accessrules/securewindow/providers/handler.ts deleted file mode 100644 index 1dae317e0..000000000 --- a/src/addon/mod/quiz/accessrules/securewindow/providers/handler.ts +++ /dev/null @@ -1,52 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; - -/** - * Handler to support secure window access rule. - */ -@Injectable() -export class AddonModQuizAccessSecureWindowHandler implements AddonModQuizAccessRuleHandler { - name = 'AddonModQuizAccessSecureWindow'; - ruleName = 'quizaccess_securewindow'; - - constructor() { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - return false; - } -} diff --git a/src/addon/mod/quiz/accessrules/securewindow/securewindow.module.ts b/src/addon/mod/quiz/accessrules/securewindow/securewindow.module.ts deleted file mode 100644 index 08617090d..000000000 --- a/src/addon/mod/quiz/accessrules/securewindow/securewindow.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModQuizAccessSecureWindowHandler } from './providers/handler'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonModQuizAccessSecureWindowHandler - ] -}) -export class AddonModQuizAccessSecureWindowModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessSecureWindowHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/accessrules/timelimit/component/addon-mod-quiz-access-time-limit.html b/src/addon/mod/quiz/accessrules/timelimit/component/addon-mod-quiz-access-time-limit.html deleted file mode 100644 index 7535b16b7..000000000 --- a/src/addon/mod/quiz/accessrules/timelimit/component/addon-mod-quiz-access-time-limit.html +++ /dev/null @@ -1,4 +0,0 @@ - -

{{ 'addon.mod_quiz.confirmstartheader' | translate }}

-

{{ 'addon.mod_quiz.confirmstart' | translate:{$a: quiz.readableTimeLimit} }}

-
diff --git a/src/addon/mod/quiz/accessrules/timelimit/component/timelimit.ts b/src/addon/mod/quiz/accessrules/timelimit/component/timelimit.ts deleted file mode 100644 index 7b3764a78..000000000 --- a/src/addon/mod/quiz/accessrules/timelimit/component/timelimit.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input } from '@angular/core'; -import { FormGroup } from '@angular/forms'; - -/** - * Component to render the preflight for time limit. - */ -@Component({ - selector: 'addon-mod-quiz-access-time-limit', - templateUrl: 'addon-mod-quiz-access-time-limit.html' -}) -export class AddonModQuizAccessTimeLimitComponent { - - @Input() rule: string; // The name of the rule. - @Input() quiz: any; // The quiz the rule belongs to. - @Input() attempt: any; // The attempt being started/continued. - @Input() prefetch: boolean; // Whether the user is prefetching the quiz. - @Input() siteId: string; // Site ID. - @Input() form: FormGroup; // Form where to add the form control. - - constructor() { - // Nothing to do, we don't need to send anything for time limit. - } -} diff --git a/src/addon/mod/quiz/accessrules/timelimit/providers/handler.ts b/src/addon/mod/quiz/accessrules/timelimit/providers/handler.ts deleted file mode 100644 index aaf3d2d32..000000000 --- a/src/addon/mod/quiz/accessrules/timelimit/providers/handler.ts +++ /dev/null @@ -1,79 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { AddonModQuizAccessRuleHandler } from '../../../providers/access-rules-delegate'; -import { AddonModQuizAccessTimeLimitComponent } from '../component/timelimit'; - -/** - * Handler to support time limit access rule. - */ -@Injectable() -export class AddonModQuizAccessTimeLimitHandler implements AddonModQuizAccessRuleHandler { - name = 'AddonModQuizAccessTimeLimit'; - ruleName = 'quizaccess_timelimit'; - - constructor() { - // Nothing to do. - } - - /** - * Return the Component to use to display the access rule preflight. - * Implement this if your access rule requires a preflight check with user interaction. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getPreflightComponent(injector: Injector): any | Promise { - return AddonModQuizAccessTimeLimitComponent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise { - // Warning only required if the attempt is not already started. - return !attempt; - } - - /** - * Whether or not the time left of an attempt should be displayed. - * - * @param attempt The attempt. - * @param endTime The attempt end time (in seconds). - * @param timeNow The current time in seconds. - * @return Whether it should be displayed. - */ - shouldShowTimeLeft(attempt: any, endTime: number, timeNow: number): boolean { - // If this is a teacher preview after the time limit expires, don't show the time left. - return !(attempt.preview && timeNow > endTime); - } -} diff --git a/src/addon/mod/quiz/accessrules/timelimit/timelimit.module.ts b/src/addon/mod/quiz/accessrules/timelimit/timelimit.module.ts deleted file mode 100644 index 04324c78b..000000000 --- a/src/addon/mod/quiz/accessrules/timelimit/timelimit.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonModQuizAccessTimeLimitHandler } from './providers/handler'; -import { AddonModQuizAccessTimeLimitComponent } from './component/timelimit'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -@NgModule({ - declarations: [ - AddonModQuizAccessTimeLimitComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - ], - providers: [ - AddonModQuizAccessTimeLimitHandler - ], - exports: [ - AddonModQuizAccessTimeLimitComponent - ], - entryComponents: [ - AddonModQuizAccessTimeLimitComponent - ] -}) -export class AddonModQuizAccessTimeLimitModule { - constructor(accessRuleDelegate: AddonModQuizAccessRuleDelegate, handler: AddonModQuizAccessTimeLimitHandler) { - accessRuleDelegate.registerHandler(handler); - } -} diff --git a/src/addon/mod/quiz/classes/auto-save.ts b/src/addon/mod/quiz/classes/auto-save.ts deleted file mode 100644 index 1c52afaa2..000000000 --- a/src/addon/mod/quiz/classes/auto-save.ts +++ /dev/null @@ -1,227 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { PopoverController, Popover } from 'ionic-angular'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { AddonModQuizProvider } from '../providers/quiz'; -import { AddonModQuizConnectionErrorComponent } from '../components/connection-error/connection-error'; -import { BehaviorSubject } from 'rxjs'; - -/** - * Class to support auto-save in quiz. Every certain seconds, it will check if there are changes in the current page answers - * and, if so, it will save them automatically. - */ -export class AddonModQuizAutoSave { - protected CHECK_CHANGES_INTERVAL = 5000; - - protected logger; - protected checkChangesInterval; // Interval to check if there are changes in the answers. - protected loadPreviousAnswersTimeout; // Timeout to load previous answers. - protected autoSaveTimeout; // Timeout to auto-save the answers. - protected popover: Popover; // Popover to display there's been an error. - protected popoverShown = false; // Whether the popover is shown. - protected previousAnswers: any; // The previous answers. It is used to check if answers have changed. - protected errorObservable: BehaviorSubject; // An observable to notify if there's been an error. - - /** - * Constructor. - * - * @param formName Name of the form where the answers are stored. - * @param buttonSelector Selector to find the button to show the connection error. - * @param loggerProvider CoreLoggerProvider instance. - * @param popoverCtrl PopoverController instance. - * @param questionHelper CoreQuestionHelperProvider instance. - * @param quizProvider AddonModQuizProvider instance. - */ - constructor(protected formName: string, protected buttonSelector: string, loggerProvider: CoreLoggerProvider, - protected popoverCtrl: PopoverController, protected questionHelper: CoreQuestionHelperProvider, - protected quizProvider: AddonModQuizProvider) { - - this.logger = loggerProvider.getInstance('AddonModQuizAutoSave'); - - // Create the popover. - this.popover = this.popoverCtrl.create(AddonModQuizConnectionErrorComponent); - this.popover.onDidDismiss(() => { - this.popoverShown = false; - }); - - // Create the observable to notify if an error happened. - this.errorObservable = new BehaviorSubject(false); - } - - /** - * Cancel a pending auto save. - */ - cancelAutoSave(): void { - clearTimeout(this.autoSaveTimeout); - this.autoSaveTimeout = undefined; - } - - /** - * Check if the answers have changed in a page. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param preflightData Preflight data. - * @param offline Whether the quiz is being attempted in offline mode. - */ - checkChanges(quiz: any, attempt: any, preflightData: any, offline?: boolean): void { - if (this.autoSaveTimeout) { - // We already have an auto save pending, no need to check changes. - return; - } - - const answers = this.getAnswers(); - - if (!this.previousAnswers) { - // Previous answers isn't set, set it now. - this.previousAnswers = answers; - } else { - // Check if answers have changed. - let equal = true; - - for (const name in answers) { - if (this.previousAnswers[name] != answers[name]) { - equal = false; - break; - } - } - - if (!equal) { - this.setAutoSaveTimer(quiz, attempt, preflightData, offline); - } - - this.previousAnswers = answers; - } - } - - /** - * Get answers from a form. - * - * @return Answers. - */ - protected getAnswers(): any { - return this.questionHelper.getAnswersFromForm(document.forms[this.formName]); - } - - /** - * Hide the auto save error. - */ - hideAutoSaveError(): void { - this.errorObservable.next(false); - this.popover.dismiss(); - } - - /** - * Returns an observable that will notify when an error happens or stops. - * It will send true when there's an error, and false when the error has been ammended. - * - * @return Observable. - */ - onError(): BehaviorSubject { - return this.errorObservable; - } - - /** - * Schedule an auto save process if it's not scheduled already. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param preflightData Preflight data. - * @param offline Whether the quiz is being attempted in offline mode. - */ - setAutoSaveTimer(quiz: any, attempt: any, preflightData: any, offline?: boolean): void { - // Don't schedule if already shceduled or quiz is almost closed. - if (quiz.autosaveperiod && !this.autoSaveTimeout && !this.quizProvider.isAttemptTimeNearlyOver(quiz, attempt)) { - - // Schedule save. - this.autoSaveTimeout = setTimeout(() => { - const answers = this.getAnswers(); - this.cancelAutoSave(); - this.previousAnswers = answers; // Update previous answers to match what we're sending to the server. - - this.quizProvider.saveAttempt(quiz, attempt, answers, preflightData, offline).then(() => { - // Save successful, we can hide the connection error if it was shown. - this.hideAutoSaveError(); - }).catch((error) => { - // Error auto-saving. Show error and set timer again. - this.logger.warn('Error auto-saving data.', error); - - // If there was no error already, show the error message. - if (!this.errorObservable.getValue()) { - this.errorObservable.next(true); - this.showAutoSaveError(); - } - - // Try again. - this.setAutoSaveTimer(quiz, attempt, preflightData, offline); - }); - }, quiz.autosaveperiod * 1000); - } - } - - /** - * Show an error popover due to an auto save error. - */ - showAutoSaveError(ev?: Event): void { - // Don't show popover if it was already shown. - if (!this.popoverShown) { - this.popoverShown = true; - - // If no event is provided, simulate it targeting the button. - this.popover.present({ - ev: ev || { target: document.querySelector(this.buttonSelector) } - }); - } - } - - /** - * Start a process to periodically check changes in answers. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param preflightData Preflight data. - * @param offline Whether the quiz is being attempted in offline mode. - */ - startCheckChangesProcess(quiz: any, attempt: any, preflightData: any, offline?: boolean): void { - if (this.checkChangesInterval || !quiz.autosaveperiod) { - // We already have the interval in place or the quiz has autosave disabled. - return; - } - - this.previousAnswers = undefined; - - // Load initial answers in 2.5 seconds so the first check interval finds them already loaded. - this.loadPreviousAnswersTimeout = setTimeout(() => { - this.checkChanges(quiz, attempt, preflightData, offline); - }, 2500); - - // Check changes every certain time. - this.checkChangesInterval = setInterval(() => { - this.checkChanges(quiz, attempt, preflightData, offline); - }, this.CHECK_CHANGES_INTERVAL); - } - - /** - * Stops the periodical check for changes. - */ - stopCheckChangesProcess(): void { - clearTimeout(this.loadPreviousAnswersTimeout); - clearInterval(this.checkChangesInterval); - - this.loadPreviousAnswersTimeout = undefined; - this.checkChangesInterval = undefined; - } -} diff --git a/src/addon/mod/quiz/components/components.module.ts b/src/addon/mod/quiz/components/components.module.ts deleted file mode 100644 index aa3de2838..000000000 --- a/src/addon/mod/quiz/components/components.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModQuizIndexComponent } from './index/index'; -import { AddonModQuizConnectionErrorComponent } from './connection-error/connection-error'; - -@NgModule({ - declarations: [ - AddonModQuizIndexComponent, - AddonModQuizConnectionErrorComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModQuizIndexComponent, - AddonModQuizConnectionErrorComponent - ], - entryComponents: [ - AddonModQuizIndexComponent, - AddonModQuizConnectionErrorComponent - ] -}) -export class AddonModQuizComponentsModule {} diff --git a/src/addon/mod/quiz/components/connection-error/connection-error.scss b/src/addon/mod/quiz/components/connection-error/connection-error.scss deleted file mode 100644 index da4543765..000000000 --- a/src/addon/mod/quiz/components/connection-error/connection-error.scss +++ /dev/null @@ -1,7 +0,0 @@ -ion-app.app-root addon-mod-quiz-connection-error { - background-color: $red-light; - - .item { - background-color: $red-light; - } -} diff --git a/src/addon/mod/quiz/components/connection-error/connection-error.ts b/src/addon/mod/quiz/components/connection-error/connection-error.ts deleted file mode 100644 index cf78619c7..000000000 --- a/src/addon/mod/quiz/components/connection-error/connection-error.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; - -/** - * Component that displays a quiz entry page. - */ -@Component({ - selector: 'addon-mod-quiz-connection-error', - template: '{{ "addon.mod_quiz.connectionerror" | translate }}', -}) -export class AddonModQuizConnectionErrorComponent { - - constructor() { - // Nothing to do. - } -} diff --git a/src/addon/mod/quiz/components/index/addon-mod-quiz-index.html b/src/addon/mod/quiz/components/index/addon-mod-quiz-index.html deleted file mode 100644 index d7586c3ac..000000000 --- a/src/addon/mod/quiz/components/index/addon-mod-quiz-index.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - -

{{ rule }}

-
- -

{{ 'addon.mod_quiz.grademethod' | translate }}

-

{{ quiz.gradeMethodReadable }}

-
- -

{{ 'core.lastsync' | translate }}

-

{{ syncTime }}

-
-
-
- - - - -
-

{{ 'addon.mod_quiz.summaryofattempts' | translate }}

-
-
- - - - - {{ 'addon.mod_quiz.attemptnumber' | translate }} - # - {{ 'addon.mod_quiz.attemptstate' | translate }} - {{ 'addon.mod_quiz.marks' | translate }} / {{ quiz.sumGradesFormatted }} - {{ 'addon.mod_quiz.grade' | translate }} / {{ quiz.gradeFormatted }} - - - - - - {{ 'addon.mod_quiz.preview' | translate }} - {{ attempt.attempt }} - -

{{ sentence }}

-
-

{{ attempt.readableMark }}

-

{{ attempt.readableGrade }}

-
-
-
-
- - - - - {{ gradeResult }} - {{ 'core.course.overriddennotice' | translate }} - -

{{ 'addon.mod_quiz.comment' | translate }}

-

-
- -

{{ 'addon.mod_quiz.overallfeedback' | translate }}

-

-
-
-
- - - - - - -

{{ message }}

-
- -

{{ 'addon.mod_quiz.noquestions' | translate }}

-
- -

{{ 'addon.mod_quiz.errorquestionsnotsupported' | translate }}

-

{{ type }}

-
- -

{{ 'addon.mod_quiz.errorrulesnotsupported' | translate }}

-

{{ name }}

-
- -

{{ 'addon.mod_quiz.errorbehaviournotsupported' | translate }}

-

{{ quiz.preferredbehaviour }}

-
- - -
- - {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} -
- - - -

{{ 'addon.mod_quiz.canattemptbutnotsubmit' | translate }}

-

{{ 'addon.mod_quiz.warningquestionsnotsupported' | translate }}

-

{{ type }}

-
- - - - - - - - - - {{ 'core.openinbrowser' | translate }} - - - - - - - - -
-
-
diff --git a/src/addon/mod/quiz/components/index/index.scss b/src/addon/mod/quiz/components/index/index.scss deleted file mode 100644 index f6f7cb907..000000000 --- a/src/addon/mod/quiz/components/index/index.scss +++ /dev/null @@ -1,57 +0,0 @@ -ion-app.app-root addon-mod-quiz-index { - - .addon-mod_quiz-table { - .addon-mod_quiz-table-header .item-inner { - background-image: none; - font-size: 0.9em; - - .col[text-center] { - @include padding-horizontal(0); - } - } - - .item-inner ion-label { - @include margin(null, 0, null, null); - } - - .item { - @include padding(null, null, null, 0); - } - - .label { - margin-top: 0; - margin-bottom: 0; - } - - .item:nth-child(even) { - background-color: $gray-lighter; - @include darkmode() { - background-color: $core-dark-item-divider-bg-color; - } - } - - .addon-mod_quiz-highlighted, - .item.addon-mod_quiz-highlighted, - .addon-mod_quiz-highlighted p, - .item.addon-mod_quiz-highlighted p { - background-color: $blue-light; - color: $blue-dark; - } - - @include darkmode() { - .addon-mod_quiz-highlighted, - .item.addon-mod_quiz-highlighted, - .addon-mod_quiz-highlighted p, - .item.addon-mod_quiz-highlighted p { - background-color: $blue-dark; - color: $blue-light; - } - - .item.addon-mod_quiz-highlighted.activated, - .item.addon-mod_quiz-highlighted.activated p { - background-color: $blue; - color: $blue-light; - } - } - } -} diff --git a/src/addon/mod/quiz/components/index/index.ts b/src/addon/mod/quiz/components/index/index.ts deleted file mode 100644 index d970c5e88..000000000 --- a/src/addon/mod/quiz/components/index/index.ts +++ /dev/null @@ -1,616 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector } from '@angular/core'; -import { Content, NavController } from 'ionic-angular'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; -import { AddonModQuizProvider } from '../../providers/quiz'; -import { AddonModQuizHelperProvider } from '../../providers/helper'; -import { AddonModQuizOfflineProvider } from '../../providers/quiz-offline'; -import { AddonModQuizSyncProvider } from '../../providers/quiz-sync'; -import { AddonModQuizPrefetchHandler } from '../../providers/prefetch-handler'; -import { CoreConstants } from '@core/constants'; - -/** - * Component that displays a quiz entry page. - */ -@Component({ - selector: 'addon-mod-quiz-index', - templateUrl: 'addon-mod-quiz-index.html', -}) -export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComponent { - component = AddonModQuizProvider.COMPONENT; - moduleName = 'quiz'; - - quiz: any; // The quiz. - now: number; // Current time. - syncTime: string; // Last synchronization time. - hasOffline: boolean; // Whether the quiz has offline data. - hasSupportedQuestions: boolean; // Whether the quiz has at least 1 supported question. - accessRules: string[]; // List of access rules of the quiz. - unsupportedRules: string[]; // List of unsupported access rules of the quiz. - unsupportedQuestions: string[]; // List of unsupported question types of the quiz. - behaviourSupported: boolean; // Whether the quiz behaviour is supported. - showResults: boolean; // Whether to show the result of the quiz (grade, etc.). - gradeOverridden: boolean; // Whether grade has been overridden. - gradebookFeedback: string; // The feedback in the gradebook. - gradeResult: string; // Message with the grade. - overallFeedback: string; // The feedback for the grade. - buttonText: string; // Text to display in the start/continue button. - preventMessages: string[]; // List of messages explaining why the quiz cannot be attempted. - showStatusSpinner = true; // Whether to show a spinner due to quiz status. - - protected fetchContentDefaultError = 'addon.mod_quiz.errorgetquiz'; // Default error to show when loading contents. - protected syncEventName = AddonModQuizSyncProvider.AUTO_SYNCED; - - protected quizData: any; // Quiz instance. This variable will store the quiz instance until it's ready to be shown - protected autoReview: any; // Data to auto-review an attempt. It's used to automatically open the review page after finishing. - protected quizAccessInfo: any; // Quiz access info. - protected attemptAccessInfo: any; // Last attempt access info. - protected attempts: any[]; // List of attempts the user has made. - protected moreAttempts: boolean; // Whether user can create/continue attempts. - protected options: any; // Combined review options. - protected bestGrade: any; // Best grade data. - protected gradebookData: {grade: number, feedback?: string}; // The gradebook grade and feedback. - protected overallStats: boolean; // Equivalent to overallstats in mod_quiz_view_object in Moodle. - protected finishedObserver: any; // It will observe attempt finished events. - protected hasPlayed = false; // Whether the user has gone to the quiz player (attempted). - - constructor(injector: Injector, protected quizProvider: AddonModQuizProvider, @Optional() content: Content, - protected quizHelper: AddonModQuizHelperProvider, protected quizOffline: AddonModQuizOfflineProvider, - protected quizSync: AddonModQuizSyncProvider, protected behaviourDelegate: CoreQuestionBehaviourDelegate, - protected prefetchHandler: AddonModQuizPrefetchHandler, protected navCtrl: NavController) { - super(injector, content); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.loadContent(false, true).then(() => { - if (!this.quizData) { - return; - } - - this.quizProvider.logViewQuiz(this.quizData.id, this.quizData.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch((error) => { - // Ignore errors. - }); - }); - - // Listen for attempt finished events. - this.finishedObserver = this.eventsProvider.on(AddonModQuizProvider.ATTEMPT_FINISHED_EVENT, (data) => { - // Go to review attempt if an attempt in this quiz was finished and synced. - if (this.quizData && data.quizId == this.quizData.id) { - this.autoReview = data; - } - }, this.siteId); - } - - /** - * Attempt the quiz. - */ - attemptQuiz(): void { - if (this.showStatusSpinner) { - // Quiz is being downloaded or synchronized, abort. - return; - } - - if (this.quizProvider.isQuizOffline(this.quizData)) { - // Quiz supports offline, check if it needs to be downloaded. - // If the site doesn't support check updates, always prefetch it because we cannot tell if there's something new. - const isDownloaded = this.currentStatus == CoreConstants.DOWNLOADED; - - if (!isDownloaded || !this.modulePrefetchDelegate.canCheckUpdates()) { - // Prefetch the quiz. - this.showStatusSpinner = true; - - this.prefetchHandler.prefetch(this.module, this.courseId, true).then(() => { - // Success downloading, open quiz. - this.openQuiz(); - }).catch((error) => { - if (this.hasOffline || (isDownloaded && !this.modulePrefetchDelegate.canCheckUpdates())) { - // Error downloading but there is something offline, allow continuing it. - // If the site doesn't support check updates, continue too because we cannot tell if there's something new. - this.openQuiz(); - } else { - this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); - } - - }).finally(() => { - this.showStatusSpinner = false; - }); - } else { - // Already downloaded, open it. - this.openQuiz(); - } - } else { - // Quiz isn't offline, just open it. - this.openQuiz(); - } - } - - /** - * Get the quiz data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - - // First get the quiz instance. - return this.quizProvider.getQuiz(this.courseId, this.module.id).then((quizData) => { - this.quizData = quizData; - this.quizData.gradeMethodReadable = this.quizProvider.getQuizGradeMethod(this.quizData.grademethod); - - this.now = new Date().getTime(); - this.dataRetrieved.emit(this.quizData); - this.description = this.quizData.intro || this.description; - - // Try to get warnings from automatic sync. - return this.quizSync.getSyncWarnings(this.quizData.id).then((warnings) => { - if (warnings && warnings.length) { - // Show warnings and delete them so they aren't shown again. - this.domUtils.showErrorModal(this.textUtils.buildMessage(warnings)); - - return this.quizSync.setSyncWarnings(this.quizData.id, []); - } - }); - }).then(() => { - if (this.quizProvider.isQuizOffline(this.quizData)) { - // Try to sync the quiz. - return this.syncActivity(showErrors).catch(() => { - // Ignore errors, keep getting data even if sync fails. - this.autoReview = undefined; - }); - } else { - this.autoReview = undefined; - this.showStatusSpinner = false; - } - }).then(() => { - - if (this.quizProvider.isQuizOffline(this.quizData)) { - // Handle status. - this.setStatusListener(); - - // Get last synchronization time and check if sync button should be seen. - // No need to return these promises, they should be faster than the rest. - this.quizSync.getReadableSyncTime(this.quizData.id).then((syncTime) => { - this.syncTime = syncTime; - }); - - this.quizSync.hasDataToSync(this.quizData.id).then((hasOffline) => { - this.hasOffline = hasOffline; - }); - } - - // Get quiz access info. - return this.quizProvider.getQuizAccessInformation(this.quizData.id, {cmId: this.module.id}).then((info) => { - this.quizAccessInfo = info; - this.quizData.showReviewColumn = info.canreviewmyattempts; - this.accessRules = info.accessrules; - this.unsupportedRules = this.quizProvider.getUnsupportedRules(info.activerulenames); - - if (this.quizData.preferredbehaviour) { - this.behaviourSupported = this.behaviourDelegate.isBehaviourSupported(this.quizData.preferredbehaviour); - } - - // Get question types in the quiz. - return this.quizProvider.getQuizRequiredQtypes(this.quizData.id, {cmId: this.module.id}).then((types) => { - this.unsupportedQuestions = this.quizProvider.getUnsupportedQuestions(types); - this.hasSupportedQuestions = !!types.find((type) => { - return type != 'random' && this.unsupportedQuestions.indexOf(type) == -1; - }); - - return this.getAttempts(); - }); - }); - - }).then(() => { - // Quiz is ready to be shown, move it to the variable that is displayed. - this.quiz = this.quizData; - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Get the user attempts in the quiz and the result info. - * - * @return Promise resolved when done. - */ - protected getAttempts(): Promise { - - // Get access information of last attempt (it also works if no attempts made). - return this.quizProvider.getAttemptAccessInformation(this.quizData.id, 0, {cmId: this.module.id}).then((info) => { - this.attemptAccessInfo = info; - - // Get attempts. - return this.quizProvider.getUserAttempts(this.quizData.id, {cmId: this.module.id}).then((atts) => { - - return this.treatAttempts(atts).then((atts) => { - this.attempts = atts; - - // Check if user can create/continue attempts. - if (this.attempts.length) { - const last = this.attempts[this.attempts.length - 1]; - this.moreAttempts = !this.quizProvider.isAttemptFinished(last.state) || !this.attemptAccessInfo.isfinished; - } else { - this.moreAttempts = !this.attemptAccessInfo.isfinished; - } - - this.getButtonText(); - - return this.getResultInfo(); - }); - }); - }); - } - - /** - * Get the text to show in the button. It also sets restriction messages if needed. - */ - protected getButtonText(): void { - this.buttonText = ''; - - if (this.quizData.hasquestions !== 0) { - if (this.attempts.length && !this.quizProvider.isAttemptFinished(this.attempts[this.attempts.length - 1].state)) { - // Last attempt is unfinished. - if (this.quizAccessInfo.canattempt) { - this.buttonText = 'addon.mod_quiz.continueattemptquiz'; - } else if (this.quizAccessInfo.canpreview) { - this.buttonText = 'addon.mod_quiz.continuepreview'; - } - - } else { - // Last attempt is finished or no attempts. - if (this.quizAccessInfo.canattempt) { - this.preventMessages = this.attemptAccessInfo.preventnewattemptreasons; - if (!this.preventMessages.length) { - if (!this.attempts.length) { - this.buttonText = 'addon.mod_quiz.attemptquiznow'; - } else { - this.buttonText = 'addon.mod_quiz.reattemptquiz'; - } - } - } else if (this.quizAccessInfo.canpreview) { - this.buttonText = 'addon.mod_quiz.previewquiznow'; - } - } - } - - if (this.buttonText) { - // So far we think a button should be printed, check if they will be allowed to access it. - this.preventMessages = this.quizAccessInfo.preventaccessreasons; - - if (!this.moreAttempts) { - this.buttonText = ''; - } else if (this.quizAccessInfo.canattempt && this.preventMessages.length) { - this.buttonText = ''; - } else if (!this.hasSupportedQuestions || this.unsupportedRules.length || !this.behaviourSupported) { - this.buttonText = ''; - } - } - } - - /** - * Get result info to show. - * - * @return Promise resolved when done. - */ - protected getResultInfo(): Promise { - - if (this.attempts.length && this.quizData.showGradeColumn && this.bestGrade.hasgrade && - typeof this.gradebookData.grade != 'undefined') { - - const formattedGradebookGrade = this.quizProvider.formatGrade(this.gradebookData.grade, this.quizData.decimalpoints), - formattedBestGrade = this.quizProvider.formatGrade(this.bestGrade.grade, this.quizData.decimalpoints); - let gradeToShow = formattedGradebookGrade; // By default we show the grade in the gradebook. - - this.showResults = true; - this.gradeOverridden = formattedGradebookGrade != formattedBestGrade; - this.gradebookFeedback = this.gradebookData.feedback; - - if (this.bestGrade.grade > this.gradebookData.grade && this.gradebookData.grade == this.quizData.grade) { - // The best grade is higher than the max grade for the quiz. - // We'll do like Moodle web and show the best grade instead of the gradebook grade. - this.gradeOverridden = false; - gradeToShow = formattedBestGrade; - } - - if (this.overallStats) { - // Show the quiz grade. The message shown is different if the quiz is finished. - if (this.moreAttempts) { - this.gradeResult = this.translate.instant('addon.mod_quiz.gradesofar', {$a: { - method: this.quizData.gradeMethodReadable, - mygrade: gradeToShow, - quizgrade: this.quizData.gradeFormatted - }}); - } else { - const outOfShort = this.translate.instant('addon.mod_quiz.outofshort', {$a: { - grade: gradeToShow, - maxgrade: this.quizData.gradeFormatted - }}); - - this.gradeResult = this.translate.instant('addon.mod_quiz.yourfinalgradeis', {$a: outOfShort}); - } - } - - if (this.quizData.showFeedbackColumn) { - // Get the quiz overall feedback. - return this.quizProvider.getFeedbackForGrade(this.quizData.id, this.gradebookData.grade, { - cmId: this.module.id, - }).then((response) => { - this.overallFeedback = response.feedbacktext; - }); - } - } else { - this.showResults = false; - } - - return Promise.resolve(); - } - - /** - * Go to review an attempt that has just been finished. - * - * @return Promise resolved when done. - */ - protected goToAutoReview(): Promise { - // If we go to auto review it means an attempt was finished. Check completion status. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - - // Verify that user can see the review. - const attemptId = this.autoReview.attemptId; - - if (this.quizAccessInfo.canreviewmyattempts) { - return this.quizProvider.getAttemptReview(attemptId, {page: -1, cmId: this.module.id}).then(() => { - this.navCtrl.push('AddonModQuizReviewPage', {courseId: this.courseId, quizId: this.quizData.id, attemptId}); - }).catch(() => { - // Ignore errors. - }); - } - - return Promise.resolve(); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return If suceed or not. - */ - protected hasSyncSucceed(result: any): boolean { - if (result.attemptFinished) { - // An attempt was finished, check completion status. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - - // If the sync call isn't rejected it means the sync was successful. - return result.answersSent; - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - super.ionViewDidEnter(); - - if (this.hasPlayed) { - this.hasPlayed = false; - - // Update data when we come back from the player since the attempt status could have changed. - let promise; - - // Check if we need to go to review an attempt automatically. - if (this.autoReview && this.autoReview.synced) { - promise = this.goToAutoReview(); - this.autoReview = undefined; - } else { - promise = Promise.resolve(); - } - - // Refresh data. - this.loaded = false; - this.refreshIcon = 'spinner'; - this.syncIcon = 'spinner'; - this.domUtils.scrollToTop(this.content); - - promise.then(() => { - this.refreshContent().finally(() => { - this.loaded = true; - this.refreshIcon = 'refresh'; - this.syncIcon = 'sync'; - }); - }); - } else { - this.autoReview = undefined; - } - } - - /** - * User left the page that contains the component. - */ - ionViewDidLeave(): void { - super.ionViewDidLeave(); - this.autoReview = undefined; - - if (this.navCtrl.getActive().component.name == 'AddonModQuizPlayerPage') { - this.hasPlayed = true; - } - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.quizProvider.invalidateQuizData(this.courseId)); - - if (this.quizData) { - promises.push(this.quizProvider.invalidateUserAttemptsForUser(this.quizData.id)); - promises.push(this.quizProvider.invalidateQuizAccessInformation(this.quizData.id)); - promises.push(this.quizProvider.invalidateQuizRequiredQtypes(this.quizData.id)); - promises.push(this.quizProvider.invalidateAttemptAccessInformation(this.quizData.id)); - promises.push(this.quizProvider.invalidateCombinedReviewOptionsForUser(this.quizData.id)); - promises.push(this.quizProvider.invalidateUserBestGradeForUser(this.quizData.id)); - promises.push(this.quizProvider.invalidateGradeFromGradebook(this.courseId)); - } - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (syncEventData.attemptFinished) { - // An attempt was finished, check completion status. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - - if (this.quizData && syncEventData.quizId == this.quizData.id) { - this.domUtils.scrollToTop(this.content); - - return true; - } - - return false; - } - - /** - * Open a quiz to attempt it. - */ - protected openQuiz(): void { - this.navCtrl.push('AddonModQuizPlayerPage', {courseId: this.courseId, quizId: this.quiz.id, moduleUrl: this.module.url}); - } - - /** - * Displays some data based on the current status. - * - * @param status The current status. - * @param previousStatus The previous status. If not defined, there is no previous status. - */ - protected showStatus(status: string, previousStatus?: string): void { - this.showStatusSpinner = status == CoreConstants.DOWNLOADING; - - if (status == CoreConstants.DOWNLOADED && previousStatus == CoreConstants.DOWNLOADING) { - // Quiz downloaded now, maybe a new attempt was created. Load content again. - this.loaded = false; - this.loadContent(); - } - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.quizSync.syncQuiz(this.quizData, true); - } - - /** - * Treat user attempts. - * - * @param attempts The attempts to treat. - * @return Promise resolved when done. - */ - protected treatAttempts(attempts: any): Promise { - if (!attempts || !attempts.length) { - // There are no attempts to treat. - return Promise.resolve(attempts); - } - - const lastFinished = this.quizProvider.getLastFinishedAttemptFromList(attempts), - promises = []; - - if (this.autoReview && lastFinished && lastFinished.id >= this.autoReview.attemptId) { - // User just finished an attempt in offline and it seems it's been synced, since it's finished in online. - // Go to the review of this attempt if the user hasn't left this view. - if (!this.isDestroyed && this.isCurrentView) { - promises.push(this.goToAutoReview()); - } - this.autoReview = undefined; - } - - // Load flag to show if attempts are finished but not synced. - promises.push(this.quizProvider.loadFinishedOfflineData(attempts)); - - // Get combined review options. - promises.push(this.quizProvider.getCombinedReviewOptions(this.quizData.id, {cmId: this.module.id}).then((result) => { - this.options = result; - })); - - // Get best grade. - promises.push(this.quizProvider.getUserBestGrade(this.quizData.id, {cmId: this.module.id}).then((best) => { - this.bestGrade = best; - - // Get gradebook grade. - return this.quizProvider.getGradeFromGradebook(this.courseId, this.module.id).then((data) => { - this.gradebookData = { - grade: data.graderaw, - feedback: data.feedback - }; - }).catch(() => { - // Fallback to quiz best grade if failure or not found. - this.gradebookData = { - grade: this.bestGrade.grade - }; - }); - })); - - return Promise.all(promises).then(() => { - const grade: number = typeof this.gradebookData.grade != 'undefined' ? this.gradebookData.grade : this.bestGrade.grade, - quizGrade = this.quizProvider.formatGrade(grade, this.quizData.decimalpoints); - - // Calculate data to construct the header of the attempts table. - this.quizHelper.setQuizCalculatedData(this.quizData, this.options); - - this.overallStats = lastFinished && this.options.alloptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX; - - // Calculate data to show for each attempt. - attempts.forEach((attempt) => { - // Highlight the highest grade if appropriate. - const shouldHighlight = this.overallStats && this.quizData.grademethod == AddonModQuizProvider.GRADEHIGHEST && - attempts.length > 1; - - this.quizHelper.setAttemptCalculatedData(this.quizData, attempt, shouldHighlight, quizGrade); - }); - - return attempts; - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - - this.finishedObserver && this.finishedObserver.off(); - } -} diff --git a/src/addon/mod/quiz/lang/en.json b/src/addon/mod/quiz/lang/en.json deleted file mode 100644 index 080059c18..000000000 --- a/src/addon/mod/quiz/lang/en.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "answercolon": "Answer:", - "attemptfirst": "First attempt", - "attemptlast": "Last attempt", - "attemptnumber": "Attempt", - "attemptquiznow": "Attempt quiz now", - "attemptstate": "State", - "canattemptbutnotsubmit": "You can attempt this quiz in the app, but you will need to submit the attempt in browser for the following reasons:", - "cannotsubmitquizdueto": "This quiz attempt cannot be submitted for the following reasons:", - "clearchoice": "Clear my choice", - "comment": "Comment", - "completedon": "Completed on", - "confirmclose": "Once you submit, you will no longer be able to change your answers for this attempt.", - "confirmcontinueoffline": "This attempt has not been synchronised since {{$a}}. If you have continued this attempt in another device since then, you may lose data.", - "confirmleavequizonerror": "An error occurred while saving the answers. Are you sure you want to leave the quiz?", - "confirmstart": "Your attempt will have a time limit of {{$a}}. When you start, the timer will begin to count down and cannot be paused. You must finish your attempt before it expires. Are you sure you wish to start now?", - "confirmstartheader": "Time limit", - "connectionerror": "Network connection lost. (Autosave failed).\n\nMake a note of any responses entered on this page in the last few minutes, then try to re-connect.\n\nOnce connection has been re-established, your responses should be saved and this message will disappear.", - "continueattemptquiz": "Continue the last attempt", - "continuepreview": "Continue the last preview", - "errorbehaviournotsupported": "This quiz can't be attempted in the app because the question behaviour is not supported by the app:", - "errordownloading": "Error downloading required data.", - "errorgetattempt": "Error getting attempt data.", - "errorgetquestions": "Error getting questions.", - "errorgetquiz": "Error getting quiz data.", - "errorparsequestions": "An error occurred while reading the questions. Please attempt this quiz in a web browser.", - "errorquestionsnotsupported": "This quiz can't be attempted in the app because it only contains questions not supported by the app:", - "errorrulesnotsupported": "This quiz can't be attempted in the app because it has access rules not supported by the app:", - "errorsaveattempt": "An error occurred while saving the attempt data.", - "feedback": "Feedback", - "finishattemptdots": "Finish attempt...", - "finishnotsynced": "Finished but not synchronised", - "grade": "Grade", - "gradeaverage": "Average grade", - "gradehighest": "Highest grade", - "grademethod": "Grading method", - "gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "marks": "Marks", - "modulenameplural": "Quizzes", - "mustbesubmittedby": "This attempt must be submitted by {{$a}}.", - "noquestions": "No questions have been added yet", - "noreviewattempt": "You are not allowed to review this attempt.", - "notyetgraded": "Not yet graded", - "opentoc": "Open navigation popover", - "outof": "{{$a.grade}} out of {{$a.maxgrade}}", - "outofpercent": "{{$a.grade}} out of {{$a.maxgrade}} ({{$a.percent}}%)", - "outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "overallfeedback": "Overall feedback", - "overdue": "Overdue", - "overduemustbesubmittedby": "This attempt is now overdue. It should already have been submitted. If you would like this quiz to be graded, you must submit it by {{$a}}. If you do not submit it by then, no marks from this attempt will be counted.", - "preview": "Preview", - "previewquiznow": "Preview quiz now", - "question": "Question", - "quiznavigation": "Quiz navigation", - "quizpassword": "Quiz password", - "reattemptquiz": "Re-attempt quiz", - "requirepasswordmessage": "To attempt this quiz you need to know the quiz password", - "returnattempt": "Return to attempt", - "review": "Review", - "reviewofattempt": "Review of attempt {{$a}}", - "reviewofpreview": "Review of preview", - "showall": "Show all questions on one page", - "showeachpage": "Show one page at a time", - "startattempt": "Start attempt", - "startedon": "Started on", - "stateabandoned": "Never submitted", - "statefinished": "Finished", - "statefinisheddetails": "Submitted {{$a}}", - "stateinprogress": "In progress", - "stateoverdue": "Overdue", - "stateoverduedetails": "Must be submitted by {{$a}}", - "status": "Status", - "submitallandfinish": "Submit all and finish", - "summaryofattempt": "Summary of attempt", - "summaryofattempts": "Summary of your previous attempts", - "timeleft": "Time left", - "timetaken": "Time taken", - "warningattemptfinished": "Offline attempt discarded as it was finished on the site or not found.", - "warningdatadiscarded": "Some offline answers were discarded because the questions were modified online.", - "warningdatadiscardedfromfinished": "Attempt unfinished because some offline answers were discarded. Please review your answers then resubmit the attempt.", - "warningquestionsnotsupported": "This quiz contains questions not supported by the app:", - "yourfinalgradeis": "Your final grade for this quiz is {{$a}}." -} \ No newline at end of file diff --git a/src/addon/mod/quiz/pages/attempt/attempt.html b/src/addon/mod/quiz/pages/attempt/attempt.html deleted file mode 100644 index 791991ada..000000000 --- a/src/addon/mod/quiz/pages/attempt/attempt.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - -

{{ 'addon.mod_quiz.attemptnumber' | translate }}

-

{{ 'addon.mod_quiz.preview' | translate }}

-

{{ attempt.attempt }}

-
- -

{{ 'addon.mod_quiz.attemptstate' | translate }}

-

{{ sentence }}

-
- -

{{ 'addon.mod_quiz.marks' | translate }} / {{ quiz.sumGradesFormatted }}

-

{{ attempt.readableMark }}

-
- -

{{ 'addon.mod_quiz.grade' | translate }} / {{ quiz.gradeFormatted }}

-

{{ attempt.readableGrade }}

-
- -

{{ 'addon.mod_quiz.feedback' | translate }}

-

-
- - - - -

{{ 'addon.mod_quiz.noreviewattempt' | translate }}

-
-
-
-
diff --git a/src/addon/mod/quiz/pages/attempt/attempt.module.ts b/src/addon/mod/quiz/pages/attempt/attempt.module.ts deleted file mode 100644 index f766e41e0..000000000 --- a/src/addon/mod/quiz/pages/attempt/attempt.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModQuizAttemptPage } from './attempt'; - -@NgModule({ - declarations: [ - AddonModQuizAttemptPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonModQuizAttemptPage), - TranslateModule.forChild() - ], -}) -export class AddonModQuizAttemptPageModule {} diff --git a/src/addon/mod/quiz/pages/attempt/attempt.ts b/src/addon/mod/quiz/pages/attempt/attempt.ts deleted file mode 100644 index d4e3d1909..000000000 --- a/src/addon/mod/quiz/pages/attempt/attempt.ts +++ /dev/null @@ -1,185 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModQuizProvider } from '../../providers/quiz'; -import { AddonModQuizHelperProvider } from '../../providers/helper'; - -/** - * Page that displays some summary data about an attempt. - */ -@IonicPage({ segment: 'addon-mod-quiz-attempt' }) -@Component({ - selector: 'page-addon-mod-quiz-attempt', - templateUrl: 'attempt.html', -}) -export class AddonModQuizAttemptPage implements OnInit { - courseId: number; // The course ID the quiz belongs to. - quiz: any; // The quiz the attempt belongs to. - attempt: any; // The attempt to view. - component = AddonModQuizProvider.COMPONENT; // Component to link the files to. - componentId: number; // Component ID to use in conjunction with the component. - loaded: boolean; // Whether data has been loaded. - - protected attemptId: number; // Attempt to view. - protected quizId: number; // ID of the quiz the attempt belongs to. - - constructor(navParams: NavParams, protected domUtils: CoreDomUtilsProvider, protected quizProvider: AddonModQuizProvider, - protected quizHelper: AddonModQuizHelperProvider) { - - this.attemptId = navParams.get('attemptId'); - this.quizId = navParams.get('quizId'); - this.courseId = navParams.get('courseId'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchQuizData().finally(() => { - this.loaded = true; - }); - } - - /** - * Refresh the data. - * - * @param refresher Refresher. - */ - doRefresh(refresher: any): void { - this.refreshData().finally(() => { - refresher.complete(); - }); - } - - /** - * Get quiz data and attempt data. - * - * @return Promise resolved when done. - */ - protected fetchQuizData(): Promise { - return this.quizProvider.getQuizById(this.courseId, this.quizId).then((quizData) => { - this.quiz = quizData; - this.componentId = this.quiz.coursemodule; - - return this.fetchAttempt(); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_quiz.errorgetattempt', true); - }); - } - - /** - * Get the attempt data. - * - * @return Promise resolved when done. - */ - protected fetchAttempt(): Promise { - const promises = []; - let options, - accessInfo; - - // Get all the attempts and search the one we want. - promises.push(this.quizProvider.getUserAttempts(this.quizId, {cmId: this.quiz.coursemodule}).then((attempts) => { - for (let i = 0; i < attempts.length; i++) { - const attempt = attempts[i]; - if (attempt.id == this.attemptId) { - this.attempt = attempt; - break; - } - } - - if (!this.attempt) { - // Attempt not found, error. - return Promise.reject(null); - } - - // Load flag to show if attempt is finished but not synced. - return this.quizProvider.loadFinishedOfflineData([this.attempt]); - })); - - promises.push(this.quizProvider.getCombinedReviewOptions(this.quiz.id, {cmId: this.quiz.coursemodule}).then((opts) => { - options = opts; - })); - - // Check if the user can review the attempt. - promises.push(this.quizProvider.getQuizAccessInformation(this.quiz.id, {cmId: this.quiz.coursemodule}) - .then((quizAccessInfo) => { - accessInfo = quizAccessInfo; - - if (accessInfo.canreviewmyattempts) { - // Check if the user can review the attempt. - return this.quizProvider.invalidateAttemptReviewForPage(this.attemptId, -1).catch(() => { - // Ignore errors. - }).then(() => { - return this.quizProvider.getAttemptReview(this.attemptId, {page: -1, cmId: this.quiz.coursemodule}); - }).catch(() => { - // Error getting the review, assume the user cannot review the attempt. - accessInfo.canreviewmyattempts = false; - }); - } - })); - - return Promise.all(promises).then(() => { - - // Determine fields to show. - this.quizHelper.setQuizCalculatedData(this.quiz, options); - this.quiz.showReviewColumn = accessInfo.canreviewmyattempts; - - // Get readable data for the attempt. - this.quizHelper.setAttemptCalculatedData(this.quiz, this.attempt, false); - - // Check if the feedback should be displayed. - const grade = Number(this.attempt.rescaledGrade); - if (this.quiz.showFeedbackColumn && this.quizProvider.isAttemptFinished(this.attempt.state) && - options.someoptions.overallfeedback && !isNaN(grade)) { - - // Feedback should be displayed, get the feedback for the grade. - return this.quizProvider.getFeedbackForGrade(this.quiz.id, grade, { - cmId: this.quiz.coursemodule, - }).then((response) => { - this.attempt.feedback = response.feedbacktext; - }); - } else { - delete this.attempt.feedback; - } - }); - } - - /** - * Refresh the data. - * - * @return Promise resolved when done. - */ - protected refreshData(): Promise { - const promises = []; - - promises.push(this.quizProvider.invalidateQuizData(this.courseId)); - promises.push(this.quizProvider.invalidateUserAttemptsForUser(this.quizId)); - promises.push(this.quizProvider.invalidateQuizAccessInformation(this.quizId)); - promises.push(this.quizProvider.invalidateCombinedReviewOptionsForUser(this.quizId)); - promises.push(this.quizProvider.invalidateAttemptReview(this.attemptId)); - - if (this.attempt && typeof this.attempt.feedback != 'undefined') { - promises.push(this.quizProvider.invalidateFeedback(this.quizId)); - } - - return Promise.all(promises).catch(() => { - // Ignore errors. - }).then(() => { - return this.fetchQuizData(); - }); - } -} diff --git a/src/addon/mod/quiz/pages/index/index.html b/src/addon/mod/quiz/pages/index/index.html deleted file mode 100644 index 228d3f202..000000000 --- a/src/addon/mod/quiz/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/quiz/pages/index/index.module.ts b/src/addon/mod/quiz/pages/index/index.module.ts deleted file mode 100644 index 73045fd3f..000000000 --- a/src/addon/mod/quiz/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModQuizComponentsModule } from '../../components/components.module'; -import { AddonModQuizIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModQuizIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModQuizComponentsModule, - IonicPageModule.forChild(AddonModQuizIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModQuizIndexPageModule {} diff --git a/src/addon/mod/quiz/pages/index/index.ts b/src/addon/mod/quiz/pages/index/index.ts deleted file mode 100644 index 9d0172d41..000000000 --- a/src/addon/mod/quiz/pages/index/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModQuizIndexComponent } from '../../components/index/index'; - -/** - * Page that displays the quiz entry page. - */ -@IonicPage({ segment: 'addon-mod-quiz-index' }) -@Component({ - selector: 'page-addon-mod-quiz-index', - templateUrl: 'index.html', -}) -export class AddonModQuizIndexPage { - @ViewChild(AddonModQuizIndexComponent) quizComponent: AddonModQuizIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the quiz instance. - * - * @param quiz Quiz instance. - */ - updateData(quiz: any): void { - this.title = quiz.name || this.title; - } - - /** - * User entered the page. - */ - ionViewDidEnter(): void { - this.quizComponent.ionViewDidEnter(); - } - - /** - * User left the page. - */ - ionViewDidLeave(): void { - this.quizComponent.ionViewDidLeave(); - } -} diff --git a/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.html b/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.html deleted file mode 100644 index 9398439f7..000000000 --- a/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.html +++ /dev/null @@ -1,46 +0,0 @@ - - - {{ 'addon.mod_quiz.quiznavigation' | translate }} - - - - - - - - diff --git a/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.module.ts b/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.module.ts deleted file mode 100644 index 886b0de8d..000000000 --- a/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { AddonModQuizNavigationModalPage } from './navigation-modal'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModQuizNavigationModalPage - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - IonicPageModule.forChild(AddonModQuizNavigationModalPage), - TranslateModule.forChild() - ] -}) -export class AddonModQuizNavigationModalPageModule {} diff --git a/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.scss b/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.scss deleted file mode 100644 index 2896cb9d3..000000000 --- a/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.scss +++ /dev/null @@ -1,22 +0,0 @@ -ion-app.app-root page-addon-mod-quiz-navigation-modal { - .item.core-question-correct, - .item.core-question-incorrect, - .item.core-question-notanswered, - .item.core-question-partiallycorrect, - .item.core-question-requiresgrading, - .item.core-question-answersaved { - ion-label.label { - color: $text-color; - } - color: $text-color; - background-color: $white; - - @include darkmode() { - ion-label.label { - color: $core-dark-text-color; - } - color: $core-dark-text-color; - background-color: $core-dark-item-bg-color; - } - } -} diff --git a/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.ts b/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.ts deleted file mode 100644 index 4d1b1da6d..000000000 --- a/src/addon/mod/quiz/pages/navigation-modal/navigation-modal.ts +++ /dev/null @@ -1,68 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, ViewController, NavParams } from 'ionic-angular'; - -/** - * Modal that renders the quiz navigation. - */ -@IonicPage({ segment: 'addon-mod-quiz-navigation-modal' }) -@Component({ - selector: 'page-addon-mod-quiz-navigation-modal', - templateUrl: 'navigation-modal.html', -}) -export class AddonModQuizNavigationModalPage { - - /** - * The instance of the page that opened the modal. We use the instance instead of the needed attributes for these reasons: - * - Some attributes can change dynamically, and we don't want to create the modal everytime the user opens it. - * - The onDidDismiss function takes a while to be called, making the app seem slow. This way we can directly call - * the functions we need without having to wait for the modal to be dismissed. - */ - pageInstance: any; - - isReview: boolean; // Whether the user is reviewing the attempt. - - constructor(params: NavParams, protected viewCtrl: ViewController) { - this.isReview = !!params.get('isReview'); - this.pageInstance = params.get('page'); - } - - /** - * Close modal. - */ - closeModal(): void { - this.viewCtrl.dismiss(); - } - - /** - * Load a certain page. - * - * @param page The page to load. - * @param slot Slot of the question to scroll to. - */ - loadPage(page: number, slot: number): void { - this.pageInstance.changePage && this.pageInstance.changePage(page, true, slot); - this.closeModal(); - } - - /** - * Switch mode in review. - */ - switchMode(): void { - this.pageInstance.switchMode && this.pageInstance.switchMode(); - this.closeModal(); - } -} diff --git a/src/addon/mod/quiz/pages/player/player.html b/src/addon/mod/quiz/pages/player/player.html deleted file mode 100644 index 9936714cf..000000000 --- a/src/addon/mod/quiz/pages/player/player.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - -
-
- - - -

{{ 'core.question.questionno' | translate:{$a: question.number} }}

-

{{ 'core.question.information' | translate }}

- -

{{question.status}}

-

{{ question.readableMark }}

-
-
- - -
-
-
- - - - - - - - - - - - - - - - -

{{ 'addon.mod_quiz.summaryofattempt' | translate }}

-
- - - - {{ 'addon.mod_quiz.question' | translate }} - # - {{ 'addon.mod_quiz.status' | translate }} - - - - - - - {{ question.number }} - {{ question.status }} - - - - - - {{ 'addon.mod_quiz.returnattempt' | translate }} - - - - {{ attempt.dueDateWarning }} - - - - - -

{{ 'addon.mod_quiz.cannotsubmitquizdueto' | translate }}

-

{{message}}

- - {{ 'core.openinbrowser' | translate }} - - -
- - - {{ 'addon.mod_quiz.submitallandfinish' | translate }} - -
- - - - -

{{ 'addon.mod_quiz.errorparsequestions' | translate }}

-
- - - {{ 'core.openinbrowser' | translate }} - - - -
-
-
diff --git a/src/addon/mod/quiz/pages/player/player.module.ts b/src/addon/mod/quiz/pages/player/player.module.ts deleted file mode 100644 index 1f6113f51..000000000 --- a/src/addon/mod/quiz/pages/player/player.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreQuestionComponentsModule } from '@core/question/components/components.module'; -import { AddonModQuizPlayerPage } from './player'; - -@NgModule({ - declarations: [ - AddonModQuizPlayerPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CoreQuestionComponentsModule, - IonicPageModule.forChild(AddonModQuizPlayerPage), - TranslateModule.forChild() - ], -}) -export class AddonModQuizPlayerPageModule {} diff --git a/src/addon/mod/quiz/pages/player/player.scss b/src/addon/mod/quiz/pages/player/player.scss deleted file mode 100644 index d7febf8dd..000000000 --- a/src/addon/mod/quiz/pages/player/player.scss +++ /dev/null @@ -1,19 +0,0 @@ -ion-app.app-root page-addon-mod-quiz-player { - .toolbar { - padding-top: 0; - padding-bottom: 0; - } - - .core-has-fixed-timer form { - padding-top: 56px; - } - - .toolbar-ios .bar-buttons-ios .bar-button { - @include padding-horizontal($content-padding); - } - - .core-question-container { - display: block; - padding-bottom: $content-padding; - } -} \ No newline at end of file diff --git a/src/addon/mod/quiz/pages/player/player.ts b/src/addon/mod/quiz/pages/player/player.ts deleted file mode 100644 index 75fb54312..000000000 --- a/src/addon/mod/quiz/pages/player/player.ts +++ /dev/null @@ -1,708 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, ViewChildren, QueryList, ElementRef } from '@angular/core'; -import { IonicPage, NavParams, Content, PopoverController, ModalController, Modal, NavController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { CoreQuestionComponent } from '@core/question/components/question/question'; -import { MoodleMobileApp } from '../../../../../app/app.component'; -import { AddonModQuizProvider } from '../../providers/quiz'; -import { AddonModQuizSyncProvider } from '../../providers/quiz-sync'; -import { AddonModQuizHelperProvider } from '../../providers/helper'; -import { AddonModQuizAutoSave } from '../../classes/auto-save'; -import { Subscription } from 'rxjs'; - -/** - * Page that allows attempting a quiz. - */ -@IonicPage({ segment: 'addon-mod-quiz-player' }) -@Component({ - selector: 'page-addon-mod-quiz-player', - templateUrl: 'player.html', -}) -export class AddonModQuizPlayerPage implements OnInit, OnDestroy { - @ViewChild(Content) content: Content; - @ViewChildren(CoreQuestionComponent) questionComponents: QueryList; - @ViewChild('quizForm') formElement: ElementRef; - - quiz: any; // The quiz the attempt belongs to. - attempt: any; // The attempt being attempted. - moduleUrl: string; // URL to the module in the site. - component = AddonModQuizProvider.COMPONENT; // Component to link the files to. - loaded: boolean; // Whether data has been loaded. - quizAborted: boolean; // Whether the quiz was aborted due to an error. - offline: boolean; // Whether the quiz is being attempted in offline mode. - navigation: any[]; // List of questions to navigate them. - questions: any[]; // Questions of the current page. - nextPage: number; // Next page. - previousPage: number; // Previous page. - showSummary: boolean; // Whether the attempt summary should be displayed. - summaryQuestions: any[]; // The questions to display in the summary. - canReturn: boolean; // Whether the user can return to a page after seeing the summary. - preventSubmitMessages: string[]; // List of messages explaining why the quiz cannot be submitted. - endTime: number; // The time when the attempt must be finished. - autoSaveError: boolean; // Whether there's been an error in auto-save. - navigationModal: Modal; // Modal to navigate through the questions. - - protected courseId: number; // The course ID the quiz belongs to. - protected quizId: number; // Quiz ID to attempt. - protected preflightData: any = {}; // Preflight data to attempt the quiz. - protected quizAccessInfo: any; // Quiz access information. - protected attemptAccessInfo: any; // Attempt access info. - protected lastAttempt: any; // Last user attempt before a new one is created (if needed). - protected newAttempt: boolean; // Whether the user is starting a new attempt. - protected quizDataLoaded: boolean; // Whether the quiz data has been loaded. - protected timeUpCalled: boolean; // Whether the time up function has been called. - protected autoSave: AddonModQuizAutoSave; // Class to auto-save answers every certain time. - protected autoSaveErrorSubscription: Subscription; // To be notified when an error happens in auto-save. - protected forceLeave = false; // If true, don't perform any check when leaving the view. - protected reloadNavigaton = false; // Whether navigation needs to be reloaded because some data was sent to server. - - constructor(navParams: NavParams, logger: CoreLoggerProvider, protected translate: TranslateService, - protected eventsProvider: CoreEventsProvider, protected sitesProvider: CoreSitesProvider, - protected syncProvider: CoreSyncProvider, protected domUtils: CoreDomUtilsProvider, popoverCtrl: PopoverController, - protected timeUtils: CoreTimeUtilsProvider, protected quizProvider: AddonModQuizProvider, - protected quizHelper: AddonModQuizHelperProvider, protected quizSync: AddonModQuizSyncProvider, - protected questionHelper: CoreQuestionHelperProvider, protected cdr: ChangeDetectorRef, - modalCtrl: ModalController, protected navCtrl: NavController, protected mmApp: MoodleMobileApp) { - - this.quizId = navParams.get('quizId'); - this.courseId = navParams.get('courseId'); - this.moduleUrl = navParams.get('moduleUrl'); - - // Block the quiz so it cannot be synced. - this.syncProvider.blockOperation(AddonModQuizProvider.COMPONENT, this.quizId); - - // Create the auto save instance. - this.autoSave = new AddonModQuizAutoSave('addon-mod_quiz-player-form', '#addon-mod_quiz-connection-error-button', - logger, popoverCtrl, questionHelper, quizProvider); - - // Create the navigation modal. - this.navigationModal = modalCtrl.create('AddonModQuizNavigationModalPage', { - page: this - }, { cssClass: 'core-modal-lateral', - showBackdrop: true, - enableBackdropDismiss: true, - enterAnimation: 'core-modal-lateral-transition', - leaveAnimation: 'core-modal-lateral-transition' }); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Start the player when the page is loaded. - this.start(); - - // Listen for errors on auto-save. - this.autoSaveErrorSubscription = this.autoSave.onError().subscribe((error) => { - this.autoSaveError = error; - this.cdr.detectChanges(); - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - // Stop auto save. - this.autoSave.cancelAutoSave(); - this.autoSave.stopCheckChangesProcess(); - this.autoSaveErrorSubscription && this.autoSaveErrorSubscription.unsubscribe(); - - // Unblock the quiz so it can be synced. - this.syncProvider.unblockOperation(AddonModQuizProvider.COMPONENT, this.quizId); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave) { - return; - } - - if (this.questions && this.questions.length && !this.showSummary) { - // Save answers. - const modal = this.domUtils.showModalLoading('core.sending', true); - - try { - await this.processAttempt(false, false); - } catch (error) { - // Save attempt failed. Show confirmation. - modal.dismiss(); - - await this.domUtils.showConfirm(this.translate.instant('addon.mod_quiz.confirmleavequizonerror')); - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } finally { - modal.dismiss(); - } - } - } - - /** - * Runs when the page is about to leave and no longer be the active page. - */ - ionViewWillLeave(): void { - this.mmApp.closeModal(); - } - - /** - * Abort the quiz. - */ - abortQuiz(): void { - this.quizAborted = true; - } - - /** - * A behaviour button in a question was clicked (Check, Redo, ...). - * - * @param button Clicked button. - */ - behaviourButtonClicked(button: any): void { - // Confirm that the user really wants to do it. - this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => { - const modal = this.domUtils.showModalLoading('core.sending', true); - - // Get the answers. - return this.prepareAnswers().then((answers) => { - - // Add the clicked button data. - answers[button.name] = button.value; - - // Behaviour checks are always in online. - return this.quizProvider.processAttempt(this.quiz, this.attempt, answers, this.preflightData); - }).then(() => { - this.reloadNavigaton = true; // Data sent to server, navigation should be reloaded. - - // Reload the current page. - const scrollElement = this.content.getScrollElement(), - scrollTop = scrollElement.scrollTop || 0, - scrollLeft = scrollElement.scrollLeft || 0; - - this.loaded = false; - this.domUtils.scrollToTop(this.content); // Scroll top so the spinner is seen. - - return this.loadPage(this.attempt.currentpage).finally(() => { - this.loaded = true; - this.domUtils.scrollTo(this.content, scrollLeft, scrollTop); - }); - }).finally(() => { - modal.dismiss(); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error performing action.'); - }); - } - - /** - * Change the current page. If slot is supplied, try to scroll to that question. - * - * @param page Page to load. -1 means summary. - * @param fromModal Whether the page was selected using the navigation modal. - * @param slot Slot of the question to scroll to. - */ - changePage(page: number, fromModal?: boolean, slot?: number): void { - if (page != -1 && (this.attempt.state == AddonModQuizProvider.ATTEMPT_OVERDUE || this.attempt.finishedOffline)) { - // We can't load a page if overdue or the local attempt is finished. - return; - } else if (page == this.attempt.currentpage && !this.showSummary && typeof slot != 'undefined') { - // Navigating to a question in the current page. - this.scrollToQuestion(slot); - - return; - } else if ((page == this.attempt.currentpage && !this.showSummary) || (fromModal && this.quiz.isSequential && page != -1)) { - // If the user is navigating to the current page we do nothing. - // Also, in sequential quizzes we don't allow navigating using the modal except for finishing the quiz (summary). - return; - } else if (page === -1 && this.showSummary) { - // Summary already shown. - return; - } - - this.loaded = false; - this.domUtils.scrollToTop(this.content); - - // First try to save the attempt data. We only save it if we're not seeing the summary. - const promise = this.showSummary ? Promise.resolve() : this.processAttempt(false, false); - promise.then(() => { - if (!this.showSummary) { - this.reloadNavigaton = true; // Data sent to server, navigation should be reloaded. - } - - // Attempt data successfully saved, load the page or summary. - let subPromise; - - // Stop checking for changes during page change. - this.autoSave.stopCheckChangesProcess(); - - if (page === -1) { - subPromise = this.loadSummary(); - } else { - subPromise = this.loadPage(page); - } - - return subPromise.catch((error) => { - // If the user isn't seeing the summary, start the check again. - if (!this.showSummary) { - this.autoSave.startCheckChangesProcess(this.quiz, this.attempt, this.preflightData, this.offline); - } - - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true); - }); - }, (error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorsaveattempt', true); - }).finally(() => { - this.loaded = true; - - if (typeof slot != 'undefined') { - // Scroll to the question. Give some time to the questions to render. - setTimeout(() => { - this.scrollToQuestion(slot); - }, 2000); - } - }); - } - - /** - * Convenience function to get the quiz data. - * - * @return Promise resolved when done. - */ - protected fetchData(): Promise { - // Wait for any ongoing sync to finish. We won't sync a quiz while it's being played. - return this.quizSync.waitForSync(this.quizId).then(() => { - // Sync finished, now get the quiz. - return this.quizProvider.getQuizById(this.courseId, this.quizId); - }).then((quizData) => { - this.quiz = quizData; - this.quiz.isSequential = this.quizProvider.isNavigationSequential(this.quiz); - - if (this.quizProvider.isQuizOffline(this.quiz)) { - // Quiz supports offline. - return true; - } else { - // Quiz doesn't support offline right now, but maybe it did and then the setting was changed. - // If we have an unfinished offline attempt then we'll use offline mode. - return this.quizProvider.isLastAttemptOfflineUnfinished(this.quiz); - } - }).then((offlineMode) => { - this.offline = offlineMode; - - if (this.quiz.timelimit > 0) { - this.quiz.readableTimeLimit = this.timeUtils.formatTime(this.quiz.timelimit); - } - - // Get access information for the quiz. - return this.quizProvider.getQuizAccessInformation(this.quiz.id, { - cmId: this.quiz.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }); - }).then((info) => { - this.quizAccessInfo = info; - - // Get user attempts to determine last attempt. - return this.quizProvider.getUserAttempts(this.quiz.id, { - cmId: this.quiz.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }); - }).then((attempts) => { - if (!attempts.length) { - // There are no attempts, start a new one. - this.newAttempt = true; - } else { - const promises = []; - - // Get the last attempt. If it's finished, start a new one. - this.lastAttempt = attempts[attempts.length - 1]; - this.newAttempt = this.quizProvider.isAttemptFinished(this.lastAttempt.state); - - // Load quiz last sync time. - promises.push(this.quizSync.getSyncTime(this.quiz.id).then((time) => { - this.quiz.syncTime = time; - this.quiz.syncTimeReadable = this.quizSync.getReadableTimeFromTimestamp(time); - })); - - // Load flag to show if attempts are finished but not synced. - promises.push(this.quizProvider.loadFinishedOfflineData(attempts)); - - return Promise.all(promises); - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquiz', true); - }); - } - - /** - * Finish an attempt, either by timeup or because the user clicked to finish it. - * - * @param userFinish Whether the user clicked to finish the attempt. - * @param timeUp Whether the quiz time is up. - * @return Promise resolved when done. - */ - finishAttempt(userFinish?: boolean, timeUp?: boolean): Promise { - let promise; - - // Show confirm if the user clicked the finish button and the quiz is in progress. - if (!timeUp && this.attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { - promise = this.domUtils.showConfirm(this.translate.instant('addon.mod_quiz.confirmclose')); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - const modal = this.domUtils.showModalLoading('core.sending', true); - - return this.processAttempt(userFinish, timeUp).then(() => { - // Trigger an event to notify the attempt was finished. - this.eventsProvider.trigger(AddonModQuizProvider.ATTEMPT_FINISHED_EVENT, { - quizId: this.quizId, - attemptId: this.attempt.id, - synced: !this.offline - }, this.sitesProvider.getCurrentSiteId()); - - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'quiz' }); - - // Leave the player. - this.forceLeave = true; - this.navCtrl.pop(); - }).finally(() => { - modal.dismiss(); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorsaveattempt', true); - }); - } - - /** - * Fix sequence checks of current page. - * - * @return Promise resolved when done. - */ - protected fixSequenceChecks(): Promise { - // Get current page data again to get the latest sequencechecks. - return this.quizProvider.getAttemptData(this.attempt.id, this.attempt.currentpage, this.preflightData, { - cmId: this.quiz.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }).then((data) => { - - const newSequenceChecks = {}; - - data.questions.forEach((question) => { - newSequenceChecks[question.slot] = this.questionHelper.getQuestionSequenceCheckFromHtml(question.html); - }); - - // Notify the new sequence checks to the components. - this.questionComponents.forEach((component) => { - component.updateSequenceCheck(newSequenceChecks); - }); - }); - } - - /** - * Get the input answers. - * - * @return Object with the answers. - */ - protected getAnswers(): any { - return this.questionHelper.getAnswersFromForm(document.forms['addon-mod_quiz-player-form']); - } - - /** - * Initializes the timer if enabled. - */ - protected initTimer(): void { - if (this.attemptAccessInfo.endtime > 0) { - // Quiz has an end time. Check if time left should be shown. - if (this.quizProvider.shouldShowTimeLeft(this.quizAccessInfo.activerulenames, this.attempt, - this.attemptAccessInfo.endtime)) { - this.endTime = this.attemptAccessInfo.endtime; - } else { - delete this.endTime; - } - } - } - - /** - * Load a page questions. - * - * @param page The page to load. - * @return Promise resolved when done. - */ - protected loadPage(page: number): Promise { - return this.quizProvider.getAttemptData(this.attempt.id, page, this.preflightData, { - cmId: this.quiz.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }).then((data) => { - // Update attempt, status could change during the execution. - this.attempt = data.attempt; - this.attempt.currentpage = page; - - this.questions = data.questions; - this.nextPage = data.nextpage; - this.previousPage = this.quiz.isSequential ? -1 : page - 1; - this.showSummary = false; - - this.questions.forEach((question) => { - // Get the readable mark for each question. - question.readableMark = this.quizHelper.getQuestionMarkFromHtml(question.html); - - // Extract the question info box. - this.questionHelper.extractQuestionInfoBox(question, '.info'); - - // Set the preferred behaviour. - question.preferredBehaviour = this.quiz.preferredbehaviour; - - // Check if the question is blocked. If it is, treat it as a description question. - if (this.quizProvider.isQuestionBlocked(question)) { - question.type = 'description'; - } - }); - - // Mark the page as viewed. We'll ignore errors in this call. - this.quizProvider.logViewAttempt(this.attempt.id, page, this.preflightData, this.offline, this.quiz).catch((error) => { - // Ignore errors. - }); - - // Start looking for changes. - this.autoSave.startCheckChangesProcess(this.quiz, this.attempt, this.preflightData, this.offline); - }); - } - - /** - * Load attempt summary. - * - * @return Promise resolved when done. - */ - protected loadSummary(): Promise { - this.summaryQuestions = []; - - return this.quizProvider.getAttemptSummary(this.attempt.id, this.preflightData, { - cmId: this.quiz.coursemodule, - loadLocal: this.offline, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }).then((qs) => { - this.showSummary = true; - this.summaryQuestions = qs; - - this.canReturn = this.attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS && !this.attempt.finishedOffline; - this.preventSubmitMessages = this.quizProvider.getPreventSubmitMessages(this.summaryQuestions); - - this.attempt.dueDateWarning = this.quizProvider.getAttemptDueDateWarning(this.quiz, this.attempt); - - // Log summary as viewed. - this.quizProvider.logViewAttemptSummary(this.attempt.id, this.preflightData, this.quizId, this.quiz.name) - .catch((error) => { - // Ignore errors. - }); - }); - } - - /** - * Load data to navigate the questions using the navigation modal. - * - * @return Promise resolved when done. - */ - protected loadNavigation(): Promise { - // We use the attempt summary to build the navigation because it contains all the questions. - return this.quizProvider.getAttemptSummary(this.attempt.id, this.preflightData, { - cmId: this.quiz.coursemodule, - loadLocal: this.offline, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }).then((questions) => { - - questions.forEach((question) => { - question.stateClass = this.questionHelper.getQuestionStateClass(question.state); - }); - - this.navigation = questions; - }); - } - - /** - * Open the navigation modal. - * - * @return Promise resolved when done. - */ - openNavigation(): Promise { - let promise; - - if (this.reloadNavigaton) { - // Some data has changed, reload the navigation. - const modal = this.domUtils.showModalLoading(); - - promise = this.loadNavigation().catch(() => { - // Ignore errors. - }).then(() => { - modal.dismiss(); - this.reloadNavigaton = false; - }); - } else { - promise = Promise.resolve(); - } - - return promise.finally(() => { - this.navigationModal.present(); - }); - } - - // Prepare the answers to be sent for the attempt. - protected prepareAnswers(): Promise { - return this.questionHelper.prepareAnswers(this.questions, this.getAnswers(), this.offline); - } - - /** - * Process attempt. - * - * @param userFinish Whether the user clicked to finish the attempt. - * @param timeUp Whether the quiz time is up. - * @return Promise resolved when done. - * @param retrying Whether we're retrying the change. - */ - protected processAttempt(userFinish?: boolean, timeUp?: boolean, retrying?: boolean): Promise { - // Get the answers to send. - return this.prepareAnswers().then((answers) => { - // Send the answers. - return this.quizProvider.processAttempt(this.quiz, this.attempt, answers, this.preflightData, userFinish, timeUp, - this.offline).catch((error) => { - - if (error && error.errorcode == 'submissionoutofsequencefriendlymessage') { - // There was an error with the sequence check. Try to ammend it. - return this.fixSequenceChecks().then((): any => { - if (retrying) { - // We're already retrying, don't send the data again because it could cause an infinite loop. - return Promise.reject(error); - } - - // Sequence checks updated, try to send the data again. - return this.processAttempt(userFinish, timeUp, true); - }, () => { - return Promise.reject(error); - }); - } - - return Promise.reject(error); - }); - }).then(() => { - // Answers saved, cancel auto save. - this.autoSave.cancelAutoSave(); - this.autoSave.hideAutoSaveError(); - - if (this.formElement) { - this.domUtils.triggerFormSubmittedEvent(this.formElement, !this.offline, this.sitesProvider.getCurrentSiteId()); - } - }); - } - - /** - * Scroll to a certain question. - * - * @param slot Slot of the question to scroll to. - */ - protected scrollToQuestion(slot: number): void { - this.domUtils.scrollToElementBySelector(this.content, '#addon-mod_quiz-question-' + slot); - } - - /** - * Show connection error. - * - * @param ev Click event. - */ - showConnectionError(ev: Event): void { - this.autoSave.showAutoSaveError(ev); - } - - /** - * Convenience function to start the player. - */ - start(): void { - let promise; - this.loaded = false; - - if (this.quizDataLoaded) { - // Quiz data has been loaded, try to start or continue. - promise = this.startOrContinueAttempt(); - } else { - // Fetch data. - promise = this.fetchData().then(() => { - this.quizDataLoaded = true; - - return this.startOrContinueAttempt(); - }); - } - - promise.finally(() => { - this.loaded = true; - }); - } - - /** - * Start or continue an attempt. - * - * @return [description] - */ - protected startOrContinueAttempt(): Promise { - const attempt = this.newAttempt ? undefined : this.lastAttempt; - - // Get the preflight data and start attempt if needed. - return this.quizHelper.getAndCheckPreflightData(this.quiz, this.quizAccessInfo, this.preflightData, attempt, this.offline, - false, 'addon.mod_quiz.startattempt').then((attempt) => { - - // Re-fetch attempt access information with the right attempt (might have changed because a new attempt was created). - return this.quizProvider.getAttemptAccessInformation(this.quiz.id, attempt.id, { - cmId: this.quiz.coursemodule, - readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - }).then((info) => { - this.attemptAccessInfo = info; - this.attempt = attempt; - - return this.loadNavigation(); - }).then(() => { - if (this.attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !this.attempt.finishedOffline) { - // Attempt not overdue and not finished in offline, load page. - return this.loadPage(this.attempt.currentpage).then(() => { - this.initTimer(); - }); - } else { - // Attempt is overdue or finished in offline, we can only load the summary. - return this.loadSummary(); - } - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true); - }); - } - - /** - * Quiz time has finished. - */ - timeUp(): void { - if (this.timeUpCalled) { - return; - } - - this.timeUpCalled = true; - this.finishAttempt(false, true); - } -} diff --git a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html deleted file mode 100644 index 69d32d7ab..000000000 --- a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html +++ /dev/null @@ -1,27 +0,0 @@ - - - {{ title | translate }} - - - - - - - -
- - - -

Couldn't find the directive to render this access rule.

-
- -
- - -
-
-
diff --git a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.module.ts b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.module.ts deleted file mode 100644 index 655074507..000000000 --- a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { AddonModQuizPreflightModalPage } from './preflight-modal'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonModQuizPreflightModalPage - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonModQuizPreflightModalPage), - TranslateModule.forChild() - ] -}) -export class AddonModQuizPreflightModalModule {} diff --git a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts deleted file mode 100644 index 794b15b8e..000000000 --- a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts +++ /dev/null @@ -1,137 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, ViewController, NavParams, Content } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; - -/** - * Modal that renders the access rules for a quiz. - */ -@IonicPage({ segment: 'addon-mod-quiz-preflight-modal' }) -@Component({ - selector: 'page-addon-mod-quiz-preflight-modal', - templateUrl: 'preflight-modal.html', -}) -export class AddonModQuizPreflightModalPage implements OnInit { - - @ViewChild(Content) content: Content; - @ViewChild('preflightFormEl') formElement: ElementRef; - - preflightForm: FormGroup; - title: string; - accessRulesData: {component: any, data: any}[] = []; // Components and data for each access rule. - loaded: boolean; - - protected quiz: any; - protected attempt: any; - protected prefetch: boolean; - protected siteId: string; - protected rules: string[]; - - constructor(params: NavParams, - fb: FormBuilder, - translate: TranslateService, - sitesProvider: CoreSitesProvider, - protected viewCtrl: ViewController, - protected accessRuleDelegate: AddonModQuizAccessRuleDelegate, - protected injector: Injector, - protected domUtils: CoreDomUtilsProvider, - protected eventsProvider: CoreEventsProvider) { - - this.title = params.get('title') || translate.instant('addon.mod_quiz.startattempt'); - this.quiz = params.get('quiz'); - this.attempt = params.get('attempt'); - this.prefetch = params.get('prefetch'); - this.siteId = params.get('siteId') || sitesProvider.getCurrentSiteId(); - this.rules = params.get('rules') || []; - - // Create an empty form group. The controls will be added by the access rules components. - this.preflightForm = fb.group({}); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const promises = []; - - this.rules.forEach((rule) => { - // Check if preflight is required for rule and, if so, get the component to render it. - promises.push(this.accessRuleDelegate.isPreflightCheckRequiredForRule(rule, this.quiz, this.attempt, this.prefetch, - this.siteId).then((required) => { - - if (required) { - return this.accessRuleDelegate.getPreflightComponent(rule, this.injector).then((component) => { - if (component) { - this.accessRulesData.push({ - component: component, - data: { - rule: rule, - quiz: this.quiz, - attempt: this.attempt, - prefetch: this.prefetch, - form: this.preflightForm, - siteId: this.siteId - } - }); - } - }); - } - })); - }); - - Promise.all(promises).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error loading rules'); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Check that the data is valid and send it back. - * - * @param e Event. - */ - sendData(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - if (!this.preflightForm.valid) { - // Form not valid. Scroll to the first element with errors. - if (!this.domUtils.scrollToInputError(this.content)) { - // Input not found, show an error modal. - this.domUtils.showErrorModal('core.errorinvalidform', true); - } - } else { - this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.siteId); - - this.viewCtrl.dismiss(this.preflightForm.value); - } - } - - /** - * Close modal. - */ - closeModal(): void { - this.domUtils.triggerFormCancelledEvent(this.formElement, this.siteId); - - this.viewCtrl.dismiss(); - } -} diff --git a/src/addon/mod/quiz/pages/review/review.html b/src/addon/mod/quiz/pages/review/review.html deleted file mode 100644 index e61d8388b..000000000 --- a/src/addon/mod/quiz/pages/review/review.html +++ /dev/null @@ -1,104 +0,0 @@ - - - {{ 'addon.mod_quiz.review' | translate }} - - - - - - - - - - - - - - - -
-

{{ 'addon.mod_quiz.reviewofpreview' | translate }}

-

{{ 'addon.mod_quiz.reviewofattempt' | translate:{$a: attempt.attempt} }}

-
-
- - -

{{ 'addon.mod_quiz.startedon' | translate }}

-

{{ attempt.timestart * 1000 | coreFormatDate }}

-
- -

{{ 'addon.mod_quiz.attemptstate' | translate }}

-

{{ attempt.readableState }}

-
- -

{{ 'addon.mod_quiz.completedon' | translate }}

-

{{ attempt.timefinish * 1000 | coreFormatDate }}

-
- -

{{ 'addon.mod_quiz.timetaken' | translate }}

-

{{ attempt.timeTaken }}

-
- -

{{ 'addon.mod_quiz.overdue' | translate }}

-

{{ attempt.overTime }}

-
- -

{{ 'addon.mod_quiz.marks' | translate }}

-

{{ attempt.readableMark }}

-
- -

{{ 'addon.mod_quiz.grade' | translate }}

-

{{ attempt.readableGrade }}

-
- -

{{ data.title }}

- -
-
-
- - -
- - - - -
- - - -

{{ 'core.question.questionno' | translate:{$a: question.number} }}

-

{{ 'core.question.information' | translate }}

- -

{{question.status}}

-

{{question.readableMark}}

-
-
- - -
-
- - - -
-
-
- - - - - - - - - - - - - - - - diff --git a/src/addon/mod/quiz/pages/review/review.module.ts b/src/addon/mod/quiz/pages/review/review.module.ts deleted file mode 100644 index d3b30b8bc..000000000 --- a/src/addon/mod/quiz/pages/review/review.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreQuestionComponentsModule } from '@core/question/components/components.module'; -import { AddonModQuizReviewPage } from './review'; - -@NgModule({ - declarations: [ - AddonModQuizReviewPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreQuestionComponentsModule, - IonicPageModule.forChild(AddonModQuizReviewPage), - TranslateModule.forChild() - ], -}) -export class AddonModQuizReviewPageModule {} diff --git a/src/addon/mod/quiz/pages/review/review.scss b/src/addon/mod/quiz/pages/review/review.scss deleted file mode 100644 index bb7509562..000000000 --- a/src/addon/mod/quiz/pages/review/review.scss +++ /dev/null @@ -1,14 +0,0 @@ -ion-app.app-root page-addon-mod-quiz-review { - .item-radio-disabled, - .item-checkbox-disabled, - .item-select-disabled, - .item-input-disabled, - [disabled], - [readonly] { - opacity: 0.8; - - .label, .radio, .checkbox, .select-disabled, .core-correct-icon, [disabled], [readonly] { - opacity: 1; - } - } -} diff --git a/src/addon/mod/quiz/pages/review/review.ts b/src/addon/mod/quiz/pages/review/review.ts deleted file mode 100644 index 50dfd53b8..000000000 --- a/src/addon/mod/quiz/pages/review/review.ts +++ /dev/null @@ -1,301 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, ViewChild } from '@angular/core'; -import { IonicPage, NavParams, Content, ModalController, Modal } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { AddonModQuizProvider } from '../../providers/quiz'; -import { AddonModQuizHelperProvider } from '../../providers/helper'; - -/** - * Page that allows reviewing a quiz attempt. - */ -@IonicPage({ segment: 'addon-mod-quiz-review' }) -@Component({ - selector: 'page-addon-mod-quiz-review', - templateUrl: 'review.html', -}) -export class AddonModQuizReviewPage implements OnInit { - @ViewChild(Content) content: Content; - - attempt: any; // The attempt being reviewed. - component = AddonModQuizProvider.COMPONENT; // Component to link the files to. - componentId: number; // ID to use in conjunction with the component. - showAll: boolean; // Whether to view all questions in the same page. - numPages: number; // Number of pages. - showCompleted: boolean; // Whether to show completed time. - additionalData: any[]; // Additional data to display for the attempt. - loaded: boolean; // Whether data has been loaded. - navigation: any[]; // List of questions to navigate them. - questions: any[]; // Questions of the current page. - nextPage: number; // Next page. - previousPage: number; // Previous page. - navigationModal: Modal; // Modal to navigate through the questions. - - protected quiz: any; // The quiz the attempt belongs to. - protected courseId: number; // The course ID the quiz belongs to. - protected quizId: number; // Quiz ID the attempt belongs to. - protected attemptId: number; // The attempt being reviewed. - protected currentPage: number; // The current page being reviewed. - protected options: any; // Review options. - - constructor(navParams: NavParams, modalCtrl: ModalController, protected translate: TranslateService, - protected domUtils: CoreDomUtilsProvider, protected timeUtils: CoreTimeUtilsProvider, - protected quizProvider: AddonModQuizProvider, protected quizHelper: AddonModQuizHelperProvider, - protected questionHelper: CoreQuestionHelperProvider, protected textUtils: CoreTextUtilsProvider) { - - this.quizId = navParams.get('quizId'); - this.courseId = navParams.get('courseId'); - this.attemptId = navParams.get('attemptId'); - this.currentPage = navParams.get('page') || -1; - this.showAll = this.currentPage == -1; - - // Create the navigation modal. - this.navigationModal = modalCtrl.create('AddonModQuizNavigationModalPage', { - isReview: true, - page: this - }, { cssClass: 'core-modal-lateral', - showBackdrop: true, - enableBackdropDismiss: true, - enterAnimation: 'core-modal-lateral-transition', - leaveAnimation: 'core-modal-lateral-transition' }); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchData().then(() => { - this.quizProvider.logViewAttemptReview(this.attemptId, this.quizId, this.quiz.name).catch((error) => { - // Ignore errors. - }); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Change the current page. If slot is supplied, try to scroll to that question. - * - * @param page Page to load. -1 means all questions in same page. - * @param fromModal Whether the page was selected using the navigation modal. - * @param slot Slot of the question to scroll to. - */ - changePage(page: number, fromModal?: boolean, slot?: number): void { - if (typeof slot != 'undefined' && (this.attempt.currentpage == -1 || page == this.currentPage)) { - // Scrol to a certain question in the current page. - this.scrollToQuestion(slot); - - return; - } else if (page == this.currentPage) { - // If the user is navigating to the current page and no question specified, we do nothing. - return; - } - - this.loaded = false; - this.domUtils.scrollToTop(this.content); - - this.loadPage(page).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true); - }).finally(() => { - this.loaded = true; - - if (typeof slot != 'undefined') { - // Scroll to the question. Give some time to the questions to render. - setTimeout(() => { - this.scrollToQuestion(slot); - }, 2000); - } - }); - } - - /** - * Convenience function to get the quiz data. - * - * @return Promise resolved when done. - */ - protected fetchData(): Promise { - return this.quizProvider.getQuizById(this.courseId, this.quizId).then((quizData) => { - this.quiz = quizData; - this.componentId = this.quiz.coursemodule; - - return this.quizProvider.getCombinedReviewOptions(this.quizId, {cmId: this.quiz.coursemodule}).then((result) => { - this.options = result; - - // Load the navigation data. - return this.loadNavigation().then(() => { - // Load questions. - return this.loadPage(this.currentPage); - }); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquiz', true); - }); - } - - /** - * Load a page questions. - * - * @param page The page to load. - * @return Promise resolved when done. - */ - protected loadPage(page: number): Promise { - return this.quizProvider.getAttemptReview(this.attemptId, {page, cmId: this.quiz.coursemodule}).then((data) => { - this.attempt = data.attempt; - this.attempt.currentpage = page; - this.currentPage = page; - - // Set the summary data. - this.setSummaryCalculatedData(data); - - this.questions = data.questions; - this.nextPage = page == -1 ? undefined : page + 1; - this.previousPage = page - 1; - - this.questions.forEach((question) => { - // Get the readable mark for each question. - question.readableMark = this.quizHelper.getQuestionMarkFromHtml(question.html); - - // Extract the question info box. - this.questionHelper.extractQuestionInfoBox(question, '.info'); - - // Set the preferred behaviour. - question.preferredBehaviour = this.quiz.preferredbehaviour; - }); - }); - } - - /** - * Load data to navigate the questions using the navigation modal. - * - * @return Promise resolved when done. - */ - protected loadNavigation(): Promise { - // Get all questions in single page to retrieve all the questions. - return this.quizProvider.getAttemptReview(this.attemptId, {page: -1, cmId: this.quiz.coursemodule}).then((data) => { - const lastQuestion = data.questions[data.questions.length - 1]; - - data.questions.forEach((question) => { - question.stateClass = this.questionHelper.getQuestionStateClass(question.state); - }); - - this.navigation = data.questions; - this.numPages = lastQuestion ? lastQuestion.page + 1 : 0; - }); - } - - /** - * Refreshes data. - * - * @param refresher Refresher - */ - refreshData(refresher: any): void { - const promises = []; - - promises.push(this.quizProvider.invalidateQuizData(this.courseId)); - promises.push(this.quizProvider.invalidateCombinedReviewOptionsForUser(this.quizId)); - promises.push(this.quizProvider.invalidateAttemptReview(this.attemptId)); - - Promise.all(promises).finally(() => { - return this.fetchData(); - }).finally(() => { - refresher.complete(); - }); - } - - /** - * Scroll to a certain question. - * - * @param slot Slot of the question to scroll to. - */ - protected scrollToQuestion(slot: number): void { - this.domUtils.scrollToElementBySelector(this.content, '#addon-mod_quiz-question-' + slot); - } - - /** - * Calculate review summary data. - * - * @param data Result of getAttemptReview. - */ - protected setSummaryCalculatedData(data: any): void { - - this.attempt.readableState = this.quizProvider.getAttemptReadableStateName(this.attempt.state); - - if (this.attempt.state == AddonModQuizProvider.ATTEMPT_FINISHED) { - this.showCompleted = true; - this.additionalData = data.additionaldata; - - const timeTaken = this.attempt.timefinish - this.attempt.timestart; - if (timeTaken) { - // Format time taken. - this.attempt.timeTaken = this.timeUtils.formatTime(timeTaken); - - // Calculate overdue time. - if (this.quiz.timelimit && timeTaken > this.quiz.timelimit + 60) { - this.attempt.overTime = this.timeUtils.formatTime(timeTaken - this.quiz.timelimit); - } - } - - // Treat grade. - if (this.options.someoptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX && - this.quizProvider.quizHasGrades(this.quiz)) { - - if (data.grade === null || typeof data.grade == 'undefined') { - this.attempt.readableGrade = this.quizProvider.formatGrade(data.grade, this.quiz.decimalpoints); - } else { - // Show raw marks only if they are different from the grade (like on the entry page). - if (this.quiz.grade != this.quiz.sumgrades) { - this.attempt.readableMark = this.translate.instant('addon.mod_quiz.outofshort', {$a: { - grade: this.quizProvider.formatGrade(this.attempt.sumgrades, this.quiz.decimalpoints), - maxgrade: this.quizProvider.formatGrade(this.quiz.sumgrades, this.quiz.decimalpoints) - }}); - } - - // Now the scaled grade. - const gradeObject: any = { - grade: this.quizProvider.formatGrade(data.grade, this.quiz.decimalpoints), - maxgrade: this.quizProvider.formatGrade(this.quiz.grade, this.quiz.decimalpoints) - }; - - if (this.quiz.grade != 100) { - gradeObject.percent = this.textUtils.roundToDecimals(this.attempt.sumgrades * 100 / this.quiz.sumgrades, 0); - this.attempt.readableGrade = this.translate.instant('addon.mod_quiz.outofpercent', {$a: gradeObject}); - } else { - this.attempt.readableGrade = this.translate.instant('addon.mod_quiz.outof', {$a: gradeObject}); - } - } - } - - // Treat additional data. - this.additionalData.forEach((data) => { - // Remove help links from additional data. - data.content = this.domUtils.removeElementFromHtml(data.content, '.helptooltip'); - }); - } - } - - /** - * Switch mode: all questions in same page OR one page at a time. - */ - switchMode(): void { - this.showAll = !this.showAll; - - // Load all questions or first page, depending on the mode. - this.loadPage(this.showAll ? -1 : 0); - } -} diff --git a/src/addon/mod/quiz/providers/access-rules-delegate.ts b/src/addon/mod/quiz/providers/access-rules-delegate.ts deleted file mode 100644 index 919f8f1a2..000000000 --- a/src/addon/mod/quiz/providers/access-rules-delegate.ts +++ /dev/null @@ -1,294 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; - -/** - * Interface that all access rules handlers must implement. - */ -export interface AddonModQuizAccessRuleHandler extends CoreDelegateHandler { - - /** - * Name of the rule the handler supports. E.g. 'password'. - */ - ruleName: string; - - /** - * Whether the rule requires a preflight check when prefetch/start/continue an attempt. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Whether the rule requires a preflight check. - */ - isPreflightCheckRequired(quiz: any, attempt?: any, prefetch?: boolean, siteId?: string): boolean | Promise; - - /** - * Add preflight data that doesn't require user interaction. The data should be added to the preflightData param. - * - * @param quiz The quiz the rule belongs to. - * @param preflightData Object where to add the preflight data. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done if async, void if it's synchronous. - */ - getFixedPreflightData?(quiz: any, preflightData: any, attempt?: any, prefetch?: boolean, siteId?: string): void | Promise; - - /** - * Return the Component to use to display the access rule preflight. - * Implement this if your access rule requires a preflight check with user interaction. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getPreflightComponent?(injector: Injector): any | Promise; - - /** - * Function called when the preflight check has passed. This is a chance to record that fact in some way. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. - * @param preflightData Preflight data gathered. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done if async, void if it's synchronous. - */ - notifyPreflightCheckPassed?(quiz: any, attempt: any, preflightData: any, prefetch?: boolean, siteId?: string) - : void | Promise; - - /** - * Function called when the preflight check fails. This is a chance to record that fact in some way. - * - * @param quiz The quiz the rule belongs to. - * @param attempt The attempt started/continued. - * @param preflightData Preflight data gathered. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done if async, void if it's synchronous. - */ - notifyPreflightCheckFailed?(quiz: any, attempt: any, preflightData: any, prefetch?: boolean, siteId?: string) - : void | Promise; - - /** - * Whether or not the time left of an attempt should be displayed. - * - * @param attempt The attempt. - * @param endTime The attempt end time (in seconds). - * @param timeNow The current time in seconds. - * @return Whether it should be displayed. - */ - shouldShowTimeLeft?(attempt: any, endTime: number, timeNow: number): boolean; -} - -/** - * Delegate to register access rules for quiz module. - */ -@Injectable() -export class AddonModQuizAccessRuleDelegate extends CoreDelegate { - - protected handlerNameProperty = 'ruleName'; - - constructor(logger: CoreLoggerProvider, sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, - protected utils: CoreUtilsProvider) { - super('AddonModQuizAccessRulesDelegate', logger, sitesProvider, eventsProvider); - } - - /** - * Get the handler for a certain rule. - * - * @param ruleName Name of the access rule. - * @return Handler. Undefined if no handler found for the rule. - */ - getAccessRuleHandler(ruleName: string): AddonModQuizAccessRuleHandler { - return this.getHandler(ruleName, true); - } - - /** - * Given a list of rules, get some fixed preflight data (data that doesn't require user interaction). - * - * @param rules List of active rules names. - * @param quiz Quiz. - * @param preflightData Object where to store the preflight data. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when all the data has been gathered. - */ - getFixedPreflightData(rules: string[], quiz: any, preflightData: any, attempt?: any, prefetch?: boolean, siteId?: string) - : Promise { - rules = rules || []; - - const promises = []; - rules.forEach((rule) => { - promises.push(Promise.resolve( - this.executeFunctionOnEnabled(rule, 'getFixedPreflightData', [quiz, preflightData, attempt, prefetch, siteId]) - )); - }); - - return this.utils.allPromises(promises).catch(() => { - // Never reject. - }); - } - - /** - * Get the Component to use to display the access rule preflight. - * - * @param injector Injector. - * @return Promise resolved with the component to use, undefined if not found. - */ - getPreflightComponent(rule: string, injector: Injector): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(rule, 'getPreflightComponent', [injector])); - } - - /** - * Check if an access rule is supported. - * - * @param ruleName Name of the rule. - * @return Whether it's supported. - */ - isAccessRuleSupported(ruleName: string): boolean { - return this.hasHandler(ruleName, true); - } - - /** - * Given a list of rules, check if preflight check is required. - * - * @param rules List of active rules names. - * @param quiz Quiz. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether it's required. - */ - isPreflightCheckRequired(rules: string[], quiz: any, attempt: any, prefetch?: boolean, siteId?: string): Promise { - rules = rules || []; - - const promises = []; - let isRequired = false; - - rules.forEach((rule) => { - promises.push(this.isPreflightCheckRequiredForRule(rule, quiz, attempt, prefetch, siteId).then((required) => { - if (required) { - isRequired = true; - } - })); - }); - - return this.utils.allPromises(promises).then(() => { - return isRequired; - }).catch(() => { - // Never reject. - return isRequired; - }); - } - - /** - * Check if preflight check is required for a certain rule. - * - * @param rule Rule name. - * @param quiz Quiz. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether it's required. - */ - isPreflightCheckRequiredForRule(rule: string, quiz: any, attempt: any, prefetch?: boolean, siteId?: string): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(rule, 'isPreflightCheckRequired', [quiz, attempt, prefetch, siteId])); - } - - /** - * Notify all rules that the preflight check has passed. - * - * @param rules List of active rules names. - * @param quiz Quiz. - * @param attempt Attempt. - * @param preflightData Preflight data gathered. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - notifyPreflightCheckPassed(rules: string[], quiz: any, attempt: any, preflightData: any, prefetch?: boolean, siteId?: string) - : Promise { - rules = rules || []; - - const promises = []; - rules.forEach((rule) => { - promises.push(Promise.resolve( - this.executeFunctionOnEnabled(rule, 'notifyPreflightCheckPassed', [quiz, attempt, preflightData, prefetch, siteId]) - )); - }); - - return this.utils.allPromises(promises).catch(() => { - // Never reject. - }); - } - - /** - * Notify all rules that the preflight check has failed. - * - * @param rules List of active rules names. - * @param quiz Quiz. - * @param attempt Attempt. - * @param preflightData Preflight data gathered. - * @param prefetch Whether the user is prefetching the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - notifyPreflightCheckFailed(rules: string[], quiz: any, attempt: any, preflightData: any, prefetch?: boolean, siteId?: string) - : Promise { - rules = rules || []; - - const promises = []; - rules.forEach((rule) => { - promises.push(Promise.resolve( - this.executeFunctionOnEnabled(rule, 'notifyPreflightCheckFailed', [quiz, attempt, preflightData, prefetch, siteId]) - )); - }); - - return this.utils.allPromises(promises).catch(() => { - // Never reject. - }); - } - - /** - * Whether or not the time left of an attempt should be displayed. - * - * @param rules List of active rules names. - * @param attempt The attempt. - * @param endTime The attempt end time (in seconds). - * @param timeNow The current time in seconds. - * @return Whether it should be displayed. - */ - shouldShowTimeLeft(rules: string[], attempt: any, endTime: number, timeNow: number): boolean { - rules = rules || []; - - for (const i in rules) { - const rule = rules[i]; - - if (this.executeFunctionOnEnabled(rule, 'shouldShowTimeLeft', [attempt, endTime, timeNow])) { - return true; - } - } - - return false; - } -} diff --git a/src/addon/mod/quiz/providers/grade-link-handler.ts b/src/addon/mod/quiz/providers/grade-link-handler.ts deleted file mode 100644 index 87b85fd38..000000000 --- a/src/addon/mod/quiz/providers/grade-link-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksModuleGradeHandler } from '@core/contentlinks/classes/module-grade-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModQuizProvider } from './quiz'; - -/** - * Handler to treat links to quiz grade. - */ -@Injectable() -export class AddonModQuizGradeLinkHandler extends CoreContentLinksModuleGradeHandler { - name = 'AddonModQuizGradeLinkHandler'; - canReview = false; - - constructor(courseHelper: CoreCourseHelperProvider, domUtils: CoreDomUtilsProvider, sitesProvider: CoreSitesProvider, - protected quizProvider: AddonModQuizProvider) { - super(courseHelper, domUtils, sitesProvider, 'AddonModQuiz', 'quiz'); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.quizProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/quiz/providers/helper.ts b/src/addon/mod/quiz/providers/helper.ts deleted file mode 100644 index 084cb6303..000000000 --- a/src/addon/mod/quiz/providers/helper.ts +++ /dev/null @@ -1,349 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { ModalController, NavController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModQuizProvider } from './quiz'; -import { AddonModQuizOfflineProvider } from './quiz-offline'; -import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; - -/** - * Helper service that provides some features for quiz. - */ -@Injectable() -export class AddonModQuizHelperProvider { - - constructor(private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private utils: CoreUtilsProvider, - private accessRuleDelegate: AddonModQuizAccessRuleDelegate, private quizProvider: AddonModQuizProvider, - private modalCtrl: ModalController, private quizOfflineProvider: AddonModQuizOfflineProvider, - private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider, - private linkHelper: CoreContentLinksHelperProvider) { } - - /** - * Validate a preflight data or show a modal to input the preflight data if required. - * It calls AddonModQuizProvider.startAttempt if a new attempt is needed. - * - * @param quiz Quiz. - * @param accessInfo Quiz access info returned by AddonModQuizProvider.getQuizAccessInformation. - * @param preflightData Object where to store the preflight data. - * @param attempt Attempt to continue. Don't pass any value if the user needs to start a new attempt. - * @param offline Whether the attempt is offline. - * @param prefetch Whether user is prefetching. - * @param title The title to display in the modal and in the submit button. - * @param siteId Site ID. If not defined, current site. - * @param retrying Whether we're retrying after a failure. - * @return Promise resolved when the preflight data is validated. The resolve param is the attempt. - */ - getAndCheckPreflightData(quiz: any, accessInfo: any, preflightData: any, attempt: any, offline?: boolean, prefetch?: boolean, - title?: string, siteId?: string, retrying?: boolean): Promise { - - const rules = accessInfo && accessInfo.activerulenames; - let isPreflightCheckRequired = false; - - // Check if the user needs to input preflight data. - return this.accessRuleDelegate.isPreflightCheckRequired(rules, quiz, attempt, prefetch, siteId).then((required) => { - isPreflightCheckRequired = required; - - if (required) { - // Preflight check is required but no preflightData has been sent. Show a modal with the preflight form. - return this.getPreflightData(quiz, accessInfo, attempt, prefetch, title, siteId).then((data) => { - // Data entered by the user, add it to preflight data and check it again. - Object.assign(preflightData, data); - }); - } - }).then(() => { - // Get some fixed preflight data from access rules (data that doesn't require user interaction). - return this.accessRuleDelegate.getFixedPreflightData(rules, quiz, preflightData, attempt, prefetch, siteId); - }).then(() => { - - // All the preflight data is gathered, now validate it. - return this.validatePreflightData(quiz, accessInfo, preflightData, attempt, offline, prefetch, siteId) - .catch((error) => { - - if (prefetch) { - return Promise.reject(error); - } else if (retrying && !isPreflightCheckRequired) { - // We're retrying after a failure, but the preflight check wasn't required. - // This means there's something wrong with some access rule or user is offline and data isn't cached. - // Don't retry again because it would lead to an infinite loop. - return Promise.reject(error); - } else { - // Show error and ask for the preflight again. - // Wait to show the error because we want it to be shown over the preflight modal. - setTimeout(() => { - this.domUtils.showErrorModalDefault(error, 'core.error', true); - }, 100); - - return this.getAndCheckPreflightData(quiz, accessInfo, preflightData, attempt, offline, prefetch, - title, siteId, true); - } - }); - }); - } - - /** - * Get the preflight data from the user using a modal. - * - * @param quiz Quiz. - * @param accessInfo Quiz access info returned by AddonModQuizProvider.getQuizAccessInformation. - * @param attempt The attempt started/continued. If not supplied, user is starting a new attempt. - * @param prefetch Whether the user is prefetching the quiz. - * @param title The title to display in the modal and in the submit button. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the preflight data. Rejected if user cancels. - */ - getPreflightData(quiz: any, accessInfo: any, attempt: any, prefetch?: boolean, title?: string, siteId?: string): Promise { - const notSupported: string[] = []; - const rules = accessInfo && accessInfo.activerulenames; - - // Check if there is any unsupported rule. - rules.forEach((rule) => { - if (!this.accessRuleDelegate.isAccessRuleSupported(rule)) { - notSupported.push(rule); - } - }); - - if (notSupported.length) { - return Promise.reject(this.translate.instant('addon.mod_quiz.errorrulesnotsupported') + ' ' + - JSON.stringify(notSupported)); - } - - // Create and show the modal. - const modal = this.modalCtrl.create('AddonModQuizPreflightModalPage', { - title: title, - quiz: quiz, - attempt: attempt, - prefetch: !!prefetch, - siteId: siteId, - rules: rules - }); - - modal.present(); - - // Wait for modal to be dismissed. - return new Promise((resolve, reject): void => { - modal.onDidDismiss((data) => { - if (typeof data != 'undefined') { - resolve(data); - } else { - reject(this.domUtils.createCanceledError()); - } - }); - }); - } - - /** - * Gets the mark string from a question HTML. - * Example result: "Marked out of 1.00". - * - * @param html Question's HTML. - * @return Question's mark. - */ - getQuestionMarkFromHtml(html: string): string { - const element = this.domUtils.convertToElement(html); - - return this.domUtils.getContentsOfElement(element, '.grade'); - } - - /** - * Get a quiz ID by attempt ID. - * - * @param attemptId Attempt ID. - * @param options Other options. - * @return Promise resolved with the quiz ID. - */ - getQuizIdByAttemptId(attemptId: number, options: {cmId?: number, siteId?: string} = {}): Promise { - // Use getAttemptReview to retrieve the quiz ID. - return this.quizProvider.getAttemptReview(attemptId, options).then((reviewData) => { - if (reviewData.attempt && reviewData.attempt.quiz) { - return reviewData.attempt.quiz; - } - - return Promise.reject(null); - }); - } - - /** - * Handle a review link. - * - * @param navCtrl Nav controller, can be undefined/null. - * @param attemptId Attempt ID. - * @param page Page to load, -1 to all questions in same page. - * @param courseId Course ID. - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - handleReviewLink(navCtrl: NavController, attemptId: number, page?: number, courseId?: number, quizId?: number, - siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const modal = this.domUtils.showModalLoading(); - let promise; - - if (quizId) { - promise = Promise.resolve(quizId); - } else { - // Retrieve the quiz ID using the attempt ID. - promise = this.getQuizIdByAttemptId(attemptId, {siteId}); - } - - return promise.then((id) => { - quizId = id; - - // Get the courseId if we don't have it. - if (courseId) { - return courseId; - } else { - return this.courseHelper.getModuleCourseIdByInstance(quizId, 'quiz', siteId); - } - }).then((courseId) => { - // Go to the review page. - const pageParams = { - quizId: quizId, - attemptId: attemptId, - courseId: courseId, - page: isNaN(page) ? -1 : page - }; - - return this.linkHelper.goInSite(navCtrl, 'AddonModQuizReviewPage', pageParams, siteId); - }).catch((error) => { - - this.domUtils.showErrorModalDefault(error, 'An error occurred while loading the required data.'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Add some calculated data to the attempt. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param highlight Whether we should check if attempt should be highlighted. - * @param bestGrade Quiz's best grade (formatted). Required if highlight=true. - */ - setAttemptCalculatedData(quiz: any, attempt: any, highlight?: boolean, bestGrade?: string): void { - - attempt.rescaledGrade = this.quizProvider.rescaleGrade(attempt.sumgrades, quiz, false); - attempt.finished = this.quizProvider.isAttemptFinished(attempt.state); - attempt.readableState = this.quizProvider.getAttemptReadableState(quiz, attempt); - - if (quiz.showMarkColumn && attempt.finished) { - attempt.readableMark = this.quizProvider.formatGrade(attempt.sumgrades, quiz.decimalpoints); - } else { - attempt.readableMark = ''; - } - - if (quiz.showGradeColumn && attempt.finished) { - attempt.readableGrade = this.quizProvider.formatGrade(attempt.rescaledGrade, quiz.decimalpoints); - - // Highlight the highest grade if appropriate. - attempt.highlightGrade = highlight && !attempt.preview && attempt.state == AddonModQuizProvider.ATTEMPT_FINISHED && - attempt.readableGrade == bestGrade; - } else { - attempt.readableGrade = ''; - } - } - - /** - * Add some calculated data to the quiz. - * - * @param quiz Quiz. - * @param options Options returned by AddonModQuizProvider.getCombinedReviewOptions. - */ - setQuizCalculatedData(quiz: any, options: any): void { - quiz.sumGradesFormatted = this.quizProvider.formatGrade(quiz.sumgrades, quiz.decimalpoints); - quiz.gradeFormatted = this.quizProvider.formatGrade(quiz.grade, quiz.decimalpoints); - - quiz.showAttemptColumn = quiz.attempts != 1; - quiz.showGradeColumn = options.someoptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX && - this.quizProvider.quizHasGrades(quiz); - quiz.showMarkColumn = quiz.showGradeColumn && quiz.grade != quiz.sumgrades; - quiz.showFeedbackColumn = quiz.hasfeedback && options.alloptions.overallfeedback; - } - - /** - * Validate the preflight data. It calls AddonModQuizProvider.startAttempt if a new attempt is needed. - * - * @param quiz Quiz. - * @param accessInfo Quiz access info returned by AddonModQuizProvider.getQuizAccessInformation. - * @param preflightData Object where to store the preflight data. - * @param attempt Attempt to continue. Don't pass any value if the user needs to start a new attempt. - * @param offline Whether the attempt is offline. - * @param sent Whether preflight data has been entered by the user. - * @param prefetch Whether user is prefetching. - * @param title The title to display in the modal and in the submit button. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the preflight data is validated. - */ - validatePreflightData(quiz: any, accessInfo: any, preflightData: any, attempt: any, offline?: boolean, prefetch?: boolean, - siteId?: string): Promise { - - const rules = accessInfo && accessInfo.activerulenames; - const modOptions = { - cmId: quiz.coursemodule, - readingStrategy: offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - let promise; - - if (attempt) { - if (attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !attempt.finishedOffline) { - // We're continuing an attempt. Call getAttemptData to validate the preflight data. - const page = attempt.currentpage; - - promise = this.quizProvider.getAttemptData(attempt.id, page, preflightData, modOptions).then(() => { - if (offline) { - // Get current page stored in local. - return this.quizOfflineProvider.getAttemptById(attempt.id).then((localAttempt) => { - attempt.currentpage = localAttempt.currentpage; - }).catch(() => { - // No local data. - }); - } - }); - } else { - // Attempt is overdue or finished in offline, we can only see the summary. - // Call getAttemptSummary to validate the preflight data. - promise = this.quizProvider.getAttemptSummary(attempt.id, preflightData, modOptions); - } - } else { - // We're starting a new attempt, call startAttempt. - promise = this.quizProvider.startAttempt(quiz.id, preflightData, false, siteId).then((att) => { - attempt = att; - }); - } - - return promise.then(() => { - // Preflight data validated. - this.accessRuleDelegate.notifyPreflightCheckPassed(rules, quiz, attempt, preflightData, prefetch, siteId); - - return attempt; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService returned an error, assume the preflight failed. - this.accessRuleDelegate.notifyPreflightCheckFailed(rules, quiz, attempt, preflightData, prefetch, siteId); - } - - return Promise.reject(error); - }); - } -} diff --git a/src/addon/mod/quiz/providers/index-link-handler.ts b/src/addon/mod/quiz/providers/index-link-handler.ts deleted file mode 100644 index dbb36cc8e..000000000 --- a/src/addon/mod/quiz/providers/index-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModQuizProvider } from './quiz'; - -/** - * Handler to treat links to quiz index. - */ -@Injectable() -export class AddonModQuizIndexLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModQuizIndexLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, protected quizProvider: AddonModQuizProvider) { - super(courseHelper, 'AddonModQuiz', 'quiz', 'q'); - } -} diff --git a/src/addon/mod/quiz/providers/list-link-handler.ts b/src/addon/mod/quiz/providers/list-link-handler.ts deleted file mode 100644 index 61bfaa8f0..000000000 --- a/src/addon/mod/quiz/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to quiz list page. - */ -@Injectable() -export class AddonModQuizListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModQuizListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModQuiz', 'quiz'); - } -} diff --git a/src/addon/mod/quiz/providers/module-handler.ts b/src/addon/mod/quiz/providers/module-handler.ts deleted file mode 100644 index 822f196d7..000000000 --- a/src/addon/mod/quiz/providers/module-handler.ts +++ /dev/null @@ -1,90 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModQuizIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support quiz modules. - */ -@Injectable() -export class AddonModQuizModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModQuiz'; - modName = 'quiz'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, - [CoreConstants.FEATURE_CONTROLS_GRADE_VISIBILITY]: true, - [CoreConstants.FEATURE_USES_QUESTIONS]: true - }; - - constructor(private courseProvider: CoreCourseProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_quiz-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModQuizIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModQuizIndexComponent; - } -} diff --git a/src/addon/mod/quiz/providers/prefetch-handler.ts b/src/addon/mod/quiz/providers/prefetch-handler.ts deleted file mode 100644 index d0847f847..000000000 --- a/src/addon/mod/quiz/providers/prefetch-handler.ts +++ /dev/null @@ -1,649 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider, CoreCourseCommonModWSOptions } from '@core/course/providers/course'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { AddonModQuizProvider } from './quiz'; -import { AddonModQuizHelperProvider } from './helper'; -import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; -import { AddonModQuizSyncProvider } from './quiz-sync'; -import { CoreConstants } from '@core/constants'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch quizzes. - */ -@Injectable() -export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModQuiz'; - modName = 'quiz'; - component = AddonModQuizProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^grades$|^gradeitems$|^questions$|^attempts$/; - - protected syncProvider: AddonModQuizSyncProvider; // It will be injected later to prevent circular dependencies. - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected injector: Injector, - protected quizProvider: AddonModQuizProvider, - protected textUtils: CoreTextUtilsProvider, - protected quizHelper: AddonModQuizHelperProvider, - protected accessRuleDelegate: AddonModQuizAccessRuleDelegate, - protected questionHelper: CoreQuestionHelperProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Download the module. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param dirPath Path of the directory where to store all the content files. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param canStart If true, start a new attempt if needed. - * @return Promise resolved when all content is downloaded. - */ - download(module: any, courseId: number, dirPath?: string, single?: boolean, canStart: boolean = true): Promise { - // Same implementation for download and prefetch. - return this.prefetch(module, courseId, single, dirPath, canStart); - } - - /** - * Get list of files. If not defined, we'll assume they're in module.contents. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the list of files. - */ - getFiles(module: any, courseId: number, single?: boolean): Promise { - return this.quizProvider.getQuiz(courseId, module.id).then((quiz) => { - const files = this.getIntroFilesFromInstance(module, quiz); - - return this.quizProvider.getUserAttempts(quiz.id, { - cmId: module.id, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - }).then((attempts) => { - return this.getAttemptsFeedbackFiles(quiz, attempts).then((attemptFiles) => { - return files.concat(attemptFiles); - }); - }); - }).catch(() => { - // Quiz not found, return empty list. - return []; - }); - } - - /** - * Get the list of downloadable files on feedback attemptss. - * - * @param quiz Quiz. - * @param attempts Quiz user attempts. - * @param siteId Site ID. If not defined, current site. - * @return List of Files. - */ - protected getAttemptsFeedbackFiles(quiz: any, attempts: any[], siteId?: string): Promise { - // We have quiz data, now we'll get specific data for each attempt. - const promises = []; - const getInlineFiles = this.sitesProvider.getCurrentSite() && - this.sitesProvider.getCurrentSite().isVersionGreaterEqualThan('3.2'); - let files = []; - - attempts.forEach((attempt) => { - if (this.quizProvider.isAttemptFinished(attempt.state)) { - // Attempt is finished, get feedback and review data. - - const attemptGrade = this.quizProvider.rescaleGrade(attempt.sumgrades, quiz, false); - if (typeof attemptGrade != 'undefined') { - promises.push(this.quizProvider.getFeedbackForGrade(quiz.id, Number(attemptGrade), { - cmId: quiz.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((feedback) => { - if (getInlineFiles && feedback.feedbackinlinefiles && feedback.feedbackinlinefiles.length) { - files = files.concat(feedback.feedbackinlinefiles); - } else if (feedback.feedbacktext && !getInlineFiles) { - files = files.concat( - this.filepoolProvider.extractDownloadableFilesFromHtmlAsFakeFileObjects(feedback.feedbacktext)); - } - })); - } - } - }); - - return Promise.all(promises).then(() => { - return files; - }); - } - - /** - * Gather some preflight data for an attempt. This function will start a new attempt if needed. - * - * @param quiz Quiz. - * @param accessInfo Quiz access info returned by AddonModQuizProvider.getQuizAccessInformation. - * @param attempt Attempt to continue. Don't pass any value if the user needs to start a new attempt. - * @param askPreflight Whether it should ask for preflight data if needed. - * @param modalTitle Lang key of the title to set to preflight modal (e.g. 'addon.mod_quiz.startattempt'). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the preflight data. - */ - getPreflightData(quiz: any, accessInfo: any, attempt?: any, askPreflight?: boolean, title?: string, siteId?: string) - : Promise { - const preflightData = {}; - let promise; - - if (askPreflight) { - // We can ask preflight, check if it's needed and get the data. - promise = this.quizHelper.getAndCheckPreflightData( - quiz, accessInfo, preflightData, attempt, false, true, title, siteId); - } else { - // Get some fixed preflight data from access rules (data that doesn't require user interaction). - const rules = accessInfo && accessInfo.activerulenames; - - promise = this.accessRuleDelegate.getFixedPreflightData(rules, quiz, preflightData, attempt, true, siteId).then(() => { - if (!attempt) { - // We need to create a new attempt. - return this.quizProvider.startAttempt(quiz.id, preflightData, false, siteId); - } - }); - } - - return promise.then(() => { - return preflightData; - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.quizProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - // Invalidate the calls required to check if a quiz is downloadable. - const promises = []; - - promises.push(this.quizProvider.invalidateQuizData(courseId)); - promises.push(this.quizProvider.invalidateUserAttemptsForUser(module.instance)); - - return Promise.all(promises); - } - - /** - * Check if a module can be downloaded. If the function is not defined, we assume that all modules are downloadable. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Whether the module can be downloaded. The promise should never be rejected. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - if (this.sitesProvider.getCurrentSite().isOfflineDisabled()) { - // Don't allow downloading the quiz if offline is disabled to prevent wasting a lot of data when opening it. - return false; - } - - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.quizProvider.getQuiz(courseId, module.id, {siteId}).then((quiz) => { - if (quiz.allowofflineattempts !== 1 || quiz.hasquestions === 0) { - return false; - } - - // Not downloadable if we reached max attempts or the quiz has an unfinished attempt. - return this.quizProvider.getUserAttempts(quiz.id, { - cmId: module.id, - siteId, - }).then((attempts) => { - const isLastFinished = !attempts.length || this.quizProvider.isAttemptFinished(attempts[attempts.length - 1].state); - - return quiz.attempts === 0 || quiz.attempts > attempts.length || !isLastFinished; - }); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.quizProvider.isPluginEnabled(); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @param canStart If true, start a new attempt if needed. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string, canStart: boolean = true): Promise { - if (module.attemptFinished) { - // Delete the value so it does not block anything if true. - delete module.attemptFinished; - - // Quiz got synced recently and an attempt has finished. Do not prefetch. - return Promise.resolve(); - } - - return this.prefetchPackage(module, courseId, single, this.prefetchQuiz.bind(this), undefined, canStart); - - } - - /** - * Prefetch a quiz. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @param canStart If true, start a new attempt if needed. - * @return Promise resolved when done. - */ - protected prefetchQuiz(module: any, courseId: number, single: boolean, siteId: string, canStart: boolean): Promise { - let attempts: any[], - startAttempt = false, - quiz, - quizAccessInfo, - attemptAccessInfo, - preflightData; - - const commonOptions = { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - const modOptions = { - cmId: module.id, - ...commonOptions, // Include all common options. - }; - - // Get quiz. - return this.quizProvider.getQuiz(courseId, module.id, commonOptions).then((quizData) => { - quiz = quizData; - - const promises = [], - introFiles = this.getIntroFilesFromInstance(module, quiz); - - // Prefetch some quiz data. - promises.push(this.quizProvider.getQuizAccessInformation(quiz.id, modOptions).then((info) => { - quizAccessInfo = info; - })); - promises.push(this.quizProvider.getQuizRequiredQtypes(quiz.id, modOptions)); - promises.push(this.quizProvider.getUserAttempts(quiz.id, modOptions).then((atts) => { - attempts = atts; - - return this.getAttemptsFeedbackFiles(quiz, attempts, siteId).then((attemptFiles) => { - return this.filepoolProvider.addFilesToQueue(siteId, attemptFiles, AddonModQuizProvider.COMPONENT, module.id); - }); - })); - promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, modOptions).then((info) => { - attemptAccessInfo = info; - })); - - promises.push(this.filepoolProvider.addFilesToQueue(siteId, introFiles, AddonModQuizProvider.COMPONENT, module.id)); - - return Promise.all(promises); - }).then(() => { - // Check if we need to start a new attempt. - let attempt = attempts[attempts.length - 1]; - - if (!canStart && !attempt) { - // No attempts and we won't start a new one, so we don't need preflight data. - return; - } - - if (canStart && (!attempt || this.quizProvider.isAttemptFinished(attempt.state))) { - // Check if the user can attempt the quiz. - if (attemptAccessInfo.preventnewattemptreasons.length) { - return Promise.reject(this.textUtils.buildMessage(attemptAccessInfo.preventnewattemptreasons)); - } - - startAttempt = true; - attempt = undefined; - } - - // Get the preflight data. This function will also start a new attempt if needed. - return this.getPreflightData(quiz, quizAccessInfo, attempt, single, 'core.download', siteId); - - }).then((data) => { - preflightData = data; - - const promises = []; - - if (startAttempt) { - // Re-fetch user attempts since we created a new one. - promises.push(this.quizProvider.getUserAttempts(quiz.id, modOptions).then((atts) => { - attempts = atts; - - return this.getAttemptsFeedbackFiles(quiz, attempts, siteId).then((attemptFiles) => { - return this.filepoolProvider.addFilesToQueue(siteId, attemptFiles, AddonModQuizProvider.COMPONENT, - module.id); - }); - })); - - // Update the download time to prevent detecting the new attempt as an update. - promises.push(this.filepoolProvider.updatePackageDownloadTime(siteId, AddonModQuizProvider.COMPONENT, module.id) - .catch(() => { - // Ignore errors. - })); - } - - // Fetch attempt related data. - promises.push(this.quizProvider.getCombinedReviewOptions(quiz.id, modOptions)); - promises.push(this.quizProvider.getUserBestGrade(quiz.id, modOptions)); - promises.push(this.quizProvider.getGradeFromGradebook(courseId, module.id, true, siteId).then((gradebookData) => { - if (typeof gradebookData.graderaw != 'undefined') { - return this.quizProvider.getFeedbackForGrade(quiz.id, gradebookData.graderaw, modOptions); - } - }).catch(() => { - // Ignore errors. - })); - promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, modOptions)); // Last attempt. - - return Promise.all(promises); - }).then(() => { - // We have quiz data, now we'll get specific data for each attempt. - const promises = []; - - attempts.forEach((attempt) => { - promises.push(this.prefetchAttempt(quiz, attempt, preflightData, siteId)); - }); - - return Promise.all(promises); - }).then(() => { - if (!canStart) { - // Nothing else to do. - return; - } - - // If there's nothing to send, mark the quiz as synchronized. - // We don't return the promises because it should be fast and we don't want to block the user for this. - if (!this.syncProvider) { - this.syncProvider = this.injector.get(AddonModQuizSyncProvider); - } - - this.syncProvider.hasDataToSync(quiz.id, siteId).then((hasData) => { - if (!hasData) { - this.syncProvider.setSyncTime(quiz.id, siteId); - } - }); - }); - } - - /** - * Prefetch all WS data for an attempt. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param preflightData Preflight required data (like password). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the prefetch is finished. Data returned is not reliable. - */ - prefetchAttempt(quiz: any, attempt: any, preflightData: any, siteId?: string): Promise { - const pages = this.quizProvider.getPagesFromLayout(attempt.layout), - promises = [], - isSequential = this.quizProvider.isNavigationSequential(quiz); - - const modOptions = { - cmId: quiz.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - if (this.quizProvider.isAttemptFinished(attempt.state)) { - // Attempt is finished, get feedback and review data. - - const attemptGrade = this.quizProvider.rescaleGrade(attempt.sumgrades, quiz, false); - if (typeof attemptGrade != 'undefined') { - promises.push(this.quizProvider.getFeedbackForGrade(quiz.id, Number(attemptGrade), modOptions)); - } - - // Get the review for each page. - pages.forEach((page) => { - promises.push(this.quizProvider.getAttemptReview(attempt.id, { - page, - ...modOptions, // Include all options. - }).catch(() => { - // Ignore failures, maybe the user can't review the attempt. - })); - }); - - // Get the review for all questions in same page. - promises.push(this.quizProvider.getAttemptReview(attempt.id, { - page: -1, - ...modOptions, // Include all options. - }).then((data) => { - // Download the files inside the questions. - const questionPromises = []; - - data.questions.forEach((question) => { - questionPromises.push(this.questionHelper.prefetchQuestionFiles( - question, this.component, quiz.coursemodule, siteId, attempt.uniqueid)); - }); - - return Promise.all(questionPromises); - }, () => { - // Ignore failures, maybe the user can't review the attempt. - })); - } else { - - // Attempt not finished, get data needed to continue the attempt. - promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, attempt.id, modOptions)); - promises.push(this.quizProvider.getAttemptSummary(attempt.id, preflightData, modOptions)); - - if (attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { - // Get data for each page. - pages.forEach((page) => { - if (isSequential && page < attempt.currentpage) { - // Sequential quiz, cannot get pages before the current one. - return; - } - - promises.push(this.quizProvider.getAttemptData(attempt.id, page, preflightData, modOptions).then((data) => { - // Download the files inside the questions. - const questionPromises = []; - - data.questions.forEach((question) => { - questionPromises.push(this.questionHelper.prefetchQuestionFiles( - question, this.component, quiz.coursemodule, siteId, attempt.uniqueid)); - }); - - return Promise.all(questionPromises); - })); - }); - } - } - - return Promise.all(promises); - } - - /** - * Prefetches some data for a quiz and its last attempt. - * This function will NOT start a new attempt, it only reads data for the quiz and the last attempt. - * - * @param quiz Quiz. - * @param askPreflight Whether it should ask for preflight data if needed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetchQuizAndLastAttempt(quiz: any, askPreflight?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - const modOptions = { - cmId: quiz.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - let attempts, - quizAccessInfo, - preflightData, - lastAttempt; - - // Get quiz data. - promises.push(this.quizProvider.getQuizAccessInformation(quiz.id, modOptions).then((info) => { - quizAccessInfo = info; - })); - promises.push(this.quizProvider.getQuizRequiredQtypes(quiz.id, modOptions)); - promises.push(this.quizProvider.getCombinedReviewOptions(quiz.id, modOptions)); - promises.push(this.quizProvider.getUserBestGrade(quiz.id, modOptions)); - promises.push(this.quizProvider.getUserAttempts(quiz.id, modOptions).then((atts) => { - attempts = atts; - })); - promises.push(this.quizProvider.getGradeFromGradebook(quiz.course, quiz.coursemodule, true, siteId) - .then((gradebookData) => { - if (typeof gradebookData.graderaw != 'undefined') { - return this.quizProvider.getFeedbackForGrade(quiz.id, gradebookData.graderaw, modOptions); - } - }).catch(() => { - // Ignore errors. - })); - promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, modOptions)); // Last attempt. - - return Promise.all(promises).then(() => { - lastAttempt = attempts[attempts.length - 1]; - if (!lastAttempt) { - // No need to get attempt data, we don't need preflight data. - return; - } - - // Get the preflight data. - return this.getPreflightData(quiz, quizAccessInfo, lastAttempt, askPreflight, 'core.download', siteId); - - }).then((data) => { - preflightData = data; - - if (lastAttempt) { - // Get data for last attempt. - return this.prefetchAttempt(quiz, lastAttempt, preflightData, siteId); - } - }).then(() => { - // Prefetch finished, set the right status. - return this.setStatusAfterPrefetch(quiz, { - cmId: quiz.coursemodule, - attempts, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId, - }); - }); - } - - /** - * Set the right status to a quiz after prefetching. - * If the last attempt is finished or there isn't one, set it as not downloaded to show download icon. - * - * @param quiz Quiz. - * @param options Other options. - * @return Promise resolved when done. - */ - setStatusAfterPrefetch(quiz: any, options: AddonModQuizSetStatusAfterPrefetchOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - let status; - let attempts = options.attempts; - - if (!attempts) { - // Get the attempts. - promises.push(this.quizProvider.getUserAttempts(quiz.id, options).then((atts) => { - attempts = atts; - })); - } - - // Check the current status of the quiz. - promises.push(this.filepoolProvider.getPackageStatus(options.siteId, this.component, quiz.coursemodule).then((stat) => { - status = stat; - })); - - return Promise.all(promises).then(() => { - - if (status !== CoreConstants.NOT_DOWNLOADED) { - // Quiz was downloaded, set the new status. - // If no attempts or last is finished we'll mark it as not downloaded to show download icon. - const lastAttempt = attempts[attempts.length - 1], - isLastFinished = !lastAttempt || this.quizProvider.isAttemptFinished(lastAttempt.state), - newStatus = isLastFinished ? CoreConstants.NOT_DOWNLOADED : CoreConstants.DOWNLOADED; - - return this.filepoolProvider.storePackageStatus(options.siteId, newStatus, this.component, quiz.coursemodule); - } - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - if (!this.syncProvider) { - this.syncProvider = this.injector.get(AddonModQuizSyncProvider); - } - - return this.quizProvider.getQuiz(courseId, module.id, {siteId}).then((quiz) => { - return this.syncProvider.syncQuiz(quiz, false, siteId).then((results) => { - module.attemptFinished = (results && results.attemptFinished) || false; - - return results; - }).catch(() => { - // Ignore errors. - - module.attemptFinished = false; - }); - }); - } -} - -/** - * Options to pass to setStatusAfterPrefetch. - */ -export type AddonModQuizSetStatusAfterPrefetchOptions = CoreCourseCommonModWSOptions & { - attempts?: any[]; // List of attempts. If not provided, they will be calculated. -}; diff --git a/src/addon/mod/quiz/providers/push-click-handler.ts b/src/addon/mod/quiz/providers/push-click-handler.ts deleted file mode 100644 index 9509d096a..000000000 --- a/src/addon/mod/quiz/providers/push-click-handler.ts +++ /dev/null @@ -1,75 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModQuizProvider } from './quiz'; -import { AddonModQuizHelperProvider } from './helper'; - -/** - * Handler for quiz push notifications clicks. - */ -@Injectable() -export class AddonModQuizPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonModQuizPushClickHandler'; - priority = 200; - featureName = 'CoreCourseModuleDelegate_AddonModQuiz'; - - protected SUPPORTED_NAMES = ['submission', 'confirmation', 'attempt_overdue']; - - constructor(private utils: CoreUtilsProvider, private quizProvider: AddonModQuizProvider, - private urlUtils: CoreUrlUtilsProvider, private courseHelper: CoreCourseHelperProvider, - private quizHelper: AddonModQuizHelperProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - return this.utils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_quiz' && - this.SUPPORTED_NAMES.indexOf(notification.name) != -1; - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - handleClick(notification: any): Promise { - const contextUrlParams = this.urlUtils.extractUrlParams(notification.contexturl), - data = notification.customdata || {}, - courseId = Number(notification.courseid); - - if (notification.name == 'submission') { - // A student made a submission, go to view the attempt. - return this.quizHelper.handleReviewLink(undefined, Number(contextUrlParams.attempt), Number(contextUrlParams.page), - courseId, Number(data.instance), notification.site); - } else { - // Open the activity. - const moduleId = Number(contextUrlParams.id); - - return this.quizProvider.invalidateContent(moduleId, courseId, notification.site).catch(() => { - // Ignore errors. - }).then(() => { - return this.courseHelper.navigateToModule(moduleId, notification.site, courseId); - }); - } - } -} diff --git a/src/addon/mod/quiz/providers/quiz-offline.ts b/src/addon/mod/quiz/providers/quiz-offline.ts deleted file mode 100644 index 6c1f5121e..000000000 --- a/src/addon/mod/quiz/providers/quiz-offline.ts +++ /dev/null @@ -1,389 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreQuestionProvider } from '@core/question/providers/question'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; -import { AddonModQuizProvider } from './quiz'; -import { SQLiteDB } from '@classes/sqlitedb'; - -/** - * Service to handle offline quiz. - */ -@Injectable() -export class AddonModQuizOfflineProvider { - - protected logger; - - // Variables for database. - static ATTEMPTS_TABLE = 'addon_mod_quiz_attempts'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModQuizOfflineProvider', - version: 1, - tables: [ - { - name: AddonModQuizOfflineProvider.ATTEMPTS_TABLE, - columns: [ - { - name: 'id', // Attempt ID. - type: 'INTEGER', - primaryKey: true - }, - { - name: 'attempt', // Attempt number. - type: 'INTEGER' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'userid', - type: 'INTEGER' - }, - { - name: 'quizid', - type: 'INTEGER' - }, - { - name: 'currentpage', - type: 'INTEGER' - }, - { - name: 'timecreated', - type: 'INTEGER' - }, - { - name: 'timemodified', - type: 'INTEGER' - }, - { - name: 'finished', - type: 'INTEGER' - } - ] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private timeUtils: CoreTimeUtilsProvider, - private questionProvider: CoreQuestionProvider, private translate: TranslateService, private utils: CoreUtilsProvider, - private behaviourDelegate: CoreQuestionBehaviourDelegate) { - this.logger = logger.getInstance('AddonModQuizOfflineProvider'); - - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Classify the answers in questions. - * - * @param answers List of answers. - * @return Object with the questions, the keys are the slot. Each question contains its answers. - */ - classifyAnswersInQuestions(answers: any): any { - const questionsWithAnswers = {}; - - // Classify the answers in each question. - for (const name in answers) { - const slot = this.questionProvider.getQuestionSlotFromName(name), - nameWithoutPrefix = this.questionProvider.removeQuestionPrefix(name); - - if (!questionsWithAnswers[slot]) { - questionsWithAnswers[slot] = { - answers: {}, - prefix: name.substr(0, name.indexOf(nameWithoutPrefix)) - }; - } - questionsWithAnswers[slot].answers[nameWithoutPrefix] = answers[name]; - } - - return questionsWithAnswers; - } - - /** - * Given a list of questions with answers classified in it (@see AddonModQuizOfflineProvider.classifyAnswersInQuestions), - * returns a list of answers (including prefix in the name). - * - * @param questions Questions. - * @return Answers. - */ - extractAnswersFromQuestions(questions: any): any { - const answers = {}; - - for (const slot in questions) { - const question = questions[slot]; - - for (const name in question.answers) { - answers[question.prefix + name] = question.answers[name]; - } - } - - return answers; - } - - /** - * Get all the offline attempts in a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the offline attempts. - */ - getAllAttempts(siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getAllRecords(AddonModQuizOfflineProvider.ATTEMPTS_TABLE); - }); - } - - /** - * Retrieve an attempt answers from site DB. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the answers. - */ - getAttemptAnswers(attemptId: number, siteId?: string): Promise { - return this.questionProvider.getAttemptAnswers(AddonModQuizProvider.COMPONENT, attemptId, siteId); - } - - /** - * Retrieve an attempt from site DB. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the attempt. - */ - getAttemptById(attemptId: number, siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getRecord(AddonModQuizOfflineProvider.ATTEMPTS_TABLE, {id: attemptId}); - }); - } - - /** - * Retrieve an attempt from site DB. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, user current site's user. - * @return Promise resolved with the attempts. - */ - getQuizAttempts(quizId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().getRecords(AddonModQuizOfflineProvider.ATTEMPTS_TABLE, {quizid: quizId, userid: userId}); - }); - } - - /** - * Load local state in the questions. - * - * @param attemptId Attempt ID. - * @param questions List of questions. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - loadQuestionsLocalStates(attemptId: number, questions: any[], siteId?: string): Promise { - const promises = []; - - questions.forEach((question) => { - promises.push(this.questionProvider.getQuestion(AddonModQuizProvider.COMPONENT, attemptId, question.slot, siteId) - .then((q) => { - - const state = this.questionProvider.getState(q.state); - question.state = q.state; - question.status = this.translate.instant('core.question.' + state.status); - }).catch(() => { - // Question not found. - })); - }); - - return Promise.all(promises).then(() => { - return questions; - }); - } - - /** - * Process an attempt, saving its data. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param questions Object with the questions of the quiz. The keys should be the question slot. - * @param data Data to save. - * @param finish Whether to finish the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - processAttempt(quiz: any, attempt: any, questions: any, data: any, finish?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const now = this.timeUtils.timestamp(); - let db: SQLiteDB; - - return this.sitesProvider.getSiteDb(siteId).then((siteDb) => { - db = siteDb; - - // Check if an attempt already exists. - return this.getAttemptById(attempt.id, siteId).catch(() => { - // Attempt doesn't exist, create a new entry. - return { - quizid: quiz.id, - userid: attempt.userid, - id: attempt.id, - courseid: quiz.course, - timecreated: now, - attempt: attempt.attempt, - currentpage: attempt.currentpage - }; - }); - }).then((entry) => { - // Save attempt in DB. - entry.timemodified = now; - entry.finished = finish ? 1 : 0; - - return db.insertRecord(AddonModQuizOfflineProvider.ATTEMPTS_TABLE, entry); - }).then(() => { - // Attempt has been saved, now we need to save the answers. - return this.saveAnswers(quiz, attempt, questions, data, now, siteId); - }); - } - - /** - * Remove an attempt and its answers from local DB. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - removeAttemptAndAnswers(attemptId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - // Remove stored answers and questions. - promises.push(this.questionProvider.removeAttemptAnswers(AddonModQuizProvider.COMPONENT, attemptId, siteId)); - promises.push(this.questionProvider.removeAttemptQuestions(AddonModQuizProvider.COMPONENT, attemptId, siteId)); - - // Remove the attempt. - promises.push(this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.deleteRecords(AddonModQuizOfflineProvider.ATTEMPTS_TABLE, {id: attemptId}); - })); - - return Promise.all(promises); - } - - /** - * Remove a question and its answers from local DB. - * - * @param attemptId Attempt ID. - * @param slot Question slot. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when finished. - */ - removeQuestionAndAnswers(attemptId: number, slot: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.questionProvider.removeQuestion(AddonModQuizProvider.COMPONENT, attemptId, slot, siteId)); - promises.push(this.questionProvider.removeQuestionAnswers(AddonModQuizProvider.COMPONENT, attemptId, slot, siteId)); - - return Promise.all(promises); - } - - /** - * Save an attempt's answers and calculate state for questions modified. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param questions Object with the questions of the quiz. The keys should be the question slot. - * @param answers Answers to save. - * @param timeMod Time modified to set in the answers. If not defined, current time. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - saveAnswers(quiz: any, attempt: any, questions: any, answers: any, timeMod?: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - timeMod = timeMod || this.timeUtils.timestamp(); - - const questionsWithAnswers = {}, - newStates = {}; - let promises = []; - - // Classify the answers in each question. - for (const name in answers) { - const slot = this.questionProvider.getQuestionSlotFromName(name), - nameWithoutPrefix = this.questionProvider.removeQuestionPrefix(name); - - if (questions[slot]) { - if (!questionsWithAnswers[slot]) { - questionsWithAnswers[slot] = questions[slot]; - questionsWithAnswers[slot].answers = {}; - } - questionsWithAnswers[slot].answers[nameWithoutPrefix] = answers[name]; - } - } - - // First determine the new state of each question. We won't save the new state yet. - for (const slot in questionsWithAnswers) { - const question = questionsWithAnswers[slot]; - - promises.push(this.behaviourDelegate.determineNewState( - quiz.preferredbehaviour, AddonModQuizProvider.COMPONENT, attempt.id, question, siteId).then((state) => { - // Check if state has changed. - if (state && state.name != question.state) { - newStates[question.slot] = state.name; - } - })); - } - - return Promise.all(promises).then(() => { - // Now save the answers. - return this.questionProvider.saveAnswers(AddonModQuizProvider.COMPONENT, quiz.id, attempt.id, attempt.userid, - answers, timeMod, siteId); - }).then(() => { - // Answers have been saved, now we can save the questions with the states. - promises = []; - - for (const slot in newStates) { - const question = questionsWithAnswers[slot]; - - promises.push(this.questionProvider.saveQuestion(AddonModQuizProvider.COMPONENT, quiz.id, attempt.id, - attempt.userid, question, newStates[slot], siteId)); - } - - return this.utils.allPromises(promises).catch((err) => { - // Ignore errors when saving question state. - this.logger.error('Error saving question state', err); - }); - }); - } - - /** - * Set attempt's current page. - * - * @param attemptId Attempt ID. - * @param page Page to set. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - setAttemptCurrentPage(attemptId: number, page: number, siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.updateRecords(AddonModQuizOfflineProvider.ATTEMPTS_TABLE, {currentpage: page}, {id: attemptId}); - }); - } -} diff --git a/src/addon/mod/quiz/providers/quiz-sync.ts b/src/addon/mod/quiz/providers/quiz-sync.ts deleted file mode 100644 index e13a013f5..000000000 --- a/src/addon/mod/quiz/providers/quiz-sync.ts +++ /dev/null @@ -1,468 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreQuestionProvider } from '@core/question/providers/question'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreCourseActivitySyncBaseProvider } from '@core/course/classes/activity-sync'; -import { AddonModQuizProvider } from './quiz'; -import { AddonModQuizOfflineProvider } from './quiz-offline'; -import { AddonModQuizPrefetchHandler } from './prefetch-handler'; - -/** - * Data returned by a quiz sync. - */ -export interface AddonModQuizSyncResult { - /** - * List of warnings. - */ - warnings: string[]; - - /** - * Whether an attempt was finished in the site due to the sync, - */ - attemptFinished: boolean; -} - -/** - * Service to sync quizzes. - */ -@Injectable() -export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_quiz_autom_synced'; - - protected componentTranslate: string; - - constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, - syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService, - private eventsProvider: CoreEventsProvider, timeUtils: CoreTimeUtilsProvider, - private quizProvider: AddonModQuizProvider, private quizOfflineProvider: AddonModQuizOfflineProvider, - protected prefetchHandler: AddonModQuizPrefetchHandler, private questionProvider: CoreQuestionProvider, - private questionDelegate: CoreQuestionDelegate, private logHelper: CoreCourseLogHelperProvider, - prefetchDelegate: CoreCourseModulePrefetchDelegate, private courseProvider: CoreCourseProvider) { - - super('AddonModQuizSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils, prefetchDelegate, prefetchHandler); - - this.componentTranslate = courseProvider.translateModuleName('quiz'); - } - - /** - * Finish a sync process: remove offline data if needed, prefetch quiz data, set sync time and return the result. - * - * @param siteId Site ID. - * @param quiz Quiz. - * @param courseId Course ID. - * @param warnings List of warnings generated by the sync. - * @param attemptId Last attempt ID. - * @param offlineAttempt Offline attempt synchronized, if any. - * @param onlineAttempt Online data for the offline attempt. - * @param removeAttempt Whether the offline data should be removed. - * @param updated Whether some data was sent to the site. - * @return Promise resolved on success. - */ - protected finishSync(siteId: string, quiz: any, courseId: number, warnings: string[], attemptId?: number, offlineAttempt?: any, - onlineAttempt?: any, removeAttempt?: boolean, updated?: boolean): Promise { - - // Invalidate the data for the quiz and attempt. - return this.quizProvider.invalidateAllQuizData(quiz.id, courseId, attemptId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - if (removeAttempt && attemptId) { - return this.quizOfflineProvider.removeAttemptAndAnswers(attemptId, siteId); - } - }).then(() => { - if (updated) { - // Data has been sent. Update prefetched data. - return this.courseProvider.getModuleBasicInfoByInstance(quiz.id, 'quiz', siteId).then((module) => { - return this.prefetchAfterUpdateQuiz(module, quiz, courseId, undefined, siteId); - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - return this.setSyncTime(quiz.id, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // Check if online attempt was finished because of the sync. - if (onlineAttempt && !this.quizProvider.isAttemptFinished(onlineAttempt.state)) { - // Attempt wasn't finished at start. Check if it's finished now. - return this.quizProvider.getUserAttempts(quiz.id, {cmId: quiz.coursemodule, siteId}).then((attempts) => { - // Search the attempt. - for (const i in attempts) { - const attempt = attempts[i]; - - if (attempt.id == onlineAttempt.id) { - return this.quizProvider.isAttemptFinished(attempt.state); - } - } - - return false; - }); - } - - return false; - }).then((attemptFinished) => { - return { - warnings: warnings, - attemptFinished: attemptFinished - }; - }); - } - - /** - * Check if a quiz has data to synchronize. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether it has data to sync. - */ - hasDataToSync(quizId: number, siteId?: string): Promise { - return this.quizOfflineProvider.getQuizAttempts(quizId, siteId).then((attempts) => { - return !!attempts.length; - }).catch(() => { - return false; - }); - } - - /** - * Conveniece function to prefetch data after an update. - * - * @param module Module. - * @param quiz Quiz. - * @param courseId Course ID. - * @param regex If regex matches, don't download the data. Defaults to check files. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - prefetchAfterUpdateQuiz(module: any, quiz: any, courseId: number, regex?: RegExp, siteId?: string): Promise { - regex = regex || /^.*files$/; - - let shouldDownload; - - // Get the module updates to check if the data was updated or not. - return this.prefetchDelegate.getModuleUpdates(module, courseId, true, siteId).then((result) => { - - if (result && result.updates && result.updates.length > 0) { - // Only prefetch if files haven't changed. - shouldDownload = !result.updates.find((entry) => { - return entry.name.match(regex); - }); - - if (shouldDownload) { - return this.prefetchHandler.download(module, courseId, undefined, false, false); - } - } - - }).then(() => { - // Prefetch finished or not needed, set the right status. - return this.prefetchHandler.setStatusAfterPrefetch(quiz, { - cmId: module.id, - readingStrategy: shouldDownload ? CoreSitesReadingStrategy.PreferCache : undefined, - siteId, - }); - }); - } - - /** - * Try to synchronize all the quizzes in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllQuizzes(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all quizzes', this.syncAllQuizzesFunc.bind(this), [force], siteId); - } - - /** - * Sync all quizzes on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllQuizzesFunc(siteId?: string, force?: boolean): Promise { - // Get all offline attempts. - return this.quizOfflineProvider.getAllAttempts(siteId).then((attempts) => { - const quizzes = [], - ids = [], // To prevent duplicates. - promises = []; - - // Get the IDs of all the quizzes that have something to be synced. - attempts.forEach((attempt) => { - if (ids.indexOf(attempt.quizid) == -1) { - ids.push(attempt.quizid); - - quizzes.push({ - id: attempt.quizid, - courseid: attempt.courseid - }); - } - }); - - // Sync all quizzes that haven't been synced for a while and that aren't attempted right now. - quizzes.forEach((quiz) => { - if (!this.syncProvider.isBlocked(AddonModQuizProvider.COMPONENT, quiz.id, siteId)) { - - // Quiz not blocked, try to synchronize it. - promises.push(this.quizProvider.getQuizById(quiz.courseid, quiz.id, {siteId}).then((quiz) => { - const promise = force ? this.syncQuiz(quiz, false, siteId) : this.syncQuizIfNeeded(quiz, false, siteId); - - return promise.then((data) => { - if (data && data.warnings && data.warnings.length) { - // Store the warnings to show them when the user opens the quiz. - return this.setSyncWarnings(quiz.id, data.warnings, siteId).then(() => { - return data; - }); - } - - return data; - }).then((data) => { - if (typeof data != 'undefined') { - // Sync successful. Send event. - this.eventsProvider.trigger(AddonModQuizSyncProvider.AUTO_SYNCED, { - quizId: quiz.id, - attemptFinished: data.attemptFinished, - warnings: data.warnings - }, siteId); - } - }); - })); - } - }); - - return Promise.all(promises); - }); - } - - /** - * Sync a quiz only if a certain time has passed since the last time. - * - * @param quiz Quiz. - * @param askPreflight Whether we should ask for preflight data if needed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the quiz is synced or if it doesn't need to be synced. - */ - syncQuizIfNeeded(quiz: any, askPreflight?: boolean, siteId?: string): Promise { - return this.isSyncNeeded(quiz.id, siteId).then((needed) => { - if (needed) { - return this.syncQuiz(quiz, askPreflight, siteId); - } - }); - } - - /** - * Try to synchronize a quiz. - * The promise returned will be resolved with an array with warnings if the synchronization is successful. - * - * @param quiz Quiz. - * @param askPreflight Whether we should ask for preflight data if needed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success. - */ - syncQuiz(quiz: any, askPreflight?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const warnings = []; - const courseId = quiz.course; - const modOptions = { - cmId: quiz.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - let syncPromise; - let preflightData; - - if (this.isSyncing(quiz.id, siteId)) { - // There's already a sync ongoing for this quiz, return the promise. - return this.getOngoingSync(quiz.id, siteId); - } - - // Verify that quiz isn't blocked. - if (this.syncProvider.isBlocked(AddonModQuizProvider.COMPONENT, quiz.id, siteId)) { - this.logger.debug('Cannot sync quiz ' + quiz.id + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug('Try to sync quiz ' + quiz.id + ' in site ' + siteId); - - // Sync offline logs. - syncPromise = this.logHelper.syncIfNeeded(AddonModQuizProvider.COMPONENT, quiz.id, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Get all the offline attempts for the quiz. - return this.quizOfflineProvider.getQuizAttempts(quiz.id, siteId); - }).then((attempts) => { - // Should return 0 or 1 attempt. - if (!attempts.length) { - return this.finishSync(siteId, quiz, courseId, warnings); - } - - const offlineAttempt = attempts.pop(); - - // Now get the list of online attempts to make sure this attempt exists and isn't finished. - return this.quizProvider.getUserAttempts(quiz.id, modOptions).then((attempts) => { - const lastAttemptId = attempts.length ? attempts[attempts.length - 1].id : undefined; - let onlineAttempt; - - // Search the attempt we retrieved from offline. - for (const i in attempts) { - const attempt = attempts[i]; - - if (attempt.id == offlineAttempt.id) { - onlineAttempt = attempt; - break; - } - } - - if (!onlineAttempt || this.quizProvider.isAttemptFinished(onlineAttempt.state)) { - // Attempt not found or it's finished in online. Discard it. - warnings.push(this.translate.instant('addon.mod_quiz.warningattemptfinished')); - - return this.finishSync(siteId, quiz, courseId, warnings, offlineAttempt.id, offlineAttempt, onlineAttempt, - true); - } - - // Get the data stored in offline. - return this.quizOfflineProvider.getAttemptAnswers(offlineAttempt.id, siteId).then((answersList) => { - - if (!answersList.length) { - // No answers stored, finish. - return this.finishSync(siteId, quiz, courseId, warnings, lastAttemptId, offlineAttempt, onlineAttempt, - true); - } - - const answers = this.questionProvider.convertAnswersArrayToObject(answersList), - offlineQuestions = this.quizOfflineProvider.classifyAnswersInQuestions(answers); - let finish; - - // We're going to need preflightData, get it. - return this.quizProvider.getQuizAccessInformation(quiz.id, modOptions).then((info) => { - - return this.prefetchHandler.getPreflightData(quiz, info, onlineAttempt, askPreflight, - 'core.settings.synchronization', siteId); - }).then((data) => { - preflightData = data; - - // Now get the online questions data. - const pages = this.quizProvider.getPagesFromLayoutAndQuestions(onlineAttempt.layout, offlineQuestions); - - return this.quizProvider.getAllQuestionsData(quiz, onlineAttempt, preflightData, { - pages, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }); - }).then((onlineQuestions) => { - - // Validate questions, discarding the offline answers that can't be synchronized. - return this.validateQuestions(onlineAttempt.id, onlineQuestions, offlineQuestions, siteId); - }).then((discardedData) => { - - // Get the answers to send. - const answers = this.quizOfflineProvider.extractAnswersFromQuestions(offlineQuestions); - finish = offlineAttempt.finished && !discardedData; - - if (discardedData) { - if (offlineAttempt.finished) { - warnings.push(this.translate.instant('addon.mod_quiz.warningdatadiscardedfromfinished')); - } else { - warnings.push(this.translate.instant('addon.mod_quiz.warningdatadiscarded')); - } - } - - return this.quizProvider.processAttempt(quiz, onlineAttempt, answers, preflightData, finish, false, false, - siteId); - }).then(() => { - - // Answers sent, now set the current page if the attempt isn't finished. - if (!finish) { - // Don't pass the quiz instance because we don't want to trigger a Firebase event in this case. - return this.quizProvider.logViewAttempt(onlineAttempt.id, offlineAttempt.currentpage, preflightData, - false, undefined, siteId).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - - // Data sent. Finish the sync. - return this.finishSync(siteId, quiz, courseId, warnings, lastAttemptId, offlineAttempt, onlineAttempt, - true, true); - }); - }); - }); - }); - - return this.addOngoingSync(quiz.id, syncPromise, siteId); - } - - /** - * Validate questions, discarding the offline answers that can't be synchronized. - * - * @param attemptId Attempt ID. - * @param onlineQuestions Online questions - * @param offlineQuestions Offline questions. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if some offline data was discarded, false otherwise. - */ - validateQuestions(attemptId: number, onlineQuestions: any, offlineQuestions: any, siteId?: string): Promise { - const promises = []; - let discardedData = false; - - for (const slot in offlineQuestions) { - const offlineQuestion = offlineQuestions[slot], - onlineQuestion = onlineQuestions[slot], - offlineSequenceCheck = offlineQuestion.answers[':sequencecheck']; - - if (onlineQuestion) { - - // We found the online data for the question, validate that the sequence check is ok. - if (!this.questionDelegate.validateSequenceCheck(onlineQuestion, offlineSequenceCheck)) { - // Sequence check is not valid, remove the offline data. - discardedData = true; - promises.push(this.quizOfflineProvider.removeQuestionAndAnswers(attemptId, Number(slot), siteId)); - delete offlineQuestions[slot]; - } else { - // Sequence check is valid. Use the online one to prevent synchronization errors. - offlineQuestion.answers[':sequencecheck'] = onlineQuestion.sequencecheck; - } - } else { - // Online question not found, it can happen for 2 reasons: - // 1- It's a sequential quiz and the question is in a page already passed. - // 2- Quiz layout has changed (shouldn't happen since it's blocked if there are attempts). - discardedData = true; - promises.push(this.quizOfflineProvider.removeQuestionAndAnswers(attemptId, Number(slot), siteId)); - delete offlineQuestions[slot]; - } - } - - return Promise.all(promises).then(() => { - return discardedData; - }); - } -} diff --git a/src/addon/mod/quiz/providers/quiz.ts b/src/addon/mod/quiz/providers/quiz.ts deleted file mode 100644 index 6caa9148e..000000000 --- a/src/addon/mod/quiz/providers/quiz.ts +++ /dev/null @@ -1,1834 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreSite } from '@classes/site'; -import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; -import { AddonModQuizOfflineProvider } from './quiz-offline'; -import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some features for quiz. - */ -@Injectable() -export class AddonModQuizProvider { - static COMPONENT = 'mmaModQuiz'; - static ATTEMPT_FINISHED_EVENT = 'addon_mod_quiz_attempt_finished'; - - // Grade methods. - static GRADEHIGHEST = 1; - static GRADEAVERAGE = 2; - static ATTEMPTFIRST = 3; - static ATTEMPTLAST = 4; - - // Question options. - static QUESTION_OPTIONS_MAX_ONLY = 1; - static QUESTION_OPTIONS_MARK_AND_MAX = 2; - - // Attempt state. - static ATTEMPT_IN_PROGRESS = 'inprogress'; - static ATTEMPT_OVERDUE = 'overdue'; - static ATTEMPT_FINISHED = 'finished'; - static ATTEMPT_ABANDONED = 'abandoned'; - - // Show the countdown timer if there is less than this amount of time left before the the quiz close date. - static QUIZ_SHOW_TIME_BEFORE_DEADLINE = 3600; - - protected ROOT_CACHE_KEY = 'mmaModQuiz:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, - private translate: TranslateService, private textUtils: CoreTextUtilsProvider, - private gradesHelper: CoreGradesHelperProvider, private questionDelegate: CoreQuestionDelegate, - private filepoolProvider: CoreFilepoolProvider, private timeUtils: CoreTimeUtilsProvider, - private accessRulesDelegate: AddonModQuizAccessRuleDelegate, private quizOfflineProvider: AddonModQuizOfflineProvider, - private domUtils: CoreDomUtilsProvider, private logHelper: CoreCourseLogHelperProvider, - protected pushNotificationsProvider: CorePushNotificationsProvider) { - this.logger = logger.getInstance('AddonModQuizProvider'); - } - - /** - * Formats a grade to be displayed. - * - * @param grade Grade. - * @param decimals Decimals to use. - * @return Grade to display. - */ - formatGrade(grade: number, decimals: number): string { - if (typeof grade == 'undefined' || grade == -1 || grade === null) { - return this.translate.instant('addon.mod_quiz.notyetgraded'); - } - - return this.utils.formatFloat(this.textUtils.roundToDecimals(grade, decimals)); - } - - /** - * Get attempt questions. Returns all of them or just the ones in certain pages. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param preflightData Preflight required data (like password). - * @param options Other options. - * @return Promise resolved with the questions. - */ - getAllQuestionsData(quiz: any, attempt: any, preflightData: any, options: AddonModQuizAllQuestionsDataOptions = {}) - : Promise { - - const promises = []; - const questions = {}; - const isSequential = this.isNavigationSequential(quiz); - const pages = options.pages || this.getPagesFromLayout(attempt.layout); - - pages.forEach((page) => { - if (isSequential && page < attempt.currentpage) { - // Sequential quiz, cannot get pages before the current one. - return; - } - - // Get the questions in the page. - promises.push(this.getAttemptData(attempt.id, page, preflightData, options).then((data) => { - // Add the questions to the result object. - data.questions.forEach((question) => { - questions[question.slot] = question; - }); - })); - }); - - return Promise.all(promises).then(() => { - return questions; - }); - } - - /** - * Get cache key for get attempt access information WS calls. - * - * @param quizId Quiz ID. - * @param attemptId Attempt ID. - * @return Cache key. - */ - protected getAttemptAccessInformationCacheKey(quizId: number, attemptId: number): string { - return this.getAttemptAccessInformationCommonCacheKey(quizId) + ':' + attemptId; - } - - /** - * Get common cache key for get attempt access information WS calls. - * - * @param quizId Quiz ID. - * @return Cache key. - */ - protected getAttemptAccessInformationCommonCacheKey(quizId: number): string { - return this.ROOT_CACHE_KEY + 'attemptAccessInformation:' + quizId; - } - - /** - * Get access information for an attempt. - * - * @param quizId Quiz ID. - * @param attemptId Attempt ID. 0 for user's last attempt. - * @param options Other options. - * @return Promise resolved with the access information. - */ - getAttemptAccessInformation(quizId: number, attemptId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - quizid: quizId, - attemptid: attemptId, - }; - const preSets = { - cacheKey: this.getAttemptAccessInformationCacheKey(quizId, attemptId), - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_attempt_access_information', params, preSets); - }); - } - - /** - * Get cache key for get attempt data WS calls. - * - * @param attemptId Attempt ID. - * @param page Page. - * @return Cache key. - */ - protected getAttemptDataCacheKey(attemptId: number, page: number): string { - return this.getAttemptDataCommonCacheKey(attemptId) + ':' + page; - } - - /** - * Get common cache key for get attempt data WS calls. - * - * @param attemptId Attempt ID. - * @return Cache key. - */ - protected getAttemptDataCommonCacheKey(attemptId: number): string { - return this.ROOT_CACHE_KEY + 'attemptData:' + attemptId; - } - - /** - * Get an attempt's data. - * - * @param attemptId Attempt ID. - * @param page Page number. - * @param preflightData Preflight required data (like password). - * @param options Other options. - * @return Promise resolved with the attempt data. - */ - getAttemptData(attemptId: number, page: number, preflightData: any, options: CoreCourseCommonModWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - attemptid: attemptId, - page: page, - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true), - }; - const preSets = { - cacheKey: this.getAttemptDataCacheKey(attemptId, page), - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_attempt_data', params, preSets); - }); - } - - /** - * Get an attempt's due date. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @return Attempt's due date, 0 if no due date or invalid data. - */ - getAttemptDueDate(quiz: any, attempt: any): number { - const deadlines = []; - - if (quiz.timelimit) { - deadlines.push(parseInt(attempt.timestart, 10) + parseInt(quiz.timelimit, 10)); - } - if (quiz.timeclose) { - deadlines.push(parseInt(quiz.timeclose, 10)); - } - - if (!deadlines.length) { - return 0; - } - - // Get min due date. - const dueDate = Math.min.apply(null, deadlines); - if (!dueDate) { - return 0; - } - - switch (attempt.state) { - case AddonModQuizProvider.ATTEMPT_IN_PROGRESS: - return dueDate * 1000; - - case AddonModQuizProvider.ATTEMPT_OVERDUE: - return (dueDate + parseInt(quiz.graceperiod, 10)) * 1000; - - default: - this.logger.warn('Unexpected state when getting due date: ' + attempt.state); - - return 0; - } - } - - /** - * Get an attempt's warning because of due date. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @return Attempt's warning, undefined if no due date. - */ - getAttemptDueDateWarning(quiz: any, attempt: any): string { - const dueDate = this.getAttemptDueDate(quiz, attempt); - - if (attempt.state === AddonModQuizProvider.ATTEMPT_OVERDUE) { - return this.translate.instant('addon.mod_quiz.overduemustbesubmittedby', {$a: this.timeUtils.userDate(dueDate)}); - } else if (dueDate) { - return this.translate.instant('addon.mod_quiz.mustbesubmittedby', {$a: this.timeUtils.userDate(dueDate)}); - } - } - - /** - * Turn attempt's state into a readable state, including some extra data depending on the state. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @return List of state sentences. - */ - getAttemptReadableState(quiz: any, attempt: any): string[] { - if (attempt.finishedOffline) { - return [this.translate.instant('addon.mod_quiz.finishnotsynced')]; - } - - switch (attempt.state) { - case AddonModQuizProvider.ATTEMPT_IN_PROGRESS: - return [this.translate.instant('addon.mod_quiz.stateinprogress')]; - - case AddonModQuizProvider.ATTEMPT_OVERDUE: - const sentences = [], - dueDate = this.getAttemptDueDate(quiz, attempt); - - sentences.push(this.translate.instant('addon.mod_quiz.stateoverdue')); - - if (dueDate) { - sentences.push(this.translate.instant('addon.mod_quiz.stateoverduedetails', - {$a: this.timeUtils.userDate(dueDate)})); - } - - return sentences; - - case AddonModQuizProvider.ATTEMPT_FINISHED: - return [ - this.translate.instant('addon.mod_quiz.statefinished'), - this.translate.instant('addon.mod_quiz.statefinisheddetails', - {$a: this.timeUtils.userDate(attempt.timefinish * 1000)}) - ]; - - case AddonModQuizProvider.ATTEMPT_ABANDONED: - return [this.translate.instant('addon.mod_quiz.stateabandoned')]; - - default: - return []; - } - } - - /** - * Turn attempt's state into a readable state name, without any more data. - * - * @param state State. - * @return Readable state name. - */ - getAttemptReadableStateName(state: string): string { - switch (state) { - case AddonModQuizProvider.ATTEMPT_IN_PROGRESS: - return this.translate.instant('addon.mod_quiz.stateinprogress'); - - case AddonModQuizProvider.ATTEMPT_OVERDUE: - return this.translate.instant('addon.mod_quiz.stateoverdue'); - - case AddonModQuizProvider.ATTEMPT_FINISHED: - return this.translate.instant('addon.mod_quiz.statefinished'); - - case AddonModQuizProvider.ATTEMPT_ABANDONED: - return this.translate.instant('addon.mod_quiz.stateabandoned'); - - default: - return ''; - } - } - - /** - * Get cache key for get attempt review WS calls. - * - * @param attemptId Attempt ID. - * @param page Page. - * @return Cache key. - */ - protected getAttemptReviewCacheKey(attemptId: number, page: number): string { - return this.getAttemptReviewCommonCacheKey(attemptId) + ':' + page; - } - - /** - * Get common cache key for get attempt review WS calls. - * - * @param attemptId Attempt ID. - * @return Cache key. - */ - protected getAttemptReviewCommonCacheKey(attemptId: number): string { - return this.ROOT_CACHE_KEY + 'attemptReview:' + attemptId; - } - - /** - * Get an attempt's review. - * - * @param attemptId Attempt ID. - * @param options Other options. - * @return Promise resolved with the attempt review. - */ - getAttemptReview(attemptId: number, options: AddonModQuizGetAttemptReviewOptions = {}): Promise { - const page = typeof options.page == 'undefined' ? -1 : options.page; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - attemptid: attemptId, - page: page, - }; - const preSets = { - cacheKey: this.getAttemptReviewCacheKey(attemptId, page), - cacheErrors: ['noreview'], - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_attempt_review', params, preSets); - }); - } - - /** - * Get cache key for get attempt summary WS calls. - * - * @param attemptId Attempt ID. - * @return Cache key. - */ - protected getAttemptSummaryCacheKey(attemptId: number): string { - return this.ROOT_CACHE_KEY + 'attemptSummary:' + attemptId; - } - - /** - * Get an attempt's summary. - * - * @param attemptId Attempt ID. - * @param preflightData Preflight required data (like password). - * @param options Other options. - * @return Promise resolved with the list of questions for the attempt summary. - */ - getAttemptSummary(attemptId: number, preflightData: any, options: AddonModQuizGetAttemptSummaryOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - attemptid: attemptId, - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true), - }; - const preSets = { - cacheKey: this.getAttemptSummaryCacheKey(attemptId), - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_attempt_summary', params, preSets).then((response) => { - if (response && response.questions) { - if (options.loadLocal) { - return this.quizOfflineProvider.loadQuestionsLocalStates(attemptId, response.questions, site.getId()); - } - - return response.questions; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get combined review options WS calls. - * - * @param quizId Quiz ID. - * @param userId User ID. - * @return Cache key. - */ - protected getCombinedReviewOptionsCacheKey(quizId: number, userId: number): string { - return this.getCombinedReviewOptionsCommonCacheKey(quizId) + ':' + userId; - } - - /** - * Get common cache key for get combined review options WS calls. - * - * @param quizId Quiz ID. - * @return Cache key. - */ - protected getCombinedReviewOptionsCommonCacheKey(quizId: number): string { - return this.ROOT_CACHE_KEY + 'combinedReviewOptions:' + quizId; - } - - /** - * Get a quiz combined review options. - * - * @param quizId Quiz ID. - * @param options Other options. - * @return Promise resolved with the combined review options. - */ - getCombinedReviewOptions(quizId: number, options: AddonModQuizUserOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = options.userId || site.getUserId(); - - const params = { - quizid: quizId, - userid: userId, - }; - const preSets = { - cacheKey: this.getCombinedReviewOptionsCacheKey(quizId, userId), - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_combined_review_options', params, preSets).then((response) => { - if (response && response.someoptions && response.alloptions) { - // Convert the arrays to objects with name -> value. - response.someoptions = this.utils.objectToKeyValueMap(response.someoptions, 'name', 'value'); - response.alloptions = this.utils.objectToKeyValueMap(response.alloptions, 'name', 'value'); - - return response; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get feedback for grade WS calls. - * - * @param quizId Quiz ID. - * @param grade Grade. - * @return Cache key. - */ - protected getFeedbackForGradeCacheKey(quizId: number, grade: number): string { - return this.getFeedbackForGradeCommonCacheKey(quizId) + ':' + grade; - } - - /** - * Get common cache key for get feedback for grade WS calls. - * - * @param quizId Quiz ID. - * @return Cache key. - */ - protected getFeedbackForGradeCommonCacheKey(quizId: number): string { - return this.ROOT_CACHE_KEY + 'feedbackForGrade:' + quizId; - } - - /** - * Get the feedback for a certain grade. - * - * @param quizId Quiz ID. - * @param grade Grade. - * @param options Other options. - * @return Promise resolved with the feedback. - */ - getFeedbackForGrade(quizId: number, grade: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - quizid: quizId, - grade: grade, - }; - const preSets = { - cacheKey: this.getFeedbackForGradeCacheKey(quizId, grade), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_quiz_feedback_for_grade', params, preSets); - }); - } - - /** - * Determine the correct number of decimal places required to format a grade. - * Based on Moodle's quiz_get_grade_format. - * - * @param quiz Quiz. - * @return Number of decimals. - */ - getGradeDecimals(quiz: any): number { - if (typeof quiz.questiondecimalpoints == 'undefined') { - quiz.questiondecimalpoints = -1; - } - - if (quiz.questiondecimalpoints == -1) { - return quiz.decimalpoints; - } - - return quiz.questiondecimalpoints; - } - - /** - * Gets a quiz grade and feedback from the gradebook. - * - * @param courseId Course ID. - * @param moduleId Quiz module ID. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved with an object containing the grade and the feedback. - */ - getGradeFromGradebook(courseId: number, moduleId: number, ignoreCache?: boolean, siteId?: string, userId?: number) - : Promise { - - return this.gradesHelper.getGradeModuleItems(courseId, moduleId, userId, null, siteId, ignoreCache).then((items) => { - return items.shift(); - }); - } - - /** - * Given a list of attempts, returns the last finished attempt. - * - * @param attempts Attempts. - * @return Last finished attempt. - */ - getLastFinishedAttemptFromList(attempts: any[]): any { - if (attempts && attempts.length) { - for (let i = attempts.length - 1; i >= 0; i--) { - const attempt = attempts[i]; - - if (this.isAttemptFinished(attempt.state)) { - return attempt; - } - } - } - } - - /** - * Given a list of questions, check if the quiz can be submitted. - * Will return an array with the messages to prevent the submit. Empty array if quiz can be submitted. - * - * @param questions Questions. - * @return List of prevent submit messages. Empty array if quiz can be submitted. - */ - getPreventSubmitMessages(questions: any[]): string[] { - const messages = []; - - questions.forEach((question) => { - if (question.type != 'random' && !this.questionDelegate.isQuestionSupported(question.type)) { - // The question isn't supported. - messages.push(this.translate.instant('core.question.questionmessage', { - $a: question.slot, - $b: this.translate.instant('core.question.errorquestionnotsupported', {$a: question.type}) - })); - } else { - let message = this.questionDelegate.getPreventSubmitMessage(question); - if (message) { - message = this.translate.instant(message); - messages.push(this.translate.instant('core.question.questionmessage', {$a: question.slot, $b: message})); - } - } - }); - - return messages; - } - - /** - * Get cache key for quiz data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getQuizDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'quiz:' + courseId; - } - - /** - * Get a Quiz with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the Quiz is retrieved. - */ - protected getQuizByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getQuizDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModQuizProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_quizzes_by_courses', params, preSets).then((response) => { - if (response && response.quizzes) { - // Search the quiz. - for (const i in response.quizzes) { - const quiz = response.quizzes[i]; - if (quiz[key] == value) { - return quiz; - } - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a quiz by module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the quiz is retrieved. - */ - getQuiz(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getQuizByField(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a quiz by quiz ID. - * - * @param courseId Course ID. - * @param id Quiz ID. - * @param options Other options. - * @return Promise resolved when the quiz is retrieved. - */ - getQuizById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getQuizByField(courseId, 'id', id, options); - } - - /** - * Get cache key for get quiz access information WS calls. - * - * @param quizId Quiz ID. - * @return Cache key. - */ - protected getQuizAccessInformationCacheKey(quizId: number): string { - return this.ROOT_CACHE_KEY + 'quizAccessInformation:' + quizId; - } - - /** - * Get access information for an attempt. - * - * @param quizId Quiz ID. - * @param options Other options. - * @return Promise resolved with the access information. - */ - getQuizAccessInformation(quizId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - quizid: quizId, - }; - const preSets = { - cacheKey: this.getQuizAccessInformationCacheKey(quizId), - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_quiz_access_information', params, preSets); - }); - } - - /** - * Get a readable Quiz grade method. - * - * @param method Grading method. - * @return Readable grading method. - */ - getQuizGradeMethod(method: number | string): string { - if (typeof method == 'string') { - method = parseInt(method, 10); - } - - switch (method) { - case AddonModQuizProvider.GRADEHIGHEST: - return this.translate.instant('addon.mod_quiz.gradehighest'); - case AddonModQuizProvider.GRADEAVERAGE: - return this.translate.instant('addon.mod_quiz.gradeaverage'); - case AddonModQuizProvider.ATTEMPTFIRST: - return this.translate.instant('addon.mod_quiz.attemptfirst'); - case AddonModQuizProvider.ATTEMPTLAST: - return this.translate.instant('addon.mod_quiz.attemptlast'); - default: - return ''; - } - } - - /** - * Get cache key for get quiz required qtypes WS calls. - * - * @param quizId Quiz ID. - * @return Cache key. - */ - protected getQuizRequiredQtypesCacheKey(quizId: number): string { - return this.ROOT_CACHE_KEY + 'quizRequiredQtypes:' + quizId; - } - - /** - * Get the potential question types that would be required for a given quiz. - * - * @param quizId Quiz ID. - * @param options Other options. - * @return Promise resolved with the access information. - */ - getQuizRequiredQtypes(quizId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - quizid: quizId, - }; - const preSets = { - cacheKey: this.getQuizRequiredQtypesCacheKey(quizId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_quiz_required_qtypes', params, preSets).then((response) => { - if (response && response.questiontypes) { - return response.questiontypes; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Given an attempt's layout, return the list of pages. - * - * @param layout Attempt's layout. - * @return Pages. - * @description - * An attempt's layout is a string with the question numbers separated by commas. A 0 indicates a change of page. - * Example: 1,2,3,0,4,5,6,0 - * In the example above, first page has questions 1, 2 and 3. Second page has questions 4, 5 and 6. - * - * This function returns a list of pages. - */ - getPagesFromLayout(layout: string): number[] { - const split = layout.split(','), - pages: number[] = []; - let page = 0; - - for (let i = 0; i < split.length; i++) { - if (split[i] == '0') { - pages.push(page); - page++; - } - } - - return pages; - } - - /** - * Given an attempt's layout and a list of questions identified by question slot, - * return the list of pages that have at least 1 of the questions. - * - * @param layout Attempt's layout. - * @param questions List of questions. It needs to be an object where the keys are question slot. - * @return Pages. - * @description - * An attempt's layout is a string with the question numbers separated by commas. A 0 indicates a change of page. - * Example: 1,2,3,0,4,5,6,0 - * In the example above, first page has questions 1, 2 and 3. Second page has questions 4, 5 and 6. - * - * This function returns a list of pages. - */ - getPagesFromLayoutAndQuestions(layout: string, questions: any): number[] { - const split = layout.split(','), - pages: number[] = []; - let page = 0, - pageAdded = false; - - for (let i = 0; i < split.length; i++) { - const value = Number(split[i]); - - if (value == 0) { - page++; - pageAdded = false; - } else if (!pageAdded && questions[value]) { - pages.push(page); - pageAdded = true; - } - } - - return pages; - } - - /** - * Given a list of question types, returns the types that aren't supported. - * - * @param questionTypes Question types to check. - * @return Not supported question types. - */ - getUnsupportedQuestions(questionTypes: string[]): string[] { - const notSupported = []; - - questionTypes.forEach((type) => { - if (type != 'random' && !this.questionDelegate.isQuestionSupported(type)) { - notSupported.push(type); - } - }); - - return notSupported; - } - - /** - * Given a list of access rules names, returns the rules that aren't supported. - * - * @param rulesNames Rules to check. - * @return Not supported rules names. - */ - getUnsupportedRules(rulesNames: string[]): string[] { - const notSupported = []; - - rulesNames.forEach((name) => { - if (!this.accessRulesDelegate.isAccessRuleSupported(name)) { - notSupported.push(name); - } - }); - - return notSupported; - } - - /** - * Get cache key for get user attempts WS calls. - * - * @param quizId Quiz ID. - * @param userId User ID. - * @return Cache key. - */ - protected getUserAttemptsCacheKey(quizId: number, userId: number): string { - return this.getUserAttemptsCommonCacheKey(quizId) + ':' + userId; - } - - /** - * Get common cache key for get user attempts WS calls. - * - * @param quizId Quiz ID. - * @return Cache key. - */ - protected getUserAttemptsCommonCacheKey(quizId: number): string { - return this.ROOT_CACHE_KEY + 'userAttempts:' + quizId; - } - - /** - * Get quiz attempts for a certain user. - * - * @param quizId Quiz ID. - * @param options Other options. - * @return Promise resolved with the attempts. - */ - getUserAttempts(quizId: number, options: AddonModQuizGetUserAttemptsOptions = {}): Promise { - - const status = options.status || 'all'; - const includePreviews = typeof options.includePreviews == 'undefined' ? true : options.includePreviews; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = options.userId || site.getUserId(); - const params = { - quizid: quizId, - userid: userId, - status: status, - includepreviews: includePreviews ? 1 : 0, - }; - const preSets = { - cacheKey: this.getUserAttemptsCacheKey(quizId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_user_attempts', params, preSets).then((response) => { - if (response && response.attempts) { - return response.attempts; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get user best grade WS calls. - * - * @param quizId Quiz ID. - * @param userId User ID. - * @return Cache key. - */ - protected getUserBestGradeCacheKey(quizId: number, userId: number): string { - return this.getUserBestGradeCommonCacheKey(quizId) + ':' + userId; - } - - /** - * Get common cache key for get user best grade WS calls. - * - * @param quizId Quiz ID. - * @return Cache key. - */ - protected getUserBestGradeCommonCacheKey(quizId: number): string { - return this.ROOT_CACHE_KEY + 'userBestGrade:' + quizId; - } - - /** - * Get best grade in a quiz for a certain user. - * - * @param quizId Quiz ID. - * @param options Other options. - * @return Promise resolved with the best grade data. - */ - getUserBestGrade(quizId: number, options: AddonModQuizUserOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = options.userId || site.getUserId(); - - const params = { - quizid: quizId, - userid: userId, - }; - const preSets = { - cacheKey: this.getUserBestGradeCacheKey(quizId, userId), - component: AddonModQuizProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_quiz_get_user_best_grade', params, preSets); - }); - } - - /** - * Invalidates all the data related to a certain quiz. - * - * @param quizId Quiz ID. - * @param courseId Course ID. - * @param attemptId Attempt ID to invalidate some WS calls. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllQuizData(quizId: number, courseId?: number, attemptId?: number, siteId?: string, userId?: number): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.invalidateAttemptAccessInformation(quizId, siteId)); - promises.push(this.invalidateCombinedReviewOptionsForUser(quizId, siteId, userId)); - promises.push(this.invalidateFeedback(quizId, siteId)); - promises.push(this.invalidateQuizAccessInformation(quizId, siteId)); - promises.push(this.invalidateQuizRequiredQtypes(quizId, siteId)); - promises.push(this.invalidateUserAttemptsForUser(quizId, siteId, userId)); - promises.push(this.invalidateUserBestGradeForUser(quizId, siteId, userId)); - - if (attemptId) { - promises.push(this.invalidateAttemptData(attemptId, siteId)); - promises.push(this.invalidateAttemptReview(attemptId, siteId)); - promises.push(this.invalidateAttemptSummary(attemptId, siteId)); - } - - if (courseId) { - promises.push(this.invalidateGradeFromGradebook(courseId, siteId, userId)); - } - - return Promise.all(promises); - } - - /** - * Invalidates attempt access information for all attempts in a quiz. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAttemptAccessInformation(quizId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getAttemptAccessInformationCommonCacheKey(quizId)); - }); - } - - /** - * Invalidates attempt access information for an attempt. - * - * @param quizId Quiz ID. - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAttemptAccessInformationForAttempt(quizId: number, attemptId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAttemptAccessInformationCacheKey(quizId, attemptId)); - }); - } - - /** - * Invalidates attempt data for all pages. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAttemptData(attemptId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getAttemptDataCommonCacheKey(attemptId)); - }); - } - - /** - * Invalidates attempt data for a certain page. - * - * @param attemptId Attempt ID. - * @param page Page. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAttemptDataForPage(attemptId: number, page: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAttemptDataCacheKey(attemptId, page)); - }); - } - - /** - * Invalidates attempt review for all pages. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAttemptReview(attemptId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getAttemptReviewCommonCacheKey(attemptId)); - }); - } - - /** - * Invalidates attempt review for a certain page. - * - * @param attemptId Attempt ID. - * @param page Page. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAttemptReviewForPage(attemptId: number, page: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAttemptReviewCacheKey(attemptId, page)); - }); - } - - /** - * Invalidates attempt summary. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAttemptSummary(attemptId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAttemptSummaryCacheKey(attemptId)); - }); - } - - /** - * Invalidates combined review options for all users. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateCombinedReviewOptions(quizId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getCombinedReviewOptionsCommonCacheKey(quizId)); - }); - } - - /** - * Invalidates combined review options for a certain user. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateCombinedReviewOptionsForUser(quizId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getCombinedReviewOptionsCacheKey(quizId, userId)); - }); - } - - /** - * Invalidate the prefetched content except files. - * To invalidate files, use AddonModQuizProvider.invalidateFiles. - * - * @param moduleId The module ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Get required data to call the invalidate functions. - return this.getQuiz(courseId, moduleId, { - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId, - }).then((quiz) => { - return this.getUserAttempts(quiz.id, {cmId: quiz.coursemodule, siteId}).then((attempts) => { - // Now invalidate it. - const lastAttemptId = attempts.length ? attempts[attempts.length - 1].id : undefined; - - return this.invalidateAllQuizData(quiz.id, courseId, lastAttemptId, siteId); - }); - }); - } - - /** - * Invalidates feedback for all grades of a quiz. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateFeedback(quizId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getFeedbackForGradeCommonCacheKey(quizId)); - }); - } - - /** - * Invalidates feedback for a certain grade. - * - * @param quizId Quiz ID. - * @param grade Grade. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateFeedbackForGrade(quizId: number, grade: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getFeedbackForGradeCacheKey(quizId, grade)); - }); - } - - /** - * Invalidate the prefetched files. - * - * @param moduleId The module ID. - * @return Promise resolved when the files are invalidated. - */ - invalidateFiles(moduleId: number): Promise { - return this.filepoolProvider.invalidateFilesByComponent(this.sitesProvider.getCurrentSiteId(), - AddonModQuizProvider.COMPONENT, moduleId); - } - - /** - * Invalidates grade from gradebook for a certain user. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateGradeFromGradebook(courseId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return this.gradesHelper.invalidateGradeModuleItems(courseId, userId, null, siteId); - }); - } - - /** - * Invalidates quiz access information for a quiz. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateQuizAccessInformation(quizId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getQuizAccessInformationCacheKey(quizId)); - }); - } - - /** - * Invalidates required qtypes for a quiz. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateQuizRequiredQtypes(quizId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getQuizRequiredQtypesCacheKey(quizId)); - }); - } - - /** - * Invalidates user attempts for all users. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateUserAttempts(quizId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getUserAttemptsCommonCacheKey(quizId)); - }); - } - - /** - * Invalidates user attempts for a certain user. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateUserAttemptsForUser(quizId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getUserAttemptsCacheKey(quizId, userId)); - }); - } - - /** - * Invalidates user best grade for all users. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateUserBestGrade(quizId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getUserBestGradeCommonCacheKey(quizId)); - }); - } - - /** - * Invalidates user best grade for a certain user. - * - * @param quizId Quiz ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateUserBestGradeForUser(quizId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getUserBestGradeCacheKey(quizId, userId)); - }); - } - - /** - * Invalidates quiz data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateQuizData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getQuizDataCacheKey(courseId)); - }); - } - - /** - * Check if an attempt is finished based on its state. - * - * @param state Attempt's state. - * @return Whether it's finished. - */ - isAttemptFinished(state: string): boolean { - return state == AddonModQuizProvider.ATTEMPT_FINISHED || state == AddonModQuizProvider.ATTEMPT_ABANDONED; - } - - /** - * Check if an attempt is finished in offline but not synced. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if finished in offline but not synced, false otherwise. - */ - isAttemptFinishedOffline(attemptId: number, siteId?: string): Promise { - return this.quizOfflineProvider.getAttemptById(attemptId, siteId).then((attempt) => { - return !!attempt.finished; - }).catch(() => { - return false; - }); - } - - /** - * Check if an attempt is nearly over. We consider an attempt nearly over or over if: - * - Is not in progress - * OR - * - It finished before autosaveperiod passes. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @return Whether it's nearly over or over. - */ - isAttemptTimeNearlyOver(quiz: any, attempt: any): boolean { - if (attempt.state != AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { - // Attempt not in progress, return true. - return true; - } - - const dueDate = this.getAttemptDueDate(quiz, attempt), - autoSavePeriod = quiz.autosaveperiod || 0; - - if (dueDate > 0 && Date.now() + autoSavePeriod >= dueDate) { - return true; - } - - return false; - } - - /** - * Check if last attempt is offline and unfinished. - * - * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, user current site's user. - * @return Promise resolved with boolean: true if last offline attempt is unfinished, false otherwise. - */ - isLastAttemptOfflineUnfinished(quiz: any, siteId?: string, userId?: number): Promise { - return this.quizOfflineProvider.getQuizAttempts(quiz.id, siteId, userId).then((attempts) => { - const last = attempts.pop(); - - return last && !last.finished; - }).catch(() => { - return false; - }); - } - - /** - * Check if a quiz navigation is sequential. - * - * @param quiz Quiz. - * @return Whether navigation is sequential. - */ - isNavigationSequential(quiz: any): boolean { - return quiz.navmethod == 'sequential'; - } - - /** - * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the quiz WS are available. - * - * @param siteId Site ID. If not defined, current site. - * @return Whether the plugin is enabled. - */ - isPluginEnabled(siteId?: string): boolean { - // Quiz WebServices were introduced in 3.1, it will always be enabled. - return true; - } - - /** - * Check if a question is blocked. - * - * @param question Question. - * @return Whether it's blocked. - */ - isQuestionBlocked(question: any): boolean { - const element = this.domUtils.convertToElement(question.html); - - return !!element.querySelector('.mod_quiz-blocked_question_warning'); - } - - /** - * Check if a quiz is enabled to be used in offline. - * - * @param quiz Quiz. - * @return Whether offline is enabled. - */ - isQuizOffline(quiz: any): boolean { - // Don't allow downloading the quiz if offline is disabled to prevent wasting a lot of data when opening it. - return !!quiz.allowofflineattempts && !this.sitesProvider.getCurrentSite().isOfflineDisabled(); - } - - /** - * Given a list of attempts, add finishedOffline=true to those attempts that are finished in offline but not synced. - * - * @param attempts List of attempts. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - loadFinishedOfflineData(attempts: any[], siteId?: string): Promise { - if (attempts.length) { - // We only need to check the last attempt because the user can only have 1 local attempt. - const lastAttempt = attempts[attempts.length - 1]; - - return this.isAttemptFinishedOffline(lastAttempt.id, siteId).then((finished) => { - lastAttempt.finishedOffline = finished; - }); - } - - return Promise.resolve(); - } - - /** - * Report an attempt as being viewed. It did not store logs offline because order of the log is important. - * - * @param attemptId Attempt ID. - * @param page Page number. - * @param preflightData Preflight required data (like password). - * @param offline Whether attempt is offline. - * @param quiz Quiz instance. If set, a Firebase event will be stored. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logViewAttempt(attemptId: number, page: number = 0, preflightData: any = {}, offline?: boolean, quiz?: any, - siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - attemptid: attemptId, - page: page, - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true) - }, - promises = []; - - promises.push(site.write('mod_quiz_view_attempt', params)); - if (offline) { - promises.push(this.quizOfflineProvider.setAttemptCurrentPage(attemptId, page, site.getId())); - } - if (quiz) { - this.pushNotificationsProvider.logViewEvent(quiz.id, quiz.name, 'quiz', 'mod_quiz_view_attempt', - {attemptid: attemptId, page: page}, siteId); - } - - return Promise.all(promises); - }); - } - - /** - * Report an attempt's review as being viewed. - * - * @param attemptId Attempt ID. - * @param quizId Quiz ID. - * @param name Name of the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logViewAttemptReview(attemptId: number, quizId: number, name?: string, siteId?: string): Promise { - const params = { - attemptid: attemptId - }; - - return this.logHelper.logSingle('mod_quiz_view_attempt_review', params, AddonModQuizProvider.COMPONENT, quizId, name, - 'quiz', params, siteId); - } - - /** - * Report an attempt's summary as being viewed. - * - * @param attemptId Attempt ID. - * @param preflightData Preflight required data (like password). - * @param quizId Quiz ID. - * @param name Name of the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logViewAttemptSummary(attemptId: number, preflightData: any, quizId: number, name?: string, siteId?: string): Promise { - const params = { - attemptid: attemptId, - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true) - }; - - return this.logHelper.logSingle('mod_quiz_view_attempt_summary', params, AddonModQuizProvider.COMPONENT, quizId, name, - 'quiz', {attemptid: attemptId}, siteId); - } - - /** - * Report a quiz as being viewed. - * - * @param id Module ID. - * @param name Name of the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logViewQuiz(id: number, name?: string, siteId?: string): Promise { - const params = { - quizid: id - }; - - return this.logHelper.logSingle('mod_quiz_view_quiz', params, AddonModQuizProvider.COMPONENT, id, name, 'quiz', {}, - siteId); - } - - /** - * Process an attempt, saving its data. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param data Data to save. - * @param preflightData Preflight required data (like password). - * @param finish Whether to finish the quiz. - * @param timeUp Whether the quiz time is up, false otherwise. - * @param offline Whether the attempt is offline. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - processAttempt(quiz: any, attempt: any, data: any, preflightData: any, finish?: boolean, timeUp?: boolean, offline?: boolean, - siteId?: string): Promise { - if (offline) { - return this.processAttemptOffline(quiz, attempt, data, preflightData, finish, siteId); - } - - return this.processAttemptOnline(attempt.id, data, preflightData, finish, timeUp, siteId); - } - - /** - * Process an online attempt, saving its data. - * - * @param attemptId Attempt ID. - * @param data Data to save. - * @param preflightData Preflight required data (like password). - * @param finish Whether to finish the quiz. - * @param timeUp Whether the quiz time is up, false otherwise. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - protected processAttemptOnline(attemptId: number, data: any, preflightData: any, finish?: boolean, timeUp?: boolean, - siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - attemptid: attemptId, - data: this.utils.objectToArrayOfObjects(data, 'name', 'value'), - finishattempt: finish ? 1 : 0, - timeup: timeUp ? 1 : 0, - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value') - }; - - return site.write('mod_quiz_process_attempt', params).then((response) => { - if (response && response.warnings && response.warnings.length) { - // Reject with the first warning. - return Promise.reject(response.warnings[0]); - } else if (response && response.state) { - return response.state; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Process an offline attempt, saving its data. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param data Data to save. - * @param preflightData Preflight required data (like password). - * @param finish Whether to finish the quiz. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - protected processAttemptOffline(quiz: any, attempt: any, data: any, preflightData: any, finish?: boolean, siteId?: string) - : Promise { - - // Get attempt summary to have the list of questions. - return this.getAttemptSummary(attempt.id, preflightData, { - cmId: quiz.coursemodule, - loadLocal: true, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId, - }).then((questionArray) => { - // Convert the question array to an object. - const questions = this.utils.arrayToObject(questionArray, 'slot'); - - return this.quizOfflineProvider.processAttempt(quiz, attempt, questions, data, finish, siteId); - }); - } - - /** - * Check if it's a graded quiz. Based on Moodle's quiz_has_grades. - * - * @param quiz Quiz. - * @return Whether quiz is graded. - */ - quizHasGrades(quiz: any): boolean { - return quiz.grade >= 0.000005 && quiz.sumgrades >= 0.000005; - } - - /** - * Convert the raw grade into a grade out of the maximum grade for this quiz. - * Based on Moodle's quiz_rescale_grade. - * - * @param rawGrade The unadjusted grade, for example attempt.sumgrades. - * @param quiz Quiz. - * @param format True to format the results for display, 'question' to format a question grade - * (different number of decimal places), false to not format it. - * @return Grade to display. - */ - rescaleGrade(rawGrade: string, quiz: any, format: boolean | string = true): string { - let grade: number; - - const rawGradeNum = parseFloat(rawGrade); - if (!isNaN(rawGradeNum)) { - if (quiz.sumgrades >= 0.000005) { - grade = rawGradeNum * quiz.grade / quiz.sumgrades; - } else { - grade = 0; - } - } - - if (format === 'question') { - return this.formatGrade(grade, this.getGradeDecimals(quiz)); - } else if (format) { - return this.formatGrade(grade, quiz.decimalpoints); - } - - if (grade === null) { - return null; - } else if (typeof grade == 'undefined') { - return undefined; - } - - return String(grade); - } - - /** - * Save an attempt data. - * - * @param quiz Quiz. - * @param attempt Attempt. - * @param data Data to save. - * @param preflightData Preflight required data (like password). - * @param offline Whether attempt is offline. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - saveAttempt(quiz: any, attempt: any, data: any, preflightData: any, offline?: boolean, siteId?: string): Promise { - try { - if (offline) { - return this.processAttemptOffline(quiz, attempt, data, preflightData, false, siteId); - } - - return this.saveAttemptOnline(attempt.id, data, preflightData, siteId); - } catch (ex) { - this.logger.error(ex); - - return Promise.reject(null); - } - } - - /** - * Save an attempt data. - * - * @param attemptId Attempt ID. - * @param data Data to save. - * @param preflightData Preflight required data (like password). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success, rejected otherwise. - */ - protected saveAttemptOnline(attemptId: number, data: any, preflightData: any, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - attemptid: attemptId, - data: this.utils.objectToArrayOfObjects(data, 'name', 'value'), - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value') - }; - - return site.write('mod_quiz_save_attempt', params).then((response) => { - if (response && response.warnings && response.warnings.length) { - // Reject with the first warning. - return Promise.reject(response.warnings[0]); - } else if (!response || !response.status) { - return Promise.reject(null); - } - }); - }); - } - - /** - * Check if time left should be shown. - * - * @param rules List of active rules names. - * @param attempt Attempt. - * @param endTime The attempt end time (in seconds). - * @return Whether time left should be displayed. - */ - shouldShowTimeLeft(rules: string[], attempt: any, endTime: number): boolean { - const timeNow = this.timeUtils.timestamp(); - - if (attempt.state != AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { - return false; - } - - return this.accessRulesDelegate.shouldShowTimeLeft(rules, attempt, endTime, timeNow); - } - - /** - * Start an attempt. - * - * @param quizId Quiz ID. - * @param preflightData Preflight required data (like password). - * @param forceNew Whether to force a new attempt or not. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the attempt data. - */ - startAttempt(quizId: number, preflightData: any, forceNew?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - quizid: quizId, - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true), - forcenew: forceNew ? 1 : 0 - }; - - return site.write('mod_quiz_start_attempt', params).then((response) => { - if (response && response.warnings && response.warnings.length) { - // Reject with the first warning. - return Promise.reject(response.warnings[0]); - } else if (response && response.attempt) { - return response.attempt; - } - - return Promise.reject(null); - }); - }); - } -} - -/** - * Common options with user ID. - */ -export type AddonModQuizUserOptions = CoreCourseCommonModWSOptions & { - userId?: number; // User ID. If not defined use site's current user. -}; - -/** - * Options to pass to getAllQuestionsData. - */ -export type AddonModQuizAllQuestionsDataOptions = CoreCourseCommonModWSOptions & { - pages?: number[]; // List of pages to get. If not defined, all pages. -}; - -/** - * Options to pass to getAttemptReview. - */ -export type AddonModQuizGetAttemptReviewOptions = CoreCourseCommonModWSOptions & { - page?: number; // List of pages to get. If not defined, all pages. -}; - -/** - * Options to pass to getAttemptSummary. - */ -export type AddonModQuizGetAttemptSummaryOptions = CoreCourseCommonModWSOptions & { - loadLocal?: boolean; // Whether it should load local state for each question. -}; - -/** - * Options to pass to getUserAttempts. - */ -export type AddonModQuizGetUserAttemptsOptions = CoreCourseCommonModWSOptions & { - status?: string; // Status of the attempts to get. By default, 'all'. - includePreviews?: boolean; // Whether to include previews. Defaults to true. - userId?: number; // User ID. If not defined use site's current user. -}; diff --git a/src/addon/mod/quiz/providers/review-link-handler.ts b/src/addon/mod/quiz/providers/review-link-handler.ts deleted file mode 100644 index 8fd018ca9..000000000 --- a/src/addon/mod/quiz/providers/review-link-handler.ts +++ /dev/null @@ -1,74 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { AddonModQuizProvider } from './quiz'; -import { AddonModQuizHelperProvider } from './helper'; - -/** - * Handler to treat links to quiz review. - */ -@Injectable() -export class AddonModQuizReviewLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModQuizReviewLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModQuiz'; - pattern = /\/mod\/quiz\/review\.php.*([\&\?]attempt=\d+)/; - - constructor(protected quizProvider: AddonModQuizProvider, protected quizHelper: AddonModQuizHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @param data Extra data to handle the URL. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number, data?: any): - CoreContentLinksAction[] | Promise { - - courseId = courseId || params.courseid || params.cid; - data = data || {}; - - return [{ - action: (siteId, navCtrl?): void => { - const attemptId = parseInt(params.attempt, 10), - page = parseInt(params.page, 10), - quizId = data.instance && parseInt(data.instance, 10); - - this.quizHelper.handleReviewLink(navCtrl, attemptId, page, courseId, quizId, siteId); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.quizProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/quiz/providers/sync-cron-handler.ts b/src/addon/mod/quiz/providers/sync-cron-handler.ts deleted file mode 100644 index 622f556f6..000000000 --- a/src/addon/mod/quiz/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModQuizSyncProvider } from './quiz-sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModQuizSyncCronHandler implements CoreCronHandler { - name = 'AddonModQuizSyncCronHandler'; - - constructor(private quizSync: AddonModQuizSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.quizSync.syncAllQuizzes(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.quizSync.syncInterval; - } -} diff --git a/src/addon/mod/quiz/quiz.module.ts b/src/addon/mod/quiz/quiz.module.ts deleted file mode 100644 index 62e92519f..000000000 --- a/src/addon/mod/quiz/quiz.module.ts +++ /dev/null @@ -1,105 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; -import { AddonModQuizAccessRuleDelegate } from './providers/access-rules-delegate'; -import { AddonModQuizProvider } from './providers/quiz'; -import { AddonModQuizOfflineProvider } from './providers/quiz-offline'; -import { AddonModQuizHelperProvider } from './providers/helper'; -import { AddonModQuizSyncProvider } from './providers/quiz-sync'; -import { AddonModQuizModuleHandler } from './providers/module-handler'; -import { AddonModQuizPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModQuizSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModQuizIndexLinkHandler } from './providers/index-link-handler'; -import { AddonModQuizGradeLinkHandler } from './providers/grade-link-handler'; -import { AddonModQuizReviewLinkHandler } from './providers/review-link-handler'; -import { AddonModQuizListLinkHandler } from './providers/list-link-handler'; -import { AddonModQuizPushClickHandler } from './providers/push-click-handler'; -import { AddonModQuizComponentsModule } from './components/components.module'; - -// Access rules. -import { AddonModQuizAccessDelayBetweenAttemptsModule } from './accessrules/delaybetweenattempts/delaybetweenattempts.module'; -import { AddonModQuizAccessIpAddressModule } from './accessrules/ipaddress/ipaddress.module'; -import { AddonModQuizAccessNumAttemptsModule } from './accessrules/numattempts/numattempts.module'; -import { AddonModQuizAccessOfflineAttemptsModule } from './accessrules/offlineattempts/offlineattempts.module'; -import { AddonModQuizAccessOpenCloseDateModule } from './accessrules/openclosedate/openclosedate.module'; -import { AddonModQuizAccessPasswordModule } from './accessrules/password/password.module'; -import { AddonModQuizAccessSafeBrowserModule } from './accessrules/safebrowser/safebrowser.module'; -import { AddonModQuizAccessSecureWindowModule } from './accessrules/securewindow/securewindow.module'; -import { AddonModQuizAccessTimeLimitModule } from './accessrules/timelimit/timelimit.module'; - -// List of providers (without handlers). -export const ADDON_MOD_QUIZ_PROVIDERS: any[] = [ - AddonModQuizAccessRuleDelegate, - AddonModQuizProvider, - AddonModQuizOfflineProvider, - AddonModQuizHelperProvider, - AddonModQuizSyncProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModQuizComponentsModule, - AddonModQuizAccessDelayBetweenAttemptsModule, - AddonModQuizAccessIpAddressModule, - AddonModQuizAccessNumAttemptsModule, - AddonModQuizAccessOfflineAttemptsModule, - AddonModQuizAccessOpenCloseDateModule, - AddonModQuizAccessPasswordModule, - AddonModQuizAccessSafeBrowserModule, - AddonModQuizAccessSecureWindowModule, - AddonModQuizAccessTimeLimitModule - ], - providers: [ - AddonModQuizAccessRuleDelegate, - AddonModQuizProvider, - AddonModQuizOfflineProvider, - AddonModQuizHelperProvider, - AddonModQuizSyncProvider, - AddonModQuizModuleHandler, - AddonModQuizPrefetchHandler, - AddonModQuizSyncCronHandler, - AddonModQuizIndexLinkHandler, - AddonModQuizGradeLinkHandler, - AddonModQuizReviewLinkHandler, - AddonModQuizListLinkHandler, - AddonModQuizPushClickHandler - ] -}) -export class AddonModQuizModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModQuizModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModQuizPrefetchHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModQuizSyncCronHandler, linksDelegate: CoreContentLinksDelegate, - indexHandler: AddonModQuizIndexLinkHandler, gradeHandler: AddonModQuizGradeLinkHandler, - reviewHandler: AddonModQuizReviewLinkHandler, - listLinkHandler: AddonModQuizListLinkHandler, - pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModQuizPushClickHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - cronDelegate.register(syncHandler); - linksDelegate.registerHandler(indexHandler); - linksDelegate.registerHandler(gradeHandler); - linksDelegate.registerHandler(reviewHandler); - linksDelegate.registerHandler(listLinkHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - } -} diff --git a/src/addon/mod/resource/components/components.module.ts b/src/addon/mod/resource/components/components.module.ts deleted file mode 100644 index cb608dcb2..000000000 --- a/src/addon/mod/resource/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModResourceIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModResourceIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModResourceIndexComponent - ], - entryComponents: [ - AddonModResourceIndexComponent - ] -}) -export class AddonModResourceComponentsModule {} diff --git a/src/addon/mod/resource/components/index/addon-mod-resource-index.html b/src/addon/mod/resource/components/index/addon-mod-resource-index.html deleted file mode 100644 index f910fabfb..000000000 --- a/src/addon/mod/resource/components/index/addon-mod-resource-index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
diff --git a/src/addon/mod/resource/components/index/index.ts b/src/addon/mod/resource/components/index/index.ts deleted file mode 100644 index 58810ad3d..000000000 --- a/src/addon/mod/resource/components/index/index.ts +++ /dev/null @@ -1,168 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Injector } from '@angular/core'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { - CoreCourseModuleMainResourceComponent, CoreCourseResourceDownloadResult -} from '@core/course/classes/main-resource-component'; -import { AddonModResourceProvider } from '../../providers/resource'; -import { AddonModResourceHelperProvider } from '../../providers/helper'; - -/** - * Component that displays a resource. - */ -@Component({ - selector: 'addon-mod-resource-index', - templateUrl: 'addon-mod-resource-index.html', -}) -export class AddonModResourceIndexComponent extends CoreCourseModuleMainResourceComponent { - component = AddonModResourceProvider.COMPONENT; - - canGetResource: boolean; - mode: string; - src: string; - contentText: string; - displayDescription = true; - warning: string; - - constructor(injector: Injector, - protected resourceProvider: AddonModResourceProvider, - protected resourceHelper: AddonModResourceHelperProvider, - protected utils: CoreUtilsProvider, - protected filepoolProvider: CoreFilepoolProvider) { - super(injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.canGetResource = this.resourceProvider.isGetResourceWSAvailable(); - - this.loadContent().then(() => { - this.resourceProvider.logView(this.module.instance, this.module.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return this.resourceProvider.invalidateContent(this.module.id, this.courseId); - } - - /** - * Download resource contents. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchContent(refresh?: boolean): Promise { - // Load module contents if needed. Passing refresh is needed to force reloading contents. - return this.courseProvider.loadModuleContents(this.module, this.courseId, null, false, refresh).then(() => { - if (!this.module.contents || !this.module.contents.length) { - return Promise.reject(this.utils.createFakeWSError('core.filenotfound', true)); - } - - // Get the resource instance to get the latest name/description and to know if it's embedded. - if (this.canGetResource) { - return this.resourceProvider.getResourceData(this.courseId, this.module.id).catch(() => { - // Ignore errors. - }); - } - - return this.courseProvider.getModule(this.module.id, this.courseId).catch(() => { - // Ignore errors. - }); - }).then((resource) => { - if (resource) { - this.description = resource.intro || resource.description; - const options = this.textUtils.unserialize(resource.displayoptions) || {}; - this.displayDescription = typeof options.printintro == 'undefined' || !!options.printintro; - this.dataRetrieved.emit(resource); - } - - if (this.resourceHelper.isDisplayedInIframe(this.module)) { - let downloadResult: CoreCourseResourceDownloadResult; - - return this.downloadResourceIfNeeded(refresh, true).then((result) => { - downloadResult = result; - }).then(() => { - return this.resourceHelper.getIframeSrc(this.module).then((src) => { - this.mode = 'iframe'; - - if (this.src && src.toString() == this.src.toString()) { - // Re-loading same page. - // Set it to empty and then re-set the src in the next digest so it detects it has changed. - this.src = ''; - setTimeout(() => { - this.src = src; - }); - } else { - this.src = src; - } - - this.warning = downloadResult.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error) : ''; - }); - }); - } else if (this.resourceHelper.isDisplayedEmbedded(this.module, resource && resource.display)) { - this.mode = 'embedded'; - this.warning = ''; - - return this.resourceHelper.getEmbeddedHtml(this.module, this.courseId).then((html) => { - this.contentText = html; - - this.mode = this.contentText.length > 0 ? 'embedded' : 'external'; - }); - } else { - this.mode = 'external'; - this.warning = ''; - } - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Opens a file. - * - * @return Promise resolved when done. - */ - async open(): Promise { - let downloadable = await this.modulePrefetchDelegate.isModuleDownloadable(this.module, this.courseId); - - if (downloadable) { - // Check if the main file is downloadle. - // This isn't done in "isDownloadable" to prevent extra WS calls in the course page. - downloadable = await this.resourceHelper.isMainFileDownloadable(this.module); - - if (downloadable) { - return this.resourceHelper.openModuleFile(this.module, this.courseId); - } - } - - // The resource cannot be downloaded, open the activity in browser. - return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(this.module.url); - } -} diff --git a/src/addon/mod/resource/lang/en.json b/src/addon/mod/resource/lang/en.json deleted file mode 100644 index bd7e9cefb..000000000 --- a/src/addon/mod/resource/lang/en.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "errorwhileloadingthecontent": "Error while loading the content.", - "modifieddate": "Modified {{$a}}", - "modulenameplural": "Files", - "openthefile": "Open the file", - "uploadeddate": "Uploaded {{$a}}" -} \ No newline at end of file diff --git a/src/addon/mod/resource/pages/index/index.html b/src/addon/mod/resource/pages/index/index.html deleted file mode 100644 index d52cc3f30..000000000 --- a/src/addon/mod/resource/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/resource/pages/index/index.module.ts b/src/addon/mod/resource/pages/index/index.module.ts deleted file mode 100644 index 1a207614d..000000000 --- a/src/addon/mod/resource/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModResourceComponentsModule } from '../../components/components.module'; -import { AddonModResourceIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModResourceIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModResourceComponentsModule, - IonicPageModule.forChild(AddonModResourceIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModResourceIndexPageModule {} diff --git a/src/addon/mod/resource/pages/index/index.ts b/src/addon/mod/resource/pages/index/index.ts deleted file mode 100644 index 331a1566d..000000000 --- a/src/addon/mod/resource/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModResourceIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a resource. - */ -@IonicPage({ segment: 'addon-mod-resource-index' }) -@Component({ - selector: 'page-addon-mod-resource-index', - templateUrl: 'index.html', -}) -export class AddonModResourceIndexPage { - @ViewChild(AddonModResourceIndexComponent) resourceComponent: AddonModResourceIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the resource instance. - * - * @param resource Resource instance. - */ - updateData(resource: any): void { - this.title = resource.name || this.title; - } -} diff --git a/src/addon/mod/resource/providers/helper.ts b/src/addon/mod/resource/providers/helper.ts deleted file mode 100644 index 62a00e511..000000000 --- a/src/addon/mod/resource/providers/helper.ts +++ /dev/null @@ -1,203 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModResourceProvider } from './resource'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreFileProvider } from '@providers/file'; -import { CoreFileHelperProvider } from '@providers/file-helper'; -import { CoreAppProvider } from '@providers/app'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Service that provides helper functions for resources. - */ -@Injectable() -export class AddonModResourceHelperProvider { - - /* Constants to determine how a resource should be displayed in Moodle. */ - // Try the best way. - protected DISPLAY_AUTO = 0; - // Display using object tag. - protected DISPLAY_EMBED = 1; - - constructor(protected courseProvider: CoreCourseProvider, - protected domUtils: CoreDomUtilsProvider, - protected resourceProvider: AddonModResourceProvider, - protected courseHelper: CoreCourseHelperProvider, - protected textUtils: CoreTextUtilsProvider, - protected mimetypeUtils: CoreMimetypeUtilsProvider, - protected fileProvider: CoreFileProvider, - protected appProvider: CoreAppProvider, - protected filepoolProvider: CoreFilepoolProvider, - protected sitesProvider: CoreSitesProvider, - protected fileHelper: CoreFileHelperProvider) { - } - - /** - * Get the HTML to display an embedded resource. - * - * @param module The module object. - * @param courseId The course ID. - * @return Promise resolved with the HTML. - */ - getEmbeddedHtml(module: any, courseId: number): Promise { - return this.courseHelper.downloadModuleWithMainFileIfNeeded(module, courseId, AddonModResourceProvider.COMPONENT, - module.id, module.contents).then((result) => { - return this.mimetypeUtils.getEmbeddedHtml(module.contents[0], result.path); - }); - } - - /** - * Download all the files needed and returns the src of the iframe. - * - * @param module The module object. - * @return Promise resolved with the iframe src. - */ - getIframeSrc(module: any): Promise { - if (!module.contents.length) { - return Promise.reject(null); - } - - const mainFile = module.contents[0]; - let mainFilePath = mainFile.filename; - - if (mainFile.filepath !== '/') { - mainFilePath = mainFile.filepath.substr(1) + mainFilePath; - } - - return this.filepoolProvider.getPackageDirUrlByUrl(this.sitesProvider.getCurrentSiteId(), module.url).then((dirPath) => { - // This URL is going to be injected in an iframe, we need trustAsResourceUrl to make it work in a browser. - return this.textUtils.concatenatePaths(dirPath, mainFilePath); - }).catch(() => { - // Error getting directory, there was an error downloading or we're in browser. Return online URL. - if (this.appProvider.isOnline() && mainFile.fileurl) { - // This URL is going to be injected in an iframe, we need this to make it work. - return this.sitesProvider.getCurrentSite().checkAndFixPluginfileURL(mainFile.fileurl); - } - - return Promise.reject(null); - }); - } - - /** - * Whether the resource has to be displayed embedded. - * - * @param module The module object. - * @param display The display mode (if available). - * @return Whether the resource should be displayed embeded. - */ - isDisplayedEmbedded(module: any, display: number): boolean { - const currentSite = this.sitesProvider.getCurrentSite(); - - if ((!module.contents.length && !module.contentsinfo) || !this.fileProvider.isAvailable() || - (currentSite && !currentSite.isVersionGreaterEqualThan('3.7') && this.isNextcloudFile(module))) { - return false; - } - - let ext; - - if (module.contentsinfo) { - ext = this.mimetypeUtils.getExtension(module.contentsinfo.mimetypes[0]); - } else { - ext = this.mimetypeUtils.getFileExtension(module.contents[0].filename); - } - - return (display == this.DISPLAY_EMBED || display == this.DISPLAY_AUTO) && this.mimetypeUtils.canBeEmbedded(ext); - } - - /** - * Whether the resource has to be displayed in an iframe. - * - * @param module The module object. - * @return Whether the resource should be displayed in an iframe. - */ - isDisplayedInIframe(module: any): boolean { - if ((!module.contents.length && !module.contentsinfo) || !this.fileProvider.isAvailable()) { - return false; - } - - let mimetype; - - if (module.contentsinfo) { - mimetype = module.contentsinfo.mimetypes[0]; - } else { - const ext = this.mimetypeUtils.getFileExtension(module.contents[0].filename); - mimetype = this.mimetypeUtils.getMimeType(ext); - } - - return mimetype == 'text/html'; - } - - /** - * Check if main file of resource is downloadable. - * - * @param module Module instance. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether main file is downloadable. - */ - isMainFileDownloadable(module: any, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const mainFile = module.contents[0]; - const fileUrl = this.fileHelper.getFileUrl(mainFile); - const timemodified = this.fileHelper.getFileTimemodified(mainFile); - - return this.filepoolProvider.isFileDownloadable(siteId, fileUrl, timemodified); - } - - /** - * Check if the resource is a Nextcloud file. - * - * @param module Module to check. - * @return Whether it's a Nextcloud file. - */ - isNextcloudFile(module: any): boolean { - if (module.contentsinfo) { - return module.contentsinfo.repositorytype == 'nextcloud'; - } - - return module.contents && module.contents[0] && module.contents[0].repositorytype == 'nextcloud'; - } - - /** - * Opens a file of the resource activity. - * - * @param module Module where to get the contents. - * @param courseId Course Id, used for completion purposes. - * @return Resolved when done. - */ - openModuleFile(module: any, courseId: number): Promise { - const modal = this.domUtils.showModalLoading(); - - // Download and open the file from the resource contents. - return this.courseHelper.downloadModuleAndOpenFile(module, courseId, AddonModResourceProvider.COMPONENT, module.id, - module.contents).then(() => { - this.resourceProvider.logView(module.instance, module.name).then(() => { - this.courseProvider.checkModuleCompletion(courseId, module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_resource.errorwhileloadingthecontent', true); - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/mod/resource/providers/link-handler.ts b/src/addon/mod/resource/providers/link-handler.ts deleted file mode 100644 index a9cc54f1a..000000000 --- a/src/addon/mod/resource/providers/link-handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModResourceProvider } from './resource'; - -/** - * Handler to treat links to resource. - */ -@Injectable() -export class AddonModResourceLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModResourceLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, - protected resourceProvider: AddonModResourceProvider) { - super(courseHelper, 'AddonModResource', 'resource', 'r'); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.resourceProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/resource/providers/list-link-handler.ts b/src/addon/mod/resource/providers/list-link-handler.ts deleted file mode 100644 index 5101685ad..000000000 --- a/src/addon/mod/resource/providers/list-link-handler.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModResourceProvider } from './resource'; - -/** - * Handler to treat links to resource list page. - */ -@Injectable() -export class AddonModResourceListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModResourceListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected resourceProvider: AddonModResourceProvider) { - super(linkHelper, translate, 'AddonModResource', 'resource'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.resourceProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/resource/providers/module-handler.ts b/src/addon/mod/resource/providers/module-handler.ts deleted file mode 100644 index 735bcae53..000000000 --- a/src/addon/mod/resource/providers/module-handler.ts +++ /dev/null @@ -1,248 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModResourceProvider } from './resource'; -import { AddonModResourceHelperProvider } from './helper'; -import { AddonModResourceIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreConstants } from '@core/constants'; -import { CoreWSExternalFile } from '@providers/ws'; - -/** - * Handler to support resource modules. - */ -@Injectable() -export class AddonModResourceModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModResource'; - modName = 'resource'; - - supportedFeatures = { - [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - protected statusObserver; - - constructor(protected resourceProvider: AddonModResourceProvider, private courseProvider: CoreCourseProvider, - protected mimetypeUtils: CoreMimetypeUtilsProvider, private resourceHelper: AddonModResourceHelperProvider, - protected prefetchDelegate: CoreCourseModulePrefetchDelegate, protected textUtils: CoreTextUtilsProvider, - protected translate: TranslateService, protected timeUtils: CoreTimeUtilsProvider) { - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.resourceProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - const updateStatus = (status: string): void => { - handlerData.buttons[0].hidden = status !== CoreConstants.DOWNLOADED || - this.resourceHelper.isDisplayedInIframe(module); - }; - - const handlerData: CoreCourseModuleHandlerData = { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_resource-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModResourceIndexPage', pageParams, options); - }, - updateStatus: updateStatus.bind(this), - buttons: [ { - hidden: true, - icon: 'document', - label: 'addon.mod_resource.openthefile', - action: (event: Event, navCtrl: NavController, module: any, courseId: number): void => { - this.hideOpenButton(module, courseId).then((hide) => { - if (!hide) { - this.resourceHelper.openModuleFile(module, courseId); - } - }); - } - } ] - }; - - this.getResourceData(module, courseId, handlerData).then((data) => { - handlerData.icon = data.icon; - handlerData.extraBadge = data.extra; - handlerData.extraBadgeColor = 'light'; - }); - - return handlerData; - } - - /** - * Returns if contents are loaded to show open button. - * - * @param module The module object. - * @param courseId The course ID. - * @return Resolved when done. - */ - protected hideOpenButton(module: any, courseId: number): Promise { - let promise; - - if (module.contentsinfo) { - // No need to load contents. - promise = Promise.resolve(); - } else { - promise = this.courseProvider.loadModuleContents(module, courseId, undefined, false, false, undefined, this.modName); - } - - return promise.then(() => { - return this.prefetchDelegate.getModuleStatus(module, courseId).then((status) => { - return status !== CoreConstants.DOWNLOADED || this.resourceHelper.isDisplayedInIframe(module); - }); - }); - } - - /** - * Returns the activity icon and data. - * - * @param module The module object. - * @param courseId The course ID. - * @return Resource data. - */ - protected getResourceData(module: any, courseId: number, handlerData: CoreCourseModuleHandlerData): Promise { - const promises = []; - let infoFiles: CoreWSExternalFile[] = [], - options: any = {}; - - // Check if the button needs to be shown or not. - promises.push(this.hideOpenButton(module, courseId).then((hideOpenButton) => { - handlerData.buttons[0].hidden = hideOpenButton; - })); - - if (typeof module.customdata != 'undefined') { - options = this.textUtils.unserialize(this.textUtils.parseJSON(module.customdata)); - } else if (this.resourceProvider.isGetResourceWSAvailable()) { - // Get the resource data. - promises.push(this.resourceProvider.getResourceData(courseId, module.id).then((info) => { - infoFiles = info.contentfiles; - options = this.textUtils.unserialize(info.displayoptions); - })); - } - - return Promise.all(promises).then(() => { - const files = module.contents && module.contents.length ? module.contents : infoFiles, - resourceData = { - icon: '', - extra: '' - }, - extra = []; - - if (module.contentsinfo) { - // No need to use the list of files. - const mimetype = module.contentsinfo.mimetypes[0]; - if (mimetype) { - resourceData.icon = this.mimetypeUtils.getMimetypeIcon(mimetype); - } - resourceData.extra = this.textUtils.cleanTags(module.afterlink); - - } else if (files && files.length) { - const file = files[0]; - - resourceData.icon = this.mimetypeUtils.getFileIcon(file.filename); - - if (options.showsize) { - let size; - if (options.filedetails) { - size = options.filedetails.size; - } else { - size = files.reduce((result, file) => { - return result + file.filesize; - }, 0); - } - - extra.push(this.textUtils.bytesToSize(size, 1)); - } - - if (options.showtype) { - // We should take it from options.filedetails.size if avalaible but it's already translated. - extra.push(this.mimetypeUtils.getMimetypeDescription(file)); - } - - if (options.showdate) { - if (options.filedetails && options.filedetails.modifieddate) { - extra.push(this.translate.instant('addon.mod_resource.modifieddate', - {$a: this.timeUtils.userDate(options.filedetails.modifieddate * 1000, 'core.strftimedatetimeshort') })); - } else if (options.filedetails && options.filedetails.uploadeddate) { - extra.push(this.translate.instant('addon.mod_resource.uploadeddate', - {$a: this.timeUtils.userDate(options.filedetails.uploadeddate * 1000, 'core.strftimedatetimeshort') })); - } else if (file.timemodified > file.timecreated + CoreConstants.SECONDS_MINUTE * 5) { - /* Modified date may be up to several minutes later than uploaded date just because - teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */ - extra.push(this.translate.instant('addon.mod_resource.modifieddate', - {$a: this.timeUtils.userDate(file.timemodified * 1000, 'core.strftimedatetimeshort') })); - } else { - extra.push(this.translate.instant('addon.mod_resource.uploadeddate', - {$a: this.timeUtils.userDate(file.timecreated * 1000, 'core.strftimedatetimeshort') })); - } - } - - resourceData.extra += extra.join(' '); - } - - // No previously set, just set the icon. - if (resourceData.icon == '') { - resourceData.icon = this.courseProvider.getModuleIconSrc(this.modName, module.modicon); - } - - return resourceData; - }); - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModResourceIndexComponent; - } -} diff --git a/src/addon/mod/resource/providers/pluginfile-handler.ts b/src/addon/mod/resource/providers/pluginfile-handler.ts deleted file mode 100644 index f9179a955..000000000 --- a/src/addon/mod/resource/providers/pluginfile-handler.ts +++ /dev/null @@ -1,59 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CorePluginFileHandler } from '@providers/plugin-file-delegate'; - -/** - * Handler to treat links to resource. - */ -@Injectable() -export class AddonModResourcePluginFileHandler implements CorePluginFileHandler { - name = 'AddonModResourcePluginFileHandler'; - component = 'mod_resource'; - - /** - * Return the RegExp to match the revision on pluginfile URLs. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return RegExp to match the revision on pluginfile URLs. - */ - getComponentRevisionRegExp(args: string[]): RegExp { - // Check filearea. - if (args[2] == 'content') { - // Component + Filearea + Revision - return new RegExp('/mod_resource/content/([0-9]+)/'); - } - } - - /** - * Should return the string to remove the revision on pluginfile url. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return String to remove the revision on pluginfile url. - */ - getComponentRevisionReplace(args: string[]): string { - // Component + Filearea + Revision - return '/mod_resource/content/0/'; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/resource/providers/prefetch-handler.ts b/src/addon/mod/resource/providers/prefetch-handler.ts deleted file mode 100644 index 17f775cce..000000000 --- a/src/addon/mod/resource/providers/prefetch-handler.ts +++ /dev/null @@ -1,169 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; -import { AddonModResourceProvider } from './resource'; -import { AddonModResourceHelperProvider } from './helper'; -import { CoreConstants } from '@core/constants'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch resources. - */ -@Injectable() -export class AddonModResourcePrefetchHandler extends CoreCourseResourcePrefetchHandlerBase { - name = 'AddonModResource'; - modName = 'resource'; - component = AddonModResourceProvider.COMPONENT; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected resourceProvider: AddonModResourceProvider, - protected resourceHelper: AddonModResourceHelperProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Return the status to show based on current status. - * - * @param module Module. - * @param status The current status. - * @param canCheck Whether the site allows checking for updates. - * @return Status to display. - */ - determineStatus(module: any, status: string, canCheck: boolean): string { - if (status == CoreConstants.DOWNLOADED && module) { - // If the main file is an external file, always display the module as outdated. - if (module.contentsinfo) { - if (module.contentsinfo.repositorytype) { - // It's an external file. - return CoreConstants.OUTDATED; - } - } else if (module.contents) { - const mainFile = module.contents[0]; - if (mainFile && mainFile.isexternalfile) { - return CoreConstants.OUTDATED; - } - } - } - - return status; - } - - /** - * Download or prefetch the content. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param prefetch True to prefetch, false to download right away. - * @param dirPath Path of the directory where to store all the content files. This is to keep the files - * relative paths and make the package work in an iframe. Undefined to download the files - * in the filepool root folder. - * @return Promise resolved when all content is downloaded. Data returned is not reliable. - */ - downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise { - let promise; - - if (this.resourceHelper.isDisplayedInIframe(module)) { - promise = this.filepoolProvider.getPackageDirPathByUrl(this.sitesProvider.getCurrentSiteId(), module.url); - } else { - promise = Promise.resolve(); - } - - return promise.then((dirPath) => { - const promises = []; - - promises.push(super.downloadOrPrefetch(module, courseId, prefetch, dirPath)); - - if (this.resourceProvider.isGetResourceWSAvailable()) { - promises.push(this.resourceProvider.getResourceData(courseId, module.id)); - } - - return Promise.all(promises); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.resourceProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const promises = []; - - promises.push(this.resourceProvider.invalidateResourceData(courseId)); - promises.push(this.courseProvider.invalidateModule(module.id, undefined, this.modName)); - - return Promise.all(promises); - } - - /** - * Check if a resource is downloadable. - * - * @param module Module to check. - * @param courseId Course ID the module belongs to. - * @return Promise resolved with true if downloadable, resolved with false otherwise. - */ - isDownloadable(module: any, courseId: number): Promise { - if (this.sitesProvider.getCurrentSite() && this.sitesProvider.getCurrentSite().isVersionGreaterEqualThan('3.7')) { - // Nextcloud files are downloadable from 3.7 onwards. - return Promise.resolve(true); - } - - // Don't allow downloading Nextcloud files in older sites. - return this.loadContents(module, courseId, false).then(() => { - return !this.resourceHelper.isNextcloudFile(module); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.resourceProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/resource/providers/resource.ts b/src/addon/mod/resource/providers/resource.ts deleted file mode 100644 index b8b5b2fd2..000000000 --- a/src/addon/mod/resource/providers/resource.ts +++ /dev/null @@ -1,208 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; - -/** - * Service that provides some features for resources. - */ -@Injectable() -export class AddonModResourceProvider { - static COMPONENT = 'mmaModResource'; - - protected ROOT_CACHE_KEY = 'mmaModResource:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, - private filepoolProvider: CoreFilepoolProvider, private utils: CoreUtilsProvider, - private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModResourceProvider'); - } - - /** - * Get cache key for resource data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getResourceCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'resource:' + courseId; - } - - /** - * Get a resource data. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the resource is retrieved. - */ - protected getResourceDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getResourceCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModResourceProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_resource_get_resources_by_courses', params, preSets) - .then((response: AddonModResourceGetResourcesByCoursesResult): any => { - - if (response && response.resources) { - const currentResource = response.resources.find((resource) => { - return resource[key] == value; - }); - if (currentResource) { - return currentResource; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a resource by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the resource is retrieved. - */ - getResourceData(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getResourceDataByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.invalidateResourceData(courseId, siteId)); - promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModResourceProvider.COMPONENT, moduleId)); - promises.push(this.courseProvider.invalidateModule(moduleId, siteId, 'resource')); - - return this.utils.allPromises(promises); - } - - /** - * Invalidates resource data. - * - * @param courseid Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateResourceData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getResourceCacheKey(courseId)); - }); - } - - /** - * Returns whether or not getResource WS available or not. - * - * @return If WS is abalaible. - * @since 3.3 - */ - isGetResourceWSAvailable(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('mod_resource_get_resources_by_courses'); - } - - /** - * Return whether or not the plugin is enabled. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.canDownloadFiles(); - }); - } - - /** - * Report the resource as being viewed. - * - * @param id Module ID. - * @param name Name of the resource. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - resourceid: id - }; - - return this.logHelper.logSingle('mod_resource_view_resource', params, AddonModResourceProvider.COMPONENT, id, name, - 'resource', {}, siteId); - } -} - -/** - * Resource returned by mod_resource_get_resources_by_courses. - */ -export type AddonModResourceResource = { - id: number; // Module id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Page name. - intro: string; // Summary. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles: CoreWSExternalFile[]; - contentfiles: CoreWSExternalFile[]; - tobemigrated: number; // Whether this resource was migrated. - legacyfiles: number; // Legacy files flag. - legacyfileslast: number; // Legacy files last control flag. - display: number; // How to display the resource. - displayoptions: string; // Display options (width, height). - filterfiles: number; // If filters should be applied to the resource content. - revision: number; // Incremented when after each file changes, to avoid cache. - timemodified: number; // Last time the resource was modified. - section: number; // Course section id. - visible: number; // Module visibility. - groupmode: number; // Group mode. - groupingid: number; // Grouping id. -}; - -/** - * Result of WS mod_resource_get_resources_by_courses. - */ -export type AddonModResourceGetResourcesByCoursesResult = { - resources: AddonModResourceResource[]; - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/resource/resource.module.ts b/src/addon/mod/resource/resource.module.ts deleted file mode 100644 index d2d2be4e9..000000000 --- a/src/addon/mod/resource/resource.module.ts +++ /dev/null @@ -1,63 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModResourceComponentsModule } from './components/components.module'; -import { AddonModResourceModuleHandler } from './providers/module-handler'; -import { AddonModResourceProvider } from './providers/resource'; -import { AddonModResourcePrefetchHandler } from './providers/prefetch-handler'; -import { AddonModResourceLinkHandler } from './providers/link-handler'; -import { AddonModResourceListLinkHandler } from './providers/list-link-handler'; -import { AddonModResourcePluginFileHandler } from './providers/pluginfile-handler'; -import { AddonModResourceHelperProvider } from './providers/helper'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -// List of providers (without handlers). -export const ADDON_MOD_RESOURCE_PROVIDERS: any[] = [ - AddonModResourceProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModResourceComponentsModule - ], - providers: [ - AddonModResourceProvider, - AddonModResourceModuleHandler, - AddonModResourceHelperProvider, - AddonModResourcePrefetchHandler, - AddonModResourceLinkHandler, - AddonModResourceListLinkHandler, - AddonModResourcePluginFileHandler - ] -}) -export class AddonModResourceModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModResourceModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModResourcePrefetchHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModResourceLinkHandler, - pluginfileDelegate: CorePluginFileDelegate, pluginfileHandler: AddonModResourcePluginFileHandler, - listLinkHandler: AddonModResourceListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - pluginfileDelegate.registerHandler(pluginfileHandler); - } -} diff --git a/src/addon/mod/scorm/classes/data-model-12.ts b/src/addon/mod/scorm/classes/data-model-12.ts deleted file mode 100644 index ef615fede..000000000 --- a/src/addon/mod/scorm/classes/data-model-12.ts +++ /dev/null @@ -1,905 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreEventsProvider } from '@providers/events'; -import { AddonModScormProvider } from '../providers/scorm'; - -/** - * SCORM data model implementation for version 1.2. - */ -export class AddonModScormDataModel12 { - - // Standard Data Type Definition. - protected CMI_STRING_256 = '^[\\u0000-\\uFFFF]{0,255}$'; - protected CMI_STRING_4096 = '^[\\u0000-\\uFFFF]{0,4096}$'; - protected CMI_TIME = '^([0-2]{1}[0-9]{1}):([0-5]{1}[0-9]{1}):([0-5]{1}[0-9]{1})(\.[0-9]{1,2})?$'; - protected CMI_TIMESPAN = '^([0-9]{2,4}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,2})?$'; - protected CMI_INTEGER = '^\\d+$'; - protected CMI_SINTEGER = '^-?([0-9]+)$'; - protected CMI_DECIMAL = '^-?([0-9]{0,3})(\.[0-9]*)?$'; - protected CMI_IDENTIFIER = '^[\\u0021-\\u007E]{0,255}$'; - protected CMI_FEEDBACK = this.CMI_STRING_256; // This must be redefined. - protected CMI_INDEX = '[._](\\d+).'; - - // Vocabulary Data Type Definition. - protected CMI_STATUS = '^passed$|^completed$|^failed$|^incomplete$|^browsed$'; - protected CMI_STATUS_2 = '^passed$|^completed$|^failed$|^incomplete$|^browsed$|^not attempted$'; - protected CMI_EXIT = '^time-out$|^suspend$|^logout$|^$'; - protected CMI_TYPE = '^true-false$|^choice$|^fill-in$|^matching$|^performance$|^sequencing$|^likert$|^numeric$'; - protected CMI_RESULT = '^correct$|^wrong$|^unanticipated$|^neutral$|^([0-9]{0,3})?(\.[0-9]*)?$'; - protected NAV_EVENT = '^previous$|^continue$'; - - // Children lists. - protected CMI_CHILDREN = 'core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions'; - protected CORE_CHILDREN = 'student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,lesson_mode,' + - 'exit,session_time'; - protected SCORE_CHILDREN = 'raw,min,max'; - protected COMMENTS_CHILDREN = 'content,location,time'; - protected OBJECTIVES_CHILDREN = 'id,score,status'; - protected CORRECT_RESPONSES_CHILDREN = 'pattern'; - protected STUDENT_DATA_CHILDREN = 'mastery_score,max_time_allowed,time_limit_action'; - protected STUDENT_PREFERENCE_CHILDREN = 'audio,language,speed,text'; - protected INTERACTIONS_CHILDREN = 'id,objectives,time,type,correct_responses,weighting,student_response,result,latency'; - - // Data ranges. - protected SCORE_RANGE = '0#100'; - protected AUDIO_RANGE = '-1#100'; - protected SPEED_RANGE = '-100#100'; - protected WEIGHTING_RANGE = '-100#100'; - protected TEXT_RANGE = '-1#1'; - - // Error messages. - protected ERROR_STRINGS = { - 0: 'No error', - 101: 'General exception', - 201: 'Invalid argument error', - 202: 'Element cannot have children', - 203: 'Element not an array - cannot have count', - 301: 'Not initialized', - 401: 'Not implemented error', - 402: 'Invalid set value, element is a keyword', - 403: 'Element is read only', - 404: 'Element is write only', - 405: 'Incorrect data type' - }; - - protected currentUserData = {}; // Current user data. - protected def = {}; // Object containing the default values. - protected defExtra = {}; // Extra object that will contain the objectives and interactions data (all the .n. elements). - protected dataModel = {}; // The SCORM 1.2 data model. - - protected initialized = false; // Whether LMSInitialize has been called. - protected errorCode: string; // Last error. - protected timeout; // Timeout to commit changes. - - /** - * Constructor. - * - * @param eventsProvider Events provider instance. - * @param scormProvider SCORM provider instance. - * @param scorm SCORM. - * @param scoId Current SCO ID. - * @param attempt Attempt number. - * @param userData The user default data. - * @param mode Mode being played. By default, MODENORMAL. - * @param offline Whether the attempt is offline. - */ - constructor(protected eventsProvider: CoreEventsProvider, protected scormProvider: AddonModScormProvider, - protected siteId: string, protected scorm: any, protected scoId: number, protected attempt: number, - userData: any, protected mode?: string, protected offline?: boolean) { - - this.mode = mode || AddonModScormProvider.MODENORMAL; - this.offline = !!offline; - - this.init(userData); - } - - /** - * Utility function for adding two times in format hh:mm:ss. - * - * @param first First time. - * @param second Second time. - * @return Total time. - */ - protected addTime(first: string, second: string): string { - const sFirst = first.split(':'), - sSecond = second.split(':'), - cFirst = sFirst[2].split('.'), - cSecond = sSecond[2].split('.'); - let change = 0; - - let firstCents = 0; // Cents. - if (cFirst.length > 1) { - firstCents = parseInt(cFirst[1], 10); - } - - let secondCents = 0; - if (cSecond.length > 1) { - secondCents = parseInt(cSecond[1], 10); - } - - let cents: string | number = firstCents + secondCents; - change = Math.floor(cents / 100); - cents = cents - (change * 100); - if (Math.floor(cents) < 10) { - cents = '0' + cents.toString(); - } - - let secs: string | number = parseInt(cFirst[0], 10) + parseInt(cSecond[0], 10) + change; // Seconds. - change = Math.floor(secs / 60); - secs = secs - (change * 60); - if (Math.floor(secs) < 10) { - secs = '0' + secs.toString(); - } - - let mins: string | number = parseInt(sFirst[1], 10) + parseInt(sSecond[1], 10) + change; // Minutes. - change = Math.floor(mins / 60); - mins = mins - (change * 60); - if (mins < 10) { - mins = '0' + mins.toString(); - } - - let hours: string | number = parseInt(sFirst[0], 10) + parseInt(sSecond[0], 10) + change; // Hours. - if (hours < 10) { - hours = '0' + hours.toString(); - } - - if (cents != '0') { - return hours + ':' + mins + ':' + secs + '.' + cents; - } else { - return hours + ':' + mins + ':' + secs; - } - } - - /** - * Utility function for cloning an object - * - * @param obj The object to be cloned - * @return The object cloned - */ - protected cloneObj(obj: any): any { - if (obj == null || typeof(obj) != 'object') { - return obj; - } - - const temp = new obj.constructor(); // Changed (twice). - for (const key in obj) { - temp[key] = this.cloneObj(obj[key]); - } - - return temp; - } - - /** - * Collect all the user tracking data that must be persisted in the system, this is usually called by LMSCommit(). - * - * @return Collected data. - */ - protected collectData(): any[] { - const data = []; - - for (const element in this.currentUserData[this.scoId]) { - // Ommit for example the nav. elements. - if (element.substr(0, 3) == 'cmi') { - const expression = new RegExp(this.CMI_INDEX, 'g'); - - // Get the generic name for this element (e.g. convert 'cmi.interactions.1.id' to 'cmi.interactions.n.id') - const elementModel = String(element).replace(expression, '.n.'); - - // Ignore the session time element. - if (element != 'cmi.core.session_time') { - - // Check if this specific element is not defined in the datamodel, but the generic element name is. - if (typeof this.dataModel[this.scoId][element] == 'undefined' && - typeof this.dataModel[this.scoId][elementModel] != 'undefined') { - - // Add this element to the data model (by cloning the generic element) so we can track changes to it. - this.dataModel[this.scoId][element] = this.cloneObj(this.dataModel[this.scoId][elementModel]); - } - - // Check if the current element exists in the datamodel. - if (typeof this.dataModel[this.scoId][element] != 'undefined') { - - // Make sure this is not a read only element. - if (this.dataModel[this.scoId][element].mod != 'r') { - - const el = { - // Moodle stores the organizations and interactions using _n. instead .n. - element: element.replace(expression, '_$1.'), - value: this.getEl(element) - }; - - // Check if the element has a default value. - if (typeof this.dataModel[this.scoId][element].defaultvalue != 'undefined') { - - // Check if the default value is different from the current value. - if (this.dataModel[this.scoId][element].defaultvalue != el.value || - typeof this.dataModel[this.scoId][element].defaultvalue != typeof(el.value)) { - - data.push(el); - - // Update the element default to reflect the current committed value. - this.dataModel[this.scoId][element].defaultvalue = el.value; - } - } else { - data.push(el); - - // No default value for the element, so set it now. - this.dataModel[this.scoId][element].defaultvalue = el.value; - } - } - } - } - } - } - - return data; - } - - /** - * Get the value of the given element from the non-persistent (current) user data. - * - * @param el The element - * @return The element value - */ - protected getEl(el: string): any { - if (typeof this.currentUserData[this.scoId] != 'undefined' && typeof this.currentUserData[this.scoId][el] != 'undefined') { - return this.currentUserData[this.scoId][el]; - } - - return ''; - } - - /** - * Initialize the model. - * - * @param userData The user default data. - */ - protected init(userData: any): void { - // Prepare the definition array containing the default values. - for (const scoId in userData) { - const sco = userData[scoId]; - this.def[scoId] = sco.defaultdata; - this.defExtra[scoId] = sco.userdata; - } - - // Set up data model for each SCO. - for (const scoId in this.def) { - this.dataModel[scoId] = { - 'cmi._children': { defaultvalue: this.CMI_CHILDREN, mod: 'r', writeerror: '402' }, - 'cmi._version': { defaultvalue: '3.4', mod: 'r', writeerror: '402' }, - 'cmi.core._children': { defaultvalue: this.CORE_CHILDREN, mod: 'r', writeerror: '402' }, - 'cmi.core.student_id': { defaultvalue: this.def[scoId]['cmi.core.student_id'], mod: 'r', writeerror: '403' }, - 'cmi.core.student_name': { defaultvalue: this.def[scoId]['cmi.core.student_name'], mod: 'r', writeerror: '403' }, - 'cmi.core.lesson_location': { defaultvalue: this.def[scoId]['cmi.core.lesson_location'], - format: this.CMI_STRING_256, mod: 'rw', writeerror: '405' }, - 'cmi.core.credit': { defaultvalue: this.def[scoId]['cmi.core.credit'], mod: 'r', writeerror: '403' }, - 'cmi.core.lesson_status': { defaultvalue: this.def[scoId]['cmi.core.lesson_status'], format: this.CMI_STATUS, - mod: 'rw', writeerror: '405' }, - 'cmi.core.entry': { defaultvalue: this.def[scoId]['cmi.core.entry'], mod: 'r', writeerror: '403' }, - 'cmi.core.score._children': { defaultvalue: this.SCORE_CHILDREN, mod: 'r', writeerror: '402' }, - 'cmi.core.score.raw': { defaultvalue: this.def[scoId]['cmi.core.score.raw'], format: this.CMI_DECIMAL, - range: this.SCORE_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.core.score.max': { defaultvalue: this.def[scoId]['cmi.core.score.max'], format: this.CMI_DECIMAL, - range: this.SCORE_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.core.score.min': { defaultvalue: this.def[scoId]['cmi.core.score.min'], format: this.CMI_DECIMAL, - range: this.SCORE_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.core.total_time': { defaultvalue: this.def[scoId]['cmi.core.total_time'], mod: 'r', writeerror: '403' }, - 'cmi.core.lesson_mode': { defaultvalue: this.def[scoId]['cmi.core.lesson_mode'], mod: 'r', writeerror: '403' }, - 'cmi.core.exit': { defaultvalue: this.def[scoId]['cmi.core.exit'], format: this.CMI_EXIT, mod: 'w', - readerror: '404', writeerror: '405' }, - 'cmi.core.session_time': { format: this.CMI_TIMESPAN, mod: 'w', defaultvalue: '00:00:00', readerror: '404', - writeerror: '405' }, - 'cmi.suspend_data': { defaultvalue: this.def[scoId]['cmi.suspend_data'], format: this.CMI_STRING_4096, - mod: 'rw', writeerror: '405' }, - 'cmi.launch_data': { defaultvalue: this.def[scoId]['cmi.launch_data'], mod: 'r', writeerror: '403' }, - 'cmi.comments': { defaultvalue: this.def[scoId]['cmi.comments'], format: this.CMI_STRING_4096, mod: 'rw', - writeerror: '405' }, - // Deprecated evaluation attributes. - 'cmi.evaluation.comments._count': { defaultvalue: '0', mod: 'r', writeerror: '402' }, - 'cmi.evaluation.comments._children': { defaultvalue: this.COMMENTS_CHILDREN, mod: 'r', writeerror: '402' }, - 'cmi.evaluation.comments.n.content': { defaultvalue: '', pattern: this.CMI_INDEX, format: this.CMI_STRING_256, - mod: 'rw', writeerror: '405' }, - 'cmi.evaluation.comments.n.location': { defaultvalue: '', pattern: this.CMI_INDEX, format: this.CMI_STRING_256, - mod: 'rw', writeerror: '405' }, - 'cmi.evaluation.comments.n.time': { defaultvalue: '', pattern: this.CMI_INDEX, format: this.CMI_TIME, - mod: 'rw', writeerror: '405' }, - 'cmi.comments_from_lms': { mod: 'r', writeerror: '403' }, - 'cmi.objectives._children': { defaultvalue: this.OBJECTIVES_CHILDREN, mod: 'r', writeerror: '402' }, - 'cmi.objectives._count': { mod: 'r', defaultvalue: '0', writeerror: '402' }, - 'cmi.objectives.n.id': { pattern: this.CMI_INDEX, format: this.CMI_IDENTIFIER, mod: 'rw', writeerror: '405' }, - 'cmi.objectives.n.score._children': { pattern: this.CMI_INDEX, mod: 'r', writeerror: '402' }, - 'cmi.objectives.n.score.raw': { defaultvalue: '', pattern: this.CMI_INDEX, format: this.CMI_DECIMAL, - range: this.SCORE_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.objectives.n.score.min': { defaultvalue: '', pattern: this.CMI_INDEX, format: this.CMI_DECIMAL, - range: this.SCORE_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.objectives.n.score.max': { defaultvalue: '', pattern: this.CMI_INDEX, format: this.CMI_DECIMAL, - range: this.SCORE_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.objectives.n.status': { pattern: this.CMI_INDEX, format: this.CMI_STATUS_2, mod: 'rw', writeerror: '405' }, - 'cmi.student_data._children': { defaultvalue: this.STUDENT_DATA_CHILDREN, mod: 'r', writeerror: '402' }, - 'cmi.student_data.mastery_score': { defaultvalue: this.def[scoId]['cmi.student_data.mastery_score'], mod: 'r', - writeerror: '403' }, - 'cmi.student_data.max_time_allowed': { defaultvalue: this.def[scoId]['cmi.student_data.max_time_allowed'], - mod: 'r', writeerror: '403' }, - 'cmi.student_data.time_limit_action': { defaultvalue: this.def[scoId]['cmi.student_data.time_limit_action'], - mod: 'r', writeerror: '403' }, - 'cmi.student_preference._children': { defaultvalue: this.STUDENT_PREFERENCE_CHILDREN, mod: 'r', - writeerror: '402' }, - 'cmi.student_preference.audio': { defaultvalue: this.def[scoId]['cmi.student_preference.audio'], - format: this.CMI_SINTEGER, range: this.AUDIO_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.student_preference.language': { defaultvalue: this.def[scoId]['cmi.student_preference.language'], - format: this.CMI_STRING_256, mod: 'rw', writeerror: '405' }, - 'cmi.student_preference.speed': { defaultvalue: this.def[scoId]['cmi.student_preference.speed'], - format: this.CMI_SINTEGER, range: this.SPEED_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.student_preference.text': { defaultvalue: this.def[scoId]['cmi.student_preference.text'], - format: this.CMI_SINTEGER, range: this.TEXT_RANGE, mod: 'rw', writeerror: '405' }, - 'cmi.interactions._children': { defaultvalue: this.INTERACTIONS_CHILDREN, mod: 'r', writeerror: '402' }, - 'cmi.interactions._count': { mod: 'r', defaultvalue: '0', writeerror: '402' }, - 'cmi.interactions.n.id': { pattern: this.CMI_INDEX, format: this.CMI_IDENTIFIER, mod: 'w', readerror: '404', - writeerror: '405' }, - 'cmi.interactions.n.objectives._count': { pattern: this.CMI_INDEX, mod: 'r', defaultvalue: '0', writeerror: '402' }, - 'cmi.interactions.n.objectives.n.id': { pattern: this.CMI_INDEX, format: this.CMI_IDENTIFIER, mod: 'w', - readerror: '404', writeerror: '405' }, - 'cmi.interactions.n.time': { pattern: this.CMI_INDEX, format: this.CMI_TIME, mod: 'w', readerror: '404', - writeerror: '405' }, - 'cmi.interactions.n.type': { pattern: this.CMI_INDEX, format: this.CMI_TYPE, mod: 'w', readerror: '404', - writeerror: '405' }, - 'cmi.interactions.n.correct_responses._count': { pattern: this.CMI_INDEX, mod: 'r', defaultvalue: '0', - writeerror: '402' }, - 'cmi.interactions.n.correct_responses.n.pattern': { pattern: this.CMI_INDEX, format: this.CMI_FEEDBACK, - mod: 'w', readerror: '404', writeerror: '405' }, - 'cmi.interactions.n.weighting': { pattern: this.CMI_INDEX, format: this.CMI_DECIMAL, - range: this.WEIGHTING_RANGE, mod: 'w', readerror: '404', writeerror: '405' }, - 'cmi.interactions.n.student_response': { pattern: this.CMI_INDEX, format: this.CMI_FEEDBACK, mod: 'w', - readerror: '404', writeerror: '405' }, - 'cmi.interactions.n.result': { pattern: this.CMI_INDEX, format: this.CMI_RESULT, mod: 'w', readerror: '404', - writeerror: '405' }, - 'cmi.interactions.n.latency': { pattern: this.CMI_INDEX, format: this.CMI_TIMESPAN, mod: 'w', - readerror: '404', writeerror: '405' }, - 'nav.event': { defaultvalue: '', format: this.NAV_EVENT, mod: 'w', readerror: '404', writeerror: '405' } - }; - - this.currentUserData[scoId] = {}; - - // Load default values. - for (const element in this.dataModel[scoId]) { - if (element.match(/\.n\./) === null) { - if (typeof this.dataModel[scoId][element].defaultvalue != 'undefined') { - this.currentUserData[scoId][element] = this.dataModel[scoId][element].defaultvalue; - } - } - } - - // Load initial user data for current SCO. - for (const element in this.def[scoId]) { - if (element.match(/\.n\./) === null) { - if (typeof this.dataModel[scoId][element].defaultvalue != 'undefined') { - this.currentUserData[scoId][element] = this.dataModel[scoId][element].defaultvalue; - } else if (typeof this.defExtra[scoId][element] != 'undefined') { - // Check in user data values. - this.currentUserData[scoId][element] = this.defExtra[scoId][element]; - } else { - this.currentUserData[scoId][element] = ''; - } - } - } - - // Load interactions and objectives, and init the counters. - const expression = new RegExp(this.CMI_INDEX, 'g'); - - for (const element in this.defExtra[scoId]) { - let counterElement = '', - currentCounterIndex: any = 0, - elementDotFormat, - currentN; - - // This check for an indexed element. cmi.objectives.1.id or cmi.objectives_1.id. - if (element.match(expression)) { - // Normalize to the expected value according the standard. - // Moodle stores this values using _n. instead .n. - elementDotFormat = element.replace(expression, '.$1.'); - this.currentUserData[scoId][elementDotFormat] = this.defExtra[scoId][element]; - - // Get the correct counter and current index. - if (elementDotFormat.indexOf('cmi.evaluation.comments') === 0) { - counterElement = 'cmi.evaluation.comments._count'; - currentCounterIndex = elementDotFormat.match(/.(\d+)./)[1]; - } else if (elementDotFormat.indexOf('cmi.objectives') === 0) { - counterElement = 'cmi.objectives._count'; - currentCounterIndex = elementDotFormat.match(/.(\d+)./)[1]; - } else if (elementDotFormat.indexOf('cmi.interactions') === 0) { - if (elementDotFormat.indexOf('.objectives.') > 0) { - currentN = elementDotFormat.match(/cmi.interactions.(\d+)./)[1]; - currentCounterIndex = elementDotFormat.match(/objectives.(\d+)./)[1]; - counterElement = 'cmi.interactions.' + currentN + '.objectives._count'; - } else if (elementDotFormat.indexOf('.correct_responses.') > 0) { - currentN = elementDotFormat.match(/cmi.interactions.(\d+)./)[1]; - currentCounterIndex = elementDotFormat.match(/correct_responses.(\d+)./)[1]; - counterElement = 'cmi.interactions.' + currentN + '.correct_responses._count'; - } else { - counterElement = 'cmi.interactions._count'; - currentCounterIndex = elementDotFormat.match(/.(\d+)./)[1]; - } - } - - if (counterElement) { - if (typeof this.currentUserData[scoId][counterElement] == 'undefined') { - this.currentUserData[scoId][counterElement] = 0; - } - // Check if we need to sum. - if (parseInt(currentCounterIndex) == parseInt(this.currentUserData[scoId][counterElement])) { - this.currentUserData[scoId][counterElement] = parseInt(this.currentUserData[scoId][counterElement]) + 1; - } - if (parseInt(currentCounterIndex) > parseInt(this.currentUserData[scoId][counterElement])) { - this.currentUserData[scoId][counterElement] = parseInt(currentCounterIndex) - 1; - } - } - - } - } - - // Set default status. - if (this.currentUserData[scoId]['cmi.core.lesson_status'] === '') { - this.currentUserData[scoId]['cmi.core.lesson_status'] = 'not attempted'; - } - - // Define mode and credit. - this.currentUserData[scoId]['cmi.core.credit'] = this.mode == AddonModScormProvider.MODENORMAL ? 'credit' : 'no-credit'; - this.currentUserData[scoId]['cmi.core.lesson_mode'] = this.mode; - } - } - - /** - * Commit the changes. - * - * @param param Param. - * @return "true" if success, "false" otherwise. - */ - LMSCommit(param: string): string { - if (this.timeout) { - clearTimeout(this.timeout); - this.timeout = null; - } - - this.errorCode = '0'; - if (param == '') { - if (this.initialized) { - const result = this.storeData(false); - - // Trigger TOC update. - this.triggerEvent(AddonModScormProvider.UPDATE_TOC_EVENT); - - this.errorCode = result ? '0' : '101'; - - // Conver to string representing a boolean. - return result ? 'true' : 'false'; - } else { - this.errorCode = '301'; - } - } else { - this.errorCode = '201'; - } - - return 'false'; - } - - /** - * Finish the data model. - * - * @param param Param. - * @return "true" if success, "false" otherwise. - */ - LMSFinish(param: string): string { - this.errorCode = '0'; - - if (param == '') { - if (this.initialized) { - this.initialized = false; - - const result = this.storeData(true); - if (this.getEl('nav.event') != '') { - if (this.getEl('nav.event') == 'continue') { - this.triggerEvent(AddonModScormProvider.LAUNCH_NEXT_SCO_EVENT); - } else { - this.triggerEvent(AddonModScormProvider.LAUNCH_PREV_SCO_EVENT); - } - } else { - if (this.scorm.auto == '1') { - this.triggerEvent(AddonModScormProvider.LAUNCH_NEXT_SCO_EVENT); - } - } - - this.errorCode = result ? '0' : '101'; - - // Trigger TOC update. - this.triggerEvent(AddonModScormProvider.UPDATE_TOC_EVENT); - - // Conver to string representing a boolean. - return result ? 'true' : 'false'; - } else { - this.errorCode = '301'; - } - } else { - this.errorCode = '201'; - } - - return 'false'; - } - - /** - * Get diagnostic. - * - * @param param Param. - * @return Result. - */ - LMSGetDiagnostic(param: string): string { - if (param == '') { - param = this.errorCode; - } - - return param; - } - - /** - * Get the error message for a certain code. - * - * @param param Error code. - * @return Error message. - */ - LMSGetErrorString(param: string): string { - if (param != '') { - return this.ERROR_STRINGS[param]; - } else { - return ''; - } - } - - /** - * Get the last error code. - * - * @return Last error code. - */ - LMSGetLastError(): string { - return this.errorCode; - } - - /** - * Get the value of a certain element. - * - * @param element Name of the element to get. - * @return Value. - */ - LMSGetValue(element: string): string { - this.errorCode = '0'; - - if (this.initialized) { - if (element != '') { - const expression = new RegExp(this.CMI_INDEX, 'g'), - elementModel = String(element).replace(expression, '.n.'); - - if (typeof this.dataModel[this.scoId][elementModel] != 'undefined') { - if (this.dataModel[this.scoId][elementModel].mod != 'w') { - this.errorCode = '0'; - - return this.getEl(element); - } else { - this.errorCode = this.dataModel[this.scoId][elementModel].readerror; - } - } else { - const childrenStr = '._children', - countStr = '._count'; - - if (elementModel.substr(elementModel.length - childrenStr.length, elementModel.length) == childrenStr) { - const parentModel = elementModel.substr(0, elementModel.length - childrenStr.length); - - if (typeof this.dataModel[this.scoId][parentModel] != 'undefined') { - this.errorCode = '202'; - } else { - this.errorCode = '201'; - } - } else if (elementModel.substr(elementModel.length - countStr.length, elementModel.length) == countStr) { - const parentModel = elementModel.substr(0, elementModel.length - countStr.length); - - if (typeof this.dataModel[this.scoId][parentModel] != 'undefined') { - this.errorCode = '203'; - } else { - this.errorCode = '201'; - } - } else { - this.errorCode = '201'; - } - } - } else { - this.errorCode = '201'; - } - } else { - this.errorCode = '301'; - } - - return ''; - } - - /** - * Initialize the data model. - * - * @param param Param. - * @return "true" if initialized, "false" otherwise. - */ - LMSInitialize(param: string): string { - this.errorCode = '0'; - - if (param == '') { - if (!this.initialized) { - this.initialized = true; - this.errorCode = '0'; - - return 'true'; - } else { - this.errorCode = '101'; - } - } else { - this.errorCode = '201'; - } - - return 'false'; - } - - /** - * Set the value of a certain element. - * - * @param element Name of the element to set. - * @param value Value to set. - * @return "true" if success, "false" otherwise. - */ - LMSSetValue(element: string, value: any): string { - this.errorCode = '0'; - - if (this.initialized) { - if (element != '') { - let expression = new RegExp(this.CMI_INDEX, 'g'); - const elementModel = String(element).replace(expression, '.n.'); - - if (typeof this.dataModel[this.scoId][elementModel] != 'undefined') { - if (this.dataModel[this.scoId][elementModel].mod != 'r') { - expression = new RegExp(this.dataModel[this.scoId][elementModel].format); - value = value + ''; - - const matches = value.match(expression); - - if (matches != null) { - // Create dynamic data model element. - if (element != elementModel) { - - // Init default counters and values. - if (element.indexOf('cmi.objectives') === 0) { - const currentN = element.match(/cmi.objectives.(\d+)./)[1], - counterElement = 'cmi.objectives.' + currentN + '.score'; - - if (typeof this.currentUserData[this.scoId][counterElement + '._children'] == 'undefined') { - this.setEl(this.currentUserData[this.scoId][counterElement + '._children'], - this.SCORE_CHILDREN); - this.setEl(this.currentUserData[this.scoId][counterElement + '.raw'], ''); - this.setEl(this.currentUserData[this.scoId][counterElement + '.min'], ''); - this.setEl(this.currentUserData[this.scoId][counterElement + '.max'], ''); - } - - } else if (element.indexOf('cmi.interactions') === 0) { - const currentN = element.match(/cmi.interactions.(\d+)./)[1]; - let counterElement = 'cmi.interactions.' + currentN + '.objectives._count'; - - if (typeof this.currentUserData[this.scoId][counterElement] == 'undefined') { - this.setEl(counterElement, 0); - } - - counterElement = 'cmi.interactions.' + currentN + '.correct_responses._count'; - if (typeof this.currentUserData[this.scoId][counterElement] == 'undefined') { - this.setEl(counterElement, 0); - } - } - - const elementIndexes = element.split('.'); - let subElement = 'cmi'; - - for (let i = 1; i < elementIndexes.length - 1; i++) { - const elementIndex = elementIndexes[i]; - - if (elementIndexes[i + 1].match(/^\d+$/)) { - const counterElement = subElement + '.' + elementIndex + '._count'; - - if (typeof this.currentUserData[this.scoId][counterElement] == 'undefined') { - this.setEl(counterElement, 0); - } - - if (elementIndexes[i + 1] == this.getEl(counterElement)) { - const count = this.getEl(counterElement); - this.setEl(counterElement, parseInt(count, 10) + 1); - } - - if (elementIndexes[i + 1] > this.getEl(counterElement)) { - this.errorCode = '201'; - } - - subElement = subElement.concat('.' + elementIndex + '.' + elementIndexes[i + 1]); - i++; - } else { - subElement = subElement.concat('.' + elementIndex); - } - } - - element = subElement.concat('.' + elementIndexes[elementIndexes.length - 1]); - } - - // Store data. - if (this.errorCode == '0') { - if (this.scorm.autocommit && !(this.timeout)) { - this.timeout = setTimeout(this.LMSCommit.bind(this), 60000, ['']); - } - - if (typeof this.dataModel[this.scoId][elementModel].range != 'undefined') { - const range = this.dataModel[this.scoId][elementModel].range, - ranges = range.split('#'); - - value = value * 1.0; - if ((value >= ranges[0]) && (value <= ranges[1])) { - this.setEl(element, value); - this.errorCode = '0'; - - return 'true'; - } else { - this.errorCode = this.dataModel[this.scoId][elementModel].writeerror; - } - } else { - if (element == 'cmi.comments') { - this.setEl('cmi.comments', this.getEl('cmi.comments') + value); - } else { - this.setEl(element, value); - } - this.errorCode = '0'; - - return 'true'; - } - } - } else { - this.errorCode = this.dataModel[this.scoId][elementModel].writeerror; - } - } else { - this.errorCode = this.dataModel[this.scoId][elementModel].writeerror; - } - } else { - this.errorCode = '201'; - } - } else { - this.errorCode = '201'; - } - } else { - this.errorCode = '301'; - } - - return 'false'; - } - - /** - * Set a SCO ID. - * The scoId is like a pointer to be able to retrieve the SCO default values and set the new ones in the overall SCORM - * data structure. - * - * @param scoId The new SCO id. - */ - loadSco(scoId: number): void { - this.scoId = scoId; - } - - /** - * Set the value of the given element in the non-persistent (current) user data. - * - * @param el The element. - * @param value The value. - */ - protected setEl(el: string, value: any): void { - if (typeof this.currentUserData[this.scoId] == 'undefined') { - this.currentUserData[this.scoId] = {}; - } - - this.currentUserData[this.scoId][el] = value; - } - - /** - * Set offline mode to true or false. - * - * @param offline True if offline, false otherwise. - */ - setOffline(offline: boolean): void { - this.offline = offline; - } - - /** - * Persist the current user data (this is usually called by LMSCommit). - * - * @param storeTotalTime If true, we need to calculate the total time too. - * @return True if success, false otherwise. - */ - protected storeData(storeTotalTime?: boolean): boolean { - let tracks; - - if (storeTotalTime) { - if (this.getEl('cmi.core.lesson_status') == 'not attempted') { - this.setEl('cmi.core.lesson_status', 'completed'); - } - - if (this.getEl('cmi.core.lesson_mode') == AddonModScormProvider.MODENORMAL) { - if (this.getEl('cmi.core.credit') == 'credit') { - if (this.getEl('cmi.student_data.mastery_score') !== '' && this.getEl('cmi.core.score.raw') !== '') { - if (parseFloat(this.getEl('cmi.core.score.raw')) >= - parseFloat(this.getEl('cmi.student_data.mastery_score'))) { - this.setEl('cmi.core.lesson_status', 'passed'); - } else { - this.setEl('cmi.core.lesson_status', 'failed'); - } - } - } - } - - if (this.getEl('cmi.core.lesson_mode') == AddonModScormProvider.MODEBROWSE) { - if (this.dataModel[this.scoId]['cmi.core.lesson_status'].defaultvalue == '' && - this.getEl('cmi.core.lesson_status') == 'not attempted') { - this.setEl('cmi.core.lesson_status', 'browsed'); - } - } - - tracks = this.collectData(); - tracks.push(this.totalTime()); - } else { - tracks = this.collectData(); - } - - const success = this.scormProvider.saveTracksSync(this.scoId, this.attempt, tracks, this.scorm, this.offline, - this.currentUserData); - - if (!this.offline && !success) { - // Failure storing data in online. Go offline. - this.offline = true; - - this.triggerEvent(AddonModScormProvider.GO_OFFLINE_EVENT); - - return this.scormProvider.saveTracksSync(this.scoId, this.attempt, tracks, this.scorm, this.offline, - this.currentUserData); - } - - return success; - } - - /** - * Utility function for calculating the total time spent in the SCO. - * - * @return Total time element. - */ - protected totalTime(): any { - const totalTime = this.addTime(this.getEl('cmi.core.total_time'), this.getEl('cmi.core.session_time')); - - return { element: 'cmi.core.total_time', value: totalTime }; - } - - /** - * Convenience function to trigger events. - * - * @param name Name of the event to trigger. - */ - protected triggerEvent(name: string): void { - this.eventsProvider.trigger(name, { - scormId: this.scorm.id, - scoId: this.scoId, - attempt: this.attempt - }, this.siteId); - } -} diff --git a/src/addon/mod/scorm/components/components.module.ts b/src/addon/mod/scorm/components/components.module.ts deleted file mode 100644 index 41b0313f8..000000000 --- a/src/addon/mod/scorm/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModScormIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModScormIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModScormIndexComponent - ], - entryComponents: [ - AddonModScormIndexComponent - ] -}) -export class AddonModScormComponentsModule {} diff --git a/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html b/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html deleted file mode 100644 index b3e5d565e..000000000 --- a/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - - - - - - - - - - - -
- - {{ scorm.warningMessage }} -
- -
- - - -
-

{{ 'addon.mod_scorm.attempts' | translate }}

-
-
- - - -

{{ 'addon.mod_scorm.noattemptsallowed' | translate }}

-

{{ 'core.unlimited' | translate }}

-

{{ scorm.maxattempt }}

-
- -

{{ 'addon.mod_scorm.noattemptsmade' | translate }}

-

{{ scorm.numAttempts }}

-
- -

{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.number}}

-

{{ attempt.grade }}

-

{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}

-
-
- -

{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.number}}

-

{{ attempt.grade }}

-

{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}

-

{{ 'addon.mod_scorm.offlineattemptnote' | translate }}

-

{{ 'addon.mod_scorm.offlineattemptovermax' | translate }}

-
- -

{{ 'addon.mod_scorm.grademethod' | translate }}

-

{{ scorm.gradeMethodReadable }}

-
- -

{{ 'addon.mod_scorm.gradereported' | translate }}

-

{{ scorm.grade }}

-

{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}

-
- -

{{ 'core.lastsync' | translate }}

-

{{ syncTime }}

-
-
-
- - -
- - {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} -
- - - - -

{{ 'addon.mod_scorm.contents' | translate }}

-
- - - {{ 'addon.mod_scorm.organizations' | translate }} - - {{ org.title }} - - - - - - - -

{{ 'addon.mod_scorm.dataattemptshown' | translate:{number: attemptToContinue} }}

-

{{ currentOrganization.title }}

-
-

- - - - ({{ 'addon.mod_scorm.score' | translate }}: {{sco.score_raw}}) -

-
-
-
-
- - - - -

{{ errorMessage | translate }}

-
- - - {{ 'core.openinbrowser' | translate }} - - - -
- - - - -

{{ 'addon.mod_scorm.exceededmaxattempts' | translate }}

-
-
- - - - - - - - {{ 'addon.mod_scorm.newattempt' | translate }} - - - - - -

{{ statusMessage | translate }}

-
- - - - - - - - - - - - -
- - - - -

{{ progressMessage | translate }}

- -
-
-
-
-
diff --git a/src/addon/mod/scorm/components/index/index.scss b/src/addon/mod/scorm/components/index/index.scss deleted file mode 100644 index 763ae6067..000000000 --- a/src/addon/mod/scorm/components/index/index.scss +++ /dev/null @@ -1,13 +0,0 @@ -ion-app.app-root addon-mod-scorm-index, -ion-app.app-root page-addon-mod-scorm-toc { - .addon-mod_scorm-toc { - // Hide all non sco icons using css to maintain padding. - ion-icon { - opacity: 0; - } - - .addon-mod_scorm-type-sco ion-icon { - opacity: 1 - } - } -} diff --git a/src/addon/mod/scorm/components/index/index.ts b/src/addon/mod/scorm/components/index/index.ts deleted file mode 100644 index f6f313360..000000000 --- a/src/addon/mod/scorm/components/index/index.ts +++ /dev/null @@ -1,593 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector } from '@angular/core'; -import { Content, NavController } from 'ionic-angular'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { AddonModScormProvider, AddonModScormAttemptCountResult } from '../../providers/scorm'; -import { AddonModScormHelperProvider } from '../../providers/helper'; -import { AddonModScormOfflineProvider } from '../../providers/scorm-offline'; -import { AddonModScormSyncProvider } from '../../providers/scorm-sync'; -import { AddonModScormPrefetchHandler } from '../../providers/prefetch-handler'; -import { CoreConstants } from '@core/constants'; - -/** - * Component that displays a SCORM entry page. - */ -@Component({ - selector: 'addon-mod-scorm-index', - templateUrl: 'addon-mod-scorm-index.html', -}) -export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityComponent { - component = AddonModScormProvider.COMPONENT; - moduleName = 'scorm'; - - scorm: any; // The SCORM object. - currentOrganization: any = {}; // Selected organization. - startNewAttempt = false; - errorMessage: string; // Error message. - syncTime: string; // Last sync time. - hasOffline: boolean; // Whether the SCORM has offline data. - attemptToContinue: number; // The attempt to continue or review. - statusMessage: string; // Message about the status. - downloading: boolean; // Whether the SCORM is being downloaded. - percentage: string; // Download/unzip percentage. - progressMessage: string; // Message about download/unzip. - organizations: any[]; // List of organizations. - loadingToc: boolean; // Whether the TOC is being loaded. - toc: any[]; // Table of contents (structure). - accessInfo: any; // Access information. - skip: boolean; // Launch immediately. - - protected fetchContentDefaultError = 'addon.mod_scorm.errorgetscorm'; // Default error to show when loading contents. - protected syncEventName = AddonModScormSyncProvider.AUTO_SYNCED; - protected attempts: AddonModScormAttemptCountResult; // Data about online and offline attempts. - protected lastAttempt: number; // Last attempt. - protected lastIsOffline: boolean; // Whether the last attempt is offline. - protected hasPlayed = false; // Whether the user has opened the player page. - protected dataSentObserver; // To detect data sent to server. - protected dataSent = false; // Whether some data was sent to server while playing the SCORM. - - constructor(injector: Injector, protected scormProvider: AddonModScormProvider, @Optional() protected content: Content, - protected scormHelper: AddonModScormHelperProvider, protected scormOffline: AddonModScormOfflineProvider, - protected scormSync: AddonModScormSyncProvider, protected prefetchHandler: AddonModScormPrefetchHandler, - protected navCtrl: NavController, protected prefetchDelegate: CoreCourseModulePrefetchDelegate, - protected utils: CoreUtilsProvider) { - super(injector, content); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.loadContent(false, true).then(() => { - if (!this.scorm) { - return; - } - - if (this.skip) { - this.open(); - } - - this.scormProvider.logView(this.scorm.id, this.scorm.name).then(() => { - this.checkCompletion(); - }).catch((error) => { - // Ignore errors. - }); - }); - } - - /** - * Check the completion. - */ - protected checkCompletion(): void { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - - /** - * Download a SCORM package or restores an ongoing download. - * - * @return Promise resolved when done. - */ - protected downloadScormPackage(): Promise { - this.downloading = true; - - return this.prefetchHandler.download(this.module, this.courseId, undefined, (data) => { - if (!data) { - return; - } - - if (data.downloading) { - // Downloading package. - if (this.scorm.packagesize && data.progress) { - this.percentage = (Number(data.progress.loaded / this.scorm.packagesize) * 100).toFixed(1); - } - } else if (data.message) { - // Show a message. - this.progressMessage = data.message; - this.percentage = undefined; - } else if (data.progress && data.progress.loaded && data.progress.total) { - // Unzipping package. - this.percentage = (Number(data.progress.loaded / data.progress.total) * 100).toFixed(1); - } else { - this.percentage = undefined; - } - - }).finally(() => { - this.progressMessage = undefined; - this.percentage = undefined; - this.downloading = false; - }); - } - - /** - * Get the SCORM data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - - // Get the SCORM instance. - return this.scormProvider.getScorm(this.courseId, this.module.id, {moduleUrl: this.module.url}).then((scormData) => { - this.scorm = scormData; - - this.dataRetrieved.emit(this.scorm); - this.description = this.scorm.intro || this.description; - - const result = this.scormProvider.isScormUnsupported(this.scorm); - if (result) { - this.errorMessage = result; - } else { - this.errorMessage = ''; - } - - if (this.scorm.warningMessage) { - return; // SCORM is closed or not open yet, we can't get more data. - } - - let promise; - if (sync) { - // Try to synchronize the assign. - promise = this.syncActivity(showErrors).catch(() => { - // Ignore errors. - }); - } else { - promise = Promise.resolve(); - } - - return promise.catch(() => { - // Ignore errors, keep getting data even if sync fails. - }).then(() => { - - // No need to return this promise, it should be faster than the rest. - this.scormSync.getReadableSyncTime(this.scorm.id).then((syncTime) => { - this.syncTime = syncTime; - }); - - const promises = []; - - // Get access information. - promises.push(this.scormProvider.getAccessInformation(this.scorm.id, {cmId: this.module.id}).then((accessInfo) => { - this.accessInfo = accessInfo; - })); - - // Get the number of attempts. - promises.push(this.scormProvider.getAttemptCount(this.scorm.id, {cmId: this.module.id}).then((attemptsData) => { - this.attempts = attemptsData; - this.hasOffline = !!this.attempts.offline.length; - - // Determine the attempt that will be continued or reviewed. - return this.scormHelper.determineAttemptToContinue(this.scorm, this.attempts); - }).then((attempt) => { - this.lastAttempt = attempt.number; - this.lastIsOffline = attempt.offline; - - if (this.lastAttempt != this.attempts.lastAttempt.number) { - this.attemptToContinue = this.lastAttempt; - } else { - this.attemptToContinue = undefined; - } - - // Check if the last attempt is incomplete. - return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.lastAttempt, { - offline: this.lastIsOffline, - cmId: this.module.id, - }); - }).then((incomplete) => { - const promises = []; - - this.scorm.incomplete = incomplete; - this.scorm.numAttempts = this.attempts.total; - this.scorm.gradeMethodReadable = this.scormProvider.getScormGradeMethod(this.scorm); - this.scorm.attemptsLeft = this.scormProvider.countAttemptsLeft(this.scorm, this.attempts.lastAttempt.number); - - if (this.scorm.forcenewattempt == AddonModScormProvider.SCORM_FORCEATTEMPT_ALWAYS || - (this.scorm.forcenewattempt && !this.scorm.incomplete)) { - this.startNewAttempt = true; - } - - promises.push(this.getReportedGrades()); - - promises.push(this.fetchStructure()); - - if (!this.scorm.packagesize && this.errorMessage === '') { - // SCORM is supported but we don't have package size. Try to calculate it. - promises.push(this.scormProvider.calculateScormSize(this.scorm).then((size) => { - this.scorm.packagesize = size; - })); - } - - // Handle status. - promises.push(this.setStatusListener()); - - return Promise.all(promises); - })); - - return Promise.all(promises).then(() => { - // Check whether to launch the SCORM immediately. - if (typeof this.skip == 'undefined') { - this.skip = !this.hasOffline && !this.errorMessage && - (!this.scorm.lastattemptlock || this.scorm.attemptsLeft > 0) && - this.accessInfo.canskipview && !this.accessInfo.canviewreport && - this.scorm.skipview >= AddonModScormProvider.SKIPVIEW_FIRST && - (this.scorm.skipview == AddonModScormProvider.SKIPVIEW_ALWAYS || this.lastAttempt == 0); - } - }); - }); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Fetch the structure of the SCORM (TOC). - * - * @return Promise resolved when done. - */ - protected fetchStructure(): Promise { - return this.scormProvider.getOrganizations(this.scorm.id, {cmId: this.module.id}).then((organizations) => { - this.organizations = organizations; - - if (!this.currentOrganization.identifier) { - // Load first organization (if any). - if (organizations.length) { - this.currentOrganization.identifier = organizations[0].identifier; - } else { - this.currentOrganization.identifier = ''; - } - } - - return this.loadOrganizationToc(this.currentOrganization.identifier); - }); - } - - /** - * Get the grade of an attempt and add it to the scorm attempts list. - * - * @param attempt The attempt number. - * @param offline Whether it's an offline attempt. - * @param attempts Object where to add the attempt. - * @return Promise resolved when done. - */ - protected getAttemptGrade(attempt: number, offline: boolean, attempts: any): Promise { - return this.scormProvider.getAttemptGrade(this.scorm, attempt, offline).then((grade) => { - attempts[attempt] = { - number: attempt, - grade: grade - }; - }); - } - - /** - * Get the grades of each attempt and the grade of the SCORM. - * - * @return Promise resolved when done. - */ - protected getReportedGrades(): Promise { - const promises = [], - onlineAttempts = {}, - offlineAttempts = {}; - - // Calculate the grade for each attempt. - this.attempts.online.forEach((attempt) => { - // Check that attempt isn't in offline to prevent showing the same attempt twice. Offline should be more recent. - if (this.attempts.offline.indexOf(attempt) == -1) { - promises.push(this.getAttemptGrade(attempt, false, onlineAttempts)); - } - }); - - this.attempts.offline.forEach((attempt) => { - promises.push(this.getAttemptGrade(attempt, true, offlineAttempts)); - }); - - return Promise.all(promises).then(() => { - - // Calculate the grade of the whole SCORM. We only use online attempts to calculate this data. - this.scorm.grade = this.scormProvider.calculateScormGrade(this.scorm, onlineAttempts); - - // Add the attempts to the SCORM in array format in ASC order, and format the grades. - this.scorm.onlineAttempts = this.utils.objectToArray(onlineAttempts); - this.scorm.offlineAttempts = this.utils.objectToArray(offlineAttempts); - this.scorm.onlineAttempts.sort((a, b) => { - return a.number - b.number; - }); - this.scorm.offlineAttempts.sort((a, b) => { - return a.number - b.number; - }); - - // Now format the grades. - this.scorm.onlineAttempts.forEach((attempt) => { - attempt.grade = this.scormProvider.formatGrade(this.scorm, attempt.grade); - }); - this.scorm.offlineAttempts.forEach((attempt) => { - attempt.grade = this.scormProvider.formatGrade(this.scorm, attempt.grade); - }); - - this.scorm.grade = this.scormProvider.formatGrade(this.scorm, this.scorm.grade); - }); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return If suceed or not. - */ - protected hasSyncSucceed(result: any): boolean { - if (result.updated || this.dataSent) { - // Check completion status if something was sent. - this.checkCompletion(); - } - - this.dataSent = false; - - return true; - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - super.ionViewDidEnter(); - - if (this.hasPlayed) { - this.hasPlayed = false; - this.startNewAttempt = false; // Uncheck new attempt. - - // Add a delay to make sure the player has started the last writing calls so we can detect conflicts. - setTimeout(() => { - this.dataSentObserver && this.dataSentObserver.off(); // Stop listening for changes. - this.dataSentObserver = undefined; - - // Refresh data. - this.showLoadingAndRefresh(true, false); - }, 500); - } - } - - /** - * User left the page that contains the component. - */ - ionViewDidLeave(): void { - super.ionViewDidLeave(); - - // Display the full page when returning to the page. - this.skip = false; - - if (this.navCtrl.getActive().component.name == 'AddonModScormPlayerPage') { - this.hasPlayed = true; - - // Detect if anything was sent to server. - this.dataSentObserver && this.dataSentObserver.off(); - - this.dataSentObserver = this.eventsProvider.on(AddonModScormProvider.DATA_SENT_EVENT, (data) => { - if (data.scormId === this.scorm.id) { - this.dataSent = true; - } - }, this.siteId); - } - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.scormProvider.invalidateScormData(this.courseId)); - - if (this.scorm) { - promises.push(this.scormProvider.invalidateAllScormData(this.scorm.id)); - } - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (syncEventData.updated && this.scorm && syncEventData.scormId == this.scorm.id) { - // Check completion status. - this.checkCompletion(); - - return true; - } - - return false; - } - - /** - * Load a organization's TOC. - */ - loadOrganization(): void { - this.loadOrganizationToc(this.currentOrganization.identifier).catch((error) => { - this.domUtils.showErrorModalDefault(error, this.fetchContentDefaultError, true); - }); - } - - /** - * Load the TOC of a certain organization. - * - * @param organizationId The organization id. - * @return Promise resolved when done. - */ - protected loadOrganizationToc(organizationId: string): Promise { - if (!this.scorm.displaycoursestructure) { - // TOC is not displayed, no need to load it. - return Promise.resolve(); - } - - this.loadingToc = true; - - return this.scormProvider.getOrganizationToc(this.scorm.id, this.lastAttempt, { - organization: organizationId, - offline: this.lastIsOffline, - cmId: this.module.id, - }).then((toc) => { - - this.toc = this.scormProvider.formatTocToArray(toc); - - // Get images for each SCO. - this.toc.forEach((sco) => { - sco.image = this.scormProvider.getScoStatusIcon(sco, this.scorm.incomplete); - }); - - // Search organization title. - this.organizations.forEach((org) => { - if (org.identifier == organizationId) { - this.currentOrganization.title = org.title; - } - }); - }).finally(() => { - this.loadingToc = false; - }); - } - - /** - * Open a SCORM. It will download the SCORM package if it's not downloaded or it has changed. - * - * @param event Event. - * @param scoId SCO that needs to be loaded when the SCORM is opened. If not defined, load first SCO. - */ - open(event?: Event, preview: boolean = false, scoId?: number): void { - if (event) { - event.preventDefault(); - event.stopPropagation(); - } - - if (this.downloading) { - // Scope is being downloaded, abort. - return; - } - - const isOutdated = this.currentStatus == CoreConstants.OUTDATED; - - if (isOutdated || this.currentStatus == CoreConstants.NOT_DOWNLOADED) { - // SCORM needs to be downloaded. - this.scormHelper.confirmDownload(this.scorm, isOutdated).then(() => { - // Invalidate WS data if SCORM is outdated. - const promise = isOutdated ? this.scormProvider.invalidateAllScormData(this.scorm.id) : Promise.resolve(); - - promise.finally(() => { - this.downloadScormPackage().then(() => { - // Success downloading, open SCORM if user hasn't left the view. - if (!this.isDestroyed) { - this.openScorm(scoId, preview); - } - }).catch((error) => { - if (!this.isDestroyed) { - this.domUtils.showErrorModalDefault(error, this.translate.instant( - 'addon.mod_scorm.errordownloadscorm', {name: this.scorm.name})); - } - }); - }); - }); - } else { - this.openScorm(scoId, preview); - } - } - - /** - * Open a SCORM package. - * - * @param scoId SCO ID. - */ - protected openScorm(scoId: number, preview: boolean = false): void { - this.navCtrl.push('AddonModScormPlayerPage', { - scorm: this.scorm, - mode: preview ? AddonModScormProvider.MODEBROWSE : AddonModScormProvider.MODENORMAL, - newAttempt: !!this.startNewAttempt, - organizationId: this.currentOrganization.identifier, - scoId: scoId - }); - } - - /** - * Displays some data based on the current status. - * - * @param status The current status. - * @param previousStatus The previous status. If not defined, there is no previous status. - */ - protected showStatus(status: string, previousStatus?: string): void { - - if (status == CoreConstants.OUTDATED && this.scorm) { - // Only show the outdated message if the file should be downloaded. - this.scormProvider.shouldDownloadMainFile(this.scorm, true).then((download) => { - this.statusMessage = download ? 'addon.mod_scorm.scormstatusoutdated' : ''; - }); - } else if (status == CoreConstants.NOT_DOWNLOADED) { - this.statusMessage = 'addon.mod_scorm.scormstatusnotdownloaded'; - } else if (status == CoreConstants.DOWNLOADING) { - if (!this.downloading) { - // It's being downloaded right now but the view isn't tracking it. "Restore" the download. - this.downloadScormPackage(); - } - } else { - this.statusMessage = ''; - } - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.scormSync.syncScorm(this.scorm).then((result) => { - if (!result.updated && this.dataSent) { - // The user sent data to server, but not in the sync process. Check if we need to fetch data. - return this.scormSync.prefetchAfterUpdate(this.module, this.courseId).catch(() => { - // Ignore errors. - }).then(() => { - return result; - }); - } - - return result; - }); - } -} diff --git a/src/addon/mod/scorm/lang/en.json b/src/addon/mod/scorm/lang/en.json deleted file mode 100644 index 3e234713d..000000000 --- a/src/addon/mod/scorm/lang/en.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "asset": "Asset", - "assetlaunched": "Asset - Viewed", - "attempts": "Attempts", - "averageattempt": "Average attempts", - "browse": "Preview", - "browsed": "Browsed", - "browsemode": "Preview mode", - "cannotcalculategrade": "Grade couldn't be calculated.", - "completed": "Completed", - "contents": "Contents", - "dataattemptshown": "This data belongs to the attempt number {{number}}.", - "enter": "Enter", - "errorcreateofflineattempt": "An error occurred while creating a new offline attempt. Please try again.", - "errordownloadscorm": "Error downloading SCORM: \"{{name}}\".", - "errorgetscorm": "Error getting SCORM data.", - "errorinvalidversion": "Sorry, the application only supports SCORM 1.2.", - "errornotdownloadable": "The download of SCORM packages is disabled. Please contact your site administrator.", - "errornovalidsco": "This SCORM package doesn't have a visible SCO to load.", - "errorpackagefile": "Sorry, the application only supports ZIP packages.", - "errorsyncscorm": "An error occurred while synchronising. Please try again.", - "exceededmaxattempts": "You have reached the maximum number of attempts.", - "failed": "Failed", - "firstattempt": "First attempt", - "gradeaverage": "Average grade", - "gradeforattempt": "Grade for attempt", - "gradehighest": "Highest grade", - "grademethod": "Grading method", - "gradereported": "Grade reported", - "gradescoes": "Learning objects", - "gradesum": "Sum grade", - "highestattempt": "Highest attempt", - "incomplete": "Incomplete", - "lastattempt": "Last completed attempt", - "modulenameplural": "SCORM packages", - "newattempt": "Start a new attempt", - "noattemptsallowed": "Number of attempts allowed", - "noattemptsmade": "Number of attempts you have made", - "notattempted": "Not attempted", - "offlineattemptnote": "This attempt has data that hasn't been synchronised.", - "offlineattemptovermax": "This attempt cannot be sent because you exceeded the maximum number of attempts.", - "organizations": "Organisations", - "passed": "Passed", - "reviewmode": "Review mode", - "score": "Score", - "scormstatusnotdownloaded": "This SCORM package is not downloaded. It will be automatically downloaded when you open it.", - "scormstatusoutdated": "This SCORM package has been modified since the last download. It will be automatically downloaded when you open it.", - "suspended": "Suspended", - "toc": "TOC", - "warningofflinedatadeleted": "Some offline data from attempt {{number}} has been discarded because it couldn't be counted as a new attempt.", - "warningsynconlineincomplete": "Some attempts couldn't be synchronised with the site because the last online attempt is not yet finished. Please finish the online attempt first." -} \ No newline at end of file diff --git a/src/addon/mod/scorm/pages/index/index.html b/src/addon/mod/scorm/pages/index/index.html deleted file mode 100644 index 9a0b2d855..000000000 --- a/src/addon/mod/scorm/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/scorm/pages/index/index.module.ts b/src/addon/mod/scorm/pages/index/index.module.ts deleted file mode 100644 index 7f2fad09d..000000000 --- a/src/addon/mod/scorm/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModScormComponentsModule } from '../../components/components.module'; -import { AddonModScormIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModScormIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModScormComponentsModule, - IonicPageModule.forChild(AddonModScormIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModScormIndexPageModule {} diff --git a/src/addon/mod/scorm/pages/index/index.ts b/src/addon/mod/scorm/pages/index/index.ts deleted file mode 100644 index 0a840bfd9..000000000 --- a/src/addon/mod/scorm/pages/index/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModScormIndexComponent } from '../../components/index/index'; - -/** - * Page that displays the SCORM entry page. - */ -@IonicPage({ segment: 'addon-mod-scorm-index' }) -@Component({ - selector: 'page-addon-mod-scorm-index', - templateUrl: 'index.html', -}) -export class AddonModScormIndexPage { - @ViewChild(AddonModScormIndexComponent) scormComponent: AddonModScormIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the SCORM instance. - * - * @param scorm SCORM instance. - */ - updateData(scorm: any): void { - this.title = scorm.name || this.title; - } - - /** - * User entered the page. - */ - ionViewDidEnter(): void { - this.scormComponent.ionViewDidEnter(); - } - - /** - * User left the page. - */ - ionViewDidLeave(): void { - this.scormComponent.ionViewDidLeave(); - } -} diff --git a/src/addon/mod/scorm/pages/player/player.html b/src/addon/mod/scorm/pages/player/player.html deleted file mode 100644 index e26b99e1d..000000000 --- a/src/addon/mod/scorm/pages/player/player.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - -

{{ errorMessage | translate }}

-
-
diff --git a/src/addon/mod/scorm/pages/player/player.module.ts b/src/addon/mod/scorm/pages/player/player.module.ts deleted file mode 100644 index ba78bcb70..000000000 --- a/src/addon/mod/scorm/pages/player/player.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModScormPlayerPage } from './player'; - -@NgModule({ - declarations: [ - AddonModScormPlayerPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonModScormPlayerPage), - TranslateModule.forChild() - ], -}) -export class AddonModScormPlayerPageModule {} diff --git a/src/addon/mod/scorm/pages/player/player.ts b/src/addon/mod/scorm/pages/player/player.ts deleted file mode 100644 index ed6e32de7..000000000 --- a/src/addon/mod/scorm/pages/player/player.ts +++ /dev/null @@ -1,515 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { IonicPage, NavParams, ModalController } from 'ionic-angular'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtils } from '@providers/utils/dom'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreIonTabsComponent } from '@components/ion-tabs/ion-tabs'; -import { AddonModScormProvider, AddonModScormAttemptCountResult } from '../../providers/scorm'; -import { AddonModScormHelperProvider } from '../../providers/helper'; -import { AddonModScormSyncProvider } from '../../providers/scorm-sync'; -import { AddonModScormDataModel12 } from '../../classes/data-model-12'; - -/** - * Page that allows playing a SCORM. - */ -@IonicPage({ segment: 'addon-mod-scorm-player' }) -@Component({ - selector: 'page-addon-mod-scorm-player', - templateUrl: 'player.html', -}) -export class AddonModScormPlayerPage implements OnInit, OnDestroy { - - title: string; // Title. - scorm: any; // The SCORM object. - showToc: boolean; // Whether to show the table of contents (TOC). - loadingToc = true; // Whether the TOC is being loaded. - toc: any[]; // List of SCOs. - loaded: boolean; // Whether the data has been loaded. - previousSco: any; // Previous SCO. - nextSco: any; // Next SCO. - src: string; // Iframe src. - errorMessage: string; // Error message. - accessInfo: any; // Access information. - scormWidth: null; // Width applied to scorm iframe. - scormHeight: null; // Height applied to scorm iframe. - - protected siteId: string; - protected mode: string; // Mode to play the SCORM. - protected newAttempt: boolean; // Whether to start a new attempt. - protected organizationId: string; // Organization ID to load. - protected attempt: number; // The attempt number. - protected offline = false; // Whether it's offline mode. - protected userData: any; // User data. - protected initialScoId: number; // Initial SCO ID to load. - protected currentSco: any; // Current SCO. - protected dataModel: AddonModScormDataModel12; // Data Model. - protected attemptToContinue: number; // Attempt to continue (for the popover). - - // Observers. - protected tocObserver: any; - protected launchNextObserver: any; - protected launchPrevObserver: any; - protected goOfflineObserver: any; - - constructor( - navParams: NavParams, - protected modalCtrl: ModalController, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider, - protected syncProvider: CoreSyncProvider, - protected timeUtils: CoreTimeUtilsProvider, - protected scormProvider: AddonModScormProvider, - protected scormHelper: AddonModScormHelperProvider, - protected scormSyncProvider: AddonModScormSyncProvider, - protected tabs: CoreIonTabsComponent - ) { - - this.scorm = navParams.get('scorm') || {}; - this.mode = navParams.get('mode') || AddonModScormProvider.MODENORMAL; - this.newAttempt = !!navParams.get('newAttempt'); - this.organizationId = navParams.get('organizationId'); - this.initialScoId = navParams.get('scoId'); - this.siteId = this.sitesProvider.getCurrentSiteId(); - - // We use SCORM name at start, later we'll use the SCO title. - this.title = this.scorm.name; - - // Block the SCORM so it cannot be synchronized. - this.syncProvider.blockOperation(AddonModScormProvider.COMPONENT, this.scorm.id, 'player'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - - this.showToc = this.scormProvider.displayTocInPlayer(this.scorm); - - if (this.scorm.popup) { - this.tabs.changeVisibility(false); - - // If we receive a value > 100 we assume it's a fixed pixel size. - if (this.scorm.width > 100) { - this.scormWidth = this.scorm.width; - - // Only get fixed size on height if width is also fixed. - if (this.scorm.height > 100) { - this.scormHeight = this.scorm.height; - } - } - } - - // Fetch the SCORM data. - this.fetchData().then(() => { - if (this.currentSco) { - // Set start time if it's a new attempt. - const promise = this.newAttempt ? this.setStartTime(this.currentSco.id) : Promise.resolve(); - - return promise.catch((error) => { - CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_scorm.errorgetscorm', true); - }).finally(() => { - // Load SCO. - this.loadSco(this.currentSco); - }); - } - }).finally(() => { - this.loaded = true; - }); - - // Listen for events to update the TOC, navigate through SCOs and go offline. - this.tocObserver = this.eventsProvider.on(AddonModScormProvider.UPDATE_TOC_EVENT, (data) => { - if (data.scormId === this.scorm.id) { - if (this.offline) { - // Wait a bit to make sure data is stored. - setTimeout(this.refreshToc.bind(this), 100); - } else { - this.refreshToc(); - } - } - }, this.siteId); - - this.launchNextObserver = this.eventsProvider.on(AddonModScormProvider.LAUNCH_NEXT_SCO_EVENT, (data) => { - if (data.scormId === this.scorm.id && this.nextSco) { - this.loadSco(this.nextSco); - } - }, this.siteId); - - this.launchPrevObserver = this.eventsProvider.on(AddonModScormProvider.LAUNCH_PREV_SCO_EVENT, (data) => { - if (data.scormId === this.scorm.id && this.previousSco) { - this.loadSco(this.previousSco); - } - }, this.siteId); - - this.goOfflineObserver = this.eventsProvider.on(AddonModScormProvider.GO_OFFLINE_EVENT, (data) => { - if (data.scormId === this.scorm.id && !this.offline) { - this.offline = true; - - // Wait a bit to prevent collisions between this store and SCORM API's store. - setTimeout(() => { - this.scormHelper.convertAttemptToOffline(this.scorm, this.attempt).catch((error) => { - CoreDomUtils.instance.showErrorModalDefault(error, 'core.error', true); - }).then(() => { - this.refreshToc(); - }); - }, 200); - } - }, this.siteId); - } - - /** - * Calculate the next and previous SCO. - * - * @param scoId Current SCO ID. - */ - protected calculateNextAndPreviousSco(scoId: number): void { - this.previousSco = this.scormHelper.getPreviousScoFromToc(this.toc, scoId); - this.nextSco = this.scormHelper.getNextScoFromToc(this.toc, scoId); - } - - /** - * Determine the attempt to use, the mode (normal/preview) and if it's offline or online. - * - * @param attemptsData Attempts count. - * @return Promise resolved when done. - */ - protected determineAttemptAndMode(attemptsData: AddonModScormAttemptCountResult): Promise { - let result; - - return this.scormHelper.determineAttemptToContinue(this.scorm, attemptsData).then((data) => { - this.attempt = data.number; - this.offline = data.offline; - - if (this.attempt != attemptsData.lastAttempt.number) { - this.attemptToContinue = this.attempt; - } - - // Check if current attempt is incomplete. - if (this.attempt > 0) { - return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.attempt, { - offline: this.offline, - cmId: this.scorm.coursemodule, - }); - } else { - // User doesn't have attempts. Last attempt is not incomplete (since he doesn't have any). - return false; - } - }).then((incomplete) => { - // Determine mode and attempt to use. - result = this.scormProvider.determineAttemptAndMode(this.scorm, this.mode, this.attempt, this.newAttempt, incomplete); - - if (result.attempt > this.attempt) { - // We're creating a new attempt. - if (this.offline) { - // Last attempt was offline, so we'll create a new offline attempt. - return this.scormHelper.createOfflineAttempt(this.scorm, result.attempt, attemptsData.online.length); - } else { - // Last attempt was online, verify that we can create a new online attempt. We ignore cache. - return this.scormProvider.getScormUserData(this.scorm.id, result.attempt, { - cmId: this.scorm.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - }).catch(() => { - // Cannot communicate with the server, create an offline attempt. - this.offline = true; - - return this.scormHelper.createOfflineAttempt(this.scorm, result.attempt, attemptsData.online.length); - }); - } - } - }).then(() => { - this.mode = result.mode; - this.newAttempt = result.newAttempt; - this.attempt = result.attempt; - }); - } - - /** - * Fetch data needed to play the SCORM. - * - * @return Promise resolved when done. - */ - protected fetchData(): Promise { - // Wait for any ongoing sync to finish. We won't sync a SCORM while it's being played. - return this.scormSyncProvider.waitForSync(this.scorm.id).then(() => { - // Get attempts data. - return this.scormProvider.getAttemptCount(this.scorm.id, {cmId: this.scorm.coursemodule}).then((attemptsData) => { - return this.determineAttemptAndMode(attemptsData).then(() => { - // Fetch TOC and get user data. - const promises = []; - - promises.push(this.fetchToc()); - promises.push(this.scormProvider.getScormUserData(this.scorm.id, this.attempt, { - cmId: this.scorm.coursemodule, - offline: this.offline, - }).then((data) => { - this.userData = data; - })); - // Get access information. - promises.push(this.scormProvider.getAccessInformation(this.scorm.id, { - cmId: this.scorm.coursemodule, - }).then((accessInfo) => { - this.accessInfo = accessInfo; - })); - - return Promise.all(promises); - }); - }).catch((error) => { - CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_scorm.errorgetscorm', true); - }); - }); - } - - /** - * Fetch the TOC. - * - * @return Promise resolved when done. - */ - protected fetchToc(): Promise { - this.loadingToc = true; - - // We need to check incomplete again: attempt number or status might have changed. - return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.attempt, { - offline: this.offline, - cmId: this.scorm.coursemodule, - }).then((incomplete) => { - this.scorm.incomplete = incomplete; - - // Get TOC. - return this.scormProvider.getOrganizationToc(this.scorm.id, this.attempt, { - organization: this.organizationId, - offline: this.offline, - cmId: this.scorm.coursemodule, - }); - }).then((toc) => { - this.toc = this.scormProvider.formatTocToArray(toc); - - // Get images for each SCO. - this.toc.forEach((sco) => { - sco.image = this.scormProvider.getScoStatusIcon(sco, this.scorm.incomplete); - }); - - if (!this.currentSco) { - if (this.newAttempt) { - // Creating a new attempt, use the first SCO defined by the SCORM. - this.initialScoId = this.scorm.launch; - } - - // Determine current SCO if we received an ID. - if (this.initialScoId > 0) { - // SCO set by parameter, get it from TOC. - this.currentSco = this.scormHelper.getScoFromToc(this.toc, this.initialScoId); - } - - if (!this.currentSco) { - // No SCO defined. Get the first valid one. - return this.scormHelper.getFirstSco(this.scorm.id, this.attempt, { - toc: this.toc, - organization: this.organizationId, - mode: this.mode, - offline: this.offline, - cmId: this.scorm.coursemodule, - }).then((sco) => { - - if (sco) { - this.currentSco = sco; - } else { - // We couldn't find a SCO to load: they're all inactive or without launch URL. - this.errorMessage = 'addon.mod_scorm.errornovalidsco'; - } - }); - } - } - }).finally(() => { - this.loadingToc = false; - }); - } - - /** - * Page will leave. - */ - ionViewWillUnload(): void { - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'scorm' }); - - // Empty src when leaving the state so unload event is triggered in the iframe. - this.src = ''; - } - - /** - * Load a SCO. - * - * @param sco The SCO to load. - */ - protected loadSco(sco: any): void { - if (!this.dataModel) { - // Create the model. - this.dataModel = new AddonModScormDataModel12(this.eventsProvider, this.scormProvider, this.siteId, this.scorm, sco.id, - this.attempt, this.userData, this.mode, this.offline); - - // Add the model to the window so the SCORM can access it. - ( window).API = this.dataModel; - } else { - // Load the SCO in the existing model. - this.dataModel.loadSco(sco.id); - } - - this.currentSco = sco; - this.title = sco.title || this.scorm.name; // Try to use SCO title. - - this.calculateNextAndPreviousSco(sco.id); - - // Load the SCO source. - this.scormProvider.getScoSrc(this.scorm, sco).then((src) => { - if (src == this.src) { - // Re-loading same page. Set it to empty and then re-set the src in the next digest so it detects it has changed. - this.src = ''; - - setTimeout(() => { - this.src = src; - }); - } else { - this.src = src; - } - }); - - if (sco.scormtype == 'asset') { - // Mark the asset as completed. - const tracks = [{ - element: 'cmi.core.lesson_status', - value: 'completed' - }]; - - this.scormProvider.saveTracks(sco.id, this.attempt, tracks, this.scorm, this.offline).catch(() => { - // Error saving data. We'll go offline if we're online and the asset is not marked as completed already. - if (!this.offline) { - return this.scormProvider.getScormUserData(this.scorm.id, this.attempt, { - cmId: this.scorm.coursemodule, - }).then((data) => { - if (!data[sco.id] || data[sco.id].userdata['cmi.core.lesson_status'] != 'completed') { - // Go offline. - return this.scormHelper.convertAttemptToOffline(this.scorm, this.attempt).then(() => { - this.offline = true; - this.dataModel.setOffline(true); - - return this.scormProvider.saveTracks(sco.id, this.attempt, tracks, this.scorm, true); - }).catch((error) => { - CoreDomUtils.instance.showErrorModalDefault(error, 'core.error', true); - }); - } - }); - } - }).then(() => { - // Refresh TOC, some prerequisites might have changed. - this.refreshToc(); - }); - } - - // Trigger SCO launch event. - this.scormProvider.logLaunchSco(this.scorm.id, sco.id, this.scorm.name).catch(() => { - // Ignore errors. - }); - } - - /** - * Show the TOC. - * - * @param event Event. - */ - openToc(event: MouseEvent): void { - const modal = this.modalCtrl.create('AddonModScormTocPage', { - toc: this.toc, - attemptToContinue: this.attemptToContinue, - mode: this.mode, - selected: this.currentSco && this.currentSco.id, - moduleId: this.scorm.coursemodule, - courseId: this.scorm.course, - accessInfo: this.accessInfo - }, { cssClass: 'core-modal-lateral', - showBackdrop: true, - enableBackdropDismiss: true, - enterAnimation: 'core-modal-lateral-transition', - leaveAnimation: 'core-modal-lateral-transition' }); - - // If the modal sends back a SCO, load it. - modal.onDidDismiss((sco) => { - if (sco) { - this.loadSco(sco); - } - }); - - modal.present({ - ev: event - }); - } - - /** - * Refresh the TOC. - * - * @return Promise resolved when done. - */ - protected refreshToc(): Promise { - return this.scormProvider.invalidateAllScormData(this.scorm.id).catch(() => { - // Ignore errors. - }).then(() => { - return this.fetchToc(); - }).catch((error) => { - CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_scorm.errorgetscorm', true); - }); - } - - /** - * Set SCORM start time. - * - * @param scoId SCO ID. - * @return Promise resolved when done. - */ - protected setStartTime(scoId: number): Promise { - const tracks = [{ - element: 'x.start.time', - value: this.timeUtils.timestamp() - }]; - - return this.scormProvider.saveTracks(scoId, this.attempt, tracks, this.scorm, this.offline).then(() => { - if (!this.offline) { - // New online attempt created, update cached data about online attempts. - this.scormProvider.getAttemptCount(this.scorm.id, { - cmId: this.scorm.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - }).catch(() => { - // Ignore errors. - }); - } - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - // Stop listening for events. - this.tocObserver && this.tocObserver.off(); - this.launchNextObserver && this.launchNextObserver.off(); - this.launchPrevObserver && this.launchPrevObserver.off(); - setTimeout(() => { - this.goOfflineObserver && this.goOfflineObserver.off(); - }, 500); - - // Unblock the SCORM so it can be synced. - this.syncProvider.unblockOperation(AddonModScormProvider.COMPONENT, this.scorm.id, 'player'); - this.tabs.changeVisibility(true); - } -} diff --git a/src/addon/mod/scorm/pages/toc/toc.html b/src/addon/mod/scorm/pages/toc/toc.html deleted file mode 100644 index 0ce9163f0..000000000 --- a/src/addon/mod/scorm/pages/toc/toc.html +++ /dev/null @@ -1,34 +0,0 @@ - - - {{ 'addon.mod_scorm.toc' | translate }} - - - - - - - - diff --git a/src/addon/mod/scorm/pages/toc/toc.module.ts b/src/addon/mod/scorm/pages/toc/toc.module.ts deleted file mode 100644 index fec325aeb..000000000 --- a/src/addon/mod/scorm/pages/toc/toc.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { AddonModScormTocPage } from './toc'; - -@NgModule({ - declarations: [ - AddonModScormTocPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - IonicPageModule.forChild(AddonModScormTocPage), - TranslateModule.forChild() - ], -}) -export class AddonModScormTocPageModule {} diff --git a/src/addon/mod/scorm/pages/toc/toc.ts b/src/addon/mod/scorm/pages/toc/toc.ts deleted file mode 100644 index 8e25df969..000000000 --- a/src/addon/mod/scorm/pages/toc/toc.ts +++ /dev/null @@ -1,70 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; -import { AddonModScormProvider } from '../../providers/scorm'; - -/** - * Modal to display the TOC of a SCORM. - */ -@IonicPage({ segment: 'addon-mod-scorm-toc-modal' }) -@Component({ - selector: 'page-addon-mod-scorm-toc', - templateUrl: 'toc.html' -}) -export class AddonModScormTocPage { - toc: any[]; - isBrowse: boolean; - isReview: boolean; - attemptToContinue: number; - selected: number; - moduleId: number; - courseId: number; - accessInfo: any; - - constructor(navParams: NavParams, private viewCtrl: ViewController) { - this.toc = navParams.get('toc') || []; - this.attemptToContinue = navParams.get('attemptToContinue'); - this.moduleId = navParams.get('moduleId'); - this.courseId = navParams.get('courseId'); - this.accessInfo = navParams.get('accessInfo'); - - const mode = navParams.get('mode'); - this.selected = navParams.get('selected'); - - this.isBrowse = mode === AddonModScormProvider.MODEBROWSE; - this.isReview = mode === AddonModScormProvider.MODEREVIEW; - } - - /** - * Function called when a SCO is clicked. - * - * @param sco Clicked SCO. - */ - loadSco(sco: any): void { - if (!sco.prereq || !sco.isvisible || !sco.launch) { - return; - } - - this.viewCtrl.dismiss(sco); - } - - /** - * Close modal. - */ - closeModal(): void { - this.viewCtrl.dismiss(); - } -} diff --git a/src/addon/mod/scorm/providers/grade-link-handler.ts b/src/addon/mod/scorm/providers/grade-link-handler.ts deleted file mode 100644 index 8f99f504a..000000000 --- a/src/addon/mod/scorm/providers/grade-link-handler.ts +++ /dev/null @@ -1,32 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksModuleGradeHandler } from '@core/contentlinks/classes/module-grade-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to SCORM grade. - */ -@Injectable() -export class AddonModScormGradeLinkHandler extends CoreContentLinksModuleGradeHandler { - name = 'AddonModScormGradeLinkHandler'; - canReview = false; - - constructor(courseHelper: CoreCourseHelperProvider, domUtils: CoreDomUtilsProvider, sitesProvider: CoreSitesProvider) { - super(courseHelper, domUtils, sitesProvider, 'AddonModScorm', 'scorm'); - } -} diff --git a/src/addon/mod/scorm/providers/helper.ts b/src/addon/mod/scorm/providers/helper.ts deleted file mode 100644 index c838894f1..000000000 --- a/src/addon/mod/scorm/providers/helper.ts +++ /dev/null @@ -1,348 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModScormProvider, AddonModScormAttemptCountResult } from './scorm'; -import { AddonModScormOfflineProvider } from './scorm-offline'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Helper service that provides some features for SCORM. - */ -@Injectable() -export class AddonModScormHelperProvider { - - // List of elements we want to ignore when copying attempts (they're calculated). - protected elementsToIgnore = ['status', 'score_raw', 'total_time', 'session_time', 'student_id', 'student_name', 'credit', - 'mode', 'entry']; - - constructor(private sitesProvider: CoreSitesProvider, private translate: TranslateService, - private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider, - private scormProvider: AddonModScormProvider, private scormOfflineProvider: AddonModScormOfflineProvider) { } - - /** - * Show a confirm dialog if needed. If SCORM doesn't have size, try to calculate it. - * - * @param scorm SCORM to download. - * @param isOutdated True if package outdated, false if not outdated, undefined to calculate it. - * @return Promise resolved if the user confirms or no confirmation needed. - */ - confirmDownload(scorm: any, isOutdated?: boolean): Promise { - // Check if file should be downloaded. - return this.scormProvider.shouldDownloadMainFile(scorm, isOutdated).then((download) => { - if (download) { - let subPromise; - - if (!scorm.packagesize) { - // We don't have package size, try to calculate it. - subPromise = this.scormProvider.calculateScormSize(scorm).then((size) => { - // Store it so we don't have to calculate it again when using the same object. - scorm.packagesize = size; - - return size; - }); - } else { - subPromise = Promise.resolve(scorm.packagesize); - } - - return subPromise.then((size) => { - return this.domUtils.confirmDownloadSize({size: size, total: true}); - }); - } - }); - } - - /** - * Creates a new offline attempt based on an existing online attempt. - * - * @param scorm SCORM. - * @param attempt Number of the online attempt. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the attempt is created. - */ - convertAttemptToOffline(scorm: any, attempt: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Get data from the online attempt. - return this.scormProvider.getScormUserData(scorm.id, attempt, {cmId: scorm.coursemodule, siteId}).then((onlineData) => { - // The SCORM API might have written some data to the offline attempt already. - // We don't want to override it with cached online data. - return this.scormOfflineProvider.getScormUserData(scorm.id, attempt, undefined, siteId).catch(() => { - // Ignore errors. - }).then((offlineData) => { - const dataToStore = this.utils.clone(onlineData); - - // Filter the data to copy. - for (const scoId in dataToStore) { - const sco = dataToStore[scoId]; - - // Delete calculated data. - this.elementsToIgnore.forEach((el) => { - delete sco.userdata[el]; - }); - - // Don't override offline data. - if (offlineData && offlineData[sco.scoid] && offlineData[sco.scoid].userdata) { - const scoUserData = {}; - - for (const element in sco.userdata) { - if (!offlineData[sco.scoid].userdata[element]) { - // This element is not stored in offline, we can save it. - scoUserData[element] = sco.userdata[element]; - } - } - - sco.userdata = scoUserData; - } - } - - return this.scormOfflineProvider.createNewAttempt(scorm, attempt, dataToStore, onlineData, siteId); - }); - }).catch(() => { - // Shouldn't happen. - return Promise.reject(this.translate.instant('addon.mod_scorm.errorcreateofflineattempt')); - }); - } - - /** - * Creates a new offline attempt. - * - * @param scorm SCORM. - * @param newAttempt Number of the new attempt. - * @param lastOnline Number of the last online attempt. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the attempt is created. - */ - createOfflineAttempt(scorm: any, newAttempt: number, lastOnline: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Try to get data from online attempts. - return this.searchOnlineAttemptUserData(scorm.id, lastOnline, {cmId: scorm.coursemodule, siteId}).then((userData) => { - // We're creating a new attempt, remove all the user data that is not needed for a new attempt. - for (const scoId in userData) { - const sco = userData[scoId], - filtered = {}; - - for (const element in sco.userdata) { - if (element.indexOf('.') == -1 && this.elementsToIgnore.indexOf(element) == -1) { - // The element doesn't use a dot notation, probably SCO data. - filtered[element] = sco.userdata[element]; - } - } - - sco.userdata = filtered; - } - - return this.scormOfflineProvider.createNewAttempt(scorm, newAttempt, userData, undefined, siteId); - }).catch(() => { - return Promise.reject(this.translate.instant('addon.mod_scorm.errorcreateofflineattempt')); - }); - } - - /** - * Determines the attempt to continue/review. It will be: - * - The last incomplete online attempt if it hasn't been continued in offline and all offline attempts are complete. - * - The attempt with highest number without surpassing max attempts otherwise. - * - * @param scorm SCORM object. - * @param attempts Attempts count. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the attempt data. - */ - determineAttemptToContinue(scorm: any, attempts: AddonModScormAttemptCountResult, siteId?: string) - : Promise<{number: number, offline: boolean}> { - - let lastOnline; - - // Get last online attempt. - if (attempts.online.length) { - lastOnline = Math.max.apply(Math, attempts.online); - } - - if (lastOnline) { - // Check if last online incomplete. - const hasOffline = attempts.offline.indexOf(lastOnline) > -1; - - return this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, { - offline: hasOffline, - cmId: scorm.coursemodule, - siteId, - }).then((incomplete) => { - if (incomplete) { - return { - number: lastOnline, - offline: hasOffline - }; - } else { - return this.getLastBeforeMax(scorm, attempts); - } - }); - } else { - return Promise.resolve(this.getLastBeforeMax(scorm, attempts)); - } - } - - /** - * Get the first SCO to load in a SCORM: the first valid and incomplete SCO. - * - * @param scormId Scorm ID. - * @param attempt Attempt number. - * @param options Other options. - * @return Promise resolved with the first SCO. - */ - getFirstSco(scormId: number, attempt: number, options: AddonModScormGetFirstScoOptions = {}): Promise { - - const mode = options.mode || AddonModScormProvider.MODENORMAL; - - let promise; - if (options.toc && options.toc.length) { - promise = Promise.resolve(options.toc); - } else { - // SCORM doesn't have a TOC. Get all the scos. - promise = this.scormProvider.getScosWithData(scormId, attempt, options); - } - - return promise.then((scos) => { - - // Search the first valid SCO. - for (let i = 0; i < scos.length; i++) { - const sco = scos[i]; - - if (sco.isvisible && sco.launch && sco.prereq && - (mode != AddonModScormProvider.MODENORMAL || this.scormProvider.isStatusIncomplete(sco.status))) { - // In browse/review mode return the first visible sco. In normal mode, first incomplete sco. - return sco; - } - } - - // No "valid" SCO, load the first one. In web it loads the first child because the toc contains the organization SCO. - return scos[0]; - }); - } - - /** - * Get the last attempt (number and whether it's offline). - * It'll be the highest number as long as it doesn't surpass the max number of attempts. - * - * @param scorm SCORM object. - * @param attempts Attempts count. - * @return Last attempt data. - */ - protected getLastBeforeMax(scorm: any, attempts: AddonModScormAttemptCountResult): {number: number, offline: boolean} { - if (scorm.maxattempt != 0 && attempts.lastAttempt.number > scorm.maxattempt) { - return { - number: scorm.maxattempt, - offline: attempts.offline.indexOf(scorm.maxattempt) > -1 - }; - } else { - return { - number: attempts.lastAttempt.number, - offline: attempts.lastAttempt.offline - }; - } - } - - /** - * Given a TOC in array format and a scoId, return the next available SCO. - * - * @param toc SCORM's TOC. - * @param scoId SCO ID. - * @return Next SCO. - */ - getNextScoFromToc(toc: any, scoId: number): any { - for (let i = 0; i < toc.length; i++) { - if (toc[i].id == scoId) { - // We found the current SCO. Now let's search the next visible SCO with fulfilled prerequisites. - for (let j = i + 1; j < toc.length; j++) { - if (toc[j].isvisible && toc[j].prereq && toc[j].launch) { - return toc[j]; - } - } - break; - } - } - } - - /** - * Given a TOC in array format and a scoId, return the previous available SCO. - * - * @param toc SCORM's TOC. - * @param scoId SCO ID. - * @return Previous SCO. - */ - getPreviousScoFromToc(toc: any, scoId: number): any { - for (let i = 0; i < toc.length; i++) { - if (toc[i].id == scoId) { - // We found the current SCO. Now let's search the previous visible SCO with fulfilled prerequisites. - for (let j = i - 1; j >= 0; j--) { - if (toc[j].isvisible && toc[j].prereq && toc[j].launch) { - return toc[j]; - } - } - break; - } - } - } - - /** - * Given a TOC in array format and a scoId, return the SCO. - * - * @param toc SCORM's TOC. - * @param scoId SCO ID. - * @return SCO. - */ - getScoFromToc(toc: any[], scoId: number): any { - for (let i = 0; i < toc.length; i++) { - if (toc[i].id == scoId) { - return toc[i]; - } - } - } - - /** - * Searches user data for an online attempt. If the data can't be retrieved, re-try with the previous online attempt. - * - * @param scormId SCORM ID. - * @param attempt Online attempt to get the data. - * @param options Other options. - * @return Promise resolved with user data. - */ - searchOnlineAttemptUserData(scormId: number, attempt: number, options: CoreCourseCommonModWSOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - return this.scormProvider.getScormUserData(scormId, attempt, options).catch(() => { - if (attempt > 0) { - // We couldn't retrieve the data. Try again with the previous online attempt. - return this.searchOnlineAttemptUserData(scormId, attempt - 1, options); - } else { - // No more attempts to try. Reject - return Promise.reject(null); - } - }); - } -} - -/** - * Options to pass to getFirstSco. - */ -export type AddonModScormGetFirstScoOptions = CoreCourseCommonModWSOptions & { - toc?: any[]; // SCORM's TOC. If not provided, it will be calculated. - organization?: string; // Organization to use. - mode?: string; // Mode. - offline?: boolean; // Whether the attempt is offline. -}; diff --git a/src/addon/mod/scorm/providers/index-link-handler.ts b/src/addon/mod/scorm/providers/index-link-handler.ts deleted file mode 100644 index a8182a2ed..000000000 --- a/src/addon/mod/scorm/providers/index-link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to SCORM index. - */ -@Injectable() -export class AddonModScormIndexLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModScormIndexLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModScorm', 'scorm', 'a'); - } -} diff --git a/src/addon/mod/scorm/providers/list-link-handler.ts b/src/addon/mod/scorm/providers/list-link-handler.ts deleted file mode 100644 index c40c733dc..000000000 --- a/src/addon/mod/scorm/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to SCORM list page. - */ -@Injectable() -export class AddonModScormListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModScormListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModScorm', 'scorm'); - } -} diff --git a/src/addon/mod/scorm/providers/module-handler.ts b/src/addon/mod/scorm/providers/module-handler.ts deleted file mode 100644 index b223bd918..000000000 --- a/src/addon/mod/scorm/providers/module-handler.ts +++ /dev/null @@ -1,88 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModScormIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support SCORM modules. - */ -@Injectable() -export class AddonModScormModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModScorm'; - modName = 'scorm'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_scorm-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModScormIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModScormIndexComponent; - } -} diff --git a/src/addon/mod/scorm/providers/pluginfile-handler.ts b/src/addon/mod/scorm/providers/pluginfile-handler.ts deleted file mode 100644 index 6cd13e393..000000000 --- a/src/addon/mod/scorm/providers/pluginfile-handler.ts +++ /dev/null @@ -1,59 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CorePluginFileHandler } from '@providers/plugin-file-delegate'; - -/** - * Handler to treat file URLs in SCORM. - */ -@Injectable() -export class AddonModScormPluginFileHandler implements CorePluginFileHandler { - name = 'AddonModScormPluginFileHandler'; - component = 'mod_scorm'; - - /** - * Return the RegExp to match the revision on pluginfile URLs. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return RegExp to match the revision on pluginfile URLs. - */ - getComponentRevisionRegExp(args: string[]): RegExp { - // Check filearea. - if (args[2] == 'content') { - // Component + Filearea + Revision - return new RegExp('/mod_scorm/content/([0-9]+)/'); - } - } - - /** - * Should return the string to remove the revision on pluginfile url. - * - * @param args Arguments of the pluginfile URL defining component and filearea at least. - * @return String to remove the revision on pluginfile url. - */ - getComponentRevisionReplace(args: string[]): string { - // Component + Filearea + Revision - return '/mod_scorm/content/0/'; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/mod/scorm/providers/prefetch-handler.ts b/src/addon/mod/scorm/providers/prefetch-handler.ts deleted file mode 100644 index dd34b8fe1..000000000 --- a/src/addon/mod/scorm/providers/prefetch-handler.ts +++ /dev/null @@ -1,460 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreFileProvider } from '@providers/file'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { AddonModScormProvider } from './scorm'; -import { AddonModScormSyncProvider } from './scorm-sync'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Progress event used when downloading a SCORM. - */ -export interface AddonModScormProgressEvent { - /** - * Whether the event is due to the download of a chunk of data. - */ - downloading?: boolean; - - /** - * Progress event sent by the download. - */ - progress?: ProgressEvent; - - /** - * A message related to the progress. This is usually used to notify that a certain step of the download has started. - */ - message?: string; -} - -/** - * Handler to prefetch SCORMs. - */ -@Injectable() -export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModScorm'; - modName = 'scorm'; - component = AddonModScormProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^tracks$/; - - protected syncProvider: AddonModScormSyncProvider; // It will be injected later to prevent circular dependencies. - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected fileProvider: CoreFileProvider, - protected textUtils: CoreTextUtilsProvider, - protected scormProvider: AddonModScormProvider, - protected injector: Injector) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Download the module. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param dirPath Path of the directory where to store all the content files. - * @param onProgress Function to call on progress. - * @return Promise resolved when all content is downloaded. - */ - download(module: any, courseId: number, dirPath?: string, onProgress?: (event: AddonModScormProgressEvent) => any) - : Promise { - - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.prefetchPackage(module, courseId, true, this.downloadOrPrefetchScorm.bind(this), siteId, false, onProgress); - } - - /** - * Download or prefetch a SCORM. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @param prefetch True to prefetch, false to download right away. - * @param onProgress Function to call on progress. - * @return Promise resolved with the "extra" data to store: the hash of the file. - */ - protected downloadOrPrefetchScorm(module: any, courseId: number, single: boolean, siteId: string, prefetch: boolean, - onProgress?: (event: AddonModScormProgressEvent) => any): Promise { - - let scorm; - - return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url, siteId}).then((scormData) => { - scorm = scormData; - - const promises = [], - introFiles = this.getIntroFilesFromInstance(module, scorm); - - // Download WS data. - promises.push(this.fetchWSData(scorm, siteId).catch(() => { - // If prefetchData fails we don't want to fail the whole download, so we'll ignore the error for now. - // @todo Implement a warning system so the user knows which SCORMs have failed. - })); - - // Download the package. - promises.push(this.downloadOrPrefetchMainFileIfNeeded(scorm, prefetch, onProgress, siteId)); - - // Download intro files. - promises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, introFiles, prefetch, false, this.component, - module.id).catch(() => { - // Ignore errors. - })); - - return Promise.all(promises); - }).then(() => { - // Success, return the hash. - return scorm.sha1hash; - }); - } - - /** - * Downloads/Prefetches and unzips the SCORM package. - * - * @param scorm SCORM object. - * @param prefetch True if prefetch, false otherwise. - * @param onProgress Function to call on progress. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the file is downloaded and unzipped. - */ - protected downloadOrPrefetchMainFile(scorm: any, prefetch?: boolean, onProgress?: (event: AddonModScormProgressEvent) => any, - siteId?: string): Promise { - - const packageUrl = this.scormProvider.getPackageUrl(scorm); - let dirPath; - - // Get the folder where the unzipped files will be. - return this.scormProvider.getScormFolder(scorm.moduleurl).then((path) => { - dirPath = path; - - // Notify that the download is starting. - onProgress && onProgress({message: 'core.downloading'}); - - // Download the ZIP file to the filepool. - if (prefetch) { - return this.filepoolProvider.addToQueueByUrl(siteId, packageUrl, this.component, scorm.coursemodule, undefined, - undefined, this.downloadProgress.bind(this, true, onProgress)); - } else { - return this.filepoolProvider.downloadUrl(siteId, packageUrl, true, this.component, scorm.coursemodule, - undefined, this.downloadProgress.bind(this, true, onProgress)); - } - }).then(() => { - // Get the ZIP file path. - return this.filepoolProvider.getFilePathByUrl(siteId, packageUrl); - }).then((zipPath) => { - // Notify that the unzip is starting. - onProgress && onProgress({message: 'core.unzipping'}); - - // Unzip and delete the zip when finished. - return this.fileProvider.unzipFile(zipPath, dirPath, this.downloadProgress.bind(this, false, onProgress)).then(() => { - return this.filepoolProvider.removeFileByUrl(siteId, packageUrl).catch(() => { - // Ignore errors. - }); - }); - }); - } - - /** - * Downloads/Prefetches and unzips the SCORM package if it should be downloaded. - * - * @param scorm SCORM object. - * @param prefetch True if prefetch, false otherwise. - * @param onProgress Function to call on progress. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the file is downloaded and unzipped. - */ - protected downloadOrPrefetchMainFileIfNeeded(scorm: any, prefetch?: boolean, - onProgress?: (event: AddonModScormProgressEvent) => any, siteId?: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const result = this.scormProvider.isScormUnsupported(scorm); - - if (result) { - return Promise.reject(this.translate.instant(result)); - } - - // First verify that the file needs to be downloaded. - // It needs to be checked manually because the ZIP file is deleted after unzipped, so the filepool will always download it. - return this.scormProvider.shouldDownloadMainFile(scorm, undefined, siteId).then((download) => { - if (download) { - return this.downloadOrPrefetchMainFile(scorm, prefetch, onProgress, siteId); - } - }); - } - - /** - * Function that converts a regular ProgressEvent into a AddonModScormProgressEvent. - * - * @param downloading True when downloading, false when unzipping. - * @param onProgress Function to call on progress. - * @param progress Event returned by the download function. - */ - protected downloadProgress(downloading: boolean, onProgress?: (event: AddonModScormProgressEvent) => any, - progress?: ProgressEvent): void { - - if (onProgress && progress && progress.loaded) { - onProgress({ - downloading: downloading, - progress: progress - }); - } - } - - /** - * Get WS data for SCORM. - * - * @param scorm SCORM object. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is prefetched. - */ - fetchWSData(scorm: any, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - const modOptions = { - cmId: scorm.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - - // Prefetch number of attempts (including not completed). - promises.push(this.scormProvider.getAttemptCountOnline(scorm.id, modOptions).catch(() => { - // If it fails, assume we have no attempts. - return 0; - }).then((numAttempts) => { - if (numAttempts > 0) { - // Get user data for each attempt. - const dataPromises = []; - - for (let i = 1; i <= numAttempts; i++) { - dataPromises.push(this.scormProvider.getScormUserDataOnline(scorm.id, i, modOptions).catch((err) => { - // Ignore failures of all the attempts that aren't the last one. - if (i == numAttempts) { - return Promise.reject(err); - } - })); - } - - return Promise.all(dataPromises); - } else { - // No attempts. We'll still try to get user data to be able to identify SCOs not visible and so. - return this.scormProvider.getScormUserDataOnline(scorm.id, 0, modOptions); - } - })); - - // Prefetch SCOs. - promises.push(this.scormProvider.getScos(scorm.id, modOptions)); - - // Prefetch access information. - promises.push(this.scormProvider.getAccessInformation(scorm.id, modOptions)); - - return Promise.all(promises); - } - - /** - * Get the download size of a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the size and a boolean indicating if it was able - * to calculate the total size. - */ - getDownloadSize(module: any, courseId: any, single?: boolean): Promise<{ size: number, total: boolean }> { - return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url}).then((scorm) => { - if (this.scormProvider.isScormUnsupported(scorm)) { - return {size: -1, total: false}; - } else if (!scorm.packagesize) { - // We don't have package size, try to calculate it. - return this.scormProvider.calculateScormSize(scorm).then((size) => { - return {size: size, total: true}; - }); - } else { - return {size: scorm.packagesize, total: true}; - } - }); - } - - /** - * Get the downloaded size of a module. If not defined, we'll use getFiles to calculate it (it can be slow). - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Size, or promise resolved with the size. - */ - getDownloadedSize(module: any, courseId: number): number | Promise { - return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url}).then((scorm) => { - // Get the folder where SCORM should be unzipped. - return this.scormProvider.getScormFolder(scorm.moduleurl); - }).then((path) => { - return this.fileProvider.getDirectorySize(path); - }); - } - - /** - * Get list of files. If not defined, we'll assume they're in module.contents. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the list of files. - */ - getFiles(module: any, courseId: number, single?: boolean): Promise { - return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url}).then((scorm) => { - return this.scormProvider.getScormFileList(scorm); - }).catch(() => { - // SCORM not found, return empty list. - return []; - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.scormProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - // Invalidate the calls required to check if a SCORM is downloadable. - return this.scormProvider.invalidateScormData(courseId); - } - - /** - * Check if a module can be downloaded. If the function is not defined, we assume that all modules are downloadable. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Whether the module can be downloaded. The promise should never be rejected. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url}).then((scorm) => { - if (scorm.warningMessage) { - // SCORM closed or not opened yet. - return false; - } - - if (this.scormProvider.isScormUnsupported(scorm)) { - return false; - } - - return true; - }); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @param onProgress Function to call on progress. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string, - onProgress?: (event: AddonModScormProgressEvent) => any): Promise { - - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.prefetchPackage(module, courseId, single, this.downloadOrPrefetchScorm.bind(this), siteId, true, onProgress); - } - - /** - * Remove module downloaded files. If not defined, we'll use getFiles to remove them (slow). - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when done. - */ - removeFiles(module: any, courseId: number): Promise { - const siteId = this.sitesProvider.getCurrentSiteId(); - let scorm; - - return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url, siteId}).then((scormData) => { - scorm = scormData; - - // Get the folder where SCORM should be unzipped. - return this.scormProvider.getScormFolder(scorm.moduleurl); - }).then((path) => { - const promises = []; - - // Remove the unzipped folder. - promises.push(this.fileProvider.removeDir(path).catch((error) => { - if (error && error.code == 1) { - // Not found, ignore error. - } else { - return Promise.reject(error); - } - })); - - // Maybe the ZIP wasn't deleted for some reason. Try to delete it too. - promises.push(this.filepoolProvider.removeFileByUrl(siteId, this.scormProvider.getPackageUrl(scorm)).catch(() => { - // Ignore errors. - })); - - return Promise.all(promises); - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - if (!this.syncProvider) { - this.syncProvider = this.injector.get(AddonModScormSyncProvider); - } - - return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url, siteId}).then((scorm) => { - return this.syncProvider.syncScorm(scorm, siteId); - }); - } -} diff --git a/src/addon/mod/scorm/providers/scorm-offline.ts b/src/addon/mod/scorm/providers/scorm-offline.ts deleted file mode 100644 index 24542b1fe..000000000 --- a/src/addon/mod/scorm/providers/scorm-offline.ts +++ /dev/null @@ -1,937 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModScormProvider } from './scorm'; -import { SQLiteDB } from '@classes/sqlitedb'; - -/** - * Service to handle offline SCORM. - */ -@Injectable() -export class AddonModScormOfflineProvider { - - protected logger; - - // Variables for database. - static ATTEMPTS_TABLE = 'addon_mod_scorm_offline_attempts'; - static TRACKS_TABLE = 'addon_mod_scorm_offline_scos_tracks'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModScormOfflineProvider', - version: 1, - tables: [ - { - name: AddonModScormOfflineProvider.ATTEMPTS_TABLE, - columns: [ - { - name: 'scormid', - type: 'INTEGER', - notNull: true - }, - { - name: 'attempt', // Attempt number. - type: 'INTEGER', - notNull: true - }, - { - name: 'userid', - type: 'INTEGER', - notNull: true - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'timecreated', - type: 'INTEGER' - }, - { - name: 'timemodified', - type: 'INTEGER' - }, - { - name: 'snapshot', - type: 'TEXT' - }, - ], - primaryKeys: ['scormid', 'userid', 'attempt'] - }, - { - name: AddonModScormOfflineProvider.TRACKS_TABLE, - columns: [ - { - name: 'scormid', - type: 'INTEGER', - notNull: true - }, - { - name: 'attempt', // Attempt number. - type: 'INTEGER', - notNull: true - }, - { - name: 'userid', - type: 'INTEGER', - notNull: true - }, - { - name: 'scoid', - type: 'INTEGER', - notNull: true - }, - { - name: 'element', - type: 'TEXT', - notNull: true - }, - { - name: 'value', - type: 'TEXT' - }, - { - name: 'timemodified', - type: 'INTEGER' - }, - { - name: 'synced', - type: 'INTEGER' - }, - ], - primaryKeys: ['scormid', 'userid', 'attempt', 'scoid', 'element'] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private timeUtils: CoreTimeUtilsProvider, - private syncProvider: CoreSyncProvider, private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider, - private userProvider: CoreUserProvider) { - this.logger = logger.getInstance('AddonModScormOfflineProvider'); - - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Changes an attempt number in the data stored in offline. - * This function is used to convert attempts into new attempts, so the stored snapshot will be removed and - * entries will be marked as not synced. - * - * @param scormId SCORM ID. - * @param attempt Number of the attempt to change. - * @param newAttempt New attempt number. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the attempt number changes. - */ - changeAttemptNumber(scormId: number, attempt: number, newAttempt: number, siteId?: string, userId?: number): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - this.logger.debug('Change attempt number from ' + attempt + ' to ' + newAttempt + ' in SCORM ' + scormId); - - // Update the attempt number. - const db = site.getDb(), - currentAttemptConditions = {scormid: scormId, userid: userId, attempt: attempt}, - newAttemptConditions = {scormid: scormId, userid: userId, attempt: newAttempt}; - let newData: any = { - attempt: newAttempt, - timemodified: this.timeUtils.timestamp() - }; - - // Block the SCORM so it can't be synced. - this.syncProvider.blockOperation(AddonModScormProvider.COMPONENT, scormId, 'changeAttemptNumber', site.id); - - return db.updateRecords(AddonModScormOfflineProvider.ATTEMPTS_TABLE, newData, currentAttemptConditions).then(() => { - - // Now update the attempt number of all the tracks and mark them as not synced. - newData = { - attempt: newAttempt, - synced: 0 - }; - - return db.updateRecords(AddonModScormOfflineProvider.TRACKS_TABLE, newData, currentAttemptConditions) - .catch((error) => { - // Failed to update the tracks, restore the old attempt number. - return db.updateRecords(AddonModScormOfflineProvider.ATTEMPTS_TABLE, { attempt: attempt }, - newAttemptConditions).then(() => { - return Promise.reject(error); - }); - }); - }).finally(() => { - // Unblock the SCORM. - this.syncProvider.unblockOperation(AddonModScormProvider.COMPONENT, scormId, 'changeAttemptNumber', site.id); - }); - }); - } - - /** - * Creates a new offline attempt. It can be created from scratch or as a copy of another attempt. - * - * @param scorm SCORM. - * @param attempt Number of the new attempt. - * @param userData User data to store in the attempt. - * @param snapshot Optional. Snapshot to store in the attempt. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the new attempt is created. - */ - createNewAttempt(scorm: any, attempt: number, userData: any, snapshot?: any, siteId?: string, userId?: number): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - this.logger.debug('Creating new offline attempt ' + attempt + ' in SCORM ' + scorm.id); - - // Block the SCORM so it can't be synced. - this.syncProvider.blockOperation(AddonModScormProvider.COMPONENT, scorm.id, 'createNewAttempt', site.id); - - // Create attempt in DB. - const db = site.getDb(), - entry: any = { - scormid: scorm.id, - userid: userId, - attempt: attempt, - courseid: scorm.course, - timecreated: this.timeUtils.timestamp(), - timemodified: this.timeUtils.timestamp(), - snapshot: null - }; - - if (snapshot) { - // Save a snapshot of the data we had when we created the attempt. - // Remove the default data, we don't want to store it. - entry.snapshot = JSON.stringify(this.removeDefaultData(snapshot)); - } - - return db.insertRecord(AddonModScormOfflineProvider.ATTEMPTS_TABLE, entry).then(() => { - // Store all the data in userData. - const promises = []; - - for (const key in userData) { - const sco = userData[key], - tracks = []; - - for (const element in sco.userdata) { - tracks.push({element: element, value: sco.userdata[element]}); - } - - promises.push(this.saveTracks(scorm, sco.scoid, attempt, tracks, userData, site.id, userId)); - } - - return Promise.all(promises); - }).finally(() => { - // Unblock the SCORM. - this.syncProvider.unblockOperation(AddonModScormProvider.COMPONENT, scorm.id, 'createNewAttempt', site.id); - }); - }); - } - - /** - * Delete all the stored data from an attempt. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when all the data has been deleted. - */ - deleteAttempt(scormId: number, attempt: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - this.logger.debug('Delete offline attempt ' + attempt + ' in SCORM ' + scormId); - - const promises = [], - db = site.getDb(); - - // Delete the attempt. - promises.push(db.deleteRecords(AddonModScormOfflineProvider.ATTEMPTS_TABLE, {scormid: scormId, userid: userId, - attempt: attempt})); - - // Delete all the tracks. - promises.push(db.deleteRecords(AddonModScormOfflineProvider.TRACKS_TABLE, {scormid: scormId, userid: userId, - attempt: attempt})); - - return Promise.all(promises); - }); - } - - /** - * Helper function to return a formatted list of interactions for reports. - * This function is based in Moodle's scorm_format_interactions. - * - * @param scoUserData Userdata from a certain SCO. - * @return Formatted userdata. - */ - protected formatInteractions(scoUserData: any): any { - const formatted: any = {}; - - // Defined in order to unify scorm1.2 and scorm2004. - formatted.score_raw = ''; - formatted.status = ''; - formatted.total_time = '00:00:00'; - formatted.session_time = '00:00:00'; - - for (const element in scoUserData) { - let value = scoUserData[element]; - - // Ignore elements that are calculated. - if (element == 'score_raw' || element == 'status' || element == 'total_time' || element == 'session_time') { - return; - } - - formatted[element] = value; - switch (element) { - case 'cmi.core.lesson_status': - case 'cmi.completion_status': - if (value == 'not attempted') { - value = 'notattempted'; - } - formatted.status = value; - break; - - case 'cmi.core.score.raw': - case 'cmi.score.raw': - formatted.score_raw = this.textUtils.roundToDecimals(value, 2); // Round to 2 decimals max. - break; - - case 'cmi.core.session_time': - case 'cmi.session_time': - formatted.session_time = value; - break; - - case 'cmi.core.total_time': - case 'cmi.total_time': - formatted.total_time = value; - break; - default: - // Nothing to do. - } - } - - return formatted; - } - - /** - * Get all the offline attempts in a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the offline attempts are retrieved. - */ - getAllAttempts(siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getAllRecords(AddonModScormOfflineProvider.ATTEMPTS_TABLE); - }).then((attempts) => { - attempts.forEach((attempt) => { - attempt.snapshot = this.textUtils.parseJSON(attempt.snapshot); - }); - - return attempts; - }); - } - - /** - * Get an offline attempt. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved with the attempt. - */ - getAttempt(scormId: number, attempt: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().getRecord(AddonModScormOfflineProvider.ATTEMPTS_TABLE, {scormid: scormId, userid: userId, - attempt: attempt}); - }).then((entry) => { - entry.snapshot = this.textUtils.parseJSON(entry.snapshot); - - return entry; - }); - } - - /** - * Get the creation time of an attempt. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved with time the attempt was created. - */ - getAttemptCreationTime(scormId: number, attempt: number, siteId?: string, userId?: number): Promise { - return this.getAttempt(scormId, attempt, siteId, userId).catch(() => { - return {}; // Attempt not found. - }).then((entry) => { - return entry.timecreated; - }); - } - - /** - * Get the offline attempts done by a user in the given SCORM. - * - * @param scormId SCORM ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the offline attempts are retrieved. - */ - getAttempts(scormId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().getRecords(AddonModScormOfflineProvider.ATTEMPTS_TABLE, {scormid: scormId, userid: userId}); - }).then((attempts) => { - attempts.forEach((attempt) => { - attempt.snapshot = this.textUtils.parseJSON(attempt.snapshot); - }); - - return attempts; - }); - } - - /** - * Get the snapshot of an attempt. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved with the snapshot or undefined if no snapshot. - */ - getAttemptSnapshot(scormId: number, attempt: number, siteId?: string, userId?: number): Promise { - return this.getAttempt(scormId, attempt, siteId, userId).catch(() => { - return {}; // Attempt not found. - }).then((entry) => { - return entry.snapshot; - }); - } - - /** - * Get launch URLs from a list of SCOs, indexing them by SCO ID. - * - * @param scos List of SCOs. Each SCO needs to have 'id' and 'launch' properties. - * @return Launch URLs indexed by SCO ID. - */ - protected getLaunchUrlsFromScos(scos: any[]): {[scoId: number]: string} { - scos = scos || []; - - const response = {}; - - scos.forEach((sco) => { - response[sco.id] = sco.launch; - }); - - return response; - } - - /** - * Get data stored in local DB for a certain scorm and attempt. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param excludeSynced Whether it should only return not synced entries. - * @param excludeNotSynced Whether it should only return synced entries. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved with the entries. - */ - getScormStoredData(scormId: number, attempt: number, excludeSynced?: boolean, excludeNotSynced?: boolean, siteId?: string, - userId?: number): Promise { - - if (excludeSynced && excludeNotSynced) { - return Promise.resolve([]); - } - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const conditions: any = { - scormid: scormId, - userid: userId, - attempt: attempt - }; - - if (excludeSynced) { - conditions.synced = 0; - } else if (excludeNotSynced) { - conditions.synced = 1; - } - - return site.getDb().getRecords(AddonModScormOfflineProvider.TRACKS_TABLE, conditions); - }).then((tracks) => { - tracks.forEach((track) => { - track.value = this.textUtils.parseJSON(track.value); - }); - - return tracks; - }); - } - - /** - * Get the user data for a certain SCORM and offline attempt. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param scos SCOs returned by AddonModScormProvider.getScos. If not supplied, this function will only return the - * SCOs that have something stored and cmi.launch_data will be undefined. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the user data is retrieved. - */ - getScormUserData(scormId: number, attempt: number, scos: any[], siteId?: string, userId?: number): Promise { - scos = scos || []; - - let fullName = '', - userName = ''; - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - // Get username and fullname. - if (userId == site.getUserId()) { - fullName = site.getInfo().fullname; - userName = site.getInfo().username; - } else { - return this.userProvider.getProfile(userId).then((profile) => { - fullName = profile.fullname; - userName = profile.username || ''; - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - - // Get user data. Ordering when using a compound index is complex, so we won't order by scoid. - return this.getScormStoredData(scormId, attempt, false, false, siteId, userId).then((entries) => { - const response = {}, - launchUrls = this.getLaunchUrlsFromScos(scos); - - // Gather user data retrieved from DB, grouping it by scoid. - entries.forEach((entry) => { - const scoId = entry.scoid; - - if (!response[scoId]) { - // Initialize SCO. - response[scoId] = { - scoid: scoId, - userdata: { - userid: userId, - scoid: scoId, - timemodified: 0 - } - }; - } - - response[scoId].userdata[entry.element] = entry.value; - if (entry.timemodified > response[scoId].userdata.timemodified) { - response[scoId].userdata.timemodified = entry.timemodified; - } - }); - - // Format each user data retrieved. - for (const scoId in response) { - const sco = response[scoId]; - sco.userdata = this.formatInteractions(sco.userdata); - } - - // Create empty entries for the SCOs without user data stored. - scos.forEach((sco) => { - if (!response[sco.id]) { - response[sco.id] = { - scoid: sco.id, - userdata: { - status: '', - score_raw: '' - } - }; - } - }); - - // Calculate defaultdata. - for (const scoId in response) { - const sco = response[scoId]; - - sco.defaultdata = {}; - sco.defaultdata['cmi.core.student_id'] = userName; - sco.defaultdata['cmi.core.student_name'] = fullName; - sco.defaultdata['cmi.core.lesson_mode'] = 'normal'; // Overridden in player. - sco.defaultdata['cmi.core.credit'] = 'credit'; // Overridden in player. - - if (sco.userdata.status === '') { - sco.defaultdata['cmi.core.entry'] = 'ab-initio'; - } else if (sco.userdata['cmi.core.exit'] === 'suspend') { - sco.defaultdata['cmi.core.entry'] = 'resume'; - } else { - sco.defaultdata['cmi.core.entry'] = ''; - } - - sco.defaultdata['cmi.student_data.mastery_score'] = this.scormIsset(sco.userdata, 'masteryscore'); - sco.defaultdata['cmi.student_data.max_time_allowed'] = this.scormIsset(sco.userdata, 'max_time_allowed'); - sco.defaultdata['cmi.student_data.time_limit_action'] = this.scormIsset(sco.userdata, 'time_limit_action'); - sco.defaultdata['cmi.core.total_time'] = this.scormIsset(sco.userdata, 'cmi.core.total_time', '00:00:00'); - sco.defaultdata['cmi.launch_data'] = launchUrls[sco.scoid]; - - // Now handle standard userdata items. - sco.defaultdata['cmi.core.lesson_location'] = this.scormIsset(sco.userdata, 'cmi.core.lesson_location'); - sco.defaultdata['cmi.core.lesson_status'] = this.scormIsset(sco.userdata, 'cmi.core.lesson_status'); - sco.defaultdata['cmi.core.score.raw'] = this.scormIsset(sco.userdata, 'cmi.core.score.raw'); - sco.defaultdata['cmi.core.score.max'] = this.scormIsset(sco.userdata, 'cmi.core.score.max'); - sco.defaultdata['cmi.core.score.min'] = this.scormIsset(sco.userdata, 'cmi.core.score.min'); - sco.defaultdata['cmi.core.exit'] = this.scormIsset(sco.userdata, 'cmi.core.exit'); - sco.defaultdata['cmi.suspend_data'] = this.scormIsset(sco.userdata, 'cmi.suspend_data'); - sco.defaultdata['cmi.comments'] = this.scormIsset(sco.userdata, 'cmi.comments'); - sco.defaultdata['cmi.student_preference.language'] = this.scormIsset(sco.userdata, - 'cmi.student_preference.language'); - sco.defaultdata['cmi.student_preference.audio'] = this.scormIsset(sco.userdata, - 'cmi.student_preference.audio', '0'); - sco.defaultdata['cmi.student_preference.speed'] = this.scormIsset(sco.userdata, - 'cmi.student_preference.speed', '0'); - sco.defaultdata['cmi.student_preference.text'] = this.scormIsset(sco.userdata, - 'cmi.student_preference.text', '0'); - - // Some data needs to be both in default data and user data. - sco.userdata.student_id = userName; - sco.userdata.student_name = fullName; - sco.userdata.mode = sco.defaultdata['cmi.core.lesson_mode']; - sco.userdata.credit = sco.defaultdata['cmi.core.credit']; - sco.userdata.entry = sco.defaultdata['cmi.core.entry']; - } - - return response; - }); - }); - } - - /** - * Insert a track in the offline tracks store. - * This function is based on Moodle's scorm_insert_track. - * - * @param scormId SCORM ID. - * @param scoId SCO ID. - * @param attempt Attempt number. - * @param element Name of the element to insert. - * @param value Value to insert. - * @param forceCompleted True if SCORM forces completed. - * @param scoData User data for the given SCO. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not set use site's current user. - * @return Promise resolved when the insert is done. - */ - protected insertTrack(scormId: number, scoId: number, attempt: number, element: string, value: any, forceCompleted?: boolean, - scoData?: any, siteId?: string, userId?: number): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - scoData = scoData || {}; - - const promises = [], // List of promises for actions previous to the real insert. - scoUserData = scoData.userdata || {}, - db = site.getDb(); - let lessonStatusInserted = false; - - if (forceCompleted) { - if (element == 'cmi.core.lesson_status' && value == 'incomplete') { - if (scoUserData['cmi.core.score.raw']) { - value = 'completed'; - } - } - if (element == 'cmi.core.score.raw') { - if (scoUserData['cmi.core.lesson_status'] == 'incomplete') { - lessonStatusInserted = true; - - promises.push(this.insertTrackToDB(db, userId, scormId, scoId, attempt, 'cmi.core.lesson_status', - 'completed')); - } - } - } - - return Promise.all(promises).then(() => { - // Don't update x.start.time, keep the original value. - if (!scoUserData[element] || element != 'x.start.time') { - let promise = > this.insertTrackToDB(db, userId, scormId, scoId, attempt, element, value); - - return promise.catch((error) => { - if (lessonStatusInserted) { - // Rollback previous insert. - promise = > this.insertTrackToDB(db, userId, scormId, scoId, attempt, - 'cmi.core.lesson_status', 'incomplete'); - - return promise.then(() => { - return Promise.reject(error); - }); - } - - return Promise.reject(null); - }); - } - }); - }); - } - - /** - * Insert a track in the DB. - * - * @param db Site's DB. - * @param userId User ID. - * @param scormId SCORM ID. - * @param scoId SCO ID. - * @param attempt Attempt number. - * @param element Name of the element to insert. - * @param value Value of the element to insert. - * @param synchronous True if insert should NOT return a promise. Please use it only if synchronous is a must. - * @return Returns a promise if synchronous=false, otherwise returns a boolean. - */ - protected insertTrackToDB(db: SQLiteDB, userId: number, scormId: number, scoId: number, attempt: number, element: string, - value: any, synchronous?: boolean): boolean | Promise { - - const entry = { - userid: userId, - scormid: scormId, - scoid: scoId, - attempt: attempt, - element: element, - value: typeof value == 'undefined' ? null : JSON.stringify(value), - timemodified: this.timeUtils.timestamp(), - synced: 0 - }; - - if (synchronous) { - // The insert operation is always asynchronous, always return true. - db.insertRecord(AddonModScormOfflineProvider.TRACKS_TABLE, entry); - - return true; - } else { - return db.insertRecord(AddonModScormOfflineProvider.TRACKS_TABLE, entry); - } - } - - /** - * Insert a track in the offline tracks store, returning a synchronous value. - * Please use this function only if synchronous is a must. It's recommended to use insertTrack. - * This function is based on Moodle's scorm_insert_track. - * - * @param scormId SCORM ID. - * @param scoId SCO ID. - * @param attempt Attempt number. - * @param element Name of the element to insert. - * @param value Value of the element to insert. - * @param forceCompleted True if SCORM forces completed. - * @param scoData User data for the given SCO. - * @param userId User ID. If not set use current user. - * @return Promise resolved when the insert is done. - */ - protected insertTrackSync(scormId: number, scoId: number, attempt: number, element: string, value: any, - forceCompleted?: boolean, scoData?: any, userId?: number): boolean { - scoData = scoData || {}; - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - - if (!this.sitesProvider.isLoggedIn()) { - // Not logged in, we can't get the site DB. User logged out or session expired while an operation was ongoing. - return false; - } - - const scoUserData = scoData.userdata || {}, - db = this.sitesProvider.getCurrentSite().getDb(); - let lessonStatusInserted = false; - - if (forceCompleted) { - if (element == 'cmi.core.lesson_status' && value == 'incomplete') { - if (scoUserData['cmi.core.score.raw']) { - value = 'completed'; - } - } - if (element == 'cmi.core.score.raw') { - if (scoUserData['cmi.core.lesson_status'] == 'incomplete') { - lessonStatusInserted = true; - - if (!this.insertTrackToDB(db, userId, scormId, scoId, attempt, 'cmi.core.lesson_status', 'completed', true)) { - return false; - } - } - } - } - - // Don't update x.start.time, keep the original value. - if (!scoUserData[element] || element != 'x.start.time') { - if (!this.insertTrackToDB(db, userId, scormId, scoId, attempt, element, value, true)) { - // Insert failed. - if (lessonStatusInserted) { - // Rollback previous insert. - this.insertTrackToDB(db, userId, scormId, scoId, attempt, 'cmi.core.lesson_status', 'incomplete', true); - } - - return false; - } - - return true; - } - } - - /** - * Mark all the entries from a SCO and attempt as synced. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param scoId SCO ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when marked. - */ - markAsSynced(scormId: number, attempt: number, scoId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - this.logger.debug('Mark SCO ' + scoId + ' as synced for attempt ' + attempt + ' in SCORM ' + scormId); - - return site.getDb().updateRecords(AddonModScormOfflineProvider.TRACKS_TABLE, {synced: 1}, { - scormid: scormId, - userid: userId, - attempt: attempt, - scoid: scoId, - synced: 0 - }); - }); - } - - /** - * Removes the default data form user data. - * - * @param userData User data returned by AddonModScormProvider.getScormUserData. - * @return User data without default data. - */ - protected removeDefaultData(userData: any): any { - const result = this.utils.clone(userData); - - for (const key in result) { - delete result[key].defaultdata; - } - - return result; - } - - /** - * Saves a SCORM tracking record in offline. - * - * @param scorm SCORM. - * @param scoId Sco ID. - * @param attempt Attempt number. - * @param tracks Tracking data to store. - * @param userData User data for this attempt and SCO. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when data is saved. - */ - saveTracks(scorm: any, scoId: number, attempt: number, tracks: any[], userData: any, siteId?: string, userId?: number) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - // Block the SCORM so it can't be synced. - this.syncProvider.blockOperation(AddonModScormProvider.COMPONENT, scorm.id, 'saveTracksOffline', siteId); - - // Insert all the tracks. - const promises = []; - tracks.forEach((track) => { - promises.push(this.insertTrack(scorm.id, scoId, attempt, track.element, track.value, scorm.forcecompleted, - userData[scoId], siteId, userId)); - }); - - return Promise.all(promises).finally(() => { - // Unblock the SCORM operation. - this.syncProvider.unblockOperation(AddonModScormProvider.COMPONENT, scorm.id, 'saveTracksOffline', siteId); - }); - }); - } - - /** - * Saves a SCORM tracking record in offline returning a synchronous value. - * Please use this function only if synchronous is a must. It's recommended to use saveTracks. - * - * @param scorm SCORM. - * @param scoId Sco ID. - * @param attempt Attempt number. - * @param tracks Tracking data to store. - * @param userData User data for this attempt and SCO. - * @return True if data to insert is valid, false otherwise. Returning true doesn't mean that the data - * has been stored, this function can return true but the insertion can still fail somehow. - */ - saveTracksSync(scorm: any, scoId: number, attempt: number, tracks: any[], userData: any, userId?: number): boolean { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); - let success = true; - - tracks.forEach((track) => { - if (!this.insertTrackSync(scorm.id, scoId, attempt, track.element, track.value, scorm.forcecompleted, userData[scoId], - userId)) { - success = false; - } - }); - - return success; - } - - /** - * Check for a parameter in userData and return it if it's set or return 'ifempty' if it's empty. - * Based on Moodle's scorm_isset function. - * - * @param userData Contains user's data. - * @param param Name of parameter that should be checked. - * @param ifEmpty Value to be replaced with if param is not set. - * @return Value from userData[param] if set, ifEmpty otherwise. - */ - protected scormIsset(userData: any, param: string, ifEmpty: any = ''): any { - if (typeof userData[param] != 'undefined') { - return userData[param]; - } - - return ifEmpty; - } - - /** - * Set an attempt's snapshot. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param userData User data to store as snapshot. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when snapshot has been stored. - */ - setAttemptSnapshot(scormId: number, attempt: number, userData: any, siteId?: string, userId?: number): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - this.logger.debug('Set snapshot for attempt ' + attempt + ' in SCORM ' + scormId); - - const newData = { - timemodified: this.timeUtils.timestamp(), - snapshot: JSON.stringify(this.removeDefaultData(userData)) - }; - - return site.getDb().updateRecords(AddonModScormOfflineProvider.ATTEMPTS_TABLE, newData, { scormid: scormId, - userid: userId, attempt: attempt }); - }); - } -} diff --git a/src/addon/mod/scorm/providers/scorm-sync.ts b/src/addon/mod/scorm/providers/scorm-sync.ts deleted file mode 100644 index aba64e427..000000000 --- a/src/addon/mod/scorm/providers/scorm-sync.ts +++ /dev/null @@ -1,877 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreCourseActivitySyncBaseProvider } from '@core/course/classes/activity-sync'; -import { AddonModScormProvider, AddonModScormAttemptCountResult } from './scorm'; -import { AddonModScormOfflineProvider } from './scorm-offline'; -import { AddonModScormPrefetchHandler } from './prefetch-handler'; - -/** - * Data returned by a SCORM sync. - */ -export interface AddonModScormSyncResult { - /** - * List of warnings. - */ - warnings: string[]; - - /** - * Whether an attempt was finished in the site due to the sync, - */ - attemptFinished: boolean; - - /** - * Whether some data was sent to the site. - */ - updated: boolean; -} - -/** - * Service to sync SCORMs. - */ -@Injectable() -export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_scorm_autom_synced'; - - protected componentTranslate: string; - - constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, - syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService, - private eventsProvider: CoreEventsProvider, timeUtils: CoreTimeUtilsProvider, - private scormProvider: AddonModScormProvider, private scormOfflineProvider: AddonModScormOfflineProvider, - prefetchHandler: AddonModScormPrefetchHandler, private utils: CoreUtilsProvider, - prefetchDelegate: CoreCourseModulePrefetchDelegate, private courseProvider: CoreCourseProvider, - private logHelper: CoreCourseLogHelperProvider) { - - super('AddonModScormSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils, prefetchDelegate, prefetchHandler); - - this.componentTranslate = courseProvider.translateModuleName('scorm'); - } - - /** - * Add an offline attempt to the right of the new attempts array if possible. - * If the attempt cannot be created as a new attempt then it will be deleted. - * - * @param scormId SCORM ID. - * @param attempt The offline attempt to treat. - * @param lastOffline Last offline attempt number. - * @param newAttemptsSameOrder Attempts that'll be created as new attempts but keeping the current order. - * @param newAttemptsAtEnd Object with attempts that'll be created at the end of the list of attempts (should be max 1). - * @param lastOfflineCreated Time when the last offline attempt was created. - * @param lastOfflineIncomplete Whether the last offline attempt is incomplete. - * @param warnings Array where to add the warnings. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected addToNewOrDelete(scormId: number, attempt: number, lastOffline: number, newAttemptsSameOrder: number[], - newAttemptsAtEnd: any, lastOfflineCreated: number, lastOfflineIncomplete: boolean, warnings: string[], - siteId: string): Promise { - - if (attempt == lastOffline) { - newAttemptsSameOrder.push(attempt); - - return Promise.resolve(); - } - - // Check if the attempt can be created. - return this.scormOfflineProvider.getAttemptCreationTime(scormId, attempt, siteId).then((time) => { - if (time > lastOfflineCreated) { - // This attempt was created after the last offline attempt, we'll add it to the end of the list if possible. - if (lastOfflineIncomplete) { - // It can't be added because the last offline attempt is incomplete, delete it. - this.logger.debug('Try to delete attempt ' + attempt + ' because it cannot be created as a new attempt.'); - - return this.scormOfflineProvider.deleteAttempt(scormId, attempt, siteId).then(() => { - warnings.push(this.translate.instant('addon.mod_scorm.warningofflinedatadeleted', {number: attempt})); - }).catch(() => { - // Maybe there's something wrong with the data or the storage implementation. - }); - } else { - // Add the attempt at the end. - newAttemptsAtEnd[time] = attempt; - } - - } else { - newAttemptsSameOrder.push(attempt); - } - }); - } - - /** - * Check if can retry an attempt synchronization. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param lastOnline Last online attempt number. - * @param cmId Module ID. - * @param siteId Site ID. - * @return Promise resolved if can retry the synchronization, rejected otherwise. - */ - protected canRetrySync(scormId: number, attempt: number, lastOnline: number, cmId: number, siteId: string): Promise { - - // If it's the last attempt we don't need to ignore cache because we already did it. - const refresh = lastOnline != attempt; - - return this.scormProvider.getScormUserData(scormId, attempt, { - cmId, - readingStrategy: refresh ? CoreSitesReadingStrategy.OnlyNetwork : undefined, - siteId, - }).then((siteData) => { - // Get synchronization snapshot (if sync fails it should store a snapshot). - return this.scormOfflineProvider.getAttemptSnapshot(scormId, attempt, siteId).then((snapshot) => { - if (!snapshot || !Object.keys(snapshot).length || !this.snapshotEquals(snapshot, siteData)) { - // No snapshot or it doesn't match, we can't retry the synchronization. - return Promise.reject(null); - } - }); - }); - } - - /** - * Create new attempts at the end of the offline attempts list. - * - * @param scormId SCORM ID. - * @param newAttempts Object with the attempts to create. The keys are the timecreated, the values are the attempt number. - * @param lastOffline Number of last offline attempt. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected createNewAttemptsAtEnd(scormId: number, newAttempts: any, lastOffline: number, siteId: string): Promise { - const times = Object.keys(newAttempts).sort(), // Sort in ASC order. - promises = []; - - if (!times.length) { - return Promise.resolve(); - } - - times.forEach((time, index) => { - const attempt = newAttempts[time]; - - promises.push(this.scormOfflineProvider.changeAttemptNumber(scormId, attempt, lastOffline + index + 1, siteId)); - }); - - return this.utils.allPromises(promises); - } - - /** - * Finish a sync process: remove offline data if needed, prefetch SCORM data, set sync time and return the result. - * - * @param siteId Site ID. - * @param scorm SCORM. - * @param warnings List of warnings generated by the sync. - * @param lastOnline Last online attempt number before the sync. - * @param lastOnlineWasFinished Whether the last online attempt was finished before the sync. - * @param initialCount Attempt count before the sync. - * @param updated Whether some data was sent to the site. - * @return Promise resolved on success. - */ - protected finishSync(siteId: string, scorm: any, warnings: string[], lastOnline?: number, lastOnlineWasFinished?: boolean, - initialCount?: AddonModScormAttemptCountResult, updated?: boolean): Promise { - - let promise; - - if (updated) { - // Update downloaded data. - promise = this.courseProvider.getModuleBasicInfoByInstance(scorm.id, 'scorm', siteId).then((module) => { - return this.prefetchAfterUpdate(module, scorm.course, undefined, siteId); - }).catch(() => { - // Ignore errors. - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - return this.setSyncTime(scorm.id, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // Check if an attempt was finished in Moodle. - if (initialCount) { - // Get attempt count again to check if an attempt was finished. - return this.scormProvider.getAttemptCount(scorm.id, {cmId: scorm.coursemodule, siteId}).then((attemptsData) => { - if (attemptsData.online.length > initialCount.online.length) { - return true; - } else if (!lastOnlineWasFinished && lastOnline > 0) { - // Last online attempt wasn't finished, let's check if it is now. - return this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, { - cmId: scorm.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((inc) => { - return !inc; - }); - } - - return false; - }); - } - - return false; - }).then((attemptFinished) => { - return { - warnings: warnings, - attemptFinished: attemptFinished, - updated: updated - }; - }); - } - - /** - * Get the creation time and the status (complete/incomplete) of an offline attempt. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param cmId Module ID. - * @param siteId Site ID. - * @return Promise resolved with the data. - */ - protected getOfflineAttemptData(scormId: number, attempt: number, cmId: number, siteId: string) - : Promise<{incomplete: boolean, timecreated: number}> { - - // Check if last offline attempt is incomplete. - return this.scormProvider.isAttemptIncomplete(scormId, attempt, { - offline: true, - cmId, - siteId, - }).then((incomplete) => { - return this.scormOfflineProvider.getAttemptCreationTime(scormId, attempt, siteId).then((timecreated) => { - return { - incomplete: incomplete, - timecreated: timecreated - }; - }); - }); - } - - /** - * Change the number of some offline attempts. We need to move all offline attempts after the collisions - * too, otherwise we would overwrite data. - * Example: We have offline attempts 1, 2 and 3. #1 and #2 have collisions. #1 can be synced, but #2 needs - * to be a new attempt. #3 will now be #4, and #2 will now be #3. - * - * @param scormId SCORM ID. - * @param newAttempts Attempts that need to be converted into new attempts. - * @param lastOnline Last online attempt. - * @param lastCollision Last attempt with collision (exists in online and offline). - * @param offlineAttempts Numbers of offline attempts. - * @param siteId Site ID. - * @return Promise resolved when attempts have been moved. - */ - protected moveNewAttempts(scormId: any, newAttempts: number[], lastOnline: number, lastCollision: number, - offlineAttempts: number[], siteId: string): Promise { - - if (!newAttempts.length) { - return Promise.resolve(); - } - - let promise = Promise.resolve(), - lastSuccessful; - - // Sort offline attempts in DESC order. - offlineAttempts = offlineAttempts.sort((a, b) => { - return Number(a) <= Number(b) ? 1 : -1; - }); - - // First move the offline attempts after the collisions. - offlineAttempts.forEach((attempt) => { - if (attempt > lastCollision) { - // We use a chain of promises because we need to move them in order. - promise = promise.then(() => { - const newNumber = attempt + newAttempts.length; - - return this.scormOfflineProvider.changeAttemptNumber(scormId, attempt, newNumber, siteId).then(() => { - lastSuccessful = attempt; - }); - }); - } - }); - - return promise.then(() => { - const successful = []; - let promises = []; - - // Sort newAttempts in ASC order. - newAttempts = newAttempts.sort((a, b) => { - return Number(a) >= Number(b) ? 1 : -1; - }); - - // Now move the attempts in newAttempts. - newAttempts.forEach((attempt, index) => { - // No need to use chain of promises. - const newNumber = lastOnline + index + 1; - - promises.push(this.scormOfflineProvider.changeAttemptNumber(scormId, attempt, newNumber, siteId).then(() => { - successful.push(attempt); - })); - }); - - return Promise.all(promises).catch((error) => { - // Moving the new attempts failed (it shouldn't happen). Let's undo the new attempts move. - promises = []; - - successful.forEach((attempt) => { - const newNumber = lastOnline + newAttempts.indexOf(attempt) + 1; - - promises.push(this.scormOfflineProvider.changeAttemptNumber(scormId, newNumber, attempt, siteId)); - }); - - return this.utils.allPromises(promises).then(() => { - return Promise.reject(error); // It will now enter the .catch that moves offline attempts after collisions. - }); - }); - - }).catch((error) => { - // Moving offline attempts after collisions failed (it shouldn't happen). Let's undo the changes. - if (!lastSuccessful) { - return Promise.reject(error); - } - - const attemptsToUndo = []; - let promise = Promise.resolve(); - - for (let i = lastSuccessful; offlineAttempts.indexOf(i) != -1; i++) { - attemptsToUndo.push(i); - } - - attemptsToUndo.forEach((attempt) => { - promise = promise.then(() => { - // Move it back. - return this.scormOfflineProvider.changeAttemptNumber(scormId, attempt + newAttempts.length, attempt, siteId); - }); - }); - - return promise.then(() => { - return Promise.reject(error); - }); - }); - } - - /** - * Save a snapshot from a synchronization. - * - * @param scormId SCORM ID. - * @param attempt Attemot number. - * @param cmId Module ID. - * @param siteId Site ID. - * @return Promise resolved when the snapshot is stored. - */ - protected saveSyncSnapshot(scormId: number, attempt: number, cmId: number, siteId: string): Promise { - // Try to get current state from the site. - return this.scormProvider.getScormUserData(scormId, attempt, { - cmId, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((data) => { - return this.scormOfflineProvider.setAttemptSnapshot(scormId, attempt, data, siteId); - }, () => { - // Error getting user data from the site. We'll have to build it ourselves. - // Let's try to get cached data about the attempt. - return this.scormProvider.getScormUserData(scormId, attempt, {cmId, siteId}).catch(() => { - // No cached data. - return {}; - }).then((data) => { - - // We need to add the synced data to the snapshot. - return this.scormOfflineProvider.getScormStoredData(scormId, attempt, false, true, siteId).then((synced) => { - synced.forEach((entry) => { - if (!data[entry.scoid]) { - data[entry.scoid] = { - scoid: entry.scoid, - userdata: {} - }; - } - data[entry.scoid].userdata[entry.element] = entry.value; - }); - - return this.scormOfflineProvider.setAttemptSnapshot(scormId, attempt, data, siteId); - }); - }); - }); - } - - /** - * Compares an attempt's snapshot with the data retrieved from the site. - * It only compares elements with dot notation. This means that, if some SCO has been added to Moodle web - * but the user hasn't generated data for it, then the snapshot will be detected as equal. - * - * @param snapshot Attempt's snapshot. - * @param userData Data retrieved from the site. - * @return True if snapshot is equal to the user data, false otherwise. - */ - protected snapshotEquals(snapshot: any, userData: any): boolean { - // Check that snapshot contains the data from the site. - for (const scoId in userData) { - const siteSco = userData[scoId], - snapshotSco = snapshot[scoId]; - - for (const element in siteSco.userdata) { - if (element.indexOf('.') > -1) { - if (!snapshotSco || siteSco.userdata[element] !== snapshotSco.userdata[element]) { - return false; - } - } - } - } - - // Now check the opposite way: site userData contains the data from the snapshot. - for (const scoId in snapshot) { - const siteSco = userData[scoId], - snapshotSco = snapshot[scoId]; - - for (const element in snapshotSco.userdata) { - if (element.indexOf('.') > -1) { - if (!siteSco || siteSco.userdata[element] !== snapshotSco.userdata[element]) { - return false; - } - } - } - } - - return true; - } - - /** - * Try to synchronize all the SCORMs in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllScorms(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all SCORMs', this.syncAllScormsFunc.bind(this), [force], siteId); - } - - /** - * Sync all SCORMs on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllScormsFunc(siteId: string, force?: boolean): Promise { - - // Get all offline attempts. - return this.scormOfflineProvider.getAllAttempts(siteId).then((attempts) => { - const scorms = [], - ids = [], // To prevent duplicates. - promises = []; - - // Get the IDs of all the SCORMs that have something to be synced. - attempts.forEach((attempt) => { - if (ids.indexOf(attempt.scormid) == -1) { - ids.push(attempt.scormid); - - scorms.push({ - id: attempt.scormid, - courseId: attempt.courseid - }); - } - }); - - // Sync all SCORMs that haven't been synced for a while and that aren't attempted right now. - scorms.forEach((scorm) => { - if (!this.syncProvider.isBlocked(AddonModScormProvider.COMPONENT, scorm.id, siteId)) { - - promises.push(this.scormProvider.getScormById(scorm.courseId, scorm.id, {siteId}).then((scorm) => { - const promise = force ? this.syncScorm(scorm, siteId) : this.syncScormIfNeeded(scorm, siteId); - - return promise.then((data) => { - if (typeof data != 'undefined') { - // We tried to sync. Send event. - this.eventsProvider.trigger(AddonModScormSyncProvider.AUTO_SYNCED, { - scormId: scorm.id, - attemptFinished: data.attemptFinished, - warnings: data.warnings, - updated: data.updated - }, siteId); - } - }); - })); - } - }); - - return Promise.all(promises); - }); - } - - /** - * Send data from a SCORM offline attempt to the site. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param cmId Module ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the attempt is successfully synced. - */ - protected syncAttempt(scormId: number, attempt: number, cmId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - this.logger.debug('Try to sync attempt ' + attempt + ' in SCORM ' + scormId + ' and site ' + siteId); - - // Get only not synced entries. - return this.scormOfflineProvider.getScormStoredData(scormId, attempt, true, false, siteId).then((entries) => { - const scos = {}, - promises = []; - let somethingSynced = false; - - // Get data to send (only elements with dots like cmi.core.exit, in Mobile we store more data to make offline work). - entries.forEach((entry) => { - if (entry.element.indexOf('.') > -1) { - if (!scos[entry.scoid]) { - scos[entry.scoid] = []; - } - - scos[entry.scoid].push({ - element: entry.element, - value: entry.value - }); - } - }); - - // Send the data in each SCO. - for (const id in scos) { - const scoId = Number(id), - tracks = scos[scoId]; - - promises.push(this.scormProvider.saveTracksOnline(scormId, scoId, attempt, tracks, siteId).then(() => { - // Sco data successfully sent. Mark them as synced. This is needed because some SCOs sync might fail. - return this.scormOfflineProvider.markAsSynced(scormId, attempt, scoId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - somethingSynced = true; - }); - })); - } - - return this.utils.allPromises(promises).then(() => { - // Attempt has been sent. Let's delete it from local. - return this.scormOfflineProvider.deleteAttempt(scormId, attempt, siteId).catch(() => { - // Failed to delete (shouldn't happen). Let's retry once. - return this.scormOfflineProvider.deleteAttempt(scormId, attempt, siteId).catch(() => { - // Maybe there's something wrong with the data or the storage implementation. - this.logger.error('After sync: error deleting attempt ' + attempt + ' in SCORM ' + scormId); - }); - }); - }).catch((error) => { - if (somethingSynced) { - // Some SCOs have been synced and some not. - // Try to store a snapshot of the current state to be able to re-try the synchronization later. - this.logger.error('Error synchronizing some SCOs for attempt ' + attempt + ' in SCORM ' + - scormId + '. Saving snapshot.'); - - return this.saveSyncSnapshot(scormId, attempt, cmId, siteId).then(() => { - return Promise.reject(error); - }); - } else { - this.logger.error('Error synchronizing attempt ' + attempt + ' in SCORM ' + scormId); - } - - return Promise.reject(error); - }); - }); - } - - /** - * Sync a SCORM only if a certain time has passed since the last time. - * - * @param scorm SCORM. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the SCORM is synced or if it doesn't need to be synced. - */ - syncScormIfNeeded(scorm: any, siteId?: string): Promise { - return this.isSyncNeeded(scorm.id, siteId).then((needed) => { - if (needed) { - return this.syncScorm(scorm, siteId); - } - }); - } - - /** - * Try to synchronize a SCORM. - * The promise returned will be resolved with an array with warnings if the synchronization is successful. A successful - * synchronization doesn't mean that all the data has been sent to the site, it's possible that some attempt can't be sent. - * - * @param scorm SCORM. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved in success. - */ - syncScorm(scorm: any, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - let warnings = [], - syncPromise, - initialCount, - lastOnline = 0, - lastOnlineWasFinished = false; - - if (this.isSyncing(scorm.id, siteId)) { - // There's already a sync ongoing for this SCORM, return the promise. - return this.getOngoingSync(scorm.id, siteId); - } - - // Verify that SCORM isn't blocked. - if (this.syncProvider.isBlocked(AddonModScormProvider.COMPONENT, scorm.id, siteId)) { - this.logger.debug('Cannot sync SCORM ' + scorm.id + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug('Try to sync SCORM ' + scorm.id + ' in site ' + siteId); - - // Sync offline logs. - syncPromise = this.logHelper.syncIfNeeded(AddonModScormProvider.COMPONENT, scorm.id, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Get attempts data. We ignore cache for online attempts, so this call will fail if offline or server down. - return this.scormProvider.getAttemptCount(scorm.id, { - cmId: scorm.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }); - }).then((attemptsData) => { - if (!attemptsData.offline || !attemptsData.offline.length) { - // Nothing to sync. - return this.finishSync(siteId, scorm, warnings, lastOnline, lastOnlineWasFinished); - } - - initialCount = attemptsData; - - const collisions = []; - - // Check if there are collisions between offline and online attempts (same number). - attemptsData.online.forEach((attempt) => { - lastOnline = Math.max(lastOnline, attempt); - if (attemptsData.offline.indexOf(attempt) > -1) { - collisions.push(attempt); - } - }); - - // Check if last online attempt is finished. Ignore cache. - const promise = lastOnline <= 0 ? Promise.resolve(false) : - this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, { - cmId: scorm.coursemodule, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }); - - return promise.then((incomplete) => { - lastOnlineWasFinished = !incomplete; - - if (!collisions.length && !incomplete) { - // No collisions and last attempt is complete. Send offline attempts to Moodle. - const promises = []; - - attemptsData.offline.forEach((attempt) => { - if (scorm.maxattempt == 0 || attempt <= scorm.maxattempt) { - promises.push(this.syncAttempt(scorm.id, attempt, scorm.coursemodule, siteId)); - } - }); - - return Promise.all(promises).then(() => { - // All data synced, finish. - return this.finishSync(siteId, scorm, warnings, lastOnline, lastOnlineWasFinished, initialCount, true); - }); - - } else if (collisions.length) { - // We have collisions, treat them. - return this.treatCollisions(scorm.id, collisions, lastOnline, attemptsData.offline, scorm.coursemodule, siteId) - .then((warns) => { - warnings = warnings.concat(warns); - - // The offline attempts might have changed since some collisions can be converted to new attempts. - return this.scormOfflineProvider.getAttempts(scorm.id, siteId).then((entries) => { - const promises = []; - let cannotSyncSome = false; - - entries = entries.map((entry) => { - return entry.attempt; // Get only the attempt number. - }); - - if (incomplete && entries.indexOf(lastOnline) > -1) { - // Last online was incomplete, but it was continued in offline. - incomplete = false; - } - - entries.forEach((attempt) => { - // We'll always sync attempts previous to lastOnline (failed sync or continued in offline). - // We'll only sync new attemps if last online attempt is completed. - if (!incomplete || attempt <= lastOnline) { - if (scorm.maxattempt == 0 || attempt <= scorm.maxattempt) { - promises.push(this.syncAttempt(scorm.id, attempt, scorm.coursemodule, siteId)); - } - } else { - cannotSyncSome = true; - } - }); - - return Promise.all(promises).then(() => { - if (cannotSyncSome) { - warnings.push(this.translate.instant('addon.mod_scorm.warningsynconlineincomplete')); - } - - return this.finishSync(siteId, scorm, warnings, lastOnline, lastOnlineWasFinished, initialCount, - true); - }); - }); - }); - } else { - // No collisions, but last online attempt is incomplete so we can't send offline attempts. - warnings.push(this.translate.instant('addon.mod_scorm.warningsynconlineincomplete')); - - return this.finishSync(siteId, scorm, warnings, lastOnline, lastOnlineWasFinished, initialCount, false); - } - }); - }); - - return this.addOngoingSync(scorm.id, syncPromise, siteId); - } - - /** - * Treat collisions found in a SCORM synchronization process. - * - * @param scormId SCORM ID. - * @param collisions Numbers of attempts that exist both in online and offline. - * @param lastOnline Last online attempt. - * @param offlineAttempts Numbers of offline attempts. - * @param cmId Module ID. - * @param siteId Site ID. - * @return Promise resolved when the collisions have been treated. It returns warnings array. - * @description - * - * Treat collisions found in a SCORM synchronization process. A collision is when an attempt exists both in offline - * and online. A collision can be: - * - * - Two different attempts. - * - An online attempt continued in offline. - * - A failure in a previous sync. - * - * This function will move into new attempts the collisions that can't be merged. It will usually keep the order of the - * offline attempts EXCEPT if the offline attempt was created after the last offline attempt (edge case). - * - * Edge case: A user creates offline attempts and when he syncs we retrieve an incomplete online attempt, so the offline - * attempts cannot be synced. Then the user continues that online attempt and goes offline, so a collision is created. - * When we perform the next sync we detect that this collision cannot be merged, so this offline attempt needs to be - * created as a new attempt. Since this attempt was created after the last offline attempt, it will be added ot the end - * of the list if the last attempt is completed. If the last attempt is not completed then the offline data will de deleted - * because we can't create a new attempt. - */ - protected treatCollisions(scormId: number, collisions: number[], lastOnline: number, offlineAttempts: number[], cmId: number, - siteId: string): Promise { - - const warnings = [], - newAttemptsSameOrder = [], // Attempts that will be created as new attempts but keeping the current order. - newAttemptsAtEnd = {}, // Attempts that will be created at the end of the list of attempts (should be max 1 attempt). - lastCollision = Math.max.apply(Math, collisions); - let lastOffline = Math.max.apply(Math, offlineAttempts); - - // Get needed data from the last offline attempt. - return this.getOfflineAttemptData(scormId, lastOffline, cmId, siteId).then((lastOfflineData) => { - const promises = []; - - collisions.forEach((attempt) => { - // First get synced entries to detect if it was a failed synchronization. - promises.push(this.scormOfflineProvider.getScormStoredData(scormId, attempt, false, true, siteId).then((synced) => { - if (synced && synced.length) { - // The attempt has synced entries, it seems to be a failed synchronization. - // Let's get the entries that haven't been synced, maybe it just failed to delete the attempt. - return this.scormOfflineProvider.getScormStoredData(scormId, attempt, true, false, siteId) - .then((entries) => { - - // Check if there are elements to sync. - let hasDataToSend = false; - for (const i in entries) { - const entry = entries[i]; - if (entry.element.indexOf('.') > -1) { - hasDataToSend = true; - break; - } - } - - if (hasDataToSend) { - // There are elements to sync. We need to check if it's possible to sync them or not. - return this.canRetrySync(scormId, attempt, lastOnline, cmId, siteId).catch(() => { - // Cannot retry sync, we'll create a new offline attempt if possible. - return this.addToNewOrDelete(scormId, attempt, lastOffline, newAttemptsSameOrder, - newAttemptsAtEnd, lastOfflineData.timecreated, lastOfflineData.incomplete, warnings, - siteId); - }); - } else { - // Nothing to sync, delete the attempt. - return this.scormOfflineProvider.deleteAttempt(scormId, attempt, siteId).catch(() => { - // Maybe there's something wrong with the data or the storage implementation. - }); - } - }); - } else { - // It's not a failed synchronization. Check if it's an attempt continued in offline. - return this.scormOfflineProvider.getAttemptSnapshot(scormId, attempt, siteId).then((snapshot) => { - if (snapshot && Object.keys(snapshot).length) { - // It has a snapshot, it means it continued an online attempt. We need to check if they've diverged. - // If it's the last attempt we don't need to ignore cache because we already did it. - const refresh = lastOnline != attempt; - - return this.scormProvider.getScormUserData(scormId, attempt, { - cmId, - readingStrategy: refresh ? CoreSitesReadingStrategy.OnlyNetwork : undefined, - siteId, - }).then((data) => { - - if (!this.snapshotEquals(snapshot, data)) { - // Snapshot has diverged, it will be converted into a new attempt if possible. - return this.addToNewOrDelete(scormId, attempt, lastOffline, newAttemptsSameOrder, - newAttemptsAtEnd, lastOfflineData.timecreated, lastOfflineData.incomplete, warnings, - siteId); - } - }); - } else { - // No snapshot, it's a different attempt. - newAttemptsSameOrder.push(attempt); - } - }); - } - })); - }); - - return Promise.all(promises).then(() => { - return this.moveNewAttempts(scormId, newAttemptsSameOrder, lastOnline, lastCollision, offlineAttempts, siteId) - .then(() => { - - // The new attempts that need to keep the order have been created. - // Now create the new attempts at the end of the list of offline attempts. It should only be 1 attempt max. - lastOffline = lastOffline + newAttemptsSameOrder.length; - - return this.createNewAttemptsAtEnd(scormId, newAttemptsAtEnd, lastOffline, siteId).then(() => { - return warnings; - }); - }); - }); - }); - } -} diff --git a/src/addon/mod/scorm/providers/scorm.ts b/src/addon/mod/scorm/providers/scorm.ts deleted file mode 100644 index da74ecc02..000000000 --- a/src/addon/mod/scorm/providers/scorm.ts +++ /dev/null @@ -1,1789 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreWSProvider } from '@providers/ws'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUrlUtils } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModScormOfflineProvider } from './scorm-offline'; -import { CoreSite } from '@classes/site'; -import { CoreConstants } from '@core/constants'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Result of getAttemptCount. - */ -export interface AddonModScormAttemptCountResult { - /** - * List of online attempts numbers. - */ - online?: number[]; - - /** - * List of offline attempts numbers. - */ - offline?: number[]; - - /** - * Total of unique attempts. - */ - total?: number; - - /** - * Last attempt in the SCORM: the number and whether it's offline. - */ - lastAttempt?: {number: number, offline: boolean}; -} - -/** - * Service that provides some features for SCORM. - */ -@Injectable() -export class AddonModScormProvider { - static COMPONENT = 'mmaModScorm'; - - // Public constants. - static GRADESCOES = 0; - static GRADEHIGHEST = 1; - static GRADEAVERAGE = 2; - static GRADESUM = 3; - - static HIGHESTATTEMPT = 0; - static AVERAGEATTEMPT = 1; - static FIRSTATTEMPT = 2; - static LASTATTEMPT = 3; - - static MODEBROWSE = 'browse'; - static MODENORMAL = 'normal'; - static MODEREVIEW = 'review'; - - static SCORM_FORCEATTEMPT_NO = 0; - static SCORM_FORCEATTEMPT_ONCOMPLETE = 1; - static SCORM_FORCEATTEMPT_ALWAYS = 2; - - static SKIPVIEW_NEVER = 0; - static SKIPVIEW_FIRST = 1; - static SKIPVIEW_ALWAYS = 2; - - // Events. - static LAUNCH_NEXT_SCO_EVENT = 'addon_mod_scorm_launch_next_sco'; - static LAUNCH_PREV_SCO_EVENT = 'addon_mod_scorm_launch_prev_sco'; - static UPDATE_TOC_EVENT = 'addon_mod_scorm_update_toc'; - static GO_OFFLINE_EVENT = 'addon_mod_scorm_go_offline'; - static DATA_SENT_EVENT = 'addon_mod_scorm_data_sent'; - - // Protected constants. - protected VALID_STATUSES = ['notattempted', 'passed', 'completed', 'failed', 'incomplete', 'browsed', 'suspend']; - protected STATUSES = { - 'passed': 'passed', - 'completed': 'completed', - 'failed': 'failed', - 'incomplete': 'incomplete', - 'browsed': 'browsed', - 'not attempted': 'notattempted', - 'p': 'passed', - 'c': 'completed', - 'f': 'failed', - 'i': 'incomplete', - 'b': 'browsed', - 'n': 'notattempted' - }; - protected static STATUS_TO_ICON = { - assetc: 'fa-file-archive-o', - asset: 'fa-file-archive-o', - browsed: 'fa-book', - completed: 'fa-check-square-o', - failed: 'fa-times', - incomplete: 'fa-pencil-square-o', - minus: 'fa-minus', - notattempted: 'fa-square-o', - passed: 'fa-check', - plus: 'fa-plus', - popdown: 'fa-window-close-o', - popup: 'fa-window-restore', - suspend: 'fa-pause', - wait: 'fa-clock-o', - }; - - protected ROOT_CACHE_KEY = 'mmaModScorm:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private translate: TranslateService, private sitesProvider: CoreSitesProvider, - private wsProvider: CoreWSProvider, private textUtils: CoreTextUtilsProvider, private utils: CoreUtilsProvider, - private filepoolProvider: CoreFilepoolProvider, private scormOfflineProvider: AddonModScormOfflineProvider, - private timeUtils: CoreTimeUtilsProvider, private syncProvider: CoreSyncProvider, - private eventsProvider: CoreEventsProvider, private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModScormProvider'); - } - - /** - * Calculates the SCORM grade based on the grading method and the list of attempts scores. - * We only treat online attempts to calculate a SCORM grade. - * - * @param scorm SCORM. - * @param onlineAttempts Object with the online attempts. Each attempt must have a property called "grade". - * @return Grade. -1 if no grade. - */ - calculateScormGrade(scorm: any, onlineAttempts: any): number { - if (!onlineAttempts || !Object.keys(onlineAttempts).length) { - return -1; - } - - switch (scorm.whatgrade) { - case AddonModScormProvider.FIRSTATTEMPT: - return onlineAttempts[1] ? onlineAttempts[1].grade : -1; - - case AddonModScormProvider.LASTATTEMPT: - // Search the last attempt number. - let max = 0; - Object.keys(onlineAttempts).forEach((attemptNumber) => { - max = Math.max(Number(attemptNumber), max); - }); - - if (max > 0) { - return onlineAttempts[max].grade; - } - - return -1; - - case AddonModScormProvider.HIGHESTATTEMPT: - // Search the highest grade. - let grade = 0; - for (const attemptNumber in onlineAttempts) { - grade = Math.max(onlineAttempts[attemptNumber].grade, grade); - } - - return grade; - - case AddonModScormProvider.AVERAGEATTEMPT: - // Calculate the average. - let sumGrades = 0, - total = 0; - - for (const attemptNumber in onlineAttempts) { - sumGrades += onlineAttempts[attemptNumber].grade; - total++; - } - - return Math.round(sumGrades / total); - - default: - return -1; - } - } - - /** - * Calculates the size of a SCORM. - * - * @param scorm SCORM. - * @return Promise resolved with the SCORM size. - */ - calculateScormSize(scorm: any): Promise { - if (scorm.packagesize) { - return Promise.resolve(scorm.packagesize); - } - - return this.wsProvider.getRemoteFileSize(this.getPackageUrl(scorm)); - } - - /** - * Count the attempts left for the given scorm. - * - * @param scorm SCORM. - * @param attemptsCount Number of attempts performed. - * @return Number of attempts left. - */ - countAttemptsLeft(scorm: any, attemptsCount: number): number { - if (scorm.maxattempt == 0) { - return Number.MAX_VALUE; // Unlimited attempts. - } - - attemptsCount = Number(attemptsCount); // Make sure it's a number. - if (isNaN(attemptsCount)) { - return -1; - } - - return scorm.maxattempt - attemptsCount; - } - - /** - * Returns the mode and attempt number to use based on mode selected and SCORM data. - * This function is based on Moodle's scorm_check_mode. - * - * @param scorm SCORM. - * @param mode Selected mode. - * @param attempt Current attempt. - * @param newAttempt Whether it should start a new attempt. - * @param incomplete Whether current attempt is incomplete. - * @return Mode, attempt number and whether to start a new attempt. - */ - determineAttemptAndMode(scorm: any, mode: string, attempt: number, newAttempt?: boolean, incomplete?: boolean) - : {mode: string, attempt: number, newAttempt: boolean} { - - if (mode == AddonModScormProvider.MODEBROWSE) { - if (scorm.hidebrowse) { - // Prevent Browse mode if hidebrowse is set. - mode = AddonModScormProvider.MODENORMAL; - } else { - // We don't need to check attempts as browse mode is set. - if (attempt == 0) { - attempt = 1; - newAttempt = true; - } - - return { - mode: mode, - attempt: attempt, - newAttempt: newAttempt - }; - } - } - - if (scorm.forcenewattempt == AddonModScormProvider.SCORM_FORCEATTEMPT_ALWAYS) { - // This SCORM is configured to force a new attempt on every re-entry. - return { - mode: AddonModScormProvider.MODENORMAL, - attempt: attempt + 1, - newAttempt: true - }; - } - - // Validate user request to start a new attempt. - if (attempt == 0) { - newAttempt = true; - } else if (incomplete) { - // The option to start a new attempt should never have been presented. Force false. - newAttempt = false; - } else if (scorm.forcenewattempt) { - // A new attempt should be forced for already completed attempts. - newAttempt = true; - } - - if (newAttempt && (scorm.maxattempt == 0 || attempt < scorm.maxattempt)) { - // Create a new attempt. Force mode normal. - attempt++; - mode = AddonModScormProvider.MODENORMAL; - } else { - if (incomplete) { - // We can't review an incomplete attempt. - mode = AddonModScormProvider.MODENORMAL; - } else { - // We aren't starting a new attempt and the current one is complete, force review mode. - mode = AddonModScormProvider.MODEREVIEW; - } - } - - return { - mode: mode, - attempt: attempt, - newAttempt: newAttempt - }; - } - - /** - * Check if TOC should be displayed in the player. - * - * @param scorm SCORM. - * @return Whether it should display TOC. - */ - displayTocInPlayer(scorm: any): boolean { - return scorm.hidetoc !== 3; - } - - /** - * This is a little language parser for AICC_SCRIPT. - * Evaluates the expression and returns a boolean answer. - * See 2.3.2.5.1. Sequencing/Navigation Today - from the SCORM 1.2 spec (CAM). - * - * @param prerequisites The AICC_SCRIPT prerequisites expression. - * @param trackData The tracked user data of each SCO. - * @return Whether the prerequisites are fulfilled. - */ - evalPrerequisites(prerequisites: string, trackData: any): boolean { - - const stack = []; // List of prerequisites. - - // Expand the amp entities. - prerequisites = prerequisites.replace(/&/gi, '&'); - // Find all my parsable tokens. - prerequisites = prerequisites.replace(/(&|\||\(|\)|\~)/gi, '\t$1\t'); - // Expand operators. - prerequisites = prerequisites.replace(/&/gi, '&&'); - prerequisites = prerequisites.replace(/\|/gi, '||'); - - // Now - grab all the tokens. - const elements = prerequisites.trim().split('\t'); - - // Process each token to build an expression to be evaluated. - elements.forEach((element) => { - element = element.trim(); - if (!element) { - return; - } - - if (!element.match(/^(&&|\|\||\(|\))$/gi)) { - // Create each individual expression. - // Search for ~ = <> X*{} . - - const re = /^(\d+)\*\{(.+)\}$/, // Sets like 3*{S34, S36, S37, S39}. - reOther = /^(.+)(\=|\<\>)(.+)$/; // Other symbols. - let matches; - - if (re.test(element)) { - matches = element.match(re); - - const repeat = matches[1], - set = matches[2].split(','); - let count = 0; - - set.forEach((setElement) => { - setElement = setElement.trim(); - - if (typeof trackData[setElement] != 'undefined' && - (trackData[setElement].status == 'completed' || trackData[setElement].status == 'passed')) { - count++; - } - }); - - if (count >= repeat) { - element = 'true'; - } else { - element = 'false'; - } - } else if (element == '~') { - // Not maps ~. - element = '!'; - } else if (reOther.test(element)) { - // Other symbols = | <> . - matches = element.match(reOther); - element = matches[1].trim(); - - if (typeof trackData[element] != 'undefined') { - let value = matches[3].trim().replace(/(\'|\")/gi), - oper; - - if (typeof this.STATUSES[value] != 'undefined') { - value = this.STATUSES[value]; - } - - if (matches[2] == '<>') { - oper = '!='; - } else { - oper = '=='; - } - - element = '(\'' + trackData[element].status + '\' ' + oper + ' \'' + value + '\')'; - } else { - element = 'false'; - } - } else { - // Everything else must be an element defined like S45 ... - if (typeof trackData[element] != 'undefined' && - (trackData[element].status == 'completed' || trackData[element].status == 'passed')) { - element = 'true'; - } else { - element = 'false'; - } - } - } - - // Add the element to the list of prerequisites. - stack.push(' ' + element + ' '); - }); - - // tslint:disable: no-eval - return eval(stack.join('') + ';'); - } - - /** - * Formats a grade to be displayed. - * - * @param scorm SCORM. - * @param grade Grade. - * @return Grade to display. - */ - formatGrade(scorm: any, grade: number): string { - if (typeof grade == 'undefined' || grade == -1) { - return this.translate.instant('core.none'); - } - - if (scorm.grademethod !== AddonModScormProvider.GRADESCOES && scorm.maxgrade > 0) { - grade = (grade / scorm.maxgrade) * 100; - - return this.translate.instant('core.percentagenumber', {$a: this.textUtils.roundToDecimals(grade, 2)}); - } - - return String(grade); - } - - /** - * Formats a tree-like TOC into an array. - * - * @param toc SCORM's TOC (tree format). - * @param level The level of the TOC we're right now. 0 by default. - * @return SCORM's TOC (array format). - */ - formatTocToArray(toc: any[], level: number = 0): any[] { - if (!toc || !toc.length) { - return []; - } - - let formatted = []; - - toc.forEach((node) => { - node.level = level; - formatted.push(node); - - formatted = formatted.concat(this.formatTocToArray(node.children, level + 1)); - }); - - return formatted; - } - - /** - * Get access information for a given SCORM. - * - * @param scormId SCORM ID. - * @param options Other options. - * @return Object with access information. - * @since 3.7 - */ - getAccessInformation(scormId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - if (!site.wsAvailable('mod_scorm_get_scorm_access_information')) { - // Access information not available for 3.6 or older sites. - return Promise.resolve({}); - } - - const params = { - scormid: scormId, - }; - const preSets = { - cacheKey: this.getAccessInformationCacheKey(scormId), - component: AddonModScormProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_scorm_get_scorm_access_information', params, preSets); - }); - } - - /** - * Get cache key for access information WS calls. - * - * @param scormId SCORM ID. - * @return Cache key. - */ - protected getAccessInformationCacheKey(scormId: number): string { - return this.ROOT_CACHE_KEY + 'accessInfo:' + scormId; - } - - /** - * Get the number of attempts done by a user in the given SCORM. - * - * @param scormId SCORM ID. - * @param options Other options. - * @return Promise resolved when done. - */ - getAttemptCount(scormId: number, options: AddonModScormGetAttemptCountOptions = {}): Promise { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = options.userId || site.getUserId(); - - const result: AddonModScormAttemptCountResult = { - lastAttempt: { - number: 0, - offline: false - } - }, - promises = []; - - promises.push(this.getAttemptCountOnline(scormId, options).then((count) => { - - // Calculate numbers of online attempts. - result.online = []; - - for (let i = 1; i <= count; i++) { - result.online.push(i); - } - - // Calculate last attempt. - if (count > result.lastAttempt.number) { - result.lastAttempt.number = count; - result.lastAttempt.offline = false; - } - })); - - promises.push(this.scormOfflineProvider.getAttempts(scormId, options.siteId, userId).then((attempts) => { - // Get only attempt numbers. - result.offline = attempts.map((entry) => { - // Calculate last attempt. We use >= to prioritize offline events if an attempt is both online and offline. - if (entry.attempt >= result.lastAttempt.number) { - result.lastAttempt.number = entry.attempt; - result.lastAttempt.offline = true; - } - - return entry.attempt; - }); - })); - - return Promise.all(promises).then(() => { - let total = result.online.length; - - result.offline.forEach((attempt) => { - // Check if this attempt also exists in online, it might have been copied to local. - if (result.online.indexOf(attempt) == -1) { - total++; - } - }); - - result.total = total; - - return result; - }); - }); - } - - /** - * Get cache key for SCORM attempt count WS calls. - * - * @param scormId SCORM ID. - * @param userId User ID. If not defined, current user. - * @return Cache key. - */ - protected getAttemptCountCacheKey(scormId: number, userId: number): string { - return this.ROOT_CACHE_KEY + 'attemptcount:' + scormId + ':' + userId; - } - - /** - * Get the number of attempts done by a user in the given SCORM in online. - * - * @param scormId SCORM ID. - * @param options Other options. - * @return Promise resolved when the attempt count is retrieved. - */ - getAttemptCountOnline(scormId: number, options: AddonModScormGetAttemptCountOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = options.userId || site.getUserId(); - - const params = { - scormid: scormId, - userid: userId, - ignoremissingcompletion: options.ignoreMissing ? 1 : 0, - }; - const preSets = { - cacheKey: this.getAttemptCountCacheKey(scormId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModScormProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_scorm_get_scorm_attempt_count', params, preSets).then((response) => { - if (response && typeof response.attemptscount != 'undefined') { - return response.attemptscount; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get the grade for a certain SCORM and attempt. - * Based on Moodle's scorm_grade_user_attempt. - * - * @param scorm SCORM. - * @param attempt Attempt number. - * @param offline Whether the attempt is offline. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the grade. If the attempt hasn't reported grade/completion, it will be -1. - */ - getAttemptGrade(scorm: any, attempt: number, offline?: boolean, siteId?: string): Promise { - const attemptScore = { - scos: 0, - values: 0, - max: 0, - sum: 0 - }; - - // Get the user data and use it to calculate the grade. - return this.getScormUserData(scorm.id, attempt, {offline, cmId: scorm.coursemodule, siteId}).then((data) => { - for (const scoId in data) { - const sco = data[scoId], - userData = sco.userdata; - - if (userData.status == 'completed' || userData.status == 'passed') { - attemptScore.scos++; - } - - if (userData.score_raw || (typeof scorm.scormtype != 'undefined' && - scorm.scormtype == 'sco' && typeof userData.score_raw != 'undefined')) { - - const scoreRaw = parseFloat(userData.score_raw); - attemptScore.values++; - attemptScore.sum += scoreRaw; - attemptScore.max = Math.max(scoreRaw, attemptScore.max); - } - } - - let score = 0; - - switch (scorm.grademethod) { - case AddonModScormProvider.GRADEHIGHEST: - score = attemptScore.max; - break; - - case AddonModScormProvider.GRADEAVERAGE: - if (attemptScore.values > 0) { - score = attemptScore.sum / attemptScore.values; - } else { - score = 0; - } - break; - - case AddonModScormProvider.GRADESUM: - score = attemptScore.sum; - break; - - case AddonModScormProvider.GRADESCOES: - score = attemptScore.scos; - break; - - default: - score = attemptScore.max; // Remote Learner GRADEHIGHEST is default. - } - - return score; - }); - } - - /** - * Get the list of a organizations defined in a SCORM package. - * - * @param scormId SCORM ID. - * @param options Other options. - * @return Promise resolved with the list of organizations. - */ - getOrganizations(scormId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.getScos(scormId, options).then((scos) => { - const organizations = []; - - scos.forEach((sco) => { - // Is an organization entry? - if (sco.organization == '' && sco.parent == '/' && sco.scormtype == '') { - organizations.push({ - identifier: sco.identifier, - title: sco.title, - sortorder: sco.sortorder - }); - } - }); - - return organizations; - }); - } - - /** - * Get the organization Toc any - * - * @param scormId SCORM ID. - * @param attempt The attempt number (to populate SCO track data). - * @param options Other options. - * @return Promise resolved with the toc object. - */ - getOrganizationToc(scormId: number, attempt: number, options: AddonModScormGetScosWithDataOptions = {}): Promise { - - return this.getScosWithData(scormId, attempt, options).then((scos) => { - const map = {}, - rootScos = []; - - scos.forEach((sco, index) => { - sco.children = []; - map[sco.identifier] = index; - - if (sco.parent !== '/') { - if (sco.parent == options.organization) { - // It's a root SCO, add it to the root array. - rootScos.push(sco); - } else { - // Add this sco to the parent. - scos[map[sco.parent]].children.push(sco); - } - } - }); - - return rootScos; - }); - } - - /** - * Get the package URL of a given SCORM. - * - * @param scorm SCORM. - * @return Package URL. - */ - getPackageUrl(scorm: any): string { - if (scorm.packageurl) { - return scorm.packageurl; - } - if (scorm.reference) { - return scorm.reference; - } - - return ''; - } - - /** - * Get the user data for a certain SCORM and attempt. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param options Other options. - * @return Promise resolved when the user data is retrieved. - */ - getScormUserData(scormId: number, attempt: number, options: AddonModScormGetUserDataOptions = {}): Promise { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - if (options.offline) { - // Get SCOs if not provided. - const promise = options.scos ? Promise.resolve(options.scos) : this.getScos(scormId, options); - - return promise.then((scos) => { - return this.scormOfflineProvider.getScormUserData(scormId, attempt, scos, options.siteId); - }); - } else { - return this.getScormUserDataOnline(scormId, attempt, options); - } - } - - /** - * Get cache key for SCORM user data WS calls. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @return Cache key. - */ - protected getScormUserDataCacheKey(scormId: number, attempt: number): string { - return this.getScormUserDataCommonCacheKey(scormId) + ':' + attempt; - } - - /** - * Get common cache key for SCORM user data WS calls. - * - * @param scormId SCORM ID. - * @return Cache key. - */ - protected getScormUserDataCommonCacheKey(scormId: number): string { - return this.ROOT_CACHE_KEY + 'userdata:' + scormId; - } - - /** - * Get the user data for a certain SCORM and attempt in online. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param options Other options. - * @return Promise resolved when the user data is retrieved. - */ - getScormUserDataOnline(scormId: number, attempt: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - scormid: scormId, - attempt: attempt, - }; - const preSets = { - cacheKey: this.getScormUserDataCacheKey(scormId, attempt), - component: AddonModScormProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_scorm_get_scorm_user_data', params, preSets).then((response) => { - if (response && response.data) { - // Format the response. - const data = {}; - - response.data.forEach((sco) => { - sco.defaultdata = this.utils.objectToKeyValueMap(sco.defaultdata, 'element', 'value'); - sco.userdata = this.utils.objectToKeyValueMap(sco.userdata, 'element', 'value'); - - data[sco.scoid] = sco; - }); - - return data; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get SCORM scos WS calls. - * - * @param scormId SCORM ID. - * @return Cache key. - */ - protected getScosCacheKey(scormId: number): string { - return this.ROOT_CACHE_KEY + 'scos:' + scormId; - } - - /** - * Retrieves the list of SCO objects for a given SCORM and organization. - * - * @param scormId SCORM ID. - * @param options Other options. - * @return Promise resolved with a list of SCO. - */ - getScos(scormId: number, options: AddonModScormOrganizationOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - return this.sitesProvider.getSite(options.siteId).then((site) => { - - // Don't send the organization to the WS, we'll filter them locally. - const params = { - scormid: scormId, - }; - const preSets = { - cacheKey: this.getScosCacheKey(scormId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModScormProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_scorm_get_scorm_scoes', params, preSets).then((response) => { - - if (response && response.scoes) { - if (options.organization) { - // Filter SCOs by organization. - return response.scoes.filter((sco) => { - return sco.organization == options.organization; - }); - } else { - return response.scoes; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Retrieves the list of SCO objects for a given SCORM and organization, including data about - * a certain attempt (status, isvisible, ...). - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param options Other options. - * @return Promise resolved with a list of SCO objects. - */ - getScosWithData(scormId: number, attempt: number, options: AddonModScormGetScosWithDataOptions = {}): Promise { - - // Get organization SCOs. - return this.getScos(scormId, options).then((scos) => { - // Get the track data for all the SCOs in the organization for the given attempt. - // We'll use this data to set SCO data like isvisible, status and so. - const userDataOptions = { - scos, - ...options, // Include all options. - }; - - return this.getScormUserData(scormId, attempt, userDataOptions).then((data) => { - - const trackDataBySCO = {}; - - // First populate trackDataBySCO to index by SCO identifier. - // We want the full list first because it's needed by evalPrerequisites. - scos.forEach((sco) => { - trackDataBySCO[sco.identifier] = data[sco.id].userdata; - }); - - scos.forEach((sco) => { - // Add specific SCO information (related to tracked data). - const scoData = data[sco.id].userdata; - - if (!scoData) { - return; - } - - // Check isvisible attribute. - sco.isvisible = typeof scoData.isvisible == 'undefined' || (scoData.isvisible && scoData.isvisible !== 'false'); - // Check pre-requisites status. - sco.prereq = typeof scoData.prerequisites == 'undefined' || - this.evalPrerequisites(scoData.prerequisites, trackDataBySCO); - // Add status. - sco.status = (typeof scoData.status == 'undefined' || scoData.status === '') ? - 'notattempted' : scoData.status; - // Exit var. - sco.exitvar = typeof scoData.exitvar == 'undefined' ? 'cmi.core.exit' : scoData.exitvar; - sco.exitvalue = scoData[sco.exitvar]; - - // Copy score. - sco.score_raw = scoData.score_raw; - }); - - return scos; - }); - }); - } - - /** - * Given a SCORM and a SCO, returns the full launch URL for the SCO. - * - * @param scorm SCORM. - * @param sco SCO. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the URL. - */ - getScoSrc(scorm: any, sco: any, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Build the launch URL. Moodle web checks SCORM version, we don't need to, it's always SCORM 1.2. - let launchUrl = sco.launch, - parameters; - - if (sco.extradata && sco.extradata.length) { - for (let i = 0; i < sco.extradata.length; i++) { - const entry = sco.extradata[i]; - - if (entry.element == 'parameters') { - parameters = entry.value; - break; - } - } - } - - if (parameters) { - const connector = launchUrl.indexOf('?') > -1 ? '&' : '?'; - if (parameters.charAt(0) == '?') { - parameters = parameters.substr(1); - } - - launchUrl += connector + parameters; - } - - if (this.isExternalLink(launchUrl)) { - // It's an online URL. - return Promise.resolve(launchUrl); - } - - return this.filepoolProvider.getPackageDirUrlByUrl(siteId, scorm.moduleurl).then((dirPath) => { - return this.textUtils.concatenatePaths(dirPath, launchUrl); - }); - } - - /** - * Get the path to the folder where a SCORM is downloaded. - * - * @param moduleUrl Module URL (returned by get_course_contents). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the folder path. - */ - getScormFolder(moduleUrl: string, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.filepoolProvider.getPackageDirPathByUrl(siteId, moduleUrl); - } - - /** - * Gets a list of files to downlaod for a SCORM, using a format similar to module.contents from get_course_contents. - * It will only return one file: the ZIP package. - * - * @param scorm SCORM. - * @return File list. - */ - getScormFileList(scorm: any): any[] { - const files = []; - - if (!this.isScormUnsupported(scorm) && !scorm.warningMessage) { - files.push({ - fileurl: this.getPackageUrl(scorm), - filepath: '/', - filename: scorm.reference, - filesize: scorm.packagesize, - type: 'file', - timemodified: 0 - }); - } - - return files; - } - - /** - * Get the URL and description of the status icon. - * - * @param sco SCO. - * @param incomplete Whether the SCORM is incomplete. - * @return Image URL and description. - */ - getScoStatusIcon(sco: any, incomplete?: boolean): {icon: string, description: string} { - let imageName = '', - descName = '', - suspendedStr = ''; - - const status = sco.status; - - if (sco.isvisible) { - if (this.VALID_STATUSES.indexOf(status) >= 0) { - if (sco.scormtype == 'sco') { - imageName = status; - descName = status; - } else { - imageName = 'asset'; - descName = 'assetlaunched'; - } - - if (!incomplete) { - // Check if SCO is completed or not. If SCORM is incomplete there's no need to check SCO. - incomplete = this.isStatusIncomplete(status); - } - - if (incomplete && sco.exitvalue == 'suspend') { - imageName = 'suspend'; - suspendedStr = ' - ' + this.translate.instant('addon.mod_scorm.suspended'); - } - } else { - incomplete = true; - - if (sco.scormtype == 'sco') { - // Status empty or not valid, use 'notattempted'. - imageName = 'notattempted'; - } else { - imageName = 'asset'; - } - descName = imageName; - } - } - - if (imageName == '') { - imageName = 'notattempted'; - descName = 'notattempted'; - suspendedStr = ''; - } - - sco.incomplete = incomplete; - - return { - icon: AddonModScormProvider.STATUS_TO_ICON[imageName], - description: this.translate.instant('addon.mod_scorm.' + descName) + suspendedStr - }; - } - - /** - * Get cache key for SCORM data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getScormDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'scorm:' + courseId; - } - - /** - * Get a SCORM with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the SCORM is retrieved. - */ - protected getScormByField(courseId: number, key: string, value: any, options: AddonModScormGetScormOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getScormDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModScormProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_scorm_get_scorms_by_courses', params, preSets).then((response) => { - if (response && response.scorms) { - const currentScorm = response.scorms.find((scorm) => { - return scorm[key] == value; - }); - - if (currentScorm) { - // If the SCORM isn't available the WS returns a warning and it doesn't return timeopen and timeclosed. - if (typeof currentScorm.timeopen == 'undefined') { - for (const i in response.warnings) { - const warning = response.warnings[i]; - if (warning.itemid === currentScorm.id) { - currentScorm.warningMessage = warning.message; - break; - } - } - } - - currentScorm.moduleurl = options.moduleUrl; - - return currentScorm; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a SCORM by module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the SCORM is retrieved. - */ - getScorm(courseId: number, cmId: number, options: AddonModScormGetScormOptions = {}): Promise { - return this.getScormByField(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a SCORM by SCORM ID. - * - * @param courseId Course ID. - * @param id SCORM ID. - * @param options Other options. - * @return Promise resolved when the SCORM is retrieved. - */ - getScormById(courseId: number, id: number, options: AddonModScormGetScormOptions = {}): Promise { - return this.getScormByField(courseId, 'id', id, options); - } - - /** - * Get a readable SCORM grade method. - * - * @param scorm SCORM. - * @return Grading method. - */ - getScormGradeMethod(scorm: any): string { - if (scorm.maxattempt == 1) { - switch (parseInt(scorm.grademethod, 10)) { - case AddonModScormProvider.GRADEHIGHEST: - return this.translate.instant('addon.mod_scorm.gradehighest'); - - case AddonModScormProvider.GRADEAVERAGE: - return this.translate.instant('addon.mod_scorm.gradeaverage'); - - case AddonModScormProvider.GRADESUM: - return this.translate.instant('addon.mod_scorm.gradesum'); - - case AddonModScormProvider.GRADESCOES: - return this.translate.instant('addon.mod_scorm.gradescoes'); - default: - return ''; - } - } else { - switch (parseInt(scorm.whatgrade, 10)) { - case AddonModScormProvider.HIGHESTATTEMPT: - return this.translate.instant('addon.mod_scorm.highestattempt'); - - case AddonModScormProvider.AVERAGEATTEMPT: - return this.translate.instant('addon.mod_scorm.averageattempt'); - - case AddonModScormProvider.FIRSTATTEMPT: - return this.translate.instant('addon.mod_scorm.firstattempt'); - - case AddonModScormProvider.LASTATTEMPT: - return this.translate.instant('addon.mod_scorm.lastattempt'); - default: - return ''; - } - } - } - - /** - * Invalidates access information. - * - * @param scormId SCORM ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAccessInformation(scormId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(scormId)); - }); - } - - /** - * Invalidates all the data related to a certain SCORM. - * - * @param scormId SCORM ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateAllScormData(scormId: number, siteId?: string, userId?: number): Promise { - const promises = []; - - promises.push(this.invalidateAttemptCount(scormId, siteId, userId)); - promises.push(this.invalidateScos(scormId, siteId)); - promises.push(this.invalidateScormUserData(scormId, siteId)); - promises.push(this.invalidateAccessInformation(scormId, siteId)); - - return Promise.all(promises); - } - - /** - * Invalidates attempt count. - * - * @param scormId SCORM ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateAttemptCount(scormId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.invalidateWsCacheForKey(this.getAttemptCountCacheKey(scormId, userId)); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string, userId?: number): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.getScorm(courseId, moduleId, {siteId}).then((scorm) => { - const promises = []; - - promises.push(this.invalidateAllScormData(scorm.id, siteId, userId)); - promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModScormProvider.COMPONENT, - moduleId, true)); - - return Promise.all(promises); - }); - } - - /** - * Invalidates SCORM data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateScormData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getScormDataCacheKey(courseId)); - }); - } - - /** - * Invalidates SCORM user data for all attempts. - * - * @param scormId SCORM ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateScormUserData(scormId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getScormUserDataCommonCacheKey(scormId)); - }); - } - - /** - * Invalidates SCORM scos for all organizations. - * - * @param scormId SCORM ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateScos(scormId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getScosCacheKey(scormId)); - }); - } - - /** - * Check if a SCORM's attempt is incomplete. - * - * @param scormId SCORM ID. - * @param attempt Attempt. - * @param options Other options. - * @return Promise resolved with a boolean: true if incomplete, false otherwise. - */ - isAttemptIncomplete(scormId: number, attempt: number, options: AddonModScormOfflineOptions = {}): Promise { - - return this.getScosWithData(scormId, attempt, options).then((scos) => { - - for (const i in scos) { - const sco = scos[i]; - - // Ignore SCOs not visible or without launch URL. - if (sco.isvisible && sco.launch) { - if (this.isStatusIncomplete(sco.status)) { - return true; - } - } - } - - return false; - }); - } - - /** - * Given a launch URL, check if it's a external link. - * Based on Moodle's scorm_external_link. - * - * @param link Link to check. - * @return Whether it's an external link. - */ - protected isExternalLink(link: string): boolean { - link = link.toLowerCase(); - - if (link.match(/^https?:\/\//i) && !CoreUrlUtils.instance.isLocalFileUrl(link)) { - return true; - } else if (link.substr(0, 4) == 'www.') { - return true; - } - - return false; - } - - /** - * Check if the given SCORM is closed. - * - * @param scorm SCORM to check. - * @return Whether the SCORM is closed. - */ - isScormClosed(scorm: any): boolean { - const timeNow = this.timeUtils.timestamp(); - - if (scorm.timeclose > 0 && timeNow > scorm.timeclose) { - return true; - } - - return false; - } - - /** - * Check if the given SCORM is downloadable. - * - * @param scorm SCORM to check. - * @return Whether the SCORM is downloadable. - */ - isScormDownloadable(scorm: any): boolean { - return typeof scorm.protectpackagedownloads != 'undefined' && scorm.protectpackagedownloads === false; - } - - /** - * Check if the given SCORM is open. - * - * @param scorm SCORM to check. - * @return Whether the SCORM is open. - */ - isScormOpen(scorm: any): boolean { - const timeNow = this.timeUtils.timestamp(); - - if (scorm.timeopen > 0 && scorm.timeopen > timeNow) { - return false; - } - - return true; - } - - /** - * Check if a SCORM is unsupported in the app. If it's not, returns the error code to show. - * - * @param scorm SCORM to check. - * @return String with error code if unsupported, undefined if supported. - */ - isScormUnsupported(scorm: any): string { - if (!this.isScormValidVersion(scorm)) { - return 'addon.mod_scorm.errorinvalidversion'; - } else if (!this.isScormDownloadable(scorm)) { - return 'addon.mod_scorm.errornotdownloadable'; - } else if (!this.isValidPackageUrl(this.getPackageUrl(scorm))) { - return 'addon.mod_scorm.errorpackagefile'; - } - } - - /** - * Check if it's a valid SCORM 1.2. - * - * @param scorm SCORM to check. - * @return Whether the SCORM is valid. - */ - isScormValidVersion(scorm: any): boolean { - return scorm.version == 'SCORM_1.2'; - } - - /** - * Check if a SCO status is incomplete. - * - * @param status SCO status. - * @return Whether it's incomplete. - */ - isStatusIncomplete(status: any): boolean { - return !status || status == 'notattempted' || status == 'incomplete' || status == 'browsed'; - } - - /** - * Check if a package URL is valid. - * - * @param packageUrl Package URL. - * @return Whether it's valid. - */ - isValidPackageUrl(packageUrl: string): boolean { - if (!packageUrl) { - return false; - } - if (packageUrl.indexOf('imsmanifest.xml') > -1) { - return false; - } - - return true; - } - - /** - * Report a SCO as being launched. - * - * @param scormId SCORM ID. - * @param scoId SCO ID. - * @param name Name of the SCORM. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logLaunchSco(scormId: number, scoId: number, name?: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - scormid: scormId, - scoid: scoId - }; - - return this.logHelper.logSingle('mod_scorm_launch_sco', params, AddonModScormProvider.COMPONENT, scormId, name, - 'scorm', {scoid: scoId}, siteId); - }); - } - - /** - * Report a SCORM as being viewed. - * - * @param id Module ID. - * @param name Name of the SCORM. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - scormid: id - }; - - return this.logHelper.logSingle('mod_scorm_view_scorm', params, AddonModScormProvider.COMPONENT, id, name, 'scorm', {}, - siteId); - } - - /** - * Saves a SCORM tracking record. - * - * @param scoId Sco ID. - * @param attempt Attempt number. - * @param tracks Tracking data to store. - * @param scorm SCORM. - * @param offline Whether the attempt is offline. - * @param userData User data for this attempt and SCO. If not defined, it will be retrieved from DB. Recommended. - * @return Promise resolved when data is saved. - */ - saveTracks(scoId: number, attempt: number, tracks: any[], scorm: any, offline?: boolean, userData?: any, siteId?: string) - : Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (offline) { - const promise = userData ? Promise.resolve(userData) : - this.getScormUserData(scorm.id, attempt, {offline, cmId: scorm.coursemodule, siteId}); - - return promise.then((userData) => { - return this.scormOfflineProvider.saveTracks(scorm, scoId, attempt, tracks, userData, siteId); - }); - } else { - return this.saveTracksOnline(scorm.id, scoId, attempt, tracks, siteId).then(() => { - // Tracks have been saved, update cached user data. - this.updateUserDataAfterSave(scorm.id, attempt, tracks, {cmId: scorm.coursemodule, siteId}); - - this.eventsProvider.trigger(AddonModScormProvider.DATA_SENT_EVENT, { - scormId: scorm.id, - scoId: scoId, - attempt: attempt - }, this.sitesProvider.getCurrentSiteId()); - }); - } - } - - /** - * Saves a SCORM tracking record. - * - * @param scormId SCORM ID. - * @param scoId Sco ID. - * @param attempt Attempt number. - * @param tracks Tracking data. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is saved. - */ - saveTracksOnline(scormId: number, scoId: number, attempt: number, tracks: any[], siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - if (!tracks || !tracks.length) { - return Promise.resolve(); // Nothing to save. - } - - const params = { - scoid: scoId, - attempt: attempt, - tracks: tracks - }; - - this.syncProvider.blockOperation(AddonModScormProvider.COMPONENT, scormId, 'saveTracksOnline', site.id); - - return site.write('mod_scorm_insert_scorm_tracks', params).then((response) => { - if (response && response.trackids) { - return response.trackids; - } - - return Promise.reject(null); - }).finally(() => { - this.syncProvider.unblockOperation(AddonModScormProvider.COMPONENT, scormId, 'saveTracksOnline', site.id); - }); - }); - } - - /** - * Saves a SCORM tracking record using a synchronous call. - * Please use this function only if synchronous is a must. It's recommended to use saveTracks. - * - * @param scoId Sco ID. - * @param attempt Attempt number. - * @param tracks Tracking data to store. - * @param scorm SCORM. - * @param offline Whether the attempt is offline. - * @param userData User data for this attempt and SCO. Required if offline=true. - * @return In online returns true if data is inserted, false otherwise. - * In offline returns true if data to insert is valid, false otherwise. True doesn't mean that the - * data has been stored, this function can return true but the insertion can still fail somehow. - */ - saveTracksSync(scoId: number, attempt: number, tracks: any[], scorm: any, offline?: boolean, userData?: any): boolean { - if (offline) { - return this.scormOfflineProvider.saveTracksSync(scorm, scoId, attempt, tracks, userData); - } else { - const success = this.saveTracksSyncOnline(scoId, attempt, tracks); - - if (success) { - // Tracks have been saved, update cached user data. - this.updateUserDataAfterSave(scorm.id, attempt, tracks, {cmId: scorm.coursemodule}); - - this.eventsProvider.trigger(AddonModScormProvider.DATA_SENT_EVENT, { - scormId: scorm.id, - scoId: scoId, - attempt: attempt - }, this.sitesProvider.getCurrentSiteId()); - } - - return success; - } - } - - /** - * Saves a SCORM tracking record using a synchronous call. - * Please use this function only if synchronous is a must. It's recommended to use saveTracksOnline. - * - * @param scoId Sco ID. - * @param attempt Attempt number. - * @param tracks Tracking data. - * @return True if success, false otherwise. - */ - saveTracksSyncOnline(scoId: number, attempt: number, tracks: any[]): boolean { - const params = { - scoid: scoId, - attempt: attempt, - tracks: tracks - }, - currentSite = this.sitesProvider.getCurrentSite(), - preSets = { - siteUrl: currentSite.getURL(), - wsToken: currentSite.getToken() - }; - let wsFunction = 'mod_scorm_insert_scorm_tracks', - response; - - if (!tracks || !tracks.length) { - return true; // Nothing to save. - } - - // Check if the method is available, use a prefixed version if possible. - if (!currentSite.wsAvailable(wsFunction, false)) { - if (currentSite.wsAvailable(CoreConstants.WS_PREFIX + wsFunction, false)) { - wsFunction = CoreConstants.WS_PREFIX + wsFunction; - } else { - this.logger.error('WS function "' + wsFunction + '" is not available, even in compatibility mode.'); - - return false; - } - } - - response = this.wsProvider.syncCall(wsFunction, params, preSets); - if (response && !response.error && response.trackids) { - return true; - } - - return false; - } - - /** - * Check if the SCORM main file should be downloaded. - * This function should only be called if the SCORM can be downloaded (not downloaded or outdated). - * - * @param scorm SCORM to check. - * @param isOutdated True if package outdated, false if not downloaded, undefined to calculate it. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if it should be downloaded, false otherwise. - */ - shouldDownloadMainFile(scorm: any, isOutdated?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const component = AddonModScormProvider.COMPONENT; - - if (typeof isOutdated == 'undefined') { - - // Calculate if it's outdated. - return this.filepoolProvider.getPackageData(siteId, component, scorm.coursemodule).then((data) => { - const isOutdated = data.status == CoreConstants.OUTDATED || - (data.status == CoreConstants.DOWNLOADING && data.previous == CoreConstants.OUTDATED); - - // Package needs to be downloaded if it's not outdated (not downloaded) or if the hash has changed. - return !isOutdated || data.extra != scorm.sha1hash; - }).catch(() => { - // Package not found, not downloaded. - return true; - }); - } else if (isOutdated) { - - // The package is outdated, but maybe the file hasn't changed. - return this.filepoolProvider.getPackageExtra(siteId, component, scorm.coursemodule).then((extra) => { - return scorm.sha1hash != extra; - }).catch(() => { - // Package not found, not downloaded. - return true; - }); - } else { - // Package is not outdated and not downloaded, download the main file. - return Promise.resolve(true); - } - } - - /** - * If needed, updates cached user data after saving tracks in online. - * - * @param scormId SCORM ID. - * @param attempt Attempt number. - * @param tracks Tracking data saved. - * @param options Other options. - * @return Promise resolved when updated. - */ - protected updateUserDataAfterSave(scormId: number, attempt: number, tracks: any[], options: {cmId?: number, siteId?: string}) - : Promise { - if (!tracks || !tracks.length) { - return Promise.resolve(); - } - - // Check if we need to update. We only update if we sent some track with a dot notation. - let needsUpdate = false; - for (let i = 0, len = tracks.length; i < len; i++) { - const track = tracks[i]; - if (track.element && track.element.indexOf('.') > -1) { - needsUpdate = true; - break; - } - } - - if (needsUpdate) { - return this.getScormUserDataOnline(scormId, attempt, { - cmId: options.cmId, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId: options.siteId, - }); - } - - return Promise.resolve(); - } -} - -/** - * Options to pass to get SCORM. - */ -export type AddonModScormGetScormOptions = CoreSitesCommonWSOptions & { - moduleUrl?: string; // Module URL. -}; - -/** - * Common options with an organization ID. - */ -export type AddonModScormOrganizationOptions = CoreCourseCommonModWSOptions & { - organization?: string; // Organization ID. -}; - -/** - * Common options with offline boolean. - */ -export type AddonModScormOfflineOptions = CoreCourseCommonModWSOptions & { - offline?: boolean; // Whether the attempt is offline. -}; - -/** - * Options to pass to getAttemptCount. - */ -export type AddonModScormGetAttemptCountOptions = CoreCourseCommonModWSOptions & { - ignoreMissing?: boolean; // Whether it should ignore attempts that haven't reported a grade/completion. - userId?: number; // User ID. If not defined use site's current user. -}; - -/** - * Options to pass to getScormUserData. - */ -export type AddonModScormGetUserDataOptions = AddonModScormOfflineOptions & { - scos?: any[]; // SCOs returned by getScos. Recommended if offline=true. -}; - -/** - * Options to pass to getScosWithData. - */ -export type AddonModScormGetScosWithDataOptions = AddonModScormOfflineOptions & AddonModScormOrganizationOptions; diff --git a/src/addon/mod/scorm/providers/sync-cron-handler.ts b/src/addon/mod/scorm/providers/sync-cron-handler.ts deleted file mode 100644 index 2f1951ef4..000000000 --- a/src/addon/mod/scorm/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModScormSyncProvider } from './scorm-sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModScormSyncCronHandler implements CoreCronHandler { - name = 'AddonModScormSyncCronHandler'; - - constructor(private scormSync: AddonModScormSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.scormSync.syncAllScorms(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.scormSync.syncInterval; - } -} diff --git a/src/addon/mod/scorm/scorm.module.ts b/src/addon/mod/scorm/scorm.module.ts deleted file mode 100644 index 8676d3769..000000000 --- a/src/addon/mod/scorm/scorm.module.ts +++ /dev/null @@ -1,73 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { AddonModScormProvider } from './providers/scorm'; -import { AddonModScormHelperProvider } from './providers/helper'; -import { AddonModScormOfflineProvider } from './providers/scorm-offline'; -import { AddonModScormModuleHandler } from './providers/module-handler'; -import { AddonModScormPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModScormSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModScormIndexLinkHandler } from './providers/index-link-handler'; -import { AddonModScormGradeLinkHandler } from './providers/grade-link-handler'; -import { AddonModScormListLinkHandler } from './providers/list-link-handler'; -import { AddonModScormSyncProvider } from './providers/scorm-sync'; -import { AddonModScormComponentsModule } from './components/components.module'; - -// List of providers (without handlers). -export const ADDON_MOD_SCORM_PROVIDERS: any[] = [ - AddonModScormProvider, - AddonModScormOfflineProvider, - AddonModScormHelperProvider, - AddonModScormSyncProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModScormComponentsModule - ], - providers: [ - AddonModScormProvider, - AddonModScormOfflineProvider, - AddonModScormHelperProvider, - AddonModScormSyncProvider, - AddonModScormModuleHandler, - AddonModScormPrefetchHandler, - AddonModScormSyncCronHandler, - AddonModScormIndexLinkHandler, - AddonModScormGradeLinkHandler, - AddonModScormListLinkHandler - ] -}) -export class AddonModScormModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModScormModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModScormPrefetchHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModScormSyncCronHandler, linksDelegate: CoreContentLinksDelegate, - indexHandler: AddonModScormIndexLinkHandler, gradeHandler: AddonModScormGradeLinkHandler, - listLinkHandler: AddonModScormListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - cronDelegate.register(syncHandler); - linksDelegate.registerHandler(indexHandler); - linksDelegate.registerHandler(gradeHandler); - linksDelegate.registerHandler(listLinkHandler); - } -} diff --git a/src/addon/mod/survey/components/components.module.ts b/src/addon/mod/survey/components/components.module.ts deleted file mode 100644 index 42ba457c4..000000000 --- a/src/addon/mod/survey/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModSurveyIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModSurveyIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModSurveyIndexComponent - ], - entryComponents: [ - AddonModSurveyIndexComponent - ] -}) -export class AddonModSurveyComponentsModule {} diff --git a/src/addon/mod/survey/components/index/addon-mod-survey-index.html b/src/addon/mod/survey/components/index/addon-mod-survey-index.html deleted file mode 100644 index 9b3531df3..000000000 --- a/src/addon/mod/survey/components/index/addon-mod-survey-index.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -

{{ 'addon.mod_survey.surveycompletednograph' | translate }}

- - - {{ 'addon.mod_survey.results' | translate }} - -
- - -
- - {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} -
- - -
- - - -
-

{{ question.text }}

- - - -
{{ 'addon.mod_survey.responses' | translate }}
-
- -
{{ option }}
-
-
-
- -

{{ question.intro }}

-
-
- - - - - - {{question.num}}. {{ question.text }} - - - - - - - - - {{ 'core.choose' | translate }} - {{option}} - - - - - - - - - - - {{question.num}}. {{ question.text }} - - - - {{option}} - - - - - - - - {{question.num}}. {{ question.text }} - - - - - -
- - - - -
- -
diff --git a/src/addon/mod/survey/components/index/index.scss b/src/addon/mod/survey/components/index/index.scss deleted file mode 100644 index e7af8521d..000000000 --- a/src/addon/mod/survey/components/index/index.scss +++ /dev/null @@ -1,30 +0,0 @@ -ion-app.app-root addon-mod-survey-index { - - .label, .label[stacked] { - font-size: initial; - color: $text-color; - } - - .addon-mod_survey-question { - border-top: 1px solid $gray; - } - - ion-grid { - background-color: $white; - @include darkmode() { - background-color: $black; - } - } - - .even { - background-color: $gray-light; - @include darkmode() { - background-color: $gray-darker; - } - } - - .addon-mod_survey-textarea textarea { - height: 100px; - border: 1px solid $gray-dark; - } -} \ No newline at end of file diff --git a/src/addon/mod/survey/components/index/index.ts b/src/addon/mod/survey/components/index/index.ts deleted file mode 100644 index 4c375e42b..000000000 --- a/src/addon/mod/survey/components/index/index.ts +++ /dev/null @@ -1,235 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector } from '@angular/core'; -import { Content } from 'ionic-angular'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreEvents, CoreEventsProvider } from '@providers/events'; -import { AddonModSurveyProvider, AddonModSurveySurvey } from '../../providers/survey'; -import { AddonModSurveyHelperProvider, AddonModSurveyQuestionFormatted } from '../../providers/helper'; -import { AddonModSurveyOfflineProvider } from '../../providers/offline'; -import { AddonModSurveySyncProvider } from '../../providers/sync'; - -/** - * Component that displays a survey. - */ -@Component({ - selector: 'addon-mod-survey-index', - templateUrl: 'addon-mod-survey-index.html', -}) -export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityComponent { - component = AddonModSurveyProvider.COMPONENT; - moduleName = 'survey'; - - survey: AddonModSurveySurvey; - questions: AddonModSurveyQuestionFormatted[]; - answers = {}; - - protected userId: number; - protected syncEventName = AddonModSurveySyncProvider.AUTO_SYNCED; - - constructor( - injector: Injector, - protected surveyProvider: AddonModSurveyProvider, - @Optional() content: Content, - protected surveyHelper: AddonModSurveyHelperProvider, - protected surveyOffline: AddonModSurveyOfflineProvider, - protected surveySync: AddonModSurveySyncProvider, - ) { - super(injector, content); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.userId = this.sitesProvider.getCurrentSiteUserId(); - - this.loadContent(false, true).then(() => { - this.surveyProvider.logView(this.survey.id, this.survey.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.surveyProvider.invalidateSurveyData(this.courseId)); - if (this.survey) { - promises.push(this.surveyProvider.invalidateQuestions(this.survey.id)); - } - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (this.survey && syncEventData.surveyId == this.survey.id && syncEventData.userId == this.userId) { - return true; - } - - return false; - } - - /** - * Download survey contents. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - return this.surveyProvider.getSurvey(this.courseId, this.module.id).then((survey) => { - this.survey = survey; - - this.description = survey.intro; - this.dataRetrieved.emit(survey); - - if (sync) { - // Try to synchronize the survey. - return this.syncActivity(showErrors).then((answersSent) => { - if (answersSent) { - // Answers were sent, update the survey. - return this.surveyProvider.getSurvey(this.courseId, this.module.id).then((survey) => { - this.survey = survey; - }); - } - }); - } - }).then(() => { - // Check if there are answers stored in offline. - return this.surveyOffline.hasAnswers(this.survey.id); - }).then((hasOffline) => { - this.hasOffline = this.survey.surveydone ? false : hasOffline; - - if (!this.survey.surveydone && !this.hasOffline) { - return this.fetchQuestions(); - } - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Convenience function to get survey questions. - * - * @return Promise resolved when done. - */ - protected fetchQuestions(): Promise { - return this.surveyProvider.getQuestions(this.survey.id, {cmId: this.module.id}).then((questions) => { - this.questions = this.surveyHelper.formatQuestions(questions); - - // Init answers object. - this.questions.forEach((q) => { - if (q.name) { - const isTextArea = q.multiArray && q.multiArray.length === 0 && q.type === 0; - this.answers[q.name] = q.required ? -1 : (isTextArea ? '' : '0'); - } - - if (q.multiArray && !q.multiArray.length && q.parent === 0 && q.type > 0) { - // Options shown in a select. Remove all HTML. - q.optionsArray = q.optionsArray.map((option) => { - return this.textUtils.cleanTags(option); - }); - } - }); - }); - } - - /** - * Check if answers are valid to be submitted. - * - * @return If answers are valid - */ - isValidResponse(): boolean { - return !this.questions.some((question) => { - return question.required && question.name && - (question.type === 0 ? this.answers[question.name] == '' : parseInt(this.answers[question.name], 10) === -1); - }); - } - - /** - * Save options selected. - */ - submit(): void { - this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => { - const answers = [], - modal = this.domUtils.showModalLoading('core.sending', true); - - for (const x in this.answers) { - answers.push({ - key: x, - value: this.answers[x] - }); - } - - return this.surveyProvider.submitAnswers(this.survey.id, this.survey.name, this.courseId, answers).then((online) => { - CoreEvents.instance.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: this.moduleName }); - - if (online && this.isPrefetched()) { - // The survey is downloaded, update the data. - return this.surveySync.prefetchAfterUpdate(this.module, this.courseId).then(() => { - // Update the view. - this.showLoadingAndFetch(false, false); - }).catch((error) => { - // Prefetch failed, refresh the data. - return this.showLoadingAndRefresh(false); - }); - } else { - // Not downloaded, refresh the data. - return this.showLoadingAndRefresh(false); - } - }).finally(() => { - modal.dismiss(); - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_survey.cannotsubmitsurvey', true); - }); - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.surveySync.syncSurvey(this.survey.id, this.userId); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return If suceed or not. - */ - protected hasSyncSucceed(result: any): boolean { - return result.answersSent; - } -} diff --git a/src/addon/mod/survey/lang/en.json b/src/addon/mod/survey/lang/en.json deleted file mode 100644 index 9ccaff870..000000000 --- a/src/addon/mod/survey/lang/en.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "cannotsubmitsurvey": "Sorry, there was a problem submitting your survey. Please try again.", - "errorgetsurvey": "Error getting survey data.", - "ifoundthat": "I found that", - "ipreferthat": "I prefer that", - "modulenameplural": "Surveys", - "responses": "Responses", - "results": "Results", - "surveycompletednograph": "You have completed this survey." -} \ No newline at end of file diff --git a/src/addon/mod/survey/pages/index/index.html b/src/addon/mod/survey/pages/index/index.html deleted file mode 100644 index 0c9f1c027..000000000 --- a/src/addon/mod/survey/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/survey/pages/index/index.module.ts b/src/addon/mod/survey/pages/index/index.module.ts deleted file mode 100644 index 8cf33a393..000000000 --- a/src/addon/mod/survey/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModSurveyComponentsModule } from '../../components/components.module'; -import { AddonModSurveyIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModSurveyIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModSurveyComponentsModule, - IonicPageModule.forChild(AddonModSurveyIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModSurveyIndexPageModule {} diff --git a/src/addon/mod/survey/pages/index/index.ts b/src/addon/mod/survey/pages/index/index.ts deleted file mode 100644 index f5cda2208..000000000 --- a/src/addon/mod/survey/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModSurveyIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a survey. - */ -@IonicPage({ segment: 'addon-mod-survey-index' }) -@Component({ - selector: 'page-addon-mod-survey-index', - templateUrl: 'index.html', -}) -export class AddonModSurveyIndexPage { - @ViewChild(AddonModSurveyIndexComponent) surveyComponent: AddonModSurveyIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the survey instance. - * - * @param survey Survey instance. - */ - updateData(survey: any): void { - this.title = survey.name || this.title; - } -} diff --git a/src/addon/mod/survey/providers/helper.ts b/src/addon/mod/survey/providers/helper.ts deleted file mode 100644 index dc2836b01..000000000 --- a/src/addon/mod/survey/providers/helper.ts +++ /dev/null @@ -1,138 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModSurveyQuestion } from './survey'; - -/** - * Service that provides helper functions for surveys. - */ -@Injectable() -export class AddonModSurveyHelperProvider { - - constructor(private translate: TranslateService) { } - - /** - * Turns a string with values separated by commas into an array. - * - * @param value Value to convert. - * @return Array. - */ - protected commaStringToArray(value: string | string[]): string[] { - if (typeof value == 'string') { - if (value.length > 0) { - return value.split(','); - } - - return []; - } - - return value; - } - - /** - * Gets the parent questions and puts them in an object: ID -> question. - * - * @param questions Questions. - * @return Object with parent questions. - */ - protected getParentQuestions(questions: AddonModSurveyQuestion[]): {[id: number]: AddonModSurveyQuestion} { - const parents = {}; - - questions.forEach((question) => { - if (question.parent === 0) { - parents[question.id] = question; - } - }); - - return parents; - } - - /** - * Format a questions list, turning "multi" and "options" strings into arrays and adding the properties - * 'num' and 'name'. - * - * @param questions Questions. - * @return Promise resolved with the formatted questions. - */ - formatQuestions(questions: AddonModSurveyQuestion[]): AddonModSurveyQuestionFormatted[] { - - const strIPreferThat = this.translate.instant('addon.mod_survey.ipreferthat'), - strIFoundThat = this.translate.instant('addon.mod_survey.ifoundthat'), - strChoose = this.translate.instant('core.choose'), - formatted: AddonModSurveyQuestionFormatted[] = [], - parents = this.getParentQuestions(questions); - - let num = 1; - - questions.forEach((question) => { - // Copy the object to prevent modifying the original. - const q1: AddonModSurveyQuestionFormatted = Object.assign({}, question), - parent = parents[q1.parent]; - - // Turn multi and options into arrays. - q1.multiArray = this.commaStringToArray(q1.multi); - q1.optionsArray = this.commaStringToArray(q1.options); - - if (parent) { - // It's a sub-question. - q1.required = true; - - if (parent.type === 1 || parent.type === 2) { - // One answer question. Set its name and add it to the returned array. - q1.name = 'q' + (parent.type == 2 ? 'P' : '') + q1.id; - q1.num = num++; - } else { - // Two answers per question (COLLES P&A). We'll add two questions. - const q2 = Object.assign({}, q1); - - q1.text = strIPreferThat + ' ' + q1.text; - q1.name = 'qP' + q1.id; - q1.num = num++; - formatted.push(q1); - - q2.text = strIFoundThat + ' ' + q2.text; - q2.name = 'q' + q1.id; - q2.num = num++; - formatted.push(q2); - - return; - } - } else if (q1.multiArray && q1.multiArray.length === 0) { - // It's a single question. - q1.name = 'q' + q1.id; - q1.num = num++; - if (q1.type > 0) { // Add "choose" option since this question is not required. - q1.optionsArray.unshift(strChoose); - } - } - - formatted.push(q1); - }); - - return formatted; - } -} - -/** - * Survey question with some calculated data. - */ -export type AddonModSurveyQuestionFormatted = AddonModSurveyQuestion & { - required?: boolean; // Calculated in the app. Whether the question is required. - name?: string; // Calculated in the app. The name of the question. - num?: number; // Calculated in the app. Number of the question. - multiArray?: string[]; // Subquestions ids, converted to an array. - optionsArray?: string[]; // Question options, converted to an array. -}; diff --git a/src/addon/mod/survey/providers/link-handler.ts b/src/addon/mod/survey/providers/link-handler.ts deleted file mode 100644 index fb986a5c6..000000000 --- a/src/addon/mod/survey/providers/link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to survey. - */ -@Injectable() -export class AddonModSurveyLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModSurveyLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModSurvey', 'survey'); - } -} diff --git a/src/addon/mod/survey/providers/list-link-handler.ts b/src/addon/mod/survey/providers/list-link-handler.ts deleted file mode 100644 index 509349dbd..000000000 --- a/src/addon/mod/survey/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to survey list page. - */ -@Injectable() -export class AddonModSurveyListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModSurveyListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModSurvey', 'survey'); - } -} diff --git a/src/addon/mod/survey/providers/module-handler.ts b/src/addon/mod/survey/providers/module-handler.ts deleted file mode 100644 index 318e084f5..000000000 --- a/src/addon/mod/survey/providers/module-handler.ts +++ /dev/null @@ -1,88 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModSurveyIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support survey modules. - */ -@Injectable() -export class AddonModSurveyModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModSurvey'; - modName = 'survey'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_survey-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModSurveyIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModSurveyIndexComponent; - } -} diff --git a/src/addon/mod/survey/providers/offline.ts b/src/addon/mod/survey/providers/offline.ts deleted file mode 100644 index 511f1db7c..000000000 --- a/src/addon/mod/survey/providers/offline.ts +++ /dev/null @@ -1,183 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Service to handle Offline survey. - */ -@Injectable() -export class AddonModSurveyOfflineProvider { - - protected logger; - - // Variables for database. - static SURVEY_TABLE = 'addon_mod_survey_answers'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModSurveyOfflineProvider', - version: 1, - tables: [ - { - name: AddonModSurveyOfflineProvider.SURVEY_TABLE, - columns: [ - { - name: 'surveyid', - type: 'INTEGER' - }, - { - name: 'name', - type: 'TEXT' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'userid', - type: 'INTEGER' - }, - { - name: 'answers', - type: 'TEXT' - }, - { - name: 'timecreated', - type: 'INTEGER' - } - ], - primaryKeys: ['surveyid', 'userid'] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider) { - this.logger = logger.getInstance('AddonModSurveyOfflineProvider'); - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete a survey answers. - * - * @param surveyId Survey ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User the answers belong to. If not defined, current user in site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteSurveyAnswers(surveyId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().deleteRecords(AddonModSurveyOfflineProvider.SURVEY_TABLE, {surveyid: surveyId, userid: userId}); - }); - } - - /** - * Get all the stored data from all the surveys. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with answers. - */ - getAllData(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getAllRecords(AddonModSurveyOfflineProvider.SURVEY_TABLE).then((entries) => { - entries.forEach((entry) => { - entry.answers = this.textUtils.parseJSON(entry.answers); - }); - - return entries; - }); - }); - } - - /** - * Get a survey stored answers. - * - * @param surveyId Survey ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User the answers belong to. If not defined, current user in site. - * @return Promise resolved with the answers. - */ - getSurveyAnswers(surveyId: number, siteId?: string, userId?: number): Promise { - return this.getSurveyData(surveyId, siteId, userId).then((entry) => { - return entry.answers || []; - }).catch(() => { - return []; - }); - } - - /** - * Get a survey stored data. - * - * @param surveyId Survey ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User the answers belong to. If not defined, current user in site. - * @return Promise resolved with the data. - */ - getSurveyData(surveyId: number, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - return site.getDb().getRecord(AddonModSurveyOfflineProvider.SURVEY_TABLE, {surveyid: surveyId, userid: userId}); - }).then((entry) => { - entry.answers = this.textUtils.parseJSON(entry.answers); - - return entry; - }); - } - - /** - * Check if there are offline answers to send. - * - * @param surveyId Survey ID. - * @param siteId Site ID. If not defined, current site. - * @param userId User the answers belong to. If not defined, current user in site. - * @return Promise resolved with boolean: true if has offline answers, false otherwise. - */ - hasAnswers(surveyId: number, siteId?: string, userId?: number): Promise { - return this.getSurveyAnswers(surveyId, siteId, userId).then((answers) => { - return !!answers.length; - }); - } - - /** - * Save answers to be sent later. - * - * @param surveyId Survey ID. - * @param name Survey name. - * @param courseId Course ID the survey belongs to. - * @param answers Answers. - * @param siteId Site ID. If not defined, current site. - * @param userId User the answers belong to. If not defined, current user in site. - * @return Promise resolved if stored, rejected if failure. - */ - saveAnswers(surveyId: number, name: string, courseId: number, answers: any[], siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - - const entry = { - surveyid: surveyId, - name: name, - courseid: courseId, - userid: userId, - answers: JSON.stringify(answers), - timecreated: new Date().getTime() - }; - - return site.getDb().insertRecord(AddonModSurveyOfflineProvider.SURVEY_TABLE, entry); - }); - } -} diff --git a/src/addon/mod/survey/providers/prefetch-handler.ts b/src/addon/mod/survey/providers/prefetch-handler.ts deleted file mode 100644 index 076da7258..000000000 --- a/src/addon/mod/survey/providers/prefetch-handler.ts +++ /dev/null @@ -1,166 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { AddonModSurveyProvider } from './survey'; -import { AddonModSurveySyncProvider } from './sync'; -import { AddonModSurveyHelperProvider } from './helper'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch surveys. - */ -@Injectable() -export class AddonModSurveyPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModSurvey'; - modName = 'survey'; - component = AddonModSurveyProvider.COMPONENT; - updatesNames = /^configuration$|^.*files$|^answers$/; - - protected syncProvider: AddonModSurveySyncProvider; // It will be injected later to prevent circular dependencies. - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected surveyProvider: AddonModSurveyProvider, - protected surveyHelper: AddonModSurveyHelperProvider, - protected injector: Injector) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Returns survey intro files. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @return Promise resolved with list of intro files. - */ - getIntroFiles(module: any, courseId: number): Promise { - return this.surveyProvider.getSurvey(courseId, module.id).catch(() => { - // Not found, return undefined so module description is used. - }).then((survey) => { - return this.getIntroFilesFromInstance(module, survey); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.surveyProvider.invalidateContent(moduleId, courseId); - } - - /** - * Invalidate WS calls needed to determine module status. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - return this.surveyProvider.invalidateSurveyData(courseId); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchSurvey.bind(this)); - } - - /** - * Prefetch a survey. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchSurvey(module: any, courseId: number, single: boolean, siteId: string): Promise { - return this.surveyProvider.getSurvey(courseId, module.id, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((survey) => { - const promises = [], - files = this.getIntroFilesFromInstance(module, survey); - - // Prefetch files. - promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, AddonModSurveyProvider.COMPONENT, module.id)); - - // If survey isn't answered, prefetch the questions. - if (!survey.surveydone) { - promises.push(this.surveyProvider.getQuestions(survey.id, { - cmId: module.id, - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - })); - } - - return Promise.all(promises); - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - if (!this.syncProvider) { - this.syncProvider = this.injector.get(AddonModSurveySyncProvider); - } - - return this.syncProvider.syncSurvey(module.instance, undefined, siteId); - } -} diff --git a/src/addon/mod/survey/providers/survey.ts b/src/addon/mod/survey/providers/survey.ts deleted file mode 100644 index 9e05db3f4..000000000 --- a/src/addon/mod/survey/providers/survey.ts +++ /dev/null @@ -1,356 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModSurveyOfflineProvider } from './offline'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some features for surveys. - */ -@Injectable() -export class AddonModSurveyProvider { - static COMPONENT = 'mmaModSurvey'; - - protected ROOT_CACHE_KEY = 'mmaModSurvey:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, - private filepoolProvider: CoreFilepoolProvider, private utils: CoreUtilsProvider, - private surveyOffline: AddonModSurveyOfflineProvider, private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModSurveyProvider'); - } - - /** - * Get a survey's questions. - * - * @param surveyId Survey ID. - * @param options Other options. - * @return Promise resolved when the questions are retrieved. - */ - getQuestions(surveyId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - surveyid: surveyId, - }; - const preSets = { - cacheKey: this.getQuestionsCacheKey(surveyId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModSurveyProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_survey_get_questions', params, preSets) - .then((response: AddonModSurveyGetQuestionsResult): any => { - - if (response.questions) { - return response.questions; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get cache key for survey questions WS calls. - * - * @param surveyId Survey ID. - * @return Cache key. - */ - protected getQuestionsCacheKey(surveyId: number): string { - return this.ROOT_CACHE_KEY + 'questions:' + surveyId; - } - - /** - * Get cache key for survey data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getSurveyCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'survey:' + courseId; - } - - /** - * Get a survey data. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the survey is retrieved. - */ - protected getSurveyDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getSurveyCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModSurveyProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_survey_get_surveys_by_courses', params, preSets) - .then((response: AddonModSurveyGetSurveysByCoursesResult): any => { - - if (response && response.surveys) { - const currentSurvey = response.surveys.find((survey) => { - return survey[key] == value; - }); - if (currentSurvey) { - return currentSurvey; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a survey by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the survey is retrieved. - */ - getSurvey(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getSurveyDataByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a survey by ID. - * - * @param courseId Course ID. - * @param id Survey ID. - * @param options Other options. - * @return Promise resolved when the survey is retrieved. - */ - getSurveyById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getSurveyDataByKey(courseId, 'id', id, options); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.getSurvey(courseId, moduleId).then((survey) => { - const ps = []; - - // Do not invalidate activity data before getting activity info, we need it! - ps.push(this.invalidateSurveyData(courseId, siteId)); - ps.push(this.invalidateQuestions(survey.id, siteId)); - - return Promise.all(ps); - })); - - promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModSurveyProvider.COMPONENT, moduleId)); - - return this.utils.allPromises(promises); - } - - /** - * Invalidates survey questions. - * - * @param surveyId Survey ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateQuestions(surveyId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getQuestionsCacheKey(surveyId)); - }); - } - - /** - * Invalidates survey data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSurveyData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getSurveyCacheKey(courseId)); - }); - } - - /** - * Report the survey as being viewed. - * - * @param id Module ID. - * @param name Name of the assign. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - surveyid: id - }; - - return this.logHelper.logSingle('mod_survey_view_survey', params, AddonModSurveyProvider.COMPONENT, id, name, 'survey', - {}, siteId); - } - - /** - * Send survey answers. If cannot send them to Moodle, they'll be stored in offline to be sent later. - * - * @param surveyId Survey ID. - * @param name Survey name. - * @param courseId Course ID the survey belongs to. - * @param answers Answers. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean if success: true if answers were sent to server, - * false if stored in device. - */ - submitAnswers(surveyId: number, name: string, courseId: number, answers: any[], siteId?: string): Promise { - // Convenience function to store a survey to be synchronized later. - const storeOffline = (): Promise => { - return this.surveyOffline.saveAnswers(surveyId, name, courseId, answers, siteId).then(() => { - return false; - }); - }; - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (!this.appProvider.isOnline()) { - // App is offline, store the message. - return storeOffline(); - } - - // If there's already answers to be sent to the server, discard it first. - return this.surveyOffline.deleteSurveyAnswers(surveyId, siteId).then(() => { - // Device is online, try to send them to server. - return this.submitAnswersOnline(surveyId, answers, siteId).then(() => { - return true; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // It's a WebService error, the user cannot send the message so don't store it. - return Promise.reject(error); - } - - // Couldn't connect to server, store in offline. - return storeOffline(); - }); - }); - } - - /** - * Send survey answers to Moodle. - * - * @param surveyId Survey ID. - * @param answers Answers. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when answers are successfully submitted. - */ - submitAnswersOnline(surveyId: number, answers: any[], siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - surveyid: surveyId, - answers: answers - }; - - return site.write('mod_survey_submit_answers', params).then((response: AddonModSurveySubmitAnswersResult) => { - if (!response.status) { - return Promise.reject(this.utils.createFakeWSError('')); - } - }); - }); - } -} - -/** - * Survey returned by WS mod_survey_get_surveys_by_courses. - */ -export type AddonModSurveySurvey = { - id: number; // Survey id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // Survey name. - intro?: string; // The Survey intro. - introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles?: CoreWSExternalFile[]; // @since 3.2. - template?: number; // Survey type. - days?: number; // Days. - questions?: string; // Question ids. - surveydone?: number; // Did I finish the survey?. - timecreated?: number; // Time of creation. - timemodified?: number; // Time of last modification. - section?: number; // Course section id. - visible?: number; // Visible. - groupmode?: number; // Group mode. - groupingid?: number; // Group id. -}; - -/** - * Survey question. - */ -export type AddonModSurveyQuestion = { - id: number; // Question id. - text: string; // Question text. - shorttext: string; // Question short text. - multi: string; // Subquestions ids. - intro: string; // The question intro. - type: number; // Question type. - options: string; // Question options. - parent: number; // Parent question (for subquestions). -}; - -/** - * Result of WS mod_survey_get_questions. - */ -export type AddonModSurveyGetQuestionsResult = { - questions: AddonModSurveyQuestion[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_survey_get_surveys_by_courses. - */ -export type AddonModSurveyGetSurveysByCoursesResult = { - surveys: AddonModSurveySurvey[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS mod_survey_submit_answers. - */ -export type AddonModSurveySubmitAnswersResult = { - status: boolean; // Status: true if success. - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/survey/providers/sync-cron-handler.ts b/src/addon/mod/survey/providers/sync-cron-handler.ts deleted file mode 100644 index b06f7c549..000000000 --- a/src/addon/mod/survey/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModSurveySyncProvider } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModSurveySyncCronHandler implements CoreCronHandler { - name = 'AddonModSurveySyncCronHandler'; - - constructor(private surveySync: AddonModSurveySyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.surveySync.syncAllSurveys(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.surveySync.syncInterval; - } -} diff --git a/src/addon/mod/survey/providers/sync.ts b/src/addon/mod/survey/providers/sync.ts deleted file mode 100644 index 2f43c7f28..000000000 --- a/src/addon/mod/survey/providers/sync.ts +++ /dev/null @@ -1,225 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreAppProvider } from '@providers/app'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { AddonModSurveyOfflineProvider } from './offline'; -import { AddonModSurveyProvider } from './survey'; -import { CoreEventsProvider } from '@providers/events'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreCourseActivitySyncBaseProvider } from '@core/course/classes/activity-sync'; -import { CoreSyncProvider } from '@providers/sync'; -import { AddonModSurveyPrefetchHandler } from './prefetch-handler'; - -/** - * Service to sync surveys. - */ -@Injectable() -export class AddonModSurveySyncProvider extends CoreCourseActivitySyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_survey_autom_synced'; - protected componentTranslate: string; - - constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, - syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService, - private courseProvider: CoreCourseProvider, private surveyOffline: AddonModSurveyOfflineProvider, - private eventsProvider: CoreEventsProvider, private surveyProvider: AddonModSurveyProvider, - private utils: CoreUtilsProvider, timeUtils: CoreTimeUtilsProvider, private logHelper: CoreCourseLogHelperProvider, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModSurveyPrefetchHandler) { - - super('AddonModSurveySyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils, prefetchDelegate, prefetchHandler); - - this.componentTranslate = courseProvider.translateModuleName('survey'); - } - - /** - * Get the ID of a survey sync. - * - * @param surveyId Survey ID. - * @param userId User the answers belong to. - * @return Sync ID. - * @protected - */ - getSyncId(surveyId: number, userId: number): string { - return surveyId + '#' + userId; - } - - /** - * Try to synchronize all the surveys in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllSurveys(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all surveys', this.syncAllSurveysFunc.bind(this), [force], siteId); - } - - /** - * Sync all pending surveys on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllSurveysFunc(siteId: string, force?: boolean): Promise { - // Get all survey answers pending to be sent in the site. - return this.surveyOffline.getAllData(siteId).then((entries) => { - // Sync all surveys. - const promises = entries.map((entry) => { - const promise = force ? this.syncSurvey(entry.surveyid, entry.userid, siteId) : - this.syncSurveyIfNeeded(entry.surveyid, entry.userid, siteId); - - return promise.then((result) => { - if (result && result.answersSent) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModSurveySyncProvider.AUTO_SYNCED, { - surveyId: entry.surveyid, - userId: entry.userid, - warnings: result.warnings - }, siteId); - } - }); - }); - - return Promise.all(promises); - }); - } - - /** - * Sync a survey only if a certain time has passed since the last time. - * - * @param surveyId Survey ID. - * @param userId User the answers belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the survey is synced or if it doesn't need to be synced. - */ - syncSurveyIfNeeded(surveyId: number, userId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const syncId = this.getSyncId(surveyId, userId); - - return this.isSyncNeeded(syncId, siteId).then((needed) => { - if (needed) { - return this.syncSurvey(surveyId, userId, siteId); - } - }); - } - - /** - * Synchronize a survey. - * - * @param surveyId Survey ID. - * @param userId User the answers belong to. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncSurvey(surveyId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); - siteId = site.getId(); - - const syncId = this.getSyncId(surveyId, userId); - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this survey and user, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - this.logger.debug(`Try to sync survey '${surveyId}' for user '${userId}'`); - - let courseId; - const result = { - warnings: [], - answersSent: false - }; - - // Sync offline logs. - const syncPromise = this.logHelper.syncIfNeeded(AddonModSurveyProvider.COMPONENT, surveyId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Get answers to be sent. - return this.surveyOffline.getSurveyData(surveyId, siteId, userId).catch(() => { - // No offline data found, return empty object. - return {}; - }); - }).then((data) => { - if (!data.answers || !data.answers.length) { - // Nothing to sync. - return; - } - - if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - courseId = data.courseid; - - // Send the answers. - return this.surveyProvider.submitAnswersOnline(surveyId, data.answers, siteId).then(() => { - result.answersSent = true; - - // Answers sent, delete them. - return this.surveyOffline.deleteSurveyAnswers(surveyId, siteId, userId); - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - - // The WebService has thrown an error, this means that answers cannot be submitted. Delete them. - result.answersSent = true; - - return this.surveyOffline.deleteSurveyAnswers(surveyId, siteId, userId).then(() => { - // Answers deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); - } - - // Couldn't connect to server, reject. - return Promise.reject(error); - }); - }).then(() => { - if (courseId) { - return this.surveyProvider.invalidateSurveyData(courseId, siteId).then(() => { - // Data has been sent to server, update survey data. - return this.courseProvider.getModuleBasicInfoByInstance(surveyId, 'survey', siteId).then((module) => { - return this.prefetchAfterUpdate(module, courseId, undefined, siteId); - }); - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(syncId, siteId); - }).then(() => { - return result; - }); - - return this.addOngoingSync(syncId, syncPromise, siteId); - }); - } - -} diff --git a/src/addon/mod/survey/survey.module.ts b/src/addon/mod/survey/survey.module.ts deleted file mode 100644 index 53829079e..000000000 --- a/src/addon/mod/survey/survey.module.ts +++ /dev/null @@ -1,70 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { AddonModSurveyComponentsModule } from './components/components.module'; -import { AddonModSurveyModuleHandler } from './providers/module-handler'; -import { AddonModSurveyProvider } from './providers/survey'; -import { AddonModSurveyLinkHandler } from './providers/link-handler'; -import { AddonModSurveyListLinkHandler } from './providers/list-link-handler'; -import { AddonModSurveyHelperProvider } from './providers/helper'; -import { AddonModSurveyPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModSurveySyncProvider } from './providers/sync'; -import { AddonModSurveySyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModSurveyOfflineProvider } from './providers/offline'; - -// List of providers (without handlers). -export const ADDON_MOD_SURVEY_PROVIDERS: any[] = [ - AddonModSurveyProvider, - AddonModSurveyHelperProvider, - AddonModSurveySyncProvider, - AddonModSurveyOfflineProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModSurveyComponentsModule - ], - providers: [ - AddonModSurveyProvider, - AddonModSurveyHelperProvider, - AddonModSurveySyncProvider, - AddonModSurveyOfflineProvider, - AddonModSurveyModuleHandler, - AddonModSurveyPrefetchHandler, - AddonModSurveyLinkHandler, - AddonModSurveyListLinkHandler, - AddonModSurveySyncCronHandler - ] -}) -export class AddonModSurveyModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModSurveyModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModSurveyPrefetchHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModSurveyLinkHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModSurveySyncCronHandler, - listLinkHandler: AddonModSurveyListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - cronDelegate.register(syncHandler); - } -} diff --git a/src/addon/mod/url/components/components.module.ts b/src/addon/mod/url/components/components.module.ts deleted file mode 100644 index 524d2b391..000000000 --- a/src/addon/mod/url/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModUrlIndexComponent } from './index/index'; - -@NgModule({ - declarations: [ - AddonModUrlIndexComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule - ], - providers: [ - ], - exports: [ - AddonModUrlIndexComponent - ], - entryComponents: [ - AddonModUrlIndexComponent - ] -}) -export class AddonModUrlComponentsModule {} diff --git a/src/addon/mod/url/components/index/addon-mod-url-index.html b/src/addon/mod/url/components/index/addon-mod-url-index.html deleted file mode 100644 index 20eba29f7..000000000 --- a/src/addon/mod/url/components/index/addon-mod-url-index.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - -
- -
- - - - - - - - -

{{ 'addon.mod_url.pointingtourl' | translate }}

-

{{ url }}

-
- - - - {{ 'addon.mod_url.accessurl' | translate }} - - -
-
diff --git a/src/addon/mod/url/components/index/index.scss b/src/addon/mod/url/components/index/index.scss deleted file mode 100644 index a9d107124..000000000 --- a/src/addon/mod/url/components/index/index.scss +++ /dev/null @@ -1,6 +0,0 @@ -ion-app.app-root addon-mod-url-index { - - .addon-mod_url-embedded-url { - height: 100%; - } -} diff --git a/src/addon/mod/url/components/index/index.ts b/src/addon/mod/url/components/index/index.ts deleted file mode 100644 index 1275b3796..000000000 --- a/src/addon/mod/url/components/index/index.ts +++ /dev/null @@ -1,192 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Injector } from '@angular/core'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreCourseModuleMainResourceComponent } from '@core/course/classes/main-resource-component'; -import { AddonModUrlProvider } from '../../providers/url'; -import { AddonModUrlHelperProvider } from '../../providers/helper'; -import { CoreConstants } from '@core/constants'; - -/** - * Component that displays a url. - */ -@Component({ - selector: 'addon-mod-url-index', - templateUrl: 'addon-mod-url-index.html', -}) -export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceComponent { - component = AddonModUrlProvider.COMPONENT; - - canGetUrl: boolean; - url: string; - name: string; - shouldEmbed = false; - shouldIframe = false; - isImage = false; - isAudio = false; - isVideo = false; - isOther = false; - mimetype: string; - displayDescription = true; - - constructor(injector: Injector, - protected urlProvider: AddonModUrlProvider, - protected urlHelper: AddonModUrlHelperProvider, - protected mimeUtils: CoreMimetypeUtilsProvider) { - super(injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.canGetUrl = this.urlProvider.isGetUrlWSAvailable(); - - this.loadContent().then(() => { - if ((this.shouldIframe || (this.shouldEmbed && this.isOther)) || - (!this.shouldIframe && (!this.shouldEmbed || !this.isOther))) { - this.logView(); - } - }); - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - return this.urlProvider.invalidateContent(this.module.id, this.courseId); - } - - /** - * Download url contents. - * - * @param refresh Whether we're refreshing data. - * @return Promise resolved when done. - */ - protected fetchContent(refresh?: boolean): Promise { - let canGetUrl = this.canGetUrl, - mod, - url, - promise; - - // Fetch the module data. - if (canGetUrl) { - promise = this.urlProvider.getUrl(this.courseId, this.module.id); - } else { - promise = Promise.reject(null); - } - - return promise.catch(() => { - canGetUrl = false; - - // Fallback in case is not prefetched or not available. - return this.courseProvider.getModule(this.module.id, this.courseId, undefined, false, false, undefined, 'url'); - }).then((urlData) => { - url = urlData; - - this.name = url.name || this.module.name; - this.description = url.intro || url.description; - this.dataRetrieved.emit(url); - - if (canGetUrl && url.displayoptions) { - const unserialized = this.textUtils.unserialize(url.displayoptions); - this.displayDescription = typeof unserialized.printintro == 'undefined' || !!unserialized.printintro; - } - - if (!canGetUrl) { - mod = url; - - if (!url.contents.length) { - // If the data was cached maybe we don't have contents. Reject. - return Promise.reject(null); - } - } else { - mod = this.module; - - // Try to load module contents, it's needed to get the URL with parameters. - return this.courseProvider.loadModuleContents(mod, this.courseId, undefined, false, refresh, undefined, 'url'); - } - }).then(() => { - // Always use the URL from the module because it already includes the parameters. - this.url = mod.contents && mod.contents[0] && mod.contents[0].fileurl ? mod.contents[0].fileurl : undefined; - - if (canGetUrl) { - return this.calculateDisplayOptions(url); - } - }); - } - - /** - * Calculate the display options to determine how the URL should be rendered. - * - * @param url Object with the URL data. - * @return Promise resolved when done. - */ - protected calculateDisplayOptions(url: any): Promise { - const displayType = this.urlProvider.getFinalDisplayType(url); - - this.shouldEmbed = displayType == CoreConstants.RESOURCELIB_DISPLAY_EMBED; - this.shouldIframe = displayType == CoreConstants.RESOURCELIB_DISPLAY_FRAME; - - if (this.shouldEmbed) { - const extension = this.mimeUtils.guessExtensionFromUrl(url.externalurl); - - this.mimetype = this.mimeUtils.getMimeType(extension); - this.isImage = this.mimeUtils.isExtensionInGroup(extension, ['web_image']); - this.isAudio = this.mimeUtils.isExtensionInGroup(extension, ['web_audio']); - this.isVideo = this.mimeUtils.isExtensionInGroup(extension, ['web_video']); - this.isOther = !this.isImage && !this.isAudio && !this.isVideo; - } - - if (this.shouldIframe || (this.shouldEmbed && !this.isImage && !this.isAudio && !this.isVideo)) { - // Will be displayed in an iframe. Check if we need to auto-login. - const currentSite = this.sitesProvider.getCurrentSite(); - - if (currentSite && currentSite.containsUrl(this.url)) { - // Format the URL to add auto-login. - return currentSite.getAutoLoginUrl(this.url, false).then((url) => { - this.url = url; - }); - } - } - - return Promise.resolve(); - } - - /** - * Log view into the site and checks module completion. - * - * @return Promise resolved when done. - */ - protected logView(): Promise { - return this.urlProvider.logView(this.module.instance, this.module.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - } - - /** - * Opens a file. - */ - go(): void { - this.logView(); - this.urlHelper.open(this.url); - } -} diff --git a/src/addon/mod/url/lang/en.json b/src/addon/mod/url/lang/en.json deleted file mode 100644 index 18eff8be5..000000000 --- a/src/addon/mod/url/lang/en.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "accessurl": "Access the URL", - "modulenameplural": "URLs", - "pointingtourl": "URL that the resource points to." -} \ No newline at end of file diff --git a/src/addon/mod/url/pages/index/index.html b/src/addon/mod/url/pages/index/index.html deleted file mode 100644 index 013e94e05..000000000 --- a/src/addon/mod/url/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/url/pages/index/index.module.ts b/src/addon/mod/url/pages/index/index.module.ts deleted file mode 100644 index 80291e456..000000000 --- a/src/addon/mod/url/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModUrlComponentsModule } from '../../components/components.module'; -import { AddonModUrlIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModUrlIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModUrlComponentsModule, - IonicPageModule.forChild(AddonModUrlIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModUrlIndexPageModule {} diff --git a/src/addon/mod/url/pages/index/index.ts b/src/addon/mod/url/pages/index/index.ts deleted file mode 100644 index 7320af4a2..000000000 --- a/src/addon/mod/url/pages/index/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModUrlIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a url. - */ -@IonicPage({ segment: 'addon-mod-url-index' }) -@Component({ - selector: 'page-addon-mod-url-index', - templateUrl: 'index.html', -}) -export class AddonModUrlIndexPage { - @ViewChild(AddonModUrlIndexComponent) urlComponent: AddonModUrlIndexComponent; - - title: string; - module: any; - courseId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.title = this.module.name; - } - - /** - * Update some data based on the url instance. - * - * @param url Url instance. - */ - updateData(url: any): void { - this.title = url.name || this.title; - } -} diff --git a/src/addon/mod/url/providers/helper.ts b/src/addon/mod/url/providers/helper.ts deleted file mode 100644 index 55d4c5c49..000000000 --- a/src/addon/mod/url/providers/helper.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; - -/** - * Service that provides helper functions for urls. - */ -@Injectable() -export class AddonModUrlHelperProvider { - - constructor(private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider, - private contentLinksHelper: CoreContentLinksHelperProvider) { } - - /** - * Opens a URL. - * - * @param url The URL to go to. - */ - open(url: string): void { - const modal = this.domUtils.showModalLoading(); - this.contentLinksHelper.handleLink(url, undefined, undefined, true, true).then((treated) => { - if (!treated) { - return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); - } - }).finally(() => { - modal.dismiss(); - }); - } -} diff --git a/src/addon/mod/url/providers/link-handler.ts b/src/addon/mod/url/providers/link-handler.ts deleted file mode 100644 index 9d43f2975..000000000 --- a/src/addon/mod/url/providers/link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to url. - */ -@Injectable() -export class AddonModUrlLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModUrlLinkHandler'; - useModNameToGetModule = true; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModUrl', 'url', 'u'); - } -} diff --git a/src/addon/mod/url/providers/list-link-handler.ts b/src/addon/mod/url/providers/list-link-handler.ts deleted file mode 100644 index 850b3a623..000000000 --- a/src/addon/mod/url/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to URL list page. - */ -@Injectable() -export class AddonModUrlListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModUrlListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModUrl', 'url'); - } -} diff --git a/src/addon/mod/url/providers/module-handler.ts b/src/addon/mod/url/providers/module-handler.ts deleted file mode 100644 index 7d15e61fc..000000000 --- a/src/addon/mod/url/providers/module-handler.ts +++ /dev/null @@ -1,184 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModUrlIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonModUrlProvider, AddonModUrlUrl } from './url'; -import { AddonModUrlHelperProvider } from './helper'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support url modules. - */ -@Injectable() -export class AddonModUrlModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModUrl'; - modName = 'url'; - - supportedFeatures = { - [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, - [CoreConstants.FEATURE_GROUPS]: false, - [CoreConstants.FEATURE_GROUPINGS]: false, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true - }; - - constructor(private courseProvider: CoreCourseProvider, private urlProvider: AddonModUrlProvider, - private urlHelper: AddonModUrlHelperProvider, private domUtils: CoreDomUtilsProvider, - private contentLinksHelper: CoreContentLinksHelperProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - // tslint:disable: no-this-assignment - const handler = this; - const handlerData = { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_url-handler', - showDownloadButton: false, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const modal = handler.domUtils.showModalLoading(); - - // First of all, make sure module contents are loaded. - handler.courseProvider.loadModuleContents(module, courseId, undefined, false, false, undefined, handler.modName) - .then(() => { - // Check if the URL can be handled by the app. If so, always open it directly. - return handler.contentLinksHelper.canHandleLink(module.contents[0].fileurl, courseId, undefined, true); - }).then((canHandle) => { - if (canHandle) { - // URL handled by the app, open it directly. - return true; - } - - // Not handled by the app, check the display type. - if (handler.urlProvider.isGetUrlWSAvailable()) { - return handler.urlProvider.getUrl(courseId, module.id).catch(() => { - // Ignore errors. - return undefined; - }).then((url: AddonModUrlUrl) => { - const displayType = handler.urlProvider.getFinalDisplayType(url); - - return displayType == CoreConstants.RESOURCELIB_DISPLAY_OPEN || - displayType == CoreConstants.RESOURCELIB_DISPLAY_POPUP; - }); - } else { - return false; - } - - }).then((shouldOpen) => { - if (shouldOpen) { - handler.openUrl(module, courseId); - } else { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModUrlIndexPage', pageParams, options); - } - }).finally(() => { - modal.dismiss(); - }); - }, - buttons: [ { - hidden: true, // Hide it until we calculate if it should be displayed or not. - icon: 'link', - label: 'core.openmodinbrowser', - action: (event: Event, navCtrl: NavController, module: any, courseId: number): void => { - handler.openUrl(module, courseId); - } - } ] - }; - - this.hideLinkButton(module, courseId).then((hideButton) => { - handlerData.buttons[0].hidden = hideButton; - - if (module.contents && module.contents[0]) { - // Calculate the icon to use. - handlerData.icon = this.urlProvider.guessIcon(module.contents[0].fileurl) || - this.courseProvider.getModuleIconSrc(this.modName, module.modicon); - } - }); - - return handlerData; - } - - /** - * Returns if contents are loaded to show link button. - * - * @param module The module object. - * @param courseId The course ID. - * @return Resolved when done. - */ - protected hideLinkButton(module: any, courseId: number): Promise { - return this.courseProvider.loadModuleContents(module, courseId, undefined, false, false, undefined, this.modName) - .then(() => { - return !(module.contents && module.contents[0] && module.contents[0].fileurl); - }).catch(() => { - // Module contents could not be loaded, most probably device is offline. - return true; - }); - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModUrlIndexComponent; - } - - /** - * Open the URL. - * - * @param module The module object. - * @param courseId The course ID. - */ - protected openUrl(module: any, courseId: number): void { - this.urlProvider.logView(module.instance, module.name).then(() => { - this.courseProvider.checkModuleCompletion(courseId, module.completiondata); - }).catch(() => { - // Ignore errors. - }); - this.urlHelper.open(module.contents[0].fileurl); - } -} diff --git a/src/addon/mod/url/providers/prefetch-handler.ts b/src/addon/mod/url/providers/prefetch-handler.ts deleted file mode 100644 index 0063f8a25..000000000 --- a/src/addon/mod/url/providers/prefetch-handler.ts +++ /dev/null @@ -1,98 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; -import { AddonModUrlProvider } from './url'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch URLs. URLs cannot be prefetched, but the handler will be used to invalidate some data on course PTR. - */ -@Injectable() -export class AddonModUrlPrefetchHandler extends CoreCourseResourcePrefetchHandlerBase { - name = 'AddonModUrl'; - modName = 'url'; - component = AddonModUrlProvider.COMPONENT; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Download the module. - * - * @param module The module object returned by WS. - * @param courseId Course ID. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when all content is downloaded. - */ - download(module: any, courseId: number, dirPath?: string): Promise { - return Promise.resolve(); - } - - /** - * Invalidate WS calls needed to determine module status (usually, to check if module is downloadable). - * It doesn't need to invalidate check updates. It should NOT invalidate files nor all the prefetched data. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - return this.courseProvider.invalidateModule(module.id, undefined, this.modName); - } - - /** - * Check if a module can be downloaded. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Whether the module can be downloaded. The promise should never be rejected. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - return false; // URLs aren't downloadable. - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return Promise.resolve(); - } -} diff --git a/src/addon/mod/url/providers/url.ts b/src/addon/mod/url/providers/url.ts deleted file mode 100644 index 5019cf89b..000000000 --- a/src/addon/mod/url/providers/url.ts +++ /dev/null @@ -1,270 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreConstants } from '@core/constants'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; - -/** - * Service that provides some features for urls. - */ -@Injectable() -export class AddonModUrlProvider { - static COMPONENT = 'mmaModUrl'; - - protected ROOT_CACHE_KEY = 'mmaModUrl:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, - private utils: CoreUtilsProvider, private mimeUtils: CoreMimetypeUtilsProvider, - private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModUrlProvider'); - } - - /** - * Get the final display type for a certain URL. Based on Moodle's url_get_final_display_type. - * - * @param url URL data. - * @return Final display type. - */ - getFinalDisplayType(url: AddonModUrlUrl): number { - if (!url) { - return -1; - } - - const extension = this.mimeUtils.guessExtensionFromUrl(url.externalurl); - - // PDFs can be embedded in web, but not in the Mobile app. - if (url.display == CoreConstants.RESOURCELIB_DISPLAY_EMBED && extension == 'pdf') { - return CoreConstants.RESOURCELIB_DISPLAY_DOWNLOAD; - } - - if (url.display != CoreConstants.RESOURCELIB_DISPLAY_AUTO) { - return url.display; - } - - // Detect links to local moodle pages. - const currentSite = this.sitesProvider.getCurrentSite(); - if (currentSite && currentSite.containsUrl(url.externalurl)) { - if (url.externalurl.indexOf('file.php') == -1 && url.externalurl.indexOf('.php') != -1) { - // Most probably our moodle page with navigation. - return CoreConstants.RESOURCELIB_DISPLAY_OPEN; - } - } - - const download = ['application/zip', 'application/x-tar', 'application/g-zip', 'application/pdf', 'text/html']; - let mimetype = this.mimeUtils.getMimeType(extension); - - if (url.externalurl.indexOf('.php') != -1 || url.externalurl.substr(-1) === '/' || - (url.externalurl.indexOf('//') != -1 && url.externalurl.match(/\//g).length == 2)) { - // Seems to be a web, use HTML mimetype. - mimetype = 'text/html'; - } - - if (download.indexOf(mimetype) != -1) { - return CoreConstants.RESOURCELIB_DISPLAY_DOWNLOAD; - } - - if (this.mimeUtils.canBeEmbedded(extension)) { - return CoreConstants.RESOURCELIB_DISPLAY_EMBED; - } - - // Let the browser deal with it somehow. - return CoreConstants.RESOURCELIB_DISPLAY_OPEN; - } - - /** - * Get cache key for url data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getUrlCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'url:' + courseId; - } - - /** - * Get a url data. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the url is retrieved. - */ - protected getUrlDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) - : Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getUrlCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModUrlProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_url_get_urls_by_courses', params, preSets) - .then((response: AddonModUrlGetUrlsByCoursesResult): any => { - - if (response && response.urls) { - const currentUrl = response.urls.find((url) => { - return url[key] == value; - }); - if (currentUrl) { - return currentUrl; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a url by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the url is retrieved. - */ - getUrl(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getUrlDataByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Guess the icon for a certain URL. Based on Moodle's url_guess_icon. - * - * @param url URL to check. - * @return Icon, empty if it should use the default icon. - */ - guessIcon(url: string): string { - url = url || ''; - - const matches = url.match(/\//g), - extension = this.mimeUtils.getFileExtension(url); - - if (!matches || matches.length < 3 || url.substr(-1) === '/' || extension == 'php') { - // Use default icon. - return ''; - } - - const icon = this.mimeUtils.getFileIcon(url); - - // We do not want to return those icon types, the module icon is more appropriate. - if (icon === this.mimeUtils.getFileIconForType('unknown') || icon === this.mimeUtils.getFileIconForType('html')) { - return ''; - } - - return icon; - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId Course ID of the module. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = []; - - promises.push(this.invalidateUrlData(courseId, siteId)); - promises.push(this.courseProvider.invalidateModule(moduleId, siteId, 'url')); - - return this.utils.allPromises(promises); - } - - /** - * Invalidates url data. - * - * @param courseid Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateUrlData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getUrlCacheKey(courseId)); - }); - } - - /** - * Returns whether or not getUrl WS available or not. - * - * @return If WS is abalaible. - * @since 3.3 - */ - isGetUrlWSAvailable(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('mod_url_get_urls_by_courses'); - } - - /** - * Report the url as being viewed. - * - * @param id Module ID. - * @param name Name of the assign. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - urlid: id - }; - - return this.logHelper.logSingle('mod_url_view_url', params, AddonModUrlProvider.COMPONENT, id, name, 'url', {}, siteId); - } -} - -/** - * URL returnd by mod_url_get_urls_by_courses. - */ -export type AddonModUrlUrl = { - id: number; // Module id. - coursemodule: number; // Course module id. - course: number; // Course id. - name: string; // URL name. - intro: string; // Summary. - introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - introfiles: CoreWSExternalFile[]; - externalurl: string; // External URL. - display: number; // How to display the url. - displayoptions: string; // Display options (width, height). - parameters: string; // Parameters to append to the URL. - timemodified: number; // Last time the url was modified. - section: number; // Course section id. - visible: number; // Module visibility. - groupmode: number; // Group mode. - groupingid: number; // Grouping id. -}; - -/** - * Result of WS mod_url_get_urls_by_courses. - */ -export type AddonModUrlGetUrlsByCoursesResult = { - urls: AddonModUrlUrl[]; - warnings?: CoreWSExternalWarning[]; -}; diff --git a/src/addon/mod/url/url.module.ts b/src/addon/mod/url/url.module.ts deleted file mode 100644 index 1066155af..000000000 --- a/src/addon/mod/url/url.module.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModUrlComponentsModule } from './components/components.module'; -import { AddonModUrlModuleHandler } from './providers/module-handler'; -import { AddonModUrlProvider } from './providers/url'; -import { AddonModUrlLinkHandler } from './providers/link-handler'; -import { AddonModUrlListLinkHandler } from './providers/list-link-handler'; -import { AddonModUrlPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModUrlHelperProvider } from './providers/helper'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; - -// List of providers (without handlers). -export const ADDON_MOD_URL_PROVIDERS: any[] = [ - AddonModUrlProvider, - AddonModUrlHelperProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModUrlComponentsModule - ], - providers: [ - AddonModUrlProvider, - AddonModUrlHelperProvider, - AddonModUrlModuleHandler, - AddonModUrlLinkHandler, - AddonModUrlListLinkHandler, - AddonModUrlPrefetchHandler - ] -}) -export class AddonModUrlModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, - moduleHandler: AddonModUrlModuleHandler, - contentLinksDelegate: CoreContentLinksDelegate, - linkHandler: AddonModUrlLinkHandler, - listLinkHandler: AddonModUrlListLinkHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, - prefetchHandler: AddonModUrlPrefetchHandler) { - - moduleDelegate.registerHandler(moduleHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - prefetchDelegate.registerHandler(prefetchHandler); - } -} diff --git a/src/addon/mod/wiki/components/components.module.ts b/src/addon/mod/wiki/components/components.module.ts deleted file mode 100644 index 9735928c2..000000000 --- a/src/addon/mod/wiki/components/components.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { CoreTagComponentsModule } from '@core/tag/components/components.module'; -import { AddonModWikiIndexComponent } from './index/index'; -import { AddonModWikiSubwikiPickerComponent } from './subwiki-picker/subwiki-picker'; - -@NgModule({ - declarations: [ - AddonModWikiIndexComponent, - AddonModWikiSubwikiPickerComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreCourseComponentsModule, - CoreTagComponentsModule - ], - providers: [ - ], - exports: [ - AddonModWikiIndexComponent, - AddonModWikiSubwikiPickerComponent - ], - entryComponents: [ - AddonModWikiIndexComponent, - AddonModWikiSubwikiPickerComponent - ] -}) -export class AddonModWikiComponentsModule {} diff --git a/src/addon/mod/wiki/components/index/addon-mod-wiki-index.html b/src/addon/mod/wiki/components/index/addon-mod-wiki-index.html deleted file mode 100644 index f9a3da731..000000000 --- a/src/addon/mod/wiki/components/index/addon-mod-wiki-index.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - {{ 'core.hasdatatosync' | translate:{$a: pageStr} }} - {{ 'core.hasdatatosync' | translate:{$a: moduleName} }} -
- - -
- - {{ pageWarning }} -
-
-
-
- - -
- -
- {{ 'core.tag.tags' | translate }}: - -
-
-
- - - - diff --git a/src/addon/mod/wiki/components/index/index.scss b/src/addon/mod/wiki/components/index/index.scss deleted file mode 100644 index a19821b4f..000000000 --- a/src/addon/mod/wiki/components/index/index.scss +++ /dev/null @@ -1,83 +0,0 @@ -$addon-mod-wiki-toc-level-padding: 12px !default; -$addon-mod-wiki-newentry-link-color: $red !default; -$addon-mod-wiki-toc-title-color: $gray-darker !default; -$addon-mod-wiki-toc-border-color: $gray-dark !default; -$addon-mod-wiki-toc-background-color: $gray-light !default; - -$addon-mod-wiki-dark-toc-title-color: $white !default; -$addon-mod-wiki-dark-toc-border-color: $gray-dark !default; -$addon-mod-wiki-dark-toc-background-color: $gray-darker !default; -$addon-mod-wiki-dark-newentry-link-color: $red-light !default; - -ion-app.app-root addon-mod-wiki-index { - background-color: $white; - @include darkmode() { - background-color: $black; - } - - .addon-mod_wiki-page-content { - background-color: $white; - border-top: 1px solid $gray; - @include safe-area-padding-horizontal(10px, 10px); - @include darkmode() { - background-color: $black; - } - } - - .wiki-toc { - border: 1px solid $addon-mod-wiki-toc-border-color; - background: $addon-mod-wiki-toc-background-color; - p { - color: $text-color !important; - } - a { - color: $link-color; - } - - @include darkmode() { - border: 1px solid $addon-mod-wiki-dark-toc-border-color; - background: $addon-mod-wiki-dark-toc-background-color; - p { - color: $core-dark-text-color !important; - } - a { - color: $core-dark-link-color; - } - } - margin: 16px; - padding: 8px; - } - - .wiki-toc-title { - color: $addon-mod-wiki-toc-title-color !important; - font-size: 1.1em; - font-variant: small-caps; - text-align: center; - } - - .wiki-toc-section { - padding: 0; - margin: 2px 8px; - } - - .wiki-toc-section-2 { - @include padding(null, null, null, $addon-mod-wiki-toc-level-padding); - } - - .wiki-toc-section-3 { - @include padding(null, null, null, $addon-mod-wiki-toc-level-padding * 2); - } - - .wiki_newentry { - color: $addon-mod-wiki-newentry-link-color; - font-style: italic; - @include darkmode() { - color: $addon-mod-wiki-dark-newentry-link-color !important; - } - } - - /* Hide edit section links */ - .addon-mod_wiki-noedit a.wiki_edit_section { - display: none; - } -} diff --git a/src/addon/mod/wiki/components/index/index.ts b/src/addon/mod/wiki/components/index/index.ts deleted file mode 100644 index 1a52936bb..000000000 --- a/src/addon/mod/wiki/components/index/index.ts +++ /dev/null @@ -1,1027 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, Injector, Input } from '@angular/core'; -import { Content, NavController, PopoverController, ViewController, ModalController } from 'ionic-angular'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModWikiProvider, AddonModWikiSubwikiListData } from '../../providers/wiki'; -import { AddonModWikiOfflineProvider } from '../../providers/wiki-offline'; -import { AddonModWikiSyncProvider } from '../../providers/wiki-sync'; -import { AddonModWikiSubwikiPickerComponent } from '../../components/subwiki-picker/subwiki-picker'; -import { CoreTagProvider } from '@core/tag/providers/tag'; - -/** - * Component that displays a wiki entry page. - */ -@Component({ - selector: 'addon-mod-wiki-index', - templateUrl: 'addon-mod-wiki-index.html', -}) -export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComponent { - - @Input() action: string; - @Input() pageId: number; - @Input() pageTitle: string; - @Input() wikiId: number; - @Input() subwikiId: number; - @Input() userId: number; - @Input() groupId: number; - - component = AddonModWikiProvider.COMPONENT; - componentId: number; - moduleName = 'wiki'; - groupWiki = false; - - wiki: any; // The wiki instance. - isMainPage: boolean; // Whether the user is viewing wiki's main page (just entered the wiki). - canEdit = false; // Whether user can edit the page. - pageStr = ''; - pageWarning: string; // Message telling that the page was discarded. - loadedSubwikis: any[] = []; // The loaded subwikis. - pageIsOffline: boolean; // Whether the loaded page is an offline page. - pageContent: string; // Page content to display. - subwikiData: AddonModWikiSubwikiListData = { // Data for the subwiki selector. - subwikiSelected: 0, - userSelected: 0, - groupSelected: 0, - subwikis: [], - count: 0 - }; - tagsEnabled: boolean; - currentPageObj: any; // Object of the current loaded page. - - protected syncEventName = AddonModWikiSyncProvider.AUTO_SYNCED; - protected currentSubwiki: any; // Current selected subwiki. - protected currentPage: number; // Current loaded page ID. - protected subwikiPages: any[]; // List of subwiki pages. - protected newPageObserver: any; // Observer to check for new pages. - protected ignoreManualSyncEvent: boolean; // Whether manual sync event should be ignored. - protected manualSyncObserver: any; // An observer to watch for manual sync events. - protected currentUserId: number; // Current user ID. - protected hasEdited = false; // Whether the user has opened the edit page. - - constructor(injector: Injector, protected wikiProvider: AddonModWikiProvider, @Optional() protected content: Content, - protected wikiOffline: AddonModWikiOfflineProvider, protected wikiSync: AddonModWikiSyncProvider, - protected navCtrl: NavController, protected utils: CoreUtilsProvider, protected groupsProvider: CoreGroupsProvider, - protected userProvider: CoreUserProvider, private popoverCtrl: PopoverController, - private tagProvider: CoreTagProvider, protected modalCtrl: ModalController) { - super(injector, content); - - this.pageStr = this.translate.instant('addon.mod_wiki.wikipage'); - this.tagsEnabled = this.tagProvider.areTagsAvailableInSite(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.currentUserId = this.sitesProvider.getCurrentSiteUserId(); - this.isMainPage = !this.pageId && !this.pageTitle; - this.currentPage = this.pageId; - - this.loadContent(false, true).then(() => { - if (!this.wiki) { - return; - } - - if (!this.pageId) { - this.wikiProvider.logView(this.wiki.id, this.wiki.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch((error) => { - // Ignore errors. - }); - } else { - this.wikiProvider.logPageView(this.pageId, this.wiki.id, this.wiki.name).catch((error) => { - // Ignore errors. - }); - } - }).finally(() => { - if (this.action == 'map') { - this.openMap(); - } - }); - - // Listen for manual sync events. - this.manualSyncObserver = this.eventsProvider.on(AddonModWikiSyncProvider.MANUAL_SYNCED, (data) => { - if (data && this.wiki && data.wikiId == this.wiki.id) { - if (this.ignoreManualSyncEvent) { - // Event needs to be ignored. - this.ignoreManualSyncEvent = false; - - return; - } - - if (this.currentSubwiki) { - this.checkPageCreatedOrDiscarded(data.subwikis[this.currentSubwiki.id]); - } - - if (!this.pageWarning) { - this.showLoadingAndFetch(false, false); - } - } - }, this.siteId); - } - - /** - * Check if the current page was created or discarded. - * - * @param data Data about created and deleted pages. - */ - protected checkPageCreatedOrDiscarded(data: any): void { - if (!this.currentPage && data) { - // This is an offline page. Check if the page was created. - const page = data.created.find((page) => page.title == this.pageTitle); - if (page) { - // Page was created, set the ID so it's retrieved from server. - this.currentPage = page.pageId; - this.pageIsOffline = false; - } else { - // Page not found in created list, check if it was discarded. - const page = data.discarded.find((page) => page.title == this.pageTitle); - if (page) { - // Page discarded, show warning. - this.pageWarning = page.warning; - this.pageContent = ''; - this.pageIsOffline = false; - this.hasOffline = false; - } - } - } - } - - /** - * Get the wiki data. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - - // Get the wiki instance. - let promise; - if (this.module.id) { - promise = this.wikiProvider.getWiki(this.courseId, this.module.id); - } else { - promise = this.wikiProvider.getWikiById(this.courseId, this.wikiId); - } - - return promise.then((wiki) => { - this.wiki = wiki; - - this.dataRetrieved.emit(this.wiki); - - if (sync) { - // Try to synchronize the wiki. - return this.syncActivity(showErrors).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - if (this.pageWarning) { - // Page discarded, stop getting data. - return Promise.reject(null); - } - - // Get module instance if it's empty. - let promise; - if (!this.module.id) { - promise = this.courseProvider.getModule(this.wiki.coursemodule, this.wiki.course, undefined, true); - } else { - promise = Promise.resolve(this.module); - } - - return promise.then((mod) => { - this.module = mod; - - this.description = this.wiki.intro || this.module.description; - this.externalUrl = this.module.url; - this.componentId = this.module.id; - - // Get real groupmode, in case it's forced by the course. - return this.groupsProvider.getActivityGroupInfo(this.wiki.coursemodule).then((groupInfo) => { - return this.fetchSubwikis(this.wiki.id).then(() => { - // Get the subwiki list data from the cache. - const subwikiList = this.wikiProvider.getSubwikiList(this.wiki.id); - - if (!subwikiList) { - // Not found in cache, create a new one. - return this.createSubwikiList(groupInfo.groups); - } - - this.subwikiData.count = subwikiList.count; - this.setSelectedWiki(this.subwikiId, this.userId, this.groupId); - - // If nothing was selected using nav params, use the selected from cache. - if (!this.isAnySubwikiSelected()) { - this.setSelectedWiki(subwikiList.subwikiSelected, subwikiList.userSelected, - subwikiList.groupSelected); - } - - this.subwikiData.subwikis = subwikiList.subwikis; - }); - }).then(() => { - - if (!this.isAnySubwikiSelected() || this.subwikiData.count <= 0) { - return Promise.reject(this.translate.instant('addon.mod_wiki.errornowikiavailable')); - } - }).then(() => { - return this.fetchWikiPage(); - }); - }); - }).finally(() => { - this.fillContextMenu(refresh); - }).catch((error) => { - if (this.pageWarning) { - // Warning is already shown in screen, no need to show a modal. - return; - } - - return Promise.reject(error); - }); - } - - /** - * Get wiki page contents. - * - * @param pageId Page to get. - * @return Promise resolved with the page data. - */ - protected fetchPageContents(pageId: number): Promise { - if (!pageId) { - const title = this.pageTitle || this.wiki.firstpagetitle; - - // No page ID but we received a title. This means we're trying to load an offline page. - return this.wikiOffline.getNewPage(title, this.currentSubwiki.id, this.currentSubwiki.wikiid, - this.currentSubwiki.userid, this.currentSubwiki.groupid).then((offlinePage) => { - - this.pageIsOffline = true; - if (!this.newPageObserver) { - // It's an offline page, listen for new pages event to detect if the user goes to Edit and submits the page. - this.newPageObserver = this.eventsProvider.on(AddonModWikiProvider.PAGE_CREATED_EVENT, (data) => { - if (data.subwikiId == this.currentSubwiki.id && data.pageTitle == title) { - // The page has been submitted. Get the page from the server. - this.currentPage = data.pageId; - - this.showLoadingAndFetch(true, false).then(() => { - if (this.currentPage) { - this.wikiProvider.logPageView(this.currentPage, this.wiki.id, this.wiki.name).catch(() => { - // Ignore errors. - }); - } - }); - - // Stop listening for new page events. - this.newPageObserver.off(); - this.newPageObserver = undefined; - } - }, this.sitesProvider.getCurrentSiteId()); - } - - return offlinePage; - }).catch(() => { - // Page not found, ignore. - }); - } - - this.pageIsOffline = false; - - return this.wikiProvider.getPageContents(pageId, {cmId: this.module.id}); - } - - /** - * Fetch the list of pages of a subwiki. - * - * @param subwiki Subwiki. - */ - protected fetchSubwikiPages(subwiki: any): Promise { - let subwikiPages; - - return this.wikiProvider.getSubwikiPages(subwiki.wikiid, { - groupId: subwiki.groupid, - userId: subwiki.userid, - cmId: this.module.id, - }).then((pages) => { - subwikiPages = pages; - - // If no page specified, search first page. - if (!this.currentPage && !this.pageTitle) { - const firstPage = subwikiPages.find((page) => page.firstpage ); - if (firstPage) { - this.currentPage = firstPage.id; - } - } - - // Now get the offline pages. - return this.wikiOffline.getSubwikiNewPages(subwiki.id, subwiki.wikiid, subwiki.userid, subwiki.groupid); - }).then((offlinePages) => { - - // If no page specified, search page title in the offline pages. - if (!this.currentPage) { - const searchTitle = this.pageTitle ? this.pageTitle : this.wiki.firstpagetitle, - pageExists = offlinePages.some((page) => { - return page.title == searchTitle; - }); - - if (pageExists) { - this.pageTitle = searchTitle; - } - } - - this.subwikiPages = this.wikiProvider.sortPagesByTitle(subwikiPages.concat(offlinePages)); - - // Reject if no currentPage selected from the subwikis given (if no subwikis available, do not reject). - if (!this.currentPage && !this.pageTitle && this.subwikiPages.length > 0) { - return Promise.reject(null); - } - }); - } - - /** - * Get the subwikis. - * - * @param wikiId Wiki ID. - */ - protected fetchSubwikis(wikiId: number): Promise { - return this.wikiProvider.getSubwikis(wikiId, {cmId: this.module.id}).then((subwikis) => { - this.loadedSubwikis = subwikis; - - return this.wikiOffline.subwikisHaveOfflineData(subwikis).then((hasOffline) => { - this.hasOffline = hasOffline; - }); - }); - } - - /** - * Fetch the page to be shown. - * - * @return [description] - */ - protected fetchWikiPage(): Promise { - // Search the current Subwiki. - this.currentSubwiki = this.loadedSubwikis.find((subwiki) => { - return this.isSubwikiSelected(subwiki); - }); - - if (!this.currentSubwiki) { - return Promise.reject(null); - } - - this.setSelectedWiki(this.currentSubwiki.id, this.currentSubwiki.userid, this.currentSubwiki.groupid); - - return this.fetchSubwikiPages(this.currentSubwiki).then(() => { - // Check can edit before to have the value if there's no valid page. - this.canEdit = this.currentSubwiki.canedit; - - return this.fetchPageContents(this.currentPage).then((pageContents) => { - if (pageContents) { - this.dataRetrieved.emit(pageContents.title); - this.setSelectedWiki(pageContents.subwikiid, pageContents.userid, pageContents.groupid); - - this.pageContent = this.replaceEditLinks(pageContents.cachedcontent); - this.canEdit = pageContents.caneditpage; - this.currentPageObj = pageContents; - } - }); - }); - } - - /** - * Get the wiki home view. If cannot determine or it's current view, return undefined. - * - * @return The view controller of the home view - */ - protected getWikiHomeView(): ViewController { - - if (!this.wiki.id) { - return; - } - - const views = this.navCtrl.getViews(); - - // Go back in history until we find a page that doesn't belong to current wiki. - for (let i = views.length - 2; i >= 0; i--) { - const view = views[i]; - - if (view.component.name != 'AddonModWikiIndexPage') { - if (i == views.length - 2) { - // Next view is current view, return undefined. - return; - } - - // This view is no longer from wiki, return the next view. - return views[i + 1]; - } - - // Check that the view belongs to the same wiki as current view. - const wikiId = view.data.wikiId ? view.data.wikiId : view.data.module.instance; - - if (!wikiId || wikiId != this.wiki.id) { - // Wiki has changed, return the next view. - return views[i + 1]; - } - } - } - - /** - * Open the view to create the first page of the wiki. - */ - protected goToCreateFirstPage(): void { - this.navCtrl.push('AddonModWikiEditPage', { - module: this.module, - courseId: this.courseId, - pageTitle: this.wiki.firstpagetitle, - wikiId: this.currentSubwiki.wikiid, - userId: this.currentSubwiki.userid, - groupId: this.currentSubwiki.groupid - }); - } - - /** - * Open the view to edit the current page. - */ - goToEditPage(): void { - if (!this.canEdit) { - return; - } - - if (this.currentPageObj) { - // Current page exists, go to edit it. - const pageParams: any = { - module: this.module, - courseId: this.courseId, - pageId: this.currentPageObj.id, - pageTitle: this.currentPageObj.title, - subwikiId: this.currentPageObj.subwikiid - }; - - if (this.currentSubwiki) { - pageParams.wikiId = this.currentSubwiki.wikiid; - pageParams.userId = this.currentSubwiki.userid; - pageParams.groupId = this.currentSubwiki.groupid; - } - - this.navCtrl.push('AddonModWikiEditPage', pageParams); - } else if (this.currentSubwiki) { - // No page loaded, the wiki doesn't have first page. - this.goToCreateFirstPage(); - } - } - - /** - * Go to the view to create a new page. - */ - goToNewPage(): void { - if (!this.canEdit) { - return; - } - - if (this.currentPageObj) { - // Current page exists, go to edit it. - const pageParams: any = { - module: this.module, - courseId: this.courseId, - subwikiId: this.currentPageObj.subwikiid - }; - - if (this.currentSubwiki) { - pageParams.wikiId = this.currentSubwiki.wikiid; - pageParams.userId = this.currentSubwiki.userid; - pageParams.groupId = this.currentSubwiki.groupid; - } - - this.navCtrl.push('AddonModWikiEditPage', pageParams); - } else if (this.currentSubwiki) { - // No page loaded, the wiki doesn't have first page. - this.goToCreateFirstPage(); - } - } - - /** - * Go to view a certain page. - * - * @param page Page to view. - */ - protected goToPage(page: any): void { - if (!page.id) { - // It's an offline page. Check if we are already in the same offline page. - if (this.currentPage || !this.pageTitle || page.title != this.pageTitle) { - this.navCtrl.push('AddonModWikiIndexPage', { - module: this.module, - courseId: this.courseId, - pageTitle: page.title, - wikiId: this.wiki.id, - subwikiId: page.subwikiid - }); - - return; - } - } else if (this.currentPage != page.id) { - // Add a new State. - this.fetchPageContents(page.id).then((page) => { - this.navCtrl.push('AddonModWikiIndexPage', { - module: this.module, - courseId: this.courseId, - pageTitle: page.title, - pageId: page.id, - wikiId: page.wikiid, - subwikiId: page.subwikiid - }); - }); - - return; - } - } - - /** - * Show the map. - * - * @param event Event. - */ - openMap(event?: MouseEvent): void { - const modal = this.modalCtrl.create('AddonModWikiMapPage', { - pages: this.subwikiPages, - selected: this.currentPageObj && this.currentPageObj.id, - homeView: this.getWikiHomeView(), - moduleId: this.module.id, - courseId: this.courseId - }, { cssClass: 'core-modal-lateral', - showBackdrop: true, - enableBackdropDismiss: true, - enterAnimation: 'core-modal-lateral-transition', - leaveAnimation: 'core-modal-lateral-transition' }); - - // If the modal sends back a page, load it. - modal.onDidDismiss((page) => { - if (page) { - if (page.type == 'home') { - // Go back to the initial page of the wiki. - this.navCtrl.popTo(page.goto); - } else { - this.goToPage(page.goto); - } - } - }); - - modal.present({ - ev: event - }); - } - - /** - * Go to the page to view a certain subwiki. - * - * @param subwikiId Subwiki ID. - * @param userId User ID of the subwiki. - * @param groupId Group ID of the subwiki. - * @param canEdit Whether the subwiki can be edited. - */ - goToSubwiki(subwikiId: number, userId: number, groupId: number, canEdit: boolean): void { - // Check if the subwiki is disabled. - if (subwikiId > 0 || canEdit) { - if (subwikiId != this.currentSubwiki.id || userId != this.currentSubwiki.userid || - groupId != this.currentSubwiki.groupid) { - - this.navCtrl.push('AddonModWikiIndexPage', { - module: this.module, - courseId: this.courseId, - wikiId: this.wiki.id, - subwikiId: subwikiId, - userId: userId, - groupId: groupId - }); - } - } - } - - /** - * Checks if there is any subwiki selected. - * - * @return Whether there is any subwiki selected. - */ - protected isAnySubwikiSelected(): boolean { - return this.subwikiData.subwikiSelected > 0 || this.subwikiData.userSelected > 0 || this.subwikiData.groupSelected > 0; - } - - /** - * Checks if the given subwiki is the one picked on the subwiki picker. - * - * @param subwiki Subwiki to check. - * @return Whether it's the selected subwiki. - */ - protected isSubwikiSelected(subwiki: any): boolean { - const subwikiId = parseInt(subwiki.id, 10) || 0; - - if (subwikiId > 0 && this.subwikiData.subwikiSelected > 0) { - return subwikiId == this.subwikiData.subwikiSelected; - } - - const userId = parseInt(subwiki.userid, 10) || 0, - groupId = parseInt(subwiki.groupid, 10) || 0; - - return userId == this.subwikiData.userSelected && groupId == this.subwikiData.groupSelected; - } - - /** - * Replace edit links to have full url. - * - * @param content Content to treat. - * @return Treated content. - */ - protected replaceEditLinks(content: string): string { - content = content.trim(); - - if (content.length > 0) { - const editUrl = this.textUtils.concatenatePaths(this.sitesProvider.getCurrentSite().getURL(), '/mod/wiki/edit.php'); - content = content.replace(/href="edit\.php/g, 'href="' + editUrl); - } - - return content; - } - - /** - * Sets the selected subwiki for the subwiki picker. - * - * @param subwikiId Subwiki ID to select. - * @param userId User ID of the subwiki to select. - * @param groupId Group ID of the subwiki to select. - */ - protected setSelectedWiki(subwikiId: number, userId: number, groupId: number): void { - this.subwikiData.subwikiSelected = this.wikiOffline.convertToPositiveNumber(subwikiId); - this.subwikiData.userSelected = this.wikiOffline.convertToPositiveNumber(userId); - this.subwikiData.groupSelected = this.wikiOffline.convertToPositiveNumber(groupId); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return If suceed or not. - */ - protected hasSyncSucceed(result: any): boolean { - result.wikiId = this.wiki.id; - - if (result.updated) { - // Trigger event. - this.ignoreManualSyncEvent = true; - this.eventsProvider.trigger(AddonModWikiSyncProvider.MANUAL_SYNCED, result); - } - - if (this.currentSubwiki) { - this.checkPageCreatedOrDiscarded(result.subwikis[this.currentSubwiki.id]); - } - - return result.updated; - } - - /** - * User entered the page that contains the component. - */ - ionViewDidEnter(): void { - super.ionViewDidEnter(); - - if (this.hasEdited) { - this.hasEdited = false; - this.showLoadingAndRefresh(true, false); - } - } - - /** - * User left the page that contains the component. - */ - ionViewDidLeave(): void { - super.ionViewDidLeave(); - - if (this.navCtrl.getActive().component.name == 'AddonModWikiEditPage') { - this.hasEdited = true; - } - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.wikiProvider.invalidateWikiData(this.courseId)); - - if (this.wiki) { - promises.push(this.wikiProvider.invalidateSubwikis(this.wiki.id)); - promises.push(this.groupsProvider.invalidateActivityAllowedGroups(this.wiki.coursemodule)); - promises.push(this.groupsProvider.invalidateActivityGroupMode(this.wiki.coursemodule)); - } - - if (this.currentSubwiki) { - promises.push(this.wikiProvider.invalidateSubwikiPages(this.currentSubwiki.wikiid)); - promises.push(this.wikiProvider.invalidateSubwikiFiles(this.currentSubwiki.wikiid)); - } - - if (this.currentPage) { - promises.push(this.wikiProvider.invalidatePage(this.currentPage)); - } - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (this.currentSubwiki && syncEventData.subwikiId == this.currentSubwiki.id && - syncEventData.wikiId == this.currentSubwiki.wikiid && syncEventData.userId == this.currentSubwiki.userid && - syncEventData.groupId == this.currentSubwiki.groupid) { - - if (this.isCurrentView && syncEventData.warnings && syncEventData.warnings.length) { - // Show warnings. - this.domUtils.showErrorModal(syncEventData.warnings[0]); - } - - // Check if current page was created or discarded. - this.checkPageCreatedOrDiscarded(syncEventData); - } - - return !this.pageWarning; - } - - /** - * Show the TOC. - * - * @param event Event. - */ - showSubwikiPicker(event: MouseEvent): void { - const popover = this.popoverCtrl.create(AddonModWikiSubwikiPickerComponent, { - subwikis: this.subwikiData.subwikis, - currentSubwiki: this.currentSubwiki - }); - - popover.onDidDismiss((subwiki) => { - if (subwiki) { - this.goToSubwiki(subwiki.id, subwiki.userid, subwiki.groupid, subwiki.canedit); - } - }); - - popover.present({ - ev: event - }); - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.wikiSync.syncWiki(this.wiki.id, this.courseId, this.wiki.coursemodule); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - - this.manualSyncObserver && this.manualSyncObserver.off(); - this.newPageObserver && this.newPageObserver.off(); - } - - /** - * Create the subwiki list for the selector and store it in the cache. - * - * @param userGroups Groups. - * @return Promise resolved when done. - */ - protected createSubwikiList(userGroups: any[]): Promise { - const subwikiList = [], - promises = []; - let userGroupsIds = [], - allParticipants = false, - showMyGroupsLabel = false, - multiLevelList = false; - - this.subwikiData.subwikis = []; - this.setSelectedWiki(this.subwikiId, this.userId, this.groupId); - this.subwikiData.count = 0; - - // Group mode available. - if (userGroups.length > 0) { - userGroupsIds = userGroups.map((g) => { - return g.id; - }); - } - - // Add the subwikis to the subwikiList. - this.loadedSubwikis.forEach((subwiki) => { - const groupId = parseInt(subwiki.groupid, 10), - userId = parseInt(subwiki.userid, 10); - let groupLabel = ''; - - if (groupId == 0 && userId == 0) { - // Add 'All participants' subwiki if needed at the start. - if (!allParticipants) { - subwikiList.unshift({ - name: this.translate.instant('core.allparticipants'), - id: subwiki.id, - userid: userId, - groupid: groupId, - groupLabel: '', - canedit: subwiki.canedit - }); - allParticipants = true; - } - } else { - if (groupId != 0 && userGroupsIds.length > 0) { - // Get groupLabel if it has groupId. - const groupIdPosition = userGroupsIds.indexOf(groupId); - if (groupIdPosition > -1) { - groupLabel = userGroups[groupIdPosition].name; - } - } else { - groupLabel = this.translate.instant('addon.mod_wiki.notingroup'); - } - - if (userId != 0) { - // Get user if it has userId. - promises.push(this.userProvider.getProfile(userId, this.courseId, true).then((user) => { - subwikiList.push({ - name: user.fullname, - id: subwiki.id, - userid: userId, - groupid: groupId, - groupLabel: groupLabel, - canedit: subwiki.canedit - }); - - })); - - if (!multiLevelList && groupId != 0) { - multiLevelList = true; - } - } else { - subwikiList.push({ - name: groupLabel, - id: subwiki.id, - userid: userId, - groupid: groupId, - groupLabel: groupLabel, - canedit: subwiki.canedit - }); - showMyGroupsLabel = true; - } - } - }); - - return Promise.all(promises).then(() => { - this.fillSubwikiData(subwikiList, showMyGroupsLabel, multiLevelList); - }); - } - - /** - * Fill the subwiki data. - * - * @param subwikiList List of subwikis. - * @param showMyGroupsLabel Whether subwikis should be grouped in "My groups" and "Other groups". - * @param multiLevelList Whether it's a multi level list. - */ - protected fillSubwikiData(subwikiList: any[], showMyGroupsLabel: boolean, multiLevelList: boolean): void { - let groupValue = -1, - grouping; - - subwikiList.sort((a, b) => { - return a.groupid - b.groupid; - }); - - this.groupWiki = showMyGroupsLabel; - - this.subwikiData.count = subwikiList.length; - - // If no subwiki is received as view param, select always the most appropiate. - if ((!this.subwikiId || (!this.userId && !this.groupId)) && !this.isAnySubwikiSelected() && subwikiList.length > 0) { - let firstCanEdit, - candidateNoFirstPage, - candidateFirstPage; - - for (const i in subwikiList) { - const subwiki = subwikiList[i]; - - if (subwiki.canedit) { - let candidateSubwikiId; - if (subwiki.userid > 0) { - // Check if it's the current user. - if (this.currentUserId == subwiki.userid) { - candidateSubwikiId = subwiki.id; - } - } else if (subwiki.groupid > 0) { - // Check if it's a current user' group. - if (showMyGroupsLabel) { - candidateSubwikiId = subwiki.id; - } - } else if (subwiki.id > 0) { - candidateSubwikiId = subwiki.id; - } - - if (typeof candidateSubwikiId != 'undefined') { - if (candidateSubwikiId > 0) { - // Subwiki found and created, no need to keep looking. - candidateFirstPage = i; - break; - } else if (typeof candidateNoFirstPage == 'undefined') { - candidateNoFirstPage = i; - } - } else if (typeof firstCanEdit == 'undefined') { - firstCanEdit = i; - } - } - } - - let subWikiToTake; - if (typeof candidateFirstPage != 'undefined') { - // Take the candidate that already has the first page created. - subWikiToTake = candidateFirstPage; - } else if (typeof candidateNoFirstPage != 'undefined') { - // No first page created, take the first candidate. - subWikiToTake = candidateNoFirstPage; - } else if (typeof firstCanEdit != 'undefined') { - // None selected, take the first the user can edit. - subWikiToTake = firstCanEdit; - } else { - // Otherwise take the very first. - subWikiToTake = 0; - } - - const subwiki = subwikiList[subWikiToTake]; - if (typeof subwiki != 'undefined') { - this.setSelectedWiki(subwiki.id, subwiki.userid, subwiki.groupid); - } - } - - if (multiLevelList) { - // As we loop over each subwiki, add it to the current group - subwikiList.forEach((subwiki) => { - // Should we create a new grouping? - if (subwiki.groupid !== groupValue) { - grouping = {label: subwiki.groupLabel, subwikis: []}; - groupValue = subwiki.groupid; - - this.subwikiData.subwikis.push(grouping); - } - - // Add the subwiki to the currently active grouping. - grouping.subwikis.push(subwiki); - }); - } else if (showMyGroupsLabel) { - const noGrouping = {label: '', subwikis: []}, - myGroupsGrouping = {label: this.translate.instant('core.mygroups'), subwikis: []}, - otherGroupsGrouping = {label: this.translate.instant('core.othergroups'), subwikis: []}; - - // As we loop over each subwiki, add it to the current group - subwikiList.forEach((subwiki) => { - // Add the subwiki to the currently active grouping. - if (typeof subwiki.canedit == 'undefined') { - noGrouping.subwikis.push(subwiki); - } else if (subwiki.canedit) { - myGroupsGrouping.subwikis.push(subwiki); - } else { - otherGroupsGrouping.subwikis.push(subwiki); - } - }); - - // Add each grouping to the subwikis - if (noGrouping.subwikis.length > 0) { - this.subwikiData.subwikis.push(noGrouping); - } - if (myGroupsGrouping.subwikis.length > 0) { - this.subwikiData.subwikis.push(myGroupsGrouping); - } - if (otherGroupsGrouping.subwikis.length > 0) { - this.subwikiData.subwikis.push(otherGroupsGrouping); - } - } else { - this.subwikiData.subwikis.push({label: '', subwikis: subwikiList}); - } - - this.wikiProvider.setSubwikiList(this.wiki.id, this.subwikiData.subwikis, this.subwikiData.count, - this.subwikiData.subwikiSelected, this.subwikiData.userSelected, this.subwikiData.groupSelected); - } -} diff --git a/src/addon/mod/wiki/components/subwiki-picker/addon-mod-wiki-subwiki-picker.html b/src/addon/mod/wiki/components/subwiki-picker/addon-mod-wiki-subwiki-picker.html deleted file mode 100644 index 0e9f93a3f..000000000 --- a/src/addon/mod/wiki/components/subwiki-picker/addon-mod-wiki-subwiki-picker.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - {{ group.label }} - - - {{ subwiki.name }} - - - - diff --git a/src/addon/mod/wiki/components/subwiki-picker/subwiki-picker.scss b/src/addon/mod/wiki/components/subwiki-picker/subwiki-picker.scss deleted file mode 100644 index 90c933749..000000000 --- a/src/addon/mod/wiki/components/subwiki-picker/subwiki-picker.scss +++ /dev/null @@ -1,18 +0,0 @@ -ion-app.app-root addon-mod-wiki-subwiki-picker { - - $core-subwiki-selected: $core-color !default; - - .item-divider, .item-divider .label { - font-weight: bold; - } - - .item.addon-mod_wiki-subwiki-selected { - background-color: $gray-light; - color: $core-subwiki-selected; - - .icon { - color: $core-subwiki-selected; - font-size: 24px; - } - } -} diff --git a/src/addon/mod/wiki/components/subwiki-picker/subwiki-picker.ts b/src/addon/mod/wiki/components/subwiki-picker/subwiki-picker.ts deleted file mode 100644 index 942ddb71b..000000000 --- a/src/addon/mod/wiki/components/subwiki-picker/subwiki-picker.ts +++ /dev/null @@ -1,67 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { NavParams, ViewController } from 'ionic-angular'; - -/** - * Component to display the a list of subwikis in a wiki. - */ -@Component({ - selector: 'addon-mod-wiki-subwiki-picker', - templateUrl: 'addon-mod-wiki-subwiki-picker.html' -}) -export class AddonModWikiSubwikiPickerComponent { - subwikis: any[]; - currentSubwiki: any; - - constructor(navParams: NavParams, private viewCtrl: ViewController) { - this.subwikis = navParams.get('subwikis'); - this.currentSubwiki = navParams.get('currentSubwiki'); - } - - /** - * Checks if the given subwiki is the one currently selected. - * - * @param subwiki Subwiki to check. - * @return Whether it's the selected subwiki. - */ - protected isSubwikiSelected(subwiki: any): boolean { - const subwikiId = parseInt(subwiki.id, 10) || 0; - - if (subwikiId > 0 && this.currentSubwiki.id > 0) { - return subwikiId == this.currentSubwiki.id; - } - - const userId = parseInt(subwiki.userid, 10) || 0, - groupId = parseInt(subwiki.groupid, 10) || 0; - - return userId == this.currentSubwiki.userid && groupId == this.currentSubwiki.groupid; - } - - /** - * Function called when a subwiki is clicked. - * - * @param subwiki The subwiki to open. - */ - openSubwiki(subwiki: any): void { - // Check if the subwiki is disabled. - if (subwiki.id > 0 || subwiki.canedit) { - // Check if it isn't current subwiki. - if (subwiki != this.currentSubwiki) { - this.viewCtrl.dismiss(subwiki); - } - } - } -} diff --git a/src/addon/mod/wiki/lang/en.json b/src/addon/mod/wiki/lang/en.json deleted file mode 100644 index 246965b9c..000000000 --- a/src/addon/mod/wiki/lang/en.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "cannoteditpage": "You can not edit this page.", - "createpage": "Create page", - "editingpage": "Editing this page '{{$a}}'", - "errorloadingpage": "An error occurred while loading the page.", - "errornowikiavailable": "This wiki does not have any content yet.", - "gowikihome": "Go to the wiki first page", - "map": "Map", - "modulenameplural": "Wikis", - "newpagehdr": "New page", - "newpagetitle": "New page title", - "nocontent": "There is no content for this page", - "notingroup": "Not in group", - "pageexists": "This page already exists.", - "pagename": "Page name", - "subwiki": "Sub-wiki", - "tagarea_wiki_pages": "Wiki pages", - "titleshouldnotbeempty": "The title should not be empty", - "viewpage": "View page", - "wikipage": "Wiki page", - "wrongversionlock": "Another user has edited this page while you were editing and your content is obsolete." -} \ No newline at end of file diff --git a/src/addon/mod/wiki/pages/edit/edit.html b/src/addon/mod/wiki/pages/edit/edit.html deleted file mode 100644 index aa2491c90..000000000 --- a/src/addon/mod/wiki/pages/edit/edit.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - -
- - - - - - - - - - {{ 'addon.mod_wiki.wrongversionlock' | translate }} - -
-
-
diff --git a/src/addon/mod/wiki/pages/edit/edit.module.ts b/src/addon/mod/wiki/pages/edit/edit.module.ts deleted file mode 100644 index ecf4b0fa4..000000000 --- a/src/addon/mod/wiki/pages/edit/edit.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWikiEditPage } from './edit'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModWikiEditPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonModWikiEditPage), - TranslateModule.forChild() - ], -}) -export class AddonModWikiEditPageModule {} diff --git a/src/addon/mod/wiki/pages/edit/edit.scss b/src/addon/mod/wiki/pages/edit/edit.scss deleted file mode 100644 index beb6a63b0..000000000 --- a/src/addon/mod/wiki/pages/edit/edit.scss +++ /dev/null @@ -1,5 +0,0 @@ -ion-app.app-root page-addon-mod-wiki-edit { - .addon-mod_wiki-wrongversionlock .label { - margin: 0; - } -} diff --git a/src/addon/mod/wiki/pages/edit/edit.ts b/src/addon/mod/wiki/pages/edit/edit.ts deleted file mode 100644 index 90210ca12..000000000 --- a/src/addon/mod/wiki/pages/edit/edit.ts +++ /dev/null @@ -1,577 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { FormControl, FormGroup, FormBuilder } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModWikiProvider } from '../../providers/wiki'; -import { AddonModWikiOfflineProvider } from '../../providers/wiki-offline'; -import { AddonModWikiSyncProvider, AddonModWikiSyncSubwikiResult } from '../../providers/wiki-sync'; - -/** - * Page that allows adding or editing a wiki page. - */ -@IonicPage({ segment: 'addon-mod-wiki-edit' }) -@Component({ - selector: 'page-addon-mod-wiki-edit', - templateUrl: 'edit.html', -}) -export class AddonModWikiEditPage implements OnInit, OnDestroy { - - @ViewChild('editPageForm') formElement: ElementRef; - - title: string; // Title to display. - pageForm: FormGroup; // The form group. - contentControl: FormControl; // The FormControl for the page content. - canEditTitle: boolean; // Whether title can be edited. - loaded: boolean; // Whether the data has been loaded. - component = AddonModWikiProvider.COMPONENT; // Component to link the files to. - componentId: number; // Component ID to link the files to. - wrongVersionLock: boolean; // Whether the page lock doesn't match the initial one. - editorExtraParams: {[name: string]: any} = {}; - - protected module: any; // Wiki module instance. - protected courseId: number; // Course the wiki belongs to. - protected subwikiId: number; // Subwiki ID the page belongs to. - protected initialSubwikiId: number; // Same as subwikiId, but it won't be updated, it'll always be the value received. - protected wikiId: number; // Wiki ID the page belongs to. - protected pageId: number; // The page ID (if editing a page). - protected section: string; // The section being edited. - protected groupId: number; // The group the subwiki belongs to. - protected userId: number; // The user the subwiki belongs to. - protected blockId: string; // ID to block the subwiki. - protected editing: boolean; // Whether the user is editing a page (true) or creating a new one (false). - protected editOffline: boolean; // Whether the user is editing an offline page. - protected subwikiFiles: any[]; // List of files of the subwiki. - protected originalContent: string; // The original page content. - protected version: number; // Page version. - protected renewLockInterval: any; // An interval to renew the lock every certain time. - protected forceLeave = false; // To allow leaving the page without checking for changes. - protected isDestroyed = false; // Whether the page has been destroyed. - protected pageParamsToLoad: any; // Params of the page to load when this page is closed. - - constructor(navParams: NavParams, fb: FormBuilder, protected navCtrl: NavController, protected sitesProvider: CoreSitesProvider, - protected syncProvider: CoreSyncProvider, protected domUtils: CoreDomUtilsProvider, - protected translate: TranslateService, protected courseProvider: CoreCourseProvider, - protected eventsProvider: CoreEventsProvider, protected wikiProvider: AddonModWikiProvider, - protected wikiOffline: AddonModWikiOfflineProvider, protected wikiSync: AddonModWikiSyncProvider, - protected textUtils: CoreTextUtilsProvider, protected courseHelper: CoreCourseHelperProvider) { - - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.subwikiId = navParams.get('subwikiId'); - this.wikiId = navParams.get('wikiId'); - this.pageId = navParams.get('pageId'); - this.section = navParams.get('section'); - this.groupId = navParams.get('groupId'); - this.userId = navParams.get('userId'); - - let pageTitle = navParams.get('pageTitle'); - pageTitle = pageTitle ? pageTitle.replace(/\+/g, ' ') : ''; - - this.initialSubwikiId = this.subwikiId; - this.componentId = this.module.id; - this.canEditTitle = !pageTitle; - this.title = pageTitle ? this.translate.instant('addon.mod_wiki.editingpage', {$a: pageTitle}) : - this.translate.instant('addon.mod_wiki.newpagehdr'); - this.blockId = this.wikiSync.getSubwikiBlockId(this.subwikiId, this.wikiId, this.userId, this.groupId); - - // Create the form group and its controls. - this.contentControl = fb.control(''); - this.pageForm = fb.group({ - title: pageTitle - }); - this.pageForm.addControl('text', this.contentControl); - - // Block the wiki so it cannot be synced. - this.syncProvider.blockOperation(this.component, this.blockId); - - if (!this.module.id) { - this.editorExtraParams.type = 'wiki'; - } - - if (this.pageId) { - this.editorExtraParams.pageid = this.pageId; - - if (this.section) { - this.editorExtraParams.section = this.section; - } - } else if (pageTitle) { - this.editorExtraParams.pagetitle = pageTitle; - } - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchWikiPageData().then((success) => { - if (success && this.blockId && !this.isDestroyed) { - // Block the subwiki now that we have blockId for sure. - const newBlockId = this.wikiSync.getSubwikiBlockId(this.subwikiId, this.wikiId, this.userId, this.groupId); - if (newBlockId != this.blockId) { - this.syncProvider.unblockOperation(this.component, this.blockId); - this.blockId = newBlockId; - this.syncProvider.blockOperation(this.component, this.blockId); - } - } - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Convenience function to get wiki page data. - * - * @return Promise resolved with boolean: whether it was successful. - */ - protected fetchWikiPageData(): Promise { - let promise, - canEdit = false; - - if (this.pageId) { - // Editing a page that already exists. - this.canEditTitle = false; - this.editing = true; - this.editOffline = false; // Cannot edit pages in offline. - - // Get page contents to obtain title and editing permission - promise = this.wikiProvider.getPageContents(this.pageId, {cmId: this.module.id}).then((pageContents) => { - this.pageForm.controls.title.setValue(pageContents.title); // Set the title in the form group. - this.wikiId = pageContents.wikiid; - this.subwikiId = pageContents.subwikiid; - this.title = this.translate.instant('addon.mod_wiki.editingpage', {$a: pageContents.title}); - this.groupId = pageContents.groupid; - this.userId = pageContents.userid; - canEdit = pageContents.caneditpage; - - // Wait for sync to be over (if any). - return this.wikiSync.waitForSync(this.blockId); - }).then(() => { - // Get subwiki files, needed to replace URLs for rich text editor. - return this.wikiProvider.getSubwikiFiles(this.wikiId, { - groupId: this.groupId, - userId: this.userId, - cmId: this.module.id, - }); - }).then((files) => { - this.subwikiFiles = files; - - // Get editable text of the page/section. - return this.wikiProvider.getPageForEditing(this.pageId, this.section); - }).then((editContents) => { - // Get the original page contents, treating file URLs if needed. - const content = this.textUtils.replacePluginfileUrls(editContents.content, this.subwikiFiles); - - this.contentControl.setValue(content); - this.originalContent = content; - this.version = editContents.version; - - if (canEdit) { - // Renew the lock every certain time. - this.renewLockInterval = setInterval(() => { - this.renewLock(); - }, AddonModWikiProvider.RENEW_LOCK_TIME); - } - }); - } else { - const pageTitle = this.pageForm.controls.title.value; - - // New page. Wait for sync to be over (if any). - promise = this.wikiSync.waitForSync(this.blockId); - - if (pageTitle) { - // Title is set, it could be editing an offline page or creating a new page using an edit link. - promise = promise.then((result: AddonModWikiSyncSubwikiResult) => { - - // First of all, verify if this page was created in the current sync. - if (result) { - const page = result.created.find((page) => { - return page.title == pageTitle; - }); - - if (page && page.pageId > 0) { - // Page was created, now it exists in the site. - this.pageId = page.pageId; - - return this.fetchWikiPageData(); - } - } - - // Check if there's already some offline data for this page. - return this.wikiOffline.getNewPage(pageTitle, this.subwikiId, this.wikiId, this.userId, this.groupId); - }).then((page) => { - // Load offline content. - this.contentControl.setValue(page.cachedcontent); - this.originalContent = page.cachedcontent; - this.editOffline = true; - }).catch(() => { - // No offline data found. - this.editOffline = false; - }); - } else { - this.editOffline = false; - } - - promise.then(() => { - this.editing = false; - canEdit = !!this.blockId; // If no blockId, the user cannot edit the page. - }); - } - - return promise.then(() => { - return true; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error getting wiki data.'); - - // Go back. - this.forceLeavePage(); - - return false; - }).finally(() => { - if (!canEdit) { - // Cannot edit, show alert and go back. - this.domUtils.showAlert(this.translate.instant('core.notice'), - this.translate.instant('addon.mod_wiki.cannoteditpage')); - this.forceLeavePage(); - } - }); - } - - /** - * Force leaving the page, without checking for changes. - */ - protected forceLeavePage(): void { - this.forceLeave = true; - this.navCtrl.pop(); - } - - /** - * Navigate to a new offline page. - * - * @param title Page title. - */ - protected goToNewOfflinePage(title: string): void { - if (this.courseId && (this.module.id || this.wikiId)) { - // We have enough data to navigate to the page. - if (!this.editOffline || this.previousViewPageIsDifferentOffline(title)) { - this.pageParamsToLoad = { - module: this.module, - courseId: this.courseId, - pageId: null, - pageTitle: title, - wikiId: this.wikiId, - subwikiId: this.subwikiId, - userId: this.userId, - groupId: this.groupId - }; - } - } else { - this.domUtils.showAlert(this.translate.instant('core.success'), this.translate.instant('core.datastoredoffline')); - } - - this.forceLeavePage(); - } - - /** - * Check if we need to navigate to a new state. - * - * @param title Page title. - * @return Promise resolved when done. - */ - protected gotoPage(title: string): Promise { - return this.retrieveModuleInfo(this.wikiId).then(() => { - let openPage = false; - - // Not the firstpage. - if (this.initialSubwikiId) { - if (!this.editing && this.editOffline && this.previousViewPageIsDifferentOffline(title)) { - // The user submitted an offline page that isn't loaded in the back view, open it. - openPage = true; - } else if (!this.editOffline && this.previousViewIsDifferentPageOnline()) { - // The user submitted an offline page that isn't loaded in the back view, open it. - openPage = true; - } - } - - if (openPage) { - // Setting that will do the app navigate to the page. - this.pageParamsToLoad = { - module: this.module, - courseId: this.courseId, - pageId: this.pageId, - pageTitle: title, - wikiId: this.wikiId, - subwikiId: this.subwikiId, - userId: this.userId, - groupId: this.groupId - }; - } - - this.forceLeavePage(); - }).catch(() => { - // Go back if it fails. - this.forceLeavePage(); - }); - } - - /** - * Check if data has changed. - * - * @return Whether data has changed. - */ - protected hasDataChanged(): boolean { - const values = this.pageForm.value; - - return !(this.originalContent == values.text || (!this.editing && !values.text && !values.title)); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave) { - return; - } - - // Check if data has changed. - if (this.hasDataChanged()) { - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - } - - /** - * View left. - */ - ionViewDidLeave(): void { - if (this.pageParamsToLoad) { - // Go to the page we've just created/edited. - this.navCtrl.push('AddonModWikiIndexPage', this.pageParamsToLoad); - } - } - - /** - * In case we are NOT editing an offline page, check if the page loaded in previous view is different than this view. - * - * @return Whether previous view wiki page is different than current page. - */ - protected previousViewIsDifferentPageOnline(): boolean { - // We cannot precisely detect when the state is the same but this is close to it. - const previousView = this.navCtrl.getPrevious(); - - return !this.editing || previousView.component.name != 'AddonModWikiIndexPage' || - previousView.data.module.id != this.module.id || previousView.data.pageId != this.pageId; - } - - /** - * In case we're editing an offline page, check if the page loaded in previous view is different than this view. - * - * @param title The current page title. - * @return Whether previous view wiki page is different than current page. - */ - protected previousViewPageIsDifferentOffline(title: string): boolean { - // We cannot precisely detect when the state is the same but this is close to it. - const previousView = this.navCtrl.getPrevious(); - - if (previousView.component.name != 'AddonModWikiIndexPage' || previousView.data.module.id != this.module.id || - previousView.data.wikiId != this.wikiId || previousView.data.pageTitle != title) { - return true; - } - - // Check subwiki using subwiki or user and group. - const previousSubwikiId = parseInt(previousView.data.subwikiId, 10) || 0; - if (previousSubwikiId > 0 && this.subwikiId > 0) { - return previousSubwikiId != this.subwikiId; - } - - const previousUserId = parseInt(previousView.data.userId, 10) || 0, - previousGroupId = parseInt(previousView.data.groupId, 10) || 0; - - return this.userId != previousUserId || this.groupId != previousGroupId; - } - - /** - * Save the data. - */ - save(): void { - const values = this.pageForm.value, - title = values.title, - modal = this.domUtils.showModalLoading('core.sending', true); - let promise, - text = values.text; - - text = this.textUtils.restorePluginfileUrls(text, this.subwikiFiles); - text = this.textUtils.formatHtmlLines(text); - - if (this.editing) { - // Edit existing page. - promise = this.wikiProvider.editPage(this.pageId, text, this.section).then(() => { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, true, this.sitesProvider.getCurrentSiteId()); - - // Invalidate page since it changed. - return this.wikiProvider.invalidatePage(this.pageId).then(() => { - return this.gotoPage(title); - }); - }); - } else { - // Creating a new page. - if (!title) { - // Title is mandatory, stop. - this.domUtils.showAlert(this.translate.instant('core.notice'), - this.translate.instant('addon.mod_wiki.titleshouldnotbeempty')); - modal.dismiss(); - - return; - } - - if (!this.editOffline) { - // Check if the user has an offline page with the same title. - promise = this.wikiOffline.getNewPage(title, this.subwikiId, this.wikiId, this.userId, this.groupId).then(() => { - // There's a page with same name, reject with error message. - return Promise.reject(this.translate.instant('addon.mod_wiki.pageexists')); - }, () => { - // Not found, page can be sent. - }); - } else { - promise = Promise.resolve(); - } - - promise = promise.then(() => { - // Try to send the page. - let wikiId = this.wikiId || (this.module && this.module.instance); - - return this.wikiProvider.newPage(title, text, { - subwikiId: this.subwikiId, - wikiId, - userId: this.userId, - groupId: this.groupId, - cmId: this.module.id, - }).then((id) => { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, id > 0, this.sitesProvider.getCurrentSiteId()); - - if (id > 0) { - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'wiki' }); - - // Page was created, get its data and go to the page. - this.pageId = id; - - return this.wikiProvider.getPageContents(this.pageId, {cmId: this.module.id}).then((pageContents) => { - const promises = []; - - wikiId = parseInt(pageContents.wikiid, 10); - if (!this.subwikiId) { - // Subwiki was not created, invalidate subwikis as well. - promises.push(this.wikiProvider.invalidateSubwikis(wikiId)); - } - - this.subwikiId = parseInt(pageContents.subwikiid, 10); - this.userId = parseInt(pageContents.userid, 10); - this.groupId = parseInt(pageContents.groupid, 10); - - // Invalidate subwiki pages since there are new. - promises.push(this.wikiProvider.invalidateSubwikiPages(wikiId)); - - return Promise.all(promises).then(() => { - return this.gotoPage(title); - }); - }).finally(() => { - // Notify page created. - this.eventsProvider.trigger(AddonModWikiProvider.PAGE_CREATED_EVENT, { - pageId: this.pageId, - subwikiId: this.subwikiId, - pageTitle: title, - }, this.sitesProvider.getCurrentSiteId()); - }); - } else { - // Page stored in offline. Go to see the offline page. - this.goToNewOfflinePage(title); - } - }); - }); - } - - return promise.catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error saving wiki data.'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Renew lock and control versions. - */ - protected renewLock(): void { - this.wikiProvider.getPageForEditing(this.pageId, this.section, true).then((response) => { - if (response.version && this.version != response.version) { - this.wrongVersionLock = true; - } - }); - } - - /** - * Fetch module information to redirect when needed. - * - * @param wikiId Wiki ID. - * @return Promise resolved when done. - */ - protected retrieveModuleInfo(wikiId: number): Promise { - if (this.module.id && this.courseId) { - // We have enough data. - return Promise.resolve(); - } - - const promise = this.module.id ? Promise.resolve(this.module) : - this.courseProvider.getModuleBasicInfoByInstance(wikiId, 'wiki'); - - return promise.then((mod) => { - this.module = mod; - this.componentId = this.module.id; - - if (!this.courseId && this.module.course) { - this.courseId = this.module.course; - } else if (!this.courseId) { - return this.courseHelper.getModuleCourseIdByInstance(wikiId, 'wiki').then((course) => { - this.courseId = course; - }); - } - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.isDestroyed = true; - clearInterval(this.renewLockInterval); - - // Unblock the subwiki. - if (this.blockId) { - this.syncProvider.unblockOperation(this.component, this.blockId); - } - } -} diff --git a/src/addon/mod/wiki/pages/index/index.html b/src/addon/mod/wiki/pages/index/index.html deleted file mode 100644 index 31c0fcf94..000000000 --- a/src/addon/mod/wiki/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/wiki/pages/index/index.module.ts b/src/addon/mod/wiki/pages/index/index.module.ts deleted file mode 100644 index b2458055a..000000000 --- a/src/addon/mod/wiki/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWikiComponentsModule } from '../../components/components.module'; -import { AddonModWikiIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModWikiIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModWikiComponentsModule, - IonicPageModule.forChild(AddonModWikiIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModWikiIndexPageModule {} diff --git a/src/addon/mod/wiki/pages/index/index.ts b/src/addon/mod/wiki/pages/index/index.ts deleted file mode 100644 index 7628bda31..000000000 --- a/src/addon/mod/wiki/pages/index/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModWikiIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a wiki page. - */ -@IonicPage({ segment: 'addon-mod-wiki-index' }) -@Component({ - selector: 'page-addon-mod-wiki-index', - templateUrl: 'index.html', -}) -export class AddonModWikiIndexPage { - @ViewChild(AddonModWikiIndexComponent) wikiComponent: AddonModWikiIndexComponent; - - title: string; - module: any; - courseId: number; - action: string; - pageId: number; - pageTitle: string; - wikiId: number; - subwikiId: number; - userId: number; - groupId: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.action = navParams.get('action') || 'page'; - this.pageId = navParams.get('pageId'); - this.pageTitle = navParams.get('pageTitle'); - this.wikiId = navParams.get('wikiId'); - this.subwikiId = navParams.get('subwikiId'); - this.userId = navParams.get('userId'); - this.groupId = navParams.get('groupId'); - - this.title = this.pageTitle || this.module.name; - } - - /** - * Update some data based on the data received. - * - * @param data The data received. - */ - updateData(data: any): void { - if (typeof data == 'string') { - // We received the title to display. - this.title = data; - } else { - // We received a wiki instance. - this.title = this.pageTitle || data.title || this.title; - } - } - - /** - * User entered the page. - */ - ionViewDidEnter(): void { - this.wikiComponent.ionViewDidEnter(); - } - - /** - * User left the page. - */ - ionViewDidLeave(): void { - this.wikiComponent.ionViewDidLeave(); - } -} diff --git a/src/addon/mod/wiki/pages/map/map.html b/src/addon/mod/wiki/pages/map/map.html deleted file mode 100644 index 2efb52c92..000000000 --- a/src/addon/mod/wiki/pages/map/map.html +++ /dev/null @@ -1,32 +0,0 @@ - - - {{ 'addon.mod_wiki.map' | translate }} - - - - - - - - diff --git a/src/addon/mod/wiki/pages/map/map.module.ts b/src/addon/mod/wiki/pages/map/map.module.ts deleted file mode 100644 index 75177b8dd..000000000 --- a/src/addon/mod/wiki/pages/map/map.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWikiMapPage } from './map'; - -@NgModule({ - declarations: [ - AddonModWikiMapPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonModWikiMapPage), - TranslateModule.forChild() - ], -}) -export class AddonModWikiMapPageModule {} diff --git a/src/addon/mod/wiki/pages/map/map.ts b/src/addon/mod/wiki/pages/map/map.ts deleted file mode 100644 index 9d5991e64..000000000 --- a/src/addon/mod/wiki/pages/map/map.ts +++ /dev/null @@ -1,97 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; - -/** - * Modal to display the map of a Wiki. - */ -@IonicPage({ segment: 'addon-mod-wiki-map' }) -@Component({ - selector: 'page-addon-mod-wiki-map', - templateUrl: 'map.html', -}) -export class AddonModWikiMapPage { - map: any[] = []; // Map of pages, categorized by letter. - selected: number; - moduleId: number; - courseId: number; - homeView: ViewController; - - constructor(navParams: NavParams, protected viewCtrl: ViewController) { - this.constructMap(navParams.get('pages') || []); - - this.selected = navParams.get('selected'); - this.homeView = navParams.get('homeView'); - this.moduleId = navParams.get('moduleId'); - this.courseId = navParams.get('courseId'); - } - - /** - * Function called when a page is clicked. - * - * @param page Clicked page. - */ - goToPage(page: any): void { - this.viewCtrl.dismiss({type: 'page', goto: page}); - } - - /** - * Go back to the initial page of the wiki. - */ - goToWikiHome(): void { - this.viewCtrl.dismiss({type: 'home', goto: this.homeView}); - } - - /** - * Construct the map of pages. - * - * @param pages List of pages. - */ - protected constructMap(pages: any[]): void { - let letter, - initialLetter; - - this.map = []; - pages.sort((a, b) => { - const compareA = a.title.toLowerCase().trim(), - compareB = b.title.toLowerCase().trim(); - - return compareA.localeCompare(compareB); - }); - - pages.forEach((page) => { - const letterCandidate = page.title.charAt(0).toLocaleUpperCase(); - - // Should we create a new grouping? - if (letterCandidate !== initialLetter) { - initialLetter = letterCandidate; - letter = {label: letterCandidate, pages: []}; - - this.map.push(letter); - } - - // Add the subwiki to the currently active grouping. - letter.pages.push(page); - }); - } - - /** - * Close modal. - */ - closeModal(): void { - this.viewCtrl.dismiss(); - } -} diff --git a/src/addon/mod/wiki/providers/create-link-handler.ts b/src/addon/mod/wiki/providers/create-link-handler.ts deleted file mode 100644 index f592b7510..000000000 --- a/src/addon/mod/wiki/providers/create-link-handler.ts +++ /dev/null @@ -1,137 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, ViewController } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModWikiProvider } from './wiki'; - -/** - * Handler to treat links to create a wiki page. - */ -@Injectable() -export class AddonModWikiCreateLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModWikiCreateLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModWiki'; - pattern = /\/mod\/wiki\/create\.php.*([\&\?]swid=\d+)/; - - constructor(protected domUtils: CoreDomUtilsProvider, protected wikiProvider: AddonModWikiProvider, - protected courseHelper: CoreCourseHelperProvider, protected linkHelper: CoreContentLinksHelperProvider, - protected courseProvider: CoreCourseProvider) { - super(); - } - - /** - * Check if the current view is a wiki page of the same wiki. - * - * @param activeView Active view. - * @param subwikiId Subwiki ID to check. - * @param siteId Site ID. - * @return Promise resolved with boolean: whether current view belongs to the same wiki. - */ - protected currentStateIsSameWiki(activeView: ViewController, subwikiId: number, siteId: string): Promise { - - if (activeView && activeView.component.name == 'AddonModWikiIndexPage') { - const moduleId = activeView.data.module && activeView.data.module.id; - - if (activeView.data.subwikiId == subwikiId) { - // Same subwiki, so it's same wiki. - return Promise.resolve(true); - - } else if (activeView.data.pageId) { - // Get the page contents to check the subwiki. - return this.wikiProvider.getPageContents(activeView.data.pageId, {cmId: moduleId, siteId}).then((page) => { - return page.subwikiid == subwikiId; - }).catch(() => { - // Not found, return false. - return false; - }); - - } else if (activeView.data.wikiId) { - // Check if the subwiki belongs to this wiki. - return this.wikiProvider.wikiHasSubwiki(activeView.data.wikiId, subwikiId, {cmId: moduleId, siteId}); - - } else if (activeView.data.courseId && activeView.data.module) { - if (moduleId) { - // Get the wiki. - return this.wikiProvider.getWiki(activeView.data.courseId, moduleId, {siteId}).then((wiki) => { - // Check if the subwiki belongs to this wiki. - return this.wikiProvider.wikiHasSubwiki(wiki.id, subwikiId, {cmId: moduleId, siteId}); - }).catch(() => { - // Not found, return false. - return false; - }); - } - } - } - - return Promise.resolve(false); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - courseId = courseId || params.courseid || params.cid; - - return [{ - action: (siteId, navCtrl?: NavController): void => { - const modal = this.domUtils.showModalLoading(), - subwikiId = parseInt(params.swid, 10), - activeView = navCtrl && navCtrl.getActive(); - - // Check if the link is inside the same wiki. - this.currentStateIsSameWiki(activeView, subwikiId, siteId).then((isSameWiki) => { - if (isSameWiki) { - // User is seeing the wiki, we can get the module from the wiki params. - if (activeView && activeView.data.module && activeView.data.module.id) { - // We already have it in the params. - return activeView.data.module; - } else if (activeView && activeView.data.wikiId) { - return this.courseProvider.getModuleBasicInfoByInstance(activeView.data.wikiId, 'wiki', siteId) - .catch(() => { - // Not found. - }); - } - } - }).then((module) => { - // Return the params. - const pageParams = { - module: module, - courseId: courseId || (module && module.course) || (activeView && activeView.data.courseId), - pageTitle: params.title, - subwikiId: subwikiId - }; - - this.linkHelper.goInSite(navCtrl, 'AddonModWikiEditPage', pageParams, siteId); - }).finally(() => { - modal.dismiss(); - }); - } - }]; - } -} diff --git a/src/addon/mod/wiki/providers/edit-link-handler.ts b/src/addon/mod/wiki/providers/edit-link-handler.ts deleted file mode 100644 index b6442157a..000000000 --- a/src/addon/mod/wiki/providers/edit-link-handler.ts +++ /dev/null @@ -1,66 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; - -/** - * Handler to treat links to edit a wiki page. - */ -@Injectable() -export class AddonModWikiEditLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModWikiEditLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModWiki'; - pattern = /\/mod\/wiki\/edit\.php.*([\&\?]pageid=\d+)/; - - constructor(protected linkHelper: CoreContentLinksHelperProvider, protected textUtils: CoreTextUtilsProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - courseId = courseId || params.courseid || params.cid; - - return [{ - action: (siteId, navCtrl?): void => { - - let section = ''; - if (typeof params.section != 'undefined') { - section = params.section.replace(/\+/g, ' '); - } - - const pageParams = { - courseId: courseId, - section: section, - pageId: parseInt(params.pageid, 10) - }; - - this.linkHelper.goInSite(navCtrl, 'AddonModWikiEditPage', pageParams, siteId); - } - }]; - } -} diff --git a/src/addon/mod/wiki/providers/index-link-handler.ts b/src/addon/mod/wiki/providers/index-link-handler.ts deleted file mode 100644 index 8b734e980..000000000 --- a/src/addon/mod/wiki/providers/index-link-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; - -/** - * Handler to treat links to wiki index. - */ -@Injectable() -export class AddonModWikiIndexLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModWikiIndexLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider) { - super(courseHelper, 'AddonModWiki', 'wiki', 'wid'); - } -} diff --git a/src/addon/mod/wiki/providers/list-link-handler.ts b/src/addon/mod/wiki/providers/list-link-handler.ts deleted file mode 100644 index 3645d1be3..000000000 --- a/src/addon/mod/wiki/providers/list-link-handler.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Handler to treat links to wiki list page. - */ -@Injectable() -export class AddonModWikiListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModWikiListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService) { - super(linkHelper, translate, 'AddonModWiki', 'wiki'); - } -} diff --git a/src/addon/mod/wiki/providers/module-handler.ts b/src/addon/mod/wiki/providers/module-handler.ts deleted file mode 100644 index 85e40225d..000000000 --- a/src/addon/mod/wiki/providers/module-handler.ts +++ /dev/null @@ -1,89 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModWikiIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support wiki modules. - */ -@Injectable() -export class AddonModWikiModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModWiki'; - modName = 'wiki'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, - [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, - [CoreConstants.FEATURE_RATE]: false, - [CoreConstants.FEATURE_COMMENT]: true - }; - - constructor(private courseProvider: CoreCourseProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean { - return true; - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_wiki-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModWikiIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModWikiIndexComponent; - } -} diff --git a/src/addon/mod/wiki/providers/page-or-map-link-handler.ts b/src/addon/mod/wiki/providers/page-or-map-link-handler.ts deleted file mode 100644 index d148e4a46..000000000 --- a/src/addon/mod/wiki/providers/page-or-map-link-handler.ts +++ /dev/null @@ -1,111 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModWikiProvider } from './wiki'; - -/** - * Handler to treat links to a wiki page or the wiki map. - */ -@Injectable() -export class AddonModWikiPageOrMapLinkHandler extends CoreContentLinksHandlerBase { - name = 'AddonModWikiPageOrMapLinkHandler'; - featureName = 'CoreCourseModuleDelegate_AddonModWiki'; - pattern = /\/mod\/wiki\/(view|map)\.php.*([\&\?]pageid=\d+)/; - - constructor(protected domUtils: CoreDomUtilsProvider, protected wikiProvider: AddonModWikiProvider, - protected courseHelper: CoreCourseHelperProvider, protected linkHelper: CoreContentLinksHelperProvider) { - super(); - } - - /** - * Get the list of actions for a link (url). - * - * @param siteIds List of sites the URL belongs to. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return List of (or promise resolved with list of) actions. - */ - getActions(siteIds: string[], url: string, params: any, courseId?: number): - CoreContentLinksAction[] | Promise { - - courseId = courseId || params.courseid || params.cid; - - return [{ - action: (siteId, navCtrl?): void => { - const modal = this.domUtils.showModalLoading(), - pageId = parseInt(params.pageid, 10), - action = url.indexOf('mod/wiki/map.php') != -1 ? 'map' : 'page'; - - // Get the page data to obtain wikiId, subwikiId, etc. - this.wikiProvider.getPageContents(pageId, {siteId}).then((page) => { - let promise; - if (courseId) { - promise = Promise.resolve(courseId); - } else { - promise = this.courseHelper.getModuleCourseIdByInstance(page.wikiid, 'wiki', siteId); - } - - return promise.then((courseId) => { - const pageParams = { - courseId: courseId, - pageId: page.id, - pageTitle: page.title, - wikiId: page.wikiid, - subwikiId: page.subwikiid, - action: action - }; - - this.linkHelper.goInSite(navCtrl, 'AddonModWikiIndexPage', pageParams, siteId); - }); - }).catch((error) => { - - this.domUtils.showErrorModalDefault(error, 'addon.mod_wiki.errorloadingpage', true); - }).finally(() => { - modal.dismiss(); - }); - } - }]; - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * If not defined, defaults to true. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - const isMap = url.indexOf('mod/wiki/map.php') != -1; - - if (params.id && !isMap) { - // ID param is more prioritary than pageid in index page, it's a index URL. - return false; - } else if (isMap && typeof params.option != 'undefined' && params.option != 5) { - // Map link but the option isn't "Page list", not supported. - return false; - } - - return true; - } -} diff --git a/src/addon/mod/wiki/providers/prefetch-handler.ts b/src/addon/mod/wiki/providers/prefetch-handler.ts deleted file mode 100644 index af4c3e15b..000000000 --- a/src/addon/mod/wiki/providers/prefetch-handler.ts +++ /dev/null @@ -1,242 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesReadingStrategy, CoreSitesCommonWSOptions } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModWikiProvider } from './wiki'; -import { AddonModWikiSyncProvider } from './wiki-sync'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch wikis. - */ -@Injectable() -export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModWiki'; - modName = 'wiki'; - component = AddonModWikiProvider.COMPONENT; - updatesNames = /^.*files$|^pages$/; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - protected wikiProvider: AddonModWikiProvider, - protected userProvider: CoreUserProvider, - protected textUtils: CoreTextUtilsProvider, - protected courseHelper: CoreCourseHelperProvider, - protected groupsProvider: CoreGroupsProvider, - protected syncProvider: AddonModWikiSyncProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Returns a list of pages that can be downloaded. - * - * @param module The module object returned by WS. - * @param courseId The course ID. - * @param options Other options. - * @return List of pages. - */ - protected getAllPages(module: any, courseId: number, options: CoreSitesCommonWSOptions = {}): Promise { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - return this.wikiProvider.getWiki(courseId, module.id, options).then((wiki) => { - return this.wikiProvider.getWikiPageList(wiki, options); - }).catch(() => { - // Wiki not found, return empty list. - return []; - }); - } - - /** - * Get the download size of a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the size and a boolean indicating if it was able - * to calculate the total size. - */ - getDownloadSize(module: any, courseId: number, single?: boolean): Promise<{ size: number, total: boolean }> { - const promises = [], - siteId = this.sitesProvider.getCurrentSiteId(); - - promises.push(this.getFiles(module, courseId, single, siteId).then((files) => { - return this.pluginFileDelegate.getFilesDownloadSize(files); - })); - - promises.push(this.getAllPages(module, courseId, { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }).then((pages) => { - let size = 0; - - pages.forEach((page) => { - if (page.contentsize) { - size = size + page.contentsize; - } - }); - - return {size: size, total: true}; - })); - - return Promise.all(promises).then((sizes) => { - // Sum values in the array. - return sizes.reduce((a, b) => { - return {size: a.size + b.size, total: a.total && b.total}; - }, {size: 0, total: true}); - }); - } - - /** - * Get list of files. If not defined, we'll assume they're in module.contents. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the list of files. - */ - getFiles(module: any, courseId: number, single?: boolean, siteId?: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.wikiProvider.getWiki(courseId, module.id, {siteId}).then((wiki) => { - const introFiles = this.getIntroFilesFromInstance(module, wiki); - - return this.wikiProvider.getWikiFileList(wiki, {siteId}).then((files) => { - return introFiles.concat(files); - }); - }).catch(() => { - // Wiki not found, return empty list. - return []; - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.wikiProvider.invalidateContent(moduleId, courseId); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - // Get the download time of the package before starting the download (otherwise we'd always get current time). - const siteId = this.sitesProvider.getCurrentSiteId(); - - return this.filepoolProvider.getPackageData(siteId, this.component, module.id).catch(() => { - // No package data yet. - }).then((data) => { - const downloadTime = (data && data.downloadTime) || 0; - - return this.prefetchPackage(module, courseId, single, this.prefetchWiki.bind(this), siteId, [downloadTime]); - }); - } - - /** - * Prefetch a wiki. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @param downloadTime The previous download time, 0 if no previous download. - * @return Promise resolved when done. - */ - protected prefetchWiki(module: any, courseId: number, single: boolean, siteId: string, downloadTime: number): Promise { - const userId = this.sitesProvider.getCurrentSiteUserId(); - - const commonOptions = { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - const modOptions = { - cmId: module.id, - ...commonOptions, // Include all common options. - }; - - // Get the list of pages. - return this.getAllPages(module, courseId, commonOptions).then((pages) => { - const promises = []; - - pages.forEach((page) => { - // Fetch page contents if it needs to be fetched. - if (page.timemodified > downloadTime) { - promises.push(this.wikiProvider.getPageContents(page.id, modOptions)); - } - }); - - // Fetch group data. - promises.push(this.groupsProvider.getActivityGroupInfo(module.id, false, userId, siteId)); - - // Fetch info to provide wiki links. - promises.push(this.wikiProvider.getWiki(courseId, module.id, {siteId}).then((wiki) => { - return this.courseHelper.getModuleCourseIdByInstance(wiki.id, 'wiki', siteId); - })); - - // Get related page files and fetch them. - promises.push(this.getFiles(module, courseId, single, siteId).then((files) => { - return this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id); - })); - - return Promise.all(promises); - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - return this.syncProvider.syncWiki(module.instance, module.course, module.id, siteId); - } -} diff --git a/src/addon/mod/wiki/providers/sync-cron-handler.ts b/src/addon/mod/wiki/providers/sync-cron-handler.ts deleted file mode 100644 index fe854f560..000000000 --- a/src/addon/mod/wiki/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModWikiSyncProvider } from './wiki-sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModWikiSyncCronHandler implements CoreCronHandler { - name = 'AddonModWikiSyncCronHandler'; - - constructor(private wikiSync: AddonModWikiSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.wikiSync.syncAllWikis(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.wikiSync.syncInterval; - } -} diff --git a/src/addon/mod/wiki/providers/tag-area-handler.ts b/src/addon/mod/wiki/providers/tag-area-handler.ts deleted file mode 100644 index a0fcaa301..000000000 --- a/src/addon/mod/wiki/providers/tag-area-handler.ts +++ /dev/null @@ -1,57 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { 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'; - -/** - * Handler to support tags. - */ -@Injectable() -export class AddonModWikiTagAreaHandler implements CoreTagAreaHandler { - name = 'AddonModWikiTagAreaHandler'; - type = 'mod_wiki/wiki_pages'; - - constructor(private tagHelper: CoreTagHelperProvider) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Parses the rendered content of a tag index and returns the items. - * - * @param content Rendered content. - * @return Area items (or promise resolved with the items). - */ - parseContent(content: string): any[] | Promise { - return this.tagHelper.parseFeedContent(content); - } - - /** - * Get the component to use to display items. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return CoreTagFeedComponent; - } -} diff --git a/src/addon/mod/wiki/providers/wiki-offline.ts b/src/addon/mod/wiki/providers/wiki-offline.ts deleted file mode 100644 index 7b98dc034..000000000 --- a/src/addon/mod/wiki/providers/wiki-offline.ts +++ /dev/null @@ -1,276 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; - -/** - * Service to handle offline wiki. - */ -@Injectable() -export class AddonModWikiOfflineProvider { - - protected logger; - - // Variables for database. - static NEW_PAGES_TABLE = 'addon_mod_wiki_new_pages_store'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonModWikiOfflineProvider', - version: 1, - tables: [ - { - name: AddonModWikiOfflineProvider.NEW_PAGES_TABLE, - columns: [ - { - name: 'wikiid', - type: 'INTEGER' - }, - { - name: 'subwikiid', - type: 'INTEGER' - }, - { - name: 'userid', - type: 'INTEGER' - }, - { - name: 'groupid', - type: 'INTEGER' - }, - { - name: 'title', - type: 'TEXT' - }, - { - name: 'cachedcontent', - type: 'TEXT' - }, - { - name: 'contentformat', - type: 'TEXT' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'timecreated', - type: 'INTEGER' - }, - { - name: 'timemodified', - type: 'INTEGER' - }, - { - name: 'caneditpage', - type: 'INTEGER' - } - ], - primaryKeys: ['wikiid', 'subwikiid', 'userid', 'groupid', 'title'] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) { - this.logger = logger.getInstance('AddonModWikiOfflineProvider'); - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Convert a value to a positive number. If not a number or less than 0, 0 will be returned. - * - * @param value Value to convert. - * @return Converted value. - */ - convertToPositiveNumber(value: any): number { - value = parseInt(value, 10); - - return value > 0 ? value : 0; - } - - /** - * Delete a new page. - * - * @param title Title of the page. - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed. - * @param userId User ID. Optional, will be used create subwiki if not informed. - * @param groupId Group ID. Optional, will be used create subwiki if not informed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteNewPage(title: string, subwikiId?: number, wikiId?: number, userId?: number, groupId?: number, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - subwikiId = this.convertToPositiveNumber(subwikiId); - wikiId = this.convertToPositiveNumber(wikiId); - userId = this.convertToPositiveNumber(userId); - groupId = this.convertToPositiveNumber(groupId); - - return site.getDb().deleteRecords(AddonModWikiOfflineProvider.NEW_PAGES_TABLE, { - subwikiid: subwikiId, - wikiid: wikiId, - userid: userId, - groupid: groupId, - title: title - }); - }); - } - - /** - * Get all the stored new pages from all the wikis. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with pages. - */ - getAllNewPages(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getAllRecords(AddonModWikiOfflineProvider.NEW_PAGES_TABLE); - }); - } - - /** - * Get a stored new page. - * - * @param title Title of the page. - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed. - * @param userId User ID. Optional, will be used create subwiki if not informed. - * @param groupId Group ID. Optional, will be used create subwiki if not informed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with page. - */ - getNewPage(title: string, subwikiId?: number, wikiId?: number, userId?: number, groupId?: number, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - subwikiId = this.convertToPositiveNumber(subwikiId); - wikiId = this.convertToPositiveNumber(wikiId); - userId = this.convertToPositiveNumber(userId); - groupId = this.convertToPositiveNumber(groupId); - - return site.getDb().getRecord(AddonModWikiOfflineProvider.NEW_PAGES_TABLE, { - subwikiid: subwikiId, - wikiid: wikiId, - userid: userId, - groupid: groupId, - title: title - }); - }); - } - - /** - * Get all the stored new pages from a certain subwiki. - * - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed. - * @param userId User ID. Optional, will be used create subwiki if not informed. - * @param groupId Group ID. Optional, will be used create subwiki if not informed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with pages. - */ - getSubwikiNewPages(subwikiId?: number, wikiId?: number, userId?: number, groupId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - - subwikiId = this.convertToPositiveNumber(subwikiId); - wikiId = this.convertToPositiveNumber(wikiId); - userId = this.convertToPositiveNumber(userId); - groupId = this.convertToPositiveNumber(groupId); - - return site.getDb().getRecords(AddonModWikiOfflineProvider.NEW_PAGES_TABLE, { - subwikiid: subwikiId, - wikiid: wikiId, - userid: userId, - groupid: groupId - }); - }); - } - - /** - * Get all the stored new pages from a list of subwikis. - * - * @param subwikis List of subwiki. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with pages. - */ - getSubwikisNewPages(subwikis: any[], siteId?: string): Promise { - const promises = []; - let pages = []; - - subwikis.forEach((subwiki) => { - promises.push(this.getSubwikiNewPages(subwiki.id, subwiki.wikiid, subwiki.userid, subwiki.groupid, siteId) - .then((subwikiPages) => { - pages = pages.concat(subwikiPages); - })); - }); - - return Promise.all(promises).then(() => { - return pages; - }); - } - - /** - * Save a new page to be sent later. - * - * @param title Title of the page. - * @param content Content of the page. - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed. - * @param userId User ID. Optional, will be used create subwiki if not informed. - * @param groupId Group ID. Optional, will be used create subwiki if not informed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - saveNewPage(title: string, content: string, subwikiId?: number, wikiId?: number, userId?: number, groupId?: number, - siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const now = new Date().getTime(), - entry = { - title: title, - cachedcontent: content, - subwikiid: this.convertToPositiveNumber(subwikiId), - wikiid: this.convertToPositiveNumber(wikiId), - userid: this.convertToPositiveNumber(userId), - groupid: this.convertToPositiveNumber(groupId), - contentformat: 'html', - timecreated: now, - timemodified: now, - caneditpage: 1 - }; - - return site.getDb().insertRecord(AddonModWikiOfflineProvider.NEW_PAGES_TABLE, entry); - }); - } - - /** - * Check if a list of subwikis have offline data stored. - * - * @param subwikis List of subwikis. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether it has offline data. - */ - subwikisHaveOfflineData(subwikis: any[], siteId?: string): Promise { - return this.getSubwikisNewPages(subwikis, siteId).then((pages) => { - return !!pages.length; - }).catch(() => { - // Error, return false. - return false; - }); - } -} diff --git a/src/addon/mod/wiki/providers/wiki-sync.ts b/src/addon/mod/wiki/providers/wiki-sync.ts deleted file mode 100644 index 4309c741c..000000000 --- a/src/addon/mod/wiki/providers/wiki-sync.ts +++ /dev/null @@ -1,397 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreSyncBaseProvider } from '@classes/base-sync'; -import { AddonModWikiProvider } from './wiki'; -import { AddonModWikiOfflineProvider } from './wiki-offline'; - -/** - * Data returned by a subwiki sync. - */ -export interface AddonModWikiSyncSubwikiResult { - /** - * List of warnings. - */ - warnings: string[]; - - /** - * Whether data was updated in the site. - */ - updated: boolean; - - /** - * List of created pages. - */ - created: {pageId: number, title: string}[]; - - /** - * List of discarded pages. - */ - discarded: {title: string, warning: string}[]; -} - -/** - * Data returned by a wiki sync. - */ -export interface AddonModWikiSyncWikiResult { - /** - * List of warnings. - */ - warnings: string[]; - - /** - * Whether data was updated in the site. - */ - updated: boolean; - - /** - * List of subwikis. - */ - subwikis: {[subwikiId: number]: { - created: {pageId: number, title: string}[], - discarded: {title: string, warning: string}[] - }}; - - /** - * Site ID. - */ - siteId: string; -} - -/** - * Service to sync wikis. - */ -@Injectable() -export class AddonModWikiSyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_wiki_autom_synced'; - static MANUAL_SYNCED = 'addon_mod_wiki_manual_synced'; - - protected componentTranslate: string; - - constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, - syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService, - courseProvider: CoreCourseProvider, private eventsProvider: CoreEventsProvider, - private wikiProvider: AddonModWikiProvider, private wikiOfflineProvider: AddonModWikiOfflineProvider, - private utils: CoreUtilsProvider, private groupsProvider: CoreGroupsProvider, timeUtils: CoreTimeUtilsProvider, - private logHelper: CoreCourseLogHelperProvider) { - - super('AddonModWikiSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils); - - this.componentTranslate = courseProvider.translateModuleName('wiki'); - } - - /** - * Get a string to identify a subwiki. If it doesn't have a subwiki ID it will be identified by wiki ID, user ID and group ID. - * - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @param userId User ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @param groupId Group ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @return Identifier. - */ - getSubwikiBlockId(subwikiId: number, wikiId?: number, userId?: number, groupId?: number): string { - subwikiId = this.wikiOfflineProvider.convertToPositiveNumber(subwikiId); - - if (subwikiId && subwikiId > 0) { - return String(subwikiId); - } - - wikiId = this.wikiOfflineProvider.convertToPositiveNumber(wikiId); - if (wikiId) { - userId = this.wikiOfflineProvider.convertToPositiveNumber(userId); - groupId = this.wikiOfflineProvider.convertToPositiveNumber(groupId); - - return wikiId + ':' + userId + ':' + groupId; - } - } - - /** - * Try to synchronize all the wikis in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllWikis(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all wikis', this.syncAllWikisFunc.bind(this), [force], siteId); - } - - /** - * Sync all wikis on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @param Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllWikisFunc(siteId: string, force?: boolean): Promise { - // Get all the pages created in offline. - return this.wikiOfflineProvider.getAllNewPages(siteId).then((pages) => { - const promises = [], - subwikis = {}; - - // Get subwikis to sync. - pages.forEach((page) => { - const index = this.getSubwikiBlockId(page.subwikiid, page.wikiid, page.userid, page.groupid); - subwikis[index] = page; - }); - - // Sync all subwikis. - for (const id in subwikis) { - const subwiki = subwikis[id]; - - const promise = force ? this.syncSubwiki(subwiki.subwikiid, subwiki.wikiid, subwiki.userid, subwiki.groupid, siteId) - : this.syncSubwikiIfNeeded(subwiki.subwikiid, subwiki.wikiid, subwiki.userid, subwiki.groupid, siteId); - - promises.push(promise.then((result) => { - - if (result && result.updated) { - // Sync successful, send event. - this.eventsProvider.trigger(AddonModWikiSyncProvider.AUTO_SYNCED, { - siteId: siteId, - subwikiId: subwiki.subwikiid, - wikiId: subwiki.wikiid, - userId: subwiki.userid, - groupId: subwiki.groupid, - created: result.created, - discarded: result.discarded, - warnings: result.warnings - }); - } - })); - } - - return Promise.all(promises); - }); - } - - /** - * Sync a subwiki only if a certain time has passed since the last time. - * - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @param userId User ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @param groupId Group ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when subwiki is synced or doesn't need to be synced. - */ - syncSubwikiIfNeeded(subwikiId: number, wikiId?: number, userId?: number, groupId?: number, siteId?: string) - : Promise { - - const blockId = this.getSubwikiBlockId(subwikiId, wikiId, userId, groupId); - - return this.isSyncNeeded(blockId, siteId).then((needed) => { - if (needed) { - return this.syncSubwiki(subwikiId, wikiId, userId, groupId, siteId); - } - }); - } - - /** - * Synchronize a subwiki. - * - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @param userId User ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @param groupId Group ID. Optional, will be used to create the subwiki if subwiki ID not provided. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncSubwiki(subwikiId: number, wikiId?: number, userId?: number, groupId?: number, siteId?: string) - : Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const result: AddonModWikiSyncSubwikiResult = { - warnings: [], - updated: false, - created: [], - discarded: [] - }, - subwikiBlockId = this.getSubwikiBlockId(subwikiId, wikiId, userId, groupId); - - if (this.isSyncing(subwikiBlockId, siteId)) { - // There's already a sync ongoing for this subwiki, return the promise. - return this.getOngoingSync(subwikiBlockId, siteId); - } - - // Verify that subwiki isn't blocked. - if (this.syncProvider.isBlocked(AddonModWikiProvider.COMPONENT, subwikiBlockId, siteId)) { - this.logger.debug('Cannot sync subwiki ' + subwikiBlockId + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug('Try to sync subwiki ' + subwikiBlockId); - - // Get offline responses to be sent. - const syncPromise = this.wikiOfflineProvider.getSubwikiNewPages(subwikiId, wikiId, userId, groupId, siteId).catch(() => { - // No offline data found, return empty array. - return []; - }).then((pages) => { - if (!pages || !pages.length) { - // Nothing to sync. - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(this.translate.instant('core.networkerrormsg')); - } - - const promises = []; - - // Send the pages. - pages.forEach((page) => { - promises.push(this.wikiProvider.newPageOnline(page.title, page.cachedcontent, { - subwikiId, - wikiId, - userId, - groupId, - siteId, - }).then((pageId) => { - - result.updated = true; - - // Add page to created pages array. - result.created.push({ - pageId: pageId, - title: page.title - }); - - // Delete the local page. - return this.wikiOfflineProvider.deleteNewPage(page.title, subwikiId, wikiId, userId, groupId, siteId); - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that the page cannot be submitted. Delete it. - return this.wikiOfflineProvider.deleteNewPage(page.title, subwikiId, wikiId, userId, groupId, siteId) - .then(() => { - - result.updated = true; - - // Page deleted, add the page to discarded pages and add a warning. - const warning = this.translate.instant('core.warningofflinedatadeleted', { - component: this.translate.instant('addon.mod_wiki.wikipage'), - name: page.title, - error: this.textUtils.getErrorMessageFromError(error) - }); - - result.discarded.push({ - title: page.title, - warning: warning - }); - - result.warnings.push(warning); - }); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - })); - }); - - return Promise.all(promises); - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(subwikiBlockId, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // All done, return the warnings. - return result; - }); - - return this.addOngoingSync(subwikiBlockId, syncPromise, siteId); - } - - /** - * Tries to synchronize a wiki. - * - * @param wikiId Wiki ID. - * @param courseId Course ID. - * @param cmId Wiki course module ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncWiki(wikiId: number, courseId?: number, cmId?: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Sync offline logs. - return this.logHelper.syncIfNeeded(AddonModWikiProvider.COMPONENT, wikiId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Sync is done at subwiki level, get all the subwikis. - return this.wikiProvider.getSubwikis(wikiId, {cmId}); - }).then((subwikis) => { - const promises = [], - result: AddonModWikiSyncWikiResult = { - warnings: [], - updated: false, - subwikis: {}, - siteId: siteId - }; - - subwikis.forEach((subwiki) => { - promises.push(this.syncSubwiki(subwiki.id, subwiki.wikiid, subwiki.userid, subwiki.groupid, siteId).then((data) => { - if (data && data.updated) { - result.warnings = result.warnings.concat(data.warnings); - result.updated = true; - result.subwikis[subwiki.id] = { - created: data.created, - discarded: data.discarded - }; - } - })); - }); - - return Promise.all(promises).then(() => { - const promises = []; - - if (result.updated) { - // Something has changed, invalidate data. - if (wikiId) { - promises.push(this.wikiProvider.invalidateSubwikis(wikiId)); - promises.push(this.wikiProvider.invalidateSubwikiPages(wikiId)); - promises.push(this.wikiProvider.invalidateSubwikiFiles(wikiId)); - } - if (courseId) { - promises.push(this.wikiProvider.invalidateWikiData(courseId)); - } - if (cmId) { - promises.push(this.groupsProvider.invalidateActivityAllowedGroups(cmId)); - promises.push(this.groupsProvider.invalidateActivityGroupMode(cmId)); - } - } - - return Promise.all(promises).catch(() => { - // Ignore errors. - }).then(() => { - return result; - }); - }); - }); - } -} diff --git a/src/addon/mod/wiki/providers/wiki.ts b/src/addon/mod/wiki/providers/wiki.ts deleted file mode 100644 index 76c000570..000000000 --- a/src/addon/mod/wiki/providers/wiki.ts +++ /dev/null @@ -1,859 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModWikiOfflineProvider } from './wiki-offline'; -import { CoreSite } from '@classes/site'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -export interface AddonModWikiSubwikiListData { - /** - * Number of subwikis. - */ - count: number; - - /** - * Subwiki ID currently selected. - */ - subwikiSelected: number; - - /** - * User of the subwiki currently selected. - */ - userSelected: number; - - /** - * Group of the subwiki currently selected. - */ - groupSelected: number; - - /** - * List of subwikis. - */ - subwikis: any[]; -} - -/** - * Service that provides some features for wikis. - */ -@Injectable() -export class AddonModWikiProvider { - static COMPONENT = 'mmaModWiki'; - static PAGE_CREATED_EVENT = 'addon_mod_wiki_page_created'; - static RENEW_LOCK_TIME = 30000; // Milliseconds. - - protected ROOT_CACHE_KEY = 'mmaModWiki:'; - protected logger; - protected subwikiListsCache: {[wikiId: number]: AddonModWikiSubwikiListData} = {}; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, - private filepoolProvider: CoreFilepoolProvider, private utils: CoreUtilsProvider, private translate: TranslateService, - private wikiOffline: AddonModWikiOfflineProvider, eventsProvider: CoreEventsProvider, - private logHelper: CoreCourseLogHelperProvider) { - this.logger = logger.getInstance('AddonModWikiProvider'); - - // Clear subwiki lists cache on logout. - eventsProvider.on(CoreEventsProvider.LOGIN, () => { - this.clearSubwikiList(); - }); - } - - /** - * Clear subwiki list cache for a certain wiki or all of them. - * - * @param wikiId wiki Id, if not provided all will be cleared. - */ - clearSubwikiList(wikiId?: number): void { - if (typeof wikiId == 'undefined') { - this.subwikiListsCache = {}; - } else { - delete this.subwikiListsCache[wikiId]; - } - } - - /** - * Save wiki contents on a page or section. - * - * @param pageId Page ID. - * @param content content to be saved. - * @param section section to get. - * @return Promise resolved with the page ID. - */ - editPage(pageId: number, content: string, section?: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params: any = { - pageid: pageId, - content: content - }; - - if (section) { - params.section = section; - } - - return site.write('mod_wiki_edit_page', params).then((response) => { - return response.pageid || Promise.reject(null); - }); - }); - } - - /** - * Get a wiki page contents. - * - * @param pageId Page ID. - * @param options Other options. - * @return Promise resolved with the page data. - */ - getPageContents(pageId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - pageid: pageId, - }; - const preSets = { - cacheKey: this.getPageContentsCacheKey(pageId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModWikiProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_wiki_get_page_contents', params, preSets).then((response) => { - return response.page || Promise.reject(null); - }); - }); - } - - /** - * Get cache key for wiki Pages Contents WS calls. - * - * @param pageId Wiki Page ID. - * @return Cache key. - */ - protected getPageContentsCacheKey(pageId: number): string { - return this.ROOT_CACHE_KEY + 'page:' + pageId; - } - - /** - * Get a wiki page contents for editing. It does not cache calls. - * - * @param pageId Page ID. - * @param section Section to get. - * @param lockOnly Just renew lock and not return content. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with page contents. - */ - getPageForEditing(pageId: number, section?: string, lockOnly?: boolean, siteId?: string): Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - const params: any = { - pageid: pageId - }; - - if (section) { - params.section = section; - } - - // This parameter requires Moodle 3.2. It saves network usage. - if (lockOnly && site.isVersionGreaterEqualThan('3.2')) { - params.lockonly = 1; - } - - return site.write('mod_wiki_get_page_for_editing', params).then((response) => { - return response.pagesection || Promise.reject(null); - }); - }); - } - - /** - * Gets the list of files from a specific subwiki. - * - * @param wikiId Wiki ID. - * @param options Other options. - * @return Promise resolved with subwiki files. - */ - getSubwikiFiles(wikiId: number, options: AddonModWikiGetSubwikiFilesOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const groupId = options.groupId || -1; - const userId = options.userId || 0; - - const params = { - wikiid: wikiId, - groupid: groupId, - userid: userId, - }; - const preSets = { - cacheKey: this.getSubwikiFilesCacheKey(wikiId, groupId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModWikiProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_wiki_get_subwiki_files', params, preSets).then((response) => { - return response.files || Promise.reject(null); - }); - }); - } - - /** - * Get cache key for wiki Subwiki Files WS calls. - * - * @param wikiId Wiki ID. - * @param groupId Group ID. - * @param userId User ID. - * @return Cache key. - */ - protected getSubwikiFilesCacheKey(wikiId: number, groupId: number, userId: number): string { - return this.getSubwikiFilesCacheKeyPrefix(wikiId) + ':' + groupId + ':' + userId; - } - - /** - * Get cache key for all wiki Subwiki Files WS calls. - * - * @param wikiId Wiki ID. - * @return Cache key. - */ - protected getSubwikiFilesCacheKeyPrefix(wikiId: number): string { - return this.ROOT_CACHE_KEY + 'subwikifiles:' + wikiId; - } - - /** - * Get a list of subwikis and related data for a certain wiki from the cache. - * - * @param wikiId wiki Id - * @return Subwiki list and related data. - */ - getSubwikiList(wikiId: number): AddonModWikiSubwikiListData { - return this.subwikiListsCache[wikiId]; - } - - /** - * Get the list of Pages of a SubWiki. - * - * @param wikiId Wiki ID. - * @param options Other options. - * @return Promise resolved with wiki subwiki pages. - */ - getSubwikiPages(wikiId: number, options: AddonModWikiGetSubwikiPagesOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const groupId = options.groupId || -1; - const userId = options.userId || 0; - const sortBy = options.sortBy || 'title'; - const sortDirection = options.sortDirection || 'ASC'; - - const params = { - wikiid: wikiId, - groupid: groupId, - userid: userId, - options: { - sortby: sortBy, - sortdirection: sortDirection, - includecontent: options.includeContent ? 1 : 0, - }, - }; - const preSets = { - cacheKey: this.getSubwikiPagesCacheKey(wikiId, groupId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES, - component: AddonModWikiProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_wiki_get_subwiki_pages', params, preSets).then((response) => { - return response.pages || Promise.reject(null); - }); - }); - } - - /** - * Get cache key for wiki Subwiki Pages WS calls. - * - * @param wikiId Wiki ID. - * @param groupId Group ID. - * @param userId User ID. - * @return Cache key. - */ - protected getSubwikiPagesCacheKey(wikiId: number, groupId: number, userId: number): string { - return this.getSubwikiPagesCacheKeyPrefix(wikiId) + ':' + groupId + ':' + userId; - } - - /** - * Get cache key for all wiki Subwiki Pages WS calls. - * - * @param wikiId Wiki ID. - * @return Cache key. - */ - protected getSubwikiPagesCacheKeyPrefix(wikiId: number): string { - return this.ROOT_CACHE_KEY + 'subwikipages:' + wikiId; - } - - /** - * Get all the subwikis of a wiki. - * - * @param wikiId Wiki ID. - * @param options Other options. - * @return Promise resolved with subwikis. - */ - getSubwikis(wikiId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - wikiid: wikiId, - }; - const preSets = { - cacheKey: this.getSubwikisCacheKey(wikiId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModWikiProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_wiki_get_subwikis', params, preSets).then((response) => { - return response.subwikis || Promise.reject(null); - }); - }); - } - - /** - * Get cache key for get wiki subWikis WS calls. - * - * @param wikiId Wiki ID. - * @return Cache key. - */ - protected getSubwikisCacheKey(wikiId: number): string { - return this.ROOT_CACHE_KEY + 'subwikis:' + wikiId; - } - - /** - * Get a wiki by module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the wiki is retrieved. - */ - getWiki(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getWikiByField(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a wiki with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the wiki is retrieved. - */ - protected getWikiByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getWikiDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModWikiProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_wiki_get_wikis_by_courses', params, preSets).then((response) => { - if (response.wikis) { - const currentWiki = response.wikis.find((wiki) => { - return wiki[key] == value; - }); - - if (currentWiki) { - return currentWiki; - } - } - - return Promise.reject(null); - }); - }); - } - - /** - * Get a wiki by wiki ID. - * - * @param courseId Course ID. - * @param id Wiki ID. - * @param options Other options. - * @return Promise resolved when the wiki is retrieved. - */ - getWikiById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getWikiByField(courseId, 'id', id, options); - } - - /** - * Get cache key for wiki data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getWikiDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'wiki:' + courseId; - } - - /** - * Gets a list of files to download for a wiki, using a format similar to module.contents from get_course_contents. - * - * @param wiki Wiki. - * @param options Other options. - * @return Promise resolved with the list of files. - */ - getWikiFileList(wiki: any, options: CoreSitesCommonWSOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - let files = []; - const modOptions = { - cmId: wiki.coursemodule, - ...options, // Include all options. - }; - - return this.getSubwikis(wiki.id, modOptions).then((subwikis) => { - const promises = []; - - subwikis.forEach((subwiki) => { - const subwikiOptions = { - groupId: subwiki.groupid, - userId: subwiki.userid, - ...modOptions, // Include all options. - }; - - promises.push(this.getSubwikiFiles(subwiki.wikiid, subwikiOptions).then((swFiles) => { - files = files.concat(swFiles); - })); - }); - - return Promise.all(promises).then(() => { - return files; - }); - }); - } - - /** - * Gets a list of all pages for a Wiki. - * - * @param wiki Wiki. - * @param options Other options. - * @return Page list. - */ - getWikiPageList(wiki: any, options: CoreSitesCommonWSOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - let pages = []; - const modOptions = { - cmId: wiki.coursemodule, - ...options, // Include all options. - }; - - return this.getSubwikis(wiki.id, modOptions).then((subwikis) => { - const promises = []; - - subwikis.forEach((subwiki) => { - promises.push(this.getSubwikiPages(subwiki.wikiid, { - groupId: subwiki.groupid, - userId: subwiki.userid, - ...modOptions, // Include all options. - }).then((subwikiPages) => { - pages = pages.concat(subwikiPages); - })); - }); - - return Promise.all(promises).then(() => { - return pages; - }); - }); - } - - /** - * Invalidate the prefetched content except files. - * To invalidate files, use invalidateFiles. - * - * @param moduleId The module ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.getWiki(courseId, moduleId, {siteId}).then((wiki) => { - const promises = []; - - promises.push(this.invalidateWikiData(courseId, siteId)); - promises.push(this.invalidateSubwikis(wiki.id, siteId)); - promises.push(this.invalidateSubwikiPages(wiki.id, siteId)); - promises.push(this.invalidateSubwikiFiles(wiki.id, siteId)); - - return Promise.all(promises); - }); - } - - /** - * Invalidate the prefetched files. - * - * @param moduleId The module ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the files are invalidated. - */ - invalidateFiles(moduleId: number, siteId?: string): Promise { - return this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModWikiProvider.COMPONENT, moduleId); - } - - /** - * Invalidates page content WS call for a certain page. - * - * @param pageId Wiki Page ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidatePage(pageId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getPageContentsCacheKey(pageId)); - }); - } - - /** - * Invalidates all the subwiki files WS calls for a certain wiki. - * - * @param wikiId Wiki ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSubwikiFiles(wikiId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getSubwikiFilesCacheKeyPrefix(wikiId)); - }); - } - - /** - * Invalidates all the subwiki pages WS calls for a certain wiki. - * - * @param wikiId Wiki ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSubwikiPages(wikiId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getSubwikiPagesCacheKeyPrefix(wikiId)); - }); - } - - /** - * Invalidates all the get subwikis WS calls for a certain wiki. - * - * @param wikiId Wiki ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSubwikis(wikiId: number, siteId?: string): Promise { - this.clearSubwikiList(wikiId); - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getSubwikisCacheKey(wikiId)); - }); - } - - /** - * Invalidates wiki data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateWikiData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getWikiDataCacheKey(courseId)); - }); - } - - /** - * Check if a page title is already used. - * - * @param wikiId Wiki ID. - * @param subwikiId Subwiki ID. - * @param title Page title. - * @param options Other options. - * @return Promise resolved with true if used, resolved with false if not used or cannot determine. - */ - isTitleUsed(wikiId: number, subwikiId: number, title: string, options: CoreCourseCommonModWSOptions = {}): Promise { - - // First get the subwiki. - return this.getSubwikis(wikiId, options).then((subwikis) => { - // Search the subwiki. - const subwiki = subwikis.find((subwiki) => { - return subwiki.id == subwikiId; - }); - - return subwiki || Promise.reject(null); - }).then((subwiki) => { - // Now get all the pages of the subwiki. - return this.getSubwikiPages(wikiId, { - groupId: subwiki.groupid, - userId: subwiki.userid, - ...options, // Include all options. - }); - }).then((pages) => { - // Check if there's any page with the same title. - const page = pages.find((page) => { - return page.title == title; - }); - - return !!page; - }).catch(() => { - return false; - }); - } - - /** - * Report a wiki page as being viewed. - * - * @param id Page ID. - * @param wikiId Wiki ID. - * @param name Name of the wiki. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logPageView(id: number, wikiId: number, name?: string, siteId?: string): Promise { - const params = { - pageid: id - }; - - return this.logHelper.logSingle('mod_wiki_view_page', params, AddonModWikiProvider.COMPONENT, wikiId, name, 'wiki', - params, siteId); - } - - /** - * Report the wiki as being viewed. - * - * @param id Wiki ID. - * @param name Name of the wiki. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - wikiid: id - }; - - return this.logHelper.logSingle('mod_wiki_view_wiki', params, AddonModWikiProvider.COMPONENT, id, name, 'wiki', {}, - siteId); - } - - /** - * Create a new page on a subwiki. - * - * @param title Title to create the page. - * @param content Content to save on the page. - * @param options Other options. - * @return Promise resolved with page ID if page was created in server, -1 if stored in device. - */ - newPage(title: string, content: string, options: AddonModWikiNewPageOptions = {}): Promise { - - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a new page to be synchronized later. - const storeOffline = (): Promise => { - let promise; - - if (options.wikiId) { - // We have wiki ID, check if there's already an online page with this title and subwiki. - promise = this.isTitleUsed(options.wikiId, options.subwikiId, title, { - cmId: options.cmId, - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId: options.siteId, - }).catch(() => { - // Error, assume not used. - return false; - }).then((used) => { - if (used) { - return Promise.reject(this.translate.instant('addon.mod_wiki.pageexists')); - } - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - return this.wikiOffline.saveNewPage(title, content, options.subwikiId, options.wikiId, options.userId, - options.groupId, options.siteId).then(() => { - return -1; - }); - }); - }; - - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - // Discard stored content for this page. If it exists it means the user is editing it. - return this.wikiOffline.deleteNewPage(title, options.subwikiId, options.wikiId, options.userId, options.groupId, - options.siteId).then(() => { - // Try to create it in online. - return this.newPageOnline(title, content, options).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that the page cannot be added. - return Promise.reject(error); - } else { - // Couldn't connect to server, store in offline. - return storeOffline(); - } - }); - }); - } - - /** - * Create a new page on a subwiki. It will fail if offline or cannot connect. - * - * @param title Title to create the page. - * @param content Content to save on the page. - * @param options Other options. - * @return Promise resolved with the page ID if created, rejected otherwise. - */ - newPageOnline(title: string, content: string, options: AddonModWikiNewPageOnlineOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params: any = { - title: title, - content: content, - contentformat: 'html', - }; - - const subwikiId = this.wikiOffline.convertToPositiveNumber(options.subwikiId); - const wikiId = this.wikiOffline.convertToPositiveNumber(options.wikiId); - - if (subwikiId && subwikiId > 0) { - params.subwikiid = subwikiId; - } else if (wikiId) { - params.wikiid = wikiId; - params.userid = this.wikiOffline.convertToPositiveNumber(options.userId); - params.groupid = this.wikiOffline.convertToPositiveNumber(options.groupId); - } - - return site.write('mod_wiki_new_page', params).then((response) => { - return response.pageid || Promise.reject(null); - }); - }); - } - - /** - * Save subwiki list for a wiki to the cache. - * - * @param wikiId Wiki Id. - * @param subwikis List of subwikis. - * @param count Number of subwikis in the subwikis list. - * @param subwikiId Subwiki Id currently selected. - * @param userId User Id currently selected. - * @param groupId Group Id currently selected. - */ - setSubwikiList(wikiId: number, subwikis: any[], count: number, subwikiId: number, userId: number, groupId: number): void { - this.subwikiListsCache[wikiId] = { - count: count, - subwikiSelected: subwikiId, - userSelected: userId, - groupSelected: groupId, - subwikis: subwikis - }; - } - - /** - * Sort an array of wiki pages by title. - * - * @param pages Pages to sort. - * @param desc True to sort in descendent order, false to sort in ascendent order. Defaults to false. - * @return Sorted pages. - */ - sortPagesByTitle(pages: any[], desc?: boolean): any[] { - return pages.sort((a, b) => { - let result = a.title >= b.title ? 1 : -1; - - if (desc) { - result = -result; - } - - return result; - }); - } - - /** - * Check if a wiki has a certain subwiki. - * - * @param wikiId Wiki ID. - * @param subwikiId Subwiki ID to search. - * @param options Other options. - * @return Promise resolved with true if it has subwiki, resolved with false otherwise. - */ - wikiHasSubwiki(wikiId: number, subwikiId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - // Get the subwikis to check if any of them matches the one passed as param. - return this.getSubwikis(wikiId, options).then((subwikis) => { - const subwiki = subwikis.find((subwiki) => { - return subwiki.id == subwikiId; - }); - - return !!subwiki; - }).catch(() => { - // Not found, return false. - return false; - }); - } -} - -/** - * Options to pass to getSubwikiFiles. - */ -export type AddonModWikiGetSubwikiFilesOptions = CoreCourseCommonModWSOptions & { - userId?: number; // User to get files from. - groupId?: number; // Group to get files from. -}; - -/** - * Options to pass to getSubwikiPages. - */ -export type AddonModWikiGetSubwikiPagesOptions = CoreCourseCommonModWSOptions & { - userId?: number; // User to get pages from. - groupId?: number; // Group to get pages from. - sortBy?: string; // The attribute to sort the returned list. Defaults to 'title'. - sortDirection?: string; // Direction to sort the returned list (ASC | DESC). Defaults to 'ASC'. - includeContent?: boolean; // Whether the pages have to include their content. -}; - -/** - * Options to pass to newPageOnline. - */ -export type AddonModWikiNewPageOnlineOptions = { - subwikiId?: number; // Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - wikiId?: number; // Wiki ID. Optional, will be used to create a new subwiki if subwikiId not supplied. - userId?: number; // User ID. Optional, will be used to create a new subwiki if subwikiId not supplied. - groupId?: number; // Group ID. Optional, will be used to create a new subwiki if subwikiId not supplied. - siteId?: string; // Site ID. If not defined, current site. -}; - -/** - * Options to pass to newPage. - */ -export type AddonModWikiNewPageOptions = AddonModWikiNewPageOnlineOptions & { - cmId?: number; // Module ID. -}; diff --git a/src/addon/mod/wiki/wiki.module.ts b/src/addon/mod/wiki/wiki.module.ts deleted file mode 100644 index f8e576209..000000000 --- a/src/addon/mod/wiki/wiki.module.ts +++ /dev/null @@ -1,82 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate'; -import { AddonModWikiComponentsModule } from './components/components.module'; -import { AddonModWikiProvider } from './providers/wiki'; -import { AddonModWikiOfflineProvider } from './providers/wiki-offline'; -import { AddonModWikiSyncProvider } from './providers/wiki-sync'; -import { AddonModWikiModuleHandler } from './providers/module-handler'; -import { AddonModWikiPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModWikiSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonModWikiIndexLinkHandler } from './providers/index-link-handler'; -import { AddonModWikiPageOrMapLinkHandler } from './providers/page-or-map-link-handler'; -import { AddonModWikiCreateLinkHandler } from './providers/create-link-handler'; -import { AddonModWikiEditLinkHandler } from './providers/edit-link-handler'; -import { AddonModWikiListLinkHandler } from './providers/list-link-handler'; -import { AddonModWikiTagAreaHandler } from './providers/tag-area-handler'; - -// List of providers (without handlers). -export const ADDON_MOD_WIKI_PROVIDERS: any[] = [ - AddonModWikiProvider, - AddonModWikiOfflineProvider, - AddonModWikiSyncProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModWikiComponentsModule - ], - providers: [ - AddonModWikiProvider, - AddonModWikiOfflineProvider, - AddonModWikiSyncProvider, - AddonModWikiModuleHandler, - AddonModWikiPrefetchHandler, - AddonModWikiSyncCronHandler, - AddonModWikiIndexLinkHandler, - AddonModWikiPageOrMapLinkHandler, - AddonModWikiCreateLinkHandler, - AddonModWikiEditLinkHandler, - AddonModWikiListLinkHandler, - AddonModWikiTagAreaHandler - ] -}) -export class AddonModWikiModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModWikiModuleHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModWikiPrefetchHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModWikiSyncCronHandler, linksDelegate: CoreContentLinksDelegate, - indexHandler: AddonModWikiIndexLinkHandler, pageOrMapHandler: AddonModWikiPageOrMapLinkHandler, - createHandler: AddonModWikiCreateLinkHandler, editHandler: AddonModWikiEditLinkHandler, - listLinkHandler: AddonModWikiListLinkHandler, - tagAreaDelegate: CoreTagAreaDelegate, tagAreaHandler: AddonModWikiTagAreaHandler) { - - moduleDelegate.registerHandler(moduleHandler); - prefetchDelegate.registerHandler(prefetchHandler); - cronDelegate.register(syncHandler); - linksDelegate.registerHandler(indexHandler); - linksDelegate.registerHandler(pageOrMapHandler); - linksDelegate.registerHandler(createHandler); - linksDelegate.registerHandler(editHandler); - linksDelegate.registerHandler(listLinkHandler); - tagAreaDelegate.registerHandler(tagAreaHandler); - } -} diff --git a/src/addon/mod/workshop/assessment/accumulative/accumulative.module.ts b/src/addon/mod/workshop/assessment/accumulative/accumulative.module.ts deleted file mode 100644 index 79091aeeb..000000000 --- a/src/addon/mod/workshop/assessment/accumulative/accumulative.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopAssessmentStrategyAccumulativeComponent } from './component/accumulative'; -import { AddonModWorkshopAssessmentStrategyAccumulativeHandler } from './providers/handler'; -import { AddonWorkshopAssessmentStrategyDelegate } from '../../providers/assessment-strategy-delegate'; - -@NgModule({ - declarations: [ - AddonModWorkshopAssessmentStrategyAccumulativeComponent, - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModWorkshopAssessmentStrategyAccumulativeHandler - ], - exports: [ - AddonModWorkshopAssessmentStrategyAccumulativeComponent - ], - entryComponents: [ - AddonModWorkshopAssessmentStrategyAccumulativeComponent - ] -}) -export class AddonModWorkshopAssessmentStrategyAccumulativeModule { - constructor(strategyDelegate: AddonWorkshopAssessmentStrategyDelegate, - strategyHandler: AddonModWorkshopAssessmentStrategyAccumulativeHandler) { - strategyDelegate.registerHandler(strategyHandler); - } -} diff --git a/src/addon/mod/workshop/assessment/accumulative/component/accumulative.ts b/src/addon/mod/workshop/assessment/accumulative/component/accumulative.ts deleted file mode 100644 index 0e7aefb26..000000000 --- a/src/addon/mod/workshop/assessment/accumulative/component/accumulative.ts +++ /dev/null @@ -1,26 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { AddonModWorkshopAssessmentStrategyComponentBase } from '../../../classes/assessment-strategy-component'; - -/** - * Component for accumulative assessment strategy. - */ -@Component({ - selector: 'addon-mod-workshop-assessment-strategy-accumulative', - templateUrl: 'addon-mod-workshop-assessment-strategy-accumulative.html', -}) -export class AddonModWorkshopAssessmentStrategyAccumulativeComponent extends AddonModWorkshopAssessmentStrategyComponentBase { -} diff --git a/src/addon/mod/workshop/assessment/accumulative/component/addon-mod-workshop-assessment-strategy-accumulative.html b/src/addon/mod/workshop/assessment/accumulative/component/addon-mod-workshop-assessment-strategy-accumulative.html deleted file mode 100644 index 229a181d3..000000000 --- a/src/addon/mod/workshop/assessment/accumulative/component/addon-mod-workshop-assessment-strategy-accumulative.html +++ /dev/null @@ -1,29 +0,0 @@ - - - -

{{ field.dimtitle }}

- -
- - {{ 'addon.mod_workshop_assessment_accumulative.dimensiongradefor' | translate : {'$a': field.dimtitle } }} - - {{grade.label}} - - - - -

{{ 'addon.mod_workshop_assessment_accumulative.dimensiongradefor' | translate : {'$a': field.dimtitle } }}

- -

{{grade.label}}

-
-
- - {{ 'addon.mod_workshop_assessment_accumulative.dimensioncommentfor' | translate : {'$a': field.dimtitle } }} - - - -

{{ 'addon.mod_workshop_assessment_accumulative.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}

-

-
-
-
diff --git a/src/addon/mod/workshop/assessment/accumulative/lang/en.json b/src/addon/mod/workshop/assessment/accumulative/lang/en.json deleted file mode 100644 index 72c26cfa8..000000000 --- a/src/addon/mod/workshop/assessment/accumulative/lang/en.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dimensioncommentfor": "Comment for {{$a}}", - "dimensiongradefor": "Grade for {{$a}}", - "dimensionnumber": "Aspect {{$a}}", - "mustchoosegrade": "You have to select a grade for this aspect" -} diff --git a/src/addon/mod/workshop/assessment/accumulative/providers/handler.ts b/src/addon/mod/workshop/assessment/accumulative/providers/handler.ts deleted file mode 100644 index a2fa5142c..000000000 --- a/src/addon/mod/workshop/assessment/accumulative/providers/handler.ts +++ /dev/null @@ -1,150 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; -import { AddonWorkshopAssessmentStrategyHandler } from '../../../providers/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyAccumulativeComponent } from '../component/accumulative'; - -/** - * Handler for accumulative assessment strategy plugin. - */ -@Injectable() -export class AddonModWorkshopAssessmentStrategyAccumulativeHandler implements AddonWorkshopAssessmentStrategyHandler { - name = 'AddonModWorkshopAssessmentStrategyAccumulative'; - strategyName = 'accumulative'; - - constructor(private translate: TranslateService, private gradesHelper: CoreGradesHelperProvider) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Return the Component to render the plugin. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonModWorkshopAssessmentStrategyAccumulativeComponent; - } - - /** - * Prepare original values to be shown and compared. - * - * @param form Original data of the form. - * @param workshopId WorkShop Id - * @return Promise resolved with original values sorted. - */ - getOriginalValues(form: any, workshopId: number): Promise { - const defaultGrade = this.translate.instant('core.choosedots'), - originalValues = [], - promises = []; - - form.fields.forEach((field, n) => { - field.dimtitle = this.translate.instant( - 'addon.mod_workshop_assessment_accumulative.dimensionnumber', {$a: field.number}); - - if (!form.current[n]) { - form.current[n] = {}; - } - - originalValues[n] = { - peercomment: form.current[n].peercomment || '', - number: field.number - }; - - form.current[n].grade = form.current[n].grade ? parseInt(form.current[n].grade, 10) : -1; - - const gradingType = parseInt(field.grade, 10); - const dimension = form.dimensionsinfo.find((dimension) => dimension.id == field.dimensionid); - const scale = dimension && gradingType < 0 ? dimension.scale : null; - - promises.push(this.gradesHelper.makeGradesMenu(gradingType, undefined, defaultGrade, -1, scale).then((grades) => { - field.grades = grades; - originalValues[n].grade = form.current[n].grade; - })); - }); - - return Promise.all(promises).then(() => { - return originalValues; - }); - } - - /** - * Check if the assessment data has changed for a certain submission and workshop for a this strategy plugin. - * - * @param originalValues Original values of the form. - * @param currentValues Current values of the form. - * @return True if data has changed, false otherwise. - */ - hasDataChanged(originalValues: any[], currentValues: any[]): boolean { - for (const x in originalValues) { - if (originalValues[x].grade != currentValues[x].grade) { - return true; - } - if (originalValues[x].peercomment != currentValues[x].peercomment) { - return true; - } - } - - return false; - } - - /** - * Prepare assessment data to be sent to the server depending on the strategy selected. - * - * @param currentValues Current values of the form. - * @param form Assessment form data. - * @return Promise resolved with the data to be sent. Or rejected with the input errors object. - */ - prepareAssessmentData(currentValues: any[], form: any): Promise { - const data = {}; - const errors = {}; - let hasErrors = false; - - form.fields.forEach((field, idx) => { - if (idx < form.dimenssionscount) { - const grade = parseInt(currentValues[idx].grade, 10); - if (!isNaN(grade) && grade >= 0) { - data['grade__idx_' + idx] = grade; - } else { - errors['grade_' + idx] = this.translate.instant('addon.mod_workshop_assessment_accumulative.mustchoosegrade'); - hasErrors = true; - } - - if (currentValues[idx].peercomment) { - data['peercomment__idx_' + idx] = currentValues[idx].peercomment; - } - - data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; - data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); - data['weight__idx_' + idx] = parseInt(field.weight, 10) || 0; - } - }); - - if (hasErrors) { - return Promise.reject(errors); - } - - return Promise.resolve(data); - } -} diff --git a/src/addon/mod/workshop/assessment/assessment.module.ts b/src/addon/mod/workshop/assessment/assessment.module.ts deleted file mode 100644 index 10855c8fb..000000000 --- a/src/addon/mod/workshop/assessment/assessment.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonModWorkshopAssessmentStrategyAccumulativeModule } from './accumulative/accumulative.module'; -import { AddonModWorkshopAssessmentStrategyCommentsModule } from './comments/comments.module'; -import { AddonModWorkshopAssessmentStrategyNumErrorsModule } from './numerrors/numerrors.module'; -import { AddonModWorkshopAssessmentStrategyRubricModule } from './rubric/rubric.module'; - -@NgModule({ - imports: [ - AddonModWorkshopAssessmentStrategyAccumulativeModule, - AddonModWorkshopAssessmentStrategyCommentsModule, - AddonModWorkshopAssessmentStrategyNumErrorsModule, - AddonModWorkshopAssessmentStrategyRubricModule, - ] -}) -export class AddonModWorkshopAssessmentStrategyModule {} diff --git a/src/addon/mod/workshop/assessment/comments/comments.module.ts b/src/addon/mod/workshop/assessment/comments/comments.module.ts deleted file mode 100644 index 1feb5958a..000000000 --- a/src/addon/mod/workshop/assessment/comments/comments.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopAssessmentStrategyCommentsComponent } from './component/comments'; -import { AddonModWorkshopAssessmentStrategyCommentsHandler } from './providers/handler'; -import { AddonWorkshopAssessmentStrategyDelegate } from '../../providers/assessment-strategy-delegate'; - -@NgModule({ - declarations: [ - AddonModWorkshopAssessmentStrategyCommentsComponent, - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModWorkshopAssessmentStrategyCommentsHandler - ], - exports: [ - AddonModWorkshopAssessmentStrategyCommentsComponent - ], - entryComponents: [ - AddonModWorkshopAssessmentStrategyCommentsComponent - ] -}) -export class AddonModWorkshopAssessmentStrategyCommentsModule { - constructor(strategyDelegate: AddonWorkshopAssessmentStrategyDelegate, - strategyHandler: AddonModWorkshopAssessmentStrategyCommentsHandler) { - strategyDelegate.registerHandler(strategyHandler); - } -} diff --git a/src/addon/mod/workshop/assessment/comments/component/addon-mod-workshop-assessment-strategy-comments.html b/src/addon/mod/workshop/assessment/comments/component/addon-mod-workshop-assessment-strategy-comments.html deleted file mode 100644 index 7b2a324d4..000000000 --- a/src/addon/mod/workshop/assessment/comments/component/addon-mod-workshop-assessment-strategy-comments.html +++ /dev/null @@ -1,17 +0,0 @@ - - - -

{{ field.dimtitle }}

- -
- - {{ 'addon.mod_workshop_assessment_comments.dimensioncommentfor' | translate : {'$a': field.dimtitle } }} - - - - -

{{ 'addon.mod_workshop_assessment_comments.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}

-

-
-
-
diff --git a/src/addon/mod/workshop/assessment/comments/component/comments.ts b/src/addon/mod/workshop/assessment/comments/component/comments.ts deleted file mode 100644 index da83af810..000000000 --- a/src/addon/mod/workshop/assessment/comments/component/comments.ts +++ /dev/null @@ -1,26 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { AddonModWorkshopAssessmentStrategyComponentBase } from '../../../classes/assessment-strategy-component'; - -/** - * Component for comments assessment strategy. - */ -@Component({ - selector: 'addon-mod-workshop-assessment-strategy-comments', - templateUrl: 'addon-mod-workshop-assessment-strategy-comments.html', -}) -export class AddonModWorkshopAssessmentStrategyCommentsComponent extends AddonModWorkshopAssessmentStrategyComponentBase { -} diff --git a/src/addon/mod/workshop/assessment/comments/lang/en.json b/src/addon/mod/workshop/assessment/comments/lang/en.json deleted file mode 100644 index 6db857323..000000000 --- a/src/addon/mod/workshop/assessment/comments/lang/en.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "dimensioncommentfor": "Comment for {{$a}}", - "dimensionnumber": "Aspect {{$a}}" -} diff --git a/src/addon/mod/workshop/assessment/comments/providers/handler.ts b/src/addon/mod/workshop/assessment/comments/providers/handler.ts deleted file mode 100644 index 592a84480..000000000 --- a/src/addon/mod/workshop/assessment/comments/providers/handler.ts +++ /dev/null @@ -1,124 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonWorkshopAssessmentStrategyHandler } from '../../../providers/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyCommentsComponent } from '../component/comments'; - -/** - * Handler for comments assessment strategy plugin. - */ -@Injectable() -export class AddonModWorkshopAssessmentStrategyCommentsHandler implements AddonWorkshopAssessmentStrategyHandler { - name = 'AddonModWorkshopAssessmentStrategyComments'; - strategyName = 'comments'; - - constructor(private translate: TranslateService) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Return the Component to render the plugin. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonModWorkshopAssessmentStrategyCommentsComponent; - } - - /** - * Prepare original values to be shown and compared. - * - * @param form Original data of the form. - * @param workshopId Workshop Id - * @return Promise resolved with original values sorted. - */ - getOriginalValues(form: any, workshopId: number): Promise { - const originalValues = []; - - form.fields.forEach((field, n) => { - field.dimtitle = this.translate.instant('addon.mod_workshop_assessment_comments.dimensionnumber', {$a: field.number}); - - if (!form.current[n]) { - form.current[n] = {}; - } - - originalValues[n] = { - peercomment: form.current[n].peercomment || '', - number: field.number - }; - }); - - return Promise.resolve(originalValues); - } - - /** - * Check if the assessment data has changed for a certain submission and workshop for a this strategy plugin. - * - * @param originalValues Original values of the form. - * @param currentValues Current values of the form. - * @return True if data has changed, false otherwise. - */ - hasDataChanged(originalValues: any[], currentValues: any[]): boolean { - for (const x in originalValues) { - if (originalValues[x].peercomment != currentValues[x].peercomment) { - return true; - } - } - - return false; - } - - /** - * Prepare assessment data to be sent to the server depending on the strategy selected. - * - * @param currentValues Current values of the form. - * @param form Assessment form data. - * @return Promise resolved with the data to be sent. Or rejected with the input errors object. - */ - prepareAssessmentData(currentValues: any[], form: any): Promise { - const data = {}; - const errors = {}; - let hasErrors = false; - - form.fields.forEach((field, idx) => { - if (idx < form.dimenssionscount) { - if (currentValues[idx].peercomment) { - data['peercomment__idx_' + idx] = currentValues[idx].peercomment; - } else { - errors['peercomment_' + idx] = this.translate.instant('core.err_required'); - hasErrors = true; - } - - data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; - data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); - } - }); - - if (hasErrors) { - return Promise.reject(errors); - } - - return Promise.resolve(data); - } -} diff --git a/src/addon/mod/workshop/assessment/numerrors/component/addon-mod-workshop-assessment-strategy-numerrors.html b/src/addon/mod/workshop/assessment/numerrors/component/addon-mod-workshop-assessment-strategy-numerrors.html deleted file mode 100644 index 0b322a738..000000000 --- a/src/addon/mod/workshop/assessment/numerrors/component/addon-mod-workshop-assessment-strategy-numerrors.html +++ /dev/null @@ -1,30 +0,0 @@ - - - -

{{ field.dimtitle }}

- -
- - - {{ 'addon.mod_workshop.yourassessmentfor' | translate : {'$a': field.dimtitle } }} - - - - - - - - - - - - - {{ 'addon.mod_workshop_assessment_numerrors.dimensioncommentfor' | translate : {'$a': field.dimtitle } }} - - - -

{{ 'addon.mod_workshop_assessment_numerrors.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}

-

-
-
-
diff --git a/src/addon/mod/workshop/assessment/numerrors/component/numerrors.ts b/src/addon/mod/workshop/assessment/numerrors/component/numerrors.ts deleted file mode 100644 index c3fd4ba83..000000000 --- a/src/addon/mod/workshop/assessment/numerrors/component/numerrors.ts +++ /dev/null @@ -1,26 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { AddonModWorkshopAssessmentStrategyComponentBase } from '../../../classes/assessment-strategy-component'; - -/** - * Component for numerrors assessment strategy. - */ -@Component({ - selector: 'addon-mod-workshop-assessment-strategy-numerrors', - templateUrl: 'addon-mod-workshop-assessment-strategy-numerrors.html', -}) -export class AddonModWorkshopAssessmentStrategyNumErrorsComponent extends AddonModWorkshopAssessmentStrategyComponentBase { -} diff --git a/src/addon/mod/workshop/assessment/numerrors/lang/en.json b/src/addon/mod/workshop/assessment/numerrors/lang/en.json deleted file mode 100644 index d12b03206..000000000 --- a/src/addon/mod/workshop/assessment/numerrors/lang/en.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dimensioncommentfor": "Comment for {{$a}}", - "dimensiongradefor": "Grade for {{$a}}", - "dimensionnumber": "Assertion {{$a}}" -} diff --git a/src/addon/mod/workshop/assessment/numerrors/numerrors.module.ts b/src/addon/mod/workshop/assessment/numerrors/numerrors.module.ts deleted file mode 100644 index fff4115e7..000000000 --- a/src/addon/mod/workshop/assessment/numerrors/numerrors.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopAssessmentStrategyNumErrorsComponent } from './component/numerrors'; -import { AddonModWorkshopAssessmentStrategyNumErrorsHandler } from './providers/handler'; -import { AddonWorkshopAssessmentStrategyDelegate } from '../../providers/assessment-strategy-delegate'; - -@NgModule({ - declarations: [ - AddonModWorkshopAssessmentStrategyNumErrorsComponent, - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModWorkshopAssessmentStrategyNumErrorsHandler - ], - exports: [ - AddonModWorkshopAssessmentStrategyNumErrorsComponent - ], - entryComponents: [ - AddonModWorkshopAssessmentStrategyNumErrorsComponent - ] -}) -export class AddonModWorkshopAssessmentStrategyNumErrorsModule { - constructor(strategyDelegate: AddonWorkshopAssessmentStrategyDelegate, - strategyHandler: AddonModWorkshopAssessmentStrategyNumErrorsHandler) { - strategyDelegate.registerHandler(strategyHandler); - } -} diff --git a/src/addon/mod/workshop/assessment/numerrors/providers/handler.ts b/src/addon/mod/workshop/assessment/numerrors/providers/handler.ts deleted file mode 100644 index 11729491d..000000000 --- a/src/addon/mod/workshop/assessment/numerrors/providers/handler.ts +++ /dev/null @@ -1,134 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonWorkshopAssessmentStrategyHandler } from '../../../providers/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyNumErrorsComponent } from '../component/numerrors'; - -/** - * Handler for numerrors assessment strategy plugin. - */ -@Injectable() -export class AddonModWorkshopAssessmentStrategyNumErrorsHandler implements AddonWorkshopAssessmentStrategyHandler { - name = 'AddonModWorkshopAssessmentStrategyNumErrors'; - strategyName = 'numerrors'; - - constructor(private translate: TranslateService) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Return the Component to render the plugin. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonModWorkshopAssessmentStrategyNumErrorsComponent; - } - - /** - * Prepare original values to be shown and compared. - * - * @param form Original data of the form. - * @param workshopId Workshop Id - * @return Promise resolved with original values sorted. - */ - getOriginalValues(form: any, workshopId: number): Promise { - const originalValues = []; - - form.fields.forEach((field, n) => { - field.dimtitle = this.translate.instant('addon.mod_workshop_assessment_numerrors.dimensionnumber', {$a: field.number}); - - if (!form.current[n]) { - form.current[n] = {}; - } - - originalValues[n] = { - peercomment: form.current[n].peercomment || '', - number: field.number, - grade: form.current[n].grade || '' - }; - }); - - return Promise.resolve(originalValues); - } - - /** - * Check if the assessment data has changed for a certain submission and workshop for a this strategy plugin. - * - * @param originalValues Original values of the form. - * @param currentValues Current values of the form. - * @return True if data has changed, false otherwise. - */ - hasDataChanged(originalValues: any[], currentValues: any[]): boolean { - for (const x in originalValues) { - if (originalValues[x].grade != currentValues[x].grade) { - return true; - } - if (originalValues[x].peercomment != currentValues[x].peercomment) { - return true; - } - } - - return false; - } - - /** - * Prepare assessment data to be sent to the server depending on the strategy selected. - * - * @param currentValues Current values of the form. - * @param form Assessment form data. - * @return Promise resolved with the data to be sent. Or rejected with the input errors object. - */ - prepareAssessmentData(currentValues: any[], form: any): Promise { - const data = {}; - const errors = {}; - let hasErrors = false; - - form.fields.forEach((field, idx) => { - if (idx < form.dimenssionscount) { - const grade = parseInt(currentValues[idx].grade); - if (!isNaN(grade) && (grade == 1 || grade == -1)) { - data['grade__idx_' + idx] = grade; - } else { - errors['grade_' + idx] = this.translate.instant('core.required'); - hasErrors = true; - } - - if (currentValues[idx].peercomment) { - data['peercomment__idx_' + idx] = currentValues[idx].peercomment; - } - - data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; - data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); - data['weight__idx_' + idx] = parseInt(field.weight, 10) || 0; - } - }); - - if (hasErrors) { - return Promise.reject(errors); - } - - return Promise.resolve(data); - } -} diff --git a/src/addon/mod/workshop/assessment/rubric/component/addon-mod-workshop-assessment-strategy-rubric.html b/src/addon/mod/workshop/assessment/rubric/component/addon-mod-workshop-assessment-strategy-rubric.html deleted file mode 100644 index fad4f86b5..000000000 --- a/src/addon/mod/workshop/assessment/rubric/component/addon-mod-workshop-assessment-strategy-rubric.html +++ /dev/null @@ -1,15 +0,0 @@ - - - -

{{ field.dimtitle }}

- - -
- - -

- -
-
-
-
diff --git a/src/addon/mod/workshop/assessment/rubric/component/rubric.ts b/src/addon/mod/workshop/assessment/rubric/component/rubric.ts deleted file mode 100644 index a4ee181d5..000000000 --- a/src/addon/mod/workshop/assessment/rubric/component/rubric.ts +++ /dev/null @@ -1,26 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { AddonModWorkshopAssessmentStrategyComponentBase } from '../../../classes/assessment-strategy-component'; - -/** - * Component for rubric assessment strategy. - */ -@Component({ - selector: 'addon-mod-workshop-assessment-strategy-rubric', - templateUrl: 'addon-mod-workshop-assessment-strategy-rubric.html', -}) -export class AddonModWorkshopAssessmentStrategyRubricComponent extends AddonModWorkshopAssessmentStrategyComponentBase { -} diff --git a/src/addon/mod/workshop/assessment/rubric/lang/en.json b/src/addon/mod/workshop/assessment/rubric/lang/en.json deleted file mode 100644 index 8b0b20c4a..000000000 --- a/src/addon/mod/workshop/assessment/rubric/lang/en.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "dimensionnumber": "Criterion {{$a}}", - "mustchooseone": "You have to select one of these items" -} \ No newline at end of file diff --git a/src/addon/mod/workshop/assessment/rubric/providers/handler.ts b/src/addon/mod/workshop/assessment/rubric/providers/handler.ts deleted file mode 100644 index ab4fb62c8..000000000 --- a/src/addon/mod/workshop/assessment/rubric/providers/handler.ts +++ /dev/null @@ -1,125 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonWorkshopAssessmentStrategyHandler } from '../../../providers/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyRubricComponent } from '../component/rubric'; - -/** - * Handler for rubric assessment strategy plugin. - */ -@Injectable() -export class AddonModWorkshopAssessmentStrategyRubricHandler implements AddonWorkshopAssessmentStrategyHandler { - name = 'AddonModWorkshopAssessmentStrategyRubric'; - strategyName = 'rubric'; - - constructor(private translate: TranslateService) {} - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Return the Component to render the plugin. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonModWorkshopAssessmentStrategyRubricComponent; - } - - /** - * Prepare original values to be shown and compared. - * - * @param form Original data of the form. - * @param workshopId Workshop Id - * @return Promise resolved with original values sorted. - */ - getOriginalValues(form: any, workshopId: number): Promise { - const originalValues = []; - - form.fields.forEach((field, n) => { - field.dimtitle = this.translate.instant('addon.mod_workshop_assessment_rubric.dimensionnumber', {$a: field.number}); - - if (!form.current[n]) { - form.current[n] = {}; - } - - originalValues[n] = { - chosenlevelid: form.current[n].chosenlevelid || '', - number: field.number - }; - }); - - return Promise.resolve(originalValues); - } - - /** - * Check if the assessment data has changed for a certain submission and workshop for a this strategy plugin. - * - * @param originalValues Original values of the form. - * @param currentValues Current values of the form. - * @return True if data has changed, false otherwise. - */ - hasDataChanged(originalValues: any[], currentValues: any[]): boolean { - for (const x in originalValues) { - if (originalValues[x].chosenlevelid != (currentValues[x].chosenlevelid || '')) { - return true; - } - } - - return false; - } - - /** - * Prepare assessment data to be sent to the server depending on the strategy selected. - * - * @param currentValues Current values of the form. - * @param form Assessment form data. - * @return Promise resolved with the data to be sent. Or rejected with the input errors object. - */ - prepareAssessmentData(currentValues: any[], form: any): Promise { - const data = {}; - const errors = {}; - let hasErrors = false; - - form.fields.forEach((field, idx) => { - if (idx < form.dimenssionscount) { - const id = parseInt(currentValues[idx].chosenlevelid, 10); - if (!isNaN(id) && id >= 0) { - data['chosenlevelid__idx_' + idx] = id; - } else { - errors['chosenlevelid_' + idx] = this.translate.instant('addon.mod_workshop_assessment_rubric.mustchooseone'); - hasErrors = true; - } - - data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; - data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); - } - }); - - if (hasErrors) { - return Promise.reject(errors); - } - - return Promise.resolve(data); - } -} diff --git a/src/addon/mod/workshop/assessment/rubric/rubric.module.ts b/src/addon/mod/workshop/assessment/rubric/rubric.module.ts deleted file mode 100644 index c8a71406e..000000000 --- a/src/addon/mod/workshop/assessment/rubric/rubric.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopAssessmentStrategyRubricComponent } from './component/rubric'; -import { AddonModWorkshopAssessmentStrategyRubricHandler } from './providers/handler'; -import { AddonWorkshopAssessmentStrategyDelegate } from '../../providers/assessment-strategy-delegate'; - -@NgModule({ - declarations: [ - AddonModWorkshopAssessmentStrategyRubricComponent, - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonModWorkshopAssessmentStrategyRubricHandler - ], - exports: [ - AddonModWorkshopAssessmentStrategyRubricComponent - ], - entryComponents: [ - AddonModWorkshopAssessmentStrategyRubricComponent - ] -}) -export class AddonModWorkshopAssessmentStrategyRubricModule { - constructor(strategyDelegate: AddonWorkshopAssessmentStrategyDelegate, - strategyHandler: AddonModWorkshopAssessmentStrategyRubricHandler) { - strategyDelegate.registerHandler(strategyHandler); - } -} diff --git a/src/addon/mod/workshop/classes/assessment-strategy-component.ts b/src/addon/mod/workshop/classes/assessment-strategy-component.ts deleted file mode 100644 index f45376472..000000000 --- a/src/addon/mod/workshop/classes/assessment-strategy-component.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Input } from '@angular/core'; - -/** - * Base class for component to render an assessment strategy. - */ -export class AddonModWorkshopAssessmentStrategyComponentBase { - @Input() workshopId: number; - @Input() assessment: any; - @Input() edit: boolean; - @Input() selectedValues: any[]; - @Input() fieldErrors: any; - @Input() strategy: string; - @Input() moduleId: number; - @Input() courseId: number; - - constructor() { - // Nothing to do. - } -} diff --git a/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html b/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html deleted file mode 100644 index c3bb1fd41..000000000 --- a/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html +++ /dev/null @@ -1,45 +0,0 @@ -

{{ 'addon.mod_workshop.assessmentform' | translate }}

- -
- - - - - -
- {{ 'addon.mod_workshop.assessmentstrategynotsupported' | translate:{$a: strategy} }} -
- - - -

{{ 'addon.mod_workshop.overallfeedback' | translate }}

-
- - {{ 'addon.mod_workshop.feedbackauthor' | translate }} - - - - - - {{ 'addon.mod_workshop.assessmentweight' | translate }} - - {{w}} - - - - - - -
- - - - - - -
-
-
-
-
\ No newline at end of file diff --git a/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts b/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts deleted file mode 100644 index 7418e92b5..000000000 --- a/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts +++ /dev/null @@ -1,363 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, Injector, ViewChild, ElementRef } from '@angular/core'; -import { FormControl } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { AddonModWorkshopProvider } from '../../providers/workshop'; -import { AddonModWorkshopHelperProvider } from '../../providers/helper'; -import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; -import { AddonWorkshopAssessmentStrategyDelegate } from '../../providers/assessment-strategy-delegate'; - -/** - * Component that displays workshop assessment strategy form. - */ -@Component({ - selector: 'addon-mod-workshop-assessment-strategy', - templateUrl: 'addon-mod-workshop-assessment-strategy.html', -}) -export class AddonModWorkshopAssessmentStrategyComponent implements OnInit { - - @Input() workshop: any; - @Input() access: any; - @Input() assessmentId: number; - @Input() userId: number; - @Input() strategy: string; - @Input() edit?: boolean; - - @ViewChild('assessmentForm') formElement: ElementRef; - - componentClass: any; - data = { - workshopId: 0, - assessment: null, - edit: false, - selectedValues: [], - fieldErrors: {}, - strategy: '', - moduleId: 0, - courseId: null - }; - assessmentStrategyLoaded = false; - notSupported = false; - feedbackText = ''; - feedbackControl = new FormControl(); - overallFeedkback = false; - overallFeedkbackRequired = false; - component = AddonModWorkshopProvider.COMPONENT; - componentId: number; - weights: any[]; - weight: number; - - protected obsInvalidated: any; - protected hasOffline: boolean; - protected originalData = { - text: '', - files: [], - weight: 1, - selectedValues: [] - }; - - constructor(private translate: TranslateService, - private injector: Injector, - private eventsProvider: CoreEventsProvider, - private fileSessionProvider: CoreFileSessionProvider, - private syncProvider: CoreSyncProvider, - private domUtils: CoreDomUtilsProvider, - private textUtils: CoreTextUtilsProvider, - private utils: CoreUtilsProvider, - private sitesProvider: CoreSitesProvider, - private uploaderProvider: CoreFileUploaderProvider, - private workshopProvider: AddonModWorkshopProvider, - private workshopHelper: AddonModWorkshopHelperProvider, - private workshopOffline: AddonModWorkshopOfflineProvider, - private strategyDelegate: AddonWorkshopAssessmentStrategyDelegate) {} - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.assessmentId || !this.strategy) { - this.assessmentStrategyLoaded = true; - - return; - } - - this.data.workshopId = this.workshop.id; - this.data.edit = this.edit; - this.data.strategy = this.strategy; - this.data.moduleId = this.workshop.coursemodule; - this.data.courseId = this.workshop.course; - - this.componentClass = this.strategyDelegate.getComponentForPlugin(this.injector, this.strategy); - if (this.componentClass) { - this.overallFeedkback = !!this.workshop.overallfeedbackmode; - this.overallFeedkbackRequired = this.workshop.overallfeedbackmode == 2; - this.componentId = this.workshop.coursemodule; - - // Load Weights selector. - if (this.edit && this.access.canallocate) { - this.weights = []; - for (let i = 16; i >= 0; i--) { - this.weights[i] = i; - } - } - - // Check if rich text editor is enabled. - if (this.edit) { - // Block the workshop. - this.syncProvider.blockOperation(AddonModWorkshopProvider.COMPONENT, this.workshop.id); - } - - this.load().then(() => { - this.obsInvalidated = this.eventsProvider.on(AddonModWorkshopProvider.ASSESSMENT_INVALIDATED, - this.load.bind(this), this.sitesProvider.getCurrentSiteId()); - }).finally(() => { - this.assessmentStrategyLoaded = true; - }); - } else { - // Helper data and fallback. - this.notSupported = !this.strategyDelegate.isPluginSupported(this.strategy); - this.assessmentStrategyLoaded = true; - } - } - - /** - * Convenience function to load the assessment data. - * - * @return Promised resvoled when data is loaded. - */ - protected load(): Promise { - return this.workshopHelper.getReviewerAssessmentById(this.workshop.id, this.assessmentId, { - userId: this.userId, - cmId: this.workshop.coursemodule, - }).then((assessmentData) => { - this.data.assessment = assessmentData; - - let promise; - if (this.edit) { - promise = this.workshopOffline.getAssessment(this.workshop.id, this.assessmentId).then((offlineAssessment) => { - const offlineData = offlineAssessment.inputdata; - - this.hasOffline = true; - - assessmentData.feedbackauthor = offlineData.feedbackauthor; - - if (this.access.canallocate) { - assessmentData.weight = offlineData.weight; - } - - // Override assessment plugins values. - assessmentData.form.current = this.workshopProvider.parseFields( - this.utils.objectToArrayOfObjects(offlineData, 'name', 'value')); - - // Override offline files. - if (offlineData) { - return this.workshopHelper.getAssessmentFilesFromOfflineFilesObject( - offlineData.feedbackauthorattachmentsid, this.workshop.id, this.assessmentId) - .then((files) => { - assessmentData.feedbackattachmentfiles = files; - }); - } - }).catch(() => { - this.hasOffline = false; - // Ignore errors. - }).finally(() => { - this.feedbackText = assessmentData.feedbackauthor; - this.feedbackControl.setValue(this.feedbackText); - - this.originalData.text = this.data.assessment.feedbackauthor; - - if (this.access.canallocate) { - this.originalData.weight = assessmentData.weight; - } - - this.originalData.files = []; - assessmentData.feedbackattachmentfiles.forEach((file) => { - let filename; - if (file.filename) { - filename = file.filename; - } else { - // We don't have filename, extract it from the path. - filename = file.filepath[0] == '/' ? file.filepath.substr(1) : file.filepath; - } - - this.originalData.files.push({ - filename : filename, - fileurl: file.fileurl - }); - }); - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - return this.strategyDelegate.getOriginalValues(this.strategy, assessmentData.form, this.workshop.id) - .then((values) => { - this.data.selectedValues = values; - }).finally(() => { - this.originalData.selectedValues = this.utils.clone(this.data.selectedValues); - if (this.edit) { - this.fileSessionProvider.setFiles(AddonModWorkshopProvider.COMPONENT, - this.workshop.id + '_' + this.assessmentId, assessmentData.feedbackattachmentfiles); - if (this.access.canallocate) { - this.weight = assessmentData.weight; - } - } - }); - }); - }); - } - - /** - * Check if data has changed. - * - * @return True if data has changed. - */ - hasDataChanged(): boolean { - if (!this.assessmentStrategyLoaded) { - return false; - } - - // Compare feedback text. - const text = this.textUtils.restorePluginfileUrls(this.feedbackText, this.data.assessment.feedbackcontentfiles || []); - if (this.originalData.text != text) { - return true; - } - - if (this.access.canallocate && this.originalData.weight != this.weight) { - return true; - } - - // Compare feedback files. - const files = this.fileSessionProvider.getFiles(AddonModWorkshopProvider.COMPONENT, - this.workshop.id + '_' + this.assessmentId) || []; - if (this.uploaderProvider.areFileListDifferent(files, this.originalData.files)) { - return true; - } - - return this.strategyDelegate.hasDataChanged(this.workshop, this.originalData.selectedValues, this.data.selectedValues); - } - - /** - * Save the assessment. - * - * @return Promise resolved when done, rejected if assessment could not be saved. - */ - saveAssessment(): Promise { - const files = this.fileSessionProvider.getFiles(AddonModWorkshopProvider.COMPONENT, - this.workshop.id + '_' + this.assessmentId) || []; - let saveOffline = false; - let allowOffline = !files.length; - - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.data.fieldErrors = {}; - - // Upload attachments first if any. - return this.workshopHelper.uploadOrStoreAssessmentFiles(this.workshop.id, this.assessmentId, files, - saveOffline).catch(() => { - // Cannot upload them in online, save them in offline. - saveOffline = true; - allowOffline = true; - - return this.workshopHelper.uploadOrStoreAssessmentFiles(this.workshop.id, this.assessmentId, files, saveOffline); - }).then((attachmentsId) => { - const text = this.textUtils.restorePluginfileUrls(this.feedbackText, this.data.assessment.feedbackcontentfiles || []); - - return this.workshopHelper.prepareAssessmentData(this.workshop, this.data.selectedValues, text, files, - this.data.assessment.form, attachmentsId).catch((errors) => { - this.data.fieldErrors = errors; - - return Promise.reject(this.translate.instant('core.errorinvalidform')); - }); - }).then((assessmentData) => { - if (saveOffline) { - // Save assessment in offline. - return this.workshopOffline.saveAssessment(this.workshop.id, this.assessmentId, this.workshop.course, - assessmentData).then(() => { - return false; - }); - } - - // Try to send it to server. - // Don't allow offline if there are attachments since they were uploaded fine. - return this.workshopProvider.updateAssessment(this.workshop.id, this.assessmentId, this.workshop.course, - assessmentData, false, allowOffline); - }).then((grade) => { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, !!grade, this.sitesProvider.getCurrentSiteId()); - - const promises = []; - - // If sent to the server, invalidate and clean. - if (grade) { - promises.push(this.workshopHelper.deleteAssessmentStoredFiles(this.workshop.id, this.assessmentId)); - promises.push(this.workshopProvider.invalidateAssessmentFormData(this.workshop.id, this.assessmentId)); - promises.push(this.workshopProvider.invalidateAssessmentData(this.workshop.id, this.assessmentId)); - } - - return Promise.all(promises).catch(() => { - // Ignore errors. - }).finally(() => { - this.eventsProvider.trigger(AddonModWorkshopProvider.ASSESSMENT_SAVED, { - workshopId: this.workshop.id, - assessmentId: this.assessmentId, - userId: this.sitesProvider.getCurrentSiteUserId(), - }, this.sitesProvider.getCurrentSiteId()); - - if (files) { - // Delete the local files from the tmp folder. - this.uploaderProvider.clearTmpFiles(files); - } - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Error saving assessment.'); - - return Promise.reject(null); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Feedback text changed. - * - * @param text The new text. - */ - onFeedbackChange(text: string): void { - this.feedbackText = text; - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.obsInvalidated && this.obsInvalidated.off(); - - if (this.data.assessment.feedbackattachmentfiles) { - // Delete the local files from the tmp folder. - this.uploaderProvider.clearTmpFiles(this.data.assessment.feedbackattachmentfiles); - } - } -} diff --git a/src/addon/mod/workshop/components/assessment/addon-mod-workshop-assessment.html b/src/addon/mod/workshop/components/assessment/addon-mod-workshop-assessment.html deleted file mode 100644 index 8c876cfd1..000000000 --- a/src/addon/mod/workshop/components/assessment/addon-mod-workshop-assessment.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - -

{{profile.fullname}}

-

- {{ 'addon.mod_workshop.submissiongradeof' | translate:{$a: workshop.grade } }}: {{assessment.grade}} -

-

- {{ 'addon.mod_workshop.gradinggradeof' | translate:{$a: workshop.gradinggrade } }}: {{assessment.gradinggrade}} -

-

- {{ 'addon.mod_workshop.gradinggradeof' | translate:{$a: workshop.gradinggrade } }}: {{assessment.gradinggradeover}} -

-

- {{ 'addon.mod_workshop.weightinfo' | translate:{$a: assessment.weight } }} -

- {{ 'addon.mod_workshop.notassessed' | translate }} - - - - {{ 'core.notsent' | translate }} - -
-
diff --git a/src/addon/mod/workshop/components/assessment/assessment.scss b/src/addon/mod/workshop/components/assessment/assessment.scss deleted file mode 100644 index f7d3ed7f2..000000000 --- a/src/addon/mod/workshop/components/assessment/assessment.scss +++ /dev/null @@ -1,4 +0,0 @@ -ion-app.app-root addon-mod-workshop-assessment, -ion-app.app-root .card.with-borders addon-mod-workshop-assessment { - @include core-as-items(); -} \ No newline at end of file diff --git a/src/addon/mod/workshop/components/assessment/assessment.ts b/src/addon/mod/workshop/components/assessment/assessment.ts deleted file mode 100644 index c80eed725..000000000 --- a/src/addon/mod/workshop/components/assessment/assessment.ts +++ /dev/null @@ -1,149 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModWorkshopHelperProvider } from '../../providers/helper'; -import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; - -/** - * Component that displays workshop assessment. - */ -@Component({ - selector: 'addon-mod-workshop-assessment', - templateUrl: 'addon-mod-workshop-assessment.html', -}) -export class AddonModWorkshopAssessmentComponent implements OnInit { - @Input() assessment: any; - @Input() summary?: boolean; - @Input() courseId: number; - @Input() submission: any; - @Input() module?: any; - @Input() workshop: any; - @Input() access: any; - - canViewAssessment = false; - canSelfAssess = false; - profile: any; - showGrade: any; - offline = false; - loaded = false; - - protected currentUserId: number; - protected assessmentId: number; - - constructor(private workshopOffline: AddonModWorkshopOfflineProvider, private workshopHelper: AddonModWorkshopHelperProvider, - private navCtrl: NavController, private userProvider: CoreUserProvider, private domUtils: CoreDomUtilsProvider, - sitesProvider: CoreSitesProvider) { - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - this.showGrade = this.workshopHelper.showGrade; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const canAssess = this.access && this.access.assessingallowed, - userId = this.assessment.userid || this.assessment.reviewerid, - promises = []; - - this.assessmentId = this.assessment.assessmentid || this.assessment.id; - this.canViewAssessment = this.assessment.grade; - this.canSelfAssess = canAssess && userId == this.currentUserId; - - if (userId) { - promises.push(this.userProvider.getProfile(userId, this.courseId, true).then((profile) => { - this.profile = profile; - })); - } - - let assessOffline; - if (userId == this.currentUserId) { - assessOffline = this.workshopOffline.getAssessment(this.workshop.id, this.assessmentId) .then((offlineAssess) => { - this.offline = true; - this.assessment.weight = offlineAssess.inputdata.weight; - }); - } else { - assessOffline = this.workshopOffline.getEvaluateAssessment(this.workshop.id, this.assessmentId) - .then((offlineAssess) => { - this.offline = true; - this.assessment.gradinggradeover = offlineAssess.gradinggradeover; - this.assessment.weight = offlineAssess.weight; - }); - } - - promises.push(assessOffline.catch(() => { - this.offline = false; - // Ignore errors. - })); - - Promise.all(promises).finally(() => { - this.loaded = true; - }); - } - - /** - * Navigate to the assessment. - */ - gotoAssessment(): void { - if (!this.canSelfAssess && this.canViewAssessment) { - const params = { - assessment: this.assessment, - submission: this.submission, - profile: this.profile, - courseId: this.courseId, - assessmentId: this.assessmentId - }; - - if (!this.submission) { - const modal = this.domUtils.showModalLoading('core.sending', true); - - this.workshopHelper.getSubmissionById(this.workshop.id, this.assessment.submissionid, - {cmId: this.workshop.coursemodule}).then((submissionData) => { - - params.submission = submissionData; - this.navCtrl.push('AddonModWorkshopAssessmentPage', params); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Cannot load submission'); - }).finally(() => { - modal.dismiss(); - }); - } else { - this.navCtrl.push('AddonModWorkshopAssessmentPage', params); - } - } - } - - /** - * Navigate to my own assessment. - */ - gotoOwnAssessment(): void { - if (this.canSelfAssess) { - const params = { - module: this.module, - workshop: this.workshop, - access: this.access, - courseId: this.courseId, - profile: this.profile, - submission: this.submission, - assessment: this.assessment - }; - - this.navCtrl.push('AddonModWorkshopSubmissionPage', params); - } - } -} diff --git a/src/addon/mod/workshop/components/components.module.ts b/src/addon/mod/workshop/components/components.module.ts deleted file mode 100644 index 279484ef6..000000000 --- a/src/addon/mod/workshop/components/components.module.ts +++ /dev/null @@ -1,58 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreCourseComponentsModule } from '@core/course/components/components.module'; -import { AddonModWorkshopIndexComponent } from './index/index'; -import { AddonModWorkshopSubmissionComponent } from './submission/submission'; -import { AddonModWorkshopAssessmentComponent } from './assessment/assessment'; -import { AddonModWorkshopAssessmentStrategyComponent } from './assessment-strategy/assessment-strategy'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModWorkshopIndexComponent, - AddonModWorkshopSubmissionComponent, - AddonModWorkshopAssessmentComponent, - AddonModWorkshopAssessmentStrategyComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - CoreCourseComponentsModule, - CoreEditorComponentsModule, - ], - providers: [ - ], - exports: [ - AddonModWorkshopIndexComponent, - AddonModWorkshopSubmissionComponent, - AddonModWorkshopAssessmentComponent, - AddonModWorkshopAssessmentStrategyComponent - ], - entryComponents: [ - AddonModWorkshopIndexComponent - ] -}) -export class AddonModWorkshopComponentsModule {} diff --git a/src/addon/mod/workshop/components/index/addon-mod-workshop-index.html b/src/addon/mod/workshop/components/index/addon-mod-workshop-index.html deleted file mode 100644 index 7e8daa461..000000000 --- a/src/addon/mod/workshop/components/index/addon-mod-workshop-index.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - - - - - - - - - - - -

{{ phases[workshop.phase].title }}

-
- - - - - - - -

{{task.title}}

-

- -
-
-
- - -
- - {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} -
- - - - -

{{ 'core.description' | translate }}

- -
-
- -
- - - - -

{{ 'addon.mod_workshop.conclusion' | translate }}

- -
-
- - - -

{{ 'addon.mod_workshop.yourgrades' | translate }}

-
- -

{{ 'addon.mod_workshop.submissiongrade' | translate }}

- {{ userGrades.submissionlongstrgrade }} -
- -

{{ 'addon.mod_workshop.gradinggrade' | translate }}

- {{ userGrades.assessmentlongstrgrade }} -
-
-
- - - - -

{{ 'addon.mod_workshop.areainstructauthors' | translate }}

- -
-
- - - -

{{ 'addon.mod_workshop.yoursubmission' | translate }}

-

{{ 'addon.mod_workshop.noyoursubmission' | translate }}

-
- - - -

{{ 'addon.mod_workshop.yoursubmission' | translate }}

-
- -
-
- - - - - - - - - - - - -

{{ 'addon.mod_workshop.publishedsubmissions' | translate }}

-
- - - -
-
- - - - - -

{{ 'addon.mod_workshop.areainstructreviewers' | translate }}

- -
-
- - - -

{{ 'addon.mod_workshop.assignedassessments' | translate }}

-
- -

{{ 'addon.mod_workshop.assignedassessmentsnone' | translate }}

-
- - - -
-
- - - - -

{{ 'addon.mod_workshop.submissionsreport' | translate }}

-
- -

{{ 'addon.mod_workshop.gradesreport' | translate }}

-
- - {{ 'core.groupsseparate' | translate }} - {{ 'core.groupsvisible' | translate }} - - {{groupOpt.name}} - - - - - - - - - - - - - - - - - -
-
-
diff --git a/src/addon/mod/workshop/components/index/index.ts b/src/addon/mod/workshop/components/index/index.ts deleted file mode 100644 index d2e7b7903..000000000 --- a/src/addon/mod/workshop/components/index/index.ts +++ /dev/null @@ -1,457 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Optional, Injector } from '@angular/core'; -import { Content, ModalController, NavController, Platform } from 'ionic-angular'; -import { CoreGroupInfo, CoreGroupsProvider } from '@providers/groups'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; -import { AddonModWorkshopProvider } from '../../providers/workshop'; -import { AddonModWorkshopHelperProvider } from '../../providers/helper'; -import { AddonModWorkshopSyncProvider } from '../../providers/sync'; -import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; - -/** - * Component that displays a workshop index page. - */ -@Component({ - selector: 'addon-mod-workshop-index', - templateUrl: 'addon-mod-workshop-index.html', -}) -export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivityComponent { - @Input() group = 0; - - component = AddonModWorkshopProvider.COMPONENT; - moduleName = 'workshop'; - workshop: any; - page = 0; - access: any; - phases: any; - grades: any; - assessments: any; - userGrades: any; - publishedSubmissions: any; - submission: any; - groupInfo: CoreGroupInfo = { - groups: [], - separateGroups: false, - visibleGroups: false - }; - canSubmit = false; - showSubmit = false; - canAssess = false; - hasNextPage = false; - - workshopPhases = { - PHASE_SETUP: AddonModWorkshopProvider.PHASE_SETUP, - PHASE_SUBMISSION: AddonModWorkshopProvider.PHASE_SUBMISSION, - PHASE_ASSESSMENT: AddonModWorkshopProvider.PHASE_ASSESSMENT, - PHASE_EVALUATION: AddonModWorkshopProvider.PHASE_EVALUATION, - PHASE_CLOSED: AddonModWorkshopProvider.PHASE_CLOSED - }; - - protected offlineSubmissions = []; - protected obsSubmissionChanged: any; - protected obsAssessmentSaved: any; - protected appResumeSubscription: any; - protected syncObserver: any; - protected syncEventName = AddonModWorkshopSyncProvider.AUTO_SYNCED; - - constructor(injector: Injector, private workshopProvider: AddonModWorkshopProvider, @Optional() content: Content, - private workshopOffline: AddonModWorkshopOfflineProvider, private groupsProvider: CoreGroupsProvider, - protected navCtrl: NavController, private modalCtrl: ModalController, private utils: CoreUtilsProvider, - platform: Platform, private workshopHelper: AddonModWorkshopHelperProvider, - private workshopSync: AddonModWorkshopSyncProvider) { - super(injector, content); - - // Listen to submission and assessment changes. - this.obsSubmissionChanged = this.eventsProvider.on(AddonModWorkshopProvider.SUBMISSION_CHANGED, (data) => { - this.eventReceived(data); - }, this.siteId); - - // Listen to submission and assessment changes. - this.obsAssessmentSaved = this.eventsProvider.on(AddonModWorkshopProvider.ASSESSMENT_SAVED, (data) => { - this.eventReceived(data); - }, this.siteId); - - // Since most actions will take the user out of the app, we should refresh the view when the app is resumed. - this.appResumeSubscription = platform.resume.subscribe(() => { - this.showLoadingAndRefresh(true); - }); - - // Refresh workshop on sync. - this.syncObserver = this.eventsProvider.on(AddonModWorkshopSyncProvider.AUTO_SYNCED, (data) => { - // Update just when all database is synced. - this.eventReceived(data); - }, this.siteId); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.loadContent(false, true).then(() => { - if (!this.workshop) { - return; - } - - this.workshopProvider.logView(this.workshop.id, this.workshop.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch((error) => { - // Ignore errors. - }); - }); - } - - /** - * Function called when we receive an event of submission changes. - * - * @param data Data received by the event. - */ - protected eventReceived(data: any): void { - if ((this.workshop && this.workshop.id === data.workshopId) || data.cmId === this.module.id) { - this.showLoadingAndRefresh(true); - - // Check completion since it could be configured to complete once the user adds a new discussion or replies. - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - } - } - - /** - * Perform the invalidate content function. - * - * @return Resolved when done. - */ - protected invalidateContent(): Promise { - const promises = []; - - promises.push(this.workshopProvider.invalidateWorkshopData(this.courseId)); - if (this.workshop) { - promises.push(this.workshopProvider.invalidateWorkshopAccessInformationData(this.workshop.id)); - promises.push(this.workshopProvider.invalidateUserPlanPhasesData(this.workshop.id)); - if (this.canSubmit) { - promises.push(this.workshopProvider.invalidateSubmissionsData(this.workshop.id)); - } - if (this.access.canviewallsubmissions) { - promises.push(this.workshopProvider.invalidateGradeReportData(this.workshop.id)); - promises.push(this.groupsProvider.invalidateActivityAllowedGroups(this.workshop.coursemodule)); - promises.push(this.groupsProvider.invalidateActivityGroupMode(this.workshop.coursemodule)); - } - if (this.canAssess) { - promises.push(this.workshopProvider.invalidateReviewerAssesmentsData(this.workshop.id)); - } - promises.push(this.workshopProvider.invalidateGradesData(this.workshop.id)); - promises.push(this.workshopProvider.invalidateWorkshopWSData(this.workshop.id)); - } - - return Promise.all(promises); - } - - /** - * Compares sync event data with current data to check if refresh content is needed. - * - * @param syncEventData Data receiven on sync observer. - * @return True if refresh is needed, false otherwise. - */ - protected isRefreshSyncNeeded(syncEventData: any): boolean { - if (this.workshop && syncEventData.workshopId == this.workshop.id) { - // Refresh the data. - this.domUtils.scrollToTop(this.content); - - return true; - } - - return false; - } - - /** - * Download feedback contents. - * - * @param refresh If it's refreshing content. - * @param sync If it should try to sync. - * @param showErrors If show errors to the user of hide them. - * @return Promise resolved when done. - */ - protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { - return this.workshopProvider.getWorkshop(this.courseId, this.module.id).then((workshop) => { - this.workshop = workshop; - - this.description = workshop.intro || workshop.description; - this.dataRetrieved.emit(workshop); - - if (sync) { - // Try to synchronize the feedback. - return this.syncActivity(showErrors); - } - }).then(() => { - // Check if there are answers stored in offline. - return this.workshopProvider.getWorkshopAccessInformation(this.workshop.id, {cmId: this.module.id}); - }).then((accessData) => { - this.access = accessData; - - if (accessData.canviewallsubmissions) { - return this.groupsProvider.getActivityGroupInfo(this.workshop.coursemodule).then((groupInfo) => { - this.groupInfo = groupInfo; - this.group = this.groupsProvider.validateGroupId(this.group, groupInfo); - }); - } - }).then(() => { - return this.workshopProvider.getUserPlanPhases(this.workshop.id, {cmId: this.module.id}); - }).then((phases) => { - this.phases = phases; - - phases[this.workshop.phase].tasks.forEach((task) => { - if (!task.link && (task.code == 'examples' || task.code == 'prepareexamples')) { - // Add links to manage examples. - task.link = this.externalUrl; - } - }); - - // Check if there are info stored in offline. - return this.workshopOffline.hasWorkshopOfflineData(this.workshop.id).then((hasOffline) => { - this.hasOffline = hasOffline; - if (hasOffline) { - return this.workshopOffline.getSubmissions(this.workshop.id).then((submissionsActions) => { - this.offlineSubmissions = submissionsActions; - }); - } else { - this.offlineSubmissions = []; - } - }); - }).then(() => { - return this.setPhaseInfo(); - }).finally(() => { - this.fillContextMenu(refresh); - }); - } - - /** - * Retrieves and shows submissions grade page. - * - * @param page Page number to be retrieved. - * @return Resolved when done. - */ - gotoSubmissionsPage(page: number): Promise { - return this.workshopProvider.getGradesReport(this.workshop.id, { - groupId: this.group, - page, - cmId: this.module.id, - }).then((report) => { - const numEntries = (report && report.grades && report.grades.length) || 0; - - this.page = page; - - this.hasNextPage = numEntries >= AddonModWorkshopProvider.PER_PAGE && ((this.page + 1) * - AddonModWorkshopProvider.PER_PAGE) < report.totalcount; - - this.grades = report.grades || []; - - this.grades.forEach((submission) => { - const actions = this.workshopHelper.filterSubmissionActions(this.offlineSubmissions, submission.submissionid - || false); - submission = this.workshopHelper.applyOfflineData(submission, actions); - - return this.workshopHelper.applyOfflineData(submission, actions).then((offlineSubmission) => { - submission = offlineSubmission; - }); - }); - }); - } - - /** - * Open task. - * - * @param task Task to be done. - */ - runTask(task: any): void { - if (task.code == 'submit') { - this.gotoSubmit(); - } else if (task.link) { - this.utils.openInBrowser(task.link); - } - } - - /** - * Go to submit page. - */ - gotoSubmit(): void { - if (this.canSubmit && ((this.access.creatingsubmissionallowed && !this.submission) || - (this.access.modifyingsubmissionallowed && this.submission))) { - const params = { - module: this.module, - access: this.access, - courseId: this.courseId, - submissionId: this.submission && this.submission.id - }; - - this.navCtrl.push('AddonModWorkshopEditSubmissionPage', params); - } - } - - /** - * View Phase info. - */ - viewPhaseInfo(): void { - if (this.phases) { - const modal = this.modalCtrl.create('AddonModWorkshopPhaseInfoPage', { - phases: this.utils.objectToArray(this.phases), - workshopPhase: this.workshop.phase, - externalUrl: this.externalUrl, - showSubmit: this.showSubmit - }); - modal.onDidDismiss((goSubmit) => { - goSubmit && this.gotoSubmit(); - }); - modal.present(); - } - } - - /** - * Set group to see the workshop. - * @param groupId Group Id. - * @return Promise resolved when done. - */ - setGroup(groupId: number): Promise { - this.group = groupId; - - return this.gotoSubmissionsPage(0); - } - - /** - * Convenience function to set current phase information. - * - * @return Promise resolved when done. - */ - protected setPhaseInfo(): Promise { - this.submission = false; - this.canAssess = false; - this.assessments = false; - this.userGrades = false; - this.publishedSubmissions = false; - - this.canSubmit = this.workshopHelper.canSubmit(this.workshop, this.access, - this.phases[AddonModWorkshopProvider.PHASE_SUBMISSION].tasks); - - this.showSubmit = this.workshop.phase == AddonModWorkshopProvider.PHASE_SUBMISSION && this.canSubmit && - ((this.access.creatingsubmissionallowed && !this.submission) || - (this.access.modifyingsubmissionallowed && this.submission)); - - const promises = []; - - if (this.canSubmit) { - promises.push(this.workshopHelper.getUserSubmission(this.workshop.id, {cmId: this.module.id}).then((submission) => { - const actions = this.workshopHelper.filterSubmissionActions(this.offlineSubmissions, submission.id || false); - - return this.workshopHelper.applyOfflineData(submission, actions).then((submission) => { - this.submission = submission; - }); - })); - } - - if (this.access.canviewallsubmissions && this.workshop.phase >= AddonModWorkshopProvider.PHASE_SUBMISSION) { - promises.push(this.gotoSubmissionsPage(this.page)); - } - - let assessPromise = Promise.resolve(); - - if (this.workshop.phase >= AddonModWorkshopProvider.PHASE_ASSESSMENT) { - this.canAssess = this.workshopHelper.canAssess(this.workshop, this.access); - if (this.canAssess) { - assessPromise = this.workshopHelper.getReviewerAssessments(this.workshop.id, { - cmId: this.module.id, - }).then((assessments) => { - const p2 = []; - - assessments.forEach((assessment) => { - assessment.strategy = this.workshop.strategy; - if (this.hasOffline) { - p2.push(this.workshopOffline.getAssessment(this.workshop.id, assessment.id) - .then((offlineAssessment) => { - assessment.offline = true; - assessment.timemodified = Math.floor(offlineAssessment.timemodified / 1000); - }).catch(() => { - // Ignore errors. - })); - } - }); - - return Promise.all(p2).then(() => { - this.assessments = assessments; - }); - }); - promises.push(assessPromise); - } - } - - if (this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { - promises.push(this.workshopProvider.getGrades(this.workshop.id, {cmId: this.module.id}).then((grades) => { - this.userGrades = grades.submissionlongstrgrade || grades.assessmentlongstrgrade ? grades : false; - })); - - if (this.access.canviewpublishedsubmissions) { - promises.push(assessPromise.then(() => { - return this.workshopProvider.getSubmissions(this.workshop.id, {cmId: this.module.id}).then((submissions) => { - this.publishedSubmissions = submissions.filter((submission) => { - if (submission.published) { - this.assessments.forEach((assessment) => { - submission.reviewedby = []; - if (assessment.submissionid == submission.id) { - submission.reviewedby.push(this.workshopHelper.realGradeValue(this.workshop, assessment)); - } - }); - - return true; - } - - return false; - }); - }); - })); - } - } - - return Promise.all(promises); - } - - /** - * Performs the sync of the activity. - * - * @return Promise resolved when done. - */ - protected sync(): Promise { - return this.workshopSync.syncWorkshop(this.workshop.id); - } - - /** - * Checks if sync has succeed from result sync data. - * - * @param result Data returned on the sync function. - * @return If suceed or not. - */ - protected hasSyncSucceed(result: any): boolean { - return result.updated; - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - this.obsSubmissionChanged && this.obsSubmissionChanged.off(); - this.obsAssessmentSaved && this.obsAssessmentSaved.off(); - this.appResumeSubscription && this.appResumeSubscription.unsubscribe(); - } -} diff --git a/src/addon/mod/workshop/components/submission/addon-mod-workshop-submission.html b/src/addon/mod/workshop/components/submission/addon-mod-workshop-submission.html deleted file mode 100644 index b0b54fde5..000000000 --- a/src/addon/mod/workshop/components/submission/addon-mod-workshop-submission.html +++ /dev/null @@ -1,73 +0,0 @@ - -
- - -

-

{{profile.fullname}}

-

- {{ 'addon.mod_workshop.submissiongradeof' | translate:{$a: workshop.grade } }}: {{submission.submissiongrade}} -

-

- {{ 'addon.mod_workshop.gradeover' | translate }}: {{submission.submissiongradeover}} -

-

- {{ 'addon.mod_workshop.gradinggradeof' | translate:{$a: workshop.gradinggrade } }}: {{submission.gradinggrade}} -

- - {{ 'core.notsent' | translate }} - - - {{submission.timemodified | coreDateDayOrTime}} - {{ 'core.notsent' | translate }} - {{ 'core.deletedoffline' | translate }} - -
- - - - - - - -

{{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateByProfile.fullname} }}

- -
- - - -
- - - - -

-

{{profile.fullname}}

-

- {{ 'addon.mod_workshop.receivedgrades' | translate }}: {{submission.reviewedbycount}} / {{submission.reviewedby.length}} -

-

- {{ 'addon.mod_workshop.givengrades' | translate }}: {{submission.reviewerofcount}} / {{submission.reviewerof.length}} -

-

- {{ 'addon.mod_workshop.submissiongradeof' | translate:{$a: workshop.grade } }}: {{submission.submissiongrade}} -

-

- {{ 'addon.mod_workshop.submissiongradeof' | translate:{$a: workshop.grade } }}: {{submission.submissiongradeover}} -

-

- {{ 'addon.mod_workshop.gradinggradeof' | translate:{$a: workshop.gradinggrade } }}: {{submission.gradinggrade}} -

- - {{ 'addon.mod_workshop.assessedsubmission' | translate }} - {{ 'addon.mod_workshop.notassessed' | translate }} - - - {{submission.timemodified | coreDateDayOrTime}} -
{{ 'core.notsent' | translate }}
-
{{ 'core.deletedoffline' | translate }}
-
-
-
diff --git a/src/addon/mod/workshop/components/submission/submission.scss b/src/addon/mod/workshop/components/submission/submission.scss deleted file mode 100644 index 6b67768d9..000000000 --- a/src/addon/mod/workshop/components/submission/submission.scss +++ /dev/null @@ -1,13 +0,0 @@ -ion-app.app-root addon-mod-workshop-submission .addon-workshop-submission-title { - - &.item-ios { - border-bottom: $list-ios-header-border-bottom; - } - &.item-md { - border-bottom: 1px solid $list-md-border-color; - } - - &:last-child { - border: 0; - } -} \ No newline at end of file diff --git a/src/addon/mod/workshop/components/submission/submission.ts b/src/addon/mod/workshop/components/submission/submission.ts deleted file mode 100644 index b02bf8813..000000000 --- a/src/addon/mod/workshop/components/submission/submission.ts +++ /dev/null @@ -1,131 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModWorkshopProvider } from '../../providers/workshop'; -import { AddonModWorkshopHelperProvider } from '../../providers/helper'; -import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; - -/** - * Component that displays workshop submission. - */ -@Component({ - selector: 'addon-mod-workshop-submission', - templateUrl: 'addon-mod-workshop-submission.html', -}) -export class AddonModWorkshopSubmissionComponent implements OnInit { - @Input() submission: any; - @Input() module: any; - @Input() workshop: any; - @Input() access: any; - @Input() courseId: number; - @Input() assessment?: any; - @Input() summary?: boolean; - - component = AddonModWorkshopProvider.COMPONENT; - componentId: number; - userId: number; - loaded = false; - offline = false; - viewDetails = false; - profile: any; - showGrade: any; - evaluateByProfile: any; - - constructor(private workshopOffline: AddonModWorkshopOfflineProvider, private workshopHelper: AddonModWorkshopHelperProvider, - private navCtrl: NavController, private userProvider: CoreUserProvider, sitesProvider: CoreSitesProvider) { - this.userId = sitesProvider.getCurrentSiteUserId(); - this.showGrade = this.workshopHelper.showGrade; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.componentId = this.module.instance; - this.userId = this.submission.authorid || this.submission.userid || this.userId; - this.submission.title = this.submission.title || this.submission.submissiontitle; - this.submission.timemodified = this.submission.timemodified || this.submission.submissionmodified; - this.submission.id = this.submission.id || this.submission.submissionid; - - if (this.workshop.phase == AddonModWorkshopProvider.PHASE_ASSESSMENT) { - if (this.submission.reviewedby && this.submission.reviewedby.length) { - this.submission.reviewedbycount = this.submission.reviewedby.reduce((a, b) => { - return a + (b.grade ? 1 : 0); - }, 0); - } - - if (this.submission.reviewerof && this.submission.reviewerof.length) { - this.submission.reviewerofcount = this.submission.reviewerof.reduce((a, b) => { - return a + (b.grade ? 1 : 0); - }, 0); - } - } - - const promises = []; - - this.offline = (this.submission && this.submission.offline) || (this.assessment && this.assessment.offline); - - if (this.submission.id) { - promises.push(this.workshopOffline.getEvaluateSubmission(this.workshop.id, this.submission.id) - .then((offlineSubmission) => { - this.submission.submissiongradeover = offlineSubmission.gradeover; - this.offline = true; - }).catch(() => { - // Ignore errors. - })); - } - - if (this.userId) { - promises.push(this.userProvider.getProfile(this.userId, this.courseId, true).then((profile) => { - this.profile = profile; - })); - } - - this.viewDetails = !this.summary && this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED && - this.navCtrl.getActive().name !== 'AddonModWorkshopSubmissionPage'; - - if (this.viewDetails && this.submission.gradeoverby) { - promises.push(this.userProvider.getProfile(this.submission.gradeoverby, this.courseId, true).then((profile) => { - this.evaluateByProfile = profile; - })); - } - - Promise.all(promises).finally(() => { - this.loaded = true; - }); - } - - /** - * Navigate to the submission. - */ - gotoSubmission(): void { - if (this.submission.timemodified) { - const params = { - module: this.module, - workshop: this.workshop, - access: this.access, - courseId: this.courseId, - profile: this.profile, - submission: this.submission, - assessment: this.assessment, - }; - - this.navCtrl.push('AddonModWorkshopSubmissionPage', params); - } - } -} diff --git a/src/addon/mod/workshop/lang/en.json b/src/addon/mod/workshop/lang/en.json deleted file mode 100644 index 51cf37dfe..000000000 --- a/src/addon/mod/workshop/lang/en.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "alreadygraded": "Already graded", - "areainstructauthors": "Instructions for submission", - "areainstructreviewers": "Instructions for assessment", - "assess": "Assess", - "assessedsubmission": "Assessed submission", - "assessmentform": "Assessment form", - "assessmentsettings": "Assessment settings", - "assessmentstrategynotsupported": "Assessment strategy {{$a}} not supported", - "assessmentweight": "Assessment weight", - "assignedassessments": "Assigned submissions to assess", - "assignedassessmentsnone": "You have no assigned submission to assess", - "conclusion": "Conclusion", - "createsubmission": "Add submission", - "deletesubmission": "Delete submission", - "editsubmission": "Edit submission", - "feedbackauthor": "Feedback for the author", - "feedbackby": "Feedback by {{$a}}", - "feedbackreviewer": "Feedback for the reviewer", - "givengrades": "Grades given", - "gradecalculated": "Calculated grade for submission", - "gradeinfo": "Grade: {{$a.received}} of {{$a.max}}", - "gradeover": "Override grade for submission", - "gradesreport": "Workshop grades report", - "gradinggrade": "Grade for assessment", - "gradinggradecalculated": "Calculated grade for assessment", - "gradinggradeof": "Grade for assessment (of {{$a}})", - "gradinggradeover": "Override grade for assessment", - "modulenameplural": "Workshops", - "nogradeyet": "No grade yet", - "notassessed": "Not assessed yet", - "notoverridden": "Not overridden", - "noyoursubmission": "You have not submitted your work yet", - "overallfeedback": "Overall feedback", - "publishedsubmissions": "Published submissions", - "publishsubmission": "Publish submission", - "publishsubmission_help": "Published submissions are available to the others when the workshop is closed.", - "reassess": "Re-assess", - "receivedgrades": "Grades received", - "submissionattachment": "Attachment", - "submissioncontent": "Submission content", - "submissiondeleteconfirm": "Are you sure you want to delete the following submission?", - "submissiongrade": "Grade for submission", - "submissiongradeof": "Grade for submission (of {{$a}})", - "submissionrequiredcontent": "You need to enter some text or add a file.", - "submissionrequiredtitle": "You need to enter a title.", - "submissionsreport": "Workshop submissions report", - "submissiontitle": "Title", - "switchphase10": "Switch to the setup phase", - "switchphase20": "Switch to the submission phase", - "switchphase30": "Switch to the assessment phase", - "switchphase40": "Switch to the evaluation phase", - "switchphase50": "Close workshop", - "userplan": "Workshop planner", - "userplancurrentphase": "Current phase", - "warningassessmentmodified": "The submission was modified on the site.", - "warningsubmissionmodified": "The assessment was modified on the site.", - "weightinfo": "Weight: {{$a}}", - "yourassessment": "Your assessment", - "yourassessmentfor": "Your assessment for {{$a}}", - "yourgrades": "Your grades", - "yoursubmission": "Your submission" -} \ No newline at end of file diff --git a/src/addon/mod/workshop/pages/assessment/assessment.html b/src/addon/mod/workshop/pages/assessment/assessment.html deleted file mode 100644 index cfb72fa00..000000000 --- a/src/addon/mod/workshop/pages/assessment/assessment.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - -

{{profile.fullname}}

- -

- {{ 'addon.mod_workshop.submissiongradeof' | translate:{$a: workshop.grade } }}: {{assessment.grade}} -

-

- {{ 'addon.mod_workshop.gradinggradeof' | translate:{$a: workshop.gradinggrade } }}: {{assessment.gradinggrade}} -

-

- {{ 'addon.mod_workshop.gradinggradeover' | translate }}: {{assessment.gradinggradeover}} -

-

- {{ 'addon.mod_workshop.weightinfo' | translate:{$a: assessment.weight } }} -

- - {{ 'addon.mod_workshop.notassessed' | translate }} - -
- - - -
- -

{{ 'addon.mod_workshop.assessmentsettings' | translate }}

-
- - {{ 'addon.mod_workshop.assessmentweight' | translate }} - - {{ w }} - - - -

{{ 'addon.mod_workshop.gradinggradecalculated' | translate }}

-

{{ assessment.gradinggrade }}

-
- - {{ 'addon.mod_workshop.gradinggradeover' | translate }} - - {{grade.label}} - - - - {{ 'addon.mod_workshop.feedbackreviewer' | translate }} - - -
- - - -

{{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateGradingByProfile.fullname} }}

- -
-
-
-
diff --git a/src/addon/mod/workshop/pages/assessment/assessment.module.ts b/src/addon/mod/workshop/pages/assessment/assessment.module.ts deleted file mode 100644 index 8a2314b34..000000000 --- a/src/addon/mod/workshop/pages/assessment/assessment.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopComponentsModule } from '../../components/components.module'; -import { AddonModWorkshopAssessmentPage } from './assessment'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModWorkshopAssessmentPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - AddonModWorkshopComponentsModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonModWorkshopAssessmentPage), - TranslateModule.forChild() - ], -}) -export class AddonModWorkshopAssessmentPageModule {} diff --git a/src/addon/mod/workshop/pages/assessment/assessment.ts b/src/addon/mod/workshop/pages/assessment/assessment.ts deleted file mode 100644 index 337bd1a63..000000000 --- a/src/addon/mod/workshop/pages/assessment/assessment.ts +++ /dev/null @@ -1,379 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, NavParams, NavController } from 'ionic-angular'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; -import { AddonModWorkshopProvider } from '../../providers/workshop'; -import { AddonModWorkshopHelperProvider } from '../../providers/helper'; -import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; -import { AddonModWorkshopSyncProvider } from '../../providers/sync'; - -/** - * Page that displays a workshop assessment. - */ -@IonicPage({ segment: 'addon-mod-workshop-assessment' }) -@Component({ - selector: 'page-addon-mod-workshop-assessment-page', - templateUrl: 'assessment.html', -}) -export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy { - - @ViewChild('evaluateFormEl') formElement: ElementRef; - - assessment: any; - submission: any; - profile: any; - courseId: number; - access: any; - assessmentId: number; - evaluating = false; - loaded = false; - showGrade: any; - evaluateForm: FormGroup; - maxGrade: number; - workshop: any; - strategy: any; - title: string; - evaluate = { - text: '', - grade: -1, - weight: 1 - }; - weights = []; - evaluateByProfile: any; - evaluationGrades: any; - - protected workshopId: number; - protected originalEvaluation: any = {}; - protected hasOffline = false; - protected syncObserver: any; - protected isDestroyed = false; - protected siteId: string; - protected currentUserId: number; - protected forceLeave = false; - - constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, protected courseProvider: CoreCourseProvider, - protected workshopProvider: AddonModWorkshopProvider, protected workshopOffline: AddonModWorkshopOfflineProvider, - protected workshopHelper: AddonModWorkshopHelperProvider, protected navCtrl: NavController, - protected syncProvider: CoreSyncProvider, protected textUtils: CoreTextUtilsProvider, protected fb: FormBuilder, - protected translate: TranslateService, protected eventsProvider: CoreEventsProvider, - protected domUtils: CoreDomUtilsProvider, protected gradesHelper: CoreGradesHelperProvider, - protected userProvider: CoreUserProvider) { - - this.assessment = navParams.get('assessment'); - this.submission = navParams.get('submission') || {}; - this.profile = navParams.get('profile'); - this.courseId = navParams.get('courseId'); - - this.assessmentId = this.assessment.assessmentid || this.assessment.id; - this.workshopId = this.submission.workshopid || null; - this.siteId = sitesProvider.getCurrentSiteId(); - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - - this.showGrade = this.workshopHelper.showGrade; - - this.evaluateForm = new FormGroup({}); - this.evaluateForm.addControl('weight', this.fb.control('', Validators.required)); - this.evaluateForm.addControl('grade', this.fb.control('')); - this.evaluateForm.addControl('text', this.fb.control('')); - - // Refresh workshop on sync. - this.syncObserver = this.eventsProvider.on(AddonModWorkshopSyncProvider.AUTO_SYNCED, (data) => { - // Update just when all database is synced. - if (this.workshopId === data.workshopId) { - this.loaded = false; - this.refreshAllData(); - } - }, this.siteId); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchAssessmentData(); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave || !this.evaluating) { - return; - } - - if (!this.hasEvaluationChanged()) { - return; - } - - // Show confirmation if some data has been modified. - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.siteId); - } - - /** - * Fetch the assessment data. - * - * @return Resolved when done. - */ - protected fetchAssessmentData(): Promise { - return this.workshopProvider.getWorkshopById(this.courseId, this.workshopId).then((workshopData) => { - this.workshop = workshopData; - this.title = this.workshop.name; - this.strategy = this.workshop.strategy; - - return this.courseProvider.getModuleBasicGradeInfo(workshopData.coursemodule); - }).then((gradeInfo) => { - this.maxGrade = gradeInfo.grade; - - return this.workshopProvider.getWorkshopAccessInformation(this.workshopId, {cmId: this.workshop.coursemodule}); - }).then((accessData) => { - this.access = accessData; - - // Load Weights selector. - if (this.assessmentId && (accessData.canallocate || accessData.canoverridegrades)) { - if (!this.isDestroyed) { - // Block the workshop. - this.syncProvider.blockOperation(AddonModWorkshopProvider.COMPONENT, this.workshopId); - } - - this.evaluating = true; - } else { - this.evaluating = false; - } - - if (this.evaluating || this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { - // Get all info of the assessment. - return this.workshopHelper.getReviewerAssessmentById(this.workshopId, this.assessmentId, { - userId: this.profile && this.profile.id, - cmId: this.workshop.coursemodule, - }).then((assessment) => { - let defaultGrade, promise; - - this.assessment = this.workshopHelper.realGradeValue(this.workshop, assessment); - this.evaluate.text = this.assessment.feedbackreviewer || ''; - this.evaluate.weight = this.assessment.weight; - - if (this.evaluating) { - if (accessData.canallocate) { - this.weights = []; - for (let i = 16; i >= 0; i--) { - this.weights[i] = i; - } - } - - if (accessData.canoverridegrades) { - defaultGrade = this.translate.instant('addon.mod_workshop.notoverridden'); - promise = this.gradesHelper.makeGradesMenu(this.workshop.gradinggrade, undefined, defaultGrade, -1) - .then((grades) => { - this.evaluationGrades = grades; - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - return this.workshopOffline.getEvaluateAssessment(this.workshopId, this.assessmentId) - .then((offlineAssess) => { - this.hasOffline = true; - this.evaluate.weight = offlineAssess.weight; - if (accessData.canoverridegrades) { - this.evaluate.text = offlineAssess.feedbacktext || ''; - this.evaluate.grade = offlineAssess.gradinggradeover || -1; - } - }).catch(() => { - this.hasOffline = false; - // No offline, load online. - if (accessData.canoverridegrades) { - this.evaluate.text = this.assessment.feedbackreviewer || ''; - this.evaluate.grade = this.assessment.gradinggradeover || -1; - } - }); - }).finally(() => { - this.originalEvaluation.weight = this.evaluate.weight; - if (accessData.canoverridegrades) { - this.originalEvaluation.text = this.evaluate.text; - this.originalEvaluation.grade = this.evaluate.grade; - } - - this.evaluateForm.controls['weight'].setValue(this.evaluate.weight); - if (accessData.canoverridegrades) { - this.evaluateForm.controls['grade'].setValue(this.evaluate.grade); - this.evaluateForm.controls['text'].setValue(this.evaluate.text); - } - }); - - } else if (this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED && this.assessment.gradinggradeoverby) { - return this.userProvider.getProfile(this.assessment.gradinggradeoverby, this.courseId, true) - .then((profile) => { - this.evaluateByProfile = profile; - }); - } - }); - } - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'mm.course.errorgetmodule', true); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Force leaving the page, without checking for changes. - */ - protected forceLeavePage(): void { - this.forceLeave = true; - this.navCtrl.pop(); - } - - /** - * Check if data has changed. - * - * @return True if changed, false otherwise. - */ - protected hasEvaluationChanged(): boolean { - if (!this.loaded || !this.evaluating) { - return false; - } - - const inputData = this.evaluateForm.value; - - if (this.originalEvaluation.weight != inputData.weight) { - return true; - } - - if (this.access && this.access.canoverridegrades) { - if (this.originalEvaluation.text != inputData.text) { - return true; - } - - if (this.originalEvaluation.grade != inputData.grade) { - return true; - } - } - - return false; - } - - /** - * Convenience function to refresh all the data. - * - * @return Resolved when done. - */ - protected refreshAllData(): Promise { - const promises = []; - - promises.push(this.workshopProvider.invalidateWorkshopData(this.courseId)); - promises.push(this.workshopProvider.invalidateWorkshopAccessInformationData(this.workshopId)); - promises.push(this.workshopProvider.invalidateReviewerAssesmentsData(this.workshopId)); - - if (this.assessmentId) { - promises.push(this.workshopProvider.invalidateAssessmentFormData(this.workshopId, this.assessmentId)); - promises.push(this.workshopProvider.invalidateAssessmentData(this.workshopId, this.assessmentId)); - } - - return Promise.all(promises).finally(() => { - this.eventsProvider.trigger(AddonModWorkshopProvider.ASSESSMENT_INVALIDATED, this.siteId); - - return this.fetchAssessmentData(); - }); - } - - /** - * Pull to refresh. - * - * @param refresher Refresher. - */ - refreshAssessment(refresher: any): void { - if (this.loaded) { - this.refreshAllData().finally(() => { - refresher.complete(); - }); - } - } - - /** - * Save the assessment evaluation. - */ - saveEvaluation(): void { - // Check if data has changed. - if (this.hasEvaluationChanged()) { - this.sendEvaluation().then(() => { - this.forceLeavePage(); - }); - } else { - // Nothing to save, just go back. - this.forceLeavePage(); - } - } - - /** - * Sends the evaluation to be saved on the server. - * - * @return Resolved when done. - */ - protected sendEvaluation(): Promise { - const modal = this.domUtils.showModalLoading('core.sending', true), - inputData = this.evaluateForm.value; - - inputData.grade = inputData.grade >= 0 ? inputData.grade : ''; - // Add some HTML to the message if needed. - inputData.text = this.textUtils.formatHtmlLines(inputData.text); - - // Try to send it to server. - return this.workshopProvider.evaluateAssessment(this.workshopId, this.assessmentId, this.courseId, inputData.text, - inputData.weight, inputData.grade).then((result) => { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, !!result, this.siteId); - - const data = { - workshopId: this.workshopId, - assessmentId: this.assessmentId, - userId: this.currentUserId - }; - - return this.workshopProvider.invalidateAssessmentData(this.workshopId, this.assessmentId).finally(() => { - this.eventsProvider.trigger(AddonModWorkshopProvider.ASSESSMENT_SAVED, data, this.siteId); - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Cannot save assessment evaluation'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.isDestroyed = true; - - this.syncObserver && this.syncObserver.off(); - // Restore original back functions. - this.syncProvider.unblockOperation(AddonModWorkshopProvider.COMPONENT, this.workshopId); - } -} diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.html b/src/addon/mod/workshop/pages/edit-submission/edit-submission.html deleted file mode 100644 index 99e4d0bf3..000000000 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.html +++ /dev/null @@ -1,27 +0,0 @@ - - - {{ 'addon.mod_workshop.editsubmission' | translate }} - - - - - - - -
- - {{ 'addon.mod_workshop.submissiontitle' | translate }} - - - - - {{ 'addon.mod_workshop.submissioncontent' | translate }} - - - - -
-
-
diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.module.ts b/src/addon/mod/workshop/pages/edit-submission/edit-submission.module.ts deleted file mode 100644 index d3b216c60..000000000 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopEditSubmissionPage } from './edit-submission'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModWorkshopEditSubmissionPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonModWorkshopEditSubmissionPage), - TranslateModule.forChild() - ], -}) -export class AddonModWorkshopEditSubmissionPageModule {} diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts deleted file mode 100644 index ea8e03f45..000000000 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts +++ /dev/null @@ -1,417 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, NavParams, NavController } from 'ionic-angular'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { AddonModWorkshopProvider } from '../../providers/workshop'; -import { AddonModWorkshopHelperProvider } from '../../providers/helper'; -import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; - -/** - * Page that displays the workshop edit submission. - */ -@IonicPage({ segment: 'addon-mod-workshop-edit-submission' }) -@Component({ - selector: 'page-addon-mod-workshop-edit-submission', - templateUrl: 'edit-submission.html', -}) -export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { - - @ViewChild('editFormEl') formElement: ElementRef; - - module: any; - courseId: number; - access: any; - submission = { - id: 0, - title: '', - content: '', - attachmentfiles: [], - }; - - loaded = false; - component = AddonModWorkshopProvider.COMPONENT; - componentId: number; - editForm: FormGroup; // The form group. - editorExtraParams: {[name: string]: any} = {}; - - protected workshopId: number; - protected submissionId: number; - protected userId: number; - protected originalData: any = {}; - protected hasOffline = false; - protected editing = false; - protected forceLeave = false; - protected siteId: string; - protected workshop: any; - protected isDestroyed = false; - protected textAvailable = false; - protected textRequired = false; - protected fileAvailable = false; - protected fileRequired = false; - - constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, protected fileUploaderProvider: CoreFileUploaderProvider, - protected workshopProvider: AddonModWorkshopProvider, protected workshopOffline: AddonModWorkshopOfflineProvider, - protected workshopHelper: AddonModWorkshopHelperProvider, protected navCtrl: NavController, - protected fileSessionprovider: CoreFileSessionProvider, protected syncProvider: CoreSyncProvider, - protected textUtils: CoreTextUtilsProvider, protected domUtils: CoreDomUtilsProvider, protected fb: FormBuilder, - protected translate: TranslateService, protected eventsProvider: CoreEventsProvider) { - this.module = navParams.get('module'); - this.courseId = navParams.get('courseId'); - this.access = navParams.get('access'); - this.submissionId = navParams.get('submissionId'); - - this.workshopId = this.module.instance; - this.componentId = this.module.id; - this.userId = sitesProvider.getCurrentSiteUserId(); - this.siteId = sitesProvider.getCurrentSiteId(); - - this.editForm = new FormGroup({}); - this.editForm.addControl('title', this.fb.control('', Validators.required)); - this.editForm.addControl('content', this.fb.control('')); - - if (this.submissionId) { - this.editorExtraParams.id = this.submissionId; - } - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.isDestroyed) { - // Block the workshop. - this.syncProvider.blockOperation(this.component, this.workshopId); - } - - this.fetchSubmissionData(); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - if (this.forceLeave) { - return; - } - - // Check if data has changed. - if (this.hasDataChanged()) { - // Show confirmation if some data has been modified. - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - } - - if (this.submission.attachmentfiles) { - // Delete the local files from the tmp folder. - this.fileUploaderProvider.clearTmpFiles(this.submission.attachmentfiles); - } - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.siteId); - } - - /** - * Fetch the submission data. - * - * @return Resolved when done. - */ - protected fetchSubmissionData(): Promise { - return this.workshopProvider.getWorkshop(this.courseId, this.module.id).then((workshopData) => { - this.workshop = workshopData; - this.textAvailable = (this.workshop.submissiontypetext != AddonModWorkshopProvider.SUBMISSION_TYPE_DISABLED); - this.textRequired = (this.workshop.submissiontypetext == AddonModWorkshopProvider.SUBMISSION_TYPE_REQUIRED); - this.fileAvailable = (this.workshop.submissiontypefile != AddonModWorkshopProvider.SUBMISSION_TYPE_DISABLED); - this.fileRequired = (this.workshop.submissiontypefile == AddonModWorkshopProvider.SUBMISSION_TYPE_REQUIRED); - - this.editForm.controls.content.setValidators(this.textRequired ? Validators.required : null); - - if (this.submissionId > 0) { - this.editing = true; - - return this.workshopHelper.getSubmissionById(this.workshopId, this.submissionId, {cmId: this.module.id}) - .then((submissionData) => { - this.submission = submissionData; - - const canEdit = (this.userId == submissionData.authorid && this.access.cansubmit && - this.access.modifyingsubmissionallowed); - if (!canEdit) { - // Should not happen, but go back if does. - this.forceLeavePage(); - - return; - } - }); - } else if (!this.access.cansubmit || !this.access.creatingsubmissionallowed) { - // Should not happen, but go back if does. - this.forceLeavePage(); - - return; - } - - }).then(() => { - return this.workshopOffline.getSubmissions(this.workshopId).then((submissionsActions) => { - if (submissionsActions && submissionsActions.length) { - this.hasOffline = true; - const actions = this.workshopHelper.filterSubmissionActions(submissionsActions, this.editing ? - this.submission.id : 0); - - return this.workshopHelper.applyOfflineData(this.submission, actions); - } else { - this.hasOffline = false; - } - }).finally(() => { - this.originalData.title = this.submission.title; - this.originalData.content = this.submission.content; - this.originalData.attachmentfiles = []; - - this.submission.attachmentfiles.forEach((file) => { - let filename; - if (file.filename) { - filename = file.filename; - } else { - // We don't have filename, extract it from the path. - filename = file.filepath[0] == '/' ? file.filepath.substr(1) : file.filepath; - } - - this.originalData.attachmentfiles.push({ - filename : filename, - fileurl: file.fileurl - }); - }); - }); - }).then(() => { - this.editForm.controls['title'].setValue(this.submission.title); - this.editForm.controls['content'].setValue(this.submission.content); - - const submissionId = this.submission.id || 'newsub'; - this.fileSessionprovider.setFiles(this.component, - this.workshopId + '_' + submissionId, this.submission.attachmentfiles || []); - - this.loaded = true; - }).catch((message) => { - this.loaded = false; - - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - - this.forceLeavePage(); - }); - } - - /** - * Force leaving the page, without checking for changes. - */ - protected forceLeavePage(): void { - this.forceLeave = true; - this.navCtrl.pop(); - } - - /** - * Get the form input data. - * - * @return Object with all the info. - */ - protected getInputData(): any { - const submissionId = this.submission.id || 'newsub'; - - const values = { - title: this.editForm.value.title, - content: null, - attachmentfiles: [] - }; - - if (this.textAvailable) { - values.content = this.editForm.value.content || ''; - } - - if (this.fileAvailable) { - values.attachmentfiles = this.fileSessionprovider.getFiles(this.component, this.workshopId + '_' + submissionId) || []; - } - - return values; - } - - /** - * Check if data has changed. - * - * @return True if changed or false if not. - */ - protected hasDataChanged(): boolean { - if (!this.loaded) { - return false; - } - - const inputData = this.getInputData(); - if (!this.originalData || typeof this.originalData.title == 'undefined') { - // There is no original data, assume it hasn't changed. - return false; - } - - if (this.originalData.title != inputData.title || this.textAvailable && this.originalData.content != inputData.content) { - return true; - } - - if (this.fileAvailable) { - return this.fileUploaderProvider.areFileListDifferent(inputData.attachmentfiles, this.originalData.attachmentfiles); - } - - return false; - } - - /** - * Save the submission. - */ - save(): void { - // Check if data has changed. - if (this.hasDataChanged()) { - this.saveSubmission().then(() => { - // Go back to entry list. - this.forceLeavePage(); - }).catch(() => { - // Nothing to do. - }); - } else { - // Nothing to save, just go back. - this.forceLeavePage(); - } - } - - /** - * Send submission and save. - * - * @return Resolved when done. - */ - protected saveSubmission(): Promise { - const inputData = this.getInputData(); - - if (!inputData.title) { - this.domUtils.showAlertTranslated('core.notice', 'addon.mod_workshop.submissionrequiredtitle'); - - return Promise.reject(null); - } - - const noText = this.textUtils.htmlIsBlank(inputData.content); - const noFiles = !inputData.attachmentfiles.length; - - if ((this.textRequired && noText) || (this.fileRequired && noFiles) || (noText && noFiles)) { - this.domUtils.showAlertTranslated('core.notice', 'addon.mod_workshop.submissionrequiredcontent'); - - return Promise.reject(null); - } - - let allowOffline = true, - saveOffline = false; - - const modal = this.domUtils.showModalLoading('core.sending', true), - submissionId = this.submission.id; - - // Add some HTML to the message if needed. - if (this.textAvailable) { - inputData.content = this.textUtils.formatHtmlLines(inputData.content); - } - - // Upload attachments first if any. - allowOffline = !inputData.attachmentfiles.length; - - return this.workshopHelper.uploadOrStoreSubmissionFiles(this.workshopId, this.submission.id, inputData.attachmentfiles, - this.editing, saveOffline).catch(() => { - // Cannot upload them in online, save them in offline. - saveOffline = true; - allowOffline = true; - - return this.workshopHelper.uploadOrStoreSubmissionFiles(this.workshopId, this.submission.id, - inputData.attachmentfiles, this.editing, saveOffline); - }).then((attachmentsId) => { - if (!saveOffline && !this.fileAvailable) { - attachmentsId = null; - } - - if (this.editing) { - if (saveOffline) { - // Save submission in offline. - return this.workshopOffline.saveSubmission(this.workshopId, this.courseId, inputData.title, - inputData.content, attachmentsId, submissionId, 'update').then(() => { - return false; - }); - } - - // Try to send it to server. - // Don't allow offline if there are attachments since they were uploaded fine. - return this.workshopProvider.updateSubmission(this.workshopId, submissionId, this.courseId, inputData.title, - inputData.content, attachmentsId, undefined, allowOffline); - } - - if (saveOffline) { - // Save submission in offline. - return this.workshopOffline.saveSubmission(this.workshopId, this.courseId, inputData.title, inputData.content, - attachmentsId, submissionId, 'add').then(() => { - return false; - }); - } - - // Try to send it to server. - // Don't allow offline if there are attachments since they were uploaded fine. - return this.workshopProvider.addSubmission(this.workshopId, this.courseId, inputData.title, inputData.content, - attachmentsId, undefined, submissionId, allowOffline); - }).then((newSubmissionId) => { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, !!newSubmissionId, this.siteId); - - const data = { - workshopId: this.workshopId, - cmId: this.module.cmid - }; - - if (newSubmissionId && submissionId) { - // Data sent to server, delete stored files (if any). - this.workshopOffline.deleteSubmissionAction(this.workshopId, submissionId, this.editing ? 'update' : 'add'); - this.workshopHelper.deleteSubmissionStoredFiles(this.workshopId, submissionId, this.editing); - data['submissionId'] = newSubmissionId; - } - - this.eventsProvider.trigger(CoreEventsProvider.ACTIVITY_DATA_SENT, { module: 'workshop' }); - - const promise = newSubmissionId ? this.workshopProvider.invalidateSubmissionData(this.workshopId, newSubmissionId) : - Promise.resolve(); - - return promise.finally(() => { - this.eventsProvider.trigger(AddonModWorkshopProvider.SUBMISSION_CHANGED, data, this.siteId); - - // Delete the local files from the tmp folder. - this.fileUploaderProvider.clearTmpFiles(inputData.attachmentfiles); - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Cannot save submission'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.isDestroyed = true; - this.syncProvider.unblockOperation(this.component, this.workshopId); - } -} diff --git a/src/addon/mod/workshop/pages/index/index.html b/src/addon/mod/workshop/pages/index/index.html deleted file mode 100644 index f15e75129..000000000 --- a/src/addon/mod/workshop/pages/index/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/addon/mod/workshop/pages/index/index.module.ts b/src/addon/mod/workshop/pages/index/index.module.ts deleted file mode 100644 index 5d9d3651d..000000000 --- a/src/addon/mod/workshop/pages/index/index.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopComponentsModule } from '../../components/components.module'; -import { AddonModWorkshopIndexPage } from './index'; - -@NgModule({ - declarations: [ - AddonModWorkshopIndexPage, - ], - imports: [ - CoreDirectivesModule, - AddonModWorkshopComponentsModule, - IonicPageModule.forChild(AddonModWorkshopIndexPage), - TranslateModule.forChild() - ], -}) -export class AddonModWorkshopIndexPageModule {} diff --git a/src/addon/mod/workshop/pages/index/index.ts b/src/addon/mod/workshop/pages/index/index.ts deleted file mode 100644 index 52bca7912..000000000 --- a/src/addon/mod/workshop/pages/index/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { AddonModWorkshopIndexComponent } from '../../components/index/index'; - -/** - * Page that displays a workshop. - */ -@IonicPage({ segment: 'addon-mod-workshop-index' }) -@Component({ - selector: 'page-addon-mod-workshop-index', - templateUrl: 'index.html', -}) -export class AddonModWorkshopIndexPage { - @ViewChild(AddonModWorkshopIndexComponent) workshopComponent: AddonModWorkshopIndexComponent; - - title: string; - module: any; - courseId: number; - selectedGroup: number; - - constructor(navParams: NavParams) { - this.module = navParams.get('module') || {}; - this.courseId = navParams.get('courseId'); - this.selectedGroup = navParams.get('group') || 0; - this.title = this.module.name; - } - - /** - * Update some data based on the workshop instance. - * - * @param workshop Workshop instance. - */ - updateData(workshop: any): void { - this.title = workshop.name || this.title; - } -} diff --git a/src/addon/mod/workshop/pages/phase/phase.html b/src/addon/mod/workshop/pages/phase/phase.html deleted file mode 100644 index 912348a3e..000000000 --- a/src/addon/mod/workshop/pages/phase/phase.html +++ /dev/null @@ -1,35 +0,0 @@ - - - {{ 'addon.mod_workshop.userplan' | translate }} - - - - - - - - - -

{{ phase.title }}

-

{{ 'addon.mod_workshop.userplancurrentphase' | translate }}

-
- - - {{ 'addon.mod_workshop.switchphase' + phase.code | translate }} - - - - - - - - -

{{task.title}}

-

- -
-
-
-
diff --git a/src/addon/mod/workshop/pages/phase/phase.module.ts b/src/addon/mod/workshop/pages/phase/phase.module.ts deleted file mode 100644 index bf971d310..000000000 --- a/src/addon/mod/workshop/pages/phase/phase.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopPhaseInfoPage } from './phase'; -import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module'; - -@NgModule({ - declarations: [ - AddonModWorkshopPhaseInfoPage, - ], - imports: [ - CoreDirectivesModule, - CoreCompileHtmlComponentModule, - IonicPageModule.forChild(AddonModWorkshopPhaseInfoPage), - TranslateModule.forChild() - ], -}) -export class AddonModWorkshopPhaseInfoPageModule {} diff --git a/src/addon/mod/workshop/pages/phase/phase.scss b/src/addon/mod/workshop/pages/phase/phase.scss deleted file mode 100644 index f30416066..000000000 --- a/src/addon/mod/workshop/pages/phase/phase.scss +++ /dev/null @@ -1,6 +0,0 @@ -ion-app.app-root page-addon-mod-workshop-phase-info { - .core-workshop-phase-selected { - background-color: $white; - @include core-selected-item($core-splitview-selected); - } -} \ No newline at end of file diff --git a/src/addon/mod/workshop/pages/phase/phase.ts b/src/addon/mod/workshop/pages/phase/phase.ts deleted file mode 100644 index 8006680a6..000000000 --- a/src/addon/mod/workshop/pages/phase/phase.ts +++ /dev/null @@ -1,73 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams, ViewController } from 'ionic-angular'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Page that displays the phase info modal. - */ -@IonicPage({ segment: 'addon-mod-workshop-phase-info' }) -@Component({ - selector: 'page-addon-mod-workshop-phase-info', - templateUrl: 'phase.html', -}) -export class AddonModWorkshopPhaseInfoPage { - phases: any; - workshopPhase: number; - showSubmit: boolean; - - constructor(params: NavParams, private viewCtrl: ViewController, private utils: CoreUtilsProvider) { - this.phases = params.get('phases'); - this.workshopPhase = params.get('workshopPhase'); - this.showSubmit = params.get('showSubmit'); - const externalUrl = params.get('externalUrl'); - - // Treat phases. - for (const x in this.phases) { - this.phases[x].tasks.forEach((task) => { - if (!task.link && (task.code == 'examples' || task.code == 'prepareexamples')) { - // Add links to manage examples. - task.link = externalUrl; - } - }); - const action = this.phases[x].actions.find((action) => { - return action.url && action.type == 'switchphase'; - }); - this.phases[x].switchUrl = action ? action.url : ''; - } - } - - /** - * Close modal. - */ - closeModal(): void { - this.viewCtrl.dismiss(); - } - - /** - * Open task. - * - * @param task Task to be done. - */ - runTask(task: any): void { - if (task.code == 'submit') { - // This will close the modal and go to the submit. - this.viewCtrl.dismiss(true); - } else if (task.link) { - this.utils.openInBrowser(task.link); - } - } -} diff --git a/src/addon/mod/workshop/pages/submission/submission.html b/src/addon/mod/workshop/pages/submission/submission.html deleted file mode 100644 index 5bff46089..000000000 --- a/src/addon/mod/workshop/pages/submission/submission.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -

{{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateByProfile.fullname} }}

- -
-
- - - -

{{ 'addon.mod_workshop.yourassessment' | translate }}

-
- -
- - - -

{{ 'addon.mod_workshop.receivedgrades' | translate }}

-
- - - -
- - - -

{{ 'addon.mod_workshop.givengrades' | translate }}

-
- -
- -
- -

{{ 'addon.mod_workshop.feedbackauthor' | translate }}

-
- - {{ 'addon.mod_workshop.publishsubmission' | translate }} - -

{{ 'addon.mod_workshop.publishsubmission_help' | translate }}

-
- - -

{{ 'addon.mod_workshop.gradecalculated' | translate }}

-

{{ submission.submissiongrade }}

-
- - {{ 'addon.mod_workshop.gradeover' | translate }} - - {{grade.label}} - - - - {{ 'addon.mod_workshop.feedbackauthor' | translate }} - - -
- - - - - - -

{{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateGradingByProfile.fullname} }}

- -
-
-
-
diff --git a/src/addon/mod/workshop/pages/submission/submission.module.ts b/src/addon/mod/workshop/pages/submission/submission.module.ts deleted file mode 100644 index 6484fdc06..000000000 --- a/src/addon/mod/workshop/pages/submission/submission.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonModWorkshopComponentsModule } from '../../components/components.module'; -import { AddonModWorkshopSubmissionPage } from './submission'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonModWorkshopSubmissionPage, - ], - imports: [ - CoreDirectivesModule, - CoreComponentsModule, - AddonModWorkshopComponentsModule, - CoreEditorComponentsModule, - IonicPageModule.forChild(AddonModWorkshopSubmissionPage), - TranslateModule.forChild() - ], -}) -export class AddonModWorkshopSubmissionPageModule {} diff --git a/src/addon/mod/workshop/pages/submission/submission.ts b/src/addon/mod/workshop/pages/submission/submission.ts deleted file mode 100644 index f1d0ec97b..000000000 --- a/src/addon/mod/workshop/pages/submission/submission.ts +++ /dev/null @@ -1,556 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, Optional, ViewChild, ElementRef } from '@angular/core'; -import { Content, IonicPage, NavParams, NavController } from 'ionic-angular'; -import { FormGroup, FormBuilder } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; -import { AddonModWorkshopAssessmentStrategyComponent } from '../../components/assessment-strategy/assessment-strategy'; -import { AddonModWorkshopProvider } from '../../providers/workshop'; -import { AddonModWorkshopHelperProvider } from '../../providers/helper'; -import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; -import { AddonModWorkshopSyncProvider } from '../../providers/sync'; - -/** - * Page that displays a workshop submission. - */ -@IonicPage({ segment: 'addon-mod-workshop-submission' }) -@Component({ - selector: 'page-addon-mod-workshop-submission-page', - templateUrl: 'submission.html', -}) -export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { - - @ViewChild(AddonModWorkshopAssessmentStrategyComponent) assessmentStrategy: AddonModWorkshopAssessmentStrategyComponent; - @ViewChild('feedbackFormEl') formElement: ElementRef; - - module: any; - workshop: any; - access: any; - assessment: any; - submissionInfo: any; - submission: any; - - courseId: number; - profile: any; - - title: string; - loaded = false; - ownAssessment = false; - strategy: any; - assessmentId: number; - assessmentUserId: number; - evaluate: any; - canAddFeedback = false; - canEdit = false; - canDelete = false; - evaluationGrades: any; - evaluateGradingByProfile: any; - evaluateByProfile: any; - feedbackForm: FormGroup; // The form group. - - protected submissionId: number; - protected workshopId: number; - protected currentUserId: number; - protected userId: number; - protected siteId: string; - protected originalEvaluation = { - published: '', - text: '', - grade: '' - }; - protected hasOffline = false; - protected component = AddonModWorkshopProvider.COMPONENT; - protected forceLeave = false; - protected obsAssessmentSaved: any; - protected syncObserver: any; - protected isDestroyed = false; - - constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, protected workshopProvider: AddonModWorkshopProvider, - protected workshopOffline: AddonModWorkshopOfflineProvider, protected syncProvider: CoreSyncProvider, - protected workshopHelper: AddonModWorkshopHelperProvider, protected navCtrl: NavController, - protected textUtils: CoreTextUtilsProvider, protected domUtils: CoreDomUtilsProvider, protected fb: FormBuilder, - protected translate: TranslateService, protected eventsProvider: CoreEventsProvider, - protected courseProvider: CoreCourseProvider, @Optional() protected content: Content, - protected gradesHelper: CoreGradesHelperProvider, protected userProvider: CoreUserProvider) { - this.module = navParams.get('module'); - this.workshop = navParams.get('workshop'); - this.access = navParams.get('access'); - this.courseId = navParams.get('courseId'); - this.profile = navParams.get('profile'); - this.submissionInfo = navParams.get('submission') || {}; - this.assessment = navParams.get('assessment') || null; - - this.title = this.module.name; - this.workshopId = this.module.instance; - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - this.siteId = sitesProvider.getCurrentSiteId(); - this.submissionId = this.submissionInfo.submissionid || this.submissionInfo.id; - this.userId = this.submissionInfo.userid || null; - this.strategy = (this.assessment && this.assessment.strategy) || (this.workshop && this.workshop.strategy); - this.assessmentId = this.assessment && (this.assessment.assessmentid || this.assessment.id); - this.assessmentUserId = this.assessment && (this.assessment.reviewerid || this.assessment.userid); - - this.feedbackForm = new FormGroup({}); - this.feedbackForm.addControl('published', this.fb.control('')); - this.feedbackForm.addControl('grade', this.fb.control('')); - this.feedbackForm.addControl('text', this.fb.control('')); - - this.obsAssessmentSaved = this.eventsProvider.on(AddonModWorkshopProvider.ASSESSMENT_SAVED, (data) => { - this.eventReceived(data); - }, this.siteId); - - // Refresh workshop on sync. - this.syncObserver = this.eventsProvider.on(AddonModWorkshopSyncProvider.AUTO_SYNCED, (data) => { - // Update just when all database is synced. - this.eventReceived(data); - }, this.siteId); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchSubmissionData().then(() => { - this.workshopProvider.logViewSubmission(this.submissionId, this.workshopId, this.workshop.name).then(() => { - this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); - }).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Check if we can leave the page or not. - * - * @return Resolved if we can leave it, rejected if not. - */ - async ionViewCanLeave(): Promise { - const assessmentHasChanged = this.assessmentStrategy && this.assessmentStrategy.hasDataChanged(); - if (this.forceLeave || (!this.hasEvaluationChanged() && !assessmentHasChanged)) { - return; - } - - // Show confirmation if some data has been modified. - await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); - - this.domUtils.triggerFormCancelledEvent(this.formElement, this.siteId); - } - - /** - * Goto edit submission page. - */ - editSubmission(): void { - const params = { - module: module, - access: this.access, - courseid: this.courseId, - submissionId: this.submission.id - }; - - this.navCtrl.push('AddonModWorkshopEditSubmissionPage', params); - } - - /** - * Function called when we receive an event of submission changes. - * - * @param data Event data received. - */ - protected eventReceived(data: any): void { - if (this.workshopId === data.workshopId) { - this.domUtils.scrollToTop(this.content); - - this.loaded = false; - this.refreshAllData(); - } - } - - /** - * Fetch the submission data. - * - * @return Resolved when done. - */ - protected fetchSubmissionData(): Promise { - return this.workshopHelper.getSubmissionById(this.workshopId, this.submissionId, { - cmId: this.module.id, - }).then((submissionData) => { - const promises = []; - - this.submission = submissionData; - this.submission.attachmentfiles = submissionData.attachmentfiles || []; - this.submission.submissiongrade = this.submissionInfo && this.submissionInfo.submissiongrade; - this.submission.gradinggrade = this.submissionInfo && this.submissionInfo.gradinggrade; - this.submission.submissiongradeover = this.submissionInfo && this.submissionInfo.submissiongradeover; - this.userId = submissionData.authorid || this.userId; - this.canEdit = this.currentUserId == this.userId && this.access.cansubmit && this.access.modifyingsubmissionallowed; - this.canDelete = this.access.candeletesubmissions; - this.canAddFeedback = !this.assessmentId && this.workshop.phase > AddonModWorkshopProvider.PHASE_ASSESSMENT && - this.workshop.phase < AddonModWorkshopProvider.PHASE_CLOSED && this.access.canoverridegrades; - this.ownAssessment = false; - - if (this.access.canviewallassessments) { - // Get new data, different that came from stateParams. - promises.push(this.workshopProvider.getSubmissionAssessments(this.workshopId, this.submissionId, { - cmId: this.module.id, - }).then((subAssessments) => { - // Only allow the student to delete their own submission if it's still editable and hasn't been assessed. - if (this.canDelete) { - this.canDelete = !subAssessments.length; - } - - this.submissionInfo.reviewedby = subAssessments; - - this.submissionInfo.reviewedby.forEach((assessment) => { - assessment.userid = assessment.reviewerid; - assessment = this.workshopHelper.realGradeValue(this.workshop, assessment); - - if (this.currentUserId == assessment.userid) { - this.ownAssessment = assessment; - assessment.ownAssessment = true; - } - }); - })); - } else if (this.currentUserId == this.userId && this.assessmentId) { - // Get new data, different that came from stateParams. - promises.push(this.workshopProvider.getAssessment(this.workshopId, this.assessmentId, { - cmId: this.module.id, - }).then((assessment) => { - // Only allow the student to delete their own submission if it's still editable and hasn't been assessed. - if (this.canDelete) { - this.canDelete = !assessment; - } - - assessment = this.parseAssessment(assessment); - - this.submissionInfo.reviewedby = [assessment]; - })); - } else if (this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED && this.userId == this.currentUserId) { - this.workshopProvider.getSubmissionAssessments(this.workshopId, this.submissionId, { - cmId: this.module.id, - }).then((assessments) => { - this.submissionInfo.reviewedby = assessments.map((assessment) => { - return this.parseAssessment(assessment); - }); - }); - } - - if (this.canAddFeedback || this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { - this.evaluate = { - published: submissionData.published, - text: submissionData.feedbackauthor || '' - }; - } - - if (this.canAddFeedback) { - - if (!this.isDestroyed) { - // Block the workshop. - this.syncProvider.blockOperation(this.component, this.workshopId); - } - - const defaultGrade = this.translate.instant('addon.mod_workshop.notoverridden'); - - promises.push(this.gradesHelper.makeGradesMenu(this.workshop.grade, undefined, defaultGrade, -1).then((grades) => { - this.evaluationGrades = grades; - - this.evaluate.grade = { - label: this.gradesHelper.getGradeLabelFromValue(grades, this.submissionInfo.submissiongradeover) || - defaultGrade, - value: this.submissionInfo.submissiongradeover || -1 - }; - - return this.workshopOffline.getEvaluateSubmission(this.workshopId, this.submissionId) - .then((offlineSubmission) => { - this.hasOffline = true; - this.evaluate.published = offlineSubmission.published; - this.evaluate.text = offlineSubmission.feedbacktext; - this.evaluate.grade = { - label: this.gradesHelper.getGradeLabelFromValue(grades, offlineSubmission.gradeover) || defaultGrade, - value: offlineSubmission.gradeover || -1 - }; - }).catch(() => { - this.hasOffline = false; - // Ignore errors. - }).finally(() => { - this.originalEvaluation.published = this.evaluate.published; - this.originalEvaluation.text = this.evaluate.text; - this.originalEvaluation.grade = this.evaluate.grade.value; - - this.feedbackForm.controls['published'].setValue(this.evaluate.published); - this.feedbackForm.controls['grade'].setValue(this.evaluate.grade.value); - this.feedbackForm.controls['text'].setValue(this.evaluate.text); - }); - })); - } else if (this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED && submissionData.gradeoverby && - this.evaluate && this.evaluate.text) { - promises.push(this.userProvider.getProfile(submissionData.gradeoverby, this.courseId, true).then((profile) => { - this.evaluateByProfile = profile; - })); - } - - if (this.assessmentId && !this.access.assessingallowed && this.assessment.feedbackreviewer && - this.assessment.gradinggradeoverby) { - promises.push(this.userProvider.getProfile(this.assessment.gradinggradeoverby, this.courseId, true) - .then((profile) => { - this.evaluateGradingByProfile = profile; - })); - } - - return Promise.all(promises); - }).then(() => { - return this.workshopOffline.getSubmissions(this.workshopId).then((submissionsActions) => { - const actions = this.workshopHelper.filterSubmissionActions(submissionsActions, this.submissionId); - - return this.workshopHelper.applyOfflineData(this.submission, actions).then((submission) => { - this.submission = submission; - }); - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true); - }).finally(() => { - this.loaded = true; - }); - } - - /** - * Parse assessment to be shown. - * - * @param assessment Original assessment. - * @return Parsed assessment. - */ - protected parseAssessment(assessment: any): any { - assessment.userid = assessment.reviewerid; - assessment = this.workshopHelper.realGradeValue(this.workshop, assessment); - - if (this.currentUserId == assessment.userid) { - this.ownAssessment = assessment; - assessment.ownAssessment = true; - } - - return assessment; - } - - /** - * Force leaving the page, without checking for changes. - */ - protected forceLeavePage(): void { - this.forceLeave = true; - this.navCtrl.pop(); - } - - /** - * Check if data has changed. - * - * @return True if changed, false otherwise. - */ - protected hasEvaluationChanged(): boolean { - if (!this.loaded || !this.access.canoverridegrades) { - return false; - } - - const inputData = this.feedbackForm.value; - - if (this.originalEvaluation.published != inputData.published) { - return true; - } - - if (this.originalEvaluation.text != inputData.text) { - return true; - } - - if (this.originalEvaluation.grade != inputData.grade) { - return true; - } - - return false; - } - - /** - * Convenience function to refresh all the data. - * - * @return Resolved when done. - */ - protected refreshAllData(): Promise { - const promises = []; - - promises.push(this.workshopProvider.invalidateSubmissionData(this.workshopId, this.submissionId)); - promises.push(this.workshopProvider.invalidateSubmissionsData(this.workshopId)); - promises.push(this.workshopProvider.invalidateSubmissionAssesmentsData(this.workshopId, this.submissionId)); - - if (this.assessmentId) { - promises.push(this.workshopProvider.invalidateAssessmentFormData(this.workshopId, this.assessmentId)); - promises.push(this.workshopProvider.invalidateAssessmentData(this.workshopId, this.assessmentId)); - } - - if (this.assessmentUserId) { - promises.push(this.workshopProvider.invalidateReviewerAssesmentsData(this.workshopId, this.assessmentId)); - } - - return Promise.all(promises).finally(() => { - this.eventsProvider.trigger(AddonModWorkshopProvider.ASSESSMENT_INVALIDATED, this.siteId); - - return this.fetchSubmissionData(); - }); - } - - /** - * Pull to refresh. - * - * @param refresher Refresher. - */ - refreshSubmission(refresher: any): void { - if (this.loaded) { - this.refreshAllData().finally(() => { - refresher.complete(); - }); - } - } - - /** - * Save the assessment. - */ - saveAssessment(): void { - if (this.assessmentStrategy && this.assessmentStrategy.hasDataChanged()) { - this.assessmentStrategy.saveAssessment().then(() => { - this.forceLeavePage(); - }).catch(() => { - // Error, stay on the page. - }); - } else { - // Nothing to save, just go back. - this.forceLeavePage(); - } - } - - /** - * Save the submission evaluation. - */ - saveEvaluation(): void { - // Check if data has changed. - if (this.hasEvaluationChanged()) { - this.sendEvaluation().then(() => { - this.forceLeavePage(); - }); - } else { - // Nothing to save, just go back. - this.forceLeavePage(); - } - } - - /** - * Sends the evaluation to be saved on the server. - * - * @return Resolved when done. - */ - protected sendEvaluation(): Promise { - const modal = this.domUtils.showModalLoading('core.sending', true); - - const inputData = this.feedbackForm.value; - - inputData.grade = inputData.grade >= 0 ? inputData.grade : ''; - // Add some HTML to the message if needed. - inputData.text = this.textUtils.formatHtmlLines(inputData.text); - - // Try to send it to server. - return this.workshopProvider.evaluateSubmission(this.workshopId, this.submissionId, this.courseId, inputData.text, - inputData.published, inputData.grade).then((result) => { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, !!result, this.siteId); - - const data = { - workshopId: this.workshopId, - cmId: this.module.cmid, - submissionId: this.submissionId - }; - - return this.workshopProvider.invalidateSubmissionData(this.workshopId, this.submissionId).finally(() => { - this.eventsProvider.trigger(AddonModWorkshopProvider.SUBMISSION_CHANGED, data, this.siteId); - }); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'Cannot save submission evaluation'); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Perform the submission delete action. - */ - deleteSubmission(): void { - this.domUtils.showDeleteConfirm('addon.mod_workshop.submissiondeleteconfirm').then(() => { - const modal = this.domUtils.showModalLoading('core.deleting', true); - let success = false; - this.workshopProvider.deleteSubmission(this.workshopId, this.submissionId, this.courseId).then(() => { - success = true; - - return this.workshopProvider.invalidateSubmissionData(this.workshopId, this.submissionId); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Cannot delete submission'); - }).finally(() => { - modal.dismiss(); - if (success) { - const data = { - workshopId: this.workshopId, - cmId: this.module.cmid, - submissionId: this.submissionId - }; - - this.eventsProvider.trigger(AddonModWorkshopProvider.SUBMISSION_CHANGED, data, this.siteId); - - this.forceLeavePage(); - } - }); - }); - } - - /** - * Undo the submission delete action. - * - * @return Resolved when done. - */ - undoDeleteSubmission(): Promise { - return this.workshopOffline.deleteSubmissionAction(this.workshopId, this.submissionId, 'delete').finally(() => { - - const data = { - workshopId: this.workshopId, - cmId: this.module.cmid, - submissionId: this.submissionId - }; - - this.eventsProvider.trigger(AddonModWorkshopProvider.SUBMISSION_CHANGED, data, this.siteId); - - return this.refreshAllData(); - }); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.isDestroyed = true; - - this.syncObserver && this.syncObserver.off(); - this.obsAssessmentSaved && this.obsAssessmentSaved.off(); - // Restore original back functions. - this.syncProvider.unblockOperation(this.component, this.workshopId); - } -} diff --git a/src/addon/mod/workshop/providers/assessment-strategy-delegate.ts b/src/addon/mod/workshop/providers/assessment-strategy-delegate.ts deleted file mode 100644 index 2d5b7aae5..000000000 --- a/src/addon/mod/workshop/providers/assessment-strategy-delegate.ts +++ /dev/null @@ -1,137 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; - -/** - * Interface that all assessment strategy handlers must implement. - */ -export interface AddonWorkshopAssessmentStrategyHandler extends CoreDelegateHandler { - /** - * The name of the assessment strategy. E.g. 'accumulative'. - */ - strategyName: string; - - /** - * Return the Component to render the plugin. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent?(injector: Injector): any | Promise; - - /** - * Prepare original values to be shown and compared. - * - * @param form Original data of the form. - * @param workshopId WorkShop Id - * @return Promise resolved with original values sorted. - */ - getOriginalValues?(form: any, workshopId: number): Promise; - - /** - * Check if the assessment data has changed for a certain submission and workshop for a this strategy plugin. - * - * @param originalValues Original values of the form. - * @param currentValues Current values of the form. - * @return True if data has changed, false otherwise. - */ - hasDataChanged?(originalValues: any[], currentValues: any[]): boolean; - - /** - * Prepare assessment data to be sent to the server depending on the strategy selected. - * - * @param currentValues Current values of the form. - * @param form Assessment form data. - * @return Promise resolved with the data to be sent. Or rejected with the input errors object. - */ - prepareAssessmentData(currentValues: any[], form: any): Promise; -} - -/** - * Delegate to register workshop assessment strategy handlers. - * You can use this service to register your own assessment strategy handlers to be used in a workshop. - */ - @Injectable() - export class AddonWorkshopAssessmentStrategyDelegate extends CoreDelegate { - - protected handlerNameProperty = 'strategyName'; - - constructor(protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, - protected eventsProvider: CoreEventsProvider) { - super('AddonWorkshopAssessmentStrategyDelegate', loggerProvider, sitesProvider, eventsProvider); - } - - /** - * Check if an assessment strategy plugin is supported. - * - * @param workshopStrategy Assessment strategy name. - * @return True if supported, false otherwise. - */ - isPluginSupported(workshopStrategy: string): boolean { - return this.hasHandler(workshopStrategy, true); - } - - /** - * Get the directive to use for a certain assessment strategy plugin. - * - * @param injector Injector. - * @param workshopStrategy Assessment strategy name. - * @return The component, undefined if not found. - */ - getComponentForPlugin(injector: Injector, workshopStrategy: string): Promise { - return this.executeFunctionOnEnabled(workshopStrategy, 'getComponent', [injector]); - } - - /** - * Prepare original values to be shown and compared depending on the strategy selected. - * - * @param workshopStrategy Workshop strategy. - * @param form Original data of the form. - * @param workshopId Workshop ID. - * @return Resolved with original values sorted. - */ - getOriginalValues(workshopStrategy: string, form: any, workshopId: number): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(workshopStrategy, 'getOriginalValues', [form, workshopId]) || []); - } - - /** - * Check if the assessment data has changed for a certain submission and workshop for a this strategy plugin. - * - * @param workshop Workshop. - * @param originalValues Original values of the form. - * @param currentValues Current values of the form. - * @return True if data has changed, false otherwise. - */ - hasDataChanged(workshop: any, originalValues: any[], currentValues: any[]): boolean { - return this.executeFunctionOnEnabled(workshop.strategy, 'hasDataChanged', [originalValues, currentValues]) || false; - } - - /** - * Prepare assessment data to be sent to the server depending on the strategy selected. - * - * @param workshopStrategy Workshop strategy to follow. - * @param currentValues Current values of the form. - * @param form Assessment form data. - * @return Promise resolved with the data to be sent. Or rejected with the input errors object. - */ - prepareAssessmentData(workshopStrategy: string, currentValues: any, form: any): Promise { - return Promise.resolve(this.executeFunctionOnEnabled(workshopStrategy, 'prepareAssessmentData', [currentValues, form])); - } -} diff --git a/src/addon/mod/workshop/providers/helper.ts b/src/addon/mod/workshop/providers/helper.ts deleted file mode 100644 index 7adf38209..000000000 --- a/src/addon/mod/workshop/providers/helper.ts +++ /dev/null @@ -1,523 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreFileProvider } from '@providers/file'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModWorkshopProvider, AddonModWorkshopUserOptions } from './workshop'; -import { AddonModWorkshopOfflineProvider } from './offline'; -import { AddonWorkshopAssessmentStrategyDelegate } from './assessment-strategy-delegate'; - -/** - * Helper to gather some common functions for workshop. - */ -@Injectable() -export class AddonModWorkshopHelperProvider { - - constructor( - private translate: TranslateService, - private fileProvider: CoreFileProvider, - private uploaderProvider: CoreFileUploaderProvider, - private sitesProvider: CoreSitesProvider, - private textUtils: CoreTextUtilsProvider, - private utils: CoreUtilsProvider, - private workshopProvider: AddonModWorkshopProvider, - private workshopOffline: AddonModWorkshopOfflineProvider, - private strategyDelegate: AddonWorkshopAssessmentStrategyDelegate) {} - - /** - * Get a task by code. - * - * @param tasks Array of tasks. - * @param taskCode Unique task code. - * @return Task requested - */ - getTask(tasks: any[], taskCode: string): any { - for (const x in tasks) { - if (tasks[x].code == taskCode) { - return tasks[x]; - } - } - - return false; - } - - /** - * Check is task code is done. - * - * @param tasks Array of tasks. - * @param taskCode Unique task code. - * @return True if task is completed. - */ - isTaskDone(tasks: any[], taskCode: string): boolean { - const task = this.getTask(tasks, taskCode); - - if (task) { - return !!task.completed; - } - - // Task not found, assume true. - return true; - } - - /** - * Return if a user can submit a workshop. - * - * @param workshop Workshop info. - * @param access Access information. - * @param tasks Array of tasks. - * @return True if the user can submit the workshop. - */ - canSubmit(workshop: any, access: any, tasks: any[]): boolean { - const examplesMust = workshop.useexamples && workshop.examplesmode == AddonModWorkshopProvider.EXAMPLES_BEFORE_SUBMISSION; - const examplesDone = access.canmanageexamples || workshop.examplesmode == AddonModWorkshopProvider.EXAMPLES_VOLUNTARY || - this.isTaskDone(tasks, 'examples'); - - return workshop.phase > AddonModWorkshopProvider.PHASE_SETUP && access.cansubmit && (!examplesMust || examplesDone); - } - - /** - * Return if a user can assess a workshop. - * - * @param workshop Workshop info. - * @param access Access information. - * @return True if the user can assess the workshop. - */ - canAssess(workshop: any, access: any): boolean { - const examplesMust = workshop.useexamples && workshop.examplesmode == AddonModWorkshopProvider.EXAMPLES_BEFORE_ASSESSMENT; - const examplesDone = access.canmanageexamples; - - return !examplesMust || examplesDone; - } - - /** - * Return a particular user submission from the submission list. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Resolved with the submission, resolved with false if not found. - */ - getUserSubmission(workshopId: number, options: AddonModWorkshopUserOptions = {}): Promise { - const userId = options.userId || this.sitesProvider.getCurrentSiteUserId(); - - return this.workshopProvider.getSubmissions(workshopId, options).then((submissions) => { - - for (const x in submissions) { - if (submissions[x].authorid == userId) { - return submissions[x]; - } - } - - return false; - }); - } - - /** - * Return a particular submission. It will use prefetched data if fetch fails. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param options Other options. - * @return Resolved with the submission, resolved with false if not found. - */ - getSubmissionById(workshopId: number, submissionId: number, options: {cmId?: number, siteId?: string} = {}): Promise { - return this.workshopProvider.getSubmission(workshopId, submissionId, options).catch(() => { - return this.workshopProvider.getSubmissions(workshopId, options).then((submissions) => { - for (const x in submissions) { - if (submissions[x].id == submissionId) { - return submissions[x]; - } - } - - return false; - }); - }); - } - - /** - * Return a particular assesment. It will use prefetched data if fetch fails. It will add assessment form data. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param options Other options. - * @return Resolved with the assessment. - */ - getReviewerAssessmentById(workshopId: number, assessmentId: number, options: AddonModWorkshopUserOptions = {}): Promise { - return this.workshopProvider.getAssessment(workshopId, assessmentId, options).catch((error) => { - return this.workshopProvider.getReviewerAssessments(workshopId, options).then((assessments) => { - for (const x in assessments) { - if (assessments[x].id == assessmentId) { - return assessments[x]; - } - } - - // Not found, return original error. - return Promise.reject(error); - }); - }).then((assessment) => { - return this.workshopProvider.getAssessmentForm(workshopId, assessmentId, options).then((assessmentForm) => { - assessment.form = assessmentForm; - - return assessment; - }); - }); - } - - /** - * Retrieves the assessment of the given user and all the related data. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Promise resolved when the workshop data is retrieved. - */ - getReviewerAssessments(workshopId: number, options: AddonModWorkshopUserOptions = {}): Promise { - options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - - return this.workshopProvider.getReviewerAssessments(workshopId, options).then((assessments) => { - const promises = assessments.map((assessment) => { - return this.getSubmissionById(workshopId, assessment.submissionid, options).then((submission) => { - assessment.submission = submission; - }); - }); - - return Promise.all(promises).then(() => { - return assessments; - }); - }); - } - - /** - * Delete stored attachment files for a submission. - * - * @param workshopId Workshop ID. - * @param submissionId If not editing, it will refer to timecreated. - * @param editing If the submission is being edited or added otherwise. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when deleted. - */ - deleteSubmissionStoredFiles(workshopId: number, submissionId: number, editing: boolean, siteId?: string): Promise { - return this.workshopOffline.getSubmissionFolder(workshopId, submissionId, editing, siteId).then((folderPath) => { - return this.fileProvider.removeDir(folderPath).catch(() => { - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists. - }); - }); - } - - /** - * Given a list of files (either online files or local files), store the local files in a local folder - * to be submitted later. - * - * @param workshopId Workshop ID. - * @param submissionId If not editing, it will refer to timecreated. - * @param editing If the submission is being edited or added otherwise. - * @param files List of files. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - storeSubmissionFiles(workshopId: number, submissionId: number, editing: boolean, files: any[], siteId?: string): Promise { - // Get the folder where to store the files. - return this.workshopOffline.getSubmissionFolder(workshopId, submissionId, editing, siteId).then((folderPath) => { - return this.uploaderProvider.storeFilesToUpload(folderPath, files); - }); - } - - /** - * Upload or store some files for a submission, depending if the user is offline or not. - * - * @param workshopId Workshop ID. - * @param submissionId If not editing, it will refer to timecreated. - * @param files List of files. - * @param editing If the submission is being edited or added otherwise. - * @param offline True if files sould be stored for offline, false to upload them. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success. - */ - uploadOrStoreSubmissionFiles(workshopId: number, submissionId: number, files: any[], editing: boolean, offline: boolean, - siteId?: string): Promise { - if (offline) { - return this.storeSubmissionFiles(workshopId, submissionId, editing, files, siteId); - } else { - return this.uploaderProvider.uploadOrReuploadFiles(files, AddonModWorkshopProvider.COMPONENT, workshopId, siteId); - } - } - - /** - * Get a list of stored attachment files for a submission. See AddonModWorkshopHelperProvider#storeFiles. - * - * @param workshopId Workshop ID. - * @param submissionId If not editing, it will refer to timecreated. - * @param editing If the submission is being edited or added otherwise. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getStoredSubmissionFiles(workshopId: number, submissionId: number, editing: boolean, siteId?: string): Promise { - return this.workshopOffline.getSubmissionFolder(workshopId, submissionId, editing, siteId).then((folderPath) => { - return this.uploaderProvider.getStoredFiles(folderPath).catch(() => { - // Ignore not found files. - return []; - }); - }); - } - - /** - * Get a list of stored attachment files for a submission and online files also. See AddonModWorkshopHelperProvider#storeFiles. - * - * @param filesObject Files object combining offline and online information. - * @param workshopId Workshop ID. - * @param submissionId If not editing, it will refer to timecreated. - * @param editing If the submission is being edited or added otherwise. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getSubmissionFilesFromOfflineFilesObject(filesObject: any, workshopId: number, submissionId: number, editing: boolean, - siteId?: string): Promise { - return this.workshopOffline.getSubmissionFolder(workshopId, submissionId, editing, siteId).then((folderPath) => { - return this.uploaderProvider.getStoredFilesFromOfflineFilesObject(filesObject, folderPath); - }); - } - - /** - * Delete stored attachment files for an assessment. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when deleted. - */ - deleteAssessmentStoredFiles(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.workshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId).then((folderPath) => { - return this.fileProvider.removeDir(folderPath).catch(() => { - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists. - }); - }); - } - - /** - * Given a list of files (either online files or local files), store the local files in a local folder - * to be submitted later. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param files List of files. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success, rejected otherwise. - */ - storeAssessmentFiles(workshopId: number, assessmentId: number, files: any[], siteId?: string): Promise { - // Get the folder where to store the files. - return this.workshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId).then((folderPath) => { - return this.uploaderProvider.storeFilesToUpload(folderPath, files); - }); - } - - /** - * Upload or store some files for an assessment, depending if the user is offline or not. - * - * @param workshopId Workshop ID. - * @param assessmentId ID. - * @param files List of files. - * @param offline True if files sould be stored for offline, false to upload them. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if success. - */ - uploadOrStoreAssessmentFiles(workshopId: number, assessmentId: number, files: any[], offline: boolean, siteId?: string): - Promise { - if (offline) { - return this.storeAssessmentFiles(workshopId, assessmentId, files, siteId); - } else { - return this.uploaderProvider.uploadOrReuploadFiles(files, AddonModWorkshopProvider.COMPONENT, workshopId, siteId); - } - } - - /** - * Get a list of stored attachment files for an assessment. See AddonModWorkshopHelperProvider#storeFiles. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getStoredAssessmentFiles(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.workshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId).then((folderPath) => { - return this.uploaderProvider.getStoredFiles(folderPath).catch(() => { - // Ignore not found files. - return []; - }); - }); - } - - /** - * Get a list of stored attachment files for an assessment and online files also. See AddonModWorkshopHelperProvider#storeFiles. - * - * @param filesObject Files object combining offline and online information. - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the files. - */ - getAssessmentFilesFromOfflineFilesObject(filesObject: any, workshopId: number, assessmentId: number, siteId?: string): - Promise { - return this.workshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId).then((folderPath) => { - return this.uploaderProvider.getStoredFilesFromOfflineFilesObject(filesObject, folderPath); - }); - } - - /** - * Returns the action of a given submission. - * - * @param actions Offline actions to be applied to the given submission. - * @param submissionId ID of the submission to filter by or false. - * @return Promise resolved with the files. - */ - filterSubmissionActions(actions: any[], submissionId: number): any[] { - return actions.filter((action) => { - if (submissionId) { - return action.submissionid == submissionId; - } else { - return action.submissionid < 0; - } - }); - } - - /** - * Applies offline data to submission. - * - * @param submission Submission object to be modified. - * @param actions Offline actions to be applied to the given submission. - * @return Promise resolved with the files. - */ - applyOfflineData(submission: any, actions: any[]): Promise { - if (actions.length && !submission) { - submission = {}; - } - - let editing = true, - attachmentsid = false, - workshopId; - - actions.forEach((action) => { - switch (action.action) { - case 'add': - submission.id = action.submissionid; - editing = false; - case 'update': - submission.title = action.title; - submission.content = action.content; - submission.title = action.title; - submission.courseid = action.courseid; - submission.submissionmodified = parseInt(action.timemodified, 10) / 1000; - submission.offline = true; - attachmentsid = action.attachmentsid; - workshopId = action.workshopid; - break; - case 'delete': - submission.deleted = true; - submission.submissionmodified = parseInt(action.timemodified, 10) / 1000; - break; - default: - } - }); - - // Check offline files for latest attachmentsid. - if (actions.length) { - if (attachmentsid) { - return this.getSubmissionFilesFromOfflineFilesObject(attachmentsid, workshopId, submission.id, editing) - .then((files) => { - submission.attachmentfiles = files; - - return submission; - }); - } else { - submission.attachmentfiles = []; - } - } - - return Promise.resolve(submission); - } - - /** - * Prepare assessment data to be sent to the server. - * - * @param workshop Workshop object. - * @param selectedValues Assessment current values - * @param feedbackText Feedback text. - * @param feedbackFiles Feedback attachments. - * @param form Assessment form original data. - * @param attachmentsId The draft file area id for attachments. - * @return Promise resolved with the data to be sent. Or rejected with the input errors object. - */ - prepareAssessmentData(workshop: any, selectedValues: any[], feedbackText: string, feedbackFiles: any[], form: any, - attachmentsId: number): Promise { - if (workshop.overallfeedbackmode == 2 && !feedbackText) { - return Promise.reject({feedbackauthor: this.translate.instant('core.err_required')}); - } - - return this.strategyDelegate.prepareAssessmentData(workshop.strategy, selectedValues, form).then((data) => { - data.feedbackauthor = feedbackText; - data.feedbackauthorattachmentsid = attachmentsId || 0; - data.nodims = form.dimenssionscount; - - return data; - }); - } - - /** - * Calculates the real value of a grade based on real_grade_value. - * - * @param value Percentual value from 0 to 100. - * @param max The maximal grade. - * @param decimals Decimals to show in the formatted grade. - * @return Real grade formatted. - */ - protected realGradeValueHelper(value: number, max: number, decimals: number): string { - if (value == null) { - return null; - } else if (max == 0) { - return '0'; - } else { - value = this.textUtils.roundToDecimals(max * value / 100, decimals); - - return this.utils.formatFloat(value); - } - } - - /** - * Calculates the real value of a grades of an assessment. - * - * @param workshop Workshop object. - * @param assessment Assessment data. - * @return Assessment with real grades. - */ - realGradeValue(workshop: any, assessment: any): any { - assessment.grade = this.realGradeValueHelper(assessment.grade, workshop.grade, workshop.gradedecimals); - assessment.gradinggrade = this.realGradeValueHelper(assessment.gradinggrade, workshop.gradinggrade, workshop.gradedecimals); - assessment.gradinggradeover = this.realGradeValueHelper(assessment.gradinggradeover, workshop.gradinggrade, - workshop.gradedecimals); - - return assessment; - } - - /** - * Check grade should be shown - * - * @param grade Grade to be shown - * @return If grade should be shown or not. - */ - showGrade(grade: any): boolean { - return typeof grade !== 'undefined' && grade !== null; - } -} diff --git a/src/addon/mod/workshop/providers/link-handler.ts b/src/addon/mod/workshop/providers/link-handler.ts deleted file mode 100644 index a934b63bc..000000000 --- a/src/addon/mod/workshop/providers/link-handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { AddonModWorkshopProvider } from './workshop'; - -/** - * Handler to treat links to workshop. - */ -@Injectable() -export class AddonModWorkshopLinkHandler extends CoreContentLinksModuleIndexHandler { - name = 'AddonModWorkshopLinkHandler'; - - constructor(courseHelper: CoreCourseHelperProvider, - protected workshopProvider: AddonModWorkshopProvider) { - super(courseHelper, AddonModWorkshopProvider.COMPONENT, 'workshop', 'w'); - } - - /** - * Check if the handler is enabled for a certain site (site + user) and a URL. - * - * @param siteId The site ID. - * @param url The URL to treat. - * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} - * @param courseId Course ID related to the URL. Optional but recommended. - * @return Whether the handler is enabled for the URL and site. - */ - isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { - return this.workshopProvider.isPluginEnabled(siteId); - } -} diff --git a/src/addon/mod/workshop/providers/list-link-handler.ts b/src/addon/mod/workshop/providers/list-link-handler.ts deleted file mode 100644 index d4c04d863..000000000 --- a/src/addon/mod/workshop/providers/list-link-handler.ts +++ /dev/null @@ -1,41 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreContentLinksModuleListHandler } from '@core/contentlinks/classes/module-list-handler'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { TranslateService } from '@ngx-translate/core'; -import { AddonModWorkshopProvider } from './workshop'; - -/** - * Handler to treat links to workshop list page. - */ -@Injectable() -export class AddonModWorkshopListLinkHandler extends CoreContentLinksModuleListHandler { - name = 'AddonModWorkshopListLinkHandler'; - - constructor(linkHelper: CoreContentLinksHelperProvider, translate: TranslateService, - protected workshopProvider: AddonModWorkshopProvider) { - super(linkHelper, translate, 'AddonModWorkshop', 'workshop'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return this.workshopProvider.isPluginEnabled(); - } -} diff --git a/src/addon/mod/workshop/providers/module-handler.ts b/src/addon/mod/workshop/providers/module-handler.ts deleted file mode 100644 index 5764a1d80..000000000 --- a/src/addon/mod/workshop/providers/module-handler.ts +++ /dev/null @@ -1,88 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; -import { AddonModWorkshopIndexComponent } from '../components/index/index'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { AddonModWorkshopProvider } from './workshop'; -import { CoreConstants } from '@core/constants'; - -/** - * Handler to support workshop modules. - */ -@Injectable() -export class AddonModWorkshopModuleHandler implements CoreCourseModuleHandler { - name = 'AddonModWorkshop'; - modName = 'workshop'; - - supportedFeatures = { - [CoreConstants.FEATURE_GROUPS]: true, - [CoreConstants.FEATURE_GROUPINGS]: true, - [CoreConstants.FEATURE_MOD_INTRO]: true, - [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, - [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true, - [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, - [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, - [CoreConstants.FEATURE_PLAGIARISM]: true - }; - - constructor(private courseProvider: CoreCourseProvider, private workshopProvider: AddonModWorkshopProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return this.workshopProvider.isPluginEnabled(); - } - - /** - * Get the data required to display the module in the course contents view. - * - * @param module The module object. - * @param courseId The course ID. - * @param sectionId The section ID. - * @return Data to render the module. - */ - getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - return { - icon: this.courseProvider.getModuleIconSrc(this.modName, module.modicon), - title: module.name, - class: 'addon-mod_workshop-handler', - showDownloadButton: true, - action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions, params?: any): void { - const pageParams = {module: module, courseId: courseId}; - if (params) { - Object.assign(pageParams, params); - } - navCtrl.push('AddonModWorkshopIndexPage', pageParams, options); - } - }; - } - - /** - * Get the component to render the module. This is needed to support singleactivity course format. - * The component returned must implement CoreCourseModuleMainComponent. - * - * @param course The course object. - * @param module The module object. - * @return The component to use, undefined if not found. - */ - getMainComponent(course: any, module: any): any { - return AddonModWorkshopIndexComponent; - } -} diff --git a/src/addon/mod/workshop/providers/offline.ts b/src/addon/mod/workshop/providers/offline.ts deleted file mode 100644 index b69b8dddc..000000000 --- a/src/addon/mod/workshop/providers/offline.ts +++ /dev/null @@ -1,796 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreFileProvider } from '@providers/file'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; - -/** - * Service to handle offline workshop. - */ -@Injectable() -export class AddonModWorkshopOfflineProvider { - - // Variables for database. - static SUBMISSIONS_TABLE = 'addon_mod_workshop_submissions'; - static ASSESSMENTS_TABLE = 'addon_mod_workshop_assessments'; - static EVALUATE_SUBMISSIONS_TABLE = 'addon_mod_workshop_evaluate_submissions'; - static EVALUATE_ASSESSMENTS_TABLE = 'addon_mod_workshop_evaluate_assessments'; - - protected siteSchema: CoreSiteSchema = { - name: 'AddonModWorkshopOfflineProvider', - version: 1, - tables: [ - { - name: AddonModWorkshopOfflineProvider.SUBMISSIONS_TABLE, - columns: [ - { - name: 'workshopid', - type: 'INTEGER', - }, - { - name: 'submissionid', - type: 'INTEGER', - }, - { - name: 'action', - type: 'TEXT', - }, - { - name: 'courseid', - type: 'INTEGER', - }, - { - name: 'title', - type: 'TEXT', - }, - { - name: 'content', - type: 'TEXT', - }, - { - name: 'attachmentsid', - type: 'TEXT', - }, - { - name: 'timemodified', - type: 'INTEGER', - } - ], - primaryKeys: ['workshopid', 'submissionid', 'action'] - }, - { - name: AddonModWorkshopOfflineProvider.ASSESSMENTS_TABLE, - columns: [ - { - name: 'workshopid', - type: 'INTEGER', - }, - { - name: 'assessmentid', - type: 'INTEGER', - }, - { - name: 'courseid', - type: 'INTEGER', - }, - { - name: 'inputdata', - type: 'TEXT', - }, - { - name: 'timemodified', - type: 'INTEGER', - }, - ], - primaryKeys: ['workshopid', 'assessmentid'] - }, - { - name: AddonModWorkshopOfflineProvider.EVALUATE_SUBMISSIONS_TABLE, - columns: [ - { - name: 'workshopid', - type: 'INTEGER', - }, - { - name: 'submissionid', - type: 'INTEGER', - }, - { - name: 'courseid', - type: 'INTEGER', - }, - { - name: 'timemodified', - type: 'INTEGER', - }, - { - name: 'feedbacktext', - type: 'TEXT', - }, - { - name: 'published', - type: 'INTEGER', - }, - { - name: 'gradeover', - type: 'TEXT', - }, - ], - primaryKeys: ['workshopid', 'submissionid'] - }, - { - name: AddonModWorkshopOfflineProvider.EVALUATE_ASSESSMENTS_TABLE, - columns: [ - { - name: 'workshopid', - type: 'INTEGER', - }, - { - name: 'assessmentid', - type: 'INTEGER', - }, - { - name: 'courseid', - type: 'INTEGER', - }, - { - name: 'timemodified', - type: 'INTEGER', - }, - { - name: 'feedbacktext', - type: 'TEXT', - }, - { - name: 'weight', - type: 'INTEGER', - }, - { - name: 'gradinggradeover', - type: 'TEXT', - }, - ], - primaryKeys: ['workshopid', 'assessmentid'] - } - ] - }; - - constructor(private fileProvider: CoreFileProvider, - private sitesProvider: CoreSitesProvider, - private textUtils: CoreTextUtilsProvider, - private timeUtils: CoreTimeUtilsProvider) { - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Get all the workshops ids that have something to be synced. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with workshops id that have something to be synced. - */ - getAllWorkshops(siteId?: string): Promise { - const promises = [ - this.getAllSubmissions(siteId), - this.getAllAssessments(siteId), - this.getAllEvaluateSubmissions(siteId), - this.getAllEvaluateAssessments(siteId) - ]; - - return Promise.all(promises).then((promiseResults) => { - const workshopIds = {}; - - // Get workshops from any offline object all should have workshopid. - promiseResults.forEach((offlineObjects) => { - offlineObjects.forEach((offlineObject) => { - workshopIds[offlineObject.workshopid] = true; - }); - }); - - return Object.keys(workshopIds).map((workshopId) => parseInt(workshopId, 10)); - }); - } - - /** - * Check if there is an offline data to be synced. - * - * @param workshopId Workshop ID to remove. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline data, false otherwise. - */ - hasWorkshopOfflineData(workshopId: number, siteId?: string): Promise { - const promises = [ - this.getSubmissions(workshopId, siteId), - this.getAssessments(workshopId, siteId), - this.getEvaluateSubmissions(workshopId, siteId), - this.getEvaluateAssessments(workshopId, siteId) - ]; - - return Promise.all(promises).then((results) => { - return results.some((result) => result && result.length); - }).catch(() => { - // No offline data found. - return false; - }); - } - - /** - * Delete workshop submission action. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param action Action to be done. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteSubmissionAction(workshopId: number, submissionId: number, action: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - submissionid: submissionId, - action: action - }; - - return site.getDb().deleteRecords(AddonModWorkshopOfflineProvider.SUBMISSIONS_TABLE, conditions); - }); - } - - /** - * Delete all workshop submission actions. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteAllSubmissionActions(workshopId: number, submissionId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - submissionid: submissionId, - }; - - return site.getDb().deleteRecords(AddonModWorkshopOfflineProvider.SUBMISSIONS_TABLE, conditions); - }); - } - - /** - * Get the all the submissions to be synced. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the objects to be synced. - */ - getAllSubmissions(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.SUBMISSIONS_TABLE).then((records) => { - records.forEach(this.parseSubmissionRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get the submissions of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getSubmissions(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId - }; - - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.SUBMISSIONS_TABLE, conditions).then((records) => { - records.forEach(this.parseSubmissionRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get all actions of a submission of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param submissionId ID of the submission. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getSubmissionActions(workshopId: number, submissionId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - submissionid: submissionId - }; - - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.SUBMISSIONS_TABLE, conditions).then((records) => { - records.forEach(this.parseSubmissionRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get an specific action of a submission of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param submissionId ID of the submission. - * @param action Action to be done. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getSubmissionAction(workshopId: number, submissionId: number, action: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - submissionid: submissionId, - action: action - }; - - return site.getDb().getRecord(AddonModWorkshopOfflineProvider.SUBMISSIONS_TABLE, conditions).then((record) => { - this.parseSubmissionRecord(record); - - return record; - }); - }); - } - - /** - * Offline version for adding a submission action to a workshop. - * - * @param workshopId Workshop ID. - * @param courseId Course ID the workshop belongs to. - * @param title The submission title. - * @param content The submission text content. - * @param attachmentsId Stored attachments. - * @param submissionId Submission Id, if action is add, the time the submission was created. - * If set to 0, current time is used. - * @param action Action to be done. ['add', 'update', 'delete'] - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when submission action is successfully saved. - */ - saveSubmission(workshopId: number, courseId: number, title: string, content: string, attachmentsId: any, - submissionId: number, action: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const timemodified = this.timeUtils.timestamp(); - const assessment = { - workshopid: workshopId, - courseid: courseId, - title: title, - content: content, - attachmentsid: JSON.stringify(attachmentsId), - action: action, - submissionid: submissionId ? submissionId : -timemodified, - timemodified: timemodified - }; - - return site.getDb().insertRecord(AddonModWorkshopOfflineProvider.SUBMISSIONS_TABLE, assessment); - }); - } - - /** - * Parse "attachments" column of a submission record. - * - * @param record Submission record, modified in place. - */ - protected parseSubmissionRecord(record: any): void { - record.attachmentsid = this.textUtils.parseJSON(record.attachmentsid); - } - - /** - * Delete workshop assessment. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteAssessment(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - assessmentid: assessmentId - }; - - return site.getDb().deleteRecords(AddonModWorkshopOfflineProvider.ASSESSMENTS_TABLE, conditions); - }); - } - - /** - * Get the all the assessments to be synced. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the objects to be synced. - */ - getAllAssessments(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.ASSESSMENTS_TABLE).then((records) => { - records.forEach(this.parseAssessmentRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get the assessments of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getAssessments(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId - }; - - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.ASSESSMENTS_TABLE, conditions).then((records) => { - records.forEach(this.parseAssessmentRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get an specific assessment of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getAssessment(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - assessmentid: assessmentId - }; - - return site.getDb().getRecord(AddonModWorkshopOfflineProvider.ASSESSMENTS_TABLE, conditions).then((record) => { - this.parseAssessmentRecord(record); - - return record; - }); - }); - } - - /** - * Offline version for adding an assessment to a workshop. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param courseId Course ID the workshop belongs to. - * @param inputData Assessment data. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when assessment is successfully saved. - */ - saveAssessment(workshopId: number, assessmentId: number, courseId: number, inputData: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const assessment = { - workshopid: workshopId, - courseid: courseId, - inputdata: JSON.stringify(inputData), - assessmentid: assessmentId, - timemodified: this.timeUtils.timestamp() - }; - - return site.getDb().insertRecord(AddonModWorkshopOfflineProvider.ASSESSMENTS_TABLE, assessment); - }); - } - - /** - * Parse "inpudata" column of an assessment record. - * - * @param record Assessnent record, modified in place. - */ - protected parseAssessmentRecord(record: any): void { - record.inputdata = this.textUtils.parseJSON(record.inputdata); - } - - /** - * Delete workshop evaluate submission. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteEvaluateSubmission(workshopId: number, submissionId: number, siteId?: string): Promise { - const conditions = { - workshopid: workshopId, - submissionid: submissionId - }; - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonModWorkshopOfflineProvider.EVALUATE_SUBMISSIONS_TABLE, conditions); - }); - } - - /** - * Get the all the evaluate submissions to be synced. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the objects to be synced. - */ - getAllEvaluateSubmissions(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.EVALUATE_SUBMISSIONS_TABLE).then((records) => { - records.forEach(this.parseEvaluateSubmissionRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get the evaluate submissions of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getEvaluateSubmissions(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId - }; - - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.EVALUATE_SUBMISSIONS_TABLE, conditions) - .then((records) => { - records.forEach(this.parseEvaluateSubmissionRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get an specific evaluate submission of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param submissionId Submission ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getEvaluateSubmission(workshopId: number, submissionId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - submissionid: submissionId - }; - - return site.getDb().getRecord(AddonModWorkshopOfflineProvider.EVALUATE_SUBMISSIONS_TABLE, conditions).then((record) => { - this.parseEvaluateSubmissionRecord(record); - - return record; - }); - }); - } - - /** - * Offline version for evaluation a submission to a workshop. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param courseId Course ID the workshop belongs to. - * @param feedbackText The feedback for the author. - * @param published Whether to publish the submission for other users. - * @param gradeOver The new submission grade (empty for no overriding the grade). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when submission evaluation is successfully saved. - */ - saveEvaluateSubmission(workshopId: number, submissionId: number, courseId: number, feedbackText: string, published: boolean, - gradeOver: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const submission = { - workshopid: workshopId, - courseid: courseId, - submissionid: submissionId, - timemodified: this.timeUtils.timestamp(), - feedbacktext: feedbackText, - published: Number(published), - gradeover: JSON.stringify(gradeOver) - }; - - return site.getDb().insertRecord(AddonModWorkshopOfflineProvider.EVALUATE_SUBMISSIONS_TABLE, submission); - }); - } - - /** - * Parse "published" and "gradeover" columns of an evaluate submission record. - * - * @param record Evaluate submission record, modified in place. - */ - protected parseEvaluateSubmissionRecord(record: any): void { - record.published = Boolean(record.published); - record.gradeover = this.textUtils.parseJSON(record.gradeover); - } - - /** - * Delete workshop evaluate assessment. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteEvaluateAssessment(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - assessmentid: assessmentId - }; - - return site.getDb().deleteRecords(AddonModWorkshopOfflineProvider.EVALUATE_ASSESSMENTS_TABLE, conditions); - }); - } - - /** - * Get the all the evaluate assessments to be synced. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the objects to be synced. - */ - getAllEvaluateAssessments(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.EVALUATE_ASSESSMENTS_TABLE).then((records) => { - records.forEach(this.parseEvaluateAssessmentRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get the evaluate assessments of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getEvaluateAssessments(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId - }; - - return site.getDb().getRecords(AddonModWorkshopOfflineProvider.EVALUATE_ASSESSMENTS_TABLE, conditions) - .then((records) => { - records.forEach(this.parseEvaluateAssessmentRecord.bind(this)); - - return records; - }); - }); - } - - /** - * Get an specific evaluate assessment of a workshop to be synced. - * - * @param workshopId ID of the workshop. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the object to be synced. - */ - getEvaluateAssessment(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const conditions = { - workshopid: workshopId, - assessmentid: assessmentId - }; - - return site.getDb().getRecord(AddonModWorkshopOfflineProvider.EVALUATE_ASSESSMENTS_TABLE, conditions).then((record) => { - this.parseEvaluateAssessmentRecord(record); - - return record; - }); - }); - } - - /** - * Offline version for evaluating an assessment to a workshop. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param courseId Course ID the workshop belongs to. - * @param feedbackText The feedback for the reviewer. - * @param weight The new weight for the assessment. - * @param gradingGradeOver The new grading grade (empty for no overriding the grade). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when assessment evaluation is successfully saved. - */ - saveEvaluateAssessment(workshopId: number, assessmentId: number, courseId: number, feedbackText: string, weight: number, - gradingGradeOver: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const assessment = { - workshopid: workshopId, - courseid: courseId, - assessmentid: assessmentId, - timemodified: this.timeUtils.timestamp(), - feedbacktext: feedbackText, - weight: weight, - gradinggradeover: JSON.stringify(gradingGradeOver) - }; - - return site.getDb().insertRecord(AddonModWorkshopOfflineProvider.EVALUATE_ASSESSMENTS_TABLE, assessment); - }); - } - - /** - * Parse "gradinggradeover" column of an evaluate assessment record. - * - * @param record Evaluate assessment record, modified in place. - */ - protected parseEvaluateAssessmentRecord(record: any): void { - record.gradinggradeover = this.textUtils.parseJSON(record.gradinggradeover); - } - - /** - * Get the path to the folder where to store files for offline attachments in a workshop. - * - * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getWorkshopFolder(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - - const siteFolderPath = this.fileProvider.getSiteFolder(site.getId()); - const workshopFolderPath = 'offlineworkshop/' + workshopId + '/'; - - return this.textUtils.concatenatePaths(siteFolderPath, workshopFolderPath); - }); - } - - /** - * Get the path to the folder where to store files for offline submissions. - * - * @param workshopId Workshop ID. - * @param submissionId If not editing, it will refer to timecreated. - * @param editing If the submission is being edited or added otherwise. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getSubmissionFolder(workshopId: number, submissionId: number, editing: boolean, siteId?: string): Promise { - return this.getWorkshopFolder(workshopId, siteId).then((folderPath) => { - folderPath += 'submission/'; - const folder = editing ? 'update_' + submissionId : 'add'; - - return this.textUtils.concatenatePaths(folderPath, folder); - }); - } - - /** - * Get the path to the folder where to store files for offline assessment. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the path. - */ - getAssessmentFolder(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.getWorkshopFolder(workshopId, siteId).then((folderPath) => { - folderPath += 'assessment/'; - - return this.textUtils.concatenatePaths(folderPath, String(assessmentId)); - }); - } -} diff --git a/src/addon/mod/workshop/providers/prefetch-handler.ts b/src/addon/mod/workshop/providers/prefetch-handler.ts deleted file mode 100644 index 9f69bb7d0..000000000 --- a/src/addon/mod/workshop/providers/prefetch-handler.ts +++ /dev/null @@ -1,418 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonModWorkshopProvider } from './workshop'; -import { AddonModWorkshopSyncProvider } from './sync'; -import { AddonModWorkshopHelperProvider } from './helper'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Handler to prefetch workshops. - */ -@Injectable() -export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModWorkshop'; - modName = 'workshop'; - component = AddonModWorkshopProvider.COMPONENT; - updatesNames = new RegExp('^configuration$|^.*files$|^completion|^gradeitems$|^outcomes$|^submissions$|^assessments$' + - '|^assessmentgrades$|^usersubmissions$|^userassessments$|^userassessmentgrades$|^userassessmentgrades$'); - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - utils: CoreUtilsProvider, - courseProvider: CoreCourseProvider, - filepoolProvider: CoreFilepoolProvider, - sitesProvider: CoreSitesProvider, - domUtils: CoreDomUtilsProvider, - filterHelper: CoreFilterHelperProvider, - pluginFileDelegate: CorePluginFileDelegate, - private groupsProvider: CoreGroupsProvider, - private userProvider: CoreUserProvider, - private workshopProvider: AddonModWorkshopProvider, - private workshopHelper: AddonModWorkshopHelperProvider, - private syncProvider: AddonModWorkshopSyncProvider) { - - super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, - pluginFileDelegate); - } - - /** - * Get list of files. If not defined, we'll assume they're in module.contents. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @return Promise resolved with the list of files. - */ - getFiles(module: any, courseId: number, single?: boolean): Promise { - return this.getWorkshopInfoHelper(module, courseId, {omitFail: true}).then((info) => { - return info.files; - }); - } - - /** - * Helper function to get all workshop info just once. - * - * @param module Module to get the files. - * @param courseId Course ID the module belongs to. - * @param options Other options. - * @return Promise resolved with the info fetched. - */ - protected getWorkshopInfoHelper(module: any, courseId: number, options: AddonModWorkshopGetInfoOptions = {}): Promise { - let workshop; - let groups = []; - let files = []; - let access; - const modOptions = { - cmId: module.id, - ...options, // Include all options. - }; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const userId = site.getUserId(); - - return this.workshopProvider.getWorkshop(courseId, module.id, options).then((data) => { - files = this.getIntroFilesFromInstance(module, data); - files = files.concat(data.instructauthorsfiles).concat(data.instructreviewersfiles); - workshop = data; - - return this.workshopProvider.getWorkshopAccessInformation(workshop.id, modOptions).then((accessData) => { - access = accessData; - if (access.canviewallsubmissions) { - return this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, options.siteId) - .then((groupInfo) => { - if (!groupInfo.groups || groupInfo.groups.length == 0) { - groupInfo.groups = [{id: 0}]; - } - groups = groupInfo.groups; - }); - } - }); - }).then(() => { - return this.workshopProvider.getUserPlanPhases(workshop.id, modOptions).then((phases) => { - // Get submission phase info. - const submissionPhase = phases[AddonModWorkshopProvider.PHASE_SUBMISSION], - canSubmit = this.workshopHelper.canSubmit(workshop, access, submissionPhase.tasks), - canAssess = this.workshopHelper.canAssess(workshop, access), - promises = []; - - if (canSubmit) { - promises.push(this.workshopHelper.getUserSubmission(workshop.id, { - userId, - cmId: module.id, - }).then((submission) => { - if (submission) { - files = files.concat(submission.contentfiles).concat(submission.attachmentfiles); - } - })); - } - - if (access.canviewallsubmissions && workshop.phase >= AddonModWorkshopProvider.PHASE_SUBMISSION) { - promises.push(this.workshopProvider.getSubmissions(workshop.id, modOptions).then((submissions) => { - const promises2 = []; - submissions.forEach((submission) => { - files = files.concat(submission.contentfiles).concat(submission.attachmentfiles); - promises2.push(this.workshopProvider.getSubmissionAssessments(workshop.id, submission.id, { - cmId: module.id, - }).then((assessments) => { - assessments.forEach((assessment) => { - files = files.concat(assessment.feedbackattachmentfiles) - .concat(assessment.feedbackcontentfiles); - }); - })); - }); - - return Promise.all(promises2); - })); - } - - // Get assessment files. - if (workshop.phase >= AddonModWorkshopProvider.PHASE_ASSESSMENT && canAssess) { - promises.push(this.workshopHelper.getReviewerAssessments(workshop.id, modOptions).then((assessments) => { - assessments.forEach((assessment) => { - files = files.concat(assessment.feedbackattachmentfiles).concat(assessment.feedbackcontentfiles); - }); - })); - } - - return Promise.all(promises); - }); - }); - }).then(() => { - return { - workshop: workshop, - groups: groups, - files: files.filter((file) => typeof file !== 'undefined') - }; - }).catch((message): any => { - if (options.omitFail) { - // Any error, return the info we have. - return { - workshop: workshop, - groups: groups, - files: files.filter((file) => typeof file !== 'undefined') - }; - } - - return Promise.reject(message); - }); - } - - /** - * Invalidate the prefetched content. - * - * @param moduleId The module ID. - * @param courseId The course ID the module belongs to. - * @return Promise resolved when the data is invalidated. - */ - invalidateContent(moduleId: number, courseId: number): Promise { - return this.workshopProvider.invalidateContent(moduleId, courseId); - } - - /** - * Check if a module can be downloaded. If the function is not defined, we assume that all modules are downloadable. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @return Whether the module can be downloaded. The promise should never be rejected. - */ - isDownloadable(module: any, courseId: number): boolean | Promise { - return this.workshopProvider.getWorkshop(courseId, module.id, { - readingStrategy: CoreSitesReadingStrategy.PreferCache, - }).then((workshop) => { - return this.workshopProvider.getWorkshopAccessInformation(workshop.id, {cmId: module.id}).then((accessData) => { - // Check if workshop is setup by phase. - return accessData.canswitchphase || workshop.phase > AddonModWorkshopProvider.PHASE_SETUP; - }); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. - */ - isEnabled(): boolean | Promise { - return this.workshopProvider.isPluginEnabled(); - } - - /** - * Prefetch a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param dirPath Path of the directory where to store all the content files. - * @return Promise resolved when done. - */ - prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, single, this.prefetchWorkshop.bind(this)); - } - - /** - * Retrieves all the grades reports for all the groups and then returns only unique grades. - * - * @param workshopId Workshop ID. - * @param groups Array of groups in the activity. - * @param cmId Module ID. - * @param siteId Site ID. If not defined, current site. - * @return All unique entries. - */ - protected getAllGradesReport(workshopId: number, groups: any[], cmId: number, siteId: string): Promise { - const promises = []; - - groups.forEach((group) => { - promises.push(this.workshopProvider.fetchAllGradeReports(workshopId, {groupId: group.id, cmId, siteId})); - }); - - return Promise.all(promises).then((grades) => { - const uniqueGrades = {}; - - grades.forEach((groupGrades) => { - groupGrades.forEach((grade) => { - if (grade.submissionid) { - uniqueGrades[grade.submissionid] = grade; - } - }); - }); - - return this.utils.objectToArray(uniqueGrades); - }); - } - - /** - * Prefetch a workshop. - * - * @param module The module object returned by WS. - * @param courseId Course ID the module belongs to. - * @param single True if we're downloading a single module, false if we're downloading a whole section. - * @param siteId Site ID. - * @return Promise resolved when done. - */ - protected prefetchWorkshop(module: any, courseId: number, single: boolean, siteId: string): Promise { - - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const userIds = []; - const commonOptions = { - readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, - siteId, - }; - const modOptions = { - cmId: module.id, - ...commonOptions, // Include all common options. - }; - - return this.sitesProvider.getSite(siteId).then((site) => { - const currentUserId = site.getUserId(); - - // Prefetch the workshop data. - return this.getWorkshopInfoHelper(module, courseId, commonOptions).then((info) => { - const workshop = info.workshop, - promises = [], - assessments = []; - - promises.push(this.filepoolProvider.addFilesToQueue(siteId, info.files, this.component, module.id)); - promises.push(this.workshopProvider.getWorkshopAccessInformation(workshop.id, modOptions).then((access) => { - return this.workshopProvider.getUserPlanPhases(workshop.id, modOptions).then((phases) => { - - // Get submission phase info. - const submissionPhase = phases[AddonModWorkshopProvider.PHASE_SUBMISSION], - canSubmit = this.workshopHelper.canSubmit(workshop, access, submissionPhase.tasks), - canAssess = this.workshopHelper.canAssess(workshop, access), - promises2 = []; - - if (canSubmit) { - promises2.push(this.workshopProvider.getSubmissions(workshop.id, modOptions)); - // Add userId to the profiles to prefetch. - userIds.push(currentUserId); - } - - let reportPromise = Promise.resolve(); - if (access.canviewallsubmissions && workshop.phase >= AddonModWorkshopProvider.PHASE_SUBMISSION) { - reportPromise = this.getAllGradesReport(workshop.id, info.groups, module.id, siteId) - .then((grades) => { - grades.forEach((grade) => { - userIds.push(grade.userid); - userIds.push(grade.gradeoverby); - - grade.reviewedby && grade.reviewedby.forEach((assessment) => { - userIds.push(assessment.userid); - userIds.push(assessment.gradinggradeoverby); - assessments[assessment.assessmentid] = assessment; - }); - - grade.reviewerof && grade.reviewerof.forEach((assessment) => { - userIds.push(assessment.userid); - userIds.push(assessment.gradinggradeoverby); - assessments[assessment.assessmentid] = assessment; - }); - }); - }); - } - - if (workshop.phase >= AddonModWorkshopProvider.PHASE_ASSESSMENT && canAssess) { - // Wait the report promise to finish to override assessments array if needed. - reportPromise = reportPromise.finally(() => { - return this.workshopHelper.getReviewerAssessments(workshop.id, { - userId: currentUserId, - cmId: module.id, - siteId, - }).then((revAssessments) => { - - const promises = []; - let files = []; // Files in each submission. - - revAssessments.forEach((assessment) => { - if (assessment.submission.authorid == currentUserId) { - promises.push(this.workshopProvider.getAssessment(workshop.id, assessment.id, - modOptions)); - } - userIds.push(assessment.reviewerid); - userIds.push(assessment.gradinggradeoverby); - assessments[assessment.id] = assessment; - - files = files.concat(assessment.submission.attachmentfiles || []) - .concat(assessment.submission.contentfiles || []); - }); - - promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id)); - - return Promise.all(promises); - }); - }); - } - - reportPromise = reportPromise.finally(() => { - if (assessments.length > 0) { - return Promise.all(assessments.map((assessment, id) => { - return this.workshopProvider.getAssessmentForm(workshop.id, id, modOptions); - })); - } - }); - promises2.push(reportPromise); - - if (workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { - promises2.push(this.workshopProvider.getGrades(workshop.id, modOptions)); - if (access.canviewpublishedsubmissions) { - promises2.push(this.workshopProvider.getSubmissions(workshop.id, modOptions)); - } - } - - return Promise.all(promises2); - }); - })); - // Add Basic Info to manage links. - promises.push(this.courseProvider.getModuleBasicInfoByInstance(workshop.id, 'workshop', siteId)); - promises.push(this.courseProvider.getModuleBasicGradeInfo(module.id, siteId)); - - return Promise.all(promises); - }); - }).then(() => { - // Prefetch user profiles. - return this.userProvider.prefetchProfiles(userIds, courseId, siteId); - }); - } - - /** - * Sync a module. - * - * @param module Module. - * @param courseId Course ID the module belongs to - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - sync(module: any, courseId: number, siteId?: any): Promise { - return this.syncProvider.syncWorkshop(module.instance, siteId); - } -} - -/** - * Options to pass to getWorkshopInfoHelper. - */ -export type AddonModWorkshopGetInfoOptions = CoreSitesCommonWSOptions & { - omitFail?: boolean; // True to always return even if fails. -}; diff --git a/src/addon/mod/workshop/providers/sync-cron-handler.ts b/src/addon/mod/workshop/providers/sync-cron-handler.ts deleted file mode 100644 index c295b8138..000000000 --- a/src/addon/mod/workshop/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonModWorkshopSyncProvider } from './sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonModWorkshopSyncCronHandler implements CoreCronHandler { - name = 'AddonModWorkshopSyncCronHandler'; - - constructor(private workshopSync: AddonModWorkshopSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.workshopSync.syncAllWorkshops(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.workshopSync.syncInterval; - } -} diff --git a/src/addon/mod/workshop/providers/sync.ts b/src/addon/mod/workshop/providers/sync.ts deleted file mode 100644 index 905f21675..000000000 --- a/src/addon/mod/workshop/providers/sync.ts +++ /dev/null @@ -1,600 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSyncBaseProvider } from '@classes/base-sync'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreAppProvider } from '@providers/app'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModWorkshopProvider } from './workshop'; -import { AddonModWorkshopHelperProvider } from './helper'; -import { AddonModWorkshopOfflineProvider } from './offline'; - -/** - * Service to sync workshops. - */ -@Injectable() -export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_mod_workshop_autom_synced'; - static MANUAL_SYNCED = 'addon_mod_workshop_manual_synced'; - - protected componentTranslate: string; - - constructor(translate: TranslateService, - appProvider: CoreAppProvider, - courseProvider: CoreCourseProvider, - private eventsProvider: CoreEventsProvider, - loggerProvider: CoreLoggerProvider, - sitesProvider: CoreSitesProvider, - syncProvider: CoreSyncProvider, - textUtils: CoreTextUtilsProvider, - timeUtils: CoreTimeUtilsProvider, - private utils: CoreUtilsProvider, - private workshopProvider: AddonModWorkshopProvider, - private workshopHelper: AddonModWorkshopHelperProvider, - private workshopOffline: AddonModWorkshopOfflineProvider, - private logHelper: CoreCourseLogHelperProvider) { - - super('AddonModWorkshopSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, - timeUtils); - - this.componentTranslate = courseProvider.translateModuleName('workshop'); - } - - /** - * Check if an workshop has data to synchronize. - * - * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has data to sync, false otherwise. - */ - hasDataToSync(workshopId: number, siteId?: string): Promise { - return this.workshopOffline.hasWorkshopOfflineData(workshopId, siteId); - } - - /** - * Try to synchronize all workshops that need it and haven't been synchronized in a while. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved when the sync is done. - */ - syncAllWorkshops(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all workshops', this.syncAllWorkshopsFunc.bind(this), [force], siteId); - } - - /** - * Sync all workshops on a site. - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - protected syncAllWorkshopsFunc(siteId: string, force?: boolean): Promise { - return this.workshopOffline.getAllWorkshops(siteId).then((workshopIds) => { - // Sync all workshops that haven't been synced for a while. - const promises = workshopIds.map((workshopId) => { - const promise = force ? this.syncWorkshop(workshopId, siteId) : this.syncWorkshopIfNeeded(workshopId, siteId); - - return promise.then((data) => { - if (data && data.updated) { - // Sync done. Send event. - this.eventsProvider.trigger(AddonModWorkshopSyncProvider.AUTO_SYNCED, { - workshopId: workshopId, - warnings: data.warnings - }, siteId); - } - }); - }); - - return Promise.all(promises); - }); - } - - /** - * Sync a workshop only if a certain time has passed since the last time. - * - * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the workshop is synced or if it doesn't need to be synced. - */ - syncWorkshopIfNeeded(workshopId: number, siteId?: string): Promise { - return this.isSyncNeeded(workshopId, siteId).then((needed) => { - if (needed) { - return this.syncWorkshop(workshopId, siteId); - } - }); - } - - /** - * Try to synchronize a workshop. - * - * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncWorkshop(workshopId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (this.isSyncing(workshopId, siteId)) { - // There's already a sync ongoing for this discussion, return the promise. - return this.getOngoingSync(workshopId, siteId); - } - - // Verify that workshop isn't blocked. - if (this.syncProvider.isBlocked(AddonModWorkshopProvider.COMPONENT, workshopId, siteId)) { - this.logger.debug('Cannot sync workshop ' + workshopId + ' because it is blocked.'); - - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); - } - - this.logger.debug('Try to sync workshop ' + workshopId); - - const syncPromises = []; - - // Get offline submissions to be sent. - syncPromises.push(this.workshopOffline.getSubmissions(workshopId, siteId).catch(() => { - // No offline data found, return empty array. - return []; - })); - - // Get offline submission assessments to be sent. - syncPromises.push(this.workshopOffline.getAssessments(workshopId, siteId).catch(() => { - // No offline data found, return empty array. - return []; - })); - - // Get offline submission evaluations to be sent. - syncPromises.push(this.workshopOffline.getEvaluateSubmissions(workshopId, siteId).catch(() => { - // No offline data found, return empty array. - return []; - })); - - // Get offline assessment evaluations to be sent. - syncPromises.push(this.workshopOffline.getEvaluateAssessments(workshopId, siteId).catch(() => { - // No offline data found, return empty array. - return []; - })); - - // Sync offline logs. - syncPromises.push(this.logHelper.syncIfNeeded(AddonModWorkshopProvider.COMPONENT, workshopId, siteId)); - - const result = { - warnings: [], - updated: false - }; - - // Get offline submissions to be sent. - const syncPromise = Promise.all(syncPromises).then((syncs) => { - let courseId; - - // Get courseId from the first object - for (const x in syncs) { - if (syncs[x].length > 0 && syncs[x][0].courseid) { - courseId = syncs[x][0].courseid; - break; - } - } - - if (!courseId) { - // Nothing to sync. - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } - - return this.workshopProvider.getWorkshopById(courseId, workshopId, {siteId}).then((workshop) => { - const submissionsActions = syncs[0], - assessments = syncs[1], - submissionEvaluations = syncs[2], - assessmentEvaluations = syncs[3], - promises = [], - offlineSubmissions = {}; - - submissionsActions.forEach((action) => { - if (typeof offlineSubmissions[action.submissionid] == 'undefined') { - offlineSubmissions[action.submissionid] = []; - } - offlineSubmissions[action.submissionid].push(action); - }); - - Object.keys(offlineSubmissions).forEach((submissionId) => { - const submissionActions = offlineSubmissions[submissionId]; - promises.push(this.syncSubmission(workshop, submissionActions, result, siteId).then(() => { - result.updated = true; - })); - }); - - assessments.forEach((assessment) => { - promises.push(this.syncAssessment(workshop, assessment, result, siteId).then(() => { - result.updated = true; - })); - }); - - submissionEvaluations.forEach((evaluation) => { - promises.push(this.syncEvaluateSubmission(workshop, evaluation, result, siteId).then(() => { - result.updated = true; - })); - }); - - assessmentEvaluations.forEach((evaluation) => { - promises.push(this.syncEvaluateAssessment(workshop, evaluation, result, siteId).then(() => { - result.updated = true; - })); - }); - - return Promise.all(promises); - }).then(() => { - if (result.updated) { - // Data has been sent to server. Now invalidate the WS calls. - return this.workshopProvider.invalidateContentById(workshopId, courseId, siteId).catch(() => { - // Ignore errors. - }); - } - }); - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(workshopId, siteId).catch(() => { - // Ignore errors. - }); - }).then(() => { - // All done, return the warnings. - return result; - }); - - return this.addOngoingSync(workshopId, syncPromise, siteId); - } - - /** - * Synchronize a submission. - * - * @param workshop Workshop. - * @param submissionActions Submission actions offline data. - * @param result Object with the result of the sync. - * @param siteId Site ID. - * @return Promise resolved if success, rejected otherwise. - */ - protected syncSubmission(workshop: any, submissionActions: any, result: any, siteId: string): Promise { - let discardError; - let editing = false; - - // Sort entries by timemodified. - submissionActions = submissionActions.sort((a, b) => { - return a.timemodified - b.timemodified; - }); - - let timePromise = null; - let submissionId = submissionActions[0].submissionid; - - if (submissionId > 0) { - editing = true; - timePromise = this.workshopProvider.getSubmission(workshop.id, submissionId, { - cmId: workshop.coursemodule, - siteId, - }).then((submission) => { - return submission.timemodified; - }).catch(() => { - return -1; - }); - } else { - timePromise = Promise.resolve(0); - } - - return timePromise.then((timemodified) => { - if (timemodified < 0 || timemodified >= submissionActions[0].timemodified) { - // The entry was not found in Moodle or the entry has been modified, discard the action. - result.updated = true; - discardError = this.translate.instant('addon.mod_workshop.warningsubmissionmodified'); - - return this.workshopOffline.deleteAllSubmissionActions(workshop.id, submissionId, siteId); - } - - let promise = Promise.resolve(); - - submissionActions.forEach((action) => { - promise = promise.then(() => { - submissionId = action.submissionid > 0 ? action.submissionid : submissionId; - - let fileProm; - // Upload attachments first if any. - if (action.attachmentsid) { - fileProm = this.workshopHelper.getSubmissionFilesFromOfflineFilesObject(action.attachmentsid, workshop.id, - submissionId, editing, siteId).then((files) => { - return this.workshopHelper.uploadOrStoreSubmissionFiles(workshop.id, submissionId, files, editing, - false, siteId); - }); - } else { - // Remove all files. - fileProm = this.workshopHelper.uploadOrStoreSubmissionFiles(workshop.id, submissionId, [], editing, false, - siteId); - } - - return fileProm.then((attachmentsId) => { - if (workshop.submissiontypefile == AddonModWorkshopProvider.SUBMISSION_TYPE_DISABLED) { - attachmentsId = null; - } - - // Perform the action. - switch (action.action) { - case 'add': - return this.workshopProvider.addSubmissionOnline(workshop.id, action.title, action.content, - attachmentsId, siteId).then((newSubmissionId) => { - submissionId = newSubmissionId; - }); - case 'update': - return this.workshopProvider.updateSubmissionOnline(submissionId, action.title, action.content, - attachmentsId, siteId); - case 'delete': - return this.workshopProvider.deleteSubmissionOnline(submissionId, siteId); - default: - return Promise.resolve(); - } - }).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means it cannot be performed. Discard. - discardError = this.textUtils.getErrorMessageFromError(error); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }).then(() => { - // Delete the offline data. - result.updated = true; - - return this.workshopOffline.deleteSubmissionAction(action.workshopid, action.submissionid, action.action, - siteId).then(() => { - // Delete stored files. - if (action.action == 'add' || action.action == 'update') { - const editing = action.action == 'update'; - - return this.workshopHelper.deleteSubmissionStoredFiles(action.workshopid, - action.submissionid, editing, siteId); - } - }); - }); - }); - }); - - return promise.then(() => { - if (discardError) { - // Submission was discarded, add a warning. - const message = this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: workshop.name, - error: discardError - }); - - if (result.warnings.indexOf(message) == -1) { - result.warnings.push(message); - } - } - }); - }); - } - - /** - * Synchronize an assessment. - * - * @param workshop Workshop. - * @param assessment Assessment offline data. - * @param result Object with the result of the sync. - * @param siteId Site ID. - * @return Promise resolved if success, rejected otherwise. - */ - protected syncAssessment(workshop: any, assessmentData: any, result: any, siteId: string): Promise { - let discardError; - const assessmentId = assessmentData.assessmentid; - - const timePromise = this.workshopProvider.getAssessment(workshop.id, assessmentId, { - cmId: workshop.coursemodule, - siteId, - }).then((assessment) => { - return assessment.timemodified; - }).catch(() => { - return -1; - }); - - return timePromise.then((timemodified) => { - if (timemodified < 0 || timemodified >= assessmentData.timemodified) { - // The entry was not found in Moodle or the entry has been modified, discard the action. - result.updated = true; - discardError = this.translate.instant('addon.mod_workshop.warningassessmentmodified'); - - return this.workshopOffline.deleteAssessment(workshop.id, assessmentId, siteId); - } - - let fileProm; - const inputData = assessmentData.inputdata; - - // Upload attachments first if any. - if (inputData.feedbackauthorattachmentsid) { - fileProm = this.workshopHelper.getAssessmentFilesFromOfflineFilesObject(inputData.feedbackauthorattachmentsid, - workshop.id, assessmentId, siteId).then((files) => { - return this.workshopHelper.uploadOrStoreAssessmentFiles(workshop.id, assessmentId, files, false, siteId); - }); - } else { - // Remove all files. - fileProm = this.workshopHelper.uploadOrStoreAssessmentFiles(workshop.id, assessmentId, [], false, siteId); - } - - return fileProm.then((attachmentsId) => { - inputData.feedbackauthorattachmentsid = attachmentsId || 0; - - return this.workshopProvider.updateAssessmentOnline(assessmentId, inputData, siteId); - }).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means it cannot be performed. Discard. - discardError = this.textUtils.getErrorMessageFromError(error); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }).then(() => { - // Delete the offline data. - result.updated = true; - - return this.workshopOffline.deleteAssessment(workshop.id, assessmentId, siteId).then(() => { - this.workshopHelper.deleteAssessmentStoredFiles(workshop.id, assessmentId, siteId); - }); - }); - }).then(() => { - if (discardError) { - // Assessment was discarded, add a warning. - const message = this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: workshop.name, - error: discardError - }); - - if (result.warnings.indexOf(message) == -1) { - result.warnings.push(message); - } - } - }); - } - - /** - * Synchronize a submission evaluation. - * - * @param workshop Workshop. - * @param evaluate Submission evaluation offline data. - * @param result Object with the result of the sync. - * @param siteId Site ID. - * @return Promise resolved if success, rejected otherwise. - */ - protected syncEvaluateSubmission(workshop: any, evaluate: any, result: any, siteId: string): Promise { - let discardError; - const submissionId = evaluate.submissionid; - - const timePromise = this.workshopProvider.getSubmission(workshop.id, submissionId, { - cmId: workshop.coursemodule, - siteId, - }).then((submission) => { - return submission.timemodified; - }).catch(() => { - return -1; - }); - - return timePromise.then((timemodified) => { - if (timemodified < 0 || timemodified >= evaluate.timemodified) { - // The entry was not found in Moodle or the entry has been modified, discard the action. - result.updated = true; - discardError = this.translate.instant('addon.mod_workshop.warningsubmissionmodified'); - - return this.workshopOffline.deleteEvaluateSubmission(workshop.id, submissionId, siteId); - } - - return this.workshopProvider.evaluateSubmissionOnline(submissionId, evaluate.feedbacktext, evaluate.published, - evaluate.gradeover, siteId).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means it cannot be performed. Discard. - discardError = this.textUtils.getErrorMessageFromError(error); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }).then(() => { - // Delete the offline data. - result.updated = true; - - return this.workshopOffline.deleteEvaluateSubmission(workshop.id, submissionId, siteId); - }); - }).then(() => { - if (discardError) { - // Assessment was discarded, add a warning. - const message = this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: workshop.name, - error: discardError - }); - - if (result.warnings.indexOf(message) == -1) { - result.warnings.push(message); - } - } - }); - } - - /** - * Synchronize a assessment evaluation. - * - * @param workshop Workshop. - * @param evaluate Assessment evaluation offline data. - * @param result Object with the result of the sync. - * @param siteId Site ID. - * @return Promise resolved if success, rejected otherwise. - */ - protected syncEvaluateAssessment(workshop: any, evaluate: any, result: any, siteId: string): Promise { - let discardError; - const assessmentId = evaluate.assessmentid; - - const timePromise = this.workshopProvider.getAssessment(workshop.id, assessmentId, { - cmId: workshop.coursemodule, - siteId, - }).then((assessment) => { - return assessment.timemodified; - }).catch(() => { - return -1; - }); - - return timePromise.then((timemodified) => { - if (timemodified < 0 || timemodified >= evaluate.timemodified) { - // The entry was not found in Moodle or the entry has been modified, discard the action. - result.updated = true; - discardError = this.translate.instant('addon.mod_workshop.warningassessmentmodified'); - - return this.workshopOffline.deleteEvaluateAssessment(workshop.id, assessmentId, siteId); - } - - return this.workshopProvider.evaluateAssessmentOnline(assessmentId, evaluate.feedbacktext, evaluate.weight, - evaluate.gradinggradeover, siteId).catch((error) => { - if (error && this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means it cannot be performed. Discard. - discardError = this.textUtils.getErrorMessageFromError(error); - } else { - // Couldn't connect to server, reject. - return Promise.reject(error); - } - }).then(() => { - // Delete the offline data. - result.updated = true; - - return this.workshopOffline.deleteEvaluateAssessment(workshop.id, assessmentId, siteId); - }); - }).then(() => { - if (discardError) { - // Assessment was discarded, add a warning. - const message = this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: workshop.name, - error: discardError - }); - - if (result.warnings.indexOf(message) == -1) { - result.warnings.push(message); - } - } - }); - } -} diff --git a/src/addon/mod/workshop/providers/workshop.ts b/src/addon/mod/workshop/providers/workshop.ts deleted file mode 100644 index cf373a796..000000000 --- a/src/addon/mod/workshop/providers/workshop.ts +++ /dev/null @@ -1,1393 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { AddonModWorkshopOfflineProvider } from './offline'; -import { CoreSite } from '@classes/site'; -import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; - -/** - * Service that provides some features for workshops. - */ -@Injectable() -export class AddonModWorkshopProvider { - static COMPONENT = 'mmaModWorkshop'; - static PER_PAGE = 10; - static PHASE_SETUP = 10; - static PHASE_SUBMISSION = 20; - static PHASE_ASSESSMENT = 30; - static PHASE_EVALUATION = 40; - static PHASE_CLOSED = 50; - static EXAMPLES_VOLUNTARY = 0; - static EXAMPLES_BEFORE_SUBMISSION = 1; - static EXAMPLES_BEFORE_ASSESSMENT = 2; - static SUBMISSION_TYPE_DISABLED = 0; - static SUBMISSION_TYPE_AVAILABLE = 1; - static SUBMISSION_TYPE_REQUIRED = 2; - - static SUBMISSION_CHANGED = 'addon_mod_workshop_submission_changed'; - static ASSESSMENT_SAVED = 'addon_mod_workshop_assessment_saved'; - static ASSESSMENT_INVALIDATED = 'addon_mod_workshop_assessment_invalidated'; - - protected ROOT_CACHE_KEY = 'mmaModWorkshop:'; - - constructor( - private appProvider: CoreAppProvider, - private filepoolProvider: CoreFilepoolProvider, - private sitesProvider: CoreSitesProvider, - private utils: CoreUtilsProvider, - private workshopOffline: AddonModWorkshopOfflineProvider, - private logHelper: CoreCourseLogHelperProvider) {} - - /** - * Get cache key for workshop data WS calls. - * - * @param courseId Course ID. - * @return Cache key. - */ - protected getWorkshopDataCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'workshop:' + courseId; - } - - /** - * Get prefix cache key for all workshop activity data WS calls. - * - * @param workshopId Workshop ID. - * @return Cache key. - */ - protected getWorkshopDataPrefixCacheKey(workshopId: number): string { - return this.ROOT_CACHE_KEY + workshopId; - } - - /** - * Get cache key for workshop access information data WS calls. - * - * @param workshopId Workshop ID. - * @return Cache key. - */ - protected getWorkshopAccessInformationDataCacheKey(workshopId: number): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':access'; - } - - /** - * Get cache key for workshop user plan data WS calls. - * - * @param workshopId Workshop ID. - * @return Cache key. - */ - protected getUserPlanDataCacheKey(workshopId: number): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':userplan'; - } - - /** - * Get cache key for workshop submissions data WS calls. - * - * @param workshopId Workshop ID. - * @param userId User ID. - * @param groupId Group ID. - * @return Cache key. - */ - protected getSubmissionsDataCacheKey(workshopId: number, userId: number = 0, groupId: number = 0): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':submissions:' + userId + ':' + groupId; - } - - /** - * Get cache key for a workshop submission data WS calls. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @return Cache key. - */ - protected getSubmissionDataCacheKey(workshopId: number, submissionId: number): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':submission:' + submissionId; - } - - /** - * Get cache key for workshop grades data WS calls. - * - * @param workshopId Workshop ID. - * @return Cache key. - */ - protected getGradesDataCacheKey(workshopId: number): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':grades'; - } - - /** - * Get cache key for workshop grade report data WS calls. - * - * @param workshopId Workshop ID. - * @param groupId Group ID. - * @return Cache key. - */ - protected getGradesReportDataCacheKey(workshopId: number, groupId: number = 0): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':report:' + groupId; - } - - /** - * Get cache key for workshop submission assessments data WS calls. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @return Cache key. - */ - protected getSubmissionAssessmentsDataCacheKey(workshopId: number, submissionId: number): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':assessments:' + submissionId; - } - - /** - * Get cache key for workshop reviewer assessments data WS calls. - * - * @param workshopId Workshop ID. - * @param userId User ID or current user. - * @return Cache key. - */ - protected getReviewerAssessmentsDataCacheKey(workshopId: number, userId: number = 0): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':reviewerassessments:' + userId; - } - - /** - * Get cache key for a workshop assessment data WS calls. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @return Cache key. - */ - protected getAssessmentDataCacheKey(workshopId: number, assessmentId: number): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':assessment:' + assessmentId; - } - - /** - * Get cache key for workshop assessment form data WS calls. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param mode Mode assessment (default) or preview. - * @return Cache key. - */ - protected getAssessmentFormDataCacheKey(workshopId: number, assessmentId: number, mode: string = 'assessment'): string { - return this.getWorkshopDataPrefixCacheKey(workshopId) + ':assessmentsform:' + assessmentId + ':' + mode; - } - - /** - * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the workshop WS are available. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('mod_workshop_get_workshops_by_courses') && - site.wsAvailable('mod_workshop_get_workshop_access_information'); - }); - } - - /** - * Get a workshop with key=value. If more than one is found, only the first will be returned. - * - * @param courseId Course ID. - * @param key Name of the property to check. - * @param value Value to search. - * @param options Other options. - * @return Promise resolved when the workshop is retrieved. - */ - protected getWorkshopByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - courseids: [courseId], - }; - const preSets = { - cacheKey: this.getWorkshopDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModWorkshopProvider.COMPONENT, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_workshops_by_courses', params, preSets).then((response) => { - if (response && response.workshops) { - const workshopFound = response.workshops.find((workshop) => workshop[key] == value); - if (workshopFound) { - return workshopFound; - } - } - - return Promise.reject(null); - }).then((workshop) => { - // Set submission types for Moodle 3.5 and older. - if (typeof workshop.submissiontypetext == 'undefined') { - if (workshop.nattachments > 0) { - workshop.submissiontypetext = AddonModWorkshopProvider.SUBMISSION_TYPE_AVAILABLE; - workshop.submissiontypefile = AddonModWorkshopProvider.SUBMISSION_TYPE_AVAILABLE; - } else { - workshop.submissiontypetext = AddonModWorkshopProvider.SUBMISSION_TYPE_REQUIRED; - workshop.submissiontypefile = AddonModWorkshopProvider.SUBMISSION_TYPE_DISABLED; - } - } - - return workshop; - }); - }); - } - - /** - * Get a workshop by course module ID. - * - * @param courseId Course ID. - * @param cmId Course module ID. - * @param options Other options. - * @return Promise resolved when the workshop is retrieved. - */ - getWorkshop(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getWorkshopByKey(courseId, 'coursemodule', cmId, options); - } - - /** - * Get a workshop by ID. - * - * @param courseId Course ID. - * @param id Workshop ID. - * @param options Other options. - * @return Promise resolved when the workshop is retrieved. - */ - getWorkshopById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { - return this.getWorkshopByKey(courseId, 'id', id, options); - } - - /** - * Invalidates workshop data. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the workshop is invalidated. - */ - invalidateWorkshopData(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getWorkshopDataCacheKey(courseId)); - }); - } - - /** - * Invalidates workshop data except files and module info. - * - * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the workshop is invalidated. - */ - invalidateWorkshopWSData(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKeyStartingWith(this.getWorkshopDataPrefixCacheKey(workshopId)); - }); - } - - /** - * Get access information for a given workshop. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Promise resolved when the workshop is retrieved. - */ - getWorkshopAccessInformation(workshopId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - workshopid: workshopId, - }; - const preSets = { - cacheKey: this.getWorkshopAccessInformationDataCacheKey(workshopId), - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_workshop_access_information', params, preSets); - }); - } - - /** - * Invalidates workshop access information data. - * - * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateWorkshopAccessInformationData(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getWorkshopAccessInformationDataCacheKey(workshopId)); - }); - } - - /** - * Return the planner information for the given user. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Promise resolved when the workshop data is retrieved. - */ - getUserPlanPhases(workshopId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - workshopid: workshopId - }; - const preSets = { - cacheKey: this.getUserPlanDataCacheKey(workshopId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_user_plan', params, preSets).then((response) => { - if (response && response.userplan && response.userplan.phases) { - return this.utils.arrayToObject(response.userplan.phases, 'code'); - } - - return Promise.reject(null); - }); - }); - } - - /** - * Invalidates workshop user plan data. - * - * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateUserPlanPhasesData(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getUserPlanDataCacheKey(workshopId)); - }); - } - - /** - * Retrieves all the workshop submissions visible by the current user or the one done by the given user. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Promise resolved when the workshop submissions are retrieved. - */ - getSubmissions(workshopId: number, options: AddonModWorkshopGetSubmissionsOptions = {}): Promise { - const userId = options.userId || 0; - const groupId = options.groupId || 0; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - workshopid: workshopId, - userid: userId, - groupid: groupId, - }; - const preSets = { - cacheKey: this.getSubmissionsDataCacheKey(workshopId, userId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_submissions', params, preSets).then((response) => { - if (response && response.submissions) { - return response.submissions; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Invalidates workshop submissions data. - * - * @param workshopId Workshop ID. - * @param userId User ID. - * @param groupId Group ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSubmissionsData(workshopId: number, userId: number = 0, groupId: number = 0, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getSubmissionsDataCacheKey(workshopId, userId, groupId)); - }); - } - - /** - * Retrieves the given submission. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param options Other options. - * @return Promise resolved when the workshop submission data is retrieved. - */ - getSubmission(workshopId: number, submissionId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - submissionid: submissionId, - }; - const preSets = { - cacheKey: this.getSubmissionDataCacheKey(workshopId, submissionId), - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_submission', params, preSets).then((response) => { - if (response && response.submission) { - return response.submission; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Invalidates workshop submission data. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSubmissionData(workshopId: number, submissionId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getSubmissionDataCacheKey(workshopId, submissionId)); - }); - } - - /** - * Returns the grades information for the given workshop and user. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Promise resolved when the workshop grades data is retrieved. - */ - getGrades(workshopId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - workshopid: workshopId - }; - const preSets = { - cacheKey: this.getGradesDataCacheKey(workshopId), - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_grades', params, preSets); - }); - } - - /** - * Invalidates workshop grades data. - * - * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateGradesData(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getGradesDataCacheKey(workshopId)); - }); - } - - /** - * Retrieves the assessment grades report. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Promise resolved when the workshop data is retrieved. - */ - getGradesReport(workshopId: number, options: AddonModWorkshopGetGradesReportOptions = {}): Promise { - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - workshopid: workshopId, - groupid: options.groupId, - page: options.page || 0, - perpage: options.perPage || AddonModWorkshopProvider.PER_PAGE - }; - const preSets = { - cacheKey: this.getGradesReportDataCacheKey(workshopId, options.groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_grades_report', params, preSets).then((response) => { - if (response && response.report) { - return response.report; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Performs the whole fetch of the grade reports in the workshop. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Promise resolved when done. - */ - fetchAllGradeReports(workshopId: number, options: AddonModWorkshopFetchAllGradesReportOptions = {}): Promise { - return this.fetchGradeReportsRecursive(workshopId, [], { - ...options, // Include all options. - page: 0, - perPage: options.perPage || AddonModWorkshopProvider.PER_PAGE, - siteId: options.siteId || this.sitesProvider.getCurrentSiteId(), - }); - } - - /** - * Recursive call on fetch all grade reports. - * - * @param workshopId Workshop ID. - * @param grades Grades already fetched (just to concatenate them). - * @param options Other options. - * @return Promise resolved when done. - */ - protected fetchGradeReportsRecursive(workshopId: number, grades: any[], options: AddonModWorkshopGetGradesReportOptions = {}) - : Promise { - - return this.getGradesReport(workshopId, options).then((report) => { - Array.prototype.push.apply(grades, report.grades); - - const canLoadMore = ((options.page + 1) * options.perPage) < report.totalcount; - if (canLoadMore) { - options.page++; - - return this.fetchGradeReportsRecursive(workshopId, grades, options); - } - - return grades; - }); - } - - /** - * Invalidates workshop grade report data. - * - * @param workshopId Workshop ID. - * @param groupId Group ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateGradeReportData(workshopId: number, groupId: number = 0, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getGradesReportDataCacheKey(workshopId, groupId)); - }); - } - - /** - * Retrieves the given submission assessment. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param options Other options. - * @return Promise resolved when the workshop data is retrieved. - */ - getSubmissionAssessments(workshopId: number, submissionId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - submissionid: submissionId, - }; - const preSets = { - cacheKey: this.getSubmissionAssessmentsDataCacheKey(workshopId, submissionId), - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_submission_assessments', params, preSets).then((response) => { - if (response && response.assessments) { - return response.assessments; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Invalidates workshop submission assessments data. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateSubmissionAssesmentsData(workshopId: number, submissionId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getSubmissionAssessmentsDataCacheKey(workshopId, submissionId)); - }); - } - - /** - * Add a new submission to a given workshop. - * - * @param workshopId Workshop ID. - * @param courseId Course ID the workshop belongs to. - * @param title The submission title. - * @param content The submission text content. - * @param attachmentsId The draft file area id for attachments. - * @param siteId Site ID. If not defined, current site. - * @param timecreated The time the submission was created. Only used when editing an offline discussion. - * @param allowOffline True if it can be stored in offline, false otherwise. - * @return Promise resolved with submission ID if sent online or false if stored offline. - */ - addSubmission(workshopId: number, courseId: number, title: string, content: string, attachmentsId?: number, siteId?: string, - timecreated?: number, allowOffline: boolean = false): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.workshopOffline.saveSubmission(workshopId, courseId, title, content, {}, timecreated, 'add', - siteId).then(() => { - return false; - }); - }; - - // If we are editing an offline discussion, discard previous first. - const discardPromise = - timecreated ? this.workshopOffline.deleteSubmissionAction(workshopId, timecreated, 'add', siteId) : Promise.resolve(); - - return discardPromise.then(() => { - if (!this.appProvider.isOnline() && allowOffline) { - // App is offline, store the action. - return storeOffline(); - } - - return this.addSubmissionOnline(workshopId, title, content, attachmentsId, siteId).catch((error) => { - if (allowOffline && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Add a new submission to a given workshop. It will fail if offline or cannot connect. - * - * @param workshopId Workshop ID. - * @param title The submission title. - * @param content The submission text content. - * @param attachmentsId The draft file area id for attachments. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the submission is created. - */ - addSubmissionOnline(workshopId: number, title: string, content: string, attachmentsId: number, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - workshopid: workshopId, - title: title, - content: content, - attachmentsid: attachmentsId || 0 - }; - - return site.write('mod_workshop_add_submission', params).then((response) => { - // Other errors ocurring. - if (!response || !response.submissionid) { - return Promise.reject(this.utils.createFakeWSError('')); - } - - return response.submissionid; - }); - }); - } - - /** - * Updates the given submission. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param courseId Course ID the workshop belongs to. - * @param title The submission title. - * @param content The submission text content. - * @param attachmentsId The draft file area id for attachments. - * @param siteId Site ID. If not defined, current site. - * @param allowOffline True if it can be stored in offline, false otherwise. - * @return Promise resolved with submission ID if sent online or false if stored offline. - */ - updateSubmission(workshopId: number, submissionId: number, courseId: number, title: string, content: string, - attachmentsId?: number, siteId?: string, allowOffline: boolean = false): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.workshopOffline.saveSubmission(workshopId, courseId, title, content, attachmentsId, submissionId, 'update', - siteId).then(() => { - return false; - }); - }; - - // If we are editing an offline discussion, discard previous first. - return this.workshopOffline.deleteSubmissionAction(workshopId, submissionId, 'update', siteId).then(() => { - if (!this.appProvider.isOnline() && allowOffline) { - // App is offline, store the action. - return storeOffline(); - } - - return this.updateSubmissionOnline(submissionId, title, content, attachmentsId, siteId).catch((error) => { - if (allowOffline && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Updates the given submission. It will fail if offline or cannot connect. - * - * @param submissionId Submission ID. - * @param title The submission title. - * @param content The submission text content. - * @param attachmentsId The draft file area id for attachments. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the submission is updated. - */ - updateSubmissionOnline(submissionId: number, title: string, content: string, attachmentsId?: number, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - submissionid: submissionId, - title: title, - content: content, - attachmentsid: attachmentsId || 0 - }; - - return site.write('mod_workshop_update_submission', params).then((response) => { - // Other errors ocurring. - if (!response || !response.status) { - return Promise.reject(this.utils.createFakeWSError('')); - } - - // Return submissionId to be consistent with addSubmission. - return Promise.resolve(submissionId); - }); - }); - } - - /** - * Deletes the given submission. - * - * @param workshopId Workshop ID. - * @param submissionId Submission ID. - * @param courseId Course ID the workshop belongs to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with submission ID if sent online, resolved with false if stored offline. - */ - deleteSubmission(workshopId: number, submissionId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.workshopOffline.saveSubmission(workshopId, courseId, '', '', 0, submissionId, 'delete', siteId).then(() => { - return false; - }); - }; - - // If we are editing an offline discussion, discard previous first. - return this.workshopOffline.deleteSubmissionAction(workshopId, submissionId, 'delete', siteId).then(() => { - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - return this.deleteSubmissionOnline(submissionId, siteId).catch((error) => { - if (!this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Deletes the given submission. It will fail if offline or cannot connect. - * - * @param submissionId Submission ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the submission is deleted. - */ - deleteSubmissionOnline(submissionId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - submissionid: submissionId - }; - - return site.write('mod_workshop_delete_submission', params).then((response) => { - // Other errors ocurring. - if (!response || !response.status) { - return Promise.reject(this.utils.createFakeWSError('')); - } - - // Return submissionId to be consistent with addSubmission. - return Promise.resolve(submissionId); - }); - }); - } - - /** - * Retrieves all the assessments reviewed by the given user. - * - * @param workshopId Workshop ID. - * @param options Other options. - * @return Promise resolved when the workshop data is retrieved. - */ - getReviewerAssessments(workshopId: number, options: AddonModWorkshopUserOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params: any = { - workshopid: workshopId, - }; - const preSets = { - cacheKey: this.getReviewerAssessmentsDataCacheKey(workshopId, options.userId), - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - if (options.userId) { - params.userid = options.userId; - } - - return site.read('mod_workshop_get_reviewer_assessments', params, preSets).then((response) => { - if (response && response.assessments) { - return response.assessments; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Invalidates workshop user assessments data. - * - * @param workshopId Workshop ID. - * @param userId User ID. If not defined, current user. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateReviewerAssesmentsData(workshopId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getReviewerAssessmentsDataCacheKey(workshopId, userId)); - }); - } - - /** - * Retrieves the given assessment. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param options Other options. - * @return Promise resolved when the workshop data is retrieved. - */ - getAssessment(workshopId: number, assessmentId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - assessmentid: assessmentId, - }; - const preSets = { - cacheKey: this.getAssessmentDataCacheKey(workshopId, assessmentId), - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_assessment', params, preSets).then((response) => { - if (response && response.assessment) { - return response.assessment; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Invalidates workshop assessment data. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAssessmentData(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAssessmentDataCacheKey(workshopId, assessmentId)); - }); - } - - /** - * Retrieves the assessment form definition (data required to be able to display the assessment form). - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param options Other options. - * @return Promise resolved when the workshop data is retrieved. - */ - getAssessmentForm(workshopId: number, assessmentId: number, options: AddonModWorkshopGetAssessmentFormOptions = {}) - : Promise { - const mode = options.mode || 'assessment'; - - return this.sitesProvider.getSite(options.siteId).then((site) => { - const params = { - assessmentid: assessmentId, - mode: mode, - }; - const preSets = { - cacheKey: this.getAssessmentFormDataCacheKey(workshopId, assessmentId, mode), - updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModWorkshopProvider.COMPONENT, - componentId: options.cmId, - ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. - }; - - return site.read('mod_workshop_get_assessment_form_definition', params, preSets).then((response) => { - if (response) { - response.fields = this.parseFields(response.fields); - response.options = this.utils.objectToKeyValueMap(response.options, 'name', 'value'); - response.current = this.parseFields(response.current); - - return response; - } - - return Promise.reject(null); - }); - }); - } - - /** - * Parse fieldes into a more handful format. - * - * @param fields Fields to parse - * @return Parsed fields - */ - parseFields(fields: any[]): any[] { - const parsedFields = []; - - fields.forEach((field) => { - const args = field.name.split('_'); - const name = args[0]; - const idx = args[3]; - const idy = args[6] || false; - - if (parseInt(idx, 10) == idx) { - if (!parsedFields[idx]) { - parsedFields[idx] = { - number: parseInt(idx, 10) + 1 - }; - } - - if (idy && parseInt(idy, 10) == idy) { - if (!parsedFields[idx].fields) { - parsedFields[idx].fields = []; - } - if (!parsedFields[idx].fields[idy]) { - parsedFields[idx].fields[idy] = { - number: parseInt(idy, 10) + 1 - }; - } - parsedFields[idx].fields[idy][name] = field.value; - } else { - parsedFields[idx][name] = field.value; - } - } - }); - - return parsedFields; - } - - /** - * Invalidates workshop assessments form data. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param mode Mode assessment (default) or preview. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the data is invalidated. - */ - invalidateAssessmentFormData(workshopId: number, assessmentId: number, mode: string = 'assessment', siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getAssessmentFormDataCacheKey(workshopId, assessmentId, mode)); - }); - } - - /** - * Updates the given assessment. - * - * @param workshopId Workshop ID. - * @param assessmentId Assessment ID. - * @param courseId Course ID the workshop belongs to. - * @param inputData Assessment data. - * @param siteId Site ID. If not defined, current site. - * @param allowOffline True if it can be stored in offline, false otherwise. - * @return Promise resolved with the grade of the submission if sent online, - * resolved with false if stored offline. - */ - updateAssessment(workshopId: number, assessmentId: number, courseId: number, inputData: any, siteId?: any, - allowOffline: boolean = false): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.workshopOffline.saveAssessment(workshopId, assessmentId, courseId, inputData, siteId).then(() => { - return false; - }); - }; - - // If we are editing an offline discussion, discard previous first. - return this.workshopOffline.deleteAssessment(workshopId, assessmentId, siteId).then(() => { - if (!this.appProvider.isOnline() && allowOffline) { - // App is offline, store the action. - return storeOffline(); - } - - return this.updateAssessmentOnline(assessmentId, inputData, siteId).catch((error) => { - if (allowOffline && !this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Updates the given assessment. It will fail if offline or cannot connect. - * - * @param assessmentId Assessment ID. - * @param inputData Assessment data. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the grade of the submission. - */ - updateAssessmentOnline(assessmentId: number, inputData: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - assessmentid: assessmentId, - data: this.utils.objectToArrayOfObjects(inputData, 'name', 'value') - }; - - return site.write('mod_workshop_update_assessment', params).then((response) => { - // Other errors ocurring. - if (!response || !response.status) { - return Promise.reject(this.utils.createFakeWSError('')); - } - - // Return rawgrade for submission - return response.rawgrade; - }); - }); - } - - /** - * Evaluates a submission (used by teachers for provide feedback or override the submission grade). - * - * @param workshopId Workshop ID. - * @param submissionId The submission id. - * @param courseId Course ID the workshop belongs to. - * @param feedbackText The feedback for the author. - * @param published Whether to publish the submission for other users. - * @param gradeOver The new submission grade (empty for no overriding the grade). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when submission is evaluated if sent online, - * resolved with false if stored offline. - */ - evaluateSubmission(workshopId: number, submissionId: number, courseId: number, feedbackText: string, published: boolean, - gradeOver: any, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.workshopOffline.saveEvaluateSubmission(workshopId, submissionId, courseId, feedbackText, published, - gradeOver, siteId).then(() => { - return false; - }); - }; - - // If we are editing an offline discussion, discard previous first. - return this.workshopOffline.deleteEvaluateSubmission(workshopId, submissionId, siteId).then(() => { - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - return this.evaluateSubmissionOnline(submissionId, feedbackText, published, gradeOver, siteId).catch((error) => { - if (!this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Evaluates a submission (used by teachers for provide feedback or override the submission grade). - * It will fail if offline or cannot connect. - * - * @param submissionId The submission id. - * @param feedbackText The feedback for the author. - * @param published Whether to publish the submission for other users. - * @param gradeOver The new submission grade (empty for no overriding the grade). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the submission is evaluated. - */ - evaluateSubmissionOnline(submissionId: number, feedbackText: string, published: boolean, gradeOver: any, - siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - submissionid: submissionId, - feedbacktext: feedbackText || '', - feedbackformat: 1, - published: published ? 1 : 0, - gradeover: gradeOver - }; - - return site.write('mod_workshop_evaluate_submission', params).then((response) => { - // Other errors ocurring. - if (!response || !response.status) { - return Promise.reject(this.utils.createFakeWSError('')); - } - - // Return if worked. - return Promise.resolve(true); - }); - }); - } - - /** - * Evaluates an assessment (used by teachers for provide feedback to the reviewer). - * - * @param workshopId Workshop ID. - * @param assessmentId The assessment id. - * @param courseId Course ID the workshop belongs to. - * @param feedbackText The feedback for the reviewer. - * @param weight The new weight for the assessment. - * @param gradingGradeOver The new grading grade (empty for no overriding the grade). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when assessment is evaluated if sent online, - * resolved with false if stored offline. - */ - evaluateAssessment(workshopId: number, assessmentId: number, courseId: number, feedbackText: string, weight: number, - gradingGradeOver: any, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a message to be synchronized later. - const storeOffline = (): Promise => { - return this.workshopOffline.saveEvaluateAssessment(workshopId, assessmentId, courseId, feedbackText, weight, - gradingGradeOver, siteId).then(() => { - return false; - }); - }; - - // If we are editing an offline discussion, discard previous first. - return this.workshopOffline.deleteEvaluateAssessment(workshopId, assessmentId, siteId).then(() => { - if (!this.appProvider.isOnline()) { - // App is offline, store the action. - return storeOffline(); - } - - return this.evaluateAssessmentOnline(assessmentId, feedbackText, weight, gradingGradeOver, siteId).catch((error) => { - if (!this.utils.isWebServiceError(error)) { - // Couldn't connect to server, store in offline. - return storeOffline(); - } else { - // The WebService has thrown an error or offline not supported, reject. - return Promise.reject(error); - } - }); - }); - } - - /** - * Evaluates an assessment (used by teachers for provide feedback to the reviewer). It will fail if offline or cannot connect. - * - * @param assessmentId The assessment id. - * @param feedbackText The feedback for the reviewer. - * @param weight The new weight for the assessment. - * @param gradingGradeOver The new grading grade (empty for no overriding the grade). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the assessment is evaluated. - */ - evaluateAssessmentOnline(assessmentId: number, feedbackText: string, weight: number, gradingGradeOver: any, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - assessmentid: assessmentId, - feedbacktext: feedbackText || '', - feedbackformat: 1, - weight: weight, - gradinggradeover: gradingGradeOver - }; - - return site.write('mod_workshop_evaluate_assessment', params).then((response) => { - // Other errors ocurring. - if (!response || !response.status) { - return Promise.reject(this.utils.createFakeWSError('')); - } - - // Return if worked. - return Promise.resolve(true); - }); - }); - } - - /** - * Invalidate the prefetched content except files. - * To invalidate files, use AddonModWorkshopProvider#invalidateFiles. - * - * @param moduleId The module ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promised resolved when content is invalidated. - */ - invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.getWorkshop(courseId, moduleId, { - readingStrategy: CoreSitesReadingStrategy.PreferCache, - siteId, - }).then((workshop) => { - return this.invalidateContentById(workshop.id, courseId, siteId); - }); - } - - /** - * Invalidate the prefetched content except files using the activityId. - * To invalidate files, use AdddonModWorkshop#invalidateFiles. - * - * @param workshopId Workshop ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when content is invalidated. - */ - invalidateContentById(workshopId: number, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const promises = [ - // Do not invalidate workshop data before getting workshop info, we need it! - this.invalidateWorkshopData(courseId, siteId), - this.invalidateWorkshopWSData(workshopId, siteId), - ]; - - return Promise.all(promises); - } - - /** - * Invalidate the prefetched files. - * - * @param moduleId The module ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the files are invalidated. - */ - invalidateFiles(moduleId: number, siteId?: string): Promise { - return this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModWorkshopProvider.COMPONENT, moduleId); - } - - /** - * Report the workshop as being viewed. - * - * @param id Workshop ID. - * @param name Name of the workshop. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(id: number, name?: string, siteId?: string): Promise { - const params = { - workshopid: id - }; - - return this.logHelper.logSingle('mod_workshop_view_workshop', params, AddonModWorkshopProvider.COMPONENT, id, name, - 'workshop', siteId); - } - - /** - * Report the workshop submission as being viewed. - * - * @param id Submission ID. - * @param workshopId Workshop ID. - * @param name Name of the workshop. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logViewSubmission(id: number, workshopId: number, name?: string, siteId?: string): Promise { - const params = { - submissionid: id - }; - - return this.logHelper.logSingle('mod_workshop_view_submission', params, AddonModWorkshopProvider.COMPONENT, workshopId, - name, 'workshop', params, siteId); - } -} - -/** - * Common options with a user ID. - */ -export type AddonModWorkshopUserOptions = CoreCourseCommonModWSOptions & { - userId?: number; // User ID. If not defined, current user. -}; - -/** - * Common options with a group ID. - */ -export type AddonModWorkshopGroupOptions = CoreCourseCommonModWSOptions & { - groupId?: number; // Group id, 0 or not defined means that the function will determine the user group. -}; - -/** - * Options to pass to getSubmissions. - */ -export type AddonModWorkshopGetSubmissionsOptions = AddonModWorkshopUserOptions & AddonModWorkshopGroupOptions; - -/** - * Options to pass to fetchAllGradeReports. - */ -export type AddonModWorkshopFetchAllGradesReportOptions = AddonModWorkshopGroupOptions & { - perPage?: number; // Records per page to return. Default AddonModWorkshopProvider.PER_PAGE. -}; - -/** - * Options to pass to getGradesReport. - */ -export type AddonModWorkshopGetGradesReportOptions = AddonModWorkshopFetchAllGradesReportOptions & { - page?: number; // Page of records to return. Default 0. -}; - -/** - * Options to pass to getAssessmentForm. - */ -export type AddonModWorkshopGetAssessmentFormOptions = CoreCourseCommonModWSOptions & { - mode?: string; // Mode assessment (default) or preview. Defaults to 'assessment'. -}; diff --git a/src/addon/mod/workshop/workshop.module.ts b/src/addon/mod/workshop/workshop.module.ts deleted file mode 100644 index 77e4052bf..000000000 --- a/src/addon/mod/workshop/workshop.module.ts +++ /dev/null @@ -1,75 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; -import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { AddonModWorkshopAssessmentStrategyModule } from './assessment/assessment.module'; -import { AddonModWorkshopComponentsModule } from './components/components.module'; -import { AddonModWorkshopModuleHandler } from './providers/module-handler'; -import { AddonModWorkshopProvider } from './providers/workshop'; -import { AddonModWorkshopLinkHandler } from './providers/link-handler'; -import { AddonModWorkshopListLinkHandler } from './providers/list-link-handler'; -import { AddonModWorkshopOfflineProvider } from './providers/offline'; -import { AddonModWorkshopSyncProvider } from './providers/sync'; -import { AddonModWorkshopHelperProvider } from './providers/helper'; -import { AddonWorkshopAssessmentStrategyDelegate } from './providers/assessment-strategy-delegate'; -import { AddonModWorkshopPrefetchHandler } from './providers/prefetch-handler'; -import { AddonModWorkshopSyncCronHandler } from './providers/sync-cron-handler'; - -// List of providers (without handlers). -export const ADDON_MOD_WORKSHOP_PROVIDERS: any[] = [ - AddonModWorkshopProvider, - AddonModWorkshopOfflineProvider, - AddonModWorkshopSyncProvider, - AddonModWorkshopHelperProvider, - AddonWorkshopAssessmentStrategyDelegate -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonModWorkshopComponentsModule, - AddonModWorkshopAssessmentStrategyModule - ], - providers: [ - AddonModWorkshopProvider, - AddonModWorkshopModuleHandler, - AddonModWorkshopLinkHandler, - AddonModWorkshopListLinkHandler, - AddonModWorkshopOfflineProvider, - AddonModWorkshopSyncProvider, - AddonModWorkshopHelperProvider, - AddonWorkshopAssessmentStrategyDelegate, - AddonModWorkshopPrefetchHandler, - AddonModWorkshopSyncCronHandler - ] -}) -export class AddonModWorkshopModule { - constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModWorkshopModuleHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModWorkshopLinkHandler, - prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModWorkshopPrefetchHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonModWorkshopSyncCronHandler, - listLinkHandler: AddonModWorkshopListLinkHandler) { - - moduleDelegate.registerHandler(moduleHandler); - contentLinksDelegate.registerHandler(linkHandler); - contentLinksDelegate.registerHandler(listLinkHandler); - prefetchDelegate.registerHandler(prefetchHandler); - cronDelegate.register(syncHandler); - } -} diff --git a/src/addon/mod/workshop/workshop.scss b/src/addon/mod/workshop/workshop.scss deleted file mode 100644 index ab0146f9c..000000000 --- a/src/addon/mod/workshop/workshop.scss +++ /dev/null @@ -1,14 +0,0 @@ -addon-mod-workshop-submission, -addon-mod-workshop-submission-page, -addon-mod-workshop-assessment, -addon-mod-workshop-assessment-page { - - p.addon-overriden-grade { - color: color($colors, success); - } - - p.addon-has-overriden-grade { - color: color($colors, danger); - text-decoration: line-through; - } -} diff --git a/src/addon/notes/components/components.module.ts b/src/addon/notes/components/components.module.ts deleted file mode 100644 index 33754fcc6..000000000 --- a/src/addon/notes/components/components.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonNotesListComponent } from './list/list'; - -@NgModule({ - declarations: [ - AddonNotesListComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule - ], - providers: [ - ], - exports: [ - AddonNotesListComponent - ], - entryComponents: [ - AddonNotesListComponent - ] -}) -export class AddonNotesComponentsModule {} diff --git a/src/addon/notes/components/list/addon-notes-list.html b/src/addon/notes/components/list/addon-notes-list.html deleted file mode 100644 index 0409e6aae..000000000 --- a/src/addon/notes/components/list/addon-notes-list.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - -

{{user.fullname}}

-
- -
- - {{ 'addon.notes.sitenotes' | translate }} - {{ 'addon.notes.coursenotes' | translate }} - {{ 'addon.notes.personalnotes' | translate }} - -
- -
- - {{ 'core.thereisdatatosync' | translate:{$a: 'addon.notes.notes' | translate | lowercase } }} -
- - - - - - - -

{{note.userfullname}}

-

- {{note.lastmodified | coreDateDayOrTime}} -

-

- {{ 'core.notsent' | translate }}

-

- {{ 'core.deletedoffline' | translate }} -

- - -
- -
-
- -
- - - -
diff --git a/src/addon/notes/components/list/list.ts b/src/addon/notes/components/list/list.ts deleted file mode 100644 index f242bcc31..000000000 --- a/src/addon/notes/components/list/list.ts +++ /dev/null @@ -1,285 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { Content, ModalController } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { coreSlideInOut } from '@classes/animations'; -import { AddonNotesProvider, AddonNotesNoteFormatted } from '../../providers/notes'; -import { AddonNotesOfflineProvider } from '../../providers/notes-offline'; -import { AddonNotesSyncProvider } from '../../providers/notes-sync'; - -/** - * Component that displays the notes of a course. - */ -@Component({ - selector: 'addon-notes-list', - templateUrl: 'addon-notes-list.html', - animations: [coreSlideInOut] -}) -export class AddonNotesListComponent implements OnInit, OnDestroy { - @Input() courseId: number; - @Input() userId?: number; - - @ViewChild(Content) content: Content; - - protected syncObserver: any; - - type = 'course'; - refreshIcon = 'spinner'; - syncIcon = 'spinner'; - notes: AddonNotesNoteFormatted[]; - hasOffline = false; - notesLoaded = false; - user: any; - showDelete = false; - canDeleteNotes = false; - currentUserId: number; - - constructor(private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider, - sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, private modalCtrl: ModalController, - private notesProvider: AddonNotesProvider, private notesSync: AddonNotesSyncProvider, - private userProvider: CoreUserProvider, private notesOffline: AddonNotesOfflineProvider) { - // Refresh data if notes are synchronized automatically. - this.syncObserver = eventsProvider.on(AddonNotesSyncProvider.AUTO_SYNCED, (data) => { - if (data.courseId == this.courseId) { - // Show the sync warnings. - this.showSyncWarnings(data.warnings); - - // Refresh the data. - this.notesLoaded = false; - this.refreshIcon = 'spinner'; - this.syncIcon = 'spinner'; - - this.domUtils.scrollToTop(this.content); - this.fetchNotes(false); - } - }, sitesProvider.getCurrentSiteId()); - - this.currentUserId = sitesProvider.getCurrentSiteUserId(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.fetchNotes(true).then(() => { - this.notesProvider.logView(this.courseId, this.userId).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Fetch notes. - * - * @param sync When to resync notes. - * @param showErrors When to display errors or not. - * @return Promise with the notes. - */ - private fetchNotes(sync: boolean, showErrors?: boolean): Promise { - const promise = sync ? this.syncNotes(showErrors) : Promise.resolve(); - - return promise.catch(() => { - // Ignore errors. - }).then(() => { - return this.notesProvider.getNotes(this.courseId, this.userId).then((notes) => { - const notesList: AddonNotesNoteFormatted[] = notes[this.type + 'notes'] || []; - - notesList.forEach((note) => { - note.content = this.textUtils.decodeHTML(note.content); - }); - - return this.notesProvider.setOfflineDeletedNotes(notesList, this.courseId).then((notesList) => { - - this.hasOffline = notesList.some((note) => note.offline || note.deleted); - - if (this.userId) { - this.notes = notesList; - - // Get the user profile to retrieve the user image. - return this.userProvider.getProfile(this.userId, this.courseId, true).then((user) => { - this.user = user; - }); - } else { - return this.notesProvider.getNotesUserData(notesList, this.courseId).then((notes) => { - this.notes = notes; - }); - } - }); - }); - }).catch((message) => { - this.domUtils.showErrorModal(message); - }).finally(() => { - let canDelete = this.notes && this.notes.length > 0; - if (canDelete && this.type == 'personal') { - canDelete = !!this.notes.find((note) => { - return note.usermodified == this.currentUserId; - }); - } - this.canDeleteNotes = canDelete; - - this.notesLoaded = true; - this.refreshIcon = 'refresh'; - this.syncIcon = 'sync'; - }); - } - - /** - * Refresh notes on PTR. - * - * @param showErrors Whether to display errors or not. - * @param refresher Refresher instance. - */ - refreshNotes(showErrors: boolean, refresher?: any): void { - this.refreshIcon = 'spinner'; - this.syncIcon = 'spinner'; - this.notesProvider.invalidateNotes(this.courseId, this.userId).finally(() => { - this.fetchNotes(true, showErrors).finally(() => { - if (refresher) { - refresher.complete(); - } - }); - }); - } - - /** - * Function called when the type has changed. - */ - typeChanged(): void { - this.notesLoaded = false; - this.refreshIcon = 'spinner'; - this.syncIcon = 'spinner'; - this.fetchNotes(true).then(() => { - this.notesProvider.logView(this.courseId, this.userId).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Add a new Note to user and course. - * - * @param e Event. - */ - addNote(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - const modal = this.modalCtrl.create('AddonNotesAddPage', { userId: this.userId, courseId: this.courseId, type: this.type }); - modal.onDidDismiss((data) => { - if (data && data.sent && data.type) { - if (data.type != this.type) { - this.type = data.type; - this.notesLoaded = false; - } - - this.refreshNotes(false); - } else if (data && data.type && data.type != this.type) { - this.type = data.type; - this.typeChanged(); - } - }); - - modal.present(); - } - - /** - * Delete a note. - * - * @param e Click event. - * @param note Note to delete. - */ - deleteNote(e: Event, note: AddonNotesNoteFormatted): void { - e.preventDefault(); - e.stopPropagation(); - - this.domUtils.showDeleteConfirm('addon.notes.deleteconfirm').then(() => { - this.notesProvider.deleteNote(note, this.courseId).then(() => { - this.showDelete = false; - - this.refreshNotes(false); - - this.domUtils.showToast('addon.notes.eventnotedeleted', true, 3000); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Delete note failed.'); - }); - }).catch(() => { - // User cancelled, nothing to do. - }); - } - - /** - * Restore a note. - * - * @param e Click event. - * @param note Note to delete. - */ - undoDeleteNote(e: Event, note: AddonNotesNoteFormatted): void { - e.preventDefault(); - e.stopPropagation(); - - this.notesOffline.undoDeleteNote(note.id).then(() => { - this.refreshNotes(true); - }); - } - - /** - * Toggle delete. - */ - toggleDelete(): void { - this.showDelete = !this.showDelete; - } - - /** - * Tries to synchronize course notes. - * - * @param showErrors Whether to display errors or not. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - private syncNotes(showErrors: boolean): Promise { - return this.notesSync.syncNotes(this.courseId).then((warnings) => { - this.showSyncWarnings(warnings); - }).catch((error) => { - if (showErrors) { - this.domUtils.showErrorModalDefault(error, 'core.errorsync', true); - } - - return Promise.reject(null); - }); - } - - /** - * Show sync warnings if any. - * - * @param warnings the warnings - */ - private showSyncWarnings(warnings: string[]): void { - const message = this.textUtils.buildMessage(warnings); - if (message) { - this.domUtils.showErrorModal(message); - } - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.syncObserver && this.syncObserver.off(); - } -} diff --git a/src/addon/notes/lang/en.json b/src/addon/notes/lang/en.json deleted file mode 100644 index c8256d0c4..000000000 --- a/src/addon/notes/lang/en.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "addnewnote": "Add a new note", - "coursenotes": "Course notes", - "deleteconfirm": "Delete this note?", - "eventnotecreated": "Note created", - "eventnotedeleted": "Note deleted", - "nonotes": "There are no notes of this type yet", - "note": "Note", - "notes": "Notes", - "personalnotes": "Personal notes", - "publishstate": "Context", - "sitenotes": "Site notes", - "userwithid": "User with ID {{id}}", - "warningnotenotsent": "Couldn't add note(s) to course {{course}}. {{error}}" -} \ No newline at end of file diff --git a/src/addon/notes/notes.module.ts b/src/addon/notes/notes.module.ts deleted file mode 100644 index 0d803fa2a..000000000 --- a/src/addon/notes/notes.module.ts +++ /dev/null @@ -1,58 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonNotesProvider } from './providers/notes'; -import { AddonNotesOfflineProvider } from './providers/notes-offline'; -import { AddonNotesSyncProvider } from './providers/notes-sync'; -import { AddonNotesCourseOptionHandler } from './providers/course-option-handler'; -import { AddonNotesSyncCronHandler } from './providers/sync-cron-handler'; -import { AddonNotesUserHandler } from './providers/user-handler'; -import { AddonNotesComponentsModule } from './components/components.module'; -import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreUserDelegate } from '@core/user/providers/user-delegate'; - -// List of providers (without handlers). -export const ADDON_NOTES_PROVIDERS: any[] = [ - AddonNotesProvider, - AddonNotesOfflineProvider, - AddonNotesSyncProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - AddonNotesComponentsModule - ], - providers: [ - AddonNotesProvider, - AddonNotesOfflineProvider, - AddonNotesSyncProvider, - AddonNotesCourseOptionHandler, - AddonNotesSyncCronHandler, - AddonNotesUserHandler ] -}) -export class AddonNotesModule { - constructor(courseOptionsDelegate: CoreCourseOptionsDelegate, courseOptionHandler: AddonNotesCourseOptionHandler, - userDelegate: CoreUserDelegate, userHandler: AddonNotesUserHandler, - cronDelegate: CoreCronDelegate, syncHandler: AddonNotesSyncCronHandler) { - - // Register handlers. - courseOptionsDelegate.registerHandler(courseOptionHandler); - userDelegate.registerHandler(userHandler); - cronDelegate.register(syncHandler); - } -} diff --git a/src/addon/notes/pages/add/add.html b/src/addon/notes/pages/add/add.html deleted file mode 100644 index 93f2a04a4..000000000 --- a/src/addon/notes/pages/add/add.html +++ /dev/null @@ -1,30 +0,0 @@ - - - {{ 'addon.notes.addnewnote' | translate }} - - - - - - -
- - {{ 'addon.notes.publishstate' | translate }} - - {{ 'addon.notes.personalnotes' | translate }} - {{ 'addon.notes.coursenotes' | translate }} - {{ 'addon.notes.sitenotes' | translate }} - - - - - -
- -
-
-
diff --git a/src/addon/notes/pages/add/add.module.ts b/src/addon/notes/pages/add/add.module.ts deleted file mode 100644 index bc4e43e23..000000000 --- a/src/addon/notes/pages/add/add.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { AddonNotesAddPage } from './add'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonNotesAddPage - ], - imports: [ - CoreDirectivesModule, - IonicPageModule.forChild(AddonNotesAddPage), - TranslateModule.forChild() - ] -}) -export class AddonNotesAddPageModule {} diff --git a/src/addon/notes/pages/add/add.ts b/src/addon/notes/pages/add/add.ts deleted file mode 100644 index 5a896c432..000000000 --- a/src/addon/notes/pages/add/add.ts +++ /dev/null @@ -1,89 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, ElementRef } from '@angular/core'; -import { IonicPage, ViewController, NavParams } from 'ionic-angular'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonNotesProvider } from '../../providers/notes'; - -/** - * Component that displays a text area for composing a note. - */ -@IonicPage({ segment: 'addon-notes-add' }) -@Component({ - selector: 'page-addon-notes-add', - templateUrl: 'add.html', -}) -export class AddonNotesAddPage { - - @ViewChild('itemEdit') formElement: ElementRef; - - userId: number; - courseId: number; - type = 'personal'; - text = ''; - processing = false; - - constructor(params: NavParams, - protected viewCtrl: ViewController, - protected appProvider: CoreAppProvider, - protected domUtils: CoreDomUtilsProvider, - protected notesProvider: AddonNotesProvider, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider) { - this.userId = params.get('userId'); - this.courseId = params.get('courseId'); - this.type = params.get('type') || 'personal'; - } - - /** - * Send the note or store it offline. - * - * @param e Event. - */ - addNote(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - this.appProvider.closeKeyboard(); - const loadingModal = this.domUtils.showModalLoading('core.sending', true); - // Freeze the add note button. - this.processing = true; - this.notesProvider.addNote(this.userId, this.courseId, this.type, this.text).then((sent) => { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, sent, this.sitesProvider.getCurrentSiteId()); - - this.viewCtrl.dismiss({type: this.type, sent: true}).finally(() => { - this.domUtils.showToast(sent ? 'addon.notes.eventnotecreated' : 'core.datastoredoffline', true, 3000); - }); - }).catch((error) => { - this.domUtils.showErrorModal(error); - this.processing = false; - }).finally(() => { - loadingModal.dismiss(); - }); - } - - /** - * Close modal. - */ - closeModal(): void { - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - - this.viewCtrl.dismiss({type: this.type}); - } -} diff --git a/src/addon/notes/pages/list/list.html b/src/addon/notes/pages/list/list.html deleted file mode 100644 index f88fe2201..000000000 --- a/src/addon/notes/pages/list/list.html +++ /dev/null @@ -1,7 +0,0 @@ - - - {{ 'addon.notes.notes' | translate }} - - - - diff --git a/src/addon/notes/pages/list/list.module.ts b/src/addon/notes/pages/list/list.module.ts deleted file mode 100644 index 6178c4838..000000000 --- a/src/addon/notes/pages/list/list.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonNotesListPage } from './list'; -import { AddonNotesComponentsModule } from '../../components/components.module'; - -@NgModule({ - declarations: [ - AddonNotesListPage - ], - imports: [ - CoreDirectivesModule, - AddonNotesComponentsModule, - IonicPageModule.forChild(AddonNotesListPage), - TranslateModule.forChild() - ] -}) -export class AddonNotesListPageModule {} diff --git a/src/addon/notes/pages/list/list.ts b/src/addon/notes/pages/list/list.ts deleted file mode 100644 index db6fb0703..000000000 --- a/src/addon/notes/pages/list/list.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; - -/** - * Page that displays a list of notes. - */ -@IonicPage({ segment: 'addon-notes-list-page' }) -@Component({ - selector: 'page-addon-notes-list-page', - templateUrl: 'list.html', -}) -export class AddonNotesListPage { - userId: number; - courseId: number; - - constructor(params: NavParams) { - this.userId = params.get('userId'); - this.courseId = params.get('courseId'); - } -} diff --git a/src/addon/notes/providers/course-option-handler.ts b/src/addon/notes/providers/course-option-handler.ts deleted file mode 100644 index 4bd746cdc..000000000 --- a/src/addon/notes/providers/course-option-handler.ts +++ /dev/null @@ -1,84 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { AddonNotesProvider } from './notes'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate'; -import { AddonNotesListComponent } from '../components/list/list'; - -/** - * Handler to inject an option into the course main menu. - */ -@Injectable() -export class AddonNotesCourseOptionHandler implements CoreCourseOptionsHandler { - name = 'AddonNotes'; - priority = 200; - - constructor(private notesProvider: AddonNotesProvider) { - } - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.notesProvider.isPluginEnabled(); - } - - /** - * Whether or not the handler is enabled for a certain course. - * - * @param courseId The course ID. - * @param accessData Access type and data. Default, guest, ... - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return True or promise resolved with true if enabled. - */ - isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { - if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) { - return false; // Not enabled for guests. - } - - if (navOptions && typeof navOptions.notes != 'undefined') { - return navOptions.notes; - } - - return this.notesProvider.isPluginViewNotesEnabledForCourse(courseId); - } - - /** - * Returns the data needed to render the handler. - * - * @param courseId The course ID. - * @return Data. - */ - getDisplayData?(injector: Injector, courseId: number): CoreCourseOptionsHandlerData { - return { - title: 'addon.notes.notes', - class: 'addon-notes-course-handler', - component: AddonNotesListComponent, - }; - } - - /** - * Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline. - * - * @param course The course. - * @return Promise resolved when done. - */ - prefetch(course: any): Promise { - return this.notesProvider.getNotes(course.id, undefined, true); - } -} diff --git a/src/addon/notes/providers/notes-offline.ts b/src/addon/notes/providers/notes-offline.ts deleted file mode 100644 index 828ee24aa..000000000 --- a/src/addon/notes/providers/notes-offline.ts +++ /dev/null @@ -1,329 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; - -/** - * Service to handle offline notes. - */ -@Injectable() -export class AddonNotesOfflineProvider { - protected logger; - - // Variables for database. - static NOTES_TABLE = 'addon_notes_offline_notes'; - static NOTES_DELETED_TABLE = 'addon_notes_deleted_offline_notes'; - protected siteSchema: CoreSiteSchema = { - name: 'AddonNotesOfflineProvider', - version: 2, - tables: [ - { - name: AddonNotesOfflineProvider.NOTES_TABLE, - columns: [ - { - name: 'userid', - type: 'INTEGER' - }, - { - name: 'courseid', - type: 'INTEGER' - }, - { - name: 'publishstate', - type: 'TEXT', - }, - { - name: 'content', - type: 'TEXT' - }, - { - name: 'format', - type: 'INTEGER' - }, - { - name: 'created', - type: 'INTEGER' - }, - { - name: 'lastmodified', - type: 'INTEGER' - } - ], - primaryKeys: ['userid', 'content', 'created'] - }, - { - name: AddonNotesOfflineProvider.NOTES_DELETED_TABLE, - columns: [ - { - name: 'noteid', - type: 'INTEGER', - primaryKey: true - }, - { - name: 'deleted', - type: 'INTEGER' - }, - { - name: 'courseid', - type: 'INTEGER' - } - ] - } - ] - }; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private timeUtils: CoreTimeUtilsProvider) { - this.logger = logger.getInstance('AddonNotesOfflineProvider'); - this.sitesProvider.registerSiteSchema(this.siteSchema); - } - - /** - * Delete an offline note. - * - * @param userId User ID the note is about. - * @param content The note content. - * @param timecreated The time the note was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - deleteOfflineNote(userId: number, content: string, timecreated: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonNotesOfflineProvider.NOTES_TABLE, { - userid: userId, - content: content, - created: timecreated - }); - }); - } - - /** - * Get all offline deleted notes. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with notes. - */ - getAllDeletedNotes(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonNotesOfflineProvider.NOTES_DELETED_TABLE); - }); - } - - /** - * Get course offline deleted notes. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with notes. - */ - getCourseDeletedNotes(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonNotesOfflineProvider.NOTES_DELETED_TABLE, {courseid: courseId}); - }); - } - - /** - * Get all offline notes. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with notes. - */ - getAllNotes(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonNotesOfflineProvider.NOTES_TABLE); - }); - } - - /** - * Get an offline note. - * - * @param userId User ID the note is about. - * @param content The note content. - * @param timecreated The time the note was created. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the notes. - */ - getNote(userId: number, content: string, timecreated: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecord(AddonNotesOfflineProvider.NOTES_TABLE, { - userid: userId, - content: content, - created: timecreated - }); - }); - } - - /** - * Get offline notes for a certain course and user. - * - * @param courseId Course ID. - * @param userId User ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with notes. - */ - getNotesForCourseAndUser(courseId: number, userId?: number, siteId?: string): Promise { - if (!userId) { - return this.getNotesForCourse(courseId, siteId); - } - - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonNotesOfflineProvider.NOTES_TABLE, {userid: userId, courseid: courseId}); - }); - } - - /** - * Get offline notes for a certain course. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with notes. - */ - getNotesForCourse(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonNotesOfflineProvider.NOTES_TABLE, {courseid: courseId}); - }); - } - - /** - * Get offline notes for a certain user. - * - * @param userId User ID the notes are about. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with notes. - */ - getNotesForUser(userId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonNotesOfflineProvider.NOTES_TABLE, {userid: userId}); - }); - } - - /** - * Get offline notes with a certain publish state (Personal, Site or Course). - * - * @param state Publish state ('personal', 'site' or 'course'). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with notes. - */ - getNotesWithPublishState(state: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().getRecords(AddonNotesOfflineProvider.NOTES_TABLE, {publishstate: state}); - }); - } - - /** - * Check if there are offline notes for a certain course. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline notes, false otherwise. - */ - hasNotesForCourse(courseId: number, siteId?: string): Promise { - return this.getNotesForCourse(courseId, siteId).then((notes) => { - return !!notes.length; - }); - } - - /** - * Check if there are offline notes for a certain user. - * - * @param userId User ID the notes are about. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline notes, false otherwise. - */ - hasNotesForUser(userId: number, siteId?: string): Promise { - return this.getNotesForUser(userId, siteId).then((notes) => { - return !!notes.length; - }); - } - - /** - * Check if there are offline notes with a certain publish state (Personal, Site or Course). - * - * @param state Publish state ('personal', 'site' or 'course'). - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if has offline notes, false otherwise. - */ - hasNotesWithPublishState(state: string, siteId?: string): Promise { - return this.getNotesWithPublishState(state, siteId).then((notes) => { - return !!notes.length; - }); - } - - /** - * Save a note to be sent later. - * - * @param userId User ID the note is about. - * @param courseId Course ID. - * @param state Publish state ('personal', 'site' or 'course'). - * @param content The note content. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - saveNote(userId: number, courseId: number, state: string, content: string, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const now = this.timeUtils.timestamp(); - const data = { - userid: userId, - courseid: courseId, - publishstate: state, - content: content, - format: 1, - created: now, - lastmodified: now - }; - - return site.getDb().insertRecord(AddonNotesOfflineProvider.NOTES_TABLE, data).then(() => { - return data; - }); - }); - } - - /** - * Delete a note offline to be sent later. - * - * @param noteId Note ID. - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if stored, rejected if failure. - */ - deleteNote(noteId: number, courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const now = this.timeUtils.timestamp(); - const data = { - noteid: noteId, - courseid: courseId, - deleted: now - }; - - return site.getDb().insertRecord(AddonNotesOfflineProvider.NOTES_DELETED_TABLE, data).then(() => { - return data; - }); - }); - } - - /** - * Undo delete a note. - * - * @param noteId Note ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if deleted, rejected if failure. - */ - undoDeleteNote(noteId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.getDb().deleteRecords(AddonNotesOfflineProvider.NOTES_DELETED_TABLE, { noteid: noteId }); - }); - } -} diff --git a/src/addon/notes/providers/notes-sync.ts b/src/addon/notes/providers/notes-sync.ts deleted file mode 100644 index 0ca1ec84a..000000000 --- a/src/addon/notes/providers/notes-sync.ts +++ /dev/null @@ -1,250 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncBaseProvider } from '@classes/base-sync'; -import { CoreAppProvider } from '@providers/app'; -import { AddonNotesOfflineProvider } from './notes-offline'; -import { AddonNotesProvider } from './notes'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSyncProvider } from '@providers/sync'; - -/** - * Service to sync notes. - */ -@Injectable() -export class AddonNotesSyncProvider extends CoreSyncBaseProvider { - - static AUTO_SYNCED = 'addon_notes_autom_synced'; - - constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, - syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService, - private notesOffline: AddonNotesOfflineProvider, private utils: CoreUtilsProvider, - private eventsProvider: CoreEventsProvider, private notesProvider: AddonNotesProvider, - private coursesProvider: CoreCoursesProvider, timeUtils: CoreTimeUtilsProvider) { - - super('AddonNotesSync', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils); - } - - /** - * Try to synchronize all the notes in a certain site or in all sites. - * - * @param siteId Site ID to sync. If not defined, sync all sites. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - syncAllNotes(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all notes', this.syncAllNotesFunc.bind(this), [force], siteId); - } - - /** - * Synchronize all the notes in a certain site - * - * @param siteId Site ID to sync. - * @param force Wether to force sync not depending on last execution. - * @return Promise resolved if sync is successful, rejected if sync fails. - */ - private syncAllNotesFunc(siteId: string, force: boolean): Promise { - const proms = []; - - proms.push(this.notesOffline.getAllNotes(siteId)); - proms.push(this.notesOffline.getAllDeletedNotes(siteId)); - - return Promise.all(proms).then((notesArray) => { - // Get all the courses to be synced. - const courseIds = {}; - notesArray.forEach((notes) => { - notes.forEach((note) => { - courseIds[note.courseid] = note.courseid; - }); - }); - // Sync all courses. - const promises = Object.keys(courseIds).map((courseId) => { - const cId = parseInt(courseIds[courseId], 10); - - const promise = force ? this.syncNotes(cId, siteId) : this.syncNotesIfNeeded(cId, siteId); - - return promise.then((warnings) => { - if (typeof warnings != 'undefined') { - // Sync successful, send event. - this.eventsProvider.trigger(AddonNotesSyncProvider.AUTO_SYNCED, { - courseId: courseId, - warnings: warnings - }, siteId); - } - }); - }); - - return Promise.all(promises); - }); - } - - /** - * Sync course notes only if a certain time has passed since the last time. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the notes are synced or if they don't need to be synced. - */ - private syncNotesIfNeeded(courseId: number, siteId?: string): Promise { - return this.isSyncNeeded(courseId, siteId).then((needed) => { - if (needed) { - return this.syncNotes(courseId, siteId); - } - }); - } - - /** - * Synchronize notes of a course. - * - * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved if sync is successful, rejected otherwise. - */ - syncNotes(courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (this.isSyncing(courseId, siteId)) { - // There's already a sync ongoing for notes, return the promise. - return this.getOngoingSync(courseId, siteId); - } - - this.logger.debug('Try to sync notes for course ' + courseId); - - const warnings = []; - const errors = []; - - const proms = []; - - // Get offline notes to be sent. - proms.push(this.notesOffline.getNotesForCourse(courseId, siteId).then((notes) => { - if (!notes.length) { - // Nothing to sync. - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(this.translate.instant('core.networkerrormsg')); - } - - const errors = []; - - // Format the notes to be sent. - const notesToSend = notes.map((note) => { - return { - userid: note.userid, - publishstate: note.publishstate, - courseid: note.courseid, - text: note.content, - format: note.format - }; - }); - - // Send the notes. - return this.notesProvider.addNotesOnline(notesToSend, siteId).then((response) => { - // Search errors in the response. - response.forEach((entry) => { - if (entry.noteid === -1 && errors.indexOf(entry.errormessage) == -1) { - errors.push(entry.errormessage); - } - }); - - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // It's a WebService error, this means the user cannot send notes. - errors.push(error); - } else { - // Not a WebService error, reject the synchronization to try again. - return Promise.reject(error); - } - }).then(() => { - // Notes were sent, delete them from local DB. - const promises = notes.map((note) => { - return this.notesOffline.deleteOfflineNote(note.userid, note.content, note.created, siteId); - }); - - return Promise.all(promises); - }); - })); - - // Get offline notes to be sent. - proms.push(this.notesOffline.getCourseDeletedNotes(courseId, siteId).then((notes) => { - if (!notes.length) { - // Nothing to sync. - return; - } else if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(this.translate.instant('core.networkerrormsg')); - } - - // Format the notes to be sent. - const notesToDelete = notes.map((note) => { - return note.noteid; - }); - - // Delete the notes. - return this.notesProvider.deleteNotesOnline(notesToDelete, courseId, siteId).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // It's a WebService error, this means the user cannot send notes. - errors.push(error); - } else { - // Not a WebService error, reject the synchronization to try again. - return Promise.reject(error); - } - }).then(() => { - // Notes were sent, delete them from local DB. - const promises = notes.map((noteId) => { - return this.notesOffline.undoDeleteNote(noteId, siteId); - }); - - return Promise.all(promises); - }); - })); - - const syncPromise = Promise.all(proms).then(() => { - // Fetch the notes from server to be sure they're up to date. - return this.notesProvider.invalidateNotes(courseId, undefined, siteId).then(() => { - return this.notesProvider.getNotes(courseId, undefined, false, true, siteId); - }).catch(() => { - // Ignore errors. - }); - }).then(() => { - if (errors && errors.length) { - // At least an error occurred, get course name and add errors to warnings array. - return this.coursesProvider.getUserCourse(courseId, true, siteId).catch(() => { - // Ignore errors. - return {}; - }).then((course) => { - errors.forEach((error) => { - warnings.push(this.translate.instant('addon.notes.warningnotenotsent', { - course: course.fullname ? course.fullname : courseId, - error: error - })); - }); - }); - } - }).then(() => { - // All done, return the warnings. - return warnings; - }); - - return this.addOngoingSync(courseId, syncPromise, siteId); - } -} diff --git a/src/addon/notes/providers/notes.ts b/src/addon/notes/providers/notes.ts deleted file mode 100644 index c0a2a85fb..000000000 --- a/src/addon/notes/providers/notes.ts +++ /dev/null @@ -1,474 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { AddonNotesOfflineProvider } from './notes-offline'; -import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; -import { CoreWSExternalWarning } from '@providers/ws'; - -/** - * Service to handle notes. - */ -@Injectable() -export class AddonNotesProvider { - - protected ROOT_CACHE_KEY = 'mmaNotes:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, - private utils: CoreUtilsProvider, private translate: TranslateService, private userProvider: CoreUserProvider, - private notesOffline: AddonNotesOfflineProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) { - this.logger = logger.getInstance('AddonNotesProvider'); - } - - /** - * Add a note. - * - * @param userId User ID of the person to add the note. - * @param courseId Course ID where the note belongs. - * @param publishState Personal, Site or Course. - * @param noteText The note text. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: true if note was sent to server, false if stored in device. - */ - addNote(userId: number, courseId: number, publishState: string, noteText: string, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - // Convenience function to store a note to be synchronized later. - const storeOffline = (): Promise => { - return this.notesOffline.saveNote(userId, courseId, publishState, noteText, siteId).then(() => { - return false; - }); - }; - - if (!this.appProvider.isOnline()) { - // App is offline, store the note. - return storeOffline(); - } - - // Send note to server. - return this.addNoteOnline(userId, courseId, publishState, noteText, siteId).then(() => { - return true; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // It's a WebService error, the user cannot send the message so don't store it. - return Promise.reject(error); - } - - // Error sending note, store it to retry later. - return storeOffline(); - }); - } - - /** - * Add a note. It will fail if offline or cannot connect. - * - * @param userId User ID of the person to add the note. - * @param courseId Course ID where the note belongs. - * @param publishState Personal, Site or Course. - * @param noteText The note text. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when added, rejected otherwise. - */ - addNoteOnline(userId: number, courseId: number, publishState: string, noteText: string, siteId?: string): Promise { - const notes = [ - { - courseid: courseId, - format: 1, - publishstate: publishState, - text: noteText, - userid: userId - } - ]; - - return this.addNotesOnline(notes, siteId).then((response) => { - if (response && response[0] && response[0].noteid === -1) { - // There was an error, and it should be translated already. - return Promise.reject(this.utils.createFakeWSError(response[0].errormessage)); - } - - // A note was added, invalidate the course notes. - return this.invalidateNotes(courseId, undefined, siteId).catch(() => { - // Ignore errors. - }); - }); - } - - /** - * Add several notes. It will fail if offline or cannot connect. - * - * @param notes Notes to save. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when added, rejected otherwise. Promise resolved doesn't mean that notes - * have been added, the resolve param can contain errors for notes not sent. - */ - addNotesOnline(notes: any[], siteId?: string): Promise { - if (!notes || !notes.length) { - return Promise.resolve([]); - } - - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - notes: notes - }; - - return site.write('core_notes_create_notes', data); - }); - } - - /** - * Delete a note. - * - * @param note Note object to delete. - * @param courseId Course ID where the note belongs. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when deleted, rejected otherwise. Promise resolved doesn't mean that notes - * have been deleted, the resolve param can contain errors for notes not deleted. - */ - deleteNote(note: AddonNotesNoteFormatted, courseId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (note.offline) { - return this.notesOffline.deleteOfflineNote(note.userid, note.content, note.created, siteId); - } - - // Convenience function to store the action to be synchronized later. - const storeOffline = (): Promise => { - return this.notesOffline.deleteNote(note.id, courseId, siteId).then(() => { - return false; - }); - }; - - if (!this.appProvider.isOnline()) { - // App is offline, store the note. - return storeOffline(); - } - - // Send note to server. - return this.deleteNotesOnline([note.id], courseId, siteId).then(() => { - return true; - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // It's a WebService error, the user cannot send the note so don't store it. - return Promise.reject(error); - } - - // Error sending note, store it to retry later. - return storeOffline(); - }); - } - - /** - * Delete a note. It will fail if offline or cannot connect. - * - * @param noteIds Note IDs to delete. - * @param courseId Course ID where the note belongs. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when deleted, rejected otherwise. Promise resolved doesn't mean that notes - * have been deleted, the resolve param can contain errors for notes not deleted. - */ - deleteNotesOnline(noteIds: number[], courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - notes: noteIds - }; - - return site.write('core_notes_delete_notes', data).then((response: CoreWSExternalWarning[]) => { - // A note was deleted, invalidate the course notes. - return this.invalidateNotes(courseId, undefined, siteId).catch(() => { - // Ignore errors. - }); - }); - }); - } - - /** - * Returns whether or not the notes plugin is enabled for a certain site. - * - * This method is called quite often and thus should only perform a quick - * check, we should not be calling WS from here. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if enabled, resolved with false or rejected otherwise. - */ - isPluginEnabled(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.canUseAdvancedFeature('enablenotes'); - }); - } - - /** - * Returns whether or not the add note plugin is enabled for a certain course. - * - * @param courseId ID of the course. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if enabled, resolved with false or rejected otherwise. - */ - isPluginAddNoteEnabledForCourse(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - // The only way to detect if it's enabled is to perform a WS call. - // We use an invalid user ID (-1) to avoid saving the note if the user has permissions. - const data = { - notes: [ - { - userid: -1, - publishstate: 'personal', - courseid: courseId, - text: '', - format: 1 - } - ] - }, - preSets = { - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - /* Use .read to cache data and be able to check it in offline. This means that, if a user loses the capabilities - to add notes, he'll still see the option in the app. */ - return this.utils.promiseWorks(site.read('core_notes_create_notes', data, preSets)); - }); - } - - /** - * Returns whether or not the read notes plugin is enabled for a certain course. - * - * @param courseId ID of the course. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if enabled, resolved with false or rejected otherwise. - */ - isPluginViewNotesEnabledForCourse(courseId: number, siteId?: string): Promise { - return this.utils.promiseWorks(this.getNotes(courseId, undefined, false, true, siteId)); - } - - /** - * Get prefix cache key for course notes. - * - * @param courseId ID of the course to get the notes from. - * @return Cache key. - */ - getNotesPrefixCacheKey(courseId: number): string { - return this.ROOT_CACHE_KEY + 'notes:' + courseId + ':'; - } - - /** - * Get the cache key for the get notes call. - * - * @param courseId ID of the course to get the notes from. - * @param userId ID of the user to get the notes from if requested. - * @return Cache key. - */ - getNotesCacheKey(courseId: number, userId?: number): string { - return this.getNotesPrefixCacheKey(courseId) + (userId ? userId : ''); - } - - /** - * Get users notes for a certain site, course and personal notes. - * - * @param courseId ID of the course to get the notes from. - * @param userId ID of the user to get the notes from if requested. - * @param ignoreCache True when we should not get the value from the cache. - * @param onlyOnline True to return only online notes, false to return both online and offline. - * @param siteId Site ID. If not defined, current site. - * @return Promise to be resolved when the notes are retrieved. - */ - getNotes(courseId: number, userId?: number, ignoreCache?: boolean, onlyOnline?: boolean, siteId?: string) - : Promise { - - this.logger.debug('Get notes for course ' + courseId); - - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - courseid: courseId - }; - - if (userId) { - data['userid'] = userId; - } - - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getNotesCacheKey(courseId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - return site.read('core_notes_get_course_notes', data, preSets).then((notes: AddonNotesGetCourseNotesResult) => { - if (onlyOnline) { - return notes; - } - - // Get offline notes and add them to the list. - return this.notesOffline.getNotesForCourseAndUser(courseId, userId, siteId).then((offlineNotes) => { - offlineNotes.forEach((note) => { - const fieldName = note.publishstate + 'notes'; - if (!notes[fieldName]) { - notes[fieldName] = []; - } - note.offline = true; - // Add note to the start of array since last notes are shown first. - notes[fieldName].unshift(note); - }); - - return notes; - }); - }); - }); - } - - /** - * Get offline deleted notes and set the state. - * - * @param notes Array of notes. - * @param courseId ID of the course the notes belong to. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - setOfflineDeletedNotes(notes: AddonNotesNoteFormatted[], courseId: number, siteId?: string) - : Promise { - - return this.notesOffline.getCourseDeletedNotes(courseId, siteId).then((deletedNotes) => { - notes.forEach((note) => { - note.deleted = deletedNotes.some((n) => n.noteid == note.id); - }); - - return notes; - }); - } - - /** - * Get user data for notes since they only have userid. - * - * @param notes Notes to get the data for. - * @param courseId ID of the course the notes belong to. - * @return Promise always resolved. Resolve param is the formatted notes. - */ - getNotesUserData(notes: AddonNotesNoteFormatted[], courseId: number): Promise { - const promises = notes.map((note) => { - // Get the user profile to retrieve the user image. - return this.userProvider.getProfile(note.userid, note.courseid, true).then((user) => { - note.userfullname = user.fullname; - note.userprofileimageurl = user.profileimageurl || null; - }).catch(() => { - note.userfullname = this.translate.instant('addon.notes.userwithid', {id: note.userid}); - }); - }); - - return Promise.all(promises).then(() => { - return notes; - }); - } - - /** - * Invalidate get notes WS call. - * - * @param courseId Course ID. - * @param userId User ID if needed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateNotes(courseId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - if (userId) { - return site.invalidateWsCacheForKey(this.getNotesCacheKey(courseId, userId)); - } - - return site.invalidateWsCacheForKeyStartingWith(this.getNotesPrefixCacheKey(courseId)); - }); - } - - /** - * Report notes as being viewed. - * - * @param courseId ID of the course. - * @param userId User ID if needed. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the WS call is successful. - */ - logView(courseId: number, userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - courseid: courseId, - userid: userId || 0 - }; - - this.pushNotificationsProvider.logViewListEvent('notes', 'core_notes_view_notes', params, site.getId()); - - return site.write('core_notes_view_notes', params); - }); - } -} - -/** - * Note data returned by core_notes_get_course_notes. - */ -export type AddonNotesNote = { - id: number; // Id of this note. - courseid: number; // Id of the course. - userid: number; // User id. - content: string; // The content text formated. - format: number; // Content format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - created: number; // Time created (timestamp). - lastmodified: number; // Time of last modification (timestamp). - usermodified: number; // User id of the creator of this note. - publishstate: string; // State of the note (i.e. draft, public, site). -}; - -/** - * Result of WS core_notes_get_course_notes. - */ -export type AddonNotesGetCourseNotesResult = { - sitenotes?: AddonNotesNote[]; // Site notes. - coursenotes?: AddonNotesNote[]; // Couse notes. - personalnotes?: AddonNotesNote[]; // Personal notes. - canmanagesystemnotes?: boolean; // @since 3.7. Whether the user can manage notes at system level. - canmanagecoursenotes?: boolean; // @since 3.7. Whether the user can manage notes at the given course. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Note returned by WS core_notes_create_notes. - */ -export type AddonNotesCreateNotesNote = { - clientnoteid?: string; // Your own id for the note. - noteid: number; // ID of the created note when successful, -1 when failed. - errormessage?: string; // Error message - if failed. -}; - -/** - * Result of WS core_notes_view_notes. - */ -export type AddonNotesViewNotesResult = { - status: boolean; // Status: true if success. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Notes with some calculated data. - */ -export type AddonNotesNoteFormatted = AddonNotesNote & { - offline?: boolean; // Calculated in the app. Whether it's an offline note. - deleted?: boolean; // Calculated in the app. Whether the note was deleted in offline. - userfullname?: string; // Calculated in the app. Full name of the user the note refers to. - userprofileimageurl?: string; // Calculated in the app. Avatar url of the user the note refers to. -}; diff --git a/src/addon/notes/providers/sync-cron-handler.ts b/src/addon/notes/providers/sync-cron-handler.ts deleted file mode 100644 index 25518cda3..000000000 --- a/src/addon/notes/providers/sync-cron-handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreCronHandler } from '@providers/cron'; -import { AddonNotesSyncProvider } from './notes-sync'; - -/** - * Synchronization cron handler. - */ -@Injectable() -export class AddonNotesSyncCronHandler implements CoreCronHandler { - name = 'AddonNotesSyncCronHandler'; - - constructor(private notesSync: AddonNotesSyncProvider) {} - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. - */ - execute(siteId?: string, force?: boolean): Promise { - return this.notesSync.syncAllNotes(siteId, force); - } - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return 300000; // 5 minutes. - } -} diff --git a/src/addon/notes/providers/user-handler.ts b/src/addon/notes/providers/user-handler.ts deleted file mode 100644 index 62e2eadb0..000000000 --- a/src/addon/notes/providers/user-handler.ts +++ /dev/null @@ -1,107 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreSitesProvider } from '@providers/sites'; -import { AddonNotesProvider } from './notes'; - -/** - * Profile notes handler. - */ -@Injectable() -export class AddonNotesUserHandler implements CoreUserProfileHandler { - name = 'AddonNotes:notes'; - priority = 100; - type = CoreUserDelegate.TYPE_NEW_PAGE; - noteEnabledCache = {}; - - constructor(private linkHelper: CoreContentLinksHelperProvider, private sitesProvider: CoreSitesProvider, - private notesProvider: AddonNotesProvider, eventsProvider: CoreEventsProvider) { - eventsProvider.on(CoreEventsProvider.LOGOUT, this.clearNoteCache.bind(this)); - eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, (data) => { - this.clearNoteCache(data.courseId); - }); - } - - /** - * Clear note cache. - * If a courseId is specified, it will only delete the entry for that course. - * - * @param courseId Course ID. - */ - private clearNoteCache(courseId?: number): void { - if (courseId) { - delete this.noteEnabledCache[courseId]; - } else { - this.noteEnabledCache = {}; - } - } - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return this.notesProvider.isPluginEnabled(); - } - - /** - * Check if handler is enabled for this user in this context. - * - * @param user User to check. - * @param courseId Course ID. - * @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. - * @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. - * @return Promise resolved with true if enabled, resolved with false otherwise. - */ - isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { - // Active course required. - if (!courseId || user.id == this.sitesProvider.getCurrentSiteUserId()) { - return false; - } - - if (typeof this.noteEnabledCache[courseId] != 'undefined') { - return this.noteEnabledCache[courseId]; - } - - return this.notesProvider.isPluginViewNotesEnabledForCourse(courseId).then((enabled) => { - this.noteEnabledCache[courseId] = enabled; - - return enabled; - }); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { - return { - icon: 'list', - title: 'addon.notes.notes', - class: 'addon-notes-handler', - action: (event, navCtrl, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - - this.linkHelper.goInSite(navCtrl, 'AddonNotesListPage', { userId: user.id, courseId: courseId }); - } - }; - } -} diff --git a/src/addon/notifications/components/actions/actions.ts b/src/addon/notifications/components/actions/actions.ts deleted file mode 100644 index 4ff7368f7..000000000 --- a/src/addon/notifications/components/actions/actions.ts +++ /dev/null @@ -1,88 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreContentLinksDelegate, CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; -import { CoreSitesProvider } from '@providers/sites'; - -/** - * Component that displays the actions for a notification. - */ -@Component({ - selector: 'addon-notifications-actions', - templateUrl: 'addon-notifications-actions.html', -}) -export class AddonNotificationsActionsComponent implements OnInit { - @Input() contextUrl: string; - @Input() courseId: number; - @Input() data?: any; // Extra data to handle the URL. - - actions: CoreContentLinksAction[] = []; - - constructor(private contentLinksDelegate: CoreContentLinksDelegate, private sitesProvider: CoreSitesProvider, - public navCtrl: NavController) {} - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.contextUrl && (!this.data || !this.data.appurl)) { - // No URL, nothing to do. - return; - } - - let promise; - - // Treat appurl first if any. - if (this.data && this.data.appurl) { - promise = this.contentLinksDelegate.getActionsFor(this.data.appurl, this.courseId, undefined, this.data); - } else { - promise = Promise.resolve([]); - } - - promise.then((actions) => { - if (!actions.length && this.contextUrl) { - // No appurl or cannot handle it. Try with contextUrl. - return this.contentLinksDelegate.getActionsFor(this.contextUrl, this.courseId, undefined, this.data); - } - - return actions; - }).then((actions) => { - - if (!actions.length) { - // URL is not supported. Add an action to open it in browser. - actions.push({ - message: 'core.view', - icon: 'eye', - action: this.defaultAction.bind(this) - }); - } - - this.actions = actions; - }); - } - - /** - * Default action. Open in browser. - * - * @param siteId Site ID to use. - * @param navCtrl NavController. - */ - protected defaultAction(siteId: string, navCtrl?: NavController): void { - const url = (this.data && this.data.appurl) || this.contextUrl; - - this.sitesProvider.getCurrentSite().openInBrowserWithAutoLogin(url); - } -} diff --git a/src/addon/notifications/components/actions/addon-notifications-actions.html b/src/addon/notifications/components/actions/addon-notifications-actions.html deleted file mode 100644 index 1f386603b..000000000 --- a/src/addon/notifications/components/actions/addon-notifications-actions.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/src/addon/notifications/components/components.module.ts b/src/addon/notifications/components/components.module.ts deleted file mode 100644 index 76b933080..000000000 --- a/src/addon/notifications/components/components.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonNotificationsActionsComponent } from './actions/actions'; - -@NgModule({ - declarations: [ - AddonNotificationsActionsComponent - ], - imports: [ - CommonModule, - IonicModule, - TranslateModule.forChild(), - ], - providers: [ - ], - exports: [ - AddonNotificationsActionsComponent - ], -}) -export class AddonNotificationsComponentsModule {} diff --git a/src/addon/notifications/lang/en.json b/src/addon/notifications/lang/en.json deleted file mode 100644 index 3844a5525..000000000 --- a/src/addon/notifications/lang/en.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "errorgetnotifications": "Error getting notifications.", - "markallread": "Mark all as read", - "notificationpreferences": "Notification preferences", - "notifications": "Notifications", - "playsound": "Play sound", - "therearentnotificationsyet": "There are no notifications." -} \ No newline at end of file diff --git a/src/addon/notifications/notifications.module.ts b/src/addon/notifications/notifications.module.ts deleted file mode 100644 index 863927401..000000000 --- a/src/addon/notifications/notifications.module.ts +++ /dev/null @@ -1,72 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule, NgZone } from '@angular/core'; -import { AddonNotificationsProvider } from './providers/notifications'; -import { AddonNotificationsHelperProvider } from './providers/helper'; -import { AddonNotificationsMainMenuHandler } from './providers/mainmenu-handler'; -import { AddonNotificationsSettingsHandler } from './providers/settings-handler'; -import { AddonNotificationsCronHandler } from './providers/cron-handler'; -import { AddonNotificationsPushClickHandler } from './providers/push-click-handler'; -import { CoreAppProvider } from '@providers/app'; -import { CoreInitDelegate } from '@providers/init'; -import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; -import { CoreSettingsDelegate } from '@core/settings/providers/delegate'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; - -// List of providers (without handlers). -export const ADDON_NOTIFICATIONS_PROVIDERS: any[] = [ - AddonNotificationsProvider, - AddonNotificationsHelperProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - ], - providers: [ - AddonNotificationsProvider, - AddonNotificationsHelperProvider, - AddonNotificationsMainMenuHandler, - AddonNotificationsSettingsHandler, - AddonNotificationsCronHandler, - AddonNotificationsPushClickHandler - ] -}) -export class AddonNotificationsModule { - constructor(mainMenuDelegate: CoreMainMenuDelegate, mainMenuHandler: AddonNotificationsMainMenuHandler, - settingsDelegate: CoreSettingsDelegate, settingsHandler: AddonNotificationsSettingsHandler, - cronDelegate: CoreCronDelegate, cronHandler: AddonNotificationsCronHandler, zone: NgZone, - appProvider: CoreAppProvider, localNotifications: CoreLocalNotificationsProvider, - initDelegate: CoreInitDelegate, pushNotificationsDelegate: CorePushNotificationsDelegate, - pushClickHandler: AddonNotificationsPushClickHandler) { - - mainMenuDelegate.registerHandler(mainMenuHandler); - settingsDelegate.registerHandler(settingsHandler); - cronDelegate.register(cronHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - - if (appProvider.isDesktop()) { - // Listen for clicks in simulated push notifications. - localNotifications.registerClick(AddonNotificationsProvider.PUSH_SIMULATION_COMPONENT, (notification) => { - initDelegate.ready().then(() => { - pushNotificationsDelegate.clicked(notification); - }); - }); - } - } -} diff --git a/src/addon/notifications/pages/list/list.html b/src/addon/notifications/pages/list/list.html deleted file mode 100644 index 1bb618eaa..000000000 --- a/src/addon/notifications/pages/list/list.html +++ /dev/null @@ -1,40 +0,0 @@ - - - {{ 'addon.notifications.notifications' | translate }} - - - - - - - -
- - -
- - - - -

{{ notification.subject }}

-

- {{notification.timecreated | coreDateDayOrTime}} - - -

-

{{ notification.userfromfullname }}

-
- -

-
- -
- - -
-
diff --git a/src/addon/notifications/pages/list/list.module.ts b/src/addon/notifications/pages/list/list.module.ts deleted file mode 100644 index 4aaf30d11..000000000 --- a/src/addon/notifications/pages/list/list.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonNotificationsComponentsModule } from '../../components/components.module'; -import { AddonNotificationsListPage } from './list'; - -@NgModule({ - declarations: [ - AddonNotificationsListPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonNotificationsListPage), - TranslateModule.forChild(), - AddonNotificationsComponentsModule, - ], -}) -export class AddonNotificationsListPageModule {} diff --git a/src/addon/notifications/pages/list/list.scss b/src/addon/notifications/pages/list/list.scss deleted file mode 100644 index 15f586332..000000000 --- a/src/addon/notifications/pages/list/list.scss +++ /dev/null @@ -1,62 +0,0 @@ -page-addon-notifications-list { - .core-notification-icon { - width: 34px; - height: 34px; - margin: 10px !important; - } - - .item core-format-text { - - .forumpost { - border: 1px solid $gray-light; - width: 100%; - margin: 0 0 1em 0; - - td { - padding: $content-padding; - } - - .header { - background-color: $gray-lighter; - } - - .picture { - width: auto; - text-align: center; - } - - .subject { - font-weight: 700; - margin-bottom: 1rem; - } - } - - a { - text-decoration: none; - } - - .userpicture { - border-radius: 50%; - } - - .mdl-right { - @include text-align('end'); - a { - display: none; - } - font { - font-size: 0.9em; - } - } - - .commands { - display: none; - } - - hr { - margin-top: 1.5rem; - margin-bottom: 1.5rem; - background-color: $gray-light; - } - } -} diff --git a/src/addon/notifications/pages/list/list.ts b/src/addon/notifications/pages/list/list.ts deleted file mode 100644 index 49fbdb209..000000000 --- a/src/addon/notifications/pages/list/list.ts +++ /dev/null @@ -1,252 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage, NavParams } from 'ionic-angular'; -import { Subscription } from 'rxjs'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreEventsProvider, CoreEventObserver } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonNotificationsProvider, AddonNotificationsAnyNotification } from '../../providers/notifications'; -import { AddonNotificationsHelperProvider } from '../../providers/helper'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; - -/** - * Page that displays the list of notifications. - */ -@IonicPage({ segment: 'addon-notifications-list' }) -@Component({ - selector: 'page-addon-notifications-list', - templateUrl: 'list.html', -}) -export class AddonNotificationsListPage { - - notifications: AddonNotificationsAnyNotification[] = []; - notificationsLoaded = false; - canLoadMore = false; - loadMoreError = false; - canMarkAllNotificationsAsRead = false; - loadingMarkAllNotificationsAsRead = false; - - protected isCurrentView: boolean; - protected cronObserver: CoreEventObserver; - protected pushObserver: Subscription; - protected pendingRefresh = false; - - constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider, - private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider, - private utils: CoreUtilsProvider, private notificationsProvider: AddonNotificationsProvider, - private pushNotificationsDelegate: CorePushNotificationsDelegate, - private notificationsHelper: AddonNotificationsHelperProvider) { - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchNotifications(); - - this.cronObserver = this.eventsProvider.on(AddonNotificationsProvider.READ_CRON_EVENT, () => { - if (this.isCurrentView) { - this.notificationsLoaded = false; - this.refreshNotifications(); - } - }, this.sitesProvider.getCurrentSiteId()); - - this.pushObserver = this.pushNotificationsDelegate.on('receive').subscribe((notification) => { - // New notification received. If it's from current site, refresh the data. - if (this.isCurrentView && this.utils.isTrueOrOne(notification.notif) && - this.sitesProvider.isCurrentSite(notification.site)) { - - this.notificationsLoaded = false; - this.refreshNotifications(); - } else if (!this.isCurrentView) { - this.pendingRefresh = true; - } - }); - } - - /** - * Convenience function to get notifications. Gets unread notifications first. - * - * @param refreh Whether we're refreshing data. - * @return Resolved when done. - */ - protected fetchNotifications(refresh?: boolean): Promise { - this.loadMoreError = false; - - return this.notificationsHelper.getNotifications(refresh ? [] : this.notifications).then((result) => { - result.notifications.forEach(this.formatText.bind(this)); - - if (refresh) { - this.notifications = result.notifications; - } else { - this.notifications = this.notifications.concat(result.notifications); - } - this.canLoadMore = result.canLoadMore; - - this.markNotificationsAsRead(result.notifications); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.notifications.errorgetnotifications', true); - this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading. - }).finally(() => { - this.notificationsLoaded = true; - }); - } - - /** - * Mark all notifications as read. - */ - markAllNotificationsAsRead(): void { - this.loadingMarkAllNotificationsAsRead = true; - this.notificationsProvider.markAllNotificationsAsRead().catch(() => { - // Omit failure. - }).then(() => { - const siteId = this.sitesProvider.getCurrentSiteId(); - this.eventsProvider.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, null, siteId); - - // All marked as read, refresh the list. - this.notificationsLoaded = false; - - return this.refreshNotifications(); - }); - } - - /** - * Mark notifications as read. - * - * @param notifications Array of notification objects. - */ - protected markNotificationsAsRead(notifications: AddonNotificationsAnyNotification[]): void { - - let promise; - - if (notifications.length > 0) { - const promises: Promise[] = notifications.map((notification) => { - if (notification.read) { - // Already read, don't mark it. - return Promise.resolve(); - } - - return this.notificationsProvider.markNotificationRead(notification.id); - }); - - promise = Promise.all(promises).catch(() => { - // Ignore errors. - }).finally(() => { - this.notificationsProvider.invalidateNotificationsList().finally(() => { - const siteId = this.sitesProvider.getCurrentSiteId(); - this.eventsProvider.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, null, siteId); - }); - }); - } else { - promise = Promise.resolve(); - } - - promise.finally(() => { - // Check if mark all notifications as read is enabled and there are some to read. - if (this.notificationsProvider.isMarkAllNotificationsAsReadEnabled()) { - this.loadingMarkAllNotificationsAsRead = true; - - return this.notificationsProvider.getUnreadNotificationsCount().then((unread) => { - this.canMarkAllNotificationsAsRead = unread > 0; - }).finally(() => { - this.loadingMarkAllNotificationsAsRead = false; - }); - } - this.canMarkAllNotificationsAsRead = false; - }); - } - - /** - * Refresh notifications. - * - * @param refresher Refresher. - * @return Promise Promise resolved when done. - */ - refreshNotifications(refresher?: any): Promise { - return this.notificationsProvider.invalidateNotificationsList().finally(() => { - return this.fetchNotifications(true).finally(() => { - if (refresher) { - refresher.complete(); - } - }); - }); - } - - /** - * Load more results. - * - * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. - */ - loadMoreNotifications(infiniteComplete?: any): void { - this.fetchNotifications().finally(() => { - infiniteComplete && infiniteComplete(); - }); - } - - /** - * Formats the text of a notification. - * - * @param notification The notification object. - */ - protected formatText(notification: AddonNotificationsAnyNotification): void { - notification.displayfullhtml = this.shouldDisplayFullHtml(notification); - - notification.mobiletext = notification.displayfullhtml ? - notification.fullmessagehtml : - this.textUtils.replaceNewLines(notification.mobiletext.replace(/-{4,}/ig, ''), '
'); - } - - /** - * Check whether we should display full HTML of the notification. - * - * @param notification Notification. - * @return Whether to display full HTML. - */ - protected shouldDisplayFullHtml(notification: AddonNotificationsAnyNotification): boolean { - return notification.component == 'mod_forum' && notification.eventtype == 'digests'; - } - - /** - * User entered the page. - */ - ionViewDidEnter(): void { - this.isCurrentView = true; - - if (this.pendingRefresh) { - this.pendingRefresh = false; - this.notificationsLoaded = false; - - this.refreshNotifications(); - } - } - - /** - * User left the page. - */ - ionViewDidLeave(): void { - this.isCurrentView = false; - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - this.cronObserver && this.cronObserver.off(); - this.pushObserver && this.pushObserver.unsubscribe(); - } -} diff --git a/src/addon/notifications/pages/settings/settings.html b/src/addon/notifications/pages/settings/settings.html deleted file mode 100644 index 66c94edf3..000000000 --- a/src/addon/notifications/pages/settings/settings.html +++ /dev/null @@ -1,80 +0,0 @@ - - - {{ 'addon.notifications.notifications' | translate }} - - - - - - - - - - - - - - - - - {{ 'addon.notifications.playsound' | translate }} - - - - - - - {{ 'addon.notifications.notifications' | translate }} - - - - {{ 'addon.notifications.playsound' | translate }} - - - - - -
- - {{ processor.displayname }} - -
- - - - - {{ component.displayname }} - {{ 'core.settings.loggedin' | translate }} - {{ 'core.settings.loggedoff' | translate }} - - - - - - {{ notification.displayname }} - - - - - -
{{'core.settings.locked' | translate }}
- - {{ 'core.settings.disabled' | translate }} -
-
- - {{ notification.displayname }} - - - {{ 'core.settings.' + state | translate }} - - - - {{'core.settings.locked' | translate }} - {{ 'core.settings.disabled' | translate }} - -
-
-
-
-
diff --git a/src/addon/notifications/pages/settings/settings.module.ts b/src/addon/notifications/pages/settings/settings.module.ts deleted file mode 100644 index 9a4bbb57e..000000000 --- a/src/addon/notifications/pages/settings/settings.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonNotificationsSettingsPage } from './settings'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonNotificationsSettingsPage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - IonicPageModule.forChild(AddonNotificationsSettingsPage), - TranslateModule.forChild() - ], -}) -export class AddonNotificationsSettingsPageModule {} diff --git a/src/addon/notifications/pages/settings/settings.scss b/src/addon/notifications/pages/settings/settings.scss deleted file mode 100644 index 7b132cda9..000000000 --- a/src/addon/notifications/pages/settings/settings.scss +++ /dev/null @@ -1,10 +0,0 @@ -ion-app.app-root page-addon-notifications-settings { - .list-header { - margin-bottom: 0; - border-top: 0; - } - - .toggle { - display: inline-block; - } -} diff --git a/src/addon/notifications/pages/settings/settings.ts b/src/addon/notifications/pages/settings/settings.ts deleted file mode 100644 index 0431e91c5..000000000 --- a/src/addon/notifications/pages/settings/settings.ts +++ /dev/null @@ -1,294 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy, Optional } from '@angular/core'; -import { IonicPage, NavController } from 'ionic-angular'; -import { - AddonNotificationsProvider, AddonNotificationsNotificationPreferences, AddonNotificationsNotificationPreferencesProcessor, - AddonNotificationsNotificationPreferencesComponent, AddonNotificationsNotificationPreferencesNotification, - AddonNotificationsNotificationPreferencesNotificationProcessorState -} from '../../providers/notifications'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSettingsHelper } from '@core/settings/providers/helper'; -import { AddonMessageOutputDelegate, AddonMessageOutputHandlerData } from '@addon/messageoutput/providers/delegate'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreConfigProvider } from '@providers/config'; -import { CoreAppProvider } from '@providers/app'; -import { CoreConstants } from '@core/constants'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; - -/** - * Page that displays notifications settings. - */ -@IonicPage({ segment: 'addon-notifications-settings' }) -@Component({ - selector: 'page-addon-notifications-settings', - templateUrl: 'settings.html', -}) -export class AddonNotificationsSettingsPage implements OnDestroy { - protected updateTimeout: any; - - components: AddonNotificationsNotificationPreferencesComponent[]; - preferences: AddonNotificationsNotificationPreferences; - preferencesLoaded: boolean; - currentProcessor: AddonNotificationsNotificationPreferencesProcessorFormatted; - notifPrefsEnabled: boolean; - canChangeSound: boolean; - notificationSound: boolean; - processorHandlers = []; - - constructor(private notificationsProvider: AddonNotificationsProvider, private domUtils: CoreDomUtilsProvider, - private settingsHelper: CoreSettingsHelper, private userProvider: CoreUserProvider, - private navCtrl: NavController, private messageOutputDelegate: AddonMessageOutputDelegate, - appProvider: CoreAppProvider, private configProvider: CoreConfigProvider, private eventsProvider: CoreEventsProvider, - private localNotificationsProvider: CoreLocalNotificationsProvider, private sitesProvider: CoreSitesProvider, - @Optional() private svComponent: CoreSplitViewComponent) { - - this.notifPrefsEnabled = notificationsProvider.isNotificationPreferencesEnabled(); - this.canChangeSound = localNotificationsProvider.canDisableSound(); - - if (this.canChangeSound) { - configProvider.get(CoreConstants.SETTINGS_NOTIFICATION_SOUND, true).then((enabled) => { - this.notificationSound = !!enabled; - }); - } - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - if (this.notifPrefsEnabled) { - this.fetchPreferences(); - } else { - this.preferencesLoaded = true; - } - } - - /** - * Fetches preference data. - * - * @return Resolved when done. - */ - protected fetchPreferences(): Promise { - return this.notificationsProvider.getNotificationPreferences().then((preferences) => { - if (!this.currentProcessor) { - // Initialize current processor. Load "Mobile" (airnotifier) if available. - this.currentProcessor = this.settingsHelper.getProcessor(preferences.processors, 'airnotifier'); - } - - if (!this.currentProcessor) { - // Shouldn't happen. - return Promise.reject('No processor found'); - } - - preferences.enableall = !preferences.disableall; - this.preferences = preferences; - this.loadProcessor(this.currentProcessor); - - // Get display data of message output handlers (thery are displayed in the context menu), - this.processorHandlers = []; - if (preferences.processors) { - preferences.processors.forEach((processor: AddonNotificationsNotificationPreferencesProcessorFormatted) => { - processor.supported = this.messageOutputDelegate.hasHandler(processor.name, true); - if (processor.hassettings && processor.supported) { - this.processorHandlers.push(this.messageOutputDelegate.getDisplayData(processor)); - } - }); - } - }).catch((message) => { - this.domUtils.showErrorModal(message); - }).finally(() => { - this.preferencesLoaded = true; - }); - } - - /** - * Load a processor. - * - * @param processor Processor object. - */ - protected loadProcessor(processor: AddonNotificationsNotificationPreferencesProcessorFormatted): void { - if (!processor) { - return; - } - this.currentProcessor = processor; - this.components = this.settingsHelper.getProcessorComponents(processor.name, this.preferences.components); - } - - /** - * Update preferences after a certain time. The purpose is to store the updated data, it won't be reflected in the view. - */ - protected updatePreferencesAfterDelay(): void { - // Cancel pending updates. - clearTimeout(this.updateTimeout); - - this.updateTimeout = setTimeout(() => { - this.updateTimeout = null; - this.updatePreferences(); - }, 5000); - } - - /** - * Update preferences. The purpose is to store the updated data, it won't be reflected in the view. - */ - protected updatePreferences(): void { - this.notificationsProvider.invalidateNotificationPreferences().finally(() => { - this.notificationsProvider.getNotificationPreferences(); - }); - } - - /** - * The selected processor was changed. - * - * @param name Name of the selected processor. - */ - changeProcessor(name: string): void { - this.preferences.processors.forEach((processor) => { - if (processor.name == name) { - this.loadProcessor(processor); - } - }); - } - - /** - * Refresh the list of preferences. - * - * @param refresher Refresher. - */ - refreshPreferences(refresher?: any): void { - this.notificationsProvider.invalidateNotificationPreferences().finally(() => { - this.fetchPreferences().finally(() => { - refresher && refresher.complete(); - }); - }); - } - - /** - * Open extra preferences. - * - * @param handlerData - */ - openExtraPreferences(handlerData: AddonMessageOutputHandlerData): void { - // Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav. - const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - navCtrl.push(handlerData.page, handlerData.pageParams); - } - - /** - * Change the value of a certain preference. - * - * @param notification Notification object. - * @param state State name, ['loggedin', 'loggedoff']. - */ - changePreference(notification: AddonNotificationsNotificationPreferencesNotificationFormatted, state: string): void { - const processorState: AddonNotificationsNotificationPreferencesNotificationProcessorStateFormatted = - notification.currentProcessor[state]; - const preferenceName = notification.preferencekey + '_' + processorState.name; - let value; - - notification.processors.forEach((processor) => { - if (processor[state].checked) { - if (!value) { - value = processor.name; - } else { - value += ',' + processor.name; - } - } - }); - - if (!value) { - value = 'none'; - } - - processorState.updating = true; - - this.userProvider.updateUserPreference(preferenceName, value).then(() => { - // Update the preferences since they were modified. - this.updatePreferencesAfterDelay(); - }).catch((message) => { - // Show error and revert change. - this.domUtils.showErrorModal(message); - notification.currentProcessor[state].checked = !notification.currentProcessor[state].checked; - }).finally(() => { - processorState.updating = false; - }); - } - - /** - * Enable all notifications changed. - */ - enableAll(enable: boolean): void { - const modal = this.domUtils.showModalLoading('core.sending', true); - this.userProvider.updateUserPreferences([], !enable).then(() => { - // Update the preferences since they were modified. - this.updatePreferencesAfterDelay(); - }).catch((message) => { - // Show error and revert change. - this.domUtils.showErrorModal(message); - this.preferences.enableall = !this.preferences.enableall; - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Change the notification sound setting. - * - * @param enabled True to enable the notification sound, false to disable it. - */ - changeNotificationSound(enabled: boolean): void { - this.configProvider.set(CoreConstants.SETTINGS_NOTIFICATION_SOUND, enabled ? 1 : 0).finally(() => { - const siteId = this.sitesProvider.getCurrentSiteId(); - this.eventsProvider.trigger(CoreEventsProvider.NOTIFICATION_SOUND_CHANGED, {enabled}, siteId); - this.localNotificationsProvider.rescheduleAll(); - }); - } - - /** - * Page destroyed. - */ - ngOnDestroy(): void { - // If there is a pending action to update preferences, execute it right now. - if (this.updateTimeout) { - clearTimeout(this.updateTimeout); - this.updatePreferences(); - } - } -} - -/** - * Notification preferences notification with some calculated data. - */ -type AddonNotificationsNotificationPreferencesNotificationFormatted = AddonNotificationsNotificationPreferencesNotification & { - currentProcessor?: AddonNotificationsNotificationPreferencesProcessorFormatted; // Calculated in the app. Current processor. -}; - -/** - * Notification preferences processor with some calculated data. - */ -type AddonNotificationsNotificationPreferencesProcessorFormatted = AddonNotificationsNotificationPreferencesProcessor & { - supported?: boolean; // Calculated in the app. Whether the processor is supported in the app. -}; - -/** - * State in notification processor in notification preferences component with some calculated data. - */ -type AddonNotificationsNotificationPreferencesNotificationProcessorStateFormatted = - AddonNotificationsNotificationPreferencesNotificationProcessorState & { - updating?: boolean; // Calculated in the app. Whether the state is being updated. -}; diff --git a/src/addon/notifications/providers/cron-handler.ts b/src/addon/notifications/providers/cron-handler.ts deleted file mode 100644 index cd3a5c4a1..000000000 --- a/src/addon/notifications/providers/cron-handler.ts +++ /dev/null @@ -1,117 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreCronHandler } from '@providers/cron'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper'; -import { AddonNotificationsProvider } from './notifications'; -import { AddonNotificationsHelperProvider } from './helper'; - -/** - * Notifications cron handler. - */ -@Injectable() -export class AddonNotificationsCronHandler implements CoreCronHandler { - name = 'AddonNotificationsCronHandler'; - - constructor(private appProvider: CoreAppProvider, private eventsProvider: CoreEventsProvider, - private sitesProvider: CoreSitesProvider, private localNotifications: CoreLocalNotificationsProvider, - private notificationsProvider: AddonNotificationsProvider, private textUtils: CoreTextUtilsProvider, - private emulatorHelper: CoreEmulatorHelperProvider, private notificationsHelper: AddonNotificationsHelperProvider) {} - - /** - * Get the time between consecutive executions. - * - * @return Time between consecutive executions (in ms). - */ - getInterval(): number { - return this.appProvider.isDesktop() ? 60000 : 600000; // 1 or 10 minutes. - } - - /** - * Check whether it's a synchronization process or not. True if not defined. - * - * @return Whether it's a synchronization process or not. - */ - isSync(): boolean { - // This is done to use only wifi if using the fallback function. - // In desktop it is always sync, since it fetches notification to see if there's a new one. - return !this.notificationsProvider.isPreciseNotificationCountEnabled() || this.appProvider.isDesktop(); - } - - /** - * Check whether the sync can be executed manually. Call isSync if not defined. - * - * @return Whether the sync can be executed manually. - */ - canManualSync(): boolean { - return true; - } - - /** - * Execute the process. - * Receives the ID of the site affected, undefined for all sites. - * - * @param siteId ID of the site affected, undefined for all sites. - * @param force Wether the execution is forced (manual sync). - * @return Promise resolved when done, rejected if failure. If the promise is rejected, this function - * will be called again often, it shouldn't be abused. - */ - execute(siteId?: string, force?: boolean): Promise { - if (this.sitesProvider.isCurrentSite(siteId)) { - this.eventsProvider.trigger(AddonNotificationsProvider.READ_CRON_EVENT, {}, this.sitesProvider.getCurrentSiteId()); - } - - if (this.appProvider.isDesktop() && this.localNotifications.isAvailable()) { - this.emulatorHelper.checkNewNotifications( - AddonNotificationsProvider.PUSH_SIMULATION_COMPONENT, - this.fetchNotifications.bind(this), this.getTitleAndText.bind(this), siteId); - } - - return Promise.resolve(null); - } - - /** - * Get the latest unread notifications from a site. - * - * @param siteId Site ID. - * @return Promise resolved with the notifications. - */ - protected fetchNotifications(siteId: string): Promise { - return this.notificationsHelper.getNotifications([], undefined, true, false, true, siteId).then((result) => { - return result.notifications; - }); - } - - /** - * Given a notification, return the title and the text for the notification. - * - * @param notification Notification. - * @return Promise resvoled with an object with title and text. - */ - protected getTitleAndText(notification: any): Promise { - const data = { - title: notification.subject || notification.userfromfullname, - text: notification.mobiletext.replace(/-{4,}/ig, '') - }; - data.text = this.textUtils.replaceNewLines(data.text, '
'); - - return Promise.resolve(data); - } -} diff --git a/src/addon/notifications/providers/helper.ts b/src/addon/notifications/providers/helper.ts deleted file mode 100644 index 13446781b..000000000 --- a/src/addon/notifications/providers/helper.ts +++ /dev/null @@ -1,95 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { - AddonNotificationsProvider, AddonNotificationsAnyNotification, AddonNotificationsGetMessagesMessage -} from './notifications'; - -/** - * Service that provides some helper functions for notifications. - */ -@Injectable() -export class AddonNotificationsHelperProvider { - - constructor(private notificationsProvider: AddonNotificationsProvider, private sitesProvider: CoreSitesProvider) { - } - - /** - * Get some notifications. It will try to use the new WS if available. - * - * @param notifications Current list of loaded notifications. It's used to calculate the offset. - * @param limit Number of notifications to get. Defaults to LIST_LIMIT. - * @param toDisplay True if notifications will be displayed to the user, either in view or in a notification. - * @param forceCache True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with notifications and if can load more. - */ - getNotifications(notifications: any[], limit?: number, toDisplay: boolean = true, forceCache?: boolean, ignoreCache?: boolean, - siteId?: string): Promise<{notifications: AddonNotificationsAnyNotification[], canLoadMore: boolean}> { - - notifications = notifications || []; - limit = limit || AddonNotificationsProvider.LIST_LIMIT; - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.notificationsProvider.isPopupAvailable(siteId).then((available) => { - - if (available) { - return this.notificationsProvider.getPopupNotifications(notifications.length, limit, toDisplay, forceCache, - ignoreCache, siteId); - - } else { - // Fallback to get_messages. We need 2 calls, one for read and the other one for unread. - const unreadFrom = notifications.reduce((total, current) => { - return total + (current.read ? 0 : 1); - }, 0); - - return this.notificationsProvider.getUnreadNotifications(unreadFrom, limit, toDisplay, forceCache, ignoreCache, - siteId).then((unread) => { - - let promise; - - if (unread.length < limit) { - // Limit not reached. Get read notifications until reach the limit. - const readLimit = limit - unread.length, - readFrom = notifications.length - unreadFrom; - - promise = this.notificationsProvider.getReadNotifications(readFrom, readLimit, toDisplay, forceCache, - ignoreCache, siteId).then((read) => { - return unread.concat(read); - }).catch((error): any => { - if (unread.length > 0) { - // We were able to get some unread, return only the unread ones. - return unread; - } - - return Promise.reject(error); - }); - } else { - promise = Promise.resolve(unread); - } - - return promise.then((notifications: AddonNotificationsGetMessagesMessage[]) => { - return { - notifications: notifications, - canLoadMore: notifications.length >= limit - }; - }); - }); - } - }); - } -} diff --git a/src/addon/notifications/providers/mainmenu-handler.ts b/src/addon/notifications/providers/mainmenu-handler.ts deleted file mode 100644 index be415fbb2..000000000 --- a/src/addon/notifications/providers/mainmenu-handler.ts +++ /dev/null @@ -1,115 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; -import { AddonNotificationsProvider } from './notifications'; -import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; -import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate'; - -/** - * Handler to inject an option into main menu. - */ -@Injectable() -export class AddonNotificationsMainMenuHandler implements CoreMainMenuHandler { - name = 'AddonNotifications'; - priority = 700; - - protected handler: CoreMainMenuHandlerData = { - icon: 'notifications', - title: 'addon.notifications.notifications', - page: 'AddonNotificationsListPage', - class: 'addon-notifications-handler', - showBadge: true, - badge: '', - loading: true, - }; - - constructor(eventsProvider: CoreEventsProvider, private sitesProvider: CoreSitesProvider, - utils: CoreUtilsProvider, private notificationsProvider: AddonNotificationsProvider, - private pushNotificationsProvider: CorePushNotificationsProvider, - pushNotificationsDelegate: CorePushNotificationsDelegate) { - - eventsProvider.on(AddonNotificationsProvider.READ_CHANGED_EVENT, (data) => { - this.updateBadge(data.siteId); - }); - - eventsProvider.on(AddonNotificationsProvider.READ_CRON_EVENT, (data) => { - this.updateBadge(data.siteId); - }); - - // Reset info on logout. - eventsProvider.on(CoreEventsProvider.LOGOUT, (data) => { - this.handler.badge = ''; - this.handler.loading = true; - }); - - // If a push notification is received, refresh the count. - pushNotificationsDelegate.on('receive').subscribe((notification) => { - // New notification received. If it's from current site, refresh the data. - if (utils.isTrueOrOne(notification.notif) && this.sitesProvider.isCurrentSite(notification.site)) { - this.updateBadge(notification.site); - } - }); - - // Register Badge counter. - pushNotificationsDelegate.registerCounterHandler('AddonNotifications'); - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreMainMenuHandlerData { - if (this.handler.loading) { - this.updateBadge(); - } - - return this.handler; - } - - /** - * Triggers an update for the badge number and loading status. Mandatory if showBadge is enabled. - * - * @param siteId Site ID or current Site if undefined. - */ - updateBadge(siteId?: string): void { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - if (!siteId) { - return; - } - - this.notificationsProvider.getUnreadNotificationsCount(null, siteId).then((unread) => { - this.handler.badge = unread > 0 ? String(unread) : ''; - this.pushNotificationsProvider.updateAddonCounter('AddonNotifications', unread, siteId); - }).catch(() => { - this.handler.badge = ''; - }).finally(() => { - this.handler.loading = false; - }); - } -} diff --git a/src/addon/notifications/providers/notifications.ts b/src/addon/notifications/providers/notifications.ts deleted file mode 100644 index 8a96d1f68..000000000 --- a/src/addon/notifications/providers/notifications.ts +++ /dev/null @@ -1,620 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUserProvider } from '@core/user/providers/user'; -import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper'; -import { AddonMessagesProvider, AddonMessagesMarkMessageReadResult } from '@addon/messages/providers/messages'; -import { CoreSite } from '@classes/site'; -import { CoreWSExternalWarning } from '@providers/ws'; - -/** - * Service to handle notifications. - */ -@Injectable() -export class AddonNotificationsProvider { - - static READ_CHANGED_EVENT = 'addon_notifications_read_changed_event'; - static READ_CRON_EVENT = 'addon_notifications_read_cron_event'; - static PUSH_SIMULATION_COMPONENT = 'AddonNotificationsPushSimulation'; - static LIST_LIMIT = 20; - - protected ROOT_CACHE_KEY = 'mmaNotifications:'; - protected logger; - - constructor(logger: CoreLoggerProvider, private appProvider: CoreAppProvider, private sitesProvider: CoreSitesProvider, - private timeUtils: CoreTimeUtilsProvider, private userProvider: CoreUserProvider, - private emulatorHelper: CoreEmulatorHelperProvider, private messageProvider: AddonMessagesProvider, - private textUtils: CoreTextUtilsProvider) { - this.logger = logger.getInstance('AddonNotificationsProvider'); - } - - /** - * Function to format notification data. - * - * @param notifications List of notifications. - * @param read Whether the notifications are read or unread. - * @return Promise resolved with notifications. - */ - protected formatNotificationsData(notifications: AddonNotificationsAnyNotification[], read?: boolean): Promise { - - const promises = notifications.map((notification) => { - - // Set message to show. - if (notification.component && notification.component == 'mod_forum') { - notification.mobiletext = notification.smallmessage; - } else { - notification.mobiletext = notification.fullmessage; - } - - notification.moodlecomponent = notification.component; - notification.notification = 1; - notification.notif = 1; - if (typeof read != 'undefined') { - notification.read = read; - } - - if (typeof notification.customdata == 'string') { - notification.customdata = this.textUtils.parseJSON(notification.customdata, {}); - } - - // Try to set courseid the notification belongs to. - if (notification.customdata && notification.customdata.courseid) { - notification.courseid = notification.customdata.courseid; - } else if (!notification.courseid) { - const cid = notification.fullmessagehtml.match(/course\/view\.php\?id=([^"]*)/); - if (cid && cid[1]) { - notification.courseid = parseInt(cid[1], 10); - } - } - - if (notification.useridfrom > 0) { - // Try to get the profile picture of the user. - return this.userProvider.getProfile(notification.useridfrom, notification.courseid, true).then((user) => { - notification.profileimageurlfrom = user.profileimageurl; - notification.userfromfullname = user.fullname; - - return notification; - }).catch(() => { - // Error getting user. This can happen if device is offline or the user is deleted. - }); - } - - return Promise.resolve(notification); - }); - - return Promise.all(promises); - } - - /** - * Get the cache key for the get notification preferences call. - * - * @return Cache key. - */ - protected getNotificationPreferencesCacheKey(): string { - return this.ROOT_CACHE_KEY + 'notificationPreferences'; - } - - /** - * Get notification preferences. - * - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the notification preferences. - */ - getNotificationPreferences(siteId?: string): Promise { - this.logger.debug('Get notification preferences'); - - return this.sitesProvider.getSite(siteId).then((site) => { - const preSets = { - cacheKey: this.getNotificationPreferencesCacheKey(), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - return site.read('core_message_get_user_notification_preferences', {}, preSets) - .then((data: AddonNotificationsGetUserNotificationPreferencesResult) => { - - return data.preferences; - }); - }); - } - - /** - * Get cache key for notification list WS calls. - * - * @return Cache key. - */ - protected getNotificationsCacheKey(): string { - return this.ROOT_CACHE_KEY + 'list'; - } - - /** - * Get notifications from site. - * - * @param read True if should get read notifications, false otherwise. - * @param limitFrom Position of the first notification to get. - * @param limitNumber Number of notifications to get or 0 to use the default limit. - * @param toDisplay True if notifications will be displayed to the user, either in view or in a notification. - * @param forceCache True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with notifications. - */ - getNotifications(read: boolean, limitFrom: number, limitNumber: number = 0, toDisplay: boolean = true, - forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - limitNumber = limitNumber || AddonNotificationsProvider.LIST_LIMIT; - this.logger.debug('Get ' + (read ? 'read' : 'unread') + ' notifications from ' + limitFrom + '. Limit: ' + limitNumber); - - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - useridto: site.getUserId(), - useridfrom: 0, - type: 'notifications', - read: read ? 1 : 0, - newestfirst: 1, - limitfrom: limitFrom, - limitnum: limitNumber - }; - const preSets: object = { - cacheKey: this.getNotificationsCacheKey(), - omitExpires: forceCache, - getFromCache: forceCache || !ignoreCache, - emergencyCache: forceCache || !ignoreCache, - }; - - // Get unread notifications. - return site.read('core_message_get_messages', data, preSets).then((response: AddonNotificationsGetMessagesResult) => { - if (response.messages) { - const notifications = response.messages; - - return this.formatNotificationsData(notifications, read).then(() => { - if (this.appProvider.isDesktop() && toDisplay && !read && limitFrom === 0) { - // Store the last received notification. Don't block the user for this. - this.emulatorHelper.storeLastReceivedNotification( - AddonNotificationsProvider.PUSH_SIMULATION_COMPONENT, notifications[0], siteId); - } - - return notifications; - }); - } else { - return Promise.reject(null); - } - }); - }); - } - - /** - * Get notifications from site using the new WebService. - * - * @param offset Position of the first notification to get. - * @param limit Number of notifications to get. Defaults to LIST_LIMIT. - * @param toDisplay True if notifications will be displayed to the user, either in view or in a notification. - * @param forceCache True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with notifications and if can load more. - * @since 3.2 - */ - getPopupNotifications(offset: number, limit?: number, toDisplay: boolean = true, forceCache?: boolean, ignoreCache?: boolean, - siteId?: string): Promise<{notifications: AddonNotificationsPopupNotificationFormatted[], canLoadMore: boolean}> { - - limit = limit || AddonNotificationsProvider.LIST_LIMIT; - - this.logger.debug('Get popup notifications from ' + offset + '. Limit: ' + limit); - - return this.sitesProvider.getSite(siteId).then((site) => { - const data = { - useridto: site.getUserId(), - newestfirst: 1, - offset: offset, - limit: limit + 1 // Get one more to calculate canLoadMore. - }, - preSets = { - cacheKey: this.getNotificationsCacheKey(), - omitExpires: forceCache, - getFromCache: forceCache || !ignoreCache, - emergencyCache: forceCache || !ignoreCache, - }; - - // Get notifications. - return site.read('message_popup_get_popup_notifications', data, preSets) - .then((response: AddonNotificationsGetPopupNotificationsResult) => { - - if (response.notifications) { - const result = { - canLoadMore: response.notifications.length > limit, - notifications: response.notifications.slice(0, limit) - }; - - return this.formatNotificationsData(result.notifications).then(() => { - const first = result.notifications[0]; - - if (this.appProvider.isDesktop() && toDisplay && offset === 0 && first && !first.read) { - // Store the last received notification. Don't block the user for this. - this.emulatorHelper.storeLastReceivedNotification( - AddonNotificationsProvider.PUSH_SIMULATION_COMPONENT, first, siteId); - } - - return result; - }); - } else { - return Promise.reject(null); - } - }); - }); - } - - /** - * Get read notifications from site. - * - * @param limitFrom Position of the first notification to get. - * @param limitNumber Number of notifications to get. - * @param toDisplay True if notifications will be displayed to the user, either in view or in a notification. - * @param forceCache True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with notifications. - */ - getReadNotifications(limitFrom: number, limitNumber: number, toDisplay: boolean = true, - forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.getNotifications(true, limitFrom, limitNumber, toDisplay, forceCache, ignoreCache, siteId); - } - - /** - * Get unread notifications from site. - * - * @param limitFrom Position of the first notification to get. - * @param limitNumber Number of notifications to get. - * @param toDisplay True if notifications will be displayed to the user, either in view or in a notification. - * @param forceCache True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with notifications. - */ - getUnreadNotifications(limitFrom: number, limitNumber: number, toDisplay: boolean = true, - forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.getNotifications(false, limitFrom, limitNumber, toDisplay, forceCache, ignoreCache, siteId); - } - - /** - * Get unread notifications count. Do not cache calls. - * - * @param userId The user id who received the notification. If not defined, use current user. - * @param siteId Site ID. If not defined, use current site. - * @return Promise resolved with the message notifications count. - */ - getUnreadNotificationsCount(userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - // @since 3.2 - if (site.wsAvailable('message_popup_get_unread_popup_notification_count')) { - userId = userId || site.getUserId(); - const params = { - useridto: userId - }; - const preSets = { - getFromCache: false, - emergencyCache: false, - saveToCache: false, - typeExpected: 'number' - }; - - return site.read('message_popup_get_unread_popup_notification_count', params, preSets).catch(() => { - // Return no messages if the call fails. - return 0; - }); - } - - // Fallback call. - const limit = AddonNotificationsProvider.LIST_LIMIT + 1; - - return this.getUnreadNotifications(0, limit, false, false, false, siteId).then((unread) => { - // Add + sign if there are more than the limit reachable. - return (unread.length > AddonNotificationsProvider.LIST_LIMIT) ? - AddonNotificationsProvider.LIST_LIMIT + '+' : unread.length; - }).catch(() => { - // Return no messages if the call fails. - return 0; - }); - }); - } - - /** - * Returns whether or not popup WS is available for a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with true if available, resolved with false or rejected otherwise. - */ - isPopupAvailable(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.wsAvailable('message_popup_get_popup_notifications'); - }); - } - - /** - * Mark all message notification as read. - * - * @return Resolved when done. - * @since 3.2 - */ - markAllNotificationsAsRead(): Promise { - const params = { - useridto: this.sitesProvider.getCurrentSiteUserId() - }; - - return this.sitesProvider.getCurrentSite().write('core_message_mark_all_notifications_as_read', params); - } - - /** - * Mark a single notification as read. - * - * @param notificationId ID of notification to mark as read - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - * @since 3.5 - */ - markNotificationRead(notificationId: number, siteId?: string) - : Promise { - - return this.sitesProvider.getSite(siteId).then((site) => { - - if (site.wsAvailable('core_message_mark_notification_read')) { - const params = { - notificationid: notificationId, - timeread: this.timeUtils.timestamp() - }; - - return site.write('core_message_mark_notification_read', params); - } else { - // Fallback for versions prior to 3.5. - return this.messageProvider.markMessageRead(notificationId, site.id); - } - }); - } - - /** - * Invalidate get notification preferences. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when data is invalidated. - */ - invalidateNotificationPreferences(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getNotificationPreferencesCacheKey()); - }); - } - - /** - * Invalidates notifications list WS calls. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when the list is invalidated. - */ - invalidateNotificationsList(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getNotificationsCacheKey()); - }); - } - - /** - * Returns whether or not we can mark all notifications as read. - * - * @return True if enabled, false otherwise. - * @since 3.2 - */ - isMarkAllNotificationsAsReadEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('core_message_mark_all_notifications_as_read'); - } - - /** - * Returns whether or not we can count unread notifications precisely. - * - * @return True if enabled, false otherwise. - * @since 3.2 - */ - isPreciseNotificationCountEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('message_popup_get_unread_popup_notification_count'); - } - - /** - * Returns whether or not the notification preferences are enabled for the current site. - * - * @return True if enabled, false otherwise. - * @since 3.2 - */ - isNotificationPreferencesEnabled(): boolean { - return this.sitesProvider.wsAvailableInCurrentSite('core_message_get_user_notification_preferences'); - } -} - -/** - * Preferences returned by core_message_get_user_notification_preferences. - */ -export type AddonNotificationsNotificationPreferences = { - userid: number; // User id. - disableall: number | boolean; // Whether all the preferences are disabled. - processors: AddonNotificationsNotificationPreferencesProcessor[]; // Config form values. - components: AddonNotificationsNotificationPreferencesComponent[]; // Available components. - enableall?: boolean; // Calculated in the app. Whether all the preferences are enabled. -}; - -/** - * Processor in notification preferences. - */ -export type AddonNotificationsNotificationPreferencesProcessor = { - displayname: string; // Display name. - name: string; // Processor name. - hassettings: boolean; // Whether has settings. - contextid: number; // Context id. - userconfigured: number; // Whether is configured by the user. -}; - -/** - * Component in notification preferences. - */ -export type AddonNotificationsNotificationPreferencesComponent = { - displayname: string; // Display name. - notifications: AddonNotificationsNotificationPreferencesNotification[]; // List of notificaitons for the component. -}; - -/** - * Notification processor in notification preferences component. - */ -export type AddonNotificationsNotificationPreferencesNotification = { - displayname: string; // Display name. - preferencekey: string; // Preference key. - processors: AddonNotificationsNotificationPreferencesNotificationProcessor[]; // Processors values for this notification. -}; - -/** - * Notification processor in notification preferences component. - */ -export type AddonNotificationsNotificationPreferencesNotificationProcessor = { - displayname: string; // Display name. - name: string; // Processor name. - locked: boolean; // Is locked by admin?. - lockedmessage?: string; // @since 3.6. Text to display if locked. - userconfigured: number; // Is configured?. - loggedin: AddonNotificationsNotificationPreferencesNotificationProcessorState; - loggedoff: AddonNotificationsNotificationPreferencesNotificationProcessorState; -}; - -/** - * State in notification processor in notification preferences component. - */ -export type AddonNotificationsNotificationPreferencesNotificationProcessorState = { - name: string; // Name. - displayname: string; // Display name. - checked: boolean; // Is checked?. -}; - -/** - * Result of WS core_message_get_messages. - */ -export type AddonNotificationsGetMessagesResult = { - messages: AddonNotificationsGetMessagesMessage[]; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Message data returned by core_message_get_messages. - */ -export type AddonNotificationsGetMessagesMessage = { - id: number; // Message id. - useridfrom: number; // User from id. - useridto: number; // User to id. - subject: string; // The message subject. - text: string; // The message text formated. - fullmessage: string; // The message. - fullmessageformat: number; // Fullmessage format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - fullmessagehtml: string; // The message in html. - smallmessage: string; // The shorten message. - notification: number; // Is a notification?. - contexturl: string; // Context URL. - contexturlname: string; // Context URL link name. - timecreated: number; // Time created. - timeread: number; // Time read. - usertofullname: string; // User to full name. - userfromfullname: string; // User from full name. - component?: string; // @since 3.7. The component that generated the notification. - eventtype?: string; // @since 3.7. The type of notification. - customdata?: any; // @since 3.7. Custom data to be passed to the message processor. -}; - -/** - * Message data returned by core_message_get_messages with some calculated data. - */ -export type AddonNotificationsGetMessagesMessageFormatted = - AddonNotificationsGetMessagesMessage & AddonNotificationsNotificationCalculatedData; - -/** - * Result of WS message_popup_get_popup_notifications. - */ -export type AddonNotificationsGetPopupNotificationsResult = { - notifications: AddonNotificationsPopupNotification[]; - unreadcount: number; // The number of unread message for the given user. -}; - -/** - * Notification returned by message_popup_get_popup_notifications. - */ -export type AddonNotificationsPopupNotification = { - id: number; // Notification id (this is not guaranteed to be unique within this result set). - useridfrom: number; // User from id. - useridto: number; // User to id. - subject: string; // The notification subject. - shortenedsubject: string; // The notification subject shortened with ellipsis. - text: string; // The message text formated. - fullmessage: string; // The message. - fullmessageformat: number; // Fullmessage format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). - fullmessagehtml: string; // The message in html. - smallmessage: string; // The shorten message. - contexturl: string; // Context URL. - contexturlname: string; // Context URL link name. - timecreated: number; // Time created. - timecreatedpretty: string; // Time created in a pretty format. - timeread: number; // Time read. - read: boolean; // Notification read status. - deleted: boolean; // Notification deletion status. - iconurl: string; // URL for notification icon. - component?: string; // The component that generated the notification. - eventtype?: string; // The type of notification. - customdata?: any; // @since 3.7. Custom data to be passed to the message processor. -}; - -/** - * Notification returned by message_popup_get_popup_notifications. - */ -export type AddonNotificationsPopupNotificationFormatted = - AddonNotificationsPopupNotification & AddonNotificationsNotificationCalculatedData; - -/** - * Any kind of notification that can be retrieved. - */ -export type AddonNotificationsAnyNotification = - AddonNotificationsPopupNotificationFormatted | AddonNotificationsGetMessagesMessageFormatted; - -/** - * Result of WS core_message_get_user_notification_preferences. - */ -export type AddonNotificationsGetUserNotificationPreferencesResult = { - preferences: AddonNotificationsNotificationPreferences; - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Result of WS core_message_mark_notification_read. - */ -export type AddonNotificationsMarkNotificationReadResult = { - notificationid: number; // Id of the notification. - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Calculated data for messages returned by core_message_get_messages. - */ -export type AddonNotificationsNotificationCalculatedData = { - mobiletext?: string; // Calculated in the app. Text to display for the notification. - moodlecomponent?: string; // Calculated in the app. Moodle's component. - notif?: number; // Calculated in the app. Whether it's a notification. - notification?: number; // Calculated in the app in some cases. Whether it's a notification. - read?: boolean; // Calculated in the app. Whether the notifications is read. - courseid?: number; // Calculated in the app. Course the notification belongs to. - profileimageurlfrom?: string; // Calculated in the app. Avatar of user that sent the notification. - userfromfullname?: string; // Calculated in the app in some cases. User from full name. - displayfullhtml?: boolean; // Whether to display the full HTML of the notification. -}; diff --git a/src/addon/notifications/providers/push-click-handler.ts b/src/addon/notifications/providers/push-click-handler.ts deleted file mode 100644 index b6504debd..000000000 --- a/src/addon/notifications/providers/push-click-handler.ts +++ /dev/null @@ -1,106 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreTextUtils } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate'; -import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { AddonNotificationsProvider } from './notifications'; - -/** - * Handler for non-messaging push notifications clicks. - */ -@Injectable() -export class AddonNotificationsPushClickHandler implements CorePushNotificationsClickHandler { - name = 'AddonNotificationsPushClickHandler'; - priority = 0; // Low priority so it's used as a fallback if no other handler treats the notification. - featureName = 'CoreMainMenuDelegate_AddonNotifications'; - - constructor(private utils: CoreUtilsProvider, private notificationsProvider: AddonNotificationsProvider, - private linkHelper: CoreContentLinksHelperProvider, private eventsProvider: CoreEventsProvider) {} - - /** - * Check if a notification click is handled by this handler. - * - * @param notification The notification to check. - * @return Whether the notification click is handled by this handler - */ - handles(notification: any): boolean | Promise { - if (this.utils.isTrueOrOne(notification.notif)) { - // Notification clicked, mark as read. Don't block for this. - const notifId = notification.savedmessageid || notification.id; - - this.notificationsProvider.markNotificationRead(notifId, notification.site).then(() => { - this.eventsProvider.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, null, notification.site); - }).catch(() => { - // Ignore errors. - }); - - return true; - } - - return false; - } - - /** - * Handle the notification click. - * - * @param notification The notification to check. - * @return Promise resolved when done. - */ - async handleClick(notification: any): Promise { - - if (notification.customdata.extendedtext) { - // Display the text in a modal. - return CoreTextUtils.instance.viewText(notification.title, notification.customdata.extendedtext, { - displayCopyButton: true, - modalOptions: { cssClass: 'core-modal-fullscreen' }, - }); - } - - // Try to handle the appurl. - if (notification.customdata && notification.customdata.appurl) { - switch (notification.customdata.appurlopenin) { - case 'inappbrowser': - this.utils.openInApp(notification.customdata.appurl); - - return; - - case 'browser': - return this.utils.openInBrowser(notification.customdata.appurl); - - default: - if (this.linkHelper.handleLink(notification.customdata.appurl, undefined, undefined, true)) { - // Link treated, stop. - return; - } - } - } - - // No appurl or cannot be handled by the app. Try to handle the contexturl now. - if (notification.contexturl) { - if (this.linkHelper.handleLink(notification.contexturl)) { - // Link treated, stop. - return; - } - } - - // No contexturl or cannot be handled by the app. Open the notifications page. - await this.utils.ignoreErrors(this.notificationsProvider.invalidateNotificationsList(notification.site)); - - await this.linkHelper.goInSite(undefined, 'AddonNotificationsListPage', undefined, notification.site); - } -} diff --git a/src/addon/notifications/providers/settings-handler.ts b/src/addon/notifications/providers/settings-handler.ts deleted file mode 100644 index 08339302a..000000000 --- a/src/addon/notifications/providers/settings-handler.ts +++ /dev/null @@ -1,57 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonNotificationsProvider } from './notifications'; -import { CoreAppProvider } from '@providers/app'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreSettingsHandler, CoreSettingsHandlerData } from '@core/settings/providers/delegate'; - -/** - * Notifications settings handler. - */ -@Injectable() -export class AddonNotificationsSettingsHandler implements CoreSettingsHandler { - name = 'AddonNotifications'; - priority = 500; - - constructor(private appProvider: CoreAppProvider, private localNotificationsProvider: CoreLocalNotificationsProvider, - private notificationsProvider: AddonNotificationsProvider) { - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - // Preferences or notification sound setting available. - return (this.notificationsProvider.isNotificationPreferencesEnabled() || - this.localNotificationsProvider.isAvailable() && !this.appProvider.isDesktop()); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreSettingsHandlerData { - return { - icon: 'notifications', - title: 'addon.notifications.notifications', - page: 'AddonNotificationsSettingsPage', - class: 'addon-notifications-settings-handler', - }; - } -} diff --git a/src/addon/qbehaviour/adaptive/adaptive.module.ts b/src/addon/qbehaviour/adaptive/adaptive.module.ts deleted file mode 100644 index 5779b56e4..000000000 --- a/src/addon/qbehaviour/adaptive/adaptive.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQbehaviourAdaptiveHandler } from './providers/handler'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQbehaviourAdaptiveHandler - ] -}) -export class AddonQbehaviourAdaptiveModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourAdaptiveHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/adaptive/providers/handler.ts b/src/addon/qbehaviour/adaptive/providers/handler.ts deleted file mode 100644 index fcb42e26c..000000000 --- a/src/addon/qbehaviour/adaptive/providers/handler.ts +++ /dev/null @@ -1,57 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; - -/** - * Handler to support adaptive question behaviour. - */ -@Injectable() -export class AddonQbehaviourAdaptiveHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourAdaptive'; - type = 'adaptive'; - - constructor(private questionHelper: CoreQuestionHelperProvider) { - // Nothing to do. - } - - /** - * Handle a question behaviour. - * If the behaviour requires a submit button, it should add it to question.behaviourButtons. - * If the behaviour requires to show some extra data, it should return the components to render it. - * - * @param injector Injector. - * @param question The question. - * @return Components (or promise resolved with components) to render some extra data in the question - * (e.g. certainty options). Don't return anything if no extra data is required. - */ - handleQuestion(injector: Injector, question: any): any[] | Promise { - // Just extract the button, it doesn't need any specific component. - this.questionHelper.extractQbehaviourButtons(question); - - return; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/adaptivenopenalty/adaptivenopenalty.module.ts b/src/addon/qbehaviour/adaptivenopenalty/adaptivenopenalty.module.ts deleted file mode 100644 index 66bd7364b..000000000 --- a/src/addon/qbehaviour/adaptivenopenalty/adaptivenopenalty.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQbehaviourAdaptiveNoPenaltyHandler } from './providers/handler'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQbehaviourAdaptiveNoPenaltyHandler - ] -}) -export class AddonQbehaviourAdaptiveNoPenaltyModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourAdaptiveNoPenaltyHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/adaptivenopenalty/providers/handler.ts b/src/addon/qbehaviour/adaptivenopenalty/providers/handler.ts deleted file mode 100644 index d95f0610f..000000000 --- a/src/addon/qbehaviour/adaptivenopenalty/providers/handler.ts +++ /dev/null @@ -1,57 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; - -/** - * Handler to support adaptive no penalty question behaviour. - */ -@Injectable() -export class AddonQbehaviourAdaptiveNoPenaltyHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourAdaptiveNoPenalty'; - type = 'adaptivenopenalty'; - - constructor(private questionHelper: CoreQuestionHelperProvider) { - // Nothing to do. - } - - /** - * Handle a question behaviour. - * If the behaviour requires a submit button, it should add it to question.behaviourButtons. - * If the behaviour requires to show some extra data, it should return the components to render it. - * - * @param injector Injector. - * @param question The question. - * @return Components (or promise resolved with components) to render some extra data in the question - * (e.g. certainty options). Don't return anything if no extra data is required. - */ - handleQuestion(injector: Injector, question: any): any[] | Promise { - // Just extract the button, it doesn't need any specific component. - this.questionHelper.extractQbehaviourButtons(question); - - return; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/deferredcbm/component/addon-qbehaviour-deferredcbm.html b/src/addon/qbehaviour/deferredcbm/component/addon-qbehaviour-deferredcbm.html deleted file mode 100644 index a141d0e03..000000000 --- a/src/addon/qbehaviour/deferredcbm/component/addon-qbehaviour-deferredcbm.html +++ /dev/null @@ -1,14 +0,0 @@ -
- -

{{ 'core.question.certainty' | translate }}

-
-
- - {{ option.text }} - - -
- - - -
diff --git a/src/addon/qbehaviour/deferredcbm/component/deferredcbm.ts b/src/addon/qbehaviour/deferredcbm/component/deferredcbm.ts deleted file mode 100644 index dcb4c1310..000000000 --- a/src/addon/qbehaviour/deferredcbm/component/deferredcbm.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, EventEmitter } from '@angular/core'; - -/** - * Component to render the deferred CBM in a question. - */ -@Component({ - selector: 'addon-qbehaviour-deferredcbm', - templateUrl: 'addon-qbehaviour-deferredcbm.html' -}) -export class AddonQbehaviourDeferredCBMComponent { - @Input() question: any; // The question. - @Input() component: string; // The component the question belongs to. - @Input() componentId: number; // ID of the component the question belongs to. - @Input() attemptId: number; // Attempt ID. - @Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline. - @Input() contextLevel?: string; // The context level. - @Input() contextInstanceId?: number; // The instance ID related to the context. - @Output() buttonClicked: EventEmitter; // Should emit an event when a behaviour button is clicked. - @Output() onAbort: EventEmitter; // Should emit an event if the question should be aborted. - - constructor() { - // Nothing to do. - } -} diff --git a/src/addon/qbehaviour/deferredcbm/deferredcbm.module.ts b/src/addon/qbehaviour/deferredcbm/deferredcbm.module.ts deleted file mode 100644 index 361b138c4..000000000 --- a/src/addon/qbehaviour/deferredcbm/deferredcbm.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQbehaviourDeferredCBMHandler } from './providers/handler'; -import { AddonQbehaviourDeferredCBMComponent } from './component/deferredcbm'; - -@NgModule({ - declarations: [ - AddonQbehaviourDeferredCBMComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreDirectivesModule - ], - providers: [ - AddonQbehaviourDeferredCBMHandler - ], - exports: [ - AddonQbehaviourDeferredCBMComponent - ], - entryComponents: [ - AddonQbehaviourDeferredCBMComponent - ] -}) -export class AddonQbehaviourDeferredCBMModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourDeferredCBMHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/deferredcbm/providers/handler.ts b/src/addon/qbehaviour/deferredcbm/providers/handler.ts deleted file mode 100644 index a32634c1f..000000000 --- a/src/addon/qbehaviour/deferredcbm/providers/handler.ts +++ /dev/null @@ -1,117 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreQuestionState } from '@core/question/providers/question'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { AddonQbehaviourDeferredFeedbackHandler } from '@addon/qbehaviour/deferredfeedback/providers/handler'; -import { AddonQbehaviourDeferredCBMComponent } from '../component/deferredcbm'; - -/** - * Handler to support deferred CBM question behaviour. - */ -@Injectable() -export class AddonQbehaviourDeferredCBMHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourDeferredCBM'; - type = 'deferredcbm'; - - constructor(private questionDelegate: CoreQuestionDelegate, private questionHelper: CoreQuestionHelperProvider, - private deferredFeedbackHandler: AddonQbehaviourDeferredFeedbackHandler) { - // Nothing to do. - } - - /** - * Determine a question new state based on its answer(s). - * - * @param component Component the question belongs to. - * @param attemptId Attempt ID the question belongs to. - * @param question The question. - * @param siteId Site ID. If not defined, current site. - * @return New state (or promise resolved with state). - */ - determineNewState(component: string, attemptId: number, question: any, siteId?: string) - : CoreQuestionState | Promise { - // Depends on deferredfeedback. - return this.deferredFeedbackHandler.determineNewStateDeferred(component, attemptId, question, siteId, - this.isCompleteResponse.bind(this), this.isSameResponse.bind(this)); - } - - /** - * Handle a question behaviour. - * If the behaviour requires a submit button, it should add it to question.behaviourButtons. - * If the behaviour requires to show some extra data, it should return the components to render it. - * - * @param injector Injector. - * @param question The question. - * @return Components (or promise resolved with components) to render some extra data in the question - * (e.g. certainty options). Don't return anything if no extra data is required. - */ - handleQuestion(injector: Injector, question: any): any[] | Promise { - if (this.questionHelper.extractQbehaviourCBM(question)) { - return [AddonQbehaviourDeferredCBMComponent]; - } - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - protected isCompleteResponse(question: any, answers: any): number { - // First check if the question answer is complete. - const complete = this.questionDelegate.isCompleteResponse(question, answers); - if (complete > 0) { - // Answer is complete, check the user answered CBM too. - return answers['-certainty'] ? 1 : 0; - } - - return complete; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...). - * @param newAnswers Object with the new question answers. - * @param newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...). - * @return Whether they're the same. - */ - protected isSameResponse(question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any, newBasicAnswers: any) - : boolean { - // First check if the question answer is the same. - const same = this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers); - if (same) { - // Same response, check the CBM is the same too. - return prevAnswers['-certainty'] == newAnswers['-certainty']; - } - - return same; - } -} diff --git a/src/addon/qbehaviour/deferredfeedback/deferredfeedback.module.ts b/src/addon/qbehaviour/deferredfeedback/deferredfeedback.module.ts deleted file mode 100644 index 3611bc8a0..000000000 --- a/src/addon/qbehaviour/deferredfeedback/deferredfeedback.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQbehaviourDeferredFeedbackHandler } from './providers/handler'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQbehaviourDeferredFeedbackHandler - ] -}) -export class AddonQbehaviourDeferredFeedbackModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourDeferredFeedbackHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/deferredfeedback/providers/handler.ts b/src/addon/qbehaviour/deferredfeedback/providers/handler.ts deleted file mode 100644 index 0edf35d41..000000000 --- a/src/addon/qbehaviour/deferredfeedback/providers/handler.ts +++ /dev/null @@ -1,154 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question'; - -/** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ -export type isCompleteResponseFunction = (question: any, answers: any) => number; - -/** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...). - * @param newAnswers Object with the new question answers. - * @param newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...). - * @return Whether they're the same. - */ -export type isSameResponseFunction = (question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any, - newBasicAnswers: any) => boolean; - -/** - * Handler to support deferred feedback question behaviour. - */ -@Injectable() -export class AddonQbehaviourDeferredFeedbackHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourDeferredFeedback'; - type = 'deferredfeedback'; - - constructor(private questionDelegate: CoreQuestionDelegate, private questionProvider: CoreQuestionProvider) { - // Nothing to do. - } - - /** - * Determine a question new state based on its answer(s). - * - * @param component Component the question belongs to. - * @param attemptId Attempt ID the question belongs to. - * @param question The question. - * @param siteId Site ID. If not defined, current site. - * @return New state (or promise resolved with state). - */ - determineNewState(component: string, attemptId: number, question: any, siteId?: string) - : CoreQuestionState | Promise { - return this.determineNewStateDeferred(component, attemptId, question, siteId); - } - - /** - * Determine a question new state based on its answer(s) for deferred question behaviour. - * - * @param component Component the question belongs to. - * @param attemptId Attempt ID the question belongs to. - * @param question The question. - * @param siteId Site ID. If not defined, current site. - * @param isCompleteFn Function to override the default isCompleteResponse check. - * @param isSameFn Function to override the default isSameResponse check. - * @return Promise resolved with state. - */ - determineNewStateDeferred(component: string, attemptId: number, question: any, siteId?: string, - isCompleteFn?: isCompleteResponseFunction, isSameFn?: isSameResponseFunction): Promise { - - // Check if we have local data for the question. - return this.questionProvider.getQuestion(component, attemptId, question.slot, siteId).catch(() => { - // No entry found, use the original data. - return question; - }).then((dbQuestion) => { - const state = this.questionProvider.getState(dbQuestion.state); - - if (state.finished || !state.active) { - // Question is finished, it cannot change. - return state; - } - - // We need to check if the answers have changed. Retrieve current stored answers. - return this.questionProvider.getQuestionAnswers(component, attemptId, question.slot, false, siteId) - .then((prevAnswers) => { - - const newBasicAnswers = this.questionProvider.getBasicAnswers(question.answers); - - prevAnswers = this.questionProvider.convertAnswersArrayToObject(prevAnswers, true); - const prevBasicAnswers = this.questionProvider.getBasicAnswers(prevAnswers); - - // If answers haven't changed the state is the same. - if (isSameFn) { - if (isSameFn(question, prevAnswers, prevBasicAnswers, question.answers, newBasicAnswers)) { - return state; - } - } else { - if (this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers)) { - return state; - } - } - - // Answers have changed. Now check if the response is complete and calculate the new state. - let complete: number, - newState: string; - if (isCompleteFn) { - // Pass all the answers since some behaviours might need the extra data. - complete = isCompleteFn(question, question.answers); - } else { - // Only pass the basic answers since questions should be independent of extra data. - complete = this.questionDelegate.isCompleteResponse(question, newBasicAnswers); - } - - if (complete < 0) { - newState = 'cannotdeterminestatus'; - } else if (complete > 0) { - newState = 'complete'; - } else { - const gradable = this.questionDelegate.isGradableResponse(question, newBasicAnswers); - if (gradable < 0) { - newState = 'cannotdeterminestatus'; - } else if (gradable > 0) { - newState = 'invalid'; - } else { - newState = 'todo'; - } - } - - return this.questionProvider.getState(newState); - }); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/immediatecbm/immediatecbm.module.ts b/src/addon/qbehaviour/immediatecbm/immediatecbm.module.ts deleted file mode 100644 index 7a1a33cb3..000000000 --- a/src/addon/qbehaviour/immediatecbm/immediatecbm.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQbehaviourImmediateCBMHandler } from './providers/handler'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQbehaviourImmediateCBMHandler - ] -}) -export class AddonQbehaviourImmediateCBMModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourImmediateCBMHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/immediatecbm/providers/handler.ts b/src/addon/qbehaviour/immediatecbm/providers/handler.ts deleted file mode 100644 index f4fe6649b..000000000 --- a/src/addon/qbehaviour/immediatecbm/providers/handler.ts +++ /dev/null @@ -1,60 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { AddonQbehaviourDeferredCBMComponent } from '@addon/qbehaviour/deferredcbm/component/deferredcbm'; - -/** - * Handler to support immediate CBM question behaviour. - */ -@Injectable() -export class AddonQbehaviourImmediateCBMHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourImmediateCBM'; - type = 'immediatecbm'; - - constructor(private questionHelper: CoreQuestionHelperProvider) { - // Nothing to do. - } - - /** - * Handle a question behaviour. - * If the behaviour requires a submit button, it should add it to question.behaviourButtons. - * If the behaviour requires to show some extra data, it should return the components to render it. - * - * @param injector Injector. - * @param question The question. - * @return Components (or promise resolved with components) to render some extra data in the question - * (e.g. certainty options). Don't return anything if no extra data is required. - */ - handleQuestion(injector: Injector, question: any): any[] | Promise { - // Just extract the button, it doesn't need any specific component. - this.questionHelper.extractQbehaviourButtons(question); - if (this.questionHelper.extractQbehaviourCBM(question)) { - // Depends on deferredcbm. - return [AddonQbehaviourDeferredCBMComponent]; - } - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/immediatefeedback/immediatefeedback.module.ts b/src/addon/qbehaviour/immediatefeedback/immediatefeedback.module.ts deleted file mode 100644 index 781733ffe..000000000 --- a/src/addon/qbehaviour/immediatefeedback/immediatefeedback.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQbehaviourImmediateFeedbackHandler } from './providers/handler'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQbehaviourImmediateFeedbackHandler - ] -}) -export class AddonQbehaviourImmediateFeedbackModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourImmediateFeedbackHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/immediatefeedback/providers/handler.ts b/src/addon/qbehaviour/immediatefeedback/providers/handler.ts deleted file mode 100644 index 4ec1fe768..000000000 --- a/src/addon/qbehaviour/immediatefeedback/providers/handler.ts +++ /dev/null @@ -1,57 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; - -/** - * Handler to support immediate feedback question behaviour. - */ -@Injectable() -export class AddonQbehaviourImmediateFeedbackHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourImmediateFeedback'; - type = 'immediatefeedback'; - - constructor(private questionHelper: CoreQuestionHelperProvider) { - // Nothing to do. - } - - /** - * Handle a question behaviour. - * If the behaviour requires a submit button, it should add it to question.behaviourButtons. - * If the behaviour requires to show some extra data, it should return the components to render it. - * - * @param injector Injector. - * @param question The question. - * @return Components (or promise resolved with components) to render some extra data in the question - * (e.g. certainty options). Don't return anything if no extra data is required. - */ - handleQuestion(injector: Injector, question: any): any[] | Promise { - // Just extract the button, it doesn't need any specific component. - this.questionHelper.extractQbehaviourButtons(question); - - return; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/informationitem/component/addon-qbehaviour-informationitem.html b/src/addon/qbehaviour/informationitem/component/addon-qbehaviour-informationitem.html deleted file mode 100644 index e7a0724c1..000000000 --- a/src/addon/qbehaviour/informationitem/component/addon-qbehaviour-informationitem.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/addon/qbehaviour/informationitem/component/informationitem.ts b/src/addon/qbehaviour/informationitem/component/informationitem.ts deleted file mode 100644 index aaa44569e..000000000 --- a/src/addon/qbehaviour/informationitem/component/informationitem.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, EventEmitter } from '@angular/core'; - -/** - * Component to render a "seen" hidden input for informationitem question behaviour. - */ -@Component({ - selector: 'addon-qbehaviour-informationitem', - templateUrl: 'addon-qbehaviour-informationitem.html' -}) -export class AddonQbehaviourInformationItemComponent { - @Input() question: any; // The question. - @Input() component: string; // The component the question belongs to. - @Input() componentId: number; // ID of the component the question belongs to. - @Input() attemptId: number; // Attempt ID. - @Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline. - @Input() contextLevel?: string; // The context level. - @Input() contextInstanceId?: number; // The instance ID related to the context. - @Output() buttonClicked: EventEmitter; // Should emit an event when a behaviour button is clicked. - @Output() onAbort: EventEmitter; // Should emit an event if the question should be aborted. - - constructor() { - // Nothing to do. - } -} diff --git a/src/addon/qbehaviour/informationitem/informationitem.module.ts b/src/addon/qbehaviour/informationitem/informationitem.module.ts deleted file mode 100644 index 8fcb958c2..000000000 --- a/src/addon/qbehaviour/informationitem/informationitem.module.ts +++ /dev/null @@ -1,42 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; -import { AddonQbehaviourInformationItemHandler } from './providers/handler'; -import { AddonQbehaviourInformationItemComponent } from './component/informationitem'; - -@NgModule({ - declarations: [ - AddonQbehaviourInformationItemComponent - ], - imports: [ - IonicModule - ], - providers: [ - AddonQbehaviourInformationItemHandler - ], - exports: [ - AddonQbehaviourInformationItemComponent - ], - entryComponents: [ - AddonQbehaviourInformationItemComponent - ] -}) -export class AddonQbehaviourInformationItemModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInformationItemHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/informationitem/providers/handler.ts b/src/addon/qbehaviour/informationitem/providers/handler.ts deleted file mode 100644 index c30e2c800..000000000 --- a/src/addon/qbehaviour/informationitem/providers/handler.ts +++ /dev/null @@ -1,74 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { AddonQbehaviourInformationItemComponent } from '../component/informationitem'; - -/** - * Handler to support information item question behaviour. - */ -@Injectable() -export class AddonQbehaviourInformationItemHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourInformationItem'; - type = 'informationitem'; - - constructor(private questionHelper: CoreQuestionHelperProvider, private questionProvider: CoreQuestionProvider) { } - - /** - * Determine a question new state based on its answer(s). - * - * @param component Component the question belongs to. - * @param attemptId Attempt ID the question belongs to. - * @param question The question. - * @param siteId Site ID. If not defined, current site. - * @return New state (or promise resolved with state). - */ - determineNewState(component: string, attemptId: number, question: any, siteId?: string) - : CoreQuestionState | Promise { - if (question.answers['-seen']) { - return this.questionProvider.getState('complete'); - } - - return this.questionProvider.getState(question.state || 'todo'); - } - - /** - * Handle a question behaviour. - * If the behaviour requires a submit button, it should add it to question.behaviourButtons. - * If the behaviour requires to show some extra data, it should return the components to render it. - * - * @param injector Injector. - * @param question The question. - * @return Components (or promise resolved with components) to render some extra data in the question - * (e.g. certainty options). Don't return anything if no extra data is required. - */ - handleQuestion(injector: Injector, question: any): any[] | Promise { - if (this.questionHelper.extractQbehaviourSeenInput(question)) { - return [AddonQbehaviourInformationItemComponent]; - } - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/interactive/interactive.module.ts b/src/addon/qbehaviour/interactive/interactive.module.ts deleted file mode 100644 index f9bef6f89..000000000 --- a/src/addon/qbehaviour/interactive/interactive.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQbehaviourInteractiveHandler } from './providers/handler'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQbehaviourInteractiveHandler - ] -}) -export class AddonQbehaviourInteractiveModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInteractiveHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/interactive/providers/handler.ts b/src/addon/qbehaviour/interactive/providers/handler.ts deleted file mode 100644 index cf65a2d52..000000000 --- a/src/addon/qbehaviour/interactive/providers/handler.ts +++ /dev/null @@ -1,57 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; - -/** - * Handler to support interactive question behaviour. - */ -@Injectable() -export class AddonQbehaviourInteractiveHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourInteractive'; - type = 'interactive'; - - constructor(private questionHelper: CoreQuestionHelperProvider) { - // Nothing to do. - } - - /** - * Handle a question behaviour. - * If the behaviour requires a submit button, it should add it to question.behaviourButtons. - * If the behaviour requires to show some extra data, it should return the components to render it. - * - * @param injector Injector. - * @param question The question. - * @return Components (or promise resolved with components) to render some extra data in the question - * (e.g. certainty options). Don't return anything if no extra data is required. - */ - handleQuestion(injector: Injector, question: any): any[] | Promise { - // Just extract the button, it doesn't need any specific component. - this.questionHelper.extractQbehaviourButtons(question); - - return; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/interactivecountback/interactivecountback.module.ts b/src/addon/qbehaviour/interactivecountback/interactivecountback.module.ts deleted file mode 100644 index d32d71246..000000000 --- a/src/addon/qbehaviour/interactivecountback/interactivecountback.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQbehaviourInteractiveCountbackHandler } from './providers/handler'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQbehaviourInteractiveCountbackHandler - ] -}) -export class AddonQbehaviourInteractiveCountbackModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInteractiveCountbackHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/interactivecountback/providers/handler.ts b/src/addon/qbehaviour/interactivecountback/providers/handler.ts deleted file mode 100644 index f8fa1d97e..000000000 --- a/src/addon/qbehaviour/interactivecountback/providers/handler.ts +++ /dev/null @@ -1,57 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; - -/** - * Handler to support interactive countback question behaviour. - */ -@Injectable() -export class AddonQbehaviourInteractiveCountbackHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourInteractiveCountback'; - type = 'interactivecountback'; - - constructor(private questionHelper: CoreQuestionHelperProvider) { - // Nothing to do. - } - - /** - * Handle a question behaviour. - * If the behaviour requires a submit button, it should add it to question.behaviourButtons. - * If the behaviour requires to show some extra data, it should return the components to render it. - * - * @param injector Injector. - * @param question The question. - * @return Components (or promise resolved with components) to render some extra data in the question - * (e.g. certainty options). Don't return anything if no extra data is required. - */ - handleQuestion(injector: Injector, question: any): any[] | Promise { - // Just extract the button, it doesn't need any specific component. - this.questionHelper.extractQbehaviourButtons(question); - - return; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/manualgraded/manualgraded.module.ts b/src/addon/qbehaviour/manualgraded/manualgraded.module.ts deleted file mode 100644 index eac6922b9..000000000 --- a/src/addon/qbehaviour/manualgraded/manualgraded.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQbehaviourManualGradedHandler } from './providers/handler'; -import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQbehaviourManualGradedHandler - ] -}) -export class AddonQbehaviourManualGradedModule { - constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourManualGradedHandler) { - behaviourDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qbehaviour/manualgraded/providers/handler.ts b/src/addon/qbehaviour/manualgraded/providers/handler.ts deleted file mode 100644 index 98da9c06d..000000000 --- a/src/addon/qbehaviour/manualgraded/providers/handler.ts +++ /dev/null @@ -1,147 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question'; - -/** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ -export type isCompleteResponseFunction = (question: any, answers: any) => number; - -/** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...). - * @param newAnswers Object with the new question answers. - * @param newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...). - * @return Whether they're the same. - */ -export type isSameResponseFunction = (question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any, - newBasicAnswers: any) => boolean; - -/** - * Handler to support manual graded question behaviour. - */ -@Injectable() -export class AddonQbehaviourManualGradedHandler implements CoreQuestionBehaviourHandler { - name = 'AddonQbehaviourManualGraded'; - type = 'manualgraded'; - - constructor(private questionDelegate: CoreQuestionDelegate, private questionProvider: CoreQuestionProvider) { - // Nothing to do. - } - - /** - * Determine a question new state based on its answer(s). - * - * @param component Component the question belongs to. - * @param attemptId Attempt ID the question belongs to. - * @param question The question. - * @param siteId Site ID. If not defined, current site. - * @return New state (or promise resolved with state). - */ - determineNewState(component: string, attemptId: number, question: any, siteId?: string) - : CoreQuestionState | Promise { - return this.determineNewStateManualGraded(component, attemptId, question, siteId); - } - - /** - * Determine a question new state based on its answer(s) for manual graded question behaviour. - * - * @param component Component the question belongs to. - * @param attemptId Attempt ID the question belongs to. - * @param question The question. - * @param siteId Site ID. If not defined, current site. - * @param isCompleteFn Function to override the default isCompleteResponse check. - * @param isSameFn Function to override the default isSameResponse check. - * @return Promise resolved with state. - */ - determineNewStateManualGraded(component: string, attemptId: number, question: any, siteId?: string, - isCompleteFn?: isCompleteResponseFunction, isSameFn?: isSameResponseFunction): Promise { - - // Check if we have local data for the question. - return this.questionProvider.getQuestion(component, attemptId, question.slot, siteId).catch(() => { - // No entry found, use the original data. - return question; - }).then((dbQuestion) => { - const state = this.questionProvider.getState(dbQuestion.state); - - if (state.finished || !state.active) { - // Question is finished, it cannot change. - return state; - } - - // We need to check if the answers have changed. Retrieve current stored answers. - return this.questionProvider.getQuestionAnswers(component, attemptId, question.slot, false, siteId) - .then((prevAnswers) => { - - const newBasicAnswers = this.questionProvider.getBasicAnswers(question.answers); - - prevAnswers = this.questionProvider.convertAnswersArrayToObject(prevAnswers, true); - const prevBasicAnswers = this.questionProvider.getBasicAnswers(prevAnswers); - - // If answers haven't changed the state is the same. - if (isSameFn) { - if (isSameFn(question, prevAnswers, prevBasicAnswers, question.answers, newBasicAnswers)) { - return state; - } - } else { - if (this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers)) { - return state; - } - } - - // Answers have changed. Now check if the response is complete and calculate the new state. - let complete: number, - newState: string; - if (isCompleteFn) { - // Pass all the answers since some behaviours might need the extra data. - complete = isCompleteFn(question, question.answers); - } else { - // Only pass the basic answers since questions should be independent of extra data. - complete = this.questionDelegate.isCompleteResponse(question, newBasicAnswers); - } - - if (complete < 0) { - newState = 'cannotdeterminestatus'; - } else if (complete > 0) { - newState = 'complete'; - } else { - newState = 'todo'; - } - - return this.questionProvider.getState(newState); - }); - }); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } -} diff --git a/src/addon/qbehaviour/qbehaviour.module.ts b/src/addon/qbehaviour/qbehaviour.module.ts deleted file mode 100644 index d2bcdbba8..000000000 --- a/src/addon/qbehaviour/qbehaviour.module.ts +++ /dev/null @@ -1,44 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { AddonQbehaviourAdaptiveModule } from './adaptive/adaptive.module'; -import { AddonQbehaviourAdaptiveNoPenaltyModule } from './adaptivenopenalty/adaptivenopenalty.module'; -import { AddonQbehaviourDeferredCBMModule } from './deferredcbm/deferredcbm.module'; -import { AddonQbehaviourDeferredFeedbackModule } from './deferredfeedback/deferredfeedback.module'; -import { AddonQbehaviourImmediateCBMModule } from './immediatecbm/immediatecbm.module'; -import { AddonQbehaviourImmediateFeedbackModule } from './immediatefeedback/immediatefeedback.module'; -import { AddonQbehaviourInformationItemModule } from './informationitem/informationitem.module'; -import { AddonQbehaviourInteractiveModule } from './interactive/interactive.module'; -import { AddonQbehaviourInteractiveCountbackModule } from './interactivecountback/interactivecountback.module'; -import { AddonQbehaviourManualGradedModule } from './manualgraded/manualgraded.module'; - -@NgModule({ - declarations: [], - imports: [ - AddonQbehaviourAdaptiveModule, - AddonQbehaviourAdaptiveNoPenaltyModule, - AddonQbehaviourDeferredCBMModule, - AddonQbehaviourDeferredFeedbackModule, - AddonQbehaviourImmediateCBMModule, - AddonQbehaviourImmediateFeedbackModule, - AddonQbehaviourInformationItemModule, - AddonQbehaviourInteractiveModule, - AddonQbehaviourInteractiveCountbackModule, - AddonQbehaviourManualGradedModule - ], - providers: [ - ], - exports: [] -}) -export class AddonQbehaviourModule { } diff --git a/src/addon/qtype/calculated/calculated.module.ts b/src/addon/qtype/calculated/calculated.module.ts deleted file mode 100644 index 08bda73d1..000000000 --- a/src/addon/qtype/calculated/calculated.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeCalculatedHandler } from './providers/handler'; -import { AddonQtypeCalculatedComponent } from './component/calculated'; - -@NgModule({ - declarations: [ - AddonQtypeCalculatedComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonQtypeCalculatedHandler - ], - exports: [ - AddonQtypeCalculatedComponent - ], - entryComponents: [ - AddonQtypeCalculatedComponent - ] -}) -export class AddonQtypeCalculatedModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeCalculatedHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/calculated/component/addon-qtype-calculated.html b/src/addon/qtype/calculated/component/addon-qtype-calculated.html deleted file mode 100644 index e0096a827..000000000 --- a/src/addon/qtype/calculated/component/addon-qtype-calculated.html +++ /dev/null @@ -1,65 +0,0 @@ -
- -

-
- - - - - - - - {{ 'addon.mod_quiz.answercolon' | translate }} - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - {{option.label}} - - - - - - - - - -
- - {{ option.text }} - - - - - -
-
diff --git a/src/addon/qtype/calculated/component/calculated.scss b/src/addon/qtype/calculated/component/calculated.scss deleted file mode 100644 index eb53fdb3d..000000000 --- a/src/addon/qtype/calculated/component/calculated.scss +++ /dev/null @@ -1,5 +0,0 @@ -ion-app.app-root addon-qtype-calculated { - ion-col .select-disabled { - @include margin(0, 20px, 0, 0); - } -} \ No newline at end of file diff --git a/src/addon/qtype/calculated/component/calculated.ts b/src/addon/qtype/calculated/component/calculated.ts deleted file mode 100644 index 946526117..000000000 --- a/src/addon/qtype/calculated/component/calculated.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; - -/** - * Component to render a calculated question. - */ -@Component({ - selector: 'addon-qtype-calculated', - templateUrl: 'addon-qtype-calculated.html' -}) -export class AddonQtypeCalculatedComponent extends CoreQuestionBaseComponent implements OnInit { - - constructor(logger: CoreLoggerProvider, injector: Injector) { - super(logger, 'AddonQtypeCalculatedComponent', injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.initCalculatedComponent(); - } -} diff --git a/src/addon/qtype/calculated/providers/handler.ts b/src/addon/qtype/calculated/providers/handler.ts deleted file mode 100644 index c7832f91c..000000000 --- a/src/addon/qtype/calculated/providers/handler.ts +++ /dev/null @@ -1,158 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeCalculatedComponent } from '../component/calculated'; - -/** - * Handler to support calculated question type. - */ -@Injectable() -export class AddonQtypeCalculatedHandler implements CoreQuestionHandler { - name = 'AddonQtypeCalculated'; - type = 'qtype_calculated'; - - constructor(private utils: CoreUtilsProvider, private domUtils: CoreDomUtilsProvider) { } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeCalculatedComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - if (this.isGradableResponse(question, answers) === 0 || !this.validateUnits(answers['answer'])) { - return 0; - } - - if (this.requiresUnits(question)) { - return this.isValidValue(answers['unit']) ? 1 : 0; - } - - return -1; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - let isGradable = this.isValidValue(answers['answer']); - if (isGradable && this.requiresUnits(question)) { - // The question requires a unit. - isGradable = this.isValidValue(answers['unit']); - } - - return isGradable ? 1 : 0; - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer') && - this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'unit'); - } - - /** - * Check if a value is valid (not empty). - * - * @param value Value to check. - * @return Whether the value is valid. - */ - isValidValue(value: string | number): boolean { - return !!value || value === '0' || value === 0; - } - - /** - * Check if a question requires units in a separate input. - * - * @param question The question. - * @return Whether the question requires units. - */ - requiresUnits(question: any): boolean { - const element = this.domUtils.convertToElement(question.html); - - return !!(element.querySelector('select[name*=unit]') || element.querySelector('input[type="radio"]')); - } - - /** - * Validate a number with units. We don't have the list of valid units and conversions, so we can't perform - * a full validation. If this function returns true it means we can't be sure it's valid. - * - * @param answer Answer. - * @return False if answer isn't valid, true if we aren't sure if it's valid. - */ - validateUnits(answer: string): boolean { - if (!answer) { - return false; - } - - const regexString = '[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[-+]?\\d+)?'; - - // Strip spaces (which may be thousands separators) and change other forms of writing e to e. - answer = answer.replace(' ', ''); - answer = answer.replace(/(?:e|E|(?:x|\*|×)10(?:\^|\*\*))([+-]?\d+)/, 'e$1'); - - // If a '.' is present or there are multiple ',' (i.e. 2,456,789) assume ',' is a thousands separator and stip it. - // Else assume it is a decimal separator, and change it to '.'. - if (answer.indexOf('.') != -1 || answer.split(',').length - 1 > 1) { - answer = answer.replace(',', ''); - } else { - answer = answer.replace(',', '.'); - } - - // We don't know if units should be before or after so we check both. - if (answer.match(new RegExp('^' + regexString)) === null || answer.match(new RegExp(regexString + '$')) === null) { - return false; - } - - return true; - } -} diff --git a/src/addon/qtype/calculatedmulti/calculatedmulti.module.ts b/src/addon/qtype/calculatedmulti/calculatedmulti.module.ts deleted file mode 100644 index ad1357191..000000000 --- a/src/addon/qtype/calculatedmulti/calculatedmulti.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { AddonQtypeCalculatedMultiHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQtypeCalculatedMultiHandler - ] -}) -export class AddonQtypeCalculatedMultiModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeCalculatedMultiHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/calculatedmulti/providers/handler.ts b/src/addon/qtype/calculatedmulti/providers/handler.ts deleted file mode 100644 index 63fcd7a3e..000000000 --- a/src/addon/qtype/calculatedmulti/providers/handler.ts +++ /dev/null @@ -1,90 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeMultichoiceHandler } from '@addon/qtype/multichoice/providers/handler'; -import { AddonQtypeMultichoiceComponent } from '@addon/qtype/multichoice/component/multichoice'; - -/** - * Handler to support calculated multi question type. - */ -@Injectable() -export class AddonQtypeCalculatedMultiHandler implements CoreQuestionHandler { - name = 'AddonQtypeCalculatedMulti'; - type = 'qtype_calculatedmulti'; - - constructor(private multichoiceHandler: AddonQtypeMultichoiceHandler) { } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - // Calculated multi behaves like a multichoice, use the same component. - return AddonQtypeMultichoiceComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - // This question type depends on multichoice. - return this.multichoiceHandler.isCompleteResponseSingle(answers); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - // This question type depends on multichoice. - return this.multichoiceHandler.isGradableResponseSingle(answers); - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - // This question type depends on multichoice. - return this.multichoiceHandler.isSameResponseSingle(prevAnswers, newAnswers); - } -} diff --git a/src/addon/qtype/calculatedsimple/calculatedsimple.module.ts b/src/addon/qtype/calculatedsimple/calculatedsimple.module.ts deleted file mode 100644 index 8f7850e1d..000000000 --- a/src/addon/qtype/calculatedsimple/calculatedsimple.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { AddonQtypeCalculatedSimpleHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQtypeCalculatedSimpleHandler - ], -}) -export class AddonQtypeCalculatedSimpleModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeCalculatedSimpleHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/calculatedsimple/providers/handler.ts b/src/addon/qtype/calculatedsimple/providers/handler.ts deleted file mode 100644 index e5f442b20..000000000 --- a/src/addon/qtype/calculatedsimple/providers/handler.ts +++ /dev/null @@ -1,90 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeCalculatedHandler } from '@addon/qtype/calculated/providers/handler'; -import { AddonQtypeCalculatedComponent } from '@addon/qtype/calculated/component/calculated'; - -/** - * Handler to support calculated simple question type. - */ -@Injectable() -export class AddonQtypeCalculatedSimpleHandler implements CoreQuestionHandler { - name = 'AddonQtypeCalculatedSimple'; - type = 'qtype_calculatedsimple'; - - constructor(private calculatedHandler: AddonQtypeCalculatedHandler) { } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - // Calculated simple behaves like a calculated, use the same component. - return AddonQtypeCalculatedComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - // This question type depends on calculated. - return this.calculatedHandler.isCompleteResponse(question, answers); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - // This question type depends on calculated. - return this.calculatedHandler.isGradableResponse(question, answers); - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - // This question type depends on calculated. - return this.calculatedHandler.isSameResponse(question, prevAnswers, newAnswers); - } -} diff --git a/src/addon/qtype/ddimageortext/classes/ddimageortext.ts b/src/addon/qtype/ddimageortext/classes/ddimageortext.ts deleted file mode 100644 index 8e303a71b..000000000 --- a/src/addon/qtype/ddimageortext/classes/ddimageortext.ts +++ /dev/null @@ -1,797 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Encapsulates operations on dd area. - */ -export interface AddonQtypeDdImageOrTextQuestionDocStructure { - topNode?: () => HTMLElement; - dragItemsArea?: () => HTMLElement; - dragItems?: () => HTMLElement[]; - dropZones?: () => HTMLElement[]; - dropZoneGroup?: (groupNo: number) => HTMLElement[]; - dragItemsClonedFrom?: (dragItemNo: number) => HTMLElement[]; - dragItem?: (dragInstanceNo: number) => HTMLElement; - dragItemsInGroup?: (groupNo: number) => HTMLElement[]; - dragItemHomes?: () => HTMLElement[]; - bgImg?: () => HTMLImageElement; - dragItemHome?: (dragItemNo: number) => HTMLElement; - getClassnameNumericSuffix?: (node: HTMLElement, prefix: string) => number; - cloneNewDragItem?: (dragInstanceNo: number, dragItemNo: number) => HTMLElement; -} - -/** - * Class to make a question of ddimageortext type work. - */ -export class AddonQtypeDdImageOrTextQuestion { - protected logger: any; - protected toLoad = 0; - protected doc: AddonQtypeDdImageOrTextQuestionDocStructure; - protected afterImageLoadDone = false; - protected topNode: HTMLElement; - protected proportion = 1; - protected selected: HTMLElement; // Selected element (being "dragged"). - protected resizeFunction; - - /** - * Create the this. - * - * @param logger Logger provider. - * @param domUtils Dom Utils provider. - * @param container The container HTMLElement of the question. - * @param question The question this. - * @param readOnly Whether it's read only. - * @param drops The drop zones received in the init object of the question. - */ - constructor(logger: CoreLoggerProvider, protected domUtils: CoreDomUtilsProvider, protected container: HTMLElement, - protected question: any, protected readOnly: boolean, protected drops: any[]) { - this.logger = logger.getInstance('AddonQtypeDdImageOrTextQuestion'); - - this.initializer(question); - } - - /** - * Calculate image proportion to make easy conversions. - */ - calculateImgProportion(): void { - const bgImg = this.doc.bgImg(); - - // Render the position related to the current image dimensions. - this.proportion = 1; - if (bgImg.width != bgImg.naturalWidth) { - this.proportion = bgImg.width / bgImg.naturalWidth; - } - } - - /** - * Convert the X and Y position of the BG IMG to a position relative to the window. - * - * @param bgImgXY X and Y of the BG IMG relative position. - * @return Position relative to the window. - */ - convertToWindowXY(bgImgXY: number[]): number[] { - const bgImg = this.doc.bgImg(), - position = this.domUtils.getElementXY(bgImg, null, 'ddarea'); - - // Render the position related to the current image dimensions. - bgImgXY[0] *= this.proportion; - bgImgXY[1] *= this.proportion; - - return [Number(bgImgXY[0]) + position[0] + 1, Number(bgImgXY[1]) + position[1] + 1]; - } - - /** - * Create and initialize all draggable elements and drop zones. - */ - createAllDragAndDrops(): void { - // Initialize drop zones. - this.initDrops(); - - // Initialize drag items area. - const dragItemsArea = this.doc.dragItemsArea(); - dragItemsArea.classList.add('clearfix'); - this.makeDragAreaClickable(); - - const dragItemHomes = this.doc.dragItemHomes(); - let i = 0; - - // Create the draggable items. - for (let x = 0; x < dragItemHomes.length; x++) { - - const dragItemHome = dragItemHomes[x], - dragItemNo = this.doc.getClassnameNumericSuffix(dragItemHome, 'dragitemhomes'), - choice = this.doc.getClassnameNumericSuffix(dragItemHome, 'choice'), - group = this.doc.getClassnameNumericSuffix(dragItemHome, 'group'); - - // Images need to be inside a div element to admit padding with width and height. - if (dragItemHome.tagName == 'IMG') { - const wrap = document.createElement('div'); - wrap.className = dragItemHome.className; - dragItemHome.className = ''; - - // Insert wrapper before the image in the DOM tree. - dragItemHome.parentNode.insertBefore(wrap, dragItemHome); - // Move the image into wrapper. - wrap.appendChild(dragItemHome); - } - - // Create a new drag item for this home. - const dragNode = this.doc.cloneNewDragItem(i, dragItemNo); - i++; - - // Make the item draggable. - this.draggableForQuestion(dragNode, group, choice); - - // If the draggable item needs to be created more than once, create the rest of copies. - if (dragNode.classList.contains('infinite')) { - const groupSize = this.doc.dropZoneGroup(group).length; - let dragsToCreate = groupSize - 1; - - while (dragsToCreate > 0) { - const newDragNode = this.doc.cloneNewDragItem(i, dragItemNo); - i++; - this.draggableForQuestion(newDragNode, group, choice); - - dragsToCreate--; - } - } - } - - // All drag items have been created, position them. - this.repositionDragsForQuestion(); - - if (!this.readOnly) { - const dropZones = this.doc.dropZones(); - dropZones.forEach((dropZone) => { - dropZone.setAttribute('tabIndex', '0'); - }); - } - } - - /** - * Deselect all drags. - */ - deselectDrags(): void { - const drags = this.doc.dragItems(); - - drags.forEach((drag) => { - drag.classList.remove('beingdragged'); - }); - - this.selected = null; - } - - /** - * Function to call when the instance is no longer needed. - */ - destroy(): void { - this.stopPolling(); - - if (this.resizeFunction) { - window.removeEventListener('resize', this.resizeFunction); - } - } - - /** - * Returns an object to encapsulate operations on dd area. - * - * @param slot The question slot. - * @return The object. - */ - docStructure(slot: number): AddonQtypeDdImageOrTextQuestionDocStructure { - const topNode = this.container.querySelector('.addon-qtype-ddimageortext-container'), - doc: AddonQtypeDdImageOrTextQuestionDocStructure = {}; - - let dragItemsArea = topNode.querySelector('div.draghomes'); - - if (dragItemsArea) { - // On 3.9+ dragitems were removed. - const dragItems = topNode.querySelector('div.dragitems'); - - if (dragItems) { - // Remove empty div.dragitems. - dragItems.remove(); - } - - // 3.6+ site, transform HTML so it has the same structure as in Moodle 3.5. - const ddArea = topNode.querySelector('div.ddarea'); - - // Move div.dropzones to div.ddarea. - ddArea.appendChild(topNode.querySelector('div.dropzones')); - - // Move div.draghomes to div.ddarea and rename the class to .dragitems. - ddArea.appendChild(dragItemsArea); - dragItemsArea.classList.remove('draghomes'); - dragItemsArea.classList.add('dragitems'); - - // Add .dragitemhomesNNN class to drag items. - Array.from(dragItemsArea.querySelectorAll('.draghome')).forEach((draghome, index) => { - draghome.classList.add('dragitemhomes' + index); - }); - } else { - dragItemsArea = topNode.querySelector('div.dragitems'); - } - - doc.topNode = (): HTMLElement => { - return topNode; - }; - doc.dragItemsArea = (): HTMLElement => { - return dragItemsArea; - }; - doc.dragItems = (): HTMLElement[] => { - return Array.from(dragItemsArea.querySelectorAll('.drag')); - }; - doc.dropZones = (): HTMLElement[] => { - return Array.from(topNode.querySelectorAll('div.dropzones div.dropzone')); - }; - doc.dropZoneGroup = (groupNo: number): HTMLElement[] => { - return Array.from(topNode.querySelectorAll('div.dropzones div.group' + groupNo)); - }; - doc.dragItemsClonedFrom = (dragItemNo: number): HTMLElement[] => { - return Array.from(dragItemsArea.querySelectorAll('.dragitems' + dragItemNo)); - }; - doc.dragItem = (dragInstanceNo: number): HTMLElement => { - return dragItemsArea.querySelector('.draginstance' + dragInstanceNo); - }; - doc.dragItemsInGroup = (groupNo: number): HTMLElement[] => { - return Array.from(dragItemsArea.querySelectorAll('.drag.group' + groupNo)); - }; - doc.dragItemHomes = (): HTMLElement[] => { - return Array.from(dragItemsArea.querySelectorAll('.draghome')); - }; - doc.bgImg = (): HTMLImageElement => { - return topNode.querySelector('.dropbackground'); - }; - doc.dragItemHome = (dragItemNo: number): HTMLElement => { - return dragItemsArea.querySelector('.dragitemhomes' + dragItemNo); - }; - doc.getClassnameNumericSuffix = (node: HTMLElement, prefix: string): number => { - - if (node.classList && node.classList.length) { - const patt1 = new RegExp('^' + prefix + '([0-9])+$'), - patt2 = new RegExp('([0-9])+$'); - - for (let index = 0; index < node.classList.length; index++) { - if (patt1.test(node.classList[index])) { - const match = patt2.exec(node.classList[index]); - - return Number(match[0]); - } - } - } - - this.logger.warn('Prefix "' + prefix + '" not found in class names.'); - }; - doc.cloneNewDragItem = (dragInstanceNo: number, dragItemNo: number): HTMLElement => { - const dragHome = doc.dragItemHome(dragItemNo); - if (dragHome === null) { - return null; - } - - const dragHomeImg = dragHome.querySelector('img'); - let divDrag: HTMLElement; - - // Images need to be inside a div element to admit padding with width and height. - if (dragHomeImg) { - // Clone the image. - const drag = dragHomeImg.cloneNode(true); - - // Create a div and put the image in it. - divDrag = document.createElement('div'); - divDrag.appendChild(drag); - divDrag.className = dragHome.className; - drag.className = ''; - } else { - // The drag item doesn't have an image, just clone it. - divDrag = dragHome.cloneNode(true); - } - - // Set the right classes and styles. - divDrag.classList.remove('dragitemhomes' + dragItemNo); - divDrag.classList.remove('draghome'); - divDrag.classList.add('dragitems' + dragItemNo); - divDrag.classList.add('draginstance' + dragInstanceNo); - divDrag.classList.add('drag'); - - divDrag.style.visibility = 'inherit'; - divDrag.style.position = 'absolute'; - divDrag.setAttribute('draginstanceno', String(dragInstanceNo)); - divDrag.setAttribute('dragitemno', String(dragItemNo)); - - // Insert the new drag after the dragHome. - dragHome.parentElement.insertBefore(divDrag, dragHome.nextSibling); - - return divDrag; - }; - - return doc; - } - - /** - * Make an element draggable. - * - * @param drag Element to make draggable. - * @param group Group the element belongs to. - * @param choice Choice the element belongs to. - */ - draggableForQuestion(drag: HTMLElement, group: number, choice: number): void { - // Set attributes. - drag.setAttribute('group', String(group)); - drag.setAttribute('choice', String(choice)); - - if (!this.readOnly) { - // Listen to click events. - drag.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - if (drag.classList.contains('beingdragged')) { - this.deselectDrags(); - } else { - this.selectDrag(drag); - } - }); - } - } - - /** - * Function called when a drop zone is clicked. - * - * @param dropNode Drop element. - */ - dropClick(dropNode: HTMLElement): void { - const drag = this.selected; - if (!drag) { - // No selected item, nothing to do. - return; - } - - // Deselect the drag and place it in the position of this drop zone if it belongs to the same group. - this.deselectDrags(); - - if (Number(dropNode.getAttribute('group')) === Number(drag.getAttribute('group'))) { - this.placeDragInDrop(drag, dropNode); - } - } - - /** - * Get all the draggable elements for a choice and a drop zone. - * - * @param choice Choice number. - * @param drop Drop zone. - * @return Draggable elements. - */ - getChoicesForDrop(choice: number, drop: HTMLElement): HTMLElement[] { - return Array.from(this.doc.topNode().querySelectorAll( - 'div.dragitemgroup' + drop.getAttribute('group') + ' .choice' + choice + '.drag')); - } - - /** - * Get an unplaced draggable element that belongs to a certain choice and drop zone. - * - * @param choice Choice number. - * @param drop Drop zone. - * @return Unplaced draggable element. - */ - getUnplacedChoiceForDrop(choice: number, drop: HTMLElement): HTMLElement { - const dragItems = this.getChoicesForDrop(choice, drop); - - return dragItems.find((dragItem) => { - return (!dragItem.classList.contains('placed') && !dragItem.classList.contains('beingdragged')); - }) || null; - } - - /** - * Initialize drop zones. - */ - initDrops(): void { - const dropAreas = this.doc.topNode().querySelector('div.dropzones'), - groupNodes = {}; - - // Create all group nodes and add them to the drop area. - for (let groupNo = 1; groupNo <= 8; groupNo++) { - const groupNode = document.createElement('div'); - groupNode.className = 'dropzonegroup' + groupNo; - - dropAreas.appendChild(groupNode); - groupNodes[groupNo] = groupNode; - } - - // Create the drops specified by the init object. - for (const dropNo in this.drops) { - const drop = this.drops[dropNo], - nodeClass = 'dropzone group' + drop.group + ' place' + dropNo, - title = drop.text.replace('"', '\"'), - dropNode = document.createElement('div'); - - dropNode.setAttribute('title', title); - dropNode.className = nodeClass; - - groupNodes[drop.group].appendChild(dropNode); - dropNode.style.opacity = '0.5'; - dropNode.setAttribute('xy', drop.xy); - dropNode.setAttribute('aria-label', drop.text); - dropNode.setAttribute('place', dropNo); - dropNode.setAttribute('inputid', drop.fieldname.replace(':', '_')); - dropNode.setAttribute('group', drop.group); - - dropNode.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - this.dropClick(dropNode); - }); - } - } - - /** - * Initialize the question. - * - * @param question Question. - */ - initializer(question: any): void { - this.doc = this.docStructure(question.slot); - - if (this.readOnly) { - const container = this.doc.topNode(); - container.classList.add('readonly'); - } - - // Wait the DOM to be rendered. - setTimeout(() => { - const bgImg = this.doc.bgImg(); - - // Wait for background image to be loaded. - // On iOS, complete is mistakenly true, check also naturalWidth for compatibility. - if (!bgImg.complete || !bgImg.naturalWidth) { - this.toLoad++; - bgImg.addEventListener('load', () => { - this.toLoad--; - }); - } - - const itemHomes = this.doc.dragItemHomes(); - itemHomes.forEach((item) => { - if (item.tagName == 'IMG') { - // Wait for drag images to be loaded. - // On iOS, complete is mistakenly true, check also naturalWidth for compatibility. - const itemImg = item; - - if (!itemImg.complete || !itemImg.naturalWidth) { - this.toLoad++; - itemImg.addEventListener('load', () => { - this.toLoad--; - }); - } - } - }); - - this.pollForImageLoad(); - }); - - this.resizeFunction = this.repositionDragsForQuestion.bind(this); - window.addEventListener('resize', this.resizeFunction); - } - - /** - * Make the drag items area clickable. - */ - makeDragAreaClickable(): void { - if (this.readOnly) { - return; - } - - const home = this.doc.dragItemsArea(); - home.addEventListener('click', (e) => { - const drag = this.selected; - if (!drag) { - // No element selected, nothing to do. - return false; - } - - // An element was selected. Deselect it and move it back to the area if needed. - this.deselectDrags(); - this.removeDragFromDrop(drag); - - e.preventDefault(); - e.stopPropagation(); - }); - } - - /** - * Place a draggable element into a certain drop zone. - * - * @param drag Draggable element. - * @param drop Drop zone element. - */ - placeDragInDrop(drag: HTMLElement, drop: HTMLElement): void { - // Search the input related to the drop zone. - const targetInputId = drop.getAttribute('inputid'), - inputNode = this.doc.topNode().querySelector('input#' + targetInputId); - - // Check if the draggable item is already assigned to an input and if it's the same as the one of the drop zone. - const originInputId = drag.getAttribute('inputid'); - if (originInputId && originInputId != targetInputId) { - // Remove it from the previous place. - const originInputNode = this.doc.topNode().querySelector('input#' + originInputId); - originInputNode.setAttribute('value', '0'); - } - - // Now position the draggable and set it to the input. - const position = this.domUtils.getElementXY(drop, null, 'ddarea'); - drag.style.left = position[0] - 1 + 'px'; - drag.style.top = position[1] - 1 + 'px'; - drag.classList.add('placed'); - - if (drag.getAttribute('choice')) { - inputNode.setAttribute('value', drag.getAttribute('choice')); - } - - drag.setAttribute('inputid', targetInputId); - } - - /** - * Wait for images to be loaded. - */ - pollForImageLoad(): void { - if (this.afterImageLoadDone) { - // Already done, stop. - return; - } - - if (this.toLoad <= 0) { - // All images loaded. - this.createAllDragAndDrops(); - this.afterImageLoadDone = true; - this.question.loaded = true; - } - - // Try again after a while. - setTimeout(() => { - this.pollForImageLoad(); - }, 1000); - } - - /** - * Remove a draggable element from the drop zone where it is. - * - * @param drag Draggable element to remove. - */ - removeDragFromDrop(drag: HTMLElement): void { - // Check if the draggable element is assigned to an input. If so, empty the input's value. - const inputId = drag.getAttribute('inputid'); - if (inputId) { - const inputNode = this.doc.topNode().querySelector('input#' + inputId); - inputNode.setAttribute('value', '0'); - } - - // Move the element to its original position. - const dragItemHome = this.doc.dragItemHome(Number(drag.getAttribute('dragitemno'))), - position = this.domUtils.getElementXY(dragItemHome, null, 'ddarea'); - - drag.style.left = position[0] + 'px'; - drag.style.top = position[1] + 'px'; - drag.classList.remove('placed'); - - drag.setAttribute('inputid', ''); - } - - /** - * Reposition all the draggable elements and drop zones. - */ - repositionDragsForQuestion(): void { - const dragItems = this.doc.dragItems(); - - // Mark all draggable items as "unplaced", they will be placed again later. - dragItems.forEach((dragItem) => { - dragItem.classList.remove('placed'); - dragItem.setAttribute('inputid', ''); - }); - - // Calculate the proportion to apply to images. - this.calculateImgProportion(); - - // Apply the proportion to all images in drag item homes. - const dragItemHomes = this.doc.dragItemHomes(); - for (let x = 0; x < dragItemHomes.length; x++) { - const dragItemHome = dragItemHomes[x], - dragItemHomeImg = dragItemHome.querySelector('img'); - - if (dragItemHomeImg && dragItemHomeImg.naturalWidth > 0) { - const widthHeight = [Math.round(dragItemHomeImg.naturalWidth * this.proportion), - Math.round(dragItemHomeImg.naturalHeight * this.proportion)]; - - dragItemHomeImg.style.width = widthHeight[0] + 'px'; - dragItemHomeImg.style.height = widthHeight[1] + 'px'; - - // Apply the proportion to all the images cloned from this home. - const dragItemNo = this.doc.getClassnameNumericSuffix(dragItemHome, 'dragitemhomes'), - groupNo = this.doc.getClassnameNumericSuffix(dragItemHome, 'group'), - dragsImg = Array.from(this.doc.topNode().querySelectorAll( - '.drag.group' + groupNo + '.dragitems' + dragItemNo + ' img')); - - dragsImg.forEach((dragImg) => { - dragImg.style.width = widthHeight[0] + 'px'; - dragImg.style.height = widthHeight[1] + 'px'; - }); - } - } - - // Update the padding of all draggable elements. - this.updatePaddingSizesAll(); - - const dropZones = this.doc.dropZones(); - for (let x = 0; x < dropZones.length; x++) { - // Re-position the drop zone based on the proportion. - const dropZone = dropZones[x], - dropZoneXY = dropZone.getAttribute('xy').split(',').map((i) => { - return Number(i); - }), - relativeXY = this.convertToWindowXY(dropZoneXY); - - dropZone.style.left = relativeXY[0] + 'px'; - dropZone.style.top = relativeXY[1] + 'px'; - - // Re-place items got from the inputs. - const inputCss = 'input#' + dropZone.getAttribute('inputid'), - input = this.doc.topNode().querySelector(inputCss), - choice = Number(input.value); - - if (choice > 0) { - const dragItem = this.getUnplacedChoiceForDrop(choice, dropZone); - - if (dragItem !== null) { - this.placeDragInDrop(dragItem, dropZone); - } - } - } - - // Re-place draggable items not placed drop zones (they will be placed in the original position). - for (let x = 0; x < dragItems.length; x++) { - const dragItem = dragItems[x]; - if (!dragItem.classList.contains('placed') && !dragItem.classList.contains('beingdragged')) { - this.removeDragFromDrop(dragItem); - } - } - } - - /** - * Mark a draggable element as selected. - * - * @param drag Element to select. - */ - selectDrag(drag: HTMLElement): void { - // Deselect previous ones. - this.deselectDrags(); - - this.selected = drag; - drag.classList.add('beingdragged'); - } - - /** - * Stop waiting for images to be loaded. - */ - stopPolling(): void { - this.afterImageLoadDone = true; - } - - /** - * Update the padding of all items in a group to make them all have the same width and height. - * - * @param groupNo The group number. - */ - updatePaddingSizeForGroup(groupNo: number): void { - - // Get all the items for this group. - const groupItems = Array.from(this.doc.topNode().querySelectorAll('.draghome.group' + groupNo)); - if (groupItems.length !== 0) { - - // Get the max width and height of the items. - let maxWidth = 0, - maxHeight = 0; - - for (let x = 0; x < groupItems.length; x++) { - // Check if the item has an img. - const item = groupItems[x], - img = item.querySelector('img'); - - if (img) { - maxWidth = Math.max(maxWidth, Math.round(this.proportion * img.naturalWidth)); - maxHeight = Math.max(maxHeight, Math.round(this.proportion * img.naturalHeight)); - } else { - // Remove the padding to calculate the size. - const originalPadding = item.style.padding; - item.style.padding = ''; - - // Text is not affected by the proportion. - maxWidth = Math.max(maxWidth, Math.round(item.clientWidth)); - maxHeight = Math.max(maxHeight, Math.round(item.clientHeight)); - - // Restore the padding. - item.style.padding = originalPadding; - } - } - - if (maxWidth <= 0 || maxHeight <= 0) { - return; - } - - // Add a variable padding to the image or text. - maxWidth = Math.round(maxWidth + this.proportion * 8); - maxHeight = Math.round(maxHeight + this.proportion * 8); - - for (let x = 0; x < groupItems.length; x++) { - // Check if the item has an img and calculate its width and height. - const item = groupItems[x], - img = item.querySelector('img'); - let width, height; - - if (img) { - width = Math.round(img.naturalWidth * this.proportion); - height = Math.round(img.naturalHeight * this.proportion); - } else { - // Remove the padding to calculate the size. - const originalPadding = item.style.padding; - item.style.padding = ''; - - // Text is not affected by the proportion. - width = Math.round(item.clientWidth); - height = Math.round(item.clientHeight); - - // Restore the padding. - item.style.padding = originalPadding; - } - - // Now set the right padding to make this item have the max height and width. - const marginTopBottom = Math.round((maxHeight - height) / 2), - marginLeftRight = Math.round((maxWidth - width) / 2); - - // Correction for the roundings. - const widthCorrection = maxWidth - (width + marginLeftRight * 2), - heightCorrection = maxHeight - (height + marginTopBottom * 2); - - item.style.padding = marginTopBottom + 'px ' + marginLeftRight + 'px ' + - (marginTopBottom + heightCorrection) + 'px ' + (marginLeftRight + widthCorrection) + 'px'; - - const dragItemNo = this.doc.getClassnameNumericSuffix(item, 'dragitemhomes'), - drags = Array.from(this.doc.topNode().querySelectorAll( - '.drag.group' + groupNo + '.dragitems' + dragItemNo)); - - drags.forEach((drag) => { - drag.style.padding = marginTopBottom + 'px ' + marginLeftRight + 'px ' + - (marginTopBottom + heightCorrection) + 'px ' + (marginLeftRight + widthCorrection) + 'px'; - }); - } - - // It adds the border of 1px to the width. - const zoneGroups = this.doc.dropZoneGroup(groupNo); - zoneGroups.forEach((zone) => { - zone.style.width = maxWidth + 2 + 'px '; - zone.style.height = maxHeight + 2 + 'px '; - }); - } - } - - /** - * Update the padding of all items in all groups. - */ - updatePaddingSizesAll(): void { - for (let groupNo = 1; groupNo <= 8; groupNo++) { - this.updatePaddingSizeForGroup(groupNo); - } - } -} diff --git a/src/addon/qtype/ddimageortext/component/addon-qtype-ddimageortext.html b/src/addon/qtype/ddimageortext/component/addon-qtype-ddimageortext.html deleted file mode 100644 index 817b1db52..000000000 --- a/src/addon/qtype/ddimageortext/component/addon-qtype-ddimageortext.html +++ /dev/null @@ -1,13 +0,0 @@ -
- - - - -

- - {{ 'core.question.howtodraganddrop' | translate }} -

-

- -
-
diff --git a/src/addon/qtype/ddimageortext/component/ddimageortext.scss b/src/addon/qtype/ddimageortext/component/ddimageortext.scss deleted file mode 100644 index 2bd859fed..000000000 --- a/src/addon/qtype/ddimageortext/component/ddimageortext.scss +++ /dev/null @@ -1,106 +0,0 @@ -// Style ddimageortext content a bit. Almost all these styles are copied from Moodle. -addon-qtype-ddimageortext { - .qtext { - margin-bottom: 0.5em; - display: block; - } - - div.droparea img { - border: 1px solid $gray-darker; - max-width: 100%; - } - - .draghome { - vertical-align: top; - margin: 5px; - visibility : hidden; - } - .draghome img { - display: block; - } - - div.draghome { - border: 1px solid $gray-darker; - cursor: pointer; - background-color: #B0C4DE; - display:inline-block; - height: auto; - width: auto; - zoom: 1; - } - - @for $i from 0 to length($core-dd-question-colors) { - .group#{$i + 1} { - background: nth($core-dd-question-colors, $i + 1); - } - } - - .group2 { - border-radius: 10px 0 0 0; - } - .group3 { - border-radius: 0 10px 0 0; - } - .group4 { - border-radius: 0 0 10px 0; - } - .group5 { - border-radius: 0 0 0 10px; - } - .group6 { - border-radius: 0 10px 10px 0; - } - .group7 { - border-radius: 10px 0 0 10px; - } - .group8 { - border-radius: 10px 10px 10px 10px; - } - - .drag { - border: 1px solid $gray-darker; - color: $text-color; - cursor: pointer; - z-index: 2; - } - .dragitems.readonly .drag { - cursor: auto; - } - .dragitems>div { - clear: both; - } - .dragitems { - cursor: pointer; - } - .dragitems.readonly { - cursor: auto; - } - .drag img { - display: block; - } - - div.ddarea { - text-align : center; - position: relative; - } - .dropbackground { - margin:0 auto; - } - .dropzone { - border: 1px solid $gray-darker; - position: absolute; - z-index: 1; - cursor: pointer; - } - .readonly .dropzone { - cursor: auto; - } - - div.dragitems div.draghome, div.dragitems div.drag { - font:13px/1.231 arial,helvetica,clean,sans-serif; - } - .drag.beingdragged { - z-index: 3; - box-shadow: $core-dd-question-selected-shadow; - } -} diff --git a/src/addon/qtype/ddimageortext/component/ddimageortext.ts b/src/addon/qtype/ddimageortext/component/ddimageortext.ts deleted file mode 100644 index 1a752bdae..000000000 --- a/src/addon/qtype/ddimageortext/component/ddimageortext.ts +++ /dev/null @@ -1,127 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, Injector, ElementRef } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; -import { AddonQtypeDdImageOrTextQuestion } from '../classes/ddimageortext'; - -/** - * Component to render a drag-and-drop onto image question. - */ -@Component({ - selector: 'addon-qtype-ddimageortext', - templateUrl: 'addon-qtype-ddimageortext.html' -}) -export class AddonQtypeDdImageOrTextComponent extends CoreQuestionBaseComponent implements OnInit, OnDestroy { - - protected element: HTMLElement; - protected questionInstance: AddonQtypeDdImageOrTextQuestion; - protected drops: any[]; // The drop zones received in the init object of the question. - protected destroyed = false; - protected textIsRendered = false; - protected ddAreaisRendered = false; - - constructor(protected loggerProvider: CoreLoggerProvider, injector: Injector, element: ElementRef) { - super(loggerProvider, 'AddonQtypeDdImageOrTextComponent', injector); - - this.element = element.nativeElement; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.question) { - this.logger.warn('Aborting because of no question received.'); - - return this.questionHelper.showComponentError(this.onAbort); - } - - const element = this.domUtils.convertToElement(this.question.html); - - // Get D&D area and question text. - const ddArea = element.querySelector('.ddarea'); - - this.question.text = this.domUtils.getContentsOfElement(element, '.qtext'); - if (!ddArea || typeof this.question.text == 'undefined') { - this.logger.warn('Aborting because of an error parsing question.', this.question.name); - - return this.questionHelper.showComponentError(this.onAbort); - } - - // Set the D&D area HTML. - this.question.ddArea = ddArea.outerHTML; - this.question.readOnly = false; - - if (this.question.initObjects) { - // Moodle version <= 3.5. - if (typeof this.question.initObjects.drops != 'undefined') { - this.drops = this.question.initObjects.drops; - } - if (typeof this.question.initObjects.readonly != 'undefined') { - this.question.readOnly = this.question.initObjects.readonly; - } - } else if (this.question.amdArgs) { - // Moodle version >= 3.6. - if (typeof this.question.amdArgs[1] != 'undefined') { - this.question.readOnly = this.question.amdArgs[1]; - } - if (typeof this.question.amdArgs[2] != 'undefined') { - this.drops = this.question.amdArgs[2]; - } - } - - this.question.loaded = false; - } - - /** - * The question ddArea has been rendered. - */ - ddAreaRendered(): void { - this.ddAreaisRendered = true; - if (this.textIsRendered) { - this.questionRendered(); - } - } - - /** - * The question text has been rendered. - */ - textRendered(): void { - this.textIsRendered = true; - if (this.ddAreaisRendered) { - this.questionRendered(); - } - } - - /** - * The question has been rendered. - */ - protected questionRendered(): void { - if (!this.destroyed) { - // Create the instance. - this.questionInstance = new AddonQtypeDdImageOrTextQuestion(this.loggerProvider, this.domUtils, this.element, - this.question, this.question.readOnly, this.drops); - } - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.destroyed = true; - this.questionInstance && this.questionInstance.destroy(); - } -} diff --git a/src/addon/qtype/ddimageortext/ddimageortext.module.ts b/src/addon/qtype/ddimageortext/ddimageortext.module.ts deleted file mode 100644 index 25507b299..000000000 --- a/src/addon/qtype/ddimageortext/ddimageortext.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeDdImageOrTextHandler } from './providers/handler'; -import { AddonQtypeDdImageOrTextComponent } from './component/ddimageortext'; - -@NgModule({ - declarations: [ - AddonQtypeDdImageOrTextComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonQtypeDdImageOrTextHandler - ], - exports: [ - AddonQtypeDdImageOrTextComponent - ], - entryComponents: [ - AddonQtypeDdImageOrTextComponent - ] -}) -export class AddonQtypeDdImageOrTextModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeDdImageOrTextHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/ddimageortext/providers/handler.ts b/src/addon/qtype/ddimageortext/providers/handler.ts deleted file mode 100644 index 1774415fb..000000000 --- a/src/addon/qtype/ddimageortext/providers/handler.ts +++ /dev/null @@ -1,118 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionProvider } from '@core/question/providers/question'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeDdImageOrTextComponent } from '../component/ddimageortext'; - -/** - * Handler to support drag-and-drop onto image question type. - */ -@Injectable() -export class AddonQtypeDdImageOrTextHandler implements CoreQuestionHandler { - name = 'AddonQtypeDdImageOrText'; - type = 'qtype_ddimageortext'; - - constructor(private questionProvider: CoreQuestionProvider) { } - - /** - * Return the name of the behaviour to use for the question. - * If the question should use the default behaviour you shouldn't implement this function. - * - * @param question The question. - * @param behaviour The default behaviour. - * @return The behaviour to use. - */ - getBehaviour(question: any, behaviour: string): string { - if (behaviour === 'interactive') { - return 'interactivecountback'; - } - - return behaviour; - } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeDdImageOrTextComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - // An answer is complete if all drop zones have an answer. - // We should always receive all the drop zones with their value ('' if not answered). - for (const name in answers) { - const value = answers[name]; - if (!value || value === '0') { - return 0; - } - } - - return 1; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - for (const name in answers) { - const value = answers[name]; - if (value && value !== '0') { - return 1; - } - } - - return 0; - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.questionProvider.compareAllAnswers(prevAnswers, newAnswers); - } -} diff --git a/src/addon/qtype/ddmarker/classes/ddmarker.ts b/src/addon/qtype/ddmarker/classes/ddmarker.ts deleted file mode 100644 index 90b5686b1..000000000 --- a/src/addon/qtype/ddmarker/classes/ddmarker.ts +++ /dev/null @@ -1,933 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { AddonQtypeDdMarkerGraphicsApi } from './graphics_api'; - -/** - * Encapsulates operations on dd area. - */ -export interface AddonQtypeDdMarkerQuestionDocStructure { - topNode?: () => HTMLElement; - bgImg?: () => HTMLImageElement; - dragItemsArea?: () => HTMLElement; - dragItems?: () => HTMLElement[]; - dragItemsForChoice?: (choiceNo: number) => HTMLElement[]; - dragItemForChoice?: (choiceNo: number, itemNo: number) => HTMLElement; - dragItemPlaceholder?: (choiceNo: number) => HTMLElement; - dragItemBeingDragged?: (choiceNo: number) => HTMLElement; - dragItemHome?: (choiceNo: number) => HTMLElement; - dragItemHomes?: () => HTMLElement[]; - getClassnameNumericSuffix?: (node: HTMLElement, prefix: string) => number; - inputsForChoices?: () => HTMLElement[]; - inputForChoice?: (choiceNo: number) => HTMLElement; - markerTexts?: () => HTMLElement; -} - -/** - * Point type. - */ -export type AddonQtypeDdMarkerQuestionPoint = { - x: number; // X axis coordinates. - y: number; // Y axis coordinates. -}; - -/** - * Class to make a question of ddmarker type work. - */ -export class AddonQtypeDdMarkerQuestion { - protected COLOURS = ['#FFFFFF', '#B0C4DE', '#DCDCDC', '#D8BFD8', '#87CEFA', '#DAA520', '#FFD700', '#F0E68C']; - - protected logger: any; - protected afterImageLoadDone = false; - protected drops; - protected topNode; - protected nextColourIndex = 0; - protected proportion = 1; - protected selected: HTMLElement; // Selected element (being "dragged"). - protected graphics: AddonQtypeDdMarkerGraphicsApi; - protected resizeFunction; - - doc: AddonQtypeDdMarkerQuestionDocStructure; - shapes = []; - - /** - * Create the instance. - * - * @param logger Logger provider. - * @param domUtils Dom Utils provider. - * @param textUtils Text Utils provider. - * @param container The container HTMLElement of the question. - * @param question The question instance. - * @param readOnly Whether it's read only. - * @param dropZones The drop zones received in the init object of the question. - * @param imgSrc Background image source (3.6+ sites). - */ - constructor(logger: CoreLoggerProvider, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, - protected container: HTMLElement, protected question: any, protected readOnly: boolean, protected dropZones: any[], - protected imgSrc?: string) { - this.logger = logger.getInstance('AddonQtypeDdMarkerQuestion'); - - this.graphics = new AddonQtypeDdMarkerGraphicsApi(this, this.domUtils); - - this.initializer(question); - } - - /** - * Calculate image proportion to make easy conversions. - */ - calculateImgProportion(): void { - const bgImg = this.doc.bgImg(); - - // Render the position related to the current image dimensions. - this.proportion = 1; - if (bgImg.width != bgImg.naturalWidth) { - this.proportion = bgImg.width / bgImg.naturalWidth; - } - } - - /** - * Create a new draggable element cloning a certain element. - * - * @param dragHome The element to clone. - * @param itemNo The number of the new item. - * @return The new element. - */ - cloneNewDragItem(dragHome: HTMLElement, itemNo: number): HTMLElement { - // Clone the element and add the right classes. - const drag = dragHome.cloneNode(true); - drag.classList.remove('draghome'); - drag.classList.add('dragitem'); - drag.classList.add('item' + itemNo); - drag.classList.remove('dragplaceholder'); // In case it has it. - dragHome.classList.add('dragplaceholder'); - - // Insert the new drag after the dragHome. - dragHome.parentElement.insertBefore(drag, dragHome.nextSibling); - if (!this.readOnly) { - this.draggable(drag); - } - - return drag; - } - - /** - * Convert the X and Y position of the BG IMG to a position relative to the window. - * - * @param bgImgXY X and Y of the BG IMG relative position. - * @return Position relative to the window. - */ - convertToWindowXY(bgImgXY: string): number[] { - const bgImg = this.doc.bgImg(); - const position = this.domUtils.getElementXY(bgImg, null, 'ddarea'); - let coordsNumbers = this.parsePoint(bgImgXY); - - coordsNumbers = this.makePointProportional(coordsNumbers); - - return [coordsNumbers.x + position[0], coordsNumbers.y + position[1]]; - } - - /** - * Check if some coordinates (X, Y) are inside the background image. - * - * @param coords Coordinates to check. - * @return Whether they're inside the background image. - */ - coordsInImg(coords: AddonQtypeDdMarkerQuestionPoint): boolean { - const bgImg = this.doc.bgImg(); - - return (coords.x * this.proportion <= bgImg.width + 1) && (coords.y * this.proportion <= bgImg.height + 1); - } - - /** - * Deselect all draggable items. - */ - deselectDrags(): void { - const drags = this.doc.dragItems(); - drags.forEach((drag) => { - drag.classList.remove('beingdragged'); - }); - this.selected = null; - } - - /** - * Function to call when the instance is no longer needed. - */ - destroy(): void { - if (this.resizeFunction) { - window.removeEventListener('resize', this.resizeFunction); - } - } - - /** - * Returns an object to encapsulate operations on dd area. - * - * @param slot The question slot. - * @return The object. - */ - docStructure(slot: number): AddonQtypeDdMarkerQuestionDocStructure { - const topNode = this.container.querySelector('.addon-qtype-ddmarker-container'), - dragItemsArea = topNode.querySelector('div.dragitems, div.draghomes'); - - return { - topNode: (): HTMLElement => { - return topNode; - }, - bgImg: (): HTMLImageElement => { - return topNode.querySelector('.dropbackground'); - }, - dragItemsArea: (): HTMLElement => { - return dragItemsArea; - }, - dragItems: (): HTMLElement[] => { - return Array.from(dragItemsArea.querySelectorAll('.dragitem')); - }, - dragItemsForChoice: (choiceNo: number): HTMLElement[] => { - return Array.from(dragItemsArea.querySelectorAll('span.dragitem.choice' + choiceNo)); - }, - dragItemForChoice: (choiceNo: number, itemNo: number): HTMLElement => { - return dragItemsArea.querySelector('span.dragitem.choice' + choiceNo + '.item' + itemNo); - }, - dragItemPlaceholder: (choiceNo: number): HTMLElement => { - return dragItemsArea.querySelector('span.dragplaceholder.choice' + choiceNo); - }, - dragItemBeingDragged: (choiceNo: number): HTMLElement => { - return dragItemsArea.querySelector('span.dragitem.beingdragged.choice' + choiceNo); - }, - dragItemHome: (choiceNo: number): HTMLElement => { - return dragItemsArea.querySelector('span.draghome.choice' + choiceNo + - ', span.marker.choice' + choiceNo); - }, - dragItemHomes: (): HTMLElement[] => { - return Array.from(dragItemsArea.querySelectorAll('span.draghome, span.marker')); - }, - getClassnameNumericSuffix: (node: HTMLElement, prefix: string): number => { - - if (node.classList && node.classList.length) { - const patt1 = new RegExp('^' + prefix + '([0-9])+$'), - patt2 = new RegExp('([0-9])+$'); - - for (let index = 0; index < node.classList.length; index++) { - if (patt1.test(node.classList[index])) { - const match = patt2.exec(node.classList[index]); - - return Number(match[0]); - } - } - } - - this.logger.warn('Prefix "' + prefix + '" not found in class names.'); - }, - inputsForChoices: (): HTMLElement[] => { - return Array.from(topNode.querySelectorAll('input.choices')); - }, - inputForChoice: (choiceNo: number): HTMLElement => { - return topNode.querySelector('input.choice' + choiceNo); - }, - markerTexts: (): HTMLElement => { - return topNode.querySelector('div.markertexts'); - } - }; - } - - /** - * Make an element "draggable". In the mobile app, items are "dragged" using tap and drop. - * - * @param drag Element. - */ - draggable(drag: HTMLElement): void { - drag.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - const dragging = this.selected; - if (dragging && !drag.classList.contains('unplaced')) { - - const position = this.domUtils.getElementXY(drag, null, 'ddarea'), - bgImg = this.doc.bgImg(), - bgImgPos = this.domUtils.getElementXY(bgImg, null, 'ddarea'); - - position[0] = position[0] - bgImgPos[0] + e.offsetX; - position[1] = position[1] - bgImgPos[1] + e.offsetY; - - // Ensure the we click on a placed dragitem. - if (position[0] <= bgImg.width && position[1] <= bgImg.height) { - this.deselectDrags(); - this.dropDrag(dragging, position); - - return; - } - } - - if (drag.classList.contains('beingdragged')) { - this.deselectDrags(); - } else { - this.selectDrag(drag); - } - }); - } - - /** - * Get the coordinates of the drag home of a certain choice. - * - * @param choiceNo Choice number. - * @return Coordinates. - */ - dragHomeXY(choiceNo: number): number[] { - const dragItemHome = this.doc.dragItemHome(choiceNo), - position = this.domUtils.getElementXY(dragItemHome, null, 'ddarea'); - - return [position[0], position[1]]; - } - - /** - * Draw a drop zone. - * - * @param dropZoneNo Number of the drop zone. - * @param markerText The marker text to set. - * @param shape Name of the shape of the drop zone (circle, rectangle, polygon). - * @param coords Coordinates of the shape. - * @param colour Colour of the shape. - */ - drawDropZone(dropZoneNo: number, markerText: string, shape: string, coords: string, colour: string): void { - let existingMarkerText: HTMLElement; - - const markerTexts = this.doc.markerTexts(); - // Check if there is already a marker text for this drop zone. - existingMarkerText = markerTexts.querySelector('span.markertext' + dropZoneNo); - - if (existingMarkerText) { - // Marker text already exists. Update it or remove it if empty. - if (markerText !== '') { - existingMarkerText.innerHTML = markerText; - } else { - existingMarkerText.remove(); - } - } else if (markerText !== '') { - // Create and add the marker text. - const classNames = 'markertext markertext' + dropZoneNo, - span = document.createElement('span'); - - span.className = classNames; - span.innerHTML = markerText; - - markerTexts.appendChild(span); - } - - // Check that a function to draw this shape exists. - const drawFunc = 'drawShape' + this.textUtils.ucFirst(shape); - if (this[drawFunc] instanceof Function) { - - // Call the function. - const xyForText = this[drawFunc](dropZoneNo, coords, colour); - if (xyForText !== null) { - - // Search the marker for the drop zone. - const markerSpan = this.doc.topNode().querySelector( - 'div.ddarea div.markertexts span.markertext' + dropZoneNo); - if (markerSpan !== null) { - const width = this.domUtils.getElementMeasure(markerSpan, true, true, false, true); - const height = this.domUtils.getElementMeasure(markerSpan, false, true, false, true); - markerSpan.style.opacity = '0.6'; - markerSpan.style.left = (xyForText.x - (width / 2)) + 'px'; - markerSpan.style.top = (xyForText.y - (height / 2)) + 'px'; - - const markerSpanAnchor = markerSpan.querySelector('a'); - if (markerSpanAnchor !== null) { - - markerSpanAnchor.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - this.shapes.forEach((elem) => { - elem.css('fill-opacity', 0.5); - }); - - this.shapes[dropZoneNo].css('fill-opacity', 1); - setTimeout(() => { - this.shapes[dropZoneNo].css('fill-opacity', 0.5); - }, 2000); - }); - - markerSpanAnchor.setAttribute('tabIndex', '0'); - } - } - } - } - } - - /** - * Draw a circle in a drop zone. - * - * @param dropZoneNo Number of the drop zone. - * @param coordinates Coordinates of the circle. - * @param colour Colour of the circle. - * @return X and Y position of the center of the circle. - */ - drawShapeCircle(dropZoneNo: number, coordinates: string, colour: string): AddonQtypeDdMarkerQuestionPoint { - if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?;\d+(\.\d+)?$/)) { - return null; - } - - const bits = coordinates.split(';'); - let centre = this.parsePoint(bits[0]); - const radius = Number(bits[1]); - - // Calculate circle limits and check it's inside the background image. - const circleLimit = {x: centre.x - radius, y: centre.y - radius}; - if (this.coordsInImg(circleLimit)) { - centre = this.makePointProportional(centre); - - // All good, create the shape. - this.shapes[dropZoneNo] = this.graphics.addShape({ - type: 'circle', - color: colour - }, { - cx: centre.x, - cy: centre.y, - r: Math.round(radius * this.proportion) - }); - - // Return the centre. - return centre; - } - - return null; - } - - /** - * Draw a rectangle in a drop zone. - * - * @param dropZoneNo Number of the drop zone. - * @param coordinates Coordinates of the rectangle. - * @param colour Colour of the rectangle. - * @return X and Y position of the center of the rectangle. - */ - drawShapeRectangle(dropZoneNo: number, coordinates: string, colour: string): AddonQtypeDdMarkerQuestionPoint { - if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?;\d+(\.\d+)?,\d+(\.\d+)?$/)) { - return null; - } - - const bits = coordinates.split(';'); - const startPoint = this.parsePoint(bits[0]); - const size = this.parsePoint(bits[1]); - - // Calculate rectangle limits and check it's inside the background image. - const rectLimits = {x: startPoint.x + size.x, y: startPoint.y + size.y}; - if (this.coordsInImg(rectLimits)) { - const startPointProp = this.makePointProportional(startPoint); - const sizeProp = this.makePointProportional(size); - - // All good, create the shape. - this.shapes[dropZoneNo] = this.graphics.addShape({ - type: 'rect', - color: colour - }, { - x: startPointProp.x, - y: startPointProp.y, - width: sizeProp.x, - height: sizeProp.y - }); - - const centre = { x: startPoint.x + (size.x / 2) , y: startPoint.y + (size.y / 2)}; - - // Return the centre. - return this.makePointProportional(centre); - } - - return null; - } - - /** - * Draw a polygon in a drop zone. - * - * @param dropZoneNo Number of the drop zone. - * @param coordinates Coordinates of the polygon. - * @param colour Colour of the polygon. - * @return X and Y position of the center of the polygon. - */ - drawShapePolygon(dropZoneNo: number, coordinates: string, colour: string): AddonQtypeDdMarkerQuestionPoint { - if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?(?:;\d+(\.\d+)?,\d+(\.\d+)?)*$/)) { - return null; - } - - const bits = coordinates.split(';'); - const centre = {x: 0, y: 0}; - const points = bits.map((bit) => { - const point = this.parsePoint(bit); - centre.x += point.x; - centre.y += point.y; - - return point; - }); - - if (points.length > 0) { - centre.x = Math.round(centre.x / points.length); - centre.y = Math.round(centre.y / points.length); - } - - const pointsOnImg = []; - points.forEach((point) => { - if (this.coordsInImg(point)) { - point = this.makePointProportional(point); - - pointsOnImg.push(point.x + ',' + point.y); - } - }); - - if (pointsOnImg.length > 2) { - this.shapes[dropZoneNo] = this.graphics.addShape({ - type: 'polygon', - color: colour - }, { - points: pointsOnImg.join(' ') - }); - - // Return the centre. - return this.makePointProportional(centre); - } - - return null; - } - - /** - * Make a point from the string representation. - * - * @param coordinates "x,y". - * @return Coordinates to the point. - */ - parsePoint(coordinates: string): AddonQtypeDdMarkerQuestionPoint { - const bits = coordinates.split(','); - if (bits.length !== 2) { - throw coordinates + ' is not a valid point'; - } - - return {x: Number(bits[0]), y: Number(bits[1])}; - } - - /** - * Make proportional position of the point. - * - * @param point Point coordinates. - * @return Converted point. - */ - makePointProportional(point: AddonQtypeDdMarkerQuestionPoint): AddonQtypeDdMarkerQuestionPoint { - return { - x: Math.round(point.x * this.proportion), - y: Math.round(point.y * this.proportion) - - }; - } - - /** - * Drop a drag element into a certain position. - * - * @param drag The element to drop. - * @param position Position to drop to (X, Y). - */ - dropDrag(drag: HTMLElement, position: number[]): void { - const choiceNo = this.getChoiceNoForNode(drag); - - if (position) { - // Set the position related to the natural image dimensions. - if (this.proportion < 1) { - position[0] = Math.round(position[0] / this.proportion); - position[1] = Math.round(position[1] / this.proportion); - } - } - - this.saveAllXYForChoice(choiceNo, drag, position); - this.redrawDragsAndDrops(); - } - - /** - * Determine which drag items need to be shown and return coords of all drag items except any that are currently being - * dragged based on contents of hidden inputs and whether drags are 'infinite' or how many drags should be shown. - * - * @param input The input element. - * @return List of coordinates. - */ - getCoords(input: HTMLElement): number[][] { - const choiceNo = this.getChoiceNoForNode(input), - fv = input.getAttribute('value'), - infinite = input.classList.contains('infinite'), - noOfDrags = this.getNoOfDragsForNode(input), - dragging = (this.doc.dragItemBeingDragged(choiceNo) !== null), - coords: number[][] = []; - - if (fv !== '' && typeof fv != 'undefined' && fv !== null) { - // Get all the coordinates in the input and add them to the coords list. - const coordsStrings = fv.split(';'); - - for (let i = 0; i < coordsStrings.length; i++) { - coords[coords.length] = this.convertToWindowXY(coordsStrings[i]); - } - } - - const displayedDrags = coords.length + (dragging ? 1 : 0); - if (infinite || (displayedDrags < noOfDrags)) { - coords[coords.length] = this.dragHomeXY(choiceNo); - } - - return coords; - } - - /** - * Get the choice number from an HTML element. - * - * @param node Element to check. - * @return Choice number. - */ - getChoiceNoForNode(node: HTMLElement): number { - return Number(this.doc.getClassnameNumericSuffix(node, 'choice')); - } - - /** - * Get the coordinates (X, Y) of a draggable element. - * - * @param dragItem The draggable item. - * @return Coordinates. - */ - getDragXY(dragItem: HTMLElement): number[] { - const position = this.domUtils.getElementXY(dragItem, null, 'ddarea'), - bgImg = this.doc.bgImg(), - bgImgXY = this.domUtils.getElementXY(bgImg, null, 'ddarea'); - - position[0] -= bgImgXY[0]; - position[1] -= bgImgXY[1]; - - // Set the position related to the natural image dimensions. - if (this.proportion < 1) { - position[0] = Math.round(position[0] / this.proportion); - position[1] = Math.round(position[1] / this.proportion); - } - - return position; - } - - /** - * Get the item number from an HTML element. - * - * @param node Element to check. - * @return Choice number. - */ - getItemNoForNode(node: HTMLElement): number { - return Number(this.doc.getClassnameNumericSuffix(node, 'item')); - } - - /** - * Get the next colour. - * - * @return Colour. - */ - getNextColour(): string { - const colour = this.COLOURS[this.nextColourIndex]; - this.nextColourIndex++; - - // If we reached the end of the list, start again. - if (this.nextColourIndex === this.COLOURS.length) { - this.nextColourIndex = 0; - } - - return colour; - } - - /** - * Get the number of drags from an HTML element. - * - * @param node Element to check. - * @return Choice number. - */ - getNoOfDragsForNode(node: HTMLElement): number { - return Number(this.doc.getClassnameNumericSuffix(node, 'noofdrags')); - } - - /** - * Initialize the question. - * - * @param question Question. - */ - initializer(question: any): void { - this.doc = this.docStructure(question.slot); - - // Wait the DOM to be rendered. - setTimeout(() => { - this.pollForImageLoad(); - }); - - this.resizeFunction = this.redrawDragsAndDrops.bind(this); - window.addEventListener('resize', this.resizeFunction); - } - - /** - * Make background image and home zone dropable. - */ - makeImageDropable(): void { - if (this.readOnly) { - return; - } - - // Listen for click events in the background image to make it dropable. - const bgImg = this.doc.bgImg(); - bgImg.addEventListener('click', (e) => { - - const drag = this.selected; - if (!drag) { - // No draggable element selected, nothing to do. - return false; - } - - // There's an element being dragged. Deselect it and drop it in the position. - const position = [e.offsetX, e.offsetY]; - this.deselectDrags(); - this.dropDrag(drag, position); - - e.preventDefault(); - e.stopPropagation(); - }); - - const home = this.doc.dragItemsArea(); - home.addEventListener('click', (e) => { - - const drag = this.selected; - if (!drag) { - // No draggable element selected, nothing to do. - return false; - } - - // There's an element being dragged but it's not placed yet, deselect. - if (drag.classList.contains('unplaced')) { - this.deselectDrags(); - - return false; - } - - // There's an element being dragged and it's placed somewhere. Move it back to the home area. - this.deselectDrags(); - this.dropDrag(drag, null); - - e.preventDefault(); - e.stopPropagation(); - }); - } - - /** - * Wait for the background image to be loaded. - */ - pollForImageLoad(): void { - if (this.afterImageLoadDone) { - // Already treated. - return; - } - - const bgImg = this.doc.bgImg(); - if (!bgImg.src && this.imgSrc) { - bgImg.src = this.imgSrc; - } - - const imgLoaded = (): void => { - bgImg.removeEventListener('load', imgLoaded); - - this.makeImageDropable(); - - setTimeout(() => { - this.redrawDragsAndDrops(); - }); - - this.afterImageLoadDone = true; - this.question.loaded = true; - }; - - if (bgImg.complete && bgImg.naturalWidth) { - imgLoaded(); - - return; - } - - bgImg.addEventListener('load', imgLoaded); - - // Try again after a while. - setTimeout(() => { - this.pollForImageLoad(); - }, 500); - } - - /** - * Redraw all draggables and drop zones. - */ - redrawDragsAndDrops(): void { - // Mark all the draggable items as not placed. - const drags = this.doc.dragItems(); - drags.forEach((drag) => { - drag.classList.add('unneeded', 'unplaced'); - }); - - // Re-calculate the image proportion. - this.calculateImgProportion(); - - // Get all the inputs. - const inputs = this.doc.inputsForChoices(); - for (let x = 0; x < inputs.length; x++) { - - // Get all the drag items for the choice. - const input = inputs[x], - choiceNo = this.getChoiceNoForNode(input), - coords = this.getCoords(input), - dragItemHome = this.doc.dragItemHome(choiceNo), - homePosition = this.dragHomeXY(choiceNo); - - for (let i = 0; i < coords.length; i++) { - let dragItem = this.doc.dragItemForChoice(choiceNo, i); - - if (!dragItem || dragItem.classList.contains('beingdragged')) { - dragItem = this.cloneNewDragItem(dragItemHome, i); - } else { - dragItem.classList.remove('unneeded'); - } - - const placeholder = this.doc.dragItemPlaceholder(choiceNo); - - // Remove the class only if is placed on the image. - if (homePosition[0] != coords[i][0] || homePosition[1] != coords[i][1]) { - dragItem.classList.remove('unplaced'); - dragItem.classList.add('placed'); - - const computedStyle = getComputedStyle(dragItem); - const left = coords[i][0] - this.domUtils.getComputedStyleMeasure(computedStyle, 'marginLeft'); - const top = coords[i][1] - this.domUtils.getComputedStyleMeasure(computedStyle, 'marginTop'); - - dragItem.style.left = left + 'px'; - dragItem.style.top = top + 'px'; - placeholder.classList.add('active'); - } else { - dragItem.classList.remove('placed'); - dragItem.classList.add('unplaced'); - placeholder.classList.remove('active'); - } - } - } - - // Remove unneeded draggable items. - for (let y = 0; y < drags.length; y++) { - const item = drags[y]; - if (item.classList.contains('unneeded') && !item.classList.contains('beingdragged')) { - item.remove(); - } - } - - // Re-draw drop zones. - if (this.dropZones && this.dropZones.length !== 0) { - this.graphics.clear(); - this.restartColours(); - - for (const dropZoneNo in this.dropZones) { - const colourForDropZone = this.getNextColour(), - dropZone = this.dropZones[dropZoneNo], - dzNo = Number(dropZoneNo); - - this.drawDropZone(dzNo, dropZone.markertext, dropZone.shape, dropZone.coords, colourForDropZone); - } - } - } - - /** - * Reset the coordinates stored for a choice. - * - * @param choiceNo Choice number. - */ - resetDragXY(choiceNo: number): void { - this.setFormValue(choiceNo, ''); - } - - /** - * Restart the colour index. - */ - restartColours(): void { - this.nextColourIndex = 0; - } - - /** - * Save all the coordinates of a choice into the right input. - * - * @param choiceNo Number of the choice. - * @param dropped Element being dropped. - * @param position Position where the element is dropped. - */ - saveAllXYForChoice(choiceNo: number, dropped: HTMLElement, position: number[]): void { - const coords = []; - let bgImgXY; - - // Calculate the coords for the choice. - const dragItemsChoice = this.doc.dragItemsForChoice(choiceNo); - for (let i = 0; i < dragItemsChoice.length; i++) { - - const dragItem = this.doc.dragItemForChoice(choiceNo, i); - if (dragItem) { - - dragItem.classList.remove('item' + i); - bgImgXY = this.getDragXY(dragItem); - dragItem.classList.add('item' + coords.length); - coords.push(bgImgXY); - } - } - - if (position !== null) { - // Element dropped into a certain position. Mark it as placed and save the position. - dropped.classList.remove('unplaced'); - dropped.classList.add('item' + coords.length); - coords.push(position); - } else { - // Element back at home, mark it as unplaced. - dropped.classList.add('unplaced'); - } - - if (coords.length > 0) { - // Save the coordinates in the input. - this.setFormValue(choiceNo, coords.join(';')); - } else { - // Empty the input. - this.resetDragXY(choiceNo); - } - } - - /** - * Save a certain value in the input of a choice. - * - * @param choiceNo Choice number. - * @param value The value to set. - */ - setFormValue(choiceNo: number, value: string): void { - this.doc.inputForChoice(choiceNo).setAttribute('value', value); - } - - /** - * Select a draggable element. - * - * @param drag Element. - */ - selectDrag(drag: HTMLElement): void { - // Deselect previous drags. - this.deselectDrags(); - - this.selected = drag; - drag.classList.add('beingdragged'); - - const itemNo = this.getItemNoForNode(drag); - if (itemNo !== null) { - drag.classList.remove('item' + itemNo); - } - } -} diff --git a/src/addon/qtype/ddmarker/classes/graphics_api.ts b/src/addon/qtype/ddmarker/classes/graphics_api.ts deleted file mode 100644 index 770031f3b..000000000 --- a/src/addon/qtype/ddmarker/classes/graphics_api.ts +++ /dev/null @@ -1,90 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonQtypeDdMarkerQuestion } from './ddmarker'; - -/** - * Graphics API for drag-and-drop markers question type. - */ -export class AddonQtypeDdMarkerGraphicsApi { - - protected NS = 'http://www.w3.org/2000/svg'; - protected dropZone: SVGSVGElement; - - /** - * Create the instance. - * - * @param instance Question instance. - * @param domUtils Dom Utils provider. - */ - constructor(protected instance: AddonQtypeDdMarkerQuestion, protected domUtils: CoreDomUtilsProvider) { } - - /** - * Add a shape. - * - * @param shapeAttribs Attributes for the shape: type and color. - * @param styles Object with the styles for the shape (name -> value). - * @return The new shape. - */ - addShape(shapeAttribs: {type: string, color: string}, styles: {[name: string]: number | string}): Element { - const shape = document.createElementNS(this.NS, shapeAttribs.type); - shape.setAttribute('fill', shapeAttribs.color); - shape.setAttribute('fill-opacity', '0.5'); - shape.setAttribute('stroke', 'black'); - - for (const x in styles) { - shape.setAttribute(x, String(styles[x])); - } - - this.dropZone.appendChild(shape); - - return shape; - } - - /** - * Clear the shapes. - */ - clear(): void { - const bgImg = this.instance.doc.bgImg(), - position = this.domUtils.getElementXY(bgImg, null, 'ddarea'), - dropZones = this.instance.doc.topNode().querySelector('div.ddarea div.dropzones'); - - dropZones.style.left = position[0] + 'px'; - dropZones.style.top = position[1] + 'px'; - dropZones.style.width = bgImg.width + 'px'; - dropZones.style.height = bgImg.height + 'px'; - - const markerTexts = this.instance.doc.markerTexts(); - markerTexts.style.left = position[0] + 'px'; - markerTexts.style.top = position[1] + 'px'; - markerTexts.style.width = bgImg.width + 'px'; - markerTexts.style.height = bgImg.height + 'px'; - - if (!this.dropZone) { - this.dropZone = document.createElementNS(this.NS, 'svg'); - dropZones.appendChild(this.dropZone); - } else { - // Remove all children. - while (this.dropZone.firstChild) { - this.dropZone.removeChild(this.dropZone.firstChild); - } - } - - this.dropZone.style.width = bgImg.width + 'px'; - this.dropZone.style.height = bgImg.height + 'px'; - - this.instance.shapes = []; - } -} diff --git a/src/addon/qtype/ddmarker/component/addon-qtype-ddmarker.html b/src/addon/qtype/ddmarker/component/addon-qtype-ddmarker.html deleted file mode 100644 index 60ec70218..000000000 --- a/src/addon/qtype/ddmarker/component/addon-qtype-ddmarker.html +++ /dev/null @@ -1,13 +0,0 @@ -
- - - - -

- - {{ 'core.question.howtodraganddrop' | translate }} -

-

- -
-
diff --git a/src/addon/qtype/ddmarker/component/ddmarker.scss b/src/addon/qtype/ddmarker/component/ddmarker.scss deleted file mode 100644 index 7c33d5302..000000000 --- a/src/addon/qtype/ddmarker/component/ddmarker.scss +++ /dev/null @@ -1,144 +0,0 @@ -// Style ddmarker content a bit. Almost all these styles are copied from Moodle. -addon-qtype-ddmarker { - .ddarea, .ddform { - user-select: none; - } - - .qtext { - margin-bottom: 0.5em; - display: block; - } - - .droparea { - display: inline-block; - } - - div.droparea img { - border: 1px solid $gray-darker; - max-width: 100%; - } - - .dropzones svg { - z-index: 3; - } - - .dragitem.beingdragged .markertext { - z-index: 5; - box-shadow: $core-dd-question-selected-shadow; - } - - .dragitems, // Previous to 3.9. - .draghomes { - &.readonly { - .dragitem, - .marker { - cursor: auto; - } - } - - .dragitem, // Previous to 3.9. - .draghome, - .marker { - vertical-align: top; - cursor: pointer; - position: relative; - margin: 10px; - display: inline-block; - &.dragplaceholder { - display: none; - visibility: hidden; - - &.active { - display: inline-block; - } - } - - &.unplaced { - position: relative; - } - &.placed { - position: absolute; - opacity: 0.6; - } - } - } - - .droparea { - .dragitem, - .marker { - cursor: pointer; - position: absolute; - vertical-align: top; - z-index: 2; - } - } - - div.ddarea { - text-align: center; - position: relative; - } - div.ddarea .dropzones, - div.ddarea .markertexts { - top: 0; - left: 0; - min-height: 80px; - position: absolute; - @include text-align('start'); - } - - .dropbackground { - margin: 0 auto; - } - - div.dragitems div.draghome, - div.dragitems div.dragitem, - div.draghome, - div.drag, - div.draghomes div.marker, - div.marker, - div.drag { - font: 13px/1.231 arial,helvetica,clean,sans-serif; - } - div.dragitems span.markertext, - div.draghomes span.markertext, - div.markertexts span.markertext { - margin: 0 5px; - z-index: 2; - background-color: $white; - border: 2px solid $gray-darker; - padding: 5px; - display: inline-block; - zoom: 1; - border-radius: 10px; - color: $text-color; - } - div.markertexts span.markertext { - z-index: 3; - background-color: $yellow-light; - border-style: solid; - border-width: 2px; - border-color: $yellow; - position: absolute; - } - span.wrongpart { - background-color: $yellow-light; - border-style: solid; - border-width: 2px; - border-color: $yellow; - padding: 5px; - border-radius: 10px; - opacity: 0.6; - margin: 5px; - display: inline-block; - } - div.dragitems img.target, - div.draghomes img.target { - position: absolute; - left: -7px; /* This must be half the size of the target image, minus 0.5. */ - top: -7px; /* In other words, this works for a 15x15 cross-hair. */ - } - div.dragitems div.draghome img.target, - div.draghomes div.marker img.target { - display: none; - } -} diff --git a/src/addon/qtype/ddmarker/component/ddmarker.ts b/src/addon/qtype/ddmarker/component/ddmarker.ts deleted file mode 100644 index 1b0870b36..000000000 --- a/src/addon/qtype/ddmarker/component/ddmarker.ts +++ /dev/null @@ -1,163 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, Injector, ElementRef, ViewChild } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker'; - -/** - * Component to render a drag-and-drop markers question. - */ -@Component({ - selector: 'addon-qtype-ddmarker', - templateUrl: 'addon-qtype-ddmarker.html' -}) -export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent implements OnInit, OnDestroy { - @ViewChild('questiontext') questionTextEl: ElementRef; - - protected element: HTMLElement; - protected questionInstance: AddonQtypeDdMarkerQuestion; - protected dropZones: any[]; // The drop zones received in the init object of the question. - protected imgSrc: string; // Background image URL. - protected destroyed = false; - protected textIsRendered = false; - protected ddAreaisRendered = false; - - constructor(protected loggerProvider: CoreLoggerProvider, injector: Injector, element: ElementRef, - protected sitesProvider: CoreSitesProvider, protected urlUtils: CoreUrlUtilsProvider, - protected filepoolProvider: CoreFilepoolProvider) { - super(loggerProvider, 'AddonQtypeDdMarkerComponent', injector); - - this.element = element.nativeElement; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.question) { - this.logger.warn('Aborting because of no question received.'); - - return this.questionHelper.showComponentError(this.onAbort); - } - - const element = this.domUtils.convertToElement(this.question.html); - - // Get D&D area, form and question text. - const ddArea = element.querySelector('.ddarea'), - ddForm = element.querySelector('.ddform'); - - this.question.text = this.domUtils.getContentsOfElement(element, '.qtext'); - if (!ddArea || !ddForm || typeof this.question.text == 'undefined') { - this.logger.warn('Aborting because of an error parsing question.', this.question.name); - - return this.questionHelper.showComponentError(this.onAbort); - } - - // Build the D&D area HTML. - this.question.ddArea = ddArea.outerHTML; - - const wrongParts = element.querySelector('.wrongparts'); - if (wrongParts) { - this.question.ddArea += wrongParts.outerHTML; - } - this.question.ddArea += ddForm.outerHTML; - this.question.readOnly = false; - - if (this.question.initObjects) { - // Moodle version <= 3.5. - if (typeof this.question.initObjects.dropzones != 'undefined') { - this.dropZones = this.question.initObjects.dropzones; - } - if (typeof this.question.initObjects.readonly != 'undefined') { - this.question.readOnly = this.question.initObjects.readonly; - } - } else if (this.question.amdArgs) { - // Moodle version >= 3.6. - let nextIndex = 1; - // Moodle version >= 3.9, imgSrc is not specified, do not advance index. - if (typeof this.question.amdArgs[nextIndex] != 'undefined' && typeof this.question.amdArgs[nextIndex] != 'boolean') { - this.imgSrc = this.question.amdArgs[nextIndex]; - nextIndex++; - } - - if (typeof this.question.amdArgs[nextIndex] != 'undefined') { - this.question.readOnly = this.question.amdArgs[nextIndex]; - } - nextIndex++; - - if (typeof this.question.amdArgs[nextIndex] != 'undefined') { - this.dropZones = this.question.amdArgs[nextIndex]; - } - } - - this.question.loaded = false; - } - - /** - * The question ddArea has been rendered. - */ - ddAreaRendered(): void { - this.ddAreaisRendered = true; - if (this.textIsRendered) { - this.questionRendered(); - } - } - - /** - * The question text has been rendered. - */ - textRendered(): void { - this.textIsRendered = true; - if (this.ddAreaisRendered) { - this.questionRendered(); - } - } - - /** - * The question has been rendered. - */ - protected questionRendered(): void { - if (!this.destroyed) { - // Download background image (3.6+ sites). - let promise = null; - const site = this.sitesProvider.getCurrentSite(); - if (this.imgSrc && site.canDownloadFiles() && this.urlUtils.isPluginFileUrl(this.imgSrc)) { - promise = this.filepoolProvider.getSrcByUrl(site.id, this.imgSrc, this.component, this.componentId, 0, true, true); - } else { - promise = Promise.resolve(this.imgSrc); - } - - promise.then((imgSrc) => { - this.domUtils.waitForImages(this.questionTextEl.nativeElement).then(() => { - // Create the instance. - this.questionInstance = new AddonQtypeDdMarkerQuestion(this.loggerProvider, this.domUtils, this.textUtils, - this.element, this.question, this.question.readOnly, this.dropZones, imgSrc); - }); - }); - } - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.destroyed = true; - this.questionInstance && this.questionInstance.destroy(); - } -} diff --git a/src/addon/qtype/ddmarker/ddmarker.module.ts b/src/addon/qtype/ddmarker/ddmarker.module.ts deleted file mode 100644 index 16ffc5612..000000000 --- a/src/addon/qtype/ddmarker/ddmarker.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeDdMarkerHandler } from './providers/handler'; -import { AddonQtypeDdMarkerComponent } from './component/ddmarker'; - -@NgModule({ - declarations: [ - AddonQtypeDdMarkerComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonQtypeDdMarkerHandler - ], - exports: [ - AddonQtypeDdMarkerComponent - ], - entryComponents: [ - AddonQtypeDdMarkerComponent - ] -}) -export class AddonQtypeDdMarkerModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeDdMarkerHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/ddmarker/providers/handler.ts b/src/addon/qtype/ddmarker/providers/handler.ts deleted file mode 100644 index 2e8195ff5..000000000 --- a/src/addon/qtype/ddmarker/providers/handler.ts +++ /dev/null @@ -1,128 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionProvider } from '@core/question/providers/question'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { AddonQtypeDdMarkerComponent } from '../component/ddmarker'; - -/** - * Handler to support drag-and-drop markers question type. - */ -@Injectable() -export class AddonQtypeDdMarkerHandler implements CoreQuestionHandler { - name = 'AddonQtypeDdMarker'; - type = 'qtype_ddmarker'; - - constructor(private questionProvider: CoreQuestionProvider, private questionHelper: CoreQuestionHelperProvider) { } - - /** - * Return the name of the behaviour to use for the question. - * If the question should use the default behaviour you shouldn't implement this function. - * - * @param question The question. - * @param behaviour The default behaviour. - * @return The behaviour to use. - */ - getBehaviour(question: any, behaviour: string): string { - if (behaviour === 'interactive') { - return 'interactivecountback'; - } - - return behaviour; - } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeDdMarkerComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - // If 1 dragitem is set we assume the answer is complete (like Moodle does). - for (const name in answers) { - if (answers[name]) { - return 1; - } - } - - return 0; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - return this.isCompleteResponse(question, answers); - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.questionProvider.compareAllAnswers(prevAnswers, newAnswers); - } - - /** - * Get the list of files that needs to be downloaded in addition to the files embedded in the HTML. - * - * @param question Question. - * @param usageId Usage ID. - * @return List of URLs. - */ - getAdditionalDownloadableFiles(question: any, usageId: number): string[] { - this.questionHelper.extractQuestionScripts(question, usageId); - - if (question.amdArgs && typeof question.amdArgs[1] !== 'undefined') { - // Moodle 3.6+. - return [question.amdArgs[1]]; - } - - return []; - } -} diff --git a/src/addon/qtype/ddwtos/classes/ddwtos.ts b/src/addon/qtype/ddwtos/classes/ddwtos.ts deleted file mode 100644 index 0e33cac95..000000000 --- a/src/addon/qtype/ddwtos/classes/ddwtos.ts +++ /dev/null @@ -1,550 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Set of functions to get the CSS selectors. - */ -export interface AddonQtypeDdwtosQuestionCSSSelectors { - topNode?: () => string; - dragContainer?: () => string; - drags?: () => string; - drag?: (no: number) => string; - dragsInGroup?: (groupNo: number) => string; - unplacedDragsInGroup?: (groupNo: number) => string; - dragsForChoiceInGroup?: (choiceNo: number, groupNo: number) => string; - unplacedDragsForChoiceInGroup?: (choiceNo: number, groupNo: number) => string; - drops?: () => string; - dropForPlace?: (placeNo: number) => string; - dropsInGroup?: (groupNo: number) => string; - dragHomes?: () => string; - dragHomesGroup?: (groupNo: number) => string; - dragHome?: (groupNo: number, choiceNo: number) => string; - dropsGroup?: (groupNo: number) => string; -} - -/** - * Class to make a question of ddwtos type work. - */ -export class AddonQtypeDdwtosQuestion { - - protected logger: any; - protected nextDragItemNo = 1; - protected selectors: AddonQtypeDdwtosQuestionCSSSelectors; // Result of cssSelectors. - protected placed: {[no: number]: number}; // Map that relates drag elements numbers with drop zones numbers. - protected selected: HTMLElement; // Selected element (being "dragged"). - protected resizeFunction; - - /** - * Create the instance. - * - * @param logger Logger provider. - * @param domUtils Dom Utils provider. - * @param container The container HTMLElement of the question. - * @param question The question instance. - * @param readOnly Whether it's read only. - * @param inputIds Ids of the inputs of the question (where the answers will be stored). - */ - constructor(logger: CoreLoggerProvider, protected domUtils: CoreDomUtilsProvider, protected container: HTMLElement, - protected question: any, protected readOnly: boolean, protected inputIds: string[], - protected textUtils: CoreTextUtilsProvider) { - this.logger = logger.getInstance('AddonQtypeDdwtosQuestion'); - - this.initializer(question); - } - - /** - * Clone a drag item and add it to the drag container. - * - * @param dragHome Item to clone - */ - cloneDragItem(dragHome: HTMLElement): void { - const drag = dragHome.cloneNode(true); - - drag.classList.remove('draghome'); - drag.classList.add('drag'); - drag.classList.add('no' + this.nextDragItemNo); - this.nextDragItemNo++; - - drag.style.visibility = 'visible'; - drag.style.position = 'absolute'; - - const container = this.container.querySelector(this.selectors.dragContainer()); - container.appendChild(drag); - - if (!this.readOnly) { - this.makeDraggable(drag); - } - } - - /** - * Clone the 'drag homes'. - * Invisible 'drag homes' are output in the question. These have the same properties as the drag items but are invisible. - * We clone these invisible elements to make the actual drag items. - */ - cloneDragItems(): void { - const dragHomes = Array.from(this.container.querySelectorAll(this.selectors.dragHomes())); - for (let x = 0; x < dragHomes.length; x++) { - this.cloneDragItemsForOneChoice(dragHomes[x]); - } - } - - /** - * Clone a certain 'drag home'. If it's an "infinite" drag, clone it several times. - * - * @param dragHome Element to clone. - */ - cloneDragItemsForOneChoice(dragHome: HTMLElement): void { - if (dragHome.classList.contains('infinite')) { - const groupNo = this.getGroup(dragHome), - noOfDrags = this.container.querySelectorAll(this.selectors.dropsInGroup(groupNo)).length; - - for (let x = 0; x < noOfDrags; x++) { - this.cloneDragItem(dragHome); - } - } else { - this.cloneDragItem(dragHome); - } - } - - /** - * Get an object with a set of functions to get the CSS selectors. - * - * @param slot Question slot. - * @return Object with the functions to get the selectors. - */ - cssSelectors(slot: number): AddonQtypeDdwtosQuestionCSSSelectors { - const topNode = '.addon-qtype-ddwtos-container', - selectors: AddonQtypeDdwtosQuestionCSSSelectors = {}; - - selectors.topNode = (): string => { - return topNode; - }; - selectors.dragContainer = (): string => { - return topNode + ' div.drags'; - }; - selectors.drags = (): string => { - return selectors.dragContainer() + ' span.drag'; - }; - selectors.drag = (no: number): string => { - return selectors.drags() + '.no' + no; - }; - selectors.dragsInGroup = (groupNo: number): string => { - return selectors.drags() + '.group' + groupNo; - }; - selectors.unplacedDragsInGroup = (groupNo: number): string => { - return selectors.dragsInGroup(groupNo) + '.unplaced'; - }; - selectors.dragsForChoiceInGroup = (choiceNo: number, groupNo: number): string => { - return selectors.dragsInGroup(groupNo) + '.choice' + choiceNo; - }; - selectors.unplacedDragsForChoiceInGroup = (choiceNo: number, groupNo: number): string => { - return selectors.unplacedDragsInGroup(groupNo) + '.choice' + choiceNo; - }; - selectors.drops = (): string => { - return topNode + ' span.drop'; - }; - selectors.dropForPlace = (placeNo: number): string => { - return selectors.drops() + '.place' + placeNo; - }; - selectors.dropsInGroup = (groupNo: number): string => { - return selectors.drops() + '.group' + groupNo; - }; - selectors.dragHomes = (): string => { - return topNode + ' span.draghome'; - }; - selectors.dragHomesGroup = (groupNo: number): string => { - return topNode + ' .draggrouphomes' + groupNo + ' span.draghome'; - }; - selectors.dragHome = (groupNo: number, choiceNo: number): string => { - return topNode + ' .draggrouphomes' + groupNo + ' span.draghome.choice' + choiceNo; - }; - selectors.dropsGroup = (groupNo: number): string => { - return topNode + ' span.drop.group' + groupNo; - }; - - return selectors; - } - - /** - * Deselect all drags. - */ - deselectDrags(): void { - // Remove the selected class from all drags. - const drags = Array.from(this.container.querySelectorAll(this.selectors.drags())); - drags.forEach((drag) => { - drag.classList.remove('selected'); - }); - this.selected = null; - } - - /** - * Function to call when the instance is no longer needed. - */ - destroy(): void { - if (this.resizeFunction) { - window.removeEventListener('resize', this.resizeFunction); - } - } - - /** - * Get the choice number of an element. It is extracted from the classes. - * - * @param node Element to check. - * @return Choice number. - */ - getChoice(node: HTMLElement): number { - return this.getClassnameNumericSuffix(node, 'choice'); - } - - /** - * Get the number in a certain class name of an element. - * - * @param node The element to check. - * @param prefix Prefix of the class to check. - * @return The number in the class. - */ - getClassnameNumericSuffix(node: HTMLElement, prefix: string): number { - if (node.classList && node.classList.length) { - const patt1 = new RegExp('^' + prefix + '([0-9])+$'), - patt2 = new RegExp('([0-9])+$'); - - for (let index = 0; index < node.classList.length; index++) { - if (patt1.test(node.classList[index])) { - const match = patt2.exec(node.classList[index]); - - return Number(match[0]); - } - } - } - - this.logger.warn('Prefix "' + prefix + '" not found in class names.'); - } - - /** - * Get the group number of an element. It is extracted from the classes. - * - * @param node Element to check. - * @return Group number. - */ - getGroup(node: HTMLElement): number { - return this.getClassnameNumericSuffix(node, 'group'); - } - - /** - * Get the number of an element ('no'). It is extracted from the classes. - * - * @param node Element to check. - * @return Number. - */ - getNo(node: HTMLElement): number { - return this.getClassnameNumericSuffix(node, 'no'); - } - - /** - * Get the place number of an element. It is extracted from the classes. - * - * @param node Element to check. - * @return Place number. - */ - getPlace(node: HTMLElement): number { - return this.getClassnameNumericSuffix(node, 'place'); - } - - /** - * Initialize the question. - * - * @param question Question. - */ - initializer(question: any): void { - this.selectors = this.cssSelectors(question.slot); - - const container = this.container.querySelector(this.selectors.topNode()); - if (this.readOnly) { - container.classList.add('readonly'); - } else { - container.classList.add('notreadonly'); - } - - this.setPaddingSizesAll(); - this.cloneDragItems(); - this.initialPlaceOfDragItems(); - this.makeDropZones(); - - // Wait the DOM to be rendered. - setTimeout(() => { - this.positionDragItems(); - }); - - this.resizeFunction = this.positionDragItems.bind(this); - window.addEventListener('resize', this.resizeFunction); - } - - /** - * Initialize drag items, putting them in their initial place. - */ - initialPlaceOfDragItems(): void { - const drags = Array.from(this.container.querySelectorAll(this.selectors.drags())); - - // Add the class 'unplaced' to all elements. - drags.forEach((drag) => { - drag.classList.add('unplaced'); - }); - - this.placed = {}; - for (const placeNo in this.inputIds) { - const inputId = this.inputIds[placeNo], - inputNode = this.container.querySelector('input#' + inputId), - choiceNo = Number(inputNode.getAttribute('value')); - - if (choiceNo !== 0) { - const drop = this.container.querySelector(this.selectors.dropForPlace(parseInt(placeNo, 10) + 1)), - groupNo = this.getGroup(drop), - drag = this.container.querySelector( - this.selectors.unplacedDragsForChoiceInGroup(choiceNo, groupNo)); - - this.placeDragInDrop(drag, drop); - this.positionDragItem(drag); - } - } - } - - /** - * Make an element "draggable". In the mobile app, items are "dragged" using tap and drop. - * - * @param drag Element. - */ - makeDraggable(drag: HTMLElement): void { - drag.addEventListener('click', () => { - if (drag.classList.contains('selected')) { - this.deselectDrags(); - } else { - this.selectDrag(drag); - } - }); - } - - /** - * Convert an element into a drop zone. - * - * @param drop Element. - */ - makeDropZone(drop: HTMLElement): void { - drop.addEventListener('click', () => { - const drag = this.selected; - if (!drag) { - // No element selected, nothing to do. - return false; - } - - // Place it only if the same group is selected. - if (this.getGroup(drag) === this.getGroup(drop)) { - this.placeDragInDrop(drag, drop); - this.deselectDrags(); - this.positionDragItem(drag); - } - }); - } - - /** - * Create all drop zones. - */ - makeDropZones(): void { - if (this.readOnly) { - return; - } - - // Create all the drop zones. - const drops = Array.from(this.container.querySelectorAll(this.selectors.drops())); - drops.forEach((drop) => { - this.makeDropZone(drop); - }); - - // If home answer zone is clicked, return drag home. - const home = this.container.querySelector(this.selectors.topNode() + ' .answercontainer'); - - home.addEventListener('click', () => { - const drag = this.selected; - if (!drag) { - // No element selected, nothing to do. - return; - } - - // Not placed yet, deselect. - if (drag.classList.contains('unplaced')) { - this.deselectDrags(); - - return; - } - - // Remove, deselect and move back home in this order. - this.removeDragFromDrop(drag); - this.deselectDrags(); - this.positionDragItem(drag); - }); - } - - /** - * Set the width and height of an element. - * - * @param node Element. - * @param width Width to set. - * @param height Height to set. - */ - protected padToWidthHeight(node: HTMLElement, width: number, height: number): void { - node.style.width = width + 'px'; - node.style.height = height + 'px'; - // Originally lineHeight was set as height to center the text but it comes on too height lines on multiline elements. - } - - /** - * Place a draggable element inside a drop zone. - * - * @param drag Draggable element. - * @param drop Drop zone. - */ - placeDragInDrop(drag: HTMLElement, drop: HTMLElement): void { - - const placeNo = this.getPlace(drop), - inputId = this.inputIds[placeNo - 1], - inputNode = this.container.querySelector('input#' + inputId); - - // Set the value of the drag element in the input of the drop zone. - if (drag !== null) { - inputNode.setAttribute('value', String(this.getChoice(drag))); - } else { - inputNode.setAttribute('value', '0'); - } - - // Remove the element from the "placed" map if it's there. - for (const alreadyThereDragNo in this.placed) { - if (this.placed[alreadyThereDragNo] === placeNo) { - delete this.placed[alreadyThereDragNo]; - } - } - - if (drag !== null) { - // Add the element in the "placed" map. - this.placed[this.getNo(drag)] = placeNo; - } - } - - /** - * Position a drag element in the right drop zone or in the home zone. - * - * @param drag Drag element. - */ - positionDragItem(drag: HTMLElement): void { - let position; - - const placeNo = this.placed[this.getNo(drag)]; - if (!placeNo) { - // Not placed, put it in home zone. - const groupNo = this.getGroup(drag), - choiceNo = this.getChoice(drag); - - position = this.domUtils.getElementXY(this.container, this.selectors.dragHome(groupNo, choiceNo), 'answercontainer'); - drag.classList.add('unplaced'); - } else { - // Get the drop zone position. - position = this.domUtils.getElementXY(this.container, this.selectors.dropForPlace(placeNo), - 'addon-qtype-ddwtos-container'); - drag.classList.remove('unplaced'); - } - - if (position) { - drag.style.left = position[0] + 'px'; - drag.style.top = position[1] + 'px'; - } - } - - /** - * Postition, or reposition, all the drag items. They're placed in the right drop zone or in the home zone. - */ - positionDragItems(): void { - const drags = Array.from(this.container.querySelectorAll(this.selectors.drags())); - drags.forEach((drag) => { - this.positionDragItem(drag); - }); - } - - /** - * Remove a draggable element from a drop zone. - * - * @param drag The draggable element. - */ - removeDragFromDrop(drag: HTMLElement): void { - const placeNo = this.placed[this.getNo(drag)], - drop = this.container.querySelector(this.selectors.dropForPlace(placeNo)); - - this.placeDragInDrop(null, drop); - } - - /** - * Select a certain element as being "dragged". - * - * @param drag Element. - */ - selectDrag(drag: HTMLElement): void { - // Deselect previous drags, only 1 can be selected. - this.deselectDrags(); - - this.selected = drag; - drag.classList.add('selected'); - } - - /** - * Set the padding size for all groups. - */ - setPaddingSizesAll(): void { - for (let groupNo = 1; groupNo <= 8; groupNo++) { - this.setPaddingSizeForGroup(groupNo); - } - } - - /** - * Set the padding size for a certain group. - * - * @param groupNo Group number. - */ - setPaddingSizeForGroup(groupNo: number): void { - const groupItems = Array.from(this.container.querySelectorAll(this.selectors.dragHomesGroup(groupNo))); - - if (groupItems.length !== 0) { - let maxWidth = 0, - maxHeight = 0; - - // Find max height and width. - groupItems.forEach((item) => { - item.innerHTML = this.textUtils.decodeHTML(item.innerHTML); - maxWidth = Math.max(maxWidth, Math.ceil(item.offsetWidth)); - maxHeight = Math.max(maxHeight, Math.ceil(item.offsetHeight)); - }); - - maxWidth += 8; - maxHeight += 2; - groupItems.forEach((item) => { - this.padToWidthHeight(item, maxWidth, maxHeight); - }); - - const dropsGroup = Array.from(this.container.querySelectorAll(this.selectors.dropsGroup(groupNo))); - dropsGroup.forEach((item) => { - this.padToWidthHeight(item, maxWidth + 2, maxHeight + 2); - }); - } - } -} diff --git a/src/addon/qtype/ddwtos/component/addon-qtype-ddwtos.html b/src/addon/qtype/ddwtos/component/addon-qtype-ddwtos.html deleted file mode 100644 index b998f2fe0..000000000 --- a/src/addon/qtype/ddwtos/component/addon-qtype-ddwtos.html +++ /dev/null @@ -1,14 +0,0 @@ -
- - - - -

- - {{ 'core.question.howtodraganddrop' | translate }} -

-

- -
-
-
diff --git a/src/addon/qtype/ddwtos/component/ddwtos.scss b/src/addon/qtype/ddwtos/component/ddwtos.scss deleted file mode 100644 index 6bce91256..000000000 --- a/src/addon/qtype/ddwtos/component/ddwtos.scss +++ /dev/null @@ -1,132 +0,0 @@ -// Style ddwtos content a bit. Almost all these styles are copied from Moodle. -addon-qtype-ddwtos { - .qtext { - margin-bottom: 0.5em; - display: block; - } - - .draghome { - margin-bottom: 1em; - max-width: calc(100%); - } - - .answertext { - margin-bottom: 0.5em; - } - - .drop { - display: inline-block; - text-align: center; - border: 1px solid $gray-darker; - margin-bottom: 2px; - border-radius: 5px; - cursor: pointer; - } - .draghome, .drag { - display: inline-block; - text-align: center; - background: transparent; - border: 0; - white-space: normal; - overflow: visible; - word-wrap: break-word; - } - .draghome, .drag.unplaced{ - border: 1px solid $gray-darker; - } - .draghome { - visibility: hidden; - } - .drag { - z-index: 2; - border-radius: 5px; - line-height: 25px; - cursor: pointer; - } - .drag.selected { - z-index: 3; - box-shadow: $core-dd-question-selected-shadow; - } - - .drop.selected { - border-color: $yellow-light; - box-shadow: 0 0 5px 5px $yellow-light; - } - - &.notreadonly .drag, - &.notreadonly .draghome, - &.notreadonly .drop, - &.notreadonly .answercontainer { - cursor: pointer; - border-radius: 5px; - } - - &.readonly .drag, - &.readonly .draghome, - &.readonly .drop, - &.readonly .answercontainer { - cursor: default; - } - - span.incorrect { - background-color: $red-light; - @include darkmode() { - background-color: $red-dark; - } - } - span.correct { - background-color: $green-light; - @include darkmode() { - background-color: $green-dark; - } - } - - @for $i from 0 to length($core-dd-question-colors) { - .group#{$i + 1} { - background: nth($core-dd-question-colors, $i + 1); - color: $text-color; - } - } - - .group2 { - border-radius: 10px 0 0 0; - } - .group3 { - border-radius: 0 10px 0 0; - } - .group4 { - border-radius: 0 0 10px 0; - } - .group5 { - border-radius: 0 0 0 10px; - } - .group6 { - border-radius: 0 10px 10px 0; - } - .group7 { - border-radius: 10px 0 0 10px; - } - .group8 { - border-radius: 10px 10px 10px 10px; - } - - sub, sup { - font-size: 80%; - position: relative; - vertical-align: baseline; - } - sup { - top: -0.4em; - } - sub { - bottom: -0.2em; - } -} - -addon-qtype-ddwtos .item-md .draghome { - max-width: calc(100% - #{($item-md-padding-start)}); -} - -addon-qtype-ddwtos .item-ios .draghome { - max-width: calc(100% - #{($item-ios-padding-start)}); -} diff --git a/src/addon/qtype/ddwtos/component/ddwtos.ts b/src/addon/qtype/ddwtos/component/ddwtos.ts deleted file mode 100644 index 077677e27..000000000 --- a/src/addon/qtype/ddwtos/component/ddwtos.ts +++ /dev/null @@ -1,135 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, OnDestroy, Injector, ElementRef, ViewChild } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; -import { AddonQtypeDdwtosQuestion } from '../classes/ddwtos'; - -/** - * Component to render a drag-and-drop words into sentences question. - */ -@Component({ - selector: 'addon-qtype-ddwtos', - templateUrl: 'addon-qtype-ddwtos.html' -}) -export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent implements OnInit, OnDestroy { - @ViewChild('questiontext') questionTextEl: ElementRef; - - protected element: HTMLElement; - protected questionInstance: AddonQtypeDdwtosQuestion; - protected inputIds: string[] = []; // Ids of the inputs of the question (where the answers will be stored). - protected destroyed = false; - protected textIsRendered = false; - protected answerAreRendered = false; - - constructor(protected loggerProvider: CoreLoggerProvider, injector: Injector, element: ElementRef) { - super(loggerProvider, 'AddonQtypeDdwtosComponent', injector); - - this.element = element.nativeElement; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.question) { - this.logger.warn('Aborting because of no question received.'); - - return this.questionHelper.showComponentError(this.onAbort); - } - - const element = this.domUtils.convertToElement(this.question.html); - - // Replace Moodle's correct/incorrect and feedback classes with our own. - this.questionHelper.replaceCorrectnessClasses(element); - this.questionHelper.replaceFeedbackClasses(element); - - // Treat the correct/incorrect icons. - this.questionHelper.treatCorrectnessIcons(element); - - const answerContainer = element.querySelector('.answercontainer'); - if (!answerContainer) { - this.logger.warn('Aborting because of an error parsing question.', this.question.name); - - return this.questionHelper.showComponentError(this.onAbort); - } - - this.question.readOnly = answerContainer.classList.contains('readonly'); - this.question.answers = answerContainer.outerHTML; - - this.question.text = this.domUtils.getContentsOfElement(element, '.qtext'); - if (typeof this.question.text == 'undefined') { - this.logger.warn('Aborting because of an error parsing question.', this.question.name); - - return this.questionHelper.showComponentError(this.onAbort); - } - - // Get the inputs where the answers will be stored and add them to the question text. - const inputEls = Array.from(element.querySelectorAll('input[type="hidden"]:not([name*=sequencecheck])')); - - inputEls.forEach((inputEl) => { - this.question.text += inputEl.outerHTML; - this.inputIds.push(inputEl.getAttribute('id')); - }); - - this.question.loaded = false; - } - - /** - * The question answers have been rendered. - */ - answersRendered(): void { - this.answerAreRendered = true; - if (this.textIsRendered) { - this.questionRendered(); - } - } - - /** - * The question text has been rendered. - */ - textRendered(): void { - this.textIsRendered = true; - if (this.answerAreRendered) { - this.questionRendered(); - } - } - - /** - * The question has been rendered. - */ - protected questionRendered(): void { - if (!this.destroyed) { - this.domUtils.waitForImages(this.questionTextEl.nativeElement).then(() => { - // Create the instance. - this.questionInstance = new AddonQtypeDdwtosQuestion(this.loggerProvider, this.domUtils, this.element, - this.question, this.question.readOnly, this.inputIds, this.textUtils); - - this.questionHelper.treatCorrectnessIconsClicks(this.element, this.component, this.componentId, this.contextLevel, - this.contextInstanceId, this.courseId); - - this.question.loaded = true; - }); - } - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.destroyed = true; - this.questionInstance && this.questionInstance.destroy(); - } -} diff --git a/src/addon/qtype/ddwtos/ddwtos.module.ts b/src/addon/qtype/ddwtos/ddwtos.module.ts deleted file mode 100644 index 7f0044937..000000000 --- a/src/addon/qtype/ddwtos/ddwtos.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeDdwtosHandler } from './providers/handler'; -import { AddonQtypeDdwtosComponent } from './component/ddwtos'; - -@NgModule({ - declarations: [ - AddonQtypeDdwtosComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonQtypeDdwtosHandler - ], - exports: [ - AddonQtypeDdwtosComponent - ], - entryComponents: [ - AddonQtypeDdwtosComponent - ] -}) -export class AddonQtypeDdwtosModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeDdwtosHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/ddwtos/providers/handler.ts b/src/addon/qtype/ddwtos/providers/handler.ts deleted file mode 100644 index d310e27b2..000000000 --- a/src/addon/qtype/ddwtos/providers/handler.ts +++ /dev/null @@ -1,116 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionProvider } from '@core/question/providers/question'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeDdwtosComponent } from '../component/ddwtos'; - -/** - * Handler to support drag-and-drop words into sentences question type. - */ -@Injectable() -export class AddonQtypeDdwtosHandler implements CoreQuestionHandler { - name = 'AddonQtypeDdwtos'; - type = 'qtype_ddwtos'; - - constructor(private questionProvider: CoreQuestionProvider) { } - - /** - * Return the name of the behaviour to use for the question. - * If the question should use the default behaviour you shouldn't implement this function. - * - * @param question The question. - * @param behaviour The default behaviour. - * @return The behaviour to use. - */ - getBehaviour(question: any, behaviour: string): string { - if (behaviour === 'interactive') { - return 'interactivecountback'; - } - - return behaviour; - } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeDdwtosComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - for (const name in answers) { - const value = answers[name]; - if (!value || value === '0') { - return 0; - } - } - - return 1; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - for (const name in answers) { - const value = answers[name]; - if (value && value !== '0') { - return 1; - } - } - - return 0; - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.questionProvider.compareAllAnswers(prevAnswers, newAnswers); - } -} diff --git a/src/addon/qtype/description/component/addon-qtype-description.html b/src/addon/qtype/description/component/addon-qtype-description.html deleted file mode 100644 index 1264d7c70..000000000 --- a/src/addon/qtype/description/component/addon-qtype-description.html +++ /dev/null @@ -1,7 +0,0 @@ -
- - - -

-
-
diff --git a/src/addon/qtype/description/component/description.ts b/src/addon/qtype/description/component/description.ts deleted file mode 100644 index 1c9870d0b..000000000 --- a/src/addon/qtype/description/component/description.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; - -/** - * Component to render a description question. - */ -@Component({ - selector: 'addon-qtype-description', - templateUrl: 'addon-qtype-description.html' -}) -export class AddonQtypeDescriptionComponent extends CoreQuestionBaseComponent implements OnInit { - - constructor(logger: CoreLoggerProvider, injector: Injector) { - super(logger, 'AddonQtypeDescriptionComponent', injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const questionEl = this.initComponent(); - if (questionEl) { - // Get the "seen" hidden input. - const input = questionEl.querySelector('input[type="hidden"][name*=seen]'); - if (input) { - this.question.seenInput = { - name: input.name, - value: input.value - }; - } - } - } -} diff --git a/src/addon/qtype/description/description.module.ts b/src/addon/qtype/description/description.module.ts deleted file mode 100644 index c5fcfca79..000000000 --- a/src/addon/qtype/description/description.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeDescriptionHandler } from './providers/handler'; -import { AddonQtypeDescriptionComponent } from './component/description'; - -@NgModule({ - declarations: [ - AddonQtypeDescriptionComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreDirectivesModule - ], - providers: [ - AddonQtypeDescriptionHandler - ], - exports: [ - AddonQtypeDescriptionComponent - ], - entryComponents: [ - AddonQtypeDescriptionComponent - ] -}) -export class AddonQtypeDescriptionModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeDescriptionHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/description/providers/handler.ts b/src/addon/qtype/description/providers/handler.ts deleted file mode 100644 index 9fcb36ebf..000000000 --- a/src/addon/qtype/description/providers/handler.ts +++ /dev/null @@ -1,77 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeDescriptionComponent } from '../component/description'; - -/** - * Handler to support description question type. - */ -@Injectable() -export class AddonQtypeDescriptionHandler implements CoreQuestionHandler { - name = 'AddonQtypeDescription'; - type = 'qtype_description'; - - constructor() { - // Nothing to do. - } - - /** - * Return the name of the behaviour to use for the question. - * If the question should use the default behaviour you shouldn't implement this function. - * - * @param question The question. - * @param behaviour The default behaviour. - * @return The behaviour to use. - */ - getBehaviour(question: any, behaviour: string): string { - return 'informationitem'; - } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeDescriptionComponent; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Validate if an offline sequencecheck is valid compared with the online one. - * This function only needs to be implemented if a specific compare is required. - * - * @param question The question. - * @param offlineSequenceCheck Sequence check stored in offline. - * @return Whether sequencecheck is valid. - */ - validateSequenceCheck(question: any, offlineSequenceCheck: string): boolean { - // Descriptions don't have any answer so we'll always treat them as valid. - return true; - } -} diff --git a/src/addon/qtype/essay/component/addon-qtype-essay.html b/src/addon/qtype/essay/component/addon-qtype-essay.html deleted file mode 100644 index 13462423b..000000000 --- a/src/addon/qtype/essay/component/addon-qtype-essay.html +++ /dev/null @@ -1,42 +0,0 @@ -
- - -

-
- - - - - - - - - - - - - - -

{{ 'core.question.errorinlinefilesnotsupported' | translate }}

-
- -

-
-
- - - -

{{ 'core.question.errorattachmentsnotsupported' | translate }}

-
- - - -

-
- - -
- -
-
-
diff --git a/src/addon/qtype/essay/component/essay.ts b/src/addon/qtype/essay/component/essay.ts deleted file mode 100644 index 8cd380933..000000000 --- a/src/addon/qtype/essay/component/essay.ts +++ /dev/null @@ -1,43 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; -import { FormControl, FormBuilder } from '@angular/forms'; - -/** - * Component to render an essay question. - */ -@Component({ - selector: 'addon-qtype-essay', - templateUrl: 'addon-qtype-essay.html' -}) -export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent implements OnInit { - - protected formControl: FormControl; - - constructor(logger: CoreLoggerProvider, injector: Injector, protected fb: FormBuilder) { - super(logger, 'AddonQtypeEssayComponent', injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.initEssayComponent(); - - this.formControl = this.fb.control(this.question.textarea && this.question.textarea.text); - } -} diff --git a/src/addon/qtype/essay/essay.module.ts b/src/addon/qtype/essay/essay.module.ts deleted file mode 100644 index d259137cc..000000000 --- a/src/addon/qtype/essay/essay.module.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeEssayHandler } from './providers/handler'; -import { AddonQtypeEssayComponent } from './component/essay'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonQtypeEssayComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - ], - providers: [ - AddonQtypeEssayHandler - ], - exports: [ - AddonQtypeEssayComponent - ], - entryComponents: [ - AddonQtypeEssayComponent - ] -}) -export class AddonQtypeEssayModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeEssayHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/essay/providers/handler.ts b/src/addon/qtype/essay/providers/handler.ts deleted file mode 100644 index 9206b4e4e..000000000 --- a/src/addon/qtype/essay/providers/handler.ts +++ /dev/null @@ -1,153 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { AddonQtypeEssayComponent } from '../component/essay'; - -/** - * Handler to support essay question type. - */ -@Injectable() -export class AddonQtypeEssayHandler implements CoreQuestionHandler { - name = 'AddonQtypeEssay'; - type = 'qtype_essay'; - - constructor(private utils: CoreUtilsProvider, private questionHelper: CoreQuestionHelperProvider, - private textUtils: CoreTextUtilsProvider, private domUtils: CoreDomUtilsProvider) { } - - /** - * Return the name of the behaviour to use for the question. - * If the question should use the default behaviour you shouldn't implement this function. - * - * @param question The question. - * @param behaviour The default behaviour. - * @return The behaviour to use. - */ - getBehaviour(question: any, behaviour: string): string { - return 'manualgraded'; - } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeEssayComponent; - } - - /** - * Check if a question can be submitted. - * If a question cannot be submitted it should return a message explaining why (translated or not). - * - * @param question The question. - * @return Prevent submit message. Undefined or empty if can be submitted. - */ - getPreventSubmitMessage(question: any): string { - const element = this.domUtils.convertToElement(question.html); - - if (element.querySelector('div[id*=filemanager]')) { - // The question allows attachments. Since the app cannot attach files yet we will prevent submitting the question. - return 'core.question.errorattachmentsnotsupported'; - } - - if (this.questionHelper.hasDraftFileUrls(element.innerHTML)) { - return 'core.question.errorinlinefilesnotsupported'; - } - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - const element = this.domUtils.convertToElement(question.html); - - const hasInlineText = answers['answer'] && answers['answer'] !== '', - allowsAttachments = !!element.querySelector('div[id*=filemanager]'); - - if (!allowsAttachments) { - return hasInlineText ? 1 : 0; - } - - // We can't know if the attachments are required or if the user added any in web. - return -1; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - return 0; - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); - } - - /** - * Prepare and add to answers the data to send to server based in the input. Return promise if async. - * - * @param question Question. - * @param answers The answers retrieved from the form. Prepared answers must be stored in this object. - * @param offline Whether the data should be saved in offline. - * @param siteId Site ID. If not defined, current site. - * @return Return a promise resolved when done if async, void if sync. - */ - prepareAnswers(question: any, answers: any, offline: boolean, siteId?: string): void | Promise { - const element = this.domUtils.convertToElement(question.html); - - // Search the textarea to get its name. - const textarea = element.querySelector('textarea[name*=_answer]'); - - if (textarea && typeof answers[textarea.name] != 'undefined') { - // Add some HTML to the text if needed. - answers[textarea.name] = this.textUtils.formatHtmlLines(answers[textarea.name]); - } - } -} diff --git a/src/addon/qtype/gapselect/component/addon-qtype-gapselect.html b/src/addon/qtype/gapselect/component/addon-qtype-gapselect.html deleted file mode 100644 index 88b29f7fc..000000000 --- a/src/addon/qtype/gapselect/component/addon-qtype-gapselect.html +++ /dev/null @@ -1,5 +0,0 @@ -
- -

-
-
diff --git a/src/addon/qtype/gapselect/component/gapselect.scss b/src/addon/qtype/gapselect/component/gapselect.scss deleted file mode 100644 index 4698805aa..000000000 --- a/src/addon/qtype/gapselect/component/gapselect.scss +++ /dev/null @@ -1,23 +0,0 @@ -// Style gapselect content a bit. All these styles are copied from Moodle. -ion-app.app-root addon-qtype-gapselect { - p { - margin: 0 0 .5em; - } - - select { - height: 30px; - line-height: 30px; - display: inline-block; - border: 1px solid $gray-dark; - padding: 4px 6px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - margin-bottom: 10px; - background: $gray-lighter; - - @include darkmode() { - background: $gray-dark; - } - } -} diff --git a/src/addon/qtype/gapselect/component/gapselect.ts b/src/addon/qtype/gapselect/component/gapselect.ts deleted file mode 100644 index 7dd56017f..000000000 --- a/src/addon/qtype/gapselect/component/gapselect.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector, ElementRef } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; - -/** - * Component to render a gap select question. - */ -@Component({ - selector: 'addon-qtype-gapselect', - templateUrl: 'addon-qtype-gapselect.html' -}) -export class AddonQtypeGapSelectComponent extends CoreQuestionBaseComponent implements OnInit { - - protected element: HTMLElement; - - constructor(logger: CoreLoggerProvider, injector: Injector, element: ElementRef) { - super(logger, 'AddonQtypeGapSelectComponent', injector); - - this.element = element.nativeElement; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.initOriginalTextComponent('.qtext'); - } - - /** - * The question has been rendered. - */ - questionRendered(): void { - this.questionHelper.treatCorrectnessIconsClicks(this.element, this.component, this.componentId, this.contextLevel, - this.contextInstanceId, this.courseId); - } -} diff --git a/src/addon/qtype/gapselect/gapselect.module.ts b/src/addon/qtype/gapselect/gapselect.module.ts deleted file mode 100644 index 25e2a3293..000000000 --- a/src/addon/qtype/gapselect/gapselect.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeGapSelectHandler } from './providers/handler'; -import { AddonQtypeGapSelectComponent } from './component/gapselect'; - -@NgModule({ - declarations: [ - AddonQtypeGapSelectComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreDirectivesModule - ], - providers: [ - AddonQtypeGapSelectHandler - ], - exports: [ - AddonQtypeGapSelectComponent - ], - entryComponents: [ - AddonQtypeGapSelectComponent - ] -}) -export class AddonQtypeGapSelectModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeGapSelectHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/gapselect/providers/handler.ts b/src/addon/qtype/gapselect/providers/handler.ts deleted file mode 100644 index 50b83068e..000000000 --- a/src/addon/qtype/gapselect/providers/handler.ts +++ /dev/null @@ -1,118 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionProvider } from '@core/question/providers/question'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeGapSelectComponent } from '../component/gapselect'; - -/** - * Handler to support gapselect question type. - */ -@Injectable() -export class AddonQtypeGapSelectHandler implements CoreQuestionHandler { - name = 'AddonQtypeGapSelect'; - type = 'qtype_gapselect'; - - constructor(private questionProvider: CoreQuestionProvider) { } - - /** - * Return the name of the behaviour to use for the question. - * If the question should use the default behaviour you shouldn't implement this function. - * - * @param question The question. - * @param behaviour The default behaviour. - * @return The behaviour to use. - */ - getBehaviour(question: any, behaviour: string): string { - if (behaviour === 'interactive') { - return 'interactivecountback'; - } - - return behaviour; - } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeGapSelectComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - // We should always get a value for each select so we can assume we receive all the possible answers. - for (const name in answers) { - const value = answers[name]; - if (!value || value === '0') { - return 0; - } - } - - return 1; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - // We should always get a value for each select so we can assume we receive all the possible answers. - for (const name in answers) { - const value = answers[name]; - if (value) { - return 1; - } - } - - return 0; - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.questionProvider.compareAllAnswers(prevAnswers, newAnswers); - } -} diff --git a/src/addon/qtype/match/component/addon-qtype-match.html b/src/addon/qtype/match/component/addon-qtype-match.html deleted file mode 100644 index 08bbe1274..000000000 --- a/src/addon/qtype/match/component/addon-qtype-match.html +++ /dev/null @@ -1,26 +0,0 @@ -
- - - - - - - -

-
- - - - {{option.label}} - - - - - - - - -
-
-
-
diff --git a/src/addon/qtype/match/component/match.scss b/src/addon/qtype/match/component/match.scss deleted file mode 100644 index 91f127a0c..000000000 --- a/src/addon/qtype/match/component/match.scss +++ /dev/null @@ -1,17 +0,0 @@ -ion-app.app-root addon-qtype-match { - ion-col .select-disabled { - @include margin(0, 20px, 0, 0); - } - - .core-correct-icon { - bottom: 50%; - margin-bottom: -7px; - } - - ion-col.col { - align-self: center; - > p { - margin: 0; - } - } -} diff --git a/src/addon/qtype/match/component/match.ts b/src/addon/qtype/match/component/match.ts deleted file mode 100644 index b986ac00c..000000000 --- a/src/addon/qtype/match/component/match.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; - -/** - * Component to render a match question. - */ -@Component({ - selector: 'addon-qtype-match', - templateUrl: 'addon-qtype-match.html' -}) -export class AddonQtypeMatchComponent extends CoreQuestionBaseComponent implements OnInit { - - constructor(logger: CoreLoggerProvider, injector: Injector) { - super(logger, 'AddonQtypeMatchComponent', injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.initMatchComponent(); - } -} diff --git a/src/addon/qtype/match/match.module.ts b/src/addon/qtype/match/match.module.ts deleted file mode 100644 index 8d032e71e..000000000 --- a/src/addon/qtype/match/match.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeMatchHandler } from './providers/handler'; -import { AddonQtypeMatchComponent } from './component/match'; - -@NgModule({ - declarations: [ - AddonQtypeMatchComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonQtypeMatchHandler - ], - exports: [ - AddonQtypeMatchComponent - ], - entryComponents: [ - AddonQtypeMatchComponent - ] -}) -export class AddonQtypeMatchModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeMatchHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/match/providers/handler.ts b/src/addon/qtype/match/providers/handler.ts deleted file mode 100644 index 90d87d1d1..000000000 --- a/src/addon/qtype/match/providers/handler.ts +++ /dev/null @@ -1,118 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionProvider } from '@core/question/providers/question'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeMatchComponent } from '../component/match'; - -/** - * Handler to support match question type. - */ -@Injectable() -export class AddonQtypeMatchHandler implements CoreQuestionHandler { - name = 'AddonQtypeMatch'; - type = 'qtype_match'; - - constructor(private questionProvider: CoreQuestionProvider) { } - - /** - * Return the name of the behaviour to use for the question. - * If the question should use the default behaviour you shouldn't implement this function. - * - * @param question The question. - * @param behaviour The default behaviour. - * @return The behaviour to use. - */ - getBehaviour(question: any, behaviour: string): string { - if (behaviour === 'interactive') { - return 'interactivecountback'; - } - - return behaviour; - } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeMatchComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - // We should always get a value for each select so we can assume we receive all the possible answers. - for (const name in answers) { - const value = answers[name]; - if (!value || value === '0') { - return 0; - } - } - - return 1; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - // We should always get a value for each select so we can assume we receive all the possible answers. - for (const name in answers) { - const value = answers[name]; - if (value && value !== '0') { - return 1; - } - } - - return 0; - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.questionProvider.compareAllAnswers(prevAnswers, newAnswers); - } -} diff --git a/src/addon/qtype/multianswer/component/addon-qtype-multianswer.html b/src/addon/qtype/multianswer/component/addon-qtype-multianswer.html deleted file mode 100644 index 919da0149..000000000 --- a/src/addon/qtype/multianswer/component/addon-qtype-multianswer.html +++ /dev/null @@ -1,5 +0,0 @@ -
- - - -
diff --git a/src/addon/qtype/multianswer/component/multianswer.scss b/src/addon/qtype/multianswer/component/multianswer.scss deleted file mode 100644 index f63e3e5ca..000000000 --- a/src/addon/qtype/multianswer/component/multianswer.scss +++ /dev/null @@ -1,42 +0,0 @@ -// Style multianswer content a bit. All these styles are copied from Moodle. -addon-qtype-multianswer { - p { - margin: 0 0 .5em; - } - - .answer div.r0, .answer div.r1, .answer td.r0, .answer td.r1 { - padding: 0.3em; - } - - table { - width: 100%; - display: table; - } - - tr { - display: table-row; - } - - td { - display: table-cell; - } - - input, select { - @include border-radius(4px); - - display: inline-block; - border: 1px solid $gray-dark; - padding: 6px 8px; - @include margin-horizontal(2px); - margin-bottom: 10px; - } - - select { - height: 30px; - line-height: 30px; - } - - input[type="radio"], input[type="checkbox"] { - @include margin(-4px, 7px, null, null); - } -} diff --git a/src/addon/qtype/multianswer/component/multianswer.ts b/src/addon/qtype/multianswer/component/multianswer.ts deleted file mode 100644 index 7c2257113..000000000 --- a/src/addon/qtype/multianswer/component/multianswer.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector, ElementRef } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; - -/** - * Component to render a multianswer question. - */ -@Component({ - selector: 'addon-qtype-multianswer', - templateUrl: 'addon-qtype-multianswer.html' -}) -export class AddonQtypeMultiAnswerComponent extends CoreQuestionBaseComponent implements OnInit { - - protected element: HTMLElement; - - constructor(logger: CoreLoggerProvider, injector: Injector, element: ElementRef) { - super(logger, 'AddonQtypeMultiAnswerComponent', injector); - - this.element = element.nativeElement; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.initOriginalTextComponent('.formulation'); - } - - /** - * The question has been rendered. - */ - questionRendered(): void { - this.questionHelper.treatCorrectnessIconsClicks(this.element, this.component, this.componentId, this.contextLevel, - this.contextInstanceId, this.courseId); - } -} diff --git a/src/addon/qtype/multianswer/multianswer.module.ts b/src/addon/qtype/multianswer/multianswer.module.ts deleted file mode 100644 index 87458fd45..000000000 --- a/src/addon/qtype/multianswer/multianswer.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeMultiAnswerHandler } from './providers/handler'; -import { AddonQtypeMultiAnswerComponent } from './component/multianswer'; - -@NgModule({ - declarations: [ - AddonQtypeMultiAnswerComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreDirectivesModule - ], - providers: [ - AddonQtypeMultiAnswerHandler - ], - exports: [ - AddonQtypeMultiAnswerComponent - ], - entryComponents: [ - AddonQtypeMultiAnswerComponent - ] -}) -export class AddonQtypeMultiAnswerModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeMultiAnswerHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/multianswer/providers/handler.ts b/src/addon/qtype/multianswer/providers/handler.ts deleted file mode 100644 index 798dcf572..000000000 --- a/src/addon/qtype/multianswer/providers/handler.ts +++ /dev/null @@ -1,142 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionProvider } from '@core/question/providers/question'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; -import { AddonQtypeMultiAnswerComponent } from '../component/multianswer'; - -/** - * Handler to support multianswer question type. - */ -@Injectable() -export class AddonQtypeMultiAnswerHandler implements CoreQuestionHandler { - name = 'AddonQtypeMultiAnswer'; - type = 'qtype_multianswer'; - - constructor(private questionProvider: CoreQuestionProvider, private questionHelper: CoreQuestionHelperProvider) { } - - /** - * Return the name of the behaviour to use for the question. - * If the question should use the default behaviour you shouldn't implement this function. - * - * @param question The question. - * @param behaviour The default behaviour. - * @return The behaviour to use. - */ - getBehaviour(question: any, behaviour: string): string { - if (behaviour === 'interactive') { - return 'interactivecountback'; - } - - return behaviour; - } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeMultiAnswerComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - // Get all the inputs in the question to check if they've all been answered. - const names = this.questionProvider.getBasicAnswers(this.questionHelper.getAllInputNamesFromHtml(question.html)); - for (const name in names) { - const value = answers[name]; - if (!value && value !== false && value !== 0) { - return 0; - } - } - - return 1; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - // We should always get a value for each select so we can assume we receive all the possible answers. - for (const name in answers) { - const value = answers[name]; - if (value || value === false) { - return 1; - } - } - - return 0; - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.questionProvider.compareAllAnswers(prevAnswers, newAnswers); - } - - /** - * Validate if an offline sequencecheck is valid compared with the online one. - * This function only needs to be implemented if a specific compare is required. - * - * @param question The question. - * @param offlineSequenceCheck Sequence check stored in offline. - * @return Whether sequencecheck is valid. - */ - validateSequenceCheck(question: any, offlineSequenceCheck: string): boolean { - if (question.sequencecheck == offlineSequenceCheck) { - return true; - } - - // For some reason, viewing a multianswer for the first time without answering it creates a new step "todo". - // We'll treat this case as valid. - if (question.sequencecheck == 2 && question.state == 'todo' && offlineSequenceCheck == '1') { - return true; - } - - return false; - } -} diff --git a/src/addon/qtype/multichoice/component/addon-qtype-multichoice.html b/src/addon/qtype/multichoice/component/addon-qtype-multichoice.html deleted file mode 100644 index 32fc617f0..000000000 --- a/src/addon/qtype/multichoice/component/addon-qtype-multichoice.html +++ /dev/null @@ -1,42 +0,0 @@ -
- - - - {{ question.prompt }} - - - - - - - -
-
- - - - - - -
-
- - -
- - - -
-
- - - -
-
- -
- - - -
-
diff --git a/src/addon/qtype/multichoice/component/multichoice.scss b/src/addon/qtype/multichoice/component/multichoice.scss deleted file mode 100644 index 6312d0e50..000000000 --- a/src/addon/qtype/multichoice/component/multichoice.scss +++ /dev/null @@ -1,13 +0,0 @@ -ion-app.app-root addon-qtype-multichoice { - .core-correct-icon { - bottom: 50%; - margin-bottom: -7px; - } - - .specificfeedback { - background-color: $core-question-feedback-color-bg; - color: $core-question-feedback-color; - display: inline; - padding: 0 .7em; - } -} diff --git a/src/addon/qtype/multichoice/component/multichoice.ts b/src/addon/qtype/multichoice/component/multichoice.ts deleted file mode 100644 index ca2e46caa..000000000 --- a/src/addon/qtype/multichoice/component/multichoice.ts +++ /dev/null @@ -1,45 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; - -/** - * Component to render a multichoice question. - */ -@Component({ - selector: 'addon-qtype-multichoice', - templateUrl: 'addon-qtype-multichoice.html' -}) -export class AddonQtypeMultichoiceComponent extends CoreQuestionBaseComponent implements OnInit { - - constructor(logger: CoreLoggerProvider, injector: Injector) { - super(logger, 'AddonQtypeMultichoiceComponent', injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.initMultichoiceComponent(); - } - - /** - * Clear selected choices. - */ - clear(): void { - this.question.singleChoiceModel = null; - } -} diff --git a/src/addon/qtype/multichoice/multichoice.module.ts b/src/addon/qtype/multichoice/multichoice.module.ts deleted file mode 100644 index 5aea5339d..000000000 --- a/src/addon/qtype/multichoice/multichoice.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { AddonQtypeMultichoiceHandler } from './providers/handler'; -import { AddonQtypeMultichoiceComponent } from './component/multichoice'; - -@NgModule({ - declarations: [ - AddonQtypeMultichoiceComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonQtypeMultichoiceHandler - ], - exports: [ - AddonQtypeMultichoiceComponent - ], - entryComponents: [ - AddonQtypeMultichoiceComponent - ] -}) -export class AddonQtypeMultichoiceModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeMultichoiceHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/multichoice/providers/handler.ts b/src/addon/qtype/multichoice/providers/handler.ts deleted file mode 100644 index 92424fbd2..000000000 --- a/src/addon/qtype/multichoice/providers/handler.ts +++ /dev/null @@ -1,171 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeMultichoiceComponent } from '../component/multichoice'; - -/** - * Handler to support multichoice question type. - */ -@Injectable() -export class AddonQtypeMultichoiceHandler implements CoreQuestionHandler { - name = 'AddonQtypeMultichoice'; - type = 'qtype_multichoice'; - - constructor(private utils: CoreUtilsProvider) { } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeMultichoiceComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - let isSingle = true, - isMultiComplete = false; - - // To know if it's single or multi answer we need to search for answers with "choice" in the name. - for (const name in answers) { - if (name.indexOf('choice') != -1) { - isSingle = false; - if (answers[name]) { - isMultiComplete = true; - } - } - } - - if (isSingle) { - // Single. - return this.isCompleteResponseSingle(answers); - } else { - // Multi. - return isMultiComplete ? 1 : 0; - } - } - - /** - * Check if a response is complete. Only for single answer. - * - * @param question The question.uestion answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponseSingle(answers: any): number { - return (answers['answer'] && answers['answer'] !== '') ? 1 : 0; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - return this.isCompleteResponse(question, answers); - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. Only for single answer. - * - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponseSingle(answers: any): number { - return this.isCompleteResponseSingle(answers); - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - let isSingle = true, - isMultiSame = true; - - // To know if it's single or multi answer we need to search for answers with "choice" in the name. - for (const name in newAnswers) { - if (name.indexOf('choice') != -1) { - isSingle = false; - if (!this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, name)) { - isMultiSame = false; - } - } - } - - if (isSingle) { - return this.isSameResponseSingle(prevAnswers, newAnswers); - } else { - return isMultiSame ; - } - } - - /** - * Check if two responses are the same. Only for single answer. - * - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponseSingle(prevAnswers: any, newAnswers: any): boolean { - return this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); - } - - /** - * Prepare and add to answers the data to send to server based in the input. Return promise if async. - * - * @param question Question. - * @param answers The answers retrieved from the form. Prepared answers must be stored in this object. - * @param offline Whether the data should be saved in offline. - * @param siteId Site ID. If not defined, current site. - * @return Return a promise resolved when done if async, void if sync. - */ - prepareAnswers(question: any, answers: any, offline: boolean, siteId?: string): void | Promise { - if (question && !question.multi && typeof answers[question.optionsName] != 'undefined' && !answers[question.optionsName]) { - /* It's a single choice and the user hasn't answered. Delete the answer because - sending an empty string (default value) will mark the first option as selected. */ - delete answers[question.optionsName]; - } - } -} diff --git a/src/addon/qtype/numerical/numerical.module.ts b/src/addon/qtype/numerical/numerical.module.ts deleted file mode 100644 index ed4ce256e..000000000 --- a/src/addon/qtype/numerical/numerical.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { AddonQtypeNumericalHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQtypeNumericalHandler - ] -}) -export class AddonQtypeNumericalModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeNumericalHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/numerical/providers/handler.ts b/src/addon/qtype/numerical/providers/handler.ts deleted file mode 100644 index b68cc15ac..000000000 --- a/src/addon/qtype/numerical/providers/handler.ts +++ /dev/null @@ -1,27 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { AddonQtypeCalculatedHandler } from '@addon/qtype/calculated/providers/handler'; - -/** - * Handler to support numerical question type. - * This question type depends on calculated question type. - */ -@Injectable() -export class AddonQtypeNumericalHandler extends AddonQtypeCalculatedHandler { - name = 'AddonQtypeNumerical'; - type = 'qtype_numerical'; -} diff --git a/src/addon/qtype/qtype.module.ts b/src/addon/qtype/qtype.module.ts deleted file mode 100644 index 2b2c3bbd5..000000000 --- a/src/addon/qtype/qtype.module.ts +++ /dev/null @@ -1,57 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonQtypeCalculatedModule } from './calculated/calculated.module'; -import { AddonQtypeCalculatedMultiModule } from './calculatedmulti/calculatedmulti.module'; -import { AddonQtypeCalculatedSimpleModule } from './calculatedsimple/calculatedsimple.module'; -import { AddonQtypeDdImageOrTextModule } from './ddimageortext/ddimageortext.module'; -import { AddonQtypeDdMarkerModule } from './ddmarker/ddmarker.module'; -import { AddonQtypeDdwtosModule } from './ddwtos/ddwtos.module'; -import { AddonQtypeDescriptionModule } from './description/description.module'; -import { AddonQtypeEssayModule } from './essay/essay.module'; -import { AddonQtypeGapSelectModule } from './gapselect/gapselect.module'; -import { AddonQtypeMatchModule } from './match/match.module'; -import { AddonQtypeMultiAnswerModule } from './multianswer/multianswer.module'; -import { AddonQtypeMultichoiceModule } from './multichoice/multichoice.module'; -import { AddonQtypeNumericalModule } from './numerical/numerical.module'; -import { AddonQtypeRandomSaMatchModule } from './randomsamatch/randomsamatch.module'; -import { AddonQtypeShortAnswerModule } from './shortanswer/shortanswer.module'; -import { AddonQtypeTrueFalseModule } from './truefalse/truefalse.module'; - -@NgModule({ - declarations: [], - imports: [ - AddonQtypeCalculatedModule, - AddonQtypeCalculatedMultiModule, - AddonQtypeCalculatedSimpleModule, - AddonQtypeDdImageOrTextModule, - AddonQtypeDdMarkerModule, - AddonQtypeDdwtosModule, - AddonQtypeDescriptionModule, - AddonQtypeEssayModule, - AddonQtypeGapSelectModule, - AddonQtypeMatchModule, - AddonQtypeMultiAnswerModule, - AddonQtypeMultichoiceModule, - AddonQtypeNumericalModule, - AddonQtypeRandomSaMatchModule, - AddonQtypeShortAnswerModule, - AddonQtypeTrueFalseModule - ], - providers: [ - ], - exports: [] -}) -export class AddonQtypeModule { } diff --git a/src/addon/qtype/randomsamatch/providers/handler.ts b/src/addon/qtype/randomsamatch/providers/handler.ts deleted file mode 100644 index a0eede940..000000000 --- a/src/addon/qtype/randomsamatch/providers/handler.ts +++ /dev/null @@ -1,90 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeMatchHandler } from '@addon/qtype/match/providers/handler'; -import { AddonQtypeMatchComponent } from '@addon/qtype/match/component/match'; - -/** - * Handler to support random short-answer matching question type. - */ -@Injectable() -export class AddonQtypeRandomSaMatchHandler implements CoreQuestionHandler { - name = 'AddonQtypeRandomSaMatch'; - type = 'qtype_randomsamatch'; - - constructor(private matchHandler: AddonQtypeMatchHandler) { } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - // Random behaves like a match question, use the same component. - return AddonQtypeMatchComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - // This question behaves like a match question. - return this.matchHandler.isCompleteResponse(question, answers); - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - // This question behaves like a match question. - return this.matchHandler.isGradableResponse(question, answers); - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - // This question behaves like a match question. - return this.matchHandler.isSameResponse(question, prevAnswers, newAnswers); - } -} diff --git a/src/addon/qtype/randomsamatch/randomsamatch.module.ts b/src/addon/qtype/randomsamatch/randomsamatch.module.ts deleted file mode 100644 index 53e72cf1e..000000000 --- a/src/addon/qtype/randomsamatch/randomsamatch.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { AddonQtypeRandomSaMatchHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQtypeRandomSaMatchHandler - ] -}) -export class AddonQtypeRandomSaMatchModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeRandomSaMatchHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/shortanswer/component/addon-qtype-shortanswer.html b/src/addon/qtype/shortanswer/component/addon-qtype-shortanswer.html deleted file mode 100644 index 5cfaf0139..000000000 --- a/src/addon/qtype/shortanswer/component/addon-qtype-shortanswer.html +++ /dev/null @@ -1,11 +0,0 @@ -
- - - - - {{ 'addon.mod_quiz.answercolon' | translate }} - - - - -
diff --git a/src/addon/qtype/shortanswer/component/shortanswer.scss b/src/addon/qtype/shortanswer/component/shortanswer.scss deleted file mode 100644 index 02c8bd099..000000000 --- a/src/addon/qtype/shortanswer/component/shortanswer.scss +++ /dev/null @@ -1,18 +0,0 @@ -addon-qtype-shortanswer { - .addon-qtype-shortanswer-text { - ion-label.label { - margin-bottom: 0; - } - - input { - @include placeholder($text-input-placeholder-color); - @include appearance(none); - @include border-radius(4px); - display: inline-block; - border: 1px solid $gray-dark; - padding: 6px 8px; - @include margin-horizontal(2px); - margin-bottom: 10px; - } - } -} diff --git a/src/addon/qtype/shortanswer/component/shortanswer.ts b/src/addon/qtype/shortanswer/component/shortanswer.ts deleted file mode 100644 index 0cc904986..000000000 --- a/src/addon/qtype/shortanswer/component/shortanswer.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, Injector } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component'; - -/** - * Component to render a short answer question. - */ -@Component({ - selector: 'addon-qtype-shortanswer', - templateUrl: 'addon-qtype-shortanswer.html' -}) -export class AddonQtypeShortAnswerComponent extends CoreQuestionBaseComponent implements OnInit { - - constructor(logger: CoreLoggerProvider, injector: Injector) { - super(logger, 'AddonQtypeShortAnswerComponent', injector); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.initInputTextComponent(); - } -} diff --git a/src/addon/qtype/shortanswer/providers/handler.ts b/src/addon/qtype/shortanswer/providers/handler.ts deleted file mode 100644 index f9629fa75..000000000 --- a/src/addon/qtype/shortanswer/providers/handler.ts +++ /dev/null @@ -1,86 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeShortAnswerComponent } from '../component/shortanswer'; - -/** - * Handler to support short answer question type. - */ -@Injectable() -export class AddonQtypeShortAnswerHandler implements CoreQuestionHandler { - name = 'AddonQtypeShortAnswer'; - type = 'qtype_shortanswer'; - - constructor(private utils: CoreUtilsProvider) { } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - return AddonQtypeShortAnswerComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - return (answers['answer'] || answers['answer'] === 0) ? 1 : 0; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - return this.isCompleteResponse(question, answers); - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); - } -} diff --git a/src/addon/qtype/shortanswer/shortanswer.module.ts b/src/addon/qtype/shortanswer/shortanswer.module.ts deleted file mode 100644 index bb16c81dc..000000000 --- a/src/addon/qtype/shortanswer/shortanswer.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { AddonQtypeShortAnswerHandler } from './providers/handler'; -import { AddonQtypeShortAnswerComponent } from './component/shortanswer'; - -@NgModule({ - declarations: [ - AddonQtypeShortAnswerComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonQtypeShortAnswerHandler - ], - exports: [ - AddonQtypeShortAnswerComponent - ], - entryComponents: [ - AddonQtypeShortAnswerComponent - ] -}) -export class AddonQtypeShortAnswerModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeShortAnswerHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/qtype/truefalse/providers/handler.ts b/src/addon/qtype/truefalse/providers/handler.ts deleted file mode 100644 index f811dfa65..000000000 --- a/src/addon/qtype/truefalse/providers/handler.ts +++ /dev/null @@ -1,103 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreQuestionHandler } from '@core/question/providers/delegate'; -import { AddonQtypeMultichoiceComponent } from '@addon/qtype/multichoice/component/multichoice'; - -/** - * Handler to support true/false question type. - */ -@Injectable() -export class AddonQtypeTrueFalseHandler implements CoreQuestionHandler { - name = 'AddonQtypeTrueFalse'; - type = 'qtype_truefalse'; - - constructor(private utils: CoreUtilsProvider) { } - - /** - * Return the Component to use to display the question. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @param question The question to render. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector, question: any): any | Promise { - // True/false behaves like a multichoice, use the same component. - return AddonQtypeMultichoiceComponent; - } - - /** - * Check if a response is complete. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if complete, 0 if not complete, -1 if cannot determine. - */ - isCompleteResponse(question: any, answers: any): number { - return answers['answer'] ? 1 : 0; - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Check if a student has provided enough of an answer for the question to be graded automatically, - * or whether it must be considered aborted. - * - * @param question The question. - * @param answers Object with the question answers (without prefix). - * @return 1 if gradable, 0 if not gradable, -1 if cannot determine. - */ - isGradableResponse(question: any, answers: any): number { - return this.isCompleteResponse(question, answers); - } - - /** - * Check if two responses are the same. - * - * @param question Question. - * @param prevAnswers Object with the previous question answers. - * @param newAnswers Object with the new question answers. - * @return Whether they're the same. - */ - isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean { - return this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer'); - } - - /** - * Prepare and add to answers the data to send to server based in the input. Return promise if async. - * - * @param question Question. - * @param answers The answers retrieved from the form. Prepared answers must be stored in this object. - * @param offline Whether the data should be saved in offline. - * @param siteId Site ID. If not defined, current site. - * @return Return a promise resolved when done if async, void if sync. - */ - prepareAnswers(question: any, answers: any, offline: boolean, siteId?: string): void | Promise { - if (question && typeof answers[question.optionsName] != 'undefined' && !answers[question.optionsName]) { - // The user hasn't answered. Delete the answer to prevent marking one of the answers automatically. - delete answers[question.optionsName]; - } - } -} diff --git a/src/addon/qtype/truefalse/truefalse.module.ts b/src/addon/qtype/truefalse/truefalse.module.ts deleted file mode 100644 index f6e6eae68..000000000 --- a/src/addon/qtype/truefalse/truefalse.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreQuestionDelegate } from '@core/question/providers/delegate'; -import { AddonQtypeTrueFalseHandler } from './providers/handler'; - -@NgModule({ - declarations: [ - ], - providers: [ - AddonQtypeTrueFalseHandler - ] -}) -export class AddonQtypeTrueFalseModule { - constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeTrueFalseHandler) { - questionDelegate.registerHandler(handler); - } -} diff --git a/src/addon/remotethemes/providers/remotethemes.ts b/src/addon/remotethemes/providers/remotethemes.ts deleted file mode 100644 index 3017cb9da..000000000 --- a/src/addon/remotethemes/providers/remotethemes.ts +++ /dev/null @@ -1,371 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { CoreWSProvider } from '@providers/ws'; -import { CoreAppProvider } from '@providers/app'; -import { CoreFileProvider } from '@providers/file'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreConstants } from '@core/constants'; -import { Md5 } from 'ts-md5/dist/md5'; - -/** - * Service to handle remote themes. A remote theme is a CSS sheet stored in the site that allows customising the Mobile app. - */ -@Injectable() -export class AddonRemoteThemesProvider { - static COMPONENT = 'mmaRemoteStyles'; - protected SEPARATOR_35 = /\/\*\*? *3\.5(\.0)? *styles? *\*\//i; // A comment like "/* 3.5 styles */". - protected TMP_SITE_ID = 'tmpsite'; - - protected logger; - protected stylesEls: {[siteId: string]: {element: HTMLStyleElement, hash: string}} = {}; - - constructor(logger: CoreLoggerProvider, - protected sitesProvider: CoreSitesProvider, - protected fileProvider: CoreFileProvider, - protected filepoolProvider: CoreFilepoolProvider, - protected wsProvider: CoreWSProvider, - protected utils: CoreUtilsProvider, - protected appProvider: CoreAppProvider) { - this.logger = logger.getInstance('AddonRemoteThemesProvider'); - } - - /** - * Add a style element for a site and load the styles for that element. The style will be disabled. - * - * @param siteId Site ID. - * @return Promise resolved when added and loaded. - */ - addSite(siteId: string): Promise { - if (!siteId || this.stylesEls[siteId]) { - // Invalid site ID or style already added. - return Promise.resolve(); - } - - // Create the style and add it to the header. - this.initSiteStyleElement(siteId, true); - - return this.load(siteId, true).catch((error) => { - this.logger.error('Error loading site after site init', error); - }); - } - - /** - * Clear styles added to the DOM, disabling them all. - */ - clear(): void { - // Disable all the styles. - this.disableElementsBySelector('style[id*=mobilecssurl]'); - - // Set StatusBar properties. - this.appProvider.setStatusBarColor(); - } - - /** - * Create a style element. - * - * @param id ID to set to the element. - * @param disabled Whether the element should be disabled. - * @return New element. - */ - protected createStyleElement(id: string, disabled: boolean): HTMLStyleElement { - const styleEl = document.createElement('style'); - - styleEl.setAttribute('id', id); - this.disableElement(styleEl, disabled); - - return styleEl; - } - - /** - * Enabled or disable a certain style element. - * - * @param element The element to enable or disable. - * @param disable Whether to disable or enable the element. - */ - disableElement(element: HTMLStyleElement, disable: boolean): void { - // Setting disabled should be enough, but we also set the attribute so it can be seen in the DOM which ones are disabled. - if (disable) { - element.disabled = true; - element.setAttribute('disabled', 'disabled'); - } else { - element.disabled = false; - element.removeAttribute('disabled'); - - if (element.innerHTML != '') { - this.appProvider.resetStatusBarColor(); - } - } - } - - /** - * Disable all the style elements based on a query selector. - * - * @param selector The selector to get the style elements. - */ - protected disableElementsBySelector(selector: string): void { - const styles = Array.from(document.querySelectorAll(selector)); - - styles.forEach((style) => { - this.disableElement(style, true); - }); - } - - /** - * Downloads a CSS file and remove old files if needed. - * - * @param siteId Site ID. - * @param url File URL. - * @return Promise resolved when the file is downloaded. - */ - protected downloadFileAndRemoveOld(siteId: string, url: string): Promise { - // Check if the file is downloaded. - return this.filepoolProvider.getFileStateByUrl(siteId, url).then((state) => { - return state !== CoreConstants.NOT_DOWNLOADED; - }).catch(() => { - return true; // An error occurred while getting state (shouldn't happen). Don't delete downloaded file. - }).then((isDownloaded) => { - if (!isDownloaded) { - // File not downloaded, URL has changed or first time. Delete downloaded CSS files. - return this.filepoolProvider.removeFilesByComponent(siteId, AddonRemoteThemesProvider.COMPONENT, 1); - } - }).then(() => { - - return this.filepoolProvider.downloadUrl(siteId, url, false, AddonRemoteThemesProvider.COMPONENT, 1); - }); - } - - /** - * Enable the styles of a certain site. - * - * @param siteId Site ID. If not defined, current site. - */ - enable(siteId?: string): void { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (this.stylesEls[siteId]) { - this.disableElement(this.stylesEls[siteId].element, false); - } - } - - /** - * Get remote styles of a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the styles and the URL of the CSS file, - * resolved with undefined if no styles to load. - */ - get(siteId?: string): Promise<{fileUrl: string, styles: string}> { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - return this.sitesProvider.getSite(siteId).then((site) => { - const infos = site.getInfo(); - let promise, - fileUrl; - - if (infos && infos.mobilecssurl) { - fileUrl = infos.mobilecssurl; - - if (this.fileProvider.isAvailable()) { - // The file system is available. Download the file and remove old CSS files if needed. - promise = this.downloadFileAndRemoveOld(siteId, fileUrl); - } else { - // Return the online URL. - promise = Promise.resolve(fileUrl); - } - } else { - if (infos && infos.mobilecssurl === '') { - // CSS URL is empty. Delete downloaded files (if any). - this.filepoolProvider.removeFilesByComponent(siteId, AddonRemoteThemesProvider.COMPONENT, 1); - } - - return; - } - - return promise.then(async (url) => { - this.logger.debug('Loading styles from: ', url); - - // Get the CSS content using HTTP because we will treat the styles before saving them in the file. - const text = await this.wsProvider.getText(url); - - return {fileUrl: fileUrl, styles: this.get35Styles(text)}; - }); - }); - } - - /** - * Check if the CSS code has a separator for 3.5 styles. If it does, get only the styles after the separator. - * - * @param cssCode The CSS code to check. - * @return The filtered styles. - */ - protected get35Styles(cssCode: string): string { - const separatorPos = cssCode.search(this.SEPARATOR_35); - if (separatorPos > -1) { - return cssCode.substr(separatorPos).replace(this.SEPARATOR_35, ''); - } - - return cssCode; - } - - /** - * Init the style element for a site. - * - * @param siteId Site ID. - * @param disabled Whether the element should be disabled. - */ - protected initSiteStyleElement(siteId: string, disabled: boolean): void { - if (this.stylesEls[siteId]) { - // Already initialized, ignore. - return; - } - - // Create the style and add it to the header. - const styleEl = this.createStyleElement('mobilecssurl-' + siteId, disabled); - - document.head.appendChild(styleEl); - this.stylesEls[siteId] = { - element: styleEl, - hash: '' - }; - } - - /** - * Load styles for a certain site. - * - * @param siteId Site ID. If not defined, current site. - * @param disabled Whether loaded styles should be disabled. - * @return Promise resolved when styles are loaded. - */ - load(siteId?: string, disabled?: boolean): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - disabled = !!disabled; - - this.logger.debug('Load site', siteId, disabled); - - if (siteId && this.stylesEls[siteId]) { - // Enable or disable the styles. - this.disableElement(this.stylesEls[siteId].element, disabled); - - return this.get(siteId).then((data) => { - if (typeof data == 'undefined') { - // Nothing to load. - return; - } - - const hash = Md5.hashAsciiStr(data.styles); - - // Update the styles only if they have changed. - if (this.stylesEls[siteId].hash !== hash) { - this.stylesEls[siteId].element.innerHTML = data.styles; - this.stylesEls[siteId].hash = hash; - - // Adding styles to a style element automatically enables it. Disable it again. - if (disabled) { - this.disableElement(this.stylesEls[siteId].element, true); - } - } - - // Styles have been loaded, now treat the CSS. - this.filepoolProvider.treatCSSCode(siteId, data.fileUrl, data.styles, AddonRemoteThemesProvider.COMPONENT, 2) - .catch(() => { - // Ignore errors. - }); - }); - } - - return Promise.reject(null); - } - - /** - * Load styles for a temporary site. These styles aren't prefetched. - * - * @param url URL to get the styles from. - * @return Promise resolved when loaded. - */ - loadTmpStyles(url: string): Promise { - if (!url) { - return Promise.resolve(); - } - - return this.wsProvider.getText(url).then((text) => { - text = this.get35Styles(text); - - this.initSiteStyleElement(this.TMP_SITE_ID, false); - this.stylesEls[this.TMP_SITE_ID].element.innerHTML = text; - }); - } - - /** - * Load styles for a temporary site, given its public config. These styles aren't prefetched. - * - * @param config Site public config. - * @return Promise resolved when loaded. - */ - loadTmpStylesForSiteConfig(config: any): Promise { - return this.loadTmpStyles(config.mobilecssurl); - } - - /** - * Preload the styles of the current site (stored in DB). - * - * @return Promise resolved when loaded. - */ - preloadCurrentSite(): Promise { - return this.sitesProvider.getStoredCurrentSiteId().then((siteId) => { - return this.addSite(siteId); - }, () => { - // No current site stored. - }); - } - - /** - * Preload the styles of all the stored sites. - * - * @return Promise resolved when loaded. - */ - preloadSites(): Promise { - return this.sitesProvider.getSitesIds().then((ids) => { - const promises = []; - ids.forEach((siteId) => { - promises.push(this.addSite(siteId)); - }); - - return this.utils.allPromises(promises); - }); - } - - /** - * Remove the styles of a certain site. - * - * @param siteId Site ID. - */ - removeSite(siteId: string): void { - if (siteId && this.stylesEls[siteId]) { - document.head.removeChild(this.stylesEls[siteId].element); - delete this.stylesEls[siteId]; - } - } - - /** - * Unload styles for a temporary site. - */ - unloadTmpStyles(): void { - return this.removeSite(this.TMP_SITE_ID); - } -} diff --git a/src/addon/remotethemes/remotethemes.module.ts b/src/addon/remotethemes/remotethemes.module.ts deleted file mode 100644 index e4e78cb87..000000000 --- a/src/addon/remotethemes/remotethemes.module.ts +++ /dev/null @@ -1,121 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonRemoteThemesProvider } from './providers/remotethemes'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreInitDelegate } from '@providers/init'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; - -// List of providers (without handlers). -export const ADDON_REMOTETHEMES_PROVIDERS: any[] = [ - AddonRemoteThemesProvider -]; - -@NgModule({ - declarations: [ - ], - imports: [ - ], - providers: [ - AddonRemoteThemesProvider - ] -}) -export class AddonRemoteThemesModule { - constructor(initDelegate: CoreInitDelegate, remoteThemesProvider: AddonRemoteThemesProvider, eventsProvider: CoreEventsProvider, - sitesProvider: CoreSitesProvider, loggerProvider: CoreLoggerProvider) { - - const logger = loggerProvider.getInstance('AddonRemoteThemesModule'); - - // Preload the current site styles. - initDelegate.registerProcess({ - name: 'AddonRemoteThemesPreloadCurrent', - priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 250, - blocking: true, - load: remoteThemesProvider.preloadCurrentSite.bind(remoteThemesProvider) - }); - - // Preload the styles of the rest of sites. - initDelegate.registerProcess({ - name: 'AddonRemoteThemesPreload', - blocking: true, - load: remoteThemesProvider.preloadSites.bind(remoteThemesProvider) - }); - - let addingSite; - - // When a new site is added to the app, add its styles. - eventsProvider.on(CoreEventsProvider.SITE_ADDED, (data) => { - addingSite = data.siteId; - - remoteThemesProvider.addSite(data.siteId).catch((error) => { - logger.error('Error adding site', error); - }).then(() => { - if (addingSite == data.siteId) { - addingSite = false; - } - - // User has logged in, remove tmp styles and enable loaded styles. - if (data.siteId == sitesProvider.getCurrentSiteId()) { - remoteThemesProvider.unloadTmpStyles(); - remoteThemesProvider.enable(data.siteId); - } - }); - }); - - // Update styles when current site is updated. - eventsProvider.on(CoreEventsProvider.SITE_UPDATED, (data) => { - if (data.siteId === sitesProvider.getCurrentSiteId()) { - remoteThemesProvider.load(data.siteId).catch((error) => { - logger.error('Error loading site after site update', error); - }); - } - }); - - // Enable styles of current site on login. - eventsProvider.on(CoreEventsProvider.LOGIN, (data) => { - remoteThemesProvider.unloadTmpStyles(); - remoteThemesProvider.enable(data.siteId); - }); - - // Disable added styles on logout. - eventsProvider.on(CoreEventsProvider.LOGOUT, (data) => { - remoteThemesProvider.clear(); - }); - - // Remove site styles when a site is deleted. - eventsProvider.on(CoreEventsProvider.SITE_DELETED, (site) => { - remoteThemesProvider.removeSite(site.id); - }); - - // Load temporary styles when site config is checked in login. - eventsProvider.on(CoreEventsProvider.LOGIN_SITE_CHECKED, (data) => { - remoteThemesProvider.loadTmpStylesForSiteConfig(data.config).catch((error) => { - logger.error('Error loading tmp styles', error); - }); - }); - - // Unload temporary styles when site config is "unchecked" in login. - eventsProvider.on(CoreEventsProvider.LOGIN_SITE_UNCHECKED, (data) => { - if (data.siteId && data.siteId === addingSite) { - // The tmp styles are from a site that is being added permanently. - // Wait for the final site styles to be loaded before removing the tmp styles so there is no blink effect. - } else { - // The tmp styles are from a site that wasn't added in the end. Just remove them. - remoteThemesProvider.unloadTmpStyles(); - } - }); - } -} diff --git a/src/addon/storagemanager/lang/en.json b/src/addon/storagemanager/lang/en.json deleted file mode 100644 index 8efc60a30..000000000 --- a/src/addon/storagemanager/lang/en.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "deletecourse": "Offload all course data", - "deletecourses": "Offload all courses data", - "deletedatafrom": "Offload data from {{name}}", - "info": "Files stored on your device make the app work faster and enable the app to be used offline. You can safely offload files if you need to free up storage space.", - "managestorage": "Manage storage", - "storageused": "File storage used:" -} diff --git a/src/addon/storagemanager/pages/course-storage/course-storage.html b/src/addon/storagemanager/pages/course-storage/course-storage.html deleted file mode 100644 index 88dffd229..000000000 --- a/src/addon/storagemanager/pages/course-storage/course-storage.html +++ /dev/null @@ -1,59 +0,0 @@ - - - {{ 'addon.storagemanager.managestorage' | translate }} - - - - - - -

{{ course.displayname }}

-

{{ 'addon.storagemanager.info' | translate }}

- - -

{{ 'addon.storagemanager.storageused' | translate }}

-
-

{{ totalSize | coreBytesToSize }}

-
- -
-
-
- - - - -

{{ section.name }}

-

- - {{ section.totalSize | coreBytesToSize }} -

- -
-
- - - -

- - {{ module.name }} -

-

- - {{ module.totalSize | coreBytesToSize }} -

- -
-
-
-
-
-
-
diff --git a/src/addon/storagemanager/pages/course-storage/course-storage.module.ts b/src/addon/storagemanager/pages/course-storage/course-storage.module.ts deleted file mode 100644 index ae12c685a..000000000 --- a/src/addon/storagemanager/pages/course-storage/course-storage.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonStorageManagerCourseStoragePage } from './course-storage'; - -@NgModule({ - declarations: [ - AddonStorageManagerCourseStoragePage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonStorageManagerCourseStoragePage), - TranslateModule.forChild() - ], -}) -export class AddonStorageManagerCourseStoragePageModule { -} diff --git a/src/addon/storagemanager/pages/course-storage/course-storage.scss b/src/addon/storagemanager/pages/course-storage/course-storage.scss deleted file mode 100644 index 77e6022d7..000000000 --- a/src/addon/storagemanager/pages/course-storage/course-storage.scss +++ /dev/null @@ -1,23 +0,0 @@ -ion-app.app-root page-addon-storagemanager-course-storage { - .item-md.item-block .item-inner { - padding-right: 0; - padding-left: 0; - } - ion-card.section ion-card-header.card-header { - border-bottom: 1px solid $list-border-color; - margin-bottom: 8px; - padding-top: 8px; - padding-bottom: 8px; - } - ion-card.section h2 { - font-weight: bold; - font-size: 2rem; - } - - .core-module-icon { - margin-right: 4px; - width: 16px; - height: 16px; - display: inline; - } -} diff --git a/src/addon/storagemanager/pages/course-storage/course-storage.ts b/src/addon/storagemanager/pages/course-storage/course-storage.ts deleted file mode 100644 index 7dace4b7b..000000000 --- a/src/addon/storagemanager/pages/course-storage/course-storage.ts +++ /dev/null @@ -1,223 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, Content, NavParams } from 'ionic-angular'; -import { CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; -import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreConstants } from '@core/constants'; - -/** - * Page that displays the amount of file storage used by each activity on the course, and allows - * the user to delete these files. - */ -@IonicPage({ segment: 'addon-storagemanager-course-storage' }) -@Component({ - selector: 'page-addon-storagemanager-course-storage', - templateUrl: 'course-storage.html', -}) -export class AddonStorageManagerCourseStoragePage { - @ViewChild(Content) content: Content; - - course: any; - loaded: boolean; - sections: any; - totalSize: number; - - constructor(navParams: NavParams, - private courseProvider: CoreCourseProvider, - private prefetchDelegate: CoreCourseModulePrefetchDelegate, - private courseHelperProvider: CoreCourseHelperProvider, - private domUtils: CoreDomUtilsProvider, - private translate: TranslateService) { - - this.course = navParams.get('course'); - } - - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.courseProvider.getSections(this.course.id, false, true).then((sections) => { - this.courseHelperProvider.addHandlerDataForModules(sections, this.course.id); - this.sections = sections; - this.totalSize = 0; - - const allPromises = []; - this.sections.forEach((section) => { - section.totalSize = 0; - section.modules.forEach((module) => { - module.parentSection = section; - module.totalSize = 0; - // Note: This function only gets the size for modules which are downloadable. - // For other modules it always returns 0, even if they have downloaded some files. - // However there is no 100% reliable way to actually track the files in this case. - // You can maybe guess it based on the component and componentid. - // But these aren't necessarily consistent, for example mod_frog vs mmaModFrog. - // There is nothing enforcing correct values. - // Most modules which have large files are downloadable, so I think this is sufficient. - const promise = this.prefetchDelegate.getModuleStoredSize(module, this.course.id). - then((size) => { - // There are some cases where the return from this is not a valid number. - if (!isNaN(size)) { - module.totalSize = Number(size); - section.totalSize += size; - this.totalSize += size; - } - }); - allPromises.push(promise); - }); - }); - - Promise.all(allPromises).then(() => { - this.loaded = true; - - if (this.totalSize == 0) { - this.markCourseAsNotDownloaded(); - } - }); - }); - } - - /** - * The user has requested a delete for the whole course data. - * - * (This works by deleting data for each module on the course that has data.) - */ - async deleteForCourse(): Promise { - try { - await this.domUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); - } catch (error) { - if (!error.coreCanceled) { - throw error; - } - - return; - } - - const modules = []; - this.sections.forEach((section) => { - section.modules.forEach((module) => { - if (module.totalSize > 0) { - modules.push(module); - } - }); - }); - - this.deleteModules(modules); - } - - /** - * The user has requested a delete for a section's data. - * - * (This works by deleting data for each module in the section that has data.) - * - * @param section Section object with information about section and modules - */ - async deleteForSection(section: any): Promise { - try { - await this.domUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); - } catch (error) { - if (!error.coreCanceled) { - throw error; - } - - return; - } - - const modules = []; - section.modules.forEach((module) => { - if (module.totalSize > 0) { - modules.push(module); - } - }); - - this.deleteModules(modules); - } - - /** - * The user has requested a delete for a module's data - * - * @param module Module details - */ - async deleteForModule(module: any): Promise { - if (module.totalSize === 0) { - return; - } - - try { - await this.domUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); - } catch (error) { - if (!error.coreCanceled) { - throw error; - } - - return; - } - - this.deleteModules([module]); - } - - /** - * Deletes the specified modules, showing the loading overlay while it happens. - * - * @param modules Modules to delete - * @return Promise Once deleting has finished - */ - protected deleteModules(modules: any[]): Promise { - const modal = this.domUtils.showModalLoading(); - - const promises = []; - modules.forEach((module) => { - // Remove the files. - const promise = this.courseHelperProvider.removeModuleStoredData(module, this.course.id).then(() => { - // When the files and cache are removed, update the size. - module.parentSection.totalSize -= module.totalSize; - this.totalSize -= module.totalSize; - module.totalSize = 0; - }); - promises.push(promise); - }); - - return Promise.all(promises).then(() => { - modal.dismiss(); - }).catch((error) => { - modal.dismiss(); - - this.domUtils.showErrorModalDefault(error, this.translate.instant('core.errordeletefile')); - }).finally(() => { - // @TODO This is a workaround that should be more specific solving MOBILE-3305. - // Also should take into account all modules are not downloaded. - - // Mark course as not downloaded if course size is 0. - if (this.totalSize == 0) { - this.markCourseAsNotDownloaded(); - } - }); - } - - /** - * Mark course as not downloaded. - */ - protected markCourseAsNotDownloaded(): void { - // @TODO This is a workaround that should be more specific solving MOBILE-3305. - // Also should take into account all modules are not downloaded. - // Check after MOBILE-3188 is integrated. - - this.courseProvider.setCourseStatus(this.course.id, CoreConstants.NOT_DOWNLOADED); - } -} diff --git a/src/addon/storagemanager/pages/courses-storage/courses-storage.html b/src/addon/storagemanager/pages/courses-storage/courses-storage.html deleted file mode 100644 index 937ddd2be..000000000 --- a/src/addon/storagemanager/pages/courses-storage/courses-storage.html +++ /dev/null @@ -1,40 +0,0 @@ - - - {{ 'addon.storagemanager.managestorage' | translate }} - - - - - - -

{{ 'core.courses.courses' | translate }}

-

{{ 'addon.storagemanager.info' | translate }}

- - -

{{ 'addon.storagemanager.storageused' | translate }}

-
-

{{ totalSize | coreBytesToSize }}

-
- -
-
-
- - - -

{{ course.displayname }}

-

{{ 'core.downloading' | translate }}

-

- - {{ course.totalSize | coreBytesToSize }} -

- -
-
-
-
-
diff --git a/src/addon/storagemanager/pages/courses-storage/courses-storage.module.ts b/src/addon/storagemanager/pages/courses-storage/courses-storage.module.ts deleted file mode 100644 index 95a210991..000000000 --- a/src/addon/storagemanager/pages/courses-storage/courses-storage.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { AddonStorageManagerCoursesStoragePage } from './courses-storage'; - -@NgModule({ - declarations: [ - AddonStorageManagerCoursesStoragePage, - ], - imports: [ - CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, - IonicPageModule.forChild(AddonStorageManagerCoursesStoragePage), - TranslateModule.forChild() - ], -}) -export class AddonStorageManagerCoursesStoragePageModule { -} diff --git a/src/addon/storagemanager/pages/courses-storage/courses-storage.scss b/src/addon/storagemanager/pages/courses-storage/courses-storage.scss deleted file mode 100644 index b26c3fb94..000000000 --- a/src/addon/storagemanager/pages/courses-storage/courses-storage.scss +++ /dev/null @@ -1,28 +0,0 @@ -ion-app.app-root page-addon-storagemanager-courses-storage { - - .item-md.item-block .item-inner { - padding-right: 0; - padding-left: 0; - } - - ion-item.course { - border-bottom: 1px solid $list-border-color; - padding-right: 16px; - padding-left: 16px; - - h2 { - font-weight: bold; - font-size: 2rem; - } - - h3 { - color: $subdued-text-color; - } - - &:last-child { - border-bottom: 0; - } - - } - -} diff --git a/src/addon/storagemanager/pages/courses-storage/courses-storage.ts b/src/addon/storagemanager/pages/courses-storage/courses-storage.ts deleted file mode 100644 index 5beb27b09..000000000 --- a/src/addon/storagemanager/pages/courses-storage/courses-storage.ts +++ /dev/null @@ -1,218 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { IonicPage } from 'ionic-angular'; -import { CoreCourse, CoreCourseProvider } from '@core/course/providers/course'; -import { CoreCourses } from '@core/courses/providers/courses'; -import { CoreArray } from '@singletons/array'; -import { CoreCourseModulePrefetch } from '@core/course/providers/module-prefetch-delegate'; -import { CoreConstants } from '@core/constants'; -import { CoreDomUtils } from '@providers/utils/dom'; -import { Translate } from '@singletons/core.singletons'; -import { CoreEvents, CoreEventsProvider, CoreEventObserver } from '@providers/events'; -import { CoreCourseHelper } from '@core/course/providers/helper'; - -/** - * Core course data. - */ -interface Course { - id: number; - displayname: string; -} - -/** - * Downloaded course data. - */ -interface DownloadedCourse extends Course { - totalSize: number; - isDownloading: boolean; -} - -/** - * Page that displays downloaded courses and allows the user to delete them. - */ -@IonicPage({ segment: 'addon-storagemanager-courses-storage' }) -@Component({ - selector: 'page-addon-storagemanager-courses-storage', - templateUrl: 'courses-storage.html', -}) -export class AddonStorageManagerCoursesStoragePage { - - userCourses: Course[] = []; - downloadedCourses: DownloadedCourse[] = []; - completelyDownloadedCourses: DownloadedCourse[] = []; - totalSize = 0; - loaded = false; - - courseStatusObserver: CoreEventObserver; - - /** - * View loaded. - */ - async ionViewDidLoad(): Promise { - this.userCourses = await CoreCourses.instance.getUserCourses(); - this.courseStatusObserver = CoreEvents.instance.on( - CoreEventsProvider.COURSE_STATUS_CHANGED, - ({ courseId, status }) => this.onCourseUpdated(courseId, status), - ); - - const downloadedCourseIds = await CoreCourse.instance.getDownloadedCourseIds(); - const downloadedCourses = await Promise.all( - this.userCourses - .filter((course) => downloadedCourseIds.indexOf(course.id) !== -1) - .map((course) => this.getDownloadedCourse(course)), - ); - - this.setDownloadedCourses(downloadedCourses); - - this.loaded = true; - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.courseStatusObserver && this.courseStatusObserver.off(); - } - - /** - * Delete all courses that have been downloaded. - */ - async deleteCompletelyDownloadedCourses(): Promise { - try { - await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletestoreddata'); - } catch (error) { - if (!error.coreCanceled) { - throw error; - } - - return; - } - - const modal = CoreDomUtils.instance.showModalLoading(); - const deletedCourseIds = this.completelyDownloadedCourses.map((course) => course.id); - - try { - await Promise.all(deletedCourseIds.map((courseId) => CoreCourseHelper.instance.deleteCourseFiles(courseId))); - - this.setDownloadedCourses(this.downloadedCourses.filter((course) => !CoreArray.contains(deletedCourseIds, course.id))); - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, Translate.instance.instant('core.errordeletefile')); - } finally { - modal.dismiss(); - } - } - - /** - * Delete course. - * - * @param course Course to delete. - */ - async deleteCourse(course: DownloadedCourse): Promise { - try { - await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletestoreddata'); - } catch (error) { - if (!error.coreCanceled) { - throw error; - } - - return; - } - - const modal = CoreDomUtils.instance.showModalLoading(); - - try { - await CoreCourseHelper.instance.deleteCourseFiles(course.id); - - this.setDownloadedCourses(CoreArray.withoutItem(this.downloadedCourses, course)); - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, Translate.instance.instant('core.errordeletefile')); - } finally { - modal.dismiss(); - } - } - - /** - * Handle course updated event. - * - * @param courseId Updated course id. - */ - private async onCourseUpdated(courseId: number, status: string): Promise { - if (courseId == CoreCourseProvider.ALL_COURSES_CLEARED) { - this.setDownloadedCourses([]); - - return; - } - - const course = this.downloadedCourses.find((course) => course.id === courseId); - - if (!course) { - return; - } - - course.isDownloading = status === CoreConstants.DOWNLOADING; - course.totalSize = await this.calculateDownloadedCourseSize(course.id); - - this.setDownloadedCourses(this.downloadedCourses); - } - - /** - * Set downloaded courses data. - * - * @param courses Courses info. - */ - private setDownloadedCourses(courses: DownloadedCourse[]): void { - this.downloadedCourses = courses; - this.completelyDownloadedCourses = courses.filter((course) => !course.isDownloading); - this.totalSize = this.downloadedCourses.reduce((totalSize, course) => totalSize + course.totalSize, 0); - } - - /** - * Get downloaded course data. - * - * @param course Course. - * @return Course info. - */ - private async getDownloadedCourse(course: Course): Promise { - const totalSize = await this.calculateDownloadedCourseSize(course.id); - const status = await CoreCourse.instance.getCourseStatus(course.id); - - return { - ...course, - totalSize, - isDownloading: status === CoreConstants.DOWNLOADING, - }; - } - - /** - * Calculate the size of a downloaded course. - * - * @param courseId Downloaded course id. - * @return Promise to be resolved with the course size. - */ - private async calculateDownloadedCourseSize(courseId: number): Promise { - const sections = await CoreCourse.instance.getSections(courseId); - const modules = CoreArray.flatten(sections.map((section) => section.modules)); - const promisedModuleSizes = modules.map(async (module) => { - const size = await CoreCourseModulePrefetch.instance.getModuleStoredSize(module, courseId); - - return isNaN(size) ? 0 : size; - }); - const moduleSizes = await Promise.all(promisedModuleSizes); - - return moduleSizes.reduce((totalSize, moduleSize) => totalSize + moduleSize, 0); - } - -} diff --git a/src/addon/storagemanager/providers/coursemenu-handler.ts b/src/addon/storagemanager/providers/coursemenu-handler.ts deleted file mode 100644 index e03d02eed..000000000 --- a/src/addon/storagemanager/providers/coursemenu-handler.ts +++ /dev/null @@ -1,64 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable, Injector } from '@angular/core'; -import { CoreCourseOptionsMenuHandler, CoreCourseOptionsMenuHandlerData } from '@core/course/providers/options-delegate'; - -/** - * Handler to inject an option into course menu so that user can get to the manage storage page. - */ -@Injectable() -export class AddonStorageManagerCourseMenuHandler implements CoreCourseOptionsMenuHandler { - name = 'AddonStorageManager'; - priority = 500; - isMenuHandler = true; - - /** - * Checks if the handler is enabled for specified course. This handler is always available. - * - * @param courseId Course id - * @param accessData Access data - * @param navOptions Navigation options if any - * @param admOptions Admin options if any - * @return True - */ - isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { - return true; - } - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Returns the data needed to render the handler. - * - * @param injector Injector. - * @param course The course. - * @return Data needed to render the handler. - */ - getMenuDisplayData(injector: Injector, course: any): CoreCourseOptionsMenuHandlerData { - return { - icon: 'cube', - title: 'addon.storagemanager.managestorage', - page: 'AddonStorageManagerCourseStoragePage', - class: 'addon-storagemanager-coursemenu-handler' - }; - } -} diff --git a/src/addon/storagemanager/storagemanager.module.ts b/src/addon/storagemanager/storagemanager.module.ts deleted file mode 100644 index c4b10a802..000000000 --- a/src/addon/storagemanager/storagemanager.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { AddonStorageManagerCourseMenuHandler } from '@addon/storagemanager/providers/coursemenu-handler'; -import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; - -@NgModule({ - declarations: [], - imports: [ - ], - providers: [ - AddonStorageManagerCourseMenuHandler - ], - exports: [] -}) -export class AddonStorageManagerModule { - constructor(private courseOptionsDelegate: CoreCourseOptionsDelegate, - private courseMenuHandler: AddonStorageManagerCourseMenuHandler) { - // Register handlers. - this.courseOptionsDelegate.registerHandler(this.courseMenuHandler); - } -} diff --git a/src/addon/userprofilefield/checkbox/checkbox.module.ts b/src/addon/userprofilefield/checkbox/checkbox.module.ts deleted file mode 100644 index 8c7b92724..000000000 --- a/src/addon/userprofilefield/checkbox/checkbox.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonUserProfileFieldCheckboxHandler } from './providers/handler'; -import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldCheckboxComponent } from './component/checkbox'; -import { CoreComponentsModule } from '@components/components.module'; - -@NgModule({ - declarations: [ - AddonUserProfileFieldCheckboxComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule - ], - providers: [ - AddonUserProfileFieldCheckboxHandler - ], - exports: [ - AddonUserProfileFieldCheckboxComponent - ], - entryComponents: [ - AddonUserProfileFieldCheckboxComponent - ] -}) -export class AddonUserProfileFieldCheckboxModule { - constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldCheckboxHandler) { - userProfileFieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/userprofilefield/checkbox/component/addon-user-profile-field-checkbox.html b/src/addon/userprofilefield/checkbox/component/addon-user-profile-field-checkbox.html deleted file mode 100644 index 086d5534f..000000000 --- a/src/addon/userprofilefield/checkbox/component/addon-user-profile-field-checkbox.html +++ /dev/null @@ -1,19 +0,0 @@ - - -

{{ field.name }}

-

- {{ 'core.yes' | translate }} -

-

- {{ 'core.no' | translate }} -

-
- - - - {{ field.name }} - - - - - \ No newline at end of file diff --git a/src/addon/userprofilefield/checkbox/component/checkbox.ts b/src/addon/userprofilefield/checkbox/component/checkbox.ts deleted file mode 100644 index 80a8143ab..000000000 --- a/src/addon/userprofilefield/checkbox/component/checkbox.ts +++ /dev/null @@ -1,54 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Directive to render a checkbox user profile field. - */ -@Component({ - selector: 'addon-user-profile-field-checkbox', - templateUrl: 'addon-user-profile-field-checkbox.html' -}) -export class AddonUserProfileFieldCheckboxComponent implements OnInit { - @Input() field: any; // The profile field to be rendered. - @Input() edit = false; // True if editing the field. Defaults to false. - @Input() disabled = false; // True if disabled. Defaults to false. - @Input() form: FormGroup; // Form where to add the form control. - @Input() contextLevel?: string; // The context level. - @Input() contextInstanceId?: number; // The instance ID related to the context. - - constructor(private fb: FormBuilder, protected utils: CoreUtilsProvider) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const field = this.field; - - if (field && this.edit && this.form) { - field.modelName = 'profile_field_' + field.shortname; - - // Initialize the value. - const formData = { - value: this.utils.isTrueOrOne(field.defaultdata), - disabled: this.disabled - }; - this.form.addControl(field.modelName, this.fb.control(formData, - field.required && !field.locked ? Validators.requiredTrue : null)); - } - } -} diff --git a/src/addon/userprofilefield/checkbox/providers/handler.ts b/src/addon/userprofilefield/checkbox/providers/handler.ts deleted file mode 100644 index 0c63b417b..000000000 --- a/src/addon/userprofilefield/checkbox/providers/handler.ts +++ /dev/null @@ -1,71 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldCheckboxComponent } from '../component/checkbox'; - -/** - * Checkbox user profile field handlers. - */ -@Injectable() -export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFieldHandler { - name = 'AddonUserProfileFieldCheckbox'; - type = 'checkbox'; - - constructor() { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Get the data to send for the field based on the input data. - * - * @param field User field to get the data for. - * @param signup True if user is in signup page. - * @param registerAuth Register auth method. E.g. 'email'. - * @param formValues Form Values. - * @return Data to send for the field. - */ - getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { - const name = 'profile_field_' + field.shortname; - - if (typeof formValues[name] != 'undefined') { - return { - type: 'checkbox', - name: name, - value: formValues[name] ? 1 : 0 - }; - } - } - - /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonUserProfileFieldCheckboxComponent; - } -} diff --git a/src/addon/userprofilefield/datetime/component/addon-user-profile-field-datetime.html b/src/addon/userprofilefield/datetime/component/addon-user-profile-field-datetime.html deleted file mode 100644 index a3d9a57f2..000000000 --- a/src/addon/userprofilefield/datetime/component/addon-user-profile-field-datetime.html +++ /dev/null @@ -1,11 +0,0 @@ - - -

{{ field.name }}

-

{{ field.value * 1000 | coreFormatDate }}

-
- - - {{ field.name }} - - - \ No newline at end of file diff --git a/src/addon/userprofilefield/datetime/component/datetime.ts b/src/addon/userprofilefield/datetime/component/datetime.ts deleted file mode 100644 index a9b66bf1f..000000000 --- a/src/addon/userprofilefield/datetime/component/datetime.ts +++ /dev/null @@ -1,80 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Directive to render a datetime user profile field. - */ -@Component({ - selector: 'addon-user-profile-field-datetime', - templateUrl: 'addon-user-profile-field-datetime.html' -}) -export class AddonUserProfileFieldDatetimeComponent implements OnInit { - @Input() field: any; // The profile field to be rendered. - @Input() edit = false; // True if editing the field. Defaults to false. - @Input() disabled = false; // True if disabled. Defaults to false. - @Input() form?: FormGroup; // Form where to add the form control. - @Input() contextLevel?: string; // The context level. - @Input() contextInstanceId?: number; // The instance ID related to the context. - - constructor(private fb: FormBuilder, private timeUtils: CoreTimeUtilsProvider, protected utils: CoreUtilsProvider, - private translate: TranslateService) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const field = this.field; - let year; - - if (field && this.edit && this.form) { - field.modelName = 'profile_field_' + field.shortname; - - // Check if it's only date or it has time too. - const hasTime = this.utils.isTrueOrOne(field.param3); - - // Calculate format to use. - field.format = this.timeUtils.fixFormatForDatetime(this.timeUtils.convertPHPToMoment( - this.translate.instant('core.' + (hasTime ? 'strftimedatetime' : 'strftimedate')))); - - // Check min value. - if (field.param1) { - year = parseInt(field.param1, 10); - if (year) { - field.min = year; - } - } - - // Check max value. - if (field.param2) { - year = parseInt(field.param2, 10); - if (year) { - field.max = year; - } - } - - const formData = { - value: field.defaultdata, - disabled: this.disabled - }; - this.form.addControl(field.modelName, this.fb.control(formData, - field.required && !field.locked ? Validators.required : null)); - } - } -} diff --git a/src/addon/userprofilefield/datetime/datetime.module.ts b/src/addon/userprofilefield/datetime/datetime.module.ts deleted file mode 100644 index 05054e3c8..000000000 --- a/src/addon/userprofilefield/datetime/datetime.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonUserProfileFieldDatetimeHandler } from './providers/handler'; -import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldDatetimeComponent } from './component/datetime'; -import { CoreComponentsModule } from '@components/components.module'; -import { CorePipesModule } from '@pipes/pipes.module'; - -@NgModule({ - declarations: [ - AddonUserProfileFieldDatetimeComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CorePipesModule - ], - providers: [ - AddonUserProfileFieldDatetimeHandler - ], - exports: [ - AddonUserProfileFieldDatetimeComponent - ], - entryComponents: [ - AddonUserProfileFieldDatetimeComponent - ] -}) -export class AddonUserProfileFieldDatetimeModule { - constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldDatetimeHandler) { - userProfileFieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/userprofilefield/datetime/providers/handler.ts b/src/addon/userprofilefield/datetime/providers/handler.ts deleted file mode 100644 index 875ca740c..000000000 --- a/src/addon/userprofilefield/datetime/providers/handler.ts +++ /dev/null @@ -1,72 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { AddonUserProfileFieldDatetimeComponent } from '../component/datetime'; - -/** - * Datetime user profile field handlers. - */ -@Injectable() -export class AddonUserProfileFieldDatetimeHandler implements CoreUserProfileFieldHandler { - name = 'AddonUserProfileFieldDatetime'; - type = 'datetime'; - - constructor(protected timeUtils: CoreTimeUtilsProvider) { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Get the data to send for the field based on the input data. - * - * @param field User field to get the data for. - * @param signup True if user is in signup page. - * @param registerAuth Register auth method. E.g. 'email'. - * @param formValues Form Values. - * @return Data to send for the field. - */ - getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { - const name = 'profile_field_' + field.shortname; - - if (formValues[name]) { - return { - type: 'datetime', - name: 'profile_field_' + field.shortname, - value: this.timeUtils.convertToTimestamp(formValues[name]) - }; - } - } - - /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonUserProfileFieldDatetimeComponent; - } -} diff --git a/src/addon/userprofilefield/menu/component/addon-user-profile-field-menu.html b/src/addon/userprofilefield/menu/component/addon-user-profile-field-menu.html deleted file mode 100644 index ac8f11b04..000000000 --- a/src/addon/userprofilefield/menu/component/addon-user-profile-field-menu.html +++ /dev/null @@ -1,14 +0,0 @@ - - -

{{ field.name }}

-

-
- - - {{ field.name }} - - {{ 'core.choosedots' | translate }} - {{option}} - - - diff --git a/src/addon/userprofilefield/menu/component/menu.ts b/src/addon/userprofilefield/menu/component/menu.ts deleted file mode 100644 index c4365a44f..000000000 --- a/src/addon/userprofilefield/menu/component/menu.ts +++ /dev/null @@ -1,62 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; - -/** - * Directive to render a menu user profile field. - */ -@Component({ - selector: 'addon-user-profile-field-menu', - templateUrl: 'addon-user-profile-field-menu.html' -}) -export class AddonUserProfileFieldMenuComponent implements OnInit { - @Input() field: any; // The profile field to be rendered. - @Input() edit = false; // True if editing the field. Defaults to false. - @Input() disabled = false; // True if disabled. Defaults to false. - @Input() form?: FormGroup; // Form where to add the form control. - @Input() contextLevel?: string; // The context level. - @Input() contextInstanceId?: number; // The instance ID related to the context. - @Input() courseId?: number; // The course the field belongs to (if any). - - constructor(private fb: FormBuilder) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const field = this.field; - - if (field && this.edit && this.form) { - field.modelName = 'profile_field_' + field.shortname; - - // Parse options. - if (field.param1) { - field.options = field.param1.split(/\r\n|\r|\n/g); - } else { - field.options = []; - } - - const formData = { - value: field.defaultdata, - disabled: this.disabled - }; - // Initialize the value using default data. - this.form.addControl(field.modelName, this.fb.control(formData, - field.required && !field.locked ? Validators.required : null)); - } - - } -} diff --git a/src/addon/userprofilefield/menu/menu.module.ts b/src/addon/userprofilefield/menu/menu.module.ts deleted file mode 100644 index 138c65ad4..000000000 --- a/src/addon/userprofilefield/menu/menu.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonUserProfileFieldMenuHandler } from './providers/handler'; -import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldMenuComponent } from './component/menu'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonUserProfileFieldMenuComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonUserProfileFieldMenuHandler - ], - exports: [ - AddonUserProfileFieldMenuComponent - ], - entryComponents: [ - AddonUserProfileFieldMenuComponent - ] -}) -export class AddonUserProfileFieldMenuModule { - constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldMenuHandler) { - userProfileFieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/userprofilefield/menu/providers/handler.ts b/src/addon/userprofilefield/menu/providers/handler.ts deleted file mode 100644 index 0be1f3dfc..000000000 --- a/src/addon/userprofilefield/menu/providers/handler.ts +++ /dev/null @@ -1,71 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldMenuComponent } from '../component/menu'; - -/** - * Menu user profile field handlers. - */ -@Injectable() -export class AddonUserProfileFieldMenuHandler implements CoreUserProfileFieldHandler { - name = 'AddonUserProfileFieldMenu'; - type = 'menu'; - - constructor() { - // Nothing to do. - } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Get the data to send for the field based on the input data. - * - * @param field User field to get the data for. - * @param signup True if user is in signup page. - * @param registerAuth Register auth method. E.g. 'email'. - * @param formValues Form Values. - * @return Data to send for the field. - */ - getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { - const name = 'profile_field_' + field.shortname; - - if (formValues[name]) { - return { - type: 'menu', - name: name, - value: formValues[name] - }; - } - } - - /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonUserProfileFieldMenuComponent; - } -} diff --git a/src/addon/userprofilefield/text/component/addon-user-profile-field-text.html b/src/addon/userprofilefield/text/component/addon-user-profile-field-text.html deleted file mode 100644 index 977c7cd49..000000000 --- a/src/addon/userprofilefield/text/component/addon-user-profile-field-text.html +++ /dev/null @@ -1,11 +0,0 @@ - - -

{{ field.name }}

-

-
- - - {{ field.name }} - - - diff --git a/src/addon/userprofilefield/text/component/text.ts b/src/addon/userprofilefield/text/component/text.ts deleted file mode 100644 index 1eda97aca..000000000 --- a/src/addon/userprofilefield/text/component/text.ts +++ /dev/null @@ -1,63 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Directive to render a text user profile field. - */ -@Component({ - selector: 'addon-user-profile-field-text', - templateUrl: 'addon-user-profile-field-text.html' -}) -export class AddonUserProfileFieldTextComponent implements OnInit { - @Input() field: any; // The profile field to be rendered. - @Input() edit = false; // True if editing the field. Defaults to false. - @Input() disabled = false; // True if disabled. Defaults to false. - @Input() form?: FormGroup; // Form where to add the form control. - @Input() contextLevel?: string; // The context level. - @Input() contextInstanceId?: number; // The instance ID related to the context. - @Input() courseId?: number; // The course the field belongs to (if any). - - constructor(private fb: FormBuilder, protected utils: CoreUtilsProvider) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const field = this.field; - - if (field && this.edit && this.form) { - field.modelName = 'profile_field_' + field.shortname; - - // Check max length. - if (field.param2) { - field.maxlength = parseInt(field.param2, 10) || ''; - } - - // Check if it's a password or text. - field.inputType = this.utils.isTrueOrOne(field.param3) ? 'password' : 'text'; - - const formData = { - value: field.defaultdata, - disabled: this.disabled - }; - // Initialize the value using default data. - this.form.addControl(field.modelName, this.fb.control(formData, - field.required && !field.locked ? Validators.required : null)); - } - } -} diff --git a/src/addon/userprofilefield/text/providers/handler.ts b/src/addon/userprofilefield/text/providers/handler.ts deleted file mode 100644 index a177b4fdd..000000000 --- a/src/addon/userprofilefield/text/providers/handler.ts +++ /dev/null @@ -1,68 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldTextComponent } from '../component/text'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Text user profile field handlers. - */ -@Injectable() -export class AddonUserProfileFieldTextHandler implements CoreUserProfileFieldHandler { - name = 'AddonUserProfileFieldText'; - type = 'text'; - - constructor(private textUtils: CoreTextUtilsProvider) { } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Get the data to send for the field based on the input data. - * - * @param field User field to get the data for. - * @param signup True if user is in signup page. - * @param registerAuth Register auth method. E.g. 'email'. - * @param formValues Form Values. - * @return Data to send for the field. - */ - getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { - const name = 'profile_field_' + field.shortname; - - return { - type: 'text', - name: name, - value: this.textUtils.cleanTags(formValues[name]) - }; - } - - /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonUserProfileFieldTextComponent; - } -} diff --git a/src/addon/userprofilefield/text/text.module.ts b/src/addon/userprofilefield/text/text.module.ts deleted file mode 100644 index f4c8f894f..000000000 --- a/src/addon/userprofilefield/text/text.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonUserProfileFieldTextHandler } from './providers/handler'; -import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldTextComponent } from './component/text'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - AddonUserProfileFieldTextComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule - ], - providers: [ - AddonUserProfileFieldTextHandler - ], - exports: [ - AddonUserProfileFieldTextComponent - ], - entryComponents: [ - AddonUserProfileFieldTextComponent - ] -}) -export class AddonUserProfileFieldTextModule { - constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldTextHandler) { - userProfileFieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/userprofilefield/textarea/component/addon-user-profile-field-textarea.html b/src/addon/userprofilefield/textarea/component/addon-user-profile-field-textarea.html deleted file mode 100644 index 8908db521..000000000 --- a/src/addon/userprofilefield/textarea/component/addon-user-profile-field-textarea.html +++ /dev/null @@ -1,13 +0,0 @@ - - -

{{ field.name }}

-

-
- - - - {{ field.name }} - - - - \ No newline at end of file diff --git a/src/addon/userprofilefield/textarea/component/textarea.ts b/src/addon/userprofilefield/textarea/component/textarea.ts deleted file mode 100644 index 43c9701b2..000000000 --- a/src/addon/userprofilefield/textarea/component/textarea.ts +++ /dev/null @@ -1,58 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { FormGroup, Validators, FormControl } from '@angular/forms'; - -/** - * Directive to render a textarea user profile field. - */ -@Component({ - selector: 'addon-user-profile-field-textarea', - templateUrl: 'addon-user-profile-field-textarea.html' -}) -export class AddonUserProfileFieldTextareaComponent implements OnInit { - @Input() field: any; // The profile field to be rendered. - @Input() edit = false; // True if editing the field. Defaults to false. - @Input() disabled = false; // True if disabled. Defaults to false. - @Input() form?: FormGroup; // Form where to add the form control. - @Input() contextLevel?: string; // The context level. - @Input() contextInstanceId?: number; // The instance ID related to the context. - @Input() courseId?: number; // The course the field belongs to (if any). - - control: FormControl; - - constructor() { - // Nothing to do. - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - const field = this.field; - - if (field && this.edit && this.form) { - field.modelName = 'profile_field_' + field.shortname; - - const formData = { - value: field.defaultdata, - disabled: this.disabled - }; - - this.control = new FormControl(formData, field.required && !field.locked ? Validators.required : null); - this.form.addControl(field.modelName, this.control); - } - } -} diff --git a/src/addon/userprofilefield/textarea/providers/handler.ts b/src/addon/userprofilefield/textarea/providers/handler.ts deleted file mode 100644 index 740e1fd87..000000000 --- a/src/addon/userprofilefield/textarea/providers/handler.ts +++ /dev/null @@ -1,77 +0,0 @@ - -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldTextareaComponent } from '../component/textarea'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; - -/** - * Textarea user profile field handlers. - */ -@Injectable() -export class AddonUserProfileFieldTextareaHandler implements CoreUserProfileFieldHandler { - name = 'AddonUserProfileFieldTextarea'; - type = 'textarea'; - - constructor(private textUtils: CoreTextUtilsProvider) { } - - /** - * Whether or not the handler is enabled on a site level. - * - * @return True or promise resolved with true if enabled. - */ - isEnabled(): boolean | Promise { - return true; - } - - /** - * Get the data to send for the field based on the input data. - * - * @param field User field to get the data for. - * @param signup True if user is in signup page. - * @param registerAuth Register auth method. E.g. 'email'. - * @param formValues Form Values. - * @return Data to send for the field. - */ - getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { - const name = 'profile_field_' + field.shortname; - - if (formValues[name]) { - let text = formValues[name] || ''; - // Add some HTML to the message in case the user edited with textarea. - text = this.textUtils.formatHtmlLines(text); - - return { - type: 'textarea', - name: name, - value: JSON.stringify({ - text: text, - format: 1 // Always send this format. - }) - }; - } - } - - /** - * Return the Component to use to display the user profile field. - * It's recommended to return the class of the component, but you can also return an instance of the component. - * - * @param injector Injector. - * @return The component (or promise resolved with component) to use, undefined if not found. - */ - getComponent(injector: Injector): any | Promise { - return AddonUserProfileFieldTextareaComponent; - } -} diff --git a/src/addon/userprofilefield/textarea/textarea.module.ts b/src/addon/userprofilefield/textarea/textarea.module.ts deleted file mode 100644 index 68ad0e678..000000000 --- a/src/addon/userprofilefield/textarea/textarea.module.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { AddonUserProfileFieldTextareaHandler } from './providers/handler'; -import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; -import { AddonUserProfileFieldTextareaComponent } from './component/textarea'; -import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; - -@NgModule({ - declarations: [ - AddonUserProfileFieldTextareaComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreComponentsModule, - CoreDirectivesModule, - CoreEditorComponentsModule, - ], - providers: [ - AddonUserProfileFieldTextareaHandler - ], - exports: [ - AddonUserProfileFieldTextareaComponent - ], - entryComponents: [ - AddonUserProfileFieldTextareaComponent - ] -}) -export class AddonUserProfileFieldTextareaModule { - constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldTextareaHandler) { - userProfileFieldDelegate.registerHandler(handler); - } -} diff --git a/src/addon/userprofilefield/userprofilefield.module.ts b/src/addon/userprofilefield/userprofilefield.module.ts deleted file mode 100644 index e9e5f4d04..000000000 --- a/src/addon/userprofilefield/userprofilefield.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { NgModule } from '@angular/core'; -import { AddonUserProfileFieldCheckboxModule } from './checkbox/checkbox.module'; -import { AddonUserProfileFieldDatetimeModule } from './datetime/datetime.module'; -import { AddonUserProfileFieldMenuModule } from './menu/menu.module'; -import { AddonUserProfileFieldTextModule } from './text/text.module'; -import { AddonUserProfileFieldTextareaModule } from './textarea/textarea.module'; - -@NgModule({ - declarations: [], - imports: [ - AddonUserProfileFieldCheckboxModule, - AddonUserProfileFieldDatetimeModule, - AddonUserProfileFieldMenuModule, - AddonUserProfileFieldTextModule, - AddonUserProfileFieldTextareaModule - ], - providers: [ - ], - exports: [] -}) -export class AddonUserProfileFieldModule { } diff --git a/src/app/app.component.ts b/src/app/app.component.ts deleted file mode 100644 index 1dc6627bb..000000000 --- a/src/app/app.component.ts +++ /dev/null @@ -1,403 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, NgZone } from '@angular/core'; -import { Config, Platform, IonicApp } from 'ionic-angular'; -import { Network } from '@ionic-native/network'; -import { CoreApp, CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreLangProvider } from '@providers/lang'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCustomURLSchemesProvider, CoreCustomURLSchemesHandleError } from '@providers/urlschemes'; -import { CoreLoginHelperProvider } from '@core/login/providers/helper'; -import { Keyboard } from '@ionic-native/keyboard'; -import { ScreenOrientation } from '@ionic-native/screen-orientation'; -import { CoreLoginSitesPage } from '@core/login/pages/sites/sites'; -import { CoreWindow } from '@singletons/window'; -import { Device } from '@ionic-native/device'; - -@Component({ - templateUrl: 'app.html' -}) -export class MoodleMobileApp implements OnInit { - // Use page name (string) because the page is lazy loaded (Ionic feature). That way we can load pages without importing them. - // The downside is that each page needs to implement a ngModule. - rootPage: any = 'CoreLoginInitPage'; - protected logger; - protected lastUrls = {}; - protected lastInAppUrl: string; - - constructor( - private platform: Platform, - logger: CoreLoggerProvider, - keyboard: Keyboard, - config: Config, - device: Device, - private app: IonicApp, - private eventsProvider: CoreEventsProvider, - private loginHelper: CoreLoginHelperProvider, - private zone: NgZone, - private appProvider: CoreAppProvider, - private langProvider: CoreLangProvider, - private sitesProvider: CoreSitesProvider, - private screenOrientation: ScreenOrientation, - private urlSchemesProvider: CoreCustomURLSchemesProvider, - private utils: CoreUtilsProvider, - private urlUtils: CoreUrlUtilsProvider, - private network: Network - ) { - this.logger = logger.getInstance('AppComponent'); - - if (this.appProvider.isIOS() && !platform.is('ios')) { - // Solve problem with wrong detected iPadOS. - const platforms = platform.platforms(); - const index = platforms.indexOf('core'); - if (index > -1) { - platforms.splice(index, 1); - } - platforms.push('mobile'); - platforms.push('ios'); - platforms.push('ipad'); - platforms.push('tablet'); - - app.setElementClass('app-root-ios', true); - platform.ready().then(() => { - if (device.version) { - const [major, minor]: string[] = device.version.split('.', 2); - app.setElementClass('platform-ios' + major, true); - app.setElementClass('platform-ios' + major + '_' + minor, true); - } - }); - - app._elementRef.nativeElement.classList.remove('app-root-md'); - - const iosConfig = config.getModeConfig('ios'); - - config.set('mode', 'ios'); - - Object.keys(iosConfig).forEach((key) => { - // Already overriden: pageTransition, do not change. - if (key != 'pageTransition') { - config.set('ios', key, iosConfig[key]); - } - }); - } - - platform.ready().then(() => { - // Okay, so the platform is ready and our plugins are available. - // Here you can do any higher level native things you might need. - - // Set StatusBar properties. - this.appProvider.setStatusBarColor(); - - keyboard.hideFormAccessoryBar(false); - - if (this.appProvider.isDesktop()) { - app.setElementClass('platform-desktop', true); - - if (this.appProvider.isMac()) { - app.setElementClass('platform-mac', true); - } else if (this.appProvider.isLinux()) { - app.setElementClass('platform-linux', true); - } else if (this.appProvider.isWindows()) { - app.setElementClass('platform-windows', true); - } - } - - // Register back button action to allow closing modals before anything else. - this.appProvider.registerBackButtonAction(() => { - return this.closeModal(); - }, 2000); - }); - - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.eventsProvider.on(CoreEventsProvider.LOGOUT, () => { - // Go to sites page when user is logged out. - // Due to DeepLinker, we need to use the ViewCtrl instead of name. - // Otherwise some pages are re-created when they shouldn't. - this.appProvider.getRootNavController().setRoot(CoreLoginSitesPage); - - // Unload lang custom strings. - this.langProvider.clearCustomStrings(); - - // Remove version classes from body. - this.removeVersionClass(); - }); - - // Listen for session expired events. - this.eventsProvider.on(CoreEventsProvider.SESSION_EXPIRED, (data) => { - this.loginHelper.sessionExpired(data); - }); - - // Listen for passwordchange and usernotfullysetup events to open InAppBrowser. - this.eventsProvider.on(CoreEventsProvider.PASSWORD_CHANGE_FORCED, (data) => { - this.loginHelper.passwordChangeForced(data.siteId); - }); - this.eventsProvider.on(CoreEventsProvider.USER_NOT_FULLY_SETUP, (data) => { - this.loginHelper.openInAppForEdit(data.siteId, '/user/edit.php', 'core.usernotfullysetup'); - }); - - // Listen for sitepolicynotagreed event to accept the site policy. - this.eventsProvider.on(CoreEventsProvider.SITE_POLICY_NOT_AGREED, (data) => { - this.loginHelper.sitePolicyNotAgreed(data.siteId); - }); - - this.platform.ready().then(() => { - // Refresh online status when changes. - this.network.onchange().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - this.zone.run(() => { - const isOnline = this.appProvider.isOnline(), - hadOfflineMessage = document.body.classList.contains('core-offline'); - - document.body.classList.toggle('core-offline', !isOnline); - - if (isOnline && hadOfflineMessage) { - document.body.classList.add('core-online'); - - setTimeout(() => { - document.body.classList.remove('core-online'); - }, 3000); - } else if (!isOnline) { - document.body.classList.remove('core-online'); - } - }); - }); - }); - - // Check URLs loaded in any InAppBrowser. - this.eventsProvider.on(CoreEventsProvider.IAB_LOAD_START, (event) => { - // URLs with a custom scheme can be prefixed with "http://" or "https://", we need to remove this. - const url = event.url.replace(/^https?:\/\//, ''); - - if (this.urlSchemesProvider.isCustomURL(url)) { - // Close the browser if it's a valid SSO URL. - this.urlSchemesProvider.handleCustomURL(url).catch((error: CoreCustomURLSchemesHandleError) => { - this.urlSchemesProvider.treatHandleCustomURLError(error); - }); - this.utils.closeInAppBrowser(false); - - } else if (CoreApp.instance.isAndroid()) { - // Check if the URL has a custom URL scheme. In Android they need to be opened manually. - const urlScheme = this.urlUtils.getUrlProtocol(url); - if (urlScheme && urlScheme !== 'file' && urlScheme !== 'cdvfile') { - // Open in browser should launch the right app if found and do nothing if not found. - this.utils.openInBrowser(url); - - // At this point the InAppBrowser is showing a "Webpage not available" error message. - // Try to navigate to last loaded URL so this error message isn't found. - if (this.lastInAppUrl) { - this.utils.openInApp(this.lastInAppUrl); - } else { - // No last URL loaded, close the InAppBrowser. - this.utils.closeInAppBrowser(false); - } - } else { - this.lastInAppUrl = url; - } - } - }); - - // Check InAppBrowser closed. - this.eventsProvider.on(CoreEventsProvider.IAB_EXIT, () => { - this.loginHelper.waitingForBrowser = false; - this.lastInAppUrl = ''; - this.loginHelper.checkLogout(); - }); - - this.platform.resume.subscribe(() => { - // Wait a second before setting it to false since in iOS there could be some frozen WS calls. - setTimeout(() => { - this.loginHelper.waitingForBrowser = false; - this.loginHelper.checkLogout(); - }, 1000); - }); - - // Handle app launched with a certain URL (custom URL scheme). - ( window).handleOpenURL = (url: string): void => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - this.zone.run(() => { - // First check that the URL hasn't been treated a few seconds ago. Sometimes this function is called more than once. - if (this.lastUrls[url] && Date.now() - this.lastUrls[url] < 3000) { - // Function called more than once, stop. - return; - } - - if (!this.urlSchemesProvider.isCustomURL(url)) { - // Not a custom URL, ignore. - return; - } - - this.logger.debug('App launched by URL ', url); - - this.lastUrls[url] = Date.now(); - - this.eventsProvider.trigger(CoreEventsProvider.APP_LAUNCHED_URL, url); - this.urlSchemesProvider.handleCustomURL(url).catch((error: CoreCustomURLSchemesHandleError) => { - this.urlSchemesProvider.treatHandleCustomURLError(error); - }); - }); - }; - - // "Expose" CoreWindow.open. - ( window).openWindowSafely = (url: string, name?: string, windowFeatures?: string): void => { - CoreWindow.open(url, name); - }; - - // Load custom lang strings. This cannot be done inside the lang provider because it causes circular dependencies. - const loadCustomStrings = (): void => { - const currentSite = this.sitesProvider.getCurrentSite(), - customStrings = currentSite && currentSite.getStoredConfig('tool_mobile_customlangstrings'); - - if (typeof customStrings != 'undefined') { - this.langProvider.loadCustomStrings(customStrings); - } - }; - - this.eventsProvider.on(CoreEventsProvider.LOGIN, (data) => { - if (data.siteId) { - this.sitesProvider.getSite(data.siteId).then((site) => { - const info = site.getInfo(); - if (info) { - // Add version classes to body. - this.removeVersionClass(); - this.addVersionClass(this.sitesProvider.getReleaseNumber(info.release || '')); - } - }); - } - - loadCustomStrings(); - }); - - this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, (data) => { - if (data.siteId == this.sitesProvider.getCurrentSiteId()) { - loadCustomStrings(); - - // Add version classes to body. - this.removeVersionClass(); - this.addVersionClass(this.sitesProvider.getReleaseNumber(data.release || '')); - } - }); - - this.eventsProvider.on(CoreEventsProvider.SITE_ADDED, (data) => { - if (data.siteId == this.sitesProvider.getCurrentSiteId()) { - loadCustomStrings(); - - // Add version classes to body. - this.removeVersionClass(); - this.addVersionClass(this.sitesProvider.getReleaseNumber(data.release || '')); - } - }); - - // Pause Youtube videos in Android when app is put in background or screen is locked. - this.platform.pause.subscribe(() => { - if (!CoreApp.instance.isAndroid()) { - return; - } - - const pauseVideos = (window: Window): void => { - // Search videos in iframes recursively. - for (let i = 0; i < window.length; i++) { - pauseVideos(window[i]); - } - - if (window.location.hostname.match(/^www\.youtube(-nocookie)?\.com$/)) { - // Embedded Youtube video, pause it. - const videos = window.document.querySelectorAll('video'); - for (let i = 0; i < videos.length; i++) { - videos[i].pause(); - } - } - }; - - pauseVideos(window); - }); - - // Detect orientation changes. - this.screenOrientation.onChange().subscribe( - () => { - if (CoreApp.instance.isIOS()) { - // Force ios to recalculate safe areas when rotating. - // This can be erased when https://issues.apache.org/jira/browse/CB-13448 issue is solved or - // After switching to WkWebview. - const viewport = document.querySelector('meta[name=viewport]'); - viewport.setAttribute('content', viewport.getAttribute('content').replace('viewport-fit=cover,', '')); - - setTimeout(() => { - viewport.setAttribute('content', 'viewport-fit=cover,' + viewport.getAttribute('content')); - }); - } - - this.eventsProvider.trigger(CoreEventsProvider.ORIENTATION_CHANGE); - } - ); - } - - /** - * Convenience function to add version to body classes. - * - * @param release Current release number of the site. - */ - protected addVersionClass(release: string): void { - const parts = release.split('.'); - - parts[1] = parts[1] || '0'; - parts[2] = parts[2] || '0'; - - document.body.classList.add('version-' + parts[0], 'version-' + parts[0] + '-' + parts[1], - 'version-' + parts[0] + '-' + parts[1] + '-' + parts[2]); - - } - - /** - * Convenience function to remove all version classes form body. - */ - protected removeVersionClass(): void { - const remove = []; - Array.from(document.body.classList).forEach((tempClass) => { - if (tempClass.substring(0, 8) == 'version-') { - remove.push(tempClass); - } - }); - - remove.forEach((tempClass) => { - document.body.classList.remove(tempClass); - }); - } - - /** - * Close one modal if any. - * - * @return True if one modal was present. - */ - closeModal(): boolean { - // Following function is hidden in Ionic Code, however there's no solution for that. - const portal = this.app._getActivePortal(); - if (portal) { - portal.pop(); - - return true; - } - - return false; - } -} diff --git a/src/app/app.html b/src/app/app.html deleted file mode 100644 index 7b88c9699..000000000 --- a/src/app/app.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/app.ios.scss b/src/app/app.ios.scss deleted file mode 100644 index 72ee007a5..000000000 --- a/src/app/app.ios.scss +++ /dev/null @@ -1,134 +0,0 @@ -ion-app.app-root.ios { - .button-ios { - min-height: $button-ios-height; - } - - ion-searchbar.searchbar-ios .button-ios { - min-height: auto; - } - - // Light buttons color. - .button-ios-light { - color: color($colors, primary, base); - } - - .col[align-self-stretch] .card-ios { - height: calc(100% - #{($card-ios-margin-end + $card-ios-margin-start)}); - } - - .bar-buttons core-context-menu .button-clear-ios { - color: $toolbar-ios-button-color; - } - - .item-ios ion-spinner[item-start], - .item-ios ion-spinner[item-end] { - @include margin($item-ios-padding-icon-top, null, $item-ios-padding-icon-bottom, 0); - } - - .item-ios ion-icon[item-start] + .item-inner, - .item-ios ion-icon[item-start] + .item-input, - .item-ios img[item-start] + .item-inner, - .item-ios img[item-start] + .item-input { - @include margin-horizontal($item-ios-padding-start, null); - } - - .item-ios ion-avatar[item-start] + .item-inner, - .item-ios ion-avatar[item-start] + .item-input { - @include margin-horizontal(0, null); - } - - @each $color-name, $color-base, $color-contrast in get-colors($colors-ios) { - .core-#{$color-name}-card { - @extend .card-ios ; - @extend .card-content-ios; - - @include darkmode() { - background-color: $core-dark-item-bg-color; - } - - &[icon-start] { - @include safe-area-padding(null, null, null, $card-ios-padding-left * 2 + 20); - - ion-icon { - @include safe-area-position(null, null, null, $card-ios-padding-left); - } - } - } - } - - .core-avoid-header ion-content { - top: $navbar-ios-height; - height: calc(100% - #{($navbar-ios-height)}); - } - - &.platform-cordova .core-avoid-header ion-content.statusbar-padding, - &.platform-cordova .core-avoid-header .menu-inner > ion-content { - height: calc(100% - #{($navbar-ios-height + $cordova-ios-statusbar-padding)}); - height: calc(100% - #{($navbar-ios-height)} - constant(safe-area-inset-top)); - height: calc(100% - #{($navbar-ios-height)} - env(safe-area-inset-top)); - - top: calc(#{$navbar-ios-height + $cordova-ios-statusbar-padding}); - top: calc(#{$navbar-ios-height} + constant(safe-area-inset-top)); - top: calc(#{$navbar-ios-height} + env(safe-area-inset-top)); - } - - &.platform-cordova .core-avoid-header .core-avoid-header .menu-inner > ion-content, - core-tab core-split-view .core-avoid-header .menu-inner > ion-content { - top: 0; - height: 100%; - } - - // Different levels of padding. - @for $i from 0 through 15 { - .core-padding-#{$i} { - @include padding(null, null, null, 15px * $i + $item-ios-padding-start); - } - } - - .radio-ios .radio-icon { - @include position(0, null, null, 0); - @include margin(0); - @include border-radius($radio-md-icon-border-radius); - - position: relative; - display: block; - - width: $checkbox-ios-icon-size; - height: $checkbox-ios-icon-size; - - border-width: $checkbox-ios-icon-border-width; - border-style: $checkbox-ios-icon-border-style; - border-color: $checkbox-ios-icon-border-color-off; - background-color: $checkbox-ios-background-color-off; - } - - - .action-sheet-ios { - .action-sheet-title { - font-size: 2rem; - } - // File Uploader - // In iOS the input is 1 level higher, so the styles are different. - input.core-fileuploader-file-handler-input { - position: absolute; - @include position(null, 0, null, 0); - min-width: 100%; - min-height: $action-sheet-ios-button-min-height; - opacity: 0; - outline: none; - z-index: 100; - cursor: pointer; - } - } - - video { - &::cue { - white-space: pre-line; - } - - &::-webkit-media-text-track-display-backdrop { - margin-left: 1.5%; - margin-right: 1.5%; - } - } -} diff --git a/src/app/app.md.scss b/src/app/app.md.scss deleted file mode 100644 index eb38e754c..000000000 --- a/src/app/app.md.scss +++ /dev/null @@ -1,94 +0,0 @@ -ion-app.app-root.md { - .button-md { - min-height: $button-md-height; - } - - // Light buttons color. - .button-md-light { - color: color($colors, primary, base); - } - - .col[align-self-stretch] .card-md { - height: calc(100% - #{($card-md-margin-end + $card-md-margin-start)}); - } - - .bar-buttons core-context-menu .button-clear-md { - color: $toolbar-md-button-color; - } - - .item-md ion-spinner[item-start] + .item-inner, - .item-md ion-spinner[item-start] + .item-input { - @include margin-horizontal($item-md-padding-start + ($item-md-padding-start / 2) - 1, null); - } - - .item-md ion-icon[item-start] + .item-inner, - .item-md ion-icon[item-start] + .item-input, - .item-md img[item-start] + .item-inner, - .item-md img[item-start] + .item-input { - @include margin-horizontal($item-md-padding-start, null); - } - - .item-md ion-avatar[item-start] + .item-inner, - .item-md ion-avatar[item-start] + .item-input { - @include margin-horizontal(0, null); - } - - @each $color-name, $color-base, $color-contrast in get-colors($colors-md) { - .core-#{$color-name}-card { - @extend .card-md; - @extend .card-content-md; - - @include darkmode() { - background-color: $core-dark-item-bg-color; - } - - &[icon-start] { - @include safe-area-padding(null, null, null, $card-md-padding-left * 2 + 20); - - ion-icon { - @include safe-area-position(null, null, null, $card-md-padding-left); - } - } - } - } - - .core-avoid-header ion-content { - top: $navbar-md-height; - height: calc(100% - #{($navbar-md-height)}); - } - - // Different levels of padding. - @for $i from 0 through 15 { - .core-padding-#{$i} { - @include padding(null, null, null, 15px * $i + $item-md-padding-start); - } - } - - .action-sheet-md { - .action-sheet-title, - .action-sheet-container > .action-sheet-group:first-child { - box-shadow: 0 3px 5px $gray; - @include darkmode() { - box-shadow: 0 3px 5px $black; - } - } - - .action-sheet-title { - padding-top: 0; - margin-top: $action-sheet-md-title-padding-top; - } - - @media (min-height: 500px) { - .action-sheet-wrapper { - bottom: 0; - top: initial; - max-height: 50%; - height: 100%; - } - } - } -} - -.platform-android4_4 .bar-buttons-md { - display: flex; -} \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts deleted file mode 100644 index cb49b0c71..000000000 --- a/src/app/app.module.ts +++ /dev/null @@ -1,651 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NgModule, COMPILER_OPTIONS, Injector } from '@angular/core'; -import { IonicApp, IonicModule, Platform, Content, ScrollEvent, Config, Refresher } from 'ionic-angular'; -import { assert } from 'ionic-angular/util/util'; -import { HttpClient, HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; -import { JitCompilerFactory } from '@angular/platform-browser-dynamic'; -import { LocationStrategy } from '@angular/common'; -import { MockLocationStrategy } from '@angular/common/testing'; - -import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; -import { TranslateHttpLoader } from '@ngx-translate/http-loader'; - -import { Diagnostic } from '@ionic-native/diagnostic'; -import { Geolocation } from '@ionic-native/geolocation'; -import { ScreenOrientation } from '@ionic-native/screen-orientation'; - -import { MoodleMobileApp } from './app.component'; -import { CoreInterceptor } from '@classes/interceptor'; -import { CorePageTransition } from '@classes/page-transition'; -import { CoreModalLateralTransition } from '@classes/modal-lateral-transition'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreDbProvider } from '@providers/db'; -import { CoreAppProvider } from '@providers/app'; -import { CoreConfigProvider } from '@providers/config'; -import { CoreLangProvider } from '@providers/lang'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreIframeUtilsProvider } from '@providers/utils/iframe'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreInitDelegate } from '@providers/init'; -import { CoreFileProvider } from '@providers/file'; -import { CoreWSProvider } from '@providers/ws'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesFactoryProvider } from '@providers/sites-factory'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; -import { CoreGroupsProvider } from '@providers/groups'; -import { CoreCronDelegate } from '@providers/cron'; -import { CoreFileSessionProvider } from '@providers/file-session'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreUpdateManagerProvider } from '@providers/update-manager'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreFileHelperProvider } from '@providers/file-helper'; -import { CoreCustomURLSchemesProvider } from '@providers/urlschemes'; -import { CoreGeolocationProvider } from '@providers/geolocation'; - -// Handlers. -import { CoreSiteInfoCronHandler } from '@providers/handlers/site-info-cron-handler'; - -// Core modules. -import { CoreComponentsModule } from '@components/components.module'; -import { CoreEmulatorModule } from '@core/emulator/emulator.module'; -import { CoreLoginModule } from '@core/login/login.module'; -import { CoreMainMenuModule } from '@core/mainmenu/mainmenu.module'; -import { CoreCoursesModule } from '@core/courses/courses.module'; -import { CoreFileUploaderModule } from '@core/fileuploader/fileuploader.module'; -import { CoreSharedFilesModule } from '@core/sharedfiles/sharedfiles.module'; -import { CoreCourseModule } from '@core/course/course.module'; -import { CoreSiteHomeModule } from '@core/sitehome/sitehome.module'; -import { CoreContentLinksModule } from '@core/contentlinks/contentlinks.module'; -import { CoreUserModule } from '@core/user/user.module'; -import { CoreGradesModule } from '@core/grades/grades.module'; -import { CoreSettingsModule } from '@core/settings/settings.module'; -import { CoreSitePluginsModule } from '@core/siteplugins/siteplugins.module'; -import { CoreCompileModule } from '@core/compile/compile.module'; -import { CoreQuestionModule } from '@core/question/question.module'; -import { CoreCommentsModule } from '@core/comments/comments.module'; -import { CoreBlockModule } from '@core/block/block.module'; -import { CoreRatingModule } from '@core/rating/rating.module'; -import { CoreTagModule } from '@core/tag/tag.module'; -import { CoreFilterModule } from '@core/filter/filter.module'; -import { CoreH5PModule } from '@core/h5p/h5p.module'; -import { CoreSearchModule } from '@core/search/search.module'; -import { CoreEditorModule } from '@core/editor/editor.module'; -import { CoreXAPIModule } from '@core/xapi/xapi.module'; - -// Addon modules. -import { AddonBadgesModule } from '@addon/badges/badges.module'; -import { AddonBlogModule } from '@addon/blog/blog.module'; -import { AddonCalendarModule } from '@addon/calendar/calendar.module'; -import { AddonCompetencyModule } from '@addon/competency/competency.module'; -import { AddonCourseCompletionModule } from '@addon/coursecompletion/coursecompletion.module'; -import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module'; -import { AddonFilesModule } from '@addon/files/files.module'; -import { AddonBlockActivityModulesModule } from '@addon/block/activitymodules/activitymodules.module'; -import { AddonBlockActivityResultsModule } from '@addon/block/activityresults/activityresults.module'; -import { AddonBlockBadgesModule } from '@addon/block/badges/badges.module'; -import { AddonBlockBlogMenuModule } from '@addon/block/blogmenu/blogmenu.module'; -import { AddonBlockBlogTagsModule } from '@addon/block/blogtags/blogtags.module'; -import { AddonBlockBlogRecentModule } from '@addon/block/blogrecent/blogrecent.module'; -import { AddonBlockCalendarMonthModule } from '@addon/block/calendarmonth/calendarmonth.module'; -import { AddonBlockCalendarUpcomingModule } from '@addon/block/calendarupcoming/calendarupcoming.module'; -import { AddonBlockCommentsModule } from '@addon/block/comments/comments.module'; -import { AddonBlockCompletionStatusModule } from '@addon/block/completionstatus/completionstatus.module'; -import { AddonBlockGlossaryRandomModule } from '@addon/block/glossaryrandom/glossaryrandom.module'; -import { AddonBlockHtmlModule } from '@addon/block/html/html.module'; -import { AddonBlockMyOverviewModule } from '@addon/block/myoverview/myoverview.module'; -import { AddonBlockNewsItemsModule } from '@addon/block/newsitems/newsitems.module'; -import { AddonBlockOnlineUsersModule } from '@addon/block/onlineusers/onlineusers.module'; -import { AddonBlockLearningPlansModule } from '@addon/block/learningplans/learningplans.module'; -import { AddonBlockPrivateFilesModule } from '@addon/block/privatefiles/privatefiles.module'; -import { AddonBlockSiteMainMenuModule } from '@addon/block/sitemainmenu/sitemainmenu.module'; -import { AddonBlockTimelineModule } from '@addon/block/timeline/timeline.module'; -import { AddonBlockRecentlyAccessedCoursesModule } from '@addon/block/recentlyaccessedcourses/recentlyaccessedcourses.module'; -import { AddonBlockRecentlyAccessedItemsModule } from '@addon/block/recentlyaccesseditems/recentlyaccesseditems.module'; -import { AddonBlockRecentActivityModule } from '@addon/block/recentactivity/recentactivity.module'; -import { AddonBlockRssClientModule } from '@addon/block/rssclient/rssclient.module'; -import { AddonBlockStarredCoursesModule } from '@addon/block/starredcourses/starredcourses.module'; -import { AddonBlockSelfCompletionModule } from '@addon/block/selfcompletion/selfcompletion.module'; -import { AddonBlockTagsModule } from '@addon/block/tags/tags.module'; -import { AddonModAssignModule } from '@addon/mod/assign/assign.module'; -import { AddonModBookModule } from '@addon/mod/book/book.module'; -import { AddonModChatModule } from '@addon/mod/chat/chat.module'; -import { AddonModChoiceModule } from '@addon/mod/choice/choice.module'; -import { AddonModDataModule } from '@addon/mod/data/data.module'; -import { AddonModLabelModule } from '@addon/mod/label/label.module'; -import { AddonModLtiModule } from '@addon/mod/lti/lti.module'; -import { AddonModResourceModule } from '@addon/mod/resource/resource.module'; -import { AddonModFeedbackModule } from '@addon/mod/feedback/feedback.module'; -import { AddonModFolderModule } from '@addon/mod/folder/folder.module'; -import { AddonModForumModule } from '@addon/mod/forum/forum.module'; -import { AddonModGlossaryModule } from '@addon/mod/glossary/glossary.module'; -import { AddonModLessonModule } from '@addon/mod/lesson/lesson.module'; -import { AddonModPageModule } from '@addon/mod/page/page.module'; -import { AddonModQuizModule } from '@addon/mod/quiz/quiz.module'; -import { AddonModScormModule } from '@addon/mod/scorm/scorm.module'; -import { AddonModUrlModule } from '@addon/mod/url/url.module'; -import { AddonModSurveyModule } from '@addon/mod/survey/survey.module'; -import { AddonModWorkshopModule } from '@addon/mod/workshop/workshop.module'; -import { AddonModImscpModule } from '@addon/mod/imscp/imscp.module'; -import { AddonModWikiModule } from '@addon/mod/wiki/wiki.module'; -import { AddonMessageOutputModule } from '@addon/messageoutput/messageoutput.module'; -import { AddonMessageOutputAirnotifierModule } from '@addon/messageoutput/airnotifier/airnotifier.module'; -import { AddonMessagesModule } from '@addon/messages/messages.module'; -import { AddonNotesModule } from '../addon/notes/notes.module'; -import { CorePushNotificationsModule } from '@core/pushnotifications/pushnotifications.module'; -import { AddonNotificationsModule } from '@addon/notifications/notifications.module'; -import { AddonRemoteThemesModule } from '@addon/remotethemes/remotethemes.module'; -import { AddonQbehaviourModule } from '@addon/qbehaviour/qbehaviour.module'; -import { AddonQtypeModule } from '@addon/qtype/qtype.module'; -import { AddonStorageManagerModule } from '@addon/storagemanager/storagemanager.module'; -import { AddonFilterModule } from '@addon/filter/filter.module'; -import { AddonModH5PActivityModule } from '@addon/mod/h5pactivity/h5pactivity.module'; - -import { setSingletonsInjector } from '@singletons/core.singletons'; - -// For translate loader. AoT requires an exported function for factories. -export function createTranslateLoader(http: HttpClient): TranslateHttpLoader { - return new TranslateHttpLoader(http, './assets/lang/', '.json'); -} - -// List of providers. -export const CORE_PROVIDERS: any[] = [ - CoreLoggerProvider, - CoreDbProvider, - CoreAppProvider, - CoreConfigProvider, - CoreLangProvider, - CoreTextUtilsProvider, - CoreDomUtilsProvider, - CoreIframeUtilsProvider, - CoreTimeUtilsProvider, - CoreUrlUtilsProvider, - CoreUtilsProvider, - CoreMimetypeUtilsProvider, - CoreInitDelegate, - CoreFileProvider, - CoreWSProvider, - CoreEventsProvider, - CoreSitesFactoryProvider, - CoreSitesProvider, - CoreLocalNotificationsProvider, - CoreGroupsProvider, - CoreCronDelegate, - CoreFileSessionProvider, - CoreFilepoolProvider, - CoreUpdateManagerProvider, - CorePluginFileDelegate, - CoreSyncProvider, - CoreFileHelperProvider, - CoreCustomURLSchemesProvider, - CoreGeolocationProvider, -]; - -export const WP_PROVIDER: any = null; - -@NgModule({ - declarations: [ - MoodleMobileApp - ], - imports: [ - BrowserModule, - BrowserAnimationsModule, - HttpClientModule, // HttpClient is used to make JSON requests. It fails for HEAD requests because there is no content. - IonicModule.forRoot(MoodleMobileApp, { - pageTransition: 'core-page-transition' - }), - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useFactory: (createTranslateLoader), - deps: [HttpClient] - } - }), - CoreComponentsModule, - CoreEmulatorModule, - CoreLoginModule, - CoreMainMenuModule, - CoreCoursesModule, - CoreFileUploaderModule, - CoreSharedFilesModule, - CoreCourseModule, - CoreSiteHomeModule, - CoreContentLinksModule, - CoreUserModule, - CoreGradesModule, - CoreSettingsModule, - CoreSitePluginsModule, - CoreCompileModule, - CoreQuestionModule, - CoreCommentsModule, - CoreBlockModule, - CoreRatingModule, - CorePushNotificationsModule, - CoreTagModule, - CoreFilterModule, - CoreH5PModule, - CoreSearchModule, - CoreEditorModule, - CoreXAPIModule, - AddonBadgesModule, - AddonBlogModule, - AddonCalendarModule, - AddonCompetencyModule, - AddonCourseCompletionModule, - AddonUserProfileFieldModule, - AddonFilesModule, - AddonBlockActivityModulesModule, - AddonBlockActivityResultsModule, - AddonBlockBadgesModule, - AddonBlockBlogMenuModule, - AddonBlockBlogRecentModule, - AddonBlockBlogTagsModule, - AddonBlockCalendarMonthModule, - AddonBlockCalendarUpcomingModule, - AddonBlockCommentsModule, - AddonBlockCompletionStatusModule, - AddonBlockGlossaryRandomModule, - AddonBlockHtmlModule, - AddonBlockLearningPlansModule, - AddonBlockMyOverviewModule, - AddonBlockNewsItemsModule, - AddonBlockOnlineUsersModule, - AddonBlockPrivateFilesModule, - AddonBlockSiteMainMenuModule, - AddonBlockTimelineModule, - AddonBlockRecentlyAccessedCoursesModule, - AddonBlockRecentlyAccessedItemsModule, - AddonBlockRecentActivityModule, - AddonBlockRssClientModule, - AddonBlockStarredCoursesModule, - AddonBlockSelfCompletionModule, - AddonBlockTagsModule, - AddonModAssignModule, - AddonModBookModule, - AddonModChatModule, - AddonModChoiceModule, - AddonModDataModule, - AddonModLabelModule, - AddonModLessonModule, - AddonModResourceModule, - AddonModFeedbackModule, - AddonModFolderModule, - AddonModForumModule, - AddonModGlossaryModule, - AddonModLtiModule, - AddonModPageModule, - AddonModQuizModule, - AddonModScormModule, - AddonModUrlModule, - AddonModSurveyModule, - AddonModWorkshopModule, - AddonModImscpModule, - AddonModWikiModule, - AddonMessageOutputModule, - AddonMessageOutputAirnotifierModule, - AddonMessagesModule, - AddonNotesModule, - AddonNotificationsModule, - AddonRemoteThemesModule, - AddonQbehaviourModule, - AddonQtypeModule, - AddonStorageManagerModule, - AddonFilterModule, - AddonModH5PActivityModule, - ], - bootstrap: [IonicApp], - entryComponents: [ - MoodleMobileApp - ], - providers: [ - CoreLoggerProvider, - CoreDbProvider, - CoreAppProvider, - CoreConfigProvider, - CoreLangProvider, - CoreTextUtilsProvider, - CoreDomUtilsProvider, - CoreIframeUtilsProvider, - CoreTimeUtilsProvider, - CoreUrlUtilsProvider, - CoreUtilsProvider, - CoreMimetypeUtilsProvider, - CoreInitDelegate, - CoreFileProvider, - CoreWSProvider, - CoreEventsProvider, - CoreSitesFactoryProvider, - CoreSitesProvider, - CoreLocalNotificationsProvider, - CoreGroupsProvider, - CoreCronDelegate, - CoreFileSessionProvider, - CoreFilepoolProvider, - CoreUpdateManagerProvider, - CorePluginFileDelegate, - CoreSyncProvider, - CoreFileHelperProvider, - CoreCustomURLSchemesProvider, - CoreGeolocationProvider, - CoreSiteInfoCronHandler, - { - provide: HTTP_INTERCEPTORS, - useClass: CoreInterceptor, - multi: true, - }, - Diagnostic, - Geolocation, - ScreenOrientation, - {provide: COMPILER_OPTIONS, useValue: {}, multi: true}, - {provide: JitCompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]}, - {provide: LocationStrategy, useClass: MockLocationStrategy}, - ] -}) -export class AppModule { - constructor( - platform: Platform, - initDelegate: CoreInitDelegate, - updateManager: CoreUpdateManagerProvider, - config: Config, - sitesProvider: CoreSitesProvider, - fileProvider: CoreFileProvider, - private eventsProvider: CoreEventsProvider, - cronDelegate: CoreCronDelegate, - siteInfoCronHandler: CoreSiteInfoCronHandler, - injector: Injector, - ) { - // Register a handler for platform ready. - initDelegate.registerProcess({ - name: 'CorePlatformReady', - priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 400, - blocking: true, - load: platform.ready - }); - - // Register the update manager as an init process. - initDelegate.registerProcess(updateManager); - - // Restore the user's session during the init process. - initDelegate.registerProcess({ - name: 'CoreRestoreSession', - priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 200, - blocking: false, - load: sitesProvider.restoreSession.bind(sitesProvider) - }); - - // Register clear app tmp folder. - initDelegate.registerProcess({ - name: 'CoreClearTmpFolder', - priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 150, - blocking: false, - load: fileProvider.clearTmpFolder.bind(fileProvider) - }); - - // Execute the init processes. - initDelegate.executeInitProcesses(); - - // Register handlers. - cronDelegate.register(siteInfoCronHandler); - - // Set the injector. - setSingletonsInjector(injector); - - // Set transition animation. - config.setTransition('core-page-transition', CorePageTransition); - config.setTransition('core-modal-lateral-transition', CoreModalLateralTransition); - - // Decorate ion-content. - this.decorateIonContent(); - - // Patch ion-refresher. - this.patchIonRefresher(); - } - - /** - * Decorate ion-content to make our ion-tabs work. - * https://github.com/ionic-team/ionic/issues/14483 - */ - protected decorateIonContent(): void { - - const parsePxUnit = (val: string): number => { - return (val.indexOf('px') > 0) ? parseInt(val, 10) : 0; - }; - - // We need to convert the prototype to any because _readDimensions is private. - // tslint:disable: typedef - ( Content.prototype)._readDimensions = function() { - const cachePaddingTop = this._pTop; - const cachePaddingRight = this._pRight; - const cachePaddingBottom = this._pBottom; - const cachePaddingLeft = this._pLeft; - const cacheHeaderHeight = this._hdrHeight; - const cacheFooterHeight = this._ftrHeight; - const cacheTabsPlacement = this._tabsPlacement; - let tabsTop = 0; - let scrollEvent: ScrollEvent; - this._pTop = 0; - this._pRight = 0; - this._pBottom = 0; - this._pLeft = 0; - this._hdrHeight = 0; - this._ftrHeight = 0; - this._tabsPlacement = null; - this._tTop = 0; - this._fTop = 0; - this._fBottom = 0; - - // In certain cases this._scroll is undefined, if that is the case then we should just return. - if (!this._scroll) { - return; - } - - scrollEvent = this._scroll.ev; - - let ele: HTMLElement = this.getNativeElement(); - if (!ele) { - assert(false, 'ele should be valid'); - - return; - } - - let computedStyle: any; - let tagName: string; - const parentEle: HTMLElement = ele.parentElement; - const children = parentEle.children; - for (let i = children.length - 1; i >= 0; i--) { - ele = children[i]; - tagName = ele.tagName; - if (tagName === 'ION-CONTENT') { - scrollEvent.contentElement = ele; - - if (this._fullscreen) { - // ******** DOM READ **************** - computedStyle = getComputedStyle(ele); - this._pTop = parsePxUnit(computedStyle.paddingTop); - this._pBottom = parsePxUnit(computedStyle.paddingBottom); - this._pRight = parsePxUnit(computedStyle.paddingRight); - this._pLeft = parsePxUnit(computedStyle.paddingLeft); - } - - } else if (tagName === 'ION-HEADER') { - scrollEvent.headerElement = ele; - - // ******** DOM READ **************** - this._hdrHeight = ele.clientHeight; - - } else if (tagName === 'ION-FOOTER') { - scrollEvent.footerElement = ele; - - // ******** DOM READ **************** - this._ftrHeight = ele.clientHeight; - this._footerEle = ele; - } - } - - ele = parentEle; - let tabbarEle: HTMLElement; - - while (ele && ele.tagName !== 'ION-MODAL' && !ele.classList.contains('tab-subpage')) { - - if (ele.tagName.indexOf('ION-TABS') != -1) { - tabbarEle = ele.firstElementChild; - // ******** DOM READ **************** - this._tabbarHeight = tabbarEle.clientHeight; - - if (this._tabsPlacement === null) { - // This is the first tabbar found, remember its position. - this._tabsPlacement = ele.getAttribute('tabsplacement'); - } - } - - ele = ele.parentElement; - } - - // Tabs top - if (this._tabs && this._tabsPlacement === 'top') { - this._tTop = this._hdrHeight; - tabsTop = this._tabs._top; - } - - // Toolbar height - this._cTop = this._hdrHeight; - this._cBottom = this._ftrHeight; - - // Tabs height - if (this._tabsPlacement === 'top') { - this._cTop += this._tabbarHeight; - - } else if (this._tabsPlacement === 'bottom') { - this._cBottom += this._tabbarHeight; - } - - // Refresher uses a border which should be hidden unless pulled - if (this._hasRefresher) { - this._cTop -= 1; - } - - // Fixed content shouldn't include content padding - this._fTop = this._cTop; - this._fBottom = this._cBottom; - - // Handle fullscreen viewport (padding vs margin) - if (this._fullscreen) { - this._cTop += this._pTop; - this._cBottom += this._pBottom; - } - - // ******** DOM READ **************** - const contentDimensions = this.getContentDimensions(); - scrollEvent.scrollHeight = contentDimensions.scrollHeight; - scrollEvent.scrollWidth = contentDimensions.scrollWidth; - scrollEvent.contentHeight = contentDimensions.contentHeight; - scrollEvent.contentWidth = contentDimensions.contentWidth; - scrollEvent.contentTop = contentDimensions.contentTop; - scrollEvent.contentBottom = contentDimensions.contentBottom; - - this._dirty = ( - cachePaddingTop !== this._pTop || - cachePaddingBottom !== this._pBottom || - cachePaddingLeft !== this._pLeft || - cachePaddingRight !== this._pRight || - cacheHeaderHeight !== this._hdrHeight || - cacheFooterHeight !== this._ftrHeight || - cacheTabsPlacement !== this._tabsPlacement || - tabsTop !== this._tTop || - this._cTop !== this.contentTop || - this._cBottom !== this.contentBottom - ); - - this._scroll.init(this.getScrollElement(), this._cTop, this._cBottom); - - // Initial imgs refresh. - this.imgsUpdate(); - }; - - const eventsProvider = this.eventsProvider; - - // tslint:disable: typedef - ( Content).prototype.ngAfterViewInit = function() { - assert(this.getFixedElement(), 'fixed element was not found'); - assert(this.getScrollElement(), 'scroll element was not found'); - - const scroll = this._scroll; - scroll.ev.fixedElement = this.getFixedElement(); - scroll.ev.scrollElement = this.getScrollElement(); - - // Subscribe to the scroll start - scroll.onScrollStart = (ev) => { - this.ionScrollStart.emit(ev); - }; - - // Subscribe to every scroll move - scroll.onScroll = (ev) => { - // Emit to all of our other friends things be scrolling - this.ionScroll.emit(ev); - - this.imgsUpdate(); - }; - - // Subscribe to the scroll end - scroll.onScrollEnd = (ev) => { - this.ionScrollEnd.emit(ev); - - this.imgsUpdate(); - }; - - // Recalculate size when screen rotates. - this._orientationObs = eventsProvider.on(CoreEventsProvider.ORIENTATION_CHANGE, this.resize.bind(this)); - }; - - // tslint:disable: typedef - ( Content).prototype.ngOnDestroy = function() { - this._scLsn && this._scLsn(); - this._viewCtrlReadSub && this._viewCtrlReadSub.unsubscribe(); - this._viewCtrlWriteSub && this._viewCtrlWriteSub.unsubscribe(); - this._viewCtrlReadSub = this._viewCtrlWriteSub = null; - this._scroll && this._scroll.destroy(); - this._footerEle = this._scLsn = this._scroll = null; - this._orientationObs && this._orientationObs.off(); - }; - } - - /** - * Patch ion-refresher to fix video menus and possibly other fixed positioned elements. - */ - patchIonRefresher(): void { - /** - * Original code: https://github.com/ionic-team/ionic/blob/v3.9.3/src/components/refresher/refresher.ts#L468 - * Changed: translateZ(0px) is not added to the CSS transform. - */ - Refresher.prototype._setCss = function(y: number, duration: string, overflowVisible: boolean, delay: string): void { - this._appliedStyles = (y > 0); - - const content = this._content; - const Css = this._plt.Css; - content.setScrollElementStyle(Css.transform, ((y > 0) ? 'translateY(' + y + 'px)' : '')); - content.setScrollElementStyle(Css.transitionDuration, duration); - content.setScrollElementStyle(Css.transitionDelay, delay); - content.setScrollElementStyle('overflow', (overflowVisible ? 'hidden' : '')); - }; - } -} diff --git a/src/app/app.scss b/src/app/app.scss deleted file mode 100644 index 186461c52..000000000 --- a/src/app/app.scss +++ /dev/null @@ -1,1325 +0,0 @@ -// http://ionicframework.com/docs/theming/ - - -// App Global Sass -// -------------------------------------------------- -// Put style rules here that you want to apply globally. These -// styles are for the entire app and not just one component. -// Additionally, this file can be also used as an entry point -// to import other Sass files to be included in the output CSS. -// -// Shared Sass variables, which can be used to adjust Ionic's -// default Sass variables, belong in "theme/variables.scss". -// -// To declare rules for a specific mode, create a child rule -// for the .md, or .ios mode classes. The mode class is -// automatically applied to the element in the app. - -// Alignment -// ------------------------- - -ion-app.app-root { - .text-left { text-align: left; } - .text-right { text-align: right; } - .text-center { text-align: center; } - .text-justify { text-align: justify; } - .clearfix { - &:after { - content: ""; - display: table; - clear: both; - } - } - .img-responsive { - display: block; - max-width: 100%; - &[height] { - height: auto; - } - } - - .opacity-hide { opacity: 0; } - .core-big { font-size: 115%; } - .invisible { visibility: hidden; } - - .button-no-uppercase { - text-transform: none; - } - - @include media-breakpoint-down(sm) { - .hidden-phone { - display: none !important; - opacity: 0 !important; - } - } - - @include media-breakpoint-up(md) { - .hidden-tablet { - display: none !important; - opacity: 0 !important; - } - } - - .has-refresher > .scroll-content { - border-top: 0 !important; - } - - // Define an alternative way to set a heading in an item without using a heading tag. - // This is done for accessibility reasons when a heading is semantically incorrect. - .item .item-heading { - @extend h6; - margin: 0; - } - - .item-dimmed { - opacity: 0.71; - } - - // Simulate item[disabled] but keeping pointer-events. - .item-disabled { - opacity: .4; - } - - .item-radio-checked { - background-color: $gray-lighter; - } - - // Make no-lines work in any element, not just ion-item and ion-list. - .item *[no-lines] .item-inner, - *[no-lines] .item .item-inner { - border: 0; - } - - .item h2 { - text-overflow: inherit; - overflow: inherit; - ion-icon, core-icon { - vertical-align: bottom; - } - } - - .item[detail-push] { - cursor: pointer; - } - - .core-nav-item-selected, .item.core-nav-item-selected { - @include core-selected-item($core-splitview-selected); - } - - // Recover borders on items inside cards and lists. - .card.with-borders .core-as-item, - .list.with-borders .core-as-item, - .core-as-item { - @include core-as-items(); - } - - .card.with-borders > .item, - .list.with-borders > .item { - @include core-items(); - } - - ion-header ion-title .toolbar-title .MathJax_Display { - display: inline-block !important; - margin: 0; - width: auto; - } - - .core-oauth-icon, .item.core-oauth-icon, .list .item.core-oauth-icon { - min-height: 32px; - img, .label { - max-height: 32px; - vertical-align: middle; - } - img { - max-width: 32px; - } - .label { - @include margin(null, null, null, 5px); - color: $gray-darker; - } - } - - .core-bold, .core-bold .label { - font-weight: bold; - } - - .core-module-icon { - width: auto; - max-width: 24px; - max-height: 24px; - } - - .core-button-spinner { - min-height: 44px; - min-width: 50px; - text-align: center; - - .spinner { - margin-top: 8px; - } - } - - // Avatar - // ------------------------- - // Large centered avatar - .item-avatar-center { - text-align: center; - - &.item-complex .item-content { - text-align: center; - @include padding(null, null, null, 49px); - } - - > img:first-child, - ion-avatar img, - img { - display: block; - margin: auto; - width: 90px; - height: 90px; - max-width: 90px; - max-height: 90px; - margin-bottom: 10px; - border-radius : 50%; - padding: 4px; - border: 1px solid #ddd; - background-color: white; - - &.avatar-full { - border-radius: 2%; - border: 0; - max-width: 100%; - max-height: 160px; - width: auto; - height: auto; - } - } - } - - ion-avatar ion-img, ion-avatar img { - text-indent: -99999px; - background-color: $gray-light; - } - - ion-note.core-note-block { - display: block; - } - - // Form items - // ------------------------- - - .item .core-input-footnote { - width: 100%; - padding-top: 10px; - padding-bottom: 10px; - font-style: italic; - } - - ion-datetime { - position: relative; - } - - input { - @include text-align('start'); - @include rtl() { - text-align: right; - } - } - - /** Format Text - Show more styles. */ - /** Styles of elements inside the directive should be placed in format-text.scss */ - core-format-text { - user-select: text; - word-break: break-word; - word-wrap: break-word; - - &[maxHeight], - &[ng-reflect-max-height] { - display: block; - position: relative; - width: 100%; - overflow: hidden; - - /* Force display inline */ - &.inline { - display: inline-block; - width: auto; - } - - // This is to allow clicks in radio/checkbox content. - &.core-text-formatted { - cursor: pointer; - pointer-events: auto; - - .core-show-more { - display: none; - } - - &:not(.core-shortened) { - max-height: none !important; - } - - &.core-shortened { - color: $gray-darker; - @include darkmode() { - color: $white; - } - - overflow: hidden; - min-height: 50px; - - .core-show-more { - color: color($colors, dark); - @include text-align('end'); - font-size: 14px; - display: block; - position: absolute; - @include position(null, 0, 0, null); - z-index: 1001; - background-color: $white; - @include padding(null, null, null, 10px); - @include darkmode() { - color: $white; - background-color: $core-dark-item-bg-color; - } - } - - &:before { - content: ''; - height: 100%; - position: absolute; - @include position(null, 0, 0, 0); - background: -moz-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px)); - background: -webkit-gradient(left top, left bottom, color-stop(calc(100% - 50px), rgba(255, 255, 255, 0)), color-stop(calc(100% - 15px), white)); - background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px)); - background: -o-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px)); - background: -ms-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px)); - background: linear-gradient(to bottom, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px)); - z-index: 1000; - - @include darkmode { - background: -moz-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), $core-dark-item-bg-color calc(100% - 15px)); - background: -webkit-gradient(left top, left bottom, color-stop(calc(100% - 50px), rgba(255, 255, 255, 0)), color-stop(calc(100% - 15px), $core-dark-item-bg-color)); - background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), $core-dark-item-bg-color calc(100% - 15px)); - background: -o-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), $core-dark-item-bg-color calc(100% - 15px)); - background: -ms-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), $core-dark-item-bg-color calc(100% - 15px)); - background: linear-gradient(to bottom, rgba(255, 255, 255, 0) calc(100% - 50px), $core-dark-item-bg-color calc(100% - 15px)); - } - } - } - } - - &.core-expand-in-fullview { - .core-show-more { - @include svg-background-image($item-md-detail-push-svg, true); - @include padding-horizontal(null, 18px); - @include background-position(end, 0, center); - - background-repeat: no-repeat; - background-size: 14px 14px; - } - } - } - - &[singleLine="true"], - &[ng-reflect-single-line="true"] { - cursor: pointer; - pointer-events: auto; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: block; - position: relative; - width: 100%; - } - - &[fullonclick="true"] { - cursor: pointer; - } - - audio, video, a, iframe { - pointer-events: auto; - } - } - - // All external files should be banned from copying it. - ion-avatar, img, audio, video, iframe, [core-external-content], [role="presentation"], [data-original-src], [src] { - user-select: none !important; - } - - .core-media-adapt-width { - max-width: 100%; - } - - img.core-media-adapt-width { - height: auto; - } - - audio.core-media-adapt-width { - width: 100%; - } - - .core-adapted-img-container { - position: relative; - display: inline-block; - width: 100%; - } - - .core-image-viewer-icon { - position: absolute; - @include position(null, 10px, 10px, null); - color: $black; - border-radius: 5px; - background: rgba(255, 255, 255, .5); - @include darkmode() { - background-color: rgba(0, 0, 0, .5); - } - text-align: center; - cursor: pointer; - - width: 32px; - height: 32px; - max-width: 32px; - line-height: 32px; - font-size: 24px; - ion-icon { - font-size: 24px; - - - } - } - - // Media item, ideal for icons. - .item-media { - min-height: $item-media-height + ($content-padding * 2); - > img:first-child { - max-width: $item-media-width; - max-height: $item-media-height; - } - } - - .item .item-button[icon-only] { - height: 2.8em; - font-size: 1.6rem; - padding: 8px 11px; - margin: 0; - } - - ion-col ion-select:not([text-start]) { - @include float(end); - max-width: none; - width: 100%; - .select-text { - white-space: normal; - @include text-align('end'); - } - } - - .item-radio-disabled ion-radio[ng-reflect-value="disabled"]{ - display: none; - } - - ion-select { - position: relative; // Ionic fix. Button can occupy all page if not. - color: $core-select-color; - align-self: start; - - .select-icon .select-icon-inner { - color: $core-select-color; - } - - &.select-disabled .select-icon .select-icon-inner { - color: $text-color; - } - @each $color-name, $color-base, $color-contrast in get-colors($colors) { - &.select-md-#{$color-name}, - &.select-ios-#{$color-name} { - color: $color-base; - - .select-icon .select-icon-inner { - color: $color-base; - } - } - } - @include darkmode() { - @each $color-name, $color-base, $color-contrast in get-colors($colors-dark) { - &.select-md-#{$color-name}, - &.select-ios-#{$color-name} { - color: $color-base; - - .select-icon .select-icon-inner { - color: $color-base; - } - } - } - } - } - - .item-label-stacked ion-select[multiple="true"] { - width: 100%; - } - - ion-select .select-placeholder { - color: $core-select-placeholder-color; - } - - ion-select.core-button-select, - .core-button-select { - background-color: white; - color: $core-select-color; - white-space: normal; - align-self: start; - max-width: none; - @include text-align('start'); - - &.select-md, - &.button-md { - background: $card-md-background-color; - box-shadow: $card-md-box-shadow; - } - - &.select-ios, - &.button-ios { - background: $card-ios-background-color; - box-shadow: $card-ios-box-shadow; - } - - .select-text { - white-space: normal; - } - - .button-inner { - justify-content: start; - } - - .select-icon .select-icon-inner { - color: $core-select-color; - } - - ion-icon:last-child { - @include margin(null, null, null, 5px); - @include text-align('end'); - flex-grow: 2; - } - - .select-text { - text-overflow: ellipsis; - white-space: nowrap; - } - } - - @include darkmode() { - ion-select.core-button-select, - .core-button-select { - background-color: $core-dark-item-bg-color; - - - &.select-md, - &.button-md, - &.select-ios, - &.button-ios { - background: $core-dark-item-bg-color; - } - } - } - - button.core-button-select .core-button-select-text { - overflow: hidden; - text-overflow: ellipsis; - line-height: 2em; - white-space: nowrap; - } - - // File uploader. - // ------------------------- - .core-fileuploader-file-handler { - position: relative; - - input { - position: absolute; - @include position(null, 0, 0, null); - min-width: 100%; - min-height: 100%; - opacity: 0; - outline: none; - z-index: 100; - cursor: pointer; - } - } - - // Question. - // ------------------------- - .core-correct-icon { - padding: 0 ($content-padding / 2); - position: absolute; - @include position(null, 0, $content-padding / 2, null); - margin-top: 0; - margin-bottom: 0; - } - - - .core-question-answer-correct { - color: $core-question-correct-color; - } - - .core-question-answer-incorrect { - color: $core-question-incorrect-color; - } - - input, select { - &.core-question-answer-correct, &.core-question-answer-incorrect { - background-color: $gray-lighter; - color: $text-color; - } - } - - .core-question-correct, - .core-question-comment { - color: $core-question-correct-color; - background-color: $core-question-correct-color-bg; - - .label, ion-label.label, .select-text, .select-icon .select-icon-inner { - color: $core-question-correct-color; - } - .radio-icon { - border-color: $core-question-correct-color; - } - .radio-inner { - background-color: $core-question-correct-color; - } - } - - .core-question-incorrect { - color: $core-question-incorrect-color; - background-color: $core-question-incorrect-color-bg; - - .label, ion-label.label, .select-text, .select-icon .select-icon-inner { - color: $core-question-incorrect-color; - } - .radio-icon { - border-color: $core-question-incorrect-color; - } - .radio-inner { - background-color: $core-question-incorrect-color; - } - } - - .core-question-feedback-container { - background-color: $core-question-feedback-color-bg; - color: $core-question-feedback-color; - - .specificfeedback, .rightanswer, .im-feedback, .feedback, .generalfeedback { - margin: 0 0 .5em; - } - - .correctness { - display: inline-block; - padding: 2px 4px; - font-weight: bold; - line-height: 14px; - color: $white; - text-shadow: 0 -1px 0 rgba(0,0,0,0.25); - background-color: $gray-dark; - -webkit-border-radius: 3px; - border-radius: 3px; - - &.incorrect { - background-color: $red; - } - &.correct { - background-color: $green; - } - } - } - - .core-question-feedback-inline { - display: inline-block; - } - - .core-question-feedback-padding { - @include padding(8px, 35px, 8px, 14px); - } - - .core-question-correct { - background-color: $core-question-state-correct-color; - } - .core-question-partiallycorrect { - background-color: $core-question-state-partial-color; - } - .core-question-notanswered, - .core-question-incorrect { - background-color: $core-question-state-incorrect-color; - } - .core-question-answersaved, - .core-question-requiresgrading { - color: $text-color; - background-color: $core-question-saved-color-bg; - } - - .core-question-warning { - color: $core-question-warning-color; - } - - .questioncorrectnessicon, - .fa.icon.questioncorrectnessicon { - font-size: 20px; - } - - .action-sheet-group { - overflow: auto; - } - - ion-action-sheet .action-sheet-wrapper .action-sheet-container { - .action-sheet-button.action-sheet-cancel { - color: $core-action-sheet-cancel-color; - @include darkmode() { - color: $core-dark-action-sheet-cancel-color; - background-color: $black; - } - } - .action-sheet-selected { - color: $core-color; - } - } - - .alert-message { - overflow-y: auto; - user-select: text; - } - ion-alert.core-nohead { - - &.alert-md .alert-message { - padding-top: $alert-md-message-padding-bottom; - } - &.alert-ios .alert-message { - padding-top: $alert-ios-message-padding-bottom; - } - .alert-head { - display: none; - } - } - - ion-alert .alert-checkbox-group { - border: 0; - } - - ion-alert .alert-message + div:not(.alert-button-group) { - overflow: auto; - alert-checkbox-group, - alert-radio-group, - alert-input-group { - overflow: visible; - max-height: none; - } - } - - ion-toast.core-toast-success .toast-wrapper{ - background: $green-dark; - } - - ion-toast.core-toast-alert .toast-wrapper{ - background: $red-dark; - } - - textarea { - width: 100%; - resize: none; - - &[core-auto-rows] { - height: auto; - line-height: 18px; - padding: 5px; - } - - &:not([core-auto-rows]) { - height: 200px; - min-height: $core-rte-min-height; - } - } - - .toolbar img.core-bar-button-image, - .toolbar .core-bar-button-image img { - padding: 0; - width: $core-toolbar-button-image-width; - height: $core-toolbar-button-image-width; - max-width: $core-toolbar-button-image-width; - border-radius: 50%; - } - - .header .toolbar-ios { - height: $toolbar-ios-height; - max-height: $toolbar-ios-height; - } - - .header .toolbar-md { - height: $toolbar-md-height; - max-height: $toolbar-md-height; - } - - // Footer with auto height. - .footer.footer-adjustable { - height: auto; - } - - .core-circle:before { - content: ' \25CF'; - } - - @each $color-name, $color-base, $color-contrast in get-colors($colors) { - // Message cards. - .core-#{$color-name}-card { - @extend ion-card; - border-bottom: 3px solid $color-base; - - @include darkmode() { - background-color: $core-dark-item-bg-color; - } - - &[icon-start] { - @include safe-area-padding(null, null, null, 52px); - position: relative; - - > ion-icon { - color: $color-base; - position: absolute; - @include safe-area-position(0, null, null, 16px); - height: 100%; - font-size: 24px; - display: flex; - align-items: center; - } - } - } - - .core-#{$color-name}-item { - border-bottom: 3px solid $color-base !important; - ion-icon { - color: $color-base; - } - @include darkmode() { - background-color: $core-dark-item-bg-color; - } - } - - .core-#{$color-name}-item.item-input { - border-bottom: 0 !important; - - &.item-md .item-inner, - &.item-md.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner, - &.item-md.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner { - @include md-input-highlight($color-base); - } - - &.item-ios .item-inner, - &.item-ios.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner, - &.item-ios.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner { - @include ios-input-highlight($color-base); - } - - } - - .core-#{$color-name}-selected-item { - @include core-selected-item($color-base); - } - - .split-pane-main .core-#{$color-name}-selected-item { - @include border-start(5px, solid, $color-base); - } - - .core-#{$color-name}-circle { - margin: 0 4px; - } - - .core-#{$color-name}-circle:before { - @extend .core-circle:before; - color: $color-base; - } - - .text-#{$color-name}, p.text-#{$color-name}, .item p.text-#{$color-name}, .card p.text-#{$color-name} { - color: $color-base; - } - } - - .accesshide { - position: absolute; - @include position(null, null, null, -10000px); - font-weight: normal; - font-size: 1em; - } - - .core-monospaced { - font-family: Andale Mono,Monaco,Courier New,DejaVu Sans Mono,monospace; - } - - - .core-white-push-arrow .item-inner{ - @include push-arrow-color($white); - } - - // For list where some items have detail icon and some others don't. - .core-list-align-detail-right .item .item-inner { - @include padding-horizontal(null, 32px); - } - - [ion-fixed] { - width: 100%; - } - - .core-modal-fullscreen .modal-wrapper { - position: absolute; - @include position(0 !important, null, null, 0 !important); - display: block; - width: 100% !important; - height: 100% !important; - } - - .core-modal-force-on-top { - z-index: 100000 !important; - } - - @media only screen and (min-height: 400px) and (min-width: 300px) { - .core-modal-lateral { - @include core-split-area-end(); - - .modal-wrapper { - position: absolute; - @include position(0 !important, 0 !important, 0 !important, auto); - display: block; - height: 100% !important; - width: auto; - min-width: 300px; - box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4); - } - ion-backdrop { - visibility: visible; - } - } - } - - .has-fab .scroll-content{ - padding-bottom: 56px; - } - - // For some reason, in iOS the pages don't inherit the background-color from ion-app, set it explicitly. - .ion-page { - background-color: $background-color; - } - - // Embed video responsive classes. - // Taken from https://github.com/twbs/bootstrap/blob/v4-dev/scss/utilities/_embed.scss - .embed-responsive { - position: relative; - display: block; - width: 100%; - padding: 0; - overflow: hidden; - - &::before { - display: block; - content: ""; - } - - .embed-responsive-item, - iframe, - embed, - object, - video { - position: absolute; - @include position(0, null, 0, 0); - width: 100%; - height: 100%; - border: 0; - } - - iframe iframe { - width: 100%; - height: 100%; - } - } - - .embed-responsive-21by9 { - &::before { - padding-top: percentage(9 / 21); - } - } - - .embed-responsive-16by9 { - &::before { - padding-top: percentage(9 / 16); - } - } - - .embed-responsive-4by3 { - &::before { - padding-top: percentage(3 / 4); - } - } - - .embed-responsive-1by1 { - &::before { - padding-top: percentage(1 / 1); - } - } - - ion-alert.core-inapp-notification { - pointer-events: none; - position: fixed; - align-items: start; - - ion-backdrop { - display: none; - } - - .alert-wrapper { - width: $toast-width; - max-width: $toast-max-width; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.3); - padding: 2px 0; - border-radius: 0; - pointer-events: auto; - } - - .alert-head { - padding: 5px 10px; - @include text-align('start'); - h2 { - font-size: 14px; - } - } - - .alert-message { - padding: 5px 10px; - @include text-align('start'); - p { - font-size: 14px; - margin-top: 5px; - margin-bottom: 5px; - } - } - } - - .core-icon-with-badge { - position: relative; - - .icon { - font-size: 32px; - } - - .core-icon-badge { - width: auto; - height: auto; - margin: 0; - position: absolute; - @include position(-18px, -8px, null, null); - color: red; - font-size: 16px; - } - } - - .item.item-radio, .item.item-checkbox { - // Fix links and videos in ion-radio and ion-checkbox. - .input-wrapper { - position: relative; - z-index: 5; - pointer-events: none; - } - - // Show whole text on options. - ion-label { - white-space: normal; - } - } - - .core-no-text-wrap { - white-space: nowrap; - - &.item.item-radio, &.item.item-checkbox { - ion-label { - white-space: nowrap; - } - } - } - - // Fix modals displayed over action sheet. - &.disable-scroll ion-modal .ion-page { - pointer-events: initial; - } - - // Avoid scroll bouncing on iOS if disabled. - &.disable-scroll .ion-page .content-ios .scroll-content::before, - &.disable-scroll .ion-page .content-ios .scroll-content::after { - content: none; - } - - .core-iframe-offline-disabled { - display: none !important; - } - - .core-horizontal-scroll { - display: flex; - flex-flow: nowrap; - overflow-x: scroll; - flex-direction: row; - - .item-ios.item-block { - @include padding-horizontal($item-ios-padding-end / 2, null); - - .item-inner { - @include padding-horizontal(null, $item-ios-padding-end / 2); - } - } - } - - ion-content.core-expand-max .scroll-content { - overflow-y: hidden; - display: flex; - flex: 1; - flex-direction: column; - - core-loading { - flex-grow: 1; - - .core-loading-content { - position: absolute; - @include position(0,0,0,0); - } - } - - ion-list { - display: flex; - height: 100%; - flex-direction: column; - > div { - overflow-y: auto; - } - } - } - - .core-expandable { - cursor: pointer; - ion-icon[item-start] + .item-inner { - @include margin-horizontal(0px, null); - } - .icon.fa.fa-caret-right, - .icon.fa.fa-caret-down { - min-width: 16px; - min-height: 1.6rem; - font-size: 1.6rem; - line-height: 1; - } - } - - ion-alert .alert-checkbox-button .alert-checkbox-label { - white-space: normal; - } - - ion-backdrop { - transition: opacity 100ms ease-in-out; - opacity: .1; - } - - a.item, - .core-clickable, - select { - cursor: pointer; - } - - a.autolink.glossary:hover { - cursor: help; - } -} - -@each $color-name, $color-base, $color-contrast in get-colors($colors) { - // If there is text with a color it should use this color - // and override whatever item sets it to - .text-#{$color-name} { - color: $color-contrast; - } - - .item-divider-md-#{$color-name} h2, - .item-divider-ios-#{$color-name} h2 { - color: $color-contrast; - } -} - -body.keyboard-is-open { - ion-content:not(.has-footer) { - > .scroll-content, > .fixed-content { - margin-bottom: 0 !important; - } - } - - core-ion-tabs[tabsplacement="bottom"] .tabbar { - display: none; - } -} - -details summary { - pointer-events: auto; -} - -.icon.fa-graduation-cap, -.item > .icon.fa, -.item-inner > .icon.fa { - font-size: 21px; - width: 21px; - line-height: 28px; -} - -// Ionic sets the "contain" CSS property to some elements. This enables CSS -// containment, which changes how elements are positioned and sized, breaking -// fixed positioned elements, iframes in full screen mode, subtitle menus in -// videos and potentially more things. CSS containment is not supported in iOS -// and Android 4.4, so it can introduce inconsistencies across devices. -// See https://www.w3.org/TR/css-contain-1 -* { - contain: none !important; -} - -// Lower z-index for ion-item-divider so it is displayed below video menus. -ion-item-divider { - z-index: 2; // Ionic default is 100. -} - -// Highlight text. -.matchtext { - background-color: $core-text-hightlight-background-color; -} - -// Styles for desktop apps only. -ion-app.platform-desktop { - video::-webkit-media-text-track-display { - white-space: normal !important; - } - - audio, video { - &::-webkit-media-controls-overlay-play-button, - &::-webkit-media-controls-play-button, - &::-webkit-media-controls-timeline, - &::-webkit-media-controls-mute-button, - &::-webkit-media-controls-fullscreen-button { - cursor: pointer; - } - } - - &.platform-windows { - video::-webkit-media-text-track-display { - font-size: 0.6em; - } - } -} - -// Fix text wrapping in block buttons. -.button-block[text-wrap] { - height: auto; - - // Add vertical padding, we cannot rely on a fixed height + centering like in normal buttons. - .item-md & { - padding-top: .5357em; - padding-bottom: .5357em; - } - .item-md &.item-button { - padding-top: .6em; - padding-bottom: .6em; - } - .item-ios & { - padding-top: .9em; - padding-bottom: .9em; - } - .item-ios &.item-button { - padding-top: .7846em; - padding-bottom: .7846em; - } - - // Keep a consistent height with normal buttons if text does not wrap. - display: flex; - flex-flow: row; - align-items: center; - &.button-md { - min-height: $button-md-height; - } - &.button-large-md { - min-height: $button-md-large-height; - } - &.button-small-md { - min-height: $button-md-small-height; - } - &.button-ios { - min-height: $button-ios-height; - } - &.button-large-ios { - min-height: $button-ios-large-height; - } - &.button-small-ios { - min-height: $button-ios-small-height; - } -} - -// Make funnel icon have iOS look. -.ion-md-funnel::before { - content: "\f182"; -} - -// Fix icon size in lists, to prevent them scaling with text. -.item, .item-inner { - > ion-icon { - font-size: 28px; - } -} - -// Safe areas -[dir="ltr"] body, [dir="rtl"] body { - padding-top: constant(safe-area-inset-top); //for iOS 11.2 - padding-top: env(safe-area-inset-top); //for iOS 11.1 -} - -ion-app.app-root { - .safe-area-page, - .safe-padding-horizontal { - @include safe-area-padding-horizontal(0px, 0px); - } - - [padding].safe-padding-horizontal, - &.ios [padding].safe-padding-horizontal { - @include safe-area-padding-horizontal($content-padding, $content-padding); - } - - // Disable safe area padding. - ion-popover, - .safe-area-page, - .safe-padding-horizontal { - .item-ios.item-block { - @include padding-horizontal($item-ios-padding-end, null); - - .item-inner { - @include padding-horizontal(null, $item-ios-padding-end / 2); - } - } - } - - .item-ios[detail-push] .item-inner, - button.item-ios:not([detail-none]) .item-inner, - a.item-ios:not([detail-none]) .item-inner { - [item-end] { - @include safe-area-margin-horizontal(($item-ios-padding-start / 2), ($item-ios-padding-end / 2)); - } - } -} - -// QR scan. The scanner is at the background of the app, we need to hide the elements that overlay it. -.core-scanning-qr { - ion-app.app-root { - background-color: transparent !important; - - .ion-page { - background-color: transparent !important; - } - ion-content, ion-backdrop, ion-modal:not(.core-modal-fullscreen), core-ion-tabs { - display: none !important; - } - - &.ios .ion-page.show-page~.nav-decor { - display: none !important; - } - } -} diff --git a/src/app/main.ts b/src/app/main.ts deleted file mode 100644 index 23d30e15b..000000000 --- a/src/app/main.ts +++ /dev/null @@ -1,25 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app.module'; - -import { shim } from 'promise.prototype.finally'; - -import 'web-animations-js'; // This is needed to make animations work in Android 5.0. - -shim(); // Support promise.finally. - -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/src/assets/countries/en.json b/src/assets/countries/en.json deleted file mode 100644 index 777763e84..000000000 --- a/src/assets/countries/en.json +++ /dev/null @@ -1,251 +0,0 @@ -{ - "AD": "Andorra", - "AE": "United Arab Emirates", - "AF": "Afghanistan", - "AG": "Antigua and Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarctica", - "AR": "Argentina", - "AS": "American Samoa", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia and Herzegovina", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Belgium", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint Barthélemy", - "BM": "Bermuda", - "BN": "Brunei Darussalam", - "BO": "Bolivia (Plurinational State of)", - "BQ": "Bonaire, Sint Eustatius and Saba", - "BR": "Brazil", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvet Island", - "BW": "Botswana", - "BY": "Belarus", - "BZ": "Belize", - "CA": "Canada", - "CC": "Cocos (Keeling) Islands", - "CD": "Congo (the Democratic Republic of the)", - "CF": "Central African Republic", - "CG": "Congo", - "CH": "Switzerland", - "CI": "Côte d'Ivoire", - "CK": "Cook Islands", - "CL": "Chile", - "CM": "Cameroon", - "CN": "China", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cabo Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "Cyprus", - "CZ": "Czechia", - "DE": "Germany", - "DJ": "Djibouti", - "DK": "Denmark", - "DM": "Dominica", - "DO": "Dominican Republic", - "DZ": "Algeria", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egypt", - "EH": "Western Sahara", - "ER": "Eritrea", - "ES": "Spain", - "ET": "Ethiopia", - "FI": "Finland", - "FJ": "Fiji", - "FK": "Falkland Islands (Malvinas)", - "FM": "Micronesia (Federated States of)", - "FO": "Faroe Islands", - "FR": "France", - "GA": "Gabon", - "GB": "United Kingdom", - "GD": "Grenada", - "GE": "Georgia", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran (Islamic Republic of)", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "Saint Kitts and Nevis", - "KP": "Korea (the Democratic People's Republic of)", - "KR": "Korea (the Republic of)", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Lao People's Democratic Republic", - "LB": "Lebanon", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova (the Republic of)", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestine, State of", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russian Federation", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard and Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "Syrian Arab Republic", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "Chad", - "TF": "French Southern Territories", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turkey", - "TT": "Trinidad and Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania, the United Republic of", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "United States Minor Outlying Islands", - "US": "United States", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "Saint Vincent and the Grenadines", - "VE": "Venezuela (Bolivarian Republic of)", - "VG": "Virgin Islands (British)", - "VI": "Virgin Islands (U.S.)", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis and Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe" -} \ No newline at end of file diff --git a/src/assets/exttomime.json b/src/assets/exttomime.json deleted file mode 100644 index 2728d8b3d..000000000 --- a/src/assets/exttomime.json +++ /dev/null @@ -1,1272 +0,0 @@ -{ -"123": {"type":"application/vnd.lotus-1-2-3"}, -"3dm": {"type":"x-world/x-3dmf"}, -"3dmf": {"type":"x-world/x-3dmf"}, -"3dml": {"type":"text/vnd.in3d.3dml"}, -"3ds": {"type":"image/x-3ds"}, -"3g2": {"type":"video/3gpp2"}, -"3gp": {"type":"video/quicktime","icon":"quicktime","string":"video","groups":["video"]}, -"7z": {"type":"application/x-7z-compressed","icon":"archive","string":"archive","groups":["archive"]}, -"a": {"type":"application/octet-stream"}, -"aab": {"type":"application/x-authorware-bin"}, -"aac": {"type":"audio/aac","icon":"audio","string":"audio","groups":["audio","html_audio","web_audio"]}, -"aam": {"type":"application/x-authorware-map"}, -"aas": {"type":"application/x-authorware-seg"}, -"abc": {"type":"text/vnd.abc"}, -"abw": {"type":"application/x-abiword"}, -"ac": {"type":"application/pkix-attr-cert"}, -"acc": {"type":"application/vnd.americandynamics.acc"}, -"accdb": {"type":"application/msaccess","icon":"base"}, -"ace": {"type":"application/x-ace-compressed","icon":"archive"}, -"acgi": {"type":"text/html"}, -"acu": {"type":"application/vnd.acucobol"}, -"acutc": {"type":"application/vnd.acucorp"}, -"adp": {"type":"audio/adpcm"}, -"aep": {"type":"application/vnd.audiograph"}, -"afl": {"type":"video/animaflex"}, -"afm": {"type":"application/x-font-type1"}, -"afp": {"type":"application/vnd.ibm.modcap"}, -"ahead": {"type":"application/vnd.ahead.space"}, -"ai": {"type":"application/postscript","icon":"eps","string":"image","groups":["image"]}, -"aif": {"type":"audio/x-aiff","icon":"audio","string":"audio","groups":["audio"]}, -"aifc": {"type":"audio/x-aiff","icon":"audio","string":"audio","groups":["audio"]}, -"aiff": {"type":"audio/x-aiff","icon":"audio","string":"audio","groups":["audio"]}, -"aim": {"type":"application/x-aim"}, -"aip": {"type":"text/x-audiosoft-intra"}, -"air": {"type":"application/vnd.adobe.air-application-installer-package+zip"}, -"ait": {"type":"application/vnd.dvb.ait"}, -"ami": {"type":"application/vnd.amiga.ami"}, -"amr": {"type":"audio/amr","icon":"audio","string":"audio","groups":["audio"]}, -"ani": {"type":"application/x-navi-animation"}, -"aos": {"type":"application/x-nokia-9000-communicator-add-on-software"}, -"apk": {"type":"application/vnd.android.package-archive"}, -"appcache": {"type":"text/cache-manifest"}, -"applescript": {"type":"text/plain","icon":"text"}, -"application": {"type":"application/x-ms-application"}, -"apr": {"type":"application/vnd.lotus-approach"}, -"aps": {"type":"application/mime"}, -"arc": {"type":"application/x-freearc"}, -"arj": {"type":"application/arj"}, -"art": {"type":"image/x-jg"}, -"asa": {"type":"text/plain"}, -"asax": {"type":"application/octet-stream"}, -"asc": {"type":"text/plain","icon":"sourcecode"}, -"ascx": {"type":"text/plain"}, -"asf": {"type":"video/x-ms-asf","icon":"wmv","string":"video","groups":["video"]}, -"ashx": {"type":"text/plain"}, -"asm": {"type":"text/plain","icon":"sourcecode"}, -"asmx": {"type":"text/plain"}, -"aso": {"type":"application/vnd.accpac.simply.aso"}, -"asp": {"type":"text/plain"}, -"aspx": {"type":"text/plain"}, -"asx": {"type":"video/x-ms-asf"}, -"atc": {"type":"application/vnd.acucorp"}, -"atom": {"type":"application/atom+xml","icon":"markup"}, -"atomcat": {"type":"application/atomcat+xml","icon":"markup"}, -"atomsvc": {"type":"application/atomsvc+xml","icon":"markup"}, -"atx": {"type":"application/vnd.antix.game-component"}, -"au": {"type":"audio/au","icon":"audio","string":"audio","groups":["audio"]}, -"avi": {"type":"video/x-ms-wm","icon":"avi","string":"video","groups":["video","web_video"]}, -"avs": {"type":"video/avs-video"}, -"aw": {"type":"application/applixware"}, -"axd": {"type":"text/plain"}, -"azf": {"type":"application/vnd.airzip.filesecure.azf","icon":"archive"}, -"azs": {"type":"application/vnd.airzip.filesecure.azs","icon":"archive"}, -"azw": {"type":"application/vnd.amazon.ebook"}, -"bat": {"type":"application/x-msdownload"}, -"bcpio": {"type":"application/x-bcpio"}, -"bdf": {"type":"application/x-font-bdf"}, -"bdm": {"type":"application/vnd.syncml.dm+wbxml"}, -"bdoc": {"type":"application/x-digidoc","icon":"document","groups":["archive"]}, -"bed": {"type":"application/vnd.realvnc.bed"}, -"bh2": {"type":"application/vnd.fujitsu.oasysprs"}, -"bin": {"type":"application/octet-stream"}, -"blb": {"type":"application/x-blorb"}, -"blorb": {"type":"application/x-blorb"}, -"bm": {"type":"image/bmp","icon":"bmp","string":"image","groups":["image","web_image"]}, -"bmi": {"type":"application/vnd.bmi"}, -"bmp": {"type":"image/bmp","icon":"bmp","string":"image","groups":["image","web_image"]}, -"boo": {"type":"application/book"}, -"book": {"type":"application/vnd.framemaker"}, -"box": {"type":"application/vnd.previewsystems.box"}, -"boz": {"type":"application/x-bzip2","icon":"archive"}, -"bpk": {"type":"application/octet-stream"}, -"bsh": {"type":"application/x-bsh"}, -"btif": {"type":"image/prs.btif"}, -"bz": {"type":"application/x-bzip","icon":"archive"}, -"bz2": {"type":"application/x-bzip2","icon":"archive"}, -"c": {"type":"text/plain","icon":"sourcecode"}, -"c++": {"type":"text/plain"}, -"c11amc": {"type":"application/vnd.cluetrust.cartomobile-config"}, -"c11amz": {"type":"application/vnd.cluetrust.cartomobile-config-pkg"}, -"c4d": {"type":"application/vnd.clonk.c4group"}, -"c4f": {"type":"application/vnd.clonk.c4group"}, -"c4g": {"type":"application/vnd.clonk.c4group"}, -"c4p": {"type":"application/vnd.clonk.c4group"}, -"c4u": {"type":"application/vnd.clonk.c4group"}, -"cab": {"type":"application/vnd.ms-cab-compressed","icon":"archive"}, -"caf": {"type":"audio/x-caf"}, -"cap": {"type":"application/vnd.tcpdump.pcap"}, -"car": {"type":"application/vnd.curl.car"}, -"cat": {"type":"application/vnd.ms-pki.seccat"}, -"cb7": {"type":"application/x-cbr"}, -"cba": {"type":"application/x-cbr"}, -"cbr": {"type":"application/x-cbr"}, -"cbt": {"type":"application/x-cbr"}, -"cbz": {"type":"application/x-cbr"}, -"cc": {"type":"text/x-c"}, -"ccad": {"type":"application/clariscad"}, -"cco": {"type":"application/x-cocoa"}, -"cct": {"type":"shockwave/director","icon":"flash"}, -"ccxml": {"type":"application/ccxml+xml"}, -"cdbcmsg": {"type":"application/vnd.contact.cmsg"}, -"cdf": {"type":"application/x-netcdf"}, -"cdkey": {"type":"application/vnd.mediastation.cdkey"}, -"cdmia": {"type":"application/cdmi-capability"}, -"cdmic": {"type":"application/cdmi-container"}, -"cdmid": {"type":"application/cdmi-domain"}, -"cdmio": {"type":"application/cdmi-object"}, -"cdmiq": {"type":"application/cdmi-queue"}, -"cdoc": {"type":"application/x-digidoc","icon":"document","groups":["archive"]}, -"cdx": {"type":"chemical/x-cdx"}, -"cdxml": {"type":"application/vnd.chemdraw+xml"}, -"cdy": {"type":"application/vnd.cinderella"}, -"cer": {"type":"application/pkix-cert"}, -"cfc": {"type":"application/x-coldfusion"}, -"cfm": {"type":"application/x-coldfusion"}, -"cfs": {"type":"application/x-cfs-compressed"}, -"cgm": {"type":"image/cgm"}, -"cha": {"type":"application/x-chat"}, -"chat": {"type":"application/x-chat"}, -"chm": {"type":"application/vnd.ms-htmlhelp"}, -"chrt": {"type":"application/vnd.kde.kchart","icon":"chart"}, -"cif": {"type":"chemical/x-cif"}, -"cii": {"type":"application/vnd.anser-web-certificate-issue-initiation"}, -"cil": {"type":"application/vnd.ms-artgalry"}, -"cla": {"type":"application/vnd.claymore"}, -"class": {"type":"application/java-vm"}, -"clkk": {"type":"application/vnd.crick.clicker.keyboard"}, -"clkp": {"type":"application/vnd.crick.clicker.palette"}, -"clkt": {"type":"application/vnd.crick.clicker.template"}, -"clkw": {"type":"application/vnd.crick.clicker.wordbank"}, -"clkx": {"type":"application/vnd.crick.clicker"}, -"clp": {"type":"application/x-msclip"}, -"cmc": {"type":"application/vnd.cosmocaller"}, -"cmdf": {"type":"chemical/x-cmdf"}, -"cml": {"type":"chemical/x-cml"}, -"cmp": {"type":"application/vnd.yellowriver-custom-menu"}, -"cmx": {"type":"image/x-cmx"}, -"cod": {"type":"application/vnd.rim.cod"}, -"com": {"type":"application/x-msdownload"}, -"conf": {"type":"text/plain"}, -"cpio": {"type":"application/x-cpio"}, -"cpp": {"type":"text/plain","icon":"sourcecode"}, -"cpt": {"type":"application/mac-compactpro"}, -"crd": {"type":"application/x-mscardfile"}, -"crl": {"type":"application/pkix-crl"}, -"crt": {"type":"application/x-x509-ca-cert"}, -"crx": {"type":"application/octet-stream"}, -"cryptonote": {"type":"application/vnd.rig.cryptonote"}, -"cs": {"type":"application/x-csh","icon":"sourcecode"}, -"csh": {"type":"application/x-csh"}, -"csml": {"type":"chemical/x-csml"}, -"csp": {"type":"application/vnd.commonspace"}, -"csr": {"type":"application/pkcs10"}, -"css": {"type":"text/css","icon":"text","groups":["web_file"]}, -"cst": {"type":"application/x-director"}, -"csv": {"type":"text/csv","icon":"spreadsheet","groups":["spreadsheet"]}, -"cu": {"type":"application/cu-seeme"}, -"curl": {"type":"text/vnd.curl"}, -"cww": {"type":"application/prs.cww"}, -"cxt": {"type":"application/x-director"}, -"cxx": {"type":"text/x-c"}, -"dae": {"type":"model/vnd.collada+xml"}, -"daf": {"type":"application/vnd.mobius.daf"}, -"dart": {"type":"application/vnd.dart"}, -"dataless": {"type":"application/vnd.fdsn.seed"}, -"davmount": {"type":"application/davmount+xml"}, -"dbk": {"type":"application/docbook+xml"}, -"dcr": {"type":"application/x-director","icon":"flash"}, -"dcurl": {"type":"text/vnd.curl.dcurl"}, -"dd2": {"type":"application/vnd.oma.dd2+xml"}, -"ddd": {"type":"application/vnd.fujixerox.ddd"}, -"ddoc": {"type":"application/x-digidoc","icon":"document","groups":["archive"]}, -"deb": {"type":"application/x-debian-package"}, -"deepv": {"type":"application/x-deepv"}, -"def": {"type":"text/plain"}, -"deploy": {"type":"application/octet-stream"}, -"der": {"type":"application/x-x509-ca-cert"}, -"dfac": {"type":"application/vnd.dreamfactory"}, -"dgc": {"type":"application/x-dgc-compressed"}, -"dic": {"type":"text/x-c"}, -"dif": {"type":"video/x-dv","icon":"quicktime","string":"video","groups":["video"]}, -"dir": {"type":"application/x-director","icon":"flash"}, -"dis": {"type":"application/vnd.mobius.dis"}, -"dist": {"type":"application/octet-stream"}, -"distz": {"type":"application/octet-stream"}, -"djv": {"type":"image/vnd.djvu"}, -"djvu": {"type":"image/vnd.djvu"}, -"dl": {"type":"video/dl"}, -"dll": {"type":"application/x-msdownload"}, -"dmg": {"type":"application/octet-stream","icon":"unknown"}, -"dmp": {"type":"application/vnd.tcpdump.pcap"}, -"dms": {"type":"application/octet-stream"}, -"dna": {"type":"application/vnd.dna"}, -"doc": {"type":"application/msword","icon":"document","groups":["document"]}, -"docm": {"type":"application/vnd.ms-word.document.macroenabled.12","icon":"document"}, -"docx": {"type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document","icon":"document","groups":["document"]}, -"dot": {"type":"application/msword"}, -"dotm": {"type":"application/vnd.ms-word.template.macroenabled.12","icon":"document"}, -"dotx": {"type":"application/vnd.openxmlformats-officedocument.wordprocessingml.template","icon":"document"}, -"dp": {"type":"application/vnd.osgi.dp"}, -"dpg": {"type":"application/vnd.dpgraph"}, -"dra": {"type":"audio/vnd.dra"}, -"drw": {"type":"application/drafting"}, -"dsc": {"type":"text/prs.lines.tag"}, -"dssc": {"type":"application/dssc+der"}, -"dtb": {"type":"application/x-dtbook+xml"}, -"dtd": {"type":"application/xml-dtd"}, -"dts": {"type":"audio/vnd.dts"}, -"dtshd": {"type":"audio/vnd.dts.hd"}, -"dump": {"type":"application/octet-stream"}, -"dv": {"type":"video/x-dv","icon":"quicktime","string":"video","groups":["video"]}, -"dvb": {"type":"video/vnd.dvb.file"}, -"dvi": {"type":"application/x-dvi"}, -"dwf": {"type":"model/vnd.dwf"}, -"dwg": {"type":"image/vnd.dwg"}, -"dxf": {"type":"image/vnd.dxf"}, -"dxp": {"type":"application/vnd.spotfire.dxp"}, -"dxr": {"type":"application/x-director","icon":"flash"}, -"ecelp4800": {"type":"audio/vnd.nuera.ecelp4800"}, -"ecelp7470": {"type":"audio/vnd.nuera.ecelp7470"}, -"ecelp9600": {"type":"audio/vnd.nuera.ecelp9600"}, -"ecma": {"type":"application/ecmascript"}, -"edm": {"type":"application/vnd.novadigm.edm"}, -"edx": {"type":"application/vnd.novadigm.edx"}, -"efif": {"type":"application/vnd.picsel"}, -"ei6": {"type":"application/vnd.pg.osasli"}, -"el": {"type":"text/x-script.elisp"}, -"elc": {"type":"application/octet-stream"}, -"emf": {"type":"application/x-msmetafile"}, -"eml": {"type":"message/rfc822"}, -"emma": {"type":"application/emma+xml"}, -"emz": {"type":"application/x-msmetafile"}, -"env": {"type":"application/x-envoy"}, -"eol": {"type":"audio/vnd.digital-winds"}, -"eot": {"type":"application/vnd.ms-fontobject"}, -"eps": {"type":"application/postscript","icon":"eps"}, -"epub": {"type":"application/epub+zip","icon":"epub","groups":["document"]}, -"es": {"type":"application/x-esrehber"}, -"es3": {"type":"application/vnd.eszigno3+xml"}, -"esa": {"type":"application/vnd.osgi.subsystem"}, -"esf": {"type":"application/vnd.epson.esf"}, -"et3": {"type":"application/vnd.eszigno3+xml"}, -"etx": {"type":"text/x-setext"}, -"eva": {"type":"application/x-eva"}, -"evy": {"type":"application/x-envoy"}, -"exe": {"type":"application/x-msdownload"}, -"exi": {"type":"application/exi"}, -"ext": {"type":"application/vnd.novadigm.ext"}, -"ez": {"type":"application/andrew-inset"}, -"ez2": {"type":"application/vnd.ezpix-album"}, -"ez3": {"type":"application/vnd.ezpix-package"}, -"f": {"type":"text/x-fortran"}, -"f4v": {"type":"video/mp4","icon":"flash","string":"video","groups":["video","web_video"]}, -"f77": {"type":"text/x-fortran"}, -"f90": {"type":"text/x-fortran"}, -"fbs": {"type":"image/vnd.fastbidsheet"}, -"fcdt": {"type":"application/vnd.adobe.formscentral.fcdt"}, -"fcs": {"type":"application/vnd.isac.fcs"}, -"fdf": {"type":"application/vnd.fdf","icon":"pdf"}, -"fdk": {"type":"application/octet-stream"}, -"fe_launch": {"type":"application/vnd.denovo.fcselayout-link"}, -"fg5": {"type":"application/vnd.fujitsu.oasysgp"}, -"fgd": {"type":"application/x-director"}, -"fh": {"type":"image/x-freehand"}, -"fh4": {"type":"image/x-freehand"}, -"fh5": {"type":"image/x-freehand"}, -"fh7": {"type":"image/x-freehand"}, -"fhc": {"type":"image/x-freehand"}, -"fif": {"type":"application/fractals"}, -"fig": {"type":"application/x-xfig"}, -"flac": {"type":"audio/flac","icon":"audio","string":"audio","groups":["audio","html_audio","web_audio"]}, -"fli": {"type":"video/x-fli"}, -"flo": {"type":"application/vnd.micrografx.flo"}, -"flv": {"type":"video/x-flv","icon":"flash","string":"video","groups":["video","web_video"]}, -"flw": {"type":"application/vnd.kde.kivio"}, -"flx": {"type":"text/vnd.fmi.flexstor"}, -"fly": {"type":"text/vnd.fly"}, -"fm": {"type":"application/vnd.framemaker"}, -"fmf": {"type":"video/x-atomic3d-feature"}, -"fmp4": {"type":"video/mp4","icon":"mpeg","string":"video","groups":["html_video","video","web_video"]}, -"fnc": {"type":"application/vnd.frogans.fnc"}, -"for": {"type":"text/x-fortran"}, -"fpx": {"type":"image/vnd.fpx"}, -"frame": {"type":"application/vnd.framemaker"}, -"frl": {"type":"application/freeloader"}, -"fsc": {"type":"application/vnd.fsc.weblaunch"}, -"fst": {"type":"image/vnd.fst"}, -"ftc": {"type":"application/vnd.fluxtime.clip"}, -"fti": {"type":"application/vnd.anser-web-funds-transfer-initiation"}, -"funk": {"type":"audio/make"}, -"fvt": {"type":"video/vnd.fvt"}, -"fxp": {"type":"application/vnd.adobe.fxp"}, -"fxpl": {"type":"application/vnd.adobe.fxp"}, -"fzs": {"type":"application/vnd.fuzzysheet"}, -"g": {"type":"text/plain"}, -"g2w": {"type":"application/vnd.geoplan"}, -"g3": {"type":"image/g3fax"}, -"g3w": {"type":"application/vnd.geospace"}, -"gac": {"type":"application/vnd.groove-account"}, -"gallery": {"type":"application/x-smarttech-notebook","icon":"archive"}, -"gallerycollection": {"type":"application/x-smarttech-notebook","icon":"archive"}, -"galleryitem": {"type":"application/x-smarttech-notebook","icon":"archive"}, -"gam": {"type":"application/x-tads"}, -"gbr": {"type":"application/rpki-ghostbusters"}, -"gca": {"type":"application/x-gca-compressed"}, -"gdl": {"type":"model/vnd.gdl"}, -"gdoc": {"type":"application/vnd.google-apps.document","icon":"document","groups":["document"]}, -"gdraw": {"type":"application/vnd.google-apps.drawing","icon":"image","groups":["image"]}, -"geo": {"type":"application/vnd.dynageo"}, -"gex": {"type":"application/vnd.geometry-explorer"}, -"ggb": {"type":"application/vnd.geogebra.file","icon":"archive"}, -"ggt": {"type":"application/vnd.geogebra.tool","icon":"archive"}, -"ghf": {"type":"application/vnd.groove-help"}, -"gif": {"type":"image/gif","icon":"gif","string":"image","groups":["image","web_image"]}, -"gim": {"type":"application/vnd.groove-identity-message"}, -"gl": {"type":"video/gl"}, -"gml": {"type":"application/gml+xml"}, -"gmx": {"type":"application/vnd.gmx"}, -"gnumeric": {"type":"application/x-gnumeric","icon":"calc"}, -"gph": {"type":"application/vnd.flographit"}, -"gpx": {"type":"application/gpx+xml"}, -"gqf": {"type":"application/vnd.grafeq"}, -"gqs": {"type":"application/vnd.grafeq"}, -"gram": {"type":"application/srgs"}, -"gramps": {"type":"application/x-gramps-xml"}, -"gre": {"type":"application/vnd.geometry-explorer"}, -"grv": {"type":"application/vnd.groove-injector"}, -"grxml": {"type":"application/srgs+xml"}, -"gsd": {"type":"audio/x-gsm"}, -"gsf": {"type":"application/x-font-ghostscript"}, -"gsheet": {"type":"application/vnd.google-apps.spreadsheet","icon":"spreadsheet","groups":["spreadsheet"]}, -"gslides": {"type":"application/vnd.google-apps.presentation","icon":"powerpoint","groups":["presentation"]}, -"gsm": {"type":"audio/x-gsm"}, -"gsp": {"type":"application/x-gsp"}, -"gss": {"type":"application/x-gss"}, -"gtar": {"type":"application/x-gtar","icon":"archive","string":"archive","groups":["archive"]}, -"gtm": {"type":"application/vnd.groove-tool-message"}, -"gtw": {"type":"model/vnd.gtw"}, -"gv": {"type":"text/vnd.graphviz"}, -"gxf": {"type":"application/gxf"}, -"gxt": {"type":"application/vnd.geonext"}, -"gz": {"type":"application/g-zip","icon":"archive","string":"archive","groups":["archive"]}, -"gzip": {"type":"application/g-zip","icon":"archive","string":"archive","groups":["archive"]}, -"h": {"type":"text/plain","icon":"sourcecode"}, -"h261": {"type":"video/h261"}, -"h263": {"type":"video/h263"}, -"h264": {"type":"video/h264"}, -"h5p": {"type":"application/zip","icon":"h5p","string":"archive"}, -"hal": {"type":"application/vnd.hal+xml"}, -"hbci": {"type":"application/vnd.hbci"}, -"hdf": {"type":"application/x-hdf"}, -"help": {"type":"application/x-helpfile"}, -"hgl": {"type":"application/vnd.hp-hpgl"}, -"hh": {"type":"text/x-c"}, -"hlb": {"type":"text/x-script"}, -"hlp": {"type":"application/winhlp"}, -"hpg": {"type":"application/vnd.hp-hpgl"}, -"hpgl": {"type":"application/vnd.hp-hpgl"}, -"hpid": {"type":"application/vnd.hp-hpid"}, -"hpp": {"type":"text/plain","icon":"sourcecode"}, -"hps": {"type":"application/vnd.hp-hps"}, -"hqx": {"type":"application/mac-binhex40","icon":"archive","string":"archive","groups":["archive"]}, -"hta": {"type":"application/octet-stream"}, -"htc": {"type":"text/x-component","icon":"markup"}, -"htke": {"type":"application/vnd.kenameaapp"}, -"htm": {"type":"text/html","icon":"html","groups":["web_file"]}, -"html": {"type":"text/html","icon":"html","groups":["web_file"]}, -"htmls": {"type":"text/html"}, -"htt": {"type":"text/webviewhtml"}, -"htx": {"type":"text/html"}, -"hvd": {"type":"application/vnd.yamaha.hv-dic"}, -"hvp": {"type":"application/vnd.yamaha.hv-voice"}, -"hvs": {"type":"application/vnd.yamaha.hv-script"}, -"i2g": {"type":"application/vnd.intergeo"}, -"ibooks": {"type":"application/x-ibooks+zip","icon":"archive"}, -"icc": {"type":"application/vnd.iccprofile"}, -"ice": {"type":"x-conference/x-cooltalk"}, -"icm": {"type":"application/vnd.iccprofile"}, -"ico": {"type":"image/vnd.microsoft.icon","icon":"image","string":"image","groups":["image"]}, -"ics": {"type":"text/calendar","icon":"text"}, -"idc": {"type":"text/plain"}, -"ief": {"type":"image/ief"}, -"iefs": {"type":"image/ief"}, -"ifb": {"type":"text/calendar"}, -"ifm": {"type":"application/vnd.shana.informed.formdata"}, -"iges": {"type":"model/iges"}, -"igl": {"type":"application/vnd.igloader"}, -"igm": {"type":"application/vnd.insors.igm"}, -"igs": {"type":"model/iges"}, -"igx": {"type":"application/vnd.micrografx.igx"}, -"iif": {"type":"application/vnd.shana.informed.interchange"}, -"ima": {"type":"application/x-ima"}, -"imap": {"type":"application/x-httpd-imap"}, -"imp": {"type":"application/vnd.accpac.simply.imp"}, -"ims": {"type":"application/vnd.ms-ims"}, -"in": {"type":"text/plain"}, -"inf": {"type":"application/inf"}, -"ini": {"type":"text/plain"}, -"ink": {"type":"application/inkml+xml"}, -"inkml": {"type":"application/inkml+xml"}, -"ins": {"type":"application/x-internett-signup"}, -"install": {"type":"application/x-install-instructions"}, -"iota": {"type":"application/vnd.astraea-software.iota"}, -"ip": {"type":"application/x-ip2"}, -"ipa": {"type":"application/octet-stream"}, -"ipfix": {"type":"application/ipfix"}, -"ipk": {"type":"application/vnd.shana.informed.package"}, -"irm": {"type":"application/vnd.ibm.rights-management"}, -"irp": {"type":"application/vnd.irepository.package+xml"}, -"isf": {"type":"application/inspiration","icon":"isf"}, -"iso": {"type":"application/x-iso9660-image"}, -"ist": {"type":"application/inspiration.template","icon":"isf"}, -"isu": {"type":"video/x-isvideo"}, -"it": {"type":"audio/it"}, -"itp": {"type":"application/vnd.shana.informed.formtemplate"}, -"iv": {"type":"application/x-inventor"}, -"ivp": {"type":"application/vnd.immervision-ivp"}, -"ivr": {"type":"i-world/i-vrml"}, -"ivu": {"type":"application/vnd.immervision-ivu"}, -"ivy": {"type":"application/x-livescreen"}, -"jad": {"type":"text/vnd.sun.j2me.app-descriptor"}, -"jam": {"type":"application/vnd.jam"}, -"jar": {"type":"application/java-archive","icon":"archive"}, -"jav": {"type":"text/plain"}, -"java": {"type":"text/plain","icon":"sourcecode"}, -"jcb": {"type":"text/xml","icon":"markup"}, -"jcl": {"type":"text/xml","icon":"markup"}, -"jcm": {"type":"application/x-java-commerce"}, -"jcw": {"type":"text/xml","icon":"markup"}, -"jfif": {"type":"image/jpeg"}, -"jfif-tbnl": {"type":"image/jpeg"}, -"jisp": {"type":"application/vnd.jisp"}, -"jlt": {"type":"application/vnd.hp-jlyt"}, -"jmt": {"type":"text/xml","icon":"markup"}, -"jmx": {"type":"text/xml","icon":"markup"}, -"jnlp": {"type":"application/x-java-jnlp-file","icon":"markup"}, -"joda": {"type":"application/vnd.joost.joda-archive"}, -"jpe": {"type":"image/jpeg","icon":"jpeg","string":"image","groups":["image","web_image"]}, -"jpeg": {"type":"image/jpeg","icon":"jpeg","string":"image","groups":["image","web_image"]}, -"jpg": {"type":"image/jpeg","icon":"jpeg","string":"image","groups":["image","web_image"]}, -"jpgm": {"type":"video/jpm"}, -"jpgv": {"type":"video/jpeg"}, -"jpm": {"type":"video/jpm"}, -"jps": {"type":"image/x-jps"}, -"jqz": {"type":"text/xml","icon":"markup"}, -"js": {"type":"application/x-javascript","icon":"text","groups":["web_file"]}, -"json": {"type":"application/json","icon":"text"}, -"jsonml": {"type":"application/jsonml+json"}, -"jut": {"type":"image/jutvision"}, -"kar": {"type":"audio/midi"}, -"karbon": {"type":"application/vnd.kde.karbon"}, -"key": {"type":"application/pkcs8"}, -"kfo": {"type":"application/vnd.kde.kformula"}, -"kia": {"type":"application/vnd.kidspiration"}, -"kml": {"type":"application/vnd.google-earth.kml+xml"}, -"kmz": {"type":"application/vnd.google-earth.kmz"}, -"kne": {"type":"application/vnd.kinar"}, -"knp": {"type":"application/vnd.kinar"}, -"kon": {"type":"application/vnd.kde.kontour"}, -"kpr": {"type":"application/vnd.kde.kpresenter"}, -"kpt": {"type":"application/vnd.kde.kpresenter"}, -"kpxx": {"type":"application/vnd.ds-keypoint"}, -"ksh": {"type":"application/x-ksh"}, -"ksp": {"type":"application/vnd.kde.kspread"}, -"ktr": {"type":"application/vnd.kahootz"}, -"ktx": {"type":"image/ktx"}, -"ktz": {"type":"application/vnd.kahootz"}, -"kwd": {"type":"application/vnd.kde.kword"}, -"kwt": {"type":"application/vnd.kde.kword"}, -"la": {"type":"audio/nspaudio"}, -"lam": {"type":"audio/x-liveaudio"}, -"lasxml": {"type":"application/vnd.las.las+xml"}, -"latex": {"type":"application/x-latex","icon":"text"}, -"lbd": {"type":"application/vnd.llamagraphics.life-balance.desktop"}, -"lbe": {"type":"application/vnd.llamagraphics.life-balance.exchange+xml"}, -"les": {"type":"application/vnd.hhe.lesson-player"}, -"lha": {"type":"application/x-lzh-compressed"}, -"lhx": {"type":"application/octet-stream"}, -"link66": {"type":"application/vnd.route66.link66+xml"}, -"list": {"type":"text/plain"}, -"list3820": {"type":"application/vnd.ibm.modcap"}, -"listafp": {"type":"application/vnd.ibm.modcap"}, -"lma": {"type":"audio/nspaudio"}, -"lnk": {"type":"application/x-ms-shortcut"}, -"log": {"type":"text/plain"}, -"lostxml": {"type":"application/lost+xml"}, -"lrf": {"type":"application/octet-stream"}, -"lrm": {"type":"application/vnd.ms-lrm"}, -"lsp": {"type":"application/x-lisp"}, -"lst": {"type":"text/plain"}, -"lsx": {"type":"text/x-la-asf"}, -"ltf": {"type":"application/vnd.frogans.ltf"}, -"ltx": {"type":"application/x-latex"}, -"lvp": {"type":"audio/vnd.lucent.voice"}, -"lwp": {"type":"application/vnd.lotus-wordpro"}, -"lz": {"type":"application/x-lzip","icon":"archive"}, -"lzh": {"type":"application/x-lzh-compressed"}, -"lzma": {"type":"application/x-lzma"}, -"lzo": {"type":"application/x-lzop"}, -"lzx": {"type":"application/lzx"}, -"m": {"type":"text/plain","icon":"sourcecode"}, -"m13": {"type":"application/x-msmediaview"}, -"m14": {"type":"application/x-msmediaview"}, -"m1v": {"type":"video/mpeg"}, -"m21": {"type":"application/mp21"}, -"m2a": {"type":"audio/mpeg"}, -"m2v": {"type":"video/mpeg"}, -"m3a": {"type":"audio/mpeg"}, -"m3u": {"type":"audio/x-mpegurl","icon":"mp3","string":"audio","groups":["audio"]}, -"m3u8": {"type":"application/x-mpegURL","icon":"mpeg","groups":["media_source"]}, -"m4a": {"type":"audio/mp4","icon":"mp3","string":"audio","groups":["audio","html_audio","web_audio"]}, -"m4u": {"type":"video/vnd.mpegurl"}, -"m4v": {"type":"video/mp4","icon":"mpeg","string":"video","groups":["html_video","video","web_video"]}, -"ma": {"type":"application/mathematica","string":"math"}, -"mads": {"type":"application/mads+xml"}, -"mag": {"type":"application/vnd.ecowin.chart"}, -"maker": {"type":"application/vnd.framemaker"}, -"man": {"type":"text/troff"}, -"map": {"type":"application/x-navimap"}, -"mar": {"type":"application/octet-stream"}, -"mathml": {"type":"application/mathml+xml","string":"math"}, -"mb": {"type":"application/mathematica","string":"math"}, -"mbd": {"type":"application/mbedlet"}, -"mbk": {"type":"application/vnd.mobius.mbk"}, -"mbox": {"type":"application/mbox"}, -"mbz": {"type":"application/vnd.moodle.backup","icon":"moodle"}, -"mc$": {"type":"application/x-magic-cap-package-1.0"}, -"mc1": {"type":"application/vnd.medcalcdata"}, -"mcd": {"type":"application/vnd.mcd"}, -"mcf": {"type":"image/vasa"}, -"mcp": {"type":"application/netmc"}, -"mcurl": {"type":"text/vnd.curl.mcurl"}, -"mdb": {"type":"application/x-msaccess","icon":"base"}, -"mdi": {"type":"image/vnd.ms-modi"}, -"me": {"type":"text/troff"}, -"mesh": {"type":"model/mesh"}, -"meta4": {"type":"application/metalink4+xml"}, -"metalink": {"type":"application/metalink+xml"}, -"mets": {"type":"application/mets+xml"}, -"mfm": {"type":"application/vnd.mfmp"}, -"mft": {"type":"application/rpki-manifest"}, -"mgp": {"type":"application/vnd.osgeo.mapguide.package"}, -"mgz": {"type":"application/vnd.proteus.magazine"}, -"mht": {"type":"message/rfc822","icon":"archive"}, -"mhtml": {"type":"message/rfc822","icon":"archive"}, -"mid": {"type":"audio/midi"}, -"midi": {"type":"audio/midi"}, -"mie": {"type":"application/x-mie"}, -"mif": {"type":"application/vnd.mif"}, -"mime": {"type":"message/rfc822"}, -"mj2": {"type":"video/mj2"}, -"mjf": {"type":"audio/x-vnd.audioexplosion.mjuicemediafile"}, -"mjp2": {"type":"video/mj2"}, -"mjpg": {"type":"video/x-motion-jpeg"}, -"mk3d": {"type":"video/x-matroska"}, -"mka": {"type":"audio/x-matroska"}, -"mks": {"type":"video/x-matroska"}, -"mkv": {"type":"video/x-matroska"}, -"mlp": {"type":"application/vnd.dolby.mlp"}, -"mm": {"type":"application/base64"}, -"mmd": {"type":"application/vnd.chipnuts.karaoke-mmd"}, -"mme": {"type":"application/base64"}, -"mmf": {"type":"application/vnd.smaf"}, -"mmr": {"type":"image/vnd.fujixerox.edmics-mmr"}, -"mng": {"type":"video/x-mng"}, -"mny": {"type":"application/x-msmoney"}, -"mobi": {"type":"application/x-mobipocket-ebook"}, -"mod": {"type":"audio/mod"}, -"mods": {"type":"application/mods+xml"}, -"moov": {"type":"video/quicktime"}, -"mov": {"type":"video/quicktime","icon":"quicktime","string":"video","groups":["video","web_video","html_video"]}, -"movie": {"type":"video/x-sgi-movie","icon":"quicktime","string":"video","groups":["video"]}, -"mp2": {"type":"audio/mpeg"}, -"mp21": {"type":"application/mp21"}, -"mp2a": {"type":"audio/mpeg"}, -"mp3": {"type":"audio/mp3","icon":"mp3","string":"audio","groups":["audio","html_audio","web_audio"]}, -"mp4": {"type":"video/mp4","icon":"mpeg","string":"video","groups":["html_video","video","web_video"]}, -"mp4a": {"type":"audio/mp4","icon":"audio","string":"audio","groups":["audio","html_audio","web_audio"]}, -"mp4s": {"type":"application/mp4"}, -"mp4v": {"type":"video/mp4","icon":"mpeg","string":"video","groups":["video","web_video"]}, -"mpa": {"type":"audio/mpeg"}, -"mpc": {"type":"application/vnd.mophun.certificate"}, -"mpd": {"type":"application/dash+xml","icon":"mpeg","groups":["media_source"]}, -"mpe": {"type":"video/mpeg","icon":"mpeg","string":"video","groups":["video","web_video"]}, -"mpeg": {"type":"video/mpeg","icon":"mpeg","string":"video","groups":["video","web_video"]}, -"mpg": {"type":"video/mpeg","icon":"mpeg","string":"video","groups":["video","web_video"]}, -"mpg4": {"type":"video/mp4"}, -"mpga": {"type":"audio/mpeg"}, -"mpkg": {"type":"application/vnd.apple.installer+xml"}, -"mpm": {"type":"application/vnd.blueice.multipass"}, -"mpn": {"type":"application/vnd.mophun.application"}, -"mpp": {"type":"application/vnd.ms-project"}, -"mpr": {"type":"application/vnd.moodle.profiling","icon":"moodle"}, -"mpt": {"type":"application/vnd.ms-project"}, -"mpv": {"type":"application/x-project"}, -"mpx": {"type":"application/x-project"}, -"mpy": {"type":"application/vnd.ibm.minipay"}, -"mqy": {"type":"application/vnd.mobius.mqy"}, -"mrc": {"type":"application/marc"}, -"mrcx": {"type":"application/marcxml+xml"}, -"ms": {"type":"text/troff"}, -"mscml": {"type":"application/mediaservercontrol+xml"}, -"mseed": {"type":"application/vnd.fdsn.mseed"}, -"mseq": {"type":"application/vnd.mseq"}, -"msf": {"type":"application/vnd.epson.msf"}, -"msh": {"type":"model/mesh"}, -"msi": {"type":"application/x-msdownload"}, -"msl": {"type":"application/vnd.mobius.msl"}, -"msty": {"type":"application/vnd.muvee.style"}, -"mts": {"type":"model/vnd.mts"}, -"mus": {"type":"application/vnd.musician"}, -"musicxml": {"type":"application/vnd.recordare.musicxml+xml"}, -"mv": {"type":"video/x-sgi-movie"}, -"mvb": {"type":"application/x-msmediaview"}, -"mw": {"type":"application/maple","icon":"math"}, -"mwf": {"type":"application/vnd.mfer"}, -"mws": {"type":"application/maple","icon":"math"}, -"mxf": {"type":"application/mxf"}, -"mxl": {"type":"application/vnd.recordare.musicxml"}, -"mxml": {"type":"application/xv+xml"}, -"mxs": {"type":"application/vnd.triscape.mxs"}, -"mxu": {"type":"video/vnd.mpegurl"}, -"my": {"type":"audio/make"}, -"mzz": {"type":"application/x-vnd.audioexplosion.mzz"}, -"n-gage": {"type":"application/vnd.nokia.n-gage.symbian.install"}, -"n3": {"type":"text/n3"}, -"nap": {"type":"image/naplps"}, -"naplps": {"type":"image/naplps"}, -"nb": {"type":"application/mathematica","string":"math"}, -"nbk": {"type":"application/x-smarttech-notebook","icon":"archive"}, -"nbp": {"type":"application/vnd.wolfram.player"}, -"nc": {"type":"application/x-netcdf"}, -"ncm": {"type":"application/vnd.nokia.configuration-message"}, -"ncx": {"type":"application/x-dtbncx+xml"}, -"nfo": {"type":"text/x-nfo"}, -"ngdat": {"type":"application/vnd.nokia.n-gage.data"}, -"nif": {"type":"image/x-niff"}, -"niff": {"type":"image/x-niff"}, -"nitf": {"type":"application/vnd.nitf"}, -"nix": {"type":"application/x-mix-transfer"}, -"nlu": {"type":"application/vnd.neurolanguage.nlu"}, -"nml": {"type":"application/vnd.enliven"}, -"nnd": {"type":"application/vnd.noblenet-directory"}, -"nns": {"type":"application/vnd.noblenet-sealer"}, -"nnw": {"type":"application/vnd.noblenet-web"}, -"notebook": {"type":"application/x-smarttech-notebook","icon":"archive"}, -"npx": {"type":"image/vnd.net-fpx"}, -"nsc": {"type":"application/x-conference"}, -"nsf": {"type":"application/vnd.lotus-notes"}, -"ntf": {"type":"application/vnd.nitf"}, -"nvd": {"type":"application/x-navidoc"}, -"nzb": {"type":"application/x-nzb"}, -"o": {"type":"application/octet-stream"}, -"oa2": {"type":"application/vnd.fujitsu.oasys2"}, -"oa3": {"type":"application/vnd.fujitsu.oasys3"}, -"oas": {"type":"application/vnd.fujitsu.oasys"}, -"obd": {"type":"application/x-msbinder"}, -"obj": {"type":"application/x-tgif"}, -"oda": {"type":"application/oda"}, -"odb": {"type":"application/vnd.oasis.opendocument.database","icon":"base"}, -"odc": {"type":"application/vnd.oasis.opendocument.chart","icon":"chart"}, -"odf": {"type":"application/vnd.oasis.opendocument.formula","icon":"math"}, -"odft": {"type":"application/vnd.oasis.opendocument.formula-template","icon":"math"}, -"odg": {"type":"application/vnd.oasis.opendocument.graphics","icon":"draw"}, -"odi": {"type":"application/vnd.oasis.opendocument.image","icon":"draw"}, -"odm": {"type":"application/vnd.oasis.opendocument.text-master","icon":"writer"}, -"odp": {"type":"application/vnd.oasis.opendocument.presentation","icon":"impress","groups":["presentation"]}, -"ods": {"type":"application/vnd.oasis.opendocument.spreadsheet","icon":"calc","groups":["spreadsheet"]}, -"odt": {"type":"application/vnd.oasis.opendocument.text","icon":"writer","groups":["document"]}, -"oga": {"type":"audio/ogg","icon":"audio","string":"audio","groups":["audio","html_audio","web_audio"]}, -"ogg": {"type":"audio/ogg","icon":"audio","string":"audio","groups":["audio","html_audio","web_audio"]}, -"ogv": {"type":"video/ogg","icon":"video","string":"video","groups":["html_video","video","web_video"]}, -"ogx": {"type":"application/ogg"}, -"omc": {"type":"application/x-omc"}, -"omcd": {"type":"application/x-omcdatamaker"}, -"omcr": {"type":"application/x-omcregerator"}, -"omdoc": {"type":"application/omdoc+xml"}, -"onepkg": {"type":"application/onenote"}, -"onetmp": {"type":"application/onenote"}, -"onetoc": {"type":"application/onenote"}, -"onetoc2": {"type":"application/onenote"}, -"opf": {"type":"application/oebps-package+xml"}, -"opml": {"type":"text/x-opml"}, -"oprc": {"type":"application/vnd.palm"}, -"org": {"type":"application/vnd.lotus-organizer"}, -"osf": {"type":"application/vnd.yamaha.openscoreformat"}, -"osfpvg": {"type":"application/vnd.yamaha.openscoreformat.osfpvg+xml"}, -"otc": {"type":"application/vnd.oasis.opendocument.chart-template","icon":"chart"}, -"otf": {"type":"application/x-font-otf"}, -"otg": {"type":"application/vnd.oasis.opendocument.graphics-template","icon":"draw"}, -"oth": {"type":"application/vnd.oasis.opendocument.text-web","icon":"oth","groups":["document"]}, -"oti": {"type":"application/vnd.oasis.opendocument.image-template"}, -"otp": {"type":"application/vnd.oasis.opendocument.presentation-template","icon":"impress","groups":["presentation"]}, -"ots": {"type":"application/vnd.oasis.opendocument.spreadsheet-template","icon":"calc","groups":["spreadsheet"]}, -"ott": {"type":"application/vnd.oasis.opendocument.text-template","icon":"writer","groups":["document"]}, -"oxps": {"type":"application/oxps"}, -"oxt": {"type":"application/vnd.openofficeorg.extension"}, -"p": {"type":"text/x-pascal"}, -"p10": {"type":"application/pkcs10"}, -"p12": {"type":"application/x-pkcs12"}, -"p7a": {"type":"application/x-pkcs7-signature"}, -"p7b": {"type":"application/x-pkcs7-certificates"}, -"p7c": {"type":"application/pkcs7-mime"}, -"p7m": {"type":"application/pkcs7-mime"}, -"p7r": {"type":"application/x-pkcs7-certreqresp"}, -"p7s": {"type":"application/pkcs7-signature"}, -"p8": {"type":"application/pkcs8"}, -"part": {"type":"application/pro_eng"}, -"pas": {"type":"text/x-pascal"}, -"paw": {"type":"application/vnd.pawaafile"}, -"pbd": {"type":"application/vnd.powerbuilder6"}, -"pbm": {"type":"image/x-portable-bitmap"}, -"pcap": {"type":"application/vnd.tcpdump.pcap"}, -"pcf": {"type":"application/x-font-pcf"}, -"pcl": {"type":"application/vnd.hp-pcl"}, -"pclxl": {"type":"application/vnd.hp-pclxl"}, -"pct": {"type":"image/pict","icon":"image","string":"image","groups":["image"]}, -"pcurl": {"type":"application/vnd.curl.pcurl"}, -"pcx": {"type":"image/x-pcx"}, -"pdb": {"type":"application/vnd.palm"}, -"pdf": {"type":"application/pdf","icon":"pdf","groups":["document"]}, -"pem": {"type":"application/x-pem-file"}, -"pfa": {"type":"application/x-font-type1"}, -"pfb": {"type":"application/x-font-type1"}, -"pfm": {"type":"application/x-font-type1"}, -"pfr": {"type":"application/font-tdpfr"}, -"pfunk": {"type":"audio/make"}, -"pfx": {"type":"application/x-pkcs12"}, -"pgm": {"type":"image/x-portable-graymap"}, -"pgn": {"type":"application/x-chess-pgn"}, -"pgp": {"type":"application/pgp-encrypted"}, -"phar": {"type":"application/octet-stream"}, -"php": {"type":"text/plain","icon":"sourcecode"}, -"phps": {"type":"application/x-httpd-phps"}, -"pic": {"type":"image/pict","icon":"image","string":"image","groups":["image"]}, -"pict": {"type":"image/pict","icon":"image","string":"image","groups":["image"]}, -"pkg": {"type":"application/octet-stream"}, -"pki": {"type":"application/pkixcmp"}, -"pkipath": {"type":"application/pkix-pkipath"}, -"pko": {"type":"application/vnd.ms-pki.pko"}, -"pl": {"type":"text/plain"}, -"plb": {"type":"application/vnd.3gpp.pic-bw-large"}, -"plc": {"type":"application/vnd.mobius.plc"}, -"plf": {"type":"application/vnd.pocketlearn"}, -"plist": {"type":"application/x-plist"}, -"pls": {"type":"application/pls+xml"}, -"plx": {"type":"application/x-pixclscript"}, -"pm": {"type":"image/x-xpixmap"}, -"pm4": {"type":"application/x-pagemaker"}, -"pm5": {"type":"application/x-pagemaker"}, -"pml": {"type":"application/vnd.ctc-posml"}, -"png": {"type":"image/png","icon":"png","string":"image","groups":["image","web_image"]}, -"pnm": {"type":"image/x-portable-anymap"}, -"portpkg": {"type":"application/vnd.macports.portpkg"}, -"pot": {"type":"application/vnd.ms-powerpoint","icon":"powerpoint","groups":["presentation"]}, -"potm": {"type":"application/vnd.ms-powerpoint.template.macroenabled.12","icon":"powerpoint","groups":["presentation"]}, -"potx": {"type":"application/vnd.openxmlformats-officedocument.presentationml.template","icon":"powerpoint","groups":["presentation"]}, -"pov": {"type":"model/x-pov"}, -"ppa": {"type":"application/vnd.ms-powerpoint","icon":"powerpoint","groups":["presentation"]}, -"ppam": {"type":"application/vnd.ms-powerpoint.addin.macroenabled.12","icon":"powerpoint","groups":["presentation"]}, -"ppd": {"type":"application/vnd.cups-ppd"}, -"ppm": {"type":"image/x-portable-pixmap"}, -"pps": {"type":"application/vnd.ms-powerpoint","icon":"powerpoint","groups":["presentation"]}, -"ppsm": {"type":"application/vnd.ms-powerpoint.slideshow.macroenabled.12","icon":"powerpoint","groups":["presentation"]}, -"ppsx": {"type":"application/vnd.openxmlformats-officedocument.presentationml.slideshow","icon":"powerpoint","groups":["presentation"]}, -"ppt": {"type":"application/vnd.ms-powerpoint","icon":"powerpoint","groups":["presentation"]}, -"pptm": {"type":"application/vnd.ms-powerpoint.presentation.macroenabled.12","icon":"powerpoint","groups":["presentation"]}, -"pptx": {"type":"application/vnd.openxmlformats-officedocument.presentationml.presentation","icon":"powerpoint","groups":["presentation"]}, -"ppz": {"type":"application/mspowerpoint"}, -"pqa": {"type":"application/vnd.palm"}, -"prc": {"type":"application/x-mobipocket-ebook"}, -"pre": {"type":"application/vnd.lotus-freelance"}, -"prf": {"type":"application/pics-rules"}, -"prt": {"type":"application/pro_eng"}, -"ps": {"type":"application/postscript","icon":"pdf"}, -"psb": {"type":"application/vnd.3gpp.pic-bw-small"}, -"psd": {"type":"image/vnd.adobe.photoshop","icon":"psd"}, -"psf": {"type":"application/x-font-linux-psf"}, -"pskcxml": {"type":"application/pskc+xml"}, -"ptid": {"type":"application/vnd.pvi.ptid1"}, -"pub": {"type":"application/x-mspublisher","icon":"publisher","groups":["presentation"]}, -"pvb": {"type":"application/vnd.3gpp.pic-bw-var"}, -"pvu": {"type":"paleovu/x-pv"}, -"pwn": {"type":"application/vnd.3m.post-it-notes"}, -"pwz": {"type":"application/vnd.ms-powerpoint","icon":"powerpoint","groups":["presentation"]}, -"py": {"type":"text/x-script.phyton"}, -"pya": {"type":"audio/vnd.ms-playready.media.pya"}, -"pyc": {"type":"application/x-bytecode.python"}, -"pyv": {"type":"video/vnd.ms-playready.media.pyv"}, -"qam": {"type":"application/vnd.epson.quickanime"}, -"qbo": {"type":"application/vnd.intu.qbo"}, -"qcp": {"type":"audio/vnd.qcelp"}, -"qd3": {"type":"x-world/x-3dmf"}, -"qd3d": {"type":"x-world/x-3dmf"}, -"qfx": {"type":"application/vnd.intu.qfx"}, -"qif": {"type":"image/x-quicktime"}, -"qps": {"type":"application/vnd.publishare-delta-tree"}, -"qt": {"type":"video/quicktime","icon":"quicktime","string":"video","groups":["video","web_video"]}, -"qtc": {"type":"video/x-qtc"}, -"qti": {"type":"image/x-quicktime"}, -"qtif": {"type":"image/x-quicktime"}, -"qwd": {"type":"application/vnd.quark.quarkxpress"}, -"qwt": {"type":"application/vnd.quark.quarkxpress"}, -"qxb": {"type":"application/vnd.quark.quarkxpress"}, -"qxd": {"type":"application/vnd.quark.quarkxpress"}, -"qxl": {"type":"application/vnd.quark.quarkxpress"}, -"qxt": {"type":"application/vnd.quark.quarkxpress"}, -"ra": {"type":"audio/x-realaudio-plugin","icon":"audio","string":"audio","groups":["audio","web_audio"]}, -"ram": {"type":"audio/x-pn-realaudio-plugin","icon":"audio","string":"audio","groups":["audio"]}, -"rar": {"type":"application/x-rar-compressed","icon":"archive","string":"archive","groups":["archive"]}, -"ras": {"type":"image/x-cmu-raster"}, -"rast": {"type":"image/cmu-raster"}, -"rb": {"type":"text/plain"}, -"rcprofile": {"type":"application/vnd.ipunplugged.rcprofile"}, -"rdf": {"type":"application/rdf+xml"}, -"rdz": {"type":"application/vnd.data-vision.rdz"}, -"rep": {"type":"application/vnd.businessobjects"}, -"res": {"type":"application/x-dtbresource+xml"}, -"resx": {"type":"text/xml"}, -"rexx": {"type":"text/x-script.rexx"}, -"rf": {"type":"image/vnd.rn-realflash"}, -"rgb": {"type":"image/x-rgb"}, -"rhb": {"type":"text/xml","icon":"markup"}, -"rif": {"type":"application/reginfo+xml"}, -"rip": {"type":"audio/vnd.rip"}, -"ris": {"type":"application/x-research-info-systems"}, -"rl": {"type":"application/resource-lists+xml"}, -"rlc": {"type":"image/vnd.fujixerox.edmics-rlc"}, -"rld": {"type":"application/resource-lists-diff+xml"}, -"rm": {"type":"audio/x-pn-realaudio-plugin","icon":"audio","string":"audio","groups":["audio"]}, -"rmi": {"type":"audio/midi"}, -"rmm": {"type":"audio/x-pn-realaudio"}, -"rmp": {"type":"audio/x-pn-realaudio-plugin"}, -"rms": {"type":"application/vnd.jcp.javame.midlet-rms"}, -"rmvb": {"type":"application/vnd.rn-realmedia-vbr","icon":"video","string":"video","groups":["video"]}, -"rnc": {"type":"application/relax-ng-compact-syntax"}, -"rng": {"type":"application/ringing-tones"}, -"rnx": {"type":"application/vnd.rn-realplayer"}, -"roa": {"type":"application/rpki-roa"}, -"roff": {"type":"text/troff"}, -"rp": {"type":"image/vnd.rn-realpix"}, -"rp9": {"type":"application/vnd.cloanto.rp9"}, -"rpm": {"type":"application/x-rpm"}, -"rpss": {"type":"application/vnd.nokia.radio-presets"}, -"rpst": {"type":"application/vnd.nokia.radio-preset"}, -"rq": {"type":"application/sparql-query"}, -"rs": {"type":"application/rls-services+xml"}, -"rsd": {"type":"application/rsd+xml"}, -"rss": {"type":"application/rss+xml"}, -"rt": {"type":"text/richtext"}, -"rtf": {"type":"text/rtf","icon":"text","groups":["document"]}, -"rtx": {"type":"text/richtext","icon":"text"}, -"rv": {"type":"audio/x-pn-realaudio-plugin","icon":"audio","string":"video","groups":["video"]}, -"s": {"type":"text/x-asm"}, -"s3m": {"type":"audio/s3m"}, -"saf": {"type":"application/vnd.yamaha.smaf-audio"}, -"safariextz": {"type":"application/octet-stream"}, -"sass": {"type":"text/x-sass"}, -"saveme": {"type":"application/octet-stream"}, -"sbk": {"type":"application/x-tbook"}, -"sbml": {"type":"application/sbml+xml"}, -"sc": {"type":"application/vnd.ibm.secure-container"}, -"scd": {"type":"application/x-msschedule"}, -"scm": {"type":"application/vnd.lotus-screencam"}, -"scq": {"type":"application/scvp-cv-request"}, -"scs": {"type":"application/scvp-cv-response"}, -"scss": {"type":"text/x-scss","icon":"text","groups":["web_file"]}, -"scurl": {"type":"text/vnd.curl.scurl"}, -"sda": {"type":"application/vnd.stardivision.draw","icon":"draw"}, -"sdc": {"type":"application/vnd.stardivision.calc","icon":"calc"}, -"sdd": {"type":"application/vnd.stardivision.impress","icon":"impress"}, -"sdkd": {"type":"application/vnd.solent.sdkm+xml"}, -"sdkm": {"type":"application/vnd.solent.sdkm+xml"}, -"sdml": {"type":"text/plain"}, -"sdp": {"type":"application/sdp"}, -"sdr": {"type":"application/sounder"}, -"sdw": {"type":"application/vnd.stardivision.writer","icon":"writer"}, -"sea": {"type":"application/sea"}, -"see": {"type":"application/vnd.seemail"}, -"seed": {"type":"application/vnd.fdsn.seed"}, -"sema": {"type":"application/vnd.sema"}, -"semd": {"type":"application/vnd.semd"}, -"semf": {"type":"application/vnd.semf"}, -"ser": {"type":"application/java-serialized-object"}, -"set": {"type":"application/set"}, -"setpay": {"type":"application/set-payment-initiation"}, -"setreg": {"type":"application/set-registration-initiation"}, -"sfd-hdstx": {"type":"application/vnd.hydrostatix.sof-data"}, -"sfs": {"type":"application/vnd.spotfire.sfs"}, -"sfv": {"type":"text/x-sfv"}, -"sgi": {"type":"image/sgi"}, -"sgl": {"type":"application/vnd.stardivision.writer-global"}, -"sgm": {"type":"text/sgml"}, -"sgml": {"type":"text/sgml"}, -"sh": {"type":"application/x-sh","icon":"sourcecode"}, -"shar": {"type":"application/x-shar"}, -"shf": {"type":"application/shf+xml"}, -"shtml": {"type":"text/html"}, -"sid": {"type":"image/x-mrsid-image"}, -"sig": {"type":"application/pgp-signature"}, -"sil": {"type":"audio/silk"}, -"silo": {"type":"model/mesh"}, -"sis": {"type":"application/vnd.symbian.install"}, -"sisx": {"type":"application/vnd.symbian.install"}, -"sit": {"type":"application/x-stuffit","icon":"archive","string":"archive","groups":["archive"]}, -"sitx": {"type":"application/x-stuffitx"}, -"skd": {"type":"application/vnd.koan"}, -"skm": {"type":"application/vnd.koan"}, -"skp": {"type":"application/vnd.koan"}, -"skt": {"type":"application/vnd.koan"}, -"sl": {"type":"application/x-seelogo"}, -"sldm": {"type":"application/vnd.ms-powerpoint.slide.macroenabled.12","icon":"powerpoint","groups":["presentation"]}, -"sldx": {"type":"application/vnd.openxmlformats-officedocument.presentationml.slide"}, -"slt": {"type":"application/vnd.epson.salt"}, -"sm": {"type":"application/vnd.stepmania.stepchart"}, -"smf": {"type":"application/vnd.stardivision.math","icon":"math"}, -"smi": {"type":"application/smil","icon":"text"}, -"smil": {"type":"application/smil","icon":"text"}, -"smv": {"type":"video/x-smv"}, -"smzip": {"type":"application/vnd.stepmania.package"}, -"snd": {"type":"audio/basic"}, -"snf": {"type":"application/x-font-snf"}, -"so": {"type":"application/octet-stream"}, -"sol": {"type":"application/solids"}, -"spc": {"type":"application/x-pkcs7-certificates"}, -"spf": {"type":"application/vnd.yamaha.smaf-phrase"}, -"spl": {"type":"application/x-futuresplash"}, -"spot": {"type":"text/vnd.in3d.spot"}, -"spp": {"type":"application/scvp-vp-response"}, -"spq": {"type":"application/scvp-vp-request"}, -"spr": {"type":"application/x-sprite"}, -"sprite": {"type":"application/x-sprite"}, -"spx": {"type":"audio/ogg"}, -"sql": {"type":"application/x-sql"}, -"sqt": {"type":"text/xml","icon":"markup"}, -"src": {"type":"application/x-wais-source"}, -"srt": {"type":"application/x-subrip"}, -"sru": {"type":"application/sru+xml"}, -"srx": {"type":"application/sparql-results+xml"}, -"ssdl": {"type":"application/ssdl+xml"}, -"sse": {"type":"application/vnd.kodak-descriptor"}, -"ssf": {"type":"application/vnd.epson.ssf"}, -"ssi": {"type":"text/x-server-parsed-html"}, -"ssm": {"type":"application/streamingmedia"}, -"ssml": {"type":"application/ssml+xml"}, -"sst": {"type":"application/vnd.ms-pki.certstore"}, -"st": {"type":"application/vnd.sailingtracker.track"}, -"stc": {"type":"application/vnd.sun.xml.calc.template","icon":"calc"}, -"std": {"type":"application/vnd.sun.xml.draw.template","icon":"draw"}, -"step": {"type":"application/step"}, -"stf": {"type":"application/vnd.wt.stf"}, -"sti": {"type":"application/vnd.sun.xml.impress.template","icon":"impress","groups":["presentation"]}, -"stk": {"type":"application/hyperstudio"}, -"stl": {"type":"application/vnd.ms-pki.stl"}, -"stp": {"type":"application/step"}, -"str": {"type":"application/vnd.pg.format"}, -"stw": {"type":"application/vnd.sun.xml.writer.template","icon":"writer"}, -"styl": {"type":"text/x-styl"}, -"sub": {"type":"image/vnd.dvb.subtitle"}, -"sus": {"type":"application/vnd.sus-calendar"}, -"susp": {"type":"application/vnd.sus-calendar"}, -"sv4cpio": {"type":"application/x-sv4cpio"}, -"sv4crc": {"type":"application/x-sv4crc"}, -"svc": {"type":"application/vnd.dvb.service"}, -"svd": {"type":"application/vnd.svd"}, -"svf": {"type":"image/vnd.dwg"}, -"svg": {"type":"image/svg+xml","icon":"image","string":"image","groups":["image","web_image"]}, -"svgz": {"type":"image/svg+xml","icon":"image","string":"image","groups":["image","web_image"]}, -"svr": {"type":"application/x-world"}, -"swa": {"type":"application/x-director","icon":"flash"}, -"swf": {"type":"application/x-shockwave-flash","icon":"flash","groups":["video"]}, -"swfl": {"type":"application/x-shockwave-flash","icon":"flash","groups":["video"]}, -"swi": {"type":"application/vnd.aristanetworks.swi"}, -"sxc": {"type":"application/vnd.sun.xml.calc","icon":"calc"}, -"sxd": {"type":"application/vnd.sun.xml.draw","icon":"draw"}, -"sxg": {"type":"application/vnd.sun.xml.writer.global","icon":"writer"}, -"sxi": {"type":"application/vnd.sun.xml.impress","icon":"impress","groups":["presentation"]}, -"sxm": {"type":"application/vnd.sun.xml.math","icon":"math"}, -"sxw": {"type":"application/vnd.sun.xml.writer","icon":"writer"}, -"t": {"type":"text/troff"}, -"t3": {"type":"application/x-t3vm-image"}, -"taglet": {"type":"application/vnd.mynfc"}, -"talk": {"type":"text/x-speech"}, -"tao": {"type":"application/vnd.tao.intent-module-archive"}, -"tar": {"type":"application/x-tar","icon":"archive","string":"archive","groups":["archive"]}, -"tbk": {"type":"application/toolbook"}, -"tcap": {"type":"application/vnd.3gpp2.tcap"}, -"tcl": {"type":"application/x-tcl"}, -"tcsh": {"type":"text/x-script.tcsh"}, -"teacher": {"type":"application/vnd.smart.teacher"}, -"tei": {"type":"application/tei+xml"}, -"teicorpus": {"type":"application/tei+xml"}, -"tex": {"type":"application/x-tex","icon":"text"}, -"texi": {"type":"application/x-texinfo","icon":"text"}, -"texinfo": {"type":"application/x-texinfo","icon":"text"}, -"text": {"type":"text/plain"}, -"tfi": {"type":"application/thraud+xml"}, -"tfm": {"type":"application/x-tex-tfm"}, -"tga": {"type":"image/x-tga"}, -"tgz": {"type":"application/g-zip","icon":"archive","string":"archive","groups":["archive"]}, -"thmx": {"type":"application/vnd.ms-officetheme"}, -"tif": {"type":"image/tiff","icon":"tiff","string":"image","groups":["image"]}, -"tiff": {"type":"image/tiff","icon":"tiff","string":"image","groups":["image"]}, -"tmo": {"type":"application/vnd.tmobile-livetv"}, -"torrent": {"type":"application/x-bittorrent"}, -"tpl": {"type":"application/vnd.groove-tool-template"}, -"tpt": {"type":"application/vnd.trid.tpt"}, -"tr": {"type":"text/troff"}, -"tra": {"type":"application/vnd.trueapp"}, -"trm": {"type":"application/x-msterminal"}, -"ts": {"type":"video/MP2T","icon":"mpeg","string":"video","groups":["video","web_video"]}, -"tsd": {"type":"application/timestamped-data"}, -"tsi": {"type":"audio/tsp-audio"}, -"tsp": {"type":"application/dsptype"}, -"tsv": {"type":"text/tab-separated-values","icon":"text"}, -"ttc": {"type":"application/x-font-ttf"}, -"ttf": {"type":"application/x-font-ttf"}, -"ttl": {"type":"text/turtle"}, -"turbot": {"type":"image/florian"}, -"twd": {"type":"application/vnd.simtech-mindmapper"}, -"twds": {"type":"application/vnd.simtech-mindmapper"}, -"txd": {"type":"application/vnd.genomatix.tuxedo"}, -"txf": {"type":"application/vnd.mobius.txf"}, -"txt": {"type":"text/plain","icon":"text","defaulticon":true}, -"udeb": {"type":"application/x-debian-package"}, -"ufd": {"type":"application/vnd.ufdl"}, -"ufdl": {"type":"application/vnd.ufdl"}, -"uil": {"type":"text/x-uil"}, -"ulx": {"type":"application/x-glulx"}, -"umj": {"type":"application/vnd.umajin"}, -"uni": {"type":"text/uri-list"}, -"unis": {"type":"text/uri-list"}, -"unityweb": {"type":"application/vnd.unity"}, -"unv": {"type":"application/i-deas"}, -"uoml": {"type":"application/vnd.uoml+xml"}, -"uri": {"type":"text/uri-list"}, -"uris": {"type":"text/uri-list"}, -"urls": {"type":"text/uri-list"}, -"ustar": {"type":"application/x-ustar"}, -"utz": {"type":"application/vnd.uiq.theme"}, -"uu": {"type":"text/x-uuencode"}, -"uue": {"type":"text/x-uuencode"}, -"uva": {"type":"audio/vnd.dece.audio"}, -"uvd": {"type":"application/vnd.dece.data"}, -"uvf": {"type":"application/vnd.dece.data"}, -"uvg": {"type":"image/vnd.dece.graphic"}, -"uvh": {"type":"video/vnd.dece.hd"}, -"uvi": {"type":"image/vnd.dece.graphic"}, -"uvm": {"type":"video/vnd.dece.mobile"}, -"uvp": {"type":"video/vnd.dece.pd"}, -"uvs": {"type":"video/vnd.dece.sd"}, -"uvt": {"type":"application/vnd.dece.ttml+xml"}, -"uvu": {"type":"video/vnd.uvvu.mp4"}, -"uvv": {"type":"video/vnd.dece.video"}, -"uvva": {"type":"audio/vnd.dece.audio"}, -"uvvd": {"type":"application/vnd.dece.data"}, -"uvvf": {"type":"application/vnd.dece.data"}, -"uvvg": {"type":"image/vnd.dece.graphic"}, -"uvvh": {"type":"video/vnd.dece.hd"}, -"uvvi": {"type":"image/vnd.dece.graphic"}, -"uvvm": {"type":"video/vnd.dece.mobile"}, -"uvvp": {"type":"video/vnd.dece.pd"}, -"uvvs": {"type":"video/vnd.dece.sd"}, -"uvvt": {"type":"application/vnd.dece.ttml+xml"}, -"uvvu": {"type":"video/vnd.uvvu.mp4"}, -"uvvv": {"type":"video/vnd.dece.video"}, -"uvvx": {"type":"application/vnd.dece.unspecified"}, -"uvvz": {"type":"application/vnd.dece.zip"}, -"uvx": {"type":"application/vnd.dece.unspecified"}, -"uvz": {"type":"application/vnd.dece.zip"}, -"vcard": {"type":"text/vcard"}, -"vcd": {"type":"application/x-cdlink"}, -"vcf": {"type":"text/x-vcard"}, -"vcg": {"type":"application/vnd.groove-vcard"}, -"vcs": {"type":"text/x-vcalendar"}, -"vcx": {"type":"application/vnd.vcx"}, -"vda": {"type":"application/vda"}, -"vdo": {"type":"video/vdo"}, -"vew": {"type":"application/groupwise"}, -"vis": {"type":"application/vnd.visionary"}, -"viv": {"type":"video/vnd.vivo"}, -"vivo": {"type":"video/vivo"}, -"vmd": {"type":"application/vocaltec-media-desc"}, -"vmf": {"type":"application/vocaltec-media-file"}, -"vob": {"type":"video/x-ms-vob"}, -"voc": {"type":"audio/voc"}, -"vor": {"type":"application/vnd.stardivision.writer","icon":"writer"}, -"vos": {"type":"video/vosaic"}, -"vox": {"type":"audio/voxware"}, -"vqe": {"type":"audio/x-twinvq-plugin"}, -"vqf": {"type":"audio/x-twinvq"}, -"vql": {"type":"audio/x-twinvq-plugin"}, -"vrml": {"type":"model/vrml"}, -"vrt": {"type":"x-world/x-vrt"}, -"vsd": {"type":"application/vnd.visio"}, -"vsf": {"type":"application/vnd.vsf"}, -"vss": {"type":"application/vnd.visio"}, -"vst": {"type":"application/vnd.visio"}, -"vsw": {"type":"application/vnd.visio"}, -"vtt": {"type":"text/vtt","icon":"text","groups":["html_track"]}, -"vtu": {"type":"model/vnd.vtu"}, -"vxml": {"type":"application/voicexml+xml"}, -"w3d": {"type":"application/x-director"}, -"w60": {"type":"application/wordperfect6.0"}, -"w61": {"type":"application/wordperfect6.1"}, -"w6w": {"type":"application/msword"}, -"wad": {"type":"application/x-doom"}, -"wav": {"type":"audio/wav","icon":"wav","string":"audio","groups":["audio","html_audio","web_audio"]}, -"wax": {"type":"audio/x-ms-wax"}, -"wb1": {"type":"application/x-qpro"}, -"wbmp": {"type":"image/vnd.wap.wbmp"}, -"wbs": {"type":"application/vnd.criticaltools.wbs+xml"}, -"wbxml": {"type":"application/vnd.wap.wbxml"}, -"wcm": {"type":"application/vnd.ms-works"}, -"wdb": {"type":"application/vnd.ms-works"}, -"wdp": {"type":"image/vnd.ms-photo"}, -"web": {"type":"application/vnd.xara"}, -"weba": {"type":"audio/webm","icon":"audio","string":"audio","groups":["audio","html_audio","web_audio"]}, -"webm": {"type":"video/webm","icon":"video","string":"video","groups":["html_video","video","web_video"]}, -"webp": {"type":"image/webp"}, -"wg": {"type":"application/vnd.pmi.widget"}, -"wgt": {"type":"application/widget"}, -"wiz": {"type":"application/msword"}, -"wk1": {"type":"application/x-123"}, -"wks": {"type":"application/vnd.ms-works"}, -"wm": {"type":"video/x-ms-wm"}, -"wma": {"type":"audio/x-ms-wma","icon":"audio","string":"audio","groups":["audio"]}, -"wmd": {"type":"application/x-ms-wmd"}, -"wmf": {"type":"application/x-msmetafile"}, -"wml": {"type":"text/vnd.wap.wml"}, -"wmlc": {"type":"application/vnd.wap.wmlc"}, -"wmls": {"type":"text/vnd.wap.wmlscript"}, -"wmlsc": {"type":"application/vnd.wap.wmlscriptc"}, -"wmv": {"type":"video/x-ms-wmv","icon":"wmv","string":"video","groups":["video"]}, -"wmx": {"type":"video/x-ms-wmx"}, -"wmz": {"type":"application/x-ms-wmz"}, -"woff": {"type":"application/x-font-woff"}, -"word": {"type":"application/msword"}, -"wp": {"type":"application/wordperfect"}, -"wp5": {"type":"application/wordperfect"}, -"wp6": {"type":"application/wordperfect"}, -"wpd": {"type":"application/vnd.wordperfect"}, -"wpl": {"type":"application/vnd.ms-wpl"}, -"wps": {"type":"application/vnd.ms-works"}, -"wq1": {"type":"application/x-lotus"}, -"wqd": {"type":"application/vnd.wqd"}, -"wri": {"type":"application/x-mswrite"}, -"wrl": {"type":"model/vrml"}, -"wrz": {"type":"model/vrml"}, -"wsc": {"type":"text/scriplet"}, -"wsdl": {"type":"application/wsdl+xml"}, -"wspolicy": {"type":"application/wspolicy+xml"}, -"wsrc": {"type":"application/x-wais-source"}, -"wtb": {"type":"application/vnd.webturbo"}, -"wtk": {"type":"application/x-wintalk"}, -"wvx": {"type":"video/x-ms-wvx"}, -"x-png": {"type":"image/png","icon":"png","string":"image","groups":["image","web_image"]}, -"x3d": {"type":"model/x3d+xml"}, -"x3db": {"type":"model/x3d+binary"}, -"x3dbz": {"type":"model/x3d+binary"}, -"x3dv": {"type":"model/x3d+vrml"}, -"x3dvz": {"type":"model/x3d+vrml"}, -"x3dz": {"type":"model/x3d+xml"}, -"xaml": {"type":"application/xaml+xml"}, -"xap": {"type":"application/x-silverlight-app"}, -"xar": {"type":"application/vnd.xara"}, -"xbap": {"type":"application/x-ms-xbap"}, -"xbd": {"type":"application/vnd.fujixerox.docuworks.binder"}, -"xbk": {"type":"application/x-smarttech-notebook","icon":"archive"}, -"xbm": {"type":"image/x-xbitmap"}, -"xdf": {"type":"application/xcap-diff+xml"}, -"xdm": {"type":"application/vnd.syncml.dm+xml"}, -"xdp": {"type":"application/vnd.adobe.xdp+xml","icon":"pdf"}, -"xdr": {"type":"video/x-amt-demorun"}, -"xdssc": {"type":"application/dssc+xml"}, -"xdw": {"type":"application/vnd.fujixerox.docuworks"}, -"xenc": {"type":"application/xenc+xml"}, -"xer": {"type":"application/patch-ops-error+xml"}, -"xfd": {"type":"application/vnd.xfdl","icon":"pdf"}, -"xfdf": {"type":"application/vnd.adobe.xfdf","icon":"pdf"}, -"xfdl": {"type":"application/vnd.xfdl"}, -"xgz": {"type":"xgl/drawing"}, -"xht": {"type":"application/xhtml+xml"}, -"xhtml": {"type":"application/xhtml+xml","icon":"html","groups":["web_file"]}, -"xhvml": {"type":"application/xv+xml"}, -"xif": {"type":"image/vnd.xiff"}, -"xl": {"type":"application/excel"}, -"xla": {"type":"application/vnd.ms-excel","icon":"spreadsheet"}, -"xlam": {"type":"application/vnd.ms-excel.addin.macroenabled.12","icon":"spreadsheet"}, -"xlb": {"type":"application/excel"}, -"xlc": {"type":"application/vnd.ms-excel","icon":"spreadsheet"}, -"xld": {"type":"application/excel"}, -"xlf": {"type":"application/x-xliff+xml"}, -"xlk": {"type":"application/excel"}, -"xll": {"type":"application/excel"}, -"xlm": {"type":"application/vnd.ms-excel","icon":"spreadsheet"}, -"xls": {"type":"application/vnd.ms-excel","icon":"spreadsheet","groups":["spreadsheet"]}, -"xlsb": {"type":"application/vnd.ms-excel.sheet.binary.macroenabled.12","icon":"spreadsheet"}, -"xlsm": {"type":"application/vnd.ms-excel.sheet.macroenabled.12","icon":"spreadsheet","groups":["spreadsheet"]}, -"xlsx": {"type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","icon":"spreadsheet","groups":["spreadsheet"]}, -"xlt": {"type":"application/vnd.ms-excel","icon":"spreadsheet"}, -"xltm": {"type":"application/vnd.ms-excel.template.macroenabled.12","icon":"spreadsheet"}, -"xltx": {"type":"application/vnd.openxmlformats-officedocument.spreadsheetml.template","icon":"spreadsheet"}, -"xlv": {"type":"application/excel"}, -"xlw": {"type":"application/vnd.ms-excel","icon":"spreadsheet"}, -"xm": {"type":"audio/xm"}, -"xml": {"type":"application/xml","icon":"markup"}, -"xmz": {"type":"xgl/movie"}, -"xo": {"type":"application/vnd.olpc-sugar"}, -"xop": {"type":"application/xop+xml"}, -"xpi": {"type":"application/x-xpinstall"}, -"xpix": {"type":"application/x-vnd.ls-xpix"}, -"xpl": {"type":"application/xproc+xml"}, -"xpm": {"type":"image/x-xpixmap"}, -"xpr": {"type":"application/vnd.is-xpr"}, -"xps": {"type":"application/vnd.ms-xpsdocument"}, -"xpw": {"type":"application/vnd.intercon.formnet"}, -"xpx": {"type":"application/vnd.intercon.formnet"}, -"xsl": {"type":"text/xml","icon":"markup"}, -"xslt": {"type":"application/xslt+xml"}, -"xsm": {"type":"application/vnd.syncml+xml"}, -"xspf": {"type":"application/xspf+xml"}, -"xsr": {"type":"video/x-amt-showrun"}, -"xul": {"type":"application/vnd.mozilla.xul+xml"}, -"xvm": {"type":"application/xv+xml"}, -"xvml": {"type":"application/xv+xml"}, -"xwd": {"type":"image/x-xwindowdump"}, -"xxx": {"type":"document/unknown","icon":"unknown"}, -"xyz": {"type":"chemical/x-xyz"}, -"xz": {"type":"application/x-xz"}, -"yaml": {"type":"text/yaml"}, -"yang": {"type":"application/yang"}, -"yin": {"type":"application/yin+xml"}, -"yml": {"type":"text/yaml"}, -"z": {"type":"application/x-compress"}, -"z1": {"type":"application/x-zmachine"}, -"z2": {"type":"application/x-zmachine"}, -"z3": {"type":"application/x-zmachine"}, -"z4": {"type":"application/x-zmachine"}, -"z5": {"type":"application/x-zmachine"}, -"z6": {"type":"application/x-zmachine"}, -"z7": {"type":"application/x-zmachine"}, -"z8": {"type":"application/x-zmachine"}, -"zaz": {"type":"application/vnd.zzazz.deck+xml"}, -"zip": {"type":"application/zip","icon":"archive","string":"archive","groups":["archive"]}, -"zir": {"type":"application/vnd.zul"}, -"zirz": {"type":"application/vnd.zul"}, -"zmm": {"type":"application/vnd.handheld-entertainment+xml"}, -"zoo": {"type":"application/octet-stream"}, -"zsh": {"type":"text/x-script.zsh"} -} \ No newline at end of file diff --git a/src/assets/fonts/noto/LICENSE.txt b/src/assets/fonts/noto/LICENSE.txt deleted file mode 100644 index 75b52484e..000000000 --- a/src/assets/fonts/noto/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/src/assets/fonts/noto/NotoSans-Bold.woff b/src/assets/fonts/noto/NotoSans-Bold.woff deleted file mode 100644 index fe87166b6..000000000 Binary files a/src/assets/fonts/noto/NotoSans-Bold.woff and /dev/null differ diff --git a/src/assets/fonts/noto/NotoSans-BoldItalic.woff b/src/assets/fonts/noto/NotoSans-BoldItalic.woff deleted file mode 100644 index f5ac220e8..000000000 Binary files a/src/assets/fonts/noto/NotoSans-BoldItalic.woff and /dev/null differ diff --git a/src/assets/fonts/noto/NotoSans-Italic.woff b/src/assets/fonts/noto/NotoSans-Italic.woff deleted file mode 100644 index f9fefea1d..000000000 Binary files a/src/assets/fonts/noto/NotoSans-Italic.woff and /dev/null differ diff --git a/src/assets/fonts/noto/NotoSans-Regular.woff b/src/assets/fonts/noto/NotoSans-Regular.woff deleted file mode 100644 index 07640c159..000000000 Binary files a/src/assets/fonts/noto/NotoSans-Regular.woff and /dev/null differ diff --git a/src/assets/fonts/roboto/LICENSE.txt b/src/assets/fonts/roboto/LICENSE.txt deleted file mode 100644 index 75b52484e..000000000 --- a/src/assets/fonts/roboto/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/src/assets/fonts/roboto/Roboto-Bold.woff b/src/assets/fonts/roboto/Roboto-Bold.woff deleted file mode 100644 index 83a333ad4..000000000 Binary files a/src/assets/fonts/roboto/Roboto-Bold.woff and /dev/null differ diff --git a/src/assets/fonts/roboto/Roboto-BoldItalic.woff b/src/assets/fonts/roboto/Roboto-BoldItalic.woff deleted file mode 100644 index 7f8c135d1..000000000 Binary files a/src/assets/fonts/roboto/Roboto-BoldItalic.woff and /dev/null differ diff --git a/src/assets/fonts/roboto/Roboto-Italic.woff b/src/assets/fonts/roboto/Roboto-Italic.woff deleted file mode 100644 index 6e4197d44..000000000 Binary files a/src/assets/fonts/roboto/Roboto-Italic.woff and /dev/null differ diff --git a/src/assets/fonts/roboto/Roboto-Light.woff b/src/assets/fonts/roboto/Roboto-Light.woff deleted file mode 100644 index d0158be69..000000000 Binary files a/src/assets/fonts/roboto/Roboto-Light.woff and /dev/null differ diff --git a/src/assets/fonts/roboto/Roboto-LightItalic.woff b/src/assets/fonts/roboto/Roboto-LightItalic.woff deleted file mode 100644 index f20dfa795..000000000 Binary files a/src/assets/fonts/roboto/Roboto-LightItalic.woff and /dev/null differ diff --git a/src/assets/fonts/roboto/Roboto-Medium.woff b/src/assets/fonts/roboto/Roboto-Medium.woff deleted file mode 100644 index cbac5e5dd..000000000 Binary files a/src/assets/fonts/roboto/Roboto-Medium.woff and /dev/null differ diff --git a/src/assets/fonts/roboto/Roboto-MediumItalic.woff b/src/assets/fonts/roboto/Roboto-MediumItalic.woff deleted file mode 100644 index 3e635c1a2..000000000 Binary files a/src/assets/fonts/roboto/Roboto-MediumItalic.woff and /dev/null differ diff --git a/src/assets/fonts/roboto/Roboto-Regular.woff b/src/assets/fonts/roboto/Roboto-Regular.woff deleted file mode 100644 index f43dd1b5f..000000000 Binary files a/src/assets/fonts/roboto/Roboto-Regular.woff and /dev/null differ diff --git a/src/assets/fonts/slash-icon.woff b/src/assets/fonts/slash-icon.woff deleted file mode 100644 index 5e02178e4..000000000 Binary files a/src/assets/fonts/slash-icon.woff and /dev/null differ diff --git a/src/assets/icon/favicon.ico b/src/assets/icon/favicon.ico deleted file mode 100644 index 483c9647c..000000000 Binary files a/src/assets/icon/favicon.ico and /dev/null differ diff --git a/src/assets/icon/icon.png b/src/assets/icon/icon.png deleted file mode 100644 index f41b1f803..000000000 Binary files a/src/assets/icon/icon.png and /dev/null differ diff --git a/src/assets/img/completion/completion-auto-fail.svg b/src/assets/img/completion/completion-auto-fail.svg deleted file mode 100644 index 771adf36f..000000000 --- a/src/assets/img/completion/completion-auto-fail.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - -]> - - - - - - diff --git a/src/assets/img/completion/completion-auto-n-override.svg b/src/assets/img/completion/completion-auto-n-override.svg deleted file mode 100644 index 6100638d0..000000000 --- a/src/assets/img/completion/completion-auto-n-override.svg +++ /dev/null @@ -1,3 +0,0 @@ - -]> \ No newline at end of file diff --git a/src/assets/img/completion/completion-auto-n.svg b/src/assets/img/completion/completion-auto-n.svg deleted file mode 100644 index 6a8bc6222..000000000 --- a/src/assets/img/completion/completion-auto-n.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - -]> - - - - - diff --git a/src/assets/img/completion/completion-auto-pass.svg b/src/assets/img/completion/completion-auto-pass.svg deleted file mode 100644 index 44df83f15..000000000 --- a/src/assets/img/completion/completion-auto-pass.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - -]> - - - - - - diff --git a/src/assets/img/completion/completion-auto-y-override.svg b/src/assets/img/completion/completion-auto-y-override.svg deleted file mode 100644 index 13cf5d700..000000000 --- a/src/assets/img/completion/completion-auto-y-override.svg +++ /dev/null @@ -1,3 +0,0 @@ - -]> \ No newline at end of file diff --git a/src/assets/img/completion/completion-auto-y.svg b/src/assets/img/completion/completion-auto-y.svg deleted file mode 100644 index 14822e173..000000000 --- a/src/assets/img/completion/completion-auto-y.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - -]> - - - - - - diff --git a/src/assets/img/completion/completion-manual-n-override.svg b/src/assets/img/completion/completion-manual-n-override.svg deleted file mode 100644 index cccfb99cd..000000000 --- a/src/assets/img/completion/completion-manual-n-override.svg +++ /dev/null @@ -1,3 +0,0 @@ - -]> \ No newline at end of file diff --git a/src/assets/img/completion/completion-manual-n.svg b/src/assets/img/completion/completion-manual-n.svg deleted file mode 100644 index f7750e25a..000000000 --- a/src/assets/img/completion/completion-manual-n.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - -]> - - - - - diff --git a/src/assets/img/completion/completion-manual-y-override.svg b/src/assets/img/completion/completion-manual-y-override.svg deleted file mode 100644 index 69270ba3e..000000000 --- a/src/assets/img/completion/completion-manual-y-override.svg +++ /dev/null @@ -1,3 +0,0 @@ - -]> \ No newline at end of file diff --git a/src/assets/img/completion/completion-manual-y.svg b/src/assets/img/completion/completion-manual-y.svg deleted file mode 100644 index 3b91bdbc7..000000000 --- a/src/assets/img/completion/completion-manual-y.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - -]> - - - - - - diff --git a/src/assets/img/files/archive-64.png b/src/assets/img/files/archive-64.png deleted file mode 100644 index ba7111a02..000000000 Binary files a/src/assets/img/files/archive-64.png and /dev/null differ diff --git a/src/assets/img/files/audio-64.png b/src/assets/img/files/audio-64.png deleted file mode 100644 index e98c78dd7..000000000 Binary files a/src/assets/img/files/audio-64.png and /dev/null differ diff --git a/src/assets/img/files/avi-64.png b/src/assets/img/files/avi-64.png deleted file mode 100644 index ec1a942cd..000000000 Binary files a/src/assets/img/files/avi-64.png and /dev/null differ diff --git a/src/assets/img/files/base-64.png b/src/assets/img/files/base-64.png deleted file mode 100644 index cf698f525..000000000 Binary files a/src/assets/img/files/base-64.png and /dev/null differ diff --git a/src/assets/img/files/bmp-64.png b/src/assets/img/files/bmp-64.png deleted file mode 100644 index 562e7bbab..000000000 Binary files a/src/assets/img/files/bmp-64.png and /dev/null differ diff --git a/src/assets/img/files/calc-64.png b/src/assets/img/files/calc-64.png deleted file mode 100644 index b813dd2cb..000000000 Binary files a/src/assets/img/files/calc-64.png and /dev/null differ diff --git a/src/assets/img/files/chart-64.png b/src/assets/img/files/chart-64.png deleted file mode 100644 index 4b8f85bd6..000000000 Binary files a/src/assets/img/files/chart-64.png and /dev/null differ diff --git a/src/assets/img/files/database-64.png b/src/assets/img/files/database-64.png deleted file mode 100644 index 33d5043c1..000000000 Binary files a/src/assets/img/files/database-64.png and /dev/null differ diff --git a/src/assets/img/files/document-64.png b/src/assets/img/files/document-64.png deleted file mode 100644 index 0888ebbbd..000000000 Binary files a/src/assets/img/files/document-64.png and /dev/null differ diff --git a/src/assets/img/files/draw-64.png b/src/assets/img/files/draw-64.png deleted file mode 100644 index 1b827c7c6..000000000 Binary files a/src/assets/img/files/draw-64.png and /dev/null differ diff --git a/src/assets/img/files/eps-64.png b/src/assets/img/files/eps-64.png deleted file mode 100644 index c42492441..000000000 Binary files a/src/assets/img/files/eps-64.png and /dev/null differ diff --git a/src/assets/img/files/epub-64.png b/src/assets/img/files/epub-64.png deleted file mode 100644 index 298d5dcd9..000000000 Binary files a/src/assets/img/files/epub-64.png and /dev/null differ diff --git a/src/assets/img/files/flash-64.png b/src/assets/img/files/flash-64.png deleted file mode 100644 index 01d28e03e..000000000 Binary files a/src/assets/img/files/flash-64.png and /dev/null differ diff --git a/src/assets/img/files/folder-64.png b/src/assets/img/files/folder-64.png deleted file mode 100644 index 2508ab252..000000000 Binary files a/src/assets/img/files/folder-64.png and /dev/null differ diff --git a/src/assets/img/files/folder-open-64.png b/src/assets/img/files/folder-open-64.png deleted file mode 100644 index 27f7271bd..000000000 Binary files a/src/assets/img/files/folder-open-64.png and /dev/null differ diff --git a/src/assets/img/files/gif-64.png b/src/assets/img/files/gif-64.png deleted file mode 100644 index 2373292b7..000000000 Binary files a/src/assets/img/files/gif-64.png and /dev/null differ diff --git a/src/assets/img/files/h5p-64.png b/src/assets/img/files/h5p-64.png deleted file mode 100644 index cdd1c9b1b..000000000 Binary files a/src/assets/img/files/h5p-64.png and /dev/null differ diff --git a/src/assets/img/files/html-64.png b/src/assets/img/files/html-64.png deleted file mode 100644 index 7f703bb83..000000000 Binary files a/src/assets/img/files/html-64.png and /dev/null differ diff --git a/src/assets/img/files/image-64.png b/src/assets/img/files/image-64.png deleted file mode 100644 index 2d8f9e4fa..000000000 Binary files a/src/assets/img/files/image-64.png and /dev/null differ diff --git a/src/assets/img/files/impress-64.png b/src/assets/img/files/impress-64.png deleted file mode 100644 index c279c62ef..000000000 Binary files a/src/assets/img/files/impress-64.png and /dev/null differ diff --git a/src/assets/img/files/isf-64.png b/src/assets/img/files/isf-64.png deleted file mode 100644 index ad5a18867..000000000 Binary files a/src/assets/img/files/isf-64.png and /dev/null differ diff --git a/src/assets/img/files/jpeg-64.png b/src/assets/img/files/jpeg-64.png deleted file mode 100644 index b4fc0c998..000000000 Binary files a/src/assets/img/files/jpeg-64.png and /dev/null differ diff --git a/src/assets/img/files/markup-64.png b/src/assets/img/files/markup-64.png deleted file mode 100644 index b89072713..000000000 Binary files a/src/assets/img/files/markup-64.png and /dev/null differ diff --git a/src/assets/img/files/math-64.png b/src/assets/img/files/math-64.png deleted file mode 100644 index d98beea61..000000000 Binary files a/src/assets/img/files/math-64.png and /dev/null differ diff --git a/src/assets/img/files/moodle-64.png b/src/assets/img/files/moodle-64.png deleted file mode 100644 index 44ad3a37d..000000000 Binary files a/src/assets/img/files/moodle-64.png and /dev/null differ diff --git a/src/assets/img/files/mp3-64.png b/src/assets/img/files/mp3-64.png deleted file mode 100644 index 13b8da0b8..000000000 Binary files a/src/assets/img/files/mp3-64.png and /dev/null differ diff --git a/src/assets/img/files/mpeg-64.png b/src/assets/img/files/mpeg-64.png deleted file mode 100644 index 05d77fa7b..000000000 Binary files a/src/assets/img/files/mpeg-64.png and /dev/null differ diff --git a/src/assets/img/files/oth-64.png b/src/assets/img/files/oth-64.png deleted file mode 100644 index 8ffa8b466..000000000 Binary files a/src/assets/img/files/oth-64.png and /dev/null differ diff --git a/src/assets/img/files/pdf-64.png b/src/assets/img/files/pdf-64.png deleted file mode 100644 index b7cdae7e9..000000000 Binary files a/src/assets/img/files/pdf-64.png and /dev/null differ diff --git a/src/assets/img/files/png-64.png b/src/assets/img/files/png-64.png deleted file mode 100644 index 3ecf3e5df..000000000 Binary files a/src/assets/img/files/png-64.png and /dev/null differ diff --git a/src/assets/img/files/powerpoint-64.png b/src/assets/img/files/powerpoint-64.png deleted file mode 100644 index 4d44c7d2b..000000000 Binary files a/src/assets/img/files/powerpoint-64.png and /dev/null differ diff --git a/src/assets/img/files/psd-64.png b/src/assets/img/files/psd-64.png deleted file mode 100644 index 0b800dffa..000000000 Binary files a/src/assets/img/files/psd-64.png and /dev/null differ diff --git a/src/assets/img/files/publisher-64.png b/src/assets/img/files/publisher-64.png deleted file mode 100644 index 9633ef3dd..000000000 Binary files a/src/assets/img/files/publisher-64.png and /dev/null differ diff --git a/src/assets/img/files/quicktime-64.png b/src/assets/img/files/quicktime-64.png deleted file mode 100644 index 90f2fbc0d..000000000 Binary files a/src/assets/img/files/quicktime-64.png and /dev/null differ diff --git a/src/assets/img/files/sourcecode-64.png b/src/assets/img/files/sourcecode-64.png deleted file mode 100644 index ad25537e4..000000000 Binary files a/src/assets/img/files/sourcecode-64.png and /dev/null differ diff --git a/src/assets/img/files/spreadsheet-64.png b/src/assets/img/files/spreadsheet-64.png deleted file mode 100644 index 00427c0e7..000000000 Binary files a/src/assets/img/files/spreadsheet-64.png and /dev/null differ diff --git a/src/assets/img/files/text-64.png b/src/assets/img/files/text-64.png deleted file mode 100644 index 7b397cea5..000000000 Binary files a/src/assets/img/files/text-64.png and /dev/null differ diff --git a/src/assets/img/files/tiff-64.png b/src/assets/img/files/tiff-64.png deleted file mode 100644 index c11a85e28..000000000 Binary files a/src/assets/img/files/tiff-64.png and /dev/null differ diff --git a/src/assets/img/files/unknown-64.png b/src/assets/img/files/unknown-64.png deleted file mode 100644 index 7f703bb83..000000000 Binary files a/src/assets/img/files/unknown-64.png and /dev/null differ diff --git a/src/assets/img/files/video-64.png b/src/assets/img/files/video-64.png deleted file mode 100644 index 570c4b2b3..000000000 Binary files a/src/assets/img/files/video-64.png and /dev/null differ diff --git a/src/assets/img/files/wav-64.png b/src/assets/img/files/wav-64.png deleted file mode 100644 index 819781a9b..000000000 Binary files a/src/assets/img/files/wav-64.png and /dev/null differ diff --git a/src/assets/img/files/wmv-64.png b/src/assets/img/files/wmv-64.png deleted file mode 100644 index 570c4b2b3..000000000 Binary files a/src/assets/img/files/wmv-64.png and /dev/null differ diff --git a/src/assets/img/files/writer-64.png b/src/assets/img/files/writer-64.png deleted file mode 100644 index 6285b6ffa..000000000 Binary files a/src/assets/img/files/writer-64.png and /dev/null differ diff --git a/src/assets/img/grades/agg_mean.png b/src/assets/img/grades/agg_mean.png deleted file mode 100644 index 78c8046df..000000000 Binary files a/src/assets/img/grades/agg_mean.png and /dev/null differ diff --git a/src/assets/img/grades/agg_sum.png b/src/assets/img/grades/agg_sum.png deleted file mode 100644 index 7bcc0e43a..000000000 Binary files a/src/assets/img/grades/agg_sum.png and /dev/null differ diff --git a/src/assets/img/grades/outcomes.png b/src/assets/img/grades/outcomes.png deleted file mode 100644 index f52d9b682..000000000 Binary files a/src/assets/img/grades/outcomes.png and /dev/null differ diff --git a/src/assets/img/group-avatar.png b/src/assets/img/group-avatar.png deleted file mode 100644 index 2e336c95c..000000000 Binary files a/src/assets/img/group-avatar.png and /dev/null differ diff --git a/src/assets/img/icons/activities.svg b/src/assets/img/icons/activities.svg deleted file mode 100644 index 56243a53c..000000000 --- a/src/assets/img/icons/activities.svg +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/icons/calendar.png b/src/assets/img/icons/calendar.png deleted file mode 100644 index e825eeee4..000000000 Binary files a/src/assets/img/icons/calendar.png and /dev/null differ diff --git a/src/assets/img/icons/courses.svg b/src/assets/img/icons/courses.svg deleted file mode 100644 index 7bd9cb672..000000000 --- a/src/assets/img/icons/courses.svg +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/icons/empty.svg b/src/assets/img/icons/empty.svg deleted file mode 100644 index a16fb7460..000000000 --- a/src/assets/img/icons/empty.svg +++ /dev/null @@ -1,3 +0,0 @@ - -]> \ No newline at end of file diff --git a/src/assets/img/icons/h5p.svg b/src/assets/img/icons/h5p.svg deleted file mode 100644 index 7856f9efb..000000000 --- a/src/assets/img/icons/h5p.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/src/assets/img/icons/paypal.png b/src/assets/img/icons/paypal.png deleted file mode 100644 index 058f6e4cb..000000000 Binary files a/src/assets/img/icons/paypal.png and /dev/null differ diff --git a/src/assets/img/login/faq_qrcode.png b/src/assets/img/login/faq_qrcode.png deleted file mode 100644 index cc936b168..000000000 Binary files a/src/assets/img/login/faq_qrcode.png and /dev/null differ diff --git a/src/assets/img/login/faq_url.png b/src/assets/img/login/faq_url.png deleted file mode 100644 index 13d92cd50..000000000 Binary files a/src/assets/img/login/faq_url.png and /dev/null differ diff --git a/src/assets/img/login_logo.png b/src/assets/img/login_logo.png deleted file mode 100644 index 0cbb69d0e..000000000 Binary files a/src/assets/img/login_logo.png and /dev/null differ diff --git a/src/assets/img/mod/assign.svg b/src/assets/img/mod/assign.svg deleted file mode 100644 index 41a788985..000000000 --- a/src/assets/img/mod/assign.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/assignment.svg b/src/assets/img/mod/assignment.svg deleted file mode 100644 index 41a788985..000000000 --- a/src/assets/img/mod/assignment.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/book.svg b/src/assets/img/mod/book.svg deleted file mode 100644 index 740a35160..000000000 --- a/src/assets/img/mod/book.svg +++ /dev/null @@ -1,80 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/chat.svg b/src/assets/img/mod/chat.svg deleted file mode 100644 index 9dd304b78..000000000 --- a/src/assets/img/mod/chat.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/choice.svg b/src/assets/img/mod/choice.svg deleted file mode 100644 index 4d455910c..000000000 --- a/src/assets/img/mod/choice.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/data.svg b/src/assets/img/mod/data.svg deleted file mode 100644 index 954777f09..000000000 --- a/src/assets/img/mod/data.svg +++ /dev/null @@ -1,87 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/database.svg b/src/assets/img/mod/database.svg deleted file mode 100644 index 954777f09..000000000 --- a/src/assets/img/mod/database.svg +++ /dev/null @@ -1,87 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/external-tool.svg b/src/assets/img/mod/external-tool.svg deleted file mode 100644 index ebbbe3084..000000000 --- a/src/assets/img/mod/external-tool.svg +++ /dev/null @@ -1,55 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/feedback.svg b/src/assets/img/mod/feedback.svg deleted file mode 100644 index 58d0f080b..000000000 --- a/src/assets/img/mod/feedback.svg +++ /dev/null @@ -1,133 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/file.svg b/src/assets/img/mod/file.svg deleted file mode 100644 index 2039a2ea2..000000000 --- a/src/assets/img/mod/file.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/folder.svg b/src/assets/img/mod/folder.svg deleted file mode 100644 index 6c2a9fe19..000000000 --- a/src/assets/img/mod/folder.svg +++ /dev/null @@ -1,65 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/forum.svg b/src/assets/img/mod/forum.svg deleted file mode 100644 index aab9a8f44..000000000 --- a/src/assets/img/mod/forum.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/glossary.svg b/src/assets/img/mod/glossary.svg deleted file mode 100644 index f330727e3..000000000 --- a/src/assets/img/mod/glossary.svg +++ /dev/null @@ -1,146 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/h5pactivity.svg b/src/assets/img/mod/h5pactivity.svg deleted file mode 100644 index 97fef5728..000000000 --- a/src/assets/img/mod/h5pactivity.svg +++ /dev/null @@ -1 +0,0 @@ -h5p finalArtboard 1 \ No newline at end of file diff --git a/src/assets/img/mod/ims.svg b/src/assets/img/mod/ims.svg deleted file mode 100644 index 5589cd0c5..000000000 --- a/src/assets/img/mod/ims.svg +++ /dev/null @@ -1,156 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/imscp.svg b/src/assets/img/mod/imscp.svg deleted file mode 100644 index 5589cd0c5..000000000 --- a/src/assets/img/mod/imscp.svg +++ /dev/null @@ -1,156 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/label.svg b/src/assets/img/mod/label.svg deleted file mode 100644 index ac232fc58..000000000 --- a/src/assets/img/mod/label.svg +++ /dev/null @@ -1,94 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/lesson.svg b/src/assets/img/mod/lesson.svg deleted file mode 100644 index 0a0e5dfd5..000000000 --- a/src/assets/img/mod/lesson.svg +++ /dev/null @@ -1,126 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/lti.svg b/src/assets/img/mod/lti.svg deleted file mode 100644 index ebbbe3084..000000000 --- a/src/assets/img/mod/lti.svg +++ /dev/null @@ -1,55 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/page.svg b/src/assets/img/mod/page.svg deleted file mode 100644 index eb7cae6c8..000000000 --- a/src/assets/img/mod/page.svg +++ /dev/null @@ -1,112 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/quiz.svg b/src/assets/img/mod/quiz.svg deleted file mode 100644 index 90473416f..000000000 --- a/src/assets/img/mod/quiz.svg +++ /dev/null @@ -1,90 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/resource.svg b/src/assets/img/mod/resource.svg deleted file mode 100644 index 2039a2ea2..000000000 --- a/src/assets/img/mod/resource.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/scorm.svg b/src/assets/img/mod/scorm.svg deleted file mode 100644 index 77891eca4..000000000 --- a/src/assets/img/mod/scorm.svg +++ /dev/null @@ -1,84 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/survey.svg b/src/assets/img/mod/survey.svg deleted file mode 100644 index a97fe77ef..000000000 --- a/src/assets/img/mod/survey.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/url.svg b/src/assets/img/mod/url.svg deleted file mode 100644 index 56bdb5541..000000000 --- a/src/assets/img/mod/url.svg +++ /dev/null @@ -1,485 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/wiki.svg b/src/assets/img/mod/wiki.svg deleted file mode 100644 index f3101ce19..000000000 --- a/src/assets/img/mod/wiki.svg +++ /dev/null @@ -1,228 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/mod/workshop.svg b/src/assets/img/mod/workshop.svg deleted file mode 100644 index f466455a6..000000000 --- a/src/assets/img/mod/workshop.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/splash.png b/src/assets/img/splash.png deleted file mode 100644 index e7889ccf9..000000000 Binary files a/src/assets/img/splash.png and /dev/null differ diff --git a/src/assets/img/user-avatar.png b/src/assets/img/user-avatar.png deleted file mode 100644 index 0345e26c8..000000000 Binary files a/src/assets/img/user-avatar.png and /dev/null differ diff --git a/src/assets/js/iframe-recaptcha.js b/src/assets/js/iframe-recaptcha.js deleted file mode 100644 index f1ff5e843..000000000 --- a/src/assets/js/iframe-recaptcha.js +++ /dev/null @@ -1,42 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -(function () { - var url = location.href; - - if (!url.match(/^https?:\/\//i) || !url.match(/\/webservice\/recaptcha\.php/i)) { - // Not the recaptcha script, stop. - return; - } - - // Define recaptcha callbacks. - window.recaptchacallback = function(value) { - window.parent.postMessage({ - environment: 'moodleapp', - context: 'recaptcha', - action: 'callback', - frameUrl: location.href, - value: value, - }, '*'); - }; - - window.recaptchaexpiredcallback = function() { - window.parent.postMessage({ - environment: 'moodleapp', - context: 'recaptcha', - action: 'expired', - frameUrl: location.href, - }, '*'); - }; -})(); \ No newline at end of file diff --git a/src/assets/js/iframe-treat-links.js b/src/assets/js/iframe-treat-links.js deleted file mode 100644 index 68f83571b..000000000 --- a/src/assets/js/iframe-treat-links.js +++ /dev/null @@ -1,210 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -(function () { - var url = location.href; - - if (url.match(/^moodleappfs:\/\/localhost/i) || !url.match(/^[a-z0-9]+:\/\//i)) { - // Same domain as the app, stop. - return; - } - - // Redefine window.open. - window.open = function(url, name, specs) { - if (name == '_self') { - // Link should be loaded in the same frame. - location.href = toAbsolute(url); - - return; - } - - getRootWindow(window).postMessage({ - environment: 'moodleapp', - context: 'iframe', - action: 'window_open', - frameUrl: location.href, - url: url, - name: name, - specs: specs, - }, '*'); - }; - - // Handle link clicks. - document.addEventListener('click', (event) => { - if (event.defaultPrevented) { - // Event already prevented by some other code. - return; - } - - // Find the link being clicked. - var el = event.target; - while (el && el.tagName !== 'A') { - el = el.parentElement; - } - - if (!el || el.treated) { - return; - } - - // Add click listener to the link, this way if the iframe has added a listener to the link it will be executed first. - el.treated = true; - el.addEventListener('click', function(event) { - linkClicked(el, event); - }); - }, { - capture: true // Use capture to fix this listener not called if the element clicked is too deep in the DOM. - }); - - - - /** - * Concatenate two paths, adding a slash between them if needed. - * - * @param leftPath Left path. - * @param rightPath Right path. - * @return Concatenated path. - */ - function concatenatePaths(leftPath, rightPath) { - if (!leftPath) { - return rightPath; - } else if (!rightPath) { - return leftPath; - } - - var lastCharLeft = leftPath.slice(-1); - var firstCharRight = rightPath.charAt(0); - - if (lastCharLeft === '/' && firstCharRight === '/') { - return leftPath + rightPath.substr(1); - } else if (lastCharLeft !== '/' && firstCharRight !== '/') { - return leftPath + '/' + rightPath; - } else { - return leftPath + rightPath; - } - } - - /** - * Get the root window. - * - * @param win Current window to check. - * @return Root window. - */ - function getRootWindow(win) { - if (win.parent === win) { - return win; - } - - return getRootWindow(win.parent); - } - - /** - * Get the scheme from a URL. - * - * @param url URL to treat. - * @return Scheme, undefined if no scheme found. - */ - function getUrlScheme(url) { - if (!url) { - return; - } - - var matches = url.match(/^([a-z][a-z0-9+\-.]*):/); - if (matches && matches[1]) { - return matches[1]; - } - } - - /** - * Check if a URL is absolute. - * - * @param url URL to treat. - * @return Whether it's absolute. - */ - function isAbsoluteUrl(url) { - return /^[^:]{2,}:\/\//i.test(url); - } - - /** - * Check whether a URL scheme belongs to a local file. - * - * @param scheme Scheme to check. - * @return Whether the scheme belongs to a local file. - */ - function isLocalFileUrlScheme(scheme) { - if (scheme) { - scheme = scheme.toLowerCase(); - } - - return scheme == 'cdvfile' || - scheme == 'file' || - scheme == 'filesystem' || - scheme == 'moodleappfs'; - } - - /** - * Handle a click on an anchor element. - * - * @param link Anchor element clicked. - * @param event Click event. - */ - function linkClicked(link, event) { - if (event.defaultPrevented) { - // Event already prevented by some other code. - return; - } - - var linkScheme = getUrlScheme(link.href); - var pageScheme = getUrlScheme(location.href); - var isTargetSelf = !link.target || link.target == '_self'; - - if (!link.href || linkScheme == 'javascript') { - // Links with no URL and Javascript links are ignored. - return; - } - - event.preventDefault(); - - if (isTargetSelf && (isLocalFileUrlScheme(linkScheme) || !isLocalFileUrlScheme(pageScheme))) { - // Link should be loaded in the same frame. Don't do it if link is online and frame is local. - location.href = toAbsolute(link.href); - - return; - } - - getRootWindow(window).postMessage({ - environment: 'moodleapp', - context: 'iframe', - action: 'link_clicked', - frameUrl: location.href, - link: {href: link.href, target: link.target}, - }, '*'); - } - - /** - * Convert a URL to an absolute URL if needed using the frame src. - * - * @param url URL to convert. - * @return Absolute URL. - */ - function toAbsolute(url) { - if (isAbsoluteUrl(url)) { - return url; - } - - // It's a relative URL, use the frame src to create the full URL. - var pathToDir = location.href.substring(0, location.href.lastIndexOf('/')); - - return concatenatePaths(pathToDir, url); - } -})(); \ No newline at end of file diff --git a/src/assets/lang/ar.json b/src/assets/lang/ar.json deleted file mode 100644 index d2759ef2f..000000000 --- a/src/assets/lang/ar.json +++ /dev/null @@ -1,1263 +0,0 @@ -{ - "addon.badges.alignment": "انحياز", - "addon.badges.badgedetails": "تفاصيل الشارة", - "addon.badges.badges": "شارات", - "addon.badges.bendorsement": "المصادقة", - "addon.badges.claimcomment": "تأييد التعليق", - "addon.badges.claimid": "رابط المطالبة", - "addon.badges.contact": "تواصل", - "addon.badges.dateawarded": "تاريخ الإصدار", - "addon.badges.expired": "منتهية الصلاحية", - "addon.badges.expirydate": "تاريخ الانتهاء", - "addon.badges.imageauthoremail": "البريد الإلكتروني لمؤلف الصورة", - "addon.badges.imageauthorname": "اسم مؤلف الصورة", - "addon.badges.imageauthorurl": "رابط عنوان URL مؤلف الصورة", - "addon.badges.imagecaption": "تعليق على الصورة", - "addon.badges.issuancedetails": "انتهاء الصلاحية الشارة", - "addon.badges.issuerdetails": "تفاصيل المصدر", - "addon.badges.issueremail": "البريد الإلكتروني", - "addon.badges.issuername": "اسم المصدر", - "addon.badges.issuerurl": "عنوان URL الخاص بالمصدر", - "addon.badges.language": "اللغة", - "addon.badges.noalignment": "لا تحتوي هذه الشارة على أي مهارات أو معايير خارجية محددة.", - "addon.badges.nobadges": "لا توجد شارات المتاحة.", - "addon.badges.norelated": "لا تحتوي هذه الشارة على أي شارات ذات صلة.", - "addon.badges.recipientdetails": "تفاصيل المستلم", - "addon.badges.relatedbages": "شارات ذات صلة", - "addon.badges.version": "الإصدار", - "addon.badges.warnexpired": "(انتهت هذه الشارة!)", - "addon.block_activitymodules.pluginname": "أنشطة", - "addon.block_activityresults.pluginname": "نتائج النشاط", - "addon.block_badges.pluginname": "أحدث الشارات", - "addon.block_blogmenu.pluginname": "قائمة المدونة", - "addon.block_blogrecent.pluginname": "مدخلات المدونة الحديثة", - "addon.block_blogtags.pluginname": "علامات المدونة", - "addon.block_calendarmonth.pluginname": "تقويم", - "addon.block_calendarupcoming.pluginname": "الأحداث القادمة", - "addon.block_comments.pluginname": "تعليقات", - "addon.block_completionstatus.pluginname": "وضع إتمام المقرر الدراسي", - "addon.block_glossaryrandom.pluginname": "مدخل مسردي عشوائي", - "addon.block_myoverview.future": "المستقبل", - "addon.block_myoverview.inprogress": "قيد التنفيذ", - "addon.block_myoverview.past": "الماضي", - "addon.block_myoverview.pluginname": "معاينة مقرر دراسي", - "addon.block_newsitems.pluginname": "آخر الأخبار", - "addon.block_onlineusers.pluginname": "المستخدمين الموجدين على الموقع الآن", - "addon.block_privatefiles.pluginname": "ملفاتي الخاصة", - "addon.block_recentactivity.pluginname": "الأنشطة الحديثة", - "addon.block_rssclient.pluginname": "مستحدم خدمة تغذية الاخبار", - "addon.block_selfcompletion.pluginname": "الإتمام الذاتي", - "addon.block_sitemainmenu.pluginname": "القائمة الرئيسية", - "addon.block_tags.pluginname": "وسوم", - "addon.blog.blog": "مدونة", - "addon.blog.blogentries": "مشاركات المدونة", - "addon.blog.linktooriginalentry": "رابط الدخول إلى المدونة الأصلية", - "addon.blog.noentriesyet": "لا توجد مشاركات ظاهرة هنا", - "addon.blog.publishtonoone": "أنت (مسودة)", - "addon.blog.publishtosite": "أي شخص على هذا الموقع", - "addon.blog.publishtoworld": "أي شخص بالعالم", - "addon.blog.siteblogheading": "مدونة الموقع", - "addon.calendar.allday": "كل اليوم", - "addon.calendar.calendar": "تقويم", - "addon.calendar.calendarevents": "أحداث التقويم", - "addon.calendar.categoryevents": "أحداث الفئة", - "addon.calendar.confirmeventdelete": "هل أنت متأكد للقيام بحذف هذا الحدث", - "addon.calendar.confirmeventseriesdelete": "يعتبر الحدث \"{{$a.name}}\" جزءًا من سلسلة. هل تريد حذف هذا الحدث فقط ، أو كل الأحداث {{$a.count}} في السلسلة؟", - "addon.calendar.courseevents": "أحداث المنهج الدراسي", - "addon.calendar.daynext": "اليوم التالي", - "addon.calendar.dayprev": "اليوم السابق", - "addon.calendar.deleteallevents": "حذف كل الأحداث", - "addon.calendar.deleteevent": "احذف الحدث", - "addon.calendar.deleteoneevent": "حذف هذا الحدث", - "addon.calendar.durationminutes": "المدة بالدقائق", - "addon.calendar.durationnone": "بدون مدة زمنية", - "addon.calendar.durationuntil": "حتى", - "addon.calendar.editevent": "تحرير حدث", - "addon.calendar.errorloadevent": "خطأ في تحميل الحدث", - "addon.calendar.errorloadevents": "خطأ في تحميل الأحداث", - "addon.calendar.eventcalendareventdeleted": "تم حذف حدث التقويم", - "addon.calendar.eventduration": "مدة زمنية", - "addon.calendar.eventendtime": "وقت الانتهاء", - "addon.calendar.eventkind": "نوع الحدث", - "addon.calendar.eventname": "اسم", - "addon.calendar.eventstarttime": "وقت البدء", - "addon.calendar.eventtype": "نوع الحدث", - "addon.calendar.fri": "الجمعة", - "addon.calendar.friday": "يوم الجمعة", - "addon.calendar.gotoactivity": "إذهب إلى النشاط", - "addon.calendar.groupevents": "أحداث مجموعة", - "addon.calendar.invalidtimedurationminutes": "المدة بالدقائق التي أدخلتها غير صالحة الرجاء أدخل المدة بالدقائق أكبر من الصفر أو اختر لا مدة", - "addon.calendar.invalidtimedurationuntil": "الوقت والتاريخ اللذان اخترتهما للمدة \"حتى\" هي قبل وقت بدء الحدث. رجاءً صحح هذا قبل الإكمال.", - "addon.calendar.mon": "الاثنين", - "addon.calendar.monday": "يوم الاثنين", - "addon.calendar.monthlyview": "معاينة شهرية", - "addon.calendar.newevent": "حدث جديد", - "addon.calendar.noevents": "لا يوجد أي أحداث", - "addon.calendar.nopermissiontoupdatecalendar": "عذراً ولكنك لا تملك حالياً الصلاحيات لتحرير حدث الرزنامة", - "addon.calendar.repeatedevents": "أحداث متكررة", - "addon.calendar.repeateditall": "نفذ التغيرات على كل {{$a}} الأحداث في هذه السلسة المتكررة.", - "addon.calendar.repeateditthis": "طبق هذه التغيرات على هذا الحدث فقط", - "addon.calendar.repeatevent": "تكرار هذا الحدث", - "addon.calendar.repeatweeksl": "كرر اسبوعياً، أنشئ الجميع", - "addon.calendar.sat": "السبت", - "addon.calendar.saturday": "يوم السبت", - "addon.calendar.siteevents": "أحداث الموقع", - "addon.calendar.sun": "الاحد", - "addon.calendar.sunday": "يوم الاحد", - "addon.calendar.thu": "الخميس", - "addon.calendar.thursday": "يوم الخميس", - "addon.calendar.today": "اليوم", - "addon.calendar.tomorrow": "غداً", - "addon.calendar.tue": "الثلاثاء", - "addon.calendar.tuesday": "يوم الثلاثاء", - "addon.calendar.typecategory": "حدث الفئة", - "addon.calendar.typeclose": "اغلاق حدث", - "addon.calendar.typecourse": "حدث منهج دراسي", - "addon.calendar.typedue": "الحدث حتى", - "addon.calendar.typegradingdue": "تقييم الحدث حتى", - "addon.calendar.typegroup": "حدث مجموعة", - "addon.calendar.typeopen": "فتح حدث", - "addon.calendar.typesite": "حدث الموقع", - "addon.calendar.typeuser": "حدث المستخدم", - "addon.calendar.upcomingevents": "الأحداث القادمة", - "addon.calendar.userevents": "أحداث المستخدم", - "addon.calendar.wed": "الاربعاء", - "addon.calendar.wednesday": "يوم الاربعاء", - "addon.calendar.when": "عند", - "addon.calendar.yesterday": "امس", - "addon.competency.activities": "أنشطة", - "addon.competency.competenciesmostoftennotproficientincourse": "في معظم الأحيان لا يتقن الكفاءات في هذه المادة", - "addon.competency.coursecompetencies": "الكفاءات المقرر", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "لا تؤثر تقييمات الكفاءة في هذه المادة على خطط التعلم.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "يتم تحديث تقييمات الكفاءة في هذه المادة على الفور في خطط التعلم.", - "addon.competency.crossreferencedcompetencies": "الكفاءات المرجعية", - "addon.competency.duedate": "تاريخ الاستحقاق", - "addon.competency.errornocompetenciesfound": "لا يوجد أي قدرات موجودة", - "addon.competency.evidence": "شاهد", - "addon.competency.learningplancompetencies": "خطة التعلم الكفاءات", - "addon.competency.learningplans": "خطط التعلم", - "addon.competency.myplans": "خطط التعلم الخاصة بي", - "addon.competency.noactivities": "لا توجد أنشطة", - "addon.competency.nocompetencies": "لا يوجد أي قدرات", - "addon.competency.nocompetenciesincourse": "لا توجد كفاءات مرتبطة بهذه المادة.", - "addon.competency.nocrossreferencedcompetencies": "لم تتم الإشارة إلى كفاءات أخرى بهذه الكفاءة.", - "addon.competency.noevidence": "لا شواهد", - "addon.competency.noplanswerecreated": "لم يتم إنشاء خطط تعليمية.", - "addon.competency.path": "المسار:", - "addon.competency.proficient": "بارع", - "addon.competency.progress": "التقدم", - "addon.competency.rating": "التقييم", - "addon.competency.reviewstatus": "مراجعة الحالة", - "addon.competency.status": "الحالة", - "addon.competency.template": "قالب خطة التعلّم", - "addon.competency.uponcoursecompletion": "عند الانتهاء من المادة:", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} من {{$a.y}} الكفاءات للإجادة", - "addon.competency.xcompetenciesproficientoutofyincourse": "أنت ماهر في {{$a.x}} من {{$a.y}} الكفاءات في هذه الدورة.", - "addon.coursecompletion.complete": "مكتمل", - "addon.coursecompletion.completecourse": "قم بإتمام المقرر الدراسي", - "addon.coursecompletion.completed": "منتهٍ", - "addon.coursecompletion.completiondate": "تاريخ الإتمام", - "addon.coursecompletion.completionmenuitem": "الإكمال", - "addon.coursecompletion.couldnotloadreport": "لا يمكن تحميل تقرير إكمال المقرر، الرجاء المحاولة في وقت آخر", - "addon.coursecompletion.coursecompletion": "إتمام مقرر دراسي", - "addon.coursecompletion.criteria": "معايير", - "addon.coursecompletion.criteriagroup": "مجموعة المعايير", - "addon.coursecompletion.criteriarequiredall": "كل المعايير في الأسفل مطلوبة", - "addon.coursecompletion.criteriarequiredany": "أي معيار في الأسفل مطلوب", - "addon.coursecompletion.inprogress": "قيد التنفيذ", - "addon.coursecompletion.manualselfcompletion": "إكمال يدوي ذاتي", - "addon.coursecompletion.nottracked": "لا يتم متابعتك حاليًا من خلال إكمال هذه المادة", - "addon.coursecompletion.notyetstarted": "لم يبدأ بعد", - "addon.coursecompletion.pending": "معلق", - "addon.coursecompletion.required": "مفروض", - "addon.coursecompletion.requiredcriteria": "المعايير المطلوبة", - "addon.coursecompletion.requirement": "متطلبات", - "addon.coursecompletion.status": "الحالة", - "addon.coursecompletion.viewcoursereport": "عرض تقرير المقرر", - "addon.files.couldnotloadfiles": "لا يمكن تحميل قائمة الملفات", - "addon.files.emptyfilelist": "لا يوجد ملفات للعرض", - "addon.files.files": "ملفات", - "addon.files.privatefiles": "ملفات خاصة", - "addon.files.sitefiles": "ملفات الموقع", - "addon.messages.addcontact": "أضف جهة أتصال", - "addon.messages.addtoyourcontacts": "أضف جهات اتصاللك", - "addon.messages.blocknoncontacts": "امنع المستخدمين الذين ليسوا في قائمة اتصالاتي من مراسلتي", - "addon.messages.contactblocked": "تم حظر جهة الاتصال", - "addon.messages.contactlistempty": "قائمة الاتصال فارغة", - "addon.messages.contacts": "جهات اتصال", - "addon.messages.deleteallconfirm": "هل أنت متأكد من رغبتك بحذف كامل الحوار؟", - "addon.messages.errordeletemessage": "خطأ عند حذف الرسالة", - "addon.messages.message": "رسالة", - "addon.messages.messagenotsent": "لم يتم إرسال الرسالة، يرجي المحاولة لاحقا", - "addon.messages.messagepreferences": "مراجع الرسالة", - "addon.messages.messages": "رسائل", - "addon.messages.newmessage": "رسالة جديدة", - "addon.messages.nomessagesfound": "لم يتم العثور على إي رسالة", - "addon.messages.nousersfound": "لا يوجد مستخدمين", - "addon.messages.removecontact": "ازل جهة الاتصال", - "addon.messages.removefromyourcontacts": "حذف من قائمة جهات اتصالك", - "addon.messages.searchcombined": "البحث عن الأشخاص والرسائل", - "addon.messages.type_offline": "غير متصل", - "addon.messages.type_online": "متصل", - "addon.messages.type_search": "نتائج البحث", - "addon.messages.type_strangers": "رسائل أخرى", - "addon.mod_assign.addattempt": "السماح بمحاولة أخرى", - "addon.mod_assign.addnewattempt": "إضافة محاولة جديدة", - "addon.mod_assign.addnewattemptfromprevious": "إضافة محاولة جديدة بناء على التسليم السابق", - "addon.mod_assign.addsubmission": "أضف تسليم", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "ستكون تفاصيل التكليف (المهمة) ونموذج التسليم مُتاح من\n {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "اسمح بالتسليم ابتداءّ من", - "addon.mod_assign.allowsubmissionsfromdatesummary": "سيبدأ قبول التسليمات لهذا التكليف (المهمة) ابتداءً من\n {{$a}}", - "addon.mod_assign.applytoteam": "تطبيق الدرجات والتغذية الراجعة (الملاحظات) على المجموعة بأكملها", - "addon.mod_assign.assignmentisdue": "فات موعد التسليم", - "addon.mod_assign.attemptnumber": "رقم المحاولة", - "addon.mod_assign.attemptreopenmethod": "تم إعادة فتح المحاولة", - "addon.mod_assign.attemptreopenmethod_manual": "يدوياً", - "addon.mod_assign.attemptreopenmethod_untilpass": "تلقائياً حتى النجاح", - "addon.mod_assign.attemptsettings": "إعدادات المحاولة", - "addon.mod_assign.confirmsubmission": "هل أنت متأكد أنك تريد أن تُسلِّم عملك للتصحيح؟ لن تكون قادراً على إجراء أية تغييرات أخرى.", - "addon.mod_assign.currentattempt": "هذه المحاولة {{$a}}.", - "addon.mod_assign.currentattemptof": "هذه المحاولة رقم{{$a.attemptnumber}} ( {{$a.maxattempts}} محاولات مسموحة ).", - "addon.mod_assign.currentgrade": "الدرجة الحالية في", - "addon.mod_assign.cutoffdate": "موعد التسليم النهائي", - "addon.mod_assign.defaultteam": "المجموعة الأساسية", - "addon.mod_assign.duedate": "تاريخ تقديم مهمة", - "addon.mod_assign.duedateno": "لا يوجد موعد لتقديم المهمة", - "addon.mod_assign.duedatereached": "لقد انتهى موعد تسليم الواجب", - "addon.mod_assign.editingstatus": "تعديل الحالة", - "addon.mod_assign.editsubmission": "حرر تسليمي", - "addon.mod_assign.extensionduedate": "تاريخ انتهاء الإضافة", - "addon.mod_assign.grade": "درجة", - "addon.mod_assign.graded": "تم رصد درجة", - "addon.mod_assign.gradedby": "تم التصحيح من قبل", - "addon.mod_assign.gradedfollowupsubmit": "تم التصحيح - يوجد ملاحظات ما بعد التسليم", - "addon.mod_assign.gradedon": "صححت على", - "addon.mod_assign.gradelocked": "هذه العلامة مغلقة أو تم الكتابة فوقها في تقرير العلامات", - "addon.mod_assign.gradeoutof": "الدرجة من {{$a}}", - "addon.mod_assign.gradingstatus": "حالة التقييم", - "addon.mod_assign.groupsubmissionsettings": "إعدادات تسليم المجموعات", - "addon.mod_assign.hiddenuser": "المشاركين", - "addon.mod_assign.latesubmissions": "التقديمات المتأخرة", - "addon.mod_assign.latesubmissionsaccepted": "السماح حتى {{$a}}", - "addon.mod_assign.markingworkflowstate": "تقييم حالة سير العمل", - "addon.mod_assign.markingworkflowstateinmarking": "في التصحيح", - "addon.mod_assign.markingworkflowstateinreview": "في المراجعة", - "addon.mod_assign.markingworkflowstatenotmarked": "لم تصحح", - "addon.mod_assign.markingworkflowstatereadyforrelease": "جاهز للنشر", - "addon.mod_assign.markingworkflowstatereadyforreview": "تم الانتهاء من التصحيح", - "addon.mod_assign.markingworkflowstatereleased": "تم النشر", - "addon.mod_assign.modulenameplural": "مهام", - "addon.mod_assign.multipleteams": "عضو لأكثر من مجموعة", - "addon.mod_assign.noattempt": "لا توجد محاولات", - "addon.mod_assign.notgraded": "لم يتم التقييم", - "addon.mod_assign.numberofdraftsubmissions": "مسودات", - "addon.mod_assign.numberofparticipants": "المشاركين", - "addon.mod_assign.numberofsubmissionsneedgrading": "بحاجة لتقييم", - "addon.mod_assign.numberofsubmittedassignments": "مسلمة", - "addon.mod_assign.numwords": "{{$a}} كلمات", - "addon.mod_assign.overdue": "فات موعد تسليم الوظيفة بـ: {{$a}}", - "addon.mod_assign.submission": "تسليم", - "addon.mod_assign.submissionslocked": "هذه الوظيفة لا تقبل التسليم الآن", - "addon.mod_assign.submissionstatus": "حالة التسليم", - "addon.mod_assign.submissionstatus_draft": "مسودة (غير مسلمة)", - "addon.mod_assign.submissionstatus_marked": "تم رصد درجة", - "addon.mod_assign.submissionstatus_submitted": "مسلمة للتقييم", - "addon.mod_assign.submissionstatusheading": "حالة التسليم", - "addon.mod_assign.submitassignment": "تقديم مهمة", - "addon.mod_assign.submittedearly": "تم تسليم الوظيفة مبكرة بـ{{$a}}", - "addon.mod_assign.submittedlate": "تم تسليم الوظيفة متأخرة بـ{{$a}}", - "addon.mod_assign.timeremaining": "الزمن المتبقي", - "addon.mod_assign.wordlimit": "كلمة الحد", - "addon.mod_assign_feedback_comments.pluginname": "تعليقات الإفادة", - "addon.mod_assign_feedback_editpdf.pluginname": "التعليق على PDF", - "addon.mod_assign_feedback_file.pluginname": "ملف التغذية الراجعة (الملاحظات)", - "addon.mod_assign_submission_comments.pluginname": "تعليقات على التقديم", - "addon.mod_assign_submission_file.pluginname": "الملف المسلم", - "addon.mod_assign_submission_onlinetext.pluginname": "تقديم نصي عبر الإنترنت ..", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "حد الكلمات لهذا الواجب هو {{$a.limit}} الكلمات وتحاول إرسال الكلمات {{$a.count}}. يرجى مراجعة التقديم و المحاولة مرة أخرى.", - "addon.mod_book.errorchapter": "خطأ في قراءة فصل من فصول الكتاب.", - "addon.mod_book.modulenameplural": "كتب", - "addon.mod_book.navnexttitle": "التالي: {{$a}}", - "addon.mod_book.navprevtitle": "السابق: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "فصول الكتاب", - "addon.mod_book.toc": "جدول المحتويات", - "addon.mod_chat.beep": "نغمة", - "addon.mod_chat.chatreport": "جلسة المحادثة", - "addon.mod_chat.currentusers": "المستخدمين الحاليين", - "addon.mod_chat.enterchat": "أضغط هناء للدخول إلى المحادثة", - "addon.mod_chat.entermessage": "أدخل رسالتك", - "addon.mod_chat.errorwhileconnecting": "خطأ عند الاتصال بالدردشة", - "addon.mod_chat.errorwhilegettingchatdata": "خطأ عند الحصول على معلومات الدردشة", - "addon.mod_chat.errorwhilegettingchatusers": "خطأ عند الحصول على مستخدمي الدردشة", - "addon.mod_chat.errorwhileretrievingmessages": "خطأ عند استرجاع الرسائل من الخادم", - "addon.mod_chat.errorwhilesendingmessage": "خطأ عند إرسال الرسالة", - "addon.mod_chat.messagebeepseveryone": "{{$a}} إرسال نغمة للجميع!", - "addon.mod_chat.messagebeepsyou": "{{$a}} أرسل نغمة لك!", - "addon.mod_chat.messageenter": "{{$a}} دخل غرفة محادثة", - "addon.mod_chat.messageexit": "{{$a}} خرج من غرفة محادثة", - "addon.mod_chat.messages": "رسائل", - "addon.mod_chat.messageyoubeep": "أرسلت نغمة صفير لـ{{$a}}", - "addon.mod_chat.modulenameplural": "محادثات", - "addon.mod_chat.nomessages": "لا توجد رسائل بعد", - "addon.mod_chat.saidto": "قال لـ", - "addon.mod_chat.send": "إرسل", - "addon.mod_chat.sessionstart": "ي {{$a.date}}, ({{$a.fromnow}} وسوف تبدأ جلسة الدردشة القادمة في من الآن", - "addon.mod_chat.talk": "حديث", - "addon.mod_chat.viewreport": "معاينة جلسات المحادثة السابقة", - "addon.mod_choice.choiceoptions": "خيارات الاختيار", - "addon.mod_choice.expired": "عذراً، تم إغلاق هذا النشاط في {{$a}} وهو غير متوفر الآن.", - "addon.mod_choice.full": "(كامل)", - "addon.mod_choice.modulenameplural": "الاختيارات", - "addon.mod_choice.noresultsviewable": "حالياً لا يمكن معاينة النتائج", - "addon.mod_choice.notopenyet": "عذرا، هذا النشاط سيكون متوفر في {{$a}}", - "addon.mod_choice.numberofuser": "عدد المستخدمين", - "addon.mod_choice.numberofuserinpercentage": "عدد المستخدمين كنسبة", - "addon.mod_choice.removemychoice": "استبعد خياري", - "addon.mod_choice.responses": "إجابات", - "addon.mod_choice.responsesresultgraphheader": "عرض كمنحني", - "addon.mod_choice.savemychoice": "حفظ الاختيار", - "addon.mod_choice.userchoosethisoption": "المستخدمين الذين اختاروا هذا الخيار", - "addon.mod_choice.yourselection": "أختيارك", - "addon.mod_data.addentries": "أضف مدخلات", - "addon.mod_data.advancedsearch": "بحث متقدم", - "addon.mod_data.alttext": "النص البديل", - "addon.mod_data.approve": "اسمح/وافق", - "addon.mod_data.approved": "تم الموافقة", - "addon.mod_data.ascending": "تصاعدي", - "addon.mod_data.authorfirstname": "الاسم الأول للكاتب", - "addon.mod_data.authorlastname": "الاسم الأخير للكاتب", - "addon.mod_data.confirmdeleterecord": "هل فعلا ترغب في حذف هذا السجل؟", - "addon.mod_data.descending": "تنازليا", - "addon.mod_data.emptyaddform": "لم تقم بتعبئة الحقول!", - "addon.mod_data.expired": "عذراً، تم إغلاق هذا النشاط في {{$a}} وهو غير متوفر الآن.", - "addon.mod_data.fields": "حقول", - "addon.mod_data.menuchoose": "اختار", - "addon.mod_data.modulenameplural": "قواعد بيانات", - "addon.mod_data.more": "المزيد", - "addon.mod_data.nomatch": "لم يتم العثور على مدخلات مطابقة!", - "addon.mod_data.norecords": "لا يوجد مدخلات في قاعدة البيانات", - "addon.mod_data.notapproved": "لم يتم الموافقة على المدخل بعد", - "addon.mod_data.notopenyet": "عذراً، هذا النشاط لن يتاح حتى {{$a}}", - "addon.mod_data.numrecords": "{{$a}} سجلات", - "addon.mod_data.other": "اخر", - "addon.mod_data.recordapproved": "تم الموافقه على السجل", - "addon.mod_data.recorddeleted": "تم حذف السجل", - "addon.mod_data.resetsettings": "إعادة تعين الحقول", - "addon.mod_data.search": "بحث", - "addon.mod_data.selectedrequired": "كل المختار مطلوب", - "addon.mod_data.single": "معاينة فردية", - "addon.mod_data.timeadded": "وقت الإضافة", - "addon.mod_data.timemodified": "وقت التعديل", - "addon.mod_data.usedate": "تضمين في البحث", - "addon.mod_feedback.analysis": "تحليل", - "addon.mod_feedback.anonymous": "غير مشخصن", - "addon.mod_feedback.anonymous_entries": "مدخلات غير مشخصنة", - "addon.mod_feedback.average": "متوسط", - "addon.mod_feedback.complete_the_form": "اجب على الأسئلة.....", - "addon.mod_feedback.completed_feedbacks": "الاجابات المسلمة", - "addon.mod_feedback.continue_the_form": "استمر من", - "addon.mod_feedback.feedbackclose": "إغلاق الأفادة", - "addon.mod_feedback.feedbackopen": "فتح الإفادة", - "addon.mod_feedback.mode": "نمط", - "addon.mod_feedback.modulenameplural": "إفادة", - "addon.mod_feedback.next_page": "الصفحة التالية", - "addon.mod_feedback.non_anonymous": "سيتم تسجيل اسم المستخدم وعرضه مع الإجابات", - "addon.mod_feedback.non_respondents_students": "غير مستجيبين", - "addon.mod_feedback.not_selected": "لم يتم الاختيار", - "addon.mod_feedback.not_started": "لم يتم البدء", - "addon.mod_feedback.overview": "نظرة عامة", - "addon.mod_feedback.page_after_submit": "الصفحة بعد الإرسال", - "addon.mod_feedback.preview": "معاينة", - "addon.mod_feedback.previous_page": "صفحة سابقة", - "addon.mod_feedback.questions": "أسئلة", - "addon.mod_feedback.responses": "إجابات", - "addon.mod_feedback.save_entries": "سلم إجاباتك", - "addon.mod_feedback.show_entries": "عرض الردود", - "addon.mod_feedback.show_nonrespondents": "معاينة غير المستجيبين", - "addon.mod_feedback.started": "بداء", - "addon.mod_feedback.this_feedback_is_already_submitted": "لقد قمت مسبقاً بإكمال هذا النشاط.", - "addon.mod_folder.emptyfilelist": "لا يوجد أي ملفات ليتم إظهارها", - "addon.mod_folder.modulenameplural": "مجلدات", - "addon.mod_forum.addanewdiscussion": "أضف موضوعا جديدا للنقاش", - "addon.mod_forum.addanewquestion": "أضف سؤال جديد", - "addon.mod_forum.addanewtopic": "أضف موضوع جديد", - "addon.mod_forum.advanced": "متقدم", - "addon.mod_forum.cannotadddiscussion": "إضافة نقشات لهذا المنتدى يتطلب عضوية مجموعات", - "addon.mod_forum.cannotadddiscussionall": "ليس لديك الصلاحيات لإضافة نقاش لكل المشتركين.", - "addon.mod_forum.couldnotadd": "تعذر إرسال مقالة نتيجة خطأ غير معروف", - "addon.mod_forum.couldnotupdate": "تعذر تعديل مقالك نتيجة خطأ غير معروف", - "addon.mod_forum.delete": "حذف", - "addon.mod_forum.deletedpost": "تم حذف هذه المقالة", - "addon.mod_forum.deletesure": "هل أنت متأكد أنك تريد إلغاء حذف هذه المشاركة؟", - "addon.mod_forum.discussion": "نقاش", - "addon.mod_forum.edit": "حرر", - "addon.mod_forum.group": "مجموعة", - "addon.mod_forum.lastpost": "اخر مشاركة", - "addon.mod_forum.message": "رسالة", - "addon.mod_forum.modeflatnewestfirst": "عرض الردود حسب الأحدث", - "addon.mod_forum.modeflatoldestfirst": "عرض الردود حسب الأقدم", - "addon.mod_forum.modenested": "عرض الردود حسب المداخلات", - "addon.mod_forum.modulenameplural": "المنتديات", - "addon.mod_forum.posttoforum": "أضف المشاركة للمنتدى", - "addon.mod_forum.re": "إعادة:", - "addon.mod_forum.reply": "رد", - "addon.mod_forum.subject": "الموضوع", - "addon.mod_forum.unread": "لم يتم قرائتها", - "addon.mod_forum.unreadpostsnumber": "{{$a}} مشاركات التي لم تقراء", - "addon.mod_forum.yourreply": "مشاركتك", - "addon.mod_glossary.addentry": "أضف مصطلح", - "addon.mod_glossary.aliases": "الكلمة الدّليليّة", - "addon.mod_glossary.attachment": "ملحق", - "addon.mod_glossary.byauthor": "التجميع طبقا للمؤلف", - "addon.mod_glossary.bynewestfirst": "الأحدث أولا", - "addon.mod_glossary.byrecentlyupdated": "تم تحديثه مؤخرا", - "addon.mod_glossary.bysearch": "بحث", - "addon.mod_glossary.casesensitive": "هذه المصطلح يتطلب حالة احرف خاصة", - "addon.mod_glossary.categories": "تصنيفات", - "addon.mod_glossary.concept": "مفهوم", - "addon.mod_glossary.definition": "تعريف", - "addon.mod_glossary.entryusedynalink": "يجب ربط هذا المصطلح آليا", - "addon.mod_glossary.errconceptalreadyexists": "هذا المصطلح موجود مسبقاً. لا يمكن التكرار في هذا المسرد", - "addon.mod_glossary.fillfields": "حقول المفهوم والتعريف اجبارية", - "addon.mod_glossary.fullmatch": "قارن كل الكلمات فقط", - "addon.mod_glossary.linking": "ربط آلي", - "addon.mod_glossary.modulenameplural": "مسردات", - "addon.mod_imscp.deploymenterror": "خطاء في محتوى الحزمة", - "addon.mod_imscp.showmoduledescription": "اظهر الوصف", - "addon.mod_imscp.toc": "قائمة المحتويات", - "addon.mod_lesson.answer": "أجب", - "addon.mod_lesson.attempt": "محاولة: {{$a}}", - "addon.mod_lesson.averagescore": "متوسط الدرجة", - "addon.mod_lesson.averagetime": "متوسط الوقت", - "addon.mod_lesson.branchtable": "محتوى", - "addon.mod_lesson.completed": "تم", - "addon.mod_lesson.congratulations": "مبروك - لقد وصلت إلى نهاية الدرس", - "addon.mod_lesson.continue": "استمر", - "addon.mod_lesson.detailedstats": "إحصائيات تفصيلية", - "addon.mod_lesson.didnotanswerquestion": "لم تتم أجابة هذا السؤال", - "addon.mod_lesson.displayofgrade": "أعرض الدرجة (للطلاب فقط)", - "addon.mod_lesson.enterpassword": "الرجاء إدخال كلمة المرور", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "لم تجبن على أي أسئلة. لقد تلقيت 0 لهذا الدرس.", - "addon.mod_lesson.finish": "نهاية", - "addon.mod_lesson.grade": "درجة", - "addon.mod_lesson.highscore": "الدرجة القصوى", - "addon.mod_lesson.hightime": "الوقت الاقصى", - "addon.mod_lesson.lessonmenu": "قائمة الدرس", - "addon.mod_lesson.lessonstats": "أحصائيات درس", - "addon.mod_lesson.loginfail": "فشلت عملية الدخول، اعد المحاولة", - "addon.mod_lesson.lowscore": "أقل درجة", - "addon.mod_lesson.lowtime": "أقل وقت", - "addon.mod_lesson.modattemptsnoteacher": "معاينة الطالب تعمل فقط للطلاب", - "addon.mod_lesson.modulenameplural": "دروس", - "addon.mod_lesson.noanswer": "لم تعطى إجابة", - "addon.mod_lesson.nolessonattempts": "لم يتم إجراء محاولات مسبقة في هذا الدرس", - "addon.mod_lesson.notcompleted": "لم يتم انهائه", - "addon.mod_lesson.numberofcorrectanswers": "عدد الاجابات الصحيحة: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "عدد الصفحات التي تم مشاهدتها: {{$a}}", - "addon.mod_lesson.or": "أو", - "addon.mod_lesson.overview": "عرض عام", - "addon.mod_lesson.preview": "معاينه", - "addon.mod_lesson.question": "سؤال", - "addon.mod_lesson.reports": "تقارير", - "addon.mod_lesson.response": "إجابة", - "addon.mod_lesson.review": "مراجعة", - "addon.mod_lesson.reviewlesson": "مراجعة الدرس", - "addon.mod_lesson.reviewquestionback": "نعم، أرغب في المحاولة ثانياً", - "addon.mod_lesson.thatsthecorrectanswer": "هذه إجابة صحيحة", - "addon.mod_lesson.thatsthewronganswer": "هذه إجابة خاطئة", - "addon.mod_lesson.timeremaining": "الزمن المتبقى", - "addon.mod_lesson.timetaken": "الزمن المستنفذ", - "addon.mod_lesson.welldone": "أحسنت!", - "addon.mod_lesson.youranswer": "إجابتك", - "addon.mod_lesson.youshouldview": "يجب أن تجب على الأقل: {{$a}}", - "addon.mod_page.modulenameplural": "صفحات", - "addon.mod_quiz.attemptfirst": "المحاولة الأولى", - "addon.mod_quiz.attemptlast": "المحاولة الأخيرة", - "addon.mod_quiz.attemptnumber": "محاولة", - "addon.mod_quiz.attemptquiznow": "قم بمحاولة أداء الاختبار الآن", - "addon.mod_quiz.attemptstate": "الحالة", - "addon.mod_quiz.cannotsubmitquizdueto": "محاولة هذا الاختبار لا يمكن إرسالها للأسباب التالية:", - "addon.mod_quiz.comment": "تعليق", - "addon.mod_quiz.completedon": "اكتمل في", - "addon.mod_quiz.confirmclose": "أنت غ=على وشك إغلاق هذه المحاولة. بمجرد إغلاقك لهذه المحاول لن تستطيع تعديل إجاباتك.", - "addon.mod_quiz.confirmstart": "هذا الاختبار محدد بوقت {{$a}}. سوف يبدأ العد التنازلي للوقت من لحظة البدأ في المحاولة والتي يجب أن ترسل قبل انتهاء الوقت المحدد لها. هل أنت متأكد أنك تريد بدأ الاختبارالآن؟", - "addon.mod_quiz.confirmstartheader": "اختبار مؤقت", - "addon.mod_quiz.continueattemptquiz": "إستمر في آخر محاولة", - "addon.mod_quiz.continuepreview": "استمر اخر معاينة", - "addon.mod_quiz.errordownloading": "خطأ عن تنزيل البيانات المطلوبة", - "addon.mod_quiz.errorgetquiz": "خطأ عند الحصول على بيانات الاختبار", - "addon.mod_quiz.feedback": "تعليق", - "addon.mod_quiz.grade": "درجة", - "addon.mod_quiz.gradeaverage": "متوسط الدرجة", - "addon.mod_quiz.gradehighest": "أعلى درجة", - "addon.mod_quiz.grademethod": "أسلوب التقييم", - "addon.mod_quiz.marks": "الدرجات", - "addon.mod_quiz.modulenameplural": "اختبارات", - "addon.mod_quiz.noquestions": "لم يتم إضافة أية أسئلة بعد", - "addon.mod_quiz.overdue": "متأخر جدا", - "addon.mod_quiz.preview": "معاينة", - "addon.mod_quiz.previewquiznow": "معاينة الاستمارة الآن", - "addon.mod_quiz.question": "سؤال", - "addon.mod_quiz.quizpassword": "كلمة المرور للاختبار", - "addon.mod_quiz.reattemptquiz": "إعادة الاختبار", - "addon.mod_quiz.requirepasswordmessage": "لأخذ هذا الاختباريجب عليك معرفة كلمة المرور", - "addon.mod_quiz.review": "مراجعة", - "addon.mod_quiz.reviewofattempt": "مراجعة المحاولة {{$a}}", - "addon.mod_quiz.showall": "أعرض جميع الأسئلة في صفحة واحدة", - "addon.mod_quiz.startattempt": "إبدأ المحاولة", - "addon.mod_quiz.startedon": "بداء في", - "addon.mod_quiz.stateinprogress": "قيد التنفيذ", - "addon.mod_quiz.stateoverdue": "مُتأخر", - "addon.mod_quiz.status": "الحالة", - "addon.mod_quiz.submitallandfinish": "سلم الجميع وانهي", - "addon.mod_quiz.summaryofattempt": "ملخص المحاولة", - "addon.mod_quiz.summaryofattempts": "ملخص محاولاتك السابقة", - "addon.mod_quiz.timeleft": "الوقت المتبقي", - "addon.mod_quiz.timetaken": "الوقت المستغرق", - "addon.mod_quiz.yourfinalgradeis": "درجتك النهائية عن هذا الاختبار هي {{$a}}", - "addon.mod_resource.modifieddate": "المُحدَّثة {{$a}}", - "addon.mod_resource.modulenameplural": "ملفات", - "addon.mod_resource.openthefile": "افتح الملف", - "addon.mod_resource.uploadeddate": "تم تحميلها {{$a}}", - "addon.mod_scorm.asset": "قييم", - "addon.mod_scorm.assetlaunched": "قوم - اعرض", - "addon.mod_scorm.attempts": "محاولات", - "addon.mod_scorm.averageattempt": "متوسط المحاولات", - "addon.mod_scorm.browse": "معاينة", - "addon.mod_scorm.browsed": "أُستعرض", - "addon.mod_scorm.browsemode": "النمط العرضي", - "addon.mod_scorm.completed": "تم", - "addon.mod_scorm.contents": "محتويات", - "addon.mod_scorm.enter": "ادخل", - "addon.mod_scorm.errordownloadscorm": "خطأ عن تنزيل الحزمة التعليمية: \"{{name}}\"", - "addon.mod_scorm.exceededmaxattempts": "لقد وصلت إلى الحد الأقصى لعدد المحاولات.", - "addon.mod_scorm.failed": "فشل", - "addon.mod_scorm.firstattempt": "المحاولة الأولى", - "addon.mod_scorm.gradeaverage": "المتوسط", - "addon.mod_scorm.gradeforattempt": "درجة للمحاولة", - "addon.mod_scorm.gradehighest": "الدرجة العليا", - "addon.mod_scorm.grademethod": "طريقة رصد الدرجات", - "addon.mod_scorm.gradereported": "الدرجة المعلنة", - "addon.mod_scorm.gradescoes": "كائنات تعلّم", - "addon.mod_scorm.gradesum": "الدرجة الاجمالية", - "addon.mod_scorm.highestattempt": "أعلى محاولة", - "addon.mod_scorm.incomplete": "غير مكتمل", - "addon.mod_scorm.lastattempt": "المحاولاة الأخيرة", - "addon.mod_scorm.modulenameplural": "حزم إسكورم", - "addon.mod_scorm.newattempt": "ابداء محاولة جديدة", - "addon.mod_scorm.noattemptsallowed": "عدد المحاولات المسموح بها", - "addon.mod_scorm.noattemptsmade": "عدد المحاولات التي قمت بها", - "addon.mod_scorm.notattempted": "لم تتم محاولته", - "addon.mod_scorm.organizations": "تنظيمات", - "addon.mod_scorm.passed": "نجح", - "addon.mod_scorm.reviewmode": "وضع المراجعة", - "addon.mod_scorm.score": "درجة", - "addon.mod_scorm.suspended": "معلّق", - "addon.mod_scorm.toc": "قائمة المحتويات", - "addon.mod_survey.ifoundthat": "وجدت أن", - "addon.mod_survey.ipreferthat": "أفضل أن", - "addon.mod_survey.modulenameplural": "استبيانات", - "addon.mod_survey.responses": "إجابات", - "addon.mod_survey.results": "النتائج", - "addon.mod_url.modulenameplural": "الروابط الإلكترونية", - "addon.mod_wiki.cannoteditpage": "لا يمكنك تحرير هذه الصفحة.", - "addon.mod_wiki.createpage": "إنشاء صفحة", - "addon.mod_wiki.editingpage": "تحرير الصفحخة: {{$a}}", - "addon.mod_wiki.map": "خريطة", - "addon.mod_wiki.modulenameplural": "ويكيات", - "addon.mod_wiki.newpagetitle": "عنواو صفحة جديد", - "addon.mod_wiki.nocontent": "لا يوجد محتوى في هذه الصفحة", - "addon.mod_wiki.notingroup": "لا ينتمي إلى مجموعة", - "addon.mod_wiki.pagename": "اسم الصفحة", - "addon.mod_wiki.wrongversionlock": "قام مستخدم آخر بتحديث هذه الصفحة بينما كنت أنت تحررها، أصبحت تعديلاتك قديمة.", - "addon.mod_workshop.assess": "قييم", - "addon.mod_workshop.editsubmission": "حرر التسليم", - "addon.mod_workshop.gradinggrade": "تقدير الدرجة", - "addon.mod_workshop.modulenameplural": "ورش عمل", - "addon.mod_workshop.reassess": "اعد التقييم", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "تعليق لـ {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "التقدير لـ {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "اوجه الدولار {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "يجب انتقاء درجة لهذا الوجه", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "تعليق لـ {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "{{$a}} اوجه الدولار", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "تعليق لـ {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "التقدير لـ {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "التأكيد {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "القياسي {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "لقد قمت باختيار هذه البنود", - "addon.notes.addnewnote": "إضافة ملاحظة جديدة", - "addon.notes.coursenotes": "ملاحظات المقرر الدراسي", - "addon.notes.deleteconfirm": "حذف هذه الملاحظة؟", - "addon.notes.nonotes": "لا يوجد إلى الآن أي ملاحظات من هذا النوع", - "addon.notes.note": "ملاحظة", - "addon.notes.notes": "ملاحظات", - "addon.notes.personalnotes": "ملاحظات شخصية", - "addon.notes.publishstate": "السياق", - "addon.notes.sitenotes": "ملاحظات الموقع", - "addon.notifications.errorgetnotifications": "خطأ في الحصول على الإشعارات", - "addon.notifications.markallread": "أشر على الكل بمقروء", - "addon.notifications.notificationpreferences": "تفضيلات الاشعار", - "addon.notifications.notifications": "الإشعارات", - "addon.notifications.therearentnotificationsyet": "لا توجد إشعارات", - "assets.countries.AD": "أندورا", - "assets.countries.AE": "الإمارات العربية المتحدة", - "assets.countries.AF": "أفغانستان", - "assets.countries.AG": "أنتيغوا وبربودا", - "assets.countries.AI": "أنغيلا", - "assets.countries.AL": "ألبانيا", - "assets.countries.AM": "أرمينيا", - "assets.countries.AO": "أنغولا", - "assets.countries.AQ": "القارة القطبية الجنوبية", - "assets.countries.AR": "الأرجنتين", - "assets.countries.AS": "ساموا الأمريكية", - "assets.countries.AT": "النمسا", - "assets.countries.AU": "أستراليا", - "assets.countries.AW": "أروبا", - "assets.countries.AX": "جزر آلاند", - "assets.countries.AZ": "أذربيجان", - "assets.countries.BA": "البوسنة والهرسك", - "assets.countries.BB": "بربادوس", - "assets.countries.BD": "بنغلاديش", - "assets.countries.BE": "بلجيكا", - "assets.countries.BF": "بوركينا فاسو", - "assets.countries.BG": "بلغاريا", - "assets.countries.BH": "البحرين", - "assets.countries.BI": "بوروندي", - "assets.countries.BJ": "بنين", - "assets.countries.BL": "سانت بارتيليمي", - "assets.countries.BM": "برمودا", - "assets.countries.BN": "بروناي دار السلام", - "assets.countries.BO": "بوليفيا", - "assets.countries.BQ": "الجزر الكاريبية الهولندية: بونير، سينت أوستاتيوس وسابا", - "assets.countries.BR": "البرازيل", - "assets.countries.BS": "جزر البهاما", - "assets.countries.BT": "بوتان", - "assets.countries.BV": "جزيرة بوفيت", - "assets.countries.BW": "بوتسوانا", - "assets.countries.BY": "روسيا البيضاء", - "assets.countries.BZ": "بليز", - "assets.countries.CA": "كندا", - "assets.countries.CC": "كوكوس (كيلينغ) ، جزر", - "assets.countries.CD": "جمهورية الكونغو الديمقراطية", - "assets.countries.CF": "جمهورية أفريقيا الوسطى", - "assets.countries.CG": "الكونغو", - "assets.countries.CH": "سويسرا", - "assets.countries.CI": "كوت ديفوار", - "assets.countries.CK": "جزر كوك", - "assets.countries.CL": "شيلي", - "assets.countries.CM": "الكاميرون", - "assets.countries.CN": "الصين", - "assets.countries.CO": "كولومبيا", - "assets.countries.CR": "كوستاريكا", - "assets.countries.CU": "كوبا", - "assets.countries.CV": "الرأس الأخضر", - "assets.countries.CW": "كوراساو", - "assets.countries.CX": "جزيرة كريسماس", - "assets.countries.CY": "قبرص", - "assets.countries.CZ": "الجمهورية التشيكية", - "assets.countries.DE": "ألمانيا", - "assets.countries.DJ": "جيبوتي", - "assets.countries.DK": "الدنمارك", - "assets.countries.DM": "دومينيكا", - "assets.countries.DO": "جمهورية الدومينيكان", - "assets.countries.DZ": "الجزائر", - "assets.countries.EC": "الاكوادور", - "assets.countries.EE": "استونيا", - "assets.countries.EG": "مصر", - "assets.countries.EH": "الصحراء الغربية", - "assets.countries.ER": "اريتريا", - "assets.countries.ES": "إسبانيا", - "assets.countries.ET": "أثيوبيا", - "assets.countries.FI": "فنلندا", - "assets.countries.FJ": "فيجي", - "assets.countries.FK": "جزر فوكلاند (مالفيناس)", - "assets.countries.FM": "ميكرونيزيا من", - "assets.countries.FO": "جزر فارو", - "assets.countries.FR": "فرنسا", - "assets.countries.GA": "الغابون", - "assets.countries.GB": "المملكة المتحدة", - "assets.countries.GD": "غرينادا", - "assets.countries.GE": "جورجيا", - "assets.countries.GF": "غيانا الفرنسية", - "assets.countries.GG": "غيرنسي", - "assets.countries.GH": "غانا", - "assets.countries.GI": "جبل طارق", - "assets.countries.GL": "جرينلاند", - "assets.countries.GM": "غامبيا", - "assets.countries.GN": "غينيا", - "assets.countries.GP": "غواديلوب", - "assets.countries.GQ": "غينيا الاستوائية", - "assets.countries.GR": "اليونان", - "assets.countries.GS": "جورجيا الجنوبية وجزر ساندويتش الجنوبية", - "assets.countries.GT": "غواتيمالا", - "assets.countries.GU": "غوام", - "assets.countries.GW": "غينيا بيساو", - "assets.countries.GY": "غيانا", - "assets.countries.HK": "هونج كونج", - "assets.countries.HM": "جزيرة هيرد وجزر ماكدونالد", - "assets.countries.HN": "هندوراس", - "assets.countries.HR": "كرواتيا", - "assets.countries.HT": "هايتي", - "assets.countries.HU": "هنغاريا", - "assets.countries.ID": "أندونيسيا", - "assets.countries.IE": "أيرلندا", - "assets.countries.IL": "إسرائيل", - "assets.countries.IM": "جزيرة مان", - "assets.countries.IN": "الهند", - "assets.countries.IO": "إقليم المحيط الهندي البريطاني", - "assets.countries.IQ": "العراق", - "assets.countries.IR": "جمهورية إيران الإسلامية", - "assets.countries.IS": "أيسلندا", - "assets.countries.IT": "إيطاليا", - "assets.countries.JE": "جيرسي", - "assets.countries.JM": "جامايكا", - "assets.countries.JO": "الأردن", - "assets.countries.JP": "اليابان", - "assets.countries.KE": "كينيا", - "assets.countries.KG": "قيرغيزستان", - "assets.countries.KH": "كمبوديا", - "assets.countries.KI": "كيريباتي", - "assets.countries.KM": "جزر القمر", - "assets.countries.KN": "سانت كيتس ونيفيس", - "assets.countries.KP": "جمهورية كوريا الديمقراطية الشعبية", - "assets.countries.KR": "جمهورية كوريا", - "assets.countries.KW": "الكويت", - "assets.countries.KY": "جزر كايمان", - "assets.countries.KZ": "كازاخستان", - "assets.countries.LA": "جمهورية لاوس الديمقراطية الشعبية", - "assets.countries.LB": "لبنان", - "assets.countries.LC": "سانت لوسيا", - "assets.countries.LI": "ليختنشتاين", - "assets.countries.LK": "سري لانكا", - "assets.countries.LR": "ليبيريا", - "assets.countries.LS": "ليسوتو", - "assets.countries.LT": "ليتوانيا", - "assets.countries.LU": "لوكسمبورغ", - "assets.countries.LV": "لاتفيا", - "assets.countries.LY": "الجماهيرية العربية الليبية", - "assets.countries.MA": "المغرب", - "assets.countries.MC": "موناكو", - "assets.countries.MD": "جمهورية مولدوفا", - "assets.countries.ME": "الجبل الأسود", - "assets.countries.MF": "سانت مارتن", - "assets.countries.MG": "مدغشقر", - "assets.countries.MH": "جزر مارشال", - "assets.countries.MK": "مقدونيا ، الجمهورية اليوغوسلافية السابقة", - "assets.countries.ML": "مالي", - "assets.countries.MM": "ميانمار", - "assets.countries.MN": "منغوليا", - "assets.countries.MO": "ماكاو", - "assets.countries.MP": "جزر ماريانا الشمالية", - "assets.countries.MQ": "مارتينيك", - "assets.countries.MR": "موريتانيا", - "assets.countries.MS": "مونتسيرات", - "assets.countries.MT": "مالطا", - "assets.countries.MU": "موريشيوس", - "assets.countries.MV": "جزر المالديف", - "assets.countries.MW": "ملاوي", - "assets.countries.MX": "المكسيك", - "assets.countries.MY": "ماليزيا", - "assets.countries.MZ": "موزامبيق", - "assets.countries.NA": "ناميبيا", - "assets.countries.NC": "كاليدونيا الجديدة", - "assets.countries.NE": "النيجر", - "assets.countries.NF": "جزيرة نورفولك", - "assets.countries.NG": "نيجيريا", - "assets.countries.NI": "نيكاراغوا", - "assets.countries.NL": "هولندا", - "assets.countries.NO": "النرويج", - "assets.countries.NP": "نيبال", - "assets.countries.NR": "ناورو", - "assets.countries.NU": "نيوي", - "assets.countries.NZ": "نيوزيلندا", - "assets.countries.OM": "عمان", - "assets.countries.PA": "بنما", - "assets.countries.PE": "بيرو", - "assets.countries.PF": "بولينيزيا الفرنسية", - "assets.countries.PG": "بابوا غينيا الجديدة", - "assets.countries.PH": "الفلبين", - "assets.countries.PK": "باكستان", - "assets.countries.PL": "بولندا", - "assets.countries.PM": "سان بيار وميكلون", - "assets.countries.PN": "بيتكيرن", - "assets.countries.PR": "بورتوريكو", - "assets.countries.PS": "الأراضي الفلسطينية والأراضي المحتلة", - "assets.countries.PT": "البرتغال", - "assets.countries.PW": "بالاو", - "assets.countries.PY": "باراغواي", - "assets.countries.QA": "قطر", - "assets.countries.RE": "réunion", - "assets.countries.RO": "رومانيا", - "assets.countries.RS": "صربيا", - "assets.countries.RU": "روسيا الاتحادية", - "assets.countries.RW": "رواندا", - "assets.countries.SA": "السعودية", - "assets.countries.SB": "جزر سليمان", - "assets.countries.SC": "سيشيل", - "assets.countries.SD": "سودان", - "assets.countries.SE": "السويد", - "assets.countries.SG": "سنغافورة", - "assets.countries.SH": "سانت هيلانة", - "assets.countries.SI": "سلوفينيا", - "assets.countries.SJ": "سفالبارد وجان مايان", - "assets.countries.SK": "سلوفاكيا", - "assets.countries.SL": "سيراليون", - "assets.countries.SM": "سان مارينو", - "assets.countries.SN": "السنغال", - "assets.countries.SO": "الصومال", - "assets.countries.SR": "سورينام", - "assets.countries.SS": "جنوب السودان", - "assets.countries.ST": "سان تومي وبرينسيبي", - "assets.countries.SV": "السلفادور", - "assets.countries.SX": "سانت مارتن (الجزء الهولندي)", - "assets.countries.SY": "الجمهورية العربية السورية", - "assets.countries.SZ": "سوازيلاند", - "assets.countries.TC": "جزر تركس وكايكوس", - "assets.countries.TD": "تشاد", - "assets.countries.TF": "الأقاليم الجنوبية الفرنسية", - "assets.countries.TG": "توغو", - "assets.countries.TH": "تايلاند", - "assets.countries.TJ": "طاجيكستان", - "assets.countries.TK": "توكيلاو", - "assets.countries.TL": "تيمور الشرقية", - "assets.countries.TM": "تركمانستان", - "assets.countries.TN": "تونس", - "assets.countries.TO": "تونغا", - "assets.countries.TR": "تركيا", - "assets.countries.TT": "ترينيداد وتوباغو", - "assets.countries.TV": "توفالو", - "assets.countries.TW": "تايوان", - "assets.countries.TZ": "جمهورية تنزانيا المتحدة", - "assets.countries.UA": "أوكرانيا", - "assets.countries.UG": "أوغندا", - "assets.countries.UM": "جزر الولايات المتحدة البعيدة الصغيرة", - "assets.countries.US": "الولايات المتحدة", - "assets.countries.UY": "أوروغواي", - "assets.countries.UZ": "أوزبكستان", - "assets.countries.VA": "دولة الفاتيكان", - "assets.countries.VC": "سانت فنسنت وغرينادين", - "assets.countries.VE": "فنزويلا", - "assets.countries.VG": "الجزر العذراء البريطانية", - "assets.countries.VI": "الجزر العذراء ، الولايات المتحدة", - "assets.countries.VN": "فيتنام", - "assets.countries.VU": "فانواتو", - "assets.countries.WF": "واليس وفوتونا", - "assets.countries.WS": "ساموا", - "assets.countries.YE": "يمني", - "assets.countries.YT": "مايوت", - "assets.countries.ZA": "جنوب أفريقيا", - "assets.countries.ZM": "زامبيا", - "assets.countries.ZW": "زيمبابوي", - "assets.mimetypes.application/msword": "وثيقة Word", - "assets.mimetypes.application/pdf": "وثيقة PDF", - "assets.mimetypes.application/vnd.ms-excel": "وثيقة أكسل", - "assets.mimetypes.application/vnd.ms-powerpoint": "عرض بور بوينت", - "assets.mimetypes.document/unknown": "ملف", - "assets.mimetypes.text/plain": "ملف نصي", - "assets.mimetypes.text/rtf": "وثيقة RTF", - "core.accounts": "حسابات", - "core.add": "أضف", - "core.agelocationverification": "التحقق من العمر والمكان", - "core.ago": "{{$a}} مضى", - "core.all": "الكل", - "core.allgroups": "جميع المجموعات", - "core.allparticipants": "كل المشاركين", - "core.answer": "إجابة", - "core.answered": "تم الاجابة", - "core.areyousure": "هل انت متأكد؟", - "core.back": "العودة", - "core.block.blocks": "كتل", - "core.cancel": "إلغاء", - "core.cannotconnect": "لا يمكن الاتصال: تحقق من أنك كتبت عنوان URL بشكل صحيح وأنك تستخدم موقع موودل {{$a}} أو أحدث.", - "core.category": "التصنيف", - "core.choose": "اختر", - "core.choosedots": "اختر...", - "core.clicktohideshow": "انقر للطي أو التوسيع", - "core.close": "أغلق", - "core.comments": "تعليقات", - "core.comments.addcomment": "أضف تعليقاً...", - "core.comments.comments": "تعليقات", - "core.comments.commentscount": "التعليقات ({{$a}})", - "core.comments.deletecommentbyon": "أزل الملاحظة المضافة من قبل {{$a.user}} في {{$a.time}}", - "core.comments.eventcommentcreated": "تم إنشاء التعليق", - "core.comments.eventcommentdeleted": "تم حذف التعليق", - "core.comments.nocomments": "لا يوجد تعليقات", - "core.comments.savecomment": "حفظ التعليق", - "core.commentscount": "التعليقات ({{$a}})", - "core.completion-alt-auto-fail": "مكتمل (لم تحقق درحة النجاح)", - "core.completion-alt-auto-n": "غير مكتمل", - "core.completion-alt-auto-n-override": "لم يكتمل: {{$a.modname}}\n (تم تعيينه بواسطة {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "مكتمل (حققت درجة النجاح)", - "core.completion-alt-auto-y": "مكتمل", - "core.completion-alt-auto-y-override": "اكتمل: {{$a.modname}}\n(تم تعيينه بواسطة {{$a.overrideuser}})", - "core.completion-alt-manual-n": "غير مكتمل؛ حدد لجعل هذا العنصر مكتمل", - "core.completion-alt-manual-n-override": "لم يكتمل: {{$a.modname}}\n(تم تعيينه بواسطة {{$a- overrideuser}}). حدد لوضع علامة كاملة.", - "core.completion-alt-manual-y": "مكتمل؛ حدد لجعل هذا العنصر غير مكتمل", - "core.completion-alt-manual-y-override": "اكتمل: {{$a.modname}}\n (تم تعيينه بواسطة {{$a.overrideuser}}). حدد لوضع علامة على أنها غير كاملة.", - "core.considereddigitalminor": "أنت أصغر من أن تنشئ حسابًا على هذا الموقع.", - "core.content": "المحتوى", - "core.contentlinks.chooseaccount": "أختر الحساب", - "core.continue": "استمر", - "core.course": "مقرر دراسي", - "core.course.allsections": "كل الأقسام", - "core.course.contents": "المحتويات", - "core.course.couldnotloadsections": "لم يتم تحميل كل الأقسام، من فضلك حاول مرة أخرى لاحقاَ", - "core.course.coursesummary": "ملخص المقرر الدراسي", - "core.course.downloadcourse": "تنزيل المقرر", - "core.course.errordownloadingsection": "خطأ عن تنزيل الأقسام", - "core.course.hiddenfromstudents": "مخفي عن الطلاب", - "core.course.hiddenoncoursepage": "متوفر لكن غير مرئي على صفحة الصف", - "core.course.overriddennotice": "تم ضبط درجتك النهائية من هذا النشاط يدويًا.", - "core.course.sections": "مجموعات", - "core.coursedetails": "تفاصيل المقرر الدراسي", - "core.courses.allowguests": "يسمح للمستخدمين الضيوف بالدخول إلى هذا المقرر الدراسي", - "core.courses.availablecourses": "المقررات الدراسية المتاحة", - "core.courses.categories": "تصنيفات المقررات الدراسية", - "core.courses.courses": "تصنيف المقررات الدراسية", - "core.courses.frontpage": "الصفحة الرئيسية", - "core.courses.ignore": "تجاهل", - "core.courses.mycourses": "مقرراتي الدراسية", - "core.courses.mymoodle": "لوحة التحكم", - "core.courses.nocourses": "لا يوجد معلومات لمقرر دراسي ليتم اظهرها", - "core.courses.nocoursesyet": "لا توجد مقررات دراسية لهذه الفئة", - "core.courses.nosearchresults": "لا يوجد نتائج", - "core.courses.notenroled": "أنت لست مسجلاً كطالب في هذا المقرر", - "core.courses.paymentrequired": "هذا المقرر الدراسي غير مجانين لذا يجب دفع القيمة للدخول.", - "core.courses.paypalaccepted": "تم قبول التبرع المدفوع", - "core.courses.reload": "إعادة تحميل", - "core.courses.search": "بحث", - "core.courses.searchcourses": "بحث مقررات دراسية", - "core.courses.sendpaymentbutton": "ارسل القيمة المدفوعة عن طريق التبرع", - "core.date": "تاريخ", - "core.day": "يوم", - "core.days": "أيام", - "core.decsep": ".", - "core.defaultvalue": "الافتراضي ({{$a}})", - "core.delete": "حذف", - "core.description": "الوصف", - "core.digitalminor": "قاصر رقمي", - "core.digitalminor_desc": "الرجاء اطلب من الوالد / ولي الأمر الاتصال بـ :", - "core.done": "تم", - "core.download": "تحميل", - "core.downloading": "يتم التنزيل", - "core.edit": "حرر", - "core.editor.bold": "غامق", - "core.editor.clear": "مسح التنسيق", - "core.editor.italic": "مائل", - "core.editor.orderedlist": "قائمة مرتبة", - "core.editor.strike": "يتوسطه خط", - "core.editor.underline": "تسطير", - "core.editor.unorderedlist": "قائمة غير مرتبة", - "core.error": "خطاء", - "core.errordownloading": "خطأ عن تنزيل الملف", - "core.explanationdigitalminor": "هذه المعلومات مطلوبة لتحديد ما إذا كان عمرك يتجاوز سن الموافقة الرقمي. و هو العمر الذي يمكن فيه للفرد الموافقة على بنود وشروط تخزين و معالجة بياناته قانونياً.", - "core.favourites": "تمييز بنجمة", - "core.filename": "اسم الملف", - "core.filenotfound": "عذراً، لم يتم العثور على الملف", - "core.fileuploader.audio": "صوتي", - "core.fileuploader.camera": "الكاميرا", - "core.fileuploader.errorcapturingaudio": "خطأ في التقاط الصوت", - "core.fileuploader.errorcapturingimage": "خطأ في التقاط الصورة", - "core.fileuploader.errorcapturingvideo": "خطأ في التقاط الفيديو", - "core.fileuploader.errormustbeonlinetoupload": "لابد أن تكون متصل بالأنترنت لكي يتم رفع الملفات", - "core.fileuploader.errorreadingfile": "خطأ في قراءة الملف", - "core.fileuploader.file": "ملف", - "core.fileuploader.filesofthesetypes": "أنواع الملفات المقبولة:", - "core.fileuploader.fileuploaded": "الملف الذي تم رفعه", - "core.fileuploader.more": "المزيد", - "core.fileuploader.readingfile": "يتم قراءة الملف", - "core.fileuploader.uploadafile": "إرفع ملف", - "core.fileuploader.uploading": "يتم الرفع", - "core.fileuploader.video": "فيديو", - "core.filter": "تصفية", - "core.folder": "مجلد", - "core.forcepasswordchangenotice": "يجب عليك تغير كلمة المرور ليتسنى لك الاستمرار", - "core.fulllistofcourses": "كل المقررات الدراسية", - "core.grades.average": "متوسط", - "core.grades.badgrade": "الدرجة المقدمة غير صالحة", - "core.grades.contributiontocoursetotal": "المساهمة في مجموع المقرر الدراسي", - "core.grades.feedback": "إفادة", - "core.grades.grade": "درجة", - "core.grades.gradeitem": "بند الدرجة", - "core.grades.grades": "درجات", - "core.grades.lettergrade": "درجة حرفية", - "core.grades.nogradesreturned": "لا توجد درجات", - "core.grades.nooutcome": "لا يوجد مخرجات تعلم", - "core.grades.percentage": "النسبة", - "core.grades.range": "مجال", - "core.grades.rank": "مرتبة", - "core.grades.weight": "الوزن", - "core.group": "مجموعة", - "core.groupsseparate": "مجموعات منفصلة", - "core.groupsvisible": "مجموعات ظاهرة", - "core.help": "مساعدة", - "core.hide": "إخفاء", - "core.hour": "ساعة", - "core.hours": "ساعات", - "core.info": "معلومات", - "core.invalidformdata": "نموذج البيانات غير صحيح", - "core.labelsep": ":", - "core.lastaccess": "آخر اتصال", - "core.lastmodified": "آخر تعديل", - "core.lastsync": "آخر تزامن", - "core.layoutgrid": "الشبكة", - "core.list": "قائمة", - "core.listsep": "،", - "core.loading": "يتم التحميل", - "core.location": "الموقع", - "core.login.auth_email": "يتم التسجيل الذاتي عن طريق البريد الإلكتروني", - "core.login.authenticating": "مصادقة", - "core.login.cancel": "إلغاء", - "core.login.changepassword": "قم بتغيير كلمة المرور", - "core.login.connect": "دخول", - "core.login.connecttomoodle": "بيانات الدخول", - "core.login.createaccount": "إنشاء حساب مشترك الجديد", - "core.login.createuserandpass": "من فضلك اختر اسم المستخدم وكلمة المرور للدخول بهما فيما بعد", - "core.login.credentialsdescription": "من فضلك قم بإدخال اسم المستخدم وكلمة المرور للدخول إلى", - "core.login.emailconfirmsent": "

تم إرسال بريد إلى {{$a}}

\n

والذي يحتوي على إرشادات سهلة تعينك على إتمام التسجيل

\n

إذا استمرت المشاكل اتصل بمدير هذا الموقع

", - "core.login.emailconfirmsentsuccess": "تم إرسال رسالة التأكيد الإلكترونية بنجاح", - "core.login.firsttime": "هل هذه هي المرة الأولى لك؟ الاشتراك من هنا", - "core.login.forcepasswordchangenotice": "يجب عليك تغير كلمة المرور ليتسنى لك الاستمرار", - "core.login.forgotten": "هل نسيت اسم الدخول أو كلمة المرور؟", - "core.login.help": "مساعدة", - "core.login.helpmelogin": "

من أجل تسجيل الدخول، يرجى التحقق مما يلي:

\n١- إصدار موقع التعلم الإلكتروني مودل هو الإصدار ٢.٤ أو أعلى.
\n٢- قد مكن مسؤول موقع التعلم الإلكتروني مودل الوصول عبر تطبيق مودل.

\n

لو لم تستطع الدخول من خلال موقع التعلم الإلكتروني مودل الخاص بك قم بالاتصال بمدير موقع التعلم الإلكتروني مودل الخاص بك وأطلب منه زيارة موقع وثائق موودل للحصول على معلومات أكثر تفصيلا والمساعدة.

\nلاختبار التطبيق باستخدام الموقع التجريبي لمودل ضع student أو teacher في حقل عنوان الموقع وانقر على زر دخول", - "core.login.instructions": "التعليمات", - "core.login.invalidaccount": "يرجى مراجعة تفاصيل تسجيل الدخول الخاصة بك أو الطلب من مسؤول موقع الويب الخاص بك للتحقق من تكوين موقع.", - "core.login.invaliddate": "تاريخ غير صحيح", - "core.login.invalidemail": "عنوان البريد الإلكتروني غير صحيح", - "core.login.invalidmoodleversion": "نسخة موودل غير صالحة. الحد الأدنى للنسخة المطلوبة هو:", - "core.login.invalidsite": "رابط عنوان الموقع غير صالح.", - "core.login.invalidurl": "رابط محدد غير صالح", - "core.login.login": "دخول", - "core.login.loginbutton": "دخول", - "core.login.loginsteps": "للوصول الكامل لهذا الموقع, عليك أن تنشئ حسابا.", - "core.login.missingemail": "العنوان البريدي لم يتم تحدده", - "core.login.missingfirstname": "الاسم الأول لم يتم تحدده", - "core.login.missinglastname": "الاسم الأخير لم يتم تحدده", - "core.login.mustconfirm": "أنت تحتاج إلى تأكيد الدخول", - "core.login.newaccount": "حساب مشترك جديد", - "core.login.password": "كلمة المرور", - "core.login.passwordforgotten": "كلمة المرور المنسيه", - "core.login.passwordforgotteninstructions2": "لإعادة تعيين كلمة المرور، أدخل اسم المستخدم أو البريد الإلكتروني الخاص بك في الأسفل. سيتم إرسال رسالة لك في حال إيجادك في قاعدة اليبانات، مع تعليمات عن كيفية الدخول مجدداً.", - "core.login.passwordrequired": "كلمة المرور مطلوبة", - "core.login.policyaccept": "لقد فهمت وأوافق", - "core.login.policyagree": "يجب الموافقة على هذه الشروط للإستمرار في إستخدام الموقع. هل أنت موافق؟", - "core.login.policyagreement": "شروط إتفاقية الموقع", - "core.login.policyagreementclick": "رابط لاتفاقية سياسة الموقع", - "core.login.potentialidps": "الدخول باستخدام حسابك في :", - "core.login.profileinvaliddata": "قيمة غير صحيحة", - "core.login.reconnect": "إعادة الدخول", - "core.login.security_question": "سؤال حماية", - "core.login.selectacountry": "اختر دولة", - "core.login.siteaddress": "عنوان الموقع", - "core.login.siteurl": "رابط الموقع", - "core.login.siteurlrequired": "رابط الموقع مطلوب ..مثل  http://www.yourmoodlesite.abc أو https://www.yourmoodlesite.efg", - "core.login.startsignup": "إنشاء اشتراك جديد", - "core.login.supplyinfo": "الرجاء إدخال تفاصيل أكثر", - "core.login.username": "اسم المستخدم", - "core.login.usernameoremail": "أدخل اسم المستخدم كاملاً أو عنوان البريد الإلكتروني", - "core.login.usernamerequired": "اسم المستخدم مطلوب", - "core.login.usernotaddederror": "المستخدم \"{{$a}}\" لم يتم إضافته - خطاء غير معروف", - "core.lostconnection": "فقدنا الاتصال تحتاج إلى إعادة الاتصال. المميز الخاص بك هو الآن غير صالح", - "core.mainmenu.help": "مساعدة", - "core.mainmenu.logout": "خروج", - "core.mainmenu.website": "الموقع", - "core.maxsizeandattachments": "الحد الأقصى لحجم الملف: {{$a.size}} ، الحد الأقصى لعدد الملفات: {{$a.attachments}}", - "core.min": "الحد الأدنى", - "core.mins": "دقائق", - "core.misc": "متنوعات", - "core.mod_assignment": "وظائف (2.2)", - "core.mod_book": "كتاب", - "core.mod_chat": "محادثة", - "core.mod_choice": "الاختيار", - "core.mod_data": "قاعدة بيانات", - "core.mod_database": "قاعدة بيانات", - "core.mod_feedback": "إفادة", - "core.mod_file": "ملف", - "core.mod_folder": "مجلد", - "core.mod_forum": "المنتدى", - "core.mod_glossary": "مسرد", - "core.mod_label": "ملصق", - "core.mod_lesson": "درس", - "core.mod_page": "صفحة", - "core.mod_quiz": "اختبار", - "core.mod_resource": "ملف", - "core.mod_scorm": "حزمة SCORM", - "core.mod_survey": "استبيان", - "core.mod_url": "رابط إلكتروني", - "core.mod_wiki": "ويكي", - "core.mod_workshop": "ورشة عمل", - "core.moduleintro": "وصف", - "core.more": "المزيد", - "core.mygroups": "مجموعاتي", - "core.name": "الاسم", - "core.networkerrormsg": "لم يتم تمكين الشبكة أو أنها لا تعمل.", - "core.never": "مطلقاً", - "core.next": "التالي", - "core.no": "لا", - "core.nocomments": "لا يوجد تعليقات", - "core.nograde": "لا توجد درجة", - "core.none": "لا يوجد", - "core.nopermissions": "عذراً ولكنك لا تملك حالياً الصلاحيات لتقوم بهذا ({{$a}})", - "core.noresults": "لا توجد نتائج", - "core.noselection": "لا اختيار", - "core.notenrolledprofile": "هذه الصفحة الشخصية غير متاحة، ﻷن هذا المستخدم غير مسجل بهذا المقرر", - "core.notice": "إشعار", - "core.notingroup": "عذراً، يجب عليك أن تكون عضواً في مجموعة لتتمكن من معاينة هذا النشاط", - "core.now": "الآن", - "core.numwords": "{{$a}} كلمات", - "core.offline": "غير متصل بالأنترنت", - "core.ok": "تم", - "core.online": "متصل بالإنترنت", - "core.othergroups": "مجموعات أخرى", - "core.pagea": "صفحة {{$a}}", - "core.paymentinstant": "استخدم الزر التالي لدفع الرسوم التسجيل خلال دقائق.", - "core.phone": "هاتف", - "core.pictureof": "صورة {{$a}}", - "core.previous": "السابق", - "core.proceed": "تمت معالجته", - "core.pulltorefresh": "اسحب للأسفل ليتم التحديث", - "core.question.answer": "إجابة", - "core.question.answersaved": "تم حفظ الإجابة", - "core.question.complete": "تم/كامل", - "core.question.correct": "صحيح/صح", - "core.question.feedback": "(تغذية راجعة (ملاحظات", - "core.question.incorrect": "خطأ", - "core.question.information": "معلومات", - "core.question.invalidanswer": "إجابة غير مكتملة", - "core.question.notanswered": "لم يتم الاجابة عليه", - "core.question.notyetanswered": "لم يتم الاجابة عليه بعد", - "core.question.partiallycorrect": "إجابة جزئية", - "core.question.questionno": "سؤال {{$a}}", - "core.question.requiresgrading": "يتطلب التصحيح", - "core.quotausage": "حتى الآن قد استخدمت {{$a.used}} من ال {{$a.total}} المسموحه", - "core.rating.aggregateavg": "متوسط التقييمات", - "core.rating.aggregatecount": "عدد التقييمات", - "core.rating.aggregatemax": "الحد الأقصى للتقييمات", - "core.rating.aggregatemin": "الحد الأدنى للتقييمات", - "core.rating.aggregatesum": "مجموع التقييمات", - "core.rating.noratings": "لم يتم تسليم أي تقييمات", - "core.rating.rating": "تقييم", - "core.rating.ratings": "تقييمات", - "core.refresh": "تحديث", - "core.remove": "ازيح", - "core.required": "مفروض", - "core.resourcedisplayopen": "فتح", - "core.resources": "المصادر", - "core.restore": "إسترجاع", - "core.restricted": "متحكم فيه", - "core.save": "احفظ", - "core.savechanges": "حفظ التغييرات", - "core.search": "بحث", - "core.searching": "يتم البحث", - "core.searchresults": "نتائج البحث", - "core.sec": "ثانية", - "core.secs": "ثواني", - "core.seemoredetail": "اضغط هنا لترى تفاصيل أكثر", - "core.selectacategory": "الرجاء اختيار التصنيف", - "core.selectacourse": "اختر المقرر الدراسي", - "core.selectagroup": "اختر المجموعة", - "core.send": "ارسال", - "core.sending": "يتم الإرسال", - "core.serverconnection": "خطأ في الاتصال بالخادم", - "core.settings.about": "حول", - "core.settings.currentlanguage": "اللغة الحالية", - "core.settings.debugdisplay": "أظهر الرسائل التصحيحية", - "core.settings.deletesitefiles": "هل أنت متأكد أنك تريد حذف الملفات التي تم تنزيلها من هذا الموقع '{{sitename}}'؟", - "core.settings.deviceinfo": "معلومات الجهاز", - "core.settings.deviceos": "نظام تشغيل الجهاز", - "core.settings.disableall": "تعطيل الإعلامات بشكل مؤقت", - "core.settings.disabled": "مُعطِّل", - "core.settings.enabledownloadsection": "تفعيل تنزيل الأقسام", - "core.settings.estimatedfreespace": "تقدير المساحة الحرة", - "core.settings.general": "عام", - "core.settings.language": "اللغة", - "core.settings.license": "رخصة", - "core.settings.localnotifavailable": "إشعارات محلية موجودة", - "core.settings.locked": "مغلق", - "core.settings.loggedin": "متواجد", - "core.settings.loggedoff": "غير موجود", - "core.settings.preferences": "تفضيلات", - "core.settings.settings": "الإعدادات", - "core.settings.sites": "المواقع", - "core.settings.spaceusage": "المساحة المستخدمة", - "core.settings.synchronization": "تزامن", - "core.settings.synchronizenow": "زامن الأن", - "core.settings.syncsettings": "إعدادات المزامنة", - "core.settings.total": "مجموع", - "core.show": "عرض", - "core.showless": "اظهر أقل...", - "core.showmore": "أظهر المزيد...", - "core.site": "الموقع", - "core.sitehome.sitehome": "صفحة الموقع", - "core.sitehome.sitenews": "أخبار الموقع", - "core.sitemaintenance": "الموقع غير متاح بسبب الصيانة!", - "core.sizeb": "بايتز", - "core.sizegb": "غيغابايت", - "core.sizekb": "كيلو بايت", - "core.sizemb": "ميغا بايت", - "core.skip": "تخطى", - "core.sort": "فرز", - "core.sortby": "إفرز بـ", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedatetimeshort": "%d/%m/%y، %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.submit": "سلم", - "core.success": "نجاح", - "core.tag.searchtags": "البحث في الوسوم", - "core.tag.tag": "علامة", - "core.tag.tags": "علامات", - "core.teachers": "معلمون", - "core.thisdirection": "rtl", - "core.time": "الوقت", - "core.timesup": "انتهى الوقت!", - "core.today": "اليوم", - "core.unexpectederror": "خطأ غير متوقع. الرجاء الإغلاق وإعادة فتح التطبيق للمحاولة مرة أخرى", - "core.unknown": "غير معروف", - "core.unlimited": "بلا حدود", - "core.upgraderunning": "ًيتم ترقية الموقع، الرجاء إعادة المحاولة لاحقا.", - "core.user": "مستخدم", - "core.user.address": "عنوان", - "core.user.city": "المدينة/البلدة", - "core.user.contact": "جهة اتصال", - "core.user.country": "الدولة", - "core.user.description": "الوصف", - "core.user.editingteacher": "معلم", - "core.user.email": "عنوان البريد الإلكتروني", - "core.user.emailagain": "إعادة إدخال البريد الإلكتروني للتأكيد ", - "core.user.firstname": "الاسم الأول", - "core.user.interests": "اهتمامات", - "core.user.lastname": "الاسم الأخير", - "core.user.manager": "مدير", - "core.user.newpicture": "صورة شخصية جديدة", - "core.user.noparticipants": "لم يتم العثور على مشاركين في هذا المقرر الدراسي", - "core.user.participants": "المشتركون", - "core.user.phone1": "هاتف", - "core.user.phone2": "رقم الهاتف المحمول", - "core.user.roles": "أدوار", - "core.user.student": "الطالب", - "core.user.teacher": "معلم بدون صلاحية التحرير", - "core.user.webpage": "صفحة إنترنت", - "core.userdeleted": "تم حذف اشتراك هذا المستخدم", - "core.userdetails": "تفاصيل المستخدم", - "core.users": "المستخدمون", - "core.view": "معاينه", - "core.viewprofile": "عرض الحساب", - "core.whatisyourage": "كم عمرك؟", - "core.wheredoyoulive": "في أي بلد عشت؟", - "core.whyisthisrequired": "ما لزوم هذا؟", - "core.year": "سنة", - "core.years": "سنوات", - "core.yes": "نعم" -} \ No newline at end of file diff --git a/src/assets/lang/bg.json b/src/assets/lang/bg.json deleted file mode 100644 index b147a7520..000000000 --- a/src/assets/lang/bg.json +++ /dev/null @@ -1,1387 +0,0 @@ -{ - "addon.badges.alignment": "Подравняване", - "addon.badges.badgedetails": "Елементи на значката", - "addon.badges.badges": "Значки", - "addon.badges.contact": "Контакт", - "addon.badges.expirydate": "Дата на изтичане", - "addon.badges.issuancedetails": "Срок на значката", - "addon.badges.issuerdetails": "Данни за връчващия", - "addon.badges.issuername": "Име на връчващия", - "addon.badges.nobadges": "Няма налични значки.", - "addon.block_activitymodules.pluginname": "Дейности", - "addon.block_activityresults.pluginname": "Резултати от дейностите", - "addon.block_badges.pluginname": "Последни значки", - "addon.block_blogmenu.pluginname": "Блог меню", - "addon.block_blogrecent.pluginname": "Най-нови блог статии", - "addon.block_blogtags.pluginname": "Блог етикети", - "addon.block_calendarmonth.pluginname": "Календар", - "addon.block_calendarupcoming.pluginname": "Предстоящи събития", - "addon.block_comments.pluginname": "Коментари", - "addon.block_completionstatus.pluginname": "Напредване в курса", - "addon.block_glossaryrandom.pluginname": "Случайна дефиниция", - "addon.block_learningplans.pluginname": "Учебни планове", - "addon.block_myoverview.all": "Всички (без изключените от изгледа)", - "addon.block_myoverview.allincludinghidden": "Всички", - "addon.block_myoverview.favourites": "Отличени", - "addon.block_myoverview.future": "Бъдещи", - "addon.block_myoverview.hiddencourses": "Премахнати от изгледа", - "addon.block_myoverview.inprogress": "В ход", - "addon.block_myoverview.lastaccessed": "Последно отваряни", - "addon.block_myoverview.morecourses": "Още курсове", - "addon.block_myoverview.nocourses": "Няма курсове", - "addon.block_myoverview.past": "Минали", - "addon.block_myoverview.pluginname": "Преглед на курсовете", - "addon.block_myoverview.title": "Име на курс", - "addon.block_newsitems.pluginname": "Последни новини", - "addon.block_onlineusers.pluginname": "Онлайн потребители", - "addon.block_privatefiles.pluginname": "Лични файлове", - "addon.block_recentactivity.pluginname": "Последни дейности", - "addon.block_recentlyaccessedcourses.nocourses": "Няма текущи курсове", - "addon.block_recentlyaccessedcourses.pluginname": "Последно отваряни курсове", - "addon.block_rssclient.pluginname": "Външни RSS емисии", - "addon.block_selfcompletion.pluginname": "Самозавършване", - "addon.block_sitemainmenu.pluginname": "Главно меню", - "addon.block_tags.pluginname": "Етикети", - "addon.block_timeline.duedate": "Дата на приключване", - "addon.block_timeline.next30days": "Следващите 30 дена", - "addon.block_timeline.next3months": "Следващите 3 месеца", - "addon.block_timeline.next6months": "Следващите 6 месеца", - "addon.block_timeline.next7days": "Следващите 7 дена", - "addon.block_timeline.nocoursesinprogress": "Няма курсове в прогрес", - "addon.block_timeline.noevents": "Няма предстоящи дейности", - "addon.block_timeline.overdue": "Минали", - "addon.block_timeline.pluginname": "Време", - "addon.block_timeline.sortbycourses": "Подреждане по курсове", - "addon.block_timeline.sortbydates": "Подреждане по дати", - "addon.blog.blog": "Блог", - "addon.blog.blogentries": "Публикации в блога", - "addon.blog.errorloadentries": "Грешка при зареждане на статии от блог.", - "addon.blog.linktooriginalentry": "Хипервръзка към оригиналната статия", - "addon.blog.noentriesyet": "Тук няма видими статии", - "addon.blog.publishtonoone": "Само за Вас (чернова)", - "addon.blog.publishtosite": "Всеки регистриран", - "addon.blog.publishtoworld": "Всички посетители", - "addon.blog.showonlyyourentries": "Само Вашите статии", - "addon.blog.siteblogheading": "Блог на сайта", - "addon.calendar.allday": "Цял ден", - "addon.calendar.calendar": "Календар", - "addon.calendar.calendarevents": "Събития от календара", - "addon.calendar.categoryevents": "Събития от категория", - "addon.calendar.confirmeventdelete": "Сигурни ли сте, че искате да изтриете събитието \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "Събитието \"{{$a.name}}\" е част от серия. Искате ли да изтриете само тово събитие или всичките {{$a.count}} събития от серията?", - "addon.calendar.courseevents": "Курсови събития", - "addon.calendar.deleteallevents": "Изтриване на всички събития", - "addon.calendar.deleteevent": "Изтриване на събитие", - "addon.calendar.durationminutes": "Продължителност в минути", - "addon.calendar.durationnone": "Без продължителност", - "addon.calendar.durationuntil": "До", - "addon.calendar.editevent": "Редактиране на събитие", - "addon.calendar.errorloadevent": "Грешка при зареждането на събитие.", - "addon.calendar.errorloadevents": "Грешка при зареждането на събитията.", - "addon.calendar.eventcalendareventdeleted": "Календарното събитие е изтрито", - "addon.calendar.eventduration": "Продължителност", - "addon.calendar.eventendtime": "Крайно време", - "addon.calendar.eventkind": "Тип на събитието", - "addon.calendar.eventname": "Заглавие на събитие", - "addon.calendar.eventstarttime": "Начално време", - "addon.calendar.fri": "Пет", - "addon.calendar.friday": "петък", - "addon.calendar.groupevents": "Групови събития", - "addon.calendar.invalidtimedurationminutes": "Продължителността в минути, която въведохте е невалидно. Моля, въведете продължителността в минути да е по-голяма от 0 или изберете без продължителност.", - "addon.calendar.mon": "Пон", - "addon.calendar.monday": "понеделник", - "addon.calendar.monthlyview": "Месечен изглед", - "addon.calendar.newevent": "Ново събитие", - "addon.calendar.noevents": "Няма събития", - "addon.calendar.nopermissiontoupdatecalendar": "За съжаление Вие нямате право да актуализирате календарно събитие", - "addon.calendar.repeatedevents": "Повтарящи се събития", - "addon.calendar.repeateditall": "Промените да се приложат също и за другите {{$a}} събития от тази серия", - "addon.calendar.repeatevent": "Повтаряне на това събитие", - "addon.calendar.repeatweeksl": "Седмично, за общ брой пъти", - "addon.calendar.sat": "Съб", - "addon.calendar.saturday": "събота", - "addon.calendar.siteevents": "Сайтови събития", - "addon.calendar.sun": "Нед", - "addon.calendar.sunday": "неделя", - "addon.calendar.thu": "Чет", - "addon.calendar.thursday": "четвъртък", - "addon.calendar.today": "Днес", - "addon.calendar.tomorrow": "Утре", - "addon.calendar.tue": "Вто", - "addon.calendar.tuesday": "вторник", - "addon.calendar.typecourse": "Курсово събитие", - "addon.calendar.typegroup": "Групово събитие", - "addon.calendar.typesite": "Събитие на сайта", - "addon.calendar.typeuser": "Потребителско събитие", - "addon.calendar.upcomingevents": "Бъдещи събития", - "addon.calendar.userevents": "Потребителски събития", - "addon.calendar.wed": "Сря", - "addon.calendar.wednesday": "сряда", - "addon.calendar.yesterday": "вчера", - "addon.competency.competencies": "Компетенции", - "addon.competency.coursecompetencies": "Компетенции на курса", - "addon.competency.crossreferencedcompetencies": "Кръстосано свързани компетенции", - "addon.competency.learningplans": "Учебни планове", - "addon.competency.myplans": "Моите учебни планове", - "addon.competency.nocompetenciesincourse": "Към този курс не са свързани компетенции", - "addon.competency.nocrossreferencedcompetencies": "Няма други компетенции кръстосано свързани с тази компетенция.", - "addon.competency.noplanswerecreated": "Не бяха създадени учебни планове.", - "addon.competency.planstatusactive": "Активен", - "addon.competency.planstatuscomplete": "Завършен", - "addon.competency.planstatusdraft": "Чернова", - "addon.competency.userplans": "Учебни планове", - "addon.coursecompletion.completecourse": "Завършване на курса", - "addon.coursecompletion.completed": "Завършено", - "addon.coursecompletion.completiondate": "Дата на завършване", - "addon.coursecompletion.completionmenuitem": "Завършеност", - "addon.coursecompletion.coursecompletion": "Напредване в курса", - "addon.coursecompletion.criteria": "Критерии", - "addon.coursecompletion.criteriagroup": "Група критерии", - "addon.coursecompletion.criteriarequiredall": "Всички критерии по-долу са задължителни", - "addon.coursecompletion.criteriarequiredany": "Някои критерии по-долу са задължителни", - "addon.coursecompletion.inprogress": "В ход", - "addon.coursecompletion.manualselfcompletion": "Ръчно самоотбелязване на завършването", - "addon.coursecompletion.nottracked": "В момента вашият напредък в този курс не се проследява", - "addon.coursecompletion.notyetstarted": "Още не е започнат", - "addon.coursecompletion.pending": "В изчакване", - "addon.coursecompletion.required": "Задължително", - "addon.coursecompletion.requiredcriteria": "Изисквани критерии", - "addon.coursecompletion.requirement": "Изискване", - "addon.coursecompletion.status": "Състояние", - "addon.coursecompletion.viewcoursereport": "Вижте отчет за курса", - "addon.files.couldnotloadfiles": "Списъкът с файлове не можа да бъде зареден.", - "addon.files.emptyfilelist": "Няма файлове, които да бъдат показани.", - "addon.files.files": "Файлове", - "addon.files.privatefiles": "Лични файлове", - "addon.messages.acceptandaddcontact": "Приемам и добавям в контакти", - "addon.messages.addcontact": "Добавяне на контакт", - "addon.messages.addcontactconfirm": "Сигурни ли сте, че искате да добавите {{$a}} в своите контакти?", - "addon.messages.addtofavourites": "Отличаване на разговора", - "addon.messages.addtoyourcontacts": "Добавяне към контакти", - "addon.messages.blocknoncontacts": "Блокирай съобщения от потребители извън списъка с контакти ", - "addon.messages.blockuser": "Блокиране на потребител", - "addon.messages.blockuserconfirm": "Сигурни ли сте, че искате да блокирате {{$a}}?", - "addon.messages.contactableprivacy": "Приемане на съобщение от:", - "addon.messages.contactableprivacy_coursemember": "Моите контакти и всики от моите курсове", - "addon.messages.contactableprivacy_onlycontacts": "Само моите контакти", - "addon.messages.contactableprivacy_site": "Всеки от сайта", - "addon.messages.contactblocked": "Контактът е блокиран", - "addon.messages.contactlistempty": "Списъка с контакти е празен", - "addon.messages.contactname": "Име на контакта", - "addon.messages.contactrequestsent": "Предложение за контакт е изпратено", - "addon.messages.contacts": "Контакти", - "addon.messages.conversationactions": "Меню за действия с разговори", - "addon.messages.decline": "Отказване", - "addon.messages.deleteallconfirm": "Сигурни ли сте, че искате да изтриете целия разговор? Това няма да го изтрие за другите участници.", - "addon.messages.deleteallselfconfirm": "Сигурни ли сте, че искате да изтриете целия този личен разговор?", - "addon.messages.deleteconversation": "Изтриване разговор", - "addon.messages.deleteforeveryone": "Изтриване за мен и всеки друг", - "addon.messages.errorwhileretrievingcontacts": "Грешка при изчитането на списъка с контакти от сървъра.", - "addon.messages.errorwhileretrievingdiscussions": "Грешка при изчитането на дискусиите от сървъра.", - "addon.messages.errorwhileretrievingmessages": "Грешка при изчитането на съобщенията от сървъра.", - "addon.messages.groupconversations": "Група", - "addon.messages.groupinfo": "Информация за група", - "addon.messages.individualconversations": "Лично", - "addon.messages.info": "Информация за потребител", - "addon.messages.isnotinyourcontacts": "{{$a}} не е във Вашите контакти", - "addon.messages.message": "Съобщение", - "addon.messages.messagenotsent": "Съобщението не беше изпратено. Моля опитайте пак по-късно.", - "addon.messages.messagepreferences": "Предпочитания за съобщенията", - "addon.messages.messages": "Съобщения", - "addon.messages.muteconversation": "Заглушаване", - "addon.messages.mutedconversation": "Заглушен разговор", - "addon.messages.newmessage": "Ново съобщение", - "addon.messages.nocontactrequests": "Няма предложения за контакт", - "addon.messages.nocontactsgetstarted": "Няма контакти", - "addon.messages.nofavourites": "Няма отличени разговори", - "addon.messages.nogroupconversations": "Няма групови разговори", - "addon.messages.noindividualconversations": "Няма лични разговори", - "addon.messages.nomessagesfound": "Не бяха открити съобщения", - "addon.messages.noncontacts": "Не-контакти", - "addon.messages.numparticipants": "{{$a}} участници", - "addon.messages.removecontact": "Премахване на контакт", - "addon.messages.removecontactconfirm": "Наистина ли сте сигурни, че искате да премахнете {{$a}} от Вашите контакти?", - "addon.messages.removefromfavourites": "Премахване отличаване на разговор", - "addon.messages.removefromyourcontacts": "Премахване от контакти", - "addon.messages.requirecontacttomessage": "Вие трябва да предложите на {{$a}} да ви включи в контакти, за да му изпратите съобщение.", - "addon.messages.searchcombined": "Търсене на хора и съобщения", - "addon.messages.selfconversation": "Лично пространство", - "addon.messages.selfconversationdefaultmessage": "Запазване чернови на съобщения, хипервръзки, бележки и т.н. за ползване по-късно.", - "addon.messages.sendcontactrequest": "Изпращане предложение за контакт", - "addon.messages.type_blocked": "Блокиран", - "addon.messages.type_offline": "Офлайн", - "addon.messages.type_online": "Онлайн", - "addon.messages.type_search": "Резултати от търсенето", - "addon.messages.type_strangers": "Други", - "addon.messages.unabletomessage": "Вие не можете да изпратите съобщение на този потребител", - "addon.messages.unblockuser": "Разблокиране на потребител", - "addon.messages.unblockuserconfirm": "Сигурни ли сте, че искате да разблокирате {{$a}}?", - "addon.messages.unmuteconversation": "Озвучаване", - "addon.messages.useentertosend": "Натискане на Enter за изпращане", - "addon.messages.userwouldliketocontactyou": "{{$a}} иска да се свърже с Вас", - "addon.messages.wouldliketocontactyou": "Желае контакт с Вас", - "addon.messages.you": "Вие:", - "addon.messages.youhaveblockeduser": "Вие сте блокирали този потребител.", - "addon.messages.yourcontactrequestpending": "Вашата покана за контакт с {{$a}} е в очакване", - "addon.mod_assign.addattempt": "Позволяване на друг опит", - "addon.mod_assign.addnewattempt": "Позволяване на нов опит", - "addon.mod_assign.addnewattemptfromprevious": "Добавяне на нов опит, основан на предишно предаване", - "addon.mod_assign.addsubmission": "Добавяне на изпълнение", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Данните за заданието и формулярът за предаване ще бъдат достъпни от {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Позволено предаване от", - "addon.mod_assign.allowsubmissionsfromdatesummary": "По това задание се приемат работи от {{$a}}", - "addon.mod_assign.applytoteam": "Прилагане на оценки и забележки към цялата група", - "addon.mod_assign.assignmentisdue": "Заданието е приключило", - "addon.mod_assign.attemptnumber": "Брой опити", - "addon.mod_assign.attemptreopenmethod": "Отворени отново опити", - "addon.mod_assign.attemptreopenmethod_manual": "Ръчно", - "addon.mod_assign.attemptreopenmethod_untilpass": "Автоматично до завършване", - "addon.mod_assign.attemptsettings": "Настройки на опитите", - "addon.mod_assign.confirmsubmission": "Сигурни ли сте, че искате да предадете работата си за оценяване? След това няма да можете да правите никакви промени повече.", - "addon.mod_assign.currentattempt": "Този опит {{$a}}.", - "addon.mod_assign.currentattemptof": "Това е {{$a.attemptnumber}}-и опит ( от разрешените {{$a.maxattempts}} ).", - "addon.mod_assign.currentgrade": "Оценка в дневника за оценки", - "addon.mod_assign.cutoffdate": "Дата на отказ", - "addon.mod_assign.defaultteam": "Група по подразбиране", - "addon.mod_assign.duedate": "Краен срок", - "addon.mod_assign.duedateno": "Няма краен срок", - "addon.mod_assign.duedatereached": "Крайният срок на това задание сега е изтекъл", - "addon.mod_assign.editingstatus": "Състояние на редактиране", - "addon.mod_assign.editsubmission": "Редактиране на задание", - "addon.mod_assign.extensionduedate": "Крайна дата на отлагането", - "addon.mod_assign.grade": "Оценка", - "addon.mod_assign.graded": "Оценена", - "addon.mod_assign.gradedby": "Оценено от", - "addon.mod_assign.gradedon": "Оценено на", - "addon.mod_assign.gradelocked": "Тази оценка е заключена или е коригирана в дневника за оценки.", - "addon.mod_assign.gradeoutof": "Оценка до {{$a}}", - "addon.mod_assign.gradingstatus": "Състояние на оценяването", - "addon.mod_assign.groupsubmissionsettings": "Настройки за групово предаване", - "addon.mod_assign.hiddenuser": "Участник", - "addon.mod_assign.latesubmissions": "Закъснели предавания", - "addon.mod_assign.latesubmissionsaccepted": "Позволено до {{$a}}", - "addon.mod_assign.markingworkflowstate": "Отбелязване на етап на оценяване", - "addon.mod_assign.markingworkflowstateinmarking": "В процес на оценяване", - "addon.mod_assign.markingworkflowstateinreview": "Преглеждане", - "addon.mod_assign.markingworkflowstatenotmarked": "Не оценени", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Готово за обявяване", - "addon.mod_assign.markingworkflowstatereadyforreview": "Оценяването е завършено", - "addon.mod_assign.markingworkflowstatereleased": "Обявено", - "addon.mod_assign.modulenameplural": "Задания", - "addon.mod_assign.multipleteams": "Член на повече от една група", - "addon.mod_assign.multipleteams_desc": "Това задание изисква предаване по групи. Вие сте член на повече от една група. За да може да предадете, трябва да сте член на само една група. Моля, свържете се с Вашия преподавател за да промените членството в групата.", - "addon.mod_assign.noattempt": "Няма опити", - "addon.mod_assign.nomoresubmissionsaccepted": "Само за участници, получили позволение за отлагане", - "addon.mod_assign.noonlinesubmissions": "Това задание не изисква от Вас нищо да качвате онлайн", - "addon.mod_assign.nosubmission": "Нищо не е изпратено по това задание", - "addon.mod_assign.noteam": "Не членуване в група", - "addon.mod_assign.noteam_desc": "Това задание изисква предаване по групи. Вие не сте член на група и не можете да предадете изпълнение. Моля, свържете се с преподавател, за да Ви включи в група.", - "addon.mod_assign.notgraded": "Неоценена", - "addon.mod_assign.numberofdraftsubmissions": "Чернови", - "addon.mod_assign.numberofparticipants": "Участници", - "addon.mod_assign.numberofsubmissionsneedgrading": "Изискващи оценка", - "addon.mod_assign.numberofsubmittedassignments": "Предадени", - "addon.mod_assign.numberofteams": "Групи", - "addon.mod_assign.numwords": "{{$a}} думи", - "addon.mod_assign.outof": "{{$a.current}} от {{$a.total}}", - "addon.mod_assign.overdue": "Предаването на работата закъснява с {{$a}} след срока", - "addon.mod_assign.submission": "Задание", - "addon.mod_assign.submissioneditable": "Студентът може да редактира това задание", - "addon.mod_assign.submissionnoteditable": "Студентът не може да редактира това задание", - "addon.mod_assign.submissionslocked": "Това задание не приема работи за предаване", - "addon.mod_assign.submissionstatus": "Състояние на заданието", - "addon.mod_assign.submissionstatus_": "Непредадена", - "addon.mod_assign.submissionstatus_draft": "Чернова (не е предадена)", - "addon.mod_assign.submissionstatus_marked": "Оценена", - "addon.mod_assign.submissionstatus_new": "Непредадена", - "addon.mod_assign.submissionstatus_reopened": "Отворена отново", - "addon.mod_assign.submissionstatus_submitted": "Предадена за оценка", - "addon.mod_assign.submissionstatusheading": "Състояние на заданието", - "addon.mod_assign.submissionteam": "Група", - "addon.mod_assign.submitassignment": "Предаване на задание", - "addon.mod_assign.submitassignment_help": "Предадете ли веднъж работата по заданието, Вие няма да можете да я променяте повече.", - "addon.mod_assign.submittedearly": "Работата е предадена {{$a}} по-рано", - "addon.mod_assign.submittedlate": "Работата по заданието е предадена с {{$a}} закъснение", - "addon.mod_assign.timemodified": "Последна промяна", - "addon.mod_assign.timeremaining": "Оставащо време", - "addon.mod_assign.ungroupedusers": "Активирана е настройката \"Членство в група за предаване\", а някои участници не участват в групи, или участват в повече от една група и не могат да предадат работи.", - "addon.mod_assign.unlimitedattempts": "Неограничен", - "addon.mod_assign.userswhoneedtosubmit": "Потребители, които трябва да предадат: {{$a}}", - "addon.mod_assign.viewsubmission": "Преглед на предадена работа", - "addon.mod_assign.wordlimit": "Максимален брой думи", - "addon.mod_assign_feedback_comments.pluginname": "Обратни коментари", - "addon.mod_assign_feedback_editpdf.pluginname": "Бележки в PDF", - "addon.mod_assign_feedback_file.pluginname": "Файл с обратна информация", - "addon.mod_assign_submission_comments.pluginname": "Коментари към заданието", - "addon.mod_assign_submission_file.pluginname": "Качване на файлове", - "addon.mod_assign_submission_onlinetext.pluginname": "Писане на текст онлайн", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Максималният брой думи за това задание е {{$a.limit}}, а Вие се опитвате да изпратите отговор от {{$a.count}} думи. Моля редактирайте своя отговор и опитайте отново.", - "addon.mod_book.errorchapter": "Грешка при четене на глава от книга.", - "addon.mod_book.modulenameplural": "Книги", - "addon.mod_book.toc": "Съдържание", - "addon.mod_chat.beep": "Биип", - "addon.mod_chat.chatreport": "Чат сесии", - "addon.mod_chat.currentusers": "Текущи потребители", - "addon.mod_chat.enterchat": "Влизане в чата сега", - "addon.mod_chat.entermessage": "Въведете Вашето съобщение", - "addon.mod_chat.messagebeepseveryone": "{{$a}} бибитка на всички!", - "addon.mod_chat.messagebeepsyou": "{{$a}} току що Ви бибитна!", - "addon.mod_chat.messageenter": "{{$a}} току що влезе в този чат", - "addon.mod_chat.messageexit": "{{$a}} напусна този чат", - "addon.mod_chat.messages": "Съобщения", - "addon.mod_chat.messageyoubeep": "Вие бибитнахте на {{$a}}", - "addon.mod_chat.modulenameplural": "Чатове", - "addon.mod_chat.nomessages": "Още няма съобщения", - "addon.mod_chat.saidto": "каза на", - "addon.mod_chat.send": "Изпращане", - "addon.mod_chat.sessionstart": "", - "addon.mod_chat.talk": "Разговор", - "addon.mod_chat.viewreport": "Преглеждане на миналите чат сесии", - "addon.mod_choice.choiceoptions": "Възможности за избиране", - "addon.mod_choice.expired": "Тази дейност е затворена от {{$a}}", - "addon.mod_choice.full": "(Пълен)", - "addon.mod_choice.modulenameplural": "Възможности", - "addon.mod_choice.noresultsviewable": "В момента резултатите не са за показване.", - "addon.mod_choice.notopenyet": "Тази дейност не е достъпна до {{$a}}", - "addon.mod_choice.numberofuser": "Брой отговори", - "addon.mod_choice.numberofuserinpercentage": "Процент от отговорите", - "addon.mod_choice.publishinfoanonafter": "Анонимни резултати ще бъдат показани след като отговорите.", - "addon.mod_choice.publishinfoanonclose": "Анонимни резултати ще бъдат публикувани след като тази дейност бъде затворена.", - "addon.mod_choice.removemychoice": "Изтриване на моя избор", - "addon.mod_choice.responses": "Отговори", - "addon.mod_choice.responsesresultgraphheader": "Показване на графика", - "addon.mod_choice.savemychoice": "Запис на моя избор", - "addon.mod_choice.userchoosethisoption": "Потребители, избрали възможността", - "addon.mod_choice.yourselection": "Вашият избор", - "addon.mod_data.addentries": "Добавяне на записи", - "addon.mod_data.advancedsearch": "Разширено търсене", - "addon.mod_data.alttext": "Алтернативен текст", - "addon.mod_data.approve": "Одобрение", - "addon.mod_data.approved": "Одобрен", - "addon.mod_data.ascending": "По нарастване", - "addon.mod_data.authorfirstname": "Име на автора", - "addon.mod_data.authorlastname": "Презиме на автора", - "addon.mod_data.confirmdeleterecord": "Сигурни ли сте, че искате да изтриете това?", - "addon.mod_data.descending": "По намаляване", - "addon.mod_data.disapprove": "Отхвърляне на одобрение", - "addon.mod_data.emptyaddform": "Не сте попълнили никакви полета!", - "addon.mod_data.entrieslefttoadd": "Трябва да добавите още {{$a.entriesleft}} записи, за да завършите тази дейност", - "addon.mod_data.entrieslefttoaddtoview": "Трябва да добавите още {{$a.entrieslefttoview}} записи преди да можете да видите записите на другите участници.", - "addon.mod_data.expired": "За съжаление тази дейност е затворена в {{$a}} и вече не е достъпна", - "addon.mod_data.fields": "Полета", - "addon.mod_data.foundrecords": "Намерени записи: {{$a.num}}/{{$a.max}} (Анулиране на филтрите)", - "addon.mod_data.menuchoose": "Изберете...", - "addon.mod_data.modulenameplural": "Бази данни", - "addon.mod_data.more": "Още", - "addon.mod_data.nomatch": "Не са намерени съответстващи записи!", - "addon.mod_data.norecords": "Няма записи в базата данни", - "addon.mod_data.notapproved": "Записът още не е качен", - "addon.mod_data.notopenyet": "За съжаление тази дейност не е достъпна от {{$a}}", - "addon.mod_data.numrecords": "{{$a}} записи", - "addon.mod_data.other": "Друго", - "addon.mod_data.recordapproved": "Записът качен", - "addon.mod_data.recorddeleted": "Записът изтрит", - "addon.mod_data.recorddisapproved": "Записът не е одобрен", - "addon.mod_data.resetsettings": "Нулиране на филтрите", - "addon.mod_data.search": "Търсене", - "addon.mod_data.selectedrequired": "Всичко избрано се изисква", - "addon.mod_data.single": "Единично показване", - "addon.mod_data.timeadded": "Време на добавяне", - "addon.mod_data.timemodified": "Време на промяна", - "addon.mod_data.usedate": "Включване в търсене.", - "addon.mod_feedback.analysis": "Анализ", - "addon.mod_feedback.anonymous": "Анонимна", - "addon.mod_feedback.anonymous_entries": "Анонимни отговори", - "addon.mod_feedback.average": "Средно", - "addon.mod_feedback.complete_the_form": "Отговаряне на въпросите...", - "addon.mod_feedback.completed_feedbacks": "Изпратени отговори", - "addon.mod_feedback.continue_the_form": "Продължаване с отговаряне на въпросите", - "addon.mod_feedback.feedback_is_not_open": "Анкетата не е отворена", - "addon.mod_feedback.feedbackclose": "Позволено е отговаряне до", - "addon.mod_feedback.feedbackopen": "Позволено е отговаряне от", - "addon.mod_feedback.mapcourses": "Свързване на анкетата с курсове", - "addon.mod_feedback.maximal": "максимум", - "addon.mod_feedback.minimal": "минимум", - "addon.mod_feedback.mode": "Режим", - "addon.mod_feedback.modulenameplural": "Обратни връзки", - "addon.mod_feedback.next_page": "Следваща страница", - "addon.mod_feedback.non_anonymous": "Да - ще се показват с отговорите", - "addon.mod_feedback.non_anonymous_entries": "няма анонимни отговори", - "addon.mod_feedback.non_respondents_students": "не отговорили студенти", - "addon.mod_feedback.not_selected": "Няма избран", - "addon.mod_feedback.not_started": "не започната", - "addon.mod_feedback.overview": "Представяне", - "addon.mod_feedback.page_after_submit": "Съобщение след попълване", - "addon.mod_feedback.preview": "Преглед", - "addon.mod_feedback.previous_page": "Предишна страница", - "addon.mod_feedback.questions": "Въпроси", - "addon.mod_feedback.response_nr": "Номер на отговора", - "addon.mod_feedback.responses": "Отговори", - "addon.mod_feedback.save_entries": "Изпращане на отговорите", - "addon.mod_feedback.show_entries": "Преглед на отговорите", - "addon.mod_feedback.show_nonrespondents": "Показване на не отговорилите", - "addon.mod_feedback.started": "започната", - "addon.mod_feedback.this_feedback_is_already_submitted": "Вие вече сте изпълнили тази дейност.", - "addon.mod_folder.modulenameplural": "Папки", - "addon.mod_forum.addanewdiscussion": "Добавяне на нова тема за обсъждане", - "addon.mod_forum.addanewquestion": "Добавяне на нов въпрос", - "addon.mod_forum.addanewtopic": "Добавяне на нова тема", - "addon.mod_forum.addtofavourites": "Оценяване на тази дискусия", - "addon.mod_forum.advanced": "Разширени", - "addon.mod_forum.cannotadddiscussion": "Добавяне на дискусии в този форум изисква членство в група.", - "addon.mod_forum.cannotadddiscussionall": "Нямате разрешение да добавяте нова тема за всички участници.", - "addon.mod_forum.cannotcreatediscussion": "Не може да се създаде нова дискусия", - "addon.mod_forum.couldnotadd": "Вашето мнение не можа да се добави поради неизвестна грешка.", - "addon.mod_forum.couldnotupdate": "Вашето мнение не можа да се обнови поради неизвестна грешка.", - "addon.mod_forum.delete": "Изтриване", - "addon.mod_forum.deletedpost": "Мнението е изтрито", - "addon.mod_forum.deletesure": "Сигурни ли сте, че искате да изтриете това мнение?", - "addon.mod_forum.discussion": "Обсъждане", - "addon.mod_forum.discussionlocked": "Тази дискусия е заключена и не можете да отговаряте повече в нея.", - "addon.mod_forum.discussionpinned": "Забодена", - "addon.mod_forum.discussionsubscription": "Абониране за обсъждането", - "addon.mod_forum.edit": "Редактиране", - "addon.mod_forum.erroremptymessage": "Мнението не може да бъде празно", - "addon.mod_forum.erroremptysubject": "Темата на мнението не може да бъде празна.", - "addon.mod_forum.forumnodiscussionsyet": "Няма теми за обсъждане в този форум", - "addon.mod_forum.group": "Група", - "addon.mod_forum.lastpost": "Последно мнение", - "addon.mod_forum.lockdiscussion": "Заключване на тази дискусия", - "addon.mod_forum.lockupdated": "Настройките за заключване бяха променени.", - "addon.mod_forum.message": "Вашето мнение", - "addon.mod_forum.modeflatnewestfirst": "Плоско показване на отговорите. Най-новият е първи.", - "addon.mod_forum.modeflatoldestfirst": "Плоско показване на отговорите. Най-старият е първи.", - "addon.mod_forum.modenested": "Показване на отговорите във вложена форма.", - "addon.mod_forum.modulenameplural": "Форуми", - "addon.mod_forum.pindiscussion": "Забождане на тази дискусия", - "addon.mod_forum.pinupdated": "Забождането беше отменено.", - "addon.mod_forum.postisprivatereply": "Това е личен отговор. Той не е видим от другите участници.", - "addon.mod_forum.posttoforum": "Изпрати във форума", - "addon.mod_forum.posttomygroups": "Изпращане копие до всички групи", - "addon.mod_forum.privatereply": "Лично отговаряне", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Опресняване на обсъжданията", - "addon.mod_forum.removefromfavourites": "Отмяна на оценяване", - "addon.mod_forum.reply": "Отговаряне", - "addon.mod_forum.subject": "Тема", - "addon.mod_forum.unlockdiscussion": "Отключване на тази дискусия", - "addon.mod_forum.unpindiscussion": "Премахване на зобождане", - "addon.mod_forum.unread": "Непрочетено", - "addon.mod_forum.unreadpostsnumber": "{{$a}} непрочетени мнения", - "addon.mod_forum.yourreply": "Вашият отговор", - "addon.mod_glossary.addentry": "Добавяне на нова дефиниция", - "addon.mod_glossary.aliases": "Ключови думи", - "addon.mod_glossary.attachment": "Прикачен файл", - "addon.mod_glossary.byauthor": "Групиране по автори", - "addon.mod_glossary.bycategory": "Групиране по категории", - "addon.mod_glossary.casesensitive": "Разлика между малки и големи букви", - "addon.mod_glossary.categories": "Категории", - "addon.mod_glossary.concept": "Термин", - "addon.mod_glossary.definition": "Тълкование", - "addon.mod_glossary.entryusedynalink": "Тази дефиниция трябва автоматично да се свързва", - "addon.mod_glossary.errconceptalreadyexists": "Този термин вече съществува. В този речник не е разрешено повтаряне на дефиниции.", - "addon.mod_glossary.errorloadingentries": "Възникна грешка докато се зареждаше записът.", - "addon.mod_glossary.errorloadingentry": "Възникна грешка докато се зареждаше записът.", - "addon.mod_glossary.errorloadingglossary": "Възникна грешка по време на изтеглянето на речника.", - "addon.mod_glossary.fillfields": "Терминът и тълкованието са задължителни полета.", - "addon.mod_glossary.fullmatch": "Свързване само на цели думи", - "addon.mod_glossary.linking": "Автоматично свързване", - "addon.mod_glossary.modulenameplural": "Речници", - "addon.mod_glossary.tagarea_glossary_entries": "Дефиниции в речника", - "addon.mod_imscp.deploymenterror": "Грешка в съдържанието на пакета!", - "addon.mod_imscp.modulenameplural": "IMS пакети", - "addon.mod_imscp.toc": "Съдържание", - "addon.mod_lesson.answer": "Отговор", - "addon.mod_lesson.attempt": "{{$a}} опит", - "addon.mod_lesson.attemptsremaining": "Имате оставащ(и) {{$a}} опит(a)", - "addon.mod_lesson.averagescore": "Среден резултат", - "addon.mod_lesson.averagetime": "Средно време", - "addon.mod_lesson.branchtable": "Съдържание", - "addon.mod_lesson.cannotfindattempt": "Грешка: не може да бъде намерен опит", - "addon.mod_lesson.cannotfinduser": "Грешка: не може да се намерят потребители", - "addon.mod_lesson.clusterjump": "Непоказван въпрос в група", - "addon.mod_lesson.completed": "Завършена", - "addon.mod_lesson.congratulations": "Поздравления - достигнахте до края на урока", - "addon.mod_lesson.continue": "Продължаване", - "addon.mod_lesson.continuetonextpage": "Продължи към следващата страница", - "addon.mod_lesson.defaultessayresponse": "Вашето есе ще бъде оценено от преподавател от курса.", - "addon.mod_lesson.detailedstats": "Детайлни статистики", - "addon.mod_lesson.didnotanswerquestion": "Did not answer this question.", - "addon.mod_lesson.displayofgrade": "Показване на оценката (само за ученици)", - "addon.mod_lesson.displayscorewithessays": "Получихте {{$a.score}} точки от общо {{$a.tempmaxgrade}} точки за въпросите с автоматично оценяване.
Вашите {{$a.essayquestions}} въпроси от тип есе ще бъдат оценени и добавени към окончателния резултат на по-късна дата.

Вашата текуща оценка, без въпросите от тип есе е {{$a.score}} от максимум {{$a.grade}}", - "addon.mod_lesson.displayscorewithoutessays": "Вашият резултат е {{$a.score}} (От максимум {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Паролата не може да е празна", - "addon.mod_lesson.enterpassword": "Please enter the password:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "You did not answer any questions. You have received a 0 for this lesson.", - "addon.mod_lesson.firstwrong": "Unfortunately you cannot earn this one point, because your response was not correct. Would you like to keep guessing, just for the sheer joy of learning (but for no point credit)?", - "addon.mod_lesson.gotoendoflesson": "Отиване в края на урока", - "addon.mod_lesson.grade": "Оценка", - "addon.mod_lesson.highscore": "Висок резултат", - "addon.mod_lesson.hightime": "High Time", - "addon.mod_lesson.leftduringtimed": "You have left during a timed lesson.
Please click on Continue to restart the lesson.", - "addon.mod_lesson.leftduringtimednoretake": "You have left during a timed lesson and you are
not allowed to retake or continue the lesson.", - "addon.mod_lesson.lessonmenu": "Lesson Menu", - "addon.mod_lesson.lessonstats": "Статистики за урока", - "addon.mod_lesson.loginfail": "Login Failed, please try again...", - "addon.mod_lesson.lowscore": "Нисък резултат", - "addon.mod_lesson.lowtime": "Low Time", - "addon.mod_lesson.maximumnumberofattemptsreached": "Достигнат е максималния брой опити - преминаване на следваща страница", - "addon.mod_lesson.modattemptsnoteacher": "\"Преглеждане от студента\" се отнася само за студент.", - "addon.mod_lesson.modulenameplural": "Уроци", - "addon.mod_lesson.noanswer": "Няма даден отговор", - "addon.mod_lesson.nolessonattempts": "Не са правени опити за изпълнение на урока.", - "addon.mod_lesson.notcompleted": "Не е завършен", - "addon.mod_lesson.numberofcorrectanswers": "Верни отговори: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Брой на отговорените въпроси: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Брой отговорени въпроси: {{$a.nquestions}}; (Трябва да отговорите поне на {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Вие сте получили до сега {{$a.score}} точки от {{$a.currenthigh}} възможни.", - "addon.mod_lesson.ongoingnormal": "Вие сте отговорили до сега на {{$a.correct}} въпрос(и) от общо {{$a.viewed}} въпроса.", - "addon.mod_lesson.or": "ИЛИ", - "addon.mod_lesson.overview": "Обобщение", - "addon.mod_lesson.preview": "Преглед", - "addon.mod_lesson.progressbarteacherwarning2": "Вие няма да видите лентата за напредък, защото можете да редактирате този урок", - "addon.mod_lesson.progresscompleted": "Вие сте изпълнили {{$a}}% от урока", - "addon.mod_lesson.question": "Въпрос", - "addon.mod_lesson.reports": "Отчети", - "addon.mod_lesson.response": "Забележка", - "addon.mod_lesson.review": "Преглед", - "addon.mod_lesson.reviewlesson": "Review Lesson", - "addon.mod_lesson.reviewquestionback": "Да, искам да опитам отново", - "addon.mod_lesson.reviewquestioncontinue": "Не, искам само да мина на следващия въпрос", - "addon.mod_lesson.secondpluswrong": "Не е точно така. Искате ли да опитате отново?", - "addon.mod_lesson.submit": "Продължаване", - "addon.mod_lesson.teacherjumpwarning": "В този урок е използван преход \"{{$a.cluster}}\" или преход \"{{$a.unseen}}\". Вместо това ще бъде използван преход \"Следваща страница\". Влезте като студент за да изпробвате тези преходи.", - "addon.mod_lesson.teacherongoingwarning": "Получените точки се показват само на студент. Влезте като студент за да проверите как се начисляват точки.", - "addon.mod_lesson.teachertimerwarning": "Timer only works for students. Test the timer by loggin in as a student.", - "addon.mod_lesson.thatsthecorrectanswer": "Това е верен отговор", - "addon.mod_lesson.thatsthewronganswer": "Това е грешен отговор", - "addon.mod_lesson.timeremaining": "Time Remaining", - "addon.mod_lesson.timetaken": "Изминало време", - "addon.mod_lesson.unseenpageinbranch": "Не показван въпрос в меню-страница", - "addon.mod_lesson.welldone": "Добре изпълнено!", - "addon.mod_lesson.youhaveseen": "Вече сте видели повече от една страница от този урок.
Искате ли да продължите от последната, която сте чели?", - "addon.mod_lesson.youranswer": "Вашият отговор", - "addon.mod_lesson.youshouldview": "Трябва да прегледате поне {{$a}}", - "addon.mod_page.errorwhileloadingthepage": "Грешка при зареждане съдържанието на страницата.", - "addon.mod_page.modulenameplural": "Страници", - "addon.mod_quiz.answercolon": "Отговор:", - "addon.mod_quiz.attemptfirst": "Първи опит", - "addon.mod_quiz.attemptlast": "Последен опит", - "addon.mod_quiz.attemptnumber": "Опит", - "addon.mod_quiz.attemptquiznow": "Започване на теста сега", - "addon.mod_quiz.attemptstate": "Състояние", - "addon.mod_quiz.clearchoice": "Премахване на моя избор", - "addon.mod_quiz.comment": "Коментар", - "addon.mod_quiz.completedon": "Приключен на", - "addon.mod_quiz.confirmclose": "След като веднъж предадете този опит, Вие повече няма да можете да променяте отговорите си за този опит.", - "addon.mod_quiz.confirmstart": "Този тест е с продължителност {{$a}}. Времето започва да тече от момента, в който започнете Вашия опит. Трябва да предадете Вашия опит преди времето да изтече. Сигурни ли сте, че искате за започнете сега?", - "addon.mod_quiz.confirmstartheader": "Тест с времеви лимит", - "addon.mod_quiz.continueattemptquiz": "Продължаване на последния опит", - "addon.mod_quiz.continuepreview": "Продължаване на последния преглед", - "addon.mod_quiz.errordownloading": "Грешка при изтегляне на изискваните данни.", - "addon.mod_quiz.feedback": "Забележка", - "addon.mod_quiz.finishattemptdots": "Приключване на опита...", - "addon.mod_quiz.grade": "Оценка", - "addon.mod_quiz.gradeaverage": "Средна оценка", - "addon.mod_quiz.gradehighest": "Най-висока оценка", - "addon.mod_quiz.grademethod": "Метод за оценяване", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Точки", - "addon.mod_quiz.modulenameplural": "Тестове", - "addon.mod_quiz.mustbesubmittedby": "Това изпълнение трябва да бъде предадено до {{$a}}.", - "addon.mod_quiz.noquestions": "Още не са добавени въпроси", - "addon.mod_quiz.noreviewattempt": "Вие нямате право да прегледате този опит.", - "addon.mod_quiz.notyetgraded": "Още не е оценен", - "addon.mod_quiz.outof": "{{$a.grade}} от {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} от {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Цялостна забележка", - "addon.mod_quiz.overdue": "Закъсняло предаване", - "addon.mod_quiz.overduemustbesubmittedby": "Това изпълнение е закъсняло. Трябва вече да е предадено. Ако искате този тест да бъде оценен, Вие трябва да го предадете до {{$a}}. Ако не го направите до тогава, оценката от това изпълнение няма да се отчете.", - "addon.mod_quiz.preview": "Преглед", - "addon.mod_quiz.previewquiznow": "Преглед на теста сега", - "addon.mod_quiz.question": "Въпрос", - "addon.mod_quiz.quiznavigation": "Навигация в теста", - "addon.mod_quiz.quizpassword": "Парола за теста", - "addon.mod_quiz.reattemptquiz": "Изпълняване на теста отново", - "addon.mod_quiz.requirepasswordmessage": "За да започнете този тест, трябва да знаете паролата за теста", - "addon.mod_quiz.returnattempt": "Връщане към изпълнението", - "addon.mod_quiz.review": "Преглед", - "addon.mod_quiz.reviewofattempt": "Преглеждане на опит {{$a}}", - "addon.mod_quiz.reviewofpreview": "Преглеждане на прегледа", - "addon.mod_quiz.showall": "Показване на всички въпроси на една страница", - "addon.mod_quiz.showeachpage": "Показване по един въпрос на страница", - "addon.mod_quiz.startattempt": "Започване на опита", - "addon.mod_quiz.startedon": "Започнат на", - "addon.mod_quiz.stateabandoned": "Непредаден", - "addon.mod_quiz.statefinished": "Завършен", - "addon.mod_quiz.statefinisheddetails": "Предаден {{$a}}", - "addon.mod_quiz.stateinprogress": "В процес", - "addon.mod_quiz.stateoverdue": "Закъснял", - "addon.mod_quiz.stateoverduedetails": "Трябва да се предаде до {{$a}}", - "addon.mod_quiz.status": "Състояние", - "addon.mod_quiz.submitallandfinish": "Предаване на всичко и приключване", - "addon.mod_quiz.summaryofattempt": "Обобщение на опита", - "addon.mod_quiz.summaryofattempts": "Обобщение на предишните Ви опити", - "addon.mod_quiz.timeleft": "Оставащо време", - "addon.mod_quiz.timetaken": "Изминало време", - "addon.mod_quiz.yourfinalgradeis": "Финалната Ви оценка за този тест е {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Грешка при зареждане на съдържание.", - "addon.mod_resource.modulenameplural": "Файлове", - "addon.mod_resource.openthefile": "Отвори файла", - "addon.mod_scorm.attempts": "Опити", - "addon.mod_scorm.averageattempt": "Средна оценка", - "addon.mod_scorm.browse": "Преглед", - "addon.mod_scorm.browsemode": "Режим на преглеждане", - "addon.mod_scorm.completed": "Приключен", - "addon.mod_scorm.contents": "Съдържание", - "addon.mod_scorm.enter": "Влизане", - "addon.mod_scorm.errordownloadscorm": "Грешка пи изтегляне на SCORM: \"{{name}}\".", - "addon.mod_scorm.firstattempt": "Първи опит", - "addon.mod_scorm.gradeaverage": "Средна оценка", - "addon.mod_scorm.gradeforattempt": "Оценка за опит", - "addon.mod_scorm.gradehighest": "Най-висока оценка", - "addon.mod_scorm.grademethod": "Метод за оценяване", - "addon.mod_scorm.gradereported": "Отчетена оценка", - "addon.mod_scorm.highestattempt": "Най-висока оценка", - "addon.mod_scorm.lastattempt": "Последен завършен опит", - "addon.mod_scorm.newattempt": "Започване нов опит", - "addon.mod_scorm.noattemptsallowed": "Брой на разрешените последователни опити", - "addon.mod_scorm.noattemptsmade": "Брой на опитите, които Вие сте направили", - "addon.mod_scorm.reviewmode": "Режим на преглеждане", - "addon.mod_survey.ifoundthat": "Аз открих, че", - "addon.mod_survey.ipreferthat": "Аз предпочинтам", - "addon.mod_survey.modulenameplural": "Анкети", - "addon.mod_survey.responses": "Отговори", - "addon.mod_survey.surveycompletednograph": "Вие сте завършили тази анкета.", - "addon.mod_url.modulenameplural": "URL-и", - "addon.mod_url.pointingtourl": "URL адрес към който този сочи този ресурс", - "addon.mod_wiki.cannoteditpage": "Вие не може да редактирате тази страница.", - "addon.mod_wiki.createpage": "Създаване на страница", - "addon.mod_wiki.editingpage": "Редактиране на страница \"{{$a}}\"", - "addon.mod_wiki.errorloadingpage": "Възникна грешка при зареждане на страницата.", - "addon.mod_wiki.map": "Карта", - "addon.mod_wiki.modulenameplural": "Уикита", - "addon.mod_wiki.newpagetitle": "Заглавие на новата страница", - "addon.mod_wiki.notingroup": "Не в група", - "addon.mod_wiki.pagename": "Име на страница", - "addon.mod_wiki.wrongversionlock": "Друг потребител редактира тази страница докато Вие редактирахте и Вашата редакция вече е остаряла.", - "addon.mod_workshop.areainstructauthors": "Инструкции за предаване", - "addon.mod_workshop.areainstructreviewers": "Инструкции за оценяване", - "addon.mod_workshop.assessedsubmission": "Оценени работи", - "addon.mod_workshop.assessmentform": "Формуляр за оценяване", - "addon.mod_workshop.assessmentsettings": "Настройки на оценяването", - "addon.mod_workshop.assessmentweight": "Тегло на иценката", - "addon.mod_workshop.conclusion": "Заключение", - "addon.mod_workshop.createsubmission": "Започване приготвянето на работа", - "addon.mod_workshop.deletesubmission": "Изтриване на работа", - "addon.mod_workshop.editsubmission": "Редактиране на работа", - "addon.mod_workshop.feedbackauthor": "Забележка към автора", - "addon.mod_workshop.gradinggrade": "Оценка за оценяването", - "addon.mod_workshop.modulenameplural": "Работилници", - "addon.mod_workshop.noyoursubmission": "Още не сте предали работата си", - "addon.mod_workshop.overallfeedback": "Обща забележка", - "addon.mod_workshop.publishedsubmissions": "Публикувани работи", - "addon.mod_workshop.publishsubmission": "Публикуване на работа", - "addon.mod_workshop.publishsubmission_help": "Публикуваните работи са достъпни за другите и когато работилницата е затворена.", - "addon.mod_workshop.receivedgrades": "Получени оценки", - "addon.mod_workshop.submissionattachment": "Прикачен файл", - "addon.mod_workshop.submissioncontent": "Съдържание на работата", - "addon.mod_workshop.submissiondeleteconfirm": "Сигурни ли сте, че искате да изтриете следната работа?", - "addon.mod_workshop.submissiongrade": "Оценка за заданието", - "addon.mod_workshop.submissiongradeof": "Оценка за работата (от {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Вие трябва да напишете някакъв текст или да качите файл.", - "addon.mod_workshop.submissionsreport": "Отчет за предадени в работилница работи", - "addon.mod_workshop.submissiontitle": "Заглавие", - "addon.mod_workshop.switchphase10": "Преминаване към фазата на установяване", - "addon.mod_workshop.switchphase20": "Преминаване към фаза за предаване", - "addon.mod_workshop.switchphase30": "Преминаване към фаза оценяване", - "addon.mod_workshop.switchphase40": "Преминаване към фаза поставяне на оценки", - "addon.mod_workshop.switchphase50": "Затваряне на работилницата", - "addon.mod_workshop.userplan": "Планиране на работилница", - "addon.mod_workshop.userplancurrentphase": "Текуща фаза", - "addon.mod_workshop.yourassessment": "Вашата оценка", - "addon.mod_workshop.yoursubmission": "Вашата работа", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Аспект {{$a}}", - "addon.notes.addnewnote": "Добавяне на нова бележка", - "addon.notes.coursenotes": "Бележки за курс", - "addon.notes.deleteconfirm": "Изтриване на бележката?", - "addon.notes.nonotes": "Все още няма бележки от този тип", - "addon.notes.note": "Бележка", - "addon.notes.notes": "Бележки", - "addon.notes.personalnotes": "Лични бележки", - "addon.notes.publishstate": "Контекст", - "addon.notes.sitenotes": "Бележки за сайта", - "addon.notifications.errorgetnotifications": "Грешка при получаването на уведомленията", - "addon.notifications.markallread": "Отбелязване всички прочетени", - "addon.notifications.notificationpreferences": "Предпочитания за уведомленията", - "addon.notifications.notifications": "Уведомления", - "addon.notifications.therearentnotificationsyet": "Няма уведомления.", - "addon.storagemanager.info": "Файловете запазени във вашето устройство помагат на приложението да работи по-бързо и да може да се използва офлайн, но можете и безопасно да ги изтриете, ако е необходимо да освободите място.", - "addon.storagemanager.managestorage": "Управляване на съхранението.", - "addon.storagemanager.storageused": "Използвани файлове за съхранение:", - "assets.countries.AD": "Андора", - "assets.countries.AE": "Обединени Арабски Емирства", - "assets.countries.AF": "Афганистан", - "assets.countries.AG": "Антигуа и Барбуда\n", - "assets.countries.AI": "Ангуила\n", - "assets.countries.AL": "Албания", - "assets.countries.AM": "Армения", - "assets.countries.AO": "Ангола", - "assets.countries.AQ": "Антарктика\n", - "assets.countries.AR": "Аржентина", - "assets.countries.AS": "Самоа\n", - "assets.countries.AT": "Австрия", - "assets.countries.AU": "Австралия", - "assets.countries.AW": "Аруба", - "assets.countries.AX": "Оландски острови", - "assets.countries.AZ": "Азербайджан\n", - "assets.countries.BA": "Босна и Херцеговина", - "assets.countries.BB": "Барбадос", - "assets.countries.BD": "Бангладеш", - "assets.countries.BE": "Белгия", - "assets.countries.BF": "Буркина Фасо", - "assets.countries.BG": "България", - "assets.countries.BH": "Бахрейн", - "assets.countries.BI": "Бурунди", - "assets.countries.BJ": "Бенин\n", - "assets.countries.BL": "Сейнт Бартс", - "assets.countries.BM": "Бермудски острови", - "assets.countries.BN": "Бруней", - "assets.countries.BO": "Боливия", - "assets.countries.BR": "Бразилия", - "assets.countries.BS": "Бахамски острови", - "assets.countries.BT": "Бутан", - "assets.countries.BV": "Остров Буве", - "assets.countries.BW": "Ботсвана", - "assets.countries.BY": "Беларус", - "assets.countries.BZ": "Белиз", - "assets.countries.CA": "Канада", - "assets.countries.CC": "Кокосови острови (Острови Кийлинг)", - "assets.countries.CD": "Демократична република Конго", - "assets.countries.CF": "Централноафриканска република", - "assets.countries.CG": "Конго", - "assets.countries.CH": "Швейцария", - "assets.countries.CI": "Кот Д`Ивоар", - "assets.countries.CK": "Острови Кук", - "assets.countries.CL": "Чили", - "assets.countries.CM": "Камерун", - "assets.countries.CN": "Китай", - "assets.countries.CO": "Колумбия", - "assets.countries.CR": "Коста Рика", - "assets.countries.CU": "Куба", - "assets.countries.CV": "Кабо Верде", - "assets.countries.CX": "Остров Рождество", - "assets.countries.CY": "Кипър", - "assets.countries.CZ": "Чешка Република", - "assets.countries.DE": "Германия", - "assets.countries.DJ": "Джибути", - "assets.countries.DK": "Дания", - "assets.countries.DM": "Доминика", - "assets.countries.DO": "Доминиканска република", - "assets.countries.DZ": "Алжир", - "assets.countries.EC": "Еквадор", - "assets.countries.EE": "Естония", - "assets.countries.EG": "Египет", - "assets.countries.EH": "Западна Сахара", - "assets.countries.ER": "Еритрея", - "assets.countries.ES": "Испания", - "assets.countries.ET": "Етиопия", - "assets.countries.FI": "Финландия", - "assets.countries.FJ": "Фиджи", - "assets.countries.FK": "Фолкландски острови ", - "assets.countries.FM": "Микронезия", - "assets.countries.FO": "Ферьорски острови", - "assets.countries.FR": "Франция", - "assets.countries.GA": "Габон", - "assets.countries.GB": "Великобритания", - "assets.countries.GD": "Гренада", - "assets.countries.GE": "Грузия", - "assets.countries.GF": "Френска Гаяна", - "assets.countries.GG": "Остров Гърнси", - "assets.countries.GH": "Гана", - "assets.countries.GI": "Гибралтар", - "assets.countries.GL": "Гренландия", - "assets.countries.GM": "Гамбия", - "assets.countries.GN": "Гвинея", - "assets.countries.GP": "Гваделупа", - "assets.countries.GQ": "Екваториална Гвинея", - "assets.countries.GR": "Гърция", - "assets.countries.GS": "Южна Джорджия и Южни Сандвичеви острови", - "assets.countries.GT": "Гватемала", - "assets.countries.GU": "Гуам", - "assets.countries.GW": "Гвинея-Бисау", - "assets.countries.GY": "Гаяна\n", - "assets.countries.HK": "Хонг Конг", - "assets.countries.HM": "Острови Хърд и Макдоналд", - "assets.countries.HN": "Хондурас", - "assets.countries.HR": "Хърватия", - "assets.countries.HT": "Хаити", - "assets.countries.HU": "Унгария", - "assets.countries.ID": "Индонезия", - "assets.countries.IE": "Ирландия", - "assets.countries.IL": "Израел", - "assets.countries.IM": "Остров Ман", - "assets.countries.IN": "Индия", - "assets.countries.IO": "Британска индоокеанска територия (Архипелаг Чагос)", - "assets.countries.IQ": "Ирак", - "assets.countries.IR": "Иран", - "assets.countries.IS": "Исландия", - "assets.countries.IT": "Италия", - "assets.countries.JE": "Остров Джърси", - "assets.countries.JM": "Ямайка", - "assets.countries.JO": "Йордания", - "assets.countries.JP": "Япония", - "assets.countries.KE": "Кения", - "assets.countries.KG": "Киргизстан", - "assets.countries.KH": "Камбоджа\n", - "assets.countries.KI": "Република Кирибати", - "assets.countries.KM": "Коморски острови", - "assets.countries.KN": "Сейнт Китс и Невис", - "assets.countries.KP": "Корейска народно-демократична република", - "assets.countries.KR": "Република Корея", - "assets.countries.KW": "Кувейт", - "assets.countries.KY": "Кайманови острови", - "assets.countries.KZ": "Казахстан\n", - "assets.countries.LA": "Лаос", - "assets.countries.LB": "Ливан", - "assets.countries.LC": "Сейнт Лусия", - "assets.countries.LI": "Лихтенщайн", - "assets.countries.LK": "Шри Ланка", - "assets.countries.LR": "Република Либерия", - "assets.countries.LS": "Лесото", - "assets.countries.LT": "Литва", - "assets.countries.LU": "Люксембург", - "assets.countries.LV": "Латвия", - "assets.countries.LY": "Либия", - "assets.countries.MA": "Мароко", - "assets.countries.MC": "Монако", - "assets.countries.MD": "Молдова", - "assets.countries.ME": "Черна гора", - "assets.countries.MF": "Сен Мартен / Синт Мартин", - "assets.countries.MG": "Мадагаскар", - "assets.countries.MH": "Маршалови острови", - "assets.countries.MK": "Македония", - "assets.countries.ML": "Мали", - "assets.countries.MM": "Мианмар (Бурма)", - "assets.countries.MN": "Монголия", - "assets.countries.MO": "Макао", - "assets.countries.MP": "Северни Мариански острови", - "assets.countries.MQ": "Мартиника", - "assets.countries.MR": "Мавритания", - "assets.countries.MS": "Монсерат", - "assets.countries.MT": "Малта", - "assets.countries.MU": "Република Мавриций", - "assets.countries.MV": "Малдиви", - "assets.countries.MW": "Малави", - "assets.countries.MX": "Мексико", - "assets.countries.MY": "Малайзия", - "assets.countries.MZ": "Мозамбик", - "assets.countries.NA": "Намибия", - "assets.countries.NC": "Нова Каледония", - "assets.countries.NE": "Нигер", - "assets.countries.NF": "Остров Норфолк", - "assets.countries.NG": "Нигерия", - "assets.countries.NI": "Никарагуа", - "assets.countries.NL": "Холандия", - "assets.countries.NO": "Норвегия", - "assets.countries.NP": "Непал", - "assets.countries.NR": "Науру", - "assets.countries.NU": "Ниуе", - "assets.countries.NZ": "Нова Зеландия", - "assets.countries.OM": "Оман", - "assets.countries.PA": "Панама", - "assets.countries.PE": "Перу", - "assets.countries.PF": "Френска Полинезия", - "assets.countries.PG": "Папуа Нова Гвинея", - "assets.countries.PH": "Филипини", - "assets.countries.PK": "Пакистан", - "assets.countries.PL": "Полша", - "assets.countries.PM": "Сен Пиер и Микелон", - "assets.countries.PN": "Острови Питкерн", - "assets.countries.PR": "Пуерто Рико", - "assets.countries.PS": "Палестина", - "assets.countries.PT": "Португалия", - "assets.countries.PW": "Палау", - "assets.countries.PY": "Парагвай", - "assets.countries.QA": "Катар", - "assets.countries.RE": "Остров Реюнион", - "assets.countries.RO": "Румъния", - "assets.countries.RS": "Сърбия", - "assets.countries.RU": "Русия", - "assets.countries.RW": "Руанда", - "assets.countries.SA": "Саудитска Арабия", - "assets.countries.SB": "Соломонови острови", - "assets.countries.SC": "Сейшелски острови", - "assets.countries.SD": "Судан", - "assets.countries.SE": "Швеция", - "assets.countries.SG": "Сингапур", - "assets.countries.SH": "Света Елена", - "assets.countries.SI": "Словения", - "assets.countries.SJ": "Свалбард и Ян Майен", - "assets.countries.SK": "Словакия", - "assets.countries.SL": "Сиера Леоне", - "assets.countries.SM": "Сан Марино", - "assets.countries.SN": "Сенегал", - "assets.countries.SO": "Сомалия", - "assets.countries.SR": "Суринам", - "assets.countries.ST": "Сао Томе и Принсипи", - "assets.countries.SV": "Салвадор", - "assets.countries.SY": "Сирия", - "assets.countries.SZ": "Свазиленд", - "assets.countries.TC": "Търкс и Кайкос", - "assets.countries.TD": "Чад", - "assets.countries.TF": "Френски южни територии", - "assets.countries.TG": "Того", - "assets.countries.TH": "Тайланд", - "assets.countries.TJ": "Таджикистан", - "assets.countries.TK": "Токелау", - "assets.countries.TL": "Източен Тимор", - "assets.countries.TM": "Тюркменистан", - "assets.countries.TN": "Тунис", - "assets.countries.TO": "Тонга\n", - "assets.countries.TR": "Турция", - "assets.countries.TT": "Тринидад и Тобаго", - "assets.countries.TV": "Тувалу (Елис)", - "assets.countries.TW": "Тайван", - "assets.countries.TZ": "Танзания", - "assets.countries.UA": "Украйна", - "assets.countries.UG": "Уганда", - "assets.countries.UM": "Американски малки островни територии", - "assets.countries.US": "Съединени Американски Щати", - "assets.countries.UY": "Уругвай", - "assets.countries.UZ": "Узбекистан", - "assets.countries.VA": "Ватикана", - "assets.countries.VC": "Сейнт Винсент и Гренадини", - "assets.countries.VE": "Венецуела", - "assets.countries.VG": "Британски Вирджински острови", - "assets.countries.VI": "Американски Вирджински острови", - "assets.countries.VN": "Виетнам", - "assets.countries.VU": "Вануату ", - "assets.countries.WF": "Уолис и Футуна ", - "assets.countries.WS": "Самоа", - "assets.countries.YE": "Йемен", - "assets.countries.YT": "Майот\n", - "assets.countries.ZA": "Южна Африка", - "assets.countries.ZM": "Замбия", - "assets.countries.ZW": "Зимбабве", - "assets.mimetypes.application/msword": "Word документ", - "assets.mimetypes.application/pdf": "PDF документ", - "assets.mimetypes.application/vnd.ms-excel": "Excel таблица", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint презентация", - "assets.mimetypes.archive": "Архив ({{$a.EXT}})", - "assets.mimetypes.document/unknown": "файл", - "assets.mimetypes.text/plain": "текстов файл", - "assets.mimetypes.text/rtf": "RTF документ", - "core.accounts": "Потребителски регистрации", - "core.add": "Добавяне", - "core.agelocationverification": "Установяване на възраст и страна", - "core.ago": "Преди {{$a}} ", - "core.all": "Всички", - "core.allgroups": "Всички групи", - "core.allparticipants": "Всички участници", - "core.answer": "Отговор", - "core.answered": "Отговорен", - "core.areyousure": "Сигурни ли сте?", - "core.back": "Обратно", - "core.block.blocks": "Блокове", - "core.cancel": "Отказване", - "core.cannotconnect": "Не става свързване: Проверете дали написахте правилно адреса и дали сайтът използва Moodle версия {{$a}} или по-нова.", - "core.cannotdownloadfiles": "Изтеглянето на файлове не е активирано. Свържете се с администратора на сайта.", - "core.category": "Категория", - "core.choose": "Изберете", - "core.choosedots": "Изберете...", - "core.clearsearch": "Изчисти търсенето", - "core.clicktohideshow": "Кликнете за да разгънете или свиете ", - "core.close": "Затваряне", - "core.comments": "Коментари", - "core.comments.addcomment": "Добавяне на коментар ...", - "core.comments.comments": "Коментари", - "core.comments.nocomments": "Няма коментари", - "core.comments.savecomment": "Записване на коментара", - "core.completion-alt-auto-fail": "Завършили: {{$a}} (не са постигнали оценка за завършване)", - "core.completion-alt-auto-n": "Не са завършили: {{$a}}", - "core.completion-alt-auto-n-override": "Не са завършили: {{$a.modname}} (отбелязал {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Завършили: {{$a}} (постигнали оценка за завършване)", - "core.completion-alt-auto-y": "Завършен: {{$a}}", - "core.completion-alt-auto-y-override": "Завършили: {{$a.modname}} (отбелязал {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Не е завършена дейността: {{$a}}. Изберете я за да я отбележите за завършена.", - "core.completion-alt-manual-n-override": "Не са завършили: {{$a.modname}} (отбелязал {{$a.overrideuser}}).\nИзберете за да отбележите като завършени.", - "core.completion-alt-manual-y": "Завършена е дейността: {{$a}}. Изберете я за да я отбележите за незавършена.", - "core.completion-alt-manual-y-override": "Завършили: {{$a.modname}} (отбелязал {{$a.overrideuser}}).\nИзберете за да отбележите като незавършени.", - "core.confirmdeletefile": "Сигурни ли сте, че искате да изтриете този файл?", - "core.considereddigitalminor": "Много сте малки за да се регистрирате на този сайт.", - "core.content": "Съдържание", - "core.continue": "Продължаване", - "core.course": "Курс", - "core.course.allsections": "Всички секции", - "core.course.contents": "Съдържание", - "core.course.couldnotloadsectioncontent": "Неуспех при зареждането на съдържанието на секцията, моля опитайте пак по-късно.", - "core.course.couldnotloadsections": "Неуспех при зареждането на секциите, моля опитайте отново.", - "core.course.coursesummary": "Резюме на курса", - "core.course.downloadcourse": "Изтегляне на курса", - "core.course.errordownloadingcourse": "Грешка при изтегляне на курс.", - "core.course.errordownloadingsection": "Грешка при изтегляне на секция.", - "core.course.hiddenfromstudents": "Скрит от ученици", - "core.course.insufficientavailablequota": "Вашето устройство не може да намери място, за да запази това изтегляне. Може би е използвало място за обновяване на приложенията и системата си. Моля, освободете място преди да опитате пак.", - "core.course.insufficientavailablespace": "Вие се опитвате да изтеглите {{size}}. Това няма да остави свободно място за нормална работа на устройството. Моля, освободете място преди да опитате пак.", - "core.course.nocontentavailable": "В момента няма достъпно съдържание.", - "core.course.refreshcourse": "Опресняване на курса", - "core.course.sections": "Секции", - "core.coursedetails": "Информация за курсове", - "core.courses.addtofavourites": "Отбележи този курс", - "core.courses.allowguests": "В този курс могат да влизат гости", - "core.courses.availablecourses": "Налични курсове", - "core.courses.categories": "Категории курсове", - "core.courses.courses": "Курсове", - "core.courses.errorloadcategories": "Възникна грешка докато се зареждаха категории.", - "core.courses.errorloadcourses": "Грешка при зареждането на курсовете.", - "core.courses.frontpage": "Начална страница", - "core.courses.hidecourse": "Премахване от изгеда", - "core.courses.ignore": "Игнорирай", - "core.courses.mycourses": "Моите курсове", - "core.courses.mymoodle": "Моето табло", - "core.courses.nocourses": "Няма информация за курса, която да бъде показана.", - "core.courses.nocoursesyet": "Няма курсове в тази категория", - "core.courses.nosearchresults": "Няма резултати", - "core.courses.notenroled": "Вие не сте записани в този курс", - "core.courses.reload": "Презареждане", - "core.courses.removefromfavourites": "Не показвай този курс като отбелязан", - "core.courses.search": "Търсене", - "core.courses.searchcourses": "Търсене на курсове", - "core.courses.show": "Връщане в изглед", - "core.date": "Дата", - "core.day": "ден", - "core.days": "дни", - "core.decsep": ",", - "core.defaultvalue": "По подразбиране ({{$a}})", - "core.delete": "Изтриване", - "core.description": "Описание", - "core.digitalminor_desc": "Моля, поискайте Ваш родител да се свърже с:", - "core.displayoptions": "Настройки на показването", - "core.done": "Извършено", - "core.download": "Изтегляне", - "core.downloading": "Изтегляне", - "core.edit": "Редактиране", - "core.editor.autosavesucceeded": "Запазена е чернова.", - "core.editor.bold": "Почернен", - "core.editor.clear": "Премахване на формата", - "core.editor.h3": "Заглавие (голямо)", - "core.editor.h4": "Заглавие (средно)", - "core.editor.h5": "Заглавие (малки)", - "core.editor.italic": "Курсив", - "core.editor.orderedlist": "Номериран списък", - "core.editor.p": "Абзац", - "core.editor.strike": "Зачертаване", - "core.editor.underline": "Подчертаване", - "core.editor.unorderedlist": "Неномериран списък", - "core.emptysplit": "Тази страница ще изглежда празна, ако левият панел е празен, или се зарежда.", - "core.error": "Грешка", - "core.errordownloading": "Грешка при теглене на файл", - "core.errordownloadingsomefiles": "Грешка при изтегляне на файлове. Някои файлове ще липсват.", - "core.errorloadingcontent": "Грешка при зареждане на съдържание.", - "core.explanationdigitalminor": "Тази информация е необходима за да определим дали сте в пълнолетна възраст. Това е възрастта на която хората могат да се съгласяват с изискванията и условията, при които техните лични данни да бъдат съхраняване и обработвани.", - "core.favourites": "Отбелязани", - "core.filename": "Име на файл", - "core.filenotfound": "За съжаление, файлът не е намерен.", - "core.fileuploader.audio": "Аудио", - "core.fileuploader.camera": "Камера", - "core.fileuploader.confirmuploadfile": "Предстои Ви да качите {{size}}. Искате ли да продължите?", - "core.fileuploader.errorcapturingaudio": "Грешка при прихващане на звук", - "core.fileuploader.errorcapturingimage": "Грешка при снимане на изображение", - "core.fileuploader.errorcapturingvideo": "Грешка при записване на видео", - "core.fileuploader.errorgettingimagealbum": "Грешка при вземането на изображението от аблума.", - "core.fileuploader.errormustbeonlinetoupload": "Трябва да сте онлайн, за да качвате файлове.", - "core.fileuploader.errornoapp": "Нямате инсталирано приложение, което да извърши това действие.", - "core.fileuploader.errorreadingfile": "Грешка при четенето на файла.", - "core.fileuploader.errorwhileuploading": "Възникна грешка при качването на файла.", - "core.fileuploader.file": "Файл", - "core.fileuploader.filesofthesetypes": "Допустими файлови типове:", - "core.fileuploader.fileuploaded": "Файлът е качен", - "core.fileuploader.more": "Още", - "core.fileuploader.photoalbums": "Фотоалбуми", - "core.fileuploader.readingfile": "Четене на файл", - "core.fileuploader.uploadafile": "Качване на файл", - "core.fileuploader.uploading": "Качване", - "core.fileuploader.uploadingperc": "Качване: {{$a}}%", - "core.fileuploader.video": "Видео", - "core.filter": "Филтър", - "core.folder": "Папка", - "core.forcepasswordchangenotice": "Трябва да промените Вашата парола, преди да продължите", - "core.fulllistofcourses": "Всички курсове", - "core.grades.average": "Средно", - "core.grades.contributiontocoursetotal": "Дял в оценката за курса", - "core.grades.feedback": "Забележка", - "core.grades.grade": "Оценка", - "core.grades.gradeitem": "Оценка за", - "core.grades.grades": "Оценки", - "core.grades.percentage": "Проценти", - "core.grades.range": "Диапазон", - "core.group": "Група", - "core.groupsseparate": "Отделни групи", - "core.groupsvisible": "Видими групи", - "core.help": "Помощ", - "core.hide": "Да не се вижда", - "core.hour": "час", - "core.hours": "часа", - "core.info": "Информация", - "core.labelsep": ":", - "core.lastaccess": "Последен достъп", - "core.lastmodified": "Последно модифициране", - "core.layoutgrid": "Мрежа", - "core.list": "Списък", - "core.listsep": ";", - "core.loading": "Зареждане", - "core.location": "Местоположение", - "core.login.auth_email": "Саморегистриране по имейл", - "core.login.authenticating": "Удостоверяване", - "core.login.cancel": "Отказване", - "core.login.changepassword": "Смяна на паролата", - "core.login.connect": "Свързване!", - "core.login.connecttomoodle": "Свързване към Moodle", - "core.login.createaccount": "Създаване на моята регистрация", - "core.login.createuserandpass": "Изберете Вашето име и парола", - "core.login.emailconfirmsent": "

Трябва вече да е изпратен имейл на вашия адрес {{$a}}

Той съдържа лесни инструкции как да завършите регистрацията си.

Ако продължите да срещате трудности, се обърнете към администратора на сайта.

", - "core.login.firsttime": "За първи път ли сте тук?", - "core.login.forcepasswordchangenotice": "Трябва да промените Вашата парола, преди да продължите", - "core.login.forgotten": "Забравени потребителско име или парола?", - "core.login.help": "Помощ", - "core.login.helpmelogin": "

Има много хиляди сайтове със система Moodle по света. Това приложение може да Ви свърже само със сайтове, които специално разрешават достъп с мобилно приложение.

Ако не можете да се свържете със своя сайт, трябва да се обърнете към администратор на Moodle системата на сайта, и да го помолите да прочете информацията от адрес: http://docs.moodle.org/en/Mobile_app

За да пробвате приложението с демонстрационен сайт, напишете teacher или student в полето Адрес на сайта и щракнете Бутон за добавяне.

", - "core.login.instructions": "Инструкции", - "core.login.invalidaccount": "Моля проверете своите данни за влизане или помолете администратор да провери конфигурацията на сайта.", - "core.login.invaliddate": "Невалидна дата", - "core.login.invalidemail": "Невалиден имейл адрес", - "core.login.invalidmoodleversion": "Невалидна версия на Moodle. Най-старата изискване версия е {{$a}}.", - "core.login.invalidsite": "Интернет адресът на сайта е невалиден.", - "core.login.login": "Вход", - "core.login.loginbutton": "Вход!", - "core.login.logininsiterequired": "Трябва да влезете в сайта през прозорец на браузър.", - "core.login.loginsteps": "За пълен достъп до този сайт трябва първо да си създадете профил.", - "core.login.missingemail": "Липсва имейл адрес", - "core.login.missingfirstname": "Даденото име липсва", - "core.login.missinglastname": "Липсва фамилия", - "core.login.mobileservicesnotenabled": "Услугите от Moodle не са активирани на Вашия сайт. Моля, свържете се с администратор на Moodle, ако мислите, че мобилния достъп трябва да бъде разрешен.", - "core.login.mustconfirm": "Трябва да потвърдите своето влизане", - "core.login.newaccount": "Нова потребителска регистрация", - "core.login.notloggedin": "Трябва са сте влезли в сайта.", - "core.login.password": "Парола", - "core.login.passwordforgotten": "Забравена парола", - "core.login.passwordforgotteninstructions2": "За да промените паролата си, въведете по-долу Вашето потребителско име или имейл адрес. Ако Ви открием в базата данни, ще Ви бъде изпратен имейл с инструкции как да получите достъп отново.", - "core.login.passwordrequired": "Паролата е задължителна", - "core.login.policyaccept": "Разбирам и съм съгласен", - "core.login.policyagree": "Трябва да се съгласите с тези условия за ползване, за да продължите да ползвате сайта. Съгласен/на ли сте?", - "core.login.policyagreement": "Условия за ползване на сайта", - "core.login.policyagreementclick": "Препратка към условията за ползване на сайта", - "core.login.potentialidps": "Влизане с вашия профил от:", - "core.login.profileinvaliddata": "Невалидна стойност", - "core.login.reconnect": "Повторно свързване", - "core.login.reconnectdescription": "Вашият ключ за идентификация не е валиден или е изтекъл. Трябва да се свържете със сайта отново.", - "core.login.reconnectssodescription": "Вашият ключ за идентификация не е валиден или е изтекъл. Трябва да се свържете със сайта отново - да влезете в сайта през прозорец в браузър.", - "core.login.security_question": "Въпрос за сигурност", - "core.login.selectacountry": "Държава", - "core.login.siteaddress": "Адрес на сайта", - "core.login.siteinmaintenance": "Вашият сайт е в режим на поддръжка", - "core.login.siteurl": "Адрес на сайта", - "core.login.siteurlrequired": "Изисква се адрес на сайта. Например: http://www.yourmoodlesite.abc или https://www.yourmoodlesite.efg", - "core.login.startsignup": "Създаване на нова регистрация", - "core.login.supplyinfo": "Още данни", - "core.login.username": "Потребителско име", - "core.login.usernameoremail": "Въведете потребителско име или имейл адрес.", - "core.login.usernamerequired": "Потребителско име се изисква", - "core.login.webservicesnotenabled": "Интернет услугите на са активирани на Вашия сайт. Моля, свържете се с администратор на Moodle сайта, ако мислите, че мобилният достъп трябва да е позволен.", - "core.lostconnection": "Изгубихме връзка и трябва да се свържете отново. Вашият ключ сега е невалиден.", - "core.mainmenu.changesite": "Смяна на сайта", - "core.mainmenu.help": "Помощ", - "core.mainmenu.logout": "Изход", - "core.mainmenu.website": "Уебсайт", - "core.maxsizeandattachments": "Максимален размер за нови файлове: {{$a.size}}, максимален брой файлове: {{$a.attachments}}", - "core.min": "мин", - "core.mins": "мин.", - "core.misc": "Разни", - "core.mod_assign": "Задание", - "core.mod_assignment": "Задание (2.2) (деактивирано)", - "core.mod_book": "Книга", - "core.mod_chat": "Чат", - "core.mod_choice": "Избор", - "core.mod_data": "База данни", - "core.mod_database": "База данни", - "core.mod_feedback": "Обратна връзка", - "core.mod_file": "Файл", - "core.mod_folder": "Папка", - "core.mod_forum": "Форум", - "core.mod_glossary": "Речник", - "core.mod_ims": "IMS пакет", - "core.mod_imscp": "IMS пакет", - "core.mod_label": "Етикет", - "core.mod_lesson": "Урок", - "core.mod_page": "Страница", - "core.mod_quiz": "Тест", - "core.mod_resource": "Файл", - "core.mod_scorm": "SCORM пакет", - "core.mod_survey": "Анкета", - "core.mod_url": "URL (хипервръзка)", - "core.mod_wiki": "Уики", - "core.mod_workshop": "Работилница", - "core.moduleintro": "Описание", - "core.more": "още", - "core.mygroups": "Моите групи", - "core.name": "Име", - "core.networkerrormsg": "Мрежата не е разрешена или не работи", - "core.never": "Никога", - "core.next": "Още", - "core.no": "Не", - "core.nocomments": "Няма коментари", - "core.nograde": "Без оценка", - "core.none": "Нищо", - "core.nopermissions": "За съжаление Вие нямате право да правите това ({{$a}})", - "core.noresults": "Няма резултати", - "core.noselection": "Няма избрани", - "core.notapplicable": "няма", - "core.notenrolledprofile": "Профилът не е достъпен, защото този потребител не е включен в този курс", - "core.notice": "Съобщене", - "core.notingroup": "Съжаляваме, но трябва да участвате в група за да видите тази страница.", - "core.now": "сега", - "core.numwords": "{{$a}} думи", - "core.offline": "Офлайн", - "core.online": "Онлайн", - "core.openinbrowser": "Отваряне в браузър", - "core.othergroups": "Други групи", - "core.phone": "Телефон", - "core.pictureof": "Снимка на {{$a}}", - "core.previous": "Обратно", - "core.proceed": "Продължаване", - "core.pulltorefresh": "", - "core.question.answer": "Отговор", - "core.question.answersaved": "Отговорът съхранен", - "core.question.certainty": "Увереност", - "core.question.complete": "Отговорен", - "core.question.correct": "Правилно", - "core.question.feedback": "Забележка", - "core.question.incorrect": "Неправилно", - "core.question.information": "Информация", - "core.question.invalidanswer": "Непълен отговор", - "core.question.notanswered": "Не е отговорен", - "core.question.notyetanswered": "Все още не е даден отговор", - "core.question.partiallycorrect": "Отчасти верен", - "core.question.questionno": "Въпрос {{$a}}", - "core.question.requiresgrading": "Изисква оценяване", - "core.rating.aggregateavg": "Среден рейтинг", - "core.rating.aggregatecount": "Брой рейтинги", - "core.rating.aggregatemax": "Максимален", - "core.rating.aggregatemin": "Минимален", - "core.rating.aggregatesum": "Сума от рейтинги", - "core.rating.noratings": "Не са определени рейтинги", - "core.rating.ratings": "Рейтинги", - "core.refresh": "Опресняване", - "core.remove": "Премахване", - "core.required": "Задължително", - "core.resourcedisplayopen": "Отваряне", - "core.resources": "Ресурси", - "core.restore": "Възстановяване", - "core.restricted": "Ограничено", - "core.save": "Запазване", - "core.savechanges": "Записване на промените", - "core.search": "Търсене", - "core.searchresults": "Резултати от търсенето", - "core.sec": "сек.", - "core.secs": "сек.", - "core.seemoredetail": "Щракнете тук за повече подробности", - "core.selectacategory": "Моля, изберете категория", - "core.selectacourse": "Изберете курс", - "core.send": "Изпращане", - "core.sending": "Изпраща се", - "core.settings.about": "Относно", - "core.settings.currentlanguage": "Текущ език", - "core.settings.debugdisplay": "Показване на debug съобщения", - "core.settings.deletesitefiles": "Сигурни ли сте, че искате да изтриете изтеглените файлове и запазените данни от сайт \"{{sitename}}\"? Няма да можете да използвате приложението в офлайн режим.", - "core.settings.disableall": "Забраняване уведомления", - "core.settings.errordeletesitefiles": "Грешка при изтриването на файловете на сайта.", - "core.settings.errorsyncsite": "Грешка при синхронизацията на данните от сайта. Моля проверете Вашата връзка към Интернет и опитайте пак.", - "core.settings.estimatedfreespace": "Пресметнато свободно пространство", - "core.settings.general": "Общо", - "core.settings.language": "Език", - "core.settings.license": "Лиценз", - "core.settings.locked": "Заключено", - "core.settings.loggedin": "Онлайн", - "core.settings.loggedoff": "Офлайн", - "core.settings.preferences": "Предпочитания", - "core.settings.settings": "Настройки", - "core.settings.showdownloadoptions": "Показване възможности за изтегляне", - "core.settings.spaceusage": "Използване на пространство", - "core.settings.synchronization": "Синхронизиране", - "core.settings.total": "Общо", - "core.sharedfiles.successstorefile": "Файлът е запазен успешно. Изберете да го качите във Вашите Лични файлове или използвате в някоя дейност.", - "core.show": "Да се вижда", - "core.showless": "Показване по-малко...", - "core.showmore": "Показване повече...", - "core.site": "Сайт", - "core.sitehome.sitehome": "Начална страница", - "core.sitehome.sitenews": "Новини от сайта", - "core.sitemaintenance": "Сайтът е в режим на поддръжка и временно не е достъпен", - "core.sizeb": "байта", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "МБ", - "core.sizetb": "ТБ", - "core.skip": "Прескачане", - "core.sort": "Подреждане", - "core.sortby": "Нареждане по", - "core.strftimedate": "%d %B %Y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "Напред", - "core.success": "Успешно", - "core.tag.noresultsfor": "Няма резултати за \"{{$a}}\"", - "core.tag.searchtags": "Търсене в етикети", - "core.tag.tags": "Етикети", - "core.teachers": "Преподаватели", - "core.thisdirection": "ltr", - "core.time": "Време", - "core.timesup": "Времето изтече!", - "core.today": "Днес", - "core.unexpectederror": "Неочаквана грешка. Моля, затворете мобилното приложение и го отворете пак, за да опитате отново.", - "core.unlimited": "Неограничен", - "core.user": "Потребител", - "core.user.address": "Адрес", - "core.user.city": "Град/село", - "core.user.contact": "Контакти", - "core.user.country": "Държава", - "core.user.description": "Описание", - "core.user.details": "Подробности", - "core.user.detailsnotavailable": "Детайлите за този потребител не са достъпни за Вас.", - "core.user.editingteacher": "Преподавател", - "core.user.email": "Имейл адрес", - "core.user.emailagain": "Имейл адрес (отново)", - "core.user.errorloaduser": "Грешка при зареждане на потребител.", - "core.user.firstname": "Име", - "core.user.interests": "Интереси", - "core.user.lastname": "Фамилия", - "core.user.manager": "Мениджър", - "core.user.newpicture": "Нова снимка", - "core.user.noparticipants": "Няма намерени участници за този курс", - "core.user.participants": "Участници", - "core.user.phone1": "Телефон", - "core.user.phone2": "Мобилен телефон", - "core.user.roles": "Роли", - "core.user.student": "Студент", - "core.user.teacher": "Не-редактиращ учител", - "core.user.webpage": "Уеб страница", - "core.userdeleted": "Тази потребителска регистрация е изтрита", - "core.userdetails": "Информация за потребителя", - "core.users": "Потребители", - "core.view": "Преглед", - "core.viewprofile": "Разглеждане на профила", - "core.whatisyourage": "Каква е Вашата възраст?", - "core.wheredoyoulive": "В коя страна живеете?", - "core.whyisthisrequired": "Защо е нужно това?", - "core.years": "години", - "core.yes": "Да" -} \ No newline at end of file diff --git a/src/assets/lang/ca.json b/src/assets/lang/ca.json deleted file mode 100644 index 3435c8f70..000000000 --- a/src/assets/lang/ca.json +++ /dev/null @@ -1,2176 +0,0 @@ -{ - "addon.badges.alignment": "Competència", - "addon.badges.badgedetails": "Detalls de la insígnia", - "addon.badges.badges": "Insígnies", - "addon.badges.bendorsement": "Suport", - "addon.badges.claimcomment": "Comentari de suport", - "addon.badges.claimid": "URL de reclamació", - "addon.badges.contact": "Contacte", - "addon.badges.dateawarded": "Data publicada", - "addon.badges.expired": "Ha caducat", - "addon.badges.expirydate": "Data d'expiració", - "addon.badges.imageauthoremail": "Correu de l'autor de la imatge", - "addon.badges.imageauthorname": "Nom de l'autor de la imatge", - "addon.badges.imageauthorurl": "URL de l'autor de la imatge", - "addon.badges.imagecaption": "Títol de la imatge", - "addon.badges.issuancedetails": "Expiració de la insígnia", - "addon.badges.issuerdetails": "Detalls de l'atorgador", - "addon.badges.issueremail": "Correu de l'atorgador", - "addon.badges.issuername": "Nom de l'atorgador", - "addon.badges.issuerurl": "URL de l'atorgador", - "addon.badges.language": "Idioma", - "addon.badges.noalignment": "Aquesta insígnia no té associats cap habilitat externa ni cap estàndard.", - "addon.badges.nobadges": "No hi ha insígnies disponibles.", - "addon.badges.norelated": "Aquesta insígnia no en té cap altra de relacionada.", - "addon.badges.recipientdetails": "Detalls del receptor", - "addon.badges.relatedbages": "Insígnies relacionades", - "addon.badges.version": "Versió", - "addon.badges.warnexpired": "(Aquesta insígnia ha expirat!)", - "addon.block_activitymodules.pluginname": "Activitats", - "addon.block_activityresults.pluginname": "Resultats d'activitat", - "addon.block_badges.pluginname": "Insígnies recents", - "addon.block_blogmenu.pluginname": "Menú de blog", - "addon.block_blogrecent.pluginname": "Entrades recents al blog", - "addon.block_blogtags.pluginname": "Etiquetes del blog", - "addon.block_calendarmonth.pluginname": "Calendari", - "addon.block_calendarupcoming.pluginname": "Esdeveniments propers", - "addon.block_comments.pluginname": "Comentaris", - "addon.block_completionstatus.pluginname": "Estat de compleció del curs", - "addon.block_glossaryrandom.pluginname": "Entrada aleatòria de glossari", - "addon.block_learningplans.pluginname": "Plans d'aprenentatge", - "addon.block_myoverview.all": "Tots (excepte els amagats)", - "addon.block_myoverview.allincludinghidden": "Tots", - "addon.block_myoverview.favourites": "Destacats", - "addon.block_myoverview.future": "Futurs", - "addon.block_myoverview.hiddencourses": "Amagats", - "addon.block_myoverview.inprogress": "Actuals", - "addon.block_myoverview.lastaccessed": "Accedits recentment", - "addon.block_myoverview.morecourses": "Més cursos", - "addon.block_myoverview.nocourses": "Cap curs", - "addon.block_myoverview.past": "Passats", - "addon.block_myoverview.pluginname": "Resum dels cursos", - "addon.block_myoverview.shortname": "Nom curt", - "addon.block_myoverview.title": "Nom del curs", - "addon.block_newsitems.pluginname": "Darrers anuncis", - "addon.block_onlineusers.pluginname": "Usuaris en línia", - "addon.block_privatefiles.pluginname": "Fitxers privats", - "addon.block_recentactivity.pluginname": "Activitat recent", - "addon.block_recentlyaccessedcourses.nocourses": "No hi ha cursos recents", - "addon.block_recentlyaccessedcourses.pluginname": "Cursos visitats recentment", - "addon.block_recentlyaccesseditems.noitems": "No hi ha elements recents", - "addon.block_recentlyaccesseditems.pluginname": "Elements visitats recentment", - "addon.block_rssclient.pluginname": "Canals d'informació RSS remots", - "addon.block_selfcompletion.pluginname": "Compleció automàtica", - "addon.block_sitemainmenu.pluginname": "Menú principal", - "addon.block_starredcourses.nocourses": "No hi ha cursos destacats", - "addon.block_starredcourses.pluginname": "Cursos destacats", - "addon.block_tags.pluginname": "Etiquetes", - "addon.block_timeline.duedate": "Data de venciment", - "addon.block_timeline.next30days": "Propers 30 dies", - "addon.block_timeline.next3months": "Propers 3 mesos", - "addon.block_timeline.next6months": "Propers 6 mesos", - "addon.block_timeline.next7days": "Propers 7 dies", - "addon.block_timeline.nocoursesinprogress": "No hi ha cursos en curs", - "addon.block_timeline.noevents": "No hi ha activitats properes vençudes", - "addon.block_timeline.overdue": "Vençut", - "addon.block_timeline.pluginname": "Cronologia", - "addon.block_timeline.sortbycourses": "Ordena per cursos", - "addon.block_timeline.sortbydates": "Ordena per data", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Entrades del blog", - "addon.blog.errorloadentries": "S'ha produït un error en carregar les entrades del blog.", - "addon.blog.linktooriginalentry": "Enllaça a l'entrada del blog original", - "addon.blog.noentriesyet": "Aquí no hi ha entrades visibles", - "addon.blog.publishtonoone": "Privat (esborrany)", - "addon.blog.publishtosite": "Usuaris del lloc", - "addon.blog.publishtoworld": "Tothom", - "addon.blog.showonlyyourentries": "Mostra només les entrades pròpies", - "addon.blog.siteblogheading": "Blog del lloc", - "addon.calendar.allday": "Tot el dia", - "addon.calendar.calendar": "Calendari", - "addon.calendar.calendarevent": "Esdeveniment de calendari", - "addon.calendar.calendarevents": "Esdeveniments del calendari", - "addon.calendar.calendarreminders": "Recordatoris del calendari", - "addon.calendar.categoryevents": "Esdeveniments de categoria", - "addon.calendar.confirmeventdelete": "Esteu segur que voleu suprimir l'esdeveniment «{{$a}}»?", - "addon.calendar.confirmeventseriesdelete": "L'esdeveniment «{{$a.name}}» és part d'una sèrie. Voleu esborrar només aquest esdeveniment o bé tots els {{$a.count}} de la sèrie?", - "addon.calendar.courseevents": "Esdeveniments del curs", - "addon.calendar.currentmonth": "Mes actual", - "addon.calendar.daynext": "Dia següent", - "addon.calendar.dayprev": "Dia anterior", - "addon.calendar.defaultnotificationtime": "Hora de notificació per defecte", - "addon.calendar.deleteallevents": "Esborra tots els esdeveniments", - "addon.calendar.deleteevent": "Suprimeix esdeveniment", - "addon.calendar.deleteoneevent": "Esborra aquest esdeveniment", - "addon.calendar.durationminutes": "Durada en minuts", - "addon.calendar.durationnone": "Sense durada", - "addon.calendar.durationuntil": "Fins al dia", - "addon.calendar.editevent": "S'està editant l'esdeveniment", - "addon.calendar.errorloadevent": "S'ha produït un error carregant l'esdeveniment.", - "addon.calendar.errorloadevents": "S'ha produït un error carregant els esdeveniments.", - "addon.calendar.eventcalendareventdeleted": "S'ha suprimit un esdeveniment del calendari", - "addon.calendar.eventduration": "Durada", - "addon.calendar.eventendtime": "Final", - "addon.calendar.eventkind": "Tipus d'esdeveniment", - "addon.calendar.eventname": "Títol de l'esdeveniment", - "addon.calendar.eventstarttime": "Data d'inici", - "addon.calendar.eventtype": "Tipus d'esdeveniment", - "addon.calendar.fri": "dv", - "addon.calendar.friday": "divendres", - "addon.calendar.gotoactivity": "Vés a l'activitat", - "addon.calendar.groupevents": "Esdeveniments de grup", - "addon.calendar.invalidtimedurationminutes": "La durada en minuts que heu introduït no és vàlida. Introduïu un valor major que zero o escolliu sense durada.", - "addon.calendar.invalidtimedurationuntil": "La data i hora que heu escollit per a l'hora final és abans de l'hora inicial. Corregiu-ho abans de continuar.", - "addon.calendar.mon": "dl", - "addon.calendar.monday": "dilluns", - "addon.calendar.monthlyview": "Vista mensual", - "addon.calendar.newevent": "Esdeveniment nou", - "addon.calendar.noevents": "No hi ha cap esdeveniment", - "addon.calendar.nopermissiontoupdatecalendar": "No teniu permís per actualitzar l'esdeveniment del calendari.", - "addon.calendar.reminders": "Recordatoris", - "addon.calendar.repeatedevents": "Esdeveniments periòdics", - "addon.calendar.repeateditall": "Aplica els canvis als altres {{$a}} esdeveniments d'aquesta sèrie", - "addon.calendar.repeateditthis": "Aplica els canvis només a aquest esdeveniment", - "addon.calendar.repeatevent": "Repeteix aquest esdeveniment", - "addon.calendar.repeatweeksl": "Nombre de repeticions setmanals", - "addon.calendar.sat": "ds", - "addon.calendar.saturday": "dissabte", - "addon.calendar.setnewreminder": "Estableix un recordatori nou", - "addon.calendar.siteevents": "Esdeveniments del lloc", - "addon.calendar.sun": "dg", - "addon.calendar.sunday": "diumenge", - "addon.calendar.thu": "dj", - "addon.calendar.thursday": "dijous", - "addon.calendar.today": "Avui", - "addon.calendar.tomorrow": "Demà", - "addon.calendar.tue": "dt", - "addon.calendar.tuesday": "dimarts", - "addon.calendar.typecategory": "Esdeveniment de categoria", - "addon.calendar.typeclose": "", - "addon.calendar.typecourse": "Esdeveniment del curs", - "addon.calendar.typedue": "Esdeveniment de venciment", - "addon.calendar.typegradingdue": "Esdeveniment de venciment de qualificació", - "addon.calendar.typegroup": "Esdeveniment de grup", - "addon.calendar.typeopen": "", - "addon.calendar.typesite": "Esdeveniment del lloc", - "addon.calendar.typeuser": "Esdeveniment de l'usuari", - "addon.calendar.upcomingevents": "Esdeveniments propers", - "addon.calendar.userevents": "Esdeveniments de l'usuari", - "addon.calendar.wed": "dc", - "addon.calendar.wednesday": "dimecres", - "addon.calendar.when": "Quan", - "addon.calendar.yesterday": "Ahir", - "addon.competency.activities": "Activitats", - "addon.competency.competencies": "Competències", - "addon.competency.competenciesmostoftennotproficientincourse": "Competències que més sovint no s'assoleixen en aquest curs", - "addon.competency.coursecompetencies": "Competències del curs", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Les qualificacions per competències d'aquest curs no afecten els plans d'aprenentatge.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Les qualificacions per competències d'aquest curs s'actualitzem als plans d'aprenentatge al moment.", - "addon.competency.crossreferencedcompetencies": "Competències referenciades", - "addon.competency.duedate": "Data límit", - "addon.competency.errornocompetenciesfound": "No s'han trobat competències", - "addon.competency.evidence": "Evidència", - "addon.competency.evidence_competencyrule": "La regla de competència s'ha satisfet.", - "addon.competency.evidence_coursecompleted": "S'ha completat el curs '{{$a}}'.", - "addon.competency.evidence_coursemodulecompleted": "S'ha completat l'activitat '{{$a}}'.", - "addon.competency.evidence_courserestored": "La qualificació ha estat restaurada juntament amb el curs «{{$a}}».", - "addon.competency.evidence_evidenceofpriorlearninglinked": "S'ha enllaçat l'evidència de l'aprenentatge previ «{{$a}}».", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "S'ha desenllaçat l'evidència de l'aprenentatge previ «{{$a}}».", - "addon.competency.evidence_manualoverride": "S'ha configurat de forma manual la qualificació de la competència.", - "addon.competency.evidence_manualoverrideincourse": "S'ha configurat de forma manual la qualificació de la competència al curs «{{$a}}».", - "addon.competency.evidence_manualoverrideinplan": "S'ha configurat de forma manual la qualificació de la competència al pla d'aprenentatge «{{$a}}».", - "addon.competency.learningplancompetencies": "Competències del pla d'aprenentatge", - "addon.competency.learningplans": "Plans d'aprenentatge", - "addon.competency.myplans": "Els meus plans d'aprenentatge", - "addon.competency.noactivities": "Cap activitat", - "addon.competency.nocompetencies": "Cap competència", - "addon.competency.nocompetenciesincourse": "No s'han enllaçat competències a aquest curs.", - "addon.competency.nocrossreferencedcompetencies": "No hi ha competències amb referències a aquesta.", - "addon.competency.noevidence": "Cap evidència", - "addon.competency.noplanswerecreated": "No s'ha creat cap pla d'aprenentatge.", - "addon.competency.nouserplanswithcompetency": "Cap pla d'aprenentatge conté aquesta competència.", - "addon.competency.path": "Ruta:", - "addon.competency.planstatusactive": "Actiu", - "addon.competency.planstatuscomplete": "Completat", - "addon.competency.planstatusdraft": "Esborrany", - "addon.competency.planstatusinreview": "En revisió", - "addon.competency.planstatuswaitingforreview": "S'està esperant la revisió", - "addon.competency.proficient": "Superada", - "addon.competency.progress": "Progrés", - "addon.competency.rating": "Qualificació", - "addon.competency.reviewstatus": "Estat de la revisió", - "addon.competency.status": "Estat", - "addon.competency.template": "Plantilla de pla d'aprenentatge", - "addon.competency.uponcoursecompletion": "En finalitzar el curs:", - "addon.competency.usercompetencystatus_idle": "Inactiu", - "addon.competency.usercompetencystatus_inreview": "En revisió", - "addon.competency.usercompetencystatus_waitingforreview": "Esperant a ser revisat", - "addon.competency.userplans": "Plans d'aprenentatge", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} de {{$a.y}} competències superades", - "addon.competency.xcompetenciesproficientoutofyincourse": "Heu superat {{$a.x}} de {{$a.y}} competències en aquest curs.", - "addon.coursecompletion.complete": "Complet", - "addon.coursecompletion.completecourse": "Curs completat", - "addon.coursecompletion.completed": "Completat", - "addon.coursecompletion.completiondate": "Data de compleció", - "addon.coursecompletion.completionmenuitem": "Compleció", - "addon.coursecompletion.couldnotloadreport": "No es pot carregar l'informe de compleció del curs, torneu a intentar-ho més tard.", - "addon.coursecompletion.coursecompletion": "Compleció del curs", - "addon.coursecompletion.criteria": "Criteris", - "addon.coursecompletion.criteriagroup": "Grup de criteris", - "addon.coursecompletion.criteriarequiredall": "Cal que es compleixin tots els criteris que es mostren a continuació", - "addon.coursecompletion.criteriarequiredany": "Cal que es compleixi algun dels criteris que es mostren a continuació", - "addon.coursecompletion.inprogress": "En curs", - "addon.coursecompletion.manualselfcompletion": "Auto-compleció manual", - "addon.coursecompletion.nottracked": "A hores d'ara no s'està realitzant el seguiment de la vostra compleció en aquest curs.", - "addon.coursecompletion.notyetstarted": "No s'ha començat encara", - "addon.coursecompletion.pending": "Pendents", - "addon.coursecompletion.required": "Necessari", - "addon.coursecompletion.requiredcriteria": "Criteri requerit", - "addon.coursecompletion.requirement": "Requisit", - "addon.coursecompletion.status": "Estat", - "addon.coursecompletion.viewcoursereport": "Visualitza l'informe del curs", - "addon.files.couldnotloadfiles": "La llista d'arxius no s'ha pogut carregar.", - "addon.files.emptyfilelist": "No hi ha fitxers per mostrar.", - "addon.files.erroruploadnotworking": "No es poden pujar fitxers al vostre lloc ara mateix.", - "addon.files.files": "Fitxers", - "addon.files.privatefiles": "Fitxers privats", - "addon.files.sitefiles": "Fitxers del lloc", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Configura els dispositius", - "addon.messages.acceptandaddcontact": "Accepta i afegeix a contactes", - "addon.messages.addcontact": "Afegeix contacte", - "addon.messages.addcontactconfirm": "Esteu segur que voleu afegir {{$a}} als vostres contactes?", - "addon.messages.addtofavourites": "Destaca la conversa", - "addon.messages.addtoyourcontacts": "Afegiu als vostres contactes", - "addon.messages.blocknoncontacts": "Impedeix que m'enviïn missatges els usuaris que no siguin a la meva llista de contactes", - "addon.messages.blockuser": "Bloca l'usuari", - "addon.messages.blockuserconfirm": "Esteu segur que voleu blocar {{$a}}?", - "addon.messages.contactableprivacy": "Accepta missatges de:", - "addon.messages.contactableprivacy_coursemember": "Els meus contactes i qualsevol persona dels meus cursos", - "addon.messages.contactableprivacy_onlycontacts": "Només els meus contactes", - "addon.messages.contactableprivacy_site": "Qualsevol al lloc", - "addon.messages.contactblocked": "Contacte blocat", - "addon.messages.contactlistempty": "La llista de contactes és buida", - "addon.messages.contactname": "Nom del contacte", - "addon.messages.contactrequestsent": "Sol·licitud de contacte enviada", - "addon.messages.contacts": "Contactes", - "addon.messages.conversationactions": "Menú d'accions de la conversa", - "addon.messages.decline": "Rebutja", - "addon.messages.deleteallconfirm": "Confirmeu que voleu suprimir aquesta conversa per complet? Això no l'eliminarà pels altres participants de la conversa.", - "addon.messages.deleteallselfconfirm": "Esteu segurs que voleu eliminar la conversa personal sencera?", - "addon.messages.deleteconversation": "Elimina la conversa", - "addon.messages.deleteforeveryone": "Elimina-ho per tothom", - "addon.messages.deletemessage": "Elimina el missatge", - "addon.messages.deletemessageconfirmation": "Segur que voleu eliminar aquest missatge? Només s'eliminarà de la vostra història de missatges però encara serà visible per l'usuari que va eliminar o rebre el missatge.", - "addon.messages.errordeletemessage": "S'ha produït un error mentre s'esborrava el missatge.", - "addon.messages.errorwhileretrievingcontacts": "S'ha produït un error en recuperar els contactes del servidor.", - "addon.messages.errorwhileretrievingdiscussions": "S'ha produït un error mentre es recuperaven els debats del servidor.", - "addon.messages.errorwhileretrievingmessages": "S'ha produït un error descarregant els missatges.", - "addon.messages.errorwhileretrievingusers": "S'ha produït un error descarregant els usuaris del servidor.", - "addon.messages.groupconversations": "Grup", - "addon.messages.groupinfo": "Informació del grup", - "addon.messages.individualconversations": "Privat", - "addon.messages.info": "Informació de l'usuari", - "addon.messages.isnotinyourcontacts": "{{$a}} no és a la vostra llista de contactes", - "addon.messages.message": "Missatge", - "addon.messages.messagenotsent": "El missatge no s'ha enviat. Si us plau, intenteu-ho més tard", - "addon.messages.messagepreferences": "Preferències dels missatges", - "addon.messages.messages": "Missatges", - "addon.messages.muteconversation": "Silencia", - "addon.messages.mutedconversation": "Conversa silenciada", - "addon.messages.newmessage": "Missatge nou", - "addon.messages.newmessages": "Nous missatges", - "addon.messages.nocontactrequests": "No teniu cap sol·licitud de contacte", - "addon.messages.nocontactsgetstarted": "No teniu cap contacte", - "addon.messages.nofavourites": "No teniu converses destacades", - "addon.messages.nogroupconversations": "No teniu converses de grup", - "addon.messages.noindividualconversations": "No teniu converses privades", - "addon.messages.nomessagesfound": "No s'han trobat missatges", - "addon.messages.noncontacts": "No contactes", - "addon.messages.nousersfound": "No s'han trobat usuaris", - "addon.messages.numparticipants": "{{$a}} participants", - "addon.messages.removecontact": "Suprimeix contacte", - "addon.messages.removecontactconfirm": "Esteu segur que voleu suprimir {{$a}} dels vostres contactes?", - "addon.messages.removefromfavourites": "Deixa de destacar la conversa", - "addon.messages.removefromyourcontacts": "Elimina dels contactes", - "addon.messages.requests": "Sol·licituds", - "addon.messages.requirecontacttomessage": "Cal que sol·liciteu a {{$a}} que us afegeixi com a contacte per poder enviar-li un missatge.", - "addon.messages.searchcombined": "Cerca gent i missatges", - "addon.messages.selfconversation": "Espai personal", - "addon.messages.selfconversationdefaultmessage": "Desa esborranys de missatges, enllaços, notes... per accedir-hi més tard.", - "addon.messages.sendcontactrequest": "Envia una sol·licitud de contacte", - "addon.messages.showdeletemessages": "Mostra els botons d'eliminació", - "addon.messages.type_blocked": "Bloquejat", - "addon.messages.type_offline": "Fora de línia", - "addon.messages.type_online": "En línia", - "addon.messages.type_search": "Resultats de la cerca", - "addon.messages.type_strangers": "Altres", - "addon.messages.unabletomessage": "No podeu enviar missatges a aquest usuari", - "addon.messages.unblockuser": "Desbloca l'usuari", - "addon.messages.unblockuserconfirm": "Esteu segur que voleu desblocar {{$a}}?", - "addon.messages.unmuteconversation": "Desactiva el silenci", - "addon.messages.useentertosend": "Prem la tecla de retorn per enviar", - "addon.messages.useentertosenddescdesktop": "Si està desactivat, podeu emprar Ctrl+Enter per enviar el missatge.", - "addon.messages.useentertosenddescmac": "Si està desactivat, podeu emprar Cmd+Enter per enviar el missatge.", - "addon.messages.userwouldliketocontactyou": "{{$a}} vol contactar-vos", - "addon.messages.warningconversationmessagenotsent": "No s'han pogut enviar missatges a la conversa {{user}}. {{error}}", - "addon.messages.warningmessagenotsent": "No s'han pogut enviar missatges a l'usuari {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Vol contactar-vos", - "addon.messages.you": "Vós:", - "addon.messages.youhaveblockeduser": "Heu bloquejat aquest usuari en el passat.", - "addon.messages.yourcontactrequestpending": "La vostra sol·licitud de contacte amb {{$a}} està pendent", - "addon.mod_assign.acceptsubmissionstatement": "Accepteu el compromís de tramesa.", - "addon.mod_assign.addattempt": "Permet un altre intent", - "addon.mod_assign.addnewattempt": "Afegeix un altre intent", - "addon.mod_assign.addnewattemptfromprevious": "Afegeix un intent nou basat en la tramesa anterior", - "addon.mod_assign.addsubmission": "Afegeix la tramesa", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Els detalls de la tasca i el formulari de la tramesa estaran disponibles des de {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Permet trameses des de", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Aquesta tasca acceptarà trameses des de {{$a}}", - "addon.mod_assign.applytoteam": "Aplica les qualificacions i la retroacció al grup sencer", - "addon.mod_assign.assignmentisdue": "La tasca ha vençut", - "addon.mod_assign.attemptnumber": "Número d'intent", - "addon.mod_assign.attemptreopenmethod": "Intents reoberts", - "addon.mod_assign.attemptreopenmethod_manual": "Manualment", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automàticament fins superació", - "addon.mod_assign.attemptsettings": "Configuració dels intents", - "addon.mod_assign.cannoteditduetostatementsubmission": "No podeu afegir o editar la tramesa en l'aplicació perquè no s'ha pogut recuperar el compromís de tramesa del lloc.", - "addon.mod_assign.cannotgradefromapp": "Alguns mètodes de qualificació no estan encara suportats per l'aplicació i no es poden modificar.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "No podeu enviar la tramesa a qualificar en l'aplicació perquè no s'ha pogut recuperar el compromís de tramesa del lloc.", - "addon.mod_assign.confirmsubmission": "Segur que voleu trametre el vostre treball per a qualificar? Ja no podreu realitzar més canvis.", - "addon.mod_assign.currentattempt": "Aquest és l'intent {{$a}}.", - "addon.mod_assign.currentattemptof": "Aquest és l'intent {{$a.attemptnumber}} ( {{$a.maxattempts}} intents permesos ).", - "addon.mod_assign.currentgrade": "Qualificació actual en el llibre de qualificacions", - "addon.mod_assign.cutoffdate": "Data límit", - "addon.mod_assign.defaultteam": "Grup per defecte", - "addon.mod_assign.duedate": "Data de venciment", - "addon.mod_assign.duedateno": "Sense data de venciment", - "addon.mod_assign.duedatereached": "La data de venciment per a aquesta tasca ja ha passat", - "addon.mod_assign.editingstatus": "S'està editant l'estat", - "addon.mod_assign.editsubmission": "Edita la tramesa", - "addon.mod_assign.erroreditpluginsnotsupported": "No podeu afegir o editar una tramesa en l'aplicació perquè alguns connectors no s'admeten per editar:", - "addon.mod_assign.errorshowinginformation": "No es pot mostrar la informació de la tramesa.", - "addon.mod_assign.extensionduedate": "Data de venciment de la pròrroga", - "addon.mod_assign.feedbacknotsupported": "Aquesta retroacció no està admesa per l'aplicació i podria no contenir tota la informació.", - "addon.mod_assign.grade": "Qualificació", - "addon.mod_assign.graded": "Qualificada", - "addon.mod_assign.gradedby": "Qualificat per", - "addon.mod_assign.gradedfollowupsubmit": "Qualificada: seguiment de la tramesa rebuda", - "addon.mod_assign.gradedon": "Qualificat el", - "addon.mod_assign.gradelocked": "Aquesta qualificació està bloquejada o rectificada al llibre de qualificacions.", - "addon.mod_assign.gradenotsynced": "La puntuació no està sincronitzada", - "addon.mod_assign.gradeoutof": "Qualificació sobre {{$a}}", - "addon.mod_assign.gradingstatus": "Estat de la qualificació", - "addon.mod_assign.groupsubmissionsettings": "Paràmetres de la tramesa en grup", - "addon.mod_assign.hiddenuser": "Participant", - "addon.mod_assign.latesubmissions": "Trameses fora de termini", - "addon.mod_assign.latesubmissionsaccepted": "Permès fins {{$a}}", - "addon.mod_assign.markingworkflowstate": "Estat del flux d'avaluació", - "addon.mod_assign.markingworkflowstateinmarking": "Avaluant-se", - "addon.mod_assign.markingworkflowstateinreview": "En revisió", - "addon.mod_assign.markingworkflowstatenotmarked": "No avaluada", - "addon.mod_assign.markingworkflowstatereadyforrelease": "A punt per publicar", - "addon.mod_assign.markingworkflowstatereadyforreview": "Avaluació completada", - "addon.mod_assign.markingworkflowstatereleased": "Publicada", - "addon.mod_assign.modulenameplural": "Tasques", - "addon.mod_assign.multipleteams": "Membre de més d'un grup", - "addon.mod_assign.multipleteams_desc": "La tasca requereix la tramesa en grups. Sou membre de més d'un grup. Per poder trametre heu de ser membre de només un grup. Poseu-vos en contacte amb el professor per canviar la vostra adhesió a un grup.", - "addon.mod_assign.noattempt": "Cap intent", - "addon.mod_assign.nomoresubmissionsaccepted": "Sols és permès per als participants que tenen concedida una pròrroga.", - "addon.mod_assign.noonlinesubmissions": "Aquesta tasca no requereix que trameteu res en línia.", - "addon.mod_assign.nosubmission": "No s'ha tramès res per a aquesta tasca", - "addon.mod_assign.notallparticipantsareshown": "Els participants que no tinguin cap tramesa no es mostren.", - "addon.mod_assign.noteam": "No és membre de cap grup", - "addon.mod_assign.noteam_desc": "La tasca requereix la tramesa en grups. No sou membre de cap grup; per tant, no podeu fer una tramesa. Poseu-vos en contacte amb el professor per afegir-vos a un grup.", - "addon.mod_assign.notgraded": "Sense qualificació", - "addon.mod_assign.numberofdraftsubmissions": "Esborranys", - "addon.mod_assign.numberofparticipants": "Participants", - "addon.mod_assign.numberofsubmissionsneedgrading": "Necessiten qualificació", - "addon.mod_assign.numberofsubmittedassignments": "S'han tramès", - "addon.mod_assign.numberofteams": "Grups", - "addon.mod_assign.numwords": "{{$a}} paraules", - "addon.mod_assign.outof": "{{$a.current}} de {{$a.total}}", - "addon.mod_assign.overdue": "La tasca ha vençut fa: {{$a}}", - "addon.mod_assign.submission": "Tramesa", - "addon.mod_assign.submissioneditable": "L'estudiant pot editar aquesta tramesa", - "addon.mod_assign.submissionnoteditable": "L'estudiant no pot editar aquesta tramesa", - "addon.mod_assign.submissionnotsupported": "Aquesta tramesa no està admesa per l'aplicació i podria no contenir tota la informació", - "addon.mod_assign.submissionslocked": "Aquesta tasca no accepta trameses", - "addon.mod_assign.submissionstatus": "Estat de la tramesa", - "addon.mod_assign.submissionstatus_": "No s'ha tramès", - "addon.mod_assign.submissionstatus_draft": "Esborrany (no s'ha tramès la versió definitiva)", - "addon.mod_assign.submissionstatus_marked": "Qualificada", - "addon.mod_assign.submissionstatus_new": "No s'ha tramès", - "addon.mod_assign.submissionstatus_reopened": "Reoberta", - "addon.mod_assign.submissionstatus_submitted": "S'ha tramès per a qualificar", - "addon.mod_assign.submissionstatusheading": "Estat de la tramesa", - "addon.mod_assign.submissionteam": "Grup", - "addon.mod_assign.submitassignment": "Tramet la tasca", - "addon.mod_assign.submitassignment_help": "Un cop s'hagi tramès la tasca no podreu fer més canvis", - "addon.mod_assign.submittedearly": "La tasca s'ha tramès {{$a}} abans del límit", - "addon.mod_assign.submittedlate": "La tasca s'ha tramès {{$a}} tard", - "addon.mod_assign.timemodified": "Darrera modificació", - "addon.mod_assign.timeremaining": "Temps restant", - "addon.mod_assign.ungroupedusers": "El paràmetre «Cal formar part d'un grup per fer una tramesa» està activat, i alguns usuaris o no són membres de cap grup o són membres de més d'un grup, per la qual cosa no poden fer trameses.", - "addon.mod_assign.ungroupedusersoptional": "El paràmetre 'Els estudiants trameten en grups' està actiu i alguns usuaris o bé no són membres de cap grup, o bé són membres de més d'un grup. Recordeu que les trameses d'aquests estudiants es mostraran com a membres del 'Grup per defecte'.", - "addon.mod_assign.unlimitedattempts": "Il·limitats", - "addon.mod_assign.userswhoneedtosubmit": "Usuaris que manquen per trametre: {{$a}}", - "addon.mod_assign.userwithid": "Usuari amb Id {{id}}", - "addon.mod_assign.viewsubmission": "Visualitza la tramesa", - "addon.mod_assign.warningsubmissiongrademodified": "S'ha modificat la qualificació de la tramesa a la web.", - "addon.mod_assign.warningsubmissionmodified": "La tramesa de l'usuari s'ha modificat a la web.", - "addon.mod_assign.wordlimit": "Límit de paraules", - "addon.mod_assign_feedback_comments.pluginname": "Comentaris de retroalimentació", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF amb comentaris", - "addon.mod_assign_feedback_file.pluginname": "Retroalimentació amb fitxer", - "addon.mod_assign_submission_comments.pluginname": "Comentaris de la tramesa", - "addon.mod_assign_submission_file.pluginname": "Fitxers de la tramesa", - "addon.mod_assign_submission_onlinetext.pluginname": "Trameses de text en línia", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "El límit de paraules per a aquesta tasca és de {{$a.limit}} paraules i intenteu trametre'n {{$a.count}}. Reviseu la vostra tramesa i torneu-ho a provar.", - "addon.mod_book.errorchapter": "S'ha produït un error en llegir el capítol del llibre.", - "addon.mod_book.modulenameplural": "Llibres", - "addon.mod_book.navnexttitle": "Següent: {{$a}}", - "addon.mod_book.navprevtitle": "Anterior: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Capítols del llibre", - "addon.mod_book.toc": "Taula de continguts", - "addon.mod_chat.beep": "Fes sonar", - "addon.mod_chat.chatreport": "Sessions de xat", - "addon.mod_chat.currentusers": "Usuaris actuals", - "addon.mod_chat.enterchat": "Feu clic aquí per entrar al xat", - "addon.mod_chat.entermessage": "Escriviu el vostre missatge", - "addon.mod_chat.errorwhileconnecting": "S'ha produït un error durant la connexió al xat.", - "addon.mod_chat.errorwhilegettingchatdata": "S'ha produït un error mentre es recuperaven les dades del xat.", - "addon.mod_chat.errorwhilegettingchatusers": "S'ha produït un error mentre es recuperaven els usuaris del xat.", - "addon.mod_chat.errorwhileretrievingmessages": "S'ha produït un error mentre es recuperaven els missatges del servidor.", - "addon.mod_chat.errorwhilesendingmessage": "S'ha produït un error mentre s'enviava el missatge.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} ha fet bip a tothom!", - "addon.mod_chat.messagebeepsyou": "{{$a}} t'acaba de fer bip!", - "addon.mod_chat.messageenter": "{{$a}} acaba d'entrar en aquest xat", - "addon.mod_chat.messageexit": "{{$a}} ha abandonat aquest xat", - "addon.mod_chat.messages": "Missatges", - "addon.mod_chat.messageyoubeep": "Heu fet un toc a {{$a}}", - "addon.mod_chat.modulenameplural": "Xats", - "addon.mod_chat.mustbeonlinetosendmessages": "Heu d'estar connectat per poder enviar missatges", - "addon.mod_chat.nomessages": "No hi ha missatges encara", - "addon.mod_chat.nosessionsfound": "No s'han trobat sessions", - "addon.mod_chat.saidto": "dit a", - "addon.mod_chat.send": "Envia", - "addon.mod_chat.sessionstart": "La propera sessió de xat començarà el: {{$a.date}}, (d'aquí a {{$a.fromnow}})", - "addon.mod_chat.showincompletesessions": "Mostra les sessions incompletes", - "addon.mod_chat.talk": "Parla", - "addon.mod_chat.viewreport": "Visualitza les sessions de xat anteriors", - "addon.mod_choice.cannotsubmit": "Hi ha hagut un problema en la tramesa de la vostra resposta. Torneu-ho a intentar.", - "addon.mod_choice.choiceoptions": "Opcions de la tria", - "addon.mod_choice.errorgetchoice": "S'ha produït un error en recuperar les dades de la consulta.", - "addon.mod_choice.expired": "Aquesta activitat es va tancar el dia {{$a}}.", - "addon.mod_choice.full": "(Complet)", - "addon.mod_choice.modulenameplural": "Consultes", - "addon.mod_choice.noresultsviewable": "A hores d'ara no es poden veure els resultats", - "addon.mod_choice.notopenyet": "Aquesta activitat no estarà disponible fins el dia {{$a}}.", - "addon.mod_choice.numberofuser": "Nombre de respostes", - "addon.mod_choice.numberofuserinpercentage": "Percentatge de respostes (%)", - "addon.mod_choice.previewonly": "Això és sols una vista prèvia de les opcions disponibles en aquesta activitat. No esteu habilitat per enviar consultes fins {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Els resultats es publicaran de manera anònima després que contesteu.", - "addon.mod_choice.publishinfoanonclose": "Els resultats es publicaran de manera anònima després que es tanqui l'activitat.", - "addon.mod_choice.publishinfofullafter": "Els resultats complets, que mostraran l'elecció de cadascú, es publicaran després que contesteu.", - "addon.mod_choice.publishinfofullclose": "Els resultats complets, que mostraran l'elecció de cadascú, es publicaran després que es tanqui l'activitat.", - "addon.mod_choice.publishinfonever": "Els resultats d'aquesta activitat no es publicaran després que contesteu.", - "addon.mod_choice.removemychoice": "Suprimeix la meva resposta", - "addon.mod_choice.responses": "Respostes", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% dels usuaris han escollit l'opció: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Mostra la gràfica", - "addon.mod_choice.resultsnotsynced": "Els resultats no inclouen la vostra darrera resposta. Sincronitzeu per actualitzar-los.", - "addon.mod_choice.savemychoice": "Desa la meva resposta", - "addon.mod_choice.userchoosethisoption": "Usuaris que han escollit aquesta opció", - "addon.mod_choice.yourselection": "La vostra selecció", - "addon.mod_data.addentries": "Afegeix entrades", - "addon.mod_data.advancedsearch": "Cerca avançada", - "addon.mod_data.alttext": "Text alternatiu", - "addon.mod_data.approve": "Aprova", - "addon.mod_data.approved": "Acceptat", - "addon.mod_data.ascending": "Ascendent", - "addon.mod_data.authorfirstname": "Nom de l'autor/autora", - "addon.mod_data.authorlastname": "Cognoms de l'autor/autora", - "addon.mod_data.confirmdeleterecord": "Segur que voleu suprimir aquesta entrada?", - "addon.mod_data.descending": "Descendent", - "addon.mod_data.disapprove": "Desfés l'aprovació", - "addon.mod_data.edittagsnotsupported": "L'edició d'etiquetes no està suportada a l'aplicació.", - "addon.mod_data.emptyaddform": "No heu emplenat cap camp", - "addon.mod_data.entrieslefttoadd": "Heu d'afegir {{$a.entriesleft}} una entrada o més per completar aquesta activitat", - "addon.mod_data.entrieslefttoaddtoview": "Heu d'afegir {{$a.entrieslefttoview}} una entrada o més abans que pugueu veure les entrades d'altres participants", - "addon.mod_data.errorapproving": "S'ha produït un error mentre s'aprovava o es rebutjava l'entrada.", - "addon.mod_data.errordeleting": "S'ha produït un error mentre s'eliminava l'entrada.", - "addon.mod_data.errormustsupplyvalue": "Cal que poseu un valor aquí.", - "addon.mod_data.expired": "Aquesta activitat es va tancar el dia {{$a}} i ja no està disponible", - "addon.mod_data.fields": "Camps", - "addon.mod_data.foundrecords": "S'han trobat els registres:{{$a.num}}/{{$a.max}} (Reinicialitza filtres)", - "addon.mod_data.gettinglocation": "S'està obtenint la localització", - "addon.mod_data.latlongboth": "Cal posar tant la latitud com la longitud.", - "addon.mod_data.locationpermissiondenied": "S'ha denegat el permís per accedir a la vostra localització.", - "addon.mod_data.menuchoose": "Trieu...", - "addon.mod_data.modulenameplural": "Bases de dades", - "addon.mod_data.more": "Més", - "addon.mod_data.mylocation": "La meva localització", - "addon.mod_data.nomatch": "No s'han trobat entrades que coincideixin", - "addon.mod_data.norecords": "No hi ha entrades en la base de dades", - "addon.mod_data.notapproved": "L'entrada encara no està aprovada.", - "addon.mod_data.notopenyet": "Aquesta activitat no estarà disponible fins al dia {{$a}}", - "addon.mod_data.numrecords": "{{$a}} entrades", - "addon.mod_data.other": "Un altre", - "addon.mod_data.recordapproved": "S'ha aprovat l'entrada", - "addon.mod_data.recorddeleted": "S'ha suprimit l'entrada", - "addon.mod_data.recorddisapproved": "Entrada no aprovada", - "addon.mod_data.resetsettings": "Reinicialitza els filtres", - "addon.mod_data.search": "Cerca", - "addon.mod_data.searchbytagsnotsupported": "La cerca d'etiquetes no està suportada a l'aplicació.", - "addon.mod_data.selectedrequired": "Cal que estigui tot seleccionat", - "addon.mod_data.single": "Visualitza una entrada", - "addon.mod_data.tagarea_data_records": "Entrades", - "addon.mod_data.timeadded": "Hora de la incorporació", - "addon.mod_data.timemodified": "Hora de la modificació", - "addon.mod_data.usedate": "Inclou en la cerca.", - "addon.mod_feedback.analysis": "Anàlisi", - "addon.mod_feedback.anonymous": "Les respostes seran anònimes", - "addon.mod_feedback.anonymous_entries": "Entrades anònimes ({{$a}})", - "addon.mod_feedback.average": "Mitjana", - "addon.mod_feedback.captchaofflinewarning": "Les retroaccions amb captcha no es poden completar si aquest no està configurat, o no es pot connectar amb el servidor.", - "addon.mod_feedback.complete_the_form": "Responeu les preguntes", - "addon.mod_feedback.completed_feedbacks": "Respostes enviades", - "addon.mod_feedback.continue_the_form": "Continueu responent les preguntes", - "addon.mod_feedback.feedback_is_not_open": "La retroalimentació no està oberta", - "addon.mod_feedback.feedback_submitted_offline": "S'ha desat la retroacció per trametre's més tard.", - "addon.mod_feedback.feedbackclose": "Permet les respostes a", - "addon.mod_feedback.feedbackopen": "Permet les respostes de", - "addon.mod_feedback.mapcourses": "Associa una retroalimentació a cursos.", - "addon.mod_feedback.maximal": "Màxim", - "addon.mod_feedback.minimal": "Mínim", - "addon.mod_feedback.mode": "Mode", - "addon.mod_feedback.modulenameplural": "Retroalimentació", - "addon.mod_feedback.next_page": "Pàgina següent", - "addon.mod_feedback.non_anonymous": "El nom de l'usuari es registrarà i es mostrarà amb les respostes", - "addon.mod_feedback.non_anonymous_entries": "Entrades no anònimes ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Estudiants que no han respost ({{$a}})", - "addon.mod_feedback.not_selected": "No s'ha seleccionat", - "addon.mod_feedback.not_started": "No s'ha iniciat", - "addon.mod_feedback.numberoutofrange": "Número fora de l'interval", - "addon.mod_feedback.overview": "Descripció", - "addon.mod_feedback.page_after_submit": "Missatge de compleció", - "addon.mod_feedback.preview": "Previsualització", - "addon.mod_feedback.previous_page": "Pàgina anterior", - "addon.mod_feedback.questions": "Preguntes", - "addon.mod_feedback.response_nr": "Número de resposta", - "addon.mod_feedback.responses": "Respostes", - "addon.mod_feedback.save_entries": "Envia les respostes", - "addon.mod_feedback.show_entries": "Mostra les respostes", - "addon.mod_feedback.show_nonrespondents": "Mostra els que no han respost", - "addon.mod_feedback.started": "S'ha iniciat", - "addon.mod_feedback.this_feedback_is_already_submitted": "Heu completat aquesta activitat", - "addon.mod_folder.emptyfilelist": "No hi ha fitxers per mostrar.", - "addon.mod_folder.modulenameplural": "Carpetes", - "addon.mod_forum.addanewdiscussion": "Afegeix un tema de debat nou", - "addon.mod_forum.addanewquestion": "Afegeix una pregunta nova", - "addon.mod_forum.addanewtopic": "Afegeix un tema nou", - "addon.mod_forum.addtofavourites": "Destaca aquest debat", - "addon.mod_forum.advanced": "Avançat", - "addon.mod_forum.cannotadddiscussion": "Afegir debats en aquest fòrum requereix pertànyer al grup.", - "addon.mod_forum.cannotadddiscussionall": "No teniu permís per a afegir un tema de debat nou per a tots els participants.", - "addon.mod_forum.cannotcreatediscussion": "No s'ha pogut obrir un debat nou", - "addon.mod_forum.couldnotadd": "Un error desconegut ha impedit afegir el vostre missatge", - "addon.mod_forum.couldnotupdate": "Un error desconegut ha impedit actualitzar el vostre missatge", - "addon.mod_forum.cutoffdatereached": "La data límit per enviar missatges al fòrum ha passat, no podeu enviar-ne més.", - "addon.mod_forum.delete": "Suprimeix", - "addon.mod_forum.deletedpost": "El missatge s'ha suprimit", - "addon.mod_forum.deletesure": "Segur que voleu suprimir aquest missatge?", - "addon.mod_forum.discussion": "Debat", - "addon.mod_forum.discussionlistsortbycreatedasc": "Ordena per data de creació en ordre ascendent", - "addon.mod_forum.discussionlistsortbycreateddesc": "Ordena per data de creació en ordre descendent", - "addon.mod_forum.discussionlistsortbylastpostasc": "Ordena per data de creació de l'ultim missatge en ordre ascendent", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Ordena per data de creació de l'ultim missatge en ordre descendent", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Ordena per nombre de respostes en ordre ascendent", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Ordena per nombre de respostes en ordre descendent", - "addon.mod_forum.discussionlocked": "Aquest fil de debat ha finalitzat, així que no admet respostes.", - "addon.mod_forum.discussionpinned": "Fixat", - "addon.mod_forum.discussionsubscription": "Subscripció als debats", - "addon.mod_forum.edit": "Edita", - "addon.mod_forum.erroremptymessage": "El missatge no pot estar buit", - "addon.mod_forum.erroremptysubject": "La casella «Assumpte» no pot estar buida", - "addon.mod_forum.errorgetforum": "S'ha produït un error descarregant les dades del fòrum.", - "addon.mod_forum.errorgetgroups": "S'ha produït un error en descarregar la configuració del grup.", - "addon.mod_forum.errorposttoallgroups": "No s'han pogut crear nous debats a tots els grups.", - "addon.mod_forum.favouriteupdated": "L'opció de destacat s'ha actualitzat.", - "addon.mod_forum.forumnodiscussionsyet": "Encara no hi ha temes de debat en aquest fòrum", - "addon.mod_forum.group": "Grup", - "addon.mod_forum.lastpost": "Darrer missatge", - "addon.mod_forum.lockdiscussion": "Bloqueja aquest debat", - "addon.mod_forum.lockupdated": "La opció de bloqueig s'ha actualitzat.", - "addon.mod_forum.message": "Missatge", - "addon.mod_forum.modeflatnewestfirst": "Visualitza les respostes, començant per la més recent", - "addon.mod_forum.modeflatoldestfirst": "Visualitza les respostes, començant per la més antiga", - "addon.mod_forum.modenested": "Visualitza les respostes escalonades", - "addon.mod_forum.modulenameplural": "Fòrums", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discussions", - "addon.mod_forum.numreplies": "{{numreplies}} respostes", - "addon.mod_forum.pindiscussion": "Fixa aquest debat", - "addon.mod_forum.pinupdated": "La opció de fixar s'ha actualitzat.", - "addon.mod_forum.postisprivatereply": "Aquesta resposta és privada. Els altres participants no la poden veure.", - "addon.mod_forum.posttoforum": "Envia al fòrum", - "addon.mod_forum.posttomygroups": "Envia una còpia a tots els grups", - "addon.mod_forum.privatereply": "Respon de forma privada", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Actualitza els debats", - "addon.mod_forum.refreshposts": "Actualitza els missatges", - "addon.mod_forum.removefromfavourites": "No destaquis aquest debat", - "addon.mod_forum.reply": "Respon", - "addon.mod_forum.replyplaceholder": "Escriviu una resposta...", - "addon.mod_forum.subject": "Assumpte", - "addon.mod_forum.tagarea_forum_posts": "Missatges del fòrum", - "addon.mod_forum.thisforumhasduedate": "La data de venciment per enviar missatges a aquest fòrum és {{$a}}.", - "addon.mod_forum.thisforumisdue": "La data de venciment per enviar missatges a aquest fòrum va ser {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Desbloqueja aquest debat", - "addon.mod_forum.unpindiscussion": "No fixis aquest debat", - "addon.mod_forum.unread": "No llegit", - "addon.mod_forum.unreadpostsnumber": "{{$a}} missatges no llegits", - "addon.mod_forum.yourreply": "La vostra resposta", - "addon.mod_glossary.addentry": "Afegeix una entrada", - "addon.mod_glossary.aliases": "Paraula o paraules clau", - "addon.mod_glossary.attachment": "Fitxer adjunt", - "addon.mod_glossary.browsemode": "Navegueu per les entrades", - "addon.mod_glossary.byalphabet": "Alfabèticament", - "addon.mod_glossary.byauthor": "Agrupat per autor", - "addon.mod_glossary.bycategory": "Agrupa per categoria", - "addon.mod_glossary.bynewestfirst": "El més nou primer", - "addon.mod_glossary.byrecentlyupdated": "Actualitzat recentment", - "addon.mod_glossary.bysearch": "Cerca", - "addon.mod_glossary.cannoteditentry": "No es pot editar l'entrada", - "addon.mod_glossary.casesensitive": "Distingeix majúscules i minúscules", - "addon.mod_glossary.categories": "Categories", - "addon.mod_glossary.concept": "Concepte", - "addon.mod_glossary.definition": "Definició", - "addon.mod_glossary.entriestobesynced": "Entrades per sincronitzar", - "addon.mod_glossary.entrypendingapproval": "Aquesta entrada està pendent d'aprovació.", - "addon.mod_glossary.entryusedynalink": "Aquesta entrada s'ha d'enllaçar automàticament", - "addon.mod_glossary.errconceptalreadyexists": "El concepte ja existeix. Aquest glossari no permet entrades duplicades.", - "addon.mod_glossary.errorloadingentries": "S'ha produït un error en carregar les entrades.", - "addon.mod_glossary.errorloadingentry": "S'ha produït un error en carregar l'entrada.", - "addon.mod_glossary.errorloadingglossary": "S'ha produït un error en carregar el glossari.", - "addon.mod_glossary.fillfields": "El concepte i la definició són camps obligatoris.", - "addon.mod_glossary.fullmatch": "Enllaça només paraules completes", - "addon.mod_glossary.linking": "Enllaços automàtics", - "addon.mod_glossary.modulenameplural": "Glossaris", - "addon.mod_glossary.noentriesfound": "No s'han trobat entrades.", - "addon.mod_glossary.searchquery": "La vostra cerca", - "addon.mod_glossary.tagarea_glossary_entries": "Entrades del glossari", - "addon.mod_h5pactivity.all_attempts": "Tots els intents de l'usuari", - "addon.mod_h5pactivity.answer_checked": "Resposta marcada", - "addon.mod_h5pactivity.answer_correct": "La vostra resposta és correcta", - "addon.mod_h5pactivity.answer_fail": "Resposta incorrecta", - "addon.mod_h5pactivity.answer_incorrect": "La vostra resposta és incorrecta", - "addon.mod_h5pactivity.answer_pass": "Resposta correcta", - "addon.mod_h5pactivity.attempt": "Intent", - "addon.mod_h5pactivity.attempt_completion_no": "L'intent no s'ha marcat com a completat", - "addon.mod_h5pactivity.attempt_completion_yes": "S'ha completat l'intent", - "addon.mod_h5pactivity.attempt_success_fail": "Error", - "addon.mod_h5pactivity.attempt_success_pass": "Correcte", - "addon.mod_h5pactivity.attempt_success_unknown": "No se n'ha informat", - "addon.mod_h5pactivity.attempts_none": "Aquest usuari no té cap intent per mostrar.", - "addon.mod_h5pactivity.completion": "Compleció", - "addon.mod_h5pactivity.downloadh5pfile": "Descarrega el fitxer H5P", - "addon.mod_h5pactivity.duration": "Durada", - "addon.mod_h5pactivity.errorgetactivity": "S'ha produït un error en obtenir les dades de l'activitat H5P.", - "addon.mod_h5pactivity.filestatenotdownloaded": "El paquet H5P no està descarregat. Cal que el descarregueu per poder-lo utilitzar.", - "addon.mod_h5pactivity.filestateoutdated": "El paquet H5P s'ha modificat des de la darrera descàrrega. Cal que el torneu a descarregar per poder-lo utilitzar.", - "addon.mod_h5pactivity.maxscore": "Puntuació màxima", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Els meus intents", - "addon.mod_h5pactivity.no_compatible_track": "La interacció ({{$a}}) no té informació de seguiment o bé el seguiment no és compatible amb la versió actual de l'activitat.", - "addon.mod_h5pactivity.offlinedisabledwarning": "Cal que estigueu connectats per veure el paquet H5P.", - "addon.mod_h5pactivity.outcome": "Competència", - "addon.mod_h5pactivity.previewmode": "S'està previsualitzant el contingut. No es desarà el seguiment de l'intent.", - "addon.mod_h5pactivity.result_fill-in": "Compleció de text", - "addon.mod_h5pactivity.result_other": "Tipus d'interacció desconeguda", - "addon.mod_h5pactivity.review_my_attempts": "Mostra els meus intents", - "addon.mod_h5pactivity.score": "Puntuació", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} sobre {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Data d'inici", - "addon.mod_h5pactivity.totalscore": "Puntuació total", - "addon.mod_h5pactivity.viewattempt": "Veure l'intent {{$a}}", - "addon.mod_imscp.deploymenterror": "Error en el contingut del paquet!", - "addon.mod_imscp.modulenameplural": "Paquets de contingut IMS", - "addon.mod_imscp.showmoduledescription": "Mostra la descripció", - "addon.mod_imscp.toc": "Taula de continguts", - "addon.mod_lesson.answer": "Resposta", - "addon.mod_lesson.attempt": "Intent: {{$a}}", - "addon.mod_lesson.attemptheader": "Intent", - "addon.mod_lesson.attemptsremaining": "Us resten {{$a}} intents", - "addon.mod_lesson.averagescore": "Puntuació mitjana", - "addon.mod_lesson.averagetime": "Temps promig", - "addon.mod_lesson.branchtable": "Contingut", - "addon.mod_lesson.cannotfindattempt": "Error: no es pot trobar l'intent", - "addon.mod_lesson.cannotfinduser": "Error: no es poden trobar els usuaris", - "addon.mod_lesson.clusterjump": "Pregunta no vista en un clúster", - "addon.mod_lesson.completed": "Completat", - "addon.mod_lesson.congratulations": "Felicitacions: heu arribat al final de la lliçó", - "addon.mod_lesson.continue": "Continua", - "addon.mod_lesson.continuetonextpage": "Continua a la pàgina següent", - "addon.mod_lesson.defaultessayresponse": "El vostre treball serà qualificat pel professor del curs.", - "addon.mod_lesson.detailedstats": "Estadístiques detallades", - "addon.mod_lesson.didnotanswerquestion": "No heu respost aquesta pregunta.", - "addon.mod_lesson.displayofgrade": "Visualització de la qualificació (només estudiants)", - "addon.mod_lesson.displayscorewithessays": "

Heu aconseguit {{$a.score}} punts d'un màxim de {{$a.tempmaxgrade}} de les preguntes qualificades automàticament.

\n

Les vostres {{$a.essayquestions}} preguntes de resposta oberta es qualificaran més endavant i s'afegiran a la puntuació final.

\n

La vostra qualificació actual, sense les preguntes de resposta oberta, és de {{$a.score}} sobre {{$a.grade}}.

", - "addon.mod_lesson.displayscorewithoutessays": "La vostra puntuació és {{$a.score}} (sobre {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "La contrasenya no es pot deixar en blanc", - "addon.mod_lesson.enterpassword": "Escriviu la contrasenya:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "No heu respost cap pregunta. La vostra puntuació d'auqesta lliçó és de 0.", - "addon.mod_lesson.errorprefetchrandombranch": "Aquesta lliçó conté salts a una pàgina aleatòria de contingut. No es podrà intentar a l'aplicació fins que no s'hagi començat en un navegador.", - "addon.mod_lesson.errorreviewretakenotlast": "Aquest intent no es pot revisar perquè un altre intent s'ha finalitzat.", - "addon.mod_lesson.finish": "Acaba", - "addon.mod_lesson.finishretakeoffline": "Aquest intent s'ha finalitzat fora de línia.", - "addon.mod_lesson.firstwrong": "La vostra resposta no és correcta. Voleu reintentar-ho? (Si ara responeu correctament, no s'incrementarà la puntuació final).", - "addon.mod_lesson.gotoendoflesson": "Vés al final de la lliçó", - "addon.mod_lesson.grade": "Qualifica", - "addon.mod_lesson.highscore": "Puntuació més alta", - "addon.mod_lesson.hightime": "Temps màxim", - "addon.mod_lesson.leftduringtimed": "Heu abandonat una lliçó cronometrada.
Feu clic a Continua per tornar-la a començar.", - "addon.mod_lesson.leftduringtimednoretake": "Heu abandonat una lliçó cronometrada i no està permès continuar-la o torna-la a fer.", - "addon.mod_lesson.lessonmenu": "Menú de la lliçó", - "addon.mod_lesson.lessonstats": "Estadístiques de la lliçó", - "addon.mod_lesson.linkedmedia": "Fitxer enllaçat", - "addon.mod_lesson.loginfail": "Ha fallat l'inici de sessió, torneu a provar-ho...", - "addon.mod_lesson.lowscore": "Puntuació més baixa", - "addon.mod_lesson.lowtime": "Temps més baix", - "addon.mod_lesson.maximumnumberofattemptsreached": "Heu arribat al nombre màxim d'intents. Ara passareu a la pàgina següent.", - "addon.mod_lesson.modattemptsnoteacher": "No podeu fer la revisió dels estudiants.", - "addon.mod_lesson.modulenameplural": "Lliçons", - "addon.mod_lesson.noanswer": "Una o més preguntes no tenen resposta. Torneu enrere i responeu-les.", - "addon.mod_lesson.nolessonattempts": "Encara no s'ha registrat cap intent en aquesta lliçó", - "addon.mod_lesson.nolessonattemptsgroup": "Ningú del grup {{$a}} ha fet encara cap intent d'aquesta lliçó.", - "addon.mod_lesson.notcompleted": "Incomplet", - "addon.mod_lesson.numberofcorrectanswers": "Nombre de respostes correctes: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Nombre de preguntes contestades: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Nombre de preguntes contestades: {{$a.nquestions}}; (n'hauríeu de contestar com a mínim {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Fins ara heu aconseguit {{$a.score}} punts de {{$a.currenthigh}}.", - "addon.mod_lesson.ongoingnormal": "Heu contestat correctament {{$a.correct}} preguntes d'un total de {{$a.viewed}} intents.", - "addon.mod_lesson.or": "O", - "addon.mod_lesson.overview": "Revisió", - "addon.mod_lesson.preview": "Previsualització", - "addon.mod_lesson.progressbarteacherwarning2": "No veureu la barra de progrés, ja que teniu permís per modificar aquesta lliçó", - "addon.mod_lesson.progresscompleted": "Heu completat el {{$a}}% de la lliçó.", - "addon.mod_lesson.question": "Pregunta", - "addon.mod_lesson.rawgrade": "Qualificació bruta", - "addon.mod_lesson.reports": "Informes", - "addon.mod_lesson.response": "Reacció", - "addon.mod_lesson.retakefinishedinsync": "S'ha sincronitzat un intent fora de línia. Voleu revisar-lo?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Revisa", - "addon.mod_lesson.reviewlesson": "Revisa la lliçó", - "addon.mod_lesson.reviewquestionback": "Sí, vull tornar a provar-ho", - "addon.mod_lesson.reviewquestioncontinue": "No, vull anar a la pregunta següent", - "addon.mod_lesson.secondpluswrong": "Incorrecte de nou. Voleu tornar a provar-ho?", - "addon.mod_lesson.submit": "Envia", - "addon.mod_lesson.teacherjumpwarning": "En aquesta lliçó s'utilitza un salt {{$a.cluster}} o un salt {{$a.unseen}}. En lloc d'això, s'utilitzarà un salt a la pàgina següent. Inicieu sessió com a estudiant per comprovar aquests salts.", - "addon.mod_lesson.teacherongoingwarning": "La puntuació acumulada només es mostra a l'estudiant. Inicieu sessió com a estudiant per comprovar la puntuació acumulada.", - "addon.mod_lesson.teachertimerwarning": "El cronòmetre només es mostra als estudiants. Inicieu la sessió com a estudiant per comprovar-ne el funcionament.", - "addon.mod_lesson.thatsthecorrectanswer": "Aquesta és la resposta correcta", - "addon.mod_lesson.thatsthewronganswer": "Aquesta resposta és errònia", - "addon.mod_lesson.timeremaining": "Temps restant", - "addon.mod_lesson.timetaken": "Temps dedicat", - "addon.mod_lesson.unseenpageinbranch": "Pregunta no vista en una pàgina de contingut", - "addon.mod_lesson.warningretakefinished": "L'intent s'ha finalitzat al lloc web.", - "addon.mod_lesson.welldone": "Molt bé", - "addon.mod_lesson.youhaveseen": "Ja heu vist més d'una pàgina d'aquesta lliçó.
Voleu començar a la darrera pàgina que vau veure?", - "addon.mod_lesson.youranswer": "La vostra resposta", - "addon.mod_lesson.yourcurrentgradeisoutof": "La vostra qualificació actual és {{$a.grade}} sobre {{$a.total}}", - "addon.mod_lesson.youshouldview": "N'hauríeu de contestar com a mínim: {{$a}}", - "addon.mod_lti.errorgetlti": "S'ha produït un error en recuperar les dades del mòdul.", - "addon.mod_lti.errorinvalidlaunchurl": "L'adreça URL no és vàlida.", - "addon.mod_lti.launchactivity": "Executa l'activitat", - "addon.mod_lti.modulenameplural": "Eines externes", - "addon.mod_page.errorwhileloadingthepage": "S'ha produït un error carregant el contingut de la pàgina.", - "addon.mod_page.modulenameplural": "Pàgines", - "addon.mod_quiz.answercolon": "Resposta:", - "addon.mod_quiz.attemptfirst": "Primer intent", - "addon.mod_quiz.attemptlast": "Darrer intent", - "addon.mod_quiz.attemptnumber": "Intent", - "addon.mod_quiz.attemptquiznow": "Contesta el qüestionari ara", - "addon.mod_quiz.attemptstate": "Estat", - "addon.mod_quiz.canattemptbutnotsubmit": "Podeu intentar aquest qüestionari a l’aplicació, però haureu d’enviar l’intent des del navegador pels següents motius:", - "addon.mod_quiz.cannotsubmitquizdueto": "No es pot trametre aquest intent del qüestionari per les raons següents:", - "addon.mod_quiz.clearchoice": "Esborra la meva selecció", - "addon.mod_quiz.comment": "Comentari", - "addon.mod_quiz.completedon": "Completat el", - "addon.mod_quiz.confirmclose": "Una vegada enviat ja no podreu canviar les respostes d'aquest intent.", - "addon.mod_quiz.confirmcontinueoffline": "Aquest intent no s'ha sincronitzat des de {{$a}}. Si heu continuat aquest intent en un altre dispositiu després d'aquesta data es podrien perdre dades.", - "addon.mod_quiz.confirmleavequizonerror": "S'ha produït un error en desar les respostes. Esteu segur que voleu abandonar el qüestionari?", - "addon.mod_quiz.confirmstart": "El qüestionari té un límit de temps de {{$a}}. El temps començarà a comptar des del moment en què inicieu l'intent i s'ha d'enviar abans que el temps acabi. Confirmeu que voleu començar ara?", - "addon.mod_quiz.confirmstartheader": "Qüestionari cronometrat", - "addon.mod_quiz.connectionerror": "S'ha perdut la connexió a la xarxa. (Ha fallat l'autodesament.)\n\nPreneu nota de les respostes introduïdes en aquesta pàgina en els últims minuts i, després, procureu tornar a connectar-vos-hi.\n\nUna vegada que la connexió s'hagi restablert, les vostres respostes s'haurien d'haver desat, i aquest missatge desapareixerà.", - "addon.mod_quiz.continueattemptquiz": "Continua el darrer intent", - "addon.mod_quiz.continuepreview": "Continua la darrera previsualització", - "addon.mod_quiz.errorbehaviournotsupported": "No es pot intentar aquest qüestionari a l'aplicació perquè aquest comportament no està admès:", - "addon.mod_quiz.errordownloading": "S'ha produït un error en descarregar les dades requerides.", - "addon.mod_quiz.errorgetattempt": "S'ha produït un error en obtenir els intents.", - "addon.mod_quiz.errorgetquestions": "S'ha produït un error en obtenir les preguntes.", - "addon.mod_quiz.errorgetquiz": "S'ha produït un error en obtenir el qüestionari.", - "addon.mod_quiz.errorparsequestions": "S'ha produït un error en llegir les preguntes. Intenteu el qüestionari des d'un navegador.", - "addon.mod_quiz.errorquestionsnotsupported": "No es pot intentar aquest qüestionari a l'aplicació perquè conté només preguntes que l'aplicació no admet:", - "addon.mod_quiz.errorrulesnotsupported": "No es pot intentar aquest qüestionari a l'aplicació perquè conté regles d'accés no admeses:", - "addon.mod_quiz.errorsaveattempt": "S'ha produït un error en desar l'intent.", - "addon.mod_quiz.feedback": "Retroacció", - "addon.mod_quiz.finishattemptdots": "Acaba l'intent...", - "addon.mod_quiz.finishnotsynced": "Acabat però no sincronitzat", - "addon.mod_quiz.grade": "Qualificació", - "addon.mod_quiz.gradeaverage": "Qualificació mitjana", - "addon.mod_quiz.gradehighest": "Qualificació més alta", - "addon.mod_quiz.grademethod": "Mètode de qualificació", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Punts", - "addon.mod_quiz.modulenameplural": "Qüestionaris", - "addon.mod_quiz.mustbesubmittedby": "Aquest intent s'ha d'enviar abans de {{$a}}.", - "addon.mod_quiz.noquestions": "Encara no s'han afegit preguntes", - "addon.mod_quiz.noreviewattempt": "No teniu permís per revisar aquest intent.", - "addon.mod_quiz.notyetgraded": "Encara no s'ha avaluat", - "addon.mod_quiz.opentoc": "Obre la navegació per preguntes.", - "addon.mod_quiz.outof": "{{$a.grade}} sobre {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} sobre {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Retroacció global", - "addon.mod_quiz.overdue": "Venciment", - "addon.mod_quiz.overduemustbesubmittedby": "Aquest intent ha arribat al termini de venciment. Ja s'hauria d'haver tramès. Si voleu que aquest qüestionari us sigui qualificat, l'heu de trametre abans de {{$a}}. Si no el trameteu llavors, no es comptabilitzarà cap qualificació d'aquest intent.", - "addon.mod_quiz.preview": "Previsualització", - "addon.mod_quiz.previewquiznow": "Previsualitza el qüestionari ara", - "addon.mod_quiz.question": "Pregunta", - "addon.mod_quiz.quiznavigation": "Navegació pel qüestionari", - "addon.mod_quiz.quizpassword": "Contrasenya del qüestionari", - "addon.mod_quiz.reattemptquiz": "Reintenta el qüestionari", - "addon.mod_quiz.requirepasswordmessage": "Per contestar aquest qüestionari heu de saber la contrasenya", - "addon.mod_quiz.returnattempt": "Torna a l'intent", - "addon.mod_quiz.review": "Revisió", - "addon.mod_quiz.reviewofattempt": "Revisió de l'intent {{$a}}", - "addon.mod_quiz.reviewofpreview": "Revisió de la previsualització", - "addon.mod_quiz.showall": "Mostra totes les preguntes en una pàgina", - "addon.mod_quiz.showeachpage": "Mostra només una pàgina", - "addon.mod_quiz.startattempt": "Inicia l'intent", - "addon.mod_quiz.startedon": "Començat el", - "addon.mod_quiz.stateabandoned": "Mai enviat", - "addon.mod_quiz.statefinished": "Acabat", - "addon.mod_quiz.statefinisheddetails": "Enviat {{$a}}", - "addon.mod_quiz.stateinprogress": "En curs", - "addon.mod_quiz.stateoverdue": "Venciment", - "addon.mod_quiz.stateoverduedetails": "Ha d'enviar-se en {{$a}}", - "addon.mod_quiz.status": "Estat", - "addon.mod_quiz.submitallandfinish": "Envia i acaba", - "addon.mod_quiz.summaryofattempt": "Resum de l'intent", - "addon.mod_quiz.summaryofattempts": "Resum dels vostres intents anteriors", - "addon.mod_quiz.timeleft": "Temps restant", - "addon.mod_quiz.timetaken": "Temps emprat", - "addon.mod_quiz.warningattemptfinished": "S'ha descartat l'intent fora de línia perquè s'ha finalitzat al lloc web o bé no s'ha trobat.", - "addon.mod_quiz.warningdatadiscarded": "S'han descartat alguns intents fora de línia perquè les seves preguntes han estat modificades.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "L'intent no s'ha acabat perquè algunes preguntes fora de línia s'han descartat. Reviseu les respostes i envieu de nou l'intent.", - "addon.mod_quiz.warningquestionsnotsupported": "Aquest qüestionari conté preguntes que no estan suportades per l'aplicació:", - "addon.mod_quiz.yourfinalgradeis": "La vostra qualificació final en aquest qüestionari és {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "S'ha produït un error carregant el contingut.", - "addon.mod_resource.modifieddate": "Modificat {{$a}}", - "addon.mod_resource.modulenameplural": "Fitxers", - "addon.mod_resource.openthefile": "Obre el fitxer", - "addon.mod_resource.uploadeddate": "Penjat dia {{$a}}", - "addon.mod_scorm.asset": "Recurs", - "addon.mod_scorm.assetlaunched": "Recurs - visualitzat", - "addon.mod_scorm.attempts": "Intents", - "addon.mod_scorm.averageattempt": "Mitjana d'intents", - "addon.mod_scorm.browse": "Explora", - "addon.mod_scorm.browsed": "Explorat", - "addon.mod_scorm.browsemode": "Mode d'exploració", - "addon.mod_scorm.cannotcalculategrade": "La qualificació no es pot calcular.", - "addon.mod_scorm.completed": "Completat", - "addon.mod_scorm.contents": "Continguts", - "addon.mod_scorm.dataattemptshown": "Aquesta dada pertany a l'intent número {{number}}.", - "addon.mod_scorm.enter": "Entra", - "addon.mod_scorm.errorcreateofflineattempt": "S'ha produït un error en crear un nou intent fora de línia. Torneu-ho a provar.", - "addon.mod_scorm.errordownloadscorm": "S'ha produït un error en descarregar el paquet SCORM: «{{name}}».", - "addon.mod_scorm.errorgetscorm": "S'ha produït un error en recuperar les dades del paquet SCORM.", - "addon.mod_scorm.errorinvalidversion": "L'aplicació només és compatible amb SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "La descàrrega de paquets SCORM està inhabilitada en aquesta instal·lació de Moodle. Contacteu amb l'administrador.", - "addon.mod_scorm.errornovalidsco": "Aquest paquet SCORM no té una SCO visible per carregar.", - "addon.mod_scorm.errorpackagefile": "L'aplicació només és compatible amb paquets ZIP.", - "addon.mod_scorm.errorsyncscorm": "S'ha produït un error durant la sincronització. Torneu-ho a provar.", - "addon.mod_scorm.exceededmaxattempts": "Has arribat al nombre màxim d'intents", - "addon.mod_scorm.failed": "Fallat", - "addon.mod_scorm.firstattempt": "Primer intent", - "addon.mod_scorm.gradeaverage": "Qualificació mitjana", - "addon.mod_scorm.gradeforattempt": "Qualificació de l'intent", - "addon.mod_scorm.gradehighest": "Qualificació més alta", - "addon.mod_scorm.grademethod": "Mètode de qualificació", - "addon.mod_scorm.gradereported": "Qualificació enviada", - "addon.mod_scorm.gradescoes": "Nombre de Sco", - "addon.mod_scorm.gradesum": "Suma de qualificacions", - "addon.mod_scorm.highestattempt": "Intent més alt", - "addon.mod_scorm.incomplete": "Incomplet", - "addon.mod_scorm.lastattempt": "Darrer intent completat", - "addon.mod_scorm.modulenameplural": "Paquets SCORM", - "addon.mod_scorm.newattempt": "Comença un nou intent", - "addon.mod_scorm.noattemptsallowed": "Nombre d'intents permesos", - "addon.mod_scorm.noattemptsmade": "Nombre d'intents realitzats", - "addon.mod_scorm.notattempted": "No intentat", - "addon.mod_scorm.offlineattemptnote": "Aquest intent conté dades que no han estat sincronitzades encara.", - "addon.mod_scorm.offlineattemptovermax": "Aquest intent no es pot enviar per què heu sobrepassat el nombre màxim d'intents.", - "addon.mod_scorm.organizations": "Organitzacions", - "addon.mod_scorm.passed": "S'ha passat", - "addon.mod_scorm.reviewmode": "Mode de revisió", - "addon.mod_scorm.score": "Puntuació", - "addon.mod_scorm.scormstatusnotdownloaded": "Aquest SCORM no s'ha descarregat. Ho farà automàticament quan l'obriu.", - "addon.mod_scorm.scormstatusoutdated": "Aquest SCORM s'ha modificat des de la darrera descàrrega. Es descarregarà automàticament quan l'obriu.", - "addon.mod_scorm.suspended": "Suspès", - "addon.mod_scorm.toc": "Taula de continguts", - "addon.mod_scorm.warningofflinedatadeleted": "Algunes dades fora de línia de l'intent {{number}} s'han eliminat perquè no poden crear-se al nou intent.", - "addon.mod_scorm.warningsynconlineincomplete": "Alguns intents no poden sincronitzar-se amb el lloc perquè el darrer intent en línia no ha finalitzat. Acabeu l'intent primer.", - "addon.mod_survey.cannotsubmitsurvey": "Hi ha un problema enviant l'enquesta. Si us plau, torneu-ho a intentar.", - "addon.mod_survey.errorgetsurvey": "S'ha produït un error recuperant les dades de l'enquesta.", - "addon.mod_survey.ifoundthat": "Trobat:", - "addon.mod_survey.ipreferthat": "Prefereixo això", - "addon.mod_survey.modulenameplural": "Enquestes", - "addon.mod_survey.responses": "Respostes", - "addon.mod_survey.results": "Resultats", - "addon.mod_survey.surveycompletednograph": "Heu completat aquesta enquesta.", - "addon.mod_url.accessurl": "Vés a la URL", - "addon.mod_url.modulenameplural": "URL", - "addon.mod_url.pointingtourl": "La URL on apunta aquest recurs.", - "addon.mod_wiki.cannoteditpage": "No podeu editar aquesta pàgina.", - "addon.mod_wiki.createpage": "Crea una pàgina", - "addon.mod_wiki.editingpage": "S'està editant aquesta pàgina «{{$a}}»", - "addon.mod_wiki.errorloadingpage": "S'ha produït un error en carregar la pàgina.", - "addon.mod_wiki.errornowikiavailable": "Aquest wiki no té cap contingut encara.", - "addon.mod_wiki.gowikihome": "Vés a l'inici del wiki", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Pàgina nova", - "addon.mod_wiki.newpagetitle": "Títol de la pàgina nova", - "addon.mod_wiki.nocontent": "No hi ha contingut per a aquesta pàgina", - "addon.mod_wiki.notingroup": "No en grup", - "addon.mod_wiki.pageexists": "Aquesta pàgina ja existeix.", - "addon.mod_wiki.pagename": "Nom de la pàgina", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Pàgines wiki", - "addon.mod_wiki.titleshouldnotbeempty": "El títol no pot ser buit", - "addon.mod_wiki.viewpage": "Mostra la pàgina", - "addon.mod_wiki.wikipage": "Pàgina wiki", - "addon.mod_wiki.wrongversionlock": "Un altre usuari ha editat aquesta pàgina wiki que esteu editant i per això el vostre contingut és obsolet.", - "addon.mod_workshop.alreadygraded": "Ja qualificat", - "addon.mod_workshop.areainstructauthors": "Instruccions per a la tramesa", - "addon.mod_workshop.areainstructreviewers": "Instruccions per a l'avaluació", - "addon.mod_workshop.assess": "Avalua", - "addon.mod_workshop.assessedsubmission": "S'ha avaluat la tramesa", - "addon.mod_workshop.assessmentform": "Formulari d'avaluació", - "addon.mod_workshop.assessmentsettings": "Paràmetres de l'avaluació", - "addon.mod_workshop.assessmentstrategynotsupported": "L'estratègia de qualificació {{$a}} no és admesa.", - "addon.mod_workshop.assessmentweight": "Pes de l'avaluació", - "addon.mod_workshop.assignedassessments": "Trameses assignades per avaluar", - "addon.mod_workshop.assignedassessmentsnone": "No teniu cap tramesa per avaluar", - "addon.mod_workshop.conclusion": "Conclusió", - "addon.mod_workshop.createsubmission": "Afegeix una tramesa", - "addon.mod_workshop.deletesubmission": "Elimina la tramesa", - "addon.mod_workshop.editsubmission": "Edita la tramesa", - "addon.mod_workshop.feedbackauthor": "Retroacció per a l'autor", - "addon.mod_workshop.feedbackby": "Retroacció de {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Retroacció per al revisor", - "addon.mod_workshop.givengrades": "Qualificacions donades", - "addon.mod_workshop.gradecalculated": "Qualificació calculada per la tramesa", - "addon.mod_workshop.gradeinfo": "Qualificació: {{$a.received}} de {{$a.max}}", - "addon.mod_workshop.gradeover": "Rectifica la qualificació de la tramesa", - "addon.mod_workshop.gradesreport": "Informe de qualificacions del taller", - "addon.mod_workshop.gradinggrade": "Qualificació de la tasca d'avaluació", - "addon.mod_workshop.gradinggradecalculated": "Qualificació calculada per l'avaluació", - "addon.mod_workshop.gradinggradeof": "Qualificació de la tasca d'avaluació (de {{$a}})", - "addon.mod_workshop.gradinggradeover": "Rectifica la qualificació de l'avaluació", - "addon.mod_workshop.modulenameplural": "Tallers", - "addon.mod_workshop.nogradeyet": "Encara sense qualificar", - "addon.mod_workshop.notassessed": "No avaluat encara", - "addon.mod_workshop.notoverridden": "No rectificada", - "addon.mod_workshop.noyoursubmission": "Encara no heu enviat la vostra feina", - "addon.mod_workshop.overallfeedback": "Retroalimentació global", - "addon.mod_workshop.publishedsubmissions": "Trameses publicades", - "addon.mod_workshop.publishsubmission": "Publica la tramesa", - "addon.mod_workshop.publishsubmission_help": "Les trameses publicades estan disponibles als altres quan el taller està tancat.", - "addon.mod_workshop.reassess": "Torna a avaluar", - "addon.mod_workshop.receivedgrades": "Qualificacions rebudes", - "addon.mod_workshop.submissionattachment": "Adjunció", - "addon.mod_workshop.submissioncontent": "Contingut de la tramesa", - "addon.mod_workshop.submissiondeleteconfirm": "Segur que voleu eliminar la tramesa?", - "addon.mod_workshop.submissiongrade": "Qualificació de la tramesa", - "addon.mod_workshop.submissiongradeof": "Qualificació de la tramesa (de {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Cal que escriviu text o afegiu un fitxer.", - "addon.mod_workshop.submissionrequiredtitle": "Cal que introduïu un títol.", - "addon.mod_workshop.submissionsreport": "Informe de les tasques del taller", - "addon.mod_workshop.submissiontitle": "Títol", - "addon.mod_workshop.switchphase10": "Canvia a la fase de configuració", - "addon.mod_workshop.switchphase20": "Canvia a la fase de tramesa", - "addon.mod_workshop.switchphase30": "Canvia a la fase d'avaluació", - "addon.mod_workshop.switchphase40": "Canvia a la fase d'avaluació de les qualificacions", - "addon.mod_workshop.switchphase50": "Tanca el taller", - "addon.mod_workshop.userplan": "Planificador del taller", - "addon.mod_workshop.userplancurrentphase": "Fase actual", - "addon.mod_workshop.warningassessmentmodified": "L'avaluació s'ha modificat al lloc web.", - "addon.mod_workshop.warningsubmissionmodified": "La tramesa s'ha modificat al lloc web.", - "addon.mod_workshop.weightinfo": "Pes: {{$a}}", - "addon.mod_workshop.yourassessment": "L'avaluació que heu fet", - "addon.mod_workshop.yourassessmentfor": "La vostra avaluació per a: {{$a}}", - "addon.mod_workshop.yourgrades": "Les vostres qualificacions", - "addon.mod_workshop.yoursubmission": "La vostra tramesa", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Comentari per a {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Puntuació per a {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspecte {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Heu de seleccionar una qualificació per aquest aspecte", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Comentari per a: {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspecte {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Comentari per a: {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Puntuació per a {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Afirmació {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Criteri {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Heu d'escollir un d'aquests elements", - "addon.notes.addnewnote": "Afegeix una anotació", - "addon.notes.coursenotes": "Anotacions del curs", - "addon.notes.deleteconfirm": "Voleu suprimir aquesta anotació?", - "addon.notes.eventnotecreated": "S'ha creat l'anotació", - "addon.notes.eventnotedeleted": "S'ha suprimit l'anotació", - "addon.notes.nonotes": "Encara no hi ha anotacions d'aquest tipus", - "addon.notes.note": "Anotació", - "addon.notes.notes": "Anotacions", - "addon.notes.personalnotes": "Anotacions personals", - "addon.notes.publishstate": "Context", - "addon.notes.sitenotes": "Anotacions del lloc", - "addon.notes.userwithid": "Usuari amb id {{id}}", - "addon.notes.warningnotenotsent": "No s'han pogut afegir les notes al curs {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "S'ha produït un error carregant les notificacions", - "addon.notifications.markallread": "Marca-ho tot com a llegit", - "addon.notifications.notificationpreferences": "Preferències de les notificacions", - "addon.notifications.notifications": "Notificacions", - "addon.notifications.playsound": "Reprodueix el so", - "addon.notifications.therearentnotificationsyet": "No hi ha notificacions", - "addon.storagemanager.deletecourse": "Elimina totes les dades del curs", - "addon.storagemanager.deletecourses": "Elimina les dades de tots els cursos", - "addon.storagemanager.deletedatafrom": "Elimina totes les dades de {{name}}", - "addon.storagemanager.info": "Si deseu els arxius al vostre dispositiu fareu l'aplicació més ràpida i que funcioni fora de línia. Es segur eliminar aquests arxius per alliberar espai d'emmagatzematge.", - "addon.storagemanager.managestorage": "Gestiona l'emmagatzematge", - "addon.storagemanager.storageused": "Emmagatzematge de fitxers utilitzat:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Unió dels Emirats Àrabs", - "assets.countries.AF": "Afganistan", - "assets.countries.AG": "Antigua i Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albània", - "assets.countries.AM": "Armènia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antàrtida", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa Americana", - "assets.countries.AT": "Àustria", - "assets.countries.AU": "Austràlia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Illes Aland", - "assets.countries.AZ": "Azerbaitjan", - "assets.countries.BA": "Bòsnia i Hercegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangla Desh", - "assets.countries.BE": "Bèlgica", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgària", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benín", - "assets.countries.BL": "Saint-Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Bolívia, Estat Plurinacional de", - "assets.countries.BQ": "Bonaire, Sint Eustatius i Saba", - "assets.countries.BR": "Brasil", - "assets.countries.BS": "Bahames", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Illa Bouvet", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Bielorússia", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canadà", - "assets.countries.CC": "Illes Cocos (Keeling)", - "assets.countries.CD": "Congo, República Democràtica del", - "assets.countries.CF": "República Centreafricana", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Suïssa", - "assets.countries.CI": "Costa d'Ivori", - "assets.countries.CK": "Illes Cook", - "assets.countries.CL": "Xile", - "assets.countries.CM": "Camerun", - "assets.countries.CN": "Xina", - "assets.countries.CO": "Colòmbia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Cap Verd", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Illa Christmas", - "assets.countries.CY": "Xipre", - "assets.countries.CZ": "Txèquia", - "assets.countries.DE": "Alemanya", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Dinamarca", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "República Dominicana", - "assets.countries.DZ": "Algèria", - "assets.countries.EC": "Equador", - "assets.countries.EE": "Estònia", - "assets.countries.EG": "Egipte", - "assets.countries.EH": "Sàhara Occidental", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Espanya", - "assets.countries.ET": "Etiòpia", - "assets.countries.FI": "Finlàndia", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Illes Malvines (Falkland)", - "assets.countries.FM": "Micronèsia, Estats Federats de", - "assets.countries.FO": "Illes Fèroe", - "assets.countries.FR": "França", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Regne Unit", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Geòrgia", - "assets.countries.GF": "Guaiana Francesa", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Groenlàndia", - "assets.countries.GM": "Gàmbia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadalupe", - "assets.countries.GQ": "Guinea Equatorial", - "assets.countries.GR": "Grècia", - "assets.countries.GS": "Illes Sandwich del Sud i Geòrgia del Sud", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Illa Heard i Illes McDonald", - "assets.countries.HN": "Hondures", - "assets.countries.HR": "Croàcia", - "assets.countries.HT": "Haití", - "assets.countries.HU": "Hongria", - "assets.countries.ID": "Indonèsia", - "assets.countries.IE": "Irlanda", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Illa de Man", - "assets.countries.IN": "Índia", - "assets.countries.IO": "Territori Britànic de l'Oceà Índic", - "assets.countries.IQ": "Iraq", - "assets.countries.IR": "Iran, República Islàmica d'", - "assets.countries.IS": "Islàndia", - "assets.countries.IT": "Itàlia", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordània", - "assets.countries.JP": "Japó", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirguizistan", - "assets.countries.KH": "Cambodja", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comores", - "assets.countries.KN": "Saint Christopher i Nevis", - "assets.countries.KP": "Corea, República Popular Democràtica de", - "assets.countries.KR": "Corea, República de", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Illes Caiman", - "assets.countries.KZ": "Kazakhstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Líban", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Libèria", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lituània", - "assets.countries.LU": "Luxemburg", - "assets.countries.LV": "Letònia", - "assets.countries.LY": "Líbia", - "assets.countries.MA": "Marroc", - "assets.countries.MC": "Mònaco", - "assets.countries.MD": "Moldàvia, República de", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint-Martin (part francesa)", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Illes Marshall", - "assets.countries.MK": "Macedònia del Nord", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongòlia", - "assets.countries.MO": "Macau", - "assets.countries.MP": "Illes Marianes del Nord", - "assets.countries.MQ": "Martinica", - "assets.countries.MR": "Mauritània", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Maurici", - "assets.countries.MV": "Maldives", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mèxic", - "assets.countries.MY": "Malàisia", - "assets.countries.MZ": "Moçambic", - "assets.countries.NA": "Namíbia", - "assets.countries.NC": "Nova Caledònia", - "assets.countries.NE": "Níger", - "assets.countries.NF": "Illa Norfolk", - "assets.countries.NG": "Nigèria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Països Baixos", - "assets.countries.NO": "Noruega", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nova Zelanda", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panamà", - "assets.countries.PE": "Perú", - "assets.countries.PF": "Polinèsia Francesa", - "assets.countries.PG": "Papua Nova Guinea", - "assets.countries.PH": "Filipines", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polònia", - "assets.countries.PM": "Saint-Pierre i Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestina, Estat de", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguai", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Reunion", - "assets.countries.RO": "Romania", - "assets.countries.RS": "Sèrbia", - "assets.countries.RU": "Rússia", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Aràbia Saudita", - "assets.countries.SB": "Salomó", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Suècia", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Santa Helena, Ascensió i Tristan da Cunha", - "assets.countries.SI": "Eslovènia", - "assets.countries.SJ": "Svalbard i Jan Mayen", - "assets.countries.SK": "Eslovàquia", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somàlia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Sudan del sud", - "assets.countries.ST": "Sâo Tome i Príncipe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (part neerlandesa)", - "assets.countries.SY": "Síria", - "assets.countries.SZ": "Eswatini (Swazilàndia)", - "assets.countries.TC": "Illes Turks i Caicos", - "assets.countries.TD": "Txad", - "assets.countries.TF": "Territoris Francesos del Sud", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tailàndia", - "assets.countries.TJ": "Tadjikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor Oriental", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunísia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turquia", - "assets.countries.TT": "Trinitat i Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzània, República Unida de", - "assets.countries.UA": "Ucraïna", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Illes Perifèriques Menors dels EUA", - "assets.countries.US": "Estats Units (EUA)", - "assets.countries.UY": "Uruguai", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Vaticà", - "assets.countries.VC": "Saint Vincent i les Grenadines", - "assets.countries.VE": "Veneçuela, República Bolivariana de", - "assets.countries.VG": "Illes Verges Britàniques", - "assets.countries.VI": "Illes Verges Nord-americanes", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis i Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Iemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Sud-àfrica, República de", - "assets.countries.ZM": "Zàmbia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "Llibre electrònic EPUB", - "assets.mimetypes.application/msword": "Document MS Word", - "assets.mimetypes.application/pdf": "Document PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Còpia de seguretat Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Full de càlcul Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Llibre de treball d'Excel 2007 amb macros habilitades", - "assets.mimetypes.application/vnd.ms-powerpoint": "Presentació PowerPoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Full de càlcul d'OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Plantilla de full de càlcul d'OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Document de text d'OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Plantilla de text d'OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Plantilla de pàgina web d'OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Presentació PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Presentació de diapositives PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Full de càlcul Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Plantilla Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Document Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Presentació de Keynote iWork", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Full de càlcul de Numbers iWork", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Document de Pages iWork", - "assets.mimetypes.application/x-javascript": "Codi font de JavaScript", - "assets.mimetypes.application/x-mspublisher": "Document de Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animació de Flash", - "assets.mimetypes.application/xhtml_xml": "Document XHTML", - "assets.mimetypes.archive": "Fitxer ({{$a.EXT}})", - "assets.mimetypes.audio": "Fitxer d'àudio ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Fitxer", - "assets.mimetypes.group:archive": "Fitxers d'arxiu", - "assets.mimetypes.group:audio": "Fitxers d'àudio", - "assets.mimetypes.group:document": "Documents", - "assets.mimetypes.group:html_audio": "Fitxers d'àudio admesos nativament pels navegadors", - "assets.mimetypes.group:html_track": "Fitxers de seguiment HTML", - "assets.mimetypes.group:html_video": "Fitxers de vídeo admesos nativament pels navegadors", - "assets.mimetypes.group:image": "Fitxers d'imatge", - "assets.mimetypes.group:presentation": "Fitxers de presentació de diapositives", - "assets.mimetypes.group:sourcecode": "Codi font", - "assets.mimetypes.group:spreadsheet": "Fitxers de full de càlcul", - "assets.mimetypes.group:video": "Fitxers de vídeo", - "assets.mimetypes.group:web_audio": "Fitxers d'àudio utilitzats a la web", - "assets.mimetypes.group:web_file": "Fitxers web", - "assets.mimetypes.group:web_image": "Fitxers d'imatge utilitzats a la web", - "assets.mimetypes.group:web_video": "Fitxers de vídeo utilitzats a la web", - "assets.mimetypes.image": "Imatge ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Icona de Windows", - "assets.mimetypes.text/css": "Full d'estil en cascada", - "assets.mimetypes.text/csv": "Valors separats per comes", - "assets.mimetypes.text/html": "Document HTML", - "assets.mimetypes.text/plain": "Fitxer de text", - "assets.mimetypes.text/rtf": "Document RTF", - "assets.mimetypes.text/vtt": "Pista de text de vídeo web", - "assets.mimetypes.video": "Fitxer de vídeo ({{$a.EXT}})", - "core.accounts": "Comptes", - "core.add": "Afegeix", - "core.agelocationverification": "Verificació de l'edat i procedència", - "core.ago": "Fa {{$a}}", - "core.all": "Tots", - "core.allgroups": "Tots els grups", - "core.allparticipants": "Tots els participants", - "core.answer": "Resposta", - "core.answered": "Contestades", - "core.areyousure": "Segur que voleu tirar endavant aquesta acció?", - "core.back": "Enrere", - "core.block.blocks": "Blocs", - "core.browser": "Navegador", - "core.cancel": "Cancel·la", - "core.cannotconnect": "No s'ha pogut connectar", - "core.cannotconnecttrouble": "Sembla que hi ha algun problema per connectar al vostre lloc.", - "core.cannotconnectverify": "Comproveu que l'adreça és correcta.", - "core.cannotdownloadfiles": "La descàrrega d'arxius està deshabilitada al vostre servei Mobile. Contacteu amb l'administrador.", - "core.captureaudio": "Enregistra àudio", - "core.capturedimage": "Fotografia feta.", - "core.captureimage": "Fes una foto", - "core.capturevideo": "Enregistra un vídeo", - "core.category": "Categoria", - "core.choose": "Tria", - "core.choosedots": "Tria...", - "core.clearsearch": "Neteja la cerca", - "core.clicktohideshow": "Feu clic per ampliar o reduir", - "core.clicktoseefull": "Cliqueu per veure el contingut complet.", - "core.close": "Tanca", - "core.comments": "Comentaris", - "core.comments.addcomment": "Afegeix un comentari...", - "core.comments.comments": "Comentaris", - "core.comments.commentscount": "Comentaris ({{$a}})", - "core.comments.commentsnotworking": "No es poden obtenir els comentaris", - "core.comments.deletecommentbyon": "Suprimeix el comentari publicat per {{$a.user}} el {{$a.time}}", - "core.comments.eventcommentcreated": "Comentari creat", - "core.comments.eventcommentdeleted": "Comentari suprimit", - "core.comments.nocomments": "Sense comentaris", - "core.comments.savecomment": "Desa el comentari", - "core.comments.warningcommentsnotsent": "No es poden sincronitzar els comentaris. {{error}}", - "core.commentscount": "Comentaris ({{$a}})", - "core.completion-alt-auto-fail": "Completat: {{$a}} (no han aconseguit assolir la qualificació)", - "core.completion-alt-auto-n": "Incomplet: {{$a}}", - "core.completion-alt-auto-n-override": "No completada: {{$a.modname}} (establert per {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Completat: {{$a}} (han assolit la qualificació)", - "core.completion-alt-auto-y": "Completat: {{$a}}", - "core.completion-alt-auto-y-override": "Completada: {{$a.modname}} (establert per {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Incomplet: {{$a}}. Seleccioneu-lo per marcar-ho com a completat.", - "core.completion-alt-manual-n-override": "No completada: {{$a.modname}} (establert per {{$a.overrideuser}}). Seleccioneu-ho per a marcar-ho com a completada.", - "core.completion-alt-manual-y": "Completat: {{$a}}. Seleccioneu-lo per marcar-ho com a incomplet.", - "core.completion-alt-manual-y-override": "Completada: {{$a.modname}} (establert per {{$a.overrideuser}}). Seleccioneu-ho per a marcar-ho com a no completada.", - "core.confirmcanceledit": "Confirmeu que voleu sortir d'aquesta pàgina? Tots els canvis fets es perdran.", - "core.confirmdeletefile": "Esteu segur que voleu suprimir el fitxer?", - "core.confirmgotabroot": "Segur que voleu tornar a {{name}}?", - "core.confirmgotabrootdefault": "Segur que voleu tornar a la pàgina inicial de la pestanya actual?", - "core.confirmleaveunknownchanges": "Esteu segurs que voleu abandonar aquesta pàgina? Els canvis que tingueu per desar es perdran.", - "core.confirmloss": "N'esteu segur? Es perdran tots els canvis.", - "core.confirmopeninbrowser": "Voleu obrir-ho al navegador?", - "core.considereddigitalminor": "Sou massa jove per crear un compte en aquest lloc.", - "core.content": "Contingut", - "core.contenteditingsynced": "El contingut que esteu editant s'ha sincronitzat.", - "core.contentlinks.chooseaccount": "Escolliu un compte", - "core.contentlinks.chooseaccounttoopenlink": "Escolliu un compte per obrir l'enllaç amb ell.", - "core.contentlinks.confirmurlothersite": "L'enllaç pertany a un altre lloc. Voleu obrir-lo?", - "core.contentlinks.errornoactions": "No s'ha trobat cap acció per fer amb aquest enllaç.", - "core.contentlinks.errornosites": "No s'ha trobat cap lloc per gestionar aquest enllaç.", - "core.contentlinks.errorredirectothersite": "L'adreça URL de redirecció no pot apuntar a un lloc diferent.", - "core.continue": "Continua", - "core.copiedtoclipboard": "S'ha copiat el text al porta-retalls", - "core.copytoclipboard": "Copia al porta-retalls", - "core.course": "Curs", - "core.course.activitydisabled": "La vostra organització ha desactivat aquesta activitat a l'app mòbil.", - "core.course.activitynotyetviewableremoteaddon": "La vostra organització ha instal·lat un complement que encara no és compatible amb l'aplicació.", - "core.course.activitynotyetviewablesiteupgradeneeded": "El Moodle de la vostra organització necessita una actualització.", - "core.course.allsections": "Totes les seccions", - "core.course.askadmintosupport": "Contacteu l'administrador del lloc i digueu-li que voleu fer servir aquesta activitat a Moodle Mobile.", - "core.course.availablespace": "Teniu aproximadament {{available}} d'espai disponible.", - "core.course.cannotdeletewhiledownloading": "No es poden eliminar els fitxers mentre l'activitat es descarrega. Espereu que finalitzi la descàrrega.", - "core.course.confirmdeletemodulefiles": "Confirmeu que voleu eliminar els fitxers d'aquest mòdul?", - "core.course.confirmdownload": "Esteu a punt de descarregar {{size}}.{{availableSpace}} Segur que voleu continuar?", - "core.course.confirmdownloadunknownsize": "No es pot calcular la mida de la descàrrega.{{availableSpace}} Segur que voleu continuar?", - "core.course.confirmdownloadzerosize": "Esteu a punt de descarregar {{availableSpace}}. Segur que voleu continuar?", - "core.course.confirmlimiteddownload": "No esteu connectat a cap xarxa Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Ara descarregareu {{size}} com a mínim.{{availableSpace}} Confirmeu que voleu continuar?", - "core.course.contents": "Continguts", - "core.course.couldnotloadsectioncontent": "No s'ha pogut carregat el contingut de la secció, torneu-ho a provar més tard.", - "core.course.couldnotloadsections": "No s'ha pogut carregar les seccions, si us plau, torneu-ho a provar més tard.", - "core.course.coursesummary": "Resum del curs", - "core.course.downloadcourse": "Descarrega el curs", - "core.course.errordownloadingcourse": "S'ha produït un error en descarregar el curs.", - "core.course.errordownloadingsection": "S'ha produït un error en descarregar la secció.", - "core.course.errorgetmodule": "S'ha produït un error en recuperar les dades del mòdul.", - "core.course.hiddenfromstudents": "Ocult per a l'estudiantat", - "core.course.hiddenoncoursepage": "Disponible, però no es mostra a la pàgina principal del curs", - "core.course.insufficientavailablequota": "No es pot desar la descàrrega per manca d'espai al dispositiu. Es podria estar reservant espai per aplicacions i actualitzacions del sistema. Per descarregar, allibereu espai d'emmagatzematge.", - "core.course.insufficientavailablespace": "Esteu provant de descarregar {{size}}. Això podria deixar sense espai suficient al dispositiu per funcionar correctament. Per descarregar, allibereu espai d'emmagatzematge.", - "core.course.manualcompletionnotsynced": "La compleció manual no està sincronitzada.", - "core.course.nocontentavailable": "No hi ha contingut disponible en aquest moment.", - "core.course.overriddennotice": "La vostra qualificació final en aquesta activitat s'ha modificat manualment.", - "core.course.refreshcourse": "Actualitza el curs", - "core.course.sections": "Seccions", - "core.course.useactivityonbrowser": "Tot i així, podeu fer-la servir al navegador del dispositiu.", - "core.course.warningmanualcompletionmodified": "S'ha modificat la compleció manual d'una activitat a la web.", - "core.course.warningofflinemanualcompletiondeleted": "S'han eliminat algunes dades fora de línia de compleció manual del curs «{{name}}».{{error}}", - "core.coursedetails": "Detalls del curs", - "core.coursenogroups": "No sou membre de cap grup d'aquest curs.", - "core.courses.addtofavourites": "Destaca el curs", - "core.courses.allowguests": "Aquest curs permet entrar als usuaris visitants", - "core.courses.availablecourses": "Cursos disponibles", - "core.courses.cannotretrievemorecategories": "No es poden recuperar categories més enllà del nivell {{$a}}.", - "core.courses.categories": "Categories de cursos", - "core.courses.confirmselfenrol": "Segur que voleu autoinscriure-us en aquest curs?", - "core.courses.courses": "Cursos", - "core.courses.downloadcourses": "Descarrega els cursos", - "core.courses.enrolme": "Inscriu-me", - "core.courses.errorloadcategories": "S'ha produït un error en carregar les categories.", - "core.courses.errorloadcourses": "S'ha produït un error carregant els cursos.", - "core.courses.errorloadplugins": "No s'han carregat correctament els connectors requerits per aquest curs. Reinicieu l'aplicació i torneu-ho a provar.", - "core.courses.errorsearching": "S'ha produït un error durant la cerca.", - "core.courses.errorselfenrol": "S'ha produït un error durant l'autoinscripció.", - "core.courses.filtermycourses": "Filtrar els meus cursos", - "core.courses.frontpage": "Primera plana", - "core.courses.hidecourse": "Amaga el curs", - "core.courses.ignore": "Ignora", - "core.courses.mycourses": "Els meus cursos", - "core.courses.mymoodle": "Tauler", - "core.courses.nocourses": "No hi ha informació de cursos per mostrar.", - "core.courses.nocoursesyet": "No hi ha cursos en aquesta categoria", - "core.courses.nosearchresults": "Cap resultat", - "core.courses.notenroled": "No us heu inscrit en aquest curs", - "core.courses.notenrollable": "No podeu autoinscriure-us en aquest curs.", - "core.courses.password": "Clau d'inscripció", - "core.courses.paymentrequired": "Aquest curs requereix pagament.", - "core.courses.paypalaccepted": "S'accepten pagaments via PayPal", - "core.courses.reload": "Torna a carregar", - "core.courses.removefromfavourites": "Deixa de destacar el curs", - "core.courses.search": "Cerca", - "core.courses.searchcourses": "Cerca cursos", - "core.courses.searchcoursesadvice": "Podeu fer servir el botó de cercar cursos per accedir als cursos com a convidat o autoinscriure-us en cursos que ho permetin.", - "core.courses.selfenrolment": "Autoinscripció", - "core.courses.sendpaymentbutton": "Envia pagament via Paypal", - "core.courses.show": "Mostra el curs", - "core.courses.totalcoursesearchresults": "Total de cursos: {{$a}}", - "core.currentdevice": "Dispositiu actual", - "core.datastoredoffline": "S'han desat les dades al dispositiu perquè no s'han pogut enviar. S'enviaran de manera automàtica més tard.", - "core.date": "Data", - "core.day": "dia", - "core.days": "dies", - "core.decsep": ",", - "core.defaultvalue": "Per defecte ({{$a}})", - "core.delete": "Suprimeix", - "core.deletedoffline": "Eliminat fora de línia", - "core.deleteduser": "Usuari eliminat", - "core.deleting": "S'està eliminant", - "core.description": "Descripció", - "core.desktop": "Sobretaula", - "core.dfdaymonthyear": "DD-MM-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY k[:]mm", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Menor digital", - "core.digitalminor_desc": "Demaneu al vostre tutor que contacti amb la persona següent:", - "core.discard": "Descarta", - "core.dismiss": "Ignora", - "core.displayoptions": "Mostra les opcions", - "core.done": "Fet", - "core.download": "Baixa", - "core.downloaded": "Descarregat", - "core.downloading": "S'està descarregant", - "core.edit": "Edita", - "core.editor.autosavesucceeded": "S'ha desat l'esborrany.", - "core.editor.bold": "Negreta", - "core.editor.clear": "Suprimeix el format", - "core.editor.h3": "Capçalera (gran)", - "core.editor.h4": "Capçalera (mitjana)", - "core.editor.h5": "Capçalera (petita)", - "core.editor.hidetoolbar": "Amaga la barra d'eines", - "core.editor.italic": "Cursiva", - "core.editor.orderedlist": "Llista ordenada", - "core.editor.p": "Paràgraf", - "core.editor.strike": "Barrat", - "core.editor.textrecovered": "Un esborrany d'aquest text s'ha restaurat automàticament.", - "core.editor.toggle": "Commuta l'editor", - "core.editor.underline": "Subratllat", - "core.editor.unorderedlist": "Llista desordenada", - "core.emptysplit": "Aquesta pàgina estarà buida si el panell esquerre està buit o s'està carregant.", - "core.error": "Error", - "core.errorchangecompletion": "S'ha produït un error en carregar l'estat de la compleció. Si us plau torneu a intentar-ho.", - "core.errordeletefile": "S'ha produït un error en eliminar el fitxer. Torneu-ho a provar més tard.", - "core.errordownloading": "S'ha produït un error en baixar el fitxer.", - "core.errordownloadingsomefiles": "S'ha produït un error en descarregar els fitxers del mòdul. Potser s'han perdut alguns fitxers.", - "core.errorfileexistssamename": "Ja hi ha un fitxer amb aquest nom.", - "core.errorinvalidform": "El formulari conté errors. Assegureu-vos que tots els camps requerits estan emplenats i que les dades introduïdes són vàlides.", - "core.errorinvalidresponse": "S'ha rebut una resposta no vàlida. Contacteu amb l'administrador de Moodle si l'error persisteix.", - "core.errorloadingcontent": "S'ha produït un error en carregar el contingut.", - "core.errorofflinedisabled": "La navegació fora de línia està deshabilitada al seu lloc web. Necessita tornar-se a connectar a Internet per fer servir l'aplicació.", - "core.erroropenfilenoapp": "S'ha produït un error en obrir el fitxer: no s'ha trobat cap aplicació per obrir aquest tipus de fitxer.", - "core.erroropenfilenoextension": "S'ha produït un error obrint el fitxer: el fitxer no té extensió.", - "core.erroropenpopup": "Aquesta activitat està intentant obrir una finestra emergent. Aquesta aplicació no ho suporta.", - "core.errorrenamefile": "S'ha produït un error en reanomenar el fitxer. Torneu-ho a provar més tard.", - "core.errorsomedatanotdownloaded": "Si heu baixat aquesta activitat, tingueu en compte que algunes dades no es descarreguen durant el procés de descàrrega per raons de rendiment i ús de dades.", - "core.errorsync": "S'ha produït un error durant la sincronització. Torneu-ho a provar més tard.", - "core.errorsyncblocked": "Aquest/a {{$a}} no es pot sincronitzar ara mateix perquè ja hi ha un procés en marxa. Torneu-ho a provar més tard. Si el problema persisteix, reinicieu l'aplicació.", - "core.errorurlschemeinvalidsite": "Aquest lloc no es pot obrir en aquesta aplicació.", - "core.explanationdigitalminor": "Aquesta informació es demana per determinar si la vostra edat està per sobre de l'edat mínima de consentiment digital. Només amb aquesta edat una persona pot acceptar els termes i les condicions de privadesa, perquè les seves dades siguin legalment desades i processades pel sistema.", - "core.favourites": "Destacats", - "core.filename": "Nom de fitxer", - "core.filenameexist": "Ja existeix un fitxer amb el nom: {{$a}}", - "core.filenotfound": "No s'ha trobat el fitxer", - "core.fileuploader.addfiletext": "Afegeix un fitxer", - "core.fileuploader.audio": "Àudio", - "core.fileuploader.camera": "Càmera", - "core.fileuploader.confirmuploadfile": "Ara pujareu {{size}}. Confirmeu que voleu continuar?", - "core.fileuploader.confirmuploadunknownsize": "No s'ha pogut calcular la mida del fitxer per pujar. Confirmeu que voleu continuar?", - "core.fileuploader.errorcapturingaudio": "S'ha produït un error en capturar l'àudio", - "core.fileuploader.errorcapturingimage": "S'ha produït un error en capturar la imatge.", - "core.fileuploader.errorcapturingvideo": "S'ha produït un error en capturar el vídeo", - "core.fileuploader.errorgettingimagealbum": "S'ha produït un error descarregant la imatge de l'àlbum.", - "core.fileuploader.errormustbeonlinetoupload": "Heu d'estar connectat per pujar arxius.", - "core.fileuploader.errornoapp": "No teniu instal·lada una app per realitzar aquesta acció.", - "core.fileuploader.errorreadingfile": "S'ha produït un error llegint el fitxer.", - "core.fileuploader.errorwhileuploading": "S'ha produït un error penjant el fitxer.", - "core.fileuploader.file": "Arxiu", - "core.fileuploader.filesofthesetypes": "Tipus de fitxers acceptats:", - "core.fileuploader.fileuploaded": "S'ha penjat el fitxer", - "core.fileuploader.invalidfiletype": "El tipus de fitxer {{$a}} no pot ser acceptat", - "core.fileuploader.maxbytesfile": "El fitxer {{$a.file}} és massa gran. La mida màxima de càrrega és {{$a.size}}.", - "core.fileuploader.more": "Més", - "core.fileuploader.photoalbums": "Àlbums de fotos", - "core.fileuploader.readingfile": "S'està llegint un fitxer", - "core.fileuploader.readingfileperc": "S'està llegint el fitxer: {{$a}}%", - "core.fileuploader.selectafile": "Selecciona un fitxer", - "core.fileuploader.uploadafile": "Penja un fitxer", - "core.fileuploader.uploading": "S'està penjant", - "core.fileuploader.uploadingperc": "S'està pujant: {{$a}}%", - "core.fileuploader.video": "Vídeo", - "core.filter": "Filtre", - "core.folder": "Carpeta", - "core.forcepasswordchangenotice": "Heu de canviar la contrasenya abans de continuar", - "core.fulllistofcourses": "Tots els cursos", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Mitjana", - "core.grades.badgrade": "La qualificació proporcionada no és vàlida", - "core.grades.contributiontocoursetotal": "Contribució al total del curs", - "core.grades.feedback": "Retroacció", - "core.grades.grade": "Qualificació", - "core.grades.gradeitem": "Element de qualificació", - "core.grades.grades": "Qualificacions", - "core.grades.lettergrade": "Qualificació per lletres", - "core.grades.nogradesreturned": "No s'han retornat qualificacions", - "core.grades.nooutcome": "Sense competències", - "core.grades.percentage": "Percentatge", - "core.grades.range": "Gamma", - "core.grades.rank": "Posició", - "core.grades.weight": "Ponderació", - "core.group": "Grup", - "core.groupsseparate": "Grups separats", - "core.groupsvisible": "Grups visibles", - "core.h5p.additionallicenseinfo": "Informació addicional sobre la llicència", - "core.h5p.author": "Autor", - "core.h5p.authorcomments": "Comentaris de l'autor", - "core.h5p.authorcommentsdescription": "Comentaris per l'editor del contingut. (Aquest text no es publicarà com a part de la informació dels drets d'autor.)", - "core.h5p.authorname": "Nom de l'autor", - "core.h5p.authorrole": "Rol de l'autor", - "core.h5p.by": "per", - "core.h5p.cancellabel": "Cancel·la", - "core.h5p.ccattribution": "Reconeixement (CC BY)", - "core.h5p.ccattributionnc": "Reconeixement-NoComercial (CC BY-NC)", - "core.h5p.ccattributionncnd": "Reconeixement-NoComercial-SenseObraDerivada (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Reconeixement-NoComercial-CompartirIgual (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Reconeixement-SenseObraDerivada (CC BY-SA)", - "core.h5p.ccattributionsa": "Reconeixement-CompartirIgual (CC BY-SA)", - "core.h5p.ccpdd": "Oferiment al Domini Públic (CC0)", - "core.h5p.changedby": "Modificat per", - "core.h5p.changedescription": "Descripció del canvi", - "core.h5p.changelog": "Registre de canvis", - "core.h5p.changeplaceholder": "Foto retallada, text canviat, etc.", - "core.h5p.close": "Tanca", - "core.h5p.confirmdialogbody": "Confirmeu que voleu continuar. Aquesta acció no es pot desfer.", - "core.h5p.confirmdialogheader": "Confirma l'acció", - "core.h5p.confirmlabel": "Confirma", - "core.h5p.connectionLost": "S'ha perdut la connexió. Els resultats s’emmagatzemaran i s’enviaran quan es restableixi la connexió.", - "core.h5p.connectionReestablished": "S'ha restablert la connexió.", - "core.h5p.contentCopied": "El contingut es copia al porta-retalls", - "core.h5p.contentchanged": "Aquest contingut ha canviat des de l'últim cop que el vau utilitzar.", - "core.h5p.contenttype": "Tipus de contingut", - "core.h5p.copyright": "Drets d'ús", - "core.h5p.copyrightinfo": "Informació de drets d'autor", - "core.h5p.copyrightstring": "Drets d'autor", - "core.h5p.copyrighttitle": "Mostra la informació dels drets d’autor d’aquest contingut.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Data", - "core.h5p.disablefullscreen": "Desactiva la pantalla completa", - "core.h5p.download": "Descarrega", - "core.h5p.downloadtitle": "Descarrega aquest contingut com a fitxer H5P.", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Incrusta", - "core.h5p.embedtitle": "Mostra el codi incrustat d'aquest contingut.", - "core.h5p.errorgetemail": "S'ha produït un error en obtenir el correu de l'usuari. Comproveu la connexió i torneu a provar.", - "core.h5p.fullscreen": "Pantalla completa", - "core.h5p.gpl": "Llicència Pública General (GPL) v3", - "core.h5p.h5ptitle": "Visiteu h5p.org per tal d'obtenir més contingut.", - "core.h5p.hideadvanced": "Amaga les opcions avançades", - "core.h5p.license": "Llicència", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) Oferiment al Domini Públic", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Genèrica", - "core.h5p.licenseCC20": "2.0 Genèrica", - "core.h5p.licenseCC25": "2.5 Genèrica", - "core.h5p.licenseCC30": "3.0 No adaptada", - "core.h5p.licenseCC40": "4.0 Internacional", - "core.h5p.licenseGPL": "Llicència Pública General", - "core.h5p.licenseV1": "Versió 1", - "core.h5p.licenseV2": "Versió 2", - "core.h5p.licenseV3": "Versió 3", - "core.h5p.licensee": "Llicenciat", - "core.h5p.licenseextras": "Extres de la llicència", - "core.h5p.licenseversion": "Versió de la llicència", - "core.h5p.nocopyright": "No hi ha informació de drets d'autor disponible per aquest contingut.", - "core.h5p.offlineDialogBody": "No s'ha pogut enviar la informació sobre la finalització d'aquesta tasca. Comproveu la vostra connexió a Internet.", - "core.h5p.offlineDialogHeader": "S'ha perdut la connexió amb el servidor", - "core.h5p.offlineDialogRetryButtonLabel": "Prova-ho de nou ara", - "core.h5p.offlineDialogRetryMessage": "S'està provant de nou en :num....", - "core.h5p.offlineSuccessfulSubmit": "S'han enviat els resultats amb èxit.", - "core.h5p.offlinedisabled": "El lloc no permet la descàrrega de paquets H5P.", - "core.h5p.originator": "Originador", - "core.h5p.pd": "Domini públic", - "core.h5p.pddl": "Oferiment al Domini Públic i llicència", - "core.h5p.pdm": "Marca de Domini Públic (PDM)", - "core.h5p.play": "Reprodueix H5P", - "core.h5p.resizescript": "Incloeu aquest script al vostre lloc web si voleu dimensionar dinàmicament el contingut incrustat:", - "core.h5p.resubmitScores": "S'està provant d'enviar els resultats emmagatzemats.", - "core.h5p.reuse": "Reutilitza", - "core.h5p.reuseContent": "Reutilitza el contingut", - "core.h5p.reuseDescription": "Reutilitza aquest contingut.", - "core.h5p.showadvanced": "Mostra les opcions avançades", - "core.h5p.showless": "Mostra'n menys", - "core.h5p.showmore": "Mostra'n més", - "core.h5p.size": "Mida", - "core.h5p.source": "Font", - "core.h5p.startingover": "Començareu de bell nou.", - "core.h5p.sublevel": "Subnivell", - "core.h5p.thumbnail": "Miniatura", - "core.h5p.title": "Títol", - "core.h5p.undisclosed": "No revelat", - "core.h5p.year": "Any", - "core.h5p.years": "Any(s)", - "core.h5p.yearsfrom": "Anys (des de)", - "core.h5p.yearsto": "Anys (a)", - "core.hasdatatosync": "Aquest/a {{$a}} té dades fora de línia per sincronitzar.", - "core.help": "Ajuda", - "core.hide": "Oculta", - "core.hour": "hora", - "core.hours": "hores", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Imatge", - "core.imageviewer": "Visor d'imatges", - "core.info": "Informació", - "core.invalidformdata": "El formulari de dades és incorrecte", - "core.labelsep": ":", - "core.lastaccess": "Darrer accés", - "core.lastdownloaded": "Descarregat per última vegada", - "core.lastmodified": "Darrera modificació", - "core.lastsync": "Darrera sincronització.", - "core.layoutgrid": "Graella", - "core.list": "Llista", - "core.listsep": ";", - "core.loading": "S'està carregant", - "core.loadmore": "Carrega'n més", - "core.location": "Localització", - "core.login.auth_email": "Autenticació basada en el correu electrònic", - "core.login.authenticating": "Us esteu autenticant", - "core.login.cancel": "Cancel·la", - "core.login.changepassword": "Canvia la contrasenya", - "core.login.changepasswordbutton": "Obre la pàgina per canviar la contrasenya", - "core.login.changepasswordhelp": "Si teniu problemes per canviar la vostra contrasenya, poseu-vos en contacte amb l'administrador del vostre lloc. Els \"Administradors del lloc\" són les persones que gestionen el Moodle de la vostra escola, universitat/empresa o organització d'aprenentatge. Si no sabeu com contactar amb ells, poseu-vos en contacte amb els vostres professors.", - "core.login.changepasswordinstructions": "No podeu canviar la vostra contrasenya a l'aplicació. Feu clic al botó següent per obrir el lloc en un navegador web per canviar la vostra contrasenya. Tingueu en compte que heu de tancar el navegador després de canviar la contrasenya, ja que no se us redirigirà a l'aplicació.", - "core.login.changepasswordlogoutinstructions": "Si preferiu canviar de lloc o sortir de sessió, feu clic al botó següent:", - "core.login.changepasswordreconnectinstructions": "Feu clic al botó següent per tornar a connectar al lloc. (Tingueu en compte que si no canvieu la contrasenya amb èxit, tornareu a la pantalla anterior).", - "core.login.confirmdeletesite": "Esteu segur que voleu eliminar el lloc {{sitename}}?", - "core.login.connect": "Connecteu-vos amb el vostre lloc", - "core.login.connecttomoodle": "Connecta al Moodle", - "core.login.connecttomoodleapp": "Esteu provant de connectar-vos a un lloc normal de Moodle. Baixeu l'aplicació oficial de Moodle per accedir a aquest lloc.", - "core.login.connecttoworkplaceapp": "Esteu provant de connectar-vos a un lloc de Moodle Workplace. Baixeu l'aplicació Moodle Workplace per accedir a aquest lloc.", - "core.login.contactyouradministrator": "Contacteu amb l'administrador del lloc per a més ajuda.", - "core.login.contactyouradministratorissue": "Demaneu a l'administrador que comprovi el problema següent: {{$a}}", - "core.login.createaccount": "Crea el meu compte", - "core.login.createuserandpass": "Trieu un nom d'usuari i una contrasenya", - "core.login.credentialsdescription": "Introduïu el vostre nom d'usuari i contrasenya per iniciar sessió.", - "core.login.emailconfirmsent": "

S'ha enviat un correu a la vostra adreça {{$a}}

\n

Conté instruccions senzilles per completar el registre.

\n

Si seguiu tenint problemes, contacteu amb l'administrador del lloc.

", - "core.login.emailconfirmsentnoemail": "

S'ha enviat un correu a la vostra adreça electrònica.

Conté instruccions per completar el registre.

Si encara teniu problemes, contacteu amb l'administrador del vostre lloc.

", - "core.login.emailconfirmsentsuccess": "El correu de confirmació s'ha enviat correctament.", - "core.login.emailnotmatch": "Les adreces de correu electrònic no coincideixen", - "core.login.erroraccesscontrolalloworigin": "La crida Cross-Origin que esteu intentant ha sigut rebutjada. Si us plau, visiteu https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium per més informació", - "core.login.errordeletesite": "S'ha produït un error en eliminar aquest lloc. Si us plau, intenteu-ho més tard.", - "core.login.errorexampleurl": "La URL https://campus.example.edu és només un exemple d'un lloc inventat. Feu servir la URL del lloc de la vostra escola o organització.", - "core.login.errorupdatesite": "S'ha produït un error actualitzant el testimoni d'aquest lloc.", - "core.login.faqcannotconnectanswer": "Contacteu l'administrador del vostre lloc.", - "core.login.faqcannotconnectquestion": "He escrit l'adreça correctament, però no em puc connectar.", - "core.login.faqcannotfindmysiteanswer": "Segur que heu escrit el nom correctament? Si encara no el podeu trobar, introduïu l'adreça del lloc. No tot els llocs Moodle es troben en el nostre directori.", - "core.login.faqcannotfindmysitequestion": "No puc trobar el meu lloc", - "core.login.faqsetupsiteanswer": "Aneu a {{$link}} per veure les diferents opcions que teniu per crear el vostre propi lloc Moodle.", - "core.login.faqsetupsitelinktitle": "Inicieu-vos.", - "core.login.faqsetupsitequestion": "Vull crear el meu lloc Moodle.", - "core.login.faqtestappanswer": "Per provar l'aplicació en un lloc Moodle de proves, escriviu «teacher» o «student» al camp «El vostre lloc» i feu clic sobre el botó «Connecteu-vos amb el vostre lloc».", - "core.login.faqtestappquestion": "Només vull provar l'aplicació, què puc fer?", - "core.login.faqwhatisurlanswer": "

Cada organització o escola té una adreça personalitzada pròpia per accedir al seu lloc Moodle. Per trobar l'adreça, feu el següent:

  1. Obriu un navegador i dirigiu-vos a la pàgina d'inici de sessió del Moodle de la vostra organització o escola.
  2. Al capdamunt de la pàgina, a la barra d'adreces, hi trobareu l'URL del vostre lloc Moodle. Per exemple: «campus.exemple.edu».
    {{$image}}
  3. Copieu-la (excepte la part que diu /login i tot allò que ve després), enganxeu-la a l'aplicació Moodle i feu clic sobre «Connecteu-vos amb el vostre lloc!»
  4. A continuació, podreu iniciar sessió al vostre lloc fent servir l'usuari i la contrasenya vostres
  5. ", - "core.login.faqwhatisurlquestion": "Què és l'URL del meu lloc Moodle? Com puc trobar el lloc de la meva escola?", - "core.login.faqwhereisqrcode": "On puc trobar el codi QR?", - "core.login.faqwhereisqrcodeanswer": "

    Si la vostra organització ho ha habilitat, podeu trobar el codi QR al capdavall de la vostra pàgina de perfil del lloc web.

    {{$image}}", - "core.login.findyoursite": "Trobeu el vostre lloc web", - "core.login.firsttime": "És la primera vegada que veniu aquí?", - "core.login.forcepasswordchangenotice": "Heu de canviar la contrasenya abans de continuar", - "core.login.forgotten": "Heu oblidat el nom d'usuari o la contrasenya?", - "core.login.help": "Ajuda", - "core.login.helpmelogin": "

    Hi ha molts llocs Moodle al món. Aquesta aplicació només pot connectar-se als llocs Moodle que hagin habilitat l'accés a l'aplicació Moodle.

    Si no pots connectar-te al teu lloc Moodle, contacta l'administrador del lloc i demana-li que llegeixi http://docs.moodle.org/en/Mobile_app

    Si vols provar l'aplicació en un lloc Moodle de mostra, escriu teacher o student al camp Adreça del lloc i fes clic al Botó Connecta.

    ", - "core.login.instructions": "Instruccions", - "core.login.invalidaccount": "Verifiqueu el vostre nom d'usuari/ària i contrasenya o demaneu a l'administració de Moodle que comprovi la configuració del lloc.", - "core.login.invaliddate": "La data no és vàlida", - "core.login.invalidemail": "L'adreça de correu no és vàlida", - "core.login.invalidmoodleversion": "

    La versió del lloc Moodle no és vàlida. L'aplicació Moodle només suporta versions {{$a}} i posteriors.

    \n

    Contacteu amb l'administrador del lloc i demaneu-li d'actualitzar el Moodle.

    \n

    Els \"Administradors del lloc\" son els encarregats de gestionar el Moodle de la vostra escola/universitat/empresa o organització. Si no sabeu com contactar-hi, pregunteu als vostres professors/entrenadors.

    ", - "core.login.invalidsite": "L'URL del lloc no és vàlid.", - "core.login.invalidtime": "L'hora no és vàlida", - "core.login.invalidurl": "S'ha especificat un URL no vàlid", - "core.login.invalidvaluemax": "El valor màxim és {{$a}}", - "core.login.invalidvaluemin": "El valor mínim és {{$a}}", - "core.login.localmobileunexpectedresponse": "Les característiques addicionals de Moodle Mobile han tornat una resposta inesperada, us heu d'autenticar fent servir el servei estàndard de Mobile.", - "core.login.loggedoutssodescription": "Heu de tornar-vos a autenticar. Cal que entreu al lloc des d'una finestra de navegador.", - "core.login.login": "Inicia la sessió", - "core.login.loginbutton": "Inicia la sessió", - "core.login.logininsiterequired": "Heu d'entrar al lloc a través d'una finestra del navegador.", - "core.login.loginsteps": "Per tenir accés complet a aquest lloc, cal que us creeu abans un compte d'usuari.", - "core.login.missingemail": "Falta l'adreça electrònica", - "core.login.missingfirstname": "Falta el nom", - "core.login.missinglastname": "Falten els cognoms", - "core.login.mobileservicesnotenabled": "El lloc Moodle no té habilitats els serveis mòbils. Contacteu amb l'administració del vostre Moodle si penseu que haurien d'estar habilitats.", - "core.login.mustconfirm": "Cal que confirmeu el vostre compte", - "core.login.newaccount": "Nou compte", - "core.login.notloggedin": "Heu d'entrar al lloc.", - "core.login.onboardingcreatemanagecourses": "Crea i gestiona els teus cursos", - "core.login.onboardingenrolmanagestudents": "Inscriu i gestiona els teus estudiants", - "core.login.onboardinggetstarted": "Inicieu-vos a Moodle", - "core.login.onboardingialreadyhaveasite": "Ja tinc un lloc Moodle", - "core.login.onboardingimalearner": "Sóc un estudiant", - "core.login.onboardingimaneducator": "Sóc un docent", - "core.login.onboardingineedasite": "Necessito un lloc Moodle", - "core.login.onboardingprovidefeedback": "Proporciona retroaccions (feedback) ràpides i adequades als teus usuaris", - "core.login.onboardingtoconnect": "Per connectar-vos a l'aplicació necessitareu un lloc Moodle", - "core.login.onboardingwelcome": "Benvinguts a l'aplicació Moodle!", - "core.login.or": "O", - "core.login.password": "Contrasenya", - "core.login.passwordforgotten": "Heu oblidat la contrasenya", - "core.login.passwordforgotteninstructions2": "Per reiniciar la vostra contrasenya, envieu el vostre nom d'usuari o el vostre correu electrònic a sota. Si el podem trobar a la base de dades, us enviarem un correu amb instruccions per tornar a entrar.", - "core.login.passwordrequired": "Contrasenya obligatòria", - "core.login.policyaccept": "Entès i conforme", - "core.login.policyagree": "Heu d'acceptar la normativa abans d'entrar en aquest lloc. Hi esteu d'acord?", - "core.login.policyagreement": "Acceptació de la normativa d'ús del lloc", - "core.login.policyagreementclick": "Enllaç a l'acceptació de la normativa d'ús del lloc", - "core.login.potentialidps": "Autentiqueu-vos utilitzant el vostre compte a:", - "core.login.profileinvaliddata": "El valor no és vàlid", - "core.login.recaptchachallengeimage": "Imatge del repte de reCAPTCHA", - "core.login.recaptchaexpired": "La verificació ha expirat. Contesta la pregunta de seguretat de nou.", - "core.login.recaptchaincorrect": "La resposta a la pregunta de seguretat no és correcta.", - "core.login.reconnect": "Torna a connectar", - "core.login.reconnectdescription": "El testimoni d'autenticació és invàlid o està caducat, heu de tornar a connectar a la plataforma.", - "core.login.reconnectssodescription": "El testimoni d'autenticació és invàlid o està caducat, heu de tornar a connectar a la plataforma. Heu d'entrar des d'un navegador web.", - "core.login.resendemail": "Reenvia el correu", - "core.login.searchby": "Cerca per:", - "core.login.security_question": "Pregunta de seguretat", - "core.login.selectacountry": "Selecciona un país", - "core.login.selectsite": "Seleccioneu un lloc:", - "core.login.signupplugindisabled": "{{$a}} no està habilitat.", - "core.login.siteaddress": "El vostre lloc", - "core.login.sitehasredirect": "El vostre lloc conté una redirecció HTTP com a mínim. L'aplicació no pot seguir les redireccions, això pot ser un problema que fa que l'aplicació no pugui connectar-se al vostre lloc.", - "core.login.siteinmaintenance": "El vostre lloc està en mode de manteniment", - "core.login.sitepolicynotagreederror": "No s'ha acceptat la política del lloc.", - "core.login.siteurl": "URL del lloc", - "core.login.siteurlrequired": "L'URL del lloc és obligatori, p. ex. http://www.elvostremoodle.abc", - "core.login.startsignup": "Crea un compte d'usuari nou", - "core.login.stillcantconnect": "Encara no hi podeu accedir?", - "core.login.supplyinfo": "Més detalls", - "core.login.username": "Nom d'usuari", - "core.login.usernameoremail": "Introduïu el nom d'usuari o l'adreça de correu", - "core.login.usernamerequired": "El nom d'usuari/ària és necessari", - "core.login.usernotaddederror": "No s'ha afegit l'usuari. Error desconegut", - "core.login.visitchangepassword": "Voleu visitar el lloc per canviar la contrasenya?", - "core.login.webservicesnotenabled": "Els serveis web no estan habilitats al vostre lloc. Contacteu amb l'administració del vostre lloc Moodle si necessiteu ajuda.", - "core.login.youcanstillconnectwithcredentials": "Podeu connectar-vos al lloc introduint el nom d'usuari i la contrasenya vostres.", - "core.login.yourenteredsite": "Connecteu al vostre lloc", - "core.lostconnection": "S'ha perdut la connexió. Necessita tornar a connectar. El testimoni ja no és vàlid.", - "core.mainmenu.changesite": "Canvia de lloc", - "core.mainmenu.help": "Ajuda", - "core.mainmenu.logout": "Surt", - "core.mainmenu.website": "Lloc web", - "core.maxsizeandattachments": "Mida màxima dels fitxers: {{$a.size}}, nombre màxim de fitxers: {{$a.attachments}}", - "core.min": "minut", - "core.mins": "minuts", - "core.misc": "Miscel·lània", - "core.mod_assign": "Tasca", - "core.mod_assignment": "Tasca (2.2) (inhabilitat)", - "core.mod_book": "Llibre", - "core.mod_chat": "Xat", - "core.mod_choice": "Consulta", - "core.mod_data": "Base de dades", - "core.mod_database": "Base de dades", - "core.mod_external-tool": "Eina externa (LTI)", - "core.mod_feedback": "Retroalimentació", - "core.mod_file": "Fitxer", - "core.mod_folder": "Carpeta", - "core.mod_forum": "Fòrum", - "core.mod_glossary": "Glossari", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "Paquet de contingut IMS", - "core.mod_imscp": "Paquet de contingut IMS", - "core.mod_label": "Etiqueta", - "core.mod_lesson": "Lliçó", - "core.mod_lti": "Eina externa (LTI)", - "core.mod_page": "Pàgina", - "core.mod_quiz": "Qüestionari", - "core.mod_resource": "Fitxer", - "core.mod_scorm": "Paquet SCORM", - "core.mod_survey": "Enquesta", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Taller", - "core.moduleintro": "Descripció", - "core.more": "més", - "core.mygroups": "Els meus grups", - "core.name": "Nom", - "core.needhelp": "Necessiteu ajuda?", - "core.networkerroriframemsg": "Aquest contingut no està disponible fora de línia. Comproveu la vostra connexió a internet i torneu-ho provar.", - "core.networkerrormsg": "La connexió a la xarxa no està habilitada o no funciona.", - "core.never": "Mai", - "core.next": "Següent", - "core.no": "No", - "core.nocomments": "Sense comentaris", - "core.nograde": "Sense qualificació", - "core.none": "Cap", - "core.nooptionavailable": "No hi ha opcions disponibles", - "core.nopasswordchangeforced": "No podeu continuar sense canviar la contrasenya.", - "core.nopermissionerror": "Actualment no teniu permisos per a fer això", - "core.nopermissions": "Actualment no teniu permisos per a fer això ({{$a}}).", - "core.noresults": "Sense resultats", - "core.noselection": "Cap selecció", - "core.notapplicable": "n/d", - "core.notenrolledprofile": "El perfil no està disponible perquè aquest usuari no està inscrit en aquest curs.", - "core.notice": "Avís", - "core.notingroup": "Heu de ser part d'un grup per veure aquesta pàgina.", - "core.notsent": "No enviat", - "core.now": "ara", - "core.nummore": "{{$a}} més", - "core.numwords": "{{$a}} paraules", - "core.offline": "Fora de línia", - "core.ok": "OK", - "core.online": "En línia", - "core.openfullimage": "Feu clic per veure la imatge a tamany complert", - "core.openinbrowser": "Obre-ho al navegador", - "core.openmodinbrowser": "Obre {{$a}} al navegador", - "core.othergroups": "Altres grups", - "core.pagea": "Pàgina {{$a}}", - "core.paymentinstant": "Utilitzeu el botó de baix per pagar i inscriure-us.", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telèfon", - "core.pictureof": "Imatge {{$a}}", - "core.previous": "Anterior", - "core.proceed": "Endavant", - "core.pulltorefresh": "Estira per actualitzar", - "core.qrscanner": "Escàner de QR", - "core.question.answer": "Resposta", - "core.question.answersaved": "Resposta desada", - "core.question.cannotdeterminestatus": "No es pot determinar l'estat", - "core.question.certainty": "Certesa", - "core.question.complete": "Completa", - "core.question.correct": "Correcte", - "core.question.errorattachmentsnotsupported": "L'aplicació encara no admet l'adjunció de fitxers.", - "core.question.errorinlinefilesnotsupported": "L'aplicació encara no és compatible amb l'edició de fitxers en línia.", - "core.question.errorquestionnotsupported": "L'aplicació no accepta aquest tipus de pregunta: {{$a}}.", - "core.question.feedback": "Retroacció", - "core.question.howtodraganddrop": "Feu un toc per seleccionar i feu un toc de nou per deixar anar.", - "core.question.incorrect": "Incorrecte", - "core.question.information": "Informació", - "core.question.invalidanswer": "Resposta no vàlida o incompleta", - "core.question.notanswered": "No s'ha respost", - "core.question.notyetanswered": "No s'ha respost encara", - "core.question.partiallycorrect": "Parcialment correcte", - "core.question.questionmessage": "Pregunta {{$a}}: {{$b}}", - "core.question.questionno": "Pregunta {{$a}}", - "core.question.requiresgrading": "Cal puntuar", - "core.quotausage": "Actualment heu utilitzat {{$a.used}} del vostre total de {{$a.total}}", - "core.rating.aggregateavg": "Mitjana de qualificacions", - "core.rating.aggregatecount": "Nombre de qualificacions", - "core.rating.aggregatemax": "Qualificació màxima", - "core.rating.aggregatemin": "Qualificació mínima", - "core.rating.aggregatesum": "Suma de qualificacions", - "core.rating.noratings": "No s'han enviat qualificacions.", - "core.rating.rating": "Puntuació", - "core.rating.ratings": "Puntuacions", - "core.redirectingtosite": "Sereu redireccionats al lloc web.", - "core.refresh": "Refresca", - "core.remove": "Suprimeix", - "core.removefiles": "Elimina els fitxers {{$a}}", - "core.required": "Necessari", - "core.requireduserdatamissing": "En aquest perfil d'usuari hi manquen dades requerides. Si us plau ompliu aquestes dades i torneu a intentar-ho.
    {{$a}}", - "core.resourcedisplayopen": "Obre", - "core.resources": "Recursos", - "core.restore": "Restaura", - "core.restricted": "Restringit", - "core.retry": "Reintenta", - "core.save": "Desa", - "core.savechanges": "Desa els canvis", - "core.scanqr": "Escaneja el codi QR", - "core.search": "Cerca", - "core.searching": "S'està cercant", - "core.searchresults": "Resultats de la cerca", - "core.sec": "segon", - "core.secs": "segons", - "core.seemoredetail": "Feu clic aquí per veure més detalls", - "core.selectacategory": "Seleccioneu una categoria", - "core.selectacourse": "Selecciona un curs", - "core.selectagroup": "Selecciona un grup", - "core.send": "Envia", - "core.sending": "S'està enviant", - "core.serverconnection": "S'ha produït un error de connexió amb el servidor", - "core.settings.about": "Quant a", - "core.settings.appsettings": "Configuració de l'aplicació", - "core.settings.appversion": "Versió de l'aplicació", - "core.settings.cannotsyncoffline": "No es poden sincronitzar dades fora de línia.", - "core.settings.cannotsyncwithoutwifi": "No es pot sincronitzar perquè els paràmetres només permeten sincronitzar utilitzant xarxes Wi-Fi. Connecteu-vos a una Wi-Fi.", - "core.settings.colorscheme": "Esquema de color", - "core.settings.colorscheme-auto": "Automàtic (basat en la configuració del sistema)", - "core.settings.colorscheme-dark": "Fosc", - "core.settings.colorscheme-light": "Clar", - "core.settings.compilationinfo": "Informació de compilació", - "core.settings.copyinfo": "Copia la informació del dispositiu al porta-retalls.", - "core.settings.cordovadevicemodel": "Model del dispositiu Cordova", - "core.settings.cordovadeviceosversion": "Versió d'OS del dispositiu Cordova", - "core.settings.cordovadeviceplatform": "Plataforma del dispositiu Cordova", - "core.settings.cordovadeviceuuid": "UUID del dispositiu Cordova", - "core.settings.cordovaversion": "Versió Cordova", - "core.settings.currentlanguage": "Idioma actual", - "core.settings.debugdisplay": "Visualitza missatges de depuració", - "core.settings.debugdisplaydescription": "Si està habilitat, els missatges d'error mostraran tota la informació possible de l'error.", - "core.settings.deletesitefiles": "Esteu segur que voleu esborrar els fitxers baixats i la memòria cau del lloc '{{sitename}}'? No podreu accedir-hi sense connexió.", - "core.settings.deletesitefilestitle": "Elimina els fitxers del lloc", - "core.settings.deviceinfo": "Informació del dispositiu", - "core.settings.deviceos": "OS del dispositiu", - "core.settings.disableall": "Inhabilita les notificacions", - "core.settings.disabled": "Inhabilitat", - "core.settings.displayformat": "Format de visualització", - "core.settings.enabledownloadsection": "Habilita la descàrrega de seccions", - "core.settings.enablefirebaseanalytics": "Activa les analítiques de Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Si està actiu, l'aplicació recollirà dades d'ús anònimes.", - "core.settings.enablerichtexteditor": "Activa l'editor de text enriquit", - "core.settings.enablerichtexteditordescription": "Si està activat, es farà servir un editor de text en els llocs que ho permetin.", - "core.settings.enablesyncwifi": "Permet la sincronització només quan la Wi-Fi estigui activada", - "core.settings.entriesincache": "{{$a}} entrades a la memòria cau", - "core.settings.errordeletesitefiles": "S'ha produït un error eliminant els fitxers del lloc.", - "core.settings.errorsyncsite": "S'ha produït un error sincronitzant les dades del lloc, comproveu la vostra connexió a internet i torneu-ho provar.", - "core.settings.estimatedfreespace": "Espai lliure estimat", - "core.settings.filesystemroot": "Arrel del fitxer de sistema", - "core.settings.fontsize": "Mida de la lletra", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "Aquest paràmetre està forçat per la configuració del vostre lloc.", - "core.settings.general": "General", - "core.settings.language": "Idioma", - "core.settings.license": "Llicència", - "core.settings.localnotifavailable": "Notificacions locals disponibles", - "core.settings.locationhref": "Webview URL", - "core.settings.locked": "Bloquejat", - "core.settings.loggedin": "En línia", - "core.settings.loggedoff": "Fora de línia", - "core.settings.navigatorlanguage": "Llenguatge del navegador", - "core.settings.navigatoruseragent": "Agent d'usuari del navegador", - "core.settings.networkstatus": "Estat de la connexió a Internet", - "core.settings.opensourcelicenses": "Llicències de codi obert", - "core.settings.preferences": "Preferències", - "core.settings.privacypolicy": "Política de privadesa", - "core.settings.publisher": "Editor", - "core.settings.pushid": "ID de notifications «push».", - "core.settings.reportinbackground": "Informa dels errors automàticament", - "core.settings.screen": "Informació de pantalla", - "core.settings.settings": "Paràmetres", - "core.settings.showdownloadoptions": "Mostra les opcions de descàrrega", - "core.settings.siteinfo": "Informació del lloc", - "core.settings.sites": "Llocs", - "core.settings.spaceusage": "Utilització de l'espai", - "core.settings.spaceusagehelp": "Si s'elimina la informació desada d'un lloc, s'esborrarà tota la informació fora de línia del lloc. Aquesta informació permet d'emprar l'aplicació fora de línia.", - "core.settings.synchronization": "Sincronització", - "core.settings.synchronizenow": "Sincronitza ara", - "core.settings.synchronizenowhelp": "Sincronitzar un lloc envia tota la informació de canvis pendent i tota l'activitat que s'ha fet fora de línia al dispositiu i sincronitza algunes dades com missatges i notificacions.", - "core.settings.syncsettings": "Configuració de la sincronització", - "core.settings.total": "Total", - "core.settings.wificonnection": "Connexió Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "Seleccioneu un compte per desar el fitxer.", - "core.sharedfiles.chooseactionrepeatedfile": "Ja hi ha un fitxer amb aquest nom. Voleu reemplaçar el fitxer existent o canviar-li el nom a «{{$a}}»?", - "core.sharedfiles.errorreceivefilenosites": "No hi ha llocs desats. Afegiu un lloc per poder compartir fitxers amb l'aplicació.", - "core.sharedfiles.nosharedfiles": "No hi ha fitxers compartits desats en aquest lloc.", - "core.sharedfiles.nosharedfilestoupload": "No hi ha fitxers per pujar. Si voleu pujar un fitxer des d'un altra aplicació, busqueu-lo i feu clic al botó «Obre a».", - "core.sharedfiles.rename": "Reanomena", - "core.sharedfiles.replace": "Reemplaça", - "core.sharedfiles.sharedfiles": "Fitxers compartits", - "core.sharedfiles.successstorefile": "S'ha desat el fitxer amb èxit. Podeu seleccionar-lo i pujar-lo als vostres fitxers privats o adjuntar-lo a alguna activitat.", - "core.show": "Mostra", - "core.showless": "Mostra'n menys...", - "core.showmore": "Mostra'n més...", - "core.site": "lloc", - "core.sitehome.sitehome": "Pàgina d'inici", - "core.sitehome.sitenews": "Anuncis del lloc", - "core.sitemaintenance": "S'estan executant tasques de manteniment i el lloc no està disponible", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Omet", - "core.sorry": "Ho sentim...", - "core.sort": "Ordena", - "core.sortby": "Ordena per", - "core.start": "Comença", - "core.storingfiles": "Emmagatzemant fixers", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d de %B de %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d de %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Tramet", - "core.success": "Èxit", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Col·lecció per defecte", - "core.tag.errorareanotsupported": "Aquesta àrea d'etiquetes no està suportada a l'aplicació.", - "core.tag.inalltagcoll": "Arreu", - "core.tag.itemstaggedwith": "{{$a.tagarea}} etiquetada amb «{{$a.tag}}»", - "core.tag.noresultsfor": "Cap resultat per a \"{{$a}}\"", - "core.tag.notagsfound": "No s'han trobat etiquetes coincidents amb «{{$a}}»", - "core.tag.searchtags": "Cerca etiquetes", - "core.tag.showingfirsttags": "S'estan mostrant les {{$a}} etiquetes més populars", - "core.tag.tag": "Etiqueta", - "core.tag.tagarea_course": "Cursos", - "core.tag.tagarea_course_modules": "Activitats i recursos", - "core.tag.tagarea_post": "Entrades del blog", - "core.tag.tagarea_user": "Interessos de l'usuari", - "core.tag.tags": "Etiquetes", - "core.tag.warningareasnotsupported": "No es mostren algunes àrees d'etiquetes perquè no estan suportades per l'aplicació.", - "core.teachers": "Professors", - "core.thereisdatatosync": "Hi ha {{$a}} fora de línia per sincronitzar.", - "core.thisdirection": "ltr", - "core.time": "Hora", - "core.timesup": "Temps esgotat", - "core.today": "Avui", - "core.tryagain": "Torna-ho a provar", - "core.twoparagraphs": "{{p1}}

    {{p2}}", - "core.uhoh": "Oh no!", - "core.unexpectederror": "S'ha produït un error inesperat. Tanqueu l'aplicació i torneu a obrir-la per a reintentar-ho.", - "core.unicodenotsupported": "Aquest lloc no accepta texte unicode com ara els emojis. El text s'enviarà sense aquests caràcters.", - "core.unicodenotsupportedcleanerror": "En eliminar els caràcters unicode ha quedat un text buit.", - "core.unknown": "Desconegut", - "core.unlimited": "Il·limitat", - "core.unzipping": "S'està descomprimint", - "core.updaterequired": "Cal que actualitzeu l'aplicació", - "core.updaterequireddesc": "Actualitzeu l'aplicació a la versió {{$a}}", - "core.upgraderunning": "El lloc s'està actualitzant. Proveu-ho més tard.", - "core.user": "Usuari", - "core.user.address": "Adreça", - "core.user.city": "Població", - "core.user.contact": "Contacte", - "core.user.country": "País", - "core.user.description": "Descripció", - "core.user.details": "Detalls", - "core.user.detailsnotavailable": "No tens accés als detalls d'aquest usuari.", - "core.user.editingteacher": "Professor", - "core.user.email": "Adreça electrònica", - "core.user.emailagain": "Correu electrònic (una altra vegada)", - "core.user.errorloaduser": "S'ha produït un error en carregar l'usuari.", - "core.user.firstname": "Nom", - "core.user.interests": "Interessos", - "core.user.lastname": "Cognoms", - "core.user.manager": "Gestor", - "core.user.newpicture": "Imatge nova", - "core.user.noparticipants": "No s'ha trobat cap participant en aquest curs", - "core.user.participants": "Participants", - "core.user.phone1": "Telèfon", - "core.user.phone2": "Telèfon mòbil", - "core.user.roles": "Rols", - "core.user.sendemail": "Correu-e", - "core.user.student": "Estudiant", - "core.user.teacher": "Professor no editor", - "core.user.webpage": "Pàgina web", - "core.userdeleted": "S'ha suprimit aquest compte d'usuari", - "core.userdetails": "Més dades de l'usuari", - "core.usernotfullysetup": "La configuració de l'usuari no s'ha completat", - "core.users": "Usuaris", - "core.view": "Visualització", - "core.viewcode": "Mostra el codi", - "core.vieweditor": "Mostra l'editor", - "core.viewembeddedcontent": "Visualitza el contingut encastat", - "core.viewprofile": "Mostra el perfil", - "core.warningofflinedatadeleted": "Les dades fora de línia de {{component}} «{{name}}» s'han eliminat. {{error}}", - "core.whatisyourage": "Quants anys teniu?", - "core.wheredoyoulive": "A quin país viviu?", - "core.whoissiteadmin": "Els «administradors del lloc» són les persones que gestionen el lloc Moodle de la vostra escola/universitat/empresa o organització educativa. Si no sabeu com contactar-hi, demaneu-ho als vostres professors/formadors.", - "core.whoops": "Ui!", - "core.whyisthishappening": "I això per què passa?", - "core.whyisthisrequired": "Per què això és necessari?", - "core.wsfunctionnotavailable": "La funció de webservice no està disponible.", - "core.year": "any", - "core.years": "anys", - "core.yes": "Sí", - "core.youreoffline": "Esteu fora de línia", - "core.youreonline": "Torneu a estar en línia" -} \ No newline at end of file diff --git a/src/assets/lang/cs.json b/src/assets/lang/cs.json deleted file mode 100644 index 990c89bce..000000000 --- a/src/assets/lang/cs.json +++ /dev/null @@ -1,2170 +0,0 @@ -{ - "addon.badges.alignment": "Kompetence", - "addon.badges.badgedetails": "Detaily odznaku", - "addon.badges.badges": "Odznaky", - "addon.badges.bendorsement": "Schválení", - "addon.badges.claimcomment": "Komentář ke schválení", - "addon.badges.claimid": "URL prohlášení", - "addon.badges.contact": "Kontakt", - "addon.badges.dateawarded": "Datum udělení", - "addon.badges.expired": "Platnost vypršela", - "addon.badges.expirydate": "Datum vypršení platnosti", - "addon.badges.imageauthoremail": "E-mail autora obrázku", - "addon.badges.imageauthorname": "Jméno autora obrázku", - "addon.badges.imageauthorurl": "Adresa URL autora obrázku", - "addon.badges.imagecaption": "Titulek obrázku", - "addon.badges.issuancedetails": "Vypršení platnosti odznaku", - "addon.badges.issuerdetails": "Podrobnosti o vydavateli", - "addon.badges.issueremail": "E-mail", - "addon.badges.issuername": "Jméno vydavatele", - "addon.badges.issuerurl": "URL schvalovatele", - "addon.badges.language": "Jazyk", - "addon.badges.noalignment": "Tento odznak nemá uvedeny žádné externí dovednosti nebo normy.", - "addon.badges.nobadges": "Žádné odznaky nejsou k dispozici.", - "addon.badges.norelated": "Tento odznak nemá žádné související odznaky.", - "addon.badges.recipientdetails": "Podrobnosti o příjemci", - "addon.badges.relatedbages": "Související odznaky", - "addon.badges.version": "Verze", - "addon.badges.warnexpired": "(Platnost odznaku vypršela!)", - "addon.block_activitymodules.pluginname": "Činnosti", - "addon.block_activityresults.pluginname": "Výsledky činnosti", - "addon.block_badges.pluginname": "Nejnovější odznaky", - "addon.block_blogmenu.pluginname": "Nástroje blogu", - "addon.block_blogrecent.pluginname": "Nové příspěvky blogů", - "addon.block_blogtags.pluginname": "Štítky blogu", - "addon.block_calendarmonth.pluginname": "Kalendář", - "addon.block_calendarupcoming.pluginname": "Nadcházející události", - "addon.block_comments.pluginname": "Komentáře", - "addon.block_completionstatus.pluginname": "Stav absolvování kurzu", - "addon.block_glossaryrandom.pluginname": "Heslo ze slovníku", - "addon.block_learningplans.pluginname": "Studijní plány", - "addon.block_myoverview.all": "Všechny (kromě odstraněných)", - "addon.block_myoverview.allincludinghidden": "Vše", - "addon.block_myoverview.favourites": "Označené hvězdičkou", - "addon.block_myoverview.future": "Budoucí", - "addon.block_myoverview.hiddencourses": "Odstraněné z přehledu", - "addon.block_myoverview.inprogress": "Probíhající", - "addon.block_myoverview.lastaccessed": "Poslední přístup", - "addon.block_myoverview.morecourses": "Další kurzy", - "addon.block_myoverview.nocourses": "Žádné kurzy", - "addon.block_myoverview.past": "Minulé", - "addon.block_myoverview.pluginname": "Přehled kurzů", - "addon.block_myoverview.shortname": "Krátký název", - "addon.block_myoverview.title": "Název kurzu", - "addon.block_newsitems.pluginname": "Poslední oznámení", - "addon.block_onlineusers.pluginname": "Připojení uživatelé", - "addon.block_privatefiles.pluginname": "Osobní soubory", - "addon.block_recentactivity.pluginname": "Nedávná činnost", - "addon.block_recentlyaccessedcourses.nocourses": "Žádné kurzy", - "addon.block_recentlyaccessedcourses.pluginname": "Nedávno navštívené kurzy", - "addon.block_recentlyaccesseditems.noitems": "Žádné nedávné položky", - "addon.block_recentlyaccesseditems.pluginname": "Nedávno navštívené položky", - "addon.block_rssclient.pluginname": "Čtečka RSS", - "addon.block_selfcompletion.pluginname": "Potvrzení absolvování", - "addon.block_sitemainmenu.pluginname": "Hlavní nabídka", - "addon.block_starredcourses.nocourses": "Žádné kurzy s hvězdičkou", - "addon.block_starredcourses.pluginname": "Kurzy s hvězdičkou", - "addon.block_tags.pluginname": "Štítky", - "addon.block_timeline.duedate": "Termín ukončení", - "addon.block_timeline.next30days": "Dalších 30 dní", - "addon.block_timeline.next3months": "Další 3 měsíce", - "addon.block_timeline.next6months": "Dalších 6 měsíců", - "addon.block_timeline.next7days": "Dalších 7 dní", - "addon.block_timeline.nocoursesinprogress": "Žádné aktivní kurzy", - "addon.block_timeline.noevents": "Žádné činnosti s termínem ukončení", - "addon.block_timeline.overdue": "Zpožděné", - "addon.block_timeline.pluginname": "Časová osa", - "addon.block_timeline.sortbycourses": "Seřadit podle kurzů", - "addon.block_timeline.sortbydates": "Seřadit podle data", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Příspěvky v blogu", - "addon.blog.errorloadentries": "Chyba při načítání položek blogu.", - "addon.blog.linktooriginalentry": "Odkaz na původní příspěvek", - "addon.blog.noentriesyet": "Nejsou k dispozici žádné viditelné příspěvky", - "addon.blog.publishtonoone": "Pouze vám (pracovní verze)", - "addon.blog.publishtosite": "Všem uživatelům těchto stránek", - "addon.blog.publishtoworld": "Komukoliv v Internetu", - "addon.blog.showonlyyourentries": "Zobrazit pouze svoje záznamy", - "addon.blog.siteblogheading": "Blog stránek", - "addon.calendar.allday": "Celý den", - "addon.calendar.calendar": "Kalendář", - "addon.calendar.calendarevent": "Událost Kalendáře", - "addon.calendar.calendarevents": "Události Kalendáře", - "addon.calendar.calendarreminders": "Připomenutí kalendáře", - "addon.calendar.categoryevents": "Události kategorie", - "addon.calendar.confirmeventdelete": "Jste si jisti, že chcete odstranit tuto událost \"{{$a}}\" ?", - "addon.calendar.confirmeventseriesdelete": "Akce \"{{$a.name}}\" je součástí série. Chcete tuto událost smazat nebo všechny události {{$a.count}} v sérii?", - "addon.calendar.courseevents": "Události kurzu", - "addon.calendar.currentmonth": "Aktuální měsíc", - "addon.calendar.daynext": "Následující den", - "addon.calendar.dayprev": "Předchozí den", - "addon.calendar.defaultnotificationtime": "Výchozí čas oznámení", - "addon.calendar.deleteallevents": "Odstranit všechny události", - "addon.calendar.deleteevent": "Odstranit událost", - "addon.calendar.deleteoneevent": "Odstranit tuto událost", - "addon.calendar.durationminutes": "Délka trvání v minutách", - "addon.calendar.durationnone": "Nemá trvání", - "addon.calendar.durationuntil": "Až do", - "addon.calendar.editevent": "Úprava události", - "addon.calendar.errorloadevent": "Chyba při načítání události.", - "addon.calendar.errorloadevents": "Chyba při načítání událostí.", - "addon.calendar.eventcalendareventdeleted": "Událost kalendáře odstraněna", - "addon.calendar.eventduration": "Délka trvání", - "addon.calendar.eventendtime": "Končí", - "addon.calendar.eventkind": "Typ události", - "addon.calendar.eventname": "Název události", - "addon.calendar.eventstarttime": "Začíná", - "addon.calendar.eventtype": "Typ události", - "addon.calendar.fri": "Pá", - "addon.calendar.friday": "Pátek", - "addon.calendar.gotoactivity": "Přejít na aktivitu", - "addon.calendar.groupevents": "Skupinové události", - "addon.calendar.invalidtimedurationminutes": "Neplatná délka trvání. Vložte délku trvání v minutách nebo zvolte \"Nemá trvání\"", - "addon.calendar.invalidtimedurationuntil": "Datum a čas, do kdy trvá událost, předchází začátek události.", - "addon.calendar.mon": "Po", - "addon.calendar.monday": "Pondělí", - "addon.calendar.monthlyview": "Zobrazit měsíce", - "addon.calendar.newevent": "Nová událost", - "addon.calendar.noevents": "Nejsou žádné události", - "addon.calendar.nopermissiontoupdatecalendar": "Promiňte, ale nemáte oprávnění aktualizovat událost v kalendáři.", - "addon.calendar.reminders": "Připomenutí", - "addon.calendar.repeatedevents": "Opakující se události", - "addon.calendar.repeateditall": "Použít změny na další události (celkem {{$a}}) v této opakující se sérii.", - "addon.calendar.repeateditthis": "Použít změny pouze na tuto událost", - "addon.calendar.repeatevent": "Opakovat tuto událost", - "addon.calendar.repeatweeksl": "Opakovat každý týden, vytvořit celkem", - "addon.calendar.sat": "So", - "addon.calendar.saturday": "Sobota", - "addon.calendar.setnewreminder": "Nastavit novou připomínku", - "addon.calendar.siteevents": "Události serveru", - "addon.calendar.sun": "Ne", - "addon.calendar.sunday": "Neděle", - "addon.calendar.thu": "Čt", - "addon.calendar.thursday": "Čtvrtek", - "addon.calendar.today": "Dnes", - "addon.calendar.tomorrow": "Zítra", - "addon.calendar.tue": "Út", - "addon.calendar.tuesday": "Úterý", - "addon.calendar.typecategory": "Událost kategorie", - "addon.calendar.typeclose": "Ukončit událost", - "addon.calendar.typecourse": "Událost kurzu", - "addon.calendar.typedue": "Důvod události", - "addon.calendar.typegradingdue": "Známkovat důvod události", - "addon.calendar.typegroup": "Skupinová událost", - "addon.calendar.typeopen": "Otevřít událost", - "addon.calendar.typesite": "Globální událost", - "addon.calendar.typeuser": "Osobní událost", - "addon.calendar.upcomingevents": "Nadcházející události", - "addon.calendar.userevents": "Osobní události", - "addon.calendar.wed": "St", - "addon.calendar.wednesday": "Středa", - "addon.calendar.when": "Když", - "addon.calendar.yesterday": "Včera", - "addon.competency.activities": "Činnosti", - "addon.competency.competencies": "Kompetence", - "addon.competency.competenciesmostoftennotproficientincourse": "Kompetence v tomto kurzu často nejsou dosaženy", - "addon.competency.coursecompetencies": "Kompetence kurzu", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Hodnocení kompetencí v tomto kurzu nemají vliv na studijní plány.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Hodnocení kompetencí v tomto kurzu jsou okamžitě aktualizovány v studijních plánech.", - "addon.competency.crossreferencedcompetencies": "Průřezové kompetence", - "addon.competency.duedate": "Termín dokončení", - "addon.competency.errornocompetenciesfound": "Nebyly nalezeny žádné kompetence", - "addon.competency.evidence": "Evidence", - "addon.competency.evidence_competencyrule": "Bylo splněno pravidlo kompetence.", - "addon.competency.evidence_coursecompleted": "Kurz \"{{$a}}\" byl dokončen.", - "addon.competency.evidence_coursemodulecompleted": "Byla dokončena aktivita \"{{$a}}\".", - "addon.competency.evidence_courserestored": "Hodnocení bylo obnoveno spolu s \"{{$a}}\".", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Evidence předchozího studia \"{{$a}}\" byla připojena.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Evidence předchozího studia \"{{$a}}\" byla odpojena.", - "addon.competency.evidence_manualoverride": "Hodnocení kompetence bylo nastaveno manuálně.", - "addon.competency.evidence_manualoverrideincourse": "Hodnocení kompetence bylo nastaveno manuálně v kurzu \"{{$a}}\".", - "addon.competency.evidence_manualoverrideinplan": "Hodnocení kompetence bylo nastaveno manuálně ve studijním plánu \"{{$a}}\".", - "addon.competency.learningplancompetencies": "Kompetence studijního plánu", - "addon.competency.learningplans": "Studijní plány", - "addon.competency.myplans": "Mé studijní plány", - "addon.competency.noactivities": "Žádné činnosti", - "addon.competency.nocompetencies": "Žádné kompetence", - "addon.competency.nocompetenciesincourse": "V tomto kurzu nebyly připojeny žádné kompetence.", - "addon.competency.nocrossreferencedcompetencies": "K této kompetenci nebyly spojeny další průřezové kompetence.", - "addon.competency.noevidence": "Bez záznamu", - "addon.competency.noplanswerecreated": "Nebyly vytvořeny žádné studijní plány.", - "addon.competency.nouserplanswithcompetency": "tuto kompetenci neobsahuje žádný studijní plán.", - "addon.competency.path": "Cesta:", - "addon.competency.planstatusactive": "Aktivní", - "addon.competency.planstatuscomplete": "Dokončeno", - "addon.competency.planstatusdraft": "Návrh", - "addon.competency.planstatusinreview": "V revizi", - "addon.competency.planstatuswaitingforreview": "Čekání na revizi", - "addon.competency.proficient": "Splněno", - "addon.competency.progress": "Pokrok", - "addon.competency.rating": "Hodnocení", - "addon.competency.reviewstatus": "Stav revize", - "addon.competency.status": "Stav", - "addon.competency.template": "Šablona studijního plánu", - "addon.competency.uponcoursecompletion": "Po absolvování kurzu:", - "addon.competency.usercompetencystatus_idle": "Nečinný", - "addon.competency.usercompetencystatus_inreview": "V revizi", - "addon.competency.usercompetencystatus_waitingforreview": "Čekání na revizi", - "addon.competency.userplans": "Studijní plány", - "addon.competency.xcompetenciesproficientoutofy": "máte splněno {{$a.x}} z {{$a.y}} kompetencí", - "addon.competency.xcompetenciesproficientoutofyincourse": "V tomto kurzu máte splněno {{$a.x}} z {{$a.y}} kompetencí.", - "addon.coursecompletion.complete": "Absolvováno", - "addon.coursecompletion.completecourse": "Prohlásit kurz za absolvovaný", - "addon.coursecompletion.completed": "Splněno", - "addon.coursecompletion.completiondate": "Datum ukončení", - "addon.coursecompletion.completionmenuitem": "Absolvování", - "addon.coursecompletion.couldnotloadreport": "Nelze načíst zprávu o absolvování kurzu. Zkuste to prosím později.", - "addon.coursecompletion.coursecompletion": "Absolvování kurzu", - "addon.coursecompletion.criteria": "Podmínky", - "addon.coursecompletion.criteriagroup": "Skupina podmínek", - "addon.coursecompletion.criteriarequiredall": "Všechny podmínky musí být splněny", - "addon.coursecompletion.criteriarequiredany": "Jakákoli z podmínek musí být splněna", - "addon.coursecompletion.inprogress": "Probíhá", - "addon.coursecompletion.manualselfcompletion": "Označení absolvování kurzu samotným studentem", - "addon.coursecompletion.nottracked": "Momentálně neprobíhá zaznamenávání vašeho postupu tímto kurzem", - "addon.coursecompletion.notyetstarted": "Zatím nezačalo", - "addon.coursecompletion.pending": "Čeká", - "addon.coursecompletion.required": "Vyžadováno", - "addon.coursecompletion.requiredcriteria": "Vyžadované podmínky", - "addon.coursecompletion.requirement": "Požadavek", - "addon.coursecompletion.status": "Stav", - "addon.coursecompletion.viewcoursereport": "Zobrazit přehled kurzu", - "addon.files.couldnotloadfiles": "Seznam souborů, které nelze načíst .", - "addon.files.emptyfilelist": "Žádný soubor k zobrazení.", - "addon.files.erroruploadnotworking": "Bohužel v současné době není možné nahrávat na stránky vašeho Moodle.", - "addon.files.files": "Soubory", - "addon.files.privatefiles": "Osobní soubory", - "addon.files.sitefiles": "Soubory stránek", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Konfigurovat zařízení", - "addon.messages.acceptandaddcontact": "Schválit a přidat do kontaktů", - "addon.messages.addcontact": "Přidat kontakt", - "addon.messages.addcontactconfirm": "Opravdu chcete do svých kontaktů přidat {{$a}}?", - "addon.messages.addtofavourites": "Konverzaci označit hvězdičkou", - "addon.messages.addtoyourcontacts": "Přidat do kontaktů", - "addon.messages.blocknoncontacts": "Blokuj všechny nové zprávy od uživatelů, které nemám v seznamu kontaktů", - "addon.messages.blockuser": "Blokovat uživatele", - "addon.messages.blockuserconfirm": "Opravdu chcete blokovat {{$a}}?", - "addon.messages.contactableprivacy": "Přijmout zprávy od:", - "addon.messages.contactableprivacy_coursemember": "Moje kontakty a kdokoliv v mých kurzech", - "addon.messages.contactableprivacy_onlycontacts": "Pouze mé kontakty", - "addon.messages.contactableprivacy_site": "Kdokoliv na webu", - "addon.messages.contactblocked": "Kontakt blokován", - "addon.messages.contactlistempty": "Seznam kontaktů je prázdný", - "addon.messages.contactname": "Jméno kontaktu", - "addon.messages.contactrequestsent": "Požadavek na kontakt byl odeslán", - "addon.messages.contacts": "Kontakty", - "addon.messages.conversationactions": "Nabídka akcí konverzace", - "addon.messages.decline": "Odmítnout", - "addon.messages.deleteallconfirm": "Opravdu chcete vymazat celou tuto konverzaci? Ostatním účastníkům konverzace nebude smazána.", - "addon.messages.deleteallselfconfirm": "Opravdu chcete odstranit celou tuto osobní konverzaci?", - "addon.messages.deleteconversation": "Odstranit konverzaci", - "addon.messages.deleteforeveryone": "Odstranit pro mě a pro všechny ostatní", - "addon.messages.deletemessage": "Odstranit zprávu", - "addon.messages.deletemessageconfirmation": "Opravdu chcete tuto zprávu odstranit? Bude smazána pouze z historie zpráv a bude nadále viditelná uživatelem, který poslal nebo přijal zprávu.", - "addon.messages.errordeletemessage": "Chyba při odstraňování zprávy.", - "addon.messages.errorwhileretrievingcontacts": "Chyba při načítání kontaktů ze serveru.", - "addon.messages.errorwhileretrievingdiscussions": "Chyba při načítání diskusí ze serveru.", - "addon.messages.errorwhileretrievingmessages": "Chyba při načítání zpráv ze serveru.", - "addon.messages.errorwhileretrievingusers": "Chyba při načítání uživatelů ze serveru.", - "addon.messages.groupconversations": "Skupina", - "addon.messages.groupinfo": "Skupinové informace", - "addon.messages.individualconversations": "Soukromé", - "addon.messages.info": "Uživatelské informace", - "addon.messages.isnotinyourcontacts": "{{$a}} není ve vašich kontaktech", - "addon.messages.message": "Zpráva", - "addon.messages.messagenotsent": "Zpráva nebyla odeslána. Zkuste to prosím později.", - "addon.messages.messagepreferences": "Nastavení zpráv", - "addon.messages.messages": "Zprávy", - "addon.messages.muteconversation": "Ztlumit", - "addon.messages.mutedconversation": "Zastavená konverzace", - "addon.messages.newmessage": "Nová zpráva", - "addon.messages.newmessages": "Nové zprávy", - "addon.messages.nocontactrequests": "Žádné žádosti o kontakt", - "addon.messages.nocontactsgetstarted": "Žádné kontakty", - "addon.messages.nofavourites": "Žádné konverzace označené hvězdičkou", - "addon.messages.nogroupconversations": "Žádné skupinové konverzace", - "addon.messages.noindividualconversations": "Žádné soukromé konverzace", - "addon.messages.nomessagesfound": "Nebyly nalezeny žádné zprávy", - "addon.messages.noncontacts": "Zablokovat zprávy", - "addon.messages.nousersfound": "Nebyl nalezen žádný uživatel", - "addon.messages.numparticipants": "{{$a}} účastníků", - "addon.messages.removecontact": "Odebrat kontakt", - "addon.messages.removecontactconfirm": "Opravdu chcete z kontaktů odstranit {{$a}}?", - "addon.messages.removefromfavourites": "Odstranit hvězdičku z konverzace", - "addon.messages.removefromyourcontacts": "Odebrat z kontaktů", - "addon.messages.requests": "Žádosti", - "addon.messages.requirecontacttomessage": "Musíte požádat {{$a}}, aby vás přidal jako kontakt pro zasílání zpráv.", - "addon.messages.searchcombined": "Hledání osob a zpráv", - "addon.messages.selfconversation": "Osobní prostor", - "addon.messages.selfconversationdefaultmessage": "Uložení konceptu zpráv, odkazů, poznámek atd. pro pozdější přístup.", - "addon.messages.sendcontactrequest": "Odeslat žádost o kontakt", - "addon.messages.showdeletemessages": "Zobrazit odstraněné zprávy", - "addon.messages.type_blocked": "Blokováno", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Výsledky hledání", - "addon.messages.type_strangers": "Další", - "addon.messages.unabletomessage": "Tomuto uživateli nemůžete odeslat zprávu", - "addon.messages.unblockuser": "Odblokovat uživatele", - "addon.messages.unblockuserconfirm": "Opravdu chcete odblokovat {{$a}}?", - "addon.messages.unmuteconversation": "Zesílit", - "addon.messages.useentertosend": "Pro odeslání použijte klávesu enter", - "addon.messages.useentertosenddescdesktop": "Pokud je zakázáno, můžete zprávu odeslat pomocí kláves Ctrl + Enter.", - "addon.messages.useentertosenddescmac": "Pokud je zakázáno, můžete zprávu odeslat pomocí kláves Ctrl + Enter.", - "addon.messages.userwouldliketocontactyou": "{{$a}} by vás chtěl kontaktovat", - "addon.messages.warningconversationmessagenotsent": "Nelze odeslat zprávu(y) do konverzace {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Nelze odeslat zprávu (y) uživateli {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Rád by vás kontaktoval", - "addon.messages.you": "Vy:", - "addon.messages.youhaveblockeduser": "Tohoto uživatele jste zablokovali.", - "addon.messages.yourcontactrequestpending": "Váš požadavek na kontakt čeká na {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Přijměte prohlášení prosím.", - "addon.mod_assign.addattempt": "Povolit další pokus", - "addon.mod_assign.addnewattempt": "Přidat nový pokus", - "addon.mod_assign.addnewattemptfromprevious": "Přidat nový pokus na základě posledního odevzdání", - "addon.mod_assign.addsubmission": "Přidat řešení úkolu", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Detaily úkolu a formulář pro odevzdání bude dostupný od {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Povolit odevzdání úkolů od", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Odevzdat úkol bude možné od {{$a}}", - "addon.mod_assign.applytoteam": "Použít hodnocení pro celou skupinu", - "addon.mod_assign.assignmentisdue": "Úkol má být hotov do", - "addon.mod_assign.attemptnumber": "Číslo pokusu", - "addon.mod_assign.attemptreopenmethod": "Opětovné otevření pokusů", - "addon.mod_assign.attemptreopenmethod_manual": "Ručně", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automaticky dokud neuspěje", - "addon.mod_assign.attemptsettings": "Nastavení pokusů", - "addon.mod_assign.cannoteditduetostatementsubmission": "V aplikaci nemůžete přidat nebo upravit řešení úkolu, protože jsme nemohli získat stav řešení z webu.", - "addon.mod_assign.cannotgradefromapp": "Některé metody známkování zatím nejsou aplikací podporovány a nemohou být změněny.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "V aplikaci nemůžete odeslat známku úkolu, protože jsme nemohli získat stav řešení z webu.", - "addon.mod_assign.confirmsubmission": "Jste si jisti, že chcete odeslat svou práci k hodnocení? Nebudete moci provádět žádné další změny.", - "addon.mod_assign.currentattempt": "Toto je pokus č. {{$a}}.", - "addon.mod_assign.currentattemptof": "Toto je pokus č. {{$a.attemptnumber}} (celkem je povoleno {{$a.maxattempts}} pokusů).", - "addon.mod_assign.currentgrade": "Momentální známka v klasifikaci kurzu", - "addon.mod_assign.cutoffdate": "Datum ukončení", - "addon.mod_assign.defaultteam": "Výchozí skupina", - "addon.mod_assign.duedate": "Termín odevzdání", - "addon.mod_assign.duedateno": "Bez termínu odevzdání", - "addon.mod_assign.duedatereached": "Termín pro odevzdání tohoto úkolu vypršel", - "addon.mod_assign.editingstatus": "Stav úprav", - "addon.mod_assign.editsubmission": "Upravit řešení úkolu", - "addon.mod_assign.erroreditpluginsnotsupported": "V aplikaci nemůžete přidat nebo upravit řešení úkolu, protože některé doplňky nepodporují úpravy.", - "addon.mod_assign.errorshowinginformation": "Nelze zobrazit informace o řešení úkolu.", - "addon.mod_assign.extensionduedate": "Prodloužený termín odevzdání", - "addon.mod_assign.feedbacknotsupported": "Tento komentář aplikace nepodporuje a nemusí obsahovat všechny informace.", - "addon.mod_assign.grade": "Známka", - "addon.mod_assign.graded": "Udělena známka", - "addon.mod_assign.gradedby": "Hodnoceno", - "addon.mod_assign.gradedfollowupsubmit": "Ohodnoceno - poté odevzdáno další řešení", - "addon.mod_assign.gradedon": "Hodnoceno dne", - "addon.mod_assign.gradelocked": "Známka je v klasifikaci kurzu zamčena nebo přepsána.", - "addon.mod_assign.gradenotsynced": "Známky nejsou synchronizovány", - "addon.mod_assign.gradeoutof": "Hodnoceno z celkových {{$a}}", - "addon.mod_assign.gradingstatus": "Stav hodnocení", - "addon.mod_assign.groupsubmissionsettings": "Nastavení skupinového řešení", - "addon.mod_assign.hiddenuser": "Účastník", - "addon.mod_assign.latesubmissions": "Zpožděné odevzdané úkoly", - "addon.mod_assign.latesubmissionsaccepted": "Povoleno do {{$a}}", - "addon.mod_assign.markingworkflowstate": "Stav postupu známkování", - "addon.mod_assign.markingworkflowstateinmarking": "Známkováno", - "addon.mod_assign.markingworkflowstateinreview": "Revidováno", - "addon.mod_assign.markingworkflowstatenotmarked": "Bez známky", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Připraveno ke zveřejnění", - "addon.mod_assign.markingworkflowstatereadyforreview": "Známkování dokončeno", - "addon.mod_assign.markingworkflowstatereleased": "Zveřejněno", - "addon.mod_assign.modulenameplural": "Úkoly", - "addon.mod_assign.multipleteams": "Člen více než jedné skupiny", - "addon.mod_assign.multipleteams_desc": "Tento úkol vyžaduje řešení úkolu ve skupinách. Jste členem více než jedné skupiny. Aby bylo možné odeslat řešení úkolu, musíte být členem pouze jedné skupiny. Obraťte se na svého učitele, aby aktualizoval vaše členství ve skupině.", - "addon.mod_assign.noattempt": "Neodevzdáno", - "addon.mod_assign.nomoresubmissionsaccepted": "Povoleny pouze pro účastníky, kterým byl prodloužen termín", - "addon.mod_assign.noonlinesubmissions": "Tento úkol nevyžaduje odpověď online", - "addon.mod_assign.nosubmission": "K tomuto úkolu nebylo nic odevzdáno", - "addon.mod_assign.notallparticipantsareshown": "Nejsou zobrazeni účastníci bez odevzdaného řešení úkolu.", - "addon.mod_assign.noteam": "Není členem žádné skupiny", - "addon.mod_assign.noteam_desc": "Tento úkol vyžaduje řešení úkolu ve skupinách. Nejste členem žádné skupiny, takže nelze vytvořit řešení úkolu. Obraťte se na učitele, kteří vás přidají do skupiny.", - "addon.mod_assign.notgraded": "Nehodnoceno", - "addon.mod_assign.numberofdraftsubmissions": "Návrhy", - "addon.mod_assign.numberofparticipants": "Účastníci", - "addon.mod_assign.numberofsubmissionsneedgrading": "Nutno ohodnotit", - "addon.mod_assign.numberofsubmittedassignments": "Odevzdáno", - "addon.mod_assign.numberofteams": "Skupiny", - "addon.mod_assign.numwords": "{{$a}} slov", - "addon.mod_assign.outof": "{{$a.current}} ze {{$a.total}}", - "addon.mod_assign.overdue": "Úkol má zpoždění: {{$a}}", - "addon.mod_assign.submission": "Odevzdané úkoly", - "addon.mod_assign.submissioneditable": "Student může upravit tento úkol", - "addon.mod_assign.submissionnoteditable": "Student nemůže upravit tento úkol", - "addon.mod_assign.submissionnotsupported": "Tento formát řešení úkolu aplikace nepodporuje a nemusí obsahovat všechny informace.", - "addon.mod_assign.submissionslocked": "V tomto úkolu nelze odevzdat práci", - "addon.mod_assign.submissionstatus": "Stav odevzdání úkolu", - "addon.mod_assign.submissionstatus_": "Neodesláno", - "addon.mod_assign.submissionstatus_draft": "Návrh (neodesláno)", - "addon.mod_assign.submissionstatus_marked": "Udělena známka", - "addon.mod_assign.submissionstatus_new": "Žádné řešení úkolu", - "addon.mod_assign.submissionstatus_reopened": "Znovu otevřeno", - "addon.mod_assign.submissionstatus_submitted": "Odesláno k hodnocení", - "addon.mod_assign.submissionstatusheading": "Stav odevzdání úkolu", - "addon.mod_assign.submissionteam": "Skupina", - "addon.mod_assign.submitassignment": "Odevzdat úkol", - "addon.mod_assign.submitassignment_help": "Po odevzdání úkolu nebudete moci provádět žádné změny.", - "addon.mod_assign.submittedearly": "Úkoly byly odevzdány {{$a}} včas", - "addon.mod_assign.submittedlate": "Úkoly byly odevzdány {{$a}} po termínu", - "addon.mod_assign.timemodified": "Naposledy změněno", - "addon.mod_assign.timeremaining": "Zbývá", - "addon.mod_assign.ungroupedusers": "Nastavení \"Vyžadovat zařazení do skupiny\" je zapnuto a někteří uživatelé nejsou zařazeni do skupiny, bude jim to bránit v odevzdání úkolu.", - "addon.mod_assign.ungroupedusersoptional": "Nastavení „Skupinové řešení“ je povoleno a někteří uživatelé buď nejsou členy žádné skupiny, nebo jsou členy více než jedné skupiny. Mějte prosím na paměti, že tito studenti budou přihlášeni jako členové „Výchozí skupiny“.", - "addon.mod_assign.unlimitedattempts": "Bez omezení", - "addon.mod_assign.userswhoneedtosubmit": "Uživatelé, kteří potřebují odevzdat úkol: {{$a}}", - "addon.mod_assign.userwithid": "Uživatel s ID {{id}}", - "addon.mod_assign.viewsubmission": "Zobrazit odevzdané úkoly", - "addon.mod_assign.warningsubmissiongrademodified": "Známka úkolu byla na webu upravena.", - "addon.mod_assign.warningsubmissionmodified": "Řešení úkolu bylo na webu upraveno.", - "addon.mod_assign.wordlimit": "Počet slov", - "addon.mod_assign_feedback_comments.pluginname": "Komentář učitele", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF poznámky", - "addon.mod_assign_feedback_file.pluginname": "Soubor se zpětnou vazbou", - "addon.mod_assign_submission_comments.pluginname": "Komentář studenta", - "addon.mod_assign_submission_file.pluginname": "Odevzdat soubor(y)", - "addon.mod_assign_submission_onlinetext.pluginname": "Odevzdání online textů", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Limit počet slov pro tento úkol je {{$a.limit}} slov a vy se pokoušíte odevzdat {{$a.count}} slov. Zkontrolujte prosím své řešení a zkuste to znovu.", - "addon.mod_book.errorchapter": "Chyba při čtení kapitoly knihy.", - "addon.mod_book.modulenameplural": "Knihy", - "addon.mod_book.navnexttitle": "Další: {{$a}}", - "addon.mod_book.navprevtitle": "Předchozí: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Kapitoly knihy", - "addon.mod_book.toc": "Obsah", - "addon.mod_chat.beep": "Prozvonit", - "addon.mod_chat.chatreport": "Chatování", - "addon.mod_chat.currentusers": "Stávající uživatelé", - "addon.mod_chat.enterchat": "Klikněte zde pro vstup do chatu", - "addon.mod_chat.entermessage": "Vložte svou zprávu", - "addon.mod_chat.errorwhileconnecting": "Chyba při připojování k chatu.", - "addon.mod_chat.errorwhilegettingchatdata": "Chyba při načítání dat chatu.", - "addon.mod_chat.errorwhilegettingchatusers": "Chyba při načítání uživatelů chatu.", - "addon.mod_chat.errorwhileretrievingmessages": "Chyba při načítání zpráv ze serveru.", - "addon.mod_chat.errorwhilesendingmessage": "Chyba při odeslání zprávy.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} někoho prozvání!", - "addon.mod_chat.messagebeepsyou": "{{$a}} vás prozvání!", - "addon.mod_chat.messageenter": "{{$a}} právě vstoupil(a) do tohoto chatu", - "addon.mod_chat.messageexit": "{{$a}} opustil(a) tento chat", - "addon.mod_chat.messages": "Zprávy", - "addon.mod_chat.messageyoubeep": "Prozvonili jste uživatele {{$a}}", - "addon.mod_chat.modulenameplural": "Chatování", - "addon.mod_chat.mustbeonlinetosendmessages": "Pro odesílání zpráv musíte být online", - "addon.mod_chat.nomessages": "Zatím žádné zprávy", - "addon.mod_chat.nosessionsfound": "Nebyly nalezeny žádné relace", - "addon.mod_chat.saidto": "řekl(-a) uživateli", - "addon.mod_chat.send": "Odeslat", - "addon.mod_chat.sessionstart": "Další chatování začne v: {{$a.date}} (tj. za {{$a.fromnow}})", - "addon.mod_chat.showincompletesessions": "Zobrazit neúplné relace", - "addon.mod_chat.talk": "Diskuse", - "addon.mod_chat.viewreport": "Ukázat proběhlé chatování", - "addon.mod_choice.cannotsubmit": "Omlouváme se, nastal problém odesláním vaší odpovědi. Zkuste to prosím znovu.", - "addon.mod_choice.choiceoptions": "Možné odpovědi na anketu", - "addon.mod_choice.errorgetchoice": "Chyba při načítání dat ankety.", - "addon.mod_choice.expired": "Tato aktivita byla uzavřena {{$a}}.", - "addon.mod_choice.full": "(Obsazeno)", - "addon.mod_choice.modulenameplural": "Ankety", - "addon.mod_choice.noresultsviewable": "Výsledky nejsou momentálně k dispozici", - "addon.mod_choice.notopenyet": "Tato činnost není k dostupná do {{$a}}.", - "addon.mod_choice.numberofuser": "Počet odpovědí", - "addon.mod_choice.numberofuserinpercentage": "Počet odpovědí v procentech", - "addon.mod_choice.previewonly": "Toto je náhled dostupných možností v této anketě. Hlasovat budete moci od {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anonymní výsledky budou publikovány po vaší odpovědi.", - "addon.mod_choice.publishinfoanonclose": "Anonymní výsledky budou publikovány po ukončení činnosti.", - "addon.mod_choice.publishinfofullafter": "Úplné výsledky, které zobrazují všechny volby, budou publikovány po vaší odpovědi.", - "addon.mod_choice.publishinfofullclose": "Úplné výsledky, které zobrazují všechny volby, budou publikovány po ukončení činnosti.", - "addon.mod_choice.publishinfonever": "Výsledky této činnosti nebudou po vaší odpovědi zveřejněny.", - "addon.mod_choice.removemychoice": "Odstranit mou volbu", - "addon.mod_choice.responses": "Odpovědi", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% uživatelů zvolilo možnost: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Graf", - "addon.mod_choice.resultsnotsynced": "Vaše poslední odpověď musí být synchronizována předtím, než je zahrnuta do výsledků.", - "addon.mod_choice.savemychoice": "Uložit mou volbu", - "addon.mod_choice.userchoosethisoption": "Uživatelé, kteří si vybrali tuto alternativu", - "addon.mod_choice.yourselection": "Vaše volba", - "addon.mod_data.addentries": "Přidat záznamy", - "addon.mod_data.advancedsearch": "Pokročilé vyhledávání", - "addon.mod_data.alttext": "Alternativní text", - "addon.mod_data.approve": "Schválit", - "addon.mod_data.approved": "Schváleno", - "addon.mod_data.ascending": "Vzestupně", - "addon.mod_data.authorfirstname": "Křestní jméno autora", - "addon.mod_data.authorlastname": "Příjmení autora", - "addon.mod_data.confirmdeleterecord": "Chcete skutečně odstranit tento záznam?", - "addon.mod_data.descending": "Sestupně", - "addon.mod_data.disapprove": "Odvolat schválení", - "addon.mod_data.edittagsnotsupported": "Litujeme, aplikace nepodporuje úpravy štítků.", - "addon.mod_data.emptyaddform": "Nevyplnili jste žádná pole!", - "addon.mod_data.entrieslefttoadd": "Ještě musíte vložit {{$a.entriesleft}} záznamů(-y), abyste dokončili tuto činnost", - "addon.mod_data.entrieslefttoaddtoview": "Ještě musíte vložit {{$a.entrieslefttoview}} záznamů(-y), než budete moci vidět záznamy ostatních účastníků.", - "addon.mod_data.errorapproving": "Chyba při schvalování nebo odmítnutí položky.", - "addon.mod_data.errordeleting": "Chyba při mazání položky.", - "addon.mod_data.errormustsupplyvalue": "Musí obsahovat nějakou hodnotu.", - "addon.mod_data.expired": "Tato činnost byla ukončena {{$a}} a není již nadále dostupná.", - "addon.mod_data.fields": "Pole", - "addon.mod_data.foundrecords": "Počet nalezených záznamů: {{$a.num}}/{{$a.max}} (resetovat filtry)", - "addon.mod_data.gettinglocation": "Získání polohy", - "addon.mod_data.latlongboth": "Je požadována zeměpisná šířka i délka.", - "addon.mod_data.locationpermissiondenied": "Oprávnění k přístupu k vaší poloze bylo zamítnuto.", - "addon.mod_data.menuchoose": "Vybrat...", - "addon.mod_data.modulenameplural": "Databáze", - "addon.mod_data.more": "Podrobněji", - "addon.mod_data.mylocation": "Moje poloha", - "addon.mod_data.nomatch": "Nenalezeny žádné záznamy!", - "addon.mod_data.norecords": "Nejsou k dispozici žádné záznamy", - "addon.mod_data.notapproved": "Záznam není zatím schválen", - "addon.mod_data.notopenyet": "Tato činnost nebude dostupná až do {{$a}}", - "addon.mod_data.numrecords": "{{$a}} záznamů", - "addon.mod_data.other": "Jiné", - "addon.mod_data.recordapproved": "Záznam byl schválen", - "addon.mod_data.recorddeleted": "Záznam byl smazán", - "addon.mod_data.recorddisapproved": "Neschválený záznam", - "addon.mod_data.resetsettings": "Resetovat filtry", - "addon.mod_data.search": "Vyhledat", - "addon.mod_data.searchbytagsnotsupported": "Je nám líto, aplikace nepodporuje vyhledávání podle značek.", - "addon.mod_data.selectedrequired": "Všechna zvolená povinná", - "addon.mod_data.single": "Prohlédnout jednotlivě", - "addon.mod_data.tagarea_data_records": "Datové záznamy", - "addon.mod_data.timeadded": "Čas vložení", - "addon.mod_data.timemodified": "Čas poslední úpravy", - "addon.mod_data.usedate": "Zahrnout do vyhledávání.", - "addon.mod_feedback.analysis": "Analýza", - "addon.mod_feedback.anonymous": "Anonymní", - "addon.mod_feedback.anonymous_entries": "Anonymní záznamy", - "addon.mod_feedback.average": "Průměr", - "addon.mod_feedback.captchaofflinewarning": "Dotazník se souborem captcha nemůže být v režinu offline dokončen, pokud není nakonfigurován režim offline nebo server je vypnutý.", - "addon.mod_feedback.complete_the_form": "Odpovězte na otázky", - "addon.mod_feedback.completed_feedbacks": "Odevzdané odpovědi", - "addon.mod_feedback.continue_the_form": "Pokračovat v zodpovídání otázek", - "addon.mod_feedback.feedback_is_not_open": "Dotazník není přístupný", - "addon.mod_feedback.feedback_submitted_offline": "Tento dotazník byl uložen k pozdějšímu odevzdání.", - "addon.mod_feedback.feedbackclose": "Povolit odpovědi", - "addon.mod_feedback.feedbackopen": "Dostupné od", - "addon.mod_feedback.mapcourses": "Mapování kurzů", - "addon.mod_feedback.maximal": "Maximum", - "addon.mod_feedback.minimal": "Minimum", - "addon.mod_feedback.mode": "Režim", - "addon.mod_feedback.modulenameplural": "Dotazníky", - "addon.mod_feedback.next_page": "Další stránka", - "addon.mod_feedback.non_anonymous": "Jména respondentů budou zaznamenána a ukázána s odpověďmi", - "addon.mod_feedback.non_anonymous_entries": "Neanonymní záznamy ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Uživatelé, kteří ještě neodpověděli ({{$a}})", - "addon.mod_feedback.not_selected": "Nevybráno", - "addon.mod_feedback.not_started": "Nebylo spuštěno", - "addon.mod_feedback.numberoutofrange": "Číslo je mimo rozsah", - "addon.mod_feedback.overview": "Přehled", - "addon.mod_feedback.page_after_submit": "Zpráva o dokončení", - "addon.mod_feedback.preview": "Náhled", - "addon.mod_feedback.previous_page": "Předchozí stránka", - "addon.mod_feedback.questions": "Otázky", - "addon.mod_feedback.response_nr": "Číslo odpovědi", - "addon.mod_feedback.responses": "Odpovědi", - "addon.mod_feedback.save_entries": "Odevzdejte své odpovědi", - "addon.mod_feedback.show_entries": "Ukázat odpovědi", - "addon.mod_feedback.show_nonrespondents": "Ukázat uživatele, kteří neodpověděli", - "addon.mod_feedback.started": "Spuštěno", - "addon.mod_feedback.this_feedback_is_already_submitted": "Tento dotazník jste již vyplnili.", - "addon.mod_folder.emptyfilelist": "Žádný soubor k zobrazení.", - "addon.mod_folder.modulenameplural": "Složky", - "addon.mod_forum.addanewdiscussion": "Přidat nové téma diskuse", - "addon.mod_forum.addanewquestion": "Přidat novou otázku", - "addon.mod_forum.addanewtopic": "Přidat nové téma", - "addon.mod_forum.addtofavourites": "Označit tuto diskusi hvězdičkou", - "addon.mod_forum.advanced": "Pokročilé", - "addon.mod_forum.cannotadddiscussion": "Abyste mohli přidávat diskuse do tohoto fóra, musíte být členy skupiny.", - "addon.mod_forum.cannotadddiscussionall": "Nemáte oprávnění vkládat téma diskuse pro všechny účastníky.", - "addon.mod_forum.cannotcreatediscussion": "Nelze vytvořit novou diskusi", - "addon.mod_forum.couldnotadd": "Neznámá chyba! Nemohu přidat váš příspěvek.", - "addon.mod_forum.couldnotupdate": "Neznámá chyba! Nemohu aktualizovat váš příspěvek.", - "addon.mod_forum.cutoffdatereached": "Toto fórum je již uzavřeno, takže na něj již nemůžete přispívat.", - "addon.mod_forum.delete": "Odstranit", - "addon.mod_forum.deletedpost": "Příspěvek byl odstraněn.", - "addon.mod_forum.deletesure": "Jste si jistí, že chcete odstranit tento příspěvek?", - "addon.mod_forum.discussion": "Diskuse", - "addon.mod_forum.discussionlistsortbycreatedasc": "Seřadit podle data vytvoření ve vzestupném pořadí", - "addon.mod_forum.discussionlistsortbycreateddesc": "Seřadit podle data vytvoření v sestupném pořadí", - "addon.mod_forum.discussionlistsortbylastpostasc": "Seřadit podle data vytvoření posledního příspěvku ve vzestupném pořadí", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Seřadit podle data vytvoření posledního příspěvku v sestupném pořadí", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Seřadit podle počtu odpovědí ve vzestupném pořadí", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Seřadit podle počtu odpovědí v sestupném pořadí", - "addon.mod_forum.discussionlocked": "Tato diskuse byla uzavřena, takže již nemůže odpovídat.", - "addon.mod_forum.discussionpinned": "Připnuté navrch", - "addon.mod_forum.discussionsubscription": "Odebírání diskuse", - "addon.mod_forum.edit": "Upravit", - "addon.mod_forum.erroremptymessage": "Text příspěvku nemůže být prázdný", - "addon.mod_forum.erroremptysubject": "Předmět příspěvku nemůže být prázdný", - "addon.mod_forum.errorgetforum": "Chyba při načítání dat fóra.", - "addon.mod_forum.errorgetgroups": "Chyba při načítání nastavení skupiny.", - "addon.mod_forum.errorposttoallgroups": "Nelze vytvořit novou diskusi ve všech skupinách.", - "addon.mod_forum.favouriteupdated": "Vaše volba hvězdičky byla aktualizována.", - "addon.mod_forum.forumnodiscussionsyet": "V tomto diskusním fóru nejsou žádná témata", - "addon.mod_forum.group": "Skupina", - "addon.mod_forum.lastpost": "Poslední příspěvek", - "addon.mod_forum.lockdiscussion": "Uzamknout tuto diskusi", - "addon.mod_forum.lockupdated": "Možnost zámku byla aktualizována.", - "addon.mod_forum.message": "Zpráva", - "addon.mod_forum.modeflatnewestfirst": "Zobrazit odpovědi za sebou (nejnovější nahoře)", - "addon.mod_forum.modeflatoldestfirst": "Zobrazit odpovědi za sebou (nejstarší nahoře)", - "addon.mod_forum.modenested": "Zobrazit hierarchii odpovědí (včetně textu)", - "addon.mod_forum.modulenameplural": "Fóra", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} diskusí", - "addon.mod_forum.numreplies": "{{numreplies}} odpovědí", - "addon.mod_forum.pindiscussion": "Připíchnout tuto diskusi nahoru", - "addon.mod_forum.pinupdated": "Volba připínáčku byla aktualizována.", - "addon.mod_forum.postisprivatereply": "Toto je soukromá odpověď. Ostatní účastníci to nevidí.", - "addon.mod_forum.posttoforum": "Poslat do fóra", - "addon.mod_forum.posttomygroups": "Odeslat kopii do všech skupin", - "addon.mod_forum.privatereply": "Odpovědět soukromě", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Obnovit diskuse", - "addon.mod_forum.refreshposts": "Obnovit diskusní příspěvky", - "addon.mod_forum.removefromfavourites": "Odstranit hvězdičku z této diskuze", - "addon.mod_forum.reply": "Odpovědět", - "addon.mod_forum.replyplaceholder": "Napište svou odpověď ...", - "addon.mod_forum.subject": "Předmět", - "addon.mod_forum.tagarea_forum_posts": "Příspěvky ve fóru", - "addon.mod_forum.thisforumhasduedate": "Termín pro odeslání do tohoto fóra je {{$a}}.", - "addon.mod_forum.thisforumisdue": "Termín pro odeslání do tohoto fóra byl {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Odemknout tuto diskusi", - "addon.mod_forum.unpindiscussion": "Odepnout tuto diskusi", - "addon.mod_forum.unread": "Nepřečteno", - "addon.mod_forum.unreadpostsnumber": "Nepřečtené příspěvky: {{$a}}", - "addon.mod_forum.yourreply": "Vaše odpověď", - "addon.mod_glossary.addentry": "Přidat novou položku", - "addon.mod_glossary.aliases": "Klíčová slova", - "addon.mod_glossary.attachment": "Příloha", - "addon.mod_glossary.browsemode": "Prohlížení příspěvků", - "addon.mod_glossary.byalphabet": "Abecedně", - "addon.mod_glossary.byauthor": "Skupina podle autora", - "addon.mod_glossary.bycategory": "Skupina podle kategorie", - "addon.mod_glossary.bynewestfirst": "Nejnovější nejdříve", - "addon.mod_glossary.byrecentlyupdated": "Posledně aktualizované", - "addon.mod_glossary.bysearch": "Hledat", - "addon.mod_glossary.cannoteditentry": "Záznam nelze upravit", - "addon.mod_glossary.casesensitive": "Tato položka rozlišuje VELKÁ/malá", - "addon.mod_glossary.categories": "Kategorie", - "addon.mod_glossary.concept": "Pojem", - "addon.mod_glossary.definition": "Definice", - "addon.mod_glossary.entriestobesynced": "Příspěvky, které mají být synchronizovány", - "addon.mod_glossary.entrypendingapproval": "Tato položka čeká na schválení", - "addon.mod_glossary.entryusedynalink": "Tato položka by měla být automaticky propojována", - "addon.mod_glossary.errconceptalreadyexists": "Tento pojem již ve slovníku existuje. Duplicitní položky jsou zde zakázány.", - "addon.mod_glossary.errorloadingentries": "Při načítání položek došlo k chybě.", - "addon.mod_glossary.errorloadingentry": "Při načítání položky došlo k chybě.", - "addon.mod_glossary.errorloadingglossary": "Při načítání slovníku došlo k chybě.", - "addon.mod_glossary.fillfields": "Pojem a definice jsou povinná pole.", - "addon.mod_glossary.fullmatch": "Srovnávat pouze celá slova
    (při automatickém propojování)", - "addon.mod_glossary.linking": "Automatické propojování", - "addon.mod_glossary.modulenameplural": "Slovníky", - "addon.mod_glossary.noentriesfound": "Nebyly nalezeny žádné záznamy.", - "addon.mod_glossary.searchquery": "Vyhledávací dotaz", - "addon.mod_glossary.tagarea_glossary_entries": "Položky slovníku", - "addon.mod_h5pactivity.all_attempts": "Všechny pokusy uživatele", - "addon.mod_h5pactivity.answer_checked": "Odpověď zkontrolována", - "addon.mod_h5pactivity.answer_correct": "Vaše odpověď je správná", - "addon.mod_h5pactivity.answer_fail": "Nesprávná odpověď", - "addon.mod_h5pactivity.answer_incorrect": "Vaše odpověď je nesprávná", - "addon.mod_h5pactivity.answer_pass": "Správná odpověď", - "addon.mod_h5pactivity.attempt": "Pokus", - "addon.mod_h5pactivity.attempt_completion_no": "Tento pokus není označen jako dokončený", - "addon.mod_h5pactivity.attempt_completion_yes": "Tento pokus je dokončen", - "addon.mod_h5pactivity.attempt_success_fail": "Nesplněno", - "addon.mod_h5pactivity.attempt_success_pass": "Splněno", - "addon.mod_h5pactivity.attempt_success_unknown": "Není hlášeno", - "addon.mod_h5pactivity.attempts_none": "Tento uživatel neměl žádné pokusy k zobrazení.", - "addon.mod_h5pactivity.completion": "Dokončení", - "addon.mod_h5pactivity.downloadh5pfile": "Stáhnout soubor H5P", - "addon.mod_h5pactivity.duration": "Doba trvání", - "addon.mod_h5pactivity.errorgetactivity": "Chyba při získávání údajů o činnosti H5P.", - "addon.mod_h5pactivity.filestatenotdownloaded": "Balíček H5P není stažen. Abyste jej mohli používat, musíte jej stáhnout.", - "addon.mod_h5pactivity.filestateoutdated": "Balíček H5P byl od posledního stažení upraven. Abyste jej mohli používat, musíte jej stáhnout znovu.", - "addon.mod_h5pactivity.maxscore": "Maximální skóre", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Moje pokusy", - "addon.mod_h5pactivity.no_compatible_track": "Tato interakce ({{$a}}) neposkytuje informace o sledování nebo poskytované sledování není kompatibilní s aktuální verzí aktivity.", - "addon.mod_h5pactivity.offlinedisabledwarning": "K zobrazení balíčku H5P budete muset být online.", - "addon.mod_h5pactivity.outcome": "Výsledek", - "addon.mod_h5pactivity.previewmode": "Tento obsah se zobrazí v režimu náhledu. Sledování pokusů nebude uloženo.", - "addon.mod_h5pactivity.result_fill-in": "Vyplnit text", - "addon.mod_h5pactivity.result_other": "Neznámý typ interakce", - "addon.mod_h5pactivity.review_my_attempts": "Zobrazit moje pokusy", - "addon.mod_h5pactivity.score": "Skóre", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} z celkem {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Datum zahájení", - "addon.mod_h5pactivity.totalscore": "Celkové skóre", - "addon.mod_h5pactivity.viewattempt": "Zobrazit pokus {{$a}}", - "addon.mod_imscp.deploymenterror": "Chyba v balíčku!", - "addon.mod_imscp.modulenameplural": "Balíček IMS", - "addon.mod_imscp.showmoduledescription": "Zobrazit popis", - "addon.mod_imscp.toc": "Tabulka obsahu", - "addon.mod_lesson.answer": "Odpověď", - "addon.mod_lesson.attempt": "Pokus: {{$a}}", - "addon.mod_lesson.attemptheader": "Pokus", - "addon.mod_lesson.attemptsremaining": "Zbývající počet pokusů: {{$a}}", - "addon.mod_lesson.averagescore": "Průměrná známka", - "addon.mod_lesson.averagetime": "Průměrný čas", - "addon.mod_lesson.branchtable": "Obsahová stránka", - "addon.mod_lesson.cannotfindattempt": "Chyba: nelze najít pokus", - "addon.mod_lesson.cannotfinduser": "Chyba: nelze najít uživatele", - "addon.mod_lesson.clusterjump": "Neprohlédnutá otázka v rámci svazku", - "addon.mod_lesson.completed": "Dokončeno", - "addon.mod_lesson.congratulations": "Gratulujeme – dosáhli jste konce přednášky", - "addon.mod_lesson.continue": "Pokračovat", - "addon.mod_lesson.continuetonextpage": "Pokračovat na následující stránku", - "addon.mod_lesson.defaultessayresponse": "Vaše tvořená odpověď bude ohodnocena vaším vyučujícím v kurzu.", - "addon.mod_lesson.detailedstats": "Podrobné statistiky", - "addon.mod_lesson.didnotanswerquestion": "Tuto otázku student(ka) nezodpověděl(a)", - "addon.mod_lesson.displayofgrade": "Zobrazení známky (pouze pro studenty)", - "addon.mod_lesson.displayscorewithessays": "Váš bodový zisk za automaticky hodnocené odpovědi je {{$a.score}} z {{$a.tempmaxgrade}}.
    Vaše tvořené odpovědi (v počtu {{$a.essayquestions}}) budou ohodnoceny a zahrnuty
    do vašeho konečného počtu bodů později.

    Váš současný bodový zisk bez tvořených odpovědí je {{$a.score}} z {{$a.grade}}.", - "addon.mod_lesson.displayscorewithoutessays": "Váš bodový zisk je {{$a.score}} (z {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Heslo nemůže být prázdné", - "addon.mod_lesson.enterpassword": "Zadejte prosím heslo:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Nezodpověděli jste žádnou otázku. Za tuto přednášku nezískáváte žádný bod.", - "addon.mod_lesson.errorprefetchrandombranch": "Tato přednáška obsahuje skok na náhodnou stránku. V aplikaci ji nelze zkoušet, dokud nebude spuštěna na webu.", - "addon.mod_lesson.errorreviewretakenotlast": "Tento pokus již nelze prohlédnout, protože byl dokončen další pokus.", - "addon.mod_lesson.finish": "Skončit", - "addon.mod_lesson.finishretakeoffline": "Tento pokus byl dokončen offline.", - "addon.mod_lesson.firstwrong": "Bohužel, vaše odpověď není správná. Chcete to zkusit ještě jednou? (ovšem již bez bodového zisku)", - "addon.mod_lesson.gotoendoflesson": "Přejít na konec přednášky", - "addon.mod_lesson.grade": "Známka", - "addon.mod_lesson.highscore": "Nejlepší známka", - "addon.mod_lesson.hightime": "Nejdelší čas", - "addon.mod_lesson.leftduringtimed": "Opustili jste přednášku s časovým limitem.
    Chcete-li přednášku začít znovu, stiskněte Pokračovat.", - "addon.mod_lesson.leftduringtimednoretake": "Opustili jste přednášku s časovým limitem a není
    vám povoleno začít znovu nebo pokračovat.", - "addon.mod_lesson.lessonmenu": "Nabídka přednášky", - "addon.mod_lesson.lessonstats": "Statistiky přednášky", - "addon.mod_lesson.linkedmedia": "Odkaz na média", - "addon.mod_lesson.loginfail": "Nesprávné heslo. Zkuste to prosím znovu.", - "addon.mod_lesson.lowscore": "Nejhorší známka", - "addon.mod_lesson.lowtime": "Nejkratší čas", - "addon.mod_lesson.maximumnumberofattemptsreached": "Vyčerpali jste maximální počet pokusů – následuje další stránka přednášky", - "addon.mod_lesson.modattemptsnoteacher": "Revize funguje pouze studentům", - "addon.mod_lesson.modulenameplural": "Přednášky", - "addon.mod_lesson.noanswer": "Neodpověděli jste jednu nebo více otázek. Prosím, vraťte se zpět a zadejte odpověď.", - "addon.mod_lesson.nolessonattempts": "O zodpovězení otázek v této přednášce se ještě nikdo nepokusil.", - "addon.mod_lesson.nolessonattemptsgroup": "Někteří ({{$a}}) členové skupiny se dosud nepokusili o absolvování této přednášky.", - "addon.mod_lesson.notcompleted": "Nedokončeno", - "addon.mod_lesson.numberofcorrectanswers": "Počet správných odpovědí: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Počet prohlédnutých stránek: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Máte {{$a.nquestions}} zodpovězených otázek; (Máte jich zodpovědět alespoň: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Váš dosavadní bodový zisk: {{$a.score}} z {{$a.currenthigh}}.", - "addon.mod_lesson.ongoingnormal": "Zatím jste správně zodpověděli {{$a.correct}} z {{$a.viewed}} otázek.", - "addon.mod_lesson.or": "NEBO", - "addon.mod_lesson.overview": "Přehled", - "addon.mod_lesson.preview": "Náhled", - "addon.mod_lesson.progressbarteacherwarning2": "Ukazatel průchodu se zobrazuje pouze studentům. Chcete-li si jej ověřit, přihlaste se jako student.", - "addon.mod_lesson.progresscompleted": "Dokončili jste {{$a}} % přednášky", - "addon.mod_lesson.question": "Otázka", - "addon.mod_lesson.rawgrade": "Hrubá známka", - "addon.mod_lesson.reports": "Výsledky", - "addon.mod_lesson.response": "Reakce", - "addon.mod_lesson.retakefinishedinsync": "Offline pokus byl synchronizován. Chcete jej zobrazit?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Revize", - "addon.mod_lesson.reviewlesson": "Revize přednášky", - "addon.mod_lesson.reviewquestionback": "Ano, chci to zkusit ještě jednou", - "addon.mod_lesson.reviewquestioncontinue": "Ne, chci pokračovat dál", - "addon.mod_lesson.secondpluswrong": "Tato odpověď není správná. Chcete to zkusit ještě jednou?", - "addon.mod_lesson.submit": "Odeslat", - "addon.mod_lesson.teacherjumpwarning": "Tato přednáška obsahuje skok typu \"{{$a.cluster}}\" nebo \"{{$a.unseen}}\". Namísto něj bude použit skok na další stránku. Přihlaste se jako student a zkontrolujte tyto skoky.", - "addon.mod_lesson.teacherongoingwarning": "Průběžný bodový zisk se zobrazuje pouze studentům. Chcete-li si jej ověřit, přihlaste se jako student.", - "addon.mod_lesson.teachertimerwarning": "Stopky fungují pouze studentům. Chcete-li si je vyzkoušet, přihlaste se jako student.", - "addon.mod_lesson.thatsthecorrectanswer": "Tato odpověď je správná.", - "addon.mod_lesson.thatsthewronganswer": "Tato odpověď není správná.", - "addon.mod_lesson.timeremaining": "Zbývající čas", - "addon.mod_lesson.timetaken": "Doba průchodu", - "addon.mod_lesson.unseenpageinbranch": "Nezobrazená otázka v rámci stránky", - "addon.mod_lesson.warningretakefinished": "Pokus byl dokončen na webových stránkách.", - "addon.mod_lesson.welldone": "Výborně!", - "addon.mod_lesson.youhaveseen": "Některé ze stránek této přednášky jste již viděli.
    Chcete začít na poslední stránce, kterou jste navštívili?", - "addon.mod_lesson.youranswer": "Vaše odpověď", - "addon.mod_lesson.yourcurrentgradeisoutof": "Vaše známka nyní je {{$a.grade}} z možných {{$a.total}}", - "addon.mod_lesson.youshouldview": "Měli byste zodpovědět nejméně {{$a}} otázek.", - "addon.mod_lti.errorgetlti": "Chyba při načítání", - "addon.mod_lti.errorinvalidlaunchurl": "Spuštěná URL není platná.", - "addon.mod_lti.launchactivity": "Zahájit aktivitu", - "addon.mod_lti.modulenameplural": "Externí nástroje", - "addon.mod_page.errorwhileloadingthepage": "Chyba při načítání", - "addon.mod_page.modulenameplural": "Stránky", - "addon.mod_quiz.answercolon": "Odpověď:", - "addon.mod_quiz.attemptfirst": "První pokus", - "addon.mod_quiz.attemptlast": "Poslední pokus", - "addon.mod_quiz.attemptnumber": "Pokus", - "addon.mod_quiz.attemptquiznow": "Pokusit se o zvládnutí testu", - "addon.mod_quiz.attemptstate": "Stav", - "addon.mod_quiz.canattemptbutnotsubmit": "O tento test se můžete pokusit v aplikaci, ale pokus budete muset odeslat v prohlížeči z následujících důvodů:", - "addon.mod_quiz.cannotsubmitquizdueto": "Tento pokus testu nemůže být odeslán z následujících důvodů:", - "addon.mod_quiz.clearchoice": "Zrušit mou volbu", - "addon.mod_quiz.comment": "Komentář", - "addon.mod_quiz.completedon": "Dokončení testu", - "addon.mod_quiz.confirmclose": "Po uzavření pokusu již nebudete mít možnost upravit své odpovědi.", - "addon.mod_quiz.confirmcontinueoffline": "Tento pokus nebyl synchronizován od {{$a}}. Pokud budete pokračovat v pokusu na jiném zařízení, pak může dojít ke ztrátě dat.", - "addon.mod_quiz.confirmleavequizonerror": "Při ukládání odpovědi došlo k chybě. Jste si jisti, že chcete test ukončit?", - "addon.mod_quiz.confirmstart": "Váš pokus má časový limit {{$a}}. Čas se začne odpočítávat od okamžiku, kdy začnete pokus a nelze jej pozastavit. Pokus musíte odevzdat před skončením limitu. Jste si jisti, že chcete začít už teď?", - "addon.mod_quiz.confirmstartheader": "Omezený čas", - "addon.mod_quiz.connectionerror": "Ztráta připojení k síti, automatické ukládání selhalo.\n\nPoznamenejte si odpovědi na této stránce a pokuste se znovu připojit.\n\nPokud se spojení samo obnoví, vaše odpovědi budou uloženy a tato zpráva zmizí.", - "addon.mod_quiz.continueattemptquiz": "Pokračovat v posledním pokusu", - "addon.mod_quiz.continuepreview": "Pokračovat v poslední prohlídce", - "addon.mod_quiz.errorbehaviournotsupported": "Tento test nelze v aplikaci spustit, protože chování není aplikací podporováno:", - "addon.mod_quiz.errordownloading": "Chyba při stahování požadovaných dat.", - "addon.mod_quiz.errorgetattempt": "Chyba při získávání dat pokusu.", - "addon.mod_quiz.errorgetquestions": "Chyba při získávání úlohy.", - "addon.mod_quiz.errorgetquiz": "Chyba při získávání dat testu.", - "addon.mod_quiz.errorparsequestions": "Při čtení úloh došlo k chybě. Prosím spusťte pokus testu ve webovém prohlížeči.", - "addon.mod_quiz.errorquestionsnotsupported": "Tento test nelze v aplikaci spustit, protože obsahuje pouze úlohy, které nejsou aplikací podporovány:", - "addon.mod_quiz.errorrulesnotsupported": "Tento test nelze v aplikaci spustit, protože podmínka omezení přístupu není aplikací podporovány:", - "addon.mod_quiz.errorsaveattempt": "Při ukládání dat pokusu došlo k chybě.", - "addon.mod_quiz.feedback": "Komentář", - "addon.mod_quiz.finishattemptdots": "Dokončení pokusu ...", - "addon.mod_quiz.finishnotsynced": "Ukončeno bez synchronizace", - "addon.mod_quiz.grade": "Známka", - "addon.mod_quiz.gradeaverage": "Průměrná známka", - "addon.mod_quiz.gradehighest": "Nejvyšší známka", - "addon.mod_quiz.grademethod": "Metoda známkování", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Body", - "addon.mod_quiz.modulenameplural": "Testy", - "addon.mod_quiz.mustbesubmittedby": "Tento pokus musí být odeslán nejpozději {{$a}}.", - "addon.mod_quiz.noquestions": "Dosud nebyly vloženy žádné úlohy.", - "addon.mod_quiz.noreviewattempt": "Nemůžete revidovat tento pokus.", - "addon.mod_quiz.notyetgraded": "Dosud nehodnoceno", - "addon.mod_quiz.opentoc": "Otevřete navigaci.", - "addon.mod_quiz.outof": "{{$a.grade}} z možných {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} z možných {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Celková reakce", - "addon.mod_quiz.overdue": "Zpožděný", - "addon.mod_quiz.overduemustbesubmittedby": "Tento pokus je zpožděn. Měl být již odeslán. Pokud byste chtěli tento test oznámkován musíte musíte jej odeslat so {{$a}}. Pokud nechcete odeslat ji do té doby, se žádné stopy po tomto pokusu se počítá.", - "addon.mod_quiz.preview": "Náhled", - "addon.mod_quiz.previewquiznow": "Okamžitý náhled testu", - "addon.mod_quiz.question": "Úloha", - "addon.mod_quiz.quiznavigation": "Navigace testu", - "addon.mod_quiz.quizpassword": "Heslo testu", - "addon.mod_quiz.reattemptquiz": "Začít další pokus", - "addon.mod_quiz.requirepasswordmessage": "Chcete-li se pokusit o absolvování tohoto testu, musíte zadat heslo.", - "addon.mod_quiz.returnattempt": "Návrat k pokusu", - "addon.mod_quiz.review": "Revize", - "addon.mod_quiz.reviewofattempt": "Prohlídka pokusu č. {{$a}}", - "addon.mod_quiz.reviewofpreview": "Prohlídka náhledu", - "addon.mod_quiz.showall": "Zobrazit všechny úlohy na jedné stránce", - "addon.mod_quiz.showeachpage": "Zobrazovat po stránkách", - "addon.mod_quiz.startattempt": "Zahájení pokusu", - "addon.mod_quiz.startedon": "Započetí testu", - "addon.mod_quiz.stateabandoned": "Neodesláno", - "addon.mod_quiz.statefinished": "Dokončeno", - "addon.mod_quiz.statefinisheddetails": "Odesláno {{$a}}", - "addon.mod_quiz.stateinprogress": "Probíhá", - "addon.mod_quiz.stateoverdue": "Překročen časový limit", - "addon.mod_quiz.stateoverduedetails": "Musí být odesláno {{$a}}", - "addon.mod_quiz.status": "Stav", - "addon.mod_quiz.submitallandfinish": "Odeslat vše a ukončit pokus", - "addon.mod_quiz.summaryofattempt": "Souhrn pokusu", - "addon.mod_quiz.summaryofattempts": "Souhrn vašich předchozích pokusů", - "addon.mod_quiz.timeleft": "Zbývající čas", - "addon.mod_quiz.timetaken": "Délka pokusu", - "addon.mod_quiz.warningattemptfinished": "Offline pokus byl vyřazen, jelikož byl dokončen na webu nebo nebyl nalezen.", - "addon.mod_quiz.warningdatadiscarded": "Některé odpovědi režimu offline byly vyřazeny, protože tyto úlohy byly na webu upraveny.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Pokus o nebyl dokončen, protože některé odpovědi v režimu offline byly vyřazeny. Zkontrolujte prosím vaše odpovědi a pak znovu pokus odešlete.", - "addon.mod_quiz.warningquestionsnotsupported": "Tento test obsahuje otázky, které aplikace nepodporuje:", - "addon.mod_quiz.yourfinalgradeis": "Vaše konečná známka za tento test je {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Chyba při načítání obsahu.", - "addon.mod_resource.modifieddate": "Změněno {{$a}}", - "addon.mod_resource.modulenameplural": "Soubory", - "addon.mod_resource.openthefile": "Otevřít soubor", - "addon.mod_resource.uploadeddate": "Nahráno {{$a}}", - "addon.mod_scorm.asset": "Komponenta", - "addon.mod_scorm.assetlaunched": "Komponenta - prohlédnuto", - "addon.mod_scorm.attempts": "Pokusy", - "addon.mod_scorm.averageattempt": "Průměr ze všech pokusů", - "addon.mod_scorm.browse": "Náhled", - "addon.mod_scorm.browsed": "Prohlédnuto", - "addon.mod_scorm.browsemode": "Režim náhledu", - "addon.mod_scorm.cannotcalculategrade": "Nelze vypočítat známku", - "addon.mod_scorm.completed": "Dokončeno", - "addon.mod_scorm.contents": "Obsah", - "addon.mod_scorm.dataattemptshown": "Tato data patří k pokusu číslo {{number}}.", - "addon.mod_scorm.enter": "Vstoupit", - "addon.mod_scorm.errorcreateofflineattempt": "Při vytváření nového offline pokusu došlo k chybě. Prosím, zkuste to znovu.", - "addon.mod_scorm.errordownloadscorm": "Chyba při stahování SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Chyba při načítání SCORM dat.", - "addon.mod_scorm.errorinvalidversion": "Litujeme, aplikace podporuje pouze SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Stažení SCORM balíčků je na stránkách Moodle zakázáno. Obraťte se na správce webu Moodle.", - "addon.mod_scorm.errornovalidsco": "Tato SCORM nemá viditelnou SCO pro stažení.", - "addon.mod_scorm.errorpackagefile": "Litujeme, aplikace podporuje pouze ZIP balíčky.", - "addon.mod_scorm.errorsyncscorm": "Při synchronizaci došlo k chybě. Prosím, zkuste to znovu.", - "addon.mod_scorm.exceededmaxattempts": "Dosáhli jste nejvyššího možného počtu pokusů.", - "addon.mod_scorm.failed": "Nedokončeno úspěšně", - "addon.mod_scorm.firstattempt": "První pokus", - "addon.mod_scorm.gradeaverage": "Průměrný bodový zisk", - "addon.mod_scorm.gradeforattempt": "Hodnocení pokusu", - "addon.mod_scorm.gradehighest": "Nejvyšší bodový zisk", - "addon.mod_scorm.grademethod": "Metoda známkování", - "addon.mod_scorm.gradereported": "Zobrazené hodnocení", - "addon.mod_scorm.gradescoes": "Učební objekty", - "addon.mod_scorm.gradesum": "Souhrnný bodový zisk", - "addon.mod_scorm.highestattempt": "Nejlepší pokus", - "addon.mod_scorm.incomplete": "Nedokončeno", - "addon.mod_scorm.lastattempt": "Poslední pokus", - "addon.mod_scorm.modulenameplural": "SCORM balíčky", - "addon.mod_scorm.newattempt": "Začít nový pokus", - "addon.mod_scorm.noattemptsallowed": "Počet povolených pokusů", - "addon.mod_scorm.noattemptsmade": "Počet pokusů, které jste vyčerpali", - "addon.mod_scorm.notattempted": "Bez pokusů", - "addon.mod_scorm.offlineattemptnote": "Tento pokus obsahuje data, která nebyla synchronizována.", - "addon.mod_scorm.offlineattemptovermax": "Tento pokus nelze odeslat, protože jste překonal maximální počet pokusů.", - "addon.mod_scorm.organizations": "Organizace", - "addon.mod_scorm.passed": "Splněno", - "addon.mod_scorm.reviewmode": "Režim opakovaného náhledu", - "addon.mod_scorm.score": "Bodový zisk", - "addon.mod_scorm.scormstatusnotdownloaded": "Tento SCORM není stažen. Bude automaticky stažen, když jej otevřete.", - "addon.mod_scorm.scormstatusoutdated": "Tento SCORM byl od posledního stažení změněn. Bude automaticky stažen, když jej otevřete.", - "addon.mod_scorm.suspended": "Pozastaveno", - "addon.mod_scorm.toc": "Obsah", - "addon.mod_scorm.warningofflinedatadeleted": "Některá offline data pokusu {{number}} byla zrušena, protože by nebylo možné vytvořit na nový pokus", - "addon.mod_scorm.warningsynconlineincomplete": "Některé pokusy nemohou být synchronizovány s webem, protože poslední online pokus není dokončen. Prosím, dokončete nejprve online pokus.", - "addon.mod_survey.cannotsubmitsurvey": "Je nám líto, došlo k chybě odeslání průzkumu. Prosím zkuste to znovu.", - "addon.mod_survey.errorgetsurvey": "Chyba při načítání dat průzkumu.", - "addon.mod_survey.ifoundthat": "Zjistil jsem, že", - "addon.mod_survey.ipreferthat": "Představoval bych si, že", - "addon.mod_survey.modulenameplural": "Průzkumy", - "addon.mod_survey.responses": "Odpovědi", - "addon.mod_survey.results": "Výsledky", - "addon.mod_survey.surveycompletednograph": "Tento dotazník jste již vyplnili.", - "addon.mod_url.accessurl": "Přístup k URL", - "addon.mod_url.modulenameplural": "URL odkazy", - "addon.mod_url.pointingtourl": "Adresa URL, na kterou zdroj odkazuje.", - "addon.mod_wiki.cannoteditpage": "Nemůžete editovat tuto stránku.", - "addon.mod_wiki.createpage": "Vytvořit stránku", - "addon.mod_wiki.editingpage": "Úprava stránky \"{{$a}}\"", - "addon.mod_wiki.errorloadingpage": "Při načítání stránky došlo k chybě.", - "addon.mod_wiki.errornowikiavailable": "Tato wiki ještě nemá žádný obsah.", - "addon.mod_wiki.gowikihome": "Přejí na výchozí stránku Wiki", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wiki", - "addon.mod_wiki.newpagehdr": "Nová stránka", - "addon.mod_wiki.newpagetitle": "Nový název stránky", - "addon.mod_wiki.nocontent": "Pro tuto stránku není obsah", - "addon.mod_wiki.notingroup": "Není ve skupině", - "addon.mod_wiki.pageexists": "Tato stránka již existuje.", - "addon.mod_wiki.pagename": "Název stránky", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki stránky", - "addon.mod_wiki.titleshouldnotbeempty": "Název by neměl být prázdný", - "addon.mod_wiki.viewpage": "Zobrazit stránku", - "addon.mod_wiki.wikipage": "Wiki stránka", - "addon.mod_wiki.wrongversionlock": "Jiný uživatel upravil tuto stránku zatímco vy jste pracovali na svých vlastních úpravách. Váš obsah je nyní zastaralý.", - "addon.mod_workshop.alreadygraded": "Ohodnoceno", - "addon.mod_workshop.areainstructauthors": "Pokyny k vypracování odevzdávaných prací", - "addon.mod_workshop.areainstructreviewers": "Pokyny k hodnocení", - "addon.mod_workshop.assess": "Hodnotit", - "addon.mod_workshop.assessedsubmission": "Ohodnocená odevzdaná práce", - "addon.mod_workshop.assessmentform": "Hodnotící formulář", - "addon.mod_workshop.assessmentsettings": "Podrobnosti hodnocení", - "addon.mod_workshop.assessmentstrategynotsupported": "Strategie hodnocení {{$a}} není podporována", - "addon.mod_workshop.assessmentweight": "Váha hodnocení", - "addon.mod_workshop.assignedassessments": "Přidělené práce k hodnocení", - "addon.mod_workshop.assignedassessmentsnone": "Nebyly vám přiděleny žádné práce k hodnocení", - "addon.mod_workshop.conclusion": "Závěr", - "addon.mod_workshop.createsubmission": "Přidat práci", - "addon.mod_workshop.deletesubmission": "Odstranit odevzdanou práci", - "addon.mod_workshop.editsubmission": "Upravit odevzdanou práci", - "addon.mod_workshop.feedbackauthor": "Zpětná vazba pro autora práce", - "addon.mod_workshop.feedbackby": "Komentář od {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Zpětná vazba pro hodnotitele", - "addon.mod_workshop.givengrades": "Udělené známky", - "addon.mod_workshop.gradecalculated": "Vypočítaná známka za odevzdanou práci", - "addon.mod_workshop.gradeinfo": "Známka: {{$a.received}} z {{$a.max}}", - "addon.mod_workshop.gradeover": "Přepsat vypočítanou známku za odevzdanou práci", - "addon.mod_workshop.gradesreport": "Přehled známek", - "addon.mod_workshop.gradinggrade": "Známka za hodnocení", - "addon.mod_workshop.gradinggradecalculated": "Vypočítaná známka za hodnocení", - "addon.mod_workshop.gradinggradeof": "Známka za hodnocení (z {{$a}})", - "addon.mod_workshop.gradinggradeover": "Přepsat známku za hodnocení", - "addon.mod_workshop.modulenameplural": "Workshopy", - "addon.mod_workshop.nogradeyet": "Zatím bez známky", - "addon.mod_workshop.notassessed": "Zatím nehodnoceno", - "addon.mod_workshop.notoverridden": "Nepřepisovat", - "addon.mod_workshop.noyoursubmission": "Zatím jste neodevzdali svou práci", - "addon.mod_workshop.overallfeedback": "Doplňující komentář", - "addon.mod_workshop.publishedsubmissions": "Zveřejněné práce", - "addon.mod_workshop.publishsubmission": "Zveřejnit práci", - "addon.mod_workshop.publishsubmission_help": "Zveřejněné práce jsou dostupné ostatním účastníkům poté, co je workshop uzavřen.", - "addon.mod_workshop.reassess": "Přehodnotit", - "addon.mod_workshop.receivedgrades": "Obdržené známky", - "addon.mod_workshop.submissionattachment": "Příloha", - "addon.mod_workshop.submissioncontent": "Obsah práce", - "addon.mod_workshop.submissiondeleteconfirm": "Jste si jisti, že chcete smazat následující odevzdané práce?", - "addon.mod_workshop.submissiongrade": "Známka za odevzdanou práci", - "addon.mod_workshop.submissiongradeof": "Známka za odevzdanou práci (z {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Musíte zadat nějaký text nebo přidat soubor.", - "addon.mod_workshop.submissionrequiredtitle": "Musíte zadat název.", - "addon.mod_workshop.submissionsreport": "Přehled odevzdaných praci", - "addon.mod_workshop.submissiontitle": "Název", - "addon.mod_workshop.switchphase10": "Přepnout do fáze nastavení", - "addon.mod_workshop.switchphase20": "Přepnout do fáze odevzdávání", - "addon.mod_workshop.switchphase30": "Přepnout do fáze hodnocení", - "addon.mod_workshop.switchphase40": "Přepnout do fáze evaluace", - "addon.mod_workshop.switchphase50": "Uzavřít workshop", - "addon.mod_workshop.userplan": "Plán workshopu", - "addon.mod_workshop.userplancurrentphase": "Aktuální fáze", - "addon.mod_workshop.warningassessmentmodified": "Odevzdaný úkol byl na webu změněn.", - "addon.mod_workshop.warningsubmissionmodified": "Úkol byl na webu změněn.", - "addon.mod_workshop.weightinfo": "Váha: {{$a}}", - "addon.mod_workshop.yourassessment": "Vaše hodnocení", - "addon.mod_workshop.yourassessmentfor": "Vaše hodnocení {{$a}}", - "addon.mod_workshop.yourgrades": "Vaše známky", - "addon.mod_workshop.yoursubmission": "Vaše práce", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Komentář pro {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Známka pro {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Hledisko {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Musíte zvolit hodnocení pro toto hledisko", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Komentář pro {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Hledisko {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Komentář pro {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Známka pro {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Tvrzení {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kritérium {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Musíte zvolit jednu z těchto položek", - "addon.notes.addnewnote": "Přidat novou poznámku", - "addon.notes.coursenotes": "Poznámky kurzu", - "addon.notes.deleteconfirm": "Odstranit tuto poznámku?", - "addon.notes.eventnotecreated": "Poznámka vytvořena", - "addon.notes.eventnotedeleted": "Poznámka odstraněna", - "addon.notes.nonotes": "Žádné poznámky tohoto typu", - "addon.notes.note": "Poznámka", - "addon.notes.notes": "Poznámky", - "addon.notes.personalnotes": "Osobní poznámky", - "addon.notes.publishstate": "Kontext", - "addon.notes.sitenotes": "Poznámky stránek", - "addon.notes.userwithid": "Uživatel s ID {{id}}", - "addon.notes.warningnotenotsent": "Nelze přidat poznámku (y) ke kurzu {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Chyba při načítání oznámení.", - "addon.notifications.markallread": "Označit vše jako přečtené", - "addon.notifications.notificationpreferences": "Nastavení oznámení", - "addon.notifications.notifications": "Oznámení", - "addon.notifications.playsound": "Přehrát zvuk", - "addon.notifications.therearentnotificationsyet": "Nejsou žádná sdělení.", - "addon.storagemanager.deletecourse": "Zrušit všechny údaje o kurzu", - "addon.storagemanager.deletedatafrom": "Zrušit data {{name}}", - "addon.storagemanager.info": "Soubory uložené ve vašem zařízení urychlují práci aplikace a umožňují aplikaci používat offline. Pokud potřebujete uvolnit úložný prostor, můžete soubory bezpečně odstranit, .", - "addon.storagemanager.managestorage": "Správa úložiště", - "addon.storagemanager.storageused": "Použité úložiště souborů:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Spojené arabské emiráty", - "assets.countries.AF": "Afghánistán", - "assets.countries.AG": "Antigua a Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albánie", - "assets.countries.AM": "Arménie", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktida", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Americká Samoa", - "assets.countries.AT": "Rakousko", - "assets.countries.AU": "Austrálie", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Ålandy", - "assets.countries.AZ": "Ázerbájdžán", - "assets.countries.BA": "Bosna a Hercegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladéš", - "assets.countries.BE": "Belgie", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulharsko", - "assets.countries.BH": "Bahrajn", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Svatý Bartoloměj", - "assets.countries.BM": "Bermudy", - "assets.countries.BN": "Brunej", - "assets.countries.BO": "Mnohonárodnostní stát Bolívie", - "assets.countries.BQ": "Karibské Nizozemsko", - "assets.countries.BR": "Brazílie", - "assets.countries.BS": "Bahamy", - "assets.countries.BT": "Bhútán", - "assets.countries.BV": "Bouvetův ostrov", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Bělorusko", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Kokosové ostrovy", - "assets.countries.CD": "Demokratická republika Kongo", - "assets.countries.CF": "Středoafrická republika", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Švýcarsko", - "assets.countries.CI": "Pobřeží slonoviny", - "assets.countries.CK": "Cookovy ostrovy", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Čína", - "assets.countries.CO": "Kolumbie", - "assets.countries.CR": "Kostarika", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Kapverdy", - "assets.countries.CW": "Curacao", - "assets.countries.CX": "Vánoční ostrov", - "assets.countries.CY": "Kypr", - "assets.countries.CZ": "Česko", - "assets.countries.DE": "Německo", - "assets.countries.DJ": "Džibutsko", - "assets.countries.DK": "Dánsko", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Dominikánská republika", - "assets.countries.DZ": "Alžírsko", - "assets.countries.EC": "Ekvádor", - "assets.countries.EE": "Estonsko", - "assets.countries.EG": "Egypt", - "assets.countries.EH": "Západní Sahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Španělsko", - "assets.countries.ET": "Etiopie", - "assets.countries.FI": "Finsko", - "assets.countries.FJ": "Fidži", - "assets.countries.FK": "Falklandy (Malvíny)", - "assets.countries.FM": "Mikronésie", - "assets.countries.FO": "Faerské ostrovy", - "assets.countries.FR": "Francie", - "assets.countries.GA": "Gabun", - "assets.countries.GB": "Velká Británie", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Francouzská Guyana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grónsko", - "assets.countries.GM": "Gambie", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Rovníková Guinea", - "assets.countries.GR": "Řecko", - "assets.countries.GS": "Jižní Georgie a Jižní Sandwichovy ostrovy", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hongkong", - "assets.countries.HM": "Heardův ostrov a McDonaldovy ostrovy", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Chorvatsko", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Maďarsko", - "assets.countries.ID": "Indonésie", - "assets.countries.IE": "Irsko", - "assets.countries.IL": "Izrael", - "assets.countries.IM": "Ostrov Man", - "assets.countries.IN": "Indie", - "assets.countries.IO": "Britské indickooceánské území", - "assets.countries.IQ": "Irák", - "assets.countries.IR": "Írán", - "assets.countries.IS": "Island", - "assets.countries.IT": "Itálie", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamajka", - "assets.countries.JO": "Jordánsko", - "assets.countries.JP": "Japonsko", - "assets.countries.KE": "Keňa", - "assets.countries.KG": "Kyrgyzstán", - "assets.countries.KH": "Kambodža", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komory", - "assets.countries.KN": "Svatý Kryštof a Nevis", - "assets.countries.KP": "Korejská lidově demokratická republika", - "assets.countries.KR": "Korea", - "assets.countries.KW": "Kuvajt", - "assets.countries.KY": "Kajmanské ostrovy", - "assets.countries.KZ": "Kazachstán", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Svatá Lucie", - "assets.countries.LI": "Lichtenštejnsko", - "assets.countries.LK": "Srí Lanka", - "assets.countries.LR": "Libérie", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litva", - "assets.countries.LU": "Lucembursko", - "assets.countries.LV": "Lotyšsko", - "assets.countries.LY": "Libye", - "assets.countries.MA": "Maroko", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Moldávie", - "assets.countries.ME": "Černá Hora", - "assets.countries.MF": "Svatý Martin (Francouzská část)", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshallovy ostrovy", - "assets.countries.MK": "Severní Makedonie", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolsko", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Severní Mariany", - "assets.countries.MQ": "Martinik", - "assets.countries.MR": "Mauritánie", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauricius", - "assets.countries.MV": "Maledivy", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexiko", - "assets.countries.MY": "Malajsie", - "assets.countries.MZ": "Mosambik", - "assets.countries.NA": "Namibie", - "assets.countries.NC": "Nová Kaledonie", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolk", - "assets.countries.NG": "Nigérie", - "assets.countries.NI": "Nikaragua", - "assets.countries.NL": "Nizozemsko", - "assets.countries.NO": "Norsko", - "assets.countries.NP": "Nepál", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nový Zéland", - "assets.countries.OM": "Omán", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Francouzská Polynésie", - "assets.countries.PG": "Papua-Nová Guinea", - "assets.countries.PH": "Filipíny", - "assets.countries.PK": "Pákistán", - "assets.countries.PL": "Polsko", - "assets.countries.PM": "Saint-Pierre a Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Portoriko", - "assets.countries.PS": "Palestina", - "assets.countries.PT": "Portugalsko", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Rumunsko", - "assets.countries.RS": "Srbsko", - "assets.countries.RU": "Ruská federace", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Saúdská Arábie", - "assets.countries.SB": "Šalamounovy ostrovy", - "assets.countries.SC": "Seychely", - "assets.countries.SD": "Súdán", - "assets.countries.SE": "Švédsko", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Svatá Helena", - "assets.countries.SI": "Slovinsko", - "assets.countries.SJ": "Špicberky", - "assets.countries.SK": "Slovenská republika", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somálsko", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Jižní Súdán", - "assets.countries.ST": "Svatý Tomáš a Princův ostrov", - "assets.countries.SV": "Salvador", - "assets.countries.SX": "Svatý Martin (Nizozemsko)", - "assets.countries.SY": "Sýrie", - "assets.countries.SZ": "Svazijsko", - "assets.countries.TC": "Turks a Caicos", - "assets.countries.TD": "Čad", - "assets.countries.TF": "Francouzská jižní území", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thajsko", - "assets.countries.TJ": "Tádžikistán", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Východní Timor", - "assets.countries.TM": "Turkmenistán", - "assets.countries.TN": "Tunisko", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turecko", - "assets.countries.TT": "Trinidad a Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Tchaj-wan", - "assets.countries.TZ": "Tanzanie", - "assets.countries.UA": "Ukrajina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Menší odlehlé ostrovy USA", - "assets.countries.US": "Spojené státy americké", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistán", - "assets.countries.VA": "Vatikán", - "assets.countries.VC": "Svatý Vincenc a Grenadiny", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Panenské ostrovy (Velká Británie)", - "assets.countries.VI": "Panenské ostrovy (USA)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallisovy ostrovy", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Jižní Afrika", - "assets.countries.ZM": "Zambie", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB ebook", - "assets.mimetypes.application/msword": "Dokument aplikace Word", - "assets.mimetypes.application/pdf": "Dokument PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Záloha Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Sešit aplikace Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Sešit Excel 2007 s povolenými makry", - "assets.mimetypes.application/vnd.ms-powerpoint": "Prezentace Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Tabulkový dokument OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Šablona tabulkového dokumentu OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Textový dokument OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Šablona textového dokumentu OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Šablona OpenDocument webové stránky", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpointová prezentace (2007)", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpointová slideshow (2007)", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Formulář aplikace Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Šablona aplkace Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Dokument aplikace Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Presentace iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Tabulka iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Dokument iWork Pages", - "assets.mimetypes.application/x-javascript": "Zdroj JavaScript", - "assets.mimetypes.application/x-mspublisher": "Publisher dokument", - "assets.mimetypes.application/x-shockwave-flash": "Flash animace", - "assets.mimetypes.application/xhtml_xml": "XHTML dokument", - "assets.mimetypes.archive": "Archiv ({{$a.EXT}})", - "assets.mimetypes.audio": "Zvukový soubor ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Soubor", - "assets.mimetypes.group:archive": "Archivní soubory", - "assets.mimetypes.group:audio": "Zvukové soubory", - "assets.mimetypes.group:document": "Soubory dokumentů", - "assets.mimetypes.group:html_audio": "Zvukové soubory nativně podporované prohlížeči", - "assets.mimetypes.group:html_track": "Soubory HTML track", - "assets.mimetypes.group:html_video": "Video soubory nativně podporované prohlížeči", - "assets.mimetypes.group:image": "Obrazové soubory", - "assets.mimetypes.group:presentation": "Souboru presentací", - "assets.mimetypes.group:sourcecode": "Zdrojový kód", - "assets.mimetypes.group:spreadsheet": "Tabulkové soubory", - "assets.mimetypes.group:video": "Video soubory", - "assets.mimetypes.group:web_audio": "Zvukové soubory používané na webu", - "assets.mimetypes.group:web_file": "Webové soubory", - "assets.mimetypes.group:web_image": "Obrazové soubory používané na webu", - "assets.mimetypes.group:web_video": "Videosoubory používané na webu", - "assets.mimetypes.image": "Obrázek ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Ikona Windows", - "assets.mimetypes.text/css": "List kaskádového stylu", - "assets.mimetypes.text/csv": "Hodnoty oddělené čárkou", - "assets.mimetypes.text/html": "HTML dokument", - "assets.mimetypes.text/plain": "Textový soubor", - "assets.mimetypes.text/rtf": "Dokument RTF", - "assets.mimetypes.text/vtt": "Formát titulků Web Video Text Track", - "assets.mimetypes.video": "Videosoubor ({{$a.EXT}})", - "core.accounts": "Účty", - "core.add": "Přidat", - "core.agelocationverification": "Ověření věku a polohy", - "core.ago": "před: {{$a}}", - "core.all": "Vše", - "core.allgroups": "Všechny skupiny", - "core.allparticipants": "Všichni účastníci", - "core.answer": "Odpověď", - "core.answered": "Zodpovězeno", - "core.areyousure": "Opravdu?", - "core.back": "Zpět", - "core.block.blocks": "Bloky", - "core.browser": "Prohlížeč", - "core.cancel": "Zrušit", - "core.cannotconnect": "Nelze se připojit", - "core.cannotconnecttrouble": "Máme potíže s připojením k vašim stránkám.", - "core.cannotconnectverify": " Zkontrolujte, zda je adresa správná. ", - "core.cannotdownloadfiles": "Stahování souborů je vypnuto v Mobilních službách webu. Prosím, obraťte se na správce webu.", - "core.captureaudio": "Nahrát zvuk", - "core.capturedimage": "Vyfocený obrázek.", - "core.captureimage": "Vyfotit", - "core.capturevideo": "Nahrát video", - "core.category": "Kategorie", - "core.choose": "Vybrat", - "core.choosedots": "Vyberte...", - "core.clearsearch": "Vymazat vyhledávání", - "core.clicktohideshow": "Klikněte pro rozbalení nebo sbalení", - "core.clicktoseefull": "Kliknutím zobrazit celý obsah.", - "core.close": "Zavřít", - "core.comments": "Komentáře", - "core.comments.addcomment": "Přidat komentář...", - "core.comments.comments": "Komentáře", - "core.comments.commentscount": "Komentáře ({{$a}})", - "core.comments.commentsnotworking": "Komentáře nelze načíst", - "core.comments.deletecommentbyon": "Odstranit komentář zapsaný {{$a.user}} v {{$a.time}}", - "core.comments.eventcommentcreated": "Vložen komentář", - "core.comments.eventcommentdeleted": "Komentář odstraněn", - "core.comments.nocomments": "Bez komentářů", - "core.comments.savecomment": "Uložit komentář", - "core.comments.warningcommentsnotsent": "Komentáře se nepodařilo synchronizovat. {{error}}", - "core.commentscount": "Komentáře ({{$a}})", - "core.completion-alt-auto-fail": "Dokončeno: {{$a}} (nebylo dosaženo požadované známky)", - "core.completion-alt-auto-n": "Nedokončeno: {{$a}}", - "core.completion-alt-auto-n-override": "Nedokončeno: {{$a.modname}} (nastaveno {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Dokončeno: {{$a}} (bylo dosaženo požadované známky)", - "core.completion-alt-auto-y": "Dokončeno: {{$a}}", - "core.completion-alt-auto-y-override": "Dokončeno: {{$a.modname}} (nastaveno {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Nedokončeno: {{$a}}. Výběrem označíte jako dokončeno.", - "core.completion-alt-manual-n-override": "Nedokončeno: {{$a.modname}} (nastaveno {{$a.overrideuser}}). Výběrem označíte jako dokončeno.", - "core.completion-alt-manual-y": "Dokončeno: {{$a}}. Výběrem označíte jako nedokončeno.", - "core.completion-alt-manual-y-override": "Dokončeno: {{$a.modname}} (nastaveno {{$a.overrideuser}}). Výběrem označíte jako nedokončeno.", - "core.confirmcanceledit": "Jste si jisti, že chcete opustit tuto stránku? Všechny změny budou ztraceny.", - "core.confirmdeletefile": "Jste si jisti, že chcete odstranit tento soubor?", - "core.confirmgotabroot": "Opravdu se chcete vrátit k {{name}}?", - "core.confirmgotabrootdefault": "Opravdu chcete přejít na úvodní stránku aktuální karty?", - "core.confirmleaveunknownchanges": "Opravdu chcete opustit tuto stránku? Pokud máte nějaké neuložené změny, budou ztraceny.", - "core.confirmloss": "Jsi si jistý? Všechny změny budou ztraceny.", - "core.confirmopeninbrowser": "Chcete jej otevřít v prohlížeči?", - "core.considereddigitalminor": "Jste příliš mladý/á, abyste na těchto stránkách vytvořili účet.", - "core.content": "Obsah", - "core.contenteditingsynced": "Obsah, který upravujete byl synchronizován.", - "core.contentlinks.chooseaccount": "Vyberte si účet", - "core.contentlinks.chooseaccounttoopenlink": "Vyberte si účet otevřením odkazu.", - "core.contentlinks.confirmurlothersite": "Tento odkaz patří k jinému webu. Chcete jej otevřít?", - "core.contentlinks.errornoactions": "Nelze najít akci, kterou chcete provést s tímto odkazem.", - "core.contentlinks.errornosites": "Nelze najít žádné stránky řešící tento odkaz.", - "core.contentlinks.errorredirectothersite": "Adresa URL přesměrování nemůže ukazovat na jiné stránky.", - "core.continue": "Pokračovat", - "core.copiedtoclipboard": "Text byl zkopírován do schránky", - "core.copytoclipboard": "Zkopírovat do schránky", - "core.course": "Kurz", - "core.course.activitydisabled": "Vaše organizace zakázal tuto činnost v mobilní aplikaci.", - "core.course.activitynotyetviewableremoteaddon": "Tato aktivita je doplněk třetí strany, který ještě není aplikací podporován.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Instalace Moodle vaší organizace je třeba aktualizovat.", - "core.course.allsections": "Všechny sekce", - "core.course.askadmintosupport": "Obraťte se na správce webu a řekněte mu, že tuto činnost chcete používat s mobilní Moodle aplikací.", - "core.course.availablespace": "Momentálně máte k dispozici {{available}} volného místa.", - "core.course.cannotdeletewhiledownloading": "Soubory nelze během stahování aktivity smazat. Počkejte prosím na dokončení stahování.", - "core.course.confirmdeletemodulefiles": "Jste si jisti, že chcete odstranit tyto soubory modulu?", - "core.course.confirmdownload": "Chystáte se stahovat {{size}}.{{availableSpace}} Opravdu chcete pokračovat?", - "core.course.confirmdownloadunknownsize": "Nebyli jsme schopni vypočítat velikost stahování. {{availableSpace}} Opravdu chcete pokračovat?", - "core.course.confirmdownloadzerosize": "Chystáte se začít stahovat. {{AvailableSpace}} Opravdu chcete pokračovat?", - "core.course.confirmlimiteddownload": "Momentálně nejste připojeni k Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Chystáte se stáhnout alespoň {{size}}.{{availableSpace}} Opravdu chcete pokračovat?", - "core.course.contents": "Obsah", - "core.course.couldnotloadsectioncontent": "Nelze načíst obsah sekce. Zkuste to prosím později.", - "core.course.couldnotloadsections": "Nelze načíst sekce. Zkuste to prosím později.", - "core.course.coursesummary": "Shrnutí kurzu", - "core.course.downloadcourse": "Stáhnout kurz", - "core.course.errordownloadingcourse": "Chyba stahování kurzu.", - "core.course.errordownloadingsection": "Chyba při stahování sekce.", - "core.course.errorgetmodule": "Chyba při načítání", - "core.course.hiddenfromstudents": "Skryté před studenty", - "core.course.hiddenoncoursepage": "Je dostupná, ale není zobrazena na stránce kurzu", - "core.course.insufficientavailablequota": "Vaše zařízení nemohlo přidělit prostor pro uložení tohoto stahování. Může to být vyhrazení místa pro aktualizace aplikací a systému. Nejdříve prosím vymažte nějaký úložný prostor.", - "core.course.insufficientavailablespace": "Pokoušíte se stáhnout {{size}}. Vaše zařízení tak nebude mít dostatek místa pro normální provoz. Nejdříve prosím vymažte nějaký úložný prostor.", - "core.course.manualcompletionnotsynced": "Ruční splnění není synchronizováno.", - "core.course.nocontentavailable": "V tuto chvíli není k dispozici žádný obsah.", - "core.course.overriddennotice": "Vaše výsledná známka za tuto činnost byla ručně upravena.", - "core.course.refreshcourse": "Obnovit kurz", - "core.course.sections": "Sekce", - "core.course.useactivityonbrowser": "Můžete i nadále používat jej pomocí prohlížeče zařízení.", - "core.course.warningmanualcompletionmodified": "Ruční splnění aktivity bylo na webu změněno.", - "core.course.warningofflinemanualcompletiondeleted": "Některá offline ruční splnění kurzu \"{{name}}\" byla odstraněna. {{error}", - "core.coursedetails": "Podrobnosti kurzu", - "core.coursenogroups": "Nejste členem žádné skupiny tohoto kurzu.", - "core.courses.addtofavourites": "Kurz označit hvězdičkou", - "core.courses.allowguests": "Tento kurz je otevřen i pro hosty", - "core.courses.availablecourses": "Dostupné kurzy", - "core.courses.cannotretrievemorecategories": "Kategorie hlubší než úroveň {{$a}} nelze načíst.", - "core.courses.categories": "Kategorie kurzů", - "core.courses.confirmselfenrol": "Jste si jisti, že chcete zapsat se do tohoto kurzu?", - "core.courses.courses": "Kurzy", - "core.courses.downloadcourses": "Stáhnout kurzy", - "core.courses.enrolme": "Zapsat se", - "core.courses.errorloadcategories": "Při načítání kategorií došlo k chybě.", - "core.courses.errorloadcourses": "Při načítání kurzů došlo k chybě.", - "core.courses.errorloadplugins": "Doplňky, které tento kurz vyžaduje, nelze správně načíst. Načtěte opět aplikaci a zkuste to znovu.", - "core.courses.errorsearching": "Při vyhledávání došlo k chybě.", - "core.courses.errorselfenrol": "Při zápisu sebe sama došlo k chybě.", - "core.courses.filtermycourses": "Filtrovat mé kurzy", - "core.courses.frontpage": "Titulní stránka", - "core.courses.hidecourse": "Odstranit z přehledu", - "core.courses.ignore": "Ignorovat", - "core.courses.mycourses": "Moje kurzy", - "core.courses.mymoodle": "Nástěnka", - "core.courses.nocourses": "Žádné dostupné informace o kurzech", - "core.courses.nocoursesyet": "Žádný kurz v této kategorii", - "core.courses.nosearchresults": "Žádné výsledky", - "core.courses.notenroled": "Nejste zapsáni v tomto kurzu", - "core.courses.notenrollable": "Do tohoto kurzu se nemůžete sami zapsat.", - "core.courses.password": "Klíč zápisu", - "core.courses.paymentrequired": "Tento kurz je placený", - "core.courses.paypalaccepted": "Platby přes PayPal přijímány", - "core.courses.reload": "Obnovit", - "core.courses.removefromfavourites": "Tomuto kurzu odstranit hvězdičku", - "core.courses.search": "Hledat", - "core.courses.searchcourses": "Vyhledat kurzy", - "core.courses.searchcoursesadvice": "Můžete použít tlačítko Vyhledat kurzy, pracovat jako host nebo se zapsat do kurzů, které to umožňují.", - "core.courses.selfenrolment": "Zápis sebe sama", - "core.courses.sendpaymentbutton": "Poslat platbu přes službu PayPal", - "core.courses.show": "Obnovit zobrazení tohoto kurzu", - "core.courses.totalcoursesearchresults": "Celkem kurzů: {{$a}}", - "core.currentdevice": "Aktuální zařízení", - "core.datastoredoffline": "Data byla uložena na zařízení, protože nemohla být odeslána. Budou odeslána automaticky později.", - "core.date": "Datum", - "core.day": "den", - "core.days": "dnů", - "core.decsep": ",", - "core.defaultvalue": "Výchozí ({{$a}})", - "core.delete": "Odstranit", - "core.deletedoffline": "Odstraněno offline", - "core.deleteduser": "Odstraněný uživatel", - "core.deleting": "Odstraňování", - "core.description": "Popis", - "core.desktop": "Desktop", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Nezletilý pro digitální souhlas", - "core.digitalminor_desc": "Požádejte svého rodiče/opatrovníka, aby kontaktoval:", - "core.discard": "Odstranit", - "core.dismiss": "Odmítnout", - "core.displayoptions": "Možnosti zobrazení", - "core.done": "Hotovo", - "core.download": "Stáhnout", - "core.downloaded": "Staženo", - "core.downloading": "Stahování", - "core.edit": "Upravit", - "core.editor.autosavesucceeded": "Pracovní verze uložena.", - "core.editor.bold": "Tučně", - "core.editor.clear": "Vymazat formátování", - "core.editor.h3": "Nadpis (velký)", - "core.editor.h4": "Nadpis (střední)", - "core.editor.h5": "Nadpis (malý)", - "core.editor.hidetoolbar": "Skrýt panel nástrojů", - "core.editor.italic": "Kurzíva", - "core.editor.orderedlist": "Číslovaný seznam", - "core.editor.p": "Odstavec", - "core.editor.strike": "Přeškrtnuté", - "core.editor.textrecovered": "Pracovní verze tohoto textu byla automaticky obnovena.", - "core.editor.toggle": "Přepnout editor", - "core.editor.underline": "Podtržení", - "core.editor.unorderedlist": "Seznam", - "core.emptysplit": "Pokud je levý panel prázdný nebo se nahrává, zobrazí se tato stránka prázdná.", - "core.error": "Chyba", - "core.errorchangecompletion": "Při změně stavu dokončení došlo k chybě. Prosím zkuste to znovu.", - "core.errordeletefile": "Chyba při odstraňování souboru. Prosím zkuste to znovu.", - "core.errordownloading": "Chyba při stahování souboru", - "core.errordownloadingsomefiles": "Chyba při stahování souborů modulu. Některé soubory mohou chybět.", - "core.errorfileexistssamename": "Soubor s tímto názvem již existuje.", - "core.errorinvalidform": "Formulář obsahuje neplatná data. Nezapomeňte prosím vyplnit všechna požadovaná pole a platná data .", - "core.errorinvalidresponse": "Dostali jste neplatnou odpověď. Pokud chyba přetrvává, obraťte se na správce Moodle .", - "core.errorloadingcontent": "Chyba při načítání obsahu.", - "core.errorofflinedisabled": "Prohlížení offline je na vašem webu zakázáno. Chcete-li používat aplikaci, musíte být připojeni k internetu.", - "core.erroropenfilenoapp": "Chyba při otevírání souboru: žádnou aplikací nelze otevřít tento typ souboru.", - "core.erroropenfilenoextension": "Chyba při otevírání souboru: soubor nemá příponu.", - "core.erroropenpopup": "Tato aktivita se pokouší otevřít vyskakovací okno. Tato možnost není v této aplikaci podporována.", - "core.errorrenamefile": "Chyba při přejmenování souboru. Prosím zkuste to znovu.", - "core.errorsomedatanotdownloaded": "Pokud jste tuto aktivitu stáhli, všimněte si, že některá data nejsou během procesu stahování stahována z důvodu výkonu a využití dat.", - "core.errorsync": "Při synchronizaci došlo k chybě. Zkuste to prosím znovu.", - "core.errorsyncblocked": "{{$a}} nelze nyní synchronizovat z důvodu probíhajícího procesu. Zkuste to prosím znovu později. Pokud problém přetrvává, zkuste restartovat aplikaci.", - "core.explanationdigitalminor": "Tyto informace jsou nutné k určení, zda je váš věk nad věkovou hranicí pro digitální souhlas. Jedná se o věk, kdy může jednotlivec souhlasit s podmínkami a a jejich údaje jsou legálně uchovávány a zpracovávány.", - "core.favourites": "Označeno hvězdičkou", - "core.filename": "Název souboru", - "core.filenameexist": "Jméno souboru již existuje: {{$a}}", - "core.filenotfound": "Litujeme, soubor nebyl nalezen", - "core.fileuploader.addfiletext": "Přidat soubor", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Fotoaparát", - "core.fileuploader.confirmuploadfile": "Chystáte nahrát {$a}. Jste si jisti, že chcete pokračovat?", - "core.fileuploader.confirmuploadunknownsize": "Nebyli jsme schopni vypočítat velikost nahrávaného souboru. Jste si jisti, že chcete pokračovat?", - "core.fileuploader.errorcapturingaudio": "Chyba snímání zvuku.", - "core.fileuploader.errorcapturingimage": "Chyba snímání obrazu.", - "core.fileuploader.errorcapturingvideo": "Chyba snímání videa.", - "core.fileuploader.errorgettingimagealbum": "Chyba při načítání snímku z alba.", - "core.fileuploader.errormustbeonlinetoupload": "Při nahrávání souboru musíte být online.", - "core.fileuploader.errornoapp": "Pro provedení této akce nemáte nainstalovanou aplikaci .", - "core.fileuploader.errorreadingfile": "Chyba při čtení souboru.", - "core.fileuploader.errorwhileuploading": "Při pokusu o nahrání souboru došlo k chybě .", - "core.fileuploader.file": "Soubor", - "core.fileuploader.filesofthesetypes": "Povolené typy souborů:", - "core.fileuploader.fileuploaded": "Soubor nahrán", - "core.fileuploader.invalidfiletype": "Typ souboru {{$a}} nemůže být přijat", - "core.fileuploader.maxbytesfile": "Soubor {{$a.file}} je příliš veliký. Maximální velikost nahrávaného souboru je {{$a.size}}.", - "core.fileuploader.more": "Podrobněji", - "core.fileuploader.photoalbums": "Fotoalba", - "core.fileuploader.readingfile": "Čtení souboru", - "core.fileuploader.readingfileperc": "Načítání souboru: {{$a}}%", - "core.fileuploader.selectafile": "Vyberte soubor", - "core.fileuploader.uploadafile": "Nahrát soubor", - "core.fileuploader.uploading": "Nahrávání", - "core.fileuploader.uploadingperc": "Nahrávání: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filtr", - "core.folder": "Složka", - "core.forcepasswordchangenotice": "Před pokračováním si musíte změnit heslo.", - "core.fulllistofcourses": "Všechny kurzy", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Průměr", - "core.grades.badgrade": "Přiřazená známka není platná", - "core.grades.contributiontocoursetotal": "Podíl z celkové známky", - "core.grades.feedback": "Komentář", - "core.grades.grade": "Známka", - "core.grades.gradeitem": "Položka hodnocení", - "core.grades.grades": "Známky", - "core.grades.lettergrade": "Slovní známka", - "core.grades.nogradesreturned": "Nebyly nalezeny žádné známky", - "core.grades.nooutcome": "Bez očekávaných výstupů", - "core.grades.percentage": "Procentuální hodnota", - "core.grades.range": "Rozsah", - "core.grades.rank": "Umístění", - "core.grades.weight": "Váha", - "core.group": "Skupina", - "core.groupsseparate": "Oddělené skupiny", - "core.groupsvisible": "Viditelné skupiny", - "core.h5p.additionallicenseinfo": "Další informace o licenci", - "core.h5p.author": "Autor", - "core.h5p.authorcomments": "Komentáře autora", - "core.h5p.authorcommentsdescription": "Komentáře pro editora obsahu. (Tento text nebude zveřejněn jako součást informací o autorských právech.)", - "core.h5p.authorname": "Jméno autora", - "core.h5p.authorrole": "Role autora", - "core.h5p.by": "od", - "core.h5p.cancellabel": "Zrušit", - "core.h5p.ccattribution": "Uveďte původ (CC BY)", - "core.h5p.ccattributionnc": "Uveďte původ-Neužívejte komerčně (CC BY-NC)", - "core.h5p.ccattributionncnd": "Uveďte původ-Neužívejte komerčně-Nezpracovávejte (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Uveďte původ-Neužívejte dílo komerčně-Zachovejte licenci (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Uveďte původ-Nezpracovávejte (CC BY-ND)", - "core.h5p.ccattributionsa": "Uveďte původ-Zachovejte licenci (CC BY-SA)", - "core.h5p.ccpdd": "Public Domain Dedication (CC0)", - "core.h5p.changedby": "Změnil", - "core.h5p.changedescription": "Popis změny", - "core.h5p.changelog": "Protokol změn", - "core.h5p.changeplaceholder": "Oříznutí fotografie, změněný text atd.", - "core.h5p.close": "Zavřeno", - "core.h5p.confirmdialogbody": "Potvrďte, že chcete pokračovat. Tuto akci nelze vrátit zpět.", - "core.h5p.confirmdialogheader": "Potvrdit akci", - "core.h5p.confirmlabel": "Potvrdit", - "core.h5p.connectionLost": "Připojení ztraceno. Výsledky budou uloženy a odeslány po obnovení spojení.", - "core.h5p.connectionReestablished": "Připojení bylo obnoveno.", - "core.h5p.contentCopied": "Obsah je zkopírován do schránky", - "core.h5p.contentchanged": "Tento obsah se od posledního použití změnil.", - "core.h5p.contenttype": "Typ obsahu", - "core.h5p.copyright": "Práva na užívání", - "core.h5p.copyrightinfo": "Informace o autorských právech", - "core.h5p.copyrightstring": "Autorská práva", - "core.h5p.copyrighttitle": "Zobrazit informace o autorských právech k tomuto obsahu.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Datum", - "core.h5p.disablefullscreen": "Zakázat celou obrazovku", - "core.h5p.download": "Stáhnout", - "core.h5p.downloadtitle": "Stáhnout tento obsah jako soubor H5P.", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Vložen", - "core.h5p.embedtitle": "Zobrazit vložený kód tohoto obsahu.", - "core.h5p.fullscreen": "Celá obrazovka", - "core.h5p.gpl": "General Public License v3", - "core.h5p.h5ptitle": "Navštivte stránku h5p.org a podívejte se na další obsah.", - "core.h5p.hideadvanced": "Skrýt pokročilé", - "core.h5p.license": "Licence", - "core.h5p.licenseCC010": "CC0 1.0 Univerzální (CC0 1.0) Potvrzení o statusu volného díla", - "core.h5p.licenseCC010U": "CC0 1.0 Univerzální", - "core.h5p.licenseCC10": "1.0 Generic", - "core.h5p.licenseCC20": "2.0 Generic", - "core.h5p.licenseCC25": "2.5 Generic", - "core.h5p.licenseCC30": "3.0 Unported", - "core.h5p.licenseCC40": "4.0 Mezinárodní", - "core.h5p.licenseGPL": "GPL - Obecná veřejná licence", - "core.h5p.licenseV1": "Verze 1", - "core.h5p.licenseV2": "Verze 2", - "core.h5p.licenseV3": "Verze 3", - "core.h5p.licensee": "Držitel licence", - "core.h5p.licenseextras": "Extra licence", - "core.h5p.licenseversion": "Verze licence", - "core.h5p.nocopyright": "Pro tento obsah nejsou k dispozici žádné informace o autorských právech.", - "core.h5p.offlineDialogBody": "Nepodařilo se nám odeslat informace o dokončení tohoto úkolu. Zkontrolujte připojení k internetu.", - "core.h5p.offlineDialogHeader": "Vaše připojení k serveru bylo ztraceno", - "core.h5p.offlineDialogRetryButtonLabel": "Zkuste to znovu", - "core.h5p.offlineDialogRetryMessage": "Opakovat za :num....", - "core.h5p.offlineSuccessfulSubmit": "Výsledky byly úspěšně odeslány.", - "core.h5p.offlinedisabled": "Stránka neumožňuje stahování balíčků H5P.", - "core.h5p.originator": "Původce", - "core.h5p.pd": "Veřejná doména", - "core.h5p.pddl": "Public Domain Dedication a licence", - "core.h5p.pdm": "Public Domain Mark (PDM)", - "core.h5p.play": "Spustit H5P", - "core.h5p.resizescript": "Chcete-li dynamicky měnit velikost vloženého obsahu, zahrňte na svůj web tento skript:", - "core.h5p.resubmitScores": "Pokus o odeslání uložených výsledků.", - "core.h5p.reuse": "Opětovné použití", - "core.h5p.reuseContent": "Opětovné použití obsahu", - "core.h5p.reuseDescription": "Znovu použít tento obsah.", - "core.h5p.showadvanced": "Zobrazit pokročilé", - "core.h5p.showless": "Zobrazit méně", - "core.h5p.showmore": "Zobrazit více", - "core.h5p.size": "Velikost", - "core.h5p.source": "Zdroj", - "core.h5p.startingover": "Začněte znovu.", - "core.h5p.sublevel": "Nižší úroveň", - "core.h5p.thumbnail": "Náhled", - "core.h5p.title": "Nadpis", - "core.h5p.undisclosed": "Nezveřejněno", - "core.h5p.year": "Rok", - "core.h5p.years": "Rok(y)", - "core.h5p.yearsfrom": "Rok (od)", - "core.h5p.yearsto": "Rok (do)", - "core.hasdatatosync": "{{$a}} má offline data, která mají být synchronizována.", - "core.help": "Nápověda", - "core.hide": "Skrýt", - "core.hour": "hodina", - "core.hours": "hodin", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Obrázek", - "core.imageviewer": "Prohlížeč obrázků", - "core.info": "Informace", - "core.invalidformdata": "Neplatná formulářová data", - "core.labelsep": ": ", - "core.lastaccess": "Poslední přístup", - "core.lastdownloaded": "Poslední stažení", - "core.lastmodified": "Naposledy změněno", - "core.lastsync": "Poslední synchronizace", - "core.layoutgrid": "Mřížka", - "core.list": "Seznam", - "core.listsep": ";", - "core.loading": "Nahrávání", - "core.loadmore": "Načíst další", - "core.location": "Umístění", - "core.login.auth_email": "Registrace na základě e-mailu", - "core.login.authenticating": "Autentizace", - "core.login.cancel": "Zrušit", - "core.login.changepassword": "Změnit heslo", - "core.login.changepasswordbutton": "Otevřít stránku pro změnu hesla", - "core.login.changepasswordhelp": "Pokud máte problémy se změnou hesla, obraťte se na správce stránek. „Správci stránek“ jsou lidé, kteří řídí Moodle ve vaší škole / univerzitě / společnosti nebo vzdělávací organizaci. Pokud nevíte, jak je kontaktovat, kontaktujte prosím své učitele / lektory.", - "core.login.changepasswordinstructions": "V aplikaci nemůžete změnit své heslo. Kliknutím na následující tlačítko otevřete stránky ve webovém prohlížeči a změňte své heslo. Vezměte v úvahu, že po změně hesla je třeba prohlížeč zavřít, protože nebudete přesměrováni na aplikaci.", - "core.login.changepasswordlogoutinstructions": "Pokud chcete změnit stránky nebo se odhlásit, klikněte na následující tlačítko:", - "core.login.changepasswordreconnectinstructions": "Klepnutím na následující tlačítko se znovu připojíte ke stránkám. (Vezměte na vědomí, že pokud jste heslo nezměnili úspěšně, vrátíte se na předchozí obrazovku).", - "core.login.confirmdeletesite": "Jste si jisti, že chcete smazat web {{sitename}}?", - "core.login.connect": "Připojit!", - "core.login.connecttomoodle": "Připojit k Moodle", - "core.login.connecttomoodleapp": "Pokoušíte se připojit k běžnému serveru Moodle. Stáhněte si oficiální aplikaci Moodle pro přístup na tento web.", - "core.login.connecttoworkplaceapp": "Pokoušíte se připojit k webu Moodle Workplace. Stáhněte si aplikaci Moodle Workplace pro přístup na tento web.", - "core.login.contactyouradministrator": "Pro další pomoc se obraťte na správce webu.", - "core.login.contactyouradministratorissue": "Požádejte správce, prosím, aby zkontroloval následující problém: {{$a}}", - "core.login.createaccount": "Vytvořit můj nový účet", - "core.login.createuserandpass": "Vytvořit nové uživatelské jméno a heslo pro přihlášení", - "core.login.credentialsdescription": "Pro přihlášení uveďte své uživatelské jméno a heslo.", - "core.login.emailconfirmsent": "

    Na vaši adresu {{$a}} byl odeslán e-mail s jednoduchými pokyny k dokončení vaší registrace.

    Narazíte-li na nějaké obtíže, spojte se se správcem těchto stránek.

    ", - "core.login.emailconfirmsentnoemail": "

    Byla odeslána e-mailová adresa na vaši adresu.

    Obsahuje jednoduché pokyny k dokončení registrace.

    Pokud máte nadále potíže, obraťte se na správce stránek.

    ", - "core.login.emailconfirmsentsuccess": "Potvrzovací e-mail byl úspěšně odeslán", - "core.login.emailnotmatch": "E-maily se neshodují", - "core.login.erroraccesscontrolalloworigin": "Cross-Origin volání - váš pokus o provedení byl odmítnut. Zkontrolujte prosím https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Při odstraňování této stránky došlo k chybě. Prosím, zkuste to znovu.", - "core.login.errorexampleurl": "URL https://campus.example.edu je pouze příklad URL adresy, nejedná se o skutečný web. Použijte prosím adresu URL vaší školy nebo organizace. ", - "core.login.errorupdatesite": "Došlo k chybě při aktualizaci tokenu webu.", - "core.login.faqcannotconnectanswer": "Obraťte se prosím na správce stránek.", - "core.login.faqcannotconnectquestion": "Zadal jsem správně adresu svých stránek, ale přesto se nemůžu připojit.", - "core.login.faqsetupsiteanswer": "Navštivte {{$link}} a podívejte se na různé možnosti, jak si vytvořit své vlastní stránky Moodle.", - "core.login.faqsetupsitelinktitle": "Začít.", - "core.login.faqsetupsitequestion": "Chci si založit vlastní stránky Moodle.", - "core.login.faqtestappanswer": "Chcete-li testovat aplikaci na demonstračním webu Moodle, do pole „Adresa vašeho webu“ zadejte „učitel“ nebo „student“ a klikněte na tlačítko „Připojit!“.", - "core.login.faqtestappquestion": "Chci jen vyzkoušet aplikaci, co mám dělat?", - "core.login.faqwhatisurlanswer": "

    Každá organizace nebo škola má svou vlastní adresu pro své stránky Moodle.

    Chcete-li najít adresu serveru Moodle, ke kterému se chcete připojit, proveďte následující kroky:

    \n
    1. Otevřete webový prohlížeč a přejděte na přihlašovací stránku Moodle vaší školy nebo organizace
    2. V horní části stránky na adresním řádku se zobrazí URL adresa vašeho Moodle. Např. \"campus.example.edu\".
      {{$ image}}
    3. \n
    4. Zkopírujte adresu (nekopírujte / přihlášení a co přijde), vložte ji do aplikace Moodle a klikněte na \"Připojit!\"
    5. Nyní jste se mohou přihlásit na vaše stránky pomocí svého uživatelského jména a hesla
    6. ", - "core.login.faqwhatisurlquestion": "Jaká je URL adresa mých stránek Moodle? Jak najdu stránky své školy?", - "core.login.faqwhereisqrcode": "Kde najdu QR kód?", - "core.login.faqwhereisqrcodeanswer": "

      Pokud to vaše organizace povolila, najdete QR kód na webu ve spodní části stránky vašeho uživatelského profilu.

      {{$ $}}", - "core.login.findyoursite": "Najít stránky", - "core.login.firsttime": "Jste tady poprvé?", - "core.login.forcepasswordchangenotice": "Před pokračováním si musíte změnit heslo.", - "core.login.forgotten": "Zapomněli jste své uživatelské jméno či heslo?", - "core.login.help": "Nápověda", - "core.login.helpmelogin": "

      Existuje mnoho tisíců stránek Moodle po celém světě. Tato aplikace se může připojit pouze ke stránkám Moodle, které specificky povolily přístup k mobilním aplikacím.

      Nemůžete-li se připojit k vašim stránkám Moodle, musíte se obrátit na správce stránek a požádat ho o přečtení http://docs.moodle.org/en/Mobile_app

      Otestujte aplikaci v demo Moodle jako učitel nebo student na poli adresa stránek a klikněte na Připojení.

      ", - "core.login.instructions": "Pokyny", - "core.login.invalidaccount": "Zkontrolujte si prosím své přihlašovací údaje nebo se obraťte na správce webu, aby zkontroloval nastavení.", - "core.login.invaliddate": "Neplatné datum", - "core.login.invalidemail": "Neplatná e-mailová adresa", - "core.login.invalidmoodleversion": "

      Neplatná verze Moodle. Aplikace Moodle podporuje systémy Moodle pouze {{$a}}.

      Můžete kontaktovat správce stránek a požádat je o aktualizaci systému Moodle.

      „Správci stránek“ jsou lidé, kteří řídí Moodle ve vaší škole / univerzitě / společnosti nebo vzdělávací organizaci. Pokud nevíte, jak je kontaktovat, kontaktujte prosím své učitele / lektory.

      ", - "core.login.invalidsite": "Adresa URL stránky je chybná", - "core.login.invalidtime": "Neplatný čas", - "core.login.invalidurl": "Chybná specifikace URL", - "core.login.invalidvaluemax": "Maximální hodnota je {{$a}}", - "core.login.invalidvaluemin": "Minimální hodnota je {{$a}}", - "core.login.localmobileunexpectedresponse": "Kontrola rozšířených vlastností Moodle Mobile vrátil neočekávanou odezvu. Budete ověřen pomocí standardních služeb mobilu .", - "core.login.loggedoutssodescription": "Musíte se znovu autentizovat. Musíte se přihlásit na stránky v okně prohlížeče.", - "core.login.login": "Přihlášení", - "core.login.loginbutton": "Přihlásit se", - "core.login.logininsiterequired": "Váš Moodlu vás nutí k přihlášení pomocí prohlížeče. Nová instance prohlížeče otevře přesměrování na vaše stránky Moodle.", - "core.login.loginsteps": "K plnému přístupu na tyto stránky, musíte nejprve vytvořit účet.", - "core.login.missingemail": "Chybí e-mailová adresa", - "core.login.missingfirstname": "Chybí křestní jméno", - "core.login.missinglastname": "Chybí příjmení", - "core.login.mobileservicesnotenabled": "Na vašem webu nejsou povoleny mobilní služby. Pokud si myslíte, že by měl být povolen mobilní přístup, obraťte se prosím na správce Moodlu.", - "core.login.mustconfirm": "Musíte potvrdit svůj účet", - "core.login.newaccount": "Nový účet", - "core.login.notloggedin": "Musíte být přihlášeni.", - "core.login.onboardingcreatemanagecourses": "Vytvářet a spravovat své kurzy", - "core.login.onboardingenrolmanagestudents": "Zapsat se a spravovat své studenty", - "core.login.onboardinggetstarted": "Začínáme s Moodle", - "core.login.onboardingialreadyhaveasite": "Už mám stránky Moodle", - "core.login.onboardingimalearner": "Jsem student", - "core.login.onboardingimaneducator": "Jsem pedagog", - "core.login.onboardingineedasite": "Potřebuji stránky Moodle", - "core.login.onboardingprovidefeedback": "Poskytnout aktuální zpětnou vazbu", - "core.login.onboardingtoconnect": "Pro připojení k aplikaci Moodle potřebujete stránky Moodle", - "core.login.onboardingwelcome": "Vítejte v aplikaci Moodle!", - "core.login.or": "NEBO", - "core.login.password": "Heslo", - "core.login.passwordforgotten": "Zapomenuté heslo", - "core.login.passwordforgotteninstructions2": "Pro resetování hesla níže vložte své uživatelské jméno nebo emailovou adresu. Pokud se podaří Vás najít v databázi, bude na Vaší e-mailovou adresu odeslán mail s instrukcemi, jak znova získat přístup.", - "core.login.passwordrequired": "Je požadováno heslo", - "core.login.policyaccept": "Rozumím a souhlasím", - "core.login.policyagree": "Před dalším používáním těchto stránek musíte souhlasit s těmito pravidly. Souhlasíte s nimi?", - "core.login.policyagreement": "Souhlas s pravidly používání stránek", - "core.login.policyagreementclick": "Odkaz na Souhlas s pravidly používání těchto stránek", - "core.login.potentialidps": "Přihlásit se pomocí účtu na:", - "core.login.profileinvaliddata": "Neplatná hodnota", - "core.login.recaptchachallengeimage": "Změnit obrázek reCAPTCHA", - "core.login.recaptchaexpired": "Ověření vypršelo. Odpovězte na bezpečnostní otázku znovu.", - "core.login.recaptchaincorrect": "Odpověď na bezpečnostní otázku je nesprávná.", - "core.login.reconnect": "Znovu připojit", - "core.login.reconnectdescription": "Váš token autentizace je neplatný nebo vypršel. Musíte se znovu připojit k serveru.", - "core.login.reconnectssodescription": "Váš token autentizace je neplatný nebo vypršel. Musíte se znovu připojit k serveru. Musíte se přihlásit na stránky v okně prohlížeče.", - "core.login.resendemail": "Přeposlat email", - "core.login.searchby": "Hledat pomocí:", - "core.login.security_question": "Bezpečnostní otázka", - "core.login.selectacountry": "Vyberte zemi", - "core.login.selectsite": "Vyberte prosím vaše stránky:", - "core.login.signupplugindisabled": "{{$a}} není povoleno.", - "core.login.siteaddress": "Adresa vaší stránky", - "core.login.sitehasredirect": "Váš web obsahuje alespoň jedno přesměrování HTTP. Aplikace nemůže sledovat přesměrování, může to být problém, který brání připojení aplikace k vašim stránkám.", - "core.login.siteinmaintenance": "Váš web je v režimu údržby", - "core.login.sitepolicynotagreederror": "Zásady bezpečnosti nebyly odsouhlaseny.", - "core.login.siteurl": "URL adresa stránky", - "core.login.siteurlrequired": "Je požadováno URL webu, tj. https://www.yourmoodlesite.org", - "core.login.startsignup": "Začněte nyní vytvořením nového účtu!", - "core.login.stillcantconnect": "Stále se nemůže připojit?", - "core.login.supplyinfo": "Více informací", - "core.login.username": "Uživatelské jméno", - "core.login.usernameoremail": "Uživatelské jméno nebo emailová adresa", - "core.login.usernamerequired": "Požadováno uživatelské jméno", - "core.login.usernotaddederror": "Uživatel nebyl přidán - neznámá chyba", - "core.login.visitchangepassword": "Chcete navštívit stránky a změnit heslo?", - "core.login.webservicesnotenabled": "Váš hostitelský web možná nemá povoleny webové služby. Požádejte o pomoc svého správce.", - "core.login.youcanstillconnectwithcredentials": "Stále se můžete připojit ke stránkám zadáním uživatelského jména a hesla.", - "core.login.yourenteredsite": "Připojit se ke svým stránkám", - "core.lostconnection": "Váš token je neplatný nebo vypršel. Budete se muset znovu připojit k webu.", - "core.mainmenu.changesite": "Změnit stránky", - "core.mainmenu.help": "Nápověda", - "core.mainmenu.logout": "Odhlásit se", - "core.mainmenu.website": "Webová stránka", - "core.maxsizeandattachments": "Maximální velikost souborů: {{$a.size}}, maximální počet souborů: {{$a.attachments}}", - "core.min": "min.", - "core.mins": "min.", - "core.misc": "Různé", - "core.mod_assign": "Úkol", - "core.mod_assignment": "Úkol 2.2 (Zakázáno)", - "core.mod_book": "Kniha", - "core.mod_chat": "Chat", - "core.mod_choice": "Anketa", - "core.mod_data": "Databáze", - "core.mod_database": "Databáze", - "core.mod_external-tool": "Externí nástroj", - "core.mod_feedback": "Dotazník", - "core.mod_file": "Soubor", - "core.mod_folder": "Složka", - "core.mod_forum": "Fórum", - "core.mod_glossary": "Slovník", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "Balíček IMS", - "core.mod_imscp": "Balíček IMS", - "core.mod_label": "Popisek", - "core.mod_lesson": "Přednáška", - "core.mod_lti": "Externí nástroj", - "core.mod_page": "Stránka", - "core.mod_quiz": "Test", - "core.mod_resource": "Soubor", - "core.mod_scorm": "SCORM balíček", - "core.mod_survey": "Průzkum", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Workshop", - "core.moduleintro": "Popis", - "core.more": "více", - "core.mygroups": "Moje skupiny", - "core.name": "Název", - "core.needhelp": "Potřebujete pomoc?", - "core.networkerroriframemsg": "Tento obsah není k dispozici v režimu offline. Připojte se prosím k internetu a zkuste to znovu.", - "core.networkerrormsg": "Při připojování k webu došlo k problému. Zkontrolujte připojení a zkuste to znovu.", - "core.never": "Nikdy", - "core.next": "Další", - "core.no": "Ne", - "core.nocomments": "Bez komentářů", - "core.nograde": "Bez známky", - "core.none": "Žádný", - "core.nooptionavailable": "Není k dispozici žádná možnost", - "core.nopasswordchangeforced": "Nelze pokračovat beze změny hesla.", - "core.nopermissionerror": "Je nám líto, ale momentálně k tomu nemáte oprávnění", - "core.nopermissions": "Je mi líto, ale momentálně nemáte oprávnění vykonat tuto operaci ({{$a}}).", - "core.noresults": "Bez výsledků", - "core.noselection": "Žádná volba", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Profil není dostupný, protože úživatel není zapsán v tomto kurzu", - "core.notice": "Poznámka", - "core.notingroup": "Je nám líto, ale musíte být součástí skupiny a zobrazit tuto stránku.", - "core.notsent": "Neodesláno", - "core.now": "nyní", - "core.nummore": "{{$a}} dalších", - "core.numwords": "{{$a}} slov", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Zde klikněte pro zobrazení obrázku v plné velikosti", - "core.openinbrowser": "Otevřít v prohlížeči", - "core.openmodinbrowser": "Otevřít {{$a}} v prohlížeči", - "core.othergroups": "Další skupiny", - "core.pagea": "Stránka {{$a}}", - "core.paymentinstant": "Pomocí tlačítka níže můžete provést platbu a během několika minut se zapsat do kurzu!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pictureof": "Obrázek: {{$a}}", - "core.previous": "Předchozí", - "core.proceed": "Pokračovat", - "core.pulltorefresh": "Stáhněte pro obnovu", - "core.qrscanner": "QR skener", - "core.question.answer": "Odpověď", - "core.question.answersaved": "Odpověď uložena", - "core.question.cannotdeterminestatus": "Stav nelze určit", - "core.question.certainty": "Jistota", - "core.question.complete": "Hotovo", - "core.question.correct": "Správně", - "core.question.errorattachmentsnotsupported": "Aplikace ještě nepodporuje připojování souborů k odpovědím.", - "core.question.errorinlinefilesnotsupported": "Aplikace ještě nepodporuje úpravy vložených souborů.", - "core.question.errorquestionnotsupported": "Tento typ úlohy není aplikací podporován: {{$a}}.", - "core.question.feedback": "Reakce", - "core.question.howtodraganddrop": "Klepnutím vyberte potom klepněte na místo umístění.", - "core.question.incorrect": "Nesprávně", - "core.question.information": "Informace", - "core.question.invalidanswer": "Neúplná odpověď", - "core.question.notanswered": "Nezodpovězeno", - "core.question.notyetanswered": "Dosud nezodpovězeno", - "core.question.partiallycorrect": "Částečně správně", - "core.question.questionmessage": "Úloha {{$a}}: {{$b}}", - "core.question.questionno": "Úloha {{$a}}", - "core.question.requiresgrading": "Vyžaduje hodnocení", - "core.quotausage": "Právě jste použili {{$a.used}} z vašeho {{$a.total}} limitu.", - "core.rating.aggregateavg": "Průměr hodnocení", - "core.rating.aggregatecount": "Počet hodnocení", - "core.rating.aggregatemax": "Nejvyšší hodnocení", - "core.rating.aggregatemin": "Nejnižší hodnocení", - "core.rating.aggregatesum": "Součet hodnocení", - "core.rating.noratings": "Bez hodnocení", - "core.rating.rating": "Hodnocení", - "core.rating.ratings": "Hodnocení", - "core.redirectingtosite": "Budete přesměrováni na web.", - "core.refresh": "Obnovit", - "core.remove": "Odstranit", - "core.removefiles": "Odstranit soubory {{$a}}", - "core.required": "Vyžadováno", - "core.requireduserdatamissing": "Tento uživatel nemá některá požadovaná data v profilu. Prosím, vyplňte tato data v systému Moodle a zkuste to znovu.
      {{$a}}", - "core.resourcedisplayopen": "Otevřít", - "core.resources": "Studijní materiály", - "core.restore": "Obnovit", - "core.restricted": "Nedostupné", - "core.retry": "Opakovat", - "core.save": "Uložit", - "core.savechanges": "Uložit změny", - "core.scanqr": "Naskenovat QR kód", - "core.search": "Hledat", - "core.searching": "Hledání", - "core.searchresults": "Výsledky hledání", - "core.sec": "sek.", - "core.secs": "sekund", - "core.seemoredetail": "Více podrobností...", - "core.selectacategory": "Vyberte kategorii", - "core.selectacourse": "Vyberte kurz", - "core.selectagroup": "Vyberte skupinu", - "core.send": "Odeslat", - "core.sending": "Odesílání", - "core.serverconnection": "Chyba spojení se serverem", - "core.settings.about": "O aplikaci", - "core.settings.appsettings": "Nastavení aplikace", - "core.settings.appversion": "Verze aplikace", - "core.settings.cannotsyncoffline": "Nelze synchronizovat offline.", - "core.settings.cannotsyncwithoutwifi": "Nelze synchronizovat, protože aktuální nastavení umožňují pouze k synchronizaci při připojení k Wi-Fi. Prosím, připojte se k síti Wi-Fi.", - "core.settings.colorscheme": "Barevné schéma", - "core.settings.colorscheme-auto": "Automaticky (na základě nastavení systému)", - "core.settings.colorscheme-dark": "Tmavé", - "core.settings.colorscheme-light": "Světlé", - "core.settings.compilationinfo": "Informace o kompilaci", - "core.settings.copyinfo": "Zkopírujte informace o zařízení do schránky", - "core.settings.cordovadevicemodel": "Cordova Device model", - "core.settings.cordovadeviceosversion": "Cordova Device OS verze", - "core.settings.cordovadeviceplatform": "Platforma Cordova Device", - "core.settings.cordovadeviceuuid": "Cordova Device uuid", - "core.settings.cordovaversion": "Cordova verze", - "core.settings.currentlanguage": "Současný jazyk", - "core.settings.debugdisplay": "Zobrazení ladících informací", - "core.settings.debugdisplaydescription": "Je-li tato možnost povolena, budou chybové výpisy zobrazovat více informací o chybě, pokud je to možné.", - "core.settings.deletesitefiles": "Opravdu chcete odstranit stažené soubory a data z mezipaměti z webu \"{{sitename}}\"? Nebudete moci aplikaci používat v offline režimu.", - "core.settings.deletesitefilestitle": "Odstranit soubory webu", - "core.settings.deviceinfo": "Informace o zařízení", - "core.settings.deviceos": "OS zařízení", - "core.settings.disableall": "Zakázat upozornění", - "core.settings.disabled": "Vypnuto", - "core.settings.displayformat": "Formát zobrazení", - "core.settings.enabledownloadsection": "Povolit stahování sekcí", - "core.settings.enablefirebaseanalytics": "Povolit analytiku Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Pokud je povoleno, aplikace bude shromažďovat využití anonymních dat.", - "core.settings.enablerichtexteditor": "Povolit textový editor", - "core.settings.enablerichtexteditordescription": "Pokud je povoleno, bude při zadávání obsahu k dispozici textový editor.", - "core.settings.enablesyncwifi": "Umožnit synchronizaci pouze na Wi-Fi", - "core.settings.entriesincache": "{{$a}} položek v mezipaměti", - "core.settings.errordeletesitefiles": "Chyba při odstraňování souborů stránek.", - "core.settings.errorsyncsite": "Chyba synchronizace dat stránek. Zkontrolujte své připojení k internetu a zkuste to znovu.", - "core.settings.estimatedfreespace": "Odhadované volné místo", - "core.settings.filesystemroot": "Kořen souborového systému", - "core.settings.fontsize": "Velikost textu", - "core.settings.forcedsetting": "Toto nastavení bylo vynuceno konfigurací vašeho webu.", - "core.settings.general": "Obecná nastavení", - "core.settings.language": "Jazyk", - "core.settings.license": "Licence", - "core.settings.localnotifavailable": "Je dostupné lokání oznámení", - "core.settings.locationhref": "Zobrazení web URL", - "core.settings.locked": "Zamknuto", - "core.settings.loggedin": "On-line", - "core.settings.loggedoff": "Off-line", - "core.settings.navigatorlanguage": "Jazyk navigátoru", - "core.settings.navigatoruseragent": "Navigátor userAgent", - "core.settings.networkstatus": "Stav připojení k Internetu", - "core.settings.opensourcelicenses": "Licence Open Source", - "core.settings.preferences": "Předvolby", - "core.settings.privacypolicy": "Zásady ochrany osobních údajů", - "core.settings.publisher": "Vydavatel", - "core.settings.pushid": "Odeslat ID oznámení", - "core.settings.reportinbackground": "Zobrazovat chyby automaticky", - "core.settings.screen": "Informace na obrazovce", - "core.settings.settings": "Nastavení", - "core.settings.showdownloadoptions": "Zobrazit možnosti stahování", - "core.settings.siteinfo": "Informace o webu", - "core.settings.sites": "Stránky", - "core.settings.spaceusage": "Použitý prostor", - "core.settings.spaceusagehelp": "Smazání uložených informací o webu odstraní všechna data offline. Tyto informace vám umožňují používat aplikaci v režimu offline.", - "core.settings.synchronization": "Synchronizace", - "core.settings.synchronizenow": "Právě synchonizuji", - "core.settings.synchronizenowhelp": "Synchronizace webu odešle čekající změny a veškerou offline aktivitu uloženou v zařízení a bude synchronizovat některá data, jako jsou zprávy a oznámení.", - "core.settings.syncsettings": "Nastavení synchronizace", - "core.settings.total": "Celkem", - "core.settings.wificonnection": "Připojení WiFi", - "core.sharedfiles.chooseaccountstorefile": "Vyberte účet pro uložení souboru.", - "core.sharedfiles.chooseactionrepeatedfile": "Soubor s tímto názvem již existuje. Chcete existující soubor nahradit nebo jej přejmenovat na \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Nejsou zde uloženy žádné stránky. Před sdílením souborů s aplikací přidejte prosím web.", - "core.sharedfiles.nosharedfiles": "Na tomto webu nejsou uložené žádné sdílené soubory.", - "core.sharedfiles.nosharedfilestoupload": "Nemáte žádné nahrané soubory. Pokud chcete nahrát soubor z jiné aplikace, vyhledejte tento soubor a klikněte na tlačítko \"Otevřít v\".", - "core.sharedfiles.rename": "Přejmenovat", - "core.sharedfiles.replace": "Nahradit", - "core.sharedfiles.sharedfiles": "Sdílené soubory", - "core.sharedfiles.successstorefile": "Soubor byl úspěšně uložen. Vyberte soubor, který chcete nahrát do vašich soukromých souborů nebo připojit ji do některých činností.", - "core.show": "Ukázat", - "core.showless": "Zobrazit méně ...", - "core.showmore": "Zobrazit více ...", - "core.site": "Stránky", - "core.sitehome.sitehome": "Titulní stránka", - "core.sitehome.sitenews": "Oznámení stránek", - "core.sitemaintenance": "Momentálně pracujeme na údržbě těchto stránek, proto nejsou dočasně k dispozici.", - "core.sizeb": "bytů", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Přeskočit", - "core.sorry": "Promiňte...", - "core.sort": "Setřídit", - "core.sortby": "Třídit podle", - "core.start": "Start", - "core.storingfiles": "Ukládání souborů", - "core.strftimedate": "%d. %B %Y", - "core.strftimedatefullshort": "%d.%m.%y", - "core.strftimedateshort": "%d. %B", - "core.strftimedatetime": "%d. %B %Y, %H.%M", - "core.strftimedatetimeshort": "%d.%m.%Y %H:%M", - "core.strftimedaydate": "%A, %d. %B %Y", - "core.strftimedaydatetime": "%A, %d. %B %Y, %H.%M", - "core.strftimedayshort": "%A, %d. %B", - "core.strftimedaytime": "%a, %H.%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d. %b., %H.%M", - "core.strftimerecentfull": "%A, %d. %B %Y, %H.%M", - "core.strftimetime": "%H.%M", - "core.strftimetime12": "%H.%M", - "core.strftimetime24": "%H.%M", - "core.submit": "Odeslat", - "core.success": "Úspěch", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Výchozí kolekce", - "core.tag.errorareanotsupported": "Aplikace nepodporuje pole štítků.", - "core.tag.inalltagcoll": "Všude", - "core.tag.itemstaggedwith": "{{$a.tagarea}} označeno štítkem \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Hledání výrazu \"{{$a}}\" nepřineslo žádné výsledky", - "core.tag.notagsfound": "Hledání výrazu ve štítku \"{{$a}}\" nepřineslo žádné výsledky", - "core.tag.searchtags": "Prohledat štítky", - "core.tag.showingfirsttags": "Zobrazit {{$a}} nejpopulárnějších štítků", - "core.tag.tag": "Štítek", - "core.tag.tagarea_course": "Kurzy", - "core.tag.tagarea_course_modules": "Činnosti a materiály", - "core.tag.tagarea_post": "Příspěvky blogu", - "core.tag.tagarea_user": "Zájmy uživatele", - "core.tag.tags": "Štítky", - "core.tag.warningareasnotsupported": "Některé oblasti štítků se nezobrazí, protože nejsou aplikací podporovány.", - "core.teachers": "Učitelé", - "core.thereisdatatosync": "K dispozici jsou v offline režimu {{$a}}, které mají být synchronizovány.", - "core.thisdirection": "ltr", - "core.time": "Čas", - "core.timesup": "Čas!", - "core.today": "Dnes", - "core.tryagain": "Zkuste znovu", - "core.twoparagraphs": "{{p1}}

      {{p2}}", - "core.uhoh": "Upozornění!", - "core.unexpectederror": "Neočekávaná chyba. Zavřete a znovu otevřete aplikaci a zkuste to znovu, prosím.", - "core.unicodenotsupported": "Některá emojis nejsou na tomto webu podporována. Takové znaky budou při odeslání zprávy odstraněny.", - "core.unicodenotsupportedcleanerror": "Při čištění Unicode znaků byl nalezen prázdný text.", - "core.unknown": "Neznámý", - "core.unlimited": "Neomezeno", - "core.unzipping": "Rozbalení", - "core.updaterequired": "Je vyžadována aktualizace aplikace", - "core.updaterequireddesc": "Aktualizujte prosím aplikaci na verzi {{$a}}", - "core.upgraderunning": "Tyto stránky se právě aktualizují. Prosím, zkuste to později.", - "core.user": "Uživatel", - "core.user.address": "Adresa", - "core.user.city": "Město/obec", - "core.user.contact": "Kontakt", - "core.user.country": "Země", - "core.user.description": "Popis", - "core.user.details": "Podrobnosti", - "core.user.detailsnotavailable": "Podrobnosti o tomto uživateli nejsou k dispozici.", - "core.user.editingteacher": "Učitel", - "core.user.email": "E-mailová adresa", - "core.user.emailagain": "E-mail (znovu)", - "core.user.errorloaduser": "Při načítání uživatele došlo k chybě.", - "core.user.firstname": "Křestní jméno", - "core.user.interests": "Zájmy", - "core.user.lastname": "Příjmení", - "core.user.manager": "Manažer", - "core.user.newpicture": "Nový obrázek", - "core.user.noparticipants": "Pro tento kurz nenalezen žádný účastník", - "core.user.participants": "Účastníci", - "core.user.phone1": "Telefon", - "core.user.phone2": "Mobilní telefon", - "core.user.roles": "Role", - "core.user.sendemail": "Email", - "core.user.student": "Student", - "core.user.teacher": "Učitel bez práva upravovat", - "core.user.webpage": "Webová stránka", - "core.userdeleted": "Uživatelský účet byl odstraněn", - "core.userdetails": "Detaily uživatele", - "core.usernotfullysetup": "Uživatel není plně nastaven", - "core.users": "Uživatelé", - "core.view": "Zobrazit", - "core.viewcode": "Zobrazit kód", - "core.vieweditor": "Zobrazit editor", - "core.viewembeddedcontent": "Zobrazení vloženého obsahu", - "core.viewprofile": "Zobrazit profil", - "core.warningofflinedatadeleted": "Offline data z {{component}} \"{{name}}\" byla odstraněna. {{error}}", - "core.whatisyourage": "Jaký je váš věk?", - "core.wheredoyoulive": "V jaké zemi žijete?", - "core.whoissiteadmin": "„Správci stránek“ jsou lidé, kteří řídí Moodle ve vaší škole / univerzitě / společnosti nebo vzdělávací organizaci. Pokud nevíte, jak je kontaktovat, kontaktujte prosím své učitele / školitele.", - "core.whoops": "Upozornění!", - "core.whyisthishappening": "Proč se to děje?", - "core.whyisthisrequired": "Proč je to nutné?", - "core.wsfunctionnotavailable": "Funkce webových služeb není k dispozici.", - "core.year": "rok", - "core.years": "roky", - "core.yes": "Ano", - "core.youreoffline": "Jste offline", - "core.youreonline": "Jste zpět online" -} \ No newline at end of file diff --git a/src/assets/lang/da.json b/src/assets/lang/da.json deleted file mode 100644 index 31bbed1bb..000000000 --- a/src/assets/lang/da.json +++ /dev/null @@ -1,1995 +0,0 @@ -{ - "addon.badges.badgedetails": "Badgedetaljer", - "addon.badges.badges": "Badges", - "addon.badges.contact": "Kontakt", - "addon.badges.dateawarded": "Udstedelsesdato", - "addon.badges.expired": "Udløbet", - "addon.badges.expirydate": "Udløbsdato", - "addon.badges.issuancedetails": "Badge-udløb", - "addon.badges.issuerdetails": "Udstederdata", - "addon.badges.issueremail": "Mailadresse", - "addon.badges.issuername": "Udsteders navn", - "addon.badges.issuerurl": "Udsteders URL", - "addon.badges.language": "Sprog", - "addon.badges.noalignment": "Denne badge har ingen eksterne færdigheder eller standarder specificeret.", - "addon.badges.nobadges": "Der er ingen tilgængelige badges.", - "addon.badges.norelated": "Denne badge har ingen relaterede badges.", - "addon.badges.recipientdetails": "Modtagerdata", - "addon.badges.relatedbages": "Relaterede badges", - "addon.badges.warnexpired": "(Badgen er udgået!)", - "addon.block_activitymodules.pluginname": "Aktiviteter", - "addon.block_activityresults.pluginname": "Aktivitetsresultater", - "addon.block_badges.pluginname": "Seneste badges", - "addon.block_blogmenu.pluginname": "Blogmenu", - "addon.block_blogrecent.pluginname": "Seneste blogindlæg", - "addon.block_blogtags.pluginname": "Blog-tags", - "addon.block_calendarmonth.pluginname": "Kalender", - "addon.block_calendarupcoming.pluginname": "Kommende begivenheder", - "addon.block_comments.pluginname": "Kommentarer", - "addon.block_completionstatus.pluginname": "Status på progression på kurset", - "addon.block_glossaryrandom.pluginname": "Tilfældigt ordbogsopslag", - "addon.block_learningplans.pluginname": "Læringsplaner", - "addon.block_myoverview.all": "Alle (bortset fra fjernede fra visning)", - "addon.block_myoverview.allincludinghidden": "Alle", - "addon.block_myoverview.favourites": "Fremhævede", - "addon.block_myoverview.future": "Fremtidige", - "addon.block_myoverview.hiddencourses": "Fjernet fra visning", - "addon.block_myoverview.inprogress": "I gang", - "addon.block_myoverview.lastaccessed": "Sidst besøgt", - "addon.block_myoverview.morecourses": "Flere kurser", - "addon.block_myoverview.nocourses": "Ingen kurser", - "addon.block_myoverview.past": "Tidligere", - "addon.block_myoverview.pluginname": "Kursusoversigt", - "addon.block_myoverview.shortname": "Kort navn", - "addon.block_myoverview.title": "Navn", - "addon.block_newsitems.pluginname": "Seneste meddelelser", - "addon.block_onlineusers.pluginname": "Brugere online", - "addon.block_privatefiles.pluginname": "Private filer", - "addon.block_recentactivity.pluginname": "Seneste aktiviteter", - "addon.block_recentlyaccessedcourses.nocourses": "Ingen senest besøgte kurser", - "addon.block_recentlyaccessedcourses.pluginname": "Senest besøgte kurser", - "addon.block_recentlyaccesseditems.noitems": "Ingen senest besøgte elementer", - "addon.block_recentlyaccesseditems.pluginname": "Senest besøgte elementer", - "addon.block_rssclient.pluginname": "Ekstern RSS-feed", - "addon.block_selfcompletion.pluginname": "Selvregistrering af gennemførelse", - "addon.block_sitemainmenu.pluginname": "Hovedmenu", - "addon.block_starredcourses.nocourses": "Ingen stjernemarkerede kurser", - "addon.block_starredcourses.pluginname": "Stjernemarkerede kurser", - "addon.block_tags.pluginname": "Tags", - "addon.block_timeline.duedate": "Forfaldsdato", - "addon.block_timeline.next30days": "Næste 30 dage", - "addon.block_timeline.next3months": "Næste 3 måneder", - "addon.block_timeline.next6months": "Næste 6 måneder", - "addon.block_timeline.next7days": "Næste 7 dage", - "addon.block_timeline.nocoursesinprogress": "Ikke igangværende kurser", - "addon.block_timeline.noevents": "Ingen forestående aktiviteter", - "addon.block_timeline.overdue": "Forfaldne", - "addon.block_timeline.pluginname": "Tidslinje", - "addon.block_timeline.sortbycourses": "Sorter efter kurser", - "addon.block_timeline.sortbydates": "Sorter efter dato", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Blogindlæg", - "addon.blog.errorloadentries": "Fejl under indlæsning af blogindlæg", - "addon.blog.linktooriginalentry": "Link til det oprindelige indlæg", - "addon.blog.noentriesyet": "Der er ingen synlige indlæg her", - "addon.blog.publishtonoone": "Dig selv (kladde)", - "addon.blog.publishtosite": "Alle her på webstedet", - "addon.blog.publishtoworld": "Alle på Internettet", - "addon.blog.showonlyyourentries": "Vis kun dine indlæg", - "addon.blog.siteblogheading": "Site-blog", - "addon.calendar.allday": "Hele dagen", - "addon.calendar.calendar": "Kalender", - "addon.calendar.calendarevents": "Kalenderbegivenheder", - "addon.calendar.calendarreminders": "Kalenderpåmindelser", - "addon.calendar.categoryevents": "Kategoribegivenheder", - "addon.calendar.confirmeventdelete": "Er du sikker på at du vil slette begivenheden \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "Begivenheden \"{{$a.name}}\" er en af flere. Vil du bare slette denne eller alle {{$a.count}} begivenheder?", - "addon.calendar.courseevents": "Kursusbegivenheder", - "addon.calendar.daynext": "Næste dag", - "addon.calendar.dayprev": "Forrige dag", - "addon.calendar.defaultnotificationtime": "Standard notifikationstidspunkt", - "addon.calendar.deleteallevents": "Slet alle begivenheder", - "addon.calendar.deleteevent": "Slet begivenhed", - "addon.calendar.deleteoneevent": "Slet denne begivenhed", - "addon.calendar.durationminutes": "Varighed i minutter", - "addon.calendar.durationnone": "Uden varighed", - "addon.calendar.durationuntil": "Indtil", - "addon.calendar.editevent": "Rediger begivenhed", - "addon.calendar.errorloadevent": "Fejl ved indlæsning af begivenhed.", - "addon.calendar.errorloadevents": "Fejl ved indlæsning af begivenheder.", - "addon.calendar.eventcalendareventdeleted": "Begivenhed slettet", - "addon.calendar.eventduration": "Varighed", - "addon.calendar.eventendtime": "Afslutningstid", - "addon.calendar.eventkind": "Begivenhedstype", - "addon.calendar.eventname": "Titel på begivenhed", - "addon.calendar.eventstarttime": "Starttid", - "addon.calendar.eventtype": "Begivenhedstype", - "addon.calendar.fri": "Fre", - "addon.calendar.friday": "Fredag", - "addon.calendar.gotoactivity": "Gå til aktiviteten", - "addon.calendar.groupevents": "Gruppebegivenheder", - "addon.calendar.invalidtimedurationminutes": "Varigheden af det indtastede antal minutter er ugyldig, vær venlig at indtaste en varighed større end 0 minutter eller vælg ingen varighed.", - "addon.calendar.invalidtimedurationuntil": "Det tidspunkt du har valgt for varighed, ligger før starttidspunktet. Ret dette inden du fortsætter.", - "addon.calendar.mon": "Man", - "addon.calendar.monday": "Mandag", - "addon.calendar.monthlyview": "Månedsvisning", - "addon.calendar.newevent": "Ny begivenhed", - "addon.calendar.noevents": "Der er ingen begivenheder", - "addon.calendar.nopermissiontoupdatecalendar": "Beklager, men du har ikke tilladelse til opdatere en begivenhed i kalenderen.", - "addon.calendar.reminders": "Påmindelser", - "addon.calendar.repeatedevents": "Gentagne begivenheder", - "addon.calendar.repeateditall": "Tilføj også ændringer til andre {{$a}} begivenheder i denne tilbagevendende serie", - "addon.calendar.repeateditthis": "Tilføj kun ændringer til denne begivenhed", - "addon.calendar.repeatevent": "Gentag denne begivenhed", - "addon.calendar.repeatweeksl": "Gentag ugentligt", - "addon.calendar.sat": "Lør", - "addon.calendar.saturday": "Lørdag", - "addon.calendar.setnewreminder": "Opret en ny påmindelse", - "addon.calendar.siteevents": "Site-begivenheder", - "addon.calendar.sun": "Søn", - "addon.calendar.sunday": "Søndag", - "addon.calendar.thu": "Tors", - "addon.calendar.thursday": "Torsdag", - "addon.calendar.today": "I dag", - "addon.calendar.tomorrow": "I morgen", - "addon.calendar.tue": "Tirs", - "addon.calendar.tuesday": "Tirsdag", - "addon.calendar.typecategory": "Kategoribegivenhed", - "addon.calendar.typeclose": "Luk begivenhed", - "addon.calendar.typecourse": "Kursus", - "addon.calendar.typedue": "Forestående begivenhed", - "addon.calendar.typegradingdue": "Bedømmer forestående begivenhed", - "addon.calendar.typegroup": "Gruppe", - "addon.calendar.typeopen": "Åbn begivenhed", - "addon.calendar.typesite": "Fælles", - "addon.calendar.typeuser": "Personlig", - "addon.calendar.upcomingevents": "Kommende begivenheder", - "addon.calendar.userevents": "Personlig kalender", - "addon.calendar.wed": "Ons", - "addon.calendar.wednesday": "Onsdag", - "addon.calendar.when": "Når", - "addon.calendar.yesterday": "I går", - "addon.competency.activities": "Aktiviteter", - "addon.competency.competencies": "Kompetencer", - "addon.competency.competenciesmostoftennotproficientincourse": "Kompetencer der ofte \"halter\" på dette kursus", - "addon.competency.coursecompetencies": "Kursuskompetencer", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Kompetencebedømmelser på dette kursus påvirker ikke læringsplaner.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Kompetencebedømmelser på dette kursus opdateres straks i læringsplaner.", - "addon.competency.crossreferencedcompetencies": "Kryds-refererede kompetencer", - "addon.competency.duedate": "Udløbsdato", - "addon.competency.errornocompetenciesfound": "Ingen kompetencer fundet", - "addon.competency.evidence": "Vidnesbyrd", - "addon.competency.evidence_competencyrule": "Kompetencereglen blev opfyldt.", - "addon.competency.evidence_coursecompleted": "Kurset \"{{$a}}\" var fuldført.", - "addon.competency.evidence_coursemodulecompleted": "Aktiviteten \"{{$a}}\" var fuldført.", - "addon.competency.evidence_courserestored": "Vurderingen blev gendannet sammen med kurset \"{{$a}}\".", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Vidnesbyrd om forudgående læring \"{{$a}}\" var linket.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Link til vidnesbyrd om forudgående læring \"{{$a}}\" var fjernet.", - "addon.competency.evidence_manualoverride": "Kompetencevurderingen blev foretaget manuelt.", - "addon.competency.evidence_manualoverrideincourse": "Kompetencevurderingen blev foretaget manuelt på kurset \"{{$a}}\".", - "addon.competency.evidence_manualoverrideinplan": "Kompetencevurderingen blev foretaget manuelt i læringsplanen \"{{$a}}\".", - "addon.competency.learningplancompetencies": "Læringsplankompetencer", - "addon.competency.learningplans": "Læringsplaner", - "addon.competency.myplans": "Mine læringsplaner", - "addon.competency.noactivities": "Ingen aktiviteter", - "addon.competency.nocompetencies": "Ingen kompetencer", - "addon.competency.nocompetenciesincourse": "Der er ingen kompetencer tilknyttet dette kursus", - "addon.competency.nocrossreferencedcompetencies": "Ingen andre kompetencer er krydsrefereret til denne kompetence.", - "addon.competency.noevidence": "Ingen vidnesbyrd", - "addon.competency.noplanswerecreated": "Der blev ikke oprettet nogen læringsplaner.", - "addon.competency.nouserplanswithcompetency": "Der er ingen læringsplaner med denne kompetence.", - "addon.competency.path": "Sti:", - "addon.competency.planstatusactive": "Aktiv", - "addon.competency.planstatuscomplete": "Fuldført", - "addon.competency.planstatusdraft": "Kladde", - "addon.competency.planstatusinreview": "I gennemsyn", - "addon.competency.planstatuswaitingforreview": "Venter på gennemsyn", - "addon.competency.proficient": "Færdighedsniveau", - "addon.competency.progress": "Progression", - "addon.competency.rating": "Bedømmelse", - "addon.competency.reviewstatus": "Status på gennemsyn", - "addon.competency.status": "Status", - "addon.competency.template": "Læringsplanskabelon", - "addon.competency.uponcoursecompletion": "Ved kursusfuldførelse:", - "addon.competency.usercompetencystatus_idle": "Tom", - "addon.competency.usercompetencystatus_inreview": "I gennemsyn", - "addon.competency.usercompetencystatus_waitingforreview": "Afventer gennemsyn", - "addon.competency.userplans": "Læringsplaner", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} ud af {{$a.y}} kompetencer er på færdighedsniveau", - "addon.competency.xcompetenciesproficientoutofyincourse": "Du har opnået færdighedsniveau {{$a.x}} ud af {{$a.y}} kompetencer på dette kursus.", - "addon.coursecompletion.complete": "Fuldfør", - "addon.coursecompletion.completecourse": "Kursus gennemført", - "addon.coursecompletion.completed": "Gennemført", - "addon.coursecompletion.completiondate": "Afslutningsdato", - "addon.coursecompletion.completionmenuitem": "Gennemførelse", - "addon.coursecompletion.couldnotloadreport": "Kunne ikke indlæse rapporten vedrørende kursusfuldførelse, prøv igen senere.", - "addon.coursecompletion.coursecompletion": "Kursusgennemførelse", - "addon.coursecompletion.criteria": "Kriterie", - "addon.coursecompletion.criteriagroup": "Kriteriegruppe", - "addon.coursecompletion.criteriarequiredall": "Alle kriterier herunder er påkrævet", - "addon.coursecompletion.criteriarequiredany": "Et af kriterierne herunder er påkrævet", - "addon.coursecompletion.inprogress": "Igangværende", - "addon.coursecompletion.manualselfcompletion": "Manuel selvregistrering af gennemførelse", - "addon.coursecompletion.nottracked": "For øjeblikket registreres din progression ikke på dette kursus", - "addon.coursecompletion.notyetstarted": "Ikke begyndt endnu", - "addon.coursecompletion.pending": "Behandles", - "addon.coursecompletion.required": "Påkrævet", - "addon.coursecompletion.requiredcriteria": "Påkrævede kriterier", - "addon.coursecompletion.requirement": "Krav", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Vis kursusrapport", - "addon.files.couldnotloadfiles": "Fillisten kunne ikke hentes", - "addon.files.emptyfilelist": "Der er ingen filer at vise.", - "addon.files.erroruploadnotworking": "Desværre er det p.t. ikke muligt at uploade filer til dit site.", - "addon.files.files": "Filer", - "addon.files.privatefiles": "Private filer", - "addon.files.sitefiles": "Site filer", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Konfigurer enheder", - "addon.messages.acceptandaddcontact": "Accepter og tilføj kontakt", - "addon.messages.addcontact": "Tilføj kontakt", - "addon.messages.addcontactconfirm": "Bekræft at du vil føje {{$a}} til dine kontakter", - "addon.messages.addtofavourites": "Stjernemarker", - "addon.messages.addtoyourcontacts": "Føj til kontakter", - "addon.messages.blocknoncontacts": "Bloker alle beskeder fra personer der ikke er i min kontaktliste.", - "addon.messages.blockuser": "Bloker bruger", - "addon.messages.blockuserconfirm": "Bekræft at du vil blokere {{$a}}", - "addon.messages.contactableprivacy": "Accepter besked fra:", - "addon.messages.contactableprivacy_coursemember": "Mine kontakter og alle på mine kurser", - "addon.messages.contactableprivacy_onlycontacts": "Kun mine kontakter", - "addon.messages.contactableprivacy_site": "Alle på sitet", - "addon.messages.contactblocked": "Kontakt blokeret", - "addon.messages.contactlistempty": "Kontaktlisten er tom", - "addon.messages.contactname": "Navn", - "addon.messages.contactrequestsent": "Anmodning om kontakt sendt", - "addon.messages.contacts": "Kontakter", - "addon.messages.decline": "Afvis", - "addon.messages.deleteallconfirm": "Er du sikker på at du vil slette hele denne samtale? Det vil ikke slette den for andre deltagere i konversationen.", - "addon.messages.deleteallselfconfirm": "Er du sikker på at du vil slette hele denne personlige samtale?", - "addon.messages.deleteconversation": "Slet samtale", - "addon.messages.deleteforeveryone": "Slet for mig og for alle andre", - "addon.messages.deletemessage": "Slet besked", - "addon.messages.deletemessageconfirmation": "Er du sikker på at du vil slette denne besked? Den vil kun forsvinde fra din historik, din samtalepartner vil stadig kunne se den.", - "addon.messages.errordeletemessage": "Fejl under sletning af filen", - "addon.messages.errorwhileretrievingcontacts": "Fejl ved hentning af kontakter fra serveren", - "addon.messages.errorwhileretrievingdiscussions": "Fejl ved hentning af diskussioner fra serveren", - "addon.messages.errorwhileretrievingmessages": "Fejl ved hentning af beskeder fra serveren.", - "addon.messages.errorwhileretrievingusers": "Fejl under hentning af brugere fra serveren.", - "addon.messages.groupconversations": "Gruppe", - "addon.messages.groupinfo": "Gruppe-info", - "addon.messages.individualconversations": "Privat", - "addon.messages.info": "Bruger-info", - "addon.messages.isnotinyourcontacts": "{{$a}} står ikke på din kontaktliste", - "addon.messages.message": "Besked", - "addon.messages.messagenotsent": "Beskeden blev ikke sendt, prøv igen senere.", - "addon.messages.messagepreferences": "Indstillinger for beskeder", - "addon.messages.messages": "Beskeder", - "addon.messages.muteconversation": "Nedton", - "addon.messages.mutedconversation": "Nedtonede samtaler", - "addon.messages.newmessage": "Ny besked", - "addon.messages.newmessages": "Nye beskeder", - "addon.messages.nocontactrequests": "Ingen kontaktanmodninger", - "addon.messages.nocontactsgetstarted": "Ingen kontakter", - "addon.messages.nofavourites": "Ingen stjernemarkerede samtaler", - "addon.messages.nogroupconversations": "Ingen gruppesamtaler", - "addon.messages.noindividualconversations": "Ingen private samtaler", - "addon.messages.nomessagesfound": "Ingen beskeder fundet", - "addon.messages.noncontacts": "Ikke-kontakter", - "addon.messages.nousersfound": "Ingen brugere fundet", - "addon.messages.numparticipants": "{{$a}} deltagere", - "addon.messages.removecontact": "Fjern kontakt", - "addon.messages.removecontactconfirm": "Bekræft at du vil fjerne {{$a}} fra kontaktlisten.", - "addon.messages.removefromfavourites": "Fjern stjerne", - "addon.messages.removefromyourcontacts": "Fjern fra kontakter", - "addon.messages.requests": "Anmodninger", - "addon.messages.requirecontacttomessage": "Du skal anmode {{$a}} om at føje dig til sin kontaktliste for at kunne skrive til hende eller til ham.", - "addon.messages.searchcombined": "Søg efter personer og beskeder", - "addon.messages.selfconversation": "Personligt område", - "addon.messages.selfconversationdefaultmessage": "Gem kladder til beskeder, links, noter mv. til senere brug.", - "addon.messages.sendcontactrequest": "Send kontaktanmodning", - "addon.messages.showdeletemessages": "Vis slet beskeder", - "addon.messages.type_blocked": "Blokeret", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Søgeresultat", - "addon.messages.type_strangers": "Andre", - "addon.messages.unabletomessage": "Du kan ikke skrive til denne bruger", - "addon.messages.unblockuser": "Fjern blokering af bruger", - "addon.messages.unblockuserconfirm": "Bekræft at du vil fjerne blokeringen af {{$a}}", - "addon.messages.unmuteconversation": "Ophæv nedtonning", - "addon.messages.useentertosend": "Send ved at trykke på Enter", - "addon.messages.useentertosenddescdesktop": "Er indstillingen deaktiveret, kan du bruge Ctrl+Enter til at sende beskeden.", - "addon.messages.useentertosenddescmac": "Er indstillingen deaktivereret, kan du bruge Cmd+Enter til at sende beskeden.", - "addon.messages.userwouldliketocontactyou": "{{$a}} vil gerne kontakte dig", - "addon.messages.warningconversationmessagenotsent": "Kunne ikke sende besked(er) til samtalen {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Kunne ikke sende besked(er) til brugeren {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Vil gerne kontakte dig", - "addon.messages.you": "Dig:", - "addon.messages.youhaveblockeduser": "Du har blokeret denne bruger", - "addon.messages.yourcontactrequestpending": "Din kontaktanmodning afventer med {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Du skal acceptere afleveringserklæringen.", - "addon.mod_assign.addattempt": "Tillad endnu et forsøg på besvarelse", - "addon.mod_assign.addnewattempt": "Tillad et nyt forsøg på besvarelse", - "addon.mod_assign.addnewattemptfromprevious": "Tilføj et nyt forsøg baseret på tidligere aflevering", - "addon.mod_assign.addsubmission": "Besvar opgaven", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Opgaveinformationer og afleveringsblanket er tilgængelig fra {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Aflevering tilladt fra", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Denne opgave accepterer afleveringer fra {{$a}}", - "addon.mod_assign.applytoteam": "Anvend karakterer og feedback til hele gruppen", - "addon.mod_assign.assignmentisdue": "Afleveringsfristen er overskredet", - "addon.mod_assign.attemptnumber": "Antal forsøg på besvarelse", - "addon.mod_assign.attemptreopenmethod": "Genåbning af opgaver", - "addon.mod_assign.attemptreopenmethod_manual": "Manuelt", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatisk indtil bestået", - "addon.mod_assign.attemptsettings": "Indstillinger for besvarelsesforsøg", - "addon.mod_assign.cannoteditduetostatementsubmission": "Du kan ikke tilføje eller redigere en opgaveaflevering i appen, da vi kunne ikke modtage afleveringserklæringen fra websiden.", - "addon.mod_assign.cannotgradefromapp": "Nogle karaktergivningsmetoder understøttes endnu ikke i appen og kan ikke ændres.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Du kan ikke aflevere til bedømmelse i appen da vi ikke kunne modtage afleveringserklæringen fra websiden.", - "addon.mod_assign.confirmsubmission": "Er du sikker på at du vil aflevere opgaven til bedømmelse nu? Bagefter vil du ikke kunne ændre den", - "addon.mod_assign.currentattempt": "Dette er besvarelsesforsøg {{$a}}.", - "addon.mod_assign.currentattemptof": "Dette er besvarelsesforsøg {{$a.attemptnumber}} ({{$a.maxattempts}} tilladte).", - "addon.mod_assign.currentgrade": "Aktuelle karakter i karakterbogen", - "addon.mod_assign.cutoffdate": "Skæringsdato", - "addon.mod_assign.defaultteam": "Standardgruppe", - "addon.mod_assign.duedate": "Afleveringsdato", - "addon.mod_assign.duedateno": "Ingen afleveringsfrist", - "addon.mod_assign.duedatereached": "Afleveringsfristen er overskredet", - "addon.mod_assign.editingstatus": "Redigeringsstatus", - "addon.mod_assign.editsubmission": "Rediger opgavebesvarelse", - "addon.mod_assign.erroreditpluginsnotsupported": "Du kan ikke tilføje eller redigere en opgaveaflevering i appen, nogle plugins understøtter ikke redigering:", - "addon.mod_assign.errorshowinginformation": "Vi kan ikke vise information om aflevering", - "addon.mod_assign.extensionduedate": "Afleveringsfrist forlænget", - "addon.mod_assign.feedbacknotsupported": "Denne Feedback understøttes ikke af appen og indeholder nok ikke alle informationer", - "addon.mod_assign.grade": "Karakter", - "addon.mod_assign.graded": "Bedømt", - "addon.mod_assign.gradedby": "Bedømt af", - "addon.mod_assign.gradedfollowupsubmit": "Bedømt - opfølgende aflevering modtaget", - "addon.mod_assign.gradedon": "Bedømt", - "addon.mod_assign.gradelocked": "Denne bedømmelse er låst eller overskrevet i karakterbogen.", - "addon.mod_assign.gradenotsynced": "Karakterer ikke synkroniseret", - "addon.mod_assign.gradeoutof": "Bedømmelse af {{$a}}", - "addon.mod_assign.gradingstatus": "Vurderingsstatus", - "addon.mod_assign.groupsubmissionsettings": "Indstillinger for gruppeaflevering", - "addon.mod_assign.hiddenuser": "Deltager", - "addon.mod_assign.latesubmissions": "Forsinkede opgaveafleveringer", - "addon.mod_assign.latesubmissionsaccepted": "Tilladt indtil {{$a}}", - "addon.mod_assign.markingworkflowstate": "Status på bedømmelsesfase", - "addon.mod_assign.markingworkflowstateinmarking": "Bedømmelse i gang", - "addon.mod_assign.markingworkflowstateinreview": "Gennemgås", - "addon.mod_assign.markingworkflowstatenotmarked": "Ikke bedømt", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Klar til udgivelse", - "addon.mod_assign.markingworkflowstatereadyforreview": "Bedømmelse fuldført", - "addon.mod_assign.markingworkflowstatereleased": "Udgivet", - "addon.mod_assign.modulenameplural": "Opgaver", - "addon.mod_assign.multipleteams": "Medlem af mere end en gruppe", - "addon.mod_assign.multipleteams_desc": "Opgaven kræver gruppeaflevering. Du er medlem af flere grupper. For at kunne aflevere må du kun være med i en gruppe. Kontakt din lærer og få ændret dit gruppemedlemskab.", - "addon.mod_assign.noattempt": "Intet besvarelsesforsøg", - "addon.mod_assign.nomoresubmissionsaccepted": "Kun tilladt for deltagere, der har fået udsættelse", - "addon.mod_assign.noonlinesubmissions": "Denne opgave kræver ikke at du afleverer noget online", - "addon.mod_assign.nosubmission": "Der er endnu ikke afleveret noget til denne opgave", - "addon.mod_assign.notallparticipantsareshown": "Deltagere uden afleveringer vises ikke", - "addon.mod_assign.noteam": "Ikke medlem af nogen grupper", - "addon.mod_assign.noteam_desc": "Opgaven kræver gruppeaflevering. Du er ikke med i en gruppe, så du kan ikke aflevere opgaven. Kontakt din lærer for at komme med i en gruppe.", - "addon.mod_assign.notgraded": "Ikke bedømt", - "addon.mod_assign.numberofdraftsubmissions": "Kladder", - "addon.mod_assign.numberofparticipants": "Deltagere", - "addon.mod_assign.numberofsubmissionsneedgrading": "Mangler bedømmelse", - "addon.mod_assign.numberofsubmittedassignments": "Afleveret", - "addon.mod_assign.numberofteams": "Grupper", - "addon.mod_assign.numwords": "{{$a}} ord", - "addon.mod_assign.outof": "{{$a.current}} ud af {{$a.total}}", - "addon.mod_assign.overdue": "Afleveringsdatoen er overskredet med {{$a}}", - "addon.mod_assign.submission": "Aflevering", - "addon.mod_assign.submissioneditable": "Den studerende kan redigere opgaven.", - "addon.mod_assign.submissionnoteditable": "Den studerende kan ikke redigere denne opgave", - "addon.mod_assign.submissionnotsupported": "Denne aflevering understøttes ikke af appen og indeholder nok ikke alle informationer", - "addon.mod_assign.submissionslocked": "Denne opgave accepterer ikke afleveringer", - "addon.mod_assign.submissionstatus": "Afleveringsstatus", - "addon.mod_assign.submissionstatus_": "Ingen opgavebesvarelse", - "addon.mod_assign.submissionstatus_draft": "Kladde (ikke afleveret)", - "addon.mod_assign.submissionstatus_marked": "Bedømt", - "addon.mod_assign.submissionstatus_new": "Ingen aflevering", - "addon.mod_assign.submissionstatus_reopened": "Genåbnet", - "addon.mod_assign.submissionstatus_submitted": "Afleveret til bedømmelse", - "addon.mod_assign.submissionstatusheading": "Afleveringsstatus", - "addon.mod_assign.submissionteam": "Gruppe", - "addon.mod_assign.submitassignment": "Aflever", - "addon.mod_assign.submitassignment_help": "Når opgaven er afleveret kan du ikke længere foretage ændringer i den.", - "addon.mod_assign.submittedearly": "Opgaven blev afleveret {{$a}} inden fristens udløb.", - "addon.mod_assign.submittedlate": "Opgaven blev afleveret {{$a}} for sent", - "addon.mod_assign.timemodified": "Seneste ændring", - "addon.mod_assign.timeremaining": "Resterende tid", - "addon.mod_assign.ungroupedusers": "Indstillingen 'Kræv gruppeaflevering' er slået til, og nogle brugere er forhindret i at aflevere opgaver da de ikke er med i en gruppe eller er medlem af mere end en gruppe.", - "addon.mod_assign.ungroupedusersoptional": "Indstillingen \"De studerende afleverer i grupper\" er aktiveret og nogle brugere er enten ikke medlem af en gruppe eller er med i mere end en gruppe. Vær opmærksom på at disse studerende vil aflevere som medlem af \"Standardgruppe\".", - "addon.mod_assign.unlimitedattempts": "Ubegrænset", - "addon.mod_assign.userswhoneedtosubmit": "Brugere, der skal aflevere: {{$a}}", - "addon.mod_assign.userwithid": "Bruger med Id {{id}}", - "addon.mod_assign.viewsubmission": "Vis aflevering", - "addon.mod_assign.warningsubmissiongrademodified": "Karakter for opgaven er ændret på websiden.", - "addon.mod_assign.warningsubmissionmodified": "Opgavebesvarelsen er ændret på websiden.", - "addon.mod_assign.wordlimit": "Maks. antal ord", - "addon.mod_assign_feedback_comments.pluginname": "Feedback-kommentarer", - "addon.mod_assign_feedback_editpdf.pluginname": "Anmærk pdf", - "addon.mod_assign_feedback_file.pluginname": "Filfeedback", - "addon.mod_assign_submission_comments.pluginname": "Kommentarer til afleverede opgaver", - "addon.mod_assign_submission_file.pluginname": "Filafleveringer", - "addon.mod_assign_submission_onlinetext.pluginname": "Online tekstafleveringer", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Maks. antal ord i denne opgave er {{$a.limit}} og du forsøger at aflevere {{$a.count}}. Tjek din aflevering og prøv igen.", - "addon.mod_book.errorchapter": "Fejl under læsning af kapitel.", - "addon.mod_book.modulenameplural": "Bøger", - "addon.mod_book.navnexttitle": "Næste: {{$a}}", - "addon.mod_book.navprevtitle": "Forrige: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Kapitler i bogen", - "addon.mod_book.toc": "Indhold", - "addon.mod_chat.beep": "Bip", - "addon.mod_chat.chatreport": "Chatsessioner", - "addon.mod_chat.currentusers": "Er i chatten nu:", - "addon.mod_chat.enterchat": "Tryk her for at gå ind i chatten nu.", - "addon.mod_chat.entermessage": "Skriv din besked", - "addon.mod_chat.errorwhileconnecting": "Fejl ved tilkobling til chatten.", - "addon.mod_chat.errorwhilegettingchatdata": "Fejl ved hentning af chatdata.", - "addon.mod_chat.errorwhilegettingchatusers": "Fejl ved hentning af chatbrugere.", - "addon.mod_chat.errorwhileretrievingmessages": "Fejl ved hentning af beskeder fra serveren.", - "addon.mod_chat.errorwhilesendingmessage": "Fejl ved afsendelse af besked.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} bipper alle!", - "addon.mod_chat.messagebeepsyou": "{{$a}} har lige bippet dig!", - "addon.mod_chat.messageenter": "{{$a}} er lige kommet til", - "addon.mod_chat.messageexit": "{{$a}} har forladt chatten", - "addon.mod_chat.messages": "Beskeder", - "addon.mod_chat.messageyoubeep": "Du bippede {{$a}}", - "addon.mod_chat.modulenameplural": "Chat", - "addon.mod_chat.mustbeonlinetosendmessages": "Du skal være online for at sende beskeder", - "addon.mod_chat.nomessages": "Ingen beskeder endnu", - "addon.mod_chat.nosessionsfound": "Ingen sessioner fundet", - "addon.mod_chat.saidto": "sagt til", - "addon.mod_chat.send": "Send", - "addon.mod_chat.sessionstart": "Næste chat starter {{$a.date}}, ({{$a.fromnow}} fra nu af)", - "addon.mod_chat.showincompletesessions": "Vis ufuldstændige sessioner", - "addon.mod_chat.talk": "Tal", - "addon.mod_chat.viewreport": "Vis forrige sessioner", - "addon.mod_choice.cannotsubmit": "Der var desværre problemer med at indsende dit valg. Prøv igen.", - "addon.mod_choice.choiceoptions": "Valgmuligheder", - "addon.mod_choice.errorgetchoice": "Fejl ved hentning af valgte data", - "addon.mod_choice.expired": "Denne aktivitet er lukket d. {{$a}}", - "addon.mod_choice.full": "(Fuld)", - "addon.mod_choice.modulenameplural": "Afstemninger", - "addon.mod_choice.noresultsviewable": "Resultaterne er ikke tilgængelige på nuværende tidspunkt.", - "addon.mod_choice.notopenyet": "Denne aktivitet er ikke tilgængelig før {{$a}}", - "addon.mod_choice.numberofuser": "Antal svar", - "addon.mod_choice.numberofuserinpercentage": "Antal svar i procent", - "addon.mod_choice.previewonly": "Dette er en forhåndsvisning af de tilgængelige valgmuligheder i denne afstemning. Du kan ikke indsende dine valg før {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anonyme resultater vil blive vist når du har svaret.", - "addon.mod_choice.publishinfoanonclose": "Anonyme resultater vil blive vist efter afstemningen er lukket.", - "addon.mod_choice.publishinfofullafter": "Fulde resultater med alles valg vises efter du har svaret.", - "addon.mod_choice.publishinfofullclose": "Fulde resultater med alles valg vises efter afstemningen er lukket.", - "addon.mod_choice.publishinfonever": "Resultaterne vil ikke blive vist efter du har svaret.", - "addon.mod_choice.removemychoice": "Slet mit valg", - "addon.mod_choice.responses": "Besvarelser", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% af brugerne vælger denne mulighed: {{test}}", - "addon.mod_choice.responsesresultgraphheader": "Grafisk visning", - "addon.mod_choice.resultsnotsynced": "Resultater medtager ikke dine seneste svar. Synkroniser for at opdatere dem.", - "addon.mod_choice.savemychoice": "Gem", - "addon.mod_choice.userchoosethisoption": "Brugerfordeling på svar", - "addon.mod_choice.yourselection": "Dine valg", - "addon.mod_data.addentries": "Tilføj poster", - "addon.mod_data.advancedsearch": "Avanceret søgning", - "addon.mod_data.alttext": "Alternativ tekst", - "addon.mod_data.approve": "Godkend", - "addon.mod_data.approved": "Godkendt", - "addon.mod_data.ascending": "Stigende", - "addon.mod_data.authorfirstname": "Forfatterens fornavn", - "addon.mod_data.authorlastname": "Forfatterens efternavn", - "addon.mod_data.confirmdeleterecord": "Er du sikker på at du vil slette denne post?", - "addon.mod_data.descending": "Faldende", - "addon.mod_data.disapprove": "Fortryd godkendelse", - "addon.mod_data.emptyaddform": "Du udfyldte ingen felter!", - "addon.mod_data.entrieslefttoadd": "Du mangler at tilføje {{$a.entriesleft}} post(er) til denne database.", - "addon.mod_data.entrieslefttoaddtoview": "Du skal tilføje {{$a.entrieslefttoview}} post(er mere) til denne database før du kan se andre deltageres bidrag.", - "addon.mod_data.errorapproving": "Fejl under godkendelse eller afvisning af opslag.", - "addon.mod_data.errordeleting": "Fejl under sletning af opslag.", - "addon.mod_data.errormustsupplyvalue": "Du skal indsætte en værdi her.", - "addon.mod_data.expired": "Beklager, denne aktivitet lukkede {{$a}} og er ikke længere tilgængelig", - "addon.mod_data.fields": "Felter", - "addon.mod_data.foundrecords": "Poster fundet: {{$a.num}}/{{$a.max}} (Nulstil filtre)", - "addon.mod_data.latlongboth": "Skriv både bredde- og længdegrad", - "addon.mod_data.menuchoose": "Vælg...", - "addon.mod_data.modulenameplural": "Databaser", - "addon.mod_data.more": "Flere", - "addon.mod_data.nomatch": "Ingen matchende poster fundet", - "addon.mod_data.norecords": "Ingen indlæg i databasen", - "addon.mod_data.notapproved": "Posten er ikke godkendt endnu.", - "addon.mod_data.notopenyet": "Beklager, denne aktivitet er først tilgængelig {{$a}}", - "addon.mod_data.numrecords": "{{$a}} poster", - "addon.mod_data.other": "Anden", - "addon.mod_data.recordapproved": "Post godkendt", - "addon.mod_data.recorddeleted": "Posten er slettet", - "addon.mod_data.recorddisapproved": "Posten er ikke godkendt", - "addon.mod_data.resetsettings": "Nulstil filtre", - "addon.mod_data.search": "Søg", - "addon.mod_data.selectedrequired": "Alle valgte er påkrævet", - "addon.mod_data.single": "Vis en enkelt post", - "addon.mod_data.tagarea_data_records": "Dataposter", - "addon.mod_data.timeadded": "Tilføjet", - "addon.mod_data.timemodified": "Ændret", - "addon.mod_data.usedate": "Inkluder i søgning.", - "addon.mod_feedback.analysis": "Analyse", - "addon.mod_feedback.anonymous": "Anonym", - "addon.mod_feedback.anonymous_entries": "Anonyme indlæg ({{$a}})", - "addon.mod_feedback.average": "Gennemsnit", - "addon.mod_feedback.captchaofflinewarning": "Feedback med captcha kan ikke bruges offline hvis den ikke er konfigureret eller hvis serveren nede.", - "addon.mod_feedback.complete_the_form": "Svar på spørgsmålene", - "addon.mod_feedback.completed_feedbacks": "Afleverede svar", - "addon.mod_feedback.continue_the_form": "Fortsæt med at besvare spørgsmålene", - "addon.mod_feedback.feedback_is_not_open": "Feedbacken er ikke åben", - "addon.mod_feedback.feedback_submitted_offline": "Denne feedback er gemt til aflevering senere.", - "addon.mod_feedback.feedbackclose": "Tillad svar til", - "addon.mod_feedback.feedbackopen": "Tillad svar fra", - "addon.mod_feedback.mapcourses": "Tilknyt feedback til kurser", - "addon.mod_feedback.maximal": "Maksimum", - "addon.mod_feedback.minimal": "Minimum", - "addon.mod_feedback.mode": "Tilstand", - "addon.mod_feedback.modulenameplural": "Feedback", - "addon.mod_feedback.next_page": "Næste side", - "addon.mod_feedback.non_anonymous": "Brugerens navn vil blive registreret og vist sammen med svarene", - "addon.mod_feedback.non_anonymous_entries": "Ikke-anonyme bidrag ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Studerende der ikke har besvaret ({{$a}})", - "addon.mod_feedback.not_selected": "Ikke valgt", - "addon.mod_feedback.not_started": "Ikke startet", - "addon.mod_feedback.numberoutofrange": "Tal udenfor interval", - "addon.mod_feedback.overview": "Overblik", - "addon.mod_feedback.page_after_submit": "Fuldførelsesbesked", - "addon.mod_feedback.preview": "Afprøv", - "addon.mod_feedback.previous_page": "Forrige side", - "addon.mod_feedback.questions": "Spørgsmål", - "addon.mod_feedback.response_nr": "Svar nummer", - "addon.mod_feedback.responses": "Svar", - "addon.mod_feedback.save_entries": "Aflever dine svar", - "addon.mod_feedback.show_entries": "Vis svar", - "addon.mod_feedback.show_nonrespondents": "Vis ikke-respondenter", - "addon.mod_feedback.started": "Startet", - "addon.mod_feedback.this_feedback_is_already_submitted": "Du har allerede gennemført denne aktivitet.", - "addon.mod_folder.emptyfilelist": "Der er ingen filer at vise.", - "addon.mod_folder.modulenameplural": "Mapper", - "addon.mod_forum.addanewdiscussion": "Tilføj en ny tråd", - "addon.mod_forum.addanewquestion": "Tilføj et nyt spørgsmål", - "addon.mod_forum.addanewtopic": "Tilføj nyt emne", - "addon.mod_forum.addtofavourites": "Giv denne tråd en stjerne", - "addon.mod_forum.advanced": "Avanceret", - "addon.mod_forum.cannotadddiscussion": "For at oprette en ny tråd i dette forum skal man være medlem af en gruppe.", - "addon.mod_forum.cannotadddiscussionall": "Du har ikke tilladelse til at oprette en ny tråd for alle deltagere.", - "addon.mod_forum.cannotcreatediscussion": "Kunne ikke oprette en ny tråd.", - "addon.mod_forum.couldnotadd": "Kunne ikke tilføje dit indlæg pga. en ukendt fejl", - "addon.mod_forum.couldnotupdate": "Kunne ikke opdatere dit indlæg pga. en ukendt fejl.", - "addon.mod_forum.cutoffdatereached": "Skæringsdagen for indlæg i dette forum er nået, så du kan ikke skrive i det.", - "addon.mod_forum.delete": "Slet", - "addon.mod_forum.deletedpost": "Indlægget er slettet", - "addon.mod_forum.deletesure": "Er du sikker på, at du vil slette dette indlæg?", - "addon.mod_forum.discussion": "Tråd", - "addon.mod_forum.discussionlistsortbycreatedasc": "Sorter efter oprettelsesdato, stigende", - "addon.mod_forum.discussionlistsortbycreateddesc": "Sorter efter oprettelsesdato, faldende", - "addon.mod_forum.discussionlistsortbylastpostasc": "Sorter efter seneste indlæg, stigende", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Sorter efter seneste indlæg, faldende", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Sorter efter antal svar, stigende", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Sorter efter antal svar, faldende", - "addon.mod_forum.discussionlocked": "Denne tråd er låst, så du kan ikke længere svare på den.", - "addon.mod_forum.discussionpinned": "Fastgjort", - "addon.mod_forum.discussionsubscription": "Abonnement på tråd", - "addon.mod_forum.edit": "Rediger", - "addon.mod_forum.erroremptymessage": "Indlægget kan ikke være tomt.", - "addon.mod_forum.erroremptysubject": "Indlæggets emne kan ikke være tomt.", - "addon.mod_forum.errorgetforum": "Fejl ved hentning af forumdata.", - "addon.mod_forum.errorgetgroups": "Fejl ved hentning af gruppeindstillinger.", - "addon.mod_forum.favouriteupdated": "Din stjerneindstilling er opdateret", - "addon.mod_forum.forumnodiscussionsyet": "Der er endnu ingen indlæg i dette forum.", - "addon.mod_forum.group": "Gruppe", - "addon.mod_forum.lastpost": "Seneste indlæg", - "addon.mod_forum.lockdiscussion": "Lås denne tråd for nye indlæg", - "addon.mod_forum.lockupdated": "Låsemuligheden er opdateret", - "addon.mod_forum.message": "Meddelelse", - "addon.mod_forum.modeflatnewestfirst": "Kronologisk visning med seneste indlæg først", - "addon.mod_forum.modeflatoldestfirst": "Kronologisk visning med første indlæg først", - "addon.mod_forum.modenested": "Trådet visning, udfoldet", - "addon.mod_forum.modulenameplural": "Fora", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} diskussioner", - "addon.mod_forum.numreplies": "{{numreplies}} svar", - "addon.mod_forum.pindiscussion": "Fastgør denne tråd", - "addon.mod_forum.pinupdated": "Fastgøringsindstillingen er opdateret", - "addon.mod_forum.postisprivatereply": "Dette er et privat svar. Andre deltagere kan ikke se det.", - "addon.mod_forum.posttoforum": "Send til forum", - "addon.mod_forum.posttomygroups": "Send til alle grupper", - "addon.mod_forum.privatereply": "Svar privat", - "addon.mod_forum.re": "Ang:", - "addon.mod_forum.refreshdiscussions": "Genindlæs diskussioner", - "addon.mod_forum.refreshposts": "Genindlæs indlæg", - "addon.mod_forum.removefromfavourites": "Fjern stjernen fra denne tråd", - "addon.mod_forum.reply": "Besvar", - "addon.mod_forum.replyplaceholder": "Skriv dit svar...", - "addon.mod_forum.subject": "Emne", - "addon.mod_forum.tagarea_forum_posts": "Forumindlæg", - "addon.mod_forum.thisforumhasduedate": "Indlæg til dette forum skal være sendt {{$a}}.", - "addon.mod_forum.thisforumisdue": "Indlæg til dette forum skulle være sendt {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Lås denne tråd op", - "addon.mod_forum.unpindiscussion": "Frigør denne tråd", - "addon.mod_forum.unread": "Ikke læst", - "addon.mod_forum.unreadpostsnumber": "{{$a}} nye indlæg", - "addon.mod_forum.yourreply": "Dit svar", - "addon.mod_glossary.addentry": "Tilføj nyt opslag", - "addon.mod_glossary.aliases": "Nøgleord", - "addon.mod_glossary.attachment": "Bilag", - "addon.mod_glossary.browsemode": "Skim indlæg", - "addon.mod_glossary.byalphabet": "Alfabetisk", - "addon.mod_glossary.byauthor": "Grupper efter forfatter", - "addon.mod_glossary.bycategory": "Gruppér efter kategori", - "addon.mod_glossary.bynewestfirst": "Nyeste først", - "addon.mod_glossary.byrecentlyupdated": "Senest opdateret", - "addon.mod_glossary.bysearch": "Søg", - "addon.mod_glossary.cannoteditentry": "Kan ikke redigere opslaget", - "addon.mod_glossary.casesensitive": "Dette opslag er versalfølsomt", - "addon.mod_glossary.categories": "Kategorier", - "addon.mod_glossary.concept": "Opslagsord", - "addon.mod_glossary.definition": "Definition", - "addon.mod_glossary.entriestobesynced": "Opslag der skal synkroniseres", - "addon.mod_glossary.entrypendingapproval": "Dette opslag afventer godkendelse", - "addon.mod_glossary.entryusedynalink": "Dette opslagsord skal autolinkes.", - "addon.mod_glossary.errconceptalreadyexists": "Der kan ikke være to ens opslagsord og dette eksisterer allerede.", - "addon.mod_glossary.errorloadingentries": "Der opstod en fejl under indlæsning af opslag", - "addon.mod_glossary.errorloadingentry": "Der opstod en fejl under indlæsning af opslaget", - "addon.mod_glossary.errorloadingglossary": "Der opstod en fejl under indlæsning af ordbogen.", - "addon.mod_glossary.fillfields": "Opslagsord og definition skal udfyldes!", - "addon.mod_glossary.fullmatch": "Autolink kun til hele ord.", - "addon.mod_glossary.linking": "Autolinkning", - "addon.mod_glossary.modulenameplural": "Opslagsværker", - "addon.mod_glossary.noentriesfound": "Ingen opslag fundet", - "addon.mod_glossary.searchquery": "Søgeord", - "addon.mod_glossary.tagarea_glossary_entries": "Opslag", - "addon.mod_h5pactivity.all_attempts": "Alle brugerforøg på besvarelser", - "addon.mod_h5pactivity.answer_checked": "Svar tjekket", - "addon.mod_h5pactivity.answer_correct": "Dit svar er", - "addon.mod_h5pactivity.answer_fail": "Forkert svar", - "addon.mod_h5pactivity.answer_incorrect": "Dit svar er ikke rigtigt", - "addon.mod_h5pactivity.answer_pass": "Rigtigt svar", - "addon.mod_h5pactivity.attempt": "Forsøg på besvarelse", - "addon.mod_h5pactivity.attempt_completion_no": "Dette besvarelsesforsøg er ikke markeret som gennemført", - "addon.mod_h5pactivity.attempt_completion_yes": "Dette besvarelsesforsøg er gennemført", - "addon.mod_h5pactivity.attempt_success_fail": "Fejl", - "addon.mod_h5pactivity.attempt_success_pass": "Bestået", - "addon.mod_h5pactivity.attempt_success_unknown": "Ikke rapporteret", - "addon.mod_h5pactivity.attempts_none": "Denne bruger har ingen besvarelsesforsøg at vise", - "addon.mod_h5pactivity.completion": "Gennemførelse", - "addon.mod_h5pactivity.duration": "Varighed", - "addon.mod_h5pactivity.maxscore": "Maksimum score", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Mine besvarelser", - "addon.mod_h5pactivity.no_compatible_track": "Denne interaktion ({{$a}}) indeholder ikke sporingsoplysninger, eller den angivne sporing er ikke kompatibel med den aktuelle aktivitetsversion.", - "addon.mod_h5pactivity.outcome": "Resultat", - "addon.mod_h5pactivity.previewmode": "Dette indhold vises i forhåndsvisningstilstand. Der gemmes ingen sporing af forsøg.", - "addon.mod_h5pactivity.result_fill-in": "Udfyld tekst", - "addon.mod_h5pactivity.result_other": "Ukendt interaktionstype", - "addon.mod_h5pactivity.review_my_attempts": "Vis mine besvarelser", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} ud af {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Startdato", - "addon.mod_h5pactivity.totalscore": "Samlet score", - "addon.mod_imscp.deploymenterror": "Fejl på indholdspakke!", - "addon.mod_imscp.modulenameplural": "IMS-pakker", - "addon.mod_imscp.showmoduledescription": "Vis beskrivelse", - "addon.mod_imscp.toc": "Indholdsfortegnelse", - "addon.mod_lesson.answer": "Svar", - "addon.mod_lesson.attempt": "Forsøg: {{$a}}", - "addon.mod_lesson.attemptheader": "Forsøg", - "addon.mod_lesson.attemptsremaining": "Du har {{$a}} forsøg endnu", - "addon.mod_lesson.averagescore": "Gennemsnitlig score", - "addon.mod_lesson.averagetime": "Gennemsnitstid", - "addon.mod_lesson.branchtable": "Indhold", - "addon.mod_lesson.cannotfindattempt": "Fejl: kunne ikke finde forsøget", - "addon.mod_lesson.cannotfinduser": "Fejl: kunne ikke finde brugere", - "addon.mod_lesson.clusterjump": "Usete spørgsmål i en klynge", - "addon.mod_lesson.completed": "Gennemført", - "addon.mod_lesson.congratulations": "Tillykke - Denne lektion er slut.", - "addon.mod_lesson.continue": "Fortsæt", - "addon.mod_lesson.continuetonextpage": "Fortsæt til næste side.", - "addon.mod_lesson.defaultessayresponse": "Din tekst vil blive vurderet af din lærer på kurset.", - "addon.mod_lesson.detailedstats": "Detaljeret statistik", - "addon.mod_lesson.didnotanswerquestion": "Besvarede ikke dette spørgsmål.", - "addon.mod_lesson.displayofgrade": "Vis bedømmelse (for studerende)", - "addon.mod_lesson.displayscorewithessays": "

      Du fik {{$a.score}} ud af {{$a.tempmaxgrade}} for de automatisk rettede spørgsmål.

      Dit/dine {{$a.essayquestions}} tekstsvar vil blive bedømt og lagt til scoren senere.

      Din nuværende karakter uden tekstsvarene er {{$a.score}} ud af {{$a.grade}}.

      ", - "addon.mod_lesson.displayscorewithoutessays": "Din score er {{$a.score}} (ud af {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Adgangskode kan ikke være tom", - "addon.mod_lesson.enterpassword": "Skriv adgangskoden", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Du besvarede ikke nogen af spørgsmålene. Du får 0 for denne lektion", - "addon.mod_lesson.errorprefetchrandombranch": "Denne lektion har et spring til en tilfældig indholdsside, den kan ikke bruges i appen før den er startet på hjemmesiden.", - "addon.mod_lesson.errorreviewretakenotlast": "Dette forsøg kan ikke længere gennemses da et nyt forsøg er fuldført.", - "addon.mod_lesson.finish": "Færdig", - "addon.mod_lesson.finishretakeoffline": "Dette forsøg blev afsluttet offline.", - "addon.mod_lesson.firstwrong": "Din besvarelse er ikke rigtig. Vil du prøve en gang mere? (Hvis du svarer rigtigt næste gang vil det ikke tælles med i din endelige karakter.)", - "addon.mod_lesson.gotoendoflesson": "Gå til slutningen af lektionen", - "addon.mod_lesson.grade": "Karakter", - "addon.mod_lesson.highscore": "Topscore", - "addon.mod_lesson.hightime": "High time", - "addon.mod_lesson.leftduringtimed": "Du har forladt en tidsbegrænset lektion
      Klik på fortsæt for genstarte lektionen.", - "addon.mod_lesson.leftduringtimednoretake": "Du har forladt en tidsbegrænset lektion
      Du kan ikke begynde forfra eller fortsætte lektionen.", - "addon.mod_lesson.lessonmenu": "Lektionsmenu", - "addon.mod_lesson.lessonstats": "Lektionsstatistik", - "addon.mod_lesson.linkedmedia": "Linket mediefil", - "addon.mod_lesson.loginfail": "Login fejlede, prøv igen..", - "addon.mod_lesson.lowscore": "Lav score", - "addon.mod_lesson.lowtime": "Kort tid", - "addon.mod_lesson.maximumnumberofattemptsreached": "Det maksimale antal forsøg er nået - du føres til næste side", - "addon.mod_lesson.modattemptsnoteacher": "Det er kun studerende der kan gennemse", - "addon.mod_lesson.modulenameplural": "Lektioner", - "addon.mod_lesson.noanswer": "Et eller flere spørgsmål er ikke besvaret. Gå tilbage og aflever en besvarelse.", - "addon.mod_lesson.nolessonattempts": "Ingen har prøvet denne lektion", - "addon.mod_lesson.nolessonattemptsgroup": "Ingen forsøg er udført af {{$a}} gruppemedlemmer i denne lektion", - "addon.mod_lesson.notcompleted": "Ikke færdiggjort", - "addon.mod_lesson.numberofcorrectanswers": "Antal rigtige svar: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Antal sider vist: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Antal besvarede spørgsmål: {{$a.nquestions}} (Du skal mindst besvare {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Du har indtil videre opnået {{$a.score}} ud af {{$a.currenthigh}} point", - "addon.mod_lesson.ongoingnormal": "Du har besvaret {{$a.correct}} spørgsmål rigtigt ud af {{$a.viewed}} viste.", - "addon.mod_lesson.or": "ELLER", - "addon.mod_lesson.overview": "Oversigt", - "addon.mod_lesson.preview": "Forhåndsvisning", - "addon.mod_lesson.progressbarteacherwarning2": "Du kan ikke se progressionslinjen da ud kan redigere lektionen", - "addon.mod_lesson.progresscompleted": "Du har gennemført {{$a}}% af lektionen", - "addon.mod_lesson.question": "Spørgsmål", - "addon.mod_lesson.rawgrade": "Rå point", - "addon.mod_lesson.reports": "Rapporter", - "addon.mod_lesson.response": "Respons", - "addon.mod_lesson.retakefinishedinsync": "Et offline forsøg er synkroniseret. Vil du gennemse det?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Prøv igen", - "addon.mod_lesson.reviewlesson": "Prøv en gang mere", - "addon.mod_lesson.reviewquestionback": "Ja, jeg vil gerne prøve igen", - "addon.mod_lesson.reviewquestioncontinue": "Nej, jeg vil bare videre til næste spørgsmål", - "addon.mod_lesson.secondpluswrong": "Ikke rigtigt. Vil du prøve en gang mere?", - "addon.mod_lesson.submit": "Aflever", - "addon.mod_lesson.teacherjumpwarning": "Et {{$a.cluster}}-spring eller et {{$a.unseen}}-spring bliver benyttet i denne lektion. Springet \"Næste side\" vil blive brugt i stedet. Log ind som studerende for at teste disse spring.", - "addon.mod_lesson.teacherongoingwarning": "Løbende score vises kun for den studerende. Log ind som studerende for at teste løbende score.", - "addon.mod_lesson.teachertimerwarning": "Timeren virker kun for studerende. Du kan teste det ved at logge ind som studerende.", - "addon.mod_lesson.thatsthecorrectanswer": "Rigtigt svar! :-)", - "addon.mod_lesson.thatsthewronganswer": "Desværre, svaret er forkert.", - "addon.mod_lesson.timeremaining": "Tid tilbage", - "addon.mod_lesson.timetaken": "Varighed", - "addon.mod_lesson.unseenpageinbranch": "Usete spørgsmål på en indholdsside", - "addon.mod_lesson.warningretakefinished": "Dette forsøg blev afsluttet på websiden.", - "addon.mod_lesson.welldone": "Flot klaret!", - "addon.mod_lesson.youhaveseen": "Du har allerede set mere end en side af denne lektion.
      Ønsker du at starte på den sidste side du så?", - "addon.mod_lesson.youranswer": "Dine svar", - "addon.mod_lesson.yourcurrentgradeisoutof": "Din totale score er {{$a.grade}} ud af {{$a.total}}", - "addon.mod_lesson.youshouldview": "Du skulle se mindst: {{$a}}", - "addon.mod_lti.errorgetlti": "Fejl ved hentning af moduldata.", - "addon.mod_lti.errorinvalidlaunchurl": "Din start-URL er ikke gyldig.", - "addon.mod_lti.launchactivity": "Start aktiviteten", - "addon.mod_lti.modulenameplural": "Eksterne værktøjer", - "addon.mod_page.errorwhileloadingthepage": "Fejl ved indlæsning af siden.", - "addon.mod_page.modulenameplural": "Sider", - "addon.mod_quiz.answercolon": "Svar:", - "addon.mod_quiz.attemptfirst": "Første besvarelse", - "addon.mod_quiz.attemptlast": "Sidste besvarelse", - "addon.mod_quiz.attemptnumber": "Besvarelse", - "addon.mod_quiz.attemptquiznow": "--== Begynd nu ==--", - "addon.mod_quiz.attemptstate": "Tilstand", - "addon.mod_quiz.cannotsubmitquizdueto": "Denne quizbesvarelse kan ikke afleveres af følgende årsager:", - "addon.mod_quiz.clearchoice": "Ryd mit valg", - "addon.mod_quiz.comment": "Kommentar", - "addon.mod_quiz.completedon": "Færdiggjort på", - "addon.mod_quiz.confirmclose": "Når du har afleveret, kan du ikke længere ændre dine svar i dette forsøg.", - "addon.mod_quiz.confirmcontinueoffline": "Denne besvarelse er ikke synkroniseret siden {{$a}}. Hvis du har arbejdet videre på besvarelsen på en anden enhed siden da, kan du miste data.", - "addon.mod_quiz.confirmleavequizonerror": "Der opstod en fejl under forsøg på at gemme svarene. Er du sikker på at du vil forlade quizzen?", - "addon.mod_quiz.confirmstart": "Quizzen har en tidsbegrænsning på {{$a}}. Tiden tælles ned fra det øjeblik du starter og kan ikke sættes på pause. Du skal aflevere inden tidens udløb. Er du sikker på at du vil starte nu?", - "addon.mod_quiz.confirmstartheader": "Tidsgrænse", - "addon.mod_quiz.connectionerror": "Forbindelse til netværket afbrudt. (Autosave slog fejl).\n\nNoter alle svar der er skrevet på denne side i løbet af de sidste par minutter, og prøv så at genoprette forbindelsen.\n\nNår forbindelsen er genoprettet, skulle dine svar være gemt, og denne besked forsvinder.", - "addon.mod_quiz.continueattemptquiz": "Fortsæt fra sidste forsøg", - "addon.mod_quiz.continuepreview": "Fortsæt sidste gennemsyn", - "addon.mod_quiz.errorbehaviournotsupported": "Quizzen kan ikke afvikles i appen, som ikke understøtter quizzens opførsel:", - "addon.mod_quiz.errordownloading": "Fejl under download af de krævede data.", - "addon.mod_quiz.errorgetattempt": "Kunne ikke hente forsøgsdata", - "addon.mod_quiz.errorgetquestions": "Kunne ikke hente spørgsmål.", - "addon.mod_quiz.errorgetquiz": "Kunne ikke hente quizdata.", - "addon.mod_quiz.errorparsequestions": "Der opstod en fejl ved læsning af spørgsmålene. Kør quizzen i en webbrowser i stedet.", - "addon.mod_quiz.errorquestionsnotsupported": "Denne quiz kan ikke tages i appen, quizzen indeholder kun spørgsmål som ikke supporteres af appen:", - "addon.mod_quiz.errorrulesnotsupported": "Denne quiz virker ikke i appen, quizzen indeholder regler som den ikke supporterer:", - "addon.mod_quiz.errorsaveattempt": "Der opstod en fejl da dine forsøgsdata skulle gemmes.", - "addon.mod_quiz.feedback": "Tilbagemelding", - "addon.mod_quiz.finishattemptdots": "Afslut quizzen...", - "addon.mod_quiz.finishnotsynced": "Afsluttet, men ikke synkroniseret", - "addon.mod_quiz.grade": "Karakter", - "addon.mod_quiz.gradeaverage": "Gennemsnitlig vurdering", - "addon.mod_quiz.gradehighest": "Højeste vurdering", - "addon.mod_quiz.grademethod": "Vurdering ved gentagne forsøg", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}}/{{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Point", - "addon.mod_quiz.modulenameplural": "Quizzer", - "addon.mod_quiz.mustbesubmittedby": "Svaret skal indsendes senest {{$a}}", - "addon.mod_quiz.noquestions": "Der er ikke tilføjet nogen spørgsmål endnu", - "addon.mod_quiz.noreviewattempt": "Du har ikke tilladelse til at gennemse denne besvarelse.", - "addon.mod_quiz.notyetgraded": "Ikke vurderet endnu", - "addon.mod_quiz.opentoc": "Åben navigations-popover", - "addon.mod_quiz.outof": "{{$a.grade}} ud af {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} ud af {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Samlet feedback", - "addon.mod_quiz.overdue": "For sent", - "addon.mod_quiz.overduemustbesubmittedby": "Tiden er udløbet. Svaret skulle have været afleveret. Hvis du gerne vil have denne quiz bedømt, skal du aflevere inden {{$a}}. Hvis du ikke afleverer inden da, vil ingen karakterer fra denne besvarelse blive talt med.", - "addon.mod_quiz.preview": "Prøvese", - "addon.mod_quiz.previewquiznow": "Prøvese quizzen", - "addon.mod_quiz.question": "Spørgsmål", - "addon.mod_quiz.quiznavigation": "Quiznavigation", - "addon.mod_quiz.quizpassword": "Quizadgangskode", - "addon.mod_quiz.reattemptquiz": "--== Prøv igen ==--", - "addon.mod_quiz.requirepasswordmessage": "For at besvarelse denne quiz skal du bruge en kode", - "addon.mod_quiz.returnattempt": "Tilbage til besvarelse", - "addon.mod_quiz.review": "Gennemse", - "addon.mod_quiz.reviewofattempt": "Gennemsyn af besvarelsen {{$a}}", - "addon.mod_quiz.reviewofpreview": "Evaluer forhåndsvisningen", - "addon.mod_quiz.showall": "Vis alle spørgsmål på en side", - "addon.mod_quiz.showeachpage": "Vis én side ad gangen", - "addon.mod_quiz.startattempt": "Start besvarelse", - "addon.mod_quiz.startedon": "Startet:", - "addon.mod_quiz.stateabandoned": "Aldrig afleveret", - "addon.mod_quiz.statefinished": "Færdig", - "addon.mod_quiz.statefinisheddetails": "Afleveret {{$a}}", - "addon.mod_quiz.stateinprogress": "I gang", - "addon.mod_quiz.stateoverdue": "Tiden er udløbet", - "addon.mod_quiz.stateoverduedetails": "Skal afleveres senest {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "--== Gem og aflever ==--", - "addon.mod_quiz.summaryofattempt": "Oversigt over besvarelse", - "addon.mod_quiz.summaryofattempts": "Oversigt over dine besvarelser", - "addon.mod_quiz.timeleft": "Tid tilbage", - "addon.mod_quiz.timetaken": "Tidsforbrug", - "addon.mod_quiz.warningattemptfinished": "Offline-forsøget blev ignoreret da det blev afsluttet på websiden eller ikke blev fundet.", - "addon.mod_quiz.warningdatadiscarded": "Nogle offline-svar blev ignoreret da spørgsmålene var ændret online", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Forsøget er ikke færdigt da nogle offline svar blev frasorteret. Gennemse dine svar og aflever igen.", - "addon.mod_quiz.yourfinalgradeis": "Den endelige vurdering af denne quiz er {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Fejl under indlæsning af indhold.", - "addon.mod_resource.modifieddate": "Ændret {{$a}}", - "addon.mod_resource.modulenameplural": "Filer", - "addon.mod_resource.openthefile": "Aben filen", - "addon.mod_resource.uploadeddate": "Uploadet {{$a}}", - "addon.mod_scorm.asset": "Materiale", - "addon.mod_scorm.assetlaunched": "Materiale - vist", - "addon.mod_scorm.attempts": "Forsøg", - "addon.mod_scorm.averageattempt": "Gennemsnitlige forsøg", - "addon.mod_scorm.browse": "Vis", - "addon.mod_scorm.browsed": "Vist", - "addon.mod_scorm.browsemode": "Forhåndsvisning", - "addon.mod_scorm.cannotcalculategrade": "Karaktererne kunne ikke beregnes.", - "addon.mod_scorm.completed": "Færdigt", - "addon.mod_scorm.contents": "Indhold", - "addon.mod_scorm.dataattemptshown": "Disse data tilhører forsøg nummer {{number}}.", - "addon.mod_scorm.enter": "Start", - "addon.mod_scorm.errorcreateofflineattempt": "En fejl opstod ved oprettelse af et nyt offline forsøg. Prøv igen.", - "addon.mod_scorm.errordownloadscorm": "Fejl ved hentning af SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Fejl ved hentning af SCORM-data.", - "addon.mod_scorm.errorinvalidversion": "Beklager, programmet understøtter kun SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Indhentning af SCORM-pakker er deaktiveret på dit Moodlewebsted. Kontakt din Moodle wedstedsadministrator.", - "addon.mod_scorm.errornovalidsco": "Denne SCORM har ikke en synlig SCO at indlæse.", - "addon.mod_scorm.errorpackagefile": "Beklager, programmet understøtter kun zip-pakker.", - "addon.mod_scorm.errorsyncscorm": "En fejl opstod under synkronisering. Prøv igen.", - "addon.mod_scorm.exceededmaxattempts": "Du har nået det maksimale antal forsøg.", - "addon.mod_scorm.failed": "Dumpet", - "addon.mod_scorm.firstattempt": "Første forsøg", - "addon.mod_scorm.gradeaverage": "Gennemsnitlig bedømmelse", - "addon.mod_scorm.gradeforattempt": "Karakter for besvarelse", - "addon.mod_scorm.gradehighest": "Højeste bedømmelse", - "addon.mod_scorm.grademethod": "Bedømmelsesmetode", - "addon.mod_scorm.gradereported": "Karakter rapporteret", - "addon.mod_scorm.gradescoes": "Læringsobjekter", - "addon.mod_scorm.gradesum": "Samlet bedømmelse", - "addon.mod_scorm.highestattempt": "Højeste forsøg", - "addon.mod_scorm.incomplete": "Ukomplet", - "addon.mod_scorm.lastattempt": "Sidste fuldførte forsøg", - "addon.mod_scorm.modulenameplural": "Scorm-pakker", - "addon.mod_scorm.newattempt": "Prøv et nyt forsøg", - "addon.mod_scorm.noattemptsallowed": "Antal tilladte forsøg", - "addon.mod_scorm.noattemptsmade": "Antal forsøg, du har gjort", - "addon.mod_scorm.notattempted": "Ikke forsøgt", - "addon.mod_scorm.offlineattemptnote": "Dette forsøg har data, der ikke er synkroniseret.", - "addon.mod_scorm.offlineattemptovermax": "Dette forsøg kan ikke sendes da du har overskredet det tilladte antal forsøg.", - "addon.mod_scorm.organizations": "Organisationer", - "addon.mod_scorm.passed": "Bestået", - "addon.mod_scorm.reviewmode": "Gennemsynstilstand", - "addon.mod_scorm.score": "Score", - "addon.mod_scorm.scormstatusnotdownloaded": "Denne SCORM er ikke downloadet. Den bliver automatisk downloadet når du åbner den.", - "addon.mod_scorm.scormstatusoutdated": "Denne SCORM er ændret efter den sidste download. Den bliver automatisk downloadet når du åbner den.", - "addon.mod_scorm.suspended": "Suspenderet", - "addon.mod_scorm.toc": "Indholdsfortegnelse", - "addon.mod_scorm.warningofflinedatadeleted": "Nogle af forsøgets offlinedata {{number}} er blevet slettet fordi de ikke kunne oprettes i et nyt forsøg.", - "addon.mod_scorm.warningsynconlineincomplete": "Nogle forsøg kunne ikke synkroniseres med webstedet, fordi det sidste onlineforsøg ikke er afsluttet. Afslut først onlineforsøget.", - "addon.mod_survey.cannotsubmitsurvey": "Beklager, kunne ikke sende din undersøgelse. Prøv igen.", - "addon.mod_survey.errorgetsurvey": "Fejl ved hentning af undersøgelsesdata.", - "addon.mod_survey.ifoundthat": "Jeg finder at", - "addon.mod_survey.ipreferthat": "Jeg foretrækker at", - "addon.mod_survey.modulenameplural": "Undersøgelser", - "addon.mod_survey.responses": "Responser", - "addon.mod_survey.results": "Resultater", - "addon.mod_survey.surveycompletednograph": "Du er færdig med denne undersøgelse", - "addon.mod_url.accessurl": "Gå til webadressen", - "addon.mod_url.modulenameplural": "URL'er", - "addon.mod_url.pointingtourl": "Ressourcen henviser til denne webadresse", - "addon.mod_wiki.cannoteditpage": "Du kan ikke redigere denne side.", - "addon.mod_wiki.createpage": "Opret side", - "addon.mod_wiki.editingpage": "Redigering af denne side \"{{$a}}\"", - "addon.mod_wiki.errorloadingpage": "Der opstod en fejl mens siden blev indlæst.", - "addon.mod_wiki.errornowikiavailable": "Denne wiki har endnu intet indhold.", - "addon.mod_wiki.gowikihome": "Gå til wiki-forsiden", - "addon.mod_wiki.map": "Kort", - "addon.mod_wiki.modulenameplural": "Wiki'er", - "addon.mod_wiki.newpagehdr": "Ny side", - "addon.mod_wiki.newpagetitle": "Ny sidetitel", - "addon.mod_wiki.nocontent": "Der er intet indhold til denne side", - "addon.mod_wiki.notingroup": "Ikke i gruppe", - "addon.mod_wiki.pageexists": "Denne side findes allerede.", - "addon.mod_wiki.pagename": "Sidenavn", - "addon.mod_wiki.subwiki": "Underwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki-sider", - "addon.mod_wiki.titleshouldnotbeempty": "Titlen skal være tom", - "addon.mod_wiki.viewpage": "Se side", - "addon.mod_wiki.wikipage": "Wikiside", - "addon.mod_wiki.wrongversionlock": "En anden bruger har redigeret denne side mens du redigerede og dit indhold er forældet.", - "addon.mod_workshop.alreadygraded": "Allerede vurderet", - "addon.mod_workshop.areainstructauthors": "Instruktioner for aflevering", - "addon.mod_workshop.areainstructreviewers": "Instruktioner for vurdering", - "addon.mod_workshop.assess": "Vurder", - "addon.mod_workshop.assessedsubmission": "Bedømt aflevering", - "addon.mod_workshop.assessmentform": "Vurderingsformular", - "addon.mod_workshop.assessmentsettings": "Vurderingsindstillinger", - "addon.mod_workshop.assessmentstrategynotsupported": "Bedømmelsesstrategien {{$a}} er ikke understøttet", - "addon.mod_workshop.assessmentweight": "Vurderingsvægtning", - "addon.mod_workshop.assignedassessments": "Tildelte opgaver at vurdere", - "addon.mod_workshop.assignedassessmentsnone": "Du har ingen tildelt aflevering at vurdere", - "addon.mod_workshop.conclusion": "Konklusion", - "addon.mod_workshop.createsubmission": "Tilføj aflevering", - "addon.mod_workshop.deletesubmission": "Slet afleveringen", - "addon.mod_workshop.editsubmission": "Rediger opgavebesvarelse", - "addon.mod_workshop.feedbackauthor": "Feedback til besvarelsen", - "addon.mod_workshop.feedbackby": "Feedback af {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Tilbagemelding fra den der vurderer", - "addon.mod_workshop.givengrades": "Karaktertildelinger", - "addon.mod_workshop.gradecalculated": "Beregnet karakter for opgave", - "addon.mod_workshop.gradeinfo": "Karakter: {{$a.received}} af {{$a.max}}", - "addon.mod_workshop.gradeover": "Tilsidesæt karakter for opgave", - "addon.mod_workshop.gradesreport": "Workshop karakterrapport", - "addon.mod_workshop.gradinggrade": "Karakter for vurdering", - "addon.mod_workshop.gradinggradecalculated": "Beregnet karakter for vurdering", - "addon.mod_workshop.gradinggradeof": "Karakter for vurdering (af {{$a}})", - "addon.mod_workshop.gradinggradeover": "Tilsidesæt karakter for vurdering", - "addon.mod_workshop.modulenameplural": "Workshopper", - "addon.mod_workshop.nogradeyet": "Ingen vurderinger endnu", - "addon.mod_workshop.notassessed": "Endnu ikke vurderet", - "addon.mod_workshop.notoverridden": "Ikke tilsidesat", - "addon.mod_workshop.noyoursubmission": "Du har ikke afleveret din opgave endnu", - "addon.mod_workshop.overallfeedback": "Samlet feedback", - "addon.mod_workshop.publishedsubmissions": "Publicerede opgaver", - "addon.mod_workshop.publishsubmission": "Publicer opgave", - "addon.mod_workshop.publishsubmission_help": "Publicerede opgaver er tilgængelige for andre efter workshoppen er lukket.", - "addon.mod_workshop.reassess": "Revurder", - "addon.mod_workshop.receivedgrades": "Karakterer modtaget", - "addon.mod_workshop.submissionattachment": "Bilag", - "addon.mod_workshop.submissioncontent": "Opgaveindhold", - "addon.mod_workshop.submissiondeleteconfirm": "Er du sikker på at du vil slette den følgende opgavebesvarelse?", - "addon.mod_workshop.submissiongrade": "Opgavekarakter", - "addon.mod_workshop.submissiongradeof": "Karakter for opgave (af {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Du skal skrive noget tekst eller tilføje en fil.", - "addon.mod_workshop.submissionrequiredtitle": "Du skal angive en titel.", - "addon.mod_workshop.submissionsreport": "Rapport over opgavebesvarelser", - "addon.mod_workshop.submissiontitle": "Titel", - "addon.mod_workshop.switchphase10": "Skift til opsætningsfasen", - "addon.mod_workshop.switchphase20": "Skift til besvarelsesfasen", - "addon.mod_workshop.switchphase30": "Skift til vurderingsfasen", - "addon.mod_workshop.switchphase40": "Skift til evalueringsfasen", - "addon.mod_workshop.switchphase50": "Luk workshoppen", - "addon.mod_workshop.userplan": "Workshopplanlægger", - "addon.mod_workshop.userplancurrentphase": "Aktuelle fase", - "addon.mod_workshop.warningassessmentmodified": "Afleveringen er blevet ændret på sitet", - "addon.mod_workshop.warningsubmissionmodified": "Bedømmelsen er blevet ændret på sitet.", - "addon.mod_workshop.weightinfo": "Vægtning: {{$a}}", - "addon.mod_workshop.yourassessment": "Din vurdering", - "addon.mod_workshop.yourassessmentfor": "Din vurdering for {{$a}}", - "addon.mod_workshop.yourgrades": "Dine karakterer", - "addon.mod_workshop.yoursubmission": "Din aflevering", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Kommentar til {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Karakter for {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspekt {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Du har valgt en karakter for dette forhold", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Kommentar til {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspekt {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Kommentar til {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Karakter for {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Udsagn {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kriterium {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Du skal vælge en af disse", - "addon.notes.addnewnote": "Tilføj en ny note", - "addon.notes.coursenotes": "Kursusnoter", - "addon.notes.deleteconfirm": "Slet denne note?", - "addon.notes.eventnotecreated": "Note oprettet", - "addon.notes.eventnotedeleted": "Note slettet", - "addon.notes.nonotes": "Der er endnu ingen noter af denne type", - "addon.notes.note": "Note", - "addon.notes.notes": "Noter", - "addon.notes.personalnotes": "Personlige noter", - "addon.notes.publishstate": "Kontekst", - "addon.notes.sitenotes": "Websteds-noter", - "addon.notes.userwithid": "Bruger med Id {{id}}", - "addon.notes.warningnotenotsent": "Kunne ikke tilføje note(r) til kurset {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Fejl ved hentning af underretninger", - "addon.notifications.markallread": "Marker alle som læst", - "addon.notifications.notificationpreferences": "Indstillinger for underretninger", - "addon.notifications.notifications": "Underretninger", - "addon.notifications.playsound": "Afspil lyd", - "addon.notifications.therearentnotificationsyet": "Der er ingen underretninger", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Forenede Arabiske Emirater", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua og Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albanien", - "assets.countries.AM": "Armenien", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktis", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Amerikansk Samoa", - "assets.countries.AT": "Østrig", - "assets.countries.AU": "Australien", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland", - "assets.countries.AZ": "Aserbajdsjan", - "assets.countries.BA": "Bosnien og Hercegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgien", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgarien", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Bolivia", - "assets.countries.BR": "Brasilien", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvetøen", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Belarus (Hviderusland)", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canada", - "assets.countries.CC": "Kokos-øerne (Keeling-øerne)", - "assets.countries.CD": "Den Demokratiske Republik Congo", - "assets.countries.CF": "Centralafrikanske Republik", - "assets.countries.CG": "Republikken Congo", - "assets.countries.CH": "Schweiz", - "assets.countries.CI": "Elfenbenskysten", - "assets.countries.CK": "Cook-øerne", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Cameroun", - "assets.countries.CN": "Kina", - "assets.countries.CO": "Colombia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Kap Verde", - "assets.countries.CX": "Juleøen", - "assets.countries.CY": "Cypern", - "assets.countries.CZ": "Tjekkiet", - "assets.countries.DE": "Tyskland", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Danmark", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Dominikanske Republik", - "assets.countries.DZ": "Algeriet", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estland", - "assets.countries.EG": "Ægypten", - "assets.countries.EH": "Vestsahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spanien", - "assets.countries.ET": "Etiopien", - "assets.countries.FI": "Finland", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Falklandsøerne (Malvinerne)", - "assets.countries.FM": "Mikronesien", - "assets.countries.FO": "Færøerne", - "assets.countries.FR": "Frankrig", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Storbritannien", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgien", - "assets.countries.GF": "Fransk Guiana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grønland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Ækvatorialguinea", - "assets.countries.GR": "Grækenland", - "assets.countries.GS": "South Georgia og South Sandwich-øerne", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hongkong", - "assets.countries.HM": "Heard- og McDonald-øerne", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Kroatien", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Ungarn", - "assets.countries.ID": "Indonesien", - "assets.countries.IE": "Irland", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Isle Of Man", - "assets.countries.IN": "Indien", - "assets.countries.IO": "British Indian Ocean Territory (Chagos Islands)", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran", - "assets.countries.IS": "Island", - "assets.countries.IT": "Italien", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordan", - "assets.countries.JP": "Japan", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirgisistan", - "assets.countries.KH": "Cambodja", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comorerne", - "assets.countries.KN": "Sankt Kitts og Nevis", - "assets.countries.KP": "Nordkorea", - "assets.countries.KR": "Sydkorea", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Caymanøerne", - "assets.countries.KZ": "Kasakhstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Sankt Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litauen", - "assets.countries.LU": "Luxembourg", - "assets.countries.LV": "Letland", - "assets.countries.LY": "Libyen", - "assets.countries.MA": "Marokko", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldova", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin (Franske del)", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshalløerne", - "assets.countries.MK": "Makedonien", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongoliet", - "assets.countries.MO": "Macau", - "assets.countries.MP": "Nordmarianerne", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauretanien", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Maldiverne", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexico", - "assets.countries.MY": "Malaysia", - "assets.countries.MZ": "Mozambique", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Ny Kaledonien", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolk-øen", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Nederlandene", - "assets.countries.NO": "Norge", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "New Zealand", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Fransk Polynesien", - "assets.countries.PG": "Papua Ny Guinea", - "assets.countries.PH": "Filippinerne", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polen", - "assets.countries.PM": "Sankt Pierre og Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palæstina", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau-øerne", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Rumænien", - "assets.countries.RS": "Serbien", - "assets.countries.RU": "Rusland", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Saudi-Arabien", - "assets.countries.SB": "Salomon-øerne", - "assets.countries.SC": "Seychellerne", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Sverige", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "St. Helena, Ascension og Tristan Da Cunha", - "assets.countries.SI": "Slovenien", - "assets.countries.SJ": "Svalbard og Jan Mayen", - "assets.countries.SK": "Slovakiet", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Sydsudan", - "assets.countries.ST": "São Tomé og Príncipe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (hollandske del)", - "assets.countries.SY": "Syrien", - "assets.countries.SZ": "Swaziland", - "assets.countries.TC": "Turks- og Caicos-øerne", - "assets.countries.TD": "Chad", - "assets.countries.TF": "Franske Sydlige Territorier", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailand", - "assets.countries.TJ": "Tadsjikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Østtimor", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunesien", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Tyrkiet", - "assets.countries.TT": "Trinidad og Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzania", - "assets.countries.UA": "Ukraine", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "USA's ydre småøer", - "assets.countries.US": "Amerikas Forenede Stater (USA)", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Usbekistan", - "assets.countries.VA": "Vatikanstaten", - "assets.countries.VC": "Sankt Vincent og Grenadinerne", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Jomfruøerne, Engelsk", - "assets.countries.VI": "Jomfruøerne, USA", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis- og Futuna-øerne", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Sydafrikanske Republik", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB-e-bog", - "assets.mimetypes.application/msword": "Worddokument", - "assets.mimetypes.application/pdf": "PDF-fil", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle-backup", - "assets.mimetypes.application/vnd.ms-excel": "Excelregneark", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 makro-aktiveret workbook", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpointpræsentation", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument Regneark", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument Regnearksskabelon", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument Tekstdokument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument Tekstskabelon", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument Websideskabelon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007-præsentation", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007-diasshow", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007-regneark", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007-skabelon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007-dokument", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote-præsentation", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers-regneark", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages-dokument", - "assets.mimetypes.application/x-javascript": "JavaScript-kilde", - "assets.mimetypes.application/x-mspublisher": "Publisher-dokument", - "assets.mimetypes.application/x-shockwave-flash": "Flashanimation", - "assets.mimetypes.application/xhtml_xml": "XHTML-dokument", - "assets.mimetypes.archive": "Arkiv ({{$a.EXT}})", - "assets.mimetypes.audio": "Lydfil ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Fil", - "assets.mimetypes.group:archive": "Arkivfiler", - "assets.mimetypes.group:audio": "Lydfiler", - "assets.mimetypes.group:document": "Dokumentfiler", - "assets.mimetypes.group:html_audio": "Lydfiler oprindeligt understøttet af browsere", - "assets.mimetypes.group:html_track": "HTML-sporingsfiler", - "assets.mimetypes.group:html_video": "Videofiler oprindeligt understøttet af browsere", - "assets.mimetypes.group:image": "Billedfiler", - "assets.mimetypes.group:presentation": "Præsentationsfiler", - "assets.mimetypes.group:sourcecode": "Kildekode", - "assets.mimetypes.group:spreadsheet": "Regnearkfiler", - "assets.mimetypes.group:video": "Videofiler", - "assets.mimetypes.group:web_audio": "Lydfiler brugt på web", - "assets.mimetypes.group:web_file": "Webfiler", - "assets.mimetypes.group:web_image": "Billedfiler brugt på web", - "assets.mimetypes.group:web_video": "Videofiler brugt på web", - "assets.mimetypes.image": "Billede ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windowsikon", - "assets.mimetypes.text/css": "Cascading Style-Sheet", - "assets.mimetypes.text/csv": "Kommaseparerede værdier", - "assets.mimetypes.text/html": "HTML-dokument", - "assets.mimetypes.text/plain": "Tekstfil", - "assets.mimetypes.text/rtf": "RTF-dokument", - "assets.mimetypes.video": "Videofil ({{$a.EXT}})", - "core.accounts": "Konti", - "core.add": "Tilføj", - "core.agelocationverification": "Alders- og lokaliseringskontrol", - "core.ago": "{{$a}} siden", - "core.all": "Alle", - "core.allgroups": "Alle grupper", - "core.allparticipants": "Alle deltagere", - "core.answer": "Svar", - "core.answered": "Besvaret", - "core.areyousure": "Er du sikker?", - "core.back": "Tilbage", - "core.block.blocks": "Blokke", - "core.cancel": "Annuller", - "core.cannotconnect": "Kan ikke forbinde", - "core.cannotdownloadfiles": "Download af filer er deaktiveret i din mobilservice. Kontakt dit websteds administrator.", - "core.captureaudio": "Optag lyd", - "core.capturedimage": "Billede taget", - "core.captureimage": "Tag et billede", - "core.capturevideo": "Optag video", - "core.category": "Kategori", - "core.choose": "Vælg", - "core.choosedots": "Vælg...", - "core.clearsearch": "Fjern søgning", - "core.clicktohideshow": "Klik for at udvide eller folde sammen", - "core.clicktoseefull": "Klik for at se alt indhold.", - "core.close": "Luk", - "core.comments": "Kommentarer", - "core.comments.addcomment": "Tilføj kommentar...", - "core.comments.comments": "Kommentarer", - "core.comments.commentscount": "Kommentarer ({{$a}})", - "core.comments.deletecommentbyon": "Slet kommentar af {{$a.user}}, {{$a.time}}", - "core.comments.eventcommentcreated": "Kommentar oprettet", - "core.comments.eventcommentdeleted": "Kommentar slettet", - "core.comments.nocomments": "Ingen kommentarer", - "core.comments.savecomment": "Gem kommentar", - "core.commentscount": "Kommentarer ({{$a}})", - "core.completion-alt-auto-fail": "Gennemført: {{$a}} (opnåede ikke beståelseskarakter)", - "core.completion-alt-auto-n": "Ikke gennemført: {{$a}}", - "core.completion-alt-auto-n-override": "Ikke fuldført: {{$a.modname}} (set by {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Gennemført: {{$a}} (opnåede beståelseskarakter)", - "core.completion-alt-auto-y": "Gennemført: {{$a}}", - "core.completion-alt-auto-y-override": "Fuldført: {{$a.modname}} (set by {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Ikke gennemført: {{$a}}. Vælg for at markere som gennemført.", - "core.completion-alt-manual-n-override": "Ikke fuldført: {{$a.modname}} (set by {{$a.overrideuser}}). Vælg til markering som fuldført.", - "core.completion-alt-manual-y": "Gennemført: {{$a}}. Vælg for at markere som ikke gennemført.", - "core.completion-alt-manual-y-override": "Fuldført: {{$a.modname}} (set by {{$a.overrideuser}}). Vælg til markering som ikke fuldført.", - "core.confirmcanceledit": "Er du sikker på at du vil forlade denne side? Alle ændringer vil gå tabt.", - "core.confirmdeletefile": "Er du sikker på at du vil slette denne fil?", - "core.confirmloss": "Er du sikker? Alle ændringer vil gå tabt.", - "core.confirmopeninbrowser": "Vil du åbne den i en browser?", - "core.considereddigitalminor": "Du er ikke gammel nok til selv at oprette en konto her.", - "core.content": "Indhold", - "core.contenteditingsynced": "Det indhold du redigerer er blevet synkroniseret.", - "core.contentlinks.chooseaccount": "Vælg konto", - "core.contentlinks.chooseaccounttoopenlink": "Vælg konto der skal åbne linket.", - "core.contentlinks.confirmurlothersite": "Linket tilhører et andet site. Vil du åbne det?", - "core.contentlinks.errornoactions": "Kunne ikke finde nogen handling af udføre med dette link", - "core.contentlinks.errornosites": "Kunne ikke finde noget site til at håndtere dette link.", - "core.continue": "Fortsæt", - "core.copiedtoclipboard": "Teksten er kopieret til udklipsholderen", - "core.course": "Kursus", - "core.course.activitydisabled": "Denne aktivitet er deaktiveret i mobilappen.", - "core.course.activitynotyetviewableremoteaddon": "Din organisation har installeret et plugin, der endnu ikke er understøttet.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Din organisations Moodle skal opdateres.", - "core.course.allsections": "Alle sektioner", - "core.course.askadmintosupport": "Kontakt din siteadministrator og fortæl, at du gerne vil bruge denne aktivitet med Moodle Mobile app'en.", - "core.course.confirmdeletemodulefiles": "Er du sikker på at du vil slette disse modulfiler?", - "core.course.confirmdownload": "Du er ved at hente {{size}}.{{availableSpace}} Er du sikker på at du vil fortsætte?", - "core.course.confirmdownloadunknownsize": "Vi kunne ikke beregne størrelsen af din download.{{availableSpace}} Er du sikker på du vil fortsætte?", - "core.course.confirmpartialdownloadsize": "Du er ved at downloade mindst {{size}}.{{availableSpace}} Er du sikker på at du vil fortsætte?", - "core.course.contents": "Indhold", - "core.course.couldnotloadsectioncontent": "Kunne ikke indlæse sektionens indhold, prøv igen senere.", - "core.course.couldnotloadsections": "Kunne ikke indlæse sektionerne, prøv igen senere.", - "core.course.coursesummary": "Kursusbeskrivelse", - "core.course.downloadcourse": "Download kursus", - "core.course.errordownloadingcourse": "Fejl i forbindelse med download af kurset.", - "core.course.errordownloadingsection": "Fejl ved indlæsning af sektion.", - "core.course.errorgetmodule": "Fejl under indhentning af aktivitetsdata.", - "core.course.hiddenfromstudents": "Skjult for studerende", - "core.course.hiddenoncoursepage": "Tilgængelig, men vises ikke på kursets forside", - "core.course.manualcompletionnotsynced": "Manuel gennemførsel er ikke synkroniseret", - "core.course.nocontentavailable": "Intet indhold tilgængeligt lige nu.", - "core.course.overriddennotice": "Din endelige karakter fra denne aktivitet blev justeret manuelt.", - "core.course.refreshcourse": "Genindlæs kurset", - "core.course.sections": "Sektioner", - "core.course.useactivityonbrowser": "Du kan stadig bruge den med din browser.", - "core.course.warningmanualcompletionmodified": "Den manuelle gennemførselsmarkering er ændret på websitet", - "core.course.warningofflinemanualcompletiondeleted": "Nogle offline manuelle gennemførselsmarkeringer '{{name}}' er blevet slettet. {{error}}", - "core.coursedetails": "Kurser", - "core.courses.addtofavourites": "Fremhæv dette kursus", - "core.courses.allowguests": "Dette kursus tillader gæster", - "core.courses.availablecourses": "Tilgængelige kurser", - "core.courses.cannotretrievemorecategories": "Kategorier dybere end niveau {{$a}} kan ikke hentes", - "core.courses.categories": "Kursuskategorier", - "core.courses.confirmselfenrol": "Er du sikker på at du ønsker at tilmelde dig dette kursus?", - "core.courses.courses": "Kurser", - "core.courses.downloadcourses": "Download kurser", - "core.courses.enrolme": "Tilmeld mig", - "core.courses.errorloadcategories": "Der skete en fejl i forbindelse med indlæsning af kategorier.", - "core.courses.errorloadcourses": "En fejl opstod ved indlæsning af kurset.", - "core.courses.errorloadplugins": "De plugins som kræves af kurset, kunne ikke indlæses korrekt. Genstart app'en og prøv igen.", - "core.courses.errorsearching": "En fejl opstod under søgning.", - "core.courses.errorselfenrol": "En fejl opstod under selvtilmelding.", - "core.courses.filtermycourses": "Filtrer mit kursus", - "core.courses.frontpage": "Forside", - "core.courses.hidecourse": "Fjern fra visning", - "core.courses.ignore": "Ignorer", - "core.courses.mycourses": "Mine kurser", - "core.courses.mymoodle": "Betjeningspanel", - "core.courses.nocourses": "Du er ikke tilmeldt nogen kurser.", - "core.courses.nocoursesyet": "Der er ingen kurser i denne kategori", - "core.courses.nosearchresults": "Ingen resultater", - "core.courses.notenroled": "Du er ikke tilmeldt dette kursus", - "core.courses.notenrollable": "Du kan ikke selv tilmelde dig dette kursus.", - "core.courses.password": "Tilmeldingsnøgle", - "core.courses.paymentrequired": "Dette kursus kræver betaling for tilmelding.", - "core.courses.paypalaccepted": "PayPal-betalinger er velkomne", - "core.courses.reload": "Genindlæs", - "core.courses.removefromfavourites": "Fjern stjerne", - "core.courses.search": "Søg", - "core.courses.searchcourses": "Søg efter kurser", - "core.courses.searchcoursesadvice": "Du kan bruge knappen kursussøgning for at få adgang som gæst eller tilmelde dig kurser der tillader det.", - "core.courses.selfenrolment": "Selvtilmelding", - "core.courses.sendpaymentbutton": "Send betaling via PayPal", - "core.courses.show": "Gendan i visning", - "core.courses.totalcoursesearchresults": "Kurser i alt: {{$a}}", - "core.currentdevice": "Aktuelle enhed", - "core.datastoredoffline": "Der blev gemt data på enheden da det ikke kunne sendes. Det vil blive sendt senere.", - "core.date": "Dato", - "core.day": "dag", - "core.days": "dage", - "core.decsep": ",", - "core.defaultvalue": "Standard ({{$a}})", - "core.delete": "Slet", - "core.deletedoffline": "Slettet offline", - "core.deleteduser": "Bruger slettet", - "core.deleting": "Sletter", - "core.description": "Beskrivelse", - "core.dfdaymonthyear": "DD-MM-YYY", - "core.dfdayweekmonth": "ddd D. MMM", - "core.dffulldate": "dddd D. MMMM YYYY h[:]mm", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm G", - "core.digitalminor": "Digital mindreårig", - "core.digitalminor_desc": "Bed dine forældre/din værge om at kontakte:", - "core.discard": "Kasser", - "core.dismiss": "Forkast", - "core.displayoptions": "Vis valgmuligheder", - "core.done": "Færdig", - "core.download": "Download", - "core.downloading": "Downloader", - "core.edit": "Rediger", - "core.editor.autosavesucceeded": "Kladde gemt.", - "core.editor.bold": "Fed", - "core.editor.clear": "Slet formatering", - "core.editor.h3": "Overskrift (stor)", - "core.editor.h4": "Overskrift (mellem)", - "core.editor.h5": "Overskrift (lille)", - "core.editor.italic": "Kursiv", - "core.editor.orderedlist": "Ordnet liste", - "core.editor.p": "Afsnit", - "core.editor.strike": "Gennemstreg", - "core.editor.textrecovered": "En kladde med denne tekst blev automatisk gendannet.", - "core.editor.underline": "Understreg", - "core.editor.unorderedlist": "Uordnet liste", - "core.emptysplit": "Denne side vil blive vist uden indhold hvis det venstre panel er tomt eller ikke bliver indlæst.", - "core.error": "Fejl", - "core.errorchangecompletion": "En fejl opstod under ændring af gennemførelsesstatus. Prøv igen.", - "core.errordeletefile": "Fejl ved sletning af filen. Prøv igen.", - "core.errordownloading": "Fejl ved download af fil.", - "core.errordownloadingsomefiles": "Fejl ved download af modulfiler. Nogle filer mangler måske.", - "core.errorfileexistssamename": "Der er allerede en fil med dette navn.", - "core.errorinvalidform": "Formularen indeholder ugyldige data. Tjek at alle krævede felter er udfyldt og at værdierne er gyldige.", - "core.errorinvalidresponse": "Ugyldigt svar modtaget. Kontakt dit Moodlewebsteds administrator hvis fejlen fortsætter.", - "core.errorloadingcontent": "Fejl under indlæsning af indhold.", - "core.errorofflinedisabled": "Offline gennemsyn er deaktiveret på dit site. Du skal være forbundet til internettet for at bruge app'en.", - "core.erroropenfilenoapp": "Fejl ved åbning af fil: intet program fundet der kan åbne denne type fil.", - "core.erroropenfilenoextension": "Fejl ved åbning af fil: filen har ingen filendelse.", - "core.erroropenpopup": "Denne aktivitet forsøger at åbne en popup. Det understøttes ikke i denne app.", - "core.errorrenamefile": "Fejl under omdøbning af filen. Prøv igen.", - "core.errorsync": "Der skete en fejl under synkronisering. Prøv igen.", - "core.errorsyncblocked": "{{$a}} kan ikke synkroniseres lige nu grundet en igangværende proces. Prøv igen senere. Fortsætter problemet, kan du prøve at genstarte appen.", - "core.explanationdigitalminor": "Denne information kræves fordi man kun må oprette en profil hvis man er over den \"digitale lavalder\". Det er den alder hvor man må give sit samtykke til betingelser og vilkår, og til at ens data lovligt må gemmes og behandles.", - "core.favourites": "Foretrukne", - "core.filename": "Filnavn", - "core.filenameexist": "Filnavnet eksisterer allerede: {{$a}}", - "core.filenotfound": "Fandt ikke filen, beklager.", - "core.fileuploader.addfiletext": "Tilføj fil", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Du er ved at oploade {{size}}. Er du sikker på at du vil fortsætte?", - "core.fileuploader.confirmuploadunknownsize": "Vi kan ikke beregne hvor stor denne upload er. Er du sikker på at du vil fortsætte?", - "core.fileuploader.errorcapturingaudio": "Fejl ved indspilning af lyd", - "core.fileuploader.errorcapturingimage": "Fejl ved indhentning af billede.", - "core.fileuploader.errorcapturingvideo": "Fejl ved indspilning af video", - "core.fileuploader.errorgettingimagealbum": "Fejl ved indhentning af billede fra album", - "core.fileuploader.errormustbeonlinetoupload": "Du skal være online for at uploade filer.", - "core.fileuploader.errornoapp": "Du har ikke installeret en app som kan udføre denne handling.", - "core.fileuploader.errorreadingfile": "Fejl ved læsning af fil.", - "core.fileuploader.errorwhileuploading": "En fejl opstod under upload af filen.", - "core.fileuploader.file": "Fil", - "core.fileuploader.filesofthesetypes": "Tilladte filtyper:", - "core.fileuploader.fileuploaded": "Filen er uploadet.", - "core.fileuploader.invalidfiletype": "Filtypen {{$a}} accepteres ikke.", - "core.fileuploader.maxbytesfile": "Filen {{$a.file}} er for stor. Den må ikke fylde mere end {{$a.size}}.", - "core.fileuploader.more": "Flere", - "core.fileuploader.photoalbums": "Fotoalbum", - "core.fileuploader.readingfile": "Læser fil", - "core.fileuploader.readingfileperc": "Læser fil: {{$a}}%", - "core.fileuploader.selectafile": "Vælg en fil", - "core.fileuploader.uploadafile": "Upload en fil", - "core.fileuploader.uploading": "Uploader", - "core.fileuploader.uploadingperc": "Uploader: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filter", - "core.folder": "Mappe", - "core.forcepasswordchangenotice": "Du skal ændre din kode inden du kan fortsætte", - "core.fulllistofcourses": "Alle kurser", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Gennemsnit", - "core.grades.badgrade": "Karakteren er ugyldig", - "core.grades.contributiontocoursetotal": "Bidrag til kursets total", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Karakter", - "core.grades.gradeitem": "Karakterelement", - "core.grades.grades": "Karakterer", - "core.grades.lettergrade": "Bogstavkarakter", - "core.grades.nogradesreturned": "Ingen karakterer returneret", - "core.grades.nooutcome": "Intet slutresultat", - "core.grades.percentage": "Procentdel", - "core.grades.range": "Interval", - "core.grades.rank": "Opstil", - "core.grades.weight": "Vægt", - "core.group": "Gruppe", - "core.groupsseparate": "Separate grupper", - "core.groupsvisible": "Synlige grupper", - "core.h5p.additionallicenseinfo": "Eventuel yderligere information om licensen", - "core.h5p.author": "Forfatter", - "core.h5p.authorcomments": "Forfatterkommentarer", - "core.h5p.authorcommentsdescription": "Kommentarer til indholdets redaktør. (Denne tekst vil ikke blive offentliggjort som en del af copyright-info.)", - "core.h5p.authorname": "Forfatters navn", - "core.h5p.authorrole": "Forfatters rolle", - "core.h5p.by": "af", - "core.h5p.cancellabel": "Annuller", - "core.h5p.changedby": "Ændret af", - "core.h5p.changedescription": "Beskrivelse af ændringen", - "core.h5p.changelog": "Ændringslog", - "core.h5p.changeplaceholder": "Foto beskåret, tekst ændret osv.", - "core.h5p.close": "Luk", - "core.h5p.confirmdialogbody": "Bekræft at du vil fortsætte. Beslutningen kan ikke fortrydes.", - "core.h5p.confirmdialogheader": "Bekræft handling", - "core.h5p.confirmlabel": "Bekræft", - "core.h5p.connectionLost": "Forbindelse mistet. Resultaterne vil blive gemt og sendt når der er forbindelse igen.", - "core.h5p.connectionReestablished": "Forbindelse genetableret.", - "core.h5p.contentCopied": "Indhold er kopieret til udklipsholderen", - "core.h5p.contentchanged": "Indholdet er ændret siden sidst du brugte det.", - "core.h5p.contenttype": "Indholdstype", - "core.h5p.copyright": "Brugsrettigheder", - "core.h5p.copyrightinfo": "Copyright-information", - "core.h5p.copyrighttitle": "Vis copyright-information om dette indhold", - "core.h5p.date": "Dato", - "core.h5p.disablefullscreen": "Deaktiver fuld skærm", - "core.h5p.downloadtitle": "Download dette indhold som en H5P-fil.", - "core.h5p.embed": "Indlejr", - "core.h5p.embedtitle": "Vis den indlejrede kode til dette indhold.", - "core.h5p.fullscreen": "Fuld skærm", - "core.h5p.h5ptitle": "Besøg h5p.org og tjek mere indhold ud.", - "core.h5p.hideadvanced": "Skjul avanceret", - "core.h5p.license": "Licens", - "core.h5p.licensee": "Licensindehaver", - "core.h5p.licenseextras": "Yderligere licens", - "core.h5p.licenseversion": "Licensversion", - "core.h5p.nocopyright": "Der er ingen tilgængelig information om copyright for dette indhold.", - "core.h5p.offlineDialogBody": "Vi kunne ikke sende information om din gennemførelse af denne opgave. Tjek din internetforbindelse.", - "core.h5p.offlineDialogHeader": "Din forbindelse til serveren forsvandt.", - "core.h5p.offlineDialogRetryButtonLabel": "Prøv igen nu", - "core.h5p.offlineDialogRetryMessage": "Prøver igen om :num....", - "core.h5p.offlineSuccessfulSubmit": "Resultater afleveret.", - "core.h5p.originator": "Forfatter", - "core.h5p.resizescript": "Inkluder dette script på din websidt hvis du vil have dynamisk skalering af det indlejrede indhold:", - "core.h5p.resubmitScores": "Forsøger at aflevere gemte resultater.", - "core.h5p.reuse": "Genbrug", - "core.h5p.reuseContent": "Genbrug indhold", - "core.h5p.reuseDescription": "Genbrug dette indhold", - "core.h5p.showadvanced": "Vis avanceret", - "core.h5p.showless": "Vis mindre", - "core.h5p.showmore": "Vis mere", - "core.h5p.size": "Størrelse", - "core.h5p.source": "Kilde", - "core.h5p.startingover": "Du starter forfra.", - "core.h5p.sublevel": "Underniveau", - "core.h5p.thumbnail": "Miniature", - "core.h5p.title": "Titel", - "core.h5p.undisclosed": "Ikke oplyst", - "core.h5p.year": "År", - "core.h5p.years": "År", - "core.h5p.yearsfrom": "År (fra)", - "core.h5p.yearsto": "År (til)", - "core.hasdatatosync": "Denne {{$a}} har data offline der skal synkroniseres.", - "core.help": "Hjælp", - "core.hide": "Skjul", - "core.hour": "time", - "core.hours": "timer", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Billede", - "core.imageviewer": "Billedfremviser", - "core.info": "Information", - "core.invalidformdata": "Forkert formulardata", - "core.labelsep": ":", - "core.lastaccess": "Sidst set", - "core.lastdownloaded": "Sidst downloadet", - "core.lastmodified": "Senest ændret", - "core.lastsync": "Sidst synkroniseret", - "core.list": "Liste", - "core.listsep": ";", - "core.loading": "Indlæser", - "core.loadmore": "Indlæs mere", - "core.location": "Sted", - "core.login.auth_email": "E-mailbaseret selvregistrering", - "core.login.authenticating": "Godkender", - "core.login.cancel": "Annuller", - "core.login.changepassword": "Skift adgangskode", - "core.login.confirmdeletesite": "Er du sikker på at du ønsker at slette websiden {{sitename}}?", - "core.login.connect": "Tilslut!", - "core.login.connecttomoodle": "Tilslut til Moodle", - "core.login.contactyouradministrator": "Kontakt administrator for yderligere hjælp", - "core.login.contactyouradministratorissue": "Bed administrator om at tjekke følgende: {{$a}}", - "core.login.createaccount": "Opret ny profil", - "core.login.createuserandpass": "Vælg brugernavn og adgangskode", - "core.login.credentialsdescription": "Skriv dit brugernavn og adgangskode for at logge på", - "core.login.emailconfirmsent": "

      En e-mail skulle være sendt til din adresse {{$a}}

      Den indeholder anvisninger på, hvordan du fuldfører din registrering.

      Hvis du stadig har problemer, så kontakt webadministratoren.

      ", - "core.login.emailconfirmsentnoemail": "

      Du har fået tilsendt en mail.

      Den har enkle retningslinjer for hvordan du bliver registreret.

      Kontakt sidens administrator hvis du behøver hjælp.

      ", - "core.login.emailconfirmsentsuccess": "Bekræftelsesmail sendt", - "core.login.emailnotmatch": "Mailadresserne matcher ikke", - "core.login.erroraccesscontrolalloworigin": "Det Cross-Origin opkald du forsøger at udføre er blevet afvist. Kontroller venligst https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "En fejl opstod ved sletning af denne webside. Prøv venligst igen.", - "core.login.errorupdatesite": "En fejl opstod under opdatering af webstedets token.", - "core.login.findyoursite": "Find dit site", - "core.login.firsttime": "Er det dit første besøg?", - "core.login.forcepasswordchangenotice": "Du skal ændre din kode inden du kan fortsætte", - "core.login.forgotten": "Har du glemt dit brugernavn eller din adgangskode?", - "core.login.help": "Hjælp", - "core.login.helpmelogin": "

      Der er mange tusinde Moodlesites rundt omkring i verden. Denne app kan kun anvendes på sites, der specifikt har aktiveret app-adgang.

      Hvis du ikke kan tilgå dit Moodlesite, kan du kontakte din siteadministrator og gøre opmærksom på siden https://docs.moodle.org/en/Mobile_app

      App'en kan testes på et Moodle-demosite ved at skrive teacher elllerstudent i feltet Site address og klikke på knappen Connect.

      ", - "core.login.instructions": "Instruktioner", - "core.login.invalidaccount": "Kontroller venligst dine loginoplysninger eller spørg webstedets administrator om at kontrollere webstedets konfiguration.", - "core.login.invaliddate": "Ugyldig dato", - "core.login.invalidemail": "Ugyldig e-mailadresse", - "core.login.invalidmoodleversion": "

      Ugyldig Moodle version. Moodleappen fungerer kun sammen med Moodle version {{$a}} eller højere.

      \n

      Du kan kontakte din Moodle-administrator og bede om at få Moodle opdateret.

      \n

      Hvis du ikke ved hvordan du gør det, kan du kontakte din lærer.

      ", - "core.login.invalidsite": "Denne webadresse er ugyldig.", - "core.login.invalidtime": "Ugyldigt klokkeslæt", - "core.login.invalidurl": "Ugyldig URL specificeret", - "core.login.invalidvaluemax": "Højeste værdi er {{$a}}", - "core.login.invalidvaluemin": "Mindste værdi er {{$a}}", - "core.login.localmobileunexpectedresponse": "Du har fået en uventet reaktion fra Moodle Mobile Additional Features, så du vil blive godkendt ved hjælp af standard Mobile service.", - "core.login.login": "Log ind", - "core.login.loginbutton": "Login", - "core.login.logininsiterequired": "Du skal logge på websiden via en browser", - "core.login.loginsteps": "For at få fuld adgang til siden, skal du oprette en brugerkonto.", - "core.login.missingemail": "Mangler e-mailadresse", - "core.login.missingfirstname": "Mangler fornavn", - "core.login.missinglastname": "Mangler efternavn", - "core.login.mobileservicesnotenabled": "Mobiladgang er ikke aktiveret på din side. Kontakt venligst din Moodles administrator, hvis du ønsker mobiladgang aktiveret.", - "core.login.mustconfirm": "Du skal bekræfte din konto", - "core.login.newaccount": "Ny konto", - "core.login.notloggedin": "Du skal være logget på.", - "core.login.password": "Adgangskode", - "core.login.passwordforgotten": "Glemt adgangskode", - "core.login.passwordforgotteninstructions2": "Skriv dit brugernavn eller din e-mailadresse nedenfor hvis du skal have ny adgangskode. Hvis vi kan finde dig i databasen, vil en mail blive sendt til din e-mailadresse med instruktioner om, hvordan du får adgang igen.", - "core.login.passwordrequired": "Adgangskode kræves", - "core.login.policyaccept": "Jeg forstår og er enig", - "core.login.policyagree": "Du må acceptere disse retningslinjer for at benytte dette site.\nAccepterer du?", - "core.login.policyagreement": "Retningslinjer for brug af webstedet", - "core.login.policyagreementclick": "Link til retningslinjer for brug af webstedet", - "core.login.potentialidps": "Log in ved brug af din konto på:", - "core.login.profileinvaliddata": "Ugyldig værdi", - "core.login.recaptchaincorrect": "Svaret på sikkerhedsspørgsmålet er forkert.", - "core.login.reconnect": "Genopret forbindelse", - "core.login.reconnectdescription": "Din godkendelse er ugyldig eller udløbet, du skal forbinde til webstedet igen.", - "core.login.reconnectssodescription": "Din godkendelse er ugyldig eller udløbet, du skal genoprette forbindelsen til webstedet. Du skal logge på webstedet via en browser.", - "core.login.resendemail": "Gensend mail", - "core.login.security_question": "Sikkerhedsspørgsmål", - "core.login.selectacountry": "Vælg et land", - "core.login.selectsite": "Vælg websted:", - "core.login.signupplugindisabled": "{{$a}} er ikke aktiveret.", - "core.login.siteaddress": "Dit websted", - "core.login.siteinmaintenance": "Dit websted er i vedligeholdelsestilstand", - "core.login.siteurl": "Webadresse", - "core.login.siteurlrequired": "Webside-URL kræves, f.eks. http://www.dinmoodle.dk eller https://www.dinmoodle.dk", - "core.login.startsignup": "Opret ny profil", - "core.login.stillcantconnect": "Stadig ingen forbindelse?", - "core.login.supplyinfo": "Profiloplysninger", - "core.login.username": "Brugernavn", - "core.login.usernameoremail": "Skriv brugernavn eller e-mailadresse", - "core.login.usernamerequired": "Brugernavn kræves", - "core.login.usernotaddederror": "Brugeren blev ikke tilføjet - fejl", - "core.login.visitchangepassword": "Vil du besøge siden for at ændre adgangskoden?", - "core.login.webservicesnotenabled": "Web Services er måske ikke aktiveret på dit websted. Bed din administrator om hjælp.", - "core.lostconnection": "Din godkendelse er ugyldig eller udløbet, så du skal genoprette forbindelsen til webstedet.", - "core.mainmenu.changesite": "Skift side", - "core.mainmenu.help": "Hjælp", - "core.mainmenu.logout": "Log ud", - "core.mainmenu.website": "Websted", - "core.maxsizeandattachments": "Maksimal størrelse på filer: {{$a.size}}, højeste antal bilag: {{$a.attachments}}", - "core.min": "min.", - "core.mins": "min.", - "core.misc": "Diverse", - "core.mod_assign": "Opgave", - "core.mod_assignment": "Opgaver (2.2) (Deaktiveret)", - "core.mod_book": "Bog", - "core.mod_chat": "Chat", - "core.mod_choice": "Afstemning", - "core.mod_data": "Database", - "core.mod_database": "Database", - "core.mod_external-tool": "Eksternt værktøj", - "core.mod_feedback": "Feedback", - "core.mod_file": "Fil", - "core.mod_folder": "Mappe", - "core.mod_forum": "Forum", - "core.mod_glossary": "Opslagsværk", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS-pakke", - "core.mod_imscp": "IMS-pakke", - "core.mod_label": "Etiket", - "core.mod_lesson": "Lektion", - "core.mod_lti": "Eksternt værktøj", - "core.mod_page": "Webside", - "core.mod_quiz": "Quiz", - "core.mod_resource": "Fil", - "core.mod_scorm": "SCORM-pakke", - "core.mod_survey": "Undersøgelse", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Workshop", - "core.moduleintro": "Beskrivelse", - "core.more": "mere", - "core.mygroups": "Mine grupper", - "core.name": "Navn", - "core.networkerroriframemsg": "Dette indhold er ikke tilgængeligt offline. Forbind til internettet og prøv igen.", - "core.networkerrormsg": "Der var problemer med at tilslutte til webstedet. Tjek din forbindelse og prøv igen.", - "core.never": "Aldrig", - "core.next": "Næste", - "core.no": "Nej", - "core.nocomments": "Ingen kommentarer", - "core.nograde": "Ingen karakter", - "core.none": "Ingen", - "core.nopasswordchangeforced": "Du kan ikke fortsætte uden at ændre din adgangskode.", - "core.nopermissions": "Beklager, men dette ({{$a}}) har du aktuelt ikke tilladelse til.", - "core.noresults": "Ingen resultater", - "core.noselection": "Intet udvalg", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Denne profil er ikke tilgængelig da brugeren ikke er tilmeldt dette kursus.", - "core.notice": "Bemærk", - "core.notingroup": "Desværre! Du skal være med i en gruppe for at se denne side.", - "core.notsent": "Ikke sendt", - "core.now": "nu", - "core.numwords": "{{$a}} ord", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Klik her for at vise billedet i fuld størrelse.", - "core.openinbrowser": "Åben i browser", - "core.othergroups": "Andre grupper", - "core.pagea": "Side {{$a}}", - "core.paymentinstant": "Brug knappen forneden til at betale og blive tilmeldt umiddelbart derefter.", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pictureof": "Billede af {{$a}}", - "core.previous": "Forrige", - "core.proceed": "Fortsæt", - "core.pulltorefresh": "Træk for at opdatere", - "core.question.answer": "Svar", - "core.question.answersaved": "Besvaret", - "core.question.certainty": "Sikkerhed", - "core.question.complete": "Gennemført", - "core.question.correct": "Rigtigt", - "core.question.errorattachmentsnotsupported": "Programmet understøtter endnu ikke bilag til svar.", - "core.question.errorinlinefilesnotsupported": "Programmet understøtter endnu ikke redigering af filer inline.", - "core.question.errorquestionnotsupported": "Appen understøtter ikke denne type spørgsmål: {{$a}}.", - "core.question.feedback": "Feedback", - "core.question.incorrect": "Forkert", - "core.question.information": "Information", - "core.question.invalidanswer": "Ufuldstændigt svar", - "core.question.notanswered": "Ikke besvaret", - "core.question.notyetanswered": "Ikke besvaret", - "core.question.partiallycorrect": "Delvis rigtigt", - "core.question.questionmessage": "Spørgsmål {{$a}}: {{$b}}", - "core.question.questionno": "Spørgsmål {{$a}}", - "core.question.requiresgrading": "Kræver bedømmelse", - "core.quotausage": "Du har nu brugt {{$a.used}} af din grænse på {{$a.total}}.", - "core.rating.aggregateavg": "Gennemsnitsbedømmelse", - "core.rating.aggregatecount": "Antal bedømmelser", - "core.rating.aggregatemax": "Højeste bedømmelse", - "core.rating.aggregatemin": "Laveste bedømmelse", - "core.rating.aggregatesum": "Bedømmelsessum", - "core.rating.noratings": "Ingen bedømmelser sendt", - "core.rating.rating": "Bedømmelse", - "core.rating.ratings": "Bedømmelser", - "core.redirectingtosite": "Du bliver videresendt til siden", - "core.refresh": "Genindlæs", - "core.remove": "Fjern", - "core.required": "Påkrævet", - "core.requireduserdatamissing": "Denne bruger mangler nogle krævede profildata. Udfyld venligst de manglende data i din Moodle og prøv igen.
      {{$a}}", - "core.resourcedisplayopen": "Åben", - "core.resources": "Materialer", - "core.restore": "Gendan", - "core.restricted": "Begrænset", - "core.retry": "Prøv igen", - "core.save": "Gem", - "core.savechanges": "Gem ændringer", - "core.search": "Søg", - "core.searching": "Søger", - "core.searchresults": "Søgeresultater", - "core.sec": "sekunder", - "core.secs": "sekunder", - "core.seemoredetail": "Klik her for flere oplysninger", - "core.selectacategory": "Vælg en kategori", - "core.selectacourse": "Vælg et kursus", - "core.selectagroup": "Vælg en gruppe", - "core.send": "Send", - "core.sending": "Sender", - "core.serverconnection": "Fejl ved forbindelse til server", - "core.settings.about": "Om", - "core.settings.cannotsyncoffline": "Kan ikke synkronisere offline", - "core.settings.cannotsyncwithoutwifi": "Kan ikke synkronisere fordi indstillingerne kun tillader synkronisering via Wi-Fi. Forbind til et Wi-Fi netværk.", - "core.settings.cordovadevicemodel": "Cordova Device model", - "core.settings.cordovadeviceosversion": "Cordova Device OS version", - "core.settings.cordovadeviceplatform": "Cordova Device platform", - "core.settings.cordovadeviceuuid": "Cordova Device uuid", - "core.settings.cordovaversion": "Cordova version", - "core.settings.currentlanguage": "Sprogvalg", - "core.settings.debugdisplay": "Vis debug-beskeder", - "core.settings.deletesitefiles": "Er du sikker på du vil slette de downloadede filer og cachede data fra webstedet '{{sitename}}'? Det vil forhindre dig i at bruge appen i offline tilstand.", - "core.settings.deletesitefilestitle": "Slet webstedsfiler", - "core.settings.deviceinfo": "Enhedsinfo", - "core.settings.deviceos": "Enheds operativsystem", - "core.settings.disableall": "Deaktiver notifikationer", - "core.settings.disabled": "Deaktiveret", - "core.settings.displayformat": "Vis format", - "core.settings.enabledownloadsection": "Aktiver download af sektioner", - "core.settings.enablerichtexteditor": "Aktiver \"rich text\"-editor", - "core.settings.enablerichtexteditordescription": "\"Rich text\"-editoren kan blive vist på steder der tillader det.", - "core.settings.enablesyncwifi": "Tillad kun synkronisering når tilsluttet Wi-Fi", - "core.settings.errordeletesitefiles": "Fejl ved sletning af websidefiler", - "core.settings.errorsyncsite": "Fejl ved synkronisering. Kontroller din Internettilslutning og prøv igen.", - "core.settings.estimatedfreespace": "Beregnet ledig plads", - "core.settings.filesystemroot": "Filsystemets rod", - "core.settings.general": "Generelt", - "core.settings.language": "Sprog", - "core.settings.license": "Licens", - "core.settings.localnotifavailable": "Lokale meddelelser tilgængelige", - "core.settings.locationhref": "Webvisning af URL", - "core.settings.locked": "Låst", - "core.settings.loggedin": "Online", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Navigatorsprog", - "core.settings.navigatoruseragent": "Navigator userAgent", - "core.settings.networkstatus": "Status for internetforbindelse", - "core.settings.preferences": "Indstillinger", - "core.settings.privacypolicy": "Privatlivspolitik", - "core.settings.reportinbackground": "Anmeld fejl automatisk", - "core.settings.settings": "Indstillinger", - "core.settings.sites": "Websteder", - "core.settings.spaceusage": "Pladsforbrug", - "core.settings.synchronization": "Synkronisering", - "core.settings.synchronizenow": "Synkroniser nu", - "core.settings.syncsettings": "Indstilling for synkronisering", - "core.settings.total": "Total", - "core.settings.wificonnection": "WiFi-forbindelse", - "core.sharedfiles.chooseaccountstorefile": "Vælg den konto filen skal gemmes under.", - "core.sharedfiles.chooseactionrepeatedfile": "Der er allerede en fil med dette navn. Vil du erstatte den eller omdøbe den til \"{{$a}}\"?", - "core.sharedfiles.nosharedfiles": "Der er ikke gemt nogen delte filer på dette websted.", - "core.sharedfiles.rename": "Omdøb", - "core.sharedfiles.replace": "Erstat", - "core.sharedfiles.sharedfiles": "Delte filer", - "core.sharedfiles.successstorefile": "Filen er gemt. Du kan vælge den til upload til dine private filer eller vedlægge den i nogle aktiviteter.", - "core.show": "Vis", - "core.showless": "Vis mindre...", - "core.showmore": "Vis mere...", - "core.site": "Websted", - "core.sitehome.sitehome": "Webstedets forside", - "core.sitehome.sitenews": "Sitemeddelelser", - "core.sitemaintenance": "Dette websted er i øjeblikket ikke tilgængeligt grundet vedligeholdelsesarbejde.", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Spring over", - "core.sorry": "Beklager...", - "core.sort": "Sortér", - "core.sortby": "Sorter efter", - "core.strftimedate": "%d. %B %Y", - "core.strftimedatefullshort": "%d/%m-%y", - "core.strftimedateshort": "%d. %B", - "core.strftimedatetime": "%d. %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m-%y %H:%M", - "core.strftimedaydate": "%A den %d. %B %Y", - "core.strftimedaydatetime": "%A den %d. %B %Y, %H:%M", - "core.strftimedayshort": "%A d. %d. %b.", - "core.strftimedaytime": "%a., %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d. %b., %H:%M", - "core.strftimerecentfull": "%a. %d. %b. %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "Gem", - "core.success": "Succes", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Standard samling", - "core.tag.inalltagcoll": "Overalt", - "core.tag.itemstaggedwith": "{{$a.tagarea}} tagget med \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Ingen resultater på \"{{$a}}\"", - "core.tag.notagsfound": "Der blev ikke fundet nogen tag-match på \"{{$a}}\"", - "core.tag.searchtags": "Søg i tags", - "core.tag.showingfirsttags": "Viser de {{$a}} mest populære tags", - "core.tag.tag": "Tag", - "core.tag.tagarea_course": "Kurser", - "core.tag.tagarea_course_modules": "Aktiviteter og materialer", - "core.tag.tagarea_post": "Blogindlæg", - "core.tag.tagarea_user": "Brugerinteresser", - "core.tag.tags": "Tags", - "core.teachers": "Lærere", - "core.thereisdatatosync": "Der er {{$a}} offline der skal synkroniseres.", - "core.thisdirection": "ltr", - "core.time": "Tidspunkt", - "core.timesup": "Tiden er gået!", - "core.today": "I dag", - "core.tryagain": "Prøv igen", - "core.twoparagraphs": "{{p1}}

      {{p2}}", - "core.unexpectederror": "Uventet fejl. Luk og åben programmet igen i et nyt forsøg.", - "core.unicodenotsupported": "Nogle smilies supporteres ikke på dette websted, og de vil blive fjernet når beskeden sendes.", - "core.unknown": "Ukendt", - "core.unlimited": "Ubegrænset", - "core.unzipping": "Udpakker", - "core.upgraderunning": "Sitet er under opgradering. Prøv igen senere.", - "core.user": "Bruger", - "core.user.address": "Adresse", - "core.user.city": "By", - "core.user.contact": "Kontakt", - "core.user.country": "Land", - "core.user.description": "Beskrivelse", - "core.user.details": "Detaljer", - "core.user.detailsnotavailable": "Denne brugers data er ikke tilgængelige for dig.", - "core.user.editingteacher": "Lærer", - "core.user.email": "E-mailadresse", - "core.user.emailagain": "E-mail (igen)", - "core.user.errorloaduser": "Fejl ved indlæsning af bruger.", - "core.user.firstname": "Fornavn", - "core.user.interests": "Interesser", - "core.user.lastname": "Efternavn", - "core.user.manager": "Administrator", - "core.user.newpicture": "Nyt billede", - "core.user.noparticipants": "Ingen deltagere fundet på dette kursus", - "core.user.participants": "Deltagere", - "core.user.phone1": "Telefon", - "core.user.phone2": "Mobil", - "core.user.roles": "Roller", - "core.user.sendemail": "E-mail", - "core.user.student": "Studerende", - "core.user.teacher": "Medunderviser", - "core.user.webpage": "Webside", - "core.userdeleted": "Denne brugerkonto er blevet slettet", - "core.userdetails": "Brugeroplysninger", - "core.usernotfullysetup": "Brugeren er ikke fuldt sat op", - "core.users": "Brugere", - "core.view": "Vis", - "core.viewprofile": "Vis profil", - "core.whatisyourage": "Hvor gammel er du?", - "core.wheredoyoulive": "Hvilket land bor du i?", - "core.whoops": "Ups!", - "core.whyisthishappening": "Hvorfor sker dette?", - "core.whyisthisrequired": "Hvorfor spørger vi om det?", - "core.wsfunctionnotavailable": "Denne webservicefunktion er ikke tilgængelig.", - "core.year": "år", - "core.years": "år", - "core.yes": "Ja" -} \ No newline at end of file diff --git a/src/assets/lang/de-du.json b/src/assets/lang/de-du.json deleted file mode 100644 index 9c52c10cf..000000000 --- a/src/assets/lang/de-du.json +++ /dev/null @@ -1,673 +0,0 @@ -{ - "addon.blog.errorloadentries": "Fehler beim Laden der Blogbeiträge", - "addon.blog.showonlyyourentries": "Nur deine Beiträge anzeigen", - "addon.calendar.calendarevent": "Kalendereintrag", - "addon.calendar.calendarevents": "Kalender", - "addon.calendar.calendarreminders": "Kalendererinnerungen", - "addon.calendar.confirmeventdelete": "Möchtest du den Termin '{{$a}}' wirklich löschen?", - "addon.calendar.confirmeventseriesdelete": "Der Termin '{{$a.name}}' ist Teil der Serie. Möchtest du nur diesen Termin entfernen oder alle {{$a.count}} Termine dieser Serie?", - "addon.calendar.currentmonth": "Aktueller Monat", - "addon.calendar.defaultnotificationtime": "Benachrichtigungszeit", - "addon.calendar.errorloadevent": "Fehler beim Laden des Kalendereintrags", - "addon.calendar.errorloadevents": "Fehler beim Laden der Kalendereinträge", - "addon.calendar.invalidtimedurationminutes": "Die angegebene Dauer in Minuten ist ungültig. Gib eine Zahl größer als 0 ein oder wähle 'Ohne Zeitangabe'.", - "addon.calendar.invalidtimedurationuntil": "Der angegebene Wert für Datum und Zeit von 'Dauer bis' liegt vor der Startzeit des Termins. Korrigiere diese Einstellung.", - "addon.calendar.noevents": "Keine Kalendereinträge", - "addon.calendar.nopermissiontoupdatecalendar": "Du hast nicht die Rechte, den Termin zu aktualisieren.", - "addon.calendar.reminders": "Erinnerungen", - "addon.calendar.setnewreminder": "Neue Erinnerung erstellen", - "addon.competency.errornocompetenciesfound": "Keine Kompetenzen gefunden", - "addon.competency.nocompetencies": "Keine Kompetenzen", - "addon.coursecompletion.complete": "Abschließen", - "addon.coursecompletion.couldnotloadreport": "Fehler beim Laden des Abschlussberichts. Versuche es später noch einmal.", - "addon.coursecompletion.nottracked": "Aktuell läuft für dich in diesem Kurs keine Abschlussverfolgung", - "addon.coursecompletion.required": "Notwendig", - "addon.coursecompletion.status": "Status", - "addon.files.couldnotloadfiles": "Die Dateiliste konnte nicht geladen werden.", - "addon.files.emptyfilelist": "Keine Dateien", - "addon.files.erroruploadnotworking": "Im Moment können keine Dateien zur Website hochgeladen werden.", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Geräte konfigurieren", - "addon.messages.addcontactconfirm": "Möchtest du wirklich {{$a}} zu deinen Kontakten hinzufügen?", - "addon.messages.blockuserconfirm": "Möchtest du wirklich {{$a}} blockieren?", - "addon.messages.contactlistempty": "Die Kontaktliste ist leer.", - "addon.messages.contactname": "Name", - "addon.messages.deleteallconfirm": "Möchtest du wirklich die gesamte Kommunikation löschen? Die Kommunikation bleibt allerdings bei den anderen beteiligten Personen erhalten.", - "addon.messages.deleteallselfconfirm": "Möchtest du diese persönliche Kommunikation wirklich löschen?", - "addon.messages.deletemessage": "Mitteilung löschen", - "addon.messages.deletemessageconfirmation": "Möchtest du diese Mitteilung wirklich löschen? Die Mitteilung wird nur in deinem Verlauf gelöscht, aber nicht bei der Person, die die Mitteilung gesendet oder empfangen hat.", - "addon.messages.errordeletemessage": "Fehler beim Löschen der Mitteilung", - "addon.messages.errorwhileretrievingcontacts": "Fehler beim Abrufen der Kontakte vom Server", - "addon.messages.errorwhileretrievingdiscussions": "Fehler beim Abrufen der Themen vom Server", - "addon.messages.errorwhileretrievingmessages": "Fehler beim Abrufen der Mitteilungen vom Server", - "addon.messages.errorwhileretrievingusers": "Fehler beim Abrufen der Personen vom Server", - "addon.messages.isnotinyourcontacts": "{{$a}} gehört nicht zu deinen Kontakten", - "addon.messages.messagenotsent": "Die Mitteilung wurde nicht gesendet. Versuche es später noch einmal.", - "addon.messages.newmessages": "Neue Mitteilungen", - "addon.messages.nousersfound": "Keine Personen gefunden", - "addon.messages.removecontactconfirm": "Möchtest du wirklich {{$a}} aus deinen Kontakten entfernen?", - "addon.messages.requirecontacttomessage": "Du musst {{$a}} anfragen, Sie als Kontakt hinzuzufügen. Erst dann kannst du eine Mitteilung senden.", - "addon.messages.selfconversationdefaultmessage": "Speichere Entwürfe von Nachrichten, Links, Notizen usw. für einen späteren Zugriff.", - "addon.messages.showdeletemessages": "Mitteilungen löschen anzeigen", - "addon.messages.type_blocked": "Gesperrt", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Suchergebnisse", - "addon.messages.type_strangers": "Weitere Personen", - "addon.messages.unblockuserconfirm": "Möchtest du die Blockierung für {{$a}} wirklich aufheben?", - "addon.messages.useentertosenddescdesktop": "Falls deaktiviert, kannst du mit Ctrl+Enter die Mitteilung senden.", - "addon.messages.useentertosenddescmac": "Falls deaktiviert, kannst du mit Cmd+Enter die Mitteilung senden.", - "addon.messages.userwouldliketocontactyou": "{{$a}} möchte dich kontaktieren", - "addon.messages.warningconversationmessagenotsent": "Mitteilung zum Thema {{conversation}} konnte nicht gesendet werden. {{error}}", - "addon.messages.warningmessagenotsent": "Mitteilung an {{user}} konnte nicht gesendet werden. {{error}}", - "addon.messages.wouldliketocontactyou": "Ich möchte dich kontaktieren", - "addon.messages.you": "Du:", - "addon.messages.youhaveblockeduser": "Du hast diese Person blockiert.", - "addon.messages.yourcontactrequestpending": "Deine Kontaktanfrage an {{$a}} ist noch offen", - "addon.mod_assign.acceptsubmissionstatement": "Bestätige die Erklärung zur Eigenständigkeit.", - "addon.mod_assign.cannoteditduetostatementsubmission": "Du kannst in der App keine Abgabe hinzufügen oder bearbeiten, weil keine Erklärung zur Eigenständigkeit von der Website abgerufen werden konnte.", - "addon.mod_assign.cannotgradefromapp": "Manche Bewertungsmethoden werden von der App bisher nicht unterstützt und können nicht verändert werden.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Du kannst in der App keine Abgabe machen, weil von der Website keine Erklärung zur Eigenständigkeit abgerufen werden konnte.", - "addon.mod_assign.confirmsubmission": "Wenn du nun deine Lösung zur Bewertung einreichst, kannst du nichts mehr verändern. Bist du dir sicher?", - "addon.mod_assign.erroreditpluginsnotsupported": "Du kannst in der App keine Abgabe hinzufügen oder bearbeiten, weil manche Plugins bisher keine Bearbeitung zulassen:", - "addon.mod_assign.errorshowinginformation": "Die Abgabeinformationen können nicht angezeigt werden.", - "addon.mod_assign.feedbacknotsupported": "Dieses Feedback wird von der App nicht unterstützt, so dass Informationen fehlen könnten.", - "addon.mod_assign.gradenotsynced": "Bewertung nicht synchronisiert", - "addon.mod_assign.multipleteams_desc": "Diese Aufgabe wird in Gruppen abgegeben. Du bist Mitglied in mehr als einer Gruppe. Um deine Einreichung korrekt deiner Gruppe zuordnen zu können, musst du Mitglied in genau einer Gruppe sein. Kontaktiere deine Trainer/innen, um deine Gruppenzugehörigkeit zu aktualisieren.", - "addon.mod_assign.notallparticipantsareshown": "Teilnehmer/innen, die nichts abgegeben haben, werden nicht angezeigt.", - "addon.mod_assign.noteam_desc": "Diese Aufgabe wird in Gruppen abgegeben. Du bist kein Mitglied in einer Gruppe und kannst die Aufgabe deswegen nicht einreichen. Kontaktiere deine Trainer/innen, um einer Gruppe hinzugefügt zu werden.", - "addon.mod_assign.submissionnotsupported": "Diese Abgabe wird von der App nicht unterstützt und es könnten Infos fehlen.", - "addon.mod_assign.submitassignment_help": "Sobald diese Aufgabe abgegeben ist, kannst du nichts mehr ändern.", - "addon.mod_assign.userwithid": "Nutzer/in mit ID {{id}}", - "addon.mod_assign.warningsubmissiongrademodified": "Die Abgabebewertung wurde auf der Website geändert.", - "addon.mod_assign.warningsubmissionmodified": "Die Abgabe wurde auf der Website geändert.", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Die maximale Anzahl von Wörtern für diese Aufgabenlösung ist {{$a.limit}}. Deine Antwort hat {{$a.count}} Wörter. Verkürze deinen Text und speichere ihn dann erneut.", - "addon.mod_chat.enterchat": "Willst du den Chat starten? Klick hier.", - "addon.mod_chat.errorwhileconnecting": "Fehler beim Verbinden zum Chat", - "addon.mod_chat.errorwhilegettingchatdata": "Fehler beim Abrufen des Chats", - "addon.mod_chat.errorwhilegettingchatusers": "Fehler beim Anzeigen der Personen im Chat", - "addon.mod_chat.errorwhileretrievingmessages": "Fehler beim Abrufen der Mitteilungen vom Server", - "addon.mod_chat.errorwhilesendingmessage": "Fehler beim Senden der Mitteilung", - "addon.mod_chat.messagebeepsyou": "{{$a}} hat dich angepiepst!", - "addon.mod_chat.messageyoubeep": "Du hast {{$a}} angepiepst", - "addon.mod_chat.mustbeonlinetosendmessages": "Du musst online sein, um Mitteilungen senden zu können.", - "addon.mod_chat.nosessionsfound": "Keine Sessions", - "addon.mod_chat.showincompletesessions": "Unvollständige Sessions anzeigen", - "addon.mod_choice.cannotsubmit": "Deine Auswahl konnte nicht gespeichert werden. Versuche es noch einmal.", - "addon.mod_choice.errorgetchoice": "Fehler beim Laden der Abstimmung", - "addon.mod_choice.previewonly": "Diese Vorschau zeigt die verfügbaren Optionen für diese Aktivität. Du kannst deine Wahl nicht vor {{$a}} einreichen.", - "addon.mod_choice.publishinfoanonafter": "Ergebnisse werden nach deiner Antwort ohne Namensnennung veröffentlicht.", - "addon.mod_choice.publishinfofullafter": "Ergebnisse werden nach deiner Antwort vollständig mit Namensnennung veröffentlicht.", - "addon.mod_choice.publishinfonever": "Die Ergebnisse werden nach deiner Antwort nicht veröffentlicht.", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% haben die Option gewählt: {{text}}.", - "addon.mod_choice.resultsnotsynced": "Deine letzte Antwort ist in den Ergebnissen noch nicht enthalten. Synchronisiere die Daten.", - "addon.mod_choice.yourselection": "Deine Auswahl", - "addon.mod_data.confirmdeleterecord": "Möchtest du diesen Datensatz in der Datenbank wirklich löschen?", - "addon.mod_data.edittagsnotsupported": "Die Bearbeitung von Tags wird in der App nicht unterstützt.", - "addon.mod_data.emptyaddform": "Du hast keine Einträge vorgenommen!", - "addon.mod_data.entrieslefttoadd": "Du musst {{$a.entriesleft}} weitere Einträge vornehmen, um diese Aktivität zu beenden.", - "addon.mod_data.entrieslefttoaddtoview": "Du musst {{$a.entrieslefttoview}} weitere Einträge vornehmen, bevor du andere Teilnehmerbeiträge anschauen darfst.", - "addon.mod_data.errorapproving": "Fehler beim Freigeben bzw. Ablehnen des Eintrags", - "addon.mod_data.errordeleting": "Fehler beim Löschen des Eintrags", - "addon.mod_data.errormustsupplyvalue": "Du musst hier einen Wert eintragen.", - "addon.mod_data.gettinglocation": "Geodaten holen ...", - "addon.mod_data.locationpermissiondenied": "Die Berechtigung zum Zugriff auf deinen Standort wurde verweigert.", - "addon.mod_data.mylocation": "Meine Standort", - "addon.mod_data.searchbytagsnotsupported": "Die Suche nach Tags wird in der App nicht unterstützt.", - "addon.mod_feedback.captchaofflinewarning": "Ein Feedback mit Captcha kann offline nicht beendet werden. Captcha funktioniert nur, wenn der Server antworten kann.", - "addon.mod_feedback.complete_the_form": "Fragebogen ausfüllen", - "addon.mod_feedback.feedback_submitted_offline": "Das Feedback wurde gespeichert, um es später zu übertragen.", - "addon.mod_feedback.this_feedback_is_already_submitted": "Du hast diese Aktivität bereits beendet.", - "addon.mod_folder.emptyfilelist": "Keine Dateien", - "addon.mod_forum.cannotadddiscussionall": "Du darfst kein neues Thema für alle Teilnehmer/innen anlegen.", - "addon.mod_forum.couldnotadd": "Dein Beitrag wurde nicht abgeschickt.", - "addon.mod_forum.couldnotupdate": "Der Beitrag wurde nicht geändert.", - "addon.mod_forum.cutoffdatereached": "Der letzte Abgabetermin für dieses Forum ist erreicht. Du kannst keinen Beitrag mehr schreiben.", - "addon.mod_forum.deletesure": "Möchtest du diesen Beitrag wirklich löschen?", - "addon.mod_forum.discussionlocked": "Dieses Thema wurde gesperrt. Du kannst hier nicht weiter antworten.", - "addon.mod_forum.erroremptymessage": "Du hast keinen Text geschrieben.", - "addon.mod_forum.erroremptysubject": "Du hast den Betreff vergessen.", - "addon.mod_forum.errorgetforum": "Fehler beim Laden der Forumsdaten", - "addon.mod_forum.errorgetgroups": "Fehler beim Laden der Gruppeneinstellungen", - "addon.mod_forum.errorposttoallgroups": "Das neue Thema konnte nicht in allen Gruppen angelegt werden.", - "addon.mod_forum.favouriteupdated": "Deine Markierungsoption wurde aktualisiert.", - "addon.mod_forum.forumnodiscussionsyet": "Keine Themen im Forum", - "addon.mod_forum.group": "Gruppe", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} Themen", - "addon.mod_forum.numreplies": "{{numreplies}} Antworten", - "addon.mod_forum.refreshdiscussions": "Themen aktualisieren", - "addon.mod_forum.refreshposts": "Forenbeiträge aktualisieren", - "addon.mod_glossary.browsemode": "Einträge durchblättern", - "addon.mod_glossary.byalphabet": "Alphabetisch", - "addon.mod_glossary.byauthor": "Nach Autor/in", - "addon.mod_glossary.bycategory": "Nach Kategorie", - "addon.mod_glossary.bynewestfirst": "Neu zuerst", - "addon.mod_glossary.byrecentlyupdated": "Kürzlich aktualisiert", - "addon.mod_glossary.bysearch": "Suchen", - "addon.mod_glossary.cannoteditentry": "Eintrag nicht bearbeitbar", - "addon.mod_glossary.entriestobesynced": "Einträge zum Synchronisieren", - "addon.mod_glossary.entrypendingapproval": "Dieser Eintrag wartet auf eine Freigabe.", - "addon.mod_glossary.errorloadingentries": "Fehler beim Laden der Einträge", - "addon.mod_glossary.errorloadingentry": "Fehler beim Laden des Eintrags", - "addon.mod_glossary.errorloadingglossary": "Fehler beim Laden des Glossars", - "addon.mod_glossary.noentriesfound": "Keine Einträge", - "addon.mod_glossary.searchquery": "Suchanfrage", - "addon.mod_h5pactivity.downloadh5pfile": "H5P-Datei herunterladen", - "addon.mod_h5pactivity.errorgetactivity": "Fehler beim Laden der H5P-Aktivitätsdaten", - "addon.mod_h5pactivity.filestatenotdownloaded": "Das H5P-Paket ist nicht heruntergeladen. Sie müssen es zuerst herunterladen, um es nutzen zu können.", - "addon.mod_h5pactivity.filestateoutdated": "Das H5P-Paket wurde nach Ihrem letzten Herunterladen verändert. Du musst es zuerst herunterladen, um es nutzen zu können.", - "addon.mod_h5pactivity.offlinedisabledwarning": "Du musst online sein, um das H5P-Paket anzeigen zu können.", - "addon.mod_h5pactivity.viewattempt": "Versuch {{$a}} anzeigen", - "addon.mod_imscp.showmoduledescription": "Beschreibung anzeigen", - "addon.mod_lesson.congratulations": "Herzlichen Glückwunsch! Du hast das Ende der Lektion erreicht.", - "addon.mod_lesson.defaultessayresponse": "Deine Freitext-Antwort wird später bewertet.", - "addon.mod_lesson.displayscorewithessays": "

      Du hast bisher für die automatisch bewerteten Fragen die Bewertung {{$a.score}} von {{$a.tempmaxgrade}} erzielt.

      \n

      Deine Freitext-Aufgaben ({{$a.essayquestions}}) werden später bewertet und zur Gesamtbewertung hinzugefügt.

      \n

      Deine derzeitige Bewertung ohne die Freitextaufgaben ist {{$a.score}} von {{$a.grade}}.

      ", - "addon.mod_lesson.displayscorewithoutessays": "Deine Bewertung: {{$a.score}} von {{$a.grade}}", - "addon.mod_lesson.enterpassword": "Gib bitte das Kennwort ein:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Du hast keine Fragen beantwortet. Du erhältst deswegen 0 Punkte für die Lektion.", - "addon.mod_lesson.errorprefetchrandombranch": "Diese Lektion enthält einen Sprung zu einer zufälligen Seite. Die Lektion kann in der App nicht versucht werden, ohne im Webbrowser begonnen worden zu sein.", - "addon.mod_lesson.errorreviewretakenotlast": "Dieser Versuch kann nicht mehr angesehen werden, weil ein weiterer Versuch beendet wurde.", - "addon.mod_lesson.finishretakeoffline": "Dieser Versuch wurde offline beendet.", - "addon.mod_lesson.firstwrong": "Ihre Antwort ist falsch. Möchtest du es noch einmal (ohne Bewertung) versuchen?", - "addon.mod_lesson.leftduringtimed": "Du hast die Lektion abgebrochen. Die Bearbeitung der Lektion ist zeitlich begrenzt.
      Klicke auf Fortsetzen, um die Lektion erneut zu beginnen.", - "addon.mod_lesson.leftduringtimednoretake": "Du hast die Lektion abgebrochen. Die Bearbeitung der Lektion ist zeitlich begrenzt.
      Du kannst diese Lektion nicht weiter bearbeiten.", - "addon.mod_lesson.loginfail": "Der Login ist gescheitert. Versuche es bitte noch einmal.", - "addon.mod_lesson.maximumnumberofattemptsreached": "Du hast die Höchstzahl der Versuche erreicht - weiter zur nächsten Seite", - "addon.mod_lesson.noanswer": "Eine oder mehrere Fragen wurden nicht beantwortet. Gehe zurück und gib Antworten ein.", - "addon.mod_lesson.progressbarteacherwarning2": "Die Fortschrittsanzeige wird dir nicht angezeigt, weil du diese Lektion als Trainer/in bearbeiten kannst.", - "addon.mod_lesson.progresscompleted": "Du hast {{$a}}% der Lektion erledigt.", - "addon.mod_lesson.retakefinishedinsync": "Ein Offline-Versuch wurde synchronisiert. Möchtest du ihn erneut überprüfen?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.secondpluswrong": "Nicht ganz. Möchtest du es noch einmal versuchen?", - "addon.mod_lesson.teacherjumpwarning": "In der Lektion werden '{{$a.cluster}}'-Sprünge und/oder '{{$a.unseen}}'-Sprünge verwendet. Diese Sprünge werden durch 'Nächste Seite'-Sprünge ersetzt. Melde dich als Teilnehmer/in an, um die Sprünge zu testen.", - "addon.mod_lesson.teacherongoingwarning": "Die aktuelle Bewertung wird nur für Teilnehmer/innen angezeigt. Melde dich als Teilnehmer/in an, um diese Funktion zu testen.", - "addon.mod_lesson.teachertimerwarning": "Die Zeitbegrenzung funktioniert nur für Teilnehmer/innen. Melde dich als Teilnehmer/in an, um diese Funktion zu testen.", - "addon.mod_lesson.warningretakefinished": "Der Versuch wurde auf der Website beendet.", - "addon.mod_lesson.youhaveseen": "Du hast einige Seiten der Lektion schon einmal bearbeitet.
      Möchtest du an der Stelle weitermachen, an der du damals aufgehört hast?", - "addon.mod_lesson.youranswer": "Deine Antwort", - "addon.mod_lesson.yourcurrentgradeisoutof": "Deine derzeitige Bewertung ist {{$a.grade}} von {{$a.total}}", - "addon.mod_lti.errorgetlti": "Fehler beim Laden der Moduldaten", - "addon.mod_lti.errorinvalidlaunchurl": "Die Launch-URL ist ungültig.", - "addon.mod_lti.launchactivity": "Aktivität starten", - "addon.mod_page.errorwhileloadingthepage": "Fehler beim Laden des Seiteninhalts", - "addon.mod_quiz.canattemptbutnotsubmit": "Du kannst einen Testversuch in der App beginnen, aber du musst ihn aus folgenden Gründen im Webbrowser abgeben:", - "addon.mod_quiz.cannotsubmitquizdueto": "Der Test kann aus folgenden Gründen nicht abgegeben werden:", - "addon.mod_quiz.confirmclose": "Sobald du diesen Versuch beendest, kannst du deine Antworten nicht mehr bearbeiten.", - "addon.mod_quiz.confirmcontinueoffline": "Dieser Versuch wurde seit {{$a}} nicht synchronisiert. Falls du inzwischen mit einem anderen Gerät weiter gearbeitet hast, könnten Daten verloren gehen.", - "addon.mod_quiz.confirmleavequizonerror": "Fehler beim Speichern der Antworten. Möchtest du den Test wirklich verlassen?", - "addon.mod_quiz.confirmstart": "Der Test hat eine Zeitbegrenzung von {{$a}}. Die Zeit wird ab dem Start des Versuchs heruntergezählt. Der Test muss vor dem Ende der Zeitbegrenzung abgeschlossen sein. Möchten du den Test wirklich jetzt beginnen?", - "addon.mod_quiz.connectionerror": "Netzwerkverbindung verloren. Das automatische Speichern ist fehlgeschlagen.\n\nNotiere dir alle in den letzten Minuten eingegebenen Antworten und versuche, die Verbindung wieder herzustellen.\n\nSobald die Verbindung wiederhergestellt wurde, solltest du die Antworten erneut speichern. Diese Nachricht verschwindet dann.", - "addon.mod_quiz.errorbehaviournotsupported": "Der Test kann nicht in der App durchgeführt werden, weil das Frageverhalten nicht unterstützt wird:", - "addon.mod_quiz.errordownloading": "Fehler beim Laden notwendiger Daten", - "addon.mod_quiz.errorgetattempt": "Fehler beim Laden der Versuche", - "addon.mod_quiz.errorgetquestions": "Fehler beim Laden der Fragen", - "addon.mod_quiz.errorgetquiz": "Fehler beim Laden der Testdaten", - "addon.mod_quiz.errorparsequestions": "Fehler beim Darstellen der Fragen. Versuche, diesen Test in einem Browser zu öffnen.", - "addon.mod_quiz.errorquestionsnotsupported": "Dieser Test wird in der App nicht ausgeführt, weil er von der App nicht unterstützte Fragen enthält:", - "addon.mod_quiz.errorrulesnotsupported": "Dieser Test wird in der App nicht ausgeführt, weil er von der App nicht unterstützte Zugriffsregeln enthält:", - "addon.mod_quiz.errorsaveattempt": "Fehler beim Speichern des Versuchs", - "addon.mod_quiz.finishnotsynced": "Beendet, aber nicht synchronisiert", - "addon.mod_quiz.noreviewattempt": "Du darfst diesen Versuch nicht erneut prüfen.", - "addon.mod_quiz.opentoc": "Navigationsfeld öffnen", - "addon.mod_quiz.overduemustbesubmittedby": "Der Versuch ist überfällig und sollte bereits abgegeben worden sein. Wenn du möchtest, dass dein Testversuch bewertet wird, musst du ihn abgeben bis {{$a}}. Falls du dies nicht tust, wird keine Bewertung für diesen Versuch gezählt.", - "addon.mod_quiz.requirepasswordmessage": "Zur Teilnahme am Test benötigst du ein Kennwort.", - "addon.mod_quiz.warningattemptfinished": "Der Offline-Versuch wurde verworfen, weil er auf der Website beendet wurde oder nicht mehr existiert.", - "addon.mod_quiz.warningdatadiscarded": "Einige Offline-Antworten könnten verworfen worden sein, weil die Fragen online verändert wurden.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Der Versuch wurde nicht beendet, weil Offline-Antworten verworfen wurden. Überprüfe deine Antworten und übermittle den Versuch noch einmal.", - "addon.mod_quiz.warningquestionsnotsupported": "Dieser Test enthält Fragen, die nicht von der App unterstützt werden:", - "addon.mod_quiz.yourfinalgradeis": "Deine Gesamtbewertung für diesen Test: {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Fehler beim Laden des Inhalts", - "addon.mod_resource.openthefile": "Datei öffnen", - "addon.mod_scorm.cannotcalculategrade": "Die Bewertung konnte nicht berechnet werden.", - "addon.mod_scorm.dataattemptshown": "Diese Daten gehören zu Versuch {{number}}.", - "addon.mod_scorm.errorcreateofflineattempt": "Fehler beim Hinzufügen eines Offline-Versuchs. Versuche es noch einmal.", - "addon.mod_scorm.errordownloadscorm": "Fehler beim Laden des Lernpakets '{{name}}'.", - "addon.mod_scorm.errorgetscorm": "Fehler beim Laden von Lernpaketdaten", - "addon.mod_scorm.errorinvalidversion": "Die App unterstützt nur Lernpakete mit SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Das Herunterladen von Lernpaketen ist für diese Website deaktiviert. Frage den Administrator.", - "addon.mod_scorm.errornovalidsco": "Dieses Lernpaket hat kein sichtbares SCO zum Laden.", - "addon.mod_scorm.errorpackagefile": "Die App unterstützt ausschließlich ZIP-Pakete.", - "addon.mod_scorm.errorsyncscorm": "Fehler beim Synchronisieren. Versuche es noch einmal.", - "addon.mod_scorm.exceededmaxattempts": "Du hast die maximale Anzahl von Versuchen erreicht.", - "addon.mod_scorm.offlineattemptnote": "Dieser Versuch enthält Daten, die noch nicht synchronisiert wurden.", - "addon.mod_scorm.offlineattemptovermax": "Dieser Versuch kann nicht gesendet werden, weil du die maximale Anzahl von Versuchen überschritten hast.", - "addon.mod_scorm.scormstatusnotdownloaded": "Dieses Lernpaket ist noch nicht geladen. Es wird automatisch heruntergeladen, wenn du es öffnst.", - "addon.mod_scorm.scormstatusoutdated": "Dieses Lernpaket wurde seit dem Herunterladen verändert. Es wird automatisch geladen, wenn du es öffnst.", - "addon.mod_scorm.warningofflinedatadeleted": "Die Offline-Daten von Versuch {{number}} wurden gelöscht, weil sie nicht als neuer Versuch gezählt werden können.", - "addon.mod_scorm.warningsynconlineincomplete": "Einige Versuche konnten nicht synchronisiert werden, weil der letzte Online-Versuch nicht beendet wurde. Beende zuerst den Online-Versuch.", - "addon.mod_survey.cannotsubmitsurvey": "Fehler beim Übertragen der Umfragedaten. Versuche es noch einmal.", - "addon.mod_survey.errorgetsurvey": "Fehler beim Laden der Umfragedaten", - "addon.mod_survey.results": "Ergebnisse", - "addon.mod_url.accessurl": "URL öffnen", - "addon.mod_url.pointingtourl": "Verlinkte URL", - "addon.mod_wiki.errorloadingpage": "Fehler beim Laden der Seite", - "addon.mod_wiki.errornowikiavailable": "Dieses Wiki hat noch keinen Inhalt.", - "addon.mod_wiki.gowikihome": "Zur Wiki-Startseite", - "addon.mod_wiki.subwiki": "Teilwiki", - "addon.mod_wiki.titleshouldnotbeempty": "Der Titel darf nicht leer sein.", - "addon.mod_wiki.viewpage": "Seite anzeigen", - "addon.mod_wiki.wikipage": "Wiki-Seite", - "addon.mod_workshop.assessmentstrategynotsupported": "Die Beurteilungsstrategie {{$a}} wird nicht unterstützt.", - "addon.mod_workshop.submissionrequiredtitle": "Du musst einen Titel eingeben.", - "addon.mod_workshop.warningassessmentmodified": "Die Abgabe wurde auf der Website geändert.", - "addon.mod_workshop.warningsubmissionmodified": "Die Beurteilung wurde auf der Website geändert.", - "addon.notes.userwithid": "Nutzer/in mit ID {{id}}", - "addon.notes.warningnotenotsent": "Die Anmerkungen konnten nicht zum Kurs {{course}} hinzugefügt werden. {{error}}", - "addon.notifications.errorgetnotifications": "Fehler beim Empfangen von Systemnachrichten", - "addon.notifications.notifications": "Systemnachrichten", - "addon.notifications.playsound": "Signalton abspielen", - "addon.notifications.therearentnotificationsyet": "Keine Systemnachrichten", - "addon.storagemanager.deletecourse": "Alle Kursdaten entladen", - "addon.storagemanager.deletecourses": "Alle Kursdaten entladen", - "addon.storagemanager.deletedatafrom": "Daten von {{name}} entladen", - "addon.storagemanager.info": "Auf deinem Gerät gespeicherte Daten beschleunigen die Arbeit und ermöglichen die Offline-Verwendung der App. Du kannst Daten entladen, wenn du Speicherplatz freigeben musst.", - "addon.storagemanager.managestorage": "Speicher verwalten", - "addon.storagemanager.storageused": "Verwendeter Dateispeicher:", - "core.areyousure": "Bist du sicher?", - "core.browser": "Browser", - "core.cannotconnect": "Keine Verbindung", - "core.cannotconnecttrouble": "Wir haben Schwierigkeiten, eine Verbindung zu deiner Website herzustellen.", - "core.cannotconnectverify": "Prüfe bitte, ob die Adresse richtig ist.", - "core.cannotdownloadfiles": "Das Herunterladen von Dateien ist deaktiviert. Frage den Administrator.", - "core.captureaudio": "Audio aufnehmen", - "core.capturedimage": "Foto aufgenommen", - "core.captureimage": "Foto aufnehmen", - "core.capturevideo": "Video aufnehmen", - "core.clearsearch": "Suche löschen", - "core.clicktoseefull": "Tippe zum Anzeigen aller Inhalte", - "core.comments.commentsnotworking": "Kommentare können nicht abgerufen werden.", - "core.comments.warningcommentsnotsent": "Kommentare konnten nicht synchronisiert werden. {{error}}", - "core.confirmcanceledit": "Möchtest du diese Seite wirklich verlassen? Alle Änderungen gehen verloren!", - "core.confirmdeletefile": "Möchtest du diese Datei wirklich löschen?", - "core.confirmgotabroot": "Möchtest du wirklich zu {{name}} zurückgehen?", - "core.confirmgotabrootdefault": "Möchtest du wirklich zur initalen Seite des aktuellen Tabs zurückkehren?", - "core.confirmleaveunknownchanges": "Möchtest du diese Seite wirklich verlassen? Wenn du ungesicherte Änderungen hast, gehen diese verloren.", - "core.confirmloss": "Möchtest du wirklich alle Änderungen verlieren?", - "core.confirmopeninbrowser": "Möchtest du dies im Webbrowser öffnen?", - "core.considereddigitalminor": "Du bist zu jung, um ein Nutzerkonto für diese Website zu erstellen.", - "core.contenteditingsynced": "Der Inhalt, den du gerade bearbeitest, wurde synchronisiert.", - "core.contentlinks.chooseaccount": "Nutzerkonto wählen", - "core.contentlinks.chooseaccounttoopenlink": "Nutzerkonto wählen, mit dem der Link geöffnet werden soll.", - "core.contentlinks.confirmurlothersite": "Dieser Link führt zu einer anderen Website. Möchtest du den Link öffnen?", - "core.contentlinks.errornoactions": "Keine Aktion zu diesem Link gefunden", - "core.contentlinks.errornosites": "Keine Website zu diesem Link gefunden", - "core.contentlinks.errorredirectothersite": "Die URL-Weiterleitung kann nicht auf eine andere Website zeigen.", - "core.copiedtoclipboard": "Text in die Zwischenablage kopiert", - "core.copytoclipboard": "In die Zwischenablage kopieren", - "core.course.activitydisabled": "Für die Website ist diese Aktivität in der mobilen App deaktiviert.", - "core.course.activitynotyetviewableremoteaddon": "Für die Website ist ein Plugin installiert, das bisher nicht unterstützt wird.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Diese Website muss aktualisiert werden.", - "core.course.allsections": "Alle Abschnitte", - "core.course.askadmintosupport": "Frage den Administrator der Website, um diese Aktivität mit der mobilen App verwenden zu können.", - "core.course.availablespace": "Du hast im Moment etwa {{available}} freien Speicher.", - "core.course.cannotdeletewhiledownloading": "Dateien können nicht gelöscht werden, während die Aktivität heruntergeladen wird. Warte, bis das Herunterladen beendet ist.", - "core.course.confirmdeletemodulefiles": "Möchtest du diese Dateien wirklich löschen?", - "core.course.confirmdownload": "Möchtest du wirklich {{size}} herunterladen? {{availableSpace}}", - "core.course.confirmdownloadunknownsize": "Die Datenmenge kann nicht berechnet werden. Möchtest du wirklich die Daten herunterladen? {{availableSpace}}", - "core.course.confirmdownloadzerosize": "Möchtest du wirklich die Daten herunterladen? {{availableSpace}}", - "core.course.confirmlimiteddownload": "Du bist im Moment nicht über WLAN verbunden.", - "core.course.confirmpartialdownloadsize": "Sie sind dabei, mindestens {{size}} herunterzuladen. {{availableSpace}} Möchtest du wirklich weitermachen?", - "core.course.contents": "Inhalte", - "core.course.couldnotloadsectioncontent": "Die Abschnittsinhalte konnten nicht geladen werden. Versuche es später noch einmal.", - "core.course.couldnotloadsections": "Die Abschnitte konnten nicht geladen werden. Versuche es später noch einmal.", - "core.course.errordownloadingcourse": "Fehler beim Herunterladen des Kurses", - "core.course.errordownloadingsection": "Fehler beim Herunterladen des Abschnitts", - "core.course.errorgetmodule": "Fehler beim Laden von Aktivitätsdaten", - "core.course.insufficientavailablequota": "Dein Gerät konnte für diesen Download keinen Speicherplatz zuweisen. Möglicherweise ist Speicherplatz für anstehende App- oder Systemupdates reserviert. Sorge zuerst für ausreichend freien Speicher.", - "core.course.insufficientavailablespace": "Du versuchst, {{size}} herunterzuladen. Dein Gerät hat danach möglicherweise nicht genügend Platz, um normal zu funktionieren. Sorge zuerst für ausreichend freien Speicher.", - "core.course.manualcompletionnotsynced": "Manueller Abschluss wurde nicht synchronisiert", - "core.course.nocontentavailable": "Momentan sind keine Inhalte verfügbar", - "core.course.refreshcourse": "Kurs neu laden", - "core.course.useactivityonbrowser": "Du kannst dies im Webbrowser verwenden.", - "core.course.warningmanualcompletionmodified": "Manueller Abschluss einer Aktivität wurde auf der Website geändert.", - "core.course.warningofflinemanualcompletiondeleted": "Manueller Offline-Abschluss des Kurses '{{name}}' wurde gelöscht. {{error}}", - "core.coursenogroups": "Du bist nicht Mitglied einer Gruppe in diesem Kurs.", - "core.courses.cannotretrievemorecategories": "Kursbereiche tiefer als Level {{$a}} können nicht abgerufen werden.", - "core.courses.confirmselfenrol": "Möchtest du dich selbst in diesen Kurs einschreiben?", - "core.courses.downloadcourses": "Kurse herunterladen", - "core.courses.enrolme": "Selbst einschreiben", - "core.courses.errorloadcategories": "Fehler beim Laden von Kursbereichen", - "core.courses.errorloadcourses": "Fehler beim Laden von Kursen", - "core.courses.errorloadplugins": "Für diesen Kurs notwendige Plugins konnten nicht richtig geladen werden. Starte die App neu und versuche es noch einmal.", - "core.courses.errorsearching": "Fehler beim Suchen", - "core.courses.errorselfenrol": "Fehler bei der Selbsteinschreibung", - "core.courses.filtermycourses": "Meine Kurse filtern", - "core.courses.nocourses": "Keine Kursinformation", - "core.courses.notenroled": "Du bist nicht in diesen Kurs eingeschrieben", - "core.courses.notenrollable": "Du kannst dich nicht selbst in diesen Kurs einschreiben.", - "core.courses.password": "Einschreibeschlüssel", - "core.courses.paymentrequired": "Dieser Kurs ist gebührenpflichtig. Bitte bezahle die Teilnahmegebühr, um im Kurs eingeschrieben zu werden.", - "core.courses.searchcoursesadvice": "Du kannst Kurse suchen, um als Gast teilzunehmen oder dich selbst einzuschreiben, falls dies erlaubt ist.", - "core.courses.selfenrolment": "Selbsteinschreibung", - "core.courses.totalcoursesearchresults": "Alle Kurse: {{$a}}", - "core.currentdevice": "Aktuelles Gerät", - "core.datastoredoffline": "Die Daten sind lokal auf dem Gerät gespeichert, weil sie nicht gesendet werden konnten. Sie werden automatisch später gesendet.", - "core.decsep": ",", - "core.deletedoffline": "Offline gelöscht", - "core.deleting": "Löschen ...", - "core.desktop": "Schreibtisch", - "core.dfdaymonthyear": "DD.MM.YYYY", - "core.dfdayweekmonth": "ddd, D. MMM", - "core.dffulldate": "dddd, D. MMMM YYYY, HH[:]mm", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "HH[:]mm", - "core.digitalminor_desc": "Um ein Nutzerkonto zu erstellen, müssen sich deine Erziehungsberechtigten an folgende Stelle wenden:", - "core.discard": "Verwerfen", - "core.dismiss": "Abbrechen", - "core.downloaded": "Heruntergeladen", - "core.downloading": "Herunterladen ...", - "core.editor.hidetoolbar": "Leiste verbergen", - "core.editor.toggle": "Editor umschalten", - "core.emptysplit": "Das Seitenmenü ist leer oder wird noch geladen ...", - "core.errorchangecompletion": "Fehler beim Ändern des Abschlussstatus. Versuche es noch einmal.", - "core.errordeletefile": "Fehler beim Löschen der Datei. Versuche es noch einmal.", - "core.errordownloading": "Fehler beim Laden der Datei", - "core.errordownloadingsomefiles": "Fehler beim Laden der Dateien. Einige Dateien könnten fehlen.", - "core.errorfileexistssamename": "Eine Datei mit gleichem Namen existiert bereits.", - "core.errorinvalidform": "Das Formular enthält ungültige Daten. Fülle alle notwendigen Felder aus und prüfe, dass alle Daten richtig sind.", - "core.errorinvalidresponse": "Ungültige Antwort empfangen. Frage den Administrator, wenn der Fehler wieder auftritt.", - "core.errorloadingcontent": "Fehler beim Laden des Inhalts", - "core.errorofflinedisabled": "Der Offline-Zugriff auf diese Website ist deaktiviert. Du musst online sein, um die App zu verwenden.", - "core.erroropenfilenoapp": "Fehler: Keine App zum Öffnen dieses Dateityps gefunden.", - "core.erroropenfilenoextension": "Fehler beim Öffnen: Die Datei hat keine Endung.", - "core.erroropenpopup": "Die Aktivität versucht, ein Popup zu öffnen. Popups werden in der App aber nicht unterstützt.", - "core.errorrenamefile": "Fehler beim Ändern des Dateinamens. Versuche es noch einmal.", - "core.errorsomedatanotdownloaded": "Beim Herunterladen dieser Aktivität könnten möglicherweise einige Daten aus Performance- und Datennutzungsgründen ausgenommen worden sein.", - "core.errorsync": "Fehler beim Synchronisieren. Versuche es noch einmal.", - "core.errorsyncblocked": "{{$a}} kann im Moment wegen eines anderen Vorgangs nicht synchronisiert werden. Versuche es später noch einmal. Falls das Problem weiterhin besteht, starte die App neu.", - "core.explanationdigitalminor": "Diese Informationen sind notwendig um festzustellen, ob du dich selber registrieren darfst. Nur wenn du alt genug bist, kannst du selber den Richtlinien und Nutzungsbedingungen zustimmen. Andernfalls müssen dies deine Erziehungsberechtigten tun.", - "core.filenameexist": "Der Dateiname existiert bereits: {{$a}}", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Möchtest du wirklich {{size}} hochladen?", - "core.fileuploader.confirmuploadunknownsize": "Die Datenmenge kann nicht berechnet werden. Möchtest du wirklich hochladen?", - "core.fileuploader.errorcapturingaudio": "Fehler bei der Audioaufnahme", - "core.fileuploader.errorcapturingimage": "Fehler beim Fotografieren", - "core.fileuploader.errorcapturingvideo": "Fehler bei der Videoaufnahme", - "core.fileuploader.errorgettingimagealbum": "Fehler beim Laden aus dem Fotoalbum", - "core.fileuploader.errormustbeonlinetoupload": "Dateien können nur online hochgeladen werden.", - "core.fileuploader.errornoapp": "Keine App zum Ausführen dieser Aktion gefunden", - "core.fileuploader.errorreadingfile": "Fehler beim Lesen der Datei", - "core.fileuploader.errorwhileuploading": "Fehler beim Hochladen der Datei", - "core.fileuploader.file": "Datei", - "core.fileuploader.fileuploaded": "Datei erfolgreich hochgeladen", - "core.fileuploader.maxbytesfile": "Die Datei {{$a.file}} ist zu groß. Die maximale Größe zum Hochladen ist {{$a.size}}.", - "core.fileuploader.photoalbums": "Fotoalbum", - "core.fileuploader.readingfile": "Datei lesen", - "core.fileuploader.readingfileperc": "Datei lesen: {{$a}}%", - "core.fileuploader.selectafile": "Datei wählen", - "core.fileuploader.uploadafile": "Datei hochladen", - "core.fileuploader.uploading": "Hochladen ...", - "core.fileuploader.uploadingperc": "Hochladen: {{$a}}%", - "core.fileuploader.video": "Video", - "core.forcepasswordchangenotice": "Ändere dein Kennwort, bevor du weiterarbeitest.", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.h5p.confirmdialogbody": "Möchtest du wirklich fortfahren? Die Aktion kann nicht zurückgenommen werden.", - "core.h5p.contentchanged": "Dieser Inhalt hat sich seit deiner letzten Verwendung geändert.", - "core.h5p.errorgetemail": "Fehler beim Abrufen der Nutzer-E-Mail-Adresse. Bitte überprüfen Sie Ihre Verbindung und versuchen Sie es erneut.", - "core.h5p.offlineDialogBody": "Infos zum Abschluss dieser Aufgabe konnten nicht gesendet werden. Überprüfe die Internetverbindung.", - "core.h5p.offlineDialogHeader": "Deine Verbindung zum Server wurde unterbrochen.", - "core.h5p.offlinedisabled": "Die Website erlaubt nicht das Herunterladen von H5P-Paketen.", - "core.h5p.play": "H5P abspielen", - "core.h5p.resizescript": "Füge dieses Skript in die Website ein, wenn du eine dynamische Größenanpassung des eingebetteten Inhalts möchtest:", - "core.h5p.startingover": "Du wirst von vorne anfangen.", - "core.hasdatatosync": "Die Offline-Daten von {{$a}} müssen synchronisiert werden.", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Bild", - "core.imageviewer": "Bildanzeige", - "core.labelsep": ":", - "core.lastdownloaded": "Zuletzt heruntergeladen", - "core.lastsync": "Zuletzt synchronisiert", - "core.listsep": ";", - "core.loadmore": "Mehr laden", - "core.login.authenticating": "Authentifizieren ...", - "core.login.changepasswordbutton": "Seite zur Kennwortänderung aufrufen", - "core.login.changepasswordhelp": "Wenn du Probleme beim Ändern deines Kennworts hast, wende dich an den Administrator deiner Website. Administrator/innen sind die Personen, die das Moodle an deiner Schule, Universität, Firma oder Organisation verwalten. Wenn du nicht weißt, wie du mit ihnen Kontakt aufnehmen kannst, wende dich an deine Trainer/innen.", - "core.login.changepasswordinstructions": "Du kannst dein Kennwort nicht in der App ändern. Klicke auf die folgende Taste, um Moodle in einem Webbrowser zu öffnen und dein Kennwort zu ändern. Beachte, dass du den Browser nach dem Ändern des Kennworts schließen musst, um wieder in die App zurückzukommen.", - "core.login.changepasswordlogoutinstructions": "Wenn du die Website wechseln oder dich abmelden möchtest, klicke auf die folgende Taste:", - "core.login.changepasswordreconnectinstructions": "Klicke auf die folgende Taste, um die Website neu zu verbinden. Wenn du das Kennwort nicht erfolgreich änderst, öffnet sich wieder der vorherige Bildschirm.", - "core.login.confirmdeletesite": "Möchtest du '{{sitename}}' wirklich aus der Liste löschen?", - "core.login.connect": "Verbinden", - "core.login.connecttomoodle": "Zu Moodle verbinden", - "core.login.connecttomoodleapp": "Du versuchst, dich mit einer normalen Moodle-Website zu verbinden. Verwende die offizielle Moodle-App, um auf diese Website zu gelangen.", - "core.login.connecttoworkplaceapp": "Du versuchst, sich mit einer Moodle-Workplace-Website zu verbinden. Verwende die Moodle-Workplace-App, um auf diese Website zu gelangen.", - "core.login.contactyouradministrator": "Frage den Administrator, um weitere Hilfe zu bekommen.", - "core.login.contactyouradministratorissue": "Frage den Administrator, um folgendes Problem prüfen zu lassen: {{$a}}", - "core.login.createuserandpass": "Wähle deinen Anmeldenamen und dein Kennwort", - "core.login.credentialsdescription": "Gib den Anmeldenamen und das Kennwort ein. ", - "core.login.emailconfirmsent": "

      Um sicherzugehen, dass sich niemand unberechtigt über die von dir angegebene E-Mail anmeldet, wird eine automatische Benachrichtigung an diese Adresse {{$a}} gesendet.

      \n

      Die Benachrichtigung enthält eine Anleitung, wie du deine Registrierung bestätigst. Danach bist du auf dieser Moodle-Seite registriert und kannst sofort loslegen.

      \n

      Bei Problemen wende dich bitte an die Administrator/innen der Website.

      ", - "core.login.emailconfirmsentnoemail": "

      An deine E-Mail-Adresse wurde eine Mitteilung gesendet.

      Die Mitteilung enthält eine einfache Anleitung, um deine Registrierung abzuschließen. Falls du Probleme hast, frage den Administrator der Website.

      ", - "core.login.emailnotmatch": "Die E-Mail-Adressen stimmen nicht überein.", - "core.login.erroraccesscontrolalloworigin": "Der Cross-Origin.Aufruf wurde zurückgewiesen. Weitere Infos: https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Fehler beim Entfernen der Website aus der Liste. Versuche es noch einmal.", - "core.login.errorexampleurl": "Die URL https://campus.example.edu ist eine Beispiel-URL, die nicht zu einer echten Website gehört. Verwende die URL für die Website deiner Schule oder deiner Organisation.", - "core.login.errorupdatesite": "Fehler bei der Authentifizierung", - "core.login.faqcannotconnectanswer": "Wende dich bitte an den Administrator.", - "core.login.faqcannotconnectquestion": "Ich habe meine Website-Adresse richtig eingegeben, aber ich kann mich nicht verbinden.", - "core.login.faqcannotfindmysiteanswer": "Hast du den Namen richtig eingegeben? Wenn du ihn nicht finden kannst, gib bitte stattdessen die Adresse der Website ein. Nicht alle Moodle-Websites sind im Verzeichnis der öffentlichen Websites verfügbar.", - "core.login.faqcannotfindmysitequestion": "Ich kann meine Website nicht finden.", - "core.login.faqsetupsiteanswer": "Besuche {{$link}}, um die verschiedenen Optionen zum Erstellen deiner eigenen Moodle-Website zu prüfen.", - "core.login.faqsetupsitelinktitle": "Leg los.", - "core.login.faqsetupsitequestion": "Ich möchte meine eigene Moodle-Website aufsetzen.", - "core.login.faqtestappanswer": "Um die App mit einer Demo-Website zu testen, gib im Feld \"Deine Website-Adresse\" entweder \"teacher\" oder \"student\" ein und tippe auf \"Verbinden!\"", - "core.login.faqtestappquestion": "Ich möchte die App testen. Was kann ich machen?", - "core.login.faqwhatisurlanswer": "

      Jede Schule und jede Institution hat eine eigene Adresse für ihre Moodle-Website.

      Um die Adresse zu finden, zu der du eine Verbindung herstellen möchtest, mache Folgendes:

      \n
      1. Öffne deinen Browser und rufe die Anmeldeseite der Moodle-Website deiner Schule oder Organisation auf.
      2. Oben in der Adressleiste siehst du die URL, z.B. \"campus.example.edu\".
        {{$image}}
      3. Kopiere die Adresse (ohne /login und was danach kommt), füge die Adresse in die Moodle App ein und tippe auf \"Verbinden!\"
      4. Jetzt kannst du dich mit deinem Anmeldenamen und deinem Kennwort anmelden.
      5. ", - "core.login.faqwhatisurlquestion": "Wie lautet die URL meiner Moodle-Website? Wie kann ich die Website meiner Schule finden?", - "core.login.faqwhereisqrcode": "Wo kann ich den QR-Code finden?", - "core.login.faqwhereisqrcodeanswer": "

        Wenn deine Schule oder Organisation dies aktiviert hat, findest du einen QR-Code auf der Seite mit deinem Nutzerprofil.

        {{$image}}", - "core.login.findyoursite": "Website suchen", - "core.login.firsttime": "Bist du zum ersten Mal auf dieser Webseite?", - "core.login.forcepasswordchangenotice": "Ändere dein Kennwort, bevor du weiterarbeitest.", - "core.login.helpmelogin": "

        Auf der Welt gibt es viele tausend Websites mit Moodle. Diese App kann aber nur mit Websites verbunden werden, die für den Zugriff mit der mobilen Moodle App freigegeben wurden.

        Falls du dich nicht mit deiner Website verbinden kannst, frage den Administrator der Website und bitte ihn, die Anleitung zu lesen. https://docs.moodle.org/de/Moodle_App

        Um die App mit einer Demoseite auszuprobieren, schreibe teacher oder student in das Feld Website-Adresse und tippe dann auf Verbinden.

        ", - "core.login.invalidaccount": "Prüfe deine Anmeldedaten oder frage den Administrator der Website.", - "core.login.invaliddate": "Ungültiges Datum", - "core.login.invalidmoodleversion": "

        Falsche Moodle-Version. Die Moodle-App unterstützt nur Moodle-Systeme ab {{$a}}.

        \n

        Wende dich an den Administrator deiner Website, damit dein Moodle-System aktualisiert wird. Administrator/innen sind die Personen, die das Moodle an deiner Schule, Universität, Firma oder Organisation verwalten. Wenn du nicht weißt, wie du mit ihnen in Kontakt treten sollst, wende dich an deine Trainer/innen.

        ", - "core.login.invalidsite": "Die URL der Website ist ungültig.", - "core.login.invalidtime": "Ungültige Zeitangabe", - "core.login.invalidvaluemax": "Der Maximalwert ist {{$a}}.", - "core.login.invalidvaluemin": "Der Minimalwert ist {{$a}}.", - "core.login.localmobileunexpectedresponse": "Die Verbindung zum Plugin 'Moodle Mobile - Zusatzfeatures' ist fehlgeschlagen. Du wirst über den standardmäßigen mobilen Webservice authentifiziert.", - "core.login.loggedoutssodescription": "Du musst dich neu authentifizieren. Melde dich im Browser auf der Website an.", - "core.login.loginbutton": "Anmelden", - "core.login.logininsiterequired": "Du musst dich für diese Website im Browser anmelden.", - "core.login.loginsteps": "Für den vollen Zugriff auf die Website brauchst du ein Nutzerkonto.", - "core.login.mobileservicesnotenabled": "Der mobile Zugriff ist für diese Website nicht aktiviert. Frage den Administrator, wenn du die mobile App aktiviert haben möchtest.", - "core.login.mustconfirm": "Du musst deinen Zugang bestätigen", - "core.login.notloggedin": "Du musst angemeldet sein.", - "core.login.onboardingcreatemanagecourses": "Deine Kurse anlegen und verwalten", - "core.login.onboardingenrolmanagestudents": "Deine Teilnehmer/innen einschreiben und verwalten", - "core.login.onboardinggetstarted": "Beginne mit Moodle", - "core.login.onboardingialreadyhaveasite": "Ich habe bereits eine Moodle-Website.", - "core.login.onboardingimalearner": "Ich bin ein Lernender.", - "core.login.onboardingimaneducator": "Ich bin eine Lehrkraft.", - "core.login.onboardingineedasite": "Ich brauche eine Moodle-Website.", - "core.login.onboardingprovidefeedback": "Zeitnahes Feedback geben", - "core.login.onboardingtoconnect": "Um eine Verbindung zur Moodle-App herzustellen, benötigst du eine Moodle-Website", - "core.login.onboardingwelcome": "Willkommen bei der Moodle-App!", - "core.login.or": "ODER", - "core.login.passwordforgotteninstructions2": "Um dein Kennwort zurückzusetzen, trage bitte entweder deinen Anmeldenamen oder deine E-Mail-Adresse ein. Wenn du in der Datenbank zu finden bist, wird eine Mitteilung an deine E-Mail-Adresse verschickt. Diese Mitteilung enthält eine Anleitung für die weiteren Schritte.", - "core.login.passwordrequired": "Kennwort fehlt", - "core.login.policyagree": "Lies diese Datenschutzinfos sorgfältig. Du musst zustimmen, um diese Website weiter nutzen zu können. Stimmst du zu?", - "core.login.policyagreementclick": "Klicke hier, um die Zustimmungserklärung zu lesen.", - "core.login.potentialidps": "Verwende dein Nutzerkonto bei:", - "core.login.recaptchachallengeimage": "reCaptcha Challenge-Bild", - "core.login.recaptchaexpired": "Überprüfung abgelaufen. Beantworte die Sicherheitsfrage noch einmal.", - "core.login.recaptchaincorrect": "Die Antwort auf die Sicherheitsfrage ist falsch.", - "core.login.reconnect": "Neu verbinden", - "core.login.reconnectdescription": "Die Authentifizierung ist abgelaufen oder ungültig. Du musst dich neu anmelden.", - "core.login.reconnectssodescription": "Die Authentifizierung ist abgelaufen oder ungültig. Du musst dich im Webbrowser neu anmelden.", - "core.login.searchby": "Suche nach:", - "core.login.selectsite": "Wähle deine Website", - "core.login.signupplugindisabled": "{{$a}} ist nicht aktiviert.", - "core.login.siteaddress": "Deine Website", - "core.login.sitehasredirect": "Deine Website enthält mindestens eine HTTP-Weiterleitung. Die App kann keine Weiterleitungen verarbeiten und der Verbindungaufbau könnte fehlschlagen.", - "core.login.siteinmaintenance": "Diese Website ist im Wartungsmodus.", - "core.login.sitepolicynotagreederror": "Die Zustimmungserklärung wurde nicht bestätigt.", - "core.login.siteurl": "URL der Website", - "core.login.siteurlrequired": "Die URL der Website ist notwendig, z.B. https://www.meinmoodle.de", - "core.login.stillcantconnect": "Kannst du dich immer noch nicht verbinden?", - "core.login.usernameoremail": "Gib den Anmeldenamen oder die E-Mail-Adresse ein.", - "core.login.usernamerequired": "Anmeldename fehlt", - "core.login.visitchangepassword": "Möchtest du die Website aufrufen, um das Kennwort zu ändern?", - "core.login.webservicesnotenabled": "Deine Website hat möglicherweise keine Webservices aktiviert. Wende dich an Ihren Administrator, um Hilfe zu erhalten.", - "core.login.youcanstillconnectwithcredentials": "du kannst weiterhin eine Verbindung zur Website herstellen, indem du deinen Anmeldenamen und dein Kennwort eingeben.", - "core.login.yourenteredsite": "Zu deiner Website verbinden", - "core.lostconnection": "Die Authentifizierung ist abgelaufen oder ungültig. Du musst dich neu anmelden.", - "core.mainmenu.changesite": "Website wechseln", - "core.mainmenu.website": "Website im Browser", - "core.needhelp": "Brauchst du Hilfe?", - "core.networkerroriframemsg": "Dieser Inhalt ist offline nicht verfügbar. Du musst online sein und es dann noch einmal versuchen.", - "core.networkerrormsg": "Problem mit der Verbindung. Prüfe die Verbindung und versuche es noch einmal.", - "core.nooptionavailable": "Keine Option verfügbar", - "core.nopasswordchangeforced": "Du kannst nicht weitermachen, ohne das Kennwort zu ändern.", - "core.nopermissionerror": "Du hast aktuell keine Rechte, um dies zu tun.", - "core.nopermissions": "Du hast derzeit keine Rechte, dies zu tun ({{$a}}).", - "core.notapplicable": "n/a", - "core.notingroup": "Diese Aktivität ist nur für Gruppenmitglieder zugänglich. Du bist kein Gruppenmitglied.", - "core.notsent": "Nicht gesendet", - "core.nummore": "{{$a}} mehr", - "core.openfullimage": "Tippe, um das Bild zu vergrößern", - "core.openinbrowser": "Im Browser öffnen", - "core.openmodinbrowser": "{{$a}} im Browser öffnen", - "core.parentlanguage": "de", - "core.paymentinstant": "Klicke auf die Taste, um die Teilnahmegebühr zu bezahlen. Sobald der Zahlvorgang abgeschlossen ist, wirst du automatisch in den Kurs eingeschrieben.", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pulltorefresh": "Zum Aktualisieren runterziehen", - "core.qrscanner": "QR-Scanner", - "core.question.cannotdeterminestatus": "Status kann nicht festgestellt werden", - "core.question.errorattachmentsnotsupported": "Die App erlaubt keine Antworten mit Dateianhängen.", - "core.question.errorinlinefilesnotsupported": "Die App unterstützt keine Bearbeitung von integrierten Dateien.", - "core.question.errorquestionnotsupported": "Die App unterstützt diesen Fragetyp nicht: {{$a}}.", - "core.question.howtodraganddrop": "Tippe zum Auswählen und tippe noch einmal zum Ablegen.", - "core.question.questionmessage": "Frage {{$a}}: {{$b}}", - "core.redirectingtosite": "Du wirst zur Website weitergeleitet.", - "core.removefiles": "Dateien entfernen {{$a}}", - "core.requireduserdatamissing": "Im Nutzerprofil fehlen notwendige Einträge. Fülle die Daten in der Website aus und versuche es noch einmal.
        {{$a}}", - "core.retry": "Neu versuchen", - "core.scanqr": "QR-Code scannen", - "core.searching": "Suchen", - "core.seemoredetail": "Klicke hier, um weitere Details sichtbar zu machen", - "core.settings.about": "Über die App", - "core.settings.appsettings": "Einstellungen der App", - "core.settings.appversion": "Version", - "core.settings.cannotsyncoffline": "Offline kann nicht synchronisiert werden.", - "core.settings.cannotsyncwithoutwifi": "Die Daten wurden nicht synchronisiert, weil die Einstellungen dies nur mit WLAN erlauben. Stelle eine WLAN-Verbindung her.", - "core.settings.colorscheme": "Farbschema", - "core.settings.colorscheme-auto": "Automatisch (entsprechend den Systemeinstellungen)", - "core.settings.colorscheme-dark": "Dunkel", - "core.settings.colorscheme-light": "Hell", - "core.settings.compilationinfo": "Compilation", - "core.settings.copyinfo": "Device-Info in die Zwischenablage kopieren", - "core.settings.cordovadevicemodel": "Cordova Device Model", - "core.settings.cordovadeviceosversion": "Cordova Device OS Version", - "core.settings.cordovadeviceplatform": "Cordova Device Platform", - "core.settings.cordovadeviceuuid": "Cordova Device UUID", - "core.settings.cordovaversion": "Cordova Version", - "core.settings.debugdisplaydescription": "Wenn diese Option aktiviert ist, werden zusätzliche Fehlerdaten angezeigt.", - "core.settings.deletesitefiles": "Möchtest du wirklich alle heruntergeladenen Dateien und gespeicherten Daten der Website '{{sitename}}' löschen? Du kannst dann die App nicht mehr offline verwenden.", - "core.settings.deletesitefilestitle": "Dateien löschen", - "core.settings.deviceinfo": "Geräteinformationen", - "core.settings.deviceos": "Geräte-OS", - "core.settings.displayformat": "Bildschirm", - "core.settings.enabledownloadsection": "Abschnitte herunterladen", - "core.settings.enablefirebaseanalytics": "Firebase Analytics aktivieren", - "core.settings.enablefirebaseanalyticsdescription": "Wenn diese Option aktiviert ist, sammelt die App anonymisierte Nutzungsdaten.", - "core.settings.enablerichtexteditor": "Texteditor aktivieren", - "core.settings.enablerichtexteditordescription": "Der Texteditor ist verfügbar, wenn Texte eingegeben werden sollen.", - "core.settings.enablesyncwifi": "Nur mit WLAN synchronierieren", - "core.settings.entriesincache": "{{$a}} Einträge im Cache", - "core.settings.errordeletesitefiles": "Fehler beim Löschen der Dateien", - "core.settings.errorsyncsite": "Fehler beim Synchronisieren der Daten. Du musst online sein und es dann noch einmal versuchen.", - "core.settings.estimatedfreespace": "Verfügbarer Speicher", - "core.settings.filesystemroot": "Dateisystembasis", - "core.settings.fontsize": "Schriftgröße", - "core.settings.forcedsetting": "Diese Einstellung wird durch die Website-Konfiguration erzwungen.", - "core.settings.localnotifavailable": "Lokale Systemnachrichten verfügbar", - "core.settings.locationhref": "Webview URL", - "core.settings.navigatorlanguage": "Browsersprache", - "core.settings.navigatoruseragent": "Browserkennung (userAgent)", - "core.settings.networkstatus": "Internetverbindung", - "core.settings.opensourcelicenses": "Open Source Lizenzen", - "core.settings.privacypolicy": "Datenschutzerklärung", - "core.settings.publisher": "Herausgeber/in", - "core.settings.pushid": "ID für Push-Nachrichten", - "core.settings.reportinbackground": "Fehler automatisch senden", - "core.settings.screen": "Bildschirmgröße", - "core.settings.showdownloadoptions": "Optionen zum Herunterladen anzeigen", - "core.settings.siteinfo": "Website", - "core.settings.spaceusage": "Speichernutzung", - "core.settings.spaceusagehelp": "Beim Löschen der gespeicherten Infos werden alle Offline-Daten der Website entfernt. Du kannst die App dann nicht mehr ohne Internetverbindung verwenden.", - "core.settings.synchronization": "Synchronisieren", - "core.settings.synchronizenow": "Synchronisieren", - "core.settings.synchronizenowhelp": "Beim Synchronisieren werden ausstehende Änderungen und alle im Gerät speicherten Offline-Aktivitäten mit der Website ausgetauscht. Mitteilungen und Systemnachrichten werden übertragen.", - "core.settings.syncsettings": "Synchronisieren", - "core.settings.wificonnection": "WLAN-Verbindung", - "core.sharedfiles.chooseaccountstorefile": "Wähle ein Nutzerkonto, um die Datei dort zu speichern.", - "core.sharedfiles.chooseactionrepeatedfile": "Eine Datei mit gleichem Namen existiert bereits. Möchtest du die vorhandene Datei ersetzen oder umbenennen in '{{$a}}'?", - "core.sharedfiles.errorreceivefilenosites": "Keine gespeicherten Websites. Füge zuerst eine Website hinzu, bevor du eine Datei mit der App teilst.", - "core.sharedfiles.nosharedfiles": "Keine geteilten Dateien für diese Website", - "core.sharedfiles.nosharedfilestoupload": "Du hast hier keine Dateien zum Hochladen. Wenn du eine Datei aus einer anderen App hochladen möchtest, suche diese Datei und tippe auf die Taste 'Öffnen in'.", - "core.sharedfiles.rename": "Umbenennen", - "core.sharedfiles.replace": "Ersetzen", - "core.sharedfiles.sharedfiles": "Geteilte Dateien", - "core.sharedfiles.successstorefile": "Die Datei wurde erfolgreich gespeichert. Du kannst die Datei in den Bereich 'Meine Dateien' hochladen oder in einer Aktivität verwenden.", - "core.sitemaintenance": "", - "core.sizetb": "TB", - "core.sorry": "Sorry ...", - "core.storingfiles": "Dateien speichern", - "core.strftimedate": "%d. %B %Y", - "core.strftimedatefullshort": "%d.%m.%y", - "core.strftimedateshort": "%d. %B", - "core.strftimedatetime": "%d. %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d.%m.%Y %H:%M", - "core.strftimedaydate": "%A, %d. %B %Y", - "core.strftimedaydatetime": "%A, %d. %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d. %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d. %b, %H:%M", - "core.strftimerecentfull": "%a, %d. %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.tablet": "Tablet", - "core.tag.errorareanotsupported": "Dieser Tag-Bereich wird von der App nicht unterstützt.", - "core.tag.warningareasnotsupported": "Einige der Tag-Bereiche werden nicht angezeigt, weil sie von der App nicht unterstützt werden.", - "core.thereisdatatosync": "Die Offline-Daten {{$a}} müssen synchronisiert werden.", - "core.thisdirection": "ltr", - "core.tryagain": "Versuche es noch einmal.", - "core.twoparagraphs": "{{p1}}

        {{p2}}", - "core.uhoh": "Uh oh!", - "core.unexpectederror": "Unerwarteter Fehler! Du musst die App neu starten und es dann noch einmal versuchen.", - "core.unicodenotsupported": "Manche Emojis können auf dieser Website nicht verwendet werden. Die betreffenden Zeichen werden beim Senden der Mitteilung gelöscht.", - "core.unicodenotsupportedcleanerror": "Der Text ist leer, nachdem die Unicode-Zeichen gelöscht wurden.", - "core.unknown": "Unbekannt", - "core.unzipping": "Entpacken ...", - "core.updaterequired": "Aktualisierung der App notwendig", - "core.updaterequireddesc": "Aktualisieren Sie Ihre App auf die Version {{$a}}", - "core.upgraderunning": "Diese Website wird gerade aktualisiert. Versuche es später nochmal.", - "core.user.contact": "Kontakt", - "core.user.detailsnotavailable": "Die Nutzerdetails zu dieser Person sind für dich nicht verfügbar.", - "core.user.editingteacher": "Trainer/in", - "core.user.errorloaduser": "Fehler beim Laden der Nutzerdetails", - "core.user.manager": "Manager/in", - "core.user.phone1": "Telefon", - "core.user.phone2": "Smartphone", - "core.user.sendemail": "E-Mail", - "core.user.student": "Teilnehmer/in", - "core.user.teacher": "Trainer/in ohne Bearbeitungsrecht", - "core.viewcode": "Code zeigen", - "core.vieweditor": "Editor zeigen", - "core.viewembeddedcontent": "Eingebettete Inhalte anzeigen", - "core.warningofflinedatadeleted": "Die Offline-Daten von {{component}} '{{name}}' wurden gelöscht. {{error}}", - "core.whatisyourage": "Wie alt bist du?", - "core.wheredoyoulive": "In welchem Land lebst du?", - "core.whoissiteadmin": "Administrator/innen sind Personen, die die Moodle-Website in deiner Schule, Universität, Organisation oder Firma verwalten. Falls du nicht weißt, wie du diese kontaktieren kannst, frage deine Trainer/innen.", - "core.whoops": "Uuups!", - "core.whyisthishappening": "Warum passiert dies?", - "core.wsfunctionnotavailable": "Die Webservice-Funktion ist nicht verfügbar.", - "core.youreoffline": "Offline", - "core.youreonline": "Sie sind wieder online" -} \ No newline at end of file diff --git a/src/assets/lang/de.json b/src/assets/lang/de.json deleted file mode 100644 index 78e2a4865..000000000 --- a/src/assets/lang/de.json +++ /dev/null @@ -1,2175 +0,0 @@ -{ - "addon.badges.alignment": "Ausrichtung", - "addon.badges.badgedetails": "Grundeinstellungen", - "addon.badges.badges": "Badges", - "addon.badges.bendorsement": "Bestätigung", - "addon.badges.claimcomment": "Kommentar zur Anerkennung", - "addon.badges.claimid": "Leitsatz (URL)", - "addon.badges.contact": "Kontakt", - "addon.badges.dateawarded": "Verleihdatum", - "addon.badges.expired": "Abgelaufen", - "addon.badges.expirydate": "Ablaufdatum", - "addon.badges.imageauthoremail": "E-Mail von Bildautor/in", - "addon.badges.imageauthorname": "Name von Bildautor/in", - "addon.badges.imageauthorurl": "URL von Bildautor/in", - "addon.badges.imagecaption": "Bildtitel", - "addon.badges.issuancedetails": "Ablaufdatum", - "addon.badges.issuerdetails": "Verleiher/in", - "addon.badges.issueremail": "E-Mail", - "addon.badges.issuername": "Verleiher/in", - "addon.badges.issuerurl": "URL des Verleihers", - "addon.badges.language": "Sprache", - "addon.badges.noalignment": "Für diesen Badge sind keine externen Skills oder Kompetenzen angegeben.", - "addon.badges.nobadges": "Keine Badges verfügbar", - "addon.badges.norelated": "Dieser Badge hat keine zugeordneten Badges.", - "addon.badges.recipientdetails": "Empfängerdetails", - "addon.badges.relatedbages": "Zugeordnete Badges", - "addon.badges.version": "Version", - "addon.badges.warnexpired": "(Dieser Badge ist abgelaufen!)", - "addon.block_activitymodules.pluginname": "Aktivitäten", - "addon.block_activityresults.pluginname": "Aktivitätsergebnisse", - "addon.block_badges.pluginname": "Letzte Badges", - "addon.block_blogmenu.pluginname": "Blogmenü", - "addon.block_blogrecent.pluginname": "Aktuelle Blogeinträge", - "addon.block_blogtags.pluginname": "Blog-Tags", - "addon.block_calendarmonth.pluginname": "Kalender", - "addon.block_calendarupcoming.pluginname": "Aktuelle Termine", - "addon.block_comments.pluginname": "Kommentare", - "addon.block_completionstatus.pluginname": "Bearbeitungsstand", - "addon.block_glossaryrandom.pluginname": "Glossareintrag", - "addon.block_learningplans.pluginname": "Lernpläne", - "addon.block_myoverview.all": "Alle (außer aus Darstellung entfernte)", - "addon.block_myoverview.allincludinghidden": "Alle", - "addon.block_myoverview.favourites": "Favoriten", - "addon.block_myoverview.future": "Künftige", - "addon.block_myoverview.hiddencourses": "Aus Darstellung entfernte", - "addon.block_myoverview.inprogress": "Laufende", - "addon.block_myoverview.lastaccessed": "Letzter Zugriff", - "addon.block_myoverview.morecourses": "Weitere Kurse", - "addon.block_myoverview.nocourses": "Keine Kurse", - "addon.block_myoverview.past": "Vergangene", - "addon.block_myoverview.pluginname": "Kursübersicht", - "addon.block_myoverview.shortname": "Kurzname", - "addon.block_myoverview.title": "Kursname", - "addon.block_newsitems.pluginname": "Neue Ankündigungen", - "addon.block_onlineusers.pluginname": "Personen online", - "addon.block_privatefiles.pluginname": "Meine Dateien", - "addon.block_recentactivity.pluginname": "Neue Aktivitäten", - "addon.block_recentlyaccessedcourses.nocourses": "Keine zuletzt besuchten Kurse", - "addon.block_recentlyaccessedcourses.pluginname": "Zuletzt besuchte Kurse", - "addon.block_recentlyaccesseditems.noitems": "Keine zuletzt genutzten Objekte", - "addon.block_recentlyaccesseditems.pluginname": "Zuletzt genutzte Objekte", - "addon.block_rssclient.pluginname": "RSS Feeds", - "addon.block_selfcompletion.pluginname": "Kurs abschließen", - "addon.block_sitemainmenu.pluginname": "Hauptmenü", - "addon.block_starredcourses.nocourses": "Keine favorisierte Kurse", - "addon.block_starredcourses.pluginname": "Favorisierte Kurse", - "addon.block_tags.pluginname": "Tags", - "addon.block_timeline.duedate": "Fälligkeitsdatum", - "addon.block_timeline.next30days": "Nächste 30 Tage", - "addon.block_timeline.next3months": "Nächste 3 Monate", - "addon.block_timeline.next6months": "Nächste 6 Monate", - "addon.block_timeline.next7days": "Nächste 7 Tage", - "addon.block_timeline.nocoursesinprogress": "Keine laufenden Kurse", - "addon.block_timeline.noevents": "Keine bevorstehenden Aktivitäten fällig", - "addon.block_timeline.overdue": "Überfällig", - "addon.block_timeline.pluginname": "Zeitleiste", - "addon.block_timeline.sortbycourses": "Sortiert nach Kursen", - "addon.block_timeline.sortbydates": "Sortiert nach Datum", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Blogeinträge", - "addon.blog.errorloadentries": "Fehler beim Laden der Blogbeiträge", - "addon.blog.linktooriginalentry": "Zum ursprünglichen Blogeintrag verlinken", - "addon.blog.noentriesyet": "Keine sichtbaren Beiträge", - "addon.blog.publishtonoone": "Persönlich", - "addon.blog.publishtosite": "Alle auf dieser Website", - "addon.blog.publishtoworld": "Weltweit öffentlich", - "addon.blog.showonlyyourentries": "Nur Ihre Beiträge anzeigen", - "addon.blog.siteblogheading": "Website-Blog", - "addon.calendar.allday": "ganztägig", - "addon.calendar.calendar": "Kalender", - "addon.calendar.calendarevent": "Kalendereintrag", - "addon.calendar.calendarevents": "Kalender", - "addon.calendar.calendarreminders": "Kalendererinnerungen", - "addon.calendar.categoryevents": "Kursbereichstermine", - "addon.calendar.confirmeventdelete": "Möchten Sie den Termin '{{$a}}' wirklich löschen?", - "addon.calendar.confirmeventseriesdelete": "Der Termin '{{$a.name}}' ist Teil der Serie. Möchten Sie nur diesen Termin entfernen oder alle {{$a.count}} Termine dieser Serie?", - "addon.calendar.courseevents": "Kurstermine", - "addon.calendar.currentmonth": "Aktueller Monat", - "addon.calendar.daynext": "Nächster Tag", - "addon.calendar.dayprev": "Vorheriger Tag", - "addon.calendar.defaultnotificationtime": "Benachrichtigungszeit", - "addon.calendar.deleteallevents": "Alle Termine entfernen", - "addon.calendar.deleteevent": "Termin löschen", - "addon.calendar.deleteoneevent": "Diesen Termin löschen", - "addon.calendar.durationminutes": "Dauer in Minuten", - "addon.calendar.durationnone": "Ohne Zeitangabe", - "addon.calendar.durationuntil": "Bis", - "addon.calendar.editevent": "Termin bearbeiten", - "addon.calendar.errorloadevent": "Fehler beim Laden des Kalendereintrags", - "addon.calendar.errorloadevents": "Fehler beim Laden der Kalendereinträge", - "addon.calendar.eventcalendareventdeleted": "Termin gelöscht", - "addon.calendar.eventduration": "Dauer", - "addon.calendar.eventendtime": "Endzeit", - "addon.calendar.eventkind": "Art des Termins", - "addon.calendar.eventname": "Titel", - "addon.calendar.eventstarttime": "Anfangszeit", - "addon.calendar.eventtype": "Typ des Termins", - "addon.calendar.fri": "Fr", - "addon.calendar.friday": "Freitag", - "addon.calendar.gotoactivity": "Zur Aktivität gehen", - "addon.calendar.groupevents": "Gruppentermine", - "addon.calendar.invalidtimedurationminutes": "Die angegebene Dauer in Minuten ist ungültig. Geben Sie eine Zahl größer als 0 ein oder wählen Sie 'Ohne Zeitangabe'.", - "addon.calendar.invalidtimedurationuntil": "Der angegebene Wert für Datum und Zeit von 'Dauer bis' liegt vor der Startzeit des Termins. Korrigieren Sie diese Einstellung.", - "addon.calendar.mon": "Mo", - "addon.calendar.monday": "Montag", - "addon.calendar.monthlyview": "Monatsansicht", - "addon.calendar.newevent": "Neuer Termin", - "addon.calendar.noevents": "Keine Kalendereinträge", - "addon.calendar.nopermissiontoupdatecalendar": "Sie haben nicht die Rechte, den Termin zu aktualisieren.", - "addon.calendar.reminders": "Erinnerungen", - "addon.calendar.repeatedevents": "Sich wiederholende Termine", - "addon.calendar.repeateditall": "Änderungen für alle {{$a}} Termine der Reihe übernehmen", - "addon.calendar.repeateditthis": "Veränderungen nur an diesem einen Termin vornehmen", - "addon.calendar.repeatevent": "Termin wiederholen", - "addon.calendar.repeatweeksl": "Wöchentliche Wiederholung, automatische Erstellung", - "addon.calendar.sat": "Sa", - "addon.calendar.saturday": "Samstag", - "addon.calendar.setnewreminder": "Neue Erinnerung erstellen", - "addon.calendar.siteevents": "Website-Termine", - "addon.calendar.sun": "So", - "addon.calendar.sunday": "Sonntag", - "addon.calendar.thu": "Do", - "addon.calendar.thursday": "Donnerstag", - "addon.calendar.today": "Heute", - "addon.calendar.tomorrow": "Morgen", - "addon.calendar.tue": "Di", - "addon.calendar.tuesday": "Dienstag", - "addon.calendar.typecategory": "Kursbereichstermin", - "addon.calendar.typeclose": "Termin beenden", - "addon.calendar.typecourse": "Kurstermin", - "addon.calendar.typedue": "Fälliger Termin", - "addon.calendar.typegradingdue": "Fälliger Bewertungstermin", - "addon.calendar.typegroup": "Gruppentermin", - "addon.calendar.typeopen": "Termin öffnen", - "addon.calendar.typesite": "Website-Termin", - "addon.calendar.typeuser": "Nutzertermin", - "addon.calendar.upcomingevents": "Aktuelle Termine", - "addon.calendar.userevents": "Nutzertermine", - "addon.calendar.wed": "Mi", - "addon.calendar.wednesday": "Mittwoch", - "addon.calendar.when": "Wann", - "addon.calendar.yesterday": "Gestern", - "addon.competency.activities": "Aktivitäten", - "addon.competency.competencies": "Kompetenzen", - "addon.competency.competenciesmostoftennotproficientincourse": "Meist ungeübte Kompetenzen in diesem Kurs", - "addon.competency.coursecompetencies": "Kurskompetenzen", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Kompetenzbewertungen in diesem Kurs beeinflussen keine Lernpläne.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Kompetenzbewertungen in diesem Kurs werden sofort in den Lernplänen aktualisiert.", - "addon.competency.crossreferencedcompetencies": "Querverwiesene Kompetenzen", - "addon.competency.duedate": "Fälligkeitsdatum", - "addon.competency.errornocompetenciesfound": "Keine Kompetenzen gefunden", - "addon.competency.evidence": "Beleg", - "addon.competency.evidence_competencyrule": "Die Kompetenzregel wurde erfüllt.", - "addon.competency.evidence_coursecompleted": "Der Kurs '{{$a}}' wurde abgeschlossen.", - "addon.competency.evidence_coursemodulecompleted": "Die Aktivität '{{$a}}' wurde abgeschlossen.", - "addon.competency.evidence_courserestored": "Die Bewertung wurde zusammen mit dem Kurs '{{$a}}' wiederhergestellt.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Der Beleg über Vorkenntnisse '{{$a}}' wurde verlinkt.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Der Beleg über Vorkenntnisse '{{$a}}' wurde freigegeben.", - "addon.competency.evidence_manualoverride": "Die Kompetenzbewertung wurde manuell vergeben.", - "addon.competency.evidence_manualoverrideincourse": "Die Kompetenzbewertung wurde manuell im Kurs '{{$a}}' vorgenommen.", - "addon.competency.evidence_manualoverrideinplan": "Die Kompetenzbewertung wurde manuell im Lernplan '{{$a}}' vorgenommen.", - "addon.competency.learningplancompetencies": "Kompetenzen des Lernplans", - "addon.competency.learningplans": "Lernpläne", - "addon.competency.myplans": "Meine Lernpläne", - "addon.competency.noactivities": "Keine Aktivitäten", - "addon.competency.nocompetencies": "Keine Kompetenzen", - "addon.competency.nocompetenciesincourse": "Für diesen Kurs sind keine Kompetenzen verlinkt.", - "addon.competency.nocrossreferencedcompetencies": "Keine anderen Kompetenzen wurden zu dieser Kompetenz referiert.", - "addon.competency.noevidence": "Keine Belege", - "addon.competency.noplanswerecreated": "Bisher sind keine Lernpläne angelegt.", - "addon.competency.nouserplanswithcompetency": "Diese Kompetenz ist in keinem Lernplan enthalten.", - "addon.competency.path": "Pfad:", - "addon.competency.planstatusactive": "Aktiv", - "addon.competency.planstatuscomplete": "Vollständig", - "addon.competency.planstatusdraft": "Entwurf", - "addon.competency.planstatusinreview": "Überprüfung läuft", - "addon.competency.planstatuswaitingforreview": "Überprüfung abwarten", - "addon.competency.proficient": "Wählbar", - "addon.competency.progress": "Fortschritt", - "addon.competency.rating": "Wertung", - "addon.competency.reviewstatus": "Überprüfungsstatus", - "addon.competency.status": "Status", - "addon.competency.template": "Lernplanvorlage", - "addon.competency.uponcoursecompletion": "Bei Kursabschluss:", - "addon.competency.usercompetencystatus_idle": "Abwarten", - "addon.competency.usercompetencystatus_inreview": "Überprüfung läuft", - "addon.competency.usercompetencystatus_waitingforreview": "Überprüfung abwarten", - "addon.competency.userplans": "Nutzerlernpläne", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} von {{$a.y}} Kompetenzen sind eingeübt", - "addon.competency.xcompetenciesproficientoutofyincourse": "Sie sind in {{$a.x}} von {{$a.y}} Kompetenzen geübt.", - "addon.coursecompletion.complete": "Abschließen", - "addon.coursecompletion.completecourse": "Kurs beenden", - "addon.coursecompletion.completed": "Abgeschlossen", - "addon.coursecompletion.completiondate": "Datum des Kursabschlusses", - "addon.coursecompletion.completionmenuitem": "Abschluss", - "addon.coursecompletion.couldnotloadreport": "Fehler beim Laden des Abschlussberichts. Versuchen Sie es später noch einmal.", - "addon.coursecompletion.coursecompletion": "Kursabschluss", - "addon.coursecompletion.criteria": "Kriterien", - "addon.coursecompletion.criteriagroup": "Kriteriengruppe", - "addon.coursecompletion.criteriarequiredall": "Alle folgenden Kriterien sind notwendig", - "addon.coursecompletion.criteriarequiredany": "Eines der folgenden Kriterien ist notwendig", - "addon.coursecompletion.inprogress": "In Bearbeitung", - "addon.coursecompletion.manualselfcompletion": "Manueller eigener Abschluss", - "addon.coursecompletion.nottracked": "Aktuell läuft für Sie in diesem Kurs keine Abschlussverfolgung", - "addon.coursecompletion.notyetstarted": "Noch nicht begonnen", - "addon.coursecompletion.pending": "Unerledigt", - "addon.coursecompletion.required": "Erforderlich", - "addon.coursecompletion.requiredcriteria": "Notwendiges Kriterium", - "addon.coursecompletion.requirement": "Anforderungen", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Kursbericht ansehen", - "addon.files.couldnotloadfiles": "Die Dateiliste konnte nicht geladen werden.", - "addon.files.emptyfilelist": "Keine Dateien", - "addon.files.erroruploadnotworking": "Im Moment können keine Dateien zur Website hochgeladen werden.", - "addon.files.files": "Dateien", - "addon.files.privatefiles": "Meine Dateien", - "addon.files.sitefiles": "Dateien der Website", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Geräte konfigurieren", - "addon.messages.acceptandaddcontact": "Annehmen und zu Kontakten hinzufügen", - "addon.messages.addcontact": "Kontakt hinzufügen", - "addon.messages.addcontactconfirm": "Möchten Sie wirklich {{$a}} zu Ihren Kontakten hinzufügen?", - "addon.messages.addtofavourites": "Kommunikation als Favorit markieren", - "addon.messages.addtoyourcontacts": "Zu Kontakten hinzufügen", - "addon.messages.blocknoncontacts": "Mitteilungen nur für Kontakte zulassen", - "addon.messages.blockuser": "Person blockieren", - "addon.messages.blockuserconfirm": "Möchten Sie wirklich {{$a}} blockieren?", - "addon.messages.contactableprivacy": "Mitteilungen akzeptieren von:", - "addon.messages.contactableprivacy_coursemember": "Meine Kontakte und jeder aus meinen Kursen", - "addon.messages.contactableprivacy_onlycontacts": "Nur meine Kontakte", - "addon.messages.contactableprivacy_site": "Jeder im System", - "addon.messages.contactblocked": "Kontakt blockiert", - "addon.messages.contactlistempty": "Die Kontaktliste ist leer.", - "addon.messages.contactname": "Name", - "addon.messages.contactrequestsent": "Kontaktanfrage gesendet", - "addon.messages.contacts": "Kontakte", - "addon.messages.conversationactions": "Kommunikations-Aktionsmenü", - "addon.messages.decline": "Ablehnen", - "addon.messages.deleteallconfirm": "Möchten Sie wirklich die gesamte Kommunikation löschen? Die Kommunikation bleibt allerdings bei den anderen beteiligten Personen erhalten.", - "addon.messages.deleteallselfconfirm": "Möchten Sie diese persönliche Kommunikation wirklich löschen?", - "addon.messages.deleteconversation": "Kommunikation löschen", - "addon.messages.deleteforeveryone": "Für mich und alle anderen löschen", - "addon.messages.deletemessage": "Mitteilung löschen", - "addon.messages.deletemessageconfirmation": "Möchten Sie diese Mitteilung wirklich löschen? Die Mitteilung wird nur in Ihrem Verlauf gelöscht, aber nicht bei der Person, die die Mitteilung gesendet oder empfangen hat.", - "addon.messages.errordeletemessage": "Fehler beim Löschen der Mitteilung", - "addon.messages.errorwhileretrievingcontacts": "Fehler beim Abrufen der Kontakte vom Server", - "addon.messages.errorwhileretrievingdiscussions": "Fehler beim Abrufen der Themen vom Server", - "addon.messages.errorwhileretrievingmessages": "Fehler beim Abrufen der Mitteilungen vom Server", - "addon.messages.errorwhileretrievingusers": "Fehler beim Abrufen der Personen vom Server", - "addon.messages.groupconversations": "Gruppe", - "addon.messages.groupinfo": "Gruppeninfo", - "addon.messages.individualconversations": "Persönlich", - "addon.messages.info": "Nutzerinformationen", - "addon.messages.isnotinyourcontacts": "{{$a}} gehört nicht zu Ihren Kontakten", - "addon.messages.message": "Mitteilung", - "addon.messages.messagenotsent": "Die Mitteilung wurde nicht gesendet. Versuchen Sie es später noch einmal.", - "addon.messages.messagepreferences": "Mitteilungen", - "addon.messages.messages": "Mitteilungen", - "addon.messages.muteconversation": "Stummschalten", - "addon.messages.mutedconversation": "Stummgeschaltete Kommunikation", - "addon.messages.newmessage": "Neue Mitteilung", - "addon.messages.newmessages": "Neue Mitteilungen", - "addon.messages.nocontactrequests": "Keine Kontaktanfragen", - "addon.messages.nocontactsgetstarted": "Keine Kontakte", - "addon.messages.nofavourites": "Keine Kommunikation als Favorit markiert", - "addon.messages.nogroupconversations": "Keine Gruppenkommunikation", - "addon.messages.noindividualconversations": "Keine persönliche Kommunikation", - "addon.messages.nomessagesfound": "Keine Mitteilungen gefunden", - "addon.messages.noncontacts": "Weitere Personen", - "addon.messages.nousersfound": "Keine Personen gefunden", - "addon.messages.numparticipants": "{{$a}} Teilnehmer/innen", - "addon.messages.removecontact": "Kontakt entfernen", - "addon.messages.removecontactconfirm": "Möchten Sie wirklich {{$a}} aus Ihren Kontakten entfernen?", - "addon.messages.removefromfavourites": "Markierung der Kommunikaton entfernen", - "addon.messages.removefromyourcontacts": "Aus Kontakten entfernen", - "addon.messages.requests": "Anfragen", - "addon.messages.requirecontacttomessage": "Sie müssen {{$a}} anfragen, Sie als Kontakt hinzuzufügen. Erst dann können Sie eine Mitteilung senden.", - "addon.messages.searchcombined": "Personen und Mitteilungen suchen", - "addon.messages.selfconversation": "Persönlicher Bereich", - "addon.messages.selfconversationdefaultmessage": "Speichern Sie Entwürfe von Nachrichten, Links, Notizen usw. für einen späteren Zugriff.", - "addon.messages.sendcontactrequest": "Kontaktanfrage senden", - "addon.messages.showdeletemessages": "Mitteilungen löschen anzeigen", - "addon.messages.type_blocked": "Gesperrt", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Suchergebnisse", - "addon.messages.type_strangers": "Weitere Personen", - "addon.messages.unabletomessage": "Sie können dieser Person keine Mitteilung senden.", - "addon.messages.unblockuser": "Blockierung für diese Person aufheben", - "addon.messages.unblockuserconfirm": "Möchten Sie die Blockierung für {{$a}} wirklich aufheben?", - "addon.messages.unmuteconversation": "Stummschaltung entfernen", - "addon.messages.useentertosend": "Zum Senden die Eingabetaste tippen", - "addon.messages.useentertosenddescdesktop": "Falls deaktiviert, können Sie mit Ctrl+Enter die Mitteilung senden.", - "addon.messages.useentertosenddescmac": "Falls deaktiviert, können Sie mit Cmd+Enter die Mitteilung senden.", - "addon.messages.userwouldliketocontactyou": "{{$a}} möchte Sie kontaktieren", - "addon.messages.warningconversationmessagenotsent": "Mitteilung zum Thema {{conversation}} konnte nicht gesendet werden. {{error}}", - "addon.messages.warningmessagenotsent": "Mitteilung an {{user}} konnte nicht gesendet werden. {{error}}", - "addon.messages.wouldliketocontactyou": "Ich möchte Sie kontaktieren", - "addon.messages.you": "Sie:", - "addon.messages.youhaveblockeduser": "Sie haben diese Person blockiert.", - "addon.messages.yourcontactrequestpending": "Ihre Kontaktanfrage an {{$a}} ist noch offen", - "addon.mod_assign.acceptsubmissionstatement": "Bestätigen Sie die Erklärung zur Eigenständigkeit.", - "addon.mod_assign.addattempt": "Weiteren Versuch zulassen", - "addon.mod_assign.addnewattempt": "Neuen Versuch hinzufügen", - "addon.mod_assign.addnewattemptfromprevious": "Neuen Versuch auf Grundlage der vorherigen Lösung abgeben", - "addon.mod_assign.addsubmission": "Abgabe hinzufügen", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Die Aufgabendetails und die Lösungsabgabe sind verfügbar ab {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Abgabebeginn", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Abgabe möglich ab {{$a}}", - "addon.mod_assign.applytoteam": "Bewertungen und Feedback der gesamten Gruppe zuweisen", - "addon.mod_assign.assignmentisdue": "Aufgabe ist fällig", - "addon.mod_assign.attemptnumber": "Nummer", - "addon.mod_assign.attemptreopenmethod": "Versuche erneut bearbeitbar", - "addon.mod_assign.attemptreopenmethod_manual": "Manuell", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatisch bis zum Bestehen", - "addon.mod_assign.attemptsettings": "Einstellungen für Versuche", - "addon.mod_assign.cannoteditduetostatementsubmission": "Sie können in der App keine Abgabe hinzufügen oder bearbeiten, weil keine Erklärung zur Eigenständigkeit von der Website abgerufen werden konnte.", - "addon.mod_assign.cannotgradefromapp": "Manche Bewertungsmethoden werden von der App bisher nicht unterstützt und können nicht verändert werden.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Sie können in der App keine Abgabe machen, weil von der Website keine Erklärung zur Eigenständigkeit abgerufen werden konnte.", - "addon.mod_assign.confirmsubmission": "Wenn Sie nun Ihre Lösung zur Bewertung einreichen, können Sie nichts mehr verändern. Sind Sie sich sicher?", - "addon.mod_assign.currentattempt": "Dies ist Versuch {{$a}}.", - "addon.mod_assign.currentattemptof": "Versuch {{$a.attemptnumber}} (mögliche Versuche {{$a.maxattempts}})", - "addon.mod_assign.currentgrade": "Aktuelle Bewertung in Bewertungen", - "addon.mod_assign.cutoffdate": "Letzte Abgabemöglichkeit", - "addon.mod_assign.defaultteam": "Standard-Gruppe", - "addon.mod_assign.duedate": "Fälligkeitsdatum", - "addon.mod_assign.duedateno": "Kein Fälligkeitsdatum", - "addon.mod_assign.duedatereached": "Das Fälligkeitsdatum für diese Aufgabe ist vorbei.", - "addon.mod_assign.editingstatus": "Bearbeitungsstatus", - "addon.mod_assign.editsubmission": "Abgabe bearbeiten", - "addon.mod_assign.erroreditpluginsnotsupported": "Sie können in der App keine Abgabe hinzufügen oder bearbeiten, weil manche Plugins bisher keine Bearbeitung erlauben:", - "addon.mod_assign.errorshowinginformation": "Die Abgabeinformationen können nicht angezeigt werden.", - "addon.mod_assign.extensionduedate": "Verlängerung des Fälligkeitsdatums", - "addon.mod_assign.feedbacknotsupported": "Dieses Feedback wird von der App nicht unterstützt, so dass Informationen fehlen könnten.", - "addon.mod_assign.grade": "Bewertung", - "addon.mod_assign.graded": "Bewertet", - "addon.mod_assign.gradedby": "Bewertet von", - "addon.mod_assign.gradedfollowupsubmit": "Bewertet - empfangene Abgabe nachverfolgen", - "addon.mod_assign.gradedon": "Bewertet am", - "addon.mod_assign.gradelocked": "Diese Bewertung ist gesperrt oder wurde im Bewertungsbereich überschrieben.", - "addon.mod_assign.gradenotsynced": "Bewertung nicht synchronisiert", - "addon.mod_assign.gradeoutof": "Bewertung (max. {{$a}})", - "addon.mod_assign.gradingstatus": "Bewertungsstatus", - "addon.mod_assign.groupsubmissionsettings": "Einstellungen für Gruppeneinreichungen", - "addon.mod_assign.hiddenuser": "Teilnehmer/in", - "addon.mod_assign.latesubmissions": "Verspätete Abgaben", - "addon.mod_assign.latesubmissionsaccepted": "Erlaubt bis {{$a}}", - "addon.mod_assign.markingworkflowstate": "Status des Bewertungsworkflows", - "addon.mod_assign.markingworkflowstateinmarking": "In Bewertung", - "addon.mod_assign.markingworkflowstateinreview": "In weiterer Überprüfung", - "addon.mod_assign.markingworkflowstatenotmarked": "Unbewertet", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Fertig zur Freigabe", - "addon.mod_assign.markingworkflowstatereadyforreview": "Bewertung abgeschlossen", - "addon.mod_assign.markingworkflowstatereleased": "Freigegeben (Teilnehmer können ihre Bewertung einsehen)", - "addon.mod_assign.modulenameplural": "Aufgaben", - "addon.mod_assign.multipleteams": "Mitglied in mehreren Gruppen", - "addon.mod_assign.multipleteams_desc": "Diese Aufgabe wird in Gruppen abgegeben. Sie sind Mitglied in mehr als einer Gruppe. Um ihre Einreichung korrekt Ihrer Gruppe zuordnen zu können, müssen Sie Mitglied in genau einer Gruppe sein. Kontaktieren Sie Ihre Trainer/innen, um Ihre Gruppenzugehörigkeit zu aktualisieren.", - "addon.mod_assign.noattempt": "Kein Versuch", - "addon.mod_assign.nomoresubmissionsaccepted": "Weitere Abgaben sind nur zugelassen, wenn der Abgabezeitraum verlängert wird.", - "addon.mod_assign.noonlinesubmissions": "Diese Aufgabe benötigt keine Online-Abgabe", - "addon.mod_assign.nosubmission": "Für diese Aufgabe wurde nichts abgegeben", - "addon.mod_assign.notallparticipantsareshown": "Teilnehmer/innen, die nichts abgegeben haben, werden nicht angezeigt.", - "addon.mod_assign.noteam": "Nicht Mitglied einer Gruppe", - "addon.mod_assign.noteam_desc": "Diese Aufgabe wird in Gruppen abgegeben. Sie sind kein Mitglied in einer Gruppe und können die Aufgabe deswegen nicht einreichen. Kontaktieren Sie Ihre Trainer/innen, um einer Gruppe hinzugefügt zu werden.", - "addon.mod_assign.notgraded": "Nicht bewertet", - "addon.mod_assign.numberofdraftsubmissions": "Entwürfe", - "addon.mod_assign.numberofparticipants": "Teilnehmer/innen", - "addon.mod_assign.numberofsubmissionsneedgrading": "Bewertung erwartet", - "addon.mod_assign.numberofsubmittedassignments": "Abgegeben", - "addon.mod_assign.numberofteams": "Gruppen", - "addon.mod_assign.numwords": "{{$a}} Wörter", - "addon.mod_assign.outof": "{{$a.current}} von {{$a.total}}", - "addon.mod_assign.overdue": "Abgabeende überschritten seit: {{$a}}", - "addon.mod_assign.submission": "Abgabe", - "addon.mod_assign.submissioneditable": "Teilnehmer/innen können eingereichte Lösung bearbeiten", - "addon.mod_assign.submissionnoteditable": "Teilnehmer/innen können die eingereichte Lösung nicht bearbeiten", - "addon.mod_assign.submissionnotsupported": "Diese Abgabe wird von der App nicht unterstützt und es könnten Infos fehlen.", - "addon.mod_assign.submissionslocked": "Bei dieser Aufgabe können derzeit keine Lösungen abgeben werden.", - "addon.mod_assign.submissionstatus": "Abgabestatus", - "addon.mod_assign.submissionstatus_": "Keine Abgabe", - "addon.mod_assign.submissionstatus_draft": "Entwurf (nicht abgegeben)", - "addon.mod_assign.submissionstatus_marked": "Bewertet", - "addon.mod_assign.submissionstatus_new": "Keine Abgabe", - "addon.mod_assign.submissionstatus_reopened": "Erneut geöffnet", - "addon.mod_assign.submissionstatus_submitted": "Zur Bewertung abgegeben", - "addon.mod_assign.submissionstatusheading": "Abgabestatus", - "addon.mod_assign.submissionteam": "Gruppe", - "addon.mod_assign.submitassignment": "Aufgabe abgeben", - "addon.mod_assign.submitassignment_help": "Sobald diese Aufgabe abgegeben ist, sind keine Änderungen mehr möglich.", - "addon.mod_assign.submittedearly": "Aufgabe wurde {{$a}} vor dem Abgabeende abgegeben", - "addon.mod_assign.submittedlate": "Aufgabe wurde {{$a}} verspätet abgegeben", - "addon.mod_assign.timemodified": "Zuletzt geändert", - "addon.mod_assign.timeremaining": "Verbleibende Zeit", - "addon.mod_assign.ungroupedusers": "Die Option 'Gruppe notwendig, um etwas abgeben zu können' ist aktiviert. Es gibt Personen ohne Gruppe oder Personen mit mehreren Gruppen, die deshalb nichts abgeben können.", - "addon.mod_assign.ungroupedusersoptional": "Die Einstellung 'Teilnehmer/innen reichen in Gruppen ein' ist aktiviert und es gibt Teilnehmer/innen, die entweder kein Mitglied einer Gruppe oder Mitglied in mehr als einer Gruppe sind. Diese Teilnehmer/innen reichen als Mitglieder der Standardgruppe ein.", - "addon.mod_assign.unlimitedattempts": "Unbegrenzt", - "addon.mod_assign.userswhoneedtosubmit": "Nutzer/innen, die noch nicht abgegeben haben: {{$a}}", - "addon.mod_assign.userwithid": "Nutzer/in mit ID {{id}}", - "addon.mod_assign.viewsubmission": "Abgabe anzeigen", - "addon.mod_assign.warningsubmissiongrademodified": "Die Abgabebewertung wurde auf der Website geändert.", - "addon.mod_assign.warningsubmissionmodified": "Die Abgabe wurde auf der Website geändert.", - "addon.mod_assign.wordlimit": "Wortbegrenzung", - "addon.mod_assign_feedback_comments.pluginname": "Feedback als Kommentar", - "addon.mod_assign_feedback_editpdf.pluginname": "Anmerkungen im PDF", - "addon.mod_assign_feedback_file.pluginname": "Feedback als Datei", - "addon.mod_assign_submission_comments.pluginname": "Abgabekommentare", - "addon.mod_assign_submission_file.pluginname": "Dateiabgabe", - "addon.mod_assign_submission_onlinetext.pluginname": "Texteingabe online", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Die maximale Anzahl von Wörtern für diese Aufgabenlösung ist {{$a.limit}}. Ihre Antwort hat {{$a.count}} Wörter. Verkürzen Sie Ihren Text und speichern Sie ihn dann erneut.", - "addon.mod_book.errorchapter": "Fehler beim Lesen des Kapitels", - "addon.mod_book.modulenameplural": "Bücher", - "addon.mod_book.navnexttitle": "Nächster: {{$a}}", - "addon.mod_book.navprevtitle": "Vorheriger: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Buchkapitel", - "addon.mod_book.toc": "Inhaltsverzeichnis", - "addon.mod_chat.beep": "Beep", - "addon.mod_chat.chatreport": "Chat-Protokolle", - "addon.mod_chat.currentusers": "Aktuelle Nutzer/innen", - "addon.mod_chat.enterchat": "Chat betreten", - "addon.mod_chat.entermessage": "Mitteilung schreiben", - "addon.mod_chat.errorwhileconnecting": "Fehler beim Verbinden zum Chat", - "addon.mod_chat.errorwhilegettingchatdata": "Fehler beim Abrufen des Chats", - "addon.mod_chat.errorwhilegettingchatusers": "Fehler beim Anzeigen der Personen im Chat", - "addon.mod_chat.errorwhileretrievingmessages": "Fehler beim Abrufen der Mitteilungen vom Server", - "addon.mod_chat.errorwhilesendingmessage": "Fehler beim Senden der Mitteilung", - "addon.mod_chat.messagebeepseveryone": "{{$a}} piepst jeden an!", - "addon.mod_chat.messagebeepsyou": "{{$a}} hat Sie angepiepst!", - "addon.mod_chat.messageenter": "{{$a}} hat den Chat betreten", - "addon.mod_chat.messageexit": "{{$a}} hat den Chat verlassen", - "addon.mod_chat.messages": "Mitteilungen", - "addon.mod_chat.messageyoubeep": "Sie haben {{$a}} angepiepst", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "Sie müssen online sein, um Mitteilungen senden zu können.", - "addon.mod_chat.nomessages": "Keine Mitteilungen", - "addon.mod_chat.nosessionsfound": "Keine Sessions", - "addon.mod_chat.saidto": "sagte zu", - "addon.mod_chat.send": "Senden", - "addon.mod_chat.sessionstart": "Der nächste Chat beginnt {{$a.date}}, (also in {{$a.fromnow}})", - "addon.mod_chat.showincompletesessions": "Unvollständige Sessions anzeigen", - "addon.mod_chat.talk": "Talk", - "addon.mod_chat.viewreport": "Chat-Protokolle anzeigen", - "addon.mod_choice.cannotsubmit": "Ihre Auswahl konnte nicht gespeichert werden. Versuchen Sie es noch einmal.", - "addon.mod_choice.choiceoptions": "Abstimmoptionen", - "addon.mod_choice.errorgetchoice": "Fehler beim Laden der Abstimmung", - "addon.mod_choice.expired": "Diese Abstimmung ist seit {{$a}} beendet.", - "addon.mod_choice.full": "(Nicht verfügbar)", - "addon.mod_choice.modulenameplural": "Abstimmungen", - "addon.mod_choice.noresultsviewable": "Ergebnisse sind aktuell nicht sichtbar.", - "addon.mod_choice.notopenyet": "Die Aktivität ist nicht verfügbar bis {{$a}}.", - "addon.mod_choice.numberofuser": "Anzahl der Antworten", - "addon.mod_choice.numberofuserinpercentage": "Prozent der Antworten", - "addon.mod_choice.previewonly": "Diese Vorschau zeigt die verfügbaren Optionen für diese Aktivität. Sie können Ihre Wahl nicht vor {{$a}} einreichen.", - "addon.mod_choice.publishinfoanonafter": "Ergebnisse werden nach Ihrer Antwort ohne Namensnennung veröffentlicht.", - "addon.mod_choice.publishinfoanonclose": "Ergebnisse werden ohne Namensnennung nach Abschluss der Aktivität veröffentlicht.", - "addon.mod_choice.publishinfofullafter": "Ergebnisse werden nach Ihrer Antwort vollständig mit Namensnennung veröffentlicht.", - "addon.mod_choice.publishinfofullclose": "Ergebnisse werden nach Abschluss der Aktivität vollständig mit Namensnennung veröffentlicht.", - "addon.mod_choice.publishinfonever": "Die Ergebnisse werden nach Ihrer Antwort nicht veröffentlicht.", - "addon.mod_choice.removemychoice": "Meine Auswahl löschen", - "addon.mod_choice.responses": "Antworten", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% haben die Option gewählt: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Grafische Darstellung", - "addon.mod_choice.resultsnotsynced": "Ihre letzte Antwort ist in den Ergebnissen noch nicht enthalten. Synchronisieren Sie die Daten.", - "addon.mod_choice.savemychoice": "Meine Auswahl speichern", - "addon.mod_choice.userchoosethisoption": "Teilnehmer/innen mit dieser Auswahl", - "addon.mod_choice.yourselection": "Ihre Auswahl", - "addon.mod_data.addentries": "Einträge hinzufügen", - "addon.mod_data.advancedsearch": "Erweiterte Suche", - "addon.mod_data.alttext": "Alternativer Text", - "addon.mod_data.approve": "Zulassen", - "addon.mod_data.approved": "Bestätigt", - "addon.mod_data.ascending": "Aufsteigend", - "addon.mod_data.authorfirstname": "Vorname (Autor/in)", - "addon.mod_data.authorlastname": "Nachname (Autor/in)", - "addon.mod_data.confirmdeleterecord": "Möchten Sie diesen Datensatz in der Datenbank wirklich löschen?", - "addon.mod_data.descending": "Absteigend", - "addon.mod_data.disapprove": "Eintrag nicht freigegeben", - "addon.mod_data.edittagsnotsupported": "Das Bearbeitung von Tags wird in der App nicht unterstützt.", - "addon.mod_data.emptyaddform": "Sie haben keine Einträge vorgenommen!", - "addon.mod_data.entrieslefttoadd": "Sie müssen {{$a.entriesleft}} weitere Einträge vornehmen, um diese Aktivität zu beenden.", - "addon.mod_data.entrieslefttoaddtoview": "Sie müssen {{$a.entrieslefttoview}} weitere Einträge vornehmen, bevor Sie andere Teilnehmerbeiträge betrachten können.", - "addon.mod_data.errorapproving": "Fehler beim Freigeben bzw. Ablehnen des Eintrags", - "addon.mod_data.errordeleting": "Fehler beim Löschen des Eintrags", - "addon.mod_data.errormustsupplyvalue": "Sie müssen hier einen Wert eintragen.", - "addon.mod_data.expired": "Die Aktivität wurde am {{$a}} abgeschlossen und ist nicht mehr verfügbar.", - "addon.mod_data.fields": "Felder", - "addon.mod_data.foundrecords": "Einträge gefunden: {{$a.num}}/{{$a.max}} (Filter zurücksetzen)", - "addon.mod_data.gettinglocation": "Standort bestimmen ...", - "addon.mod_data.latlongboth": "Längen- und Breitengrad müssen eingetragen werden.", - "addon.mod_data.locationpermissiondenied": "Die Berechtigung zum Zugriff auf Ihren Standort wurde verweigert.", - "addon.mod_data.menuchoose": "Auswählen ...", - "addon.mod_data.modulenameplural": "Datenbanken", - "addon.mod_data.more": "Einzelansicht", - "addon.mod_data.mylocation": "Mein Standort", - "addon.mod_data.nomatch": "Keine passenden Einträge gefunden", - "addon.mod_data.norecords": "Keine Einträge in der Datenbank", - "addon.mod_data.notapproved": "Der Eintrag wurde bisher nicht freigegeben", - "addon.mod_data.notopenyet": "Die Aktivität ist nicht verfügbar bis {{$a}}.", - "addon.mod_data.numrecords": "{{$a}} Datensätze", - "addon.mod_data.other": "Andere", - "addon.mod_data.recordapproved": "Datensatz wurde angenommen", - "addon.mod_data.recorddeleted": "Datensatz gelöscht", - "addon.mod_data.recorddisapproved": "Eintrag nicht freigegeben", - "addon.mod_data.resetsettings": "Filter zurücksetzen", - "addon.mod_data.search": "Suche", - "addon.mod_data.searchbytagsnotsupported": "Die Suche nach Tags wird in der App nicht unterstützt.", - "addon.mod_data.selectedrequired": "Gesamte Auswahl ist erforderlich", - "addon.mod_data.single": "Einzelansicht", - "addon.mod_data.tagarea_data_records": "Datensätze", - "addon.mod_data.timeadded": "Zeit erstellt", - "addon.mod_data.timemodified": "Zuletzt geändert", - "addon.mod_data.usedate": "In Suche einbeziehen", - "addon.mod_feedback.analysis": "Auswertung", - "addon.mod_feedback.anonymous": "Anonym", - "addon.mod_feedback.anonymous_entries": "Anonyme Einträge ({{$a}})", - "addon.mod_feedback.average": "Mittelwert", - "addon.mod_feedback.captchaofflinewarning": "Ein Feedback mit Captcha kann offline nicht beendet werden. Captcha funktioniert nur, wenn der Server antworten kann.", - "addon.mod_feedback.complete_the_form": "Fragebogen ausfüllen", - "addon.mod_feedback.completed_feedbacks": "Ausgefüllte Feedbacks", - "addon.mod_feedback.continue_the_form": "Ausfüllen des Fragebogens fortsetzen", - "addon.mod_feedback.feedback_is_not_open": "Ein Feedback ist zu diesem Zeitpunkt nicht möglich", - "addon.mod_feedback.feedback_submitted_offline": "Das Feedback wurde gespeichert, um es später zu übertragen.", - "addon.mod_feedback.feedbackclose": "Antworten erlaubt bis", - "addon.mod_feedback.feedbackopen": "Antworten erlaubt ab", - "addon.mod_feedback.mapcourses": "Feedback zu Kursen zuordnen", - "addon.mod_feedback.maximal": "Maximal", - "addon.mod_feedback.minimal": "Minimal", - "addon.mod_feedback.mode": "Modus", - "addon.mod_feedback.modulenameplural": "Feedback", - "addon.mod_feedback.next_page": "Nächste Seite", - "addon.mod_feedback.non_anonymous": "Nicht anonym", - "addon.mod_feedback.non_anonymous_entries": "Nicht-anonyme Einträge ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Teilnehmer/innen ohne Antwort ({{$a}})", - "addon.mod_feedback.not_selected": "Nicht ausgewählt", - "addon.mod_feedback.not_started": "Nicht begonnen", - "addon.mod_feedback.numberoutofrange": "Zahl außerhalb des Bereichs", - "addon.mod_feedback.overview": "Überblick", - "addon.mod_feedback.page_after_submit": "Abschlussmitteilung", - "addon.mod_feedback.preview": "Vorschau", - "addon.mod_feedback.previous_page": "Vorherige Seite", - "addon.mod_feedback.questions": "Fragen", - "addon.mod_feedback.response_nr": "Antwort Nr.", - "addon.mod_feedback.responses": "Antworten", - "addon.mod_feedback.save_entries": "Einträge speichern", - "addon.mod_feedback.show_entries": "Einträge anzeigen", - "addon.mod_feedback.show_nonrespondents": "Ohne Antwort", - "addon.mod_feedback.started": "Begonnen", - "addon.mod_feedback.this_feedback_is_already_submitted": "Sie haben diese Aktivität bereits beendet.", - "addon.mod_folder.emptyfilelist": "Keine Dateien", - "addon.mod_folder.modulenameplural": "Verzeichnisse", - "addon.mod_forum.addanewdiscussion": "Neues Thema hinzufügen", - "addon.mod_forum.addanewquestion": "Neue Frage hinzufügen", - "addon.mod_forum.addanewtopic": "Neues Thema hinzufügen", - "addon.mod_forum.addtofavourites": "Dieses Thema markieren", - "addon.mod_forum.advanced": "Erweitert", - "addon.mod_forum.cannotadddiscussion": "Nur Gruppenmitglieder dürfen Beiträge zum Forum hinzufügen.", - "addon.mod_forum.cannotadddiscussionall": "Sie dürfen kein neues Thema für alle Teilnehmer/innen anlegen.", - "addon.mod_forum.cannotcreatediscussion": "Das neue Thema konnte nicht angelegt werden.", - "addon.mod_forum.couldnotadd": "Fehler. Der Beitrag konnte nicht hinzugefügt werden.", - "addon.mod_forum.couldnotupdate": "Fehler. Der Beitrag konnte nicht geändert werden.", - "addon.mod_forum.cutoffdatereached": "Der letzte Abgabetermin für dieses Forum ist erreicht. Sie können keinen Beitrag mehr schreiben.", - "addon.mod_forum.delete": "Löschen", - "addon.mod_forum.deletedpost": "Der Beitrag wurde gelöscht.", - "addon.mod_forum.deletesure": "Möchten Sie diesen Beitrag wirklich löschen?", - "addon.mod_forum.discussion": "Thema", - "addon.mod_forum.discussionlistsortbycreatedasc": "Nach Erstelldatum aufsteigend sortieren", - "addon.mod_forum.discussionlistsortbycreateddesc": "Nach Erstelldatum absteigend sortieren", - "addon.mod_forum.discussionlistsortbylastpostasc": "Nach Erstelldatum des letzten Beitrags aufsteigend sortieren", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Nach Erstelldatum des letzten Beitrags absteigend sortieren", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Nach Anzahl der Antworten aufsteigend sortieren", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Nach Anzahl der Antworten absteigend sortieren", - "addon.mod_forum.discussionlocked": "Dieses Thema wurde gesperrt. Sie können hier nicht weiter antworten.", - "addon.mod_forum.discussionpinned": "Angepinnt", - "addon.mod_forum.discussionsubscription": "Themenabonnement", - "addon.mod_forum.edit": "Bearbeiten", - "addon.mod_forum.erroremptymessage": "Die Mitteilung darf nicht leer sein.", - "addon.mod_forum.erroremptysubject": "Der Betreff darf nicht leer sein.", - "addon.mod_forum.errorgetforum": "Fehler beim Laden der Forumsdaten", - "addon.mod_forum.errorgetgroups": "Fehler beim Laden der Gruppeneinstellungen", - "addon.mod_forum.errorposttoallgroups": "Das neue Thema konnte nicht in allen Gruppen angelegt werden.", - "addon.mod_forum.favouriteupdated": "Ihre Markierungsoption wurde aktualisiert.", - "addon.mod_forum.forumnodiscussionsyet": "Keine Themen im Forum", - "addon.mod_forum.group": "Gruppe", - "addon.mod_forum.lastpost": "Letzter Beitrag", - "addon.mod_forum.lockdiscussion": "Dieses Thema sperren", - "addon.mod_forum.lockupdated": "Die Sperroption wurde aktualisiert.", - "addon.mod_forum.message": "Mitteilung", - "addon.mod_forum.modeflatnewestfirst": "Anzeige nach Zeit (neu > alt)", - "addon.mod_forum.modeflatoldestfirst": "Anzeige nach Zeit (alt > neu)", - "addon.mod_forum.modenested": "Anzeige in geschachtelter Form", - "addon.mod_forum.modulenameplural": "Foren", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} Themen", - "addon.mod_forum.numreplies": "{{numreplies}} Antworten", - "addon.mod_forum.pindiscussion": "Dieses Thema anpinnen", - "addon.mod_forum.pinupdated": "Die Anpinnoption wurde aktualisiert.", - "addon.mod_forum.postisprivatereply": "Der Beitrag wurde privat verfasst und ist nicht für alle Personen sichtbar.", - "addon.mod_forum.posttoforum": "Beitrag absenden", - "addon.mod_forum.posttomygroups": "Kopie an alle Gruppen senden", - "addon.mod_forum.privatereply": "Privat antworten", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Themen aktualisieren", - "addon.mod_forum.refreshposts": "Forenbeiträge aktualisieren", - "addon.mod_forum.removefromfavourites": "Themenmarkierung entfernen", - "addon.mod_forum.reply": "Antworten", - "addon.mod_forum.replyplaceholder": "Schreiben Sie Ihre Antwort …", - "addon.mod_forum.subject": "Betreff", - "addon.mod_forum.tagarea_forum_posts": "Forenbeiträge", - "addon.mod_forum.thisforumhasduedate": "Das Fälligkeitsdatum, um in dieses Forum zu schreiben, ist {{$a}}.", - "addon.mod_forum.thisforumisdue": "Das Fälligkeitsdatum, um diesem Forum zu schreiben, war {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Dieses Thema entsperren", - "addon.mod_forum.unpindiscussion": "Dieses Thema abpinnen", - "addon.mod_forum.unread": "Ungelesen", - "addon.mod_forum.unreadpostsnumber": "{{$a}} ungelesene Beiträge", - "addon.mod_forum.yourreply": "Antwort", - "addon.mod_glossary.addentry": "Eintrag hinzufügen", - "addon.mod_glossary.aliases": "Alternativbegriffe", - "addon.mod_glossary.attachment": "Anhang", - "addon.mod_glossary.browsemode": "Einträge durchblättern", - "addon.mod_glossary.byalphabet": "Alphabetisch", - "addon.mod_glossary.byauthor": "Nach Autor/in", - "addon.mod_glossary.bycategory": "Nach Kategorie", - "addon.mod_glossary.bynewestfirst": "Neu zuerst", - "addon.mod_glossary.byrecentlyupdated": "Kürzlich aktualisiert", - "addon.mod_glossary.bysearch": "Suchen", - "addon.mod_glossary.cannoteditentry": "Eintrag nicht bearbeitbar", - "addon.mod_glossary.casesensitive": "Groß-/Kleinschreibung", - "addon.mod_glossary.categories": "Kategorien", - "addon.mod_glossary.concept": "Begriff", - "addon.mod_glossary.definition": "Definition", - "addon.mod_glossary.entriestobesynced": "Einträge zum Synchronisieren", - "addon.mod_glossary.entrypendingapproval": "Dieser Eintrag wartet auf eine Freigabe.", - "addon.mod_glossary.entryusedynalink": "Eintrag automatisch verlinken", - "addon.mod_glossary.errconceptalreadyexists": "Diesen Begriff gibt es bereits. In diesem Glossar sind keine Doppeleinträge erlaubt.", - "addon.mod_glossary.errorloadingentries": "Fehler beim Laden der Einträge", - "addon.mod_glossary.errorloadingentry": "Fehler beim Laden des Eintrags", - "addon.mod_glossary.errorloadingglossary": "Fehler beim Laden des Glossars", - "addon.mod_glossary.fillfields": "Begriff und Definition sind Pflichtfelder.", - "addon.mod_glossary.fullmatch": "Nur vollständige Wörter", - "addon.mod_glossary.linking": "Autoverlinkung", - "addon.mod_glossary.modulenameplural": "Glossare", - "addon.mod_glossary.noentriesfound": "Keine Einträge", - "addon.mod_glossary.searchquery": "Suchanfrage", - "addon.mod_glossary.tagarea_glossary_entries": "Glossareinträge", - "addon.mod_h5pactivity.all_attempts": "Alle Nutzerversuche", - "addon.mod_h5pactivity.answer_checked": "Antwort geprüft", - "addon.mod_h5pactivity.answer_correct": "Ihre Antwort ist richtig", - "addon.mod_h5pactivity.answer_fail": "Falsche Antwort", - "addon.mod_h5pactivity.answer_incorrect": "Ihre Antwort ist falsch", - "addon.mod_h5pactivity.answer_pass": "Richtige Antwort", - "addon.mod_h5pactivity.attempt": "Versuch", - "addon.mod_h5pactivity.attempt_completion_no": "Dieser Versuch ist nicht als abgeschlossen gekennzeichnet", - "addon.mod_h5pactivity.attempt_completion_yes": "Dieser Versuch ist abgeschlossen", - "addon.mod_h5pactivity.attempt_success_fail": "Nicht bestanden", - "addon.mod_h5pactivity.attempt_success_pass": "Bestanden", - "addon.mod_h5pactivity.attempt_success_unknown": "Nicht gemeldet", - "addon.mod_h5pactivity.attempts_none": "Nutzer/in hat keine Versuche anzuzeigen", - "addon.mod_h5pactivity.completion": "Vollständigkeit", - "addon.mod_h5pactivity.downloadh5pfile": "H5P-Datei herunterladen", - "addon.mod_h5pactivity.duration": "Dauer", - "addon.mod_h5pactivity.errorgetactivity": "Fehler beim Laden der H5P-Aktivitätsdaten", - "addon.mod_h5pactivity.filestatenotdownloaded": "Das H5P-Paket ist nicht heruntergeladen. Sie müssen es zuerst herunterladen, um es nutzen zu können.", - "addon.mod_h5pactivity.filestateoutdated": "Das H5P-Paket wurde nach Ihrem letzten Herunterladen verändert. Du musst es zuerst herunterladen, um es nutzen zu können.", - "addon.mod_h5pactivity.maxscore": "Maximale Punktzahl", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Meine Versuche", - "addon.mod_h5pactivity.no_compatible_track": "Diese Interaktion ({{$a}}) liefert keine Tracking-Informationen oder das bereitgestellte Tracking ist nicht kompatibel mit der aktuellen Aktivitätsversion.", - "addon.mod_h5pactivity.offlinedisabledwarning": "Sie müssen online sein, um das H5P-Paket anzeigen zu können.", - "addon.mod_h5pactivity.outcome": "Ergebnis", - "addon.mod_h5pactivity.previewmode": "Dieser Inhalt wird im Vorschaumodus angezeigt. Es wird keine Versuchsverfolgung gespeichert.", - "addon.mod_h5pactivity.result_fill-in": "Lückentext", - "addon.mod_h5pactivity.result_other": "Unbekannter Interaktionstyp", - "addon.mod_h5pactivity.review_my_attempts": "Meine Versuche ansehen", - "addon.mod_h5pactivity.score": "Punktzahl", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} von {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Startdatum", - "addon.mod_h5pactivity.totalscore": "Gesamtpunktzahl", - "addon.mod_h5pactivity.viewattempt": "Versuch {{$a}} anzeigen", - "addon.mod_imscp.deploymenterror": "Fehler im IMS-Content!", - "addon.mod_imscp.modulenameplural": "IMS-Content", - "addon.mod_imscp.showmoduledescription": "Beschreibung anzeigen", - "addon.mod_imscp.toc": "Inhaltsverzeichnis", - "addon.mod_lesson.answer": "Antwort", - "addon.mod_lesson.attempt": "Versuch: {{$a}}", - "addon.mod_lesson.attemptheader": "Versuch", - "addon.mod_lesson.attemptsremaining": "Verbleibende Versuche: {{$a}}", - "addon.mod_lesson.averagescore": "Durchschnittliche Bewertung", - "addon.mod_lesson.averagetime": "Durchschnittliche Zeit", - "addon.mod_lesson.branchtable": "Inhaltsseite", - "addon.mod_lesson.cannotfindattempt": "Fehler: Versuch konnte nicht gefunden werden", - "addon.mod_lesson.cannotfinduser": "Fehler: Nutzer/innen konnten nicht gefunden werden", - "addon.mod_lesson.clusterjump": "Ungesehene Frage innerhalb des Clusters", - "addon.mod_lesson.completed": "Abgeschlossen", - "addon.mod_lesson.congratulations": "Das Ende der Lektion ist erreicht!", - "addon.mod_lesson.continue": "Fortsetzen", - "addon.mod_lesson.continuetonextpage": "Auf der nächsten Seite fortsetzen", - "addon.mod_lesson.defaultessayresponse": "Die Freitext-Antwort wird später bewertet.", - "addon.mod_lesson.detailedstats": "Ergebnisanalyse", - "addon.mod_lesson.didnotanswerquestion": "Hat diese Frage nicht beantwortet.", - "addon.mod_lesson.displayofgrade": "Anzeige der Bewertungen (für Teilnehmer/innen)", - "addon.mod_lesson.displayscorewithessays": "

        Sie haben bisher für die automatisch bewerteten Fragen die Bewertung {{$a.score}} von {{$a.tempmaxgrade}} erzielt.

        \n

        Ihre Freitext-Aufgaben ({{$a.essayquestions}}) werden später bewertet und zur Gesamtbewertung hinzugefügt.

        \n

        Ihre derzeitige Bewertung ohne die Freitextaufgaben ist {{$a.score}} von {{$a.grade}}.

        ", - "addon.mod_lesson.displayscorewithoutessays": "Ihre Bewertung: {{$a.score}} von {{$a.grade}}", - "addon.mod_lesson.emptypassword": "Das Kennwort darf nicht leer sein.", - "addon.mod_lesson.enterpassword": "Geben Sie bitte das Kennwort ein:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Sie haben keine Fragen beantwortet. Sie erhalten deswegen 0 Punkte für die Lektion.", - "addon.mod_lesson.errorprefetchrandombranch": "Diese Lektion enthält einen Sprung zu einer zufälligen Seite. Die Lektion kann in der App nicht versucht werden, ohne im Webbrowser begonnen worden zu sein.", - "addon.mod_lesson.errorreviewretakenotlast": "Dieser Versuch kann nicht mehr angesehen werden, weil ein weiterer Versuch beendet wurde.", - "addon.mod_lesson.finish": "Fertigstellen", - "addon.mod_lesson.finishretakeoffline": "Dieser Versuch wurde offline beendet.", - "addon.mod_lesson.firstwrong": "Ihre Antwort ist falsch. Möchten Sie es noch einmal (ohne Bewertung) versuchen?", - "addon.mod_lesson.gotoendoflesson": "Zum Lektionende gehen", - "addon.mod_lesson.grade": "Relative Bewertung", - "addon.mod_lesson.highscore": "Beste Bewertung", - "addon.mod_lesson.hightime": "Längste Zeit", - "addon.mod_lesson.leftduringtimed": "Sie haben die Lektion abgebrochen. Die Bearbeitung der Lektion ist zeitlich begrenzt.
        Klicken Sie auf Fortsetzen, um die Lektion erneut zu beginnen.", - "addon.mod_lesson.leftduringtimednoretake": "Sie haben die Lektion abgebrochen. Die Bearbeitung der Lektion ist zeitlich begrenzt.
        Sie können diese Lektion nicht weiter bearbeiten.", - "addon.mod_lesson.lessonmenu": "Inhaltsverzeichnis", - "addon.mod_lesson.lessonstats": "Statistik", - "addon.mod_lesson.linkedmedia": "Verlinkte Medien", - "addon.mod_lesson.loginfail": "Der Login ist gescheitert. Versuchen Sie es bitte noch einmal.", - "addon.mod_lesson.lowscore": "Schlechteste Bewertung", - "addon.mod_lesson.lowtime": "Niedrigste Zeit", - "addon.mod_lesson.maximumnumberofattemptsreached": "Sie haben die Höchstzahl der Versuche erreicht - weiter zur nächsten Seite", - "addon.mod_lesson.modattemptsnoteacher": "Navigation nur für Teilnehmer/innen möglich", - "addon.mod_lesson.modulenameplural": "Lektionen", - "addon.mod_lesson.noanswer": "Eine oder mehrere Fragen wurden nicht beantwortet. Gehen Sie zurück und geben Sie Antworten ein.", - "addon.mod_lesson.nolessonattempts": "Bisher keine Versuche", - "addon.mod_lesson.nolessonattemptsgroup": "Keines der {{$a}} Gruppenmitglieder hat zu dieser Lektion einen Versuch gemacht.", - "addon.mod_lesson.notcompleted": "Nicht beendet", - "addon.mod_lesson.numberofcorrectanswers": "Richtige Antworten: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Beantwortete Fragen: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Zahl der beantworteten Fragen: {{$a.nquestions}} (mindestens zu beantworten: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Gesamtpunktzahl: {{$a.score}} / {{$a.currenthigh}}", - "addon.mod_lesson.ongoingnormal": "Richtige Antworten: {{$a.correct}} / {{$a.viewed}}", - "addon.mod_lesson.or": "ODER", - "addon.mod_lesson.overview": "Übersicht", - "addon.mod_lesson.preview": "Vorschau", - "addon.mod_lesson.progressbarteacherwarning2": "Die Fortschrittsanzeige wird Ihnen nicht angezeigt, weil Sie diese Lektion als Trainer/in bearbeiten können.", - "addon.mod_lesson.progresscompleted": "Sie haben {{$a}}% der Lektion erledigt.", - "addon.mod_lesson.question": "Frage", - "addon.mod_lesson.rawgrade": "Absolute Bewertung", - "addon.mod_lesson.reports": "Ergebnisse", - "addon.mod_lesson.response": "Feedback", - "addon.mod_lesson.retakefinishedinsync": "Ein Offline-Versuch wurde synchronisiert. Möchten Sie ihn erneut prüfen?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Rückschau", - "addon.mod_lesson.reviewlesson": "Zur Lektion", - "addon.mod_lesson.reviewquestionback": "Wiederholen", - "addon.mod_lesson.reviewquestioncontinue": "Fortsetzen", - "addon.mod_lesson.secondpluswrong": "Nicht ganz. Möchten Sie es noch einmal versuchen?", - "addon.mod_lesson.submit": "Einreichen", - "addon.mod_lesson.teacherjumpwarning": "In der Lektion werden '{{$a.cluster}}'-Sprünge und/oder '{{$a.unseen}}'-Sprünge verwendet. Diese Sprünge werden durch 'Nächste Seite'-Sprünge ersetzt. Melden Sie sich als Teilnehmer/in an, um die Sprünge zu testen.", - "addon.mod_lesson.teacherongoingwarning": "Die aktuelle Bewertung wird nur für Teilnehmer/innen angezeigt. Melden Sie sich als Teilnehmer/in an, um diese Funktion zu testen.", - "addon.mod_lesson.teachertimerwarning": "Die Zeitbegrenzung funktioniert nur für Teilnehmer/innen. Melden Sie sich als Teilnehmer/in an, um diese Funktion zu testen.", - "addon.mod_lesson.thatsthecorrectanswer": "Richtig", - "addon.mod_lesson.thatsthewronganswer": "Falsch", - "addon.mod_lesson.timeremaining": "Verbleibende Zeit", - "addon.mod_lesson.timetaken": "Aufgewendete Zeit", - "addon.mod_lesson.unseenpageinbranch": "Noch nicht angezeigte Frage innerhalb des Zweiges", - "addon.mod_lesson.warningretakefinished": "Der Versuch wurde auf der Website beendet.", - "addon.mod_lesson.welldone": "Glückwunsch!", - "addon.mod_lesson.youhaveseen": "Sie haben einige Seiten der Lektion schon einmal bearbeitet.
        Möchten Sie an der Stelle weitermachen, an der Sie damals aufgehört haben?", - "addon.mod_lesson.youranswer": "Ihre Antwort", - "addon.mod_lesson.yourcurrentgradeisoutof": "Ihre derzeitige Bewertung ist {{$a.grade}} von {{$a.total}}", - "addon.mod_lesson.youshouldview": "mindestens zu beantworten: {{$a}}", - "addon.mod_lti.errorgetlti": "Fehler beim Laden der Moduldaten", - "addon.mod_lti.errorinvalidlaunchurl": "Die Launch-URL ist ungültig.", - "addon.mod_lti.launchactivity": "Aktivität starten", - "addon.mod_lti.modulenameplural": "Externe Tools", - "addon.mod_page.errorwhileloadingthepage": "Fehler beim Laden des Seiteninhalts", - "addon.mod_page.modulenameplural": "Textseiten", - "addon.mod_quiz.answercolon": "Antwort:", - "addon.mod_quiz.attemptfirst": "Erster Versuch", - "addon.mod_quiz.attemptlast": "Letzter Versuch", - "addon.mod_quiz.attemptnumber": "Versuch", - "addon.mod_quiz.attemptquiznow": "Test jetzt durchführen", - "addon.mod_quiz.attemptstate": "Status", - "addon.mod_quiz.canattemptbutnotsubmit": "Sie können einen Testversuch in der App beginnen, aber Sie müssen ihn aus folgenden Gründen im Webbrowser abgeben:", - "addon.mod_quiz.cannotsubmitquizdueto": "Der Test kann aus folgenden Gründen nicht abgegeben werden:", - "addon.mod_quiz.clearchoice": "Meine Auswahl widerrufen", - "addon.mod_quiz.comment": "Kommentar", - "addon.mod_quiz.completedon": "Beendet am", - "addon.mod_quiz.confirmclose": "Sobald Sie diesen Versuch beenden, können Sie Ihre Antworten nicht mehr bearbeiten.", - "addon.mod_quiz.confirmcontinueoffline": "Dieser Versuch wurde seit {{$a}} nicht synchronisiert. Falls Sie inzwischen mit einem anderen Gerät weiter gearbeitet haben, könnten Daten verloren gehen.", - "addon.mod_quiz.confirmleavequizonerror": "Fehler beim Speichern der Antworten. Möchten Sie den Test wirklich verlassen?", - "addon.mod_quiz.confirmstart": "Der Test hat ein Zeitlimit von {{$a}}. Die Zeit wird ab dem Start des Versuchs heruntergezählt. Der Test muss vor dem Ende des Zeitlimits abgeschlossen sein. Möchten Sie den Test wirklich jetzt beginnen?", - "addon.mod_quiz.confirmstartheader": "Zeitlimit", - "addon.mod_quiz.connectionerror": "Netzwerkverbindung verloren. Das automatische Speichern ist fehlgeschlagen.\n\nNotieren Sie sich alle in den letzten Minuten eingegebenen Antworten und versuchen Sie, die Verbindung wieder herzustellen.\n\nSobald die Verbindung wiederhergestellt wurde, sollten Sie die Antworten erneut speichern. Diese Nachricht verschwindet dann.", - "addon.mod_quiz.continueattemptquiz": "Letzten Versuch fortsetzen", - "addon.mod_quiz.continuepreview": "Letzte Vorschau fortsetzen", - "addon.mod_quiz.errorbehaviournotsupported": "Der Test kann nicht in der App durchgeführt werden, weil das Frageverhalten nicht unterstützt wird:", - "addon.mod_quiz.errordownloading": "Fehler beim Laden notwendiger Daten", - "addon.mod_quiz.errorgetattempt": "Fehler beim Laden der Versuche", - "addon.mod_quiz.errorgetquestions": "Fehler beim Laden der Fragen", - "addon.mod_quiz.errorgetquiz": "Fehler beim Laden der Testdaten", - "addon.mod_quiz.errorparsequestions": "Fehler beim Darstellen der Fragen. Versuchen Sie, diesen Test in einem Browser zu öffnen.", - "addon.mod_quiz.errorquestionsnotsupported": "Dieser Test wird in der App nicht ausgeführt, weil er von der App nicht unterstützte Fragen enthält:", - "addon.mod_quiz.errorrulesnotsupported": "Dieser Test wird in der App nicht ausgeführt, weil er von der App nicht unterstützte Zugriffsregeln enthält:", - "addon.mod_quiz.errorsaveattempt": "Fehler beim Speichern des Versuchs", - "addon.mod_quiz.feedback": "Feedback", - "addon.mod_quiz.finishattemptdots": "Versuch beenden...", - "addon.mod_quiz.finishnotsynced": "Beendet, aber nicht synchronisiert", - "addon.mod_quiz.grade": "Bewertung", - "addon.mod_quiz.gradeaverage": "Durchschnitt", - "addon.mod_quiz.gradehighest": "Bester Versuch", - "addon.mod_quiz.grademethod": "Bewertungsmethode", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Punkte", - "addon.mod_quiz.modulenameplural": "Tests", - "addon.mod_quiz.mustbesubmittedby": "Dieser Versuch muss abgegeben werden vor {{$a}}.", - "addon.mod_quiz.noquestions": "Es wurden noch keine Fragen eingetragen.", - "addon.mod_quiz.noreviewattempt": "Sie dürfen diesen Versuch nicht erneut prüfen.", - "addon.mod_quiz.notyetgraded": "Bisher nicht bewertet", - "addon.mod_quiz.opentoc": "Navigationsfeld öffnen", - "addon.mod_quiz.outof": "{{$a.grade}} von {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} von {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Gesamtfeedback", - "addon.mod_quiz.overdue": "Überfällig", - "addon.mod_quiz.overduemustbesubmittedby": "Der Versuch ist überfällig und sollte bereits abgegeben worden sein. Wenn Sie möchten, dass Ihr Testversuch bewertet wird, müssen Sie ihn abgeben bis {{$a}}. Falls Sie dies nicht tun, wird keine Bewertung für diesen Versuch gezählt.", - "addon.mod_quiz.preview": "Vorschau", - "addon.mod_quiz.previewquiznow": "Vorschau ansehen", - "addon.mod_quiz.question": "Frage", - "addon.mod_quiz.quiznavigation": "Test-Navigation", - "addon.mod_quiz.quizpassword": "Testkennwort", - "addon.mod_quiz.reattemptquiz": "Test wiederholen", - "addon.mod_quiz.requirepasswordmessage": "Zur Teilnahme am Test benötigen Sie ein Kennwort.", - "addon.mod_quiz.returnattempt": "Zurück zum Versuch", - "addon.mod_quiz.review": "Überprüfung", - "addon.mod_quiz.reviewofattempt": "Überprüfung des Versuchs {{$a}}", - "addon.mod_quiz.reviewofpreview": "Vorschauansicht überprüfen", - "addon.mod_quiz.showall": "Alle Fragen auf einer Seite anzeigen", - "addon.mod_quiz.showeachpage": "Seiten einzeln anzeigen", - "addon.mod_quiz.startattempt": "Versuch beginnen", - "addon.mod_quiz.startedon": "Begonnen am", - "addon.mod_quiz.stateabandoned": "Nie abgegeben", - "addon.mod_quiz.statefinished": "Beendet", - "addon.mod_quiz.statefinisheddetails": "Abgegeben {{$a}}", - "addon.mod_quiz.stateinprogress": "In Bearbeitung", - "addon.mod_quiz.stateoverdue": "Überfällig", - "addon.mod_quiz.stateoverduedetails": "Muss abgegeben werden bis {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Abgabe", - "addon.mod_quiz.summaryofattempt": "Zusammenfassung der Versuche", - "addon.mod_quiz.summaryofattempts": "Zusammenfassung der vorherigen Versuche", - "addon.mod_quiz.timeleft": "Verbleibende Zeit", - "addon.mod_quiz.timetaken": "Verbrauchte Zeit", - "addon.mod_quiz.warningattemptfinished": "Der Offline-Versuch wurde verworfen, weil er auf der Website beendet wurde oder nicht mehr existiert.", - "addon.mod_quiz.warningdatadiscarded": "Einige Offline-Antworten könnten verworfen worden sein, weil die Fragen online verändert wurden.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Der Versuch wurde nicht beendet, weil Offline-Antworten verworfen wurden. Überprüfen Sie Ihre Antworten und übermitteln Sie den Versuch noch einmal.", - "addon.mod_quiz.warningquestionsnotsupported": "Dieser Test enthält Fragen, die nicht von der App unterstützt werden:", - "addon.mod_quiz.yourfinalgradeis": "Ihre Gesamtbewertung für diesen Test: {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Fehler beim Laden des Inhalts", - "addon.mod_resource.modifieddate": "Geändert {{$a}}", - "addon.mod_resource.modulenameplural": "Dateien", - "addon.mod_resource.openthefile": "Datei öffnen", - "addon.mod_resource.uploadeddate": "Hochgeladen {{$a}}", - "addon.mod_scorm.asset": "Anlage", - "addon.mod_scorm.assetlaunched": "Anlage gesehen", - "addon.mod_scorm.attempts": "Versuche", - "addon.mod_scorm.averageattempt": "Durchschnitt", - "addon.mod_scorm.browse": "Vorschau", - "addon.mod_scorm.browsed": "Durchsucht", - "addon.mod_scorm.browsemode": "Vorschaumodus", - "addon.mod_scorm.cannotcalculategrade": "Die Bewertung konnte nicht berechnet werden.", - "addon.mod_scorm.completed": "Abgeschlossen", - "addon.mod_scorm.contents": "Inhalte", - "addon.mod_scorm.dataattemptshown": "Diese Daten gehören zu Versuch {{number}}.", - "addon.mod_scorm.enter": "Start", - "addon.mod_scorm.errorcreateofflineattempt": "Fehler beim Hinzufügen eines Offline-Versuchs. Versuchen Sie es noch einmal.", - "addon.mod_scorm.errordownloadscorm": "Fehler beim Laden des Lernpakets '{{name}}'.", - "addon.mod_scorm.errorgetscorm": "Fehler beim Laden von Lernpaketdaten", - "addon.mod_scorm.errorinvalidversion": "Die App unterstützt nur Lernpakete mit SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Das Herunterladen von Lernpaketen ist für diese Website deaktiviert. Fragen Sie den Administrator.", - "addon.mod_scorm.errornovalidsco": "Dieses Lernpaket hat kein sichtbares SCO zum Laden.", - "addon.mod_scorm.errorpackagefile": "Die App unterstützt ausschließlich ZIP-Pakete.", - "addon.mod_scorm.errorsyncscorm": "Fehler beim Synchronisieren. Versuchen Sie es noch einmal.", - "addon.mod_scorm.exceededmaxattempts": "Sie haben die maximale Anzahl von Versuchen erreicht.", - "addon.mod_scorm.failed": "Fehlgeschlagen", - "addon.mod_scorm.firstattempt": "Erster Versuch", - "addon.mod_scorm.gradeaverage": "Durchschnittsnote", - "addon.mod_scorm.gradeforattempt": "Bewertung für Versuch", - "addon.mod_scorm.gradehighest": "Höchstnote", - "addon.mod_scorm.grademethod": "Bewertungsmethode", - "addon.mod_scorm.gradereported": "Bewertung veröffentlicht", - "addon.mod_scorm.gradescoes": "Zahl der Lernobjekte", - "addon.mod_scorm.gradesum": "Summe der Bewertungen", - "addon.mod_scorm.highestattempt": "Bester Versuch", - "addon.mod_scorm.incomplete": "Unvollständig", - "addon.mod_scorm.lastattempt": "Letzter vollständiger Versuch", - "addon.mod_scorm.modulenameplural": "Lernpakete", - "addon.mod_scorm.newattempt": "Neuen Versuch beginnen", - "addon.mod_scorm.noattemptsallowed": "Zahl zulässiger Versuche", - "addon.mod_scorm.noattemptsmade": "Zahl Ihrer Versuche", - "addon.mod_scorm.notattempted": "Nicht versucht", - "addon.mod_scorm.offlineattemptnote": "Dieser Versuch enthält Daten, die noch nicht synchronisiert wurden.", - "addon.mod_scorm.offlineattemptovermax": "Dieser Versuch kann nicht gesendet werden, weil Sie die maximale Anzahl von Versuchen überschritten haben.", - "addon.mod_scorm.organizations": "Organisationen", - "addon.mod_scorm.passed": "Abgeschlossen", - "addon.mod_scorm.reviewmode": "Rückblick-Modus", - "addon.mod_scorm.score": "Bewertung", - "addon.mod_scorm.scormstatusnotdownloaded": "Dieses Lernpaket ist noch nicht geladen. Es wird automatisch heruntergeladen, wenn Sie es öffnen.", - "addon.mod_scorm.scormstatusoutdated": "Dieses Lernpaket wurde seit dem Herunterladen verändert. Es wird automatisch geladen, wenn Sie es öffnen.", - "addon.mod_scorm.suspended": "Ausgeschlossen", - "addon.mod_scorm.toc": "Inhaltsverzeichnis", - "addon.mod_scorm.warningofflinedatadeleted": "Die Offline-Daten von Versuch {{number}} wurden gelöscht, weil sie nicht als neuer Versuch gezählt werden können.", - "addon.mod_scorm.warningsynconlineincomplete": "Einige Versuche konnten nicht synchronisiert werden, weil der letzte Online-Versuch nicht beendet wurde. Beenden Sie zuerst den Online-Versuch.", - "addon.mod_survey.cannotsubmitsurvey": "Fehler beim Übertragen der Umfragedaten. Versuchen Sie es noch einmal.", - "addon.mod_survey.errorgetsurvey": "Fehler beim Laden der Umfragedaten", - "addon.mod_survey.ifoundthat": "Dies findet statt:", - "addon.mod_survey.ipreferthat": "Ich bevorzuge es so:", - "addon.mod_survey.modulenameplural": "Umfragen", - "addon.mod_survey.responses": "Antworten", - "addon.mod_survey.results": "Ergebnisse", - "addon.mod_survey.surveycompletednograph": "Sie haben diese Umfrage abgeschlossen.", - "addon.mod_url.accessurl": "URL öffnen", - "addon.mod_url.modulenameplural": "Links/URLs", - "addon.mod_url.pointingtourl": "Verlinkte URL", - "addon.mod_wiki.cannoteditpage": "Sie können diese Seite nicht bearbeiten.", - "addon.mod_wiki.createpage": "Seite erstellen", - "addon.mod_wiki.editingpage": "Die Seite '{{$a}}' wird bearbeitet", - "addon.mod_wiki.errorloadingpage": "Fehler beim Laden der Seite", - "addon.mod_wiki.errornowikiavailable": "Dieses Wiki hat noch keinen Inhalt.", - "addon.mod_wiki.gowikihome": "Zur Wiki-Startseite", - "addon.mod_wiki.map": "Spezialseiten", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Neue Seite", - "addon.mod_wiki.newpagetitle": "Titel für neue Seite\n", - "addon.mod_wiki.nocontent": "Kein Inhalt auf dieser Seite", - "addon.mod_wiki.notingroup": "Nicht in Gruppen", - "addon.mod_wiki.pageexists": "Diese Seite existiert bereits.", - "addon.mod_wiki.pagename": "Seitenname", - "addon.mod_wiki.subwiki": "Teilwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wikiseiten", - "addon.mod_wiki.titleshouldnotbeempty": "Der Titel darf nicht leer sein.", - "addon.mod_wiki.viewpage": "Seite anzeigen", - "addon.mod_wiki.wikipage": "Wiki-Seite", - "addon.mod_wiki.wrongversionlock": "Jemand anderes hat die Seite aktualisiert, während Sie daran gearbeitet haben. Deswegen ist der von Ihnen verwendete Seiteninhalt veraltet.", - "addon.mod_workshop.alreadygraded": "Bereits bewertet", - "addon.mod_workshop.areainstructauthors": "Anleitungen für die Einreichung", - "addon.mod_workshop.areainstructreviewers": "Hinweise zur Beurteilung", - "addon.mod_workshop.assess": "Beurteilen", - "addon.mod_workshop.assessedsubmission": "Beurteilte Einreichung", - "addon.mod_workshop.assessmentform": "Beurteilungsbogen", - "addon.mod_workshop.assessmentsettings": "Beurteilungseinstellungen", - "addon.mod_workshop.assessmentstrategynotsupported": "Die Beurteilungsstrategie {{$a}} wird nicht unterstützt.", - "addon.mod_workshop.assessmentweight": "Gewichtung Beurteilung", - "addon.mod_workshop.assignedassessments": "Zur Beurteilung zugeordnete Einreichungen", - "addon.mod_workshop.assignedassessmentsnone": "Sie haben keine zugeordneten Einreichungen zu beurteilen", - "addon.mod_workshop.conclusion": "Abschluss", - "addon.mod_workshop.createsubmission": "Einreichung hinzufügen", - "addon.mod_workshop.deletesubmission": "Einreichung löschen", - "addon.mod_workshop.editsubmission": "Abgabe bearbeiten", - "addon.mod_workshop.feedbackauthor": "Rückmeldung an den / die Autor/in", - "addon.mod_workshop.feedbackby": "Beurteilung von {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Rückmeldung an den/die Beurteiler/in", - "addon.mod_workshop.givengrades": "Vergebene Bewertungen", - "addon.mod_workshop.gradecalculated": "Berechnete Bewertung für Einreichung", - "addon.mod_workshop.gradeinfo": "Bewertung: {{$a.received}} von {{$a.max}}", - "addon.mod_workshop.gradeover": "Bewertung für die Einreichung überschreiben", - "addon.mod_workshop.gradesreport": "Bewertungsbericht für gegenseitige Beurteilung", - "addon.mod_workshop.gradinggrade": "Bewertung für die Beurteilung", - "addon.mod_workshop.gradinggradecalculated": "Berechnete Bewertung für die Beurteilung", - "addon.mod_workshop.gradinggradeof": "Bewertung für die Beurteilung (von {{$a}})", - "addon.mod_workshop.gradinggradeover": "Bewertung für die Beurteilung überschreiben", - "addon.mod_workshop.modulenameplural": "Gegenseitige Beurteilungen", - "addon.mod_workshop.nogradeyet": "Bisher keine Bewertung", - "addon.mod_workshop.notassessed": "Bisher nicht beurteilt", - "addon.mod_workshop.notoverridden": "Nicht überschrieben", - "addon.mod_workshop.noyoursubmission": "Sie haben Ihre Einreichung bisher nicht übermittelt", - "addon.mod_workshop.overallfeedback": "Gesamtfeedback", - "addon.mod_workshop.publishedsubmissions": "Veröffentlichte Einreichungen", - "addon.mod_workshop.publishsubmission": "Einreichung veröffentlichen", - "addon.mod_workshop.publishsubmission_help": "Wenn die gegenseitige Beurteilung beendet ist, werden die Einreichungen den anderen Teilnehmer/innen veröffentlicht.", - "addon.mod_workshop.reassess": "Erneut beurteilen", - "addon.mod_workshop.receivedgrades": "Erhaltene Bewertung", - "addon.mod_workshop.submissionattachment": "Dateianhang", - "addon.mod_workshop.submissioncontent": "Einreichungsinhalt", - "addon.mod_workshop.submissiondeleteconfirm": "Möchten Sie wirklich die folgende Einreichung löschen?", - "addon.mod_workshop.submissiongrade": "Bewertung für die Einreichung", - "addon.mod_workshop.submissiongradeof": "Bewertung für die Einreichung (von {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Text eingeben oder Datei hinzufügen", - "addon.mod_workshop.submissionrequiredtitle": "Sie müssen einen Titel eingeben.", - "addon.mod_workshop.submissionsreport": "Einreichungsübersicht für gegenseitige Beurteilung", - "addon.mod_workshop.submissiontitle": "Titel", - "addon.mod_workshop.switchphase10": "In Vorbereitungsphase wechseln", - "addon.mod_workshop.switchphase20": "In Einreichungsphase wechseln", - "addon.mod_workshop.switchphase30": "In Beurteilungsphase wechseln", - "addon.mod_workshop.switchphase40": "In Bewertungsphase wechseln", - "addon.mod_workshop.switchphase50": "Gegenseitige Beurteilung abschließen", - "addon.mod_workshop.userplan": "Navigator für gegenseitige Beurteilung", - "addon.mod_workshop.userplancurrentphase": "Aktuelle Phase", - "addon.mod_workshop.warningassessmentmodified": "Die Abgabe wurde auf der Website geändert.", - "addon.mod_workshop.warningsubmissionmodified": "Die Beurteilung wurde auf der Website geändert.", - "addon.mod_workshop.weightinfo": "Gewichtung: {{$a}}", - "addon.mod_workshop.yourassessment": "Ihre Beurteilung", - "addon.mod_workshop.yourassessmentfor": "Ihre Beurteilung für {{$a}}", - "addon.mod_workshop.yourgrades": "Ihre Bewertungen", - "addon.mod_workshop.yoursubmission": "Ihre Einreichung", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Kommentar für {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Bewertung für {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Kriterium {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Sie müssen eine Bewertung für dieses Kriterium vergeben.", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Kommentar für {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Kriterium {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Kommentar für {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Bewertung für {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Kriterium", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kriterium {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Sie müssen eines dieser Elemente auswählen", - "addon.notes.addnewnote": "Neue Notiz hinzufügen", - "addon.notes.coursenotes": "Notiz nur für Trainer/innen im Kurs sichtbar", - "addon.notes.deleteconfirm": "Diese Notiz löschen?", - "addon.notes.eventnotecreated": "Notiz angelegt", - "addon.notes.eventnotedeleted": "Notiz gelöscht", - "addon.notes.nonotes": "Es gibt bisher keine Notizen dieser Art.", - "addon.notes.note": "Notiz", - "addon.notes.notes": "Notizen", - "addon.notes.personalnotes": "Persönliche Notizen", - "addon.notes.publishstate": "Kontext", - "addon.notes.sitenotes": "Notizen für alle Trainer/innen sichtbar (kursübergreifend)", - "addon.notes.userwithid": "Nutzer/in mit ID {{id}}", - "addon.notes.warningnotenotsent": "Die Anmerkungen konnten nicht zum Kurs {{course}} hinzugefügt werden. {{error}}", - "addon.notifications.errorgetnotifications": "Fehler beim Empfangen von Systemnachrichten", - "addon.notifications.markallread": "Alle als gelesen markieren", - "addon.notifications.notificationpreferences": "Systemnachrichten", - "addon.notifications.notifications": "Systemnachrichten", - "addon.notifications.playsound": "Signalton abspielen", - "addon.notifications.therearentnotificationsyet": "Keine Systemnachrichten", - "addon.storagemanager.deletecourse": "Alle Kursdaten entladen", - "addon.storagemanager.deletecourses": "Alle Kursdaten entladen", - "addon.storagemanager.deletedatafrom": "Daten von {{name}} entladen", - "addon.storagemanager.info": "Auf Ihrem Gerät gespeicherte Daten beschleunigen die Arbeit und ermöglichen die Offline-Verwendung der App. Sie können Daten entladen, wenn Sie Speicherplatz freigeben müssen.", - "addon.storagemanager.managestorage": "Speicher verwalten", - "addon.storagemanager.storageused": "Verwendeter Dateispeicher:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Vereinigte Arabische Emirate", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua und Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albanien", - "assets.countries.AM": "Armenien", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktis", - "assets.countries.AR": "Argentinien", - "assets.countries.AS": "Amerikanisch Samoa", - "assets.countries.AT": "Österreich", - "assets.countries.AU": "Australien", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland Inseln", - "assets.countries.AZ": "Aserbaidschan", - "assets.countries.BA": "Bosnien Herzegowina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesch", - "assets.countries.BE": "Belgien", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgarien", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "St. Bartholomäus", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei Darussalam", - "assets.countries.BO": "Bolivien", - "assets.countries.BQ": "Bonaire, Sint Eustatius und Saba", - "assets.countries.BR": "Brasilien", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvet Insel", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Weißrussland", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Kokosinseln", - "assets.countries.CD": "Kongo (Demokratische Republik)", - "assets.countries.CF": "Zentralafrikanische Republik", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Schweiz", - "assets.countries.CI": "Elfenbeinküste", - "assets.countries.CK": "Cook Inseln", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "China", - "assets.countries.CO": "Kolumbien", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Cabo Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Weihnachtsinseln", - "assets.countries.CY": "Zypern", - "assets.countries.CZ": "Tschechien", - "assets.countries.DE": "Deutschland", - "assets.countries.DJ": "Djibuti", - "assets.countries.DK": "Dänemark", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Dominikanische Republik", - "assets.countries.DZ": "Algerien", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estland", - "assets.countries.EG": "Ägypten", - "assets.countries.EH": "Westsahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spanien", - "assets.countries.ET": "Äthiopien", - "assets.countries.FI": "Finnland", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Falkland Inseln (Malvinas)", - "assets.countries.FM": "Mikronesien (Föderierte Staaten)", - "assets.countries.FO": "Färöer-Inseln", - "assets.countries.FR": "Frankreich", - "assets.countries.GA": "Gabun", - "assets.countries.GB": "Großbritannien", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgien", - "assets.countries.GF": "Französisch Guayana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grönland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Äquatorial Guinea", - "assets.countries.GR": "Griechenland", - "assets.countries.GS": "Südgeorgien und Südliche Sandwichinseln", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hongkong", - "assets.countries.HM": "Heard-Inseln und McDonald-Inseln", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Kroatien", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Ungarn", - "assets.countries.ID": "Indonesien", - "assets.countries.IE": "Irland", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Isle of Man", - "assets.countries.IN": "Indien", - "assets.countries.IO": "Britisches Territorium im Indischen Ozean", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran (Islamische Republik)", - "assets.countries.IS": "Island", - "assets.countries.IT": "Italien", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaika", - "assets.countries.JO": "Jordanien", - "assets.countries.JP": "Japan", - "assets.countries.KE": "Kenia", - "assets.countries.KG": "Kirgistan", - "assets.countries.KH": "Kambodscha", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komoren", - "assets.countries.KN": "Saint Kitts und Nevis", - "assets.countries.KP": "Korea (Demokratische Volksrepublik)", - "assets.countries.KR": "Korea (Republik)", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Kaiman Inseln", - "assets.countries.KZ": "Kasachstan", - "assets.countries.LA": "Laos (Demokratische Volksrepublik)", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Santa Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litauen", - "assets.countries.LU": "Luxemburg", - "assets.countries.LV": "Lettland", - "assets.countries.LY": "Libyen", - "assets.countries.MA": "Marokko", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldavien", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin (F)", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshall Inseln", - "assets.countries.MK": "Nord Mazedonien", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolei", - "assets.countries.MO": "Makao", - "assets.countries.MP": "Nordmarinen", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauretanien", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Malediven", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexiko", - "assets.countries.MY": "Malaysia", - "assets.countries.MZ": "Mosambik", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Neukaledonien", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolk Inseln", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Niederlande", - "assets.countries.NO": "Norwegen", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Neuseeland", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Französich Polynesien", - "assets.countries.PG": "Papua Neuguinea", - "assets.countries.PH": "Philippinen", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polen", - "assets.countries.PM": "Saint Pierre und Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palästina (Staat)", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Rumänien", - "assets.countries.RS": "Serbien", - "assets.countries.RU": "Russland", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Saudi-Arabien", - "assets.countries.SB": "Salomon Inseln", - "assets.countries.SC": "Seychellen", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Schweden", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "Saint Helena, Ascension und Tristan Da Cunha", - "assets.countries.SI": "Slowenien", - "assets.countries.SJ": "Svalbard und Jan Mayen", - "assets.countries.SK": "Slowakei", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Süd-Sudan", - "assets.countries.ST": "Sao Tome und Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Saint Maarten (NL)", - "assets.countries.SY": "Syrien", - "assets.countries.SZ": "Eswatini", - "assets.countries.TC": "Turks und Caicos Inseln", - "assets.countries.TD": "Tschad", - "assets.countries.TF": "Französische Süd- und Antarktisgebiete", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailand", - "assets.countries.TJ": "Tadschikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunesien", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Türkei", - "assets.countries.TT": "Trinidad und Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tansania (Vereinigte Republik)", - "assets.countries.UA": "Ukraine", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Amerikanisch-Ozeanien", - "assets.countries.US": "Vereinigte Staaten von Amerika (USA)", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Usbekistan", - "assets.countries.VA": "Vatikanstaat", - "assets.countries.VC": "Saint Vincent und Grenadinen", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Virgin Islands (GB)", - "assets.countries.VI": "Virgin Islands (US)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis und Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Südafrika", - "assets.countries.ZM": "Sambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB-Buch", - "assets.mimetypes.application/msword": "Word Textdokument", - "assets.mimetypes.application/pdf": "PDF-Dokument", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle-Sicherung", - "assets.mimetypes.application/vnd.ms-excel": "Excel Tabellendokument", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 makroaktiviertes Workbook", - "assets.mimetypes.application/vnd.ms-powerpoint": "PowerPoint Präsentation", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument Tabellendokument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument Tabellenvorlage", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument Textdokument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument Textvorlage", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument Webseitenvorlage", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 Präsentation", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 Slideshow", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 Tabellendokument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 Tabellenvorlage", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 Textdokument", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote Präsentation", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers Tabelle", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages Dokument", - "assets.mimetypes.application/x-javascript": "Javascript Quelltext", - "assets.mimetypes.application/x-mspublisher": "Publisher Dokument", - "assets.mimetypes.application/x-shockwave-flash": "Flash Animation", - "assets.mimetypes.application/xhtml_xml": "XHTML Dokument", - "assets.mimetypes.archive": "Archiv ({{$a.EXT}})", - "assets.mimetypes.audio": "Audiodatei ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Datei", - "assets.mimetypes.group:archive": "Archivdateien", - "assets.mimetypes.group:audio": "Audiodateien", - "assets.mimetypes.group:document": "Dokumente", - "assets.mimetypes.group:html_audio": "Audiodateien, die von Browsern unterstützt werden", - "assets.mimetypes.group:html_track": "HTML Track Dateien", - "assets.mimetypes.group:html_video": "Videodateien, die von Browsern unterstützt werden", - "assets.mimetypes.group:image": "Bilddateien", - "assets.mimetypes.group:presentation": "Präsentationsdateien", - "assets.mimetypes.group:sourcecode": "Quelltext", - "assets.mimetypes.group:spreadsheet": "Tabellendateien", - "assets.mimetypes.group:video": "Videodateien", - "assets.mimetypes.group:web_audio": "Audiodateien für das Web", - "assets.mimetypes.group:web_file": "Webdateien", - "assets.mimetypes.group:web_image": "Bilddateien für das Web", - "assets.mimetypes.group:web_video": "Videodateien für das Web", - "assets.mimetypes.image": "Bilddatei ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows-Icon", - "assets.mimetypes.text/css": "Cascading Style-Sheet", - "assets.mimetypes.text/csv": "Kommagetrennte Werte", - "assets.mimetypes.text/html": "HTML-Dokument", - "assets.mimetypes.text/plain": "Unformatierte Textdatei", - "assets.mimetypes.text/rtf": "RTF-Dokument", - "assets.mimetypes.text/vtt": "Web Video Text Track", - "assets.mimetypes.video": "Video Datei ({{$a.EXT}})", - "core.accounts": "Nutzerkonten", - "core.add": "Hinzufügen", - "core.agelocationverification": "Überprüfung von Alter und Aufenthaltsort", - "core.ago": "{{$a}} alt", - "core.all": "Alle", - "core.allgroups": "Alle Gruppen", - "core.allparticipants": "Alle Teilnehmer/innen", - "core.answer": "Antwort", - "core.answered": "Beantwortet", - "core.areyousure": "Sind Sie sicher?", - "core.back": "Zurück", - "core.block.blocks": "Blöcke", - "core.browser": "Browser", - "core.cancel": "Abbrechen", - "core.cannotconnect": "Keine Verbindung", - "core.cannotconnecttrouble": "Wir haben Schwierigkeiten, eine Verbindung zu Ihrer Website herzustellen.", - "core.cannotconnectverify": "Prüfen Sie bitte, ob die Adresse richtig ist.", - "core.cannotdownloadfiles": "Das Herunterladen von Dateien ist deaktiviert. Wenden Sie sich an den Administrator.", - "core.captureaudio": "Audio aufnehmen", - "core.capturedimage": "Foto aufgenommen", - "core.captureimage": "Foto aufnehmen", - "core.capturevideo": "Video aufnehmen", - "core.category": "Kursbereich", - "core.choose": "Auswahl", - "core.choosedots": "Auswählen ...", - "core.clearsearch": "Suche löschen", - "core.clicktohideshow": "Zum Erweitern oder Einklappen tippen", - "core.clicktoseefull": "Tippen zum Anzeigen aller Inhalte", - "core.close": "Schließen", - "core.comments": "Kommentare", - "core.comments.addcomment": "Kommentar hinzufügen...", - "core.comments.comments": "Kommentare", - "core.comments.commentscount": "Kommentare ({{$a}})", - "core.comments.commentsnotworking": "Kommentare können nicht abgerufen werden", - "core.comments.deletecommentbyon": "Kommentar löschen, der von {{$a.user}} am {{$a.time}} gepostet wurde", - "core.comments.eventcommentcreated": "Kommentar angelegt", - "core.comments.eventcommentdeleted": "Kommentar gelöscht", - "core.comments.nocomments": "Keine Kommentare", - "core.comments.savecomment": "Kommentar speichern", - "core.comments.warningcommentsnotsent": "Kommentare konnten nicht synchronisiert werden. {{error}}", - "core.commentscount": "Kommentare ({{$a}})", - "core.completion-alt-auto-fail": "Abgeschlossen: {{$a}} (Bestehensgrenze nicht erreicht)", - "core.completion-alt-auto-n": "Nicht abgeschlossen: {{$a}}", - "core.completion-alt-auto-n-override": "Nicht abgeschlossen: {{$a.modname}} (gesetzt von {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Abgeschlossen: {{$a}} (Bestehensgrenze erreicht)", - "core.completion-alt-auto-y": "Abgeschlossen: {{$a}}", - "core.completion-alt-auto-y-override": "Abgeschlossen: {{$a.modname}} (gesetzt von {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Nicht abgeschlossen: {{$a}} - mit Auswahl als abgeschlossen markieren", - "core.completion-alt-manual-n-override": "Nicht abgeschlossen: {{$a.modname}} (gesetzt von {{$a.overrideuser}}). Wählen, um dies als abgeschlossen zu markieren.", - "core.completion-alt-manual-y": "Abgeschlossen: {{$a}} - mit Auswahl als nicht abgeschlossen markieren", - "core.completion-alt-manual-y-override": "Abgeschlossen: {{$a.modname}} (gesetzt von {{$a.overrideuser}}). Wählen, um dies als nicht abgeschlossen zu markieren.", - "core.confirmcanceledit": "Möchten Sie diese Seite wirklich verlassen? Alle Änderungen gehen verloren!", - "core.confirmdeletefile": "Möchten Sie diese Datei wirklich löschen?", - "core.confirmgotabroot": "Möchten Sie wirklich zu {{name}} zurückgehen?", - "core.confirmgotabrootdefault": "Möchten Sie wirklich zur initalen Seite des aktuellen Tabs zurückkehren?", - "core.confirmleaveunknownchanges": "Möchten Sie diese Seite wirklich verlassen? Wenn Sie ungesicherte Änderungen haben, gehen diese verloren.", - "core.confirmloss": "Möchten Sie wirklich alle Änderungen verlieren?", - "core.confirmopeninbrowser": "Möchten Sie dies im Webbrowser öffnen?", - "core.considereddigitalminor": "Sie sind zu jung, um ein Nutzerkonto für diese Website zu erstellen.", - "core.content": "Inhalt", - "core.contenteditingsynced": "Der Inhalt, den Sie gerade bearbeiten, wurde synchronisiert.", - "core.contentlinks.chooseaccount": "Nutzerkonto wählen", - "core.contentlinks.chooseaccounttoopenlink": "Nutzerkonto wählen, mit dem der Link geöffnet werden soll.", - "core.contentlinks.confirmurlothersite": "Dieser Link führt zu einer anderen Website. Möchten Sie den Link öffnen?", - "core.contentlinks.errornoactions": "Keine Aktion zu diesem Link gefunden", - "core.contentlinks.errornosites": "Keine Website zu diesem Link gefunden", - "core.contentlinks.errorredirectothersite": "Die URL-Weiterleitung kann nicht auf eine andere Website zeigen.", - "core.continue": "Weiter", - "core.copiedtoclipboard": "Text in die Zwischenablage kopiert", - "core.copytoclipboard": "In die Zwischenablage kopieren", - "core.course": "Kurs", - "core.course.activitydisabled": "Für die Website ist diese Aktivität in der mobilen App deaktiviert.", - "core.course.activitynotyetviewableremoteaddon": "Für die Website ist ein Plugin installiert, das bisher nicht unterstützt wird.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Diese Website muss aktualisiert werden.", - "core.course.allsections": "Alle Abschnitte", - "core.course.askadmintosupport": "Wenden Sie sich an den Administrator der Website, um diese Aktivität mit der mobilen App verwenden zu können.", - "core.course.availablespace": "Sie haben im Moment etwa {{available}} freien Speicher.", - "core.course.cannotdeletewhiledownloading": "Dateien können nicht gelöscht werden, während die Aktivität heruntergeladen wird. Warten Sie, bis das Herunterladen beendet ist.", - "core.course.confirmdeletemodulefiles": "Möchten Sie diese Dateien wirklich löschen?", - "core.course.confirmdownload": "Möchten Sie wirklich {{size}} herunterladen? {{availableSpace}}", - "core.course.confirmdownloadunknownsize": "Die Datenmenge kann nicht berechnet werden. Möchten Sie wirklich die Daten herunterladen? {{availableSpace}}", - "core.course.confirmdownloadzerosize": "Möchten Sie wirklich die Daten herunterladen? {{availableSpace}}", - "core.course.confirmlimiteddownload": "Sie sind im Moment nicht über WLAN verbunden.", - "core.course.confirmpartialdownloadsize": "Sie sind dabei, mindestens {{size}} herunterzuladen. {{availableSpace}} Möchten Sie wirklich weitermachen?", - "core.course.contents": "Inhalte", - "core.course.couldnotloadsectioncontent": "Die Abschnittsinhalte konnten nicht geladen werden. Versuchen Sie es später noch einmal.", - "core.course.couldnotloadsections": "Die Abschnitte konnten nicht geladen werden. Versuchen Sie es später noch einmal.", - "core.course.coursesummary": "Kursbeschreibung", - "core.course.downloadcourse": "Kurs herunterladen", - "core.course.errordownloadingcourse": "Fehler beim Herunterladen des Kurses", - "core.course.errordownloadingsection": "Fehler beim Herunterladen des Abschnitts", - "core.course.errorgetmodule": "Fehler beim Laden von Aktivitätsdaten", - "core.course.hiddenfromstudents": "Für Teilnehmer/innen verborgen", - "core.course.hiddenoncoursepage": "Verfügbar, aber auf der Kursseite verborgen", - "core.course.insufficientavailablequota": "Ihr Gerät konnte für diesen Download keinen Speicherplatz zuweisen. Möglicherweise ist Speicherplatz für anstehende App- oder Systemupdates reserviert. Sorgen Sie zuerst für ausreichend freien Speicher.", - "core.course.insufficientavailablespace": "Sie versuchen, {{size}} herunterzuladen. Ihr Gerät hat danach möglicherweise nicht genügend Platz, um normal zu funktionieren. Sorgen Sie zuerst für ausreichend freien Speicher.", - "core.course.manualcompletionnotsynced": "Manueller Abschluss wurde nicht synchronisiert", - "core.course.nocontentavailable": "Momentan sind keine Inhalte verfügbar", - "core.course.overriddennotice": "Die endgültige Bewertung zu dieser Aktivität wurde manuell bearbeitet.", - "core.course.refreshcourse": "Kurs neu laden", - "core.course.sections": "Abschnitte", - "core.course.useactivityonbrowser": "Sie können dies im Webbrowser verwenden.", - "core.course.warningmanualcompletionmodified": "Manueller Abschluss einer Aktivität wurde auf der Website geändert.", - "core.course.warningofflinemanualcompletiondeleted": "Manueller Offline-Abschluss des Kurses '{{name}}' wurde gelöscht. {{error}}", - "core.coursedetails": "Kursdetails", - "core.coursenogroups": "Sie sind nicht Mitglied einer Gruppe in diesem Kurs.", - "core.courses.addtofavourites": "Diesen Kurs markieren", - "core.courses.allowguests": "Dieser Kurs erlaubt einen Gastzugang.", - "core.courses.availablecourses": "Kursliste", - "core.courses.cannotretrievemorecategories": "Kursbereiche tiefer als Level {{$a}} können nicht abgerufen werden.", - "core.courses.categories": "Kursbereiche", - "core.courses.confirmselfenrol": "Möchten Sie sich selbst in diesen Kurs einschreiben?", - "core.courses.courses": "Kurse", - "core.courses.downloadcourses": "Kurse herunterladen", - "core.courses.enrolme": "Selbst einschreiben", - "core.courses.errorloadcategories": "Fehler beim Laden von Kursbereichen", - "core.courses.errorloadcourses": "Fehler beim Laden von Kursen", - "core.courses.errorloadplugins": "Für diesen Kurs notwendige Plugins konnten nicht richtig geladen werden. Starten Sie die App neu und versuchen Sie es noch einmal.", - "core.courses.errorsearching": "Fehler beim Suchen", - "core.courses.errorselfenrol": "Fehler bei der Selbsteinschreibung", - "core.courses.filtermycourses": "Meine Kurse filtern", - "core.courses.frontpage": "Startseite", - "core.courses.hidecourse": "Aus Darstellung entfernen", - "core.courses.ignore": "Ignorieren", - "core.courses.mycourses": "Meine Kurse", - "core.courses.mymoodle": "Dashboard", - "core.courses.nocourses": "Keine Kursinfos", - "core.courses.nocoursesyet": "Keine Kurse in diesem Kursbereich", - "core.courses.nosearchresults": "Keine Ergebnisse", - "core.courses.notenroled": "Sie sind nicht in diesen Kurs eingeschrieben", - "core.courses.notenrollable": "Sie können sich nicht selbst in diesen Kurs einschreiben.", - "core.courses.password": "Einschreibeschlüssel", - "core.courses.paymentrequired": "Dieser Kurs ist entgeltpflichtig. Bitte bezahlen Sie das Teilnahmeentgelt, um in den Kurs eingeschrieben zu werden.", - "core.courses.paypalaccepted": "PayPal-Zahlungen möglich", - "core.courses.reload": "Neu laden", - "core.courses.removefromfavourites": "Kursmarkierung entfernen", - "core.courses.search": "Suchen", - "core.courses.searchcourses": "Kurse suchen", - "core.courses.searchcoursesadvice": "Sie können Kurse suchen, um als Gast teilzunehmen oder sich selbst einzuschreiben, falls dies erlaubt ist.", - "core.courses.selfenrolment": "Selbsteinschreibung", - "core.courses.sendpaymentbutton": "Zahlung über PayPal", - "core.courses.show": "Erneut anzeigen", - "core.courses.totalcoursesearchresults": "Alle Kurse: {{$a}}", - "core.currentdevice": "Aktuelles Gerät", - "core.datastoredoffline": "Die Daten wurden Lokal auf dem Gerät gespeichert, weil sie nicht gesendet werden konnten. Sie werden automatisch später gesendet.", - "core.date": "Datum", - "core.day": "Tag", - "core.days": "Tage", - "core.decsep": ",", - "core.defaultvalue": "Standard ({{$a}})", - "core.delete": "Löschen", - "core.deletedoffline": "Offline gelöscht", - "core.deleteduser": "Gelöschtes Nutzerkonto", - "core.deleting": "Löschen ...", - "core.description": "Beschreibung", - "core.desktop": "Schreibtisch", - "core.dfdaymonthyear": "DD.MM.YYYY", - "core.dfdayweekmonth": "ddd, D. MMM", - "core.dffulldate": "dddd, D. MMMM YYYY, HH[:]mm", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "HH[:]mm", - "core.digitalminor": "Nicht alt genug", - "core.digitalminor_desc": "Um ein Nutzerkonto zu erstellen, müssen sich Ihre Erziehungsberechtigten an folgende Stelle wenden:", - "core.discard": "Verwerfen", - "core.dismiss": "Abbrechen", - "core.displayoptions": "Darstellung", - "core.done": "Erledigt", - "core.download": "Herunterladen", - "core.downloaded": "Heruntergeladen", - "core.downloading": "Herunterladen ...", - "core.edit": "Bearbeiten", - "core.editor.autosavesucceeded": "Entwurf gesichert.", - "core.editor.bold": "Fett", - "core.editor.clear": "Formatierung entfernen", - "core.editor.h3": "Überschrift (groß)", - "core.editor.h4": "Überschrift (mittel)", - "core.editor.h5": "Überschrift (klein)", - "core.editor.hidetoolbar": "Leiste verbergen", - "core.editor.italic": "Kursiv", - "core.editor.orderedlist": "Geordnete Liste", - "core.editor.p": "Absatz", - "core.editor.strike": "Durchstreichen", - "core.editor.textrecovered": "Der Entwurf dieses Textes wurde automatisch wiederhergestellt.", - "core.editor.toggle": "Editor umschalten", - "core.editor.underline": "Unterstreichen", - "core.editor.unorderedlist": "Ungeordnete Liste", - "core.emptysplit": "Das Seitenmenü ist leer oder wird noch geladen ...", - "core.error": "Fehler", - "core.errorchangecompletion": "Fehler beim Ändern des Abschlussstatus. Versuchen Sie es noch einmal.", - "core.errordeletefile": "Fehler beim Löschen der Datei. Versuchen Sie es noch einmal.", - "core.errordownloading": "Fehler beim Laden der Datei", - "core.errordownloadingsomefiles": "Fehler beim Laden der Dateien. Einige Dateien könnten fehlen.", - "core.errorfileexistssamename": "Eine Datei mit gleichem Namen existiert bereits.", - "core.errorinvalidform": "Das Formular enthält ungültige Daten. Füllen Sie alle notwendigen Felder aus und prüfen Sie, dass alle Daten richtig sind.", - "core.errorinvalidresponse": "Ungültige Antwort empfangen. Wenden Sie sich an den Administrator, wenn der Fehler wieder auftritt.", - "core.errorloadingcontent": "Fehler beim Laden des Inhalts", - "core.errorofflinedisabled": "Der Offline-Zugriff auf diese Website ist deaktiviert. Sie müssen online sein, um die App zu verwenden.", - "core.erroropenfilenoapp": "Fehler: Keine App zum Öffnen dieses Dateityps gefunden.", - "core.erroropenfilenoextension": "Fehler beim Öffnen: Die Datei hat keine Endung.", - "core.erroropenpopup": "Die Aktivität versucht, ein Popup zu öffnen. Popups werden in der App aber nicht unterstützt.", - "core.errorrenamefile": "Fehler beim Ändern des Dateinamens. Versuchen Sie es noch einmal.", - "core.errorsomedatanotdownloaded": "Beim Herunterladen dieser Aktivität könnten möglicherweise einige Daten aus Performance- und Datennutzungsgründen ausgenommen worden sein.", - "core.errorsync": "Fehler beim Synchronisieren. Versuchen Sie es noch einmal.", - "core.errorsyncblocked": "{{$a}} kann im Moment wegen eines anderen Vorgangs nicht synchronisiert werden. Versuchen Sie es später noch einmal. Falls das Problem weiterhin besteht, starten Sie die App neu.", - "core.explanationdigitalminor": "Diese Informationen sind notwendig um festzustellen, ob Sie sich selber registrieren dürfen. Nur wenn Sie alt genug sind, können Sie selber den Richtlinien und Nutzungsbedingungen zustimmen. Andernfalls müssen dies Ihre Erziehungsberechtigten tun.", - "core.favourites": "Favoriten", - "core.filename": "Dateiname", - "core.filenameexist": "Der Dateiname existiert bereits: {{$a}}", - "core.filenotfound": "Datei nicht gefunden", - "core.fileuploader.addfiletext": "Datei hinzufügen", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Möchten Sie wirklich {{size}} hochladen?", - "core.fileuploader.confirmuploadunknownsize": "Die Datenmenge kann nicht berechnet werden. Möchten Sie wirklich hochladen?", - "core.fileuploader.errorcapturingaudio": "Fehler bei der Audioaufnahme", - "core.fileuploader.errorcapturingimage": "Fehler beim Fotografieren", - "core.fileuploader.errorcapturingvideo": "Fehler bei der Videoaufnahme", - "core.fileuploader.errorgettingimagealbum": "Fehler beim Laden aus dem Fotoalbum", - "core.fileuploader.errormustbeonlinetoupload": "Dateien können nur online hochgeladen werden.", - "core.fileuploader.errornoapp": "Keine App zum Ausführen dieser Aktion gefunden", - "core.fileuploader.errorreadingfile": "Fehler beim Lesen der Datei", - "core.fileuploader.errorwhileuploading": "Fehler beim Hochladen der Datei", - "core.fileuploader.file": "Datei", - "core.fileuploader.filesofthesetypes": "Akzeptierte Dateitypen:", - "core.fileuploader.fileuploaded": "Datei erfolgreich hochgeladen", - "core.fileuploader.invalidfiletype": "Dateityp {{$a}} wird nicht akzeptiert", - "core.fileuploader.maxbytesfile": "Die Datei {{$a.file}} ist zu groß. Die maximale Größe zum Hochladen ist {{$a.size}}.", - "core.fileuploader.more": "Einzelansicht", - "core.fileuploader.photoalbums": "Fotoalbum", - "core.fileuploader.readingfile": "Datei lesen", - "core.fileuploader.readingfileperc": "Datei lesen: {{$a}}%", - "core.fileuploader.selectafile": "Datei wählen", - "core.fileuploader.uploadafile": "Datei hochladen", - "core.fileuploader.uploading": "Hochladen ...", - "core.fileuploader.uploadingperc": "Hochladen: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filter", - "core.folder": "Verzeichnis", - "core.forcepasswordchangenotice": "Ändern Sie Ihr Kennwort, bevor Sie weiterarbeiten.", - "core.fulllistofcourses": "Alle Kurse", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Durchschnitt", - "core.grades.badgrade": "Ungültige Bewertung", - "core.grades.contributiontocoursetotal": "Beiträge zum Kurs gesamt", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Bewertung", - "core.grades.gradeitem": "Bewertungsaspekt", - "core.grades.grades": "Bewertungen", - "core.grades.lettergrade": "Notenstufenbewertung", - "core.grades.nogradesreturned": "Keine Bewertung zum Anzeigen vorhanden", - "core.grades.nooutcome": "Kein Lernziel", - "core.grades.percentage": "Prozentsatz", - "core.grades.range": "Bereich", - "core.grades.rank": "Rang", - "core.grades.weight": "Gewichtung", - "core.group": "Gruppe", - "core.groupsseparate": "Getrennte Gruppen", - "core.groupsvisible": "Sichtbare Gruppen", - "core.h5p.additionallicenseinfo": "Zusätzliche Infos zur Lizenz", - "core.h5p.author": "Autor/in", - "core.h5p.authorcomments": "Kommentare", - "core.h5p.authorcommentsdescription": "Kommentare an Herausgeber/in des Inhalts. Dieser Text wird nicht als Teil der Copyright-Infos veröffentlicht.", - "core.h5p.authorname": "Autorenname", - "core.h5p.authorrole": "Autorenrolle", - "core.h5p.by": "von", - "core.h5p.cancellabel": "Abbrechen", - "core.h5p.ccattribution": "Namensnennung (CC BY)", - "core.h5p.ccattributionnc": "Namensnennung - nicht kommerziell (CC BY-NC)", - "core.h5p.ccattributionncnd": "Namensnennung - nicht kommerziell - keine Bearbeitung (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Namensnennung - nicht kommerziell - Weitergabe unter gleichen Bedingungen (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Namensnennung - keine Bearbeitung (CC BY-ND)", - "core.h5p.ccattributionsa": "Namensnennung - Weitergabe unter gleichen Bedingungen (CC BY-SA)", - "core.h5p.ccpdd": "Public Domain (CC0)", - "core.h5p.changedby": "Geändert von", - "core.h5p.changedescription": "Änderungsbeschreibung", - "core.h5p.changelog": "Änderungsverlauf", - "core.h5p.changeplaceholder": "Foto beschnitten, Text geändert, usw.", - "core.h5p.close": "Schließen", - "core.h5p.confirmdialogbody": "Möchten Sie wirklich fortfahren? Die Aktion kann nicht zurückgenommen werden.", - "core.h5p.confirmdialogheader": "Aktion bestätigen", - "core.h5p.confirmlabel": "Bestätigen", - "core.h5p.connectionLost": "Verbindung unterbrochen. Die Ergebnisse werden gespeichert und gesendet, wenn die Verbindung wiederhergestellt ist.", - "core.h5p.connectionReestablished": "Verbindung wiederhergestellt", - "core.h5p.contentCopied": "Der Inhalt wurde in die Zwischenablage kopiert", - "core.h5p.contentchanged": "Dieser Inhalt hat sich seit Ihrer letzten Verwendung geändert.", - "core.h5p.contenttype": "Inhaltstyp", - "core.h5p.copyright": "Nutzungsrechte", - "core.h5p.copyrightinfo": "Copyright-Info", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "Copyright-Info für diesen Inhalt anzeigen", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Datum", - "core.h5p.disablefullscreen": "Vollbild deaktivieren", - "core.h5p.download": "Herunterladen", - "core.h5p.downloadtitle": "Diesen Inhalt als H5P-Datei herunterladen", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Einbetten", - "core.h5p.embedtitle": "Einbettcode für diesen Inhalt anzeigen", - "core.h5p.errorgetemail": "Fehler beim Abrufen der Nutzer-E-Mail-Adresse. Bitte überprüfen Sie Ihre Verbindung und versuchen Sie es erneut.", - "core.h5p.fullscreen": "Vollbild", - "core.h5p.gpl": "General Public License v3", - "core.h5p.h5ptitle": "Besuchen Sie die Website H5P.org, um weitere Infos zu erhalten.", - "core.h5p.hideadvanced": "Erweitert ausblenden", - "core.h5p.license": "Lizenz", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) Public Domain", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Generic", - "core.h5p.licenseCC20": "2.0 Generic", - "core.h5p.licenseCC25": "2.5 Generic", - "core.h5p.licenseCC30": "3.0 Unported", - "core.h5p.licenseCC40": "4.0 International", - "core.h5p.licenseGPL": "General Public License", - "core.h5p.licenseV1": "Version 1", - "core.h5p.licenseV2": "Version 2", - "core.h5p.licenseV3": "Version 3", - "core.h5p.licensee": "Lizenznehmer/in", - "core.h5p.licenseextras": "Lizenzextras", - "core.h5p.licenseversion": "Lizenzversion", - "core.h5p.nocopyright": "Für diesen Inhalt sind keine Copyright-Infos verfügbar.", - "core.h5p.offlineDialogBody": "Infos zum Abschluss dieser Aufgabe konnten nicht gesendet werden. Überprüfen Sie die Internetverbindung.", - "core.h5p.offlineDialogHeader": "Ihre Verbindung zum Server wurde unterbrochen.", - "core.h5p.offlineDialogRetryButtonLabel": "Sofort wiederholen", - "core.h5p.offlineDialogRetryMessage": "Wiederholung in :num ...", - "core.h5p.offlineSuccessfulSubmit": "Ergebnisse erfolgreich übertragen.", - "core.h5p.offlinedisabled": "Die Website erlaubt nicht das Herunterladen von H5P-Paketen.", - "core.h5p.originator": "Urheber/in", - "core.h5p.pd": "Public Domain", - "core.h5p.pddl": "Public Domain Widmung und Lizenz", - "core.h5p.pdm": "Public Domain Mark (PDM)", - "core.h5p.play": "H5P abspielen", - "core.h5p.resizescript": "Fügen Sie dieses Skript in die Website ein, wenn Sie eine dynamische Größenanpassung des eingebetteten Inhalts möchtest:", - "core.h5p.resubmitScores": "Versuch, gespeicherte Ergebnisse zu übermitteln.", - "core.h5p.reuse": "Wiederverwenden", - "core.h5p.reuseContent": "Inhalt wiederverwenden", - "core.h5p.reuseDescription": "Diesen Inhalt wiederverwenden", - "core.h5p.showadvanced": "Erweitert anzeigen", - "core.h5p.showless": "Weniger anzeigen", - "core.h5p.showmore": "Mehr anzeigen", - "core.h5p.size": "Größe", - "core.h5p.source": "Quelle", - "core.h5p.startingover": "Sie werden von vorne anfangen.", - "core.h5p.sublevel": "Sublevel", - "core.h5p.thumbnail": "Vorschaubild", - "core.h5p.title": "Titel", - "core.h5p.undisclosed": "Geheim gehalten", - "core.h5p.year": "Jahr", - "core.h5p.years": "Jahre", - "core.h5p.yearsfrom": "Jahre (von)", - "core.h5p.yearsto": "Jahre (bis)", - "core.hasdatatosync": "Die Offline-Daten von {{$a}} müssen synchronisiert werden.", - "core.help": "Hilfe", - "core.hide": "Verbergen", - "core.hour": "Stunde", - "core.hours": "Stunden", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Bild", - "core.imageviewer": "Bildanzeige", - "core.info": "Infos", - "core.invalidformdata": "Falsche Formulardaten!", - "core.labelsep": ": ", - "core.lastaccess": "Letzter Zugriff", - "core.lastdownloaded": "Zuletzt heruntergeladen", - "core.lastmodified": "Zuletzt geändert", - "core.lastsync": "Zuletzt synchronisiert", - "core.layoutgrid": "Horizontal", - "core.list": "Auflisten", - "core.listsep": ";", - "core.loading": "Laden ...", - "core.loadmore": "Mehr laden", - "core.location": "Ort", - "core.login.auth_email": "E-Mail basierte Selbstregistrierung", - "core.login.authenticating": "Authentifizieren ...", - "core.login.cancel": "Abbrechen", - "core.login.changepassword": "Kennwort ändern", - "core.login.changepasswordbutton": "Seite zur Kennwortänderung aufrufen", - "core.login.changepasswordhelp": "Wenn Sie Probleme beim Ändern Ihres Kennworts haben, wenden Sie sich an den Administrator Ihrer Website. Administrator/innen sind die Personen, die das Moodle an Ihrer Schule, Universität, Firma oder Organisation verwalten. Wenn Sie nicht wissen, wie Sie mit ihnen Kontakt aufnehmen können, wenden Sie sich an Ihre Trainer/innen.", - "core.login.changepasswordinstructions": "Sie können Ihr Kennwort nicht in der App ändern. Klicken Sie auf die folgende Taste, um Moodle in einem Webbrowser zu öffnen und Ihr Kennwort zu ändern. Beachten Sie, dass Sie den Browser nach dem Ändern des Kennworts schließen müssen, um wieder in die App zurückzukommen.", - "core.login.changepasswordlogoutinstructions": "Wenn Sie die Website wechseln oder sich abmelden möchten, klicken Sie auf die folgende Taste:", - "core.login.changepasswordreconnectinstructions": "Klicken Sie auf die folgende Taste, um die Website neu zu verbinden. Wenn Sie das Kennwort nicht erfolgreich ändern, öffnet sich wieder der vorherige Bildschirm.", - "core.login.confirmdeletesite": "Möchten Sie '{{sitename}}' wirklich aus der Liste löschen?", - "core.login.connect": "Verbinden", - "core.login.connecttomoodle": "Zu Moodle verbinden", - "core.login.connecttomoodleapp": "Sie versuchen, sich mit einer normalen Moodle-Website zu verbinden. Verwenden Sie die offizielle Moodle-App, um auf diese Website zu gelangen.", - "core.login.connecttoworkplaceapp": "Sie versuchen, sich mit einer Moodle-Workplace-Website zu verbinden. Verwenden Sie die Moodle-Workplace-App, um auf diese Website zu gelangen.", - "core.login.contactyouradministrator": "Wenden Sie sich an den Administrator, um weitere Hilfe zu bekommen.", - "core.login.contactyouradministratorissue": "Wenden Sie sich an den Administrator, um folgendes Problem prüfen zu lassen: {{$a}}", - "core.login.createaccount": "Neues Nutzerkonto anlegen", - "core.login.createuserandpass": "Anmeldedaten wählen", - "core.login.credentialsdescription": "Geben Sie den Anmeldenamen und das Kennwort ein. ", - "core.login.emailconfirmsent": "

        Um sicherzugehen, dass sich niemand unberechtigt über die von Ihnen angegebene E-Mail anmeldet, wird eine automatische Benachrichtigung an diese Adresse {{$a}} gesendet.

        \n

        Die Benachrichtigung enthält eine Anleitung, wie Sie Ihre Registrierung bestätigen. Danach sind Sie auf dieser Moodle-Seite registriert und können sofort loslegen.

        \n

        Bei Problemen wenden Sie sich bitte an die Administrator/innen der Website.

        ", - "core.login.emailconfirmsentnoemail": "

        An Ihre E-Mail-Adresse wurde eine Mitteilung gesendet.

        Die Mitteilung enthält eine einfache Anleitung, um Ihre Registrierung abzuschließen. Falls Sie Probleme haben, wenden Sie sich an den Administrator der Website.

        ", - "core.login.emailconfirmsentsuccess": "Die Bestätigungsmitteilung wurde erfolgreich versendet.", - "core.login.emailnotmatch": "Die E-Mail-Adressen stimmen nicht überein.", - "core.login.erroraccesscontrolalloworigin": "Der Cross-Origin-Aufruf wurde zurückgewiesen. Weitere Infos: https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Fehler beim Entfernen der Website aus der Liste. Versuchen Sie es noch einmal.", - "core.login.errorexampleurl": "Die URL https://campus.example.edu ist eine Beispiel-URL, die nicht zu einer echten Website gehört. Verwenden Sie die URL für die Website Ihrer Schule oder Ihrer Organisation.", - "core.login.errorupdatesite": "Fehler bei der Authentifizierung", - "core.login.faqcannotconnectanswer": "Wenden Sie sich bitte an den Administrator.", - "core.login.faqcannotconnectquestion": "Ich habe meine Website-Adresse richtig eingegeben, aber ich kann mich nicht verbinden.", - "core.login.faqcannotfindmysiteanswer": "Haben Sie den Namen der Website richtig eingegeben? Wenn Sie ihn nicht finden können, geben Sie bitte stattdessen die Adresse ein. Nicht alle Moodle-Websites sind im Verzeichnis der öffentlichen Websites verfügbar.", - "core.login.faqcannotfindmysitequestion": "Ich kann meine Website nicht finden.", - "core.login.faqsetupsiteanswer": "Besuchen Sie {{$link}}, um die verschiedenen Optionen zum Erstellen Ihrer eigenen Moodle-Website zu prüfen.", - "core.login.faqsetupsitelinktitle": "Beginnen Sie.", - "core.login.faqsetupsitequestion": "Ich möchte meine eigene Moodle-Website aufsetzen.", - "core.login.faqtestappanswer": "Um die App mit einer Demo-Website zu testen, geben Sie im Feld \"Ihre Website\" entweder \"teacher\" oder \"student\" ein und tippen Sie auf \"Verbinden!\"", - "core.login.faqtestappquestion": "Ich möchte die App testen. Was kann ich machen?", - "core.login.faqwhatisurlanswer": "

        Jede Schule und jede Institution hat eine eigene Adresse für ihre Moodle-Website.

        Um die Adresse zu finden, zu der Sie eine Verbindung herstellen möchten, machen Sie Folgendes:

        \n
        1. Öffnen Sie Ihren Browser und rufen Sie die Anmeldeseite der Moodle-Website Ihrer Schule oder Organisation auf.
        2. Oben in der Adressleiste sehen Sie die URL, z.B. \"campus.example.edu\".
          {{$image}}
        3. Kopieren Sie die Adresse (ohne /login und was danach kommt), fügen Sie die Adresse in die Moodle App ein und tippen Sie auf \"Verbinden!\"
        4. Jetzt können Sie sich mit Ihrem Anmeldenamen und Ihrem Kennwort anmelden.
        5. ", - "core.login.faqwhatisurlquestion": "Wie lautet die Adresse meiner Website? Wie kann ich die die URL meiner Website finden?", - "core.login.faqwhereisqrcode": "Wo kann ich den QR-Code finden?", - "core.login.faqwhereisqrcodeanswer": "

          Wenn Ihre Schule oder Organisation dies aktiviert hat, finden Sie einen QR-Code auf der Seite mit Ihrem Nutzerprofil.

          {{$image}}", - "core.login.findyoursite": "Website suchen", - "core.login.firsttime": "Sind Sie zum ersten Mal auf dieser Webseite?", - "core.login.forcepasswordchangenotice": "Ändern Sie Ihr Kennwort, bevor Sie weiterarbeiten.", - "core.login.forgotten": "Kennwort vergessen?", - "core.login.help": "Hilfe", - "core.login.helpmelogin": "

          Auf der Welt gibt es viele tausend Websites mit Moodle. Diese App kann aber nur mit Websites verbunden werden, die für den Zugriff mit der mobilen Moodle App freigegeben wurden.

          Falls Sie sich nicht mit Ihrer Website verbinden können, wenden Sie sich an den Administrator der Website und bitten ihn, die Anleitung zu lesen. https://docs.moodle.org/de/Moodle_App

          Um die App mit einer Demoseite auszuprobieren, schreiben Sie teacher oder student in das Feld Website-Adresse und tippen dann auf Verbinden.

          ", - "core.login.instructions": "Anleitung", - "core.login.invalidaccount": "Prüfen Sie Ihre Anmeldedaten oder wenden Sie sich an den Administrator der Website.", - "core.login.invaliddate": "Ungültiges Datum", - "core.login.invalidemail": "Ungültige E-Mail-Adresse", - "core.login.invalidmoodleversion": "

          Falsche Moodle-Version. Die Moodle-App unterstützt nur Moodle-Systeme ab {{$a}}.

          \n

          Wenden Sie sich an den Administrator Ihrer Website, damit Ihr Moodle-System aktualisiert wird. Administrator/innen sind die Personen, die das Moodle an Ihrer Schule, Universität, Firma oder Organisation verwalten. Wenn Sie nicht wissen, wie Sie mit ihnen in Kontakt treten sollen, wenden Sie sich an Ihre Trainer/innen.

          ", - "core.login.invalidsite": "Die URL der Website ist ungültig.", - "core.login.invalidtime": "Ungültige Zeitangabe", - "core.login.invalidurl": "Ungültige URL angegeben", - "core.login.invalidvaluemax": "Der Maximalwert ist {{$a}}.", - "core.login.invalidvaluemin": "Der Minimalwert ist {{$a}}.", - "core.login.localmobileunexpectedresponse": "Die Verbindung zum Plugin 'Moodle Mobile - Zusatzfeatures' ist fehlgeschlagen. Sie werden über den standardmäßigen mobilen Webservice authentifiziert.", - "core.login.loggedoutssodescription": "Sie müssen sich neu authentifizieren. Melden Sie sich im Browser auf der Website an.", - "core.login.login": "Login", - "core.login.loginbutton": "Anmelden", - "core.login.logininsiterequired": "Sie müssen sich für diese Website im Browser anmelden.", - "core.login.loginsteps": "Für den vollen Zugriff auf die Website brauchen Sie ein Nutzerkonto.", - "core.login.missingemail": "E-Mail-Adresse fehlt", - "core.login.missingfirstname": "Vorname fehlt", - "core.login.missinglastname": "Nachname fehlt", - "core.login.mobileservicesnotenabled": "Der mobile Zugriff ist für dieses Moodle nicht aktiviert. Wenden Sie sich an den Administrator, wenn Sie die mobile App aktiviert haben möchten.", - "core.login.mustconfirm": "Sie müssen Ihren Zugang bestätigen", - "core.login.newaccount": "Neues Nutzerkonto", - "core.login.notloggedin": "Sie müssen angemeldet sein.", - "core.login.onboardingcreatemanagecourses": "Ihre Kurse anlegen und verwalten", - "core.login.onboardingenrolmanagestudents": "Ihre Teilnehmer/innen einschreiben und verwalten", - "core.login.onboardinggetstarted": "Beginnen Sie mit Moodle", - "core.login.onboardingialreadyhaveasite": "Ich habe bereits eine Moodle-Website.", - "core.login.onboardingimalearner": "Ich bin ein Lernender.", - "core.login.onboardingimaneducator": "Ich bin eine Lehrkraft.", - "core.login.onboardingineedasite": "Ich brauche eine Moodle-Website.", - "core.login.onboardingprovidefeedback": "Zeitnahes Feedback geben", - "core.login.onboardingtoconnect": "Um eine Verbindung zur Moodle-App herzustellen, benötigen Sie eine Moodle-Website", - "core.login.onboardingwelcome": "Willkommen bei der Moodle-App!", - "core.login.or": "ODER", - "core.login.password": "Kennwort", - "core.login.passwordforgotten": "Kennwort vergessen", - "core.login.passwordforgotteninstructions2": "Um Ihr Kennwort zurückzusetzen, tragen Sie bitte entweder Ihren Anmeldenamen oder Ihre E-Mail-Adresse ein. Wenn Sie in der Datenbank zu finden sind, wird eine Mitteilung an Ihre E-Mail-Adresse verschickt. Diese Mitteilung enthält eine Anleitung für die weiteren Schritte.", - "core.login.passwordrequired": "Kennwort fehlt", - "core.login.policyaccept": "Ich habe den Text gelesen und stimme ihm zu", - "core.login.policyagree": "Lesen Sie diese Datenschutzinfos sorgfältig. Sie müssen zustimmen, um die Website nutzen zu können. Stimmen Sie zu?", - "core.login.policyagreement": "Datenschutzinfos", - "core.login.policyagreementclick": "URL zu den Datenschutzinfos", - "core.login.potentialidps": "Verwenden Sie Ihr Nutzerkonto bei:", - "core.login.profileinvaliddata": "Ungültiger Wert", - "core.login.recaptchachallengeimage": "reCaptcha Challenge-Bild", - "core.login.recaptchaexpired": "Überprüfung abgelaufen. Beantworten Sie die Sicherheitsfrage noch einmal.", - "core.login.recaptchaincorrect": "Die Antwort auf die Sicherheitsfrage ist falsch.", - "core.login.reconnect": "Neu verbinden", - "core.login.reconnectdescription": "Die Authentifizierung ist abgelaufen oder ungültig. Sie müssen sich neu anmelden.", - "core.login.reconnectssodescription": "Die Authentifizierung ist abgelaufen oder ungültig. Sie müssen sich im Webbrowser neu anmelden.", - "core.login.resendemail": "E-Mail erneut senden", - "core.login.searchby": "Suche nach:", - "core.login.security_question": "Sicherheitsfrage", - "core.login.selectacountry": "Land auswählen", - "core.login.selectsite": "Wählen Sie Ihre Website", - "core.login.signupplugindisabled": "{{$a}} ist nicht aktiviert.", - "core.login.siteaddress": "Ihre Website", - "core.login.sitehasredirect": "Ihre Website enthält mindestens eine HTTP-Weiterleitung. Die App kann keine Weiterleitungen verarbeiten und der Verbindungaufbau könnte fehlschlagen.", - "core.login.siteinmaintenance": "Diese Website ist im Wartungsmodus.", - "core.login.sitepolicynotagreederror": "Die Zustimmungserklärung wurde nicht bestätigt.", - "core.login.siteurl": "URL der Website", - "core.login.siteurlrequired": "Die URL der Website ist notwendig, z.B. https://www.meinmoodle.de", - "core.login.startsignup": "Neues Konto anlegen?", - "core.login.stillcantconnect": "Können Sie sich immer noch nicht verbinden?", - "core.login.supplyinfo": "Weitere Details", - "core.login.username": "Anmeldename", - "core.login.usernameoremail": "Geben Sie den Anmeldenamen oder die E-Mail-Adresse ein.", - "core.login.usernamerequired": "Anmeldename fehlt", - "core.login.usernotaddederror": "Fehler. Nutzer/in wurde nicht hinzugefügt", - "core.login.visitchangepassword": "Möchten Sie die Website aufrufen, um das Kennwort zu ändern?", - "core.login.webservicesnotenabled": "Ihre Website hat möglicherweise keine Webservices aktiviert. Wenden Sie sich an Ihren Administrator, um Hilfe zu erhalten.", - "core.login.youcanstillconnectwithcredentials": "Sie können weiterhin eine Verbindung zur Website herstellen, indem Sie Ihren Anmeldenamen und Ihr Kennwort eingeben.", - "core.login.yourenteredsite": "Zu Ihrer Website verbinden", - "core.lostconnection": "Die Authentifizierung ist abgelaufen oder ungültig. Sie müssen sich neu anmelden.", - "core.mainmenu.changesite": "Website wechseln", - "core.mainmenu.help": "Hilfe", - "core.mainmenu.logout": "Logout", - "core.mainmenu.website": "Website im Browser", - "core.maxsizeandattachments": "Maximale Größe für Dateien: {{$a.size}}, maximale Anzahl von Anhängen: {{$a.attachments}}", - "core.min": "Minute", - "core.mins": "Minuten", - "core.misc": "Verschiedenes", - "core.mod_assign": "Aufgabe", - "core.mod_assignment": "Aufgabe 2.2 (deaktiviert)", - "core.mod_book": "Buch", - "core.mod_chat": "Chat", - "core.mod_choice": "Abstimmung", - "core.mod_data": "Datenbank", - "core.mod_database": "Datenbank", - "core.mod_external-tool": "Externes Tool", - "core.mod_feedback": "Feedback", - "core.mod_file": "Datei", - "core.mod_folder": "Verzeichnis", - "core.mod_forum": "Forum", - "core.mod_glossary": "Glossar", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS-Content", - "core.mod_imscp": "IMS-Content", - "core.mod_label": "Textfeld", - "core.mod_lesson": "Lektion", - "core.mod_lti": "Externes Tool", - "core.mod_page": "Textseite", - "core.mod_quiz": "Test", - "core.mod_resource": "Datei", - "core.mod_scorm": "Lernpaket", - "core.mod_survey": "Umfrage", - "core.mod_url": "Link/URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Gegenseitige Beurteilung", - "core.moduleintro": "Beschreibung", - "core.more": "mehr", - "core.mygroups": "Meine Gruppen", - "core.name": "Name", - "core.needhelp": "Brauchen Sie Hilfe?", - "core.networkerroriframemsg": "Dieser Inhalt ist offline nicht verfügbar. Sie müssen online sein und es dann noch einmal versuchen.", - "core.networkerrormsg": "Problem mit der Verbindung. Prüfen Sie die Verbindung und versuchen Sie es noch einmal.", - "core.never": "Nie", - "core.next": "Weiter", - "core.no": "Nein", - "core.nocomments": "Keine Kommentare", - "core.nograde": "Keine Bewertung", - "core.none": "Keine", - "core.nooptionavailable": "Keine Option verfügbar", - "core.nopasswordchangeforced": "Sie können nicht weitermachen, ohne das Kennwort zu ändern.", - "core.nopermissionerror": "Sie haben aktuell keine Rechte, um dies zu tun.", - "core.nopermissions": "Sie haben derzeit keine Rechte, dies zu tun ({{$a}}).", - "core.noresults": "Keine Ergebnisse", - "core.noselection": "Keine Auswahl", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Das Profil ist nicht verfügbar, weil diese Person den Kurs bisher nicht betreten hat.", - "core.notice": "Hinweis", - "core.notingroup": "Die Aktivität ist nur für Gruppenmitglieder zugänglich.", - "core.notsent": "Nicht gesendet", - "core.now": "jetzt", - "core.nummore": "{{$a}} mehr", - "core.numwords": "{{$a}} Wörter", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Tippen, um das Bild zu vergrößern", - "core.openinbrowser": "Im Browser öffnen", - "core.openmodinbrowser": "{{$a}} im Browser öffnen", - "core.othergroups": "Weitere Gruppen", - "core.pagea": "Seite {{$a}}", - "core.paymentinstant": "Klicken Sie auf die Taste, um das Teilnahmeentgelt zu bezahlen. Sobald der Zahlvorgang abgeschlossen ist, werden Sie automatisch in den Kurs eingeschrieben.", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pictureof": "Nutzerbild von {{$a}}", - "core.previous": "Zurück", - "core.proceed": "Weitermachen", - "core.pulltorefresh": "Zum Aktualisieren runterziehen", - "core.qrscanner": "QR-Scanner", - "core.question.answer": "Antwort", - "core.question.answersaved": "Antwort gespeichert", - "core.question.cannotdeterminestatus": "Status kann nicht festgestellt werden", - "core.question.certainty": "Gewissheit", - "core.question.complete": "Vollständig", - "core.question.correct": "Richtig", - "core.question.errorattachmentsnotsupported": "Die App erlaubt keine Antworten mit Dateianhängen.", - "core.question.errorinlinefilesnotsupported": "Die App unterstützt keine Bearbeitung von integrierten Dateien.", - "core.question.errorquestionnotsupported": "Die App unterstützt diesen Fragetyp nicht: {{$a}}.", - "core.question.feedback": "Feedback", - "core.question.howtodraganddrop": "Tippen Sie zum Auswählen und tippen Sie noch einmal zum Ablegen.", - "core.question.incorrect": "Falsch", - "core.question.information": "Information", - "core.question.invalidanswer": "Unvollständige Antwort", - "core.question.notanswered": "Nicht beantwortet", - "core.question.notyetanswered": "Bisher nicht beantwortet", - "core.question.partiallycorrect": "Teilweise richtig", - "core.question.questionmessage": "Frage {{$a}}: {{$b}}", - "core.question.questionno": "Frage {{$a}}", - "core.question.requiresgrading": "Bewertung notwendig", - "core.quotausage": "Aktuell sind {{$a.used}} vom möglichen Speicher {{$a.total}} belegt.", - "core.rating.aggregateavg": "Mittelwert", - "core.rating.aggregatecount": "Anzahl der Bewertungen", - "core.rating.aggregatemax": "Maximalwert", - "core.rating.aggregatemin": "Minimalwert", - "core.rating.aggregatesum": "Summe der Bewertungen", - "core.rating.noratings": "Keine Wertungen abgegeben", - "core.rating.rating": "Bewertung", - "core.rating.ratings": "Bewertungen", - "core.redirectingtosite": "Sie werden zur Website weitergeleitet.", - "core.refresh": "Aktualisieren", - "core.remove": "Löschen", - "core.removefiles": "Dateien entfernen {{$a}}", - "core.required": "Erforderlich", - "core.requireduserdatamissing": "Im Nutzerprofil fehlen notwendige Einträge. Füllen Sie die Daten in der Website aus und versuchen Sie es noch einmal.
          {{$a}}", - "core.resourcedisplayopen": "Öffnen", - "core.resources": "Arbeitsmaterial", - "core.restore": "Wiederherstellen", - "core.restricted": "Eingeschränkt", - "core.retry": "Neu versuchen", - "core.save": "Sichern", - "core.savechanges": "Änderungen sichern", - "core.scanqr": "QR-Code scannen", - "core.search": "Suchen", - "core.searching": "Suchen", - "core.searchresults": "Suchergebnisse", - "core.sec": "Sekunde", - "core.secs": "Sekunden", - "core.seemoredetail": "Hier klicken, um weitere Details sichtbar zu machen", - "core.selectacategory": "Kursbereich auswählen", - "core.selectacourse": "Kurs auswählen", - "core.selectagroup": "Gruppe auswählen", - "core.send": "Senden", - "core.sending": "wird gesendet", - "core.serverconnection": "Fehler beim Verbinden zum Server", - "core.settings.about": "Über die App", - "core.settings.appsettings": "Einstellungen der App", - "core.settings.appversion": "Version", - "core.settings.cannotsyncoffline": "Offline kann nicht synchronisiert werden.", - "core.settings.cannotsyncwithoutwifi": "Die Daten wurden nicht synchronisiert, weil die Einstellungen dies nur mit WLAN erlauben. Stellen Sie eine WLAN-Verbindung her.", - "core.settings.colorscheme": "Farbschema", - "core.settings.colorscheme-auto": "Automatisch (entsprechend den Systemeinstellungen)", - "core.settings.colorscheme-dark": "Dunkel", - "core.settings.colorscheme-light": "Hell", - "core.settings.compilationinfo": "Compilation", - "core.settings.copyinfo": "Device-Info in die Zwischenablage kopieren", - "core.settings.cordovadevicemodel": "Cordova Device Model", - "core.settings.cordovadeviceosversion": "Cordova Device OS Version", - "core.settings.cordovadeviceplatform": "Cordova Device Platform", - "core.settings.cordovadeviceuuid": "Cordova Device UUID", - "core.settings.cordovaversion": "Cordova Version", - "core.settings.currentlanguage": "Aktuelle Sprache", - "core.settings.debugdisplay": "Debug-Meldungen anzeigen", - "core.settings.debugdisplaydescription": "Wenn diese Option aktiviert ist, werden zusätzliche Fehlerdaten angezeigt.", - "core.settings.deletesitefiles": "Möchten Sie wirklich alle heruntergeladenen Dateien und gespeicherten Daten der Website '{{sitename}}' löschen? Sie können dann die App nicht mehr offline verwenden.", - "core.settings.deletesitefilestitle": "Dateien löschen", - "core.settings.deviceinfo": "Geräteinformationen", - "core.settings.deviceos": "Geräte-OS", - "core.settings.disableall": "Systemnachrichten deaktivieren", - "core.settings.disabled": "Deaktiviert", - "core.settings.displayformat": "Bildschirm", - "core.settings.enabledownloadsection": "Abschnitte herunterladen", - "core.settings.enablefirebaseanalytics": "Firebase Analytics aktivieren", - "core.settings.enablefirebaseanalyticsdescription": "Wenn diese Option aktiviert ist, sammelt die App anonymisierte Nutzungsdaten.", - "core.settings.enablerichtexteditor": "Texteditor aktivieren", - "core.settings.enablerichtexteditordescription": "Der Texteditor ist verfügbar, wenn Texte eingegeben werden sollen.", - "core.settings.enablesyncwifi": "Nur mit WLAN synchronierieren", - "core.settings.entriesincache": "{{$a}} Einträge im Cache", - "core.settings.errordeletesitefiles": "Fehler beim Löschen der Dateien", - "core.settings.errorsyncsite": "Fehler beim Synchronisieren der Daten. Sie müssen online sein und es dann noch einmal versuchen.", - "core.settings.estimatedfreespace": "Verfügbarer Speicher", - "core.settings.filesystemroot": "Dateisystembasis", - "core.settings.fontsize": "Schriftgröße", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "Diese Einstellung wird durch die Website-Konfiguration erzwungen.", - "core.settings.general": "Allgemein", - "core.settings.language": "Sprache", - "core.settings.license": "Lizenz", - "core.settings.localnotifavailable": "Lokale Systemnachrichten verfügbar", - "core.settings.locationhref": "Webview URL", - "core.settings.locked": "Gesperrt", - "core.settings.loggedin": "Online", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Browsersprache", - "core.settings.navigatoruseragent": "Browserkennung (userAgent)", - "core.settings.networkstatus": "Internetverbindung", - "core.settings.opensourcelicenses": "Open Source Lizenzen", - "core.settings.preferences": "Einstellungen", - "core.settings.privacypolicy": "Datenschutzerklärung", - "core.settings.publisher": "Herausgeber/in", - "core.settings.pushid": "ID für Push-Nachrichten", - "core.settings.reportinbackground": "Fehler automatisch senden", - "core.settings.screen": "Bildschirmgröße", - "core.settings.settings": "Einstellungen", - "core.settings.showdownloadoptions": "Optionen zum Herunterladen anzeigen", - "core.settings.siteinfo": "Website", - "core.settings.sites": "Websites", - "core.settings.spaceusage": "Speichernutzung", - "core.settings.spaceusagehelp": "Beim Löschen der gespeicherten Infos werden alle Offline-Daten der Website entfernt. Sie können die App dann nicht mehr ohne Internetverbindung verwenden.", - "core.settings.synchronization": "Synchronisieren", - "core.settings.synchronizenow": "Synchronisieren", - "core.settings.synchronizenowhelp": "Beim Synchronisieren werden ausstehende Änderungen und alle im Gerät speicherten Offline-Aktivitäten mit der Website ausgetauscht. Mitteilungen und Systemnachrichten werden übertragen.", - "core.settings.syncsettings": "Synchronisieren", - "core.settings.total": "Insgesamt", - "core.settings.wificonnection": "WLAN-Verbindung", - "core.sharedfiles.chooseaccountstorefile": "Wählen Sie ein Nutzerkonto, um die Datei dort zu speichern.", - "core.sharedfiles.chooseactionrepeatedfile": "Eine Datei mit gleichem Namen existiert bereits. Möchten Sie die vorhandene Datei ersetzen oder umbenennen in '{{$a}}'?", - "core.sharedfiles.errorreceivefilenosites": "Keine gespeicherten Websites. Fügen Sie zuerst eine Website hinzu, bevor Sie eine Datei mit der App teilen.", - "core.sharedfiles.nosharedfiles": "Keine geteilten Dateien für diese Website", - "core.sharedfiles.nosharedfilestoupload": "Sie haben hier keine Dateien zum Hochladen. Wenn Sie eine Datei aus einer anderen App hochladen möchten, suchen Sie diese Datei und tippen Sie auf die Taste 'Öffnen in'.", - "core.sharedfiles.rename": "Umbenennen", - "core.sharedfiles.replace": "Ersetzen", - "core.sharedfiles.sharedfiles": "Geteilte Dateien", - "core.sharedfiles.successstorefile": "Die Datei wurde erfolgreich gespeichert. Sie können die Datei in 'Meine Dateien' hochladen oder in einer Aktivität verwenden.", - "core.show": "Anzeigen", - "core.showless": "Weniger anzeigen ...", - "core.showmore": "Mehr anzeigen ...", - "core.site": "Website", - "core.sitehome.sitehome": "Startseite", - "core.sitehome.sitenews": "Ankündigungen", - "core.sitemaintenance": "Wartungsmodus: Die Website ist momentan nicht verfügbar!", - "core.sizeb": "Bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Überspringen", - "core.sorry": "Sorry ...", - "core.sort": "Sortieren", - "core.sortby": "Sortiert nach", - "core.start": "Start", - "core.storingfiles": "Dateien speichern", - "core.strftimedate": "%d. %B %Y", - "core.strftimedatefullshort": "%d.%m.%y", - "core.strftimedateshort": "%d. %B", - "core.strftimedatetime": "%d. %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d.%m.%Y %H:%M", - "core.strftimedaydate": "%A, %d. %B %Y", - "core.strftimedaydatetime": "%A, %d. %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d. %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d. %b, %H:%M", - "core.strftimerecentfull": "%a, %d. %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Speichern", - "core.success": "erfolgreich", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Standard-Collection", - "core.tag.errorareanotsupported": "Dieser Tag-Bereich wird von der App nicht unterstützt.", - "core.tag.inalltagcoll": "Überall", - "core.tag.itemstaggedwith": "{{$a.tagarea}} mit '{{$a.tag}}' markiert", - "core.tag.noresultsfor": "Kein Suchergebnis für '{{$a}}'", - "core.tag.notagsfound": "Kein Tag passend zu '{{$a}}' gefunden", - "core.tag.searchtags": "Tags suchen", - "core.tag.showingfirsttags": "Beliebteste Tags anzeigen: {{$a}}", - "core.tag.tag": "Tag", - "core.tag.tagarea_course": "Kurse", - "core.tag.tagarea_course_modules": "Aktivitäten und Material", - "core.tag.tagarea_post": "Blogeinträge", - "core.tag.tagarea_user": "Nutzerinteressen", - "core.tag.tags": "Tags", - "core.tag.warningareasnotsupported": "Einige der Tag-Bereiche werden nicht angezeigt, weil sie von der App nicht unterstützt werden.", - "core.teachers": "Trainer/innen", - "core.thereisdatatosync": "Die Offline-Daten {{$a}} müssen synchronisiert werden.", - "core.thisdirection": "ltr", - "core.time": "Zeit", - "core.timesup": "Zeit ist abgelaufen.", - "core.today": "Heute", - "core.tryagain": "Versuchen Sie es noch einmal.", - "core.twoparagraphs": "{{p1}}

          {{p2}}", - "core.uhoh": "Uh oh!", - "core.unexpectederror": "Unerwarteter Fehler! Sie müssen die App neu starten und es dann noch einmal versuchen.", - "core.unicodenotsupported": "Manche Emojis können auf dieser Website nicht verwendet werden. Die betreffenden Zeichen werden beim Senden der Mitteilung gelöscht.", - "core.unicodenotsupportedcleanerror": "Der Text ist leer, nachdem die Unicode-Zeichen gelöscht wurden.", - "core.unknown": "Unbekannt", - "core.unlimited": "Unbegrenzt", - "core.unzipping": "Entpacken ...", - "core.updaterequired": "Aktualisierung der App notwendig", - "core.updaterequireddesc": "Aktualisieren Sie Ihre App auf die Version {{$a}}", - "core.upgraderunning": "Diese Website wird gerade aktualisiert. Versuchen Sie es später nochmal.", - "core.user": "Nutzer", - "core.user.address": "Adresse", - "core.user.city": "Stadt", - "core.user.contact": "Kontakt", - "core.user.country": "Land", - "core.user.description": "Beschreibung", - "core.user.details": "Details", - "core.user.detailsnotavailable": "Die Nutzerdetails zu dieser Person sind für Sie nicht verfügbar.", - "core.user.editingteacher": "Trainer/in", - "core.user.email": "E-Mail-Adresse", - "core.user.emailagain": "E-Mail-Adresse (wiederholen)", - "core.user.errorloaduser": "Fehler beim Laden der Nutzerdetails", - "core.user.firstname": "Vorname", - "core.user.interests": "Persönliche Interessen", - "core.user.lastname": "Nachname", - "core.user.manager": "Manager/in", - "core.user.newpicture": "Neues Foto", - "core.user.noparticipants": "Keine Teilnehmer/innen für diesen Kurs gefunden", - "core.user.participants": "Teilnehmer/innen", - "core.user.phone1": "Telefon", - "core.user.phone2": "Smartphone", - "core.user.roles": "Rollen", - "core.user.sendemail": "E-Mail", - "core.user.student": "Teilnehmer/in", - "core.user.teacher": "Trainer/in ohne Bearbeitungsrecht", - "core.user.webpage": "Webseite", - "core.userdeleted": "Dieses Nutzerkonto wurde gelöscht", - "core.userdetails": "Mehr Details", - "core.usernotfullysetup": "Nutzerkonto unvollständig", - "core.users": "Nutzer/innen", - "core.view": "Anzeigen", - "core.viewcode": "Code zeigen", - "core.vieweditor": "Editor zeigen", - "core.viewembeddedcontent": "Eingebettete Inhalte anzeigen", - "core.viewprofile": "Profil anzeigen", - "core.warningofflinedatadeleted": "Die Offline-Daten von {{component}} '{{name}}' wurden gelöscht. {{error}}", - "core.whatisyourage": "Wie alt sind Sie?", - "core.wheredoyoulive": "In welchem Land leben Sie?", - "core.whoissiteadmin": "Administrator/innen sind Personen, die die Moodle-Website in Ihrer Schule, Universität, Organisation oder Firma verwalten. Falls Sie nicht wissen, wie Sie diese kontaktieren können, fragen Sie Ihre Trainer/innen.", - "core.whoops": "Uuups!", - "core.whyisthishappening": "Warum passiert das?", - "core.whyisthisrequired": "Warum ist dies notwendig?", - "core.wsfunctionnotavailable": "Die Webservice-Funktion ist nicht verfügbar.", - "core.year": "Jahr", - "core.years": "Jahre", - "core.yes": "Ja", - "core.youreoffline": "Offline", - "core.youreonline": "Sie sind wieder online" -} \ No newline at end of file diff --git a/src/assets/lang/el.json b/src/assets/lang/el.json deleted file mode 100644 index e2177420d..000000000 --- a/src/assets/lang/el.json +++ /dev/null @@ -1,2166 +0,0 @@ -{ - "addon.badges.alignment": "Εναρμόνιση", - "addon.badges.badgedetails": "Λεπτομέρειες διακριτικού", - "addon.badges.badges": "Διακριτικά", - "addon.badges.bendorsement": "Έγκριση", - "addon.badges.claimcomment": "Σχόλιο έγκρισης", - "addon.badges.claimid": "Διεύθυνση URL παραλαβής", - "addon.badges.contact": "Επικοινωνία", - "addon.badges.dateawarded": "Ημερομηνία έκδοσης", - "addon.badges.expired": "Έληξε", - "addon.badges.expirydate": "Ημερομηνία λήξης", - "addon.badges.imageauthoremail": "Διεύθυνση ηλε.ταχυδρομείου του δημιουργού της εικόνας", - "addon.badges.imageauthorname": "Όνομα του δημιουργού της εικόνας", - "addon.badges.imageauthorurl": "Η διεύθυνση ιστού του δημιουργού της εικόνας", - "addon.badges.imagecaption": "Λεζάντα εικόνας", - "addon.badges.issuancedetails": "Λήξη διακριτικού", - "addon.badges.issuerdetails": "Στοιχεία εκδότη", - "addon.badges.issueremail": "Διεύθυνση ηλε.ταχυδρομείου", - "addon.badges.issuername": "Όνομα εκδότη", - "addon.badges.issuerurl": "Διεύθυνση URL εκδότη", - "addon.badges.language": "Γλώσσα", - "addon.badges.noalignment": "Αυτό το διακριτικό δεν έχει καθορισμένα πρότυπα ή εξωτερικές δεξιότητες.", - "addon.badges.nobadges": "Δεν υπάρχουν διαθέσιμα διακριτικά", - "addon.badges.norelated": "Αυτό το διακριτικό δεν έχει κανένα σχετικό διακριτικό.", - "addon.badges.recipientdetails": "Στοιχεία αποδέκτη", - "addon.badges.relatedbages": "Σχετικά διακριτικά", - "addon.badges.version": "Έκδοση", - "addon.badges.warnexpired": "(Το διακριτικό αυτό έληξε!)", - "addon.block_activitymodules.pluginname": "Δραστηριότητες", - "addon.block_activityresults.pluginname": "Αποτελέσματα δραστηριότητας", - "addon.block_badges.pluginname": "Πρόσφατα διακριτικά", - "addon.block_blogmenu.pluginname": "Μενού ιστολογίου", - "addon.block_blogrecent.pluginname": "Πρόσφατες αναρτήσεις ιστολογίου", - "addon.block_blogtags.pluginname": "Ετικέτες ιστολογίου", - "addon.block_calendarmonth.pluginname": "Ημερολόγιο", - "addon.block_calendarupcoming.pluginname": "Επικείμενα γεγονότα", - "addon.block_comments.pluginname": "Σχόλια", - "addon.block_completionstatus.pluginname": "Κατάσταση ολοκλήρωσης μαθήματος", - "addon.block_glossaryrandom.pluginname": "Τυχαία λέξη από γλωσσάριο", - "addon.block_learningplans.pluginname": "Σχέδια μάθησης", - "addon.block_myoverview.all": "Όλα (εκτός από αυτά που αφαιρέθηκαν από την εμφάνιση)", - "addon.block_myoverview.allincludinghidden": "Όλα", - "addon.block_myoverview.favourites": "Με αστερίσκο", - "addon.block_myoverview.future": "Μελλοντικά", - "addon.block_myoverview.hiddencourses": "Αφαιρέθηκαν από την εμφάνιση", - "addon.block_myoverview.inprogress": "Σε εξέλιξη", - "addon.block_myoverview.lastaccessed": "Τελευταία πρόσβαση", - "addon.block_myoverview.morecourses": "Περισσότερα μαθήματα", - "addon.block_myoverview.nocourses": "Κανένα μάθημα", - "addon.block_myoverview.past": "Προηγούμενα", - "addon.block_myoverview.pluginname": "Επισκόπηση μαθημάτων", - "addon.block_myoverview.shortname": "Σύντομο μήνυμα", - "addon.block_myoverview.title": "Όνομα μαθήματος", - "addon.block_newsitems.pluginname": "Τελευταίες ανακοινώσεις", - "addon.block_onlineusers.pluginname": "Συνδεδεμένοι χρήστες", - "addon.block_privatefiles.pluginname": "Προσωπικά αρχεία", - "addon.block_recentactivity.pluginname": "Πρόσφατη δραστηριότητα", - "addon.block_recentlyaccessedcourses.nocourses": "Κανένα πρόσφατο μάθημα", - "addon.block_recentlyaccessedcourses.pluginname": "Μαθήματα με πρόσφατη πρόσβαση", - "addon.block_recentlyaccesseditems.noitems": "Καθόλου πρόσφατα στοιχεία", - "addon.block_recentlyaccesseditems.pluginname": "Στοιχεία με πρόσφατη πρόσβαση", - "addon.block_rssclient.pluginname": "Απομακρυσμένες τροφοδοσίες RSS", - "addon.block_selfcompletion.pluginname": "Αυτο-ολοκλήρωση", - "addon.block_sitemainmenu.pluginname": "Κύριο μενού", - "addon.block_starredcourses.nocourses": "Δεν υπάρχουν μαθήματα με αστερίσκο", - "addon.block_starredcourses.pluginname": "Μαθήματα με αστερίσκο", - "addon.block_tags.pluginname": "Ετικέτες", - "addon.block_timeline.duedate": "Οφειλόμενη ημερομηνία", - "addon.block_timeline.next30days": "Επόμενες 30 ημέρες", - "addon.block_timeline.next3months": "Επόμενοι 3 μήνες", - "addon.block_timeline.next6months": "Επόμενοι 6 μήνες", - "addon.block_timeline.next7days": "Επόμενες 7 ημέρες", - "addon.block_timeline.nocoursesinprogress": "Καθόλου μαθήματα σε εξέλιξη", - "addon.block_timeline.noevents": "Δεν οφείλονται καθόλου επερχόμενες δραστηριότητες", - "addon.block_timeline.overdue": "Εκπρόθεσμα", - "addon.block_timeline.pluginname": "Χρονολόγιο", - "addon.block_timeline.sortbycourses": "Ταξινόμηση κατά μαθήματα", - "addon.block_timeline.sortbydates": "Ταξινόμηση κατά ημερομηνίες", - "addon.blog.blog": "Ιστολόγιο", - "addon.blog.blogentries": "Αναρτήσεις ιστολογίου", - "addon.blog.errorloadentries": "Σφάλμα φόρτωσης καταχωρήσεων ιστολογίου.", - "addon.blog.linktooriginalentry": "Σύνδεσμος με την αρχική ανάρτηση ιστολογίου", - "addon.blog.noentriesyet": "Δεν υπάρχουν ορατές καταχωρήσεις ακόμα", - "addon.blog.publishtonoone": "Στον εαυτό σας (προσχέδιο)", - "addon.blog.publishtosite": "Οποιοσδήποτε σε αυτόν τον ιστότοπο", - "addon.blog.publishtoworld": "Οποιοσδήποτε στον κόσμο", - "addon.blog.showonlyyourentries": "Εμφάνιση μόνο των καταχωρήσεών σας", - "addon.blog.siteblogheading": "Ιστολόγιο ιστοτόπου", - "addon.calendar.allday": "Ολοήμερο", - "addon.calendar.calendar": "Ημερολόγιο", - "addon.calendar.calendarevent": "Γεγονός ημερολογίου", - "addon.calendar.calendarevents": "Γεγονότα ημερολογίου", - "addon.calendar.calendarreminders": "Υπενθυμίσεις ημερολογίου", - "addon.calendar.categoryevents": "Γεγονότα κατηγορίας", - "addon.calendar.confirmeventdelete": "Θέλετε σίγουρα να διαγράψετε αυτό το γεγονός «{{$a}}»;", - "addon.calendar.confirmeventseriesdelete": "Το γεγονός «{{$a.name}}» είναι μέρος μιας σειράς γεγονότων. Θέλετε να διαγράψετε μόνο αυτό το γεγονός ή και τα {{$a.count}} γεγονότα της σειράς;", - "addon.calendar.courseevents": "Γεγονότα μαθήματος", - "addon.calendar.currentmonth": "Τρέχων μήνας", - "addon.calendar.daynext": "Επόμενη ημέρα", - "addon.calendar.dayprev": "Προηγούμενη ημέρα", - "addon.calendar.defaultnotificationtime": "Προεπιλεγμένος χρόνος ειδοποίησης", - "addon.calendar.deleteallevents": "Διαγραφή όλων των γεγονότων", - "addon.calendar.deleteevent": "Διαγραφή γεγονότος", - "addon.calendar.deleteoneevent": "Διαγραφή αυτού του γεγονότος", - "addon.calendar.durationminutes": "Διάρκεια σε λεπτά", - "addon.calendar.durationnone": "Χωρίς διάρκεια", - "addon.calendar.durationuntil": "Μέχρι", - "addon.calendar.editevent": "Επεξεργασία γεγονότος", - "addon.calendar.errorloadevent": "Σφάλμα στην φόρτωση γεγονότος.", - "addon.calendar.errorloadevents": "Σφάλμα στην φόρτωση γεγονότων.", - "addon.calendar.eventcalendareventdeleted": "Το γεγονός ημερολογίου διαγράφηκε", - "addon.calendar.eventduration": "Διάρκεια", - "addon.calendar.eventendtime": "Ώρα λήξης", - "addon.calendar.eventkind": "Τύπος του γεγονότος", - "addon.calendar.eventname": "Τίτλος γεγονότος", - "addon.calendar.eventstarttime": "Ώρα έναρξης", - "addon.calendar.eventtype": "Τύπος γεγονότος", - "addon.calendar.fri": "Παρ", - "addon.calendar.friday": "Παρασκευή", - "addon.calendar.gotoactivity": "Πηγαίνετε στη δραστηριότητα", - "addon.calendar.groupevents": "Ομαδικά γεγονότα", - "addon.calendar.invalidtimedurationminutes": "Η διάρκεια σε λεπτά που έχετε εισάγει δεν είναι έγκυρη. Παρακαλούμε εισάγετε την διάρκεια σε λεπτά μεγαλύτερη από 0 ή επιλέξτε Όχι διάρκεια.", - "addon.calendar.invalidtimedurationuntil": "Η ημερομηνία και η ώρα που επιλέξατε για διάρκεια έως είναι προγενέστερη από την ώρα έναρξης του γεγονότος. Παρακαλούμε διορθώστε πριν προχωρήσετε.", - "addon.calendar.mon": "Δευ", - "addon.calendar.monday": "Δευτέρα", - "addon.calendar.monthlyview": "Εμφάνιση μήνα", - "addon.calendar.newevent": "Νέο γεγονός", - "addon.calendar.noevents": "Δεν υπάρχουν γεγονότα", - "addon.calendar.nopermissiontoupdatecalendar": "Λυπούμαστε, αλλά δεν έχετε δικαίωμα να ενημερώσετε το γεγονός ημερολογίου.", - "addon.calendar.reminders": "Υπενθυμίσεις", - "addon.calendar.repeatedevents": "Επαναλαμβανόμενα γεγονότα", - "addon.calendar.repeateditall": "Εφαρμογή αλλαγών σε όλα τα γεγονότα ({{$a}}) σε αυτή την επαναλαμβανόμενη σειρά", - "addon.calendar.repeateditthis": "Εφαρμογή αλλαγών σε αυτό το γεγονός μόνο", - "addon.calendar.repeatevent": "Επανάληψη γεγονότος", - "addon.calendar.repeatweeksl": "Εβδομαδιαία επανάληψη, γενική δημιουργία", - "addon.calendar.sat": "Σαβ", - "addon.calendar.saturday": "Σάββατο", - "addon.calendar.setnewreminder": "Ορισμός μιας νέας υπενθύμισης", - "addon.calendar.siteevents": "Γεγονότα ιστοτόπου", - "addon.calendar.sun": "Κυρ", - "addon.calendar.sunday": "Κυριακή", - "addon.calendar.thu": "Πεμ", - "addon.calendar.thursday": "Πέμπτη", - "addon.calendar.today": "Σήμερα", - "addon.calendar.tomorrow": "Αύριο", - "addon.calendar.tue": "Τρι", - "addon.calendar.tuesday": "Τρίτη", - "addon.calendar.typecategory": "Γεγονός κατηγορίας", - "addon.calendar.typeclose": "Κλείσιμο γεγονότος", - "addon.calendar.typecourse": "Γεγονός μαθήματος", - "addon.calendar.typedue": "Γεγονός που λήγει", - "addon.calendar.typegradingdue": "Γεγονός λήξης βαθμολόγησης", - "addon.calendar.typegroup": "Ομαδικό γεγονός", - "addon.calendar.typeopen": "Ανοιχτό γεγονός", - "addon.calendar.typesite": "Γεγονός ιστοτόπου", - "addon.calendar.typeuser": "Γεγονός χρήστη", - "addon.calendar.upcomingevents": "Επικείμενα γεγονότα", - "addon.calendar.userevents": "Γεγονότα χρήστη", - "addon.calendar.wed": "Τετ", - "addon.calendar.wednesday": "Τετάρτη", - "addon.calendar.when": "Όταν", - "addon.calendar.yesterday": "Χθες", - "addon.competency.activities": "Δραστηριότητες", - "addon.competency.competencies": "Προσόντα", - "addon.competency.competenciesmostoftennotproficientincourse": "Προσόντα συνηθέστερα όχι άριστα σε αυτό το μάθημα", - "addon.competency.coursecompetencies": "Προσόντα μαθήματος", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Οι εκτιμήσεις προσόντων σε αυτό το μάθημα δεν επηρεάζουν τα σχέδια μάθησης.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Οι εκτιμήσεις προσόντων σε αυτό το μάθημα ενημερώνονται αμέσως στα σχέδια μάθησης.", - "addon.competency.crossreferencedcompetencies": "Διασταυρωμένα προσόντα", - "addon.competency.duedate": "Ημερομηνία λήξης", - "addon.competency.errornocompetenciesfound": "Δεν βρέθηκαν ικανότητες", - "addon.competency.evidence": "Αποδεικτικό", - "addon.competency.evidence_competencyrule": "Ο κανόνας του προσόντος ικανοποιήθηκε.", - "addon.competency.evidence_coursecompleted": "Το μάθημα «{{$a}}» ολοκληρώθηκε.", - "addon.competency.evidence_coursemodulecompleted": "Η δραστηριότητα «{{$a}}» ολοκληρώθηκε.", - "addon.competency.evidence_courserestored": "Η εκτίμηση επαναφέρθηκε μαζί με το μάθημα «{{$a}}».", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Το αποδεικτικό προηγούμενης μάθησης «{{$a}}» συνδέθηκε.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Το αποδεικτικό προηγούμενης μάθησης «{{$a}}» αποσυνδέθηκε.", - "addon.competency.evidence_manualoverride": "Η εκτίμηση του προσόντος έγινε μη αυτόματα.", - "addon.competency.evidence_manualoverrideincourse": "Η εκτίμηση του προσόντος έγινε μη αυτόματα στο μάθημα «{{$a}}».", - "addon.competency.evidence_manualoverrideinplan": "Η εκτίμηση του προσόντος έγινε μη αυτόματα στο σχέδιο μάθησης «{{$a}}».", - "addon.competency.learningplancompetencies": "Προσόντα σχεδίου μάθησης", - "addon.competency.learningplans": "Σχέδια μάθησης", - "addon.competency.myplans": "Τα σχέδια μάθησής μου", - "addon.competency.noactivities": "Δεν υπάρχουν δραστηριότητες", - "addon.competency.nocompetencies": "Δεν βρέθηκαν ικανότητες", - "addon.competency.nocompetenciesincourse": "Δεν συνδέθηκαν καθόλου προσόντα με αυτό το μάθημα.", - "addon.competency.nocrossreferencedcompetencies": "Κανένα άλλο (υπο)προσόν δεν έχει αναφερθεί σε αυτό το προσόν.", - "addon.competency.noevidence": "Κανένα αποδεικτικό", - "addon.competency.noplanswerecreated": "Δεν δημιουργήθηκαν καθόλου σχέδια μάθησης.", - "addon.competency.nouserplanswithcompetency": "Κανένα σχέδιο μάθησης δεν περιλαμβάνει αυτήν την ικανότητα.", - "addon.competency.path": "Μονοπάτι:", - "addon.competency.planstatusactive": "Ενεργό", - "addon.competency.planstatuscomplete": "Πλήρες", - "addon.competency.planstatusdraft": "Προσχέδιο", - "addon.competency.planstatusinreview": "Εξετάζεται", - "addon.competency.planstatuswaitingforreview": "Αναμονή για εξέταση", - "addon.competency.proficient": "Επαρκής", - "addon.competency.progress": "Πρόοδος", - "addon.competency.rating": "Αξιολόγηση", - "addon.competency.reviewstatus": "Κατάσταση εξέτασης", - "addon.competency.status": "Κατάσταση", - "addon.competency.template": "Υπόδειγμα σχεδίου μάθησης", - "addon.competency.uponcoursecompletion": "Μόλις ολοκληρωθεί το μάθημα:", - "addon.competency.usercompetencystatus_idle": "Σε αδράνεια", - "addon.competency.usercompetencystatus_inreview": "Εξετάζεται", - "addon.competency.usercompetencystatus_waitingforreview": "Σε αναμονή για εξέταση", - "addon.competency.userplans": "Σχέδια μάθησης", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} από {{$a.y}} προσόντα σε άριστο βαθμό", - "addon.competency.xcompetenciesproficientoutofyincourse": "Είστε άριστος σε {{$a.x}} από {{$a.y}} προσόντα σε αυτό το μάθημα.", - "addon.coursecompletion.complete": "Ολοκλήρωση", - "addon.coursecompletion.completecourse": "Ολοκληρωμένο μάθημα", - "addon.coursecompletion.completed": "Ολοκληρωμένο", - "addon.coursecompletion.completiondate": "Ημερομηνία ολοκλήρωσης", - "addon.coursecompletion.completionmenuitem": "Ολοκλήρωση", - "addon.coursecompletion.couldnotloadreport": "Δεν ήταν δυνατή η φόρτωση της αναφοράς ολοκλήρωσης του μαθήματος, δοκιμάστε ξανά αργότερα.", - "addon.coursecompletion.coursecompletion": "Ολοκλήρωση μαθήματος", - "addon.coursecompletion.criteria": "Κριτήρια", - "addon.coursecompletion.criteriagroup": "Ομάδα κριτηρίων", - "addon.coursecompletion.criteriarequiredall": "Όλα τα παρακάτω κριτήρια είναι απαραίτητα", - "addon.coursecompletion.criteriarequiredany": "Τα παρακάτω κριτήρια είναι απαραίτητα", - "addon.coursecompletion.inprogress": "Σε εξέλιξη", - "addon.coursecompletion.manualselfcompletion": "Χειροκίνητη αυτο-ολοκλήρωση", - "addon.coursecompletion.nottracked": "Δεν παρακολουθείται, επί του παρόντος, η ολοκλήρωση αυτού του μαθήματος", - "addon.coursecompletion.notyetstarted": "Δεν έχει ξεκινήσει ακόμα", - "addon.coursecompletion.pending": "Σε εκκρεμότητα", - "addon.coursecompletion.required": "Απαιτείται", - "addon.coursecompletion.requiredcriteria": "Απαιτούμενα κριτήρια", - "addon.coursecompletion.requirement": "Απαίτηση", - "addon.coursecompletion.status": "Κατάσταση", - "addon.coursecompletion.viewcoursereport": "Εμφάνιση αναφοράς μαθήματος", - "addon.files.couldnotloadfiles": "Η λίστα των αρχείων δεν φορτώθηκε.", - "addon.files.emptyfilelist": "Δεν υπάρχουν αρχεία.", - "addon.files.erroruploadnotworking": "Δυστυχώς, προσωρινά δεν είναι δυνατό να ανεβάσετε τα αρχεία στον ιστότοπό σας.", - "addon.files.files": "Αρχεία", - "addon.files.privatefiles": "Προσωπικά αρχεία", - "addon.files.sitefiles": "Αρχεία ιστοτόπου", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Διαμόρφωση συσκευών", - "addon.messages.acceptandaddcontact": "Αποδοχή και προσθήκη στις επαφές", - "addon.messages.addcontact": "Προσθήκη επαφής", - "addon.messages.addcontactconfirm": "Είστε βέβαιοι ότι θέλετε να προσθέσετε τον/την {{$a}} στις επαφές σας;", - "addon.messages.addtofavourites": "Επισήμανση συνομιλίας με αστερίσκο", - "addon.messages.addtoyourcontacts": "Προσθήκη στις επαφές", - "addon.messages.blocknoncontacts": "Φραγή όλων των νέων μηνυμάτων από χρήστες που δεν βρίσκονται στη λίστα επαφών σας.", - "addon.messages.blockuser": "Αποκλεισμός χρήστη", - "addon.messages.blockuserconfirm": "Είστε βέβαιοι ότι θέλετε να αποκλείσετε τον/την {{$a}};", - "addon.messages.contactableprivacy": "Αποδοχή μηνυμάτων από:", - "addon.messages.contactableprivacy_coursemember": "Οι επαφές μου και οποιοσδήποτε από τα μαθήματά μου", - "addon.messages.contactableprivacy_onlycontacts": "Μόνο οι επαφές μου", - "addon.messages.contactableprivacy_site": "Οποιοσδήποτε στον ιστότοπο", - "addon.messages.contactblocked": "Η επαφή αποκλείστηκε", - "addon.messages.contactlistempty": "Η λίστα επαφών είναι κενή", - "addon.messages.contactname": "Όνομα επαφής", - "addon.messages.contactrequestsent": "Το αίτημα επαφής στάλθηκε", - "addon.messages.contacts": "Επαφές", - "addon.messages.conversationactions": "Μενού ενεργειών συνομιλίας", - "addon.messages.decline": "Άρνηση", - "addon.messages.deleteallconfirm": "Είστε βέβαιοι ότι θέλετε να διαγράψετε όλη αυτή τη συνομιλία; Δεν θα διαγραφεί το αντίγραφο των άλλων συμμετεχόντων στη συζήτηση.", - "addon.messages.deleteallselfconfirm": "Σίγουρα θέλετε να διαγράψετε ολόκληρη αυτή την προσωπική συνομιλία;", - "addon.messages.deleteconversation": "Διαγραφή συζήτησης", - "addon.messages.deleteforeveryone": "Διαγραφή για μένα και για όλους τους άλλους", - "addon.messages.deletemessage": "Διαγραφή μηνύματος", - "addon.messages.deletemessageconfirmation": "Σίγουρα θέλετε να διαγράψετε αυτό το μήνυμα; Θα διαγραφεί μόνο από το ιστορικό μηνυμάτων σας και θα εξακολουθεί να είναι ορατό από το χρήστη που έστειλε ή έλαβε το μήνυμα.", - "addon.messages.errordeletemessage": "Σφάλμα κατά τη διαγραφή του μηνύματος.", - "addon.messages.errorwhileretrievingcontacts": "Σφάλμα κατά την ανάκτηση των επαφών από τον εξυπηρετητή.", - "addon.messages.errorwhileretrievingdiscussions": "Σφάλμα κατά την ανάκτηση των συζητήσεων από τον εξυπηρετητή.", - "addon.messages.errorwhileretrievingmessages": "Σφάλμα κατά την ανάκτηση των μηνυμάτων από τον εξυπηρετητή.", - "addon.messages.errorwhileretrievingusers": "Σφάλμα κατά την λήψη χρηστών από τον εξυπηρετητή.", - "addon.messages.groupconversations": "Ομάδα", - "addon.messages.groupinfo": "Στοιχεία ομάδας", - "addon.messages.individualconversations": "Ιδιωτικές", - "addon.messages.info": "Πληροφορίες χρήστη", - "addon.messages.isnotinyourcontacts": "Ο/Η {{$a}} δεν βρίσκεται στις επαφές σας", - "addon.messages.message": "Μήνυμα", - "addon.messages.messagenotsent": "Το μήνυμα δεν στάλθηκε, δοκιμάστε ξανά αργότερα.", - "addon.messages.messagepreferences": "Προτιμήσεις μηνυμάτων", - "addon.messages.messages": "Μηνύματα", - "addon.messages.muteconversation": "Σίγαση", - "addon.messages.mutedconversation": "Συζήτηση σε σίγαση", - "addon.messages.newmessage": "Νέο μήνυμα", - "addon.messages.newmessages": "Νέα μηνύματα", - "addon.messages.nocontactrequests": "Καθόλου αιτήματα επαφών", - "addon.messages.nocontactsgetstarted": "Καθόλου επαφές", - "addon.messages.nofavourites": "Δεν υπάρχουν συνομιλίες με αστερίσκο", - "addon.messages.nogroupconversations": "Καθόλου ομαδικές συζητήσεις", - "addon.messages.noindividualconversations": "Καθόλου ιδιωτικές συζητήσεις", - "addon.messages.nomessagesfound": "Δε βρέθηκαν μηνύματα", - "addon.messages.noncontacts": "Μη επαφές", - "addon.messages.nousersfound": "Δεν βρέθηκαν χρήστες", - "addon.messages.numparticipants": "{{$a}} συμμετέχοντες", - "addon.messages.removecontact": "Αφαίρεση επαφής", - "addon.messages.removecontactconfirm": "Είστε βέβαιοι ότι θέλετε να καταργήσετε τον/την {{$a}} από τις επαφές σας;", - "addon.messages.removefromfavourites": "Αφαίρεση αστερίσκου", - "addon.messages.removefromyourcontacts": "Αφαίρεση από τις επαφές", - "addon.messages.requests": "Αιτήματα", - "addon.messages.requirecontacttomessage": "Θα πρέπει να ζητήσετε από τον/την {{$a}} να σας προσθέσει ως επαφή για να μπορέσετε να στείλετε μήνυμα.", - "addon.messages.searchcombined": "Αναζήτηση επαφών και μηνυμάτων", - "addon.messages.selfconversation": "Προσωπικός χώρος", - "addon.messages.selfconversationdefaultmessage": "Αποθήκευση προσχεδίων μηνυμάτων, συνδέσμων, σημειώσεων κλπ. για πρόσβαση αργότερα.", - "addon.messages.sendcontactrequest": "Αποστολή αιτήματος δημιουργίας επαφής", - "addon.messages.showdeletemessages": "Εμφάνιση διαγραμμένων μηνυμάτων", - "addon.messages.type_blocked": "Μπλοκαρισμένοι", - "addon.messages.type_offline": "Εκτός σύνδεσης", - "addon.messages.type_online": "Εντός σύνδεσης", - "addon.messages.type_search": "Αποτελέσματα αναζήτησης", - "addon.messages.type_strangers": "Άλλοι", - "addon.messages.unabletomessage": "Δεν μπορείτε να στείλετε μήνυμα σε αυτόν τον χρήστη", - "addon.messages.unblockuser": "Ξεκλείδωμα χρήστη", - "addon.messages.unblockuserconfirm": "Είστε βέβαιοι ότι θέλετε να ξεκλειδώσετε τον/την {{$a}};", - "addon.messages.unmuteconversation": "Αναίρεση σίγασης", - "addon.messages.useentertosend": "Χρήση του πλήκτρου Enter για τέλος εισόδου & αποστολή", - "addon.messages.useentertosenddescdesktop": "Εάν απενεργοποιηθεί, μπορείτε να χρησιμοποιήσετε το συνδυασμό πλήκτρων Ctrl+Enter για να στείλετε το μήνυμα.", - "addon.messages.useentertosenddescmac": "Εάν απενεργοποιηθεί, μπορείτε να χρησιμοποιήσετε το συνδυασμό πλήκτρων Cmd+Enter για να στείλετε το μήνυμα.", - "addon.messages.userwouldliketocontactyou": "Ο/Η {{$a}} θα ήθελε να επικοινωνήσει μαζί σας", - "addon.messages.warningconversationmessagenotsent": "Αδυναμία αποστολής μηνύματος/-άτων στην συζήτηση {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Το/Τα μηνύματα δεν μπόρεσαν να σταλούν στο χρήστη {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Θα ήθελαν να επικοινωνήσουν μαζί σας", - "addon.messages.you": "Εσείς:", - "addon.messages.youhaveblockeduser": "Έχετε αποκλείσει αυτόν τον χρήστη.", - "addon.messages.yourcontactrequestpending": "Η αίτηση επαφής σας επικοινωνίας εκκρεμεί για τον/την {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Παρακαλώ δεχθείτε τη δήλωση υποβολής.", - "addon.mod_assign.addattempt": "Να επιτρέπεται και άλλη προσπάθεια", - "addon.mod_assign.addnewattempt": "Προσθήκη μίας νέας προσπάθειας", - "addon.mod_assign.addnewattemptfromprevious": "Προσθήκη νέας προσπάθειας βασισμένη σε προηγούμενη υποβολή", - "addon.mod_assign.addsubmission": "Προσθήκη υποβολής", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Τα στοιχεία της εργασίας και η φόρμα υποβολής θα είναι διαθέσιμα από το {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Ημερομηνία έναρξης υποβολών", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Για αυτή την ανάθεση θα γίνουν δεκτές υποβολές από {{$a}}", - "addon.mod_assign.applytoteam": "Εφαρμογή βαθμών και ανατροφοδότησης σε ολόκληρη την ομάδα", - "addon.mod_assign.assignmentisdue": "Η προθεσμία έχει λήξει", - "addon.mod_assign.attemptnumber": "Αριθμός προσπάθειας", - "addon.mod_assign.attemptreopenmethod": "Να ξανανοίξουν οι προσπάθειες", - "addon.mod_assign.attemptreopenmethod_manual": "Χειροκίνητα", - "addon.mod_assign.attemptreopenmethod_untilpass": "Αυτόματα μέχρι να επιτύχει", - "addon.mod_assign.attemptsettings": "Ρυθμίσεις προσπάθειας", - "addon.mod_assign.cannoteditduetostatementsubmission": "Δεν μπορείτε να προσθέσετε ή να επεξεργαστείτε μια υποβολή στην εφαρμογή, γιατί δεν κατέστη δυνατό να ανακτηθεί η δήλωση υποβολής από τον ιστότοπο.", - "addon.mod_assign.cannotgradefromapp": "Μερικές μέθοδοι βαθμολόγησης δεν υποστηρίζονται ακόμα από την εφαρμογή και δεν μπορεί να τροποποιηθεί.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Δεν μπορείτε να υποβάλετε για βαθμολόγηση στην εφαρμογή, γιατί δεν κατέστη δυνατό να ανακτηθεί η δήλωση υποβολής από τον ιστότοπο.", - "addon.mod_assign.confirmsubmission": "Σίγουρα θέλετε να υποβάλετε την εργασία σας προς βαθμολόγηση; Δε θα μπορείτε να κάνετε περαιτέρω αλλαγές.", - "addon.mod_assign.currentattempt": "Αυτή είναι η {{$a}} προσπάθεια.", - "addon.mod_assign.currentattemptof": "Αυτή είναι η {{$a.attemptnumber}} προσπάθεια ( {{$a.maxattempts}} προσπάθειες επιτρέπονται).", - "addon.mod_assign.currentgrade": "Τωρινός βαθμός στο βαθμολόγιο", - "addon.mod_assign.cutoffdate": "Ημερομηνία τερματισμού (υποβολών)", - "addon.mod_assign.defaultteam": "Προεπιλεγμένη ομάδα", - "addon.mod_assign.duedate": "Οφειλόμενη ημερομηνία", - "addon.mod_assign.duedateno": "Χωρίς οφειλόμενη ημερομηνία", - "addon.mod_assign.duedatereached": "Η οφειλόμενη ημερομηνία για την εργασία αυτή έχει τώρα περάσει", - "addon.mod_assign.editingstatus": "Επεξεργασία κατάστασης", - "addon.mod_assign.editsubmission": "Επεξεργασία υποβολής", - "addon.mod_assign.erroreditpluginsnotsupported": "Δεν μπορείτε να προσθέσετε ή να επεξεργαστείτε μια υποβολή στην εφαρμογή κινητού επειδή ορισμένα πρόσθετα δεν υποστηρίζονται ακόμη για επεξεργασία:", - "addon.mod_assign.errorshowinginformation": "Δεν μπορούν να εμφανιστούν οι πληροφορίες υποβολής", - "addon.mod_assign.extensionduedate": "Οφειλόμενη ημερομηνία παράτασης", - "addon.mod_assign.feedbacknotsupported": "Αυτό το σχόλιο δεν υποστηρίζεται από την εφαρμογή και μπορεί να μην περιέχει όλες τις πληροφορίες", - "addon.mod_assign.grade": "Βαθμός", - "addon.mod_assign.graded": "Βαθμολογήθηκε", - "addon.mod_assign.gradedby": "Βαθμολογήθηκε από", - "addon.mod_assign.gradedfollowupsubmit": "Βαθμολογήθηκε - παραλήφθηκε η υποβολή επακόλουθης δραστηριότητας", - "addon.mod_assign.gradedon": "Βαθμολογήθηκε στις", - "addon.mod_assign.gradelocked": "Αυτός ο βαθμός είναι κλειδωμένος ή έχει παρακαμφθεί στο βαθμολόγιο.", - "addon.mod_assign.gradenotsynced": "Η βαθμολογία δεν συγχρονίστηκε", - "addon.mod_assign.gradeoutof": "Βαθμός στα {{$a}}", - "addon.mod_assign.gradingstatus": "Κατάσταση βαθμολόγησης", - "addon.mod_assign.groupsubmissionsettings": "Ρυθμίσεις ομαδικής υποβολής", - "addon.mod_assign.hiddenuser": "Συμμετέχων", - "addon.mod_assign.latesubmissions": "Καθυστερημένες υποβολές", - "addon.mod_assign.latesubmissionsaccepted": "Επιτρέπεται μέχρι {{$a}}", - "addon.mod_assign.markingworkflowstate": "Κατάσταση ροής εργασίας βαθμολόγησης", - "addon.mod_assign.markingworkflowstateinmarking": "Σε βαθμολόγηση", - "addon.mod_assign.markingworkflowstateinreview": "Εξετάζεται", - "addon.mod_assign.markingworkflowstatenotmarked": "Μη βαθμολογημένη", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Βαθμολογία ανακοινώσιμη", - "addon.mod_assign.markingworkflowstatereadyforreview": "Βαθμολόγηση ολοκληρωμένη", - "addon.mod_assign.markingworkflowstatereleased": "Ανακοινώθηκε", - "addon.mod_assign.modulenameplural": "Αναθέσεις εργασιών", - "addon.mod_assign.multipleteams": "Μέλος περισσότερων από μία ομάδων", - "addon.mod_assign.multipleteams_desc": "Η ανάθεση απαιτεί την υποβολή σε ομάδες. Είστε μέλος περισσότερων από μία ομάδων. Για να μπορέσετε να υποβάλετε εργασία πρέπει να είστε μέλος μόνο μιας ομάδας. Επικοινωνήστε με τον διδάσκοντά σας για να αλλάξετε την συμμετοχή σας σε ομάδες.", - "addon.mod_assign.noattempt": "Καμία προσπάθεια", - "addon.mod_assign.nomoresubmissionsaccepted": "Επιτρέπονται μόνο στους συμμετέχοντες που τους έχει χορηγηθεί παράταση", - "addon.mod_assign.noonlinesubmissions": "Δεν απαιτείται κάποια υποβολή", - "addon.mod_assign.nosubmission": "Δεν έχει υποβληθεί τίποτα για την εργασία αυτή", - "addon.mod_assign.notallparticipantsareshown": "Συμμετέχοντες χωρίς υποβολές δεν προβάλλονται", - "addon.mod_assign.noteam": "Δεν είναι μέλος καμίας ομάδας", - "addon.mod_assign.noteam_desc": "Αυτή η ανάθεση απαιτεί την υποβολή σε ομάδες. Δεν είστε μέλος οποιασδήποτε ομάδας, έτσι δεν μπορείτε να δημιουργήσετε μια υποβολή. Επικοινωνήστε με τον διδάσκοντά σας για να προστεθείτε σε μια ομάδα.", - "addon.mod_assign.notgraded": "Χωρίς βαθμό", - "addon.mod_assign.numberofdraftsubmissions": "Προσχέδια", - "addon.mod_assign.numberofparticipants": "Συμμετέχοντες", - "addon.mod_assign.numberofsubmissionsneedgrading": "Απαιτείται βαθμολόγηση", - "addon.mod_assign.numberofsubmittedassignments": "Υποβλήθηκαν", - "addon.mod_assign.numberofteams": "Ομάδες", - "addon.mod_assign.numwords": "{{$a}} λέξεις", - "addon.mod_assign.outof": "{{$a.current}} από {{$a.total}}", - "addon.mod_assign.overdue": "Η εργασία είναι εκπρόθεσμη κατά: {{$a}}", - "addon.mod_assign.submission": "Υποβολή", - "addon.mod_assign.submissioneditable": "Ο μαθητής μπορεί να επεξεργαστεί αυτήν την υποβολή", - "addon.mod_assign.submissionnoteditable": "Ο μαθητής δεν μπορεί να επεξεργαστεί αυτήν την υποβολή", - "addon.mod_assign.submissionnotsupported": "Η υποβολή αυτή δεν υποστηρίζεται από την εφαρμογή κινητού και μπορεί να μην περιέχει όλα τα στοιχεία.", - "addon.mod_assign.submissionslocked": "Αυτή η ανάθεση δεν δέχεται υποβολές", - "addon.mod_assign.submissionstatus": "Κατάσταση Υποβολής", - "addon.mod_assign.submissionstatus_": "Καμία υποβολή", - "addon.mod_assign.submissionstatus_draft": "Προσχέδιο (δεν υποβλήθηκε)", - "addon.mod_assign.submissionstatus_marked": "Βαθμολογήθηκε", - "addon.mod_assign.submissionstatus_new": "Καμία υποβολή", - "addon.mod_assign.submissionstatus_reopened": "Άνοιξε ξανά", - "addon.mod_assign.submissionstatus_submitted": "Υποβλήθηκε για βαθμολόγηση", - "addon.mod_assign.submissionstatusheading": "Κατάσταση Υποβολής", - "addon.mod_assign.submissionteam": "Ομάδα", - "addon.mod_assign.submitassignment": "Υποβολή εργασίας", - "addon.mod_assign.submitassignment_help": "Από τη στιγμή που θα υποβληθεί η εργασία δεν θα μπορείτε να κάνετε οποιαδήποτε αλλαγή.", - "addon.mod_assign.submittedearly": "Η εργασία υποβλήθηκε νωρίτερα κατά {{$a}}", - "addon.mod_assign.submittedlate": "Η εργασία υποβλήθηκε {{$a}} αργότερα", - "addon.mod_assign.timemodified": "Τελευταία τροποποίηση", - "addon.mod_assign.timeremaining": "Χρόνος που απομένει", - "addon.mod_assign.ungroupedusers": "Η ρύθμιση «Απαίτηση για υποβολή ανά ομάδες» είναι ενεργοποιημένη και ορισμένοι χρήστες είτε δεν είναι μέλη κάποιας ομάδας, είτε είναι μέλη περισσότερων από μία ομάδων, οπότε δεν μπορούν να υποβάλουν εργασίες.", - "addon.mod_assign.ungroupedusersoptional": "Η ρύθμιση «Οι μαθητές υποβάλλουν σε ομάδες» είναι ενεργοποιημένη και ορισμένοι χρήστες είτε δεν είναι μέλος καμίας ομάδας, είτε είναι μέλος περισσότερων από μία ομάδων. Παρακαλούμε, λάβετε υπόψη ότι οι μαθητές αυτοί θα υποβάλλουν ως μέλη της προεπιλεγμένης ομάδας.", - "addon.mod_assign.unlimitedattempts": "Χωρίς περιορισμό", - "addon.mod_assign.userswhoneedtosubmit": "Χρήστες που πρέπει να υποβάλουν: {{$a}}", - "addon.mod_assign.userwithid": "Ο χρήστης με αναγνωριστικό {{id}}", - "addon.mod_assign.viewsubmission": "Εμφάνιση υποβολής", - "addon.mod_assign.warningsubmissiongrademodified": "Ο βαθμός υποβολής τροποποιήθηκε στον ιστότοπο.", - "addon.mod_assign.warningsubmissionmodified": "Η υποβολή του χρήστη τροποποιήθηκε στον ιστότοπο.", - "addon.mod_assign.wordlimit": "Όριο λέξεων", - "addon.mod_assign_feedback_comments.pluginname": "Σχόλια ανατροφοδότησης", - "addon.mod_assign_feedback_editpdf.pluginname": "Επισημείωση PDF", - "addon.mod_assign_feedback_file.pluginname": "Αρχείο ανατροφοδότησης", - "addon.mod_assign_submission_comments.pluginname": "Σχόλια υποβολής", - "addon.mod_assign_submission_file.pluginname": "Υποβολές αρχείων", - "addon.mod_assign_submission_onlinetext.pluginname": "Υποβολές κειμένων εντός σύνδεσης", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Το όριο λέξεων για αυτή την εργασία είναι {{$a.limit}} και προσπαθείτε να υποβάλετε {{$a.count}} λέξεις. Ελέγξτε την υποβολή σας και δοκιμάστε ξανά.", - "addon.mod_book.errorchapter": "Σφάλμα ανάγνωσης κεφαλαίου βιβλίου", - "addon.mod_book.modulenameplural": "Βιβλία", - "addon.mod_book.navnexttitle": "Επόμενο: {{$a}}", - "addon.mod_book.navprevtitle": "Προηγούμενο: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Κεφάλαια βιβλίου", - "addon.mod_book.toc": "Πίνακας περιεχομένων", - "addon.mod_chat.beep": "Ηχητικό σήμα", - "addon.mod_chat.chatreport": "Σύνοδοι συνομιλιών", - "addon.mod_chat.currentusers": "Τρέχοντες χρήστες", - "addon.mod_chat.enterchat": "Κάντε κλικ εδώ για να μπείτε στο δωμάτιο συνομιλίας τώρα", - "addon.mod_chat.entermessage": "Δώστε το μήνυμά σας", - "addon.mod_chat.errorwhileconnecting": "Σφάλμα κατά τη σύνδεση στην συνομιλία.", - "addon.mod_chat.errorwhilegettingchatdata": "Σφάλμα κατά την ανάκτηση των δεδομένων της συνομιλίας.", - "addon.mod_chat.errorwhilegettingchatusers": "Σφάλμα κατά την ανάκτηση των χρηστών της συνομιλίας.", - "addon.mod_chat.errorwhileretrievingmessages": "Σφάλμα κατά την ανάκτηση των μηνυμάτων από τον εξυπηρετητή.", - "addon.mod_chat.errorwhilesendingmessage": "Σφάλμα κατά την αποστολή του μηνύματος.", - "addon.mod_chat.messagebeepseveryone": "Ο/Η {{$a}} στέλνει ηχητικό σήμα σε όλους!", - "addon.mod_chat.messagebeepsyou": "Ο/Η {{$a}} μόλις σας έστειλε ηχητικό σήμα!", - "addon.mod_chat.messageenter": "Ο/Η {{$a}} μόλις έχει μπει σε αυτή τη συνομιλία", - "addon.mod_chat.messageexit": "Ο/Η {{$a}} έχει φύγει από αυτή τη συνομιλία", - "addon.mod_chat.messages": "Μηνύματα", - "addon.mod_chat.messageyoubeep": "Στείλατε ηχητικό σήμα στον/στην {{$a}}", - "addon.mod_chat.modulenameplural": "Συνομιλίες", - "addon.mod_chat.mustbeonlinetosendmessages": "Πρέπει να είστε συνδεδεμένοι για να στείλετε μηνύματα.", - "addon.mod_chat.nomessages": "Καθόλου μηνύματα ακόμη", - "addon.mod_chat.nosessionsfound": "Δεν βρέθηκε καμία σύνοδος", - "addon.mod_chat.saidto": "είπε στον/στην", - "addon.mod_chat.send": "Αποστολή", - "addon.mod_chat.sessionstart": "Η επόμενη σύνοδος συνομιλίας θα αρχίσει στις {{$a.date}}, ({{$a.fromnow}} από τώρα)", - "addon.mod_chat.showincompletesessions": "Εμφάνιση μη ολοκληρωμένων συνόδων", - "addon.mod_chat.talk": "Μιλήστε", - "addon.mod_chat.viewreport": "Δείτε περασμένες συνόδους", - "addon.mod_choice.cannotsubmit": "Λυπούμαστε, υπήρξε πρόβλημα κατά την υποβολή της επιλογής σας. Παρακαλώ προσπαθήστε ξανά.", - "addon.mod_choice.choiceoptions": "Επιλογές επιλογής", - "addon.mod_choice.errorgetchoice": "Σφάλμα κατά τη λήψη των επιλεγμένων δεδομένων.", - "addon.mod_choice.expired": "Η δραστηριότητα αυτή έκλεισε στις {{$a}}.", - "addon.mod_choice.full": "(Πλήρες)", - "addon.mod_choice.modulenameplural": "Επιλογές", - "addon.mod_choice.noresultsviewable": "Τα αποτελέσματα δεν είναι ορατά προς το παρόν.", - "addon.mod_choice.notopenyet": "Η δραστηριότητα αυτή δεν είναι διαθέσιμη μέχρι {{$a}}.", - "addon.mod_choice.numberofuser": "Αριθμός αποκρίσεων", - "addon.mod_choice.numberofuserinpercentage": "Ποσοστό αποκρίσεων", - "addon.mod_choice.previewonly": "Πρόκειται για μια προεπισκόπηση των διαθέσιμων επιλογών για αυτήν τη δραστηριότητα. Δεν θα μπορείτε να υποβάλετε την επιλογή σας μέχρι {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Ανώνυμα αποτελέσματα θα δημοσιευθούν μετά την απάντησή σας.", - "addon.mod_choice.publishinfoanonclose": "Ανώνυμα αποτελέσματα θα δημοσιευτούν μετά τη λήξη της δραστηριότητας.", - "addon.mod_choice.publishinfofullafter": "Τα πλήρη αποτελέσματα, που δείχνουν τις επιλογές όλων, θα δημοσιευθούν μετά την απάντησή σας.", - "addon.mod_choice.publishinfofullclose": "Τα πλήρη αποτελέσματα, που δείχνουν τις επιλογές όλων, θα δημοσιευθούν μετά τη λήξη της δραστηριότητας.", - "addon.mod_choice.publishinfonever": "Τα αποτελέσματα αυτής της δραστηριότητας δεν θα δημοσιευθούν μετά την απάντησή σας.", - "addon.mod_choice.removemychoice": "Διαγραφή της επιλογής μου", - "addon.mod_choice.responses": "Αποκρίσεις", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% των χρηστών επέλεξε την επιλογή: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Εμφάνιση γραφήματος", - "addon.mod_choice.resultsnotsynced": "Η τελευταία απόκρισή σας χρειάζεται συγχρονισμό πριν συμπεριληφθεί στα αποτελέσματα.", - "addon.mod_choice.savemychoice": "Αποθήκευση επιλογής", - "addon.mod_choice.userchoosethisoption": "Χρήστες που επέλεξαν αυτήν την επιλογή", - "addon.mod_choice.yourselection": "Η επιλογή σας", - "addon.mod_data.addentries": "Προσθήκη καταχωρήσεων", - "addon.mod_data.advancedsearch": "Προχωρημένη αναζήτηση", - "addon.mod_data.alttext": "Εναλλακτικό κείμενο", - "addon.mod_data.approve": "Έγκριση", - "addon.mod_data.approved": "Εγκρίθηκε", - "addon.mod_data.ascending": "Αύξουσα", - "addon.mod_data.authorfirstname": "Όνομα συγγραφέα", - "addon.mod_data.authorlastname": "Επίθετο συγγραφέα", - "addon.mod_data.confirmdeleterecord": "Σίγουρα θέλετε να διαγραφεί αυτή η καταχώρηση;", - "addon.mod_data.descending": "Φθίνουσα", - "addon.mod_data.disapprove": "Αναίρεση έγκρισης", - "addon.mod_data.edittagsnotsupported": "Λυπούμαστε! Η επεξεργασία ετικετών δεν υποστηρίζεται από την εφαρμογή κινητού.", - "addon.mod_data.emptyaddform": "Δεν συμπληρώσατε κανένα από τα πεδία!", - "addon.mod_data.entrieslefttoadd": "Πρέπει να προσθέσετε {{$a.entriesleft}} επιπλέον καταχωρήσεις(η) ώστε να ολοκληρώσετε αυτήν τη δραστηριότητα", - "addon.mod_data.entrieslefttoaddtoview": "Πρέπει να προσθέσετε {{$a.entrieslefttoview}} επιπλέον καταχωρήσεις(η) πριν να μπορέσετε να δείτε τις καταχωρήσεις άλλων χρηστών.", - "addon.mod_data.errorapproving": "Σφάλμα έγκρισης ή απόρριψης καταχώρησης.", - "addon.mod_data.errordeleting": "Σφάλμα διαγραφής καταχώρησης.", - "addon.mod_data.errormustsupplyvalue": "Πρέπει να εισάγετε μια τιμή εδώ.", - "addon.mod_data.expired": "Δυστυχώς, η δραστηριότητα αυτή έκλεισε στις {{$a}} και δεν είναι πλέον διαθέσιμη", - "addon.mod_data.fields": "Πεδία", - "addon.mod_data.foundrecords": "Βρέθηκαν οι εγγραφές: {{$a.num}}/{{$a.max}} (Επαναρχικοποίηση φίλτρων)", - "addon.mod_data.gettinglocation": "Λήψη τοποθεσίας", - "addon.mod_data.latlongboth": "Απαιτούνται και γεωγραφικό πλάτος και γεωγραφικό μήκος.", - "addon.mod_data.locationpermissiondenied": "Η άδεια για πρόσβαση στην τοποθεσία σας απορρίφθηκε.", - "addon.mod_data.menuchoose": "Επιλέξτε....", - "addon.mod_data.modulenameplural": "Βάσεις δεδομένων", - "addon.mod_data.more": "Περισσότερα", - "addon.mod_data.mylocation": "Η τοποθεσία μου", - "addon.mod_data.nomatch": "Δεν βρέθηκαν καταχωρήσεις που να ταιριάζουν!", - "addon.mod_data.norecords": "Δεν υπάρχουν καταχωρήσεις στη βάση δεδομένων", - "addon.mod_data.notapproved": "Καταχώρηση μη εγκεκριμένη ακόμη.", - "addon.mod_data.notopenyet": "Συγγνώμη, αυτή η δραστηριότητα δεν είναι διαθέσιμη μέχρι {{$a}}", - "addon.mod_data.numrecords": "{{$a}} καταχωρήσεις", - "addon.mod_data.other": "Άλλο", - "addon.mod_data.recordapproved": "Η καταχώρηση εγκρίθηκε", - "addon.mod_data.recorddeleted": "Η καταχώρηση διαγράφηκε", - "addon.mod_data.recorddisapproved": "Η έγκριση της καταχώρησης ακυρώθηκε", - "addon.mod_data.resetsettings": "Επαναρχικοποίηση φίλτρων", - "addon.mod_data.search": "Αναζήτηση", - "addon.mod_data.searchbytagsnotsupported": "Λυπούμαστε! Η αναζήτηση κατά ετικέτες δεν υποστηρίζεται από την εφαρμογή κινητού.", - "addon.mod_data.selectedrequired": "Απαιτούνται όλα τα επιλεγμένα", - "addon.mod_data.single": "Ατομική προβολή", - "addon.mod_data.tagarea_data_records": "Εγγραφές δεδομένων", - "addon.mod_data.timeadded": "Χρόνος προσθήκης", - "addon.mod_data.timemodified": "Χρόνος τροποποίησης", - "addon.mod_data.usedate": "Συμπερίληψη στην αναζήτηση.", - "addon.mod_feedback.analysis": "Ανάλυση", - "addon.mod_feedback.anonymous": "Ανώνυμα", - "addon.mod_feedback.anonymous_entries": "Ανώνυμες καταχωρήσεις ({{$a}})", - "addon.mod_feedback.average": "Μέσος όρος", - "addon.mod_feedback.captchaofflinewarning": "Η ανατροφοδότηση με χρήση captcha δεν μπορεί να ολοκληρωθεί εκτός σύνδεσης, ή αν δεν έχει ρυθμιστεί, ή αν ο εξυπηρετητής δεν λειτουργεί.", - "addon.mod_feedback.complete_the_form": "Απαντήστε στις ερωτήσεις", - "addon.mod_feedback.completed_feedbacks": "Απαντήσεις που έχουν υποβληθεί", - "addon.mod_feedback.continue_the_form": "Συνεχίστε την απάντηση των ερωτήσεων", - "addon.mod_feedback.feedback_is_not_open": "Η ανατροφοδότηση δεν είναι ανοιχτή", - "addon.mod_feedback.feedback_submitted_offline": "Αυτή η ανατροφοδότηση έχει αποθηκευτεί για να υποβληθεί αργότερα.", - "addon.mod_feedback.feedbackclose": "Επιτρέπονται απαντήσεις μέχρι", - "addon.mod_feedback.feedbackopen": "Επιτρέπονται απαντήσεις από", - "addon.mod_feedback.mapcourses": "Αντιστοίχιση ανατροφοδότησης σε μαθήματα", - "addon.mod_feedback.maximal": "Μέγιστο", - "addon.mod_feedback.minimal": "Ελάχιστο", - "addon.mod_feedback.mode": "Λειτουργία", - "addon.mod_feedback.modulenameplural": "Ανατροφοδοτήσεις", - "addon.mod_feedback.next_page": "Επόμενη σελίδα", - "addon.mod_feedback.non_anonymous": "Το όνομα του χρήστη θα καταγραφεί και θα εμφανίζεται με τις απαντήσεις", - "addon.mod_feedback.non_anonymous_entries": "Χωρίς ανώνυμες καταχωρήσεις ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Μαθητές που δεν απάντησαν ({{$a}})", - "addon.mod_feedback.not_selected": "Δεν έχουν επιλεχθεί", - "addon.mod_feedback.not_started": "Δεν ξεκίνησαν", - "addon.mod_feedback.numberoutofrange": "Αριθμός εκτός εύρους", - "addon.mod_feedback.overview": "Επισκόπηση", - "addon.mod_feedback.page_after_submit": "Μήνυμα ολοκλήρωσης", - "addon.mod_feedback.preview": "Προεπισκόπηση", - "addon.mod_feedback.previous_page": "Προηγούμενη σελίδα", - "addon.mod_feedback.questions": "Ερωτήσεις", - "addon.mod_feedback.response_nr": "Αριθμός απόκρισης", - "addon.mod_feedback.responses": "Αποκρίσεις", - "addon.mod_feedback.save_entries": "Υποβολή των απαντήσεών σας", - "addon.mod_feedback.show_entries": "Εμφάνιση αποκρίσεων", - "addon.mod_feedback.show_nonrespondents": "Εμφάνιση όσων δεν αποκρίθηκαν", - "addon.mod_feedback.started": "Ξεκίνησε", - "addon.mod_feedback.this_feedback_is_already_submitted": "Έχετε ολοκληρώσει ήδη αυτή την δραστηριότητα.", - "addon.mod_folder.emptyfilelist": "Δεν υπάρχουν αρχεία.", - "addon.mod_folder.modulenameplural": "Φάκελοι", - "addon.mod_forum.addanewdiscussion": "Προσθήκη νέου θέματος συζήτησης", - "addon.mod_forum.addanewquestion": "Προσθήκη νέας ερώτησης", - "addon.mod_forum.addanewtopic": "Προσθήκη νέου θέματος", - "addon.mod_forum.addtofavourites": "Επισήμανση αυτής της συζήτησης με αστερίσκο", - "addon.mod_forum.advanced": "Προχωρημένο", - "addon.mod_forum.cannotadddiscussion": "Η προσθήκη συζητήσεων σε αυτό το φόρουμ απαιτεί συμμετοχή σε ομάδα.", - "addon.mod_forum.cannotadddiscussionall": "Δεν έχετε δικαίωμα προσθήκης νέου θέματος συζήτησης για όλους τους συμμετέχοντες.", - "addon.mod_forum.cannotcreatediscussion": "Δεν ήταν δυνατή η δημιουργία νέας συζήτησης", - "addon.mod_forum.couldnotadd": "Δεν ήταν δυνατή η προσθήκη του μηνύματός σας λόγω άγνωστου σφάλματος", - "addon.mod_forum.couldnotupdate": "Δεν ήταν δυνατή η ενημέρωση του μηνύματός σας λόγω άγνωστου σφάλματος", - "addon.mod_forum.cutoffdatereached": "Η ημερομηνία τερματισμού αναρτήσεων σε αυτό το φόρουμ έχει περάσει, οπότε δεν μπορείτε πλέον να αναρτήσετε σε αυτό.", - "addon.mod_forum.delete": "Διαγραφή", - "addon.mod_forum.deletedpost": "Η ανάρτηση διαγράφηκε", - "addon.mod_forum.deletesure": "Σίγουρα θέλετε να διαγράψετε αυτήν την ανάρτηση;", - "addon.mod_forum.discussion": "Συζήτηση", - "addon.mod_forum.discussionlistsortbycreatedasc": "Ταξινόμηση κατά ημερομηνία δημιουργίας σε αύξουσα σειρά", - "addon.mod_forum.discussionlistsortbycreateddesc": "Ταξινόμηση κατά την ημερομηνία δημιουργίας σε φθίνουσα σειρά", - "addon.mod_forum.discussionlistsortbylastpostasc": "Ταξινόμηση κατά την ημερομηνία δημιουργίας της τελευταίας ανάρτησης σε αύξουσα σειρά", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Ταξινόμηση κατά την ημερομηνία δημιουργίας της τελευταίας ανάρτησης σε φθίνουσα σειρά", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Ταξινόμηση κατά τον αριθμό των απαντήσεων σε αύξουσα σειρά", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Ταξινόμηση κατά τον αριθμό των απαντήσεων σε φθίνουσα σειρά", - "addon.mod_forum.discussionlocked": "Αυτή η συζήτηση κλειδώθηκε κι έτσι δεν μπορείτε να απαντήσετε σε αυτή.", - "addon.mod_forum.discussionpinned": "Καρφιτσωμένο", - "addon.mod_forum.discussionsubscription": "Εγγραφή στη συζήτηση", - "addon.mod_forum.edit": "Επεξεργασία", - "addon.mod_forum.erroremptymessage": "Το μήνυμα της ανάρτησης δεν μπορεί να είναι άδειο", - "addon.mod_forum.erroremptysubject": "Το θέμα της ανάρτησης δεν μπορεί να είναι άδειο.", - "addon.mod_forum.errorgetforum": "Σφάλμα κατά τη λήψη των δεδομένων του forum.", - "addon.mod_forum.errorgetgroups": "Σφάλμα κατά τη λήψη των ρυθμίσεων της ομάδας.", - "addon.mod_forum.errorposttoallgroups": "Αδυναμία δημιουργίας νέας συζήτησης σε όλες τις ομάδες.", - "addon.mod_forum.favouriteupdated": "Η επιλογή σας για αστερίσκο ενημερώθηκε.", - "addon.mod_forum.forumnodiscussionsyet": "Δεν υπάρχουν ακόμα θέματα συζήτησης σε αυτό το forum.", - "addon.mod_forum.group": "Ομάδα", - "addon.mod_forum.lastpost": "Τελευταία ανάρτηση", - "addon.mod_forum.lockdiscussion": "Κλείδωμα αυτής της συζήτησης", - "addon.mod_forum.lockupdated": "Η επιλογή κλειδώματος ενημερώθηκε.", - "addon.mod_forum.message": "Μήνυμα", - "addon.mod_forum.modeflatnewestfirst": "Εμφάνιση απαντήσεων οριζοντίως, με την πιο πρόσφατη πρώτη", - "addon.mod_forum.modeflatoldestfirst": "Εμφάνιση απαντήσεων οριζοντίως, με την παλαιότερη πρώτη", - "addon.mod_forum.modenested": "Εμφάνιση απαντήσεων σε φωλιασμένη μορφή", - "addon.mod_forum.modulenameplural": "Φόρουμ", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} συζητήσεις", - "addon.mod_forum.numreplies": "{{numreplies}} απαντήσεις", - "addon.mod_forum.pindiscussion": "Καρφίτσωμα αυτής της συζήτησης", - "addon.mod_forum.pinupdated": "Η επιλογή καρφιτσώματος ενημερώθηκε.", - "addon.mod_forum.postisprivatereply": "Αυτή η ανάρτηση είναι ιδιωτική. Δεν είναι ορατή σε άλλους χρήστες.", - "addon.mod_forum.posttoforum": "Ανάρτηση στο φόρουμ", - "addon.mod_forum.posttomygroups": "Αναρτήστε ένα αντίγραφο σε όλες τις ομάδες", - "addon.mod_forum.privatereply": "Απάντηση ιδιωτικά", - "addon.mod_forum.re": "Απάντηση:", - "addon.mod_forum.refreshdiscussions": "Ανανεώστε τις συζητήσεις", - "addon.mod_forum.refreshposts": "Ανανεώστε τα μηνύματα της συζήτησης", - "addon.mod_forum.removefromfavourites": "Αφαίρεση επισήμανσης αυτής της συζήτησης με αστερίσκο", - "addon.mod_forum.reply": "Απάντηση", - "addon.mod_forum.replyplaceholder": "Γράψτε την απάντησή σας ...", - "addon.mod_forum.subject": "Θέμα", - "addon.mod_forum.tagarea_forum_posts": "Αναρτήσεις φόρουμ", - "addon.mod_forum.thisforumhasduedate": "Η ημερομηνία λήξης για ανάρτηση σε αυτό το φόρουμ είναι {{$a}}.", - "addon.mod_forum.thisforumisdue": "Η ημερομηνία λήξης για ανάρτηση σε αυτό το φόρουμ ήταν {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Ξεκλείδωμα αυτής της συζήτησης", - "addon.mod_forum.unpindiscussion": "Ξεκαρφίτσωμα αυτής της συζήτησης", - "addon.mod_forum.unread": "Μη αναγνωσμένο", - "addon.mod_forum.unreadpostsnumber": "{{$a}} μη αναγνωσμένες αναρτήσεις", - "addon.mod_forum.yourreply": "Η απάντησή σας", - "addon.mod_glossary.addentry": "Προσθήκη νέας εγγραφής", - "addon.mod_glossary.aliases": "Λέξεις/-η κλειδιά", - "addon.mod_glossary.attachment": "Συνημμένο", - "addon.mod_glossary.browsemode": "Περιήγηση στις καταχωρήσεις", - "addon.mod_glossary.byalphabet": "Αλφαβητικά", - "addon.mod_glossary.byauthor": "Ομαδοποίηση ανά συγγραφέα", - "addon.mod_glossary.bycategory": "Ομαδοποίηση ανά κατηγορία", - "addon.mod_glossary.bynewestfirst": "Νεότερα πρώτα", - "addon.mod_glossary.byrecentlyupdated": "Ανανεώθηκαν πρόσφατα", - "addon.mod_glossary.bysearch": "Αναζήτηση", - "addon.mod_glossary.cannoteditentry": "Δεν είναι δυνατή η επεξεργασία της καταχώρισης", - "addon.mod_glossary.casesensitive": "Αυτή η εγγραφή είναι ευαίσθητη σε μικρά/κεφαλαία", - "addon.mod_glossary.categories": "Κατηγορίες", - "addon.mod_glossary.concept": "Έννοια", - "addon.mod_glossary.definition": "Ορισμός", - "addon.mod_glossary.entriestobesynced": "Entries που πρέπει να συγχρονιστούν", - "addon.mod_glossary.entrypendingapproval": "Εκκρεμεί η έγκριση για αυτή την καταχώρηση.", - "addon.mod_glossary.entryusedynalink": "Σε αυτήν την καταχώρηση θα γίνεται σύνδεση αυτόματα.", - "addon.mod_glossary.errconceptalreadyexists": "Αυτή η έννοια υπάρχει ήδη. Δεν επιτρέπονται διπλότυπα σε αυτό το γλωσσάριο.", - "addon.mod_glossary.errorloadingentries": "Παρουσιάστηκε σφάλμα κατά τη φόρτωση των καταχωρήσεων.", - "addon.mod_glossary.errorloadingentry": "Παρουσιάστηκε σφάλμα κατά τη φόρτωση της καταχώρησης.", - "addon.mod_glossary.errorloadingglossary": "Παρουσιάστηκε σφάλμα κατά τη φόρτωση του γλωσσαρίου.", - "addon.mod_glossary.fillfields": "Η έννοια και ο ορισμός είναι υποχρεωτικά πεδία.", - "addon.mod_glossary.fullmatch": "Ταίριασμα ολόκληρων λέξεων μόνο", - "addon.mod_glossary.linking": "Αυτόματοι σύνδεσμοι", - "addon.mod_glossary.modulenameplural": "Γλωσσάρια", - "addon.mod_glossary.noentriesfound": "Δεν βρέθηκαν καταχωρήσεις.", - "addon.mod_glossary.searchquery": "Αναζήτηση ερωτήματος", - "addon.mod_glossary.tagarea_glossary_entries": "Καταχωρήσεις γλωσσαρίου", - "addon.mod_h5pactivity.all_attempts": "Όλες οι προσπάθειες χρήστη", - "addon.mod_h5pactivity.answer_checked": "Η απάντηση ελέγχθηκε", - "addon.mod_h5pactivity.answer_correct": "Η απάντησή σας είναι σωστή", - "addon.mod_h5pactivity.answer_fail": "Λανθασμένη απάντηση", - "addon.mod_h5pactivity.answer_incorrect": "Η απάντησή σας είναι λανθασμένη", - "addon.mod_h5pactivity.answer_pass": "Σωστή απάντηση", - "addon.mod_h5pactivity.attempt": "Προσπάθεια", - "addon.mod_h5pactivity.attempt_completion_no": "Αυτή η προσπάθεια δεν έχει επισημανθεί ως ολοκληρωμένη", - "addon.mod_h5pactivity.attempt_completion_yes": "Αυτή η προσπάθεια είναι ολοκληρωμένη", - "addon.mod_h5pactivity.attempt_success_fail": "Αποτυχία", - "addon.mod_h5pactivity.attempt_success_pass": "Επιτυχία", - "addon.mod_h5pactivity.attempt_success_unknown": "Δεν έχει αναφερθεί", - "addon.mod_h5pactivity.attempts_none": "Αυτός ο χρήστης δεν έχει καμία προσπάθεια προς εμφάνιση.", - "addon.mod_h5pactivity.completion": "Ολοκλήρωση", - "addon.mod_h5pactivity.downloadh5pfile": "Λήψη αρχείου H5P", - "addon.mod_h5pactivity.duration": "Διάρκεια", - "addon.mod_h5pactivity.errorgetactivity": "Σφάλμα κατά τη λήψη δεδομένων δραστηριότητας H5P.", - "addon.mod_h5pactivity.filestatenotdownloaded": "Δεν έγινε λήψη του πακέτου H5P. Πρέπει να το κατεβάσετε για να μπορείτε να το χρησιμοποιήσετε.", - "addon.mod_h5pactivity.filestateoutdated": "Το πακέτο H5P έχει τροποποιηθεί από την τελευταία λήψη. Πρέπει να το κατεβάσετε ξανά για να μπορείτε να το χρησιμοποιήσετε.", - "addon.mod_h5pactivity.maxscore": "Μέγιστο σκορ", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Οι προσπάθειές μου", - "addon.mod_h5pactivity.no_compatible_track": "Αυτή η αλληλεπίδραση ({{$a}}) δεν παρέχει πληροφορίες παρακολούθησης ή η παρακολούθηση που παρέχεται δεν είναι συμβατή με την τρέχουσα έκδοση δραστηριότητας.", - "addon.mod_h5pactivity.offlinedisabledwarning": "Πρέπει να είστε συνδεδεμένοι για να δείτε το πακέτο H5P.", - "addon.mod_h5pactivity.outcome": "Μαθ. αποτέλεσμα", - "addon.mod_h5pactivity.previewmode": "Αυτό το περιεχόμενο εμφανίζεται σε λειτουργία προεπισκόπησης. Δεν θα αποθηκευτεί καμία παρακολούθηση προσπάθειας.", - "addon.mod_h5pactivity.result_fill-in": "Συμπληρώστε το κείμενο", - "addon.mod_h5pactivity.result_other": "Άγνωστος τύπος αλληλεπίδρασης", - "addon.mod_h5pactivity.review_my_attempts": "Προβολή των προσπαθειών μου", - "addon.mod_h5pactivity.score": "Σκορ", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} στα {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Ημερομηνία έναρξης", - "addon.mod_h5pactivity.totalscore": "Συνολικό σκορ", - "addon.mod_h5pactivity.viewattempt": "Προβολή προσπάθειας {{$a}}", - "addon.mod_imscp.deploymenterror": "Σφάλμα πακέτου περιεχομένου!", - "addon.mod_imscp.modulenameplural": "Πακέτο περιεχομένου IMS", - "addon.mod_imscp.showmoduledescription": "Εμφάνιση περιγραφής", - "addon.mod_imscp.toc": "Πίνακας περιεχομένων (TOC: Table Of Contents)", - "addon.mod_lesson.answer": "Απάντηση", - "addon.mod_lesson.attempt": "Προσπάθεια: {{$a}}", - "addon.mod_lesson.attemptheader": "Προσπάθεια", - "addon.mod_lesson.attemptsremaining": "Σας απομένουν {{$a}} προσπάθειες", - "addon.mod_lesson.averagescore": "Μέσο σκορ", - "addon.mod_lesson.averagetime": "Μέσος χρόνος", - "addon.mod_lesson.branchtable": "Περιεχόμενο (Πίν. διακλ.)", - "addon.mod_lesson.cannotfindattempt": "Σφάλμα: δεν ήταν δυνατή η εύρεση προσπάθειας", - "addon.mod_lesson.cannotfinduser": "Σφάλμα: δεν ήταν δυνατή η εύρεση χρηστών", - "addon.mod_lesson.clusterjump": "Ερώτηση που δεν εμφανίστηκε μέσα σε συστάδα", - "addon.mod_lesson.completed": "Ολοκληρώθηκε", - "addon.mod_lesson.congratulations": "Συγχαρητήρια - τελειώσατε την διδασκαλία", - "addon.mod_lesson.continue": "Συνέχεια", - "addon.mod_lesson.continuetonextpage": "Συνέχεια στην επόμενη σελίδα.", - "addon.mod_lesson.defaultessayresponse": "Η έκθεσή σας θα βαθμολογηθεί από τον διδάσκοντά σας.", - "addon.mod_lesson.detailedstats": "Αναλυτικά στατιστικά", - "addon.mod_lesson.didnotanswerquestion": "Δεν απάντησε αυτήν την ερώτηση.", - "addon.mod_lesson.displayofgrade": "Εμφάνιση βαθμού (μόνο για μαθητές)", - "addon.mod_lesson.displayscorewithessays": "

          Πήρατε {{$a.score}} στα {{$a.tempmaxgrade}} για τις αυτόματα βαθμολογούμενες ερωτήσεις.

          Οι {{$a.essayquestions}} ερωτήσεις έκθεσης θα βαθμολογηθούν και θα προστεθούν στο τελικό σας σκορ αργότερα.

          Ο τρέχων βαθμός σας χωρίς τις ερωτήσεις έκθεσης είναι {{$a.score}} στα {{$a.grade}}.

          ", - "addon.mod_lesson.displayscorewithoutessays": "Το σκορ σας είναι {{$a.score}} (στα {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Ο κωδικός πρόσβασης δεν μπορεί να είναι κενός", - "addon.mod_lesson.enterpassword": "Παρακαλούμε εισάγετε τον κωδικό πρόσβασης:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Δεν απαντήσατε καμία ερώτηση. Πήρατε 0 σε αυτήν τη διδασκαλία.", - "addon.mod_lesson.errorprefetchrandombranch": "Αυτή η διδασκαλία περιέχει μια μεταπήδηση σε μια σελίδα τυχαίου περιεχομένου. Δεν μπορεί να την προσπαθήσει κανείς στην εφαρμογή κινητού, μέχρι να ξεκινήσει σε έναν περιηγητή ιστού.", - "addon.mod_lesson.errorreviewretakenotlast": "Αυτή η προσπάθεια δεν μπορεί πλέον να αναθεωρηθεί επειδή μια άλλη προσπάθεια έχει ήδη ολοκληρωθεί.", - "addon.mod_lesson.finish": "Τέλος", - "addon.mod_lesson.finishretakeoffline": "Αυτή η προσπάθεια έχει ολοκληρωθεί εκτός σύνδεσης.", - "addon.mod_lesson.firstwrong": "Απαντήσατε λανθασμένα. Θα θέλατε να προσπαθήσετε ξανά την ερώτηση; (Αν απαντήσετε τώρα την ερώτηση σωστά, δεν θα μετρήσει στο τελικό σκορ σας.)", - "addon.mod_lesson.gotoendoflesson": "Μετάβαση στο τέλος του μαθήματος", - "addon.mod_lesson.grade": "Βαθμός", - "addon.mod_lesson.highscore": "Υψηλό σκορ", - "addon.mod_lesson.hightime": "Υψηλός χρόνος", - "addon.mod_lesson.leftduringtimed": "Αποχωρήσατε κατά τη διάρκεια χρονομετρημένης διδασκαλίας.
          Παρακαλούμε, πατήστε Συνέχεια για να επανεκκινήσετε την διδασκαλία.", - "addon.mod_lesson.leftduringtimednoretake": "Αποχωρήσατε κατά τη διάρκεια χρονομετρημένης διδασκαλίας και
          δεν έχετε το δικαίωμα να επαναλάβετε ή να συνεχίσετε την διδασκαλία.", - "addon.mod_lesson.lessonmenu": "Μενού διδασκαλίας", - "addon.mod_lesson.lessonstats": "Στατιστικά διδασκαλίας", - "addon.mod_lesson.linkedmedia": "Συνδεδεμένα πολυμέσα", - "addon.mod_lesson.loginfail": "Αποτυχημένη σύνδεση. Παρακαλούμε προσπαθήστε ξανά...", - "addon.mod_lesson.lowscore": "Χαμηλό σκορ", - "addon.mod_lesson.lowtime": "Χαμηλός χρόνος", - "addon.mod_lesson.maximumnumberofattemptsreached": "Συμπληρώθηκε ο μέγιστος αριθμός προσπαθειών - Μετάβαση στην επόμενη σελίδα", - "addon.mod_lesson.modattemptsnoteacher": "Η ανασκόπηση από μαθητή δουλεύει μόνο για τους μαθητές", - "addon.mod_lesson.modulenameplural": "Ενότητες", - "addon.mod_lesson.noanswer": "Καμία απάντηση δεν δόθηκε. Παρακαλούμε, πηγαίνετε πίσω και καταχωρήστε την απάντησή σας.", - "addon.mod_lesson.nolessonattempts": "Δεν πραγματοποιήθηκαν προσπάθειες σε αυτήν τη διδασκαλία.", - "addon.mod_lesson.nolessonattemptsgroup": "Δεν έχουν γίνει προσπάθειες από {{$a}} μέλη της ομάδας σε αυτή τη διδασκαλία.", - "addon.mod_lesson.notcompleted": "Δεν ολοκληρώθηκε", - "addon.mod_lesson.numberofcorrectanswers": "Αριθμός σωστών απαντήσεων: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Αριθμός απαντημένων ερωτήσεων: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Αριθμός απαντημένων ερωτήσεων: {{$a.nquestions}} (Πρέπει να απαντήσετε τουλάχιστον: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Έχετε πάρει {{$a.score}} από τους {{$a.currenthigh}} πόντους μέχρι τώρα.", - "addon.mod_lesson.ongoingnormal": "Έχετε απαντήσει {{$a.correct}} σωστά από τις {{$a.viewed}} προσπάθειες.", - "addon.mod_lesson.or": "Ή", - "addon.mod_lesson.overview": "Επισκόπηση", - "addon.mod_lesson.preview": "Προεπισκόπηση", - "addon.mod_lesson.progressbarteacherwarning2": "Δεν θα δείτε την μπάρα προόδου γιατί μπορείτε να επεξεργαστείτε αυτή την διδασκαλία", - "addon.mod_lesson.progresscompleted": "Έχετε ολοκληρώσει το {{$a}}% της διδασκαλίας", - "addon.mod_lesson.question": "Ερώτηση", - "addon.mod_lesson.rawgrade": "Βαθμός χωρίς επεξεργασία", - "addon.mod_lesson.reports": "Αναφορές", - "addon.mod_lesson.response": "Απόκριση", - "addon.mod_lesson.retakefinishedinsync": "Μια προσπάθεια εκτός σύνδεσης συγχρονίστηκε. Θέλετε να την αναθεωρήσετε;", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Ανασκόπηση", - "addon.mod_lesson.reviewlesson": "Ανασκόπηση της διδασκαλίας", - "addon.mod_lesson.reviewquestionback": "Ναι, θα ήθελα να δοκιμάσω πάλι", - "addon.mod_lesson.reviewquestioncontinue": "Όχι, θα ήθελα να πάω στην επόμενη ερώτηση", - "addon.mod_lesson.secondpluswrong": "Όχι ακριβώς. Θέλετε να δοκιμάσετε ξανά;", - "addon.mod_lesson.submit": "Υποβολή", - "addon.mod_lesson.teacherjumpwarning": "Μία μεταπήδηση {{$a.cluster}} ή μια μεταπήδηση {{$a.unseen}} χρησιμοποιείται σε αυτήν τη διδασκαλία. Η μεταπήδηση επόμενης σελίδας θα χρησιμοποιηθεί αντί αυτών. Συνδεθείτε ως μαθητής για να δοκιμάσετε αυτές τις μεταπηδήσεις.", - "addon.mod_lesson.teacherongoingwarning": "Το τρέχον σκορ εμφανίζεται μόνο στον μαθητή. Συνδεθείτε ως μαθητής για να ελέγξετε το τρέχον σκορ.", - "addon.mod_lesson.teachertimerwarning": "Το χρονόμετρο λειτουργεί μόνο για τους μαθητές. Συνδεθείτε ως μαθητής για να δοκιμάσετε το χρονόμετρο.", - "addon.mod_lesson.thatsthecorrectanswer": "Αυτή είναι η σωστή απάντηση", - "addon.mod_lesson.thatsthewronganswer": "Αυτή είναι η λάθος απάντηση", - "addon.mod_lesson.timeremaining": "Χρόνος που απομένει", - "addon.mod_lesson.timetaken": "Χρόνος που χρειάστηκε", - "addon.mod_lesson.unseenpageinbranch": "Ερώτηση που δεν εμφανίσθηκε, μέσα σε μια σελίδα περιεχομένου", - "addon.mod_lesson.warningretakefinished": "Η προσπάθεια ολοκληρώθηκε στον ιστότοπο.", - "addon.mod_lesson.welldone": "Μπράβο!", - "addon.mod_lesson.youhaveseen": "Έχετε δει περισσότερες από μία σελίδες αυτής της διδασκαλίας.
          Θέλετε να ξεκινήσετε από την τελευταία σελίδα που είδατε;", - "addon.mod_lesson.youranswer": "Η απάντησή σας", - "addon.mod_lesson.yourcurrentgradeisoutof": "Ο τρέχον βαθμός σας είναι {{$a.grade}} στα {{$a.total}}", - "addon.mod_lesson.youshouldview": "Πρέπει να απαντήσετε τουλάχιστον: {{$a}}", - "addon.mod_lti.errorgetlti": "Σφάλμα κατά τη λήψη των δεδομένων του module.", - "addon.mod_lti.errorinvalidlaunchurl": "Η αρχική διεύθυνση δεν είναι έγκυρη.", - "addon.mod_lti.launchactivity": "Ξεκινήστε την δραστηριότητα", - "addon.mod_lti.modulenameplural": "Εξωτερικά εργαλεία", - "addon.mod_page.errorwhileloadingthepage": "Σφάλμα κατά τη φόρτωση του περιεχομένου της σελίδας.", - "addon.mod_page.modulenameplural": "Σελίδες", - "addon.mod_quiz.answercolon": "Απάντηση:", - "addon.mod_quiz.attemptfirst": "Πρώτη προσπάθεια", - "addon.mod_quiz.attemptlast": "Τελευταία προσπάθεια", - "addon.mod_quiz.attemptnumber": "Προσπάθεια", - "addon.mod_quiz.attemptquiznow": "Προσπάθεια κουίζ τώρα", - "addon.mod_quiz.attemptstate": "Κατάσταση", - "addon.mod_quiz.canattemptbutnotsubmit": "Μπορείτε να προσπαθήσετε αυτό το κουίζ στην εφαρμογή κινητού, αλλά θα πρέπει να υποβάλετε την προσπάθεια στον περιηγητή για τους εξής λόγους:", - "addon.mod_quiz.cannotsubmitquizdueto": "Αυτή η προσπάθεια επίλυσης του κουίζ δεν μπορεί να υποβληθεί για τους εξής λόγους:", - "addon.mod_quiz.clearchoice": "Καθαρισμός της επιλογής σας", - "addon.mod_quiz.comment": "Σχόλιο", - "addon.mod_quiz.completedon": "Ολοκληρώθηκε στις", - "addon.mod_quiz.confirmclose": "Μόλις υποβάλετε, δεν θα μπορείτε πλέον να αλλάξετε τις απαντήσεις σας για αυτήν την προσπάθεια.", - "addon.mod_quiz.confirmcontinueoffline": "Αυτή η προσπάθεια δεν έχει συγχρονιστεί από {{$a}}. Αν έχετε συνεχίσει την προσπάθεια αυτή σε μια άλλη συσκευή από τότε, μπορεί να χάσετε δεδομένα.", - "addon.mod_quiz.confirmleavequizonerror": "Παρουσιάστηκε σφάλμα κατά την αποθήκευση των απαντήσεων. Είστε σίγουροι ότι θέλετε να εξέλθετε από το κουίζ;", - "addon.mod_quiz.confirmstart": "Η προσπάθειά σας θα έχει χρονικό όριο {{$a}}. Όταν ξεκινήσετε, το χρονόμετρο θα αρχίσει να μετράει αντίστροφα και δεν θα μπορεί να τεθεί σε παύση. Πρέπει να ολοκληρώσετε την προσπάθειά σας προτού λήξει. Σίγουρα επιθυμείτε να ξεκινήσετε τώρα;", - "addon.mod_quiz.confirmstartheader": "Χρονικό όριο", - "addon.mod_quiz.connectionerror": "Η σύνδεση δικτύου χάθηκε. (Αποτυχία αυτόματης αποθήκευσης).\n\nΣημειώστε τις απαντήσεις που καταχωρήσατε σε αυτή τη σελίδα τα τελευταία λεπτά και δοκιμάστε να συνδεθείτε ξανά.\n\nΜόλις αποκατασταθεί η σύνδεση, οι αποκρίσεις σας θα πρέπει να αποθηκευτούν και αυτό το μήνυμα θα εξαφανιστεί.", - "addon.mod_quiz.continueattemptquiz": "Συνέχιση της τελευταίας προσπάθειας", - "addon.mod_quiz.continuepreview": "Συνέχιση της τελευταίας προεπισκόπησης", - "addon.mod_quiz.errorbehaviournotsupported": "Δεν μπορείτε να πραγματοποιήσετε αυτό το κουίζ στην εφαρμογή κινητού, διότι η συμπεριφορά ερωτήσεων δεν υποστηρίζεται από την εφαρμογή:", - "addon.mod_quiz.errordownloading": "Σφάλμα κατά τη λήψη των απαιτούμενων δεδομένων.", - "addon.mod_quiz.errorgetattempt": "Σφάλμα κατά τη λήψη δεδομένων προσπάθειας.", - "addon.mod_quiz.errorgetquestions": "Σφάλμα κατά τη λήψη των ερωτήσεων.", - "addon.mod_quiz.errorgetquiz": "Σφάλμα κατά τη λήψη δεδομένων κουίζ.", - "addon.mod_quiz.errorparsequestions": "Παρουσιάστηκε σφάλμα κατά την ανάγνωση των ερωτήσεων. Παρακαλώ να κάνετε αυτό το κουίζ σε ένα web browser.", - "addon.mod_quiz.errorquestionsnotsupported": "Αυτό το κουίζ δεν μπορεί να επιχειρηθεί στην εφαρμογή κινητού, γιατί περιέχει μόνο ερωτήσεις που δεν υποστηρίζονται από την εφαρμογή:", - "addon.mod_quiz.errorrulesnotsupported": "Αυτό το κουίζ δεν μπορεί να επιχειρηθεί στην εφαρμογή κινητού επειδή έχει κανόνες πρόσβασης που δεν υποστηρίζονται από την εφαρμογή:", - "addon.mod_quiz.errorsaveattempt": "Παρουσιάστηκε σφάλμα κατά την προσπάθεια αποθήκευσης των δεδομένων.", - "addon.mod_quiz.feedback": "Ανατροφοδότηση", - "addon.mod_quiz.finishattemptdots": "Τελειώστε την προσπάθεια ...", - "addon.mod_quiz.finishnotsynced": "Ολοκληρωμένο αλλά όχι συγχρονισμένο", - "addon.mod_quiz.grade": "Βαθμός", - "addon.mod_quiz.gradeaverage": "Μέσος βαθμός", - "addon.mod_quiz.gradehighest": "Ο υψηλότερος βαθμός", - "addon.mod_quiz.grademethod": "Μέθοδος βαθμολόγησης", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Βαθμοί", - "addon.mod_quiz.modulenameplural": "Κουίζ", - "addon.mod_quiz.mustbesubmittedby": "Αυτή η προσπάθεια πρέπει να υποβληθεί από {{$a}}.", - "addon.mod_quiz.noquestions": "Καμία ερώτηση δεν έχει προστεθεί ακόμα", - "addon.mod_quiz.noreviewattempt": "Δεν επιτρέπεται να αναθεωρήσετε αυτήν την προσπάθεια.", - "addon.mod_quiz.notyetgraded": "Δεν έχει βαθμολογηθεί ακόμη", - "addon.mod_quiz.opentoc": "Ανοίξτε το popover πλοήγησης.", - "addon.mod_quiz.outof": "{{$a.grade}} από {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} από ένα μέγιστο {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Συνολική ανατροφοδότηση", - "addon.mod_quiz.overdue": "Εκπρόθεσμο", - "addon.mod_quiz.overduemustbesubmittedby": "Αυτή η προσπάθεια είναι πλέον εκπρόθεσμη. Έπρεπε να έχει υποβληθεί ήδη. Εάν θέλετε να βαθμολογηθεί αυτό το κουίζ, πρέπει να το υποβάλετε μέχρι {{$a}}. Εάν δεν το υποβάλετε μέχρι τότε, δεν θα ληφθούν υπόψη βαθμολογίες για αυτή την προσπάθεια.", - "addon.mod_quiz.preview": "Προεπισκόπηση", - "addon.mod_quiz.previewquiznow": "Προεπισκόπηση κουίζ", - "addon.mod_quiz.question": "Ερώτηση", - "addon.mod_quiz.quiznavigation": "Πλοήγηση κουίζ", - "addon.mod_quiz.quizpassword": "Κωδικός πρόσβασης κουίζ", - "addon.mod_quiz.reattemptquiz": "Επαναπροσπάθεια του κουίζ", - "addon.mod_quiz.requirepasswordmessage": "Για να δοκιμάσετε αυτό το κουίζ πρέπει να γνωρίζετε τον κωδικό του", - "addon.mod_quiz.returnattempt": "Επιστροφή στην προσπάθεια", - "addon.mod_quiz.review": "Ανασκόπηση", - "addon.mod_quiz.reviewofattempt": "Αναθεώρηση προσπάθειας {{$a}}", - "addon.mod_quiz.reviewofpreview": "Αναθεώρηση της προεπισκόπησης", - "addon.mod_quiz.showall": "Εμφάνιση όλων των ερωτήσεων σε μία σελίδα", - "addon.mod_quiz.showeachpage": "Εμφάνιση μία σελίδα την φορά", - "addon.mod_quiz.startattempt": "Έναρξη προσπάθειας", - "addon.mod_quiz.startedon": "Ξεκίνησε στις", - "addon.mod_quiz.stateabandoned": "Δεν έχει υποβληθεί", - "addon.mod_quiz.statefinished": "Ολοκληρωμένο", - "addon.mod_quiz.statefinisheddetails": "Υποβλήθηκε {{$a}}", - "addon.mod_quiz.stateinprogress": "Σε εξέλιξη", - "addon.mod_quiz.stateoverdue": "Εκπρόθεσμο", - "addon.mod_quiz.stateoverduedetails": "Πρέπει να υποβληθεί μέχρι {{$a}}", - "addon.mod_quiz.status": "Κατάσταση", - "addon.mod_quiz.submitallandfinish": "Υποβολή όλων και τέλος", - "addon.mod_quiz.summaryofattempt": "Περίληψη προσπάθειας", - "addon.mod_quiz.summaryofattempts": "Σύνοψη προηγούμενων προσπαθειών σας", - "addon.mod_quiz.timeleft": "Υπολειπόμενος χρόνος", - "addon.mod_quiz.timetaken": "Χρόνος που χρειάστηκε", - "addon.mod_quiz.warningattemptfinished": "Η προσπάθεια εκτός σύνδεσης απορρίφθηκε διότι είτε δεν βρέθηκε, είτε έχει ολοκληρωθεί στον ιστότοπο.", - "addon.mod_quiz.warningdatadiscarded": "Μερικές απαντήσεις εκτός σύνδεσης απορρίφθηκαν διότι οι ερωτήσεις είχαν τροποποιηθεί εντός σύνδεσης.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Η προσπάθεια δεν ολοκληρώθηκε διότι κάποιες απαντήσεις απορρίφθηκαν. Παρακαλώ ελέγξτε τις απαντήσεις και υποβάλετε ξανά την προσπάθεια.", - "addon.mod_quiz.warningquestionsnotsupported": "Αυτό το κουίζ περιέχει ερωτήσεις που δεν υποστηρίζονται από την εφαρμογή κινητού:", - "addon.mod_quiz.yourfinalgradeis": "Ο τελικός σας βαθμός για αυτό το κουίζ είναι {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Σφάλμα κατά τη φόρτωση του περιεχομένου.", - "addon.mod_resource.modifieddate": "Τροποποιήθηκε {{$a}}", - "addon.mod_resource.modulenameplural": "Αρχεία", - "addon.mod_resource.openthefile": "Ανοίξτε το αρχείο", - "addon.mod_resource.uploadeddate": "Ανέβηκε στις {{$a}}", - "addon.mod_scorm.asset": "Στοιχείο", - "addon.mod_scorm.assetlaunched": "Στοιχείο - Προβλήθηκε", - "addon.mod_scorm.attempts": "Προσπάθειες", - "addon.mod_scorm.averageattempt": "Μέσος όρος προσπαθειών", - "addon.mod_scorm.browse": "Προεπισκόπηση", - "addon.mod_scorm.browsed": "Προβλήθηκε", - "addon.mod_scorm.browsemode": "Κατάσταση προεπισκόπησης", - "addon.mod_scorm.cannotcalculategrade": "Ο βαθμός δεν μπόρεσε να υπολογιστεί.", - "addon.mod_scorm.completed": "Ολοκληρωμένο", - "addon.mod_scorm.contents": "Περιεχόμενα", - "addon.mod_scorm.dataattemptshown": "Αυτά τα δεδομένα ανήκουν στην προσπάθεια με αριθμό {{number}}.", - "addon.mod_scorm.enter": "Είσοδος/Σύνδεση", - "addon.mod_scorm.errorcreateofflineattempt": "Παρουσιάστηκε σφάλμα κατά τη εκκίνηση μιας νέας προσπάθειας εκτός σύνδεσης. Παρακαλώ προσπαθήστε ξανά.", - "addon.mod_scorm.errordownloadscorm": "Σφάλμα κατά τη λήψη SCORM: «{{name}}».", - "addon.mod_scorm.errorgetscorm": "Σφάλμα κατά τη λήψη δεδομένων SCORM.", - "addon.mod_scorm.errorinvalidversion": "Λυπούμαστε, η εφαρμογή υποστηρίζει μόνο SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Η λήψη των πακέτων SCORM είναι απενεργοποιημένη στον δικτυακό τόπο του Moodle. Παρακαλώ επικοινωνήστε με το διαχειριστή του Moodle ιστοτόπου σας.", - "addon.mod_scorm.errornovalidsco": "Αυτό το SCORM δεν έχει ορατό SCO για να φορτωθεί.", - "addon.mod_scorm.errorpackagefile": "Λυπούμαστε, η εφαρμογή υποστηρίζει μόνο πακέτα ZIP.", - "addon.mod_scorm.errorsyncscorm": "Παρουσιάστηκε σφάλμα κατά το συγχρονισμό. Παρακαλώ προσπαθήστε ξανά.", - "addon.mod_scorm.exceededmaxattempts": "Έχετε φτάσει το όριο του επιτρεπόμενου αριθμού προσπαθειών.", - "addon.mod_scorm.failed": "Απέτυχε", - "addon.mod_scorm.firstattempt": "Πρώτη προσπάθεια", - "addon.mod_scorm.gradeaverage": "Μέσος βαθμός", - "addon.mod_scorm.gradeforattempt": "Βαθμός προσπάθειας", - "addon.mod_scorm.gradehighest": "Υψηλότερος βαθμός", - "addon.mod_scorm.grademethod": "Μέθοδος βαθμολόγησης", - "addon.mod_scorm.gradereported": "Βαθμολογία", - "addon.mod_scorm.gradescoes": "Μαθησιακά αντικείμενα", - "addon.mod_scorm.gradesum": "Άθροισμα βαθμού", - "addon.mod_scorm.highestattempt": "Υψηλότερη βαθμολογία", - "addon.mod_scorm.incomplete": "Μη ολοκληρωμένο", - "addon.mod_scorm.lastattempt": "Τελευταία ολοκληρωμένη προσπάθεια", - "addon.mod_scorm.modulenameplural": "Πακέτα SCORM", - "addon.mod_scorm.newattempt": "Έναρξη νέας προσπάθειας", - "addon.mod_scorm.noattemptsallowed": "Επιτρεπόμενος αριθμός προσπαθειών", - "addon.mod_scorm.noattemptsmade": "Αριθμός προσπαθειών που κάνατε", - "addon.mod_scorm.notattempted": "Δεν έγινε προσπάθεια", - "addon.mod_scorm.offlineattemptnote": "Αυτή η προσπάθεια έχει στοιχεία που δεν έχουν συγχρονιστεί.", - "addon.mod_scorm.offlineattemptovermax": "Αυτή η προσπάθεια δεν μπορεί να σταλεί, γιατί έχετε ξεπεράσει το μέγιστο αριθμό προσπαθειών.", - "addon.mod_scorm.organizations": "Οργανισμοί", - "addon.mod_scorm.passed": "Πέρασε", - "addon.mod_scorm.reviewmode": "Κατάσταση ανασκόπησης", - "addon.mod_scorm.score": "Σκορ", - "addon.mod_scorm.scormstatusnotdownloaded": "Αυτό το SCORM δεν έχει κατέβει. Θα κατέβει αυτόματα όταν το ανοίξετε.", - "addon.mod_scorm.scormstatusoutdated": "Αυτό το SCORM έχει τροποποιηθεί από την τελευταία λήψη. Θα κατέβει αυτόματα όταν το ανοίξετε.", - "addon.mod_scorm.suspended": "Σε αναστολή", - "addon.mod_scorm.toc": "Πίνακας περιεχομένων", - "addon.mod_scorm.warningofflinedatadeleted": "Ορισμένα εκτός σύνδεσης δεδομένα της προσπάθειας {{number}} διαγράφηκαν επειδή δεν ήταν δυνατή η δημιουργία νέας προσπάθειας.", - "addon.mod_scorm.warningsynconlineincomplete": "Κάποιες προσπάθειες δεν μπόρεσαν να συγχρονιστούν με τον ιστότοπο διότι η τελευταία εντός σύνδεσης προσπάθεια δεν έχει ολοκληρωθεί. Παρακαλώ τελειώστε την εντός σύνδεσης προσπάθεια πρώτα.", - "addon.mod_survey.cannotsubmitsurvey": "Λυπούμαστε, υπήρξε πρόβλημα υποβολής της έρευνας. Παρακαλώ δοκιμάστε ξανά.", - "addon.mod_survey.errorgetsurvey": "Σφάλμα κατά τη λήψη των δεδομένων της έρευνας.", - "addon.mod_survey.ifoundthat": "Ανακάλυψα ότι", - "addon.mod_survey.ipreferthat": "Προτιμώ το ότι", - "addon.mod_survey.modulenameplural": "Έρευνες", - "addon.mod_survey.responses": "Απαντήσεις", - "addon.mod_survey.results": "Αποτελέσματα", - "addon.mod_survey.surveycompletednograph": "Έχετε ολοκληρώσει αυτή την έρευνα.", - "addon.mod_url.accessurl": "Πρόσβαση στη διεύθυνση URL", - "addon.mod_url.modulenameplural": "Διευθύνσεις URL", - "addon.mod_url.pointingtourl": "Η διεύθυνση URL στην οποία δείχενει ο πόρος.", - "addon.mod_wiki.cannoteditpage": "Δεν μπορείτε να επεξεργαστείτε αυτήν τη σελίδα.", - "addon.mod_wiki.createpage": "Δημιουργία σελίδας", - "addon.mod_wiki.editingpage": "Επεξεργασία αυτής της σελίδας «{{$a}}».", - "addon.mod_wiki.errorloadingpage": "Παρουσιάστηκε σφάλμα κατά τη φόρτωση της σελίδας.", - "addon.mod_wiki.errornowikiavailable": "Αυτό το wiki δεν έχει ακόμα περιεχόμενο.", - "addon.mod_wiki.gowikihome": "Μετάβαση στην αρχική σελίδα του wiki", - "addon.mod_wiki.map": "Χάρτης", - "addon.mod_wiki.modulenameplural": "Wiki", - "addon.mod_wiki.newpagehdr": "Νέα σελίδα", - "addon.mod_wiki.newpagetitle": "Νέος τίτλος σελίδας", - "addon.mod_wiki.nocontent": "Δεν υπάρχει περιεχόμενο για αυτήν τη σελίδα", - "addon.mod_wiki.notingroup": "Όχι σε ομάδα", - "addon.mod_wiki.pageexists": "Αυτή η σελίδα υπάρχει ήδη.", - "addon.mod_wiki.pagename": "Όνομα σελίδας", - "addon.mod_wiki.subwiki": "Υπο-wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Σελίδες wiki", - "addon.mod_wiki.titleshouldnotbeempty": "Ο τίτλος δεν πρέπει να είναι κενός", - "addon.mod_wiki.viewpage": "Δείτε τη σελίδα", - "addon.mod_wiki.wikipage": "Σελίδα Wiki", - "addon.mod_wiki.wrongversionlock": "Ένας άλλος χρήστης έχει επεξεργαστεί αυτήν τη σελίδα ενώ την επεξεργάζεστε και το περιεχόμενό σας είναι ξεπερασμένο.", - "addon.mod_workshop.alreadygraded": "Ήδη βαθμολογημένο", - "addon.mod_workshop.areainstructauthors": "Οδηγίες για την υποβολή", - "addon.mod_workshop.areainstructreviewers": "Οδηγίες για την αξιολόγηση", - "addon.mod_workshop.assess": "Αξιολόγηση", - "addon.mod_workshop.assessedsubmission": "Αξιολογημένη υποβολή", - "addon.mod_workshop.assessmentform": "Φόρμα αξιολόγησης", - "addon.mod_workshop.assessmentsettings": "Ρυθμίσεις αξιολόγησης", - "addon.mod_workshop.assessmentstrategynotsupported": "Η στρατηγική αξιολόγησης {{$a}} δεν υποστηρίζεται", - "addon.mod_workshop.assessmentweight": "Συντελεστής βαρύτητας αξιολόγησης", - "addon.mod_workshop.assignedassessments": "Υποβολές προς αξιολόγηση που έχουν ανατεθεί", - "addon.mod_workshop.assignedassessmentsnone": "Δεν σας έχει ανατεθεί υποβολή για αξιολόγηση", - "addon.mod_workshop.conclusion": "Κατάληξη", - "addon.mod_workshop.createsubmission": "Προσθήκη υποβολής", - "addon.mod_workshop.deletesubmission": "Διαγραφή υποβολής", - "addon.mod_workshop.editsubmission": "Επεξεργασία υποβολής", - "addon.mod_workshop.feedbackauthor": "Ανατροφοδότηση για τον συγγραφέα", - "addon.mod_workshop.feedbackby": "Ανατροφοδότηση από {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Ανατροφοδότηση για τον αξιολογητή", - "addon.mod_workshop.givengrades": "Βαθμοί που δόθηκαν", - "addon.mod_workshop.gradecalculated": "Υπολογισμός βαθμού για υποβολή", - "addon.mod_workshop.gradeinfo": "Βαθμός: {{$a.received}} από {{$a.max}}", - "addon.mod_workshop.gradeover": "Παράκαμψη βαθμού για υποβολή", - "addon.mod_workshop.gradesreport": "Αναφορά βαθμών εργαστηρίου", - "addon.mod_workshop.gradinggrade": "Βαθμός για την αξιολόγηση", - "addon.mod_workshop.gradinggradecalculated": "Υπολογισμένος βαθμός για την αξιολόγηση", - "addon.mod_workshop.gradinggradeof": "Βαθμός για την αξιολόγηση (από {{$a}})", - "addon.mod_workshop.gradinggradeover": "Παράκαμψη βαθμού για εκτίμηση", - "addon.mod_workshop.modulenameplural": "Εργαστήρια", - "addon.mod_workshop.nogradeyet": "Χωρίς βαθμό ακόμη", - "addon.mod_workshop.notassessed": "Δεν έχει αξιολογηθεί ακόμη", - "addon.mod_workshop.notoverridden": "Δεν παρακάμπτεται", - "addon.mod_workshop.noyoursubmission": "Δεν έχετε υποβάλει την εργασία σας ακόμα", - "addon.mod_workshop.overallfeedback": "Συνολική ανατροφοδότηση", - "addon.mod_workshop.publishedsubmissions": "Δημοσιευμένες υποβολές", - "addon.mod_workshop.publishsubmission": "Δημοσίευση υποβολής", - "addon.mod_workshop.publishsubmission_help": "Οι δημοσιευμένες υποβολές διατίθενται στους υπόλοιπους όταν το εργαστήριο είναι κλειστό.", - "addon.mod_workshop.reassess": "Επαναξιολόγηση", - "addon.mod_workshop.receivedgrades": "Βαθμοί που ελήφθησαν", - "addon.mod_workshop.submissionattachment": "Συνημμένο", - "addon.mod_workshop.submissioncontent": "Περιεχόμενο υποβολής", - "addon.mod_workshop.submissiondeleteconfirm": "Σίγουρα θέλετε να διαγράψετε την παρακάτω υποβολή;", - "addon.mod_workshop.submissiongrade": "Βαθμός για την υποβολή", - "addon.mod_workshop.submissiongradeof": "Βαθμός για την υποβολή (από {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Πρέπει να εισαγάγετε κάποιο κείμενο ή να προσθέσετε ένα αρχείο.", - "addon.mod_workshop.submissionrequiredtitle": "Πρέπει να δώσετε έναν τίτλο.", - "addon.mod_workshop.submissionsreport": "Αναφορά υποβολών εργαστηρίου", - "addon.mod_workshop.submissiontitle": "Τίτλος", - "addon.mod_workshop.switchphase10": "Μετάβαση σε φάση εγκατάστασης", - "addon.mod_workshop.switchphase20": "Μετάβαση στη φάση υποβολής", - "addon.mod_workshop.switchphase30": "Μετάβαση στη φάση αξιολόγησης", - "addon.mod_workshop.switchphase40": "Μετάβαση στη φάση αποτίμησης", - "addon.mod_workshop.switchphase50": "Κλείσιμο εργαστηρίου", - "addon.mod_workshop.userplan": "Σχεδιασμός εργαστηρίου", - "addon.mod_workshop.userplancurrentphase": "Τρέχουσα φάση", - "addon.mod_workshop.warningassessmentmodified": "Η υποβολή τροποποιήθηκε στον ιστότοπο.", - "addon.mod_workshop.warningsubmissionmodified": "Η αξιολόγηση τροποποιήθηκε στον ιστότοπο.", - "addon.mod_workshop.weightinfo": "Συντελεστής βαρύτητας: {{$a}}", - "addon.mod_workshop.yourassessment": "Η αξιολόγησή σας", - "addon.mod_workshop.yourassessmentfor": "Η αξιολόγησή σας για {{$a}}", - "addon.mod_workshop.yourgrades": "Οι βαθμοί σας", - "addon.mod_workshop.yoursubmission": "Η υποβολή σας", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Σχόλιο για {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Βαθμός για {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Πτυχή {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Πρέπει να επιλέξετε ένα βαθμό για αυτήν την πτυχή", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Σχόλιο για {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Πτυχή {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Σχόλιο για {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Βαθμός για {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Ισχυρισμός {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Κριτήριο {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Θα πρέπει να επιλέξετε ένα από αυτά τα στοιχεία", - "addon.notes.addnewnote": "Προσθήκη νέας σημείωσης", - "addon.notes.coursenotes": "Σημειώσεις μαθήματος", - "addon.notes.deleteconfirm": "Διαγραφή αυτής της σημείωσης;", - "addon.notes.eventnotecreated": "Δημιουργία σημείωσης", - "addon.notes.eventnotedeleted": "Διαγραφή σημείωσης", - "addon.notes.nonotes": "Δεν υπάρχουν σημειώσεις αυτού του τύπου ακόμα", - "addon.notes.note": "Σημείωση", - "addon.notes.notes": "Σημειώσεις", - "addon.notes.personalnotes": "Προσωπικές σημειώσεις", - "addon.notes.publishstate": "Πλαίσιο", - "addon.notes.sitenotes": "Σημειώσεις ιστοτόπου", - "addon.notes.userwithid": "Χρήστης με αναγνωριστικό {{id}}", - "addon.notes.warningnotenotsent": "Αδυναμία προσθήκης σημειώσεων στο μάθημα {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Σφάλμα κατά τη λήψη ειδοποιήσεων.", - "addon.notifications.markallread": "Σημείωση όλων ως αναγνωσμένων", - "addon.notifications.notificationpreferences": "Προτιμήσεις ειδοποιήσεων", - "addon.notifications.notifications": "Ειδοποιήσεις", - "addon.notifications.playsound": "Αναπαραγωγή ήχου", - "addon.notifications.therearentnotificationsyet": "Δεν υπάρχουν ειδοποιήσεις.", - "addon.storagemanager.deletecourse": "Ξεφόρτωση όλων των δεδομένων μαθήματος", - "addon.storagemanager.deletecourses": "Ξεφορτώστε όλα τα δεδομένα μαθημάτων", - "addon.storagemanager.deletedatafrom": "Ξεφόρτωση δεδομένων από {{name}}", - "addon.storagemanager.info": "Αρχεία αποθηκευμένα στη συσκευή σας κάνουν την εφαρμογή κινητού να λειτουργεί γρηγορότερα και να επιτρέπει τη δυνατότητα εκτέλεσης εκτός σύνδεσης. Μπορείτε να ξεφορτώσετε με ασφάλεια αρχεία αν χρειάζεται να ελευθερώσετε χώρο.", - "addon.storagemanager.managestorage": "Διαχείριση αποθήκευσης", - "addon.storagemanager.storageused": "Χρησιμοποιούμενη αποθήκευση αρχείων:", - "assets.countries.AD": "Ανδόρρα", - "assets.countries.AE": "Ηνωμένα Αραβικά Εμιράτα", - "assets.countries.AF": "Αφγανιστάν", - "assets.countries.AG": "Αντίγκουα και Μπαρμπούντα", - "assets.countries.AI": "Ανγκουίλα", - "assets.countries.AL": "Αλβανία", - "assets.countries.AM": "Αρμενία", - "assets.countries.AO": "Ανγκόλα", - "assets.countries.AQ": "Ανταρκτική", - "assets.countries.AR": "Αργεντινή", - "assets.countries.AS": "Αμερικανική Σαμόα", - "assets.countries.AT": "Αυστρία", - "assets.countries.AU": "Αυστραλία", - "assets.countries.AW": "Αρούμπα", - "assets.countries.AX": "Νήσοι Ώλαντ", - "assets.countries.AZ": "Αζερμπαϊτζάν", - "assets.countries.BA": "Βοσνία και Ερζεγοβίνη", - "assets.countries.BB": "Μπαρμπάντος", - "assets.countries.BD": "Μπαγκλαντές", - "assets.countries.BE": "Βέλγιο", - "assets.countries.BF": "Μπουρκίνα Φάσο", - "assets.countries.BG": "Βουλγαρία", - "assets.countries.BH": "Μπαχρέιν", - "assets.countries.BI": "Μπουρούντι", - "assets.countries.BJ": "Μπενίν", - "assets.countries.BL": "Άγιος Βαρθολομαίος", - "assets.countries.BM": "Βερμούδες", - "assets.countries.BN": "Μπρουνέι", - "assets.countries.BO": "Βολιβία, Πολυεθνοτικό Κράτος της", - "assets.countries.BQ": "Νήσοι Μποναίρ, Άγιος Ευστάθιος & Σάμπα", - "assets.countries.BR": "Βραζιλία", - "assets.countries.BS": "Μπαχάμες", - "assets.countries.BT": "Μπουτάν", - "assets.countries.BV": "Νησί Μπουβέ", - "assets.countries.BW": "Μποτσουάνα", - "assets.countries.BY": "Λευκορωσία", - "assets.countries.BZ": "Μπελίζ", - "assets.countries.CA": "Καναδάς", - "assets.countries.CC": "Νήσοι Κόκος (Κήλινγκ)", - "assets.countries.CD": "Κονγκό, Λαϊκή Δημοκρατία του", - "assets.countries.CF": "Κεντροαφρικανική Δημοκρατία", - "assets.countries.CG": "Κονγκό", - "assets.countries.CH": "Ελβετία", - "assets.countries.CI": "Ακτή Ελεφαντοστού", - "assets.countries.CK": "Νήσοι Κουκ", - "assets.countries.CL": "Χιλή", - "assets.countries.CM": "Καμερούν", - "assets.countries.CN": "Κίνα", - "assets.countries.CO": "Κολομβία", - "assets.countries.CR": "Κόστα Ρίκα", - "assets.countries.CU": "Κούβα", - "assets.countries.CV": "Πράσινο Ακρωτήριο", - "assets.countries.CW": "Κουρασάο", - "assets.countries.CX": "Νήσος των Χριστουγέννων", - "assets.countries.CY": "Κύπρος", - "assets.countries.CZ": "Τσεχία", - "assets.countries.DE": "Γερμανία", - "assets.countries.DJ": "Τζιμπουτί", - "assets.countries.DK": "Δανία", - "assets.countries.DM": "Δομινίκα", - "assets.countries.DO": "Δομινικανή Δημοκρατία", - "assets.countries.DZ": "Αλγερία", - "assets.countries.EC": "Εκουαδόρ", - "assets.countries.EE": "Εσθονία", - "assets.countries.EG": "Αίγυπτος", - "assets.countries.EH": "Δυτική Σαχάρα", - "assets.countries.ER": "Ερυθραία", - "assets.countries.ES": "Ισπανία", - "assets.countries.ET": "Αιθιοπία", - "assets.countries.FI": "Φινλανδία", - "assets.countries.FJ": "Φίτζι", - "assets.countries.FK": "Νήσοι Φώκλαντ", - "assets.countries.FM": "Μικρονησία, Ομόσπονδες Πολιτείες της", - "assets.countries.FO": "Νήσοι Φερόε", - "assets.countries.FR": "Γαλλία", - "assets.countries.GA": "Γκαμπόν", - "assets.countries.GB": "Ηνωμένο Βασίλειο", - "assets.countries.GD": "Γρενάδα", - "assets.countries.GE": "Γεωργία", - "assets.countries.GF": "Γαλλική Γουιάνα", - "assets.countries.GG": "Γκέρνζι", - "assets.countries.GH": "Γκάνα", - "assets.countries.GI": "Γιβραλτάρ", - "assets.countries.GL": "Γροιλανδία", - "assets.countries.GM": "Γκάμπια", - "assets.countries.GN": "Γουινέα", - "assets.countries.GP": "Γουαδελούπη", - "assets.countries.GQ": "Ισημερινή Γουινέα", - "assets.countries.GR": "Ελλάδα", - "assets.countries.GS": "Νήσοι Νότια Γεωργία & Νότιες Σάντουιτς", - "assets.countries.GT": "Γουατεμάλα", - "assets.countries.GU": "Γκουάμ", - "assets.countries.GW": "Γουινέα-Μπισσάου", - "assets.countries.GY": "Γουιάνα", - "assets.countries.HK": "Χονγκ Κονγκ", - "assets.countries.HM": "Νήσοι Χερντ & Μακντόναλντ", - "assets.countries.HN": "Ονδούρα", - "assets.countries.HR": "Κροατία", - "assets.countries.HT": "Αϊτή", - "assets.countries.HU": "Ουγγαρία", - "assets.countries.ID": "Ινδονησία", - "assets.countries.IE": "Ιρλανδία", - "assets.countries.IL": "Ισραήλ", - "assets.countries.IM": "Νήσος του Μαν", - "assets.countries.IN": "Ινδία", - "assets.countries.IO": "Βρετανικό Έδαφος Ινδικού Ωκεανού", - "assets.countries.IQ": "Ιράκ", - "assets.countries.IR": "Ισλαμική Δημοκρατία του Ιράν", - "assets.countries.IS": "Ισλανδία", - "assets.countries.IT": "Ιταλία", - "assets.countries.JE": "Τζέρσεϊ", - "assets.countries.JM": "Τζαμάικα", - "assets.countries.JO": "Ιορδανία", - "assets.countries.JP": "Ιαπωνία", - "assets.countries.KE": "Κένυα", - "assets.countries.KG": "Κιργιζία", - "assets.countries.KH": "Καμπότζη", - "assets.countries.KI": "Κιριμπάτι", - "assets.countries.KM": "Κομόρες", - "assets.countries.KN": "Άγιος Χριστόφορος και Νέβις", - "assets.countries.KP": "Βόρεια Κορέα, Λαϊκή Δημοκρατία της", - "assets.countries.KR": "Νότια Κορέα, Δημοκρατία της", - "assets.countries.KW": "Κουβέιτ", - "assets.countries.KY": "Νήσοι Κέιμαν", - "assets.countries.KZ": "Καζακστάν", - "assets.countries.LA": "Λάος", - "assets.countries.LB": "Λίβανος", - "assets.countries.LC": "Αγία Λουκία", - "assets.countries.LI": "Λιχτενστάιν", - "assets.countries.LK": "Σρι Λάνκα", - "assets.countries.LR": "Λιβερία", - "assets.countries.LS": "Λεσότο", - "assets.countries.LT": "Λιθουανία", - "assets.countries.LU": "Λουξεμβούργο", - "assets.countries.LV": "Λετονία", - "assets.countries.LY": "Λιβύη", - "assets.countries.MA": "Μαρόκο", - "assets.countries.MC": "Μονακό", - "assets.countries.MD": "Δημοκρατία της Μολδαβίας", - "assets.countries.ME": "Μαυροβούνιο", - "assets.countries.MF": "Άγιος Μαρτίνος, Κοινότητα του (Γαλλία)", - "assets.countries.MG": "Μαδαγασκάρη", - "assets.countries.MH": "Νήσοι Μάρσαλ", - "assets.countries.MK": "Βόρεια Μακεδονία", - "assets.countries.ML": "Μάλι", - "assets.countries.MM": "Μιανμάρ", - "assets.countries.MN": "Μογγολία", - "assets.countries.MO": "Μακάο", - "assets.countries.MP": "Βόρειες Μαριάνες Νήσοι", - "assets.countries.MQ": "Μαρτινίκα", - "assets.countries.MR": "Μαυριτανία", - "assets.countries.MS": "Μοντσερράτ", - "assets.countries.MT": "Μάλτα", - "assets.countries.MU": "Μαυρίκιος", - "assets.countries.MV": "Μαλδίβες", - "assets.countries.MW": "Μαλάουι", - "assets.countries.MX": "Μεξικό", - "assets.countries.MY": "Μαλαισία", - "assets.countries.MZ": "Μοζαμβίκη", - "assets.countries.NA": "Ναμίμπια", - "assets.countries.NC": "Νέα Καληδονία", - "assets.countries.NE": "Νίγηρας", - "assets.countries.NF": "Νησί Νόρφολκ", - "assets.countries.NG": "Νιγηρία", - "assets.countries.NI": "Νικαράγουα", - "assets.countries.NL": "Ολλανδία", - "assets.countries.NO": "Νορβηγία", - "assets.countries.NP": "Νεπάλ", - "assets.countries.NR": "Ναουρού", - "assets.countries.NU": "Νιούε", - "assets.countries.NZ": "Νέα Ζηλανδία", - "assets.countries.OM": "Ομάν", - "assets.countries.PA": "Παναμάς", - "assets.countries.PE": "Περού", - "assets.countries.PF": "Γαλλική Πολυνησία", - "assets.countries.PG": "Παπούα Νέα Γουινέα", - "assets.countries.PH": "Φιλιππίνες", - "assets.countries.PK": "Πακιστάν", - "assets.countries.PL": "Πολωνία", - "assets.countries.PM": "Σαιν-Πιερ και Μικελόν", - "assets.countries.PN": "Νήσοι Πίτκαιρν", - "assets.countries.PR": "Πουέρτο Ρίκο", - "assets.countries.PS": "Παλαιστίνη, Κράτος της", - "assets.countries.PT": "Πορτογαλία", - "assets.countries.PW": "Παλάου", - "assets.countries.PY": "Παραγουάη", - "assets.countries.QA": "Κατάρ", - "assets.countries.RE": "Ρεϋνιόν", - "assets.countries.RO": "Ρουμανία", - "assets.countries.RS": "Σερβία", - "assets.countries.RU": "Ρωσική Ομοσπονδία", - "assets.countries.RW": "Ρουάντα", - "assets.countries.SA": "Σαουδική Αραβία", - "assets.countries.SB": "Νήσοι Σολομώντα", - "assets.countries.SC": "Σεϋχέλλες", - "assets.countries.SD": "Σουδάν", - "assets.countries.SE": "Σουηδία", - "assets.countries.SG": "Σιγκαπούρη", - "assets.countries.SH": "Αγία Ελένη, Ασενσιόν και Τριστάν ντα Κούνια", - "assets.countries.SI": "Σλοβενία", - "assets.countries.SJ": "Νήσοι Σβάλμπαρντ και Γιαν Μάγεν", - "assets.countries.SK": "Σλοβακία", - "assets.countries.SL": "Σιέρα Λεόνε", - "assets.countries.SM": "Σαν Μαρίνο", - "assets.countries.SN": "Σενεγάλη", - "assets.countries.SO": "Σομαλία", - "assets.countries.SR": "Σουρινάμ", - "assets.countries.SS": "Νότιο Σουδάν", - "assets.countries.ST": "Σάο Τομέ και Πρίνσιπε", - "assets.countries.SV": "Ελ Σαλβαδόρ", - "assets.countries.SX": "Άγιος Μαρτίνος, Χώρα του (Ολλανδία)", - "assets.countries.SY": "Συρία", - "assets.countries.SZ": "Εσουατίνι", - "assets.countries.TC": "Νήσοι Τερκς και Κέικος", - "assets.countries.TD": "Τσαντ", - "assets.countries.TF": "Γαλλικά Νότια και Ανταρκτικά Εδάφη", - "assets.countries.TG": "Τόγκο", - "assets.countries.TH": "Ταϊλάνδη", - "assets.countries.TJ": "Τατζικιστάν", - "assets.countries.TK": "Τοκελάου", - "assets.countries.TL": "Ανατολικό Τιμόρ", - "assets.countries.TM": "Τουρκμενιστάν", - "assets.countries.TN": "Τυνησία", - "assets.countries.TO": "Τόνγκα", - "assets.countries.TR": "Τουρκία", - "assets.countries.TT": "Τρινιντάντ και Τομπάγκο", - "assets.countries.TV": "Τουβαλού", - "assets.countries.TW": "Ταϊβάν", - "assets.countries.TZ": "Τανζανία, Ηνωμένη Δημοκρατία της", - "assets.countries.UA": "Ουκρανία", - "assets.countries.UG": "Ουγκάντα", - "assets.countries.UM": "Απομακρυσμένες Νησίδες των Ηνωμένων Πολιτειών", - "assets.countries.US": "Ηνωμένες Πολιτείες Αμερικής", - "assets.countries.UY": "Ουρουγουάη", - "assets.countries.UZ": "Ουζμπεκιστάν", - "assets.countries.VA": "Αγία Έδρα (Βατικανό)", - "assets.countries.VC": "Άγιος Βικέντιος και Γρεναδίνες", - "assets.countries.VE": "Βενεζουέλα, Μπολιβαριανή Δημοκρατία της", - "assets.countries.VG": "Βρετανικές Παρθένοι Νήσοι", - "assets.countries.VI": "Αμερικανικές Παρθένοι Νήσοι", - "assets.countries.VN": "Βιετνάμ", - "assets.countries.VU": "Βανουάτου", - "assets.countries.WF": "Ουαλίς και Φουτουνά", - "assets.countries.WS": "Σαμόα", - "assets.countries.YE": "Υεμένη", - "assets.countries.YT": "Μαγιότ", - "assets.countries.ZA": "Νότιος Αφρική", - "assets.countries.ZM": "Ζάμπια", - "assets.countries.ZW": "Ζιμπάμπουε", - "assets.mimetypes.application/epub_zip": "Ηλε.βιβλίο EPUB", - "assets.mimetypes.application/msword": "Έγγραφο Word", - "assets.mimetypes.application/pdf": "Έγγραφο PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Αντίγραφο ασφαλείας Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Υπολογιστικό φύλλο Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Βιβλίο εργασίας με δυνατότητα μακροεντολών Excel 2007", - "assets.mimetypes.application/vnd.ms-powerpoint": "Παρουσίαση Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Υπολογιστικό φύλλο OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Υπόδειγμα υπολογιστικού φύλλου OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Έγγραφο κειμένου OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Υπόδειγμα κειμένου OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Υπόδειγμα ιστοσελίδας του OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Παρουσίαση του Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Εμφάνιση διαφανειών PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Υπολογιστικό φύλλο Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Υπόδειγμα του Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Έγγραφο Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote παρουσίαση", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers υπολογιστικό φύλλο", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages έγγραφο", - "assets.mimetypes.application/x-javascript": "Πηγαίος κώδικας JavaScript", - "assets.mimetypes.application/x-mspublisher": "Έγγραφο Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Κινούμενα σχέδια Flash", - "assets.mimetypes.application/xhtml_xml": "Έγγραφο XHTML", - "assets.mimetypes.archive": "Αρχειοθήκη ({{$a.EXT}})", - "assets.mimetypes.audio": "Αρχείο ήχου ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Αρχείο", - "assets.mimetypes.group:archive": "Αρχειοθήκες", - "assets.mimetypes.group:audio": "Αρχεία ήχου", - "assets.mimetypes.group:document": "Αρχεία εγγράφων", - "assets.mimetypes.group:html_audio": "Αρχεία ήχου με εγγενή υποστήριξη από τους περιηγητές", - "assets.mimetypes.group:html_track": "Αρχεία κομματιών πολυμέσων HTML", - "assets.mimetypes.group:html_video": "Αρχεία βίντεο με εγγενή υποστήριξη από τους περιηγητές", - "assets.mimetypes.group:image": "Αρχεία εικόνων", - "assets.mimetypes.group:presentation": "Αρχεία παρουσιάσεων", - "assets.mimetypes.group:sourcecode": "Πηγαίος κώδικας", - "assets.mimetypes.group:spreadsheet": "Αρχεία υπολογιστικών φύλλων", - "assets.mimetypes.group:video": "Αρχεία βίντεο", - "assets.mimetypes.group:web_audio": "Αρχεία ήχου Ιστού", - "assets.mimetypes.group:web_file": "Αρχεία Ιστού", - "assets.mimetypes.group:web_image": "Αρχεία εικόνων Ιστού", - "assets.mimetypes.group:web_video": "Αρχεία βίντεο Ιστού", - "assets.mimetypes.image": "Εικόνα ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Εικονίδιο των Windows", - "assets.mimetypes.text/css": "Cascading Style-Sheet αρχεία στυλ", - "assets.mimetypes.text/csv": "Τιμές διαχωρισμένες με κόμμα (csv)", - "assets.mimetypes.text/html": "Έγγραφο HTML", - "assets.mimetypes.text/plain": "Αρχείο κειμένου", - "assets.mimetypes.text/rtf": "Έγγραφο RTF (Αρχείο Εμπλουτισμένου Κειμένου)", - "assets.mimetypes.text/vtt": "Αρχείο υπότιτλων βίντεο Ιστού", - "assets.mimetypes.video": "Αρχείο βίντεο ({{$a.EXT}})", - "core.accounts": "Λογαριασμοί χρηστών", - "core.add": "Προσθήκη", - "core.agelocationverification": "Επαλήθευση ηλικίας και τοποθεσίας", - "core.ago": "πριν {{$a}}", - "core.all": "Όλα", - "core.allgroups": "Όλες οι ομάδες", - "core.allparticipants": "Όλοι οι συμμετέχοντες", - "core.answer": "Απάντηση", - "core.answered": "Απαντημένη", - "core.areyousure": "Είστε σίγουρος ;", - "core.back": "Επιστροφή", - "core.block.blocks": "Μπλοκ", - "core.browser": "Περιηγητής", - "core.cancel": "Άκυρο", - "core.cannotconnect": "Αδυναμία σύνδεσης", - "core.cannotconnecttrouble": "Αντιμετωπίζουμε πρόβλημα κατά τη σύνδεση στον ιστότοπό σας.", - "core.cannotconnectverify": "Παρακαλούμε, ελέγξτε ότι η διεύθυνση είναι σωστή.", - "core.cannotdownloadfiles": "Το κατέβασμα αρχείων είναι απενεργοποιημένο. Παρακαλώ, επικοινωνήστε με τον διαχειριστή του ιστοτόπου σας.", - "core.captureaudio": "Εγγραφή ήχου", - "core.capturedimage": "Ληφθείσα εικόνα.", - "core.captureimage": "Λήψη εικόνας", - "core.capturevideo": "Εγγραφή βίντεο", - "core.category": "Κατηγορία", - "core.choose": "Επιλέξτε", - "core.choosedots": "Επιλέξτε...", - "core.clearsearch": "Καθαρισμός αναζήτησης", - "core.clicktohideshow": "Πατήστε για ανάπτυξη ή σύμπτυξη", - "core.clicktoseefull": "Κάντε κλικ για να δείτε το πλήρες περιεχόμενο.", - "core.close": "Κλείσιμο", - "core.comments": "Σχόλια", - "core.comments.addcomment": "Προσθήκη σχολίου...", - "core.comments.comments": "Σχόλια", - "core.comments.commentscount": "Σχόλια ({{$a}})", - "core.comments.commentsnotworking": "Τα σχόλια δεν μπορούν να ανακτηθούν", - "core.comments.deletecommentbyon": "Διαγραφή σχολίου που αναρτήθηκε από {{$a.user}} στις {{$a.time}}", - "core.comments.eventcommentcreated": "Δημιουργία σχολίου", - "core.comments.eventcommentdeleted": "Το σχόλιο διαγράφηκε", - "core.comments.nocomments": "Χωρίς σχόλια", - "core.comments.savecomment": "Αποθήκευση σχολίου", - "core.comments.warningcommentsnotsent": "Αδυναμία συγχρονισμού σχολίων. {{error}}", - "core.commentscount": "Σχόλια ({{$a}})", - "core.completion-alt-auto-fail": "Ολοκληρωμένο: {{$a}} (με βαθμό κάτω της βάσης)", - "core.completion-alt-auto-n": "Μη ολοκληρωμένο: {{$a}}", - "core.completion-alt-auto-n-override": "Δεν ολοκληρώθηκε: {{$a.modname}} (καθορίστηκε από τον/την {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Ολοκληρωμένο: {{$a}} (με βαθμό άνω της βάσης)", - "core.completion-alt-auto-y": "Ολοκληρωμένο: {{$a}}", - "core.completion-alt-auto-y-override": "Ολοκληρώθηκε: {{$a.modname}} (καθορίστηκε από τον/την {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Δεν ολοκληρώθηκε: {{$a}}. Επιλέξτε για επισήμανση ολοκλήρωσης.", - "core.completion-alt-manual-n-override": "Δεν ολοκληρώθηκε: {{$a.modname}} (καθορίστηκε από τον/την {{$a.overrideuser}}). Επιλέξτε για να την επισημάνετε ως ολοκληρωμένη.", - "core.completion-alt-manual-y": "Ολοκληρώθηκε: {{$a}}. Επιλέξτε για επισήμανση μη ολοκλήρωσης.", - "core.completion-alt-manual-y-override": "Ολοκληρώθηκε: {{$a.modname}} (καθορίστηκε από τον/την {{$a.overrideuser}}). Δεν ολοκληρώθηκε: {{$a.modname}} (καθορίστηκε από τον/την {{$a.overrideuser}}). Επιλέξτε για να την επισημάνετε ως μη ολοκληρωμένη.", - "core.confirmcanceledit": "Είστε σίγουροι ότι θέλετε να αποχωρήσετε από αυτήν τη σελίδα; Όλες οι αλλαγές θα χαθούν.", - "core.confirmdeletefile": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το αρχείο;", - "core.confirmgotabroot": "Σίγουρα θέλετε επιστροφή στο {{name}};", - "core.confirmgotabrootdefault": "Σίγουρα θέλετε να μεταβείτε στην αρχική σελίδα της τρέχουσας καρτέλας;", - "core.confirmleaveunknownchanges": "Είστε βέβαιοι ότι θέλετε να αποχωρήσετε από αυτήν τη σελίδα; Εάν έχετε μη αποθηκευμένες αλλαγές, θα χαθούν.", - "core.confirmloss": "Σίγουρα; Όλες οι αλλαγές θα χαθούν.", - "core.confirmopeninbrowser": "Θέλετε να το ανοίξετε στο πρόγραμμα περιήγησης;", - "core.considereddigitalminor": "Είστε πολύ νέος για να δημιουργήσετε λογαριασμό σε αυτόν τον ιστότοπο.", - "core.content": "Περιεχόμενο", - "core.contenteditingsynced": "Το περιεχόμενο που επεξεργάζεστε έχει συγχρονιστεί.", - "core.contentlinks.chooseaccount": "Επιλέξτε λογαριασμό", - "core.contentlinks.chooseaccounttoopenlink": "Επιλέξτε λογαριασμό για να ανοίξετε το σύνδεσμο.", - "core.contentlinks.confirmurlothersite": "Αυτός ο σύνδεσμος προέρχεται από άλλον ιστότοπο. Θέλετε να τον ανοίξετε;", - "core.contentlinks.errornoactions": "Αυτός ο σύνδεσμος δεν μπορεί να ανοίξει.", - "core.contentlinks.errornosites": "Δεν βρέθηκε κάποιος ιστότοπος για αυτό το σύνδεσμο.", - "core.contentlinks.errorredirectothersite": "Η διεύθυνση URL ανακατεύθυνσης δεν μπορεί να δείχνει σε έναν διαφορετικό ιστότοπο.", - "core.continue": "Συνέχεια", - "core.copiedtoclipboard": "Το κείμενο αντιγράφηκε στο πρόχειρο", - "core.copytoclipboard": "Αντιγραφή στο πρόχειρο", - "core.course": "Μάθημα", - "core.course.activitydisabled": "Ο οργανισμός σας απενεργοποίησε αυτήν την δραστηριότητα στην εφαρμογή κινητού.", - "core.course.activitynotyetviewableremoteaddon": "Ο οργανισμός σας εγκατέστησε ένα πρόσθετο που δεν υποστηρίζεται ακόμη.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Η εγκατάσταση Moodle του οργανισμού σας πρέπει να αναβαθμιστεί.", - "core.course.allsections": "Όλα τα τμήματα", - "core.course.askadmintosupport": "Επικοινωνήστε με το διαχειριστή του ιστοτόπου για να πείτε ότι θέλετε να χρησιμοποιήσετε αυτή τη δραστηριότητα με την εφαρμογή κινητού Moodle.", - "core.course.availablespace": "Αυτή τη στιγμή έχετε περίπου {{available}} διαθέσιμο χώρο.", - "core.course.cannotdeletewhiledownloading": "Δεν είναι δυνατή η διαγραφή αρχείων κατά τη λήψη της δραστηριότητας. Παρακαλούμε, περιμένετε να ολοκληρωθεί η λήψη.", - "core.course.confirmdeletemodulefiles": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτά τα αρχεία;", - "core.course.confirmdownload": "Πρόκειται να κατεβάσετε {{size}} σε διαθέσιμο χώρο {{availableSpace}}. Σίγουρα θέλετε να συνεχίσετε;", - "core.course.confirmdownloadunknownsize": "Αδυναμία υπολογισμού του μεγέθους των αρχείων προς λήψη.{{availableSpace}} Σίγουρα θέλετε να συνεχίσετε;", - "core.course.confirmdownloadzerosize": "Πρόκειται να ξεκινήσετε λήψη σε διαθέσιμο χώρο {{availableSpace}} Σίγουρα συνέχεια;", - "core.course.confirmlimiteddownload": "Αυτή τη στιγμή δεν υπάρχει σύνδεση με Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Πρόκειται να κατεβάσετε τουλάχιστον {{size}} σε διαθέσιμο χώρο {{availableSpace}}. Είστε σίγουροι ότι θέλετε να συνεχίσετε;", - "core.course.contents": "Περιεχόμενα", - "core.course.couldnotloadsectioncontent": "Δεν ήταν δυνατή η φόρτωση του περιεχομένου αυτού του τμήματος, δοκιμάστε ξανά αργότερα.", - "core.course.couldnotloadsections": "Δεν ήταν δυνατή η φόρτωση αυτού του τμήματος, δοκιμάστε ξανά αργότερα.", - "core.course.coursesummary": "Περίληψη μαθήματος", - "core.course.downloadcourse": "Λήψη μαθήματος", - "core.course.errordownloadingcourse": "Σφάλμα λήψης μαθήματος.", - "core.course.errordownloadingsection": "Σφάλμα λήψης τμήματος.", - "core.course.errorgetmodule": "Σφάλμα κατά τη λήψη των δεδομένων της δραστηριότητας.", - "core.course.hiddenfromstudents": "Κρυμμένο από τους μαθητές", - "core.course.hiddenoncoursepage": "Διαθέσιμο αλλά δεν εμφανίζεται στη σελίδα του μαθήματος", - "core.course.insufficientavailablequota": "Η συσκευή σας δεν ήταν δυνατό να εκχωρήσει χώρο για να αποθηκεύσει αυτό το στοιχείο λήψης. Μπορεί να διατηρεί απόθεμα χώρου για ενημερώσεις εφαρμογών και συστήματος. Παρακαλούμε να καθαρίσετε λίγο χώρο αποθήκευσης πρώτα.", - "core.course.insufficientavailablespace": "Προσπαθείτε να κάνετε λήψη {{size}}. Αυτό θα αφήσει στη συσκευή σας ανεπαρκή χώρο για να λειτουργήσει κανονικά. Παρακαλούμε να καθαρίσετε λίγο χώρο αποθήκευσης πρώτα.", - "core.course.manualcompletionnotsynced": "Η χειροκίνητη ολοκλήρωση δεν συγχρονίστηκε.", - "core.course.nocontentavailable": "Δεν υπάρχει περιεχόμενο διαθέσιμο αυτή τη στιγμή.", - "core.course.overriddennotice": "Ο τελικός βαθμός από αυτή τη δραστηριότητα ρυθμίστηκε/προσαρμόστηκε χειροκίνητα.", - "core.course.refreshcourse": "Ανανέωση μαθήματος", - "core.course.sections": "Τομείς", - "core.course.useactivityonbrowser": "Μπορείτε ακόμη να το χρησιμοποιήσετε μέσω του περιηγητή ιστού της συσκευής σας.", - "core.course.warningmanualcompletionmodified": "Η χειροκίνητη ολοκλήρωση μιας δραστηριότητας τροποποιήθηκε στον ιστότοπο.", - "core.course.warningofflinemanualcompletiondeleted": "Κάποια εκτός σύνδεσης ολοκλήρωση του μαθήματος «{{name}}» έχει διαγραφεί. {{error}}", - "core.coursedetails": "Λεπτομέρειες μαθήματος", - "core.coursenogroups": "Δεν είστε μέλος καμιάς ομάδας αυτού του μαθήματος.", - "core.courses.addtofavourites": "Επισήμανση του μαθήματος με αστερίσκο", - "core.courses.allowguests": "Σε αυτό το μάθημα επιτρέπονται και οι επισκέπτες", - "core.courses.availablecourses": "Διαθέσιμα μαθήματα", - "core.courses.cannotretrievemorecategories": "Δεν είναι δυνατή η ανάκτηση κατηγοριών μετά από το επίπεδο {{$a}}.", - "core.courses.categories": "Κατηγορίες μαθημάτων", - "core.courses.confirmselfenrol": "Είστε σίγουροι ότι θέλετε να εγγραφείτε σε αυτό το μάθημα;", - "core.courses.courses": "Μαθήματα", - "core.courses.downloadcourses": "Λήψη μαθημάτων", - "core.courses.enrolme": "Εγγραφή", - "core.courses.errorloadcategories": "Παρουσιάστηκε σφάλμα κατά την φόρτωση των κατηγοριών.", - "core.courses.errorloadcourses": "Παρουσιάστηκε σφάλμα κατά τη φόρτωση των μαθημάτων.", - "core.courses.errorloadplugins": "Τα πρόσθετα που απαιτούνται από αυτό το μάθημα δεν μπόρεσαν να φορτωθούν σωστά. Παρακαλούμε, ξαναφορτώστε την εφαρμογή κινητού για να προσπαθήσετε ξανά.", - "core.courses.errorsearching": "Παρουσιάστηκε σφάλμα κατά τη διάρκεια της αναζήτησης.", - "core.courses.errorselfenrol": "Παρουσιάστηκε σφάλμα κατά τη διάρκεια της αυτο-εγγραφής.", - "core.courses.filtermycourses": "Φιλτράρισμα των μαθημάτων μου", - "core.courses.frontpage": "Αρχική σελίδα", - "core.courses.hidecourse": "Αφαίρεση από την εμφάνιση", - "core.courses.ignore": "Αγνόηση", - "core.courses.mycourses": "Τα μαθήματά μου", - "core.courses.mymoodle": "Ταμπλό", - "core.courses.nocourses": "Δεν υπάρχει πληροφορία του μαθήματος για προβολή.", - "core.courses.nocoursesyet": "Δεν υπάρχουν μαθήματα σε αυτή την κατηγορία", - "core.courses.nosearchresults": "Χωρίς αποτέλεσμα", - "core.courses.notenroled": "Δεν είσαι εγγεγραμμένος σε αυτό το μάθημα", - "core.courses.notenrollable": "Δεν μπορείτε να αυτο-εγγραφείτε σε αυτό το μάθημα.", - "core.courses.password": "Κλειδί εγγραφής", - "core.courses.paymentrequired": "Αυτό το μάθημα απαιτεί πληρωμή για την είσοδο.", - "core.courses.paypalaccepted": "Αποδεκτές οι πληρωμές μέσω PayPal", - "core.courses.reload": "Επαναφόρτωση", - "core.courses.removefromfavourites": "Αφαίρεση της επισήμανσης με αστερίσκο από το μάθημα", - "core.courses.search": "Αναζήτηση", - "core.courses.searchcourses": "Αναζήτηση μαθημάτων", - "core.courses.searchcoursesadvice": "Μπορείτε να χρησιμοποιήσετε το κουμπί Αναζήτηση μαθημάτων για πρόσβαση ως επισκέπτης ή για να αυτο-εγγραφείτε σε μαθήματα που το επιτρέπουν.", - "core.courses.selfenrolment": "Αυτο-εγγραφή", - "core.courses.sendpaymentbutton": "Αποστολή πληρωμής με Paypal", - "core.courses.show": "Επαναφορά στην εμφάνιση", - "core.courses.totalcoursesearchresults": "Συνολικά μαθήματα: {{$a}}", - "core.currentdevice": "Τρέχουσα συσκευή", - "core.datastoredoffline": "Τα δεδομένα αποθηκεύονται στη συσκευή, διότι δεν μπορούν να σταλούν. Θα αποσταλούν αυτόματα αργότερα.", - "core.date": "Ημερομηνία", - "core.day": "ημέρα", - "core.days": "ημέρες", - "core.decsep": ",", - "core.defaultvalue": "Προεπιλογή ({{$a}})", - "core.delete": "Διαγραφή", - "core.deletedoffline": "Διαγράφηκε εκτός σύνδεσης", - "core.deleteduser": "Διαγραμμένος χρήστης", - "core.deleting": "Γίνεται διαγραφή", - "core.description": "Περιγραφή", - "core.desktop": "Επιφάνεια εργασίας", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Ψηφιακά ανήλικος", - "core.digitalminor_desc": "Παρακαλούμε, ζητήστε από τον γονέα/κηδεμόνα σας να επικοινωνήσει με:", - "core.discard": "Απόρριψη", - "core.dismiss": "Απόρριψη", - "core.displayoptions": "Εμφάνιση επιλογών", - "core.done": "Ολοκληρώθηκε", - "core.download": "Λήψη", - "core.downloaded": "Η λήψη έγινε", - "core.downloading": "Κατέβασμα", - "core.edit": "Επεξεργασία ", - "core.editor.autosavesucceeded": "Το προσχέδιο αποθηκεύτηκε.", - "core.editor.bold": "Έντονο", - "core.editor.clear": "Απαλοιφή μορφοποίησης", - "core.editor.h3": "Επικεφαλίδα (μεγάλη)", - "core.editor.h4": "Επικεφαλίδα (μεσαία)", - "core.editor.h5": "Επικεφαλίδα (μικρή)", - "core.editor.hidetoolbar": "Απόκρυψη γραμμής εργαλείων", - "core.editor.italic": "Πλάγια", - "core.editor.orderedlist": "Αριθμημένη λίστα", - "core.editor.p": "Παράγραφος", - "core.editor.strike": "Διαγραμμένα", - "core.editor.textrecovered": "Μια έκδοση προσχεδίου αυτού του κειμένου επαναφέρθηκε αυτόματα.", - "core.editor.toggle": "Εναλλαγή επεξεργαστή κειμένου", - "core.editor.underline": "Υπογράμμιση", - "core.editor.unorderedlist": "Μη αριθμημένη λίστα", - "core.emptysplit": "Αυτή η σελίδα θα εμφανιστεί κενή, εάν ο αριστερός πίνακας είναι κενός ή φορτώνεται.", - "core.error": "Σφάλμα", - "core.errorchangecompletion": "Παρουσιάστηκε σφάλμα κατά την αλλαγή της κατάστασης ολοκλήρωσης. Παρακαλώ προσπαθήστε ξανά.", - "core.errordeletefile": "Σφάλμα κατά τη διαγραφή του αρχείου. Παρακαλώ προσπαθήστε ξανά.", - "core.errordownloading": "Σφάλμα στο κατέβασμα του αρχείου.", - "core.errordownloadingsomefiles": "Σφάλμα κατά τη λήψη αρχείων μονάδας. Ορισμένα αρχεία μπορεί να λείπουν.", - "core.errorfileexistssamename": "Υπάρχει ήδη ένα αρχείο με αυτό το όνομα.", - "core.errorinvalidform": "Η φόρμα περιέχει μη έγκυρα δεδομένα. Παρακαλούμε φροντίστε να συμπληρώσετε όλα τα απαιτούμενα πεδία και ότι τα δεδομένα είναι έγκυρα.", - "core.errorinvalidresponse": "Ελήφθη μη έγκυρη απόκριση. Επικοινωνήστε με τον διαχειριστή του ιστοτόπου σας εάν το πρόβλημα παραμένει.", - "core.errorloadingcontent": "Σφάλμα κατά τη φόρτωση περιεχομένου.", - "core.errorofflinedisabled": "Η περιήγηση εκτός σύνδεσης είναι απενεργοποιημένη στον ιστότοπό σας. Πρέπει να είστε συνδεδεμένος στο Διαδίκτυο για να χρησιμοποιήσετε την εφαρμογή κινητού.", - "core.erroropenfilenoapp": "Σφάλμα κατά το άνοιγμα αρχείου: δεν βρέθηκε εφαρμογή για το άνοιγμα αυτού του είδους αρχείων.", - "core.erroropenfilenoextension": "Σφάλμα κατά το άνοιγμα αρχείου: το αρχείο δεν έχει επέκταση.", - "core.erroropenpopup": "Αυτή η δραστηριότητα προσπαθεί να ανοίξει ένα αναδυόμενο παράθυρο. Αυτό δεν υποστηρίζεται σε αυτή την εφαρμογή.", - "core.errorrenamefile": "Σφάλμα κατά τη μετονομασία αρχείου. Παρακαλώ δοκιμάστε ξανά.", - "core.errorsomedatanotdownloaded": "Αν κάνατε λήψη αυτής της δραστηριότητας, παρακαλούμε, σημειώστε ότι κάποια δεδομένα δεν κατεβαίνουν κατά την λήψη για λόγους απόδοσης και σωστής χρήσης δεδομένων.", - "core.errorsync": "Παρουσιάστηκε σφάλμα κατά τη διαδικασία του συγχρονισμού. Παρακαλώ δοκιμάστε ξανά.", - "core.errorsyncblocked": "Αυτό το {{$a}} δεν μπορεί να συγχρονιστεί τώρα εξαιτίας μιας συνεχιζόμενης διαδικασίας. Παρακαλώ δοκιμάστε ξανά αργότερα. Εάν το πρόβλημα παραμένει, δοκιμάστε να επανεκκινήσετε την εφαρμογή.", - "core.explanationdigitalminor": "Αυτές οι πληροφορίες είναι απαραίτητες για να καθοριστεί αν η ηλικία σας είναι πάνω από την ηλικία ψηφιακής συναίνεσης. Αυτή είναι η ηλικία κατά την οποία ένα άτομο μπορεί να συναινέσει στους όρους και τις προϋποθέσεις και στην νόμιμη αποθήκευση και επεξεργασία των δεδομένων του.", - "core.favourites": "Με αστερίσκο", - "core.filename": "Όνομα αρχείου", - "core.filenameexist": "Το όνομα του αρχείου υπάρχει ήδη: {{$a}}", - "core.filenotfound": "Λυπούμαστε! Το αρχείο δεν βρέθηκε.", - "core.fileuploader.addfiletext": "Προσθήκη αρχείου", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Κάμερα", - "core.fileuploader.confirmuploadfile": "Πρόκειται να ανεβάσετε {{size}}. Είστε σίγουροι ότι θέλετε να συνεχίσετε;", - "core.fileuploader.confirmuploadunknownsize": "Δεν κατέστη δυνατός ο υπολογισμός των αρχείων για ανέβασμα. Είστε σίγουροι ότι θέλετε να συνεχίσετε;", - "core.fileuploader.errorcapturingaudio": "Σφάλμα σύλληψης ήχου.", - "core.fileuploader.errorcapturingimage": "Σφάλμα σύλληψης εικόνας.", - "core.fileuploader.errorcapturingvideo": "Σφάλμα σύλληψης βίντεο.", - "core.fileuploader.errorgettingimagealbum": "Σφάλμα κατά τη λήψη εικόνας από το άλμπουμ.", - "core.fileuploader.errormustbeonlinetoupload": "Πρέπει να είστε συνδεδεμένοι για να ανεβάσετε αρχεία.", - "core.fileuploader.errornoapp": "Δεν έχετε εγκατεστημένη εφαρμογή για να εκτελέσετε αυτή την ενέργεια.", - "core.fileuploader.errorreadingfile": "Σφάλμα ανάγνωσης αρχείου.", - "core.fileuploader.errorwhileuploading": "Παρουσιάστηκε σφάλμα κατά το ανέβασμα του αρχείου.", - "core.fileuploader.file": "Αρχείο", - "core.fileuploader.filesofthesetypes": "Αποδεκτοί τύποι αρχείων:", - "core.fileuploader.fileuploaded": "Το αρχείο ανέβηκε επιτυχώς.", - "core.fileuploader.invalidfiletype": "Ο τύπος αρχείου {{$a}} δεν μπορεί να γίνει αποδεκτός.", - "core.fileuploader.maxbytesfile": "Το αρχείο {{$a.file}} είναι πολύ μεγάλο. Το μέγιστο μέγεθος που μπορείτε να ανεβάσετε είναι {{$a.size}}.", - "core.fileuploader.more": "Περισσότερα", - "core.fileuploader.photoalbums": "Άλμπουμ φωτογραφιών", - "core.fileuploader.readingfile": "Ανάγνωση αρχείου", - "core.fileuploader.readingfileperc": "Ανάγνωση αρχείου: {{$a}}%", - "core.fileuploader.selectafile": "Επιλογή αρχείου", - "core.fileuploader.uploadafile": "Ανεβάστε ένα αρχείο", - "core.fileuploader.uploading": "Ανέβασμα", - "core.fileuploader.uploadingperc": "Ανέβασμα: {{$a}}%", - "core.fileuploader.video": "Βίντεο", - "core.filter": "Φίλτρο", - "core.folder": "Φάκελος", - "core.forcepasswordchangenotice": "Πρέπει να αλλάξετε τον κωδικό πρόσβασης για να συνεχίσετε.", - "core.fulllistofcourses": "Όλα τα μαθήματα", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Μέσος όρος", - "core.grades.badgrade": "Ο βαθμός που δόθηκε δεν είναι έγκυρος", - "core.grades.contributiontocoursetotal": "Συνεισφορά στον τελικό βαθμό", - "core.grades.feedback": "Ανατροφοδότηση", - "core.grades.grade": "Βαθμός", - "core.grades.gradeitem": "Στοιχείο βαθμού", - "core.grades.grades": "Βαθμοί", - "core.grades.lettergrade": "Βαθμός με γράμμα", - "core.grades.nogradesreturned": "Δεν επιστράφηκε κανένας βαθμός", - "core.grades.nooutcome": "Χωρίς μαθησιακά αποτελέσματα", - "core.grades.percentage": "Ποσοστό", - "core.grades.range": "Εύρος", - "core.grades.rank": "Κατάταξη", - "core.grades.weight": "Συντελεστής βαρύτητας", - "core.group": "Ομάδα", - "core.groupsseparate": "Ξεχωριστές ομάδες", - "core.groupsvisible": "Ορατές ομάδες", - "core.h5p.additionallicenseinfo": "Κάθε επιπρόσθετη πληροφορία για την άδεια χρήσης", - "core.h5p.author": "Συγγραφέας", - "core.h5p.authorcomments": "Σχόλια συγγραφέα", - "core.h5p.authorcommentsdescription": "Σχόλια για τον συντάκτη του περιεχομένου. (Αυτό το κείμενο δεν θα δημοσιευτεί ως ένα μέρος των πληροφοριών πνευματικών δικαιωμάτων.)", - "core.h5p.authorname": "Όνομα συγγραφέα", - "core.h5p.authorrole": "Ρόλος συγγραφέα", - "core.h5p.by": "από", - "core.h5p.cancellabel": "Άκυρο", - "core.h5p.ccattribution": "Αναφορά Δημιουργού (CC BY)", - "core.h5p.ccattributionnc": "Αναφορά Δημιουργού - Μη Εμπορική Χρήση (CC BY-NC)", - "core.h5p.ccattributionncnd": "Αναφορά Δημιουργού - Μη Εμπορική Χρήση + Όχι Παράγωγα (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Αναφορά Δημιουργού - Μη Εμπορική Χρήση - Παρόμοια Διανομή (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Αναφορά Δημιουργού - Όχι Παράγωγα (CC BY-ND)", - "core.h5p.ccattributionsa": "Αναφορά Δημιουργού - Παρόμοια Διανομή (CC BY-SA)", - "core.h5p.ccpdd": "Αφιέρωση στον Δημόσιο Τομέα (CC0)", - "core.h5p.changedby": "Τροποποίηση από", - "core.h5p.changedescription": "Περιγραφή της τροποποίησης", - "core.h5p.changelog": "Καταγραφές τροποποίησης", - "core.h5p.changeplaceholder": "Περικοπή φωτογραφίας, αλλαγή κειμένου, κλπ.", - "core.h5p.close": "Κλείσιμο", - "core.h5p.confirmdialogbody": "Παρακαλούμε, επιβεβαιώστε ότι επιθυμείτε τη συνέχεια. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", - "core.h5p.confirmdialogheader": "Επιβεβαίωση ενέργειας", - "core.h5p.confirmlabel": "Επιβεβαίωση", - "core.h5p.connectionLost": "Η σύνδεση χάθηκε. Τα αποτελέσματα θα αποθηκευτούν και θα αποσταλούν όταν η σύνδεση αποκατασταθεί.", - "core.h5p.connectionReestablished": "Η σύνδεση αποκαταστάθηκε.", - "core.h5p.contentCopied": "Το περιεχόμενο αντιγράφηκε στο πρόχειρο", - "core.h5p.contentchanged": "Αυτό το περιεχόμενο έχει αλλάξει από τότε που το χρησιμοποιήσατε τελευταία φορά.", - "core.h5p.contenttype": "Τύπος περιεχομένου", - "core.h5p.copyright": "Άδειες χρήσης", - "core.h5p.copyrightinfo": "Πληροφορίες δικαιωμάτων πνευματικής ιδιοκτησίας", - "core.h5p.copyrightstring": "Δικαιώματα πνευματικής ιδιοκτησίας", - "core.h5p.copyrighttitle": "Εμφάνιση πληροφοριών δικαιωμάτων πνευματικής ιδιοκτησίας για αυτό το περιεχόμενο.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Ημερομηνία", - "core.h5p.disablefullscreen": "Απενεργοποίηση πλήρους οθόνης", - "core.h5p.download": "Λήψη", - "core.h5p.downloadtitle": "Λήψη αυτού του περιεχομένου ως ένα αρχείο H5P.", - "core.h5p.editor": "Συντάκτης", - "core.h5p.embed": "Ενσωμάτωση", - "core.h5p.embedtitle": "Εμφάνιση του κώδικα ενσωμάτωσης για αυτό το περιεχόμενο.", - "core.h5p.errorgetemail": "Σφάλμα κατά τη λήψη της διεύθυνσης ηλε.ταχυδρομείου χρήστη. Παρακαλούμε, ελέγξτε τη σύνδεσή σας και δοκιμάστε ξανά.", - "core.h5p.fullscreen": "Πλήρης οθόνη", - "core.h5p.gpl": "Γενική δημόσια άδεια έκδ. 3", - "core.h5p.h5ptitle": "Επισκεφθείτε το h5p.org για να δείτε κι άλλο περιεχόμενο.", - "core.h5p.hideadvanced": "Απόκρυψη προχωρημένων", - "core.h5p.license": "Άδεια χρήσης", - "core.h5p.licenseCC010": "CC0 1.0 Παγκόσμια (CC0 1.0) Εκχώρηση ως Κοινό Κτήμα", - "core.h5p.licenseCC010U": "CC0 1.0 Παγκόσμια", - "core.h5p.licenseCC10": "1.0 Γενική", - "core.h5p.licenseCC20": "2.0 Γενική", - "core.h5p.licenseCC25": "2.5 Γενική", - "core.h5p.licenseCC30": "3.0 Unported (χωρίς προσαρμογή στην τοπική νομοθεσία)", - "core.h5p.licenseCC40": "4.0 Διεθνής", - "core.h5p.licenseGPL": "Γενική δημόσια άδεια", - "core.h5p.licenseV1": "Έκδοση 1", - "core.h5p.licenseV2": "Έκδοση 2", - "core.h5p.licenseV3": "Έκδοση 3", - "core.h5p.licensee": "Αδειούχος", - "core.h5p.licenseextras": "Επιπρόσθετα άδειας χρήσης", - "core.h5p.licenseversion": "Έκδοση άδειας χρήσης", - "core.h5p.nocopyright": "Καμία διαθέσιμη πληροφορία δικαιωμάτων πνευματικής ιδιοκτησίας, για αυτό το περιεχόμενο.", - "core.h5p.offlineDialogBody": "Δεν μπορέσαμε να αποστείλουμε πληροφορίες για την ολοκλήρωση αυτής της εργασίας σας. Παρακαλούμε, ελέγξτε την σύνδεσή σας στο Διαδίκτυο.", - "core.h5p.offlineDialogHeader": "Η σύνδεσή σας με τον εξυπηρετητή χάθηκε", - "core.h5p.offlineDialogRetryButtonLabel": "Προσπάθεια ξανά τώρα", - "core.h5p.offlineDialogRetryMessage": "Προσπάθεια ξανά σε :αρ....", - "core.h5p.offlineSuccessfulSubmit": "Τα αποτελέσματα υποβλήθηκαν με επιτυχία.", - "core.h5p.offlinedisabled": "Ο ιστότοπος δεν επιτρέπει τη λήψη πακέτων H5P.", - "core.h5p.originator": "Αρχικός δημιουργός", - "core.h5p.pd": "Κοινό Κτήμα", - "core.h5p.pddl": "Εκχώρηση και Άδεια ως Κοινό Κτήμα", - "core.h5p.pdm": "Σήμα Κοινού Κτήματος (PDM)", - "core.h5p.play": "Αναπαραγωγή H5P", - "core.h5p.resizescript": "Συμπεριλάβετε αυτό το σενάριο στον ιστότοπό σας αν θέλετε δυναμικό μέγεθος του ενσωματωμένου περιεχομένου:", - "core.h5p.resubmitScores": "Προσπάθεια για υποβολή αποθηκευμένων αποτελεσμάτων.", - "core.h5p.reuse": "Επαναχρησιμοποίηση", - "core.h5p.reuseContent": "Επαναχρησιμοποίηση περιεχομένου", - "core.h5p.reuseDescription": "Επαναχρησιμοποίηση αυτού του περιεχομένου", - "core.h5p.showadvanced": "Εμφάνιση προχωρημένων", - "core.h5p.showless": "Εμφάνιση λιγότερων", - "core.h5p.showmore": "Εμφάνιση περισσότερων", - "core.h5p.size": "Μέγεθος", - "core.h5p.source": "Πηγή", - "core.h5p.startingover": "Θα ξεκινήσετε από την αρχή.", - "core.h5p.sublevel": "Υποεπίπεδο", - "core.h5p.thumbnail": "Μικρογραφία", - "core.h5p.title": "Τίτλος", - "core.h5p.undisclosed": "Μυστικός", - "core.h5p.year": "Έτος", - "core.h5p.years": "Έτος/-η", - "core.h5p.yearsfrom": "Έτη (από)", - "core.h5p.yearsto": "Έτη (έως)", - "core.hasdatatosync": "Αυτό το {{$a}} έχει δεδομένα εκτός σύνδεσης τα οποία πρέπει να συγχρονιστούν.", - "core.help": "Βοήθεια", - "core.hide": "Απόκρυψη", - "core.hour": "ώρα", - "core.hours": "ώρες", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Εικόνα", - "core.imageviewer": "Εργαλείο προβολής εικόνων", - "core.info": "Πληροφορίες", - "core.invalidformdata": "Λανθασμένα δεδομένα φόρμας", - "core.labelsep": ":", - "core.lastaccess": "Τελευταία πρόσβαση", - "core.lastdownloaded": "Τελευταία λήψη", - "core.lastmodified": "Τελευταία τροποποίηση", - "core.lastsync": "Τελευταίος συγχρονισμός", - "core.layoutgrid": "Πλέγμα", - "core.list": "Λίστα", - "core.listsep": ";", - "core.loading": "Φόρτωση", - "core.loadmore": "Φόρτωση περισσότερων", - "core.location": "Τόπος", - "core.login.auth_email": "Αυτο-καταχώρηση βασισμένη στο ηλε.ταχυδρομείο", - "core.login.authenticating": "Έλεγχος ταυτότητας", - "core.login.cancel": "Άκυρο", - "core.login.changepassword": "Αλλαγή του κωδικού πρόσβασης", - "core.login.changepasswordbutton": "Άνοιγμα της σελίδας αλλαγής κωδικού πρόσβασης", - "core.login.changepasswordhelp": "Εάν έχετε προβλήματα με την αλλαγή του κωδικού πρόσβασής σας, επικοινωνήστε με το διαχειριστή του ιστοτόπου σας. «Διαχειριστές ιστοτόπου» είναι οι άνθρωποι που διαχειρίζονται το Moodle στο σχολείο/πανεπιστήμιο/εταιρεία ή τον εκπαιδευτικό οργανισμό σας. Αν δεν γνωρίζετε πώς να επικοινωνήσετε μαζί τους, παρακαλούμε επικοινωνήστε με τους διδάσκοντες/εκπαιδευτές σας.", - "core.login.changepasswordinstructions": "Δεν μπορείτε να αλλάξετε τον κωδικό πρόσβασής σας στην εφαρμογή κινητού. Κάντε κλικ στο παρακάτω πλήκτρο για να ανοίξετε τον ιστότοπο σε έναν περιηγητή ιστού για να αλλάξετε τον κωδικό πρόσβασής σας. Λάβετε υπόψη ότι θα πρέπει να κλείσετε το πρόγραμμα περιήγησης μετά την αλλαγή του κωδικού πρόσβασης, καθώς δεν θα γίνει ανακατεύθυνση στην εφαρμογή κινητού.", - "core.login.changepasswordlogoutinstructions": "Αν προτιμάτε να αλλάξετε ιστότοπο ή να αποσυνδεθείτε, παρακαλούμε, κάντε κλικ στο ακόλουθο πλήκτρο:", - "core.login.changepasswordreconnectinstructions": "Κάντε κλικ στο ακόλουθο πλήκτρο για επανασύνδεση με τον ιστότοπο. (Λάβετε υπόψιν ότι αν δεν αλλάξατε τον κωδικό πρόσβασής σας με επιτυχία, θα επιστρέψετε στην προηγούμενη οθόνη.)", - "core.login.confirmdeletesite": "Είστε σίγουροι ότι θέλετε να διαγράψετε τον ιστότοπο {{sitename}};", - "core.login.connect": "Σύνδεση!", - "core.login.connecttomoodle": "Σύνδεση στο Moodle", - "core.login.connecttomoodleapp": "Προσπαθείτε να συνδεθείτε σε έναν κανονικό ιστότοπο Moodle. Παρακαλούμε, για πρόσβαση σε αυτόν τον ιστότοπο, κατεβάστε την επίσημη εφαρμογή κινητού Moodle.", - "core.login.connecttoworkplaceapp": "Προσπαθείτε να συνδεθείτε σε έναν ιστότοπο Moodle για Χώρους Εργασίας. Παρακαλούμε, για πρόσβαση σε αυτόν τον ιστότοπο, κατεβάστε την εφαρμογή κινητού Moodle για Χώρους Εργασίας.", - "core.login.contactyouradministrator": "Επικοινωνήστε με το διαχειριστή του ιστοτόπου για περαιτέρω βοήθεια.", - "core.login.contactyouradministratorissue": "Ζητήστε από το διαχειριστή του ιστοτόπου να ελέγξει το ζήτημα: {{$a}}", - "core.login.createaccount": "Δημιουργία του λογαριασμού μου", - "core.login.createuserandpass": "Δημιουργία ενός νέου ονόματος χρήστη και κωδικού πρόσβασης για σύνδεση στον δικτυακό τόπο", - "core.login.credentialsdescription": "Δώστε το όνομα χρήστη και τον κωδικό πρόσβασής σας για να συνδεθείτε.", - "core.login.emailconfirmsent": "

          Ένα μήνυμα ηλε.ταχυδρομείου θα πρέπει να έχει σταλεί στη διεύθυνσή σας, {{$a}}

          \n

          Περιέχει απλές οδηγίες για την ολοκλήρωση της εγγραφής σας.

          \n

          Αν συνεχίζετε να αντιμετωπίζετε δυσκολίες, επικοινωνήστε με το διαχειριστή του δικτυακού τόπου.

          ", - "core.login.emailconfirmsentnoemail": "

          Ένα μήνυμα ηλε.ταχυδρομείου πρέπει να έχει σταλεί στην διεύθυνσή σας.

          Περιέχει εύκολες οδηγίες ολοκλήρωσης της καταχώρησής σας.

          Αν συνεχίσετε να αντιμετωπίζετε κάποια δυσχέρεια, επικοινωνήστε με τον διαχειριστή του ιστοτόπου.

          ", - "core.login.emailconfirmsentsuccess": "Η αποστολή μηνύματος επιβεβαίωσης με ηλε.ταχυδρομείο έγινε με επιτυχία.", - "core.login.emailnotmatch": "Οι διευθύνσεις ηλε.ταχυδρομείου δεν ταιριάζουν", - "core.login.erroraccesscontrolalloworigin": "Η κλήση πολλαπλών προελεύσεων (Cross-Origin call) που προσπαθείτε να εκτελέσετε έχει απορριφθεί. Παρακαλώ ελέγξτε https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Παρουσιάστηκε σφάλμα κατά την διαγραφή του ιστοτόπου. Παρακαλούμε, προσπαθήστε ξανά.", - "core.login.errorexampleurl": "Η διεύθυνση URL https://campus.example.edu είναι μόνο ένα παράδειγμα διεύθυνσης URL· δεν είναι πραγματικός ιστότοπος. Παρακαλούμε, χρησιμοποιήστε τη διεύθυνση URL του ιστότοπου του σχολείου ή του οργανισμού σας.", - "core.login.errorupdatesite": "Παρουσιάστηκε σφάλμα κατά την ενημέρωση του κουπονιού του ιστοτόπου.", - "core.login.faqcannotconnectanswer": "Παρακαλούμε, επικοινωνήστε με τον διαχειριστή του ιστοτόπου σας.", - "core.login.faqcannotconnectquestion": "Πληκτρολόγησα τη διεύθυνση του ιστοτόπου μου σωστά αλλά ακόμα δεν μπορώ να συνδεθώ.", - "core.login.faqcannotfindmysiteanswer": "Πληκτρολογήσατε σωστά το όνομα; Είναι επίσης πιθανό ο ιστότοπός σας να μην περιλαμβάνεται στον κατάλογο μας δημόσιων ιστότοπων. Εάν εξακολουθείτε να μην μπορείτε να τον βρείτε, παρακαλούμε εισαγάγετε τη διεύθυνση του ιστοτόπου σας.", - "core.login.faqcannotfindmysitequestion": "Δεν μπορώ να βρω τον ιστότοπό μου.", - "core.login.faqsetupsiteanswer": "Επισκεφτείτε το σύνδεσμο {{$link}} για να δείτε τις διάφορες επιλογές που έχετε για να δημιουργήσετε τον δικό σας ιστότοπο Moodle.", - "core.login.faqsetupsitelinktitle": "Ξεκινήστε.", - "core.login.faqsetupsitequestion": "Θέλω να δημιουργήσω τον δικό μου ιστότοπο Moodle.", - "core.login.faqtestappanswer": "Για να δοκιμάσετε την εφαρμογή κινητού με έναν δοκιμαστικό ιστότοπο Moodle, πληκτρολογήστε «teacher» (δάσκαλος) ή «student» (μαθητής) στο πεδίο «Ο ιστότοπός σας» και κάντε κλικ στο κουμπί «Σύνδεση με τον ιστότοπό σας».", - "core.login.faqtestappquestion": "Θέλω απλώς να δοκιμάσω την εφαρμογή κινητού, τι μπορώ να κάνω;", - "core.login.faqwhatisurlquestion": "Ποια είναι η διεύθυνση του ιστοτόπου μου; Πώς μπορώ να βρω τη διεύθυνση URL του ιστοτόπου μου;", - "core.login.faqwhereisqrcode": "Πού μπορώ να βρω τον κωδικό QR;", - "core.login.faqwhereisqrcodeanswer": "

          Εάν ο οργανισμός σας τον έχει ενεργοποιήσει, θα βρείτε έναν κωδικό QR στον ιστότοπο στο κάτω μέρος της σελίδας προφίλ χρήστη.

          {{$image}}", - "core.login.findyoursite": "Εύρεση του ιστοτόπου σας", - "core.login.firsttime": "Είναι η πρώτη σας φορά εδώ;", - "core.login.forcepasswordchangenotice": "Πρέπει να αλλάξετε τον κωδικό πρόσβασης για να συνεχίσετε.", - "core.login.forgotten": "Ξεχάσατε το όνομα χρήστη ή τον κωδικό πρόσβασης;", - "core.login.help": "Βοήθεια", - "core.login.helpmelogin": "

          Υπάρχουν πολλές χιλιάδες ιστότοποι Moodle στον κόσμο. Αυτή η εφαρμογή κινητού μπορεί να συνδεθεί μόνο σε ιστοτόπους Moodle που έχουν ενεργοποιήσει συγκεκριμένα την πρόσβαση μέσω εφαρμογής κινητού.

          Αν δεν μπορείτε να συνδεθείτε στον ιστότοπο Moodle σας τότε πρέπει να επικοινωνήσετε με τον διαχειριστή ιστοτόπου σας και να του ζητήσετε να διαβάσει τοhttp://docs.moodle.org/en/Mobile_app

          Για να δοκιμάσετε την εφαρμογή κινητού σε έναν δοκιμαστικό ιστότοπο πληκτρολογήστε «teacher» (δάσκαλος) ή «student» (μαθητής) στο πεδίο για τη Διεύθυνση ιστοτόπου και κάντε κλικ στο πλήκτρο σύνδεσης.

          ", - "core.login.instructions": "Οδηγίες", - "core.login.invalidaccount": "Ελέγξτε τα στοιχεία σύνδεσης ή ζητήστε από τον διαχειριστή του ιστοτόπου σας να ελέγξει τη διαμόρφωση του ιστοτόπου.", - "core.login.invaliddate": "Μη έγκυρη ημερομηνία", - "core.login.invalidemail": "Εσφαλμένη διεύθυνση ηλε.ταχυδρομείου", - "core.login.invalidmoodleversion": "

          Μη έγκυρη έκδοση ιστοτόπου Moodle. Η εφαρμογή Moodle υποστηρίζει μόνο συστήματα Moodle {{$a}} και νεότερα.

          \n

          Μπορείτε να επικοινωνήσετε με τους διαχειριστές του ιστοτόπου σας και να τους ζητήσετε να ενημερώσουν το Moodle σύστημά τους.

          \n

          «Διαχειριστές ιστοτόπου» είναι οι άνθρωποι που διαχειρίζονται το Moodle στο δικό σας σχολείο/πανεπιστήμιο/εταιρεία ή εκπαιδευτικό οργανισμό. Αν δεν ξέρετε πώς να επικοινωνήσετε μαζί τους, παρακαλούμε, επικοινωνήστε με τους εκπαιδευτικούς/εκπαιδευτές σας.

          ", - "core.login.invalidsite": "Η διεύθυνση ιστοτόπου δεν είναι έγκυρη.", - "core.login.invalidtime": "Μη έγκυρη ώρα", - "core.login.invalidurl": "Μη έγκυρο URL", - "core.login.invalidvaluemax": "Η μεγαλύτερη τιμή είναι {{$a}}", - "core.login.invalidvaluemin": "Η μικρότερη τιμή είναι {{$a}}", - "core.login.localmobileunexpectedresponse": "Ο έλεγχος επιπρόσθετων λειτουργιών της εφαρμογής για κινητά Moodle επέστρεψε μια απροσδόκητη απόκριση. Θα πιστοποιηθείτε χρησιμοποιώντας την τυπική υπηρεσία κινητής τηλεφωνίας.", - "core.login.loggedoutssodescription": "Πρέπει να επαληθεύσετε ξανά τον έλεγχο ταυτότητας. Θα πρέπει να συνδεθείτε στον ιστότοπο σε ένα παράθυρο περιηγητή.", - "core.login.login": "Σύνδεση", - "core.login.loginbutton": "Σύνδεση", - "core.login.logininsiterequired": "Θα πρέπει να συνδεθείτε στον ιστότοπο σε ένα παράθυρο περιηγητή.", - "core.login.loginsteps": "Για πλήρη πρόσβαση σε αυτόν τον ιστότοπο, πρέπει πρώτα να δημιουργήσετε έναν λογαριασμό.", - "core.login.missingemail": "Λείπει η διεύθυνση ηλε.ταχυδρομείου", - "core.login.missingfirstname": "Λείπει το όνομα", - "core.login.missinglastname": "Λείπει το επώνυμο", - "core.login.mobileservicesnotenabled": "Οι υπηρεσίες κινητής τηλεφωνίας δεν είναι ενεργοποιημένες στον ιστότοπό σας. Παρακαλούμε, επικοινωνήστε με τον διαχειριστή του ιστοτόπου εάν πιστεύετε ότι η πρόσβαση στο κινητό θα πρέπει να είναι ενεργοποιημένη.", - "core.login.mustconfirm": "Πρέπει να επιβεβαιώσετε τον λογαριασμό σας", - "core.login.newaccount": "Νέος λογαριασμός", - "core.login.notloggedin": "Πρέπει να συνδεθείτε", - "core.login.onboardingcreatemanagecourses": "Δημιουργήστε και διαχειριστείτε τα μαθήματά σας", - "core.login.onboardingenrolmanagestudents": "Εγγραφή και διαχείριση των μαθητών σας", - "core.login.onboardinggetstarted": "Ξεκινήστε με το Moodle", - "core.login.onboardingialreadyhaveasite": "Έχω ήδη έναν ιστότοπο Moodle", - "core.login.onboardingimalearner": "Είμαι εκπαιδευόμενος", - "core.login.onboardingimaneducator": "Είμαι εκπαιδευτής", - "core.login.onboardingineedasite": "Χρειάζομαι έναν ιστότοπο Moodle", - "core.login.onboardingprovidefeedback": "Δώστε έγκαιρα σχόλια", - "core.login.onboardingtoconnect": "Για να συνδεθείτε στην εφαρμογή κινητού Moodle θα χρειαστείτε έναν ιστότοπο Moodle", - "core.login.onboardingwelcome": "Καλώς ορίσατε στην εφαρμογή κινητού Moodle!", - "core.login.or": "Ή", - "core.login.password": "Κωδικός πρόσβασης", - "core.login.passwordforgotten": "Ξεχάσατε τον κωδικό σας;", - "core.login.passwordforgotteninstructions2": "Για να επαναφέρετε τον κωδικό πρόσβασής σας, συμπληρώστε το όνομα χρήστη σας ή τη διεύθυνση email σας παρακάτω. Εάν βρεθεί στη βάση δεδομένων, ένα μήνυμα θα αποσταλεί στη διεύθυνση ηλε.ταχυδρομείου σας, με οδηγίες για το πώς να έχετε και πάλι πρόσβαση.", - "core.login.passwordrequired": "Απαιτείται κωδικός πρόσβασης", - "core.login.policyaccept": "Καταλαβαίνω και συμφωνώ", - "core.login.policyagree": "Πρέπει να συμφωνήσετε με αυτή την πολιτική για να συνεχίσετε να χρησιμοποιείτε αυτόν τον ιστότοπο. Συμφωνείτε;", - "core.login.policyagreement": "Συμφωνία με την πολιτική ιστοτόπου", - "core.login.policyagreementclick": "Πατήστε εδώ για να διαβάσετε τους όρους χρήσης", - "core.login.potentialidps": "Συνδεθείτε χρησιμοποιώντας το λογαριασμό σας στο:", - "core.login.profileinvaliddata": "Μη έγκυρη επιλογή", - "core.login.recaptchachallengeimage": "Εικόνα reCAPTCHA", - "core.login.recaptchaexpired": "Η επαλήθευση έληξε. Απαντήστε την ερώτηση ασφαλείας ξανά.", - "core.login.recaptchaincorrect": "Η απάντηση στην ερώτηση ασφαλείας είναι λανθασμένη.", - "core.login.reconnect": "Επανασύνδεση", - "core.login.reconnectdescription": "Η σύνδεση με το λογαριασμό σας δεν είναι έγκυρη ή έχει λήξει, πρέπει να επανασυνδεθείτε με τον ιστότοπο.", - "core.login.reconnectssodescription": "Η σύνδεση με το λογαριασμό σας δεν είναι έγκυρη ή έχει λήξει, πρέπει να επανασυνδεθείτε με τον ιστότοπο. Θα πρέπει να συνδεθείτε στον ιστότοπο σε ένα παράθυρο περιηγητή.", - "core.login.resendemail": "Επαναποστολή μηνύματος ηλε.ταχυδρομείου", - "core.login.searchby": "Αναζήτηση κατά:", - "core.login.security_question": "Ερώτηση ασφαλείας", - "core.login.selectacountry": "Επιλέξτε μια χώρα", - "core.login.selectsite": "Παρακαλούμε, επιλέξτε τον ιστότοπό σας:", - "core.login.signupplugindisabled": "{{$a}} δεν έχει ενεργοποιηθεί.", - "core.login.siteaddress": "Ο ιστότοπός σας", - "core.login.sitehasredirect": "Ο ιστότοπός σας περιέχει τουλάχιστον μία ανακατεύθυνση HTTP. Η εφαρμογή κινητού δεν μπορεί να ακολουθήσει ανακατευθύνσεις, κι αυτό μπορεί να είναι το πρόβλημα που δεν επιτρέπει στην εφαρμογή να συνδεθεί με τον ιστότοπό σας.", - "core.login.siteinmaintenance": "Ο ιστότοπός σας βρίσκεται σε λειτουργία συντήρησης", - "core.login.sitepolicynotagreederror": "Η πολιτική του ιστοτόπου δεν έγινε δεκτή.", - "core.login.siteurl": "URL του ιστοτόπου", - "core.login.siteurlrequired": "Το URL του ιστοτόπου είναι απαραίτητο, π.χ. http://www.yourmoodlesite.abc or https://www.yourmoodlesite.efg", - "core.login.startsignup": "Ξεκινήστε τώρα δημιουργώντας νέο λογαριασμό!", - "core.login.stillcantconnect": "Ακόμα δεν μπορείτε να συνδεθείτε;", - "core.login.supplyinfo": "Περισσότερες πληροφορίες", - "core.login.username": "Όνομα χρήστη", - "core.login.usernameoremail": "Εισαγάγετε όνομα χρήστη ή διεύθυνση email", - "core.login.usernamerequired": "Το όνομα χρήστη είναι απαραίτητο", - "core.login.usernotaddederror": "Ο χρήστης δεν προστέθηκε - άγνωστο σφάλμα", - "core.login.visitchangepassword": "Θέλετε να επισκεφτείτε τον ιστότοπο για να αλλάξετε τον κωδικό πρόσβασης;", - "core.login.webservicesnotenabled": "Ο Η/Υ που φιλοξενεί τον ιστότοπό σας ενδέχεται να μην έχει ενεργοποιήσει τις υπηρεσίες ιστού. Παρακαλούμε, επικοινωνήστε με τον διαχειριστή του Η/Υ για βοήθεια.", - "core.lostconnection": "Η σύνδεσή σας είναι άκυρη ή έχει λήξει. Θα πρέπει να ξανασυνδεθείτε στον ιστότοπο.", - "core.mainmenu.changesite": "Αλλαγή ιστοτόπου", - "core.mainmenu.help": "Βοήθεια", - "core.mainmenu.logout": "Αποσύνδεση", - "core.mainmenu.website": "Ιστότοπος", - "core.maxsizeandattachments": "Μέγιστο μέγεθος αρχείου: {{$a.size}}. Μέγιστος αριθμός συνημμένων: {{$a.attachments}}.", - "core.min": "λεπτό", - "core.mins": "λεπτά", - "core.misc": "Διάφορα", - "core.mod_assign": "Ανάθεση εργασίας", - "core.mod_assignment": "Ανάθεση (εργασίας) 2.2 (Απενεργοποιημένο)", - "core.mod_book": "Βιβλίο", - "core.mod_chat": "Συνομιλία", - "core.mod_choice": "Επιλογή", - "core.mod_data": "Βάση δεδομένων", - "core.mod_database": "Βάση δεδομένων", - "core.mod_external-tool": "Εξωτερικό εργαλείο", - "core.mod_feedback": "Ανατροφοδότηση", - "core.mod_file": "Αρχείο", - "core.mod_folder": "Φάκελος", - "core.mod_forum": "Φόρουμ", - "core.mod_glossary": "Γλωσσάριο", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "Πακέτο περιεχομένου IMS", - "core.mod_imscp": "Πακέτο περιεχομένου IMS", - "core.mod_label": "Ταμπέλα", - "core.mod_lesson": "Διδασκαλία", - "core.mod_lti": "Εξωτερικό εργαλείο", - "core.mod_page": "Σελίδα", - "core.mod_quiz": "Κουίζ", - "core.mod_resource": "Αρχείο", - "core.mod_scorm": "Πακέτο SCORM", - "core.mod_survey": "Έρευνα", - "core.mod_url": "Διεύθυνση URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Εργαστήριο", - "core.moduleintro": "Περιγραφή", - "core.more": "περισσότερα", - "core.mygroups": "Οι ομάδες μου", - "core.name": "Όνομα", - "core.networkerroriframemsg": "Αυτό το περιεχόμενο δεν είναι διαθέσιμο εκτός σύνδεσης. Παρακαλούμε, συνδεθείτε στο Διαδίκτυο και προσπαθήστε ξανά.", - "core.networkerrormsg": "Το δίκτυο δεν είναι ενεργοποιημένο ή δεν δουλεύει. Παρακαλούμε, ελέγξτε την σύνδεσή σας και δοκιμάστε ξανά.", - "core.never": "Ποτέ", - "core.next": "Επόμενο", - "core.no": "Όχι", - "core.nocomments": "Χωρίς σχόλια", - "core.nograde": "Κανένας βαθμός", - "core.none": "Καμία", - "core.nooptionavailable": "Δεν υπάρχει καμιά διαθέσιμη επιλογή", - "core.nopasswordchangeforced": "Δεν μπορείτε να προχωρήσετε χωρίς να αλλάξετε τον κωδικό πρόσβασής σας.", - "core.nopermissionerror": "Λυπούμαστε, αλλά δεν έχετε αυτή τη στιγμή δικαιώματα για να το κάνετε αυτό.", - "core.nopermissions": "Λυπούμαστε, αλλά αυτή τη στιγμή δεν έχετε τα δικαιώματα για να το κάνετε αυτό ({{$a}}).", - "core.noresults": "Κανένα αποτέλεσμα", - "core.noselection": "Καμία επιλογή", - "core.notapplicable": "δ/υ", - "core.notenrolledprofile": "Αυτό το προφίλ δεν είναι διαθέσιμο γιατί ο χρήστης δεν έχει γραφτεί σε αυτό το μάθημα.", - "core.notice": "Ειδοποίηση", - "core.notingroup": "Συγνώμη, αλλά θα πρέπει να είστε μέλος μιας ομάδας για να δείτε αυτή τη σελίδα.", - "core.notsent": "Δεν εστάλη", - "core.now": "τώρα", - "core.nummore": "{{$a}} περισσότερα", - "core.numwords": "{{$a}} λέξεις", - "core.offline": "Εκτός σύνδεσης", - "core.ok": "ΟΚ", - "core.online": "Εντός σύνδεσης", - "core.openfullimage": "Πατήστε εδώ για να δείτε την εικόνα σε πλήρες μέγεθος", - "core.openinbrowser": "Ανοίξτε στον περιηγητή.", - "core.othergroups": "Άλλες ομάδες", - "core.pagea": "Σελίδα {{$a}}", - "core.paymentinstant": "Χρησιμοποιήστε το παρακάτω πλήκτρο για να πληρώσετε και να εγγραφείτε μέσα σε λίγα λεπτά!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Τηλέφωνο", - "core.pictureof": "Φωτογραφία {{$a}}", - "core.previous": "Προηγούμενο", - "core.proceed": "Συνέχεια", - "core.pulltorefresh": "Τραβήξτε προς τα κάτω για ανανέωση", - "core.question.answer": "Απάντηση", - "core.question.answersaved": "Η απάντηση αποθηκεύτηκε", - "core.question.cannotdeterminestatus": "Αδυναμία προσδιορισμού κατάστασης", - "core.question.certainty": "Βεβαιότητα", - "core.question.complete": "Ολοκλήρωση", - "core.question.correct": "Σωστό", - "core.question.errorattachmentsnotsupported": "Η εφαρμογή δεν υποστηρίζει ακόμα την προσάρτηση αρχείων σε απαντήσεις.", - "core.question.errorinlinefilesnotsupported": "Η εφαρμογή δεν υποστηρίζει ακόμα την επεξεργασία αρχείων.", - "core.question.errorquestionnotsupported": "Αυτός ο τύπος ερωτήματος δεν υποστηρίζεται από την εφαρμογή: {{$a}}.", - "core.question.feedback": "Ανατροφοδότηση", - "core.question.howtodraganddrop": "Πατήστε για να επιλέξετε και στη συνέχεια, πατήστε για να αφήσετε.", - "core.question.incorrect": "Λανθασμένη", - "core.question.information": "Πληροφορίες", - "core.question.invalidanswer": "Ημιτελής απάντηση", - "core.question.notanswered": "Δεν απαντήθηκε", - "core.question.notyetanswered": "Δεν έχει απαντηθεί ακόμα", - "core.question.partiallycorrect": "Μερικώς σωστή", - "core.question.questionmessage": "Ερώτηση {{$a}}: {{$b}}", - "core.question.questionno": "Ερώτηση {{$a}}", - "core.question.requiresgrading": "Απαιτεί βαθμολόγηση", - "core.quotausage": "Χρησιμοποιείτε αυτή τη στιγμή {{$a.used}} από το συνολικό όριο {{$a.total}}.", - "core.rating.aggregateavg": "Μέσος όρος βαθμολογίας", - "core.rating.aggregatecount": "Άθροισμα βαθμολογιών", - "core.rating.aggregatemax": "Μεγαλύτερος βαθμός", - "core.rating.aggregatemin": "Μικρότερος βαθμός", - "core.rating.aggregatesum": "Συνολική βαθμολογία", - "core.rating.noratings": "Δεν υποβλήθηκαν βαθμολογίες", - "core.rating.rating": "Αξιολόγηση", - "core.rating.ratings": "Βαθμοί", - "core.redirectingtosite": "Θα μεταφερθείτε στον ιστότοπο.", - "core.refresh": "Ανανέωση", - "core.remove": "Αφαίρεση", - "core.removefiles": "Διαγραφή αρχείων {{$a}}", - "core.required": "Απαιτείται", - "core.requireduserdatamissing": "Αυτός ο χρήστης δεν έχει συμπληρωμένα κάποια απαιτούμενα στοιχεία του προφίλ του. Συμπληρώστε τα δεδομένα στο Moodle σας και προσπαθήστε ξανά.
          {{$a}}", - "core.resourcedisplayopen": "Άνοιγμα", - "core.resources": "Πόροι", - "core.restore": "Επαναφορά", - "core.restricted": "Περιορισμένο", - "core.retry": "Προσπαθήστε ξανά", - "core.save": "Αποθήκευση", - "core.savechanges": "Αποθήκευση αλλαγών", - "core.search": "Αναζήτηση", - "core.searching": "Αναζήτηση", - "core.searchresults": "Αποτελέσματα αναζήτησης", - "core.sec": "δευτερόλεπτο", - "core.secs": "δευτερόλεπτα", - "core.seemoredetail": "Κάντε κλικ εδώ για να δείτε περισσότερες λεπτομέρειες", - "core.selectacategory": "Παρακαλούμε, επιλέξτε μια κατηγορία", - "core.selectacourse": "Επιλέξτε ένα μάθημα", - "core.selectagroup": "Επιλέξτε μια ομάδα", - "core.send": "Αποστολή", - "core.sending": "Αποστέλλεται", - "core.serverconnection": "Σφάλμα σύνδεσης με τον εξυπηρετητή", - "core.settings.about": "Σχετικά", - "core.settings.appsettings": "Ρυθμίσεις εφαρμογής κινητού", - "core.settings.appversion": "Έκδοση εφαρμογής κινητού", - "core.settings.cannotsyncoffline": "Δεν είναι δυνατός ο συγχρονισμός εκτός σύνδεσης.", - "core.settings.cannotsyncwithoutwifi": "Δεν μπορεί να γίνει συγχρονισμός, επειδή οι τρέχουσες ρυθμίσεις επιτρέπουν μόνο συγχρονισμό όταν είστε συνδεδεμένοι με Wi-Fi. Συνδεθείτε με ένα δίκτυο Wi-Fi.", - "core.settings.colorscheme": "Συνδυασμός χρωμάτων", - "core.settings.colorscheme-auto": "Αυτόματα (με βάση τις ρυθμίσεις συστήματος)", - "core.settings.colorscheme-dark": "Σκούρο", - "core.settings.colorscheme-light": "Ανοιχτό", - "core.settings.compilationinfo": "Πληροφορίες συλλογής", - "core.settings.copyinfo": "Αντιγραφή πληροφοριών συσκευής στο πρόχειρο", - "core.settings.cordovadevicemodel": "Μοντέλο συσκευής Cordova", - "core.settings.cordovadeviceosversion": "Cordova έκδοση συσκευής OS", - "core.settings.cordovadeviceplatform": "Πλατφόρμα συσκευής Cordova", - "core.settings.cordovadeviceuuid": "Cordova Device uuid", - "core.settings.cordovaversion": "Έκδοση Cordova", - "core.settings.currentlanguage": "Επιλεγμένη γλώσσα", - "core.settings.debugdisplay": "Εμφάνιση μηνυμάτων αποσφαλμάτωσης", - "core.settings.debugdisplaydescription": "Αν ενεργοποιηθεί, τα μηνύματα σφάλματος ((στα αναδυόμενα παραθυράκια)) θα εμφανίζουν περισσότερα δεδομένα για το σφάλμα, αν είναι δυνατόν.", - "core.settings.deletesitefiles": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τα ληφθέντα αρχεία και τα δεδομένα κρυφής μνήμης από τον ιστότοπο «{{sitename}}»; Δεν θα μπορείτε να χρησιμοποιήσετε την εφαρμογή κινητού σε κατάσταση εκτός σύνδεσης.", - "core.settings.deletesitefilestitle": "Διαγραφή αρχείων ιστοτόπου", - "core.settings.deviceinfo": "Πληροφορίες συσκευές", - "core.settings.deviceos": "Λειτουργικό σύστημα συσκευής", - "core.settings.disableall": "Απενεργοποίηση ειδοποιήσεων", - "core.settings.disabled": "Απενεργοποιημένο", - "core.settings.displayformat": "Μορφή εμφάνισης", - "core.settings.enabledownloadsection": "Ενεργοποίηση της λήψης τμημάτων", - "core.settings.enablefirebaseanalytics": "Ενεργοποίηση αναλυτικής Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Εάν ενεργοποιηθεί, η εφαρμογή κινητού θα συλλέγει δεδομένα χρήσης ανώνυμα.", - "core.settings.enablerichtexteditor": "Ενεργοποιήστε τον επεξεργαστή εμπλουτισμένου κειμένου", - "core.settings.enablerichtexteditordescription": "Εάν ενεργοποιηθεί, ένας επεξεργαστής εμπλουτισμένου κειμένου θα είναι διαθέσιμος όταν εισέρχεστε σε περιεχόμενο.", - "core.settings.enablesyncwifi": "Να επιτρέπεται ο συγχρονισμός μόνο όταν είστε συνδεδεμένοι μέσω Wifi", - "core.settings.entriesincache": "{{$a}} καταχωρήσεις στην κρυφή μνήμη", - "core.settings.errordeletesitefiles": "Σφάλμα κατά τη διαγραφή αρχείων ιστοτόπου.", - "core.settings.errorsyncsite": "Παρουσιάστηκε σφάλμα κατά το συγχρονισμό των δεδομένων ιστοτόπου, ελέγξτε τη σύνδεση στο διαδίκτυο και δοκιμάστε ξανά.", - "core.settings.estimatedfreespace": "Εκτιμώμενος ελεύθερος χώρος", - "core.settings.filesystemroot": "Σύστημα αρχείων root", - "core.settings.fontsize": "Μέγεθος κειμένου", - "core.settings.fontsizecharacter": "Α", - "core.settings.forcedsetting": "Αυτή η ρύθμιση έχει επιβληθεί από τις ρυθμίσεις του ιστοτόπου σας.", - "core.settings.general": "Γενικά", - "core.settings.language": "Γλώσσα", - "core.settings.license": "Άδεια", - "core.settings.localnotifavailable": "Διαθέσιμες τοπικές ειδοποιήσεις", - "core.settings.locationhref": "Webview URL", - "core.settings.locked": "Κλειδωμένο", - "core.settings.loggedin": "Εντός σύνδεσης", - "core.settings.loggedoff": "Εκτός σύνδεσης", - "core.settings.navigatorlanguage": "Γλώσσα πλοήγησης", - "core.settings.navigatoruseragent": "Navigator userAgent", - "core.settings.networkstatus": "Κατάσταση σύνδεσης στο Internet", - "core.settings.opensourcelicenses": "Άδειες ανοικτού κώδικα", - "core.settings.preferences": "Προτιμήσεις", - "core.settings.privacypolicy": "Πολιτική απορρήτου", - "core.settings.publisher": "Εκδότης", - "core.settings.pushid": "Αναγνωριστικό προωθητικών ειδοποιήσεων", - "core.settings.reportinbackground": "Αυτόματη αναφορά σφαλμάτων", - "core.settings.screen": "Πληροφορίες οθόνης", - "core.settings.settings": "Ρυθμίσεις", - "core.settings.showdownloadoptions": "Εμφάνιση επιλογών λήψης", - "core.settings.siteinfo": "Πληροφορίες ιστοτόπου", - "core.settings.sites": "Δικτυακοί τόποι", - "core.settings.spaceusage": "Χρήση χώρου", - "core.settings.spaceusagehelp": "Η διαγραφή των αποθηκευμένων πληροφοριών του ισττόπου θα καταργήσει όλα τα δεδομένα εκτός σύνδεσης ιστότοπου. Αυτές οι πληροφορίες σάς επιτρέπουν να χρησιμοποιείτε την εφαρμογή όταν είστε εκτός σύνδεσης.", - "core.settings.synchronization": "Συγχρονισμός", - "core.settings.synchronizenow": "Συγχρονίστε τώρα", - "core.settings.synchronizenowhelp": "Ο συγχρονισμός ενός ιστοτόπου θα στείλει εκκρεμείς αλλαγές και όλη τη δραστηριότητα εκτός σύνδεσης που είναι αποθηκευμένη στη συσκευή και θα συγχρονίσει κάποια δεδομένα όπως μηνύματα και ειδοποιήσεις.", - "core.settings.syncsettings": "Ρυθμίσεις συγχρονισμού", - "core.settings.total": "Συνολικά", - "core.settings.wificonnection": "Σύνδεση WiFi", - "core.sharedfiles.chooseaccountstorefile": "Επιλέξτε ένα λογαριασμό για να αποθηκεύσετε το αρχείο.", - "core.sharedfiles.chooseactionrepeatedfile": "Υπάρχει ήδη ένα αρχείο με αυτό το όνομα. Θέλετε να αντικαταστήσετε το υπάρχον αρχείο ή να το μετονομάσετε σε «{{$a}}»;", - "core.sharedfiles.errorreceivefilenosites": "Δεν υπάρχουν αποθηκευμένες τοποθεσίες. Προσθέστε έναν ιστότοπο προτού μοιραστείτε ένα αρχείο με την εφαρμογή.", - "core.sharedfiles.nosharedfiles": "Δεν υπάρχουν αποθηκευμένα αρχεία σε αυτόν τον ιστότοπο.", - "core.sharedfiles.nosharedfilestoupload": "Δεν έχετε αρχεία για ανέβασμα εδώ. Αν θέλετε να ανεβάσετε ένα αρχείο από άλλη εφαρμογή, εντοπίστε το αρχείο και κάντε κλικ στο κουμπί «Άνοιγμα σε».", - "core.sharedfiles.rename": "Μετονομασία", - "core.sharedfiles.replace": "Αντικατάσταση", - "core.sharedfiles.sharedfiles": "Κοινόχρηστα αρχεία", - "core.sharedfiles.successstorefile": "Το αρχείο αποθηκεύτηκε με επιτυχία. Τώρα μπορείτε να επιλέξετε αυτό το αρχείο για να το ανεβάσετε στα ιδιωτικά αρχεία σας ή να το επισυνάψετε σε ορισμένες δραστηριότητες.", - "core.show": "Προβολή", - "core.showless": "Εμφάνιση λιγότερων...", - "core.showmore": "Εμφάνιση περισσότερων...", - "core.site": "Ιστότοπος", - "core.sitehome.sitehome": "Αρχική ιστοτόπου", - "core.sitehome.sitenews": "Ανακοινώσεις ιστοτόπου", - "core.sitemaintenance": "Πραγματοποιούνται εργασίες συντήρησης στο σύστημα.", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Παράκαμψη", - "core.sorry": "Λυπάμαι...", - "core.sort": "Ταξινόμηση", - "core.sortby": "Ταξινόμηση κατά", - "core.start": "Έναρξη", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedatetimeshort": "%d/%m/%y, %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Υποβολή", - "core.success": "Επιτυχία", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Προεπιλεγμένη συλλογή", - "core.tag.errorareanotsupported": "Αυτή η περιοχή ετικέτας δεν υποστηρίζεται από την εφαρμογή κινητού.", - "core.tag.inalltagcoll": "Παντού", - "core.tag.itemstaggedwith": "Στην περιοχή «{{$a.tagarea}}» τοποθετήθηκε ετικέτα «{{$a.tag}}»", - "core.tag.noresultsfor": "Δεν βρέθηκε το «{{$a}}»", - "core.tag.notagsfound": "Δεν βρέθηκαν ετικέτες που να ταιριάζουν με το «{{$a}}»", - "core.tag.searchtags": "Αναζήτηση ετικετών", - "core.tag.showingfirsttags": "Προβάλλονται οι {{$a}} περισσότερο δημοφιλείς ετικέτες", - "core.tag.tag": "Ετικέτα", - "core.tag.tagarea_course": "Μαθήματα", - "core.tag.tagarea_course_modules": "Δραστηριότητες και πόροι", - "core.tag.tagarea_post": "Αναρτήσεις ιστολογίου", - "core.tag.tagarea_user": "Ενδιαφέροντα χρήστη", - "core.tag.tags": "Ετικέτες", - "core.tag.warningareasnotsupported": "Μερικές από τις περιοχές ετικέτας δεν εμφανίζονται, διότι δεν υποστηρίζονται από την εφαρμογή κινητού.", - "core.teachers": "Διδάσκοντες", - "core.thereisdatatosync": "Υπάρχουν εκτός σύνδεσης {{$a}} για συγχρονισμό.", - "core.thisdirection": "ltr", - "core.time": "Ώρα", - "core.timesup": "Έληξε ο χρόνος!", - "core.today": "Σήμερα", - "core.tryagain": "Προσπαθήστε ξανά.", - "core.twoparagraphs": "{{p1}}

          {{p2}}", - "core.uhoh": "Ωχ όχι!", - "core.unexpectederror": "Απρόσμενο σφάλμα. Κλείστε και ανοίξτε ξανά την εφαρμογή για να προσπαθήσετε ξανά", - "core.unicodenotsupported": "Ορισμένα εμότζι δεν υποστηρίζονται σε αυτόν τον ιστότοπο. Αυτοί οι χαρακτήρες θα αφαιρεθούν κατά την αποστολή του μηνύματος.", - "core.unicodenotsupportedcleanerror": "Κενό κείμενο βρέθηκε κατά τον καθαρισμό χαρακτήρων Unicode.", - "core.unknown": "Άγνωστο", - "core.unlimited": "Χωρίς περιορισμό", - "core.unzipping": "Αποσυμπίεση", - "core.updaterequired": "Απαιτείται ενημέρωση εφαρμογής κινητού", - "core.updaterequireddesc": "Παρακαλούμε, ενημερώστε την εφαρμογή κινητού σας σε έκδοση {{$a}}", - "core.upgraderunning": "Ο ιστότοπος βρίσκεται υπό αναβάθμιση. Παρακαλούμε ξαναπροσπαθήστε αργότερα.", - "core.user": "Χρήστης", - "core.user.address": "Διεύθυνση", - "core.user.city": "Πόλη/χωριό", - "core.user.contact": "Επικοινωνία", - "core.user.country": "Χώρα", - "core.user.description": "Περιγραφή", - "core.user.details": "Λεπτομέρειες", - "core.user.detailsnotavailable": "Λεπτομέρειες για αυτό τον χρήστη δεν είναι διαθέσιμες.", - "core.user.editingteacher": "Διδάσκων", - "core.user.email": "Διεύθυνση ηλε.ταχυδρομείου", - "core.user.emailagain": "Διεύθυνση ηλε.ταχυδρομείου (ξανά)", - "core.user.errorloaduser": "Σφάλμα φόρτωσης χρήστη.", - "core.user.firstname": "Μικρό/Βαπτιστικό όνομα", - "core.user.interests": "Ενδιαφέροντα", - "core.user.lastname": "Επώνυμο", - "core.user.manager": "Διαχειριστής", - "core.user.newpicture": "Νέα εικόνα", - "core.user.noparticipants": "Δε βρέθηκαν συμμετέχοντες για αυτό το μάθημα", - "core.user.participants": "Συμμετέχοντες", - "core.user.phone1": "Τηλέφωνο", - "core.user.phone2": "Κινητό τηλέφωνο", - "core.user.roles": "Ρόλοι", - "core.user.sendemail": "Ηλε.ταχυδρομείο", - "core.user.student": "Μαθητής", - "core.user.teacher": "Περιορισμένος διδάσκων", - "core.user.webpage": "Ιστοσελίδα", - "core.userdeleted": "Αυτός ο λογαριασμός χρήστη έχει διαγραφεί", - "core.userdetails": "Λεπτομέρειες χρήστη", - "core.usernotfullysetup": "Ο χρήστης δεν ρυθμίστηκε πλήρως", - "core.users": "Χρήστες", - "core.view": "Προβολή", - "core.viewcode": "Εμφάνιση κώδικα", - "core.vieweditor": "Εμφάνιση επεξεργαστή κειμένου", - "core.viewembeddedcontent": "Εμφάνιση ενσωματωμένου περιεχομένου", - "core.viewprofile": "Επισκόπηση του προφίλ", - "core.warningofflinedatadeleted": "Τα δεδομένα εκτός σύνδεσης {{component}} «{{name}}» έχουν σβηστεί. {{error}}", - "core.whatisyourage": "Ποιά είναι η ηλικία σας;", - "core.wheredoyoulive": "Σε ποιά χώρα ζείτε;", - "core.whoops": "Ωχ!", - "core.whyisthishappening": "Γιατί συμβαίνει αυτό;", - "core.whyisthisrequired": "Γιατί απαιτούνται αυτά;", - "core.wsfunctionnotavailable": "Η λειτουργία διαδικτύου δεν είναι διαθέσιμη.", - "core.year": "έτος", - "core.years": "έτη", - "core.yes": "Ναι", - "core.youreoffline": "Είστε εκτός σύνδεσης", - "core.youreonline": "Είστε πάλι εντός σύνδεσης" -} \ No newline at end of file diff --git a/src/assets/lang/en-us.json b/src/assets/lang/en-us.json deleted file mode 100644 index c8a3e9ae4..000000000 --- a/src/assets/lang/en-us.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "addon.block_newsitems.pluginname": "Latest news", - "addon.block_recentactivity.pluginname": "Recent activity", - "addon.block_sitemainmenu.pluginname": "Main menu", - "addon.mod_assign.markingworkflowstate": "Grading workflow state", - "addon.mod_assign.markingworkflowstateinmarking": "In grading", - "addon.mod_assign.markingworkflowstatenotmarked": "Not graded", - "addon.mod_assign.markingworkflowstatereadyforreview": "Grading completed", - "addon.mod_quiz.marks": "Points", - "core.courses.notenroled": "You are not enrolled in this course", - "core.decsep": ".", - "core.h5p.pddl": "Public Domain Dedication and License", - "core.labelsep": ":", - "core.listsep": ",", - "core.login.loginsteps": "For full access to this site, you first need to create an account.", - "core.notenrolledprofile": "This profile is not available because this user is not enrolled in this course.", - "core.parentlanguage": "en", - "core.paymentinstant": "Use the button below to pay and be enrolled within minutes!", - "core.settings.license": "License", - "core.strftimedate": "%B %d, %Y", - "core.strftimedatefullshort": "%m/%d/%y", - "core.strftimedateshort": "%B %d", - "core.strftimedatetime": "%B %d %Y, %I:%M %p", - "core.strftimedatetimeshort": "%m/%d/%y, %H:%M", - "core.strftimedaydate": "%A, %B %d, %Y", - "core.strftimedaydatetime": "%A, %B %d, %Y, %I:%M %p", - "core.strftimedayshort": "%A, %B %d", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%I:%M %p, %b %d", - "core.strftimerecentfull": "%a, %b %d, %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.thisdirection": "ltr", - "core.user.lastname": "Last name" -} \ No newline at end of file diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json deleted file mode 100644 index dedb0a4f6..000000000 --- a/src/assets/lang/en.json +++ /dev/null @@ -1,2192 +0,0 @@ -{ - "addon.badges.alignment": "Alignment", - "addon.badges.badgedetails": "Badge details", - "addon.badges.badges": "Badges", - "addon.badges.bendorsement": "Endorsement", - "addon.badges.claimcomment": "Endorsement comment", - "addon.badges.claimid": "Claim URL", - "addon.badges.contact": "Contact", - "addon.badges.dateawarded": "Date issued", - "addon.badges.expired": "Expired", - "addon.badges.expirydate": "Expiry date", - "addon.badges.imageauthoremail": "Image author's email", - "addon.badges.imageauthorname": "Image author's name", - "addon.badges.imageauthorurl": "Image author's URL", - "addon.badges.imagecaption": "Image caption", - "addon.badges.issuancedetails": "Badge expiry", - "addon.badges.issuerdetails": "Issuer details", - "addon.badges.issueremail": "Email", - "addon.badges.issuername": "Issuer name", - "addon.badges.issuerurl": "Issuer URL", - "addon.badges.language": "Language", - "addon.badges.noalignment": "This badge does not have any external skills or standards specified.", - "addon.badges.nobadges": "There are no badges available.", - "addon.badges.norelated": "This badge does not have any related badges.", - "addon.badges.recipientdetails": "Recipient details", - "addon.badges.relatedbages": "Related badges", - "addon.badges.version": "Version", - "addon.badges.warnexpired": "(This badge has expired!)", - "addon.block_activitymodules.pluginname": "Activities", - "addon.block_activityresults.pluginname": "Activity results", - "addon.block_badges.pluginname": "Latest badges", - "addon.block_blogmenu.pluginname": "Blog menu", - "addon.block_blogrecent.pluginname": "Recent blog entries", - "addon.block_blogtags.pluginname": "Blog tags", - "addon.block_calendarmonth.pluginname": "Calendar", - "addon.block_calendarupcoming.pluginname": "Upcoming events", - "addon.block_comments.pluginname": "Comments", - "addon.block_completionstatus.pluginname": "Course completion status", - "addon.block_glossaryrandom.pluginname": "Random glossary entry", - "addon.block_learningplans.pluginname": "Learning plans", - "addon.block_myoverview.all": "All (except removed from view)", - "addon.block_myoverview.allincludinghidden": "All", - "addon.block_myoverview.favourites": "Starred", - "addon.block_myoverview.future": "Future", - "addon.block_myoverview.hiddencourses": "Removed from view", - "addon.block_myoverview.inprogress": "In progress", - "addon.block_myoverview.lastaccessed": "Last accessed", - "addon.block_myoverview.nocourses": "No courses", - "addon.block_myoverview.past": "Past", - "addon.block_myoverview.pluginname": "Course overview", - "addon.block_myoverview.shortname": "Short name", - "addon.block_myoverview.title": "Course name", - "addon.block_newsitems.pluginname": "Latest announcements", - "addon.block_onlineusers.pluginname": "Online users", - "addon.block_privatefiles.pluginname": "Private files", - "addon.block_recentactivity.pluginname": "Recent activity", - "addon.block_recentlyaccessedcourses.nocourses": "No recent courses", - "addon.block_recentlyaccessedcourses.pluginname": "Recently accessed courses", - "addon.block_recentlyaccesseditems.noitems": "No recent items", - "addon.block_recentlyaccesseditems.pluginname": "Recently accessed items", - "addon.block_rssclient.pluginname": "Remote RSS feeds", - "addon.block_selfcompletion.pluginname": "Self completion", - "addon.block_sitemainmenu.pluginname": "Main menu", - "addon.block_starredcourses.nocourses": "No starred courses", - "addon.block_starredcourses.pluginname": "Starred courses", - "addon.block_tags.pluginname": "Tags", - "addon.block_timeline.duedate": "Due date", - "addon.block_timeline.next30days": "Next 30 days", - "addon.block_timeline.next3months": "Next 3 months", - "addon.block_timeline.next6months": "Next 6 months", - "addon.block_timeline.next7days": "Next 7 days", - "addon.block_timeline.nocoursesinprogress": "No in-progress courses", - "addon.block_timeline.noevents": "No upcoming activities due", - "addon.block_timeline.overdue": "Overdue", - "addon.block_timeline.pluginname": "Timeline", - "addon.block_timeline.sortbycourses": "Sort by courses", - "addon.block_timeline.sortbydates": "Sort by dates", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Blog entries", - "addon.blog.errorloadentries": "Error loading blog entries.", - "addon.blog.linktooriginalentry": "Link to original blog entry", - "addon.blog.noentriesyet": "No visible entries here", - "addon.blog.publishtonoone": "Yourself (draft)", - "addon.blog.publishtosite": "Anyone on this site", - "addon.blog.publishtoworld": "Anyone in the world", - "addon.blog.showonlyyourentries": "Show only your entries", - "addon.blog.siteblogheading": "Site blog", - "addon.calendar.allday": "All day", - "addon.calendar.calendar": "Calendar", - "addon.calendar.calendarevent": "Calendar event", - "addon.calendar.calendarevents": "Calendar events", - "addon.calendar.calendarreminders": "Calendar reminders", - "addon.calendar.categoryevents": "Category events", - "addon.calendar.confirmeventdelete": "Are you sure you want to delete the \"{{$a}}\" event?", - "addon.calendar.confirmeventseriesdelete": "The \"{{$a.name}}\" event is part of a series. Do you want to delete just this event, or all {{$a.count}} events in the series?", - "addon.calendar.courseevents": "Course events", - "addon.calendar.currentmonth": "Current Month", - "addon.calendar.daynext": "Next day", - "addon.calendar.dayprev": "Previous day", - "addon.calendar.defaultnotificationtime": "Default notification time", - "addon.calendar.deleteallevents": "Delete all events", - "addon.calendar.deleteevent": "Delete event", - "addon.calendar.deleteoneevent": "Delete this event", - "addon.calendar.durationminutes": "Duration in minutes", - "addon.calendar.durationnone": "Without duration", - "addon.calendar.durationuntil": "Until", - "addon.calendar.editevent": "Editing event", - "addon.calendar.errorloadevent": "Error loading event.", - "addon.calendar.errorloadevents": "Error loading events.", - "addon.calendar.eventcalendareventdeleted": "Calendar event deleted", - "addon.calendar.eventduration": "Duration", - "addon.calendar.eventendtime": "End time", - "addon.calendar.eventkind": "Type of event", - "addon.calendar.eventname": "Event title", - "addon.calendar.eventstarttime": "Start time", - "addon.calendar.eventtype": "Event type", - "addon.calendar.fri": "Fri", - "addon.calendar.friday": "Friday", - "addon.calendar.gotoactivity": "Go to activity", - "addon.calendar.groupevents": "Group events", - "addon.calendar.invalidtimedurationminutes": "The duration in minutes you have entered is invalid. Please enter the duration in minutes greater than 0 or select no duration.", - "addon.calendar.invalidtimedurationuntil": "The date and time you selected for duration until is before the start time of the event. Please correct this before proceeding.", - "addon.calendar.mon": "Mon", - "addon.calendar.monday": "Monday", - "addon.calendar.monthlyview": "Monthly view", - "addon.calendar.newevent": "New event", - "addon.calendar.noevents": "There are no events", - "addon.calendar.nopermissiontoupdatecalendar": "Sorry, but you do not have permission to update the calendar event.", - "addon.calendar.reminders": "Reminders", - "addon.calendar.repeatedevents": "Repeated events", - "addon.calendar.repeateditall": "Also apply changes to the other {{$a}} events in this repeat series", - "addon.calendar.repeateditthis": "Apply changes to this event only", - "addon.calendar.repeatevent": "Repeat this event", - "addon.calendar.repeatweeksl": "Repeat weekly, creating altogether", - "addon.calendar.sat": "Sat", - "addon.calendar.saturday": "Saturday", - "addon.calendar.setnewreminder": "Set a new reminder", - "addon.calendar.siteevents": "Site events", - "addon.calendar.sun": "Sun", - "addon.calendar.sunday": "Sunday", - "addon.calendar.thu": "Thu", - "addon.calendar.thursday": "Thursday", - "addon.calendar.today": "Today", - "addon.calendar.tomorrow": "Tomorrow", - "addon.calendar.tue": "Tue", - "addon.calendar.tuesday": "Tuesday", - "addon.calendar.typecategory": "Category event", - "addon.calendar.typeclose": "Close event", - "addon.calendar.typecourse": "Course event", - "addon.calendar.typedue": "Due event", - "addon.calendar.typegradingdue": "Grading due event", - "addon.calendar.typegroup": "Group event", - "addon.calendar.typeopen": "Open event", - "addon.calendar.typesite": "Site event", - "addon.calendar.typeuser": "User event", - "addon.calendar.upcomingevents": "Upcoming events", - "addon.calendar.userevents": "User events", - "addon.calendar.wed": "Wed", - "addon.calendar.wednesday": "Wednesday", - "addon.calendar.when": "When", - "addon.calendar.yesterday": "Yesterday", - "addon.competency.activities": "Activities", - "addon.competency.competencies": "Competencies", - "addon.competency.competenciesmostoftennotproficientincourse": "Competencies most often not proficient in this course", - "addon.competency.coursecompetencies": "Course competencies", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Competency ratings in this course do not affect learning plans.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Competency ratings in this course are updated immediately in learning plans.", - "addon.competency.crossreferencedcompetencies": "Cross-referenced competencies", - "addon.competency.duedate": "Due date", - "addon.competency.errornocompetenciesfound": "No competencies found", - "addon.competency.evidence": "Evidence", - "addon.competency.evidence_competencyrule": "The rule of the competency was met.", - "addon.competency.evidence_coursecompleted": "The course '{{$a}}' was completed.", - "addon.competency.evidence_coursemodulecompleted": "The activity '{{$a}}' was completed.", - "addon.competency.evidence_courserestored": "The rating was restored along with the course '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "The evidence of prior learning '{{$a}}' was linked.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "The evidence of prior learning '{{$a}}' was unlinked.", - "addon.competency.evidence_manualoverride": "The competency rating was manually set.", - "addon.competency.evidence_manualoverrideincourse": "The competency rating was manually set in the course '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "The competency rating was manually set in the learning plan '{{$a}}'.", - "addon.competency.learningplancompetencies": "Learning plan competencies", - "addon.competency.learningplans": "Learning plans", - "addon.competency.myplans": "My learning plans", - "addon.competency.noactivities": "No activities", - "addon.competency.nocompetencies": "No competencies", - "addon.competency.nocompetenciesincourse": "No competencies have been linked to this course.", - "addon.competency.nocrossreferencedcompetencies": "No other competencies have been cross-referenced to this competency.", - "addon.competency.noevidence": "No evidence", - "addon.competency.noplanswerecreated": "No learning plans were created.", - "addon.competency.nouserplanswithcompetency": "No learning plans contain this competency.", - "addon.competency.path": "Path:", - "addon.competency.planstatusactive": "Active", - "addon.competency.planstatuscomplete": "Complete", - "addon.competency.planstatusdraft": "Draft", - "addon.competency.planstatusinreview": "In review", - "addon.competency.planstatuswaitingforreview": "Waiting for review", - "addon.competency.proficient": "Proficient", - "addon.competency.progress": "Progress", - "addon.competency.rating": "Rating", - "addon.competency.reviewstatus": "Review status", - "addon.competency.status": "Status", - "addon.competency.template": "Learning plan template", - "addon.competency.uponcoursecompletion": "Upon course completion:", - "addon.competency.usercompetencystatus_idle": "Idle", - "addon.competency.usercompetencystatus_inreview": "In review", - "addon.competency.usercompetencystatus_waitingforreview": "Waiting for review", - "addon.competency.userplans": "Learning plans", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} out of {{$a.y}} competencies are proficient", - "addon.competency.xcompetenciesproficientoutofyincourse": "You are proficient in {{$a.x}} out of {{$a.y}} competencies in this course.", - "addon.coursecompletion.complete": "Complete", - "addon.coursecompletion.completecourse": "Complete course", - "addon.coursecompletion.completed": "Completed", - "addon.coursecompletion.completiondate": "Completion date", - "addon.coursecompletion.completionmenuitem": "Completion", - "addon.coursecompletion.couldnotloadreport": "Could not load the course completion report. Please try again later.", - "addon.coursecompletion.coursecompletion": "Course completion", - "addon.coursecompletion.criteria": "Criteria", - "addon.coursecompletion.criteriagroup": "Criteria group", - "addon.coursecompletion.criteriarequiredall": "All criteria below are required", - "addon.coursecompletion.criteriarequiredany": "Any criteria below are required", - "addon.coursecompletion.inprogress": "In progress", - "addon.coursecompletion.manualselfcompletion": "Manual self completion", - "addon.coursecompletion.nottracked": "You are currently not being tracked by completion in this course", - "addon.coursecompletion.notyetstarted": "Not yet started", - "addon.coursecompletion.pending": "Pending", - "addon.coursecompletion.required": "Required", - "addon.coursecompletion.requiredcriteria": "Required criteria", - "addon.coursecompletion.requirement": "Requirement", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "View course report", - "addon.files.couldnotloadfiles": "The list of files could not be loaded.", - "addon.files.emptyfilelist": "There are no files to show.", - "addon.files.erroruploadnotworking": "Unfortunately it is currently not possible to upload files to your site.", - "addon.files.files": "Files", - "addon.files.privatefiles": "Private files", - "addon.files.sitefiles": "Site files", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Configure devices", - "addon.messages.acceptandaddcontact": "Accept and add to contacts", - "addon.messages.addcontact": "Add contact", - "addon.messages.addcontactconfirm": "Are you sure you want to add {{$a}} to your contacts?", - "addon.messages.addtofavourites": "Star conversation", - "addon.messages.addtoyourcontacts": "Add to contacts", - "addon.messages.blocknoncontacts": "Prevent non-contacts from messaging me", - "addon.messages.blockuser": "Block user", - "addon.messages.blockuserconfirm": "Are you sure you want to block {{$a}}?", - "addon.messages.contactableprivacy": "Accept messages from:", - "addon.messages.contactableprivacy_coursemember": "My contacts and anyone in my courses", - "addon.messages.contactableprivacy_onlycontacts": "My contacts only", - "addon.messages.contactableprivacy_site": "Anyone on the site", - "addon.messages.contactblocked": "Contact blocked", - "addon.messages.contactlistempty": "The contact list is empty", - "addon.messages.contactname": "Contact name", - "addon.messages.contactrequestsent": "Contact request sent", - "addon.messages.contacts": "Contacts", - "addon.messages.conversationactions": "Conversation actions menu", - "addon.messages.decline": "Decline", - "addon.messages.deleteallconfirm": "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.", - "addon.messages.deleteallselfconfirm": "Are you sure you would like to delete this entire personal conversation?", - "addon.messages.deleteconversation": "Delete conversation", - "addon.messages.deleteforeveryone": "Delete for me and for everyone else", - "addon.messages.deletemessage": "Delete message", - "addon.messages.deletemessageconfirmation": "Are you sure you want to delete this message? It will only be deleted from your messaging history and will still be viewable by the user who sent or received the message.", - "addon.messages.errordeletemessage": "Error while deleting the message.", - "addon.messages.errorwhileretrievingcontacts": "Error while retrieving contacts from the server.", - "addon.messages.errorwhileretrievingdiscussions": "Error while retrieving discussions from the server.", - "addon.messages.errorwhileretrievingmessages": "Error while retrieving messages from the server.", - "addon.messages.errorwhileretrievingusers": "Error while retrieving users from the server.", - "addon.messages.groupconversations": "Group", - "addon.messages.groupinfo": "Group info", - "addon.messages.individualconversations": "Private", - "addon.messages.info": "User info", - "addon.messages.isnotinyourcontacts": "{{$a}} is not in your contacts", - "addon.messages.message": "Message", - "addon.messages.messagenotsent": "The message was not sent. Please try again later.", - "addon.messages.messagepreferences": "Message preferences", - "addon.messages.messages": "Messages", - "addon.messages.muteconversation": "Mute", - "addon.messages.mutedconversation": "Muted conversation", - "addon.messages.newmessage": "New message", - "addon.messages.newmessages": "New messages", - "addon.messages.nocontactrequests": "No contact requests", - "addon.messages.nocontactsgetstarted": "No contacts", - "addon.messages.nofavourites": "No starred conversations", - "addon.messages.nogroupconversations": "No group conversations", - "addon.messages.noindividualconversations": "No private conversations", - "addon.messages.nomessagesfound": "No messages were found", - "addon.messages.noncontacts": "Non-contacts", - "addon.messages.nousersfound": "No users found", - "addon.messages.numparticipants": "{{$a}} participants", - "addon.messages.removecontact": "Remove contact", - "addon.messages.removecontactconfirm": "Are you sure you want to remove {{$a}} from your contacts?", - "addon.messages.removefromfavourites": "Unstar conversation", - "addon.messages.removefromyourcontacts": "Remove from contacts", - "addon.messages.requests": "Requests", - "addon.messages.requirecontacttomessage": "You need to request {{$a}} to add you as a contact to be able to message them.", - "addon.messages.searchcombined": "Search people and messages", - "addon.messages.selfconversation": "Personal space", - "addon.messages.selfconversationdefaultmessage": "Save draft messages, links, notes etc. to access later.", - "addon.messages.sendcontactrequest": "Send contact request", - "addon.messages.showdeletemessages": "Show delete messages", - "addon.messages.type_blocked": "Blocked", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Search results", - "addon.messages.type_strangers": "Others", - "addon.messages.unabletomessage": "You are unable to message this user", - "addon.messages.unblockuser": "Unblock user", - "addon.messages.unblockuserconfirm": "Are you sure you want to unblock {{$a}}?", - "addon.messages.unmuteconversation": "Unmute", - "addon.messages.useentertosend": "Use enter to send", - "addon.messages.useentertosenddescdesktop": "If disabled, you can use Ctrl+Enter to send the message.", - "addon.messages.useentertosenddescmac": "If disabled, you can use Cmd+Enter to send the message.", - "addon.messages.userwouldliketocontactyou": "{{$a}} would like to contact you", - "addon.messages.warningconversationmessagenotsent": "Couldn't send message(s) to conversation {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Couldn't send message(s) to user {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Would like to contact you", - "addon.messages.you": "You:", - "addon.messages.youhaveblockeduser": "You have blocked this user.", - "addon.messages.yourcontactrequestpending": "Your contact request is pending with {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Please accept the submission statement.", - "addon.mod_assign.addattempt": "Allow another attempt", - "addon.mod_assign.addnewattempt": "Add a new attempt", - "addon.mod_assign.addnewattemptfromprevious": "Add a new attempt based on previous submission", - "addon.mod_assign.addsubmission": "Add submission", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "The assignment details and submission form will be available from {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Allow submissions from", - "addon.mod_assign.allowsubmissionsfromdatesummary": "This assignment will accept submissions from {{$a}}", - "addon.mod_assign.applytoteam": "Apply grades and feedback to entire group", - "addon.mod_assign.assignmentisdue": "Assignment is due", - "addon.mod_assign.attemptnumber": "Attempt number", - "addon.mod_assign.attemptreopenmethod": "Attempts reopened", - "addon.mod_assign.attemptreopenmethod_manual": "Manually", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatically until pass", - "addon.mod_assign.attemptsettings": "Attempt settings", - "addon.mod_assign.cannoteditduetostatementsubmission": "You can't add or edit a submission in the app because the submission statement could not be retrieved from the site.", - "addon.mod_assign.cannotgradefromapp": "Certain grading methods are not yet supported by the app and cannot be modified.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "You can't make a submission in the app because the submission statement could not be retrieved from the site.", - "addon.mod_assign.confirmsubmission": "Are you sure you want to submit your work for grading? You will not be able to make any more changes.", - "addon.mod_assign.currentattempt": "This is attempt {{$a}}.", - "addon.mod_assign.currentattemptof": "This is attempt {{$a.attemptnumber}} ( {{$a.maxattempts}} attempts allowed ).", - "addon.mod_assign.currentgrade": "Current grade in gradebook", - "addon.mod_assign.cutoffdate": "Cut-off date", - "addon.mod_assign.defaultteam": "Default group", - "addon.mod_assign.duedate": "Due date", - "addon.mod_assign.duedateno": "No due date", - "addon.mod_assign.duedatereached": "The due date for this assignment has now passed", - "addon.mod_assign.editingstatus": "Editing status", - "addon.mod_assign.editsubmission": "Edit submission", - "addon.mod_assign.erroreditpluginsnotsupported": "You can't add or edit a submission in the app because certain plugins are not yet supported for editing.", - "addon.mod_assign.errorshowinginformation": "Submission information cannot be displayed.", - "addon.mod_assign.extensionduedate": "Extension due date", - "addon.mod_assign.feedbacknotsupported": "This feedback is not supported by the app and may not contain all the information.", - "addon.mod_assign.grade": "Grade", - "addon.mod_assign.graded": "Graded", - "addon.mod_assign.gradedby": "Graded by", - "addon.mod_assign.gradedfollowupsubmit": "Graded - follow up submission received", - "addon.mod_assign.gradedon": "Graded on", - "addon.mod_assign.gradelocked": "This grade is locked or overridden in the gradebook.", - "addon.mod_assign.gradenotsynced": "Grade not synced", - "addon.mod_assign.gradeoutof": "Grade out of {{$a}}", - "addon.mod_assign.gradingstatus": "Grading status", - "addon.mod_assign.groupsubmissionsettings": "Group submission settings", - "addon.mod_assign.hiddenuser": "Participant", - "addon.mod_assign.latesubmissions": "Late submissions", - "addon.mod_assign.latesubmissionsaccepted": "Allowed until {{$a}}", - "addon.mod_assign.markingworkflowstate": "Marking workflow state", - "addon.mod_assign.markingworkflowstateinmarking": "In marking", - "addon.mod_assign.markingworkflowstateinreview": "In review", - "addon.mod_assign.markingworkflowstatenotmarked": "Not marked", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Ready for release", - "addon.mod_assign.markingworkflowstatereadyforreview": "Marking completed", - "addon.mod_assign.markingworkflowstatereleased": "Released", - "addon.mod_assign.modulenameplural": "Assignments", - "addon.mod_assign.multipleteams": "Member of more than one group", - "addon.mod_assign.multipleteams_desc": "The assignment requires submission in groups. You are a member of more than one group. To be able to submit you must be a member of only one group. Please contact your teacher to change your group membership.", - "addon.mod_assign.noattempt": "No attempt", - "addon.mod_assign.nomoresubmissionsaccepted": "Only allowed for participants who have been granted an extension", - "addon.mod_assign.noonlinesubmissions": "This assignment does not require you to submit anything online", - "addon.mod_assign.nosubmission": "Nothing has been submitted for this assignment", - "addon.mod_assign.notallparticipantsareshown": "Participants who have not made a submission are not shown.", - "addon.mod_assign.noteam": "Not a member of any group", - "addon.mod_assign.noteam_desc": "This assignment requires submission in groups. You are not a member of any group, so you cannot create a submission. Please contact your teacher to be added to a group.", - "addon.mod_assign.notgraded": "Not graded", - "addon.mod_assign.numberofdraftsubmissions": "Drafts", - "addon.mod_assign.numberofparticipants": "Participants", - "addon.mod_assign.numberofsubmissionsneedgrading": "Needs grading", - "addon.mod_assign.numberofsubmittedassignments": "Submitted", - "addon.mod_assign.numberofteams": "Groups", - "addon.mod_assign.numwords": "{{$a}} words", - "addon.mod_assign.outof": "{{$a.current}} out of {{$a.total}}", - "addon.mod_assign.overdue": "Assignment is overdue by: {{$a}}", - "addon.mod_assign.submission": "Submission", - "addon.mod_assign.submissioneditable": "Student can edit this submission", - "addon.mod_assign.submissionnoteditable": "Student cannot edit this submission", - "addon.mod_assign.submissionnotsupported": "This submission is not supported by the app and may not contain all the information.", - "addon.mod_assign.submissionslocked": "This assignment is not accepting submissions", - "addon.mod_assign.submissionstatus": "Submission status", - "addon.mod_assign.submissionstatus_": "No submission", - "addon.mod_assign.submissionstatus_draft": "Draft (not submitted)", - "addon.mod_assign.submissionstatus_marked": "Graded", - "addon.mod_assign.submissionstatus_new": "No submission", - "addon.mod_assign.submissionstatus_reopened": "Reopened", - "addon.mod_assign.submissionstatus_submitted": "Submitted for grading", - "addon.mod_assign.submissionstatusheading": "Submission status", - "addon.mod_assign.submissionteam": "Group", - "addon.mod_assign.submitassignment": "Submit assignment", - "addon.mod_assign.submitassignment_help": "Once this assignment is submitted you will not be able to make any more changes.", - "addon.mod_assign.submittedearly": "Assignment was submitted {{$a}} early", - "addon.mod_assign.submittedlate": "Assignment was submitted {{$a}} late", - "addon.mod_assign.syncblockedusercomponent": "user grade", - "addon.mod_assign.timemodified": "Last modified", - "addon.mod_assign.timeremaining": "Time remaining", - "addon.mod_assign.ungroupedusers": "The setting 'Require group to make submission' is enabled and some users are either not a member of any group, or are a member of more than one group, so are unable to make submissions.", - "addon.mod_assign.ungroupedusersoptional": "The setting 'Students submit in groups' is enabled and some users are either not a member of any group, or are a member of more than one group. Please be aware that these students will submit as members of the 'Default group'.", - "addon.mod_assign.unlimitedattempts": "Unlimited", - "addon.mod_assign.userswhoneedtosubmit": "Users who need to submit: {{$a}}", - "addon.mod_assign.userwithid": "User with ID {{id}}", - "addon.mod_assign.viewsubmission": "View submission", - "addon.mod_assign.warningsubmissiongrademodified": "The submission grade was modified on the site.", - "addon.mod_assign.warningsubmissionmodified": "The user submission was modified on the site.", - "addon.mod_assign.wordlimit": "Word limit", - "addon.mod_assign_feedback_comments.pluginname": "Feedback comments", - "addon.mod_assign_feedback_editpdf.pluginname": "Annotate PDF", - "addon.mod_assign_feedback_file.pluginname": "File feedback", - "addon.mod_assign_submission_comments.pluginname": "Submission comments", - "addon.mod_assign_submission_file.pluginname": "File submissions", - "addon.mod_assign_submission_onlinetext.pluginname": "Online text submissions", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "The word limit for this assignment is {{$a.limit}} words and you are attempting to submit {{$a.count}} words. Please review your submission and try again.", - "addon.mod_book.errorchapter": "Error reading chapter of book.", - "addon.mod_book.modulenameplural": "Books", - "addon.mod_book.navnexttitle": "Next: {{$a}}", - "addon.mod_book.navprevtitle": "Previous: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Book chapters", - "addon.mod_book.toc": "Table of contents", - "addon.mod_chat.beep": "Beep", - "addon.mod_chat.chatreport": "Chat sessions", - "addon.mod_chat.currentusers": "Current users", - "addon.mod_chat.enterchat": "Click here to enter the chat now", - "addon.mod_chat.entermessage": "Enter your message", - "addon.mod_chat.errorwhileconnecting": "Error while connecting to the chat.", - "addon.mod_chat.errorwhilegettingchatdata": "Error while getting chat data.", - "addon.mod_chat.errorwhilegettingchatusers": "Error while getting chat users.", - "addon.mod_chat.errorwhileretrievingmessages": "Error while retrieving messages from the server.", - "addon.mod_chat.errorwhilesendingmessage": "Error while sending the message.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} beeps everyone!", - "addon.mod_chat.messagebeepsyou": "{{$a}} has just beeped you!", - "addon.mod_chat.messageenter": "{{$a}} has just entered this chat", - "addon.mod_chat.messageexit": "{{$a}} has left this chat", - "addon.mod_chat.messages": "Messages", - "addon.mod_chat.messageyoubeep": "You beeped {{$a}}", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "You must be online to send messages.", - "addon.mod_chat.nomessages": "No messages yet", - "addon.mod_chat.nosessionsfound": "No sessions found", - "addon.mod_chat.saidto": "said to", - "addon.mod_chat.send": "Send", - "addon.mod_chat.sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)", - "addon.mod_chat.showincompletesessions": "Show incomplete sessions", - "addon.mod_chat.talk": "Talk", - "addon.mod_chat.viewreport": "View past chat sessions", - "addon.mod_choice.cannotsubmit": "Sorry, there was a problem submitting your choice. Please try again.", - "addon.mod_choice.choiceoptions": "Choice options", - "addon.mod_choice.errorgetchoice": "Error getting choice data.", - "addon.mod_choice.expired": "This activity closed on {{$a}}.", - "addon.mod_choice.full": "(Full)", - "addon.mod_choice.limita": "Limit: {{$a}}", - "addon.mod_choice.modulenameplural": "Choices", - "addon.mod_choice.noresultsviewable": "The results are not currently viewable.", - "addon.mod_choice.notopenyet": "This activity is not available until {{$a}}.", - "addon.mod_choice.numberofuser": "Number of responses", - "addon.mod_choice.numberofuserinpercentage": "Percentage of responses", - "addon.mod_choice.previewonly": "This is just a preview of the available options for this activity. You will not be able to submit your choice until {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anonymous results will be published after you answer.", - "addon.mod_choice.publishinfoanonclose": "Anonymous results will be published after the activity is closed.", - "addon.mod_choice.publishinfofullafter": "Full results, showing everyone's choices, will be published after you answer.", - "addon.mod_choice.publishinfofullclose": "Full results, showing everyone's choices, will be published after the activity is closed.", - "addon.mod_choice.publishinfonever": "The results of this activity will not be published after you answer.", - "addon.mod_choice.removemychoice": "Remove my choice", - "addon.mod_choice.responses": "Responses", - "addon.mod_choice.responsesa": "Responses: {{$a}}", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% of the users chose the option: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Graph display", - "addon.mod_choice.resultsnotsynced": "Your last response must be synchronised before it is included in the results.", - "addon.mod_choice.savemychoice": "Save my choice", - "addon.mod_choice.userchoosethisoption": "Users who chose this option", - "addon.mod_choice.yourselection": "Your selection", - "addon.mod_data.addentries": "Add entries", - "addon.mod_data.advancedsearch": "Advanced search", - "addon.mod_data.alttext": "Alternative text", - "addon.mod_data.approve": "Approve", - "addon.mod_data.approved": "Approved", - "addon.mod_data.ascending": "Ascending", - "addon.mod_data.authorfirstname": "Author first name", - "addon.mod_data.authorlastname": "Author surname", - "addon.mod_data.confirmdeleterecord": "Are you sure you want to delete this entry?", - "addon.mod_data.descending": "Descending", - "addon.mod_data.disapprove": "Undo approval", - "addon.mod_data.edittagsnotsupported": "Sorry, editing tags is not supported by the app.", - "addon.mod_data.emptyaddform": "You did not fill out any fields!", - "addon.mod_data.entrieslefttoadd": "You must add {{$a.entriesleft}} more entry/entries in order to complete this activity", - "addon.mod_data.entrieslefttoaddtoview": "You must add {{$a.entrieslefttoview}} more entry/entries before you can view other participants' entries.", - "addon.mod_data.errorapproving": "Error approving or unapproving entry.", - "addon.mod_data.errordeleting": "Error deleting entry.", - "addon.mod_data.errormustsupplyvalue": "You must supply a value here.", - "addon.mod_data.expired": "Sorry, this activity closed on {{$a}} and is no longer available", - "addon.mod_data.fields": "Fields", - "addon.mod_data.foundrecords": "Found records: {{$a.num}}/{{$a.max}} (Reset filters)", - "addon.mod_data.gettinglocation": "Getting location", - "addon.mod_data.latlongboth": "Both latitude and longitude are required.", - "addon.mod_data.locationnotenabled": "Location is not enabled", - "addon.mod_data.locationpermissiondenied": "Permission to access your location has been denied.", - "addon.mod_data.menuchoose": "Choose...", - "addon.mod_data.modulenameplural": "Databases", - "addon.mod_data.more": "More", - "addon.mod_data.mylocation": "My location", - "addon.mod_data.nomatch": "No matching entries found!", - "addon.mod_data.norecords": "No entries in database", - "addon.mod_data.notapproved": "Entry is not approved yet.", - "addon.mod_data.notopenyet": "Sorry, this activity is not available until {{$a}}", - "addon.mod_data.numrecords": "{{$a}} entries", - "addon.mod_data.other": "Other", - "addon.mod_data.recordapproved": "Entry approved", - "addon.mod_data.recorddeleted": "Entry deleted", - "addon.mod_data.recorddisapproved": "Entry unapproved", - "addon.mod_data.resetsettings": "Reset filters", - "addon.mod_data.search": "Search", - "addon.mod_data.searchbytagsnotsupported": "Sorry, searching by tags is not supported by the app.", - "addon.mod_data.selectedrequired": "All selected required", - "addon.mod_data.single": "View single", - "addon.mod_data.tagarea_data_records": "Data records", - "addon.mod_data.timeadded": "Time added", - "addon.mod_data.timemodified": "Time modified", - "addon.mod_data.usedate": "Include in search.", - "addon.mod_feedback.analysis": "Analysis", - "addon.mod_feedback.anonymous": "Anonymous", - "addon.mod_feedback.anonymous_entries": "Anonymous entries ({{$a}})", - "addon.mod_feedback.average": "Average", - "addon.mod_feedback.captchaofflinewarning": "Feedback with CAPTCHA cannot be completed offline, or if not configured, or if the server is down.", - "addon.mod_feedback.complete_the_form": "Answer the questions", - "addon.mod_feedback.completed_feedbacks": "Submitted answers", - "addon.mod_feedback.continue_the_form": "Continue answering the questions", - "addon.mod_feedback.feedback_is_not_open": "The feedback is not open", - "addon.mod_feedback.feedback_submitted_offline": "This feedback has been saved to be submitted later.", - "addon.mod_feedback.feedbackclose": "Allow answers to", - "addon.mod_feedback.feedbackopen": "Allow answers from", - "addon.mod_feedback.mapcourses": "Map feedback to courses", - "addon.mod_feedback.maximal": "Maximum", - "addon.mod_feedback.minimal": "Minimum", - "addon.mod_feedback.mode": "Mode", - "addon.mod_feedback.modulenameplural": "Feedback", - "addon.mod_feedback.next_page": "Next page", - "addon.mod_feedback.non_anonymous": "User's name will be logged and shown with answers", - "addon.mod_feedback.non_anonymous_entries": "Non anonymous entries ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Non-respondent students ({{$a}})", - "addon.mod_feedback.not_selected": "Not selected", - "addon.mod_feedback.not_started": "Not started", - "addon.mod_feedback.numberoutofrange": "Number out of range", - "addon.mod_feedback.overview": "Overview", - "addon.mod_feedback.page_after_submit": "Completion message", - "addon.mod_feedback.preview": "Preview", - "addon.mod_feedback.previous_page": "Previous page", - "addon.mod_feedback.questions": "Questions", - "addon.mod_feedback.response_nr": "Response number", - "addon.mod_feedback.responses": "Responses", - "addon.mod_feedback.save_entries": "Submit your answers", - "addon.mod_feedback.show_entries": "Show responses", - "addon.mod_feedback.show_nonrespondents": "Show non-respondents", - "addon.mod_feedback.started": "Started", - "addon.mod_feedback.this_feedback_is_already_submitted": "You've already completed this activity.", - "addon.mod_folder.emptyfilelist": "There are no files to show.", - "addon.mod_folder.modulenameplural": "Folders", - "addon.mod_forum.addanewdiscussion": "Add a new discussion topic", - "addon.mod_forum.addanewquestion": "Add a new question", - "addon.mod_forum.addanewtopic": "Add a new topic", - "addon.mod_forum.addtofavourites": "Star this discussion", - "addon.mod_forum.advanced": "Advanced", - "addon.mod_forum.cannotadddiscussion": "Adding discussions to this forum requires group membership.", - "addon.mod_forum.cannotadddiscussionall": "You do not have permission to add a new discussion topic for all participants.", - "addon.mod_forum.cannotcreatediscussion": "Could not create new discussion", - "addon.mod_forum.couldnotadd": "Could not add your post due to an unknown error", - "addon.mod_forum.couldnotupdate": "Could not update your post due to an unknown error", - "addon.mod_forum.cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.", - "addon.mod_forum.delete": "Delete", - "addon.mod_forum.deletedpost": "The post has been deleted", - "addon.mod_forum.deletesure": "Are you sure you want to delete this post?", - "addon.mod_forum.discussion": "Discussion", - "addon.mod_forum.discussionlistsortbycreatedasc": "Sort by creation date in ascending order", - "addon.mod_forum.discussionlistsortbycreateddesc": "Sort by creation date in descending order", - "addon.mod_forum.discussionlistsortbylastpostasc": "Sort by last post creation date in ascending order", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Sort by last post creation date in descending order", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Sort by number of replies in ascending order", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Sort by number of replies in descending order", - "addon.mod_forum.discussionlocked": "This discussion has been locked so you can no longer reply to it.", - "addon.mod_forum.discussionpinned": "Pinned", - "addon.mod_forum.discussionsubscription": "Discussion subscription", - "addon.mod_forum.edit": "Edit", - "addon.mod_forum.erroremptymessage": "Post message cannot be empty", - "addon.mod_forum.erroremptysubject": "Post subject cannot be empty.", - "addon.mod_forum.errorgetforum": "Error getting forum data.", - "addon.mod_forum.errorgetgroups": "Error getting group settings.", - "addon.mod_forum.errorposttoallgroups": "Could not create new discussion in all groups.", - "addon.mod_forum.favouriteupdated": "Your star option has been updated.", - "addon.mod_forum.forumnodiscussionsyet": "There are no discussions yet in this forum.", - "addon.mod_forum.group": "Group", - "addon.mod_forum.lastpost": "Last post", - "addon.mod_forum.lockdiscussion": "Lock this discussion", - "addon.mod_forum.lockupdated": "The lock option has been updated.", - "addon.mod_forum.message": "Message", - "addon.mod_forum.modeflatnewestfirst": "Display replies flat, with newest first", - "addon.mod_forum.modeflatoldestfirst": "Display replies flat, with oldest first", - "addon.mod_forum.modenested": "Display replies in nested form", - "addon.mod_forum.modulenameplural": "Forums", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discussions", - "addon.mod_forum.numreplies": "{{numreplies}} replies", - "addon.mod_forum.pindiscussion": "Pin this discussion", - "addon.mod_forum.pinupdated": "The pin option has been updated.", - "addon.mod_forum.postisprivatereply": "This is a private reply. It is not visible to other participants.", - "addon.mod_forum.posttoforum": "Post to forum", - "addon.mod_forum.posttomygroups": "Post a copy to all groups", - "addon.mod_forum.privatereply": "Reply privately", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Refresh discussions", - "addon.mod_forum.refreshposts": "Refresh posts", - "addon.mod_forum.removefromfavourites": "Unstar this discussion", - "addon.mod_forum.reply": "Reply", - "addon.mod_forum.replyplaceholder": "Write your reply...", - "addon.mod_forum.subject": "Subject", - "addon.mod_forum.tagarea_forum_posts": "Forum posts", - "addon.mod_forum.thisforumhasduedate": "The due date for posting to this forum is {{$a}}.", - "addon.mod_forum.thisforumisdue": "The due date for posting to this forum was {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Unlock this discussion", - "addon.mod_forum.unpindiscussion": "Unpin this discussion", - "addon.mod_forum.unread": "Unread", - "addon.mod_forum.unreadpostsnumber": "{{$a}} unread posts", - "addon.mod_forum.yourreply": "Your reply", - "addon.mod_glossary.addentry": "Add a new entry", - "addon.mod_glossary.aliases": "Keyword(s)", - "addon.mod_glossary.attachment": "Attachment", - "addon.mod_glossary.browsemode": "Browse entries", - "addon.mod_glossary.byalphabet": "Alphabetically", - "addon.mod_glossary.byauthor": "Group by author", - "addon.mod_glossary.bycategory": "Group by category", - "addon.mod_glossary.bynewestfirst": "Newest first", - "addon.mod_glossary.byrecentlyupdated": "Recently updated", - "addon.mod_glossary.bysearch": "Search", - "addon.mod_glossary.cannoteditentry": "Cannot edit entry", - "addon.mod_glossary.casesensitive": "This entry is case sensitive", - "addon.mod_glossary.categories": "Categories", - "addon.mod_glossary.concept": "Concept", - "addon.mod_glossary.definition": "Definition", - "addon.mod_glossary.entriestobesynced": "Entries to be synced", - "addon.mod_glossary.entrypendingapproval": "This entry is pending approval.", - "addon.mod_glossary.entryusedynalink": "This entry should be automatically linked", - "addon.mod_glossary.errconceptalreadyexists": "This concept already exists. No duplicates allowed in this glossary.", - "addon.mod_glossary.errorloadingentries": "An error occurred while loading entries.", - "addon.mod_glossary.errorloadingentry": "An error occurred while loading the entry.", - "addon.mod_glossary.errorloadingglossary": "An error occurred while loading the glossary.", - "addon.mod_glossary.fillfields": "Concept and definition are mandatory fields.", - "addon.mod_glossary.fullmatch": "Match whole words only", - "addon.mod_glossary.linking": "Auto-linking", - "addon.mod_glossary.modulenameplural": "Glossaries", - "addon.mod_glossary.noentriesfound": "No entries were found.", - "addon.mod_glossary.searchquery": "Search query", - "addon.mod_glossary.tagarea_glossary_entries": "Glossary entries", - "addon.mod_h5pactivity.all_attempts": "All user attempts", - "addon.mod_h5pactivity.answer_checked": "Answer checked", - "addon.mod_h5pactivity.answer_correct": "Your answer is correct", - "addon.mod_h5pactivity.answer_fail": "Incorrect answer", - "addon.mod_h5pactivity.answer_incorrect": "Your answer is incorrect", - "addon.mod_h5pactivity.answer_pass": "Correct answer", - "addon.mod_h5pactivity.attempt": "Attempt", - "addon.mod_h5pactivity.attempt_completion_no": "This attempt is not marked as completed", - "addon.mod_h5pactivity.attempt_completion_yes": "This attempt is completed", - "addon.mod_h5pactivity.attempt_success_fail": "Fail", - "addon.mod_h5pactivity.attempt_success_pass": "Pass", - "addon.mod_h5pactivity.attempt_success_unknown": "Not reported", - "addon.mod_h5pactivity.attempts_none": "This user has no attempts to display.", - "addon.mod_h5pactivity.completion": "Completion", - "addon.mod_h5pactivity.downloadh5pfile": "Download H5P file", - "addon.mod_h5pactivity.duration": "Duration", - "addon.mod_h5pactivity.errorgetactivity": "Error getting H5P activity data.", - "addon.mod_h5pactivity.filestatenotdownloaded": "The H5P package is not downloaded. You need to download it to be able to use it.", - "addon.mod_h5pactivity.filestateoutdated": "The H5P package has been modified since the last download. You need to download it again to be able to use it.", - "addon.mod_h5pactivity.maxscore": "Max score", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "My attempts", - "addon.mod_h5pactivity.no_compatible_track": "This interaction ({{$a}}) does not provide tracking information or the tracking\n provided is not compatible with the current activity version.", - "addon.mod_h5pactivity.offlinedisabledwarning": "You need to be online to view the H5P package.", - "addon.mod_h5pactivity.outcome": "Outcome", - "addon.mod_h5pactivity.previewmode": "This content is displayed in preview mode. No attempt tracking will be stored.", - "addon.mod_h5pactivity.result_fill-in": "Fill-in text", - "addon.mod_h5pactivity.result_other": "Unknown interaction type", - "addon.mod_h5pactivity.review_my_attempts": "View my attempts", - "addon.mod_h5pactivity.score": "Score", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} out of {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Start date", - "addon.mod_h5pactivity.totalscore": "Total score", - "addon.mod_h5pactivity.viewattempt": "View attempt {{$a}}", - "addon.mod_imscp.deploymenterror": "Content package error!", - "addon.mod_imscp.modulenameplural": "IMS content packages", - "addon.mod_imscp.showmoduledescription": "Show description", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "Answer", - "addon.mod_lesson.attempt": "Attempt: {{$a}}", - "addon.mod_lesson.attemptheader": "Attempt", - "addon.mod_lesson.attemptsremaining": "You have {{$a}} attempt(s) remaining", - "addon.mod_lesson.averagescore": "Average score", - "addon.mod_lesson.averagetime": "Average time", - "addon.mod_lesson.branchtable": "Content", - "addon.mod_lesson.cannotfindattempt": "Error: could not find attempt", - "addon.mod_lesson.cannotfinduser": "Error: could not find users", - "addon.mod_lesson.clusterjump": "Unseen question within a cluster", - "addon.mod_lesson.completed": "Completed", - "addon.mod_lesson.congratulations": "Congratulations - end of lesson reached", - "addon.mod_lesson.continue": "Continue", - "addon.mod_lesson.continuetonextpage": "Continue to next page.", - "addon.mod_lesson.defaultessayresponse": "Your essay will be graded by your teacher.", - "addon.mod_lesson.detailedstats": "Detailed statistics", - "addon.mod_lesson.didnotanswerquestion": "Did not answer this question.", - "addon.mod_lesson.displayofgrade": "Display of grade (for students only)", - "addon.mod_lesson.displayscorewithessays": "

          You earned {{$a.score}} out of {{$a.tempmaxgrade}} for the automatically graded questions.

          \n

          Your {{$a.essayquestions}} essay question(s) will be graded and added into your final score at a later date.

          \n

          Your current grade without the essay question(s) is {{$a.score}} out of {{$a.grade}}.

          ", - "addon.mod_lesson.displayscorewithoutessays": "Your score is {{$a.score}} (out of {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Password cannot be empty", - "addon.mod_lesson.enterpassword": "Please enter the password:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "You did not answer any questions. You have received a 0 for this lesson.", - "addon.mod_lesson.errorprefetchrandombranch": "This lesson contains a jump to a random content page. It can't be attempted in the app until it has been started in a web browser.", - "addon.mod_lesson.errorreviewretakenotlast": "This attempt can no longer be reviewed because another attempt has been finished.", - "addon.mod_lesson.finish": "Finish", - "addon.mod_lesson.finishretakeoffline": "This attempt was finished offline.", - "addon.mod_lesson.firstwrong": "You have answered incorrectly. Would you like to attempt the question again? (If you now answer the question correctly, it will not count towards your final score.)", - "addon.mod_lesson.gotoendoflesson": "Go to the end of the lesson", - "addon.mod_lesson.grade": "Grade", - "addon.mod_lesson.highscore": "High score", - "addon.mod_lesson.hightime": "High time", - "addon.mod_lesson.leftduringtimed": "You have left during a timed lesson.
          Please click on Continue to restart the lesson.", - "addon.mod_lesson.leftduringtimednoretake": "You have left during a timed lesson and you are
          not allowed to retake or continue the lesson.", - "addon.mod_lesson.lessonmenu": "Lesson menu", - "addon.mod_lesson.lessonstats": "Lesson statistics", - "addon.mod_lesson.linkedmedia": "Linked media", - "addon.mod_lesson.loginfail": "Login failed, please try again...", - "addon.mod_lesson.lowscore": "Low score", - "addon.mod_lesson.lowtime": "Low time", - "addon.mod_lesson.maximumnumberofattemptsreached": "Maximum number of attempts reached - Moving to next page", - "addon.mod_lesson.modattemptsnoteacher": "Student review only works for students.", - "addon.mod_lesson.modulenameplural": "Lessons", - "addon.mod_lesson.noanswer": "One or more questions have no answer given. Please go back and submit an answer.", - "addon.mod_lesson.nolessonattempts": "No attempts have been made on this lesson.", - "addon.mod_lesson.nolessonattemptsgroup": "No attempts have been made by {{$a}} group members on this lesson.", - "addon.mod_lesson.notcompleted": "Not completed", - "addon.mod_lesson.numberofcorrectanswers": "Number of correct answers: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Number of questions answered: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Number of questions answered: {{$a.nquestions}} (You should answer at least {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "You have earned {{$a.score}} point(s) out of {{$a.currenthigh}} point(s) thus far.", - "addon.mod_lesson.ongoingnormal": "You have answered {{$a.correct}} correctly out of {{$a.viewed}} attempts.", - "addon.mod_lesson.or": "OR", - "addon.mod_lesson.overview": "Overview", - "addon.mod_lesson.preview": "Preview", - "addon.mod_lesson.progressbarteacherwarning2": "You will not see the progress bar because you can edit this lesson", - "addon.mod_lesson.progresscompleted": "You have completed {{$a}}% of the lesson", - "addon.mod_lesson.question": "Question", - "addon.mod_lesson.rawgrade": "Raw grade", - "addon.mod_lesson.reports": "Reports", - "addon.mod_lesson.response": "Response", - "addon.mod_lesson.retakefinishedinsync": "An offline attempt was synchronised. Do you want to review it?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Review", - "addon.mod_lesson.reviewlesson": "Review lesson", - "addon.mod_lesson.reviewquestionback": "Yes, I'd like to try again", - "addon.mod_lesson.reviewquestioncontinue": "No, I just want to go on to the next question", - "addon.mod_lesson.secondpluswrong": "Not quite. Would you like to try again?", - "addon.mod_lesson.submit": "Submit", - "addon.mod_lesson.teacherjumpwarning": "A {{$a.cluster}} jump or an {{$a.unseen}} jump is being used in this lesson. The next page jump will be used instead. Log in as a student to test these jumps.", - "addon.mod_lesson.teacherongoingwarning": "The ongoing score is only displayed for the student. Log in as a student to test the ongoing score.", - "addon.mod_lesson.teachertimerwarning": "Timer only works for students. Test the timer by logging in as a student.", - "addon.mod_lesson.thatsthecorrectanswer": "That's the correct answer", - "addon.mod_lesson.thatsthewronganswer": "That's the wrong answer", - "addon.mod_lesson.timeremaining": "Time remaining", - "addon.mod_lesson.timetaken": "Time taken", - "addon.mod_lesson.unseenpageinbranch": "Unseen question within a content page", - "addon.mod_lesson.warningretakefinished": "The attempt was finished on the site.", - "addon.mod_lesson.welldone": "Well done!", - "addon.mod_lesson.youhaveseen": "You have seen more than one page of this lesson already.
          Do you want to start at the last page you saw?", - "addon.mod_lesson.youranswer": "Your answer", - "addon.mod_lesson.yourcurrentgradeisoutof": "Your current grade is {{$a.grade}} out of {{$a.total}}", - "addon.mod_lesson.youshouldview": "You should answer at least: {{$a}}", - "addon.mod_lti.errorgetlti": "Error getting module data.", - "addon.mod_lti.errorinvalidlaunchurl": "The launch URL is not valid.", - "addon.mod_lti.launchactivity": "Launch the activity", - "addon.mod_lti.modulenameplural": "External tools", - "addon.mod_page.errorwhileloadingthepage": "Error while loading the page content.", - "addon.mod_page.modulenameplural": "Pages", - "addon.mod_quiz.answercolon": "Answer:", - "addon.mod_quiz.attemptfirst": "First attempt", - "addon.mod_quiz.attemptlast": "Last attempt", - "addon.mod_quiz.attemptnumber": "Attempt", - "addon.mod_quiz.attemptquiznow": "Attempt quiz now", - "addon.mod_quiz.attemptstate": "State", - "addon.mod_quiz.canattemptbutnotsubmit": "You can attempt this quiz in the app, but you will need to submit the attempt in browser for the following reasons:", - "addon.mod_quiz.cannotsubmitquizdueto": "This quiz attempt cannot be submitted for the following reasons:", - "addon.mod_quiz.clearchoice": "Clear my choice", - "addon.mod_quiz.comment": "Comment", - "addon.mod_quiz.completedon": "Completed on", - "addon.mod_quiz.confirmclose": "Once you submit, you will no longer be able to change your answers for this attempt.", - "addon.mod_quiz.confirmcontinueoffline": "This attempt has not been synchronised since {{$a}}. If you have continued this attempt in another device since then, you may lose data.", - "addon.mod_quiz.confirmleavequizonerror": "An error occurred while saving the answers. Are you sure you want to leave the quiz?", - "addon.mod_quiz.confirmstart": "Your attempt will have a time limit of {{$a}}. When you start, the timer will begin to count down and cannot be paused. You must finish your attempt before it expires. Are you sure you wish to start now?", - "addon.mod_quiz.confirmstartheader": "Time limit", - "addon.mod_quiz.connectionerror": "Network connection lost. (Autosave failed).\n\nMake a note of any responses entered on this page in the last few minutes, then try to re-connect.\n\nOnce connection has been re-established, your responses should be saved and this message will disappear.", - "addon.mod_quiz.continueattemptquiz": "Continue the last attempt", - "addon.mod_quiz.continuepreview": "Continue the last preview", - "addon.mod_quiz.errorbehaviournotsupported": "This quiz can't be attempted in the app because the question behaviour is not supported by the app:", - "addon.mod_quiz.errordownloading": "Error downloading required data.", - "addon.mod_quiz.errorgetattempt": "Error getting attempt data.", - "addon.mod_quiz.errorgetquestions": "Error getting questions.", - "addon.mod_quiz.errorgetquiz": "Error getting quiz data.", - "addon.mod_quiz.errorparsequestions": "An error occurred while reading the questions. Please attempt this quiz in a web browser.", - "addon.mod_quiz.errorquestionsnotsupported": "This quiz can't be attempted in the app because it only contains questions not supported by the app:", - "addon.mod_quiz.errorrulesnotsupported": "This quiz can't be attempted in the app because it has access rules not supported by the app:", - "addon.mod_quiz.errorsaveattempt": "An error occurred while saving the attempt data.", - "addon.mod_quiz.feedback": "Feedback", - "addon.mod_quiz.finishattemptdots": "Finish attempt...", - "addon.mod_quiz.finishnotsynced": "Finished but not synchronised", - "addon.mod_quiz.grade": "Grade", - "addon.mod_quiz.gradeaverage": "Average grade", - "addon.mod_quiz.gradehighest": "Highest grade", - "addon.mod_quiz.grademethod": "Grading method", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Marks", - "addon.mod_quiz.modulenameplural": "Quizzes", - "addon.mod_quiz.mustbesubmittedby": "This attempt must be submitted by {{$a}}.", - "addon.mod_quiz.noquestions": "No questions have been added yet", - "addon.mod_quiz.noreviewattempt": "You are not allowed to review this attempt.", - "addon.mod_quiz.notyetgraded": "Not yet graded", - "addon.mod_quiz.opentoc": "Open navigation popover", - "addon.mod_quiz.outof": "{{$a.grade}} out of {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} out of {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Overall feedback", - "addon.mod_quiz.overdue": "Overdue", - "addon.mod_quiz.overduemustbesubmittedby": "This attempt is now overdue. It should already have been submitted. If you would like this quiz to be graded, you must submit it by {{$a}}. If you do not submit it by then, no marks from this attempt will be counted.", - "addon.mod_quiz.preview": "Preview", - "addon.mod_quiz.previewquiznow": "Preview quiz now", - "addon.mod_quiz.question": "Question", - "addon.mod_quiz.quiznavigation": "Quiz navigation", - "addon.mod_quiz.quizpassword": "Quiz password", - "addon.mod_quiz.reattemptquiz": "Re-attempt quiz", - "addon.mod_quiz.requirepasswordmessage": "To attempt this quiz you need to know the quiz password", - "addon.mod_quiz.returnattempt": "Return to attempt", - "addon.mod_quiz.review": "Review", - "addon.mod_quiz.reviewofattempt": "Review of attempt {{$a}}", - "addon.mod_quiz.reviewofpreview": "Review of preview", - "addon.mod_quiz.showall": "Show all questions on one page", - "addon.mod_quiz.showeachpage": "Show one page at a time", - "addon.mod_quiz.startattempt": "Start attempt", - "addon.mod_quiz.startedon": "Started on", - "addon.mod_quiz.stateabandoned": "Never submitted", - "addon.mod_quiz.statefinished": "Finished", - "addon.mod_quiz.statefinisheddetails": "Submitted {{$a}}", - "addon.mod_quiz.stateinprogress": "In progress", - "addon.mod_quiz.stateoverdue": "Overdue", - "addon.mod_quiz.stateoverduedetails": "Must be submitted by {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Submit all and finish", - "addon.mod_quiz.summaryofattempt": "Summary of attempt", - "addon.mod_quiz.summaryofattempts": "Summary of your previous attempts", - "addon.mod_quiz.timeleft": "Time left", - "addon.mod_quiz.timetaken": "Time taken", - "addon.mod_quiz.warningattemptfinished": "Offline attempt discarded as it was finished on the site or not found.", - "addon.mod_quiz.warningdatadiscarded": "Some offline answers were discarded because the questions were modified online.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Attempt unfinished because some offline answers were discarded. Please review your answers then resubmit the attempt.", - "addon.mod_quiz.warningquestionsnotsupported": "This quiz contains questions not supported by the app:", - "addon.mod_quiz.yourfinalgradeis": "Your final grade for this quiz is {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Error while loading the content.", - "addon.mod_resource.modifieddate": "Modified {{$a}}", - "addon.mod_resource.modulenameplural": "Files", - "addon.mod_resource.openthefile": "Open the file", - "addon.mod_resource.uploadeddate": "Uploaded {{$a}}", - "addon.mod_scorm.asset": "Asset", - "addon.mod_scorm.assetlaunched": "Asset - Viewed", - "addon.mod_scorm.attempts": "Attempts", - "addon.mod_scorm.averageattempt": "Average attempts", - "addon.mod_scorm.browse": "Preview", - "addon.mod_scorm.browsed": "Browsed", - "addon.mod_scorm.browsemode": "Preview mode", - "addon.mod_scorm.cannotcalculategrade": "Grade couldn't be calculated.", - "addon.mod_scorm.completed": "Completed", - "addon.mod_scorm.contents": "Contents", - "addon.mod_scorm.dataattemptshown": "This data belongs to the attempt number {{number}}.", - "addon.mod_scorm.enter": "Enter", - "addon.mod_scorm.errorcreateofflineattempt": "An error occurred while creating a new offline attempt. Please try again.", - "addon.mod_scorm.errordownloadscorm": "Error downloading SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Error getting SCORM data.", - "addon.mod_scorm.errorinvalidversion": "Sorry, the application only supports SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "The download of SCORM packages is disabled. Please contact your site administrator.", - "addon.mod_scorm.errornovalidsco": "This SCORM package doesn't have a visible SCO to load.", - "addon.mod_scorm.errorpackagefile": "Sorry, the application only supports ZIP packages.", - "addon.mod_scorm.errorsyncscorm": "An error occurred while synchronising. Please try again.", - "addon.mod_scorm.exceededmaxattempts": "You have reached the maximum number of attempts.", - "addon.mod_scorm.failed": "Failed", - "addon.mod_scorm.firstattempt": "First attempt", - "addon.mod_scorm.gradeaverage": "Average grade", - "addon.mod_scorm.gradeforattempt": "Grade for attempt", - "addon.mod_scorm.gradehighest": "Highest grade", - "addon.mod_scorm.grademethod": "Grading method", - "addon.mod_scorm.gradereported": "Grade reported", - "addon.mod_scorm.gradescoes": "Learning objects", - "addon.mod_scorm.gradesum": "Sum grade", - "addon.mod_scorm.highestattempt": "Highest attempt", - "addon.mod_scorm.incomplete": "Incomplete", - "addon.mod_scorm.lastattempt": "Last completed attempt", - "addon.mod_scorm.modulenameplural": "SCORM packages", - "addon.mod_scorm.newattempt": "Start a new attempt", - "addon.mod_scorm.noattemptsallowed": "Number of attempts allowed", - "addon.mod_scorm.noattemptsmade": "Number of attempts you have made", - "addon.mod_scorm.notattempted": "Not attempted", - "addon.mod_scorm.offlineattemptnote": "This attempt has data that hasn't been synchronised.", - "addon.mod_scorm.offlineattemptovermax": "This attempt cannot be sent because you exceeded the maximum number of attempts.", - "addon.mod_scorm.organizations": "Organisations", - "addon.mod_scorm.passed": "Passed", - "addon.mod_scorm.reviewmode": "Review mode", - "addon.mod_scorm.score": "Score", - "addon.mod_scorm.scormstatusnotdownloaded": "This SCORM package is not downloaded. It will be automatically downloaded when you open it.", - "addon.mod_scorm.scormstatusoutdated": "This SCORM package has been modified since the last download. It will be automatically downloaded when you open it.", - "addon.mod_scorm.suspended": "Suspended", - "addon.mod_scorm.toc": "TOC", - "addon.mod_scorm.warningofflinedatadeleted": "Some offline data from attempt {{number}} has been discarded because it couldn't be counted as a new attempt.", - "addon.mod_scorm.warningsynconlineincomplete": "Some attempts couldn't be synchronised with the site because the last online attempt is not yet finished. Please finish the online attempt first.", - "addon.mod_survey.cannotsubmitsurvey": "Sorry, there was a problem submitting your survey. Please try again.", - "addon.mod_survey.errorgetsurvey": "Error getting survey data.", - "addon.mod_survey.ifoundthat": "I found that", - "addon.mod_survey.ipreferthat": "I prefer that", - "addon.mod_survey.modulenameplural": "Surveys", - "addon.mod_survey.responses": "Responses", - "addon.mod_survey.results": "Results", - "addon.mod_survey.surveycompletednograph": "You have completed this survey.", - "addon.mod_url.accessurl": "Access the URL", - "addon.mod_url.modulenameplural": "URLs", - "addon.mod_url.pointingtourl": "URL that the resource points to.", - "addon.mod_wiki.cannoteditpage": "You can not edit this page.", - "addon.mod_wiki.createpage": "Create page", - "addon.mod_wiki.editingpage": "Editing this page '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "An error occurred while loading the page.", - "addon.mod_wiki.errornowikiavailable": "This wiki does not have any content yet.", - "addon.mod_wiki.gowikihome": "Go to the wiki first page", - "addon.mod_wiki.map": "Map", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "New page", - "addon.mod_wiki.newpagetitle": "New page title", - "addon.mod_wiki.nocontent": "There is no content for this page", - "addon.mod_wiki.notingroup": "Not in group", - "addon.mod_wiki.pageexists": "This page already exists.", - "addon.mod_wiki.pagename": "Page name", - "addon.mod_wiki.subwiki": "Sub-wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki pages", - "addon.mod_wiki.titleshouldnotbeempty": "The title should not be empty", - "addon.mod_wiki.viewpage": "View page", - "addon.mod_wiki.wikipage": "Wiki page", - "addon.mod_wiki.wrongversionlock": "Another user has edited this page while you were editing and your content is obsolete.", - "addon.mod_workshop.alreadygraded": "Already graded", - "addon.mod_workshop.areainstructauthors": "Instructions for submission", - "addon.mod_workshop.areainstructreviewers": "Instructions for assessment", - "addon.mod_workshop.assess": "Assess", - "addon.mod_workshop.assessedsubmission": "Assessed submission", - "addon.mod_workshop.assessmentform": "Assessment form", - "addon.mod_workshop.assessmentsettings": "Assessment settings", - "addon.mod_workshop.assessmentstrategynotsupported": "Assessment strategy {{$a}} not supported", - "addon.mod_workshop.assessmentweight": "Assessment weight", - "addon.mod_workshop.assignedassessments": "Assigned submissions to assess", - "addon.mod_workshop.assignedassessmentsnone": "You have no assigned submission to assess", - "addon.mod_workshop.conclusion": "Conclusion", - "addon.mod_workshop.createsubmission": "Add submission", - "addon.mod_workshop.deletesubmission": "Delete submission", - "addon.mod_workshop.editsubmission": "Edit submission", - "addon.mod_workshop.feedbackauthor": "Feedback for the author", - "addon.mod_workshop.feedbackby": "Feedback by {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Feedback for the reviewer", - "addon.mod_workshop.givengrades": "Grades given", - "addon.mod_workshop.gradecalculated": "Calculated grade for submission", - "addon.mod_workshop.gradeinfo": "Grade: {{$a.received}} of {{$a.max}}", - "addon.mod_workshop.gradeover": "Override grade for submission", - "addon.mod_workshop.gradesreport": "Workshop grades report", - "addon.mod_workshop.gradinggrade": "Grade for assessment", - "addon.mod_workshop.gradinggradecalculated": "Calculated grade for assessment", - "addon.mod_workshop.gradinggradeof": "Grade for assessment (of {{$a}})", - "addon.mod_workshop.gradinggradeover": "Override grade for assessment", - "addon.mod_workshop.modulenameplural": "Workshops", - "addon.mod_workshop.nogradeyet": "No grade yet", - "addon.mod_workshop.notassessed": "Not assessed yet", - "addon.mod_workshop.notoverridden": "Not overridden", - "addon.mod_workshop.noyoursubmission": "You have not submitted your work yet", - "addon.mod_workshop.overallfeedback": "Overall feedback", - "addon.mod_workshop.publishedsubmissions": "Published submissions", - "addon.mod_workshop.publishsubmission": "Publish submission", - "addon.mod_workshop.publishsubmission_help": "Published submissions are available to the others when the workshop is closed.", - "addon.mod_workshop.reassess": "Re-assess", - "addon.mod_workshop.receivedgrades": "Grades received", - "addon.mod_workshop.submissionattachment": "Attachment", - "addon.mod_workshop.submissioncontent": "Submission content", - "addon.mod_workshop.submissiondeleteconfirm": "Are you sure you want to delete the following submission?", - "addon.mod_workshop.submissiongrade": "Grade for submission", - "addon.mod_workshop.submissiongradeof": "Grade for submission (of {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "You need to enter some text or add a file.", - "addon.mod_workshop.submissionrequiredtitle": "You need to enter a title.", - "addon.mod_workshop.submissionsreport": "Workshop submissions report", - "addon.mod_workshop.submissiontitle": "Title", - "addon.mod_workshop.switchphase10": "Switch to the setup phase", - "addon.mod_workshop.switchphase20": "Switch to the submission phase", - "addon.mod_workshop.switchphase30": "Switch to the assessment phase", - "addon.mod_workshop.switchphase40": "Switch to the evaluation phase", - "addon.mod_workshop.switchphase50": "Close workshop", - "addon.mod_workshop.userplan": "Workshop planner", - "addon.mod_workshop.userplancurrentphase": "Current phase", - "addon.mod_workshop.warningassessmentmodified": "The submission was modified on the site.", - "addon.mod_workshop.warningsubmissionmodified": "The assessment was modified on the site.", - "addon.mod_workshop.weightinfo": "Weight: {{$a}}", - "addon.mod_workshop.yourassessment": "Your assessment", - "addon.mod_workshop.yourassessmentfor": "Your assessment for {{$a}}", - "addon.mod_workshop.yourgrades": "Your grades", - "addon.mod_workshop.yoursubmission": "Your submission", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Comment for {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Grade for {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspect {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "You have to select a grade for this aspect", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Comment for {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspect {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Comment for {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Grade for {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Assertion {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Criterion {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "You have to select one of these items", - "addon.notes.addnewnote": "Add a new note", - "addon.notes.coursenotes": "Course notes", - "addon.notes.deleteconfirm": "Delete this note?", - "addon.notes.eventnotecreated": "Note created", - "addon.notes.eventnotedeleted": "Note deleted", - "addon.notes.nonotes": "There are no notes of this type yet", - "addon.notes.note": "Note", - "addon.notes.notes": "Notes", - "addon.notes.personalnotes": "Personal notes", - "addon.notes.publishstate": "Context", - "addon.notes.sitenotes": "Site notes", - "addon.notes.userwithid": "User with ID {{id}}", - "addon.notes.warningnotenotsent": "Couldn't add note(s) to course {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Error getting notifications.", - "addon.notifications.markallread": "Mark all as read", - "addon.notifications.notificationpreferences": "Notification preferences", - "addon.notifications.notifications": "Notifications", - "addon.notifications.playsound": "Play sound", - "addon.notifications.therearentnotificationsyet": "There are no notifications.", - "addon.storagemanager.deletecourse": "Offload all course data", - "addon.storagemanager.deletecourses": "Offload all courses data", - "addon.storagemanager.deletedatafrom": "Offload data from {{name}}", - "addon.storagemanager.info": "Files stored on your device make the app work faster and enable the app to be used offline. You can safely offload files if you need to free up storage space.", - "addon.storagemanager.managestorage": "Manage storage", - "addon.storagemanager.storageused": "File storage used:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "United Arab Emirates", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua and Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarctica", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "American Samoa", - "assets.countries.AT": "Austria", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland Islands", - "assets.countries.AZ": "Azerbaijan", - "assets.countries.BA": "Bosnia and Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgium", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei Darussalam", - "assets.countries.BO": "Bolivia (Plurinational State of)", - "assets.countries.BQ": "Bonaire, Sint Eustatius and Saba", - "assets.countries.BR": "Brazil", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvet Island", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Belarus", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canada", - "assets.countries.CC": "Cocos (Keeling) Islands", - "assets.countries.CD": "Congo (the Democratic Republic of the)", - "assets.countries.CF": "Central African Republic", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Switzerland", - "assets.countries.CI": "Côte d'Ivoire", - "assets.countries.CK": "Cook Islands", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Cameroon", - "assets.countries.CN": "China", - "assets.countries.CO": "Colombia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Cabo Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Christmas Island", - "assets.countries.CY": "Cyprus", - "assets.countries.CZ": "Czechia", - "assets.countries.DE": "Germany", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Denmark", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Dominican Republic", - "assets.countries.DZ": "Algeria", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estonia", - "assets.countries.EG": "Egypt", - "assets.countries.EH": "Western Sahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spain", - "assets.countries.ET": "Ethiopia", - "assets.countries.FI": "Finland", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Falkland Islands (Malvinas)", - "assets.countries.FM": "Micronesia (Federated States of)", - "assets.countries.FO": "Faroe Islands", - "assets.countries.FR": "France", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "United Kingdom", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "French Guiana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Greenland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Equatorial Guinea", - "assets.countries.GR": "Greece", - "assets.countries.GS": "South Georgia and the South Sandwich Islands", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Heard Island and McDonald Islands", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Croatia", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Hungary", - "assets.countries.ID": "Indonesia", - "assets.countries.IE": "Ireland", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Isle of Man", - "assets.countries.IN": "India", - "assets.countries.IO": "British Indian Ocean Territory", - "assets.countries.IQ": "Iraq", - "assets.countries.IR": "Iran (Islamic Republic of)", - "assets.countries.IS": "Iceland", - "assets.countries.IT": "Italy", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordan", - "assets.countries.JP": "Japan", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kyrgyzstan", - "assets.countries.KH": "Cambodia", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comoros", - "assets.countries.KN": "Saint Kitts and Nevis", - "assets.countries.KP": "Korea (the Democratic People's Republic of)", - "assets.countries.KR": "Korea (the Republic of)", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Cayman Islands", - "assets.countries.KZ": "Kazakhstan", - "assets.countries.LA": "Lao People's Democratic Republic", - "assets.countries.LB": "Lebanon", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lithuania", - "assets.countries.LU": "Luxembourg", - "assets.countries.LV": "Latvia", - "assets.countries.LY": "Libya", - "assets.countries.MA": "Morocco", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldova (the Republic of)", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin (French part)", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Marshall Islands", - "assets.countries.MK": "North Macedonia", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Northern Mariana Islands", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Maldives", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexico", - "assets.countries.MY": "Malaysia", - "assets.countries.MZ": "Mozambique", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "New Caledonia", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolk Island", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Netherlands", - "assets.countries.NO": "Norway", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "New Zealand", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "French Polynesia", - "assets.countries.PG": "Papua New Guinea", - "assets.countries.PH": "Philippines", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Poland", - "assets.countries.PM": "Saint Pierre and Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestine, State of", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Romania", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Russian Federation", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Saudi Arabia", - "assets.countries.SB": "Solomon Islands", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Sweden", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "Saint Helena, Ascension and Tristan da Cunha", - "assets.countries.SI": "Slovenia", - "assets.countries.SJ": "Svalbard and Jan Mayen", - "assets.countries.SK": "Slovakia", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Suriname", - "assets.countries.SS": "South Sudan", - "assets.countries.ST": "Sao Tome and Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (Dutch part)", - "assets.countries.SY": "Syrian Arab Republic", - "assets.countries.SZ": "Eswatini", - "assets.countries.TC": "Turks and Caicos Islands", - "assets.countries.TD": "Chad", - "assets.countries.TF": "French Southern Territories", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailand", - "assets.countries.TJ": "Tajikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunisia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turkey", - "assets.countries.TT": "Trinidad and Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzania, the United Republic of", - "assets.countries.UA": "Ukraine", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "United States", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Holy See", - "assets.countries.VC": "Saint Vincent and the Grenadines", - "assets.countries.VE": "Venezuela (Bolivarian Republic of)", - "assets.countries.VG": "Virgin Islands (British)", - "assets.countries.VI": "Virgin Islands (U.S.)", - "assets.countries.VN": "Viet Nam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis and Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "South Africa", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB ebook", - "assets.mimetypes.application/msword": "Word document", - "assets.mimetypes.application/pdf": "PDF document", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle backup", - "assets.mimetypes.application/vnd.ms-excel": "Excel spreadsheet", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 macro-enabled workbook", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint presentation", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument Spreadsheet", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument Spreadsheet template", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument Text document", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument Text template", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument Web page template", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 presentation", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 slideshow", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 spreadsheet", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 template", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 document", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote presentation", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers spreadsheet", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages document", - "assets.mimetypes.application/x-javascript": "JavaScript source", - "assets.mimetypes.application/x-mspublisher": "Publisher document", - "assets.mimetypes.application/x-shockwave-flash": "Flash animation", - "assets.mimetypes.application/xhtml_xml": "XHTML document", - "assets.mimetypes.archive": "Archive ({{$a.EXT}})", - "assets.mimetypes.audio": "Audio file ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "File", - "assets.mimetypes.group:archive": "Archive files", - "assets.mimetypes.group:audio": "Audio files", - "assets.mimetypes.group:document": "Document files", - "assets.mimetypes.group:html_audio": "Audio files natively supported by browsers", - "assets.mimetypes.group:html_track": "HTML track files", - "assets.mimetypes.group:html_video": "Video files natively supported by browsers", - "assets.mimetypes.group:image": "Image files", - "assets.mimetypes.group:presentation": "Presentation files", - "assets.mimetypes.group:sourcecode": "Source code", - "assets.mimetypes.group:spreadsheet": "Spreadsheet files", - "assets.mimetypes.group:video": "Video files", - "assets.mimetypes.group:web_audio": "Audio files used on the web", - "assets.mimetypes.group:web_file": "Web files", - "assets.mimetypes.group:web_image": "Image files used on the web", - "assets.mimetypes.group:web_video": "Video files used on the web", - "assets.mimetypes.image": "Image ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows icon", - "assets.mimetypes.text/css": "Cascading Style-Sheet", - "assets.mimetypes.text/csv": "Comma-separated values", - "assets.mimetypes.text/html": "HTML document", - "assets.mimetypes.text/plain": "Text file", - "assets.mimetypes.text/rtf": "RTF document", - "assets.mimetypes.text/vtt": "Web Video Text Track", - "assets.mimetypes.video": "Video file ({{$a.EXT}})", - "core.accounts": "Accounts", - "core.add": "Add", - "core.agelocationverification": "Age and location verification", - "core.ago": "{{$a}} ago", - "core.all": "All", - "core.allgroups": "All groups", - "core.allparticipants": "All participants", - "core.answer": "Answer", - "core.answered": "Answered", - "core.areyousure": "Are you sure?", - "core.back": "Back", - "core.block.blocks": "Blocks", - "core.browser": "Browser", - "core.cancel": "Cancel", - "core.cannotconnect": "Cannot connect", - "core.cannotconnecttrouble": "We're having trouble connecting to your site.", - "core.cannotconnectverify": "Please check the address is correct.", - "core.cannotdownloadfiles": "File downloading is disabled. Please contact your site administrator.", - "core.cannotopeninapp": "This file may not work as expected on this device. Would you like to open it anyway?", - "core.cannotopeninappdownload": "This file may not work as expected on this device. Would you like to download it anyway?", - "core.captureaudio": "Record audio", - "core.capturedimage": "Taken picture.", - "core.captureimage": "Take picture", - "core.capturevideo": "Record video", - "core.category": "Category", - "core.choose": "Choose", - "core.choosedots": "Choose...", - "core.clearsearch": "Clear search", - "core.clearstoreddata": "Clear storage {{$a}}", - "core.clicktohideshow": "Click to expand or collapse", - "core.clicktoseefull": "Click to see full contents.", - "core.close": "Close", - "core.comments": "Comments", - "core.comments.addcomment": "Add a comment...", - "core.comments.comments": "Comments", - "core.comments.commentscount": "Comments ({{$a}})", - "core.comments.commentsnotworking": "Comments cannot be retrieved", - "core.comments.deletecommentbyon": "Delete comment posted by {{$a.user}} on {{$a.time}}", - "core.comments.eventcommentcreated": "Comment created", - "core.comments.eventcommentdeleted": "Comment deleted", - "core.comments.nocomments": "No comments", - "core.comments.savecomment": "Save comment", - "core.comments.warningcommentsnotsent": "Couldn't sync comments. {{error}}", - "core.commentscount": "Comments ({{$a}})", - "core.completion-alt-auto-fail": "Completed: {{$a}} (did not achieve pass grade)", - "core.completion-alt-auto-n": "Not completed: {{$a}}", - "core.completion-alt-auto-n-override": "Not completed: {{$a.modname}} (set by {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Completed: {{$a}} (achieved pass grade)", - "core.completion-alt-auto-y": "Completed: {{$a}}", - "core.completion-alt-auto-y-override": "Completed: {{$a.modname}} (set by {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Not completed: {{$a}}. Select to mark as complete.", - "core.completion-alt-manual-n-override": "Not completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as complete.", - "core.completion-alt-manual-y": "Completed: {{$a}}. Select to mark as not complete.", - "core.completion-alt-manual-y-override": "Completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as not complete.", - "core.confirmcanceledit": "Are you sure you want to leave this page? All changes will be lost.", - "core.confirmdeletefile": "Are you sure you want to delete this file?", - "core.confirmgotabroot": "Are you sure you want to go back to {{name}}?", - "core.confirmgotabrootdefault": "Are you sure you want to go to the initial page of the current tab?", - "core.confirmleaveunknownchanges": "Are you sure you want to leave this page? If you have unsaved changes they will be lost.", - "core.confirmloss": "Are you sure? All changes will be lost.", - "core.confirmopeninbrowser": "Do you want to open it in a web browser?", - "core.considereddigitalminor": "You are too young to create an account on this site.", - "core.content": "Content", - "core.contenteditingsynced": "The content you are editing has been synced.", - "core.contentlinks.chooseaccount": "Choose account", - "core.contentlinks.chooseaccounttoopenlink": "Choose an account to open the link with.", - "core.contentlinks.confirmurlothersite": "This link belongs to another site. Do you want to open it?", - "core.contentlinks.errornoactions": "Couldn't find an action to perform with this link.", - "core.contentlinks.errornosites": "Couldn't find any site to handle this link.", - "core.contentlinks.errorredirectothersite": "The redirect URL cannot point to a different site.", - "core.continue": "Continue", - "core.copiedtoclipboard": "Text copied to clipboard", - "core.copytoclipboard": "Copy to clipboard", - "core.course": "Course", - "core.course.activitydisabled": "Your organisation has disabled this activity in the mobile app.", - "core.course.activitynotyetviewableremoteaddon": "Your organisation installed a plugin that is not yet supported.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Your organisation's Moodle installation needs to be updated.", - "core.course.allsections": "All sections", - "core.course.askadmintosupport": "Contact the site administrator and tell them you want to use this activity with the Moodle Mobile app.", - "core.course.availablespace": " You currently have about {{available}} free space.", - "core.course.cannotdeletewhiledownloading": "Files cannot be deleted while the activity is being downloaded. Please wait for the download to finish.", - "core.course.confirmdeletemodulefiles": "Are you sure you want to delete these files?", - "core.course.confirmdeletestoreddata": "Are you sure you want to delete the stored data?", - "core.course.confirmdownload": "You are about to download {{size}}.{{availableSpace}} Are you sure you want to continue?", - "core.course.confirmdownloadunknownsize": "It was not possible to calculate the size of the download.{{availableSpace}} Are you sure you want to continue?", - "core.course.confirmdownloadzerosize": "You are about to start downloading.{{availableSpace}} Are you sure you want to continue?", - "core.course.confirmlimiteddownload": "You are not currently connected to Wi-Fi. ", - "core.course.confirmpartialdownloadsize": "You are about to download at least {{size}}.{{availableSpace}} Are you sure you want to continue?", - "core.course.contents": "Contents", - "core.course.couldnotloadsectioncontent": "Could not load the section content. Please try again later.", - "core.course.couldnotloadsections": "Could not load the sections. Please try again later.", - "core.course.coursesummary": "Course summary", - "core.course.downloadcourse": "Download course", - "core.course.errordownloadingcourse": "Error downloading course.", - "core.course.errordownloadingsection": "Error downloading section.", - "core.course.errorgetmodule": "Error getting activity data.", - "core.course.hiddenfromstudents": "Hidden from students", - "core.course.hiddenoncoursepage": "Available but not shown on course page", - "core.course.insufficientavailablequota": "Your device could not allocate space to save this download. It may be reserving space for app and system updates. Please clear some storage space first.", - "core.course.insufficientavailablespace": "You are trying to download {{size}}. This will leave your device with insufficient space to operate normally. Please clear some storage space first.", - "core.course.manualcompletionnotsynced": "Manual completion not synchronised.", - "core.course.nocontentavailable": "No content available at the moment.", - "core.course.overriddennotice": "Your final grade from this activity was manually adjusted.", - "core.course.refreshcourse": "Refresh course", - "core.course.sections": "Sections", - "core.course.useactivityonbrowser": "You can still use it using your device's web browser.", - "core.course.warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.", - "core.course.warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}", - "core.coursedetails": "Course details", - "core.coursenogroups": "You are not a member of any group of this course.", - "core.courses.addtofavourites": "Star this course", - "core.courses.allowguests": "This course allows guest users to enter", - "core.courses.availablecourses": "Available courses", - "core.courses.cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.", - "core.courses.categories": "Course categories", - "core.courses.confirmselfenrol": "Are you sure you want to enrol yourself in this course?", - "core.courses.courses": "Courses", - "core.courses.downloadcourses": "Download courses", - "core.courses.enrolme": "Enrol me", - "core.courses.errorloadcategories": "An error occurred while loading categories.", - "core.courses.errorloadcourses": "An error occurred while loading courses.", - "core.courses.errorloadplugins": "The plugins required by this course could not be loaded correctly. Please reload the app to try again.", - "core.courses.errorsearching": "An error occurred while searching.", - "core.courses.errorselfenrol": "An error occurred while self enrolling.", - "core.courses.filtermycourses": "Filter my courses", - "core.courses.frontpage": "Front page", - "core.courses.hidecourse": "Remove from view", - "core.courses.ignore": "Ignore", - "core.courses.mycourses": "My courses", - "core.courses.mymoodle": "Dashboard", - "core.courses.nocourses": "No course information to show.", - "core.courses.nocoursesyet": "No courses in this category", - "core.courses.nosearchresults": "No results", - "core.courses.notenroled": "You are not enrolled in this course", - "core.courses.notenrollable": "You cannot enrol yourself in this course.", - "core.courses.password": "Enrolment key", - "core.courses.paymentrequired": "This course requires a payment for entry.", - "core.courses.paypalaccepted": "PayPal payments accepted", - "core.courses.reload": "Reload", - "core.courses.removefromfavourites": "Unstar this course", - "core.courses.search": "Search", - "core.courses.searchcourses": "Search courses", - "core.courses.searchcoursesadvice": "You can use the search courses button to find courses to access as a guest or enrol yourself in courses that allow it.", - "core.courses.selfenrolment": "Self enrolment", - "core.courses.sendpaymentbutton": "Send payment via PayPal", - "core.courses.show": "Restore to view", - "core.courses.totalcoursesearchresults": "Total courses: {{$a}}", - "core.currentdevice": "Current device", - "core.datastoredoffline": "Data stored in the device because it couldn't be sent. It will be sent automatically later.", - "core.date": "Date", - "core.day": "day", - "core.days": "days", - "core.decsep": ".", - "core.defaultvalue": "Default ({{$a}})", - "core.delete": "Delete", - "core.deletedoffline": "Deleted offline", - "core.deleteduser": "Deleted user", - "core.deleting": "Deleting", - "core.description": "Description", - "core.desktop": "Desktop", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Digital minor", - "core.digitalminor_desc": "Please ask your parent/guardian to contact:", - "core.discard": "Discard", - "core.dismiss": "Dismiss", - "core.displayoptions": "Display options", - "core.done": "Done", - "core.download": "Download", - "core.downloaded": "Downloaded", - "core.downloadfile": "Download file", - "core.downloading": "Downloading", - "core.edit": "Edit", - "core.editor.autosavesucceeded": "Draft saved.", - "core.editor.bold": "Bold", - "core.editor.clear": "Clear formatting", - "core.editor.h3": "Heading (large)", - "core.editor.h4": "Heading (medium)", - "core.editor.h5": "Heading (small)", - "core.editor.hidetoolbar": "Hide toolbar", - "core.editor.italic": "Italic", - "core.editor.orderedlist": "Ordered list", - "core.editor.p": "Paragraph", - "core.editor.strike": "Strike through", - "core.editor.textrecovered": "A draft version of this text was automatically restored.", - "core.editor.toggle": "Toggle editor", - "core.editor.underline": "Underline", - "core.editor.unorderedlist": "Unordered list", - "core.emptysplit": "This page will appear blank if the left panel is empty or is loading.", - "core.error": "Error", - "core.errorchangecompletion": "An error occurred while changing the completion status. Please try again.", - "core.errordeletefile": "Error deleting the file. Please try again.", - "core.errordownloading": "Error downloading file.", - "core.errordownloadingsomefiles": "Error downloading files. Some files might be missing.", - "core.errorfileexistssamename": "A file with this name already exists.", - "core.errorinvalidform": "The form contains invalid data. Please check that all required fields are filled in and that the data is valid.", - "core.errorinvalidresponse": "Invalid response received. Please contact your site administrator if the error persists.", - "core.errorloadingcontent": "Error loading content.", - "core.errorofflinedisabled": "Offline browsing is disabled on your site. You need to be connected to the internet to use the app.", - "core.erroropenfilenoapp": "Error opening file: no app found to open this type of file.", - "core.erroropenfilenoextension": "Error opening file: the file doesn't have an extension.", - "core.erroropenpopup": "This activity is trying to open a popup. This is not supported in the app.", - "core.errorrenamefile": "Error renaming file. Please try again.", - "core.errorsomedatanotdownloaded": "If you downloaded this activity, please notice that some data isn't downloaded during the download process for performance and data usage reasons.", - "core.errorsync": "An error occurred while synchronising. Please try again.", - "core.errorsyncblocked": "This {{$a}} cannot be synchronised right now because of an ongoing process. Please try again later. If the problem persists, try restarting the app.", - "core.errorurlschemeinvalidscheme": "This URL is meant to be used in another app: {{$a}}.", - "core.errorurlschemeinvalidsite": "This site URL cannot be opened in this app.", - "core.explanationdigitalminor": "This information is required to determine if your age is over the digital age of consent. This is the age when an individual can consent to terms and conditions and their data being legally stored and processed.", - "core.favourites": "Starred", - "core.filename": "Filename", - "core.filenameexist": "File name already exists: {{$a}}", - "core.filenotfound": "File not found, sorry.", - "core.fileuploader.addfiletext": "Add file", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Camera", - "core.fileuploader.confirmuploadfile": "You are about to upload {{size}}. Are you sure you want to continue?", - "core.fileuploader.confirmuploadunknownsize": "It was not possible to calculate the size of the upload. Are you sure you want to continue?", - "core.fileuploader.errorcapturingaudio": "Error capturing audio.", - "core.fileuploader.errorcapturingimage": "Error capturing image.", - "core.fileuploader.errorcapturingvideo": "Error capturing video.", - "core.fileuploader.errorgettingimagealbum": "Error getting image from album.", - "core.fileuploader.errormustbeonlinetoupload": "You have to be online to upload files.", - "core.fileuploader.errornoapp": "You don't have an app installed to perform this action.", - "core.fileuploader.errorreadingfile": "Error reading file.", - "core.fileuploader.errorwhileuploading": "An error occurred during the file upload.", - "core.fileuploader.file": "File", - "core.fileuploader.filesofthesetypes": "Accepted file types:", - "core.fileuploader.fileuploaded": "The file was successfully uploaded.", - "core.fileuploader.invalidfiletype": "{{$a}} filetype cannot be accepted.", - "core.fileuploader.maxbytesfile": "The file {{$a.file}} is too large. The maximum size you can upload is {{$a.size}}.", - "core.fileuploader.more": "More", - "core.fileuploader.photoalbums": "Photo albums", - "core.fileuploader.readingfile": "Reading file", - "core.fileuploader.readingfileperc": "Reading file: {{$a}}%", - "core.fileuploader.selectafile": "Select a file", - "core.fileuploader.uploadafile": "Upload a file", - "core.fileuploader.uploading": "Uploading", - "core.fileuploader.uploadingperc": "Uploading: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filter", - "core.folder": "Folder", - "core.forcepasswordchangenotice": "You must change your password to proceed.", - "core.fulllistofcourses": "All courses", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Average", - "core.grades.badgrade": "Supplied grade is invalid", - "core.grades.contributiontocoursetotal": "Contribution to course total", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Grade", - "core.grades.gradeitem": "Grade item", - "core.grades.grades": "Grades", - "core.grades.lettergrade": "Letter grade", - "core.grades.nogradesreturned": "No grades returned", - "core.grades.nooutcome": "No outcome", - "core.grades.percentage": "Percentage", - "core.grades.range": "Range", - "core.grades.rank": "Rank", - "core.grades.weight": "Weight", - "core.group": "Group", - "core.groupsseparate": "Separate groups", - "core.groupsvisible": "Visible groups", - "core.h5p.additionallicenseinfo": "Any additional information about the licence", - "core.h5p.author": "Author", - "core.h5p.authorcomments": "Author comments", - "core.h5p.authorcommentsdescription": "Comments for the editor of the content. (This text will not be published as a part of the copyright info.)", - "core.h5p.authorname": "Author's name", - "core.h5p.authorrole": "Author's role", - "core.h5p.by": "by", - "core.h5p.cancellabel": "Cancel", - "core.h5p.ccattribution": "Attribution (CC BY)", - "core.h5p.ccattributionnc": "Attribution-NonCommercial (CC BY-NC)", - "core.h5p.ccattributionncnd": "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Attribution-NoDerivs (CC BY-ND)", - "core.h5p.ccattributionsa": "Attribution-ShareAlike (CC BY-SA)", - "core.h5p.ccpdd": "Public Domain Dedication (CC0)", - "core.h5p.changedby": "Changed by", - "core.h5p.changedescription": "Description of change", - "core.h5p.changelog": "Changelog", - "core.h5p.changeplaceholder": "Photo cropped, text changed, etc.", - "core.h5p.close": "Close", - "core.h5p.confirmdialogbody": "Please confirm that you wish to proceed. This action cannot be undone.", - "core.h5p.confirmdialogheader": "Confirm action", - "core.h5p.confirmlabel": "Confirm", - "core.h5p.connectionLost": "Connection lost. Results will be stored and sent when the connection is reestablished.", - "core.h5p.connectionReestablished": "Connection reestablished.", - "core.h5p.contentCopied": "Content is copied to the clipboard", - "core.h5p.contentchanged": "This content has changed since you last used it.", - "core.h5p.contenttype": "Content type", - "core.h5p.copyright": "Rights of use", - "core.h5p.copyrightinfo": "Copyright information", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "View copyright information for this content.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Date", - "core.h5p.disablefullscreen": "Disable fullscreen", - "core.h5p.download": "Download", - "core.h5p.downloadtitle": "Download this content as a H5P file.", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Embed", - "core.h5p.embedtitle": "View the embed code for this content.", - "core.h5p.errorgetemail": "Error obtaining the user email. Please check your connection and try again.", - "core.h5p.fullscreen": "Fullscreen", - "core.h5p.gpl": "General Public License v3", - "core.h5p.h5ptitle": "Visit h5p.org to check out more content.", - "core.h5p.hideadvanced": "Hide advanced", - "core.h5p.license": "Licence", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Generic", - "core.h5p.licenseCC20": "2.0 Generic", - "core.h5p.licenseCC25": "2.5 Generic", - "core.h5p.licenseCC30": "3.0 Unported", - "core.h5p.licenseCC40": "4.0 International", - "core.h5p.licenseGPL": "General Public License", - "core.h5p.licenseV1": "Version 1", - "core.h5p.licenseV2": "Version 2", - "core.h5p.licenseV3": "Version 3", - "core.h5p.licensee": "Licensee", - "core.h5p.licenseextras": "Licence extras", - "core.h5p.licenseversion": "Licence version", - "core.h5p.nocopyright": "No copyright information available for this content.", - "core.h5p.offlineDialogBody": "We were unable to send information about your completion of this task. Please check your internet connection.", - "core.h5p.offlineDialogHeader": "Your connection to the server was lost", - "core.h5p.offlineDialogRetryButtonLabel": "Retry now", - "core.h5p.offlineDialogRetryMessage": "Retrying in :num....", - "core.h5p.offlineSuccessfulSubmit": "Successfully submitted results.", - "core.h5p.offlinedisabled": "The site doesn't allow downloading H5P packages.", - "core.h5p.originator": "Originator", - "core.h5p.pd": "Public Domain", - "core.h5p.pddl": "Public Domain Dedication and Licence", - "core.h5p.pdm": "Public Domain Mark (PDM)", - "core.h5p.play": "Play H5P", - "core.h5p.resizescript": "Include this script on your website if you want dynamic sizing of the embedded content:", - "core.h5p.resubmitScores": "Attempting to submit stored results.", - "core.h5p.reuse": "Reuse", - "core.h5p.reuseContent": "Reuse content", - "core.h5p.reuseDescription": "Reuse this content.", - "core.h5p.showadvanced": "Show advanced", - "core.h5p.showless": "Show less", - "core.h5p.showmore": "Show more", - "core.h5p.size": "Size", - "core.h5p.source": "Source", - "core.h5p.startingover": "You'll be starting over.", - "core.h5p.sublevel": "Sublevel", - "core.h5p.thumbnail": "Thumbnail", - "core.h5p.title": "Title", - "core.h5p.undisclosed": "Undisclosed", - "core.h5p.year": "Year", - "core.h5p.years": "Year(s)", - "core.h5p.yearsfrom": "Years (from)", - "core.h5p.yearsto": "Years (to)", - "core.hasdatatosync": "This {{$a}} has offline data to be synchronised.", - "core.help": "Help", - "core.hide": "Hide", - "core.hour": "hour", - "core.hours": "hours", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Image", - "core.imageviewer": "Image viewer", - "core.info": "Information", - "core.invalidformdata": "Incorrect form data", - "core.labelsep": ":", - "core.lastaccess": "Last access", - "core.lastdownloaded": "Last downloaded", - "core.lastmodified": "Last modified", - "core.lastsync": "Last synchronisation", - "core.layoutgrid": "Grid", - "core.list": "List", - "core.listsep": ",", - "core.loading": "Loading", - "core.loadmore": "Load more", - "core.location": "Location", - "core.login.auth_email": "Email-based self-registration", - "core.login.authenticating": "Authenticating", - "core.login.cancel": "Cancel", - "core.login.changepassword": "Change password", - "core.login.changepasswordbutton": "Open the change password page", - "core.login.changepasswordhelp": "If you have problems changing your password, please contact your site administrator. \"Site Administrators\" are the people who manages the Moodle at your school/university/company or learning organisation. If you don't know how to contact them, please contact your teachers/trainers.", - "core.login.changepasswordinstructions": "You cannot change your password in the app. Please click the following button to open the site in a web browser to change your password. Take into account you need to close the browser after changing the password as you will not be redirected to the app.", - "core.login.changepasswordlogoutinstructions": "If you prefer to change site or log out, please click the following button:", - "core.login.changepasswordreconnectinstructions": "Click the following button to reconnect to the site. (Take into account that if you didn't change your password successfully, you would return to the previous screen).", - "core.login.confirmdeletesite": "Are you sure you want to delete the site {{sitename}}?", - "core.login.connect": "Connect!", - "core.login.connecttomoodle": "Connect to Moodle", - "core.login.connecttomoodleapp": "You are trying to connect to a regular Moodle site. Please download the official Moodle app to access this site.", - "core.login.connecttoworkplaceapp": "You are trying to connect to a Moodle Workplace site. Please download the Moodle Workplace app to access this site.", - "core.login.contactyouradministrator": "Contact your site administrator for further help.", - "core.login.contactyouradministratorissue": "Please ask your site administrator to check the following issue: {{$a}}", - "core.login.createaccount": "Create my new account", - "core.login.createuserandpass": "Choose your username and password", - "core.login.credentialsdescription": "Please provide your username and password to log in.", - "core.login.emailconfirmsent": "

          An email should have been sent to your address at {{$a}}

          \n

          It contains easy instructions to complete your registration.

          \n

          If you continue to have difficulty, contact the site administrator.

          ", - "core.login.emailconfirmsentnoemail": "

          An email should have been sent to your address.

          It contains easy instructions to complete your registration.

          If you continue to have difficulty, contact the site administrator.

          ", - "core.login.emailconfirmsentsuccess": "Confirmation email sent successfully", - "core.login.emailnotmatch": "Emails do not match", - "core.login.erroraccesscontrolalloworigin": "The cross-origin call you're trying to perform has been rejected. Please check https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "An error occurred while deleting this site. Please try again.", - "core.login.errorexampleurl": "The URL https://campus.example.edu is only an example URL, it's not a real site. Please use the URL of your school or organization's site.", - "core.login.errorqrnoscheme": "This URL isn't a valid login URL.", - "core.login.errorupdatesite": "An error occurred while updating the site's token.", - "core.login.faqcannotconnectanswer": "Please, contact your site administrator.", - "core.login.faqcannotconnectquestion": "I typed my site address correctly but I still can't connect.", - "core.login.faqcannotfindmysiteanswer": "Have you typed the name correctly? It's also possible that your site is not included in our public sites directory. If you still can't find it, please enter your site address instead.", - "core.login.faqcannotfindmysitequestion": "I can't find my site.", - "core.login.faqsetupsiteanswer": "Visit {{$link}} to check out the different options you have to create your own Moodle site.", - "core.login.faqsetupsitelinktitle": "Get started.", - "core.login.faqsetupsitequestion": "I want to set up my own Moodle site.", - "core.login.faqtestappanswer": "To test the app in a Moodle Demo Site, type \"teacher\" or \"student\" in the \"Your site\" field and click the \"Connect to your site\" button.", - "core.login.faqtestappquestion": "I just want to test the app, what can I do?", - "core.login.faqwhatisurlanswer": "

          Every organisation has their own unique address or URL for their Moodle site. To find the address:

          1. Open a web browser and go to your Moodle site login page.
          2. At the top of the page, in the address bar, you will see the URL of your Moodle site e.g. \"campus.example.edu\".
            {{$image}}
          3. Copy the address (do not copy the /login and what comes after), paste it into the Moodle app then click \"Connect to your site\"
          4. Now you can log in to your site using your username and password.
          5. ", - "core.login.faqwhatisurlquestion": "What is my site address? How can I find my site URL?", - "core.login.faqwhereisqrcode": "Where can I find the QR code?", - "core.login.faqwhereisqrcodeanswer": "

            If your organisation has enabled it, you will find a QR code on the web site at the bottom of your user profile page.

            {{$image}}", - "core.login.findyoursite": "Find your site", - "core.login.firsttime": "Is this your first time here?", - "core.login.forcepasswordchangenotice": "You must change your password to proceed.", - "core.login.forgotten": "Forgotten your username or password?", - "core.login.help": "Help", - "core.login.helpmelogin": "

            There are many thousands of Moodle sites around the world. This app can only connect to Moodle sites that have specifically enabled Mobile app access.

            If you can't connect to your Moodle site then you need to contact your site administrator and ask them to read http://docs.moodle.org/en/Mobile_app

            To test the app in a Moodle demo site type teacher or student in the Site address field and click the Connect button.

            ", - "core.login.instructions": "Instructions", - "core.login.invalidaccount": "Please check your login details or ask your site administrator to check the site configuration.", - "core.login.invaliddate": "Invalid date", - "core.login.invalidemail": "Invalid email address", - "core.login.invalidmoodleversion": "

            Invalid Moodle site version. The Moodle app only supports Moodle systems {{$a}} onwards.

            \n

            You can contact your site administrators and ask them to update their Moodle system.

            \n

            \"Site Administrators\" are the people who manages the Moodle at your school/university/company or learning organisation. If you don't know how to contact them, please contact your teachers/trainers.

            ", - "core.login.invalidsite": "The site URL is invalid.", - "core.login.invalidtime": "Invalid time", - "core.login.invalidurl": "Invalid URL specified", - "core.login.invalidvaluemax": "The maximum value is {{$a}}", - "core.login.invalidvaluemin": "The minimum value is {{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle Mobile Additional Features check returned an unexpected response. You will be authenticated using the standard mobile service.", - "core.login.loggedoutssodescription": "You have to authenticate again. You need to log in to the site in a browser window.", - "core.login.login": "Log in", - "core.login.loginbutton": "Log in", - "core.login.logininsiterequired": "You need to log in to the site in a browser window.", - "core.login.loginsteps": "For full access to this site, you first need to create an account.", - "core.login.missingemail": "Missing email address", - "core.login.missingfirstname": "Missing given name", - "core.login.missinglastname": "Missing surname", - "core.login.mobileservicesnotenabled": "Mobile access is not enabled on your site. Please contact your site administrator if you think it should be enabled.", - "core.login.mustconfirm": "You need to confirm your account", - "core.login.newaccount": "New account", - "core.login.notloggedin": "You need to be logged in.", - "core.login.onboardingcreatemanagecourses": "Create & manage your courses", - "core.login.onboardingenrolmanagestudents": "Enrol & manage your students", - "core.login.onboardinggetstarted": "Get started with Moodle", - "core.login.onboardingialreadyhaveasite": "I already have a Moodle site", - "core.login.onboardingimalearner": "I'm a learner", - "core.login.onboardingimaneducator": "I'm an educator", - "core.login.onboardingineedasite": "I need a Moodle site", - "core.login.onboardingprovidefeedback": "Provide timely feedback", - "core.login.onboardingtoconnect": "To connect to the Moodle App you'll need a Moodle site", - "core.login.onboardingwelcome": "Welcome to the Moodle App!", - "core.login.or": "OR", - "core.login.password": "Password", - "core.login.passwordforgotten": "Forgotten password", - "core.login.passwordforgotteninstructions2": "To reset your password, submit your username or your email address below. If we can find you in the database, an email will be sent to your email address, with instructions how to get access again.", - "core.login.passwordrequired": "Password required", - "core.login.policyaccept": "I understand and agree", - "core.login.policyagree": "You must agree to this policy to continue using this site. Do you agree?", - "core.login.policyagreement": "Site policy agreement", - "core.login.policyagreementclick": "Link to site policy agreement", - "core.login.potentialidps": "Log in using your account on:", - "core.login.profileinvaliddata": "Invalid value", - "core.login.recaptchachallengeimage": "reCAPTCHA challenge image", - "core.login.recaptchaexpired": "Verification expired. Answer the security question again.", - "core.login.recaptchaincorrect": "The security question answer is incorrect.", - "core.login.reconnect": "Reconnect", - "core.login.reconnectdescription": "Your authentication token is invalid or has expired. You have to reconnect to the site.", - "core.login.reconnectssodescription": "Your authentication token is invalid or has expired. You have to reconnect to the site. You need to log in to the site in a browser window.", - "core.login.resendemail": "Resend email", - "core.login.searchby": "Search by:", - "core.login.security_question": "Security question", - "core.login.selectacountry": "Select a country", - "core.login.selectsite": "Please select your site:", - "core.login.signupplugindisabled": "{{$a}} is not enabled.", - "core.login.signuprequiredfieldnotsupported": "The signup form contains a required custom field that isn't supported in the app. Please create your account using a web browser.", - "core.login.siteaddress": "Your site", - "core.login.siteaddressplaceholder": "https://campus.example.edu", - "core.login.sitehasredirect": "Your site contains at least one HTTP redirect. The app cannot follow redirects, this could be the issue that's preventing the app from connecting to your site.", - "core.login.siteinmaintenance": "Your site is in maintenance mode", - "core.login.sitepolicynotagreederror": "Site policy not agreed.", - "core.login.siteurl": "Site URL", - "core.login.siteurlrequired": "Site URL required i.e http://www.yourmoodlesite.org", - "core.login.startsignup": "Create new account", - "core.login.stillcantconnect": "Still can't connect?", - "core.login.supplyinfo": "More details", - "core.login.username": "Username", - "core.login.usernameoremail": "Enter either username or email address", - "core.login.usernamerequired": "Username required", - "core.login.usernotaddederror": "User not added - error", - "core.login.visitchangepassword": "Do you want to visit the site to change the password?", - "core.login.webservicesnotenabled": "Your host site may not have enabled Web services. Please contact your administrator for help.", - "core.login.youcanstillconnectwithcredentials": "You can still connect to the site by entering your username and password.", - "core.login.yourenteredsite": "Connect to your site", - "core.lostconnection": "Your authentication token is invalid or has expired. You will have to reconnect to the site.", - "core.mainmenu.changesite": "Change site", - "core.mainmenu.help": "Help", - "core.mainmenu.logout": "Log out", - "core.mainmenu.website": "Website", - "core.maxsizeandattachments": "Maximum file size: {{$a.size}}, maximum number of files: {{$a.attachments}}", - "core.min": "min", - "core.mins": "mins", - "core.misc": "Miscellaneous", - "core.mod_assign": "Assignment", - "core.mod_assignment": "Assignment 2.2 (Disabled)", - "core.mod_book": "Book", - "core.mod_chat": "Chat", - "core.mod_choice": "Choice", - "core.mod_data": "Database", - "core.mod_database": "Database", - "core.mod_external-tool": "External tool", - "core.mod_feedback": "Feedback", - "core.mod_file": "File", - "core.mod_folder": "Folder", - "core.mod_forum": "Forum", - "core.mod_glossary": "Glossary", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS content package", - "core.mod_imscp": "IMS content package", - "core.mod_label": "Label", - "core.mod_lesson": "Lesson", - "core.mod_lti": "External tool", - "core.mod_page": "Page", - "core.mod_quiz": "Quiz", - "core.mod_resource": "File", - "core.mod_scorm": "SCORM package", - "core.mod_survey": "Survey", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Workshop", - "core.moduleintro": "Description", - "core.more": "more", - "core.mygroups": "My groups", - "core.name": "Name", - "core.needhelp": "Need help?", - "core.networkerroriframemsg": "This content is not available offline. Please connect to the internet and try again.", - "core.networkerrormsg": "There was a problem connecting to the site. Please check your connection and try again.", - "core.never": "Never", - "core.next": "Next", - "core.no": "No", - "core.nocomments": "No comments", - "core.nograde": "No grade", - "core.none": "None", - "core.nooptionavailable": "No option available", - "core.nopasswordchangeforced": "You cannot proceed without changing your password.", - "core.nopermissionerror": "Sorry, but you do not currently have permissions to do that", - "core.nopermissions": "Sorry, but you do not currently have permissions to do that ({{$a}}).", - "core.noresults": "No results", - "core.noselection": "No selection", - "core.notapplicable": "n/a", - "core.notavailable": "Not available", - "core.notenrolledprofile": "This profile is not available because this user is not enrolled in this course.", - "core.notice": "Notice", - "core.notingroup": "Sorry, but you need to be part of a group to see this page.", - "core.notsent": "Not sent", - "core.now": "now", - "core.nummore": "{{$a}} more", - "core.numwords": "{{$a}} words", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfile": "Open file", - "core.openfullimage": "Click here to display the full size image", - "core.openinbrowser": "Open in browser", - "core.openmodinbrowser": "Open {{$a}} in browser", - "core.othergroups": "Other groups", - "core.pagea": "Page {{$a}}", - "core.parentlanguage": "", - "core.paymentinstant": "Use the button below to pay and be enrolled within minutes!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Phone", - "core.pictureof": "Picture of {{$a}}", - "core.previous": "Previous", - "core.proceed": "Proceed", - "core.pulltorefresh": "Pull to refresh", - "core.qrscanner": "QR scanner", - "core.question.answer": "Answer", - "core.question.answersaved": "Answer saved", - "core.question.cannotdeterminestatus": "Cannot determine status", - "core.question.certainty": "Certainty", - "core.question.complete": "Complete", - "core.question.correct": "Correct", - "core.question.errorattachmentsnotsupported": "The application doesn't support attaching files to answers yet.", - "core.question.errorinlinefilesnotsupported": "The application doesn't support editing inline files yet.", - "core.question.errorquestionnotsupported": "This question type is not supported by the app: {{$a}}.", - "core.question.feedback": "Feedback", - "core.question.howtodraganddrop": "Tap to select then tap to drop.", - "core.question.incorrect": "Incorrect", - "core.question.information": "Information", - "core.question.invalidanswer": "Incomplete answer", - "core.question.notanswered": "Not answered", - "core.question.notyetanswered": "Not yet answered", - "core.question.partiallycorrect": "Partially correct", - "core.question.questionmessage": "Question {{$a}}: {{$b}}", - "core.question.questionno": "Question {{$a}}", - "core.question.requiresgrading": "Requires grading", - "core.quotausage": "You have currently used {{$a.used}} of your {{$a.total}} limit.", - "core.rating.aggregateavg": "Average of ratings", - "core.rating.aggregatecount": "Count of ratings", - "core.rating.aggregatemax": "Maximum rating", - "core.rating.aggregatemin": "Minimum rating", - "core.rating.aggregatesum": "Sum of ratings", - "core.rating.noratings": "No ratings submitted", - "core.rating.rating": "Rating", - "core.rating.ratings": "Ratings", - "core.redirectingtosite": "You will be redirected to the site.", - "core.refresh": "Refresh", - "core.remove": "Remove", - "core.removefiles": "Remove files {{$a}}", - "core.required": "Required", - "core.requireduserdatamissing": "This user lacks some required profile data. Please enter the data in your site and try again.
            {{$a}}", - "core.resourcedisplayopen": "Open", - "core.resources": "Resources", - "core.restore": "Restore", - "core.restricted": "Restricted", - "core.retry": "Retry", - "core.save": "Save", - "core.savechanges": "Save changes", - "core.scanqr": "Scan QR code", - "core.search": "Search", - "core.searching": "Searching", - "core.searchresults": "Search results", - "core.sec": "sec", - "core.secs": "secs", - "core.seemoredetail": "Click here to see more detail", - "core.selectacategory": "Please select a category", - "core.selectacourse": "Select a course", - "core.selectagroup": "Select a group", - "core.send": "Send", - "core.sending": "Sending", - "core.serverconnection": "Error connecting to the server", - "core.settings.about": "About", - "core.settings.appsettings": "App settings", - "core.settings.appversion": "App version", - "core.settings.cannotsyncloggedout": "This site cannot be synchronised because you've logged out. Please try again when you're logged in the site again.", - "core.settings.cannotsyncoffline": "Cannot synchronise offline.", - "core.settings.cannotsyncwithoutwifi": "Cannot synchronise because the current settings only allow to synchronise when connected to Wi-Fi. Please connect to a Wi-Fi network.", - "core.settings.colorscheme": "Color Scheme", - "core.settings.colorscheme-auto": "Auto (based on system settings)", - "core.settings.colorscheme-dark": "Dark", - "core.settings.colorscheme-light": "Light", - "core.settings.compilationinfo": "Compilation info", - "core.settings.copyinfo": "Copy device info on the clipboard", - "core.settings.cordovadevicemodel": "Cordova device model", - "core.settings.cordovadeviceosversion": "Cordova device OS version", - "core.settings.cordovadeviceplatform": "Cordova device platform", - "core.settings.cordovadeviceuuid": "Cordova device UUID", - "core.settings.cordovaversion": "Cordova version", - "core.settings.currentlanguage": "Current language", - "core.settings.debugdisplay": "Display debug messages", - "core.settings.debugdisplaydescription": "If enabled, error modals will display more data about the error if possible.", - "core.settings.deletesitefiles": "Are you sure that you want to delete the downloaded files and cached data from the site '{{sitename}}'? You won't be able to use the app in offline mode.", - "core.settings.deletesitefilestitle": "Delete site files", - "core.settings.deviceinfo": "Device info", - "core.settings.deviceos": "Device OS", - "core.settings.disableall": "Disable notifications", - "core.settings.disabled": "Disabled", - "core.settings.displayformat": "Display format", - "core.settings.enabledownloadsection": "Enable download sections", - "core.settings.enablefirebaseanalytics": "Enable Firebase analytics", - "core.settings.enablefirebaseanalyticsdescription": "If enabled, the app will collect anonymous data usage.", - "core.settings.enablerichtexteditor": "Enable text editor", - "core.settings.enablerichtexteditordescription": "If enabled, a text editor will be available when entering content.", - "core.settings.enablesyncwifi": "Allow sync only when on Wi-Fi", - "core.settings.entriesincache": "{{$a}} entries in cache", - "core.settings.errordeletesitefiles": "Error deleting site files.", - "core.settings.errorsyncsite": "Error synchronising site data. Please check your Internet connection and try again.", - "core.settings.estimatedfreespace": "Estimated free space", - "core.settings.filesystemroot": "File system root", - "core.settings.fontsize": "Text size", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "This setting has been forced by your site configuration.", - "core.settings.general": "General", - "core.settings.language": "Language", - "core.settings.license": "Licence", - "core.settings.localnotifavailable": "Local notifications available", - "core.settings.locationhref": "Web view URL", - "core.settings.locked": "Locked", - "core.settings.loggedin": "Online", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Navigator language", - "core.settings.navigatoruseragent": "Navigator userAgent", - "core.settings.networkstatus": "Internet connection status", - "core.settings.opensourcelicenses": "Open Source Licences", - "core.settings.preferences": "Preferences", - "core.settings.privacypolicy": "Privacy policy", - "core.settings.publisher": "Publisher", - "core.settings.pushid": "Push notifications ID", - "core.settings.reportinbackground": "Report errors automatically", - "core.settings.screen": "Screen information", - "core.settings.settings": "Settings", - "core.settings.showdownloadoptions": "Show download options", - "core.settings.siteinfo": "Site info", - "core.settings.sites": "Sites", - "core.settings.spaceusage": "Space usage", - "core.settings.spaceusagehelp": "Deleting the stored information of the site will remove all the site offline data. This information allows you to use the app when offline. ", - "core.settings.synchronization": "Synchronisation", - "core.settings.synchronizenow": "Synchronise now", - "core.settings.synchronizenowhelp": "Synchronising a site will send pending changes and all offline activity stored in the device and will synchronise some data like messages and notifications.", - "core.settings.syncsettings": "Synchronisation settings", - "core.settings.total": "Total", - "core.settings.wificonnection": "Wi-Fi connection", - "core.sharedfiles.chooseaccountstorefile": "Choose an account to store the file in.", - "core.sharedfiles.chooseactionrepeatedfile": "A file with this name already exists. Do you want to replace the existing file or rename it to \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "There are no sites stored. Please add a site before sharing a file with the app.", - "core.sharedfiles.nosharedfiles": "There are no shared files stored on this site.", - "core.sharedfiles.nosharedfilestoupload": "You have no files to upload here. If you want to upload a file from another app, locate the file and click the 'Open in' button.", - "core.sharedfiles.rename": "Rename", - "core.sharedfiles.replace": "Replace", - "core.sharedfiles.sharedfiles": "Shared files", - "core.sharedfiles.successstorefile": "File successfully stored. Select the file to upload to your private files or use in an activity.", - "core.show": "Show", - "core.showless": "Show less...", - "core.showmore": "Show more...", - "core.site": "Site", - "core.sitehome.sitehome": "Site home", - "core.sitehome.sitenews": "Site announcements", - "core.sitemaintenance": "The site is undergoing maintenance and is currently not available", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Skip", - "core.sorry": "Sorry...", - "core.sort": "Sort", - "core.sortby": "Sort by", - "core.start": "Start", - "core.storingfiles": "Storing files", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedatetimeshort": "%d/%m/%y, %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Submit", - "core.success": "Success", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Default collection", - "core.tag.errorareanotsupported": "This tag area is not supported by the app.", - "core.tag.inalltagcoll": "Everywhere", - "core.tag.itemstaggedwith": "{{$a.tagarea}} tagged with \"{{$a.tag}}\"", - "core.tag.noresultsfor": "No results for \"{{$a}}\"", - "core.tag.notagsfound": "No tags matching \"{{$a}}\" found", - "core.tag.searchtags": "Search tags", - "core.tag.showingfirsttags": "Showing {{$a}} most popular tags", - "core.tag.tag": "Tag", - "core.tag.tagarea_course": "Courses", - "core.tag.tagarea_course_modules": "Activities and resources", - "core.tag.tagarea_post": "Blog posts", - "core.tag.tagarea_user": "User interests", - "core.tag.tags": "Tags", - "core.tag.warningareasnotsupported": "Some of the tag areas are not displayed because they are not supported by the app.", - "core.teachers": "Teachers", - "core.thereisdatatosync": "There are offline {{$a}} to be synchronised.", - "core.thisdirection": "ltr", - "core.time": "Time", - "core.timesup": "Time is up!", - "core.today": "Today", - "core.tryagain": "Try again", - "core.twoparagraphs": "{{p1}}

            {{p2}}", - "core.uhoh": "Uh oh!", - "core.unexpectederror": "Unexpected error. Please close and reopen the application then try again.", - "core.unicodenotsupported": "Some emojis are not supported on this site. Such characters will be removed when the message is sent.", - "core.unicodenotsupportedcleanerror": "Empty text was found when cleaning Unicode chars.", - "core.unknown": "Unknown", - "core.unlimited": "Unlimited", - "core.unzipping": "Unzipping", - "core.updaterequired": "App update required", - "core.updaterequireddesc": "Please update your app to version {{$a}}", - "core.upgraderunning": "Site is being upgraded, please retry later.", - "core.user": "User", - "core.user.address": "Address", - "core.user.city": "City/town", - "core.user.contact": "Contact", - "core.user.country": "Country", - "core.user.description": "Description", - "core.user.details": "Details", - "core.user.detailsnotavailable": "The details of this user are not available to you.", - "core.user.editingteacher": "Teacher", - "core.user.email": "Email address", - "core.user.emailagain": "Email (again)", - "core.user.errorloaduser": "Error loading user.", - "core.user.firstname": "First name", - "core.user.interests": "Interests", - "core.user.lastname": "Surname", - "core.user.manager": "Manager", - "core.user.newpicture": "New picture", - "core.user.noparticipants": "No participants found for this course", - "core.user.participants": "Participants", - "core.user.phone1": "Phone", - "core.user.phone2": "Mobile phone", - "core.user.roles": "Roles", - "core.user.sendemail": "Email", - "core.user.student": "Student", - "core.user.teacher": "Non-editing teacher", - "core.user.webpage": "Web page", - "core.userdeleted": "This user account has been deleted", - "core.userdetails": "User details", - "core.usernotfullysetup": "User not fully set-up", - "core.users": "Users", - "core.view": "View", - "core.viewcode": "View code", - "core.vieweditor": "View editor", - "core.viewembeddedcontent": "View embedded content", - "core.viewprofile": "View profile", - "core.warningofflinedatadeleted": "Offline data from {{component}} '{{name}}' has been deleted. {{error}}", - "core.whatisyourage": "What is your age?", - "core.wheredoyoulive": "In which country do you live?", - "core.whoissiteadmin": "\"Site Administrators\" are the people who manage the Moodle at your school/university/company or learning organisation. If you don't know how to contact them, please contact your teachers/trainers.", - "core.whoops": "Oops!", - "core.whyisthishappening": "Why is this happening?", - "core.whyisthisrequired": "Why is this required?", - "core.wsfunctionnotavailable": "The web service function is not available.", - "core.year": "year", - "core.years": "years", - "core.yes": "Yes", - "core.youreoffline": "You are offline", - "core.youreonline": "You are back online" -} \ No newline at end of file diff --git a/src/assets/lang/es-mx.json b/src/assets/lang/es-mx.json deleted file mode 100644 index f9eb48f90..000000000 --- a/src/assets/lang/es-mx.json +++ /dev/null @@ -1,2176 +0,0 @@ -{ - "addon.badges.alignment": "Alineación", - "addon.badges.badgedetails": "Detalles de insignia", - "addon.badges.badges": "Insignias", - "addon.badges.bendorsement": "Aprobación (idoneidad)", - "addon.badges.claimcomment": "Comentario a la Aprobación (Idoneidad)", - "addon.badges.claimid": "URL de Pretensión", - "addon.badges.contact": "Contacto", - "addon.badges.dateawarded": "Fecha de emisión", - "addon.badges.expired": "Caducada", - "addon.badges.expirydate": "Fecha de caducidad", - "addon.badges.imageauthoremail": "Email del autor de la imagen", - "addon.badges.imageauthorname": "Nombre del autor de la imagen", - "addon.badges.imageauthorurl": "URL del autor de la imagen", - "addon.badges.imagecaption": "Leyenda de la imagen", - "addon.badges.issuancedetails": "Caducidad de insignia", - "addon.badges.issuerdetails": "Detalles del emisor", - "addon.badges.issueremail": "Email", - "addon.badges.issuername": "Nombre del emisor", - "addon.badges.issuerurl": "URL del emisor", - "addon.badges.language": "Idioma", - "addon.badges.noalignment": "Esta insignia no tiene ninguna habilidad externa o estándar especificado.", - "addon.badges.nobadges": "No hay insignias disponibles.", - "addon.badges.norelated": "La insignia no tiene ninguna insignia relacionada.", - "addon.badges.recipientdetails": "Detalles del destinatario", - "addon.badges.relatedbages": "Insignias relacionadas", - "addon.badges.version": "Versión", - "addon.badges.warnexpired": "(Esta insignia ha caducado)", - "addon.block_activitymodules.pluginname": "Actividades", - "addon.block_activityresults.pluginname": "Resultados de actividad", - "addon.block_badges.pluginname": "Insignias recientes", - "addon.block_blogmenu.pluginname": "Menú del blog", - "addon.block_blogrecent.pluginname": "Entradas de blog recientes", - "addon.block_blogtags.pluginname": "Marcas del blog", - "addon.block_calendarmonth.pluginname": "Calendario", - "addon.block_calendarupcoming.pluginname": "Eventos próximos", - "addon.block_comments.pluginname": "Comentarios", - "addon.block_completionstatus.pluginname": "Estatus de finalización del curso", - "addon.block_glossaryrandom.pluginname": "Entrada aleatoria del glosario", - "addon.block_learningplans.pluginname": "Planes de aprendizaje", - "addon.block_myoverview.all": "Todos (excepto quitados de la vista)", - "addon.block_myoverview.allincludinghidden": "Todos", - "addon.block_myoverview.favourites": "Destacados", - "addon.block_myoverview.future": "Futuros", - "addon.block_myoverview.hiddencourses": "Quitado de la vista", - "addon.block_myoverview.inprogress": "En progreso", - "addon.block_myoverview.lastaccessed": "Último accedido", - "addon.block_myoverview.morecourses": "Más cursos", - "addon.block_myoverview.nocourses": "Ningún curso", - "addon.block_myoverview.past": "Pasados", - "addon.block_myoverview.pluginname": "Vista general del curso", - "addon.block_myoverview.shortname": "Nombre corto", - "addon.block_myoverview.title": "Nombre del curso", - "addon.block_newsitems.pluginname": "Avisos recientes", - "addon.block_onlineusers.pluginname": "Usuarios en línea", - "addon.block_privatefiles.pluginname": "Archivos privados", - "addon.block_recentactivity.pluginname": "Actividad reciente", - "addon.block_recentlyaccessedcourses.nocourses": "Sin cursos recientes", - "addon.block_recentlyaccessedcourses.pluginname": "Cursos accedidos recientemente", - "addon.block_recentlyaccesseditems.noitems": "Sin ítems recientes", - "addon.block_recentlyaccesseditems.pluginname": "Ítems accedidos recientemente", - "addon.block_rssclient.pluginname": "Canales RSS remotos", - "addon.block_selfcompletion.pluginname": "Auto finalización", - "addon.block_sitemainmenu.pluginname": "Menú principal", - "addon.block_starredcourses.nocourses": "No hay cursos destacados", - "addon.block_starredcourses.pluginname": "Cursos destacados", - "addon.block_tags.pluginname": "Marcas", - "addon.block_timeline.duedate": "Fecha esperada", - "addon.block_timeline.next30days": "Próximos 30 días", - "addon.block_timeline.next3months": "Próximos 3 meses", - "addon.block_timeline.next6months": "Próximos 6 meses", - "addon.block_timeline.next7days": "Próximos 7 días", - "addon.block_timeline.nocoursesinprogress": "Sin cursos en progreso", - "addon.block_timeline.noevents": "Sin actividades próximas pendientes", - "addon.block_timeline.overdue": "Atrasados", - "addon.block_timeline.pluginname": "Línea de tiempo", - "addon.block_timeline.sortbycourses": "Ordenar por cursos", - "addon.block_timeline.sortbydates": "Ordenar por fechas", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Entradas del blog", - "addon.blog.errorloadentries": "Error al cargar entradas de bitácoras.", - "addon.blog.linktooriginalentry": "Enlace a la entrada de blog original", - "addon.blog.noentriesyet": "Entradas no visibles aquí", - "addon.blog.publishtonoone": "Usted (borrador)", - "addon.blog.publishtosite": "Todos en este sitio", - "addon.blog.publishtoworld": "Todo el mundo", - "addon.blog.showonlyyourentries": "Mostrar solamente entradas propias", - "addon.blog.siteblogheading": "Blog del sitio", - "addon.calendar.allday": "Todo el día", - "addon.calendar.calendar": "Calendario", - "addon.calendar.calendarevent": "Evento del calendario", - "addon.calendar.calendarevents": "Eventos del calendario", - "addon.calendar.calendarreminders": "Recordatorios de calendario", - "addon.calendar.categoryevents": "Eventos de categoría", - "addon.calendar.confirmeventdelete": "¿Está seguro de que desea eliminar el evento \"{{$a}}\" ?", - "addon.calendar.confirmeventseriesdelete": "El evento \"{{$a.name}}\" es parte de una serie. ¿ Quiere Usted eliminar solamente este evento, o todos los {{$a.count}} eventos en la serie ?", - "addon.calendar.courseevents": "Eventos de curso", - "addon.calendar.currentmonth": "Mes Actual", - "addon.calendar.daynext": "Día siguiente", - "addon.calendar.dayprev": "Día anterior", - "addon.calendar.defaultnotificationtime": "Hora de notificación por defecto", - "addon.calendar.deleteallevents": "Eliminar todos los eventos", - "addon.calendar.deleteevent": "Eliminar evento", - "addon.calendar.deleteoneevent": "Eliminar este evento", - "addon.calendar.durationminutes": "Duración en minutos", - "addon.calendar.durationnone": "Sin duración", - "addon.calendar.durationuntil": "Hasta", - "addon.calendar.editevent": "Editando evento", - "addon.calendar.errorloadevent": "Error al cargar evento.", - "addon.calendar.errorloadevents": "Error al cargar eventos.", - "addon.calendar.eventcalendareventdeleted": "Evento de calendario eliminado", - "addon.calendar.eventduration": "Duración", - "addon.calendar.eventendtime": "Hora final", - "addon.calendar.eventkind": "Tipo de evento", - "addon.calendar.eventname": "Nombre del evento", - "addon.calendar.eventstarttime": "Hora de comienzo", - "addon.calendar.eventtype": "Tipo de evento", - "addon.calendar.fri": "Vie", - "addon.calendar.friday": "Viernes", - "addon.calendar.gotoactivity": "Ir a la actividad", - "addon.calendar.groupevents": "Eventos de grupo", - "addon.calendar.invalidtimedurationminutes": "La duración en minutos introducida no es válida; por favor, introduzca una duración en minutos mayor que 0 o seleccione sin duración.", - "addon.calendar.invalidtimedurationuntil": "La fecha y hora seleccionadas para la duración hasta es anterior a la fecha de inicio del evento. Por favor, corríjalo antes de seguir.", - "addon.calendar.mon": "Lun", - "addon.calendar.monday": "Lunes", - "addon.calendar.monthlyview": "Vista mensual", - "addon.calendar.newevent": "Nuevo evento", - "addon.calendar.noevents": "No hay eventos", - "addon.calendar.nopermissiontoupdatecalendar": "Lo sentimos, pero Usted no tiene permisos para actualizar el evento del calendario.", - "addon.calendar.reminders": "Recordatorios", - "addon.calendar.repeatedevents": "Eventos repetidos", - "addon.calendar.repeateditall": "Aplicar los cambios a todos los eventos {{$a}} de la serie", - "addon.calendar.repeateditthis": "Aplicar los cambios sólo a este evento", - "addon.calendar.repeatevent": "Repetir este evento", - "addon.calendar.repeatweeksl": "Repetir semanalmente, creando juntos", - "addon.calendar.sat": "Sáb", - "addon.calendar.saturday": "Sábado", - "addon.calendar.setnewreminder": "Configurar un nuevo recordatorio", - "addon.calendar.siteevents": "Eventos del sitio", - "addon.calendar.sun": "Dom", - "addon.calendar.sunday": "Domingo", - "addon.calendar.thu": "Jue", - "addon.calendar.thursday": "Jueves", - "addon.calendar.today": "Hoy", - "addon.calendar.tomorrow": "Mañana", - "addon.calendar.tue": "Mar", - "addon.calendar.tuesday": "Martes", - "addon.calendar.typecategory": "evento de categoría", - "addon.calendar.typeclose": "Evento cerrar", - "addon.calendar.typecourse": "Evento de curso", - "addon.calendar.typedue": "Evento esperado", - "addon.calendar.typegradingdue": "Evento de calificación pendiente", - "addon.calendar.typegroup": "Evento de grupo", - "addon.calendar.typeopen": "Evento abrir", - "addon.calendar.typesite": "Evento de sitio", - "addon.calendar.typeuser": "Evento de usuario", - "addon.calendar.upcomingevents": "Eventos próximos", - "addon.calendar.userevents": "Eventos de usuario", - "addon.calendar.wed": "Mié", - "addon.calendar.wednesday": "Miércoles", - "addon.calendar.when": "Cuando", - "addon.calendar.yesterday": "Ayer", - "addon.competency.activities": "Actividades", - "addon.competency.competencies": "Competencias", - "addon.competency.competenciesmostoftennotproficientincourse": "Las competencias que con más frecuencia no están dominadas en este curso", - "addon.competency.coursecompetencies": "Competencias del curso", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Las valoraciones de competencia en este curso no afectan a los planes de aprendizaje.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Las valoraciones de competencia en este curso son actualizadas inmediatamente dentro de planes de aprendizaje.", - "addon.competency.crossreferencedcompetencies": "Competencias con referencias-cruzadas", - "addon.competency.duedate": "Fecha prometida/esperada", - "addon.competency.errornocompetenciesfound": "No se encontraron competencias", - "addon.competency.evidence": "Evidencia", - "addon.competency.evidence_competencyrule": "Se cumplió la regla de la competencia.", - "addon.competency.evidence_coursecompleted": "El curso '{{$a}}' fue completado.", - "addon.competency.evidence_coursemodulecompleted": "La actividad '{{$a}}' fue completada.", - "addon.competency.evidence_courserestored": "La valoración fue restaurada junto con el curso '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "La evidencia de aprendizaje previo '{{$a}}' fue enlazada.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "La evidencia de aprendizaje previo '{{$a}}' fue des-enlazada.", - "addon.competency.evidence_manualoverride": "La valoración de competencia fue configurada manualmente.", - "addon.competency.evidence_manualoverrideincourse": "La valoración de competencia fue configurada manualmente en el curso '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "La valoración de competencia fue configurada manualmente en el plan de aprendizaje '{{$a}}'.", - "addon.competency.learningplancompetencies": "Competencias del plan de aprendizaje", - "addon.competency.learningplans": "Planes de aprendizaje", - "addon.competency.myplans": "Mis planes de aprendizaje", - "addon.competency.noactivities": "Sin actividades", - "addon.competency.nocompetencies": "Sin competencias", - "addon.competency.nocompetenciesincourse": "No se han enlazado competencias a este curso.", - "addon.competency.nocrossreferencedcompetencies": "No se han referenciado cruzadamente otras competencias con esta competencia.", - "addon.competency.noevidence": "Sin evidencia", - "addon.competency.noplanswerecreated": "No se crearon planes de aprendizaje.", - "addon.competency.nouserplanswithcompetency": "Ningún plan de aprendizaje contiene esta competencia.", - "addon.competency.path": "Ruta:", - "addon.competency.planstatusactive": "Activa/o", - "addon.competency.planstatuscomplete": "Completo", - "addon.competency.planstatusdraft": "Borrador", - "addon.competency.planstatusinreview": "En revisión", - "addon.competency.planstatuswaitingforreview": "Esperando para revisión", - "addon.competency.proficient": "Dominio/pericia", - "addon.competency.progress": "Progreso", - "addon.competency.rating": "Valoración", - "addon.competency.reviewstatus": "Revisar estatus", - "addon.competency.status": "Estatus", - "addon.competency.template": "Plantilla de plan de aprendizaje", - "addon.competency.uponcoursecompletion": "Al completarse el curso:", - "addon.competency.usercompetencystatus_idle": "desocupado", - "addon.competency.usercompetencystatus_inreview": "En revisión", - "addon.competency.usercompetencystatus_waitingforreview": "Esperando para revisión", - "addon.competency.userplans": "Planes de aprendizaje", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} de un total de {{$a.y}} competencias se tienen dominadas", - "addon.competency.xcompetenciesproficientoutofyincourse": "Usted es capaz/perito/experto en {{$a.x}} de un total de {{$a.y}} competencias en este curso.", - "addon.coursecompletion.complete": "Completo", - "addon.coursecompletion.completecourse": "Completar curso", - "addon.coursecompletion.completed": "Completado", - "addon.coursecompletion.completiondate": "Fecha de finalización", - "addon.coursecompletion.completionmenuitem": "Finalización", - "addon.coursecompletion.couldnotloadreport": "No pudo cargarse el reporte de finalización del curso. Por favor inténtelo nuevamente más tarde.", - "addon.coursecompletion.coursecompletion": "Finalización del curso", - "addon.coursecompletion.criteria": "Criterios", - "addon.coursecompletion.criteriagroup": "Grupo de criterios", - "addon.coursecompletion.criteriarequiredall": "Son necesarios todos los criterios que aparecen más abajo", - "addon.coursecompletion.criteriarequiredany": "Es necesario cualquiera de los criterios que aparecen más abajo", - "addon.coursecompletion.inprogress": "En curso", - "addon.coursecompletion.manualselfcompletion": "Auto-finalizar manualmente", - "addon.coursecompletion.nottracked": "Usted actualmente no está siendo monitoreado en cuanto a su grado de avance de este curso", - "addon.coursecompletion.notyetstarted": "Aún no ha comenzado", - "addon.coursecompletion.pending": "Pendiente", - "addon.coursecompletion.required": "Obligatorio", - "addon.coursecompletion.requiredcriteria": "Criterios necesarios", - "addon.coursecompletion.requirement": "Requisito", - "addon.coursecompletion.status": "Estatus", - "addon.coursecompletion.viewcoursereport": "Ver reporte del curso", - "addon.files.couldnotloadfiles": "La lista de archivos no pudo cargarse.", - "addon.files.emptyfilelist": "No hay archivos para mostrar.", - "addon.files.erroruploadnotworking": "Desafortunadamente ahorita no es posible subir archivos a su sitio.", - "addon.files.files": "Archivos", - "addon.files.privatefiles": "Archivos privados", - "addon.files.sitefiles": "Archivos del sitio", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Configurar dispositivos", - "addon.messages.acceptandaddcontact": "Aceptar y añadir a contactos", - "addon.messages.addcontact": "Añadir contacto", - "addon.messages.addcontactconfirm": "¿Está seguro de querer añadir a {{$a}} a sus contactos?", - "addon.messages.addtofavourites": "Marcar con estrella la conversación", - "addon.messages.addtoyourcontacts": "Añadir a contactos", - "addon.messages.blocknoncontacts": "Bloquear mensajes de usuarios que no figuren en mi lista de contactos", - "addon.messages.blockuser": "Bloquear usuario", - "addon.messages.blockuserconfirm": "¿Está seguro de querer bloquear a {{$a}}?", - "addon.messages.contactableprivacy": "Aceptar mensaje de:", - "addon.messages.contactableprivacy_coursemember": "Mis contactos y cualquiera en mis cursos", - "addon.messages.contactableprivacy_onlycontacts": "Solamente mis contactos", - "addon.messages.contactableprivacy_site": "Cualquiera en el sitio", - "addon.messages.contactblocked": "Contacto bloqueado", - "addon.messages.contactlistempty": "La lista de contactos está vacía", - "addon.messages.contactname": "Nombre del contacto", - "addon.messages.contactrequestsent": "Solicitud de contacto enviada", - "addon.messages.contacts": "Contactos", - "addon.messages.conversationactions": "Menú de acciones para conversación", - "addon.messages.decline": "Declinar", - "addon.messages.deleteallconfirm": "¿Está usted seguro de querer eliminar toda esta conversación? Esto no la eliminará para otros participantes de la conversación", - "addon.messages.deleteallselfconfirm": "¿Está seguro de querer eliminar toda esta conversación personal?", - "addon.messages.deleteconversation": "Eliminar conversación", - "addon.messages.deleteforeveryone": "Borrar para mí y para todos los demás", - "addon.messages.deletemessage": "Eliminar mensaje", - "addon.messages.deletemessageconfirmation": "¿Está seguro de querer eliminar este mensaje? Será eliminado solamente de su historial de mensaje y todavía será visible por el usuario que envió o recibió el mensaje.", - "addon.messages.errordeletemessage": "Error al eliminar el mensaje", - "addon.messages.errorwhileretrievingcontacts": "Error al recuperar los contactos del servidor.", - "addon.messages.errorwhileretrievingdiscussions": "Error al recuperar las discusiones del servidor.", - "addon.messages.errorwhileretrievingmessages": "Error al recuperar mensajes del servidor.", - "addon.messages.errorwhileretrievingusers": "Error al recuperar usuarios del servidor.", - "addon.messages.groupconversations": "Grupo", - "addon.messages.groupinfo": "Información del grupo", - "addon.messages.individualconversations": "Privado", - "addon.messages.info": "Información del usuario", - "addon.messages.isnotinyourcontacts": "{{$a}} no está en sus contactos", - "addon.messages.message": "Mensaje", - "addon.messages.messagenotsent": "El mensaje no fue enviado. Por favor inténtelo nuevamente después.", - "addon.messages.messagepreferences": "Preferencias de Mensaje", - "addon.messages.messages": "Mensajes", - "addon.messages.muteconversation": "Enmudecer", - "addon.messages.mutedconversation": "Conversación silenciada", - "addon.messages.newmessage": "Nuevo mensaje", - "addon.messages.newmessages": "Nuevos mensajes", - "addon.messages.nocontactrequests": "Sin solicitudes de contacto", - "addon.messages.nocontactsgetstarted": "Sin contactos", - "addon.messages.nofavourites": "Sin conversaciones destacadas", - "addon.messages.nogroupconversations": "Sin conversaciones grupales", - "addon.messages.noindividualconversations": "Sin conversaciones privadas", - "addon.messages.nomessagesfound": "No se encontraron mensajes", - "addon.messages.noncontacts": "No-contactos", - "addon.messages.nousersfound": "No se encontraron usuarios", - "addon.messages.numparticipants": "{{$a}} participantes", - "addon.messages.removecontact": "Eliminar contacto", - "addon.messages.removecontactconfirm": "¿Está seguro de querer quitar a {{$a}} de sus contactos?", - "addon.messages.removefromfavourites": "Quitar la marca de estrella a la conversación", - "addon.messages.removefromyourcontacts": "Quitar de los contactos", - "addon.messages.requests": "Solicitudes", - "addon.messages.requirecontacttomessage": "Usted necesita solicitarle a {{$a}} que lo añada a Usted como contacto para poder mensajearle.", - "addon.messages.searchcombined": "Buscar personas y mensajes", - "addon.messages.selfconversation": "Espacio personal", - "addon.messages.selfconversationdefaultmessage": "Guardar borradores de mensajes, notas, etc, para acceder a ellos más tarde.", - "addon.messages.sendcontactrequest": "Enviar solicitud de contacto", - "addon.messages.showdeletemessages": "Mostrar eliminar mensajes", - "addon.messages.type_blocked": "Bloqueado", - "addon.messages.type_offline": "Fuera-de-línea", - "addon.messages.type_online": "En-línea", - "addon.messages.type_search": "Resultados de la búsqueda", - "addon.messages.type_strangers": "Otros", - "addon.messages.unabletomessage": "Usted no puede mensajear a este usuario", - "addon.messages.unblockuser": "Des-bloquear usuario", - "addon.messages.unblockuserconfirm": "¿Está seguro de querer des-bloquear a {{$a}}?", - "addon.messages.unmuteconversation": "Des-enmudecer", - "addon.messages.useentertosend": "Usar ENTER para enviar", - "addon.messages.useentertosenddescdesktop": "Si se deshabilita, Usted puede usar Ctrl + Enter para enviar el mensaje.", - "addon.messages.useentertosenddescmac": "Si se deshabilita, Usted puede usar Cmd + Enter para enviar el mensaje.", - "addon.messages.userwouldliketocontactyou": "{{$a}} quisiera contactarlo a Usted", - "addon.messages.warningconversationmessagenotsent": "No se pudo enviar mensaje(s) a conversación {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "No se pudo enviar mensaje(s) al usuario {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Quisiera contactarlo a Usted", - "addon.messages.you": "Usted:", - "addon.messages.youhaveblockeduser": "Usted ha bloqueado a este usuario.", - "addon.messages.yourcontactrequestpending": "Su solicitud de contacto está pendiente con {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Por favor acepte la declaratoria de envío.", - "addon.mod_assign.addattempt": "Permitir otro intento", - "addon.mod_assign.addnewattempt": "Añadir un nuevo intento", - "addon.mod_assign.addnewattemptfromprevious": "Añadir un nuevo intento basado en un envío anterior", - "addon.mod_assign.addsubmission": "Añadir envío", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Los detalles de la tarea y el formato de entrega estarán disponibles en {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Permitir envíos a partir de", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Esta tarea aceptará entregas de {{$a}}", - "addon.mod_assign.applytoteam": "Aplicar calificaciones y retroalimentación a todo el equipo", - "addon.mod_assign.assignmentisdue": "La tarea ha vencido", - "addon.mod_assign.attemptnumber": "Número de intento", - "addon.mod_assign.attemptreopenmethod": "Intentos reabiertos", - "addon.mod_assign.attemptreopenmethod_manual": "Manualmente", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automáticamente hasta pasar", - "addon.mod_assign.attemptsettings": "Configuraciones del intento", - "addon.mod_assign.cannoteditduetostatementsubmission": "Usted no puede añadir o editar un envío en la App porque no pudimos recuperar la declaratoria de envío del sitio.", - "addon.mod_assign.cannotgradefromapp": "Algunos métodos para calificar aun no están soportados por la App y no pueden ser modificados.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Usted no puede hacer un envío en la App porque no pudimos recuperar la declaratoria de envío del sitio.", - "addon.mod_assign.confirmsubmission": "¿Está Usted seguro de querer enviar su trabajo para que sea calificado? Usted ya no le podrá hacer más cambios", - "addon.mod_assign.currentattempt": "Éste es el intento {{$a}}.", - "addon.mod_assign.currentattemptof": "Éste es el intento {{$a.attemptnumber}} ( {{$a.maxattempts}} intentos permitidos ).", - "addon.mod_assign.currentgrade": "Calificación actual en el libro", - "addon.mod_assign.cutoffdate": "Fecha de corte", - "addon.mod_assign.defaultteam": "Grupo por defecto", - "addon.mod_assign.duedate": "Fecha de entrega", - "addon.mod_assign.duedateno": "No hay fecha de entrega", - "addon.mod_assign.duedatereached": "La fecha de vencimiento de esta tarea ya ha pasado", - "addon.mod_assign.editingstatus": "Estatus de edición", - "addon.mod_assign.editsubmission": "Editar envío", - "addon.mod_assign.erroreditpluginsnotsupported": "Usted no puede añadir o editar un envío en la App porque algunos plugins aun no están soportados para editar.", - "addon.mod_assign.errorshowinginformation": "No se puede mostrar la información del envío.", - "addon.mod_assign.extensionduedate": "Fecha de entrega extendida", - "addon.mod_assign.feedbacknotsupported": "La retroalimentación no está soportada por la App y es posible que no contenga toda la información.", - "addon.mod_assign.grade": "Calificación", - "addon.mod_assign.graded": "Calificado", - "addon.mod_assign.gradedby": "Calificado por", - "addon.mod_assign.gradedfollowupsubmit": "Calificada - envío posterior recibido", - "addon.mod_assign.gradedon": "Calificado en", - "addon.mod_assign.gradelocked": "La calificación está bloqueada o anulada en el libro de calificaciones.", - "addon.mod_assign.gradenotsynced": "Calificación no sincronizada", - "addon.mod_assign.gradeoutof": "Calificación sobre {{$a}}", - "addon.mod_assign.gradingstatus": "Estatus de calificación", - "addon.mod_assign.groupsubmissionsettings": "Configuraciones de envío de grupo", - "addon.mod_assign.hiddenuser": "Participante", - "addon.mod_assign.latesubmissions": "Envíos retrasados", - "addon.mod_assign.latesubmissionsaccepted": "Permitido hasta {{$a}}", - "addon.mod_assign.markingworkflowstate": "Estado del flujograma calificador", - "addon.mod_assign.markingworkflowstateinmarking": "En calificación", - "addon.mod_assign.markingworkflowstateinreview": "En revisión", - "addon.mod_assign.markingworkflowstatenotmarked": "No calificado", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Listo para liberar", - "addon.mod_assign.markingworkflowstatereadyforreview": "Calificación completada", - "addon.mod_assign.markingworkflowstatereleased": "Liberada", - "addon.mod_assign.modulenameplural": "Tareas", - "addon.mod_assign.multipleteams": "Miembro de más de un grupo", - "addon.mod_assign.multipleteams_desc": "Esta tarea requiere envío en grupo. Usted es miembro de más de un grupo. Para poder enviar, Usted debe de ser miembro de exactamente un grupo. Por favor, póngase en contacto con su profesor para actualizar su membresía de grupo.", - "addon.mod_assign.noattempt": "Sin intento", - "addon.mod_assign.nomoresubmissionsaccepted": "Solamente permitido para participantes que hayan recibido una extensión", - "addon.mod_assign.noonlinesubmissions": "Esta tarea no requiere que usted envíe nada de forma online", - "addon.mod_assign.nosubmission": "No se ha enviado nada en esta tarea", - "addon.mod_assign.notallparticipantsareshown": "Los participantes que no hayan hecho un envío no son mostrados.", - "addon.mod_assign.noteam": "No es miembro de ningún grupo", - "addon.mod_assign.noteam_desc": "Esta tarea requiere envío en grupos. Usted no es miembro de ningún grupo, por lo que no puede crear un envío Por favor, póngase en contacto con su profesor para ser añadido a un grupo.", - "addon.mod_assign.notgraded": "No calificado", - "addon.mod_assign.numberofdraftsubmissions": "Borradores", - "addon.mod_assign.numberofparticipants": "Participantes", - "addon.mod_assign.numberofsubmissionsneedgrading": "Necesita calificarse", - "addon.mod_assign.numberofsubmittedassignments": "Enviados", - "addon.mod_assign.numberofteams": "Grupos", - "addon.mod_assign.numwords": "{{$a}} palabras", - "addon.mod_assign.outof": "{{$a.current}} de un total de {{$a.total}}", - "addon.mod_assign.overdue": "La Tarea está retrasada por: {{$a}}", - "addon.mod_assign.submission": "Entrega", - "addon.mod_assign.submissioneditable": "Los estudiantes pueden editar este envío", - "addon.mod_assign.submissionnoteditable": "Los estudiantes no pueden editar este envío", - "addon.mod_assign.submissionnotsupported": "Este envío no es soportado por la App y es posible que no contenga toda la información", - "addon.mod_assign.submissionslocked": "Esta tarea no acepta entregas", - "addon.mod_assign.submissionstatus": "Estatus de la entrega", - "addon.mod_assign.submissionstatus_": "Sin entrega", - "addon.mod_assign.submissionstatus_draft": "Borrador (no enviado)", - "addon.mod_assign.submissionstatus_marked": "Calificado", - "addon.mod_assign.submissionstatus_new": "Sin envío", - "addon.mod_assign.submissionstatus_reopened": "Reabierto", - "addon.mod_assign.submissionstatus_submitted": "Enviado para calificar", - "addon.mod_assign.submissionstatusheading": "Estatus de la entrega", - "addon.mod_assign.submissionteam": "Grupo", - "addon.mod_assign.submitassignment": "Enviar tarea", - "addon.mod_assign.submitassignment_help": "Una vez que esta tarea se haya enviado, usted no podrá hacerle más cambios", - "addon.mod_assign.submittedearly": "La tarea fue enviada {{$a}} antes", - "addon.mod_assign.submittedlate": "La tarea fue enviada {{$a}} después", - "addon.mod_assign.timemodified": "Última modificación", - "addon.mod_assign.timeremaining": "Tiempo restante", - "addon.mod_assign.ungroupedusers": "La configuración de 'Requerir grupo para hacer envío' está activada y algunos usuarios, o no son miembros de ningún grupo, o son miembros de más de un grupo, por lo que no pueden hacer envíos.", - "addon.mod_assign.ungroupedusersoptional": "La configuración 'Estudiantes envían en grupos' está habilitada y algunos usuarios, o no son miembros de ningún grupo, o son miembros de más de un grupo. Por favor tenga en cuenta que estos estudiantes enviarán como miembros del 'Grupo predeterminado'.", - "addon.mod_assign.unlimitedattempts": "Ilimitados", - "addon.mod_assign.userswhoneedtosubmit": "Usuarios que necesitan enviar: {{$a}}", - "addon.mod_assign.userwithid": "Usuario con ID {{id}}", - "addon.mod_assign.viewsubmission": "Ver entrega", - "addon.mod_assign.warningsubmissiongrademodified": "La calificación por el envío fue modificada en el sitio.", - "addon.mod_assign.warningsubmissionmodified": "El envío del usuario fue modificado en el sitio.", - "addon.mod_assign.wordlimit": "Límite de palabras", - "addon.mod_assign_feedback_comments.pluginname": "Comentarios de retroalimentación", - "addon.mod_assign_feedback_editpdf.pluginname": "Anotación PDF", - "addon.mod_assign_feedback_file.pluginname": "Retroalimentación por archivo", - "addon.mod_assign_submission_comments.pluginname": "Comentarios al envío", - "addon.mod_assign_submission_file.pluginname": "Envíos de archivo", - "addon.mod_assign_submission_onlinetext.pluginname": "Envíos de texto en línea", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "El límite de palabras para esta tarea es de {{$a.limit}} palabras, y Usted está intentando enviar {{$a.count}} palabras. Por favor, revise su envío e inténtelo de nuevo.", - "addon.mod_book.errorchapter": "Error al leer capítulo del libro.", - "addon.mod_book.modulenameplural": "Libros", - "addon.mod_book.navnexttitle": "Siguiente: {{$a}}", - "addon.mod_book.navprevtitle": "Anterior: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Capítulos de libro", - "addon.mod_book.toc": "Tabla de Contenidos", - "addon.mod_chat.beep": "Bip", - "addon.mod_chat.chatreport": "Sesiones", - "addon.mod_chat.currentusers": "Usuarios", - "addon.mod_chat.enterchat": "Entrar a la sala", - "addon.mod_chat.entermessage": "Escriba su mensaje", - "addon.mod_chat.errorwhileconnecting": "Error al conectarse al chat.", - "addon.mod_chat.errorwhilegettingchatdata": "Error al obtener datos del chat.", - "addon.mod_chat.errorwhilegettingchatusers": "Error al obtener usuarios del chat.", - "addon.mod_chat.errorwhileretrievingmessages": "Error al recuperar mensajes del servidor.", - "addon.mod_chat.errorwhilesendingmessage": "Error al enviar el mensaje.", - "addon.mod_chat.messagebeepseveryone": "¡{{$a}} envía un beep a todos!", - "addon.mod_chat.messagebeepsyou": "¡{{$a}} le acaba de enviar un beep!", - "addon.mod_chat.messageenter": "{{$a}} recién entró a la sala", - "addon.mod_chat.messageexit": "{{$a}} salió de la sala", - "addon.mod_chat.messages": "Mensajes", - "addon.mod_chat.messageyoubeep": "Usted hizo señal de sonido bip {{$a}}", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "Usted debe de estar en-línea para enviar mensajes.", - "addon.mod_chat.nomessages": "Aún no hay mensajes", - "addon.mod_chat.nosessionsfound": "No se encontraron sesiones", - "addon.mod_chat.saidto": "dicho a", - "addon.mod_chat.send": "Enviar", - "addon.mod_chat.sessionstart": "La siguiente sesión de chat comenzará en {{$a.date}}, dentro de ({{$a.fromnow}} a partir de ahora)", - "addon.mod_chat.showincompletesessions": "Mostrar sesiones incompletas", - "addon.mod_chat.talk": "Charla", - "addon.mod_chat.viewreport": "Ver las sesiones anteriores", - "addon.mod_choice.cannotsubmit": "Lo sentimos, hubo un problema al enviar su eleccción. Por favor, inténtelo de nuevo.", - "addon.mod_choice.choiceoptions": "Opciones de la elección", - "addon.mod_choice.errorgetchoice": "Error al obtener datos de elección.", - "addon.mod_choice.expired": "Esta actividad se cerró el {{$a}}.", - "addon.mod_choice.full": "(Lleno)", - "addon.mod_choice.modulenameplural": "Elecciones", - "addon.mod_choice.noresultsviewable": "Los resultados no pueden verse en este momento.", - "addon.mod_choice.notopenyet": "Esta actividad no estará disponible hasta {{$a}}.", - "addon.mod_choice.numberofuser": "Número de respuestas", - "addon.mod_choice.numberofuserinpercentage": "Porcentaje de respuestas", - "addon.mod_choice.previewonly": "Esto es solamente una vista previa de las opciones disponibles para esta actividad. Usted no podrá enviar su elección hasta {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Los resultados anónimos serán publicados después de su respuesta.", - "addon.mod_choice.publishinfoanonclose": "Los resultados anónimos serán publicados después de que la actividad sea cerrada.", - "addon.mod_choice.publishinfofullafter": "Resultados completos, mostrando los opciones de todos, serán publicados después de su respuesta.", - "addon.mod_choice.publishinfofullclose": "Resultados completos, mostrando los opciones de todos, serán publicados después de que la actividad sea cerrada.", - "addon.mod_choice.publishinfonever": "Los resultados de esta actividad no serán publicados después de que Usted responda.", - "addon.mod_choice.removemychoice": "Eliminar mi elección", - "addon.mod_choice.responses": "Respuestas", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% de los usuarios eligió la opción: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Vista de Gráfica", - "addon.mod_choice.resultsnotsynced": "Su última respuesta debe ser sincronizada antes de que sea incluida en los resultados.", - "addon.mod_choice.savemychoice": "Guardar mi elección", - "addon.mod_choice.userchoosethisoption": "Usuarios que eligen esta opción", - "addon.mod_choice.yourselection": "Su selección", - "addon.mod_data.addentries": "Añadir entradas", - "addon.mod_data.advancedsearch": "Búsqueda avanzada", - "addon.mod_data.alttext": "Texto alternativo", - "addon.mod_data.approve": "Aprobar", - "addon.mod_data.approved": "Aprobado", - "addon.mod_data.ascending": "Ascendente", - "addon.mod_data.authorfirstname": "Nombre del autor", - "addon.mod_data.authorlastname": "Apellidos del autor", - "addon.mod_data.confirmdeleterecord": "¿Está seguro de que desea eliminar esta entrada?", - "addon.mod_data.descending": "Descendente", - "addon.mod_data.disapprove": "Deshacer aprobación", - "addon.mod_data.edittagsnotsupported": "Lo sentimos, las marcas de edición no están soportadas por el App.", - "addon.mod_data.emptyaddform": "¡No ha rellenado ningún campo!", - "addon.mod_data.entrieslefttoadd": "Debe añadir {{$a.entriesleft}} entrada(s) más antes de poder ver las entradas de otro participante.", - "addon.mod_data.entrieslefttoaddtoview": "Debe añadir {{$a.entrieslefttoview}} entrada(s) antes de poder ver las entradas de otros participantes.", - "addon.mod_data.errorapproving": "Error al aprobar o des_aprobar una entrada.", - "addon.mod_data.errordeleting": "Error al eliminar entrada.", - "addon.mod_data.errormustsupplyvalue": "Usted debe proporcionar un valor aquí.", - "addon.mod_data.expired": "Lo sentimos, esta actividad se cerró el {{$a}} y ya no está disponible", - "addon.mod_data.fields": "Campos", - "addon.mod_data.foundrecords": "Registros encontrados: {{$a.num}}/{{$a.max}} (Reset filters)", - "addon.mod_data.gettinglocation": "Obteniendo localización", - "addon.mod_data.latlongboth": "Tanto la Latitud como la Longitud son necesarias.", - "addon.mod_data.locationpermissiondenied": "Se ha negado el permiso para acceder a su localización", - "addon.mod_data.menuchoose": "Seleccionar...", - "addon.mod_data.modulenameplural": "Bases de datos", - "addon.mod_data.more": "Más", - "addon.mod_data.mylocation": "Mi localización", - "addon.mod_data.nomatch": "No se han encontrado entradas", - "addon.mod_data.norecords": "No entradas en la base de datos", - "addon.mod_data.notapproved": "La entrada aún no ha sido aprobada.", - "addon.mod_data.notopenyet": "Lo sentimos, esta actividad no está disponible hasta {{$a}}", - "addon.mod_data.numrecords": "{{$a}} entradas", - "addon.mod_data.other": "Otro", - "addon.mod_data.recordapproved": "Entrada aprobada", - "addon.mod_data.recorddeleted": "Entrada eliminada", - "addon.mod_data.recorddisapproved": "Entrada desaprobada", - "addon.mod_data.resetsettings": "Restablecer filtros", - "addon.mod_data.search": "Buscar", - "addon.mod_data.searchbytagsnotsupported": "Lo sentimos, las marcas para búsqueda no están soportados por la App.", - "addon.mod_data.selectedrequired": "Se requieren todos los seleccionados", - "addon.mod_data.single": "Ver individual", - "addon.mod_data.tagarea_data_records": "Registros de datos", - "addon.mod_data.timeadded": "Tiempo añadido", - "addon.mod_data.timemodified": "Tiempo modificado", - "addon.mod_data.usedate": "Incluir en búsqueda.", - "addon.mod_feedback.analysis": "Análisis", - "addon.mod_feedback.anonymous": "Anónima", - "addon.mod_feedback.anonymous_entries": "Respuestas anónimas ({{$a}})", - "addon.mod_feedback.average": "Promedio", - "addon.mod_feedback.captchaofflinewarning": "La retroalimentación con CAPTCHA no puede ser completada fuera de línea, o si no está configurada, ni con el servidor caído.", - "addon.mod_feedback.complete_the_form": "Contestar las preguntas", - "addon.mod_feedback.completed_feedbacks": "Respuestas enviadas", - "addon.mod_feedback.continue_the_form": "Continuar contestando las preguntas", - "addon.mod_feedback.feedback_is_not_open": "La retroalimentación no está disponible", - "addon.mod_feedback.feedback_submitted_offline": "Esta retroalimentación ha sido guardada para enviarse más tarde.", - "addon.mod_feedback.feedbackclose": "Permitir respuestas a", - "addon.mod_feedback.feedbackopen": "Permitir respuestas de", - "addon.mod_feedback.mapcourses": "Asignar retroalimentación a cursos", - "addon.mod_feedback.maximal": "Máximo", - "addon.mod_feedback.minimal": "Mínimo", - "addon.mod_feedback.mode": "Modo", - "addon.mod_feedback.modulenameplural": "Retroalimentaciones", - "addon.mod_feedback.next_page": "Siguiente página", - "addon.mod_feedback.non_anonymous": "Los nombres de los usuarios se mostrarán y registrarán con las respuestas", - "addon.mod_feedback.non_anonymous_entries": "Entradas no anónimas ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Estudiantes no respondientes ({{$a}})", - "addon.mod_feedback.not_selected": "No seleccionada", - "addon.mod_feedback.not_started": "No comenzado", - "addon.mod_feedback.numberoutofrange": "Número fuera del rango", - "addon.mod_feedback.overview": "Vista general", - "addon.mod_feedback.page_after_submit": "Página a mostrar tras la terminación del envío", - "addon.mod_feedback.preview": "Vista previa", - "addon.mod_feedback.previous_page": "Página anterior", - "addon.mod_feedback.questions": "Preguntas", - "addon.mod_feedback.response_nr": "Respuesta número", - "addon.mod_feedback.responses": "Respuestas", - "addon.mod_feedback.save_entries": "Enviar sus respuestas", - "addon.mod_feedback.show_entries": "Mostrar respuestas", - "addon.mod_feedback.show_nonrespondents": "Mostrar no respondientes", - "addon.mod_feedback.started": "Comenzado", - "addon.mod_feedback.this_feedback_is_already_submitted": "Usted ya ha finalizado esta actividad.", - "addon.mod_folder.emptyfilelist": "No hay archivos para mostrar.", - "addon.mod_folder.modulenameplural": "Carpetas (folders)", - "addon.mod_forum.addanewdiscussion": "Añadir un nuevo tópico/tema de discusión aquí", - "addon.mod_forum.addanewquestion": "Añadir una nueva pregunta", - "addon.mod_forum.addanewtopic": "Añadir un nuevo tópico/tema", - "addon.mod_forum.addtofavourites": "Marcar con estrella esta discusión", - "addon.mod_forum.advanced": "Avanzada", - "addon.mod_forum.cannotadddiscussion": "Para agregar discusiones a este foro se requiere pertenecer al grupo.", - "addon.mod_forum.cannotadddiscussionall": "No tiene permiso para añadir un nuevo tópico/tema de discusión para todos los participantes.", - "addon.mod_forum.cannotcreatediscussion": "No se pudo crear una discusión nueva", - "addon.mod_forum.couldnotadd": "No se puede colocar su mensaje debido a un problema desconocido.", - "addon.mod_forum.couldnotupdate": "No se ha podido actualizar su mensaje debido a un error desconocido.", - "addon.mod_forum.cutoffdatereached": "La fecha de corte para publicar a este foro se ha alcanzado, por lo que Usted ya no puede publicar en él.", - "addon.mod_forum.delete": "Eliminar", - "addon.mod_forum.deletedpost": "El mensaje se ha borrado", - "addon.mod_forum.deletesure": "¿Está seguro de que desea borrar este mensaje?", - "addon.mod_forum.discussion": "Discusión", - "addon.mod_forum.discussionlistsortbycreatedasc": "Ordenar por la fecha de creación en orden ascendente", - "addon.mod_forum.discussionlistsortbycreateddesc": "Ordenar por la fecha de creación en orden descendente", - "addon.mod_forum.discussionlistsortbylastpostasc": "Ordenar por la fecha de creación de la última publicación en orden ascendente", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Ordenar por la fecha de creación de la última publicación en orden descendente", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Ordenar por el número de respuestas en orden ascendente", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Ordenar por el número de respuestas en orden descendente", - "addon.mod_forum.discussionlocked": "Esta discusión ha sido bloqueada, por lo que Usted ya no puede responder a ella.", - "addon.mod_forum.discussionpinned": "Fijada", - "addon.mod_forum.discussionsubscription": "Suscripción a discusión", - "addon.mod_forum.edit": "Editar", - "addon.mod_forum.erroremptymessage": "El mensaje no puede estar vacío", - "addon.mod_forum.erroremptysubject": "El asunto del mensaje no puede estar vacío.", - "addon.mod_forum.errorgetforum": "Error al obtener datos del foro.", - "addon.mod_forum.errorgetgroups": "Error al obtener configuraciones del grupo.", - "addon.mod_forum.errorposttoallgroups": "No se pudo crear nueva discusión en todos los grupos.", - "addon.mod_forum.favouriteupdated": "Su opción de estrella ha sido actualizada.", - "addon.mod_forum.forumnodiscussionsyet": "Todavía no hay tópicos de discusión en este foro.", - "addon.mod_forum.group": "Grupo", - "addon.mod_forum.lastpost": "Último mensaje", - "addon.mod_forum.lockdiscussion": "Bloquear esta discusión", - "addon.mod_forum.lockupdated": "La opción para bloquear ha sido actualizada.", - "addon.mod_forum.message": "Mensaje", - "addon.mod_forum.modeflatnewestfirst": "Ordenar desde el más reciente", - "addon.mod_forum.modeflatoldestfirst": "Ordenar desde el más antiguo", - "addon.mod_forum.modenested": "Mostrar respuestas anidadas", - "addon.mod_forum.modulenameplural": "Foros", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discusiones", - "addon.mod_forum.numreplies": "{{numreplies}} respuestas", - "addon.mod_forum.pindiscussion": "Fijar esta discusión", - "addon.mod_forum.pinupdated": "La opción de fijación ha sido actualizada.", - "addon.mod_forum.postisprivatereply": "Esta es una contestación privada. No es visible para otros participantes.", - "addon.mod_forum.posttoforum": "Enviar al foro", - "addon.mod_forum.posttomygroups": "Publicar una copia a todos los grupos", - "addon.mod_forum.privatereply": "Responder en privado", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Refrescar discusiones", - "addon.mod_forum.refreshposts": "Refrescar publicaciones", - "addon.mod_forum.removefromfavourites": "Quitar estrella de esta discusión", - "addon.mod_forum.reply": "Responder (réplica)", - "addon.mod_forum.replyplaceholder": "Escriba su respuesta...", - "addon.mod_forum.subject": "Asunto", - "addon.mod_forum.tagarea_forum_posts": "Publicaciones en foro", - "addon.mod_forum.thisforumhasduedate": "La fecha esperada para publicar a este foro es {{$a}}.", - "addon.mod_forum.thisforumisdue": "La fecha esperada para publicar a este foro era {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Des-bloquear esta discusión", - "addon.mod_forum.unpindiscussion": "Quitar fijación de esta discusión", - "addon.mod_forum.unread": "No leído", - "addon.mod_forum.unreadpostsnumber": "{{$a}} mensaje(s) no leído(s)", - "addon.mod_forum.yourreply": "Su respuesta (réplica)", - "addon.mod_glossary.addentry": "Añadir una nueva entrada", - "addon.mod_glossary.aliases": "Palabra(s) clave", - "addon.mod_glossary.attachment": "Adjunto", - "addon.mod_glossary.browsemode": "Ver entradas", - "addon.mod_glossary.byalphabet": "Alfabéticamente", - "addon.mod_glossary.byauthor": "Agrupar por autor", - "addon.mod_glossary.bycategory": "Agrupar por categoría", - "addon.mod_glossary.bynewestfirst": "El más nuevo primero", - "addon.mod_glossary.byrecentlyupdated": "Recientemente actualizado", - "addon.mod_glossary.bysearch": "Buscar", - "addon.mod_glossary.cannoteditentry": "No puede editarse entrada", - "addon.mod_glossary.casesensitive": "Esta entrada es en MAYÚSCULAS y minúsculas", - "addon.mod_glossary.categories": "Categorías", - "addon.mod_glossary.concept": "Concepto", - "addon.mod_glossary.definition": "Definición", - "addon.mod_glossary.entriestobesynced": "Entradas pendientes de ser sincronizadas", - "addon.mod_glossary.entrypendingapproval": "Esta entrada está pendiente de aprobación.", - "addon.mod_glossary.entryusedynalink": "Esta entrada será enlazada automáticamente", - "addon.mod_glossary.errconceptalreadyexists": "Este concepto ya existe. En este glosario no se permiten duplicados.", - "addon.mod_glossary.errorloadingentries": "Ocurrió un error al cargar entradas.", - "addon.mod_glossary.errorloadingentry": "Ocurrió un error al cargar la entrada.", - "addon.mod_glossary.errorloadingglossary": "Ocurrió un error al cargar el glosario.", - "addon.mod_glossary.fillfields": "Los campos Concepto y Definición son obligatorios.", - "addon.mod_glossary.fullmatch": "Sólo enlazar palabras completas", - "addon.mod_glossary.linking": "Auto-enlace", - "addon.mod_glossary.modulenameplural": "Glosarios", - "addon.mod_glossary.noentriesfound": "No se encontraron entradas.", - "addon.mod_glossary.searchquery": "Consulta de búsqueda", - "addon.mod_glossary.tagarea_glossary_entries": "Entradas del glosario", - "addon.mod_h5pactivity.all_attempts": "Todos los intentos del usuario", - "addon.mod_h5pactivity.answer_checked": "Respuesta revisada", - "addon.mod_h5pactivity.answer_correct": "Su respuesta es correcta", - "addon.mod_h5pactivity.answer_fail": "Respuesta incorrecta", - "addon.mod_h5pactivity.answer_incorrect": "Su respuesta es incorrecta", - "addon.mod_h5pactivity.answer_pass": "Respuesta correcta", - "addon.mod_h5pactivity.attempt": "Intento", - "addon.mod_h5pactivity.attempt_completion_no": "Este intento no está marcado como completado", - "addon.mod_h5pactivity.attempt_completion_yes": "Este intento está completado", - "addon.mod_h5pactivity.attempt_success_fail": "Reprobado", - "addon.mod_h5pactivity.attempt_success_pass": "Aprobado", - "addon.mod_h5pactivity.attempt_success_unknown": "No reportado", - "addon.mod_h5pactivity.attempts_none": "Este usuario no tiene intentos para mostrar.", - "addon.mod_h5pactivity.completion": "Finalización", - "addon.mod_h5pactivity.downloadh5pfile": "Descargar archivo H5P", - "addon.mod_h5pactivity.duration": "Duración", - "addon.mod_h5pactivity.errorgetactivity": "Error al obtener datos de actividad H5P", - "addon.mod_h5pactivity.filestatenotdownloaded": "El paquete H5P no está descargado. Usted necesita descargarlo para poder usarlo.", - "addon.mod_h5pactivity.filestateoutdated": "El paquete H5P ha sido modificado desde la última descarga. Usted necesita descargarlo nuevamente para poder usarlo.", - "addon.mod_h5pactivity.maxscore": "Puntaje máximo", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Mis intentos", - "addon.mod_h5pactivity.no_compatible_track": "Esta interacción ({{$a}}) no proporciona información de seguimiento o el seguimiento\n proporcionado no es compatible con la versión actual de la actividad.", - "addon.mod_h5pactivity.offlinedisabledwarning": "Usted necesita estar en línea para ver el paquete H5P.", - "addon.mod_h5pactivity.outcome": "Resultado", - "addon.mod_h5pactivity.previewmode": "Este contenido es mostrado en modo previsualización. No se almacenará ningún monitoreo del intento.", - "addon.mod_h5pactivity.result_fill-in": "Texto a rellenar", - "addon.mod_h5pactivity.result_other": "Tipo desconocido de interacción", - "addon.mod_h5pactivity.review_my_attempts": "Ver mis intentos", - "addon.mod_h5pactivity.score": "Puntaje", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} de {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Fecha de inicio", - "addon.mod_h5pactivity.totalscore": "Puntaje total", - "addon.mod_h5pactivity.viewattempt": "Ver intento {{$a}}", - "addon.mod_imscp.deploymenterror": "¡Error en paquete de contenidos!", - "addon.mod_imscp.modulenameplural": "Paquetes de contenido IMS", - "addon.mod_imscp.showmoduledescription": "Mostrar descripción", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "Respuesta", - "addon.mod_lesson.attempt": "Intento: {{$a}}", - "addon.mod_lesson.attemptheader": "Intento", - "addon.mod_lesson.attemptsremaining": "Tiene {{$a}} intento(s) restante(s)", - "addon.mod_lesson.averagescore": "Puntuación promedio", - "addon.mod_lesson.averagetime": "Tiempo promedio", - "addon.mod_lesson.branchtable": "Contenido", - "addon.mod_lesson.cannotfindattempt": "Error: no se pudo encontrar el intento", - "addon.mod_lesson.cannotfinduser": "Error: no se pudieron encontrar los usuarios", - "addon.mod_lesson.clusterjump": "Pregunta no vista dentro de un cluster", - "addon.mod_lesson.completed": "Finalizado", - "addon.mod_lesson.congratulations": "Enhorabuena, ha llegado al final de la lección", - "addon.mod_lesson.continue": "Continuar", - "addon.mod_lesson.continuetonextpage": "Continuar a página siguiente", - "addon.mod_lesson.defaultessayresponse": "Su ensayo será calificado por su profesor.", - "addon.mod_lesson.detailedstats": "Estadísticas detalladas", - "addon.mod_lesson.didnotanswerquestion": "No ha contestado a esta pregunta.", - "addon.mod_lesson.displayofgrade": "Mostrar calificación (sólo para estudiantes)", - "addon.mod_lesson.displayscorewithessays": "Usted ha obtenido una puntuación de {{$a.score}} sobre {{$a.tempmaxgrade}} para las preguntas calificadas automáticamente.
            Su(s) {{$a.essayquestions}} pregunta(s) de su ensayo serán calificadas y añadidas
            a su calificación final en una fecha posterior.

            Su calificación actual sin contar esa(s) pregunta(s) es de is {{$a.score}} sobre {{$a.grade}}.

            ", - "addon.mod_lesson.displayscorewithoutessays": "Su puntuación es {{$a.score}} (sobre {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "La contraseña no puede estar vacía", - "addon.mod_lesson.enterpassword": "Por favor, escriba la contraseña:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "No ha contestado a ninguna pregunta. En esta lección ha obtenido 0 puntos.", - "addon.mod_lesson.errorprefetchrandombranch": "Esta lección contiene un salto hacia una página aleatoria de contenido. No puede ser intentada en la App hasta que haya sido comenzada en un navegador web.", - "addon.mod_lesson.errorreviewretakenotlast": "Este intento ya no puede ser revisado más porque se ha terminado otro intento.", - "addon.mod_lesson.finish": "Terminar", - "addon.mod_lesson.finishretakeoffline": "Este intento fue terminado fuera-de-línea.", - "addon.mod_lesson.firstwrong": "Lo sentimos, usted ha contestado incorrectamente. ¡Le gustaría volver a intentar la pregunta de nuevo? (si Usted contesta ahora la pregunta correctamente, no contará hacia su puntaje final).", - "addon.mod_lesson.gotoendoflesson": "Ir al final de lección", - "addon.mod_lesson.grade": "Calificación", - "addon.mod_lesson.highscore": "Calificación máxima", - "addon.mod_lesson.hightime": "Tiempo máximo", - "addon.mod_lesson.leftduringtimed": "Se ha interrumpido una lección con tiempo fijo.
            Por favor, haga clic en Continuar para volver a empezar la lección.", - "addon.mod_lesson.leftduringtimednoretake": "Se ha interrumpido una lección con tiempo fijo y
            no se permite volver a empezar o continuar la lección.", - "addon.mod_lesson.lessonmenu": "Menú Lección", - "addon.mod_lesson.lessonstats": "Estadísticas de la lección", - "addon.mod_lesson.linkedmedia": "Medios enlazados", - "addon.mod_lesson.loginfail": "Ingreso fallido, por favor pruebe de nuevo...", - "addon.mod_lesson.lowscore": "Puntuación baja", - "addon.mod_lesson.lowtime": "Tiempo mínimo", - "addon.mod_lesson.maximumnumberofattemptsreached": "Se ha alcanzado el número máximo de intentos. Traslado a la página siguiente", - "addon.mod_lesson.modattemptsnoteacher": "La revisión del estudiante sólo está disponible para los estudiantes.", - "addon.mod_lesson.modulenameplural": "Lecciones", - "addon.mod_lesson.noanswer": "Una o más preguntas no tienen respuesta dada. Por favor regrese y envíe una respuesta.", - "addon.mod_lesson.nolessonattempts": "No se han hecho intentos de esta lección.", - "addon.mod_lesson.nolessonattemptsgroup": "No se han hecho intentos por miembros del grupo {{$a}} en esta lección.", - "addon.mod_lesson.notcompleted": "Sin finalizar", - "addon.mod_lesson.numberofcorrectanswers": "Número de respuestas correctas: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Número de preguntas contestadas: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Número de preguntas contestadas: {{$a.nquestions}} (Usted debería contestar al menos {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Esta es una lección de {{$a.score}} puntos. Usted ha obtenido {{$a.score}} punto(s) sobre {{$a.currenthigh}} hasta ahora.", - "addon.mod_lesson.ongoingnormal": "Usted ha respondido correctamente {{$a.correct}} pregunta(s) de un total de {{$a.viewed}} pregunta(s).", - "addon.mod_lesson.or": "O", - "addon.mod_lesson.overview": "Visión general", - "addon.mod_lesson.preview": "Previsualizar", - "addon.mod_lesson.progressbarteacherwarning2": "Usted no verá la barra de progreso porque puede editar esta lección", - "addon.mod_lesson.progresscompleted": "Usted ha completado {{$a}}% de la lección", - "addon.mod_lesson.question": "Pregunta", - "addon.mod_lesson.rawgrade": "Calificación en bruto", - "addon.mod_lesson.reports": "Reportes", - "addon.mod_lesson.response": "Comentario", - "addon.mod_lesson.retakefinishedinsync": "Un intento fuera de línea fue sincronizado.¿Quiere revisarlo?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Revisión", - "addon.mod_lesson.reviewlesson": "Revisar lección", - "addon.mod_lesson.reviewquestionback": "Sí, me gustaría probar de nuevo", - "addon.mod_lesson.reviewquestioncontinue": "No, deseo pasar a la siguiente", - "addon.mod_lesson.secondpluswrong": "No. ¿Desea probar de nuevo?", - "addon.mod_lesson.submit": "Enviar", - "addon.mod_lesson.teacherjumpwarning": "Un salto {{$a.cluster}} o {{$a.unseen}} se está usando en esta lección. En su lugar se usará el salto a la página siguiente. Ingrese como estudiante para probar estos saltos.", - "addon.mod_lesson.teacherongoingwarning": "La puntuación acumulada sólo se muestra al estudiante. Ingrese como estudiante para probar la puntuación acumulada.", - "addon.mod_lesson.teachertimerwarning": "El temporizador sólo funciona con estudiantes. Entre como estudiante para probar el temporizador.", - "addon.mod_lesson.thatsthecorrectanswer": "Esta es la respuesta correcta", - "addon.mod_lesson.thatsthewronganswer": "Esta es la respuesta equivocada", - "addon.mod_lesson.timeremaining": "Tiempo restante", - "addon.mod_lesson.timetaken": "Tiempo empleado", - "addon.mod_lesson.unseenpageinbranch": "Pregunta no vista dentro de una página de conenidos", - "addon.mod_lesson.warningretakefinished": "Este intento fue terminado en el sitio.", - "addon.mod_lesson.welldone": "¡Bien hecho!", - "addon.mod_lesson.youhaveseen": "Usted ya ha visto más de una página de esta lección.
            ¿Desea comenzar desde la última página vista?", - "addon.mod_lesson.youranswer": "Su respuesta", - "addon.mod_lesson.yourcurrentgradeisoutof": "Su calificación actual es {{$a.grade}} sobre {{$a.total}}", - "addon.mod_lesson.youshouldview": "Usted debería ver como mínimo: {{$a}}", - "addon.mod_lti.errorgetlti": "Error al obtener datos del módulo", - "addon.mod_lti.errorinvalidlaunchurl": "La URL a lanzar no es válida.", - "addon.mod_lti.launchactivity": "Lanzar la actividad", - "addon.mod_lti.modulenameplural": "Herramientas externas", - "addon.mod_page.errorwhileloadingthepage": "Error al cargar el contenido de la página.", - "addon.mod_page.modulenameplural": "Páginas", - "addon.mod_quiz.answercolon": "Respuesta:", - "addon.mod_quiz.attemptfirst": "Primer intento", - "addon.mod_quiz.attemptlast": "Último intento", - "addon.mod_quiz.attemptnumber": "Intento", - "addon.mod_quiz.attemptquiznow": "Comenzar el examen ya", - "addon.mod_quiz.attemptstate": "Estado", - "addon.mod_quiz.canattemptbutnotsubmit": "Usted puede intentar este examen en la App, pero Usted necesita enviar el intento en un navegador de internet por las razones siguientes :", - "addon.mod_quiz.cannotsubmitquizdueto": "Este intento de examen no puede enviarse por las siguientes razones:", - "addon.mod_quiz.clearchoice": "Borrar mi elección", - "addon.mod_quiz.comment": "Comentario", - "addon.mod_quiz.completedon": "Finalizado en", - "addon.mod_quiz.confirmclose": "Una vez que envíe el examen, no podrá cambiar sus respuestas para este intento.", - "addon.mod_quiz.confirmcontinueoffline": "ESte intento no se ha sincronizado desde {{$a}}. Si Usted ha continuado este intento en otro dispositivo después de esta hora, Usted podría perder datos.", - "addon.mod_quiz.confirmleavequizonerror": "Ocurrió un error al guardar las respuestas. ¿Está seguro de querer abandonar el examen?", - "addon.mod_quiz.confirmstart": "Su examen tendrá un límite de tiempo de {{$a}}. Cuando Usted empieza su intento, el contador comenzará a contar y no puede ser pausado. Usted debe terminar su intento antes de que expire. ¿Está Usted seguro de querer comenzar ahorita?", - "addon.mod_quiz.confirmstartheader": "Límite de tiempo", - "addon.mod_quiz.connectionerror": "Se perdió la conexión de red. (Falló auto-guardar).\n\nTome nota de todas las respuestas escritas en esta página en los últimos minutos, y después, intente re-conectar.\n\nUna vez que se haya restablecido la reconexión, sus respuestas deberían de guardarse y este mensaje desaparecerá.", - "addon.mod_quiz.continueattemptquiz": "Continuar el último intento", - "addon.mod_quiz.continuepreview": "Continuar la previsualización anterior", - "addon.mod_quiz.errorbehaviournotsupported": "Este examen no puede intentarse en la App porque el comportamiento de pregunta no está soportado por la App:", - "addon.mod_quiz.errordownloading": "Error al descargar datos necesarios.", - "addon.mod_quiz.errorgetattempt": "Error al obtener datos del intento.", - "addon.mod_quiz.errorgetquestions": "Error al obtener preguntas", - "addon.mod_quiz.errorgetquiz": "Error al obtener datos del examen", - "addon.mod_quiz.errorparsequestions": "Ocurrió un error al leer las preguntas. Por favor intente este examen en un navegador web.", - "addon.mod_quiz.errorquestionsnotsupported": "Este examen no puede intentarse en la App porque solamente contiene preguntas no soportadas por la App:", - "addon.mod_quiz.errorrulesnotsupported": "Este examen no puede intentarse en la App porque tiene reglas de acceso no soportadas por la App:", - "addon.mod_quiz.errorsaveattempt": "Ocurrió un error al guardar los datos del intento.", - "addon.mod_quiz.feedback": "Comentario de retroalimentación", - "addon.mod_quiz.finishattemptdots": "Terminar intento ...", - "addon.mod_quiz.finishnotsynced": "Terminado pero no sincronizado", - "addon.mod_quiz.grade": "Calificación", - "addon.mod_quiz.gradeaverage": "Promedio de calificaciones", - "addon.mod_quiz.gradehighest": "Calificación más alta", - "addon.mod_quiz.grademethod": "Método de calificación", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Puntos", - "addon.mod_quiz.modulenameplural": "Exámenes", - "addon.mod_quiz.mustbesubmittedby": "Este intento debe ser enviado para {{$a}}.", - "addon.mod_quiz.noquestions": "Aún no se han agregado preguntas", - "addon.mod_quiz.noreviewattempt": "Usted no tiene permiso para revisar este intento.", - "addon.mod_quiz.notyetgraded": "Sin calificar aún", - "addon.mod_quiz.opentoc": "Abrir ventanilla de navegación.", - "addon.mod_quiz.outof": "{{$a.grade}} de un total de {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} de un total de {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Retroalimentación global según calificación", - "addon.mod_quiz.overdue": "Vencido", - "addon.mod_quiz.overduemustbesubmittedby": "Este intento actualmente está vencido. Usted ya debería de haberlo enviado. Si Usted pretende que le califiquen este examen, debe de enviarlo a más tardar antes de {{$a}}.Si no lo envía para entonces, no se le contarán puntos de calificación para este intento.", - "addon.mod_quiz.preview": "Vista previa", - "addon.mod_quiz.previewquiznow": "Previsualizar el examen ahora", - "addon.mod_quiz.question": "Pregunta", - "addon.mod_quiz.quiznavigation": "Navegación dentro del examen", - "addon.mod_quiz.quizpassword": "Contraseña del examen", - "addon.mod_quiz.reattemptquiz": "Reintentar el examen", - "addon.mod_quiz.requirepasswordmessage": "Para contestar este examen necesita conocer la contraseña", - "addon.mod_quiz.returnattempt": "Regresar al intento", - "addon.mod_quiz.review": "Revisión", - "addon.mod_quiz.reviewofattempt": "Revisión del intento {{$a}}", - "addon.mod_quiz.reviewofpreview": "Revisión de la vista previa", - "addon.mod_quiz.showall": "Mostrar todas las preguntas en una página", - "addon.mod_quiz.showeachpage": "Mostrar una página cada vez", - "addon.mod_quiz.startattempt": "Iniciar intento", - "addon.mod_quiz.startedon": "Comenzado en", - "addon.mod_quiz.stateabandoned": "Nunca enviados", - "addon.mod_quiz.statefinished": "Terminados", - "addon.mod_quiz.statefinisheddetails": "Enviado {{$a}}", - "addon.mod_quiz.stateinprogress": "En progreso", - "addon.mod_quiz.stateoverdue": "Atrasados", - "addon.mod_quiz.stateoverduedetails": "Debe ser enviado para {{$a}}", - "addon.mod_quiz.status": "Estatus", - "addon.mod_quiz.submitallandfinish": "Enviar todo y terminar", - "addon.mod_quiz.summaryofattempt": "Resumen del intento", - "addon.mod_quiz.summaryofattempts": "Resumen de sus intentos previos", - "addon.mod_quiz.timeleft": "Tiempo restante", - "addon.mod_quiz.timetaken": "Tiempo empleado", - "addon.mod_quiz.warningattemptfinished": "El intento fuera de línea fue descartado porque fue terminado en el sitio o no fue encontrado.", - "addon.mod_quiz.warningdatadiscarded": "Algunas respuestas fuera de línea fueron descartas porque las preguntas fueron modificadas en línea.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "El intento no fue terminado porque algunas respuestas fuera de línea fueron descartadas. Por favor, revise sus respuestas y entonces envíe el intento nuevamente.", - "addon.mod_quiz.warningquestionsnotsupported": "Este examen contiene preguntas no soportadas por la App:", - "addon.mod_quiz.yourfinalgradeis": "Su calificación final en este examen es {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Error al cargar el contenido.", - "addon.mod_resource.modifieddate": "Modificado {{$a}}", - "addon.mod_resource.modulenameplural": "Archivos", - "addon.mod_resource.openthefile": "Abrir el archivo", - "addon.mod_resource.uploadeddate": "Subido {{$a}}", - "addon.mod_scorm.asset": "Recurso", - "addon.mod_scorm.assetlaunched": "Recurso - Visto", - "addon.mod_scorm.attempts": "Intentos", - "addon.mod_scorm.averageattempt": "Intentos promedio", - "addon.mod_scorm.browse": "Vista previa", - "addon.mod_scorm.browsed": "Navegado", - "addon.mod_scorm.browsemode": "Modo de presentación preliminar", - "addon.mod_scorm.cannotcalculategrade": "No pudo calcularse la calificación.", - "addon.mod_scorm.completed": "Finalizado", - "addon.mod_scorm.contents": "Contenido", - "addon.mod_scorm.dataattemptshown": "Estos datos pertenecen al intento número {{number}}.", - "addon.mod_scorm.enter": "Entrar", - "addon.mod_scorm.errorcreateofflineattempt": "Ocurrió un error al crear un nuevo intento fuera de línea. Por favor, inténtelo nuevamente.", - "addon.mod_scorm.errordownloadscorm": "Error al descargar SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Error al obtener datos del SCORM", - "addon.mod_scorm.errorinvalidversion": "Lo sentimos, la aplicación solamente soporta SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "La descarga de paquetes SCORM está deshabilitada. Por favor, póngase en contacto con su administrador del sitio.", - "addon.mod_scorm.errornovalidsco": "Este paquete SCORM no tiene un SCO visible para cargar.", - "addon.mod_scorm.errorpackagefile": "Lo sentimos, la aplicación solamente soporta paquetes ZIP.", - "addon.mod_scorm.errorsyncscorm": "Ocurrió un error al sincronizar. Por favor, inténtelo nuevamente.", - "addon.mod_scorm.exceededmaxattempts": "Ha alcanzado el número máximo de intentos", - "addon.mod_scorm.failed": "Error", - "addon.mod_scorm.firstattempt": "Primer intento", - "addon.mod_scorm.gradeaverage": "Calificación promedio", - "addon.mod_scorm.gradeforattempt": "Calificación del intento", - "addon.mod_scorm.gradehighest": "Calificación más alta", - "addon.mod_scorm.grademethod": "Método de calificación", - "addon.mod_scorm.gradereported": "Calificación informada", - "addon.mod_scorm.gradescoes": "Objetos de aprendizaje", - "addon.mod_scorm.gradesum": "Calificaciones sumadas", - "addon.mod_scorm.highestattempt": "Intento más alto", - "addon.mod_scorm.incomplete": "Incompleto", - "addon.mod_scorm.lastattempt": "Último intento", - "addon.mod_scorm.modulenameplural": "Paquetes SCORM", - "addon.mod_scorm.newattempt": "Comenzar un nuevo intento", - "addon.mod_scorm.noattemptsallowed": "Número de intentos permitidos", - "addon.mod_scorm.noattemptsmade": "Número de intentos realizados", - "addon.mod_scorm.notattempted": "No se ha intentado", - "addon.mod_scorm.offlineattemptnote": "Este intento tiene datos que no han sido sincronizados.", - "addon.mod_scorm.offlineattemptovermax": "Este itento no puede enviarse porque Usted sobrepasó el número máximo de intentos.", - "addon.mod_scorm.organizations": "Organizaciones", - "addon.mod_scorm.passed": "Pasado", - "addon.mod_scorm.reviewmode": "Modo Revisión", - "addon.mod_scorm.score": "Puntuación", - "addon.mod_scorm.scormstatusnotdownloaded": "Este paquete SCORM no está descargado. Será descargado automáticamente cuando Usted lo abra.", - "addon.mod_scorm.scormstatusoutdated": "Este paquete SCORM ha sido modificado desde la última descarga. Será descargado automáticamente cuando Usted lo abra.", - "addon.mod_scorm.suspended": "Suspendido", - "addon.mod_scorm.toc": "TOC (Tabla de Contenidos)", - "addon.mod_scorm.warningofflinedatadeleted": "Algunos datos fuera-de-línea del intento {{number}} han sido eliminados porque no pudo contarse como un nuevo intento.", - "addon.mod_scorm.warningsynconlineincomplete": "Algunos intentos no pudieron ser sincronizados con el sitio debido a que el último intento en línea todavía no está terminado. Por favor, termine primeramente el intento en línea.", - "addon.mod_survey.cannotsubmitsurvey": "Lo sentimos, hubo un error al enviar su encuesta. Por favor, inténtelo nuevamente.", - "addon.mod_survey.errorgetsurvey": "Error al obtener datos de la encuesta.", - "addon.mod_survey.ifoundthat": "Encontrado:", - "addon.mod_survey.ipreferthat": "Prefiero esto", - "addon.mod_survey.modulenameplural": "Encuestas", - "addon.mod_survey.responses": "Respuestas", - "addon.mod_survey.results": "Resultados", - "addon.mod_survey.surveycompletednograph": "Usted ha completado esta encuesta.", - "addon.mod_url.accessurl": "Acceder a la URL", - "addon.mod_url.modulenameplural": "URLs", - "addon.mod_url.pointingtourl": "URL a donde apunta este recurso.", - "addon.mod_wiki.cannoteditpage": "Usted no puede editar esta página", - "addon.mod_wiki.createpage": "Crear Página", - "addon.mod_wiki.editingpage": "Editando esta página '{{$a}}", - "addon.mod_wiki.errorloadingpage": "Ocurrió un error al cargar la página.", - "addon.mod_wiki.errornowikiavailable": "Este wiki todavía no tiene ningún contenido.", - "addon.mod_wiki.gowikihome": "Ir a la primera página del wiki", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Página nueva", - "addon.mod_wiki.newpagetitle": "Título nuevo de la página", - "addon.mod_wiki.nocontent": "No hay contenido para esta página", - "addon.mod_wiki.notingroup": "No está en un grupo", - "addon.mod_wiki.pageexists": "Esta página ya existe.", - "addon.mod_wiki.pagename": "Nombre de página", - "addon.mod_wiki.subwiki": "Sub-wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Paginas de wiki", - "addon.mod_wiki.titleshouldnotbeempty": "El título no debería de estar vacío", - "addon.mod_wiki.viewpage": "Ver página", - "addon.mod_wiki.wikipage": "Página de wiki", - "addon.mod_wiki.wrongversionlock": "Otro usuario ha editado esta página mientras usted estaba editando: su contenido es obsoleto.", - "addon.mod_workshop.alreadygraded": "Ya calificada", - "addon.mod_workshop.areainstructauthors": "Instrucciones para el envío", - "addon.mod_workshop.areainstructreviewers": "Instrucciones para la valoración", - "addon.mod_workshop.assess": "Evaluar", - "addon.mod_workshop.assessedsubmission": "Envío evaluado", - "addon.mod_workshop.assessmentform": "Formato de valoración", - "addon.mod_workshop.assessmentsettings": "Configuración de la valoración", - "addon.mod_workshop.assessmentstrategynotsupported": "Estrategia de evaluación {{$a}} no soportada", - "addon.mod_workshop.assessmentweight": "Ponderación de la valoración", - "addon.mod_workshop.assignedassessments": "Envíos asignados para evaluar", - "addon.mod_workshop.assignedassessmentsnone": "No tiene envíos asignados para evaluar", - "addon.mod_workshop.conclusion": "Conclusión", - "addon.mod_workshop.createsubmission": "Añadir envío", - "addon.mod_workshop.deletesubmission": "Eliminar envío", - "addon.mod_workshop.editsubmission": "Editar lo enviado", - "addon.mod_workshop.feedbackauthor": "Retroalimentación para el autor", - "addon.mod_workshop.feedbackby": "Retroalimentación por {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Retroalimentación para el revisor", - "addon.mod_workshop.givengrades": "Calificaciones otorgadas", - "addon.mod_workshop.gradecalculated": "Calificación calculada para el envío", - "addon.mod_workshop.gradeinfo": "Calificación: {{$a.received}} of {{$a.max}}", - "addon.mod_workshop.gradeover": "Pasar por alto calificación del envío", - "addon.mod_workshop.gradesreport": "Reporte de calificaciones del Taller", - "addon.mod_workshop.gradinggrade": "Calificación por valoración", - "addon.mod_workshop.gradinggradecalculated": "Calificación calculada por valoración", - "addon.mod_workshop.gradinggradeof": "Calificación por valoración (de {{$a}})", - "addon.mod_workshop.gradinggradeover": "Anular calificación por valoración", - "addon.mod_workshop.modulenameplural": "Talleres", - "addon.mod_workshop.nogradeyet": "Aún no hay calificación", - "addon.mod_workshop.notassessed": "Aún no evaluado", - "addon.mod_workshop.notoverridden": "No anulado", - "addon.mod_workshop.noyoursubmission": "Usted aún no ha enviado su trabajo", - "addon.mod_workshop.overallfeedback": "Retroalimentación global", - "addon.mod_workshop.publishedsubmissions": "Envíos publicados", - "addon.mod_workshop.publishsubmission": "Publicar envío", - "addon.mod_workshop.publishsubmission_help": "Los envíos publicados estarán disponibles para los demás cuando el taller esté cerrado.", - "addon.mod_workshop.reassess": "Re-evaluar", - "addon.mod_workshop.receivedgrades": "Calificaciones recibidas", - "addon.mod_workshop.submissionattachment": "Adjunto", - "addon.mod_workshop.submissioncontent": "Contenido del envío", - "addon.mod_workshop.submissiondeleteconfirm": "¿Está Usted seguro de querer eliminar el envío siguiente?", - "addon.mod_workshop.submissiongrade": "Calificación por el envío", - "addon.mod_workshop.submissiongradeof": "Calificación por el envío (de {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Usted necesita ingresar texto aquí o subir un archivo", - "addon.mod_workshop.submissionrequiredtitle": "Usted necesita ingresar un título.", - "addon.mod_workshop.submissionsreport": "Reporte de envíos de taller", - "addon.mod_workshop.submissiontitle": "Título", - "addon.mod_workshop.switchphase10": "Cambiar a la fase de configuración", - "addon.mod_workshop.switchphase20": "Cambiar a la fase de envío", - "addon.mod_workshop.switchphase30": "Cambiar a la fase de valoración", - "addon.mod_workshop.switchphase40": "Cambiar a la fase de evaluación", - "addon.mod_workshop.switchphase50": "Cerrar taller", - "addon.mod_workshop.userplan": "Planificador de taller", - "addon.mod_workshop.userplancurrentphase": "Fase actual", - "addon.mod_workshop.warningassessmentmodified": "El envío fue modificado en el sitio.", - "addon.mod_workshop.warningsubmissionmodified": "La evaluación fue modificada en el sitio.", - "addon.mod_workshop.weightinfo": "Ponderación: {{$a}}", - "addon.mod_workshop.yourassessment": "Su valoración", - "addon.mod_workshop.yourassessmentfor": "Su valoración para {{$a}}", - "addon.mod_workshop.yourgrades": "Sus calificaciones", - "addon.mod_workshop.yoursubmission": "Su envío", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Comentario para {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Calificación para {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspecto {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Tiene que elegir una calificación para este aspecto", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Comentario para {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspecto {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Comentario para {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Calificación para {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Afirmación {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Criterio {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Tiene que seleccionar uno de estos ítems", - "addon.notes.addnewnote": "Añadir una nota nueva", - "addon.notes.coursenotes": "Anotaciones del curso", - "addon.notes.deleteconfirm": "¿Eliminar esta nota?", - "addon.notes.eventnotecreated": "Nota creada", - "addon.notes.eventnotedeleted": "Nota eliminada", - "addon.notes.nonotes": "No hay anotaciones de este tipo aún", - "addon.notes.note": "Nota", - "addon.notes.notes": "Anotaciones", - "addon.notes.personalnotes": "Anotaciones personales", - "addon.notes.publishstate": "Contexto", - "addon.notes.sitenotes": "Anotaciones del sitio", - "addon.notes.userwithid": "Usuario con Id {{id}}", - "addon.notes.warningnotenotsent": "No se pudo añadir anotación(es) al curso {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Error al obtener notificaciones.", - "addon.notifications.markallread": "Marcar todo como leído", - "addon.notifications.notificationpreferences": "Preferencias de notificación", - "addon.notifications.notifications": "Notificaciones", - "addon.notifications.playsound": "Reproducir sonido", - "addon.notifications.therearentnotificationsyet": "No hay notificaciones.", - "addon.storagemanager.deletecourse": "Descargar datos de todo el curso", - "addon.storagemanager.deletecourses": "Descargar todos los datos del curso", - "addon.storagemanager.deletedatafrom": "Descargar datos de {{name}}", - "addon.storagemanager.info": "Los archivos almacenados en su dispositivo hacen que la App funcione más rápido y le permiten a la App el ser usada fuera de línea. Usted puede quitar archivos con seguridad si usted necesita liberar espacio de almacenamiento.", - "addon.storagemanager.managestorage": "Gestionar almacenamiento", - "addon.storagemanager.storageused": "Almacenamiento de archivo usado:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Emiratos Árabes Unidos", - "assets.countries.AF": "Afganistán", - "assets.countries.AG": "Antigua y Barbuda", - "assets.countries.AI": "Anguila", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antártida", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa Americana", - "assets.countries.AT": "Austria", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Islas Åland", - "assets.countries.AZ": "Azerbaiyán", - "assets.countries.BA": "Bosnia y Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Bélgica", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrein", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "San Bartolomé", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei Darussalam", - "assets.countries.BO": "Bolivia (Estado Plurinacional de)", - "assets.countries.BQ": "Bonaire, Sint Eustatius And Saba", - "assets.countries.BR": "Brasil", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bután", - "assets.countries.BV": "Islas Bouvet", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Bielorrusia", - "assets.countries.BZ": "Belice", - "assets.countries.CA": "Canadá", - "assets.countries.CC": "Islas Cocos", - "assets.countries.CD": "Congo (la República Democrática del)", - "assets.countries.CF": "República Centroafricana", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Suiza", - "assets.countries.CI": "Costa de Marfil", - "assets.countries.CK": "Islas Cook", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Camerún", - "assets.countries.CN": "China", - "assets.countries.CO": "Colombia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Cabo Verde", - "assets.countries.CW": "Curazao", - "assets.countries.CX": "Islas de Navidad", - "assets.countries.CY": "Chipre", - "assets.countries.CZ": "República Checa", - "assets.countries.DE": "Alemania", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Dinamarca", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "República Dominicana", - "assets.countries.DZ": "Argelia", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estonia", - "assets.countries.EG": "Egipto", - "assets.countries.EH": "Sahara Occidental", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "España", - "assets.countries.ET": "Etiopía", - "assets.countries.FI": "Finlandia", - "assets.countries.FJ": "Fiyi", - "assets.countries.FK": "Islas Malvinas", - "assets.countries.FM": "Micronesia (Estados Federados de)", - "assets.countries.FO": "Islas Faroe", - "assets.countries.FR": "Francia", - "assets.countries.GA": "Gabón", - "assets.countries.GB": "Reino Unido", - "assets.countries.GD": "Granada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Guyana Francesa", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Groenlandia", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadalupe", - "assets.countries.GQ": "Guinea Ecuatorial", - "assets.countries.GR": "Grecia", - "assets.countries.GS": "Georgia del Sur y las Islas Sandwich del Sur", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Islas Heard y McDonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Croacia", - "assets.countries.HT": "Haití", - "assets.countries.HU": "Hungría", - "assets.countries.ID": "Indonesia", - "assets.countries.IE": "Irlanda", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Isla de Man", - "assets.countries.IN": "India", - "assets.countries.IO": "Territorio Británico del Océano Índico", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Irán (República Islámica de)", - "assets.countries.IS": "Islandia", - "assets.countries.IT": "Italia", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordania", - "assets.countries.JP": "Japón", - "assets.countries.KE": "Kenia", - "assets.countries.KG": "Kirguistán", - "assets.countries.KH": "Camboya", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comoras", - "assets.countries.KN": "San Cristóbal y Nevis", - "assets.countries.KP": "Corea (la República Democrática del Pueblo de)", - "assets.countries.KR": "Corea (la República de)", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Islas Caimán", - "assets.countries.KZ": "Kazajstán", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Líbano", - "assets.countries.LC": "Santa Lucía", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lituania", - "assets.countries.LU": "Luxemburgo", - "assets.countries.LV": "Latvia", - "assets.countries.LY": "Libia", - "assets.countries.MA": "Marruecos", - "assets.countries.MC": "Mónaco", - "assets.countries.MD": "Moldovia (la República de)", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "San Martín (Porción Francesa)", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Islas Marshall", - "assets.countries.MK": "Macedonia del Norte", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Islas Marianas del Norte", - "assets.countries.MQ": "Martinica", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauricio", - "assets.countries.MV": "Maldivas", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "México", - "assets.countries.MY": "Malasia", - "assets.countries.MZ": "Mozambique", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Nueva Caledonia", - "assets.countries.NE": "Níger", - "assets.countries.NF": "Islas Norfolk", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Holanda", - "assets.countries.NO": "Noruega", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Naurú", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nueva Zelandia", - "assets.countries.OM": "Omán", - "assets.countries.PA": "Panamá", - "assets.countries.PE": "Perú", - "assets.countries.PF": "Polinesia Francesa", - "assets.countries.PG": "Papúa Nueva Guinea", - "assets.countries.PH": "Filipinas", - "assets.countries.PK": "Pakistán", - "assets.countries.PL": "Polonia", - "assets.countries.PM": "San Pedro y Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestina, Estado de", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Reunión", - "assets.countries.RO": "Rumania", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Federación Rusa", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Arabia Saudita", - "assets.countries.SB": "Islas Salomón", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudán", - "assets.countries.SE": "Suecia", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Santa Helena, Ascención y Tristan Da Cunha", - "assets.countries.SI": "Eslovenia", - "assets.countries.SJ": "Svalbard y Jan Mayen", - "assets.countries.SK": "Eslovaquia", - "assets.countries.SL": "Sierra Leona", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalía", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Sudán del Sur", - "assets.countries.ST": "Santo Tomé y Príncipe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (Porción Holandesa)", - "assets.countries.SY": "Siria", - "assets.countries.SZ": "Eswatini", - "assets.countries.TC": "Islas Turcas y Caicos", - "assets.countries.TD": "Chad", - "assets.countries.TF": "Territorios Franceses del Sur", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tailandia", - "assets.countries.TJ": "Tayikistán", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkmenistán", - "assets.countries.TN": "Túnez", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turquía", - "assets.countries.TT": "Trinidad y Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwán", - "assets.countries.TZ": "Tanzania, la República Unida de", - "assets.countries.UA": "Ucrania", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Islas Ultramarinas Menores de Estados Unidos", - "assets.countries.US": "Estados Unidos", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "El Vaticano", - "assets.countries.VC": "San Vincente y Las Granadinas", - "assets.countries.VE": "Venezuela (República Bolivariana de)", - "assets.countries.VG": "Islas Vírgenes (Británícas)", - "assets.countries.VI": "Islas Vírgenes (Americanas)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis y Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Sudáfrica", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbawe", - "assets.mimetypes.application/epub_zip": "Ebook formato EPUB", - "assets.mimetypes.application/msword": "Documento Word", - "assets.mimetypes.application/pdf": "Documento PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Respaldo Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Hoja de cálculo Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Libro_de_trabajo Excel 2007 con macros habilitados", - "assets.mimetypes.application/vnd.ms-powerpoint": "Presentación PowerPoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Hoja de cálculo de OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Plantilla de hoja de cálculo de OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Documento de Texto de OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Plantilla de Texto de OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Plantilla de Página Web de OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Presentación PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Pase de diapositivas PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Hoja de cálculo Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Plantilla Excel 20007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Documento Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Presentación iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Hoja de cálculo iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Documento de iWork Pages", - "assets.mimetypes.application/x-javascript": "Fuente JavaScript", - "assets.mimetypes.application/x-mspublisher": "Documento de Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animación Flash", - "assets.mimetypes.application/xhtml_xml": "Documento XHTML", - "assets.mimetypes.archive": "Archivo ({{$a.EXT}})", - "assets.mimetypes.audio": "Archivo de audio ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Archivo", - "assets.mimetypes.group:archive": "Archivos de archivo", - "assets.mimetypes.group:audio": "Archivos de audio", - "assets.mimetypes.group:document": "Archivos de documento", - "assets.mimetypes.group:html_audio": "Archivos de audio soportados nativamente por navegadores", - "assets.mimetypes.group:html_track": "Archivos de pista HTML", - "assets.mimetypes.group:html_video": "Archivos de video soportados nativamente por navegadores", - "assets.mimetypes.group:image": "Archivos de imagen", - "assets.mimetypes.group:presentation": "Archivos de presentación", - "assets.mimetypes.group:sourcecode": "Código fuente", - "assets.mimetypes.group:spreadsheet": "Archivos de hoja de cálculo", - "assets.mimetypes.group:video": "Archivos de video", - "assets.mimetypes.group:web_audio": "Archivos de audio usados en la web", - "assets.mimetypes.group:web_file": "Archivos web", - "assets.mimetypes.group:web_image": "Archivos de imagen usados en la web", - "assets.mimetypes.group:web_video": "Archivos de video usados en la web", - "assets.mimetypes.image": "Imagen ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Ícono de Windows", - "assets.mimetypes.text/css": "Hoja de Estilo en Cascada", - "assets.mimetypes.text/csv": "Valores separados por coma", - "assets.mimetypes.text/html": "Documento HTML", - "assets.mimetypes.text/plain": "Archivo de texto", - "assets.mimetypes.text/rtf": "Documento RTF", - "assets.mimetypes.text/vtt": "Pista de Texto de Video Web", - "assets.mimetypes.video": "Archivo de video ({{$a.EXT}})", - "core.accounts": "Cuentas", - "core.add": "Agregar", - "core.agelocationverification": "Verificación de ubicación y edad", - "core.ago": "hace {{$a}}", - "core.all": "Todos", - "core.allgroups": "Todos los grupos", - "core.allparticipants": "Todos los participantes", - "core.answer": "Respuesta", - "core.answered": "Contestadas", - "core.areyousure": "¿Está Usted seguro?", - "core.back": "Atrás", - "core.block.blocks": "Bloques", - "core.browser": "Navegador", - "core.cancel": "Cancelar", - "core.cannotconnect": "No se puede conectar", - "core.cannotconnecttrouble": "Estamos teniendo problemas para conectarnos a su sitio.", - "core.cannotconnectverify": "Por favor revise que la dirección sea correcta.", - "core.cannotdownloadfiles": "La descarga de archivos está deshabilitada. Por favor póngase en contacto con su administrador del sitio.", - "core.captureaudio": "Grabar audio", - "core.capturedimage": "Foto tomada.", - "core.captureimage": "Tomar foto", - "core.capturevideo": "Grabar video", - "core.category": "Categoría", - "core.choose": "Elegir", - "core.choosedots": "Elegir...", - "core.clearsearch": "Limpiar búsqueda", - "core.clicktohideshow": "Clic para expandir o colapsar", - "core.clicktoseefull": "Hacer clic para ver los contenidos completos.", - "core.close": "Cerrar", - "core.comments": "Comentarios", - "core.comments.addcomment": "Agregar un comentario...", - "core.comments.comments": "Comentarios", - "core.comments.commentscount": "Comentarios ({{$a}})", - "core.comments.commentsnotworking": "Los comentarios no pueden ser recuperados", - "core.comments.deletecommentbyon": "Eliminar comentario publicado por {{$a.user}} en {{$a.time}}", - "core.comments.eventcommentcreated": "Comentario creado", - "core.comments.eventcommentdeleted": "Comentario eliminado", - "core.comments.nocomments": "No hay comentarios", - "core.comments.savecomment": "Guardar comentario", - "core.comments.warningcommentsnotsent": "No se pudo sincronizar comentarios. {{error}}", - "core.commentscount": "Comentarios ({{$a}})", - "core.completion-alt-auto-fail": "Finalizado {{$a}} (no obtuvo calificación de aprobado)", - "core.completion-alt-auto-n": "Sin finalizar: {{$a}}", - "core.completion-alt-auto-n-override": "No completado: {{$a.modname}} (configurado por {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Finalizado: {{$a}} (obtuvo calificación de aprobado)", - "core.completion-alt-auto-y": "Finalizado: {{$a}}", - "core.completion-alt-auto-y-override": "Completado: {{$a.modname}} (configurado por {{$a.overrideuser}})", - "core.completion-alt-manual-n": "No finalizado; {{$a}}. Seleccione para marcar como finalizado", - "core.completion-alt-manual-n-override": "No completado: {{$a.modname}} (configurado por {{$a.overrideuser}}). Seleccionar para marcarlo como completado.", - "core.completion-alt-manual-y": "Finalizado; {{$a}} seleccione para marcar como no finalizado", - "core.completion-alt-manual-y-override": "Completado: {{$a.modname}} (configurado por {{$a.overrideuser}}). Seleccionar para marcarlo como no completado.", - "core.confirmcanceledit": "¿Está Usted seguro de que quiere abandonar esta página? Se perderán todos los cambios.", - "core.confirmdeletefile": "¿Está seguro de que desea eliminar este archivo?", - "core.confirmgotabroot": "¿Está seguro de querer regresar a {{name}}?", - "core.confirmgotabrootdefault": "¿Está seguro de querer ir a la página inicial de la pestaña actual?", - "core.confirmleaveunknownchanges": "¿Está seguro de querer abandonar esta página? Si tiene algunos cambios no guardados, se perderán.", - "core.confirmloss": "¿Está seguro? Se perderán todos los cambios.", - "core.confirmopeninbrowser": "¿Desea abrirla en un navegador web?", - "core.considereddigitalminor": "Usted es demasiado joven para crear una cuenta en este sitio.", - "core.content": "Contenido", - "core.contenteditingsynced": "El contenido que está editando ha sido sincronizado.", - "core.contentlinks.chooseaccount": "Elegir cuenta", - "core.contentlinks.chooseaccounttoopenlink": "Elegir una cuenta con la cual abrir el enlace.", - "core.contentlinks.confirmurlothersite": "Este enlace pertenece a otro sitio. ¿Quiere abrirlo?", - "core.contentlinks.errornoactions": "No pudo encontrase una acción a realizar con este enlace.", - "core.contentlinks.errornosites": "No pudo encontrase ningún sitio para manejar éste enlace.", - "core.contentlinks.errorredirectothersite": "La URL redirigida no puede apuntar a un sitio diferente.", - "core.continue": "Continuar", - "core.copiedtoclipboard": "Texto copiado al portapapeles", - "core.copytoclipboard": "Copiar al portapapeles", - "core.course": "Curso", - "core.course.activitydisabled": "Su organización ha deshabilitado esta actividad en la App mobile.", - "core.course.activitynotyetviewableremoteaddon": "Su organización instaló un plugin complemento de terceros que todavía no está soportado por la App.", - "core.course.activitynotyetviewablesiteupgradeneeded": "La instalación Moodle de su organización necesita actualizarse.", - "core.course.allsections": "Todas las secciones", - "core.course.askadmintosupport": "Póngase en contacto conel administrador del sitio y dígale que Usted quiere usar esta actividad junto con la App Moodle Mobile.", - "core.course.availablespace": "Usted tiene actualmente {{available}} de espacio libre.", - "core.course.cannotdeletewhiledownloading": "Los archivos no pueden ser eliminados mientras la actividad está siendo descargada. Por favor espere a que termine la descarga.", - "core.course.confirmdeletemodulefiles": "¿Está seguro de querer eliminar estos archivos?", - "core.course.confirmdownload": "Usted está a punto de descargar {{size}}.{{availableSpace}} . ¿Está seguro de querer continuar?", - "core.course.confirmdownloadunknownsize": "No pudimos calcular el tamaño de la descarga. {{availableSpace}} ¿Está seguro de querer continuar?", - "core.course.confirmdownloadzerosize": "Usted está a punto de comenzar a descargar .{{availableSpace}} ¡Está seguro de querer continuar?", - "core.course.confirmlimiteddownload": "Actualmente Usted no está conectado a Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Usted está a punto de descargar al menos {{size}}.{{availableSpace}} . ¿Está seguro de querer continuar?", - "core.course.contents": "Contenidos", - "core.course.couldnotloadsectioncontent": "No pudo cargarse el contenido de la sección. Por favor inténtelo nuevamente después.", - "core.course.couldnotloadsections": "No pudieron cargarse las secciones. Por favor inténtelo nuevamente después.", - "core.course.coursesummary": "Resumen del curso", - "core.course.downloadcourse": "Descargar curso", - "core.course.errordownloadingcourse": "Error al descargar curso.", - "core.course.errordownloadingsection": "Error al descargar sección.", - "core.course.errorgetmodule": "Error al obtener datos de la actividad.", - "core.course.hiddenfromstudents": "Oculto para los estudiantes", - "core.course.hiddenoncoursepage": "Disponible pero no mostrada en página del curso", - "core.course.insufficientavailablequota": "Su dispositivo no pudo asignar espacio para guardar esta descarga. Podría estar reservando espacio para actualizaciones del sistema y de la App. Por favor primeramente libere espacio de almacenamiento.", - "core.course.insufficientavailablespace": "Usted está tratando de descargar {{size}}. Esto dejará a su dispositivo con espacio insuficiente para operar normalmente. Por favor primeramente libere espacio de almacenamiento.", - "core.course.manualcompletionnotsynced": "Finalización manual no sincronizada.", - "core.course.nocontentavailable": "Sin contenido disponible al momento.", - "core.course.overriddennotice": "La calificación final de esta actividad ha sido ajustada manualmente.", - "core.course.refreshcourse": "Refrescar curso", - "core.course.sections": "Secciones", - "core.course.useactivityonbrowser": "Usted todavía puede usarla si emplea el navegador web de su dispositivo.", - "core.course.warningmanualcompletionmodified": "La finalización manual de una actividad fue modificada en el sitio.", - "core.course.warningofflinemanualcompletiondeleted": "Una parte de la finalización manual del curso '{{name}}' ha sido eliminada. {{error}}", - "core.coursedetails": "Detalles del curso", - "core.coursenogroups": "Usted no es un miembro de ningún grupo en este curso.", - "core.courses.addtofavourites": "Destacar (marcar con estrella) este curso", - "core.courses.allowguests": "Este curso permite la entrada de invitados", - "core.courses.availablecourses": "Cursos disponibles", - "core.courses.cannotretrievemorecategories": "No se pueden recuperar categorías más profundas que el nivel {{$a}}.", - "core.courses.categories": "Categorías", - "core.courses.confirmselfenrol": "¿Está seguro de querer inscribirse Usted mismo en este curso?", - "core.courses.courses": "Cursos", - "core.courses.downloadcourses": "Descargar cursos", - "core.courses.enrolme": "Inscribirme", - "core.courses.errorloadcategories": "Ocurrió un error al cargar categorías.", - "core.courses.errorloadcourses": "Ocurrió un error al cargar los cursos.", - "core.courses.errorloadplugins": "Los plugins necesarios para este curso no se pudieron cargar correctamente. Por favor vuelva a cargar la App e inténtelo nuevamente.", - "core.courses.errorsearching": "Ocurrio un error al buscar.", - "core.courses.errorselfenrol": "Ocurrio un error al auto-inscribir.", - "core.courses.filtermycourses": "Filtrar mis cursos", - "core.courses.frontpage": "Portada", - "core.courses.hidecourse": "Quitar de la vista", - "core.courses.ignore": "Ignorar", - "core.courses.mycourses": "Mis cursos", - "core.courses.mymoodle": "Tablero", - "core.courses.nocourses": "No hay información del curso para mostrar.", - "core.courses.nocoursesyet": "No hay cursos en esta categoría", - "core.courses.nosearchresults": "Sin resultados", - "core.courses.notenroled": "Usted no está inscrito en este curso", - "core.courses.notenrollable": "Usted no puede inscribirse Usted mismo en este curso.", - "core.courses.password": "Clave de inscripción", - "core.courses.paymentrequired": "Para entrar a este curso es necesario pagar.", - "core.courses.paypalaccepted": "Pagos PayPal aceptados", - "core.courses.reload": "Recargar", - "core.courses.removefromfavourites": "Quitar estrella de este curso", - "core.courses.search": "Buscar", - "core.courses.searchcourses": "Buscar cursos", - "core.courses.searchcoursesadvice": "Usted puede usar el botón de 'buscar cursos' para encontrar cursos a los cuales acceder como un invitado o para inscribirse Usted mismo en los cursos que lo permitan.", - "core.courses.selfenrolment": "Auto-inscripción", - "core.courses.sendpaymentbutton": "Enviar pago por Paypal", - "core.courses.show": "Restaurar para verlo", - "core.courses.totalcoursesearchresults": "Total de cursos: {{$a}}", - "core.currentdevice": "Dispositivo actual", - "core.datastoredoffline": "Los datos se almacenaron en el dispositivo debido a que no se pudieron enviar. Serán enviados automáticamente más tarde.", - "core.date": "Fecha", - "core.day": "día", - "core.days": "días", - "core.decsep": ".", - "core.defaultvalue": "Valor por defecto ({{$a}})", - "core.delete": "Eliminar", - "core.deletedoffline": "Eliminado fuera-de-línea", - "core.deleteduser": "Usuario eliminado", - "core.deleting": "Eliminando", - "core.description": "Descripción", - "core.desktop": "Escritorio", - "core.dfdaymonthyear": "MM-DD-AAAA", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM AAAA h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Menor digital", - "core.digitalminor_desc": "Por favor pídale a su padre o tutor que se ponga en contacto con:", - "core.discard": "Descartar", - "core.dismiss": "Descartar", - "core.displayoptions": "Opciones de visualización", - "core.done": "Hecho", - "core.download": "Descargar", - "core.downloaded": "Descargado", - "core.downloading": "Descargando", - "core.edit": "Editar", - "core.editor.autosavesucceeded": "Borrador guardado.", - "core.editor.bold": "Negritas", - "core.editor.clear": "Limpiar formato", - "core.editor.h3": "Encabezado (grande)", - "core.editor.h4": "Encabezado (mediano)", - "core.editor.h5": "Encabezado (pequeño)", - "core.editor.hidetoolbar": "Ocultar barra de herramientas", - "core.editor.italic": "Cursivas", - "core.editor.orderedlist": "Lista ordenada", - "core.editor.p": "Párrafo", - "core.editor.strike": "Tachado", - "core.editor.textrecovered": "Se restauró automáticamente una versión de borrador de este texto.", - "core.editor.toggle": "Alternar editor", - "core.editor.underline": "Subrayado", - "core.editor.unorderedlist": "Lista ordenada", - "core.emptysplit": "Esta página aparecerá en blanco si el panel izquierdo está vacío o si está cargando.", - "core.error": "Error", - "core.errorchangecompletion": "Ocurrió un error al cambiar el estatus de finalización. Por favor inténtelo nuevamente.", - "core.errordeletefile": "Error al eliminar el archivo. Por favor inténtelo nuevamente.", - "core.errordownloading": "Error al descargar archivo", - "core.errordownloadingsomefiles": "Error al descargar archivos. Pueden faltar algunos archivos.", - "core.errorfileexistssamename": "Ya existe un archivo con este nombre.", - "core.errorinvalidform": "El formato contiene datos inválidos. Por favor, asegúrese de llenar todos los campos solicitados y de que los datos sean válidos.", - "core.errorinvalidresponse": "Se recibió respuesta inválida. Por favor póngase en contacto con su administrador del sitio si el error persiste.", - "core.errorloadingcontent": "Error al cargar contenido.", - "core.errorofflinedisabled": "La navegación fuera de línea está deshabilitada en su sitio. Usted necesita reconectarse a Internet para usar la App.", - "core.erroropenfilenoapp": "Error al abrir el archivo. No se encontró App para abrir este tipo de archivo.", - "core.erroropenfilenoextension": "Error al abrir el archivo: el archivo no tiene una extensión.", - "core.erroropenpopup": "Esta actividad está tratando de abrir una ventana emergente. Esto no está soportado en la App.", - "core.errorrenamefile": "Error al renombrar el archivo. Por favor inténtelo nuevamente.", - "core.errorsomedatanotdownloaded": "Si Usted descarga esta actividad, por favor tenga en cuenta que algunos datos no son descargados durante el proceso de descarga por razones de desempeño y uso de datos.", - "core.errorsync": "Ocurrió un error al sincronizar. Por favor inténtelo nuevamente.", - "core.errorsyncblocked": "Este/a {{$a}} no puede sincronizarse ahorita porque hay un proceso trabajando. Por favor inténtelo nuevamente más tarde. Si el problema persiste, intente reiniciar la App.", - "core.explanationdigitalminor": "Esta información es necesaria para determinar si su edad está arriba de la edad digital de consentimiento. Esta es la edad cuando un individuo puede consentir a los términos y condiciones y que sus datos sean legalmente almacenados y procesados.", - "core.favourites": "Destacados", - "core.filename": "Nombre del archivo", - "core.filenameexist": "El nombre del archivo ya existe: {{$a}}", - "core.filenotfound": "Lo sentimos, el archivo no se ha encontrado.", - "core.fileuploader.addfiletext": "Añadir archivo", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Cámara", - "core.fileuploader.confirmuploadfile": "Usted está a punto de subir {{size}}. ¿Está seguro de querer continuar?", - "core.fileuploader.confirmuploadunknownsize": "No se pudo calcular el tamaño de la subida. ¿Está seguro de querer continuar?", - "core.fileuploader.errorcapturingaudio": "Error al capturar audio.", - "core.fileuploader.errorcapturingimage": "Error al capturar imagen.", - "core.fileuploader.errorcapturingvideo": "Error al capturar video.", - "core.fileuploader.errorgettingimagealbum": "Error al obtener imagen del album.", - "core.fileuploader.errormustbeonlinetoupload": "Usted tiene que estar en línea para subir archivos.", - "core.fileuploader.errornoapp": "Usted no tiene una App instalada para realizar esta acción.", - "core.fileuploader.errorreadingfile": "Error al leer archivo.", - "core.fileuploader.errorwhileuploading": "Ocurrió un error durante la subida del archivo.", - "core.fileuploader.file": "Archivo", - "core.fileuploader.filesofthesetypes": "Tipos de archivos aceptados:", - "core.fileuploader.fileuploaded": "El archivo fue subido exitosamente.", - "core.fileuploader.invalidfiletype": "El tipo de archivo {{$a}} no se acepta.", - "core.fileuploader.maxbytesfile": "El archivo {{$a.file}} es demasido grande. El tamaño máximo que Usted puede subir es {{$a.size}}.", - "core.fileuploader.more": "Más", - "core.fileuploader.photoalbums": "Álbumes de fotos", - "core.fileuploader.readingfile": "Leyendo archivo", - "core.fileuploader.readingfileperc": "Leyendo archivo: {{$a}}%", - "core.fileuploader.selectafile": "Seleccionar un archivo", - "core.fileuploader.uploadafile": "Subir un archivo", - "core.fileuploader.uploading": "Subiendo", - "core.fileuploader.uploadingperc": "Subiendo: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filtro", - "core.folder": "Carpeta", - "core.forcepasswordchangenotice": "Para continuar, deberá cambiar su contraseña.", - "core.fulllistofcourses": "Todos los cursos", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Promedio", - "core.grades.badgrade": "La calificación suministrada no es válida", - "core.grades.contributiontocoursetotal": "Contribución al total del curso", - "core.grades.feedback": "Retroalimentación", - "core.grades.grade": "Calificación", - "core.grades.gradeitem": "Ítem de calificación", - "core.grades.grades": "Calificaciones", - "core.grades.lettergrade": "Calificación por letra", - "core.grades.nogradesreturned": "No se han devuelto calificaciones", - "core.grades.nooutcome": "Sin resultado", - "core.grades.percentage": "Porcentaje", - "core.grades.range": "Rango", - "core.grades.rank": "Rango", - "core.grades.weight": "Ponderación", - "core.group": "Grupo", - "core.groupsseparate": "Grupos separados", - "core.groupsvisible": "Grupos visibles", - "core.h5p.additionallicenseinfo": "Cualquier información adicional acerca de la licencia", - "core.h5p.author": "Autor", - "core.h5p.authorcomments": "Comentarios del autor", - "core.h5p.authorcommentsdescription": "Comentarios para el editor del contenido. (Este texto no será publicado como parte de la información de copyright)", - "core.h5p.authorname": "Nombre del autor", - "core.h5p.authorrole": "Rol del autor", - "core.h5p.by": "por", - "core.h5p.cancellabel": "Cancelar", - "core.h5p.ccattribution": "Atribución (CC BY)", - "core.h5p.ccattributionnc": "Licencia de Atribución-No-Comercial (CC BY-NC)", - "core.h5p.ccattributionncnd": "Licencia de Atribución-No-Comercial-NoDerivs (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Licencia de Atribución-No-Comercial-ShareAlike (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Licencia de Atribución-NoDerivs (CC BY-ND)", - "core.h5p.ccattributionsa": "Licencia de Atribución-ShareAlike (CC BY-SA)", - "core.h5p.ccpdd": "Dedicación del Dominio Público (CC0)", - "core.h5p.changedby": "Cambiado por", - "core.h5p.changedescription": "Descripción del cambio", - "core.h5p.changelog": "Bitácora de cambios", - "core.h5p.changeplaceholder": "Foto recortada, texto cambiado, etc.", - "core.h5p.close": "Cerrar", - "core.h5p.confirmdialogbody": "Por favor confirme que desea proseguir. Esta acción no puede deshacerse.", - "core.h5p.confirmdialogheader": "Confirmar acción", - "core.h5p.confirmlabel": "Confirmar", - "core.h5p.connectionLost": "Conexión perdida. Los resultados serán almacenados y enviados cuando la conexión sea restablecida.", - "core.h5p.connectionReestablished": "Conexión restablecida", - "core.h5p.contentCopied": "El contenido es copiado al portapapeles", - "core.h5p.contentchanged": "Este contenido ha cambiado desde la última vez que Usted lo utilizó.", - "core.h5p.contenttype": "Tipo de Contenido", - "core.h5p.copyright": "Derechos de uso", - "core.h5p.copyrightinfo": "Información del Copyright", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "Ver información de copyright para este contenido.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Fecha", - "core.h5p.disablefullscreen": "Deshabilitar pantalla completa", - "core.h5p.download": "Descargar", - "core.h5p.downloadtitle": "Descargar este contenido como un archivo H5P.", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Incrustar", - "core.h5p.embedtitle": "Ver el contenido incrustado para este comentario", - "core.h5p.errorgetemail": "Error al obtener el Email del usuario. Por favor revise su conexión e inténtelo nuevamente.", - "core.h5p.fullscreen": "Pantalla completa", - "core.h5p.gpl": "Licencia Pública General v3", - "core.h5p.h5ptitle": "Visitar H5P.org para revisar más contenido.", - "core.h5p.hideadvanced": "Ocultar avanzadas", - "core.h5p.license": "Licencia", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) Dedicación del Dominio Público", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Genérico", - "core.h5p.licenseCC20": "2.0 Genérico", - "core.h5p.licenseCC25": "2.5 Genérico", - "core.h5p.licenseCC30": "3.0 No Portado", - "core.h5p.licenseCC40": "4.0 Internacional", - "core.h5p.licenseGPL": "Licencia Pública general", - "core.h5p.licenseV1": "Versión 1", - "core.h5p.licenseV2": "Versión 2", - "core.h5p.licenseV3": "Versión 3", - "core.h5p.licensee": "Licenciatario", - "core.h5p.licenseextras": "Extras de Licencia", - "core.h5p.licenseversion": "Versión de Licencia", - "core.h5p.nocopyright": "Sin información de copyright disponible para este contenido.", - "core.h5p.offlineDialogBody": "No pudimos enviar información acerca de su finalización de este trabajo. Por favor revise su conexión de internet.", - "core.h5p.offlineDialogHeader": "Se perdió su conexión al servidor", - "core.h5p.offlineDialogRetryButtonLabel": "Reintentar ahora", - "core.h5p.offlineDialogRetryMessage": "Reintentando en :num....", - "core.h5p.offlineSuccessfulSubmit": "Resultados enviados exitosamente.", - "core.h5p.offlinedisabled": "El sitio no permite descargar paquetes H5P.", - "core.h5p.originator": "Originador", - "core.h5p.pd": "Dominio Público", - "core.h5p.pddl": "Dedicación y Licencia del Dominio Público", - "core.h5p.pdm": "Marca de Dominio Público (PDM)", - "core.h5p.play": "Reproducir H5P", - "core.h5p.resizescript": "Incluya este script en su sitio web si desea ajuste de tamaño dinámico del contenido incrustado.", - "core.h5p.resubmitScores": "Intentando enviar resultados guardados.", - "core.h5p.reuse": "Reutilizar", - "core.h5p.reuseContent": "Reutilizar Contenido", - "core.h5p.reuseDescription": "Reutilizar este contenido.", - "core.h5p.showadvanced": "Mostrar avanzadas", - "core.h5p.showless": "Mostrar menos", - "core.h5p.showmore": "Mostrar más", - "core.h5p.size": "Tamaño", - "core.h5p.source": "Orígen", - "core.h5p.startingover": "Usted estará re-comenzando.", - "core.h5p.sublevel": "Subnivel", - "core.h5p.thumbnail": "Miniatura", - "core.h5p.title": "Título", - "core.h5p.undisclosed": "No revelado", - "core.h5p.year": "Año", - "core.h5p.years": "Año(s)", - "core.h5p.yearsfrom": "Años (desde)", - "core.h5p.yearsto": "Años (hasta)", - "core.hasdatatosync": "Este/a {{$a}} tiene datos fuera-de-línea para sincronizarse.", - "core.help": "Ayuda", - "core.hide": "Ocultar", - "core.hour": "hora", - "core.hours": "horas", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Imagen", - "core.imageviewer": "Visor de imágenes", - "core.info": "Información", - "core.invalidformdata": "Formato de datos incorrecto", - "core.labelsep": ":", - "core.lastaccess": "Último acceso", - "core.lastdownloaded": "Última descarga", - "core.lastmodified": "Última modificación", - "core.lastsync": "Última sincronización", - "core.layoutgrid": "Rejilla", - "core.list": "Lista", - "core.listsep": ";", - "core.loading": "Cargando", - "core.loadmore": "Cargar más", - "core.location": "Ubicación", - "core.login.auth_email": "Auto-registro basado en Email", - "core.login.authenticating": "Autenticando", - "core.login.cancel": "Cancelar", - "core.login.changepassword": "Cambiar contraseña", - "core.login.changepasswordbutton": "Abrir la página para cambiar contraseña", - "core.login.changepasswordhelp": "Si Usted tiene problemas para cambiar su contraseña, por favor póngase en contacto con su administrador del sitio. Los \"Administradores del sitio\" son las personas que gestionan el Moodle de su escuela/universidad/compañía o su organización educativa. Si Usted no sabe como ponerse en contacto con ellas, por favor póngase en contacto con sus profesores/entrenadores", - "core.login.changepasswordinstructions": "Usted no puede cambiar su contraseña en la App. Por favor elija el botón siguiente para abrir el sitio en un navegador web para cambiar su contraseña. Considere que Usted necesita cerrar el navegador después de cambiar la contraseña, ya que Usted no será redirigido a la App.", - "core.login.changepasswordlogoutinstructions": "Si prefiere cambiar de sitio o salirse, por favor elija el botón siguiente:", - "core.login.changepasswordreconnectinstructions": "Elija el botón siguiente para reconectarse al sitio. (considere que si no cambia exitosamente su contraseña, Usted regresaría a la pantalla anterior).", - "core.login.confirmdeletesite": "¿Está seguro de querer eliminar el sitio {{sitename}}?", - "core.login.connect": "¡Conectar!", - "core.login.connecttomoodle": "Conectar a Moodle", - "core.login.connecttomoodleapp": "Usted está intentando conectarse a un sitio Moodle regular. Por favor descargue la App oficial Moodle para acceder a este sitio.", - "core.login.connecttoworkplaceapp": "Usted está intentando conectarse a un sitio Moodle Workplace. Por favor descargue la App oficial Moodle Workplace para acceder a este sitio.", - "core.login.contactyouradministrator": "Póngase en contacto con su administrador del sitio para más ayuda.", - "core.login.contactyouradministratorissue": "Por favor, pídale al administrador que revise el siguiente problema: {{$a}}", - "core.login.createaccount": "Crear mi cuenta nueva", - "core.login.createuserandpass": "Elegir su nombre_de_usuario y contraseña", - "core.login.credentialsdescription": "Por favor proporcione su nombre_de_usuario y contraseña para ingresar.", - "core.login.emailconfirmsent": "

            Hemos enviado un correo electrónico a {{$a}}

            \n

            En él encontrará instrucciones sencillas para concluir el proceso.

            \n

            Si tuviera alguna dificultad, póngase en contacto con el Administrador del Sistema.

            ", - "core.login.emailconfirmsentnoemail": "

            Un Email debería de haberse enviado a su dirección.

            Contiene instrucciones fáciles para completar su registro.

            Si persisten las dificultades, póngase en contacto con el administrador del sitio.

            ", - "core.login.emailconfirmsentsuccess": "Email de confirmación enviado exitosamente", - "core.login.emailnotmatch": "No coinciden los Emails", - "core.login.erroraccesscontrolalloworigin": "La llamada de Orígen Cruzado (''Cross-Origin'') que Usted está tratando de realizar ha sido rechazada. Por favor revise https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Ocurrió un error al eliminar el sitio. Por favor inténtelo nuevamente.", - "core.login.errorexampleurl": "La URL https://campus.example.edu es solamente un ejemplo de URL. No es un sitio real. Por favor, use la URL del sitio de su escuela o su organización.", - "core.login.errorupdatesite": "Ocurrió un error al actualizar la ficha (''token'') del sitio", - "core.login.faqcannotconnectanswer": "Por favor, póngase en contacto con su administrador del sitio.", - "core.login.faqcannotconnectquestion": "Yo escribí correctamente la dirección de mi sitio, pero aún no me puedo conectar.", - "core.login.faqcannotfindmysiteanswer": "¿Escribió el nombre correctamente? Si todavía no puede encontrarlo, por favor ingrese en su lugar la dirección del sitio. No todos los sitios Moodle están en nuestro directorio público.", - "core.login.faqcannotfindmysitequestion": "No puedo encontrar mi sitio.", - "core.login.faqsetupsiteanswer": "Visitar {{$link}} para revisar las diferentes opciones que tiene para crear su propio sitio Moodle.", - "core.login.faqsetupsitelinktitle": "Comenzar.", - "core.login.faqsetupsitequestion": "Deseo configurar mi propio sitio Moodle.", - "core.login.faqtestappanswer": "Para probar la App en un sitio Moodle Demo, escriba \"teacher\" o \"student\" en el campo para \"La dirección de su sitio\" y elija el botón para \"¡Conectar!\" .", - "core.login.faqtestappquestion": "Solamente quiero probar la App. ¿Qué puedo hacer?", - "core.login.faqwhatisurlanswer": "

            Cada organización o escuela tiene su propia dirección personalizada para su sitio Moodle. Para encontrar la dirección Usted puede hacer lo siguiente:

            1. Abra un navegador web y vaya a la página para ingresar al sitio Moodle de su organización o escuela
            2. En la parte superior de la página, en la barra de dirección, Usted verá la URL de su sitio Moodle. Por ejemplo \"campus.example.edu\".{{$image}}
            3. Copie la dirección (no copie la parte de /login y lo que sigue después), péguelo en la App Moodle y haga clic en \"¡Conectar!\"
            4. Ahora Usted puede ingresar a su sitio, usando su nombre_de_usuario y contraseña
            5. ", - "core.login.faqwhatisurlquestion": "¿Cual es la URL de mi sitio Moodle? ¿Cómo encuentro la URL del sitio de mi escuela?", - "core.login.faqwhereisqrcode": "¿Donde puedo encontrar el código QR?", - "core.login.faqwhereisqrcodeanswer": "

              Si su organización lo ha habilitado, Usted encontrará un código QR en el sitio web al fondo de su página del perfil del usuario.

              {{$image}}", - "core.login.findyoursite": "Encuentre su sitio", - "core.login.firsttime": "Registrarse como usuario", - "core.login.forcepasswordchangenotice": "Para continuar, deberá cambiar su contraseña.", - "core.login.forgotten": "¿Olvidó su nombre_de_usuario o contraseña?", - "core.login.help": "Ayuda", - "core.login.helpmelogin": "

              Existen muchos miles de sitios Moodle en el mundo. Esta App solamente puede conectar a sitios Moodle que tengan específicamente habilitado el acceso por la App Mobile.

              Si Usted no puede conectarse a su sitio Moodle, entonces Usted necesita ponerse en contacto con su administrador del sitio y pedirle que lea http://docs.moodle.org/en/Mobile_app

              Para probar la App en un sitio Moodle demostrativo, escriba teacher o student en el campo de la Dirección del sitio y haga click en el Botón Conectar.

              ", - "core.login.instructions": "Instrucciones", - "core.login.invalidaccount": "Por favor, verifique sus datos de acceso, o consulte con el administrador para revisar la configuración del sitio.", - "core.login.invaliddate": "Fecha no válida", - "core.login.invalidemail": "Dirección de correo no válida", - "core.login.invalidmoodleversion": "

              Versión inválida de sitio Moodle. La App Moodle solamente soporta sistemas Moodle {{$a}} en adelante.

              \n

              Usted puede ponerse en contacto con el administrador de sus sitio y pedirle que actualice su sistema Moodle.

              \n

              Los \"Administradores del sitio\" son las personas que gestionan el Moodle en su escuela/universidad/compañía o su organización educativa. Si Usted no sabe a quien contactar, por favor póngase en contacto con sus profesores/entrenadores.

              ", - "core.login.invalidsite": "La URL del sitio es inválida.", - "core.login.invalidtime": "Hora inválida", - "core.login.invalidurl": "Se ha especificado una URL no válida", - "core.login.invalidvaluemax": "El valor máximo es {{$a}}", - "core.login.invalidvaluemin": "El valor mínimo es {{$a}}", - "core.login.localmobileunexpectedresponse": "La revisión de las características Adicionales de Moodle Mobile regresó una respuesta inesperada; Usted será autenticado usando el servicio Mobile estándar.", - "core.login.loggedoutssodescription": "Usted tiene que autenticarse nuevamente. Usted necesita ingresar al sitio en una ventana del navegador.", - "core.login.login": "Ingresar", - "core.login.loginbutton": "Ingresar", - "core.login.logininsiterequired": "Usted necesita ingresar al sitio en una ventana del navegador.", - "core.login.loginsteps": "Para acceso completo a este sitio, Usted necesita primeramente crear una cuenta.", - "core.login.missingemail": "Escribir: dirección de correo", - "core.login.missingfirstname": "Escribir: nombre", - "core.login.missinglastname": "Falta apellido(s)", - "core.login.mobileservicesnotenabled": "El acceso Mobile no está habilitado en su sitio. Por favor, póngase en contacto con su administrador del sitio si Usted piensa que se debería habilitar.", - "core.login.mustconfirm": "Necesita confirmar su cuenta", - "core.login.newaccount": "Nueva cuenta", - "core.login.notloggedin": "Usted necesita estar ingresado.", - "core.login.onboardingcreatemanagecourses": "Crear y gestionar sus cursos", - "core.login.onboardingenrolmanagestudents": "Inscribir y gestionar a sus estudiantes", - "core.login.onboardinggetstarted": "Empezar con Moodle", - "core.login.onboardingialreadyhaveasite": "Yo ya tengo un sitio Moodle", - "core.login.onboardingimalearner": "Soy un estudiante", - "core.login.onboardingimaneducator": "Soy un educador", - "core.login.onboardingineedasite": "Necesito un sitio Moodle", - "core.login.onboardingprovidefeedback": "Proporcionar retroalimentación oportuna", - "core.login.onboardingtoconnect": "Para conectarse a la App Moodle Usted necesitará un sitio Moodle", - "core.login.onboardingwelcome": "¡BienVenido a la App Moodle!", - "core.login.or": "O", - "core.login.password": "Contraseña", - "core.login.passwordforgotten": "Contraseña olvidada", - "core.login.passwordforgotteninstructions2": "Para reajustar su contraseña, envíe su nombre_de_usuario o su dirección de correo electrónico. Si podemos encontrarlo en la base de datos, le enviaremos un email con instrucciones para poder acceder de nuevo.", - "core.login.passwordrequired": "Contraseña obligatoria", - "core.login.policyaccept": "Entiendo y estoy de acuerdo", - "core.login.policyagree": "Usted deberá estar de acuerdo con estas condiciones antes de seguir usando este sitio. ¿Está de acuerdo?", - "core.login.policyagreement": "Acuerdo con las Condiciones del Sitio", - "core.login.policyagreementclick": "Haga clic aquí para leer el acuerdo con las condiciones del sitio", - "core.login.potentialidps": "Ingrese usando su cuenta en:", - "core.login.profileinvaliddata": "Valor no válido", - "core.login.recaptchachallengeimage": "Imagen del desafío reCAPTCHA", - "core.login.recaptchaexpired": "Verificación expiró. Conteste nuevamente la pregunta de seguridad.", - "core.login.recaptchaincorrect": "La respuesta a la pregunta de seguridad es incorrecta.", - "core.login.reconnect": "Reconectar", - "core.login.reconnectdescription": "Su ficha (''token'') para autenticación es inválida o ha caducado; Usted tiene que reconectarse al sitio.", - "core.login.reconnectssodescription": "Su ficha (''token'') para autenticación es inválida o ha caducado; Usted tiene que reconectarse al sitio. Usted tiene que ingresar al sitio en una ventana del navegador.", - "core.login.resendemail": "Re-enviar Email", - "core.login.searchby": "Buscar por:", - "core.login.security_question": "Pregunta de seguridad", - "core.login.selectacountry": "Seleccione su país", - "core.login.selectsite": "Por favor seleccione su sitio:", - "core.login.signupplugindisabled": "{{$a}} no está habilitado/a.", - "core.login.siteaddress": "Su sitio", - "core.login.sitehasredirect": "Su sitio contiene al menos una redirección HTTP. La App no puede seguir redirecciones; esto podría ser el problema que está impidiéndole a la App que se conecte a su sitio.", - "core.login.siteinmaintenance": "Su sitio está en modo de mantenimiento", - "core.login.sitepolicynotagreederror": "No se aceptó la política del sitio.", - "core.login.siteurl": "URL del sitio", - "core.login.siteurlrequired": "La URL del sitio es obligatoria, por ejemplo http://www.susitiomoodle.com o https://www.susitiomoodle.mx", - "core.login.startsignup": "Comience ahora creando una cuenta nueva", - "core.login.stillcantconnect": "¿Todavía no puede conectarse?", - "core.login.supplyinfo": "Más detalles", - "core.login.username": "Nombre_de_usuario", - "core.login.usernameoremail": "Escribir nombre_de_usuario o dirección email", - "core.login.usernamerequired": "Nombre_de_usuario obligatorio", - "core.login.usernotaddederror": "No se agregó el usuario - error desconocido", - "core.login.visitchangepassword": "¿Desea visitar el sitio para cambiar la contraseña?", - "core.login.webservicesnotenabled": "Es posible que su sitio no haya habilitado los servicios web. Por favor, póngase en contacto con su administrador del sitio para solicitar ayuda.", - "core.login.youcanstillconnectwithcredentials": "Usted todavía puede conectarse al sitio al ingresar su nombre_de_usuario y contraseña", - "core.login.yourenteredsite": "Conectarse a su sitio", - "core.lostconnection": "Si ficha (token) de autenticación es inválida o ha caducado. Usted tendrá que re-conectarse al sitio.", - "core.mainmenu.changesite": "Cambiar sitio", - "core.mainmenu.help": "Ayuda", - "core.mainmenu.logout": "Salir", - "core.mainmenu.website": "Página web", - "core.maxsizeandattachments": "Tamaño máximo para archivos: {{$a.size}}, número máximo de archivos: {{$a.attachments}}", - "core.min": "min", - "core.mins": "mins", - "core.misc": "Misceláneos", - "core.mod_assign": "Tarea", - "core.mod_assignment": "Tarea 2.2 (deshabilitada)", - "core.mod_book": "Libro", - "core.mod_chat": "Chat", - "core.mod_choice": "Elección", - "core.mod_data": "Base de datos", - "core.mod_database": "Base de datos", - "core.mod_external-tool": "Herramienta externa", - "core.mod_feedback": "Retroalimentación", - "core.mod_file": "Archivo", - "core.mod_folder": "Carpeta (folder)", - "core.mod_forum": "Foro", - "core.mod_glossary": "Glosario", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "Paquete contenidos IMS", - "core.mod_imscp": "Paquete contenidos IMS", - "core.mod_label": "Etiqueta", - "core.mod_lesson": "Lección", - "core.mod_lti": "Herramienta externa", - "core.mod_page": "Página", - "core.mod_quiz": "Examen", - "core.mod_resource": "Archivo", - "core.mod_scorm": "Paquete SCORM", - "core.mod_survey": "Encuesta predefinida", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Taller", - "core.moduleintro": "Descripción", - "core.more": "más", - "core.mygroups": "Mis grupos", - "core.name": "Nombre", - "core.needhelp": "¿Necesita ayuda?", - "core.networkerroriframemsg": "El contenido no está disponible fuera de línea. Por favor conéctese a Internet e inténtelo de nuevo.", - "core.networkerrormsg": "Hubo un problema para conectarse al sitio. Por favor revise su conexión e inténtelo nuevamente.", - "core.never": "Nunca", - "core.next": "Siguiente", - "core.no": "No", - "core.nocomments": "No hay comentarios", - "core.nograde": "No hay calificación", - "core.none": "Ninguno(a)", - "core.nooptionavailable": "Sin opción disponible", - "core.nopasswordchangeforced": "Usted no puede proceder sin cambiar su contraseña.", - "core.nopermissionerror": "Lo sentimos, pero Usted actualmente no tiene permisos para hacer eso", - "core.nopermissions": "Lo sentimos, pero por el momento no tiene permiso para hacer eso ({{$a}}).", - "core.noresults": "No hay resultados", - "core.noselection": "Sin selección", - "core.notapplicable": "no disp.", - "core.notenrolledprofile": "Este perfil no está disponible porque el usuario no está inscrito en este curso.", - "core.notice": "Aviso", - "core.notingroup": "Lo sentimos, pero Usted necesita ser miembro de un grupo para ver esta página.", - "core.notsent": "No enviado", - "core.now": "ahora", - "core.nummore": "{{$a}} más", - "core.numwords": "{{$a}} palabras", - "core.offline": "Fuera de línea", - "core.ok": "OK", - "core.online": "En línea", - "core.openfullimage": "Hacer clic aquí para mostrar la imagen a tamaño completo", - "core.openinbrowser": "Abrir en navegador", - "core.openmodinbrowser": "Abrir {{$a}} en navegador", - "core.othergroups": "Otros grupos", - "core.pagea": "Página {{$a}}", - "core.parentlanguage": "es", - "core.paymentinstant": "¡Utilice el botón de abajo para pagar y poder inscribirse en minutos!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Teléfono", - "core.pictureof": "Imagen de {{$a}}", - "core.previous": "Anterior", - "core.proceed": "Proceder", - "core.pulltorefresh": "''Pull'' para refrescar", - "core.qrscanner": "Escanner QR", - "core.question.answer": "Respuesta", - "core.question.answersaved": "Respuesta guardada", - "core.question.cannotdeterminestatus": "No pudo determinarse el estado", - "core.question.certainty": "Certeza", - "core.question.complete": "Completada", - "core.question.correct": "Correcta", - "core.question.errorattachmentsnotsupported": "La aplicación todavía no soporta anexarle archivos a las respuestas.", - "core.question.errorinlinefilesnotsupported": "La aplicación aun no soporta el editar archivos en línea.", - "core.question.errorquestionnotsupported": "Este tipo de pregunta no está soportada por la App: {{$a}}.", - "core.question.feedback": "Retroalimentación", - "core.question.howtodraganddrop": "Tocar para seleccionar y tocar para soltar.", - "core.question.incorrect": "Incorrecta", - "core.question.information": "Información", - "core.question.invalidanswer": "Respuesta incompleta", - "core.question.notanswered": "Sin contestar", - "core.question.notyetanswered": "Sin responder aún", - "core.question.partiallycorrect": "Parcialmente correcta", - "core.question.questionmessage": "Pregunta {{$a}}: {{$b}}", - "core.question.questionno": "Pregunta {{$a}}", - "core.question.requiresgrading": "Requiere re-calificar", - "core.quotausage": "Actualmente Usted ha usado {{$a.used}} de sus {{$a.total}} límite.", - "core.rating.aggregateavg": "Promedio de valuaciones (ratings)", - "core.rating.aggregatecount": "Número de valuaciones (ratings)", - "core.rating.aggregatemax": "Valuación (rating) máxima", - "core.rating.aggregatemin": "Valuación (rating) mínima", - "core.rating.aggregatesum": "Suma de valuaciones (ratings)", - "core.rating.noratings": "No se han enviado valuaciones (ratings)", - "core.rating.rating": "Valuación (rating)", - "core.rating.ratings": "Valuaciones (ratings)", - "core.redirectingtosite": "Usted será redireccionado al sitio.", - "core.refresh": "Refrescar", - "core.remove": "Quitar", - "core.removefiles": "Quitar archivos {{$a}}", - "core.required": "Obligatorio", - "core.requireduserdatamissing": "A este usuario le faltan algunos datos requeridos del perfil. Por favor, llene estos datos en su sitio e inténtelo nuevamente.
              {{$a}}", - "core.resourcedisplayopen": "Abrir", - "core.resources": "Recursos", - "core.restore": "Restaurar", - "core.restricted": "Restringido", - "core.retry": "Reintentar", - "core.save": "Guardar", - "core.savechanges": "Guardar cambios", - "core.scanqr": "Escanear código QR", - "core.search": "Buscar", - "core.searching": "Buscando", - "core.searchresults": "Resultados de la búsqueda", - "core.sec": "segundos", - "core.secs": "segundos", - "core.seemoredetail": "Haga clic aquí para ver más detalles", - "core.selectacategory": "Por favor, seleccione una categoría", - "core.selectacourse": "Elija un curso", - "core.selectagroup": "Elija un grupo", - "core.send": "Enviar", - "core.sending": "Enviando", - "core.serverconnection": "Error al conectarse al servidor", - "core.settings.about": "Acerca de", - "core.settings.appsettings": "Configuraciones de la App", - "core.settings.appversion": "Versión de App", - "core.settings.cannotsyncoffline": "No puede sincronizarse fuera de línea.", - "core.settings.cannotsyncwithoutwifi": "No puede sincronizarse porque las configuraciones actuales solamente permiten sincronizar cuando está conectado a Wi-Fi. Por favor, conéctese a una red Wi-Fi.", - "core.settings.colorscheme": "Esquema de Colores", - "core.settings.colorscheme-auto": "Auto (basado en configuraciones del sistema)", - "core.settings.colorscheme-dark": "Oscuro", - "core.settings.colorscheme-light": "Claro", - "core.settings.compilationinfo": "Información de compilación", - "core.settings.copyinfo": "Copiar información del dispositivo al portapapeles", - "core.settings.cordovadevicemodel": "Modelo de Dispositivo Cordova", - "core.settings.cordovadeviceosversion": "Versión de OS de Dispositivo Cordova", - "core.settings.cordovadeviceplatform": "Plataforma de Dispositivo Cordova", - "core.settings.cordovadeviceuuid": "UUID del Dispositivo Cordova", - "core.settings.cordovaversion": "Versión Cordova", - "core.settings.currentlanguage": "Idioma actual", - "core.settings.debugdisplay": "Mostrar mensajes de depuración", - "core.settings.debugdisplaydescription": "Si se habilita, errores modales mostrará más datos acerca del error si fuese posible,", - "core.settings.deletesitefiles": "¿Está seguro de que desea eliminar todos los archivos descargados y datos cacheados del sitio '{{sitename}}'? Usted no podrá usar la App en modo fuera de línea.", - "core.settings.deletesitefilestitle": "Eliminar archivos del sitio", - "core.settings.deviceinfo": "Información del dispositivo", - "core.settings.deviceos": "Sistema Operativo del dispositivo", - "core.settings.disableall": "Deshabilitar notificaciones", - "core.settings.disabled": "Deshabilitado", - "core.settings.displayformat": "Formato de visualización", - "core.settings.enabledownloadsection": "Habilitar descargar secciones", - "core.settings.enablefirebaseanalytics": "Habilitar Analítica Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Si se habilita, la App colectará uso anónimo de datos .", - "core.settings.enablerichtexteditor": "Habilitar editor de texto", - "core.settings.enablerichtexteditordescription": "Si se habilita, un editor de texto estará disponible al ingresar contenido.", - "core.settings.enablesyncwifi": "Permitir sincronización solamente cuando tenga Wi-Fi", - "core.settings.entriesincache": "{{$a}} entradas en caché", - "core.settings.errordeletesitefiles": "Error al eliminar archivos del sitio.", - "core.settings.errorsyncsite": "Error al sincronizar los datos del sitio; por favor revise su conexión de Internet e inténtelo de nuevo.", - "core.settings.estimatedfreespace": "Espacio libre estimado", - "core.settings.filesystemroot": "Raíz del sistema-de-archivos", - "core.settings.fontsize": "Tamaño del texto", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "Esta configuración ha sido forzada por su configuración del sitio.", - "core.settings.general": "General", - "core.settings.language": "Idioma", - "core.settings.license": "Licencia", - "core.settings.localnotifavailable": "Notificaciones locales disponibles", - "core.settings.locationhref": "URL Vista Web", - "core.settings.locked": "Bloqueado", - "core.settings.loggedin": "En línea", - "core.settings.loggedoff": "Fuera de línea", - "core.settings.navigatorlanguage": "Idioma del Navegador", - "core.settings.navigatoruseragent": "Agente del usuario del Navegador", - "core.settings.networkstatus": "Estado de conexión a Internet", - "core.settings.opensourcelicenses": "Licencias de Código Abierto", - "core.settings.preferences": "Preferencias", - "core.settings.privacypolicy": "Política de privacidad", - "core.settings.publisher": "Publicador/Editor", - "core.settings.pushid": "ID notificaciones push", - "core.settings.reportinbackground": "Reportar errores automáticamente", - "core.settings.screen": "Información de pantalla", - "core.settings.settings": "Configuración", - "core.settings.showdownloadoptions": "Mostrar opciones para descarga", - "core.settings.siteinfo": "Información del sitio", - "core.settings.sites": "Sitios", - "core.settings.spaceusage": "Espacio", - "core.settings.spaceusagehelp": "Al eliminar la información almacenada del sitio se eliminarán todos los datos fuera de línea del sitio. Esta información le permite usar la App cuando Usted está fuera de línea.", - "core.settings.synchronization": "Sincronización", - "core.settings.synchronizenow": "Sincronizar ahorita", - "core.settings.synchronizenowhelp": "Al sincronizar un sitio se enviarán cambios pendientes y toda la actividad fuera de línea almacenada en el dispositivo y se sincronizarán algunos datos tales como mensajes y notificaciones.", - "core.settings.syncsettings": "Configuraciones de la sincronización", - "core.settings.total": "Total", - "core.settings.wificonnection": "Conexión Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "Elija una cuenta en la cual almacenar el archivo.", - "core.sharedfiles.chooseactionrepeatedfile": "Ya existe un archivo con este nombre. ¿Desea remplazar el archivo existente o renombrarlo a \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "No hay sitios almacenados. Por favor, añada un sitio antes de compartir un archivo con la Appp.", - "core.sharedfiles.nosharedfiles": "No hay archivos compartidos almacenados en este sitio.", - "core.sharedfiles.nosharedfilestoupload": "Usted no tiene archivos para subir aquí. Si desea subir un archivo desde otra App, localice ese archivo y haga clic en el botón para 'Abrir en'.", - "core.sharedfiles.rename": "Renombrar", - "core.sharedfiles.replace": "Remplazar", - "core.sharedfiles.sharedfiles": "Archivos compartidos", - "core.sharedfiles.successstorefile": "Archivo almacenado exitosamente. Seleccione el archivo a subir a sus archivos privados o para usar en una actividad.", - "core.show": "Mostrar", - "core.showless": "Mostrar menos...", - "core.showmore": "Mostrar más...", - "core.site": "Sitio", - "core.sitehome.sitehome": "Página inicial del sitio", - "core.sitehome.sitenews": "Anuncios del sitio", - "core.sitemaintenance": "Este sitio está en fase de mantenimiento y no está disponible en este momento", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Saltar", - "core.sorry": "Lo siento...", - "core.sort": "Ordenar", - "core.sortby": "Ordenar por", - "core.start": "Inicio", - "core.storingfiles": "Almacenando archivos", - "core.strftimedate": "%d de %B de %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d de %B", - "core.strftimedatetime": "%d de %B de %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%y, %H:%M", - "core.strftimedaydate": "%A, %d de %B de %Y", - "core.strftimedaydatetime": "%A, %d de %B de %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d de %b de %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Enviar", - "core.success": "Éxito", - "core.tablet": "Tableta", - "core.tag.defautltagcoll": "Colección por defecto", - "core.tag.errorareanotsupported": "Esta área de marca no está soportada por la App.", - "core.tag.inalltagcoll": "En todas partes", - "core.tag.itemstaggedwith": "{{$a.tagarea}} marcadas con \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Sin resultados para \"{{$a}}\"", - "core.tag.notagsfound": "No se encontraron marcas que coincidan con \"{{$a}}\"", - "core.tag.searchtags": "Buscar marcas", - "core.tag.showingfirsttags": "Mostrando las {{$a}} marcas más populares", - "core.tag.tag": "Marca", - "core.tag.tagarea_course": "Cursos", - "core.tag.tagarea_course_modules": "Actividades y recursos", - "core.tag.tagarea_post": "Publicacines en blog", - "core.tag.tagarea_user": "Intereses del usuario", - "core.tag.tags": "Marcas", - "core.tag.warningareasnotsupported": "Algunas de las áreas de marca no son mostradas porque no están soportadas por la App.", - "core.teachers": "Profesores", - "core.thereisdatatosync": "Existen {{$a}} fuera de línea para ser sincronizados/as.", - "core.thisdirection": "ltr", - "core.time": "Hora", - "core.timesup": "¡Se ha pasado el tiempo!", - "core.today": "Hoy", - "core.tryagain": "Intentar nuevamente", - "core.twoparagraphs": "{{p1}}

              {{p2}}", - "core.uhoh": "¡Órale!", - "core.unexpectederror": "Error inesperado. Por favor cierre y vuelva a abrir la aplicación y entonces inténtelo de nuevo", - "core.unicodenotsupported": "Los emojis no están soportado en este sitio; esos caracteres serán quitados cuando el mensaje sea enviado.", - "core.unicodenotsupportedcleanerror": "Se encontró texto vacío al limpiar caracteres Unicode.", - "core.unknown": "Desconocido", - "core.unlimited": "Sin límite", - "core.unzipping": "Descomprimiendo ZIP", - "core.updaterequired": "Se necesita actualizar App", - "core.updaterequireddesc": "Por favor actualice su App a la versión {{$a}}", - "core.upgraderunning": "El sitio está siendo actualizado, por favor trate más tarde.", - "core.user": "Usuario", - "core.user.address": "Dirección", - "core.user.city": "Ciudad", - "core.user.contact": "Contacto", - "core.user.country": "País", - "core.user.description": "Descripción", - "core.user.details": "Detalles", - "core.user.detailsnotavailable": "Los detalles de este usuario no están disponibles para Usted.", - "core.user.editingteacher": "Profesor", - "core.user.email": "Dirección Email", - "core.user.emailagain": "Correo (de nuevo)", - "core.user.errorloaduser": "Error al cargar usuario.", - "core.user.firstname": "Nombre", - "core.user.interests": "Intereses", - "core.user.lastname": "Apellido(s)", - "core.user.manager": "Mánager", - "core.user.newpicture": "Imagen nueva", - "core.user.noparticipants": "No se encontraron participantes en este curso", - "core.user.participants": "Participantes", - "core.user.phone1": "Teléfono", - "core.user.phone2": "Teléfono móvil", - "core.user.roles": "Roles", - "core.user.sendemail": "Email", - "core.user.student": "Estudiante", - "core.user.teacher": "Profesor sin permiso de edición", - "core.user.webpage": "Página web", - "core.userdeleted": "Esta cuenta se ha cancelado", - "core.userdetails": "Detalles de usuario", - "core.usernotfullysetup": "Usuario no cnfigurado completamente", - "core.users": "Usuarios", - "core.view": "Ver", - "core.viewcode": "Ver código", - "core.vieweditor": "Ver editor", - "core.viewembeddedcontent": "Ver contenido incrustado", - "core.viewprofile": "Ver perfil", - "core.warningofflinedatadeleted": "Los datos fuera-de-línea de {{component}} '{{name}}' han sido borrados. {{error}}", - "core.whatisyourage": "¿Qué edad tiene?", - "core.wheredoyoulive": "¿En qué país vive?", - "core.whoissiteadmin": "\"Administradores del sitio\" son las personas que administran el Moodle en su escuela/universidad/compañía o su organización educativa. Si Usted no sabe como ponerse en contacto con ellos, por favor póngase en contacto con sus profesores/entrenadores.", - "core.whoops": "¡Órale!", - "core.whyisthishappening": "¿Porqué está pasando esto?", - "core.whyisthisrequired": "¿Por qué es necesario esto?", - "core.wsfunctionnotavailable": "La función servicio web no está disponible.", - "core.year": "año", - "core.years": "años", - "core.yes": "Sí", - "core.youreoffline": "Usted está desconectado", - "core.youreonline": "Usted está conectado nuevamente" -} \ No newline at end of file diff --git a/src/assets/lang/es.json b/src/assets/lang/es.json deleted file mode 100644 index a167d1f3c..000000000 --- a/src/assets/lang/es.json +++ /dev/null @@ -1,2173 +0,0 @@ -{ - "addon.badges.alignment": "Alineación", - "addon.badges.badgedetails": "Detalles de la insignia", - "addon.badges.badges": "Insignias", - "addon.badges.bendorsement": "Confirmación", - "addon.badges.claimcomment": "Comentario a la Confirmación", - "addon.badges.claimid": "URL de petición", - "addon.badges.contact": "Contacto", - "addon.badges.dateawarded": "Fecha de la emisión", - "addon.badges.expired": "Expirada", - "addon.badges.expirydate": "Fecha de expiración", - "addon.badges.imageauthoremail": "Email del autor de la imagen", - "addon.badges.imageauthorname": "Nombre del autor de la imagen", - "addon.badges.imageauthorurl": "URL del autor de la imagen", - "addon.badges.imagecaption": "Leyenda de la imagen", - "addon.badges.issuancedetails": "Caducidad de la insignia", - "addon.badges.issuerdetails": "Detalles del emisor", - "addon.badges.issueremail": "Email", - "addon.badges.issuername": "Nombre del emisor", - "addon.badges.issuerurl": "URL del emisor", - "addon.badges.language": "Idioma", - "addon.badges.noalignment": "Esta insignia no tiene ninguna habilidad externa o estándar especificado.", - "addon.badges.nobadges": "No hay insignias disponibles", - "addon.badges.norelated": "Esta insignia no tiene ninguna insignia relacionada.", - "addon.badges.recipientdetails": "Detalles del destinatario", - "addon.badges.relatedbages": "Insignias relacionadas", - "addon.badges.version": "Versión", - "addon.badges.warnexpired": "(¡Esta insignia ha expirado!)", - "addon.block_activitymodules.pluginname": "Actividades", - "addon.block_activityresults.pluginname": "Resultados de la actividad", - "addon.block_badges.pluginname": "Insignias recientes", - "addon.block_blogmenu.pluginname": "Menú Blog", - "addon.block_blogrecent.pluginname": "Entradas de blog recientes", - "addon.block_blogtags.pluginname": "Marcas Blog", - "addon.block_calendarmonth.pluginname": "Calendario", - "addon.block_calendarupcoming.pluginname": "Eventos próximos", - "addon.block_comments.pluginname": "Comentarios", - "addon.block_completionstatus.pluginname": "Estatus de finalización del curso", - "addon.block_glossaryrandom.pluginname": "Entrada aleatoria del glosario", - "addon.block_learningplans.pluginname": "Planes de aprendizaje", - "addon.block_myoverview.all": "Todos (a excepción de los eliminados de la vista)", - "addon.block_myoverview.allincludinghidden": "Todo", - "addon.block_myoverview.favourites": "Destacados", - "addon.block_myoverview.future": "Futuros", - "addon.block_myoverview.hiddencourses": "Eliminados de la vista", - "addon.block_myoverview.inprogress": "En progreso", - "addon.block_myoverview.lastaccessed": "Último accedido", - "addon.block_myoverview.morecourses": "Más cursos", - "addon.block_myoverview.nocourses": "Sin cursos", - "addon.block_myoverview.past": "Pasados", - "addon.block_myoverview.pluginname": "Vista general de curso", - "addon.block_myoverview.shortname": "Nombre corto", - "addon.block_myoverview.title": "Nombre del curso", - "addon.block_newsitems.pluginname": "Avisos recientes", - "addon.block_onlineusers.pluginname": "Usuarios en línea", - "addon.block_privatefiles.pluginname": "Archivos privados", - "addon.block_recentactivity.pluginname": "Actividad reciente", - "addon.block_recentlyaccessedcourses.nocourses": "No hay cursos recientes", - "addon.block_recentlyaccessedcourses.pluginname": "Cursos accedidos recientemente", - "addon.block_recentlyaccesseditems.noitems": "Sin elementos recientes", - "addon.block_recentlyaccesseditems.pluginname": "Elementos accedidos recientemente", - "addon.block_rssclient.pluginname": "Canal RSS remoto", - "addon.block_selfcompletion.pluginname": "Autocompletar", - "addon.block_sitemainmenu.pluginname": "Menú principal", - "addon.block_starredcourses.nocourses": "No hay cursos destacados", - "addon.block_starredcourses.pluginname": "Cursos destacados", - "addon.block_tags.pluginname": "Marcas", - "addon.block_timeline.duedate": "Fecha de vencimiento", - "addon.block_timeline.next30days": "Próximos 30 días", - "addon.block_timeline.next3months": "Próximos 3 meses", - "addon.block_timeline.next6months": "Próximos 6 meses", - "addon.block_timeline.next7days": "Próximos 7 días", - "addon.block_timeline.nocoursesinprogress": "No hay cursos actuales", - "addon.block_timeline.noevents": "No hay actividades previstas", - "addon.block_timeline.overdue": "Atrasados", - "addon.block_timeline.pluginname": "Línea de tiempo", - "addon.block_timeline.sortbycourses": "Ordenar por curso", - "addon.block_timeline.sortbydates": "Ordenar por fecha", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Entradas del blog", - "addon.blog.errorloadentries": "Error cargando entradas del blog.", - "addon.blog.linktooriginalentry": "Enlace a la entrada de blog original", - "addon.blog.noentriesyet": "Entradas no visibles aquí", - "addon.blog.publishtonoone": "Usted (borrador)", - "addon.blog.publishtosite": "Todos en este sitio", - "addon.blog.publishtoworld": "Todo el mundo", - "addon.blog.showonlyyourentries": "Mostrar sólo sus entradas del blog.", - "addon.blog.siteblogheading": "Blog del sitio", - "addon.calendar.allday": "Todo el día", - "addon.calendar.calendar": "Calendario", - "addon.calendar.calendarevent": "Evento de calendario", - "addon.calendar.calendarevents": "Eventos de calendario", - "addon.calendar.calendarreminders": "Recordatorios del calendario", - "addon.calendar.categoryevents": "Eventos de categoría", - "addon.calendar.confirmeventdelete": "¿Está seguro de que desea eliminar el evento \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "El evento \"{{$a.name}}\" es parte de una serie. ¿Quiere eliminar solamente este evento, o todos los {{$a.count}} eventos en la serie?", - "addon.calendar.courseevents": "Eventos de curso", - "addon.calendar.currentmonth": "Mes actual", - "addon.calendar.daynext": "Próximo día", - "addon.calendar.dayprev": "Día anterior", - "addon.calendar.defaultnotificationtime": "Hora de notificación por defecto", - "addon.calendar.deleteallevents": "Eliminar todos los eventos", - "addon.calendar.deleteevent": "Eliminar evento", - "addon.calendar.deleteoneevent": "Eliminar este evento", - "addon.calendar.durationminutes": "Duración en minutos", - "addon.calendar.durationnone": "Sin duración", - "addon.calendar.durationuntil": "Hasta", - "addon.calendar.editevent": "Editando evento", - "addon.calendar.errorloadevent": "Error cargando el evento.", - "addon.calendar.errorloadevents": "Error cargando los eventos.", - "addon.calendar.eventcalendareventdeleted": "Evento de calendario borrado", - "addon.calendar.eventduration": "Duración", - "addon.calendar.eventendtime": "Hora final", - "addon.calendar.eventkind": "Tipo de evento", - "addon.calendar.eventname": "Título del evento", - "addon.calendar.eventstarttime": "Hora de comienzo", - "addon.calendar.eventtype": "Tipo de evento", - "addon.calendar.fri": "Vie", - "addon.calendar.friday": "Viernes", - "addon.calendar.gotoactivity": "Ir a la actividad", - "addon.calendar.groupevents": "Eventos de grupo", - "addon.calendar.invalidtimedurationminutes": "La duración en minutos introducida no es válida; por favor, introduzca una duración en minutos mayor que 0 o seleccione sin duración.", - "addon.calendar.invalidtimedurationuntil": "La fecha y hora seleccionadas para la duración hasta es anterior a la fecha de inicio del evento. Por favor, corríjalo antes de seguir.", - "addon.calendar.mon": "Lun", - "addon.calendar.monday": "Lunes", - "addon.calendar.monthlyview": "Vista del Mes", - "addon.calendar.newevent": "Nuevo evento", - "addon.calendar.noevents": "No hay eventos", - "addon.calendar.nopermissiontoupdatecalendar": "Lo sentimos, no tiene permisos para actualizar los eventos del calendario.", - "addon.calendar.reminders": "Recordatorios", - "addon.calendar.repeatedevents": "Eventos repetidos", - "addon.calendar.repeateditall": "Aplicar también los cambios a los otros {{$a}} eventos de esta serie", - "addon.calendar.repeateditthis": "Aplicar los cambios sólo a este evento", - "addon.calendar.repeatevent": "Repetir este evento", - "addon.calendar.repeatweeksl": "Repetir semanalmente, creando juntos", - "addon.calendar.sat": "Sáb", - "addon.calendar.saturday": "Sábado", - "addon.calendar.setnewreminder": "Crear un nuevo recordatorio", - "addon.calendar.siteevents": "Eventos Globales", - "addon.calendar.sun": "Dom", - "addon.calendar.sunday": "Domingo", - "addon.calendar.thu": "Jue", - "addon.calendar.thursday": "Jueves", - "addon.calendar.today": "Hoy", - "addon.calendar.tomorrow": "Mañana", - "addon.calendar.tue": "Mar", - "addon.calendar.tuesday": "Martes", - "addon.calendar.typecategory": "Evento de categoría", - "addon.calendar.typeclose": "Evento cerrar", - "addon.calendar.typecourse": "Evento de curso", - "addon.calendar.typedue": "Evento esperado", - "addon.calendar.typegradingdue": "Evento de calificación pendiente", - "addon.calendar.typegroup": "Evento de grupo", - "addon.calendar.typeopen": "Evento abrir", - "addon.calendar.typesite": "Evento de sitio", - "addon.calendar.typeuser": "Evento de usuario", - "addon.calendar.upcomingevents": "Eventos próximos", - "addon.calendar.userevents": "Eventos de usuario", - "addon.calendar.wed": "Mié", - "addon.calendar.wednesday": "Miércoles", - "addon.calendar.when": "Cuándo", - "addon.calendar.yesterday": "Ayer", - "addon.competency.activities": "Actividades", - "addon.competency.competencies": "Competencias", - "addon.competency.competenciesmostoftennotproficientincourse": "Competencias que más a menudo no se superan en este curso", - "addon.competency.coursecompetencies": "Competencias del curso", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Las calificaciones de competencias de este curso no afectan los planes de aprendizaje.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Las calificaciones de competencias en este curso actualizan de inmediato los planes de aprendizaje.", - "addon.competency.crossreferencedcompetencies": "Competencias referenciadas", - "addon.competency.duedate": "Fecha límite", - "addon.competency.errornocompetenciesfound": "No se encontraron competencias", - "addon.competency.evidence": "Evidencia", - "addon.competency.evidence_competencyrule": "Se cumplió la regla de la competencia.", - "addon.competency.evidence_coursecompleted": "El curso '{{$a}}' ha sido completado.", - "addon.competency.evidence_coursemodulecompleted": "La actividad '{{$a}}' ha sido completada.", - "addon.competency.evidence_courserestored": "La valoración fue restaurada junto con el curso '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "La evidencia de aprendizaje previo '{{$a}}' fue enlazada.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "La evidencia de aprendizaje previo '{{$a}}' fue desvinculada.", - "addon.competency.evidence_manualoverride": "La valoración de competencia fue configurada manualmente.", - "addon.competency.evidence_manualoverrideincourse": "La valoración de competencia fue configurada manualmente en el curso '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "La valoración de competencia fue configurada manualmente en el plan de aprendizaje '{{$a}}'.", - "addon.competency.learningplancompetencies": "Competencias del plan de aprendizaje", - "addon.competency.learningplans": "Planes de aprendizaje", - "addon.competency.myplans": "Mis planes de aprendizaje", - "addon.competency.noactivities": "Sin actividades", - "addon.competency.nocompetencies": "Sin competencias", - "addon.competency.nocompetenciesincourse": "No se han vinculado competencias con este curso.", - "addon.competency.nocrossreferencedcompetencies": "No se han referenciado otras competencias a esta competencia.", - "addon.competency.noevidence": "Sin evidencias", - "addon.competency.noplanswerecreated": "No se han creado planes de aprendizaje.", - "addon.competency.nouserplanswithcompetency": "Ningún plan de aprendizaje contiene esta competencia.", - "addon.competency.path": "Ruta:", - "addon.competency.planstatusactive": "Activo", - "addon.competency.planstatuscomplete": "Completado", - "addon.competency.planstatusdraft": "Borrador", - "addon.competency.planstatusinreview": "En revisión", - "addon.competency.planstatuswaitingforreview": "Esperando revisión", - "addon.competency.proficient": "Superada", - "addon.competency.progress": "Avance", - "addon.competency.rating": "Calificación", - "addon.competency.reviewstatus": "Estado de la revisión", - "addon.competency.status": "Estado", - "addon.competency.template": "Plantilla de plan de aprendizaje", - "addon.competency.uponcoursecompletion": "Al terminar el curso:", - "addon.competency.usercompetencystatus_idle": "No activo", - "addon.competency.usercompetencystatus_inreview": "En revision", - "addon.competency.usercompetencystatus_waitingforreview": "Esperando revisión", - "addon.competency.userplans": "Planes de aprendizaje", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} de {{$a.y}} competencias superadas", - "addon.competency.xcompetenciesproficientoutofyincourse": "Has superado {{$a.x}} de las {{$a.y}} competencias del curso.", - "addon.coursecompletion.complete": "Completado", - "addon.coursecompletion.completecourse": "Completar curso", - "addon.coursecompletion.completed": "Finalizado", - "addon.coursecompletion.completiondate": "Fecha de finalización", - "addon.coursecompletion.completionmenuitem": "Finalización", - "addon.coursecompletion.couldnotloadreport": "No se puede cargar el informe de finalización del curso, por favor inténtalo de nuevo más tarde.", - "addon.coursecompletion.coursecompletion": "Finalización del curso", - "addon.coursecompletion.criteria": "Criterios", - "addon.coursecompletion.criteriagroup": "Grupo de criterios", - "addon.coursecompletion.criteriarequiredall": "Son necesarios todos los criterios que aparecen más abajo", - "addon.coursecompletion.criteriarequiredany": "Es necesario cualquiera de los criterios que aparecen más abajo", - "addon.coursecompletion.inprogress": "En curso", - "addon.coursecompletion.manualselfcompletion": "Autocompletar manualmente", - "addon.coursecompletion.nottracked": "En este momento no se le está realizando un seguimiento en la finalización de este curso", - "addon.coursecompletion.notyetstarted": "Aún no comenzado", - "addon.coursecompletion.pending": "Pendiente", - "addon.coursecompletion.required": "Obligatorio", - "addon.coursecompletion.requiredcriteria": "Criterios necesarios", - "addon.coursecompletion.requirement": "Requisito", - "addon.coursecompletion.status": "Estado", - "addon.coursecompletion.viewcoursereport": "Ver informe del curso", - "addon.files.couldnotloadfiles": "La lista de archivos no ha podido cargarse.", - "addon.files.emptyfilelist": "No hay archivos que mostrar", - "addon.files.erroruploadnotworking": "Desafortunadamente en estos momentos no es posible subir archivos al sitio.", - "addon.files.files": "Archivos", - "addon.files.privatefiles": "Archivos privados", - "addon.files.sitefiles": "Archivos del sitio", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Configurar dispositivos", - "addon.messages.acceptandaddcontact": "Aceptar y añadir a los contactos", - "addon.messages.addcontact": "Añadir contacto", - "addon.messages.addcontactconfirm": "¿Estás seguro de querer añadir a {{$a}} a tus contactos?", - "addon.messages.addtofavourites": "Marcar conversación", - "addon.messages.addtoyourcontacts": "Añadir a tus contactos", - "addon.messages.blocknoncontacts": "Bloquear mensajes de usuarios que no figuren en mi lista de contactos", - "addon.messages.blockuser": "Bloquear usuario", - "addon.messages.blockuserconfirm": "¿Estás seguro de querer bloquear a {{$a}}?", - "addon.messages.contactableprivacy": "Aceptar mensajes desde:", - "addon.messages.contactableprivacy_coursemember": "Mis contactos y cualquier persona de mis cursos", - "addon.messages.contactableprivacy_onlycontacts": "Únicamente mis contactos", - "addon.messages.contactableprivacy_site": "Cualquiera", - "addon.messages.contactblocked": "Contacto bloqueado", - "addon.messages.contactlistempty": "Lista de contactos vacía", - "addon.messages.contactname": "Nombre del contacto", - "addon.messages.contactrequestsent": "Solicitud de contacto enviada", - "addon.messages.contacts": "Contactos", - "addon.messages.conversationactions": "Menú de acciones para conversación", - "addon.messages.decline": "Rechazar", - "addon.messages.deleteallconfirm": "¿Está seguro de que desea eliminar toda la conversación? Esto no borrará la conversación para otros participantes.", - "addon.messages.deleteallselfconfirm": "¿Está seguro de querer eliminar toda esta conversación personal?", - "addon.messages.deleteconversation": "Eliminar conversación", - "addon.messages.deleteforeveryone": "Borrar para mí y para todos los demás", - "addon.messages.deletemessage": "Eliminar mensaje", - "addon.messages.deletemessageconfirmation": "¿Está seguro que quiere eliminar este mensaje? Será eliminado solamente de su historial de mensaje y todavía será visible por el usuario que envió o recibió el mensaje.", - "addon.messages.errordeletemessage": "Error borrando el mensaje.", - "addon.messages.errorwhileretrievingcontacts": "Error al recuperar los contactos del servidor.", - "addon.messages.errorwhileretrievingdiscussions": "Error al recuperar las discusiones del servidor.", - "addon.messages.errorwhileretrievingmessages": "Error al recuperar los mensajes del servidor.", - "addon.messages.errorwhileretrievingusers": "Error al recuperar usuarios del servidor.", - "addon.messages.groupconversations": "Grupo", - "addon.messages.groupinfo": "Información del grupo", - "addon.messages.individualconversations": "Privado", - "addon.messages.info": "Información del usuario", - "addon.messages.isnotinyourcontacts": "{{$a}} no está en tus contactos", - "addon.messages.message": "Mensaje", - "addon.messages.messagenotsent": "El mensaje no fue enviado; por favor inténtelo nuevamente después.", - "addon.messages.messagepreferences": "Preferencias de mensajes", - "addon.messages.messages": "Mensajes", - "addon.messages.muteconversation": "Silenciar", - "addon.messages.mutedconversation": "Silenciar conversación", - "addon.messages.newmessage": "Nuevo mensaje", - "addon.messages.newmessages": "Nuevos mensajes", - "addon.messages.nocontactrequests": "No hay solicitudes de contacto", - "addon.messages.nocontactsgetstarted": "No hay contactos", - "addon.messages.nofavourites": "No hay mensajes destacados", - "addon.messages.nogroupconversations": "No hay conversaciónes de grupo", - "addon.messages.noindividualconversations": "No hay conversaciones privadas", - "addon.messages.nomessagesfound": "No se encontraron mensajes", - "addon.messages.noncontacts": "No contactos", - "addon.messages.nousersfound": "No se encuentran usuarios", - "addon.messages.numparticipants": "{{$a}} participantes", - "addon.messages.removecontact": "Eliminar contacto", - "addon.messages.removecontactconfirm": "¿Quieres quitar a {{$a}} de tus contactos?", - "addon.messages.removefromfavourites": "Desmarcar conversación", - "addon.messages.removefromyourcontacts": "Eliminar de los contactos", - "addon.messages.requests": "Solicitudes", - "addon.messages.requirecontacttomessage": "Debes pedir a {{$a}} que te añada como contacto para poder enviarle un mensaje.", - "addon.messages.searchcombined": "Buscar personas y mensajes", - "addon.messages.selfconversation": "Espacio personal", - "addon.messages.selfconversationdefaultmessage": "Guardar borradores de mensajes, notas, etc, para acceder a ellos más tarde.", - "addon.messages.sendcontactrequest": "Enviar solicitud de contacto", - "addon.messages.showdeletemessages": "Mostrar mensajes borrados", - "addon.messages.type_blocked": "Bloqueado", - "addon.messages.type_offline": "Desconectado", - "addon.messages.type_online": "En línea", - "addon.messages.type_search": "Resultados de la búsqueda", - "addon.messages.type_strangers": "Otros", - "addon.messages.unabletomessage": "No puedes enviar mensajes a este usuario", - "addon.messages.unblockuser": "Desbloquear usuario", - "addon.messages.unblockuserconfirm": "¿Estás seguro de querer desbloquear a {{$a}}?", - "addon.messages.unmuteconversation": "Habilitar notificaciones", - "addon.messages.useentertosend": "Usar \"intro\" para enviar", - "addon.messages.useentertosenddescdesktop": "Si deshabilitado: puede usar Ctrl+Entrar para enviar el mensaje", - "addon.messages.useentertosenddescmac": "Si deshabilitado: puede usar Cmd+Entrar para enviar el mensaje", - "addon.messages.userwouldliketocontactyou": "{{$a}} quiere contactar contigo", - "addon.messages.warningconversationmessagenotsent": "No se pudo enviar mensaje(s) a conversación {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "No se pudo enviar mensaje(s) al usuario {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Quiere contactar contigo", - "addon.messages.you": "Tú:", - "addon.messages.youhaveblockeduser": "Ha bloqueado a este usuario.", - "addon.messages.yourcontactrequestpending": "Tu solicitud de contacto a {{$a}} sigue pendiente", - "addon.mod_assign.acceptsubmissionstatement": "Por favor acepte las condiciones de envío.", - "addon.mod_assign.addattempt": "Permitir otro intento", - "addon.mod_assign.addnewattempt": "Añadir una nueva entrega", - "addon.mod_assign.addnewattemptfromprevious": "Añadir un nuevo intento basado en el envío anterior", - "addon.mod_assign.addsubmission": "Agregar entrega", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Los detalles de la tarea y el formulario de entregas estarán disponibles en {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Permitir entregas desde", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Esta tarea aceptará entregas desde el {{$a}}", - "addon.mod_assign.applytoteam": "Aplicar las mismas calificaciones y comentarios al grupo completo", - "addon.mod_assign.assignmentisdue": "Tarea pendiente", - "addon.mod_assign.attemptnumber": "Número del intento", - "addon.mod_assign.attemptreopenmethod": "Permitir reapertura", - "addon.mod_assign.attemptreopenmethod_manual": "Manual", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automática hasta aprobar", - "addon.mod_assign.attemptsettings": "Configuración de intentos", - "addon.mod_assign.cannoteditduetostatementsubmission": "No puede añadir o editar un envío en la app porque no pudimos recuperar las condiciones de envío del sitio.", - "addon.mod_assign.cannotgradefromapp": "Algunos métodos de calificación no son compatibles aún con la aplicación y no se pueden modificar.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "No puede enviar para calificar en la app porque no pudimos recuperar las condiciones de envío del sitio.", - "addon.mod_assign.confirmsubmission": "¿Está seguro que quiere enviar su trabajo para que sea evaluado? Una vez enviado ya no podrá realizar modificaciones.", - "addon.mod_assign.currentattempt": "Este es el intento {{$a}}.", - "addon.mod_assign.currentattemptof": "Este es el intento {{$a.attemptnumber}} ( {{$a.maxattempts}} intentos permitidos ).", - "addon.mod_assign.currentgrade": "Calificación actual en el libro de calificaciones", - "addon.mod_assign.cutoffdate": "Fecha límite", - "addon.mod_assign.defaultteam": "Grupo predeterminado", - "addon.mod_assign.duedate": "Fecha de entrega", - "addon.mod_assign.duedateno": "No hay fecha de entrega", - "addon.mod_assign.duedatereached": "La fecha de vencimiento de esta tarea ya ha pasado", - "addon.mod_assign.editingstatus": "Edición de estado", - "addon.mod_assign.editsubmission": "Editar entrega", - "addon.mod_assign.erroreditpluginsnotsupported": "No puede añadir o editar un envío en la app porque algunos plugins no tienen soporte para editar:", - "addon.mod_assign.errorshowinginformation": "No se puede mostrar la información del envío", - "addon.mod_assign.extensionduedate": "Ampliar plazo", - "addon.mod_assign.feedbacknotsupported": "La retroalimentación no está soportada por la app y podría no mostrar la información completa", - "addon.mod_assign.grade": "Calificación", - "addon.mod_assign.graded": "Calificado", - "addon.mod_assign.gradedby": "Calificado por", - "addon.mod_assign.gradedfollowupsubmit": "Calificado - recibida nueva entrega", - "addon.mod_assign.gradedon": "Calificado sobre", - "addon.mod_assign.gradelocked": "Esta calificación está bloqueada o ha sido modificada en el libro de calificaciones", - "addon.mod_assign.gradenotsynced": "Calificación no sincronizada", - "addon.mod_assign.gradeoutof": "Calificación sobre {{$a}}", - "addon.mod_assign.gradingstatus": "Estado de la calificación", - "addon.mod_assign.groupsubmissionsettings": "Configuración de entrega por grupo", - "addon.mod_assign.hiddenuser": "Participante", - "addon.mod_assign.latesubmissions": "Entrega fuera de plazo", - "addon.mod_assign.latesubmissionsaccepted": "Permitido hasta {{$a}}", - "addon.mod_assign.markingworkflowstate": "Estado del workflow (flujo de trabajo) de calificaciones", - "addon.mod_assign.markingworkflowstateinmarking": "En proceso de calificación", - "addon.mod_assign.markingworkflowstateinreview": "En revisión", - "addon.mod_assign.markingworkflowstatenotmarked": "No calificada", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Lista para publicar", - "addon.mod_assign.markingworkflowstatereadyforreview": "Calificación terminada", - "addon.mod_assign.markingworkflowstatereleased": "Publicada", - "addon.mod_assign.modulenameplural": "Tareas", - "addon.mod_assign.multipleteams": "Miembro de más de un grupo.", - "addon.mod_assign.multipleteams_desc": "Esta tarea requiere entrega por grupos. Usted es miembro de más de un grupo. Para poder realizar entregas debe ser miembro solamente de un grupo. Por favor, contacte con su profesor para que modifique su pertenencia a grupos.", - "addon.mod_assign.noattempt": "No entregado", - "addon.mod_assign.nomoresubmissionsaccepted": "Permitido solo para miembros que han recibido una extensión", - "addon.mod_assign.noonlinesubmissions": "Esta tarea no requiere que usted envíe nada de forma online", - "addon.mod_assign.nosubmission": "No se ha enviado nada en esta tarea", - "addon.mod_assign.notallparticipantsareshown": "Los participantes sin envíos no se muestran", - "addon.mod_assign.noteam": "No perteneces a ningún grupo.", - "addon.mod_assign.noteam_desc": "Esta tarea requiere entrega por grupos. Usted no es miempro de ningún grupo, así que no puede realizar entregas. Por favor, contacte con su profesor para que le agregue a un grupo.", - "addon.mod_assign.notgraded": "Sin calificar", - "addon.mod_assign.numberofdraftsubmissions": "Borradores", - "addon.mod_assign.numberofparticipants": "Participantes", - "addon.mod_assign.numberofsubmissionsneedgrading": "Pendientes por calificar", - "addon.mod_assign.numberofsubmittedassignments": "Enviados", - "addon.mod_assign.numberofteams": "Grupos", - "addon.mod_assign.numwords": "{{$a}} palabras", - "addon.mod_assign.outof": "{{$a.current}} de {{$a.total}}", - "addon.mod_assign.overdue": "La Tarea está retrasada por: {{$a}}", - "addon.mod_assign.submission": "Entrega", - "addon.mod_assign.submissioneditable": "El estudiante puede editar esta entrega", - "addon.mod_assign.submissionnoteditable": "El estudiante no puede editar esta entrega", - "addon.mod_assign.submissionnotsupported": "Este envío no está soportada por la app y es posible que no muestre la información completa", - "addon.mod_assign.submissionslocked": "Esta tarea no acepta entregas", - "addon.mod_assign.submissionstatus": "Estado de la entrega", - "addon.mod_assign.submissionstatus_": "Sin entrega", - "addon.mod_assign.submissionstatus_draft": "Borrador (no enviado)", - "addon.mod_assign.submissionstatus_marked": "Calificado", - "addon.mod_assign.submissionstatus_new": "No entregado", - "addon.mod_assign.submissionstatus_reopened": "Reabierto", - "addon.mod_assign.submissionstatus_submitted": "Enviado para calificar", - "addon.mod_assign.submissionstatusheading": "Estado de la entrega", - "addon.mod_assign.submissionteam": "Grupo", - "addon.mod_assign.submitassignment": "Enviar tarea", - "addon.mod_assign.submitassignment_help": "Una vez que esta tarea se haya enviado usted no podrá hacer más cambios.", - "addon.mod_assign.submittedearly": "La tarea fue enviada {{$a}} antes", - "addon.mod_assign.submittedlate": "La tarea fue enviada {{$a}} después", - "addon.mod_assign.timemodified": "Última modificación", - "addon.mod_assign.timeremaining": "Tiempo restante", - "addon.mod_assign.ungroupedusers": "Si se activa la configuración \"se requiere formar parte de un grupo para realizar la entrega\" se evitará que los usuarios no asignados a los grupos realicen entregas.", - "addon.mod_assign.ungroupedusersoptional": "La opción 'Entregar en grupo' está habilitada y algunos usuarios o no son miembros de ningún grupo o son miembros de más de un grupo. Por favor tener en cuenta que en estos casos los estudiantes entregaran sus trabajos como \"Grupo Predeterminado\".", - "addon.mod_assign.unlimitedattempts": "Ilimitado", - "addon.mod_assign.userswhoneedtosubmit": "Componentes del equipo pendientes de entrega: {{$a}}", - "addon.mod_assign.userwithid": "Usuario con ID {{id}}", - "addon.mod_assign.viewsubmission": "Ver entrega", - "addon.mod_assign.warningsubmissiongrademodified": "La calificación del envío se modificó en el sitio.", - "addon.mod_assign.warningsubmissionmodified": "El envío del usuario fue modificado en el sitio.", - "addon.mod_assign.wordlimit": "Límite de palabras", - "addon.mod_assign_feedback_comments.pluginname": "Comentarios de retroalimentación", - "addon.mod_assign_feedback_editpdf.pluginname": "Anotación PDF", - "addon.mod_assign_feedback_file.pluginname": "Archivo de retroalimentación", - "addon.mod_assign_submission_comments.pluginname": "Comentarios de la entrega", - "addon.mod_assign_submission_file.pluginname": "Archivos enviados", - "addon.mod_assign_submission_onlinetext.pluginname": "Entregas de texto en línea", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "El límite de palabras para esta entrada es de {{$a.limit}} palabras y estas intentando de enviar {{$a.count}} palabras. Por favor, revisa el contenido e inténtalo de nuevo.", - "addon.mod_book.errorchapter": "Error al leer el capítulo del libro.", - "addon.mod_book.modulenameplural": "Libros", - "addon.mod_book.navnexttitle": "Siguiente: {{$a}}", - "addon.mod_book.navprevtitle": "Anterior: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Capítulos del libro", - "addon.mod_book.toc": "Tabla de contenidos", - "addon.mod_chat.beep": "Beep", - "addon.mod_chat.chatreport": "Sesiones", - "addon.mod_chat.currentusers": "Usuarios", - "addon.mod_chat.enterchat": "Entrar a la sala", - "addon.mod_chat.entermessage": "Inserta tu mensaje", - "addon.mod_chat.errorwhileconnecting": "Se ha producido un error conectando al chat.", - "addon.mod_chat.errorwhilegettingchatdata": "Se ha producido un error recuperando los datos del chat.", - "addon.mod_chat.errorwhilegettingchatusers": "Se ha producido un error recuperando los usuarios del chat.", - "addon.mod_chat.errorwhileretrievingmessages": "Error al recuperar los mensajes del servidor.", - "addon.mod_chat.errorwhilesendingmessage": "Se ha producido un error al enviar el mensaje.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} envía un beep a todos", - "addon.mod_chat.messagebeepsyou": "{{$a}} le acaba de enviar un beep", - "addon.mod_chat.messageenter": "{{$a}} entró a la sala", - "addon.mod_chat.messageexit": "{{$a}} salió de la sala", - "addon.mod_chat.messages": "Mensajes", - "addon.mod_chat.messageyoubeep": "Su señal de sonido beep {{$a}}", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "Usted debe de estar conectado a Internet para enviar mensajes", - "addon.mod_chat.nomessages": "Aún no hay mensajes", - "addon.mod_chat.nosessionsfound": "No se han encontrado sesiones", - "addon.mod_chat.saidto": "dicho a", - "addon.mod_chat.send": "Enviar", - "addon.mod_chat.sessionstart": "La próxima sesión de chat empezará el {{$a.date}}, (dentro de {{$a.fromnow}})", - "addon.mod_chat.showincompletesessions": "Mostrar sesiones incompletas", - "addon.mod_chat.talk": "Charla", - "addon.mod_chat.viewreport": "Ver las sesiones anteriores", - "addon.mod_choice.cannotsubmit": "Lo lamentamos, se ha producido un error con el envío de su respuesta. Por favor inténtelo de nuevo.", - "addon.mod_choice.choiceoptions": "Opciones de la Consulta", - "addon.mod_choice.errorgetchoice": "Se ha producido un error recuperando los datos de la consulta.", - "addon.mod_choice.expired": "Esta actividad cerró el {{$a}}.", - "addon.mod_choice.full": "(Lleno)", - "addon.mod_choice.modulenameplural": "Consultas", - "addon.mod_choice.noresultsviewable": "Los resultados no pueden verse en este momento.", - "addon.mod_choice.notopenyet": "Esta actividad no estará disponible hasta el {{$a}}.", - "addon.mod_choice.numberofuser": "Número de respuestas", - "addon.mod_choice.numberofuserinpercentage": "Porcentaje de respuestas", - "addon.mod_choice.previewonly": "Esta es solamente una previsualización de las opciones disponibles para esta actividad. No podrá enviar su respuesta a la consulta hasta el {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Podrá ver los resultados de forma anónima una vez responda.", - "addon.mod_choice.publishinfoanonclose": "Podrá ver los resultados de forma anónima una vez finalice la actividad.", - "addon.mod_choice.publishinfofullafter": "Podrá ver los resultados completos una vez responda.", - "addon.mod_choice.publishinfofullclose": "Podrá ver los resultados completos una vez finalice la actividad.", - "addon.mod_choice.publishinfonever": "Los resultados de esta actividad no se publicarán después de su respuesta.", - "addon.mod_choice.removemychoice": "Eliminar mi elección", - "addon.mod_choice.responses": "Respuestas", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% de los usuarios han escogido la opción: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Vista de gráfica", - "addon.mod_choice.resultsnotsynced": "Los resultados no incluyen su última respuesta. Por favor, sincronícelos para actualizarlos.", - "addon.mod_choice.savemychoice": "Guardar mi elección", - "addon.mod_choice.userchoosethisoption": "Usuarios que eligieron esta opción", - "addon.mod_choice.yourselection": "Su elección", - "addon.mod_data.addentries": "Añadir entradas", - "addon.mod_data.advancedsearch": "Búsqueda avanzada", - "addon.mod_data.alttext": "Texto alternativo", - "addon.mod_data.approve": "Aprobar", - "addon.mod_data.approved": "Aprobado", - "addon.mod_data.ascending": "Ascendente", - "addon.mod_data.authorfirstname": "Nombre del autor", - "addon.mod_data.authorlastname": "Apellido del autor", - "addon.mod_data.confirmdeleterecord": "¿Está seguro de que desea eliminar esta entrada?", - "addon.mod_data.descending": "Descendente", - "addon.mod_data.disapprove": "Desaprobar", - "addon.mod_data.edittagsnotsupported": "Lo sentimos, la edición de etiquetas no está soportada por la app.", - "addon.mod_data.emptyaddform": "¡No ha rellenado ningún campo!", - "addon.mod_data.entrieslefttoadd": "Debe agregar {{$a.entriesleft}} entrada(s) más para poder finalizar esta actividad", - "addon.mod_data.entrieslefttoaddtoview": "Debe añadir {{$a.entrieslefttoview}} entrada(s) antes de poder ver las entradas de otros participantes.", - "addon.mod_data.errorapproving": "Error al aprobar o desaprobar una entrada.", - "addon.mod_data.errordeleting": "Error al eliminar entrada.", - "addon.mod_data.errormustsupplyvalue": "Debe proporcionar un valor aquí.", - "addon.mod_data.expired": "Lo sentimos, esta actividad se cerró el {{$a}} y ya no está disponible", - "addon.mod_data.fields": "Campos", - "addon.mod_data.foundrecords": "Registros encontrados: {{$a.num}}/{{$a.max}} (Reset filters)", - "addon.mod_data.gettinglocation": "Obteniendo localización", - "addon.mod_data.latlongboth": "Tanto la latitud como la longitud son necesarias.", - "addon.mod_data.locationpermissiondenied": "No se ha podido obtener permiso para acceder a su localización.", - "addon.mod_data.menuchoose": "Seleccionar...", - "addon.mod_data.modulenameplural": "Bases de datos", - "addon.mod_data.more": "Más", - "addon.mod_data.mylocation": "Mi localización", - "addon.mod_data.nomatch": "No se han encontrado entradas", - "addon.mod_data.norecords": "No hay entradas en la base de datos", - "addon.mod_data.notapproved": "La entrada aún no ha sido aprobada.", - "addon.mod_data.notopenyet": "Lo sentimos, esta actividad no está disponible hasta {{$a}}", - "addon.mod_data.numrecords": "{{$a}} entradas", - "addon.mod_data.other": "Otro", - "addon.mod_data.recordapproved": "Entrada aprobada", - "addon.mod_data.recorddeleted": "Entrada eliminada", - "addon.mod_data.recorddisapproved": "Entrada desaprobada", - "addon.mod_data.resetsettings": "Restablecer filtros", - "addon.mod_data.search": "Buscar", - "addon.mod_data.searchbytagsnotsupported": "Lo sentimos, la búsqueda por etiquetas no está soportada por la app.", - "addon.mod_data.selectedrequired": "Se requieren todos los seleccionados", - "addon.mod_data.single": "Ver individual", - "addon.mod_data.tagarea_data_records": "Entradas de la base de datos", - "addon.mod_data.timeadded": "Tiempo añadido", - "addon.mod_data.timemodified": "Tiempo modificado", - "addon.mod_data.usedate": "Incluir en la búsqueda", - "addon.mod_feedback.analysis": "Análisis", - "addon.mod_feedback.anonymous": "Anónima", - "addon.mod_feedback.anonymous_entries": "Entradas anónimas ({{$a}})", - "addon.mod_feedback.average": "Promedio", - "addon.mod_feedback.captchaofflinewarning": "La retroalimentación con captcha no puede ser completada si no está configurada, si está en modo fuera-de-línea o con el servidor caído.", - "addon.mod_feedback.complete_the_form": "Responda a las preguntas", - "addon.mod_feedback.completed_feedbacks": "Respuestas enviadas", - "addon.mod_feedback.continue_the_form": "Continúe contestando las preguntas", - "addon.mod_feedback.feedback_is_not_open": "La encuesta no está disponible", - "addon.mod_feedback.feedback_submitted_offline": "Esta retroalimentación ha sido guardada para enviarse más tarde.", - "addon.mod_feedback.feedbackclose": "Permitir respuestas a", - "addon.mod_feedback.feedbackopen": "Permitir respuestas de", - "addon.mod_feedback.mapcourses": "Asignar encuesta a cursos", - "addon.mod_feedback.maximal": "Máximo", - "addon.mod_feedback.minimal": "Mínimo", - "addon.mod_feedback.mode": "Modo", - "addon.mod_feedback.modulenameplural": "Encuestas", - "addon.mod_feedback.next_page": "Siguiente página", - "addon.mod_feedback.non_anonymous": "Los nombres de los usuarios se mostrarán y registrarán con las respuestas", - "addon.mod_feedback.non_anonymous_entries": "Entradas no anónimas ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Estudiantes sin responder ({{$a}})", - "addon.mod_feedback.not_selected": "No seleccionada", - "addon.mod_feedback.not_started": "Sin comenzar", - "addon.mod_feedback.numberoutofrange": "Número fuera de rango", - "addon.mod_feedback.overview": "Vista general", - "addon.mod_feedback.page_after_submit": "Mensaje de finalizado", - "addon.mod_feedback.preview": "Vista previa", - "addon.mod_feedback.previous_page": "Página anterior", - "addon.mod_feedback.questions": "Preguntas", - "addon.mod_feedback.response_nr": "Respuesta número", - "addon.mod_feedback.responses": "Respuestas", - "addon.mod_feedback.save_entries": "Enviar sus respuestas", - "addon.mod_feedback.show_entries": "Mostrar respuestas", - "addon.mod_feedback.show_nonrespondents": "Mostrar no respondientes", - "addon.mod_feedback.started": "Iniciado", - "addon.mod_feedback.this_feedback_is_already_submitted": "Usted ya ha finalizado esta actividad.", - "addon.mod_folder.emptyfilelist": "No hay archivos que mostrar.", - "addon.mod_folder.modulenameplural": "Carpetas", - "addon.mod_forum.addanewdiscussion": "Añadir un nuevo tema de discusión", - "addon.mod_forum.addanewquestion": "Añadir una nueva pregunta", - "addon.mod_forum.addanewtopic": "Añadir un nuevo tema", - "addon.mod_forum.addtofavourites": "Marcar como favorita esta discusión", - "addon.mod_forum.advanced": "Avanzada", - "addon.mod_forum.cannotadddiscussion": "Para añadir debates a este foro hay que ser miembro de un grupo.", - "addon.mod_forum.cannotadddiscussionall": "No tiene permiso para añadir un nuevo tema de discusión para todos los participantes.", - "addon.mod_forum.cannotcreatediscussion": "No se pudo crear un debate nuevo", - "addon.mod_forum.couldnotadd": "No se puede colocar su mensaje debido a un problema desconocido.", - "addon.mod_forum.couldnotupdate": "No se ha podido actualizar su mensaje debido a un error desconocido.", - "addon.mod_forum.cutoffdatereached": "Se ha alcanzado la fecha límite para publicar en este foro, por lo que ya no puede publicar en él.", - "addon.mod_forum.delete": "Borrar", - "addon.mod_forum.deletedpost": "El mensaje se ha borrado", - "addon.mod_forum.deletesure": "¿Está seguro de que desea borrar este mensaje?", - "addon.mod_forum.discussion": "Tema", - "addon.mod_forum.discussionlistsortbycreatedasc": "Ordenar por la fecha de creación en orden ascendente", - "addon.mod_forum.discussionlistsortbycreateddesc": "Ordenar por la fecha de creación en orden descendente", - "addon.mod_forum.discussionlistsortbylastpostasc": "Ordenar por la fecha de creación de la última publicación en orden ascendente", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Ordenar por la fecha de creación de la última publicación en orden descendente", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Ordenar por el número de respuestas en orden ascendente", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Ordenar por el número de respuestas en orden descendente", - "addon.mod_forum.discussionlocked": "Este tema ha sido bloqueado así que no podrás responder en el.", - "addon.mod_forum.discussionpinned": "Fijado", - "addon.mod_forum.discussionsubscription": "Suscripción a la discusión", - "addon.mod_forum.edit": "Editar", - "addon.mod_forum.erroremptymessage": "El mensaje no puede estar vacío", - "addon.mod_forum.erroremptysubject": "El asunto del mensaje no puede estar vacío.", - "addon.mod_forum.errorgetforum": "Error al obtener datos del foro.", - "addon.mod_forum.errorgetgroups": "Error al obtener los datos del grupo.", - "addon.mod_forum.errorposttoallgroups": "No se puede crear un nueva discusión en todos los grupos.", - "addon.mod_forum.favouriteupdated": "Su opción de estrella ha sido actualizada.", - "addon.mod_forum.forumnodiscussionsyet": "No existen temas de discusión en este foro", - "addon.mod_forum.group": "Grupo", - "addon.mod_forum.lastpost": "Último mensaje", - "addon.mod_forum.lockdiscussion": "Bloquear este debate", - "addon.mod_forum.lockupdated": "La opción para bloquear ha sido actualizada.", - "addon.mod_forum.message": "Mensaje", - "addon.mod_forum.modeflatnewestfirst": "Ordenar desde el más reciente", - "addon.mod_forum.modeflatoldestfirst": "Ordenar desde el más antiguo", - "addon.mod_forum.modenested": "Mostrar respuestas anidadas", - "addon.mod_forum.modulenameplural": "Foros", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discusiones", - "addon.mod_forum.numreplies": "{{numreplies}} respuestas", - "addon.mod_forum.pindiscussion": "Fijar esta discusión", - "addon.mod_forum.pinupdated": "La opción de fijación ha sido actualizada.", - "addon.mod_forum.postisprivatereply": "Esta es una respuesta privada. No es visible para otros participantes.", - "addon.mod_forum.posttoforum": "Enviar al foro", - "addon.mod_forum.posttomygroups": "Publicar una copia para todos los grupos", - "addon.mod_forum.privatereply": "Responder en privado", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Actualizar foro", - "addon.mod_forum.refreshposts": "Actualizar publicaciones del foro", - "addon.mod_forum.removefromfavourites": "Desmarcar como favorita esta discusión", - "addon.mod_forum.reply": "Responder", - "addon.mod_forum.replyplaceholder": "Escriba su respuesta...", - "addon.mod_forum.subject": "Asunto", - "addon.mod_forum.tagarea_forum_posts": "Publicaciones del foro", - "addon.mod_forum.thisforumhasduedate": "La fecha límite para publicar en este foro es {{$a}}.", - "addon.mod_forum.thisforumisdue": "La fecha límite para publicar en este foro fue {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Desbloquea esta discusión", - "addon.mod_forum.unpindiscussion": "Desfijar esta discusión", - "addon.mod_forum.unread": "No leído", - "addon.mod_forum.unreadpostsnumber": "{{$a}} mensajes no leídos", - "addon.mod_forum.yourreply": "Su respuesta", - "addon.mod_glossary.addentry": "Añadir entrada", - "addon.mod_glossary.aliases": "Palabra(s) clave", - "addon.mod_glossary.attachment": "Adjunto", - "addon.mod_glossary.browsemode": "Navegar por las entradas", - "addon.mod_glossary.byalphabet": "Alfabéticamente", - "addon.mod_glossary.byauthor": "Agrupado por autor", - "addon.mod_glossary.bycategory": "Agrupar por categoría", - "addon.mod_glossary.bynewestfirst": "El más reciente primero", - "addon.mod_glossary.byrecentlyupdated": "Actualizado recientemente", - "addon.mod_glossary.bysearch": "Buscar", - "addon.mod_glossary.cannoteditentry": "No se puede editar la entrada", - "addon.mod_glossary.casesensitive": "Esta entrada es en Mayúsculas y minúsculas", - "addon.mod_glossary.categories": "Categorías", - "addon.mod_glossary.concept": "Concepto", - "addon.mod_glossary.definition": "Definición", - "addon.mod_glossary.entriestobesynced": "Entradas pendientes de ser sincronizadas", - "addon.mod_glossary.entrypendingapproval": "Esta entrada está pendiente de aprobación.", - "addon.mod_glossary.entryusedynalink": "Esta entrada será enlazada automáticamente", - "addon.mod_glossary.errconceptalreadyexists": "Este concepto ya existe. En este glosario no se permiten duplicados.", - "addon.mod_glossary.errorloadingentries": "Ha ocurrido un error cargando las entradas.", - "addon.mod_glossary.errorloadingentry": "Ha ocurrido un error cargando la entrada.", - "addon.mod_glossary.errorloadingglossary": "Ha ocurrido un error cargando el glosario.", - "addon.mod_glossary.fillfields": "Los campos Concepto y Definición son obligatorios.", - "addon.mod_glossary.fullmatch": "Sólo enlazar palabras completas", - "addon.mod_glossary.linking": "Auto-enlace", - "addon.mod_glossary.modulenameplural": "Glosarios", - "addon.mod_glossary.noentriesfound": "No se han encontrado entradas.", - "addon.mod_glossary.searchquery": "Tú búsqueda", - "addon.mod_glossary.tagarea_glossary_entries": "Entradas de glosario", - "addon.mod_h5pactivity.all_attempts": "Todos los intentos del usuario", - "addon.mod_h5pactivity.answer_checked": "Respuesta comprobada", - "addon.mod_h5pactivity.answer_correct": "Su respuesta es correcta", - "addon.mod_h5pactivity.answer_fail": "Respuesta incorrecta", - "addon.mod_h5pactivity.answer_incorrect": "Su respuesta es incorrecta", - "addon.mod_h5pactivity.answer_pass": "Respuesta correcta", - "addon.mod_h5pactivity.attempt": "Intento", - "addon.mod_h5pactivity.attempt_completion_no": "Este intento no está marcado como completado", - "addon.mod_h5pactivity.attempt_completion_yes": "Este intento no está completado", - "addon.mod_h5pactivity.attempt_success_fail": "Fallo", - "addon.mod_h5pactivity.attempt_success_pass": "Aprobado", - "addon.mod_h5pactivity.attempt_success_unknown": "No informado", - "addon.mod_h5pactivity.attempts_none": "El usuario no tiene intentos que mostrar.", - "addon.mod_h5pactivity.completion": "C", - "addon.mod_h5pactivity.downloadh5pfile": "Descargar archivo H5P", - "addon.mod_h5pactivity.duration": "Duración", - "addon.mod_h5pactivity.errorgetactivity": "Error al obtener información de la actividad H5P.", - "addon.mod_h5pactivity.filestatenotdownloaded": "El paquete H5P no está descargado. Necesita descargarlo para poder usarlo.", - "addon.mod_h5pactivity.filestateoutdated": "El paquete H5P ha sido modificado desde la última vez que lo descargó. Necesita descargarlo de nuevo para poder acceder.", - "addon.mod_h5pactivity.maxscore": "Máxima puntuación", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Mis intentos", - "addon.mod_h5pactivity.no_compatible_track": "Esta interacción ({{$a}}) no facilita información de rastreo o el rastreo\nfacilitado no es compatible con la versión actual de la actividad", - "addon.mod_h5pactivity.offlinedisabledwarning": "Necesita estar conectado para ver el paquete H5P", - "addon.mod_h5pactivity.outcome": "Resultado", - "addon.mod_h5pactivity.previewmode": "Este contenido se muestra en modo de vista previa. No se guardará información de rastreo.", - "addon.mod_h5pactivity.result_fill-in": "Rellene texto", - "addon.mod_h5pactivity.result_other": "Tipo de interacción desconocida", - "addon.mod_h5pactivity.review_my_attempts": "Ver mis intentos", - "addon.mod_h5pactivity.score": "Puntuación", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} de un máximo de {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Fecha de inicio", - "addon.mod_h5pactivity.totalscore": "Puntuación total", - "addon.mod_h5pactivity.viewattempt": "Ver intento {{$a}}", - "addon.mod_imscp.deploymenterror": "Error en el paquete de contenidos", - "addon.mod_imscp.modulenameplural": "Paquete de contenidos IMS", - "addon.mod_imscp.showmoduledescription": "Mostrar descripción", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "Respuesta", - "addon.mod_lesson.attempt": "Intento: {{$a}}", - "addon.mod_lesson.attemptheader": "Intento", - "addon.mod_lesson.attemptsremaining": "Tiene {{$a}} intento(s) pendiente(s)", - "addon.mod_lesson.averagescore": "Puntuación promedio", - "addon.mod_lesson.averagetime": "Tiempo promedio", - "addon.mod_lesson.branchtable": "Contenido", - "addon.mod_lesson.cannotfindattempt": "Error: no se pudo encontrar el intento", - "addon.mod_lesson.cannotfinduser": "Error: no se pudieron encontrar los usuarios", - "addon.mod_lesson.clusterjump": "Pregunta no vista dentro de un cluster", - "addon.mod_lesson.completed": "Finalizado", - "addon.mod_lesson.congratulations": "Enhorabuena, ha llegado al final de la lección", - "addon.mod_lesson.continue": "Continuar", - "addon.mod_lesson.continuetonextpage": "Continuar en la página siguiente", - "addon.mod_lesson.defaultessayresponse": "Su ensayo será calificado por su profesor.", - "addon.mod_lesson.detailedstats": "Estadísticas detalladas", - "addon.mod_lesson.didnotanswerquestion": "No ha contestado a esta pregunta.", - "addon.mod_lesson.displayofgrade": "Mostrar calificación (sólo para estudiantes)", - "addon.mod_lesson.displayscorewithessays": "

              Usted ha obtenido un total de {{$a.score}} sobre {{$a.tempmaxgrade}} en las preguntas calificadas de manera automática.

              \n

              Sus {{$a.essayquestions}} pregunta(s) de ensayo se calificarán se añadirán a su calificación final.

              \n

              Su calificación actual sin incluir las pregunta(s) ensayo es de {{$a.score}} sobre {{$a.grade}}.

              ", - "addon.mod_lesson.displayscorewithoutessays": "Su puntuación es {{$a.score}} (sobre {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "La contraseña no puede estar vacía", - "addon.mod_lesson.enterpassword": "Por favor, escriba la contraseña:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "No ha contestado a ninguna pregunta. En esta lección ha obtenido 0 puntos.", - "addon.mod_lesson.errorprefetchrandombranch": "Esta lección contiene un salto hacia una página aleatoria de contenido. No puede ser intentada en la aplicación hasta que haya sido comenzada en un navegador web.", - "addon.mod_lesson.errorreviewretakenotlast": "Este intento no puede ser revisado ya que se ha terminado otro intento.", - "addon.mod_lesson.finish": "Terminado", - "addon.mod_lesson.finishretakeoffline": "Este intento se ha terminado en fuera-de-línea.", - "addon.mod_lesson.firstwrong": "Su respuesta es incorrecta. ¿Desea intentarlo de nuevo? ( si contesta la pregunta correctamente no se tomará en cuenta en la puntuación final)", - "addon.mod_lesson.gotoendoflesson": "Ir al final de la lección", - "addon.mod_lesson.grade": "Calificación", - "addon.mod_lesson.highscore": "Puntuación alta", - "addon.mod_lesson.hightime": "Tiempo alto", - "addon.mod_lesson.leftduringtimed": "Se ha interrumpido una lección con tiempo fijo.
              Por favor, haga clic en Continuar para volver a empezar la lección.", - "addon.mod_lesson.leftduringtimednoretake": "Se ha interrumpido una lección con tiempo fijo y
              no se permite volver a empezar o continuar la lección.", - "addon.mod_lesson.lessonmenu": "Menú Lección", - "addon.mod_lesson.lessonstats": "Estadísticas de la lección", - "addon.mod_lesson.linkedmedia": "Medios enlazados", - "addon.mod_lesson.loginfail": "Acceso fallido, por favor pruebe de nuevo...", - "addon.mod_lesson.lowscore": "Puntuación baja", - "addon.mod_lesson.lowtime": "Tiempo bajo", - "addon.mod_lesson.maximumnumberofattemptsreached": "Se ha alcanzado el número máximo de intentos. Traslado a la página siguiente", - "addon.mod_lesson.modattemptsnoteacher": "La revisión del estudiante sólo está disponible para los estudiantes.", - "addon.mod_lesson.modulenameplural": "Lecciones", - "addon.mod_lesson.noanswer": "Una o más preguntas no se han contestado. Por favor regrese y de una respuesta.", - "addon.mod_lesson.nolessonattempts": "No se han hecho intentos de practicar esta lección.", - "addon.mod_lesson.nolessonattemptsgroup": "{{$a}} miembros del grupo no han realizado ningún intento en esta lección.", - "addon.mod_lesson.notcompleted": "Sin finalizar", - "addon.mod_lesson.numberofcorrectanswers": "Número de respuestas correctas: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Número de páginas vistas: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Número de preguntas contestadas: {{$a.nquestions}} (Debería contestar al menos {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Ha obtenido {{$a.score}} punto(s) sobre {{$a.currenthigh}} hasta ahora.", - "addon.mod_lesson.ongoingnormal": "Usted ha respondido correctamente {{$a.correct}} pregunta(s) de un total de {{$a.viewed}} pregunta(s).", - "addon.mod_lesson.or": "O", - "addon.mod_lesson.overview": "Revisión", - "addon.mod_lesson.preview": "Previsualizar", - "addon.mod_lesson.progressbarteacherwarning2": "Usted no verá la barra de progreso porque puede editar esta lección", - "addon.mod_lesson.progresscompleted": "Ha alcanzado el {{$a}}% de esta lección", - "addon.mod_lesson.question": "Pregunta", - "addon.mod_lesson.rawgrade": "Calificación en bruto", - "addon.mod_lesson.reports": "Informes", - "addon.mod_lesson.response": "Comentario", - "addon.mod_lesson.retakefinishedinsync": "Un intento fuera-de-línea fue sincronizado. ¿Quiere usted revisarlo?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Revisión", - "addon.mod_lesson.reviewlesson": "Revisar lección", - "addon.mod_lesson.reviewquestionback": "Sí, me gustaría probar de nuevo", - "addon.mod_lesson.reviewquestioncontinue": "No, deseo pasar a la siguiente", - "addon.mod_lesson.secondpluswrong": "No. ¿Desea probar de nuevo?", - "addon.mod_lesson.submit": "Enviar", - "addon.mod_lesson.teacherjumpwarning": "En esta lección se ha utilizado un salto {{$a.cluster}} o un salto a {{$a.unseen}}. En su lugar se usará el salto a la siguiente página. Acceda a su cuenta como estudiante para comprobar estos saltos.", - "addon.mod_lesson.teacherongoingwarning": "La puntuación acumulada sólo se muestra al estudiante. Acceda como estudiante para probar la puntuación acumulada.", - "addon.mod_lesson.teachertimerwarning": "El temporizador sólo funciona con estudiantes. Entre como estudiante para probar el temporizador.", - "addon.mod_lesson.thatsthecorrectanswer": "Esta es la respuesta correcta", - "addon.mod_lesson.thatsthewronganswer": "Esta es la respuesta equivocada", - "addon.mod_lesson.timeremaining": "Tiempo restante", - "addon.mod_lesson.timetaken": "Tiempo empleado", - "addon.mod_lesson.unseenpageinbranch": "Pregunta no vista dentro de una página de conenidos", - "addon.mod_lesson.warningretakefinished": "Este intento se terminó en el sitio.", - "addon.mod_lesson.welldone": "¡Bien hecho!", - "addon.mod_lesson.youhaveseen": "Usted ya ha visto más de una página de esta lección.
              ¿Desea comenzar desde la última página vista?", - "addon.mod_lesson.youranswer": "Su respuesta", - "addon.mod_lesson.yourcurrentgradeisoutof": "Su calificación actual es {{$a.grade}} sobre {{$a.total}}", - "addon.mod_lesson.youshouldview": "Usted debería ver como mínimo: {{$a}}", - "addon.mod_lti.errorgetlti": "Se ha producido un error recuperando los datos del módulo.", - "addon.mod_lti.errorinvalidlaunchurl": "La dirección URL no es válida.", - "addon.mod_lti.launchactivity": "Ejecutar la actividad", - "addon.mod_lti.modulenameplural": "Herramientas externas", - "addon.mod_page.errorwhileloadingthepage": "Error al cargar el contenido de la página.", - "addon.mod_page.modulenameplural": "Páginas", - "addon.mod_quiz.answercolon": "Respuesta:", - "addon.mod_quiz.attemptfirst": "Primer intento", - "addon.mod_quiz.attemptlast": "Último intento", - "addon.mod_quiz.attemptnumber": "Intento", - "addon.mod_quiz.attemptquiznow": "Intente resolver el cuestionario ahora", - "addon.mod_quiz.attemptstate": "Estado", - "addon.mod_quiz.canattemptbutnotsubmit": "Puede tomar este cuestionario desde la aplicación, pero debe enviar la respuesta final utilizando el navegador por las razones siguientes:", - "addon.mod_quiz.cannotsubmitquizdueto": "Este intento de cuestionario no puede enviarse por las siguientes razones:", - "addon.mod_quiz.clearchoice": "Quitar mi selección", - "addon.mod_quiz.comment": "Comentario", - "addon.mod_quiz.completedon": "Finalizado en", - "addon.mod_quiz.confirmclose": "Una vez que haga el envío, no podrá cambiar sus respuestas de este intento de resolver el cuestionario..", - "addon.mod_quiz.confirmcontinueoffline": "Este intento no se ha sincronizado desde {{$a}}. Si ha continuado este intento en otro dispositivo después de esta hora, podría perder datos.", - "addon.mod_quiz.confirmleavequizonerror": "Ocurrió un error al guardar las respuestas. ¿Está seguro de querer abandonar el cuestionario?", - "addon.mod_quiz.confirmstart": "El cuestionario tiene un tiempo límite de {{$a}}. El tiempo empezará a contar desde el momento en el que inicie su intento y debe presentarlo antes de que el tiempo termine ¿seguro que desea empezar ahora?", - "addon.mod_quiz.confirmstartheader": "Tiempo del cuestionario", - "addon.mod_quiz.connectionerror": "La conexión de red se ha perdido. (Ha fallado el guardado automático).\n\nAnote las respuestas introducidas en esta página en los últimos minutos, y trate de volver a conectarse.\n\nUna vez que la conexión se haya restablecido, sus respuestas deben ser guardados y este mensaje desaparecerá.", - "addon.mod_quiz.continueattemptquiz": "Continuar el último intento", - "addon.mod_quiz.continuepreview": "Continuar la previsualización anterior", - "addon.mod_quiz.errorbehaviournotsupported": "Este cuestionario no puede realizarse en la app porque su comportamiento no está soportado:", - "addon.mod_quiz.errordownloading": "Error al descargar datos necesarios.", - "addon.mod_quiz.errorgetattempt": "Error al obtener datos del intento.", - "addon.mod_quiz.errorgetquestions": "Error al obtener las preguntas.", - "addon.mod_quiz.errorgetquiz": "Error al obtener datos del cuestionario.", - "addon.mod_quiz.errorparsequestions": "Ocurrió un error al leer las preguntas. Por favor intente este cuestionario desde un navegador web.", - "addon.mod_quiz.errorquestionsnotsupported": "Este cuestionario no puede realizarse desde la aplicación porque contiene preguntas no incluidas en esta aplicación", - "addon.mod_quiz.errorrulesnotsupported": "Este cuestionario no puede realizarse desde la app porque tiene condiciones de acceso no soportadas:", - "addon.mod_quiz.errorsaveattempt": "Ocurrió un error al guardar los datos del intento.", - "addon.mod_quiz.feedback": "Comentario -", - "addon.mod_quiz.finishattemptdots": "Terminar intento...", - "addon.mod_quiz.finishnotsynced": "Finalizado pero no sincronizado.", - "addon.mod_quiz.grade": "Calificación", - "addon.mod_quiz.gradeaverage": "Promedio de calificaciones", - "addon.mod_quiz.gradehighest": "Calificación más alta", - "addon.mod_quiz.grademethod": "Método de calificación", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Puntos", - "addon.mod_quiz.modulenameplural": "Cuestionarios", - "addon.mod_quiz.mustbesubmittedby": "Este intento debe ser presentado por {{$a}}.", - "addon.mod_quiz.noquestions": "Aún no se han agregado preguntas", - "addon.mod_quiz.noreviewattempt": "Usted no tiene permiso para revisar este intento.", - "addon.mod_quiz.notyetgraded": "Sin calificar aún", - "addon.mod_quiz.opentoc": "Abrir menú de navegación.", - "addon.mod_quiz.outof": "{{$a.grade}} de {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} de {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Retroalimentación global", - "addon.mod_quiz.overdue": "Vencido", - "addon.mod_quiz.overduemustbesubmittedby": "Este intento ha caducado. Debería ya haber sido presentado. Si quiere que este cuestionario se califique, debería presentarlo en {{$a}}. Si no lo presenta para entonces, no se contarán las puntuaciones de este intento.", - "addon.mod_quiz.preview": "Vista previa", - "addon.mod_quiz.previewquiznow": "Previsualizar el cuestionario ahora", - "addon.mod_quiz.question": "Pregunta", - "addon.mod_quiz.quiznavigation": "Navegación por el cuestionario", - "addon.mod_quiz.quizpassword": "Contraseña del cuestionario", - "addon.mod_quiz.reattemptquiz": "Reintentar el cuestionario", - "addon.mod_quiz.requirepasswordmessage": "Para contestar este cuestionario necesita conocer la contraseña", - "addon.mod_quiz.returnattempt": "Volver al intento", - "addon.mod_quiz.review": "Revisión", - "addon.mod_quiz.reviewofattempt": "Revisión del intento {{$a}}", - "addon.mod_quiz.reviewofpreview": "Revisión de la vista previa", - "addon.mod_quiz.showall": "Mostrar todas las preguntas en una página", - "addon.mod_quiz.showeachpage": "Mostrar una página cada vez", - "addon.mod_quiz.startattempt": "Comenzar intento", - "addon.mod_quiz.startedon": "Comenzado el", - "addon.mod_quiz.stateabandoned": "Nunca enviado", - "addon.mod_quiz.statefinished": "Finalizado", - "addon.mod_quiz.statefinisheddetails": "Enviado: {{$a}}", - "addon.mod_quiz.stateinprogress": "En curso", - "addon.mod_quiz.stateoverdue": "Atrasados", - "addon.mod_quiz.stateoverduedetails": "Debe ser presentado por {{$a}}", - "addon.mod_quiz.status": "Estatus", - "addon.mod_quiz.submitallandfinish": "Enviar todo y terminar", - "addon.mod_quiz.summaryofattempt": "Resumen del intento", - "addon.mod_quiz.summaryofattempts": "Resumen de sus intentos previos", - "addon.mod_quiz.timeleft": "Tiempo restante", - "addon.mod_quiz.timetaken": "Tiempo empleado", - "addon.mod_quiz.warningattemptfinished": "El intento fuera de línea fue descartado porque fue terminado en el sitio o no fue encontrado.", - "addon.mod_quiz.warningdatadiscarded": "Algunas respuestas fuera de línea fueron descartas porque las preguntas fueron modificadas en línea.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "El intento no fue terminado porque algunas respuestas fuera de línea fueron descartadas. Por favor, revise sus respuestas y entonces envíe el intento nuevamente.", - "addon.mod_quiz.warningquestionsnotsupported": "Este cuestionario contiene preguntas no incluidas en esta aplicación", - "addon.mod_quiz.yourfinalgradeis": "Su calificación final en este cuestionario es {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Se ha producido un error cargando el contenido.", - "addon.mod_resource.modifieddate": "Actualizado {{$a}}", - "addon.mod_resource.modulenameplural": "Archivos", - "addon.mod_resource.openthefile": "Abrir el archivo", - "addon.mod_resource.uploadeddate": "Subido {{$a}}", - "addon.mod_scorm.asset": "Recurso", - "addon.mod_scorm.assetlaunched": "Recurso - Visto", - "addon.mod_scorm.attempts": "Intentos", - "addon.mod_scorm.averageattempt": "Intentos promedio", - "addon.mod_scorm.browse": "Vista previa", - "addon.mod_scorm.browsed": "Navegado", - "addon.mod_scorm.browsemode": "Modo de presentación preliminar", - "addon.mod_scorm.cannotcalculategrade": "La calificación no se puede calcular.", - "addon.mod_scorm.completed": "Finalizado", - "addon.mod_scorm.contents": "Contenido", - "addon.mod_scorm.dataattemptshown": "Este dato pertenece al intento número {{number}}.", - "addon.mod_scorm.enter": "Entrar", - "addon.mod_scorm.errorcreateofflineattempt": "Se ha producido un error creando un nuevo intento fuera de línea. Por favor inténtelo otra vez.", - "addon.mod_scorm.errordownloadscorm": "Se ha producido un error descargando el paquete SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Se ha producido un error recuperando los datos del paquete SCORM.", - "addon.mod_scorm.errorinvalidversion": "Lo sentimos, la aplicación solo soporta SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "La descarga de paquetes SCORM está deshabilitada en esta instalación de Moodle. Contacte con el administrador.", - "addon.mod_scorm.errornovalidsco": "Este paquete SCORM no tiene una SCO visible para cargar.", - "addon.mod_scorm.errorpackagefile": "Lo sentimos, la aplicación solo soporta paquetes ZIP.", - "addon.mod_scorm.errorsyncscorm": "Se ha producido un error durante la sincronización. Por favor inténtelo de nuevo.", - "addon.mod_scorm.exceededmaxattempts": "Ha alcanzado el número máximo de intentos", - "addon.mod_scorm.failed": "Error", - "addon.mod_scorm.firstattempt": "Primer intento", - "addon.mod_scorm.gradeaverage": "Calificación promedio", - "addon.mod_scorm.gradeforattempt": "Calificación del intento", - "addon.mod_scorm.gradehighest": "Calificación más alta", - "addon.mod_scorm.grademethod": "Método de calificación", - "addon.mod_scorm.gradereported": "Calificación informada", - "addon.mod_scorm.gradescoes": "Objetos de aprendizaje", - "addon.mod_scorm.gradesum": "Calificaciones sumadas", - "addon.mod_scorm.highestattempt": "Intento más alto", - "addon.mod_scorm.incomplete": "Incompleto", - "addon.mod_scorm.lastattempt": "Último intento finalizado", - "addon.mod_scorm.modulenameplural": "Paquetes SCORM", - "addon.mod_scorm.newattempt": "Comenzar un nuevo intento", - "addon.mod_scorm.noattemptsallowed": "Número de intentos permitidos", - "addon.mod_scorm.noattemptsmade": "Número de intentos realizados", - "addon.mod_scorm.notattempted": "No se ha intentado", - "addon.mod_scorm.offlineattemptnote": "Este intento contiene datos que todavía no se han sincronizado .", - "addon.mod_scorm.offlineattemptovermax": "Este intento no se puede enviar porque has sobrepasado el máximo número de intentos.", - "addon.mod_scorm.organizations": "Organizaciones", - "addon.mod_scorm.passed": "Pasado", - "addon.mod_scorm.reviewmode": "Modo Revisión", - "addon.mod_scorm.score": "Puntuación", - "addon.mod_scorm.scormstatusnotdownloaded": "Este SCORM no se ha descargado. Lo hará automáticamente cuando lo abras.", - "addon.mod_scorm.scormstatusoutdated": "Este SCORM se ha modificado desde la última descarga. Se descargará automáticamente cuando lo abras.", - "addon.mod_scorm.suspended": "Suspendido", - "addon.mod_scorm.toc": "TOC (Tabla de Contenidos)", - "addon.mod_scorm.warningofflinedatadeleted": "Algunos datos fuera de línea del intento {{number}} se han eliminado porque no pueden crearse en el nuevo intento.", - "addon.mod_scorm.warningsynconlineincomplete": "Algunos intentos no pueden sincronizarse con el sitio porque el último intento en línea no ha finalizado. Por favor acabe primero el intento.", - "addon.mod_survey.cannotsubmitsurvey": "Lo sentimos, hay un problema enviando la encuesta. Por favor inténtelo otra vez.", - "addon.mod_survey.errorgetsurvey": "Se ha producido un error recuperando los datos de la encuesta.", - "addon.mod_survey.ifoundthat": "Encontrado:", - "addon.mod_survey.ipreferthat": "Prefiero esto", - "addon.mod_survey.modulenameplural": "Encuestas", - "addon.mod_survey.responses": "Respuestas", - "addon.mod_survey.results": "Resultados", - "addon.mod_survey.surveycompletednograph": "Has completado esta encuesta.", - "addon.mod_url.accessurl": "Ir a la URL", - "addon.mod_url.modulenameplural": "URLs", - "addon.mod_url.pointingtourl": "La URL donde apunta este recurso", - "addon.mod_wiki.cannoteditpage": "No puedes editar esta página.", - "addon.mod_wiki.createpage": "Crear Página", - "addon.mod_wiki.editingpage": "Editando esta página '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Ocurrió un error al cargar la página.", - "addon.mod_wiki.errornowikiavailable": "Este wiki todavía no tiene ningun contenido.", - "addon.mod_wiki.gowikihome": "Ir al inicio del wiki", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Página nueva", - "addon.mod_wiki.newpagetitle": "Título nuevo de la página", - "addon.mod_wiki.nocontent": "No hay contenido para esta página", - "addon.mod_wiki.notingroup": "No está en un grupo", - "addon.mod_wiki.pageexists": "Esta página ya existe.", - "addon.mod_wiki.pagename": "Nombre de la página", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Páginas Wiki", - "addon.mod_wiki.titleshouldnotbeempty": "El título no debería estar vacío.", - "addon.mod_wiki.viewpage": "Ver página", - "addon.mod_wiki.wikipage": "Página del wiki", - "addon.mod_wiki.wrongversionlock": "Otro usuario ha editado esta página mientras usted estaba editando: su contenido es obsoleto.", - "addon.mod_workshop.alreadygraded": "Ya calificada", - "addon.mod_workshop.areainstructauthors": "Instrucciones para el envío", - "addon.mod_workshop.areainstructreviewers": "Instrucciones para la evaluación", - "addon.mod_workshop.assess": "Evaluar", - "addon.mod_workshop.assessedsubmission": "Envío evaluado", - "addon.mod_workshop.assessmentform": "Formato de evaluación", - "addon.mod_workshop.assessmentsettings": "Configuración de la evaluación", - "addon.mod_workshop.assessmentstrategynotsupported": "Estrategia de calificación {{$a}} no soportada", - "addon.mod_workshop.assessmentweight": "Ponderación de la evaluación", - "addon.mod_workshop.assignedassessments": "Envíos asignados para evaluar", - "addon.mod_workshop.assignedassessmentsnone": "No tiene envíos asignados para evaluar", - "addon.mod_workshop.conclusion": "Conclusión", - "addon.mod_workshop.createsubmission": "Empiece a preparar su envío", - "addon.mod_workshop.deletesubmission": "Eliminar envío", - "addon.mod_workshop.editsubmission": "Editar lo enviado", - "addon.mod_workshop.feedbackauthor": "Retroalimentación para el autor", - "addon.mod_workshop.feedbackby": "Retroalimentación por {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Retroalimentación para el revisor", - "addon.mod_workshop.givengrades": "Calificaciones otorgadas", - "addon.mod_workshop.gradecalculated": "Calificación calculada para el envío", - "addon.mod_workshop.gradeinfo": "Calificación: {{$a.received}} of {{$a.max}}", - "addon.mod_workshop.gradeover": "Pasar por alto calificación del envío", - "addon.mod_workshop.gradesreport": "Informe de calificaciones del Taller", - "addon.mod_workshop.gradinggrade": "Calificación de la evaluación", - "addon.mod_workshop.gradinggradecalculated": "Calificación calculada de la evaluación", - "addon.mod_workshop.gradinggradeof": "Calificación de la evaluación (de {{$a}})", - "addon.mod_workshop.gradinggradeover": "Pasar por alto calificación de la evaluación", - "addon.mod_workshop.modulenameplural": "Talleres", - "addon.mod_workshop.nogradeyet": "Aún no hay calificación", - "addon.mod_workshop.notassessed": "Aún no evaluado", - "addon.mod_workshop.notoverridden": "No anulado", - "addon.mod_workshop.noyoursubmission": "Usted aún no ha enviado su trabajo", - "addon.mod_workshop.overallfeedback": "Retroalimentación global", - "addon.mod_workshop.publishedsubmissions": "Envíos publicados", - "addon.mod_workshop.publishsubmission": "Publicar envío", - "addon.mod_workshop.publishsubmission_help": "Los envíos publicados estarán disponibles para los demás cuando el taller esté cerrado.", - "addon.mod_workshop.reassess": "Re-evaluar", - "addon.mod_workshop.receivedgrades": "Calificaciones recibidas", - "addon.mod_workshop.submissionattachment": "Adjunto", - "addon.mod_workshop.submissioncontent": "Contenido del envío", - "addon.mod_workshop.submissiondeleteconfirm": "¿Está seguro de querer borrar el siguiente envío?", - "addon.mod_workshop.submissiongrade": "Calificación por el envío", - "addon.mod_workshop.submissiongradeof": "Calificación por el envío (de {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Debe introducir algún texto o añadir un archivo.", - "addon.mod_workshop.submissionrequiredtitle": "Debe introducir algún título.", - "addon.mod_workshop.submissionsreport": "Informe de envíos del taller", - "addon.mod_workshop.submissiontitle": "Título", - "addon.mod_workshop.switchphase10": "Cambiar a la fase de configuración", - "addon.mod_workshop.switchphase20": "Cambiar a la fase de envío", - "addon.mod_workshop.switchphase30": "Cambiar a la fase de evaluación", - "addon.mod_workshop.switchphase40": "Cambiar a la fase de calificación", - "addon.mod_workshop.switchphase50": "Cerrar taller", - "addon.mod_workshop.userplan": "Planificador de taller", - "addon.mod_workshop.userplancurrentphase": "Fase actual", - "addon.mod_workshop.warningassessmentmodified": "El envío fue modificado en el sitio.", - "addon.mod_workshop.warningsubmissionmodified": "La evaluación fue modificada en el sitio.", - "addon.mod_workshop.weightinfo": "Ponderación: {{$a}}", - "addon.mod_workshop.yourassessment": "Su evaluación", - "addon.mod_workshop.yourassessmentfor": "Su valoración para {{$a}}", - "addon.mod_workshop.yourgrades": "Sus calificaciones", - "addon.mod_workshop.yoursubmission": "Su envío", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Comentario para {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Calificación para {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspecto {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Tiene que seleccionar una calificación para este aspecto", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Comentario para {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspecto {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Comentario para {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Calificación para {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Afirmación {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Criterio {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Tiene que seleccionar uno de estos ítems", - "addon.notes.addnewnote": "Agregar una nueva nota", - "addon.notes.coursenotes": "Anotaciones del curso", - "addon.notes.deleteconfirm": "¿Eliminar esta nota?", - "addon.notes.eventnotecreated": "Nota creada", - "addon.notes.eventnotedeleted": "Nota eliminada", - "addon.notes.nonotes": "Aún no hay anotaciones de este tipo", - "addon.notes.note": "Nota", - "addon.notes.notes": "Anotaciones", - "addon.notes.personalnotes": "Anotaciones personales", - "addon.notes.publishstate": "Contexto", - "addon.notes.sitenotes": "Anotaciones del sitio", - "addon.notes.userwithid": "Usuario con id {{id}}", - "addon.notes.warningnotenotsent": "No se pudieron añadir notas al curso {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Error al obtener notificaciones.", - "addon.notifications.markallread": "Marcar todo como leído", - "addon.notifications.notificationpreferences": "Preferencias de notificación", - "addon.notifications.notifications": "Notificaciones", - "addon.notifications.playsound": "Reproducir sonido", - "addon.notifications.therearentnotificationsyet": "No hay notificaciones", - "addon.storagemanager.deletecourse": "Descargar todos los datos del curso", - "addon.storagemanager.deletecourses": "Borrar toda la información de los cursos.", - "addon.storagemanager.deletedatafrom": "Descargar los datos de {{name}}", - "addon.storagemanager.info": "Los ficheros restaurados en su dispositivo hacen que la aplicación funcione mas rápidamente y le permiten su uso sin estar conectado al internet. Puede descargar ficheros de manera segura para liberar espacio.", - "addon.storagemanager.managestorage": "Administre el almacenamiento", - "addon.storagemanager.storageused": "Almacenamiento utilizado:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Emiratos Árabes Unidos", - "assets.countries.AF": "Afganistán", - "assets.countries.AG": "Antigua y Barbuda", - "assets.countries.AI": "Anguila", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antártida", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa Americana", - "assets.countries.AT": "Austria", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Islas Åland", - "assets.countries.AZ": "Azerbaiyán", - "assets.countries.BA": "Bosnia y Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Bélgica", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrein", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei Darussalam", - "assets.countries.BO": "Bolivia (Estado Plurinacional de)", - "assets.countries.BQ": "Bonaire, San Eustaquio y Saba", - "assets.countries.BR": "Brasil", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bután", - "assets.countries.BV": "Islas Bouvet", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Bielorrusia", - "assets.countries.BZ": "Belice", - "assets.countries.CA": "Canadá", - "assets.countries.CC": "Islas Cocos", - "assets.countries.CD": "Congo (República Democrática del)", - "assets.countries.CF": "República Centroafricana", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Suiza", - "assets.countries.CI": "Costa de Marfil", - "assets.countries.CK": "Islas Cook", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Camerún", - "assets.countries.CN": "China", - "assets.countries.CO": "Colombia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Cabo Verde", - "assets.countries.CW": "Curazao", - "assets.countries.CX": "Islas de Navidad", - "assets.countries.CY": "Chipre", - "assets.countries.CZ": "República Checa", - "assets.countries.DE": "Alemania", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Dinamarca", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "República Dominicana", - "assets.countries.DZ": "Argelia", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estonia", - "assets.countries.EG": "Egipto", - "assets.countries.EH": "Sahara Occidental", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "España", - "assets.countries.ET": "Etiopía", - "assets.countries.FI": "Finlandia", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Islas Malvinas", - "assets.countries.FM": "Micronesia ( Estados Federados de)", - "assets.countries.FO": "Islas Faroe", - "assets.countries.FR": "Francia", - "assets.countries.GA": "Gabón", - "assets.countries.GB": "Reino Unido", - "assets.countries.GD": "Granada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Guyana Francesa", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Groenlandia", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadalupe", - "assets.countries.GQ": "Guinea Ecuatorial", - "assets.countries.GR": "Grecia", - "assets.countries.GS": "Georgia del Sur y Las Islas Sandwich del Sur", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Isla Heard e Islas McDonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Croacia", - "assets.countries.HT": "Haití", - "assets.countries.HU": "Hungría", - "assets.countries.ID": "Indonesia", - "assets.countries.IE": "Irlanda", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Isla de Man", - "assets.countries.IN": "India", - "assets.countries.IO": "Territorio Británico del Océano Índico", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Irán (República Islámica de)", - "assets.countries.IS": "Islandia", - "assets.countries.IT": "Italia", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordania", - "assets.countries.JP": "Japón", - "assets.countries.KE": "Kenia", - "assets.countries.KG": "Kirguistán", - "assets.countries.KH": "Camboya", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comoras", - "assets.countries.KN": "San Cristóbal y Nevis", - "assets.countries.KP": "Corea (República Popular Democrática de)", - "assets.countries.KR": "Corea (República de)", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Islas Caimán", - "assets.countries.KZ": "Kazajstán", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Líbano", - "assets.countries.LC": "Santa Lucía", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lituania", - "assets.countries.LU": "Luxemburgo", - "assets.countries.LV": "Latvia", - "assets.countries.LY": "Libia", - "assets.countries.MA": "Marruecos", - "assets.countries.MC": "Mónaco", - "assets.countries.MD": "Moldovia ( República de)", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "San Martín (zona Francesa)", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Islas Marshall", - "assets.countries.MK": "Macedonia del Norte", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Islas Marianas del Norte", - "assets.countries.MQ": "Martinica", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauricio", - "assets.countries.MV": "Maldivas", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "México", - "assets.countries.MY": "Malasia", - "assets.countries.MZ": "Mozambique", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Nueva Caledonia", - "assets.countries.NE": "Níger", - "assets.countries.NF": "Islas Norfolk", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Holanda", - "assets.countries.NO": "Noruega", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Naurú", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nueva Zelandia", - "assets.countries.OM": "Omán", - "assets.countries.PA": "Panamá", - "assets.countries.PE": "Perú", - "assets.countries.PF": "Polinesia Francesa", - "assets.countries.PG": "Papúa Nueva Guinea", - "assets.countries.PH": "Filipinas", - "assets.countries.PK": "Pakistán", - "assets.countries.PL": "Polonia", - "assets.countries.PM": "San Pedro y Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestina, Estado de", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Reunión", - "assets.countries.RO": "Rumania", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Federación Rusa", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Arabia Saudita", - "assets.countries.SB": "Islas Salomón", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudán", - "assets.countries.SE": "Suecia", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Santa Elena, Ascensión y Tristán de Acuña", - "assets.countries.SI": "Eslovenia", - "assets.countries.SJ": "Islas Svalbard y Jan Mayen", - "assets.countries.SK": "Eslovaquia", - "assets.countries.SL": "Sierra Leona", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalía", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Sudán del Sur", - "assets.countries.ST": "Santo Tomé y Príncipe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "San Martín (Parte Holandesa)", - "assets.countries.SY": "Siria", - "assets.countries.SZ": "Swazilandia", - "assets.countries.TC": "Islas Turcas y Caicos", - "assets.countries.TD": "Chad", - "assets.countries.TF": "Territorios Franceses del Sur", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tailandia", - "assets.countries.TJ": "Tayikistán", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkmenistán", - "assets.countries.TN": "Túnez", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turquía", - "assets.countries.TT": "Trinidad y Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwán", - "assets.countries.TZ": "Tanzania, República Unida de", - "assets.countries.UA": "Ucrania", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "Estados Unidos", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "El Vaticano", - "assets.countries.VC": "San Vincente y Las Granadinas", - "assets.countries.VE": "Venezuela (República Bolivariana de)", - "assets.countries.VG": "Islas Vírgenes (Británícas)", - "assets.countries.VI": "Islas Vírgenes (Americanas)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Islas Wallis y Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Sudáfrica", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbawe", - "assets.mimetypes.application/epub_zip": "Ebook formato EPUB", - "assets.mimetypes.application/msword": "documento Word", - "assets.mimetypes.application/pdf": "documento PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Copia de seguridad Moodle", - "assets.mimetypes.application/vnd.ms-excel": "hoja de cálculo Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Macro habilitada de Excel 2007", - "assets.mimetypes.application/vnd.ms-powerpoint": "presentación Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Hoja de cálculo OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Plantilal de hoja de cálculo OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Documento OpenDocument Text", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Plantilla de OpenDocument Text", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Plantilla de página web OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Presentación PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Pase de diapositivas PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Hoja de cálculo Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Plantilla Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Documento Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Presentación de iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Hoja de cálculo de iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Documento iWork Pages", - "assets.mimetypes.application/x-javascript": "Código JavaScript", - "assets.mimetypes.application/x-mspublisher": "Documento Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animación Flash", - "assets.mimetypes.application/xhtml_xml": "Documento XHTML", - "assets.mimetypes.archive": "Archivo ({{$a.EXT}})", - "assets.mimetypes.audio": "Archivo de audio ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Archivo", - "assets.mimetypes.group:archive": "Archivos de fichero", - "assets.mimetypes.group:audio": "Archivos de audio", - "assets.mimetypes.group:document": "Archivos de documentos", - "assets.mimetypes.group:html_audio": "Archivos de audio compatibles con navegadores de forma nativa", - "assets.mimetypes.group:html_track": "Archivos de rastreo HTML", - "assets.mimetypes.group:html_video": "Archivos de vídeo compatibles con navegadores de forma nativa", - "assets.mimetypes.group:image": "Archivos de imagen", - "assets.mimetypes.group:presentation": "Archivos de presentación", - "assets.mimetypes.group:sourcecode": "Código fuente", - "assets.mimetypes.group:spreadsheet": "Archivos de hoja de cálculo", - "assets.mimetypes.group:video": "Archivos de vídeo", - "assets.mimetypes.group:web_audio": "Archivos de audio utilizados en la web", - "assets.mimetypes.group:web_file": "Archivos web", - "assets.mimetypes.group:web_image": "Formatos de imagen usados para web", - "assets.mimetypes.group:web_video": "Archivos de vídeo utilizados en la web", - "assets.mimetypes.image": "Imagen ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Icono de Windows", - "assets.mimetypes.text/css": "Hoja de estilos en cascada", - "assets.mimetypes.text/csv": "Archivo de valores separados por coma", - "assets.mimetypes.text/html": "Documento HTML", - "assets.mimetypes.text/plain": "Archivo de texto", - "assets.mimetypes.text/rtf": "documento RTF", - "assets.mimetypes.text/vtt": "Web Video Text Track", - "assets.mimetypes.video": "Fichero de video ({{$a.EXT}})", - "core.accounts": "Cuentas", - "core.add": "Agregar", - "core.agelocationverification": "Verificación de localización y edad", - "core.ago": "hace {{$a}}", - "core.all": "Todos", - "core.allgroups": "Todos los grupos", - "core.allparticipants": "Todos los participantes", - "core.answer": "Respuesta", - "core.answered": "Contestadas", - "core.areyousure": "¿Está seguro?", - "core.back": "Atrás", - "core.block.blocks": "Bloques", - "core.browser": "Navegador.", - "core.cancel": "Cancelar", - "core.cannotconnect": "No se puede conectar: Verifique que la URL es correcta y que el sitio Moodle usa la versión {{$a}} o posterior.", - "core.cannotconnecttrouble": "Estamos experemintando problemas para conectar a su sitio.", - "core.cannotconnectverify": "Por favor compruebe que la dirección es correcta.", - "core.cannotdownloadfiles": "La descarga de archivos está deshabilitada en su servicio Mobile. Por favor contacte al administrador de su sitio.", - "core.captureaudio": "Grabar audio", - "core.capturedimage": "Foto tomada.", - "core.captureimage": "Tomar foto", - "core.capturevideo": "Grabar video", - "core.category": "Categoría", - "core.choose": "Elegir", - "core.choosedots": "Elegir...", - "core.clearsearch": "Limpiar búsqueda", - "core.clicktohideshow": "Clic para expandir o colapsar", - "core.clicktoseefull": "Clic para ver el contenido al completo", - "core.close": "Cerrar", - "core.comments": "Comentarios", - "core.comments.addcomment": "Agrega un comentario...", - "core.comments.comments": "Comentarios", - "core.comments.commentscount": "Comentarios ({{$a}})", - "core.comments.commentsnotworking": "Los comentarios no se pueden recuperar.", - "core.comments.deletecommentbyon": "Borrar el comentario publicado por {{$a.user}} el {{$a.time}}", - "core.comments.eventcommentcreated": "Comentario creado", - "core.comments.eventcommentdeleted": "Comentario eliminado", - "core.comments.nocomments": "No hay comentarios", - "core.comments.savecomment": "Guardar comentario", - "core.comments.warningcommentsnotsent": "No se han podido sincronizar los comentarios. {{error}}", - "core.commentscount": "Comentarios ({{$a}})", - "core.completion-alt-auto-fail": "Finalizado {{$a}} (no ha alcanzado la calificación de aprobado)", - "core.completion-alt-auto-n": "Sin finalizar: {{$a}}", - "core.completion-alt-auto-n-override": "No completado: {{$a.modname}} (configurado por {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Finalizado: {{$a}} (ha alcanzado la calificación de aprobado)", - "core.completion-alt-auto-y": "Finalizado: {{$a}}", - "core.completion-alt-auto-y-override": "Completado: {{$a.modname}} (configurado por {{$a.overrideuser}})", - "core.completion-alt-manual-n": "No finalizado; {{$a}}. Seleccione para marcar como finalizado", - "core.completion-alt-manual-n-override": "No completado: {{$a.modname}} (configurado por {{$a.overrideuser}}). Seleccionar para marcarlo como completado.", - "core.completion-alt-manual-y": "Finalizado; {{$a}} seleccione para marcar como no finalizado", - "core.completion-alt-manual-y-override": "Completado: {{$a.modname}} (configurado por {{$a.overrideuser}}). Seleccionar para marcarlo como no completado.", - "core.confirmcanceledit": "¿Está usted seguro de que quiere abandonar esta página? Se perderán todos los cambios.", - "core.confirmdeletefile": "¿Está seguro de que desea eliminar este archivo?", - "core.confirmgotabroot": "¿Está seguro que quiere regresar a {{name}}?", - "core.confirmgotabrootdefault": "¿Está seguro que quiere regresar a la pagina inicial de esta pestaña?", - "core.confirmleaveunknownchanges": "¿Está seguro que desea abandonar esta página? Si tiene cambios no guardados se perderán.", - "core.confirmloss": "¿Está seguro? Se perderán todos los cambios.", - "core.confirmopeninbrowser": "¿Quiere abrirlo en el navegador?", - "core.considereddigitalminor": "Es muy joven para crear una cuenta en esta página.", - "core.content": "Contenido", - "core.contenteditingsynced": "El contenido que está editando ha sido sincronizado.", - "core.contentlinks.chooseaccount": "Escoger una cuenta", - "core.contentlinks.chooseaccounttoopenlink": "Escoger una cuenta con la que abrir el enlace.", - "core.contentlinks.confirmurlothersite": "Este enlace pertenece a otro sitio. ¿Quieres abrirlo?", - "core.contentlinks.errornoactions": "No se ha encontrado ninguna acción para realizar con este enlace.", - "core.contentlinks.errornosites": "No se ha encontrado ningún sitio para gestionar este enlace.", - "core.contentlinks.errorredirectothersite": "La dirección web solicitada no puede dirigirse a otra pagina.", - "core.continue": "Continuar", - "core.copiedtoclipboard": "Texto copiado al portapapeles", - "core.copytoclipboard": "Copiar al portapapeles", - "core.course": "Curso", - "core.course.activitydisabled": "Su organización ha deshabilitado esta actividad en la aplicación móvil.", - "core.course.activitynotyetviewableremoteaddon": "Esta actividad es un complemento de terceros que todavía no está soportada por la aplicación.", - "core.course.activitynotyetviewablesiteupgradeneeded": "El sitio no está usando la versión más reciente de Moodle. Por favor, contacte al administrador del sitio.", - "core.course.allsections": "Todas las secciones", - "core.course.askadmintosupport": "Contacte al administrador del sitio e indíquele que desea poder usar esta actividad en la aplicación.", - "core.course.availablespace": "Actualmente tiene {{available}} espacio disponible.", - "core.course.cannotdeletewhiledownloading": "No se pueden borrar archivos mientras la actividad se descarga. Por favor espere a que finalice la descarga.", - "core.course.confirmdeletemodulefiles": "¿Está seguro de querer eliminar estos archivos del módulo?", - "core.course.confirmdownload": "Está a punto de descargar {{size}}.{{availableSpace}} ¿Está seguro que desea continuar?", - "core.course.confirmdownloadunknownsize": "Ha sido imposible calcular el tamaño de la descarga. ¿Está seguro que desea continuar?", - "core.course.confirmdownloadzerosize": "Esta a punto de descargar. {{availableSpace}} ¿Está seguro de que desea continuar?", - "core.course.confirmlimiteddownload": "En este momento no está conectado al WI-FI.", - "core.course.confirmpartialdownloadsize": "Está a punto de descargar al menos {{size}}. {{availableSpace}} ¿Está seguro que desea continuar?", - "core.course.contents": "Contenidos", - "core.course.couldnotloadsectioncontent": "No se ha podido cargar el contenido de la sección, por favor inténtelo más tarde.", - "core.course.couldnotloadsections": "No se ha podido cargar las secciones, por favor inténtelo más tarde.", - "core.course.coursesummary": "Resumen del curso", - "core.course.downloadcourse": "Descargar curso", - "core.course.errordownloadingcourse": "Error al descargar el curso.", - "core.course.errordownloadingsection": "Error durante la descarga de la sección.", - "core.course.errorgetmodule": "Se ha producido un error recuperando los datos del módulo.", - "core.course.hiddenfromstudents": "No mostrado a los estudiantes", - "core.course.hiddenoncoursepage": "Disponibles pero no visibles en la página del curso", - "core.course.insufficientavailablequota": "Su dispositivo no pudo encontrar espacio para guardar esta descarga. Debe estar reservando espacio para la actualización del sistema y de la aplicación. Por favor libere espacio en la memoria primero.", - "core.course.insufficientavailablespace": "Está intentando descargar {{size}}. Esto dejará su dispositivo con espacio insuficiente para operar normalmente. Por favor libere espacio en la memoria primero.", - "core.course.manualcompletionnotsynced": "Finalización manual no sincronizada.", - "core.course.nocontentavailable": "No hay contenido disponible en este momento.", - "core.course.overriddennotice": "La calificación final de esta actividad ha sido ajustada manualmente.", - "core.course.refreshcourse": "Refrescar curso", - "core.course.sections": "Secciones", - "core.course.useactivityonbrowser": "Puede acceder a esta actividad mediante el navegador de su dispositivo.", - "core.course.warningmanualcompletionmodified": "La finalización manual de una actividad fue modificada en el sitio.", - "core.course.warningofflinemanualcompletiondeleted": "Una parte de la finalización manual del curso '{{name}}' ha sido eliminada. {{error}}", - "core.coursedetails": "Detalles del curso", - "core.coursenogroups": "Usted no es miembro de ningún grupo de este curso.", - "core.courses.addtofavourites": "Destacar este curso", - "core.courses.allowguests": "Este curso permite la entrada de invitados", - "core.courses.availablecourses": "Cursos disponibles", - "core.courses.cannotretrievemorecategories": "No se pueden recuperar categorías más profundas que el nivel {{$a}}.", - "core.courses.categories": "Categorías", - "core.courses.confirmselfenrol": "¿Está seguro que desea auto-matricularse en este curso?", - "core.courses.courses": "Cursos", - "core.courses.downloadcourses": "Descargar cursos", - "core.courses.enrolme": "Matricularme", - "core.courses.errorloadcategories": "Ocurrió un error al cargar categorías.", - "core.courses.errorloadcourses": "Se ha producido un error cargando los cursos.", - "core.courses.errorloadplugins": "Los plugins requeridos por este curso no se han podido cargar correctamente. Por favor, reinicie la aplicación e inténtelo de nuevo.", - "core.courses.errorsearching": "Se ha producido un error durante la búsqueda.", - "core.courses.errorselfenrol": "Se ha producido un error durante la auto-matriculación.", - "core.courses.filtermycourses": "Filtrar mis cursos", - "core.courses.frontpage": "Página Principal", - "core.courses.hidecourse": "Eliminados de la vista", - "core.courses.ignore": "Ignorar", - "core.courses.mycourses": "Mis cursos", - "core.courses.mymoodle": "Área personal", - "core.courses.nocourses": "No hay información del curso para mostrar.", - "core.courses.nocoursesyet": "No hay cursos en esta categoría", - "core.courses.nosearchresults": "Sin resultados", - "core.courses.notenroled": "Usted no está matriculado en este curso", - "core.courses.notenrollable": "No puede auto-matricularse en este curso.", - "core.courses.password": "Clave", - "core.courses.paymentrequired": "Para entrar a este curso es necesario pagar.", - "core.courses.paypalaccepted": "Pagos PayPal aceptados", - "core.courses.reload": "Recargar", - "core.courses.removefromfavourites": "Quitar estrella a este curso", - "core.courses.search": "Buscar", - "core.courses.searchcourses": "Buscar cursos", - "core.courses.searchcoursesadvice": "Puede utilizar el botón de buscar cursos para acceder como invitado o auto-matricularse en los cursos que lo permitan.", - "core.courses.selfenrolment": "Auto-matriculación", - "core.courses.sendpaymentbutton": "Enviar pago por Paypal", - "core.courses.show": "Mostrar vista", - "core.courses.totalcoursesearchresults": "Total de cursos: {{$a}}", - "core.currentdevice": "Dispositivo actual", - "core.datastoredoffline": "Los datos se almacenaron en el dispositivo debido a que no se pudieron enviar. Serán enviados automáticamente más tarde.", - "core.date": "Fecha", - "core.day": "día", - "core.days": "días", - "core.decsep": ",", - "core.defaultvalue": "Por defecto ({{$a}})", - "core.delete": "Borrar", - "core.deletedoffline": "Eliminado fuera de línea", - "core.deleteduser": "Usuario eliminado", - "core.deleting": "Borrando", - "core.description": "Descripción", - "core.desktop": "Escritorio", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Menor digital", - "core.digitalminor_desc": "Por favor solicitele a su padre o tutor que contacte a:", - "core.discard": "Descartar", - "core.dismiss": "Descartar", - "core.displayoptions": "Opciones de visualización", - "core.done": "Hecho", - "core.download": "Descargar", - "core.downloaded": "Descargada", - "core.downloading": "Descargando...", - "core.edit": "Editar", - "core.editor.autosavesucceeded": "Borrador guardado.", - "core.editor.bold": "Negrita", - "core.editor.clear": "Limpiar el formato", - "core.editor.h3": "Encabezado (largo)", - "core.editor.h4": "Encabezado (medio)", - "core.editor.h5": "Encabezado (pequeño)", - "core.editor.hidetoolbar": "Ocultar la barra de herramientas", - "core.editor.italic": "Itálica", - "core.editor.orderedlist": "Lista ordenada", - "core.editor.p": "Párrafo", - "core.editor.strike": "Tachar", - "core.editor.textrecovered": "Una versión en borrador de este texto ha sido automáticamente restaurada.", - "core.editor.toggle": "Alternar el editor", - "core.editor.underline": "Subrayar", - "core.editor.unorderedlist": "Lista desordenada", - "core.emptysplit": "Esta página aparecerá en blanco si el panel izquierdo está vacío o si está cargando.", - "core.error": "Error", - "core.errorchangecompletion": "Ha ocurrido un error cargando el grado de realización. Por favor inténtalo de nuevo.", - "core.errordeletefile": "Error al eliminar el archivo. Por favor inténtelo de nuevo.", - "core.errordownloading": "Ocurrió un error descargando el archivo", - "core.errordownloadingsomefiles": "Se ha producido un error descargando los ficheros del módulo. Algunos archivos se pueden haber perdido.", - "core.errorfileexistssamename": "Ya existe un archivo con este nombre.", - "core.errorinvalidform": "El formulario contiene datos inválidos. Por favor, asegúrese de rellenar todos los campos requeridos y que los datos son válidos.", - "core.errorinvalidresponse": "Se ha recibido una respuesta no válida. Por favor contactar con el administrador de Moodle si el error persiste.", - "core.errorloadingcontent": "Error cargando contenido.", - "core.errorofflinedisabled": "La navegación fuera de línea está deshabilitada en su sitio. Necesita estar conectado a Internet para usar la aplicación.", - "core.erroropenfilenoapp": "Error durante la apertura del archivo: no se encontró ninguna aplicación capaz de abrir este tipo de archivo.", - "core.erroropenfilenoextension": "Se ha producido un error abriendo el archivo: el archivo no tiene extensión.", - "core.erroropenpopup": "Esta actividad está intentando abrir una ventana emergente. Esta aplicación no lo soporta.", - "core.errorrenamefile": "Error al renombrar el archivo. Por favor inténtelo de nuevo.", - "core.errorsomedatanotdownloaded": "Si ha descargado esta actividad, por favor tenga en cuenta que la información no ha sido descargada durante el proceso de descarga por problemas en el uso de datos y funcionamiento de la aplicación.", - "core.errorsync": "Ocurrió un error al sincronizar. Por favor inténtelo de nuevo más tarde.", - "core.errorsyncblocked": "Este/a {{$a}} no puede sincronizarse ahora mismo porque hay un proceso trabajando. Por favor inténtelo de nuevo más tarde. Si el problema persiste, intente reiniciar la aplicación.", - "core.explanationdigitalminor": "Esta información es necesaria para determinar si su edad está por encima de la edad digital de consentimiento. Esta es la edad a la que un individuo puede aceptar los términos y condiciones y que sus datos sean legalmente almacenados y procesados.", - "core.favourites": "Destacados", - "core.filename": "Nombre del archivo", - "core.filenameexist": "El nombre de archivo ya existe: {{$a}}", - "core.filenotfound": "Lo sentimos, el archivo no se ha encontrado.", - "core.fileuploader.addfiletext": "Añadir fichero", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Cámara", - "core.fileuploader.confirmuploadfile": "Usted va a subir {{size}}. ¿Desea continuar?", - "core.fileuploader.confirmuploadunknownsize": "No pudimos calcular el tamaño de la subida. ¿Está seguro de querer continuar?", - "core.fileuploader.errorcapturingaudio": "Error capturando audio", - "core.fileuploader.errorcapturingimage": "Error capturando imagen.", - "core.fileuploader.errorcapturingvideo": "Error grabando el vídeo", - "core.fileuploader.errorgettingimagealbum": "Error tomando la imagen desde al álbum.", - "core.fileuploader.errormustbeonlinetoupload": "Debe estar conectado para subir archivos.", - "core.fileuploader.errornoapp": "Usted no tiene una app instalada para realizar esta acción.", - "core.fileuploader.errorreadingfile": "Error al leer el archivo \"{$a}\"", - "core.fileuploader.errorwhileuploading": "Ocurrió un error al subir un archivo.", - "core.fileuploader.file": "Archivo", - "core.fileuploader.filesofthesetypes": "Tipos de archivo aceptados:", - "core.fileuploader.fileuploaded": "Archivo subido", - "core.fileuploader.invalidfiletype": "El tipo de archivo {{$a}} no se acepta.", - "core.fileuploader.maxbytesfile": "El archivo {{$a.file}} es demasido grande. El tamaño máximo que puede subir es {{$a.size}}.", - "core.fileuploader.more": "Más", - "core.fileuploader.photoalbums": "Álbumes de fotos", - "core.fileuploader.readingfile": "Leyendo archivo", - "core.fileuploader.readingfileperc": "Leyendo fichero: {{$a}}%", - "core.fileuploader.selectafile": "Seleccionar un archivo", - "core.fileuploader.uploadafile": "Subir un archivo", - "core.fileuploader.uploading": "Subiendo", - "core.fileuploader.uploadingperc": "Subiendo: {{$a}}%", - "core.fileuploader.video": "Vídeo", - "core.filter": "Filtro", - "core.folder": "Carpeta", - "core.forcepasswordchangenotice": "Para continuar, deberá cambiar su contraseña.", - "core.fulllistofcourses": "Todos los cursos", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Promedio", - "core.grades.badgrade": "La calificación suministrada no es válida", - "core.grades.contributiontocoursetotal": "Aporta al total del curso", - "core.grades.feedback": "Retroalimentación", - "core.grades.grade": "Calificación", - "core.grades.gradeitem": "Ítem de calificación", - "core.grades.grades": "Calificaciones", - "core.grades.lettergrade": "Calificación por letra", - "core.grades.nogradesreturned": "No se han devuelto calificaciones", - "core.grades.nooutcome": "No resultado", - "core.grades.percentage": "Porcentaje", - "core.grades.range": "Rango", - "core.grades.rank": "Ranking", - "core.grades.weight": "Peso", - "core.group": "Grupo", - "core.groupsseparate": "Grupos separados", - "core.groupsvisible": "Grupos visibles", - "core.h5p.additionallicenseinfo": "Cualquier información adicional acerca de la licencia", - "core.h5p.author": "Autor", - "core.h5p.authorcomments": "Comentarios del autor", - "core.h5p.authorcommentsdescription": "Comentarios para el editor del contenido. (Este texto no será publicado como parte de la información de copyright)", - "core.h5p.authorname": "Nombre del autor", - "core.h5p.authorrole": "Rol del autor", - "core.h5p.by": "por", - "core.h5p.cancellabel": "Cancelar", - "core.h5p.ccattribution": "Atribución (CC BY)", - "core.h5p.ccattributionnc": "Licencia de Atribución-No-Comercial (CC BY-NC)", - "core.h5p.ccattributionncnd": "Licencia de Atribución-No-Comercial-NoDerivs (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Licencia de Atribución-No-Comercial-ShareAlike (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Licencia de Atribución-NoDerivs (CC BY-ND)", - "core.h5p.ccattributionsa": "Licencia de Atribución-ShareAlike (CC BY-SA)", - "core.h5p.ccpdd": "Dedicación del Dominio Público (CC0)", - "core.h5p.changedby": "Cambiado por", - "core.h5p.changedescription": "Descripción del cambio", - "core.h5p.changelog": "Bitácora de cambios", - "core.h5p.changeplaceholder": "Foto recortada, texto cambiado, etc.", - "core.h5p.close": "Cerrar", - "core.h5p.confirmdialogbody": "Por favor confirme que desea proseguir. Esta acción no puede deshacerse.", - "core.h5p.confirmdialogheader": "Confirmar acción", - "core.h5p.confirmlabel": "Confirmar", - "core.h5p.connectionLost": "Conexión perdida. Los resultados serán almacenados y enviados cuando la conexión sea restablecida.", - "core.h5p.connectionReestablished": "Conexión restablecida", - "core.h5p.contentCopied": "El contenido es copiado al portapapeles", - "core.h5p.contentchanged": "Este contenido ha cambiado desde la última vez que lo utilizó.", - "core.h5p.contenttype": "Tipo de Contenido", - "core.h5p.copyright": "Derechos de uso", - "core.h5p.copyrightinfo": "Información del Copyright", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "Ver información de copyright para este contenido.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Fecha", - "core.h5p.disablefullscreen": "Deshabilitar pantalla completa", - "core.h5p.download": "Descargar", - "core.h5p.downloadtitle": "Descargar este contenido como un archivo H5P.", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Incrustar", - "core.h5p.embedtitle": "Ver el contenido incrustado para este comentario", - "core.h5p.errorgetemail": "Ocurrió un error al obtener su email. Por favor, compruebe su conexión e inténtelo de nuevo.", - "core.h5p.fullscreen": "Pantalla completa", - "core.h5p.gpl": "Licencia Pública General v3", - "core.h5p.h5ptitle": "Visitar H5P.org para revisar más contenido.", - "core.h5p.hideadvanced": "Ocultar avanzadas", - "core.h5p.license": "Licencia", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) Dedicación del Dominio Público", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Genérico", - "core.h5p.licenseCC20": "2.0 Genérico", - "core.h5p.licenseCC25": "2.5 Genérico", - "core.h5p.licenseCC30": "3.0 No Portada", - "core.h5p.licenseCC40": "4.0 Internacional", - "core.h5p.licenseGPL": "Licencia Pública General", - "core.h5p.licenseV1": "Versión 1", - "core.h5p.licenseV2": "Versión 2", - "core.h5p.licenseV3": "Versión 3", - "core.h5p.licensee": "Licenciatario", - "core.h5p.licenseextras": "Extras de Licencia", - "core.h5p.licenseversion": "Versión de Licencia", - "core.h5p.nocopyright": "Sin información de copyright disponible para este contenido.", - "core.h5p.offlineDialogBody": "No pudimos enviar información acerca de su finalización de este trabajo. Por favor revise su conexión de internet.", - "core.h5p.offlineDialogHeader": "Se perdió su conexión al servidor", - "core.h5p.offlineDialogRetryButtonLabel": "Reintentar ahora", - "core.h5p.offlineDialogRetryMessage": "Reintentando en :num....", - "core.h5p.offlineSuccessfulSubmit": "Resultados enviados exitosamente.", - "core.h5p.offlinedisabled": "Este sitio no permite la descarga de paquetes H5P", - "core.h5p.originator": "Originador", - "core.h5p.pd": "Dominio Público", - "core.h5p.pddl": "Dedicación y Licencia del Dominio Público", - "core.h5p.pdm": "Marca de Dominio Público (PDM)", - "core.h5p.play": "Visualizar H5P", - "core.h5p.resizescript": "Incluya este script en su sitio web si desea ajuste de tamaño dinámico del contenido incrustado.", - "core.h5p.resubmitScores": "Intentando enviar resultados guardados.", - "core.h5p.reuse": "Reutilizar", - "core.h5p.reuseContent": "Reutilizar Contenido", - "core.h5p.reuseDescription": "Reutilizar este contenido.", - "core.h5p.showadvanced": "Mostrar avanzadas", - "core.h5p.showless": "Mostrar menos", - "core.h5p.showmore": "Mostrar más", - "core.h5p.size": "Tamaño", - "core.h5p.source": "Origen", - "core.h5p.startingover": "Usted estará re-comenzando.", - "core.h5p.sublevel": "Subnivel", - "core.h5p.thumbnail": "Miniatura", - "core.h5p.title": "Título", - "core.h5p.undisclosed": "No revelado", - "core.h5p.year": "Año", - "core.h5p.years": "Año(s)", - "core.h5p.yearsfrom": "Años (desde)", - "core.h5p.yearsto": "Años (hasta)", - "core.hasdatatosync": "Este/a {{$a}} tiene datos fuera de línea para sincronizarse.", - "core.help": "Ayuda", - "core.hide": "Ocultar", - "core.hour": "hora", - "core.hours": "horas", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Imagen", - "core.imageviewer": "Visor de imágenes", - "core.info": "Información", - "core.invalidformdata": "Formulario de datos incorrecto", - "core.labelsep": ":", - "core.lastaccess": "Último acceso", - "core.lastdownloaded": "Última descarga", - "core.lastmodified": "Última modificación", - "core.lastsync": "Última sincronización", - "core.layoutgrid": "Rejilla", - "core.list": "Lista", - "core.listsep": ";", - "core.loading": "Cargando", - "core.loadmore": "Cargar más", - "core.location": "Ubicación", - "core.login.auth_email": "Identificación basada en Email", - "core.login.authenticating": "Autenticando", - "core.login.cancel": "Cancelar", - "core.login.changepassword": "Cambiar contraseña", - "core.login.changepasswordbutton": "Abrir la página de cambio de contraseña", - "core.login.changepasswordhelp": "Si tiene problemas cambiando su contraseña por favor contacto al administrador de su sitio. \"El administrador del sitio\" es el encargado de la gestión de Moodle en su escuela/Universidad/empresa. Si desconoce como contactarle, por favor, contacte con su profesor.", - "core.login.changepasswordinstructions": "No puede cambiar su contraseña en la aplicación. Por favor, pulse el siguiente botón para abrir el sitio en un navegador web y cambiar allí su contraseña. Tenga en cuenta que si cierra el navegador antes de cambiar la contraseña no se le redirigirá de nuevo a la aplicación.", - "core.login.changepasswordlogoutinstructions": "Si prefiere cambiar el sitio o salir, por favor pulse el siguiente botón:", - "core.login.changepasswordreconnectinstructions": "Pulse el siguiente botón para reconectar a su sitio. (Tenga en cuenta que si no cambió correctamente su contraseña volverá a la pantall aanterior).", - "core.login.confirmdeletesite": "¿Está seguro de que quiere eliminar el sitio {{sitename}}?", - "core.login.connect": "Conectar", - "core.login.connecttomoodle": "Conectar a Moodle", - "core.login.connecttomoodleapp": "Está intentando conectarse a una página regular Moodle. Por favor descargue la aplicación Moodle para acceder a esta página.", - "core.login.connecttoworkplaceapp": "Está intentando conectarse a una pagina de trabajo Moodle. Por favor descargue la aplicación Estación Trabajo Moodle para acceder a esta página.", - "core.login.contactyouradministrator": "Contacte a su administrador del sitio para más ayuda.", - "core.login.contactyouradministratorissue": "Por favor, pídale al administrador que revise el siguiente problema: {{$a}}", - "core.login.createaccount": "Crear cuenta", - "core.login.createuserandpass": "Crear un nuevo usuario y contraseña para acceder al sistema", - "core.login.credentialsdescription": "Introduzca su nombre se usuario y contraseña para entrar", - "core.login.emailconfirmsent": "

              Hemos enviado un correo electrónico a {{$a}}

              \n

              En él encontrará instrucciones sencillas para concluir el proceso.

              \n

              Si tuviera alguna dificultad, contacte con el Administrador del Sistema.

              ", - "core.login.emailconfirmsentnoemail": "

              Un email debería de haberse enviado a su dirección.

              Contiene instrucciones fáciles para completar su registro.

              Si persisten las dificultades, póngase en contacto con el administrador del sitio.

              ", - "core.login.emailconfirmsentsuccess": "Correo de confirmación enviado exitosamente", - "core.login.emailnotmatch": "Las direcciones de correo no coinciden.", - "core.login.erroraccesscontrolalloworigin": "La llamada Cross-Origin que está intentando ha sido rechazada. Por favor visite https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Ha ocurrido un error eliminando este sitio. Por favor, inténtelo de nuevo.", - "core.login.errorexampleurl": "La URL https://campus.example.edu es un ejemplo, no es un sitio de verdad. Por favor use la URL de su escuela u organización.", - "core.login.errorupdatesite": "Se ha producido un error actualizando el token del sitio.", - "core.login.faqcannotconnectanswer": "Por favor, contacte al administrador de su sitio.", - "core.login.faqcannotconnectquestion": "He introducido correctamente la dirección de mi sitio pero aún no puedo conectar.", - "core.login.faqcannotfindmysiteanswer": "¿Ha introducido el nombre correctamente? También es posible que su sitio no esté incluido en el directorio público. Si no lo puede encontrar, por favor introduzca en su lugar la dirección web (URL) de su sitio.", - "core.login.faqcannotfindmysitequestion": "No puedo encontrar mi sitio.", - "core.login.faqsetupsiteanswer": "Visite {{$link}} para comprobar las diferentes opciones disponibles a la hora de crear su propio sitio Moodle.", - "core.login.faqsetupsitelinktitle": "Comenzar", - "core.login.faqsetupsitequestion": "Quiero crear mi propio sitio Moodle", - "core.login.faqtestappanswer": "Para probar la app en un sitio Moodle de pruebas, escriba \"teacher\" o \"student\" en el campo \"Su sitio\" y haga clic en el botón \"Conectar a su sitio\"", - "core.login.faqtestappquestion": "Solamente quiero probar la app, ¿Qué puedo hacer?", - "core.login.faqwhatisurlanswer": "

              Todas las organizaciones tienen su dirección URL propia para su sitio Moodle. Para encontrar su dirección:

              1. Abra un navegador web y vaya a la página de inicio de su sitio Moodle.
              2. En la parte superior de la página, en la barra de direcciones, verá la URL de su sitio Moodle p.ej. \"campus.example.edu\".
                {{$image}}
              3. Copie la dirección (omitiendo todo lo que va después de /login), y pégala en la app de Moodle, pulse \"Conectar a su sitio\"
              4. Ahora ya podrá introducir su usuario y contraseña para acceder a su sitio.
              5. ", - "core.login.faqwhatisurlquestion": "¿Cuál es la dirección de mi sitio? ¿Dónde puedo encontrar la URL de mi sitio?", - "core.login.faqwhereisqrcode": "¿Dónde puedo encontrar el código QR?", - "core.login.faqwhereisqrcodeanswer": "

                Si su organización lo ha habilitado, encontrará el código QR en su sitio Moodle en la parte inferior de su página de perfil de usuario.

                {{$image}}", - "core.login.findyoursite": "Encuentre su sitio", - "core.login.firsttime": "Registrarse como usuario", - "core.login.forcepasswordchangenotice": "Para continuar, deberá cambiar su contraseña.", - "core.login.forgotten": "¿Olvidó su nombre de usuario o contraseña?", - "core.login.help": "Ayuda", - "core.login.helpmelogin": "

                Existen muchos sitios Moodle en el mundo. Esta aplicación solamente puede conectar a sitios Moodle que tengan específicamente habilitado el acceso por la aplicación Moodle.

                Si no puede conectarse a su sitio Moodle, póngase en contacto con el administrador del sitio y pídale que lea http://docs.moodle.org/es/Mobile_app

                Para probar la aplicación en un sitio Moodle de muestra, escriba teacher o student en el campo de la Dirección del sitio y haga click en el Botón Conectar.

                ", - "core.login.instructions": "Instrucciones", - "core.login.invalidaccount": "Por favor, verifique sus datos de acceso, o consulte con el administrador para revisar la configuración del sitio.", - "core.login.invaliddate": "Fecha no válida", - "core.login.invalidemail": "Dirección de correo no válida", - "core.login.invalidmoodleversion": "

                Versión inválida de sitio Moodle. La App Moodle solamente soporta sistemas Moodle {{$a}} en adelante.

                \n

                Puede ponerse en contacto con el administrador de sus sitio y pedirle que actualice su sistema Moodle.

                \n

                Los \"Administradores del sitio\" son las personas que gestionan el Moodle en su escuela/universidad/compañía o su organización educativa. Si no sabe a quien contactar, por favor póngase en contacto con sus profesores/entrenadores.

                ", - "core.login.invalidsite": "La URL del sitio es inválida.", - "core.login.invalidtime": "Hora incorrecta", - "core.login.invalidurl": "Se ha especificado una URL no válida", - "core.login.invalidvaluemax": "El valor máximo es {{$a}}", - "core.login.invalidvaluemin": "El valor mínimo es {{$a}}", - "core.login.localmobileunexpectedresponse": "Las características adicionales de Moodle Mobile han devuelto una respuesta inesperada, debe autenticarse utilizando el servicio estándar de Mobile.", - "core.login.loggedoutssodescription": "Tiene que autenticarse nuevamente. Necesita acceder al sitio en una ventana del navegador.", - "core.login.login": "Acceder", - "core.login.loginbutton": "Acceder", - "core.login.logininsiterequired": "Para autentificarse en el sitio se ha de abrir una ventana de navegador.", - "core.login.loginsteps": "Para acceder a esta página debe crear una cuenta primero.", - "core.login.missingemail": "Falta la dirección de correo", - "core.login.missingfirstname": "Falta el nombre dado", - "core.login.missinglastname": "Falta el apellido(s)", - "core.login.mobileservicesnotenabled": "El acceso móvil no está habilitado para este sitio, por favor, contacte con su administrador si piensa que debería estar habilitado.", - "core.login.mustconfirm": "Necesita confirmar su cuenta", - "core.login.newaccount": "Nueva cuenta", - "core.login.notloggedin": "Ha de entrar al sitio.", - "core.login.onboardingcreatemanagecourses": "Crear y gestionar sus curos", - "core.login.onboardingenrolmanagestudents": "Matricular y gestionar sus estudiantes", - "core.login.onboardinggetstarted": "Comenzar con Moodle", - "core.login.onboardingialreadyhaveasite": "Ya tengo un sitio Moodle", - "core.login.onboardingimalearner": "Soy un estudiante", - "core.login.onboardingimaneducator": "Soy un mentor/profesor", - "core.login.onboardingineedasite": "Necesito un sitio Moodle", - "core.login.onboardingprovidefeedback": "Denos su opinión", - "core.login.onboardingtoconnect": "Para utilizar la app de Moodle necesita un sitio Moodle.", - "core.login.onboardingwelcome": "¡Bienvenido a la app de Moodle!", - "core.login.or": "O", - "core.login.password": "Contraseña", - "core.login.passwordforgotten": "Contraseña olvidada", - "core.login.passwordforgotteninstructions2": "Para reajustar su contraseña, envíe su nombre de usuario o su dirección de correo electrónico. Si podemos encontrarlo en la base de datos, le enviaremos un email con instrucciones para poder acceder de nuevo.", - "core.login.passwordrequired": "Contraseña obligatoria", - "core.login.policyaccept": "Entiendo y estoy de acuerdo", - "core.login.policyagree": "Usted deberá estar de acuerdo con estas condiciones antes de seguir usando este sitio. ¿Está de acuerdo?", - "core.login.policyagreement": "Acuerdo con las Condiciones del Sitio", - "core.login.policyagreementclick": "Haga clic aquí para leer el acuerdo con las condiciones del sitio", - "core.login.potentialidps": "Identifíquese usando su cuenta en:", - "core.login.profileinvaliddata": "Valor no válido", - "core.login.recaptchachallengeimage": "reto reCAPTCHA", - "core.login.recaptchaexpired": "La verificación ha expirado. Contesta nuevamente la pregunta de seguridad.", - "core.login.recaptchaincorrect": "La respuesta a la pregunta de seguridad es incorrecta.", - "core.login.reconnect": "Reconectar", - "core.login.reconnectdescription": "El token de autenticación no es válido o ha caducado, ha de volver a conectarse al sitio.", - "core.login.reconnectssodescription": "El token de autenticación no es válido o ha caducado, ha de volver a conectarse al sitio. Necesita hacerlo desde un navegador web.", - "core.login.resendemail": "Reenviar correo", - "core.login.searchby": "Buscar por:", - "core.login.security_question": "Pregunta de seguridad", - "core.login.selectacountry": "Seleccione su país", - "core.login.selectsite": "Por favor seleccione su sitio:", - "core.login.signupplugindisabled": "{{$a}} no está habilitado", - "core.login.siteaddress": "Su sitio", - "core.login.sitehasredirect": "Su sitio contiene al menos una redirección HTTP. La aplicación no puede seguir redirectiones, este puede ser el problema que impide a la aplicación conectar a su sitio.", - "core.login.siteinmaintenance": "Su sitio está en modo mantenimiento", - "core.login.sitepolicynotagreederror": "No se aceptó la política del sitio.", - "core.login.siteurl": "URL del sitio", - "core.login.siteurlrequired": "La URL del sitio es obligatoria, por ejemplo http://www.yourmoodlesite.es o https://www.yourmoodlesite.org", - "core.login.startsignup": "Crear nueva cuenta", - "core.login.stillcantconnect": "¿Todavía no puede conectar?", - "core.login.supplyinfo": "Por favor, escriba algunos datos sobre usted", - "core.login.username": "Nombre de usuario", - "core.login.usernameoremail": "Escribir nombre de usuario o dirección email", - "core.login.usernamerequired": "Nombre de usuario obligatorio", - "core.login.usernotaddederror": "No se agregó el usuario - error desconocido", - "core.login.visitchangepassword": "¿Quiere visitar el sitio para cambiar la contraseña?", - "core.login.webservicesnotenabled": "Los Servicios Web no están habilitados en su sitio, por favor, contacte con su administrador de Moodle si piensa que deberían estarlo.", - "core.login.youcanstillconnectwithcredentials": "Todavía puede conectar al sitio introduciendo su usuario y contraseña.", - "core.login.yourenteredsite": "Conectar a su sitio", - "core.lostconnection": "Hemos perdido la conexión, necesita reconectar. Su token ya no es válido", - "core.mainmenu.changesite": "Cambiar de sitio", - "core.mainmenu.help": "Ayuda", - "core.mainmenu.logout": "Salir", - "core.mainmenu.website": "Página web", - "core.maxsizeandattachments": "Tamaño máximo de archivo: {{$a.size}}, número máximo de archivos: {{$a.attachments}}", - "core.min": "minutos", - "core.mins": "minutos", - "core.misc": "Varios", - "core.mod_assign": "Tarea", - "core.mod_assignment": "Tarea 2.2 (Desactivada)", - "core.mod_book": "Libro", - "core.mod_chat": "Chat", - "core.mod_choice": "Consulta", - "core.mod_data": "Base de datos", - "core.mod_database": "Base de datos", - "core.mod_external-tool": "Herramienta Externa", - "core.mod_feedback": "Módulo de encuesta", - "core.mod_file": "Archivo", - "core.mod_folder": "Carpeta", - "core.mod_forum": "Foro", - "core.mod_glossary": "Glosario", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "Paquete de contenidos IMS", - "core.mod_imscp": "Paquete de contenidos IMS", - "core.mod_label": "Etiqueta", - "core.mod_lesson": "Lección", - "core.mod_lti": "Herramienta Externa", - "core.mod_page": "Página", - "core.mod_quiz": "Cuestionario", - "core.mod_resource": "Recurso", - "core.mod_scorm": "Paquete SCORM", - "core.mod_survey": "Encuesta predefinida", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Taller", - "core.moduleintro": "Descripción", - "core.more": "más", - "core.mygroups": "Mis grupos", - "core.name": "Nombre", - "core.needhelp": "¿Necesita ayuda?", - "core.networkerroriframemsg": "Este contenido no está disponible sin conexión. Por favor, conecte a Internet e inténtelo de nuevo.", - "core.networkerrormsg": "Conexión no disponible o sin funcionar.", - "core.never": "Nunca", - "core.next": "Siguiente", - "core.no": "No", - "core.nocomments": "No hay comentarios", - "core.nograde": "No hay calificación", - "core.none": "Ninguno", - "core.nooptionavailable": "No hay opción disponible", - "core.nopasswordchangeforced": "No puede continuar sin cambiar su contraseña.", - "core.nopermissionerror": "Discuple, pero no tiene permisos para realizar la acción.", - "core.nopermissions": "Lo sentimos, pero no tiene los permisos para hacer esto ({{$a}}).", - "core.noresults": "No hay resultados", - "core.noselection": "No hay selección", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Este perfil no está disponible porque el usuario no está matriculado en este curso.", - "core.notice": "Aviso", - "core.notingroup": "Lo sentimos, necesita ser miembro de un grupo para ver esta página.", - "core.notsent": "No enviado", - "core.now": "ahora", - "core.nummore": "{{$a}} más", - "core.numwords": "{{$a}} palabras", - "core.offline": "Fuera de línea", - "core.ok": "OK", - "core.online": "En línea", - "core.openfullimage": "Haga clic aquí para ver la imagen a tamaño completo", - "core.openinbrowser": "Abrir en el navegador", - "core.openmodinbrowser": "Abrir {{$a}} en el navegador", - "core.othergroups": "Otros grupos", - "core.pagea": "Página {{$a}}", - "core.paymentinstant": "¡Utilice el botón de abajo para pagar y poder matricularse en minutos!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Teléfono", - "core.pictureof": "Imagen de {{$a}}", - "core.previous": "Anterior", - "core.proceed": "Proceder", - "core.pulltorefresh": "Tirar para recargar", - "core.qrscanner": "Escáner de QR", - "core.question.answer": "Respuesta", - "core.question.answersaved": "Respuesta guardada", - "core.question.cannotdeterminestatus": "No se puede determinar el estado.", - "core.question.certainty": "Certeza", - "core.question.complete": "Finalizado", - "core.question.correct": "Correcta", - "core.question.errorattachmentsnotsupported": "La aplicación no soporta adjuntar archivos a respuestas todavía.", - "core.question.errorinlinefilesnotsupported": "La aplicación aun no soporta el editar archivos en-línea.", - "core.question.errorquestionnotsupported": "Este tipo de pregunta no está soportada por la aplicación: {{$a}}.", - "core.question.feedback": "Retroalimentación", - "core.question.howtodraganddrop": "Tocar para seleccionar y tocar para soltar.", - "core.question.incorrect": "Incorrecta", - "core.question.information": "Información", - "core.question.invalidanswer": "Respuesta incompleta", - "core.question.notanswered": "Sin contestar", - "core.question.notyetanswered": "Sin responder aún", - "core.question.partiallycorrect": "Parcialmente correcta", - "core.question.questionmessage": "Pregunta {{$a}}: {{$b}}", - "core.question.questionno": "Pregunta {{$a}}", - "core.question.requiresgrading": "Requiere calificación", - "core.quotausage": "Actualmente has utilizado {{$a.used}} de tu {{$a.total}} límite.", - "core.rating.aggregateavg": "Promedio de calificaciones", - "core.rating.aggregatecount": "Número de Calificaciones", - "core.rating.aggregatemax": "Calificación máxima", - "core.rating.aggregatemin": "Calificación mínima", - "core.rating.aggregatesum": "Suma de calificaciones", - "core.rating.noratings": "No se han emitido calificaciones", - "core.rating.rating": "Calificación", - "core.rating.ratings": "Calificaciones", - "core.redirectingtosite": "Será redirigido al sitio.", - "core.refresh": "Recargar", - "core.remove": "Quitar", - "core.removefiles": "Eliminar archivos {{$a}}", - "core.required": "Obligatorio", - "core.requireduserdatamissing": "En este perfil de usuario faltan datos requeridos. Por favor, rellene estos datos e inténtelo otra vez.
                {{$a}}", - "core.resourcedisplayopen": "Abrir", - "core.resources": "Recursos", - "core.restore": "Restaurar", - "core.restricted": "Restringido", - "core.retry": "Reintentar", - "core.save": "Guardar", - "core.savechanges": "Guardar cambios", - "core.scanqr": "Escanear código QR", - "core.search": "Buscar", - "core.searching": "Buscando", - "core.searchresults": "Resultados de la búsqueda", - "core.sec": "segundos", - "core.secs": "segundos", - "core.seemoredetail": "Haga clic aquí para ver más detalles", - "core.selectacategory": "Por favor, seleccione una categoría", - "core.selectacourse": "Seleccionar un curso", - "core.selectagroup": "Seleccionar un grupo", - "core.send": "Enviar", - "core.sending": "Enviando", - "core.serverconnection": "Error al conectarse al servidor", - "core.settings.about": "Acerca de", - "core.settings.appsettings": "Configuración de la aplicación", - "core.settings.appversion": "Versión de la aplicación", - "core.settings.cannotsyncoffline": "No puede sincronizarse fuera de línea.", - "core.settings.cannotsyncwithoutwifi": "No puede sincronizarse porque las configuraciones actuales solamente permiten sincronizar cuando está conectado a Wi-Fi. Por favor, conéctese a una red Wi-Fi.", - "core.settings.colorscheme": "Esquema de colores", - "core.settings.colorscheme-auto": "Automático (basada en la configuración del sistema)", - "core.settings.colorscheme-dark": "Oscuro", - "core.settings.colorscheme-light": "Claro", - "core.settings.compilationinfo": "Información de compilación", - "core.settings.copyinfo": "Copiar información del dispositivo al portapapeles", - "core.settings.cordovadevicemodel": "Modelo de dispositivo Cordova", - "core.settings.cordovadeviceosversion": "Versión de OS de dispositivo Cordova", - "core.settings.cordovadeviceplatform": "Plataforma de dispositivo Cordova", - "core.settings.cordovadeviceuuid": "uuid del dispositivo Cordova", - "core.settings.cordovaversion": "Versión Cordova", - "core.settings.currentlanguage": "Idioma actual", - "core.settings.debugdisplay": "Mostrar mensajes de depuración", - "core.settings.debugdisplaydescription": "Si habilitado, las ventanas modales de error mostrarán más datos si es posible.", - "core.settings.deletesitefiles": "¿Está seguro de que desea borrar todos los archivos descargados de este sitio'{{sitename}}'? No podrá usar esta aplicación sin estar conectado al internet.", - "core.settings.deletesitefilestitle": "Elimina los ficheros del sitio", - "core.settings.deviceinfo": "Información del dispositivo", - "core.settings.deviceos": "OS del dispositivo", - "core.settings.disableall": "Desactivar las notificaciones", - "core.settings.disabled": "Desactivado", - "core.settings.displayformat": "Formato de visualización", - "core.settings.enabledownloadsection": "Habilitar la descarga de secciones", - "core.settings.enablefirebaseanalytics": "Habilitar Análisis de Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Si se configura, esta aplicación recogerá el uso de datos anónimamente.", - "core.settings.enablerichtexteditor": "Activar editor de texto enriquecido", - "core.settings.enablerichtexteditordescription": "Si se habilita, se mostrará un editor de texto enriquecido en los lugares que lo permitan.", - "core.settings.enablesyncwifi": "Permitir la sincronización solo con la WiFi activada", - "core.settings.entriesincache": "{{$a}} entradas en caché", - "core.settings.errordeletesitefiles": "Se ha producido un error eliminando los ficheros del sitio.", - "core.settings.errorsyncsite": "Se ha producido un error sincronizando los datos del sitio, por favor compruebe su conexión a internet y pruebe de nuevo.", - "core.settings.estimatedfreespace": "Espacio libre (estimado)", - "core.settings.filesystemroot": "Raíz del sistema de archivos", - "core.settings.fontsize": "Tamaño del texto", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "Esta opción ha sido forzada en la configuración de su sitio.", - "core.settings.general": "General", - "core.settings.language": "Idioma", - "core.settings.license": "Licencia", - "core.settings.localnotifavailable": "Notificaciones locales disponibles", - "core.settings.locationhref": "Webview URL", - "core.settings.locked": "Bloqueado", - "core.settings.loggedin": "En línea", - "core.settings.loggedoff": "Desconectado", - "core.settings.navigatorlanguage": "Lenguaje del navegador", - "core.settings.navigatoruseragent": "Agente de usuario del navegador", - "core.settings.networkstatus": "Estado de la conexión a internet", - "core.settings.opensourcelicenses": "Licencias de Código Abierto", - "core.settings.preferences": "Preferencias", - "core.settings.privacypolicy": "Política de privacidad.", - "core.settings.publisher": "Editor", - "core.settings.pushid": "Identificador de notificaciones Push", - "core.settings.reportinbackground": "Informar de los errores automáticamente", - "core.settings.screen": "Información de pantalla", - "core.settings.settings": "Configuración", - "core.settings.showdownloadoptions": "Mostrar opciones de descarga", - "core.settings.siteinfo": "Información del sitio", - "core.settings.sites": "Sitios", - "core.settings.spaceusage": "Espacio", - "core.settings.spaceusagehelp": "Al borrar la información guardada del sitio se borrará toda la información disponible fuera de línea. Esta información le permite usar la aplicación cuando no está conectado a Internet.", - "core.settings.synchronization": "Sincronización", - "core.settings.synchronizenow": "Sincronizar ahora", - "core.settings.synchronizenowhelp": "Al sincronizar un sitio se enviarán todos los cambios y actividad que realizó fuera de línea en el dispositivo, también sincronizará información como mensajes y notificaciones.", - "core.settings.syncsettings": "Configuración de la sincronización", - "core.settings.total": "Total", - "core.settings.wificonnection": "Conexión WiFi", - "core.sharedfiles.chooseaccountstorefile": "Elija una cuenta en la cual almacenar el archivo.", - "core.sharedfiles.chooseactionrepeatedfile": "Ya existe un archivo con este nombre. ¿Desea usted remplazar el archivo existente o renombrarlo a \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "No hay sitios almacenados. Por favor, añada un sitio antes de compartir un archivo con la aplicación.", - "core.sharedfiles.nosharedfiles": "No hay archivos compartidos en este sitio.", - "core.sharedfiles.nosharedfilestoupload": "No tiene archivos para subir aquí. Si desea subir un archivo desde otra aplicación, localice ese archivo y haga clic en el botón para 'Abrir en'.", - "core.sharedfiles.rename": "Renombrar", - "core.sharedfiles.replace": "Reemplazar", - "core.sharedfiles.sharedfiles": "Archivos compartidos", - "core.sharedfiles.successstorefile": "Archivo almacenado correctamente. Ahora puede seleccionar este archivo para subirlo a sus archivos privados, o adjuntarlo a algunas actividades.", - "core.show": "Mostrar", - "core.showless": "Ver menos...", - "core.showmore": "Mostrar más...", - "core.site": "Sitio", - "core.sitehome.sitehome": "Inicio del sitio", - "core.sitehome.sitenews": "Anuncios de la página", - "core.sitemaintenance": "Este sitio está en fase de mantenimiento y no está disponible en este momento", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Omitir", - "core.sorry": "Disculpe...", - "core.sort": "Ordenar", - "core.sortby": "Ordenar por", - "core.start": "Comenzar", - "core.storingfiles": "Almacenando archivos", - "core.strftimedate": "%d de %B de %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d de %B", - "core.strftimedatetime": "%d de %B de %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d de %B de %Y", - "core.strftimedaydatetime": "%A, %d de %B de %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d de %b, %H:%M", - "core.strftimerecentfull": "%a, %d de %b de %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Enviar", - "core.success": "Éxito", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Colección por defecto", - "core.tag.errorareanotsupported": "Esta etiqueta de area no está permitida por esta aplicación.", - "core.tag.inalltagcoll": "En todas partes", - "core.tag.noresultsfor": "No hay resultados para \"{{$a}}\"", - "core.tag.searchtags": "Buscar marcas", - "core.tag.showingfirsttags": "Mostrando las {{$a}} marcas más populares", - "core.tag.tag": "Marca", - "core.tag.tagarea_course": "Cursos", - "core.tag.tagarea_course_modules": "Actividades y recursos", - "core.tag.tagarea_post": "Publicaciones de blog", - "core.tag.tagarea_user": "Intereses del usuario", - "core.tag.tags": "Marcas", - "core.tag.warningareasnotsupported": "Algunas de las etiquetas de areas no se muestran porque no están permitidas en esta aplicación.", - "core.teachers": "Profesores", - "core.thereisdatatosync": "Hay {{$a}} fuera de línea pendiente de ser sincronizado.", - "core.thisdirection": "ltr", - "core.time": "Hora", - "core.timesup": "¡Se ha pasado el tiempo!", - "core.today": "Hoy", - "core.tryagain": "Intentar de nuevo", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "¡Oh oh!", - "core.unexpectederror": "Error inesperado. Por favor cierre y vuelva a abrir la aplicación para intentarlo de nuevo", - "core.unicodenotsupported": "Los emojis no están soportado en este sitio; esos caracteres serán quitados cuando el mensaje sea enviado.", - "core.unicodenotsupportedcleanerror": "Se encontró texto vacío al limpiar caracteres Unicode.", - "core.unknown": "Desconocido", - "core.unlimited": "Sin límite", - "core.unzipping": "Descomprimiendo", - "core.updaterequired": "Actualización de la aplicación requerida", - "core.updaterequireddesc": "Por favor actualice la aplicación a la versión {{$a}}", - "core.upgraderunning": "El sitio está siendo actualizado, por favor inténtelo de nuevo más tarde.", - "core.user": "Usuario", - "core.user.address": "Dirección", - "core.user.city": "Ciudad", - "core.user.contact": "Contacto", - "core.user.country": "País", - "core.user.description": "Descripción", - "core.user.details": "Detalles", - "core.user.detailsnotavailable": "No tiene acceso a los detalles de este usuario.", - "core.user.editingteacher": "Profesor", - "core.user.email": "Dirección de correo", - "core.user.emailagain": "Correo (de nuevo)", - "core.user.errorloaduser": "Error cargando el usuario.", - "core.user.firstname": "Nombre", - "core.user.interests": "Intereses", - "core.user.lastname": "Apellido(s)", - "core.user.manager": "Gestor", - "core.user.newpicture": "Imagen nueva", - "core.user.noparticipants": "No se encontraron participantes en este curso", - "core.user.participants": "Participantes", - "core.user.phone1": "Teléfono", - "core.user.phone2": "Teléfono móvil", - "core.user.roles": "Roles", - "core.user.sendemail": "Correo electrónico", - "core.user.student": "Estudiante", - "core.user.teacher": "Profesor sin permiso de edición", - "core.user.webpage": "Página web", - "core.userdeleted": "Esta cuenta se ha cancelado", - "core.userdetails": "Detalles de usuario", - "core.usernotfullysetup": "El usuario no está completamente configurado", - "core.users": "Usuarios", - "core.view": "Vista", - "core.viewcode": "Ver código", - "core.vieweditor": "Ver editor", - "core.viewembeddedcontent": "Ver contenido embebido.", - "core.viewprofile": "Ver perfil", - "core.warningofflinedatadeleted": "Los datos fuera de línea de {{component}} '{{name}}' han sido borrados. {{error}}", - "core.whatisyourage": "¿Qué edad tiene?", - "core.wheredoyoulive": "¿En qué país vive?", - "core.whoissiteadmin": "\"El administrador del sitio\" es el encargado de la gestión de Moodle en su escuela/Universidad/empresa. Si desconoce como contactarle, por favor, contacte con su profesor.", - "core.whoops": "Oops!", - "core.whyisthishappening": "¿Porqué está pasando esto?", - "core.whyisthisrequired": "¿Por qué es necesario esto?", - "core.wsfunctionnotavailable": "La función de webservice no está disponible.", - "core.year": "año", - "core.years": "años", - "core.yes": "Sí", - "core.youreoffline": "Está desconectado", - "core.youreonline": "Conectado de nuevo" -} \ No newline at end of file diff --git a/src/assets/lang/eu.json b/src/assets/lang/eu.json deleted file mode 100644 index bb78e43d0..000000000 --- a/src/assets/lang/eu.json +++ /dev/null @@ -1,2175 +0,0 @@ -{ - "addon.badges.alignment": "Lerrokatzea", - "addon.badges.badgedetails": "Dominaren xehetasunak", - "addon.badges.badges": "Dominak", - "addon.badges.bendorsement": "Onarpena", - "addon.badges.claimcomment": "Onarpenaren iruzkina", - "addon.badges.claimid": "Erreklamatzeko URLa", - "addon.badges.contact": "Kontaktua", - "addon.badges.dateawarded": "Emate-data", - "addon.badges.expired": "Iraungita", - "addon.badges.expirydate": "Epemugaren data", - "addon.badges.imageauthoremail": "Irudiaren egilearen helbide elektronikoa", - "addon.badges.imageauthorname": "Irudiaren egilearen izena", - "addon.badges.imageauthorurl": "Irudiaren egilearen URLa", - "addon.badges.imagecaption": "Irudi-argazkia", - "addon.badges.issuancedetails": "Dominaren iraungitzea", - "addon.badges.issuerdetails": "Emailearen xehetasunak", - "addon.badges.issueremail": "Helbide elektronikoa", - "addon.badges.issuername": "Emailearen izena", - "addon.badges.issuerurl": "Emailearen URLa", - "addon.badges.language": "Hizkuntza", - "addon.badges.noalignment": "Dominak ez du kanpoko trebetasun edo estandarrik ezarrita.", - "addon.badges.nobadges": "Ez dago dominarik eskuragarri.", - "addon.badges.norelated": "Dominak ez du erlazionatutako beste dominarik.", - "addon.badges.recipientdetails": "Jasotzailearen zehaztasunak", - "addon.badges.relatedbages": "Erlazionatutako dominak", - "addon.badges.version": "Bertsioa", - "addon.badges.warnexpired": "(Domina hau iraungita dago!)", - "addon.block_activitymodules.pluginname": "Jarduerak", - "addon.block_activityresults.pluginname": "Jardueraren emaitzak", - "addon.block_badges.pluginname": "Azken dominak", - "addon.block_blogmenu.pluginname": "Blog-menua", - "addon.block_blogrecent.pluginname": "Azken aldiko blog-sarrerak", - "addon.block_blogtags.pluginname": "Blog-etiketak", - "addon.block_calendarmonth.pluginname": "Egutegia", - "addon.block_calendarupcoming.pluginname": "Datozen ekitaldiak", - "addon.block_comments.pluginname": "Iruzkinak", - "addon.block_completionstatus.pluginname": "Ikastaro-osaketaren egoera", - "addon.block_glossaryrandom.pluginname": "Glosarioko ausazko sarrera", - "addon.block_learningplans.pluginname": "Ikasketa-planak", - "addon.block_myoverview.all": "Guztiak (ikuspegitik kendutakoak izan ezik)", - "addon.block_myoverview.allincludinghidden": "Guztiak", - "addon.block_myoverview.favourites": "Nabarmenduta", - "addon.block_myoverview.future": "Etorkizunean", - "addon.block_myoverview.hiddencourses": "Ikuspegitik kendutakoak", - "addon.block_myoverview.inprogress": "Martxan", - "addon.block_myoverview.lastaccessed": "Azken sarrera", - "addon.block_myoverview.morecourses": "Ikastaro gehiago", - "addon.block_myoverview.nocourses": "Ez dago ikastarorik", - "addon.block_myoverview.past": "Iraganean", - "addon.block_myoverview.pluginname": "Ikuspegi orokorra", - "addon.block_myoverview.shortname": "Izen laburra", - "addon.block_myoverview.title": "Ikastaroaren izena", - "addon.block_newsitems.pluginname": "Azken berriak", - "addon.block_onlineusers.pluginname": "On-line erabiltzaileak", - "addon.block_privatefiles.pluginname": "Fitxategi pribatuak", - "addon.block_recentactivity.pluginname": "Duela gutxiko aktibitatea", - "addon.block_recentlyaccessedcourses.nocourses": "Ez dago duela gutxiko ikastarorik", - "addon.block_recentlyaccessedcourses.pluginname": "Duela gutxi bisitatutako ikastaroak", - "addon.block_recentlyaccesseditems.noitems": "Ez dago duela gutxiko elementurik", - "addon.block_recentlyaccesseditems.pluginname": "Duela gutxi bisitatutako elementuak", - "addon.block_rssclient.pluginname": "Urrutiko RSS jarioak", - "addon.block_selfcompletion.pluginname": "Auto-osaketa", - "addon.block_sitemainmenu.pluginname": "Menu nagusia", - "addon.block_starredcourses.nocourses": "Ez dago nabarmendutako ikastarorik", - "addon.block_starredcourses.pluginname": "Nabarmendutako ikastaroak", - "addon.block_tags.pluginname": "Etiketak", - "addon.block_timeline.duedate": "Entregatze-data", - "addon.block_timeline.next30days": "Datozen 30 egunetan", - "addon.block_timeline.next3months": "Datozen 3 hilabetetan", - "addon.block_timeline.next6months": "Datozen 6 hilabetetan", - "addon.block_timeline.next7days": "Datozen 7 egunetan", - "addon.block_timeline.nocoursesinprogress": "Ez dago ikastarorik martxa", - "addon.block_timeline.noevents": "Ez da jarduerarik programatu", - "addon.block_timeline.overdue": "Atzeratuta", - "addon.block_timeline.pluginname": "Kronologia", - "addon.block_timeline.sortbycourses": "Ordenatu ikastaroen arabera", - "addon.block_timeline.sortbydates": "Ordenatu daten arabera", - "addon.blog.blog": "Bloga", - "addon.blog.blogentries": "Blog-sarrerak", - "addon.blog.errorloadentries": "Errorea blogeko sarrerak kargatzean.", - "addon.blog.linktooriginalentry": "Jatorrizko blog-sarrerarako esteka", - "addon.blog.noentriesyet": "Sarrerak ez daude ikusgai hemen", - "addon.blog.publishtonoone": "Zuretzat soilik (zirriborroa)", - "addon.blog.publishtosite": "Gune honetako guztientzat", - "addon.blog.publishtoworld": "Mundu osoarentzat", - "addon.blog.showonlyyourentries": "Zure sarrerak soilik erakutsi", - "addon.blog.siteblogheading": "Guneko bloga", - "addon.calendar.allday": "Egun guztiak", - "addon.calendar.calendar": "Egutegia", - "addon.calendar.calendarevent": "Egutegiko ekitaldia", - "addon.calendar.calendarevents": "Egutegiko ekitaldiak", - "addon.calendar.calendarreminders": "Egutegiko gogorarazpenak", - "addon.calendar.categoryevents": "Kategoria-mailako ekitaldiak", - "addon.calendar.confirmeventdelete": "Ziur zaude \"{{$a}}\" ekitaldia ezabatu nahi duzula?", - "addon.calendar.confirmeventseriesdelete": "\"{{$a.name}}\" ekitaldia ekitaldi-serie batekoa da. Soilik ekitadi hau ezabatu nahi duzu edo ekitaldi-serieko guztiak ezabatu nahi dituzu ( {{$a.count}} guztira)?", - "addon.calendar.courseevents": "Ikastaroko ekitaldiak", - "addon.calendar.currentmonth": "Hilabete honetan", - "addon.calendar.daynext": "Hurrengo eguna", - "addon.calendar.dayprev": "Aurreko eguna", - "addon.calendar.defaultnotificationtime": "Berezko jakinarazpen-ordua", - "addon.calendar.deleteallevents": "Ezabatu ekitaldi guztiak", - "addon.calendar.deleteevent": "Ezabatu ekitaldia", - "addon.calendar.deleteoneevent": "Ezabatu ekitaldi hau", - "addon.calendar.durationminutes": "Iraupena minututan", - "addon.calendar.durationnone": "Iraupenik ez", - "addon.calendar.durationuntil": "Noiz arte", - "addon.calendar.editevent": "Ekitaldia editatzen", - "addon.calendar.errorloadevent": "Errorea gertakaria kargatzean.", - "addon.calendar.errorloadevents": "Errorea gertakariak kargatzean.", - "addon.calendar.eventcalendareventdeleted": "Egutegi-ekitaldia ezabatu da", - "addon.calendar.eventduration": "Iraupena", - "addon.calendar.eventendtime": "Amaiera-ordua", - "addon.calendar.eventkind": "Ekitaldi mota", - "addon.calendar.eventname": "Ekitaldiaren izenburua", - "addon.calendar.eventstarttime": "Hasiera-ordua", - "addon.calendar.eventtype": "Ekitaldi-mota", - "addon.calendar.fri": "Or", - "addon.calendar.friday": "Ostirala", - "addon.calendar.gotoactivity": "Joan jarduerara", - "addon.calendar.groupevents": "Talde-ekitaldiak", - "addon.calendar.invalidtimedurationminutes": "Minututan sartu duzun iraupena ez da egokia. Mesedez, sartu 0 baino handiagoko iraupen egoki bat minututan edo aukeratu \"iraupenik ez\".", - "addon.calendar.invalidtimedurationuntil": "Iraupenerako aukeratu duzun data eta ordua ekitaldiaren hasiera-ordua baina aurrekoa da. Mesedez zuzendu hau aurrera egin aurretik.", - "addon.calendar.mon": "Al", - "addon.calendar.monday": "Astelehena", - "addon.calendar.monthlyview": "Hileko ikuspegia", - "addon.calendar.newevent": "Ekitaldi berria", - "addon.calendar.noevents": "Ez dago ekitaldirik", - "addon.calendar.nopermissiontoupdatecalendar": "Sentitzen dugu, baina ez duzu egutegiko ekitaldia eguneratzeko baimenik.", - "addon.calendar.reminders": "Gogorarazpenak", - "addon.calendar.repeatedevents": "Errepikatutako ekitaldiak", - "addon.calendar.repeateditall": "Aplikatu aldaketak {{$a}} ekitaldiaren errepikapen-serieari", - "addon.calendar.repeateditthis": "Aplikatu aldaketak soilik ekitaldi honi", - "addon.calendar.repeatevent": "Errepikatu ekitaldi hau", - "addon.calendar.repeatweeksl": "Errepikatu astero, denak batera sortuz", - "addon.calendar.sat": "Lr", - "addon.calendar.saturday": "Larunbata", - "addon.calendar.setnewreminder": "Zehaztu gogorarazpen berri bat", - "addon.calendar.siteevents": "Guneko ekitaldiak", - "addon.calendar.sun": "Ig", - "addon.calendar.sunday": "Igandea", - "addon.calendar.thu": "Og", - "addon.calendar.thursday": "Osteguna", - "addon.calendar.today": "Gaur", - "addon.calendar.tomorrow": "Bihar", - "addon.calendar.tue": "Ar", - "addon.calendar.tuesday": "Asteartea", - "addon.calendar.typecategory": "Kategoria-mailako ekitaldia", - "addon.calendar.typeclose": "Itxi ekitaldia", - "addon.calendar.typecourse": "Ikastaroko ekitaldia", - "addon.calendar.typedue": "Ekitaldiaren amaiera-data", - "addon.calendar.typegradingdue": "Ekitaldiaren kalifikatze-data", - "addon.calendar.typegroup": "Taldeko ekitaldia", - "addon.calendar.typeopen": "Ireki ekitaldia", - "addon.calendar.typesite": "Webguneko ekitaldia", - "addon.calendar.typeuser": "Erabiltzailearen ekitaldia", - "addon.calendar.upcomingevents": "Datozen ekitaldiak", - "addon.calendar.userevents": "Erabiltzailearen ekitaldiak", - "addon.calendar.wed": "Az", - "addon.calendar.wednesday": "Asteazkena", - "addon.calendar.when": "Noiz", - "addon.calendar.yesterday": "Atzo", - "addon.competency.activities": "Jarduerak", - "addon.competency.competencies": "Konpetentziak", - "addon.competency.competenciesmostoftennotproficientincourse": "Ikastaro honetan sarriago ez-gai diren konpetentziak", - "addon.competency.coursecompetencies": "Ikastaroko konpetentziak", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Ikastaro honetako konpetentzien kalifikazioek ez dute ikasketa-planean eragiten.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Ikastaro honetan konpetentzien kalifikazioek ikasketa-planak berehala eguneratzen dituzte.", - "addon.competency.crossreferencedcompetencies": "Erreferentzia gurutzatuko konpetentziak", - "addon.competency.duedate": "Entregatze-data", - "addon.competency.errornocompetenciesfound": "Ez da gaitasunik aurkitu", - "addon.competency.evidence": "Ebidentzia", - "addon.competency.evidence_competencyrule": "Konpetentzia-araua bete da.", - "addon.competency.evidence_coursecompleted": "'{{$a}}' ikastaroa osatu da.", - "addon.competency.evidence_coursemodulecompleted": "'{{$a}}' jarduera osatu da.", - "addon.competency.evidence_courserestored": "Puntuazioak '{{$a}}' ikastaroarekin batera berreskuratu ziren.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Aurretik ikasitakoaren '{{$a}}' ebidentzia estekatu da.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Aurretik ikasitakoaren '{{$a}}' ebidentziaren esteka kendu da.", - "addon.competency.evidence_manualoverride": "Konpetentzien puntuazioa eskuz ezarri da.", - "addon.competency.evidence_manualoverrideincourse": "Konpetentzien puntuazioa eskuz ezarri da '{{$a}}' ikastaroan.", - "addon.competency.evidence_manualoverrideinplan": "Konpetentzien puntuazioa eskuz ezarri da '{{$a}}' ikasketa-planean.", - "addon.competency.learningplancompetencies": "Ikasketa-planaren konpetentziak", - "addon.competency.learningplans": "Ikasketa-planak", - "addon.competency.myplans": "Nire ikasketa-planak", - "addon.competency.noactivities": "Ez dago jarduerarik", - "addon.competency.nocompetencies": "Gaitasunik ez", - "addon.competency.nocompetenciesincourse": "Ez da konpetentziarik estekatu ikastaro honetara.", - "addon.competency.nocrossreferencedcompetencies": "Ez dago konpetentzia honekiko erreferentzia gurutzatua duen beste konpetentziarik.", - "addon.competency.noevidence": "Ez dago ebidentziarik", - "addon.competency.noplanswerecreated": "Ez da ikasketa-planik sortu.", - "addon.competency.nouserplanswithcompetency": "Ez dago konpetentzia hau barne duen ikasketa-planik.", - "addon.competency.path": "Bidea:", - "addon.competency.planstatusactive": "Aktiboa", - "addon.competency.planstatuscomplete": "Osatu", - "addon.competency.planstatusdraft": "Zirriborroa", - "addon.competency.planstatusinreview": "Berrikusten", - "addon.competency.planstatuswaitingforreview": "Berrikusketaren zain", - "addon.competency.proficient": "Gai", - "addon.competency.progress": "Aurrerapena", - "addon.competency.rating": "Puntuazioa", - "addon.competency.reviewstatus": "Berrikusi egora", - "addon.competency.status": "Egoera", - "addon.competency.template": "Ikasketa-planerako txantiloia", - "addon.competency.uponcoursecompletion": "Ikastaroaren osaketari buruz:", - "addon.competency.usercompetencystatus_idle": "Ez dago aktiboa", - "addon.competency.usercompetencystatus_inreview": "Berrikusten", - "addon.competency.usercompetencystatus_waitingforreview": "Berrikusketaren zain", - "addon.competency.userplans": "Ikasketa-planak", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.y}} konpetentziatik {{$a.x}} gai dira", - "addon.competency.xcompetenciesproficientoutofyincourse": "Ikastaro honetako {{$a.y}} konpetentziatik {{$a.x}}-tan zara gai.", - "addon.coursecompletion.complete": "Osoa", - "addon.coursecompletion.completecourse": "Osatu ikastaroa", - "addon.coursecompletion.completed": "Osatuta", - "addon.coursecompletion.completiondate": "Osaketa-data", - "addon.coursecompletion.completionmenuitem": "Osaketa", - "addon.coursecompletion.couldnotloadreport": "Ezin izan da ikastaro-osaketaren txostena kargatu. Mesedez saiatu beranduago.", - "addon.coursecompletion.coursecompletion": "Ikastaro-osaketa", - "addon.coursecompletion.criteria": "Irizpidea", - "addon.coursecompletion.criteriagroup": "Irizpide-multzoa", - "addon.coursecompletion.criteriarequiredall": "Beheko irizpide guztiak dira beharrezko", - "addon.coursecompletion.criteriarequiredany": "Beheko hainbat irizpide dira beharrezko", - "addon.coursecompletion.inprogress": "Ari da", - "addon.coursecompletion.manualselfcompletion": "Norberak eskuz osatu", - "addon.coursecompletion.nottracked": "Ez zaizu ikastaro honetako jarraipenik egiten ari une honetan.", - "addon.coursecompletion.notyetstarted": "Ez da hasi", - "addon.coursecompletion.pending": "Egin gabe", - "addon.coursecompletion.required": "Beharrezkoa", - "addon.coursecompletion.requiredcriteria": "Irizpidea behar da", - "addon.coursecompletion.requirement": "Betekizuna", - "addon.coursecompletion.status": "Egoera", - "addon.coursecompletion.viewcoursereport": "Ikusi ikastaroaren txostena", - "addon.files.couldnotloadfiles": "Ezin izan da fitxategien zerrenda kargatu.", - "addon.files.emptyfilelist": "Ez dago fitxategirik erakusteko.", - "addon.files.erroruploadnotworking": "Zoritxarrez une honetan ezin dira fitxategiak zure gunera igo.", - "addon.files.files": "Fitxategiak", - "addon.files.privatefiles": "Fitxategi pribatuak", - "addon.files.sitefiles": "Guneko fitxategiak", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Gailuak konfiguratu", - "addon.messages.acceptandaddcontact": "Onartu eta gehitu kontaktuetara", - "addon.messages.addcontact": "Gehitu kontaktua", - "addon.messages.addcontactconfirm": "Ziur zaude {{$a}} gehitu nahi duzula zure kontaktuetara?", - "addon.messages.addtofavourites": "Esleitu izarra elkarrizketari", - "addon.messages.addtoyourcontacts": "Gehitu kontaktuetara", - "addon.messages.blocknoncontacts": "Galarazi niri mezuak bidaltzea kontaktu ez direnei", - "addon.messages.blockuser": "Blokeatu erabiltzailea", - "addon.messages.blockuserconfirm": "Ziur zaude {{$a}} blokeatu nahi duzula?", - "addon.messages.contactableprivacy": "Onartu hauen mezuak:", - "addon.messages.contactableprivacy_coursemember": "Nire kontaktuek eta nire ikastaroetako edonork", - "addon.messages.contactableprivacy_onlycontacts": "Nire kontaktuek soilik", - "addon.messages.contactableprivacy_site": "Guneko edonor", - "addon.messages.contactblocked": "Kontaktua blokeatu da", - "addon.messages.contactlistempty": "Kontaktu zerrenda hutsik dago", - "addon.messages.contactname": "Kontaktuaren izena", - "addon.messages.contactrequestsent": "Kontaktu-eskaria bidali da", - "addon.messages.contacts": "Kontaktuak", - "addon.messages.conversationactions": "Elkarrizketarako ekintza-menua", - "addon.messages.decline": "Ez onartu", - "addon.messages.deleteallconfirm": "Ziur zaude elkarrizketa oso hau ezabatu nahi duzula? Honek ez die elkarrizketa beste partaideei ezabatuko.", - "addon.messages.deleteallselfconfirm": "Ziur zaude elkarrizketa pertsonal hau guztiz ezabatu nahi duzula?", - "addon.messages.deleteconversation": "Ezabatu elkarrizketa", - "addon.messages.deleteforeveryone": "Ezabatu niretzako eta beste guztientzako", - "addon.messages.deletemessage": "Ezabatu mezua", - "addon.messages.deletemessageconfirmation": "Ziur zaude mezu hau ezabatu nahi duzula? Mezua zure mezuen historiatik baino ez da ezabatuko eta bidali edo jaso zuten beste erabiltzaileek oraindik ikusgai izango dute.", - "addon.messages.errordeletemessage": "Errorea mezua ezabatzean.", - "addon.messages.errorwhileretrievingcontacts": "Errore bat gertatu da kontaktuak zerbitzaritik jasotzean.", - "addon.messages.errorwhileretrievingdiscussions": "Errore bat gertatu da elkarrizketak zerbitzaritik jasotzean.", - "addon.messages.errorwhileretrievingmessages": "Errore bat gertatu da mezuak zerbitzaritik jasotzean.", - "addon.messages.errorwhileretrievingusers": "Errorea erabiltzaileak zerbitzaritik jasotzerakoan", - "addon.messages.groupconversations": "Taldea", - "addon.messages.groupinfo": "Taldearen informazioa", - "addon.messages.individualconversations": "Pribatua", - "addon.messages.info": "Erabiltzailearen informazioa", - "addon.messages.isnotinyourcontacts": "{{$a}} ez dago zure kontaktuetan", - "addon.messages.message": "Mezua", - "addon.messages.messagenotsent": "Mezua ez da bidali. Mesedez, saiatu beranduago.", - "addon.messages.messagepreferences": "Mezuen hobespenak", - "addon.messages.messages": "Mezuak", - "addon.messages.muteconversation": "Isilarazi", - "addon.messages.mutedconversation": "Mutututako eztabaida", - "addon.messages.newmessage": "Mezu berria", - "addon.messages.newmessages": "Mezu beriak", - "addon.messages.nocontactrequests": "Ez dago kontaktu-eskaririk", - "addon.messages.nocontactsgetstarted": "Ez dago kontakturik", - "addon.messages.nofavourites": "Ez dago nabarmendutako elkarrizketarik", - "addon.messages.nogroupconversations": "Taldeko elkarrizketarik ez", - "addon.messages.noindividualconversations": "Elkarrizketa pribaturik ez", - "addon.messages.nomessagesfound": "Ez da mezurik aurkitu", - "addon.messages.noncontacts": "Kontaktu ez direnak", - "addon.messages.nousersfound": "Ez da erabiltzailerik aurkitu", - "addon.messages.numparticipants": "{{$a}} partaide", - "addon.messages.removecontact": "Ezabatu kontaktua", - "addon.messages.removecontactconfirm": "Ziur zaude {{$a}} kendu nahi duzula zure kontaktuetatik?", - "addon.messages.removefromfavourites": "Kendu izarra elkarrizketari", - "addon.messages.removefromyourcontacts": "Ezabatu kontaktuetatik", - "addon.messages.requests": "Eskariak", - "addon.messages.requirecontacttomessage": "{{$a}}-(r)i egin behar diozu kontaktu-eskaria mezua bidali ahal izateko.", - "addon.messages.searchcombined": "Jendea eta mezuak bilatu", - "addon.messages.selfconversation": "Gune pertsonala", - "addon.messages.selfconversationdefaultmessage": "Gorde mezuen zirriborroak, estekak, oharrak... beranduago eskuratzeko", - "addon.messages.sendcontactrequest": "Bidali kontaktu-eskaria", - "addon.messages.showdeletemessages": "Erakutsi ezabatutako mezuak", - "addon.messages.type_blocked": "Blokeatuta", - "addon.messages.type_offline": "Lineaz kanpo", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Bilaketaren emaitzak", - "addon.messages.type_strangers": "Beste batzuk", - "addon.messages.unabletomessage": "Erabiltzaile honi ezin diozu mezurik bidali", - "addon.messages.unblockuser": "Desblokeatu erabiltzailea", - "addon.messages.unblockuserconfirm": "Ziur zaude {{$a}} desblokeatu nahi duzula?", - "addon.messages.unmuteconversation": "Kendu isilarazpena", - "addon.messages.useentertosend": "Erabili ENTER bidaltzeko", - "addon.messages.useentertosenddescdesktop": "Desgaituz gero, Ctrl+Enter erabili dezakezu mezua bidaltzeko.", - "addon.messages.useentertosenddescmac": "Desgaituz gero, Cmd+Enter erabili dezakezu mezua bidaltzeko.", - "addon.messages.userwouldliketocontactyou": "{{$a}}-(e)k zurekin jarri nahi du harremanetan", - "addon.messages.warningconversationmessagenotsent": "Ezin izan d(ir)a mezua(k) {{conversation}} elkarrizketara bidali. {{error}}", - "addon.messages.warningmessagenotsent": "Ezin izan d(ir)a mezua(k) bidali {{user}} erabiltzaileari. {{error}}", - "addon.messages.wouldliketocontactyou": "Zurekin harremanetan jarri nahi luke", - "addon.messages.you": "Zu:", - "addon.messages.youhaveblockeduser": "Iraganean erabiltzaile hau blokeatu duzu", - "addon.messages.yourcontactrequestpending": "{{$a}}-(r)en kontaktua izateko eskaria zain dago", - "addon.mod_assign.acceptsubmissionstatement": "Mesedez onartu bidalketa-adierazpena.", - "addon.mod_assign.addattempt": "Baimendu beste saiakera bat", - "addon.mod_assign.addnewattempt": "Gehitu saiakera berria", - "addon.mod_assign.addnewattemptfromprevious": "Gehitu aurreko bidalketan oinarritutako saiakera berria", - "addon.mod_assign.addsubmission": "Gehitu bidalketa", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Zereginaren nondik norakoak eta bidalketarako formularioa eskuragarri izango dituzu {{$a}}-tik aurrera", - "addon.mod_assign.allowsubmissionsfromdate": "Bidalketetarako hasiera-data", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Zeregin honetan bidalketak onartuko dira {{$a}}-tik aurrera", - "addon.mod_assign.applytoteam": "Aplikatu kalifikazioak eta feedbackak talde osoari", - "addon.mod_assign.assignmentisdue": "Epea amaitu da", - "addon.mod_assign.attemptnumber": "Saiakera-kopurua", - "addon.mod_assign.attemptreopenmethod": "Saiakerak berriro ireki dira", - "addon.mod_assign.attemptreopenmethod_manual": "Eskuz", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatikoki gainditu arte", - "addon.mod_assign.attemptsettings": "Saiakeren ezarpenak", - "addon.mod_assign.cannoteditduetostatementsubmission": "Ezin duzu bidalketa aplikazioan gehitu edo editatu ezin izan dugulako guneko bidalketa-adierazpena berreskuratu.", - "addon.mod_assign.cannotgradefromapp": "Kalifikazio-metodo batzuk oraindik ez daude aplikazioan onartuta eta ezin dira aldatu .", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Ezin duzu aplikazioan bidalketa bat egin ezin izan dugulako guneko bidalketa-adierazpena berreskuratu.", - "addon.mod_assign.confirmsubmission": "Ziur zaude zure lana bidali nahi duzula kalifikatzeko? Ezin izango duzu aldaketarik egin.", - "addon.mod_assign.currentattempt": "Hau da {{$a}} saiakera.", - "addon.mod_assign.currentattemptof": "Hau {{$a.attemptnumber}}. saiakera da ( {{$a.maxattempts}} saiakera onartzen dira ).", - "addon.mod_assign.currentgrade": "Oraingo kalifikazioa kalifikazio-liburuan", - "addon.mod_assign.cutoffdate": "Itxiera-data", - "addon.mod_assign.defaultteam": "Talde lehenetsia", - "addon.mod_assign.duedate": "Entregatze-data", - "addon.mod_assign.duedateno": "Entregatze-datarik ez", - "addon.mod_assign.duedatereached": "Zeregin hau bidaltzeko epea amaitu da", - "addon.mod_assign.editingstatus": "Editatze-egoera", - "addon.mod_assign.editsubmission": "Editatu bidalketa", - "addon.mod_assign.erroreditpluginsnotsupported": "Ezin duzu bidalketa aplikazioan gehitu edo editatu gehigarri batzuk ez dutelako editatzea onartzen.", - "addon.mod_assign.errorshowinginformation": "Ezin da bidalketaren informazioa erakutsi.", - "addon.mod_assign.extensionduedate": "Luzapenaren entregatze-data", - "addon.mod_assign.feedbacknotsupported": "Feedback hau ez da aplikazioan onartzen eta baliteke informazio guztia jasota ez egotea.", - "addon.mod_assign.grade": "Kalifikazioa", - "addon.mod_assign.graded": "Kalifikatua", - "addon.mod_assign.gradedby": "Nork kalifikatua", - "addon.mod_assign.gradedfollowupsubmit": "Kalifikatuta - bidalketaren jakinarazpena jaso da", - "addon.mod_assign.gradedon": "Noiz kalifikatua", - "addon.mod_assign.gradelocked": "Kalifikazio hau blokeatu edo aldatu da kalifikazio-liburuan.", - "addon.mod_assign.gradenotsynced": "Kalifikazioa ez da sinkronizatu", - "addon.mod_assign.gradeoutof": "Kalifikazioa (gehienezko {{$a}}-(e)tik)", - "addon.mod_assign.gradingstatus": "Kalifikazioaren egoera", - "addon.mod_assign.groupsubmissionsettings": "Taldeko bidalketen ezarpenak", - "addon.mod_assign.hiddenuser": "Partaidea", - "addon.mod_assign.latesubmissions": "Epez kanpoko bidalketak", - "addon.mod_assign.latesubmissionsaccepted": "Noiz arte baimendua: {{$a}}", - "addon.mod_assign.markingworkflowstate": "Kalifikazio lan-fluxuaren egoera", - "addon.mod_assign.markingworkflowstateinmarking": "Kalifikatzen", - "addon.mod_assign.markingworkflowstateinreview": "Berrikusten", - "addon.mod_assign.markingworkflowstatenotmarked": "Kalifikatu gabe", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Ikusgai jartzeko prest", - "addon.mod_assign.markingworkflowstatereadyforreview": "Kalifikazioa osatua", - "addon.mod_assign.markingworkflowstatereleased": "Ikusgai", - "addon.mod_assign.modulenameplural": "Zereginak", - "addon.mod_assign.multipleteams": "Talde bateko baino gehiagoko partaidea", - "addon.mod_assign.multipleteams_desc": "Zereginak taldekako bidalketa behartzen du. Zu talde bat baino gehiagoko kidea zara. Bidalketak egin ahal izateko talde bakar bateko kidea izan behar duzu. Mesedez jarri harremanetan zure irakaslearekin zure talde-partaidetzak aldatu ditzan.", - "addon.mod_assign.noattempt": "Saiakerarik ez", - "addon.mod_assign.nomoresubmissionsaccepted": "Soilik epearen luzapena jaso duten kideei baimenduta.", - "addon.mod_assign.noonlinesubmissions": "Zeregin honek ez du ezer on-line aurkezteko eskatzen", - "addon.mod_assign.nosubmission": "Ez dago bildalketarik zeregin honetan", - "addon.mod_assign.notallparticipantsareshown": "Bidalketarik egin ez duten ikasleak ez dira erakusten.", - "addon.mod_assign.noteam": "Ez zara inongo taldetako kide", - "addon.mod_assign.noteam_desc": "Zereginak taldekako bidalketa behartzen du. Zu ez zara inongo taldeko kidea, eta ondorioz ezin duzu bidalketarik egin. Mesedez jarri harremanetan zure irakaslearekin zure talde-partaidetzak aldatu ditzan.", - "addon.mod_assign.notgraded": "Kalifikatu gabea", - "addon.mod_assign.numberofdraftsubmissions": "Zirriborroak", - "addon.mod_assign.numberofparticipants": "Partaideak", - "addon.mod_assign.numberofsubmissionsneedgrading": "Kalifikatu beharrekoak", - "addon.mod_assign.numberofsubmittedassignments": "Bidalita", - "addon.mod_assign.numberofteams": "Taldeak", - "addon.mod_assign.numwords": "{{$a}} hitz", - "addon.mod_assign.outof": "{{$a.current}}.a {{$a.total}}-(e)tik", - "addon.mod_assign.overdue": "Zeregina bidaltzeko epea amaitu da orain dela {{$a}}", - "addon.mod_assign.submission": "Bidalketa", - "addon.mod_assign.submissioneditable": "Ikasleak bere bidalketa edita dezake", - "addon.mod_assign.submissionnoteditable": "Ikasleak ezin du editatu bidalketa hau", - "addon.mod_assign.submissionnotsupported": "Bidalketa hau ez da aplikazioan onartzen eta baliteke informazio guztia jasota ez egotea.", - "addon.mod_assign.submissionslocked": "Zeregin honek ez du bidalketarik onartzen", - "addon.mod_assign.submissionstatus": "Bidalketaren egoera", - "addon.mod_assign.submissionstatus_": "Ez dago bidalketarik", - "addon.mod_assign.submissionstatus_draft": "Zirriborroa (bidali gabea)", - "addon.mod_assign.submissionstatus_marked": "Kalifikatua", - "addon.mod_assign.submissionstatus_new": "Bidalketarik ez", - "addon.mod_assign.submissionstatus_reopened": "Berrirekita", - "addon.mod_assign.submissionstatus_submitted": "Kalifikatzeko bidalia", - "addon.mod_assign.submissionstatusheading": "Bidalketen egoera", - "addon.mod_assign.submissionteam": "Taldea", - "addon.mod_assign.submitassignment": "Bidali zeregina", - "addon.mod_assign.submitassignment_help": "Behin zeregina bidalita, ezin izango duzu aldaketarik egin.", - "addon.mod_assign.submittedearly": "Bidalketa epea amaitu baino {{$a}} lehenago bidali zen", - "addon.mod_assign.submittedlate": "Zeregina berandu bidali zen: {{$a}}", - "addon.mod_assign.timemodified": "Azken aldaketa", - "addon.mod_assign.timeremaining": "Geratzen den denbora", - "addon.mod_assign.ungroupedusers": "'Behartu taldeko kidea izatea bidalketa egiteko' ezarpena gaituta dago eta erabiltzaile batzuk ez dira inongo taldeko kide edo talde bat baino gehiagoko kideak dira, eta beraz erabiltzaile horiek ezingo dute bidalketarik egin.", - "addon.mod_assign.ungroupedusersoptional": "'Ikasleek taldeka bidaliko dute' ezarpena gaituta dago eta erabiltzaile batzuk ez dira ezein taldeko kide, edo talde anitzetako kideak dira. Kontuan izan ikasle hauek 'Talde lehenetsia'-ren kide gisa egingo dituztela bidalketak.", - "addon.mod_assign.unlimitedattempts": "Mugarik gabea", - "addon.mod_assign.userswhoneedtosubmit": "Bidalketa egin behar duten erabiltzaileak: {{$a}}", - "addon.mod_assign.userwithid": "{{id}} IDa duen erabiltzailea", - "addon.mod_assign.viewsubmission": "Ikusi bidalketa", - "addon.mod_assign.warningsubmissiongrademodified": "Bidalketaren kalifikazioa gunean aldatua izan da.", - "addon.mod_assign.warningsubmissionmodified": "Erabiltzailearen bidalketa gunean aldatua izan da.", - "addon.mod_assign.wordlimit": "Gehienezko hitz-kopurua", - "addon.mod_assign_feedback_comments.pluginname": "Feedback-iruzkinak", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF gaineko oharrak", - "addon.mod_assign_feedback_file.pluginname": "Feedback-fitxategia", - "addon.mod_assign_submission_comments.pluginname": "Bidalketaren iruzkinak", - "addon.mod_assign_submission_file.pluginname": "Fitxategi-bidalketak", - "addon.mod_assign_submission_onlinetext.pluginname": "On-line testuen bidalketa", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Zeregin honetarako gehienezko hitz-kopurua {{$a.limit}} da eta zu {{$a.count}} hitz bidaltzen saiatzen ari zara. Mesedez, alda ezazu zure bidalketa eta ondoren saiatu berriz.", - "addon.mod_book.errorchapter": "Errorea liburuaren atala irakurtzean.", - "addon.mod_book.modulenameplural": "Liburuak", - "addon.mod_book.navnexttitle": "Hurrengoa: {{$a}}", - "addon.mod_book.navprevtitle": "Aurrekoa: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Liburu-atalak", - "addon.mod_book.toc": "Eduki-taula", - "addon.mod_chat.beep": "Abisua", - "addon.mod_chat.chatreport": "Txat-saioetako txostenak", - "addon.mod_chat.currentusers": "Oraingo erabiltzaileak", - "addon.mod_chat.enterchat": "Egin klik hemen txat-gelara sartzeko", - "addon.mod_chat.entermessage": "Idatzi zure mezua", - "addon.mod_chat.errorwhileconnecting": "Errorea txatera konektatzean.", - "addon.mod_chat.errorwhilegettingchatdata": "Errorea txataren datuak eskuratzean.", - "addon.mod_chat.errorwhilegettingchatusers": "Errorea txataren erabiltzaileak eskuratzean.", - "addon.mod_chat.errorwhileretrievingmessages": "Errore bat gertatu da zerbitzaritik mezuak ekartzean.", - "addon.mod_chat.errorwhilesendingmessage": "Errorea mezua bidaltzean.", - "addon.mod_chat.messagebeepseveryone": "{{$a}}-(e)k dio: Aizue! Hemen nago!", - "addon.mod_chat.messagebeepsyou": "{{$a}}-(e)k dio: Aizu! Hemen nago!", - "addon.mod_chat.messageenter": "{{$a}} oraintxe sartu da gelan", - "addon.mod_chat.messageexit": "{{$a}} irten egin da gelatik", - "addon.mod_chat.messages": "Mezuak", - "addon.mod_chat.messageyoubeep": "Zure soinua: {{$a}}", - "addon.mod_chat.modulenameplural": "Txat-gelak", - "addon.mod_chat.mustbeonlinetosendmessages": "Online egon behar zara mezuak bidali ahal izateko.", - "addon.mod_chat.nomessages": "Ez dago mezurik oraindik", - "addon.mod_chat.nosessionsfound": "Ez da saiorik aurkitu", - "addon.mod_chat.saidto": "zera dio", - "addon.mod_chat.send": "Bidali", - "addon.mod_chat.sessionstart": "Txat-saioa {{$a.date}}-(e)tan hasiko da, (hemendik {{$a.fromnow}}-(e)ra)", - "addon.mod_chat.showincompletesessions": "Erakutsi osatu gabeko saioak", - "addon.mod_chat.talk": "Hitz egin", - "addon.mod_chat.viewreport": "Ikusi izan diren txat-saioak", - "addon.mod_choice.cannotsubmit": "Sentitzen dugu, arazoa gertatu da zure aukera bidaltzean. Mesedez, saiatu berriz.", - "addon.mod_choice.choiceoptions": "Kontsultaren aukerak", - "addon.mod_choice.errorgetchoice": "Errorea kontsultaren datuak eskuratzean.", - "addon.mod_choice.expired": "Jarduera hau {{$a}}(e)n itxi zen.", - "addon.mod_choice.full": "(Beteta)", - "addon.mod_choice.modulenameplural": "Kontsultak", - "addon.mod_choice.noresultsviewable": "Emaitzak ezin dira orain ikusi", - "addon.mod_choice.notopenyet": "Jarduera hau ez dago erabiltzeko moduan {{$a}} arte.", - "addon.mod_choice.numberofuser": "Erantzun-kopurua", - "addon.mod_choice.numberofuserinpercentage": "Erantzunen ehunekoa", - "addon.mod_choice.previewonly": "Hau jarduera honetan eskuragarri dauden aukeren aurrebista besterik ez da. Ezingo duzu zure erantzuna bidali {{$a}}-(e)ra arte.", - "addon.mod_choice.publishinfoanonafter": "Zuk erantzun ondoren erantzun anonimoak publikatuko dira.", - "addon.mod_choice.publishinfoanonclose": "Jarduera itxi ondoren erantzun anonimoak publikatuko dira.", - "addon.mod_choice.publishinfofullafter": "Zuk erantzun ondoren, emaitza osoak publikatuko dira, guztion erantzunak erakutsiz.", - "addon.mod_choice.publishinfofullclose": "Jarduera itxi ondoren, emaitza osoak publikatuko dira, guztion erantzunak erakutsiz.", - "addon.mod_choice.publishinfonever": "Zuk erantzun ondoren Jarduera honetako emaitzak ez dira publikatuko.", - "addon.mod_choice.removemychoice": "Ezabatu nire aukera", - "addon.mod_choice.responses": "Erantzunak", - "addon.mod_choice.responsesresultgraphdescription": "Erabiltzaileen %{{number}}-ak aukera hau aukeratu zuten: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Erakutsi grafikoa", - "addon.mod_choice.resultsnotsynced": "Zure azken erantzuna sinkronizatu behar da emaitzetan kontuan hartu ahal izateko.", - "addon.mod_choice.savemychoice": "Gorde nire aukera", - "addon.mod_choice.userchoosethisoption": "Aukera hau hautatu duten erabiltzaileak", - "addon.mod_choice.yourselection": "Zure aukera", - "addon.mod_data.addentries": "Gehitu sarrerak", - "addon.mod_data.advancedsearch": "Bilaketa aurreratua", - "addon.mod_data.alttext": "Ordezko testua", - "addon.mod_data.approve": "Onartu", - "addon.mod_data.approved": "Onartuta", - "addon.mod_data.ascending": "Goranzkoa", - "addon.mod_data.authorfirstname": "Egilearen izena", - "addon.mod_data.authorlastname": "Egilearen abizena", - "addon.mod_data.confirmdeleterecord": "Ziur zaude sarrera hau ezabatu nahi duzula?", - "addon.mod_data.descending": "Beheranzkoa", - "addon.mod_data.disapprove": "Desegin onarpena", - "addon.mod_data.edittagsnotsupported": "Sentitzen dugu, etiketak ezin dira aplikaziotik editatu.", - "addon.mod_data.emptyaddform": "Ez duzu eremurik bete!", - "addon.mod_data.entrieslefttoadd": "{{$a.entriesleft}} sarrera gehiago gehitu behar dituzu jarduera hau osatzeko.", - "addon.mod_data.entrieslefttoaddtoview": "{{$a.entrieslefttoview}} sarrera gehiago gehitu behar dituzu beste partaideen sarrerak ikusi ahal izateko.", - "addon.mod_data.errorapproving": "Errorea sarrera onartu edo baztertzean.", - "addon.mod_data.errordeleting": "Errore bat gertatu da sarrera ezabatzean.", - "addon.mod_data.errormustsupplyvalue": "Hemen balio bat eman behar duzu.", - "addon.mod_data.expired": "Sentitzen dugu, jarduera hau {{$a}} datan itxi zen eta dagoeneko ez dago eskuragarri", - "addon.mod_data.fields": "Eremuak", - "addon.mod_data.foundrecords": "Erregistroak: {{$a.num}}/{{$a.max}} (Garbitu iragazkiak)", - "addon.mod_data.gettinglocation": "Kokapena eskuratzen", - "addon.mod_data.latlongboth": "Latitudea eta longitudea beharrekoak dira.", - "addon.mod_data.locationpermissiondenied": "Zure kokapena eskuratzeko baimena ukatua izan da.", - "addon.mod_data.menuchoose": "Aukeratu...", - "addon.mod_data.modulenameplural": "Datu-baseak", - "addon.mod_data.more": "Gehiago", - "addon.mod_data.mylocation": "Nire kokapena", - "addon.mod_data.nomatch": "Ez da sarrera egokirik aurkitu!", - "addon.mod_data.norecords": "Datu-basean sarrerarik ez", - "addon.mod_data.notapproved": "Sarrera ez da oraindik onartu", - "addon.mod_data.notopenyet": "Sentitzen dugu, jarduera hau ez dago eskuragarri {{$a}} arte", - "addon.mod_data.numrecords": "{{$a}} sarrera", - "addon.mod_data.other": "Beste bat", - "addon.mod_data.recordapproved": "Sarrera onartu da", - "addon.mod_data.recorddeleted": "Sarrera ezabatu da", - "addon.mod_data.recorddisapproved": "Onartu gabeko sarrera", - "addon.mod_data.resetsettings": "Garbitu iragazkiak", - "addon.mod_data.search": "Bilatu", - "addon.mod_data.searchbytagsnotsupported": "Sentitzen dugu, aplikazioan ezin da etiketen bidezko bilaketak egin.", - "addon.mod_data.selectedrequired": "Aukeratutako guztia beharrezko da", - "addon.mod_data.single": "Ikusi banaka", - "addon.mod_data.tagarea_data_records": "Datu-erregistroak", - "addon.mod_data.timeadded": "Gehitze-data", - "addon.mod_data.timemodified": "Aldatze-data", - "addon.mod_data.usedate": "Sartu bilaketan.", - "addon.mod_feedback.analysis": "Analisia", - "addon.mod_feedback.anonymous": "Anonimoa", - "addon.mod_feedback.anonymous_entries": "Sarrera anonimoak ({{$a}})", - "addon.mod_feedback.average": "Batez bestekoa", - "addon.mod_feedback.captchaofflinewarning": "CAPTCHA-dun feedback-ak ezin dira lineaz kanpo osatu, ezta konfiguratuta ez badaude edo zerbitzaria eskuragarri ez badago ere.", - "addon.mod_feedback.complete_the_form": "Erantzun galderei", - "addon.mod_feedback.completed_feedbacks": "Bidalitako erantzunak", - "addon.mod_feedback.continue_the_form": "Jarraitu galderei erantzuten", - "addon.mod_feedback.feedback_is_not_open": "Inkesta ez dago zabalik", - "addon.mod_feedback.feedback_submitted_offline": "Feedback hau beranduago bidaltzeko gorde da.", - "addon.mod_feedback.feedbackclose": "Erantzunetarako itxiera-data", - "addon.mod_feedback.feedbackopen": "Erantzunetarako hasiera-data", - "addon.mod_feedback.mapcourses": "Esleitu inkesta ikastaroetarako", - "addon.mod_feedback.maximal": "Gehienezkoa", - "addon.mod_feedback.minimal": "Gutxienekoa", - "addon.mod_feedback.mode": "Modua", - "addon.mod_feedback.modulenameplural": "Inkesta", - "addon.mod_feedback.next_page": "Hurrengo orria", - "addon.mod_feedback.non_anonymous": "Erabiltzaile-izena erantzunekin erakutsiko da", - "addon.mod_feedback.non_anonymous_entries": "Sarrera anonimorik ez ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Erantzun ez duten ikasleak ({{$a}})", - "addon.mod_feedback.not_selected": "Aukeratu gabea", - "addon.mod_feedback.not_started": "Hasi gabea", - "addon.mod_feedback.numberoutofrange": "Tartetik kanpoko zenbakia", - "addon.mod_feedback.overview": "Ikuspegi orokorra", - "addon.mod_feedback.page_after_submit": "Osaketa-mezua", - "addon.mod_feedback.preview": "Aurreikusi", - "addon.mod_feedback.previous_page": "Aurreko orria", - "addon.mod_feedback.questions": "Galderak", - "addon.mod_feedback.response_nr": "Erantzun-kopurua", - "addon.mod_feedback.responses": "Erantzunak", - "addon.mod_feedback.save_entries": "Bidali zure erantzunak", - "addon.mod_feedback.show_entries": "Erakutsi erantzunak", - "addon.mod_feedback.show_nonrespondents": "Erakutsi erantzun gabeak", - "addon.mod_feedback.started": "Hasita", - "addon.mod_feedback.this_feedback_is_already_submitted": "Dagoeneko egina duzu jarduera hau.", - "addon.mod_folder.emptyfilelist": "Ez dago fitxategirik erakusteko.", - "addon.mod_folder.modulenameplural": "Karpetak", - "addon.mod_forum.addanewdiscussion": "Gehitu eztabaidagai berria", - "addon.mod_forum.addanewquestion": "Gehitu galdera berria", - "addon.mod_forum.addanewtopic": "Gehitu gai berria", - "addon.mod_forum.addtofavourites": "Markatu eztabaida gogoko gisa", - "addon.mod_forum.advanced": "Aurreratua", - "addon.mod_forum.cannotadddiscussion": "Foro honetan eztabaidak gehitzeko talde bateko kide izan behar da", - "addon.mod_forum.cannotadddiscussionall": "Ez duzu baimenik partaide guztientzako eztabaidagai berriak gehitzeko.", - "addon.mod_forum.cannotcreatediscussion": "Ezin da eztabaida sortu", - "addon.mod_forum.couldnotadd": "Ezin izan da zure mezua gehitu; errore bat gertatu da nonbait.", - "addon.mod_forum.couldnotupdate": "Ezin izan da zure mezua eguneratu; errore bat gertatu da nonbait.", - "addon.mod_forum.cutoffdatereached": "Foro honetan idazteko itxiera-data igaro da eta ondorioz dagoeneko ezin duzu bertan idatzi.", - "addon.mod_forum.delete": "Ezabatu", - "addon.mod_forum.deletedpost": "Mezua ezabatu da", - "addon.mod_forum.deletesure": "Ziur zaude mezu hau ezabatu nahi duzula?", - "addon.mod_forum.discussion": "Eztabaida", - "addon.mod_forum.discussionlistsortbycreatedasc": "Ordenatu sorrera-dataren arabera (zaharrenetik berrienera)", - "addon.mod_forum.discussionlistsortbycreateddesc": "Ordenatu sorrera-dataren arabera (berrienetik zaharrenera)", - "addon.mod_forum.discussionlistsortbylastpostasc": "Ordenatu azken mezuen sorrera-dataren arabera (zaharrenetik berrienera)", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Ordenatu azken mezuen sorrera-dataren arabera (berrienetik zaharrenera)", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Ordenatu erantzun kopuruaren arabera (behetik gora)", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Ordenatu erantzun kopuruaren arabera (goitik behera)", - "addon.mod_forum.discussionlocked": "Eztabaida hau blokeatu da eta beraz aurrerantzean ezingo duzu bertan erantzun.", - "addon.mod_forum.discussionpinned": "Finkatuta", - "addon.mod_forum.discussionsubscription": "Eztabaidaren harpidetza", - "addon.mod_forum.edit": "Editatu", - "addon.mod_forum.erroremptymessage": "Mezua ezin da hutsik egon", - "addon.mod_forum.erroremptysubject": "Mezuaren gaia ezin da hutsik egon", - "addon.mod_forum.errorgetforum": "Errorea foroko datuak jasotzean.", - "addon.mod_forum.errorgetgroups": "Errorea talde-ezarpenak eskuratzean.", - "addon.mod_forum.errorposttoallgroups": "Ezin izan da sortu eztabaida berria talde guztietan.", - "addon.mod_forum.favouriteupdated": "Zure gogoko-aukera eguneratu da.", - "addon.mod_forum.forumnodiscussionsyet": "Oraindik ez dago elkarrizketarik foro honetan.", - "addon.mod_forum.group": "Taldea", - "addon.mod_forum.lastpost": "Azken mezua", - "addon.mod_forum.lockdiscussion": "Blokeatu eztabaida", - "addon.mod_forum.lockupdated": "Blokeatze-aukera eguneratu da.", - "addon.mod_forum.message": "Mezua", - "addon.mod_forum.modeflatnewestfirst": "Erakutsi erantzunak era lauan, berrienak lehen", - "addon.mod_forum.modeflatoldestfirst": "Erakutsi erantzunak era lauan, zaharrenak lehen", - "addon.mod_forum.modenested": "Erakutsi erantzunak hariaren arabera", - "addon.mod_forum.modulenameplural": "Foroak", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} elkarrizketa", - "addon.mod_forum.numreplies": "{{numreplies}} erantzun", - "addon.mod_forum.pindiscussion": "Finkatu eztabaida hau", - "addon.mod_forum.pinupdated": "Finkatze-aukera eguneratu da.", - "addon.mod_forum.postisprivatereply": "Hau erantzun pribatu bat da. Beste erabiltzaileek ezin dute ikusi.", - "addon.mod_forum.posttoforum": "Bidali mezua forora", - "addon.mod_forum.posttomygroups": "Idatzi kopia bat talde guztietan", - "addon.mod_forum.privatereply": "Erantzun modu pribatuan", - "addon.mod_forum.re": "Er:", - "addon.mod_forum.refreshdiscussions": "Freskatu eztabaidak", - "addon.mod_forum.refreshposts": "Freskatu eztabaidetako mezuak", - "addon.mod_forum.removefromfavourites": "Kendu eztabaida honen gogoko marka", - "addon.mod_forum.reply": "Erantzun", - "addon.mod_forum.replyplaceholder": "Idatzi zure erantzuna...", - "addon.mod_forum.subject": "Gaia", - "addon.mod_forum.tagarea_forum_posts": "Foroetako mezuak", - "addon.mod_forum.thisforumhasduedate": "Foro honetan idazteko amaiera-data {{$a}} da.", - "addon.mod_forum.thisforumisdue": "Foro honetan idazteko amaiera-data {{$a}} zen.", - "addon.mod_forum.unlockdiscussion": "Desblokeatu eztabaida hau", - "addon.mod_forum.unpindiscussion": "Kendu eztabaida honen finkatzea", - "addon.mod_forum.unread": "Irakurri gabe", - "addon.mod_forum.unreadpostsnumber": "Irakurri gabeko {{$a}} mezu", - "addon.mod_forum.yourreply": "Zure erantzuna", - "addon.mod_glossary.addentry": "Gehitu sarrera berria", - "addon.mod_glossary.aliases": "Hitz gakoak", - "addon.mod_glossary.attachment": "Eranskina", - "addon.mod_glossary.browsemode": "Aztertu sarrerak", - "addon.mod_glossary.byalphabet": "Alfabetikoki", - "addon.mod_glossary.byauthor": "Taldekatu egilearen arabera", - "addon.mod_glossary.bycategory": "Taldekatu kategoriaren arabera", - "addon.mod_glossary.bynewestfirst": "Berrienak lehenago", - "addon.mod_glossary.byrecentlyupdated": "Duela gutxi eguneratuak", - "addon.mod_glossary.bysearch": "Bilatu", - "addon.mod_glossary.cannoteditentry": "Ezin da sarrera editatu", - "addon.mod_glossary.casesensitive": "Sarrera honetan letra larriak eta xeheak bereizten dira", - "addon.mod_glossary.categories": "Kategoriak", - "addon.mod_glossary.concept": "Kontzeptua", - "addon.mod_glossary.definition": "Definizoa", - "addon.mod_glossary.entriestobesynced": "Sinkronizatu beharreko sarrerak", - "addon.mod_glossary.entrypendingapproval": "Sarrera hau onarpenaren zain dago.", - "addon.mod_glossary.entryusedynalink": "Sarrera hau automatikoki estekatu", - "addon.mod_glossary.errconceptalreadyexists": "Kontzeptu hau dagoeneko existitzen da. Glosario honetan ezin dira sarrerak bikoiztu.", - "addon.mod_glossary.errorloadingentries": "Errore bat gertatu da sarrerak kargatzean.", - "addon.mod_glossary.errorloadingentry": "Errore bat gertatu da sarrera kargatzean.", - "addon.mod_glossary.errorloadingglossary": "Errore bat gertatu da glosategia kargatzean.", - "addon.mod_glossary.fillfields": "Kontzeptua eta azalpena derrigorrezko datuak dira", - "addon.mod_glossary.fullmatch": "Bilatu soilik hitz osoak", - "addon.mod_glossary.linking": "Autoesteka", - "addon.mod_glossary.modulenameplural": "Glosarioak", - "addon.mod_glossary.noentriesfound": "Ez da sarrerarik aurkitu", - "addon.mod_glossary.searchquery": "Egin bilaketa", - "addon.mod_glossary.tagarea_glossary_entries": "Glosarioko sarrerak", - "addon.mod_h5pactivity.all_attempts": "Erabiltzaileen saiakera guztiak", - "addon.mod_h5pactivity.answer_checked": "Markatutako erantzuna", - "addon.mod_h5pactivity.answer_correct": "Zure erantzuna zuzena da", - "addon.mod_h5pactivity.answer_fail": "Erantzun okerra", - "addon.mod_h5pactivity.answer_incorrect": "Zure erantzuna ez da zuzena", - "addon.mod_h5pactivity.answer_pass": "Erantzun zuzena", - "addon.mod_h5pactivity.attempt": "Saiakera", - "addon.mod_h5pactivity.attempt_completion_no": "Saiakera hau ez dago osatu gisa markatuta", - "addon.mod_h5pactivity.attempt_completion_yes": "Saiakera hau osatuta dago", - "addon.mod_h5pactivity.attempt_success_fail": "Gainditu gabe", - "addon.mod_h5pactivity.attempt_success_pass": "Gaindituta", - "addon.mod_h5pactivity.attempt_success_unknown": "Txostenik ez", - "addon.mod_h5pactivity.attempts_none": "Erabiltzaile honek ez dauka erakusteko saiakerarik", - "addon.mod_h5pactivity.completion": "Osaketa", - "addon.mod_h5pactivity.downloadh5pfile": "Jaitsi H5P fitxategia", - "addon.mod_h5pactivity.duration": "Iraupena", - "addon.mod_h5pactivity.errorgetactivity": "Errorea H5P jardueraren datuak eskuratzean.", - "addon.mod_h5pactivity.filestatenotdownloaded": "H5P paketea ez dago deskargatuta. Erabili ahal izateko aurretik jaitsi beharko duzu.", - "addon.mod_h5pactivity.filestateoutdated": "H5P paketea aldatua izan da azkenengoz jaitsi zenuenetik. Erabili ahal izateko aurretik berriz jaitsi beharko duzu.", - "addon.mod_h5pactivity.maxscore": "Gehienezko puntuazioa", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Nire saiakerak", - "addon.mod_h5pactivity.no_compatible_track": "Interakzio honek ({{$a}}) ez du osaketaren inguruko informaziorik eskaintzen edo emandako informazioa ez da oraingo jarduera-bertsioarekin bateragarria.", - "addon.mod_h5pactivity.offlinedisabledwarning": "Online egon behar duzu H5P paketea ikusi ahal izateko.", - "addon.mod_h5pactivity.outcome": "Emaitza", - "addon.mod_h5pactivity.previewmode": "Eduki hau aurrebista moduan erakusten da. Ez da saiakeraren jarraipenik gordeko.", - "addon.mod_h5pactivity.result_fill-in": "Bete beharreko testua", - "addon.mod_h5pactivity.result_other": "Interakzio-mota ezezaguna", - "addon.mod_h5pactivity.review_my_attempts": "Ikusi nire saiakerak", - "addon.mod_h5pactivity.score": "Puntuazioa", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} gehienezko {{$a.maxscore}}-(e)tik", - "addon.mod_h5pactivity.startdate": "Hasiera-data", - "addon.mod_h5pactivity.totalscore": "Puntuazioa guztira", - "addon.mod_h5pactivity.viewattempt": "Ikusi {{$a}} saiakera", - "addon.mod_imscp.deploymenterror": "Errorea eduki-paketea egitean!", - "addon.mod_imscp.modulenameplural": "IMS eduki-paketeak", - "addon.mod_imscp.showmoduledescription": "Deskribapena erakutsi", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "Erantzuna", - "addon.mod_lesson.attempt": "Saiakera: {{$a}}", - "addon.mod_lesson.attemptheader": "Saiakera", - "addon.mod_lesson.attemptsremaining": "{{$a}} saiakera pendiente daukazu", - "addon.mod_lesson.averagescore": "Batez besteko puntuazioa", - "addon.mod_lesson.averagetime": "Batez besteko denbora", - "addon.mod_lesson.branchtable": "Edukia", - "addon.mod_lesson.cannotfindattempt": "Errorea: ezin da saiakera aurkitu", - "addon.mod_lesson.cannotfinduser": "Errorea: ezin dira erabiltzaileak aurkitu", - "addon.mod_lesson.clusterjump": "Multzo batean ikusi gabeko galdera", - "addon.mod_lesson.completed": "Osatuta", - "addon.mod_lesson.congratulations": "Zorionak! Ikasgaiaren amaierara iritsi zara", - "addon.mod_lesson.continue": "Jarraitu", - "addon.mod_lesson.continuetonextpage": "Jarraitu hurrengo orrira.", - "addon.mod_lesson.defaultessayresponse": "Zure entsegua irakasleak kalifikatuko du.", - "addon.mod_lesson.detailedstats": "Estatistika zehatzak", - "addon.mod_lesson.didnotanswerquestion": "Galdera honi ez diozu erantzunik eman.", - "addon.mod_lesson.displayofgrade": "Erakutsi kalifikazioa (ikasleentzat soilik)", - "addon.mod_lesson.displayscorewithessays": "

                Automatikoki kalifikatutako galderetan hau da zure puntuazioa: {{$a.score}}\nposible zen gehienezko puntuazio honetatik: {{$a.tempmaxgrade}}.

                \n

                {{$a.essayquestions}} entsegu motako galdera(k) beranduago kalifikatuko dira eta zure azken emaitzari erantsiko zaizkio aurrerago.

                \n

                Oraingoz, eta entsegu-galdera(k) kontuan hartu gabe, zure emaitza {{$a.score}} (e)koa da {{$a.grade}} (e)tik

                ", - "addon.mod_lesson.displayscorewithoutessays": "Zure puntuazioa hau da: {{$a.score}} (gehienezkoa {{$a.grade}}-(e)tik).", - "addon.mod_lesson.emptypassword": "Pasahitza ezin da hutsik egon", - "addon.mod_lesson.enterpassword": "Idatzi pasahitza, mesedez:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Ez duzu erantzunik eman. Ikasgai honetan 0 puntu lortu duzu.", - "addon.mod_lesson.errorprefetchrandombranch": "Ikasgai honek ausazko eduki-orri baterako jauzia dauka. Ezin da aplikazioan saiakerarik egin aurretik web nabigatzailean hasi ezean.", - "addon.mod_lesson.errorreviewretakenotlast": "Saiakera hau ezin da berrikusi dagoeneko beste saiakera bat amaitu delako.", - "addon.mod_lesson.finish": "Amaitu", - "addon.mod_lesson.finishretakeoffline": "Saiakera hau lineaz kanpo bukatu zen.", - "addon.mod_lesson.firstwrong": "Zure erantzuna ez da zuzena izan. Berriz saiatu nahi al duzu? (Oraingoan ondo erantzunez gero ez da zure azken emaitzan zenbatuko).", - "addon.mod_lesson.gotoendoflesson": "Joan ikasgaiaren amaierara", - "addon.mod_lesson.grade": "Kalifikazioa", - "addon.mod_lesson.highscore": "Puntuazio altua", - "addon.mod_lesson.hightime": "Denbora altua", - "addon.mod_lesson.leftduringtimed": "Denbora-epe finkoko ikasgaia eten da.
                Berriz hasteko egin klik \"Jarraitu\" botoian, mesedez.", - "addon.mod_lesson.leftduringtimednoretake": "Denbora-epe finkoko ikasgaia eten da eta
                ez dago berriz hasteko edo jarraitzeko baimenik.", - "addon.mod_lesson.lessonmenu": "Ikasgaiaren menua", - "addon.mod_lesson.lessonstats": "Ikasgaiaren estatistikak", - "addon.mod_lesson.linkedmedia": "Estekatutako media", - "addon.mod_lesson.loginfail": "Sarrerak huts egin du. Berriz saiatu, mesedez...", - "addon.mod_lesson.lowscore": "Puntuazio baxua", - "addon.mod_lesson.lowtime": "Denbora baxua", - "addon.mod_lesson.maximumnumberofattemptsreached": "Gehienezko saiakera-kopurura iritsi zara. Hurrengo orrira jauzi", - "addon.mod_lesson.modattemptsnoteacher": "Ikaslearen berrikuspena soilik ikasleen eskura dago.", - "addon.mod_lesson.modulenameplural": "Ikasgaiak", - "addon.mod_lesson.noanswer": "Galdera batek edo gehiagok ez du erantzunik. Mesedez, joan atzera eta osatu.", - "addon.mod_lesson.nolessonattempts": "Ez da ikasgai hau praktikatzeko saiakerarik egin.", - "addon.mod_lesson.nolessonattemptsgroup": "{{$a}} taldeko partaideek ez dute saiakerarik egin ikasgai honetan.", - "addon.mod_lesson.notcompleted": "Ez osatua", - "addon.mod_lesson.numberofcorrectanswers": "Erantzun zuzenen kopurua: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Erantzundako galdera-kopurua: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Erantzundako galdera-kopurua: {{$a.nquestions}}; (Gutxienez, {{$a.minquestions}} erantzun beharko zenituzke).", - "addon.mod_lesson.ongoingcustom": "Ikasgai hau gehienez {{$a.score}} punturekin balioesten da. Zuk {{$a.score}} puntu lortu duzu gehienezko {{$a.currenthigh}} puntutatik.", - "addon.mod_lesson.ongoingnormal": "Zuk {{$a.viewed}} galderatik {{$a.correct}} ondo erantzun duzu.", - "addon.mod_lesson.or": "edo", - "addon.mod_lesson.overview": "Laburpena", - "addon.mod_lesson.preview": "Aurreikusi", - "addon.mod_lesson.progressbarteacherwarning2": "Ikasgaia editatzeko baimena duzunez, zuk ez duzu aurrerapen-barra ikusiko", - "addon.mod_lesson.progresscompleted": "Ikasgai honen % {{$a}} osatu duzu", - "addon.mod_lesson.question": "Galdera", - "addon.mod_lesson.rawgrade": "Puntuazio gordina", - "addon.mod_lesson.reports": "Txostenak", - "addon.mod_lesson.response": "Erantzuna", - "addon.mod_lesson.retakefinishedinsync": "Lineaz kanpoko saiakera bat sinkronizatu da. Berrikusi nahi duzu?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Berrikuspena", - "addon.mod_lesson.reviewlesson": "Berrikusi ikasgaia", - "addon.mod_lesson.reviewquestionback": "Bai, berriz saiatu nahi nuke", - "addon.mod_lesson.reviewquestioncontinue": "Ez, hurrengora pasa nahi dut", - "addon.mod_lesson.secondpluswrong": "Ez. Berriz saiatu nahi al duzu?", - "addon.mod_lesson.submit": "Bidali", - "addon.mod_lesson.teacherjumpwarning": "Ikasgai honetan {{$a.cluster}} edo {{$a.unseen}} jauzia erabiltzen ari da. Bere ordez, hurrengo orrirako jauzia erabiliko da. Ikasle gisa sartu zaitez jauzi horiek probatzeko.", - "addon.mod_lesson.teacherongoingwarning": "Metatutako puntuazioa soilik ikasleari erakusten zaio. Ikasle gisa sartu zaitez metatutako puntuazioa probatzeko.", - "addon.mod_lesson.teachertimerwarning": "Denbora-neurgailua ikasleei erakusten zaie soilik. Denbora-neurgailua probatzeko ikasle gisa sartu.", - "addon.mod_lesson.thatsthecorrectanswer": "Hauxe da erantzun zuzena", - "addon.mod_lesson.thatsthewronganswer": "Hauxe da erantzun okerra", - "addon.mod_lesson.timeremaining": "Geratzen den denbora", - "addon.mod_lesson.timetaken": "Erabilitako denbora", - "addon.mod_lesson.unseenpageinbranch": "Eduki-orrian ikusi ez den galdera", - "addon.mod_lesson.warningretakefinished": "Saiakera gunean bukatu zen.", - "addon.mod_lesson.welldone": "Ederki!", - "addon.mod_lesson.youhaveseen": "Ikasgai honetako orri bat edo gehiago ikusi duzu jadanik.
                Ikusitako azken orritik hasi nahi al duzu?", - "addon.mod_lesson.youranswer": "Zure erantzuna", - "addon.mod_lesson.yourcurrentgradeisoutof": "Zure oraingo kalifikazioa {{$a.grade}} da gehienezko {{$a.total}}-(e)tik.", - "addon.mod_lesson.youshouldview": "Gutxienez hau ikusi beharko zenuke: {{$a}}", - "addon.mod_lti.errorgetlti": "Errorea moduluaren datuak eskuratzean.", - "addon.mod_lti.errorinvalidlaunchurl": "Hasierako URLa ez da baliagarria", - "addon.mod_lti.launchactivity": "Abiarazi jarduera", - "addon.mod_lti.modulenameplural": "Kanpoko tresnak", - "addon.mod_page.errorwhileloadingthepage": "Errorea orriaren edukia kargatzean.", - "addon.mod_page.modulenameplural": "Orriak", - "addon.mod_quiz.answercolon": "Erantzuna:", - "addon.mod_quiz.attemptfirst": "Lehenengo saiakera", - "addon.mod_quiz.attemptlast": "Azken saiakera", - "addon.mod_quiz.attemptnumber": "Saiakera", - "addon.mod_quiz.attemptquiznow": "Saiatu orain galdetegia egiten", - "addon.mod_quiz.attemptstate": "Egoera", - "addon.mod_quiz.canattemptbutnotsubmit": "Galdetegi hau erantzuten saiatu zaitezke, baina saiakera nabigatzaile batetik bidali beharko duzu hurrengo arrazoiak direla eta:", - "addon.mod_quiz.cannotsubmitquizdueto": "Galdetegi honen saiakera ezin da bidali hurrengo arrazoiak direla eta:", - "addon.mod_quiz.clearchoice": "Garbitu nire aukera", - "addon.mod_quiz.comment": "Iruzkina", - "addon.mod_quiz.completedon": "Noiz osatua", - "addon.mod_quiz.confirmclose": "Behin saiakera bidalitakoan ezingo duzu bertako erantzunetan aldaketarik egin.", - "addon.mod_quiz.confirmcontinueoffline": "Saiakera hau ez da {{$a}}-tik sinkronizatu. Ordutik beste gailu batean saiakerarekin jarraitu baduzu datuak galdu ahal zenitzakeen.", - "addon.mod_quiz.confirmleavequizonerror": "Errorea gertatu da erantzunak gordetzen ari zirenean. Ziur zaude galdetegia utzi nahi duzula?", - "addon.mod_quiz.confirmstart": "Zure saiakerak {{$a}}-(e)ko denbora-muga izango du. Hasten zarenean atzera kontaketa hasiko da eta zein da pausatu. Zure saiakera denbora amaitu aurretik bidali beharko duzu. Ziur zaude orain hasi nahi duzula?", - "addon.mod_quiz.confirmstartheader": "Denbora-muga", - "addon.mod_quiz.connectionerror": "Sarearen konexioa galdu egin da (Gordetze automatikoak huts egin du)\n\nGorde idatziz azken minutuetan orri honetan egin dituzun erantzunak eta ondoren saiatu berriz konektatzen.\n\nKonexioa berreskuratzen denean, zure erantzunak gorde beharko lirateke eta mezu hau desagertu egingo da.", - "addon.mod_quiz.continueattemptquiz": "Jarraitu azken saiakerarekin", - "addon.mod_quiz.continuepreview": "Jarraitu azken aurrebistarekin", - "addon.mod_quiz.errorbehaviournotsupported": "Galdetegi hau ezin da aplikazioan erantzun galderen jokaera ez dagoelako aplikazioan onartua:", - "addon.mod_quiz.errordownloading": "Errorea behar diren datuak deskargatzean.", - "addon.mod_quiz.errorgetattempt": "Errorea saiakeraren datuak eskuratzean.", - "addon.mod_quiz.errorgetquestions": "Errorea galderak eskuratzean.", - "addon.mod_quiz.errorgetquiz": "Errorea galdetegiaren datuak eskuratzean.", - "addon.mod_quiz.errorparsequestions": "Errorea gertatu da galderak irakurtzean. Mesedez galdetegia nabigatzailean egiten saiatu.", - "addon.mod_quiz.errorquestionsnotsupported": "Galdetegi hau ezin da aplikazioan erantzun aplikazioan onartzen ez diren galdera-motak baino ez dituelako:", - "addon.mod_quiz.errorrulesnotsupported": "Galdetegi hau ezin da aplikazioan erantzun bertan onartzen ez diren sarbide-arauak dituelako:", - "addon.mod_quiz.errorsaveattempt": "Errorea gertatu da saiakeraren datuak gordetzean.", - "addon.mod_quiz.feedback": "Feedbacka", - "addon.mod_quiz.finishattemptdots": "Amaitu saiakera...", - "addon.mod_quiz.finishnotsynced": "Bukatua baina sinkronizatu gabe.", - "addon.mod_quiz.grade": "Kalifikazioa", - "addon.mod_quiz.gradeaverage": "Batez besteko kalifikazioa", - "addon.mod_quiz.gradehighest": "Kalifikazio altuena", - "addon.mod_quiz.grademethod": "Kalifikazio-metodoa", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Puntuazioak", - "addon.mod_quiz.modulenameplural": "Galdetegiak", - "addon.mod_quiz.mustbesubmittedby": "Saiakera hau bidaltzeko epearen amaiera: {{$a}}", - "addon.mod_quiz.noquestions": "Ez da galderarik gehitu oraindik", - "addon.mod_quiz.noreviewattempt": "Ez duzu saiakera hau berrikusteko baimenik.", - "addon.mod_quiz.notyetgraded": "Ez da oraindik kalifikatu", - "addon.mod_quiz.opentoc": "Ireki nabigazioa gainean.", - "addon.mod_quiz.outof": "{{$a.grade}} gehienezko {{$a.maxgrade}}-(e)tik", - "addon.mod_quiz.outofpercent": "{{$a.grade}} gehienezko {{$a.maxgrade}}-(e)tik (%{{$a.percent}})", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Galdetegiko feedback orokorra", - "addon.mod_quiz.overdue": "Atzeratuta", - "addon.mod_quiz.overduemustbesubmittedby": "Saiakera hau epez kanpo dago. Dagoeneko bidalita egon beharko luke. Galdetegia kalifikatua izatea nahi baduzu, {{$a}}-(e)rako bidali behar duzu. Ez baduzu data horretarako bidaltzen, saiakera hau ez da aintzat hartuko.", - "addon.mod_quiz.preview": "Aurreikusi", - "addon.mod_quiz.previewquiznow": "Aurreikusi galdetegia orain", - "addon.mod_quiz.question": "Galdera", - "addon.mod_quiz.quiznavigation": "Galdetegiaren nabigazioa", - "addon.mod_quiz.quizpassword": "Galdetegiaren pasahitza", - "addon.mod_quiz.reattemptquiz": "Saiatu berriro galdetegia egiten", - "addon.mod_quiz.requirepasswordmessage": "Galdetegi hau egiten saiatzeko galdetegiko pasahitza ezagutu behar duzu", - "addon.mod_quiz.returnattempt": "Itzuli saiakerara", - "addon.mod_quiz.review": "Berrikusi", - "addon.mod_quiz.reviewofattempt": "{{$a}} saiakeraren berrikusketa", - "addon.mod_quiz.reviewofpreview": "Aurrebistaren berrikusketa", - "addon.mod_quiz.showall": "Erakutsi galdera guztiak orri bakarrean", - "addon.mod_quiz.showeachpage": "Erakutsi orri bat aldi bakoitzean", - "addon.mod_quiz.startattempt": "Hasi saiakera", - "addon.mod_quiz.startedon": "Hasiera-unea", - "addon.mod_quiz.stateabandoned": "Bidali gabeak", - "addon.mod_quiz.statefinished": "Amaituta", - "addon.mod_quiz.statefinisheddetails": "{{$a}} bidali da", - "addon.mod_quiz.stateinprogress": "Abian", - "addon.mod_quiz.stateoverdue": "Atzeratuta", - "addon.mod_quiz.stateoverduedetails": "{{$a}}-(e)rako bidali behar da", - "addon.mod_quiz.status": "Egoera", - "addon.mod_quiz.submitallandfinish": "Bidali dena eta amaitu", - "addon.mod_quiz.summaryofattempt": "Saiakeren laburpena", - "addon.mod_quiz.summaryofattempts": "Orain arteko zure saiakeren laburpena", - "addon.mod_quiz.timeleft": "Geratzen den denbora", - "addon.mod_quiz.timetaken": "Hartutako denbora", - "addon.mod_quiz.warningattemptfinished": "Lineaz kanpoko saiakera baztertu da gunean bukatu delako edo ez delako aurkitu.", - "addon.mod_quiz.warningdatadiscarded": "Lineaz kanpoko galdera batzuk baztertu dira galderak online aldatu direlako", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Saiakera ez da bukatu lineaz kanpoko galdera batzuk baztertu direlako. Mesedez zure erantzunak berrikusi eta saiakera berriz bidali.", - "addon.mod_quiz.warningquestionsnotsupported": "Galdetegi honek aplikazioan onartzen ez diren galderak ditu:", - "addon.mod_quiz.yourfinalgradeis": "Galdetegi honetan zure azken kalifikazioa {{$a}} da.", - "addon.mod_resource.errorwhileloadingthecontent": "Errorea edukia kargatzean.", - "addon.mod_resource.modifieddate": "Aldatze-data: {{$a}}", - "addon.mod_resource.modulenameplural": "Fitxategiak", - "addon.mod_resource.openthefile": "Ireki fitxategia", - "addon.mod_resource.uploadeddate": "Igoera-data: {{$a}}", - "addon.mod_scorm.asset": "Baliabidea", - "addon.mod_scorm.assetlaunched": "Baliabidea - Ikusia", - "addon.mod_scorm.attempts": "Saiakerak", - "addon.mod_scorm.averageattempt": "Saiakeren batez bestekoa", - "addon.mod_scorm.browse": "Aurrebista", - "addon.mod_scorm.browsed": "Nabigatua", - "addon.mod_scorm.browsemode": "Aurrebista-modua", - "addon.mod_scorm.cannotcalculategrade": "Ezin izan da kalifikazioa kalkulatu.", - "addon.mod_scorm.completed": "Osatua", - "addon.mod_scorm.contents": "Edukiak", - "addon.mod_scorm.dataattemptshown": "Datu hauek {{number}} saiakerakoak dira.", - "addon.mod_scorm.enter": "Sartu", - "addon.mod_scorm.errorcreateofflineattempt": "Errorea gertatu da lineaz kanpoko saiakera berria sortzean. Mesedez, saiatu berriz.", - "addon.mod_scorm.errordownloadscorm": "Errorea SCORMa jaistean: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Errorea SCORM datuak eskuratzean.", - "addon.mod_scorm.errorinvalidversion": "Barkatu, aplikazioak SCORM 1.2 besterik ez du onartzen.", - "addon.mod_scorm.errornotdownloadable": "SCORM paketeen deskarga desgaituta dago.Mesedez jarri harremanetan zure guneko kudeatzailearekin.", - "addon.mod_scorm.errornovalidsco": "SCORM pakete honek ez du kargatzeko SCOrik ikusgai.", - "addon.mod_scorm.errorpackagefile": "Barkatu, aplikazioak ZIP paketeak besterik ez du onartzen.", - "addon.mod_scorm.errorsyncscorm": "Errorea gertatu da sinkronizatzean. Mesedez, saiatu berriz.", - "addon.mod_scorm.exceededmaxattempts": "Gehienezko saiakera-kopurua egin duzu.", - "addon.mod_scorm.failed": "Gainditu gabea", - "addon.mod_scorm.firstattempt": "Lehenengo saiakera", - "addon.mod_scorm.gradeaverage": "Batez besteko kalifikazioa", - "addon.mod_scorm.gradeforattempt": "Saikerarako kalifikazioa", - "addon.mod_scorm.gradehighest": "Kalifikazio altuena", - "addon.mod_scorm.grademethod": "Kalifikazio-metodoa", - "addon.mod_scorm.gradereported": "Emandako kalifikazioa", - "addon.mod_scorm.gradescoes": "Ikasteko objektuak", - "addon.mod_scorm.gradesum": "Batutako kalifikazioak", - "addon.mod_scorm.highestattempt": "Saiakera altuena", - "addon.mod_scorm.incomplete": "Osatu gabea", - "addon.mod_scorm.lastattempt": "Osatutako azken saiakera", - "addon.mod_scorm.modulenameplural": "SCORM paketeak", - "addon.mod_scorm.newattempt": "Hasi saiakera berria", - "addon.mod_scorm.noattemptsallowed": "Baimendutako saiakera-kopurua", - "addon.mod_scorm.noattemptsmade": "Egin duzun saiakera-kopurua", - "addon.mod_scorm.notattempted": "Saiatu gabea", - "addon.mod_scorm.offlineattemptnote": "Saiakera honek sinkronizatu gabeko datuak dauzka.", - "addon.mod_scorm.offlineattemptovermax": "Saiakera hau ezin da bidali dagoeneko gehienezko saiakera-kopurua gainditu duzulako.", - "addon.mod_scorm.organizations": "Erakundeak", - "addon.mod_scorm.passed": "Gainditua", - "addon.mod_scorm.reviewmode": "Berrikusketa-modua", - "addon.mod_scorm.score": "Puntuazioa", - "addon.mod_scorm.scormstatusnotdownloaded": "SCORM pakete hau ez dago aurretik jaitsita. Automatikoki jaitsiko da irekitzen duzunean.", - "addon.mod_scorm.scormstatusoutdated": "SCORM pakete hau eguneratua izan da azkenekoz jaitsi zenuenetik. Automatikoki jaitsiko da irekitzen duzunean.", - "addon.mod_scorm.suspended": "Bere horretan utzia", - "addon.mod_scorm.toc": "TOC", - "addon.mod_scorm.warningofflinedatadeleted": "{{number}}. saiakeraren lineaz kanpoko datu batzuk ezabatu dira ezin izan direlako saiakera berri gisa hartu.", - "addon.mod_scorm.warningsynconlineincomplete": "Saiakera batzuk ezin izan dira gunearekin sinkronizatu azken online saiakera ez delako oraindik bukatu. Mesedez, bukatu online saiakera aurretik.", - "addon.mod_survey.cannotsubmitsurvey": "Barkatu, baina arazo bat egon da zure hausnarketa bidaltzean. Mesedez saiatu berriz.", - "addon.mod_survey.errorgetsurvey": "Errorea hausnarketaren datuak eskuratzean.", - "addon.mod_survey.ifoundthat": "Hau aurkitu dut", - "addon.mod_survey.ipreferthat": "Nahiago dut", - "addon.mod_survey.modulenameplural": "Hausnarketak", - "addon.mod_survey.responses": "Erantzunak", - "addon.mod_survey.results": "Emaitzak", - "addon.mod_survey.surveycompletednograph": "Bete duzu hausnarketa hau", - "addon.mod_url.accessurl": "URLan sartu", - "addon.mod_url.modulenameplural": "URLak", - "addon.mod_url.pointingtourl": "Baliabideak estekatzen duen URLa", - "addon.mod_wiki.cannoteditpage": "Ezin duzu orri hau editatu.", - "addon.mod_wiki.createpage": "Sortu orria", - "addon.mod_wiki.editingpage": "Orri hau editatzen: '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Errore bat gertatu da orria kargatzean.", - "addon.mod_wiki.errornowikiavailable": "Wiki honek oraindik ez du edukirik.", - "addon.mod_wiki.gowikihome": "Joan wikiaren lehen orrira", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wikiak", - "addon.mod_wiki.newpagehdr": "Orri berria", - "addon.mod_wiki.newpagetitle": "Orri berriaren izenburua", - "addon.mod_wiki.nocontent": "Ez dago edukirik orri honetarako", - "addon.mod_wiki.notingroup": "Ez dago taldean", - "addon.mod_wiki.pageexists": "Orri hau badago dagoeneko.", - "addon.mod_wiki.pagename": "Orriaren izena", - "addon.mod_wiki.subwiki": "Azpiwikia", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki-orriak", - "addon.mod_wiki.titleshouldnotbeempty": "Izenburua ezin da hutsik egon", - "addon.mod_wiki.viewpage": "Ikusi orria", - "addon.mod_wiki.wikipage": "Wiki orria", - "addon.mod_wiki.wrongversionlock": "Beste erabiltzaile batek editatu du orri hau zeu editatzen ari zinen bitartean: zeure edukia zaharkituta dago.", - "addon.mod_workshop.alreadygraded": "Dagoeneko kalifikatuta", - "addon.mod_workshop.areainstructauthors": "Bidalketarako argibideak", - "addon.mod_workshop.areainstructreviewers": "Ebaluaziorako argibideak", - "addon.mod_workshop.assess": "Ebaluatu", - "addon.mod_workshop.assessedsubmission": "Ebaluatutakoaren bidalketa", - "addon.mod_workshop.assessmentform": "Ebaluaziorako formularioa", - "addon.mod_workshop.assessmentsettings": "Ebaluazioaren ezarpenak", - "addon.mod_workshop.assessmentstrategynotsupported": "Ez da {{$a}} ebaluazio-estrategia onartzen", - "addon.mod_workshop.assessmentweight": "Ebaluazioaren pisua", - "addon.mod_workshop.assignedassessments": "Ebaluatzeko esleitutako bidalketak", - "addon.mod_workshop.assignedassessmentsnone": "Ez duzu bidalketarik esleituta ebaluatzeko", - "addon.mod_workshop.conclusion": "Ondorioak", - "addon.mod_workshop.createsubmission": "Gehitu bidalketa", - "addon.mod_workshop.deletesubmission": "Ezabatu bidalketa", - "addon.mod_workshop.editsubmission": "Editatu bidalketa", - "addon.mod_workshop.feedbackauthor": "Egilearentzako feedbacka", - "addon.mod_workshop.feedbackby": "Ondokoaren feedbacka: {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Ebaluatzaileak emandako feedbacka", - "addon.mod_workshop.givengrades": "Emandako kalifikazioak", - "addon.mod_workshop.gradecalculated": "Bidalketarako kalkulatutako kalifikazioa", - "addon.mod_workshop.gradeinfo": "Kalifikazioa: {{$a.received}}/{{$a.max}}", - "addon.mod_workshop.gradeover": "Indargabetu kalifikazioa bidalketarako", - "addon.mod_workshop.gradesreport": "Tailerraren kalifikazio-txostena", - "addon.mod_workshop.gradinggrade": "Ebaluazioaren kalifikazioa", - "addon.mod_workshop.gradinggradecalculated": "Kalkulatutako kalifikazioa ebaluaziorako", - "addon.mod_workshop.gradinggradeof": "Ebaluazioaren kalifikazioa ({{$a}}(e)tik)", - "addon.mod_workshop.gradinggradeover": "Indargabetu kalifikazioa ebaluaziorako", - "addon.mod_workshop.modulenameplural": "Tailerrak", - "addon.mod_workshop.nogradeyet": "Ez da oraindik kalifikatu", - "addon.mod_workshop.notassessed": "Ebaluatu gabea", - "addon.mod_workshop.notoverridden": "Baliogabetu gabe", - "addon.mod_workshop.noyoursubmission": "Oraindik ez duzu lanik bidali", - "addon.mod_workshop.overallfeedback": "Feedback orokorra", - "addon.mod_workshop.publishedsubmissions": "Argitaratutako bidalketak", - "addon.mod_workshop.publishsubmission": "Argitaratu bidalketa", - "addon.mod_workshop.publishsubmission_help": "Publikatutako bidalketak besteentzat eskuragarri egongo dira tailerra ixten denean", - "addon.mod_workshop.reassess": "Berriro ebaluatu", - "addon.mod_workshop.receivedgrades": "Jasotako kalifikazioak", - "addon.mod_workshop.submissionattachment": "Eranskina", - "addon.mod_workshop.submissioncontent": "Bidalketaren edukia", - "addon.mod_workshop.submissiondeleteconfirm": "Ziur zaude hurrengo bidalketa ezabatu nahi duzula?", - "addon.mod_workshop.submissiongrade": "Bidalketaren kalifikazioa", - "addon.mod_workshop.submissiongradeof": "Bidalketaren kalifikazioa ({{$a}}(e)tik)", - "addon.mod_workshop.submissionrequiredcontent": "Testuren bat idatzi edo fitxategiren bat gehitu behar duzu.", - "addon.mod_workshop.submissionrequiredtitle": "Izenburu bat idatzi behar duzu.", - "addon.mod_workshop.submissionsreport": "Tailerraren bidalketa-txostena", - "addon.mod_workshop.submissiontitle": "Izenburua", - "addon.mod_workshop.switchphase10": "Aldatu ezarpenak zehazteko fasera", - "addon.mod_workshop.switchphase20": "Aldatu bidalketa-fasera", - "addon.mod_workshop.switchphase30": "Aldatu ebaluazio-fasera", - "addon.mod_workshop.switchphase40": "Aldatu ebaluazioak kalifikatzeko fasera", - "addon.mod_workshop.switchphase50": "Itxi tailerra", - "addon.mod_workshop.userplan": "Tailerraren planifikazioa", - "addon.mod_workshop.userplancurrentphase": "Oraingo fasea", - "addon.mod_workshop.warningassessmentmodified": "Bidalketa gunean aldatu izan da.", - "addon.mod_workshop.warningsubmissionmodified": "Bidalketa gunean aldatu izan da.", - "addon.mod_workshop.weightinfo": "Pisua: {{$a}}", - "addon.mod_workshop.yourassessment": "Zeuk egindako ebaluazioa", - "addon.mod_workshop.yourassessmentfor": "{{$a}}-(r)ako zure ebaluazioa", - "addon.mod_workshop.yourgrades": "Zure kalifikazioak", - "addon.mod_workshop.yoursubmission": "Zure bidalketak", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "{{$a}}ren iruzkina", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "{{$a}}ren kalifikazioa", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "{{$a}}. irizpidea", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Kalifikazio bat aukeratu behar duzu irizpide honetarako", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "{{$a}}ren iruzkina", - "addon.mod_workshop_assessment_comments.dimensionnumber": "{{$a}}. irizpidea", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "{{$a}}ren iruzkina", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "{{$a}}ren kalifikazioa", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "{{$a}}. baieztapena", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "{{$a}}. irizpidea", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Elementu hauetako bat aukeratu behar duzu", - "addon.notes.addnewnote": "Gehitu ohar berria", - "addon.notes.coursenotes": "Ikastaroaren oharrak", - "addon.notes.deleteconfirm": "Ezabatu ohar hau?", - "addon.notes.eventnotecreated": "Oharra sortu da", - "addon.notes.eventnotedeleted": "Oharra ezabatu da", - "addon.notes.nonotes": "Ez dago mota honetako oharrik", - "addon.notes.note": "Oharra", - "addon.notes.notes": "Oharrak", - "addon.notes.personalnotes": "Norberaren oharrak", - "addon.notes.publishstate": "Testuingurua", - "addon.notes.sitenotes": "Guneko oharrak", - "addon.notes.userwithid": "{{id}} IDa duen erabiltzailea", - "addon.notes.warningnotenotsent": "Ezin izan da {{course}} ikastaroan oharrak gehitu. {{error}}", - "addon.notifications.errorgetnotifications": "Errore bat gertatu da jakinarazpenak jasotzean.", - "addon.notifications.markallread": "Markatu guztiak irakurritako gisa", - "addon.notifications.notificationpreferences": "Jakinarazpenen hobespenak", - "addon.notifications.notifications": "Jakinarazpenak", - "addon.notifications.playsound": "Erreproduzitu soinua", - "addon.notifications.therearentnotificationsyet": "Ez dago jakinarazpenik.", - "addon.storagemanager.deletecourse": "Garbitu ikastaro guztietako datuak", - "addon.storagemanager.deletecourses": "Garbitu ikastaro guztien datuak", - "addon.storagemanager.deletedatafrom": "Garbitu {{name}}-(e)ko datuak", - "addon.storagemanager.info": "Zure gailuan gordetako fitxategiek aplikazioaren funtzionamendua azkartzen dute eta aplikazioa lineaz kanpo ere erabiltzeko aukera ematen dizute. Zure gailuan lekua egin behar baduzu fitxategi hauek arazorik gabe garbitu ditzakezu.", - "addon.storagemanager.managestorage": "Kudeatu biltegiratzea", - "addon.storagemanager.storageused": "Erabilitako biltegiratze-lekua:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Arabiar Emirerri Batuak", - "assets.countries.AF": "Afganistan", - "assets.countries.AG": "Antigua eta Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktika", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa", - "assets.countries.AT": "Austria", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland Irlak", - "assets.countries.AZ": "Azerbaijan", - "assets.countries.BA": "Bosnia eta Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgika", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Boliviako Estatu Plurinazionala", - "assets.countries.BQ": "Bonaire, Sint Eustatius eta Saba", - "assets.countries.BR": "Brasil", - "assets.countries.BS": "Bahamak", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvet Uhartea", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Bielorrusia", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Cocos (Keeling) Uharteak", - "assets.countries.CD": "Kongoko Errepublika Demokratikoa", - "assets.countries.CF": "Afrika Erdiko Errepublika", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Suitza", - "assets.countries.CI": "Côte D'Ivoire", - "assets.countries.CK": "Cook Uharteak", - "assets.countries.CL": "Txile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Txina", - "assets.countries.CO": "Kolonbia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Cabo Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Christmas Uharteak", - "assets.countries.CY": "Zipre", - "assets.countries.CZ": "Txekia", - "assets.countries.DE": "Alemania", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Danimarka", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Dominikar Errepublika", - "assets.countries.DZ": "Aljeria", - "assets.countries.EC": "Ekuador", - "assets.countries.EE": "Estonia", - "assets.countries.EG": "Egipto", - "assets.countries.EH": "Euskal Herria", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Espainia", - "assets.countries.ET": "Etiopia", - "assets.countries.FI": "Finlandia", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Falkland Uharteak (Malvinak)", - "assets.countries.FM": "Micronesiako Estatu Federatuak", - "assets.countries.FO": "Faroe Uharteak", - "assets.countries.FR": "Frantzia", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Erresuma Batua", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Frantziako Guyana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Greenland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Ginea", - "assets.countries.GP": "Guadalupe", - "assets.countries.GQ": "Ekuatore Ginea", - "assets.countries.GR": "Grezia", - "assets.countries.GS": "Hegoaldeko Georgiak eta Hegoaldeko Sandwichak uharteak", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Ginea Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Heard eta McDonald uharteak", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Kroazia", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Hungaria", - "assets.countries.ID": "Indonesia", - "assets.countries.IE": "Eire / Irlanda", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Man uhartea", - "assets.countries.IN": "India", - "assets.countries.IO": "British Indian Ocean Territory", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Irango Islamiar Errepublika", - "assets.countries.IS": "Islandia", - "assets.countries.IT": "Italia", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaika", - "assets.countries.JO": "Jordania", - "assets.countries.JP": "Japonia", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirgizistan", - "assets.countries.KH": "Kanbodia", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komoreak", - "assets.countries.KN": "Saint Kitts eta Nevis", - "assets.countries.KP": "Koreako Herri Errepublika Demokratikoa (Ipar Korea)", - "assets.countries.KR": "Koreako Errepublika (Hego Korea)", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Cayman Uharteak", - "assets.countries.KZ": "Kazakhstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Libano", - "assets.countries.LC": "Santa Luzia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lituania", - "assets.countries.LU": "Luxenburgo", - "assets.countries.LV": "Letonia", - "assets.countries.LY": "Libia", - "assets.countries.MA": "Maroko", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Moldaviako Errepublika", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin (uhartearen zati frantsesa)", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshall Uharteak", - "assets.countries.MK": "Ipar Mazedonia", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Birmania", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Makau", - "assets.countries.MP": "Northern Mariana Irlak", - "assets.countries.MQ": "Martinika", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritania", - "assets.countries.MV": "Maldivak", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexiko", - "assets.countries.MY": "Malaysia", - "assets.countries.MZ": "Mozambike", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Kaledonia Berria", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolk Uhartea", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nikaragua", - "assets.countries.NL": "Herbehereak", - "assets.countries.NO": "Norvegia", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Zeelanda Berria", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Frantziako Polinesia", - "assets.countries.PG": "Papua Ginea Berria", - "assets.countries.PH": "Filipinak", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polonia", - "assets.countries.PM": "St. Pierre eta Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestinako estatua", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguai", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Reunion", - "assets.countries.RO": "Errumania", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Errusia", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Saudi Arabia", - "assets.countries.SB": "Salomon Uharteak", - "assets.countries.SC": "Seychelleak", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Suedia", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Saint Helena, Ascension eta Tristan da Cunha", - "assets.countries.SI": "Eslovenia", - "assets.countries.SJ": "Svalbard eta Jan Mayen", - "assets.countries.SK": "Eslovakia", - "assets.countries.SL": "Sierra Leona", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Hego Sudan", - "assets.countries.ST": "Sao Tome eta Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (Herbeheretako zatia)", - "assets.countries.SY": "Siria", - "assets.countries.SZ": "Eswatini", - "assets.countries.TC": "Turks eta Caicos Uharteak", - "assets.countries.TD": "Txad", - "assets.countries.TF": "Frantziako Hego Lurraldeak", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailandia", - "assets.countries.TJ": "Tadjikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Ekialdeko Timor", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunisia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turkia", - "assets.countries.TT": "Trinidad eta Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzaniako Errepublika Batua", - "assets.countries.UA": "Ukraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "Ameriketako Estatu Batuak", - "assets.countries.UY": "Uruguai", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Vatikano Hiria", - "assets.countries.VC": "Saint Vicent eta Grenadinak", - "assets.countries.VE": "Venezuelako Bolibartar Errepublika", - "assets.countries.VG": "Virgin Uharteak (Britaniarrak)", - "assets.countries.VI": "Virgin Uharteak (AEB)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis eta Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Hegoafrika", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB e-liburua", - "assets.mimetypes.application/msword": "Word dokumentua", - "assets.mimetypes.application/pdf": "PDF dokumentua", - "assets.mimetypes.application/vnd.moodle.backup": "Moodleren segurtasun-kopia", - "assets.mimetypes.application/vnd.ms-excel": "Excel kalkulu-orria", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 lan-liburua makroak gaituta", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint aurkezpena", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument kalkulu-orria", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument kalkulu-orri txantiloia", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument testu dokumentua", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument testu txantiloia", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument web orri txantiloia", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 aukezpena", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 diapositiba-aurkezpena", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 kalkulu-orria", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 txantiloia", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 dokumentua", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote aurkezpena", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers kalkulu-orria", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages dokumentua", - "assets.mimetypes.application/x-javascript": "JavaScript iturriburua", - "assets.mimetypes.application/x-mspublisher": "Publisher dokumentua", - "assets.mimetypes.application/x-shockwave-flash": "Flash animazioa", - "assets.mimetypes.application/xhtml_xml": "XHTML dokumentua", - "assets.mimetypes.archive": "Artxiboa ({{$a.EXT}})", - "assets.mimetypes.audio": "Audio-fitxategia ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Fitxategia", - "assets.mimetypes.group:archive": "Artxiboko fitxategiak", - "assets.mimetypes.group:audio": "Audioo-fitxategiak", - "assets.mimetypes.group:document": "Dokumentu fitxategiak", - "assets.mimetypes.group:html_audio": "Nabigatzaileek berez onartzen dituzten audio fitxategiak", - "assets.mimetypes.group:html_track": "HTML pista fitxategia", - "assets.mimetypes.group:html_video": "Nabigatzaileek berez onartzen dituzten bideo-fitxategiak", - "assets.mimetypes.group:image": "Irudi-fitxategiak", - "assets.mimetypes.group:presentation": "Aurkezpen-fitxategiak", - "assets.mimetypes.group:sourcecode": "Iturburu kodea", - "assets.mimetypes.group:spreadsheet": "Kalkulu-orri fitxategiak", - "assets.mimetypes.group:video": "Bideo-fitxategeiak", - "assets.mimetypes.group:web_audio": "Webean erabilitako audio fitxategiak", - "assets.mimetypes.group:web_file": "Web fitxategiak", - "assets.mimetypes.group:web_image": "Webean erabilitako irudi fitxategiak", - "assets.mimetypes.group:web_video": "Webean erabilitako bideo-fitxategiak", - "assets.mimetypes.image": "Irudia ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows-eko ikonoa", - "assets.mimetypes.text/css": "Kaskadako estilo-orriak (CSS)", - "assets.mimetypes.text/csv": "Komaz banatutako balioak (CSV)", - "assets.mimetypes.text/html": "HTML dokumentua", - "assets.mimetypes.text/plain": "Testu-fitxategia", - "assets.mimetypes.text/rtf": "RTF dokumentua", - "assets.mimetypes.text/vtt": "Web Video Text Track", - "assets.mimetypes.video": "Bideo fitxategia ({{$a.EXT}})", - "core.accounts": "Kontuak", - "core.add": "Gehitu", - "core.agelocationverification": "Adina eta kokapenaren egiaztapena", - "core.ago": "Orain dela {{$a}}", - "core.all": "Guztiak", - "core.allgroups": "Talde guztiak", - "core.allparticipants": "Partaide guztiak", - "core.answer": "Erantzun", - "core.answered": "Erantzuna emanda", - "core.areyousure": "Ziur zaude?", - "core.back": "Atzera", - "core.block.blocks": "Blokeak", - "core.browser": "Nabigatzailea", - "core.cancel": "Utzi", - "core.cannotconnect": "Ezin izan da konektatu", - "core.cannotconnecttrouble": "Arazoak ditugu zure gunera konektatzeko.", - "core.cannotconnectverify": "Helbidea ondo dagoela egiaztatu ezazu.", - "core.cannotdownloadfiles": "Fitxategiak jaistea ezgaituta dago. Mesedez, jar zaitez harremanetan zure guneko kudeatzailearekin.", - "core.captureaudio": "Grabatu audioa", - "core.capturedimage": "Argazkia atera da.", - "core.captureimage": "Atera argazkia", - "core.capturevideo": "Grabatu bideoa", - "core.category": "Kategoria", - "core.choose": "Aukeratu", - "core.choosedots": "Aukeratu...", - "core.clearsearch": "Bilaketa garbia", - "core.clicktohideshow": "Egin klik zabaltzeko edo tolesteko", - "core.clicktoseefull": "Klik egin eduki guztiak ikusteko.", - "core.close": "Itxi", - "core.comments": "Iruzkinak", - "core.comments.addcomment": "Gehitu iruzkina...", - "core.comments.comments": "Iruzkinak", - "core.comments.commentscount": "Iruzkinak: ({{$a}})", - "core.comments.commentsnotworking": "Iruzkinak ezin dira eskuratu", - "core.comments.deletecommentbyon": "Ezabatu {{$a.user}}-(k) argitaratutako iruzkina {{$a.time}} datan", - "core.comments.eventcommentcreated": "Iruzkina sortu da", - "core.comments.eventcommentdeleted": "Iruzkina ezabatu da", - "core.comments.nocomments": "Iruzkinik ez", - "core.comments.savecomment": "Gorde iruzkina", - "core.comments.warningcommentsnotsent": "Ezin izan dira iruzkinak sinkronizatu. {{error}}", - "core.commentscount": "Iruzkinak: ({{$a}})", - "core.completion-alt-auto-fail": "Osatuta: {{$a}} (ez dute gutxieneko kalifikazioa lortu)", - "core.completion-alt-auto-n": "Osatu gabea: {{$a}}", - "core.completion-alt-auto-n-override": "Osatu gabea: {{$a.modname}} ({{$a.overrideuser}}-(e)k baliogabetua)", - "core.completion-alt-auto-pass": "Osatuta: {{$a}} (gutxieneko kalifikazioa lortu dute)", - "core.completion-alt-auto-y": "Osatuta: {{$a}}", - "core.completion-alt-auto-y-override": "Osatua: {{$a.modname}} ({{$a.overrideuser}}-(e)k baliogabetua)", - "core.completion-alt-manual-n": "Osatu gabea: {{$a}}. Aukeratu osatutzat markatzeko", - "core.completion-alt-manual-n-override": "Osatu gabea: {{$a.modname}} ({{$a.overrideuser}}-(e)k baliogabetua). Aukeratu osatutzat markatzeko.", - "core.completion-alt-manual-y": "Osatuta: {{$a}}. Aukeratu osatugabe gisa markatzeko", - "core.completion-alt-manual-y-override": "Osatua: {{$a.modname}} ({{$a.overrideuser}}-(e)k baliogabetua). Aukeratu osatu gabe gisa markatzeko.", - "core.confirmcanceledit": "Ziur zaude orri hau utzi nahi duzula? Aldaketa guztiak galduko dira.", - "core.confirmdeletefile": "Ziur zaude fitxategi hau ezabatu nahi duzula?", - "core.confirmgotabroot": "Ziur zaude {{name}}-(e)ra itzuli nahi duzula?", - "core.confirmgotabrootdefault": "Ziur zaude oraingo fitxaren hasiera-orrira joan nahi duzula?", - "core.confirmleaveunknownchanges": "Ziur zaude orri hau utzi nahi duzula? Gorde gabeko aldaketak dituzu eta galdu egingo dira.", - "core.confirmloss": "Ziur zaude? Aldaketa guztiak galdu egingo dira.", - "core.confirmopeninbrowser": "Nabigatzailean ireki nahi duzu?", - "core.considereddigitalminor": "Gazteegia zara gune honetan kontua sortzeko.", - "core.content": "Edukia", - "core.contenteditingsynced": "Editatzen ari zaren edukiak sinkronizatu dira.", - "core.contentlinks.chooseaccount": "Aukeratu kontua", - "core.contentlinks.chooseaccounttoopenlink": "Aukeratu esteka zabaltzeko erabiliko den kontua.", - "core.contentlinks.confirmurlothersite": "Esteka hau beste gune batekoa da. Ireki nahi duzu?", - "core.contentlinks.errornoactions": "Ezin izan da ekintzarik aurkitu esteka hau irekitzeko.", - "core.contentlinks.errornosites": "Ezin izan da gunerik aurkitu esteka hau irekitzeko.", - "core.contentlinks.errorredirectothersite": "URLak gune ezberdin batera berbideratzea ez dago baimenduta.", - "core.continue": "Jarraitu", - "core.copiedtoclipboard": "Testua arbelean kopiatu da", - "core.copytoclipboard": "Kopiatu arbelera", - "core.course": "Ikastaroa", - "core.course.activitydisabled": "Zure erakundeak jarduera hau mobile aplikazioan desgaitu du.", - "core.course.activitynotyetviewableremoteaddon": "Zure erakundeak oraindik aplikazioean onartzen ez den gehigarri bat instalatuta dauka..", - "core.course.activitynotyetviewablesiteupgradeneeded": "Zure erakundeko Moodle instalazioak eguneraketa bat behar du.", - "core.course.allsections": "Atal guztiak", - "core.course.askadmintosupport": "Jarri harremanetan zure guneko kudeatzailearekin eta esaiozu jarduera hau Moodle Mobile aplikazioan erabili nahi duzula.", - "core.course.availablespace": "Une honetan {{available}} libre inguru dituzu.", - "core.course.cannotdeletewhiledownloading": "Fitxategiak ezin dira ezabatu jarduera deskargatzen ari den bitartean. Itxaron ezazu mesedez deskarga amaitu arte.", - "core.course.confirmdeletemodulefiles": "Ziur zaude modulu honetako fitxategiak ezabatu nahi dituzula?", - "core.course.confirmdownload": "{{size}} jaistear zaude.{{availableSpace}} Ziur zaude aurrera egin nahi duzula?", - "core.course.confirmdownloadunknownsize": "Ezin izan dugu deskargaren tamaina kalkulatu.{{availableSpace}} Ziur zaude jaitsi nahi duzula?", - "core.course.confirmdownloadzerosize": "Deskargatzen hastear zaude.{{availableSpace}} Ziur zaude jarraitu nahi duzula?", - "core.course.confirmlimiteddownload": "Une honetan ez zaude Wi-Fira konektatuta.", - "core.course.confirmpartialdownloadsize": "Gutxienez {{size}} jaistera zoaz.{{availableSpace}} Ziur zaude jarraitu nahi duzula?", - "core.course.contents": "Edukiak", - "core.course.couldnotloadsectioncontent": "Ezin izan da ataleko edukia kargatu. Saiatu beranduago mesedez.", - "core.course.couldnotloadsections": "Ezin izan dira atalak kargatu. Saiatu beranduago mesedez.", - "core.course.coursesummary": "Ikastaroaren laburpena", - "core.course.downloadcourse": "Jaitsi ikastaroa", - "core.course.errordownloadingcourse": "Errorea ikastaroa jaistean.", - "core.course.errordownloadingsection": "Errorea atala jaistean.", - "core.course.errorgetmodule": "Errore bat gertatu da jardueraren datuak eskuratzean.", - "core.course.hiddenfromstudents": "Ezkutuan ikasleentzat", - "core.course.hiddenoncoursepage": "Eskuragarri baina ikastaro-orrian erakutsi gabe", - "core.course.insufficientavailablequota": "Zure gailuak ez du deskarga hau gordetzeko haina leku libre eskuragarri. Baliteke app eta sistemaren eguneraketentzako lekua erreserbatuta egotea. Deskarga egin aurretik lekua egin ezazu, mesedez.", - "core.course.insufficientavailablespace": "{{size}} deskargatzen saiatzen ari zara. Honek zure gailua normal funtzionatzeko behar duen lekurik gabe utziko luke. Deskarga egin aurretik lekua egin ezazu, mesedez.", - "core.course.manualcompletionnotsynced": "Eskuzko osatzea ez da sinkronizatu.", - "core.course.nocontentavailable": "Ez dago edukirik eskuragarri momentu honetan.", - "core.course.overriddennotice": "Jarduera honetako zure azken kalifikazioa eskuz egokitu da.", - "core.course.refreshcourse": "Freskatu ikastaroa", - "core.course.sections": "Atalak", - "core.course.useactivityonbrowser": "Hala ere zure gailuko nabigatzailea erabilita erabil dezakezu.", - "core.course.warningmanualcompletionmodified": "Jardueraren eskuzko osatzea gunean aldatua izan da.", - "core.course.warningofflinemanualcompletiondeleted": "'{{name}}' ikastaroaroko lineaz kanpoko eskuzko osatze batzuk ezabatuak izan dira. {{error}}", - "core.coursedetails": "Ikastaro-xehetasunak", - "core.coursenogroups": "Ez zara ikastaro honetako inongo taldeko kide.", - "core.courses.addtofavourites": "Nabarmendu ikastaro hau", - "core.courses.allowguests": "Ikastaro honetan bisitariak sar daitezke", - "core.courses.availablecourses": "Eskura dauden ikastaroak", - "core.courses.cannotretrievemorecategories": "{{$a}}. maila baino sakonagoko kategoriak ezin dira eskuratu.", - "core.courses.categories": "Ikastaro-kategoriak", - "core.courses.confirmselfenrol": "Ziur zaude ikastaro honetan izena eman nahi duzula?", - "core.courses.courses": "Ikastaroak", - "core.courses.downloadcourses": "Jaitsi ikastaroak", - "core.courses.enrolme": "Matrikulatu nazazu", - "core.courses.errorloadcategories": "Errore bat gertatu da kategoriak kargatzean.", - "core.courses.errorloadcourses": "Errore bat gertatu da ikastaroak kargatzean.", - "core.courses.errorloadplugins": "Ikastaro honek behar dituen pluginak ezin izan dira behar bezala kargatu. Mesedez aplikazioa berriz kargatu ezazu eta ondoren berriz saiatu zaitez.", - "core.courses.errorsearching": "Errorea gertatu da bilatzean.", - "core.courses.errorselfenrol": "Errore bat gertatu da matrikulazio automatikoa egitean", - "core.courses.filtermycourses": "Nire ikastaroak iragazi", - "core.courses.frontpage": "Hasiera-orria", - "core.courses.hidecourse": "Kendu ikuspegitik", - "core.courses.ignore": "Baztertu", - "core.courses.mycourses": "Nire ikastaroak", - "core.courses.mymoodle": "Aginte-panela", - "core.courses.nocourses": "Ez dago ikastaroei buruzko informaziorik", - "core.courses.nocoursesyet": "Ez dago ikastarorik kategoria honetan", - "core.courses.nosearchresults": "Emaitzarik ez", - "core.courses.notenroled": "Ez zaude matrikulatuta ikastaro honetan", - "core.courses.notenrollable": "Ezin duzu zeure burua ikastaro honetan matrikulatu.", - "core.courses.password": "Matrikulazio-giltza", - "core.courses.paymentrequired": "Ikastaro hau ordainpekoa da", - "core.courses.paypalaccepted": "Paypal ordainketak onartu dira", - "core.courses.reload": "Berriz kargatu", - "core.courses.removefromfavourites": "Ez nabarmendu ikastaro hau", - "core.courses.search": "Bilatu", - "core.courses.searchcourses": "Bilatu Ikastaroak", - "core.courses.searchcoursesadvice": "Bilatu ikastaroak botoia erabil dezakezu ikastaroak topatu eta bisitari gisa sartu edo bertan matrikulatzeko ikastaroak baimentzen badu.", - "core.courses.selfenrolment": "Matrikulazio automatikoa", - "core.courses.sendpaymentbutton": "Bidali ordainketa Paypal bidez", - "core.courses.show": "Berriz erakutsi ikastaro hau ikuspegian", - "core.courses.totalcoursesearchresults": "Ikastaroak guztira: {{$a}}", - "core.currentdevice": "Oraingo gailua", - "core.datastoredoffline": "Informazioa gailuan gorde da ezin izan delako bidali. Beranduago automatikoki bidaliko da.", - "core.date": "Data", - "core.day": "egun(a)", - "core.days": "egun", - "core.decsep": ",", - "core.defaultvalue": "Lehenetsitakoa ({{$a}})", - "core.delete": "Ezabatu", - "core.deletedoffline": "Lineaz kanpo ezabatu da", - "core.deleteduser": "Erabiltzailea ezabatu da", - "core.deleting": "Ezabatzen", - "core.description": "Deskribapena", - "core.desktop": "Mahaigaina", - "core.dfdaymonthyear": "YYYY-MM-DD", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Digitalki adin txikikoa", - "core.digitalminor_desc": "Eska iezaiozu zure guraso/tutore bati hurrengo pertsonarekin harremanetan jartzeko:", - "core.discard": "Baztertu", - "core.dismiss": "Baztertu", - "core.displayoptions": "Erakusteko aukerak", - "core.done": "Eginda", - "core.download": "Jaitsi", - "core.downloaded": "Deskargatuta", - "core.downloading": "Jaisten", - "core.edit": "Editatu", - "core.editor.autosavesucceeded": "Zirriborroa gorde da.", - "core.editor.bold": "Lodia", - "core.editor.clear": "Ezabatu formatua", - "core.editor.h3": "Izenburua (handia)", - "core.editor.h4": "Izenburua (ertaina)", - "core.editor.h5": "Izenburua (txikia)", - "core.editor.hidetoolbar": "Ezkutatu tresna-barra", - "core.editor.italic": "Etzana", - "core.editor.orderedlist": "Ordenatutako zerrenda", - "core.editor.p": "Paragrafoa", - "core.editor.strike": "Marratua", - "core.editor.textrecovered": "Testu honen zirriborroa automatikoki berrezarri da.", - "core.editor.toggle": "Etengailuaren editorea", - "core.editor.underline": "Azpimarratua", - "core.editor.unorderedlist": "Ordenatu gabeko zerrenda", - "core.emptysplit": "Orri hau hutsik agertuko da ezkerreko panela hutsik badago edo kargatzen ari bada.", - "core.error": "Errorea", - "core.errorchangecompletion": "Errorea gertatu da osaketa-egoera aldatzean. Mesedez saiatu berriz.", - "core.errordeletefile": "Errorea fitxategia ezabatzean. Mesedez, saiatu berriz.", - "core.errordownloading": "Errorea fitxategia jaistean.", - "core.errordownloadingsomefiles": "Errore bat gertatu da moduluaren fitxategiak jaistean. Fitxategi batzuk falta daitezke.", - "core.errorfileexistssamename": "Dagoeneko badago izen hori duen fitxategi bat.", - "core.errorinvalidform": "Formularioak baliozkoak ez diren datuak dauzka. Mesedez egiaztatu derrigorrezko eremuak bete dituzula eta datuak egokiak direla.", - "core.errorinvalidresponse": "Erantzun baliogabea jaso da. Errorea iraunkorra bada mesedez jar zaitez harremanetan zure guneko kudeatzailearekin .", - "core.errorloadingcontent": "Errorea edukia kargatzean.", - "core.errorofflinedisabled": "Lineaz kanpoko nabigazioa desgaituta dago zure gunean. Aplikazioa erabiltzeko Internetera konektaturik egon behar duzu.", - "core.erroropenfilenoapp": "Errorea fitxategia irekitzean: ez da aurkitu fitxategi mota hau irekitzeko aplikaziorik.", - "core.erroropenfilenoextension": "Errorea fitxategia irekitzean: fitxategiak ez dauka luzapenik.", - "core.erroropenpopup": "Jarduera hau leiho berri bat zabaltzen saiatzen ari da. Hau ez da app honetan onartzen.", - "core.errorrenamefile": "Errorea izena berrizendatzean. Mesedez, saiatu berriz.", - "core.errorsomedatanotdownloaded": "Jarduera hau jaitsi baduzu, kontuan izan zenbait datu ez direla deskaraga-prozesuan jaisten errendimendu eta datuen erabilera arrazoiak direla eta.", - "core.errorsync": "Errorea gertatu da sinkronizatzean. Mesedez, saiatu berriz.", - "core.errorsyncblocked": "{{$a}} hau ezin izan da orain sinkronizatu prozesu bat martxan dagoelako. Mesedez, saiatu berriz beranduago. Arazoa errepikatzen bada, saiatu aplikazioa berrabiarazten.", - "core.explanationdigitalminor": "Informazio hau beharrezkoa da zure adina onespen-adin digitaletik goragokoa dela egiaztatzeko. Adin honetara heltzean norbanakoek baldintzei onespena eman diezaiekete eta ondorioz euren datuak legalki gorde eta prozesatu daitezke.", - "core.favourites": "Nabarmendutakoak", - "core.filename": "Fitxategiaren izena", - "core.filenameexist": "Fitxategi-izena dagoeneko existitzen da: {{$a}}", - "core.filenotfound": "Ez da fitxategia aurkitu, sentitzen dugu.", - "core.fileuploader.addfiletext": "Gehitu fitxategia", - "core.fileuploader.audio": "Audioa", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "{{size}} igotzear zaude. Ziur zaude aurrera egin nahi duzula?", - "core.fileuploader.confirmuploadunknownsize": "Ezin izan dugu igotzera zoazenaren tamaina kalkulatu. Ziur zaude jarraitu nahi duzula?", - "core.fileuploader.errorcapturingaudio": "Errorea audioa kapturatzean", - "core.fileuploader.errorcapturingimage": "Errorea irudia kapturatzean", - "core.fileuploader.errorcapturingvideo": "Errorea bideoa kapturatzean", - "core.fileuploader.errorgettingimagealbum": "Errorea irudia bildumatik jasotzean.", - "core.fileuploader.errormustbeonlinetoupload": "Online egon behar zara fitxategiak igotzeko.", - "core.fileuploader.errornoapp": "Ez daukazu ekintza hau burutzeko aplikazioik instalatuta.", - "core.fileuploader.errorreadingfile": "Errorea fitxategia irakurtzean.", - "core.fileuploader.errorwhileuploading": "Errorea gertatu da fitxategia igotzean.", - "core.fileuploader.file": "Fitxategia", - "core.fileuploader.filesofthesetypes": "Onartutako fitxategi-motak:", - "core.fileuploader.fileuploaded": "Fitxategia ondo igo da.", - "core.fileuploader.invalidfiletype": "{{$a}} fitxategi-mota ezin da onartu.", - "core.fileuploader.maxbytesfile": "{{$a.file}} fitxategia oso handia da. Igo ditzakezun fitxategien gehienezko tamaina {{$a.size}} da.", - "core.fileuploader.more": "Gehiago", - "core.fileuploader.photoalbums": "Argazki-bildumak", - "core.fileuploader.readingfile": "Fitxategia irakurtzen", - "core.fileuploader.readingfileperc": "Fitxategia irakurtzen: %{{$a}}", - "core.fileuploader.selectafile": "Aukeratu fitxategi bat", - "core.fileuploader.uploadafile": "Igo fitxategia", - "core.fileuploader.uploading": "Igotzen", - "core.fileuploader.uploadingperc": "Igotzen: %{{$a}}", - "core.fileuploader.video": "Bideoa", - "core.filter": "Iragazi", - "core.folder": "Karpeta", - "core.forcepasswordchangenotice": "Jarraitzeko zure pasahitza aldatu behar duzu.", - "core.fulllistofcourses": "Ikastaro guztiak", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Batez bestekoa", - "core.grades.badgrade": "Emandako kalifikazioak ez du balio", - "core.grades.contributiontocoursetotal": "Ikastaroaren denerako ekarpena", - "core.grades.feedback": "Feedbacka", - "core.grades.grade": "Kalifikazioa", - "core.grades.gradeitem": "Kalifikazio-elementua", - "core.grades.grades": "Kalifikazioak", - "core.grades.lettergrade": "Letren bidezko kalifikazioa", - "core.grades.nogradesreturned": "Ez da kalifikaziorik itzuli", - "core.grades.nooutcome": "Ez dago ikaste-emaitzarik", - "core.grades.percentage": "Ehunekoa", - "core.grades.range": "Ibiltartea", - "core.grades.rank": "Sailkapena", - "core.grades.weight": "Pisua", - "core.group": "Taldea", - "core.groupsseparate": "Taldeek ezin elkar ikusi", - "core.groupsvisible": "Taldeek elkar ikusten dute", - "core.h5p.additionallicenseinfo": "Lizentziari buruzko edozein informazio gehigarri", - "core.h5p.author": "Egilea", - "core.h5p.authorcomments": "Egilearen iruzkinak", - "core.h5p.authorcommentsdescription": "Edukiaren inguruko egilearen iruzkinak (testu hau ez da copyright-aren zati gisa publikatuko).", - "core.h5p.authorname": "Egilearen izena", - "core.h5p.authorrole": "Egilearen rola", - "core.h5p.by": "egilea", - "core.h5p.cancellabel": "Utzi", - "core.h5p.ccattribution": "Aitortu (CC BY)", - "core.h5p.ccattributionnc": "Aitortu-EzKomertziala (CC BY-NC)", - "core.h5p.ccattributionncnd": "Aitortu-EzKomertziala-LanEratorririkGabe (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Aitortu-EzKomertziala-PartekatuBerdin (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Aitortu-LanEratorririkGabe (CC BY-ND)", - "core.h5p.ccattributionsa": "Aitortu-PartekatuBerdin (CC BY-SA)", - "core.h5p.ccpdd": "Domeinu Publikoaren Lagapena (CC0)", - "core.h5p.changedby": "Aldaketaren egilea:", - "core.h5p.changedescription": "Aldaketaren deskribapena", - "core.h5p.changelog": "Aldaketa-erregistroa", - "core.h5p.changeplaceholder": "Argazkia moztu da, testua aldatu da...", - "core.h5p.close": "Itxi", - "core.h5p.confirmdialogbody": "Baieztatu ezazu mesedez aurrera egin nahi duzula. Ekintza honek ez du atzera bueltarik.", - "core.h5p.confirmdialogheader": "Baieztatu ekintza", - "core.h5p.confirmlabel": "Baieztatu", - "core.h5p.connectionLost": "Konexioa galdu da. Emaitzak gordeko dira eta konexioa berreskuratzen denean bidaliko dira.", - "core.h5p.connectionReestablished": "Konexioa berreskuratu da.", - "core.h5p.contentCopied": "Edukia arbelera kopiatzen da", - "core.h5p.contentchanged": "Eduki hau aldatu da azkenengoz erabili zenuenetik.", - "core.h5p.contenttype": "Eduki mota", - "core.h5p.copyright": "Erabilera eskubideak", - "core.h5p.copyrightinfo": "Copyright informazioa", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "Ikusi eduki honen copyright informazioa.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Data", - "core.h5p.disablefullscreen": "Desgaitu pantaila osoa", - "core.h5p.download": "Jaitsi", - "core.h5p.downloadtitle": "Jaitsi eduki hau H5P fitxategi gisa", - "core.h5p.editor": "Editorea", - "core.h5p.embed": "Enbotatu", - "core.h5p.embedtitle": "Ikusi eduki honen enbotatutako kodea.", - "core.h5p.errorgetemail": "Errorea erabiltzailearen helbide elektronikoa eskuratzean. Egiaztatu ezazu zure konexioa eta berriz saiatu zaitez.", - "core.h5p.fullscreen": "Pantaila osoa", - "core.h5p.gpl": "GNU Lizentzia Publiko Orokorraren 3. bertsioa", - "core.h5p.h5ptitle": "Ikusi h5p.org eduki gehiago ezagutzeko.", - "core.h5p.hideadvanced": "Ezkutatu aurreratuak", - "core.h5p.license": "Lizentzia", - "core.h5p.licenseCC010": "CC0 1.0 Unibertsala (CC0 1.0) Domeinu Publikoaren Lagapena", - "core.h5p.licenseCC010U": "CC0 1.0 Unibertsala", - "core.h5p.licenseCC10": "1.0 Generikoa", - "core.h5p.licenseCC20": "2.0 Generikoa", - "core.h5p.licenseCC25": "2.5 Generikoa", - "core.h5p.licenseCC30": "3.0 Moldatu gabea", - "core.h5p.licenseCC40": "4.0 Nazioartekoa", - "core.h5p.licenseGPL": "GNU Lizentzia Publiko Orokorra", - "core.h5p.licenseV1": "1. bertsioa", - "core.h5p.licenseV2": "2. bertsioa", - "core.h5p.licenseV3": "3. bertsioa", - "core.h5p.licensee": "Baimendutakoa", - "core.h5p.licenseextras": "Lizentzia gehigarriak", - "core.h5p.licenseversion": "Lizentziaren bertsioa", - "core.h5p.nocopyright": "Ez dago copyright informaziorik eskuragarri eduki honetarako.", - "core.h5p.offlineDialogBody": "Ezin izan dugu zeregin honen osaketaren inguruko informaziorik bidali. Egiaztatu ezazu mesedez zure Internet-erako konexioa.", - "core.h5p.offlineDialogHeader": "Zerbitzarirako zure konexioa eten egin da", - "core.h5p.offlineDialogRetryButtonLabel": "Berriz saiatu orain", - "core.h5p.offlineDialogRetryMessage": "Berriz saiatuko :num-(e)tan...", - "core.h5p.offlineSuccessfulSubmit": "Emaitzak ondo bidali dira.", - "core.h5p.offlinedisabled": "Guneak ez du H5P paketeen deskarga baimentzen.", - "core.h5p.originator": "Sortzailea", - "core.h5p.pd": "Domeinu Publikoa", - "core.h5p.pddl": "Domeinu Publikoaren Lagapena eta Lizentzia", - "core.h5p.pdm": "Domeinu Publikoaren Marka (PDM)", - "core.h5p.play": "Erreproduzitu H5P", - "core.h5p.resizescript": "Txertatu ezazu script hau zure webgunean enbotatutako edukiaren tamaina dinamikoki egokitzeko:", - "core.h5p.resubmitScores": "Gordetako emaitzak bidaltzen saiatzen.", - "core.h5p.reuse": "Berrerabili", - "core.h5p.reuseContent": "Berrerabili edukia", - "core.h5p.reuseDescription": "Berrerabili eduki hau.", - "core.h5p.showadvanced": "Erakutsi aurreratuak", - "core.h5p.showless": "Erakutsi gutxiago", - "core.h5p.showmore": "Erakutsi gehiago", - "core.h5p.size": "Tamaina", - "core.h5p.source": "Iturria", - "core.h5p.startingover": "Berriz hasiko zara.", - "core.h5p.sublevel": "Azpimaila", - "core.h5p.thumbnail": "Irudi txikia", - "core.h5p.title": "Izenburua", - "core.h5p.undisclosed": "Jakinarazi gabea", - "core.h5p.year": "Urtea", - "core.h5p.years": "Urtea(k)", - "core.h5p.yearsfrom": "Urteak (ordutik)", - "core.h5p.yearsto": "Urteak (heltzeko)", - "core.hasdatatosync": "{{$a}} honek sinkronizatu beharreko lineaz kanpoko informazioa du.", - "core.help": "Laguntza", - "core.hide": "Ezkutatu", - "core.hour": "ordu", - "core.hours": "ordu", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Irudia", - "core.imageviewer": "Irudi ikuskatzailea", - "core.info": "Informazioa", - "core.invalidformdata": "Datu-formularioa ez da zuzena", - "core.labelsep": ":", - "core.lastaccess": "Azken sarrera", - "core.lastdownloaded": "Azkenik jaitsita", - "core.lastmodified": "Azken aldaketa", - "core.lastsync": "Azken sinkronizazioa", - "core.layoutgrid": "Laukia", - "core.list": "Zerrendatu", - "core.listsep": ";", - "core.loading": "Kargatzen", - "core.loadmore": "Kargatu gehiago", - "core.location": "Kokapena", - "core.login.auth_email": "Posta elektronikoan oinarritutako auto-erregistroa", - "core.login.authenticating": "Egiaztatzen", - "core.login.cancel": "Utzi", - "core.login.changepassword": "Aldatu pasahitza", - "core.login.changepasswordbutton": "Ireki pasahitza aldatzeko orria", - "core.login.changepasswordhelp": "Pasahitza aldatzerakoan arazoak badituzu, jarri harremanetan mesedez zure guneko kudeatzailearekin. \"Guneko Kudeatzaileak\" zure eskola/unibertsitate/enpresa edo hezkuntza erakundean Moodle kudeatzen dutenak dira. Ez badakizu nola jarri harremanetan eurekin galdetu zure irakasle edo trebatzaileei.", - "core.login.changepasswordinstructions": "Ezin duzu pasahitza aplikaziotik aldatu. Egin klik mesedez hurrengo botoian gunea nabigatzaile batean ireki eta bertatik pasahitza aldatzeko. Kontuan izan pasahitza aldatu ondoren ez zarela aplikaziora automatikoki bideratua izango eta beraz aplikaziora itzultzeko nabigatzailea itxi beharko duzu.", - "core.login.changepasswordlogoutinstructions": "Gunea aldatu edo saioa amaitu nahi baduzu, egin klik hurrengo botoian:", - "core.login.changepasswordreconnectinstructions": "Egin klik hurrengo botoian gunera berriz konektatzeko. (Kontuan izan pasahitza aldatzea lortu ez baduzu aurreko pantailara itzuliko zarela)", - "core.login.confirmdeletesite": "Ziur zaude {{sitename}} gunea ezabatu nahi duzula?", - "core.login.connect": "Konektatu!", - "core.login.connecttomoodle": "Konektatu Moodlera", - "core.login.connecttomoodleapp": "Moodle gune arrunt batera konektatzen saiatzen ari zara. Jaitsi ezazu mesedez Moodle aplikazio ofiziala Moodle gune horretara sartzeko.", - "core.login.connecttoworkplaceapp": "Moodle Workplace gune batera konektatzen saiatzen ari zara. Jaitsi ezazu mesedez Moodle Workplace aplikazioa Moodle gune horretara sartzeko.", - "core.login.contactyouradministrator": "Zure guneko kudeatzailearekin harremanetan jarri laguntza gehiagorako.", - "core.login.contactyouradministratorissue": "Mesedez, eskatu zure guneko kudeatzaileari hurrengo arazoa ikuskatu dezala: {{$a}}", - "core.login.createaccount": "Sortu nire kontu berria", - "core.login.createuserandpass": "Aukeratu zure erabiltzaile-izena eta pasahitza", - "core.login.credentialsdescription": "Mesedez saioa hasteko zure sartu erabiltzaile eta pasahitza.", - "core.login.emailconfirmsent": "

                E-mail bat bidali dugu zure hurrengo helbide honetara: {{$a}}

                \n

                Izena ematen amaitzeko argibide erraz batzuk ditu.

                \n

                Arazorik baduzu, jarri harremanetan kudeatzailearekin.

                ", - "core.login.emailconfirmsentnoemail": "

                Zure helbidera e-posta mezu bat bidali da.

                Bertan erregistroa amaitzeko argibide errazak aurkituko dituzu.

                Arazoekin jarraitzen baduzu jarri zaitez harremanetan zure guneko kudeatzailearekin.

                ", - "core.login.emailconfirmsentsuccess": "Baieztatze e-maila ondo bidali da", - "core.login.emailnotmatch": "E-posta helbideak ez datoz bat", - "core.login.erroraccesscontrolalloworigin": "Saiatzen ari zaren cross-origin deia ez da onartu. Ikusi mesedez https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Errorea gertatu da gunea ezabatzean. Mesedez saiatu beranduago.", - "core.login.errorexampleurl": "https://campus.adibidea.eus URLa adibide bat baino ez da, ez da benetako gune bat. Erabili ezazu mesedez zure eskola edo erakundearen gunearen URLa.", - "core.login.errorupdatesite": "Errore bat gertatu da guneko token-a eguneratzean.", - "core.login.faqcannotconnectanswer": "Jarri zaitez harremanetan mesedez zure guneko kudeatzailearekin.", - "core.login.faqcannotconnectquestion": "Nire guneko helbidea ondo idatzi dut baina ezin dut konektatu.", - "core.login.faqcannotfindmysiteanswer": "Izena ondo idatzi duzu? Baliteke zure gunea gure gune publikoen direktorioan ez egotea. Aurkitu gabe jarraitzen baduzu, horren ordez zure guneko helbidea idatzi ezazu.", - "core.login.faqcannotfindmysitequestion": "Ezin dut nire gunea aurkitu.", - "core.login.faqsetupsiteanswer": "Egin klik {{$link}} zure Moodle gunea sortzeko eskura dituzun aukera ezberdinak ezagutzeko.", - "core.login.faqsetupsitelinktitle": "Hasi zaitez.", - "core.login.faqsetupsitequestion": "Nire Moodle gunea martxan jarri nahi dut.", - "core.login.faqtestappanswer": "Aplikazioa Moodle Demo Gunean probatzeko, idatzi ezazu \"teacher\" edo \"student\" \"Zure gunea\" eremuan eta egin klik \"Konektatu zure gunera\" botoian.", - "core.login.faqtestappquestion": "Aplikazioa probatu baino ez dut egin nahi, hori egin al dezaket?", - "core.login.faqwhatisurlanswer": "

                Erakunde bakoitzak bere Moodle gunerako helbide bakarra edo URLa dauka. Helbidea aurkitzeko:

                \n
                  \n
                1. Ireki web-nabigatzaile bat eta joan Moodle gunearen hasiera-orrira.
                2. \n
                3. Leihoaren goiko aldean, helbide-barran, zure Moodle gunearen helbidea ikusiko duzu. Adibidez \"campus.adibidea.eus\".
                  {{$image}}
                4. \n
                5. Kopiatu ezazu helbide hori (ez kopiatu /login eta horren atzetik dagoena), itsatsi ezazu Moodle aplikazioan eta egin klik \"Konektatu zure gunera\" botoian
                6. \n
                7. Orain zure gunean sartu ahalko zara zure erabiltzaile-izena eta pasahitza erabilita.
                8. \n
                ", - "core.login.faqwhatisurlquestion": "Zein da nire gunearen helbidea? Nola aurkitu dezaket nire gunearen URLa?", - "core.login.faqwhereisqrcode": "Non aurkitu dezaket QR kodea?", - "core.login.faqwhereisqrcodeanswer": "

                Zure erakundeak gaitu badu, QR kodea zure profil-orriaren webgunearen azpiko aldean aurkituko duzu.

                {{$image}}", - "core.login.findyoursite": "Aurkitu zure gunea", - "core.login.firsttime": "Hau al da zure lehen aldia hemen?", - "core.login.forcepasswordchangenotice": "Jarraitzeko zure pasahitza aldatu behar duzu.", - "core.login.forgotten": "Zure erabiltzaile-izena edo pasahitza ahaztu duzu?", - "core.login.help": "Laguntza", - "core.login.helpmelogin": "

                Mundu osoan milaka Moodle gune dago. App honek soilik Mobile sarbidea espresuki gaitu duten guneetara konektatu daiteke.

                Zure Moodle gunera ezin bazara konektatu zure guneko kudeatzailearekin harremanetan jarri beharko zara eta eskatu http://docs.moodle.org/en/Mobile_app irakurtzeko

                Aplikazioa probetarako Moodle ingurunean probatzeko idatzi teacher edo student Gunearen helbidea eremuan eta egin klik Konektatu! botoian.

                ", - "core.login.instructions": "Argibideak", - "core.login.invalidaccount": "Zure erabiltzaile eta pasahitza egiaztatu itzazu edo zure guneko administratzaileari guneko ezarpenak egiaztatzeko eskatu.", - "core.login.invaliddate": "Data ezegokia", - "core.login.invalidemail": "Helbide elektroniko baliogabea", - "core.login.invalidmoodleversion": "

                Moodle bertsio baliogabea. Moodle aplikazioak soilik {{$a}} bertsiotik aurrerakoak onartzen ditu.

                \n

                Zure guneko kudeatzaileekin harremanetan jarri zaitezke zuen Moodle sistema eguneratu dezaten eskatzeko.

                \n

                \"Guneko Kudeatzaileak\" zure eskola/unibertsitate/enpresa edo hezkuntza erakundean Moodle kudeatzen dutenak dira. Ez badakizu nola jarri harremanetan eurekin galdetu zure irakasle edo trebatzaileei.

                ", - "core.login.invalidsite": "Guneko URLa ez da zuzena", - "core.login.invalidtime": "Ordu baliogabea", - "core.login.invalidurl": "Balio ez duen URLa ezarri da", - "core.login.invalidvaluemax": "Gehieneko balioa {{$a}} da.", - "core.login.invalidvaluemin": "Gutxieneko balioa {{$a}} da.", - "core.login.localmobileunexpectedresponse": "Moodle Mobile-ko Funtzio Aurreratuen kontrolak ezusteko erantzuna eman du. Mobile zerbitzu estandarra erabilita autentifikatuko zaitugu.", - "core.login.loggedoutssodescription": "Berriz autentifikatu behar duzua. Gunean nabigatzaile leiho baten bitartez hasi behar duzu saioa.", - "core.login.login": "Sartu", - "core.login.loginbutton": "Hasi saioa", - "core.login.logininsiterequired": "Gunean web-nabigatzaile baten bidez sartu behar zara.", - "core.login.loginsteps": "Gune honetara sarbide osoa izateko, lehenik eta behin kontua sortu behar duzu.", - "core.login.missingemail": "Helbide elektronikoa falta da", - "core.login.missingfirstname": "Izena falta da", - "core.login.missinglastname": "Abizena falta da", - "core.login.mobileservicesnotenabled": "Mobile bidezko sarbidea gaitu gabe dago zure gunean. Mesedez, jar zaitez harremanetan guneko kudeatzailearekin, mobile sarbidea gaitu behar dela uste baduzu.", - "core.login.mustconfirm": "Zure kontua baieztatu behar dituzu", - "core.login.newaccount": "Kontu berria", - "core.login.notloggedin": "Autentifikaturik egon behar duzu.", - "core.login.onboardingcreatemanagecourses": "Sortu eta kudeatu zure ikastaroak", - "core.login.onboardingenrolmanagestudents": "Matrikulatu eta kudeatu zure ikasleak", - "core.login.onboardinggetstarted": "Moodlekin hasi zaitez", - "core.login.onboardingialreadyhaveasite": "Dagoeneko Moodle gune bat daukat", - "core.login.onboardingimalearner": "Ikaslea naiz", - "core.login.onboardingimaneducator": "Hezitzailea naiz", - "core.login.onboardingineedasite": "Moodle gune bat behar dut", - "core.login.onboardingprovidefeedback": "Eman feedbacka", - "core.login.onboardingtoconnect": "Moodle aplikaziora konektatzeko Moodle gune bat beharko duzu", - "core.login.onboardingwelcome": "Ongi etorria Moodle aplikaziora!", - "core.login.or": "EDO", - "core.login.password": "Pasahitza", - "core.login.passwordforgotten": "Ahaztutako pasahitza", - "core.login.passwordforgotteninstructions2": "Zure pasahitza berritzeko, bidali zure erabiltzaile-izena edo zure helbide elektronikoa. Datu-basean aurkitzen bazaitugu, zure helbide elektronikora e-maila bidaliko dizugu berriz sartzeko argibideekin.", - "core.login.passwordrequired": "Pasahitza behar da", - "core.login.policyaccept": "Ulertu dut eta ados nago", - "core.login.policyagree": "Gune hau erabiltzen jarraitu aurretik politika honi onespena eman behar diozu. Ados al zaude?", - "core.login.policyagreement": "Guneko politiken onespena", - "core.login.policyagreementclick": "Guneko politikak irakurtzeko esteka", - "core.login.potentialidps": "Sartu beste kontu bat erabiliz:", - "core.login.profileinvaliddata": "Balio ezegokia", - "core.login.recaptchachallengeimage": "reCAPTCHA erronkaren irudia", - "core.login.recaptchaexpired": "Egiaztatzea iraungitu da. Erantzun berriz ere segurtasun-galdera.", - "core.login.recaptchaincorrect": "Segurtasun-galderaren erantzuna okerra da.", - "core.login.reconnect": "Berriz konektatu", - "core.login.reconnectdescription": "Zure autentikazio-token-a ez da baliozkoa edo iraungitu da. Gunera berriz konektatu beharko zara.", - "core.login.reconnectssodescription": "Zure autentikazio-token-a ez da baliozkoa edo iraungitu da. Gunera berriz konektatu beharko duzu. Gunean web-nabigatzaile baten bidez sartu behar duzu.", - "core.login.resendemail": "Berriz bidali e-maila", - "core.login.searchby": "Bilatu honen arabera:", - "core.login.security_question": "Segurtasun-galdera", - "core.login.selectacountry": "Aukeratu herrialde bat", - "core.login.selectsite": "Aukeratu mesedez zure gunea:", - "core.login.signupplugindisabled": "{{$a}} ez dago gaituta.", - "core.login.siteaddress": "Zure gunea", - "core.login.sitehasredirect": "Zure gunean HTTP berbideraketaren bat dauka. Aplikazioak ezin ditu berbideraketak jarraitu, hau izan daiteke zure gunera konektatzea ekiditen duen arazoa.", - "core.login.siteinmaintenance": "Zure gunea mantenu-moduan dago", - "core.login.sitepolicynotagreederror": "Ez da guneko politika onartu.", - "core.login.siteurl": "Gunearen URLa", - "core.login.siteurlrequired": "Gunearen URLa behar da, http://www.zuremoodlegunea.eus adibidez", - "core.login.startsignup": "Sortu kontu berri bat", - "core.login.stillcantconnect": "Oraindik ezin zara konektatu?", - "core.login.supplyinfo": "Xehetasun gehiago", - "core.login.username": "Erabiltzaile-izena", - "core.login.usernameoremail": "Idatzi erabiltzaile-izena edo helbide elektronikoa", - "core.login.usernamerequired": "Erabiltzailea behar da", - "core.login.usernotaddederror": "Erabiltzailea ez da gehitu - errorea", - "core.login.visitchangepassword": "Gunera joan nahi duzu pasahitza aldatzeko?", - "core.login.webservicesnotenabled": "Zure guneak web zerbitzuak gaitu gabe dauzka. Laguntza eskuratzeko zure guneko kudeatzailearekin harremanetan jarri zaitez.", - "core.login.youcanstillconnectwithcredentials": "Bestela gunera zure erabiltzaile-izen eta pasahitza erabiliz sartu zaitezke.", - "core.login.yourenteredsite": "Konektatu zure gunera", - "core.lostconnection": "Zure autentikazio-token-a ez da baliozkoa edo iraungitu da. Gunera berriz konektatu beharko duzu.", - "core.mainmenu.changesite": "Aldatu gunea", - "core.mainmenu.help": "Laguntza", - "core.mainmenu.logout": "Irten", - "core.mainmenu.website": "Webgunea", - "core.maxsizeandattachments": "Fitxategientzako gehienezko tamaina: {{$a.size}}, gehienezko fitxategi-kopurua: {{$a.attachments}}", - "core.min": "minutu", - "core.mins": "minutu", - "core.misc": "Bestelakoak", - "core.mod_assign": "Zeregina", - "core.mod_assignment": "Zeregina 2.2 (Desgaituta)", - "core.mod_book": "Liburua", - "core.mod_chat": "Txat-gela", - "core.mod_choice": "Kontsulta", - "core.mod_data": "Datu-basea", - "core.mod_database": "Datu-basea", - "core.mod_external-tool": "Kanpoko tresna", - "core.mod_feedback": "Inkesta", - "core.mod_file": "Fitxategia", - "core.mod_folder": "Karpeta", - "core.mod_forum": "Foroa", - "core.mod_glossary": "Glosarioa", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS eduki-paketea", - "core.mod_imscp": "IMS eduki-paketea", - "core.mod_label": "Etiketa", - "core.mod_lesson": "Ikasgaia", - "core.mod_lti": "Kanpoko tresna", - "core.mod_page": "Orria", - "core.mod_quiz": "Galdetegia", - "core.mod_resource": "Fitxategia", - "core.mod_scorm": "SCORM paketea", - "core.mod_survey": "Hausnarketa", - "core.mod_url": "URLa", - "core.mod_wiki": "Wikia", - "core.mod_workshop": "Tailerra", - "core.moduleintro": "Deskribapena", - "core.more": "gehiago", - "core.mygroups": "Nire taldeak", - "core.name": "Izena", - "core.needhelp": "Laguntzarik behar duzu?", - "core.networkerroriframemsg": "Eduki hau ez dago eskuragarri lineaz kanpo. Internetera konektatu zaitez eta ondoren saiatu zaitez berriro, mesedez.", - "core.networkerrormsg": "Arazo bat izan da gunearekin konektatzerakoan. Mesedez egiaztatu zure konexioa eta ondoren berriz saiatu zaitez.", - "core.never": "Inoiz ez", - "core.next": "Hurrengoa", - "core.no": "Ez", - "core.nocomments": "Iruzkinik ez", - "core.nograde": "Kalifikaziorik ez", - "core.none": "Bat ere ez", - "core.nooptionavailable": "Ez dago eskuragarri dagoen aukerarik", - "core.nopasswordchangeforced": "Ezin duzu jarraitu zure pasahitza aldatu gabe.", - "core.nopermissionerror": "Sentitzen dugu, baina une honetan ez duzu hori egiteko baimenik.", - "core.nopermissions": "Sentitzen dugu, baina oraingoz ez duzu hori egiteko baimenik ({{$a}}).", - "core.noresults": "Emaitzarik ez", - "core.noselection": "Aukeratu gabe", - "core.notapplicable": "e/a", - "core.notenrolledprofile": "Profil hau ez da eskuragarria erabiltzailea ez baitago ikastaro honetan matrikulatuta.", - "core.notice": "Abisua", - "core.notingroup": "Sentitzen dugu, baina taldekide izan behar duzu orri hau ikusteko.", - "core.notsent": "Bidali gabea", - "core.now": "Orain", - "core.nummore": "{{$a}} gehiago", - "core.numwords": "{{$a}} hitz", - "core.offline": "Lineaz kanpo", - "core.ok": "Ados", - "core.online": "On-line", - "core.openfullimage": "Klik egin hemen irudia jatorrizko tamainan ikusteko.", - "core.openinbrowser": "Ireki nabigatzailean", - "core.openmodinbrowser": "Ireki {{$a}} nabigatzailean", - "core.othergroups": "Beste taldeak", - "core.pagea": "{{$a}}. orria", - "core.paymentinstant": "Erabili beheko botoia ordaindu eta minutu gutxitan matrikulatzeko!", - "core.percentagenumber": "%{{$a}}", - "core.phone": "Telefonoa", - "core.pictureof": "{{$a}}-ren irudia", - "core.previous": "Aurrekoa", - "core.proceed": "Jarraitu", - "core.pulltorefresh": "Sakatu freskatzeko", - "core.qrscanner": "QR eskanerra", - "core.question.answer": "Erantzuna", - "core.question.answersaved": "Erantzuna gorde da", - "core.question.cannotdeterminestatus": "Ezin izan da egoera zehaztu.", - "core.question.certainty": "Ziurtasuna", - "core.question.complete": "Osatu", - "core.question.correct": "Zuzena", - "core.question.errorattachmentsnotsupported": "Aplikazioak oraindik ez du erantzunei fitxategiak eranstea onartzen.", - "core.question.errorinlinefilesnotsupported": "Aplikazioak oraindik ez du fitxategien lerro-arteko edizioa onartzen.", - "core.question.errorquestionnotsupported": "Galdera mota hau ez dago aplikazioan onartuta: {{$a}}", - "core.question.feedback": "Feedbacka", - "core.question.howtodraganddrop": "Sakatu aukeratzeko eta ondoren sakatu ezabatzeko.", - "core.question.incorrect": "Okerra", - "core.question.information": "Informazioa", - "core.question.invalidanswer": "Erantzuna ez dago osorik", - "core.question.notanswered": "Erantzun gabea", - "core.question.notyetanswered": "Erantzun gabea", - "core.question.partiallycorrect": "Zuzena zati batean", - "core.question.questionmessage": "{{$a}} galdera: {{$b}}", - "core.question.questionno": "{{$a}}. galdera", - "core.question.requiresgrading": "Kalifikazioa behar du", - "core.quotausage": "Une honetan {{$a.used}} erabili dituzu, eta zure muga {{$a.total}} da.", - "core.rating.aggregateavg": "Puntuazioen batez bestekoa", - "core.rating.aggregatecount": "Puntuazio-kontaketa", - "core.rating.aggregatemax": "Gehienezko puntuazioa", - "core.rating.aggregatemin": "Gutxieneko puntuazioa", - "core.rating.aggregatesum": "Puntuazioen batuketa", - "core.rating.noratings": "Ez da puntuaziorik bidali", - "core.rating.rating": "Puntuazioa", - "core.rating.ratings": "Puntuazioak", - "core.redirectingtosite": "Gunera berbideratua izango zara.", - "core.refresh": "Freskatu", - "core.remove": "Kendu", - "core.removefiles": "Ezabatu fitxategiak {{$a}}", - "core.required": "Beharrezkoa", - "core.requireduserdatamissing": "Erabiltzaile honek beharrezkoak diren profileko datuak bete gabe ditu. Mesedez, bete itzazu datu hauek zure gunean eta saiatu berriz.
                {{$a}}", - "core.resourcedisplayopen": "Ireki", - "core.resources": "Baliabideak", - "core.restore": "Berreskuratu", - "core.restricted": "Eskuragarritasun murriztua", - "core.retry": "Berriz saiatu", - "core.save": "Gorde", - "core.savechanges": "Gorde aldaketak", - "core.scanqr": "Eskaneatu QR kodea", - "core.search": "Bilatu", - "core.searching": "Bilatzen", - "core.searchresults": "Bilaketaren emaitzak", - "core.sec": "seg", - "core.secs": "segundu", - "core.seemoredetail": "Egin klik hemen xehetasun gehiago ikusteko", - "core.selectacategory": "Mesedez, aukeratu kategoria bat", - "core.selectacourse": "Aukeratu ikastaro bat", - "core.selectagroup": "Aukeratu talde bat", - "core.send": "Bidali", - "core.sending": "Bidaltzen", - "core.serverconnection": "Errorea zerbitzariarekin konektatzean", - "core.settings.about": "Honi buruz", - "core.settings.appsettings": "Aplikazioaren ezarpenak", - "core.settings.appversion": "Aplikazioaren bertsioa", - "core.settings.cannotsyncoffline": "Ezin da sinkronizatu lineaz kanpo.", - "core.settings.cannotsyncwithoutwifi": "Ezin da sinkronizatu oraingo ezarpenek sinkronizazio Wi-Fi bidez konektaturik egonda soilik baimentzen dutelako. Mesedez konektatu Wi-Fi sare batera.", - "core.settings.colorscheme": "Kolore-eskema", - "core.settings.colorscheme-auto": "Automatikoa (sistemako ezarpenetan oinarritutakoa)", - "core.settings.colorscheme-dark": "Iluna", - "core.settings.colorscheme-light": "Argia", - "core.settings.compilationinfo": "Konpilazioaren informazioa", - "core.settings.copyinfo": "Kopiatu gailuaren informazioa arbelean", - "core.settings.cordovadevicemodel": "Cordova device eredua", - "core.settings.cordovadeviceosversion": "Cordova device SE bertsioa", - "core.settings.cordovadeviceplatform": "Cordova device plataforma", - "core.settings.cordovadeviceuuid": "Cordova device uuid", - "core.settings.cordovaversion": "Cordova bertsioa", - "core.settings.currentlanguage": "Oraingo hizkuntza", - "core.settings.debugdisplay": "Erakutsi arazketa-mezuak", - "core.settings.debugdisplaydescription": "Gaituz gero, ahal denean errore-mezuek errorearen inguruko informazio gehiago erakutsiko dute.", - "core.settings.deletesitefiles": "Ziur zaude '{{sitename}}' gunetik jaitsitako fitxategiak eta cache-ko datuak ezabatu nahi dituzula? Hori eginez gero ezingo duzu aplikazioa offline moduan erabili.", - "core.settings.deletesitefilestitle": "Ezabatu guneko fitxategiak", - "core.settings.deviceinfo": "Gailuaren informazioa", - "core.settings.deviceos": "Gailuaren SEa", - "core.settings.disableall": "Desgaitu jakinarazpenak", - "core.settings.disabled": "Desgaituta", - "core.settings.displayformat": "Erakusteko modua", - "core.settings.enabledownloadsection": "Gaitu gaien deskarga", - "core.settings.enablefirebaseanalytics": "Gaitu Firebase analytics", - "core.settings.enablefirebaseanalyticsdescription": "Gaituz gero, aplikazioak erabilera-datuak anonimoki bilduko ditu.", - "core.settings.enablerichtexteditor": "Gaitu testu-editorea", - "core.settings.enablerichtexteditordescription": "Gaituz gero, testu-editorea erakutsiko da edukia sartu ahal den lekuetan.", - "core.settings.enablesyncwifi": "Baimendu sinkronizazioa Wi-Fi bidezko konexioa dagoenean soilik", - "core.settings.entriesincache": "{{$a}} sarrera cache-an", - "core.settings.errordeletesitefiles": "Errorea fitxategiak ezabatzean.", - "core.settings.errorsyncsite": "Errorea guneko datuak sinkronizatzean. Mesedez, egiaztatu zure interneterako konexioa eta saiatu berriz.", - "core.settings.estimatedfreespace": "Estimatutako leku librea", - "core.settings.filesystemroot": "Fitxategi-sistemaren jatorria", - "core.settings.fontsize": "Testuaren tamaina", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "Ezarpen hau zure gunearen konfigurazioak behartutakoa da.", - "core.settings.general": "Orokorra", - "core.settings.language": "Hizkuntza", - "core.settings.license": "Lizentzia", - "core.settings.localnotifavailable": "Jakinarazpen lokalak eskuragarri", - "core.settings.locationhref": "Web view URLa", - "core.settings.locked": "Blokeatuta", - "core.settings.loggedin": "On-line", - "core.settings.loggedoff": "Lineaz kanpo", - "core.settings.navigatorlanguage": "Nabigatzailearen hizkuntza", - "core.settings.navigatoruseragent": "Nabigatzailearen userAgent-a", - "core.settings.networkstatus": "Internet konexioaren egoera", - "core.settings.opensourcelicenses": "Kode Irekiko Lizentziak", - "core.settings.preferences": "Aukerak", - "core.settings.privacypolicy": "Pribatutasun politika.", - "core.settings.publisher": "Editorea", - "core.settings.pushid": "Push jakinarazpenaren IDa", - "core.settings.reportinbackground": "Erroreak automatikoki jakinarazi", - "core.settings.screen": "Pantailako informazioa", - "core.settings.settings": "Ezarpenak", - "core.settings.showdownloadoptions": "Erakutsi deskargatzeko aukerak", - "core.settings.siteinfo": "Gunearen informazioa", - "core.settings.sites": "Guneak", - "core.settings.spaceusage": "Lekuaren erabilera", - "core.settings.spaceusagehelp": "Gordetako guneko informazioa ezabatuta bertako lineaz kanpoko datuak ezabatuko dira. Informazio horrek aplikazioa konexiorik gabe erabiltzea ahalbidetzen dizu.", - "core.settings.synchronization": "Sinkronizazioa", - "core.settings.synchronizenow": "Sinkronizatu orain", - "core.settings.synchronizenowhelp": "Gune bat sinkronizatuta zain dauden aldaketak eta gailuan gordetako lineaz kanpoko jarduerak bidaliko ditu eta mezu eta jakinarazpenen moduko datuak sinkronizatuko ditu.", - "core.settings.syncsettings": "Sinkronizazioaren ezarpenak", - "core.settings.total": "Guztira", - "core.settings.wificonnection": "WiFi konexioa", - "core.sharedfiles.chooseaccountstorefile": "Aukeratu kontua fitxategiak bertan gordetzeko.", - "core.sharedfiles.chooseactionrepeatedfile": "Dagoeneko fitxategi bat dago izen horrekin. Existitzen den fitxategi hori ordezkatu edo \"{{$a}}\" gisa berrizendatu nahi duzu ?", - "core.sharedfiles.errorreceivefilenosites": "Ez dago gunerik gordeta. Mesedez gehitu gune bat aplikazioaren bitartez fitxategi bat partekatu aurretik.", - "core.sharedfiles.nosharedfiles": "Ez dago partekatutako fitxategirik gune honetan.", - "core.sharedfiles.nosharedfilestoupload": "Ez duzu igotzeko fitxategirik hemen. Beste app baten bitartez fitxategia igo nahi baduzu, fitxategia aurkitu eta ondoren 'Ireki honekin' botoia sakatu.", - "core.sharedfiles.rename": "Aldatu izena", - "core.sharedfiles.replace": "Ordeztu", - "core.sharedfiles.sharedfiles": "Partekatutako fitxategiak", - "core.sharedfiles.successstorefile": "Fitxategia ondo gorde da. Orain fitxategi hau aukeratu dezakezu zure gune pribatura igo edo jarduera batean erabiltzeko.", - "core.show": "Erakutsi", - "core.showless": "Erakutsi gutxiago...", - "core.showmore": "Erakutsi gehiago...", - "core.site": "Gunea", - "core.sitehome.sitehome": "Gunearen hasiera", - "core.sitehome.sitenews": "Guneko albisteak", - "core.sitemaintenance": "Gune hau mantenuan dago eta oraingoz ez da eskuragarria", - "core.sizeb": "byte", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Saltatu", - "core.sorry": "Barkatu...", - "core.sort": "Ordenatu", - "core.sortby": "Ordenatze-irizpidea", - "core.start": "Hasiera", - "core.storingfiles": "Fitxategiak gordetzen", - "core.strftimedate": "%Y(e)ko %Bk %d", - "core.strftimedatefullshort": "%y/%m/%d", - "core.strftimedateshort": "%Bk %d", - "core.strftimedatetime": "%Y(e)ko %Bren %d(e)n, %H:%M(e)an", - "core.strftimedatetimeshort": "%Y/%m/%d %H:%M", - "core.strftimedaydate": "%A, %Y(e)ko %Bk %d", - "core.strftimedaydatetime": "%A, %Y(e)ko %Bren %d(e)an, %H:%M(e)an", - "core.strftimedayshort": "%A, %Bk %d", - "core.strftimedaytime": "%a, %H:%M(e)an", - "core.strftimemonthyear": "%Y(e)ko %B", - "core.strftimerecent": "%bk %d, %H:%M(e)an", - "core.strftimerecentfull": "%a, %Y(e)ko %bren %d(e)an, %H:%M(e)an", - "core.strftimetime": "%H:%M %p", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Bidali", - "core.success": "Ondo", - "core.tablet": "Tablet-a", - "core.tag.defautltagcoll": "Lehenetsitako bilduma", - "core.tag.errorareanotsupported": "Etiketa-eremu hau ez da aplikazioan onartzen.", - "core.tag.inalltagcoll": "Edonon", - "core.tag.itemstaggedwith": "{{$a.tagarea}} ondokoarekin etiketatu da: \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Ez dago emaitzarik \"{{$a}}\"-(e)rako", - "core.tag.notagsfound": "Ez da aurkitu \"{{$a}}\"-(r)ekin bat datorren etiketarik", - "core.tag.searchtags": "Bilatu etiketak", - "core.tag.showingfirsttags": "{{$a}} etiketa ospetsuenak erakusten", - "core.tag.tag": "Etiketa", - "core.tag.tagarea_course": "Ikastaroak", - "core.tag.tagarea_course_modules": "Jarduerak eta baliabideak", - "core.tag.tagarea_post": "Blogetako mezuak", - "core.tag.tagarea_user": "Erabiltzailearen interesak", - "core.tag.tags": "Etiketak", - "core.tag.warningareasnotsupported": "Etiketa-eremu batzuk ez dira erakusten aplikazioan onartzen ez direlako.", - "core.teachers": "Irakasleak", - "core.thereisdatatosync": "Lineaz-kanpoko {{$a}} daude sinkronizatzeko .", - "core.thisdirection": "ltr", - "core.time": "Ordua", - "core.timesup": "Denbora amaitu egin da!", - "core.today": "Gaur", - "core.tryagain": "Saiatu berriz", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "Oh oh!", - "core.unexpectederror": "Ezusteko errore bat gertatu da. Mesedez aplikazioa itxi eta berriz ireki ondoren berriz saiatu zaitez.", - "core.unicodenotsupported": "Emoji batzuk ez dira gune honetan onartzen. Mezua karaktere horiek kenduta bidaliko da.", - "core.unicodenotsupportedcleanerror": "Testu hutsa aurkitu da Unicode karaktereak ezabatzean.", - "core.unknown": "Ezezaguna", - "core.unlimited": "Mugarik gabe", - "core.unzipping": "Erauzten", - "core.updaterequired": "Aplikazioa eguneratu behar da", - "core.updaterequireddesc": "Mesedez zure aplikazioa {{$a}} bertsiora eguneratu ezazu", - "core.upgraderunning": "Gunea eguneratzen ari da. Mesedez, saiatu zaitez beranduago.", - "core.user": "Erabiltzailea", - "core.user.address": "Helbidea", - "core.user.city": "Hiria/Herria", - "core.user.contact": "Kontaktua", - "core.user.country": "Herrialdea", - "core.user.description": "Deskribapena", - "core.user.details": "Xehetasunak", - "core.user.detailsnotavailable": "Erabiltzaile honen xehetasunak ez daude zuretzat eskuragarri", - "core.user.editingteacher": "Irakaslea", - "core.user.email": "Helbide elektronikoa", - "core.user.emailagain": "Helbide elektronikoa (berriro)", - "core.user.errorloaduser": "Errorea erabiltzailea kargatzean.", - "core.user.firstname": "Izena", - "core.user.interests": "Interesguneak", - "core.user.lastname": "Abizena", - "core.user.manager": "Kudeatzailea", - "core.user.newpicture": "Irudi berria", - "core.user.noparticipants": "Ez da partaiderik aurkitu ikastaro honetarako", - "core.user.participants": "Partaideak", - "core.user.phone1": "Telefonoa", - "core.user.phone2": "Telefono mugikorra", - "core.user.roles": "Rolak", - "core.user.sendemail": "E-posta", - "core.user.student": "Ikaslea", - "core.user.teacher": "Edizio-baimenik gabeko irakaslea", - "core.user.webpage": "Web-orria", - "core.userdeleted": "Erabiltzaile-kontu hau ezabatu da", - "core.userdetails": "Erabiltzaileen xehetasunak", - "core.usernotfullysetup": "Erabiltzailea ez dago guztiz prest", - "core.users": "Erabiltzaileak", - "core.view": "Ikusi", - "core.viewcode": "Ikusi kodea", - "core.vieweditor": "Ikusi editorea", - "core.viewembeddedcontent": "Ikusi txertatutako edukia", - "core.viewprofile": "Ikusi profila", - "core.warningofflinedatadeleted": "'{{name}}' {{component}}-aren lineaz kanpoko informazioa ezabatua izan da. {{error}}", - "core.whatisyourage": "Zein da zure adina?", - "core.wheredoyoulive": "Zein herrialdetan bizi zara?", - "core.whoissiteadmin": "\"Guneko Kudeatzaileak\" zure eskola/unibertsitate/enpresa edo hezkuntza erakundearen Moodlea kudeatzen dutenak dira. Eurekin harremanetan nola jarri ez badakizu, jarri harremanetan zure irakasle edo hezitzaileekin.", - "core.whoops": "Ups!", - "core.whyisthishappening": "Zergatik ari da hau gertatzen?", - "core.whyisthisrequired": "Zergatik da hau beharrezkoa?", - "core.wsfunctionnotavailable": "Web-zerbitzu funtzioa ez dago eskuragarri.", - "core.year": "urtea", - "core.years": "urte", - "core.yes": "Bai", - "core.youreoffline": "Lineaz kanpo zaude", - "core.youreonline": "Berriz ere on-line zaude" -} \ No newline at end of file diff --git a/src/assets/lang/fa.json b/src/assets/lang/fa.json deleted file mode 100644 index db099483a..000000000 --- a/src/assets/lang/fa.json +++ /dev/null @@ -1,1495 +0,0 @@ -{ - "addon.badges.badgedetails": "مشخصات مدال", - "addon.badges.badges": "مدال‌ها", - "addon.badges.contact": "تماس", - "addon.badges.dateawarded": "تاریخ صدور", - "addon.badges.expired": "منقضی‌شده", - "addon.badges.expirydate": "تاریخ انقضا", - "addon.badges.issuancedetails": "انقضای مدال", - "addon.badges.issuerdetails": "مشخصات صادرکننده", - "addon.badges.issuername": "نام صادرکننده", - "addon.badges.issuerurl": "آدرس صادرکننده", - "addon.badges.nobadges": "مدالی موجود نیست.", - "addon.badges.recipientdetails": "مشخصات دریافت‌کننده", - "addon.badges.warnexpired": "(این مدال منقضی شده است!)", - "addon.block_activitymodules.pluginname": "فعالیت‌ها", - "addon.block_activityresults.pluginname": "نتایج فعالیت", - "addon.block_badges.pluginname": "مدال‌های اخیر", - "addon.block_blogmenu.pluginname": "منوی وبلاگ", - "addon.block_blogrecent.pluginname": "نوشته‌های تازه در وبلاگ", - "addon.block_blogtags.pluginname": "برچسب‌های وبلاگ", - "addon.block_calendarmonth.pluginname": "تقویم", - "addon.block_calendarupcoming.pluginname": "رویدادهای نزدیک", - "addon.block_comments.pluginname": "نظرات", - "addon.block_completionstatus.pluginname": "وضعیت تکمیل درس", - "addon.block_glossaryrandom.pluginname": "واژهٔ تصادفی فرهنگ", - "addon.block_learningplans.pluginname": "برنامه‌های یادگیری", - "addon.block_myoverview.all": "همه (به جز موارد حذف شده)", - "addon.block_myoverview.allincludinghidden": "همه", - "addon.block_myoverview.favourites": "ستاره‌دار", - "addon.block_myoverview.future": "آینده", - "addon.block_myoverview.hiddencourses": "برای نمایش حذف شد", - "addon.block_myoverview.inprogress": "در جریان", - "addon.block_myoverview.lastaccessed": "آخرین زمان دسترسی", - "addon.block_myoverview.morecourses": "درس‌های بیشتر", - "addon.block_myoverview.nocourses": "درسی وجود ندارد", - "addon.block_myoverview.past": "گذشته", - "addon.block_myoverview.pluginname": "نمای کلی درس", - "addon.block_myoverview.title": "نام درس", - "addon.block_newsitems.pluginname": "تابلوی اعلانات", - "addon.block_onlineusers.pluginname": "کاربران حاضر", - "addon.block_privatefiles.pluginname": "فایل‌های خصوصی", - "addon.block_recentactivity.pluginname": "فعالیت‌های اخیر", - "addon.block_recentlyaccessedcourses.pluginname": "درس‌های اخیرا مراجعه‌شده", - "addon.block_selfcompletion.pluginname": "تکمیل خود", - "addon.block_sitemainmenu.pluginname": "منوی اصلی", - "addon.block_tags.pluginname": "برچسب‌ها", - "addon.block_timeline.duedate": "مهلت", - "addon.block_timeline.next30days": "۳۰ روز آینده", - "addon.block_timeline.next3months": "۳ ماه آینده", - "addon.block_timeline.next6months": "۶ ماه آینده", - "addon.block_timeline.next7days": "۷ روز آینده", - "addon.block_timeline.noevents": "مهلت هیچ فعالیتی نزدیک نیست", - "addon.block_timeline.overdue": "تاخیر دار", - "addon.block_timeline.pluginname": "گاه‌شمار فعالیت‌ها", - "addon.block_timeline.sortbycourses": "مرتب‌سازی بر اساس درس‌ها", - "addon.block_timeline.sortbydates": "مرتب‌سازی بر اساس تاریخ‌ها", - "addon.blog.blog": "بلاگ", - "addon.blog.blogentries": "نوشته‌های بلاگ", - "addon.blog.linktooriginalentry": "پیوند به نوشتهٔ وبلاگ اصلی", - "addon.blog.noentriesyet": "هیچ نوشتهٔ قابل مشاهده‌ای وجود ندارد", - "addon.blog.publishtonoone": "خودتان (پیش‌نویس)", - "addon.blog.publishtosite": "هر کس در این سایت", - "addon.blog.publishtoworld": "هر کس در دنیا", - "addon.blog.siteblogheading": "وبلاگ سایت", - "addon.calendar.allday": "تمام روز", - "addon.calendar.calendar": "تقویم", - "addon.calendar.calendarevents": "رویدادهای تقویم", - "addon.calendar.categoryevents": "رویدادهای دسته", - "addon.calendar.confirmeventdelete": "آیا مطمئن هستید که می‌خواهید رویداد «{{$a}}» را حذف کنید؟", - "addon.calendar.courseevents": "رویدادهای درس‌ها", - "addon.calendar.daynext": "روز بعد", - "addon.calendar.dayprev": "روز قبل", - "addon.calendar.defaultnotificationtime": "زمان پیش‌فرض اطلاع‌رسانی", - "addon.calendar.deleteallevents": "حذف تمامی رویدادها", - "addon.calendar.deleteevent": "حذف رویداد", - "addon.calendar.deleteoneevent": "حذف این رویداد", - "addon.calendar.durationminutes": "مدت به دقیقه", - "addon.calendar.durationnone": "بدون استمرار", - "addon.calendar.durationuntil": "تا", - "addon.calendar.editevent": "در حال ویرایش رویداد", - "addon.calendar.errorloadevent": "خطا در بارگیری رویداد.", - "addon.calendar.errorloadevents": "خطا در بارگیری رویدادها.", - "addon.calendar.eventcalendareventdeleted": "رویداد تقویم حذف شد", - "addon.calendar.eventduration": "مدت", - "addon.calendar.eventendtime": "زمان اتمام", - "addon.calendar.eventkind": "نوع رویداد", - "addon.calendar.eventname": "عنوان رویداد", - "addon.calendar.eventstarttime": "زمان شروع", - "addon.calendar.eventtype": "نوع رویداد", - "addon.calendar.fri": "ج", - "addon.calendar.friday": "جمعه", - "addon.calendar.gotoactivity": "رفتن به فعالیت", - "addon.calendar.groupevents": "رویدادهای گروه", - "addon.calendar.invalidtimedurationminutes": "مقداری که برای «مدت به دقیقه» وارد کرده‌اید نامعتبر است. لطفا یک مقدار بزرگ‌تر از صفر برای «مدت به دقیقه» وارد کنید یا اینکه «بدون استمرار» را انتخاب کنید.", - "addon.calendar.invalidtimedurationuntil": "روز و ساعتی که برای تکرار تا آن زمان وارد کرده‌اید قبل از زمان شروع رویداد است. لطفا پیش از ادامه دادن، این مشکل را برطرف کنید.", - "addon.calendar.mon": "د", - "addon.calendar.monday": "دوشنبه", - "addon.calendar.monthlyview": "نمای ماهانه", - "addon.calendar.newevent": "رویداد جدید", - "addon.calendar.noevents": "هیچ رویدادی نیست", - "addon.calendar.nopermissiontoupdatecalendar": "متاسفیم، ولی هم‌اکنون شما مجوز به‌روز کردن رویداد تقویم را ندارید", - "addon.calendar.repeatedevents": "رویداد تکرار شونده", - "addon.calendar.repeateditall": "اعمال تغییرات روی هر {{$a}} رویداد دیگر در این سلسله رویداد", - "addon.calendar.repeateditthis": "اعمال تعییرات فقط روی این رویداد", - "addon.calendar.repeatevent": "تکرار این رویداد", - "addon.calendar.repeatweeksl": "به صورت هفتگی، در مجموع", - "addon.calendar.sat": "ش", - "addon.calendar.saturday": "شنبه", - "addon.calendar.siteevents": "رویدادهای سایت", - "addon.calendar.sun": "ی", - "addon.calendar.sunday": "یک‌شنبه", - "addon.calendar.thu": "پ", - "addon.calendar.thursday": "پنج‌شنبه", - "addon.calendar.today": "امروز", - "addon.calendar.tomorrow": "فردا", - "addon.calendar.tue": "س", - "addon.calendar.tuesday": "سه‌شنبه", - "addon.calendar.typecategory": "رویدادهای دسته", - "addon.calendar.typeclose": "بستن رویداد", - "addon.calendar.typecourse": "رویداد درس", - "addon.calendar.typedue": "موعد رویداد", - "addon.calendar.typegradingdue": "", - "addon.calendar.typegroup": "رویداد گروهی", - "addon.calendar.typeopen": "باز کردن رویداد", - "addon.calendar.typesite": "رویداد سایت", - "addon.calendar.typeuser": "رویداد کاربر", - "addon.calendar.upcomingevents": "رویدادهای نزدیک", - "addon.calendar.userevents": "رویدادهای کاربر", - "addon.calendar.wed": "چ", - "addon.calendar.wednesday": "چهارشنبه", - "addon.calendar.when": "چه زمانی", - "addon.calendar.yesterday": "دیروز", - "addon.competency.activities": "فعالیت‌ها", - "addon.competency.competencies": "شایستگی‌ها", - "addon.competency.coursecompetencies": "شایستگی‌های درس", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "امتیازهای شایستگی‌ها در این درس تاثیری در برنامه‌های یادگیری ندارند.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "امتیازهای شایستگی‌ها در این درس بلافاصله در برنامه‌های یادگیری به‌روز می‌شوند.", - "addon.competency.crossreferencedcompetencies": "شایستگی‌های دارای ارجاع متقابل", - "addon.competency.duedate": "مهلت", - "addon.competency.errornocompetenciesfound": "هیچ شایستگی‌ای پیدا نشد", - "addon.competency.evidence": "مدرک", - "addon.competency.evidence_competencyrule": "شرط شایستگی برقرار شد.", - "addon.competency.evidence_coursecompleted": "درس «{{$a}}» کامل شد.", - "addon.competency.evidence_coursemodulecompleted": "فعالیت «{{$a}}» کامل شد.", - "addon.competency.evidence_courserestored": "امتیاز همراه با درس «{{$a}}» بازیابی شد.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "مدرک یادگیری قبلی «{{$a}}» متصل شد.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "مدرک یادگیری قبلی «{{$a}}» قطع اتصال شد.", - "addon.competency.evidence_manualoverride": "امتیاز شایستگی به‌طور دستی تعیین شد.", - "addon.competency.evidence_manualoverrideincourse": "امتیاز شایستگی به‌طور دستی در درس «{{$a}}» تعیین شد.", - "addon.competency.evidence_manualoverrideinplan": "امتیاز شایستگی به‌طور دستی در برنامهٔ یادگیری «{{$a}}» تعیین شد.", - "addon.competency.learningplancompetencies": "شایستگی‌های برنامه یادگیری", - "addon.competency.learningplans": "برنامه‌های آزموشی", - "addon.competency.myplans": "برنامه‌های یادگیری من", - "addon.competency.nocompetencies": "بدون شایستگی", - "addon.competency.nocompetenciesincourse": "هیچ شایستگی‌ای به این درس مرتبط نشده است.", - "addon.competency.nocrossreferencedcompetencies": "هیچ شایستگی دیگری به این شایستگی به‌طور متقابل ارجاع داده نشده است.", - "addon.competency.noevidence": "بدون مدرک", - "addon.competency.noplanswerecreated": "هیچ برنامهٔ یادگیری‌ای ساخته نشده است.", - "addon.competency.path": "مسیر:", - "addon.competency.planstatusactive": "فعال", - "addon.competency.planstatuscomplete": "کامل", - "addon.competency.planstatusdraft": "پیش‌نویس", - "addon.competency.planstatusinreview": "درحال بازبینی", - "addon.competency.planstatuswaitingforreview": "در انتظار بازبینی", - "addon.competency.proficient": "کسب مهارت", - "addon.competency.progress": "پیشروی", - "addon.competency.rating": "امتیاز", - "addon.competency.reviewstatus": "بازبینی وضعیت", - "addon.competency.status": "وضعیت", - "addon.competency.template": "الگوی برنامه یادگیری", - "addon.competency.uponcoursecompletion": "هم‌زمان با تکمیل درس:", - "addon.competency.usercompetencystatus_idle": "بی کار", - "addon.competency.usercompetencystatus_inreview": "درحال بازبینی", - "addon.competency.usercompetencystatus_waitingforreview": "در انتظار بازبینی", - "addon.competency.userplans": "برنامه‌های یادگیری", - "addon.competency.xcompetenciesproficientoutofy": "در {{$a.x}} شایستگی از مجموع {{$a.y}} شایستگی مهارت کسب شده است", - "addon.competency.xcompetenciesproficientoutofyincourse": "شما در {{$a.x}} شایستگی از {{$a.y}} شایستگی ماهر هستید.", - "addon.coursecompletion.complete": "کامل شد", - "addon.coursecompletion.completecourse": "کامل‌کردن درس", - "addon.coursecompletion.completed": "تکمیل‌شده", - "addon.coursecompletion.completiondate": "تاریخ تکمیل", - "addon.coursecompletion.completionmenuitem": "تکمیل", - "addon.coursecompletion.couldnotloadreport": "امکان بارگذاری گزارش تکمیل فعالیت درس نیست. لطفا دوباره بعداً تلاش کنید", - "addon.coursecompletion.coursecompletion": "تکمیل درس", - "addon.coursecompletion.criteria": "ضابطه", - "addon.coursecompletion.criteriagroup": "گروه ضوابط", - "addon.coursecompletion.criteriarequiredall": "تمام ضوابط زیر باید برآورده شوند", - "addon.coursecompletion.criteriarequiredany": "حداقل یکی از ضوابط زیر برآورده شود", - "addon.coursecompletion.inprogress": "در جریان", - "addon.coursecompletion.manualselfcompletion": "علامت زدن به عنوان کامل توسط خود افراد", - "addon.coursecompletion.nottracked": "تکمیل شما در این درس رصد نمی‌شود", - "addon.coursecompletion.notyetstarted": "هنوز شروع نشده است", - "addon.coursecompletion.pending": "در حال بررسی", - "addon.coursecompletion.required": "لازم است", - "addon.coursecompletion.requiredcriteria": "ضوابط مورد نیاز", - "addon.coursecompletion.requirement": "نیازمندی", - "addon.coursecompletion.status": "وضعیت", - "addon.coursecompletion.viewcoursereport": "مشاهده گزارش درس", - "addon.files.couldnotloadfiles": "لیست فایل‌ها نمی‌تواند باگذاری شود.", - "addon.files.emptyfilelist": "هیچ فایلی برای نمایش وجود ندارد.", - "addon.files.files": "فایل‌ها", - "addon.files.privatefiles": "فایل‌های شخصی", - "addon.files.sitefiles": "فایل‌های سایت", - "addon.messageoutput_airnotifier.processorsettingsdesc": "پیکربندی دستگاه‌ها", - "addon.messages.acceptandaddcontact": "پذیرفتن و اضافه‌کردن به مخاطبین", - "addon.messages.addcontact": "افزودن به مخاطبين", - "addon.messages.addcontactconfirm": "آیا مطمئن هستید که می‌خواهید {{$a}} را به لیست مخاطبین خود اضافه کنید؟", - "addon.messages.addtofavourites": "شروع مکالمه", - "addon.messages.addtoyourcontacts": "اضافه‌شدن به لیست مخاطبین", - "addon.messages.blocknoncontacts": "افرادی که در لیست مخاطبین من نیستند نتوانند برای من پیام بفرستند", - "addon.messages.blockuser": "مسدود کردن کاربر", - "addon.messages.blockuserconfirm": "آیا مطمئن هستید که می‌خواهید {{$a}} را مسدود کنید؟", - "addon.messages.contactableprivacy": "پذیرش پیام‌ها از:", - "addon.messages.contactableprivacy_coursemember": "مخاطبین من و کسانی که در درس‌های من هستند", - "addon.messages.contactableprivacy_onlycontacts": "فقط کسانی که در لیست مخاطبین من هستند", - "addon.messages.contactableprivacy_site": "هر کس در سایت", - "addon.messages.contactblocked": "مخاطب مسدود شده است", - "addon.messages.contactlistempty": "لیست مخاطبین خالی است", - "addon.messages.contactname": "نام مخاطب", - "addon.messages.contactrequestsent": "درخواست اضافه شدن به مخاطبین ارسال شد", - "addon.messages.contacts": "مخاطبین", - "addon.messages.decline": "نپذیرفتن", - "addon.messages.deleteallconfirm": "آیا مطمئن هستید که می‌خواهید کل این مکالمه را حذف کنید؟ این کار موجب پاک شدن مکالمه برای سایر شرکت‌کنندگان مکالمه نخواهد شد.", - "addon.messages.deleteconversation": "حذف مکالمه", - "addon.messages.deletemessage": "حذف پیغام", - "addon.messages.errorwhileretrievingdiscussions": "خطا در دریافت مباحثه‌ها از کارگزار.", - "addon.messages.groupconversations": "گروه", - "addon.messages.groupinfo": "اطلاعات گروه", - "addon.messages.individualconversations": "خصوصی", - "addon.messages.info": "اطلاعات", - "addon.messages.isnotinyourcontacts": "{{$a}} در لیست مخاطبین شما نیست", - "addon.messages.message": "پیام", - "addon.messages.messagenotsent": "پیغام ارسال نشد. لطفا دوباره بعداً تلاش کنید", - "addon.messages.messagepreferences": "ترجیحات پیام‌دهی", - "addon.messages.messages": "پیام‌ها", - "addon.messages.newmessage": "پیام جدید", - "addon.messages.newmessages": "پیغام جدید", - "addon.messages.nocontactrequests": "درخواستی برای اضافه کردن به مخاطبین وجود ندارد", - "addon.messages.nocontactsgetstarted": "مخاطبی در لیست نیست", - "addon.messages.nogroupconversations": "هیچ مکالمه گروهی ندارید", - "addon.messages.noindividualconversations": "هیچ مکالمه خصوصی ندارید", - "addon.messages.nomessagesfound": "پیامی پیدا نشد", - "addon.messages.noncontacts": "خارج از لیست مخاطبین", - "addon.messages.nousersfound": "هیچ کاربری پیدا نشد", - "addon.messages.numparticipants": "{{$a}} شرکت‌کننده", - "addon.messages.removecontact": "حذف کردن مخاطب", - "addon.messages.removefromyourcontacts": "حذف‌شدن از لیست مخاطبین", - "addon.messages.searchcombined": "جستجو بین افراد و پیام‌ها", - "addon.messages.selfconversation": "فضای شخصی", - "addon.messages.selfconversationdefaultmessage": "ذخیره پیام‌های موقت، لینک‌ها، یادداشت‌ها و سایر موارد. برای دسترسی بعدتر.", - "addon.messages.sendcontactrequest": "ارسال درخواست اضافه‌شدن به مخاطبین", - "addon.messages.showdeletemessages": "نمایش گزینهٔ حذف پیغام‌ها", - "addon.messages.type_blocked": "مسدود شده", - "addon.messages.type_offline": "آفلاین", - "addon.messages.type_online": "آنلاین", - "addon.messages.type_search": "نتایج جستجو", - "addon.messages.type_strangers": "دیگران", - "addon.messages.unabletomessage": "شما نمی‌توانید به این کاربر پیام بفرستید", - "addon.messages.unblockuserconfirm": "آیا مطمئن هستید که می‌خواهید {{$a}} را مسدود بودن خارج کنید؟", - "addon.messages.useentertosend": "استفاده از کلید Enter برای ارسال", - "addon.messages.userwouldliketocontactyou": "{{$a}} تمایل دارد که شما را به لیست مخاطبینش اضافه کند", - "addon.messages.warningmessagenotsent": "امکان ارسال پیغام به کاربر {{user}} وجود ندارد. {{error}}", - "addon.messages.wouldliketocontactyou": "مایل است با شما تماس بگیرد", - "addon.messages.you": "شما:", - "addon.messages.youhaveblockeduser": "شما این کاربر را مسدود کرده‌اید", - "addon.mod_assign.acceptsubmissionstatement": "لطفا پذیریش بیانیه‌ی تحویل تکلیف را تایید کنید.", - "addon.mod_assign.addattempt": "اجازه‌دادن یک بار تلاش دیگر", - "addon.mod_assign.addnewattempt": "اضافه‌کردن یک تلاش جدید", - "addon.mod_assign.addnewattemptfromprevious": "اضافه‌کردن یک تلاش جدید بر اساس ارسال قبلی", - "addon.mod_assign.addsubmission": "تحویل تکلیف", - "addon.mod_assign.allowsubmissionsfromdate": "مجاز بودن تحویل از", - "addon.mod_assign.applytoteam": "اعمال نمره و بازخورد به کل گروه", - "addon.mod_assign.assignmentisdue": "موعد تحویل گذشته است", - "addon.mod_assign.attemptreopenmethod": "مجاز شدن تلاش مجدد", - "addon.mod_assign.attemptreopenmethod_manual": "دستی", - "addon.mod_assign.attemptreopenmethod_untilpass": "به‌طور خودکار تا قبول شدن", - "addon.mod_assign.confirmsubmission": "آیا مطمئن هستید که می‌خواهید تکلیفتان را برای تصحیح تحویل دهید؟ پس از تحویل قادر به ایجاد تغییر در تکلیف نخواهید بود.", - "addon.mod_assign.currentattempt": "این تلاش {{$a}} ام است.", - "addon.mod_assign.currentattemptof": "این تلاش {{$a.attemptnumber}} ام است (در کل {{$a.maxattempts}} تلاش مجاز است).", - "addon.mod_assign.currentgrade": "نمرهٔ فعلی در دفتر نمره", - "addon.mod_assign.cutoffdate": "تاریخ عدم پذیرش", - "addon.mod_assign.defaultteam": "گروه پیش‌فرض", - "addon.mod_assign.duedate": "مهلت تحویل", - "addon.mod_assign.duedateno": "بدون موعد", - "addon.mod_assign.editingstatus": "وضعیت ویرایش", - "addon.mod_assign.editsubmission": "ویرایش پاسخ", - "addon.mod_assign.extensionduedate": "مهلت تمدید شده", - "addon.mod_assign.grade": "نمره", - "addon.mod_assign.graded": "نمره داده شده است", - "addon.mod_assign.gradedby": "نمره دهنده", - "addon.mod_assign.gradedon": "تاریخ ثبت نمره", - "addon.mod_assign.gradenotsynced": "نمره هم‌گام‌ نشد", - "addon.mod_assign.gradeoutof": "نمره از {{$a}}", - "addon.mod_assign.gradingstatus": "وضعیت تصحیح", - "addon.mod_assign.groupsubmissionsettings": "تنظیمات تحویل گروهی", - "addon.mod_assign.hiddenuser": "شاگرد شماره", - "addon.mod_assign.markingworkflowstate": "وضعیت گردش‌کار تصحیح", - "addon.mod_assign.markingworkflowstateinmarking": "درحال تصحیح", - "addon.mod_assign.markingworkflowstateinreview": "درحال بازبینی", - "addon.mod_assign.markingworkflowstatenotmarked": "نمره داده نشده", - "addon.mod_assign.markingworkflowstatereadyforrelease": "آمادهٔ انتشار", - "addon.mod_assign.markingworkflowstatereadyforreview": "نمره‌دهی کامل شده", - "addon.mod_assign.markingworkflowstatereleased": "منتشر‌شده", - "addon.mod_assign.modulenameplural": "تکالیف", - "addon.mod_assign.noattempt": "هیچ چیزی تحویل داده نشده است", - "addon.mod_assign.nosubmission": "هیچ چیزی برای این تکلیف تحویل داده نشده است", - "addon.mod_assign.notgraded": "نمره داده نشده است", - "addon.mod_assign.numberofdraftsubmissions": "پیش‌نویس‌ها", - "addon.mod_assign.numberofparticipants": "شرکت کنندگان", - "addon.mod_assign.numberofsubmissionsneedgrading": "نیازمند نمره‌دهی", - "addon.mod_assign.numberofsubmittedassignments": "تحویل داده شده", - "addon.mod_assign.numberofteams": "تعداد گروه‌ها", - "addon.mod_assign.numwords": "{{$a}} کلمه", - "addon.mod_assign.outof": "{{$a.current}} از {{$a.total}}", - "addon.mod_assign.overdue": "از موعد تحویل تکلیف {{$a}} گذشته است", - "addon.mod_assign.submission": "پاسخ ارسالی", - "addon.mod_assign.submissioneditable": "شاگرد می‌تواند این تکلیف تحویل داده شده را ویرایش کند", - "addon.mod_assign.submissionnoteditable": "شاگرد نمی‌تواند این تکلیف را ویرایش کند", - "addon.mod_assign.submissionslocked": "این تکلیف، تحویل جدیدی را نمی‌پذیرد", - "addon.mod_assign.submissionstatus": "وضعیت تحویل", - "addon.mod_assign.submissionstatus_": "تحویل نداده است", - "addon.mod_assign.submissionstatus_draft": "پیش‌نویس (تحویل داده نشده است)", - "addon.mod_assign.submissionstatus_marked": "تصحیح شده", - "addon.mod_assign.submissionstatus_reopened": "دوباره بازشده", - "addon.mod_assign.submissionstatus_submitted": "برای تصحیح تحویل داده شده است", - "addon.mod_assign.submissionstatusheading": "وضعیت تحویل", - "addon.mod_assign.submissionteam": "گروه", - "addon.mod_assign.submitassignment": "تحویل تکلیف", - "addon.mod_assign.submitassignment_help": "بعد از اینکه این تکلیف تحویل داده شد دیگر قادر به تغییر دادن آن نخواهید بود", - "addon.mod_assign.submittedearly": "تکلیف {{$a}} زود تحویل داده شد", - "addon.mod_assign.submittedlate": "تکلیف {{$a}} با تأخیر تحویل داده شد", - "addon.mod_assign.timemodified": "آخرین تغییر", - "addon.mod_assign.timeremaining": "زمان باقیمانده", - "addon.mod_assign.ungroupedusers": "تنظیم «نیازمند گروه برای تحویل‌دادن» فعال است و بعضی از کاربران یا عضو هیچ گروهی نیستند، یا عضو بیش از یک گروه هستند. در نتیجه نمی‌توانند تکلیف را تحویل دهند.", - "addon.mod_assign.unlimitedattempts": "نامحدود", - "addon.mod_assign.userswhoneedtosubmit": "کاربرانی که باید تحویل دهند: {{$a}}", - "addon.mod_assign.viewsubmission": "مشاهدهٔ تحویل", - "addon.mod_assign.wordlimit": "محدودیت کلمه", - "addon.mod_assign_feedback_comments.pluginname": "بازخورد متنی", - "addon.mod_assign_feedback_editpdf.pluginname": "یادداشت‌نویسی روی PDF", - "addon.mod_assign_feedback_file.pluginname": "بازخورد فایلی", - "addon.mod_assign_submission_comments.pluginname": "توضیحات برای تحویل", - "addon.mod_assign_submission_file.pluginname": "تحویل فایل", - "addon.mod_assign_submission_onlinetext.pluginname": "نوشتن متن برخط", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "محدودیت کلمه برای این تکلیف {{$a.limit}} کلمه است در حالی که شما در حال ارسال {{$a.count}} کلمه هستید. لطفا پاسختان را بررسی کرده و دوباره ارسال کنید.", - "addon.mod_book.modulenameplural": "کتاب‌ها", - "addon.mod_book.navnexttitle": "صفحهٔ بعد: {{$a}}", - "addon.mod_book.navprevtitle": "صفحهٔ قبل: {{$a}}", - "addon.mod_book.toc": "فهرست", - "addon.mod_chat.beep": "بوق", - "addon.mod_chat.chatreport": "جلسات گفتگو", - "addon.mod_chat.currentusers": "کاربران حاضر", - "addon.mod_chat.enterchat": "برای ورود به اتاق گفتگو اینجا کلیک کنید", - "addon.mod_chat.errorwhileconnecting": "خطا در هنگام اتصال به چت", - "addon.mod_chat.errorwhilegettingchatdata": "خطا در هنگام دریافت داده‌های چت", - "addon.mod_chat.errorwhilegettingchatusers": "خطا در هنگام دریافت کاربران چت", - "addon.mod_chat.errorwhileretrievingmessages": "خطا در هنگام بازیابی پیغام‌ها از سرور", - "addon.mod_chat.errorwhilesendingmessage": "خطا در هنگام ارسال پیغام", - "addon.mod_chat.messagebeepseveryone": "{{$a}} یک بوق برای همه فرستاد!", - "addon.mod_chat.messagebeepsyou": "{{$a}} برای شما بوق فرستاد!", - "addon.mod_chat.messageenter": "{{$a}} وارد اتاق گفتگو شد", - "addon.mod_chat.messageexit": "{{$a}} اتاق گفتگو را ترک کرد", - "addon.mod_chat.messages": "پیام‌ها", - "addon.mod_chat.messageyoubeep": "شما یک بوق برای {{$a}} فرستادید", - "addon.mod_chat.modulenameplural": "اتاق‌های گفتگو", - "addon.mod_chat.mustbeonlinetosendmessages": "شما برای ارسال پیغام، باید آنلاین باشید", - "addon.mod_chat.nomessages": "هنوز پیامی گفته نشده است", - "addon.mod_chat.saidto": "گفت به", - "addon.mod_chat.send": "ارسال", - "addon.mod_chat.sessionstart": "جلسهٔ بعدی گفتگو {{$a.date}} شروع خواهد شد ({{$a.fromnow}} دیگر)", - "addon.mod_chat.talk": "صحبت", - "addon.mod_chat.viewreport": "دیدن جلسات گفتگوی قبلی", - "addon.mod_choice.choiceoptions": "گزینه‌های انتخابی", - "addon.mod_choice.errorgetchoice": "خطا در دریافت داده‌های ماژول انتخاب", - "addon.mod_choice.expired": "با عرض پوزش، این فعالیت در {{$a}} بسته شد و دیگر در دسترس نیست", - "addon.mod_choice.full": "(پر)", - "addon.mod_choice.modulenameplural": "انتخاب‌ها", - "addon.mod_choice.noresultsviewable": "نتایج در این لحظه قابل دیدن نیستند.", - "addon.mod_choice.notopenyet": "با عرض معذرت، این فعالیت تا قبل از {{$a}} در دسترس نیست", - "addon.mod_choice.numberofuser": "تعداد کاربران", - "addon.mod_choice.numberofuserinpercentage": "درصد پاسخ‌ها", - "addon.mod_choice.removemychoice": "حذف انتخاب من", - "addon.mod_choice.responses": "پاسخ‌ها", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% از کاربران گزینه : {{text}} انتخاب کرده‌اند", - "addon.mod_choice.responsesresultgraphheader": "نمایش نمودار", - "addon.mod_choice.resultsnotsynced": "پاسخ آخر شما قبل از اینکه در نتایج لحاظ گردد باید هم‌گام‌سازی شود.", - "addon.mod_choice.savemychoice": "ذخیرهٔ انتخاب من", - "addon.mod_choice.userchoosethisoption": "کاربرانی که این گزینه را انتخاب کرده‌اند", - "addon.mod_choice.yourselection": "انتخاب شما", - "addon.mod_data.addentries": "داده‌هایی را اضافه کنید", - "addon.mod_data.advancedsearch": "جستجوی پیشرفته", - "addon.mod_data.alttext": "متن جایگزین", - "addon.mod_data.approve": "تایید", - "addon.mod_data.approved": "وضعیت تایید", - "addon.mod_data.ascending": "صعودی", - "addon.mod_data.authorfirstname": "نام وارد کننده", - "addon.mod_data.authorlastname": "نام خانوادگی وارد کننده", - "addon.mod_data.confirmdeleterecord": "آیا مطمئن هستید که می‌خواهید این مورد را حذف کنید؟", - "addon.mod_data.descending": "نزولی", - "addon.mod_data.emptyaddform": "شما هیچ فیلدی را پر نکردید!", - "addon.mod_data.entrieslefttoadd": "برای کامل کردن این فعالیت باید {{$a.entriesleft}} دادهٔ دیگر وارد کنید", - "addon.mod_data.entrieslefttoaddtoview": "باید {{$a.entrieslefttoview}} دادهٔ دیگر وارد کنید تا بتوانید داده‌های وارد شده توسط سایرین را ببینید.", - "addon.mod_data.errordeleting": "خطا در حذف کردن داده‌های ورودی", - "addon.mod_data.expired": "متأسفیم، این فعالیت در {{$a}} بسته شد و دیگر در دسترس نیست", - "addon.mod_data.fields": "فیلدها", - "addon.mod_data.foundrecords": "رکوردهای پیدا شده: {{$a.num}} از {{$a.max}} (بازنشانی فیلترها)", - "addon.mod_data.menuchoose": "انتخاب کنید...", - "addon.mod_data.modulenameplural": "بانک‌های اطلاعاتی", - "addon.mod_data.more": "نمایش جزئیات", - "addon.mod_data.nomatch": "دادهٔ مطابقی پیدا نشد!", - "addon.mod_data.norecords": "بانک اطلاعاتی خالی است", - "addon.mod_data.notapproved": "دادهٔ ورودی هنوز تایید نشده است.", - "addon.mod_data.notopenyet": "با عرض پوزش، این فعالیت تا قبل از {{$a}} در دسترس نیست", - "addon.mod_data.numrecords": "{{$a}} دادهٔ ورودی", - "addon.mod_data.other": "غیره", - "addon.mod_data.recordapproved": "دادهٔ ورودی تایید شد", - "addon.mod_data.recorddeleted": "حذف شد", - "addon.mod_data.resetsettings": "بازنشانی فیلترها", - "addon.mod_data.search": "جستجو", - "addon.mod_data.selectedrequired": "تمامی گزینه‌های انتخاب شده لازم هستند", - "addon.mod_data.single": "مشاهدهٔ تکی", - "addon.mod_data.timeadded": "زمان اضافه شدن", - "addon.mod_data.timemodified": "زمان آخرین تغییر", - "addon.mod_feedback.analysis": "تحلیل", - "addon.mod_feedback.anonymous": "ناشناس", - "addon.mod_feedback.anonymous_entries": "نظرهای وارد شدهٔ ناشناس ({{$a}})", - "addon.mod_feedback.average": "میانگین", - "addon.mod_feedback.complete_the_form": "پاسخ دادن به سوال‌ها...", - "addon.mod_feedback.completed_feedbacks": "جواب‌های ارائه شده", - "addon.mod_feedback.continue_the_form": "ادامه دادن فرم", - "addon.mod_feedback.feedback_is_not_open": "بازخورد باز نیست", - "addon.mod_feedback.feedback_submitted_offline": "این بازخورد ذخیره شد تا بعداً ثبت شود", - "addon.mod_feedback.feedbackclose": "بستن بازخورد در", - "addon.mod_feedback.feedbackopen": "باز کردن بازخورد در", - "addon.mod_feedback.mapcourses": "نسبت دادن بازخورد به درس‌ها", - "addon.mod_feedback.maximal": "حداکثر", - "addon.mod_feedback.mode": "حالت", - "addon.mod_feedback.modulenameplural": "بازخوردها", - "addon.mod_feedback.next_page": "صفحهٔ بعد", - "addon.mod_feedback.non_anonymous": "نام کاربران ثبت و به همراه پاسخ‌ها نمایش داده خواهد شد", - "addon.mod_feedback.non_anonymous_entries": "اطلاعا وارد شده به صورت غیر ناشناس", - "addon.mod_feedback.non_respondents_students": "شاگردانی که پاسخ نداده‌اند", - "addon.mod_feedback.not_selected": "انتخاب نشده", - "addon.mod_feedback.not_started": "شروع نکرده است", - "addon.mod_feedback.overview": "مرور اجمالی", - "addon.mod_feedback.page_after_submit": "صفحهٔ بعد از پر کردن فرم", - "addon.mod_feedback.preview": "پیش‌نمایش", - "addon.mod_feedback.previous_page": "صفحهٔ قبل", - "addon.mod_feedback.questions": "سوال‌ها", - "addon.mod_feedback.response_nr": "پاسخ شمارهٔ", - "addon.mod_feedback.responses": "پاسخ‌ها", - "addon.mod_feedback.save_entries": "فرستادن پاسخ‌های شما", - "addon.mod_feedback.show_entries": "نمایش پاسخ‌ها", - "addon.mod_feedback.show_nonrespondents": "نمایش کسانی که پاسخ نداده‌اند", - "addon.mod_feedback.started": "شروع کرده است", - "addon.mod_feedback.this_feedback_is_already_submitted": "شما قبلا این فعالیت را کامل کرده‌اید.", - "addon.mod_folder.emptyfilelist": "هیچ فایلی برای نمایش وجود ندارد", - "addon.mod_folder.modulenameplural": "پوشه‌ها", - "addon.mod_forum.addanewdiscussion": "شروع یک مباحثهٔ جدید", - "addon.mod_forum.addanewquestion": "طرح یک سؤال جدید", - "addon.mod_forum.addanewtopic": "طرح مباحثهٔ جدید", - "addon.mod_forum.advanced": "پیشرفته", - "addon.mod_forum.cannotadddiscussion": "طرح مباحثه در این تالار نیازمند عضویت در گروه است.", - "addon.mod_forum.cannotadddiscussionall": "شما مجوز شروع کردن یک مباحثهٔ جدید برای همهٔ اعضا را ندارید.", - "addon.mod_forum.cannotcreatediscussion": "ایجاد مباحثهٔ‌جدید ممکن نشد", - "addon.mod_forum.couldnotadd": "به دلیل بروز یک خطای ناشناخته ارائه مطلب شما امکان پذیر نبود", - "addon.mod_forum.couldnotupdate": "به دلیل بروز یک خطای ناشناخته به‌روزرسانی مطلب شما امکان پذیر نبود", - "addon.mod_forum.delete": "حذف", - "addon.mod_forum.deletedpost": "این مطلب حذف شده است", - "addon.mod_forum.deletesure": "آیا مطمئنید که می‌خواهید این مطلب حذف شود؟", - "addon.mod_forum.discussion": "مباحثه", - "addon.mod_forum.discussionlocked": "این مباحثه قفل شده است و در نتیجه دیگر نمی‌توانید در آن شرکت کنید.", - "addon.mod_forum.discussionpinned": "سنجاق‌شده", - "addon.mod_forum.discussionsubscription": "آبونه‌شدن در مباحثه", - "addon.mod_forum.edit": "ویرایش", - "addon.mod_forum.erroremptymessage": "متن ارسالی نمی‌تواند خالی باشد", - "addon.mod_forum.erroremptysubject": "موضوع مطلب نمی‌تواند خالی باشد.", - "addon.mod_forum.errorgetforum": "خطا در دریافت داده‌های تالارگفتگو", - "addon.mod_forum.errorgetgroups": "خطا در دریافت تنظیمات گروه", - "addon.mod_forum.forumnodiscussionsyet": "هنوز هیچ مباحثه ای در این تالار شروع نشده است.", - "addon.mod_forum.group": "گروه", - "addon.mod_forum.lastpost": "آخرین مطلب", - "addon.mod_forum.message": "متن", - "addon.mod_forum.modeflatnewestfirst": "نمایش مطالب به صورت مسطح (از جدید به قدیمی)", - "addon.mod_forum.modeflatoldestfirst": "نمایش مطالب به صورت مسطح (از قدیمی به جدید)", - "addon.mod_forum.modenested": "نمایش مطالب به صورت تو در تو", - "addon.mod_forum.modulenameplural": "تالارهای گفتگو", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} مباحثه", - "addon.mod_forum.numreplies": "{{numreplies}} پاسخ", - "addon.mod_forum.posttoforum": "طرح در تالار", - "addon.mod_forum.posttomygroups": "ارسال یک نسخه به تمام گروه‌ها", - "addon.mod_forum.re": "در پاسخ به:", - "addon.mod_forum.refreshdiscussions": "تازه‌سازی مباحثه‌ها", - "addon.mod_forum.refreshposts": "تازه‌سازی مطالب مباحثه", - "addon.mod_forum.reply": "ارسال پاسخ", - "addon.mod_forum.subject": "موضوع", - "addon.mod_forum.thisforumhasduedate": "مهلت ارسال برای پست کردن مطلب در این تالارگفتگو {{$a}} است.", - "addon.mod_forum.thisforumisdue": "مهلت ارسال برای پست کردن مطلب در این تالارگفتگو {{$a}} بود.", - "addon.mod_forum.unread": "خوانده نشده", - "addon.mod_forum.unreadpostsnumber": "{{$a}} مطلب خوانده نشده", - "addon.mod_forum.yourreply": "پاسخ شما", - "addon.mod_glossary.addentry": "تعریف یک اصطلاح جدید", - "addon.mod_glossary.aliases": "کلمات کلیدی", - "addon.mod_glossary.attachment": "فایل ضمیمه", - "addon.mod_glossary.byalphabet": "الفبایی", - "addon.mod_glossary.byauthor": "گروه بر اساس نویسنده", - "addon.mod_glossary.bycategory": "گروه بر اساس دسته", - "addon.mod_glossary.bynewestfirst": "جدیدترین اول", - "addon.mod_glossary.byrecentlyupdated": "اخیرا به‌روز شد", - "addon.mod_glossary.bysearch": "جستجو", - "addon.mod_glossary.cannoteditentry": "ویرایش داده ورودی امکان ندارد", - "addon.mod_glossary.casesensitive": "بزرگ و کوچک بودن حروف این اصطلاح مهم است", - "addon.mod_glossary.categories": "دسته‌ها", - "addon.mod_glossary.concept": "اصطلاح", - "addon.mod_glossary.definition": "تعریف", - "addon.mod_glossary.entryusedynalink": "به این اصطلاح باید به صورت خودکار پیوند ایجاد شود", - "addon.mod_glossary.errconceptalreadyexists": "این اصطلاح قبلاً تعریف شده است. وجود اصطلاحات تکراری در این واژه‌نامه مجاز نمی‌باشد.", - "addon.mod_glossary.fillfields": "اصطلاح و تعریف جزو موارد ضروری هستند.", - "addon.mod_glossary.fullmatch": "تطابق در کل کلمه", - "addon.mod_glossary.linking": "پیوند دادن خودکار", - "addon.mod_glossary.modulenameplural": "واژه‌نامه‌ها", - "addon.mod_imscp.showmoduledescription": "نمایش توضیحات", - "addon.mod_imscp.toc": "فهرست", - "addon.mod_lesson.answer": "جواب", - "addon.mod_lesson.attempt": "تلاش: {{$a}}", - "addon.mod_lesson.attemptsremaining": "می‌توانید {{$a}} بار دیگر تلاش کنید", - "addon.mod_lesson.averagescore": "نمرهٔ میانگین", - "addon.mod_lesson.averagetime": "زمان میانگین", - "addon.mod_lesson.branchtable": "مندرجات", - "addon.mod_lesson.cannotfindattempt": "خطا: پیدا کردن تلاش مقدور نبود", - "addon.mod_lesson.cannotfinduser": "خطا: پیدا کردن کاربران مقدور نبود", - "addon.mod_lesson.clusterjump": "سؤال دیده نشده از یک خوشه", - "addon.mod_lesson.completed": "تکمیل", - "addon.mod_lesson.congratulations": "تبریک - به انتهای مبحث درسی رسیدید", - "addon.mod_lesson.continue": "ادامه", - "addon.mod_lesson.defaultessayresponse": "پاسخ تشریحی شما توسط استادتان تصحیح خواهد شد.", - "addon.mod_lesson.detailedstats": "آمار تفصیلی", - "addon.mod_lesson.didnotanswerquestion": "به این سؤال پاسخ نداده است.", - "addon.mod_lesson.displayofgrade": "نمایش نمره (مختص شاگردان)", - "addon.mod_lesson.displayscorewithessays": "

                شما از قسمت سؤالاتی که به صورت خودکار تصحیح می‌شوند نمرهٔ {{$a.score}} از {{$a.tempmaxgrade}} را بدست آوردید.

                \n

                {{$a.essayquestions}} سؤال تشریحی باقیمانده بعداً تصحیح و نمرهٔ بدست آمده به نمرهٔ نهایی شما اضافه خواهد شد.

                \n

                نمرهٔ فعلی شما بدون در نظر گرفتن نمرهٔ سؤالات تشریحی {{$a.score}} از {{$a.grade}} است.

                ", - "addon.mod_lesson.displayscorewithoutessays": "نمره شما {{$a.score}} است (از {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "کلمهٔ رمز نمی‌تواند خالی باشد", - "addon.mod_lesson.enterpassword": "لطفاً کلمهٔ رمز را وارد نمائید:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "به هیچ سؤالی پاسخ ندادید. نمرهٔ شما در این مبحث درسی صفر می‌باشد.", - "addon.mod_lesson.firstwrong": "متأسفانه پاسخ شما اشتباه بود و نمرهٔ این سؤال را بدست نیاوردید. آیا مایلید که تنها برای لذت یادگیری (و بدون تغییر در نمره) به حدس زدن ادامه دهید؟", - "addon.mod_lesson.grade": "نمره", - "addon.mod_lesson.highscore": "بیشترین نمره", - "addon.mod_lesson.hightime": "بیشترین زمان", - "addon.mod_lesson.leftduringtimed": "شما مطالعهٔ یک مبحث درسی زمان‌دار را به صورت نیمه‌کاره رها کردید.
                برای شروع مجدد لطفاً بر روی ادامه کلیک کنید.", - "addon.mod_lesson.leftduringtimednoretake": "شما مطالعهٔ یک مبحث درسی زمان‌دار را به صورت نیمه‌کاره رها کردید و
                اجازهٔ شروع مجدد یا ادامهٔ آن را ندارید.", - "addon.mod_lesson.lessonmenu": "منوی مبحث درسی", - "addon.mod_lesson.lessonstats": "آمار مبحث درسی", - "addon.mod_lesson.linkedmedia": "پیوند به فایل", - "addon.mod_lesson.loginfail": "رمز وارد شده اشتباه بود. لطفاً مجدداً سعی کنید...", - "addon.mod_lesson.lowscore": "کمترین نمره", - "addon.mod_lesson.lowtime": "کمترین زمان", - "addon.mod_lesson.maximumnumberofattemptsreached": "حداکثر دفعات تلاش مجاز را انجام داده‌اید - حرکت به صفحهٔ بعد", - "addon.mod_lesson.modattemptsnoteacher": "قابلیت مرور فقط برای شاگردان کار می‌کند.", - "addon.mod_lesson.modulenameplural": "مباحث درسی", - "addon.mod_lesson.noanswer": "به یک یا چند سؤال پاسخی داده نشده است. لطفاً بازگردید و پاسخی را ارائه نمائید.", - "addon.mod_lesson.nolessonattempts": "هیچ تلاشی در این مبحث درسی صورت نگرفته است.", - "addon.mod_lesson.notcompleted": "تمام نشده", - "addon.mod_lesson.numberofcorrectanswers": "تعداد پاسخ‌های صحیح: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "تعداد سؤال‌هایی که پاسخ داده‌اید: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "تعداد سؤال‌هایی که پاسخ داده‌اید: {{$a.nquestions}} (حداقل باید به {{$a.minquestions}} سؤال پاسخ دهید)", - "addon.mod_lesson.ongoingcustom": "تا الان {{$a.score}} نمره از {{$a.currenthigh}} بدست آورده‌اید.", - "addon.mod_lesson.ongoingnormal": "به {{$a.correct}} سؤال از {{$a.viewed}} سؤال پاسخ صحیح داده‌اید.", - "addon.mod_lesson.or": "یا", - "addon.mod_lesson.overview": "مرور کلی", - "addon.mod_lesson.preview": "پیش‌نمایش", - "addon.mod_lesson.progressbarteacherwarning2": "شما نوار پیشرفت را نخواهید دید زیرا می‌توانید این مبحث درسی را ویرایش کنید", - "addon.mod_lesson.progresscompleted": "{{$a}}٪ از این مبحث درسی را کامل کرده‌اید", - "addon.mod_lesson.question": "سؤال", - "addon.mod_lesson.rawgrade": "نمرهٔ خام", - "addon.mod_lesson.reports": "گزارش‌ها", - "addon.mod_lesson.response": "واکنش به جواب", - "addon.mod_lesson.review": "مرور", - "addon.mod_lesson.reviewlesson": "مرور مبحث درسی", - "addon.mod_lesson.reviewquestionback": "بله، می‌خواهم مجدداً تلاش کنم", - "addon.mod_lesson.reviewquestioncontinue": "خیر، می‌خواهم به سؤال بعدی بروم", - "addon.mod_lesson.secondpluswrong": "صحیح نیست. آیا مایلید مجدداً تلاش کنید؟", - "addon.mod_lesson.submit": "ارائه", - "addon.mod_lesson.teacherjumpwarning": "در این مبحث درسی از یک پرش به {{$a.cluster}} یا پرش به {{$a.unseen}} استفاده شده است. به جای آن‌ها از پرش به «صفحهٔ بعد» استفاده خواهد شد. برای آزمایش این پرش‌ها بعنوان شاگرد وارد شوید.", - "addon.mod_lesson.teacherongoingwarning": "نمره در حال شرکت فقط به شاگردان نمایش داده می‌شود. برای آزمایش نمره در حال شرکت، در قالب یک شاگر وارد شوید.", - "addon.mod_lesson.teachertimerwarning": "تایمر فقط برای شاگردان کار می‌کند. با ورود در قالب یک شاگرد، تایمر را آزمایش کنید.", - "addon.mod_lesson.thatsthecorrectanswer": "پاسخ شما صحیح است", - "addon.mod_lesson.thatsthewronganswer": "پاسخ شما اشتباه است", - "addon.mod_lesson.timeremaining": "زمان باقیمانده", - "addon.mod_lesson.timetaken": "زمان صرف شده", - "addon.mod_lesson.unseenpageinbranch": "سؤال دیده نشده از صفحه مندرجات", - "addon.mod_lesson.welldone": "آفرین!", - "addon.mod_lesson.youhaveseen": "شما قبلاً بیش از یک صفحه از این مبحث درسی را دیده اید.
                آیا می‌خواهید از آخرین صفحه‌ای که دیده‌اید شروع کنید؟", - "addon.mod_lesson.youranswer": "پاسخ شما", - "addon.mod_lesson.yourcurrentgradeisoutof": "نمرهٔ فعلی شما {{$a.grade}} از {{$a.total}} است", - "addon.mod_lesson.youshouldview": "باید حداقل به {{$a}} سؤال پاسخ دهید", - "addon.mod_lti.modulenameplural": "ابزارهای خارجی", - "addon.mod_page.modulenameplural": "صفحه‌ها", - "addon.mod_quiz.attemptfirst": "اولین تلاش", - "addon.mod_quiz.attemptlast": "آخرین تلاش", - "addon.mod_quiz.attemptnumber": "تلاش", - "addon.mod_quiz.attemptquiznow": "شرکت در آزمون", - "addon.mod_quiz.attemptstate": "وضعیت", - "addon.mod_quiz.comment": "توضیح", - "addon.mod_quiz.completedon": "پایان", - "addon.mod_quiz.confirmclose": "شما در آستانهٔ اتمام شرکت خود در آزمون هستید. پس ا ز اتمام شرکت دیگر نمی‌توانید پاسخ‌های خود را تغییر دهید.", - "addon.mod_quiz.confirmleavequizonerror": "در حین ذخیرهٔ پاسخ‌ها خطایی رخ داد. آیا مطمئن هستید که می‌خواهید از آزمون خارج شوید؟", - "addon.mod_quiz.confirmstart": "محدودیت زمانی این آزمون {{$a}} است. زمان شما از همان لحظه‌ای که آزمون را شروع کنید محاسبه خواهد شد و پیش از آنکه تمام شود، باید آزمون را تحویل دهید. آیا آماده شروع آزمون هستید؟", - "addon.mod_quiz.confirmstartheader": "آزمون زمان‌دار", - "addon.mod_quiz.connectionerror": "اتصال شبکه قطع شد. (ذخیرهٔ خودکار با شکست مواجه شد).\n\nپاسخ‌هایی که در چند دقیقهٔ اخیر در این صفحه وارد کرده‌اید را یادداشت کنید، سپس سعی کنید دوباره متصل شوید.\n\nپس از اتصال مجدد ارتباط، پاسخ‌های شما باید ذخیره شده باشند و این پیغام ناپدید خواهد شد.", - "addon.mod_quiz.continueattemptquiz": "ادامهٔ آخرین دفعهٔ شرکت در آزمون", - "addon.mod_quiz.continuepreview": "ادامهٔ آخرین پیش‌نمایش", - "addon.mod_quiz.feedback": "بازخورد", - "addon.mod_quiz.finishattemptdots": "اتمام آزمون...", - "addon.mod_quiz.grade": "نمره", - "addon.mod_quiz.gradeaverage": "نمرهٔ میانگین", - "addon.mod_quiz.gradehighest": "بیشترین نمره", - "addon.mod_quiz.grademethod": "روش نمره دادن", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} از {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "جمع نمره", - "addon.mod_quiz.modulenameplural": "آزمون‌ها", - "addon.mod_quiz.mustbesubmittedby": "تا پیش از {{$a}} باید پاسخ‌های خود را ثبت کنید.", - "addon.mod_quiz.noquestions": "هنوز سؤالی اضافه نشده است", - "addon.mod_quiz.outof": "{{$a.grade}} از {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} از {{$a.maxgrade}} (٪{{$a.percent}})", - "addon.mod_quiz.outofshort": "{{$a.grade}} از {{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "بازخورد کلی", - "addon.mod_quiz.overdue": "تأخیر تحویل", - "addon.mod_quiz.preview": "پیش‌نمایش", - "addon.mod_quiz.previewquiznow": "پیش‌نمایش آزمون", - "addon.mod_quiz.question": "سؤال", - "addon.mod_quiz.quiznavigation": "راهبری آزمون", - "addon.mod_quiz.quizpassword": "رمز دسترسی به آزمون", - "addon.mod_quiz.reattemptquiz": "شرکت مجدد در آزمون", - "addon.mod_quiz.returnattempt": "بازگشت و ادامهٔ آزمون", - "addon.mod_quiz.review": "مرور", - "addon.mod_quiz.reviewofattempt": "مرور دفعهٔ {{$a}} ام شرکت در آزمون", - "addon.mod_quiz.reviewofpreview": "مرور پیش‌نمایش", - "addon.mod_quiz.showall": "نمایش همهٔ سؤال‌ها در یک صفحه", - "addon.mod_quiz.showeachpage": "نمایش صفحه‌ها به صورت جداگانه", - "addon.mod_quiz.startattempt": "شرکت در آزمون", - "addon.mod_quiz.startedon": "شروع", - "addon.mod_quiz.stateabandoned": "هرگز تحویل داده نشده", - "addon.mod_quiz.statefinished": "پایان‌یافته", - "addon.mod_quiz.statefinisheddetails": "ثبت شده در: {{$a}}", - "addon.mod_quiz.stateinprogress": "در جریان", - "addon.mod_quiz.stateoverdue": "از موعد گذشته", - "addon.mod_quiz.status": "وضعیت", - "addon.mod_quiz.submitallandfinish": "ثبت همهٔ پاسخ‌ها و اتمام آزمون", - "addon.mod_quiz.summaryofattempt": "وضعیت شرکت در آزمون", - "addon.mod_quiz.summaryofattempts": "نتایج خلاصهٔ دفعه‌های قبلی شرکت شما در آزمون", - "addon.mod_quiz.timeleft": "زمان باقیمانده", - "addon.mod_quiz.timetaken": "زمان صرف شده", - "addon.mod_quiz.yourfinalgradeis": "نمرهٔ نهایی شما در این آزمون {{$a}} است.", - "addon.mod_resource.modulenameplural": "فایل‌ها", - "addon.mod_resource.openthefile": "باز کردن فایل", - "addon.mod_resource.uploadeddate": "(تاریخ بارگذاری: {{$a}})", - "addon.mod_scorm.asset": "دارایی", - "addon.mod_scorm.assetlaunched": "دارایی - دیده شده", - "addon.mod_scorm.attempts": "تلاش", - "addon.mod_scorm.averageattempt": "متوسط تعداد دفعات تلاش", - "addon.mod_scorm.browse": "پیش‌نمایش", - "addon.mod_scorm.browsed": "به صورت پیش‌نمایش دیده شده", - "addon.mod_scorm.browsemode": "حالت پیش‌نمایش", - "addon.mod_scorm.cannotcalculategrade": "نمرات نمی‌توانند محاسبه شوند", - "addon.mod_scorm.completed": "کامل شده", - "addon.mod_scorm.contents": "محتویات", - "addon.mod_scorm.enter": "ورود", - "addon.mod_scorm.errorinvalidversion": "متاسفانه برنامه تنها از اسکورم ۱٫۲ پشتیبانی می‌کند.", - "addon.mod_scorm.errorpackagefile": "متاسفانه برنامه تنها از بسته‌های ZIP پشتیبانی می‌کند.", - "addon.mod_scorm.exceededmaxattempts": "حداکثر تعداد تلاش‌ها را انجام داده‌اید.", - "addon.mod_scorm.failed": "ناموفق", - "addon.mod_scorm.firstattempt": "اولین تلاش", - "addon.mod_scorm.gradeaverage": "نمرهٔ میانگین", - "addon.mod_scorm.gradeforattempt": "نمرهٔ تلاش", - "addon.mod_scorm.gradehighest": "بالاترین نمره", - "addon.mod_scorm.grademethod": "روش نمره‌دهی", - "addon.mod_scorm.gradereported": "نمرهٔ گزارش شده", - "addon.mod_scorm.gradescoes": "اهداف آموزشی", - "addon.mod_scorm.gradesum": "جمع نمره", - "addon.mod_scorm.highestattempt": "بالاترین تلاش", - "addon.mod_scorm.incomplete": "ناتمام", - "addon.mod_scorm.lastattempt": "آخرین تلاش", - "addon.mod_scorm.modulenameplural": "بسته‌های اسکورم", - "addon.mod_scorm.newattempt": "شروع یک تلاش جدید", - "addon.mod_scorm.noattemptsallowed": "تعداد تلاش‌های مجاز", - "addon.mod_scorm.noattemptsmade": "تعداد تلاش‌هایی که انجام داده‌اید", - "addon.mod_scorm.notattempted": "شرکت صورت نگرفته است", - "addon.mod_scorm.organizations": "سازمان‌ها", - "addon.mod_scorm.passed": "موفق", - "addon.mod_scorm.reviewmode": "حالت مرور", - "addon.mod_scorm.score": "امتیاز", - "addon.mod_scorm.suspended": "معلق", - "addon.mod_scorm.toc": "فهرست", - "addon.mod_survey.cannotsubmitsurvey": "متاسفیم. مشکلی در ثبت نظرسنجی شما وجود داشت. لطفا دوباره تلاش کنید.", - "addon.mod_survey.ifoundthat": "متوجه شدم که:", - "addon.mod_survey.ipreferthat": "ترجیح می‌دهم که:", - "addon.mod_survey.modulenameplural": "ارزیابی‌ها", - "addon.mod_survey.responses": "پاسخ‌ها", - "addon.mod_survey.results": "نتایج", - "addon.mod_url.modulenameplural": "پیوندها", - "addon.mod_wiki.createpage": "ایجاد صفحه", - "addon.mod_wiki.editingpage": "ویرایش این صفحه: «{{$a}}»", - "addon.mod_wiki.map": "معماری صفحه‌ها", - "addon.mod_wiki.modulenameplural": "ویکی‌ها", - "addon.mod_wiki.newpagetitle": "عنوان صفحهٔ جدید", - "addon.mod_wiki.nocontent": "محتوایی برای این صفحه وجود ندارد", - "addon.mod_wiki.notingroup": "بدون گروه", - "addon.mod_wiki.pageexists": "این صفحه در حال حاضر وجود دارد. در حال تغییر مسیر به صفحهٔ موجود.", - "addon.mod_wiki.tagarea_wiki_pages": "صفحه‌های ویکی", - "addon.mod_wiki.viewpage": "مشاهدهٔ صفحه", - "addon.mod_wiki.wrongversionlock": "هنگامی که شما در حال ویرایش این صفحه بودید، کاربر دیگری آن را ویرایش کرد و محتوایش را تغییر داد.", - "addon.mod_workshop.alreadygraded": "نمره داده شده است", - "addon.mod_workshop.areainstructauthors": "دستورالعمل چگونگی تحویل دادن", - "addon.mod_workshop.areainstructreviewers": "دستورالعمل چگونگی ارزشیابی", - "addon.mod_workshop.assess": "ارزشیابی", - "addon.mod_workshop.assessedsubmission": "کار مورد ارزشیابی", - "addon.mod_workshop.assessmentform": "فرم ارزشیابی", - "addon.mod_workshop.assessmentsettings": "تنظیمات ارزشیابی", - "addon.mod_workshop.assessmentweight": "وزن ارزشیابی", - "addon.mod_workshop.assignedassessments": "کارهای محول شده برای ارزشیابی", - "addon.mod_workshop.assignedassessmentsnone": "کاری برای ارزشیابی به شما محول نشده است", - "addon.mod_workshop.createsubmission": "ارائه", - "addon.mod_workshop.editsubmission": "ویرایش کار ارائه شده", - "addon.mod_workshop.feedbackauthor": "بازخورد برای ارائه دهنده", - "addon.mod_workshop.feedbackby": "بازخورد توسط {{$a}}", - "addon.mod_workshop.feedbackreviewer": "بازخورد برای بازبین", - "addon.mod_workshop.givengrades": "نمره‌هایی که داده است", - "addon.mod_workshop.gradecalculated": "نمرهٔ محاسبه شده برای ارائه", - "addon.mod_workshop.gradeinfo": "نمره: {{$a.received}} از {{$a.max}}", - "addon.mod_workshop.gradeover": "ابطال نمرهٔ محاسبه شده برای ارائه و استفاده از این نمره", - "addon.mod_workshop.gradesreport": "گزارش نمره‌های کارگاه", - "addon.mod_workshop.gradinggrade": "نمره برای ارزشیابی کردن", - "addon.mod_workshop.gradinggradecalculated": "نمرهٔ محاسبه شده برای ارزشیابی", - "addon.mod_workshop.gradinggradeof": "نمره برای ارزشیابی (از {{$a}})", - "addon.mod_workshop.gradinggradeover": "ابطال نمرهٔ محاسبه شده و استفاده از این نمره برای ارزشیابی", - "addon.mod_workshop.modulenameplural": "کارگاه‌ها", - "addon.mod_workshop.nogradeyet": "هنوز نمره ندارد", - "addon.mod_workshop.notassessed": "هنوز ارزشیابی نشده است", - "addon.mod_workshop.notoverridden": "باطل نشود", - "addon.mod_workshop.noyoursubmission": "هنوز کارتان را تحویل نداده‌اید", - "addon.mod_workshop.publishedsubmissions": "کارهای منتشر شده", - "addon.mod_workshop.publishsubmission": "انتشار کار ارائه شده", - "addon.mod_workshop.publishsubmission_help": "ارائه‌های منتشر شده پس از بسته شدن کارگاه در دسترس سایرین خواهند بود.", - "addon.mod_workshop.reassess": "ارزشیابی مجدد", - "addon.mod_workshop.receivedgrades": "نمره‌هایی که دریافت کرده است", - "addon.mod_workshop.submissionattachment": "پیوست", - "addon.mod_workshop.submissioncontent": "محتوای ارائه", - "addon.mod_workshop.submissiongrade": "نمره برای تحویل دادن", - "addon.mod_workshop.submissiongradeof": "نمره برای کار ارائه شده (از {{$a}})", - "addon.mod_workshop.submissiontitle": "عنوان", - "addon.mod_workshop.userplan": "طراح کارگاه", - "addon.mod_workshop.weightinfo": "وزن: {{$a}}", - "addon.mod_workshop.yourassessment": "ارزشیابی شما", - "addon.mod_workshop.yoursubmission": "کار شما", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "جنبهٔ {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "باید یک نمره برای این جنبه انتخاب کنید", - "addon.mod_workshop_assessment_comments.dimensionnumber": "جنبهٔ {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "ادعای {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "معیار {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "باید یکی از این موارد را انتخاب کنید", - "addon.notes.addnewnote": "اضافه کردن یک یادداشت جدید", - "addon.notes.coursenotes": "یادداشت‌های درس", - "addon.notes.deleteconfirm": "حذف این یادداشت؟", - "addon.notes.eventnotecreated": "یادداشت ایجاد شد", - "addon.notes.eventnotedeleted": "یادداشت حذف شد", - "addon.notes.nonotes": "هنوز یادداشتی از این نوع نوشته نشده است", - "addon.notes.note": "یادداشت", - "addon.notes.notes": "یادداشت‌ها", - "addon.notes.personalnotes": "یادداشت‌های خصوصی", - "addon.notes.publishstate": "دامنه", - "addon.notes.sitenotes": "یادداشت‌های سایت", - "addon.notes.userwithid": "کاربر با ID {{id}}", - "addon.notifications.markallread": "علامت‌زدن همه به‌عنوان خوانده‌شده", - "addon.notifications.notificationpreferences": "ترجیحات اطلاعیه‌ها", - "addon.notifications.notifications": "هشدارها", - "addon.notifications.playsound": "پخش صدا", - "addon.notifications.therearentnotificationsyet": "هیچ هشداری وجود ندارد", - "assets.countries.AD": "آندورا", - "assets.countries.AE": "امارات متحدهٔ عربی", - "assets.countries.AF": "افغانستان", - "assets.countries.AG": "آنتیگوا و باربودا", - "assets.countries.AI": "آنگویلا", - "assets.countries.AL": "آلبانی", - "assets.countries.AM": "ارمنستان", - "assets.countries.AO": "آنگولا", - "assets.countries.AQ": "جنوبگان", - "assets.countries.AR": "آرژانتین", - "assets.countries.AS": "ساموآی آمریکا", - "assets.countries.AT": "اتریش", - "assets.countries.AU": "استرالیا", - "assets.countries.AW": "آروبا", - "assets.countries.AX": "جزایر اولان", - "assets.countries.AZ": "آذربایجان", - "assets.countries.BA": "بوسنی و هرزگوین", - "assets.countries.BB": "باربادوس", - "assets.countries.BD": "بنگلادش", - "assets.countries.BE": "بلژیک", - "assets.countries.BF": "بورکینافاسو", - "assets.countries.BG": "بلغارستان", - "assets.countries.BH": "بحرین", - "assets.countries.BI": "بروندی", - "assets.countries.BJ": "بنین", - "assets.countries.BL": "سنت بارتلمی", - "assets.countries.BM": "برمودا", - "assets.countries.BN": "برونئی", - "assets.countries.BO": "بولیوی، دولت چند ملیتی", - "assets.countries.BQ": "بونیر، سینت یوستیشس و سیبا", - "assets.countries.BR": "برزیل", - "assets.countries.BS": "باهاماس", - "assets.countries.BT": "بوتان", - "assets.countries.BV": "جزیرهٔ بووه", - "assets.countries.BW": "بوتسوانا", - "assets.countries.BY": "بلاروس", - "assets.countries.BZ": "بلایز", - "assets.countries.CA": "کانادا", - "assets.countries.CC": "جزایر (کیلینگ) کوکو", - "assets.countries.CD": "کنگو، جمهوری دمکرات", - "assets.countries.CF": "آفریقای مرکزی", - "assets.countries.CG": "کنگو", - "assets.countries.CH": "سوئیس", - "assets.countries.CI": "ساحل عاج", - "assets.countries.CK": "جزایر کوک", - "assets.countries.CL": "شیلی", - "assets.countries.CM": "کامرون", - "assets.countries.CN": "چین", - "assets.countries.CO": "کلمبیا", - "assets.countries.CR": "کاستاریکا", - "assets.countries.CU": "کوبا", - "assets.countries.CV": "کیپ ورد", - "assets.countries.CW": "کوراسائو", - "assets.countries.CX": "جزیرهٔ کریسمس", - "assets.countries.CY": "قبرس", - "assets.countries.CZ": "جمهوری چک", - "assets.countries.DE": "آلمان", - "assets.countries.DJ": "جیبوتی", - "assets.countries.DK": "دانمارک", - "assets.countries.DM": "دومنیکا", - "assets.countries.DO": "جمهوری دومنیکن", - "assets.countries.DZ": "الجزایر", - "assets.countries.EC": "اکوادور", - "assets.countries.EE": "استونی", - "assets.countries.EG": "مصر", - "assets.countries.EH": "صحرای غربی", - "assets.countries.ER": "اریتره", - "assets.countries.ES": "اسپانیا", - "assets.countries.ET": "اتیوپی", - "assets.countries.FI": "فنلاند", - "assets.countries.FJ": "فیجی", - "assets.countries.FK": "جزایر فالکلند", - "assets.countries.FM": "میکرونزی، ایالات فدرال", - "assets.countries.FO": "جزایر فارو", - "assets.countries.FR": "فرانسه", - "assets.countries.GA": "گابن", - "assets.countries.GB": "پادشاهی متحده", - "assets.countries.GD": "گرنادا", - "assets.countries.GE": "گرجستان", - "assets.countries.GF": "گویان فرانسه", - "assets.countries.GG": "گرنزی", - "assets.countries.GH": "غنا", - "assets.countries.GI": "جبل‌الطارق", - "assets.countries.GL": "گرینلند", - "assets.countries.GM": "گامبیا", - "assets.countries.GN": "گینه", - "assets.countries.GP": "گوادلوپ", - "assets.countries.GQ": "گینه استوایی", - "assets.countries.GR": "یونان", - "assets.countries.GS": "جزایر جورجیای جنوبی و ساندویچ جنوبی", - "assets.countries.GT": "گواتمالا", - "assets.countries.GU": "گوآم", - "assets.countries.GW": "گینه بیسائو", - "assets.countries.GY": "گویان", - "assets.countries.HK": "هنگ کنگ", - "assets.countries.HM": "جزیرهٔ هرد و جزایر مک دونالد", - "assets.countries.HN": "هندوراس", - "assets.countries.HR": "کرواسی", - "assets.countries.HT": "هائیتی", - "assets.countries.HU": "مجارستان", - "assets.countries.ID": "اندونزی", - "assets.countries.IE": "ایرلند", - "assets.countries.IL": "اسرائیل", - "assets.countries.IM": "جزیرهٔ مان", - "assets.countries.IN": "هندوستان", - "assets.countries.IO": "قلمرو اقیانوس هندی انگلیسی", - "assets.countries.IQ": "عراق", - "assets.countries.IR": "ایران، جمهوری اسلامی", - "assets.countries.IS": "ایسلند", - "assets.countries.IT": "ایتالیا", - "assets.countries.JE": "جرزی", - "assets.countries.JM": "جامائیکا", - "assets.countries.JO": "اردن", - "assets.countries.JP": "ژاپن", - "assets.countries.KE": "کنیا", - "assets.countries.KG": "قرقیزستان", - "assets.countries.KH": "کامبوج", - "assets.countries.KI": "کیریباتی", - "assets.countries.KM": "کومور", - "assets.countries.KN": "سنت کیتس و نویس", - "assets.countries.KP": "کرهٔ شمالی", - "assets.countries.KR": "کرهٔ جنوبی", - "assets.countries.KW": "کویت", - "assets.countries.KY": "جزایر کایمان", - "assets.countries.KZ": "قزاقستان", - "assets.countries.LA": "لائوس", - "assets.countries.LB": "لبنان", - "assets.countries.LC": "سنت لوسیا", - "assets.countries.LI": "لیشتنشتاین", - "assets.countries.LK": "سری لانکا", - "assets.countries.LR": "لیبریا", - "assets.countries.LS": "لسوتو", - "assets.countries.LT": "لیتوانی", - "assets.countries.LU": "لوکزامبورگ", - "assets.countries.LV": "لتونی", - "assets.countries.LY": "لیبی", - "assets.countries.MA": "مراکش", - "assets.countries.MC": "موناکو", - "assets.countries.MD": "مولداوی", - "assets.countries.ME": "مونته‌نگرو", - "assets.countries.MF": "سنت مارتین (بخش فرانسوی)", - "assets.countries.MG": "ماداگاسکار", - "assets.countries.MH": "جزایر مارشال", - "assets.countries.MK": "مقدونیه", - "assets.countries.ML": "مالی", - "assets.countries.MM": "میانمار", - "assets.countries.MN": "مغولستان", - "assets.countries.MO": "ماکائو", - "assets.countries.MP": "مجمع‌الجزایر ماریانای شمالی", - "assets.countries.MQ": "مارتینیک", - "assets.countries.MR": "موریتانی", - "assets.countries.MS": "مونتسرات", - "assets.countries.MT": "مالت", - "assets.countries.MU": "موریس (جزیره)", - "assets.countries.MV": "مالدیو", - "assets.countries.MW": "مالاوی", - "assets.countries.MX": "مکزیک", - "assets.countries.MY": "مالزی", - "assets.countries.MZ": "موزامبیک", - "assets.countries.NA": "نامیبیا", - "assets.countries.NC": "کالدونیای جدید", - "assets.countries.NE": "نیجر", - "assets.countries.NF": "جزیرهٔ نورفولک", - "assets.countries.NG": "نیجریه", - "assets.countries.NI": "نیکاراگوآ", - "assets.countries.NL": "هلند", - "assets.countries.NO": "نروژ", - "assets.countries.NP": "نپال", - "assets.countries.NR": "نائورو", - "assets.countries.NU": "نیووی", - "assets.countries.NZ": "نیوزیلند", - "assets.countries.OM": "عمان", - "assets.countries.PA": "پاناما", - "assets.countries.PE": "پرو", - "assets.countries.PF": "پلی‌نزی فرانسه", - "assets.countries.PG": "پاپوآ گینهٔ نو", - "assets.countries.PH": "فیلیپین", - "assets.countries.PK": "پاکستان", - "assets.countries.PL": "لهستان", - "assets.countries.PM": "سنت پیر و ماژلان", - "assets.countries.PN": "جزایر پیت کرن", - "assets.countries.PR": "پورتوریکو", - "assets.countries.PS": "فلسطین، دولت", - "assets.countries.PT": "پرتقال", - "assets.countries.PW": "پالائو", - "assets.countries.PY": "پاراگوئه", - "assets.countries.QA": "قطر", - "assets.countries.RE": "رئونیون", - "assets.countries.RO": "رومانی", - "assets.countries.RS": "صربستان", - "assets.countries.RU": "روسیه", - "assets.countries.RW": "رواندا", - "assets.countries.SA": "عربستان سئودی", - "assets.countries.SB": "جزایر سلیمان", - "assets.countries.SC": "سیشل", - "assets.countries.SD": "سودان", - "assets.countries.SE": "سوئد", - "assets.countries.SG": "سنگاپور", - "assets.countries.SH": "سنت هلنا، اسنشن و تریستان دا کونا", - "assets.countries.SI": "اسلوونی", - "assets.countries.SJ": "سوالبارد و یان ماین", - "assets.countries.SK": "اسلواکی", - "assets.countries.SL": "سیرالئون", - "assets.countries.SM": "سان مارینو", - "assets.countries.SN": "سنگال", - "assets.countries.SO": "سومالی", - "assets.countries.SR": "سورینام", - "assets.countries.SS": "سودان جنوبی", - "assets.countries.ST": "سائوتومه و پرینسیپ", - "assets.countries.SV": "السالوادور", - "assets.countries.SX": "سینت مارتن (بخش هلندی)", - "assets.countries.SY": "سوریه", - "assets.countries.SZ": "سوازیلند", - "assets.countries.TC": "جزایر ترکس و کایکوس", - "assets.countries.TD": "چاد", - "assets.countries.TF": "سرزمین‌های جنوبی فرانسوی", - "assets.countries.TG": "توگو", - "assets.countries.TH": "تایلند", - "assets.countries.TJ": "تاجیکستان", - "assets.countries.TK": "توکلائو", - "assets.countries.TL": "تیمور شرقی", - "assets.countries.TM": "ترکمنستان", - "assets.countries.TN": "تونس", - "assets.countries.TO": "تونگا", - "assets.countries.TR": "ترکیه", - "assets.countries.TT": "ترینیداد و توباگو", - "assets.countries.TV": "تووالو", - "assets.countries.TW": "تایوان", - "assets.countries.TZ": "تانزانیا، جمهوری متحد", - "assets.countries.UA": "اوکراین", - "assets.countries.UG": "اوگاندا", - "assets.countries.UM": "جزایر کوچک حاشیه‌ای ایالات متحده", - "assets.countries.US": "ایالات متحده", - "assets.countries.UY": "اوروگوئه", - "assets.countries.UZ": "ازبکستان", - "assets.countries.VA": "دریای مقدس (واتیکان)", - "assets.countries.VC": "سنت وینسنت و گرنادین", - "assets.countries.VE": "ونزوئلا، جمهوری بولیواری", - "assets.countries.VG": "جزایر ویرجین بریتانیا", - "assets.countries.VI": "مجمع‌الجزایر ویرجین آمریکا", - "assets.countries.VN": "ویتنام", - "assets.countries.VU": "وانواتو", - "assets.countries.WF": "والیس و فوتونا", - "assets.countries.WS": "ساموآ", - "assets.countries.YE": "یمن", - "assets.countries.YT": "مایوت", - "assets.countries.ZA": "آفریقای جنوبی", - "assets.countries.ZM": "زامبیا", - "assets.countries.ZW": "زیمباوه", - "assets.mimetypes.application/epub_zip": "کتاب الکترونیکی EPUB", - "assets.mimetypes.application/msword": "سند ورد", - "assets.mimetypes.application/pdf": "سند PDF", - "assets.mimetypes.application/vnd.moodle.backup": "پشتیبان مودل", - "assets.mimetypes.application/vnd.ms-excel": "صفحهٔ گستردهٔ اکسل", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "ورک‌بوک اکسل ۲۰۰۷ با ماکروهای فعال", - "assets.mimetypes.application/vnd.ms-powerpoint": "ارائه پاورپوینت", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "صفحه گسترده اوپن‌داکیومنت", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "قالب صفحه گسترده اوپن‌داکیومنت", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "سند متنی اوپن‌داکیومنت", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "قالب متنی اوپن‌داکیومنت", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "قالب صفحهٔ وب اوپن‌داکیومنت", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "ارائه پاورپوینت ۲۰۰۷", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "نمایش اسلاید پاورپوینت ۲۰۰۷", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "صفحهٔ گستردهٔ اکسل ۲۰۰۷", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "قالب اکسل ۲۰۰۷", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "سند ورد ۲۰۰۷", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "سخنرانی آی‌ورک", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "صفحه گسترده اعداد آی‌ورک", - "assets.mimetypes.application/x-iwork-pages-sffpages": "سند صفحه‌های آی‌ورک", - "assets.mimetypes.application/x-javascript": "کد جاوااسکریپت", - "assets.mimetypes.application/x-mspublisher": "سند پابلیشر", - "assets.mimetypes.application/x-shockwave-flash": "انیمیشن فلش", - "assets.mimetypes.application/xhtml_xml": "سند XHTML", - "assets.mimetypes.archive": "آرشیو ({{$a.EXT}})", - "assets.mimetypes.audio": "فایل صوتی ({{$a.EXT}})", - "assets.mimetypes.default": "", - "assets.mimetypes.document/unknown": "فایل", - "assets.mimetypes.group:archive": "فایل‌های آرشیو", - "assets.mimetypes.group:audio": "فایل‌های صوتی", - "assets.mimetypes.group:document": "فایل‌های اسناد", - "assets.mimetypes.group:html_audio": "فایل‌های صوتی که به‌طور بومی توسط مرورگرها پشتیبانی می‌شوند", - "assets.mimetypes.group:html_video": "فایل‌های ویدیویی که به‌طور بومی توسط مرورگرها پشتیبانی می‌شوند", - "assets.mimetypes.group:image": "فایل‌های تصویری", - "assets.mimetypes.group:presentation": "فایل‌های ارائه", - "assets.mimetypes.group:sourcecode": "کد منبع", - "assets.mimetypes.group:spreadsheet": "فایل‌های صفحه گسترده", - "assets.mimetypes.group:video": "فایل‌های ویدیویی", - "assets.mimetypes.group:web_audio": "فایل‌های صوتی استفاده‌شده در وب", - "assets.mimetypes.group:web_file": "فایل‌های وب", - "assets.mimetypes.group:web_image": "فایل‌های تصویری استفاده‌شده در وب", - "assets.mimetypes.group:web_video": "فایل‌های ویدیویی استفاده‌شده در وب", - "assets.mimetypes.image": "عکس ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "آیکن ویندوز", - "assets.mimetypes.text/csv": "مقادیر جداشده با کاما", - "assets.mimetypes.text/html": "سند HTML", - "assets.mimetypes.text/plain": "فایل متنی", - "assets.mimetypes.text/rtf": "سند RTF", - "assets.mimetypes.text/vtt": "تراک متنی ویدیوی تحت وب", - "assets.mimetypes.video": "فایل ویدیویی ({{$a.EXT}})", - "core.accounts": "حساب‌های کاربری", - "core.add": "اضافه کردن", - "core.ago": "{{$a}} قبل", - "core.all": "همه", - "core.allgroups": "همهٔ گروه‌ها", - "core.allparticipants": "همهٔ اعضاء", - "core.answer": "پاسخ", - "core.answered": "پاسخ داده شد", - "core.areyousure": "آیا مطمئن هستید؟", - "core.back": "بازگشت", - "core.block.blocks": "بلوک‌ها", - "core.cancel": "انصراف", - "core.cannotconnect": "اتصال به سایت ممکن نبود. بررسی کنید که نشانی سایت را درست وارد کرده باشید و اینکه سایت شما از مودل ۲٫۴ یا جدیدتر استفاده کند.", - "core.captureaudio": "ضبط صدا", - "core.capturedimage": "عکس گرفته‌شده.", - "core.captureimage": "گرفتن عکس", - "core.capturevideo": "ضبط ویديو", - "core.category": "طبقه", - "core.choose": "انتخاب کنید", - "core.choosedots": "انتخاب کنید...", - "core.clearsearch": "پاک کردن جستجو", - "core.clicktohideshow": "برای باز یا بسته شدن کلیک کنید", - "core.comments": "نظرات", - "core.comments.addcomment": "اضافه کردن نظر...", - "core.comments.comments": "نظرات", - "core.comments.commentscount": "نظرات ({{$a}})", - "core.comments.deletecommentbyon": "پاک‌کردن نظر ارسال‌شده توسط {{$a.user}} در {{$a.time}}", - "core.comments.eventcommentcreated": "توضیح ایجاد شد", - "core.comments.eventcommentdeleted": "توضیح حذف شد", - "core.comments.nocomments": "بدون دیدگاه", - "core.comments.savecomment": "ثبت نظر", - "core.commentscount": "نظرات ({{$a}})", - "core.completion-alt-auto-fail": "تکمیل شده است (بدون اکتساب نمرهٔ قبولی)", - "core.completion-alt-auto-n": "تکمیل نشده است", - "core.completion-alt-auto-pass": "تکمیل شده است (با اکتساب نمرهٔ قبولی)", - "core.completion-alt-auto-y": "تکمیل شده است", - "core.completion-alt-manual-n": "کامل نشده است؛ انتخاب کنید تا به عنوان کامل شده علامت بخورد", - "core.completion-alt-manual-y": "کامل شده است؛ انتخاب کنید تا به عنوان کامل شده علامت بخورد", - "core.confirmcanceledit": "آیا مطمئنید که می‌خواهید این صفحه را ترک کنید؟ تمام تغییرات از بین خواهند رفت.", - "core.confirmdeletefile": "آیا مطمئنید که می‌خواهید این فایل را حذف کنید؟", - "core.confirmloss": "آیا مطمئن هستید؟ تمام تغییرات از بین خواهند رفت.", - "core.considereddigitalminor": "سن شما برای ایجاد حساب کاربری در این سایت کم ایست.", - "core.content": "محتوا", - "core.continue": "ادامه", - "core.course": "درس", - "core.course.allsections": "تمام قسمت‌ها", - "core.course.confirmdeletemodulefiles": "آیا مطمئنید که می‌خواهید فایل‌های این ماژول را پاک کنید؟", - "core.course.confirmdownload": "شما در آستانهٔ دریافت {{size}} هستید. آیا مطمئنید که می‌خواهید ادامه دهید؟", - "core.course.confirmdownloadunknownsize": "ما نتوانستیم حجم دریافت را محاسبه کنیم. آیا مطمئنید که می‌خواهید ادامه دهید؟", - "core.course.confirmpartialdownloadsize": "شما در آستانهٔ دریافت حداقل {{size}} هستید. آیا مطمئنید که می‌خواهید ادامه دهید؟", - "core.course.contents": "محتواها", - "core.course.couldnotloadsectioncontent": "محتوای قسمت نتوانست بارگیری شود. لطفا بعدا دوباره تلاش کنید.", - "core.course.couldnotloadsections": "قسمت‌ها نتوانستند بارگیری شوند. لطفا بعدا دوباره تلاش کنید.", - "core.course.coursesummary": "توصیف درس", - "core.course.hiddenfromstudents": "پنهان از شاگردان", - "core.course.hiddenoncoursepage": "قابل دسترسی است ولی در صفحهٔ درس نمایش داده نمی‌شود", - "core.course.sections": "قسمت‌ها", - "core.course.useactivityonbrowser": "البته همچنان می‌توانید با استفاده از مرورگر دستگاه خود، از آن استفاده کنید.", - "core.coursedetails": "جزئیات درس", - "core.courses.addtofavourites": "ستاره‌دار کردن این درس", - "core.courses.allowguests": "ورود کاربران مهمان به این درس مجاز است", - "core.courses.availablecourses": "درس‌های موجود", - "core.courses.categories": "طبقه‌های درسی", - "core.courses.confirmselfenrol": "آیا مطمئنید که می‌خواهید خود را در این درس ثبت‌نام کنید؟", - "core.courses.courses": "درس‌ها", - "core.courses.errorloadcourses": "در هنگام بارگیری درس‌ها خطایی رخ داد.", - "core.courses.filtermycourses": "پالایش درس‌های من", - "core.courses.frontpage": "صفحهٔ اول", - "core.courses.hidecourse": "حذف از نمایش دادن", - "core.courses.ignore": "نادیده بگیر", - "core.courses.mycourses": "درس‌های من", - "core.courses.mymoodle": "میز کار", - "core.courses.nocourses": "هیچ درسی برای نمایش وجود ندارد", - "core.courses.nocoursesyet": "درسی در این طبقه وجود ندارد", - "core.courses.nosearchresults": "بدون نتیجه", - "core.courses.notenroled": "شما در این درس ثبت‌نام نیستید", - "core.courses.paymentrequired": "ثبت‌نام در این درس مستلزم پرداخت شهریه است.", - "core.courses.reload": "بررسی مجدد", - "core.courses.removefromfavourites": "بی ستاره‌دار کردن این درس", - "core.courses.search": "جستجو", - "core.courses.searchcourses": "جستجو بین درس‌ها", - "core.courses.show": "بازیابی برای نمایش", - "core.currentdevice": "دستگاه فعلی", - "core.datastoredoffline": "ارسال اطلاعات ناموفق بود. اطلاعات روی دستگاه ذخیره شد و بعدا به‌طور خودکار فرستاده خواهد شد.", - "core.date": "تاریخ", - "core.day": "روز", - "core.days": "روز", - "core.decsep": ".", - "core.defaultvalue": "پیش‌فرض ({{$a}})", - "core.delete": "حذف", - "core.description": "توصیف", - "core.digitalminor_desc": "لطفا از سرپرست یا والدین خود بخواهید تا با این شخص تماس بگیرد:", - "core.displayoptions": "تنظیمات نمایش", - "core.done": "پر کرده است", - "core.download": "دریافت", - "core.edit": "ویرایش ", - "core.editor.autosavesucceeded": "پیش‌نویس ذخیره شد.", - "core.editor.bold": "ذخیم", - "core.editor.h3": "عنوان (بزرگ)", - "core.editor.h4": "عنوان (متوسط)", - "core.editor.h5": "عنوان (کوچک)", - "core.editor.italic": "مورب", - "core.editor.orderedlist": "لیست ترتیبی", - "core.editor.p": "پاراگراف", - "core.editor.strike": "خط‌خورده", - "core.editor.unorderedlist": "لیست غیرترتیبی", - "core.error": "خطا", - "core.errordownloading": "خطا در دانلود فایل", - "core.favourites": "ستاره‌دار", - "core.filenotfound": "متاسفانه فایل پیدا نشد.", - "core.fileuploader.addfiletext": "اضافه‌کردن فایل", - "core.fileuploader.confirmuploadfile": "شما در آستانهٔ ارسال {{size}} هستید. آیا مطمئنید که می‌خواهید ادامه دهید؟", - "core.fileuploader.confirmuploadunknownsize": "ما نتوانستیم حجم ارسال را محاسبه کنیم. آیا مطمئنید که می‌خواهید ادامه دهید؟", - "core.fileuploader.errorcapturingaudio": "خطا در ضبط صدا", - "core.fileuploader.errorcapturingvideo": "خطا در ضبط ویدئو", - "core.fileuploader.filesofthesetypes": "انواع فایل‌های مورد پذیرش:", - "core.fileuploader.fileuploaded": "فایل آپلود شده", - "core.fileuploader.invalidfiletype": "فایل از نوع {{$a}} نمی‌تواند پذیرفته شود.", - "core.fileuploader.more": "نمایش جزئیات", - "core.fileuploader.uploading": "درحال بارگذاری", - "core.fileuploader.video": "ویدئو", - "core.filter": "فیلتر", - "core.folder": "پوشه", - "core.forcepasswordchangenotice": "برای پیش‌روی باید رمز ورود خود را تغییر دهید.", - "core.fulllistofcourses": "همهٔ درس‌ها", - "core.grades.average": "میانگین", - "core.grades.contributiontocoursetotal": "میزان تاثیر در نمره نهایی درس", - "core.grades.feedback": "بازخورد", - "core.grades.grade": "نمره", - "core.grades.gradeitem": "مورد", - "core.grades.grades": "نمره‌ها", - "core.grades.lettergrade": "نمرهٔ حرفی", - "core.grades.percentage": "درصد", - "core.grades.range": "محدوده", - "core.grades.weight": "وزن", - "core.group": "گروه", - "core.groupsseparate": "گروه‌های جداگانه", - "core.groupsvisible": "گروه‌های مرئی", - "core.help": "راهنمایی", - "core.hide": "پنهان کردن", - "core.hour": "ساعت", - "core.hours": "ساعت", - "core.imageviewer": "نمایشگر تصویر", - "core.info": "توضیحات", - "core.invalidformdata": "دادهٔ فرم نادرست", - "core.labelsep": ": ", - "core.lastaccess": "زمان آخرین دسترسی", - "core.lastmodified": "آخرین تغییر", - "core.layoutgrid": "جدول", - "core.list": "لیست محتوا", - "core.listsep": ",", - "core.loading": "در حال بارگیری", - "core.location": "مکان", - "core.login.auth_email": "عضویت بر اساس پست الکترونیک", - "core.login.authenticating": "در حال شناسایی", - "core.login.cancel": "انصراف", - "core.login.changepassword": "تغییر رمز ورود", - "core.login.confirmdeletesite": "آیا مطمئن هستید که می‌خواهید سایت {{sitename}} را پاک کنید؟", - "core.login.connecttomoodle": "اتصال به مودل", - "core.login.createaccount": "ایجاد حساب کاربری من", - "core.login.createuserandpass": "نام کاربری و رمز عبور خود را انتخاب کنید", - "core.login.emailconfirmsent": "

                باید نامه‌ای به آدرس شما در {{$a}} فرستاده شده باشد

                \n

                این نامه شامل دستورالعمل‌های ساده‌ای برای تکمیل عضویت شما است.

                \n

                در صورت تداوم مواجهه با مشکل، با مدیر سایت تماس بگیرید.

                ", - "core.login.emailconfirmsentsuccess": "ارسال ایمیل تاییدیه موفقیت آمیز بود", - "core.login.firsttime": "برای اولین بار به این صفحه آمده‌اید؟", - "core.login.forcepasswordchangenotice": "برای پیش‌روی باید رمز ورود خود را تغییر دهید.", - "core.login.forgotten": "نام کاربری و یا رمز ورود خود را فراموش کرده‌اید؟", - "core.login.help": "راهنمایی", - "core.login.helpmelogin": "

                چندین هزار سایت مودل در دنیا وجود دارد. این برنامه فقط می‌تواند به آن مودل‌هایی متصل شود که صراحتا دسترسی «برنامه موبایل» را فعال کرده‌باشند.

                اگر نمی‌توانید به سایت مودل خود وصل شوید، باید با مدیر مودل آن سایت تماس بگیرید و بخواهید که http://docs.moodle.org/en/Mobile_app را مطالعه کنند.

                \n

                برای آزمایش برنامه با استفاده از یک سایت آزمایشی، در قسمت مربوط به آدرس سایت عبارت teacher یا student را وارد کرده و بر روی دکمه اتصال کلیک کنید.

                ", - "core.login.instructions": "دستورالعمل", - "core.login.invalidaccount": "لطفا اطلاعات ورودخود را بررسی نمایید", - "core.login.invaliddate": "تاریخ نامعتبر", - "core.login.invalidemail": "آدرس پست الکترونیک نامعتبر", - "core.login.invalidmoodleversion": "این نسخه از برنامه سایت قابلیت اتصال به موبایل ندارد", - "core.login.invalidsite": "نشانی سایت معتبر نیست", - "core.login.login": "ورود به سایت", - "core.login.logininsiterequired": "لازم است به سایت از طریق مرورگر متصل گردید", - "core.login.loginsteps": "برای داشتن دسترسی کامل به این سایت، پیش از هر چیز باید یک حساب کاربری بسازید.", - "core.login.missingemail": "آدرس پست الکترونیک را وارد کنید", - "core.login.missingfirstname": "نام را وارد کنید", - "core.login.missinglastname": "نام خانوادگی را وارد کنید", - "core.login.mobileservicesnotenabled": "سرویس دسترسی موبایل در سایت شما فعال نیست", - "core.login.mustconfirm": "شما نیاز به تایید حساب کاربری خود دارید", - "core.login.newaccount": "حساب کاربری جدید", - "core.login.password": "رمز ورود", - "core.login.passwordforgotten": "رمز ورود فراموش شده", - "core.login.passwordforgotteninstructions2": "برای بازنشانی رمز ورودتان، نام کاربری یا آدرس پست الکترونیک خود را وارد نمائید. در صورتی که مشخصات ورودی شما در پایگاه داده پیدا شود، یک نامهٔ الکترونیکی شامل دستورالعمل‌هایی در مورد نحوه دسترسی دوباره به آدرس پست الکترونیک شما ارسال خواهد شد.", - "core.login.passwordrequired": "گذرواژه مورد نیاز است", - "core.login.policyaccept": "موافقم و می‌پذیرم", - "core.login.policyagree": "برای ادامهٔ استفاده از این سایت باید موافقت خود را با خط مشی آن اعلام کنید. آیا موافقید؟", - "core.login.policyagreement": "موافقت با خط مشی سایت", - "core.login.policyagreementclick": "مشاهدهٔ موافقت‌نامهٔ خط مشی سایت", - "core.login.potentialidps": "ورود با استفاده از حساب شما روی:", - "core.login.profileinvaliddata": "مقدار نامعتبر", - "core.login.resendemail": "ارسال مجدد ایمیل", - "core.login.security_question": "سؤال امنیتی", - "core.login.selectacountry": "انتخاب کشور", - "core.login.siteaddress": "آدرس سایت", - "core.login.siteinmaintenance": "سایت شما در حالت نگهداشت می باشد", - "core.login.siteurl": "نشانی سایت", - "core.login.siteurlrequired": "نشانی سایت لازم است. مثال: http://www.yourmoodlesite.abc یا https://www.yourmoodlesite.efg", - "core.login.startsignup": "ایجاد حساب کاربری جدید", - "core.login.supplyinfo": "جزئیات بیشتر", - "core.login.username": "نام کاربری", - "core.login.usernameoremail": "نام کاربری یا آدرس پست الکترونیک را وارد کنید", - "core.login.usernamerequired": "نام کاربر لازم است", - "core.login.usernotaddederror": "کاربر اضافه نشد - خطا", - "core.login.webservicesnotenabled": "وب سرویس سایت اشکال دارد.با مدیر سایت هماهنگ نمایید", - "core.lostconnection": "اطلاعات توکن شناسایی شما معتبر نیست یا منقضی شده است. باید دوباره به سایت متصل شوید.", - "core.mainmenu.help": "راهنمایی", - "core.mainmenu.logout": "خروج از سایت", - "core.mainmenu.website": "پایگاه اینترنتی", - "core.maxsizeandattachments": "حداکثر اندازه برای فایل‌های جدید: {{$a.size}}، حداکثر تعداد فایل‌های پیوست: {{$a.attachments}}", - "core.min": "دقیقه", - "core.mins": "دقیقه", - "core.misc": "متفرقه", - "core.mod_assign": "تکلیف", - "core.mod_assignment": "تکلیف ۲٫۲ (غیرفعال)", - "core.mod_book": "کتاب", - "core.mod_chat": "اتاق گفتگو", - "core.mod_choice": "انتخاب", - "core.mod_data": "بانک اطلاعاتی", - "core.mod_database": "بانک اطلاعاتی", - "core.mod_external-tool": "ابزار خارجی", - "core.mod_feedback": "بازخورد", - "core.mod_file": "فایل", - "core.mod_folder": "پوشه", - "core.mod_forum": "تالار گفتگو", - "core.mod_glossary": "واژه‌نامه", - "core.mod_ims": "بستهٔ محتوای IMS", - "core.mod_imscp": "بستهٔ محتوای IMS", - "core.mod_label": "برچسب", - "core.mod_lesson": "مبحث درسی", - "core.mod_lti": "ابزار خارجی", - "core.mod_page": "صفحه", - "core.mod_quiz": "آزمون", - "core.mod_resource": "فایل", - "core.mod_scorm": "بستهٔ اسکورم", - "core.mod_survey": "ارزیابی", - "core.mod_url": "پیوند", - "core.mod_wiki": "ویکی", - "core.mod_workshop": "کارگاه", - "core.moduleintro": "توصیف", - "core.more": "بیشتر", - "core.mygroups": "گروه‌های من", - "core.name": "نام", - "core.networkerrormsg": "شبکه قابل دسترسی نیست", - "core.never": "هیچ‌وقت", - "core.next": "ادامه", - "core.no": "خیر", - "core.nocomments": "بدون دیدگاه", - "core.nograde": "بدون نمره", - "core.none": "هیچ", - "core.nopermissions": "متأسفیم، در حال حاضر شما مجوز انجام این کار را ندارید ({{$a}})", - "core.noresults": "بدون نتیجه", - "core.noselection": "چیزی انتخاب نشده", - "core.notenrolledprofile": "این مشخصات فردی قابل مشاهده نمی‌باشد زیرا کاربر مورد نظر در این درس ثبت‌نام نیست.", - "core.notice": "اخطار", - "core.notingroup": "متأسفیم، ولی برای دیدن این فعالیت باید جزئی از یک گروه باشید.", - "core.now": "اکنون", - "core.numwords": "{{$a}} کلمه", - "core.offline": "آفلاین", - "core.ok": "تایید", - "core.online": "آنلاین", - "core.openinbrowser": "باز کردن در مرورگر", - "core.othergroups": "سایر گروه‌ها", - "core.pagea": "صفحه {{$a}}", - "core.paymentinstant": "از دکمهٔ زیر برای پرداخت و ثبت‌نام شدن در درس در ظرف چند دقیقه استفاده نمائید!", - "core.phone": "تلفن", - "core.pictureof": "عکس {{$a}}", - "core.previous": "قبلی", - "core.pulltorefresh": "برای تازه‌سازی بکشید", - "core.question.answer": "پاسخ", - "core.question.answersaved": "پاسخ ذخیره شده", - "core.question.complete": "کامل", - "core.question.correct": "درست", - "core.question.feedback": "بازخورد", - "core.question.incorrect": "نادرست", - "core.question.information": "توضیح", - "core.question.invalidanswer": "پاسخ ناقص", - "core.question.notanswered": "پاسخ داده نشده", - "core.question.notyetanswered": "هنوز پاسخ داده نشده است", - "core.question.partiallycorrect": "پاسخ نیمه درست", - "core.question.questionno": "سؤال {{$a}}", - "core.question.requiresgrading": "نمره‌دهی لازم است", - "core.quotausage": "شما در حال حاضر {{$a.used}} از {{$a.total}} محدودیت‌تان را استفاده کرده‌اید.", - "core.rating.aggregateavg": "میانگین امتیازات", - "core.rating.aggregatecount": "تعداد امتیازات", - "core.rating.aggregatemax": "بیشترین امتیاز", - "core.rating.aggregatemin": "کمترین امتیاز", - "core.rating.aggregatesum": "جمع امتیازات", - "core.rating.rating": "ارزیابی", - "core.rating.ratings": "ارزیابی‌ها", - "core.refresh": "تازه‌سازی", - "core.remove": "حذف", - "core.required": "لازم است", - "core.resourcedisplayopen": "باز شدن", - "core.resources": "منابع", - "core.restore": "بازیابی", - "core.restricted": "محدود", - "core.save": "ذخیره", - "core.savechanges": "ذخیره تغییرات", - "core.search": "جستجو", - "core.searchresults": "نتیجهٔ جستجو", - "core.sec": "ثانیه", - "core.secs": "ثانیه", - "core.seemoredetail": "برای دیدن جزئیات بیشتر اینجا را کلیک کنید", - "core.selectacategory": "لطفا یک طبقه بندی را انتخاب کنید", - "core.selectacourse": "یک درس را انتخاب کنید", - "core.selectagroup": "یک گروه را انتخاب کنید", - "core.send": "ارسال", - "core.sending": "در حال ارسال", - "core.serverconnection": "خطا در اتصال به کارگزار", - "core.settings.about": "درباره", - "core.settings.cordovadevicemodel": "مدل دستگاه کُردوا", - "core.settings.currentlanguage": "زبان فعلی", - "core.settings.debugdisplay": "نمایش پیغام‌های اشکال‌زدایی", - "core.settings.deletesitefiles": "آیا از حذف کلیه فایل‌های این سایت مطمئنید؟", - "core.settings.deletesitefilestitle": "پاک‌کردن فایل‌های سایت", - "core.settings.deviceinfo": "اطلاعات دستگاه", - "core.settings.deviceos": "سیستم‌عامل دستگاه", - "core.settings.disableall": "غیرفعال‌کردن اطلاعیه‌ها", - "core.settings.enabledownloadsection": "فعال‌کردن دریافت قسمت‌ها", - "core.settings.enablerichtexteditor": "فعال‌کردن ویرایشگر متنی پیشرفته", - "core.settings.enablerichtexteditordescription": "اگر فعال باشد، در جاهایی که ممکن باشد یک ویرایشگر متنی پیشرفته نمایش داده خواهد شد.", - "core.settings.estimatedfreespace": "فضای آزاد تخمینی", - "core.settings.general": "عمومی", - "core.settings.language": "زبان", - "core.settings.license": "اجازه‌نامه", - "core.settings.locked": "قفل", - "core.settings.loggedin": "هنگام بودن در سایت", - "core.settings.loggedoff": "هنگام نبودن در سایت", - "core.settings.preferences": "ترجیحات", - "core.settings.privacypolicy": "خط مشی حفظ حریم خصوصی", - "core.settings.reportinbackground": "گزارش خطاها به‌صورت خودکار", - "core.settings.settings": "تنظیمات", - "core.settings.sites": "سایت‌ها", - "core.settings.spaceusage": "فضای مورد استفاده", - "core.settings.synchronization": "هماهنگسازی", - "core.settings.total": "مجموع", - "core.show": "نمایش", - "core.showless": "نمایش کمتر...", - "core.showmore": "نمایش بیشتر...", - "core.site": "سایت", - "core.sitehome.sitehome": "صفحهٔ اصلی سایت", - "core.sitehome.sitenews": "اعلانات سایت", - "core.sitemaintenance": "The site is undergoing maintenance and is currently not available", - "core.sizeb": "بایت", - "core.sizegb": "گیگابایت", - "core.sizekb": "کیلوبایت", - "core.sizemb": "مگابایت", - "core.sorry": "متاسفیم...", - "core.sort": "مرتب سازی", - "core.sortby": "مرتب شدن بر اساس", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y، %I:%M %p", - "core.strftimedatetimeshort": "%y/%m/%d، %H:%M", - "core.strftimedaydate": "%A، %d %B %Y", - "core.strftimedaydatetime": "%A، %d %B %Y، %I:%M %p", - "core.strftimedayshort": "%A، %d %B", - "core.strftimedaytime": "%a، %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b، %H:%M", - "core.strftimerecentfull": "%a، %d %b %Y، %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.submit": "ارسال", - "core.success": "موفق", - "core.tag.defautltagcoll": "مجموعه پیش‌فرض", - "core.tag.noresultsfor": "نتیجه‌ای برای «{{$a}}» وجود نداشت", - "core.tag.searchtags": "جستجوی برچسب‌ها", - "core.tag.tag": "برچسب", - "core.tag.tagarea_course": "درس‌ها", - "core.tag.tagarea_course_modules": "فعالیت‌ها و منابع", - "core.tag.tagarea_post": "مطلب‌های وبلاگ", - "core.tag.tagarea_user": "علائق کاربران", - "core.tag.tags": "tag ها", - "core.teachers": "استاد", - "core.thisdirection": "rtl", - "core.time": "زمان", - "core.timesup": "وقت تمام شد!", - "core.today": "امروز", - "core.unexpectederror": "خطای غیرمنتظره.لطفا برنامه را ببندید و دوباره اجرا نمایید", - "core.unlimited": "نامحدود", - "core.upgraderunning": "سایت در حال ارتقا است، لطفا بعدا تلاش کنید.", - "core.user": "کاربر", - "core.user.address": "آدرس", - "core.user.city": "شهر/شهرک", - "core.user.contact": "مخاطب", - "core.user.country": "کشور", - "core.user.description": "توصیف", - "core.user.details": "جزئیات", - "core.user.editingteacher": "استاد", - "core.user.email": "آدرس پست الکترونیک", - "core.user.emailagain": "پست الکترونیک (دوباره)", - "core.user.firstname": "نام", - "core.user.interests": "علایق", - "core.user.lastname": "نام خانوادگی", - "core.user.manager": "مدیر", - "core.user.newpicture": "عکس جدید", - "core.user.noparticipants": "این درس شرکت‌کننده‌ای ندارد", - "core.user.participants": "شرکت کنندگان", - "core.user.phone1": "تلفن", - "core.user.phone2": "تلفن همراه", - "core.user.roles": "نقش‌ها", - "core.user.student": "شاگرد", - "core.user.teacher": "استاد بدون حق ویرایش", - "core.user.webpage": "صفحهٔ وب", - "core.userdeleted": "این حساب کاربری حذف شده است", - "core.userdetails": "با جزئیات", - "core.usernotfullysetup": "کاربر به‌طور کامل برپا نشده است", - "core.users": "کاربران", - "core.view": "مشاهده", - "core.viewprofile": "مشاهدهٔ صفحهٔ مشخصات فردی", - "core.whatisyourage": "سن شما چقدر است؟", - "core.year": "سال", - "core.years": "سال", - "core.yes": "بله" -} \ No newline at end of file diff --git a/src/assets/lang/fi.json b/src/assets/lang/fi.json deleted file mode 100644 index 9f28c4b2d..000000000 --- a/src/assets/lang/fi.json +++ /dev/null @@ -1,1839 +0,0 @@ -{ - "addon.badges.alignment": "Asemointi", - "addon.badges.badgedetails": "Osaamismerkin tiedot", - "addon.badges.badges": "Osaamismerkit", - "addon.badges.contact": "Yhteystieto", - "addon.badges.dateawarded": "Myöntämispäivä", - "addon.badges.expired": "Päättynyt", - "addon.badges.expirydate": "Vanhenemispäivä", - "addon.badges.issuancedetails": "Osaamismerkin vanhentuminen", - "addon.badges.issuerdetails": "Osaamismerkin myöntäjän tiedot", - "addon.badges.issueremail": "Sähköposti", - "addon.badges.issuername": "Osaamismerkin myöntäjän nimi", - "addon.badges.issuerurl": "Myöntäjän URL-osoite", - "addon.badges.language": "Kieli", - "addon.badges.nobadges": "Yhtään osaamismerkkiä ei ole tarjolla", - "addon.badges.recipientdetails": "Vastaanottajan tiedot", - "addon.badges.version": "Versio", - "addon.badges.warnexpired": "(Tämä osaamismerkki on vanhentunut)", - "addon.block_activitymodules.pluginname": "Aktiviteetit", - "addon.block_activityresults.pluginname": "Aktiviteetin tulokset", - "addon.block_badges.pluginname": "Uusimmat osaamismerkit", - "addon.block_blogmenu.pluginname": "Blogivalikko", - "addon.block_blogrecent.pluginname": "Viimeisimmät merkinnät", - "addon.block_blogtags.pluginname": "Asiasanat", - "addon.block_calendarmonth.pluginname": "Kalenteri", - "addon.block_calendarupcoming.pluginname": "Tulevat tapahtumat", - "addon.block_comments.pluginname": "Kommentit", - "addon.block_completionstatus.pluginname": "Kurssisuoritusten tila", - "addon.block_glossaryrandom.pluginname": "Satunnainen hakusana sanastosta", - "addon.block_learningplans.pluginname": "Opintosuunnitelmat", - "addon.block_myoverview.all": "Kaikki (paitsi näkymästä poistetut)", - "addon.block_myoverview.allincludinghidden": "Kaikki", - "addon.block_myoverview.favourites": "Suosikit", - "addon.block_myoverview.future": "Tulevat kurssit", - "addon.block_myoverview.hiddencourses": "Piilotetut", - "addon.block_myoverview.inprogress": "Keskeneräiset kurssit", - "addon.block_myoverview.lastaccessed": "Viimeksi käyty", - "addon.block_myoverview.morecourses": "Lisää kursseja", - "addon.block_myoverview.nocourses": "Ei kursseja", - "addon.block_myoverview.past": "Menneet kurssit", - "addon.block_myoverview.pluginname": "Kurssien yhteenveto", - "addon.block_myoverview.title": "Kurssin nimi", - "addon.block_newsitems.pluginname": "Viimeisimmät uutiset", - "addon.block_onlineusers.pluginname": "Kirjautuneet osallistujat", - "addon.block_privatefiles.pluginname": "Yksityiset tiedostot", - "addon.block_recentactivity.pluginname": "Viimeisin toiminta", - "addon.block_recentlyaccessedcourses.nocourses": "Ei lähiaikona katsottuja kursseja", - "addon.block_recentlyaccessedcourses.pluginname": "Viimeksi katsotut kurssit", - "addon.block_recentlyaccesseditems.noitems": "Ei viimeaikaisia kohteita", - "addon.block_recentlyaccesseditems.pluginname": "Viimeaikaiset kohteet", - "addon.block_rssclient.pluginname": "RSS-syötteet", - "addon.block_selfcompletion.pluginname": "Oma edistymiseni", - "addon.block_sitemainmenu.pluginname": "Päävalikko", - "addon.block_starredcourses.nocourses": "Ei suosikiksi merkittyjä kursseja", - "addon.block_starredcourses.pluginname": "Suosikit", - "addon.block_tags.pluginname": "Tunnisteet", - "addon.block_timeline.duedate": "Eräpäivä", - "addon.block_timeline.next30days": "Seuraavat 30 päivää", - "addon.block_timeline.next3months": "Seuraavat 3 kuukautta", - "addon.block_timeline.next6months": "Seuraavat 6 kuukautta", - "addon.block_timeline.next7days": "Seuraavat 7 päivää", - "addon.block_timeline.nocoursesinprogress": "Ei käynnissä olevia kursseja", - "addon.block_timeline.noevents": "Ei tulevia eräpäiviä", - "addon.block_timeline.overdue": "Myöhässä", - "addon.block_timeline.pluginname": "Aikajana", - "addon.block_timeline.sortbycourses": "Lajittele kurssien mukaan", - "addon.block_timeline.sortbydates": "Lajittele päivämäärien mukaan", - "addon.blog.blog": "Blogi", - "addon.blog.blogentries": "Merkintöjä", - "addon.blog.linktooriginalentry": "Linkki alkuperäiseen blogikirjoitukseen", - "addon.blog.noentriesyet": "Ei näkyviä merkintöjä", - "addon.blog.publishtonoone": "Itsellesi (keskeneräinen)", - "addon.blog.publishtosite": "Kaikille tällä sivustolla", - "addon.blog.publishtoworld": "Kaikille Internetin käyttäjille", - "addon.blog.siteblogheading": "Sivuston blogi", - "addon.calendar.allday": "Koko päivä", - "addon.calendar.calendar": "Kalenteri", - "addon.calendar.calendarevents": "Kalenteritapahtumat", - "addon.calendar.categoryevents": "Kategoriatapahtumat", - "addon.calendar.confirmeventdelete": "Haluatko varmasti poistaa tapahtuman \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "Tapahtuma \"{{$a.name}}\" on osa tapahtumasarjaa. Haluatko poistaa vain tämän tapahtuman vai kaikki {{$a.count}} tapahtumaa sarjassa?", - "addon.calendar.courseevents": "Kurssin tapahtumat", - "addon.calendar.daynext": "Seuraava päivä", - "addon.calendar.dayprev": "Edellinen päivä", - "addon.calendar.defaultnotificationtime": "Oletusilmoitusaika", - "addon.calendar.deleteallevents": "Poista kaikki tapahtumat", - "addon.calendar.deleteevent": "Poista tapahtuma", - "addon.calendar.deleteoneevent": "Poista tämä tapahtuma", - "addon.calendar.durationminutes": "Kesto minuutteina", - "addon.calendar.durationnone": "Ilman kestoa", - "addon.calendar.durationuntil": "Päättymisaika", - "addon.calendar.editevent": "Muokkaa tapahtumaa", - "addon.calendar.errorloadevent": "Ladattaessa tapahtumaa tapahtui virhe.", - "addon.calendar.errorloadevents": "Ladattaessa tapahtumia tapahtui virhe.", - "addon.calendar.eventcalendareventdeleted": "Tapahtuma poistettu", - "addon.calendar.eventduration": "Kesto", - "addon.calendar.eventendtime": "Tapahtuman päättymisaika", - "addon.calendar.eventkind": "Tapahtuman tyyppi", - "addon.calendar.eventname": "Tapahtuman otsikko", - "addon.calendar.eventstarttime": "Tapahtuman alkamisaika", - "addon.calendar.eventtype": "Tapahtuman tyyppi", - "addon.calendar.fri": "pe", - "addon.calendar.friday": "perjantai", - "addon.calendar.gotoactivity": "Siirry aktiviteettiin", - "addon.calendar.groupevents": "Ryhmän tapahtumat", - "addon.calendar.invalidtimedurationminutes": "Asettamasi kesto minuutteina ei ole kelvollinen. Syötä kesto joka on suurempi kuin 0 tai älä syötä kestoa ollenkaan.", - "addon.calendar.invalidtimedurationuntil": "Asettamasi päättymispäivä ja -aika on ennen kuin tapahtuman alkamispäivä ja -aika. Korjaa tämä ennen kuin jatkat.", - "addon.calendar.mon": "ma", - "addon.calendar.monday": "maanantai", - "addon.calendar.monthlyview": "Kuukausinäkymä", - "addon.calendar.newevent": "Uusi tapahtuma", - "addon.calendar.noevents": "Tapahtumia ei ole", - "addon.calendar.nopermissiontoupdatecalendar": "Valitettavasti sinulla ei ole oikeuksia päivittää kalenterimerkintää", - "addon.calendar.repeatedevents": "Toistetut tapahtumat", - "addon.calendar.repeateditall": "Tee muutokset kaikkiin {{$a}} tapahtumaan tässä toistuvassa sarjassa", - "addon.calendar.repeateditthis": "Tee muutokset vain tähän tapahtumaan", - "addon.calendar.repeatevent": "Toista tätä tapahtumaa", - "addon.calendar.repeatweeksl": "Toista viikoittain", - "addon.calendar.sat": "la", - "addon.calendar.saturday": "lauantai", - "addon.calendar.siteevents": "Sivuston tapahtumat", - "addon.calendar.sun": "su", - "addon.calendar.sunday": "sunnuntai", - "addon.calendar.thu": "to", - "addon.calendar.thursday": "torstai", - "addon.calendar.today": "tänään", - "addon.calendar.tomorrow": "huomenna", - "addon.calendar.tue": "ti", - "addon.calendar.tuesday": "tiistai", - "addon.calendar.typecategory": "Kategoriatapahtuma", - "addon.calendar.typeclose": "Sulje tapahtuma", - "addon.calendar.typecourse": "koko kurssille", - "addon.calendar.typedue": "Erääntyvä tapahtuma", - "addon.calendar.typegradingdue": "Erääntyvän tapahtuman arviointi", - "addon.calendar.typegroup": "ryhmälle", - "addon.calendar.typeopen": "Avaa tapahtuma", - "addon.calendar.typesite": "koko sivustolle", - "addon.calendar.typeuser": "oma tapahtuma", - "addon.calendar.upcomingevents": "Tulevat tapahtumat", - "addon.calendar.userevents": "Omat tapahtumat", - "addon.calendar.wed": "ke", - "addon.calendar.wednesday": "keskiviikko", - "addon.calendar.when": "Milloin", - "addon.calendar.yesterday": "eilen", - "addon.competency.activities": "Aktiviteetit", - "addon.competency.competencies": "Pätevyydet", - "addon.competency.competenciesmostoftennotproficientincourse": "Seuraavia pätevyyksiä on saavutettu tällä kurssilla vähiten.", - "addon.competency.coursecompetencies": "Kurssin pätevyydet", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Tämän kurssin pätevyyksien arvioinnit eivät vaikuta opintosuunnitelmiin.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Tämän kurssin pätevyyksien arvioinnit päivitetään heti opintosuunnitelmiin.", - "addon.competency.crossreferencedcompetencies": "Ristiviitatut pätevyydet", - "addon.competency.duedate": "Määräaika", - "addon.competency.errornocompetenciesfound": "Pätevyyksiä ei löytynyt", - "addon.competency.evidence": "Todiste", - "addon.competency.evidence_competencyrule": "Osaamissääntökriteerit täytetty.", - "addon.competency.evidence_coursecompleted": "Kurssi '{{$a}}' suoritettiin", - "addon.competency.evidence_coursemodulecompleted": "Aktiviteetti '{{$a}}' suoritettiin", - "addon.competency.evidence_courserestored": "Osaamisenarviointi palautettiin kurssin '{{$a}}' palautuksen mukana.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Todiste aiemmasta osaamisesta '{{$a}}' on nyt linkitetty", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Aiemman osaamisen '{{$a}} linkitys on nyt purettu", - "addon.competency.evidence_manualoverride": "Pätevyys arvioitiin manuaalisesti", - "addon.competency.evidence_manualoverrideincourse": "Osaamisen arviointi asetettiin manuaalisesti kurssille '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "Osaamisen arviointi asetettiin manuaalisesti opintosuunnitelmassa '{{$a}}'.", - "addon.competency.learningplancompetencies": "Opintosuunnitelman pätevyydet", - "addon.competency.learningplans": "Opintosuunnitelmat", - "addon.competency.myplans": "Omat opintosuunnitelmani", - "addon.competency.noactivities": "Ei aktiviteetteja", - "addon.competency.nocompetencies": "Ei pätevyyksiä", - "addon.competency.nocompetenciesincourse": "Yhtään pätevyyttä ei ole vielä linkitetty tähän kurssiin.", - "addon.competency.nocrossreferencedcompetencies": "Muita pätevyyksiä ei ole ristiviitattu tähän pätevyyteen.", - "addon.competency.noevidence": "Ei todistetta", - "addon.competency.noplanswerecreated": "Yhtään opintosuunnitelmaa ei luotu.", - "addon.competency.nouserplanswithcompetency": "Yksikään opintosuunnitelma ei sisällä tätä kompetenssia.", - "addon.competency.path": "Polku:", - "addon.competency.planstatusactive": "Aktiivinen", - "addon.competency.planstatuscomplete": "Valmis", - "addon.competency.planstatusdraft": "Luonnos", - "addon.competency.planstatusinreview": "Arvioitavana", - "addon.competency.planstatuswaitingforreview": "Odottaa arviointia", - "addon.competency.proficient": "Pätevä", - "addon.competency.progress": "Eteneminen", - "addon.competency.rating": "Arviointi", - "addon.competency.status": "Tila", - "addon.competency.template": "Opintosuunnitelman viitekehys", - "addon.competency.uponcoursecompletion": "Kurssin suorituksen jälkeen:", - "addon.competency.usercompetencystatus_idle": "Turha", - "addon.competency.usercompetencystatus_inreview": "Arvioitavana", - "addon.competency.usercompetencystatus_waitingforreview": "Odottaa arviointia", - "addon.competency.userplans": "Opintosuunnitelmat", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} kaikkiaan {{$a.y}}:sta pätevyydestä on suoritettu", - "addon.competency.xcompetenciesproficientoutofyincourse": "Olet suorittanut {{$a.x}} kaikkiaan {{$a.y}}:sta pätevyydestä tällä kurssilla.", - "addon.coursecompletion.complete": "Valmis", - "addon.coursecompletion.completecourse": "Merkitse kurssi suoritetuksi", - "addon.coursecompletion.completed": "Suoritettu", - "addon.coursecompletion.completiondate": "Suorituspäivämäärä", - "addon.coursecompletion.completionmenuitem": "Suoritus", - "addon.coursecompletion.couldnotloadreport": "Kurssin suoritusraporttia ei pystytty lataamaan. Ole hyvä ja yritä myöhemmin uudelleen.", - "addon.coursecompletion.coursecompletion": "Kurssin suoritus", - "addon.coursecompletion.criteria": "Kriteeri", - "addon.coursecompletion.criteriagroup": "Kriteeriryhmä", - "addon.coursecompletion.criteriarequiredall": "Kaikki alla olevat kriteerit vaaditaan", - "addon.coursecompletion.criteriarequiredany": "Jokin alla olevista kriteereistä vaaditaan", - "addon.coursecompletion.inprogress": "Kesken", - "addon.coursecompletion.manualselfcompletion": "Opiskelijan itse hyväksymät suoritukset", - "addon.coursecompletion.nottracked": "Suorituksiasi tällä kurssilla ei seurata.", - "addon.coursecompletion.notyetstarted": "Ei vielä aloitettu", - "addon.coursecompletion.pending": "Kesken", - "addon.coursecompletion.required": "Vaadittu", - "addon.coursecompletion.requiredcriteria": "Vaaditut kriteerit", - "addon.coursecompletion.requirement": "Vaatimus", - "addon.coursecompletion.status": "Tilanne", - "addon.coursecompletion.viewcoursereport": "Näytä kurssin raportti", - "addon.files.couldnotloadfiles": "Tiedostolistaa ei pystytty lataamaan.", - "addon.files.emptyfilelist": "Ei näytettäviä tiedostoja.", - "addon.files.erroruploadnotworking": "Valitettavasti tiedostojen lataaminen järjestelmään ei tällä hetkellä onnistu.", - "addon.files.files": "Tiedostot", - "addon.files.privatefiles": "Yksityiset tiedostot", - "addon.files.sitefiles": "Sivuston tiedostot", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Määrittele laitteistot", - "addon.messages.acceptandaddcontact": "Hyväksy ja lisää kontakteihin", - "addon.messages.addcontact": "Lisää kontakti", - "addon.messages.addcontactconfirm": "Oletko varma, että haluat lisätä kontakteihisi käyttäjän {{$a}}?", - "addon.messages.addtofavourites": "Lisää keskustelu suosikkeihin", - "addon.messages.addtoyourcontacts": "Lisää kontakteihin", - "addon.messages.blocknoncontacts": "Estä kaikki viestit, jos lähettäjä ei ole kontaktilistallani", - "addon.messages.blockuser": "Estä käyttäjä", - "addon.messages.blockuserconfirm": "Oletko varma, että haluat estää käyttäjän {{$a}}?", - "addon.messages.contactableprivacy": "Hyväksy viestit käyttäjältä:", - "addon.messages.contactableprivacy_coursemember": "Kontaktini ja kuka tahansa kursseiltani", - "addon.messages.contactableprivacy_onlycontacts": "Vain kontaktini", - "addon.messages.contactableprivacy_site": "Kuka tahansa sivustolla", - "addon.messages.contactblocked": "Kontakti estetty", - "addon.messages.contactlistempty": "Yhteystietolistasi on tyhjä", - "addon.messages.contactname": "Yhteystiedon nimi", - "addon.messages.contactrequestsent": "Kontaktipyynnöt lähetetty", - "addon.messages.contacts": "Kontaktit", - "addon.messages.conversationactions": "Keskustelujen toimintoihin liittyvä valikko", - "addon.messages.decline": "Hylkää", - "addon.messages.deleteallconfirm": "Oletko varma, että haluat poistaa tämän koko keskustelun? Tämä ei poista sitä muilta keskusteluun osallistujilta.", - "addon.messages.deleteallselfconfirm": "Haluatko varmasti poistaa koko henkilökohtaisen keskustelun?", - "addon.messages.deleteconversation": "Poista keskustelu", - "addon.messages.deleteforeveryone": "Poista minulta ja kaikilta muilta", - "addon.messages.errordeletemessage": "Viestiä poistettaessa tapahtui virhe.", - "addon.messages.errorwhileretrievingcontacts": "Virhe ladattaessa yhteystietoja palvelimelta.", - "addon.messages.errorwhileretrievingdiscussions": "Virhe ladattaessa keskusteluja palvelimelta.", - "addon.messages.errorwhileretrievingmessages": "Virhe ladattaessa viestejä palvelimelta.", - "addon.messages.groupconversations": "Ryhmäviestit", - "addon.messages.groupinfo": "Ryhmän tiedot", - "addon.messages.individualconversations": "Henkilökohtaiset", - "addon.messages.info": "Käyttäjätiedot", - "addon.messages.isnotinyourcontacts": "{{$a}} ei ole kontakteissasi", - "addon.messages.message": "Viesti", - "addon.messages.messagenotsent": "Viestiä ei lähetetty. Ole hyvä ja yritä uudelleen myöhemmin.", - "addon.messages.messagepreferences": "Viestien asetukset", - "addon.messages.messages": "Viestit", - "addon.messages.muteconversation": "Mykistä", - "addon.messages.mutedconversation": "Mykistetyt keskustelut", - "addon.messages.newmessage": "Uusi viesti", - "addon.messages.newmessages": "Uusia viestejä", - "addon.messages.nocontactrequests": "Ei kontaktipyyntöjä", - "addon.messages.nocontactsgetstarted": "Ei kontakteja", - "addon.messages.nofavourites": "Ei suosikiksi merkittyjä keskusteluja", - "addon.messages.nogroupconversations": "Ei ryhmäkeskusteluja", - "addon.messages.noindividualconversations": "Ei henkilökohtaisia keskusteluja", - "addon.messages.nomessagesfound": "Viestejä ei löytynyt", - "addon.messages.noncontacts": "Ei kontaktilistalla", - "addon.messages.nousersfound": "Käyttäjiä ei löytynyt", - "addon.messages.numparticipants": "{{$a}} osallistujaa", - "addon.messages.removecontact": "Poista kontakti", - "addon.messages.removecontactconfirm": "Oletko varma, että haluat poistaa käyttäjän {{$a}} kontakteistasi ?", - "addon.messages.removefromfavourites": "Poista keskustelu suosikeista", - "addon.messages.removefromyourcontacts": "Poista kontakteista", - "addon.messages.requests": "Pyynnöt", - "addon.messages.requirecontacttomessage": "Sinun pitää pyytää käyttäjää {{$a}} lisäämään sinut kontakteihin, jotta voit lähettää hänelle viestin.", - "addon.messages.searchcombined": "Hae käyttäjiä ja viestejä", - "addon.messages.selfconversation": "Henkilökohtainen tila", - "addon.messages.selfconversationdefaultmessage": "Tallenna viestiluonnokset, linkit, muistiinpanot jne. ja palaa niihin myöhemmin.", - "addon.messages.sendcontactrequest": "Lähetä kontaktipyyntö", - "addon.messages.type_blocked": "Estetty", - "addon.messages.type_search": "Hakutulokset", - "addon.messages.type_strangers": "Muut", - "addon.messages.unabletomessage": "Et voi lähettää viestiä tälle käyttäjälle", - "addon.messages.unblockuser": "Poista esto", - "addon.messages.unblockuserconfirm": "Oletko varma, että haluat poistaa eston käyttäjältä {{$a}}?", - "addon.messages.unmuteconversation": "Poista mykistys", - "addon.messages.useentertosend": "Käytä Enter-näppäintä lähettämiseen", - "addon.messages.userwouldliketocontactyou": "{{$a}} haluaisi ottaa sinuun yhteyttä", - "addon.messages.warningmessagenotsent": "Ei voitu lähettää viestiä/viestejä käyttäjälle {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Haluaisi ottaa yhteyttä", - "addon.messages.you": "Sinä:", - "addon.messages.youhaveblockeduser": "Olet estänyt tämän käyttäjän.", - "addon.messages.yourcontactrequestpending": "Kontaktipyyntösi odottaa {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Ole hyvä ja hyväksy vakuutus oman työn osuudesta.", - "addon.mod_assign.addattempt": "Anna toinen suorituskerta", - "addon.mod_assign.addnewattempt": "Anna uusi suorituskerta", - "addon.mod_assign.addnewattemptfromprevious": "Lisää uusi suorituskerta, joka perustuu aiempaan palautukseen", - "addon.mod_assign.addsubmission": "Lisää palautus", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Tehtävän yksityiskohdat ja palautuslomake tulevat näkyville {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Palautettavissa alkaen", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Tämän tehtävän voi palauttaa aikaisintaan {{$a}}", - "addon.mod_assign.applytoteam": "Anna sama arvosana ja palaute koko ryhmälle", - "addon.mod_assign.assignmentisdue": "Palautusaika on päättynyt", - "addon.mod_assign.attemptnumber": "Suorituskerran numero", - "addon.mod_assign.attemptreopenmethod": "Milloin palautukset avataan opiskelijoiden muokattaviksi", - "addon.mod_assign.attemptreopenmethod_manual": "Opettaja", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automaattisesti kunnes läpäisee", - "addon.mod_assign.attemptsettings": "Suorituskertojen asetukset", - "addon.mod_assign.cannoteditduetostatementsubmission": "Et voi lisätä tai muokata palautustasi mobiilisovelluksessa, koska järjestelmä ei pystynyt lataamaan vakuutusta oman työn osuudesta.", - "addon.mod_assign.cannotgradefromapp": "Kaikkia arviointitapoja ei vielä tueta mobiilisovelluksessa, eikä niitä voi myöskään muokata.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Et voi lähettää palautustasi arvioitavaksi mobiilisovelluksessa, koska järjestelmä ei pystynyt lataamaan vakuutusta oman työn osuudesta.", - "addon.mod_assign.confirmsubmission": "Oletko varma, että haluat palauttaa työsi arvioitavaksi? Sen jälkeen et voi enää tehdä muutoksia.", - "addon.mod_assign.currentattempt": "Tämä on suorituskerta {{$a}}", - "addon.mod_assign.currentattemptof": "Tämä on suorituskerta {{$a.attemptnumber}} (suorituskertoja sallitaan enintään {{$a.maxattempts}}).", - "addon.mod_assign.currentgrade": "Nykyinen arvosana", - "addon.mod_assign.cutoffdate": "Ehdoton takaraja palautukselle", - "addon.mod_assign.defaultteam": "Oletusryhmä", - "addon.mod_assign.duedate": "Palautettava viimeistään", - "addon.mod_assign.duedateno": "Ei palautusten päättymisaikaa", - "addon.mod_assign.duedatereached": "Tehtävän palautusten päättymisaika on mennyt", - "addon.mod_assign.editingstatus": "Muokataan tilaa", - "addon.mod_assign.editsubmission": "Muokkaa palautusta", - "addon.mod_assign.erroreditpluginsnotsupported": "Et voi lisätä tai muokata palautustasi mobiilisovelluksessa, koska tietyt lisäosat eivät tue muokkausta.", - "addon.mod_assign.errorshowinginformation": "Palautteen tietoja ei voida näyttää.", - "addon.mod_assign.extensionduedate": "Lisäajan päättymisaika", - "addon.mod_assign.feedbacknotsupported": "Palautetta ei tueta mobiilisovelluksessa, eikä se välttämättä sisällä kaikkia tietoja.", - "addon.mod_assign.grade": "Arvosana", - "addon.mod_assign.graded": "Arvioitu", - "addon.mod_assign.gradedby": "Arvioija", - "addon.mod_assign.gradedfollowupsubmit": "Arvioitu - uusi palautus tullut", - "addon.mod_assign.gradedon": "Arviointipäivä", - "addon.mod_assign.gradelocked": "Tämä arvosana on lukittu tai ylitetty arviointikirjassa", - "addon.mod_assign.gradenotsynced": "Arvosanaa ei ole synkronoitu", - "addon.mod_assign.gradeoutof": "Arvosana (0 - {{$a}})", - "addon.mod_assign.gradingstatus": "Arvioinnin tila", - "addon.mod_assign.groupsubmissionsettings": "Ryhmäpalautuksen asetukset", - "addon.mod_assign.hiddenuser": "Opiskelija", - "addon.mod_assign.latesubmissions": "Myöhässä tulleet palautukset", - "addon.mod_assign.latesubmissionsaccepted": "Sallittu {{$a}} asti", - "addon.mod_assign.markingworkflowstate": "Arviointiprosessin vaihe", - "addon.mod_assign.markingworkflowstateinmarking": "Arvioinnissa", - "addon.mod_assign.markingworkflowstateinreview": "Tarkastuskierroksella", - "addon.mod_assign.markingworkflowstatenotmarked": "Ei arvioitu", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Valmis julkaistavaksi", - "addon.mod_assign.markingworkflowstatereadyforreview": "Arviointi tehty", - "addon.mod_assign.markingworkflowstatereleased": "Julkaistu", - "addon.mod_assign.modulenameplural": "Tehtävät", - "addon.mod_assign.multipleteams": "Useamman kuin yhden ryhmän jäsen", - "addon.mod_assign.multipleteams_desc": "Tehtävä edellyttää palauttamista ryhmässä. Olet useamman kuin yhden ryhmän jäsen. Jotta voisit palauttaa, sinun on oltava vain yhden ryhmän jäsen. Ota yhteyttä opettajaasi vaihtaaksesi ryhmän jäsenyyttäsi.", - "addon.mod_assign.noattempt": "Ei suorituskertoja", - "addon.mod_assign.nomoresubmissionsaccepted": "Vain opiskelijat, joille on annettu lisäaikaa, voivat enää palauttaa vastauksensa.", - "addon.mod_assign.noonlinesubmissions": "Tähän tehtävään ei vastata Moodlessa", - "addon.mod_assign.nosubmission": "Tähän tehtävään ei ole vielä vastattu", - "addon.mod_assign.notallparticipantsareshown": "Opiskelijoita, joilla ei ole palautuksia, ei näytetä.", - "addon.mod_assign.noteam": "Ei yhdenkään ryhmän jäsen", - "addon.mod_assign.noteam_desc": "Tehtävä edellyttää palauttamista ryhmässä Et ole minkään ryhmän jäsen, joten et voi tehdä palautusta. Ota yhteyttä opettajaasi, jotta sinut voidaan lisätä ryhmään.", - "addon.mod_assign.notgraded": "Ei arvioitu", - "addon.mod_assign.numberofdraftsubmissions": "Keskeneräiset", - "addon.mod_assign.numberofparticipants": "Opiskelijoita", - "addon.mod_assign.numberofsubmissionsneedgrading": "Odottaa arviointia", - "addon.mod_assign.numberofsubmittedassignments": "Palautuksia", - "addon.mod_assign.numberofteams": "Ryhmät", - "addon.mod_assign.numwords": "{{$a}} sanaa", - "addon.mod_assign.outof": "{{$a.current}} enimmäismäärästä {{$a.total}}", - "addon.mod_assign.overdue": "Tehtävän viimeinen palautusaika oli {{$a}} sitten", - "addon.mod_assign.submission": "Palautus", - "addon.mod_assign.submissioneditable": "Opiskelija saa muokata tätä palautusta", - "addon.mod_assign.submissionnoteditable": "Opiskelija ei voi muokata tätä palautusta", - "addon.mod_assign.submissionnotsupported": "Tätä palautusta ei tueta mobiilisovelluksessa, eikä se välttämättä sisällä kaikkia tietoja.", - "addon.mod_assign.submissionslocked": "Tähän tehtävään ei voi vastata", - "addon.mod_assign.submissionstatus": "Palautuksen tila", - "addon.mod_assign.submissionstatus_": "Ei palautusta", - "addon.mod_assign.submissionstatus_draft": "Keskeneräinen (ei lähetetty arvioitavaksi)", - "addon.mod_assign.submissionstatus_marked": "Arvioitu", - "addon.mod_assign.submissionstatus_new": "Ei palautusta", - "addon.mod_assign.submissionstatus_reopened": "Avattu uudelleen", - "addon.mod_assign.submissionstatus_submitted": "Lähetetty arvioitavaksi", - "addon.mod_assign.submissionstatusheading": "Palautuksen tila", - "addon.mod_assign.submissionteam": "Ryhmä", - "addon.mod_assign.submitassignment": "Lähetä arvioitavaksi", - "addon.mod_assign.submitassignment_help": "Palautettuasi tehtävän et voi enää muokata sitä.", - "addon.mod_assign.submittedearly": "Palautus tehtiin {{$a}} ennen määräaikaa", - "addon.mod_assign.submittedlate": "Palautus tehtiin myöhässä {{$a}} määräajan jälkeen", - "addon.mod_assign.timemodified": "Viimeksi muokattu", - "addon.mod_assign.timeremaining": "Palautusaikaa jäljellä", - "addon.mod_assign.ungroupedusers": "Asetus \"Opiskelijan pitää olla ryhmän jäsen voidakseen palauttaa\" on käytössä, ja osaa opiskelijoista ei ole jaettu ryhmiin. Huomaa, että tämä estää heitä palauttamasta tehtävää.", - "addon.mod_assign.unlimitedattempts": "Rajoittamaton", - "addon.mod_assign.userswhoneedtosubmit": "Opiskelijat, joilla on palautettavaa: {{$a}}", - "addon.mod_assign.userwithid": "Käyttäjä, jonka ID on {{id}}", - "addon.mod_assign.viewsubmission": "Näytä palautus", - "addon.mod_assign.warningsubmissiongrademodified": "Palautuksen arvosanaa muokattiin sivustolla.", - "addon.mod_assign.warningsubmissionmodified": "Käyttäjän palautusta muokattiin sivustolla.", - "addon.mod_assign.wordlimit": "Sanamäärän yläraja", - "addon.mod_assign_feedback_comments.pluginname": "Palaute", - "addon.mod_assign_feedback_editpdf.pluginname": "Kommentoitava PDF", - "addon.mod_assign_feedback_file.pluginname": "Tiedostopalaute", - "addon.mod_assign_submission_comments.pluginname": "Palautuksen lisätiedot", - "addon.mod_assign_submission_file.pluginname": "Tiedostojen palautus", - "addon.mod_assign_submission_onlinetext.pluginname": "Verkkotekstipalautukset", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Tämän tehtävän sanamäärälle on laitettu rajaksi {{$a.limit}} sanaa. Yrität palauttaa {{$a.count}} sanan mittaista vastausta. Lyhennä vastaustasi ja palauta se sitten uudestaan.", - "addon.mod_book.errorchapter": "Virhe kirjan luvun lukemisessa.", - "addon.mod_book.modulenameplural": "Kirjat", - "addon.mod_book.navnexttitle": "Seuraava: {{$a}}", - "addon.mod_book.navprevtitle": "Edellinen: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Kirjan luvut", - "addon.mod_book.toc": "Sisällysluettelo", - "addon.mod_chat.beep": "kutsu", - "addon.mod_chat.chatreport": "Chat-keskustelut", - "addon.mod_chat.currentusers": "Tämänhetkiset osallistujat", - "addon.mod_chat.enterchat": "Napsauta tästä päästäksesi mukaan chattiin", - "addon.mod_chat.entermessage": "Kirjoita viesti", - "addon.mod_chat.errorwhileconnecting": "Virhe yhdistettäessä chattiin", - "addon.mod_chat.errorwhilegettingchatdata": "Käyttäjän chat-tietoja haettaessa tapahtui virhe.", - "addon.mod_chat.errorwhilegettingchatusers": "Chatin osallistujia haettaessa tapahtui virhe.", - "addon.mod_chat.errorwhileretrievingmessages": "Virhe ladattaessa viestejä palvelimelta.", - "addon.mod_chat.errorwhilesendingmessage": "Virhe viestinlähetyksessä.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} kutsuu kaikkia!", - "addon.mod_chat.messagebeepsyou": "{{$a}} on juuri kutsunut sinua!", - "addon.mod_chat.messageenter": "{{$a}} on juuri tullut mukaan chattiin", - "addon.mod_chat.messageexit": "{{$a}} on lopettanut chatin", - "addon.mod_chat.messages": "Viestit", - "addon.mod_chat.messageyoubeep": "Kutsuit toista osallistujaa {{$a}}", - "addon.mod_chat.modulenameplural": "Chatit", - "addon.mod_chat.mustbeonlinetosendmessages": "Sinun on oltava online-tilassa lähettääksesi viestejä.", - "addon.mod_chat.nomessages": "Ei viestejä", - "addon.mod_chat.saidto": "sanottu henkilölle", - "addon.mod_chat.send": "Lähetä", - "addon.mod_chat.sessionstart": "Keskustelun aloitusaika: {{$a}}", - "addon.mod_chat.talk": "Puhu", - "addon.mod_chat.viewreport": "Näytä menneet keskustelut", - "addon.mod_choice.cannotsubmit": "Vastauksen tallennuksessa oli teknisiä ongelmia. Vastaathan uudestaan.", - "addon.mod_choice.choiceoptions": "Vastausvaihtoehdot", - "addon.mod_choice.errorgetchoice": "Virhe haettaessa valinnan tietoja.", - "addon.mod_choice.expired": "Tämä aktiviteeti on suljettu {{$a}} eikä ole enää käytettävissä.", - "addon.mod_choice.full": "(Täysi)", - "addon.mod_choice.modulenameplural": "Valinnat", - "addon.mod_choice.noresultsviewable": "Tulokset eivät ole juuri nyt nähtävillä", - "addon.mod_choice.notopenyet": "Tämä aktiviteetti ei ole saatavilla ennen kuin {{$a}}", - "addon.mod_choice.numberofuser": "Osallistujamäärä", - "addon.mod_choice.numberofuserinpercentage": "Osallistujamäärä prosentteina", - "addon.mod_choice.previewonly": "Tämä on vain esikatselu valinnasta. Valinta avautuu {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Nimettömät vastaukset julkaistaan vastaamisen jälkeen.", - "addon.mod_choice.publishinfoanonclose": "Nimettömät vastaukset julkaistaan, kun vastausaika on päättynyt.", - "addon.mod_choice.publishinfofullafter": "Nimet ja vastaukset julkaistaan vastaamisen jälkeen.", - "addon.mod_choice.publishinfofullclose": "Nimet ja vastaukset julkaistaan, kun vastausaika on päättynyt.", - "addon.mod_choice.publishinfonever": "Vastauksia ei julkaista vastaamisen jälkeen.", - "addon.mod_choice.removemychoice": "Poista valintani", - "addon.mod_choice.responses": "Vastaukset", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% käyttäjistä valitsi vaihtoehdon: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Graafinen esitys", - "addon.mod_choice.resultsnotsynced": "Tulokset eivät sisällä viimeisintä vastaustasi. Ole hyvä ja synkronoi päivittääksesi ne.", - "addon.mod_choice.savemychoice": "Tallenna valintani", - "addon.mod_choice.userchoosethisoption": "Käyttäjä valitsee tämän vaihtoehdon", - "addon.mod_choice.yourselection": "Valintasi", - "addon.mod_data.addentries": "Lisää tietueita", - "addon.mod_data.advancedsearch": "Laajennettu haku", - "addon.mod_data.alttext": "Vaihtoehtoinen teksti", - "addon.mod_data.approve": "Hyväksy", - "addon.mod_data.approved": "Hyväksytty", - "addon.mod_data.ascending": "Nouseva järjestys", - "addon.mod_data.authorfirstname": "Lisääjän etunimi", - "addon.mod_data.authorlastname": "Lisääjän sukunimi", - "addon.mod_data.confirmdeleterecord": "Haluatko varmasti poistaa tämän hakusanan?", - "addon.mod_data.descending": "Laskeva järjestys", - "addon.mod_data.disapprove": "Peru hyväksyminen", - "addon.mod_data.emptyaddform": "Et täyttänyt tietueen kenttiä!", - "addon.mod_data.entrieslefttoadd": "Sinun täytyy lisätä {{$a.entriesleft}} tietue(tta) saadaksesi tehtävän valmiiksi.", - "addon.mod_data.entrieslefttoaddtoview": "Sinun täytyy lisätä {{$a.entriesleft}} tietuetta ennen kuin pääset katsomaan muiden lisäämiä tietueita.", - "addon.mod_data.errorapproving": "Virhe hyväksyttäessä tai hylätessä merkintää.", - "addon.mod_data.errordeleting": "Virhe poistettaessa merkintää.", - "addon.mod_data.errormustsupplyvalue": "Sinun täytyy syöttää arvo tähän.", - "addon.mod_data.expired": "Tämä aktiviteetti on jo suljettu ({{$a}}).", - "addon.mod_data.fields": "Kentät", - "addon.mod_data.foundrecords": "Hakusanoja löytyi: {{$a.num}}/{{$a.max}} (Poista suodattimet)", - "addon.mod_data.menuchoose": "Valitse...", - "addon.mod_data.modulenameplural": "Tietokannat", - "addon.mod_data.more": "Lisää...", - "addon.mod_data.nomatch": "Haluttuja hakusanoja ei löytynyt!", - "addon.mod_data.norecords": "Tietokannassa ei ole hakusanoja", - "addon.mod_data.notapproved": "Tietuetta ei ole vielä hyväksytty.", - "addon.mod_data.notopenyet": "Tämä aktiviteetti on avoinna vasta {{$a}}.", - "addon.mod_data.numrecords": "{{$a}} tietuetta", - "addon.mod_data.other": "Muu", - "addon.mod_data.recordapproved": "Tietue hyväksytty", - "addon.mod_data.recorddeleted": "Tietue poistettu", - "addon.mod_data.recorddisapproved": "Merkintää ei ole hyväksytty", - "addon.mod_data.resetsettings": "Tyhjennä hakuehdot", - "addon.mod_data.search": "Hae", - "addon.mod_data.selectedrequired": "Kaikki valitut vaaditaan", - "addon.mod_data.single": "Näytä yksittäin", - "addon.mod_data.timeadded": "Lisäysajankohta", - "addon.mod_data.timemodified": "Muokkausajankohta", - "addon.mod_data.usedate": "Sisällytä hakuun", - "addon.mod_feedback.analysis": "Yhteenveto", - "addon.mod_feedback.anonymous": "Anonyymi", - "addon.mod_feedback.anonymous_entries": "Anonyymit palautteet", - "addon.mod_feedback.average": "Keskiarvo", - "addon.mod_feedback.captchaofflinewarning": "Palautetta, johon on määritelty CAPTCHA, ei voi antaa offline-tilassa, tai sitä ei ole määritelty tai järjestelmä on kaatunut.", - "addon.mod_feedback.complete_the_form": "Vastaa kysymyksiin", - "addon.mod_feedback.completed_feedbacks": "Lähetetyt vastaukset", - "addon.mod_feedback.continue_the_form": "Jatka lomaketta", - "addon.mod_feedback.feedback_is_not_open": "Palaute ei ole auki", - "addon.mod_feedback.feedback_submitted_offline": "Tämä palaute on tallennettu ja voit lähettää sen myöhemmin.", - "addon.mod_feedback.feedbackclose": "Sulje palaute", - "addon.mod_feedback.feedbackopen": "Avaa palaute", - "addon.mod_feedback.mapcourses": "Yhdistä palaute kursseihin", - "addon.mod_feedback.maximal": "Maksimi", - "addon.mod_feedback.minimal": "Minimi", - "addon.mod_feedback.mode": "Tila", - "addon.mod_feedback.modulenameplural": "Palautteet", - "addon.mod_feedback.next_page": "Seuraava sivu", - "addon.mod_feedback.non_anonymous": "Vastaajien nimet tallennetaan ja näytetään vastausten kanssa", - "addon.mod_feedback.non_anonymous_entries": "vastaukset nimillä", - "addon.mod_feedback.non_respondents_students": "vastaamattomat opiskelijat", - "addon.mod_feedback.not_selected": "Ei valittu", - "addon.mod_feedback.not_started": "ei aloitettu", - "addon.mod_feedback.numberoutofrange": "Numero on skaalan ulkopuolella", - "addon.mod_feedback.overview": "Yleiskatsaus", - "addon.mod_feedback.page_after_submit": "Vastauksen jälkeinen viesti", - "addon.mod_feedback.preview": "Esikatsele", - "addon.mod_feedback.previous_page": "Edellinen sivu", - "addon.mod_feedback.questions": "Kysymykset", - "addon.mod_feedback.response_nr": "Vastaus numero", - "addon.mod_feedback.responses": "Vastaukset", - "addon.mod_feedback.save_entries": "Lähetä vastauksesi", - "addon.mod_feedback.show_entries": "Näytä vastaukset", - "addon.mod_feedback.show_nonrespondents": "Näytä vastaamattomat", - "addon.mod_feedback.started": "aloitettu", - "addon.mod_feedback.this_feedback_is_already_submitted": "Olet jo tehnyt tämän aktiviteetin.", - "addon.mod_folder.emptyfilelist": "Ei näytettäviä tiedostoja.", - "addon.mod_folder.modulenameplural": "Kansiot", - "addon.mod_forum.addanewdiscussion": "Lisää uusi keskustelu", - "addon.mod_forum.addanewquestion": "Lisää uusi kysymys", - "addon.mod_forum.addanewtopic": "Lisää uusi aihe", - "addon.mod_forum.addtofavourites": "Merkitse tämä keskustelu suosikiksi", - "addon.mod_forum.advanced": "Muokkaustoiminnot", - "addon.mod_forum.cannotadddiscussion": "Vain ryhmän jäsenet voivat lisätä viestejä tälle keskustelualueelle.", - "addon.mod_forum.cannotadddiscussionall": "Sinulla ei ole oikeuksia lisätä kaikille osallistujille näkyvää viestiä.", - "addon.mod_forum.cannotcreatediscussion": "Ei voitu luoda uutta keskustelua", - "addon.mod_forum.couldnotadd": "Viestiäsi ei voida lisätä tuntemattoman virheen takia.", - "addon.mod_forum.couldnotupdate": "Viestiäsi ei voida päivittää tuntemattoman virheen takia", - "addon.mod_forum.cutoffdatereached": "Keskustelu on päättynyt, et voi enää lisätä viestejä.", - "addon.mod_forum.delete": "Poista", - "addon.mod_forum.deletedpost": "Viesti on poistettu", - "addon.mod_forum.deletesure": "Oletko varma, että haluat poistaa tämän viestin?", - "addon.mod_forum.discussion": "Keskustelu", - "addon.mod_forum.discussionlistsortbycreatedasc": "Lajittele nousevaan järjestykseen luontipäivän mukaan", - "addon.mod_forum.discussionlistsortbycreateddesc": "Lajittele laskevaan järjestykseen luontipäivän mukaan", - "addon.mod_forum.discussionlistsortbylastpostasc": "Lajittele nousevaan järjestykseen viimeisimmän viestin luontipäivän mukaan", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Lajittele laskevaan järjestykseen viimeisimmän viestin luontipäivän mukaan", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Lajittele nousevaan järjestykseen vastausten lukumäärän mukaan", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Lajittele laskevaan järjestykseen vastausten lukumäärän mukaan", - "addon.mod_forum.discussionlocked": "Keskustelu on lukittu, joten et voi enää vastata siihen.", - "addon.mod_forum.discussionpinned": "Kiinnitetty", - "addon.mod_forum.discussionsubscription": "Keskustelualueviestitilaus", - "addon.mod_forum.edit": "Muokkaa", - "addon.mod_forum.erroremptymessage": "Viestin tekstiosa ei voi olla tyhjä", - "addon.mod_forum.erroremptysubject": "Viestin otsikko ei voi olla tyhjä", - "addon.mod_forum.errorgetforum": "Virhe haettaessa keskustelualueen tietoja.", - "addon.mod_forum.errorgetgroups": "Virhe haettaessa ryhmäasetuksia.", - "addon.mod_forum.favouriteupdated": "Sinun suosikkisi on päivitetty.", - "addon.mod_forum.forumnodiscussionsyet": "Tällä keskustelualueella ei ole vielä viestejä.", - "addon.mod_forum.group": "Ryhmä", - "addon.mod_forum.lastpost": "Viimeisin viesti", - "addon.mod_forum.lockdiscussion": "Lukitse tämä keskustelu", - "addon.mod_forum.lockupdated": "Lukitusasetus on päivitetty.", - "addon.mod_forum.message": "Viesti", - "addon.mod_forum.modeflatnewestfirst": "Näytä vastaukset peräkkäin, uusin ensin", - "addon.mod_forum.modeflatoldestfirst": "Näytä vastaukset peräkkäin, vanhin ensin", - "addon.mod_forum.modenested": "Näytä vastaukset sisäkkäin", - "addon.mod_forum.modulenameplural": "Keskustelualueet", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} keskustelua", - "addon.mod_forum.numreplies": "{{numreplies}} vastausta", - "addon.mod_forum.pindiscussion": "Kiinnitä tämä keskustelu", - "addon.mod_forum.pinupdated": "Kiinnityksen asetukset on päivitetty.", - "addon.mod_forum.postisprivatereply": "Tämä on yksityisviesti, joka ei näy muille osallistujille.", - "addon.mod_forum.posttoforum": "Lähetä viesti", - "addon.mod_forum.posttomygroups": "Lähetä kopio viestistä kaikille ryhmille.", - "addon.mod_forum.privatereply": "Vastaa yksityisesti", - "addon.mod_forum.re": "Vs:", - "addon.mod_forum.refreshdiscussions": "Päivitä keskustelut", - "addon.mod_forum.refreshposts": "Päivitä keskustelujen viestit", - "addon.mod_forum.removefromfavourites": "Poista tämä keskustelu suosikeistasi", - "addon.mod_forum.reply": "Vastaa", - "addon.mod_forum.replyplaceholder": "Kirjoita vastauksesi....", - "addon.mod_forum.subject": "Aihe", - "addon.mod_forum.tagarea_forum_posts": "Keskustelualueen viestit", - "addon.mod_forum.thisforumhasduedate": "Keskustelun päättymispäivä on {{$a}}.", - "addon.mod_forum.thisforumisdue": "Keskustelun päättymispäivä oli {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Avaa tämä keskustelu lukituksesta", - "addon.mod_forum.unpindiscussion": "Poista tämän keskustelun kiinnitys", - "addon.mod_forum.unread": "Lukematta", - "addon.mod_forum.unreadpostsnumber": "{{$a}} lukematonta viestiä", - "addon.mod_forum.yourreply": "Vastauksesi", - "addon.mod_glossary.addentry": "Lisää uusi hakusana", - "addon.mod_glossary.aliases": "Avainsana(t)", - "addon.mod_glossary.attachment": "Liite", - "addon.mod_glossary.browsemode": "Selaa merkintöjä", - "addon.mod_glossary.byalphabet": "Aakkosjärjestyksessä", - "addon.mod_glossary.byauthor": "Ryhmittele kirjoittajan mukaisesti", - "addon.mod_glossary.bycategory": "Ryhmittele kategorian mukaan", - "addon.mod_glossary.bynewestfirst": "Uusin ensin", - "addon.mod_glossary.byrecentlyupdated": "Äskettäin päivitetty", - "addon.mod_glossary.bysearch": "Hae", - "addon.mod_glossary.cannoteditentry": "Merkintää ei voi muokata", - "addon.mod_glossary.casesensitive": "Kirjasinkoolla on merkitystä", - "addon.mod_glossary.categories": "Kategoriat", - "addon.mod_glossary.concept": "Käsite", - "addon.mod_glossary.definition": "Määritelmä", - "addon.mod_glossary.entriestobesynced": "Synkronoitavat merkinnät", - "addon.mod_glossary.entrypendingapproval": "Tämä merkintä odottaa hyväksyntää.", - "addon.mod_glossary.entryusedynalink": "Linkitä automaattisesti", - "addon.mod_glossary.errconceptalreadyexists": "Tämä käsite on jo olemassa. Tähän sanastoon ei voi lisätä keskenään samannimisiä hakusanoja.", - "addon.mod_glossary.errorloadingentries": "Merkintöjä ladattaessa tapahtui virhe.", - "addon.mod_glossary.errorloadingentry": "Virhe ladattaessa merkintää.", - "addon.mod_glossary.errorloadingglossary": "Virhe ladattaessa sanastoa.", - "addon.mod_glossary.fillfields": "Käsite ja määritelmä ovat pakollisia kenttiä.", - "addon.mod_glossary.fullmatch": "Linkitä vain kokonaisiin sanoihin", - "addon.mod_glossary.linking": "Automaattinen linkitys", - "addon.mod_glossary.modulenameplural": "Sanastot", - "addon.mod_glossary.noentriesfound": "Merkintöjä ei löytynyt.", - "addon.mod_glossary.searchquery": "Hakukysely", - "addon.mod_glossary.tagarea_glossary_entries": "Sanaston tietueet", - "addon.mod_h5pactivity.all_attempts": "Kaikki suorituskerrat", - "addon.mod_h5pactivity.answer_checked": "Oikea vastaus", - "addon.mod_h5pactivity.answer_correct": "Vastauksesi on oikein", - "addon.mod_h5pactivity.answer_fail": "Väärä vastaus", - "addon.mod_h5pactivity.answer_incorrect": "Vastauksesi on väärin", - "addon.mod_h5pactivity.answer_pass": "Oikea vastaus", - "addon.mod_h5pactivity.attempt": "Suorituskerta", - "addon.mod_h5pactivity.attempt_completion_no": "Suoritus on vielä kesken", - "addon.mod_h5pactivity.attempt_completion_yes": "Onnistuneesti suoritettu", - "addon.mod_h5pactivity.attempt_success_fail": "Tavoitetta (Outcome) ei saavutettu", - "addon.mod_h5pactivity.attempt_success_pass": "Tavoite (Outcome) saavutettu", - "addon.mod_h5pactivity.attempts_none": "Tällä käyttäjällä ei ole yhtään suorituskertaa", - "addon.mod_h5pactivity.completion": "Suorituksen tila", - "addon.mod_h5pactivity.duration": "Kesto", - "addon.mod_h5pactivity.maxscore": "Maksimipisteet", - "addon.mod_h5pactivity.myattempts": "Minun suorituskertani", - "addon.mod_h5pactivity.outcome": "Tavoite (Outcome)", - "addon.mod_h5pactivity.review_my_attempts": "Näytä suorituskertojeni tiedot", - "addon.mod_h5pactivity.score": "Pisteet", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} pistettä {{$a.maxscore}} pisteestä", - "addon.mod_h5pactivity.startdate": "Aloituspäivämäärä", - "addon.mod_h5pactivity.totalscore": "Kokonaispisteet", - "addon.mod_imscp.deploymenterror": "Sisällön pakkaamisvirhe!", - "addon.mod_imscp.modulenameplural": "IMS-sisältöpaketit", - "addon.mod_imscp.showmoduledescription": "Näytä kuvaus", - "addon.mod_imscp.toc": "Sisällysluettelo", - "addon.mod_lesson.answer": "Vastaus", - "addon.mod_lesson.attempt": "Suorituskerta: {{$a}}", - "addon.mod_lesson.attemptheader": "Suorituskerta", - "addon.mod_lesson.attemptsremaining": "Sinulla on {{$a}} suorituskerta(a) jäljellä.", - "addon.mod_lesson.averagescore": "Pisteiden keskiarvo", - "addon.mod_lesson.averagetime": "Suoritusaikojen keskiarvo", - "addon.mod_lesson.branchtable": "Sisältö", - "addon.mod_lesson.cannotfindattempt": "Virhe: suorituskertaa ei löydetty", - "addon.mod_lesson.cannotfinduser": "Virhe: ei löydetty käyttäjiä", - "addon.mod_lesson.clusterjump": "Katsomaton kysymys kokoelmassa", - "addon.mod_lesson.completed": "Suoritettu", - "addon.mod_lesson.congratulations": "Onnittelut - olet päässyt oppitunnin loppuun.", - "addon.mod_lesson.continue": "Jatka", - "addon.mod_lesson.continuetonextpage": "Jatka seuraavalle sivulle.", - "addon.mod_lesson.defaultessayresponse": "Kurssin opettaja arvioi esseesi.", - "addon.mod_lesson.detailedstats": "Tarkemmat tiedot", - "addon.mod_lesson.didnotanswerquestion": "Ei vastannut tähän kysymykseen.", - "addon.mod_lesson.displayofgrade": "Arvosanan näyttäminen (vain opiskelijoille)", - "addon.mod_lesson.displayscorewithessays": "

                Olet ansainnut {{$a.score}} / {{$a.tempmaxgrade}} automaattisesti arvioiduista kysymyksistä.

                \n

                Esseekysymyksesi {{$a.essayquestions}} arvioidaan ja lisätään lopulliseen tulokseesi myöhemmin.

                \n

                Tämänhetkinen tuloksesi ilman esseekysymyksiä on {{$a.score}} / {{$a.grade}}.

                ", - "addon.mod_lesson.displayscorewithoutessays": "Tuloksesi on {{$a.score}} ({{$a.grade}}:sta).", - "addon.mod_lesson.emptypassword": "Salasana ei voi olla tyhjä", - "addon.mod_lesson.enterpassword": "Syötä salasana:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Et vastannut yhteenkään kysymykseen. Et saanut pisteitä tästä oppitunnista.", - "addon.mod_lesson.errorprefetchrandombranch": "Tämä oppitunti sisältää hypyn sattumanvaraiselle sisältösivulle, et voi tehdä sitä mobiilisovelluksella, ennen kuin olet aloittanut sen www-selaimella.", - "addon.mod_lesson.errorreviewretakenotlast": "Tätä suoritusta ei voi enää tarkastella, koska toinen suoritus on päättynyt.", - "addon.mod_lesson.finish": "Valmis", - "addon.mod_lesson.finishretakeoffline": "Tämä suoritus tehtiin offline-tilassa.", - "addon.mod_lesson.firstwrong": "Antamastasi vastauksesta ei tule pisteitä, koska vastaus ei ollut oikein. Haluatko vastata uudestaan? (Jos vastaat nyt kysymykseen oikein, sitä ei lasketa lopputulokseen.)", - "addon.mod_lesson.gotoendoflesson": "Mene oppitunnin loppuun", - "addon.mod_lesson.grade": "Arvosana", - "addon.mod_lesson.highscore": "Paras tulos", - "addon.mod_lesson.hightime": "Pisin suoritusaika", - "addon.mod_lesson.leftduringtimed": "Olet lähtenyt ajastetun oppitunnin aikana.
                Ole hyvä ja napsauta \"jatka\" aloittaaksesi oppitunnin uudelleen.", - "addon.mod_lesson.leftduringtimednoretake": "Olet lähtenyt ajastetun oppitunnin aikana, etkä
                saa aloittaa uudelleen tai jatkaa oppituntia.", - "addon.mod_lesson.lessonmenu": "Oppitunnin valikko", - "addon.mod_lesson.lessonstats": "Tilastotietoja oppitunnista", - "addon.mod_lesson.linkedmedia": "Linkitetty media", - "addon.mod_lesson.loginfail": "Sisäänkirjautuminen epäonnistui, yritä uudelleen...", - "addon.mod_lesson.lowscore": "Alin tulos", - "addon.mod_lesson.lowtime": "Lyhin suoritusaika", - "addon.mod_lesson.maximumnumberofattemptsreached": "Maksimimäärä suorituskertoja - siirrytään seuraavalle sivulle", - "addon.mod_lesson.modattemptsnoteacher": "Vain opiskelijat voivat esikatsella", - "addon.mod_lesson.modulenameplural": "Oppitunnit", - "addon.mod_lesson.noanswer": "Vastauksesi puuttuu vähintään yhdestä kysymyksestä. Palaathan vastaamaan.", - "addon.mod_lesson.nolessonattempts": "Tässä oppitunnissa ei ole vielä suorituksia", - "addon.mod_lesson.nolessonattemptsgroup": "{{$a}} ryhmän jäsentä ei ole yrittänyt tätä oppituntia.", - "addon.mod_lesson.notcompleted": "Ei valmis", - "addon.mod_lesson.numberofcorrectanswers": "Oikeiden vastausten lukumäärä: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Vastattujen kysymysten määrä: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Vastattujen kysymysten lukumäärä: {{$a.nquestions}} (sinun pitää vastat vähintään {{$a.minquestions}} kysymykseen)", - "addon.mod_lesson.ongoingcustom": "Olet ansainnut {{$a.score}} pistettä {{$a.currenthigh}} pisteestä tähän mennessä.", - "addon.mod_lesson.ongoingnormal": "Olet vastannut oikein {{$a.correct}} kysymykseen {{$a.viewed}} kysymyksestä.", - "addon.mod_lesson.or": "TAI", - "addon.mod_lesson.overview": "Yleistä", - "addon.mod_lesson.preview": "Esikatsele", - "addon.mod_lesson.progressbarteacherwarning2": "Et näe edistymispalkkia koska voit muokata tätä oppituntia", - "addon.mod_lesson.progresscompleted": "Olet suorittanut {{$a}} % oppitunnista", - "addon.mod_lesson.question": "Kysymys", - "addon.mod_lesson.rawgrade": "Pisteet", - "addon.mod_lesson.reports": "Raportit", - "addon.mod_lesson.response": "Palaute", - "addon.mod_lesson.retakefinishedinsync": "Offline-suoritus synkronoitiin, haluatko tarkastella sitä nyt?", - "addon.mod_lesson.review": "Tarkastele", - "addon.mod_lesson.reviewlesson": "Tarkastele oppituntia", - "addon.mod_lesson.reviewquestionback": "Kyllä, haluan vastata uudestaan", - "addon.mod_lesson.reviewquestioncontinue": "Ei, haluan siirtyä seuraavaan kysymykseen", - "addon.mod_lesson.secondpluswrong": "Ei aivan. Haluaisitko yrittää uudelleen?", - "addon.mod_lesson.submit": "Lähetä", - "addon.mod_lesson.teacherjumpwarning": "Tässä oppitunnissa käytetään {{$a.cluster}} -hyppyä tai {{$a.unseen}} -hyppyä. Esikatselussa käytetään tilalla Seuraava sivu -hyppyä. Kirjaudu sisään opiskelijana testataksesi em. hyppyjä.", - "addon.mod_lesson.teacherongoingwarning": "Menossa oleva tulos on näytetty ainoastaan opiskelijalle. Kirjaudu sisään opiskelijana testataksesi menossa olevaa tulosta.", - "addon.mod_lesson.teachertimerwarning": "Ajastin toimii vain opiskelijoille. Vaihda rooli opiskelijaksi, jos haluat testatata ajastinta.", - "addon.mod_lesson.thatsthecorrectanswer": "Vastaus on oikein", - "addon.mod_lesson.thatsthewronganswer": "Vastaus on väärin", - "addon.mod_lesson.timeremaining": "Aikaa jäljellä", - "addon.mod_lesson.timetaken": "Aikaa kulunut", - "addon.mod_lesson.unseenpageinbranch": "Katsomaton kysymys haarassa", - "addon.mod_lesson.warningretakefinished": "Tämä suoritus tehtiin sivustolla.", - "addon.mod_lesson.welldone": "Hyvin tehty!", - "addon.mod_lesson.youhaveseen": "Olet aikaisemmin aloittanut tämän oppitunnin.
                Haluatko jatkaa sivulta, jolle viimeksi jäit?", - "addon.mod_lesson.youranswer": "Vastauksesi", - "addon.mod_lesson.yourcurrentgradeisoutof": "Nykyinen arvosanasi on {{$a.grade}} maksimista {{$a.total}}", - "addon.mod_lesson.youshouldview": "Sinun tulee katsoa vähintään {{$a}} sivua", - "addon.mod_lti.errorgetlti": "Virhe ladattaessa moduulin tietoja.", - "addon.mod_lti.errorinvalidlaunchurl": "Laukaisu-URL ei ole kelvollinen.", - "addon.mod_lti.launchactivity": "Käynnistä aktiviteetti", - "addon.mod_lti.modulenameplural": "Ulkoiset työkalut", - "addon.mod_page.errorwhileloadingthepage": "Sivun sisältöä ladattaessa tapahtui virhe.", - "addon.mod_page.modulenameplural": "Sivut", - "addon.mod_quiz.answercolon": "Vastaus:", - "addon.mod_quiz.attemptfirst": "Ensimmäinen suorituskerta", - "addon.mod_quiz.attemptlast": "Viimeisin suorituskerta", - "addon.mod_quiz.attemptnumber": "Suorituskerta", - "addon.mod_quiz.attemptquiznow": "Tee tentti nyt", - "addon.mod_quiz.attemptstate": "Tila", - "addon.mod_quiz.cannotsubmitquizdueto": "Tenttisuoritusta ei voitu palauttaa seuraavista syistä johtuen:", - "addon.mod_quiz.clearchoice": "Poista valintani", - "addon.mod_quiz.comment": "Kommentti", - "addon.mod_quiz.completedon": "Valmis", - "addon.mod_quiz.confirmclose": "Kun palautat tentin, et voi enää muokata vastauksiasi tältä vastauskerralta.", - "addon.mod_quiz.confirmcontinueoffline": "Tätä suorituskertaa ei ole synkronoitu sitten {{$a}}. Jos olet jatkanut tätä suoritusta toisella laitteella, voit menettää nämä tiedot.", - "addon.mod_quiz.confirmleavequizonerror": "Vastauksia tallennettaessa tapahtui virhe. Oletko varma, että haluat poistua tentistä?", - "addon.mod_quiz.confirmstart": "Tenttisuorituksen aikaraja on {{$a}}. Suoritusajan laskeminen alkaa siitä hetkestä, kun aloitat tentin. Tentti tulee lopettaa ennen kuin suoritusaika menee umpeen. Haluatko varmasti aloittaa tentin nyt?", - "addon.mod_quiz.confirmstartheader": "Ajastettu tentti", - "addon.mod_quiz.connectionerror": "Internet-yhteys menetettiin (automaattinen tallennus epäonnistui)\n\nKopioi talteen tai yritä muistaa tämän sivun vastauksesi, jotta voit tarvittaessa syöttää ne uudelleen tenttiin ja yritä yhdistää uudelleen internetiin.\n\nKunhan yhteys palautuu onnistuneesti, vastauksiesi pitäisi tallentua järjestelmään ja tämä ilmoitus katoaa.", - "addon.mod_quiz.continueattemptquiz": "Jatka viimeisintä suorituskertaa", - "addon.mod_quiz.continuepreview": "Jatka edellistä esikatselua", - "addon.mod_quiz.errorbehaviournotsupported": "Tätä tenttiä ei voi suorittaa mobiilisovelluksessa, koska sovellus ei tue seuraavaa:", - "addon.mod_quiz.errordownloading": "Virhe ladattaessa vaadittuja tietoja.", - "addon.mod_quiz.errorgetattempt": "Virhe ladattaessa suoritustietoja.", - "addon.mod_quiz.errorgetquestions": "Virhe ladattaessa kysymyksiä.", - "addon.mod_quiz.errorgetquiz": "Virhe ladattaessa tentin tietoja.", - "addon.mod_quiz.errorparsequestions": "Vastauksia luettaessa tapahtui virhe. Ole hyvä ja tee tämä tentti www-selaimella.", - "addon.mod_quiz.errorquestionsnotsupported": "Tätä tenttiä ei voi suorittaa mobiilisovelluksella, koska se sisältää kysymystyyppejä, joita sovellus ei tue:", - "addon.mod_quiz.errorrulesnotsupported": "Tätä tenttiä ei voi suorittaa mobiilisovelluksessa, koska se sisältää pääsysääntöjä, joita ei tueta sovelluksessa:", - "addon.mod_quiz.errorsaveattempt": "Suoritustietoja tallennettaessa tapahtui virhe.", - "addon.mod_quiz.feedback": "Palaute", - "addon.mod_quiz.finishattemptdots": "Lopeta tentti...", - "addon.mod_quiz.finishnotsynced": "Valmis, mutta ei synkronoitu.", - "addon.mod_quiz.grade": "Arvosana", - "addon.mod_quiz.gradeaverage": "Arviointien keskiarvo", - "addon.mod_quiz.gradehighest": "Korkein arvosana", - "addon.mod_quiz.grademethod": "Arviointitapa", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Pistettä", - "addon.mod_quiz.modulenameplural": "Tentit", - "addon.mod_quiz.mustbesubmittedby": "Tämä suoritus on palautettava {{$a}} mennessä.", - "addon.mod_quiz.noquestions": "Kysymyksiä ei ole vielä lisätty.", - "addon.mod_quiz.noreviewattempt": "Et voi katsella tätä suorituskertaa", - "addon.mod_quiz.notyetgraded": "Ei vielä arvioitu", - "addon.mod_quiz.opentoc": "Avaa navigaation ponnahdusilmoitus.", - "addon.mod_quiz.outof": "{{$a.grade}} pistettä, täydet pisteet {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} pistettä maksimista {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Palaute kokonaisuudesta", - "addon.mod_quiz.overdue": "Myöhässä", - "addon.mod_quiz.overduemustbesubmittedby": "Suorituksesi on yliajalla; sinun olisi jo pitänyt palauttaa vastauksesi. Jos haluat, että vastauksesi arvioidaan, palauta se {{$a}} mennessä. Jos et palauta annettuun aikaan mennessä, tämän suorituskerran pisteitä ei huomioida arvioinnissa.", - "addon.mod_quiz.preview": "Esikatselu", - "addon.mod_quiz.previewquiznow": "Esikatsele tenttiä", - "addon.mod_quiz.question": "Kysymys", - "addon.mod_quiz.quiznavigation": "Tentin navigaatio", - "addon.mod_quiz.quizpassword": "Tentin salasana", - "addon.mod_quiz.reattemptquiz": "Yritä tenttiä uudestaan", - "addon.mod_quiz.requirepasswordmessage": "Sinun pitää tietää tentin salasana, jotta voisit yrittää tenttiä", - "addon.mod_quiz.returnattempt": "Palaa suoritukseen", - "addon.mod_quiz.review": "Näytä uudelleen", - "addon.mod_quiz.reviewofattempt": "Näytetään suorituskerta {{$a}}", - "addon.mod_quiz.reviewofpreview": "Esikatselun tarkastelu", - "addon.mod_quiz.showall": "Näytä kaikki kysymykset yhdellä sivulla", - "addon.mod_quiz.showeachpage": "Näytä yksi sivu kerrallaan", - "addon.mod_quiz.startattempt": "Aloita tentti", - "addon.mod_quiz.startedon": "Aloitettiin", - "addon.mod_quiz.stateabandoned": "Palauttamaton", - "addon.mod_quiz.statefinished": "Palautettu", - "addon.mod_quiz.statefinisheddetails": "Palautettu {{$a}}", - "addon.mod_quiz.stateinprogress": "Meneillään", - "addon.mod_quiz.stateoverdue": "Olisi jo pitänyt palauttaa", - "addon.mod_quiz.stateoverduedetails": "Palautettava {{$a}} mennessä", - "addon.mod_quiz.status": "Tila", - "addon.mod_quiz.submitallandfinish": "Palauta kaikki ja lopeta", - "addon.mod_quiz.summaryofattempt": "Tenttisuorituksen yhteenveto", - "addon.mod_quiz.summaryofattempts": "Yhteenveto aiemmista suorituskerroistasi", - "addon.mod_quiz.timeleft": "Aikaa jäljellä", - "addon.mod_quiz.timetaken": "Suoritusaika", - "addon.mod_quiz.warningattemptfinished": "Offline-suoritus hylättiin/poistettiin, koska se oli jo suoritettu www-sivustolla tai sitä ei löytynyt.", - "addon.mod_quiz.warningdatadiscarded": "Jotkin offline-vastaukset hylättiin/poistettiin, koska kysymyksiä oli muokattu www-sivustolla.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Suoritus merkittiin ei-palautetuksi, koska joitain offline-vastauksia hylättiin/poistettiin. Ole hyvä, tarkasta vastauksesi ja lähetä suorituksesi uudestaan.", - "addon.mod_quiz.yourfinalgradeis": "Lopullinen arvosanasi tästä tentistä on: {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Virhe ladattaessa sisältöä.", - "addon.mod_resource.modifieddate": "Muokattu {{$a}}", - "addon.mod_resource.modulenameplural": "Tiedostot", - "addon.mod_resource.openthefile": "Avaa tiedosto", - "addon.mod_resource.uploadeddate": "Ladattu {{$a}}", - "addon.mod_scorm.asset": "Sivu", - "addon.mod_scorm.assetlaunched": "Sivu - katseltu", - "addon.mod_scorm.attempts": "Suorituskerrat", - "addon.mod_scorm.averageattempt": "Suorituskertojen keskiarvo", - "addon.mod_scorm.browse": "Selaa", - "addon.mod_scorm.browsed": "Selattu", - "addon.mod_scorm.browsemode": "Esikatselunäkymä", - "addon.mod_scorm.cannotcalculategrade": "Arvosanaa ei pystytty laskemaan.", - "addon.mod_scorm.completed": "Suoritettu", - "addon.mod_scorm.contents": "Sisältö", - "addon.mod_scorm.dataattemptshown": "Tämä tieto liittyy suoritukseen numero {{number}}.", - "addon.mod_scorm.enter": "Aloita", - "addon.mod_scorm.errorcreateofflineattempt": "Uutta offline-suoritusta luotaessa tapahtui virhe. Ole hyvä ja yritä uudelleen.", - "addon.mod_scorm.errordownloadscorm": "Virhe ladattaessa SCORM:a: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Virhe ladattaessa SCORM-tietoja.", - "addon.mod_scorm.errorinvalidversion": "Anteeksi, tämä sovellus tukee vain versiota SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "SCORM-pakettien lataaminen on estetty. Ole hyvä ja ota yhteyttä järjestelmän pääkäyttäjään.", - "addon.mod_scorm.errornovalidsco": "Tällä SCORM:lla ei ole näkyvää SCO:ta ladattavaksi.", - "addon.mod_scorm.errorpackagefile": "Anteeksi, tämä sovellus tukee vain ZIP-tiedostoja.", - "addon.mod_scorm.errorsyncscorm": "Virhe synkronoinnissa. Ole hyvä ja yritä uudelleen.", - "addon.mod_scorm.exceededmaxattempts": "Olet suorittanut maksimimäärän kertoja", - "addon.mod_scorm.failed": "Epäonnistui", - "addon.mod_scorm.firstattempt": "Ensimmäinen suorituskerta", - "addon.mod_scorm.gradeaverage": "Keskiarvo", - "addon.mod_scorm.gradeforattempt": "Suorituskerran arvosana", - "addon.mod_scorm.gradehighest": "Korkein arviointi", - "addon.mod_scorm.grademethod": "Arviointitapa", - "addon.mod_scorm.gradereported": "Arvosana raportoitu", - "addon.mod_scorm.gradescoes": "Oppimisaihiot", - "addon.mod_scorm.gradesum": "Yhteistulos", - "addon.mod_scorm.highestattempt": "Paras suorituskerta", - "addon.mod_scorm.incomplete": "Kesken", - "addon.mod_scorm.lastattempt": "Viimeisin suorituskerta", - "addon.mod_scorm.modulenameplural": "SCORM-paketit", - "addon.mod_scorm.newattempt": "Aloita uusi suorituskerta", - "addon.mod_scorm.noattemptsallowed": "Sallittujen suorituskertojen määrä", - "addon.mod_scorm.noattemptsmade": "Suorituskertojesi määrä", - "addon.mod_scorm.notattempted": "Ei yritetty", - "addon.mod_scorm.offlineattemptnote": "Tämän suorituksen tietoja ei ole vielä synkronoitu.", - "addon.mod_scorm.offlineattemptovermax": "Tätä suoritusta ei voida lähettää koska olet ylittänyt maksimisuorituskerrat.", - "addon.mod_scorm.organizations": "Organisaatiot", - "addon.mod_scorm.passed": "Hyväksytty", - "addon.mod_scorm.reviewmode": "Esikatselutila", - "addon.mod_scorm.score": "Tulos", - "addon.mod_scorm.scormstatusnotdownloaded": "Tätä SCORM-pakettia ei ole vielä ladattu laitteellesi. Se ladataan automaattisesti kun avaat sen.", - "addon.mod_scorm.scormstatusoutdated": "Tätä SCORM-pakettia on muutettu sen jälkeen, kun se on viimeksi ladattu laitteellesi. Se ladataan automaattisesti uudelleen kun avaat sen.", - "addon.mod_scorm.suspended": "Keskytetty", - "addon.mod_scorm.toc": "sisällysluettelo", - "addon.mod_scorm.warningofflinedatadeleted": "Jotain suorituksen {{number}} offline-tiedoista poistettiin, koska siitä ei voitu luoda uutta suorituskertaa.", - "addon.mod_scorm.warningsynconlineincomplete": "Joitain suorituksia ei voitu synkronoida järjestelmään, koska viimeinen online-yritys on vielä kesken. Ole hyvä ja palauta ensin online-suoritus.", - "addon.mod_survey.cannotsubmitsurvey": "Anteeksi, kyselysi lähettämisessä ilmeni ongelma. Ole hyvä ja yritä uudelleen.", - "addon.mod_survey.errorgetsurvey": "Virhe ladattaessa kyselyn tietoja.", - "addon.mod_survey.ifoundthat": "Huomasin tällä kurssilla, että", - "addon.mod_survey.ipreferthat": "Olisi hyvä, että", - "addon.mod_survey.modulenameplural": "Kyselyt", - "addon.mod_survey.responses": "Vastauksia", - "addon.mod_survey.results": "Tulokset", - "addon.mod_survey.surveycompletednograph": "Olet suorittanut tämän kyselyn.", - "addon.mod_url.modulenameplural": "Verkko-osoitteet", - "addon.mod_wiki.cannoteditpage": "Et saa muokata tätä sivua", - "addon.mod_wiki.createpage": "Luo sivu", - "addon.mod_wiki.editingpage": "Muokataan tätä sivua '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Virhe ladattaessa sivua.", - "addon.mod_wiki.errornowikiavailable": "Tässä Wikissä ei ole vielä yhtään sivua luotuna.", - "addon.mod_wiki.map": "Kartta", - "addon.mod_wiki.modulenameplural": "Wikit", - "addon.mod_wiki.newpagehdr": "Uusi sivu", - "addon.mod_wiki.newpagetitle": "Uusi sivun otsikko", - "addon.mod_wiki.nocontent": "Tälle sivulle ei ole sisältöä", - "addon.mod_wiki.notingroup": "Ei ryhmässä", - "addon.mod_wiki.pageexists": "Tämä sivu on jo olemassa. Ohjataan olemassa olevalle sivulle.", - "addon.mod_wiki.pagename": "Sivun nimi", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki-sivut", - "addon.mod_wiki.titleshouldnotbeempty": "Otsikko ei saa olla tyhjä", - "addon.mod_wiki.viewpage": "Näytä sivu", - "addon.mod_wiki.wikipage": "Wikisivu", - "addon.mod_wiki.wrongversionlock": "Sisältösi on vanhentunut, koska toinen käyttäjä on muokannut sivua samaan aikaan.", - "addon.mod_workshop.alreadygraded": "Arvioitu", - "addon.mod_workshop.areainstructauthors": "Ohjeet työn palautukselle", - "addon.mod_workshop.areainstructreviewers": "Arviointiohjeet", - "addon.mod_workshop.assess": "Arvioi", - "addon.mod_workshop.assessedsubmission": "Arvioitu palautus", - "addon.mod_workshop.assessmentform": "Arviointimatriisi", - "addon.mod_workshop.assessmentsettings": "Arviointiasetukset", - "addon.mod_workshop.assessmentweight": "Arvioinnin painotus", - "addon.mod_workshop.assignedassessments": "Vertaisarvioitavaksi annetut palautukset", - "addon.mod_workshop.assignedassessmentsnone": "Sinulle ei ole annettu vertaisarviointeja", - "addon.mod_workshop.conclusion": "Opettajan kirjoittama kooste työpajatyöskentelyn lopussa", - "addon.mod_workshop.createsubmission": "Aloita palautuksen tekeminen", - "addon.mod_workshop.deletesubmission": "Poista palautus", - "addon.mod_workshop.editsubmission": "Muokkaa palautusta", - "addon.mod_workshop.feedbackauthor": "Palaute tekijälle", - "addon.mod_workshop.feedbackby": "Palautteen antoi {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Palaute arvioijalle", - "addon.mod_workshop.givengrades": "Osallistujan antamat vertaisarviot", - "addon.mod_workshop.gradecalculated": "Palautuksen laskettu arvosana", - "addon.mod_workshop.gradeinfo": "Arvosana: {{$a.received}} / {{$a.max}}", - "addon.mod_workshop.gradeover": "Korvaa palautuksen arvosana", - "addon.mod_workshop.gradesreport": "Työpajan arvosanaraportti", - "addon.mod_workshop.gradinggrade": "Vertaisarvioinnin arvosana", - "addon.mod_workshop.gradinggradecalculated": "Arvioinnin laskettu arvosana", - "addon.mod_workshop.gradinggradeof": "Vertaisarvioinnin arvosana (max {{$a}})", - "addon.mod_workshop.gradinggradeover": "Sivuuta vertaisarvioinnin arvosana", - "addon.mod_workshop.modulenameplural": "Työpajat", - "addon.mod_workshop.nogradeyet": "Ei arvioitu", - "addon.mod_workshop.notassessed": "Ei vielä vertaisarvioitu", - "addon.mod_workshop.notoverridden": "Ei ylitetty", - "addon.mod_workshop.noyoursubmission": "Et ole vielä palauttanut työtäsi", - "addon.mod_workshop.overallfeedback": "Yleispalaute", - "addon.mod_workshop.publishedsubmissions": "Julkaistut palautukset", - "addon.mod_workshop.publishsubmission": "Julkaise palautus", - "addon.mod_workshop.publishsubmission_help": "Julkaistut palautukset näkyvät muille kun työpaja on suljettu", - "addon.mod_workshop.reassess": "Arvioi uudelleen", - "addon.mod_workshop.receivedgrades": "Osallistujan saamat vertaisarviot", - "addon.mod_workshop.submissionattachment": "Liite", - "addon.mod_workshop.submissioncontent": "Palautuksen sisältö", - "addon.mod_workshop.submissiondeleteconfirm": "Oletko varma, että haluat poistaa seuraavan palautuksen?", - "addon.mod_workshop.submissiongrade": "Työn arvosana", - "addon.mod_workshop.submissiongradeof": "Työn arvosana (max {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Sinun pitää lisätä tekstiä tai liittää tiedosto.", - "addon.mod_workshop.submissionsreport": "Työpajan palautusten raportti", - "addon.mod_workshop.submissiontitle": "Otsikko", - "addon.mod_workshop.switchphase10": "Vaihda asetusten määrittely -vaiheeseen", - "addon.mod_workshop.switchphase20": "Vaihda töiden palautus -vaiheeseen", - "addon.mod_workshop.switchphase30": "Vaihda vertaisarviointivaiheeseen", - "addon.mod_workshop.switchphase40": "Vaihda vertaisarvioiden arviointi -vaiheeseen", - "addon.mod_workshop.switchphase50": "Sulje työpaja", - "addon.mod_workshop.userplan": "Työpajan suunnittelija", - "addon.mod_workshop.userplancurrentphase": "Nykyinen vaihe", - "addon.mod_workshop.weightinfo": "Painotus: {{$a}}", - "addon.mod_workshop.yourassessment": "Arviointisi", - "addon.mod_workshop.yourassessmentfor": "Arviointisi {{$a}}", - "addon.mod_workshop.yourgrades": "Arvosanasi", - "addon.mod_workshop.yoursubmission": "Palautuksesi", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Kommentti - {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Arvosana - {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Arviointikriteeri {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Valitse asteikko tälle arviointikriteerille", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Kommentti - {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Arviointikriteeri {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Kommentti - {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Arvosana - {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Väite {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kriteeri {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Valitse yksi näistä", - "addon.notes.addnewnote": "Lisää uusi huomautus", - "addon.notes.coursenotes": "Kurssin huomautukset", - "addon.notes.deleteconfirm": "Poista tämä huomautus?", - "addon.notes.eventnotecreated": "Muistio luotu", - "addon.notes.eventnotedeleted": "Muistio poistettu", - "addon.notes.nonotes": "Tämän tyyppisiä huomautuksia ei ole", - "addon.notes.note": "Huomautus", - "addon.notes.notes": "Huomautukset", - "addon.notes.personalnotes": "Henkilökohtaiset huomautukset", - "addon.notes.publishstate": "Konteksti", - "addon.notes.sitenotes": "Sivuston huomautukset", - "addon.notes.userwithid": "Käyttäjä, jonka ID on {{id}}", - "addon.notes.warningnotenotsent": "Ei voitu lisätä muistiipanoa/panoja kurssille {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Virhe ladattaessa ilmoituksia", - "addon.notifications.markallread": "Merkitse kaikki luetuiksi", - "addon.notifications.notificationpreferences": "Ilmoituksien asetukset", - "addon.notifications.notifications": "Ilmoitukset", - "addon.notifications.playsound": "Soita äänimerkki", - "addon.notifications.therearentnotificationsyet": "Ei uusia ilmoituksia.", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Yhdistyneet arabiemiirikunnat", - "assets.countries.AF": "Afganistan", - "assets.countries.AG": "Antigua ja Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktis", - "assets.countries.AR": "Argentiina", - "assets.countries.AS": "Amerikan Samoa", - "assets.countries.AT": "Itävalta", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Ahvenanmaa", - "assets.countries.AZ": "Azerbaidzan", - "assets.countries.BA": "Bosnia-Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgia", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Bolivia", - "assets.countries.BR": "Brasilia", - "assets.countries.BS": "Bahamasaaret", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvet'n saari", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Valko-Venäjä", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Kookossaaret", - "assets.countries.CD": "Kongon Demokraattinen Tasavalta", - "assets.countries.CF": "Keski-Afrikan tasavalta", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Sveitsi", - "assets.countries.CI": "Norsunluurannikko", - "assets.countries.CK": "Cookinsaaret", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Kiina", - "assets.countries.CO": "Kolumbia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Kuuba", - "assets.countries.CV": "Cap Verde", - "assets.countries.CX": "Joulusaari", - "assets.countries.CY": "Kypros", - "assets.countries.CZ": "Tsekin tasavalta", - "assets.countries.DE": "Saksa", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Tanska", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Dominikaaninen tasavalta", - "assets.countries.DZ": "Algeria", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Viro", - "assets.countries.EG": "Egypti", - "assets.countries.EH": "Länsi-Sahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Espanja", - "assets.countries.ET": "Etiopia", - "assets.countries.FI": "Suomi", - "assets.countries.FJ": "Fidzi", - "assets.countries.FK": "Falkland-saaret", - "assets.countries.FM": "Mikronesian liittovaltio", - "assets.countries.FO": "Färsaaret", - "assets.countries.FR": "Ranska", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Iso-Britannia", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Ranskan Guayana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grönlanti", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Päiväntasaajan Guinea", - "assets.countries.GR": "Kreikka", - "assets.countries.GS": "South Georgia And The South Sandwich Islands", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guayana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Heard- ja Mc Donald-saaret", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Kroatia", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Unkari", - "assets.countries.ID": "Indonesia", - "assets.countries.IE": "Irlanti", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Manin Saari", - "assets.countries.IN": "Intia", - "assets.countries.IO": "Brittiläinen Intian valtameren territorio", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran", - "assets.countries.IS": "Islanti", - "assets.countries.IT": "Italia", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaika", - "assets.countries.JO": "Jordania", - "assets.countries.JP": "Japani", - "assets.countries.KE": "Kenia", - "assets.countries.KG": "Kirgistan", - "assets.countries.KH": "Kambodza", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komorit", - "assets.countries.KN": "Saint Kitts ja Nevis", - "assets.countries.KP": "Pohjois-Korea", - "assets.countries.KR": "Etelä-Korea", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Caymansaaret", - "assets.countries.KZ": "Kazakstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesoto", - "assets.countries.LT": "Liettua", - "assets.countries.LU": "Luxemburg", - "assets.countries.LV": "Latvia", - "assets.countries.LY": "Libya", - "assets.countries.MA": "Marokko", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldova", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshallsaaret", - "assets.countries.MK": "Makedonia", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Pohjois-Mariaanit", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Malediivit", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Meksiko", - "assets.countries.MY": "Malesia", - "assets.countries.MZ": "Mosambik", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Uusi-Kaledonia", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolkinsaari", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Alankomaat", - "assets.countries.NO": "Norja", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Uusi-Seelanti", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Ranskan Polynesia", - "assets.countries.PG": "Papua-Uusi-Guinea", - "assets.countries.PH": "Filippiinit", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Puola", - "assets.countries.PM": "St. Pierre ja Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestiina", - "assets.countries.PT": "Portugali", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Romania", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Venäjä", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Saudi-Arabia", - "assets.countries.SB": "Salomonsaaret", - "assets.countries.SC": "Seychellit", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Ruotsi", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "St. Helena", - "assets.countries.SI": "Slovenia", - "assets.countries.SJ": "Huippuvuoret ja Jan Mayen", - "assets.countries.SK": "Slovakia", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Etelä-Sudan", - "assets.countries.ST": "Sao Tome ja Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SY": "Syyria", - "assets.countries.SZ": "Swazimaa", - "assets.countries.TC": "Turks- ja Caicossaaret", - "assets.countries.TD": "Tshad", - "assets.countries.TF": "Ranskan eteläiset territoriot", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thaimaa", - "assets.countries.TJ": "Tadzhikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Itä-Timor", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunisia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turkki", - "assets.countries.TT": "Trinidad ja Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tansania", - "assets.countries.UA": "Ukraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "Yhdysvallat", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Vatikaani", - "assets.countries.VC": "Saint Vincent ja Grenadiinit", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Brittiläiset Neitsytsaaret", - "assets.countries.VI": "Yhdysvaltain Neitsytsaaret", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis- ja Futunasaaret", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Etelä-Afrikka", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB-ekirja", - "assets.mimetypes.application/msword": "Word-tiedosto", - "assets.mimetypes.application/pdf": "Pdf-tiedosto", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle-varmuuskopio", - "assets.mimetypes.application/vnd.ms-excel": "Excel-taulukko", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 -makrot salliva työkirja", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint-esitys", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument-taulukkotiedosto", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument-taulukkotiedoston mallipohja", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument-tekstitiedosto", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument-tekstitiedoston mallipohja", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument-verkkosivun mallipohja", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint-esitys", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint-esitys", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel-taulukko", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel-mallipohja", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word-dokumentti", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote -esitys", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers -taulukkotiedosto", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages -tiedosto", - "assets.mimetypes.application/x-javascript": "JavaScript-lähdekoodi", - "assets.mimetypes.application/x-mspublisher": "Publisher-tiedosto", - "assets.mimetypes.application/x-shockwave-flash": "Flash-animaatio", - "assets.mimetypes.application/xhtml_xml": "XHTML-tiedosto", - "assets.mimetypes.archive": "Arkisto ({{$a.EXT}})", - "assets.mimetypes.audio": "Äänitiedosto ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Tiedosto", - "assets.mimetypes.group:archive": "Arkistotiedostot", - "assets.mimetypes.group:audio": "Äänitiedostot", - "assets.mimetypes.group:document": "Tekstitiedostot", - "assets.mimetypes.group:html_audio": "Selaimien tukemat äänitiedostot", - "assets.mimetypes.group:html_video": "Selaimien tukemat videotiedostot", - "assets.mimetypes.group:image": "Kuvatiedostot", - "assets.mimetypes.group:presentation": "Esitystiedostot", - "assets.mimetypes.group:sourcecode": "Lähdekoodi", - "assets.mimetypes.group:spreadsheet": "Taulukkotiedostot", - "assets.mimetypes.group:video": "Videotiedostot", - "assets.mimetypes.group:web_audio": "Verkossa käytettävät äänitiedostot", - "assets.mimetypes.group:web_file": "Verkkosivut", - "assets.mimetypes.group:web_image": "Verkossa käytettävät kuvatiedostot", - "assets.mimetypes.group:web_video": "Verkossa käytettävät videotiedostot", - "assets.mimetypes.image": "Kuva ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows-ikoni", - "assets.mimetypes.text/css": "CSS-tyylitiedosto", - "assets.mimetypes.text/csv": "Pilkulla erotetut arvot", - "assets.mimetypes.text/html": "HTML-tiedosto", - "assets.mimetypes.text/plain": "Tekstitiedosto", - "assets.mimetypes.text/rtf": "Rtf-tiedosto", - "assets.mimetypes.video": "Videotiedosto ({{$a.EXT}})", - "core.accounts": "Käyttäjätilit", - "core.add": "Lisää", - "core.agelocationverification": "Iän ja sijainnin vahvistus", - "core.ago": "{{$a}} sitten", - "core.all": "Kaikki", - "core.allgroups": "Kaikki ryhmät", - "core.allparticipants": "Kaikki osallistujat", - "core.answer": "Vastaus", - "core.answered": "Vastattu", - "core.areyousure": "Oletko varma?", - "core.back": "Takaisin", - "core.block.blocks": "Lohkot", - "core.cancel": "Peruuta", - "core.cannotconnect": "Yhdistäminen epäonnistui: Varmista, että olet kirjoittanut verkko-osoitteen (URL) oikein ja Moodlen versio on {{$a}} tai uudempi.", - "core.cannotdownloadfiles": "Tiedostojen lataus on pois päältä. Ole hyvä ja ota yhteyttä järjestelmän pääkäyttäjään.", - "core.captureaudio": "Nauhoita ääntä", - "core.capturedimage": "Otettu valokuva.", - "core.captureimage": "Ota valokuva", - "core.capturevideo": "Nauhoita video", - "core.category": "Kategoria", - "core.choose": "Valitse", - "core.choosedots": "Valitse...", - "core.clearsearch": "Tyhjennä haku", - "core.clicktohideshow": "Klikkaa avataksesi tai sulkeaksesi", - "core.clicktoseefull": "Klikkaa tästä nähdäksesi koko sisällön.", - "core.close": "Sulje", - "core.comments": "Kommentit", - "core.comments.addcomment": "Lisää kommentti...", - "core.comments.comments": "Kommentit", - "core.comments.commentscount": "Kommentit ({{$a}})", - "core.comments.deletecommentbyon": "Poista käyttäjän {{$a.user}} kommentti ajalla {{$a.time}}", - "core.comments.eventcommentcreated": "Kommentti luotu", - "core.comments.eventcommentdeleted": "Kommentti poistettu", - "core.comments.nocomments": "Ei kommentteja", - "core.comments.savecomment": "Tallenna kommentti", - "core.commentscount": "Kommentit ({{$a}})", - "core.completion-alt-auto-fail": "Suoritettu: {{$a}} (ei saavutettu hyväksyttyä arvosanaa)", - "core.completion-alt-auto-n": "Suorittamatta: {{$a}}", - "core.completion-alt-auto-n-override": "Suorittamatta: {{$a.modname}} (asettanut {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Suoritettu: {{$a}} (hyväksytty arvosana)", - "core.completion-alt-auto-y": "Suoritettu: {{$a}}", - "core.completion-alt-auto-y-override": "Suoritettu: {{$a.modname}} (asettanut {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Ei suoritettu: {{$a}}. Valitse merkitäksesi suoritetuksi.", - "core.completion-alt-manual-n-override": "Suorittamatta: {{$a.modname}} (asettanut {{$a.overrideuser}}). Valitse merkitäksesi suoritetuksi.", - "core.completion-alt-manual-y": "Suoritettu: {{$a}}. Valitse merkitäksesi keskeneräiseksi.", - "core.completion-alt-manual-y-override": "Suoritettu: {{$a.modname}} (asettanut {{$a.overrideuser}}). Valitse merkitäksesi keskeneräiseksi.", - "core.confirmcanceledit": "Oletko varma, että haluat poistua tältä sivulta? Kaikki tekemäsi muutokset katoavat.", - "core.confirmdeletefile": "Oletko varma että haluat poistaa tämän tiedoston?", - "core.confirmloss": "Oletko varma? Kaikki muutokset katoavat.", - "core.confirmopeninbrowser": "Haluatko avata tämän www-selaimessa?", - "core.considereddigitalminor": "Olet liian nuori luodaksi tunnuksen tälle sivustolle.", - "core.content": "Sisältö", - "core.contenteditingsynced": "Sisältö, jota muokkaat, on synkronoitu.", - "core.contentlinks.chooseaccount": "Valitse käyttäjätili", - "core.contentlinks.chooseaccounttoopenlink": "Valitse käyttäjätili, jolle linkki avataan.", - "core.contentlinks.confirmurlothersite": "Tämä linkki johtaa ulkopuoliselle sivustolle. Haluatko avata sen?", - "core.contentlinks.errornoactions": "Toimintoa, jota voisi hyödyntää tähän linkkiin, ei löytynyt.", - "core.contentlinks.errornosites": "Sivustoa, joka käsittelisi tämän linkin, ei löytynyt.", - "core.continue": "Jatka", - "core.copiedtoclipboard": "Teksti kopioitu leikepöydälle", - "core.course": "Kurssi", - "core.course.activitydisabled": "Organisaatiosi on estänyt tämän aktiviteettityypin mobiilisovelluksessa.", - "core.course.activitynotyetviewableremoteaddon": "Organisaatiosi on asentanut lisäosan, jota ei vielä tueta.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Organisaatiosi Moodle täytyy päivittää uudempaan versioon. Ole hyvä ja ota yhteyttä järjestelmän pääkäyttäjään.", - "core.course.allsections": "Kaikki osiot", - "core.course.askadmintosupport": "Ota yhteyttä sivuston pääkäyttäjään ja kerro heille, että haluat käyttää tätä aktiviteettia Moodlen mobiilisovelluksessa.", - "core.course.confirmdeletemodulefiles": "Oletko varma, että haluat poistaa nämä tiedostot?", - "core.course.confirmdownload": "Olet lataamassa {{size}}. Oletko varma, että haluat jatkaa?", - "core.course.confirmdownloadunknownsize": "Lataamaasi tiedoston kokoa ei pystytty määrittelemään. Oletko varma, että haluat jatkaa?", - "core.course.confirmpartialdownloadsize": "Olet lataamassa vähintään {{size}}. Oletko varma, että haluat jatkaa?", - "core.course.contents": "Sisältö", - "core.course.couldnotloadsectioncontent": "Osion sisältöä ei pystytty lataamaan. Ole hyvä ja yritä uudelleen.", - "core.course.couldnotloadsections": "Osioita ei pystytty lataamaan. Ole hyvä ja yritä uudelleen.", - "core.course.coursesummary": "Kurssikuvaus", - "core.course.downloadcourse": "Lataa kurssi", - "core.course.errordownloadingsection": "Kurssin osiota ladattaessa tapahtui virhe.", - "core.course.errorgetmodule": "Virhe haettaessa aktiviteetin tietoja.", - "core.course.hiddenfromstudents": "Piilotettu opiskelijoilta", - "core.course.hiddenoncoursepage": "Piilotettu ja pääsy sallittu", - "core.course.nocontentavailable": "Yhtään sisältöä ei ole tällä hetkellä saatavilla.", - "core.course.overriddennotice": "Sinun arvosanasi tältä aktiviteetilta käsiteltiin manuaalisesti.", - "core.course.sections": "Osiot", - "core.course.useactivityonbrowser": "Voit käyttää sitä silti laitteesi www-selaimella.", - "core.coursedetails": "Kurssitiedot", - "core.courses.addtofavourites": "Merkitse suosikiksi", - "core.courses.allowguests": "Tämä kurssi päästää vierailijatunnuksella sisään.", - "core.courses.availablecourses": "Saatavilla olevat kurssit", - "core.courses.cannotretrievemorecategories": "Kategorioita, jotka ovat \"syvemmällä\" kuin tasolla {{$a}} ei voida noutaa.", - "core.courses.categories": "Kurssikategoriat", - "core.courses.confirmselfenrol": "Oletko varma, että haluat lisätä itsesi kurssin osallistujaksi?", - "core.courses.courses": "Kurssit", - "core.courses.enrolme": "Lisää minut kurssin osallistujaksi", - "core.courses.errorloadcategories": "Kategorioita ladattaessa tapahtui virhe.", - "core.courses.errorloadcourses": "Kursseja ladattaessa tapahtui virhe.", - "core.courses.errorsearching": "Hauan aikana tapahtu virhe.", - "core.courses.errorselfenrol": "Itserekisteröitymisessä tapahtui virhe.", - "core.courses.filtermycourses": "Suodata kursseja", - "core.courses.frontpage": "Etusivu", - "core.courses.hidecourse": "Piilota näkymästäni", - "core.courses.ignore": "Ohita", - "core.courses.mycourses": "Omat kurssini", - "core.courses.mymoodle": "Työpöytä", - "core.courses.nocourses": "Ei kurssitietoja näytettäväksi", - "core.courses.nocoursesyet": "Ei kursseja tässä kategoriassa", - "core.courses.nosearchresults": "Ei tuloksia", - "core.courses.notenroled": "Et ole ilmoittautuneena kurssille", - "core.courses.notenrollable": "Et voi itserekisteröityä tälle kursille.", - "core.courses.password": "Kurssiavain", - "core.courses.paymentrequired": "Tämä kurssi vaatii osallistumismaksun.", - "core.courses.paypalaccepted": "PayPal maksu hyväksytty", - "core.courses.reload": "Lataa uudelleen", - "core.courses.removefromfavourites": "Poista suosikeista", - "core.courses.search": "Hae", - "core.courses.searchcourses": "Etsi kursseja", - "core.courses.searchcoursesadvice": "Voit käyttää kurssinhakupainiketta liittääksesi itsesi kurssille vierailijaksi tai osallistujaksi, mikäli kurssin asetukset sallivat sen.", - "core.courses.selfenrolment": "Itserekisteröityminen", - "core.courses.sendpaymentbutton": "Lähetä maksu PayPalin kautta", - "core.courses.show": "Poista piilotus", - "core.courses.totalcoursesearchresults": "Kursseja yhteensä: {{$a}}", - "core.currentdevice": "Nykyinen laite", - "core.datastoredoffline": "Tiedot tallennettiin tälle laitteelle, koska sitä ei voitu lähettää. Se lähetetään automaattisesti myöhemmin uudelleen.", - "core.date": "Päiväys", - "core.day": "päivä", - "core.days": "päivää", - "core.decsep": ",", - "core.defaultvalue": "Oletus ({{$a}})", - "core.delete": "Poista", - "core.deleteduser": "Poistettu käyttäjä", - "core.deleting": "Poistetaan", - "core.description": "Kuvaus", - "core.dfdaymonthyear": "KK-PP-VVVV", - "core.digitalminor": "Digitaalinen alaikäinen", - "core.digitalminor_desc": "Pyydä huoltajaasi ottamaan yhteyttä:", - "core.discard": "Hylkää", - "core.dismiss": "Hylkää", - "core.displayoptions": "Näytön asetukset", - "core.done": "Tehty", - "core.download": "Lataa", - "core.downloading": "Ladataan", - "core.edit": "Muokkaa ", - "core.editor.bold": "Lihavointi", - "core.editor.clear": "Poista muotoilut", - "core.editor.h3": "Otsikko (suuri)", - "core.editor.h4": "Otsikko (keski)", - "core.editor.h5": "Otsikko (pieni)", - "core.editor.orderedlist": "Järjestetty lista", - "core.editor.p": "Kappale", - "core.editor.strike": "Yliviivaus", - "core.editor.textrecovered": "Automaattisesti tallennettu luonnos palautettiin.", - "core.editor.underline": "Alleviivaus", - "core.editor.unorderedlist": "Järjestämätön lista", - "core.emptysplit": "Tämä sivu näyttää tyhjältä mikäli vasen paneeli on tyhjä tai sitä ladataan yhä.", - "core.error": "Virhe", - "core.errorchangecompletion": "Suorituksen statusta muutettaessa tapahtui virhe. Ole hyvä ja yritä uudelleen.", - "core.errordeletefile": "Tiedostoa poistettaessa tapahtui virhe. Ole hyvä ja yritä uudelleen.", - "core.errordownloading": "Tiedostoa ladattaessa tapahtui virhe.", - "core.errordownloadingsomefiles": "Tiedostoja ladattaessa tapahtui virhe. Joitain tiedostoja voi puuttua.", - "core.errorfileexistssamename": "Tämän niminen tiedosto on jo olemassa.", - "core.errorinvalidform": "Lomake sisältää puutteellisia tietoja. Ole hyvä ja tarkista, että kaikki vaaditut kentät ovat täytetty ja tiedot ovat oikeellisia.", - "core.errorinvalidresponse": "Virheellinen vaste vastaanotettiin. Ole hyvä ja ota yhteyttä järjestelmän pääkäyttäjään jos ongelma jatkuu.", - "core.errorloadingcontent": "Sisältöä ladattaessa tapahtui virhe.", - "core.erroropenfilenoapp": "Tiedostoa avatessa tapahtui virhe: sovellusta, joka osaisi avata tämän tyyppisen tiedoston, ei löytynyt.", - "core.erroropenfilenoextension": "Tiedostoa avatessa tapahtui virhe: tiedostolle ei ole määritelty tiedostopäätettä.", - "core.erroropenpopup": "Tämä aktiviteetti yrittää avata ponnahdusikkunan. Ponnahdusikkunan avaamista ei tueta mobiilisovelluksessa.", - "core.errorrenamefile": "Tiedoston uudelleennimeämisessä tapahtui virhe. Ole hyvä ja yritä uudelleen.", - "core.errorsync": "Synkronoinnissa tapahtui virhe. Ole hyvä ja yritä uudelleen.", - "core.errorsyncblocked": "{{$a}} ei voida synkrnoida juuri nyt koska toinen prosessi on kesken. Ole hyvä ja yritä myöhemmin uudelleen. Jos ongelma jatkuu ole hyvä ja käynnistä mobiilisovellus uudelleen.", - "core.explanationdigitalminor": "Tämä tieto tarvitaan, jotta tiedetään, oletko riittävän vanha. Suojaikäraja on se ikä, jolloin voit itsenäisesti hyväksyä ehdot henkilötietojen käsittelylle.", - "core.favourites": "Suosikit", - "core.filename": "Tiedostonimi", - "core.filenameexist": "Tiedoston nimi on jo käytössä: {{$a}}", - "core.filenotfound": "Valitettavasti tiedostoa ei löydy", - "core.fileuploader.addfiletext": "Lisää tiedosto", - "core.fileuploader.audio": "Ääni", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Olet lataamassa {{size}}. Oletko varma, että haluat jatkaa?", - "core.fileuploader.confirmuploadunknownsize": "Lataamaasi tiedoston kokoa ei pystytty määrittelemään. Oletko varma, että haluat jatkaa?", - "core.fileuploader.errorcapturingaudio": "Virhe ääntä nauhoittaessa.", - "core.fileuploader.errorcapturingimage": "Virhe kuvaa otettaessa.", - "core.fileuploader.errorcapturingvideo": "Videon nauhoituksessa tapahtui virhe.", - "core.fileuploader.errorgettingimagealbum": "Tapahtui virhe kun kuvaa yritettiin hakea albumista.", - "core.fileuploader.errormustbeonlinetoupload": "Sinun täytyy olla verkossa, jotta voit lähettää tiedostoja.", - "core.fileuploader.errornoapp": "Sinulla ei ole asennettuna sovellusta, joka osaisi käsitellä tämän toiminnon.", - "core.fileuploader.errorreadingfile": "Tiedostoa luettaessa tapahtui virhe.", - "core.fileuploader.errorwhileuploading": "Tiedostonlähetyksessä tapahtui virhe.", - "core.fileuploader.file": "Tiedosto", - "core.fileuploader.filesofthesetypes": "Hyväksytyt tiedostomuodot:", - "core.fileuploader.fileuploaded": "Tiedosto lähettiin onnistuneesti.", - "core.fileuploader.invalidfiletype": "{{$a}} tiedostotyyppiä ei voida hyväksyä.", - "core.fileuploader.maxbytesfile": "Tiedosto {{$a.file}} on liian suuri. Lähetettävän tiedoston maksimikoko on {{$a.size}}.", - "core.fileuploader.more": "Lisää...", - "core.fileuploader.photoalbums": "Valokuva-albumit", - "core.fileuploader.readingfile": "Luetaan tiedostoa", - "core.fileuploader.selectafile": "Valitse tiedosto", - "core.fileuploader.uploadafile": "Lataa tiedosto", - "core.fileuploader.uploading": "Lähetetään", - "core.fileuploader.uploadingperc": "Lähetetään: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Suodatin", - "core.folder": "Kansio", - "core.forcepasswordchangenotice": "Sinun pitää vaihtaa salasanasi ennen kuin voit jatkaa.", - "core.fulllistofcourses": "Kaikki kurssit", - "core.grades.average": "Keskiarvo", - "core.grades.badgrade": "Virhe annetussa arvosanassa", - "core.grades.contributiontocoursetotal": "Osuus kurssin kokonaisarvosanasta", - "core.grades.feedback": "Palaute", - "core.grades.grade": "Arvosana", - "core.grades.gradeitem": "Arviointikohde", - "core.grades.grades": "Arvioinnit", - "core.grades.lettergrade": "Arviointiasteikkoon skaalattu arvosana", - "core.grades.nogradesreturned": "Ei tuotu arvosanoja", - "core.grades.nooutcome": "Ei tavoitetta", - "core.grades.percentage": "Prosenttia", - "core.grades.range": "Vaihteluväli", - "core.grades.rank": "Sijoitus", - "core.grades.weight": "painotus", - "core.group": "Ryhmä", - "core.groupsseparate": "Erilliset ryhmät", - "core.groupsvisible": "Näkyvät ryhmät", - "core.hasdatatosync": "{{$a}} sisältää synkronoitavaa offline-tietoja.", - "core.help": "Ohje", - "core.hide": "Piilota", - "core.hour": "tunti", - "core.hours": "tuntia", - "core.image": "Kuva", - "core.info": "Tiedot", - "core.invalidformdata": "Väärä lomaketieto", - "core.labelsep": ":", - "core.lastaccess": "Viimeksi käytetty", - "core.lastdownloaded": "Viimeksi ladattu", - "core.lastmodified": "Viimeksi muutettu", - "core.lastsync": "Viimeinen synkroinointi", - "core.layoutgrid": "Ruudukko", - "core.list": "Lista", - "core.listsep": ";", - "core.loading": "Ladataan", - "core.loadmore": "Lataa lisää", - "core.location": "Sijainti", - "core.login.auth_email": "Käytä sähköpostivarmistusta", - "core.login.authenticating": "Autentikoidaan", - "core.login.cancel": "Peruuta", - "core.login.changepassword": "Vaihda salasana", - "core.login.confirmdeletesite": "Oletko varma, että haluat poistaa sivuston {{sitename}}?", - "core.login.connect": "Yhdistä!", - "core.login.connecttomoodle": "Yhdistä Moodleen", - "core.login.contactyouradministrator": "Ota yhteyttä järjestelmän pääkäyttäjään saadaksesi lisää apua.", - "core.login.contactyouradministratorissue": "Ole hyvä ja pyydä järjestelmän pääkäyttäjää tarkistamaan seuraava ongelma: {{$a}}", - "core.login.createaccount": "Luo uusi käyttäjätunnus.", - "core.login.createuserandpass": "Valitse käyttäjätunnus ja salasana", - "core.login.credentialsdescription": "Ole hyvä ja kirjoita käyttäjänimesi ja salasanasi, jotta voit kirjautua sisään.", - "core.login.emailconfirmsent": "

                Vahvistusviesti on lähetetty osoitteeseesi {{$a}}

                \n

                Se sisältää ohjeet, kuinka voit vahvistaa käyttäjätunnuksesi.

                \n

                Jos vahvistuksessa on ongelmia, ota yhteyttä ylläpitäjään.

                ", - "core.login.emailconfirmsentsuccess": "Vahvistusviesti lähetetty onnistuneesti", - "core.login.emailnotmatch": "Sähköpostiosoitteet eivät täsmää", - "core.login.errordeletesite": "Sivustoa poistettaessa tapahtui virhe. Ole hyvä ja yritä uudelleen.", - "core.login.errorupdatesite": "Sivuston tokenia päivittäessä tapahtui virhe.", - "core.login.firsttime": "Jos sinulla ei ole vielä tunnusta:", - "core.login.forcepasswordchangenotice": "Sinun pitää vaihtaa salasanasi ennen kuin voit jatkaa.", - "core.login.forgotten": "Unohditko käyttäjätunnuksesi tai salasanasi?", - "core.login.help": "Ohje", - "core.login.instructions": "Ohjeet", - "core.login.invalidaccount": "Ole hyvä ja tarkista kirjautumistietosi, jos ne ovat oikein pyydä järjestelmän pääkäyttäjää tarkistamaan, että palvelu toimii oikein.", - "core.login.invaliddate": "Virheellinen päivämäärä", - "core.login.invalidemail": "Virheellinen sähköpostiosoite", - "core.login.invalidmoodleversion": "Virheellinen Moodlen versio. Sovellus vaatii vähintään version {{$a}}.", - "core.login.invalidsite": "Sivuston verkko-osoite URL on virheellinen.", - "core.login.invalidtime": "Virheellinen aika", - "core.login.invalidurl": "Virheellinen web-osoite määritelty", - "core.login.invalidvaluemax": "Maksimiarvo on {{$a}}", - "core.login.invalidvaluemin": "Minimiarvo on {{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle Mobilen lisäasetuksien tarkistus palautti odottomattoman vasteen. Sinut autentikoidaan käyttäen normaalia mobiilipalvelua.", - "core.login.loggedoutssodescription": "Sinun täytyy autentikoida itsesi uudestaan. Sinun täytyy kirjautua sivustolle käyttäen www-selainta.", - "core.login.login": "Kirjaudu", - "core.login.loginbutton": "Kirjaudu sisään", - "core.login.logininsiterequired": "Sinun täytyy kirjautua sivustolle www-selaimella.", - "core.login.loginsteps": "Hei!\n\nPäästäksesi kurssialueille luo ensin itsellesi käyttäjätunnus ja salasana sivustoon. Joillekin kurssialueille saatat tarvita ensimmäisellä kerralla kurssiavaimen, jonka saat tarvittaessa opettajaltasi. Luo tunnus näin:\n
                  \n
                1. Täytä lomake uusi tunnus henkilötiedoillasi.
                2. \n
                3. Saat sähköpostitse vahvistusviestin antamaasi osoitteeseen. Jos tarkistusviestiä ei kuulu, tarkistathan roskapostilaatikon.
                4. \n
                5. Saamassasi vahvistusviestissä on linkki; napsauta sitä.
                6. \n
                7. Tunnuksesi vahvistetaan ja sinut kirjataan sisään Moodleen.
                8. \n
                9. Valitse kurssialue, jolle haluat päästä.
                10. \n
                11. Jos kurssialueelle pääsy vaatii avaimen, kirjoita se vastaavaan kenttään.
                12. \n
                13. Tämän jälkeen pääset kurssialueelle. Jatkossa pääset kurssialueillesi kirjautumalla Moodleen ja valitsemalla haluamasi kurssialueen Katsaus kursseistani -sivulta.
                14. \n
                ", - "core.login.missingemail": "Puuttuva sähköpostiosoite", - "core.login.missingfirstname": "Puuttuva etunimi", - "core.login.missinglastname": "Puuttuva sukunimi", - "core.login.mobileservicesnotenabled": "Mobiilipalvelut eivät ole tällä hetkellä päällä järjestelmässä. Ole hyvä ja ota yhteyttä järjestelmän pääkäyttäjään ja pyydä heitä ottamaan mobiilipalvelut käyttöön.", - "core.login.mustconfirm": "Vahvista käyttäjätunnuksesi", - "core.login.newaccount": "Uusi tunnus", - "core.login.notloggedin": "Sinun täytyy olla kirjautuneena sisään.", - "core.login.password": "Salasana", - "core.login.passwordforgotten": "Unohdettu salasana", - "core.login.passwordforgotteninstructions2": "Syötä käyttäjätunnuksesi tai sähköpostiosoitteesi alle ja paina Hae. Jos käyttäjätunnuksesi löytyy tietokannasta, saat sähköpostitse viestin, jossa pyydetään vahvistamaan salasanan vaihto.", - "core.login.passwordrequired": "Salasana vaaditaan", - "core.login.policyaccept": "Ymmärrän ja hyväksyn", - "core.login.policyagree": "Sinun täytyy hyväksyä tämä käyttösopimus voidaksesi jatkaa sivun käyttöä. Hyväksytkö käyttösopimuksen?", - "core.login.policyagreement": "Sivuston käyttösopimus", - "core.login.policyagreementclick": "Napsauttamalla tästä voit lukea sivuston käyttösopimuksen", - "core.login.potentialidps": "Kirjaudu sisään käyttäen tiliäsi kohteessa:", - "core.login.profileinvaliddata": "Epäkelpo arvo", - "core.login.recaptchachallengeimage": "reCAPTCHA-tarkistuskuva", - "core.login.reconnect": "Yhdistä uudelleen", - "core.login.reconnectdescription": "Käyttäjätunnuksesi on virheellinen tai vanhentunut. Sinun täytyy kirjautua uudelleen sivustolle.", - "core.login.reconnectssodescription": "Käyttäjätunnuksesi on virheellinen tai vanhentunut. Sinun täytyy kirjautua uudelleen sivustolle. Tehdäksesi tämän avaa sivusto www-selaimella ja kirjaudu sisään.", - "core.login.resendemail": "Lähetä sähköposti uudelleen", - "core.login.security_question": "Suojauskysymys", - "core.login.selectacountry": "Valitse maa", - "core.login.signupplugindisabled": "{{$a}} ei ole sallittu.", - "core.login.siteaddress": "Sivuston osoite", - "core.login.siteinmaintenance": "Sivusto on huoltotilassa", - "core.login.sitepolicynotagreederror": "Sivustonkäyttöehtoja ei hyväksytty.", - "core.login.siteurl": "Sivuston verkko-osoite URL", - "core.login.siteurlrequired": "Sivuston verkko-osoite (URL) vaaditaan esim. http://www.moodle.fi tai https://www.moodle.fi", - "core.login.startsignup": "Aloita nyt luomalla uusi käyttäjätunnus!", - "core.login.supplyinfo": "Lisätietoja", - "core.login.username": "Käyttäjätunnus", - "core.login.usernameoremail": "Kirjoita joko käyttäjätunnus tai sähköpostiosoite", - "core.login.usernamerequired": "Käyttäjänimi vaaditaan", - "core.login.usernotaddederror": "Käyttäjää ei lisätty - virhe", - "core.login.visitchangepassword": "Haluatko vierailla sivulla vaihtaaksesi salasanan?", - "core.login.webservicesnotenabled": "Webservice-palvelua ei ole käytössä järjestelmässänne. Ole hyvä ja ota yhteyttä sivuston pääkäyttäjään, jos haluat ottaa sen käyttöön.", - "core.lostconnection": "Käyttäjätunnuksesi on virheellinen tai vanhentunut. Sinun täytyy kirjautua uudelleen sivustolle.", - "core.mainmenu.changesite": "Vaihda sivustoa", - "core.mainmenu.help": "Ohje", - "core.mainmenu.logout": "Kirjaudu ulos", - "core.mainmenu.website": "Www-sivusto", - "core.maxsizeandattachments": "Tiedostojen kokoraja: {{$a.size}} ja tiedostojen maksimimäärä: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Muut", - "core.mod_assign": "Tehtävä", - "core.mod_assignment": "Tehtävä (2.2) (ei käytössä)", - "core.mod_book": "Kirja", - "core.mod_chat": "Chat", - "core.mod_choice": "Valinta", - "core.mod_data": "Tietokanta", - "core.mod_database": "Tietokanta", - "core.mod_external-tool": "Ulkoinen työkalu", - "core.mod_feedback": "Palaute", - "core.mod_file": "Tiedosto", - "core.mod_folder": "Kansio", - "core.mod_forum": "Keskustelualue", - "core.mod_glossary": "Sanasto", - "core.mod_ims": "IMS-sisältöpaketti", - "core.mod_imscp": "IMS-sisältöpaketti", - "core.mod_label": "Ohjeteksti", - "core.mod_lesson": "Oppitunti", - "core.mod_lti": "Ulkoinen työkalu", - "core.mod_page": "Sivu", - "core.mod_quiz": "Tentti", - "core.mod_resource": "Aineisto", - "core.mod_scorm": "SCORM-paketti", - "core.mod_survey": "Kysely", - "core.mod_url": "Verkko-osoite", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Työpaja", - "core.moduleintro": "Kuvaus", - "core.more": "lisää", - "core.mygroups": "Omat ryhmäni", - "core.name": "Nimi", - "core.networkerrormsg": "Sivustoon yhdistettäessä tapahtui virhe. Ole hyvä ja tarkista yhteytesi ja yritä uudestaan.", - "core.never": "Ei koskaan", - "core.next": "Seuraava", - "core.no": "Ei", - "core.nocomments": "Ei kommentteja", - "core.nograde": "Ei arviointia", - "core.none": "Ei yhtään", - "core.nopasswordchangeforced": "Et voi jatkaa ennen kuin vaihdat salasanasi.", - "core.nopermissions": "Sinulla ei ole oikeutta tehdä kyseistä operaatiota ({{$a}})", - "core.noresults": "Ei tuloksia", - "core.noselection": "Ei valintaa", - "core.notenrolledprofile": "Profiilia ei voida näyttää, sillä tämä käyttäjä ei ole kirjautuneena tälle kurssille.", - "core.notice": "Ilmoitus", - "core.notingroup": "Sinun pitää olla ryhmän jäsen nähdäksesi tämän sivun.", - "core.notsent": "Ei lähetetty", - "core.now": "nyt", - "core.numwords": "{{$a}} sanaa", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Klikkaa tästä nähdäksesi kuvan täydessä koossa", - "core.openinbrowser": "Avaa selaimessa", - "core.othergroups": "Muut ryhmät", - "core.pagea": "Sivu {{$a}}", - "core.paymentinstant": "Napsauta painiketta maksaaksesi kurssista ja liittyäksesi kurssille.", - "core.phone": "Puhelin", - "core.pictureof": "Kuva: {{$a}}", - "core.previous": "Edellinen", - "core.proceed": "Etene", - "core.pulltorefresh": "Vedä päivittääksesi", - "core.question.answer": "Vastaus", - "core.question.answersaved": "Vastaus tallennettu", - "core.question.certainty": "Varmuus", - "core.question.complete": "Valmis", - "core.question.correct": "Oikein", - "core.question.errorattachmentsnotsupported": "Mobiilisovellus ei vielä tue vastausten liitetiedostoja.", - "core.question.errorinlinefilesnotsupported": "Mobiilisovellus ei tue vielä tiedoston muokkaamista.", - "core.question.errorquestionnotsupported": "Mobiilisovellus ei tue kysymystyyppiä: {{$a}}.", - "core.question.feedback": "Palaute", - "core.question.howtodraganddrop": "Napauta valitaksesi ja napauta toisen kerran pudottaaksesi.", - "core.question.incorrect": "Väärin", - "core.question.information": "Informaatio", - "core.question.invalidanswer": "Puutteellinen vastaus", - "core.question.notanswered": "Ei vastattu", - "core.question.notyetanswered": "Ei vielä vastattu", - "core.question.partiallycorrect": "Osittain oikein", - "core.question.questionmessage": "Kysymys {{$a}}: {{$b}}", - "core.question.questionno": "Kysymys {{$a}}", - "core.question.requiresgrading": "Vaatii arvioinnin", - "core.quotausage": "Olet tällä hetkellä käyttänyt {{$a.used}} rajastasi {{$a.total}}.", - "core.rating.aggregateavg": "Arviointien keskiarvo", - "core.rating.aggregatecount": "Arviointien määrä", - "core.rating.aggregatemax": "Arviointimaksimi", - "core.rating.aggregatemin": "Arviointiminimi", - "core.rating.aggregatesum": "Arviointien summa", - "core.rating.noratings": "Ei palautettuja arvioita", - "core.rating.rating": "Arviointi", - "core.rating.ratings": "Arvioinnit", - "core.redirectingtosite": "Sinut uudelleenohjataan sivustolle.", - "core.refresh": "Päivitä", - "core.remove": "Poista", - "core.required": "Vaadittu", - "core.requireduserdatamissing": "Tältä käyttäjältä puuttuu vaadittuja tietoja profiilistaan. Ole hyvä ja täytä tiedot sivustolla ja yritä uudestaan.
                {{$a}}", - "core.resourcedisplayopen": "Avaa", - "core.resources": "Aineistot", - "core.restore": "Palauta", - "core.restricted": "Rajoitettu", - "core.retry": "Yritä uudelleen", - "core.save": "Tallenna", - "core.savechanges": "Tallenna muutokset", - "core.search": "Hae", - "core.searching": "Hakee", - "core.searchresults": "Hakutulokset", - "core.sec": "sekunti", - "core.secs": "sekuntia", - "core.seemoredetail": "Napsauta tästä nähdäksesi lisätietoja", - "core.selectacategory": "Ole hyvä ja valitse kategoria", - "core.selectacourse": "Valitse kurssi", - "core.selectagroup": "Valitse ryhmä", - "core.send": "Lähetä", - "core.sending": "Lähettää", - "core.serverconnection": "Virhe yhdistettäessä palvelimelle", - "core.settings.about": "Tietoja", - "core.settings.cannotsyncoffline": "Ei pystytty synkronoimaan offline-tietoja.", - "core.settings.cannotsyncwithoutwifi": "Ei pystytty synkronoimaan koska nykyiset asetukset sallivat synkronoinnin vain langattoman yhteyden kautta. Ole hyvä ja yhdistä langattomaan verkkoon (Wi-Fi).", - "core.settings.currentlanguage": "Nykyinen kieli", - "core.settings.debugdisplay": "Näytä virheenjäljitysviestit", - "core.settings.deletesitefiles": "Oletko varma, että haluat poistaa ladatut tiedostot sivustolta '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Poista sivuston tiedostot", - "core.settings.deviceinfo": "Laitteen tiedot", - "core.settings.deviceos": "Laitteen käyttöjärjestelmä", - "core.settings.disableall": "Estä ilmoitukset", - "core.settings.disabled": "Poistettu käytöstä", - "core.settings.errordeletesitefiles": "Sivuston tiedostoja poistettaessa tapahtui virhe.", - "core.settings.errorsyncsite": "Sivuston tietojen synkronoinnissa tapahtui virhe. Ole hyvä ja tarkista internet-yhteytesi ja yritä uudelleen.", - "core.settings.general": "Yleinen", - "core.settings.language": "Kieli", - "core.settings.license": "Lisenssi", - "core.settings.locked": "lukittu", - "core.settings.loggedin": "Kirjautuneena", - "core.settings.loggedoff": "Poissa", - "core.settings.preferences": "Asetukset", - "core.settings.settings": "Asetukset", - "core.settings.sites": "Sivustot", - "core.settings.synchronization": "Synkronointi", - "core.settings.synchronizenow": "Synkronoi nyt", - "core.settings.syncsettings": "Synkronoinnin asetukset", - "core.settings.total": "Yhteensä", - "core.settings.wificonnection": "Langaton (Wi-Fi) yhteys", - "core.sharedfiles.chooseaccountstorefile": "Valitse mille käyttäjätilille tiedostot tallennetaan.", - "core.sharedfiles.chooseactionrepeatedfile": "Tällä nimellä löytyy jo tiedosto. Haluatko korvata olemassa olevan tiedoston vai nimetä sen uudelleen \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Yhtään sivustoa ei ole lisätty. Ole hyvä ja lisää sivusto ennen kuin jaat tiedostoja sovelluksella.", - "core.sharedfiles.nosharedfiles": "Tällä sivustolla ei ole yhtään jaettuja tiedostoja.", - "core.sharedfiles.nosharedfilestoupload": "Sinulla ei ole yhtään tiedostoa lähetettäväksi. Jos haluat lähettää tiedoston toisesta sovelluksesta, niin valitse tiedosto ja paina \"avaa sovelluksessa\"-painiketta ja valitse Moodle Mobile app.", - "core.sharedfiles.rename": "Uudelleennimeä", - "core.sharedfiles.replace": "Korvaa", - "core.sharedfiles.sharedfiles": "Jaetut tiedosto", - "core.sharedfiles.successstorefile": "Tiedosto tallennettiin onnistuneesti. Nyt voit valita tämän tiedoston lähettääksesi sen yksityisiin tiedostoihisi tai liittääksesi sen johonkin aktiviteettiin.", - "core.show": "Näytä", - "core.showless": "Näytä vähemmän...", - "core.showmore": "Näytä lisää...", - "core.site": "Sivusto", - "core.sitehome.sitehome": "Sivuston etusivu", - "core.sitehome.sitenews": "Sivuston uutiset", - "core.sitemaintenance": "Sivusto on huoltotilassa eikä se ole nyt saatavilla", - "core.sizeb": "tavua", - "core.sizegb": "Gt", - "core.sizekb": "Kt", - "core.sizemb": "Mt", - "core.sorry": "Anteeksi..", - "core.sort": "Järjestä uudelleen", - "core.sortby": "Lajittele", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d.%m.%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d.%m.%Y %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "Lähetä", - "core.success": "Valmis!", - "core.tablet": "Tabletti-tietokone", - "core.tag.noresultsfor": "Ei tuloksia haulla '{{$a}}'", - "core.tag.searchtags": "Hae tunnisteita", - "core.tag.tag": "Tunniste", - "core.tag.tags": "Tunnisteet", - "core.teachers": "Opettajat", - "core.thereisdatatosync": "Offline {{$a}} odottaa synkronointia.", - "core.thisdirection": "ltr", - "core.time": "Aika", - "core.timesup": "Aika loppui!", - "core.today": "Tänään", - "core.tryagain": "Yritä uudelleen", - "core.uhoh": "Voi ei!", - "core.unexpectederror": "Odottomaton virhe. Ole hyvä, sulje sovellus ja käynnistä se uudelleen.", - "core.unicodenotsupported": "Joitain hymiöitä ei tueta tällä sivustolla. Nämä merkit poistetaan automaattisesti viestin lähetyksen yhteydessä.", - "core.unknown": "Tuntematon", - "core.unlimited": "Rajoittamaton", - "core.unzipping": "Puretaan ZIP-tiedostoa", - "core.upgraderunning": "Sivustoa päivitetään, ole hyvä ja yritä myöhemmin uudelleen.", - "core.user": "Käyttäjä", - "core.user.address": "Osoite", - "core.user.city": "Paikkakunta", - "core.user.contact": "Yhteystiedot", - "core.user.country": "Maa", - "core.user.description": "Kuvaus", - "core.user.details": "Yksityiskohdat", - "core.user.detailsnotavailable": "Tämän käyttäjän käyttäjätiedot eivät ole saatavilla sinulle.", - "core.user.editingteacher": "Opettaja", - "core.user.email": "Sähköpostiosoite", - "core.user.emailagain": "Sähköposti (varmistus)", - "core.user.firstname": "Etunimi", - "core.user.interests": "Kiinnostukset", - "core.user.lastname": "Sukunimi", - "core.user.manager": "Hallinnoija", - "core.user.newpicture": "Uusi kuva", - "core.user.noparticipants": "Tälle kurssille ei löytynyt osallistujia", - "core.user.participants": "Osallistujat", - "core.user.phone1": "Puhelin", - "core.user.phone2": "Matkapuhelin", - "core.user.roles": "Roolit", - "core.user.sendemail": "Sähköposti", - "core.user.student": "Opiskelija", - "core.user.teacher": "Opettaja ilman muokkausoikeutta", - "core.user.webpage": "Verkkosivu", - "core.userdeleted": "Tämä tunnus on poistettu", - "core.userdetails": "Käyttäjätiedot", - "core.users": "Käyttäjähallinta", - "core.view": "Näytä", - "core.viewprofile": "Näytä profiili", - "core.warningofflinedatadeleted": "Komponentin {{component}} offline-tiedot '{{name}}' on poistettu. {{error}}", - "core.whatisyourage": "Kuinka vanha olet?", - "core.wheredoyoulive": "Missä maassa asut?", - "core.whoops": "Oho!", - "core.whyisthishappening": "Miksi tämä tapahtuu?", - "core.whyisthisrequired": "Miksi tätä vaaditaan?", - "core.wsfunctionnotavailable": "Verkkopalvelun toiminto ei ole käytettävissä.", - "core.year": "vuosi", - "core.years": "vuotta", - "core.yes": "Kyllä" -} \ No newline at end of file diff --git a/src/assets/lang/fr.json b/src/assets/lang/fr.json deleted file mode 100644 index 16e04103f..000000000 --- a/src/assets/lang/fr.json +++ /dev/null @@ -1,2165 +0,0 @@ -{ - "addon.badges.alignment": "Recouvrement", - "addon.badges.badgedetails": "Description du badge", - "addon.badges.badges": "Badges", - "addon.badges.bendorsement": "Approbation", - "addon.badges.claimcomment": "Commentaire d'approbation", - "addon.badges.claimid": "URL de revendication", - "addon.badges.contact": "Contact", - "addon.badges.dateawarded": "Date de remise", - "addon.badges.expired": "Échu", - "addon.badges.expirydate": "Date d'échéance", - "addon.badges.imageauthoremail": "Courriel de l'auteur de l'image", - "addon.badges.imageauthorname": "Nom de l'auteur de l'image", - "addon.badges.imageauthorurl": "URL de l'auteur de l'image", - "addon.badges.imagecaption": "Légende de l'image", - "addon.badges.issuancedetails": "Échéance du badge", - "addon.badges.issuerdetails": "Détail de l'émetteur", - "addon.badges.issueremail": "Courriel", - "addon.badges.issuername": "Nom de l'émetteur", - "addon.badges.issuerurl": "URL de l'émetteur", - "addon.badges.language": "Langue", - "addon.badges.noalignment": "Ce badge n'a pas d'aptitude externe ni de standard spécifié.", - "addon.badges.nobadges": "Il n'y a pas de badge disponible.", - "addon.badges.norelated": "Ce badge n'a aucun badge associé.", - "addon.badges.recipientdetails": "Infos détenteur", - "addon.badges.relatedbages": "Badges associés", - "addon.badges.version": "Version", - "addon.badges.warnexpired": " (Ce badge est arrivé à échéance !)", - "addon.block_activitymodules.pluginname": "Activités", - "addon.block_activityresults.pluginname": "Résultats d'activité", - "addon.block_badges.pluginname": "Derniers badges", - "addon.block_blogmenu.pluginname": "Menu blog", - "addon.block_blogrecent.pluginname": "Articles de blog récents", - "addon.block_blogtags.pluginname": "Tags de blog", - "addon.block_calendarmonth.pluginname": "Calendrier", - "addon.block_calendarupcoming.pluginname": "Événements à venir", - "addon.block_comments.pluginname": "Commentaires", - "addon.block_completionstatus.pluginname": "Achèvement de cours", - "addon.block_glossaryrandom.pluginname": "Article d'un glossaire", - "addon.block_learningplans.pluginname": "Plans de formation", - "addon.block_myoverview.all": "Tout (sauf cours retirés de l'affichage)", - "addon.block_myoverview.allincludinghidden": "Tout", - "addon.block_myoverview.favourites": "Favoris", - "addon.block_myoverview.future": "À venir", - "addon.block_myoverview.hiddencourses": "Retirés de l'affichage", - "addon.block_myoverview.inprogress": "En cours", - "addon.block_myoverview.lastaccessed": "Dernier accès", - "addon.block_myoverview.morecourses": "Plus de cours", - "addon.block_myoverview.nocourses": "Pas de cours", - "addon.block_myoverview.past": "Passés", - "addon.block_myoverview.pluginname": "Vue d'ensemble des cours", - "addon.block_myoverview.shortname": "Nom abrégé", - "addon.block_myoverview.title": "Nom", - "addon.block_newsitems.pluginname": "Dernières annonces", - "addon.block_onlineusers.pluginname": "Utilisateurs en ligne", - "addon.block_privatefiles.pluginname": "Fichiers personnels", - "addon.block_recentactivity.pluginname": "Activité récente", - "addon.block_recentlyaccessedcourses.nocourses": "Pas de cours récent", - "addon.block_recentlyaccessedcourses.pluginname": "Cours consultés récemment", - "addon.block_recentlyaccesseditems.noitems": "Pas d'élément récent", - "addon.block_recentlyaccesseditems.pluginname": "Éléments consultés récemment", - "addon.block_rssclient.pluginname": "Flux RSS", - "addon.block_selfcompletion.pluginname": "Auto-achèvement", - "addon.block_sitemainmenu.pluginname": "Menu principal", - "addon.block_starredcourses.nocourses": "Aucun cours marqué comme favori", - "addon.block_starredcourses.pluginname": "Cours favoris", - "addon.block_tags.pluginname": "Tags", - "addon.block_timeline.duedate": "Délai de remise", - "addon.block_timeline.next30days": "30 prochains jours", - "addon.block_timeline.next3months": "3 prochains mois", - "addon.block_timeline.next6months": "6 prochains mois", - "addon.block_timeline.next7days": "7 prochains jours", - "addon.block_timeline.nocoursesinprogress": "Aucun cours actif", - "addon.block_timeline.noevents": "Aucune activité à rendre à venir", - "addon.block_timeline.overdue": "En retard", - "addon.block_timeline.pluginname": "Chronologie", - "addon.block_timeline.sortbycourses": "Trier par cours", - "addon.block_timeline.sortbydates": "Trier par date", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Articles de blog", - "addon.blog.errorloadentries": "Erreur lors du chargement des articles de blog.", - "addon.blog.linktooriginalentry": "Lien vers l'article original", - "addon.blog.noentriesyet": "Aucun article visible ici", - "addon.blog.publishtonoone": "Vous-même (brouillon)", - "addon.blog.publishtosite": "Tout le monde sur ce site", - "addon.blog.publishtoworld": "Tout le monde (grand public)", - "addon.blog.showonlyyourentries": "N'afficher que vos articles", - "addon.blog.siteblogheading": "Blog du site", - "addon.calendar.allday": "Tout le jour", - "addon.calendar.calendar": "Calendrier", - "addon.calendar.calendarevent": "Événement de calendrier", - "addon.calendar.calendarevents": "Événements du calendrier", - "addon.calendar.calendarreminders": "Rappels du calendrier", - "addon.calendar.categoryevents": "Événements de catégorie", - "addon.calendar.confirmeventdelete": "Voulez-vous vraiment supprimer l'événement « {{$a}} » ?", - "addon.calendar.confirmeventseriesdelete": "L'événement « {{$a.name}} » fait partie d'une série d'événements. Voulez-vous supprimer cet événement seulement ou les {{$a.count}} événements de la série ?", - "addon.calendar.courseevents": "Événements de cours", - "addon.calendar.currentmonth": "Mois actuel", - "addon.calendar.daynext": "Jour suivant", - "addon.calendar.dayprev": "Jour précédent", - "addon.calendar.defaultnotificationtime": "Heure de notification par défaut", - "addon.calendar.deleteallevents": "Supprimer tous les événements", - "addon.calendar.deleteevent": "Supprimer l'événement", - "addon.calendar.deleteoneevent": "Supprimer cet événement", - "addon.calendar.durationminutes": "Durée en minutes", - "addon.calendar.durationnone": "Sans durée", - "addon.calendar.durationuntil": "Jusqu'au", - "addon.calendar.editevent": "Modification de l'événement", - "addon.calendar.errorloadevent": "Erreur de chargement de l'événement", - "addon.calendar.errorloadevents": "Erreur de chargement des événements", - "addon.calendar.eventcalendareventdeleted": "Événement de calendrier supprimé", - "addon.calendar.eventduration": "Durée", - "addon.calendar.eventendtime": "Fin", - "addon.calendar.eventkind": "Type d'événement", - "addon.calendar.eventname": "Nom de l'événement", - "addon.calendar.eventstarttime": "Début", - "addon.calendar.eventtype": "Type d'événement", - "addon.calendar.fri": "Ve", - "addon.calendar.friday": "Vendredi", - "addon.calendar.gotoactivity": "Vers l'activité", - "addon.calendar.groupevents": "Événements de groupe", - "addon.calendar.invalidtimedurationminutes": "La durée en minutes que vous avez saisie est incorrecte. Veuillez indiquer un nombre de minutes plus grand que 0 ou choisir sans durée.", - "addon.calendar.invalidtimedurationuntil": "La date et l'heure choisies pour la fin de l'événement sont antérieures au début de l'événement. Veuillez corriger ceci avant de continuer.", - "addon.calendar.mon": "Lu", - "addon.calendar.monday": "Lundi", - "addon.calendar.monthlyview": "Vue mensuelle", - "addon.calendar.newevent": "Nouvel événement", - "addon.calendar.noevents": "Il n'y a pas d'événement", - "addon.calendar.nopermissiontoupdatecalendar": "Vous n'avez pas les permissions requises pour modifier un événement de calendrier.", - "addon.calendar.reminders": "Rappels", - "addon.calendar.repeatedevents": "Événements répétés", - "addon.calendar.repeateditall": "Appliquer également les modifications aux {{$a}} autres événements de cette série", - "addon.calendar.repeateditthis": "Appliquer les modifications à cet événement seulement", - "addon.calendar.repeatevent": "Répéter cet événement", - "addon.calendar.repeatweeksl": "Nombre de répétitions hebdomadaires", - "addon.calendar.sat": "Sa", - "addon.calendar.saturday": "Samedi", - "addon.calendar.setnewreminder": "Nouveau rappel", - "addon.calendar.siteevents": "Événements de site", - "addon.calendar.sun": "Di", - "addon.calendar.sunday": "Dimanche", - "addon.calendar.thu": "Je", - "addon.calendar.thursday": "Jeudi", - "addon.calendar.today": "Aujourd'hui", - "addon.calendar.tomorrow": "Demain", - "addon.calendar.tue": "Ma", - "addon.calendar.tuesday": "Mardi", - "addon.calendar.typecategory": "Événement de catégorie", - "addon.calendar.typeclose": "Événement de fin", - "addon.calendar.typecourse": "Événement de cours", - "addon.calendar.typedue": "Événement de délai", - "addon.calendar.typegradingdue": "Événement d'évaluation à effectuer", - "addon.calendar.typegroup": "Événement de groupe", - "addon.calendar.typeopen": "Événement de début", - "addon.calendar.typesite": "Événement global", - "addon.calendar.typeuser": "Événement privé", - "addon.calendar.upcomingevents": "Événements à venir", - "addon.calendar.userevents": "Événements privés", - "addon.calendar.wed": "Me", - "addon.calendar.wednesday": "Mercredi", - "addon.calendar.when": "Quand", - "addon.calendar.yesterday": "Hier", - "addon.competency.activities": "Activités", - "addon.competency.competencies": "Compétences", - "addon.competency.competenciesmostoftennotproficientincourse": "Compétences non atteintes le plus souvent dans ce cours", - "addon.competency.coursecompetencies": "Compétences du cours", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Les évaluations de compétences de ce cours n'ont pas d'influence sur les plans de formation.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Les évaluations de compétences de ce cours sont immédiatement reportées dans les plans de formation.", - "addon.competency.crossreferencedcompetencies": "Compétences transversales", - "addon.competency.duedate": "Délai d'achèvement", - "addon.competency.errornocompetenciesfound": "Aucune compétence trouvée", - "addon.competency.evidence": "Preuve", - "addon.competency.evidence_competencyrule": "La règle pour la compétence a été atteinte.", - "addon.competency.evidence_coursecompleted": "Le cours « {{$a}} » a été achevé.", - "addon.competency.evidence_coursemodulecompleted": "L'activité « {{$a}} » a été achevée.", - "addon.competency.evidence_courserestored": "L'évaluation a été restaurée avec le cours « {{$a}} ».", - "addon.competency.evidence_evidenceofpriorlearninglinked": "La preuve d'acquis « {{$a}} » a été liée.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "La preuve d'acquis « {{$a}} » a été déliée.", - "addon.competency.evidence_manualoverride": "L'évaluation de la compétence a été donnée manuellement.", - "addon.competency.evidence_manualoverrideincourse": "L'évaluation de la compétence a été donnée manuellement dans le cours « {{$a}} ».", - "addon.competency.evidence_manualoverrideinplan": "L'évaluation de la compétence a été donnée manuellement dans le plan « {{$a}} ».", - "addon.competency.learningplancompetencies": "Compétences du plan de formation", - "addon.competency.learningplans": "Plans de formation", - "addon.competency.myplans": "Mes plans de formation", - "addon.competency.noactivities": "Aucune activité", - "addon.competency.nocompetencies": "Aucune compétence", - "addon.competency.nocompetenciesincourse": "Aucune compétence n'a été liée à ce cours.", - "addon.competency.nocrossreferencedcompetencies": "Aucune autre compétence n'est transversale pour cette compétence.", - "addon.competency.noevidence": "Aucune preuve d'acquis", - "addon.competency.noplanswerecreated": "Aucun plan de formation n'a été créé.", - "addon.competency.nouserplanswithcompetency": "Aucun plan de formation ne contient cette compétence.", - "addon.competency.path": "Chemin :", - "addon.competency.planstatusactive": "Actif", - "addon.competency.planstatuscomplete": "Achevé", - "addon.competency.planstatusdraft": "Brouillon", - "addon.competency.planstatusinreview": "En cours de validation", - "addon.competency.planstatuswaitingforreview": "En attente de validation", - "addon.competency.proficient": "Compétence acquise", - "addon.competency.progress": "Progrès", - "addon.competency.rating": "Évaluation", - "addon.competency.reviewstatus": "Statut de validation", - "addon.competency.status": "Statut", - "addon.competency.template": "Modèle de plan de formation", - "addon.competency.uponcoursecompletion": "Lors de l'achèvement du cours :", - "addon.competency.usercompetencystatus_idle": "En suspens", - "addon.competency.usercompetencystatus_inreview": "En cours de validation", - "addon.competency.usercompetencystatus_waitingforreview": "En attente de validation", - "addon.competency.userplans": "Plans de formation", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} compétences sur {{$a.y}} sont acquises", - "addon.competency.xcompetenciesproficientoutofyincourse": "Vous avez acquis {{$a.x}} compétences sur {{$a.y}} dans ce cours.", - "addon.coursecompletion.complete": "Terminer", - "addon.coursecompletion.completecourse": "Marquer le cours comme achevé", - "addon.coursecompletion.completed": "Terminé", - "addon.coursecompletion.completiondate": "Date d'achèvement", - "addon.coursecompletion.completionmenuitem": "Achèvement", - "addon.coursecompletion.couldnotloadreport": "Impossible de charger le rapport d'achèvement de cours. Veuillez essayer plus tard.", - "addon.coursecompletion.coursecompletion": "Achèvement de cours", - "addon.coursecompletion.criteria": "Critères", - "addon.coursecompletion.criteriagroup": "Groupe de critères", - "addon.coursecompletion.criteriarequiredall": "Tous les critères ci-dessous sont requis", - "addon.coursecompletion.criteriarequiredany": "Un des critères ci-dessous est requis", - "addon.coursecompletion.inprogress": "En cours", - "addon.coursecompletion.manualselfcompletion": "Auto-achèvement manuel", - "addon.coursecompletion.nottracked": "Vous n'êtes actuellement pas suivi pour l'achèvement de ce cours", - "addon.coursecompletion.notyetstarted": "Pas encore commencé", - "addon.coursecompletion.pending": "En suspens", - "addon.coursecompletion.required": "Requis", - "addon.coursecompletion.requiredcriteria": "Critères requis", - "addon.coursecompletion.requirement": "Prérequis", - "addon.coursecompletion.status": "Statut", - "addon.coursecompletion.viewcoursereport": "Consulter le rapport du cours", - "addon.files.couldnotloadfiles": "La liste des fichiers n'a pas pu être chargée.", - "addon.files.emptyfilelist": "Aucun fichier à afficher.", - "addon.files.erroruploadnotworking": "Il n'est actuellement pas possible de déposer des fichiers sur votre site.", - "addon.files.files": "Fichiers", - "addon.files.privatefiles": "Fichiers personnels", - "addon.files.sitefiles": "Fichiers du site", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Configurer les appareils", - "addon.messages.acceptandaddcontact": "Accepter et ajouter aux contacts", - "addon.messages.addcontact": "Ajouter ce contact", - "addon.messages.addcontactconfirm": "Voulez-vous vraiment ajouter {{$a}} à vos contacts ?", - "addon.messages.addtofavourites": "Marquer la conversation comme favorite", - "addon.messages.addtoyourcontacts": "Ajouter aux contacts", - "addon.messages.blocknoncontacts": "Empêcher les utilisateurs hors liste de contacts de m'envoyer des messages personnels", - "addon.messages.blockuser": "Bloquer l'utilisateur", - "addon.messages.blockuserconfirm": "Voulez-vous vraiment bloquer {{$a}} ?", - "addon.messages.contactableprivacy": "Accepter des messages de :", - "addon.messages.contactableprivacy_coursemember": "Mes contacts et tout le monde dans mes cours", - "addon.messages.contactableprivacy_onlycontacts": "Mes contacts seulement", - "addon.messages.contactableprivacy_site": "Tout le monde sur le site", - "addon.messages.contactblocked": "Contact bloqué", - "addon.messages.contactlistempty": "La liste des contacts est vide", - "addon.messages.contactname": "Nom du contact", - "addon.messages.contactrequestsent": "Demande de contact envoyée", - "addon.messages.contacts": "Contacts", - "addon.messages.conversationactions": "Menu d'actions de conversation", - "addon.messages.decline": "Décliner", - "addon.messages.deleteallconfirm": "Voulez-vous vraiment supprimer l'intégralité de cette conversation ? Cette opération ne la supprimera pas pour les autres participants à la conversation.", - "addon.messages.deleteallselfconfirm": "Voulez-vous vraiment supprimer l'intégralité de cette conversation personnelle ?", - "addon.messages.deleteconversation": "Supprimer la conversation", - "addon.messages.deleteforeveryone": "Supprimer pour moi et et pour tous les autres", - "addon.messages.deletemessage": "Supprimer le message", - "addon.messages.deletemessageconfirmation": "Voulez-vous vraiment supprimer ce message ? Il ne sera supprimé que de votre historique de messagerie et continuera d'être visible par l'utilisateur qui a envoyé ou reçu le message.", - "addon.messages.errordeletemessage": "Erreur lors de la suppression du message.", - "addon.messages.errorwhileretrievingcontacts": "Erreur lors de la récupération de contacts depuis le serveur.", - "addon.messages.errorwhileretrievingdiscussions": "Erreur lors de la récupération de discussions depuis le serveur.", - "addon.messages.errorwhileretrievingmessages": "Erreur lors de la récupération de messages depuis le serveur.", - "addon.messages.errorwhileretrievingusers": "Error lors de la récupération des utilisateurs depuis le serveur.", - "addon.messages.groupconversations": "Groupe", - "addon.messages.groupinfo": "Info du groupe", - "addon.messages.individualconversations": "Privée", - "addon.messages.info": "Info utilisateur", - "addon.messages.isnotinyourcontacts": "{{$a}} n'est pas dans vos contacts", - "addon.messages.message": "Message personnel", - "addon.messages.messagenotsent": "Ce message n'a pas été envoyé. Veuillez essayer plus tard.", - "addon.messages.messagepreferences": "Préférences des messages", - "addon.messages.messages": "Messages personnels", - "addon.messages.muteconversation": "Mettre en sourdine", - "addon.messages.mutedconversation": "Conversation en sourdine", - "addon.messages.newmessage": "Nouveau message", - "addon.messages.newmessages": "Nouveaux messages", - "addon.messages.nocontactrequests": "Aucune demande de contact", - "addon.messages.nocontactsgetstarted": "Aucun contact", - "addon.messages.nofavourites": "Aucune conversation favorite", - "addon.messages.nogroupconversations": "Pas de conversation de groupe", - "addon.messages.noindividualconversations": "Pas de conversation privée", - "addon.messages.nomessagesfound": "Aucun message personnel trouvé", - "addon.messages.noncontacts": "Non contact", - "addon.messages.nousersfound": "Aucun utilisateur trouvé", - "addon.messages.numparticipants": "{{$a}} participants", - "addon.messages.removecontact": "Supprimer ce contact", - "addon.messages.removecontactconfirm": "Voulez-vous vraiment retirer {{$a}} de vos contacts ?", - "addon.messages.removefromfavourites": "Retirer la conversation des favoris", - "addon.messages.removefromyourcontacts": "Retirer des contacts", - "addon.messages.requests": "Demandes", - "addon.messages.requirecontacttomessage": "Vous devez demander à {{$a}} de vous ajouter comme contact pour pouvoir lui envoyer un message", - "addon.messages.searchcombined": "Rechercher des personnes et des messages", - "addon.messages.selfconversation": "Espace personnel", - "addon.messages.selfconversationdefaultmessage": "Enregistrer des brouillons, liens, note, etc. pour un usage ultérieur.", - "addon.messages.sendcontactrequest": "Envoyer une demande de contact", - "addon.messages.showdeletemessages": "Afficher les messages supprimés", - "addon.messages.type_blocked": "Bloqué", - "addon.messages.type_offline": "Hors connexion", - "addon.messages.type_online": "En ligne", - "addon.messages.type_search": "Résultats de recherche", - "addon.messages.type_strangers": "Autres", - "addon.messages.unabletomessage": "Vous ne pouvez pas envoyer un message à cet utilisateur", - "addon.messages.unblockuser": "Débloquer l'utilisateur", - "addon.messages.unblockuserconfirm": "Voulez-vous vraiment débloquer {{$a}} ?", - "addon.messages.unmuteconversation": "Enlever la sourdine", - "addon.messages.useentertosend": "Taper entrée pour envoyer", - "addon.messages.useentertosenddescdesktop": "Si ce réglage est désactivé, vous pouvez utiliser Ctrl+Entrée pour envoyer le message.", - "addon.messages.useentertosenddescmac": "Si ce réglage est désactivé, vous pouvez utiliser Cmd+Entrée pour envoyer le message.", - "addon.messages.userwouldliketocontactyou": "{{$a}} aimerait vous contacter", - "addon.messages.warningconversationmessagenotsent": "Impossible d'envoyer le(s) message(s) à la conversation {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Impossible d'envoyer de message à l'utilisateur {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Aimerait vous contacter", - "addon.messages.you": "Vous :", - "addon.messages.youhaveblockeduser": "Vous avez bloqué cet utilisateur.", - "addon.messages.yourcontactrequestpending": "Votre demande de contact avec {{$a}} est en suspens", - "addon.mod_assign.acceptsubmissionstatement": "Veuillez accepter les conditions d'envoi.", - "addon.mod_assign.addattempt": "Autoriser une autre tentative", - "addon.mod_assign.addnewattempt": "Ajouter une tentative", - "addon.mod_assign.addnewattemptfromprevious": "Ajouter une tentative basée sur le travail remis précédemment", - "addon.mod_assign.addsubmission": "Ajouter un travail", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Les détails du devoir et le formulaire de remise de document seront disponibles dès le {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Autoriser la remise dès le", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Ce devoir acceptera la remise de documents dès le {{$a}}", - "addon.mod_assign.applytoteam": "Appliquer les notes et le feedback à tout le groupe", - "addon.mod_assign.assignmentisdue": "Devoir à effectuer", - "addon.mod_assign.attemptnumber": "Numéro de tentative", - "addon.mod_assign.attemptreopenmethod": "Réouverture des travaux remis", - "addon.mod_assign.attemptreopenmethod_manual": "Manuellement", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatiquement jusqu'à réussite", - "addon.mod_assign.attemptsettings": "Réglages de tentative", - "addon.mod_assign.cannoteditduetostatementsubmission": "Vous ne pouvez ni ajouter ni modifier de données dans l'app, car la déclaration de remise n'a pas pu être récupérée depuis le site.", - "addon.mod_assign.cannotgradefromapp": "Certaines méthodes d'évaluations ne sont pas encore supportées par l'app et ne peuvent pas être modifiées.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Vous ne pouvez pas déposer de travail à évaluer dans l'app, car il n'est actuellement pas possible de récupérer la déclaration de remise.", - "addon.mod_assign.confirmsubmission": "Voulez-vous vraiment remettre votre travail pour évaluation ? Vous ne pourrez plus effectuer de changement.", - "addon.mod_assign.currentattempt": "Ceci est la tentative {{$a}}.", - "addon.mod_assign.currentattemptof": "Ceci est la tentative {{$a.attemptnumber}} ({{$a.maxattempts}} tentatives permises).", - "addon.mod_assign.currentgrade": "Note actuelle dans le carnet de notes", - "addon.mod_assign.cutoffdate": "Date limite", - "addon.mod_assign.defaultteam": "Groupe par défaut", - "addon.mod_assign.duedate": "Date de remise", - "addon.mod_assign.duedateno": "Pas de date de remise", - "addon.mod_assign.duedatereached": "La date de remise de ce devoir est passée", - "addon.mod_assign.editingstatus": "Statut d'édition", - "addon.mod_assign.editsubmission": "Modifier le travail", - "addon.mod_assign.erroreditpluginsnotsupported": "Vous ne pouvez ni ajouter ni modifier de données dans l'app, car certains plugins ne permettent pas la modification.", - "addon.mod_assign.errorshowinginformation": "Il n'est pas possible d'afficher les informations de remise.", - "addon.mod_assign.extensionduedate": "Date de prolongation", - "addon.mod_assign.feedbacknotsupported": "Le feedback n'est pas supporté par l'app et pourrait ne pas contenir la totalité des informations.", - "addon.mod_assign.grade": "Note", - "addon.mod_assign.graded": "Noté", - "addon.mod_assign.gradedby": "Évalué par", - "addon.mod_assign.gradedfollowupsubmit": "Évalué - travail supplémentaire reçu", - "addon.mod_assign.gradedon": "Évalué le", - "addon.mod_assign.gradelocked": "Cette note est verrouillée ou modifiée dans le carnet de notes.", - "addon.mod_assign.gradenotsynced": "Note non synchronisée", - "addon.mod_assign.gradeoutof": "Note sur {{$a}}", - "addon.mod_assign.gradingstatus": "Statut de l'évaluation", - "addon.mod_assign.groupsubmissionsettings": "Réglages de remises en groupe", - "addon.mod_assign.hiddenuser": "Participant", - "addon.mod_assign.latesubmissions": "Devoirs en retard", - "addon.mod_assign.latesubmissionsaccepted": "Permis jusqu'au {{$a}}", - "addon.mod_assign.markingworkflowstate": "Statut du flux d'évaluation", - "addon.mod_assign.markingworkflowstateinmarking": "En cours d'évaluation", - "addon.mod_assign.markingworkflowstateinreview": "En relecture", - "addon.mod_assign.markingworkflowstatenotmarked": "Non évalué", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Prêt pour publication", - "addon.mod_assign.markingworkflowstatereadyforreview": "Évaluation terminée", - "addon.mod_assign.markingworkflowstatereleased": "Publié", - "addon.mod_assign.modulenameplural": "Devoirs", - "addon.mod_assign.multipleteams": "Membre de plusieurs groupes", - "addon.mod_assign.multipleteams_desc": "Ce devoir nécessite la remise des travaux en groupes. Vous faites partie de plusieurs groupes. Pour pouvoir remettre un travail, vous devez ne faire partie que d'un seul groupe. Veuillez contacter votre enseignant pour qu'il change votre appartenance aux groupes.", - "addon.mod_assign.noattempt": "Aucune tentative", - "addon.mod_assign.nomoresubmissionsaccepted": "Permis uniquement pour les participants ayant obtenu une prolongation", - "addon.mod_assign.noonlinesubmissions": "Ce devoir ne requiert pas de fichier à remettre de votre part", - "addon.mod_assign.nosubmission": "Rien n'a été déposé pour ce devoir", - "addon.mod_assign.notallparticipantsareshown": "Les participants qui n'ont pas encore remis de travail ne sont pas affichés.", - "addon.mod_assign.noteam": "Membre d'aucun groupe", - "addon.mod_assign.noteam_desc": "Ce devoir nécessite la remise des travaux en groupes. Vous ne faites partie d'aucun groupe, et ne pouvez donc pas remettre de travail. Veuillez contacter votre enseignant pour qu'il vous ajoute à un groupe.", - "addon.mod_assign.notgraded": "Non évalué", - "addon.mod_assign.numberofdraftsubmissions": "Brouillons", - "addon.mod_assign.numberofparticipants": "Participants", - "addon.mod_assign.numberofsubmissionsneedgrading": "Nécessitant évaluation", - "addon.mod_assign.numberofsubmittedassignments": "Remis", - "addon.mod_assign.numberofteams": "Groupes", - "addon.mod_assign.numwords": "{{$a}} mots", - "addon.mod_assign.outof": "{{$a.current}} de {{$a.total}}", - "addon.mod_assign.overdue": "Le devoir est en retard de {{$a}}", - "addon.mod_assign.submission": "Devoir rendu", - "addon.mod_assign.submissioneditable": "L'étudiant peut modifier ce travail remis", - "addon.mod_assign.submissionnoteditable": "L'étudiant ne peut pas modifier ce travail remis", - "addon.mod_assign.submissionnotsupported": "Le remise de documents n'est pas supportée par l'app et pourrait ne pas contenir la totalité des informations.", - "addon.mod_assign.submissionslocked": "L'ajout, la modification et la suppression de travaux ont été bloqués", - "addon.mod_assign.submissionstatus": "Statut des travaux remis", - "addon.mod_assign.submissionstatus_": "Pas de travail remis", - "addon.mod_assign.submissionstatus_draft": "Brouillon (non remis)", - "addon.mod_assign.submissionstatus_marked": "Noté", - "addon.mod_assign.submissionstatus_new": "Non remis", - "addon.mod_assign.submissionstatus_reopened": "Rouvert", - "addon.mod_assign.submissionstatus_submitted": "Remis pour évaluation", - "addon.mod_assign.submissionstatusheading": "Statut de remise", - "addon.mod_assign.submissionteam": "Groupe", - "addon.mod_assign.submitassignment": "Envoyer le devoir", - "addon.mod_assign.submitassignment_help": "Une fois ce devoir envoyé, vous ne pourrez plus y effectuer de modification.", - "addon.mod_assign.submittedearly": "Le travail a été remis en avance de {{$a}}", - "addon.mod_assign.submittedlate": "Le travail a été remis en retard de {{$a}}", - "addon.mod_assign.timemodified": "Dernière modification", - "addon.mod_assign.timeremaining": "Temps restant", - "addon.mod_assign.ungroupedusers": "Le réglage « Requiert un groupe pour remettre un devoir » est activé et certains utilisateurs ne sont membres d'aucun groupe ou membres de plusieurs groupes. Cette situation les empêchera de remettre un travail.", - "addon.mod_assign.ungroupedusersoptional": "Le réglage « Les étudiants remettent leur travail en groupe » est activé et certains utilisateurs ne font partie d'aucun groupe ou sont membres de plusieurs groupes. Veuillez prendre note que ces étudiants remettront leur travail en tant que membre du « Groupe par défaut ».", - "addon.mod_assign.unlimitedattempts": "Illimité", - "addon.mod_assign.userswhoneedtosubmit": "Utilisateurs devant valider l'envoi du devoir : {{$a}}", - "addon.mod_assign.userwithid": "Utilisateur d'ID {{id}}", - "addon.mod_assign.viewsubmission": "Afficher le travail remis", - "addon.mod_assign.warningsubmissiongrademodified": "La note a été modifiée sur le site.", - "addon.mod_assign.warningsubmissionmodified": "Le travail remis par l'utilisateur a été modifié sur le site.", - "addon.mod_assign.wordlimit": "Nombre maximal de mots", - "addon.mod_assign_feedback_comments.pluginname": "Feedback par commentaires", - "addon.mod_assign_feedback_editpdf.pluginname": "Annotation PDF", - "addon.mod_assign_feedback_file.pluginname": "Fichiers comme feedback", - "addon.mod_assign_submission_comments.pluginname": "Commentaires", - "addon.mod_assign_submission_file.pluginname": "Remises de fichiers", - "addon.mod_assign_submission_onlinetext.pluginname": "Remise de textes en ligne", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Le nombre maximal de mots pour ce devoir est de {{$a.limit}} mots, alors que vous essayez de remettre un devoir de {{$a.count}} mots. Veuillez retravailler votre devoir et essayer à nouveau.", - "addon.mod_book.errorchapter": "Erreur lors de la lecture du chapitre", - "addon.mod_book.modulenameplural": "Livres", - "addon.mod_book.navnexttitle": "Suivant : {{$a}}", - "addon.mod_book.navprevtitle": "Précédent : {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Chapitres du livre", - "addon.mod_book.toc": "Table des matières", - "addon.mod_chat.beep": "Bip", - "addon.mod_chat.chatreport": "Sessions de chat", - "addon.mod_chat.currentusers": "Utilisateurs en ligne", - "addon.mod_chat.enterchat": "Cliquer ici pour participer au chat", - "addon.mod_chat.entermessage": "Saisissez votre message", - "addon.mod_chat.errorwhileconnecting": "Erreur lors de la connexion au chat.", - "addon.mod_chat.errorwhilegettingchatdata": "Erreur lors de l'obtention des données de chat.", - "addon.mod_chat.errorwhilegettingchatusers": "Erreur lors de l'obtention des usagers du chat.", - "addon.mod_chat.errorwhileretrievingmessages": "Erreur lors de la récupération de messages sur le serveur.", - "addon.mod_chat.errorwhilesendingmessage": "Erreur lors de l'envoi du message.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} bipe tout le monde !", - "addon.mod_chat.messagebeepsyou": "{{$a}} vient de vous biper !", - "addon.mod_chat.messageenter": "{{$a}} vient d'entrer", - "addon.mod_chat.messageexit": "{{$a}} a quitté ce chat", - "addon.mod_chat.messages": "Messages", - "addon.mod_chat.messageyoubeep": "Vous avez bippé {{$a}}", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "Vous devez être en ligne pour envoyer des messages.", - "addon.mod_chat.nomessages": "Pas encore de messages", - "addon.mod_chat.nosessionsfound": "Aucune session trouvée", - "addon.mod_chat.saidto": "a dit à", - "addon.mod_chat.send": "Envoyer", - "addon.mod_chat.sessionstart": "La prochaine session de chat commencera le {{$a.date}}, (dans {{$a.fromnow}})", - "addon.mod_chat.showincompletesessions": "Afficher les sessions incomplètes", - "addon.mod_chat.talk": "Parler", - "addon.mod_chat.viewreport": "Consulter les sessions précédentes", - "addon.mod_choice.cannotsubmit": "Un problème est survenu lors de l'envoi de votre choix.", - "addon.mod_choice.choiceoptions": "Options du sondage", - "addon.mod_choice.errorgetchoice": "Erreur lors de l'obtention des données du choix.", - "addon.mod_choice.expired": "Cette activité s'est terminée le {{$a}}.", - "addon.mod_choice.full": "(complet)", - "addon.mod_choice.modulenameplural": "Sondages", - "addon.mod_choice.noresultsviewable": "Les résultats ne sont actuellement pas visibles.", - "addon.mod_choice.notopenyet": "Cette activité n'est pas disponible avant le {{$a}}.", - "addon.mod_choice.numberofuser": "Nombre de réponses", - "addon.mod_choice.numberofuserinpercentage": "Pourcentage de réponses", - "addon.mod_choice.previewonly": "Cet affichage est une prévisualisation des options disponibles pour cette activité. Vous ne pourrez pas enregistrer votre choix avant le {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Les résultats seront publiés de façon anonyme après votre réponse.", - "addon.mod_choice.publishinfoanonclose": "Les résultats seront publiés de façon anonyme lorsque l'activité sera terminée.", - "addon.mod_choice.publishinfofullafter": "Les résultats complets, montrant les choix de chaque participant, seront publiés après votre réponse.", - "addon.mod_choice.publishinfofullclose": "Les résultats complets, montrant les choix de chaque participant, seront publiés lorsque l'activité sera terminée.", - "addon.mod_choice.publishinfonever": "Les résultats de cette activité ne seront pas publiés après votre réponse.", - "addon.mod_choice.removemychoice": "Retirer mon vote", - "addon.mod_choice.responses": "Réponses", - "addon.mod_choice.responsesresultgraphdescription": "{{number}} % des participants ont choisi l'option : {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Affichage graphique", - "addon.mod_choice.resultsnotsynced": "Votre dernière réponse doit être synchronisée pour être inclue dans les résultats.", - "addon.mod_choice.savemychoice": "Enregistrer mon choix", - "addon.mod_choice.userchoosethisoption": "Utilisateurs qui ont choisi l'option", - "addon.mod_choice.yourselection": "Votre choix", - "addon.mod_data.addentries": "Ajouter des fiches", - "addon.mod_data.advancedsearch": "Recherche avancée", - "addon.mod_data.alttext": "Texte alternatif", - "addon.mod_data.approve": "Approuver", - "addon.mod_data.approved": "Approuvé", - "addon.mod_data.ascending": "Ascendant", - "addon.mod_data.authorfirstname": "Prénom auteur", - "addon.mod_data.authorlastname": "Nom auteur", - "addon.mod_data.confirmdeleterecord": "Voulez-vous vraiment supprimer cette fiche ?", - "addon.mod_data.descending": "Descendant", - "addon.mod_data.disapprove": "Retirer l'approbation", - "addon.mod_data.edittagsnotsupported": "La modification des tags n'est pas supportée par l'app.", - "addon.mod_data.emptyaddform": "Vous n'avez rempli aucun champ !", - "addon.mod_data.entrieslefttoadd": "Vous devez ajouter encore {{$a.entriesleft}} fiches pour terminer cette activité", - "addon.mod_data.entrieslefttoaddtoview": "Vous devez ajouter encore {{$a.entrieslefttoview}} fiches avant de pouvoir consulter les fiches des autres participants.", - "addon.mod_data.errorapproving": "Erreur lors de l'approbation ou de la désapprobation de la fiche.", - "addon.mod_data.errordeleting": "Erreur lors de suppression de la fiche.", - "addon.mod_data.errormustsupplyvalue": "Veuillez renseigner une valeur.", - "addon.mod_data.expired": "Cette activité est fermée depuis {{$a}} et n'est plus disponible", - "addon.mod_data.fields": "Champs", - "addon.mod_data.foundrecords": "Fiches trouvées : {{$a.num}}/{{$a.max}} (Réinitialiser les filtres)", - "addon.mod_data.gettinglocation": "Obtention de l'emplacement", - "addon.mod_data.latlongboth": "La latitude et la longitude sont requises.", - "addon.mod_data.locationpermissiondenied": "L'autorisation d'accès à votre localisation a été refusée.", - "addon.mod_data.menuchoose": "Sélectionner…", - "addon.mod_data.modulenameplural": "Bases de données", - "addon.mod_data.more": "Plus", - "addon.mod_data.mylocation": "Mon emplacement", - "addon.mod_data.nomatch": "Aucune fiche trouvée !", - "addon.mod_data.norecords": "Aucune fiche dans la base de données", - "addon.mod_data.notapproved": "La fiche n'est pas encore approuvée.", - "addon.mod_data.notopenyet": "Cette activité n'est pas disponible avant le {{$a}}", - "addon.mod_data.numrecords": "{{$a}} fiches", - "addon.mod_data.other": "Autre", - "addon.mod_data.recordapproved": "Fiche approuvée", - "addon.mod_data.recorddeleted": "Fiche supprimée", - "addon.mod_data.recorddisapproved": "Fiche désapprouvée", - "addon.mod_data.resetsettings": "Réinitialiser les filtres", - "addon.mod_data.search": "Recherche", - "addon.mod_data.searchbytagsnotsupported": "La recherche par tags n'est pas supportée par l'app.", - "addon.mod_data.selectedrequired": "Toute la sélection requise", - "addon.mod_data.single": "Affichage fiche", - "addon.mod_data.tagarea_data_records": "Fiches de données", - "addon.mod_data.timeadded": "Date ajout", - "addon.mod_data.timemodified": "Date modification", - "addon.mod_data.usedate": "Inclure dans la recherche.", - "addon.mod_feedback.analysis": "Analyse", - "addon.mod_feedback.anonymous": "Anonyme", - "addon.mod_feedback.anonymous_entries": "Réponses anonymes ({{$a}})", - "addon.mod_feedback.average": "Moyenne", - "addon.mod_feedback.captchaofflinewarning": "Le feedback avec CAPTCHA ne peut pas être terminé hors connexion, ou s'il n'est pas configuré, ou si le serveur est arrêté.", - "addon.mod_feedback.complete_the_form": "Répondre aux questions", - "addon.mod_feedback.completed_feedbacks": "Réponses envoyées", - "addon.mod_feedback.continue_the_form": "Continuer à répondre aux questions", - "addon.mod_feedback.feedback_is_not_open": "Le feedback n'est pas ouvert", - "addon.mod_feedback.feedback_submitted_offline": "Ce feedback a été enregistré pour être remis plus tard.", - "addon.mod_feedback.feedbackclose": "Permettre les réponses jusqu'au", - "addon.mod_feedback.feedbackopen": "Permettre les réponses dès le", - "addon.mod_feedback.mapcourses": "Associer le feedback aux cours", - "addon.mod_feedback.maximal": "Maximum", - "addon.mod_feedback.minimal": "Minimum", - "addon.mod_feedback.mode": "Mode", - "addon.mod_feedback.modulenameplural": "Feedbacks", - "addon.mod_feedback.next_page": "Page suivante", - "addon.mod_feedback.non_anonymous": "Le nom du participant sera enregistré et affiché avec ses réponses", - "addon.mod_feedback.non_anonymous_entries": "Réponses non anonymes ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Étudiants sans réponse ({{$a}})", - "addon.mod_feedback.not_selected": "Sans réponse", - "addon.mod_feedback.not_started": "Pas commencé", - "addon.mod_feedback.numberoutofrange": "Nombre en dehors de l'intervalle permis", - "addon.mod_feedback.overview": "Vue d'ensemble", - "addon.mod_feedback.page_after_submit": "Message de fin", - "addon.mod_feedback.preview": "Aperçu", - "addon.mod_feedback.previous_page": "Page précédente", - "addon.mod_feedback.questions": "Questions", - "addon.mod_feedback.response_nr": "Réponse No", - "addon.mod_feedback.responses": "Réponses", - "addon.mod_feedback.save_entries": "Remettre vos réponses", - "addon.mod_feedback.show_entries": "Afficher les réponses", - "addon.mod_feedback.show_nonrespondents": "Afficher les utilisateurs sans réponse", - "addon.mod_feedback.started": "Commencé", - "addon.mod_feedback.this_feedback_is_already_submitted": "Vous avez déjà effectué cette activité.", - "addon.mod_folder.emptyfilelist": "Aucun fichier à afficher.", - "addon.mod_folder.modulenameplural": "Dossiers", - "addon.mod_forum.addanewdiscussion": "Ajouter une discussion", - "addon.mod_forum.addanewquestion": "Ajouter une nouvelle question", - "addon.mod_forum.addanewtopic": "Ajouter un nouveau sujet", - "addon.mod_forum.addtofavourites": "Marquer la discussion comme favorite", - "addon.mod_forum.advanced": "Avancé", - "addon.mod_forum.cannotadddiscussion": "Pour créer une discussion dans ce forum, vous devez être membre de ce groupe.", - "addon.mod_forum.cannotadddiscussionall": "Vous n'avez pas les permissions requises pour lancer une nouvelle discussion pour tous les participants.", - "addon.mod_forum.cannotcreatediscussion": "Impossible de créer une nouvelle discussion", - "addon.mod_forum.couldnotadd": "Impossible d'ajouter votre message à cause d'une erreur indéterminée", - "addon.mod_forum.couldnotupdate": "Impossible de modifier votre message à cause d'une erreur inconnue", - "addon.mod_forum.cutoffdatereached": "L'échéance pour écrire dans ce forum est atteinte ; il n'est plus possible de poster de message.", - "addon.mod_forum.delete": "Supprimer", - "addon.mod_forum.deletedpost": "Ce message a été supprimé", - "addon.mod_forum.deletesure": "Voulez-vous vraiment supprimer ce message ?", - "addon.mod_forum.discussion": "Discussion", - "addon.mod_forum.discussionlistsortbycreatedasc": "Trier par date de création, ordre croissant", - "addon.mod_forum.discussionlistsortbycreateddesc": "Trier par date de création, ordre décroissant", - "addon.mod_forum.discussionlistsortbylastpostasc": "Trier par date de création du dernier message, ordre croissant", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Trier par date de création du dernier message, ordre décroissant", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Trier par nombre de réponses, ordre croissant", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Trier par nombre de réponses, ordre décroissant", - "addon.mod_forum.discussionlocked": "Cette discussion a été verrouillée. Vous ne pouvez plus y répondre.", - "addon.mod_forum.discussionpinned": "Épinglé", - "addon.mod_forum.discussionsubscription": "Abonnement à la discussion", - "addon.mod_forum.edit": "Modifier", - "addon.mod_forum.erroremptymessage": "Un message ne peut pas être vide", - "addon.mod_forum.erroremptysubject": "L'objet d'un message ne peut pas être vide", - "addon.mod_forum.errorgetforum": "Erreur de récupération des données de forum.", - "addon.mod_forum.errorgetgroups": "Erreur lors de l'obtention des réglages de groupe.", - "addon.mod_forum.errorposttoallgroups": "Impossible de créer une nouvelle discussion dans tous les groupes.", - "addon.mod_forum.favouriteupdated": "Le réglage favori a été modifié.", - "addon.mod_forum.forumnodiscussionsyet": "Il n'y a pas encore de discussion dans ce forum.", - "addon.mod_forum.group": "Groupe", - "addon.mod_forum.lastpost": "Dernier message", - "addon.mod_forum.lockdiscussion": "Verrouiller cette discussion", - "addon.mod_forum.lockupdated": "Le réglage verrouillé a été modifié.", - "addon.mod_forum.message": "Message", - "addon.mod_forum.modeflatnewestfirst": "Réponses en ligne, la plus récente en premier", - "addon.mod_forum.modeflatoldestfirst": "Réponses en ligne, la plus ancienne en premier", - "addon.mod_forum.modenested": "Réponses emboîtées", - "addon.mod_forum.modulenameplural": "Forums", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discussions", - "addon.mod_forum.numreplies": "{{numreplies}} réponses", - "addon.mod_forum.pindiscussion": "Épingler cette discussion", - "addon.mod_forum.pinupdated": "Le réglage épinglé a été modifié.", - "addon.mod_forum.postisprivatereply": "Ceci est une réponse privée. Elle n'est pas visible par d'autres participants.", - "addon.mod_forum.posttoforum": "Envoyer", - "addon.mod_forum.posttomygroups": "Envoyer une copie à tous mes groupes", - "addon.mod_forum.privatereply": "Répondre en privé", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Rafraîchir les discussions", - "addon.mod_forum.refreshposts": "Actualiser les messages", - "addon.mod_forum.removefromfavourites": "Retirer cette discussion des favoris", - "addon.mod_forum.reply": "Répondre", - "addon.mod_forum.replyplaceholder": "Écrivez votre réponse…", - "addon.mod_forum.subject": "Sujet", - "addon.mod_forum.tagarea_forum_posts": "Messages de forum", - "addon.mod_forum.thisforumhasduedate": "Le délai pour poster dans ce forum est le {{$a}}.", - "addon.mod_forum.thisforumisdue": "Le délai pour poster dans ce forum était le {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Déverrouiller cette discussion", - "addon.mod_forum.unpindiscussion": "Dépingler cette discussion", - "addon.mod_forum.unread": "Non lu", - "addon.mod_forum.unreadpostsnumber": "{{$a}} messages non lus", - "addon.mod_forum.yourreply": "Votre réponse", - "addon.mod_glossary.addentry": "Ajouter un nouvel article", - "addon.mod_glossary.aliases": "Termes associés", - "addon.mod_glossary.attachment": "Annexe", - "addon.mod_glossary.browsemode": "Parcourir les articles", - "addon.mod_glossary.byalphabet": "Alphabétiquement", - "addon.mod_glossary.byauthor": "Grouper par auteur", - "addon.mod_glossary.bycategory": "Grouper par catégorie", - "addon.mod_glossary.bynewestfirst": "Les plus récents en premier", - "addon.mod_glossary.byrecentlyupdated": "Modifiés récemment", - "addon.mod_glossary.bysearch": "Rechercher", - "addon.mod_glossary.cannoteditentry": "Impossible de modifier l'article", - "addon.mod_glossary.casesensitive": "Article sensible à la casse", - "addon.mod_glossary.categories": "Catégories", - "addon.mod_glossary.concept": "Concept", - "addon.mod_glossary.definition": "Définition", - "addon.mod_glossary.entriestobesynced": "Articles à synchroniser", - "addon.mod_glossary.entrypendingapproval": "Cet article est en attente d'approbation", - "addon.mod_glossary.entryusedynalink": "Article lié automatiquement", - "addon.mod_glossary.errconceptalreadyexists": "Ce concept existe déjà dans le glossaire. Les doublons ne sont pas permis dans ce glossaire.", - "addon.mod_glossary.errorloadingentries": "Une erreur est survenue lors du chargement des articles.", - "addon.mod_glossary.errorloadingentry": "Une erreur est survenue lors du chargement de l'article.", - "addon.mod_glossary.errorloadingglossary": "Une erreur est survenue lors du chargement du glossaire.", - "addon.mod_glossary.fillfields": "Les champs concept et définition sont obligatoires.", - "addon.mod_glossary.fullmatch": "Correspondance mot pour mot", - "addon.mod_glossary.linking": "Liaison automatique", - "addon.mod_glossary.modulenameplural": "Glossaires", - "addon.mod_glossary.noentriesfound": "Aucun article trouvé.", - "addon.mod_glossary.searchquery": "Texte recherché", - "addon.mod_glossary.tagarea_glossary_entries": "Articles de glossaire", - "addon.mod_h5pactivity.all_attempts": "Toutes les tentatives utilisateur", - "addon.mod_h5pactivity.answer_checked": "Réponse vérifiée", - "addon.mod_h5pactivity.answer_correct": "Votre réponse est correcte", - "addon.mod_h5pactivity.answer_fail": "Réponse incorrecte", - "addon.mod_h5pactivity.answer_incorrect": "Votre réponse est incorrecte", - "addon.mod_h5pactivity.answer_pass": "Réponse correcte", - "addon.mod_h5pactivity.attempt": "Tentative", - "addon.mod_h5pactivity.attempt_completion_no": "Cette tentative n'est pas marquée comme terminée", - "addon.mod_h5pactivity.attempt_completion_yes": "Cette tentative est terminée", - "addon.mod_h5pactivity.attempt_success_fail": "Échouée", - "addon.mod_h5pactivity.attempt_success_pass": "Réussie", - "addon.mod_h5pactivity.attempt_success_unknown": "Pas rapportée", - "addon.mod_h5pactivity.attempts_none": "Cette utilisateur n'a pas de tentative.", - "addon.mod_h5pactivity.completion": "Complétude", - "addon.mod_h5pactivity.downloadh5pfile": "Télécharger le fichier H5P", - "addon.mod_h5pactivity.duration": "Durée", - "addon.mod_h5pactivity.maxscore": "Score max", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Mes tentatives", - "addon.mod_h5pactivity.no_compatible_track": "Cette interaction ({{$a}}) ne fournit pas d'information de suivi ou l'information de suivi fournie n'est pas compatible avec la version de l'activité.", - "addon.mod_h5pactivity.outcome": "Objectif", - "addon.mod_h5pactivity.previewmode": "Le contenu est affiché en mode prévisualisation. Aucun suivi des tentatives ne sera pas enregistré.", - "addon.mod_h5pactivity.result_fill-in": "Texte à trous", - "addon.mod_h5pactivity.result_other": "Type d'interaction inconnu", - "addon.mod_h5pactivity.review_my_attempts": "Consulter mes tentatives", - "addon.mod_h5pactivity.score": "Score", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} sur {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Date de début", - "addon.mod_h5pactivity.totalscore": "Score total", - "addon.mod_h5pactivity.viewattempt": "Consulter la tentative {{$a}}", - "addon.mod_imscp.deploymenterror": "Erreur dans le contenu du paquetage !", - "addon.mod_imscp.modulenameplural": "Paquetages IMS Content", - "addon.mod_imscp.showmoduledescription": "Afficher la description", - "addon.mod_imscp.toc": "TDM", - "addon.mod_lesson.answer": "Réponse", - "addon.mod_lesson.attempt": "Tentative : {{$a}}", - "addon.mod_lesson.attemptheader": "Tentative", - "addon.mod_lesson.attemptsremaining": "Il vous reste {{$a}} tentative(s)", - "addon.mod_lesson.averagescore": "Note moyenne", - "addon.mod_lesson.averagetime": "Durée moyenne", - "addon.mod_lesson.branchtable": "Table de contenu", - "addon.mod_lesson.cannotfindattempt": "Erreur : impossible de trouver la tentative", - "addon.mod_lesson.cannotfinduser": "Erreur : impossible de trouver les utilisateurs", - "addon.mod_lesson.clusterjump": "Question non vue du groupe", - "addon.mod_lesson.completed": "Terminé", - "addon.mod_lesson.congratulations": "Félicitations - la leçon est terminée", - "addon.mod_lesson.continue": "Continuer", - "addon.mod_lesson.continuetonextpage": "Continuer vers la page suivante.", - "addon.mod_lesson.defaultessayresponse": "Votre composition sera évaluée par votre enseignant.", - "addon.mod_lesson.detailedstats": "Statistiques détaillées", - "addon.mod_lesson.didnotanswerquestion": "N'a pas répondu à cette question", - "addon.mod_lesson.displayofgrade": "Affichage de la note (pour l'étudiant)", - "addon.mod_lesson.displayscorewithessays": "

                Vous avez obtenu un score de {{$a.score}} sur {{$a.tempmaxgrade}} aux questions notées automatiquement.

                \n

                Les notes de votre(vos) {{$a.essayquestions}} composition(s) sera(ont) évaluée(s) et ajoutée(s) au score final ultérieurement.

                \n

                Votre note actuelle, sans la(les) composition(s), est de {{$a.score}} sur {{$a.grade}}.

                ", - "addon.mod_lesson.displayscorewithoutessays": "Votre score est de {{$a.score}} (sur {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Le mot de passe ne peut pas être vide", - "addon.mod_lesson.enterpassword": "Veuillez saisir le mot de passe :", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "vous n'avez répondu à aucune question. Votre note pour cette leçon est de 0.", - "addon.mod_lesson.errorprefetchrandombranch": "Cette leçon comporte un saut vers une page aléatoire. Elle ne peut être effectuée dans l'app avant d'avoir été commencée dans un navigateur.", - "addon.mod_lesson.errorreviewretakenotlast": "Cette tentative ne peut plus être relue, car une autre tentative a été terminée.", - "addon.mod_lesson.finish": "Terminer", - "addon.mod_lesson.finishretakeoffline": "Cette tentative a été terminée localement.", - "addon.mod_lesson.firstwrong": "Vous n'avez pas répondu correctement. Voulez-vous essayer de deviner la bonne réponse ? Si vous répondez maintenant correctement, vous ne recevrez cependant pas de point.", - "addon.mod_lesson.gotoendoflesson": "Aller à la fin de la leçon", - "addon.mod_lesson.grade": "Note", - "addon.mod_lesson.highscore": "Meilleure note", - "addon.mod_lesson.hightime": "Plus longue durée", - "addon.mod_lesson.leftduringtimed": "Vous avez quitté une leçon à durée limitée.
                Veuillez cliquer sur Continuer pour recommencer cette leçon.", - "addon.mod_lesson.leftduringtimednoretake": "Vous avez quitté une leçon à durée limitée et vous n'êtes
                pas autorisé à la recommencer ou la continuer.", - "addon.mod_lesson.lessonmenu": "Menu leçon", - "addon.mod_lesson.lessonstats": "Statistiques de la leçon", - "addon.mod_lesson.linkedmedia": "Médias liés", - "addon.mod_lesson.loginfail": "Connexion échouée, veuillez réessayer…", - "addon.mod_lesson.lowscore": "Note la plus basse", - "addon.mod_lesson.lowtime": "Plus courte durée", - "addon.mod_lesson.maximumnumberofattemptsreached": "Le nombre maximal de tentatives a été atteint - On passe à la page suivante", - "addon.mod_lesson.modattemptsnoteacher": "La critique par les étudiants ne fonctionne que pour les étudiants.", - "addon.mod_lesson.modulenameplural": "Leçons", - "addon.mod_lesson.noanswer": "Une ou plusieurs questions n'ont pas de réponse donnée. Veuillez revenir en arrière et donner une réponse.", - "addon.mod_lesson.nolessonattempts": "Personne n'a encore fait cette leçon.", - "addon.mod_lesson.nolessonattemptsgroup": "Aucune tentative n'a été effectuée dans cette leçon par des membres du groupe {{$a}}.", - "addon.mod_lesson.notcompleted": "Pas terminé", - "addon.mod_lesson.numberofcorrectanswers": "Nombre de réponses correctes : {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Nombre de questions répondues : {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Nombre de questions répondues : {{$a.nquestions}} (vous devez répondre au moins à {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Vous avez jusqu'ici reçu {{$a.score}} sur un maximum de {{$a.currenthigh}} point(s).", - "addon.mod_lesson.ongoingnormal": "Vous avez répondu correctement à {{$a.correct}} tentatives sur {{$a.viewed}}.", - "addon.mod_lesson.or": "OU", - "addon.mod_lesson.overview": "Vue d'ensemble", - "addon.mod_lesson.preview": "Prévisualisation", - "addon.mod_lesson.progressbarteacherwarning2": "La barre de progression ne sera pas affichée, car vous pouvez modifier cette leçon", - "addon.mod_lesson.progresscompleted": "Vous avez terminé {{$a}} % de la leçon", - "addon.mod_lesson.question": "Question", - "addon.mod_lesson.rawgrade": "Note brute", - "addon.mod_lesson.reports": "Rapports", - "addon.mod_lesson.response": "Feedback", - "addon.mod_lesson.retakefinishedinsync": "Une tentative locale a été synchronisée. Voulez-vous la relire ?", - "addon.mod_lesson.retakelabelfull": "{{retake}} : {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}} : {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Relecture", - "addon.mod_lesson.reviewlesson": "Revoir la leçon", - "addon.mod_lesson.reviewquestionback": "Oui, j'aimerais essayer à nouveau", - "addon.mod_lesson.reviewquestioncontinue": "Non, je veux passer à la question suivante", - "addon.mod_lesson.secondpluswrong": "Pas tout à fait. Voulez-vous essayer à nouveau ?", - "addon.mod_lesson.submit": "Envoyer", - "addon.mod_lesson.teacherjumpwarning": "Un lien {{$a.cluster}} ou un lien {{$a.unseen}} est utilisé dans cette leçon. Un lien « Page suivante » sera utilisé à sa place. Veuillez vous connecter en tant qu'étudiant pour tester ces liens.", - "addon.mod_lesson.teacherongoingwarning": "Le score actuel n'est affiché que pour les étudiants. Veuillez vous connecter en tant qu'étudiant pour tester le score actuel.", - "addon.mod_lesson.teachertimerwarning": "Le chronomètre ne fonctionne que pour les étudiants. Veuillez vous connecter en tant qu'étudiant pour tester le chronomètre.", - "addon.mod_lesson.thatsthecorrectanswer": "C'est une réponse correcte", - "addon.mod_lesson.thatsthewronganswer": "C'est une mauvaise réponse", - "addon.mod_lesson.timeremaining": "Durée restante", - "addon.mod_lesson.timetaken": "Durée utilisée", - "addon.mod_lesson.unseenpageinbranch": "Question non vue au sein d'un groupe", - "addon.mod_lesson.warningretakefinished": "Cette tentative a été terminée dans un navigateur.", - "addon.mod_lesson.welldone": "Bien joué !", - "addon.mod_lesson.youhaveseen": "Vous avez déjà vu au moins une page de cette leçon.
                Voulez-vous commencer à la dernière page que vous avez vue ?", - "addon.mod_lesson.youranswer": "Votre réponse", - "addon.mod_lesson.yourcurrentgradeisoutof": "Votre note actuelle est {{$a.grade}} sur {{$a.total}}", - "addon.mod_lesson.youshouldview": "Vous devriez répondre au moins à {{$a}}", - "addon.mod_lti.errorgetlti": "Erreur lors de l'obtention des données du module.", - "addon.mod_lti.errorinvalidlaunchurl": "L'URL de lancement n'est pas valide.", - "addon.mod_lti.launchactivity": "Lancer l'activité", - "addon.mod_lti.modulenameplural": "Outils externes", - "addon.mod_page.errorwhileloadingthepage": "Erreur lors du chargement du contenu de la page.", - "addon.mod_page.modulenameplural": "Pages", - "addon.mod_quiz.answercolon": "Réponse :", - "addon.mod_quiz.attemptfirst": "Première tentative", - "addon.mod_quiz.attemptlast": "Dernière tentative", - "addon.mod_quiz.attemptnumber": "Tentative", - "addon.mod_quiz.attemptquiznow": "Faire le test", - "addon.mod_quiz.attemptstate": "État", - "addon.mod_quiz.canattemptbutnotsubmit": "Vous pouvez répondre à ce test dans l'app, mais vous devrez envoyer la tentative avec un navigateur, pour les raisons suivantes :", - "addon.mod_quiz.cannotsubmitquizdueto": "Cette tentative de test ne peut pas être envoyée pour les raisons suivantes :", - "addon.mod_quiz.clearchoice": "Effacer mon choix", - "addon.mod_quiz.comment": "Commentaire", - "addon.mod_quiz.completedon": "Terminé le", - "addon.mod_quiz.confirmclose": "Une fois la tentative envoyée, vous n'aurez plus la possibilité de modifier vos réponses pour cette tentative.", - "addon.mod_quiz.confirmcontinueoffline": "La tentative n'a pas été synchronisée depuis {{$a}}. Si entretemps vous avez continué cette tentative avec un autre appareil, vous risquez de perdre des données.", - "addon.mod_quiz.confirmleavequizonerror": "Une erreur est survenu lors de l'enregistrement des réponses. Voulez-vous vraiment quitter le test ?", - "addon.mod_quiz.confirmstart": "Votre tentative aura une limite de temps de {{$a}}. Au moment où vous commencerez, le temps commencera à être décompté et ne pourra pas être mis en pause. Vous devrez terminer votre tentative avant la fin du compte à rebours. Voulez-vous commencer maintenant ?", - "addon.mod_quiz.confirmstartheader": "Limite de temps", - "addon.mod_quiz.connectionerror": "Connexion réseau perdue (échec de l'enregistrement automatique).\n\nVeuillez prendre note de toutes les réponses que vous avez saisies sur cette page au cours des quelques dernières minutes, puis essayez de vous reconnecter.\n\nUne fois la connexion rétablie, vos réponses devraient être enregistrées et ce message disparaîtra.", - "addon.mod_quiz.continueattemptquiz": "Continuer la dernière tentative", - "addon.mod_quiz.continuepreview": "Continuer la dernière prévisualisation", - "addon.mod_quiz.errorbehaviournotsupported": "Ce test ne peut être effectué dans l'app, car le comportement de question n'est pas supporté par l'app :", - "addon.mod_quiz.errordownloading": "Erreur lors du téléchargement des données requises.", - "addon.mod_quiz.errorgetattempt": "Erreur lors de l'obtention des données de tentative.", - "addon.mod_quiz.errorgetquestions": "Erreur lors de l'obtention des questions.", - "addon.mod_quiz.errorgetquiz": "Erreur lors de l'obtention des données du test.", - "addon.mod_quiz.errorparsequestions": "Une erreur est survenue lors de la lecture des questions. Veuillez faire ce test dans un navigateur web.", - "addon.mod_quiz.errorquestionsnotsupported": "Ce test ne peut être effectué dans l'app, car il ne contient que des questions qui ne sont pas supportées :", - "addon.mod_quiz.errorrulesnotsupported": "Ce test ne peut être effectué dans l'app, car il contient des règles d'accès qui ne sont pas supportées :", - "addon.mod_quiz.errorsaveattempt": "Une erreur est survenue lors de l'enregistrement des données de tentative.", - "addon.mod_quiz.feedback": "Feedback", - "addon.mod_quiz.finishattemptdots": "Terminer le test…", - "addon.mod_quiz.finishnotsynced": "Terminé, mais pas synchronisé", - "addon.mod_quiz.grade": "Note", - "addon.mod_quiz.gradeaverage": "Note moyenne", - "addon.mod_quiz.gradehighest": "Note la plus haute", - "addon.mod_quiz.grademethod": "Méthode d'évaluation", - "addon.mod_quiz.gradesofar": "{{$a.method}} : {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Points", - "addon.mod_quiz.modulenameplural": "Tests", - "addon.mod_quiz.mustbesubmittedby": "Cette tentative doit être envoyée avant {{$a}}.", - "addon.mod_quiz.noquestions": "Aucune question n'a été encore ajoutée", - "addon.mod_quiz.noreviewattempt": "Vous n'êtes pas autorisé à relire cette tentative.", - "addon.mod_quiz.notyetgraded": "Pas encore évalué", - "addon.mod_quiz.opentoc": "Ouvrir la navigation", - "addon.mod_quiz.outof": "{{$a.grade}} sur {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} sur {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Feedback global", - "addon.mod_quiz.overdue": "En retard", - "addon.mod_quiz.overduemustbesubmittedby": "Cette tentative est maintenant en retard et aurait déjà dû être envoyée. Si vous désirez que ce test soit évalué, vous devez l'envoyer jusqu'à {{$a}}. Si vous ne le faites pas, vous n'obtiendrez pas de note pour cette tentative.", - "addon.mod_quiz.preview": "Prévisualisation", - "addon.mod_quiz.previewquiznow": "Prévisualiser le test maintenant", - "addon.mod_quiz.question": "Libellé de la question", - "addon.mod_quiz.quiznavigation": "Navigation du test", - "addon.mod_quiz.quizpassword": "Mot de passe du test", - "addon.mod_quiz.reattemptquiz": "Effectuer de nouveau le test", - "addon.mod_quiz.requirepasswordmessage": "Vous devez connaître la clef du test pour pouvoir l'effectuer", - "addon.mod_quiz.returnattempt": "Retour à la tentative", - "addon.mod_quiz.review": "Relecture", - "addon.mod_quiz.reviewofattempt": "Relecture de la tentative {{$a}}", - "addon.mod_quiz.reviewofpreview": "Relecture de la prévisualisation", - "addon.mod_quiz.showall": "Afficher toutes les questions sur une page", - "addon.mod_quiz.showeachpage": "Afficher une page à la fois", - "addon.mod_quiz.startattempt": "Démarrer une tentative", - "addon.mod_quiz.startedon": "Commencé le", - "addon.mod_quiz.stateabandoned": "Jamais remis", - "addon.mod_quiz.statefinished": "Terminé", - "addon.mod_quiz.statefinisheddetails": "Remis {{$a}}", - "addon.mod_quiz.stateinprogress": "En cours", - "addon.mod_quiz.stateoverdue": "En retard", - "addon.mod_quiz.stateoverduedetails": "Doit être remis par {{$a}}", - "addon.mod_quiz.status": "État", - "addon.mod_quiz.submitallandfinish": "Tout envoyer et terminer", - "addon.mod_quiz.summaryofattempt": "Résumé de la tentative", - "addon.mod_quiz.summaryofattempts": "Résumé de vos tentatives précédentes", - "addon.mod_quiz.timeleft": "Temps restant", - "addon.mod_quiz.timetaken": "Temps mis", - "addon.mod_quiz.warningattemptfinished": "Tentative locale écartée, car elle a été terminée sur le site web ou n'a pas été trouvée.", - "addon.mod_quiz.warningdatadiscarded": "Certaines réponses faites hors connexion ont été écartées, car les questions ont été modifiées.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Tentative non terminée, car certaines réponses faites hors connexion ont été écartées. Veuillez revoir vos réponses et envoyer à nouveau votre tentative.", - "addon.mod_quiz.warningquestionsnotsupported": "Ce test contient des questions qui ne sont pas supportées par l'app :", - "addon.mod_quiz.yourfinalgradeis": "Votre note finale pour ce test est {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Erreur lors du chargement du contenu", - "addon.mod_resource.modifieddate": "Modifié {{$a}}", - "addon.mod_resource.modulenameplural": "Fichiers", - "addon.mod_resource.openthefile": "Ouvrir le fichier", - "addon.mod_resource.uploadeddate": "Déposé le {{$a}}", - "addon.mod_scorm.asset": "Élément", - "addon.mod_scorm.assetlaunched": "Élément - affiché", - "addon.mod_scorm.attempts": "tentatives", - "addon.mod_scorm.averageattempt": "Moyenne des tentatives", - "addon.mod_scorm.browse": "Prévisualiser", - "addon.mod_scorm.browsed": "Consulté", - "addon.mod_scorm.browsemode": "Mode prévisualisation", - "addon.mod_scorm.cannotcalculategrade": "La note n'a pas pu être calculée.", - "addon.mod_scorm.completed": "Terminé", - "addon.mod_scorm.contents": "Contenus", - "addon.mod_scorm.dataattemptshown": "Ces données appartiennent à la tentative numéro {{number}}", - "addon.mod_scorm.enter": "Entrer", - "addon.mod_scorm.errorcreateofflineattempt": "Une erreur est survenue lors de la création d'une tentative hors connexion. Veuillez réessayer.", - "addon.mod_scorm.errordownloadscorm": "Erreur de téléchargement SCORM : « {{name}} »", - "addon.mod_scorm.errorgetscorm": "Erreur de récupération des données SCORM.", - "addon.mod_scorm.errorinvalidversion": "Désolé, l'application ne supporte que SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Le téléchargement des paquetages SCORM est désactivé. Veuillez contacter l'administrateur de la plateforme.", - "addon.mod_scorm.errornovalidsco": "Ce paquetage SCORM n'a pas de SCO visible à charger.", - "addon.mod_scorm.errorpackagefile": "L'application ne supporte que les paquetages ZIP.", - "addon.mod_scorm.errorsyncscorm": "Une erreur est survenue lors de la synchronisation. Veuillez réessayer.", - "addon.mod_scorm.exceededmaxattempts": "Vous avez atteint le nombre maximum de tentatives.", - "addon.mod_scorm.failed": "Échec", - "addon.mod_scorm.firstattempt": "Première tentative", - "addon.mod_scorm.gradeaverage": "Note moyenne", - "addon.mod_scorm.gradeforattempt": "Note de la tentative", - "addon.mod_scorm.gradehighest": "Note la plus haute", - "addon.mod_scorm.grademethod": "Méthode d'évaluation", - "addon.mod_scorm.gradereported": "Note envoyée", - "addon.mod_scorm.gradescoes": "Objets d'apprentissage", - "addon.mod_scorm.gradesum": "Note totale", - "addon.mod_scorm.highestattempt": "Meilleure tentative", - "addon.mod_scorm.incomplete": "Incomplet", - "addon.mod_scorm.lastattempt": "Dernière tentative terminée", - "addon.mod_scorm.modulenameplural": "Paquetages SCORM", - "addon.mod_scorm.newattempt": "Commencer une nouvelle tentative", - "addon.mod_scorm.noattemptsallowed": "Nombre de tentatives permises", - "addon.mod_scorm.noattemptsmade": "Nombre de tentatives effectuées", - "addon.mod_scorm.notattempted": "Aucune tentative", - "addon.mod_scorm.offlineattemptnote": "Cette tentative comporte des données non synchronisées.", - "addon.mod_scorm.offlineattemptovermax": "Cette tentative ne peut pas être envoyée, car vous avez dépassé le nombre maximum de tentatives.", - "addon.mod_scorm.organizations": "Organisations", - "addon.mod_scorm.passed": "Réussi", - "addon.mod_scorm.reviewmode": "Mode relecture", - "addon.mod_scorm.score": "Résultat", - "addon.mod_scorm.scormstatusnotdownloaded": "Ce paquetage SCORM n'est pas téléchargé. Il sera téléchargé automatiquement dès que vous l'ouvrirez.", - "addon.mod_scorm.scormstatusoutdated": "Ce paquetage SCORM a été modifié depuis son dernier téléchargement. Il sera téléchargé automatiquement dès que vous l'ouvrirez.", - "addon.mod_scorm.suspended": "Suspendu", - "addon.mod_scorm.toc": "TOC", - "addon.mod_scorm.warningofflinedatadeleted": "Des données locales de la tentative {{number}} ont été supprimées, car elles n'ont pas pu être intégrées dans une nouvelle tentative.", - "addon.mod_scorm.warningsynconlineincomplete": "Certaines tentatives n'ont pas pu être synchronisées avec la plateforme, car la dernière tentative en ligne n'est pas terminée. Veuillez d'abord finir votre tentative en ligne.", - "addon.mod_survey.cannotsubmitsurvey": "Un problème est survenu lors de l'envoi de votre consultation. Veuillez essayer à nouveau.", - "addon.mod_survey.errorgetsurvey": "Erreur lors de l'obtention des données de la consultation.", - "addon.mod_survey.ifoundthat": "J'ai trouvé que", - "addon.mod_survey.ipreferthat": "J'aime mieux quand", - "addon.mod_survey.modulenameplural": "Consultations", - "addon.mod_survey.responses": "Réponses", - "addon.mod_survey.results": "Résultats", - "addon.mod_survey.surveycompletednograph": "Vous avez terminé ce sondage.", - "addon.mod_url.accessurl": "Accéder à l'URL", - "addon.mod_url.modulenameplural": "URLs", - "addon.mod_url.pointingtourl": "URL vers laquelle cette ressource dirige.", - "addon.mod_wiki.cannoteditpage": "Vous ne pouvez pas modifier cette page", - "addon.mod_wiki.createpage": "Créer une page", - "addon.mod_wiki.editingpage": "Modification de cette page « {{$a}} »", - "addon.mod_wiki.errorloadingpage": "Une erreur est survenue lors du chargement de la page.", - "addon.mod_wiki.errornowikiavailable": "Ce wiki n'a pas encore de contenu.", - "addon.mod_wiki.gowikihome": "Vers la première page du wiki", - "addon.mod_wiki.map": "Carte", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Nouvelle page", - "addon.mod_wiki.newpagetitle": "Titre de la nouvelle page", - "addon.mod_wiki.nocontent": "Cette page n'a pas de contenu", - "addon.mod_wiki.notingroup": "Pas dans le groupe", - "addon.mod_wiki.pageexists": "Cette page existe déjà.", - "addon.mod_wiki.pagename": "Nom de page", - "addon.mod_wiki.subwiki": "Sous-wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Pages wiki", - "addon.mod_wiki.titleshouldnotbeempty": "Le titre ne doit pas être vide", - "addon.mod_wiki.viewpage": "Consulter la page", - "addon.mod_wiki.wikipage": "Page wiki", - "addon.mod_wiki.wrongversionlock": "Un autre utilisateur a modifié cette page pendant que vous la modifiiez. Vos modifications sont obsolètes.", - "addon.mod_workshop.alreadygraded": "Déjà noté", - "addon.mod_workshop.areainstructauthors": "Instructions pour la remise des travaux", - "addon.mod_workshop.areainstructreviewers": "Instructions pour l'évaluation des travaux", - "addon.mod_workshop.assess": "Évaluer", - "addon.mod_workshop.assessedsubmission": "Travail évalué", - "addon.mod_workshop.assessmentform": "Formulaire d'évaluation", - "addon.mod_workshop.assessmentsettings": "Modalités d'évaluation", - "addon.mod_workshop.assessmentstrategynotsupported": "Stratégie d'évaluation {{$a}} non supportée", - "addon.mod_workshop.assessmentweight": "Pondération de l'évaluation", - "addon.mod_workshop.assignedassessments": "Travaux à évaluer", - "addon.mod_workshop.assignedassessmentsnone": "Vous n'avez pas de travail à évaluer", - "addon.mod_workshop.conclusion": "Conclusion", - "addon.mod_workshop.createsubmission": "Ajouter un travail", - "addon.mod_workshop.deletesubmission": "Supprimer le travail remis", - "addon.mod_workshop.editsubmission": "Modifier le travail remis", - "addon.mod_workshop.feedbackauthor": "Feedback pour l'auteur", - "addon.mod_workshop.feedbackby": "Feedback de {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Feedback pour l'évaluateur", - "addon.mod_workshop.givengrades": "Notes données", - "addon.mod_workshop.gradecalculated": "Note calculée pour le travail remis", - "addon.mod_workshop.gradeinfo": "Note : {{$a.received}} sur {{$a.max}}", - "addon.mod_workshop.gradeover": "Modifier la note du travail remis", - "addon.mod_workshop.gradesreport": "Rapport d'évaluation de l'atelier", - "addon.mod_workshop.gradinggrade": "Note du processus d'évaluation", - "addon.mod_workshop.gradinggradecalculated": "Note calculée pour l'évaluation", - "addon.mod_workshop.gradinggradeof": "Note pour l'évaluation (sur {{$a}})", - "addon.mod_workshop.gradinggradeover": "Modifier la note de l'évaluation", - "addon.mod_workshop.modulenameplural": "Ateliers", - "addon.mod_workshop.nogradeyet": "Pas encore de note", - "addon.mod_workshop.notassessed": "Pas encore évalué", - "addon.mod_workshop.notoverridden": "Pas modifié", - "addon.mod_workshop.noyoursubmission": "Vous n'avez pas encore remis votre travail", - "addon.mod_workshop.overallfeedback": "Feedback général", - "addon.mod_workshop.publishedsubmissions": "Travaux remis publiés", - "addon.mod_workshop.publishsubmission": "Publier travail remis", - "addon.mod_workshop.publishsubmission_help": "Les travaux remis publiés sont disponibles pour les autres dès que l'atelier est terminé.", - "addon.mod_workshop.reassess": "Ré-évaluer", - "addon.mod_workshop.receivedgrades": "Notes reçues", - "addon.mod_workshop.submissionattachment": "Annexe", - "addon.mod_workshop.submissioncontent": "Contenu du travail remis", - "addon.mod_workshop.submissiondeleteconfirm": "Voulez-vous vraiment supprimer le travail suivant ?", - "addon.mod_workshop.submissiongrade": "Note pour le travail remis", - "addon.mod_workshop.submissiongradeof": "Note pour le travail remis (sur {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Veuillez saisir du texte ou ajouter un fichier.", - "addon.mod_workshop.submissionrequiredtitle": "Veuillez indiquer un titre.", - "addon.mod_workshop.submissionsreport": "Rapport de remise de l'atelier", - "addon.mod_workshop.submissiontitle": "Titre", - "addon.mod_workshop.switchphase10": "Passer à la phase de configuration", - "addon.mod_workshop.switchphase20": "Passer à la phase de remise des travaux", - "addon.mod_workshop.switchphase30": "Passer à la phase d'évaluation", - "addon.mod_workshop.switchphase40": "Passer à la phase de notation des évaluations", - "addon.mod_workshop.switchphase50": "Terminer l'atelier", - "addon.mod_workshop.userplan": "Planning de l'atelier", - "addon.mod_workshop.userplancurrentphase": "Phase actuelle", - "addon.mod_workshop.warningassessmentmodified": "Le travail remis a été modifié sur la plateforme.", - "addon.mod_workshop.warningsubmissionmodified": "L'évaluation a été modifiée sur la plateforme.", - "addon.mod_workshop.weightinfo": "Pondération : {{$a}}", - "addon.mod_workshop.yourassessment": "Votre évaluation", - "addon.mod_workshop.yourassessmentfor": "Votre évaluation pour {{$a}}", - "addon.mod_workshop.yourgrades": "Vos notes", - "addon.mod_workshop.yoursubmission": "Votre travail remis", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Commentaire pour {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Note pour {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspect {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Vous devez sélectionner une note pour cet aspect", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Commentaire pour {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspect {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Commentaire pour {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Note pour {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Assertion {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Critère {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Vous devez sélectionner un de ces éléments", - "addon.notes.addnewnote": "Ajouter une annotation", - "addon.notes.coursenotes": "Annotations de cours", - "addon.notes.deleteconfirm": "Supprimer cette annotation ?", - "addon.notes.eventnotecreated": "Annotation créée", - "addon.notes.eventnotedeleted": "Annotation supprimée", - "addon.notes.nonotes": "Il n'y a pas encore d'annotations de ce type.", - "addon.notes.note": "Annotation", - "addon.notes.notes": "Annotations", - "addon.notes.personalnotes": "Annotations personnelles", - "addon.notes.publishstate": "Contexte", - "addon.notes.sitenotes": "Annotations de site", - "addon.notes.userwithid": "Utilisateur d'ID {{id}}", - "addon.notes.warningnotenotsent": "Impossible d'ajouter d'annotation au cours {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Erreur lors de la récupération des notifications.", - "addon.notifications.markallread": "Tout marquer comme lu", - "addon.notifications.notificationpreferences": "Préférences de notification", - "addon.notifications.notifications": "Notifications", - "addon.notifications.playsound": "Jouer le son", - "addon.notifications.therearentnotificationsyet": "Aucune notification.", - "addon.storagemanager.deletecourse": "Enlever de l'appareil toutes les données du cours", - "addon.storagemanager.deletedatafrom": "Enlever de l'appareil les données de {{name}}", - "addon.storagemanager.info": "Le stockage de fichiers sur votre appareil rend le travail plus rapide et permet d'utiliser l'app hors ligne. Vous pouvez enlever des fichiers en toute sécurité pour libérer de la place de stockage.", - "addon.storagemanager.managestorage": "Gérer le stockage", - "addon.storagemanager.storageused": "Stockage utilisé :", - "assets.countries.AD": "Andorre", - "assets.countries.AE": "Émirats arabes unis", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua-et-Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albanie", - "assets.countries.AM": "Arménie", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarctique", - "assets.countries.AR": "Argentine", - "assets.countries.AS": "Samoas américaines", - "assets.countries.AT": "Autriche", - "assets.countries.AU": "Australie", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Îles d'Åland", - "assets.countries.AZ": "Azerbaïdjan", - "assets.countries.BA": "Bosnie-Herzégovine", - "assets.countries.BB": "Barbade", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgique", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgarie", - "assets.countries.BH": "Bahreïn", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Bénin", - "assets.countries.BL": "Saint-Barthélemy", - "assets.countries.BM": "Bermudes", - "assets.countries.BN": "Brunéi Darussalam", - "assets.countries.BO": "Bolivie (État plurinational de)", - "assets.countries.BQ": "Bonaire, Saint-Eustache et Saba", - "assets.countries.BR": "Brésil", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhoutan", - "assets.countries.BV": "Île Bouvet", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Bélarus", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canada", - "assets.countries.CC": "Îles Cocos (Keeling)", - "assets.countries.CD": "Congo (République Démocratique du)", - "assets.countries.CF": "République centrafricaine", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Suisse", - "assets.countries.CI": "Côte d'Ivoire", - "assets.countries.CK": "Îles Cook", - "assets.countries.CL": "Chili", - "assets.countries.CM": "Cameroun", - "assets.countries.CN": "Chine", - "assets.countries.CO": "Colombie", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Cap-Vert", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Île Christmas", - "assets.countries.CY": "Chypre", - "assets.countries.CZ": "Tchéquie", - "assets.countries.DE": "Allemagne", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Danemark", - "assets.countries.DM": "Dominique", - "assets.countries.DO": "République Dominicaine", - "assets.countries.DZ": "Algérie", - "assets.countries.EC": "Équateur", - "assets.countries.EE": "Estonie", - "assets.countries.EG": "Égypte", - "assets.countries.EH": "Sahara occidental", - "assets.countries.ER": "Érythrée", - "assets.countries.ES": "Espagne", - "assets.countries.ET": "Éthiopie", - "assets.countries.FI": "Finlande", - "assets.countries.FJ": "Fidji", - "assets.countries.FK": "Îles Falkland (Malvinas)", - "assets.countries.FM": "Micronésie (États fédérés de)", - "assets.countries.FO": "Îles Féroé", - "assets.countries.FR": "France", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Royaume-Uni", - "assets.countries.GD": "Grenade", - "assets.countries.GE": "Géorgie", - "assets.countries.GF": "Guyane française", - "assets.countries.GG": "Guernesey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Groenland", - "assets.countries.GM": "Gambie", - "assets.countries.GN": "Guinée", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Guinée équatoriale", - "assets.countries.GR": "Grèce", - "assets.countries.GS": "Géorgie du Sud-et-les Îles Sandwich du Sud", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinée-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong-kong", - "assets.countries.HM": "Île Heard-et-Îles MacDonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Croatie", - "assets.countries.HT": "Haïti", - "assets.countries.HU": "Hongrie", - "assets.countries.ID": "Indonésie", - "assets.countries.IE": "Irlande", - "assets.countries.IL": "Israël", - "assets.countries.IM": "Île de Man", - "assets.countries.IN": "Inde", - "assets.countries.IO": "Territoire britannique de l'Océan Indien", - "assets.countries.IQ": "Iraq", - "assets.countries.IR": "Iran (République islamique d')", - "assets.countries.IS": "Islande", - "assets.countries.IT": "Italie", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaïque", - "assets.countries.JO": "Jordanie", - "assets.countries.JP": "Japon", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirghizistan", - "assets.countries.KH": "Cambodge", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comores", - "assets.countries.KN": "Saint-Kitts-et-Nevis", - "assets.countries.KP": "Corée (République populaire démocratique de)", - "assets.countries.KR": "Corée (République de)", - "assets.countries.KW": "Koweït", - "assets.countries.KY": "Îles Caïmanes", - "assets.countries.KZ": "Kazakhstan", - "assets.countries.LA": "République démocratique populaire Lao", - "assets.countries.LB": "Liban", - "assets.countries.LC": "Sainte-Lucie", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Libéria", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lituanie", - "assets.countries.LU": "Luxembourg", - "assets.countries.LV": "Lettonie", - "assets.countries.LY": "Libye", - "assets.countries.MA": "Maroc", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldova (République de)", - "assets.countries.ME": "Monténégro", - "assets.countries.MF": "Saint-Martin (partie française)", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Îles Marshall", - "assets.countries.MK": "Macédoine du Nord", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolie", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Îles Mariannes septentrionales", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauritanie", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malte", - "assets.countries.MU": "Maurice", - "assets.countries.MV": "Maldives", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexique", - "assets.countries.MY": "Malaisie", - "assets.countries.MZ": "Mozambique", - "assets.countries.NA": "Namibie", - "assets.countries.NC": "Nouvelle-Calédonie", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Île Norfolk", - "assets.countries.NG": "Nigéria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Pays-Bas", - "assets.countries.NO": "Norvège", - "assets.countries.NP": "Népal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niué", - "assets.countries.NZ": "Nouvelle-Zélande", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Pérou", - "assets.countries.PF": "Polynésie française", - "assets.countries.PG": "Papouasie-Nouvelle-Guinée", - "assets.countries.PH": "Philippines", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Pologne", - "assets.countries.PM": "Saint-Pierre-et-Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Porto Rico", - "assets.countries.PS": "Palestine, État de", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palaos", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Roumanie", - "assets.countries.RS": "Serbie", - "assets.countries.RU": "Fédération de Russie", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Arabie saoudite", - "assets.countries.SB": "Îles Salomon", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Soudan", - "assets.countries.SE": "Suède", - "assets.countries.SG": "Singapour", - "assets.countries.SH": "Sainte-Hélène, Ascension et Tristan da Cunha", - "assets.countries.SI": "Slovénie", - "assets.countries.SJ": "Svalbard et l'Île Jan Mayen", - "assets.countries.SK": "Slovaquie", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "Saint-Marino", - "assets.countries.SN": "Sénégal", - "assets.countries.SO": "Somalie", - "assets.countries.SR": "Suriname", - "assets.countries.SS": "Soudan du Sud", - "assets.countries.ST": "Sao Tomé-et-Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Saint-Martin (partie néerlandaise)", - "assets.countries.SY": "République arabe syrienne", - "assets.countries.SZ": "Eswatini", - "assets.countries.TC": "Îles Turks-et-Caïcos", - "assets.countries.TD": "Tchad", - "assets.countries.TF": "Terres australes françaises", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thaïlande", - "assets.countries.TJ": "Tadjikistan", - "assets.countries.TK": "Tokélaou", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkménistan", - "assets.countries.TN": "Tunisie", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turquie", - "assets.countries.TT": "Trinité-et-Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taïwan", - "assets.countries.TZ": "Tanzanie (République-Unie de)", - "assets.countries.UA": "Ukraine", - "assets.countries.UG": "Ouganda", - "assets.countries.UM": "Îles mineures éloignées des États-Unis", - "assets.countries.US": "États-Unis", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Ouzbékistan", - "assets.countries.VA": "Saint-Siège", - "assets.countries.VC": "Saint-Vincent-et-les Grenadines", - "assets.countries.VE": "Venezuela (République bolivarienne du)", - "assets.countries.VG": "Îles Vierges britanniques", - "assets.countries.VI": "Îles Vierges des États-Unis", - "assets.countries.VN": "Viet Nam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis-et-Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yémen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Afrique du Sud", - "assets.countries.ZM": "Zambie", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "Ebook EPUB", - "assets.mimetypes.application/msword": "Document Word", - "assets.mimetypes.application/pdf": "Document PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Sauvegarde Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Feuille de calcul Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Fichiers Excel 2007 avec macros", - "assets.mimetypes.application/vnd.ms-powerpoint": "Présentation Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Feuille de calcul OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Modèle de feuille de calcul OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Document texte OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Modèle de texte OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Modèle de page web OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Présentation Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Diaporama Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Feuille de calcul Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Modèle Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Document Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Présentation iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Feuille de calcul iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Document iWork Pages", - "assets.mimetypes.application/x-javascript": "Source JavaScript", - "assets.mimetypes.application/x-mspublisher": "Documents Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animation flash", - "assets.mimetypes.application/xhtml_xml": "Document XHTML", - "assets.mimetypes.archive": "Archive ({{$a.EXT}})", - "assets.mimetypes.audio": "Fichier audio ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Fichier", - "assets.mimetypes.group:archive": "Fichiers compressés", - "assets.mimetypes.group:audio": "Fichiers audio", - "assets.mimetypes.group:document": "Fichiers document", - "assets.mimetypes.group:html_audio": "Fichiers audio supportés nativement par les navigateurs", - "assets.mimetypes.group:html_track": "Fichiers de piste HTML", - "assets.mimetypes.group:html_video": "Fichiers vidéo supportés nativement par les navigateurs", - "assets.mimetypes.group:image": "Fichiers image", - "assets.mimetypes.group:presentation": "Fichiers de présentation", - "assets.mimetypes.group:sourcecode": "Code source", - "assets.mimetypes.group:spreadsheet": "Fichiers feuille de calcul", - "assets.mimetypes.group:video": "Fichiers vidéo", - "assets.mimetypes.group:web_audio": "Fichiers audio utilisés sur le web", - "assets.mimetypes.group:web_file": "Fichiers web", - "assets.mimetypes.group:web_image": "Fichiers image utilisés sur le web", - "assets.mimetypes.group:web_video": "Fichiers vidéo utilisés sur le web", - "assets.mimetypes.image": "Image ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Icone Windows", - "assets.mimetypes.text/css": "Feuille de styles cascadée", - "assets.mimetypes.text/csv": "Valeurs séparées par des virgules", - "assets.mimetypes.text/html": "Document HTML", - "assets.mimetypes.text/plain": "Fichier texte", - "assets.mimetypes.text/rtf": "Document RTF", - "assets.mimetypes.text/vtt": "Piste de texte du la vidéo web", - "assets.mimetypes.video": "Fichier vidéo ({{$a.EXT}})", - "core.accounts": "Comptes", - "core.add": "Ajouter", - "core.agelocationverification": "Vérification de l'âge et du lieu", - "core.ago": "Il y a {{$a}}", - "core.all": "Tout", - "core.allgroups": "Tous les groupes", - "core.allparticipants": "Tous les participants", - "core.answer": "Réponse", - "core.answered": "Répondu", - "core.areyousure": "En êtes-vous bien sûr ?", - "core.back": "Retour", - "core.block.blocks": "Blocs", - "core.browser": "Navigateur", - "core.cancel": "Annuler", - "core.cannotconnect": "Connexion impossible", - "core.cannotconnecttrouble": "Il y a des difficultés pour vous connecter à votre site.", - "core.cannotconnectverify": "Veuillez vérifier si l'adresse est correcte.", - "core.cannotdownloadfiles": "Le téléchargement de fichiers est désactivé. Veuillez contacter l'administrateur de la plateforme.", - "core.captureaudio": "Enregistrer un son", - "core.capturedimage": "Photo prise", - "core.captureimage": "Prendre une photo", - "core.capturevideo": "Enregistrer une vidéo", - "core.category": "Catégorie", - "core.choose": "Choisir", - "core.choosedots": "Choisir…", - "core.clearsearch": "Effacer la recherche", - "core.clicktohideshow": "Cliquer pour déplier ou replier", - "core.clicktoseefull": "Cliquer pour voir tout le contenu.", - "core.close": "Fermer", - "core.comments": "Commentaires", - "core.comments.addcomment": "Ajouter un commentaire…", - "core.comments.comments": "Commentaires", - "core.comments.commentscount": "Commentaires ({{$a}})", - "core.comments.commentsnotworking": "Les commentaires ne peuvent pas être récupérés", - "core.comments.deletecommentbyon": "Supprimer le commentaire de {{$a.user}} écrit le {{$a.time}}", - "core.comments.eventcommentcreated": "Commentaire créé", - "core.comments.eventcommentdeleted": "Commentaire supprimé", - "core.comments.nocomments": "Aucun commentaire", - "core.comments.savecomment": "Enregistrer le commentaire", - "core.comments.warningcommentsnotsent": "Impossible de synchroniser les commentaires. {{error}}", - "core.commentscount": "Commentaires ({{$a}})", - "core.completion-alt-auto-fail": "Terminé : {{$a}} (n'a pas atteint la note pour passer)", - "core.completion-alt-auto-n": "Non terminé : {{$a}}", - "core.completion-alt-auto-n-override": "Non terminé : {{$a.modname}} (défini par {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Terminé : {{$a}} (a atteint la note pour passer)", - "core.completion-alt-auto-y": "Terminé : {{$a}}", - "core.completion-alt-auto-y-override": "Terminé : {{$a.modname}} (défini par {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Non terminé : {{$a}}. Sélectionner pour marquer comme terminé.", - "core.completion-alt-manual-n-override": "Non terminé : {{$a.modname}} (défini par {{$a.overrideuser}}). Sélectionner pour marquer comme terminé.", - "core.completion-alt-manual-y": "Terminé : {{$a}}. Sélectionner pour marquer comme non terminé.", - "core.completion-alt-manual-y-override": "Terminé : {{$a.modname}} (défini par {{$a.overrideuser}}). Sélectionner pour marquer comme non terminé.", - "core.confirmcanceledit": "Voulez-vous vraiment quitter cette page ? Toutes vos modifications seront perdues.", - "core.confirmdeletefile": "Voulez-vous vraiment supprimer ce fichier ?", - "core.confirmgotabroot": "Voulez-vous vraiment revenir à {{name}} ?", - "core.confirmgotabrootdefault": "Voulez-vous vraiment aller sur la page initiale de l'onglet actuel ?", - "core.confirmleaveunknownchanges": "Voulez-vous vraiment quitter cette page ? Les modifications non enregistrées seront perdues.", - "core.confirmloss": "Vraiment ? Toutes les modifications seront perdues.", - "core.confirmopeninbrowser": "Voulez-vous l'ouvrir dans un navigateur ?", - "core.considereddigitalminor": "Vous êtes trop jeune pour créer un compte sur ce site.", - "core.content": "Contenu", - "core.contenteditingsynced": "Le contenu que vous modifiez a été synchronisé.", - "core.contentlinks.chooseaccount": "Sélectionner un compte", - "core.contentlinks.chooseaccounttoopenlink": "Sélectionner un compte avec lequel ouvrir le lien.", - "core.contentlinks.confirmurlothersite": "Ce lien provient d'un autre site. Voulez-vous l'ouvrir ?", - "core.contentlinks.errornoactions": "Impossible de trouver une action à effectuer avec ce lien.", - "core.contentlinks.errornosites": "Impossible de trouver un site pour traiter ce lien.", - "core.contentlinks.errorredirectothersite": "L'URL de redirection ne peut pas pointer vers un site différent.", - "core.continue": "Continuer", - "core.copiedtoclipboard": "Texte copié dans le presse-papier", - "core.copytoclipboard": "Copier dans le presse-papiers", - "core.course": "Cours", - "core.course.activitydisabled": "Votre institution a désactivé cette activité dans l'App mobile.", - "core.course.activitynotyetviewableremoteaddon": "Votre institution a installé un plugin qui n'est pas encore supporté.", - "core.course.activitynotyetviewablesiteupgradeneeded": "L'installation Moodle de votre institution doit être mise à jour.", - "core.course.allsections": "Toutes les sections", - "core.course.askadmintosupport": "Veuillez contacter l'administrateur de la plateforme et l'informer que vous désirez utiliser cette activité avec l'app mobile Moodle.", - "core.course.availablespace": "Vous avez actuellement environ {{available}} de place disponible.", - "core.course.cannotdeletewhiledownloading": "Les fichiers ne peuvent pas être supprimées lorsque l'activité est en téléchargement. Veuillez attendre la fin du téléchargement pour terminer.", - "core.course.confirmdeletemodulefiles": "Voulez-vous vraiment supprimer ces fichiers de module ?", - "core.course.confirmdownload": "Vous allez télécharger {{size}}. {{availableSpace}} Voulez-vous vraiment continuer ?", - "core.course.confirmdownloadunknownsize": "Il n'a pas été possible de calculer la taille du téléchargement. {{availableSpace}} Voulez-vous vraiment continuer ?", - "core.course.confirmdownloadzerosize": "Vous allez télécharger{{availableSpace}} Voulez-vous vraiment continuer ?", - "core.course.confirmlimiteddownload": "Vous n'êtes actuellement pas connecté au Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Vous allez télécharger au moins {{size}}. {{availableSpace}} Voulez-vous vraiment continuer ?", - "core.course.contents": "Contenus", - "core.course.couldnotloadsectioncontent": "Impossible de charger le contenu de la section. Veuillez essayer plus tard.", - "core.course.couldnotloadsections": "Impossible de charger les sections. Veuillez essayer plus tard.", - "core.course.coursesummary": "Résumé du cours", - "core.course.downloadcourse": "Télécharger le cours", - "core.course.errordownloadingcourse": "Erreur lors du téléchargement du cours.", - "core.course.errordownloadingsection": "Erreur lors du téléchargement de la section.", - "core.course.errorgetmodule": "Erreur lors de l'obtention des données du module.", - "core.course.hiddenfromstudents": "Caché pour les étudiants", - "core.course.hiddenoncoursepage": "Disponible, mais pas affiché sur la page de cours", - "core.course.insufficientavailablequota": "Votre appareil n'a pas pu faire de la place pour enregistrer ce téléchargement. Il est possible qu'il réserve de la place pour des mise à jour d'apps et du système. Veuillez libérer de la place de stockage.", - "core.course.insufficientavailablespace": "Vous essayez de télécharger {{size}}. Il ne restera alors plus assez de place sur votre appareil pour qu'il fonctionne correctement. Veuillez libérer de la place de stockage.", - "core.course.manualcompletionnotsynced": "Achèvement manuel non synchronisé.", - "core.course.nocontentavailable": "Aucun contenu disponible actuellement.", - "core.course.overriddennotice": "Votre note finale pour cette activité a été ajustée manuellement.", - "core.course.refreshcourse": "Actualiser le cours", - "core.course.sections": "Sections", - "core.course.useactivityonbrowser": "Vous pouvez continuer à l'utiliser avec le navigateur de votre appareil.", - "core.course.warningmanualcompletionmodified": "L'achèvement manuel d'une activité a été modifié sur le site.", - "core.course.warningofflinemanualcompletiondeleted": "Certains achèvement manuels hors ligne du cours « {{name}} » ont été supprimés. {{error}}", - "core.coursedetails": "Informations détaillées du cours", - "core.coursenogroups": "Vous n'êtes membre d'aucun groupe dans ce cours.", - "core.courses.addtofavourites": "Marquer comme favori", - "core.courses.allowguests": "Les visiteurs anonymes ont accès à ce cours", - "core.courses.availablecourses": "Cours disponibles", - "core.courses.cannotretrievemorecategories": "Les catégories de niveau plus grand que {{$a}} ne peuvent pas être chargées.", - "core.courses.categories": "Catégories de cours", - "core.courses.confirmselfenrol": "Voulez-vous vraiment vous inscrire dans ce cours ?", - "core.courses.courses": "Cours", - "core.courses.downloadcourses": "Télécharger les cours", - "core.courses.enrolme": "M'inscrire", - "core.courses.errorloadcategories": "Une erreur est survenue lors du chargement des catégories.", - "core.courses.errorloadcourses": "Une erreur est survenue lors du chargement des cours.", - "core.courses.errorloadplugins": "Les plugins requis par ce cours n'ont pas pu être chargés correctement. Veuillez recharger l'app et essayer à nouveau.", - "core.courses.errorsearching": "Une erreur est survenue durant la recherche.", - "core.courses.errorselfenrol": "Une erreur est survenue durant l'auto-inscription.", - "core.courses.filtermycourses": "Filtrer mes cours", - "core.courses.frontpage": "Page d'accueil", - "core.courses.hidecourse": "Retirer de l'affichage", - "core.courses.ignore": "Ignorer", - "core.courses.mycourses": "Mes cours", - "core.courses.mymoodle": "Tableau de bord", - "core.courses.nocourses": "Aucune information de cours à afficher.", - "core.courses.nocoursesyet": "Il n'y a pas encore de cours", - "core.courses.nosearchresults": "Aucun résultat", - "core.courses.notenroled": "Vous n'êtes pas inscrit à ce cours", - "core.courses.notenrollable": "Vous ne pouvez pas vous inscrire vous-même à ce cours.", - "core.courses.password": "Clef d'inscription", - "core.courses.paymentrequired": "Un paiement est requis pour accéder à ce cours.", - "core.courses.paypalaccepted": "Paiements par PayPal acceptés", - "core.courses.reload": "Actualiser", - "core.courses.removefromfavourites": "Retirer ce cours des favoris", - "core.courses.search": "Rechercher", - "core.courses.searchcourses": "Rechercher des cours", - "core.courses.searchcoursesadvice": "Veuillez utiliser le bouton de recherche de cours pour accéder anonymement à des cours ou vous inscrire vous-même à des cours qui le permettent.", - "core.courses.selfenrolment": "Auto-inscription", - "core.courses.sendpaymentbutton": "Envoyer un paiement avec PayPal", - "core.courses.show": "Montrer dans l'affichage", - "core.courses.totalcoursesearchresults": "Nombre de cours : {{$a}}", - "core.currentdevice": "Appareil actuel", - "core.datastoredoffline": "Données stockées sur l'appareil, car elles n'ont pas pu être envoyées. Elles seront automatiquement envoyées ultérieurement.", - "core.date": "Date", - "core.day": "jour", - "core.days": "jours", - "core.decsep": ",", - "core.defaultvalue": "Défaut ({{$a}})", - "core.delete": "Supprimer", - "core.deletedoffline": "Supprimé en local", - "core.deleteduser": "Utilisateur supprimé", - "core.deleting": "Suppression", - "core.description": "Description", - "core.desktop": "Bureau", - "core.dfdaymonthyear": "DD-MM-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "hh[:]mm", - "core.digitalminor": "Mineur numérique", - "core.digitalminor_desc": "Veuillez demander à votre représentant légal de contacter :", - "core.discard": "Ignorer", - "core.dismiss": "Rejeter", - "core.displayoptions": "Options d'affichage", - "core.done": "Terminé", - "core.download": "Télécharger", - "core.downloaded": "Téléchargé", - "core.downloading": "Téléchargement en cours", - "core.edit": "Modifier", - "core.editor.autosavesucceeded": "Brouillon enregistré", - "core.editor.bold": "Gras", - "core.editor.clear": "Supprimer le format", - "core.editor.h3": "Titre (grand)", - "core.editor.h4": "Titre (moyen)", - "core.editor.h5": "Titre (petit)", - "core.editor.hidetoolbar": "Cacher la barre d'outils§", - "core.editor.italic": "Italique", - "core.editor.orderedlist": "Liste numérotée", - "core.editor.p": "Paragraphe", - "core.editor.strike": "Barré", - "core.editor.textrecovered": "Un brouillon de ce texte a été récupéré automatiquement.", - "core.editor.toggle": "Activer/désactiver l'éditeur", - "core.editor.underline": "Souligné", - "core.editor.unorderedlist": "Liste non numérotée", - "core.emptysplit": "Cette page paraîtra vide si le panneau de gauche est vide ou en cours de chargement.", - "core.error": "Erreur", - "core.errorchangecompletion": "Une erreur est survenue lors du changement de l'état d'achèvement. Veuillez essayer à nouveau.", - "core.errordeletefile": "Erreur lors de la suppression du fichier. Veuillez essayer à nouveau.", - "core.errordownloading": "Erreur lors du téléchargement du fichier.", - "core.errordownloadingsomefiles": "Erreur lors du téléchargement de fichiers. Certains fichiers peuvent être manquants.", - "core.errorfileexistssamename": "Un fichier de même nom est déjà présent.", - "core.errorinvalidform": "Le formulaire comporte des données non valides. Veuillez vous assurer que tous les champs requis sont renseignés et que les données sont valides.", - "core.errorinvalidresponse": "Réponse reçue non valide. Veuillez contacter l'administrateur de votre plateforme si l'erreur persiste.", - "core.errorloadingcontent": "Erreur lors du chargement du contenu.", - "core.errorofflinedisabled": "La consultation hors ligne est désactivée sur votre site. Vous devez vous connecter à Internet pour utiliser l'app.", - "core.erroropenfilenoapp": "Erreur lors de l'ouverture du fichier : aucune app trouvée pour ouvrir ce type de fichier.", - "core.erroropenfilenoextension": "Erreur lors de l'ouverture du fichier : le nom du fichier n'a pas d'extension.", - "core.erroropenpopup": "Cette activité essaie d'ouvrir dans une fenêtre surgissante. Ceci n'est pas supporté dans l'app.", - "core.errorrenamefile": "Erreur lors du renommage du fichier. Veuillez essayer à nouveau.", - "core.errorsomedatanotdownloaded": "Si vous avez téléchargé cette activité, veuillez prendre note que certaines données ne sont pas téléchargées pour des raisons de performance et d'utilisation de données.", - "core.errorsync": "Une erreur est survenue lors de la synchronisation. Veuillez essayer plus tard.", - "core.errorsyncblocked": "Ce {{$a}} ne peut pas être synchronisé maintenant en raison d'une tâche en cours. Veuillez essayer plus tard. Si le problème persiste, veuillez relancer l'app.", - "core.explanationdigitalminor": "Cette information est requise pour déterminer si vous avez l'âge de la majorité numérique. Cet âge est celui qu'une personne doit avoir pour pouvoir donner son consentement à l'accès et au stockage par un tiers des données personnelles la concernant.", - "core.favourites": "Favori", - "core.filename": "Nom de fichier", - "core.filenameexist": "Le nom de fichier existe déjà : {{$a}}", - "core.filenotfound": "Fichier introuvable.", - "core.fileuploader.addfiletext": "Ajouter un fichier", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Caméra", - "core.fileuploader.confirmuploadfile": "Vous allez déposer {{size}}. Voulez-vous vraiment continuer ?", - "core.fileuploader.confirmuploadunknownsize": "Impossible de calculer la taille du fichier à dépasser. Voulez-vous malgré tout continuer ?", - "core.fileuploader.errorcapturingaudio": "Erreur lors de l'enregistrement audio.", - "core.fileuploader.errorcapturingimage": "Erreur lors de la capture de l'image.", - "core.fileuploader.errorcapturingvideo": "Erreur lors de la capture vidéo.", - "core.fileuploader.errorgettingimagealbum": "Erreur lors de la récupération de l'image de l'album.", - "core.fileuploader.errormustbeonlinetoupload": "Vous devez être en ligne pour déposer des fichiers.", - "core.fileuploader.errornoapp": "Vous n'avez pas d'app installée pour effectuer cette action.", - "core.fileuploader.errorreadingfile": "Erreur de lecture du fichier.", - "core.fileuploader.errorwhileuploading": "Une erreur est survenue lors du dépôt du fichier.", - "core.fileuploader.file": "Fichier", - "core.fileuploader.filesofthesetypes": "Types de fichier acceptés :", - "core.fileuploader.fileuploaded": "Le fichier a été déposé.", - "core.fileuploader.invalidfiletype": "Le type de fichier {{$a}} ne peut pas être accepté.", - "core.fileuploader.maxbytesfile": "Le fichier {{$a.file}} est trop gros. La taille maximale permise est de {{$a.size}}.", - "core.fileuploader.more": "Plus", - "core.fileuploader.photoalbums": "Albums photos", - "core.fileuploader.readingfile": "Lecture du fichier", - "core.fileuploader.readingfileperc": "Lecture du fichier : {{$a}} %", - "core.fileuploader.selectafile": "Choisir un fichier", - "core.fileuploader.uploadafile": "Déposer un fichier", - "core.fileuploader.uploading": "Envoi", - "core.fileuploader.uploadingperc": "Dépôt en cours : {{$a}} %", - "core.fileuploader.video": "Vidéo", - "core.filter": "Filtre", - "core.folder": "Dossier", - "core.forcepasswordchangenotice": "Vous devez changer votre mot de passe pour continuer.", - "core.fulllistofcourses": "Tous les cours", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Moyenne", - "core.grades.badgrade": "La note fournie n'est pas valide", - "core.grades.contributiontocoursetotal": "Contribution au total du cours", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Note", - "core.grades.gradeitem": "Élément d'évaluation", - "core.grades.grades": "Notes", - "core.grades.lettergrade": "Note lettre", - "core.grades.nogradesreturned": "Aucune note retournée", - "core.grades.nooutcome": "Aucun objectif", - "core.grades.percentage": "Pourcentage", - "core.grades.range": "Valeurs possibles", - "core.grades.rank": "Rang", - "core.grades.weight": "Pondération", - "core.group": "Groupe", - "core.groupsseparate": "Groupes séparés", - "core.groupsvisible": "Groupes visibles", - "core.h5p.additionallicenseinfo": "Toute information supplémentaire sur la licence", - "core.h5p.author": "Auteur", - "core.h5p.authorcomments": "Commentaires de l'auteur", - "core.h5p.authorcommentsdescription": "Commentaire pour l'éditeur du contenu (ce texte ne sera pas publié dans les informations de copyright).", - "core.h5p.authorname": "Nom de l'auteur", - "core.h5p.authorrole": "Rôle de l'auteur", - "core.h5p.by": "par", - "core.h5p.cancellabel": "Annuler", - "core.h5p.ccattribution": "Attribution (CC BY)", - "core.h5p.ccattributionnc": "Attribution-Pas d'utilisation commerciale (CC BY NC)", - "core.h5p.ccattributionncnd": "Attribution-Pas d'utilisation commerciale-Pas de modification (CC BY NC ND)", - "core.h5p.ccattributionncsa": "Attribution-Pas d'utilisation commerciale-Partage dans les mêmes conditions (CC BY NC SA)", - "core.h5p.ccattributionnd": "Attribution-Pas de modification (CC BY ND)", - "core.h5p.ccattributionsa": "Attribution-Partage dans les mêmes conditions (CC BY SA)", - "core.h5p.ccpdd": "Marque du domaine public (CC0)", - "core.h5p.changedby": "Modifié par", - "core.h5p.changedescription": "Description de la modification", - "core.h5p.changelog": "Journal des modifications", - "core.h5p.changeplaceholder": "Photo recadrée, texte modifié, etc.", - "core.h5p.close": "Fermer", - "core.h5p.confirmdialogbody": "Veuillez confirmer que vous voulez continuer. Cette action est irréversible.", - "core.h5p.confirmdialogheader": "Confirmer l'action", - "core.h5p.confirmlabel": "Confirmer", - "core.h5p.connectionLost": "Connexion perdue. Les résultats seront enregistrés et envoyés lorsque la connexion sera rétablie.", - "core.h5p.connectionReestablished": "Connexion rétablie.", - "core.h5p.contentCopied": "Le contenu est copié dans le presse-papier", - "core.h5p.contentchanged": "Ce contenu a changé depuis votre dernière utilisation.", - "core.h5p.contenttype": "Type de contenu", - "core.h5p.copyright": "Droits d'utilisation", - "core.h5p.copyrightinfo": "Information de copyright", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "Afficher les informations de copyright de ce contenu.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Date", - "core.h5p.disablefullscreen": "Désactiver le plein écran", - "core.h5p.download": "Télécharger", - "core.h5p.downloadtitle": "Télécharger ce contenu comme fichier H5P.", - "core.h5p.editor": "Éditeur", - "core.h5p.embed": "Intégrer", - "core.h5p.embedtitle": "Afficher le code d'intégration de ce contenu.", - "core.h5p.errorgetemail": "Erreur lors de l'obtention du courriel de l'utilisateur. Veuillez vérifier votre connexion et réessayer.", - "core.h5p.fullscreen": "Plein écran", - "core.h5p.gpl": "Licence publique générale GNU v3", - "core.h5p.h5ptitle": "Visitez h5p.org pour obtenir encore plus de contenus cools.", - "core.h5p.hideadvanced": "Cacher avancé", - "core.h5p.license": "Licence", - "core.h5p.licenseCC010": "CC0 1.0 universel (CC0 1.0) Transfert dans le Domaine Public", - "core.h5p.licenseCC010U": "CC0 1.0 universel", - "core.h5p.licenseCC10": "1.0 générique", - "core.h5p.licenseCC20": "2.0 générique", - "core.h5p.licenseCC25": "2.5 générique", - "core.h5p.licenseCC30": "3.0 non portée", - "core.h5p.licenseCC40": "4.0 internationale", - "core.h5p.licenseGPL": "Licence générale publique", - "core.h5p.licenseV1": "Version 1", - "core.h5p.licenseV2": "Version 2", - "core.h5p.licenseV3": "Version 3", - "core.h5p.licensee": "Détenteur de licence", - "core.h5p.licenseextras": "Suppléments de licence", - "core.h5p.licenseversion": "Version de la licence", - "core.h5p.nocopyright": "Aucune information de copyright disponible pour ce contexte.", - "core.h5p.offlineDialogBody": "Impossible d'envoyer des informations sur votre achèvement de cette tâche. Veuillez vérifier votre connexion internet.", - "core.h5p.offlineDialogHeader": "La connexion avec le serveur a été perdue", - "core.h5p.offlineDialogRetryButtonLabel": "Réessayer maintenant", - "core.h5p.offlineDialogRetryMessage": "Nouvelle tentative dans :num…", - "core.h5p.offlineSuccessfulSubmit": "Résultats envoyés sans erreur.", - "core.h5p.offlinedisabled": "Le site ne permet pas le téléchargement des paquet H5P.", - "core.h5p.originator": "Expéditeur", - "core.h5p.pd": "Domaine public", - "core.h5p.pddl": "Licence et Transfert dans le Domaine Public", - "core.h5p.pdm": "Marque du domaine public (PDM)", - "core.h5p.play": "Lire H5P", - "core.h5p.resizescript": "Pour permettre le redimensionnement dynamique des contenus intégrés, inclure ce script dans votre site.", - "core.h5p.resubmitScores": "Tentative d'envoyer les résultats enregistrés.", - "core.h5p.reuse": "Réutiliser", - "core.h5p.reuseContent": "Réutiliser contenu", - "core.h5p.reuseDescription": "Réutiliser ce contenu.", - "core.h5p.showadvanced": "Afficher avancé", - "core.h5p.showless": "Montrer moins", - "core.h5p.showmore": "Montrer plus", - "core.h5p.size": "Taille", - "core.h5p.source": "Source", - "core.h5p.startingover": "Vous allez recommencer depuis le début.", - "core.h5p.sublevel": "Sous-niveau", - "core.h5p.thumbnail": "Vignette", - "core.h5p.title": "Titre", - "core.h5p.undisclosed": "Non communiqué", - "core.h5p.year": "Année", - "core.h5p.years": "Année(s)", - "core.h5p.yearsfrom": "Années (de)", - "core.h5p.yearsto": "Années (à)", - "core.hasdatatosync": "Ce {{$a}} a des données locales à synchroniser.", - "core.help": "Aide", - "core.hide": "Cacher", - "core.hour": "heure", - "core.hours": "heures", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Image", - "core.imageviewer": "Lecteur d'images", - "core.info": "Information", - "core.invalidformdata": "Données du formulaire incorrectes", - "core.labelsep": " ", - "core.lastaccess": "Dernier accès", - "core.lastdownloaded": "Dernier téléchargement", - "core.lastmodified": "Modifié le", - "core.lastsync": "Dernière synchronisation", - "core.layoutgrid": "Grille", - "core.list": "Liste", - "core.listsep": ";", - "core.loading": "Chargement", - "core.loadmore": "Charger plus", - "core.location": "Emplacement", - "core.login.auth_email": "Auto-enregistrement par courriel", - "core.login.authenticating": "Authentification", - "core.login.cancel": "Annuler", - "core.login.changepassword": "Changer le mot de passe", - "core.login.changepasswordbutton": "Ouvrir la page de changement de mot de passe", - "core.login.changepasswordhelp": "Si vous rencontrez des problèmes lors du changement de mot de passe, veuillez contacter l'administrateur de votre site. Si vous ne savez pas comment le contacter, veuillez contacter votre enseignant.", - "core.login.changepasswordinstructions": "Il n'est pas possible de changer votre mot de passe dans l'app. Veuillez cliquer le bouton ci-dessous pour ouvrir le site dans un navigateur, afin de changer votre mot de passe. N'oubliez pas de fermer votre navigateur après avoir changé de mot de passe, car vous ne serez pas redirigé vers l'app.", - "core.login.changepasswordlogoutinstructions": "Si vous préférez changer de site ou vous déconnecter, veuillez cliquer le bouton ci-dessous :", - "core.login.changepasswordreconnectinstructions": "Veuillez cliquer le bouton ci-dessous pour vous reconnecter au site. Si vous n'avez pas réussi à changer votre mot de passe, vous retournerez à l'écran précédent.", - "core.login.confirmdeletesite": "Voulez-vous vraiment supprimer la plateforme {{sitename}} ?", - "core.login.connect": "Connecter !", - "core.login.connecttomoodle": "Connexion à Moodle", - "core.login.connecttomoodleapp": "Vous tentez de vous connecter à un site Moodle standard. Veuillez télécharger l'app Moodle officielle pour y accéder.", - "core.login.connecttoworkplaceapp": "Vous tentez de vous connecter à un site Moodle Workplace. Veuillez télécharger l'app Moodle Workplace pour y accéder.", - "core.login.contactyouradministrator": "Veuillez contacter l'administrateur de la plateforme pour plus d'aide.", - "core.login.contactyouradministratorissue": "Veuillez demander à l'administrateur de la plateforme de vérifier l'élément suivant : {{$a}}", - "core.login.createaccount": "Créer mon compte", - "core.login.createuserandpass": "Créer un compte", - "core.login.credentialsdescription": "Veuillez fournir votre nom d'utilisateur et votre mot de passe pour vous connecter.", - "core.login.emailconfirmsent": "

                Un message vous a été envoyé à l'adresse de courriel {{$a}}.

                Il contient les instructions pour terminer votre enregistrement.

                Si vous rencontrez des difficultés, veuillez contacter l'administrateur du site.

                ", - "core.login.emailconfirmsentnoemail": "

                Un courriel a été envoyé à votre adresse.

                Il contient des instructions simples à effectuer pour terminer votre enregistrement.

                Si vous rencontrez des difficultés, veuillez contacte l'administrateur du site.", - "core.login.emailconfirmsentsuccess": "Courriel de confirmation envoyé", - "core.login.emailnotmatch": "Les adresses de courriel ne correspondent pas", - "core.login.erroraccesscontrolalloworigin": "La tentative d'appel « Cross-Origin » que vous avez effectuée a été rejetée. Veuillez consulter https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Une erreur est survenue lors de la suppression de la plateforme. Veuillez essayer plus tard.", - "core.login.errorexampleurl": "L'URL https://campus.example.edu est seulement un exemple d'adresse et n'est pas un véritable site. Veuillez indiquer l'URL du site de votre école ou de votre institution.", - "core.login.errorupdatesite": "Une erreur est survenue lors de la mise à jour du jeton du site.", - "core.login.faqcannotconnectanswer": "Veuillez contacter votre administrateur", - "core.login.faqsetupsitelinktitle": "Commencer.", - "core.login.faqsetupsitequestion": "Je souhaite configurer mon propre site Moodle.", - "core.login.faqtestappanswer": "Pour tester l'app avec un site Moodle de démonstration, tapez « teacher » ou « student » dans le champ « Votre site » et tapotez le bouton « Connexion à votre site ».", - "core.login.faqtestappquestion": "Je souhaite simplement tester l'app ; comment faire ?", - "core.login.faqwhatisurlquestion": "Quelle est l'adresse de mon site ? Comment trouver son URL ?", - "core.login.faqwhereisqrcode": "Où trouver le code QR ?", - "core.login.faqwhereisqrcodeanswer": "

                Si votre institution l'a activé, vous trouverez un code QR au bas de votre page de profil sur son site web.

                {{$image}}", - "core.login.findyoursite": "Trouver votre site", - "core.login.firsttime": "Première visite sur ce site ?", - "core.login.forcepasswordchangenotice": "Vous devez changer votre mot de passe pour continuer.", - "core.login.forgotten": "Vous avez oublié votre nom d'utilisateur et/ou votre mot de passe ?", - "core.login.help": "Aide", - "core.login.helpmelogin": "Il y a plusieurs milliers de sites Moodle dans le monde. Cette app peut uniquement se connecter aux sites Moodle qui ont activé l'accès via l'app mobile.

                Si vous n'arrivez pas à vous connecter, veuillez contacter l'administrateur de votre site Moodle et lui demander de lire https://docs.moodle.org/fr/App_Moodle

                Pour tester l'app avec un site Moodle de démonstration, tapez teacher ou student dans le champ Adresse du site et tapotez sur le bouton Connexion.

                ", - "core.login.instructions": "Instructions", - "core.login.invalidaccount": "Veuillez vérifier vos données de connexion ou demander à l'administrateur de votre site de vérifier sa configuration.", - "core.login.invaliddate": "Date invalide", - "core.login.invalidemail": "Adresse de courriel incorrecte", - "core.login.invalidmoodleversion": "

                Version de Moodle non valide. L'app Moodle ne fonctionne qu'avec Moodle {{$a}} ou une version ultérieure.

                \n

                Veuillez contacter l'administrateur de votre site et lui demander de mettre à jour son système Moodle.

                ", - "core.login.invalidsite": "Cette URL n'est pas valide.", - "core.login.invalidtime": "Temps non valide", - "core.login.invalidurl": "URL spécifié non valide", - "core.login.invalidvaluemax": "La valeur maximale est {{$a}}", - "core.login.invalidvaluemin": "La valeur minimale est {{$a}}", - "core.login.localmobileunexpectedresponse": "La vérification des fonctionnalités additionnelles de Moodle Mobile a envoyé une réponse inattendue. Vous allez être connecté au moyen du service mobile standard.", - "core.login.loggedoutssodescription": "Veuillez vous ré-authentifier en vous connectant au site au moyen d'un navigateur web.", - "core.login.login": "Connexion", - "core.login.loginbutton": "Se connecter", - "core.login.logininsiterequired": "Vous devez vous connecter au moyen d'un navigateur.", - "core.login.loginsteps": "Pour un accès complet à ce site, veuillez créer un compte utilisateur.", - "core.login.missingemail": "L'adresse de courriel ne peut pas être vide", - "core.login.missingfirstname": "Le prénom ne peut pas être vide", - "core.login.missinglastname": "Le nom ne peut pas être vide", - "core.login.mobileservicesnotenabled": "Les Services Moodle ne sont pas activés sur votre site. Veuillez contacter l'administrateur de votre site si vous pensez qu'ils devraient être activés.", - "core.login.mustconfirm": "Vous devez confirmer l'enregistrement de votre compte", - "core.login.newaccount": "Nouveau compte", - "core.login.notloggedin": "Vous devez être connecté.", - "core.login.onboardingcreatemanagecourses": "Créer & gérer vos cours", - "core.login.onboardingenrolmanagestudents": "Inscrire & gérer vos étudiants", - "core.login.onboardinggetstarted": "Commencer avec Moodle", - "core.login.onboardingialreadyhaveasite": "J'ai déjà un site Moodle", - "core.login.onboardingimalearner": "Je suis un étudiant", - "core.login.onboardingimaneducator": "Je suis un enseignant", - "core.login.onboardingineedasite": "J'ai besoin d'un site Moodle", - "core.login.onboardingprovidefeedback": "Fournir un feedback", - "core.login.onboardingtoconnect": "Vous devez avoir un site Moodle pour utiliser l'App Moodle", - "core.login.onboardingwelcome": "Bienvenue dans l'App Moodle !", - "core.login.or": "OU", - "core.login.password": "Mot de passe", - "core.login.passwordforgotten": "Mot de passe oublié", - "core.login.passwordforgotteninstructions2": "Pour recevoir un nouveau mot de passe, veuillez indiquer ci-dessous votre adresse de courriel ou votre nom d'utilisateur. Si les données correspondantes se trouvent dans la base de données, un message vous sera envoyé par courriel, avec des instructions vous permettant de vous connecter.", - "core.login.passwordrequired": "Mot de passe requis", - "core.login.policyaccept": "Je comprends et je me déclare d'accord", - "core.login.policyagree": "Vous devez accepter de vous conformer à cette politique pour continuer à utiliser ce site. Acceptez-vous cette politique ?", - "core.login.policyagreement": "Acceptation de la politique du site", - "core.login.policyagreementclick": "Lien vers l'acceptation de la politique du site", - "core.login.potentialidps": "Se connecter au moyen du compte :", - "core.login.profileinvaliddata": "Valeur incorrecte", - "core.login.recaptchachallengeimage": "Image reCAPTCHA", - "core.login.recaptchaexpired": "Vérification échue. Veuillez de nouveau répondre à la question de sécurité.", - "core.login.recaptchaincorrect": "La réponse à la question de sécurité est incorrecte.", - "core.login.reconnect": "Reconnecter", - "core.login.reconnectdescription": "Votre jeton d'authentification est non valide ou échu. Veuillez vous reconnecter à la plateforme.", - "core.login.reconnectssodescription": "Votre jeton d'authentification est non valide ou échu. Veuillez vous reconnecter à la plateforme, en vous connectant dans un navigateur.", - "core.login.resendemail": "Renvoyer le courriel", - "core.login.searchby": "Rechercher par :", - "core.login.security_question": "Question de sécurité", - "core.login.selectacountry": "Choisir un pays", - "core.login.selectsite": "Veuillez sélectionner votre site :", - "core.login.signupplugindisabled": "{{$a}} n'est pas activée.", - "core.login.siteaddress": "Votre site", - "core.login.sitehasredirect": "Votre site contient au moins une redirection HTTP. L'app ne peut pas suivre les redirections. C'est sans doute la raison empêche l'app de se connecter à votre site.", - "core.login.siteinmaintenance": "Votre site est en mode de maintenance", - "core.login.sitepolicynotagreederror": "Règlement du site pas accepté.", - "core.login.siteurl": "URL du site", - "core.login.siteurlrequired": "URL du site requis, p. ex. http://www.votre_moodle.fr ou https://www.votre_moodle.fr", - "core.login.startsignup": "Créer un compte", - "core.login.stillcantconnect": "Toujours impossible de se connecter ?", - "core.login.supplyinfo": "Plus de détails", - "core.login.username": "Nom d'utilisateur", - "core.login.usernameoremail": "Veuillez indiquer soit le nom d'utilisateur, soit l'adresse de courriel", - "core.login.usernamerequired": "Nom d'utilisateur requis", - "core.login.usernotaddederror": "Utilisateur pas ajouté : erreur inconnue", - "core.login.visitchangepassword": "Voulez-vous vraiment visiter le site pour modifier votre mot de passe ?", - "core.login.webservicesnotenabled": "Votre site web semble ne pas avoir activé les services Web. Veuillez contacter l'administrateur de votre site pour de l'aide.", - "core.login.youcanstillconnectwithcredentials": "Vous pouvez malgré tout vous connecter à votre site en saisissant votre nom d'utilisateur et votre mot de passe.", - "core.login.yourenteredsite": "Connexion à votre site", - "core.lostconnection": "Votre jeton n'est pas valide ou est échu. Veuillez vous reconnecter à la plateforme.", - "core.mainmenu.changesite": "Changer de plateforme", - "core.mainmenu.help": "Aide", - "core.mainmenu.logout": "Déconnexion", - "core.mainmenu.website": "Site web", - "core.maxsizeandattachments": "Taille maximale des fichiers : {{$a.size}} ; nombre maximal de fichiers : {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Divers", - "core.mod_assign": "Devoir", - "core.mod_assignment": "Devoir 2.2 (désactivé)", - "core.mod_book": "Livre", - "core.mod_chat": "Chat", - "core.mod_choice": "Sondage", - "core.mod_data": "Base de données", - "core.mod_database": "Base de données", - "core.mod_external-tool": "Outil externe", - "core.mod_feedback": "Feedback", - "core.mod_file": "Fichier", - "core.mod_folder": "Dossier", - "core.mod_forum": "Forum", - "core.mod_glossary": "Glossaire", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "Paquetage IMS Content", - "core.mod_imscp": "Paquetage IMS Content", - "core.mod_label": "Étiquette", - "core.mod_lesson": "Leçon", - "core.mod_lti": "Outil externe", - "core.mod_page": "Page", - "core.mod_quiz": "Test", - "core.mod_resource": "Fichier", - "core.mod_scorm": "Paquetage SCORM", - "core.mod_survey": "Consultation", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Atelier", - "core.moduleintro": "Description", - "core.more": "suite", - "core.mygroups": "Mes groupes", - "core.name": "Nom", - "core.needhelp": "Avez-vous besoin d'aide ?", - "core.networkerroriframemsg": "Ce contenu n'est pas disponible hors ligne. Veuillez vous connecter à Internet et ressayer.", - "core.networkerrormsg": "Un problème est survenu lors de la connexion au site. Veuillez vérifier votre connexion et essayer à nouveau.", - "core.never": "Jamais", - "core.next": "Suivant", - "core.no": "Non", - "core.nocomments": "Aucun commentaire", - "core.nograde": "Pas de note", - "core.none": "Aucun", - "core.nooptionavailable": "Aucune option disponible", - "core.nopasswordchangeforced": "Vous ne pouvez pas continuer sans changer votre mot de passe.", - "core.nopermissionerror": "Vous n'avez pas l'autorisation de faire cela", - "core.nopermissions": "Désolé, vous n'avez actuellement pas les permissions requises pour effectuer ceci ({{$a}})", - "core.noresults": "Aucun résultat", - "core.noselection": "Aucune sélection", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Ce profil n'est pas accessible, car cet utilisateur n'est pas inscrit à ce cours.", - "core.notice": "Remarque", - "core.notingroup": "Désolé, vous devez faire partie d'un groupe pour voir cette page.", - "core.notsent": "Pas envoyé", - "core.now": "maintenant", - "core.nummore": "{{$a}} de plus", - "core.numwords": "{{$a}} mots", - "core.offline": "Déconnecté", - "core.ok": "OK", - "core.online": "En ligne", - "core.openfullimage": "Cliquer ici pour afficher l'image en pleine grandeur", - "core.openinbrowser": "Ouvrir dans le navigateur", - "core.openmodinbrowser": "Ouvrir {{$a}} dans le navigateur", - "core.othergroups": "Autres groupes", - "core.pagea": "Page {{$a}}", - "core.paymentinstant": "Le bouton ci-dessous vous permet de payer et de vous inscrire en quelques minutes !", - "core.percentagenumber": "{{$a}} %", - "core.phone": "Téléphone", - "core.pictureof": "Avatar {{$a}}", - "core.previous": "Précédent", - "core.proceed": "Continuer", - "core.pulltorefresh": "Tirer pour actualiser", - "core.qrscanner": "Lecteur code QR", - "core.question.answer": "Réponse", - "core.question.answersaved": "Réponse enregistrée", - "core.question.cannotdeterminestatus": "Impossible de déterminer le statut", - "core.question.certainty": "Certitude", - "core.question.complete": "Terminer", - "core.question.correct": "Correct", - "core.question.errorattachmentsnotsupported": "L'application ne permet pas encore d'annexer des fichiers aux réponses.", - "core.question.errorinlinefilesnotsupported": "L'app ne permet pas encore la modification de fichiers en ligne.", - "core.question.errorquestionnotsupported": "Ce type de question n'est pas supporté par l'app : {{$a}}.", - "core.question.feedback": "Feedback", - "core.question.howtodraganddrop": "Tapoter pour sélectionner, puis tapoter pour déposer.", - "core.question.incorrect": "Incorrect", - "core.question.information": "Description", - "core.question.invalidanswer": "Réponse incomplète", - "core.question.notanswered": "Non répondue", - "core.question.notyetanswered": "Pas encore répondu", - "core.question.partiallycorrect": "Partiellement correct", - "core.question.questionmessage": "Question {{$a}} : {{$b}}", - "core.question.questionno": "Question {{$a}}", - "core.question.requiresgrading": "Nécessite évaluation", - "core.quotausage": "Vous utilisez actuellement {{$a.used}} de votre quota de {{$a.total}}.", - "core.rating.aggregateavg": "Moyenne des évaluations", - "core.rating.aggregatecount": "Nombre d'évaluations", - "core.rating.aggregatemax": "Évaluation maximale", - "core.rating.aggregatemin": "Évaluation minimale", - "core.rating.aggregatesum": "Somme des évaluations", - "core.rating.noratings": "Aucune évaluation remise", - "core.rating.rating": "Évaluation", - "core.rating.ratings": "Évaluations", - "core.redirectingtosite": "Vous allez être redirigé vers le site.", - "core.refresh": "Actualiser", - "core.remove": "Supprimer", - "core.removefiles": "Supprimer les fichiers {{$a}}", - "core.required": "Requis", - "core.requireduserdatamissing": "Il manque certaines données au profil de cet utilisateur. Veuillez compléter ces données dans votre plateforme et essayer à nouveau.
                {{$a}}", - "core.resourcedisplayopen": "Ouvrir", - "core.resources": "Ressources", - "core.restore": "Restauration", - "core.restricted": "Accès restreint", - "core.retry": "Essayer à nouveau", - "core.save": "Enregistrer", - "core.savechanges": "Enregistrer", - "core.scanqr": "Lire le code QR", - "core.search": "Rechercher", - "core.searching": "Recherche", - "core.searchresults": "Résultats de la recherche", - "core.sec": "s", - "core.secs": "s", - "core.seemoredetail": "Cliquer ici pour avoir plus de détail", - "core.selectacategory": "Veuillez sélectionner une catégorie", - "core.selectacourse": "Sélectionner un cours", - "core.selectagroup": "Sélectionner un groupe", - "core.send": "Envoyer", - "core.sending": "Envoi", - "core.serverconnection": "Erreur lors de la connexion au serveur", - "core.settings.about": "À propos", - "core.settings.appsettings": "Réglages de l'app", - "core.settings.appversion": "Version de l'app", - "core.settings.cannotsyncoffline": "Impossible de synchroniser hors connexion.", - "core.settings.cannotsyncwithoutwifi": "Impossible de synchroniser car les réglages ne permettent la synchronisation que lorsque une connexion Wi-Fi est établie. Veuillez vous connecter à un réseau Wi-Fi.", - "core.settings.colorscheme": "Thème de couleur", - "core.settings.colorscheme-auto": "Automatique (basé sur les réglages système)", - "core.settings.colorscheme-dark": "Sombre", - "core.settings.colorscheme-light": "Clair", - "core.settings.compilationinfo": "Info de compilation", - "core.settings.copyinfo": "Copier les infos de l'appareil dans le presse-papier", - "core.settings.cordovadevicemodel": "Modèle Cordova Device", - "core.settings.cordovadeviceosversion": "Version OS Cordova Device", - "core.settings.cordovadeviceplatform": "Plateforme Cordova Device", - "core.settings.cordovadeviceuuid": "UUID Cordova Device", - "core.settings.cordovaversion": "Version Cordova", - "core.settings.currentlanguage": "Langue active", - "core.settings.debugdisplay": "Afficher les informations de débogage", - "core.settings.debugdisplaydescription": "Si ce réglage est activé, les dialogues d'erreur afficheront plus d'informations sur l'erreur, dans la mesure du possible.", - "core.settings.deletesitefiles": "Voulez-vous vraiment supprimer les fichiers téléchargés depuis le site « {{sitename}} » ?", - "core.settings.deletesitefilestitle": "Supprimer les fichiers du site", - "core.settings.deviceinfo": "Info sur l'appareil", - "core.settings.deviceos": "Système d'exploitation de l'appareil", - "core.settings.disableall": "Désactiver les notifications", - "core.settings.disabled": "Désactivée", - "core.settings.displayformat": "Format d'affichage", - "core.settings.enabledownloadsection": "Activer le téléchargement des sections", - "core.settings.enablefirebaseanalytics": "Activer les statistiques Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Si ce réglage est activé, l'app collectera des données d'usage anonymes.", - "core.settings.enablerichtexteditor": "Activer l'éditeur WYSIWYG", - "core.settings.enablerichtexteditordescription": "Si ce réglage est activé, un éditeur WYSIWYG sera affiché aux emplacements permis.", - "core.settings.enablesyncwifi": "Ne permettre la synchronisation que sur Wi-Fi", - "core.settings.entriesincache": "{{$a}} éléments en cache", - "core.settings.errordeletesitefiles": "Erreur lors de la suppression des fichiers du site.", - "core.settings.errorsyncsite": "Erreur de synchronisation des données. Veuillez vérifier votre connexion internet et essayer plus tard.", - "core.settings.estimatedfreespace": "Espace libre estimé", - "core.settings.filesystemroot": "Racine du système de fichiers", - "core.settings.fontsize": "Corps du texte", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "Ce réglage est imposé par la configuration de votre site.", - "core.settings.general": "Général", - "core.settings.language": "Langue", - "core.settings.license": "Licence", - "core.settings.localnotifavailable": "Notifications locales disponibles", - "core.settings.locationhref": "URL webview", - "core.settings.locked": "Verrouillé", - "core.settings.loggedin": "En ligne", - "core.settings.loggedoff": "Hors ligne", - "core.settings.navigatorlanguage": "Langue du navigateur", - "core.settings.navigatoruseragent": "UserAgent du navigateur", - "core.settings.networkstatus": "Statut de la connexion internet", - "core.settings.opensourcelicenses": "Licences Open Source", - "core.settings.preferences": "Préférences", - "core.settings.privacypolicy": "Politique de confidentialité", - "core.settings.publisher": "Éditeur", - "core.settings.pushid": "Identifiant de notifications push", - "core.settings.reportinbackground": "Annoncer les erreurs automatiquement", - "core.settings.screen": "Infos écran", - "core.settings.settings": "Paramètres", - "core.settings.showdownloadoptions": "Afficher les options de téléchargement", - "core.settings.siteinfo": "Infos site", - "core.settings.sites": "Sites", - "core.settings.spaceusage": "Espace utilisé", - "core.settings.spaceusagehelp": "La suppression des infos enregistrées du site supprimera toutes les données hors ligne du site. Ces infos vous permettent d'utiliser l'app lorsque vous êtes hors ligne.", - "core.settings.synchronization": "Synchronisation", - "core.settings.synchronizenow": "Synchroniser maintenant", - "core.settings.synchronizenowhelp": "La synchronisation envoie au site les modifications en attente et les données de toutes les activités hors ligne enregistrées sur l'appareil et synchronise d'autres données telles que les messages et les notifications.", - "core.settings.syncsettings": "Réglages de synchronisation", - "core.settings.total": "Total", - "core.settings.wificonnection": "Connexion WiFi", - "core.sharedfiles.chooseaccountstorefile": "Veuillez choisir un compte dans lequel enregistrer le fichier.", - "core.sharedfiles.chooseactionrepeatedfile": "Un fichier de ce nom existe déjà. Voulez-vous remplacer le fichier existant ou le renommer en « {{$a}} » ?", - "core.sharedfiles.errorreceivefilenosites": "Aucun site n'est enregistré. Veuillez ajouter un site avant de partager un fichier avec l'app.", - "core.sharedfiles.nosharedfiles": "Il n'y a pas de fichier partagé sur ce site.", - "core.sharedfiles.nosharedfilestoupload": "Il n'y a pas de fichier à déposer ici. Si vous voulez déposer un fichier à partir d'une autre app, localiser le fichier et tapoter « Ouvrir dans ».", - "core.sharedfiles.rename": "Renommer", - "core.sharedfiles.replace": "Remplacer", - "core.sharedfiles.sharedfiles": "Fichiers partagés", - "core.sharedfiles.successstorefile": "Fichier enregistré. Vous pouvez maintenant le sélectionner pour le déposer dans vos fichiers privés ou le joindre à des activités.", - "core.show": "Afficher", - "core.showless": "Afficher moins…", - "core.showmore": "Afficher plus…", - "core.site": "Site", - "core.sitehome.sitehome": "Accueil du site", - "core.sitehome.sitenews": "Annonces du site", - "core.sitemaintenance": "Ce site est actuellement en maintenance. Il est donc temporairement non disponible", - "core.sizeb": "octets", - "core.sizegb": "Go", - "core.sizekb": "Ko", - "core.sizemb": "Mo", - "core.sizetb": "To", - "core.skip": "Passer", - "core.sorry": "Désolé…", - "core.sort": "Trier", - "core.sortby": "Trier par", - "core.start": "Démarrer", - "core.storingfiles": "Stockage des fichiers", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d %b %y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d %b %y, %H:%M", - "core.strftimedaydate": "%A %d %B %Y", - "core.strftimedaydatetime": "%A %d %B %Y, %H:%M", - "core.strftimedayshort": "%A %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M", - "core.strftimetime24": "%H:%M", - "core.submit": "Envoyer", - "core.success": "Succès", - "core.tablet": "Tablette", - "core.tag.defautltagcoll": "Collection par défaut", - "core.tag.errorareanotsupported": "Cette zone de tag n'est pas supportée par l'app.", - "core.tag.inalltagcoll": "Partout", - "core.tag.itemstaggedwith": "{{$a.tagarea}} signalées avec « {{$a.tag}} »", - "core.tag.noresultsfor": "Aucun résultat pour « {{$a}} »", - "core.tag.notagsfound": "Aucun tags avec « {{$a}} » trouvé", - "core.tag.searchtags": "Chercher des tags", - "core.tag.showingfirsttags": "Affichage des {{$a}} tags les plus utilisés", - "core.tag.tag": "Tag", - "core.tag.tagarea_course": "Cours", - "core.tag.tagarea_course_modules": "Activités et ressources", - "core.tag.tagarea_post": "Articles de blog", - "core.tag.tagarea_user": "Centres d'intérêt", - "core.tag.tags": "Tags", - "core.tag.warningareasnotsupported": "Certaines zones de tag ne sont pas affichées, car elles ne sont pas supportées par l'app.", - "core.teachers": "Enseignants", - "core.thereisdatatosync": "Il y a des {{$a}} locales à synchroniser.", - "core.thisdirection": "ltr", - "core.time": "Heure", - "core.timesup": "Le chrono est enclenché !", - "core.today": "Aujourd'hui", - "core.tryagain": "Essayer encore", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "Aïe !", - "core.unexpectederror": "Erreur inattendue. Veuillez fermer et rouvrir l'app et réessayer.", - "core.unicodenotsupported": "Certains emojis ne sont pas supportés sur ce site. Ils seront supprimés avant l'envoi.", - "core.unicodenotsupportedcleanerror": "Un texte vide a été rencontré lors du nettoyage des caractères Unicode.", - "core.unknown": "Inconnu", - "core.unlimited": "Illimité", - "core.unzipping": "Décompression", - "core.updaterequired": "Mise à jour de l'app requise", - "core.updaterequireddesc": "Veuillez mettre à jour l'app à la versiopn {{$a}}", - "core.upgraderunning": "Ce site est en phase de mise à jour. Veuillez essayer plus tard.", - "core.user": "Utilisateur", - "core.user.address": "Adresse", - "core.user.city": "Ville", - "core.user.contact": "Contact", - "core.user.country": "Pays", - "core.user.description": "Description", - "core.user.details": "Détails", - "core.user.detailsnotavailable": "Vous n'avez pas accès aux informations de cet utilisateur.", - "core.user.editingteacher": "Enseignant", - "core.user.email": "Adresse de courriel", - "core.user.emailagain": "Courriel (confirmation)", - "core.user.errorloaduser": "Erreur lors du chargement de l'utilisateur.", - "core.user.firstname": "Prénom", - "core.user.interests": "Centres d'intérêt", - "core.user.lastname": "Nom", - "core.user.manager": "Gestionnaire", - "core.user.newpicture": "Nouvelle image", - "core.user.noparticipants": "Aucun participant trouvé dans ce cours", - "core.user.participants": "Participants", - "core.user.phone1": "Téléphone", - "core.user.phone2": "Téléphone mobile", - "core.user.roles": "Rôles", - "core.user.sendemail": "Courriel", - "core.user.student": "Étudiant", - "core.user.teacher": "Enseignant non éditeur", - "core.user.webpage": "Page Web", - "core.userdeleted": "Le compte de cet utilisateur a été supprimé", - "core.userdetails": "Informations détaillées", - "core.usernotfullysetup": "Utilisateur pas complètement défini", - "core.users": "Utilisateurs", - "core.view": "Affichage", - "core.viewcode": "Afficher le code", - "core.vieweditor": "Afficher l'éditeur", - "core.viewembeddedcontent": "Afficher le contenu intégré", - "core.viewprofile": "Consulter le profil", - "core.warningofflinedatadeleted": "Des données locales de {{component}} « {{name}} » ont été supprimées. {{error}}", - "core.whatisyourage": "Quel âge avez-vous ?", - "core.wheredoyoulive": "Dans quel pays vivez-vous ?", - "core.whoissiteadmin": "Les administrateurs du site sont des personnes qui gèrent la plateforme Moodle de votre institution (école/université/entreprise). Si vous ne savez pas comment les contacter, demandez à vos enseignants/formateurs.", - "core.whoops": "Oups !", - "core.whyisthishappening": "Que se passe-t-il ?", - "core.whyisthisrequired": "Pourquoi ceci est nécessaire ?", - "core.wsfunctionnotavailable": "Le service web n'est pas disponible.", - "core.year": "année", - "core.years": "années", - "core.yes": "Oui", - "core.youreoffline": "Vous êtes hors ligne", - "core.youreonline": "Vous êtes de nouveau en ligne" -} \ No newline at end of file diff --git a/src/assets/lang/he.json b/src/assets/lang/he.json deleted file mode 100644 index e1008613e..000000000 --- a/src/assets/lang/he.json +++ /dev/null @@ -1,1590 +0,0 @@ -{ - "addon.badges.alignment": "עימוד", - "addon.badges.badgedetails": "פרטי ההישג", - "addon.badges.badges": "הישגים", - "addon.badges.bendorsement": "חסות", - "addon.badges.claimcomment": "הערת חסות", - "addon.badges.contact": "ליצירת קשר", - "addon.badges.dateawarded": "תאריך הקבלה", - "addon.badges.expired": "פג תוקף", - "addon.badges.expirydate": "תאריך תפוגה", - "addon.badges.imageauthoremail": "דוא\"ל היוצר", - "addon.badges.imageauthorname": "שם היוצר", - "addon.badges.imageauthorurl": "כתובת האינטרנט של היוצר", - "addon.badges.imagecaption": "כותרת התמונה", - "addon.badges.issuancedetails": "מועד תפוגת ההישג", - "addon.badges.issuerdetails": "פרטי הגורם אשר העניק את ההישג", - "addon.badges.issueremail": "דוא\"ל", - "addon.badges.issuername": "שם מעניק ההישג", - "addon.badges.issuerurl": "קישור מעניק ההישג", - "addon.badges.language": "שפה", - "addon.badges.nobadges": "אין הישגים זמינים.", - "addon.badges.recipientdetails": "פרטי המכותב", - "addon.badges.relatedbages": "השגים קשורים", - "addon.badges.version": "גרסה", - "addon.badges.warnexpired": "(תוקפו של הישג זה פג!)", - "addon.block_activitymodules.pluginname": "פעילויות", - "addon.block_activityresults.pluginname": "תוצאות הפעילות", - "addon.block_badges.pluginname": "ההישגים האחרונים שלי", - "addon.block_blogmenu.pluginname": "ניהול בלוג אישי", - "addon.block_blogrecent.pluginname": "כניסות בלוג אחרונות", - "addon.block_blogtags.pluginname": "תגי בלוג", - "addon.block_calendarmonth.pluginname": "לוח־שנה", - "addon.block_calendarupcoming.pluginname": "ארועים קרבים", - "addon.block_comments.pluginname": "תגובות (הערות)", - "addon.block_completionstatus.pluginname": "מעקב התקדמות למידה", - "addon.block_glossaryrandom.pluginname": "מונח אקראי מאגרון־המונחים", - "addon.block_learningplans.pluginname": "תוכניות־לימוד", - "addon.block_myoverview.all": "כולם (למעט מוסתרים)", - "addon.block_myoverview.allincludinghidden": "הכל", - "addon.block_myoverview.favourites": "מועדפים", - "addon.block_myoverview.future": "עתידי", - "addon.block_myoverview.hiddencourses": "מוסתר", - "addon.block_myoverview.inprogress": "בלמידה", - "addon.block_myoverview.lastaccessed": "גישה אחרונה", - "addon.block_myoverview.morecourses": "קורסים נוספים", - "addon.block_myoverview.nocourses": "טרם נרשמתם לקורס כלשהו", - "addon.block_myoverview.past": "ארכיון", - "addon.block_myoverview.pluginname": "עדכונים בקורסים שלי", - "addon.block_myoverview.shortname": "שם קצר", - "addon.block_myoverview.title": "שם הקורס", - "addon.block_newsitems.pluginname": "חדשות אחרונות", - "addon.block_onlineusers.pluginname": "משתמשים מחוברים", - "addon.block_privatefiles.pluginname": "ניהול קבצי משתמש", - "addon.block_recentactivity.pluginname": "פעילות אחרונה", - "addon.block_recentlyaccessedcourses.nocourses": "לא נמצאו קורסים בהם צפיתם לאחרונה", - "addon.block_recentlyaccessedcourses.pluginname": "קורסים בהם צפיתם לאחרונה", - "addon.block_recentlyaccesseditems.pluginname": "רכיבים בהם צפיתם לאחרונה", - "addon.block_rssclient.pluginname": "הזנות RSS", - "addon.block_selfcompletion.pluginname": "אישור השלמה קורס", - "addon.block_sitemainmenu.pluginname": "תפריט ראשי", - "addon.block_starredcourses.nocourses": "אין קורסים המסומנים בכוכב", - "addon.block_starredcourses.pluginname": "קורסים המסומנים בכוכב", - "addon.block_tags.pluginname": "תגים", - "addon.block_timeline.duedate": "תאריך הגשה", - "addon.block_timeline.next30days": "30 יום", - "addon.block_timeline.next3months": "3 חודשים", - "addon.block_timeline.next6months": "6 חודשים", - "addon.block_timeline.next7days": "7 ימים", - "addon.block_timeline.nocoursesinprogress": "אין קורסים בלמידה", - "addon.block_timeline.noevents": "אין פעילויות ממתינות", - "addon.block_timeline.overdue": "באיחור", - "addon.block_timeline.pluginname": "ממתין לביצוע", - "addon.block_timeline.sortbycourses": "מיון לפי קורסים", - "addon.block_timeline.sortbydates": "מיון לפי תאריך", - "addon.blog.blog": "בלוג אישי", - "addon.blog.blogentries": "פרסומיי הבלוג", - "addon.blog.linktooriginalentry": "קישור לידיעת בלוג מקורי", - "addon.blog.noentriesyet": "אין כאן ידיעות גלויות", - "addon.blog.publishtonoone": "פרסום זה זמין ל\"עצמך בלבד\"", - "addon.blog.publishtosite": "פרסום זה זמין ל\"כל משתמש מזוהה\"", - "addon.blog.publishtoworld": "פרסום זה זמין ל\"אורחים\"", - "addon.blog.siteblogheading": "בלוג אישי (מערכתי)", - "addon.calendar.allday": "כל היום", - "addon.calendar.calendar": "לוח-שנה", - "addon.calendar.calendarevents": "אירועי לוח שנה", - "addon.calendar.categoryevents": "אירועים בקטגוריה", - "addon.calendar.confirmeventdelete": "האם למחוק אירוע \"{{$a}}\"?", - "addon.calendar.courseevents": "אירועי קורס", - "addon.calendar.daynext": "היום הבא", - "addon.calendar.dayprev": "היום הקודם", - "addon.calendar.deleteallevents": "מחיקת כל האירועים", - "addon.calendar.deleteevent": "מחיקת אירוע", - "addon.calendar.deleteoneevent": "מחיקת אירוע זה", - "addon.calendar.durationminutes": "משך זמן בדקות", - "addon.calendar.durationnone": "ללא משך זמן", - "addon.calendar.durationuntil": "עד", - "addon.calendar.editevent": "עריכת אירוע", - "addon.calendar.errorloadevent": "שגיאה בטעינת האירוע.", - "addon.calendar.errorloadevents": "שגיאה בטעינת האירועים.", - "addon.calendar.eventcalendareventdeleted": "אירוע לוח-שנה נמחק", - "addon.calendar.eventduration": "משך", - "addon.calendar.eventendtime": "זמן סיום", - "addon.calendar.eventkind": "סוג האירוע", - "addon.calendar.eventname": "כותרת אירוע", - "addon.calendar.eventstarttime": "זמן התחלה", - "addon.calendar.eventtype": "סוג ארוע", - "addon.calendar.fri": "ו", - "addon.calendar.friday": "שישי", - "addon.calendar.gotoactivity": "צפיה בפעילות", - "addon.calendar.groupevents": "אירועים קבוצתיים", - "addon.calendar.invalidtimedurationminutes": "משך הזמן בדקותשהכנסת אינו תקף. אנא הכנס משך זמן בדקות גדול מ-0 או הימנע מבחירת משך זמן .", - "addon.calendar.invalidtimedurationuntil": "התאריך והזמן שבחרת עבור משך הזמן הוא לפני תחילת התאריך של האירוע. אנא בחר תאריך נכון לפני המשך התהליך.", - "addon.calendar.mon": "ב", - "addon.calendar.monday": "שני", - "addon.calendar.monthlyview": "תצוגה חודשית", - "addon.calendar.newevent": "אירוע חדש", - "addon.calendar.noevents": "אין אירועים", - "addon.calendar.nopermissiontoupdatecalendar": "לצערנו אין לך הרשאות מתאימות בכדי לעדכן אירוע יומן זה", - "addon.calendar.repeatedevents": "אירועים חוזרים", - "addon.calendar.repeateditall": "החלת שינויים עבור {{$a}} האירועים בסדרות החוזרות הללו", - "addon.calendar.repeateditthis": "החלת השינוי על אירוע זה בלבד", - "addon.calendar.repeatevent": "חזרה על אירוע זה", - "addon.calendar.repeatweeksl": "חזרה כל שבוע, יצירת סיכום", - "addon.calendar.sat": "ש", - "addon.calendar.saturday": "שבת", - "addon.calendar.siteevents": "אירועי מערכת", - "addon.calendar.sun": "א", - "addon.calendar.sunday": "ראשון", - "addon.calendar.thu": "ה", - "addon.calendar.thursday": "חמישי", - "addon.calendar.today": "היום", - "addon.calendar.tomorrow": "מחר", - "addon.calendar.tue": "ג", - "addon.calendar.tuesday": "שלישי", - "addon.calendar.typecategory": "אירוע קטגוריה", - "addon.calendar.typeclose": "סגירת אירוע", - "addon.calendar.typecourse": "אירוע קורס", - "addon.calendar.typedue": "מועד האירוע", - "addon.calendar.typegradingdue": "אירוע בדיקת ציונים", - "addon.calendar.typegroup": "אירוע קבוצתי", - "addon.calendar.typeopen": "פתיחת אירוע", - "addon.calendar.typesite": "אירוע מערכתי", - "addon.calendar.typeuser": "אירוע משתמש", - "addon.calendar.upcomingevents": "אירועים קרבים", - "addon.calendar.userevents": "אירועי משתמשים", - "addon.calendar.wed": "ד", - "addon.calendar.wednesday": "רביעי", - "addon.calendar.when": "בתאריך", - "addon.calendar.yesterday": "אתמול", - "addon.competency.activities": "פעילויות", - "addon.competency.competencies": "מיומנויות", - "addon.competency.competenciesmostoftennotproficientincourse": "מיומנויות בהן אינכם בקיאים בקורס זה", - "addon.competency.coursecompetencies": "מיומנויות הקורס", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "השלמת מיומנויות בקורס זה לא מתעדכנות בתוכניות־הלימוד", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "מצב רכישת מיומנות כתוצאה מהשלמת פעילות בקורס, מתעדכן באופן מידי בתוכניות־הלימוד.", - "addon.competency.crossreferencedcompetencies": "מקושר למיומנויות", - "addon.competency.duedate": "עד לתאריך", - "addon.competency.evidence": "ראיה לבקיאות", - "addon.competency.evidence_competencyrule": "תנאי המיומנות נענה", - "addon.competency.evidence_coursecompleted": "הקורס '{{$a}}' הושלם.", - "addon.competency.evidence_coursemodulecompleted": "הפעילות '{{$a}}' הושלמה.", - "addon.competency.evidence_courserestored": "הניקוד שוחזר יחד עם הקורס '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "קישור לאישור על לימוד מקדים של '{{$a}}' צורף.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "קישור לאישור על לימוד מקדים של '{{$a}}' הוסר.", - "addon.competency.evidence_manualoverride": "מצב השלמת המיומנות עודכן באופן ידני.", - "addon.competency.evidence_manualoverrideincourse": "מצב השלמת המיומנות עודכן באופן ידני בקורס '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "מצב השלמת המיומנות עודכן באופן ידני בתוכנית־הלימוד '{{$a}}'.", - "addon.competency.learningplancompetencies": "מיומנויות תוכנית־הלימוד", - "addon.competency.learningplans": "תוכניות־לימוד", - "addon.competency.myplans": "תוכניות הלימודים שלי", - "addon.competency.noactivities": "לא מקושר לאף פעילות בקורס", - "addon.competency.nocompetenciesincourse": "בקורס זה לא קיימות פעילויות אשר קושרו למיומנויות כלשהן.", - "addon.competency.nocrossreferencedcompetencies": "אף מיומנות לא מקושרת למיומנות זו.", - "addon.competency.noevidence": "טרם צורפה ראיה לבקיאות", - "addon.competency.noplanswerecreated": "טרם נוצרו תוכניות־לימוד.", - "addon.competency.path": "נתיב", - "addon.competency.planstatusactive": "פעיל", - "addon.competency.planstatuscomplete": "הושלם", - "addon.competency.planstatusdraft": "טיוטה", - "addon.competency.planstatusinreview": "בסקירה", - "addon.competency.planstatuswaitingforreview": "מחכה לסקירה", - "addon.competency.proficient": "בקיאות", - "addon.competency.progress": "התקדמות", - "addon.competency.rating": "דרוג", - "addon.competency.reviewstatus": "סקירת מצב", - "addon.competency.status": "מצב", - "addon.competency.template": "תבנית תוכנית־לימוד", - "addon.competency.uponcoursecompletion": "בעת השלמת קורס:", - "addon.competency.usercompetencystatus_idle": "לא־פעיל", - "addon.competency.usercompetencystatus_inreview": "בסקירה", - "addon.competency.usercompetencystatus_waitingforreview": "מחכה לסקירה", - "addon.competency.userplans": "תוכניות־לימוד", - "addon.competency.xcompetenciesproficientoutofy": "בקיאות ב {{$a.x}} מיומנויות מתוך {{$a.y}}", - "addon.competency.xcompetenciesproficientoutofyincourse": "רכשתם בקיאות ב {{$a.x}} מתוך {{$a.y}} המיומנויות בקורס זה.", - "addon.coursecompletion.complete": "השלמה", - "addon.coursecompletion.completecourse": "אישור השלמה קורס", - "addon.coursecompletion.completed": "הושלם", - "addon.coursecompletion.completiondate": "תאריך השלמה", - "addon.coursecompletion.completionmenuitem": "השלמה", - "addon.coursecompletion.coursecompletion": "תנאי השלמת קורס", - "addon.coursecompletion.criteria": "תנאי", - "addon.coursecompletion.criteriagroup": "קבוצת תנאים", - "addon.coursecompletion.criteriarequiredall": "כל התנאים המצויינים מטה נדרשים", - "addon.coursecompletion.criteriarequiredany": "לפחות אחד מהתנאים המצויינים מטה נדרשים", - "addon.coursecompletion.inprogress": "בתהליך", - "addon.coursecompletion.manualselfcompletion": "השלמה עצמאית ידנית", - "addon.coursecompletion.nottracked": "מנגנון מעקב השלמת פעילות לא פעיל עבורך (יש להירשם לקורס)", - "addon.coursecompletion.notyetstarted": "עדיין לא התחיל", - "addon.coursecompletion.pending": "בתהליך למידה", - "addon.coursecompletion.required": "דרוש", - "addon.coursecompletion.requiredcriteria": "תנאי נדרש", - "addon.coursecompletion.requirement": "דרישות", - "addon.coursecompletion.status": "מצב", - "addon.coursecompletion.viewcoursereport": "צפיה בדוח הקורס", - "addon.files.couldnotloadfiles": "לא ניתן לטעון את רשימת הקבצים.", - "addon.files.emptyfilelist": "אין קבצים להצגה.", - "addon.files.files": "קבצים", - "addon.files.privatefiles": "הקבצים שלי", - "addon.files.sitefiles": "קבצי האתר", - "addon.messages.addcontact": "הוספת איש קשר", - "addon.messages.addtofavourites": "סימון שיחה בכוכבית", - "addon.messages.addtoyourcontacts": "הוספה לרשימת אנשי־קשר", - "addon.messages.blocknoncontacts": "חסימת כל המסרים החדשים מאנשים שלא נמצאים ברשימת אנשי הקשר שלי", - "addon.messages.blockuser": "חסימת משתמש", - "addon.messages.blockuserconfirm": "האם לחסום את המשתמש {{$a}}?", - "addon.messages.contactableprivacy": "קבלת הודעות מ:", - "addon.messages.contactableprivacy_coursemember": "מרשימת החברים שלי וכל אחד מהקורס", - "addon.messages.contactableprivacy_onlycontacts": "מרשימת החברים שלי בלבד", - "addon.messages.contactableprivacy_site": "כל אחד מהמערכת", - "addon.messages.contactblocked": "משתמש חסום", - "addon.messages.contactlistempty": "רשימת אנשי הקשר ריקה", - "addon.messages.contactname": "שם איש קשר", - "addon.messages.contactrequestsent": "נשלחה בקשת הצטרפות כחבר", - "addon.messages.contacts": "אנשי קשר", - "addon.messages.conversationactions": "תפריט פעולות בשיחה", - "addon.messages.decline": "סרוב בקשת חברות", - "addon.messages.deleteallconfirm": "האם למחוק את כל השיחה?\n(זה לא ימחוק את השיחה מהמשתתפים האחרים)", - "addon.messages.deleteconversation": "מחיקת שיחה", - "addon.messages.errorwhileretrievingcontacts": "שגיאה בזמן טעינת אנשי קשר מהשרת.", - "addon.messages.errorwhileretrievingdiscussions": "שגיאה בזמן טעינת הדיונים מהשרת.", - "addon.messages.errorwhileretrievingmessages": "שגיאה בזמן טעינת המסרים מהשרת.", - "addon.messages.groupconversations": "קבוצה", - "addon.messages.groupinfo": "מידע אודות הקבוצה", - "addon.messages.individualconversations": "פרטי", - "addon.messages.info": "מידע אודות המשתמש", - "addon.messages.isnotinyourcontacts": "{{$a}} לא נמצא ברשימת החברים שלך", - "addon.messages.message": "מסר", - "addon.messages.messagenotsent": "מסר זה לא נשלח, אנא נסה שוב מאוחר יותר.", - "addon.messages.messagepreferences": "העדפות מסרים", - "addon.messages.messages": "מסרים", - "addon.messages.muteconversation": "הפסקת התראות", - "addon.messages.mutedconversation": "הפסקת התראות השיחה", - "addon.messages.newmessage": "הודעה חדשה", - "addon.messages.nocontactrequests": "אין בקשות הממתינות לאישור", - "addon.messages.nocontactsgetstarted": "רשימת החברים שלך ריקה", - "addon.messages.nofavourites": "אין שיחות המסומנות בכוכבית", - "addon.messages.nogroupconversations": "אין שיחות קבוצתיות", - "addon.messages.noindividualconversations": "אין שיחות פרטיות", - "addon.messages.nomessagesfound": "לא נמצאו מסרים", - "addon.messages.noncontacts": "שאינם אנשי קשר", - "addon.messages.nousersfound": "לא נמצאו משתמשים", - "addon.messages.numparticipants": "{{$a}} משתתפים", - "addon.messages.removecontact": "הסרת איש הקשר", - "addon.messages.removefromfavourites": "ביטול סימון השיחה", - "addon.messages.removefromyourcontacts": "הסרה מרשימת אנשי־הקשר", - "addon.messages.requests": "בקשות", - "addon.messages.requirecontacttomessage": "יש לפנות ל {{$a}} לקבלת אישור הצטרפות לאנשי הקשר שלך כדי לשלוח אליו/ה מסרים", - "addon.messages.searchcombined": "חיפוש משתמשים ומסרים", - "addon.messages.selfconversation": "אזור אישי", - "addon.messages.selfconversationdefaultmessage": "שמירת מסרים במצב טיוטה, קישורים, פתקים וכו' לשימוש עתידי.", - "addon.messages.sendcontactrequest": "שליחת בקשת הצטרפות לאנשי הקשר", - "addon.messages.type_blocked": "חסומים", - "addon.messages.type_offline": "לא מחוברים", - "addon.messages.type_online": "מחוברים", - "addon.messages.type_search": "תוצאות חיפוש", - "addon.messages.type_strangers": "אחרים", - "addon.messages.unabletomessage": "לא ניתן לשלוח הודעה למשתמש", - "addon.messages.unblockuser": "ביטול חסימת משתמש", - "addon.messages.unmuteconversation": "הפעלת התראות", - "addon.messages.useentertosend": "הקליק על מקש לשליחה", - "addon.messages.userwouldliketocontactyou": "{{$a}} מעוניין ליצור איתך קשר", - "addon.messages.you": "את/ה:", - "addon.messages.yourcontactrequestpending": "בקשת הצטרפות {{$a}} לרשימת אנשי הקשר ממתינה לאישור", - "addon.mod_assign.addattempt": "אפשר נסיון נוסף", - "addon.mod_assign.addnewattempt": "הוספת נסיון חדש", - "addon.mod_assign.addnewattemptfromprevious": "הוספת נסיון נוסף המבוסס על ההגשה האחרונה", - "addon.mod_assign.addsubmission": "הוספת הגשה", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "פרטי טופס המטלה וההגשה יהיו זמינים מ-\n{{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "אפשר הגשות מ", - "addon.mod_assign.allowsubmissionsfromdatesummary": "מטלה זו תאפשר הגשות החל מ- {{$a}}", - "addon.mod_assign.applytoteam": "החל ציונים ומשוב לכל הקבוצה", - "addon.mod_assign.assignmentisdue": "יש להגיש את המטלה", - "addon.mod_assign.attemptnumber": "נסיון מספר", - "addon.mod_assign.attemptreopenmethod": "מנגנון הגשה חוזרת", - "addon.mod_assign.attemptreopenmethod_manual": "ידני (בניהול המורה)", - "addon.mod_assign.attemptreopenmethod_untilpass": "אוטומטית עד ציון עובר", - "addon.mod_assign.attemptsettings": "הגדרות הנסיון", - "addon.mod_assign.confirmsubmission": "האם ברצונך להגיש את העבודה שלך עבור מתן ציון? לאחר ההגשה, לא ניתן יהיה לערוך שינויים נוספים.", - "addon.mod_assign.currentattempt": "זהו נסיון {{$a}}.", - "addon.mod_assign.currentattemptof": "זהו נסיון מספר {{$a.attemptnumber}} ( {{$a.maxattempts}} מנסיונות שאופשרו.", - "addon.mod_assign.currentgrade": "הציון הנוכחי בגליון הציונים", - "addon.mod_assign.cutoffdate": "מועד הגשה סופי", - "addon.mod_assign.defaultteam": "קבוצת בררת־המחדל", - "addon.mod_assign.duedate": "עד לתאריך", - "addon.mod_assign.duedateno": "אין תאריך הגשה", - "addon.mod_assign.duedatereached": "משך הזמן שהוגדר עבור מטלה זו חלף", - "addon.mod_assign.editingstatus": "מצב עריכה", - "addon.mod_assign.editsubmission": "עריכת ההגשה", - "addon.mod_assign.extensionduedate": "הארכת מועד הגשה", - "addon.mod_assign.grade": "ניקוד בפעילות", - "addon.mod_assign.graded": "נבדק", - "addon.mod_assign.gradedby": "נבדק על-ידי", - "addon.mod_assign.gradedon": "הציון ניתן על", - "addon.mod_assign.gradelocked": "הציון נעול או שעודכן בגליון הציונים", - "addon.mod_assign.gradeoutof": "ציון מתוך {{$a}}", - "addon.mod_assign.gradingstatus": "מצב מתן הציון", - "addon.mod_assign.groupsubmissionsettings": "הגדרות הגשה בקבוצות", - "addon.mod_assign.hiddenuser": "משתתף:", - "addon.mod_assign.latesubmissions": "הגשות באיחור", - "addon.mod_assign.latesubmissionsaccepted": "סטודנטים שנתנה להם הרשאה להארכה יכולים עדיין להגיש עד {{$a}}", - "addon.mod_assign.markingworkflowstate": "מצב מנגנון בדיקה מתקדם", - "addon.mod_assign.markingworkflowstateinmarking": "בתהליך בדיקה", - "addon.mod_assign.markingworkflowstateinreview": "בבדיקת המרצה", - "addon.mod_assign.markingworkflowstatenotmarked": "טרם נבדק", - "addon.mod_assign.markingworkflowstatereadyforrelease": "הניקוד ממתין להפצה", - "addon.mod_assign.markingworkflowstatereadyforreview": "בדיקה ראשונית הסתיימה", - "addon.mod_assign.markingworkflowstatereleased": "הניקוד זמין לסטודנט", - "addon.mod_assign.modulenameplural": "מטלות", - "addon.mod_assign.multipleteams": "חבר ביותר מקבוצה אחת", - "addon.mod_assign.noattempt": "אין נסיונות", - "addon.mod_assign.nomoresubmissionsaccepted": "מורשה רק למשתתפים שניתנה להם האפשרות להגשה מאוחרת", - "addon.mod_assign.noonlinesubmissions": "מטלה זו לא דורשת ממך להגיש דבר באופן מקוון", - "addon.mod_assign.nosubmission": "לא הוגש שום דבר עבור מטלה זו.", - "addon.mod_assign.noteam": "אינך חבר באף קבוצה, אנא פנה למורה.", - "addon.mod_assign.noteam_desc": "המטלה מחייבת הגשה בקבוצות. אינך חבר באף קבוצה, כך שלא ניתן להגיש במצב כזה. יש ליצור קשר עם המורה כדי להוסיפך לקבוצה.", - "addon.mod_assign.notgraded": "לא ניתן ציון", - "addon.mod_assign.numberofdraftsubmissions": "טיוטות", - "addon.mod_assign.numberofparticipants": "משתתפים", - "addon.mod_assign.numberofsubmissionsneedgrading": "דורש מתן ציון", - "addon.mod_assign.numberofsubmittedassignments": "הוגש", - "addon.mod_assign.numberofteams": "קבוצות", - "addon.mod_assign.numwords": "{{$a}} מילים", - "addon.mod_assign.outof": "{{$a.current}} מתוך ס\"ה {{$a.total}}", - "addon.mod_assign.overdue": "\nהמטלה באיחור של:\n{{$a}}", - "addon.mod_assign.submission": "הגשה", - "addon.mod_assign.submissioneditable": "סטודנטים יכולים לערוך הגשה זו", - "addon.mod_assign.submissionnoteditable": "סטודנטים אינם יכולים לערוך הגשה זו", - "addon.mod_assign.submissionslocked": "מטלה זו אינה מאפשרת הגשות", - "addon.mod_assign.submissionstatus": "מצב ההגשה", - "addon.mod_assign.submissionstatus_": "אין הגשה", - "addon.mod_assign.submissionstatus_draft": "מצב טיוטה ( לא הוגש)", - "addon.mod_assign.submissionstatus_marked": "נבדק", - "addon.mod_assign.submissionstatus_new": "אין הגשה", - "addon.mod_assign.submissionstatus_reopened": "נפתח מחדש", - "addon.mod_assign.submissionstatus_submitted": "הוגש למתן ציון", - "addon.mod_assign.submissionstatusheading": "מצב הגשה", - "addon.mod_assign.submissionteam": "קבוצה", - "addon.mod_assign.submitassignment": "הגשת מטלה", - "addon.mod_assign.submitassignment_help": "ברגע שהמטלה תוגש, לא ניתן יהיה לערוך שינויים נוספים", - "addon.mod_assign.submittedearly": "המטלה הוגשה {{$a}} לפני תאריך ההגשה הסופי.", - "addon.mod_assign.submittedlate": "המטלה הוגשה באחור של {{$a}}", - "addon.mod_assign.timemodified": "עדכון אחרון", - "addon.mod_assign.timeremaining": "הזמן שנותר", - "addon.mod_assign.ungroupedusers": "ההגדרה \"יש להיות חבר בקבוצה בכדי להגיש\" פעילה אך כמה משתמשים לא מוקצים לקבוצות, הדבר ימנע מהם מלהגיש את המטלות.", - "addon.mod_assign.unlimitedattempts": "לא מוגבל", - "addon.mod_assign.userswhoneedtosubmit": "משתמשים שצריכים להגיש: {{$a}}", - "addon.mod_assign.userwithid": "משתמש עם מספר-זיהוי {{id}}", - "addon.mod_assign.viewsubmission": "צפיה בהגשה", - "addon.mod_assign.wordlimit": "הגבלת מילים", - "addon.mod_assign_feedback_comments.pluginname": "משוב מרצה לסטודנטים", - "addon.mod_assign_feedback_editpdf.pluginname": "מתן הערות מקוונות למסמך PDF", - "addon.mod_assign_feedback_file.pluginname": "משוב כקובץ", - "addon.mod_assign_submission_comments.pluginname": "הערות סטודנט להגשה", - "addon.mod_assign_submission_file.pluginname": "הגשות קובץ", - "addon.mod_assign_submission_onlinetext.pluginname": "הגשות תוכן מקוון", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "הגבלת המילים עבור מטלה זו היא {{$a.limit}} מילים והינך מנסה להגיש {{$a.count}} מילים. יש לבדוק מחדש ולהגיש שוב לפי הגבלת המילים.", - "addon.mod_book.errorchapter": "שגיאה בעת קריאת הפרק", - "addon.mod_book.modulenameplural": "ספרים", - "addon.mod_book.navnexttitle": "הבא: {{$a}}", - "addon.mod_book.navprevtitle": "הקודם: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "פרקי הספר", - "addon.mod_book.toc": "תוכן עניינים", - "addon.mod_chat.beep": "ציפצוף", - "addon.mod_chat.chatreport": "מפגש רב-שיח מקוון", - "addon.mod_chat.currentusers": "משתמשים נוכחיים", - "addon.mod_chat.enterchat": "הקליקו כאן כדי להיכנס לרב-שיח הנוכחי", - "addon.mod_chat.entermessage": "הזנת ההודעה שלך", - "addon.mod_chat.messagebeepseveryone": "{{$a}} ציפצף לכולם!", - "addon.mod_chat.messagebeepsyou": "{{$a}} ציפצף לך הרגע!", - "addon.mod_chat.messageenter": "{{$a}} נכנס/ה לרב-שיח זה", - "addon.mod_chat.messageexit": "{{$a}} עזב/ה רב-שיח זה", - "addon.mod_chat.messages": "הודעות", - "addon.mod_chat.messageyoubeep": "צפצפת ל {{$a}}", - "addon.mod_chat.modulenameplural": "רבי־שיח", - "addon.mod_chat.mustbeonlinetosendmessages": "עליך להיות מחובר בכדי לשלוח הודעות.", - "addon.mod_chat.nomessages": "אין הודעות עדיין", - "addon.mod_chat.saidto": "אמר ל-", - "addon.mod_chat.send": "שליחה", - "addon.mod_chat.sessionstart": "מפגש הרב-שיח הבא יתחיל ב: {{$a.date}}, ({{$a.fromnow}} מעכשיו)", - "addon.mod_chat.talk": "דברו", - "addon.mod_chat.viewreport": "הצגת מפגשי רב-שיח ישנים", - "addon.mod_choice.cannotsubmit": "קרתה תקלה בשליחת הבחירה שלך, ניתן לנסות שנית מאוחר יותר.", - "addon.mod_choice.choiceoptions": "אפשרויות שאלת־סקר", - "addon.mod_choice.errorgetchoice": "שגיאה בטעינת המידע של שאלת הבחירה.", - "addon.mod_choice.expired": "מצטערים, פעילות זו נסגרה על {{$a}} והיא איננה זמינה יותר", - "addon.mod_choice.full": "(מלא)", - "addon.mod_choice.modulenameplural": "שאלות־סקר", - "addon.mod_choice.noresultsviewable": "כרגע לא ניתן לצפות בתוצאות.", - "addon.mod_choice.notopenyet": "לצערנו, פעילות זו אינה זמינה עד {{$a}}", - "addon.mod_choice.numberofuser": "מספר המשתתפים", - "addon.mod_choice.numberofuserinpercentage": "מספר המשתתפים באחוזים", - "addon.mod_choice.previewonly": "זוהי תצוגה מקדימה לאפשרויות הזמינות עבור פעילות זו. לא ניתן יהיה הגיש את הבחירה שלך עד {{$a}}.", - "addon.mod_choice.removemychoice": "הסרת בחירתי", - "addon.mod_choice.responses": "תשובות", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% מהמשתמשים בחרו באפשרות {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "תצוגה גרפית", - "addon.mod_choice.savemychoice": "שמירת הבחירה שלי", - "addon.mod_choice.userchoosethisoption": "משתמשים הבוחרים את האפשרות הזו", - "addon.mod_choice.yourselection": "הבחירה שלך", - "addon.mod_data.addentries": "הוספת פריטים", - "addon.mod_data.advancedsearch": "חיפוש מורחב", - "addon.mod_data.alttext": "תוכן חלופי", - "addon.mod_data.approve": "אישור", - "addon.mod_data.approved": "מאושר", - "addon.mod_data.ascending": "בסדר עולה", - "addon.mod_data.authorfirstname": "שם פרטי של המחבר", - "addon.mod_data.authorlastname": "שם משפחה של המחבר", - "addon.mod_data.confirmdeleterecord": "האם למחוק את הפריט הזה?", - "addon.mod_data.descending": "בסדר יורד", - "addon.mod_data.disapprove": "ביטול האישור", - "addon.mod_data.emptyaddform": "לא מילאת אף שדה!", - "addon.mod_data.entrieslefttoadd": "יש להזין {{$a.entriesleft}} פריטים נוספים בכדי להשלים פעילות זו", - "addon.mod_data.entrieslefttoaddtoview": "יש להזין {{$a.entrieslefttoview}} פריטים נוספים בכדי לראות פריטים של משתתפים אחרים.", - "addon.mod_data.errormustsupplyvalue": "יש להזין ערך בשדה זה.", - "addon.mod_data.expired": "מצטערים, פעילות זו נסגרה ב {{$a}} ואיננה זמינה יותר.", - "addon.mod_data.fields": "שדות", - "addon.mod_data.foundrecords": "נמצאו פריטים: {{$a.num}}/{{$a.max}} (איפוס מסננים)", - "addon.mod_data.latlongboth": "נדרשים נתוני קו־רוחב וגם קו־אורך", - "addon.mod_data.menuchoose": "יש לבחור...", - "addon.mod_data.modulenameplural": "בסיסי־נתונים", - "addon.mod_data.more": "עוד", - "addon.mod_data.nomatch": "לא נמצאו פריטים מתאימים!", - "addon.mod_data.norecords": "אין פריטים בבסיס הנתונים", - "addon.mod_data.notapproved": "הפריט עדיין לא אושר.", - "addon.mod_data.notopenyet": "מצטערים, פעילות זו איננה זמינה עד {{$a}} .", - "addon.mod_data.numrecords": "{{$a}} פריטים", - "addon.mod_data.other": "אחר", - "addon.mod_data.recordapproved": "הפריט אושר", - "addon.mod_data.recorddeleted": "הפריט נמחק", - "addon.mod_data.recorddisapproved": "הפריט לא מאושר", - "addon.mod_data.resetsettings": "איפוס שדות", - "addon.mod_data.search": "חיפוש", - "addon.mod_data.selectedrequired": "כל הנבחרים דרושים", - "addon.mod_data.single": "תצוגת פריט", - "addon.mod_data.timeadded": "זמן הוספה", - "addon.mod_data.timemodified": "זמן עדכון", - "addon.mod_data.usedate": "כלול בחיפוש", - "addon.mod_feedback.analysis": "ניתוח", - "addon.mod_feedback.anonymous": "אנונימי", - "addon.mod_feedback.anonymous_entries": "משובים אנונימיים ({{$a}})", - "addon.mod_feedback.average": "ממוצע", - "addon.mod_feedback.complete_the_form": "מענה על השאלות...", - "addon.mod_feedback.completed_feedbacks": "תשובות אשר הוגשו", - "addon.mod_feedback.continue_the_form": "המשך השאלון", - "addon.mod_feedback.feedback_is_not_open": "שאלון־מותנה זה איננו זמין כעת", - "addon.mod_feedback.feedbackclose": "אפשר תשובות ל", - "addon.mod_feedback.feedbackopen": "אפשרת תשובות מ", - "addon.mod_feedback.mapcourses": "שיוך שאלון־מותנה עבור הקורסים", - "addon.mod_feedback.maximal": "מירבי", - "addon.mod_feedback.minimal": "מיזערי", - "addon.mod_feedback.mode": "מצב", - "addon.mod_feedback.modulenameplural": "שאלון־מותנה", - "addon.mod_feedback.next_page": "העמוד הבא", - "addon.mod_feedback.non_anonymous": "שמות המשתמשים ישמרו ויוצגו יחד עם התשובות", - "addon.mod_feedback.non_anonymous_entries": "לא ניתן לענות באופן אנונימי (לא מזוהה)", - "addon.mod_feedback.non_respondents_students": "לא קיימות תגובות של סטודנטים", - "addon.mod_feedback.not_selected": "לא נבחר", - "addon.mod_feedback.not_started": "לא התחיל", - "addon.mod_feedback.overview": "סקירה", - "addon.mod_feedback.page_after_submit": "הודעת השלמה", - "addon.mod_feedback.preview": "תצוגה מקדימה", - "addon.mod_feedback.previous_page": "העמוד הקודם", - "addon.mod_feedback.questions": "שאלות", - "addon.mod_feedback.response_nr": "מספר תגובה", - "addon.mod_feedback.responses": "תגובות", - "addon.mod_feedback.save_entries": "הגשת תשובותיך", - "addon.mod_feedback.show_entries": "הצגת תגובות", - "addon.mod_feedback.show_nonrespondents": "הצגת שאלות ללא התגובות", - "addon.mod_feedback.started": "התחיל", - "addon.mod_feedback.this_feedback_is_already_submitted": "השלמתם פעילות זו, בעבר.", - "addon.mod_folder.emptyfilelist": "אין קבצים להציג.", - "addon.mod_folder.modulenameplural": "תקיות קבצים", - "addon.mod_forum.addanewdiscussion": "הוספת נושא דיון חדש", - "addon.mod_forum.addanewquestion": "הוספת שאלה חדשה", - "addon.mod_forum.addanewtopic": "הוספת נושא חדש", - "addon.mod_forum.addtofavourites": "סימון הדיון", - "addon.mod_forum.advanced": "הגדרות נוספות", - "addon.mod_forum.cannotadddiscussion": "הוספת דיונים לפורום זה דורשת השתייכות לקבוצה.", - "addon.mod_forum.cannotadddiscussionall": "אין לך הרשאות להוספת נושא דיון חדש לכלל המשתתפים.", - "addon.mod_forum.cannotcreatediscussion": "הוספת דיון חדש לא צלחה", - "addon.mod_forum.couldnotadd": "ההודעה שלך לא פורסמה עקב תקלה בלתי מזוהה", - "addon.mod_forum.couldnotupdate": "עדכון ההודעה שלך נכשל עקב בעיה לא מזוהה", - "addon.mod_forum.delete": "מחיקה", - "addon.mod_forum.deletedpost": "הודעה זו נמחקה", - "addon.mod_forum.deletesure": "האם למחוק הודעה זו?", - "addon.mod_forum.discussion": "דיון", - "addon.mod_forum.discussionlistsortbycreatedasc": "מיון בסדר עולה לפי תאריך יצירה", - "addon.mod_forum.discussionlistsortbycreateddesc": "מיון בסדר יורד לפי תאריך יצירה", - "addon.mod_forum.discussionlistsortbylastpostasc": "מיון בסדר עולה לפי תאריך יצירת דיון", - "addon.mod_forum.discussionlistsortbyrepliesasc": "מיון בסדר עולה לפי מספר תגובות", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "מיון בסדר יורד לפי מספר תגובות", - "addon.mod_forum.discussionlocked": "הדיון ננעל, כך שלא ניתן יותר להגיב אליו.", - "addon.mod_forum.discussionpinned": "בראש הרשימה", - "addon.mod_forum.discussionsubscription": "מנוי לעדכונים בדיון", - "addon.mod_forum.edit": "עריכה", - "addon.mod_forum.erroremptymessage": "הודעת הפרסום איננה יכולה להיות ריקה", - "addon.mod_forum.erroremptysubject": "הנושא הפרסום אינו יכול להיות ריק", - "addon.mod_forum.errorgetforum": "שגיאה בטעינת מידע הפורום.", - "addon.mod_forum.errorgetgroups": "שגיאה בטעינת הגדרות קבוצה.", - "addon.mod_forum.forumnodiscussionsyet": "עדיין לא קיימים נושאי דיונים בפורום זה.", - "addon.mod_forum.group": "קבוצה", - "addon.mod_forum.lastpost": "הודעה אחרונה", - "addon.mod_forum.lockdiscussion": "נעילת הדיון", - "addon.mod_forum.message": "הודעה", - "addon.mod_forum.modeflatnewestfirst": "הצגת תגובות בצורה שטוחה, החדשות ביותר ראשונות", - "addon.mod_forum.modeflatoldestfirst": "הצגת תגובות בצורה שטוחה, הישנות ביותר ראשונות", - "addon.mod_forum.modenested": "הצגת תגובות באופן מקונן", - "addon.mod_forum.modulenameplural": "פורומים", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} דיונים", - "addon.mod_forum.numreplies": "{{numreplies}} תגובות", - "addon.mod_forum.pindiscussion": "קיבוע דיון בראש הרשימה", - "addon.mod_forum.postisprivatereply": "תגובה זו פורסמה באופן פרטי. היא איננה מוצגת למשתתפים אחרים.", - "addon.mod_forum.posttoforum": "שליחת הודעה לפורום", - "addon.mod_forum.posttomygroups": "פרסום העתק לכל הקבוצות", - "addon.mod_forum.privatereply": "תגובה באופן פרטי", - "addon.mod_forum.re": "תשובה ל:", - "addon.mod_forum.removefromfavourites": "ביטול סימון הדיון", - "addon.mod_forum.reply": "תגובה", - "addon.mod_forum.replyplaceholder": "הזינו את תגובתכם...", - "addon.mod_forum.subject": "נושא", - "addon.mod_forum.tagarea_forum_posts": "פרסומי פורום", - "addon.mod_forum.unlockdiscussion": "ביטול נעילת הדיון", - "addon.mod_forum.unpindiscussion": "ביטול קיבוע הדיון", - "addon.mod_forum.unread": "לא נקרא", - "addon.mod_forum.unreadpostsnumber": "{{$a}} הודעות שלא נקראו", - "addon.mod_forum.yourreply": "התגובה שלך", - "addon.mod_glossary.addentry": "הוספת מונח חדש", - "addon.mod_glossary.aliases": "מילות מפתח", - "addon.mod_glossary.attachment": "קובץ מצורף", - "addon.mod_glossary.casesensitive": "מונח זה תלוי אותיות רישיות", - "addon.mod_glossary.categories": "הסיווגים", - "addon.mod_glossary.concept": "מונח", - "addon.mod_glossary.definition": "הגדרה", - "addon.mod_glossary.entryusedynalink": "יש ליצור קישורים באופן אוטומטי למונח זה", - "addon.mod_glossary.errconceptalreadyexists": "מושג זה כבר קיים. באגרון מונחים זה לא ניתן להכניס ערכים כפולים.", - "addon.mod_glossary.fillfields": "יש למלא את שדות המושג וההגדרה.", - "addon.mod_glossary.fullmatch": "התאם מילים שלמות בלבד", - "addon.mod_glossary.linking": "יצירת קישורים באופן אוטומטי", - "addon.mod_glossary.modulenameplural": "אגרוני מונחים", - "addon.mod_glossary.tagarea_glossary_entries": "מונחי אגרון המונחים", - "addon.mod_imscp.deploymenterror": "שגיאה בחבילת התוכן", - "addon.mod_imscp.modulenameplural": "חבילות תוכן IMS", - "addon.mod_imscp.showmoduledescription": "הצגת תיאור", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "תשובה", - "addon.mod_lesson.attempt": "ניסיון: {{$a}}", - "addon.mod_lesson.attemptheader": "ניסיון", - "addon.mod_lesson.attemptsremaining": "נשארו לך עוד {{$a}} ניסיונות", - "addon.mod_lesson.averagescore": "תוצאה ממוצעת", - "addon.mod_lesson.averagetime": "זמן ממוצע", - "addon.mod_lesson.branchtable": "עמוד תוכן מסתעף", - "addon.mod_lesson.cannotfindattempt": "שגיאה: הניביון לא נמצא", - "addon.mod_lesson.cannotfinduser": "שגיאה: לא ניתן היה למצוא משתמשים", - "addon.mod_lesson.clusterjump": "שאלת אנסין בתוך אשכול", - "addon.mod_lesson.completed": "הושלם", - "addon.mod_lesson.congratulations": "איחולים לבבים - הגעת לסוף השיעור", - "addon.mod_lesson.continue": "המשך", - "addon.mod_lesson.continuetonextpage": "המשך לעמוד הבא.", - "addon.mod_lesson.defaultessayresponse": "המאמר שלך ייבדק על ידי המדריך של הקורס.", - "addon.mod_lesson.detailedstats": "סטטיסטיקה", - "addon.mod_lesson.didnotanswerquestion": "לא ענה על השאלה.", - "addon.mod_lesson.displayofgrade": "תצוגת הציון (לתלמידים בלבד)", - "addon.mod_lesson.displayscorewithessays": "עבור השאלות שנבדקות באופן אוטומטי קיבלת {{$a.score}} נקודות מתוך {{$a.tempmaxgrade}} .
                \nשאלת(ות) החיבור {{$a.essayquestions}} שלך תבדק, והציון שתקבל עבורה יתווסף
                במועד מאוחר יותר לחישוב של הציון הסופי שלך.

                \nהציון הנוכחי שלך, מבלי שאלת(ות) החיבור הוא תוצאה של {{$a.score}} מתוך {{$a.grade}}.", - "addon.mod_lesson.displayscorewithoutessays": "התוצאה שלך היא {{$a.score}} (מתוך {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "הסיסמה איננה יכולה להיות ריקה", - "addon.mod_lesson.enterpassword": "בבקשה הכנס את הסיסמה:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "לא ענית על אף שאלה. קיבלת ציון 0 עבור שיעור זה.", - "addon.mod_lesson.finish": "סיום", - "addon.mod_lesson.firstwrong": "למרבה הצער, אינך יכול לזכות בנקודה זו מפני שהתשובה שלך היתה שגויה. האם תרצה להמשיך לנחש בשביל האושר שכרוך בלמידה (אבל ללא נקודות נוספות)?", - "addon.mod_lesson.gotoendoflesson": "המשך לסוף השיעור", - "addon.mod_lesson.grade": "ציון", - "addon.mod_lesson.highscore": "תוצאה גבוהה", - "addon.mod_lesson.hightime": "זמן גבוה", - "addon.mod_lesson.leftduringtimed": "עזבת באמצע שיעור מתוזמן.
                אנא הקליקו על 'המשך' בשביל להתחיל מחדש את השיעור.", - "addon.mod_lesson.leftduringtimednoretake": "עזבת באמצע שיעור מתוזמן
                ואינך מורשה לחזור עליו או להמשיך אותו.", - "addon.mod_lesson.lessonmenu": "תפריט השיעור", - "addon.mod_lesson.lessonstats": "הנתונים הסטטיסטיים של השיעורים.", - "addon.mod_lesson.linkedmedia": "מדיה מקושרת", - "addon.mod_lesson.loginfail": "ההתחברות נכשלה, אנא נסה שנית.....", - "addon.mod_lesson.lowscore": "תוצאה נמוכה.", - "addon.mod_lesson.lowtime": "זמן נמוך", - "addon.mod_lesson.maximumnumberofattemptsreached": "הושג המספר המירבי של נסיונות - עובר לעמוד הבא", - "addon.mod_lesson.modattemptsnoteacher": "סקירת סטודנטים עובדת רק בשביל סטודנטים.", - "addon.mod_lesson.modulenameplural": "שיעורים", - "addon.mod_lesson.noanswer": "לא ניתנה תשובה", - "addon.mod_lesson.nolessonattempts": "לא נעשו נסיונות מענה לשיעור זה.", - "addon.mod_lesson.notcompleted": "לא גמור", - "addon.mod_lesson.numberofcorrectanswers": "מספר התשובות הנכונות: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "מספר השאלות שנענו: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "מספר התשובות שנענו: {{$a.nquestions}}; (עליך לענות על {{$a.minquestions}} שאלות לפחות.)", - "addon.mod_lesson.ongoingcustom": "הרווחת {{$a.score}} נקודה(ות) מתוך {{$a.currenthigh}} נקודה(ות) עד כה.", - "addon.mod_lesson.ongoingnormal": "ענית על {{$a.correct}} נכונה מתוך {{$a.viewed}} ניסיונות.", - "addon.mod_lesson.or": "או", - "addon.mod_lesson.overview": "סקירה כללית", - "addon.mod_lesson.preview": "תצוגה מקדימה", - "addon.mod_lesson.progressbarteacherwarning2": "לא ניתן לראות את סרגל־ההתקדמות מפני שיש לך יכולת עריכה של שיעור זה", - "addon.mod_lesson.progresscompleted": "{{$a}}% מהשיעור הושלמו", - "addon.mod_lesson.question": "שאלה", - "addon.mod_lesson.rawgrade": "ציון גולמי", - "addon.mod_lesson.reports": "דוחות", - "addon.mod_lesson.response": "תגובה", - "addon.mod_lesson.review": "סקירה", - "addon.mod_lesson.reviewlesson": "סקירת השיעור", - "addon.mod_lesson.reviewquestionback": "כן, הייתי רוצה לנסות בשנית", - "addon.mod_lesson.reviewquestioncontinue": "לא, אני רוצה להמשיך לשאלה הבאה", - "addon.mod_lesson.secondpluswrong": "לא בדיוק. תרצה לנסות שוב?", - "addon.mod_lesson.submit": "הגש", - "addon.mod_lesson.teacherjumpwarning": "שיעור זה משתמש באשכול־מעברים {{$a.cluster}} או במעברים {{$a.unseen}}. כחלופה, יעשה שימוש במעבר ל'עמוד הבא'. יש להתחבר כסטודנט על מנת לבחון את התנהגות מעבר העמודים לעיל.", - "addon.mod_lesson.teacherongoingwarning": "תוצאה מצטברת מוצגת רק בפני הסטודנט. התחבר כסטודנט כדי לבחון את התוצאה המתמשכת.", - "addon.mod_lesson.teachertimerwarning": "שעון העצר עובד רק לסטודנטים. התחבר כסטודנט כדי לבחון את שעון העצר.", - "addon.mod_lesson.thatsthecorrectanswer": "זוהי התשובה הנכונה.", - "addon.mod_lesson.thatsthewronganswer": "זוהי לא התשובה הנכונה.", - "addon.mod_lesson.timeremaining": "הזמן שנשאר", - "addon.mod_lesson.timetaken": "זמן שנלקח", - "addon.mod_lesson.unseenpageinbranch": "שאלת אנסין בתוך ההסתעפות", - "addon.mod_lesson.welldone": "כל הכבוד!", - "addon.mod_lesson.youhaveseen": "כבר ראית יותר מעמוד אחד בשיעור זה.
                האם תרצה להתחיל בעמוד האחרון שראית?", - "addon.mod_lesson.youranswer": "התשובה שלך", - "addon.mod_lesson.yourcurrentgradeisoutof": "הציון הנוכחי שלך הוא {{$a.grade}} מתוך {{$a.total}}.", - "addon.mod_lesson.youshouldview": "על התשובה שלך להיות לפחות: {{$a}}", - "addon.mod_lti.errorgetlti": "שגיאה בטעינת מידע המודול.", - "addon.mod_lti.modulenameplural": "כלים/תכנים חיצוניים (LTI)", - "addon.mod_page.errorwhileloadingthepage": "שגיאה בזמן טעינת תוכן הדף.", - "addon.mod_page.modulenameplural": "דפים", - "addon.mod_quiz.answercolon": "תשובה:", - "addon.mod_quiz.attemptfirst": "ניסיון מענה ראשון", - "addon.mod_quiz.attemptlast": "נסיון מענה אחרון", - "addon.mod_quiz.attemptnumber": "נסיון מענה", - "addon.mod_quiz.attemptquiznow": "התחלת ניסיון מענה", - "addon.mod_quiz.attemptstate": "מצב", - "addon.mod_quiz.clearchoice": "איפוס הבחירה שלי", - "addon.mod_quiz.comment": "הערה", - "addon.mod_quiz.completedon": "הושלם ב-", - "addon.mod_quiz.confirmclose": "אתה עומד להגיש באופן סופי את ניסיון מענה זה. מרגע שהגשת וסיימת את ניסיון המענה לא תוכל לשנות את תשובותיך.", - "addon.mod_quiz.confirmstart": "לבוחן זה יש זמן מוגבל של {{$a}}. שעון העצר יזוז לאחור מרגע תחילת נסיון המענה. עליך להגיש לפני שהזמן מסתיים. האם להתחיל את נסיון המענה לבוחן?", - "addon.mod_quiz.confirmstartheader": "בוחן מתוזמן", - "addon.mod_quiz.continueattemptquiz": "המשך נסיון המענה האחרון שלך", - "addon.mod_quiz.continuepreview": "המשך בצפיה המוקדמת האחרונה", - "addon.mod_quiz.feedback": "משוב", - "addon.mod_quiz.finishattemptdots": "סיום נסיון מענה...", - "addon.mod_quiz.grade": "ציון", - "addon.mod_quiz.gradeaverage": "ציון ממוצע", - "addon.mod_quiz.gradehighest": "הציון הגבוה ביותר", - "addon.mod_quiz.grademethod": "שיטת מתן ציונים", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "נקודות", - "addon.mod_quiz.modulenameplural": "בחנים", - "addon.mod_quiz.mustbesubmittedby": "יש צורך להגיש את נסיון מענה זה", - "addon.mod_quiz.noquestions": "בשלב זה, יש להוסיף שאלות לבוחן על מנת שאפשר יהיה להציג אותו בפני התלמידים", - "addon.mod_quiz.noreviewattempt": "אין הרשאה לצפות בנסיון מענה זה", - "addon.mod_quiz.notyetgraded": "לא ניתן ציון עדיין", - "addon.mod_quiz.outof": "{{$a.grade}} מתוך ציון מירבי של {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} מתוך הציון המירבי {{$a.maxgrade}}({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "הערכה מילולית מסכמת לבוחן", - "addon.mod_quiz.overdue": "איחר את המועד", - "addon.mod_quiz.overduemustbesubmittedby": "עבר זמן ההגשה של בוחן זה. אם אתם מעוניינים לקבל ציון, עליכם להגישו ב- {{$a}}. במידה ולא תגישו עד אז, אף ניקוד על שאלות בבוחן לא יחושב.", - "addon.mod_quiz.preview": "תצוגה מקדימה", - "addon.mod_quiz.previewquiznow": "צפיה מוקדמת בבוחן", - "addon.mod_quiz.question": "שאלה", - "addon.mod_quiz.quiznavigation": "ניווט בוחן", - "addon.mod_quiz.quizpassword": "סיסמת הבוחן", - "addon.mod_quiz.reattemptquiz": "התחלת ניסיון מענה חדש של הבוחן", - "addon.mod_quiz.requirepasswordmessage": "חובה עליך לדעת את הסיסמה כדי לנסות ולפתור את בוחן זה.", - "addon.mod_quiz.returnattempt": "בחזרה לנסיון המענה", - "addon.mod_quiz.review": "עיון בתשובות", - "addon.mod_quiz.reviewofattempt": "עיון בתשובות שניתנו בניסיון מענה $a", - "addon.mod_quiz.reviewofpreview": "סקירת תצוגה־מקדימה", - "addon.mod_quiz.showall": "הצגת כל השאלות בעמוד אחד", - "addon.mod_quiz.showeachpage": "הצגת עמוד אחד בכל פעם", - "addon.mod_quiz.startattempt": "תחילת נסיון המענה", - "addon.mod_quiz.startedon": "התחיל ב:", - "addon.mod_quiz.stateabandoned": "טרם הוגש", - "addon.mod_quiz.statefinished": "הסתיים", - "addon.mod_quiz.statefinisheddetails": "{{$a}} הוגש", - "addon.mod_quiz.stateinprogress": "בתהליך", - "addon.mod_quiz.stateoverdue": "באיחור", - "addon.mod_quiz.stateoverduedetails": "מוכרח להיות מוגש עד {{$a}}", - "addon.mod_quiz.status": "מצב", - "addon.mod_quiz.submitallandfinish": "הגשה סופית, וסיום ניסיון המענה", - "addon.mod_quiz.summaryofattempt": "סיכום הנסיון", - "addon.mod_quiz.summaryofattempts": "סיכום ניסיונות המענה הקודמים שלך", - "addon.mod_quiz.timeleft": "זמן נותר", - "addon.mod_quiz.timetaken": "הזמן שלקח", - "addon.mod_quiz.yourfinalgradeis": "ציונך הסופי בבוחן זה הוא {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "שגיאה בזמן טעינת התוכן.", - "addon.mod_resource.modifieddate": "{{$a}} השתנה", - "addon.mod_resource.modulenameplural": "קבצים", - "addon.mod_resource.openthefile": "הצגת הקובץ", - "addon.mod_resource.uploadeddate": "{{$a}} הועלה", - "addon.mod_scorm.asset": "נכס", - "addon.mod_scorm.assetlaunched": "משאב - נצפה", - "addon.mod_scorm.attempts": "ניסיונות", - "addon.mod_scorm.averageattempt": "ממוצע ניסיונות", - "addon.mod_scorm.browse": "תצוגה מקדימה", - "addon.mod_scorm.browsed": "נצפה", - "addon.mod_scorm.browsemode": "מצב תצוגה מקדימה", - "addon.mod_scorm.completed": "הושלם", - "addon.mod_scorm.contents": "תוכן", - "addon.mod_scorm.enter": "הפעלת הלומדה", - "addon.mod_scorm.exceededmaxattempts": "הגעת למספר המירבי המותר של נסיונות.", - "addon.mod_scorm.failed": "נכשל", - "addon.mod_scorm.firstattempt": "ניסיון מענה ראשון", - "addon.mod_scorm.gradeaverage": "ממוצע הניקוד בניסיונות המענה", - "addon.mod_scorm.gradeforattempt": "הניקוד לנסיון", - "addon.mod_scorm.gradehighest": "הניקוד הגבוה ביותר", - "addon.mod_scorm.grademethod": "שיטת מתן ניקוד", - "addon.mod_scorm.gradereported": "הניקוד הסופי", - "addon.mod_scorm.gradescoes": "רכיבי למידה", - "addon.mod_scorm.gradesum": "סיכום הניקוד בניסיונות המענה", - "addon.mod_scorm.highestattempt": "ניסיון המענה בעל הניקוד הגבוה ביותר", - "addon.mod_scorm.incomplete": "התחיל וטרם הסתיים", - "addon.mod_scorm.lastattempt": "ניסיון מענה אחרון שהושלם", - "addon.mod_scorm.modulenameplural": "חבילות לומדה - SCORM", - "addon.mod_scorm.newattempt": "התחלת ניסיון מענה חדש", - "addon.mod_scorm.noattemptsallowed": "מספר הנסיונות המותר", - "addon.mod_scorm.noattemptsmade": "מספר הנסיונות שביצעת", - "addon.mod_scorm.notattempted": "טרם החל", - "addon.mod_scorm.organizations": "רצפי־למידה", - "addon.mod_scorm.passed": "עבר", - "addon.mod_scorm.reviewmode": "מצב עיון בתשובות", - "addon.mod_scorm.score": "תוצאה", - "addon.mod_scorm.suspended": "מושהה", - "addon.mod_scorm.toc": "תוכן ענינים", - "addon.mod_survey.cannotsubmitsurvey": "מתנצלים, היתה בעיה בהגשת הסקר שלך. אנא נסה שוב.", - "addon.mod_survey.errorgetsurvey": "שגיאה בטעינת מידע אודות הסקר.", - "addon.mod_survey.ifoundthat": "מצאתי ש-", - "addon.mod_survey.ipreferthat": "אני מעדיף ש-", - "addon.mod_survey.modulenameplural": "תבניות סקרים מובנות", - "addon.mod_survey.responses": "תגובות", - "addon.mod_survey.results": "תוצאות", - "addon.mod_survey.surveycompletednograph": "השלמת את הסקר.", - "addon.mod_url.accessurl": "גישה לקישור", - "addon.mod_url.modulenameplural": "כתובות אינטרנט", - "addon.mod_url.pointingtourl": "משאב קישור זה מפנה אל", - "addon.mod_wiki.cannoteditpage": "לא ניתן לערוך עמוד זה", - "addon.mod_wiki.createpage": "יצירת דף חדש", - "addon.mod_wiki.editingpage": "עריכת עמוד זה {{$a}}", - "addon.mod_wiki.map": "דפים מיוחדים", - "addon.mod_wiki.modulenameplural": "ויקי(ם)", - "addon.mod_wiki.newpagehdr": "דף חדש", - "addon.mod_wiki.newpagetitle": "כותרת דף חדש", - "addon.mod_wiki.nocontent": "אין תוכן לדף זה", - "addon.mod_wiki.notingroup": "לא בקבוצה", - "addon.mod_wiki.pageexists": "הדף כבר קיים. הפנה אליו מחדש.", - "addon.mod_wiki.pagename": "שם העמוד", - "addon.mod_wiki.tagarea_wiki_pages": "דפי הויקי", - "addon.mod_wiki.wrongversionlock": "משתמש אחר ערך את הדף הזה בזמן שאת/ה ערכת/ה. התוכן שלך לא ישמר.", - "addon.mod_workshop.alreadygraded": "כבר ניתן ציון", - "addon.mod_workshop.areainstructauthors": "הוראות להגשות", - "addon.mod_workshop.areainstructreviewers": "הוראות להערכה", - "addon.mod_workshop.assess": "ביצוע הערכה", - "addon.mod_workshop.assessedsubmission": "הערכת הגשה", - "addon.mod_workshop.assessmentform": "טופס הערכה", - "addon.mod_workshop.assessmentsettings": "הגדרות ההערכה", - "addon.mod_workshop.assessmentweight": "משקל ההערכה", - "addon.mod_workshop.assignedassessments": "הגשות להן נדרשת הערכה ומתן ציון", - "addon.mod_workshop.assignedassessmentsnone": "אין לך הקצאה של הגשות להערכה", - "addon.mod_workshop.conclusion": "סיכום", - "addon.mod_workshop.createsubmission": "התחלת ההגשה שלך", - "addon.mod_workshop.deletesubmission": "מחיקת הגשה", - "addon.mod_workshop.editsubmission": "עריכת הגשה", - "addon.mod_workshop.feedbackauthor": "משוב למחבר", - "addon.mod_workshop.feedbackby": "משוב על־ידי: {{$a}}", - "addon.mod_workshop.feedbackreviewer": "משוב למעריך", - "addon.mod_workshop.givengrades": "הציונים שהתקבלו מהסטודנטים", - "addon.mod_workshop.gradecalculated": "ציון מחושב להגשה", - "addon.mod_workshop.gradeinfo": "ציון: {{$a.received}} מ: {{$a.max}}", - "addon.mod_workshop.gradeover": "ציון עוקף להגשה", - "addon.mod_workshop.gradesreport": "דוח ציוני פעילות הערכת עמיתים", - "addon.mod_workshop.gradinggrade": "ציון ההערכה", - "addon.mod_workshop.gradinggradecalculated": "ציון מחושב להערכה", - "addon.mod_workshop.gradinggradeof": "ציון להערכה ({{$a}})", - "addon.mod_workshop.gradinggradeover": "ציון עוקף להערכה", - "addon.mod_workshop.modulenameplural": "הערכות־עמיתים", - "addon.mod_workshop.nogradeyet": "טרם ניתן ציון", - "addon.mod_workshop.notassessed": "טרם הוערך", - "addon.mod_workshop.notoverridden": "לא עודכן", - "addon.mod_workshop.noyoursubmission": "עדיין לא הגשת את עבודתך", - "addon.mod_workshop.overallfeedback": "משוב כללי", - "addon.mod_workshop.publishedsubmissions": "הגשות שפורסמו", - "addon.mod_workshop.publishsubmission": "פרסום ההגשה", - "addon.mod_workshop.publishsubmission_help": "הגשות שפורסמו זמינות לאחרים כאשר פעילות הערכת עמיתים מסתיימת.", - "addon.mod_workshop.reassess": "הערכה מחדש", - "addon.mod_workshop.receivedgrades": "ציונים שניתנו לסטודנטים", - "addon.mod_workshop.submissionattachment": "נספח", - "addon.mod_workshop.submissioncontent": "תוכן ההגשה", - "addon.mod_workshop.submissiondeleteconfirm": "האם בטוח כי ברצונך למחוק את ההגשה הבאה?", - "addon.mod_workshop.submissiongrade": "ציון ההגשה", - "addon.mod_workshop.submissiongradeof": "ציון ההגשה (מתוך {{$a}})", - "addon.mod_workshop.submissionsreport": "דוח הגשות", - "addon.mod_workshop.submissiontitle": "כותרת", - "addon.mod_workshop.switchphase10": "הקליקו, למעבר לשלב ההכנה", - "addon.mod_workshop.switchphase20": "הקליקו, למעבר לשלב ההגשה", - "addon.mod_workshop.switchphase30": "הקליקו, למעבר לשלב הערכות", - "addon.mod_workshop.switchphase40": "הקליקו, למעבר לשלב חישוב הציונים", - "addon.mod_workshop.switchphase50": "סגירת הערכת העמיתים", - "addon.mod_workshop.userplan": "טבלת תכנון הערכת עמיתים", - "addon.mod_workshop.userplancurrentphase": "שלב נוכחי", - "addon.mod_workshop.weightinfo": "משקל: {{$a}}", - "addon.mod_workshop.yourassessment": "הערכתך", - "addon.mod_workshop.yourassessmentfor": "ההערכה שלך עבור phases", - "addon.mod_workshop.yourgrades": "הציונים שלך", - "addon.mod_workshop.yoursubmission": "ההגשה שלך", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "הערה ל{{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "ציון ל {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "אמת־מידה {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "עליך לבחור ניקוד עבור אמת־מידה זו", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "הערות ל {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "אמת־מידה {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "הערות ל {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "ציון ל {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "טענה {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "אמת־מידה {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "עליך לבחור באחד מהפריטים האלו ", - "addon.notes.addnewnote": "הוספת הערה חדשה", - "addon.notes.coursenotes": "הערות על הסטודנט הזמינות לכל המורים בקורס ", - "addon.notes.deleteconfirm": "למחוק הערה זו?", - "addon.notes.eventnotecreated": "הערה נוצרה", - "addon.notes.eventnotedeleted": "הערה נמחקה", - "addon.notes.nonotes": "אין הערות מורה", - "addon.notes.note": "הערה", - "addon.notes.notes": "הערות המורה", - "addon.notes.personalnotes": "הערות על הסטודנט הזמינות לך בלבד", - "addon.notes.publishstate": "הקשר", - "addon.notes.sitenotes": "הערות על הסטודנט הזמינות לכל המורים במערכת", - "addon.notes.userwithid": "משתמש עם מספר-זיהוי {{id}}", - "addon.notifications.errorgetnotifications": "שגיאה בטעינת התראות", - "addon.notifications.markallread": "סימון הכל כ\"נקרא\"", - "addon.notifications.notificationpreferences": "העדפות הודעות", - "addon.notifications.notifications": "התראות", - "addon.notifications.therearentnotificationsyet": "אין התראות", - "assets.countries.AD": "אנדורה", - "assets.countries.AE": "מדינות ערב המאוחדות", - "assets.countries.AF": "אפגניסטן", - "assets.countries.AG": "אנטיגואה וברבודה", - "assets.countries.AI": "אנגווילה", - "assets.countries.AL": "אלבניה", - "assets.countries.AM": "ארמניה", - "assets.countries.AO": "אנגולה", - "assets.countries.AQ": "אנטרטיקה", - "assets.countries.AR": "ארגנטינה", - "assets.countries.AS": "אמריקה הסמואית", - "assets.countries.AT": "אוסטריה", - "assets.countries.AU": "אוסטרליה", - "assets.countries.AW": "ארובה", - "assets.countries.AX": "איי אולנד", - "assets.countries.AZ": "אזרביג'אן", - "assets.countries.BA": "בוסניה והרצגובינה", - "assets.countries.BB": "ברבדוס", - "assets.countries.BD": "בנגלדש", - "assets.countries.BE": "בלגיה", - "assets.countries.BF": "בורקינה פאסו", - "assets.countries.BG": "בולגריה", - "assets.countries.BH": "בחריין", - "assets.countries.BI": "בורונדי", - "assets.countries.BJ": "בנין", - "assets.countries.BL": "סנט ברתלמי", - "assets.countries.BM": "ברמודה", - "assets.countries.BN": "ברוניי דאר-סלאם", - "assets.countries.BO": "בוליביה", - "assets.countries.BQ": "בונייר", - "assets.countries.BR": "ברזיל", - "assets.countries.BS": "איי באהאמה", - "assets.countries.BT": "בהוטאן", - "assets.countries.BV": "אי בובט", - "assets.countries.BW": "בוטסוואנה", - "assets.countries.BY": "בלרוס", - "assets.countries.BZ": "בליז", - "assets.countries.CA": "קנדה", - "assets.countries.CC": "איי קוקוס (קילינג)", - "assets.countries.CD": "קונגו", - "assets.countries.CF": "רפובליקת אפריקה המרכזית", - "assets.countries.CG": "קונגו", - "assets.countries.CH": "שוויצריה", - "assets.countries.CI": "קוט ד'לבור", - "assets.countries.CK": "איי קוק", - "assets.countries.CL": "צ'ילה", - "assets.countries.CM": "קאמרון", - "assets.countries.CN": "סין", - "assets.countries.CO": "קולומביה", - "assets.countries.CR": "קוסטה ריקה", - "assets.countries.CU": "קובה", - "assets.countries.CV": "קייפ ורדה", - "assets.countries.CW": "קוראסאו", - "assets.countries.CX": "אי חג המולד", - "assets.countries.CY": "קפריסין", - "assets.countries.CZ": "רפובליקה צ'כית", - "assets.countries.DE": "גרמניה", - "assets.countries.DJ": "ג'יבוטי", - "assets.countries.DK": "דנמרק", - "assets.countries.DM": "דומיניקה", - "assets.countries.DO": "הרפובליקה הדומיניקנית", - "assets.countries.DZ": "אלג'יריה", - "assets.countries.EC": "אקוודור", - "assets.countries.EE": "אסתוניה", - "assets.countries.EG": "מצרים", - "assets.countries.EH": "סהרה המערבית", - "assets.countries.ER": "אריתריאה", - "assets.countries.ES": "ספרד", - "assets.countries.ET": "אתיופיה", - "assets.countries.FI": "פינלנד", - "assets.countries.FJ": "פיג'י", - "assets.countries.FK": "איי פוקלנד (מאלוינאס)", - "assets.countries.FM": "מיקרונזיה", - "assets.countries.FO": "איי פרעה", - "assets.countries.FR": "צרפת", - "assets.countries.GA": "גבון", - "assets.countries.GB": "בריטניה", - "assets.countries.GD": "גרנדה", - "assets.countries.GE": "ג'ורג'יה", - "assets.countries.GF": "גינאה הצרפתית", - "assets.countries.GG": "גרנזי", - "assets.countries.GH": "גאנה", - "assets.countries.GI": "גיברלטר", - "assets.countries.GL": "גרינלנד", - "assets.countries.GM": "גמביה", - "assets.countries.GN": "גיניאה", - "assets.countries.GP": "גוואדלופה", - "assets.countries.GQ": "גיניאה המשוונית", - "assets.countries.GR": "יוון", - "assets.countries.GS": "סאות ג'ורג'יה ואיי סנדוויץ' הדרומיים", - "assets.countries.GT": "גוואטמלה", - "assets.countries.GU": "גואם", - "assets.countries.GW": "גיניאה-ביסאו", - "assets.countries.GY": "גויאנה", - "assets.countries.HK": "הונג קונג", - "assets.countries.HM": "איי הרד ומקדונלד", - "assets.countries.HN": "הונדורס", - "assets.countries.HR": "קרואטיה (Hrvatska)", - "assets.countries.HT": "האיטי", - "assets.countries.HU": "הונגריה", - "assets.countries.ID": "אינדוניזיה", - "assets.countries.IE": "אירלנד", - "assets.countries.IL": "ישראל", - "assets.countries.IM": "האי מאן", - "assets.countries.IN": "הודו", - "assets.countries.IO": "טריטוריית הים ההודי הבריטית", - "assets.countries.IQ": "עירק", - "assets.countries.IR": "אירן (הרפובליקה האיסלמית של)", - "assets.countries.IS": "איסלנד", - "assets.countries.IT": "איטליה", - "assets.countries.JE": "ג'רסיי", - "assets.countries.JM": "ג'אמייקה", - "assets.countries.JO": "ירדן", - "assets.countries.JP": "יפן", - "assets.countries.KE": "קניה", - "assets.countries.KG": "קיריגיסטן", - "assets.countries.KH": "קמבודיה", - "assets.countries.KI": "קיריבטי", - "assets.countries.KM": "קומורו", - "assets.countries.KN": "סיינט קיטס ונוויס", - "assets.countries.KP": "קוריאה - הרפובליקה העממית הדמוקרטית של", - "assets.countries.KR": "קוריאה - רפובליקת", - "assets.countries.KW": "כוויית", - "assets.countries.KY": "איי קיימן", - "assets.countries.KZ": "קאזאחסטן", - "assets.countries.LA": "הרפובליקה הדמוקרטית העממית של לאו", - "assets.countries.LB": "לבנון", - "assets.countries.LC": "סנט לוציה", - "assets.countries.LI": "ליכטנשטיין", - "assets.countries.LK": "סרי לנקה", - "assets.countries.LR": "ליבריה", - "assets.countries.LS": "לסוטו", - "assets.countries.LT": "ליטואניה", - "assets.countries.LU": "לוקסמבורג", - "assets.countries.LV": "לטביה", - "assets.countries.LY": "לוב", - "assets.countries.MA": "מרוקו", - "assets.countries.MC": "מונקו", - "assets.countries.MD": "הרפובליקה של מולדובה", - "assets.countries.ME": "מונטנגרו", - "assets.countries.MF": "סן מרטין (הצרפתית)", - "assets.countries.MG": "מאדאגסקר", - "assets.countries.MH": "איי מרשל", - "assets.countries.MK": "מקדוניה - רפובליקת יוגוסלביה לשעבר של", - "assets.countries.ML": "מאלי", - "assets.countries.MM": "מיאנמאר", - "assets.countries.MN": "מונגוליה", - "assets.countries.MO": "מקאו", - "assets.countries.MP": "איי מריאנה הצפוניים", - "assets.countries.MQ": "מרטיניק", - "assets.countries.MR": "מאוריטניה", - "assets.countries.MS": "מונסרט", - "assets.countries.MT": "מלטה", - "assets.countries.MU": "מאוריטיוס", - "assets.countries.MV": "מלדיבס", - "assets.countries.MW": "מלאווי", - "assets.countries.MX": "מקסיקו", - "assets.countries.MY": "מלזיה", - "assets.countries.MZ": "מוזמביק", - "assets.countries.NA": "נמיביה", - "assets.countries.NC": "קלדוניה החדשה", - "assets.countries.NE": "ניג'ר", - "assets.countries.NF": "האי נורפולק", - "assets.countries.NG": "ניגריה", - "assets.countries.NI": "ניקרגואה", - "assets.countries.NL": "הולנד", - "assets.countries.NO": "נורווגיה", - "assets.countries.NP": "נפל", - "assets.countries.NR": "נאורו", - "assets.countries.NU": "ניואה", - "assets.countries.NZ": "ניו זילנד", - "assets.countries.OM": "עומאן", - "assets.countries.PA": "פנמה", - "assets.countries.PE": "פרו", - "assets.countries.PF": "פולינזיה הצרפתית", - "assets.countries.PG": "פפואה ניו גיני", - "assets.countries.PH": "הפיליפינים", - "assets.countries.PK": "פקיסטן", - "assets.countries.PL": "פולין", - "assets.countries.PM": "סן פייר ומיקלון", - "assets.countries.PN": "איי פיטקרן", - "assets.countries.PR": "פווארטו ריקו", - "assets.countries.PS": "פלסטין", - "assets.countries.PT": "פורטוגל", - "assets.countries.PW": "פלאו", - "assets.countries.PY": "פארגוואי", - "assets.countries.QA": "קאטאר", - "assets.countries.RE": "ריוניון", - "assets.countries.RO": "רומניה", - "assets.countries.RS": "סרביה", - "assets.countries.RU": "הפדרציה הרוסית", - "assets.countries.RW": "רווואנדה", - "assets.countries.SA": "ערב הסעודית", - "assets.countries.SB": "איי סולומון", - "assets.countries.SC": "סיישל", - "assets.countries.SD": "סודן", - "assets.countries.SE": "שוודיה", - "assets.countries.SG": "סינגפור", - "assets.countries.SH": "סיינט הלנה", - "assets.countries.SI": "סלובניה", - "assets.countries.SJ": "איי סוואלברד ויאן מאיין", - "assets.countries.SK": "סלובקיה (הרפובליקה הסלובקית)", - "assets.countries.SL": "סיירה ליאון", - "assets.countries.SM": "סאן מארינו", - "assets.countries.SN": "סנגל", - "assets.countries.SO": "סומליה", - "assets.countries.SR": "סורינם", - "assets.countries.SS": "דרום סודן", - "assets.countries.ST": "סאו תומה ופרינסיפה", - "assets.countries.SV": "אל סלבדור", - "assets.countries.SX": "הקדוש מארטיין (חלק הולנדי)", - "assets.countries.SY": "סוריה", - "assets.countries.SZ": "שוויץ", - "assets.countries.TC": "איי טורקס וקאיקוס", - "assets.countries.TD": "צ'אד", - "assets.countries.TF": "הטריטוריות הדרומיות של צרפת", - "assets.countries.TG": "טוגו", - "assets.countries.TH": "תאילנד", - "assets.countries.TJ": "טג'יקיסטן", - "assets.countries.TK": "טוקלאו", - "assets.countries.TL": "טימור-לסטה", - "assets.countries.TM": "טורקמניסטן", - "assets.countries.TN": "טוניסיה", - "assets.countries.TO": "טונגה", - "assets.countries.TR": "טורקיה", - "assets.countries.TT": "טרינידד וטובגו", - "assets.countries.TV": "טובאלו", - "assets.countries.TW": "טייוואן", - "assets.countries.TZ": "טנזניה, הרפובליקה המאוחדת של", - "assets.countries.UA": "אוקראינה", - "assets.countries.UG": "אוגנדה", - "assets.countries.UM": "איים תחת שלטון ארה\"ב", - "assets.countries.US": "ארה\"ב", - "assets.countries.UY": "אורוגוואי", - "assets.countries.UZ": "אוזבקיסטאן", - "assets.countries.VA": "וותיקן", - "assets.countries.VC": "סנט וינסנט והגרנדינים", - "assets.countries.VE": "וונצואלה", - "assets.countries.VG": "איי הבתולה (בריטניה)", - "assets.countries.VI": "איי הבתולה (אמריקה)", - "assets.countries.VN": "ווייטנאם", - "assets.countries.VU": "ונואטו", - "assets.countries.WF": "איי ווליס ופוטונה", - "assets.countries.WS": "סמואה", - "assets.countries.YE": "תימן", - "assets.countries.YT": "מאיוט", - "assets.countries.ZA": "דרום אפריקה", - "assets.countries.ZM": "זמביה", - "assets.countries.ZW": "זימבבואה", - "assets.mimetypes.application/msword": "מסמך Word", - "assets.mimetypes.application/pdf": "מסמך PDF", - "assets.mimetypes.application/vnd.moodle.backup": "גיבוי Moodle", - "assets.mimetypes.application/vnd.ms-excel": "גיליון Excel", - "assets.mimetypes.application/vnd.ms-powerpoint": "מצגת Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "מצגת PowerPoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "מצגת PowerPoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "גיליון Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "תבנית Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "קובץ Word", - "assets.mimetypes.archive": "ארכיון ({{$a.EXT}})", - "assets.mimetypes.audio": "קובץ שמע ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}\nGoogle Translate\nעזור עם זהמחרוזת המכילה סימניה", - "assets.mimetypes.document/unknown": "קובץ", - "assets.mimetypes.image": "תמונת ({{$a.MIMETYPE2}})", - "assets.mimetypes.text/html": "קובץ HTML", - "assets.mimetypes.text/plain": "קובץ טקסט", - "assets.mimetypes.text/rtf": "מסמך RTF", - "core.accounts": "חשבונות", - "core.add": "הוספה", - "core.agelocationverification": "אימות גיל ומיקום", - "core.ago": "לפני {{$a}}", - "core.all": "הכל", - "core.allgroups": "כל הקבוצות", - "core.allparticipants": "כל המשתתפים", - "core.answer": "תשובה", - "core.answered": "נענתה", - "core.areyousure": "האם את/ה בטוח/ה?", - "core.back": "חזרה", - "core.block.blocks": "משבצות (בלוקים)", - "core.cancel": "ביטול", - "core.cannotconnect": "אין אפשרות להתחבר: אנא ודאו כי הזנתם נכון את כתובת האתר ושמערכת מוודל בגרסה {{$a}} ומעלה.", - "core.cannotdownloadfiles": "הורדת קבצים אינה מאופשרת במכשירים ניידים. יש לפנות למנהל/ת האתר להפעלת אפשרות זו.", - "core.category": "קטגוריה", - "core.choose": "יש לבחור...", - "core.choosedots": "יש לבחור...", - "core.clearsearch": "איפוס חיפוש", - "core.clicktohideshow": "הקליקו להרחבה או צמצום", - "core.close": "סגירה", - "core.comments": "הערות", - "core.comments.addcomment": "הוספת הערה...", - "core.comments.comments": "הערות", - "core.comments.commentscount": "({{$a}}) הערות", - "core.comments.deletecommentbyon": "מחיקת הערות שהתפרסמו על-ידי {{$a.user}} ב- {{$a.time}}", - "core.comments.eventcommentcreated": "תגובה נוצרה", - "core.comments.eventcommentdeleted": "תגובה נמחקה", - "core.comments.nocomments": "ללא הערות", - "core.comments.savecomment": "שמירת הערה", - "core.commentscount": "({{$a}}) הערות", - "core.completion-alt-auto-fail": "הושלם: {{$a}} (לא השיג ציון עובר)", - "core.completion-alt-auto-n": "לא הושלם: {{$a}}", - "core.completion-alt-auto-n-override": "טרם הושלם: {{$a.modname}} (נקבע על ידי {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "הושלם: {{$a}} (השיג ציון עובר)", - "core.completion-alt-auto-y": "הושלם: {{$a}}", - "core.completion-alt-auto-y-override": "הושלם: {{$a.modname}} (נקבע על ידי {{$a.overrideuser}})", - "core.completion-alt-manual-n": "{{$a}} לא הושלם. הקליקו לסימון כ\"הושלם\"", - "core.completion-alt-manual-n-override": "טרם הושלם: {{$a.modname}} (נקבע על ידי {{$a.overrideuser}}). הקליקו לסימון כהושלם.", - "core.completion-alt-manual-y": "{{$a}} הושלם. הקליקו לסימון כ\"לא הושלם\"", - "core.completion-alt-manual-y-override": "הושלם: {{$a.modname}} (נקבע על ידי {{$a.overrideuser}}). הקליקו לסימון כלא הושלם.", - "core.confirmdeletefile": "האם הינך בטוח כי ברצונך למחוק קובץ זה?", - "core.considereddigitalminor": "את/ה נחשב/ת קטין דיגיטלי.", - "core.content": "תוכן", - "core.continue": "המשך", - "core.course": "קורס", - "core.course.allsections": "כל יחידות ההוראה", - "core.course.contents": "תוכן", - "core.course.couldnotloadsectioncontent": "אין אפשרות לטעון ולהציג את תוכן יחידת ההוראה. יש לנסות שוב מאוחר יותר.", - "core.course.couldnotloadsections": "אין אפשרות לטעון ולהציג את יחידות ההוראה, יש לנסות שוב מאוחר יותר.", - "core.course.coursesummary": "תקציר הקורס", - "core.course.downloadcourse": "הורדת הקורס", - "core.course.hiddenfromstudents": "מוסתר מסטודנטים", - "core.course.hiddenoncoursepage": "זמין לסטודנטים, אך אינו מוצג בעמוד הראשי של הקורס", - "core.course.nocontentavailable": "אין תוכן זמין כרגע.", - "core.course.overriddennotice": "הציון הסופי שלך מפעילות זו הותאם ידנית.", - "core.course.sections": "יחידות־הוראה", - "core.coursedetails": "פרטי הקורס", - "core.courses.addtofavourites": "הוספת קורס למועדפים", - "core.courses.allowguests": "הקורס הזה מרשה למשתמשים אורחים להכנס", - "core.courses.availablecourses": "קורסים זמינים", - "core.courses.categories": "קטגוריות קורסים", - "core.courses.courses": "קורסים", - "core.courses.errorloadcourses": "התרחשה שגיאה בזמן טעינת הקורסים.", - "core.courses.frontpage": "העמוד הראשי", - "core.courses.hidecourse": "הסתרת קורס", - "core.courses.ignore": "התעלם", - "core.courses.mycourses": "הקורסים שלי", - "core.courses.mymoodle": "עדכונים בקורסים שלי", - "core.courses.nocourses": "אין מידע שניתן להציג על הקורס", - "core.courses.nocoursesyet": "אין קורסים בקטגוריה זו", - "core.courses.nosearchresults": "אין תוצאות", - "core.courses.notenroled": "אינך רשום לקורס זה", - "core.courses.paymentrequired": "קורס זה מצריך תשלום לכניסה", - "core.courses.paypalaccepted": "התקבלו תשלומי Paypal", - "core.courses.reload": "טעינה מחדש", - "core.courses.removefromfavourites": "הסרת קורס מהמועדפים", - "core.courses.search": "חיפוש", - "core.courses.searchcourses": "חיפוש קורסים", - "core.courses.sendpaymentbutton": "שליחת תשלום דרך Paypal", - "core.courses.show": "תצוגת קורס זה", - "core.date": "תאריך", - "core.day": "יום", - "core.days": "ימים", - "core.decsep": ".", - "core.defaultvalue": "ברירת מחדל ({{$a}})", - "core.delete": "מחיקה", - "core.deleteduser": "משתמש שנמחק", - "core.description": "תיאור", - "core.dfdayweekmonth": "dddd, D MMMM", - "core.dflastweekdate": "dddd", - "core.dftimedate": "hh[:]mm", - "core.digitalminor": "קטין דיגיטלי", - "core.digitalminor_desc": "בכדי ליצור חשבון באתר זה עליך להפנות את ההורים/אחרי לאדם הבא.", - "core.displayoptions": "אפשרויות תצוגה", - "core.done": "גמור", - "core.download": "הורדה", - "core.downloading": "מוריד", - "core.edit": "עריכה", - "core.editor.autosavesucceeded": "הטיוטה נשמרה.", - "core.editor.bold": "מודגש", - "core.editor.clear": "ניקוי עיצוב", - "core.editor.h3": "כותרת 1", - "core.editor.h4": "כותרת 2", - "core.editor.h5": "כותרת 3", - "core.editor.italic": "מוטה", - "core.editor.orderedlist": "רשימה ממוספרת", - "core.editor.p": "פסקה", - "core.editor.strike": "קו חוצה", - "core.editor.textrecovered": "טיוטה של טקסט זה שוחזרה באופן אוטומטי.", - "core.editor.underline": "קו תחתון", - "core.editor.unorderedlist": "רשימת תבליטים", - "core.error": "טעות", - "core.errordownloading": "שגיאה בהורדת קובץ", - "core.errordownloadingsomefiles": "שגיאה בהורדת קבצי המודול. יתכן וחלק מהקבצים חסרים.", - "core.favourites": "מועדף", - "core.filename": "שם הקובץ", - "core.filenotfound": "לצערנו הקובץ לא נמצא.", - "core.fileuploader.addfiletext": "הוספת קובץ", - "core.fileuploader.audio": "קול", - "core.fileuploader.camera": "מצלמה", - "core.fileuploader.confirmuploadfile": "את/ה עומד/ת להעלות קובץ בגודל {{size}}. האם את/ה רוצא/ה להמשיך?", - "core.fileuploader.errorcapturingaudio": "שגיאה בלכידת הקול.", - "core.fileuploader.errorcapturingimage": "שגיאה בלכידת התמונה.", - "core.fileuploader.errorcapturingvideo": "שגיאה בלכידת הסרט.", - "core.fileuploader.errorgettingimagealbum": "שגיאה בטעינת תמונה מאלבום.", - "core.fileuploader.errormustbeonlinetoupload": "עליך להיות מחובר/ת בכדי להעלות קבצים.", - "core.fileuploader.errornoapp": "לא נמצא יישומון זמין לביצוע פעולה הזו.", - "core.fileuploader.errorreadingfile": "שגיאה בקריאת קובץ.", - "core.fileuploader.errorwhileuploading": "התרחשה שגיאה בזמן הניסיון להעלות את הקובץ.", - "core.fileuploader.file": "קובץ", - "core.fileuploader.filesofthesetypes": "סוגי הקבצים אפשריים:", - "core.fileuploader.fileuploaded": "הקובץ הועלה בהצלחה.", - "core.fileuploader.invalidfiletype": "בפעילות זו, לא ניתן להעלות או להגיש קבצים מסוג: {{$a}}", - "core.fileuploader.more": "עוד", - "core.fileuploader.photoalbums": "אלבומי תמונות", - "core.fileuploader.readingfile": "קורא קובץ", - "core.fileuploader.uploadafile": "העלה קובץ", - "core.fileuploader.uploading": "מעלה קבצים", - "core.fileuploader.video": "וידאו", - "core.filter": "מסנן", - "core.folder": "תיקייה", - "core.forcepasswordchangenotice": "יש לשנות סיסמה כדי להמשיך", - "core.fulllistofcourses": "כל הקורסים", - "core.grades.average": "ממוצע (בקורס)", - "core.grades.badgrade": "הציון שנמסר איננו נאות", - "core.grades.contributiontocoursetotal": "ניקוד משוקלל, כתרומה לציון הסופי", - "core.grades.feedback": "משוב", - "core.grades.grade": "ניקוד בפעילות", - "core.grades.gradeitem": "פריט ציון", - "core.grades.grades": "ציונים", - "core.grades.lettergrade": "ציון אות", - "core.grades.nogradesreturned": "לא הוחזרו ציונים", - "core.grades.nooutcome": "לא קיים מדד־הערכה", - "core.grades.percentage": "הניקוד כאחוז מהציון המירבי בפעילות", - "core.grades.range": "טווח", - "core.grades.rank": "דרוג יחסי", - "core.grades.weight": "משקל", - "core.group": "קבוצה", - "core.groupsseparate": "קבוצות נפרדות", - "core.groupsvisible": "קבוצות נראות", - "core.help": "עזרה", - "core.hide": "הסתרה", - "core.hour": "שעה", - "core.hours": "שעות", - "core.image": "תמונה", - "core.imageviewer": "מציג תמונות", - "core.info": "מידע", - "core.invalidformdata": "נתון הטופס שגוי", - "core.labelsep": ":", - "core.lastaccess": "כניסה אחרונה", - "core.lastmodified": "שינוי אחרון", - "core.layoutgrid": "סריג", - "core.list": "רשימה", - "core.listsep": ",", - "core.loading": "טעינה", - "core.location": "מיקום", - "core.login.auth_email": "אימות מבוסס דואר־אלקטרוני", - "core.login.authenticating": "מאמת נתונים", - "core.login.cancel": "ביטול", - "core.login.changepassword": "שינוי סיסמה", - "core.login.confirmdeletesite": "האם את/ה בטוח/ה שברצונך למחוק את האתר {{sitename}}?", - "core.login.connect": "התחברות!", - "core.login.connecttomoodle": "התחברות למוודל", - "core.login.createaccount": "יצירת חשבון חדש", - "core.login.createuserandpass": "הזנת שם־משתמש וסיסמה", - "core.login.credentialsdescription": "יש להזין את שם המשתמש והסיסמה שלך כדי להתחבר", - "core.login.emailconfirmsent": "

                לכתובתך ב {{$a}} תשלח בקרוב הודעת דואר אלקטרוני.

                הודעה זו מכילה הוראות פשוטות להשלמת הרשמתך.

                אם תיתקל בקשיים, אנא צור קשר עם מנהל האתר.

                ", - "core.login.emailconfirmsentsuccess": "אימות דוא\"ל נשלח בהצלחה", - "core.login.errordeletesite": "התרחשה שגיאה בזמן מחיקת אתר זה. אנא נסה שוב.", - "core.login.errorupdatesite": "התרחשה שגיאה בזמן עדכון אסימון האתר (token).", - "core.login.firsttime": "האם זהו הביקור הראשון שלך באתר זה?", - "core.login.forcepasswordchangenotice": "יש לשנות סיסמה כדי להמשיך", - "core.login.forgotten": "שכחת את שם המשתמש או את הסיסמה שלך?", - "core.login.help": "עזרה", - "core.login.helpmelogin": "

                קיימים אלפים רבים של אתרי מוודל ברחבי העולם. אפליקציה זו יכולה להתחבר רק לאתרים בהם .הופעלה גישה לאפליקצית מובייל

                אם אין באפרותך להתחבר לאתר המוודל שלך, עליך לפנות למנהל האתר הדרוש ולבקש מהם לקרוא http://docs.moodle.org/en/Mobile_app

                בכדי לבדוק את האפליקציה באתר דמו של מוודל, יש להזין teacher או student בשדה כתובת אתר וללחוץ על כפתור הוסף.

                ", - "core.login.instructions": "הוראות", - "core.login.invalidaccount": "אנא בדוק את פרטי ההתחברות שלך או פנה למנהל האתר לבדיקת תצורת האתר.", - "core.login.invaliddate": "תאריך לא תקף", - "core.login.invalidemail": "כתובת דואר אלקטרוני לא תקפה", - "core.login.invalidmoodleversion": "גרסת מוודל לא תקינה. נדרשת גרסה {{$a}} ומעלה.", - "core.login.invalidsite": "כתובת האתר אינה תקינה.", - "core.login.invalidurl": "הוגדר קישור לא תקין", - "core.login.login": "התחברות", - "core.login.loginbutton": "כניסה!", - "core.login.logininsiterequired": "עליך להתחבר לאתר בחלון דפדפן.", - "core.login.loginsteps": "שלום לך! לקבלת גישה מלאה לקורסים עליך להקדיש דקה לפתיחת חשבון חדש באתר זה.\nלכל אחד מהקורסים הבודדים יכול להיות גם \"מפתח רישום\" שתצטרך רק מאוחר יותר. להלן שלבי פתיחת החשבון:\n
                  \n
                1. מלא את הטופס חשבון חדש בפרטים שלך
                2. \n
                3. דואר אלקטרוני יישלח מיידית לכתובת הדואר האלקטרוני שסיפקת (אם לא נמצא, נא לחפש גם בדואר הזבל או הספאם).
                4. \n
                5. קרא את הדואר אלקטרוני שלך ולחץ על קישור האינטרנט שהוא מכיל.
                6. \n
                7. החשבון שלך יאושר ואתה תהיה מחובר.
                8. \n
                9. עכשיו בחר את הקורס בו תרצה להשתתף.
                10. \n
                11. אם מבקשים ממך \"מפתח רישום\" השתמש בזה שנתן לך המורה שלך. זה \"ירשום\" אותך לקורס.
                12. \n
                13. עכשיו יש בידך גישה לכל הקורס. מעתה והלאה כל שתצטרך בכדי להתחבר ולהיכנס לכל קורס שנרשמת אליו הוא שם המשתמש שלך והסיסמה (בטופס המופיע בדף זה).
                14. \n
                ", - "core.login.missingemail": "חסרה כתובת דואר אלקטרוני", - "core.login.missingfirstname": "חסר שם פרטי", - "core.login.missinglastname": "חסר שם משפחה", - "core.login.mobileservicesnotenabled": "Mobile Services לא מאופשרים באתר זה. אנא פנה למנהל האתר באם רצונך לשנות זאת.", - "core.login.mustconfirm": "עליך לאשר את החשבון שלך", - "core.login.newaccount": "חשבון חדש", - "core.login.notloggedin": "עליך להיות מחובר/ת", - "core.login.password": "סיסמה", - "core.login.passwordforgotten": "שכחת סיסמה", - "core.login.passwordforgotteninstructions2": "לאיפוס ססמתך, רשום את שם המשתמש או את כתובת הדוא\"ל שלך למטה. אם נמצא אותך במסד הנתונים של האתר, ישלח אליך דוא\"ל לכתובת הדוא\"ל שלך, עם הוראות כיצד ניתן להיכנס לאתר.", - "core.login.passwordrequired": "דרושה סיסמה", - "core.login.policyaccept": "אני מבין ומסכים", - "core.login.policyagree": "הינך חייב לאשר את מדיניות זו על מנת להמשיך להשתמש באתר זה. האם אתה מסכים?", - "core.login.policyagreement": "הסכם מדיניות המערכת", - "core.login.policyagreementclick": "יש להקליק כאן על מנת לקרוא את הסכם מדיניות המערכת", - "core.login.potentialidps": "התחברות (הזדהות) מבוססת החשבון שלך ב:", - "core.login.profileinvaliddata": "ערך לא חוקי", - "core.login.reconnect": "חיבור מחדש", - "core.login.reconnectdescription": "אסימון האימות שלך (token) אינו תקף או לא תקין, עליך להתחבר לאתר מחדש.", - "core.login.reconnectssodescription": "אסימון האימות שלך (token) אינו תקף או לא תקין, עליך להתחבר לאתר מחדש. יש להתחבר לאתר בשימוש חלון דפדפן.", - "core.login.resendemail": "שלח דוא\"ל מחדש", - "core.login.security_question": "שאלת אבטחה", - "core.login.selectacountry": "בחירת ארץ", - "core.login.siteaddress": "כתובת אתר", - "core.login.siteinmaintenance": "האתר נמצא במצב תחזוקה", - "core.login.siteurl": "כתובת אתר", - "core.login.siteurlrequired": "דרושה כתובת אתר, לדוגמה:http://www.yourmoodlesite.abc או https://www.yourmoodlesite.efg", - "core.login.startsignup": "יצירת חשבון חדש!", - "core.login.supplyinfo": "אנא ספק מספר נתונים על עצמך", - "core.login.username": "שם משתמש", - "core.login.usernameoremail": "הכנס אחד משניים שם משתמש או כתובת דוא\"ל", - "core.login.usernamerequired": "דרוש שם משתמש", - "core.login.usernotaddederror": "משתמש לא התווסף - טעות", - "core.login.webservicesnotenabled": "Web Services לא מאופשרים באתר זה. אנא פנה למנהל האתר באם רצונך לשנות זאת", - "core.lostconnection": "לקוד הזיהוי המאובטח שלך פג התוקף, ולכן החיבור נותק. עליך להתחבר שוב.", - "core.mainmenu.help": "עזרה", - "core.mainmenu.logout": "התנתקות", - "core.mainmenu.website": "אתר אינטרנט", - "core.maxsizeandattachments": "נפח קבצים מירבי: {{$a.size}}, מספר קבצים מצורפים מירבי: {{$a.attachments}}", - "core.min": "דקה", - "core.mins": "דקות", - "core.misc": "שונות", - "core.mod_assign": "מטלה", - "core.mod_assignment": "מטלה 2.2 (איננה-זמינה)", - "core.mod_book": "ספר", - "core.mod_chat": "רב־שיח", - "core.mod_choice": "שאלת סקר", - "core.mod_data": "בסיס־נתונים", - "core.mod_database": "בסיס־נתונים", - "core.mod_external-tool": "רכיב/תוכן חיצוני (LTI)", - "core.mod_feedback": "שאלון מותנה", - "core.mod_file": "קובץ", - "core.mod_folder": "תצוגת תיקיית קבצים", - "core.mod_forum": "פורום (קבוצת־דיון)", - "core.mod_glossary": "אגרון מונחים", - "core.mod_ims": "חבילת תוכן IMS", - "core.mod_imscp": "חבילת תוכן IMS", - "core.mod_label": "פִסקה מעוצבת", - "core.mod_lesson": "שיעור", - "core.mod_lti": "רכיב/תוכן חיצוני (LTI)", - "core.mod_page": "דף תוכן מעוצב", - "core.mod_quiz": "בוחן", - "core.mod_resource": "משאב (קובץ)", - "core.mod_scorm": "חבילת SCORM", - "core.mod_survey": "תבנית סקרים מובנת", - "core.mod_url": "קישור לאתר אינטרנט", - "core.mod_wiki": "ויקי (תוצר־משותף)", - "core.mod_workshop": "הערכת־עמיתים", - "core.moduleintro": "הנחיה לפעילות", - "core.more": "עוד", - "core.mygroups": "הקבוצות שלי", - "core.name": "שם", - "core.networkerrormsg": "הרשת לא מופעלת או לא עובדת.", - "core.never": "אף פעם לא", - "core.next": "הבא", - "core.no": "לא", - "core.nocomments": "ללא הערות", - "core.nograde": "אין ציון", - "core.none": "אין", - "core.nopermissions": "למשתמש שלכם אין את ההרשאה לבצע את הפעולה \"{{$a}}\".\n
                \nיש לפנות למנהל(ת) המערכת שלכם לקבלת ההרשאות המתאימות.", - "core.noresults": "אין תוצאות", - "core.noselection": "יש לבחור פריט מהרשימה או להזין תוכן, מופרד בפסיקים (ENTER לשמירה)", - "core.notapplicable": "לא זמין", - "core.notenrolledprofile": "פרופיל זה לא זמין מפני שמשתמש זה לא רשום לקורס זה.", - "core.notice": "לתשומת לב", - "core.notingroup": "פעילות זו זמינה רק לחברים בקבוצת למידה.", - "core.now": "עכשיו", - "core.numwords": "{{$a}} מילים", - "core.offline": "לא מקוון", - "core.ok": "אישור", - "core.online": "מקוון", - "core.openfullimage": "יש להקליק כאן להצגת התמונה בגודל מלא", - "core.openinbrowser": "תצוגה בדפדפן", - "core.othergroups": "קבוצות אחרות", - "core.pagea": "עמוד {{$a}}", - "core.paymentinstant": "השתמש בכפתור למטה לשלם ולהירשם תוך דקות!", - "core.phone": "טלפון", - "core.pictureof": "תמונה של {{$a}}", - "core.previous": "קודם", - "core.proceed": "להמשיך", - "core.pulltorefresh": "משיכה לרענון", - "core.question.answer": "תשובה", - "core.question.answersaved": "תשובה נשמרה", - "core.question.certainty": "וודאות", - "core.question.complete": "הושלם", - "core.question.correct": "תקין", - "core.question.feedback": "משוב לבחירה בתשובה זו", - "core.question.incorrect": "שגוי", - "core.question.information": "מידע", - "core.question.invalidanswer": "תשובה שלא הושלמה", - "core.question.notanswered": "לא נענה", - "core.question.notyetanswered": "שאלה זו טרם נענתה", - "core.question.partiallycorrect": "נכון באופן חלקי", - "core.question.questionno": "שאלה {{$a}}", - "core.question.requiresgrading": "נדרש מתן ציון", - "core.quotausage": "השתמשת כרגע ב {{$a.used}} מהמגבלה שלך בסך {{$a.total}}.", - "core.rating.aggregateavg": "ממוצע דרוגים", - "core.rating.aggregatecount": "ספירת הדרוגים", - "core.rating.aggregatemax": "דרוג מירבי", - "core.rating.aggregatemin": "דרוג מזערי", - "core.rating.aggregatesum": "סיכום דרוגים", - "core.rating.noratings": "לא בוצעו דרוגים", - "core.rating.rating": "דרוג", - "core.rating.ratings": "דרוגים", - "core.refresh": "רענון", - "core.remove": "הסרה", - "core.required": "דרוש", - "core.requireduserdatamissing": "למשתמש זה חסרים שדות נדרשים בפרופיל המשתמש. יש להשלים מידע זה באתר המוודל שלך ולנסות שוב.
                {{$a}}", - "core.resourcedisplayopen": "באותו דף", - "core.resources": "משאבים", - "core.restore": "שחזור", - "core.restricted": "גישה מותנת", - "core.save": "שמירה", - "core.savechanges": "שמירת שינויים", - "core.search": "חיפוש", - "core.searchresults": "תוצאות חיפוש", - "core.sec": "שניה", - "core.secs": "שניות", - "core.seemoredetail": "הקליקו כאן כדי לראות פרטים נוספים", - "core.selectacategory": "יש לבחור קטגוריה", - "core.selectacourse": "בחירת קורס", - "core.selectagroup": "בחירת קבוצה", - "core.send": "שליחה", - "core.sending": "שולחים", - "core.serverconnection": "שגיאה בהתחברות לשרת", - "core.settings.about": "אודות", - "core.settings.currentlanguage": "שפה נוכחית", - "core.settings.debugdisplay": "תצוגת הודעות ניפוי שגיאות (debug)", - "core.settings.deletesitefiles": "האם אתה בטוח שברצונך למחוק את כל הקבצים שהורדו מאתר זה?", - "core.settings.deletesitefilestitle": "מחיקת קבצי אתר", - "core.settings.deviceinfo": "מידע אודות המכשיר", - "core.settings.deviceos": "מערכת הפעלה של המכשיר", - "core.settings.disableall": "כיבוי מנגנון הודעות־מערכת", - "core.settings.disabled": "לא-מאופשר", - "core.settings.displayformat": "תבנית תצוגה", - "core.settings.errordeletesitefiles": "שגיאה במחיקת קבצי האתר.", - "core.settings.errorsyncsite": "שגיאה בסנכרון מידע האתר, אנא בדוק את החיבור לרשת האינטרנט ונסה שוב.", - "core.settings.estimatedfreespace": "אומדן מקום פנוי", - "core.settings.fontsizecharacter": "א", - "core.settings.general": "כללי", - "core.settings.language": "שפת ממשק", - "core.settings.license": "רשיון", - "core.settings.localnotifavailable": "התראות מקומיות זמינות", - "core.settings.locked": "נעול", - "core.settings.loggedin": "מקוון", - "core.settings.loggedoff": "לא מקוון", - "core.settings.networkstatus": "מצב חיבור לרשת האינטרנט", - "core.settings.preferences": "העדפות", - "core.settings.reportinbackground": "דיווח על שגיאות באופן אוטומטי", - "core.settings.settings": "הגדרות", - "core.settings.sites": "אתרים", - "core.settings.spaceusage": "זיכרון בשימוש", - "core.settings.synchronization": "סינכרון", - "core.settings.synchronizenow": "סינכרון עכשיו", - "core.settings.total": "סך הכל", - "core.settings.wificonnection": "חיבור אלחוטי", - "core.show": "הצגה", - "core.showless": "הגדרות בסיסיות...", - "core.showmore": "הגדרות נוספות...", - "core.site": "מערכת", - "core.sitehome.sitehome": "דף הבית", - "core.sitehome.sitenews": "חדשות האתר", - "core.sitemaintenance": "מערכת Moodle זו מבצעת ברגעים אילו פעולות תחזוקה חשובות ואיננה זמינה כעת, אנא נסו שוב בעוד מספר דקות", - "core.sizeb": "בתים", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.skip": "דלגו", - "core.sort": "מיון", - "core.sortby": "מיון לפי", - "core.strftimedate": "%d/%m/%Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d/%m/%Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%d/%m/%Y", - "core.strftimedaydatetime": "%d/%m/%Y, %H:%M", - "core.strftimedayshort": "%d/%m/%Y", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%d/%m/%Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "שמירה", - "core.success": "הצלחה", - "core.tablet": "טאבלט", - "core.tag.defautltagcoll": "אוסף בררת־המחדל", - "core.tag.inalltagcoll": "בכל מקום", - "core.tag.itemstaggedwith": "התג '{{$a.tag}}' נוסף ל'{{$a.tagarea}}'", - "core.tag.noresultsfor": "אין תוצאות ל \"{{$a}}\"", - "core.tag.notagsfound": "לא נמצאו תגים בשם \"{{$a}}\"", - "core.tag.searchtags": "חיפוש תגים", - "core.tag.showingfirsttags": "תצוגת {{$a}} התגים השכיחים ביותר", - "core.tag.tag": "תג", - "core.tag.tagarea_course": "קורסים", - "core.tag.tagarea_course_modules": "משאבים ופעילויות", - "core.tag.tagarea_post": "פרסומי בלוגים", - "core.tag.tagarea_user": "תחומי עניין של משתמש", - "core.tag.tags": "תגים", - "core.teachers": "מורים", - "core.thisdirection": "rtl", - "core.time": "זמן", - "core.timesup": "זמנך נגמר!", - "core.today": "היום", - "core.unexpectederror": "שגיאה לא ידועה. יש לסגור את היישום Moodle ואז לפתוח מחדש ולנסות שוב", - "core.unknown": "לא ידוע", - "core.unlimited": "אין הגבלה", - "core.upgraderunning": "האתר נמצא בשדרוג,\nאנא נסה שנית מאוחר יותר.", - "core.user": "משתמש", - "core.user.address": "כתובת", - "core.user.city": "ישוב", - "core.user.contact": "צור קשר", - "core.user.country": "ארץ", - "core.user.description": "תיאור", - "core.user.details": "פרטים", - "core.user.detailsnotavailable": "פרטי משתמש זה אינם זמינים לך.", - "core.user.editingteacher": "מורה", - "core.user.email": "כתובת דואר אלקטרוני", - "core.user.emailagain": "דואר אלקטרוני (שוב)", - "core.user.firstname": "שם פרטי", - "core.user.interests": "תחומי עניין", - "core.user.lastname": "שם משפחה", - "core.user.manager": "מנהל/ת", - "core.user.newpicture": "תמונה חדשה", - "core.user.noparticipants": "לא נמצאו משתתפים עבור קורס זה", - "core.user.participants": "משתתפים", - "core.user.phone1": "טלפון", - "core.user.phone2": "טלפון נייד", - "core.user.roles": "תפקידים", - "core.user.student": "סטודנט", - "core.user.teacher": "מורה לא עורך", - "core.user.webpage": "דף בית באינטרנט", - "core.userdeleted": "חשבון משתמש זה נמחק", - "core.userdetails": "מאפייניי המשתמש", - "core.users": "משתמשים", - "core.view": "תצוגה", - "core.viewprofile": "תצוגת מאפיינים", - "core.whatisyourage": "מה הגיל שלך?", - "core.wheredoyoulive": "באיזה ארץ את/ה גר/ה?", - "core.whyisthisrequired": "למה זה נדרש?", - "core.year": "שנה", - "core.years": "שנים", - "core.yes": "כן" -} \ No newline at end of file diff --git a/src/assets/lang/hi.json b/src/assets/lang/hi.json deleted file mode 100644 index f0a0a6e25..000000000 --- a/src/assets/lang/hi.json +++ /dev/null @@ -1,877 +0,0 @@ -{ - "addon.badges.badgedetails": "पदक विवरण", - "addon.badges.badges": "पदक", - "addon.badges.contact": "संपर्क", - "addon.block_activitymodules.pluginname": "गतिविधियाँ", - "addon.block_activityresults.pluginname": "गतिविधि के परिणाम", - "addon.block_badges.pluginname": "नवीनतम बैज", - "addon.block_blogmenu.pluginname": "ब्लॉग मेनू", - "addon.block_blogrecent.pluginname": "हाल की ब्लॉग प्रविष्टियाँ", - "addon.block_blogtags.pluginname": "ब्लॉग टैग्स", - "addon.block_calendarmonth.pluginname": "कैलॅन्डर", - "addon.block_calendarupcoming.pluginname": "आगामी कार्यक्रम", - "addon.block_comments.pluginname": "टिप्पणियां", - "addon.block_glossaryrandom.pluginname": "रैंडम शब्दावली प्रविष्टि", - "addon.block_learningplans.pluginname": "सीखने की योजना", - "addon.block_myoverview.all": "सब", - "addon.block_myoverview.allincludinghidden": "सब", - "addon.block_myoverview.favourites": "तारांकित", - "addon.block_myoverview.future": "भविष्य", - "addon.block_myoverview.hiddencourses": "दृश्य से हटा दिया गया", - "addon.block_myoverview.inprogress": "चालू", - "addon.block_myoverview.lastaccessed": "अंतिम अक्सेस्सेड", - "addon.block_myoverview.morecourses": "अधिक पाठ्यक्रम", - "addon.block_myoverview.nocourses": "कोई पाठ्यक्रम नहीं", - "addon.block_myoverview.past": "अतीत", - "addon.block_myoverview.pluginname": "पाठ्यक्रम अवलोकन", - "addon.block_myoverview.title": "पाठ्यक्रम का नाम", - "addon.block_newsitems.pluginname": "नवीनतम घोषणाएं", - "addon.block_onlineusers.pluginname": "ऑनलाइन उपयोगकर्ताए", - "addon.block_privatefiles.pluginname": "निजी फाइलें", - "addon.block_recentactivity.pluginname": "हाल की गतिविधियां", - "addon.block_recentlyaccessedcourses.nocourses": "कोई हाल का पाठ्यक्रम नहीं", - "addon.block_recentlyaccessedcourses.pluginname": "हाल ही में पहुँचा पाठ्यक्रम", - "addon.block_recentlyaccesseditems.noitems": "हाल की कोई आइटम नहीं", - "addon.block_recentlyaccesseditems.pluginname": "हाल ही में एक्सेस किए गए आइटम", - "addon.block_selfcompletion.pluginname": "खुद को पूरा करना", - "addon.block_sitemainmenu.pluginname": "मुख्य मॅन्यु", - "addon.block_starredcourses.nocourses": "कोई तारांकित पाठ्यक्रम नहीं", - "addon.block_starredcourses.pluginname": "तारांकित पाठ्यक्रम", - "addon.block_timeline.duedate": "नियत तारीख", - "addon.block_timeline.next30days": "अगले 30 दिन", - "addon.block_timeline.next3months": "अगले 3 महीने", - "addon.block_timeline.next6months": "अगले 6 महीने", - "addon.block_timeline.next7days": "अगले 7 दिन", - "addon.block_timeline.nocoursesinprogress": "कोई इन-प्रोग्रेस कोर्सेस नहीं", - "addon.block_timeline.noevents": "कोई आगामी गतिविधियाँ नहीं बाकि", - "addon.block_timeline.overdue": "अतिदेय", - "addon.block_timeline.pluginname": "समय", - "addon.block_timeline.sortbycourses": "पाठ्यक्रमों द्वारा क्रमबद्ध करें", - "addon.block_timeline.sortbydates": "तिथियों के आधार पर छाँटें", - "addon.calendar.calendar": "कैलॅन्डर", - "addon.calendar.calendarevents": "कैलेंडर ईवेंट", - "addon.calendar.confirmeventdelete": "क्या आप सचमुच में इस ईवेन्ट को डिलीट करना चाहते हैं?", - "addon.calendar.courseevents": "कोर्स ईवेन्ट", - "addon.calendar.defaultnotificationtime": "डिफ़ॉल्ट सूचना समय", - "addon.calendar.deleteevent": "ईवेन्ट को डिलीट करें", - "addon.calendar.durationminutes": "मिनटों में अवधि", - "addon.calendar.durationnone": "अवधि के बिना", - "addon.calendar.durationuntil": "जब तक", - "addon.calendar.editevent": "ईवेन्ट का सम्पादन किया जा रहा है", - "addon.calendar.errorloadevent": "कार्यक्रम लोड करने में त्रुटि हुई।", - "addon.calendar.errorloadevents": "कार्यक्रमों को लोड करने में त्रुटि हुई।", - "addon.calendar.eventduration": "समय", - "addon.calendar.eventendtime": "समाप्ति समय", - "addon.calendar.eventkind": "ईवेन्ट प्रकार", - "addon.calendar.eventname": "नाम", - "addon.calendar.eventstarttime": "आराम्भ समय", - "addon.calendar.fri": "शुक्र", - "addon.calendar.friday": "शुक्रवार", - "addon.calendar.mon": "सोम", - "addon.calendar.monday": "सोमवार", - "addon.calendar.monthlyview": "मासिक दृश्य", - "addon.calendar.newevent": "नया ईवेन्ट", - "addon.calendar.noevents": "कोई घटना नहीं है", - "addon.calendar.repeatevent": "इस घटना को दोहराएँ", - "addon.calendar.sat": "शनि", - "addon.calendar.saturday": "शनिवार", - "addon.calendar.sun": "रवि", - "addon.calendar.sunday": "रविवार", - "addon.calendar.thu": "गुरु", - "addon.calendar.thursday": "गुरुवार", - "addon.calendar.tue": "मंगल", - "addon.calendar.tuesday": "मंगल", - "addon.calendar.upcomingevents": "आगामी घटनाएँ", - "addon.calendar.wed": "बुध", - "addon.calendar.wednesday": "बुधवार", - "addon.calendar.yesterday": "कल", - "addon.competency.errornocompetenciesfound": "कोई योग्यता नहीं मिली", - "addon.competency.nocompetencies": "कोई योग्यता नहीं", - "addon.coursecompletion.complete": "पूर्ण", - "addon.coursecompletion.completecourse": "पूरा कोर्स", - "addon.coursecompletion.completed": "संपन्न", - "addon.coursecompletion.completiondate": "पूरा करने की तिथि", - "addon.coursecompletion.completionmenuitem": "समापन", - "addon.coursecompletion.couldnotloadreport": "पाठ्यक्रम पूरा होने की रिपोर्ट लोड नहीं कर सका। बाद में पुन: प्रयास करें।", - "addon.coursecompletion.coursecompletion": "पाठ्यक्रम सम्पूर्ण", - "addon.coursecompletion.criteria": "मानदंड", - "addon.coursecompletion.criteriagroup": "मानदंड समूह", - "addon.coursecompletion.criteriarequiredall": "नीचे दिए गए सभी मापदंडों आवश्यक हैं", - "addon.coursecompletion.criteriarequiredany": "नीचे दिए गए कोई भी मापदंड आवश्यक हैं", - "addon.coursecompletion.required": "ज़रूरी हैं", - "addon.coursecompletion.status": "दर्जा", - "addon.coursecompletion.viewcoursereport": "पाठ्यक्रम रिपोर्ट देखें", - "addon.files.couldnotloadfiles": "फाइलों की सूची लोड नहीं की जा सकी।", - "addon.files.emptyfilelist": "दिखाने के लिए फाइलें नहीं हैं।", - "addon.files.erroruploadnotworking": "दुर्भाग्य से वर्तमान में आपकी साइट पर फ़ाइलें अपलोड करना संभव नहीं है।", - "addon.files.files": "फ़ाइलें", - "addon.files.sitefiles": "साइट फ़ाइलें", - "addon.messageoutput_airnotifier.processorsettingsdesc": "उपकरणों को कॉन्फ़िगर करें", - "addon.messages.addcontact": "संपर्क जोड़ना", - "addon.messages.contactableprivacy_coursemember": "मेरे संपर्क और मेरे पाठ्यक्रमों में कोई भी", - "addon.messages.contactlistempty": "संपर्क सूची खाली है", - "addon.messages.contactname": "संपर्क नाम", - "addon.messages.contacts": "संपर्क", - "addon.messages.deletemessage": "संदेश को हटाएं", - "addon.messages.deletemessageconfirmation": "क्या आप निश्चित रूप से यह संदेश हटाना चाहते हैं? यह केवल आपके संदेश इतिहास से हटा दिया जाएगा और अभी भी उपयोगकर्ता द्वारा देखा जा सकता है जिसने संदेश भेजा या प्राप्त किया।", - "addon.messages.errordeletemessage": "संदेश हटाते समय त्रुटि।", - "addon.messages.errorwhileretrievingcontacts": "सर्वर से संपर्क पुनः प्राप्त करते समय त्रुटि।", - "addon.messages.errorwhileretrievingdiscussions": "सर्वर से चर्चाएँ प्राप्त करते समय त्रुटि।", - "addon.messages.errorwhileretrievingmessages": "सर्वर से संदेश प्राप्त करते समय त्रुटि।", - "addon.messages.errorwhileretrievingusers": "सर्वर से उपयोगकर्ताओं को पुनर्प्राप्त करते समय त्रुटि।", - "addon.messages.message": "सन्देश", - "addon.messages.messagenotsent": "संदेश नहीं भेजा गया था। बाद में पुन: प्रयास करें।", - "addon.messages.messages": "सन्देश", - "addon.messages.newmessages": "नए संदेश", - "addon.messages.nousersfound": "कोई उपयोग्कर्ता नहीं मिले", - "addon.messages.showdeletemessages": "डिलीट मैसेज दिखाओ", - "addon.messages.type_blocked": "अवरोधित", - "addon.messages.type_offline": "ऑफलाइन", - "addon.messages.type_online": "ऑनलाइन", - "addon.messages.type_search": "खोज परिणाम", - "addon.messages.type_strangers": "अन्य लोग", - "addon.messages.warningconversationmessagenotsent": "बातचीत के लिए संदेश नहीं भेजा जा सका {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "उपयोगकर्ता को संदेश (संदेश) नहीं भेज सका{{user}}. {{error}}", - "addon.mod_assign.acceptsubmissionstatement": "कृपया सबमिशन स्टेटमेंट स्वीकार करें।", - "addon.mod_assign.addattempt": "एक और प्रयास को अनुमति दें", - "addon.mod_assign.addnewattempt": "एक नया प्रयास जोड़ें", - "addon.mod_assign.addsubmission": "प्रस्तुति जोड़ें", - "addon.mod_assign.allowsubmissionsfromdate": "से प्रस्तुतियाँ की अनुमति", - "addon.mod_assign.assignmentisdue": "कार्य देय है", - "addon.mod_assign.attemptnumber": "प्रयास संख्या", - "addon.mod_assign.attemptreopenmethod_manual": "हाथ से", - "addon.mod_assign.cannoteditduetostatementsubmission": "आप एप्लिकेशन में कोई सबमिशन जोड़ या संपादित नहीं कर सकते क्योंकि सबमिशन स्टेटमेंट को साइट से पुनर्प्राप्त नहीं किया जा सकता था", - "addon.mod_assign.cannotgradefromapp": "कुछ ग्रेडिंग विधियों को अभी तक ऐप द्वारा समर्थित नहीं किया गया है और उन्हें संशोधित नहीं किया जा सकता है।", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "आप एप्लिकेशन में एक सबमिशन नहीं कर सकते क्योंकि सबमिशन स्टेटमेंट को साइट से पुनर्प्राप्त नहीं किया जा सकता था।", - "addon.mod_assign.duedate": "नियत तिथि", - "addon.mod_assign.erroreditpluginsnotsupported": "आप एप्लिकेशन में कोई सबमिशन जोड़ या संपादित नहीं कर सकते क्योंकि कुछ प्लगइन्स अभी तक संपादन के लिए समर्थित नहीं हैं।", - "addon.mod_assign.errorshowinginformation": "सबमिशन की जानकारी प्रदर्शित नहीं की जा सकती।", - "addon.mod_assign.feedbacknotsupported": "यह फ़ीडबैक ऐप द्वारा समर्थित नहीं है और इसमें सभी जानकारी नहीं हो सकती है।", - "addon.mod_assign.gradenotsynced": "ग्रेड सिंक नहीं किया गया", - "addon.mod_assign.notallparticipantsareshown": "जिन प्रतिभागियों ने प्रस्तुत नहीं किया है, उन्हें नहीं दिखाया गया है।", - "addon.mod_assign.numwords": "{{$a}} शब्द", - "addon.mod_assign.submissionnotsupported": "यह सबमिशन ऐप द्वारा समर्थित नहीं है और इसमें सभी जानकारी नहीं हो सकती है।", - "addon.mod_assign.userwithid": "आईडी के साथ उपयोगकर्ता {{id}}", - "addon.mod_assign.warningsubmissiongrademodified": "प्रस्तुत ग्रेड को साइट पर संशोधित किया गया था।", - "addon.mod_assign.warningsubmissionmodified": "उपयोगकर्ता प्रस्तुत साइट पर संशोधित किया गया था।", - "addon.mod_assign_feedback_comments.pluginname": "प्रतिक्रिया टिप्पणी", - "addon.mod_assign_feedback_editpdf.pluginname": "एनोटेट पीडीएफ", - "addon.mod_assign_feedback_file.pluginname": "फ़ाइल प्रतिक्रिया", - "addon.mod_assign_submission_comments.pluginname": "प्रस्तुत टिप्पणी", - "addon.mod_assign_submission_file.pluginname": "फ़ाइल प्रस्तुतियाँ", - "addon.mod_book.errorchapter": "पुस्तक का अध्याय पढ़ने में त्रुटि हुई।", - "addon.mod_book.modulenameplural": "पुस्तकें", - "addon.mod_book.tagarea_book_chapters": "पुस्तक के अध्याय", - "addon.mod_chat.beep": "बीप्", - "addon.mod_chat.chatreport": "चैट सेशन", - "addon.mod_chat.currentusers": "वर्तमान यूज़र", - "addon.mod_chat.errorwhileconnecting": "चैट से कनेक्ट करते समय त्रुटि।", - "addon.mod_chat.errorwhilegettingchatdata": "चैट डेटा प्राप्त करते समय त्रुटि।", - "addon.mod_chat.errorwhilegettingchatusers": "चैट उपयोगकर्ताओं को प्राप्त करते समय त्रुटि।", - "addon.mod_chat.errorwhileretrievingmessages": "सर्वर से संदेश प्राप्त करते समय त्रुटि।", - "addon.mod_chat.errorwhilesendingmessage": "संदेश भेजते समय त्रुटि।", - "addon.mod_chat.messages": "सन्देश", - "addon.mod_chat.modulenameplural": "चैट्स्", - "addon.mod_chat.mustbeonlinetosendmessages": "आपको संदेश भेजने के लिए ऑनलाइन होना चाहिए।", - "addon.mod_chat.nomessages": "कोई सन्देश नहीं", - "addon.mod_choice.errorgetchoice": "विकल्प डेटा प्राप्त करने में त्रुटि।", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% उपयोगकर्ताओं ने विकल्प चुना: {{text}}.", - "addon.mod_choice.resultsnotsynced": "परिणामों में शामिल करने से पहले आपकी अंतिम प्रतिक्रिया को सिंक्रनाइज़ किया जाना चाहिए।", - "addon.mod_data.errorapproving": "प्रविष्टि को अस्वीकार या अस्वीकार करने में त्रुटि।", - "addon.mod_data.errordeleting": "प्रविष्टि हटाने में त्रुटि।", - "addon.mod_feedback.captchaofflinewarning": "कैप्चा के साथ प्रतिक्रिया ऑफ़लाइन पूरी नहीं हो सकती है, या यदि कॉन्फ़िगर नहीं है, या यदि सर्वर डाउन है।", - "addon.mod_feedback.feedback_submitted_offline": "इस फ़ीडबैक को बाद में सबमिट करने के लिए सहेजा गया है।", - "addon.mod_folder.emptyfilelist": "दिखाने के लिए फाइलें नहीं हैं।", - "addon.mod_forum.addanewtopic": "नया विषय जोड़िये", - "addon.mod_forum.advanced": "उन्नत", - "addon.mod_forum.delete": "डिलीट", - "addon.mod_forum.discussion": "चर्चा", - "addon.mod_forum.errorgetforum": "फ़ोरम डेटा प्राप्त करने में त्रुटि।", - "addon.mod_forum.errorgetgroups": "समूह सेटिंग्स प्राप्त करने में त्रुटि।", - "addon.mod_forum.forumnodiscussionsyet": "इस फोरम में अभी कोई चर्चा नहीं हुई है।", - "addon.mod_forum.group": "समूह", - "addon.mod_forum.lastpost": "पिछली पोस्ट", - "addon.mod_forum.message": "सन्देश", - "addon.mod_forum.modulenameplural": "फ़ोरम", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} चर्चाएँ", - "addon.mod_forum.numreplies": "{{numreplies}} उत्तर", - "addon.mod_forum.refreshdiscussions": "चर्चा को ताज़ा करें", - "addon.mod_forum.refreshposts": "पदों को ताज़ा करें", - "addon.mod_forum.reply": "उत्तर दीजिए", - "addon.mod_forum.subject": "विषय", - "addon.mod_forum.yourreply": "आपका उत्तर", - "addon.mod_glossary.browsemode": "प्रविष्टियां ब्राउज़ करें", - "addon.mod_glossary.byalphabet": "वर्णक्रम", - "addon.mod_glossary.byauthor": "लेखक द्वारा समूह", - "addon.mod_glossary.bycategory": "श्रेणी के अनुसार समूह", - "addon.mod_glossary.bynewestfirst": "नवीनतम पहले", - "addon.mod_glossary.byrecentlyupdated": "हाल ही में अद्यतित", - "addon.mod_glossary.bysearch": "खोज", - "addon.mod_glossary.cannoteditentry": "प्रविष्टि संपादित नहीं की जा सकती", - "addon.mod_glossary.definition": "परिभाषा", - "addon.mod_glossary.entriestobesynced": "प्रविष्ट किए जाने की प्रविष्टियाँ", - "addon.mod_glossary.entrypendingapproval": "यह प्रविष्टि लंबित अनुमोदन है।", - "addon.mod_glossary.errorloadingentries": "प्रविष्टियाँ लोड करते समय एक त्रुटि हुई।", - "addon.mod_glossary.errorloadingentry": "प्रविष्टि लोड करते समय एक त्रुटि हुई।", - "addon.mod_glossary.errorloadingglossary": "शब्दकोष लोड करते समय एक त्रुटि हुई।", - "addon.mod_glossary.noentriesfound": "कोई प्रविष्टि नहीं मिली।", - "addon.mod_glossary.searchquery": "पूछताछ कीजिए", - "addon.mod_imscp.showmoduledescription": "विवरण दिखाएं", - "addon.mod_lesson.answer": "उत्तर", - "addon.mod_lesson.errorprefetchrandombranch": "इस पाठ में एक रैंडम कंटेंट पेज पर जंप होता है। जब तक इसे वेब ब्राउज़र में शुरू नहीं किया गया है, तब तक इसे ऐप में डालने का प्रयास नहीं किया जा सकता है।", - "addon.mod_lesson.errorreviewretakenotlast": "इस प्रयास की अब समीक्षा नहीं की जा सकती है क्योंकि एक और प्रयास समाप्त हो चुका है।", - "addon.mod_lesson.finishretakeoffline": "यह प्रयास ऑफ़लाइन समाप्त हो गया था।", - "addon.mod_lesson.loginfail": "लॉगिन विफल हुआ। कृपया पुन: प्रयास करें...", - "addon.mod_lesson.noanswer": "कुछ प्रश्नों का उत्तर नहीं दिया गया है। कृपया वापस जाएं और अनुत्तरित प्रश्नों का उत्तर दें।", - "addon.mod_lesson.or": "या", - "addon.mod_lesson.question": "प्रश्न", - "addon.mod_lesson.retakefinishedinsync": "एक ऑफ़लाइन प्रयास सिंक्रनाइज़ किया गया था। क्या आप इसकी समीक्षा करना चाहते हैं?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.warningretakefinished": "साइट पर प्रयास समाप्त हो गया था।", - "addon.mod_lti.errorgetlti": "मॉड्यूल डेटा प्राप्त करने में त्रुटि।", - "addon.mod_lti.errorinvalidlaunchurl": "लॉन्च URL मान्य नहीं है।", - "addon.mod_lti.launchactivity": "गतिविधि का शुभारंभ करें", - "addon.mod_page.errorwhileloadingthepage": "पृष्ठ सामग्री लोड करते समय त्रुटि।", - "addon.mod_quiz.cannotsubmitquizdueto": "यह प्रश्नोत्तरी प्रयास निम्नलिखित कारणों से प्रस्तुत नहीं किया जा सकता है:", - "addon.mod_quiz.confirmcontinueoffline": "यह प्रयास {{$a}} के बाद से सिंक्रनाइज़ नहीं किया गया है। यदि आपने इस प्रयास को किसी अन्य डिवाइस में जारी रखा है, तो आप डेटा खो सकते हैं।", - "addon.mod_quiz.confirmleavequizonerror": "उत्तर सहेजते समय एक त्रुटि हुई। क्या आप वाकई क्विज़ छोड़ना चाहते हैं?", - "addon.mod_quiz.errorbehaviournotsupported": "इस क्विज़ को ऐप में डालने का प्रयास नहीं किया जा सकता क्योंकि प्रश्न व्यवहार ऐप द्वारा समर्थित नहीं है:", - "addon.mod_quiz.errordownloading": "आवश्यक डेटा डाउनलोड करने में त्रुटि।", - "addon.mod_quiz.errorgetattempt": "प्रयास डेटा प्राप्त करने में त्रुटि।", - "addon.mod_quiz.errorgetquestions": "प्रश्न प्राप्त करने में त्रुटि।", - "addon.mod_quiz.errorgetquiz": "प्रश्नोत्तरी डेटा प्राप्त करने में त्रुटि", - "addon.mod_quiz.errorparsequestions": "प्रश्न पढ़ते समय एक त्रुटि हुई। कृपया वेब ब्राउज़र में इस क्विज़ का प्रयास करें।", - "addon.mod_quiz.errorquestionsnotsupported": "इस क्विज़ को ऐप में लेने का प्रयास नहीं किया जा सकता है क्योंकि इसमें ऐप द्वारा समर्थित प्रश्न नहीं हैं:", - "addon.mod_quiz.errorrulesnotsupported": "इस क्विज़ को ऐप में लेने का प्रयास नहीं किया जा सकता क्योंकि इसमें ऐप द्वारा समर्थित नियम नहीं हैं:", - "addon.mod_quiz.errorsaveattempt": "प्रयास डेटा सहेजते समय एक त्रुटि हुई।", - "addon.mod_quiz.finishnotsynced": "समाप्त हो गया, लेकिन सिंक्रनाइज़ नहीं किया गया", - "addon.mod_quiz.opentoc": "नेविगेशन पॉपओवर खोलें", - "addon.mod_quiz.warningattemptfinished": "ऑफ़लाइन प्रयास खारिज कर दिया गया क्योंकि यह साइट पर समाप्त हो गया था या नहीं मिला।", - "addon.mod_quiz.warningdatadiscarded": "कुछ ऑफ़लाइन उत्तरों को छोड़ दिया गया क्योंकि प्रश्न ऑनलाइन संशोधित किए गए थे।", - "addon.mod_quiz.warningdatadiscardedfromfinished": "कुछ अधूरे उत्तर छोड़ दिए जाने के कारण अधूरा रह गया। कृपया अपने उत्तरों की समीक्षा करें और फिर प्रयास को फिर से शुरू करें।", - "addon.mod_resource.errorwhileloadingthecontent": "सामग्री लोड करते समय त्रुटि।", - "addon.mod_resource.openthefile": "फ़ाइल खोलें", - "addon.mod_scorm.cannotcalculategrade": "ग्रेड की गणना नहीं की जा सकी।", - "addon.mod_scorm.dataattemptshown": "यह डेटा प्रयास नंबर {{number}} का है।", - "addon.mod_scorm.errorcreateofflineattempt": "नया ऑफ़लाइन प्रयास करते समय एक त्रुटि हुई। कृपया पुन: प्रयास करें", - "addon.mod_scorm.errordownloadscorm": "SCORM डाउनलोड करने में त्रुटि:\"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "SCORM डेटा प्राप्त करने में त्रुटि।", - "addon.mod_scorm.errorinvalidversion": "क्षमा करें, एप्लिकेशन केवल SCORM 1.2 का समर्थन करता है।", - "addon.mod_scorm.errornotdownloadable": "SCORM पैकेज का डाउनलोड अक्षम है। कृपया अपने साइट व्यवस्थापक से संपर्क करें।", - "addon.mod_scorm.errornovalidsco": "इस SCORM पैकेज में लोड करने के लिए एक दृश्य SCO नहीं है।", - "addon.mod_scorm.errorpackagefile": "क्षमा करें, एप्लिकेशन केवल ज़िप पैकेज का समर्थन करता है।", - "addon.mod_scorm.errorsyncscorm": "सिंक्रनाइज़ करते समय एक त्रुटि हुई। कृपया पुन: प्रयास करें।", - "addon.mod_scorm.modulenameplural": "SCORMs", - "addon.mod_scorm.offlineattemptnote": "इस प्रयास में ऐसा डेटा है जिसे सिंक्रनाइज़ नहीं किया गया है।", - "addon.mod_scorm.offlineattemptovermax": "यह प्रयास नहीं भेजा जा सकता है क्योंकि आप अधिकतम प्रयासों को पार कर चुके हैं।", - "addon.mod_scorm.passed": "उत्तीर्ण", - "addon.mod_scorm.scormstatusnotdownloaded": "यह SCORM पैकेज डाउनलोड नहीं किया गया है। इसे खोलते ही यह अपने आप डाउनलोड हो जाएगा।", - "addon.mod_scorm.scormstatusoutdated": "इस SCORM पैकेज को अंतिम डाउनलोड के बाद संशोधित किया गया है। इसे खोलते ही यह अपने आप डाउनलोड हो जाएगा।", - "addon.mod_scorm.warningofflinedatadeleted": "प्रयास {{number}} से कुछ ऑफ़लाइन डेटा को छोड़ दिया गया है क्योंकि इसे नए प्रयास के रूप में नहीं गिना जा सकता।", - "addon.mod_scorm.warningsynconlineincomplete": "कुछ प्रयास साइट के साथ सिंक्रनाइज़ नहीं किए जा सके क्योंकि अंतिम ऑनलाइन प्रयास अभी तक समाप्त नहीं हुआ है। कृपया पहले ऑनलाइन प्रयास समाप्त करें।", - "addon.mod_survey.cannotsubmitsurvey": "क्षमा करें, आपके सर्वेक्षण को सबमिट करने में एक समस्या थी। कृपया पुन: प्रयास करें।", - "addon.mod_survey.errorgetsurvey": "सर्वेक्षण डेटा प्राप्त करने में त्रुटि।", - "addon.mod_survey.results": "परिणाम", - "addon.mod_url.accessurl": "URL तक पहुँचें", - "addon.mod_url.pointingtourl": "URL जो संसाधन को इंगित करता है।", - "addon.mod_wiki.errorloadingpage": "पृष्ठ लोड करते समय एक त्रुटि हुई।", - "addon.mod_wiki.errornowikiavailable": "इस विकी के पास अभी तक कोई सामग्री नहीं है।", - "addon.mod_wiki.gowikihome": "इस विकी के पास अभी तक कोई सामग्री नहीं है।", - "addon.mod_wiki.subwiki": "उप-विकि", - "addon.mod_wiki.titleshouldnotbeempty": "शीर्षक खाली नहीं होना चाहिए", - "addon.mod_wiki.viewpage": "पृष्ठ देखें", - "addon.mod_wiki.wikipage": "विकी पृष्ठ", - "addon.mod_workshop.assessmentstrategynotsupported": "मूल्यांकन रणनीति {{$a}} समर्थित नहीं है", - "addon.mod_workshop.submissionrequiredtitle": "शीर्षक डालना अनिवार्य है।", - "addon.mod_workshop.warningassessmentmodified": "साइट पर सबमिशन संशोधित किया गया था।", - "addon.mod_workshop.warningsubmissionmodified": "मूल्यांकन साइट पर संशोधित किया गया था।", - "addon.notes.userwithid": "आईडी {{id}} के साथ उपयोगकर्ता", - "addon.notes.warningnotenotsent": "नोट (ओं) को निश्चित रूप से नहीं जोड़ा जा सका {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "सूचनाएं प्राप्त करने में त्रुटि हुई।", - "addon.notifications.notifications": "सूचनाएं", - "addon.notifications.playsound": "ध्वनि चलाएं", - "addon.notifications.therearentnotificationsyet": "कोई सूचना नहीं है।", - "addon.storagemanager.managestorage": "भंडारण प्रबंधित करें", - "assets.countries.AD": "एंडोरा", - "assets.countries.AE": "संयुक्त अरब अमीरात", - "assets.countries.AF": "अफ़्गानिस्तान", - "assets.countries.AG": "एंटीगुआ और बारबुडा", - "assets.countries.AL": "अल्बानिया", - "assets.countries.AM": "अर्मेनिआ", - "assets.countries.AO": "अंगोला", - "assets.countries.AQ": "अन्टार्टिका", - "assets.countries.AR": "अर्जेंटीना", - "assets.countries.AS": "अमेरिकन समोआ", - "assets.countries.AT": "ऑस्ट्रिया", - "assets.countries.AU": "ऑस्ट्रेलिया", - "assets.countries.AW": "अरूबा", - "assets.countries.BB": "बार्बाडोस", - "assets.countries.BD": "बांग्लादेश", - "assets.countries.BE": "बेल्जियम", - "assets.countries.BG": "बुल्गारिया", - "assets.countries.BH": "बहरीन", - "assets.countries.BJ": "बेनिन", - "assets.countries.BM": "बर्मूडा", - "assets.countries.BN": "ब्रुनेइ दारुस्सलम", - "assets.countries.BR": "ब्राज़ील", - "assets.countries.BS": "बहामाज़", - "assets.countries.BT": "भूटान", - "assets.countries.BW": "बोट्स्वाना", - "assets.countries.BY": "बेलारूस", - "assets.countries.BZ": "बेलीज़", - "assets.countries.CA": "कनाडा", - "assets.countries.CF": "मध्य अफ्रीकी गणराज्य", - "assets.countries.CG": "कौंगो", - "assets.countries.CK": "कुक द्वीपसमूह", - "assets.countries.CL": "चिली", - "assets.countries.CM": "कैमेरून", - "assets.countries.CN": "चीन", - "assets.countries.CU": "क्यूबा", - "assets.countries.CY": "साय्प्रस", - "assets.countries.CZ": "चेक गणराज्य", - "assets.countries.DE": "जर्मनी", - "assets.countries.DK": "डेन्मार्क", - "assets.countries.EH": "पश्चिमी सहारा", - "assets.countries.ES": "स्पेन", - "assets.countries.ET": "इथियोपिया", - "assets.countries.FI": "फ़िनलैंड", - "assets.countries.FJ": "फ़िजी", - "assets.countries.FR": "फ़्रांस", - "assets.countries.GB": "यूनाइटेड किंगडम", - "assets.countries.GE": "जॉर्जिया", - "assets.countries.GH": "घाना", - "assets.countries.GL": "ग्रीन्लैन्ड", - "assets.countries.GR": "ग्रीस", - "assets.countries.GT": "ग्वाटेमाला", - "assets.countries.GY": "गुयाना", - "assets.countries.HK": "हांग कांग", - "assets.countries.HN": "होंडुरस", - "assets.countries.HR": "क्रोएशिया", - "assets.countries.HT": "हैती", - "assets.countries.HU": "हंगरी", - "assets.countries.ID": "इंडोनेशिया", - "assets.countries.IE": "आयरलैंड", - "assets.countries.IL": "इज़रायल", - "assets.countries.IN": "भारत", - "assets.countries.IQ": "इराक", - "assets.countries.IT": "इटली", - "assets.countries.JP": "जापान", - "assets.countries.KZ": "कजाखस्तान", - "assets.countries.LB": "लेबनान", - "assets.countries.LI": "लिचेंस्टीन", - "assets.countries.LK": "श्रीलंका", - "assets.countries.LR": "लाइबेरिया", - "assets.countries.LT": "लिथुआनिया", - "assets.countries.LU": "लक्ज़मबर्ग", - "assets.countries.LY": "लीबिया", - "assets.countries.MA": "मोरक्को", - "assets.countries.MC": "मोनाको", - "assets.countries.MG": "मेडागास्कर", - "assets.countries.ML": "माली", - "assets.countries.MM": "म्यांमार", - "assets.countries.MN": "मंगोलिया", - "assets.countries.MU": "मॉरिशस", - "assets.countries.MV": "मालद्वीप", - "assets.countries.MX": "मेक्सिको", - "assets.countries.MY": "मलेशिया", - "assets.countries.PH": "फ़िलिपींस", - "assets.countries.PK": "पाकिस्तान", - "assets.countries.PL": "पोलैंड", - "assets.countries.PT": "पुर्तगाल", - "assets.countries.QA": "क़तर", - "assets.countries.RO": "रोमानिया", - "assets.countries.RS": "सर्बिआ", - "assets.countries.SA": "सऊदी अरब", - "assets.countries.SD": "सूडान", - "assets.countries.SE": "स्वीडन", - "assets.countries.SG": "सिंगापुर", - "assets.countries.SI": "स्लोवेनिया", - "assets.countries.SK": "स्लोवाकिया", - "assets.countries.SN": "सेनेगल", - "assets.countries.SO": "सोमालिया", - "assets.countries.TK": "टोकेलाऊ", - "assets.countries.TR": "टर्की", - "assets.countries.TT": "त्रिनिदाद एवं टोबेगो", - "assets.countries.TW": "ताइवान", - "assets.countries.UA": "यूक्रेन", - "assets.countries.UG": "यूगांडा", - "assets.countries.US": "अमेरिका", - "assets.countries.UY": "उरुग्वे", - "assets.countries.UZ": "उज़्बेकिस्तान", - "assets.countries.VN": "वियतनाम", - "assets.countries.YE": "यमन", - "assets.countries.ZA": "दक्षिण अफ्रीका", - "assets.countries.ZW": "ज़िम्बाब्वे", - "core.accounts": "खाते ", - "core.add": "जोड़िए", - "core.agelocationverification": "आयु और स्थान सत्यापन", - "core.ago": "{{$a}} पहले", - "core.all": "सभी", - "core.allgroups": "सभी समूह", - "core.allparticipants": "सभी प्रतिभागी", - "core.answer": "जवाब", - "core.areyousure": "आप को यकीन हैं ?", - "core.back": "वापस", - "core.block.blocks": "ब्लॉक", - "core.browser": "ब्राउज़र", - "core.cancel": "रद्द करें", - "core.cannotconnect": "कनेक्ट नहीं कर सकता: सत्यापित करें कि आपने URL को सही प्रकार से टाइप किया है और आपकी साइट Moodle {{$a}} या बाद का उपयोग करती है।", - "core.cannotdownloadfiles": "फ़ाइल डाउनलोडिंग अक्षम है। कृपया अपने साइट व्यवस्थापक से संपर्क करें।", - "core.captureaudio": "ध्वनि रिकॉर्ड करें", - "core.capturedimage": "चित्र लिया।", - "core.captureimage": "तस्वीर ले लो", - "core.capturevideo": "वीडियो रिकॉर्ड करो", - "core.category": "वर्ग", - "core.choose": "चुनिए", - "core.choosedots": "चुनें ...", - "core.clearsearch": "स्पष्ट खोज", - "core.clicktohideshow": "विस्तार या पतन के लिए क्लिक करें", - "core.clicktoseefull": "पूर्ण सामग्री देखने के लिए क्लिक करें।", - "core.comments": "टिप्पणियां", - "core.comments.addcomment": "टिप्पणी करे...", - "core.comments.comments": "टिप्पणियां", - "core.comments.commentscount": "टिप्पणियाँ ({{$a}})", - "core.comments.deletecommentbyon": "{{$a.time}} पर {{$a.user}} द्वारा पोस्ट की गई टिप्पणी हटाएं", - "core.comments.eventcommentcreated": "टिप्पणी बनाई गई", - "core.comments.eventcommentdeleted": "टिप्पणी हटा दी गई", - "core.commentscount": "टिप्पणियाँ ({{$a}})", - "core.completion-alt-auto-fail": "समाप्त किया: {{$a}} (पास ग्रेड प्राप्त नहीं हुआ)", - "core.completion-alt-auto-n": "समाप्त नहीं हुई: {{$a}}", - "core.completion-alt-auto-n-override": "समाप्त नहीं हुई: {{$a.modname}} (set by {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "समाप्त: {{$a}} (पास ग्रेड हासिल किया गया)", - "core.completion-alt-auto-y": "समाप्त: {{$a}}", - "core.completion-alt-auto-y-override": "समाप्त: {{$a.modname}} ({{$a.overrideuser}} द्वारा निर्धारित)", - "core.completion-alt-manual-n": "समाप्त नहीं हुआ: {{$a}} समाप्त के रूप में चिह्नित करने के लिए चुनें।", - "core.completion-alt-manual-n-override": "समाप्त नहीं हुआ: {{$a.modname}} ({{$a.overrideuser}} द्वारा निर्धारित) समाप्त के रूप में चिह्नित करने के लिए चुनें।", - "core.completion-alt-manual-y": "समाप्त हो गया: {{$a}} समाप्त नहीं होने के रूप में चिह्नित करने के लिए चुनें।", - "core.completion-alt-manual-y-override": "समाप्त हो गया: {{$a.modname}} ({{$a.overrideuser}} द्वारा निर्धारित) समाप्त नहीं होने के रूप में चिह्नित करने के लिए चुनें।", - "core.confirmcanceledit": "क्या आप वाकई इस पृष्ठ को छोड़ना चाहते हैं? सभी परिवर्तन खो जाएंगे।", - "core.confirmgotabroot": "क्या आप सचमुच {{name}} पर वापस जाना चाहते हैं?", - "core.confirmloss": "क्या आपको यकीन है? सभी परिवर्तन खो जाएंगे।", - "core.confirmopeninbrowser": "क्या आप इसे वेब ब्राउज़र में खोलना चाहते हैं?", - "core.considereddigitalminor": "इस साइट पर खाता बनाने के लिए आप बहुत छोटे हैं।", - "core.content": "सामग्री", - "core.contenteditingsynced": "आपके द्वारा संपादित की जा रही सामग्री सिंक हो गई है।", - "core.contentlinks.chooseaccount": "खाता चुनें", - "core.contentlinks.chooseaccounttoopenlink": "लिंक खोलने के लिए एक खाता चुनें।", - "core.contentlinks.confirmurlothersite": "यह लिंक दूसरी साइट का है। क्या आप इसे खोलना चाहते हैं?", - "core.contentlinks.errornoactions": "इस लिंक के साथ प्रदर्शन करने के लिए कोई क्रिया नहीं मिली।", - "core.contentlinks.errornosites": "इस लिंक को संभालने के लिए कोई साइट नहीं मिली।", - "core.continue": "जारी रखें", - "core.copiedtoclipboard": "क्लिपबोर्ड पर पाठ कॉपी किया गया", - "core.course": "कोर्स", - "core.course.activitydisabled": "आपके संगठन ने इस गतिविधि को मोबाइल ऐप में अक्षम कर दिया है।", - "core.course.activitynotyetviewableremoteaddon": "आपके संगठन ने एक प्लगइन स्थापित किया है जो अभी तक समर्थित नहीं है।", - "core.course.activitynotyetviewablesiteupgradeneeded": "आपके संगठन की Moodle स्थापना को अद्यतन करने की आवश्यकता है।", - "core.course.allsections": "सभी अनुभाग", - "core.course.askadmintosupport": "साइट व्यवस्थापक से संपर्क करें और उन्हें बताएं कि आप इस गतिविधि का उपयोग Moodle मोबाइल ऐप के साथ करना चाहते हैं।", - "core.course.confirmdeletemodulefiles": "क्या आप वाकई इन फ़ाइलों को हटाना चाहते हैं?", - "core.course.confirmdownload": "आप {{size}} डाउनलोड करने वाले हैं। क्या तुम वाकई जारी रखना चाहते हो?", - "core.course.confirmdownloadunknownsize": "डाउनलोड के आकार की गणना करना संभव नहीं था। क्या तुम वाकई जारी रखना चाहते हो?", - "core.course.confirmpartialdownloadsize": "आप कम से कम {{size}} डाउनलोड करने वाले हैं। क्या तुम वाकई जारी रखना चाहते हो?", - "core.course.contents": "अंतर्वस्तु", - "core.course.couldnotloadsectioncontent": "अनुभाग सामग्री लोड नहीं की जा सकी। बाद में पुन: प्रयास करें।", - "core.course.couldnotloadsections": "अनुभागों को लोड नहीं किया जा सका। बाद में पुन: प्रयास करें।", - "core.course.coursesummary": "पाठ्यक्रम सारांश", - "core.course.errordownloadingcourse": "पाठ्यक्रम डाउनलोड करने में त्रुटि।", - "core.course.errordownloadingsection": "अनुभाग डाउनलोड करने में त्रुटि हुई।", - "core.course.errorgetmodule": "गतिविधि डेटा प्राप्त करने में त्रुटि।", - "core.course.hiddenfromstudents": "छात्रों से छिपा हुआ", - "core.course.hiddenoncoursepage": "उपलब्ध है लेकिन पाठ्यक्रम पृष्ठ पर नहीं दिखाया गया है", - "core.course.manualcompletionnotsynced": "मैनुअल पूरा नहीं सिंक्रनाइज़ किया गया", - "core.course.nocontentavailable": "फिलहाल कोई सामग्री उपलब्ध नहीं है।", - "core.course.refreshcourse": "रिफ्रेश कोर्स", - "core.course.sections": "अनुभाग", - "core.course.useactivityonbrowser": "आप अभी भी अपने डिवाइस के वेब ब्राउज़र का उपयोग करके इसका उपयोग कर सकते हैं।", - "core.course.warningmanualcompletionmodified": "एक गतिविधि के मैनुअल पूरा होने को साइट पर संशोधित किया गया था।", - "core.course.warningofflinemanualcompletiondeleted": "निश्चित रूप से '{{name}}' के कुछ ऑफ़लाइन मैनुअल को हटा दिया गया है। {{error}}", - "core.coursedetails": "पाठ्यक्रम विवरण", - "core.courses.addtofavourites": "इस पाठ्यक्रम को स्टार दें", - "core.courses.allowguests": "यह कोर्स अतिथि यूज़र्स को प्रवेश करने देता है", - "core.courses.availablecourses": "उपलब्ध पाठ्यक्रम", - "core.courses.cannotretrievemorecategories": "स्तर {{$ a}} से अधिक गहरी श्रेणियों को पुनर्प्राप्त नहीं किया जा सकता है।", - "core.courses.categories": "पाठ्यक्रम वर्ग", - "core.courses.confirmselfenrol": "क्या आप वाकई इस पाठ्यक्रम में स्वयं को नामांकित करना चाहते हैं?", - "core.courses.courses": "पाठ्यक्रम", - "core.courses.downloadcourses": "पाठ्यक्रम डाउनलोड करें", - "core.courses.enrolme": "मेरे दाखिला लिया", - "core.courses.errorloadcategories": "श्रेणियां लोड करते समय एक त्रुटि हुई।", - "core.courses.errorloadcourses": "पाठ्यक्रम लोड करते समय एक त्रुटि हुई", - "core.courses.errorsearching": "खोज करते समय एक त्रुटि हुई।", - "core.courses.errorselfenrol": "स्वयं नामांकन करते समय एक त्रुटि हुई।", - "core.courses.filtermycourses": "मेरे पाठ्यक्रमों को छानें", - "core.courses.frontpage": "मुखपृष्ठ", - "core.courses.hidecourse": "दृश्य से निकालें", - "core.courses.ignore": "उपेक्षा करें", - "core.courses.mycourses": "मेरे पाठ्यक्रम", - "core.courses.mymoodle": "डैशबोर्ड", - "core.courses.nocoursesyet": "इस वर्ग में कोई कोर्स उपलब्ध नहीं है", - "core.courses.notenrollable": "आप इस पाठ्यक्रम में अपना नामांकन नहीं कर सकते।", - "core.courses.password": "नामांकन की कुंजी", - "core.courses.reload": "सीमा से अधिक लादना", - "core.courses.removefromfavourites": "इस पाठ्यक्रम को अतारांकित करें", - "core.courses.search": "खोज", - "core.courses.searchcourses": "पाठ्यक्रम खोजें", - "core.courses.searchcoursesadvice": "आप अतिथि के रूप में उपयोग करने के लिए पाठ्यक्रम खोजने के लिए खोज पाठ्यक्रम बटन का उपयोग कर सकते हैं या उन पाठ्यक्रमों में खुद को नामांकित कर सकते हैं जो इसे अनुमति देते हैं।", - "core.courses.selfenrolment": "स्व नामांकन करना", - "core.courses.show": "इस कोर्स को दिखाएं", - "core.courses.totalcoursesearchresults": "कुल पाठ्यक्रम: {{$a}}", - "core.currentdevice": "वर्तमान डिवाइस", - "core.datastoredoffline": "डिवाइस में डेटा संग्रहीत है क्योंकि इसे भेजा नहीं जा सका है। इसे बाद में स्वचालित रूप से भेजा जाएगा।", - "core.date": "तारीख", - "core.day": "दिन", - "core.days": "दिन", - "core.decsep": ".", - "core.delete": "डिलीट", - "core.deletedoffline": "ऑफ़लाइन हटा दिया गया", - "core.deleting": "हटाया जा रहा है", - "core.description": "विवरण", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "डिजिटल नाबालिग", - "core.digitalminor_desc": "इस साइट पर खाता बनाने के लिए कृपया अपने माता-पिता / अभिभावक को निम्नलिखित व्यक्ति से संपर्क करें।", - "core.discard": "छोड़ना", - "core.dismiss": "खारिज", - "core.download": "डाउनलोड", - "core.downloading": "डाउनलोड कर रहा है", - "core.edit": " का सम्पादन कीजिए", - "core.emptysplit": "यदि बाएं पैनल खाली है या लोड हो रहा है तो यह पृष्ठ रिक्त दिखाई देगा", - "core.error": "गलती", - "core.errorchangecompletion": "पूर्ण स्थिति बदलते समय एक त्रुटि हुई। कृपया पुन: प्रयास करें।", - "core.errordeletefile": "फ़ाइल को हटाने में त्रुटि। कृपया पुन: प्रयास करें।", - "core.errordownloading": "फ़ाइल डाउनलोड करने में त्रुटि हुई।", - "core.errordownloadingsomefiles": "फ़ाइलों को डाउनलोड करने में त्रुटि। कुछ फाइलें गायब हो सकती हैं।", - "core.errorfileexistssamename": "इस नाम की एक फ़ाइल पहले से मौजूद है।", - "core.errorinvalidform": "फॉर्म में अमान्य डेटा है। कृपया जांचें कि सभी आवश्यक फ़ील्ड भरे हुए हैं और डेटा मान्य है।", - "core.errorinvalidresponse": "अमान्य प्रतिसाद प्राप्त हुआ। यदि त्रुटि बनी रहती है, तो कृपया अपने साइट व्यवस्थापक से संपर्क करें।", - "core.errorloadingcontent": "सामग्री लोड करने में त्रुटि हुई।", - "core.errorofflinedisabled": "ऑफ़लाइन ब्राउज़िंग आपकी साइट पर अक्षम है। ऐप का उपयोग करने के लिए आपको इंटरनेट से जुड़ा होना चाहिए।", - "core.erroropenfilenoapp": "फ़ाइल खोलने में त्रुटि: इस प्रकार की फ़ाइल को खोलने के लिए कोई ऐप नहीं मिला।", - "core.erroropenfilenoextension": "फ़ाइल खोलने में त्रुटि: फ़ाइल में एक्सटेंशन नहीं है।", - "core.erroropenpopup": "यह गतिविधि पॉपअप खोलने का प्रयास कर रही है। यह एप्लिकेशन में समर्थित नहीं है।", - "core.errorrenamefile": "फ़ाइल का नाम बदलने में त्रुटि। कृपया पुन: प्रयास करें।", - "core.errorsync": "सिंक्रनाइज़ करते समय एक त्रुटि हुई। कृपया पुन: प्रयास करें।", - "core.errorsyncblocked": "यह {{$a}} अभी चल रही प्रक्रिया के कारण सिंक्रनाइज़ नहीं किया जा सकता है। बाद में पुन: प्रयास करें। यदि समस्या बनी रहती है, तो एप्लिकेशन को पुनरारंभ करने का प्रयास करें।", - "core.explanationdigitalminor": "यह जानकारी यह निर्धारित करने के लिए आवश्यक है कि आपकी उम्र सहमति की डिजिटल आयु से अधिक है या नहीं। यह वह उम्र है जब कोई व्यक्ति नियम और शर्तों और उनके डेटा को कानूनी रूप से संग्रहीत और संसाधित करने के लिए सहमति दे सकता है।", - "core.filenameexist": "फ़ाइल नाम पहले से मौजूद है: {{$a}}", - "core.fileuploader.audio": "ऑडियो", - "core.fileuploader.camera": "कैमरा", - "core.fileuploader.confirmuploadfile": "आप {{size}} अपलोड करने वाले हैं। क्या तुम वाकई जारी रखना चाहते हो?", - "core.fileuploader.confirmuploadunknownsize": "अपलोड के आकार की गणना करना संभव नहीं था। क्या तुम वाकई जारी रखना चाहते हो?", - "core.fileuploader.errorcapturingaudio": "ऑडियो कैप्चर करने में त्रुटि।", - "core.fileuploader.errorcapturingimage": "छवि कैप्चर करने में त्रुटि", - "core.fileuploader.errorcapturingvideo": "वीडियो कैप्चर करने में त्रुटि।", - "core.fileuploader.errorgettingimagealbum": "एल्बम से छवि प्राप्त करने में त्रुटि।", - "core.fileuploader.errormustbeonlinetoupload": "फाइल अपलोड करने के लिए आपको ऑनलाइन रहना होगा।", - "core.fileuploader.errornoapp": "इस क्रिया को करने के लिए आपके पास कोई ऐप इंस्टॉल नहीं है।", - "core.fileuploader.errorreadingfile": "फ़ाइल पढ़ने में त्रुटि।", - "core.fileuploader.errorwhileuploading": "फ़ाइल अपलोड के दौरान एक त्रुटि हुई।", - "core.fileuploader.file": "फ़ाइल", - "core.fileuploader.filesofthesetypes": "स्वीकृत फ़ाइल प्रकार:", - "core.fileuploader.fileuploaded": "फ़ाइल सफलतापूर्वक अपलोड कर दी गई थी।", - "core.fileuploader.maxbytesfile": "फ़ाइल {{$a.file}} बहुत बड़ी है। आपके द्वारा अपलोड किया जाने वाला अधिकतम आकार {{$a.size}} है।", - "core.fileuploader.photoalbums": "तस्वीर चित्राधार", - "core.fileuploader.readingfile": "फाइल पढ़ना", - "core.fileuploader.readingfileperc": "फ़ाइल पढ़ना: {{$a}}%", - "core.fileuploader.selectafile": "किसी फाइल का चयन करें", - "core.fileuploader.uploadafile": "एक फाइल अपलोड करें", - "core.fileuploader.uploading": "अपलोड हो रहा है", - "core.fileuploader.uploadingperc": "अपलोड करना: {{$a}}%", - "core.fileuploader.video": "वीडियो", - "core.filter": "फ़िल्टर", - "core.folder": "फोल्डर", - "core.forcepasswordchangenotice": "आगे बढ़ने के लिए आपको अपना पासवर्ड बदलना होगा।", - "core.fulllistofcourses": "सभी कोर्सस", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "औसत", - "core.group": "ग्रूप", - "core.groupsseparate": "पृथक समूह", - "core.groupsvisible": "दृष्टिगोचर समूह", - "core.h5p.cancellabel": "रद्द करें", - "core.hasdatatosync": "इस {{$a}} समकालित होने के लिए ऑफ़लाइन डेटा है।", - "core.help": "सहायता", - "core.hide": "छिपादो", - "core.hour": "घंटा", - "core.hours": "घंट", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "छवि", - "core.imageviewer": "छवि दर्शक", - "core.info": "सूचना", - "core.lastaccess": "पिछ्ला आगमन", - "core.lastdownloaded": "अंतिम बार डाउनलोड किया गया", - "core.lastmodified": "पिछ्ला सुधार", - "core.lastsync": "अंतिम तुल्यकालन", - "core.list": "सूची", - "core.listsep": ";", - "core.loading": "ला रहा है", - "core.loadmore": "और लोड करें", - "core.location": "स्थान", - "core.login.authenticating": "प्रमाणित कर रहा है", - "core.login.cancel": "रद्द करें", - "core.login.changepassword": "पासवर्ड बदलिए", - "core.login.confirmdeletesite": "क्या आप वाकई {{sitename}} साइट को हटाना चाहते हैं?", - "core.login.connect": "जुडिये!", - "core.login.connecttomoodle": "मूडल से कनेक्ट करें", - "core.login.contactyouradministrator": "आगे की मदद के लिए अपनी साइट के व्यवस्थापक से संपर्क करें।", - "core.login.contactyouradministratorissue": "कृपया निम्न समस्या की जाँच करने के लिए अपने साइट व्यवस्थापक से पूछें: {{$a}}", - "core.login.createaccount": "मेरा नया खाता बनाइए", - "core.login.createuserandpass": "लॉगिन करने के लिए यूज़रनेम और पास्वर्ड बनाइए", - "core.login.credentialsdescription": "कृपया लॉग इन करने के लिए अपना उपयोगकर्ता नाम और पासवर्ड प्रदान करें।", - "core.login.emailconfirmsent": "

                आपके पते {{$a}}

                पर एक ईमेल भेजा जाना चाहिए था\n

                इसमें आपके पंजीकरण को पूरा करने के लिए आसान निर्देश हैं। \n

                यदि आपको कुछ कठिनाई हो रही है, तो साइट व्यवस्थापक से संपर्क करें। ", - "core.login.emailconfirmsentnoemail": "

                आपके पते पर एक ईमेल भेजा जाना चाहिए।

                इसमें आपका पंजीकरण पूरा करने के लिए आसान निर्देश शामिल हैं।

                यदि आपको कठिनाई जारी है, तो साइट व्यवस्थापक से संपर्क करें। ", - "core.login.emailconfirmsentsuccess": "पुष्टिकरण ईमेल सफलतापूर्वक भेजा गया", - "core.login.emailnotmatch": "ईमेल एक - दूसरे से मेल नहीं खाते", - "core.login.erroraccesscontrolalloworigin": "जिस क्रॉस-ऑरिजिन कॉल को आप करने की कोशिश कर रहे हैं, उसे अस्वीकार कर दिया गया है। कृपया https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_hhhium पर जाएं", - "core.login.errordeletesite": "इस साइट को हटाते समय एक त्रुटि हुई। कृपया पुन: प्रयास करें।", - "core.login.errorupdatesite": "साइट के टोकन को अपडेट करते समय एक त्रुटि हुई।", - "core.login.findyoursite": "अपनी साइट खोजें", - "core.login.firsttime": "क्या आप यहाँ पहली बार आयें हैं?", - "core.login.forcepasswordchangenotice": "आगे बढ़ने के लिए आपको अपना पासवर्ड बदलना होगा।", - "core.login.forgotten": "क्या आप अपना यूज़रनेम या पासवर्ड भूल गये हैं?", - "core.login.help": "सहायता", - "core.login.helpmelogin": "

                दुनिया भर में कई हजारों Moodle साइट्स हैं। यह ऐप केवल उन Moodle साइटों से कनेक्ट हो सकता है जिन्होंने विशेष रूप से मोबाइल ऐप एक्सेस सक्षम किया है।

                यदि आप अपनी Moodle साइट से कनेक्ट नहीं कर सकते हैं, तो आपको अपने साइट व्यवस्थापक से संपर्क करने और उन्हें पढ़ने के लिए कहने की आवश्यकता है \"http://docs.moodle.org/en/Mobile_app\" लक्ष्य = \"_blank\"> http://docs.moodle.org/en/Mobile_app

                में एप्लिकेशन का परीक्षण करने के लिए Moodle डेमो साइट प्रकार शिक्षक या छात्र साइट पते फ़ील्ड में और कनेक्ट बटन ", - "core.login.instructions": "निर्देश", - "core.login.invalidaccount": "कृपया अपना लॉगिन विवरण जांचें या साइट के विन्यास की जांच करने के लिए अपने साइट व्यवस्थापक से पूछें।", - "core.login.invalidemail": "अमान्य ईमेल पता", - "core.login.invalidmoodleversion": "अमान्य Moodle संस्करण। आवश्यक न्यूनतम संस्करण {{$a}} है।", - "core.login.invalidsite": "साइट URL अमान्य है।", - "core.login.invalidtime": "अमान्य समय", - "core.login.invalidvaluemax": "अधिकतम मूल्य {{$ a}} है", - "core.login.invalidvaluemin": "न्यूनतम मान {{$ a}} है", - "core.login.localmobileunexpectedresponse": "Moodle मोबाइल एडिशनल फीचर्स चेक ने अप्रत्याशित प्रतिक्रिया दी। आपको मानक मोबाइल सेवा का उपयोग करके प्रमाणित किया जाएगा।", - "core.login.loggedoutssodescription": "आपको फिर से प्रमाणित करना होगा। आपको ब्राउज़र विंडो में साइट पर लॉग इन करना होगा", - "core.login.login": "लॉग-इन", - "core.login.loginbutton": "लॉग इन करें", - "core.login.logininsiterequired": "आपको ब्राउज़र विंडो में साइट पर लॉग इन करना होगा।", - "core.login.loginsteps": "नमस्ते! पूर्ण प्रवेश के लिये आपको एक मिनट का समय निकाल कर इस सा‌इट पर अपना ऍका‌उन्ट बनना होगा। प्रत्येक कोर्स के लिये एक\n\"प्रवेश की\" भी हो सकती है, जिसकी आवश्यकता आपको बाद में\nपड़ सकती है। कृपया निम्नलिखित निर्देशों का पालन कीजिये:\n\n

                  \n
                1. नये ऍकाउन्ट का फ़ॉर्म भरिये।
                2. \n\n
                3. हम आपको ई-मेल भेजेंगे।
                4. \n\n
                5. आप भेजे गए ई-मेल सन्देश को पढ़ने के बाद उसमें दी गई लिंक पर क्लिक करिए।
                6. \n\n
                7. आपके ऍकाउन्ट की पुष्टि होने के बाद आप लॉग-इन कर सकेंगे।
                8. \n\n
                9. अब आप अपना मनपसन्द कोर्स चुन सकते हैं।
                10. \n\n
                11. आगर कोर्स के लिये \"प्रवेश की\" की आवश्यकता है, तो अपने\nटीचर द्वारा दी गयी \"प्रवेश की\" क उपयोग करें. आप कोर्स में प्रवेश\nपा सकेंगे।
                12. \n\n
                13. अब आप कोर्स में पूर्ण प्रवेश पा सकेंगे. अब से आपको अपने कोर्स में प्रवेश पाने के लिये केवल अपना यूज़रनेम और पासवर्ड देना गा।
                14. \n\n
                ", - "core.login.missingemail": "ई-मेल गायब", - "core.login.missingfirstname": "प्रथम नाम गायब", - "core.login.missinglastname": "उपनाम गायब", - "core.login.mobileservicesnotenabled": "आपकी साइट पर मोबाइल पहुंच सक्षम नहीं है। कृपया अपने साइट व्यवस्थापक से संपर्क करें यदि आपको लगता है कि इसे सक्षम किया जाना चाहिए।", - "core.login.newaccount": "नया ऍकाउन्ट", - "core.login.notloggedin": "आपको लॉग इन करने की जरूरत है।", - "core.login.password": "पासवर्ड", - "core.login.passwordrequired": "पासवर्ड आवश्यक", - "core.login.profileinvaliddata": "अमान्य मूल्य", - "core.login.recaptchachallengeimage": "reCAPTCHA चुनौती छवि", - "core.login.recaptchaexpired": "सत्यापन समाप्त हो गया। सुरक्षा प्रश्न का उत्तर फिर से दें।", - "core.login.recaptchaincorrect": "सुरक्षा प्रश्न उत्तर गलत है।", - "core.login.reconnect": "फिर से कनेक्ट करें", - "core.login.reconnectdescription": "आपका प्रमाणीकरण टोकन अमान्य है या समाप्त हो चुका है। आपको साइट को फिर से कनेक्ट करना होगा।", - "core.login.reconnectssodescription": "आपका प्रमाणीकरण टोकन अमान्य है या समाप्त हो चुका है। आपको साइट को फिर से कनेक्ट करना होगा। आपको ब्राउज़र विंडो में साइट पर लॉग इन करना होगा।", - "core.login.searchby": "खोज से:", - "core.login.selectacountry": "एक देश को चुनिए", - "core.login.selectsite": "Please select your site:", - "core.login.signupplugindisabled": "{{$ a}} सक्षम नहीं है।", - "core.login.siteaddress": "साइट का पता", - "core.login.sitehasredirect": "आपकी साइट में कम से कम एक HTTP पुनर्निर्देशित है। ऐप पुनर्निर्देशित नहीं कर सकता है, यह समस्या हो सकती है जो ऐप को आपकी साइट से कनेक्ट करने से रोक रही है।", - "core.login.siteinmaintenance": "आपकी साइट रखरखाव मोड में है", - "core.login.sitepolicynotagreederror": "साइट नीति सहमत नहीं है।", - "core.login.siteurl": "साईट यूआरएल", - "core.login.siteurlrequired": "साइट URL की आवश्यकता है i.e http://www.yourmoodlesite.org ", - "core.login.startsignup": "नया ऍकाउन्ट बनाइये", - "core.login.stillcantconnect": "अभी भी नहीं जुड़ सकता है?", - "core.login.username": "यूज़रनेम", - "core.login.usernameoremail": "यूज़रनेम या ईमेल दर्ज करें", - "core.login.usernamerequired": "उपयोगकर्ता का नाम (आवश्यक", - "core.login.visitchangepassword": "क्या आप पासवर्ड बदलने के लिए साइट पर जाना चाहते हैं?", - "core.login.webservicesnotenabled": "आपकी साइट में वेब सेवाएँ सक्षम नहीं हैं। कृपया अपने साइट व्यवस्थापक से संपर्क करें यदि आपको लगता है कि उन्हें सक्षम होना चाहिए।", - "core.lostconnection": "आपका प्रमाणीकरण टोकन अमान्य है या समाप्त हो चुका है। आपको साइट को फिर से कनेक्ट करना होगा।", - "core.mainmenu.changesite": "साइट बदलें", - "core.mainmenu.help": "सहायता", - "core.mainmenu.logout": "लॉग ऑउट", - "core.mainmenu.website": "वेबसाइट", - "core.maxsizeandattachments": "नई फ़ाइलों के लिए अधिकतम आकार: {$ a->size}}, अधिकतम संलग्नक: {{$a.attachments}}", - "core.min": "मिनट", - "core.mins": "मिनट", - "core.misc": "विविध", - "core.mod_book": "पुस्तक", - "core.mod_chat": "चैट", - "core.mod_file": "फ़ाइल", - "core.mod_forum": "फ़ोरम", - "core.mod_label": "लेबल", - "core.mod_workshop": "वर्कशॉप", - "core.more": "और", - "core.name": "नाम", - "core.networkerroriframemsg": "यह सामग्री ऑफ़लाइन उपलब्ध नहीं है। कृपया इंटरनेट से कनेक्ट करें और पुनः प्रयास करें।", - "core.networkerrormsg": "साइट से कनेक्ट करने में समस्या थी। कृपया अपने संपर्क की जांच करे और फिर से प्रयास करें।", - "core.never": "कभी नहीं", - "core.next": "अगला", - "core.no": "नहीं", - "core.nograde": "कोई ग्रेड नही", - "core.none": "कोई नहीं", - "core.nopasswordchangeforced": "आप अपना पासवर्ड बदले बिना आगे नहीं बढ़ सकते।", - "core.nopermissionerror": "क्षमा करें, लेकिन आपके पास वर्तमान में ऐसा करने की अनुमति नहीं है", - "core.noresults": "परिणाम", - "core.noselection": "कोई चयन नहीं", - "core.notapplicable": "n/a", - "core.notsent": "नहीं भेजा गया", - "core.numwords": "{{$a}} शब्द", - "core.offline": "ऑफ़लाइन", - "core.ok": "ठीक है", - "core.online": "ऑनलाइन", - "core.openfullimage": "पूर्ण आकार की छवि प्रदर्शित करने के लिए यहां क्लिक करें", - "core.openinbrowser": "ब्राउज़र में खोलें", - "core.percentagenumber": "{{$a}}%", - "core.phone": "टेलीफोन", - "core.pictureof": "{{$a}} का चित्र", - "core.pulltorefresh": "रीफ़्रेश करने के लिए खींचें", - "core.question.answer": "उत्तर", - "core.question.cannotdeterminestatus": "स्थिति का निर्धारण नहीं किया जा सकता", - "core.question.complete": "पूरा", - "core.question.correct": "सही", - "core.question.errorattachmentsnotsupported": "एप्लिकेशन उत्तर देने के लिए फ़ाइलों को संलग्न करने का समर्थन नहीं करता है।", - "core.question.errorinlinefilesnotsupported": "एप्लिकेशन अभी तक इनलाइन फ़ाइलों के संपादन का समर्थन नहीं करता है।", - "core.question.errorquestionnotsupported": "यह प्रश्न प्रकार ऐप द्वारा समर्थित नहीं है: {{$a}}", - "core.question.howtodraganddrop": "चुनने के लिए टैप करें फिर ड्रॉप करने के लिए टैप करें।", - "core.question.questionmessage": "प्रश्न {{$a}}: {{$b}}", - "core.redirectingtosite": "आपको साइट पर पुनः निर्देशित किया जाएगा।", - "core.refresh": "ताज़ा करना", - "core.remove": "निकले", - "core.required": "ज़रूरी हैं", - "core.requireduserdatamissing": "इस उपयोगकर्ता के पास कुछ आवश्यक प्रोफ़ाइल डेटा का अभाव है। कृपया अपनी साइट में डेटा दर्ज करें और पुनः प्रयास करें।
                {{$a}}", - "core.resources": "संसाधने", - "core.restore": "रिस्टोर", - "core.retry": "पुन: प्रयास करें", - "core.savechanges": "परिवर्तन सहेजें", - "core.search": "खोज", - "core.searching": "खोज कर", - "core.searchresults": "खोज परिणाम", - "core.sec": "सेकेंड", - "core.secs": "सेकेंड्स", - "core.settings.about": "के बारे में", - "core.settings.cannotsyncoffline": "ऑफ़लाइन सिंक्रनाइज़ नहीं किया जा सकता।", - "core.settings.cannotsyncwithoutwifi": "समन्‍वयित नहीं किया जा सकता है क्‍योंकि वर्तमान सेटिंग केवल वाई-फाई से कनेक्‍ट होने पर समन्‍वयित करने की अनुमति देती है। कृपया वाई-फाई नेटवर्क से कनेक्ट करें।", - "core.settings.compilationinfo": "संकलन की जानकारी", - "core.settings.cordovadevicemodel": "कॉर्डोवा डिवाइस मॉडल", - "core.settings.cordovadeviceosversion": "कॉर्डोवा डिवाइस ओएस संस्करण", - "core.settings.cordovadeviceplatform": "कॉर्डोवा डिवाइस प्लेटफॉर्म", - "core.settings.cordovadeviceuuid": "Cordova device UUID", - "core.settings.cordovaversion": "कॉर्डोवा संस्करण", - "core.settings.currentlanguage": "वर्तमान भाषा", - "core.settings.debugdisplaydescription": "यदि सक्षम किया गया है, तो त्रुटि मोडल्यूस संभव हो तो त्रुटि के बारे में अधिक डेटा प्रदर्शित करेगा।", - "core.settings.deletesitefiles": "क्या आप वाकई '{{sitename}}' साइट से डाउनलोड की गई फ़ाइलों को हटाना चाहते हैं?", - "core.settings.deletesitefilestitle": "साइट फ़ाइलें हटाएं", - "core.settings.deviceinfo": "यंत्र की जानकारी", - "core.settings.deviceos": "डिवाइस ओएस", - "core.settings.displayformat": "प्रारूप को प्रदर्शित करें", - "core.settings.enabledownloadsection": "अनुभागों को डाउनलोड करना सक्षम करें", - "core.settings.enablerichtexteditor": "पाठ संपादक सक्षम करें", - "core.settings.enablerichtexteditordescription": "यदि सक्षम है, तो सामग्री दर्ज करते समय एक पाठ संपादक उपलब्ध होगा।", - "core.settings.enablesyncwifi": "वाई-फाई पर केवल तब ही सिंक करने की अनुमति दें", - "core.settings.errordeletesitefiles": "साइट फ़ाइलों को हटाने में त्रुटि।", - "core.settings.errorsyncsite": "साइट डेटा सिंक्रनाइज़ करने में त्रुटि। अपने इंटरनेट कनेक्शन की जाँच करें और पुन: प्रयास करें।", - "core.settings.estimatedfreespace": "अनुमानित खाली स्थान", - "core.settings.filesystemroot": "फ़ाइल सिस्टम रूट", - "core.settings.general": "सामान्य", - "core.settings.language": "भाषा", - "core.settings.license": "GPL लाइसेन्स", - "core.settings.localnotifavailable": "स्थानीय सूचनाएं उपलब्ध हैं", - "core.settings.locationhref": "वेब व्यू URL", - "core.settings.locked": "बंद", - "core.settings.loggedin": "ऑनलाइन", - "core.settings.loggedoff": "ऑनलाइन नहीं", - "core.settings.navigatorlanguage": "नेविगेटर भाषा", - "core.settings.navigatoruseragent": "नेविगेटर userAgent", - "core.settings.networkstatus": "Internet connection status", - "core.settings.privacypolicy": "गोपनीयता नीति", - "core.settings.reportinbackground": "त्रुटियों की रिपोर्ट स्वचालित रूप से करें", - "core.settings.settings": "सेट्टिंग्स", - "core.settings.showdownloadoptions": "डाउनलोड विकल्प दिखाएं", - "core.settings.sites": "साईट्स", - "core.settings.spaceusage": "अंतरिक्ष उपयोग", - "core.settings.synchronization": "तुल्यकालन", - "core.settings.synchronizenow": "अब सिंक्रनाइज़ करें", - "core.settings.syncsettings": "सिंक्रनाइज़ेशन सेटिंग्स", - "core.settings.total": "कुल", - "core.settings.wificonnection": "वाईफाई कनेक्शन", - "core.sharedfiles.chooseaccountstorefile": "फ़ाइल को स्टोर करने के लिए एक खाता चुनें।", - "core.sharedfiles.chooseactionrepeatedfile": "इस नाम की एक फ़ाइल पहले से मौजूद है। क्या आप मौजूदा फ़ाइल को बदलना चाहते हैं या इसे \"{{$a}}\" नाम देना चाहते हैं?", - "core.sharedfiles.errorreceivefilenosites": "There are no sites stored. Please add a site before sharing a file with the app.", - "core.sharedfiles.nosharedfiles": "इस साइट पर कोई साझा फ़ाइलें संग्रहीत नहीं हैं।", - "core.sharedfiles.nosharedfilestoupload": "आपके पास यहां अपलोड करने के लिए कोई फाइल नहीं है। यदि आप किसी अन्य ऐप से फ़ाइल अपलोड करना चाहते हैं, तो फ़ाइल का पता लगाएं और 'ओपन' बटन पर क्लिक करें।", - "core.sharedfiles.rename": "नाम बदलें", - "core.sharedfiles.replace": "बदलने के", - "core.sharedfiles.sharedfiles": "फ़ाइलें साझा की हैं", - "core.sharedfiles.successstorefile": "फ़ाइल सफलतापूर्वक संग्रहीत की गई। अपनी निजी फ़ाइलों को अपलोड करने के लिए फ़ाइल का चयन करें या किसी गतिविधि में उपयोग करें।", - "core.show": "दिखाए", - "core.showless": "कम दिखाएं...", - "core.showmore": "अधिक दिखाएं...", - "core.site": "साइट", - "core.sitehome.sitenews": "साइट समाचार", - "core.sitemaintenance": "साइट का रखरखाव चल रहा है और वर्तमान में उपलब्ध नहीं है", - "core.sizeb": "बाइट्स", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.sorry": "माफ़ कीजिये...", - "core.strftimedate": "%d %B %Y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.tablet": "Tablet", - "core.teachers": "अध्यपके", - "core.thereisdatatosync": "सिंक्रनाइज़ होने के लिए ऑफ़लाइन {{$a}} हैं।", - "core.thisdirection": "ltr", - "core.time": "समय", - "core.today": "आज", - "core.tryagain": "पुनः प्रयास करें", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "उह ओह!", - "core.unexpectederror": "अप्रत्याशित त्रुटि। कृपया आवेदन को बंद करें और फिर से खोलें।", - "core.unicodenotsupported": "इस साइट पर कुछ इमोजीस समर्थित नहीं हैं। संदेश भेजे जाने पर ऐसे वर्ण हटा दिए जाएंगे।", - "core.unicodenotsupportedcleanerror": "यूनिकोड वर्णों की सफाई करते समय खाली पाठ पाया गया।", - "core.unknown": "अनजान", - "core.unlimited": "असीमित", - "core.unzipping": "अनज़िप", - "core.user": "उपयोगकर्ता", - "core.user.address": "पता", - "core.user.city": "शहर/गाँव", - "core.user.contact": "संपर्क करें", - "core.user.country": "देश", - "core.user.description": "विवरण", - "core.user.details": "विस्तार", - "core.user.detailsnotavailable": "इस उपयोगकर्ता का विवरण आपके लिए उपलब्ध नहीं है।", - "core.user.editingteacher": "टीचर", - "core.user.email": "ई-मेल पता", - "core.user.emailagain": "ई-मेल (दोबारा)", - "core.user.errorloaduser": "उपयोगकर्ता लोड करने में त्रुटि हुई।", - "core.user.firstname": "प्रथम नाम", - "core.user.interests": "रुचिया", - "core.user.lastname": "सरनेम", - "core.user.newpicture": "नया चित्र", - "core.user.phone1": "टेलीफोन", - "core.user.roles": "भूमिकाएं", - "core.user.sendemail": "ईमेल", - "core.user.student": "विद्यार्थी", - "core.user.webpage": "वेब पेज", - "core.userdeleted": "यह यूज़र ऍकाउन्ट डिलीट कर दिया गया है", - "core.userdetails": "उपयोगकर्ता का विवरण", - "core.users": "उपयोगकर्ता", - "core.view": "देखें", - "core.viewcode": "कोड देखें", - "core.vieweditor": "संपादक देखें", - "core.viewembeddedcontent": "एम्बेडेड सामग्री देखें", - "core.viewprofile": "प्रोफ़ाइल देखें", - "core.warningofflinedatadeleted": "{{componenet}} '{{name}}' से ऑफ़लाइन डेटा हटा दिया गया है। {{error}}", - "core.whoops": "Oops!", - "core.whyisthishappening": "ये क्यों हो रहा है?", - "core.wsfunctionnotavailable": "वेब सेवा फ़ंक्शन उपलब्ध नहीं है।", - "core.year": "साल", - "core.years": "सालो", - "core.yes": "हाँ" -} \ No newline at end of file diff --git a/src/assets/lang/hr.json b/src/assets/lang/hr.json deleted file mode 100644 index 188e1210d..000000000 --- a/src/assets/lang/hr.json +++ /dev/null @@ -1,1690 +0,0 @@ -{ - "addon.badges.alignment": "Poravnanje", - "addon.badges.badgedetails": "Detalji značke", - "addon.badges.badges": "Značke", - "addon.badges.bendorsement": "Odobrenje", - "addon.badges.claimcomment": "Komentar odobrenja", - "addon.badges.contact": "Kontakt", - "addon.badges.dateawarded": "Datum izdavanja", - "addon.badges.expired": "Isteklo", - "addon.badges.expirydate": "Datum isteka", - "addon.badges.imageauthorname": "Ime autora fotografije", - "addon.badges.imageauthorurl": "URL autora fotografije", - "addon.badges.imagecaption": "Natpis ispod slike", - "addon.badges.issuancedetails": "Istek značke", - "addon.badges.issuerdetails": "Detalji o izdavaču", - "addon.badges.issueremail": "E-pošta", - "addon.badges.issuername": "Ime izdavača", - "addon.badges.issuerurl": "URL izdavača", - "addon.badges.language": "Jezik", - "addon.badges.noalignment": "Ova značka nema zadane vanjske vještine ili standarde.", - "addon.badges.nobadges": "Nema dostupnih značaka.", - "addon.badges.norelated": "Ova značka nema povezane značke.", - "addon.badges.recipientdetails": "Podaci o dobitniku", - "addon.badges.relatedbages": "Srodne značke", - "addon.badges.version": "Inačica", - "addon.badges.warnexpired": "(Ova značka je istekla!)", - "addon.block_activitymodules.pluginname": "Aktivnosti", - "addon.block_activityresults.pluginname": "Rezultati aktivnosti", - "addon.block_badges.pluginname": "Najnovije značke", - "addon.block_blogmenu.pluginname": "Blog izbornik", - "addon.block_blogrecent.pluginname": "Nedavni blog članci", - "addon.block_blogtags.pluginname": "Oznake bloga", - "addon.block_calendarmonth.pluginname": "Kalendar", - "addon.block_calendarupcoming.pluginname": "Buduća događanja", - "addon.block_comments.pluginname": "Komentari", - "addon.block_completionstatus.pluginname": "Stupanj dovršenosti e-kolegija", - "addon.block_glossaryrandom.pluginname": "Slučajni odabir iz rječnika", - "addon.block_learningplans.pluginname": "Planovi učenja", - "addon.block_myoverview.all": "Svi", - "addon.block_myoverview.allincludinghidden": "Svi", - "addon.block_myoverview.favourites": "Označeno zvjezdicom", - "addon.block_myoverview.future": "Buduće", - "addon.block_myoverview.hiddencourses": "Skriveno", - "addon.block_myoverview.inprogress": "U tijeku", - "addon.block_myoverview.lastaccessed": "Zadnji pristup", - "addon.block_myoverview.morecourses": "Prikaz ostalih e-kolegija", - "addon.block_myoverview.nocourses": "Nema e-kolegija", - "addon.block_myoverview.past": "Prošlo", - "addon.block_myoverview.pluginname": "Pregled e-kolegija", - "addon.block_myoverview.title": "Naziv e-kolegija", - "addon.block_newsitems.pluginname": "Nove obavijesti", - "addon.block_onlineusers.pluginname": "Korisnici online", - "addon.block_privatefiles.pluginname": "Osobne datoteke", - "addon.block_recentactivity.pluginname": "Nedavna aktivnost", - "addon.block_recentlyaccessedcourses.nocourses": "Nema nedavnih e-kolegija", - "addon.block_recentlyaccessedcourses.pluginname": "E-kolegiji kojima ste nedavno pristupili", - "addon.block_recentlyaccesseditems.noitems": "Nema nedavnih stavki", - "addon.block_recentlyaccesseditems.pluginname": "Nedavno pristupljene stavke", - "addon.block_rssclient.pluginname": "RSS klijent", - "addon.block_selfcompletion.pluginname": "Samostalni dovršetak", - "addon.block_sitemainmenu.pluginname": "Glavni izbornik", - "addon.block_starredcourses.nocourses": "Nema e-kolegija označenih zvjezdicom", - "addon.block_starredcourses.pluginname": "E-kolegiji označeni zvjezdicom", - "addon.block_tags.pluginname": "Oznake", - "addon.block_timeline.duedate": "Rok", - "addon.block_timeline.next30days": "Sljedećih 30 dana", - "addon.block_timeline.next3months": "Sljedeća 3 mjeseca", - "addon.block_timeline.next6months": "Sljedećih 6 mjeseci", - "addon.block_timeline.next7days": "Sljedećih 7 dana", - "addon.block_timeline.nocoursesinprogress": "Nema e-kolegija u tijeku", - "addon.block_timeline.noevents": "Nema budućih aktivnosti s rokovima", - "addon.block_timeline.overdue": "Kašnjenje", - "addon.block_timeline.pluginname": "Vremenska crta", - "addon.block_timeline.sortbycourses": "Sortirajte po e-kolegijima", - "addon.block_timeline.sortbydates": "Sortirajte po datumima", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Blog članci", - "addon.blog.linktooriginalentry": "Poveznica na izvorni blog članak", - "addon.blog.noentriesyet": "Nema vidljivih članaka", - "addon.blog.publishtonoone": "Sebi (nacrt)", - "addon.blog.publishtosite": "Svima na sustavu", - "addon.blog.publishtoworld": "Cijeli svijet", - "addon.blog.showonlyyourentries": "Prikaži samo moje zapise", - "addon.blog.siteblogheading": "Blog sustava", - "addon.calendar.allday": "Cijeli dan", - "addon.calendar.calendar": "Kalendar", - "addon.calendar.calendarevents": "Događaji u kalendaru", - "addon.calendar.calendarreminders": "Kalendar podsjetnika", - "addon.calendar.categoryevents": "Kategorije događaja", - "addon.calendar.confirmeventdelete": "Želite li izbrisati događaj \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "Događaj \"{{$a.name}}\" je dio niza. Želite li izbrisati samo taj događaj ili sve događaje {{$a.count}} u nizu?", - "addon.calendar.courseevents": "Događaji na razini e-kolegija", - "addon.calendar.daynext": "Sljedeći dan", - "addon.calendar.dayprev": "Prethodni dan", - "addon.calendar.defaultnotificationtime": "Zadano vrijeme za obavijesti", - "addon.calendar.deleteallevents": "Obriši sve događaje", - "addon.calendar.deleteevent": "Izbriši događaj", - "addon.calendar.deleteoneevent": "Obriši ovaj događaj", - "addon.calendar.durationminutes": "Trajanje u minutama", - "addon.calendar.durationnone": "Nema određeno trajanje", - "addon.calendar.durationuntil": "Do", - "addon.calendar.editevent": "Promijeni postavke događaja", - "addon.calendar.errorloadevent": "Greška pri učitavanju događanja.", - "addon.calendar.errorloadevents": "Greška pri učitavanju događanja.", - "addon.calendar.eventcalendareventdeleted": "Obrisan događaj u kalendaru", - "addon.calendar.eventduration": "Trajanje", - "addon.calendar.eventendtime": "Završava", - "addon.calendar.eventkind": "Tip događaja", - "addon.calendar.eventname": "Naziv događaja", - "addon.calendar.eventstarttime": "Počinje", - "addon.calendar.eventtype": "Vrsta događaja", - "addon.calendar.fri": "Pet", - "addon.calendar.friday": "Petak", - "addon.calendar.gotoactivity": "Idi na aktivnost", - "addon.calendar.groupevents": "Grupni događaji", - "addon.calendar.invalidtimedurationminutes": "Trajanje u minutama koje ste unijeli nije ispravno. Molimo unesite trajanje u minutama dulje od 0 ili odaberite 'Nema određeno trajanje'.", - "addon.calendar.invalidtimedurationuntil": "Datum i vrijeme koje ste odabrali za završetak je zapravo prije datuma i vremena u kojem događaj otpočinje. Molimo ispravite to.", - "addon.calendar.mon": "Pon", - "addon.calendar.monday": "Ponedjeljak", - "addon.calendar.monthlyview": "Mjesečni prikaz", - "addon.calendar.newevent": "Novi događaj", - "addon.calendar.noevents": "Nema događanja", - "addon.calendar.nopermissiontoupdatecalendar": "Trenutačno nemate ovlasti osvježavanja događaja u kalendaru.", - "addon.calendar.reminders": "Podsjetnici", - "addon.calendar.repeatedevents": "Događaji koji se ponavljaju", - "addon.calendar.repeateditall": "Primijeni promjene na {{$a}} događaja u ovom repetitivnom nizu", - "addon.calendar.repeateditthis": "Primijeni promjene samo na ovaj događaj", - "addon.calendar.repeatevent": "Ponovi ovaj događaj", - "addon.calendar.repeatweeksl": "Ponovi tjedno, stvaranje ispočetka", - "addon.calendar.sat": "Sub", - "addon.calendar.saturday": "Subota", - "addon.calendar.setnewreminder": "Postavi novi podsjetnik", - "addon.calendar.siteevents": "Događaji na razini sjedišta", - "addon.calendar.sun": "Ned", - "addon.calendar.sunday": "Nedjelja", - "addon.calendar.thu": "Čet", - "addon.calendar.thursday": "Četvrtak", - "addon.calendar.today": "Danas", - "addon.calendar.tomorrow": "Sutra", - "addon.calendar.tue": "Uto", - "addon.calendar.tuesday": "Utorak", - "addon.calendar.typecategory": "Kategorija događaja", - "addon.calendar.typeclose": "Zatvori događaj", - "addon.calendar.typecourse": "Događaji unutar e-kolegija", - "addon.calendar.typedue": "Predstojeći događaj", - "addon.calendar.typegradingdue": "Predstojeći događaj s ocjenjivanjem", - "addon.calendar.typegroup": "Događaji unutar grupe", - "addon.calendar.typeopen": "Otvori događaj", - "addon.calendar.typesite": "Dpgađaj na razini sitea", - "addon.calendar.typeuser": "Osobni događaji", - "addon.calendar.upcomingevents": "Budući događaji", - "addon.calendar.userevents": "Korisnički događaji", - "addon.calendar.wed": "Sri", - "addon.calendar.wednesday": "Srijeda", - "addon.calendar.when": "Kada", - "addon.calendar.yesterday": "Jučer", - "addon.competency.activities": "Aktivnosti", - "addon.competency.competencies": "Kompetencije", - "addon.competency.coursecompetencies": "Kompetencije na razini e-kolegija", - "addon.competency.duedate": "Rok", - "addon.competency.errornocompetenciesfound": "Kompetencije nisu pronađene", - "addon.competency.evidence": "Dokaz", - "addon.competency.evidence_competencyrule": "Ispunjeno je pravilo kompetencije.", - "addon.competency.evidence_coursecompleted": "Kolegij '{{$a}}' je dovršen.", - "addon.competency.evidence_coursemodulecompleted": "Aktivnost '{{$a}}' je dovršena.", - "addon.competency.evidence_manualoverride": "Ocjena kompetencije ručno je postavljena.", - "addon.competency.learningplans": "Planovi učenja", - "addon.competency.myplans": "Moji planovi učenja", - "addon.competency.noactivities": "Nema aktivnosti", - "addon.competency.nocompetencies": "Nema kompetencija", - "addon.competency.noevidence": "Nema dokaza", - "addon.competency.path": "Putanja:", - "addon.competency.planstatusactive": "Aktivno", - "addon.competency.planstatuscomplete": "Dovršeno", - "addon.competency.planstatusdraft": "Nacrt", - "addon.competency.planstatusinreview": "Na pregledu", - "addon.competency.planstatuswaitingforreview": "Čeka na pregled", - "addon.competency.progress": "Napredak", - "addon.competency.status": "Stanje", - "addon.competency.template": "Predložak plana učenja", - "addon.competency.usercompetencystatus_inreview": "Na pregledu", - "addon.competency.usercompetencystatus_waitingforreview": "Čeka na pregled", - "addon.competency.userplans": "Planovi učenja", - "addon.coursecompletion.complete": "Dovršeno", - "addon.coursecompletion.completecourse": "Dovrši e-kolegij", - "addon.coursecompletion.completed": "Dovršeno", - "addon.coursecompletion.completiondate": "Datum završetka", - "addon.coursecompletion.completionmenuitem": "Dovršenost", - "addon.coursecompletion.coursecompletion": "Dovršenost e-kolegija", - "addon.coursecompletion.criteria": "Kriterij", - "addon.coursecompletion.criteriagroup": "Grupa kriterija", - "addon.coursecompletion.criteriarequiredall": "Potrebno je zadovoljenje svih doljnjih kriterija", - "addon.coursecompletion.criteriarequiredany": "Potrebno je zadovoljenje bilo kojeg doljnjeg kriterija", - "addon.coursecompletion.inprogress": "U tijeku", - "addon.coursecompletion.manualselfcompletion": "Ručni dovršetak", - "addon.coursecompletion.notyetstarted": "Nije još započelo", - "addon.coursecompletion.pending": "Na čekanju", - "addon.coursecompletion.required": "Obvezatno", - "addon.coursecompletion.requiredcriteria": "Obvezatni kriterij", - "addon.coursecompletion.requirement": "Zahtjev", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Prikaz izvješća e-kolegija", - "addon.files.couldnotloadfiles": "Popis datoteka se ne može učitati.", - "addon.files.emptyfilelist": "Nema datoteka za prikaz.", - "addon.files.erroruploadnotworking": "Trenutačno prijenos datoteka na sjedište nije moguć.", - "addon.files.files": "Datoteke", - "addon.files.privatefiles": "Osobne datoteke korisnika", - "addon.files.sitefiles": "Site files", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Postavljanje uređaja", - "addon.messages.acceptandaddcontact": "Prihvati i dodaj u kontakte", - "addon.messages.addcontact": "Dodaj kontakt", - "addon.messages.addcontactconfirm": "Jest eli sigurni da želite dodati {{$a}} u kontakte?", - "addon.messages.addtofavourites": "Zvjezdica", - "addon.messages.addtoyourcontacts": "Dodaj u kontakte", - "addon.messages.blocknoncontacts": "Blokiraj nepoznate korisnike", - "addon.messages.blockuser": "Blokiraj korisnika", - "addon.messages.blockuserconfirm": "Jeste li sigurni da želite blokirati {{$a}}?", - "addon.messages.contactableprivacy": "Prihvati poruke od:", - "addon.messages.contactableprivacy_coursemember": "Moji kontakti i svi na mojim e-kolegijima", - "addon.messages.contactableprivacy_onlycontacts": "Samo moji kontakti", - "addon.messages.contactableprivacy_site": "Svi na sjedištu", - "addon.messages.contactblocked": "Kontakt blokiran", - "addon.messages.contactlistempty": "Lista kontakata je prazna", - "addon.messages.contactname": "Ime kontakta", - "addon.messages.contactrequestsent": "Zahtjev za kontaktom poslan", - "addon.messages.contacts": "Kontakti", - "addon.messages.decline": "Odbij", - "addon.messages.deleteallconfirm": "Jeste li sigurni da želite izbrisati cijeli ovaj razgovor? To neće izbrisati razgovor za druge sudionike razgovora.", - "addon.messages.deleteconversation": "Izbriši razgovor", - "addon.messages.deleteforeveryone": "Izbriši za mene i za sve ostale", - "addon.messages.deletemessage": "Izbriši poruku", - "addon.messages.errordeletemessage": "Dogodila se greška pri brisanju poruke.", - "addon.messages.errorwhileretrievingcontacts": "Dogodila se greška pri preuzimanju kontaktata s poslužitelja.", - "addon.messages.errorwhileretrievingdiscussions": "Dogodila se greška pri preuzimanju rasprave s poslužitelja.", - "addon.messages.errorwhileretrievingmessages": "Dogodila se greška pri preuzimanju poruka s poslužitelja.", - "addon.messages.errorwhileretrievingusers": "Dogodila se greška pri preuzimanju studenata s poslužitelja.", - "addon.messages.groupconversations": "Grupa", - "addon.messages.groupinfo": "Informacija o grupi", - "addon.messages.individualconversations": "Privatno", - "addon.messages.info": "Info", - "addon.messages.isnotinyourcontacts": "{{$a}} nije u vašim kontaktima", - "addon.messages.message": "Poruka", - "addon.messages.messagenotsent": "Poruka nije poslana. Pokušajte opet kasnije.", - "addon.messages.messagepreferences": "Postavke za poruke", - "addon.messages.messages": "Poruke", - "addon.messages.muteconversation": "Utišaj", - "addon.messages.mutedconversation": "Utišani razgovori", - "addon.messages.newmessage": "Nova poruka", - "addon.messages.newmessages": "Nove poruke", - "addon.messages.nocontactrequests": "Nema zahtjeva za kontakt", - "addon.messages.nocontactsgetstarted": "Nema kontakta", - "addon.messages.nofavourites": "Nema razgovora označenih zvjezdicom", - "addon.messages.nogroupconversations": "Nema grupnih razgovora", - "addon.messages.noindividualconversations": "Nema privatnih razgovora", - "addon.messages.nomessagesfound": "Nema poruka za prikazati", - "addon.messages.noncontacts": "Nisu kontakti", - "addon.messages.nousersfound": "Nema pronađenih studenata", - "addon.messages.numparticipants": "{{$a}} sudionik(a)", - "addon.messages.removecontact": "Ukloni kontakt", - "addon.messages.removecontactconfirm": "Jeste li sigurni da želite ukloniti {{$a}} iz svojih kontakta?", - "addon.messages.removefromfavourites": "Ukloni zvjezdicu", - "addon.messages.removefromyourcontacts": "Ukloni iz kontakata", - "addon.messages.requests": "Zahtjevi", - "addon.messages.requirecontacttomessage": "Ako želite slati poruke {{$a}}, trebate prvo poslati zahtjev za dodavanje u kontakte.", - "addon.messages.searchcombined": "Pretraži korisnike i poruke", - "addon.messages.selfconversation": "Osobni prostor", - "addon.messages.sendcontactrequest": "Pošalji zahtjev za kontaktom", - "addon.messages.showdeletemessages": "Prikaži obrisane poruke", - "addon.messages.type_blocked": "Blokirano", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Rezultati pretraživanja", - "addon.messages.type_strangers": "Ostali", - "addon.messages.unabletomessage": "Ne možete slati poruke ovom korisniku", - "addon.messages.unblockuser": "Odblokiraj korisnika", - "addon.messages.unblockuserconfirm": "Jeste li sigurni da želite odblokirati {{$a}}?", - "addon.messages.unmuteconversation": "Isključi opciju utišavanja", - "addon.messages.useentertosend": "Koristi tipku ENTER za slanje", - "addon.messages.userwouldliketocontactyou": "{{$a}} vas želi kontaktirati", - "addon.messages.wouldliketocontactyou": "Želi vas kontaktirati", - "addon.messages.you": "Vi:", - "addon.messages.youhaveblockeduser": "Prethodno ste blokirali ovog korisnika.", - "addon.messages.yourcontactrequestpending": "Vaš zahtjev za kontakt s {{$a}} je na čekanju", - "addon.mod_assign.addattempt": "Predajte drugu zadaću", - "addon.mod_assign.addnewattempt": "Predajte novu zadaću", - "addon.mod_assign.addnewattemptfromprevious": "Dopusti novi pokušaj zasnovan na prethodno predanoj zadaći", - "addon.mod_assign.addsubmission": "Predajte zadaću", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Detaljnije informacije o zadaći i obrazac za predaju zadaće bit će dostupni od {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Dopusti predavanje zadaće od", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Ova zadaća dopušta predavanje vaših radova od {{$a}}", - "addon.mod_assign.applytoteam": "Primijeni ocjene i povratnu informaciju na cijelu grupu", - "addon.mod_assign.assignmentisdue": "Rok za predaju zadaće", - "addon.mod_assign.attemptnumber": "Broj predanih zadaća", - "addon.mod_assign.attemptreopenmethod": "Ponovno otvoreni pokušaji", - "addon.mod_assign.attemptreopenmethod_manual": "Ručno", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatski do prolazne", - "addon.mod_assign.attemptsettings": "Postavke predavanja zadaće", - "addon.mod_assign.confirmsubmission": "Želite li uistinu predati vašu zadaću na ocjenjivanje? Više nećete moći raditi ikakve izmjene nad zadaćom.", - "addon.mod_assign.currentattempt": "Ovo je predana zadaća broj {{$a}}.", - "addon.mod_assign.currentattemptof": "Ovo je predana zadaća broj {{$a.attemptnumber}} (od dopuštenih {{$a.maxattempts}}).", - "addon.mod_assign.currentgrade": "Trenutačno pohranjena ocjena u popisu ocjena", - "addon.mod_assign.cutoffdate": "Krajnji rok predaje", - "addon.mod_assign.defaultteam": "Zadana grupa", - "addon.mod_assign.duedate": "Rok predaje", - "addon.mod_assign.duedateno": "Bez roka predaje", - "addon.mod_assign.duedatereached": "Rok za predaju ove zadaće je istekao", - "addon.mod_assign.editingstatus": "Status uređivanja", - "addon.mod_assign.editsubmission": "Uredi moju zadaću", - "addon.mod_assign.extensionduedate": "Produljeni krajnji rok", - "addon.mod_assign.grade": "Ocjena", - "addon.mod_assign.graded": "Ocijenjeno", - "addon.mod_assign.gradedby": "Ocijenjeno od", - "addon.mod_assign.gradedfollowupsubmit": "Ocijenjeneno - primljena je nanovo predana zadaća", - "addon.mod_assign.gradedon": "Datum ocjene", - "addon.mod_assign.gradelocked": "Ocjena je zaključena ili izmjenjena u popisu ocjena.", - "addon.mod_assign.gradenotsynced": "Ocjena nije sinkronizirana", - "addon.mod_assign.gradeoutof": "Ocjena od ukupno {{$a}}", - "addon.mod_assign.gradingstatus": "Ocjena", - "addon.mod_assign.groupsubmissionsettings": "Postavke za grupnu predaju zadaće", - "addon.mod_assign.hiddenuser": "Sudionik", - "addon.mod_assign.latesubmissions": "Kasnija predaja zadaće", - "addon.mod_assign.latesubmissionsaccepted": "Dozvoljeno do {{$a}}", - "addon.mod_assign.markingworkflowstate": "Stanje procedure za ocjenjivanje", - "addon.mod_assign.markingworkflowstateinmarking": "U postupku ocjenjivanja", - "addon.mod_assign.markingworkflowstateinreview": "U postupku pregledavanja", - "addon.mod_assign.markingworkflowstatenotmarked": "Nije ocijenjeno", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Spremno za objavu", - "addon.mod_assign.markingworkflowstatereadyforreview": "Ocjenjivanje dovršeno", - "addon.mod_assign.markingworkflowstatereleased": "Objavljeno", - "addon.mod_assign.modulenameplural": "Zadaće", - "addon.mod_assign.multipleteams": "Član više od jedne grupe", - "addon.mod_assign.multipleteams_desc": "Ova zadaća zahtjeva predaju u grupama. Član ste više od jedne grupe. Za predaju morate biti član samo jedne grupe. Javite se svom nastavniku kako biste promijenili svoju pripadnost grupama.", - "addon.mod_assign.noattempt": "Zadaća nije predana", - "addon.mod_assign.nomoresubmissionsaccepted": "Dozvoljeno samo polaznicima s odobrenim produženjem", - "addon.mod_assign.noonlinesubmissions": "Za ovu zadaću ne morate ništa predavati online", - "addon.mod_assign.nosubmission": "Nije predana nijedna zadaća", - "addon.mod_assign.noteam": "Nije član niti jedne grupe", - "addon.mod_assign.noteam_desc": "Zadaća zahtijeva predaju u grupama. Niste član niti jedne grupe, pa ne možete predati zadaću. Javite se svom nastavniku za dodavanje u grupu.", - "addon.mod_assign.notgraded": "Nije ocijenjeno", - "addon.mod_assign.numberofdraftsubmissions": "Nacrti", - "addon.mod_assign.numberofparticipants": "Sudionici", - "addon.mod_assign.numberofsubmissionsneedgrading": "Potrebno ocijeniti", - "addon.mod_assign.numberofsubmittedassignments": "Predano", - "addon.mod_assign.numberofteams": "Grupe", - "addon.mod_assign.numwords": "{{$a}} riječi", - "addon.mod_assign.outof": "{{$a.current}} od {{$a.total}}", - "addon.mod_assign.overdue": "Predaja zadaće kasni za: {{$a}}", - "addon.mod_assign.submission": "Predaja", - "addon.mod_assign.submissioneditable": "Studenti mogu nadograđivati zadaću", - "addon.mod_assign.submissionnoteditable": "Studenti ne mogu nadograđivati zadaću", - "addon.mod_assign.submissionslocked": "Ova zadaća ne dopušta predavanje rada", - "addon.mod_assign.submissionstatus": "Stanje predane zadaće", - "addon.mod_assign.submissionstatus_": "Nema predanih zadaća", - "addon.mod_assign.submissionstatus_draft": "Nacrt (nije predano)", - "addon.mod_assign.submissionstatus_marked": "Ocijenjeno", - "addon.mod_assign.submissionstatus_new": "Nema predanih zadaća", - "addon.mod_assign.submissionstatus_reopened": "Ponovno otvoreno", - "addon.mod_assign.submissionstatus_submitted": "Predano na ocjenjivanje", - "addon.mod_assign.submissionstatusheading": "Stanje predane zadaće", - "addon.mod_assign.submissionteam": "Grupa", - "addon.mod_assign.submitassignment": "Predaj zadaću", - "addon.mod_assign.submitassignment_help": "Jednom kada predate zadaću, daljnje izmjene nisu moguće", - "addon.mod_assign.submittedearly": "Ranija predaja zadaće: {{$a}}", - "addon.mod_assign.submittedlate": "Zakašnjela predaja zadaće: {{$a}}", - "addon.mod_assign.timemodified": "Posljednja izmjena", - "addon.mod_assign.timeremaining": "Preostalo vrijeme", - "addon.mod_assign.ungroupedusers": "Postavka 'Zahtijevaj grupnu predaju zadaće' je odabrana; neki korisnici nisu članovi niti jedne grupe, ili su članovi više od jedne grupe, stoga ne mogu predati zadaću.", - "addon.mod_assign.ungroupedusersoptional": "Postavka 'Studenti predaju u grupama' je odabrana; neki korisnici nisu članovi niti jedne grupe, ili su članovi više od jedne grupe. Takvi će studenti zadaću predati kao članovi 'Zadane grupe'.", - "addon.mod_assign.unlimitedattempts": "Neograničeno", - "addon.mod_assign.userswhoneedtosubmit": "Korisnici koji moraju predati zadaću: {{$a}}", - "addon.mod_assign.userwithid": "Korisnički ID {{id}}", - "addon.mod_assign.viewsubmission": "Prikaz predane zadaće", - "addon.mod_assign.wordlimit": "Ograničenje broja riječi", - "addon.mod_assign_feedback_comments.pluginname": "Povratna informacija", - "addon.mod_assign_feedback_editpdf.pluginname": "Unos bilješki u PDF", - "addon.mod_assign_feedback_file.pluginname": "Datoteka s povratnom informacijom", - "addon.mod_assign_submission_comments.pluginname": "Napomene uz zadaću", - "addon.mod_assign_submission_file.pluginname": "Postavljanje datoteke", - "addon.mod_assign_submission_onlinetext.pluginname": "Predaja online tekstova", - "addon.mod_book.errorchapter": "Pogreška pri učitavanju poglavlja u knjizi", - "addon.mod_book.modulenameplural": "Knjige", - "addon.mod_book.navnexttitle": "Dalje: {{$a}}", - "addon.mod_book.navprevtitle": "Natrag: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Poglavlja knjige", - "addon.mod_book.toc": "Sadržaj", - "addon.mod_chat.beep": "Bocni", - "addon.mod_chat.chatreport": "Chat sesije", - "addon.mod_chat.currentusers": "Trenutačno prijavljeni korisnici", - "addon.mod_chat.enterchat": "Kliknite ovdje za pristup chatu", - "addon.mod_chat.entermessage": "Upišite svoju poruku", - "addon.mod_chat.errorwhileretrievingmessages": "Pogreška pri preuzimanju poruka s poslužitelja.", - "addon.mod_chat.errorwhilesendingmessage": "Pogreška pri slanju poruke.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} je bocnuo sve!", - "addon.mod_chat.messagebeepsyou": "{{$a}} vas je upravo bocnuo!", - "addon.mod_chat.messageenter": "Korisnik {{$a}} je došao na chat", - "addon.mod_chat.messageexit": "Korisnik {{$a}} je otišao s chata", - "addon.mod_chat.messages": "Poruke", - "addon.mod_chat.messageyoubeep": "Bocnuli ste {{$a}}", - "addon.mod_chat.modulenameplural": "Razgovori", - "addon.mod_chat.nomessages": "Nema poruka (još)", - "addon.mod_chat.saidto": "napisano sudioniku", - "addon.mod_chat.send": "Pošalji", - "addon.mod_chat.sessionstart": "Sljedeća chat sesija bit će pokrenuta u {{$a.date}}, ({{$a.fromnow}} od ovog trenutka)", - "addon.mod_chat.talk": "Pričaj", - "addon.mod_chat.viewreport": "Prikaži prošle chat sesije", - "addon.mod_choice.cannotsubmit": "Dogodila se pogreška pri slanju vašeg izbora, pokušajte ponovno.", - "addon.mod_choice.choiceoptions": "Postavke odabira", - "addon.mod_choice.expired": "Ova aktivnost je zatvorena od {{$a}}", - "addon.mod_choice.full": "(Popunjeno)", - "addon.mod_choice.modulenameplural": "Odabiri", - "addon.mod_choice.noresultsviewable": "Rezultate trenutačno nije moguće prikazati.", - "addon.mod_choice.notopenyet": "Nažalost, ova aktivnost nije dostupna do {{$a}}.", - "addon.mod_choice.numberofuser": "Broj odgovora", - "addon.mod_choice.numberofuserinpercentage": "Postotak odgovora", - "addon.mod_choice.publishinfoanonafter": "Anonimni rezultati bit će objavljeni nakon što odgovorite.", - "addon.mod_choice.publishinfoanonclose": "Anonimni rezultati bit će objavljeni nakon što se aktivnost zatvori.", - "addon.mod_choice.publishinfofullafter": "Cjelokupni rezultati koji pokazuju svačiji izbor bit će objavljeni nakon što na njih odgovorite.", - "addon.mod_choice.publishinfofullclose": "Potpuni rezultati koji pokazuju svačiji izbor bit će objavljeni nakon zatvaranja aktivnosti.", - "addon.mod_choice.publishinfonever": "Rezultati ove aktivnosti neće biti objavljeni nakon što odgovorite.", - "addon.mod_choice.removemychoice": "Ukloni moj odabir", - "addon.mod_choice.responses": "Odabiri", - "addon.mod_choice.responsesresultgraphheader": "Grafički prikaz", - "addon.mod_choice.savemychoice": "Pohrani moj odabir", - "addon.mod_choice.userchoosethisoption": "Korisnik je odabrao ovu opciju", - "addon.mod_choice.yourselection": "Vaš odabir", - "addon.mod_data.addentries": "Dodaj zapise", - "addon.mod_data.advancedsearch": "Napredno pretraživanje", - "addon.mod_data.alttext": "Alternativni tekst", - "addon.mod_data.approve": "Dopusti", - "addon.mod_data.approved": "Dopušteno", - "addon.mod_data.ascending": "Uzlazno", - "addon.mod_data.authorfirstname": "Ime autora", - "addon.mod_data.authorlastname": "Prezime autora", - "addon.mod_data.confirmdeleterecord": "Želite li izbrisati ovaj zapis?", - "addon.mod_data.descending": "Silazno", - "addon.mod_data.disapprove": "Ukloni odobrenje", - "addon.mod_data.emptyaddform": "Niste ispunili nijedno polje!", - "addon.mod_data.entrieslefttoadd": "Morate dodati barem {{$a.entriesleft}} zapisa kako biste završili ovu aktivnost", - "addon.mod_data.entrieslefttoaddtoview": "Morate dodati barem {{$a.entriesleft}} zapisa kako biste mogli pregledavati zapise drugih sudionika.", - "addon.mod_data.errormustsupplyvalue": "Ovdje morate navesti vrijednost.", - "addon.mod_data.expired": "Nažalost, ova aktivnost je zatvorena {{$a}} i nije više dostupna", - "addon.mod_data.fields": "Polja", - "addon.mod_data.foundrecords": "Pronađeni zapisi: {{$a.num}}/{{$a.max}}(Vrati stare postavke filtra)", - "addon.mod_data.latlongboth": "Obavezna je i širina i dužina.", - "addon.mod_data.menuchoose": "Odaberite...", - "addon.mod_data.modulenameplural": "Baze podataka", - "addon.mod_data.more": "Više", - "addon.mod_data.nomatch": "Nema odgovarajućih zapisa!", - "addon.mod_data.norecords": "U bazi podataka nema zapisa", - "addon.mod_data.notapproved": "Zapis još nije potvrđen.", - "addon.mod_data.notopenyet": "Nažalost, ova aktivnost nije dostupna do {{$a}}", - "addon.mod_data.numrecords": "{{$a}} zapisa", - "addon.mod_data.other": "Drugo", - "addon.mod_data.recordapproved": "Zapis je odobren", - "addon.mod_data.recorddeleted": "Zapis izbrisan", - "addon.mod_data.recorddisapproved": "Unos nije odobren", - "addon.mod_data.resetsettings": "Ukloni filtre", - "addon.mod_data.search": "Pretraživanje", - "addon.mod_data.selectedrequired": "Sve odabrano je označeno", - "addon.mod_data.single": "Prikaži pojedinačno", - "addon.mod_data.tagarea_data_records": "Zapisi podataka", - "addon.mod_data.timeadded": "Vrijeme dodavanja", - "addon.mod_data.timemodified": "Vrijeme izmjene", - "addon.mod_data.usedate": "Obuhvati u pretraživanju.", - "addon.mod_feedback.analysis": "Analiza", - "addon.mod_feedback.anonymous": "Anonimno", - "addon.mod_feedback.anonymous_entries": "Anonimni odgovori ({{$a}})", - "addon.mod_feedback.average": "Prosječno", - "addon.mod_feedback.complete_the_form": "Odgovorite na pitanja", - "addon.mod_feedback.completed_feedbacks": "Predani odgovori", - "addon.mod_feedback.continue_the_form": "Nastavi s popunjavanjem obrasca", - "addon.mod_feedback.feedback_is_not_open": "Anketa nije dostupna", - "addon.mod_feedback.feedbackclose": "Dozvoli odgovore na", - "addon.mod_feedback.feedbackopen": "Dozvoli odgovore od", - "addon.mod_feedback.mapcourses": "Pridruži anketu e-kolegijima", - "addon.mod_feedback.maximal": "Najviše", - "addon.mod_feedback.minimal": "Najmanje", - "addon.mod_feedback.mode": "Vrsta", - "addon.mod_feedback.modulenameplural": "Ankete", - "addon.mod_feedback.next_page": "Sljedeća stranica", - "addon.mod_feedback.non_anonymous": "Ime korisnika će se zapisati i prikazati uz odgovore", - "addon.mod_feedback.non_anonymous_entries": "Neanonimni unosi ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Studenti koji nisu odgovorili ({{$a}})", - "addon.mod_feedback.not_selected": "Nije odabrano", - "addon.mod_feedback.not_started": "Nije počelo", - "addon.mod_feedback.numberoutofrange": "Broj izvan raspona", - "addon.mod_feedback.overview": "Pregled", - "addon.mod_feedback.page_after_submit": "Stranica poslije predaje", - "addon.mod_feedback.preview": "Pregled", - "addon.mod_feedback.previous_page": "Prethodna stranica", - "addon.mod_feedback.questions": "Pitanja", - "addon.mod_feedback.response_nr": "Odgovor broj", - "addon.mod_feedback.responses": "Odgovori", - "addon.mod_feedback.save_entries": "Predaj svoje odgovore", - "addon.mod_feedback.show_entries": "Prikaži odgovore", - "addon.mod_feedback.show_nonrespondents": "Prikaži korisnike koji nisu odgovorili", - "addon.mod_feedback.started": "Započeto", - "addon.mod_feedback.this_feedback_is_already_submitted": "Već ste obavili ovu aktivnost.", - "addon.mod_folder.emptyfilelist": "Nema datoteka za prikaz.", - "addon.mod_folder.modulenameplural": "Mape", - "addon.mod_forum.addanewdiscussion": "Dodaj novu raspravu", - "addon.mod_forum.addanewquestion": "Dodajte novo pitanje", - "addon.mod_forum.addanewtopic": "Dodajte novu temu", - "addon.mod_forum.addtofavourites": "Označi ovu raspravu", - "addon.mod_forum.advanced": "Napredno", - "addon.mod_forum.cannotadddiscussion": "Za dodavanje rasprave u ovaj forum treba biti član grupe.", - "addon.mod_forum.cannotadddiscussionall": "Nemate ovlasti da biste dodali novu raspravu za sve sudionike. ", - "addon.mod_forum.cannotcreatediscussion": "Nije moguće otvoriti novu raspravu", - "addon.mod_forum.couldnotadd": "Nažalost, nije moguće dodati vašu poruku zbog nepoznate pogreške", - "addon.mod_forum.couldnotupdate": "Sustav nije mogao promijeniti sadržaj vaše poruke zbog nepoznate pogreške", - "addon.mod_forum.cutoffdatereached": "Krajnji rok predaje za ovaj forum je istekao, pa forum ne prihvaća nove poruke.", - "addon.mod_forum.delete": "Izbriši", - "addon.mod_forum.deletedpost": "Poruka je obrisana", - "addon.mod_forum.deletesure": "Želite li izbrisati ovu poruku?", - "addon.mod_forum.discussion": "Rasprava", - "addon.mod_forum.discussionlistsortbycreatedasc": "Poredaj po datumu stvaranja uzlaznim redoslijedom", - "addon.mod_forum.discussionlistsortbycreateddesc": "Poredaj po datumu stvaranja silaznim redoslijedom", - "addon.mod_forum.discussionlistsortbylastpostasc": "Poredaj po datumu posljednje objave uzlaznim redoslijedom", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Poredaj po datumu posljednje objave silaznim redoslijedom", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Poredaj po broju odgovora u uzlaznom redoslijedu", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Poredaj po broju odgovora u silaznom redoslijedu", - "addon.mod_forum.discussionlocked": "Ova je rasprava zaključana, pa više nije moguće odgovoriti na nju.", - "addon.mod_forum.discussionpinned": "Prikvačena na vrh", - "addon.mod_forum.discussionsubscription": "Pretplata na raspravu", - "addon.mod_forum.edit": "Promijeni", - "addon.mod_forum.erroremptymessage": "Tijelo poruke ne može biti prazno", - "addon.mod_forum.erroremptysubject": "Naslov poruke ne može biti prazan.", - "addon.mod_forum.favouriteupdated": "Opcija označavanja je ažurirana.", - "addon.mod_forum.group": "Grupa", - "addon.mod_forum.lastpost": "Zadnja poruka", - "addon.mod_forum.lockdiscussion": "Zaključaj raspravu", - "addon.mod_forum.lockupdated": "Postavke za zaključavanje su ažurirane.", - "addon.mod_forum.message": "Poruka", - "addon.mod_forum.modeflatnewestfirst": "Prikaz odgovora, počevši s najnovijim", - "addon.mod_forum.modeflatoldestfirst": "Prikaz odgovora, počevši s najstarijim", - "addon.mod_forum.modenested": "Prikaz odgovora u hijerarhijskoj strukturi", - "addon.mod_forum.modulenameplural": "Forumi", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} rasprava", - "addon.mod_forum.numreplies": "{{numreplies}} odgovora", - "addon.mod_forum.pindiscussion": "Označi raspravu", - "addon.mod_forum.pinupdated": "Postavke za pin su ažurirane.", - "addon.mod_forum.postisprivatereply": "Ovo je privatan odgovor. Nije vidljiv drugim sudionicima.", - "addon.mod_forum.posttoforum": "Pošaljite poruku na forum", - "addon.mod_forum.posttomygroups": "Pošaljite kopiju svim grupama", - "addon.mod_forum.privatereply": "Odgovori privatno", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.removefromfavourites": "Ukloni zvjezdicu s ove rasprave", - "addon.mod_forum.reply": "Odgovori (reply)", - "addon.mod_forum.replyplaceholder": "Napiši odgovor...", - "addon.mod_forum.subject": "Naslov", - "addon.mod_forum.tagarea_forum_posts": "Objave na forumu", - "addon.mod_forum.thisforumhasduedate": "Rok za objavu na ovom forumu je do {{$a}}.", - "addon.mod_forum.thisforumisdue": "Rok za objavu na ovom forumu bio je do {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Otključaj raspravu", - "addon.mod_forum.unpindiscussion": "Otkvači raspravu s vrha", - "addon.mod_forum.unread": "Nepročitano", - "addon.mod_forum.unreadpostsnumber": "Broj nepročitanih poruka: {{$a}}", - "addon.mod_forum.yourreply": "Vaš odgovor", - "addon.mod_glossary.addentry": "Dodajte novi pojam", - "addon.mod_glossary.aliases": "Ključne riječi", - "addon.mod_glossary.attachment": "Privitak", - "addon.mod_glossary.byalphabet": "Abecedno", - "addon.mod_glossary.byauthor": "Grupirano po autoru", - "addon.mod_glossary.bycategory": "Grupirano po kategoriji", - "addon.mod_glossary.bynewestfirst": "Prvo najnoviji", - "addon.mod_glossary.byrecentlyupdated": "Nedavno osvježeno", - "addon.mod_glossary.bysearch": "Pretraživanje", - "addon.mod_glossary.casesensitive": "Potrebno je paziti na velika i mala slova", - "addon.mod_glossary.categories": "Kategorije", - "addon.mod_glossary.concept": "Koncept", - "addon.mod_glossary.definition": "Definicija", - "addon.mod_glossary.entryusedynalink": "Ovaj pojam treba biti automatski povezan", - "addon.mod_glossary.errconceptalreadyexists": "Ovaj pojam već postoji. Dvostruki pojmovi nisu dopušteni u ovom rječniku.", - "addon.mod_glossary.fillfields": "Pojam i definicija su obvezatna polja.", - "addon.mod_glossary.fullmatch": "Poveži samo cijele riječi", - "addon.mod_glossary.linking": "Automatsko povezivanje", - "addon.mod_glossary.modulenameplural": "Rječnici", - "addon.mod_glossary.tagarea_glossary_entries": "Pojmovi u rječniku", - "addon.mod_imscp.deploymenterror": "Pogreška pri pokretanju paketa!", - "addon.mod_imscp.modulenameplural": "IMS paketi", - "addon.mod_imscp.showmoduledescription": "Prikaži opis", - "addon.mod_imscp.toc": "Sadržaj (TOC)", - "addon.mod_lesson.answer": "Odgovor", - "addon.mod_lesson.attempt": "Pokušaj: {{$a}}", - "addon.mod_lesson.attemptheader": "Pokušaj", - "addon.mod_lesson.attemptsremaining": "Broj preostalih pokušaja: {{$a}}", - "addon.mod_lesson.averagescore": "Prosječan broj bodova", - "addon.mod_lesson.averagetime": "Prosječno vrijeme", - "addon.mod_lesson.branchtable": "Grana", - "addon.mod_lesson.cannotfindattempt": "Pogreška: pokušaj nije pronađen", - "addon.mod_lesson.cannotfinduser": "Pogreška: korisnici nisu pronađeni", - "addon.mod_lesson.clusterjump": "Neprikazano pitanje iz grupe pitanja", - "addon.mod_lesson.completed": "Završeno", - "addon.mod_lesson.congratulations": "Čestitamo - stigli ste do kraja lekcije", - "addon.mod_lesson.continue": "Nastavak", - "addon.mod_lesson.continuetonextpage": "Nastavite na sljedeću stranicu", - "addon.mod_lesson.defaultessayresponse": "Nastavnik će pregledati vaš esej.", - "addon.mod_lesson.detailedstats": "Detaljna statistika", - "addon.mod_lesson.didnotanswerquestion": "Niste odgovorili na ovo pitanje.", - "addon.mod_lesson.displayofgrade": "Prikaz ocjena (samo za studente)", - "addon.mod_lesson.displayscorewithessays": "Osvojili ste {{$a.score}} od najviše {{$a.tempmaxgrade}} bodova za pitanja koja se automatski ocjenjuju.
                Broj vaših odgovora na pitanja tipa esej koji će biti naknadno ocjenjeni i pridodani ukupnoj ocjeni: {{$a.essayquestions}}.

                Vaša trenutačna ocjena (bez eseja) je {{$a.score}} od {{$a.grade}}", - "addon.mod_lesson.displayscorewithoutessays": "Vaš rezultat je {{$a.score}} (od mogućih {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Lozinka ne može biti prazna (bez ijednog znaka)", - "addon.mod_lesson.enterpassword": "Molim unesite lozinku:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Niste odgovorili niti na jedno pitanje. Vaša ocjena za ovu lekciju je 0.", - "addon.mod_lesson.finish": "Završi", - "addon.mod_lesson.firstwrong": "Nažalost, ne možete osvojiti ovaj bod jer je Vaš odgovor netočan. Želite li pokušati ponovo kako biste učili (ali bez mogućnosti osvojanja boda)?", - "addon.mod_lesson.gotoendoflesson": "Idi na kraj lekcije", - "addon.mod_lesson.grade": "Ocjena", - "addon.mod_lesson.highscore": "Najbolji rezultat", - "addon.mod_lesson.hightime": "Najbolje vrijeme", - "addon.mod_lesson.leftduringtimed": "Napustili ste lekciju koja je vremenski ograničena.
                Pritisnite gumb \"Nastavi\" kako biste pogledali lekciju od početka.", - "addon.mod_lesson.leftduringtimednoretake": "Napustili ste lekciju koja je vremenski ograničena i nije Vam
                dopušteno da nastavite ili počnete lekciju iz početka.", - "addon.mod_lesson.lessonmenu": "Izbornik lekcije", - "addon.mod_lesson.lessonstats": "Statistika lekcije", - "addon.mod_lesson.linkedmedia": "Povezana multimedija", - "addon.mod_lesson.loginfail": "Prijava neuspješna, pokušajte ponovno...", - "addon.mod_lesson.lowscore": "Naslabiji rezultat", - "addon.mod_lesson.lowtime": "Najslabije vrijeme", - "addon.mod_lesson.maximumnumberofattemptsreached": "Dosegnut je maksimalni broj pokušaja - prelazi se na sljedeću stranicu", - "addon.mod_lesson.modattemptsnoteacher": "Pregled studentima vrijedi samo za studente.", - "addon.mod_lesson.modulenameplural": "Lekcije", - "addon.mod_lesson.noanswer": "Nema predanih odgovora. Molimo vratite se natrag i odgovorite na pitanje.", - "addon.mod_lesson.nolessonattempts": "U lekciji trenutačno nema pokušaja.", - "addon.mod_lesson.nolessonattemptsgroup": "U lekciji trenutačno nema pokušaja od člana grupe {{$a}}.", - "addon.mod_lesson.notcompleted": "Nije završeno", - "addon.mod_lesson.numberofcorrectanswers": "Broj točnih odgovora: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Broj odgovorenih pitanja: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Broj odgovorenih pitanja: {{$a.nquestions}}; (Trebali bi odgovoriti na barem: {{$a.minquestions}} pitanja)", - "addon.mod_lesson.ongoingcustom": "Do sada ste osvojili {{$a.score}} od maksimalno {{$a.currenthigh}} bodova.", - "addon.mod_lesson.ongoingnormal": "Točno odgovorenih pitanja: {{$a.correct}} od {{$a.viewed}} pokušaja.", - "addon.mod_lesson.or": "ILI", - "addon.mod_lesson.overview": "Pregled", - "addon.mod_lesson.preview": "Pregled", - "addon.mod_lesson.progressbarteacherwarning2": "Traka s prikazom napredovanja kroz lekciju Vam neće biti prikazana jer imate mogućnost uređivanja lekcije", - "addon.mod_lesson.progresscompleted": "Dovršili ste {{$a}}% ove lekcije", - "addon.mod_lesson.question": "Pitanje", - "addon.mod_lesson.rawgrade": "Neobrađena ocjena", - "addon.mod_lesson.reports": "Izvještaji", - "addon.mod_lesson.response": "Povratna informacija", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Pregled", - "addon.mod_lesson.reviewlesson": "Pregled lekcije", - "addon.mod_lesson.reviewquestionback": "Da, želim pokušati ponovo", - "addon.mod_lesson.reviewquestioncontinue": "Ne, želim preći na sljedeće pitanje", - "addon.mod_lesson.secondpluswrong": "Nije u potpunosti točno. Želite li pokušati ponovno?", - "addon.mod_lesson.submit": "Predaj", - "addon.mod_lesson.teacherjumpwarning": "U ovoj lekciji se koristi {{$a.cluster}} ili {{$a.unseen}} prijelaz između stranica. Prijelaz \"Sljedeća stranica\" će se koristiti u prikazu. Prijavite se u ulozi studenta ako želite testirati prijelaze.", - "addon.mod_lesson.teacherongoingwarning": "Prikaz trenutačnog rezultata tijekom pregleda lekcije je omogućen samo studentima. Prijavite se u ulozi studenta da biste isprobali ovu mogućnost.", - "addon.mod_lesson.teachertimerwarning": "Mjerenje vremena je omogućeno samo studentima. Prijavite se u ulozi studenta da biste isprobali ovu mogućnost.", - "addon.mod_lesson.thatsthecorrectanswer": "Ispravan odgovor", - "addon.mod_lesson.thatsthewronganswer": "Netočan odgovor", - "addon.mod_lesson.timeremaining": "Preostalo vrijeme", - "addon.mod_lesson.timetaken": "Utrošeno vrijeme", - "addon.mod_lesson.unseenpageinbranch": "Još neprikazano pitanje unutar grane", - "addon.mod_lesson.welldone": "Bravo!", - "addon.mod_lesson.youhaveseen": "Već ste pregledali više od jedne stranice ove lekcije.
                Želite li početi od posljednje stranice koju ste pregledali?", - "addon.mod_lesson.youranswer": "Vaš odgovor", - "addon.mod_lesson.yourcurrentgradeisoutof": "Vaša trenutačna ocjena je {{$a.grade}} od mogućih {{$a.total}}", - "addon.mod_lesson.youshouldview": "Trebali bi odgovoriti na barem: {{$a}}", - "addon.mod_lti.launchactivity": "Pokreni aktivnost", - "addon.mod_lti.modulenameplural": "Vanjski alati", - "addon.mod_page.modulenameplural": "Stranice", - "addon.mod_quiz.answercolon": "Odgovor:", - "addon.mod_quiz.attemptfirst": "Prvi pokušaj", - "addon.mod_quiz.attemptlast": "Posljednji pokušaj", - "addon.mod_quiz.attemptnumber": "Pokušaj", - "addon.mod_quiz.attemptquiznow": "Započnite test", - "addon.mod_quiz.attemptstate": "Stanje", - "addon.mod_quiz.clearchoice": "Obriši moj odabir", - "addon.mod_quiz.comment": "Komentar", - "addon.mod_quiz.completedon": "Završeno", - "addon.mod_quiz.confirmclose": "Spremni ste završiti test. Kada završite test, više nećete biti u mogućnosti promijeniti svoje odgovore.", - "addon.mod_quiz.confirmstart": "Ovaj test ima vremensko ograničenje od {{$a}}. Vremensko odbrojavanje će započeti u trenutku kada krenete s rješavanjem testa. Vaš pokušaj rješavanja morate predati prije nego to vrijeme istekne. Želite li sigurno sada započeti s rješavanjem?", - "addon.mod_quiz.confirmstartheader": "Vremenski ograničeni test", - "addon.mod_quiz.connectionerror": "Povezanost s mrežom je prekinuta. (Automatsko spremanje nije uspjelo).\nNapravite bilješke svih odgovora koji su uneseni na ovu stranicu u posljednjih nekoliko minuta te se pokušajte ponovno povezati.\nKada je veza ponovno uspostavljena, vaši odgovori će biti spremljeni i ova poruka će nestati.", - "addon.mod_quiz.continueattemptquiz": "Nastavite na osnovu svog zadnjeg pokušaja", - "addon.mod_quiz.continuepreview": "Nastavite posljednji pregled.", - "addon.mod_quiz.feedback": "Povratna informacija (Feedback)", - "addon.mod_quiz.finishattemptdots": "Završi pokušaj...", - "addon.mod_quiz.grade": "Ocjena", - "addon.mod_quiz.gradeaverage": "Srednja ocjena", - "addon.mod_quiz.gradehighest": "Najviša ocjena", - "addon.mod_quiz.grademethod": "Način ocjenjivanja", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Bodovi", - "addon.mod_quiz.modulenameplural": "Testovi", - "addon.mod_quiz.mustbesubmittedby": "Ovaj je test potrebno predati do {{$a}}.", - "addon.mod_quiz.noquestions": "U ovom testu trenutačno nema pitanja", - "addon.mod_quiz.noreviewattempt": "Nemate mogućnost pregledati ovaj pokušaj.", - "addon.mod_quiz.notyetgraded": "Još nije ocijenjeno", - "addon.mod_quiz.outof": "{{$a.grade}} od maksimalno {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} od maksimalno {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Sveobuhvatna povratna informacija", - "addon.mod_quiz.overdue": "Kasne", - "addon.mod_quiz.overduemustbesubmittedby": "Ovaj test je već trebalo predati. Ako želite da pokušaj bude ocijenjen, morate ga predati do {{$a}}. Ako ga ne predate do tada, nećete biti ocijenjeni.", - "addon.mod_quiz.preview": "Pregled (preview)", - "addon.mod_quiz.previewquiznow": "Pogledajte test", - "addon.mod_quiz.question": "Pitanje", - "addon.mod_quiz.quiznavigation": "Navigacija u testu", - "addon.mod_quiz.quizpassword": "Lozinka testa", - "addon.mod_quiz.reattemptquiz": "Pokušaj ponovo riješiti ovaj test", - "addon.mod_quiz.requirepasswordmessage": "Kako biste pristupili ovom testu morate znati lozinku testa", - "addon.mod_quiz.returnattempt": "Povratak na pokušaj", - "addon.mod_quiz.review": "Pregled", - "addon.mod_quiz.reviewofattempt": "Pregled pokušaja {{$a}}", - "addon.mod_quiz.reviewofpreview": "Pregled prikaza", - "addon.mod_quiz.showall": "Prikaži sva pitanja na jednoj stranici", - "addon.mod_quiz.showeachpage": "Prikaži jednu po jednu stranicu", - "addon.mod_quiz.startattempt": "Započni rješavanje", - "addon.mod_quiz.startedon": "Započeto", - "addon.mod_quiz.stateabandoned": "Nikada nije predano", - "addon.mod_quiz.statefinished": "Završeno", - "addon.mod_quiz.statefinisheddetails": "Predano {{$a}}", - "addon.mod_quiz.stateinprogress": "U tijeku", - "addon.mod_quiz.stateoverdue": "Kašnjenje", - "addon.mod_quiz.stateoverduedetails": "Mora biti predano do {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Predaj sve i završi", - "addon.mod_quiz.summaryofattempt": "Sažetak pokušaja", - "addon.mod_quiz.summaryofattempts": "Sažetak prethodnih pokušaja", - "addon.mod_quiz.timeleft": "Preostalo vrijeme", - "addon.mod_quiz.timetaken": "Proteklo vrijeme", - "addon.mod_quiz.yourfinalgradeis": "Vaša završna ocjena na ovom testu je {{$a}}", - "addon.mod_resource.modifieddate": "Izmijenjeno {{$a}}", - "addon.mod_resource.modulenameplural": "Datoteke", - "addon.mod_resource.openthefile": "Otvori datoteku", - "addon.mod_resource.uploadeddate": "Učitano {{$a}}", - "addon.mod_scorm.asset": "Statički resurs", - "addon.mod_scorm.assetlaunched": "Pregled statičkih resursa", - "addon.mod_scorm.attempts": "Pokušaji", - "addon.mod_scorm.averageattempt": "Prosječni pokušaji", - "addon.mod_scorm.browse": "Pregled", - "addon.mod_scorm.browsed": "Pregledano", - "addon.mod_scorm.browsemode": "Način pregleda", - "addon.mod_scorm.completed": "Završeno", - "addon.mod_scorm.contents": "Sadržaj", - "addon.mod_scorm.enter": "Uđi", - "addon.mod_scorm.exceededmaxattempts": "Dosegli ste najveći dopušteni broj pokušaja", - "addon.mod_scorm.failed": "Neuspješno", - "addon.mod_scorm.firstattempt": "Prvi pokušaj", - "addon.mod_scorm.gradeaverage": "Prosječna ocjena", - "addon.mod_scorm.gradeforattempt": "Ocjena za pokušaj", - "addon.mod_scorm.gradehighest": "Najviša ocjena", - "addon.mod_scorm.grademethod": "Način ocjenjivanja", - "addon.mod_scorm.gradereported": "Ocjena koja je zabilježena", - "addon.mod_scorm.gradescoes": "Objekti učenja", - "addon.mod_scorm.gradesum": "Zbirna ocjena", - "addon.mod_scorm.highestattempt": "Najbolji pokušaj", - "addon.mod_scorm.incomplete": "Nedovršeno", - "addon.mod_scorm.lastattempt": "Posljednji dovršeni pokušaj", - "addon.mod_scorm.modulenameplural": "SCORM paketi", - "addon.mod_scorm.newattempt": "Započni novi pokušaj", - "addon.mod_scorm.noattemptsallowed": "Broj dopuštenih pokušaja", - "addon.mod_scorm.noattemptsmade": "Broj pokušaja koji ste vi učinili", - "addon.mod_scorm.notattempted": "Nije pokušano", - "addon.mod_scorm.organizations": "Organizacije", - "addon.mod_scorm.passed": "Prošao", - "addon.mod_scorm.reviewmode": "Način za pregled", - "addon.mod_scorm.score": "Rezultat", - "addon.mod_scorm.suspended": "Zaključano", - "addon.mod_scorm.toc": "Sadržaj", - "addon.mod_survey.ifoundthat": "Otkrio sam da", - "addon.mod_survey.ipreferthat": "Rado bih da", - "addon.mod_survey.modulenameplural": "Upitnici", - "addon.mod_survey.responses": "Odgovori", - "addon.mod_survey.results": "Rezultati", - "addon.mod_survey.surveycompletednograph": "Ispunili ste ovaj upitnik.", - "addon.mod_url.modulenameplural": "Poveznice", - "addon.mod_wiki.cannoteditpage": "Ne možete urediti ovu stranicu.", - "addon.mod_wiki.createpage": "Stvori stranicu", - "addon.mod_wiki.editingpage": "Uređivanje stranice '{{$a}}'", - "addon.mod_wiki.map": "Sadržaj", - "addon.mod_wiki.modulenameplural": "Wikiji", - "addon.mod_wiki.newpagehdr": "Nova stranica", - "addon.mod_wiki.newpagetitle": "Naslov nove stranice", - "addon.mod_wiki.nocontent": "Na ovoj stranici nema sadržaja", - "addon.mod_wiki.notingroup": "Nije u grupi", - "addon.mod_wiki.pageexists": "Ova stranica već postoji.", - "addon.mod_wiki.pagename": "Naziv stranice", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki stranice", - "addon.mod_wiki.viewpage": "Prikaži stranicu", - "addon.mod_wiki.wikipage": "Wiki stranica", - "addon.mod_wiki.wrongversionlock": "Drugi korisnik je uređivao ovu stranicu kada i vi, vaš sadržaj je zastario.", - "addon.mod_workshop.alreadygraded": "Već ocijenjeno", - "addon.mod_workshop.areainstructauthors": "Upute za predaju rada", - "addon.mod_workshop.areainstructreviewers": "Upute za procjenu", - "addon.mod_workshop.assess": "Procijenite", - "addon.mod_workshop.assessedsubmission": "Procijenjen predani rad", - "addon.mod_workshop.assessmentform": "Obrazac za procjenu", - "addon.mod_workshop.assessmentsettings": "Postavke procjene", - "addon.mod_workshop.assessmentweight": "Težina procjene", - "addon.mod_workshop.assignedassessments": "Radovi dodijeljeni za procjenu", - "addon.mod_workshop.assignedassessmentsnone": "Nemate niti jedan dodijeljeni rad za procjenu", - "addon.mod_workshop.conclusion": "Zaključak", - "addon.mod_workshop.createsubmission": "Započnite pripremu za predaju zadaće", - "addon.mod_workshop.deletesubmission": "Izbriši rad", - "addon.mod_workshop.editsubmission": "Uredi predani rad", - "addon.mod_workshop.feedbackauthor": "Povratna informacija autoru", - "addon.mod_workshop.feedbackby": "Povratna informacija: {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Povratna informacija recenzentu", - "addon.mod_workshop.givengrades": "Dodijeljene ocjene", - "addon.mod_workshop.gradecalculated": "Izračun ocjena za predani rad", - "addon.mod_workshop.gradeinfo": "Ocjena: {{$a.received}} od {{$a.max}}", - "addon.mod_workshop.gradeover": "Poništi prethodnu ocjenu za predani rad", - "addon.mod_workshop.gradesreport": "Izvještaj o ocjenama radionice", - "addon.mod_workshop.gradinggrade": "Ocjena za obavljene procjene", - "addon.mod_workshop.gradinggradecalculated": "Izračunata ocjena za obavljene procjene", - "addon.mod_workshop.gradinggradeof": "Ocjena za obavljenu procjenu (od {{$a}})", - "addon.mod_workshop.gradinggradeover": "Poništi prethodnu ocjenu za obavljene procjene", - "addon.mod_workshop.modulenameplural": "Radionice", - "addon.mod_workshop.nogradeyet": "Još nema ocjene", - "addon.mod_workshop.notassessed": "Rad još nije procijenjen ", - "addon.mod_workshop.notoverridden": "Nije poništeno", - "addon.mod_workshop.noyoursubmission": "Još niste predali svoj rad", - "addon.mod_workshop.overallfeedback": "Sveobuhvatna povratna informacija", - "addon.mod_workshop.publishedsubmissions": "Objavljeni radovi", - "addon.mod_workshop.publishsubmission": "Objavi rad", - "addon.mod_workshop.publishsubmission_help": "Objavljeni radovi bit će dostupni ostalim sudionicima nakon zatvaranja radionice", - "addon.mod_workshop.reassess": "Ponovo procijeni", - "addon.mod_workshop.receivedgrades": "Ocjene su dodijeljene", - "addon.mod_workshop.submissionattachment": "Privitak", - "addon.mod_workshop.submissioncontent": "Sadržaj predanog rada", - "addon.mod_workshop.submissiondeleteconfirm": "Želite li izbrisati ove radove?", - "addon.mod_workshop.submissiongrade": "Ocjena za predani rad", - "addon.mod_workshop.submissiongradeof": "Ocjena za predani rad (od {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Morate upisati tekst ili dodati datoteku.", - "addon.mod_workshop.submissionsreport": "Izvješće o predanim radovima na radionici", - "addon.mod_workshop.submissiontitle": "Naslov", - "addon.mod_workshop.switchphase10": "Prebaci na fazu uređivanja postavki radionice", - "addon.mod_workshop.switchphase20": "Prebaci na fazu predaje radova", - "addon.mod_workshop.switchphase30": "Prebaci na fazu procjene radova", - "addon.mod_workshop.switchphase40": "Prebaci na fazu evaluacije procjena", - "addon.mod_workshop.switchphase50": "Zatvori radionicu", - "addon.mod_workshop.userplan": "Planer radionice", - "addon.mod_workshop.userplancurrentphase": "Trenutačna faza", - "addon.mod_workshop.weightinfo": "Težina: {{$a}}", - "addon.mod_workshop.yourassessment": "Vaša procjena", - "addon.mod_workshop.yourassessmentfor": "Vaše procjene za {{$a}}", - "addon.mod_workshop.yourgrades": "Vaše ocjene", - "addon.mod_workshop.yoursubmission": "Vaš rad", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Ocjena za {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Kriterij {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Morate odabrati ocjenu za ovaj kriterij", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Kriterij {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Ocjena za {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Tvrdnja {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kriterij {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Potrebno je odabrati jednu od ovih stavki", - "addon.notes.addnewnote": "Dodajte novu bilješku", - "addon.notes.coursenotes": "Bilješke na razini e-kolegija", - "addon.notes.deleteconfirm": "Izbriši ovu bilješku?", - "addon.notes.eventnotecreated": "Bilješka stvorena", - "addon.notes.eventnotedeleted": "Bilješka obrisana", - "addon.notes.nonotes": "Trenutačno nema bilješki ove vrste", - "addon.notes.note": "Bilješka", - "addon.notes.notes": "Bilješke", - "addon.notes.personalnotes": "Osobne bilješke", - "addon.notes.publishstate": "Kontekst", - "addon.notes.sitenotes": "Bilješke na razini poslužitelja", - "addon.notifications.markallread": "Označi sve kao pročitane", - "addon.notifications.notificationpreferences": "Postavke za obavijesti", - "addon.notifications.notifications": "Obavijesti", - "addon.notifications.playsound": "Reproduciraj zvuk", - "addon.notifications.therearentnotificationsyet": "Nema obavijesti", - "assets.countries.AD": "Andora", - "assets.countries.AE": "Ujedinjeni Arapski Emirati", - "assets.countries.AF": "Afganistan", - "assets.countries.AG": "Antigva i Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albanija", - "assets.countries.AM": "Armenija", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antartika", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Američka Samoa", - "assets.countries.AT": "Austrija", - "assets.countries.AU": "Australija", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Ålandski otoci", - "assets.countries.AZ": "Azerbajdžan", - "assets.countries.BA": "Bosna i Hercegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladeš", - "assets.countries.BE": "Belgija", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bugarska", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "\t\nBurundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Sveti Barthélemy", - "assets.countries.BM": "Bermudi", - "assets.countries.BN": "Brunej Darussalam", - "assets.countries.BO": "Bolivija", - "assets.countries.BQ": "Bonaire, Sint Eustatius i Saba", - "assets.countries.BR": "Brazil", - "assets.countries.BS": "Bahami", - "assets.countries.BT": "Butan", - "assets.countries.BV": "Otok Bouvet", - "assets.countries.BW": "Bocvana", - "assets.countries.BY": "Bjelorusija", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Kokosovi otoci", - "assets.countries.CD": "Kongo, Demokratska republika", - "assets.countries.CF": "Srednjoafrička Republika", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Švicarska", - "assets.countries.CI": "Obala Bjelokosti", - "assets.countries.CK": "Cook otoci", - "assets.countries.CL": "Čile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Kina", - "assets.countries.CO": "Kolumbija", - "assets.countries.CR": "Kostarika", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Zelenortski Otoci", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Božićni otok", - "assets.countries.CY": "Cipar", - "assets.countries.CZ": "Češka", - "assets.countries.DE": "Njemačka", - "assets.countries.DJ": "Džibuti", - "assets.countries.DK": "Danska", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Dominikanska republika", - "assets.countries.DZ": "Alžir", - "assets.countries.EC": "Ekvador", - "assets.countries.EE": "Estonija", - "assets.countries.EG": "Egipat", - "assets.countries.EH": "Zapadna Sahara", - "assets.countries.ER": "Eritreja", - "assets.countries.ES": "Španjolska", - "assets.countries.ET": "Etiopija", - "assets.countries.FI": "Finska", - "assets.countries.FJ": "Fidži", - "assets.countries.FK": "Falklandski otoci (Malvini)", - "assets.countries.FM": "Mikronezija, Savezne Države", - "assets.countries.FO": "Farski otoci", - "assets.countries.FR": "Francuska", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Ujedinjeno kraljevstvo", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Gruzija", - "assets.countries.GF": "Francuska Gvajana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Gana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grenland", - "assets.countries.GM": "Gambija", - "assets.countries.GN": "Gvineja", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Ekvatorijalna Gvineja", - "assets.countries.GR": "Grčka", - "assets.countries.GS": "Južna Georgija i otočje Južni Sandwich", - "assets.countries.GT": "Gvatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Gvineja Bisau", - "assets.countries.GY": "Gvajana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Otok Heard i otočje McDonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Hrvatska", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Mađarska", - "assets.countries.ID": "Indonezija", - "assets.countries.IE": "Irska", - "assets.countries.IL": "Izrael", - "assets.countries.IM": "Otok Man", - "assets.countries.IN": "Indija", - "assets.countries.IO": "Britanski Teritorij u Indijskom Oceanu", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran, Islamska republika", - "assets.countries.IS": "Island", - "assets.countries.IT": "Italija", - "assets.countries.JE": "Otok Jersey", - "assets.countries.JM": "Jamajka", - "assets.countries.JO": "Jordan", - "assets.countries.JP": "Japan", - "assets.countries.KE": "Kenija", - "assets.countries.KG": "Kirgistan", - "assets.countries.KH": "Kambodža", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komori", - "assets.countries.KN": "Sveti Kristofor i Nevis", - "assets.countries.KP": "Koreja, Demokratska Narodna Republika", - "assets.countries.KR": "Koreja, Republika", - "assets.countries.KW": "Kuvajt", - "assets.countries.KY": "Kajmanski otoci", - "assets.countries.KZ": "Kazahstan", - "assets.countries.LA": "Laoska Narodna Demokratska Republika", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Sveta Lucija", - "assets.countries.LI": "Lihtenštajn", - "assets.countries.LK": "Šri Lanka", - "assets.countries.LR": "Liberija", - "assets.countries.LS": "Lesoto", - "assets.countries.LT": "Litva", - "assets.countries.LU": "Luksemburg", - "assets.countries.LV": "Latvija", - "assets.countries.LY": "Libija", - "assets.countries.MA": "Maroko", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Moldova, Republika", - "assets.countries.ME": "Crna Gora", - "assets.countries.MF": "Sveti Martin (francuski dio)", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Maršalovi otoci", - "assets.countries.MK": "Makedonija", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Mjanmar", - "assets.countries.MN": "Mongolija", - "assets.countries.MO": "Makao", - "assets.countries.MP": "Sjevernomarijanski otoci", - "assets.countries.MQ": "Martinik", - "assets.countries.MR": "Mauritanija", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauricijus", - "assets.countries.MV": "Maldivi", - "assets.countries.MW": "Malavi", - "assets.countries.MX": "Meksiko", - "assets.countries.MY": "Malezija", - "assets.countries.MZ": "Mozambik", - "assets.countries.NA": "Namibija", - "assets.countries.NC": "Nova Kaledonija", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Otok Norfolk", - "assets.countries.NG": "Nigerija", - "assets.countries.NI": "Nikaragva", - "assets.countries.NL": "Nizozemska", - "assets.countries.NO": "Norveška", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Novi Zeland", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Francuska Polinezija", - "assets.countries.PG": "Papua Nova Gvineja", - "assets.countries.PH": "Filipini", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Poljska", - "assets.countries.PM": "Sveti Pierre i Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Portoriko", - "assets.countries.PS": "Palestina", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paragvaj", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Rumunjska", - "assets.countries.RS": "Srbija", - "assets.countries.RU": "Ruska Federacija", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Saudijska Arabija", - "assets.countries.SB": "Solomonski Otoci", - "assets.countries.SC": "Sejšeli", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Švedska", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Sveta Helena, Ascension i Tristan Da Cunha", - "assets.countries.SI": "Slovenija", - "assets.countries.SJ": "Svalbard i Jan Mayen", - "assets.countries.SK": "Slovačka", - "assets.countries.SL": "Sijera Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalija", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Južni Sudan", - "assets.countries.ST": "Sveti Toma i Prinsipe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sveti Martin (nizozemski dio)", - "assets.countries.SY": "Sirijska Arapska Republika", - "assets.countries.SZ": "Svazi", - "assets.countries.TC": "Otoci Turks i Caicos", - "assets.countries.TD": "Čad", - "assets.countries.TF": "Francuski Južni Teritorij", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tajland", - "assets.countries.TJ": "Tadžikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunis", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turska", - "assets.countries.TT": "Trinindad i Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Tajvan", - "assets.countries.TZ": "Tanzanija, Ujedinjena Republika", - "assets.countries.UA": "Ukrajina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Mali udaljeni otoci SAD-a", - "assets.countries.US": "Sjedinjene Američke Države", - "assets.countries.UY": "Urugvaj", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Sveta Stolica (Vatikan)", - "assets.countries.VC": "Sveti Vincent i Grenadini", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Djevičanski Otoci, Britanski", - "assets.countries.VI": "Djevičanski Otoci, SAD", - "assets.countries.VN": "Vijetnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis i Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Južna Afrika", - "assets.countries.ZM": "Zambija", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB knjiga", - "assets.mimetypes.application/msword": "Word dokument", - "assets.mimetypes.application/pdf": "PDF dokument", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle backup", - "assets.mimetypes.application/vnd.ms-excel": "Excel tablica", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 workbook s uključenim makroima", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint prezentacija", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument tablica", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument predložak tablice", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument tekst dokument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument predložak teksta", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument predložak web stranice", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 prezentacija", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 slideshow", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 tablica", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 predložak", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 dokument", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote prezentacija", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers tablica", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages dokument", - "assets.mimetypes.application/x-javascript": "JavaScript izvorna datoteka", - "assets.mimetypes.application/x-mspublisher": "Publisher dokument", - "assets.mimetypes.application/x-shockwave-flash": "Flash animacija", - "assets.mimetypes.application/xhtml_xml": "XHTML dokument", - "assets.mimetypes.archive": "Arhiva ({{$a.EXT}})", - "assets.mimetypes.audio": "Zvučna datoteka ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Datoteka", - "assets.mimetypes.group:archive": "Arhivske datoteke", - "assets.mimetypes.group:audio": "Zvučne datoteke", - "assets.mimetypes.group:document": "Dokumenti", - "assets.mimetypes.group:html_audio": "Zvučne datoteke automatski podržane od preglednika", - "assets.mimetypes.group:html_track": "HTML track datoteke", - "assets.mimetypes.group:html_video": "Video datoteke automatski podržane od preglednika", - "assets.mimetypes.group:image": "Slikovne datoteke", - "assets.mimetypes.group:presentation": "PRezentacijske datoteke", - "assets.mimetypes.group:sourcecode": "Izvorni kôd", - "assets.mimetypes.group:spreadsheet": "Tablice", - "assets.mimetypes.group:video": "Video datoteke", - "assets.mimetypes.group:web_audio": "Zvučne datoteke korištene na webu", - "assets.mimetypes.group:web_file": "Web datoteke", - "assets.mimetypes.group:web_image": "Slikovne datoteke korištene na webu", - "assets.mimetypes.group:web_video": "Video datoteke korištene na webu", - "assets.mimetypes.image": "Slika ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows ikona", - "assets.mimetypes.text/css": "Cascading Style-Sheet", - "assets.mimetypes.text/csv": "Zarezom odvojene vrijednosti", - "assets.mimetypes.text/html": "HTML datoteka", - "assets.mimetypes.text/plain": "Tekstualna datoteka", - "assets.mimetypes.text/rtf": "RTF dokument", - "assets.mimetypes.text/vtt": "Web Video tekstualni track", - "assets.mimetypes.video": "Video datoteka ({{$a.EXT}})", - "core.accounts": "Korisnički računi", - "core.add": "Dodajte", - "core.agelocationverification": "Provjera dobi i lokacije", - "core.ago": "prije {{$a}}", - "core.all": "Sve", - "core.allgroups": "Sve grupe", - "core.allparticipants": "Svi sudionici", - "core.answer": "Odgovor", - "core.answered": "Odgovoreno", - "core.areyousure": "Jeste li sigurni?", - "core.back": "Natrag", - "core.block.blocks": "Blokovi", - "core.cancel": "Odustani", - "core.captureaudio": "Snimi zvuk", - "core.captureimage": "Snimi fotografiju", - "core.capturevideo": "Snimi video", - "core.category": "Kategorija", - "core.choose": "Odaberite", - "core.choosedots": "Odaberi...", - "core.clicktohideshow": "Kliknite za otvaranje ili zatvaranje", - "core.close": "Zatvori", - "core.comments": "Komentari", - "core.comments.addcomment": "Dodaj komentar...", - "core.comments.comments": "Komentari", - "core.comments.commentscount": "Komentari ({{$a}})", - "core.comments.deletecommentbyon": "Izbriši komentar koji je napisao {{$a.user}} u {{$a.time}}", - "core.comments.eventcommentcreated": "Komentar stvoren", - "core.comments.eventcommentdeleted": "Komentar izbrisan", - "core.comments.nocomments": "Nema komentara", - "core.comments.savecomment": "Pohrani komentar", - "core.commentscount": "Komentari ({{$a}})", - "core.completion-alt-auto-fail": "Dovršeno (nije postignuta prolazna ocjena): {{$a}}", - "core.completion-alt-auto-n": "Nije dovršeno: {{$a}}", - "core.completion-alt-auto-n-override": "Nije dovršeno: {{$a.modname}} (zadao/la {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Dovršeno (postignuta prolazna ocjena): {{$a}}", - "core.completion-alt-auto-y": "Dovršeno: {{$a}}", - "core.completion-alt-auto-y-override": "Dovršeno: {{$a.modname}} (zadao/la {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Nije dovršeno: {{$a}}, odaberite za označavanje kao dovršeno.", - "core.completion-alt-manual-n-override": "Nije dovršeno: {{$a.modname}} (zadao/la {{$a.overrideuser}}). Odaberite za označavanje kao dovršeno.", - "core.completion-alt-manual-y": "Dovršeno: {{$a}}. Odaberite za označavanje kao nedovršeno.", - "core.completion-alt-manual-y-override": "Dovršeno: {{$a.modname}} (zadao/la {{$a.overrideuser}}). Odaberite za označavanje kao nedovršeno.", - "core.confirmdeletefile": "Želite li izbrisati ovu datoteku?", - "core.considereddigitalminor": "Premladi ste da biste izradili račun na ovoj web-lokaciji.", - "core.content": "Sadržaj", - "core.contentlinks.chooseaccount": "Odaberite račun", - "core.continue": "Nastavi", - "core.course": "E-kolegij", - "core.course.allsections": "Sve sekcije / teme", - "core.course.contents": "Sadržaj", - "core.course.couldnotloadsectioncontent": "Sadržaj", - "core.course.coursesummary": "Sažetak e-kolegija", - "core.course.downloadcourse": "Preuzmi e-kolegij", - "core.course.hiddenfromstudents": "Skriveno od studenata", - "core.course.hiddenoncoursepage": "Dostupno, ali nije prikazano na stranici e-kolegija", - "core.course.overriddennotice": "Konačna ocjena za ovu aktivnost ručno je podešena.", - "core.course.refreshcourse": "Osvježi e-kolegij", - "core.course.sections": "Sekcije", - "core.coursedetails": "Detalji kolegija", - "core.courses.addtofavourites": "Označi zvjezdicom ovaj e-kolegij", - "core.courses.allowguests": "Ovaj e-kolegij dopušta pristup gostima (anonimnim korisnicima)", - "core.courses.availablecourses": "Dostupni e-kolegiji", - "core.courses.categories": "Popis e-kolegija", - "core.courses.courses": "E-kolegiji", - "core.courses.downloadcourses": "Preuzmi e-kolegij", - "core.courses.enrolme": "Upiši me", - "core.courses.filtermycourses": "Filtriraj moje e-kolegije", - "core.courses.frontpage": "Naslovnica", - "core.courses.hidecourse": "Skrij", - "core.courses.ignore": "Ignoriraj", - "core.courses.mycourses": "Moji e-kolegiji", - "core.courses.mymoodle": "Moja naslovnica", - "core.courses.nocourses": "Nema podataka o e-kolegijima za prikaz.", - "core.courses.nocoursesyet": "U ovoj kategoriji nema e-kolegija", - "core.courses.nosearchresults": "Nema rezultata", - "core.courses.notenroled": "Niste upisani kao student na ovom e-kolegiju", - "core.courses.notenrollable": "Ne možete se samostalno upisati na ovaj e-kolegij.", - "core.courses.password": "Lozinka e-kolegija", - "core.courses.paymentrequired": "Ovaj e-kolegij zahtjeva plaćanje za pristup.", - "core.courses.paypalaccepted": "Plaćanje PayPalom omogućeno", - "core.courses.reload": "Učitaj ponovno", - "core.courses.removefromfavourites": "Uklonite zvjezdicu s ovog e-kolegija", - "core.courses.search": "Pretraži", - "core.courses.searchcourses": "Pretraži e-kolegije", - "core.courses.sendpaymentbutton": "Pošalji uplatu kroz PayPal", - "core.courses.show": "Prikaži ovaj e-kolegij", - "core.courses.totalcoursesearchresults": "Ukupni broj e-kolegija: {{$a}}", - "core.currentdevice": "Trenutačni uređaj", - "core.date": "Datum", - "core.day": "dan", - "core.days": "dana", - "core.decsep": ",", - "core.defaultvalue": "Zadano ({{$a}})", - "core.delete": "Izbriši", - "core.deleteduser": "Izbrisani korisnik", - "core.description": "Opis", - "core.dfdaymonthyear": "DD-MM-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Digitalni maloljetnik", - "core.digitalminor_desc": "Zamolite svog roditelja/skrbnika da kontaktira:", - "core.discard": "Odbaci", - "core.dismiss": "Odustani", - "core.displayoptions": "Opcije prikaza", - "core.done": "Gotovo", - "core.download": "Preuzimanje", - "core.downloading": "Preuzimanje", - "core.edit": "Uredi", - "core.editor.autosavesucceeded": "Nacrt pohranjen.", - "core.editor.bold": "Masno", - "core.editor.clear": "Ukloni oblikovanje", - "core.editor.h3": "Naslov (veći)", - "core.editor.h4": "Naslov (srednji)", - "core.editor.h5": "Naslov (manji)", - "core.editor.italic": "Kurziv", - "core.editor.p": "Paragraf", - "core.editor.strike": "Precrtaj", - "core.editor.textrecovered": "Nacrt ovog teksta automatski je vraćen.", - "core.editor.underline": "Podvlačenje", - "core.error": "Greška", - "core.favourites": "Označeno zvjezdicom", - "core.filename": "Naziv datoteke", - "core.filenameexist": "Naziv datoteke već postoji: {{$a}}", - "core.filenotfound": "Nažalost, datoteka nije pronađena.", - "core.fileuploader.addfiletext": "Dodaj datoteku", - "core.fileuploader.audio": "Zvuk", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.file": "Datoteka", - "core.fileuploader.filesofthesetypes": "Dopušteni vrste datoteka:", - "core.fileuploader.invalidfiletype": "{{$a}}: vrsta datoteke nije prihvaćena.", - "core.fileuploader.more": "Više", - "core.fileuploader.photoalbums": "Foto albumi", - "core.fileuploader.selectafile": "Odaberite datoteku", - "core.fileuploader.video": "Video", - "core.filter": "Filtar", - "core.folder": "Mapa", - "core.forcepasswordchangenotice": "Morate promijeniti svoju lozinku kako biste mogli nastaviti s radom.", - "core.fulllistofcourses": "Svi e-kolegiji", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Prosjek", - "core.grades.badgrade": "Upisana ocjena nije ispravna", - "core.grades.contributiontocoursetotal": "Ukupan doprinos e-kolegiju", - "core.grades.feedback": "Povratna informacija", - "core.grades.grade": "Ocjena", - "core.grades.gradeitem": "Stavka ocjene", - "core.grades.grades": "Ocjene", - "core.grades.lettergrade": "Slovna ocjena", - "core.grades.nogradesreturned": "Nema ocjena", - "core.grades.nooutcome": "Bez ishoda učenja", - "core.grades.percentage": "Postotak", - "core.grades.range": "Opseg", - "core.grades.rank": "Rang", - "core.grades.weight": "Ponder", - "core.group": "Grupa", - "core.groupsseparate": "Odvojene grupe", - "core.groupsvisible": "Vidljive grupe", - "core.h5p.author": "Autor", - "core.h5p.authorcommentsdescription": "Komentari autora sadržaja. (Ovaj tekst neće biti vidljiv dok god nisu dio podataka o autorskim pravima.)", - "core.h5p.authorname": "Autorovo ime", - "core.h5p.authorrole": "Uloga autora", - "core.h5p.cancellabel": "Poništi", - "core.h5p.ccattribution": "Imenovanje (CC BY)", - "core.h5p.ccattributionnc": "Imenovanje-Nekomercionalno (CC BY-NC)", - "core.h5p.ccattributionncnd": "Imenovanje-Nekomercijalno-Bez prerada (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Imenovanje-Nekomercijalno-Dijeli pod istim uvjetima (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Imenovanje-Bez prerada (CC BY-ND)", - "core.h5p.ccattributionsa": "Imenovanje-Dijeli pod istim uvjetima (CC BY-SA)", - "core.h5p.ccpdd": "Prenošenje u javno dobro (CC0)", - "core.h5p.close": "Zatvori", - "core.h5p.confirmdialogheader": "Potvrdi akciju", - "core.h5p.confirmlabel": "Potvrdi", - "core.h5p.date": "Datum", - "core.h5p.editor": "Urednik", - "core.h5p.license": "Licenca", - "core.h5p.licenseCC010": "CC0 1.0 Univerzalno (CC0 1.0) Prenošenje u javno dobro", - "core.h5p.licenseCC010U": "CC0 1.0 Univerzalno", - "core.h5p.licenseV1": "Inačica 1", - "core.h5p.licenseV2": "Inačica 2", - "core.h5p.licenseV3": "Inačica 3", - "core.h5p.licenseversion": "Inačica licence", - "core.help": "Pomoć", - "core.hide": "Skrij", - "core.hour": "sat", - "core.hours": "sat(a)", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Slika", - "core.info": "Informacija", - "core.invalidformdata": "Neispravni podaci u obrascu", - "core.labelsep": ":", - "core.lastaccess": "Zadnji pristup", - "core.lastmodified": "Zadnji puta izmijenjeno", - "core.layoutgrid": "Mreža", - "core.list": "Popis", - "core.listsep": ";", - "core.loading": "Učitavanje", - "core.location": "Lokacija", - "core.login.auth_email": "Autentikacija zasnovana na e-pošti", - "core.login.cancel": "Odustani", - "core.login.changepassword": "Promijeni lozinku", - "core.login.connect": "Prijavi se!", - "core.login.connecttomoodle": "Prijavi se na Moodle", - "core.login.createaccount": "Stvori moj novi korisnički račun", - "core.login.createuserandpass": "Stvori novo korisničko ime i lozinku s kojom se mogu prijaviti sustavu", - "core.login.emailconfirmsent": "

                Uskoro biste trebali primiti poruku e-poste na vasu adresu {{$a}}

                \n

                Poruka sadrzava jednostavne upute o daljnjem postupku registracije.

                \n

                Ako imate tehnickih problema s registracijom ili pitanja u vezi iste, posaljite poruku e-postom administratoru sustava.

                ", - "core.login.emailconfirmsentsuccess": "Poruka potvrde registracije uspješno poslana", - "core.login.firsttime": "Ovdje ste prvi put?", - "core.login.forcepasswordchangenotice": "Morate promijeniti svoju lozinku kako biste mogli nastaviti s radom.", - "core.login.forgotten": "Zaboravili ste svoje korisničko ime ili lozinku?", - "core.login.help": "Pomoć", - "core.login.instructions": "Upute", - "core.login.invaliddate": "Neispravan datum", - "core.login.invalidemail": "Nevažeća adresa e-pošte", - "core.login.invalidmoodleversion": "Moodle inačica treba biti barem {{$a}}.", - "core.login.invalidsite": "URL sjedišta nije valjan.", - "core.login.invalidtime": "Neispravno vrijeme", - "core.login.invalidurl": "Zadan je neispravan URL", - "core.login.invalidvaluemax": "Najveća vrijednost je {{$a}}", - "core.login.invalidvaluemin": "Najmanja vrijednost je {{$a}}", - "core.login.login": "Prijava", - "core.login.loginbutton": "Prijava", - "core.login.loginsteps": "Kako biste imali puni pristup e-kolegijima na ovom sustavu, morate stvoriti novi korisnički račun.", - "core.login.missingemail": "Nedostaje adresa e-pošte", - "core.login.missingfirstname": "Nedostaje ime", - "core.login.missinglastname": "Nedostaje prezime", - "core.login.mustconfirm": "Morate potvrditi vaš korisnički račun", - "core.login.newaccount": "Novi korisnički račun", - "core.login.notloggedin": "Morate biti prijavljeni", - "core.login.password": "Lozinka", - "core.login.passwordforgotten": "Zaboravljena lozinka", - "core.login.passwordforgotteninstructions2": "Za resetiranje vaše lozinke, upišite svoje korisničko ime ili adresu e-pošte. Ako ti podaci postoje u bazi podataka, primit ćete poruku na pripadajuću adresu e-pošte, s uputama kako ponovno dobiti pristup sustavu.", - "core.login.passwordrequired": "Unesite lozinku", - "core.login.policyaccept": "Razumijem i prihvaćam", - "core.login.policyagree": "Prije korištenja ovog sustava morate prihvatiti pravilnik ponašanja na sustavu. Prihvaćate li?", - "core.login.policyagreement": "Pravilnik o korištenju sustava", - "core.login.policyagreementclick": "Poveznica na pravilnik o korištenju sustava", - "core.login.potentialidps": "Prijavite se koristeći svoj korisnički račun koji imate na:", - "core.login.profileinvaliddata": "Netočna vrijednost", - "core.login.reconnect": "Spoji se ponovno", - "core.login.resendemail": "Pošalji poruku e-pošte ponovno", - "core.login.security_question": "Sigurnosno pitanje", - "core.login.selectacountry": "Odaberite državu", - "core.login.selectsite": "Odaberi sjedište:", - "core.login.signupplugindisabled": "{{$a}} nije omogućen(o).", - "core.login.siteaddress": "Adresa poslužitelja", - "core.login.siteinmaintenance": "Poslužitelj je trenutno u načinu održavanja.", - "core.login.siteurl": "URL poslužitelja", - "core.login.siteurlrequired": "Potreban je URL sjedišta, npr. http://www.yourmoodlesite.org", - "core.login.startsignup": "Stvori novi korisnički račun!", - "core.login.stillcantconnect": "Još se ne možeš spojiti?", - "core.login.supplyinfo": "Molimo unesite osobne informacije", - "core.login.username": "Korisničko ime", - "core.login.usernameoremail": "Unesite korisničko ime ILI adresu e-pošte", - "core.login.usernamerequired": "Unesite korisničko ime", - "core.login.usernotaddederror": "Korisnik nije dodan - nepoznata pogreška", - "core.mainmenu.changesite": "Promijeni poslužitelj", - "core.mainmenu.help": "Pomoć", - "core.mainmenu.logout": "Odjava", - "core.mainmenu.website": "Web", - "core.maxsizeandattachments": "Najveća dopuštena veličina za nove datoteke: {{$a.size}}, najveći broj privitaka: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Razno", - "core.mod_assign": "Zadaća", - "core.mod_assignment": "Zadaća 2.2 (onemogućeno)", - "core.mod_book": "Knjiga", - "core.mod_chat": "Chat", - "core.mod_choice": "Odabir", - "core.mod_data": "Baza podataka", - "core.mod_database": "Baza podataka", - "core.mod_external-tool": "Vanjski alat", - "core.mod_feedback": "Povratna informacija", - "core.mod_file": "Datoteka", - "core.mod_folder": "Mapa", - "core.mod_forum": "Forum", - "core.mod_glossary": "Rječnik", - "core.mod_ims": "IMS paket", - "core.mod_imscp": "IMS paket", - "core.mod_label": "Natpis", - "core.mod_lesson": "Lekcija", - "core.mod_lti": "Vanjski alat", - "core.mod_page": "Stranica", - "core.mod_quiz": "Test", - "core.mod_resource": "Datoteka", - "core.mod_scorm": "SCORM paket", - "core.mod_survey": "Upitnik", - "core.mod_url": "Poveznica", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Radionica", - "core.moduleintro": "Opis", - "core.more": "više", - "core.mygroups": "Moje grupe", - "core.name": "Ime", - "core.networkerrormsg": "Dogodila se pogreška pri spajanju na sjedište. Provjerite vezu i pokušajte ponovno.", - "core.never": "Nikad", - "core.next": "Nastavi", - "core.no": "Ne", - "core.nocomments": "Nema komentara", - "core.nograde": "Nema ocjene", - "core.none": "Nijedan", - "core.nopermissions": "Trenutačno nemate ovlasti da napravite ({{$a}}).", - "core.noresults": "Nema rezultata", - "core.noselection": "Ništa nije odabrano", - "core.notenrolledprofile": "Ovaj profil je nedostupan jer korisnik nije prijavljen na ovaj e-kolegij.", - "core.notice": "Obavijest", - "core.notingroup": "Nažalost, morate biti član grupe kako biste vidjeli ovu stranicu.", - "core.notsent": "Nije poslano", - "core.now": "sada", - "core.numwords": "{{$a}} riječi", - "core.offline": "Offline", - "core.ok": "U redu", - "core.online": "Online", - "core.openinbrowser": "Otvori u pregledniku", - "core.othergroups": "Ostale grupe", - "core.pagea": "Stranica {{$a}}", - "core.paymentinstant": "Upotrijebite donju poveznicu kako biste platili i bili u mogućnosti pristupiti e-kolegiju za manje od par minuta!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pictureof": "Slika {{$a}}", - "core.previous": "Prethodni", - "core.proceed": "Nastavi", - "core.question.answer": "Odgovor", - "core.question.answersaved": "Odgovor pohranjen", - "core.question.certainty": "Sigurnost", - "core.question.complete": "Završeno", - "core.question.correct": "Točno", - "core.question.feedback": "Povratna informacija", - "core.question.incorrect": "Netočno", - "core.question.information": "Informacija", - "core.question.invalidanswer": "Nepotpun odgovor", - "core.question.notanswered": "Nije odgovoreno", - "core.question.notyetanswered": "Nije još odgovoreno", - "core.question.partiallycorrect": "Djelomično točno", - "core.question.questionmessage": "Pitanje {{$a}}: {{$b}}", - "core.question.questionno": "Pitanje {{$a}}", - "core.question.requiresgrading": "Zahtijeva ocjenjivanje", - "core.quotausage": "Trenutno ste upotrijebili {{$a.used}} od dostupnog ograničenja od {{$a.total}}.", - "core.rating.aggregateavg": "Prosjek ocjena", - "core.rating.aggregatecount": "Broj ocjena", - "core.rating.aggregatemax": "Najviša ocjena", - "core.rating.aggregatemin": "Najniža ocjena", - "core.rating.aggregatesum": "Zbroj ocjena", - "core.rating.noratings": "Nema predanih ocjena", - "core.rating.rating": "Ocjena", - "core.rating.ratings": "Ocjene", - "core.refresh": "Osvježavanje", - "core.remove": "Ukloni", - "core.required": "Obvezatno", - "core.resourcedisplayopen": "Otvori", - "core.resources": "Resursi", - "core.restore": "Vraćanje iz kopije", - "core.restricted": "Ograničenje", - "core.save": "Pohrani", - "core.savechanges": "Pohrani promjene", - "core.search": "Pretraži", - "core.searching": "Pretraga", - "core.searchresults": "Rezultati pretraživanja", - "core.sec": "sek", - "core.secs": "s", - "core.seemoredetail": "Kliknite ovdje za više detalja", - "core.selectacategory": "Odaberite kategoriju", - "core.selectacourse": "Odaberite državu", - "core.selectagroup": "Odaberite grupu", - "core.send": "Šalji", - "core.sending": "Slanje", - "core.serverconnection": "Pogreška pri spajanju na poslužitelj", - "core.settings.about": "O", - "core.settings.compilationinfo": "Informacije o kompilaciji", - "core.settings.cordovadevicemodel": "Model Cordova uređaja", - "core.settings.cordovadeviceosversion": "OS verzija Cordova uređaja", - "core.settings.cordovadeviceplatform": "Platforma Cordova uređaja", - "core.settings.cordovadeviceuuid": "UUID Cordova uređaja", - "core.settings.cordovaversion": "Verzija Cordove", - "core.settings.currentlanguage": "Trenutačno važeći jezik", - "core.settings.debugdisplay": "Prikaži debug poruke", - "core.settings.debugdisplaydescription": "Ako je omogućeno, kad je moguće bit će prikazane detaljnije poruke o pogreškama.", - "core.settings.deletesitefilestitle": "Obriši datoteke sjedišta", - "core.settings.deviceinfo": "Informacija o uređaju", - "core.settings.deviceos": "OS uređaja", - "core.settings.disableall": "Onemogući obavijesti", - "core.settings.disabled": "Onemogućeno", - "core.settings.displayformat": "Oblik prikaza", - "core.settings.enabledownloadsection": "Omogući preuzimanje sekcija/tema", - "core.settings.enablerichtexteditor": "Omogući uređivač teksta", - "core.settings.enablerichtexteditordescription": "Ako je omogućeno, uređivač teksta bit će dostupan pri unosu sadržaja.", - "core.settings.enablesyncwifi": "Sinkronizacija samo putem Wi-Fi", - "core.settings.errordeletesitefiles": "Pogreška pri brisanju datoteka sjedišta.", - "core.settings.errorsyncsite": "Dogodila se pogreška pri sinkronizaciji podataka. Provjerite internet vezu i pokušajte ponovno.", - "core.settings.estimatedfreespace": "Procijenjeni slobodni prostor", - "core.settings.filesystemroot": "Korijenska mapa datotečnog sustava", - "core.settings.fontsizecharacter": "A", - "core.settings.general": "Općenito", - "core.settings.language": "Jezik", - "core.settings.license": "Licenca", - "core.settings.locked": "Zaključano", - "core.settings.loggedin": "Online", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Jezik preglednika", - "core.settings.navigatoruseragent": "userAgent preglednika", - "core.settings.networkstatus": "Status internet veze", - "core.settings.preferences": "Postavke", - "core.settings.privacypolicy": "Politika privatnosti", - "core.settings.pushid": "ID push obavijesti", - "core.settings.reportinbackground": "Automatski prijavi pogreške", - "core.settings.settings": "Postavke", - "core.settings.showdownloadoptions": "Prikaži opcije za preuzimanje", - "core.settings.sites": "Sustavi", - "core.settings.spaceusage": "Utrošak prostora", - "core.settings.synchronization": "Sinkronizacija", - "core.settings.synchronizenow": "Sinkronizirajte sada", - "core.settings.syncsettings": "Postavke sinkronizacije", - "core.settings.total": "Ukupno", - "core.settings.wificonnection": "Wi-Fi veza", - "core.sharedfiles.rename": "Preimenuj", - "core.sharedfiles.replace": "Zamijeni", - "core.sharedfiles.sharedfiles": "Dijeljene datoteke", - "core.show": "Prikaži", - "core.showless": "Prikaži manje...", - "core.showmore": "Prikaži više...", - "core.site": "Site", - "core.sitehome.sitehome": "Naslovnica", - "core.sitehome.sitenews": "Obavijesti na razini sjedišta", - "core.sitemaintenance": "Sustav je trenutačno nedostupan zbog održavanja i radova.", - "core.sizeb": "bajtovi", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Preskoči", - "core.sort": "Sortiranje", - "core.sortby": "Sortiraj prema", - "core.start": "Započni", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%y, %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "", - "core.strftimetime24": "%H:%M", - "core.submit": "Predaj", - "core.success": "Uspješno", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Zadana zbirka", - "core.tag.inalltagcoll": "Svuda", - "core.tag.noresultsfor": "Nema rezultata u \"{{$a}}\"", - "core.tag.searchtags": "Pretraži oznake", - "core.tag.tag": "Oznaka (tag)", - "core.tag.tagarea_course": "E-kolegiji", - "core.tag.tagarea_course_modules": "Aktivnosti i resursi", - "core.tag.tagarea_post": "Blog članci", - "core.tag.tagarea_user": "Interesi korisnika", - "core.tag.tags": "Oznake", - "core.teachers": "Nastavnici", - "core.thereisdatatosync": "Postoje offline {{$a}} koje treba sinkronizirati.", - "core.thisdirection": "ltr", - "core.time": "Vrijeme", - "core.timesup": "Vrijeme je isteklo!", - "core.today": "Danas", - "core.tryagain": "Pokušaj opet", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "Ups!", - "core.unexpectederror": "Nepredviđena greška. Zatvorite aplikaciju, ponovno je pokrenite i pokušajte opet.", - "core.unknown": "Nepoznato", - "core.unlimited": "Neograničeno", - "core.upgraderunning": "Sustav je u postupku nadogradnje, molimo pokušajte kasnije.", - "core.user": "Korisnik", - "core.user.address": "Adresa", - "core.user.city": "Grad", - "core.user.contact": "Kontakt", - "core.user.country": "Država", - "core.user.description": "Opis", - "core.user.details": "Detalji", - "core.user.detailsnotavailable": "Detalji o navedenom korisniku nisu vam dostupni.", - "core.user.editingteacher": "Nastavnik", - "core.user.email": "Adresa e-pošte", - "core.user.emailagain": "E-pošta (ponovno)", - "core.user.errorloaduser": "Greška pri učitavanju korisnika.", - "core.user.firstname": "Ime", - "core.user.interests": "Interesi", - "core.user.lastname": "Prezime", - "core.user.manager": "Menadžer", - "core.user.newpicture": "Nova slika", - "core.user.noparticipants": "Na ovom e-kolegiju nema sudionika", - "core.user.participants": "Sudionici", - "core.user.phone1": "Telefon", - "core.user.phone2": "Mobitel", - "core.user.roles": "Uloge", - "core.user.sendemail": "E-pošta", - "core.user.student": "Student", - "core.user.teacher": "Nastavnik bez ovlasti za uređivanje", - "core.user.webpage": "Web stranica", - "core.userdeleted": "Ovaj korisnički račun je izbrisan", - "core.userdetails": "Detalji o korisniku", - "core.usernotfullysetup": "Korisnički račun nije podešen u potpunosti", - "core.users": "Korisnici", - "core.view": "Prikaz", - "core.viewcode": "Prikaži kôd", - "core.vieweditor": "Prikaži uređivač", - "core.viewprofile": "Prikaži profil", - "core.warningofflinedatadeleted": "Offline podaci iz {{component}} '{{name}}' su izbrisani. {{error}}", - "core.whatisyourage": "Koliko imate godina?", - "core.wheredoyoulive": "U kojoj državi živite?", - "core.whoops": "Ups!", - "core.whyisthishappening": "Zašto se navedeno događa?", - "core.whyisthisrequired": "Zašto je ovo neophodno?", - "core.wsfunctionnotavailable": "Funkcija web servisa nije dostupna.", - "core.year": "godina", - "core.years": "godine", - "core.yes": "Da" -} \ No newline at end of file diff --git a/src/assets/lang/hu.json b/src/assets/lang/hu.json deleted file mode 100644 index c077541b9..000000000 --- a/src/assets/lang/hu.json +++ /dev/null @@ -1,1678 +0,0 @@ -{ - "addon.badges.alignment": "Igazítás", - "addon.badges.badgedetails": "Részletek", - "addon.badges.badges": "Kitűzők", - "addon.badges.bendorsement": "Jóváhagyás", - "addon.badges.claimcomment": "Megjegyzés a támogató megerősítéshez", - "addon.badges.claimid": "Igény webcíme", - "addon.badges.contact": "Kapcsolat", - "addon.badges.dateawarded": "Kiadás dátuma", - "addon.badges.expired": "Lejárt", - "addon.badges.expirydate": "Lejárat időpontja", - "addon.badges.imageauthoremail": "Kép szerzőjének e-mailje", - "addon.badges.imageauthorname": "A kép szerzőjének a neve", - "addon.badges.imageauthorurl": "A kép szerzőjének webcíme", - "addon.badges.imagecaption": "Képfelirat", - "addon.badges.issuancedetails": "A kitűző lejárata", - "addon.badges.issuerdetails": "Az adományozó adatai", - "addon.badges.issueremail": "E-mail", - "addon.badges.issuername": "Az adományozó neve", - "addon.badges.issuerurl": "Az adományozó webcíme", - "addon.badges.language": "Nyelv", - "addon.badges.noalignment": "A kitűzőhöz nincs megadva külső jártasság vagy szabvány.", - "addon.badges.nobadges": "Nincs elérhető kitűző.", - "addon.badges.norelated": "A kitűzőhöz nincsenek kapcsolódó kitűzők.", - "addon.badges.recipientdetails": "A megjutalmazott adatai", - "addon.badges.relatedbages": "Kapcsolódó kitűzők", - "addon.badges.version": "Változat", - "addon.badges.warnexpired": "(A kitűző érvényessége lejárt.)", - "addon.block_activitymodules.pluginname": "Tevékenységek", - "addon.block_activityresults.pluginname": "Tevékenység eredményei", - "addon.block_badges.pluginname": "Legutóbbi kitűzőim", - "addon.block_blogmenu.pluginname": "Blogmenü", - "addon.block_blogrecent.pluginname": "Utóbbi blogüzenetek", - "addon.block_blogtags.pluginname": "Blogcímkék", - "addon.block_calendarmonth.pluginname": "Naptár", - "addon.block_calendarupcoming.pluginname": "Elkövetkező események", - "addon.block_comments.pluginname": "Megjegyzések", - "addon.block_completionstatus.pluginname": "Kurzusteljesítés állapota", - "addon.block_glossaryrandom.pluginname": "Véletlenszerű fogalom", - "addon.block_learningplans.pluginname": "Tanulási tervek", - "addon.block_myoverview.all": "Mind (a rejtettek kivételével)", - "addon.block_myoverview.allincludinghidden": "Összes", - "addon.block_myoverview.favourites": "Bejelölve", - "addon.block_myoverview.future": "Jövőbeni", - "addon.block_myoverview.hiddencourses": "Rejtett", - "addon.block_myoverview.inprogress": "Folyamatban lévő", - "addon.block_myoverview.lastaccessed": "Utolsó hozzáférés", - "addon.block_myoverview.morecourses": "További kurzusok", - "addon.block_myoverview.nocourses": "Nincs kurzus", - "addon.block_myoverview.past": "Korábbi", - "addon.block_myoverview.pluginname": "Kurzusáttekintés", - "addon.block_myoverview.shortname": "Rövid név", - "addon.block_myoverview.title": "Cím", - "addon.block_newsitems.pluginname": "Legfrissebb közlemények", - "addon.block_onlineusers.pluginname": "Online felhasználók", - "addon.block_privatefiles.pluginname": "Saját állományaim", - "addon.block_recentactivity.pluginname": "Legutóbbi tevékenység", - "addon.block_recentlyaccessedcourses.nocourses": "Nincsenek friss kurzusok", - "addon.block_recentlyaccessedcourses.pluginname": "Mostanában felkeresett kurzusok", - "addon.block_recentlyaccesseditems.noitems": "Nincsenek friss tételek.", - "addon.block_recentlyaccesseditems.pluginname": "Mostanában felkeresett tételek", - "addon.block_rssclient.pluginname": "Távoli RSS-hírek", - "addon.block_selfcompletion.pluginname": "Önálló teljesítés", - "addon.block_sitemainmenu.pluginname": "Főmenü", - "addon.block_starredcourses.nocourses": "Nincsenek", - "addon.block_starredcourses.pluginname": "Megjelölt kurzusok", - "addon.block_tags.pluginname": "Címkék", - "addon.block_timeline.duedate": "Esedékesség", - "addon.block_timeline.next30days": "A következő 30 nap", - "addon.block_timeline.next3months": "A következő 3 hónap", - "addon.block_timeline.next6months": "A következő 6 hónap", - "addon.block_timeline.next7days": "A következő 7 nap", - "addon.block_timeline.nocoursesinprogress": "Nincs aktuális kurzus", - "addon.block_timeline.noevents": "Nincs esedékes tevékenység", - "addon.block_timeline.overdue": "Lejárt", - "addon.block_timeline.pluginname": "Idősor", - "addon.block_timeline.sortbycourses": "Rendezés kurzusonként", - "addon.block_timeline.sortbydates": "Rendezés dátumonként", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Blogüzenetek", - "addon.blog.linktooriginalentry": "Kapcsolás az eredeti blogüzenethez", - "addon.blog.noentriesyet": "Itt nincsenek látható üzenetek", - "addon.blog.publishtonoone": "Csak én (piszkozat)", - "addon.blog.publishtosite": "A portálon bárki", - "addon.blog.publishtoworld": "A világon bárki", - "addon.blog.siteblogheading": "Portálblog", - "addon.calendar.allday": "Egész nap", - "addon.calendar.calendar": "Naptár", - "addon.calendar.categoryevents": "Kategóriába tartozó események", - "addon.calendar.confirmeventdelete": "Biztosan törölni akarja ezt a(z) \"{{$a}}\" eseményt?", - "addon.calendar.confirmeventseriesdelete": "A(z) \"{{$a.name}}\" esemény egy sorozat része. Csak ezt az eseményt törli vagy a sorozat összes {{$a.count}} eseményét?", - "addon.calendar.courseevents": "Kurzusesemények", - "addon.calendar.daynext": "A következő nap", - "addon.calendar.dayprev": "Az előző nap", - "addon.calendar.deleteallevents": "Az összes esemény törlése", - "addon.calendar.deleteevent": "Esemény törlése", - "addon.calendar.deleteoneevent": "Ezen esemény törlése", - "addon.calendar.durationminutes": "Időtartam percben", - "addon.calendar.durationnone": "Időtartam nélkül", - "addon.calendar.durationuntil": "Meddig", - "addon.calendar.editevent": "Esemény szerkesztése", - "addon.calendar.eventcalendareventdeleted": "Naptáresemény törölve", - "addon.calendar.eventduration": "Időtartam", - "addon.calendar.eventendtime": "Befejezési időpont", - "addon.calendar.eventkind": "Esemény típusa", - "addon.calendar.eventname": "Esemény megnevezése", - "addon.calendar.eventstarttime": "Kezdési időpont", - "addon.calendar.eventtype": "Esemény típusa", - "addon.calendar.fri": "P", - "addon.calendar.friday": "Péntek", - "addon.calendar.gotoactivity": "Áttérés a tevékenységre", - "addon.calendar.groupevents": "Csoportesemények", - "addon.calendar.invalidtimedurationminutes": "A percekben megadott időtartam érvénytelen. Adjon meg 0 percnél hosszabb időtartamot, vagy válassza a nincs időtartamot.", - "addon.calendar.invalidtimedurationuntil": "A záró időtartam napja és időpontja az esemény kezdete előttre esik. Továbblépés előtt javítsa ki.", - "addon.calendar.mon": "H", - "addon.calendar.monday": "Hétfő", - "addon.calendar.monthlyview": "Havi nézet", - "addon.calendar.newevent": "Új esemény", - "addon.calendar.nopermissiontoupdatecalendar": "A naptáresemény frissítéséhez nincs engedélye.", - "addon.calendar.repeatedevents": "Ismétlődő események", - "addon.calendar.repeateditall": "Változás kiterjesztése ezen ismétlés minden {{$a}} eseményére", - "addon.calendar.repeateditthis": "Változás kiterjesztése csak erre az eseményre", - "addon.calendar.repeatevent": "Esemény megismétlése", - "addon.calendar.repeatweeksl": "Heti ismétlés, összesen", - "addon.calendar.sat": "Szo", - "addon.calendar.saturday": "Szombat", - "addon.calendar.siteevents": "Portálesemények", - "addon.calendar.sun": "V", - "addon.calendar.sunday": "Vasárnap", - "addon.calendar.thu": "Cs", - "addon.calendar.thursday": "Csütörtök", - "addon.calendar.today": "Ma", - "addon.calendar.tomorrow": "Holnap", - "addon.calendar.tue": "K", - "addon.calendar.tuesday": "Kedd", - "addon.calendar.typecategory": "Kategóriába tartozó esemény", - "addon.calendar.typeclose": "Esemény bezárása", - "addon.calendar.typecourse": "Kurzusesemény", - "addon.calendar.typedue": "Esedékes esemény", - "addon.calendar.typegradingdue": "Osztályozandó esemény", - "addon.calendar.typegroup": "Csoportesemény", - "addon.calendar.typeopen": "Esemény megnyitása", - "addon.calendar.typesite": "Portálhoz tartozó esemény", - "addon.calendar.typeuser": "Felhasználóhoz tartozó esemény", - "addon.calendar.upcomingevents": "Elkövetkező események", - "addon.calendar.userevents": "Felhasználóhoz tartozó események", - "addon.calendar.wed": "Sze", - "addon.calendar.wednesday": "Szerda", - "addon.calendar.when": "Mikor", - "addon.calendar.yesterday": "Tegnap", - "addon.competency.activities": "Tevékenységek", - "addon.competency.competencies": "Készségek", - "addon.competency.competenciesmostoftennotproficientincourse": "A kurzusban leginkább eredménytelen készségek", - "addon.competency.coursecompetencies": "Kurzuskészségek", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "A kurzus készségbesorolásai nem érintik a tanulási terveket.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "A kurzus készségbesorolásai azonnal frissülnek a tanulási tervekben.", - "addon.competency.crossreferencedcompetencies": "Kereszthivatkozott készségek", - "addon.competency.duedate": "Esedékesség", - "addon.competency.evidence": "Bizonyíték", - "addon.competency.evidence_competencyrule": "A készséghez tartozó szabály teljesítve.", - "addon.competency.evidence_coursecompleted": "'{{$a}}' kurzus teljesítve.", - "addon.competency.evidence_coursemodulecompleted": "'{{$a}}' tevékenység teljesítve.", - "addon.competency.evidence_courserestored": "A besorolás a(z) '{{$a}}' kurzussal együtt helyreállt..", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Előtanulmányok '{{$a}}' bizonyítéka összekapcsolva.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Előtanulmányok '{{$a}}' bizonyítéka szétválasztva.", - "addon.competency.evidence_manualoverride": "Készségbesorolás kézzel beállítva.", - "addon.competency.evidence_manualoverrideincourse": "Készségbesorolás '{{$a}}' kurzusban kézzel beállítva.", - "addon.competency.evidence_manualoverrideinplan": "Készségbesorolás '{{$a}}' tanulási tervben kézzel beállítva.", - "addon.competency.learningplancompetencies": "Tanulási tervhez tartozó készségek", - "addon.competency.learningplans": "Tanulási tervek", - "addon.competency.myplans": "Tanulási terveim", - "addon.competency.noactivities": "Nincs tevékenység", - "addon.competency.nocompetenciesincourse": "A kurzushoz nem kapcsolódnak készségek.", - "addon.competency.nocrossreferencedcompetencies": "A készséghez kereszthivatkozással nem kapcsolódik más készség.", - "addon.competency.noevidence": "Nincs bizonyíték", - "addon.competency.noplanswerecreated": "Nem jött létre tanulási terv.", - "addon.competency.nouserplanswithcompetency": "Ezt a készséget egyik tanulási terv sem tartalmazza.", - "addon.competency.path": "Útvonal:", - "addon.competency.planstatusactive": "Aktív", - "addon.competency.planstatuscomplete": "Kész", - "addon.competency.planstatusdraft": "Vázlat", - "addon.competency.planstatusinreview": "Ellenőrzés alatt", - "addon.competency.planstatuswaitingforreview": "Ellenőrzésre vár", - "addon.competency.proficient": "Sikeres", - "addon.competency.progress": "Előmenetel", - "addon.competency.rating": "Besorolás", - "addon.competency.reviewstatus": "Ellenőrzés állapota", - "addon.competency.status": "Állapot", - "addon.competency.template": "Tanulási tervsablon", - "addon.competency.uponcoursecompletion": "Kurzus teljesítésekor:", - "addon.competency.usercompetencystatus_idle": "Inaktív", - "addon.competency.usercompetencystatus_inreview": "Ellenőrzés alatt", - "addon.competency.usercompetencystatus_waitingforreview": "Ellenőrzésre vár", - "addon.competency.userplans": "Tanulási tervek", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} / {{$a.y}} készség eredményes", - "addon.competency.xcompetenciesproficientoutofyincourse": "Ön a kurzusban {{$a.y}} közül {{$a.x}} készség tekintetében eredményesnek bizonyul.", - "addon.coursecompletion.completecourse": "Kurzus teljesítése", - "addon.coursecompletion.completed": "Teljesítve", - "addon.coursecompletion.completiondate": "Teljesítés időpontja", - "addon.coursecompletion.completionmenuitem": "Teljesítés", - "addon.coursecompletion.coursecompletion": "Kurzus teljesítése", - "addon.coursecompletion.criteria": "Követelmények", - "addon.coursecompletion.criteriagroup": "Követelménycsoport", - "addon.coursecompletion.criteriarequiredall": "Az összes alábbi követelmény teljesítendő", - "addon.coursecompletion.criteriarequiredany": "Bármely alábbi követelmény teljesítendő", - "addon.coursecompletion.inprogress": "Folyamatban", - "addon.coursecompletion.manualselfcompletion": "Saját teljesítés kézzel", - "addon.coursecompletion.nottracked": "A kurzusban a teljesítését jelenleg nem követjük nyomon", - "addon.coursecompletion.notyetstarted": "Még nem kezdődött el", - "addon.coursecompletion.pending": "Függőben", - "addon.coursecompletion.required": "Kitöltendő", - "addon.coursecompletion.requiredcriteria": "Előírt követelmények", - "addon.coursecompletion.requirement": "Követelmény", - "addon.coursecompletion.status": "Állapot", - "addon.coursecompletion.viewcoursereport": "Kurzusjelentés megtekintése", - "addon.files.files": "Állományok", - "addon.files.privatefiles": "Saját állományaim", - "addon.files.sitefiles": "Portál állományai", - "addon.messages.acceptandaddcontact": "Elfogadás és hozzáadás a kapcsolatokhoz", - "addon.messages.addcontact": "Kapcsolat hozzáadása", - "addon.messages.addcontactconfirm": "Biztosan hozzáadja {{$a}}-t a kapcsolataihoz", - "addon.messages.addtofavourites": "Beszélgetés megjelölése", - "addon.messages.addtoyourcontacts": "Felvétel a kapcsolatok közé", - "addon.messages.blocknoncontacts": "Ismeretlenek üzeneteit ne kapjam meg", - "addon.messages.blockuser": "Felhasználó kizárása", - "addon.messages.blockuserconfirm": "Biztosan kizárja {{$a}}-t?", - "addon.messages.contactableprivacy": "Üzenet fogadhatók innen:", - "addon.messages.contactableprivacy_coursemember": "Kapcsolataim és bárki a kurzusomból", - "addon.messages.contactableprivacy_onlycontacts": "Csak a kapcsolataim", - "addon.messages.contactableprivacy_site": "A portálon bárki", - "addon.messages.contactblocked": "Kapcsolat zárolva", - "addon.messages.contactrequestsent": "Kapcsolatfelvételi kérés elküldve", - "addon.messages.contacts": "Kapcsolatok", - "addon.messages.conversationactions": "Beszélgetéssel kapcsolatos lépések menüje", - "addon.messages.decline": "Elutasítás", - "addon.messages.deleteallconfirm": "Biztosan törli az egész beszélgetést? Ezzel egyéb résztvevők részére nem törli a beszélgetésből.", - "addon.messages.deleteallselfconfirm": "Biztosan törli a teljes személyes beszélgetést?", - "addon.messages.deleteconversation": "Beszélgetés törlése", - "addon.messages.deleteforeveryone": "Törlés mind a nevemben, mind mások nevében", - "addon.messages.groupconversations": "Csoport", - "addon.messages.groupinfo": "Csoportinfó", - "addon.messages.individualconversations": "Magán", - "addon.messages.info": "Felhasználói infó", - "addon.messages.isnotinyourcontacts": "{{$a}} nem szerepel a kapcsolatai között", - "addon.messages.message": "Üzenet", - "addon.messages.messagepreferences": "Üzenet beállításai", - "addon.messages.messages": "Üzenetek", - "addon.messages.muteconversation": "Elnémítás", - "addon.messages.mutedconversation": "Elnémított beszélgetés", - "addon.messages.newmessage": "Új üzenet", - "addon.messages.nocontactrequests": "Nincs kapcsolatkérés", - "addon.messages.nocontactsgetstarted": "Nincs kapcsolata", - "addon.messages.nofavourites": "Nincsenek megjelölt beszélgetések", - "addon.messages.nogroupconversations": "Nincsenek csoportbeszélgetések", - "addon.messages.noindividualconversations": "Nincsenek magánbeszélgetések", - "addon.messages.nomessagesfound": "Nem találtam üzeneteket", - "addon.messages.noncontacts": "Nem kapcsolat", - "addon.messages.numparticipants": "{{$a}} résztvevő", - "addon.messages.removecontact": "Kapcsolat törlése", - "addon.messages.removecontactconfirm": "Biztosan eltávolítja {{$a}}-t a kapcsolatai közül?", - "addon.messages.removefromfavourites": "Beszélgetés megjelölésének törlése", - "addon.messages.removefromyourcontacts": "Törlés a kapcsolatok közül", - "addon.messages.requests": "Kérések", - "addon.messages.requirecontacttomessage": "Az üzenetküldéshez kérje meg {{$a}}-t, hogy vegye fel Önt a kapcsolatok közé.", - "addon.messages.searchcombined": "Emberek vagy üzenetek keresése", - "addon.messages.selfconversation": "Személyes tér", - "addon.messages.selfconversationdefaultmessage": "Piszkozatok, ugrópontok, jegyzetek stb. mentése későbbi hozzáféréshez.", - "addon.messages.sendcontactrequest": "Kapcsolatkérés küldése", - "addon.messages.unabletomessage": "Ennek a felhasználónak nem küldhet üzenetet", - "addon.messages.unblockuser": "Felhasználó kizárásának feloldása", - "addon.messages.unblockuserconfirm": "Biztosan feloldja {{$a}} kizárását?", - "addon.messages.unmuteconversation": "Elnémítás feloldása", - "addon.messages.useentertosend": "Küldéshez használja az Entert", - "addon.messages.userwouldliketocontactyou": "{{$a}} szeretne kapcsolatba lépni Önnel", - "addon.messages.wouldliketocontactyou": "Szeretnék kapcsolatba lépni Önnel", - "addon.messages.you": "Ön:", - "addon.messages.youhaveblockeduser": "Kizárta ezt a felhasználót", - "addon.messages.yourcontactrequestpending": "Kapcsolatkérése {{$a}} esetén függőben", - "addon.mod_assign.addattempt": "Újabb próbálkozás engedélyezése", - "addon.mod_assign.addnewattempt": "Új próbálkozás hozzáadása", - "addon.mod_assign.addnewattemptfromprevious": "Új próbálkozás hozzáadása az előző leadás alapján", - "addon.mod_assign.addsubmission": "Leadott munka hozzáadása", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "A feladat részletei és a leadási űrlap {{$a}} időponttól lesznek elérhetők.", - "addon.mod_assign.allowsubmissionsfromdate": "Leadások engedélyezése ekkortól", - "addon.mod_assign.allowsubmissionsfromdatesummary": "A feladathoz {{$a}} időponttól lehet leadni munkát.", - "addon.mod_assign.applytoteam": "Osztályzatok és visszajelzés az egész csoportra kiterjesztve", - "addon.mod_assign.assignmentisdue": "A feladat teljesítendő", - "addon.mod_assign.attemptnumber": "Próbálkozás száma", - "addon.mod_assign.attemptreopenmethod": "Újból megnyitott próbálkozások", - "addon.mod_assign.attemptreopenmethod_manual": "Kézileg", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatikusan a teljesítésig", - "addon.mod_assign.attemptsettings": "Próbálkozás beállításai", - "addon.mod_assign.confirmsubmission": "Biztosan leadja munkáját osztályozásra? További módosításokra nem lesz lehetősége.", - "addon.mod_assign.currentattempt": "Ez a(z) {{$a}}. próbálkozás", - "addon.mod_assign.currentattemptof": "Ez a lehetséges {{$a.maxattempts}} közül a(z) {{$a.attemptnumber}}. próbálkozás", - "addon.mod_assign.currentgrade": "Aktuális osztályzat az osztályozónaplóban", - "addon.mod_assign.cutoffdate": "Lezárás időpontja", - "addon.mod_assign.defaultteam": "Alapcsoport", - "addon.mod_assign.duedate": "Határidő", - "addon.mod_assign.duedateno": "Nincs határidő", - "addon.mod_assign.duedatereached": "A feladat teljesítésének határideje lejárt.", - "addon.mod_assign.editingstatus": "Szerkesztés állapota", - "addon.mod_assign.editsubmission": "Leadott munkám szerkesztése", - "addon.mod_assign.extensionduedate": "Meghosszabbítás lejárata", - "addon.mod_assign.grade": "Pont", - "addon.mod_assign.graded": "Pontozott", - "addon.mod_assign.gradedby": "Osztályozta", - "addon.mod_assign.gradedfollowupsubmit": "Osztályozott - nyomon követő leadott munka átvéve", - "addon.mod_assign.gradedon": "Osztályozás időpontja", - "addon.mod_assign.gradelocked": "Ez az osztályzat zárolva van vagy felülírták az osztályozónaplóban", - "addon.mod_assign.gradeoutof": "{{$a}} pontból", - "addon.mod_assign.gradingstatus": "Osztályozás állapota", - "addon.mod_assign.groupsubmissionsettings": "Csoportos leadás beállításai", - "addon.mod_assign.hiddenuser": "Résztvevő", - "addon.mod_assign.latesubmissions": "Későn leadott munkák", - "addon.mod_assign.latesubmissionsaccepted": "Csak {{$a}} időpontig engedélyezett", - "addon.mod_assign.markingworkflowstate": "Értékelési folyamat állapota", - "addon.mod_assign.markingworkflowstateinmarking": "Értékelés alatt", - "addon.mod_assign.markingworkflowstateinreview": "Ellenőrzés alatt", - "addon.mod_assign.markingworkflowstatenotmarked": "Nincs értékelve", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Közzétételre kész", - "addon.mod_assign.markingworkflowstatereadyforreview": "Az értékelés véget ért.", - "addon.mod_assign.markingworkflowstatereleased": "Közzétéve", - "addon.mod_assign.modulenameplural": "Feladatok", - "addon.mod_assign.multipleteams": "Több csoportnak is tagja", - "addon.mod_assign.multipleteams_desc": "A feladat csoportos leadott munkát ír elő. Ön egynél több csoport tagja. Leadáshoz csak egy csoportnak lehet a tagja. Csoporttagságának módosításáért forduljon tanárához.", - "addon.mod_assign.noattempt": "Nincs próbálkozás", - "addon.mod_assign.nomoresubmissionsaccepted": "Csak a halasztást kapók számára engedélyezett", - "addon.mod_assign.noonlinesubmissions": "Ehhez a feladathoz semmit nem kell neten keresztül leadnia.", - "addon.mod_assign.nosubmission": "A feladathoz nem érkezett leadott munka.", - "addon.mod_assign.noteam": "Ön egyik csoportnak sem tagja", - "addon.mod_assign.noteam_desc": "A feladat csoportos leadott munkát ír elő. Ön nem tagja egy csoportnak sem, így nem hozhat létre leadott munkát. Csoport felvételéért forduljon tanárához.", - "addon.mod_assign.notgraded": "Nincs osztályozva", - "addon.mod_assign.numberofdraftsubmissions": "Piszkozatok", - "addon.mod_assign.numberofparticipants": "Résztvevők", - "addon.mod_assign.numberofsubmissionsneedgrading": "Osztályozandó", - "addon.mod_assign.numberofsubmittedassignments": "Leadva", - "addon.mod_assign.numberofteams": "Csoportok", - "addon.mod_assign.numwords": "{{$a}} szó", - "addon.mod_assign.outof": "{{$a.current}} / {{$a.total}}", - "addon.mod_assign.overdue": "A feladat lejárt: {{$a}}", - "addon.mod_assign.submission": "Leadott munka", - "addon.mod_assign.submissioneditable": "A tanuló a leadandót szerkesztheti.", - "addon.mod_assign.submissionnoteditable": "A tanuló a leadandót nem szerkesztheti.", - "addon.mod_assign.submissionslocked": "A feladathoz nem lehet munkát leadni.", - "addon.mod_assign.submissionstatus": "Leadás állapota", - "addon.mod_assign.submissionstatus_": "Nincs leadott munka", - "addon.mod_assign.submissionstatus_draft": "Piszkozat (nincs leadva)", - "addon.mod_assign.submissionstatus_marked": "Osztályozott", - "addon.mod_assign.submissionstatus_new": "Nincs leadott munka", - "addon.mod_assign.submissionstatus_reopened": "Újból megnyitva", - "addon.mod_assign.submissionstatus_submitted": "Osztályozásra leadva", - "addon.mod_assign.submissionstatusheading": "Leadás állapota", - "addon.mod_assign.submissionteam": "Csoport", - "addon.mod_assign.submitassignment": "Feladat leadása", - "addon.mod_assign.submitassignment_help": "A feladat leadás után nem módosítható.", - "addon.mod_assign.submittedearly": "Feladat {{$a}} idővel korábban leadva", - "addon.mod_assign.submittedlate": "Feladat {{$a}} idővel később leadva", - "addon.mod_assign.timemodified": "Utolsó módosítás", - "addon.mod_assign.timeremaining": "Hátralévő idő", - "addon.mod_assign.ungroupedusers": "A 'Csoport számára leadás előírása' be van kapcsolva, de egyes felhasználók nincsenek csoporthoz - vagy több csoporthoz vannak - hozzárendelve, így nem adhatnak le munkát.", - "addon.mod_assign.ungroupedusersoptional": "Be van kapcsolva a csoportos tanulói leadás, egyes felhasználók azonban vagy nem csoporttagok, vagy egynél több csoport tagjai. Ezek a tanulók az Alapcsoport tagjaiként fogják leadni munkájukat!", - "addon.mod_assign.unlimitedattempts": "Korlátlan", - "addon.mod_assign.userswhoneedtosubmit": "Leadásra kötelezett tanulók: {{$a}}", - "addon.mod_assign.viewsubmission": "Leadott munka megtekintése", - "addon.mod_assign.wordlimit": "Szókorlát", - "addon.mod_assign_feedback_comments.pluginname": "Visszajelző megjegyzések", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF-hez széljegyzet készítése", - "addon.mod_assign_feedback_file.pluginname": "Visszajelzés állományban", - "addon.mod_assign_submission_comments.pluginname": "Leadáshoz fűzött megjegyzések", - "addon.mod_assign_submission_file.pluginname": "Leadás állományban", - "addon.mod_assign_submission_onlinetext.pluginname": "Online szövegben leadott munkák", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "A feladathoz tartozó szókorlát {{$a.limit}} szó, Ön pedig {{$a.count}} szót kíván leadni. Vizsgálja felül leadandó munkáját, majd próbálkozzék újból.", - "addon.mod_book.errorchapter": "Hiba a fejezet olvasása közben.", - "addon.mod_book.modulenameplural": "Könyvek", - "addon.mod_book.navnexttitle": "Következő: {{$a}}", - "addon.mod_book.navprevtitle": "Előző: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Könyvfejezetek", - "addon.mod_book.toc": "Tartalomjegyzék", - "addon.mod_chat.beep": "Hangjelzés", - "addon.mod_chat.chatreport": "Csevegések", - "addon.mod_chat.currentusers": "Mostani felhasználók", - "addon.mod_chat.enterchat": "Kattintson ide a csevegésbe való bekapcsolódáshoz", - "addon.mod_chat.entermessage": "Írja be az üzenetet", - "addon.mod_chat.messagebeepseveryone": "{{$a}} mindenkit csönget!", - "addon.mod_chat.messagebeepsyou": "{{$a}} most csöngetett Önnek!", - "addon.mod_chat.messageenter": "{{$a}} most lépett be a csevegésbe", - "addon.mod_chat.messageexit": "{{$a}} most lépett ki a csevegésből", - "addon.mod_chat.messages": "Üzenetek", - "addon.mod_chat.messageyoubeep": "{{$a}} csengetett", - "addon.mod_chat.modulenameplural": "Csevegések", - "addon.mod_chat.nomessages": "Még nincs üzenet", - "addon.mod_chat.saidto": "mondta", - "addon.mod_chat.send": "Elküld", - "addon.mod_chat.sessionstart": "A következő csevegés kezdete {{$a.date}}, (mostantól {{$a.fromnow}})", - "addon.mod_chat.talk": "Beszéljen", - "addon.mod_chat.viewreport": "Korábbi csevegések megtekintése", - "addon.mod_choice.cannotsubmit": "Választásának leadásával gondok vannak. Próbálja meg később.", - "addon.mod_choice.choiceoptions": "Választási lehetőségek", - "addon.mod_choice.expired": "A tevékenység {{$a}} időpontban lezárult.", - "addon.mod_choice.full": "(Betelt)", - "addon.mod_choice.modulenameplural": "Válaszlehetőségek", - "addon.mod_choice.noresultsviewable": "Az eredmények jelenleg nem tekinthetők meg.", - "addon.mod_choice.notopenyet": "A tevékenység {{$a}} időpontig nem elérhető", - "addon.mod_choice.numberofuser": "Válaszok száma", - "addon.mod_choice.numberofuserinpercentage": "Válaszok száma százalékban", - "addon.mod_choice.previewonly": "Ez a tevékenység lehetőségeinek előnézete. A választását {{$a}} időpontig nem közölheti.", - "addon.mod_choice.publishinfoanonafter": "A névtelen eredmények válasza után jelennek meg.", - "addon.mod_choice.publishinfoanonclose": "A névtelen eredmények a tevékenység lezárása után jelennek meg.", - "addon.mod_choice.publishinfofullafter": "A mindenki választását mutató eredmények a válasza után jelennek meg.", - "addon.mod_choice.publishinfofullclose": "A mindenki választását mutató eredmények a válasza után jelennek meg.", - "addon.mod_choice.publishinfonever": "A tevékenység eredményei a válasza után nem jelennek meg.", - "addon.mod_choice.removemychoice": "Válaszlehetőségem törlése", - "addon.mod_choice.responses": "Tanuló által adott válaszok", - "addon.mod_choice.responsesresultgraphheader": "Gráfmegjelenítés", - "addon.mod_choice.savemychoice": "Válaszlehetőségem mentése", - "addon.mod_choice.userchoosethisoption": "Ezt választó felhasználók", - "addon.mod_choice.yourselection": "Az Ön választása", - "addon.mod_data.addentries": "Bejegyzések hozzáadása", - "addon.mod_data.advancedsearch": "Részletes keresés", - "addon.mod_data.alttext": "Alternatív szöveg", - "addon.mod_data.approve": "Jóváhagy", - "addon.mod_data.approved": "Jóváhagyva", - "addon.mod_data.ascending": "Növekvő", - "addon.mod_data.authorfirstname": "Szerző keresztneve", - "addon.mod_data.authorlastname": "Szerző vezetékneve", - "addon.mod_data.confirmdeleterecord": "Biztosan törölni akarja ezt a bejegyzést?", - "addon.mod_data.descending": "Csökkenő", - "addon.mod_data.disapprove": "Jóváhagyás elvetése", - "addon.mod_data.emptyaddform": "Nem töltött ki egy mezőt sem!", - "addon.mod_data.entrieslefttoadd": "A tevékenység befejezéséhez {{$a.entriesleft}} további tételt kell hozzáadnia.", - "addon.mod_data.entrieslefttoaddtoview": "Még {{$a.entrieslefttoview}} bejegyzést kell hozzáadnia, mielőtt megtekintheti más résztvevők bejegyzéseit.", - "addon.mod_data.errormustsupplyvalue": "Itt egy értéket kell megadnia.", - "addon.mod_data.expired": "A tevékenység {{$a}} időpontban lezárult és már nem érhető el.", - "addon.mod_data.fields": "Mezők", - "addon.mod_data.foundrecords": "Vannak rekordok: {{$a.num}}/{{$a.max}} (Szűrők visszaállítása)", - "addon.mod_data.latlongboth": "Mind a szélesség, mind a hosszúság kitöltendő.", - "addon.mod_data.menuchoose": "Választás...", - "addon.mod_data.modulenameplural": "Adatbázisok", - "addon.mod_data.more": "Tovább", - "addon.mod_data.nomatch": "Nincs egyező fogalom!", - "addon.mod_data.norecords": "Nincsenek bejegyzések az adatbázisban", - "addon.mod_data.notapproved": "A bejegyzés még nincs jóváhagyva.", - "addon.mod_data.notopenyet": "A tevékenység {{$a}} időpontig nem elérhető.", - "addon.mod_data.numrecords": "{{$a}} bejegyzés", - "addon.mod_data.other": "Más", - "addon.mod_data.recordapproved": "Bejegyzés jóváhagyva", - "addon.mod_data.recorddeleted": "Bejegyzés törölve", - "addon.mod_data.recorddisapproved": "Nem jóváhagyott fogalom", - "addon.mod_data.resetsettings": "Szűrők visszaállítása", - "addon.mod_data.search": "Keresés", - "addon.mod_data.selectedrequired": "Minden kiválasztott szükséges", - "addon.mod_data.single": "Egyetlen megtekintése", - "addon.mod_data.tagarea_data_records": "Adatrekordok", - "addon.mod_data.timeadded": "Időpont hozzáadva", - "addon.mod_data.timemodified": "Időpont módosult", - "addon.mod_data.usedate": "Keresésben szerepeljen.", - "addon.mod_feedback.analysis": "Elemzés", - "addon.mod_feedback.anonymous": "Névtelen", - "addon.mod_feedback.anonymous_entries": "Névtelen bejegyzések ({{$a}})", - "addon.mod_feedback.average": "Átlag", - "addon.mod_feedback.complete_the_form": "Válaszoljon a kérdésekre", - "addon.mod_feedback.completed_feedbacks": "Leadott válaszok", - "addon.mod_feedback.continue_the_form": "Kérdések megválaszolásának folytatása", - "addon.mod_feedback.feedback_is_not_open": "A visszajelzés nincs nyitva", - "addon.mod_feedback.feedbackclose": "Válaszok engedélyezése a címzettnek", - "addon.mod_feedback.feedbackopen": "Válaszok engedélyezése a feladótól", - "addon.mod_feedback.mapcourses": "A visszajelzés illesztése kurzusokhoz.", - "addon.mod_feedback.maximal": "Maximum", - "addon.mod_feedback.minimal": "Minimum", - "addon.mod_feedback.mode": "Mód", - "addon.mod_feedback.modulenameplural": "Visszajelzések", - "addon.mod_feedback.next_page": "Következő oldal", - "addon.mod_feedback.non_anonymous": "A felhasználó nevét a rendszer naplózza és a válaszok mellett megjeleníti", - "addon.mod_feedback.non_anonymous_entries": "Nem névtelen bejegyzések ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Nem válaszoló tanulók ({{$a}})", - "addon.mod_feedback.not_selected": "Nincs kiválasztva", - "addon.mod_feedback.not_started": "Nem kezdődött el", - "addon.mod_feedback.numberoutofrange": "Tartományon kívül eső szám", - "addon.mod_feedback.overview": "Áttekintés", - "addon.mod_feedback.page_after_submit": "Teljesítéssel kapcsolatos üzenet", - "addon.mod_feedback.preview": "Előzetes megtekintés", - "addon.mod_feedback.previous_page": "Előző oldal", - "addon.mod_feedback.questions": "Kérdések", - "addon.mod_feedback.response_nr": "Válasz száma:", - "addon.mod_feedback.responses": "Tanuló válaszai", - "addon.mod_feedback.save_entries": "Válaszainak leadása", - "addon.mod_feedback.show_entries": "Tanuló válaszainak megjelenítése", - "addon.mod_feedback.show_nonrespondents": "Nem válaszolók megjelenítése", - "addon.mod_feedback.started": "Elkezdődött", - "addon.mod_feedback.this_feedback_is_already_submitted": "Ezt a tevékenységet már befejezte.", - "addon.mod_folder.modulenameplural": "Mappák", - "addon.mod_forum.addanewdiscussion": "Új vitatéma hozzáadása", - "addon.mod_forum.addanewquestion": "Új kérdés hozzáadása", - "addon.mod_forum.addanewtopic": "Új téma hozzáadása", - "addon.mod_forum.addtofavourites": "Megbeszélés megjelölése", - "addon.mod_forum.advanced": "Részletes", - "addon.mod_forum.cannotadddiscussion": "Ahhoz, hogy hozzáadhasson vitát, ezen a fórumon csoporttagságra van szükség.", - "addon.mod_forum.cannotadddiscussionall": "Ön nem adhat hozzá új vitatémát az összes résztvevő számára.", - "addon.mod_forum.cannotcreatediscussion": "Nem sikerült új vitát létrehozni.", - "addon.mod_forum.couldnotadd": "Ismeretlen hiba miatt a hozzászólást nem lehetett hozzáadni", - "addon.mod_forum.couldnotupdate": "Ismeretlen hiba miatt a hozzászólását nem lehetett frissíteni", - "addon.mod_forum.cutoffdatereached": "A fórumüzenetek beküldésének határideje lejárt, ezért ide már nem küldhet üzenetet.", - "addon.mod_forum.delete": "Törlés", - "addon.mod_forum.deletedpost": "A hozzászólást törölték", - "addon.mod_forum.deletesure": "Biztosan törölni akarja a hozzászólást?", - "addon.mod_forum.discussion": "Vita", - "addon.mod_forum.discussionlistsortbycreatedasc": "Rendezés növekvő sorrendben a létrehozási idő alapján", - "addon.mod_forum.discussionlistsortbycreateddesc": "Rendezés csökkenő sorrendben a létrehozási idő alapján", - "addon.mod_forum.discussionlistsortbylastpostasc": "Rendezés növekvő sorrendben az utolsó bejegyzés létrehozási ideje alapján", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Rendezés csökkenő sorrendben az utolsó bejegyzés létrehozási ideje alapján", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Rendezés növekvő sorrendben a válaszok száma alapján", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Rendezés csökkenő sorrendben a válaszok száma alapján", - "addon.mod_forum.discussionlocked": "A vitát lezárták, már nem szólhat hozzá.", - "addon.mod_forum.discussionpinned": "Megjelölt", - "addon.mod_forum.discussionsubscription": "Megbeszélésre való feliratkozás", - "addon.mod_forum.edit": "Szerkesztés", - "addon.mod_forum.erroremptymessage": "A hozzászólás szövege nem lehet üres.", - "addon.mod_forum.erroremptysubject": "A hozzászólás tárgya nem lehet üres.", - "addon.mod_forum.favouriteupdated": "Megjelölését frissítettük.", - "addon.mod_forum.lastpost": "Utolsó hozzászólás", - "addon.mod_forum.lockdiscussion": "Megbeszélés zárolása", - "addon.mod_forum.lockupdated": "Zárolását frissítettük.", - "addon.mod_forum.message": "Üzenet", - "addon.mod_forum.modeflatnewestfirst": "Válaszok egymás után, a legújabbal kezdve", - "addon.mod_forum.modeflatoldestfirst": "Válaszok egymás után, a legrégebbivel kezdve", - "addon.mod_forum.modenested": "Válaszok beágyazott formában", - "addon.mod_forum.modulenameplural": "Fórumok", - "addon.mod_forum.pindiscussion": "Megbeszélés fölülre helyezése", - "addon.mod_forum.pinupdated": "Fölülre helyezését frissítettük.", - "addon.mod_forum.postisprivatereply": "A hozzászólás magánjellegű volt, mások nem láthatják.", - "addon.mod_forum.posttoforum": "Hozzászólás a fórumhoz", - "addon.mod_forum.posttomygroups": "Minden csoporthoz másolat küldése", - "addon.mod_forum.privatereply": "Magánjellegű válaszadás", - "addon.mod_forum.re": "Tárgy:", - "addon.mod_forum.removefromfavourites": "Megbeszélés megjelölésének törlése", - "addon.mod_forum.reply": "Válasz", - "addon.mod_forum.replyplaceholder": "Írja meg válaszát...", - "addon.mod_forum.subject": "Téma", - "addon.mod_forum.tagarea_forum_posts": "Fórumüzenetek", - "addon.mod_forum.thisforumhasduedate": "Üzenet fórumra beküldésének esedékességi határideje {{$a}}.", - "addon.mod_forum.thisforumisdue": "Üzenet fórumra beküldésének esedékességi határideje {{$a}} volt.", - "addon.mod_forum.unlockdiscussion": "Megbeszélés feloldása", - "addon.mod_forum.unpindiscussion": "Megbeszélés fölülre helyezésének törlése", - "addon.mod_forum.unread": "Elolvasatlan", - "addon.mod_forum.unreadpostsnumber": "{{$a}} elolvasatlan hozzászólás", - "addon.mod_forum.yourreply": "Az Ön válasza", - "addon.mod_glossary.addentry": "Új fogalom hozzáadása", - "addon.mod_glossary.aliases": "Kulcsszó(-szavak)", - "addon.mod_glossary.attachment": "Csatolt állomány", - "addon.mod_glossary.casesensitive": "A kis- és nagybetűk különböznek", - "addon.mod_glossary.categories": "Kategóriák", - "addon.mod_glossary.concept": "Fogalom", - "addon.mod_glossary.definition": "Meghatározás", - "addon.mod_glossary.entryusedynalink": "A fogalom automatikusan kapcsolódjon", - "addon.mod_glossary.errconceptalreadyexists": "A fogalom már létezik. A fogalomtárban nem szerepelhetnek ismétlődések.", - "addon.mod_glossary.fillfields": "A fogalom és a meghatározás kötelezően kitöltendő.", - "addon.mod_glossary.fullmatch": "Csak egész szavak egyezése", - "addon.mod_glossary.linking": "Automatikus kapcsolás", - "addon.mod_glossary.modulenameplural": "Fogalomtárak", - "addon.mod_glossary.tagarea_glossary_entries": "Szójegyzék egységei", - "addon.mod_h5pactivity.all_attempts": "Minden felhasználói próbálkozás", - "addon.mod_h5pactivity.answer_checked": "A válasz ellenőrizve", - "addon.mod_h5pactivity.answer_correct": "A válasza helyes", - "addon.mod_h5pactivity.answer_fail": "Hibás válasz", - "addon.mod_h5pactivity.answer_incorrect": "A válasza hibás", - "addon.mod_h5pactivity.answer_pass": "Helyes válasz", - "addon.mod_h5pactivity.attempt": "Próbálkozás", - "addon.mod_h5pactivity.attempt_completion_no": "A próbálkozás nincs befejezettként megjelölve", - "addon.mod_h5pactivity.attempt_completion_yes": "A próbálkozás kész", - "addon.mod_h5pactivity.attempt_success_fail": "Sikertelen", - "addon.mod_h5pactivity.attempt_success_pass": "Sikeres", - "addon.mod_h5pactivity.attempt_success_unknown": "Nincs jelentés", - "addon.mod_h5pactivity.attempts_none": "Ennek a felhasználónak nincs megjelenítendő próbálkozása.", - "addon.mod_h5pactivity.completion": "Teljesítés", - "addon.mod_h5pactivity.duration": "Időtartam", - "addon.mod_h5pactivity.maxscore": "Max. pontszám", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Próbálkozásaim", - "addon.mod_h5pactivity.no_compatible_track": "Ez a művelet ({$ a}}) nem ad nyomkövetési információt, vagy a nyomon követés nem kompatibilis a jelenlegi tevékenységi verzióval.", - "addon.mod_h5pactivity.outcome": "Eredmény", - "addon.mod_h5pactivity.previewmode": "A tartalom előnézetben jelenik meg. A próbálkozások követésére nem kerül sor.", - "addon.mod_h5pactivity.result_fill-in": "Kitöltendő szöveg", - "addon.mod_h5pactivity.result_other": "Ismeretlen művelettípus", - "addon.mod_h5pactivity.review_my_attempts": "Próbálkozásaim megtekintése", - "addon.mod_h5pactivity.score": "Pontszám", - "addon.mod_h5pactivity.score_out_of": "{{$a.maxscore}} / {{$a.rawscore}} pont", - "addon.mod_h5pactivity.startdate": "Kezdési dátum", - "addon.mod_h5pactivity.totalscore": "Összesített pontszám", - "addon.mod_imscp.deploymenterror": "Hiba a tartalomcsomagban!", - "addon.mod_imscp.modulenameplural": "IMS tartalomcsomagok", - "addon.mod_imscp.toc": "Tartalomjegyzék", - "addon.mod_lesson.answer": "Válasz", - "addon.mod_lesson.attempt": "Próbálkozás: {{$a}}", - "addon.mod_lesson.attemptheader": "Próbálkozás", - "addon.mod_lesson.attemptsremaining": "{{$a}} próbálkozása maradt", - "addon.mod_lesson.averagescore": "Átlagpontszám", - "addon.mod_lesson.averagetime": "Átlagidő", - "addon.mod_lesson.branchtable": "Tartalom", - "addon.mod_lesson.cannotfindattempt": "Hiba: nem található a próbálkozás", - "addon.mod_lesson.cannotfinduser": "Hiba: nem találhatók felhasználók", - "addon.mod_lesson.clusterjump": "Kihagyott kérdés egy tömbben", - "addon.mod_lesson.completed": "Kész", - "addon.mod_lesson.congratulations": "Gratulálunk - elérte a lecke végét!", - "addon.mod_lesson.continue": "Tovább", - "addon.mod_lesson.continuetonextpage": "Tovább a következő oldalra", - "addon.mod_lesson.defaultessayresponse": "Az esszét tanára fogja pontozni.", - "addon.mod_lesson.detailedstats": "Részletes statisztika", - "addon.mod_lesson.didnotanswerquestion": "Nem válaszolt erre a kérdésre", - "addon.mod_lesson.displayofgrade": "Pont kijelzése (csak tanulóknak)", - "addon.mod_lesson.displayscorewithessays": "Az automatikus pontozású kérdéseknél {{$a.score}} pontot kapott a maximális {{$a.tempmaxgrade}} pontból.

                Az {{$a.essayquestions}} esszékérdés(é/ei)t később pontozzák és adják hozzá végső pontszámához.

                Az esszékérdés(ek) nélküli jelenlegi pontszáma {{$a.score}} a(z) {{$a.grade}} pontból", - "addon.mod_lesson.displayscorewithoutessays": "Pontszáma {{$a.score}} ({{$a.grade}} pontból).", - "addon.mod_lesson.emptypassword": "A jelszó nem lehet üres", - "addon.mod_lesson.enterpassword": "Adja meg a jelszót:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Nem válaszolt egyetlen kérdésre sem. A leckére 0 pontot kapott.", - "addon.mod_lesson.finish": "Kész", - "addon.mod_lesson.firstwrong": "Hibásan válaszolt. Azért a tanulás kedvéért folytatná még a próbálkozást (de már pontszerzés nélkül)?", - "addon.mod_lesson.gotoendoflesson": "Tovább a lecke végére", - "addon.mod_lesson.grade": "Pont", - "addon.mod_lesson.highscore": "Magas pontszám", - "addon.mod_lesson.hightime": "Leghosszabb idő", - "addon.mod_lesson.leftduringtimed": "Időméréses lecke közben távozott.
                Kattintson a Folytatás gombra, és kezdje újra a leckét.", - "addon.mod_lesson.leftduringtimednoretake": "Időméréses lecke közben távozott, ezért nem ismételheti meg és nem folytathatja a leckét.", - "addon.mod_lesson.lessonmenu": "Lecke menü", - "addon.mod_lesson.lessonstats": "Leckestatisztika", - "addon.mod_lesson.linkedmedia": "Kapcsolt média", - "addon.mod_lesson.loginfail": "Belépése nem sikerült, próbálja újra...", - "addon.mod_lesson.lowscore": "Alacsony pontszám", - "addon.mod_lesson.lowtime": "Legrövidebb idő", - "addon.mod_lesson.maximumnumberofattemptsreached": "A próbálkozások maximális számát elérte - áttérés a következő oldalra.", - "addon.mod_lesson.modattemptsnoteacher": "Önellenőrzést csak tanulók végezhetnek.", - "addon.mod_lesson.modulenameplural": "Leckék", - "addon.mod_lesson.noanswer": "Nincs megadva válasz. Lépjen vissza és adjon le egy választ.", - "addon.mod_lesson.nolessonattempts": "A leckével kapcsolatosan nem történt próbálkozás.", - "addon.mod_lesson.nolessonattemptsgroup": "{{$a}} csoporttag a leckével nem próbálkozott.", - "addon.mod_lesson.notcompleted": "Nincs befejezve", - "addon.mod_lesson.numberofcorrectanswers": "A helyes válaszok száma: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "A megtekintett oldalak száma: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Megválaszolt kérdések száma: {{$a.nquestions}}; (Megválaszolandó legalább: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "{{$a.score}} pontot ért el az eddig elérhető {{$a.currenthigh}} pontból.", - "addon.mod_lesson.ongoingnormal": "{{$a->correct}} kérdést válaszolt meg helyesen az eddigi {{$a->viewed}} próbálkozásból", - "addon.mod_lesson.or": "VAGY", - "addon.mod_lesson.overview": "Áttekintés", - "addon.mod_lesson.preview": "Előzetes megtekintés", - "addon.mod_lesson.progressbarteacherwarning2": "A folyamatjelzőt nem fogja látni, mert Ön szerkesztheti ezt a leckét.", - "addon.mod_lesson.progresscompleted": "A lecke {{$a}}%-át fejezte be.", - "addon.mod_lesson.question": "Kérdés", - "addon.mod_lesson.rawgrade": "Nyers pont", - "addon.mod_lesson.reports": "Jelentések", - "addon.mod_lesson.response": "Tanuló válasza", - "addon.mod_lesson.review": "Ellenőrzés", - "addon.mod_lesson.reviewlesson": "Összefoglaló lecke", - "addon.mod_lesson.reviewquestionback": "Igen, megpróbálnám", - "addon.mod_lesson.reviewquestioncontinue": "Nem, továbblépek a következő kérdésre", - "addon.mod_lesson.secondpluswrong": "Nem pontos. Próbálkozna újra?", - "addon.mod_lesson.submit": "Leadás", - "addon.mod_lesson.teacherjumpwarning": "A lecke {{$a.cluster}} ugrást vagy {{$a.unseen}} ugrást tartalmaz. Helyette a következő oldalra ugrik. Az ugrások ellenőrzéséhez lépjen be tanulóként.", - "addon.mod_lesson.teacherongoingwarning": "A pillanatnyi pontszám csak tanuló számára látható. Ellenőrzéséhez lépjen be tanulóként.", - "addon.mod_lesson.teachertimerwarning": "Az időmérő csak tanuló számára látható. Ellenőrzéséhez lépjen be tanulóként.", - "addon.mod_lesson.thatsthecorrectanswer": "Ez a helyes válasz", - "addon.mod_lesson.thatsthewronganswer": "Ez a hibás válasz", - "addon.mod_lesson.timeremaining": "Hátralévő idő", - "addon.mod_lesson.timetaken": "Felhasznált idő", - "addon.mod_lesson.unseenpageinbranch": "Kihagyott kérdés egy tartalomoldalon", - "addon.mod_lesson.welldone": "Nagyszerű!", - "addon.mod_lesson.youhaveseen": "Ebből a leckéből már egynél több oldalt látott.
                Az utolsó megtekintett oldalon kívánja folytatni?", - "addon.mod_lesson.youranswer": "Válasza", - "addon.mod_lesson.yourcurrentgradeisoutof": "Jelenlegi pontjainak száma {{$a.grade}} pont a {{$a.total}} pontból", - "addon.mod_lesson.youshouldview": "Minimálisan megválaszolandó: {{$a}}", - "addon.mod_lti.modulenameplural": "Külső eszközök", - "addon.mod_page.modulenameplural": "Oldalak", - "addon.mod_quiz.answercolon": "Válasz", - "addon.mod_quiz.attemptfirst": "Első próbálkozás", - "addon.mod_quiz.attemptlast": "Utolsó próbálkozás", - "addon.mod_quiz.attemptnumber": "Próbálkozás", - "addon.mod_quiz.attemptquiznow": "Teszt megoldása most", - "addon.mod_quiz.attemptstate": "Állapot", - "addon.mod_quiz.clearchoice": "Választásom törlése", - "addon.mod_quiz.comment": "Megjegyzés", - "addon.mod_quiz.completedon": "Befejezés dátuma", - "addon.mod_quiz.confirmclose": "A leadás után a próbálkozással kapcsolatos válaszait nem módosíthatja.", - "addon.mod_quiz.confirmstart": "A teszthez {{$a}} időkorlát kapcsolódik. Kezdéskor elindul a visszaszámlálás, amelyet nem szakíthat meg. A vége előtt le kell adnia a tesztet. Biztosan készen áll a kezdésre?", - "addon.mod_quiz.confirmstartheader": "Időkorlát", - "addon.mod_quiz.connectionerror": "A hálózati kapcsolat megszűnt (az automatikus mentés nem sikerült).\n\nJegyezze fel az oldalon megadott esetleges válaszokat, majd próbáljon meg újból kapcsolódni.\n\nHa sikerül. válaszainak mentése után ez az üzenet eltűnik.", - "addon.mod_quiz.continueattemptquiz": "Utolsó próbálkozás folytatása", - "addon.mod_quiz.continuepreview": "Utolsó megtekintés folytatása", - "addon.mod_quiz.feedback": "Visszajelzés", - "addon.mod_quiz.finishattemptdots": "Próbálkozás befejezése...", - "addon.mod_quiz.grade": "Pont", - "addon.mod_quiz.gradeaverage": "Átlagpont", - "addon.mod_quiz.gradehighest": "Legmagasabb pont", - "addon.mod_quiz.grademethod": "Pontozási módszer", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Pontok", - "addon.mod_quiz.modulenameplural": "Tesztek", - "addon.mod_quiz.mustbesubmittedby": "A próbálkozást {{$a}} időpontig le kell adni.", - "addon.mod_quiz.noquestions": "Nincs még hozzáadva kérdés", - "addon.mod_quiz.noreviewattempt": "Ezt a próbálkozást nem ellenőrizheti.", - "addon.mod_quiz.notyetgraded": "Még nincs lepontozva", - "addon.mod_quiz.outof": "{{$a.grade}} a maximum {{$a.maxgrade}} közül", - "addon.mod_quiz.outofpercent": "{{$a.grade}} a maximum {{$a.maxgrade}} közül ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Globális visszajelzés", - "addon.mod_quiz.overdue": "Lejárt", - "addon.mod_quiz.overduemustbesubmittedby": "A próbálkozását már le kellett volna adni. Ha a tesztet értékelni kívánja, {{$a}}-ig le kell adnia. Ha elmulasztja a próbálkozásra nem kap osztályzatot.", - "addon.mod_quiz.preview": "Előzetes megtekintés", - "addon.mod_quiz.previewquiznow": "Teszt előzetes megtekintése most", - "addon.mod_quiz.question": "Kérdés", - "addon.mod_quiz.quiznavigation": "Tesztnavigáció", - "addon.mod_quiz.quizpassword": "Teszt jelszava", - "addon.mod_quiz.reattemptquiz": "Újbóli próbálkozás a teszt megoldásával", - "addon.mod_quiz.requirepasswordmessage": "A teszt megoldásához ismernie kell a teszt jelszavát", - "addon.mod_quiz.returnattempt": "Vissza a próbálkozáshoz", - "addon.mod_quiz.review": "Ellenőrzés", - "addon.mod_quiz.reviewofattempt": "A(z) {{$a}} próbálkozás ellenőrzése", - "addon.mod_quiz.reviewofpreview": "Előzetes megtekintés ellenőrzése", - "addon.mod_quiz.showall": "Minden kérdés egy oldalon látsszon", - "addon.mod_quiz.showeachpage": "Egyszerre egy oldal megjelenítése", - "addon.mod_quiz.startattempt": "Próbálkozás indítása", - "addon.mod_quiz.startedon": "Kezdés ideje", - "addon.mod_quiz.stateabandoned": "Sohasem adott le munkát", - "addon.mod_quiz.statefinished": "Befejezte", - "addon.mod_quiz.statefinisheddetails": "Leadott: {{$a}}", - "addon.mod_quiz.stateinprogress": "Folyamatban", - "addon.mod_quiz.stateoverdue": "Lejárt", - "addon.mod_quiz.stateoverduedetails": "Leadanadó eddig: {{$a}}", - "addon.mod_quiz.status": "Állapot", - "addon.mod_quiz.submitallandfinish": "Az összes leadása és befejezés", - "addon.mod_quiz.summaryofattempt": "Próbálkozások összegzése", - "addon.mod_quiz.summaryofattempts": "Korábbi próbálkozásainak összegzése", - "addon.mod_quiz.timeleft": "Hátralévő idő", - "addon.mod_quiz.timetaken": "Felhasznált idő", - "addon.mod_quiz.yourfinalgradeis": "Végső pontja erre a tesztre {{$a}}", - "addon.mod_resource.modifieddate": "Módosítás dátuma: {{$a}}", - "addon.mod_resource.modulenameplural": "Tananyagok", - "addon.mod_resource.uploadeddate": "Feltöltés dátuma: {{$a}}", - "addon.mod_scorm.asset": "Tudáselem", - "addon.mod_scorm.assetlaunched": "Tudáselem - megtekintve", - "addon.mod_scorm.attempts": "Próbálkozások", - "addon.mod_scorm.averageattempt": "Próbálkozások átlaga", - "addon.mod_scorm.browse": "Előzetes megtekintés", - "addon.mod_scorm.browsed": "Böngészés megtörtént", - "addon.mod_scorm.browsemode": "Előzetes megtekintés üzemmódja", - "addon.mod_scorm.completed": "Kész", - "addon.mod_scorm.contents": "Tartalom", - "addon.mod_scorm.enter": "Belépés", - "addon.mod_scorm.exceededmaxattempts": "Elérte a próbálkozások maximális számát.", - "addon.mod_scorm.failed": "Nem sikerült", - "addon.mod_scorm.firstattempt": "Első próbálkozás", - "addon.mod_scorm.gradeaverage": "Átlagpont", - "addon.mod_scorm.gradeforattempt": "Próbálkozásra adott pont", - "addon.mod_scorm.gradehighest": "Legmagasabb pont", - "addon.mod_scorm.grademethod": "Pontozási módszer", - "addon.mod_scorm.gradereported": "Jelentett pontszám", - "addon.mod_scorm.gradescoes": "Tudásegységek", - "addon.mod_scorm.gradesum": "Összes pont", - "addon.mod_scorm.highestattempt": "Legjobb próbálkozás", - "addon.mod_scorm.incomplete": "Nem teljes", - "addon.mod_scorm.lastattempt": "Utolsó befejezett próbálkozás", - "addon.mod_scorm.modulenameplural": "SCORM/AICC csomagok", - "addon.mod_scorm.newattempt": "Új próbálkozás elkezdése", - "addon.mod_scorm.noattemptsallowed": "Engedélyezett próbálkozások száma", - "addon.mod_scorm.noattemptsmade": "Próbálkozásainak száma", - "addon.mod_scorm.notattempted": "Nem oldották meg", - "addon.mod_scorm.organizations": "Szervezetek", - "addon.mod_scorm.passed": "Sikerült", - "addon.mod_scorm.reviewmode": "Ellenőrző üzemmód", - "addon.mod_scorm.score": "Pontszám", - "addon.mod_scorm.suspended": "Felfüggesztve", - "addon.mod_scorm.toc": "Tartalomjegyzék", - "addon.mod_survey.ifoundthat": "Azt találtam", - "addon.mod_survey.ipreferthat": "Inkább ezt szeretném", - "addon.mod_survey.modulenameplural": "Felmérések", - "addon.mod_survey.responses": "Tanuló által adott válaszok", - "addon.mod_survey.surveycompletednograph": "A felmérést befejezte.", - "addon.mod_url.modulenameplural": "Webcímek", - "addon.mod_wiki.cannoteditpage": "Ezt az oldalt nem szerkesztheti.", - "addon.mod_wiki.createpage": "Oldal létrehozása", - "addon.mod_wiki.editingpage": "'{{$a}}' oldal szerkesztése", - "addon.mod_wiki.map": "Térkép", - "addon.mod_wiki.modulenameplural": "Wikik", - "addon.mod_wiki.newpagehdr": "Új oldal", - "addon.mod_wiki.newpagetitle": "Új oldalcím", - "addon.mod_wiki.nocontent": "Az oldalhoz nincs tartalom", - "addon.mod_wiki.notingroup": "Nem része a csoportnak", - "addon.mod_wiki.pageexists": "Az oldal már létezik.", - "addon.mod_wiki.pagename": "Oldal neve", - "addon.mod_wiki.tagarea_wiki_pages": "Wikioldalak", - "addon.mod_wiki.wrongversionlock": "A szerkesztés közben egy másik felhasználó szerkesztette ezt az oldalt, így az Ön tartalma elavult.", - "addon.mod_workshop.alreadygraded": "Már pontozták", - "addon.mod_workshop.areainstructauthors": "Leadási tudnivalók", - "addon.mod_workshop.areainstructreviewers": "Értékelési tudnivalók", - "addon.mod_workshop.assess": "Értékelés", - "addon.mod_workshop.assessedsubmission": "Értékelt leadott munka", - "addon.mod_workshop.assessmentform": "Értékelő űrlap", - "addon.mod_workshop.assessmentsettings": "Értékelési beállítások", - "addon.mod_workshop.assessmentweight": "Értékelési súly", - "addon.mod_workshop.assignedassessments": "Értékelendő hozzárendelt leadott munkák", - "addon.mod_workshop.assignedassessmentsnone": "Önnek nincs értékelendő hozzárendelt leadott munkája", - "addon.mod_workshop.conclusion": "Összegzés", - "addon.mod_workshop.createsubmission": "Leadott munka hozzáadása", - "addon.mod_workshop.deletesubmission": "Leadás törlése", - "addon.mod_workshop.editsubmission": "Leadott munka szerkesztése", - "addon.mod_workshop.feedbackauthor": "Visszajelzés a szerzőnek", - "addon.mod_workshop.feedbackby": "{{$a}} visszajelzése", - "addon.mod_workshop.feedbackreviewer": "Visszajelzés az ellenőrnek", - "addon.mod_workshop.givengrades": "Adott pontok", - "addon.mod_workshop.gradecalculated": "A leadott munkára adott kiszámított pont", - "addon.mod_workshop.gradeinfo": "Pont: {{$a.received}} / {{$a.max}}", - "addon.mod_workshop.gradeover": "Leadott munkára adott pont felülírása", - "addon.mod_workshop.gradesreport": "Műhelymunka pontjairól szóló jelentés", - "addon.mod_workshop.gradinggrade": "Értékelési pont", - "addon.mod_workshop.gradinggradecalculated": "Értékelés számított pontja", - "addon.mod_workshop.gradinggradeof": "értékelési pont (/ {{$a}})", - "addon.mod_workshop.gradinggradeover": "Értékelésre adott pont felülírása", - "addon.mod_workshop.modulenameplural": "Műhelymunkák", - "addon.mod_workshop.nogradeyet": "Még nincs pont", - "addon.mod_workshop.notassessed": "Még nincs értékelve", - "addon.mod_workshop.notoverridden": "Nincs felülírva", - "addon.mod_workshop.noyoursubmission": "Még nem adta le munkáját.", - "addon.mod_workshop.overallfeedback": "Általános visszajelzés", - "addon.mod_workshop.publishedsubmissions": "Közzétett leadott munkák", - "addon.mod_workshop.publishsubmission": "Leadott munka közzététele", - "addon.mod_workshop.publishsubmission_help": "A közzétett leadott munkákat a többiek a műhelymunka lezárását követően érhetik el.", - "addon.mod_workshop.reassess": "Újraértékelés", - "addon.mod_workshop.receivedgrades": "Beérkezett pontok", - "addon.mod_workshop.submissionattachment": "Csatolmány", - "addon.mod_workshop.submissioncontent": "Leadott munka tartalma", - "addon.mod_workshop.submissiondeleteconfirm": "Biztosan törli az alábbi leadott munkát?", - "addon.mod_workshop.submissiongrade": "Leadott munkára adott pont", - "addon.mod_workshop.submissiongradeof": "Leadott munkára adott pont (szerző: {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Írjon be szöveget vagy adjon hozzá egy állományt.", - "addon.mod_workshop.submissionsreport": "Műhely leadott munkáiról szóló jelentés", - "addon.mod_workshop.submissiontitle": "Cím", - "addon.mod_workshop.switchphase10": "Kapcsoljon át a beállítási fázisra", - "addon.mod_workshop.switchphase20": "Kapcsoljon át a leadási fázisra", - "addon.mod_workshop.switchphase30": "Kapcsoljon át a felmérési fázisra", - "addon.mod_workshop.switchphase40": "Kapcsoljon át az értékelési fázisra", - "addon.mod_workshop.switchphase50": "Műhelymunka bezárása", - "addon.mod_workshop.userplan": "Műhelymunka-tervező", - "addon.mod_workshop.userplancurrentphase": "Jelenlegi fázis", - "addon.mod_workshop.weightinfo": "Súly: {{$a}}", - "addon.mod_workshop.yourassessment": "Saját értékelés", - "addon.mod_workshop.yourassessmentfor": "{{$a}} értékelése", - "addon.mod_workshop.yourgrades": "Osztályzatai", - "addon.mod_workshop.yoursubmission": "Saját leadott munkája", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Megjegyzés ehhez: {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "{{$a}} osztályzata", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "{{$a}} szempont", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Ehhez a nézethez ki kell választania egy pontszámot.", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Megjegyzés ehhez: {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "{{$a}} szempont", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Megjegyzés ehhez: {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "{{$a}} osztályzata", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "{{$a}} állítás", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "{{$a}} feltétel", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Választania kell ezek közül egyet", - "addon.notes.addnewnote": "Új megjegyzés hozzáadása", - "addon.notes.coursenotes": "Kurzussal kapcsolatos megjegyzések", - "addon.notes.deleteconfirm": "Törli a megjegyzést?", - "addon.notes.eventnotecreated": "Megjegyzés létrehozva", - "addon.notes.eventnotedeleted": "Megjegyzés törölve", - "addon.notes.nonotes": "Nincsenek még ilyen megjegyzések.", - "addon.notes.note": "Megjegyzés", - "addon.notes.notes": "Megjegyzések", - "addon.notes.personalnotes": "Személyes megjegyzések", - "addon.notes.publishstate": "Környezet", - "addon.notes.sitenotes": "Portállal kapcsolatos megjegyzések", - "addon.notifications.markallread": "Összes megjelölése olvasottként", - "addon.notifications.notificationpreferences": "Értesítés beállításai", - "addon.notifications.notifications": "Értesítések", - "addon.notifications.therearentnotificationsyet": "Nincs értesítés", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Egyesült Arab Emírségek", - "assets.countries.AF": "Afganisztán", - "assets.countries.AG": "Antigua és Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albánia", - "assets.countries.AM": "Örményország", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktisz", - "assets.countries.AR": "Argentína", - "assets.countries.AS": "Amerikai Szamoa", - "assets.countries.AT": "Ausztria", - "assets.countries.AU": "Ausztrália", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland-szigetek", - "assets.countries.AZ": "Azerbajdzsán", - "assets.countries.BA": "Bosznia-Hercegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Banglades", - "assets.countries.BE": "Belgium", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgária", - "assets.countries.BH": "Bahrein", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint-Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Bolíviai Többnemzetiségű Állam", - "assets.countries.BQ": "Karibi Hollandia", - "assets.countries.BR": "Brazília", - "assets.countries.BS": "Bahama-szigetek", - "assets.countries.BT": "Bhután", - "assets.countries.BV": "Bouvet-sziget", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Belorusszia", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Coco-sziget", - "assets.countries.CD": "Kongói Demokratikus Köztársaság", - "assets.countries.CF": "Közép-Afrikai Köztársaság", - "assets.countries.CG": "Kongó", - "assets.countries.CH": "Svájc", - "assets.countries.CI": "Elefántcsontpart", - "assets.countries.CK": "Cook-szigetek", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Kína", - "assets.countries.CO": "Kolumbia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Zöldfoki Köztársaság", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Karácsony-sziget", - "assets.countries.CY": "Ciprus", - "assets.countries.CZ": "Csehország", - "assets.countries.DE": "Németország", - "assets.countries.DJ": "Dzsibuti", - "assets.countries.DK": "Dánia", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Dominikai Köztársaság", - "assets.countries.DZ": "Algéria", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Észtország", - "assets.countries.EG": "Egyiptom", - "assets.countries.EH": "Nyugat-Szahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spanyolország", - "assets.countries.ET": "Etiópia", - "assets.countries.FI": "Finnország", - "assets.countries.FJ": "Fidzsi-szigetek", - "assets.countries.FK": "Falkland-szigetek", - "assets.countries.FM": "Mikronéziai Szövetségi Államok", - "assets.countries.FO": "Feröer-szigetek", - "assets.countries.FR": "Franciaország", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Egyesült Királyság", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Grúzia", - "assets.countries.GF": "Francia Guyana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghána", - "assets.countries.GI": "Gibraltár", - "assets.countries.GL": "Grönland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Egyenlítői-Guinea", - "assets.countries.GR": "Görögország", - "assets.countries.GS": "Déli-Georgia és Déli-Sandwich-szigetek", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Bissau-Guinea", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hongkong", - "assets.countries.HM": "Heard-sziget és McDonalds-szigetek", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Horvátország", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Magyarország", - "assets.countries.ID": "Indonézia", - "assets.countries.IE": "Írország", - "assets.countries.IL": "Izrael", - "assets.countries.IM": "Man-sziget", - "assets.countries.IN": "India", - "assets.countries.IO": "Brit Indiai-óceániai Terület", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iráni Iszlám Köztársaság", - "assets.countries.IS": "Izland", - "assets.countries.IT": "Olaszország", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordánia", - "assets.countries.JP": "Japán", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirgizisztán", - "assets.countries.KH": "Kambodzsa", - "assets.countries.KI": "Kiribati Köztársaság", - "assets.countries.KM": "Comore-szigetek", - "assets.countries.KN": "Saint Christopher és Nevis Államszövetség", - "assets.countries.KP": "Koreai NDK", - "assets.countries.KR": "Dél-Korea", - "assets.countries.KW": "Kuvait", - "assets.countries.KY": "Kajmán-szigetek", - "assets.countries.KZ": "Kazahsztán", - "assets.countries.LA": "Laosz", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Libéria", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litvánia", - "assets.countries.LU": "Luxemburg", - "assets.countries.LV": "Lettország", - "assets.countries.LY": "Líbia", - "assets.countries.MA": "Marokkó", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldávia", - "assets.countries.ME": "Montenegró", - "assets.countries.MF": "Saint-Martin (francia rész)", - "assets.countries.MG": "Madagaszkár", - "assets.countries.MH": "Marshall-szigetek", - "assets.countries.MK": "Észak-macedón Köztársaság", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Mianmar", - "assets.countries.MN": "Mongólia", - "assets.countries.MO": "Makaó", - "assets.countries.MP": "Északi-Mariana szigetek", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauritánia", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Málta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Maldív-szigetek", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexikó", - "assets.countries.MY": "Malajzia", - "assets.countries.MZ": "Mozambik", - "assets.countries.NA": "Namíbia", - "assets.countries.NC": "Új-Kaledónia", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolk-szigetek", - "assets.countries.NG": "Nigéria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Hollandia", - "assets.countries.NO": "Norvégia", - "assets.countries.NP": "Nepál", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Új-Zéland", - "assets.countries.OM": "Omán", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Francia Polinézia", - "assets.countries.PG": "Pápua Új-Guinea", - "assets.countries.PH": "Fülöp-szigetek", - "assets.countries.PK": "Pakisztán", - "assets.countries.PL": "Lengyelország", - "assets.countries.PM": "St. Pierre és Miquelon", - "assets.countries.PN": "Pitcairn-sziget", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palesztin Autonómia", - "assets.countries.PT": "Portugália", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Reunion", - "assets.countries.RO": "Románia", - "assets.countries.RS": "Szerbia", - "assets.countries.RU": "Oroszország", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Szaúd-Arábia", - "assets.countries.SB": "Salamon-szigetek", - "assets.countries.SC": "Seychelles-szigetek", - "assets.countries.SD": "Szudán", - "assets.countries.SE": "Svédország", - "assets.countries.SG": "Szingapúr", - "assets.countries.SH": "Saint Helena, Ascension és Tristan Da Cunha", - "assets.countries.SI": "Szlovénia", - "assets.countries.SJ": "Spitzbergák", - "assets.countries.SK": "Szlovákia", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Szenegál", - "assets.countries.SO": "Szomália", - "assets.countries.SR": "Suriname", - "assets.countries.SS": "Dél-Szudán", - "assets.countries.ST": "Sao Tomé és Príncipe", - "assets.countries.SV": "Salvador", - "assets.countries.SX": "Sint Maarten (holland rész)", - "assets.countries.SY": "Szíria", - "assets.countries.SZ": "Szváziföld", - "assets.countries.TC": "Turk- és Caicos-szigetek", - "assets.countries.TD": "Csád", - "assets.countries.TF": "Francia Déli Területek", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thaiföld", - "assets.countries.TJ": "Tádzsikisztán", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Kelet-Timor", - "assets.countries.TM": "Türkmenisztán", - "assets.countries.TN": "Tunézia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Törökország", - "assets.countries.TT": "Trinidad és Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Tajvan", - "assets.countries.TZ": "Tanzánia", - "assets.countries.UA": "Ukrajna", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Amerikai Egyesült Államokhoz tartozó lakatlan szigetek", - "assets.countries.US": "Amerikai Egyesült Államok", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Üzbegisztán", - "assets.countries.VA": "Vatikánváros", - "assets.countries.VC": "Szent Vincent és a Grenadine-szigetek", - "assets.countries.VE": "Venezuelai Bolivári Köztársaság", - "assets.countries.VG": "Virgin-szigetek (brit)", - "assets.countries.VI": "Virgin-szigetek (USA)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis és Futuna-szigetek", - "assets.countries.WS": "Szamoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Dél-Afrika", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB e-könyv", - "assets.mimetypes.application/msword": "Word-dokumentum", - "assets.mimetypes.application/pdf": "PDF-dokumentum", - "assets.mimetypes.application/vnd.moodle.backup": "A Moodle biztonsági mentése", - "assets.mimetypes.application/vnd.ms-excel": "Excel-táblázat", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 makrót működtető munkafüzet", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint-bemutató", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument számolótábla", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument számolótábla sablonja", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument szöveges állomány", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument szövegsablon", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument weboldalsablon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 bemutató", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 diavetítés", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 számolótábla", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 sablon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 dokumentum", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote bemutató", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers számolótábla", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages dokumentum", - "assets.mimetypes.application/x-javascript": "JavaScript forrásfájl", - "assets.mimetypes.application/x-mspublisher": "Publisher-dokumentum", - "assets.mimetypes.application/x-shockwave-flash": "Flash-animáció", - "assets.mimetypes.application/xhtml_xml": "XHTML-dokumentum", - "assets.mimetypes.archive": "Archív ({{$a.EXT}})", - "assets.mimetypes.audio": "Hangállomány ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Állomány", - "assets.mimetypes.group:archive": "Archív állományok", - "assets.mimetypes.group:audio": "Hangállományok", - "assets.mimetypes.group:document": "Szöveges állományok", - "assets.mimetypes.group:html_audio": "Böngészők által alapból támogatott hangállományok", - "assets.mimetypes.group:html_track": "HTML-sáv állományai", - "assets.mimetypes.group:html_video": "Böngészők által alapból támogatott videóállományok", - "assets.mimetypes.group:image": "Képállományok", - "assets.mimetypes.group:presentation": "Bemutatóállományok", - "assets.mimetypes.group:sourcecode": "Forráskód", - "assets.mimetypes.group:spreadsheet": "Számolótábla-állományok", - "assets.mimetypes.group:video": "Videóállományok", - "assets.mimetypes.group:web_audio": "Neten használt hangállományok", - "assets.mimetypes.group:web_file": "Netes állományok", - "assets.mimetypes.group:web_image": "Neten használt képállományok", - "assets.mimetypes.group:web_video": "Neten használt videóállományok", - "assets.mimetypes.image": "Kép ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows-ikon", - "assets.mimetypes.text/css": "CSS", - "assets.mimetypes.text/csv": "Vesszővel elválasztott értékek", - "assets.mimetypes.text/html": "HTML-dokumentum", - "assets.mimetypes.text/plain": "Szöveges állomány", - "assets.mimetypes.text/rtf": "RTF-dokumentum", - "assets.mimetypes.text/vtt": "Webed videó szövegének követése", - "assets.mimetypes.video": "({{$a.EXT}}) videóállomány", - "core.accounts": "Fiókok", - "core.add": "Hozzáadás", - "core.agelocationverification": "Életkor és hely ellenőrzése", - "core.ago": "{{$a}} óta", - "core.all": "Mind", - "core.allgroups": "Minden csoport", - "core.allparticipants": "Összes résztvevő", - "core.answer": "Válasz", - "core.answered": "Megválaszolva", - "core.areyousure": "Biztos?", - "core.back": "Vissza", - "core.block.blocks": "Blokkok", - "core.cancel": "Mégse", - "core.cannotconnect": "Sikertelen kapcsolódás: ellenőrizze, helyesen írta-e be a webcímet, és a portál legalább {{$a}}. verziójú Moodle-t használ-e.", - "core.category": "Kategória", - "core.choose": "Választás", - "core.choosedots": "Választás...", - "core.clicktohideshow": "Kattintson a kibontáshoz vagy a becsukáshoz.", - "core.close": "Bezárás", - "core.comments": "Megjegyzések", - "core.comments.addcomment": "Megjegyzés hozzáadása...", - "core.comments.comments": "Megjegyzések", - "core.comments.commentscount": "Megjegyzések ({{$a}})", - "core.comments.deletecommentbyon": "{{$a.user}} által {{$a.time}} időpontban beküldött megjegyzés törlése", - "core.comments.eventcommentcreated": "Megjegyzés létrehozva", - "core.comments.eventcommentdeleted": "Megjegyzés törölve", - "core.comments.nocomments": "Nincs megjegyzés.", - "core.comments.savecomment": "Megjegyzés mentése", - "core.commentscount": "Megjegyzések ({{$a}})", - "core.completion-alt-auto-fail": "Teljesítve: {{$a}} (a teljesítési pontszámot nem érte el)", - "core.completion-alt-auto-n": "Nincs teljesítve: {{$a}}", - "core.completion-alt-auto-n-override": "Nincs teljesítve: {{$a.modname}} (beállította {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Teljesítve: {{$a}} (a teljesítési pontszámot elérte)", - "core.completion-alt-auto-y": "Teljesítve: {{$a}}", - "core.completion-alt-auto-y-override": "Teljesítve: {{$a.modname}} (beállította {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Nincs teljesítve: {{$a}}; teljesítettként való megjelöléséhez válassza ki.", - "core.completion-alt-manual-n-override": "Nincs teljesítve: {{$a.modname}} (beállította {{$a.overrideuser}}). Válassza ki teljesítettként való megjelölésre.", - "core.completion-alt-manual-y": "Teljesítve: {{$a}}, teljesítetlenként való megjelöléséhez válassza ki.", - "core.completion-alt-manual-y-override": "Teljesítve: {{$a.modname}} (beállította {{$a.overrideuser}}). Válassza ki nem teljesítettként való megjelölésre.", - "core.confirmdeletefile": "Biztosan törli ezt az állományt?", - "core.considereddigitalminor": "Túl fiatal vagy a portálon fiók létrehozásához.", - "core.content": "Tartalom", - "core.continue": "Folytatás", - "core.course": "Kurzus", - "core.course.contents": "Tartalomjegyzék", - "core.course.coursesummary": "Kurzusösszegzés", - "core.course.downloadcourse": "Kurzus letöltése", - "core.course.hiddenfromstudents": "Tanulók elől rejtve", - "core.course.hiddenoncoursepage": "Elérhető, de a kurzusoldalon nem látható", - "core.course.overriddennotice": "A tevékenységgel kapcsolatos végső pontja kézzel módosítva lett.", - "core.course.sections": "Szekciók", - "core.coursedetails": "Kurzusadatok", - "core.courses.addtofavourites": "Jelölje meg a kurzust", - "core.courses.allowguests": "Ez a kurzus megengedi vendégek belépését", - "core.courses.availablecourses": "Felvehető kurzusok", - "core.courses.categories": "Kurzuskategóriák", - "core.courses.courses": "Kurzusok", - "core.courses.frontpage": "Kezdőoldal", - "core.courses.hidecourse": "Elrejtés", - "core.courses.ignore": "Kihagy", - "core.courses.mycourses": "Kurzusaim", - "core.courses.mymoodle": "Irányítópult", - "core.courses.nocourses": "Nincs megjelenítendő kurzusinformáció.", - "core.courses.nocoursesyet": "Ebben a kategóriában nincsenek kurzusok", - "core.courses.nosearchresults": "Nincs eredmény", - "core.courses.notenroled": "Ezt a kurzust nem vette föl", - "core.courses.paymentrequired": "Ebbe a kurzusba csak fizetés után léphet be.", - "core.courses.paypalaccepted": "PayPal-lel való fizetést elfogadunk", - "core.courses.reload": "Újbóli betöltés", - "core.courses.removefromfavourites": "Kurzusbejelölés törlése", - "core.courses.search": "Keresés", - "core.courses.searchcourses": "Kurzusok keresése", - "core.courses.sendpaymentbutton": "Fizetés küldése Paypal-lel", - "core.courses.show": "Ezen kurzus megjelenítése", - "core.date": "Dátum", - "core.day": "nap", - "core.days": "nap", - "core.decsep": ",", - "core.defaultvalue": "Alapeset ({{$a}})", - "core.delete": "Törlés", - "core.deleteduser": "Törölt felhasználó", - "core.description": "Leírás", - "core.digitalminor": "Digitálisan kiskorú", - "core.digitalminor_desc": "Fiók létrehozásához szülőd/gondviselőd lépjen kapcsolatba az alábbi személlyel:", - "core.displayoptions": "Megjelenítési lehetőségek", - "core.done": "Kész", - "core.download": "Letöltés", - "core.edit": "Szerkesztés", - "core.editor.autosavesucceeded": "Piszkozat mentve.", - "core.editor.bold": "Vastag", - "core.editor.clear": "Formázás törlése", - "core.editor.h3": "Fejléc (nagy)", - "core.editor.h4": "Fejléc (közepes)", - "core.editor.h5": "Fejléc (kicsi)", - "core.editor.italic": "Dőlt", - "core.editor.orderedlist": "Rendezett lista", - "core.editor.p": "Bekezdés", - "core.editor.strike": "Áthúzott", - "core.editor.textrecovered": "A szöveg piszkozatának automatikus helyreállítása megtörtént.", - "core.editor.underline": "Aláhúzás", - "core.editor.unorderedlist": "Rendezetlen lista", - "core.error": "Hiba", - "core.errordownloading": "Nem sikerült letölteni az állományt.", - "core.explanationdigitalminor": "Erre az adatra nagykorúsága megállapításához van szükség. A nagykorú jogosult feltételeket vállalni, adatai pedig tárolhatók és feldolgozhatók.", - "core.favourites": "Megjelölt", - "core.filename": "Állománynév", - "core.filenotfound": "Nincs meg az állomány.", - "core.fileuploader.addfiletext": "Állomány hozzáadása", - "core.fileuploader.errorcapturingaudio": "Hiba a hang rögzítése közben.", - "core.fileuploader.errorcapturingvideo": "Nem sikerült videofelvételt készíteni.", - "core.fileuploader.filesofthesetypes": "Elfogadott állománytípusok:", - "core.fileuploader.fileuploaded": "Fájl feltöltve", - "core.fileuploader.invalidfiletype": "{{$a}} típusú állomány nem fogadható el.", - "core.fileuploader.more": "Tovább", - "core.fileuploader.uploading": "Feltöltés", - "core.fileuploader.video": "Videó", - "core.filter": "Szűrő", - "core.folder": "Mappa", - "core.forcepasswordchangenotice": "Továbblépéshez módosítsa jelszavát.", - "core.fulllistofcourses": "Minden kurzus", - "core.grades.average": "Átlag", - "core.grades.badgrade": "A megadott pont érvénytelen", - "core.grades.contributiontocoursetotal": "Hozzájárulás a kurzus összpontszámához", - "core.grades.feedback": "Visszajelzés", - "core.grades.grade": "Pont", - "core.grades.gradeitem": "Osztályozási tétel", - "core.grades.grades": "Pontok", - "core.grades.lettergrade": "Betűpontozás", - "core.grades.nogradesreturned": "Nincs kapott pont", - "core.grades.nooutcome": "Nincs eredmény", - "core.grades.percentage": "Százalék", - "core.grades.range": "Tartomány", - "core.grades.rank": "Sorrend", - "core.grades.weight": "Súly", - "core.group": "Csoport", - "core.groupsseparate": "Külön csoportok", - "core.groupsvisible": "Látható csoportok", - "core.h5p.additionallicenseinfo": "Az engedélyre vonatkozó egyéb információk", - "core.h5p.author": "Szerző", - "core.h5p.authorcomments": "Szerző megjegyzései", - "core.h5p.authorcommentsdescription": "A tartalom szerkesztőjének szánt megjegyzések (ez a szöveg nem kerül bele a szerzői jogi tájékoztatóba).", - "core.h5p.authorname": "A szerző neve", - "core.h5p.authorrole": "A szerző szerepe", - "core.h5p.by": "készítette", - "core.h5p.cancellabel": "Törlés", - "core.h5p.ccattribution": "Forrásmegjelölés (CC BY)", - "core.h5p.ccattributionnc": "Forrásmegjelölés-NeAddEl (CC BY-NC)", - "core.h5p.ccattributionncnd": "Forrásmegjelölés-NeAddEl-NeVáltoztasd (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Forrásmegjelölés-NeAddEl-ÍgyAddTovább (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Forrásmegjelölés-NeVáltoztasd (CC BY-ND)", - "core.h5p.ccattributionsa": "Forrásmegjelölés-ÍgyAddTovább (CC BY-SA)", - "core.h5p.ccpdd": "Közkincs (CC0)", - "core.h5p.changedby": "Módosította", - "core.h5p.changedescription": "Módosítás leírása", - "core.h5p.changelog": "Módosítási napló", - "core.h5p.changeplaceholder": "Fénykép körülvágva, szöveg módosítva stb.", - "core.h5p.close": "Bezárás", - "core.h5p.confirmdialogbody": "Erősítse meg, hogy folytatni kívánja. A lépés nem visszafordítható.", - "core.h5p.confirmdialogheader": "Lépés megerősítése", - "core.h5p.confirmlabel": "Megerősítés", - "core.h5p.connectionLost": "A kapcsolat megszűnt. Az eredményeket tároljuk és akkor küldjük el, ha a kapcsolat helyreállt.", - "core.h5p.connectionReestablished": "A kapcsolat helyreállt", - "core.h5p.contentCopied": "A tartalom a vágólapra másolva", - "core.h5p.contentchanged": "A tartalom az utolsó használat óta módosult.", - "core.h5p.contenttype": "A tartalom típusa", - "core.h5p.copyright": "Használati jogok", - "core.h5p.copyrightinfo": "Szerzői jogi információ", - "core.h5p.copyrightstring": "Szerzői jog", - "core.h5p.copyrighttitle": "A tartalomra vonatkozó szerzői jogi információ megtekintése", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Dátum", - "core.h5p.disablefullscreen": "Teljes képernyő kikapcsolása", - "core.h5p.download": "Letöltés", - "core.h5p.downloadtitle": "A tartalom letöltése H5P-állomány formájában", - "core.h5p.editor": "Szerkesztő", - "core.h5p.embed": "Beágyazás", - "core.h5p.embedtitle": "A tartalom beágyazási kódjának megtekintése", - "core.h5p.fullscreen": "Teljes képernyő", - "core.h5p.gpl": "3. általános licenc", - "core.h5p.h5ptitle": "További tartalmakért látogasson el a h5p.org weboldalra.", - "core.h5p.hideadvanced": "Részletes elrejtése", - "core.h5p.license": "Engedély", - "core.h5p.licenseCC010": "CC0 1.0 Egyetemes (CC0 1.0) Közkincs", - "core.h5p.licenseCC010U": "CC0 1.0 Egyetemes", - "core.h5p.licenseCC10": "1.0 Általános", - "core.h5p.licenseCC20": "2.0 Általános", - "core.h5p.licenseCC25": "2.5 Általános", - "core.h5p.licenseCC30": "3.0 Jogrendtől független", - "core.h5p.licenseCC40": "4.0 Nemzetközi", - "core.h5p.licenseGPL": "Általános nyilvános licenc", - "core.h5p.licenseV1": "1. verzió", - "core.h5p.licenseV2": "2. verzió", - "core.h5p.licenseV3": "3. verzió", - "core.h5p.licensee": "Engedélyes", - "core.h5p.licenseextras": "Kiegészítő engedélyek", - "core.h5p.licenseversion": "Engedély verziója", - "core.h5p.nocopyright": "A tartalomhoz nincs szerzői jogi tájékoztatás.", - "core.h5p.offlineDialogBody": "A feladat teljesítéséről nem tudunk tájékoztatást küldeni. Ellenőrizze internetes csatlakozását.", - "core.h5p.offlineDialogHeader": "A szerverrel való kapcsolata megszakadt.", - "core.h5p.offlineDialogRetryButtonLabel": "Újrapróbálkozás most", - "core.h5p.offlineDialogRetryMessage": "Újrapróbálkozás ... múlva", - "core.h5p.offlineSuccessfulSubmit": "Eredmények sikeresen leadva.", - "core.h5p.originator": "Feladó", - "core.h5p.pd": "Közkincs", - "core.h5p.pddl": "Közkinccsé tétel és licenc", - "core.h5p.pdm": "Közkincshez tartozó (PDM)", - "core.h5p.resizescript": "Ha portálján dinamikusan szeretné méretezni a beágyazott tartalmat, szúrja be az alábbi kódrészletet:", - "core.h5p.resubmitScores": "Próbálkozás a tárolt eredmények leadásával", - "core.h5p.reuse": "Újbóli használat", - "core.h5p.reuseContent": "Tartalom újbóli használata", - "core.h5p.reuseDescription": "A tartalom újbóli használata.", - "core.h5p.showadvanced": "Részletek megjelenítése", - "core.h5p.showless": "Kevesebb megjelenítése", - "core.h5p.showmore": "Több megjelenítése", - "core.h5p.size": "Méret", - "core.h5p.source": "Forrás", - "core.h5p.startingover": "Újra fogja kezdeni.", - "core.h5p.sublevel": "Alszint", - "core.h5p.thumbnail": "Miniatűr", - "core.h5p.title": "Cím", - "core.h5p.undisclosed": "Rejtett", - "core.h5p.year": "Év", - "core.h5p.years": "Év(ek)", - "core.h5p.yearsfrom": "Évek (ekkortól)", - "core.h5p.yearsto": "Évek (eddig)", - "core.help": "Súgó", - "core.hide": "Elrejtés", - "core.hour": "óra", - "core.hours": "óra", - "core.info": "Információ", - "core.invalidformdata": "Hibás űrlapadatok", - "core.labelsep": ":", - "core.lastaccess": "Utolsó belépés", - "core.lastmodified": "Utolsó módosítás", - "core.layoutgrid": "Rács", - "core.list": "Lista", - "core.listsep": ";", - "core.loading": "Betöltés", - "core.location": "Hely", - "core.login.auth_email": "E-mail alapú önregisztráció", - "core.login.authenticating": "Hitelesítés", - "core.login.cancel": "Mégse", - "core.login.changepassword": "Jelszó módosítása", - "core.login.createaccount": "Új felhasználói azonosítóm létrehozása", - "core.login.createuserandpass": "Új felhasználónév és jelszó megadása", - "core.login.emailconfirmsent": "Egy e-mailt kellett kapnia {{$a}} címére. A levél a regisztráció kitöltéséhez szükséges egyszerű teendőket tartalmazza. Ha továbbra is nehézségekbe ütközik, lépjen kapcsolatba a portál rendszergazdájával.", - "core.login.emailconfirmsentsuccess": "A visszaigazoló e-mail elküldése sikerült", - "core.login.firsttime": "Most van itt először?", - "core.login.forcepasswordchangenotice": "Továbblépéshez módosítsa jelszavát.", - "core.login.forgotten": "Elfelejtette felhasználónevét vagy jelszavát?", - "core.login.help": "Súgó", - "core.login.helpmelogin": "

                Több ezer Moodle-portál üzemel szerte a világban. Ez az alkalmazás csak azokhoz a portálokhoz képes kapcsolódni, amelyek lehetővé teszik ezt a mobil alkalmazás számára.

                Ha gondot okoz a kapcsolódás, forduljon a Moodle azon rendszergazdájához, aki az adott portált üzemelteti, és kérje meg, hogy olvassa el a http://docs.moodle.org/en/Mobile_app weboldalon lévő információt

                Az alkalmazás demó üzemmódú Moodle-portálon való teszteléséhez a Site URL mezőbe írja be a teacher vagy student szót, és kattintson az Add button gombra.

                ", - "core.login.instructions": "Utasítások", - "core.login.invalidaccount": "Ellenőrizze belépési adatait vagy ellenőriztesse a rendszergazdával a portál beállításait.", - "core.login.invaliddate": "Érvénytelen dátum", - "core.login.invalidemail": "Érvénytelen e-mail cím", - "core.login.invalidmoodleversion": "Érvénytelen Moodle-verzió. A minimális verziószám a {{$a}}.", - "core.login.invalidsite": "A portál-URL nem érvényes.", - "core.login.invalidurl": "Érvénytelen webcímet adott meg", - "core.login.login": "Belépés", - "core.login.logininsiterequired": "A portálra böngészőablakban kell bejelentkeznie.", - "core.login.loginsteps": "Ahhoz, hogy teljesen hozzáférjen a portálhoz, először új fiókot kell létrehoznia.", - "core.login.missingemail": "Hiányzó e-mail cím", - "core.login.missingfirstname": "Hiányzó keresztnév", - "core.login.missinglastname": "Hiányzó vezetéknév", - "core.login.mobileservicesnotenabled": "Portálján a mobil szolgáltatások nincsenek engedélyezve. A mobil elérhetőség bekapcsolásához forduljon a rendszergazdához.", - "core.login.mustconfirm": "Meg kell erősítenie a fiókját", - "core.login.newaccount": "Új fiók", - "core.login.password": "Jelszó", - "core.login.passwordforgotten": "Elfelejtett jelszó", - "core.login.passwordforgotteninstructions2": "Jelszavának visszaállításához adja meg alább felhasználónevét vagy e-mail címét. Ha szerepel az adatbázisban, e-mailt küldünk az e-mail címére, melyben tájékoztatjuk, hogy léphet be ismét.", - "core.login.passwordrequired": "Jelszó szükséges", - "core.login.policyaccept": "Megértettem és elfogadom", - "core.login.policyagree": "A portál használatához el kell fogadnia a feltételeket. Elfogadja őket?", - "core.login.policyagreement": "A portál használati feltételeiről szóló megállapodás", - "core.login.policyagreementclick": "Ugrópont a portál használati feltételeiről szóló megállapodáshoz", - "core.login.potentialidps": "Lépjen be itteni fiókjával:", - "core.login.profileinvaliddata": "Érvénytelen érték", - "core.login.resendemail": "E-mail újbóli elküldése", - "core.login.security_question": "Biztonsági kérdés", - "core.login.selectacountry": "Válasszon egy országot", - "core.login.siteinmaintenance": "A portálja karbantartási üzemmódban van.", - "core.login.siteurl": "Portál-URL", - "core.login.siteurlrequired": "Portál-URL-t meg kell adni, vagyis http://www.yourmoodlesite.abc vagy https://www.yourmoodlesite.efg", - "core.login.startsignup": "Új fiók létrehozása", - "core.login.supplyinfo": "További részletek", - "core.login.username": "Felhasználónév", - "core.login.usernameoremail": "Felhasználónevet vagy e-mail címet adjon meg", - "core.login.usernamerequired": "Felhasználónevet kell megadni", - "core.login.usernotaddederror": "A felhasználó hozzáadására nem került sor - hiba.", - "core.login.webservicesnotenabled": "A webszolgáltatások nincsenek engedélyezve a portálján. A mobil elérhetőség bekapcsolásához forduljon a rendszergazdához.", - "core.lostconnection": "A kapcsolat megszakadt, kacsolódjon újból. Jele érvénytelen.", - "core.mainmenu.help": "Súgó", - "core.mainmenu.logout": "Kilépés", - "core.mainmenu.website": "Weboldal", - "core.maxsizeandattachments": "Állományok maximális mérete: {{$a.size}}, maximális csatolt állomány: {{$a.attachments}}", - "core.min": "p", - "core.mins": "perc", - "core.misc": "Egyéb", - "core.mod_assign": "Feladat", - "core.mod_assignment": "2.2 (kiiktatott) feladat", - "core.mod_book": "Könyv", - "core.mod_chat": "Csevegés", - "core.mod_choice": "Válaszlehetőség", - "core.mod_data": "Adatbázis", - "core.mod_database": "Adatbázis", - "core.mod_external-tool": "Külső eszköz", - "core.mod_feedback": "Visszajelzés", - "core.mod_file": "Állomány", - "core.mod_folder": "Mappa", - "core.mod_forum": "Fórum", - "core.mod_glossary": "Fogalomtár", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS-tartalomcsomag", - "core.mod_imscp": "IMS-tartalomcsomag", - "core.mod_label": "Címke", - "core.mod_lesson": "Lecke", - "core.mod_lti": "Külső eszköz", - "core.mod_page": "Oldal", - "core.mod_quiz": "Teszt", - "core.mod_resource": "Tananyag", - "core.mod_scorm": "SCORM-csomag", - "core.mod_survey": "Felmérés", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Műhelymunka", - "core.moduleintro": "Leírás", - "core.more": "tovább", - "core.mygroups": "Csoportjaim", - "core.name": "Név", - "core.networkerrormsg": "A hálózat nincs bekapcsolva vagy nem működik.", - "core.never": "Soha", - "core.next": "Következő", - "core.no": "Nem", - "core.nocomments": "Nincs megjegyzés.", - "core.nograde": "Nincs pont", - "core.none": "Nincs", - "core.nopermissions": "Ehhez ({{$a}}) jelenleg nincs engedélye.", - "core.noresults": "Nincs eredmény", - "core.noselection": "Nincs semmi kiválasztva", - "core.notenrolledprofile": "Ez a profil nem elérhető, mert a felhasználó nem vette fel a kurzust.", - "core.notice": "Tájékoztatás", - "core.notingroup": "Az oldal megtekintéséhez egy csoporthoz kell tartoznia.", - "core.now": "most", - "core.numwords": "{{$a}} szó", - "core.offline": "Offline", - "core.ok": "Rendben", - "core.online": "Online", - "core.othergroups": "Egyéb csoportok", - "core.pagea": "{{$a}} oldal", - "core.paymentinstant": "A fizetéshez és a perceken belüli beiratkozáshoz használja az alábbi gombot!", - "core.phone": "Telefon", - "core.pictureof": "Kép", - "core.previous": "Előző", - "core.proceed": "Tovább", - "core.question.answer": "Válasz", - "core.question.answersaved": "A válasz elmentve", - "core.question.certainty": "Bizonyosság", - "core.question.complete": "Kész", - "core.question.correct": "Helyes", - "core.question.feedback": "Visszajelzés", - "core.question.incorrect": "Hibás", - "core.question.information": "Információ", - "core.question.invalidanswer": "Hiányos válasz", - "core.question.notanswered": "Nincs rá válasz", - "core.question.notyetanswered": "Még nincs rá válasz", - "core.question.partiallycorrect": "Részben helyes", - "core.question.questionno": "{{$a}}. kérdés", - "core.question.requiresgrading": "Pontozandó", - "core.quotausage": "Összesen {{$a.total}} egységből {{$a.used}}-t használt fel.", - "core.rating.aggregateavg": "Értékelések átlaga", - "core.rating.aggregatecount": "Értékelések száma", - "core.rating.aggregatemax": "Maximális értékelés", - "core.rating.aggregatemin": "Minimális értékelés", - "core.rating.aggregatesum": "Értékelések összege", - "core.rating.noratings": "Nincs leadott értékelés", - "core.rating.rating": "Értékelés", - "core.rating.ratings": "Értékelések", - "core.refresh": "Frissítés", - "core.remove": "Törlés", - "core.required": "Kitöltendő", - "core.resourcedisplayopen": "Megnyitás", - "core.resources": "tananyagok", - "core.restore": "Helyreállítás", - "core.restricted": "Korlátozott", - "core.save": "Mentés", - "core.savechanges": "Módosítások mentése", - "core.search": "Keresés", - "core.searchresults": "Keresési eredmények", - "core.sec": "mp", - "core.secs": "mp", - "core.seemoredetail": "A részletekért kattintson ide", - "core.selectacategory": "Válasszon kategóriát", - "core.selectacourse": "Válasszon kurzust", - "core.selectagroup": "Válasszon csoportot", - "core.send": "Küldés", - "core.sending": "Küldés", - "core.serverconnection": "Hiba a szerverhez csatlakozás közben", - "core.settings.about": "Ismertetés", - "core.settings.currentlanguage": "Aktuális nyelv", - "core.settings.debugdisplay": "Hibaszűrési üzenetek megjelenítése", - "core.settings.deletesitefiles": "Biztosan törli a portálról a letöltött fájlokat?", - "core.settings.disableall": "Értesítések kikapcsolása", - "core.settings.disabled": "Kikapcsolva", - "core.settings.estimatedfreespace": "Becsült szabad tárhely", - "core.settings.general": "Általános", - "core.settings.language": "Nyelv", - "core.settings.license": "Licenc", - "core.settings.locked": "Zárolva", - "core.settings.loggedin": "Bejelentkezve:", - "core.settings.loggedoff": "Kijelentkezve", - "core.settings.preferences": "Beállítások", - "core.settings.settings": "Beállítások", - "core.settings.sites": "Portálok", - "core.settings.spaceusage": "Tárhelyhasználat", - "core.settings.synchronization": "Szinkronizálás", - "core.settings.total": "Összesen", - "core.show": "Mutat", - "core.showless": "Kevesebb megjelenítése", - "core.showmore": "Több megjelenítése", - "core.site": "Portál", - "core.sitehome.sitehome": "Portál kezdőoldala", - "core.sitehome.sitenews": "Portálhírek", - "core.sitemaintenance": "A portálon karbantartás folyik, jelenleg nem elérhető", - "core.sizeb": "bájt", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.skip": "Átugrás", - "core.sort": "Rendezés", - "core.sortby": "Rendezési szempont", - "core.strftimedate": "%Y. %B %d.", - "core.strftimedatefullshort": "%y/%m/%d", - "core.strftimedateshort": "%B %d.", - "core.strftimedatetime": "%Y. %B %d., %H:%M", - "core.strftimedatetimeshort": "%Y/%m/%d %H:%M", - "core.strftimedaydate": "%Y. %B %d., %A", - "core.strftimedaydatetime": "%Y. %B %d., %A, %H:%M", - "core.strftimedayshort": "%B, %d., %A", - "core.strftimedaytime": "%H:%M, %a", - "core.strftimemonthyear": "%Y. %B", - "core.strftimerecent": "%b %d., %H:%M", - "core.strftimerecentfull": "%Y. %b. %d., %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Leadás", - "core.success": "Sikerült", - "core.tag.defautltagcoll": "Alapgyűjtemény", - "core.tag.inalltagcoll": "Mindenhol", - "core.tag.itemstaggedwith": "{{$a.tagarea}} \"{{$a.tag}}\" címkével megjelölve", - "core.tag.noresultsfor": "Nincs eredmény \"{{$a}}\" esetén", - "core.tag.notagsfound": "Nincs \"{{$a}}\" szerint megfelelő címke", - "core.tag.searchtags": "Címkék közötti keresés", - "core.tag.showingfirsttags": "{{$a}} legnépszerűbb címke megjelenítése", - "core.tag.tag": "Címke", - "core.tag.tagarea_course": "Kurzusok", - "core.tag.tagarea_course_modules": "Tevékenységek és tananyagok", - "core.tag.tagarea_post": "Blogüzenetek", - "core.tag.tagarea_user": "Érdeklődési kör", - "core.tag.tags": "Címkék", - "core.teachers": "Tanárok", - "core.thisdirection": "ltr", - "core.time": "Idő", - "core.timesup": "Ideje elfogyott", - "core.today": "Ma", - "core.unexpectederror": "Váratlan hiba. Zárja be és nyissa meg újból az alkalmazást.", - "core.unlimited": "Korlátlan", - "core.upgraderunning": "A portál frissítése folyamatban, próbálkozzék később.", - "core.user": "Felhasználó", - "core.user.address": "Cím", - "core.user.city": "Helység", - "core.user.contact": "Kapcsolat", - "core.user.country": "Ország", - "core.user.description": "Leírás", - "core.user.details": "Részletek", - "core.user.editingteacher": "Tanár", - "core.user.email": "E-mail cím", - "core.user.emailagain": "E-mail (ismét)", - "core.user.firstname": "Keresztnév", - "core.user.interests": "Érdeklődési kör", - "core.user.lastname": "Vezetéknév", - "core.user.manager": "Igazgató", - "core.user.newpicture": "Új kép", - "core.user.noparticipants": "A kurzushoz nincsenek résztvevők!", - "core.user.participants": "Résztvevők", - "core.user.phone1": "Telefon", - "core.user.phone2": "Mobiltelefon", - "core.user.roles": "Szerepek", - "core.user.student": "Tanuló", - "core.user.teacher": "Nem szerkesztő tanár", - "core.user.webpage": "Weboldal", - "core.userdeleted": "Ez a felhasználó törölve lett", - "core.userdetails": "Felhasználó adatai", - "core.usernotfullysetup": "A felhasználó beállítása még nincs kész", - "core.users": "Felhasználó", - "core.view": "Nézet", - "core.viewprofile": "Profil megtekintése", - "core.whatisyourage": "Hány éves?", - "core.wheredoyoulive": "Melyik országban él?", - "core.whyisthisrequired": "Miért van erre szükség?", - "core.year": "év", - "core.years": "év", - "core.yes": "Igen" -} \ No newline at end of file diff --git a/src/assets/lang/id.json b/src/assets/lang/id.json deleted file mode 100644 index 63a731092..000000000 --- a/src/assets/lang/id.json +++ /dev/null @@ -1,1144 +0,0 @@ -{ - "addon.block_activitymodules.pluginname": "Aktivitas", - "addon.block_calendarmonth.pluginname": "Kalender", - "addon.block_calendarupcoming.pluginname": "Acara akan datang", - "addon.block_newsitems.pluginname": "Berita terbaru", - "addon.block_onlineusers.pluginname": "Pengguna Online", - "addon.block_recentactivity.pluginname": "Aktifitas lalu", - "addon.block_sitemainmenu.pluginname": "Menu utama", - "addon.calendar.allday": "Sepanjang hari", - "addon.calendar.calendar": "Kalender", - "addon.calendar.calendarevents": "Kalender Acara", - "addon.calendar.categoryevents": "Kategori Acara Acara", - "addon.calendar.confirmeventdelete": "Apakah Anda yakin ingin menghapus acara ini?", - "addon.calendar.confirmeventseriesdelete": "\"{{$a.name}}\" acara adalah bagian dari rangkaian. Apakah anda akan menghapus acara ini, atau semua {{$a.count}} acara dalam rangkaian?", - "addon.calendar.courseevents": "Agenda kursus", - "addon.calendar.daynext": "Hari selanjutnya", - "addon.calendar.dayprev": "Hari sebelumnya", - "addon.calendar.defaultnotificationtime": "Pemberitahuan waktu default", - "addon.calendar.deleteallevents": "Hapus semua acara", - "addon.calendar.deleteevent": "Hapus acara", - "addon.calendar.deleteoneevent": "Hapus acara ini", - "addon.calendar.durationminutes": "Durasi dalam hitungan menit", - "addon.calendar.durationnone": "Tanpa durasi", - "addon.calendar.durationuntil": "Sampai", - "addon.calendar.editevent": "Mengedit acara", - "addon.calendar.errorloadevent": "Terjadi kesalahan saat memuat acara", - "addon.calendar.errorloadevents": "Terjadi kesalahan saat memuat acara", - "addon.calendar.eventduration": "Durasi", - "addon.calendar.eventendtime": "Waktu selesai", - "addon.calendar.eventkind": "Jenis acara", - "addon.calendar.eventname": "Judul acara", - "addon.calendar.eventstarttime": "Waktu mulai", - "addon.calendar.fri": "Jum", - "addon.calendar.friday": "Jum'at", - "addon.calendar.groupevents": "Acara grup", - "addon.calendar.mon": "Sen", - "addon.calendar.monday": "Senin", - "addon.calendar.monthlyview": "Tampilan bulanan", - "addon.calendar.newevent": "Acara baru", - "addon.calendar.noevents": "Tidak ada acara", - "addon.calendar.sat": "Sab", - "addon.calendar.saturday": "Sabtu", - "addon.calendar.sun": "Ming", - "addon.calendar.sunday": "Minggu", - "addon.calendar.thu": "Kam", - "addon.calendar.thursday": "Kamis", - "addon.calendar.today": "Hari ini", - "addon.calendar.tomorrow": "Besok", - "addon.calendar.tue": "Sel", - "addon.calendar.tuesday": "Selasa", - "addon.calendar.typecourse": "Agenda kursus", - "addon.calendar.typegroup": "Agenda grup", - "addon.calendar.typesite": "Agenda situs", - "addon.calendar.typeuser": "Agenda pengguna", - "addon.calendar.upcomingevents": "Agenda mendatang", - "addon.calendar.userevents": "Agenda pengguna", - "addon.calendar.wed": "Rab", - "addon.calendar.wednesday": "Rabu", - "addon.calendar.yesterday": "Kemarin", - "addon.competency.errornocompetenciesfound": "Tidak ditemukan kompetensi", - "addon.competency.nocompetencies": "Tidak ada kompetensi", - "addon.coursecompletion.complete": "Lengkap", - "addon.coursecompletion.couldnotloadreport": "Tidak dapat memuat laporan penyelesaian materi", - "addon.coursecompletion.required": "Diwajibkan", - "addon.coursecompletion.status": "Status", - "addon.files.couldnotloadfiles": "Daftar file tidak dapat dimuat", - "addon.files.emptyfilelist": "Tidak ada file yang bisa ditampilkan.", - "addon.files.erroruploadnotworking": "Sayang sekali saat ini tidak memungkinkan untuk mengunggah file ke situs Anda.", - "addon.files.files": "File", - "addon.files.privatefiles": "File pribadi", - "addon.files.sitefiles": "File situs", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Konfigurasikan perangkat", - "addon.messages.addcontact": "Tambahkan kontak", - "addon.messages.blocknoncontacts": "Cegah pengguna yang tidak ada dalam kontak untuk mengirim pesan kepada saya", - "addon.messages.contactlistempty": "Daftar kontak kosong.", - "addon.messages.contactname": "Nama kontak", - "addon.messages.contacts": "Kontak", - "addon.messages.errordeletemessage": "Terjadi kesalahan saat menghapus pesan ini.", - "addon.messages.errorwhileretrievingcontacts": "Terjadi kesalahan saat mengambil kontak dari server.", - "addon.messages.errorwhileretrievingdiscussions": "Terjadi kesalahan saat mengambil diskusi dari server.", - "addon.messages.errorwhileretrievingmessages": "Terjadi kesalahan saat mengambil pesan dari server.", - "addon.messages.message": "Pesan", - "addon.messages.messagenotsent": "Pesan tidak terkirim. Silahkan coba lagi.", - "addon.messages.messages": "Pesan-pesan", - "addon.messages.newmessages": "Pesan baru", - "addon.messages.nomessagesfound": "Tidak ada pesan ditemukan", - "addon.messages.nousersfound": "Tidak ada pengguna ditemukan", - "addon.messages.removecontact": "Hapus kontak", - "addon.messages.removecontactconfirm": "Kontak akan dihapus dari daftar kontak Anda.", - "addon.messages.requests": "Permintaan", - "addon.messages.searchcombined": "Cari orang dan pesan", - "addon.messages.type_blocked": "Blokir", - "addon.messages.type_offline": "Ofline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Hasil pencarian", - "addon.messages.type_strangers": "Lainnya", - "addon.messages.warningmessagenotsent": "Tidak dapat mengirim pesan ke pengguna {{user}}. {{kesalahan}}", - "addon.mod_assign.acceptsubmissionstatement": "Mohon terima pernyataan penyerahan.", - "addon.mod_assign.addattempt": "Izinkan kesempatan lain", - "addon.mod_assign.addnewattempt": "Tambahkan kesempatan baru", - "addon.mod_assign.addnewattemptfromprevious": "Tambahkan kesempatan baru berdasarkan pengajuan sebelumnya", - "addon.mod_assign.addsubmission": "Tambahkan pengajuan (tugas/laporan)", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Rincian tugas dan formulir pengiriman akan tersedia dari {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Izinkan kiriman dari", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Tugas ini akan menerima kiriman dari {{$a}}", - "addon.mod_assign.applytoteam": "Terapkan nilai dan umpan balik ke seluruh grup", - "addon.mod_assign.assignmentisdue": "Kesempatan sudah berakhir", - "addon.mod_assign.attemptnumber": "Jumlah kesempatan", - "addon.mod_assign.attemptreopenmethod": "Kesempatan dibuka kembali", - "addon.mod_assign.attemptreopenmethod_manual": "Manual", - "addon.mod_assign.attemptreopenmethod_untilpass": "Otomatis sampai selesai", - "addon.mod_assign.attemptsettings": "Pengaturan kesempatan", - "addon.mod_assign.cannoteditduetostatementsubmission": "Anda tidak dapat menambahkan atau mengedit pengajuan di aplikasi karena kami tidak dapat mengambil pernyataan pengiriman dari situs.", - "addon.mod_assign.cannotgradefromapp": "Beberapa metode penilaian belum didukung oleh aplikasi dan tidak bisa dimodifikasi.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Anda tidak dapat mengajukan penilaian di aplikasi karena kami tidak dapat mengambil pernyataan pengiriman dari situs.", - "addon.mod_assign.confirmsubmission": "Anda yakin ingin mengirimkan pekerjaan Anda untuk dinilai? Anda tidak akan dapat melakukan perubahan lagi.", - "addon.mod_assign.currentattempt": "Ini adalah kesempatan {{$a}}.", - "addon.mod_assign.currentattemptof": "Ini adalah kesempatan ke-{{$a.attemptnumber}} ( {{$a.maxattempts}} kesempatan diizinkan).", - "addon.mod_assign.currentgrade": "Nilai Saat ini di raport", - "addon.mod_assign.cutoffdate": "Batas akhir waktu", - "addon.mod_assign.defaultteam": "Grup standar", - "addon.mod_assign.duedate": "Batas waktu", - "addon.mod_assign.duedateno": "Tidak ada batas waktu", - "addon.mod_assign.erroreditpluginsnotsupported": "Anda tidak dapat mengajukan penilaian di aplikasi karena beberapa plugin tidak didukung untuk mengedit.", - "addon.mod_assign.errorshowinginformation": "Kami tidak dapat menampilkan infomasi pengumpulan.", - "addon.mod_assign.feedbacknotsupported": "Umpan balik ini tidak didukung oleh aplikasi danmungkin tidak menyimpan semua informasi.", - "addon.mod_assign.grade": "Nilai", - "addon.mod_assign.gradenotsynced": "Nilai belum disinkronisasi.", - "addon.mod_assign.notallparticipantsareshown": "Peserta yang tidak mengumpulkan tidak ditampilkan.", - "addon.mod_assign.numwords": "{{$a}} kata", - "addon.mod_assign.submissionnotsupported": "Pengajuan ini tidak didukung oleh aplikasi dan mungkin tidak berisi semua informasi", - "addon.mod_assign.userwithid": "Pengguna dengan id {{id}}", - "addon.mod_assign.warningsubmissiongrademodified": "Pengajuan nilai telah dimodifikasi dalam situs.", - "addon.mod_assign.warningsubmissionmodified": "Pengajuan pengguna telah dimodifikasi pada situs.", - "addon.mod_assign.wordlimit": "Batas kata", - "addon.mod_assign_feedback_comments.pluginname": "Komentar umpan balik.", - "addon.mod_assign_submission_file.pluginname": "Pengiriman berkas", - "addon.mod_assign_submission_onlinetext.pluginname": "Pengiriman teks daring", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Batas kata untuk tugas ini adalah kata-kata {{$a.limit}} dan Anda mencoba mengirimkan kata-kata {{$a.count}}. Harap tinjau kiriman Anda dan coba lagi.", - "addon.mod_book.modulenameplural": "Buku-buku", - "addon.mod_book.toc": "Daftar Isi", - "addon.mod_chat.beep": "Beep", - "addon.mod_chat.chatreport": "Sesi obrolan", - "addon.mod_chat.currentusers": "Pengguna saat ini", - "addon.mod_chat.enterchat": "Klik disini untuk masuk chat sekarang", - "addon.mod_chat.entermessage": "Tulis pesan Anda", - "addon.mod_chat.errorwhileconnecting": "Terjadi kesalahan ketika terhubung ke obrolan.", - "addon.mod_chat.errorwhilegettingchatdata": "Terjadi kesalahan ketika mengumpulkan data obrolan.", - "addon.mod_chat.errorwhilegettingchatusers": "Terjadi kesalahan ketika mendapatkan pengguna obrolan.", - "addon.mod_chat.errorwhileretrievingmessages": "Terjadi kesalahan saat mengambil pesan dari server.", - "addon.mod_chat.errorwhilesendingmessage": "Terjadi kesalahan saat mengirim pesan.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} beep semua!", - "addon.mod_chat.messagebeepsyou": "{{$a}} baru saja membeep Anda!", - "addon.mod_chat.messageenter": "{{$a}} baru saja memasuki obrolan ini", - "addon.mod_chat.messageexit": "{{$a}} telah meninggalkan obrolan ini", - "addon.mod_chat.messages": "Pesan", - "addon.mod_chat.messageyoubeep": "Anda membeep {{$a}}", - "addon.mod_chat.modulenameplural": "Obrolan", - "addon.mod_chat.mustbeonlinetosendmessages": "Anda harus online untuk mengirim pesan.", - "addon.mod_chat.nomessages": "Tidak ada pesan", - "addon.mod_chat.send": "Kirim", - "addon.mod_chat.sessionstart": "Sesi percakapan selanjutnya akan mulai pada {{$a}}", - "addon.mod_chat.talk": "Cakap", - "addon.mod_chat.viewreport": "Lihat sesi percakapan sebelumnya", - "addon.mod_choice.errorgetchoice": "Terjadi kesalahan saat medapatkan data pilihan.", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% pengguna yang telah memilih pilihanof : {{text}}.", - "addon.mod_choice.resultsnotsynced": "Hasil tidak termasuk respon terakhir Anda. Silahkan sinkronisasi untuk update.", - "addon.mod_data.errorapproving": "Terjadi kesalahan saat menyetujui atau tidak menyetujui entri.", - "addon.mod_data.errordeleting": "Terjadi kesalahan saat menghapus entri.", - "addon.mod_feedback.captchaofflinewarning": "Umpan balik dengan captcha tidak dapat diselesaikan secara offline, atau jika tidak dikonfigurasi, atau jika server sedang down.", - "addon.mod_feedback.feedback_submitted_offline": "Umpan balik ini telah disimpan untuk disampaikan kemudian.", - "addon.mod_feedback.preview": "Pra-tampil", - "addon.mod_folder.emptyfilelist": "Tidak ada file untuk ditampilkan.", - "addon.mod_forum.addanewdiscussion": "Tambah topik diskusi baru", - "addon.mod_forum.addanewquestion": "Tambah pertanyaan baru", - "addon.mod_forum.addanewtopic": "Tambah topik baru", - "addon.mod_forum.advanced": "Lanjutan", - "addon.mod_forum.cannotadddiscussion": "Anda harus menjadi member grup untuk menambahkan diskusi ke forum ini", - "addon.mod_forum.cannotadddiscussionall": "Anda tidak punya wewenang untuk menambahkan topik diskusi baru untuk semua partisipan.", - "addon.mod_forum.cannotcreatediscussion": "Tidak dapat membuat diskusi baru", - "addon.mod_forum.couldnotadd": "Could not add your post due to an unknown error", - "addon.mod_forum.couldnotupdate": "Could not update your post due to an unknown error", - "addon.mod_forum.delete": "Hapus", - "addon.mod_forum.deletedpost": "The post has been deleted", - "addon.mod_forum.deletesure": "Are you sure you want to delete this post?", - "addon.mod_forum.discussion": "Diskusi", - "addon.mod_forum.discussionlocked": "This discussion has been locked so you can no longer reply to it.", - "addon.mod_forum.discussionpinned": "Sematkan", - "addon.mod_forum.discussionsubscription": "Diskusi berlangganan", - "addon.mod_forum.edit": "Edit", - "addon.mod_forum.erroremptymessage": "Post message cannot be empty", - "addon.mod_forum.erroremptysubject": "Post subject cannot be empty.", - "addon.mod_forum.errorgetforum": "Terjadi kesalahan saat mendapatkan data forum.", - "addon.mod_forum.errorgetgroups": "Terjadi kesalahan saat mendapatkan pengaturan grup.", - "addon.mod_forum.forumnodiscussionsyet": "Tidak ada topik diskusi di forum ini.", - "addon.mod_forum.group": "Grup", - "addon.mod_forum.lastpost": "Post terakhir", - "addon.mod_forum.message": "Pesan", - "addon.mod_forum.modeflatnewestfirst": "Tampilkan tanggapan secara secara flat, dari yang terbaru", - "addon.mod_forum.modeflatoldestfirst": "Tampilkan tanggapan secara secara flat, dari yang terlama", - "addon.mod_forum.modenested": "Tampilkan tanggapan dalam bentuk sarang", - "addon.mod_forum.modulenameplural": "Forum", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} diskusi", - "addon.mod_forum.numreplies": "{{numreplies}} jawaban", - "addon.mod_forum.posttoforum": "Post ke forum", - "addon.mod_forum.posttomygroups": "Kirimkan salinan ke semua grup", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Muat ulang diskusi", - "addon.mod_forum.refreshposts": "Muat ulang pos diskusi.", - "addon.mod_forum.reply": "Tanggapi", - "addon.mod_forum.subject": "Subjek", - "addon.mod_forum.tagarea_forum_posts": "Forum post", - "addon.mod_forum.unread": "Belum dibaca", - "addon.mod_forum.unreadpostsnumber": "{{$a}} postingan belum dibaca", - "addon.mod_forum.yourreply": "Tanggapan anda", - "addon.mod_glossary.browsemode": "Jelajah entri", - "addon.mod_glossary.byalphabet": "Menurut abjad", - "addon.mod_glossary.byauthor": "Kelompok berdasarkan penulis", - "addon.mod_glossary.bycategory": "Kelompok berdasarkan kategori", - "addon.mod_glossary.bynewestfirst": "Terbaru lebih dahulu", - "addon.mod_glossary.byrecentlyupdated": "Baru saja diperbarui", - "addon.mod_glossary.bysearch": "Cari", - "addon.mod_glossary.cannoteditentry": "Tidak dapat mengedit entri", - "addon.mod_glossary.entriestobesynced": "Entri yang akan disinkronkan", - "addon.mod_glossary.entrypendingapproval": "Entri ini menunggu persetujuan.", - "addon.mod_glossary.errorloadingentries": "Terjadi kesalahan saat memuat entri-entri.", - "addon.mod_glossary.errorloadingentry": "Terjadi kesalahan saat memuat entri.", - "addon.mod_glossary.errorloadingglossary": "Terjadi kesalahan saat memuat glosarium.", - "addon.mod_glossary.noentriesfound": "Tidak ada entri ditemukan.", - "addon.mod_glossary.searchquery": "Penelusuran queri.", - "addon.mod_imscp.showmoduledescription": "Menampilkan deskripsi.", - "addon.mod_lesson.answer": "Jawaban", - "addon.mod_lesson.attempt": "Percobaan ke: {{$a}}", - "addon.mod_lesson.attemptsremaining": "Anda masih memiliki {{$a}} percobaan", - "addon.mod_lesson.averagescore": "Nilai rata-rata", - "addon.mod_lesson.averagetime": "Waktu rata-rata", - "addon.mod_lesson.branchtable": "Tabel Percabangan", - "addon.mod_lesson.clusterjump": "Pertanyaan yang belum dilihat di dalam kelompok", - "addon.mod_lesson.completed": "Selesai", - "addon.mod_lesson.congratulations": "Selamat - Akhir dari pelajaran telah sampai", - "addon.mod_lesson.continue": "Melanjutkan", - "addon.mod_lesson.defaultessayresponse": "Essai Anda akan dinilai oleh instruktur kursus.", - "addon.mod_lesson.detailedstats": "Statistik yang terperinci", - "addon.mod_lesson.didnotanswerquestion": "Tidak menjawab pertanyaan ini.", - "addon.mod_lesson.displayofgrade": "Tampilan peringkat (hanya untuk siswa)", - "addon.mod_lesson.displayscorewithessays": "Anda mendapatkan skor {{$a.score}} dari total {{$a.tempmaxgrade}} untuk pertanyaan yang dinilai secara otomatis.
                {{$a.essayquestions}} pertanyaan essai Anda akan dinilai dan ditambahkan
                dengan skor akhir Anda pada tanggal yang ditentukan.

                Nilai Anda sekarang tanpa pertanyaan essai adalah {{$a.score}} dari total {{$a.grade}}", - "addon.mod_lesson.displayscorewithoutessays": "Nilai Anda adalah {{$a.score}} (dari {{$a.grade}}).", - "addon.mod_lesson.enterpassword": "Harap masukkan password:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Anda tidak menjawab soal apapun. Nilai Anda 0 untuk pelajaran ini.", - "addon.mod_lesson.errorprefetchrandombranch": "Pelajaran ini berisi lompatan ke halaman konten acak, ini tidak dapat dicoba di aplikasi sampai dimulai di web.", - "addon.mod_lesson.errorreviewretakenotlast": "Upaya ini tidak bisa lagi ditinjau ulang karena usaha lain telah selesai.", - "addon.mod_lesson.finishretakeoffline": "Upaya ini telah selesai secara offline.", - "addon.mod_lesson.firstwrong": "Sayang sekali Anda tidak mendapatkan satu poin ini, karena respon Anda tidak tepat. Apakah Anda tetap ingin menebak-nebak, hanya untuk kesenangan belaka (tetapi tidak untuk kredit poin)?", - "addon.mod_lesson.grade": "Nilai", - "addon.mod_lesson.highscore": "Daftar nilai tertinggi", - "addon.mod_lesson.hightime": "Waktu terlama", - "addon.mod_lesson.leftduringtimed": "Anda telah pergi sebelum waktu pelajaran usai.
                Klik pada Melanjutkan untuk mengulangi pelajaran.", - "addon.mod_lesson.leftduringtimednoretake": "Anda telah pergi sebelum waktu pelajaran usai dan Anda
                tidak dibolehkan untuk lanjut atau mengambil ulang pelajaran.", - "addon.mod_lesson.lessonmenu": "Menu pelajaran", - "addon.mod_lesson.lessonstats": "Statistik pelajaran", - "addon.mod_lesson.linkedmedia": "Media yang terhubung", - "addon.mod_lesson.loginfail": "Login gagal, coba lagi...", - "addon.mod_lesson.lowscore": "Daftar nilai terendah", - "addon.mod_lesson.lowtime": "Daftar waktu terendah", - "addon.mod_lesson.maximumnumberofattemptsreached": "Sudah mencapai jumlah maksimum dari percobaan-Lanjut ke halaman berikutnya", - "addon.mod_lesson.modattemptsnoteacher": "Siswa hanya boleh meninjau pekerjaan siswa.", - "addon.mod_lesson.modulenameplural": "Pelajaran", - "addon.mod_lesson.noanswer": "Tidak ada jawaban diberikan", - "addon.mod_lesson.nolessonattempts": "Belum ada percobaan yang dilakukan pada pelajaran kali ini.", - "addon.mod_lesson.notcompleted": "Tidak selesai", - "addon.mod_lesson.numberofcorrectanswers": "Jumlah jawaban yang benar: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Jumlah halaman yang dilihat: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Jumlah pertanyaan yang telah dijawab: {{$a.nquestions}}; (Anda seharusnya menjawab minimal: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Anda telah mendapat {{$a.score}} poin dari {{$a.currenthigh}} poin sejauh ini.", - "addon.mod_lesson.ongoingnormal": "Anda telah menjawab {{$a.correct}} soal benar dari {{$a.viewed}} kali percobaan.", - "addon.mod_lesson.or": "ATAU", - "addon.mod_lesson.overview": "Tinjau luas", - "addon.mod_lesson.preview": "Tinjau sebelumnya", - "addon.mod_lesson.question": "Pertanyaan", - "addon.mod_lesson.rawgrade": "Nilai buruk", - "addon.mod_lesson.reports": "Laporan", - "addon.mod_lesson.response": "Respon", - "addon.mod_lesson.retakefinishedinsync": "Sebuah upaya offline telah disinkronisasi. Apakah Anda ingin meninjaunya?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Tinjau ulang", - "addon.mod_lesson.reviewlesson": "Tinjau ulang pelajaran", - "addon.mod_lesson.reviewquestionback": "Ya, saya ingin mencoba lagi", - "addon.mod_lesson.reviewquestioncontinue": "Tidak, saya hanya ingin lanjut ke pertanyaan selamjutnya", - "addon.mod_lesson.secondpluswrong": "Belum tepat. Mau coba lagi?", - "addon.mod_lesson.teacherjumpwarning": "Lompatan {{$a.cluster}} atau lompatan {{$a.unseen}} sedang dipakai dalam pelajaran ini. Lompatan Halaman Selanjutnya akan digunakan otomatis. Login sebagai siswa untuk mengetesnya", - "addon.mod_lesson.teacherongoingwarning": "Nilai yang akan datang hanya ditampilkan untuk siswa. Login sebagai siswa untuk mngetesnya", - "addon.mod_lesson.teachertimerwarning": "Timer hanya bekerja untuk siswa. Tes timer dengan log in sebagai siswa.", - "addon.mod_lesson.thatsthecorrectanswer": "Itu adalah jawaban yang benar", - "addon.mod_lesson.thatsthewronganswer": "Itu adalah jawaban yang salah", - "addon.mod_lesson.timeremaining": "Waktu tersisa", - "addon.mod_lesson.timetaken": "Waktu yang terambil", - "addon.mod_lesson.unseenpageinbranch": "Pertanyaan yang belum dilihat di dalam percabangan", - "addon.mod_lesson.warningretakefinished": "Upaya telah selesai di website.", - "addon.mod_lesson.welldone": "Bagus!", - "addon.mod_lesson.youhaveseen": "Anda telah melihat lebih dari satu halaman dari pelajaran ini.
                Anda ingin mulai dari halaman terakhir yang Anda lihat?", - "addon.mod_lesson.youranswer": "Jawaban Anda", - "addon.mod_lesson.yourcurrentgradeisoutof": "Nilai Anda adalah {{$a.grade}} dari total {{$a.total}}", - "addon.mod_lesson.youshouldview": "Anda seharusnya menjawab sekurangnya: {{$a}}", - "addon.mod_lti.errorgetlti": "Terjadi kesalahan saat mendapatkan modul data.", - "addon.mod_lti.errorinvalidlaunchurl": "URL peluncuran tidak valid", - "addon.mod_lti.launchactivity": "Luncurkan kegiatan.", - "addon.mod_page.errorwhileloadingthepage": "Terjadi kesalahan saat memuat halaman konten.", - "addon.mod_quiz.attemptfirst": "Nilai Pertama", - "addon.mod_quiz.attemptlast": "Nilai Terakhir", - "addon.mod_quiz.attemptquiznow": "Mencoba kuis sekarang", - "addon.mod_quiz.cannotsubmitquizdueto": "Upaya kuis ini tidak dapat diajukan karena alasan berikut:", - "addon.mod_quiz.confirmcontinueoffline": "Upaya ini belum disinkronkan sejak {{$ a}}. Jika Anda terus melakukan upaya ini di perangkat lain sejak saat itu, Anda mungkin kehilangan data.", - "addon.mod_quiz.confirmleavequizonerror": "Terjadi kesalahan saat menyimpan jawabannya. Apakah Anda yakin ingin meninggalkan kuis?", - "addon.mod_quiz.confirmstart": "Waktu mengerjakan kuis adalah {{$a}}. Waktu akan menghitung mundur dari saat pertama Anda mengerjakan dan Anda harus mengirimkannya sebelum berakhir. Anda yakin akan memulai kuis sekarang?", - "addon.mod_quiz.confirmstartheader": "Kuis dengan batasan waktu", - "addon.mod_quiz.errorbehaviournotsupported": "Kuis ini tidak dapat dicoba di aplikasi karena tindakan tidak didukung oleh aplikasi:", - "addon.mod_quiz.errordownloading": "Terjadi kesalahan saat mengunduh data yang diperlukan.", - "addon.mod_quiz.errorgetattempt": "Terjadi kesalahan mendapatkan data percobaan", - "addon.mod_quiz.errorgetquestions": "Terjadi kesalahan mendapatkan pertanyaan.", - "addon.mod_quiz.errorgetquiz": "Terjadi kesalahan mendapatkan data kuis.", - "addon.mod_quiz.errorparsequestions": "Terjadi kesalahan ketika membaca pertanyaan ini. Silahkan upayakan kuis ini di web browser.", - "addon.mod_quiz.errorquestionsnotsupported": "Kuis ini tidak bisa diupayakan di aplikasi karena mengandung pertanyaan yang tidak didukung oleh aplikasi.", - "addon.mod_quiz.errorrulesnotsupported": "Kuis ini tidak dapat dicoba di aplikasi karena memiliki aturan akses yang tidak didukung oleh aplikasi:", - "addon.mod_quiz.errorsaveattempt": "Terjadi kesalahan saat menyimpan data percobaan.", - "addon.mod_quiz.feedback": "Saran", - "addon.mod_quiz.finishnotsynced": "Selesai tapi belum tersinkronisasi.", - "addon.mod_quiz.gradeaverage": "Nilai Rata-rata", - "addon.mod_quiz.gradehighest": "Nilai Tertinggi", - "addon.mod_quiz.grademethod": "Metode penilaian", - "addon.mod_quiz.marks": "Tanda", - "addon.mod_quiz.modulenameplural": "Kuis", - "addon.mod_quiz.noquestions": "Belum ada pertanyaan yang dimasukkan", - "addon.mod_quiz.opentoc": "Buka navigasi popover.", - "addon.mod_quiz.question": "Pertanyaan", - "addon.mod_quiz.quizpassword": "Password Kuis", - "addon.mod_quiz.requirepasswordmessage": "Diperlukan kata kunci untuk membuka kuis ini", - "addon.mod_quiz.review": "Ulasan", - "addon.mod_quiz.timeleft": "Waktu tersisa", - "addon.mod_quiz.timetaken": "Waktu yang digunakan", - "addon.mod_quiz.warningattemptfinished": "Upaya offline telah dibuang seperti telah selesai di situs atau tidak ditemukan.", - "addon.mod_quiz.warningdatadiscarded": "Beberapa jawaban offline telah dibuang karena pertanyaan telah dimodifikasi secara online.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Upaya tidak terselesaikan karena beberapa jawaban ofline telah dibuang. Harap tinjau kembali jawaban Anda kemudian submit ulang.", - "addon.mod_quiz.yourfinalgradeis": "Nilai akhir Anda untuk kuis ini adalah {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Terjadi kesalahan ketika memuat konten.", - "addon.mod_resource.openthefile": "Membuka file.", - "addon.mod_scorm.cannotcalculategrade": "Nilai tidak bisa dihitung.", - "addon.mod_scorm.dataattemptshown": "Data ini termasuk nomor percobaan {{number}}.", - "addon.mod_scorm.errorcreateofflineattempt": "Terjadi kesalahan saat membuat percobaan offline baru. Silahkan coba lagi", - "addon.mod_scorm.errordownloadscorm": "Terjadi kesalahan saat mengunduh SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Terjadi kesalahan saat mendapatkan data SCORM.", - "addon.mod_scorm.errorinvalidversion": "Maaf, aplikasi hanya mendukung SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Unduhan paket SCORM dinonaktifkan di situs Moodle Anda. Silahkan hubungi administrator situs Moodle Anda.", - "addon.mod_scorm.errornovalidsco": "SCORM ini tidak memiliki SCO yang terlihat untuk dimuat.", - "addon.mod_scorm.errorpackagefile": "Maaf, aplikasi ini hanya mendukung paket ZIP.", - "addon.mod_scorm.errorsyncscorm": "Terjadi kesalahan saat sinkronisasi. Silahkan coba lagi.", - "addon.mod_scorm.offlineattemptnote": "Upaya ini memiliki data yang belum tersinkronisasi.", - "addon.mod_scorm.offlineattemptovermax": "Upaya ini tidak dapat dikirim karena Anda melampaui jumlah upaya maksimal.", - "addon.mod_scorm.scormstatusnotdownloaded": "SCORM ini tidak diunduh Ini akan otomatis diunduh saat Anda membukanya.", - "addon.mod_scorm.scormstatusoutdated": "SCORM ini telah dimodifikasi sejak pengunduhan terakhir. Ini akan otomatis diunduh saat Anda membukanya.", - "addon.mod_scorm.warningofflinedatadeleted": "Beberapa upaya data offline {{number}} telah dihapus karena tidak dapat dibuat menjadi upaya baru.", - "addon.mod_scorm.warningsynconlineincomplete": "Beberapa upaya tidak bisa disinkronisasi dengan situs karena upaya online terakhir tidak selesai. Silahkan selesaikan upaya online terlebih dahulu.", - "addon.mod_survey.cannotsubmitsurvey": "Maaf, terdapat masalah saat mengumpulkan survey Anda. Silahkan coba lagi.", - "addon.mod_survey.errorgetsurvey": "Terjadi kesalahan mendapatkan data survey.", - "addon.mod_survey.ifoundthat": "Saya menemukan bahwa", - "addon.mod_survey.ipreferthat": "Saya lebih menyukai bahwa", - "addon.mod_survey.modulenameplural": "Survei", - "addon.mod_survey.results": "Hasil.", - "addon.mod_url.accessurl": "Akses URL", - "addon.mod_url.pointingtourl": "URL poin sumber ini ke", - "addon.mod_wiki.errorloadingpage": "Terjadi kesalahan saat memuat halaman.", - "addon.mod_wiki.errornowikiavailable": "Wiki belum memiliki konten.", - "addon.mod_wiki.gowikihome": "Ke Beranda Wiki.", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.titleshouldnotbeempty": "Judul tidak boleh kosong", - "addon.mod_wiki.viewpage": "Lihat halaman", - "addon.mod_wiki.wikipage": "Halaman Wiki", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Anda harus memilih salah satu dari item ini:", - "addon.notes.userwithid": "Pengguna dengan id {{id}}", - "addon.notes.warningnotenotsent": "Tidak bisa menambah catatan ke materi {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Terjadi kesalahan saat mendapatkan pemberitahuan.", - "addon.notifications.markallread": "Tandai semua telah dibaca", - "addon.notifications.notifications": "Pemberitahuan", - "addon.notifications.playsound": "Putar suara", - "addon.notifications.therearentnotificationsyet": "Tidak ada pemberitahuan", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Uni Emirat Arab", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua And Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antartika", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa Amerika", - "assets.countries.AT": "Austria", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Kepulauan Åland", - "assets.countries.AZ": "Azerbaijan", - "assets.countries.BA": "Bosnia Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgia", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei Darussalam", - "assets.countries.BO": "Bolivia", - "assets.countries.BQ": "Bonaire, Sint Eustatius dan Saba", - "assets.countries.BR": "Brasil", - "assets.countries.BS": "Bahama", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Pulau Bouvet", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Belarusia", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Kepulauan Cocos (Keeling)", - "assets.countries.CD": "Republik Demokratik Kongo", - "assets.countries.CF": "Afrika Tengah", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Swiss", - "assets.countries.CI": "Pantai Gading", - "assets.countries.CK": "Kepulauan Cook", - "assets.countries.CL": "Chili", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Cina", - "assets.countries.CO": "Kolombia", - "assets.countries.CR": "Kosta Rika", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Tanjung Verde", - "assets.countries.CW": "Curacao", - "assets.countries.CX": "Pulau Christmas", - "assets.countries.CY": "Siprus", - "assets.countries.CZ": "Republik Ceko", - "assets.countries.DE": "Jerman", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Denmark", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Republik Dominika", - "assets.countries.DZ": "Algeria", - "assets.countries.EC": "Ekuador", - "assets.countries.EE": "Estonia", - "assets.countries.EG": "Mesir", - "assets.countries.EH": "Sahara Barat", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spanyol", - "assets.countries.ET": "Ethiopia", - "assets.countries.FI": "Finlandia", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Kepulauan Falkland (Malvinas)", - "assets.countries.FM": "Federasi Mikronesia", - "assets.countries.FO": "Kepulauan Faroe", - "assets.countries.FR": "Perancis", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Inggris", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Guinea Prancis", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Greenland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Guinea Khatulistiwa", - "assets.countries.GR": "Yunani", - "assets.countries.GS": "Georgia Selatan dan Kepulauan Sandwich Selatan", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hongkong", - "assets.countries.HM": "Pulau Heard dan Kepulauan McDonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Kroasia", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Hungaria", - "assets.countries.ID": "Indonesia", - "assets.countries.IE": "Irlandia", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Isle Of Man", - "assets.countries.IN": "India", - "assets.countries.IO": "British Indian Ocean Territory", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran", - "assets.countries.IS": "Islandia", - "assets.countries.IT": "Italia", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaika", - "assets.countries.JO": "Yordania", - "assets.countries.JP": "Jepang", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirgistan", - "assets.countries.KH": "Kamboja", - "assets.countries.KI": "Kirbati", - "assets.countries.KM": "Komoro", - "assets.countries.KN": "Saint Kitts dan Nevis", - "assets.countries.KP": "Korea", - "assets.countries.KR": "Korea Selatan", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Kepulauan Cayman", - "assets.countries.KZ": "Kazakhstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Srilangka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lithuania", - "assets.countries.LU": "Luksemburg", - "assets.countries.LV": "Latvia", - "assets.countries.LY": "Libia", - "assets.countries.MA": "Moroko", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Moldova", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Kepulauan Marshall", - "assets.countries.MK": "Macedonia", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Northern Mariana Islands", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Moldova", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Meksiko", - "assets.countries.MY": "Malaysia", - "assets.countries.MZ": "Mozambik", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Kaledonia Baru", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Pulau Norfolk", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Belanda", - "assets.countries.NO": "Norwegia", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Selandia Baru", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Polinesia Prancis", - "assets.countries.PG": "Papua Nugini", - "assets.countries.PH": "Filipina", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polandia", - "assets.countries.PM": "Saint Pierre Dan Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Riko", - "assets.countries.PS": "Palesina", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Rumania", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Federasi Rusia", - "assets.countries.RW": "Ruwanda", - "assets.countries.SA": "Arab Saudi", - "assets.countries.SB": "Kepulauan Solomon", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Swedia", - "assets.countries.SG": "Singapura", - "assets.countries.SH": "Saint Helena", - "assets.countries.SI": "Slovenia", - "assets.countries.SJ": "Svalbard dan Jan Mayen", - "assets.countries.SK": "Slovakia", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Suriname", - "assets.countries.SS": "Sudan Selatan", - "assets.countries.ST": "Sao Tome Dan Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SY": "Syria", - "assets.countries.SZ": "Swaziland", - "assets.countries.TC": "Turks Dan Kepulauan Caicos", - "assets.countries.TD": "Chad", - "assets.countries.TF": "Teritori Prancis Selatan", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailand", - "assets.countries.TJ": "Tajikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunisia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turki", - "assets.countries.TT": "Trinidad Dan Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzania", - "assets.countries.UA": "Ukraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "Amerika Serikat", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Tahta Suci Vatikan", - "assets.countries.VC": "Saint Vincent And The Grenadines", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Kepulauan Virgin, Inggris", - "assets.countries.VI": "Kepulauan Virgin, Amerika Serikat", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis Dan Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yaman", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Afrika Selatan", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "core.accounts": "Akun", - "core.add": "Tambah", - "core.agelocationverification": "Verifikasi usia dan lokasi", - "core.ago": "{{$a}} yang lalu", - "core.all": "Semua", - "core.allgroups": "Semua grup", - "core.allparticipants": "Semua peserta", - "core.answer": "Jawaban", - "core.areyousure": "Anda yakin?", - "core.back": "Kembali", - "core.block.blocks": "Blok", - "core.cancel": "Batal", - "core.cannotconnect": "Tidak dapat terhubung: Verifikasi bahwa Anda telah mengetik dengan benar URL dan situs Anda menggunakan Moodle {{$a}} atau yang lebih baru.", - "core.cannotdownloadfiles": "Pengunduhan file dinonaktifkan di layanan Mobile Anda. Tolong, hubungi administrator situs Anda.", - "core.captureaudio": "Rekam audio", - "core.capturedimage": "Gambar diambil", - "core.captureimage": "Ambil gambar", - "core.capturevideo": "Rekam video", - "core.category": "Kategori", - "core.choose": "Pilih", - "core.choosedots": "Pilih...", - "core.clearsearch": "Hapus pencarian", - "core.clicktohideshow": "Klik untuk membuka atau menutup", - "core.clicktoseefull": "Klik untuk melihat konten lengkap", - "core.comments": "Komentar", - "core.comments.addcomment": "Tambahkan komentar...", - "core.comments.comments": "Komentar", - "core.comments.commentscount": "Komentar ({{$a}})", - "core.comments.deletecommentbyon": "Hapus komentar yang di posting oleh {{$a.user}} pada {{$a.time}}", - "core.comments.eventcommentcreated": "Komentar telah dibuat", - "core.comments.eventcommentdeleted": "Komentar telah dihapus", - "core.comments.nocomments": "Tidak ada komentar", - "core.comments.savecomment": "Simpan komentar", - "core.commentscount": "Komentar ({{$a}})", - "core.confirmcanceledit": "Apakah Anda yakin ingin meninggalkan halaman ini? Semua perubahan akan hilang.", - "core.confirmloss": "Apakah Anda yakin? Semua perubahan akan hilang", - "core.confirmopeninbrowser": "Apakah Anda ingin membukanya di browse?", - "core.considereddigitalminor": "Anda belum memiliki usia yang cukup untuk membuat akun di situs ini.", - "core.content": "Konten", - "core.contenteditingsynced": "Komentar yang Anda edit telah disinkronisasi.", - "core.contentlinks.chooseaccount": "Pilih akun", - "core.contentlinks.chooseaccounttoopenlink": "Pilih akun untuk membuka link.", - "core.contentlinks.confirmurlothersite": "Link milik situs lain. Apakah Anda ingin membukanya?", - "core.contentlinks.errornoactions": "Tidak dapat menemukan tindakan yang akan dilakukan dengan tautan ini.", - "core.contentlinks.errornosites": "Tidak dapat menemukan situs apapun untuk mengendalikan link ini.", - "core.continue": "lanjut", - "core.copiedtoclipboard": "Teks disalin ke clipboard", - "core.course": "Kursus", - "core.course.activitydisabled": "Organisasi Anda telah menonaktifkan aktifitas ini di aplikasi mobile.", - "core.course.activitynotyetviewableremoteaddon": "Organisasi Anda menginstall plugin yang belum didukung.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Instalasi Moodle organisasi Anda butuh pembaruan.", - "core.course.allsections": "Semua", - "core.course.askadmintosupport": "Hubungi admin situs dan sampaikan Anda ingin menggunakan aktifitas ini dengan aplikasi Moodle mobile.", - "core.course.confirmdeletemodulefiles": "Anda yakin ingin menghapus file modul ini?", - "core.course.confirmdownload": "Anda akan mengunduh {{size}}. Apa Anda ingin melanjutkan?", - "core.course.confirmdownloadunknownsize": "Kami tidak dapat menghitung ukuran unduhan. Anda yakin ingin mengunduh?", - "core.course.confirmpartialdownloadsize": "Anda akan mendownload at least {{size}}. Apakah anda yakin ingin melanjutkan?", - "core.course.contents": "Konten", - "core.course.couldnotloadsectioncontent": "Tidak dapat memuat bagian konten. Silahkan Coba lagi nanti.", - "core.course.couldnotloadsections": "Tidak dapat memuat bagian. Silahkan Coba lagi nanti.", - "core.course.coursesummary": "Ringkasan kursus", - "core.course.errordownloadingsection": "Terjadi kesalahan saat mengunduh bagian.", - "core.course.errorgetmodule": "Terjadi kesalahan saat mendapatkan data modul.", - "core.course.hiddenfromstudents": "Tersembunyi dari siswa", - "core.course.hiddenoncoursepage": "Tersedia tapi tidak ditampilkan di halaman kursus", - "core.course.nocontentavailable": "Tidak ada konten tersedia untuk saat ini.", - "core.course.overriddennotice": "Nilai akhir anda fari aktifitas ini telah disesuaikan secara manual.", - "core.course.sections": "Bagian", - "core.course.useactivityonbrowser": "Anda masih dapat menggunakannya dengan perangkat pencarian.", - "core.coursedetails": "Detil kursus", - "core.courses.allowguests": "Kursus ini membolehkan pengunjung tamu untuk masuk", - "core.courses.availablecourses": "Kursus yang tersedia", - "core.courses.cannotretrievemorecategories": "Kategori yang lebih dalam dari tingkat {{$ a}} tidak dapat diambil.", - "core.courses.categories": "Kategori Kursus", - "core.courses.confirmselfenrol": "Apakah Anda yakin ingin mendaftarkan diri mengikuti materi ini?", - "core.courses.courses": "Kursus", - "core.courses.enrolme": "Daftarkan saya", - "core.courses.errorloadcategories": "Terjadi kesalahan saat memuat kategori.", - "core.courses.errorloadcourses": "Terjadi kesalahan saat memuat materi", - "core.courses.errorsearching": "Terjadi kesalahan saat mencari.", - "core.courses.errorselfenrol": "Terjadi kesalahan saat mendaftarkan diri.", - "core.courses.filtermycourses": "Saring materi saya", - "core.courses.frontpage": "Halaman depan", - "core.courses.ignore": "Abaikan", - "core.courses.mycourses": "Kursus Yang Saya Ikuti", - "core.courses.mymoodle": "Dasbor", - "core.courses.nocourses": "Tidak ada informasi kursus untuk ditampilkan.", - "core.courses.nocoursesyet": "Belum ada kursus saat ini", - "core.courses.notenrollable": "Anda tidak dapat mendaftarkan diri ke materi.", - "core.courses.password": "kunci pendaftaran", - "core.courses.paymentrequired": "Kursus ini membutuhkan pembayaran untuk mengikutinya.", - "core.courses.reload": "Muat ulang", - "core.courses.search": "Cari", - "core.courses.searchcourses": "Cari kursus", - "core.courses.searchcoursesadvice": "Anda dapat menggunakan tombol pencarian materi untuk mengakses sebagai tamu atau mendaftarkan diri Anda dalam materi yang memungkinkannya.", - "core.courses.selfenrolment": "Pendaftaran sendiri", - "core.courses.totalcoursesearchresults": "Total materi {{$a}}", - "core.currentdevice": "Perangkat sekarang", - "core.datastoredoffline": "Data disimpan dalam perangkat karena tidak dapat terkirim. Data akan otomatis dikirim kemudian.", - "core.date": "Tanggal", - "core.day": "hari", - "core.days": "hari", - "core.decsep": ",", - "core.delete": "Hapus", - "core.deleting": "Menghapus", - "core.description": "Keterangan", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Digital minor", - "core.digitalminor_desc": "Harap meminta orang tua/wali untuk menghubungi:", - "core.discard": "Membuang", - "core.dismiss": "Menghilangkan", - "core.done": "Selesai", - "core.download": "Unduh", - "core.downloading": "Mengunduh", - "core.edit": "Ubah", - "core.emptysplit": "Halaman kosong akan muncul jika panel sebelah kiri kosong atau sedang memuat.", - "core.error": "Kesalahan", - "core.errorchangecompletion": "Terjadi kesalahan saat mengubah status kelengkapan. Silahkan coba lagi.", - "core.errordeletefile": "Terjadi kesalahan saat menghapus file. Silahkan coba lagi.", - "core.errordownloading": "Terjadi kesalahan saat mengunduh file", - "core.errordownloadingsomefiles": "Terjadi kesalahan saat mengunduh file modul. beberapa file mungkin hilang.", - "core.errorfileexistssamename": "Sudah ada file dengan nama ini.", - "core.errorinvalidform": "Form berisi data yang tidak valid. Pastikan untuk mengisi kolom wajib sehingga data menjadi valid.", - "core.errorinvalidresponse": "Respon tidak valid diterima. Silahkan kontak administrator Moodle Anda jika kesalahan berlanjut.", - "core.errorloadingcontent": "Terjadi kesalahan saat memuat konten.", - "core.erroropenfilenoapp": "Terjadi kesalahan saat membuka file: tidak ditemukan aplikasi untuk membuka jenis file ini.", - "core.erroropenfilenoextension": "Terjadi kesalahan saat membuka file: file tidak memiliki ekstensi", - "core.erroropenpopup": "Aktifitas ini mencoba membuka popup. Ini tidak didukung oleh aplikasi.", - "core.errorrenamefile": "Terjadi kesalahan saat mengubah nama file. silahkan coba lagi.", - "core.errorsync": "Sebuah kesalahan terjadi ketika sinkronisasi", - "core.errorsyncblocked": "{{$a}} ini tidak dapat disinkronisasi sekarang karena adanya proses yang sedang berlangsung. Silahkan coba lagi nanti. Jika masalah masih berlanjut, coba restart aplikasi.", - "core.explanationdigitalminor": "Informasi ini diperlukan untuk menentukan apakah usia Anda melebihi usia digital dari persetujuan. Ini adalah usia ketika seorang individu dapat menyetujui syarat dan ketentuan dan data mereka secara hukum disimpan dan diproses.", - "core.favourites": "Berbintang", - "core.filenameexist": "Nama file sudah tersedia: {{$a}}", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Anda akan mengunggah file {{size}}. Apakah Anda yakin ingin melanjutkan?", - "core.fileuploader.confirmuploadunknownsize": "Kami tidak dapat menghitung ukuran pengunggahan. Apakah Anda yakin ingin melanjutkan?", - "core.fileuploader.errorcapturingaudio": "Terjadi kesalahan saat pengambilan audio.", - "core.fileuploader.errorcapturingimage": "Terjadi kesalahan saat pengambilan gambar.", - "core.fileuploader.errorcapturingvideo": "Terjadi kesalahan saat pengambilan video.", - "core.fileuploader.errorgettingimagealbum": "Terjadi kesalahan saat mendapatkan gambar dari album.", - "core.fileuploader.errormustbeonlinetoupload": "Anda harus online untuk mengunggah file.", - "core.fileuploader.errornoapp": "Anda tidak memiliki aplikasi terinstal untuk menjalankan ini.", - "core.fileuploader.errorreadingfile": "Terjadi kesalahan saat memuat file.", - "core.fileuploader.errorwhileuploading": "Terjadi kesalahan saat mengunggah file.", - "core.fileuploader.file": "File", - "core.fileuploader.fileuploaded": "File berhasil diunggah.", - "core.fileuploader.maxbytesfile": "File terlalu besar {{$a.file}}. Ukuran maksimal yang dapat diunggah adalah {{$a.size}}.", - "core.fileuploader.photoalbums": "Album foto", - "core.fileuploader.readingfile": "Membaca file", - "core.fileuploader.selectafile": "Memilih sebuah file", - "core.fileuploader.uploadafile": "Mengunggah file", - "core.fileuploader.uploading": "Sedang mengunggah", - "core.fileuploader.uploadingperc": "Sedang mengunggah: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filter", - "core.folder": "Folder", - "core.forcepasswordchangenotice": "Anda harus mengubah kata sandi Anda untuk memproses lebih lanjut.", - "core.fulllistofcourses": "Materi Kursus yang sudah tersedia", - "core.fullnameandsitename": "{{namalengkap}} ({{{{namalengkap}} ({{namasitus}})namasitus}})", - "core.grades.average": "Rata-rata", - "core.grades.badgrade": "Nilai yang dimasukkan tidak benar", - "core.grades.contributiontocoursetotal": "Sumbangsih terhadap total kursus", - "core.grades.feedback": "Umpan balik", - "core.grades.grade": "Nilai", - "core.grades.gradeitem": "Butir nilai", - "core.grades.grades": "Nilai-nilai", - "core.grades.lettergrade": "Nilai huruf", - "core.grades.nogradesreturned": "Tidak ada nilai kembali", - "core.grades.nooutcome": "Tidak ada hasil", - "core.grades.percentage": "Persentase", - "core.grades.range": "Rentang", - "core.grades.rank": "Tingkat", - "core.grades.weight": "Bobot", - "core.group": "Grup", - "core.groupsseparate": "Grup terpisah", - "core.groupsvisible": "Grup yang terlihat", - "core.hasdatatosync": "{{$ A}} ini memiliki data offline yang akan disinkronkan.", - "core.help": "Bantuan", - "core.hide": "Sembunyikan", - "core.hour": "jam", - "core.hours": "jam", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Gambar", - "core.imageviewer": "Penampil gambar", - "core.info": "Informasi", - "core.labelsep": ":", - "core.lastaccess": "Terakhir akses", - "core.lastdownloaded": "Terakhir diunduh", - "core.lastmodified": "Terakhir diperbaharui", - "core.lastsync": "Terakhir disinkronisasi", - "core.list": "daftar", - "core.listsep": ";", - "core.loading": "Memuat", - "core.loadmore": "Muat lebih banyak", - "core.location": "Lokasi", - "core.login.auth_email": "Otentikasi berdasarkan Email", - "core.login.authenticating": "Mengotentikasi", - "core.login.cancel": "Batal", - "core.login.changepassword": "Ubah password", - "core.login.confirmdeletesite": "Apakah Anda yakin ingin menghapus situs {{sitename}}?", - "core.login.connect": "Terhubung!", - "core.login.connecttomoodle": "Terhubung ke Moodle", - "core.login.contactyouradministrator": "HUbungi Administrator Anda untuk bantuan lebih lanjut.", - "core.login.contactyouradministratorissue": "Silahkan bertanya kepada Administrator Anda untuk memeriksa masalah berikut: {{$a}}", - "core.login.createaccount": "Buat keanggotaan baru", - "core.login.createuserandpass": "Buat nama dan password pengguna baru untuk penggunaan login", - "core.login.credentialsdescription": "Silahkan masukkan username dan password untuk log in", - "core.login.emailconfirmsent": "

                Sebuah email telah dikirimkan ke alamat Anda pada {{$a}}\n

                yang berisi petunjuk ringkas untuk melengkapi pendaftaran Anda.\n

                Jika Anda menemukan kesulitan, hubungi administrator situs ini.

                ", - "core.login.emailconfirmsentsuccess": "Pengiriman surel konfirmasi sukses", - "core.login.emailnotmatch": "Email tidak sesuai", - "core.login.erroraccesscontrolalloworigin": "Panggilan Cross-Origin yang coba Anda lakukan telah ditolak. Harap periksa https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Terjadi kesalahan saat menghapus situs. Silahkan coba lagi.", - "core.login.errorupdatesite": "Terjadi kesalahan saat memperbaharui situs. Silahkan coba lagi.", - "core.login.firsttime": "Ini yang pertama kali Anda kesini?", - "core.login.forcepasswordchangenotice": "Anda harus mengubah kata sandi Anda untuk memproses lebih lanjut.", - "core.login.forgotten": "Lupa nama pengguna dan password Anda?", - "core.login.help": "Bantuan", - "core.login.helpmelogin": "

                Ada banyak situs Moodle di seluruh duinia. Aplikasi ini hanya bisa terhubung ke situs Moodle yang akses Aplikasi Mobilenya telah diaktifkan

                Jika Anda tidak bisa terhubung ke situs Moodle Anda maka Anda butuh menghubungi administrator di tempat dimana Anda ingin terhubung dan menginginkan mereka untuk membacanya http://docs.moodle.org/en/Mobile_app

                To test the app in a Moodle demo site type teacher or student in the Site address field and click the Connect button.

                ", - "core.login.instructions": "Instruksi", - "core.login.invalidaccount": "Silahkan periksa rincian login Anda atau tanyakan pada administrator situs Anda untuk memeriksa konfigurasi situs.", - "core.login.invaliddate": "Tanggal tidak valid", - "core.login.invalidemail": "Kesalahan pada alamat email", - "core.login.invalidmoodleversion": "Versi Moodle tidak valid.versi minimum yang dibutuhkan adalah {{$a}}.", - "core.login.invalidsite": "Situs URL tidak valid.", - "core.login.invalidtime": "Waktu tidak valid.", - "core.login.invalidvaluemax": "Maksimal nilai adalah {{$a}}", - "core.login.invalidvaluemin": "Minimum nilai adalah {{$a}}", - "core.login.localmobileunexpectedresponse": "Fitur Tambahan Moodle Mobile mengembalikan respons yang tak terduga, Anda akan diautentikasi menggunakan layanan Mobile standar.", - "core.login.loggedoutssodescription": "Anda harus mengautentikasi ulang. Anda harus log in ke situs di sebuah jendela browser.", - "core.login.login": "Masuk", - "core.login.loginbutton": "Masuk", - "core.login.logininsiterequired": "Anda butuh log in ke situs di jendela browser.", - "core.login.loginsteps": "Untuk akses penuh ke situs ini, Anda harus membuat akun terlebih dahulu.", - "core.login.missingemail": "Alamat email tiak diisi", - "core.login.missingfirstname": "Nama depan tidak diisi", - "core.login.missinglastname": "Nama akhir tidak diisi", - "core.login.mobileservicesnotenabled": "Layanan Mobile tidak aktif di situs Anda. Silahkan hubungi kontak administrator jika menurut Anda akses mobile perlu diaktifkan.", - "core.login.mustconfirm": "Anda perlu mengkonfirmasi akun Anda", - "core.login.newaccount": "Anggota baru", - "core.login.notloggedin": "Anda butuh log in.", - "core.login.password": "Password", - "core.login.passwordforgotten": "Password yang terlupakan", - "core.login.passwordforgotteninstructions2": "Untuk mereset password Anda, masukkan nama pengguna atau alamat email Anda di bawah. Jika kami dapat menemukan data Anda di database, email akan dikirimkan ke alamat email Anda, dengan instruksi bagaimana untuk mendapatkan akses kembali.", - "core.login.passwordrequired": "Kata sandi dibutuhkan.", - "core.login.policyaccept": "Saya mengerti dan setuju", - "core.login.policyagree": "Anda harus setuju pada kebijakan ini untuk menggunakan situs. Apakah anda setuju?", - "core.login.policyagreement": "Kesepakatan kebijakan situs", - "core.login.policyagreementclick": "Tautan kesepakatan kebijakan situs", - "core.login.potentialidps": "Log in menggunakan akun Anda pada:", - "core.login.profileinvaliddata": "Nilai tidak valid", - "core.login.recaptchachallengeimage": "reCAPTCHA challenge image", - "core.login.reconnect": "Sambungkan kembali", - "core.login.reconnectdescription": "Token otentikasi Anda tidak valid atau telah kedaluwarsa, Anda harus menyambung kembali ke situs.", - "core.login.reconnectssodescription": "Token otentikasi Anda tidak valid atau telah kedaluwarsa, Anda harus menyambung kembali ke situs. Anda perlu masuk ke situs di jendela browser.", - "core.login.resendemail": "Kirim ulang surel", - "core.login.security_question": "Pertanyaan keamanan", - "core.login.selectacountry": "Pilih Negara", - "core.login.signupplugindisabled": "{{$a}} tidak aktif.", - "core.login.siteaddress": "Alamat situs", - "core.login.siteinmaintenance": "Situs Anda sedang dalam mode perbaikan", - "core.login.sitepolicynotagreederror": "Kebijakan situs tidak disepakati.", - "core.login.siteurl": "URL situs", - "core.login.siteurlrequired": "URL situs diperlukan, misal http://www.yourmoodlesite.abc atau https: //www.yourmoodlesite.efg ", - "core.login.startsignup": "Silahkan daftar untuk jadi anggota baru!", - "core.login.stillcantconnect": "Masih belum dapat terhubung?", - "core.login.supplyinfo": "Silahkan masukkan beberapa informasi mengenai diri Anda", - "core.login.username": "Nama Pengguna", - "core.login.usernameoremail": "Masukkan nama pengguna atau alamat email", - "core.login.usernamerequired": "Username dibutuhkan", - "core.login.visitchangepassword": "Apakah Anda ingin mengunjungi situs untuk mengganti password?", - "core.login.webservicesnotenabled": "Layanan Web tidak aktif di situs Anda. silahkan hubungi kontak Administrator Moodle Anda jika Anda merasa perlu mgeaktifkan akses Mobile.", - "core.lostconnection": "Token otentikasi Anda tidak valid atau telah kedaluwarsa, Anda harus menyambung kembali ke situs.", - "core.mainmenu.changesite": "Mengubah situs", - "core.mainmenu.help": "Bantuan", - "core.mainmenu.logout": "Keluar", - "core.mainmenu.website": "Situs web", - "core.maxsizeandattachments": "Ukuran maksimal untuk file baru: {{$a.size}}, lampiran maksimum: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Lain-lain", - "core.mod_assignment": "Penugasan 2.2 (Non-aktif)", - "core.mod_book": "Buku", - "core.mod_chat": "Obrolan", - "core.mod_file": "File", - "core.mod_forum": "Forum", - "core.mod_lesson": "Pelajaran", - "core.mod_quiz": "Kuis", - "core.mod_survey": "Survei", - "core.moduleintro": "Deskripsi", - "core.more": "selanjutnya", - "core.name": "Nama", - "core.networkerrormsg": "Terdapat masalah untuk terhubung ke situs. Silahkan periksa koneksi Anda dan coba lagi.", - "core.never": "Tidak pernah", - "core.next": "Selanjutnya", - "core.no": "Tidak", - "core.nocomments": "Tidak ada komentar", - "core.nograde": "Tidak ada tingkat", - "core.none": "Tidak ada", - "core.nopasswordchangeforced": "Anda tidak dapat memproses tanpa mengganti password.", - "core.noresults": "Tidak ada hasil", - "core.noselection": "Tidak ada pilihaan", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Profil ini tidak tersedia karena pengguna belum didaftarkan di kursus ini.", - "core.notice": "Pemberitahuan", - "core.notingroup": "Maaf, tetapi Anda harus menjadi bagian dari grup untuk melihat halaman ini.", - "core.notsent": "Tidak terkirim", - "core.now": "sekarang", - "core.numwords": "{{$a}} kata", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Klik disini untuk melihat gambar dengan ukuran penuh", - "core.openinbrowser": "Buka di browser", - "core.pagea": "Halaman {{$a}}", - "core.paymentinstant": "Gunakan tombol di bawah ini untuk membayar dan didaftarkan dalam hitungan menit!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telepon", - "core.pictureof": "Gambar dari {{$a}}", - "core.previous": "Sebelumnya", - "core.proceed": "Lanjut", - "core.pulltorefresh": "Tarik untuk muat ulang", - "core.question.answer": "Jawaban", - "core.question.answersaved": "Jawaban disimpan", - "core.question.complete": "Selesai", - "core.question.correct": "Benar", - "core.question.errorattachmentsnotsupported": "Aplikasi ini belum mendukung pelampiran file untuk menjawab.", - "core.question.errorinlinefilesnotsupported": "Aplikasi ini tidak mendukung penyuntingan file inline.", - "core.question.errorquestionnotsupported": "Tipe pertanyaan ini tidak didukung oleh aplikasi: {{$a}}.", - "core.question.feedback": "Umpan balik", - "core.question.howtodraganddrop": "Ketuk untuk memilih lalu tekan untuk menjatuhkan.", - "core.question.incorrect": "Tidak benar", - "core.question.information": "Informasi", - "core.question.invalidanswer": "Jawaban tidak lengkap", - "core.question.notanswered": "Tidak dijawab", - "core.question.notyetanswered": "Belum dijawab", - "core.question.partiallycorrect": "Separuh benar", - "core.question.questionmessage": "Pertanyaan {{$a}}: {{$b}}", - "core.question.questionno": "Soal {{$a}}", - "core.question.requiresgrading": "Meminta penilaian", - "core.quotausage": "Saat ini Anda telah menggunakan {{$a.used}} dari batas {{$a.total}} Anda.", - "core.rating.aggregateavg": "Rata-rata penilaian", - "core.rating.aggregatecount": "Hitungan penilaian", - "core.rating.aggregatemax": "Penilaian maksimum", - "core.rating.aggregatemin": "Penilaian minimum", - "core.rating.aggregatesum": "Jumlah seluruh penilaian", - "core.rating.noratings": "Tidak ada penilaian yang telah diberikan", - "core.rating.rating": "Penilaian", - "core.rating.ratings": "Penilaian-penilaian", - "core.redirectingtosite": "Anda akan diarahkan ke situs.", - "core.refresh": "Refresh", - "core.remove": "Hapus", - "core.required": "Diwajibkan", - "core.requireduserdatamissing": "Pengguna ini tidak memiliki beberapa data profil yang dibutuhkan. Tolong isi data ini di Moodle Anda dan coba lagi.
                {{$ a}}", - "core.resourcedisplayopen": "Buka", - "core.resources": "Sumber", - "core.restore": "Kembalikan", - "core.restricted": "Terbatas", - "core.retry": "Coba lagi", - "core.save": "Simpan", - "core.savechanges": "Simpan perubahan", - "core.search": "Cari", - "core.searching": "Mencari", - "core.searchresults": "Hasil pencarian", - "core.sec": "detik", - "core.secs": "detik", - "core.seemoredetail": "Klik disini untuk melihat rinciannya", - "core.selectacategory": "Silahkan pilih kategori", - "core.selectacourse": "Pilih kursus", - "core.selectagroup": "Pilih grup", - "core.sending": "Mengirimkan", - "core.settings.about": "Tentang", - "core.settings.cannotsyncoffline": "Tidak bisa melakukan sinkronisasi secara offline.", - "core.settings.cannotsyncwithoutwifi": "Tidak dapat mensinkronisasi karena pengaturan saat ini hanya mengizikan sinkronisasi melalui Wi-Fi. Silahkan terhubung ke Wi-Fi", - "core.settings.cordovadevicemodel": "Cordova Device model", - "core.settings.cordovadeviceosversion": "Versi Cordova Device OS", - "core.settings.cordovadeviceplatform": "Cordova Device platform", - "core.settings.cordovadeviceuuid": "Cordova Device uuid", - "core.settings.cordovaversion": "Versi Cordova", - "core.settings.currentlanguage": "Bahasa saat ini", - "core.settings.debugdisplay": "Tampilkan pesan debug", - "core.settings.deletesitefiles": "Apa Anda yakin ingin menghapus file yang diunduh dari situs '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Hapus situs file", - "core.settings.deviceinfo": "Hapus info", - "core.settings.deviceos": "Device OS", - "core.settings.displayformat": "Tampilkan format", - "core.settings.enabledownloadsection": "Aktifkan download bagian", - "core.settings.enablerichtexteditor": "Aktifkan rich text editor", - "core.settings.enablerichtexteditordescription": "Jika diaktifkan, rich text editor akan ditampilkan di tempat yang memungkinkan.", - "core.settings.enablesyncwifi": "Izinkan sinkronisasi hanya saat terkoneksi Wi-Fi", - "core.settings.errordeletesitefiles": "Terjadi kesalahan saat menghapus file situs.", - "core.settings.errorsyncsite": "Kesalahan menyinkronkan data situs, periksa koneksi internet Anda dan coba lagi.", - "core.settings.estimatedfreespace": "Perkiraan ruang kosong", - "core.settings.filesystemroot": "Root sistem file", - "core.settings.general": "Umum", - "core.settings.language": "Bahasa", - "core.settings.license": "Lisensi", - "core.settings.localnotifavailable": "Pemberitahuan lokal tersedia", - "core.settings.locationhref": "URL Webview", - "core.settings.locked": "Terkunci", - "core.settings.loggedin": "Online", - "core.settings.navigatorlanguage": "Bahasa navigasi", - "core.settings.navigatoruseragent": "Agen pengguna navigasi", - "core.settings.networkstatus": "Status koneksi internet", - "core.settings.preferences": "Preferensi", - "core.settings.privacypolicy": "Kebijakan pribadi", - "core.settings.reportinbackground": "Laporkan kesalahan secara otomatis", - "core.settings.settings": "Pengaturan", - "core.settings.sites": "Situs", - "core.settings.spaceusage": "Penggunaan ruang", - "core.settings.synchronization": "Sinkronisasi", - "core.settings.synchronizenow": "Sinkronisasi sekarang", - "core.settings.syncsettings": "Pengaturan sinkronisasi", - "core.settings.total": "Total", - "core.settings.wificonnection": "Koneksi Wifi", - "core.sharedfiles.chooseaccountstorefile": "Pilih akun untuk menyimpan file di dalamnya", - "core.sharedfiles.chooseactionrepeatedfile": "Sudah ada file dengan nama ini. Apakah Anda ingin mengganti file yang ada atau mengganti nama menjadi \"{{$ a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Tidak ada situs tersimpan. Silahkan tambahkan situs sebelum berbagi file dengan aplikasi ini", - "core.sharedfiles.nosharedfiles": "Tidak ada file bersama yang disimpan di situs ini.", - "core.sharedfiles.nosharedfilestoupload": "Anda tidak memiliki file untuk diunggah disini. Jika Anda ingin mengunggah file dari aplikasi lain, Cari file itu dan klik tombol 'Open in'.", - "core.sharedfiles.rename": "Ganti Nama", - "core.sharedfiles.replace": "Ganti", - "core.sharedfiles.sharedfiles": "File bersama", - "core.sharedfiles.successstorefile": "File berhasil disimpan. Sekarang Anda dapat memilih file ini untuk diunggah ke file pribadi Anda atau melampirkannya pada beberapa kegiatan.", - "core.show": "Tampilkan", - "core.showless": "Tampikan lebih sedikit ...", - "core.showmore": "Tampilkan lebih banyak ...", - "core.site": "Situs", - "core.sitehome.sitehome": "Beranda situs", - "core.sitehome.sitenews": "Pengumuman situs", - "core.sitemaintenance": "Situs ini sedang dalam pemeliharaan dan saat ini tidak tersedia", - "core.sizeb": "Byte", - "core.sizegb": "Gb", - "core.sizekb": "Kb", - "core.sizemb": "Mb", - "core.sizetb": "TB", - "core.sorry": "Maaf...", - "core.sort": "Sortir", - "core.sortby": "Urutan berdasarkan", - "core.strftimedate": "%d %B %Y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "Sampaikan", - "core.success": "Sukses", - "core.tablet": "Tablet", - "core.tag.tag": "Tag", - "core.tag.tags": "Tag", - "core.teachers": "Para Pengajar", - "core.thereisdatatosync": "Ada offline {{$ a}} yang akan disinkronkan.", - "core.thisdirection": "ltr", - "core.time": "Waktu", - "core.today": "Hari ini", - "core.tryagain": "Coba lagi", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "Uh oh!", - "core.unexpectederror": "Terdapat kesalahan yang tidak diinginkan. Silahkan tutup dan buka kembali apliksai untuk mencoba lagi.", - "core.unicodenotsupported": "Beberapa emotikon tidak didukung di aplikasi ini. Beberapa karakter akan dihapus ketika pesan terkirim.", - "core.unicodenotsupportedcleanerror": "Teks kosong ditemukan saat membersihkan karakter Unicode.", - "core.unknown": "Tidak diketahui", - "core.unlimited": "Tidak terbatas", - "core.unzipping": "Membuka zip", - "core.user": "Pengguna", - "core.user.address": "Alamat", - "core.user.city": "Kota", - "core.user.contact": "Kontak", - "core.user.country": "Negara", - "core.user.description": "Keterangan", - "core.user.detailsnotavailable": "Rincian pengguna ini tidak tersedia untuk Anda", - "core.user.editingteacher": "Pengajar", - "core.user.email": "Alamat Email", - "core.user.emailagain": "Email (lagi)", - "core.user.firstname": "Nama Depan", - "core.user.interests": "Minat", - "core.user.lastname": "Nama akhir", - "core.user.manager": "Manajer", - "core.user.newpicture": "Foto baru", - "core.user.participants": "Peserta", - "core.user.phone1": "Telepon", - "core.user.phone2": "Telepon selular", - "core.user.roles": "Peran", - "core.user.sendemail": "Email", - "core.user.student": "Siswa", - "core.user.teacher": "Pengajar yang tidak mengedit", - "core.user.webpage": "Halaman Web", - "core.userdeleted": "Anggota ini telah dihapus", - "core.userdetails": "Detail pengguna", - "core.users": "Pengguna", - "core.view": "Lihat", - "core.viewprofile": "Tampilkan profil", - "core.warningofflinedatadeleted": "Data offline {{component}} '{{name}}' sudah dihapus. {{error}}", - "core.whatisyourage": "Berapa umur Anda?", - "core.wheredoyoulive": "Di negara mana Anda tinggal?", - "core.whoops": "Ups!", - "core.whyisthishappening": "Kenapa ini terjadi?", - "core.whyisthisrequired": "Mengapa ini diperlukan?", - "core.wsfunctionnotavailable": "Fungsi layanan web ini tidak tersedia.", - "core.year": "Tahun", - "core.years": "Tahun", - "core.yes": "Ya" -} \ No newline at end of file diff --git a/src/assets/lang/it.json b/src/assets/lang/it.json deleted file mode 100644 index 3adebfa03..000000000 --- a/src/assets/lang/it.json +++ /dev/null @@ -1,1976 +0,0 @@ -{ - "addon.badges.alignment": "Equivalenza", - "addon.badges.badgedetails": "Dettagli badge", - "addon.badges.badges": "Badge", - "addon.badges.bendorsement": "Garanzia di terzi", - "addon.badges.claimcomment": "Commento della garanzia di terzi", - "addon.badges.claimid": "URL di verifica", - "addon.badges.contact": "Contatto", - "addon.badges.dateawarded": "Data di rilascio", - "addon.badges.expired": "Scaduto", - "addon.badges.expirydate": "Data di scadenza", - "addon.badges.imageauthoremail": "Email dell'autore dell'immagine", - "addon.badges.imageauthorname": "Nome dell'autore dell'immagine", - "addon.badges.imageauthorurl": "URL dell'autore dell'immagine", - "addon.badges.imagecaption": "Didascalia dell'immagine", - "addon.badges.issuancedetails": "Scadenza badge", - "addon.badges.issuerdetails": "Dettagli di chi rilascia il badge", - "addon.badges.issueremail": "Email", - "addon.badges.issuername": "Nome di chi rilascia il badge", - "addon.badges.issuerurl": "URL di di chi rilascia il basge", - "addon.badges.language": "Lingua", - "addon.badges.noalignment": "Il badge non ha equivalenze con standard o abilità esterni.", - "addon.badges.nobadges": "Non sono presenti badge.", - "addon.badges.norelated": "Il badge non ha badge correlati", - "addon.badges.recipientdetails": "Dettagli destinatario", - "addon.badges.relatedbages": "Badge correlati", - "addon.badges.version": "Versione", - "addon.badges.warnexpired": "(Questo badge è scaduto!)", - "addon.block_activitymodules.pluginname": "Attività", - "addon.block_activityresults.pluginname": "Valutazioni delle attività", - "addon.block_badges.pluginname": "I miei nuovi badge", - "addon.block_blogmenu.pluginname": "Menu blog", - "addon.block_blogrecent.pluginname": "Interventi Blog recenti", - "addon.block_blogtags.pluginname": "Tag dei blog", - "addon.block_calendarmonth.pluginname": "Calendario", - "addon.block_calendarupcoming.pluginname": "Prossimi eventi", - "addon.block_comments.pluginname": "Commenti", - "addon.block_completionstatus.pluginname": "Completamento corso", - "addon.block_glossaryrandom.pluginname": "Voce casuale di glossario", - "addon.block_learningplans.pluginname": "Piani di formazione", - "addon.block_myoverview.all": "Tutti (eccetto eliminati dalla visualizzazione)", - "addon.block_myoverview.allincludinghidden": "Tutti", - "addon.block_myoverview.favourites": "Preferiti", - "addon.block_myoverview.future": "Futuri", - "addon.block_myoverview.hiddencourses": "Eliminati dalla visualizzazione", - "addon.block_myoverview.inprogress": "In svolgimento", - "addon.block_myoverview.lastaccessed": "Ultima visita", - "addon.block_myoverview.morecourses": "Altri corsi", - "addon.block_myoverview.nocourses": "Non ci sono corsi", - "addon.block_myoverview.past": "Conclusi", - "addon.block_myoverview.pluginname": "Panoramica corsi", - "addon.block_myoverview.shortname": "Titolo abbreviato", - "addon.block_myoverview.title": "Titolo del corso", - "addon.block_newsitems.pluginname": "Annunci recenti", - "addon.block_onlineusers.pluginname": "Utenti online", - "addon.block_privatefiles.pluginname": "File personali", - "addon.block_recentactivity.pluginname": "Attività recente", - "addon.block_recentlyaccessedcourses.nocourses": "Non ci sono corsi visitati recentemente", - "addon.block_recentlyaccessedcourses.pluginname": "Corsi visitati recentemente", - "addon.block_recentlyaccesseditems.noitems": "Nessun elemento recente", - "addon.block_recentlyaccesseditems.pluginname": "Elementi recenti", - "addon.block_rssclient.pluginname": "Feed RSS remoto", - "addon.block_selfcompletion.pluginname": "Conferma di completamento", - "addon.block_sitemainmenu.pluginname": "Menu principale", - "addon.block_starredcourses.nocourses": "Non ci sono corsi preferiti", - "addon.block_starredcourses.pluginname": "Corsi preferiti", - "addon.block_tags.pluginname": "Tag", - "addon.block_timeline.duedate": "Data di scadenza", - "addon.block_timeline.next30days": "Prossimi 30 giorni", - "addon.block_timeline.next3months": "Prossimi 3 mesi", - "addon.block_timeline.next6months": "Prossimi 6 mesi", - "addon.block_timeline.next7days": "Prossimi 7 giorni", - "addon.block_timeline.nocoursesinprogress": "Non ci sono corsi in svolgimento", - "addon.block_timeline.noevents": "Non ci sono attività in scadenza", - "addon.block_timeline.overdue": "In ritardo", - "addon.block_timeline.pluginname": "Cronologia", - "addon.block_timeline.sortbycourses": "Ordina per corso", - "addon.block_timeline.sortbydates": "Ordina per data", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Interventi blog", - "addon.blog.errorloadentries": "Si è verificato un errore durante il caricamento del blog.", - "addon.blog.linktooriginalentry": "Collegamento all'intervento originale", - "addon.blog.noentriesyet": "Non ci sono interventi visibili", - "addon.blog.publishtonoone": "Personale (Bozza)", - "addon.blog.publishtosite": "Su questo sito", - "addon.blog.publishtoworld": "Su tutto il web", - "addon.blog.showonlyyourentries": "Visualizza solo i tuoi inserimenti", - "addon.blog.siteblogheading": "Blog del sito", - "addon.calendar.allday": "Tutto il giorno", - "addon.calendar.calendar": "Calendario", - "addon.calendar.calendarevent": "Eventi del calendario", - "addon.calendar.calendarevents": "Eventi del calendario", - "addon.calendar.calendarreminders": "Promemoria del calendario", - "addon.calendar.categoryevents": "Eventi di categoria", - "addon.calendar.confirmeventdelete": "Sei sicuro di eliminare l'evento \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "L'evento \"{{$a.name}}\" fa parte di una serie di eventi. Vuoi eliminare soloq uesto evento o tutta le serie di {{$a.count}} eventi?", - "addon.calendar.courseevents": "Eventi di corso", - "addon.calendar.currentmonth": "Mese corrente", - "addon.calendar.daynext": "Giorno successivo", - "addon.calendar.dayprev": "Giorno precedente", - "addon.calendar.defaultnotificationtime": "Orario di notifica di default", - "addon.calendar.deleteallevents": "Elimina tutti gli eventi", - "addon.calendar.deleteevent": "Elimina evento", - "addon.calendar.deleteoneevent": "Elimina evento", - "addon.calendar.durationminutes": "Durata in minuti", - "addon.calendar.durationnone": "Senza durata", - "addon.calendar.durationuntil": "Fino al", - "addon.calendar.editevent": "Modifica evento", - "addon.calendar.errorloadevent": "Si è verificato un errore durante il caricamento degli eventi.", - "addon.calendar.errorloadevents": "Si è verificato un errore durante il caricamento degli eventi.", - "addon.calendar.eventcalendareventdeleted": "Eliminato evento di calendario", - "addon.calendar.eventduration": "Durata", - "addon.calendar.eventendtime": "Scadenza", - "addon.calendar.eventkind": "Tipo di evento", - "addon.calendar.eventname": "Titolo dell'evento", - "addon.calendar.eventstarttime": "Ora d'inizio", - "addon.calendar.eventtype": "Tipo di evento", - "addon.calendar.fri": "Ven", - "addon.calendar.friday": "Venerdì", - "addon.calendar.gotoactivity": "Vai all'attività", - "addon.calendar.groupevents": "Eventi di gruppo", - "addon.calendar.invalidtimedurationminutes": "La durata in minuti dell'evento non è valida. La durata in minuti deve essere maggiore di 0, oppure non selezionare una durata.", - "addon.calendar.invalidtimedurationuntil": "La data e l'orario di fine evento sono precedenti alla data di inizio. Per proseguire devi correggere questi valori.", - "addon.calendar.mon": "Lun", - "addon.calendar.monday": "Lunedi", - "addon.calendar.monthlyview": "Vista mensile", - "addon.calendar.newevent": "Nuovo evento", - "addon.calendar.noevents": "Non ci sono eventi", - "addon.calendar.nopermissiontoupdatecalendar": "Non sei autorizzato ad aggiornare gli eventi nel calendario", - "addon.calendar.reminders": "Promemoria", - "addon.calendar.repeatedevents": "Evento ripetuto", - "addon.calendar.repeateditall": "Applica i cambiamenti anche a tutti gli altri {{$a}} eventi presenti in questa serie", - "addon.calendar.repeateditthis": "Applica cambiamenti solo per questo evento", - "addon.calendar.repeatevent": "Ripeti l'evento", - "addon.calendar.repeatweeksl": "Ripetizioni settimanali", - "addon.calendar.sat": "Sab", - "addon.calendar.saturday": "Sabato", - "addon.calendar.setnewreminder": "Imposta un nuovo promemoria", - "addon.calendar.siteevents": "Eventi del sito", - "addon.calendar.sun": "Dom", - "addon.calendar.sunday": "Domenica", - "addon.calendar.thu": "Gio", - "addon.calendar.thursday": "Giovedì", - "addon.calendar.today": "Oggi", - "addon.calendar.tomorrow": "Domani", - "addon.calendar.tue": "Mar", - "addon.calendar.tuesday": "Martedì", - "addon.calendar.typecategory": "Evento di categoria", - "addon.calendar.typeclose": "Chiudi evento", - "addon.calendar.typecourse": "Evento di corso", - "addon.calendar.typedue": "Evento in scadenza", - "addon.calendar.typegradingdue": "Valutazione in scadenza", - "addon.calendar.typegroup": "Evento di gruppo", - "addon.calendar.typeopen": "Apri evento", - "addon.calendar.typesite": "Evento globale", - "addon.calendar.typeuser": "Evento dell'utente", - "addon.calendar.upcomingevents": "Prossimi eventi", - "addon.calendar.userevents": "Eventi dell'utente", - "addon.calendar.wed": "Mer", - "addon.calendar.wednesday": "Mercoledì", - "addon.calendar.when": "Quando", - "addon.calendar.yesterday": "Ieri", - "addon.competency.activities": "Attività", - "addon.competency.competencies": "Competenze", - "addon.competency.competenciesmostoftennotproficientincourse": "Competenze del corso dove più frequentemente non sono stati raggiunti i livelli di esperto", - "addon.competency.coursecompetencies": "Competenze del corso", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Le valutazioni delle competenze nel corso non si riflettono nei piani di formazione.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Le valutazione delle competenze nel corso si riflettono immediatamente nei piani di formazione.", - "addon.competency.crossreferencedcompetencies": "Competenze con riferimento incrociato", - "addon.competency.duedate": "Data di scadenza", - "addon.competency.errornocompetenciesfound": "Non sono state trovate competenze", - "addon.competency.evidence": "Attestazione", - "addon.competency.evidence_competencyrule": "La regola della competenza è stata soddisfatta.", - "addon.competency.evidence_coursecompleted": "Il corso '{{$a}}' è stato completato.", - "addon.competency.evidence_coursemodulecompleted": "L'attività '{{$a}}' è stata completato.", - "addon.competency.evidence_courserestored": "La valutazione è stata ripristinata assieme al corso '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "L'attestazione della formazione pregressa '{{$a}}' è stata collegata.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "L'attestazione della formazione pregressa '{{$a}}' è stata scollegata.", - "addon.competency.evidence_manualoverride": "La valutazione della competenza è stata data manualmente.", - "addon.competency.evidence_manualoverrideincourse": "La valutazione della competenza nel corso '{{$a}}' è stata data manualmente.", - "addon.competency.evidence_manualoverrideinplan": "La valutazione della competenza nel piano di formazione '{{$a}}' è stata data manualmente.", - "addon.competency.learningplancompetencies": "Competenze del piano di formazione", - "addon.competency.learningplans": "Piani di formazione", - "addon.competency.myplans": "I miei piani di formazione", - "addon.competency.noactivities": "Nessuna attività.", - "addon.competency.nocompetencies": "Non sono presenti competenze", - "addon.competency.nocompetenciesincourse": "Questo corso non è collegato a competenze", - "addon.competency.nocrossreferencedcompetencies": "Non ci sono competenze con riferimenti incrociati a questa competenza", - "addon.competency.noevidence": "Non sono presenti attestazioni.", - "addon.competency.noplanswerecreated": "Non sono stati creati piani di formazione", - "addon.competency.nouserplanswithcompetency": "Nessun piano di formazione contiene la competenza.", - "addon.competency.path": "Percorso:", - "addon.competency.planstatusactive": "Attivo", - "addon.competency.planstatuscomplete": "Raggiunta", - "addon.competency.planstatusdraft": "Bozza", - "addon.competency.planstatusinreview": "In revisione", - "addon.competency.planstatuswaitingforreview": "In attesa di revisione", - "addon.competency.proficient": "Esperto", - "addon.competency.progress": "Avanzamento", - "addon.competency.rating": "Valutazione", - "addon.competency.reviewstatus": "Stato della revisione", - "addon.competency.status": "Stato", - "addon.competency.template": "Modello di piano di formazione", - "addon.competency.uponcoursecompletion": "Al completamento del corso:", - "addon.competency.usercompetencystatus_idle": "Non attiva", - "addon.competency.usercompetencystatus_inreview": "In revisione", - "addon.competency.usercompetencystatus_waitingforreview": "In attesa di revisione", - "addon.competency.userplans": "Piani di formazione", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} competenze su {{$a.y}} sono a livello di esperto", - "addon.competency.xcompetenciesproficientoutofyincourse": "Possiedi un livello di esperto in {{$a.x}} competenze su {{$a.y}} competenze di questo corso.", - "addon.coursecompletion.complete": "Completato", - "addon.coursecompletion.completecourse": "Conferma il completamento del corso", - "addon.coursecompletion.completed": "Completato", - "addon.coursecompletion.completiondate": "Data di completamento", - "addon.coursecompletion.completionmenuitem": "Completamento", - "addon.coursecompletion.couldnotloadreport": "Non è stato possibile caricare il report di completamento del corso, per favore riprova più tardi.", - "addon.coursecompletion.coursecompletion": "Criteri di completamento", - "addon.coursecompletion.criteria": "Criteri", - "addon.coursecompletion.criteriagroup": "Gruppo di criteri", - "addon.coursecompletion.criteriarequiredall": "E' richiesto il soddisfacimento di tutti i criteri elencati", - "addon.coursecompletion.criteriarequiredany": "E' richiesto il soddisfacimento di almeno uno dei criteri elencati", - "addon.coursecompletion.inprogress": "In corso", - "addon.coursecompletion.manualselfcompletion": "Conferma personale di completamento", - "addon.coursecompletion.nottracked": "In questo corso il tuo stato di completamento non è tracciato.", - "addon.coursecompletion.notyetstarted": "Non ancora iniziato", - "addon.coursecompletion.pending": "In attesa", - "addon.coursecompletion.required": "Obbligatorio", - "addon.coursecompletion.requiredcriteria": "Criteri da soddisfare", - "addon.coursecompletion.requirement": "Requisito", - "addon.coursecompletion.status": "Stato", - "addon.coursecompletion.viewcoursereport": "Visualizza il report del corso", - "addon.files.couldnotloadfiles": "Non è stato possibile caricare l'elenco dei file.", - "addon.files.emptyfilelist": "Non ci sono file da visualizzare.", - "addon.files.erroruploadnotworking": "Al momento non è possibile caricare file sul sito.", - "addon.files.files": "File", - "addon.files.privatefiles": "File personali", - "addon.files.sitefiles": "File del sito", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Configura dsipositivi", - "addon.messages.acceptandaddcontact": "Accetta ed aggiungi ai contatti", - "addon.messages.addcontact": "Aggiungi contatto", - "addon.messages.addcontactconfirm": "Sei sicuro di aggiungere {{$a}} ai contatti?", - "addon.messages.addtofavourites": "Contrassegna conversazione", - "addon.messages.addtoyourcontacts": "Aggiungi ai contatti", - "addon.messages.blocknoncontacts": "Evita messaggi da parte di utenti che non fanno parte dei miei contatti", - "addon.messages.blockuser": "Blocca utente", - "addon.messages.blockuserconfirm": "Sei sicuro di bloccare {{$a}}?", - "addon.messages.contactableprivacy": "Accetta messaggi da:", - "addon.messages.contactableprivacy_coursemember": "I miei contatti e i miei compagni di corso", - "addon.messages.contactableprivacy_onlycontacts": "I miei contatti solamente", - "addon.messages.contactableprivacy_site": "Tutti gli utenti del sito", - "addon.messages.contactblocked": "Contatto bloccato", - "addon.messages.contactlistempty": "L'elenco dei contatti è vuoto", - "addon.messages.contactname": "Nome del contatto", - "addon.messages.contactrequestsent": "Richiesta di contatto inviata", - "addon.messages.contacts": "Contatti", - "addon.messages.decline": "Rifiuta", - "addon.messages.deleteallconfirm": "Sei sicuro di eliminare l'intera conversazione? La conversazione non sarà eliminata per gli altri partecipanti.", - "addon.messages.deleteallselfconfirm": "Sei sicuro di eliminare tutta la conversazione personale?", - "addon.messages.deleteconversation": "Elimina conversazione", - "addon.messages.deleteforeveryone": "Elimina per me e per tutti gli altri", - "addon.messages.deletemessage": "Elimina messaggio", - "addon.messages.deletemessageconfirmation": "Sei sicuro di eliminare il messaggio? Verrà eliminato soltanto dalla tua conversazione, ma sarà ancora visibile dall'utente che ha ricevuto o inviato il messaggio.", - "addon.messages.errordeletemessage": "Si è verificato un errore durante l'eliminazione del messaggio.", - "addon.messages.errorwhileretrievingcontacts": "Si è verificato un errore durante la ricezione dei contatti dal server.", - "addon.messages.errorwhileretrievingdiscussions": "Si è verificato un errore durante la ricezione delle discussioni dal server.", - "addon.messages.errorwhileretrievingmessages": "Si è verificato un errore durante la ricezione dei messaggi dal server.", - "addon.messages.errorwhileretrievingusers": "Si è verificato un errore durante il caricamento degli utenti dal server.", - "addon.messages.groupconversations": "Gruppo", - "addon.messages.groupinfo": "Informazioni sul gruppo", - "addon.messages.individualconversations": "privato", - "addon.messages.info": "Informazioni utente", - "addon.messages.isnotinyourcontacts": "{{$a}} non è nei tuoi contatti", - "addon.messages.message": "Messaggio", - "addon.messages.messagenotsent": "Il messaggio non è stato inviato, per favore riprova più tardi.", - "addon.messages.messagepreferences": "Preferenze messaggi", - "addon.messages.messages": "Messaggi", - "addon.messages.muteconversation": "Silenzia", - "addon.messages.mutedconversation": "Conversazione silenziata", - "addon.messages.newmessage": "Nuovo messaggio", - "addon.messages.newmessages": "Nuovi messaggi", - "addon.messages.nocontactrequests": "Nessuna richiesta di contatto", - "addon.messages.nocontactsgetstarted": "Nessun contatto", - "addon.messages.nofavourites": "Nessuna conversazione contrassegnata", - "addon.messages.nogroupconversations": "Nessuna conversazione di gruppo", - "addon.messages.noindividualconversations": "Nessuna conversazione privata", - "addon.messages.nomessagesfound": "Non è stato trovato nessun messaggio", - "addon.messages.noncontacts": "Non tra i contatti", - "addon.messages.nousersfound": "Non sono stati trovati utenti", - "addon.messages.numparticipants": "{{$a}} partecipanti", - "addon.messages.removecontact": "Elimina contatto", - "addon.messages.removecontactconfirm": "Sei sicuro di eliminare {{$a}} dai contatti?", - "addon.messages.removefromfavourites": "Rimuovi conversazione dalle preferite", - "addon.messages.removefromyourcontacts": "Elimina dai contatti", - "addon.messages.requests": "Richieste", - "addon.messages.searchcombined": "Cerca persone e messaggi", - "addon.messages.selfconversation": "Spazio personale", - "addon.messages.sendcontactrequest": "Invia richiesta di contatto", - "addon.messages.showdeletemessages": "Visualzza messaggi eliminati", - "addon.messages.type_blocked": "Bloccato", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Risultati della ricerca", - "addon.messages.type_strangers": "Altri", - "addon.messages.unabletomessage": "Non puoi inviare messaggi a questo utente", - "addon.messages.unblockuser": "Sblocca utente", - "addon.messages.unblockuserconfirm": "Sei sicuro di sbloccare {{$a}}?", - "addon.messages.useentertosend": "Tasto invio per spedire", - "addon.messages.userwouldliketocontactyou": "{{$a}} desidera entrare in contatto", - "addon.messages.warningmessagenotsent": "Non è stato possibile inviare messaggi all'utente {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Desidera entrare in contatto", - "addon.messages.you": "Tu:", - "addon.messages.youhaveblockeduser": "Hai bloccato l'utente.", - "addon.messages.yourcontactrequestpending": "La richiesta di contatto con {{$a}} è in attesa", - "addon.mod_assign.addattempt": "Consenti tentativo ulteriore", - "addon.mod_assign.addnewattempt": "Aggiungi tentativo", - "addon.mod_assign.addnewattemptfromprevious": "Aggiungi tentativo sulla base della consegna precedente", - "addon.mod_assign.addsubmission": "Aggiungi consegna", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "I dettagli del compito ed il form di consegna saranno disponibili a partire dal {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Inizio consegne", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Questo compito accetta consegne a partire dal {{$a}}", - "addon.mod_assign.applytoteam": "Usa lo stesso feedback e la stessa valutazione per tutto il gruppo", - "addon.mod_assign.assignmentisdue": "Consegna compito", - "addon.mod_assign.attemptnumber": "Numero tentativo", - "addon.mod_assign.attemptreopenmethod": "Riapertura tentativo", - "addon.mod_assign.attemptreopenmethod_manual": "Manualmente", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automaticamente fino al superamento", - "addon.mod_assign.attemptsettings": "Impostazioni tentativo", - "addon.mod_assign.cannotgradefromapp": "Alcuni metodi di valutazione non sono ancora supportati dall'app e non possono essere modificati.", - "addon.mod_assign.confirmsubmission": "Sei sicuro di voler consegnare il tuo lavoro per farlo valutare? La consegna non potrà più essere modificata.", - "addon.mod_assign.currentattempt": "Tentativo {{$a}}.", - "addon.mod_assign.currentattemptof": "Tentativo {{$a.attemptnumber}} (Tentativi consentiti: {{$a.maxattempts}}).", - "addon.mod_assign.currentgrade": "Voto nel Registro valutatore", - "addon.mod_assign.cutoffdate": "Data limite", - "addon.mod_assign.defaultteam": "Gruppo di default", - "addon.mod_assign.duedate": "Termine consegne", - "addon.mod_assign.duedateno": "Senza termine consegne", - "addon.mod_assign.duedatereached": "La data di scadenza del compito è già trascorsa", - "addon.mod_assign.editingstatus": "Possibilità di modifica", - "addon.mod_assign.editsubmission": "Modifica consegna", - "addon.mod_assign.extensionduedate": "Data scadenza proroga", - "addon.mod_assign.feedbacknotsupported": "Questo feedback non è supportato dalla app e può non contenere tutte le informazioni.", - "addon.mod_assign.grade": "Valutazione", - "addon.mod_assign.graded": "Valutata", - "addon.mod_assign.gradedby": "Valutatore", - "addon.mod_assign.gradedfollowupsubmit": "Valutata - segue ricezione consegna", - "addon.mod_assign.gradedon": "Data di valutazione", - "addon.mod_assign.gradelocked": "Questa valutazione è bloccata oppure modificata nel registro valutatore.", - "addon.mod_assign.gradenotsynced": "Valutazione non sincronizzata", - "addon.mod_assign.gradeoutof": "Punteggio (su {{$a}})", - "addon.mod_assign.gradingstatus": "Stato valutazione", - "addon.mod_assign.groupsubmissionsettings": "Impostazioni consegna di gruppo", - "addon.mod_assign.hiddenuser": "Partecipante", - "addon.mod_assign.latesubmissions": "Consegne in ritardo", - "addon.mod_assign.latesubmissionsaccepted": "Consentito fino al {{$a}}", - "addon.mod_assign.markingworkflowstate": "Stato del flusso di lavoro della valutazione", - "addon.mod_assign.markingworkflowstateinmarking": "In valutazione", - "addon.mod_assign.markingworkflowstateinreview": "In revisione", - "addon.mod_assign.markingworkflowstatenotmarked": "Non valutata", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Pronta per il rilascio", - "addon.mod_assign.markingworkflowstatereadyforreview": "Valutazione completata", - "addon.mod_assign.markingworkflowstatereleased": "Rilasciata", - "addon.mod_assign.modulenameplural": "Compiti", - "addon.mod_assign.multipleteams": "Appartieni a più gruppi", - "addon.mod_assign.multipleteams_desc": "Il compito prevede la consegna di gruppo ma tu sei membro di più di un gruppo. Per poter consegnare, devi essere membro di un solo gruppo. Per favore contatta il docente per modificare la tua appartenenza ai gruppi.", - "addon.mod_assign.noattempt": "Nessun tentativo", - "addon.mod_assign.nomoresubmissionsaccepted": "Consentito solamente ai partecipanti ai quali è stata concessa una proroga", - "addon.mod_assign.noonlinesubmissions": "Questo compito non richiede consegne online", - "addon.mod_assign.nosubmission": "Non sono presenti consegne da valutare", - "addon.mod_assign.notallparticipantsareshown": "I partecipanti che non hanno consegnato non sono visualizzati.", - "addon.mod_assign.noteam": "Non appartieni a nessun gruppo", - "addon.mod_assign.noteam_desc": "Il compito prevede la consegna di gruppo ma tu non sei membro di nessun gruppo e non puoi effettuare consegne. Per favore contatta il docente affinché ti inserisca in un gruppo.", - "addon.mod_assign.notgraded": "Non valutata", - "addon.mod_assign.numberofdraftsubmissions": "Bozze", - "addon.mod_assign.numberofparticipants": "Partecipanti", - "addon.mod_assign.numberofsubmissionsneedgrading": "In attesa di valutazione", - "addon.mod_assign.numberofsubmittedassignments": "Consegne", - "addon.mod_assign.numberofteams": "Gruppi", - "addon.mod_assign.numwords": "{{$a}} parole", - "addon.mod_assign.outof": "{{$a.current}} su {{$a.total}}", - "addon.mod_assign.overdue": "Consegna in ritardo da: {{$a}}", - "addon.mod_assign.submission": "Consegna", - "addon.mod_assign.submissioneditable": "Lo studente può modificare la consegna", - "addon.mod_assign.submissionnoteditable": "Gli studenti non possono modificare la consegna", - "addon.mod_assign.submissionnotsupported": "Questa consegna non è supportata dalla app e può non contenere tutte le informazioni.", - "addon.mod_assign.submissionslocked": "Il compito non accetta consegne", - "addon.mod_assign.submissionstatus": "Stato consegna", - "addon.mod_assign.submissionstatus_": "Nessuna consegna", - "addon.mod_assign.submissionstatus_draft": "Bozza (non consegnato)", - "addon.mod_assign.submissionstatus_marked": "Valutato", - "addon.mod_assign.submissionstatus_new": "Nessuna consegna", - "addon.mod_assign.submissionstatus_reopened": "Riaperto", - "addon.mod_assign.submissionstatus_submitted": "Consegnato per la valutazione", - "addon.mod_assign.submissionstatusheading": "Stato consegna", - "addon.mod_assign.submissionteam": "Gruppo", - "addon.mod_assign.submitassignment": "Consegna compito", - "addon.mod_assign.submitassignment_help": "Dopo aver consegnato il compito non potrai più modificarlo", - "addon.mod_assign.submittedearly": "Il compito è stato consegnato {{$a}} in anticipo", - "addon.mod_assign.submittedlate": "Il compito è stato consegnato {{$a}} in ritardo", - "addon.mod_assign.timemodified": "Ultima modifica", - "addon.mod_assign.timeremaining": "Tempo rimasto", - "addon.mod_assign.ungroupedusers": "L'impostazione 'Consegna di gruppo obbligatoria' è abilitata ma alcuni utenti non fanno parte di gruppi o fanno parte di più gruppi e pertanto non potranno effettuare consegne.", - "addon.mod_assign.ungroupedusersoptional": "L'impostazione 'Consegna di gruppo' è abilitata ma alcuni utenti non sono membri di nessun gruppo, o sono membri di più gruppi. Questi studenti consegnano come membri del 'Gruppo di default'.", - "addon.mod_assign.unlimitedattempts": "Illimitati", - "addon.mod_assign.userswhoneedtosubmit": "Utenti che non hanno consegnato: {{$a}}", - "addon.mod_assign.userwithid": "Utente con ID {{id}}", - "addon.mod_assign.viewsubmission": "Visualizza consegne", - "addon.mod_assign.warningsubmissiongrademodified": "La valutazione dell'utente è stata modificata sul sito.", - "addon.mod_assign.warningsubmissionmodified": "La consegna dell'utente è stata modificata sul sito.", - "addon.mod_assign.wordlimit": "Limite parole", - "addon.mod_assign_feedback_comments.pluginname": "Commenti", - "addon.mod_assign_feedback_editpdf.pluginname": "Annotazione PDF", - "addon.mod_assign_feedback_file.pluginname": "File di commento", - "addon.mod_assign_submission_comments.pluginname": "Commenti alle consegne", - "addon.mod_assign_submission_file.pluginname": "Consegna file", - "addon.mod_assign_submission_onlinetext.pluginname": "Consegne testo online", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Questo compito ha un limite di parole pari a {{$a.limit}}, il tuo compito contiene {{$a.count}} parole. Per favore rivedi il compito prima di consegnarlo.", - "addon.mod_book.errorchapter": "Si è verificato un errore durante la lettura di un capitolo del libro", - "addon.mod_book.modulenameplural": "Libri", - "addon.mod_book.navnexttitle": "Successivo: {{$a}}", - "addon.mod_book.navprevtitle": "Precedente: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Capitolo dei libri", - "addon.mod_book.toc": "Sommario", - "addon.mod_chat.beep": "Beep", - "addon.mod_chat.chatreport": "Sessioni di chat", - "addon.mod_chat.currentusers": "Utenti attivi", - "addon.mod_chat.enterchat": "Entra nella chat", - "addon.mod_chat.entermessage": "Inserisci il tuo messaggio", - "addon.mod_chat.errorwhileconnecting": "Si è verificato un errore durante il collegamento alla chat.", - "addon.mod_chat.errorwhilegettingchatdata": "Si è verificato un errore durante la ricezione dei dati della chat.", - "addon.mod_chat.errorwhilegettingchatusers": "Si è verificato un errore durante la ricezione degli utenti della chat.", - "addon.mod_chat.errorwhileretrievingmessages": "Si è verificato un errore durante la ricezione dei messaggi dal server.", - "addon.mod_chat.errorwhilesendingmessage": "Si è verificato un errore durante l'invio del messaggio.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} richiama tutti!", - "addon.mod_chat.messagebeepsyou": "{{$a}} ti ha richiamato!", - "addon.mod_chat.messageenter": "{{$a}} è entrato nella chat", - "addon.mod_chat.messageexit": "{{$a}} ha lasciato la chat", - "addon.mod_chat.messages": "Messaggi", - "addon.mod_chat.messageyoubeep": "Hai richiamato {{$a}}", - "addon.mod_chat.modulenameplural": "Chat", - "addon.mod_chat.mustbeonlinetosendmessages": "Per inviare messaggi devi essere online.", - "addon.mod_chat.nomessages": "Non ci sono ancora messaggi", - "addon.mod_chat.nosessionsfound": "Non è stata trovata nessuna sezione", - "addon.mod_chat.saidto": "ha detto a", - "addon.mod_chat.send": "Invia", - "addon.mod_chat.sessionstart": "La prossima sessione di chat inizierà il {{$a.date}}, ({{$a.fromnow}} da adesso)", - "addon.mod_chat.showincompletesessions": "Visualizza sessioni non completate", - "addon.mod_chat.talk": "Parla", - "addon.mod_chat.viewreport": "Visualizza le sessioni già svolte", - "addon.mod_choice.cannotsubmit": "Si è verificato un errore durante l'invio della scelta. Per favore riprova.", - "addon.mod_choice.choiceoptions": "Opzioni scelta", - "addon.mod_choice.errorgetchoice": "Si è verificato un errore durante la ricezione di dati della scelta.", - "addon.mod_choice.expired": "L'attività è stata chiusa il {{$a}}.", - "addon.mod_choice.full": "(Completo)", - "addon.mod_choice.modulenameplural": "Scelte", - "addon.mod_choice.noresultsviewable": "I risultati non sono al momento visualizzabili.", - "addon.mod_choice.notopenyet": "L'attività non sarà disponibile fino al {{$a}}", - "addon.mod_choice.numberofuser": "Numero di risposte", - "addon.mod_choice.numberofuserinpercentage": "Percentuale delle risposte", - "addon.mod_choice.previewonly": "Questa è un'anteprima delle scelte disponibili. Potrai inviare la tua scelta solo dal {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "I risultati saranno pubblicati in forma anonima dopo la risposta.", - "addon.mod_choice.publishinfoanonclose": "I risultati saranno pubblicati in forma anonima dopo la chiusura dell'attività.", - "addon.mod_choice.publishinfofullafter": "I risultati delle scelte degli utenti saranno pubblicati dopo la tua risposta.", - "addon.mod_choice.publishinfofullclose": "I risultati delle scelte degli utenti saranno pubblicati dopo la chiusura dell'attività.", - "addon.mod_choice.publishinfonever": "I risultati delle scelte non saranno pubblicati dopo la tua risposta.", - "addon.mod_choice.removemychoice": "Elimina la mia scelta", - "addon.mod_choice.responses": "Scelte", - "addon.mod_choice.responsesresultgraphdescription": "Il {{number}}% ha scelto l'opzione {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Visualizzazione grafico", - "addon.mod_choice.savemychoice": "Salva la mia scelta", - "addon.mod_choice.userchoosethisoption": "Scelte degli utenti", - "addon.mod_choice.yourselection": "La tua selezione", - "addon.mod_data.addentries": "Aggiungi record", - "addon.mod_data.advancedsearch": "Ricerca avanzata", - "addon.mod_data.alttext": "Testo alternativo", - "addon.mod_data.approve": "Approva", - "addon.mod_data.approved": "Approvato", - "addon.mod_data.ascending": "Crescente", - "addon.mod_data.authorfirstname": "Nome autore", - "addon.mod_data.authorlastname": "Cognome autore", - "addon.mod_data.confirmdeleterecord": "Stai per eliminare questo record. Ne sei certo?", - "addon.mod_data.descending": "Decrescente", - "addon.mod_data.disapprove": "Disapprova", - "addon.mod_data.edittagsnotsupported": "L'app non supporta la modifica di tag", - "addon.mod_data.emptyaddform": "Non hai riempito nessun campo!", - "addon.mod_data.entrieslefttoadd": "Per poter visualizzare i record inseriti dagli altri partecipanti è necessario inserire altri {{$a.entriesleft}} record.", - "addon.mod_data.entrieslefttoaddtoview": "Devi aggiungere {{$a.entrieslefttoview}} altri record prima di poter vedere i record degli altri partecipanti.", - "addon.mod_data.errorapproving": "Si è verificato un errore durante l'approvazione o disapprovazione del record.", - "addon.mod_data.errordeleting": "Si è verificato un errore durante l'eliminazione del record.", - "addon.mod_data.errormustsupplyvalue": "Devi inserire un valore.", - "addon.mod_data.expired": "Spiacente, l'attività non è più disponibile poiché è stata chiusa il {{$a}} ", - "addon.mod_data.fields": "Campi", - "addon.mod_data.foundrecords": "Record trovati: {{$a.num}}/{{$a.max}} (Reset filtri)", - "addon.mod_data.latlongboth": "Devi compilare sia la latitudine sia la longitudine.", - "addon.mod_data.locationpermissiondenied": "L'accesso alla tua posizione è stato negato.", - "addon.mod_data.menuchoose": "Scegli...", - "addon.mod_data.modulenameplural": "Database", - "addon.mod_data.more": "Dettagli", - "addon.mod_data.mylocation": "La mia posizione", - "addon.mod_data.nomatch": "Non è stato trovato nessun record che corrisponda!", - "addon.mod_data.norecords": "Nessun record è presente nel Database", - "addon.mod_data.notapproved": "Il record inserito è in attesa di essere approvato.", - "addon.mod_data.notopenyet": "Spiacente, questa attività non sarà disponibile prima del {{$a}}", - "addon.mod_data.numrecords": "{{$a}} record", - "addon.mod_data.other": "Altro", - "addon.mod_data.recordapproved": "Record approvati", - "addon.mod_data.recorddeleted": "Il record è stato eliminato", - "addon.mod_data.recorddisapproved": "Record disapprovato", - "addon.mod_data.resetsettings": "Reimposta filtri", - "addon.mod_data.search": "Cerca", - "addon.mod_data.selectedrequired": "Necessari tutti i selezionati", - "addon.mod_data.single": "Dettagli", - "addon.mod_data.tagarea_data_records": "Record", - "addon.mod_data.timeadded": "Data/ora inserimento", - "addon.mod_data.timemodified": "Data/ora modifica", - "addon.mod_data.usedate": "Includi nella ricerca.", - "addon.mod_feedback.analysis": "Analisi", - "addon.mod_feedback.anonymous": "Anonimo", - "addon.mod_feedback.anonymous_entries": "Compilazioni anonime ({{$a}})", - "addon.mod_feedback.average": "Media", - "addon.mod_feedback.complete_the_form": "Compila il feedback", - "addon.mod_feedback.completed_feedbacks": "Risposte inviate", - "addon.mod_feedback.continue_the_form": "Continua a compilare il feedback", - "addon.mod_feedback.feedback_is_not_open": "Il feedback non è aperto", - "addon.mod_feedback.feedback_submitted_offline": "Il feedback è stato salvato e sarà inviato più tardi.", - "addon.mod_feedback.feedbackclose": "Chiusura", - "addon.mod_feedback.feedbackopen": "Apertura", - "addon.mod_feedback.mapcourses": "Associa feedback ai corsi", - "addon.mod_feedback.maximal": "Massimo", - "addon.mod_feedback.minimal": "Minimo", - "addon.mod_feedback.mode": "Modalità", - "addon.mod_feedback.modulenameplural": "Feedback", - "addon.mod_feedback.next_page": "Pagina successiva", - "addon.mod_feedback.non_anonymous": "Il nome del partecipante verrà registrato e visualizzato nelle risposte", - "addon.mod_feedback.non_anonymous_entries": "Risposte non anonime ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Studenti che non hanno risposto ({{$a}})", - "addon.mod_feedback.not_selected": "Nessuna scelta", - "addon.mod_feedback.not_started": "Non iniziato", - "addon.mod_feedback.numberoutofrange": "Il numero è fuori scala", - "addon.mod_feedback.overview": "Panoramica", - "addon.mod_feedback.page_after_submit": "Messaggio da visualizzare dopo la compilazione", - "addon.mod_feedback.preview": "Anteprima", - "addon.mod_feedback.previous_page": "Pagina precedente", - "addon.mod_feedback.questions": "Domande", - "addon.mod_feedback.response_nr": "Risposta numero", - "addon.mod_feedback.responses": "Risposte", - "addon.mod_feedback.save_entries": "Invia le risposte", - "addon.mod_feedback.show_entries": "Risposte", - "addon.mod_feedback.show_nonrespondents": "Risposte mancanti", - "addon.mod_feedback.started": "Aperto", - "addon.mod_feedback.this_feedback_is_already_submitted": "Hai già completato questa attività.", - "addon.mod_folder.emptyfilelist": "Non ci sono file da visualizzare.", - "addon.mod_folder.modulenameplural": "Cartelle", - "addon.mod_forum.addanewdiscussion": "Aggiungi un argomento di discussione", - "addon.mod_forum.addanewquestion": "Aggiungi nuova domanda", - "addon.mod_forum.addanewtopic": "Aggiungi nuovo argomento", - "addon.mod_forum.addtofavourites": "Inserisci tra le preferite", - "addon.mod_forum.advanced": "Avanzata", - "addon.mod_forum.cannotadddiscussion": "Per aggiungere discussioni in questo forum è necessario appartenere ad un gruppo.", - "addon.mod_forum.cannotadddiscussionall": "Non hai il permesso per aggiungere un argomento di discussione per tutti i partecipanti.", - "addon.mod_forum.cannotcreatediscussion": "Non è stato possibile creare una nuova discussione", - "addon.mod_forum.couldnotadd": "Non è possibile aggiungere l'intervento a causa di un errore sconosciuto.", - "addon.mod_forum.couldnotupdate": "Non è possibile aggiornare l'intervento a causa di un errore sconosciuto.", - "addon.mod_forum.cutoffdatereached": "Non è più possibile intervenire nel forum poiché è trascorsa la data limite.", - "addon.mod_forum.delete": "Elimina", - "addon.mod_forum.deletedpost": "L'intervento è stato eliminato.", - "addon.mod_forum.deletesure": "Sei sicuro di voler eliminare questo intervento?", - "addon.mod_forum.discussion": "Discussione", - "addon.mod_forum.discussionlistsortbycreatedasc": "Ordina per data di creazione, ascendente", - "addon.mod_forum.discussionlistsortbycreateddesc": "Ordina per data di creazione, discendente", - "addon.mod_forum.discussionlistsortbylastpostasc": "Ordina per data dell'intervento più recente, ascendente", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Ordina per data dell'intervento più recente, discendente", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Ordina per numero di risposte, ascendente", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Ordina per numero di risposte, discendente", - "addon.mod_forum.discussionlocked": "La discussione è stata bloccata e non è più possibile intervenire.", - "addon.mod_forum.discussionpinned": "In evidenza", - "addon.mod_forum.discussionsubscription": "Sottoscrizione della discussione", - "addon.mod_forum.edit": "Modifica", - "addon.mod_forum.erroremptymessage": "Il corpo del messaggio non può essere vuoto", - "addon.mod_forum.erroremptysubject": "L'oggetto non può essere vuoto", - "addon.mod_forum.errorgetforum": "Si è verificato un errore durante la ricezione dei dati del forum.", - "addon.mod_forum.errorgetgroups": "Si è verificato un errore durante la ricezione delle impostazioni gruppo.", - "addon.mod_forum.errorposttoallgroups": "Non è stato possibile creare la discussione in tutti i gruppi.", - "addon.mod_forum.favouriteupdated": "La preferenza è stata aggiornata.", - "addon.mod_forum.forumnodiscussionsyet": "In questo forum non sono presenti discussioni.", - "addon.mod_forum.group": "Gruppo", - "addon.mod_forum.lastpost": "Ultimo intervento", - "addon.mod_forum.lockdiscussion": "Blocca la discussione", - "addon.mod_forum.lockupdated": "Il bloccaggio è stato aggiornato.", - "addon.mod_forum.message": "Messaggio", - "addon.mod_forum.modeflatnewestfirst": "Visualizza le repliche in formato lineare, con le più recenti all'inizio", - "addon.mod_forum.modeflatoldestfirst": "Visualizza le repliche in formato lineare, con le più vecchie all'inizio", - "addon.mod_forum.modenested": "Visualizza le repliche in formato nidificato", - "addon.mod_forum.modulenameplural": "Forum", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discussioni", - "addon.mod_forum.numreplies": "{{numreplies}} interventi", - "addon.mod_forum.pindiscussion": "Metti la discussione in evidenza", - "addon.mod_forum.pinupdated": "L'evidenza della discussione è stata aggiornata.", - "addon.mod_forum.postisprivatereply": "Questo intervento è una risposta in privato e non è visibile ad altri utenti.", - "addon.mod_forum.posttoforum": "Invia al forum", - "addon.mod_forum.posttomygroups": "Intervento in tutti i gruppi", - "addon.mod_forum.privatereply": "Rispondi privatamente", - "addon.mod_forum.re": "Ri:", - "addon.mod_forum.refreshdiscussions": "Aggiorna discussioni", - "addon.mod_forum.refreshposts": "Aggiorna interventi", - "addon.mod_forum.removefromfavourites": "Rimuovi dalle preferite", - "addon.mod_forum.reply": "Rispondi", - "addon.mod_forum.replyplaceholder": "Scrivi la risposta...", - "addon.mod_forum.subject": "Oggetto", - "addon.mod_forum.tagarea_forum_posts": "Interventi forum", - "addon.mod_forum.thisforumhasduedate": "La data entro la quale intervenire nel forum {{$a}}.", - "addon.mod_forum.thisforumisdue": "La data entro la quale intervenire nel forum era il {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Sblocca la discussione", - "addon.mod_forum.unpindiscussion": "Rimuovi la discussione dall'evidenza", - "addon.mod_forum.unread": "Non letto", - "addon.mod_forum.unreadpostsnumber": "{{$a}} interventi non letti", - "addon.mod_forum.yourreply": "La tua replica", - "addon.mod_glossary.addentry": "Aggiungi voce", - "addon.mod_glossary.aliases": "Alias (parole alternative)", - "addon.mod_glossary.attachment": "Allegato", - "addon.mod_glossary.byauthor": "Raggruppa per autore", - "addon.mod_glossary.bycategory": "Raggruppa per categoria", - "addon.mod_glossary.byrecentlyupdated": "Aggiornati di recente", - "addon.mod_glossary.bysearch": "Cerca", - "addon.mod_glossary.cannoteditentry": "Non è possibile modificare la voce", - "addon.mod_glossary.casesensitive": "Voce sensibile ai caratteri maiuscoli/minuscoli", - "addon.mod_glossary.categories": "Categorie", - "addon.mod_glossary.concept": "Concetto", - "addon.mod_glossary.definition": "Definizione", - "addon.mod_glossary.entriestobesynced": "Voci da sincronizzare", - "addon.mod_glossary.entrypendingapproval": "Questa voce è in attesa di approvazione.", - "addon.mod_glossary.entryusedynalink": "Collega la voce automaticamente", - "addon.mod_glossary.errconceptalreadyexists": "La voce è già presente. In questo glossario non sono consentite voci duplicate.", - "addon.mod_glossary.errorloadingentries": "Si è verificato un errore durante il caricamento delle voci.", - "addon.mod_glossary.errorloadingentry": "Si è verificato un errore durante il caricamento della voce.", - "addon.mod_glossary.errorloadingglossary": "Si è verificato un errore durante il caricamento del glossario.", - "addon.mod_glossary.fillfields": "Concetto e definizione sono campi obbligatori.", - "addon.mod_glossary.fullmatch": "Collega solo le parole intere", - "addon.mod_glossary.linking": "Link automatico", - "addon.mod_glossary.modulenameplural": "Glossari", - "addon.mod_glossary.noentriesfound": "Non sono state trovate voci.", - "addon.mod_glossary.tagarea_glossary_entries": "Voci di glossario", - "addon.mod_h5pactivity.all_attempts": "Tutti i tentativi dell'utente", - "addon.mod_h5pactivity.answer_correct": "La risposta è corretta", - "addon.mod_h5pactivity.answer_fail": "Risposta errata", - "addon.mod_h5pactivity.answer_incorrect": "La risposta è errata", - "addon.mod_h5pactivity.answer_pass": "Risposta corretta", - "addon.mod_h5pactivity.attempt": "Tentativo", - "addon.mod_h5pactivity.attempt_completion_no": "Il tentativo non è stato completato", - "addon.mod_h5pactivity.attempt_completion_yes": "Il tentativo è stato completato", - "addon.mod_h5pactivity.attempts_none": "L'utente non ha svolto tentativi.", - "addon.mod_h5pactivity.completion": "Completamento", - "addon.mod_h5pactivity.downloadh5pfile": "Scarica file H5P", - "addon.mod_h5pactivity.duration": "Durata", - "addon.mod_h5pactivity.errorgetactivity": "Si è verificato un errore durante lo scaricamento dei dati dell'attività H5P", - "addon.mod_h5pactivity.filestatenotdownloaded": "Il pacchetto H5P non è stato scaricato. Per utilizzarlo è necessario scaricarlo.", - "addon.mod_h5pactivity.filestateoutdated": "Il pacchetto H5P è stato modificato dopo l'ultimo scaricamento. Per utilizzarlo è necessario scaricarlo nuovamente.", - "addon.mod_h5pactivity.maxscore": "Punteggio massimo", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "I miei tentativi", - "addon.mod_h5pactivity.offlinedisabledwarning": "Sarà necessario essere online per visualizzare il pacchetto H5P.", - "addon.mod_h5pactivity.result_other": "Tipo di interazione sconosciuta", - "addon.mod_h5pactivity.review_my_attempts": "Visualizza i miei tentativi", - "addon.mod_h5pactivity.score": "Punteggio", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} su {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Data di inizio", - "addon.mod_h5pactivity.totalscore": "Punteggio totale", - "addon.mod_h5pactivity.viewattempt": "Visualizza tentativo {{$a}}", - "addon.mod_imscp.deploymenterror": "Si è verificato un errore nel pacchetto", - "addon.mod_imscp.modulenameplural": "IMS content package", - "addon.mod_imscp.showmoduledescription": "Visualizza descrizione", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "Risposta", - "addon.mod_lesson.attempt": "Tentativo: {{$a}}", - "addon.mod_lesson.attemptheader": "Tentativo", - "addon.mod_lesson.attemptsremaining": "Tentativi rimasti: {{$a}}", - "addon.mod_lesson.averagescore": "Punteggio medio", - "addon.mod_lesson.averagetime": "Tempo medio", - "addon.mod_lesson.branchtable": "Contenuto", - "addon.mod_lesson.cannotfindattempt": "Errore: non è stato possibile trovare il tentativo", - "addon.mod_lesson.cannotfinduser": "Errore: non è stato possibile trovare utenti", - "addon.mod_lesson.clusterjump": "Domanda non visualizzata di un gruppo ", - "addon.mod_lesson.completed": "Completata", - "addon.mod_lesson.congratulations": "Hai raggiunto la fine della lezione", - "addon.mod_lesson.continue": "Continua", - "addon.mod_lesson.continuetonextpage": "Vai alla prossima pagina.", - "addon.mod_lesson.defaultessayresponse": "Il testo libero sarà valutato dal docente.", - "addon.mod_lesson.detailedstats": "Statistiche dettagliate", - "addon.mod_lesson.didnotanswerquestion": "Non è stata data risposta a questa domanda.", - "addon.mod_lesson.displayofgrade": "Visualizzazione voti (solo per studenti)", - "addon.mod_lesson.displayscorewithessays": "

                Hai ottenuto {{$a.score}} su {{$a.tempmaxgrade}} per le domande valutate automaticamente.

                \n

                Le tue {{$a.essayquestions}} domande a testo libero saranno valutate successivamente e il voto sarà aggiunto
                al tuo punteggio finale.

                \n

                La tua valutazione senza le domande a testo libero è {{$a.score}} su {{$a.grade}}.

                ", - "addon.mod_lesson.displayscorewithoutessays": "Il tuo punteggio è {{$a.score}} (su {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "La password deve essere inserita", - "addon.mod_lesson.enterpassword": "Inserisci la password:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Non hai risposto a nessuna domanda. Per questa lezione hai ottenuto 0 punti.", - "addon.mod_lesson.finish": "Termina", - "addon.mod_lesson.finishretakeoffline": "Il tentativo è stato completato offline.", - "addon.mod_lesson.firstwrong": "la tua risposta non è corretta. Desideri provare a rispondere di nuovo? (L'eventuale risposta corretta non sarà comunque tenuta in considerazione per il calcolo del punteggio finale).", - "addon.mod_lesson.gotoendoflesson": "Vai alla fine della lezione", - "addon.mod_lesson.grade": "Punteggio", - "addon.mod_lesson.highscore": "Voto migliore", - "addon.mod_lesson.hightime": "Tempo migliore", - "addon.mod_lesson.leftduringtimed": "Hai abbandonato il tentativo durante una lezione a tempo.
                Clicca su Continua per ricominciare la lezione.", - "addon.mod_lesson.leftduringtimednoretake": "Hai abbandonato il tentativo durante una lezione a tempo e
                non puoi riprovare o continuare la lezione.", - "addon.mod_lesson.lessonmenu": "Menu della lezione", - "addon.mod_lesson.lessonstats": "Statistiche della lezione", - "addon.mod_lesson.linkedmedia": "Media linkati", - "addon.mod_lesson.loginfail": "Login fallito, prova ancora...", - "addon.mod_lesson.lowscore": "Voti peggiori", - "addon.mod_lesson.lowtime": "Tempi peggiori", - "addon.mod_lesson.maximumnumberofattemptsreached": "E' stato raggiunto il massimo numero di tentativi - Passaggio alla pagina successiva", - "addon.mod_lesson.modattemptsnoteacher": "La revisione funziona solo per gli studenti.", - "addon.mod_lesson.modulenameplural": "Lezioni", - "addon.mod_lesson.noanswer": "Non è stata data risposta ad una o più domande. Torna indietro e dai una risposta.", - "addon.mod_lesson.nolessonattempts": "Non è stato effettuato nessun tentativo su questa lezione.", - "addon.mod_lesson.nolessonattemptsgroup": "La lezione non è stata tentata da {{$a}} membri del gruppo.", - "addon.mod_lesson.notcompleted": "Non completato", - "addon.mod_lesson.numberofcorrectanswers": "Numero di risposte esatte: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Numero di domande risposte: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Numero di domande dove hai risposto: {{$a.nquestions}}; (Devi rispondere ad almeno: {{$a.minquestions}} domande)", - "addon.mod_lesson.ongoingcustom": "Finora hai ottenuto {{$a.score}} punti su {{$a.currenthigh}}.", - "addon.mod_lesson.ongoingnormal": "Hai risposto correttamente a {{$a.correct}} domande su {{$a.viewed}} tentativi.", - "addon.mod_lesson.or": "OR", - "addon.mod_lesson.overview": "Riepilogo", - "addon.mod_lesson.preview": "Anteprima", - "addon.mod_lesson.progressbarteacherwarning2": "Non vedrai la barra di avanzamento in quanto puoi modificare il contenuto della lezione", - "addon.mod_lesson.progresscompleted": "Hai completato il {{$a}}% della lezione", - "addon.mod_lesson.question": "Domanda", - "addon.mod_lesson.rawgrade": "Voto grezzo", - "addon.mod_lesson.reports": "Risultati", - "addon.mod_lesson.response": "Replica", - "addon.mod_lesson.retakefinishedinsync": "E' stato sincronizzato un tentativo offline. Desideri rivederlo?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Revisione", - "addon.mod_lesson.reviewlesson": "Rivedi la lezione", - "addon.mod_lesson.reviewquestionback": "Si, voglio provare ancora", - "addon.mod_lesson.reviewquestioncontinue": "No, voglio andare alla prossima domanda", - "addon.mod_lesson.secondpluswrong": "Non proprio. Vuoi riprovare?", - "addon.mod_lesson.submit": "Invia", - "addon.mod_lesson.teacherjumpwarning": "Stai utilizzando un salto a {{$a.cluster}} o un salto a {{$a.unseen}}. Sarà invece utilizzato il salto alla Pagina seguente. Per verificare i salti devi collegarti con il ruolo di studente.", - "addon.mod_lesson.teacherongoingwarning": "Durante lo svolgimento della lezione il punteggio ottenuto viene visualizzato solo agli studenti. Esegui il Login come studente per verificare tale punteggio", - "addon.mod_lesson.teachertimerwarning": "Il timer funziona solamente per gli studenti. Prova il timer facendo il login come studente.", - "addon.mod_lesson.thatsthecorrectanswer": "Risposta corretta", - "addon.mod_lesson.thatsthewronganswer": "Risposta sbagliata", - "addon.mod_lesson.timeremaining": "Tempo rimanente", - "addon.mod_lesson.timetaken": "Tempo impiegato", - "addon.mod_lesson.unseenpageinbranch": "Domanda non vista in una pagina con contenuto", - "addon.mod_lesson.warningretakefinished": "Il tentativo è stato completato sul sito.", - "addon.mod_lesson.welldone": "-", - "addon.mod_lesson.youhaveseen": "Hai già visto più di una pagina di questa lezione.
                Vuoi iniziare dall'ultima pagina visitata?", - "addon.mod_lesson.youranswer": "La tua risposta", - "addon.mod_lesson.yourcurrentgradeisoutof": "Punteggio ottentuo: {{$a.grade}} su {{$a.total}}", - "addon.mod_lesson.youshouldview": "Devi rispondere ad almeno: {{$a}}", - "addon.mod_lti.errorgetlti": "Si è verificato un errore durante la ricezione dei dati del modulo.", - "addon.mod_lti.errorinvalidlaunchurl": "l'URL di lancio non è valida.", - "addon.mod_lti.launchactivity": "Lancia l'attività", - "addon.mod_lti.modulenameplural": "Tool Esterni", - "addon.mod_page.errorwhileloadingthepage": "Si è verificato un errore durante il caricamento della pagina.", - "addon.mod_page.modulenameplural": "Pagine", - "addon.mod_quiz.answercolon": "Risposta:", - "addon.mod_quiz.attemptfirst": "Primo tentativo", - "addon.mod_quiz.attemptlast": "Ultimo tentativo", - "addon.mod_quiz.attemptnumber": "Tentativo", - "addon.mod_quiz.attemptquiznow": "Tenta il quiz adesso", - "addon.mod_quiz.attemptstate": "Stato", - "addon.mod_quiz.clearchoice": "Annulla la scelta", - "addon.mod_quiz.comment": "Commento", - "addon.mod_quiz.completedon": "Terminato", - "addon.mod_quiz.confirmclose": "Stai per completare questo tentativo. Una volta chiuso il tentativo non potrai più modificare le risposte.", - "addon.mod_quiz.confirmleavequizonerror": "Si è verificato un errore durante il salvataggio delle domande. Sei sicuro di abbandonare il quiz?", - "addon.mod_quiz.confirmstart": "Il quiz ha un limite di tempo di {{$a}}. Il tempo sarà conteggiato a partire dall'inizio del tentativo, il quiz deve essere inviato prima della scadenza. Sei sicuro di iniziare?", - "addon.mod_quiz.confirmstartheader": "Tempo limite", - "addon.mod_quiz.connectionerror": "La connessione di rete si è interrotta (Il salvataggio automatico è fallito).\n\nAnnota tutte le risposte date in questa pagina negli ultimi minuti, poi prova a riconnetterti.\n\nUna volta ripristinata la connessione, le tue risposte saranno salvate e questo messaggio scomparirà.", - "addon.mod_quiz.continueattemptquiz": "Riprendi ultimo tentativo", - "addon.mod_quiz.continuepreview": "Continua l'ultima anteprima", - "addon.mod_quiz.errordownloading": "Si è verificato un errore durante lo scaricamento dei dati.", - "addon.mod_quiz.errorgetattempt": "Si è verificato un errore durante la ricezione dei dati del tentativo.", - "addon.mod_quiz.errorgetquestions": "Si è verificato un errore durante la ricezione delle domande.", - "addon.mod_quiz.errorgetquiz": "Si è verificato un errore durante la ricezione dei dati del quiz.", - "addon.mod_quiz.errorparsequestions": "Si è verificato un errore durante la lettura delle domande. Per favore svolgi il quiz usando un browser.", - "addon.mod_quiz.errorsaveattempt": "Si è verificato un errore durante il salvataggio del tentativo.", - "addon.mod_quiz.feedback": "Feedback", - "addon.mod_quiz.finishattemptdots": "Completa il tentativo...", - "addon.mod_quiz.finishnotsynced": "Completato ma non sincronizzato.", - "addon.mod_quiz.grade": "Valutazione", - "addon.mod_quiz.gradeaverage": "Voto medio", - "addon.mod_quiz.gradehighest": "Voto più alto", - "addon.mod_quiz.grademethod": "Metodo di valutazione", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Punteggio", - "addon.mod_quiz.modulenameplural": "Quiz", - "addon.mod_quiz.mustbesubmittedby": "Questo tentativo deve essere inviato entro {{$a}}.", - "addon.mod_quiz.noquestions": "Ancora non sono state aggiunte domande", - "addon.mod_quiz.noreviewattempt": "Non hai il privilegio di rivedere questo tentativo.", - "addon.mod_quiz.notyetgraded": "Non ancora valutato", - "addon.mod_quiz.outof": "{{$a.grade}} su un massimo di {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} su un massimo di {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Feedback complessivo", - "addon.mod_quiz.overdue": "In ritardo", - "addon.mod_quiz.overduemustbesubmittedby": "Il tentativo è fuori tempo massimo e dovrebbe essere già stato inviato. Se desideri che il quiz sia valutato, devi inviarlo entro {{$a}}. In mancanza, il tentativo non riceverà nessun punteggio.", - "addon.mod_quiz.preview": "Anteprima", - "addon.mod_quiz.previewquiznow": "Anteprima quiz", - "addon.mod_quiz.question": "Domanda", - "addon.mod_quiz.quiznavigation": "Navigazione quiz", - "addon.mod_quiz.quizpassword": "Password quiz", - "addon.mod_quiz.reattemptquiz": "Ritenta il quiz", - "addon.mod_quiz.requirepasswordmessage": "Per tentare questo quiz è necessario conoscere la password d'accesso.", - "addon.mod_quiz.returnattempt": "Torna al tentativo", - "addon.mod_quiz.review": "Revisione", - "addon.mod_quiz.reviewofattempt": "Revisione del tentativo {{$a}}", - "addon.mod_quiz.reviewofpreview": "Revisione dell'anteprima", - "addon.mod_quiz.showall": "Visualizza tutte le domande nella stessa pagina", - "addon.mod_quiz.showeachpage": "Visualizza una pagina alla volta", - "addon.mod_quiz.startattempt": "Avvia il tentativo", - "addon.mod_quiz.startedon": "Iniziato", - "addon.mod_quiz.stateabandoned": "Mai inviati", - "addon.mod_quiz.statefinished": "Completato", - "addon.mod_quiz.statefinisheddetails": "Inviato {{$a}}", - "addon.mod_quiz.stateinprogress": "In svolgimento", - "addon.mod_quiz.stateoverdue": "Fuori tempo massimo", - "addon.mod_quiz.stateoverduedetails": "Deve essere inviato da {{$a}}", - "addon.mod_quiz.status": "Stato", - "addon.mod_quiz.submitallandfinish": "Invia tutto e termina", - "addon.mod_quiz.summaryofattempt": "Riepilogo del tentativo", - "addon.mod_quiz.summaryofattempts": "Riepilogo dei tuoi tentativi precedenti ", - "addon.mod_quiz.timeleft": "Tempo rimasto", - "addon.mod_quiz.timetaken": "Tempo impiegato", - "addon.mod_quiz.warningquestionsnotsupported": "Il quiz contiene domande non supportare dall'app:", - "addon.mod_quiz.yourfinalgradeis": "Il tuo voto finale per questo quiz è {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Si è verificato un errore durante il caricamento del contenuto.", - "addon.mod_resource.modifieddate": "Modificato il {{$a}}", - "addon.mod_resource.modulenameplural": "File", - "addon.mod_resource.openthefile": "Apri il file", - "addon.mod_resource.uploadeddate": "Caricato il {{$a}}", - "addon.mod_scorm.asset": "Asset", - "addon.mod_scorm.assetlaunched": "Asset - Visualizzato", - "addon.mod_scorm.attempts": "Tentativi", - "addon.mod_scorm.averageattempt": "Media tentativi", - "addon.mod_scorm.browse": "Anteprima", - "addon.mod_scorm.browsed": "Visitato", - "addon.mod_scorm.browsemode": "Modalità anteprima", - "addon.mod_scorm.cannotcalculategrade": "Non è stato possibile calcolare le valutazioni.", - "addon.mod_scorm.completed": "Completato", - "addon.mod_scorm.contents": "Contenuti", - "addon.mod_scorm.dataattemptshown": "Questi dati appartengono al tentativo numero {{number}}.", - "addon.mod_scorm.enter": "Entra", - "addon.mod_scorm.errorcreateofflineattempt": "Si è verificato un errore durante la creazione di un tentativo offline. Per favore riprova.", - "addon.mod_scorm.errordownloadscorm": "Si è verificato un errore durante lo scaricamento dello SCORM \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Si è verificato un errore durante la ricezione dei dati SCORM.", - "addon.mod_scorm.errorinvalidversion": "Spiacente, l'applicazione supporta solamente SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Lo scaricamento di pacchetti SCORM è disabilitato. Per favore contatta l'amministratore del sito.", - "addon.mod_scorm.errornovalidsco": "Questo SCORM non ha SCO visibili da caricare.", - "addon.mod_scorm.errorpackagefile": "Spiacente, l'applicazione supporta solamente pacchetti ZIP.", - "addon.mod_scorm.errorsyncscorm": "Si è verificato un errore durante la sincronizzazione. Per favore riprova.", - "addon.mod_scorm.exceededmaxattempts": "Hai raggiunto il massimo numero di tentativi consentito.", - "addon.mod_scorm.failed": "Fallito", - "addon.mod_scorm.firstattempt": "Primo tentativo", - "addon.mod_scorm.gradeaverage": "Media dei voti", - "addon.mod_scorm.gradeforattempt": "Valutazione del tentativo", - "addon.mod_scorm.gradehighest": "Voto migliore", - "addon.mod_scorm.grademethod": "Metodo di valutazione", - "addon.mod_scorm.gradereported": "Voto ottenuto", - "addon.mod_scorm.gradescoes": "Learning object", - "addon.mod_scorm.gradesum": "Somma dei voti", - "addon.mod_scorm.highestattempt": "Tentativo migliore", - "addon.mod_scorm.incomplete": "Incompleto", - "addon.mod_scorm.lastattempt": "Ultimo tentativo completato", - "addon.mod_scorm.modulenameplural": "Pacchetti SCORM", - "addon.mod_scorm.newattempt": "Avvia un nuovo tentativo", - "addon.mod_scorm.noattemptsallowed": "Numero di tentativi consentito", - "addon.mod_scorm.noattemptsmade": "Numero di tentativi che hai effettuato", - "addon.mod_scorm.notattempted": "Non tentato", - "addon.mod_scorm.offlineattemptnote": "Questo tentativo ha dati non sincronizzati.", - "addon.mod_scorm.offlineattemptovermax": "Questo tentativo non può essere inviato perché hai superato il numero massimo di tentativi.", - "addon.mod_scorm.organizations": "Organizzazioni", - "addon.mod_scorm.passed": "Superato", - "addon.mod_scorm.reviewmode": "Modalità revisione", - "addon.mod_scorm.score": "Punteggio", - "addon.mod_scorm.scormstatusnotdownloaded": "Questo SCORM non è stato scaricato, lo sarà non appena lo aprirai.", - "addon.mod_scorm.scormstatusoutdated": "Questo SCORM è stato modificato dall'ultimo scaricamento e sarà scaricato nuovamente non appena lo aprirai.", - "addon.mod_scorm.suspended": "Sospeso", - "addon.mod_scorm.toc": "TOC", - "addon.mod_scorm.warningofflinedatadeleted": "Alcuni dati offline del tentativo {{number}} sono stati eliminati perché non è stato possibile inserirli in un nuovo tentativo.", - "addon.mod_scorm.warningsynconlineincomplete": "Alcuni tentativi non sono stati sincronizzati sul sito poiché è presente un tentativo svolto online che non è stato terminato. Per sincronizzare, devi prima terminare il tentativo online.", - "addon.mod_survey.cannotsubmitsurvey": "Si è verificato un errore durante l'invio del sondaggio. Per favore riprova.", - "addon.mod_survey.errorgetsurvey": "Si è verificato un errore durante la ricezione di dati del sondaggio.", - "addon.mod_survey.ifoundthat": "Realmente", - "addon.mod_survey.ipreferthat": "Idealmente", - "addon.mod_survey.modulenameplural": "Sondaggi", - "addon.mod_survey.responses": "Risposte", - "addon.mod_survey.results": "Risultati", - "addon.mod_survey.surveycompletednograph": "Hai completato il sondaggio.", - "addon.mod_url.accessurl": "Accedi all'URL", - "addon.mod_url.modulenameplural": "URL", - "addon.mod_url.pointingtourl": "URL dove punta la risorsa.", - "addon.mod_wiki.cannoteditpage": "Non puoi modificare questa pagina", - "addon.mod_wiki.createpage": "Crea pagina", - "addon.mod_wiki.editingpage": "Modifica pagina '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Si è verificato un errore durante il caricamento della pagina.", - "addon.mod_wiki.errornowikiavailable": "Il wiki non ha contenuti.", - "addon.mod_wiki.gowikihome": "Vai alla prima pagina del wiki", - "addon.mod_wiki.map": "Mappa", - "addon.mod_wiki.modulenameplural": "Wiki", - "addon.mod_wiki.newpagehdr": "Nuova pagina", - "addon.mod_wiki.newpagetitle": "Titolo nuova pagina", - "addon.mod_wiki.nocontent": "Questa pagina non ha contenuti", - "addon.mod_wiki.notingroup": "Non è in un gruppo", - "addon.mod_wiki.pageexists": "La pagina esiste già.", - "addon.mod_wiki.pagename": "Nome pagina", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Pagine wiki", - "addon.mod_wiki.titleshouldnotbeempty": "Il titolo non può essere vuoto", - "addon.mod_wiki.viewpage": "Visualizza pagina", - "addon.mod_wiki.wikipage": "Pagina wiki", - "addon.mod_wiki.wrongversionlock": "Un altro utente ha modificato questa pagina mentre la stavi modificando anche tu. Le tue modifiche son ora obsolete.", - "addon.mod_workshop.alreadygraded": "Ha già un voto", - "addon.mod_workshop.areainstructauthors": "Istruzioni di consegna", - "addon.mod_workshop.areainstructreviewers": "Istruzioni per la valutazione", - "addon.mod_workshop.assess": "Valuta", - "addon.mod_workshop.assessedsubmission": "Valutazione consegna", - "addon.mod_workshop.assessmentform": "Scheda di valutazione", - "addon.mod_workshop.assessmentsettings": "Impostazioni valutazione", - "addon.mod_workshop.assessmentweight": "Peso della valutazione", - "addon.mod_workshop.assignedassessments": "Consegne da valutare", - "addon.mod_workshop.assignedassessmentsnone": "Non hai consegne assegnate da valutare", - "addon.mod_workshop.conclusion": "Conclusione", - "addon.mod_workshop.createsubmission": "Inizia a preparare la tua consegna", - "addon.mod_workshop.deletesubmission": "Elimina consegna", - "addon.mod_workshop.editsubmission": "Modifica consegna", - "addon.mod_workshop.feedbackauthor": "Feedback per l'autore", - "addon.mod_workshop.feedbackby": "Feedback di {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Feedback per il revisore", - "addon.mod_workshop.givengrades": "Voti dati", - "addon.mod_workshop.gradecalculated": "Voto calcolato per la consegna", - "addon.mod_workshop.gradeinfo": "Voto: {{$a.received}} su {{$a.max}}", - "addon.mod_workshop.gradeover": "Modifica il voto per la consegna", - "addon.mod_workshop.gradesreport": "Report dei voti del workshop", - "addon.mod_workshop.gradinggrade": "Voto per la valutazione", - "addon.mod_workshop.gradinggradecalculated": "Voto calcolato per la valutazione", - "addon.mod_workshop.gradinggradeof": "Voto per la valutazione (su {{$a}})", - "addon.mod_workshop.gradinggradeover": "Modifica il voto per la valutazione", - "addon.mod_workshop.modulenameplural": "Workshop", - "addon.mod_workshop.nogradeyet": "Senza voto", - "addon.mod_workshop.notassessed": "Non valutata", - "addon.mod_workshop.notoverridden": "Non modificati", - "addon.mod_workshop.noyoursubmission": "Non hai ancora consegnato il tuo lavoro", - "addon.mod_workshop.overallfeedback": "Feedback complessivo", - "addon.mod_workshop.publishedsubmissions": "Consegne pubblicate", - "addon.mod_workshop.publishsubmission": "Pubblica consegna", - "addon.mod_workshop.publishsubmission_help": "Le consegne pubblicate saranno disponibili agli altri partecipanti dopo la chiusura del workshop.", - "addon.mod_workshop.reassess": "Modifica valutazione", - "addon.mod_workshop.receivedgrades": "Voti ricevuti", - "addon.mod_workshop.submissionattachment": "Allegato", - "addon.mod_workshop.submissioncontent": "Contenuto consegna", - "addon.mod_workshop.submissiondeleteconfirm": "Sei sicuro di eliminare la consegna seguente?", - "addon.mod_workshop.submissiongrade": "Voto per la consegna", - "addon.mod_workshop.submissiongradeof": "Voto per la consegna (su {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Devi inserire del testo o aggiungere un file.", - "addon.mod_workshop.submissionrequiredtitle": "Devi inserire un titolo.", - "addon.mod_workshop.submissionsreport": "Report delle consegne", - "addon.mod_workshop.submissiontitle": "Titolo", - "addon.mod_workshop.switchphase10": "Vai alla fase di allestimento", - "addon.mod_workshop.switchphase20": "Passa alla fase di consegna", - "addon.mod_workshop.switchphase30": "Passa alla fase di valutazione", - "addon.mod_workshop.switchphase40": "Passa alla fase di calcolo dei voti", - "addon.mod_workshop.switchphase50": "Chiudi workshop", - "addon.mod_workshop.userplan": "Workshop planner", - "addon.mod_workshop.userplancurrentphase": "Fase attuale", - "addon.mod_workshop.warningassessmentmodified": "La consegna è stata modificata sul sito.", - "addon.mod_workshop.warningsubmissionmodified": "La valutazione è stata modificata sul sito.", - "addon.mod_workshop.weightinfo": "Peso: {{$a}}", - "addon.mod_workshop.yourassessment": "La tua valutazione", - "addon.mod_workshop.yourassessmentfor": "La tua valutazione su {{$a}}", - "addon.mod_workshop.yourgrades": "Le vostre valutazioni", - "addon.mod_workshop.yoursubmission": "La tua consegna", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Commento su {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Voto per {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Elemento {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Devi scegliere una valutazione per questo aspetto", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Commento su {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Elemento {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Commento su {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Voto per {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Affermazione {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Criterio {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Devi scegliere uno di questi elementi", - "addon.notes.addnewnote": "Aggiungi una annotazione", - "addon.notes.coursenotes": "Annotazioni corso", - "addon.notes.deleteconfirm": "Confermi l'eliminazione?", - "addon.notes.eventnotecreated": "Creata nota", - "addon.notes.eventnotedeleted": "Eliminata nota", - "addon.notes.nonotes": "Non ci sono ancora annotazioni di questo tipo", - "addon.notes.note": "Annotazione", - "addon.notes.notes": "Annotazioni", - "addon.notes.personalnotes": "Annotazioni personali", - "addon.notes.publishstate": "Contesto", - "addon.notes.sitenotes": "Annotazioni del sito", - "addon.notes.userwithid": "Utente con iID {{id}}", - "addon.notes.warningnotenotsent": "Non è stato possibile aggiungere note al corso {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Si è verificato un errore durante la ricezione delle notifiche.", - "addon.notifications.markallread": "Segna tutti come letti", - "addon.notifications.notificationpreferences": "Preferenze notifiche", - "addon.notifications.notifications": "Notifiche", - "addon.notifications.playsound": "Riproduci suono", - "addon.notifications.therearentnotificationsyet": "Non ci sono notifiche.", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Emirati Arabi Uniti", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua e Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antartico", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa Americana", - "assets.countries.AT": "Austria", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Isole Åland", - "assets.countries.AZ": "Azerbaijan", - "assets.countries.BA": "Bosnia e Erzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgio", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Sultanato del Brunei", - "assets.countries.BO": "Bolivia (Stato Plurinazionale della)", - "assets.countries.BQ": "Bonaire, Sint Eustatius e Saba", - "assets.countries.BR": "Brasile", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Isola Bouvet", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Bielorussia", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canada", - "assets.countries.CC": "Isole Cocos (Keeling)", - "assets.countries.CD": "Congo (Repubblica democratica del)", - "assets.countries.CF": "Repubblica Centrafricana", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Svizzera", - "assets.countries.CI": "Costa D'Avorio", - "assets.countries.CK": "Isole Cook", - "assets.countries.CL": "Cile", - "assets.countries.CM": "Camerun", - "assets.countries.CN": "Cina", - "assets.countries.CO": "Colombia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Capo Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Isola di Natale", - "assets.countries.CY": "Cipro", - "assets.countries.CZ": "Repubblica Ceca", - "assets.countries.DE": "Germania", - "assets.countries.DJ": "Gibuti", - "assets.countries.DK": "Danimarca", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Repubblica Dominicana", - "assets.countries.DZ": "Algeria", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estonia", - "assets.countries.EG": "Egitto", - "assets.countries.EH": "Sahara Occidentale", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spagna", - "assets.countries.ET": "Etiopia", - "assets.countries.FI": "Finlandia", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Isole Falkland (Malvinas)", - "assets.countries.FM": "Micronesia (Federazione Stati della)", - "assets.countries.FO": "Isole Faroe", - "assets.countries.FR": "Francia", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Regno Unito", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Guiana francese", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibilterra", - "assets.countries.GL": "Groenlandia", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadalupa", - "assets.countries.GQ": "Guinea Equatoriale", - "assets.countries.GR": "Grecia", - "assets.countries.GS": "Georgia del Sud e isole Sandwich meridionali", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guiana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Isole Heard e McDonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Croazia", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Ungheria", - "assets.countries.ID": "Indonesia", - "assets.countries.IE": "Irlanda", - "assets.countries.IL": "Israele", - "assets.countries.IM": "Isola di Man", - "assets.countries.IN": "India", - "assets.countries.IO": "Territorio Britannico dell'Oceano Indiano", - "assets.countries.IQ": "Iraq", - "assets.countries.IR": "Iran (Repubblica Islamica del)", - "assets.countries.IS": "Islanda", - "assets.countries.IT": "Italia", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Giordania", - "assets.countries.JP": "Giappone", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirghizistan", - "assets.countries.KH": "Cambogia", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comoros", - "assets.countries.KN": "Saint Kitts e Nevis", - "assets.countries.KP": "Corea (Repubblica Democratica Popolare di)", - "assets.countries.KR": "Corea (Repubblica di)", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Isole Cayman", - "assets.countries.KZ": "Kazakistan", - "assets.countries.LA": "Lao, Repubblica Democratica Popolare del", - "assets.countries.LB": "Libano", - "assets.countries.LC": "Santa Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lituania", - "assets.countries.LU": "Lussemburgo", - "assets.countries.LV": "Lettonia", - "assets.countries.LY": "Libia", - "assets.countries.MA": "Marocco", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldavi (Repubblica di)", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin (parte francese)", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Isole Marshall", - "assets.countries.MK": "Macedonia del Nord", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Isole Marianne Settentrionali", - "assets.countries.MQ": "Martinica", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Isole Maurizie", - "assets.countries.MV": "Maldive", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Messico", - "assets.countries.MY": "Malaysia", - "assets.countries.MZ": "Mozambico", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Nuova Caledonia", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Isola di Norfolk", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Paesi Bassi", - "assets.countries.NO": "Norvegia", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nuova Zelanda", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Perù", - "assets.countries.PF": "Polinesia Francese", - "assets.countries.PG": "Papua Nuova Guinea", - "assets.countries.PH": "Filippine", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polonia", - "assets.countries.PM": "Saint Pierre e Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Porto Rico", - "assets.countries.PS": "Palestina, Stato di", - "assets.countries.PT": "Portogallo", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Reunion", - "assets.countries.RO": "Romania", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Federazione Russa", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Arabia Saudita", - "assets.countries.SB": "Isole Salomone", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Svezia", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "Sant'Elena, Ascensione e Tristan Da Cunha", - "assets.countries.SI": "Slovenia", - "assets.countries.SJ": "Isole Svalbard e Jan Mayen", - "assets.countries.SK": "Slovacchia", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Suriname", - "assets.countries.SS": "Sudan del Sud", - "assets.countries.ST": "Sao Tome e Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Saint Martin (parte olandese)", - "assets.countries.SY": "Siria, Repubblica Araba di", - "assets.countries.SZ": "Swaziland", - "assets.countries.TC": "Isole Turks e Caicos", - "assets.countries.TD": "Ciad", - "assets.countries.TF": "Territori Meridionali Francesi", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailandia", - "assets.countries.TJ": "Tagikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor Est", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunisia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turchia", - "assets.countries.TT": "Trinidad e Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzania, Repubblica Unita di", - "assets.countries.UA": "Ucraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Isole Minori degli Stati Uniti d'America", - "assets.countries.US": "Stati Uniti d'America", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Città del Vaticano", - "assets.countries.VC": "Saint Vincent e Grenadine", - "assets.countries.VE": "Venezuela (Repubblica Bolivariana del)", - "assets.countries.VG": "Isole Vergini (Britanniche)", - "assets.countries.VI": "Isole Vergini (Stati Uniti)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis e Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Sud Africa", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "Ebook EPUB", - "assets.mimetypes.application/msword": "documento Word", - "assets.mimetypes.application/pdf": "documento PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Backup Moodle", - "assets.mimetypes.application/vnd.ms-excel": "foglio di calcolo Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Workbook Excel 2007 con macro abilitate", - "assets.mimetypes.application/vnd.ms-powerpoint": "presentazione Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Foglio elettronico OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Modello di foglio elettronico OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Documento di testo OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Modello di documento di testo OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Modello di pagina web OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Presentazione Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Slideshow Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Foglio elettronico Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Modello Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Documento Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Presentazione iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Foglio elettronico iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Documento iWork Pages", - "assets.mimetypes.application/x-javascript": "Sorgente javascript", - "assets.mimetypes.application/x-mspublisher": "Documento Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animazione Flash", - "assets.mimetypes.application/xhtml_xml": "Documento XHTML", - "assets.mimetypes.archive": "Archivio ({{$a.EXT}})", - "assets.mimetypes.audio": "File audio ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "File", - "assets.mimetypes.group:archive": "File archivio", - "assets.mimetypes.group:audio": "File audio", - "assets.mimetypes.group:document": "File documento", - "assets.mimetypes.group:html_audio": "File audio supportati nativamente dai browser", - "assets.mimetypes.group:html_track": "File traccia HTML", - "assets.mimetypes.group:html_video": "File video supportati nativamente dai browser", - "assets.mimetypes.group:image": "File immagine", - "assets.mimetypes.group:presentation": "File presentazione", - "assets.mimetypes.group:sourcecode": "Codice sorgente", - "assets.mimetypes.group:spreadsheet": "File foglio elettronico", - "assets.mimetypes.group:video": "File video", - "assets.mimetypes.group:web_audio": "File audio utilizzati sul web", - "assets.mimetypes.group:web_file": "File wb", - "assets.mimetypes.group:web_image": "File immagine utilizzati sul web", - "assets.mimetypes.group:web_video": "File video utilizzati sul web", - "assets.mimetypes.image": "Immagine ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Icona Windows", - "assets.mimetypes.text/css": "Cascading Style-Sheet", - "assets.mimetypes.text/csv": "Valori separati da virgola", - "assets.mimetypes.text/html": "Documento HTML", - "assets.mimetypes.text/plain": "File di testo", - "assets.mimetypes.text/rtf": "documento RTF", - "assets.mimetypes.text/vtt": "Web Video Text Track", - "assets.mimetypes.video": "File video ({{$a.EXT}})", - "core.accounts": "Profili", - "core.add": "Aggiungi", - "core.agelocationverification": "Verifica dell'età e della della nazione", - "core.ago": "{{$a}} fa", - "core.all": "Tutti", - "core.allgroups": "Tutti i gruppi", - "core.allparticipants": "Tutti i partecipanti", - "core.answer": "Risposta", - "core.answered": "Risposta fornita", - "core.areyousure": "Sei sicuro?", - "core.back": "Indietro", - "core.block.blocks": "Blocchi", - "core.browser": "Browser", - "core.cancel": "Annulla", - "core.cannotconnect": "Non è stato possibile effettuare il collegamento", - "core.cannotconnecttrouble": "Si sono verificati problemi durante il tentativo di collegamento al sito.", - "core.cannotconnectverify": "Verificare che l'indirizzo sia corretto.", - "core.cannotdownloadfiles": "Lo scaricamento di file è disabilitato. Per favore contatta l'amministratore del sito.", - "core.captureaudio": "Registra audio", - "core.capturedimage": "Foto scattata.", - "core.captureimage": "Scatta foto", - "core.capturevideo": "Registra video", - "core.category": "Categoria", - "core.choose": "Seleziona", - "core.choosedots": "Scegli...", - "core.clearsearch": "Pulisci la ricerca", - "core.clicktohideshow": "Click per aprire e chiudere", - "core.clicktoseefull": "Click per visualizzare il contenuto completo.", - "core.close": "Chiudi", - "core.comments": "Commenti", - "core.comments.addcomment": "Scrivi un commento...", - "core.comments.comments": "Commenti", - "core.comments.commentscount": "Commenti: ({{$a}})", - "core.comments.deletecommentbyon": "Elimina i commenti dell'utente {{$a.user}} effettuati il {{$a.time}}", - "core.comments.eventcommentcreated": "Creato commento", - "core.comments.eventcommentdeleted": "Eliminato commento", - "core.comments.nocomments": "Non ci sono commenti", - "core.comments.savecomment": "Salva commento", - "core.comments.warningcommentsnotsent": "Non è stato possibile sincronizzare i commenti. {{error}}", - "core.commentscount": "Commenti: ({{$a}})", - "core.completion-alt-auto-fail": "Completato: {{$a}} (senza raggiungere la sufficienza)", - "core.completion-alt-auto-n": "Non completata: {{$a}}", - "core.completion-alt-auto-n-override": "Non completato: {{$a.modname}} (impostato da {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Completato: {{$a}} (raggiunta la sufficienza)", - "core.completion-alt-auto-y": "Completata: {{$a}}", - "core.completion-alt-auto-y-override": "Completato: {{$a.modname}} (impostato da {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Non completata: {{$a}}.\nSelezionarla per spuntarla come completata.", - "core.completion-alt-manual-n-override": "Non completato: {{$a.modname}} (impostato da {{$a.overrideuser}}). Selezionare per contrassegnare come completato.", - "core.completion-alt-manual-y": "Completata: {{$a}}. Selezionarla per spuntarla come non completata.", - "core.completion-alt-manual-y-override": "Completato: {{$a.modname}} (impostato da {{$a.overrideuser}}). Selezionare per contrassegnare come non completato.", - "core.confirmcanceledit": "Sei sicuro di abbandonare questa pagina? Tutte le modifiche saranno perdute.", - "core.confirmdeletefile": "Sei sicuro di voler eliminare questo file?", - "core.confirmloss": "Sei sicuro? Le modifiche andranno perdute.", - "core.confirmopeninbrowser": "Desideri aprirlo nel browser?", - "core.considereddigitalminor": "Sei troppo giovane per creare un account su questo sito.", - "core.content": "Contenuto", - "core.contenteditingsynced": "Il contenuto in fase di modifica è stato sincronizzato.", - "core.contentlinks.chooseaccount": "Seleziona account", - "core.continue": "Continua", - "core.copiedtoclipboard": "Testo copiato nella clipboard", - "core.copytoclipboard": "Copia nella clipboard", - "core.course": "Corso", - "core.course.allsections": "Tutte le sezioni", - "core.course.confirmdeletemodulefiles": "Sei sicuro di eliminare questi file?", - "core.course.confirmdownload": "Stai per scaricare {size}}.{{availableSpace}} Vuoi continuare?", - "core.course.confirmdownloadunknownsize": "Non è stato possibile calcolare la dimensione del download.{{availableSpace}} Vuoi continuare?", - "core.course.confirmdownloadzerosize": "Stai per iniziare il download. {{availableSpace}} Sei sicuro di voler continuare?", - "core.course.confirmlimiteddownload": "Non sei collagato al Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Stai per scaricare almeno/strong> {{size}}.{{availableSpace}} Vuoi continuare?", - "core.course.contents": "Contenuti", - "core.course.couldnotloadsectioncontent": "Non è stato possibile caricare il contenuto della sezione, per favore riprova più tardi.", - "core.course.couldnotloadsections": "Non è stato possibile caricare le sezioni, per favore riprova più tardi.", - "core.course.coursesummary": "Introduzione al corso", - "core.course.downloadcourse": "Scarica corso", - "core.course.errordownloadingcourse": "Si è verificato un errore durante lo scaricamento del corso.", - "core.course.errordownloadingsection": "Si è verificato un errore durante lo scaricamento della sezione.", - "core.course.errorgetmodule": "Si è verificato un errore durante la ricezione dei dati dell'attività.", - "core.course.hiddenfromstudents": "Nascosta agli studenti", - "core.course.hiddenoncoursepage": "Disponibile ma non visibile sulla pagina del corso", - "core.course.nocontentavailable": "Non sono presenti contenuti.", - "core.course.overriddennotice": "La tua valutazione finale di questa attività è stata modificata manualmente.", - "core.course.sections": "Sezioni", - "core.coursedetails": "Dettagli corso", - "core.courses.addtofavourites": "Aggiungi corso ai preferiti", - "core.courses.allowguests": "Questo corso consente l'accesso ad utenti ospiti", - "core.courses.availablecourses": "Corsi disponibili", - "core.courses.categories": "Categorie di corso", - "core.courses.confirmselfenrol": "Desideri iscriverti al corso?", - "core.courses.courses": "Corsi", - "core.courses.downloadcourses": "Scarica corsi", - "core.courses.enrolme": "Iscrivimi", - "core.courses.errorloadcategories": "Si è verificato un errore durante il caricamento delle categorie.", - "core.courses.errorloadcourses": "Si è verificato un errore durante il caricamento dei corsi.", - "core.courses.errorsearching": "Si è verificato un errore durante la ricerca.", - "core.courses.errorselfenrol": "Si è verificato un errore mentre ti stavi iscrivendo.", - "core.courses.filtermycourses": "Filtra i miei corsi", - "core.courses.frontpage": "Pagina home", - "core.courses.hidecourse": "Elimina dalla visualizzazione", - "core.courses.ignore": "Ignora", - "core.courses.mycourses": "I miei corsi", - "core.courses.mymoodle": "Dashboard", - "core.courses.nocourses": "Non sono presenti informazioni sui corsi.", - "core.courses.nocoursesyet": "Questa categoria non ha corsi", - "core.courses.nosearchresults": "Nessun risultato", - "core.courses.notenroled": "Non sei iscritto in questo corso", - "core.courses.notenrollable": "Non puoi iscriverti al corso.", - "core.courses.password": "Chiave di iscrizione", - "core.courses.paymentrequired": "L'accesso al corso richiede un pagamento.", - "core.courses.paypalaccepted": "Pagamenti Paypal accettati", - "core.courses.reload": "Ricarica", - "core.courses.removefromfavourites": "Rimuovi dai preferiti", - "core.courses.search": "Cerca", - "core.courses.searchcourses": "Cerca corsi", - "core.courses.searchcoursesadvice": "Puoi utilizzare la ricerca corsi per accedere come ospite oppure iscriverti nei corsi che lo consentono.", - "core.courses.selfenrolment": "Iscrizione spontanea", - "core.courses.sendpaymentbutton": "Invia pagamento tramite Paypal", - "core.courses.show": "Visualizza il corso", - "core.courses.totalcoursesearchresults": "Totale corsi: {{$a}}", - "core.currentdevice": "Dispositivo attuale", - "core.date": "Data", - "core.day": "giorno", - "core.days": "giorni", - "core.decsep": ",", - "core.defaultvalue": "Default ({{$a}})", - "core.delete": "Elimina", - "core.deletedoffline": "Eliminato offline", - "core.deleteduser": "Utente eliminato", - "core.deleting": "Eliminazione in corso", - "core.description": "Descrizione", - "core.desktop": "Desktop", - "core.dfdaymonthyear": "DD-MM-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Minore digitale", - "core.digitalminor_desc": "Per creare un account un genitore o chi ne fa le veci deve contattare la seguente persona:", - "core.displayoptions": "Opzioni di visualizzazione", - "core.done": "Fatto", - "core.download": "Scarica", - "core.downloaded": "Scaricato", - "core.downloading": "Scaricamento in corso", - "core.edit": "Modifica", - "core.editor.autosavesucceeded": "Bozza salvata.", - "core.editor.bold": "Grassetto", - "core.editor.clear": "Elimina formattazione", - "core.editor.h3": "Intestazione (grande)", - "core.editor.h4": "Intestazione (media)", - "core.editor.h5": "Intestazione (piccola)", - "core.editor.italic": "Corsivo", - "core.editor.orderedlist": "Elenco numerato", - "core.editor.p": "Paragrafo", - "core.editor.strike": "Barrato", - "core.editor.textrecovered": "E' stata ripristinata automaticamente una bozza del testo", - "core.editor.underline": "Sottolineato", - "core.editor.unorderedlist": "Elenco puntato", - "core.error": "Errore", - "core.errorchangecompletion": "Si è verificato un errore durante la modifica dello stato di completamento. Per favore riprova.", - "core.errordeletefile": "Si è verificato un errore durante l'eliminazione del file. Per favore riprova.", - "core.errordownloading": "Si è verificato un errore durante lo scaricamento del file.", - "core.errordownloadingsomefiles": "Si è verificato un errore durante lo scaricamento dei file. E' possibile che manchino alcuni file.", - "core.errorfileexistssamename": "Un file con lo stesso nome è già presente.", - "core.errorinvalidresponse": "E' stata ricevuta una risposta non valida. Se l'errore persiste, contatta l'amministratore del sito.", - "core.errorloadingcontent": "Si è verificato un errore durante il caricamento del contenuto.", - "core.erroropenfilenoapp": "Si è verificato un errore durante l'apertura del file: non sono disponibili app per aprire questo tipo di file.", - "core.erroropenfilenoextension": "Si è verificato un errore durante l'apertura del file: il file non ha un'estensione.", - "core.erroropenpopup": "L'attività tenta di aprirsi in una finestra popup. Le popup non sono supportate nella app.", - "core.errorrenamefile": "Si è verificato un errore durante la modifica del nome del file. Per favore riprova.", - "core.errorsync": "Si è verificato un errore durante la sincronizzazione. Per favore riprova.", - "core.explanationdigitalminor": "L’informazione è necessaria per stabilire se la tua età è maggiore dell’età del consenso digitale, ossia l’età in cui un individuo può accettare termini e condizioni e acconsentire legalmente alla memorizzazione ed elaborazione dei propri dati.", - "core.favourites": "Preferiti", - "core.filename": "Nome del file", - "core.filenameexist": "Un file con lo stesso non è già presente: {{$a}}", - "core.filenotfound": "Spiacente, il file non è stato trovato", - "core.fileuploader.addfiletext": "Aggiungi file", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Camera", - "core.fileuploader.confirmuploadfile": "Stai per caricare {{size}}. Sei sicuro di continuare?", - "core.fileuploader.confirmuploadunknownsize": "Non è stato possibile calcolare la dimensione del caricamento. Vuoi continuare?", - "core.fileuploader.errorcapturingaudio": "Si è verificato un errore durante la registrazione dell'audio", - "core.fileuploader.errorcapturingimage": "Si è verificato un errore durante l'acquisizione dell'immagine.", - "core.fileuploader.errorcapturingvideo": "Si è verificato un errore durante l'acquisizione del video", - "core.fileuploader.errorgettingimagealbum": "Si è verificato un errore durante la ricezione dell'immagine dall'album.", - "core.fileuploader.errormustbeonlinetoupload": "Devi essere online per caricare file.", - "core.fileuploader.errornoapp": "Non hai un'app per svolgere questa azione.", - "core.fileuploader.errorreadingfile": "Si è verificato un errore durante la lettura del file.", - "core.fileuploader.errorwhileuploading": "Si è verificato un errore durante il caricamento del file.", - "core.fileuploader.file": "File", - "core.fileuploader.filesofthesetypes": "Tipi di file accettati:", - "core.fileuploader.fileuploaded": "File caricato correttamente", - "core.fileuploader.invalidfiletype": "Non è possibile accettare il tipo di file {{$a}}", - "core.fileuploader.maxbytesfile": "Il file {{$a.file}} è troppo grande. La dimensione massima consentita è {{$a.size}}.", - "core.fileuploader.more": "Dettagli", - "core.fileuploader.photoalbums": "Album fotografico", - "core.fileuploader.readingfile": "Letture file in corso", - "core.fileuploader.selectafile": "Seleziona un file", - "core.fileuploader.uploadafile": "Carica un file", - "core.fileuploader.uploading": "Caricamento in corso", - "core.fileuploader.uploadingperc": "Caricamento: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filtro", - "core.folder": "Cartella", - "core.forcepasswordchangenotice": "È necessario cambiare la password per proseguire.", - "core.fulllistofcourses": "Tutti i corsi", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Media", - "core.grades.badgrade": "Il voto inserito non è valido", - "core.grades.contributiontocoursetotal": "Quota di contribuzione sul totale del corso", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Valutazione", - "core.grades.gradeitem": "Elemento di valutazione", - "core.grades.grades": "Valutazioni", - "core.grades.lettergrade": "Graduatoria letterale", - "core.grades.nogradesreturned": "Non è stata ottenuta alcuna valutazione", - "core.grades.nooutcome": "Nessun obiettivo", - "core.grades.percentage": "Percentuale", - "core.grades.range": "Intervallo", - "core.grades.rank": "Classifica", - "core.grades.weight": "Peso", - "core.group": "Gruppo", - "core.groupsseparate": "Gruppi separati", - "core.groupsvisible": "Gruppi visibili", - "core.h5p.additionallicenseinfo": "Informazioni aggiuntive sulla licenza", - "core.h5p.author": "Autore", - "core.h5p.authorcomments": "Commenti dell'autore", - "core.h5p.authorcommentsdescription": "Commenti per il redattore del contenuto (il testo non sarà visualizzato nelle informazioni di copyright).", - "core.h5p.authorname": "Nome dell'autore", - "core.h5p.authorrole": "Ruolo dell'autore", - "core.h5p.by": "di", - "core.h5p.cancellabel": "Annulla", - "core.h5p.ccattribution": "Attribuzione (CC BY)", - "core.h5p.ccattributionnc": "Attribuzione - Non commerciale (CC BY-NC)", - "core.h5p.ccattributionncnd": "Attribuzione - Non commerciale - Non opere derivate (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Attribuzione - Non commerciale - Condividi allo stesso modo (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Attribuzione - Non opere derivate (CC BY-ND)", - "core.h5p.ccattributionsa": "Attribuzione - Condividi allo stesso modo (CC BY-SA)", - "core.h5p.ccpdd": "Public Domain Dedication (CC0)", - "core.h5p.changedby": "Modificato da", - "core.h5p.changedescription": "Descrizione delle modifiche", - "core.h5p.changelog": "Log delle modifche", - "core.h5p.changeplaceholder": "Fotografia ritagliata, testo modificato, eccetera.", - "core.h5p.close": "Chiudi", - "core.h5p.confirmdialogbody": "Per favore conferma che desideri continuare. L'azione non è reversibile.", - "core.h5p.confirmdialogheader": "Conferma azione", - "core.h5p.confirmlabel": "Conferma", - "core.h5p.connectionLost": "Il collegamento si è interrotto. I risultati saranno conservati e inviati quando la connessione sarà ristabilita.", - "core.h5p.connectionReestablished": "Connessione ristabilita.", - "core.h5p.contentCopied": "Il contenuto è stato copiato negli appunti", - "core.h5p.contentchanged": "Il contenuto è cambiato dall'ultima volta che lo hai utilizzato.", - "core.h5p.contenttype": "Tipo di contenuto", - "core.h5p.copyright": "Diritto di utilizzo", - "core.h5p.copyrightinfo": "Informazioni di copyright", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "Visualizza le Informazioni sul copyright di questo contenuto", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Data", - "core.h5p.disablefullscreen": "Disabilita schermo intero", - "core.h5p.download": "Scarica", - "core.h5p.downloadtitle": "Scarica il contenuto come file H5P.", - "core.h5p.editor": "Redattore", - "core.h5p.embed": "Incorpora", - "core.h5p.embedtitle": "Visualizza il codice di incorporamento del contenuto", - "core.h5p.fullscreen": "Schermo intero", - "core.h5p.gpl": "General Public License v3", - "core.h5p.h5ptitle": "Visitare il sito H5P.org per trovare altri contenuti.", - "core.h5p.hideadvanced": "Nascondi avanzate", - "core.h5p.license": "Licenza", - "core.h5p.licenseCC010": "CC0 1.0 Universale (CC0 1.0) Public Domain Dedication", - "core.h5p.licenseCC010U": "CC0 1.0 Universale", - "core.h5p.licenseCC10": "1.0 Generica", - "core.h5p.licenseCC20": "2.0 Generica", - "core.h5p.licenseCC25": "2.5 Generica", - "core.h5p.licenseCC30": "3.0 Unported", - "core.h5p.licenseCC40": "4.0 Internazionale", - "core.h5p.licenseGPL": "General Public License", - "core.h5p.licenseV1": "Versione 1", - "core.h5p.licenseV2": "Versione 2", - "core.h5p.licenseV3": "Versione 3", - "core.h5p.licensee": "Licenziatario", - "core.h5p.licenseextras": "Licenze extra", - "core.h5p.licenseversion": "Versione delle licenza", - "core.h5p.nocopyright": "Il contenuto non è corredato da informazioni sul copyright.", - "core.h5p.offlineDialogBody": "Non è stato possibile inviare le informazioni riguardo al completamento di questa azione. Si prega di controllare la connessione a internet.", - "core.h5p.offlineDialogHeader": "La connessione al server è stata persa", - "core.h5p.offlineDialogRetryButtonLabel": "Riprova ora", - "core.h5p.offlineDialogRetryMessage": "Riprova tra :num...", - "core.h5p.offlineSuccessfulSubmit": "Risultati inviati correttamente.", - "core.h5p.originator": "Ideatore", - "core.h5p.pd": "Pubblico dominio", - "core.h5p.pddl": "Public Domain Dedication e Licenza", - "core.h5p.pdm": "Public Domain Mark (PDM)", - "core.h5p.resizescript": "Includer questo script sul sito web se vuoi si desidera dimensionamento dinamico del contenuto incorporato:", - "core.h5p.resubmitScores": "Tentativo di inviare risultati memorizzati", - "core.h5p.reuse": "Riutilizza", - "core.h5p.reuseContent": "Riutilizza contenuto", - "core.h5p.reuseDescription": "Riutilizza questo contenuto.", - "core.h5p.showadvanced": "Visualizza avanzate", - "core.h5p.showless": "Visualizza di meno", - "core.h5p.showmore": "Visualizza di più", - "core.h5p.size": "Dimensione", - "core.h5p.source": "Fonte", - "core.h5p.startingover": "Stai per ricominciare.", - "core.h5p.sublevel": "Sottolivello", - "core.h5p.thumbnail": "Miniatura", - "core.h5p.title": "Titolo", - "core.h5p.undisclosed": "Non divulgato", - "core.h5p.year": "Anno", - "core.h5p.years": "Anno(i)", - "core.h5p.yearsfrom": "Anni (da)", - "core.h5p.yearsto": "Anni (a)", - "core.help": "Aiuto", - "core.hide": "Nascondi", - "core.hour": "ora", - "core.hours": "ore", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Immagine", - "core.imageviewer": "Visualizzatore di immagini", - "core.info": "Informazioni", - "core.invalidformdata": "Dati form errati", - "core.labelsep": ": ", - "core.lastaccess": "Accesso più recente", - "core.lastmodified": "Ultime modifiche", - "core.lastsync": "Ultima sincronizzazione", - "core.layoutgrid": "Griglia", - "core.list": "Elenco", - "core.listsep": ";", - "core.loading": "Caricamento in corso", - "core.location": "File o sito web", - "core.login.auth_email": "Account via email", - "core.login.authenticating": "Autenticazione in corso", - "core.login.cancel": "Annulla", - "core.login.changepassword": "Cambia password", - "core.login.confirmdeletesite": "Sei sicuro di eliminare il sito {{sitename}}?", - "core.login.connect": "Collegati!", - "core.login.connecttomoodle": "Collegati a Moodle", - "core.login.createaccount": "Crea il mio nuovo account", - "core.login.createuserandpass": "Scegli username e password", - "core.login.credentialsdescription": "Per favore inserisci username e password per l'autenticazione", - "core.login.emailconfirmsent": "

                Una email è stata inviata al tuo indirizzo {{$a}}

                \n

                Contiene semplici istruzioni per completare la tua registrazione.

                \n

                Se hai qualche difficoltà contatta l'amministratore del sito.

                ", - "core.login.emailconfirmsentsuccess": "L'email di conferma è stata inviata correttamente", - "core.login.erroraccesscontrolalloworigin": "La chiamata cross-origin che stai effettuando è stata rifiutata. Per ulteriori informazioni: https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Si è verificato un errore durante l'eliminazione di questo sito. Per favore riprova.", - "core.login.errorupdatesite": "Si è verificato un errore durante l'aggiornamento del token del sito.", - "core.login.firsttime": "È la prima volta che accedi qui?", - "core.login.forcepasswordchangenotice": "È necessario cambiare la password per proseguire.", - "core.login.forgotten": "Hai dimenticato lo username o la password?", - "core.login.help": "Aiuto", - "core.login.helpmelogin": "

                Esistono diverse migliaia di siti Moodle al mondo. Queta app può collegarsi solamente in quei siti dove è stato configurato l'accesso Mobile app.

                Se non riesci a collegarti al sito Moodle desiderato devi contattare l'amministratore di quel sito e chiedergli di documentarsi su http://docs.moodle.org/en/Mobile_app

                Per provare l'applicazione su un sito Moodle demo inserire teacher oppure student nel campo Username e fare click sul pulsante Aggiungi.

                ", - "core.login.instructions": "Istruzioni", - "core.login.invalidaccount": "Verificare le proprie credenziali o chiedere all'amministratore del sito di controllare la configurazione del sito.", - "core.login.invaliddate": "Data non corretta", - "core.login.invalidemail": "Indirizzo email non valido", - "core.login.invalidmoodleversion": "Versione di Moodle non valida. La versione minima richiesta è la {{$a}}:", - "core.login.invalidsite": "L'URL del sito non è valida", - "core.login.invalidtime": "Orario non valido", - "core.login.invalidurl": "La URL specificata non è valida", - "core.login.invalidvaluemax": "Il valore massimo è {{$a}}", - "core.login.invalidvaluemin": "Il valore minimo è {{$a}}", - "core.login.localmobileunexpectedresponse": "Il controllo delle Moodle Mobile Additional Feature ha restituito una risposta inattesa, sarai autenticato tramite i servizi Mobile standard.", - "core.login.login": "Login", - "core.login.loginbutton": "Login", - "core.login.logininsiterequired": "Devi autenticarti usando una finestra browser", - "core.login.loginsteps": "Per accedere al sito devi creare un account.", - "core.login.missingemail": "Non hai inserito l'email", - "core.login.missingfirstname": "Non hai inserito il nome", - "core.login.missinglastname": "Non hai inserito il cognome", - "core.login.mobileservicesnotenabled": "I servizi mobile non sono abilitati nel sito. Per richiederne l'abilitazione, contattare l'amministrazione del sito.", - "core.login.mustconfirm": "È necessario confermare l'account", - "core.login.newaccount": "Nuovo account", - "core.login.notloggedin": "Devi essere autenticato", - "core.login.password": "Password", - "core.login.passwordforgotten": "Password dimenticata", - "core.login.passwordforgotteninstructions2": "Per recuperare la password, inserisci lo username oppure l'email nei campi sottostanti. Se il dato inserito è presente nel database, riceverai un'email con le istruzioni per completare il recupero.", - "core.login.passwordrequired": "Password necessaria", - "core.login.policyaccept": "Ho letto le condizioni e le accetto", - "core.login.policyagree": "Per continuare ad usare questo sito, è necessario accettare le condizioni riportate.", - "core.login.policyagreement": "Condizioni di utilizzo del sito", - "core.login.policyagreementclick": "Leggi le condizioni di utilizzo del sito", - "core.login.potentialidps": "Autenticati su:", - "core.login.profileinvaliddata": "Valore non valido", - "core.login.reconnect": "Riconnetti", - "core.login.reconnectdescription": "Il token di autenticazione non è valido oppure è scaduto. Devi ricollegarti al sito.", - "core.login.reconnectssodescription": "Il token di autenticazione non è valido oppure è scaduto. Devi ricollegarti al sito autenticandoti tramite il browser.", - "core.login.resendemail": "Invia di nuovo l'email", - "core.login.security_question": "Domanda di sicurezza", - "core.login.selectacountry": "Stato", - "core.login.selectsite": "Seleziona il sito:", - "core.login.signupplugindisabled": "{{$a}} non è abilitato.", - "core.login.siteaddress": "Indirizzo del sito", - "core.login.siteinmaintenance": "Il sito è in modalità manutenzione", - "core.login.sitepolicynotagreederror": "Politiche del sito non accettate.", - "core.login.siteurl": "URL del sito", - "core.login.siteurlrequired": "L'URL del sito è obbligatoria. Esempio:http://www.yourmoodlesite.abc or https://www.yourmoodlesite.efg", - "core.login.startsignup": "Crea un account", - "core.login.supplyinfo": "Ulteriori informazioni", - "core.login.username": "Username", - "core.login.usernameoremail": "Inserisci username o indirizzo email", - "core.login.usernamerequired": "Username obbligatorio", - "core.login.usernotaddederror": "Errore - L'utente \"{{$a}}\" non è stato aggiunto", - "core.login.webservicesnotenabled": "Nel sito non sono stati abilitati i web service. Per favore contatta l'amministratore del sito per verificare la configurazione.", - "core.lostconnection": "Il token di autenticazione non è valido o è scaduto. Devi autenticarti nuovamente.", - "core.mainmenu.changesite": "Cambia sito", - "core.mainmenu.help": "Aiuto", - "core.mainmenu.logout": "Esci", - "core.mainmenu.website": "Sito web", - "core.maxsizeandattachments": "Dimensione massima dei file: {{$a.size}}, numero massimo di file: {{$a.attachments}}", - "core.min": "min.", - "core.mins": "min.", - "core.misc": "Generale", - "core.mod_assign": "Compito", - "core.mod_assignment": "Compito 2.2 (Disabilitato)", - "core.mod_book": "Libro", - "core.mod_chat": "Chat", - "core.mod_choice": "Scelta", - "core.mod_data": "Database", - "core.mod_database": "Database", - "core.mod_external-tool": "Tool esterno", - "core.mod_feedback": "Feedback", - "core.mod_file": "File", - "core.mod_folder": "Cartella", - "core.mod_forum": "Forum", - "core.mod_glossary": "Glossario", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS Content Package", - "core.mod_imscp": "IMS Content Package", - "core.mod_label": "Etichetta", - "core.mod_lesson": "Lezione", - "core.mod_lti": "Tool esterno", - "core.mod_page": "Pagina", - "core.mod_quiz": "Quiz", - "core.mod_resource": "Risorsa", - "core.mod_scorm": "Pacchetto SCORM", - "core.mod_survey": "Sondaggio", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Workshop", - "core.moduleintro": "Descrizione", - "core.more": "ancora", - "core.mygroups": "I miei gruppi", - "core.name": "Nome", - "core.networkerrormsg": "La rete non è abilitata o non funziona.", - "core.never": "Mai", - "core.next": "Successivo", - "core.no": "No", - "core.nocomments": "Non ci sono commenti", - "core.nograde": "Senza valutazione", - "core.none": "Nessuno", - "core.nopermissions": "Non sei autorizzato a svolgere questa azione ({{$a}})", - "core.noresults": "Nessun risultato", - "core.noselection": "Nessuna selezione", - "core.notapplicable": "n/d", - "core.notenrolledprofile": "Questo profilo non è disponibile perché questo utente non è iscritto a questo corso.", - "core.notice": "Nota", - "core.notingroup": "Per visualizzare l'attività devi appartenere ad un gruppo .", - "core.notsent": "Non inviato", - "core.now": "adesso", - "core.numwords": "{{$a}} parole", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Click per visualizare l'immagine a dimensioni reali", - "core.openinbrowser": "Apri nel browser", - "core.othergroups": "Altri gruppi", - "core.pagea": "Pagina {{$a}}", - "core.paymentinstant": "Utilizza il pulsante sottostante per pagare ed essere iscritto in pochi minuti!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefono", - "core.pictureof": "Immagine {{$a}}", - "core.previous": "Precedente", - "core.proceed": "Prosegui", - "core.pulltorefresh": "Trascina per aggiornare", - "core.question.answer": "Risposta", - "core.question.answersaved": "Risposta salvata", - "core.question.certainty": "Confidenza", - "core.question.complete": "Completo", - "core.question.correct": "Risposta corretta", - "core.question.feedback": "Feedback", - "core.question.incorrect": "Risposta errata", - "core.question.information": "Informazione", - "core.question.invalidanswer": "Risposta incompleta", - "core.question.notanswered": "Risposta non data", - "core.question.notyetanswered": "Risposta non ancora data", - "core.question.partiallycorrect": "Parzialmente corretta", - "core.question.questionmessage": "Domanda {{$a}}: {{$b}}", - "core.question.questionno": "Domanda {{$a}}", - "core.question.requiresgrading": "Richiede valutazione", - "core.quotausage": "Hai utilizzato {{$a.used}} su un massimo di {{$a.total}}", - "core.rating.aggregateavg": "Media dei voti", - "core.rating.aggregatecount": "Numero di voti", - "core.rating.aggregatemax": "Voto massimo", - "core.rating.aggregatemin": "Voto minimo", - "core.rating.aggregatesum": "Somma dei voti", - "core.rating.noratings": "Nessuna valutazione inviata", - "core.rating.rating": "Valutazione", - "core.rating.ratings": "Valutazioni", - "core.refresh": "Aggiorna", - "core.remove": "Rimuovi", - "core.required": "Obbligatorio", - "core.requireduserdatamissing": "Nel profilo di questo utente mancano alcuni dati. Per favore compila i dati mancanti e riprova.
                {{$a}}", - "core.resourcedisplayopen": "Apri", - "core.resources": "Risorse", - "core.restore": "Ripristino", - "core.restricted": "Accesso vincolato", - "core.retry": "Riprova", - "core.save": "Salva", - "core.savechanges": "Salva modifiche", - "core.search": "Cerca", - "core.searching": "Ricerca in corso", - "core.searchresults": "Risultati della ricerca", - "core.sec": "secondo", - "core.secs": "secondi", - "core.seemoredetail": "Clicca qui per ulteriori dettagli", - "core.selectacategory": "Per favore scegliere una categoria", - "core.selectacourse": "Scegli un corso", - "core.selectagroup": "Scegli un gruppo", - "core.send": "Invia", - "core.sending": "Invio in corso", - "core.serverconnection": "Si è verificato un errore durante la connessione al server", - "core.settings.about": "Informazioni", - "core.settings.cordovadevicemodel": "Modello del dispositivo Cordova", - "core.settings.cordovadeviceosversion": "Versione SO del dispositivo Cordova", - "core.settings.cordovadeviceplatform": "Piattaforma del dispositivo Cordova", - "core.settings.cordovadeviceuuid": "UUID del dispositivo Cordova", - "core.settings.cordovaversion": "Versione Cordova", - "core.settings.currentlanguage": "Lingua in uso", - "core.settings.debugdisplay": "Visualizza messaggi di debug", - "core.settings.deletesitefiles": "Sei sicuro di eliminare i file scaricati dal sito '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Elimina file del sito", - "core.settings.deviceinfo": "Informazioni sul dispositivo", - "core.settings.deviceos": "SO del dispositivo", - "core.settings.disableall": "Disabilita le notifiche", - "core.settings.disabled": "Disabilitato", - "core.settings.displayformat": "Formato di visualizzazione", - "core.settings.enabledownloadsection": "Abilita scaricamento sezioni", - "core.settings.enablerichtexteditor": "Abilita editor di testo", - "core.settings.enablesyncwifi": "Sincronizza solo tramite Wi-Fi", - "core.settings.errordeletesitefiles": "Si è verificato un errore durante l'eliminazione dei file del sito.", - "core.settings.errorsyncsite": "Si è verificato un errore durante la sincronizzazione dei dati dal sito. Per favore verifica la connessione internet e riprova.", - "core.settings.estimatedfreespace": "Spazio libero stimato", - "core.settings.filesystemroot": "Root del filesystem", - "core.settings.fontsizecharacter": "A", - "core.settings.general": "Generale", - "core.settings.language": "Lingua", - "core.settings.license": "Licenza", - "core.settings.localnotifavailable": "Notifiche locali disponibili", - "core.settings.locationhref": "URL Webview", - "core.settings.locked": "Bloccato", - "core.settings.loggedin": "Online", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Navigator language", - "core.settings.navigatoruseragent": "Navigator userAgent", - "core.settings.networkstatus": "Stato della connessione internet", - "core.settings.preferences": "Preferenze", - "core.settings.privacypolicy": "Privacy policy", - "core.settings.reportinbackground": "Invia errori automaticamente", - "core.settings.settings": "Impostazioni", - "core.settings.sites": "Siti", - "core.settings.spaceusage": "Spazio utilizzato", - "core.settings.synchronization": "Sincronizzazione", - "core.settings.synchronizenow": "Sincronizza adesso", - "core.settings.syncsettings": "Impostazioni di sincronizzazione", - "core.settings.total": "Totale", - "core.settings.wificonnection": "Connessione WiFi", - "core.sharedfiles.rename": "Rinomna", - "core.sharedfiles.replace": "Sostituisci", - "core.sharedfiles.sharedfiles": "File condivisi", - "core.show": "Visualizza", - "core.showless": "Visualizza meno elementi...", - "core.showmore": "Visualizza più elementi...", - "core.site": "Sito", - "core.sitehome.sitehome": "Home del sito", - "core.sitehome.sitenews": "Annunci del sito", - "core.sitemaintenance": "Il sito è sottoposto a manutenzione e non è attualmente disponibile", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Salta", - "core.sort": "Ordina", - "core.sortby": "Ordina per", - "core.start": "Inizia", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b %Y, %H:%M:%S", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Invia", - "core.success": "Operazione eseguita correttamente", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Raccolta di default", - "core.tag.inalltagcoll": "Ovunque", - "core.tag.itemstaggedwith": "{{$a.tagarea}} è stata associata a \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Nessun risultato per \"{{$a}}\"", - "core.tag.notagsfound": "Non sono stati trovati tag corrispondenti a {{$a}}\"", - "core.tag.searchtags": "Cerca tag", - "core.tag.showingfirsttags": "Visualizzazione dei {{$a}} più utilizzati", - "core.tag.tag": "Tag", - "core.tag.tagarea_course": "Corsi", - "core.tag.tagarea_course_modules": "Attività e risorse", - "core.tag.tagarea_post": "Intervento blog", - "core.tag.tagarea_user": "Interessi dell'utente", - "core.tag.tags": "Tag", - "core.teachers": "Docenti", - "core.thisdirection": "ltr", - "core.time": "Data/Ora", - "core.timesup": "Tempo scaduto!", - "core.today": "Oggi", - "core.tryagain": "Riprova", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "Nota", - "core.unexpectederror": "Si è verificato un errore inatteso. Riprova chiudendo e riaprendo l'applicazione.", - "core.unknown": "Sconosciuto", - "core.unlimited": "Nessun limite", - "core.unzipping": "Decompressione in corso", - "core.upgraderunning": "Il sito è in fase di aggiornamento, per favore provate ad accedere più tardi", - "core.user": "Utente", - "core.user.address": "Indirizzo", - "core.user.city": "Città /Località", - "core.user.contact": "Contatto", - "core.user.country": "Nazione", - "core.user.description": "Descrizione", - "core.user.details": "Dettagli", - "core.user.detailsnotavailable": "Non puoi visualizzare i dettagli di questo utente.", - "core.user.editingteacher": "Docente", - "core.user.email": "Indirizzo email", - "core.user.emailagain": "Indirizzo email (ripeti)", - "core.user.firstname": "Nome", - "core.user.interests": "Interessi", - "core.user.lastname": "Cognome", - "core.user.manager": "Manager", - "core.user.newpicture": "Nuova immagine", - "core.user.noparticipants": "In questo corso non sono stati trovati partecipanti", - "core.user.participants": "Partecipanti", - "core.user.phone1": "Telefono", - "core.user.phone2": "Cellulare", - "core.user.roles": "Ruoli", - "core.user.sendemail": "Email", - "core.user.student": "Studente", - "core.user.teacher": "Docente non editor", - "core.user.webpage": "Pagina web", - "core.userdeleted": "Questo account è stato eliminato", - "core.userdetails": "Dettagli dell'utente", - "core.usernotfullysetup": "Utente non completamente impostato", - "core.users": "Utenti", - "core.view": "Visualizza", - "core.viewprofile": "Visualizza", - "core.whatisyourage": "Quanti anni hai?", - "core.wheredoyoulive": "In quale nazioni vivi?", - "core.whoops": "Oops!", - "core.whyisthisrequired": "Perché viene richiesto?", - "core.wsfunctionnotavailable": "La funzione webservice non è disponibile.", - "core.year": "anno", - "core.years": "anni", - "core.yes": "Sì", - "core.youreoffline": "Sei offline", - "core.youreonline": "Sei di nuovo online" -} \ No newline at end of file diff --git a/src/assets/lang/ja.json b/src/assets/lang/ja.json deleted file mode 100644 index a72f59259..000000000 --- a/src/assets/lang/ja.json +++ /dev/null @@ -1,1894 +0,0 @@ -{ - "addon.badges.alignment": "調整", - "addon.badges.badgedetails": "バッジ詳細", - "addon.badges.badges": "バッジ", - "addon.badges.bendorsement": "承認", - "addon.badges.claimcomment": "承認コメント", - "addon.badges.claimid": "請求URL", - "addon.badges.contact": "連絡先", - "addon.badges.dateawarded": "発効日", - "addon.badges.expired": "有効期限切れ", - "addon.badges.expirydate": "有効期限", - "addon.badges.imageauthoremail": "イメージ作成者メール", - "addon.badges.imageauthorname": "イメージ作成者名", - "addon.badges.imageauthorurl": "イメージ作成者URL", - "addon.badges.imagecaption": "イメージ説明", - "addon.badges.issuancedetails": "バッジ有効期限", - "addon.badges.issuerdetails": "発行者詳細", - "addon.badges.issueremail": "Eメール", - "addon.badges.issuername": "発行者名", - "addon.badges.issuerurl": "発行者URL", - "addon.badges.language": "言語", - "addon.badges.noalignment": "このバッジには指定された外部スキルまたは標準はありません。", - "addon.badges.nobadges": "取得できるバッジはありません。", - "addon.badges.norelated": "このバッジには関連バッジはありません。", - "addon.badges.recipientdetails": "取得者詳細", - "addon.badges.relatedbages": "関連バッジ", - "addon.badges.version": "バージョン", - "addon.badges.warnexpired": "(このバッジの有効期限は切れています!)", - "addon.block_activitymodules.pluginname": "活動", - "addon.block_activityresults.pluginname": "活動結果", - "addon.block_badges.pluginname": "最新バッジ", - "addon.block_blogmenu.pluginname": "ブログメニュー", - "addon.block_blogrecent.pluginname": "最近のブログエントリ", - "addon.block_blogtags.pluginname": "ブログタグ", - "addon.block_calendarmonth.pluginname": "カレンダー", - "addon.block_calendarupcoming.pluginname": "直近イベント", - "addon.block_comments.pluginname": "コメント", - "addon.block_completionstatus.pluginname": "コース完了ステータス", - "addon.block_glossaryrandom.pluginname": "ランダム用語集エントリ", - "addon.block_learningplans.pluginname": "学習プラン", - "addon.block_myoverview.all": "すべて (表示から削除済みを除く)", - "addon.block_myoverview.allincludinghidden": "すべて", - "addon.block_myoverview.favourites": "星付き", - "addon.block_myoverview.future": "未来", - "addon.block_myoverview.hiddencourses": "表示から削除済み", - "addon.block_myoverview.inprogress": "進行中", - "addon.block_myoverview.lastaccessed": "最終アクセス", - "addon.block_myoverview.morecourses": "コースをさらに", - "addon.block_myoverview.nocourses": "コースなし", - "addon.block_myoverview.past": "過去", - "addon.block_myoverview.pluginname": "コース概要", - "addon.block_myoverview.shortname": "省略名", - "addon.block_myoverview.title": "コース名", - "addon.block_newsitems.pluginname": "最新アナウンスメント", - "addon.block_onlineusers.pluginname": "オンラインユーザ", - "addon.block_privatefiles.pluginname": "プライベートファイル", - "addon.block_recentactivity.pluginname": "最近の活動", - "addon.block_recentlyaccessedcourses.nocourses": "最近のコースなし", - "addon.block_recentlyaccessedcourses.pluginname": "最近アクセスされたコース", - "addon.block_recentlyaccesseditems.noitems": "最近のアイテムなし", - "addon.block_recentlyaccesseditems.pluginname": "最近アクセスされたアイテム", - "addon.block_rssclient.pluginname": "リモートRSSフィード", - "addon.block_selfcompletion.pluginname": "自己完了", - "addon.block_sitemainmenu.pluginname": "メインメニュー", - "addon.block_starredcourses.nocourses": "星付きコースなし", - "addon.block_starredcourses.pluginname": "星付きコース", - "addon.block_tags.pluginname": "タグ", - "addon.block_timeline.duedate": "期限", - "addon.block_timeline.next30days": "次の30日", - "addon.block_timeline.next3months": "次の3ヶ月", - "addon.block_timeline.next6months": "次の6ヶ月", - "addon.block_timeline.next7days": "次の7日", - "addon.block_timeline.nocoursesinprogress": "進行中のコースはありません。", - "addon.block_timeline.noevents": "直近の活動期限はありません。", - "addon.block_timeline.overdue": "期限切れ", - "addon.block_timeline.pluginname": "タイムライン", - "addon.block_timeline.sortbycourses": "コースで並べ替える", - "addon.block_timeline.sortbydates": "日付で並べ替える", - "addon.blog.blog": "ブログ", - "addon.blog.blogentries": "ブログエントリ", - "addon.blog.linktooriginalentry": "オリジナルのブログエントリにリンクする", - "addon.blog.noentriesyet": "表示できるエントリはありません。", - "addon.blog.publishtonoone": "あなたのみ閲覧可 (下書き)", - "addon.blog.publishtosite": "このサイトの誰でも閲覧可", - "addon.blog.publishtoworld": "世界中の誰でも閲覧可", - "addon.blog.siteblogheading": "サイトブログ", - "addon.calendar.allday": "終日", - "addon.calendar.calendar": "カレンダー", - "addon.calendar.calendarevents": "カレンダーイベント", - "addon.calendar.categoryevents": "カテゴリイベント", - "addon.calendar.confirmeventdelete": "本当にこのイベント「 {{$a}} 」を削除してもよろしいですか?", - "addon.calendar.confirmeventseriesdelete": "「 {{$a.name}} 」イベントはシリーズの一部です。このイベントのみ削除しますか、それともシリーズ内 {{$a.count}} イベントすべてを削除しますか?", - "addon.calendar.courseevents": "コースイベント", - "addon.calendar.daynext": "翌日", - "addon.calendar.dayprev": "前日", - "addon.calendar.defaultnotificationtime": "デフォルト通知時間", - "addon.calendar.deleteallevents": "すべてのイベントを削除する", - "addon.calendar.deleteevent": "イベントを削除する", - "addon.calendar.deleteoneevent": "このイベントを削除する", - "addon.calendar.durationminutes": "期間 (分)", - "addon.calendar.durationnone": "期間なし", - "addon.calendar.durationuntil": "終了日時:", - "addon.calendar.editevent": "イベントの編集", - "addon.calendar.errorloadevent": "イベントの読み込み時にエラーがありました。", - "addon.calendar.errorloadevents": "イベントの読み込み時にエラーがありました。", - "addon.calendar.eventcalendareventdeleted": "カレンダーイベントが削除されました。", - "addon.calendar.eventduration": "期間", - "addon.calendar.eventendtime": "終了日時", - "addon.calendar.eventkind": "イベントタイプ", - "addon.calendar.eventname": "イベントタイトル", - "addon.calendar.eventstarttime": "開始日時", - "addon.calendar.eventtype": "イベントタイプ", - "addon.calendar.fri": "金", - "addon.calendar.friday": "金曜日", - "addon.calendar.gotoactivity": "活動に移動する", - "addon.calendar.groupevents": "グループイベント", - "addon.calendar.invalidtimedurationminutes": "あなたが入力した期間は有効ではありません。ゼロよりも大きな期間を分で入力するか空白にしてください。", - "addon.calendar.invalidtimedurationuntil": "あなたはイベント開始日時より前の終了日時を選択しました。処理を続行する前に訂正してください。", - "addon.calendar.mon": "月", - "addon.calendar.monday": "月曜日", - "addon.calendar.monthlyview": "月表示", - "addon.calendar.newevent": "新しいイベント", - "addon.calendar.noevents": "イベントはありません", - "addon.calendar.nopermissiontoupdatecalendar": "申し訳ございません、あなたにはカレンダーイベントを更新するためのパーミッションがありません。", - "addon.calendar.repeatedevents": "繰り返しイベント", - "addon.calendar.repeateditall": "変更内容を他の {{$a}} 件の繰り返しイベントに適用する", - "addon.calendar.repeateditthis": "変更内容をこのイベントにのみ適用する。", - "addon.calendar.repeatevent": "このイベントを繰り返す", - "addon.calendar.repeatweeksl": "毎週、作成イベント数", - "addon.calendar.sat": "土", - "addon.calendar.saturday": "土曜日", - "addon.calendar.siteevents": "サイトイベント", - "addon.calendar.sun": "日", - "addon.calendar.sunday": "日曜日", - "addon.calendar.thu": "木", - "addon.calendar.thursday": "木曜日", - "addon.calendar.today": "本日", - "addon.calendar.tomorrow": "明日", - "addon.calendar.tue": "火", - "addon.calendar.tuesday": "火曜日", - "addon.calendar.typecategory": "カテゴリイベント", - "addon.calendar.typeclose": "終了イベント", - "addon.calendar.typecourse": "コースイベント", - "addon.calendar.typedue": "期限到来イベント", - "addon.calendar.typegradingdue": "評定期限イベント", - "addon.calendar.typegroup": "グループイベント", - "addon.calendar.typeopen": "開催中イベント", - "addon.calendar.typesite": "サイトイベント", - "addon.calendar.typeuser": "ユーザイベント", - "addon.calendar.upcomingevents": "直近イベント", - "addon.calendar.userevents": "ユーザイベント", - "addon.calendar.wed": "水", - "addon.calendar.wednesday": "水曜日", - "addon.calendar.when": "いつ", - "addon.calendar.yesterday": "昨日", - "addon.competency.activities": "活動", - "addon.competency.competencies": "コンピテンシー", - "addon.competency.competenciesmostoftennotproficientincourse": "このコースでほとんど熟達していないコンピテンシー", - "addon.competency.coursecompetencies": "コースコンピテンシー", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "このコース内でのコンピテンシー評定は学習プランに影響しません。", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "このコース内でのコンピテンシー評定は学習プラン内ですぐに更新されます。", - "addon.competency.crossreferencedcompetencies": "クロスリファレンスコンピテンシー", - "addon.competency.duedate": "期限", - "addon.competency.errornocompetenciesfound": "コンピテンシーが見つかりません", - "addon.competency.evidence": "エビデンス", - "addon.competency.evidence_competencyrule": "コンピテンシールールが合致しません。", - "addon.competency.evidence_coursecompleted": "コース「 {{$a}} 」が完了しました。", - "addon.competency.evidence_coursemodulecompleted": "活動「 {{$a}} 」が完了しました。", - "addon.competency.evidence_courserestored": "コース「 {{$a}} 」と共に評定がリストアされました。", - "addon.competency.evidence_evidenceofpriorlearninglinked": "事前学習エビデンス「 {{$a}} 」がリンクされました。", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "事前学習エビデンス「 {{$a}} 」がリンク解除されました。", - "addon.competency.evidence_manualoverride": "コンピテンシー評定は手動設定されました。", - "addon.competency.evidence_manualoverrideincourse": "コンピテンシー評定はコース「 {{$a}} 」で手動設定されました。", - "addon.competency.evidence_manualoverrideinplan": "コンピテンシー評定は学習プラン「 {{$a}} 」で手動設定されました。", - "addon.competency.learningplancompetencies": "学習プランコンピテンシー", - "addon.competency.learningplans": "学習プラン", - "addon.competency.myplans": "マイ学習プラン", - "addon.competency.noactivities": "活動なし", - "addon.competency.nocompetencies": "コンピテンシーなし", - "addon.competency.nocompetenciesincourse": "このコースにリンクされたコンピテンシーはありません。", - "addon.competency.nocrossreferencedcompetencies": "このコンピテンシーに相互参照されている他のコンピテンシーはありません。", - "addon.competency.noevidence": "エビデンスなし", - "addon.competency.noplanswerecreated": "学習プランは作成されませんでした。", - "addon.competency.nouserplanswithcompetency": "このコンピテンシーに学習プランは含まれていません。", - "addon.competency.path": "パス:", - "addon.competency.planstatusactive": "アクティブ", - "addon.competency.planstatuscomplete": "完了", - "addon.competency.planstatusdraft": "下書き", - "addon.competency.planstatusinreview": "レビュー中", - "addon.competency.planstatuswaitingforreview": "レビュー待ち", - "addon.competency.proficient": "熟達", - "addon.competency.progress": "進捗", - "addon.competency.rating": "評定", - "addon.competency.reviewstatus": "レビューステータス", - "addon.competency.status": "ステータス", - "addon.competency.template": "学習プランテンプレート", - "addon.competency.uponcoursecompletion": "コース完了時:", - "addon.competency.usercompetencystatus_idle": "待機", - "addon.competency.usercompetencystatus_inreview": "レビュー中", - "addon.competency.usercompetencystatus_waitingforreview": "レビュー待ち", - "addon.competency.userplans": "学習プラン", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} / {{$a.y}} のコンピテンシーで熟達しています。", - "addon.competency.xcompetenciesproficientoutofyincourse": "あなたはこのコースの {{$a.x}} / {{$a.y}} のコンピテンシーで熟達しています。", - "addon.coursecompletion.complete": "完了", - "addon.coursecompletion.completecourse": "コースを完了する", - "addon.coursecompletion.completed": "完了", - "addon.coursecompletion.completiondate": "完了日時", - "addon.coursecompletion.completionmenuitem": "完了", - "addon.coursecompletion.couldnotloadreport": "コース完了の読み込みができませんでした。後でもう一度試してください。", - "addon.coursecompletion.coursecompletion": "コース完了", - "addon.coursecompletion.criteria": "クライテリア", - "addon.coursecompletion.criteriagroup": "クライテリアグループ", - "addon.coursecompletion.criteriarequiredall": "以下のクライテリアすべてが必須である", - "addon.coursecompletion.criteriarequiredany": "以下いくつかのクライテリアが必須である", - "addon.coursecompletion.inprogress": "進行中", - "addon.coursecompletion.manualselfcompletion": "手動による自己完了", - "addon.coursecompletion.nottracked": "現在、あなたはこのコース内で完了により追跡されていません。", - "addon.coursecompletion.notyetstarted": "未開始", - "addon.coursecompletion.pending": "保留", - "addon.coursecompletion.required": "必須", - "addon.coursecompletion.requiredcriteria": "必須クライテリア", - "addon.coursecompletion.requirement": "前提条件", - "addon.coursecompletion.status": "ステータス", - "addon.coursecompletion.viewcoursereport": "コースレポートを表示する", - "addon.files.couldnotloadfiles": "以下のファイルが読み込みできませんでした。", - "addon.files.emptyfilelist": "表示するファイルがありません。", - "addon.files.erroruploadnotworking": "残念ながら、現在、あなたのサイトにファイルをアップロードすることはできません。", - "addon.files.files": "ファイル", - "addon.files.privatefiles": "プライベートファイル", - "addon.files.sitefiles": "サイトファイル", - "addon.messageoutput_airnotifier.processorsettingsdesc": "デバイスの設定", - "addon.messages.acceptandaddcontact": "承認およびコンタクトに追加する", - "addon.messages.addcontact": "コンタクトに追加する", - "addon.messages.addcontactconfirm": "本当にあなたのコンタクトに {{$a}} を追加してもよろしいですか?", - "addon.messages.addtofavourites": "会話に星を付ける", - "addon.messages.addtoyourcontacts": "コンタクトに追加する", - "addon.messages.blocknoncontacts": "不明なユーザをブロックする", - "addon.messages.blockuser": "ユーザをブロックする", - "addon.messages.blockuserconfirm": "本当に {{$a}} をブロックしてもよろしいですか?", - "addon.messages.contactableprivacy": "受け入れるメッセージの送信元:", - "addon.messages.contactableprivacy_coursemember": "マイコンタクトおよびマイコースの誰でも", - "addon.messages.contactableprivacy_onlycontacts": "マイコンタクトのみ", - "addon.messages.contactableprivacy_site": "サイトの誰でも", - "addon.messages.contactblocked": "コンタクトブロック済み", - "addon.messages.contactlistempty": "連絡先リストが空", - "addon.messages.contactname": "連絡先名称", - "addon.messages.contactrequestsent": "コンタクトリクエストが送信されました。", - "addon.messages.contacts": "コンタクト", - "addon.messages.conversationactions": "会話アクションメニュー", - "addon.messages.decline": "拒否", - "addon.messages.deleteallconfirm": "本当にこの会話すべてを削除してもよろしいですか? これにより他の会話参加者の会話が削除されるこはありません。", - "addon.messages.deleteallselfconfirm": "本当にこのプライベートな会話全体を削除してもよろしいですか?", - "addon.messages.deleteconversation": "会話を削除する", - "addon.messages.deleteforeveryone": "私および他の人のために削除する", - "addon.messages.errordeletemessage": "メッセージ消去中にエラーが発生しました。", - "addon.messages.errorwhileretrievingcontacts": "サーバから連絡先を取得中にエラーが発生しました。", - "addon.messages.errorwhileretrievingdiscussions": "サーバからディスカッションを受信中にエラーが発生しました。", - "addon.messages.errorwhileretrievingmessages": "サーバからメッセージを受信中にエラーが発生しました。", - "addon.messages.groupconversations": "グループ", - "addon.messages.groupinfo": "グループ情報", - "addon.messages.individualconversations": "プライベート", - "addon.messages.info": "ユーザ情報", - "addon.messages.isnotinyourcontacts": "あなたのコンタクトに {{$a}} は登録されていません。", - "addon.messages.message": "メッセージ", - "addon.messages.messagenotsent": "メッセージは送信されませんでした。後で再び試みてください。", - "addon.messages.messagepreferences": "メッセージプリファレンス", - "addon.messages.messages": "メッセージ", - "addon.messages.muteconversation": "ミュート", - "addon.messages.mutedconversation": "ミュート済み会話", - "addon.messages.newmessage": "新しいメッセージ", - "addon.messages.newmessages": "新規メッセージ...", - "addon.messages.nocontactrequests": "コンタクトリストはありません。", - "addon.messages.nocontactsgetstarted": "コンタクトなし", - "addon.messages.nofavourites": "星付きの会話はありません。", - "addon.messages.nogroupconversations": "グループ会話なし", - "addon.messages.noindividualconversations": "プライベート会話なし", - "addon.messages.nomessagesfound": "メッセージが見つかりませんでした。", - "addon.messages.noncontacts": "非コンタクト", - "addon.messages.nousersfound": "ユーザが見つかりません", - "addon.messages.numparticipants": "{{$a}} 参加者", - "addon.messages.removecontact": "コンタクトから削除する", - "addon.messages.removecontactconfirm": "本当にあなたのコンタクトから {{$a}} を削除してもよろしいですか?", - "addon.messages.removefromfavourites": "会話から星を解除する", - "addon.messages.removefromyourcontacts": "コンタクトから削除する", - "addon.messages.requests": "リクエスト", - "addon.messages.requirecontacttomessage": "あなたがメッセージを送信できるようにするには {{$a}} にコンタクトへの追加をリクエストする必要があります。", - "addon.messages.searchcombined": "人およびメッセージを検索する", - "addon.messages.selfconversation": "パーソナルスペース", - "addon.messages.selfconversationdefaultmessage": "後でアクセスするため、下書きメッセージ、リンク、ノート等を保存します。", - "addon.messages.sendcontactrequest": "コンタクトリクエストを送信する", - "addon.messages.type_blocked": "ブロックされています", - "addon.messages.type_offline": "オフライン", - "addon.messages.type_online": "オンライン", - "addon.messages.type_search": "結果の検索", - "addon.messages.type_strangers": "その他", - "addon.messages.unabletomessage": "あなたはこのユーザにメッセージを送信できません。", - "addon.messages.unblockuser": "ユーザのブロックを解除する", - "addon.messages.unblockuserconfirm": "本当に {{$a}} をブロック解除してもよろしいですか?", - "addon.messages.unmuteconversation": "ミュート解除", - "addon.messages.useentertosend": "Enterで送信する", - "addon.messages.userwouldliketocontactyou": "{{$a}} があなたにコンタクトしたいようです。", - "addon.messages.warningmessagenotsent": "ユーザ {{user}} へのメッセージ送信ができませんでした。 {{error}}", - "addon.messages.wouldliketocontactyou": "あなたへのコンタクト希望", - "addon.messages.you": "あなた:", - "addon.messages.youhaveblockeduser": "あなたはこのユーザをブロックしました。", - "addon.messages.yourcontactrequestpending": "あなたのコンタクトリクエストは {{$a}} により保留されています。", - "addon.mod_assign.acceptsubmissionstatement": "提出時宣誓文を受諾してください。", - "addon.mod_assign.addattempt": "別の提出を許可する", - "addon.mod_assign.addnewattempt": "新しい提出を追加する", - "addon.mod_assign.addnewattemptfromprevious": "前回の提出をもとに新しい提出を追加する", - "addon.mod_assign.addsubmission": "提出物をアップロード・入力する", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "課題詳細および提出フォームは {{$a}} から利用できます。", - "addon.mod_assign.allowsubmissionsfromdate": "開始日時", - "addon.mod_assign.allowsubmissionsfromdatesummary": "この課題は {{$a}} から提出を受け付けます。", - "addon.mod_assign.applytoteam": "グループ全体に評定およびフィードバックを提供する", - "addon.mod_assign.assignmentisdue": "課題の提出期限が到来しています。", - "addon.mod_assign.attemptnumber": "提出回数", - "addon.mod_assign.attemptreopenmethod": "提出再オープン", - "addon.mod_assign.attemptreopenmethod_manual": "手動", - "addon.mod_assign.attemptreopenmethod_untilpass": "合格するまで自動", - "addon.mod_assign.attemptsettings": "受験設定", - "addon.mod_assign.cannoteditduetostatementsubmission": "サイトから提出時宣誓文を取得することができなかったため、アプリでは提出物の追加や編集ができません。", - "addon.mod_assign.cannotgradefromapp": "評定方法にアプリでは未サポートあるいは変更できないものがあります。", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "サイトから提出時宣誓文が取得できなかったため、アプリから評定を提出することができませんでした。", - "addon.mod_assign.confirmsubmission": "本当にあなたの作業を評定のために提出してもよろしいですか? これ以上、あなたは変更できないようになります。", - "addon.mod_assign.currentattempt": "これは {{$a}} 回目の提出です。", - "addon.mod_assign.currentattemptof": "これは {{$a.attemptnumber}} 回目の提出です ( {{$a.maxattempts}} 回の提出が許可されています)。", - "addon.mod_assign.currentgrade": "評定表内の現在の評定", - "addon.mod_assign.cutoffdate": "遮断日時", - "addon.mod_assign.defaultteam": "デフォルトグループ", - "addon.mod_assign.duedate": "終了日時", - "addon.mod_assign.duedateno": "提出期限なし", - "addon.mod_assign.duedatereached": "この課題の提出期限を過ぎました。", - "addon.mod_assign.editingstatus": "編集ステータス", - "addon.mod_assign.editsubmission": "提出を編集する", - "addon.mod_assign.erroreditpluginsnotsupported": "提出物の編集がサポートされていないプラグインがあるため、アプリから提出物の追加や編集ができませんでした。", - "addon.mod_assign.errorshowinginformation": "提出物の情報を表示できません。", - "addon.mod_assign.extensionduedate": "延長提出期限", - "addon.mod_assign.feedbacknotsupported": "このフィードバックはアプリでは未サポートのため、すべての情報が含まれていない可能性があります", - "addon.mod_assign.grade": "評定", - "addon.mod_assign.graded": "評定済み", - "addon.mod_assign.gradedby": "評定者", - "addon.mod_assign.gradedfollowupsubmit": "評定済み - フォローアップ提出受信済み", - "addon.mod_assign.gradedon": "評定日時", - "addon.mod_assign.gradelocked": "この評点はロックされているか評定表内で上書きされています。", - "addon.mod_assign.gradenotsynced": "評定が同期できませんでした", - "addon.mod_assign.gradeoutof": "{{$a}} 点中の評点", - "addon.mod_assign.gradingstatus": "評定ステータス", - "addon.mod_assign.groupsubmissionsettings": "グループ提出設定", - "addon.mod_assign.hiddenuser": "参加者", - "addon.mod_assign.latesubmissions": "提出期限後の提出", - "addon.mod_assign.latesubmissionsaccepted": "{{$a}} まで許可されます。", - "addon.mod_assign.markingworkflowstate": "採点ワークフローステータス", - "addon.mod_assign.markingworkflowstateinmarking": "採点中", - "addon.mod_assign.markingworkflowstateinreview": "レビュー中", - "addon.mod_assign.markingworkflowstatenotmarked": "未採点", - "addon.mod_assign.markingworkflowstatereadyforrelease": "リリース準備完了", - "addon.mod_assign.markingworkflowstatereadyforreview": "採点完了", - "addon.mod_assign.markingworkflowstatereleased": "リリース済み", - "addon.mod_assign.modulenameplural": "課題", - "addon.mod_assign.multipleteams": "2つ以上のグループのメンバー", - "addon.mod_assign.multipleteams_desc": "グループで課題を提出する必要があります。あなたは2つ以上のグループのメンバーです。提出できるためにはあなたは1つのみのグループのメンバーである必要があります。あなたのグループメンバーシップを変更するには教師にご連絡ください。", - "addon.mod_assign.noattempt": "未提出", - "addon.mod_assign.nomoresubmissionsaccepted": "延長を許可された参加者のみ許可されます。", - "addon.mod_assign.noonlinesubmissions": "この課題においてあなたがオンラインで提出するものはありません。", - "addon.mod_assign.nosubmission": "この課題に関して提出されているものはありません。", - "addon.mod_assign.notallparticipantsareshown": "提出物のない参加者は表示されていません", - "addon.mod_assign.noteam": "どのグループのメンバーでもない", - "addon.mod_assign.noteam_desc": "この課題はグループで提出する必要があります。あなたはどのグループにも属していないため提出を作成することはできません。グループに追加されるにはあなたの教師にご連絡ください。", - "addon.mod_assign.notgraded": "未評定", - "addon.mod_assign.numberofdraftsubmissions": "下書き", - "addon.mod_assign.numberofparticipants": "参加者", - "addon.mod_assign.numberofsubmissionsneedgrading": "要評定", - "addon.mod_assign.numberofsubmittedassignments": "提出", - "addon.mod_assign.numberofteams": "グループ", - "addon.mod_assign.numwords": "{{$a}} 語", - "addon.mod_assign.outof": "{{$a.current}} / {{$a.total}}", - "addon.mod_assign.overdue": "課題は次の時間を超過しています: {{$a}}", - "addon.mod_assign.submission": "提出課題", - "addon.mod_assign.submissioneditable": "学生はこの提出を編集できます。", - "addon.mod_assign.submissionnoteditable": "学生はこの提出を編集できません。", - "addon.mod_assign.submissionnotsupported": "この提出物はアプリでは未サポートのため、すべての情報が含まれていない可能性があります", - "addon.mod_assign.submissionslocked": "この課題は提出を受け付けていません。", - "addon.mod_assign.submissionstatus": "提出ステータス", - "addon.mod_assign.submissionstatus_": "提出なし", - "addon.mod_assign.submissionstatus_draft": "下書き (未提出)", - "addon.mod_assign.submissionstatus_marked": "評定済み", - "addon.mod_assign.submissionstatus_new": "提出なし", - "addon.mod_assign.submissionstatus_reopened": "再オープン", - "addon.mod_assign.submissionstatus_submitted": "評定のために提出済み", - "addon.mod_assign.submissionstatusheading": "提出ステータス", - "addon.mod_assign.submissionteam": "グループ", - "addon.mod_assign.submitassignment": "課題を提出する", - "addon.mod_assign.submitassignment_help": "この課題を提出した時点であなたはこれ以上変更できないようになります。", - "addon.mod_assign.submittedearly": "課題は {{$a}} 早く提出されました。", - "addon.mod_assign.submittedlate": "課題は {{$a}} 遅く提出されました。", - "addon.mod_assign.timemodified": "最終更新日時", - "addon.mod_assign.timeremaining": "残り時間", - "addon.mod_assign.ungroupedusers": "設定「提出にグループを必要とする」が有効にされているため、そして何名かのユーザがグループのメンバーではないため、または2つ以上のグループメンバーであるため、提出することはできません。", - "addon.mod_assign.ungroupedusersoptional": "設定「学生がグループで提出する」が有効にされていますが、何名かのユーザはグループに属していないか、2つ以上のグループに属しています。これらの学生が「デフォルトグループ」のメンバーとして提出することをご了承ください。", - "addon.mod_assign.unlimitedattempts": "無制限", - "addon.mod_assign.userswhoneedtosubmit": "提出が必要なユーザ: {{$a}}", - "addon.mod_assign.userwithid": "ID {{id}} を持つユーザ", - "addon.mod_assign.viewsubmission": "提出を表示する", - "addon.mod_assign.warningsubmissiongrademodified": "提出物の評定がサイト上で変更されました。", - "addon.mod_assign.warningsubmissionmodified": "ユーザの提出物がサイト上で変更されました。", - "addon.mod_assign.wordlimit": "語数制限", - "addon.mod_assign_feedback_comments.pluginname": "フィードバックコメント", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF注釈", - "addon.mod_assign_feedback_file.pluginname": "ファイルフィードバック", - "addon.mod_assign_submission_comments.pluginname": "提出コメント", - "addon.mod_assign_submission_file.pluginname": "ファイル提出", - "addon.mod_assign_submission_onlinetext.pluginname": "オンラインテキスト提出", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "この課題の語数制限は {{$a.limit}} 語です。あなたは {{$a.count}} 語を提出しようとしています。あなたの提出を見直して再度提出してください。", - "addon.mod_book.errorchapter": "ブックの章の読み込み中にエラーが発生しました。", - "addon.mod_book.modulenameplural": "ブック", - "addon.mod_book.navnexttitle": "次へ: {{$a}}", - "addon.mod_book.navprevtitle": "前へ: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "ブックの章", - "addon.mod_book.toc": "目次", - "addon.mod_chat.beep": "ビープ", - "addon.mod_chat.chatreport": "チャットセッション", - "addon.mod_chat.currentusers": "現在のユーザ", - "addon.mod_chat.enterchat": "ここをクリックしてチャットルームに入室する", - "addon.mod_chat.entermessage": "あなたのメッセージを入力してください。", - "addon.mod_chat.errorwhileconnecting": "チャットに接続する際にエラーが発生しました。", - "addon.mod_chat.errorwhilegettingchatdata": "チャットのデータを取得中にエラーが発生しました。", - "addon.mod_chat.errorwhilegettingchatusers": "チャットのユーザを取得中にエラーが発生しました。", - "addon.mod_chat.errorwhileretrievingmessages": "サーバからメッセージを取得中にエラーが発生しました。", - "addon.mod_chat.errorwhilesendingmessage": "メッセージ送信中にエラーが発生しました。", - "addon.mod_chat.messagebeepseveryone": "{{$a}} が全員にビープします!", - "addon.mod_chat.messagebeepsyou": "{{$a}} があなたにビープしました!", - "addon.mod_chat.messageenter": "このチャットに {{$a}} が入室しました。", - "addon.mod_chat.messageexit": "このチャットから {{$a}} が退室しました。", - "addon.mod_chat.messages": "メッセージ", - "addon.mod_chat.messageyoubeep": "あなたは {{$a}} にビープしました。", - "addon.mod_chat.modulenameplural": "チャット", - "addon.mod_chat.mustbeonlinetosendmessages": "メッセージを送信するにはオンラインでなければなりません。", - "addon.mod_chat.nomessages": "メッセージがありません。", - "addon.mod_chat.saidto": ">", - "addon.mod_chat.send": "送信", - "addon.mod_chat.sessionstart": "次のチャットセッションは {{$a.date}} に開始されます (現在から {{$a.fromnow}} 後)。", - "addon.mod_chat.talk": "会話", - "addon.mod_chat.viewreport": "過去のチャットセッションを表示する", - "addon.mod_choice.cannotsubmit": "申し訳ございません、あなたの投票送信時に問題が発生しました。再度お試しください。", - "addon.mod_choice.choiceoptions": "投票オプション", - "addon.mod_choice.errorgetchoice": "選択データの取得中にエラーが発生しました。", - "addon.mod_choice.expired": "この活動は {{$a}} に終了しました。", - "addon.mod_choice.full": "(上限到達)", - "addon.mod_choice.modulenameplural": "投票", - "addon.mod_choice.noresultsviewable": "現在、投票結果は閲覧できません。", - "addon.mod_choice.notopenyet": "この活動は {{$a}} まで利用できません。", - "addon.mod_choice.numberofuser": "投票者数", - "addon.mod_choice.numberofuserinpercentage": "投票者数 (%)", - "addon.mod_choice.previewonly": "これはこの活動で利用可能なオプションのプレビューです。あなたの投票は {{$a}} まで送信することができません。", - "addon.mod_choice.publishinfoanonafter": "あなたが答えた後、匿名の結果が公開されます。", - "addon.mod_choice.publishinfoanonclose": "活動終了後、匿名の結果が公開されます。", - "addon.mod_choice.publishinfofullafter": "あなたが答えた後、すべての人の選択を表示する完全な結果が公開されます。", - "addon.mod_choice.publishinfofullclose": "活動終了後、すべての人の選択を表示する完全な結果が公開されます。", - "addon.mod_choice.publishinfonever": "あなたが答えた後、この活動の結果は公開されません。", - "addon.mod_choice.removemychoice": "私の投票を削除する", - "addon.mod_choice.responses": "投票結果", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% のユーザが選択肢: {{text}} を選択しました。", - "addon.mod_choice.responsesresultgraphheader": "グラフ表示", - "addon.mod_choice.resultsnotsynced": "結果にあなたの最後のレスポンスが含まれていません。更新のため、同期を行ってください。", - "addon.mod_choice.savemychoice": "私の投票を保存する", - "addon.mod_choice.userchoosethisoption": "このオプションを選択したユーザ", - "addon.mod_choice.yourselection": "あなたの投票", - "addon.mod_data.addentries": "エントリを追加する", - "addon.mod_data.advancedsearch": "高度な検索", - "addon.mod_data.alttext": "代替テキスト", - "addon.mod_data.approve": "承認", - "addon.mod_data.approved": "承認日時", - "addon.mod_data.ascending": "昇順", - "addon.mod_data.authorfirstname": "著者の名", - "addon.mod_data.authorlastname": "著者の姓", - "addon.mod_data.confirmdeleterecord": "本当にこのエントリを削除してもよろしいですか?", - "addon.mod_data.descending": "降順", - "addon.mod_data.disapprove": "承認を取り消す", - "addon.mod_data.emptyaddform": "あなたはどのフィールドにも入力していません!", - "addon.mod_data.entrieslefttoadd": "この活動を完了するにはさらに {{$a.entriesleft}} 件以上のエントリを追加してください。", - "addon.mod_data.entrieslefttoaddtoview": "他の参加者のエントリを閲覧するにはさらに {{$a.entrieslefttoview}} 件以上のエントリを追加してください。", - "addon.mod_data.errormustsupplyvalue": "あなたはここで値を提供する必要があります。", - "addon.mod_data.expired": "申し訳ございません、この活動は {{$a}} に終了して利用することはできません。", - "addon.mod_data.fields": "フィールド", - "addon.mod_data.foundrecords": "{{$a.num}}/{{$a.max}} 件のレコードが見つかりました (フィルタをリセットする)", - "addon.mod_data.latlongboth": "緯度および経度の両方とも必須です。", - "addon.mod_data.menuchoose": "選択 ...", - "addon.mod_data.modulenameplural": "データベース", - "addon.mod_data.more": "詳細", - "addon.mod_data.nomatch": "該当するエントリが見つかりませんでした!", - "addon.mod_data.norecords": "データベースにエントリはありません。", - "addon.mod_data.notapproved": "エントリはまだ承認されていません。", - "addon.mod_data.notopenyet": "申し訳ございません、この活動は {{$a}} まで利用できません。", - "addon.mod_data.numrecords": "{{$a}} エントリ", - "addon.mod_data.other": "その他", - "addon.mod_data.recordapproved": "エントリが承認されました。", - "addon.mod_data.recorddeleted": "エントリが削除されました。", - "addon.mod_data.recorddisapproved": "エントリ未承認", - "addon.mod_data.resetsettings": "フィルタをリセットする", - "addon.mod_data.search": "検索", - "addon.mod_data.selectedrequired": "選択したすべてを含む", - "addon.mod_data.single": "個別表示", - "addon.mod_data.tagarea_data_records": "データレコード", - "addon.mod_data.timeadded": "追加日時", - "addon.mod_data.timemodified": "修正日時", - "addon.mod_data.usedate": "検索に含む", - "addon.mod_feedback.analysis": "分析", - "addon.mod_feedback.anonymous": "匿名", - "addon.mod_feedback.anonymous_entries": "匿名エントリ ({{$a}})", - "addon.mod_feedback.average": "平均", - "addon.mod_feedback.captchaofflinewarning": "Capchaつきのフィードバックは、未設定の場合、オフラインモードの場合、サーバがダウンしている場合には完了できません。", - "addon.mod_feedback.complete_the_form": "質問に回答する", - "addon.mod_feedback.completed_feedbacks": "送信済み回答", - "addon.mod_feedback.continue_the_form": "質問への回答を続ける", - "addon.mod_feedback.feedback_is_not_open": "フィードバックは利用できません。", - "addon.mod_feedback.feedback_submitted_offline": "このフィードバックを、あとで提出するために保存しました。", - "addon.mod_feedback.feedbackclose": "フィードバック終了日時", - "addon.mod_feedback.feedbackopen": "フィードバック開始日時", - "addon.mod_feedback.mapcourses": "フィードバックをコースにマップする", - "addon.mod_feedback.maximal": "最大", - "addon.mod_feedback.minimal": "最小", - "addon.mod_feedback.mode": "モード", - "addon.mod_feedback.modulenameplural": "フィードバック", - "addon.mod_feedback.next_page": "次のページ", - "addon.mod_feedback.non_anonymous": "ユーザ名を記録して回答と共に表示する", - "addon.mod_feedback.non_anonymous_entries": "非匿名エントリ ({{$a}})", - "addon.mod_feedback.non_respondents_students": "未回答の学生 ({{$a}})", - "addon.mod_feedback.not_selected": "未選択", - "addon.mod_feedback.not_started": "未開始", - "addon.mod_feedback.numberoutofrange": "数字が範囲を超えています。", - "addon.mod_feedback.overview": "概要", - "addon.mod_feedback.page_after_submit": "完了メッセージ", - "addon.mod_feedback.preview": "プレビュー", - "addon.mod_feedback.previous_page": "前のページ", - "addon.mod_feedback.questions": "質問", - "addon.mod_feedback.response_nr": "回答No", - "addon.mod_feedback.responses": "回答", - "addon.mod_feedback.save_entries": "あなたの回答を送信する", - "addon.mod_feedback.show_entries": "回答を表示する", - "addon.mod_feedback.show_nonrespondents": "未回答者を表示する", - "addon.mod_feedback.started": "開始済み", - "addon.mod_feedback.this_feedback_is_already_submitted": "あなたはすでにこの活動を完了しています。", - "addon.mod_folder.emptyfilelist": "表示するファイルがありません。", - "addon.mod_folder.modulenameplural": "フォルダ", - "addon.mod_forum.addanewdiscussion": "新しいディスカッショントピックを追加する", - "addon.mod_forum.addanewquestion": "新しい質問を追加する", - "addon.mod_forum.addanewtopic": "新しいトピックを追加する", - "addon.mod_forum.addtofavourites": "このディスカッションに星を付ける", - "addon.mod_forum.advanced": "高度", - "addon.mod_forum.cannotadddiscussion": "このフォーラムにディスカッションを追加するにはグループのメンバーである必要があります。", - "addon.mod_forum.cannotadddiscussionall": "あなたにはすべての参加者のための新しいディスカッショントピックを追加するパーミッションがありません。", - "addon.mod_forum.cannotcreatediscussion": "新しいディスカッションを作成できませんでした。", - "addon.mod_forum.couldnotadd": "不明なエラーのためあなたの投稿を追加できませんでした。", - "addon.mod_forum.couldnotupdate": "不明なエラーのため投稿を更新できませんでした。", - "addon.mod_forum.cutoffdatereached": "このフォーラムの投稿遮断日に達しました。そのため、あなたはフォーラムに投稿できません。", - "addon.mod_forum.delete": "削除", - "addon.mod_forum.deletedpost": "投稿が削除されました。", - "addon.mod_forum.deletesure": "この投稿を削除してもよろしいですか?", - "addon.mod_forum.discussion": "ディスカッション", - "addon.mod_forum.discussionlistsortbycreatedasc": "作成日の昇順で並べ替える", - "addon.mod_forum.discussionlistsortbycreateddesc": "作成日の降順で並べ替える", - "addon.mod_forum.discussionlistsortbylastpostasc": "最終投稿の作成日の昇順で並べ替える", - "addon.mod_forum.discussionlistsortbylastpostdesc": "最終投稿の作成日の降順で並べ替える", - "addon.mod_forum.discussionlistsortbyrepliesasc": "返信数の昇順で並べ替える", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "返信数の降順で並べ替える", - "addon.mod_forum.discussionlocked": "このディスカッションはロックされているため、あなたは返信することはできません。", - "addon.mod_forum.discussionpinned": "ピン留め", - "addon.mod_forum.discussionsubscription": "ディスカッション購読", - "addon.mod_forum.edit": "編集", - "addon.mod_forum.erroremptymessage": "投稿メッセージを空にすることはできません。", - "addon.mod_forum.erroremptysubject": "投稿件名を空にすることはできません。", - "addon.mod_forum.errorgetforum": "フォーラムのデータ取得中にエラーが発生しました。", - "addon.mod_forum.errorgetgroups": "グループ設定の取得中にエラーが発生しました。", - "addon.mod_forum.favouriteupdated": "あなたの星オプションが更新されました。", - "addon.mod_forum.forumnodiscussionsyet": "このフォーラムにはまだディスカッショントピックがありません", - "addon.mod_forum.group": "グループ", - "addon.mod_forum.lastpost": "最新の投稿", - "addon.mod_forum.lockdiscussion": "このディスカッションをロックする", - "addon.mod_forum.lockupdated": "ロックオプションが更新されました。", - "addon.mod_forum.message": "メッセージ", - "addon.mod_forum.modeflatnewestfirst": "返信を新しいものからフラット表示する", - "addon.mod_forum.modeflatoldestfirst": "返信を古いものからフラット表示する", - "addon.mod_forum.modenested": "返信をネスト表示する", - "addon.mod_forum.modulenameplural": "フォーラム", - "addon.mod_forum.numdiscussions": "ディスカッション数 {{numdiscussions}}", - "addon.mod_forum.numreplies": "返信数 {{numreplies}}", - "addon.mod_forum.pindiscussion": "このディスカッションにピン留めする", - "addon.mod_forum.pinupdated": "ピン留めオプションが更新されました。", - "addon.mod_forum.postisprivatereply": "この投稿はプライベートなため他の参加者は閲覧できません。", - "addon.mod_forum.posttoforum": "フォーラムに投稿する", - "addon.mod_forum.posttomygroups": "すべてのグループにコピーを投稿する", - "addon.mod_forum.privatereply": "プライベートに返信する", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "ディスカッションをリフレッシュ", - "addon.mod_forum.refreshposts": "ディスカッション投稿をリフレッシュ", - "addon.mod_forum.removefromfavourites": "このディスカッションの星を外す", - "addon.mod_forum.reply": "返信", - "addon.mod_forum.replyplaceholder": "あなたの返信を記述する ...", - "addon.mod_forum.subject": "件名", - "addon.mod_forum.tagarea_forum_posts": "フォーラム投稿", - "addon.mod_forum.thisforumhasduedate": "このフォーラムの投稿の期限日は {{$a}} です。", - "addon.mod_forum.thisforumisdue": "このフォーラムの投稿の期限日は {{$a}} でした。", - "addon.mod_forum.unlockdiscussion": "このディスカッションのロックを解除する", - "addon.mod_forum.unpindiscussion": "このディスカッションのピン留めを外す", - "addon.mod_forum.unread": "未読", - "addon.mod_forum.unreadpostsnumber": "未読件数 {{$a}}", - "addon.mod_forum.yourreply": "あなたの返信", - "addon.mod_glossary.addentry": "新しいエントリを追加する", - "addon.mod_glossary.aliases": "キーワード", - "addon.mod_glossary.attachment": "添付", - "addon.mod_glossary.browsemode": "エントリをブラウズ", - "addon.mod_glossary.byalphabet": "アルファベット順", - "addon.mod_glossary.byauthor": "著者でグループ", - "addon.mod_glossary.bycategory": "カテゴリでグループ", - "addon.mod_glossary.bynewestfirst": "新規順", - "addon.mod_glossary.byrecentlyupdated": "最近の更新", - "addon.mod_glossary.bysearch": "検索", - "addon.mod_glossary.cannoteditentry": "エントリの編集ができませんでした", - "addon.mod_glossary.casesensitive": "大文字小文字を区別する", - "addon.mod_glossary.categories": "カテゴリ", - "addon.mod_glossary.concept": "用語", - "addon.mod_glossary.definition": "定義", - "addon.mod_glossary.entriestobesynced": "エントリの同期ができませんでした", - "addon.mod_glossary.entrypendingapproval": "このエントリは承認待ちです。", - "addon.mod_glossary.entryusedynalink": "このエントリを自動的にリンクさせる", - "addon.mod_glossary.errconceptalreadyexists": "この用語はすでに登録されています。この用語集では重複は許可されていません。", - "addon.mod_glossary.errorloadingentries": "エントリ読み込み中にエラーが発生しました。", - "addon.mod_glossary.errorloadingentry": "エントリ読み込み中にエラーが発生しました。", - "addon.mod_glossary.errorloadingglossary": "用語集を読み込み中にエラーが発生しました。", - "addon.mod_glossary.fillfields": "用語および定義は必須入力フィールドです。", - "addon.mod_glossary.fullmatch": "完全一致のみ", - "addon.mod_glossary.linking": "オートリンク", - "addon.mod_glossary.modulenameplural": "用語集", - "addon.mod_glossary.noentriesfound": "エントリが見つかりませんでした。", - "addon.mod_glossary.searchquery": "検索内容", - "addon.mod_glossary.tagarea_glossary_entries": "用語集エントリ", - "addon.mod_h5pactivity.all_attempts": "すべてのユーザ受験", - "addon.mod_h5pactivity.answer_checked": "解答チェック済み", - "addon.mod_h5pactivity.answer_correct": "あなたの答えは正解です。", - "addon.mod_h5pactivity.answer_fail": "不正解", - "addon.mod_h5pactivity.answer_incorrect": "あなたの答えは不正解です。", - "addon.mod_h5pactivity.answer_pass": "正解", - "addon.mod_h5pactivity.attempt": "受験", - "addon.mod_h5pactivity.attempt_completion_no": "この受験は完了マークされていません。", - "addon.mod_h5pactivity.attempt_completion_yes": "この受験は完了しています。", - "addon.mod_h5pactivity.attempt_success_fail": "不合格", - "addon.mod_h5pactivity.attempt_success_pass": "合格", - "addon.mod_h5pactivity.attempt_success_unknown": "未報告", - "addon.mod_h5pactivity.attempts_none": "このユーザには表示する受験がありません。", - "addon.mod_h5pactivity.completion": "完了", - "addon.mod_h5pactivity.duration": "継続時間", - "addon.mod_h5pactivity.maxscore": "最高点", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "マイ受験", - "addon.mod_h5pactivity.no_compatible_track": "このインタラクション ({{$a}}) がトラッキング情報を提供していないか、提供されたトラッキングに現在の活動バージョンとの互換性がありません。", - "addon.mod_h5pactivity.outcome": "アウトカム", - "addon.mod_h5pactivity.previewmode": "このコンテンツはプレビューモードで表示されています。受験追跡は保存されません。", - "addon.mod_h5pactivity.result_fill-in": "テキスト入力", - "addon.mod_h5pactivity.result_other": "不明なインタラクションタイプ", - "addon.mod_h5pactivity.review_my_attempts": "私の受験を表示する", - "addon.mod_h5pactivity.score": "点数", - "addon.mod_h5pactivity.score_out_of": "$a.rawscore}} / {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "開始日", - "addon.mod_h5pactivity.totalscore": "合計点", - "addon.mod_imscp.deploymenterror": "コンテンツパッケージエラー!", - "addon.mod_imscp.modulenameplural": "IMSコンテンツパッケージ", - "addon.mod_imscp.showmoduledescription": "説明の表示", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "答え", - "addon.mod_lesson.attempt": "受験: {{$a}}", - "addon.mod_lesson.attemptheader": "受験", - "addon.mod_lesson.attemptsremaining": "あなたには {{$a}} 回の受験回数が残っています。", - "addon.mod_lesson.averagescore": "平均評点", - "addon.mod_lesson.averagetime": "平均時間", - "addon.mod_lesson.branchtable": "コンテンツ", - "addon.mod_lesson.cannotfindattempt": "エラー: 受験が見つかりませんでした。", - "addon.mod_lesson.cannotfinduser": "エラー: ユーザが見つかりませんでした。", - "addon.mod_lesson.clusterjump": "クラスタ内の未閲覧の問題", - "addon.mod_lesson.completed": "完了", - "addon.mod_lesson.congratulations": "おめでとうございます - レッスンの最後に到達しました。", - "addon.mod_lesson.continue": "続ける", - "addon.mod_lesson.continuetonextpage": "続けて次のページに移動します。", - "addon.mod_lesson.defaultessayresponse": "あなたの作文はあなたの教師によって評定されます。", - "addon.mod_lesson.detailedstats": "詳細統計", - "addon.mod_lesson.didnotanswerquestion": "この問題に解答していません。", - "addon.mod_lesson.displayofgrade": "評点を表示する (学生のみ)", - "addon.mod_lesson.displayscorewithessays": "

                自動評定によるあなたの評点は {{$a.tempmaxgrade}} 点中 {{$a.score}} 点です。

                \n

                作文問題 {{$a.essayquestions}} が評定された後、あなたの最終評点に後日追加されます。

                \n

                作文問題を除くあなたの現在の評点は {{$a.grade}} 点中 {{$a.score}} 点です。

                ", - "addon.mod_lesson.displayscorewithoutessays": "あなたの評点は {{$a.score}} ({{$a.grade}} 点中)です。", - "addon.mod_lesson.emptypassword": "パスワードは空白にできません。", - "addon.mod_lesson.enterpassword": "パスワードを入力してください:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "あなたはどの問題にも解答していません。このレッスンのあなたの評点は0点です。", - "addon.mod_lesson.errorreviewretakenotlast": "この回答は、別の回答が完了しているため評価できません。", - "addon.mod_lesson.finish": "終了", - "addon.mod_lesson.finishretakeoffline": "この回答はオフライン状態で完了しました。", - "addon.mod_lesson.firstwrong": "あなたの解答は正しくありません。問題を再度受験しますか? (今から問題に正しく解答した場合、あなたの最終評点には加算されません)", - "addon.mod_lesson.gotoendoflesson": "レッスンの最後に移動する", - "addon.mod_lesson.grade": "評点", - "addon.mod_lesson.highscore": "最高評点", - "addon.mod_lesson.hightime": "最長時間", - "addon.mod_lesson.leftduringtimed": "あなたは制限時間のあるレッスンを途中で終了しました。
                レッスンを再スタートするには「続ける」をクリックしてください。", - "addon.mod_lesson.leftduringtimednoretake": "あなたは制限時間のあるレッスンを途中で終了しました。
                レッスンの再受験または継続は許可されていません。", - "addon.mod_lesson.lessonmenu": "レッスンメニュー", - "addon.mod_lesson.lessonstats": "レッスン統計", - "addon.mod_lesson.linkedmedia": "リンクメディア", - "addon.mod_lesson.loginfail": "ログインに失敗しました、再度ログインしてください ...", - "addon.mod_lesson.lowscore": "最低評点", - "addon.mod_lesson.lowtime": "最短時間", - "addon.mod_lesson.maximumnumberofattemptsreached": "最大受験回数に達しました - 次のページに移動しています。", - "addon.mod_lesson.modattemptsnoteacher": "学生レビューは学生にのみ表示されます。", - "addon.mod_lesson.modulenameplural": "レッスン", - "addon.mod_lesson.noanswer": "1つまたはそれ以上の問題が解答されていません。戻って解答を送信してください。", - "addon.mod_lesson.nolessonattempts": "このレッスンは受験されていません。", - "addon.mod_lesson.nolessonattemptsgroup": "このレッスンでは {{$a}} グループメンバーによる受験はありません。", - "addon.mod_lesson.notcompleted": "未了", - "addon.mod_lesson.numberofcorrectanswers": "正解数: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "解答済み問題数: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "解答済み問題数: {{$a.nquestions}} (あなたは少なくとも {{$a.minquestions}} 問の問題に解答する必要があります)", - "addon.mod_lesson.ongoingcustom": "現在、あなたの評点は {{$a.currenthigh}} 点中 {{$a.score}} 点です。", - "addon.mod_lesson.ongoingnormal": "あなたは {{$a.viewed}} 問中 {{$a.correct}} 問を正しく答えました。", - "addon.mod_lesson.or": "または", - "addon.mod_lesson.overview": "概要", - "addon.mod_lesson.preview": "プレビュー", - "addon.mod_lesson.progressbarteacherwarning2": "あなたはこのレッスンを編集できるため、プログレスバーは表示されません。", - "addon.mod_lesson.progresscompleted": "あなたは {{$a}}% のレッスンを完了しました。", - "addon.mod_lesson.question": "問題", - "addon.mod_lesson.rawgrade": "素点", - "addon.mod_lesson.reports": "レポート", - "addon.mod_lesson.response": "返答", - "addon.mod_lesson.retakefinishedinsync": "オフラインの回答が同期されました。評価しますか?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "レビュー", - "addon.mod_lesson.reviewlesson": "レッスンをレビューする", - "addon.mod_lesson.reviewquestionback": "はい、もう一度受験します", - "addon.mod_lesson.reviewquestioncontinue": "いいえ、次の問題に移動します", - "addon.mod_lesson.secondpluswrong": "もう一度受験しますか?", - "addon.mod_lesson.submit": "送信", - "addon.mod_lesson.teacherjumpwarning": "このレッスンでは {{$a.cluster}} ジャンプまたは {{$a.unseen}} ジャンプが使用されています。代わりに「次のページ」へのジャンプが使用されます。これらのジャンプを確認するには学生としてログインしてください。", - "addon.mod_lesson.teacherongoingwarning": "進行中の評点は学生にのみ表示されます。進行中の評点をテストするには学生としてログインしてください。", - "addon.mod_lesson.teachertimerwarning": "タイマーは学生に対してのみ作動します。タイマーをテストするには学生としてログインしてください。", - "addon.mod_lesson.thatsthecorrectanswer": "正解です。", - "addon.mod_lesson.thatsthewronganswer": "不正解です。", - "addon.mod_lesson.timeremaining": "残り時間", - "addon.mod_lesson.timetaken": "経過時間", - "addon.mod_lesson.unseenpageinbranch": "コンテンツページ内の未閲覧の問題", - "addon.mod_lesson.warningretakefinished": "回答はサイト内で完了しました。", - "addon.mod_lesson.welldone": "よくできました!", - "addon.mod_lesson.youhaveseen": "あなたはすでにこのレッスンを2ページ以上表示しました。
                あなたが表示した最後のページから始めますか?", - "addon.mod_lesson.youranswer": "あなたの答え", - "addon.mod_lesson.yourcurrentgradeisoutof": "あなたの現在の評点は {{$a.total}} 点中 {{$a.grade}} 点です。", - "addon.mod_lesson.youshouldview": "少なくとも {{$a}} 回解答してください。", - "addon.mod_lti.errorgetlti": "モジュールデータ取得中にエラーが発生しました。", - "addon.mod_lti.errorinvalidlaunchurl": "起動するURLが不正です。", - "addon.mod_lti.launchactivity": "アクティビティを起動", - "addon.mod_lti.modulenameplural": "外部ツール", - "addon.mod_page.errorwhileloadingthepage": "ページ内容を読み込み中にエラーが発生しました。", - "addon.mod_page.modulenameplural": "ページ", - "addon.mod_quiz.answercolon": "答え:", - "addon.mod_quiz.attemptfirst": "最初の受験", - "addon.mod_quiz.attemptlast": "最新の受験", - "addon.mod_quiz.attemptnumber": "受験", - "addon.mod_quiz.attemptquiznow": "問題を受験する", - "addon.mod_quiz.attemptstate": "状態", - "addon.mod_quiz.cannotsubmitquizdueto": "このクイズの回答は、以下の理由で提出できませんでした:", - "addon.mod_quiz.clearchoice": "私の選択をクリアする", - "addon.mod_quiz.comment": "コメント", - "addon.mod_quiz.completedon": "完了日時", - "addon.mod_quiz.confirmclose": "送信した場合、あなたは今回の受験の解答をこれ以上変更することはできません。", - "addon.mod_quiz.confirmcontinueoffline": "この回答は、理由「 {{$a}} 」により、同期できませんでした。もしあなたが別のデバイスで回答を続けていた場合、データが失われている可能性があります。", - "addon.mod_quiz.confirmleavequizonerror": "回答の保存中にエラーが発生しました。クイズを終了してもよいですか?", - "addon.mod_quiz.confirmstart": "あなたの受験には {{$a}} の時間制限があります。あなたが受験を開始した時点からタイマーがカウントダウンを開始します。タイマーは途中で停止できません。あなたは制限時間内に受験を終了する必要があります。本当に今から開始してもよろしいですか?", - "addon.mod_quiz.confirmstartheader": "時間制限", - "addon.mod_quiz.connectionerror": "ネットワークコネクションが切断されました (オートセーブ失敗)。\n\nこのページで入力した最後の数分間の解答をメモした後、再接続を試みてください。\n\n再度接続が確立された場合、あなたの解答は保存されこのメッセージは表示されなくなります。", - "addon.mod_quiz.continueattemptquiz": "前回の受験を続ける", - "addon.mod_quiz.continuepreview": "前回のプレビューを続ける", - "addon.mod_quiz.errorbehaviournotsupported": "このクイズは、アプリでサポートされていない動作が含まれているため回答することができません。", - "addon.mod_quiz.errordownloading": "必要なデータのダウンロード中にエラーが発生しました。", - "addon.mod_quiz.errorgetattempt": "回答データの取得中にエラーが発生しました。", - "addon.mod_quiz.errorgetquestions": "質問の取得中にエラーが発生しました。", - "addon.mod_quiz.errorgetquiz": "クイズデータの取得中にエラーが発生しました。", - "addon.mod_quiz.errorparsequestions": "質問の読み込み中にエラーが発生しました。Webブラウザからこのクイズに回答してください。", - "addon.mod_quiz.errorquestionsnotsupported": "このクイズは、アプリでサポートされていない質問を含む可能性があるため回答することができません。", - "addon.mod_quiz.errorrulesnotsupported": "このクイズは、アプリでサポートされていないアクセスルールがあるため回答することができません。", - "addon.mod_quiz.errorsaveattempt": "回答データの保存中にエラーが発生しました。", - "addon.mod_quiz.feedback": "フィードバック", - "addon.mod_quiz.finishattemptdots": "受験を終了する処理へ ...", - "addon.mod_quiz.finishnotsynced": "完了しましたが同期されていません。", - "addon.mod_quiz.grade": "評点", - "addon.mod_quiz.gradeaverage": "平均評点", - "addon.mod_quiz.gradehighest": "最高評点", - "addon.mod_quiz.grademethod": "評定方法", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}", - "addon.mod_quiz.marks": "得点", - "addon.mod_quiz.modulenameplural": "小テスト", - "addon.mod_quiz.mustbesubmittedby": "この受験は {{$a}} までに送信される必要があります。", - "addon.mod_quiz.noquestions": "まだ問題が追加されていません。", - "addon.mod_quiz.noreviewattempt": "あなたはこの受験のレビューを許可されていません。", - "addon.mod_quiz.notyetgraded": "未評定", - "addon.mod_quiz.opentoc": "ナビゲーションポップオーバーを開いてください。", - "addon.mod_quiz.outof": "{{$a.grade}} / {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} / {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}} / {{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "全体フィードバック", - "addon.mod_quiz.overdue": "期限切れ", - "addon.mod_quiz.overduemustbesubmittedby": "この受験は制限時間を過ぎています。この受験はすでに送信されている必要があります。あなたがこの小テストの評定を希望する場合、{{$a}} までに送信してください。それまでに送信しない場合、この受験の評点はカウントされません。", - "addon.mod_quiz.preview": "プレビュー", - "addon.mod_quiz.previewquiznow": "小テストをプレビューする", - "addon.mod_quiz.question": "問題", - "addon.mod_quiz.quiznavigation": "小テストナビゲーション", - "addon.mod_quiz.quizpassword": "小テストパスワード", - "addon.mod_quiz.reattemptquiz": "もう一度受験する", - "addon.mod_quiz.requirepasswordmessage": "この小テストを受験するためはパスワードを入力する必要があります。", - "addon.mod_quiz.returnattempt": "受験に戻る", - "addon.mod_quiz.review": "レビュー", - "addon.mod_quiz.reviewofattempt": "受験 {{$a}} のレビュー", - "addon.mod_quiz.reviewofpreview": "プレビューのレビュー", - "addon.mod_quiz.showall": "すべての問題を1ページに表示する", - "addon.mod_quiz.showeachpage": "一度に1ページのみ表示する", - "addon.mod_quiz.startattempt": "受験を開始する", - "addon.mod_quiz.startedon": "開始日時", - "addon.mod_quiz.stateabandoned": "未送信", - "addon.mod_quiz.statefinished": "終了", - "addon.mod_quiz.statefinisheddetails": "送信日時 {{$a}}", - "addon.mod_quiz.stateinprogress": "進行中", - "addon.mod_quiz.stateoverdue": "期限切れ", - "addon.mod_quiz.stateoverduedetails": "送信期限: {{$a}}", - "addon.mod_quiz.status": "ステータス", - "addon.mod_quiz.submitallandfinish": "すべての解答を送信して採点待ちにする", - "addon.mod_quiz.summaryofattempt": "受験概要", - "addon.mod_quiz.summaryofattempts": "あなたの前回の受験概要", - "addon.mod_quiz.timeleft": "残り時間", - "addon.mod_quiz.timetaken": "所要時間", - "addon.mod_quiz.warningattemptfinished": "オフラインの回答は、サイト上で完了していた、もしくは見当たらなかったため廃棄されました。", - "addon.mod_quiz.warningdatadiscarded": "質問文がオンラインで変更されたため、一部のオフライン回答が廃棄されました。", - "addon.mod_quiz.warningdatadiscardedfromfinished": "オフライン回答が廃棄されたため、回答が完了していません。回答内容を確認し、回答を再度提出してください。", - "addon.mod_quiz.yourfinalgradeis": "あなたの小テスト最終評点は {{$a}} です。", - "addon.mod_resource.errorwhileloadingthecontent": "内容を読み込み中にエラーが発生しました。", - "addon.mod_resource.modifieddate": "修正 {{$a}}", - "addon.mod_resource.modulenameplural": "ファイル", - "addon.mod_resource.openthefile": "ファイルを開く", - "addon.mod_resource.uploadeddate": "アップロード {{$a}}", - "addon.mod_scorm.asset": "アセット", - "addon.mod_scorm.assetlaunched": "アセット - 閲覧済み", - "addon.mod_scorm.attempts": "受験", - "addon.mod_scorm.averageattempt": "平均評点", - "addon.mod_scorm.browse": "プレビュー", - "addon.mod_scorm.browsed": "閲覧済み", - "addon.mod_scorm.browsemode": "プレビューモード", - "addon.mod_scorm.cannotcalculategrade": "評定が計算できませんでした。", - "addon.mod_scorm.completed": "完了", - "addon.mod_scorm.contents": "コンテンツ", - "addon.mod_scorm.dataattemptshown": "このデータは回答番号 {{number}} に該当します。", - "addon.mod_scorm.enter": "問題に入る", - "addon.mod_scorm.errorcreateofflineattempt": "新たなオフライン回答の作成中にエラーが発生しました。再度実行してください。", - "addon.mod_scorm.errordownloadscorm": "SCORM \"{{name}}\" のダウンロード中にエラーが発生しました。", - "addon.mod_scorm.errorgetscorm": "SCORMデータの取得中にエラーが発生しました。", - "addon.mod_scorm.errorinvalidversion": "申し訳ありません。アプリケーションはSCORM 1.2のみをサポートしています。", - "addon.mod_scorm.errornotdownloadable": "あなたのMoodleサイトではSCORMパッケージのダウンロードができません。Moodleサイト管理者に連絡してください。", - "addon.mod_scorm.errornovalidsco": "このSCORMはロードに必要な表示できるSCOがありません。", - "addon.mod_scorm.errorpackagefile": "申し訳ありません。アプリケーションはZIPパッケージのみをサポートしています。", - "addon.mod_scorm.errorsyncscorm": "同期中にエラーが発生しました。再度実行してください。", - "addon.mod_scorm.exceededmaxattempts": "あなたは最大受験回数に達しました。", - "addon.mod_scorm.failed": "不合格", - "addon.mod_scorm.firstattempt": "最初の受験", - "addon.mod_scorm.gradeaverage": "平均評点", - "addon.mod_scorm.gradeforattempt": "受験の評点", - "addon.mod_scorm.gradehighest": "最高評点", - "addon.mod_scorm.grademethod": "評定方法", - "addon.mod_scorm.gradereported": "記録済み評定", - "addon.mod_scorm.gradescoes": "学習オブジェクト", - "addon.mod_scorm.gradesum": "評点の合計", - "addon.mod_scorm.highestattempt": "最高評点", - "addon.mod_scorm.incomplete": "不完全", - "addon.mod_scorm.lastattempt": "最新の完了済み受験", - "addon.mod_scorm.modulenameplural": "SCORMパッケージ", - "addon.mod_scorm.newattempt": "新しい受験を開始する", - "addon.mod_scorm.noattemptsallowed": "許可された受験回数", - "addon.mod_scorm.noattemptsmade": "あなたの受験回数", - "addon.mod_scorm.notattempted": "未受験", - "addon.mod_scorm.offlineattemptnote": "この回答には動悸されなかったデータがあります。", - "addon.mod_scorm.offlineattemptovermax": "受験可能回数を超えたため、この回答を送信できません。", - "addon.mod_scorm.organizations": "組織", - "addon.mod_scorm.passed": "合格", - "addon.mod_scorm.reviewmode": "レビューモード", - "addon.mod_scorm.score": "評点", - "addon.mod_scorm.scormstatusnotdownloaded": "このSCORMはダウンロードされていません。これを開くときに、自動的にダウンロードされます。", - "addon.mod_scorm.scormstatusoutdated": "このSCORMは最後にダウンロードされた後に更新されています。開くときに、自動的に新しいものがダウンロードされます。", - "addon.mod_scorm.suspended": "一時停止", - "addon.mod_scorm.toc": "TOC", - "addon.mod_scorm.warningofflinedatadeleted": "回答番号 {{number}} を新しい回答として作成することができなかったため、オフラインデータの一部が消去されました。", - "addon.mod_scorm.warningsynconlineincomplete": "最終のオンライン回答が完了していないため、一部の回答が同期できませんでした。先にオンライン回答を完了してください。", - "addon.mod_survey.cannotsubmitsurvey": "申し訳ありません。あなたの調査提出で問題が発生しました。再度提出を実行してください。", - "addon.mod_survey.errorgetsurvey": "調査データの取得中にエラーが発生しました。", - "addon.mod_survey.ifoundthat": "私は次のことを発見しました:", - "addon.mod_survey.ipreferthat": "私は次のことが好きです:", - "addon.mod_survey.modulenameplural": "調査", - "addon.mod_survey.responses": "回答", - "addon.mod_survey.results": "結果", - "addon.mod_survey.surveycompletednograph": "あなたはこの調査を完了しました。", - "addon.mod_url.accessurl": "URLへのアクセス", - "addon.mod_url.modulenameplural": "URL", - "addon.mod_url.pointingtourl": "このリソースを示すURL", - "addon.mod_wiki.cannoteditpage": "あなたはこのページを編集できません。", - "addon.mod_wiki.createpage": "ページを作成する", - "addon.mod_wiki.editingpage": "このページ「 {{$a}} 」を編集する", - "addon.mod_wiki.errorloadingpage": "ページの読み込み中にエラーが発生しました。", - "addon.mod_wiki.errornowikiavailable": "このwikiにはまだ内容がありません。", - "addon.mod_wiki.gowikihome": "Wikiのホームへ移動", - "addon.mod_wiki.map": "マップ", - "addon.mod_wiki.modulenameplural": "Wiki", - "addon.mod_wiki.newpagehdr": "新しいページ", - "addon.mod_wiki.newpagetitle": "新しいページタイトル", - "addon.mod_wiki.nocontent": "このページにはコンテンツがありません。", - "addon.mod_wiki.notingroup": "グループ外", - "addon.mod_wiki.pageexists": "このページはすでに存在します。", - "addon.mod_wiki.pagename": "ページ名", - "addon.mod_wiki.subwiki": "サブwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wikiページ", - "addon.mod_wiki.titleshouldnotbeempty": "空のタイトルは受け付けられません", - "addon.mod_wiki.viewpage": "ページを表示", - "addon.mod_wiki.wikipage": "Wikiページ", - "addon.mod_wiki.wrongversionlock": "あなたが編集している間、別のユーザがこのページを編集しました。そのため、あなたのコンテンツは古くなりました。", - "addon.mod_workshop.alreadygraded": "評定済み", - "addon.mod_workshop.areainstructauthors": "提出のインストラクション", - "addon.mod_workshop.areainstructreviewers": "評価のインストラクション", - "addon.mod_workshop.assess": "評価", - "addon.mod_workshop.assessedsubmission": "評価済み提出", - "addon.mod_workshop.assessmentform": "評価フォーム", - "addon.mod_workshop.assessmentsettings": "評価設定", - "addon.mod_workshop.assessmentweight": "評価加重", - "addon.mod_workshop.assignedassessments": "評価が必要な割り当て済み提出", - "addon.mod_workshop.assignedassessmentsnone": "あなたには評価する提出が割り当てられていません。", - "addon.mod_workshop.conclusion": "結論", - "addon.mod_workshop.createsubmission": "あなたの送信準備を開始する", - "addon.mod_workshop.deletesubmission": "提出を削除する", - "addon.mod_workshop.editsubmission": "提出を編集する", - "addon.mod_workshop.feedbackauthor": "作成者へのフィードバック", - "addon.mod_workshop.feedbackby": "{{$a}} によるフィードバック", - "addon.mod_workshop.feedbackreviewer": "評価者へのフィードバック", - "addon.mod_workshop.givengrades": "与えた評点", - "addon.mod_workshop.gradecalculated": "提出に対する計算済み評点", - "addon.mod_workshop.gradeinfo": "評点: {{$a.received}} / {{$a.max}}", - "addon.mod_workshop.gradeover": "提出に対する評点をオーバーライドする", - "addon.mod_workshop.gradesreport": "ワークショップ評定レポート", - "addon.mod_workshop.gradinggrade": "評価に対する評点", - "addon.mod_workshop.gradinggradecalculated": "評価に対する計算済み評点", - "addon.mod_workshop.gradinggradeof": "評価に対する評点 (最大 {{$a}})", - "addon.mod_workshop.gradinggradeover": "評価に対する評点をオーバーライドする", - "addon.mod_workshop.modulenameplural": "ワークショップ", - "addon.mod_workshop.nogradeyet": "未評価", - "addon.mod_workshop.notassessed": "未評価", - "addon.mod_workshop.notoverridden": "未オーバーライド", - "addon.mod_workshop.noyoursubmission": "あなたはまだ自分の作業を提出していません。", - "addon.mod_workshop.overallfeedback": "全体フィードバック", - "addon.mod_workshop.publishedsubmissions": "公開済み提出", - "addon.mod_workshop.publishsubmission": "提出を公開する", - "addon.mod_workshop.publishsubmission_help": "ワークショップ終了後、公開済み提出を他のユーザが利用できます。", - "addon.mod_workshop.reassess": "再評価", - "addon.mod_workshop.receivedgrades": "与えられた評点", - "addon.mod_workshop.submissionattachment": "添付", - "addon.mod_workshop.submissioncontent": "提出コンテンツ", - "addon.mod_workshop.submissiondeleteconfirm": "本当に次の提出を削除してもよろしいですか?", - "addon.mod_workshop.submissiongrade": "提出に対する評点", - "addon.mod_workshop.submissiongradeof": "提出に対する評点 (最大 {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "あなたはテキストを入力するかファイルを追加する必要があります。", - "addon.mod_workshop.submissionsreport": "ワークショップ提出レポート", - "addon.mod_workshop.submissiontitle": "タイトル", - "addon.mod_workshop.switchphase10": "セットアップフェーズにスイッチする", - "addon.mod_workshop.switchphase20": "提出フェーズにスイッチする", - "addon.mod_workshop.switchphase30": "評価フェーズにスイッチする", - "addon.mod_workshop.switchphase40": "成績評価フェーズにスイッチする", - "addon.mod_workshop.switchphase50": "ワークショップを閉じる", - "addon.mod_workshop.userplan": "ワークショッププランナ", - "addon.mod_workshop.userplancurrentphase": "現在のフェーズ", - "addon.mod_workshop.weightinfo": "加重: {{$a}}", - "addon.mod_workshop.yourassessment": "あなたの評価", - "addon.mod_workshop.yourassessmentfor": "あなたの {{$a}} の評価", - "addon.mod_workshop.yourgrades": "あなたの評点", - "addon.mod_workshop.yoursubmission": "あなたの提出課題", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "{{$a}} へのコメント", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "{{$a}} の評定", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "アスペクト {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "あなたはこのアスペクトの評点を選択する必要があります。", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "{{$a}} へのコメント", - "addon.mod_workshop_assessment_comments.dimensionnumber": "アスペクト {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "{{$a}} へのコメント", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "{{$a}} の評定", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "主張 {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "クライテリア {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "あなたはこれらのアイテムから1つを選択する必要があります。", - "addon.notes.addnewnote": "新しいノートを追加する", - "addon.notes.coursenotes": "コースノート", - "addon.notes.deleteconfirm": "このノートを削除してもよろしいですか?", - "addon.notes.eventnotecreated": "ノートが作成されました。", - "addon.notes.eventnotedeleted": "ノートが削除されました。", - "addon.notes.nonotes": "まだこのタイプのノートはありません。", - "addon.notes.note": "ノート", - "addon.notes.notes": "ノート", - "addon.notes.personalnotes": "パーソナルノート", - "addon.notes.publishstate": "コンテクスト", - "addon.notes.sitenotes": "サイトノート", - "addon.notes.userwithid": "ID {{id}} のユーザ", - "addon.notes.warningnotenotsent": "コース {{course}} にノートを追加することができません。 {{error}}", - "addon.notifications.errorgetnotifications": "通知の取得中にエラーが発生しました。", - "addon.notifications.markallread": "すべてを既読にする", - "addon.notifications.notificationpreferences": "通知プリファレンス", - "addon.notifications.notifications": "通知", - "addon.notifications.playsound": "音を出力", - "addon.notifications.therearentnotificationsyet": "通知はありません", - "assets.countries.AD": "アンドラ公国", - "assets.countries.AE": "アラブ首長国連邦", - "assets.countries.AF": "アフガニスタン・イスラム共和国", - "assets.countries.AG": "アンティグア・バーブーダ", - "assets.countries.AI": "アンギラ", - "assets.countries.AL": "アルバニア共和国", - "assets.countries.AM": "アルメニア共和国", - "assets.countries.AO": "アンゴラ共和国", - "assets.countries.AQ": "南極地域", - "assets.countries.AR": "アルゼンチン共和国", - "assets.countries.AS": "米国領サモア", - "assets.countries.AT": "オーストリア共和国", - "assets.countries.AU": "オーストラリア", - "assets.countries.AW": "アルバ", - "assets.countries.AX": "オーランド諸島", - "assets.countries.AZ": "アゼルバイジャン共和国", - "assets.countries.BA": "ボスニア・ヘルツェゴビナ", - "assets.countries.BB": "バルバドス", - "assets.countries.BD": "バングラデシュ人民共和国", - "assets.countries.BE": "ベルギー王国", - "assets.countries.BF": "ブルキナファソ", - "assets.countries.BG": "ブルガリア共和国", - "assets.countries.BH": "バーレーン国", - "assets.countries.BI": "ブルンジ共和国", - "assets.countries.BJ": "ベナン共和国", - "assets.countries.BL": "サン・バルテルミー島", - "assets.countries.BM": "バーミューダ諸島", - "assets.countries.BN": "ブルネイ・ダルサラーム国", - "assets.countries.BO": "ボリビア多民族国", - "assets.countries.BQ": "ボネール、シント・ユースタティウスおよびサバ", - "assets.countries.BR": "ブラジル連邦共和国", - "assets.countries.BS": "バハマ国", - "assets.countries.BT": "ブータン王国", - "assets.countries.BV": "ブーベ島", - "assets.countries.BW": "ボツワナ共和国", - "assets.countries.BY": "ベラルーシ", - "assets.countries.BZ": "ベリーズ", - "assets.countries.CA": "カナダ", - "assets.countries.CC": "ココス (キーリング) 諸島", - "assets.countries.CD": "コンゴ民主共和国", - "assets.countries.CF": "中央アフリカ共和国", - "assets.countries.CG": "コンゴ共和国", - "assets.countries.CH": "スイス連邦", - "assets.countries.CI": "コートジボワール", - "assets.countries.CK": "クック諸島", - "assets.countries.CL": "チリ共和国", - "assets.countries.CM": "カメルーン共和国", - "assets.countries.CN": "中華人民共和国", - "assets.countries.CO": "コロンビア共和国", - "assets.countries.CR": "コスタリカ共和国", - "assets.countries.CU": "キューバ共和国", - "assets.countries.CV": "カーボベルデ共和国", - "assets.countries.CW": "キュラソー", - "assets.countries.CX": "クリスマス島", - "assets.countries.CY": "キプロス共和国", - "assets.countries.CZ": "チェコ", - "assets.countries.DE": "ドイツ連邦共和国", - "assets.countries.DJ": "ジブチ共和国", - "assets.countries.DK": "デンマーク王国", - "assets.countries.DM": "ドミニカ国", - "assets.countries.DO": "ドミニカ共和国", - "assets.countries.DZ": "アルジェリア民主人民共和国", - "assets.countries.EC": "エクアドル共和国", - "assets.countries.EE": "エストニア共和国", - "assets.countries.EG": "エジプト・アラブ共和国", - "assets.countries.EH": "西サハラ", - "assets.countries.ER": "エリトリア国", - "assets.countries.ES": "スペイン", - "assets.countries.ET": "エチオピア連邦民主共和国", - "assets.countries.FI": "フィンランド共和国", - "assets.countries.FJ": "フィジー共和国", - "assets.countries.FK": "フォークランド (マルビナス) 諸島", - "assets.countries.FM": "ミクロネシア連邦", - "assets.countries.FO": "フェロー諸島", - "assets.countries.FR": "フランス共和国", - "assets.countries.GA": "ガボン共和国", - "assets.countries.GB": "英国", - "assets.countries.GD": "グレナダ", - "assets.countries.GE": "グルジア共和国", - "assets.countries.GF": "フランス領ギアナ", - "assets.countries.GG": "ガーンジー島", - "assets.countries.GH": "ガーナ共和国", - "assets.countries.GI": "ジブラルタル", - "assets.countries.GL": "グリーンランド", - "assets.countries.GM": "ガンビア共和国", - "assets.countries.GN": "ギニア共和国", - "assets.countries.GP": "グアドループ島", - "assets.countries.GQ": "赤道ギニア共和国", - "assets.countries.GR": "ギリシャ共和国", - "assets.countries.GS": "サウスジョージア・サウスサンドウィッチ諸島", - "assets.countries.GT": "グアテマラ共和国", - "assets.countries.GU": "グァム", - "assets.countries.GW": "ギニアビサウ共和国", - "assets.countries.GY": "ガイアナ協同共和国", - "assets.countries.HK": "香港", - "assets.countries.HM": "ヘアド島・マクドナルド諸島", - "assets.countries.HN": "ホンジュラス共和国", - "assets.countries.HR": "クロアチア共和国", - "assets.countries.HT": "ハイチ共和国", - "assets.countries.HU": "ハンガリー共和国", - "assets.countries.ID": "インドネシア共和国", - "assets.countries.IE": "アイルランド", - "assets.countries.IL": "イスラエル国", - "assets.countries.IM": "マン島", - "assets.countries.IN": "インド", - "assets.countries.IO": "英国領インド洋地域", - "assets.countries.IQ": "イラク共和国", - "assets.countries.IR": "イラン・イスラム共和国", - "assets.countries.IS": "アイスランド共和国", - "assets.countries.IT": "イタリア共和国", - "assets.countries.JE": "ジャージー島", - "assets.countries.JM": "ジャマイカ", - "assets.countries.JO": "ヨルダン・ハシミテ王国", - "assets.countries.JP": "日本", - "assets.countries.KE": "ケニア共和国", - "assets.countries.KG": "キルギスタン共和国", - "assets.countries.KH": "カンボジア王国", - "assets.countries.KI": "キリバス共和国", - "assets.countries.KM": "コモロ・イスラム連邦共和国", - "assets.countries.KN": "セントクリストファー・ネイビス", - "assets.countries.KP": "朝鮮民主主義人民共和国", - "assets.countries.KR": "大韓民国", - "assets.countries.KW": "クウェート国", - "assets.countries.KY": "ケイマン諸島", - "assets.countries.KZ": "カザフスタン共和国", - "assets.countries.LA": "ラオス人民民主共和国", - "assets.countries.LB": "レバノン共和国", - "assets.countries.LC": "セントルシア", - "assets.countries.LI": "リヒテンシュタイン公国", - "assets.countries.LK": "スリランカ民主社会主義共和国", - "assets.countries.LR": "リベリア共和国", - "assets.countries.LS": "レソト王国", - "assets.countries.LT": "リトアニア共和国", - "assets.countries.LU": "ルクセンブルク大公国", - "assets.countries.LV": "ラトビア共和国", - "assets.countries.LY": "リビア", - "assets.countries.MA": "モロッコ王国", - "assets.countries.MC": "モナコ公国", - "assets.countries.MD": "モルドバ共和国", - "assets.countries.ME": "モンテネグロ", - "assets.countries.MF": "サン・マルタン (フランス領)", - "assets.countries.MG": "マダガスカル共和国", - "assets.countries.MH": "マーシャル諸島共和国", - "assets.countries.MK": "北マケドニア", - "assets.countries.ML": "マリ共和国", - "assets.countries.MM": "ミャンマー連邦", - "assets.countries.MN": "モンゴル国", - "assets.countries.MO": "マカオ", - "assets.countries.MP": "北マリアナ諸島", - "assets.countries.MQ": "マルチニーク島 (フランス統治)", - "assets.countries.MR": "モーリタニア・イスラム共和国", - "assets.countries.MS": "モントセラト (英国統治)", - "assets.countries.MT": "マルタ共和国", - "assets.countries.MU": "モーリシャス共和国", - "assets.countries.MV": "モルディヴ共和国", - "assets.countries.MW": "マラウイ共和国", - "assets.countries.MX": "メキシコ合衆国", - "assets.countries.MY": "マレーシア", - "assets.countries.MZ": "モザンビーク共和国", - "assets.countries.NA": "ナミビア共和国", - "assets.countries.NC": "ニューカレドニア", - "assets.countries.NE": "ニジェール共和国", - "assets.countries.NF": "ノーフォーク島", - "assets.countries.NG": "ナイジェリア連邦共和国", - "assets.countries.NI": "ニカラグア共和国", - "assets.countries.NL": "オランダ王国", - "assets.countries.NO": "ノルウェー王国", - "assets.countries.NP": "ネパール王国", - "assets.countries.NR": "ナウル共和国", - "assets.countries.NU": "ニウエ", - "assets.countries.NZ": "ニュージーランド", - "assets.countries.OM": "オマーン国", - "assets.countries.PA": "パナマ共和国", - "assets.countries.PE": "ペルー共和国", - "assets.countries.PF": "フランス領ポリネシア", - "assets.countries.PG": "パプアニューギニア", - "assets.countries.PH": "フィリピン共和国", - "assets.countries.PK": "パキスタン・イスラム共和国", - "assets.countries.PL": "ポーランド共和国", - "assets.countries.PM": "サンピエール島・ミクロン島", - "assets.countries.PN": "ピトケアン島", - "assets.countries.PR": "プエルトリコ", - "assets.countries.PS": "パレスチナ国", - "assets.countries.PT": "ポルトガル共和国", - "assets.countries.PW": "パラオ共和国", - "assets.countries.PY": "パラグアイ共和国", - "assets.countries.QA": "カタール国", - "assets.countries.RE": "レユニオン", - "assets.countries.RO": "ルーマニア", - "assets.countries.RS": "セルビア共和国", - "assets.countries.RU": "ロシア連邦", - "assets.countries.RW": "ルワンダ共和国", - "assets.countries.SA": "サウジアラビア王国", - "assets.countries.SB": "ソロモン諸島", - "assets.countries.SC": "セイシェル共和国", - "assets.countries.SD": "スーダン共和国", - "assets.countries.SE": "スウェーデン王国", - "assets.countries.SG": "シンガポール共和国", - "assets.countries.SH": "セントヘレナ・アセンションおよびトリスタンダクーニャ", - "assets.countries.SI": "スロベニア共和国", - "assets.countries.SJ": "スバールバル諸島およびヤンマイエン島", - "assets.countries.SK": "スロバキア共和国", - "assets.countries.SL": "シエラレオネ共和国", - "assets.countries.SM": "サンマリノ共和国", - "assets.countries.SN": "セネガル共和国", - "assets.countries.SO": "ソマリア民主共和国", - "assets.countries.SR": "スリナム共和国", - "assets.countries.SS": "南スーダン", - "assets.countries.ST": "サントメ・プリンシペ民主共和国", - "assets.countries.SV": "エルサルバドル共和国", - "assets.countries.SX": "シント・マールテン (オランダ領)", - "assets.countries.SY": "シリア・アラブ共和国", - "assets.countries.SZ": "エスワティニ王国", - "assets.countries.TC": "タークス・カイコス諸島", - "assets.countries.TD": "チャド共和国", - "assets.countries.TF": "フランス領極南諸島", - "assets.countries.TG": "トーゴ共和国", - "assets.countries.TH": "タイ王国", - "assets.countries.TJ": "タジキスタン共和国", - "assets.countries.TK": "トケラウ諸島", - "assets.countries.TL": "東ティモール民主共和国", - "assets.countries.TM": "トルクメニスタン", - "assets.countries.TN": "チュニジア共和国", - "assets.countries.TO": "トンガ王国", - "assets.countries.TR": "トルコ共和国", - "assets.countries.TT": "トリニダード・トバゴ共和国", - "assets.countries.TV": "ツバル", - "assets.countries.TW": "台湾", - "assets.countries.TZ": "タンザニア連合共和国", - "assets.countries.UA": "ウクライナ", - "assets.countries.UG": "ウガンダ共和国", - "assets.countries.UM": "アメリカ合衆国外諸島", - "assets.countries.US": "アメリカ合衆国", - "assets.countries.UY": "ウルグアイ東方共和国", - "assets.countries.UZ": "ウズベキスタン共和国", - "assets.countries.VA": "教皇庁 (バチカン市国)", - "assets.countries.VC": "セントビンセントおよびグレナディーン諸島", - "assets.countries.VE": "ベネズエラ・ボリバル共和国", - "assets.countries.VG": "英国領バージン諸島", - "assets.countries.VI": "米国領バージン諸島", - "assets.countries.VN": "ベトナム社会主義共和国", - "assets.countries.VU": "バヌアツ共和国", - "assets.countries.WF": "ウォリス・フツナ諸島", - "assets.countries.WS": "西サモア", - "assets.countries.YE": "イエメン共和国", - "assets.countries.YT": "マイヨット島", - "assets.countries.ZA": "南アフリカ共和国", - "assets.countries.ZM": "ザンビア共和国", - "assets.countries.ZW": "ジンバブエ共和国", - "assets.mimetypes.application/epub_zip": "EPUB電子書籍", - "assets.mimetypes.application/msword": "Wordドキュメント", - "assets.mimetypes.application/pdf": "PDFドキュメント", - "assets.mimetypes.application/vnd.moodle.backup": "Moodleバックアップ", - "assets.mimetypes.application/vnd.ms-excel": "Excelスプレッドシート", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007マクロ有効化ワークブック", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpointプレゼンテーション", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocumentスプレッドシート", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocumentスプレッドシートテンプレート", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocumentテキストドキュメント", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocumentテキストテンプレート", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocumentウェブページテンプレート", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007プレゼンテーション", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007スライドショー", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007スプレッドシート", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007テンプレート", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007ドキュメント", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynoteプレゼンテーション", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbersスプレッドシート", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pagesドキュメント", - "assets.mimetypes.application/x-javascript": "JavaScriptソース", - "assets.mimetypes.application/x-mspublisher": "Publisherドキュメント", - "assets.mimetypes.application/x-shockwave-flash": "Flashアニメーション", - "assets.mimetypes.application/xhtml_xml": "XHTMLドキュメント", - "assets.mimetypes.archive": "アーカイブ ({{$a.EXT}})", - "assets.mimetypes.audio": "オーディオファイル ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "ファイル", - "assets.mimetypes.group:archive": "アーカイブファイル", - "assets.mimetypes.group:audio": "オーディオファイル", - "assets.mimetypes.group:document": "ドキュメントファイル", - "assets.mimetypes.group:html_audio": "ブラウザでネイティブにサポートされるオーディオファイル", - "assets.mimetypes.group:html_track": "HTML trackファイル", - "assets.mimetypes.group:html_video": "ブラウザでネイティブにサポートされるビデオオファイル", - "assets.mimetypes.group:image": "イメージファイル", - "assets.mimetypes.group:presentation": "プレゼンテーションファイル", - "assets.mimetypes.group:sourcecode": "ソースコード", - "assets.mimetypes.group:spreadsheet": "スプレッドシートファイル", - "assets.mimetypes.group:video": "ビデオファイル", - "assets.mimetypes.group:web_audio": "ウェブで使用されるオーディオファイル", - "assets.mimetypes.group:web_file": "ウェブファイル", - "assets.mimetypes.group:web_image": "ウェブで使用されるイメージファイル", - "assets.mimetypes.group:web_video": "ウェブで使用されるビデオファイル", - "assets.mimetypes.image": "イメージ ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windowsアイコン", - "assets.mimetypes.text/css": "カスケーディングスタイルシート", - "assets.mimetypes.text/csv": "カンマ区切り値", - "assets.mimetypes.text/html": "HTMLドキュメント", - "assets.mimetypes.text/plain": "テキストファイル", - "assets.mimetypes.text/rtf": "RTFドキュメント", - "assets.mimetypes.text/vtt": "ウェブビデオテキストトラック", - "assets.mimetypes.video": "ビデオファイル ({{$a.EXT}})", - "core.accounts": "アカウント", - "core.add": "追加", - "core.agelocationverification": "年齢および場所の確認", - "core.ago": "{{$a}} 前", - "core.all": "すべて", - "core.allgroups": "すべてのグループ", - "core.allparticipants": "すべての参加者", - "core.answer": "回答", - "core.answered": "解答済み", - "core.areyousure": "本当によろしいですか?", - "core.back": "戻る", - "core.block.blocks": "ブロック", - "core.cancel": "キャンセル", - "core.cannotconnect": "接続できません:正しいURLを入力しているか、サイトのMoodleが{{$a}}以降であることを確認してください。", - "core.cannotdownloadfiles": "ダウンロードしようとしているファイルは、あなたのモバイルサービスでは無効になっています。あなたのサイト管理者に連絡してください。", - "core.category": "カテゴリ", - "core.choose": "選択", - "core.choosedots": "選択 ...", - "core.clearsearch": "検索のクリア", - "core.clicktohideshow": "展開または折りたたむにはここをクリックしてください。", - "core.clicktoseefull": "クリックで全てのコンテンツを見る", - "core.close": "閉じる", - "core.comments": "コメント", - "core.comments.addcomment": "コメントを追加する ...", - "core.comments.comments": "コメント", - "core.comments.commentscount": "コメント ({{$a}})", - "core.comments.deletecommentbyon": "{{$a.user}} により {{$a.time}} に投稿されたコメントを削除する", - "core.comments.eventcommentcreated": "コメントが作成されました。", - "core.comments.eventcommentdeleted": "コメントが削除されました。", - "core.comments.nocomments": "コメントなし", - "core.comments.savecomment": "コメントを保存する", - "core.commentscount": "コメント ({{$a}})", - "core.completion-alt-auto-fail": "完了: {{$a}} (合格点未到達)", - "core.completion-alt-auto-n": "未完了: {{$a}}", - "core.completion-alt-auto-n-override": "未完了: {{$a.modname}} ({{$a.overrideuser}} による設定)", - "core.completion-alt-auto-pass": "完了: {{$a}} (合格点到達)", - "core.completion-alt-auto-y": "完了: {{$a}}", - "core.completion-alt-auto-y-override": "完了: {{$a.modname}} ({{$a.overrideuser}} による設定)", - "core.completion-alt-manual-n": "未完了: {{$a}} 完了マークするには選択してください。", - "core.completion-alt-manual-n-override": "未完了: {{$a.modname}} ({{$a.overrideuser}} による設定) 完了をマークするには選択してください。", - "core.completion-alt-manual-y": "完了: {{$a}} 未完了マークするには選択してください。", - "core.completion-alt-manual-y-override": "完了: {{$a.modname}} ({{$a.overrideuser}} による設定) 未完了をマークするには選択してください。", - "core.confirmcanceledit": "本当にこのページを離れますか? 全ての変更が失われます。", - "core.confirmdeletefile": "本当にこのファイルを削除してもよろしいですか?", - "core.confirmloss": "本当ですか? すべての変更が失われます。", - "core.confirmopeninbrowser": "これをブラウザで開きますか?", - "core.considereddigitalminor": "あなたはこのサイトでアカウントを作成するには若すぎます。", - "core.content": "コンテンツ", - "core.contenteditingsynced": "編集中のコンテンツが同期されました。", - "core.contentlinks.chooseaccount": "アカウントの選択", - "core.contentlinks.chooseaccounttoopenlink": "リンクを開くアカウントを選択してください。", - "core.contentlinks.confirmurlothersite": "このリンクは別のサイトに属しています。本当に開きますか?", - "core.contentlinks.errornoactions": "このリンクを実行するためのアクションが見つかりませんでした。", - "core.contentlinks.errornosites": "このリンクをもつサイトが見つかりませんでした。", - "core.continue": "続ける", - "core.copiedtoclipboard": "クリップボードにコピーされたテキスト", - "core.course": "コース", - "core.course.allsections": "全てのセクション", - "core.course.contents": "コンテンツ", - "core.course.coursesummary": "コース概要", - "core.course.downloadcourse": "コースをダウンロードする", - "core.course.hiddenfromstudents": "学生から秘匿", - "core.course.hiddenoncoursepage": "利用可、しかしコースページに表示しない", - "core.course.overriddennotice": "この活動に関するあなたの評点は手動で調整されました。", - "core.course.sections": "セクション", - "core.coursedetails": "コース詳細", - "core.courses.addtofavourites": "このコースに星を付ける", - "core.courses.allowguests": "このコースにはゲストユーザも入ることができます。", - "core.courses.availablecourses": "コース一覧", - "core.courses.categories": "コースカテゴリ", - "core.courses.courses": "コース", - "core.courses.frontpage": "フロントページ", - "core.courses.hidecourse": "表示から削除する", - "core.courses.ignore": "無視", - "core.courses.mycourses": "マイコース", - "core.courses.mymoodle": "ダッシュボード", - "core.courses.nocourses": "表示するコース情報はありません。", - "core.courses.nocoursesyet": "このカテゴリにコースはありません。", - "core.courses.nosearchresults": "該当なし", - "core.courses.notenroled": "あなたはこのコースに登録されていません。", - "core.courses.paymentrequired": "このコースへの登録は有料です。", - "core.courses.paypalaccepted": "PayPal支払済み", - "core.courses.reload": "リロード", - "core.courses.removefromfavourites": "このコースの星を外す", - "core.courses.search": "検索", - "core.courses.searchcourses": "コースを検索する", - "core.courses.sendpaymentbutton": "PayPalで支払いを送信する", - "core.courses.show": "表示にリストアする", - "core.currentdevice": "現在のデバイス", - "core.datastoredoffline": "送信できなかったため、データはデバイスに保存されました。後で自動的に送信されます。", - "core.date": "日付", - "core.day": "日", - "core.days": "日", - "core.decsep": ".", - "core.defaultvalue": "デフォルト ({{$a}})", - "core.delete": "削除", - "core.deleteduser": "削除済みユーザ", - "core.deleting": "消去中", - "core.description": "説明", - "core.dfdaymonthyear": "YYYY/MM/DD", - "core.dfdayweekmonth": "MMM月D日(ddd)", - "core.dffulldate": "YYYY年MMMM月D日(dddd) h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "デジタル未成年", - "core.digitalminor_desc": "あなたの保護者が次の人に連絡するようにしてください:", - "core.discard": "無視", - "core.dismiss": "取消", - "core.displayoptions": "オプションを表示する", - "core.done": "完了", - "core.download": "ダウンロード", - "core.downloading": "ダウンロード中", - "core.edit": "編集", - "core.editor.autosavesucceeded": "下書きが保存されました。", - "core.editor.bold": "太字", - "core.editor.clear": "書式設定をクリアする", - "core.editor.h3": "ヘッディング (大)", - "core.editor.h4": "ヘッディング (中)", - "core.editor.h5": "ヘッディング (小)", - "core.editor.italic": "イタリック", - "core.editor.orderedlist": "順序付きリスト", - "core.editor.p": "段落", - "core.editor.strike": "打ち消し線", - "core.editor.textrecovered": "このテキストの下書きバージョンが自動的にリストアされました。", - "core.editor.underline": "下線", - "core.editor.unorderedlist": "番号なしリスト", - "core.emptysplit": "左パネルが空またはロード中のため本ページは空白", - "core.error": "エラー", - "core.errorchangecompletion": "完了状態の変更中にエラーが発生しました。再度実行してください。", - "core.errordeletefile": "ファイル消去中にエラーが発生しました。再度実行してください。", - "core.errordownloading": "ファイルダウンロードのエラー", - "core.errordownloadingsomefiles": "モジュールファイルのダウンロード中にエラーが発生しました。欠落しているファイルがあるかもしれません。", - "core.errorfileexistssamename": "同じ名前のファイルがあります。", - "core.errorinvalidform": "フォームに不正なデータが含まれています。必須フィールドすべてが記入され、データが正しいことを確認してください。", - "core.errorinvalidresponse": "不正な返信を受信しました。エラーが連続する場合、あなたのMoodleサイト管理者に連絡をとってください。", - "core.errorloadingcontent": "コンテンツのロード中にエラーが発生しました。", - "core.erroropenfilenoapp": "ファイルを開く際にエラーが発生しました。この種のファイルを開くアプリが見つかりません。", - "core.erroropenfilenoextension": "ファイルを開く際にエラーが発生しました。ファイルに拡張子がありません。", - "core.erroropenpopup": "このアクティビティはポップアップを開こうとしています。本アプリではサポートされていません。", - "core.errorrenamefile": "ファイル名変更でエラーが発生しました。再度実行してください。", - "core.errorsync": "同期中にエラーが発生しました。再度実行してください。", - "core.errorsyncblocked": "実行中のプロセスがあったため、この {{$a}} はすぐに同期できませんでした。再度実行してください。問題が継続する場合、アプリを再起動してください。", - "core.explanationdigitalminor": "この情報はあなたの年齢がデジタル許可年齢以上であることを確認するため必要です。これは個人が利用条件および自分のデータが合法的に保存および処理されることに同意できる年齢です。", - "core.favourites": "星付き", - "core.filename": "ファイル名", - "core.filenameexist": "ファイル名がすでに存在しています:{{$a}}", - "core.filenotfound": "申し訳ございません、ファイルが見つかりませんでした。", - "core.fileuploader.addfiletext": "ファイルを追加する", - "core.fileuploader.confirmuploadfile": "{$a}をアップロードしようとしています。続けますか?", - "core.fileuploader.errorcapturingaudio": "音声キャプチャーのエラー", - "core.fileuploader.errorcapturingvideo": "ビデオキャプチャーのエラー", - "core.fileuploader.errorreadingfile": "ファイル\"{$a}\"の読み取りエラー", - "core.fileuploader.file": "ファイル", - "core.fileuploader.filesofthesetypes": "許可されるファイルタイプ:", - "core.fileuploader.fileuploaded": "アップロードしたファイル", - "core.fileuploader.invalidfiletype": "{{$a}} ファイルタイプを受け付けることはできません。", - "core.fileuploader.more": "詳細", - "core.fileuploader.readingfile": "ファイル読み取り", - "core.fileuploader.uploading": "アップロード中", - "core.fileuploader.video": "ビデオ", - "core.filter": "フィルタ", - "core.folder": "フォルダ", - "core.forcepasswordchangenotice": "続けるにはパスワードを変更してください。", - "core.fulllistofcourses": "すべてのコース", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "平均", - "core.grades.badgrade": "提供された評定は有効ではありません。", - "core.grades.contributiontocoursetotal": "コース合計への寄与", - "core.grades.feedback": "フィードバック", - "core.grades.grade": "評定", - "core.grades.gradeitem": "評定項目", - "core.grades.grades": "評定", - "core.grades.lettergrade": "評定文字", - "core.grades.nogradesreturned": "評点がありません。", - "core.grades.nooutcome": "アウトカムなし", - "core.grades.percentage": "パーセンテージ", - "core.grades.range": "範囲", - "core.grades.rank": "ランク", - "core.grades.weight": "加重", - "core.group": "グループ", - "core.groupsseparate": "分離グループ", - "core.groupsvisible": "可視グループ", - "core.h5p.additionallicenseinfo": "ライセンスに関する追加情報", - "core.h5p.author": "作成者", - "core.h5p.authorcomments": "作成者コメント", - "core.h5p.authorcommentsdescription": "コンテンツのエディタのコメントです (このテキストは著作権情報の一部としては公開されません)。", - "core.h5p.authorname": "作成者名", - "core.h5p.authorrole": "作成者ロール", - "core.h5p.by": "by", - "core.h5p.cancellabel": "キャンセル", - "core.h5p.ccattribution": "表示 (CC BY)", - "core.h5p.ccattributionnc": "表示-非営利 (CC BY-NC)", - "core.h5p.ccattributionncnd": "表示-非営利-改変禁止 (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "表示-非営利-継承 (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "表示-改変禁止 (CC BY-ND)", - "core.h5p.ccattributionsa": "表示-継承 (CC BY-SA)", - "core.h5p.ccpdd": "著作権なし (CC0)", - "core.h5p.changedby": "変更", - "core.h5p.changedescription": "変更の説明", - "core.h5p.changelog": "変更ログ", - "core.h5p.changeplaceholder": "写真トリミング、テキスト変更等です。", - "core.h5p.close": "閉じる", - "core.h5p.confirmdialogbody": "あなたが続けたい場合、確認してください。この操作は元に戻せません。", - "core.h5p.confirmdialogheader": "操作確認", - "core.h5p.confirmlabel": "確認", - "core.h5p.connectionLost": "接続が切断されました。結果は保存された後、接続復帰時に送信されます。", - "core.h5p.connectionReestablished": "接続が再確立されました。", - "core.h5p.contentCopied": "コンテンツがクリップボードにコピーされます。", - "core.h5p.contentchanged": "あなたが前回使用して以来、このコンテンツは変更されています。", - "core.h5p.contenttype": "コンテンツタイプ", - "core.h5p.copyright": "使用権", - "core.h5p.copyrightinfo": "著作権情報", - "core.h5p.copyrightstring": "著作権", - "core.h5p.copyrighttitle": "このコンテンツの著作権情報を表示します。", - "core.h5p.creativecommons": "クリエイティブ・コモンズ", - "core.h5p.date": "日付", - "core.h5p.disablefullscreen": "フルスクリーンを無効にする", - "core.h5p.download": "ダウンロード", - "core.h5p.downloadtitle": "このコンテンツをH5Pファイルとしてダウンロードします。", - "core.h5p.editor": "エディタ", - "core.h5p.embed": "埋め込み", - "core.h5p.embedtitle": "このコンテンツの埋め込みコードを表示します。", - "core.h5p.fullscreen": "フルスクリーン", - "core.h5p.gpl": "一般公衆利用許諾書 v3", - "core.h5p.h5ptitle": "さらにコンテンツをチェックするにはH5P.orgにアクセスしてください。", - "core.h5p.hideadvanced": "拡張要素を隠す", - "core.h5p.license": "ライセンス", - "core.h5p.licenseCC010": "CC0 1.0 全世界 (CC0 1.0) パブリック・ドメイン提供", - "core.h5p.licenseCC010U": "CC0 1.0 全世界", - "core.h5p.licenseCC10": "1.0 一般", - "core.h5p.licenseCC20": "2.0 一般", - "core.h5p.licenseCC25": "2.5 一般", - "core.h5p.licenseCC30": "3.0 非移植", - "core.h5p.licenseCC40": "4.0 国際", - "core.h5p.licenseGPL": "一般公衆利用許諾書", - "core.h5p.licenseV1": "バージョン 1", - "core.h5p.licenseV2": "バージョン 2", - "core.h5p.licenseV3": "バージョン 3", - "core.h5p.licensee": "ライセンス", - "core.h5p.licenseextras": "追加ライセンス", - "core.h5p.licenseversion": "ライセンスバージョン", - "core.h5p.nocopyright": "このコンテンツに利用できる著作権情報はありません。", - "core.h5p.offlineDialogBody": "私たちはこのタスクのあなたの完了に関する情報を送信できませんでした。あなたのインターネット接続を確認してください。", - "core.h5p.offlineDialogHeader": "あなたのサーバへの接続が切断されました。", - "core.h5p.offlineDialogRetryButtonLabel": "リトライする", - "core.h5p.offlineDialogRetryMessage": "リトライ :num....", - "core.h5p.offlineSuccessfulSubmit": "結果を正常に送信しました。", - "core.h5p.originator": "オーガナイザ", - "core.h5p.pd": "パブリック・ドメイン", - "core.h5p.pddl": "パブリック・ドメインへの供与と認証", - "core.h5p.pdm": "パブリック・ドメイン・マーク (PDM)", - "core.h5p.resizescript": "埋め込みコンテンツを動的にサイズ変更したい場合、あなたのウェブサイトにこのスクリプトを組み込んでください:", - "core.h5p.resubmitScores": "保存された結果の送信を試みます。", - "core.h5p.reuse": "再利用", - "core.h5p.reuseContent": "コンテンツを再利用する", - "core.h5p.reuseDescription": "このコンテンツを再利用します。", - "core.h5p.showadvanced": "拡張要素を表示する", - "core.h5p.showless": "表示を減らす", - "core.h5p.showmore": "表示を増やす", - "core.h5p.size": "サイズ", - "core.h5p.source": "ソース", - "core.h5p.startingover": "あなたは再開します。", - "core.h5p.sublevel": "サブレベル", - "core.h5p.thumbnail": "サムネイル", - "core.h5p.title": "タイトル", - "core.h5p.undisclosed": "未公開", - "core.h5p.year": "年", - "core.h5p.years": "年", - "core.h5p.yearsfrom": "年 (from)", - "core.h5p.yearsto": "年 (to)", - "core.hasdatatosync": "この {{$a}} には同期すべきオフラインデータがあります。", - "core.help": "ヘルプ", - "core.hide": "非表示", - "core.hour": "時間", - "core.hours": "時間", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "画像", - "core.imageviewer": "画像ビューア", - "core.info": "情報", - "core.invalidformdata": "フォームデータが正しくありません。", - "core.labelsep": ":", - "core.lastaccess": "最終アクセス", - "core.lastmodified": "最終更新日時", - "core.lastsync": "最後の同期", - "core.layoutgrid": "グリッド", - "core.list": "リスト", - "core.listsep": ",", - "core.loading": "読み込み", - "core.loadmore": "続きを読み込む", - "core.location": "ロケーション", - "core.login.auth_email": "Eメールによる自己登録", - "core.login.authenticating": "認証中", - "core.login.cancel": "キャンセル", - "core.login.changepassword": "パスワードを変更する", - "core.login.createaccount": "私の新しいアカウントを作成する", - "core.login.createuserandpass": "あなたのユーザ名 およびパスワードを入力してください。", - "core.login.emailconfirmsent": "

                あなたの {{$a}} のメールアドレス宛にメールが送信されました。

                \n

                メールには登録を確認するための簡単な説明が記載されています。

                \n

                分からない場合、サイト管理者にご連絡ください。

                ", - "core.login.emailconfirmsentsuccess": "確認メールが正常に送信されました。", - "core.login.firsttime": "はじめての方ですか?", - "core.login.forcepasswordchangenotice": "続けるにはパスワードを変更してください。", - "core.login.forgotten": "あなたのユーザ名またはパスワードを忘れましたか?", - "core.login.help": "ヘルプ", - "core.login.helpmelogin": "

                世界中に無数のMoodleサイトが存在しますが、本アプリはモバイルアプリからのアクセスを有効にした特別なMoodleサイトでなければ接続できません。

                あなたのMoodleサイトにアプリから接続できず、それをできるようにしてもらいたいときには、Moodleサイトの管理ユーザにhttp://docs.moodle.org/en/Mobile_appを読むよう依頼してみてください。

                Moodleでもサイトでアプリをテストするには、サイトURLの欄にteacherあるいはstudentを入力し、追加ボタンを押してください。

                ", - "core.login.instructions": "説明", - "core.login.invalidaccount": "あなたがログインするのに必要な情報を再確認し、サイト管理者にサイトの設定と合致しているか確認するよう依頼してください。", - "core.login.invaliddate": "日付が正しくありません", - "core.login.invalidemail": "無効なメールアドレスです。", - "core.login.invalidmoodleversion": "

                無効なMoodleサイトバージョンです。MoodleアプリはMoodleシステム {{$a}} 以降のみサポートします。

                \n

                あなたのサイト管理者に連絡してMoodleシステムの更新を依頼できます。

                \n

                「サイト管理者」はあなたの学校/大学/法人または教育機関でMoodleを管理する人です。サイト管理者への連絡方法が分からない場合、あなたの教師/指導者にご連絡ください。

                ", - "core.login.invalidsite": "サイトURLが正しくありません。", - "core.login.invalidurl": "無効なURLが指定されました。", - "core.login.login": "ログイン", - "core.login.loginbutton": "ログイン", - "core.login.logininsiterequired": "ブラウザウインドウからサイトにログインする必要があります。", - "core.login.loginsteps": "このサイトにフルアクセスするため、あなたは最初にアカウントを作成する必要があります。", - "core.login.missingemail": "メールアドレスが入力されていません。", - "core.login.missingfirstname": "名が入力されていません。", - "core.login.missinglastname": "姓が入力されていません。", - "core.login.mobileservicesnotenabled": "あなたのサイトではモバイルサービスが有効になっていません。モバイルアクセスが必要と思うなら、あなたのMoodleサイト管理者にその相談をしてください。", - "core.login.mustconfirm": "あなたのアカウントを確認してください。", - "core.login.newaccount": "新しいアカウント", - "core.login.password": "パスワード", - "core.login.passwordforgotten": "パスワード喪失", - "core.login.passwordforgotteninstructions2": "パスワードをリセットするにはあなたのユーザ名またはメールアドレスを以下に入力してください。データベース内であなたを探すことができた場合、再度アクセスするための手順を記載したメールをあなたのメールアドレス宛に送信します。", - "core.login.passwordrequired": "パスワードがありません", - "core.login.policyaccept": "私は内容を理解して同意します。", - "core.login.policyagree": "このサイトを継続して利用するにはあなたは使用許諾に同意する必要があります。同意しますか?", - "core.login.policyagreement": "サイト使用許諾", - "core.login.policyagreementclick": "サイト使用許諾にリンクする", - "core.login.potentialidps": "あなたのアカウントを使用してログインします:", - "core.login.profileinvaliddata": "値が有効ではありません。", - "core.login.resendemail": "メールを再送する", - "core.login.security_question": "セキュリティ質問", - "core.login.selectacountry": "国を選択する", - "core.login.siteinmaintenance": "このサイトはメンテナンス中です", - "core.login.siteurl": "サイトURL", - "core.login.siteurlrequired": "サイトURLがありません。書き方は、例えば次の通りです:http://www.yourmoodlesite.abc あるいは https://www.yourmoodlesite.efg", - "core.login.startsignup": "新しいアカウントを作成する", - "core.login.supplyinfo": "詳細情報", - "core.login.username": "ユーザ名", - "core.login.usernameoremail": "ユーザ名またはメールアドレスを入力してください。", - "core.login.usernamerequired": "ユーザー名がありません", - "core.login.usernotaddederror": "ユーザは追加されませんでした - エラー。", - "core.login.webservicesnotenabled": "あなたのサイトでWebサービスが有効になっていません。モバイルアクセスが必要と思うなら、あなたのMoodleサイト管理者に相談してください。", - "core.lostconnection": "あなたのトークンが無効になったため、再接続に必要な情報がサーバにはありません。", - "core.mainmenu.help": "ヘルプ", - "core.mainmenu.logout": "ログアウト", - "core.mainmenu.website": "ウェブサイト", - "core.maxsizeandattachments": "最大ファイルサイズ: {{$a.size}} / 最大ファイル数: {{$a.attachments}}", - "core.min": "分", - "core.mins": "分", - "core.misc": "その他", - "core.mod_assign": "課題", - "core.mod_assignment": "課題2.2 (無効)", - "core.mod_book": "ブック", - "core.mod_chat": "チャット", - "core.mod_choice": "投票", - "core.mod_data": "データベース", - "core.mod_database": "データベース", - "core.mod_external-tool": "外部ツール", - "core.mod_feedback": "フィードバック", - "core.mod_file": "ファイル", - "core.mod_folder": "フォルダ", - "core.mod_forum": "フォーラム", - "core.mod_glossary": "用語集", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMSコンテンツパッケージ", - "core.mod_imscp": "IMSコンテンツパッケージ", - "core.mod_label": "ラベル", - "core.mod_lesson": "レッスン", - "core.mod_lti": "外部ツール", - "core.mod_page": "ページ", - "core.mod_quiz": "小テスト", - "core.mod_resource": "ファイル", - "core.mod_scorm": "SCORMパッケージ", - "core.mod_survey": "調査", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "ワークショップ", - "core.moduleintro": "説明", - "core.more": "詳細", - "core.mygroups": "マイグループ", - "core.name": "名称", - "core.networkerrormsg": "ネットワークが無効もしくは機能していません", - "core.never": "なし", - "core.next": "次へ", - "core.no": "No", - "core.nocomments": "コメントなし", - "core.nograde": "評点なし", - "core.none": "なし", - "core.nopasswordchangeforced": "パスワードを変更するまで続きを実行できません。", - "core.nopermissions": "申し訳ございません、現在、あなたは 「 {{$a}} 」を実行するためのパーミッションがありません。", - "core.noresults": "該当データはありません。", - "core.noselection": "選択なし", - "core.notapplicable": "なし", - "core.notenrolledprofile": "コースに登録されていないため、このユーザのプロファイルを閲覧できません。", - "core.notice": "警告", - "core.notingroup": "申し訳ございません、このページを閲覧するためにはあなたはグループに属している必要があります。", - "core.notsent": "未送信", - "core.now": "現在", - "core.numwords": "{{$a}} 語", - "core.offline": "オフライン", - "core.ok": "OK", - "core.online": "オンライン", - "core.openfullimage": "クリックしてフルサイズの画像を表示", - "core.openinbrowser": "ブラウザで開く", - "core.othergroups": "他のグループ", - "core.pagea": "ページ {{$a}}", - "core.paymentinstant": "下のボタンをお使いください。支払いおよび登録がすぐに完了します!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "電話", - "core.pictureof": "画像 {{$a}}", - "core.previous": "前へ", - "core.proceed": "進む", - "core.pulltorefresh": "引いて更新", - "core.question.answer": "答え", - "core.question.answersaved": "解答保存済み", - "core.question.certainty": "確信度", - "core.question.complete": "完了", - "core.question.correct": "正解", - "core.question.feedback": "フィードバック", - "core.question.howtodraganddrop": "選んだものをタッチして、あてはまる所にタッチして入れましょう。", - "core.question.incorrect": "不正解", - "core.question.information": "情報", - "core.question.invalidanswer": "不完全な答え", - "core.question.notanswered": "未解答", - "core.question.notyetanswered": "未解答", - "core.question.partiallycorrect": "部分的に正解", - "core.question.questionmessage": "問題 {{$a}}: {{$b}}", - "core.question.questionno": "問題 {{$a}}", - "core.question.requiresgrading": "要評定", - "core.quotausage": "現在、あなたは {{$a.total}} 制限のうち {{$a.used}} を使用しています。", - "core.rating.aggregateavg": "評点平均", - "core.rating.aggregatecount": "評価数", - "core.rating.aggregatemax": "最大評点", - "core.rating.aggregatemin": "最小評点", - "core.rating.aggregatesum": "評点合計", - "core.rating.noratings": "送信された評価はありません。", - "core.rating.rating": "評価", - "core.rating.ratings": "評価", - "core.redirectingtosite": "サイトにリダイレクトされます。", - "core.refresh": "リフレッシュ", - "core.remove": "削除", - "core.required": "必須", - "core.requireduserdatamissing": "このユーザは必須のプロフィールデータが欠けています。Moodleでデータを補い、再度開いてください。
                {{$a}}", - "core.resourcedisplayopen": "オープン", - "core.resources": "リソース", - "core.restore": "リストア", - "core.restricted": "利用制限", - "core.retry": "再実行", - "core.save": "保存", - "core.savechanges": "変更を保存する", - "core.search": "検索", - "core.searching": "検索中", - "core.searchresults": "検索結果", - "core.sec": "秒", - "core.secs": "秒", - "core.seemoredetail": "さらに詳細を見るためにはここをクリックしてください。", - "core.selectacategory": "カテゴリを選択してください。", - "core.selectacourse": "コースを選択する", - "core.selectagroup": "グループを選択する", - "core.send": "送信", - "core.sending": "送信中", - "core.serverconnection": "サーバへの接続中にエラーが発生しました。", - "core.settings.about": "本アプリについて", - "core.settings.currentlanguage": "現在の言語", - "core.settings.debugdisplay": "デバックメッセージを表示する", - "core.settings.deletesitefiles": "本当にこのサイトからダウンロードしたファイルを消去しますか?", - "core.settings.disableall": "通知を無効にする", - "core.settings.disabled": "無効", - "core.settings.estimatedfreespace": "概算の空き容量", - "core.settings.general": "一般", - "core.settings.language": "言語設定", - "core.settings.license": "ライセンス", - "core.settings.locked": "ロック", - "core.settings.loggedin": "オンライン", - "core.settings.loggedoff": "オフライン", - "core.settings.preferences": "プリファレンス", - "core.settings.settings": "設定", - "core.settings.sites": "サイト", - "core.settings.spaceusage": "保存領域使用量", - "core.settings.synchronization": "同期", - "core.settings.total": "合計", - "core.show": "表示", - "core.showless": "表示を減らす ...", - "core.showmore": "さらに表示する ...", - "core.site": "サイト", - "core.sitehome.sitehome": "サイトホーム", - "core.sitehome.sitenews": "サイトアナウンスメント", - "core.sitemaintenance": "このサイトはメンテナンス中です。現在ご利用いただけません。", - "core.sizeb": "バイト", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "スキップ", - "core.sorry": "すみません...", - "core.sort": "並べ替え", - "core.sortby": "並べ替え", - "core.strftimedate": "%Y年 %m月 %d日", - "core.strftimedatefullshort": "%y/%m/%d", - "core.strftimedateshort": "%m/%d", - "core.strftimedatetime": "%Y年 %m月 %d日 %H:%M", - "core.strftimedatetimeshort": "%y年 %m月 %d日 %H:%M", - "core.strftimedaydate": "%Y年 %m月 %d日(%A)", - "core.strftimedaydatetime": "%Y年 %m月 %d日(%A) %H:%M", - "core.strftimedayshort": "%Y年 %m月 %d日", - "core.strftimedaytime": "(%a) %H:%M", - "core.strftimemonthyear": "%Y年 %m月", - "core.strftimerecent": "%m月 %d日 %H:%M", - "core.strftimerecentfull": "%Y年 %m月 %d日(%a) %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "送信", - "core.success": "成功", - "core.tablet": "タブレット", - "core.tag.defautltagcoll": "デフォルトコレクション", - "core.tag.inalltagcoll": "どこでも", - "core.tag.itemstaggedwith": "「 {{$a.tag}} 」がタグ付けされた {{$a.tagarea}}", - "core.tag.noresultsfor": "「 {{$a}} 」の検索結果なし", - "core.tag.notagsfound": "「 {{$a}} 」に合致するタグは見つかりませんでした。", - "core.tag.searchtags": "タグを検索する", - "core.tag.showingfirsttags": "{{$a}} 件の最も人気のあるタグを表示中", - "core.tag.tag": "タグ", - "core.tag.tagarea_course": "コース", - "core.tag.tagarea_course_modules": "活動およびリソース", - "core.tag.tagarea_post": "ブログ記事", - "core.tag.tagarea_user": "ユーザの興味のあること", - "core.tag.tags": "タグ", - "core.teachers": "教師", - "core.thereisdatatosync": "同期が必要なオフライン {{$a}} があります。", - "core.thisdirection": "ltr", - "core.time": "時間", - "core.timesup": "時間切れです!", - "core.today": "本日", - "core.tryagain": "再実行", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "おおっと!", - "core.unexpectederror": "不明なエラー。アプリを閉じて再起動してみてください。", - "core.unicodenotsupported": "本サイトでは一部の絵文字がサポートされていません。それらは送信されたメッセージから削除されます。", - "core.unicodenotsupportedcleanerror": "Unicode文字をクリアする際に空のテキストがありました。", - "core.unknown": "不明な", - "core.unlimited": "無制限", - "core.unzipping": "未展開の", - "core.upgraderunning": "サイトはアップグレード中です。後ほどお試しください。", - "core.user": "ユーザ", - "core.user.address": "住所", - "core.user.city": "都道府県", - "core.user.contact": "コンタクト", - "core.user.country": "国", - "core.user.description": "説明", - "core.user.details": "詳細", - "core.user.editingteacher": "教師", - "core.user.email": "メールアドレス", - "core.user.emailagain": "メールアドレス (もう一度)", - "core.user.firstname": "名", - "core.user.interests": "興味のあること", - "core.user.lastname": "姓", - "core.user.manager": "マネージャ", - "core.user.newpicture": "新しい画像", - "core.user.noparticipants": "このコースには参加者が登録されていません。", - "core.user.participants": "参加者", - "core.user.phone1": "電話", - "core.user.phone2": "携帯電話", - "core.user.roles": "ロール", - "core.user.student": "学生", - "core.user.teacher": "編集権限のない教師", - "core.user.webpage": "ウェブページ", - "core.userdeleted": "このユーザのアカウントは削除されました。", - "core.userdetails": "ユーザ詳細", - "core.usernotfullysetup": "ユーザは完全には設定されていません。", - "core.users": "ユーザ", - "core.view": "表示", - "core.viewprofile": "プロファイルを表示する", - "core.whatisyourage": "あなたの年齢は?", - "core.wheredoyoulive": "あなたが住んでいる国は?", - "core.whoops": "しまった!", - "core.whyisthisrequired": "なぜこれが必要ですか?", - "core.year": "年", - "core.years": "年", - "core.yes": "Yes" -} \ No newline at end of file diff --git a/src/assets/lang/km.json b/src/assets/lang/km.json deleted file mode 100644 index 0b3da3aec..000000000 --- a/src/assets/lang/km.json +++ /dev/null @@ -1,1810 +0,0 @@ -{ - "addon.badges.alignment": "តម្រឹម", - "addon.badges.badgedetails": "ព័ត៌មានអំពីផ្លាកសញ្ញា", - "addon.badges.badges": "ផ្លាកសញ្ញា", - "addon.badges.contact": "ព័ត៌មានសម្រាប់ទាក់ទង", - "addon.badges.dateawarded": "ប្រគល់នៅថ្ងៃ", - "addon.badges.expired": "ផុតកំណត់", - "addon.badges.expirydate": "កាលបរិច្ឆេទផុតកំណត់", - "addon.badges.issuancedetails": "ការផុតកំណត់របស់ផ្លាកសញ្ញា", - "addon.badges.issuerdetails": "ព័ត៌មានអំពីអ្នកចេញផ្លាកសញ្ញា", - "addon.badges.issuername": "ឈ្មោះអ្នកចេញផ្លាកសញ្ញា", - "addon.badges.nobadges": "មិនមានផ្លាកសញ្ញាទេ", - "addon.badges.recipientdetails": "ព័ត៌មានអំពីអ្នកទទួល", - "addon.block_activitymodules.pluginname": "សកម្មភាព", - "addon.block_activityresults.pluginname": "លទ្ធផលពីសកម្មភាព", - "addon.block_badges.pluginname": "ផ្លាកសញ្ញាថ្មីៗ", - "addon.block_blogmenu.pluginname": "ម៉ឺនុយកំណត់ហេតុបណ្ដាញ", - "addon.block_blogrecent.pluginname": "ធាតុកំណត់ហេតុបណ្ដាញថ្មីៗ", - "addon.block_blogtags.pluginname": "ស្លាកកំណត់ហេតុបណ្ដាញ", - "addon.block_calendarmonth.pluginname": "ប្រតិទិន", - "addon.block_calendarupcoming.pluginname": "ព្រឹត្តិការណ៍ជិតមកដល់", - "addon.block_comments.pluginname": "មតិយោបល់", - "addon.block_completionstatus.pluginname": "ស្ថានភាពនៃការបញ្ចប់វគ្គសិក្សា", - "addon.block_glossaryrandom.pluginname": "ធាតុសទ្ទានុក្រមចៃដន្យ", - "addon.block_learningplans.pluginname": "ផែនការសិក្សា", - "addon.block_myoverview.all": "ទាំងអស់", - "addon.block_myoverview.allincludinghidden": "ទាំងអស់", - "addon.block_myoverview.favourites": "បានដាក់ផ្កាយ", - "addon.block_myoverview.future": "អនាគត", - "addon.block_myoverview.hiddencourses": "បានលាក់", - "addon.block_myoverview.inprogress": "កំពុងដំណើរការ", - "addon.block_myoverview.lastaccessed": "បានចូលដំណើរការចុងក្រោយ", - "addon.block_myoverview.morecourses": "វគ្គសិក្សាច្រើនទៀត", - "addon.block_myoverview.nocourses": "មិនមានវគ្គសិក្សាទេ", - "addon.block_myoverview.past": "អតីតកាល", - "addon.block_myoverview.pluginname": "ទិដ្ឋភាពទូទៅនៃវគ្គសិក្សា", - "addon.block_myoverview.title": "ឈ្មោះវគ្គសិក្សា", - "addon.block_newsitems.pluginname": "ព័ត៌មានថ្មីៗ", - "addon.block_onlineusers.pluginname": "អ្នកប្រើលើបណ្ដាញ", - "addon.block_privatefiles.pluginname": "ឯកសារផ្ទាល់ខ្លួន", - "addon.block_recentactivity.pluginname": "សកម្មភាពថ្មីៗ", - "addon.block_recentlyaccessedcourses.nocourses": "មិនមានវគ្គសិក្សាថ្មីៗទេ", - "addon.block_recentlyaccessedcourses.pluginname": "វគ្គសិក្សាដែលទើបតែបានចូលមើលថ្មីៗ", - "addon.block_recentlyaccesseditems.noitems": "មិនមានធាតុថ្មីៗទេ", - "addon.block_recentlyaccesseditems.pluginname": "ធាតុដែលទើបតែបានចូលមើលថ្មីៗ", - "addon.block_rssclient.pluginname": "កម្មវិធី RSS", - "addon.block_selfcompletion.pluginname": "ការបញ្ចប់ដោយខ្លួនឯង", - "addon.block_sitemainmenu.pluginname": "ម៉ឺនុយមេ", - "addon.block_tags.pluginname": "ស្លាក", - "addon.block_timeline.duedate": "កាលបរិច្ឆេទកំណត់", - "addon.block_timeline.next30days": "30 ថ្ងៃបន្ទាប់", - "addon.block_timeline.next3months": "3 ខែបន្ទាប់", - "addon.block_timeline.next6months": "6 ខែបន្ទាប់", - "addon.block_timeline.next7days": "7 ថ្ងៃបន្ទាប់", - "addon.block_timeline.nocoursesinprogress": "គ្មានវគ្គសិក្សាដែលកំពុងដំណើរការ", - "addon.block_timeline.noevents": "មិនមានសកម្មភាពជិតមកដល់ពេលកំណត់ទេ", - "addon.block_timeline.overdue": "ហួសកំណត់", - "addon.block_timeline.pluginname": "បន្ទាត់កាលសម័យ", - "addon.block_timeline.sortbycourses": "តម្រៀបតាមវគ្គសិក្សា", - "addon.block_timeline.sortbydates": "តម្រៀបតាមកាលបរិច្ឆេទ", - "addon.blog.blog": "កំណត់ហេតុបណ្ដាញ", - "addon.blog.blogentries": "កំណត់ហេតុបណ្តាញ", - "addon.blog.noentriesyet": "គ្មានធាតុដែលមើលឃើញនៅទីនេះឡើយ", - "addon.blog.publishtonoone": "ខ្លួនអ្នកផ្ទាល់ (ព្រាង)", - "addon.blog.publishtosite": "អ្នកនៅលើតំបន់បណ្ដាញនេះ", - "addon.blog.publishtoworld": "អ្នកនៅក្នុងពិភពលោក", - "addon.calendar.allday": "រាល់ថ្ងៃ", - "addon.calendar.calendar": "ប្រតិទិន", - "addon.calendar.calendarevents": "ព្រឹត្តិការណ៍ក្នុងប្រតិទិន", - "addon.calendar.categoryevents": "ព្រឹត្តិការណ៍ក្នុងប្រភេទវគ្គសិក្សា", - "addon.calendar.confirmeventdelete": "តើអ្នកពិតជាចង់លុបព្រឹត្តិការណ៍ \"{{$a}}\" ឬទេ?", - "addon.calendar.confirmeventseriesdelete": "ព្រឹត្តិការណ៍ \"{{$a.name}}\" ជាផ្នែកមួយនៃស៊េរីព្រឹត្តិការណ៍។ តើអ្នកចង់លុបតែព្រឹត្តិការណ៍នេះ ឬព្រឹត្តិការណ៍ទាំង {{$a.count}} នៅក្នុងស៊េរីនេះ?", - "addon.calendar.courseevents": "ព្រឹត្តិការណ៍វគ្គសិក្សា", - "addon.calendar.daynext": "ថ្ងៃបន្ទាប់", - "addon.calendar.dayprev": "ថ្ងៃមុន", - "addon.calendar.defaultnotificationtime": "ពេលវេលាជូនដំណឹងតាមលំនាំដើម", - "addon.calendar.deleteallevents": "លុបព្រឹត្តិការណ៍ទាំងអស់", - "addon.calendar.deleteevent": "លុបព្រឹត្តការណ៍", - "addon.calendar.deleteoneevent": "លុបព្រឹត្តិការណ៍នេះ", - "addon.calendar.durationminutes": "ថិរវេលាគិតជានាទី", - "addon.calendar.durationnone": "ដោយគ្មានថិរវេលា", - "addon.calendar.durationuntil": "រហូតដល់", - "addon.calendar.editevent": "កែសម្រួលព្រឹត្តិការណ៍", - "addon.calendar.errorloadevent": "មានកំហុសក្នុងការដំណើរព្រឹត្តិការណ៍", - "addon.calendar.errorloadevents": "មានកំហុសក្នុងការដំណើរព្រឹត្តិការណ៍", - "addon.calendar.eventcalendareventdeleted": "ព្រឹត្តិការណ៍ក្នុងប្រតិទិនត្រូវបានលុបចេញ", - "addon.calendar.eventduration": "ថិរវេលា", - "addon.calendar.eventendtime": "ពេលវេលាបញ្ចប់", - "addon.calendar.eventkind": "ប្រភេទព្រឹត្តិការណ៍", - "addon.calendar.eventname": "ឈ្មោះព្រឹត្តិការណ៍", - "addon.calendar.eventstarttime": "ពេលវេលាចាប់ផ្តើម", - "addon.calendar.eventtype": "ប្រភេទព្រឹត្តិការណ៍", - "addon.calendar.fri": "សុ", - "addon.calendar.friday": "សុក្រ", - "addon.calendar.gotoactivity": "ទៅកាន់សកម្មភាព", - "addon.calendar.groupevents": "ព្រឹត្តិការណ៍ក្រុម", - "addon.calendar.invalidtimedurationminutes": "ថេរវេលាគិតជានាទីដែលអ្នកបានបញ្ចូលមិនត្រឹមត្រូវទេ។ សូមបញ្ចូលថេរវេលាគិតជានាទីដែលធំជាង 0 ឬជ្រើស “គ្មានថេរវេលា”។", - "addon.calendar.invalidtimedurationuntil": "កាលបរិច្ឆេទនិងពេលវេលាដែលអ្នកបានជ្រើសសម្រាប់ \"ថេរវេលាទៅដល់\" គឺមុនពេលការចាប់ផ្ដើមនៃព្រឹត្តិការណ៍។ សូមកែប្រែព័ត៌មាននេះមុនពេលបន្តទៅមុខទៀត។", - "addon.calendar.mon": "ច", - "addon.calendar.monday": "ច័ន្ទ", - "addon.calendar.monthlyview": "ទិដ្ឋភាពប្រចាំខែ", - "addon.calendar.newevent": "ព្រឹត្តិការណ៍ថ្មី", - "addon.calendar.noevents": "មិនមានព្រឹត្តិការណ៍ទេ", - "addon.calendar.repeatedevents": "ព្រឹត្តិការណ៍កើតឡើងដដែលៗ", - "addon.calendar.repeateditall": "អនុវត្តការផ្លាស់ប្ដូរចំពោះព្រឹត្តិការណ៍ {{$a}} ទាំងអស់ក្នុងស៊េរីធ្វើឡើងវិញនេះ", - "addon.calendar.repeateditthis": "អនុវត្តការផ្លាស់ប្ដូរចំពោះតែព្រឹត្តិការណ៍នេះប៉ុណ្ណោះ", - "addon.calendar.repeatevent": "ព្រឹត្តិការណ៍កើតឡើងដដែលៗ", - "addon.calendar.repeatweeksl": "ធ្វើឡើងវិញប្រចាំសប្ដាហ៍ ដោយបង្កើតទាំងអស់", - "addon.calendar.sat": "ស", - "addon.calendar.saturday": "សៅរ៏", - "addon.calendar.siteevents": "ព្រឹត្តិការណ៍ក្នុងតំបន់បណ្តាញ", - "addon.calendar.sun": "អា", - "addon.calendar.sunday": "អាទិត្យ", - "addon.calendar.thu": "ព្រ", - "addon.calendar.thursday": "ព្រហស្បត្ត៏", - "addon.calendar.today": "ថ្ងៃនេះ", - "addon.calendar.tomorrow": "ថ្ងៃស្អែក", - "addon.calendar.tue": "អ", - "addon.calendar.tuesday": "អង្គារ", - "addon.calendar.typecategory": "ព្រឹត្តិការណ៍សម្រាប់ប្រភេទវគ្គសិក្សា", - "addon.calendar.typecourse": "ព្រឹត្តិការណ៍វគ្គសិក្សា", - "addon.calendar.typedue": "ព្រឹត្តិការណ៍ពីការដល់កំណត់", - "addon.calendar.typegradingdue": "ព្រឹត្តិការណ៍អំពីការដល់កំណត់ក្នុងការដាក់ពិន្ទុ", - "addon.calendar.typegroup": "ព្រឹត្តិការណ៍ក្រុម", - "addon.calendar.typesite": "ព្រឹត្តិការណ៍តំបន់បណ្ដាញ", - "addon.calendar.typeuser": "ព្រឹត្តិការណ៍អ្នកប្រើ", - "addon.calendar.upcomingevents": "ព្រឹត្តិការណ៍ជិតមកដល់", - "addon.calendar.userevents": "ព្រឹត្តិការណ៍អ្នកប្រើ", - "addon.calendar.wed": "ព", - "addon.calendar.wednesday": "ពុធ", - "addon.calendar.when": "នៅពេល", - "addon.calendar.yesterday": "ម្សិលម៉ិញ", - "addon.competency.competencies": "សមត្ថភាព", - "addon.competency.competenciesmostoftennotproficientincourse": "សមត្ថភាពដែលមិនស្ទាត់ជំនាញជារឿយៗបំផុតក្នុងវគ្គសិក្សា", - "addon.competency.coursecompetencies": "សមត្ថភាពក្នុងវគ្គសិក្សា", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "ការវាយតម្លៃសមត្ថភាពក្នុងវគ្គសិក្សានេះត្រូវបានធ្វើបច្ចុប្បន្នភាពភ្លាមៗនៅក្នុងផែនការសិក្សា។", - "addon.competency.errornocompetenciesfound": "គ្មានសមត្ថភាពត្រូវបានរកឃើញទេ", - "addon.competency.evidence_competencyrule": "លក្ខខណ្ឌសម្រាប់សមត្ថភាពត្រូវបានបំពេញ។", - "addon.competency.evidence_coursecompleted": "វគ្គសិក្សា '{{$a}}' បានបញ្ចប់។", - "addon.competency.evidence_coursemodulecompleted": "សកម្មភាព '{{$a}}' បានបញ្ចប់។", - "addon.competency.evidence_courserestored": "រង្វាយតម្លៃបានស្តារមកវិញជាមួយនឹងវគ្គសិក្សា '{{$a}}' ។", - "addon.competency.evidence_evidenceofpriorlearninglinked": "ភស្តុតាងនៃការសិក្សាពីមុន '{{$a}}' ត្រូវបានតភ្ជាប់។", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "ភស្តុតាងនៃការសិក្សាពីមុន '{{$a}}' ត្រូវបានផ្តាច់។", - "addon.competency.evidence_manualoverride": "រង្វាយតម្លៃសមត្ថភាពត្រូវបានបង្កើតដោយផ្ទាល់។", - "addon.competency.evidence_manualoverrideincourse": "រង្វាយតម្លៃសមត្ថភាពត្រូវបានបង្កើតដោយផ្ទាល់ក្នុងវគ្គសិក្សា '{{$a}}'។", - "addon.competency.evidence_manualoverrideinplan": "រង្វាយតម្លៃសមត្ថភាពត្រូវបានបង្កើតដោយផ្ទាល់ក្នុងគម្រោងសិក្សា '{{$a}}'។", - "addon.competency.learningplans": "ផែនការសិក្សា", - "addon.competency.nocompetencies": "គ្មានសមត្ថភាព", - "addon.competency.nocompetenciesincourse": "គ្មានសមត្ថភាពត្រូវបានភ្ជាប់ទៅនឹងវគ្គសិក្សានេះទេ។", - "addon.competency.planstatusactive": "សកម្ម", - "addon.competency.planstatuscomplete": "បានបញ្ចប់", - "addon.competency.planstatusdraft": "ព្រៀង", - "addon.competency.planstatusinreview": "កំពុងពិនិត្យ", - "addon.competency.planstatuswaitingforreview": "រងចាំការពិនិត្យ", - "addon.competency.progress": "វឌ្ឍនភាព", - "addon.competency.uponcoursecompletion": "នៅពេលបញ្ចប់ការវគ្គសិក្សា", - "addon.competency.usercompetencystatus_inreview": "កំពុងពិនិត្យ", - "addon.competency.usercompetencystatus_waitingforreview": "រង់ចាំការពិនិត្យ", - "addon.competency.userplans": "គម្រោងសិក្សា", - "addon.coursecompletion.complete": "បញ្ចប់", - "addon.coursecompletion.completecourse": "បញ្ចប់វគ្គសិក្សា", - "addon.coursecompletion.completed": "បានបញ្ចប់", - "addon.coursecompletion.completionmenuitem": "ការបញ្ចប់", - "addon.coursecompletion.couldnotloadreport": "មិនអាចដំណើរការរបាយការណ៍បញ្ចប់វគ្គសិក្សាទេ។ សូមព្យាយាមម្តងទៀតនៅពេលក្រោយ។", - "addon.coursecompletion.coursecompletion": "ការបញ្ចប់វគ្គសិក្សា", - "addon.coursecompletion.criteria": "លក្ខណៈវិនិច្ឆ័យ", - "addon.coursecompletion.criteriagroup": "ក្រុមនៃលក្ខណៈវិនិច្ឆ័យ", - "addon.coursecompletion.criteriarequiredall": "ទាមទារលក្ខណៈវិនិច្ឆ័យទាំងអស់ខាងក្រោម", - "addon.coursecompletion.criteriarequiredany": "ទាមទារលក្ខណៈវិនិច្ឆ័យណាមួយខាងក្រោម", - "addon.coursecompletion.inprogress": "កំពុងដំណើរការ", - "addon.coursecompletion.manualselfcompletion": "ការបញ្ចប់ដោយផ្ទាល់ (ដោយខ្លួនឯង)", - "addon.coursecompletion.nottracked": "នៅពេលនេះអ្នកមិនត្រូវបានតាមដានពីការបញ្ចប់កិច្ចការក្នុងវគ្គសិក្សានេះទេ", - "addon.coursecompletion.notyetstarted": "មិនទាន់ចាប់ផ្តើម", - "addon.coursecompletion.pending": "កំពុងរង់ចាំ", - "addon.coursecompletion.required": "ទាមទារ", - "addon.coursecompletion.requiredcriteria": "លក្ខណៈវិនិច្ឆ័យដែលទាមទារ", - "addon.coursecompletion.requirement": "ការទាមទារ", - "addon.coursecompletion.status": "ស្ថានភាព", - "addon.coursecompletion.viewcoursereport": "មើលរបាយការណ៍វគ្គសិក្សា", - "addon.files.couldnotloadfiles": "មិនអាចដំណើរការបញ្ជីឯកសារទេ។", - "addon.files.emptyfilelist": "មិនមានឯកសារសម្រាប់បង្ហាញទេ។", - "addon.files.erroruploadnotworking": "ជាអកុសល បច្ចុប្បន្នអ្នកមិនអាចផ្ទុកឡើងនូវឯកសារទៅតំបន់បណ្តាញរបស់អ្នកបានទេ។", - "addon.files.files": "ឯកសារ", - "addon.files.privatefiles": "ឯកសារផ្ទាល់ខ្លួន", - "addon.files.sitefiles": "ឯកសារតំបន់បណ្ដាញ", - "addon.messageoutput_airnotifier.processorsettingsdesc": "កំណត់រចនាសម្ព័ន្ធឧបករណ៍", - "addon.messages.acceptandaddcontact": "ព្រមទទួលនិងបន្ថែមទៅកាន់បញ្ជីទំនាក់ទំនង", - "addon.messages.addcontact": "បន្ថែមទំនាក់ទំនង", - "addon.messages.addcontactconfirm": "តើអ្នកប្រាកដជាចង់បន្ថែម {{$a}} ទៅកាន់បញ្ជីទំនាក់ទំនងរបស់អ្នកមែនទេ?", - "addon.messages.addtofavourites": "ដាក់ផ្កាយការសន្ទនា", - "addon.messages.addtoyourcontacts": "បន្ថែមទៅកាន់បញ្ជីទំនាក់ទំនងរបស់អ្នក", - "addon.messages.blocknoncontacts": "ទប់ស្កាត់សារថ្មីទាំងអស់ពីមនុស្សដែលមិនមាននៅក្នុងបញ្ជីទំនាក់ទំនងរបស់ខ្ញុំ", - "addon.messages.blockuser": "ប្លុកអ្នកប្រើ", - "addon.messages.blockuserconfirm": "តើអ្នកពិតជាចង់ប្លុក {{$a}} មែនទេ?", - "addon.messages.contactableprivacy": "ទទួលសារពី ៖", - "addon.messages.contactableprivacy_coursemember": "ទំនាក់ទំនងរបស់ខ្ញុំ និងអ្នកដែលនៅក្នុងវគ្គសិក្សារបស់ខ្ញុំ", - "addon.messages.contactableprivacy_onlycontacts": "តែទំនាក់ទំនងរបស់ខ្ញុំប៉ុណ្ណោះ", - "addon.messages.contactableprivacy_site": "គ្រប់គ្នាលើតំបន់បណ្តាញ", - "addon.messages.contactblocked": "ទំនាក់ទំនងត្រូវបានទប់ស្កាត់", - "addon.messages.contactlistempty": "បញ្ជីទំនាក់ទំនងគឺទទេ", - "addon.messages.contactname": "ឈ្មោះទំនាក់ទំនង", - "addon.messages.contactrequestsent": "សំណើទំនាក់ទំនងត្រូវបានផ្ញើ", - "addon.messages.contacts": "ទំនាក់ទំនង", - "addon.messages.conversationactions": "ម៊ឺនុយសកម្មភាពសម្រាប់ការសន្ទនា", - "addon.messages.decline": "បដិសេធ", - "addon.messages.deleteallconfirm": "តើអ្នកប្រាកដជាចង់លុបការសន្ទនាទាំងស្រុងមែនទេ?", - "addon.messages.deleteallselfconfirm": "តើអ្នកប្រាកដជាចង់លុបការសន្ទនាផ្ទាល់ខ្លួនទាំងមូលមែនទេ?", - "addon.messages.deleteconversation": "លុបការសន្ទនា", - "addon.messages.deleteforeveryone": "លុបសម្រាប់ខ្ញុំ និងសម្រាប់អ្នកផ្សេងទៀត", - "addon.messages.deletemessage": "លុបសារ", - "addon.messages.deletemessageconfirmation": "តើអ្នកប្រាកដថាចង់លុបសារនេះដែរឬទេ? វានឹងត្រូវបានលុបចេញតែពីបញ្ជីសាររបស់អ្នកប៉ុណ្ណោះ ហើយអ្នកប្រើដទៃដែលបានផ្ញើ ឬទទួលសារនេះនឹងនៅតែអាចមើលឃើញវាដដែល។", - "addon.messages.errordeletemessage": "មានកំហុសពេលលុបសារនេះ។", - "addon.messages.errorwhileretrievingcontacts": "មានកំហុសនៅពេលកំពុងទាញយកព័ត៌មានទំនាក់ទំនងពីម៉ាស៊ីនបម្រើ", - "addon.messages.errorwhileretrievingdiscussions": "មានកំហុសនៅពេលកំពុងទាញយកការពិភាក្សាពីម៉ាស៊ីនបម្រើ", - "addon.messages.errorwhileretrievingmessages": "មានកំហុសនៅពេលកំពុងទាញយកសារពីម៉ាស៊ីនបម្រើ", - "addon.messages.errorwhileretrievingusers": "មានកំហុសនៅពេលកំពុងទាញយកអ្នកប្រើពីម៉ាស៊ីនបម្រើ", - "addon.messages.groupconversations": "ក្រុម", - "addon.messages.groupinfo": "ព័ត៌មានក្រុម", - "addon.messages.individualconversations": "ឯកជន", - "addon.messages.info": "ព័ត៌មានអ្នកប្រើប្រាស់", - "addon.messages.isnotinyourcontacts": "{{$a}} មិនស្ថិតក្នុងបញ្ជីទំនាក់ទំនងរបស់អ្នកទេ", - "addon.messages.message": "សារ", - "addon.messages.messagenotsent": "សារនេះមិនត្រូវបានផ្ញើចេញទេ។ សូមព្យាយាមម្ដងទៀតពេលក្រោយ។", - "addon.messages.messagepreferences": "ការកំណត់ផ្ទាល់ខ្លួនសម្រាប់សារ", - "addon.messages.messages": "សារ", - "addon.messages.muteconversation": "បិទការជូនដំណឹង", - "addon.messages.mutedconversation": "ការសន្ទនាដែលបានបិទការជូនដំណឹង", - "addon.messages.newmessage": "សារថ្មី", - "addon.messages.newmessages": "សារថ្មី", - "addon.messages.nocontactrequests": "មិនមានសំណើទំនាក់ទំនងទេ", - "addon.messages.nocontactsgetstarted": "គ្មានទំនាក់ទំនង", - "addon.messages.nofavourites": "មិនមានការសន្ទនាដែលបានដាក់ផ្កាយទេ", - "addon.messages.nogroupconversations": "មិនមានការសន្ទនាក្រុមទេ", - "addon.messages.noindividualconversations": "មិនមានការសន្ទនាឯកជនទេ", - "addon.messages.nomessagesfound": "រកមិនឃើញសារ", - "addon.messages.noncontacts": "មិននៅក្នុងបញ្ជីទំនាក់ទំនង", - "addon.messages.nousersfound": "រកពុំឃើញអ្នកប្រើប្រាស់ទេ", - "addon.messages.numparticipants": "អ្នកចូលរួម {{$a}} នាក់", - "addon.messages.removecontact": "យកទំនាក់ទំនងចេញ", - "addon.messages.removecontactconfirm": "តើអ្នកពិតជាចង់ដក {{$a}} ពីបញ្ជីទំនាក់ទំនងរបស់អ្នកមែនទេ?", - "addon.messages.removefromfavourites": "ដកផ្កាយការសន្ទនា", - "addon.messages.removefromyourcontacts": "ដកចេញពីបញ្ជីទំនាក់ទំនង", - "addon.messages.requests": "សំណើ", - "addon.messages.requirecontacttomessage": "អ្នកត្រូវស្នើឲ្យ {{$a}} ដាក់អ្នកក្នុងបញ្ជីទំនាក់ទំនងដើម្បីអាចផ្ញើសារទៅគាត់បាន។", - "addon.messages.searchcombined": "ស្វែងរកមនុស្ស និងសារ", - "addon.messages.selfconversation": "ទីតាំងផ្ទាល់ខ្លួន", - "addon.messages.selfconversationdefaultmessage": "រក្សាទុកសារព្រាង តំណ ចំណាំ -ល- ដើម្បីចូលមើលពេលក្រោយ។", - "addon.messages.sendcontactrequest": "ផ្ញើសំណើទំនាក់ទំនង", - "addon.messages.showdeletemessages": "បង្ហាញសារដែលបានលុប", - "addon.messages.type_blocked": "ត្រូវបានរារាំង", - "addon.messages.type_offline": "ក្រៅបណ្ដាញ", - "addon.messages.type_online": "ក្នុងបណ្ដាញ", - "addon.messages.type_search": "លទ្ធផលនៃការស្វែងរក", - "addon.messages.type_strangers": "ផ្សេងៗ", - "addon.messages.unabletomessage": "អ្នកមិនអាចផ្ញើសារទៅកាន់អ្នកប្រើនេះទេ", - "addon.messages.unblockuser": "ឈប់ប្លុកអ្នកប្រើ", - "addon.messages.unblockuserconfirm": "តើអ្នកពិតជាចង់ឈប់ប្លុក{{$a}}មែនទេ?", - "addon.messages.unmuteconversation": "ឈប់បិទការជូនដំណឹង", - "addon.messages.useentertosend": "ប្រើច្នុច Enter ដើម្បីផ្ញើ", - "addon.messages.userwouldliketocontactyou": "{{$a}} ចង់ទាក់ទងអ្នក", - "addon.messages.warningconversationmessagenotsent": "មិនអាចផ្ញើសារចូលក្នុងការសន្ទនា {{conversation}} បានទេ។ {{error}}", - "addon.messages.warningmessagenotsent": "មិនអាចផ្ញើសារទៅអ្នកប្រើប្រាស់ {{user}}។ {{error}}", - "addon.messages.wouldliketocontactyou": "ចង់ទាក់ទងអ្នក", - "addon.messages.you": "អ្នកៈ", - "addon.messages.youhaveblockeduser": "អ្នកបានប្លុកអ្នកប្រើនេះពីមុន", - "addon.messages.yourcontactrequestpending": "សំណើទំនាក់ទំនងរបស់អ្នកស្ថិតក្នុងការរង់ចាំជាមួយនឹង {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "សូមទទួលយកតាមសេចក្តីថ្លែងអំពីការប្រគល់កិច្ចការ។", - "addon.mod_assign.addattempt": "អនុញ្ញាតឲ្យប្រគល់កិច្ចការម្តងទៀត", - "addon.mod_assign.addnewattempt": "ប្រគល់កិច្ចការម្តងទៀត", - "addon.mod_assign.addnewattemptfromprevious": "ប្រគល់កិច្ចការថ្មីមួយយោងតាមកិច្ចការដែលបានប្រគល់ពីមុន", - "addon.mod_assign.addsubmission": "ប្រគល់កិច្ចការ", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "ព័ត៌មានពីកិច្ចការ និងបែបបទសម្រាប់ប្រគល់កិច្ចការនឹងមានចាប់ពី {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "ទទួលកិច្ចការចាប់ពី", - "addon.mod_assign.allowsubmissionsfromdatesummary": "កិច្ចការនេះទទួល", - "addon.mod_assign.applytoteam": "ឲ្យពិន្ទុ និងមូលវិចារណ៍ទៅកាន់ក្រុមទាំងមូល", - "addon.mod_assign.assignmentisdue": "កិច្ចការដល់ពេលកំណត់", - "addon.mod_assign.attemptnumber": "ចំនួនអនុញ្ណាត", - "addon.mod_assign.attemptreopenmethod": "បានបើកឲ្យព្យាយាមម្តងទៀត", - "addon.mod_assign.attemptreopenmethod_manual": "ដោយផ្ទាល់", - "addon.mod_assign.attemptreopenmethod_untilpass": "ដោយស្វ័យប្រវត្តិរហូតដល់ជាប់", - "addon.mod_assign.attemptsettings": "ការកំណត់សម្រាប់ការប្រគល់កិច្ចការម្តងទៀត", - "addon.mod_assign.cannoteditduetostatementsubmission": "អ្នកមិនអាចបន្ថែម ឬ កែកិច្ចការនៅក្នុងកម្មវិធីទូរស័ព្ទបានទេ ព្រោះមិនអាចទាញយកសេចក្តីថ្លែងអំពីការប្រគល់កិច្ចការពីបណ្ដាញបាន។", - "addon.mod_assign.cannotgradefromapp": "របៀបដាក់ពិន្ទុមួយចំនួនពុំទាន់អាចប្រើប្រាស់បាននៅលើកម្មវិធីទូរស័ព្ទបានទេ និង មិនអាចកែប្រែបានទៀតផង។", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "អ្នកមិនអាចធ្វើការប្រគល់កិច្ចការតាមរយៈទូរស័ព្ទបានទេ ព្រោះមិនអាចទាញយកសេចក្តីថ្លែងអំពីការប្រគល់កិច្ចការពីបណ្តាញបាន។", - "addon.mod_assign.confirmsubmission": "តើអ្នកប្រាកដជាចង់ប្រគល់កិច្ចការរបស់អ្នកសម្រាប់ការពិន្ទុមែនទេ? អ្នកនឹងមិនអាចកែប្រែកិច្ចការរបស់អ្នកទៀតទេ។", - "addon.mod_assign.currentattempt": "នេះជាការព្យាយាម {{$a}}។", - "addon.mod_assign.currentattemptof": "នេះជាការព្យាយាមទី {{$a.attemptnumber}} (អាចព្យាយាមរហូតដល់ទៅ {{$a.maxattempts}} ដង)។", - "addon.mod_assign.currentgrade": "ពិន្ទុបច្ចុប្បន្នក្នុងបញ្ជីពិន្ទុ", - "addon.mod_assign.cutoffdate": "កាលបរិច្ឆេទឈប់ទទួល", - "addon.mod_assign.defaultteam": "ក្រុមតាមលំនាំដើម", - "addon.mod_assign.duedate": "កាលបរិច្ឆេទដល់កំណត់", - "addon.mod_assign.duedateno": "គ្មានកាលបរិច្ឆេទដល់កំណត់", - "addon.mod_assign.duedatereached": "ពេលដល់កំណត់សម្រាប់កិច្ចការនេះបានផុតទៅហើយ", - "addon.mod_assign.editingstatus": "កែប្រែស្ថានភាព", - "addon.mod_assign.editsubmission": "កែសម្រួលការដាក់ស្នើ", - "addon.mod_assign.erroreditpluginsnotsupported": "អ្នកមិនអាចបន្ថែម ឬ កែកិច្ចការនៅក្នុងកម្មវិធីទូរស័ព្ទបានទេ ព្រោះកម្មវិធីបន្ថែមមួយចំនួនមិនទាន់អាចប្រើបានទេសម្រាប់ការកែតម្រូវ។", - "addon.mod_assign.errorshowinginformation": "ពុំអាចបង្ហាញព័ត៌មានពីកិច្ចការបានទេ។", - "addon.mod_assign.extensionduedate": "ពន្យារពេលផុតកំណត់", - "addon.mod_assign.feedbacknotsupported": "មតិមូលវិចារនេះមិនអាចប្រើបានក្នុងកម្មវិធីទូរស័ព្ទទេ ហើយ អាចមិនមានព័ត៌មានទាំងអស់ទេ ។", - "addon.mod_assign.grade": "ពិន្ទុ", - "addon.mod_assign.graded": "បានដាក់ពិន្ទុ", - "addon.mod_assign.gradedby": "បានដាក់ពិន្ទុដោយ", - "addon.mod_assign.gradedfollowupsubmit": "បានដាក់ពិន្ទុ - បានទទួលការដាក់បញ្ចូនតាមដាន", - "addon.mod_assign.gradedon": "បានដាក់ពិន្ទុនៅ", - "addon.mod_assign.gradenotsynced": "ពិន្ទុមិនទាន់បានបញ្ជូន។", - "addon.mod_assign.gradeoutof": "ពិន្ទុលើ {{$a}}", - "addon.mod_assign.gradingstatus": "ស្ថានភាពនៃការដាក់ពិន្ទុ", - "addon.mod_assign.groupsubmissionsettings": "ការកំណត់សម្រាប់ការប្រគល់កិច្ចការជាក្រុម", - "addon.mod_assign.hiddenuser": "អ្នកចូលរួម", - "addon.mod_assign.latesubmissions": "កិច្ចការដែលបានប្រគល់យឺត", - "addon.mod_assign.latesubmissionsaccepted": "បានអនុញ្ញាតដល់ {{$a}}", - "addon.mod_assign.markingworkflowstate": "សភាពនៃលំហូរការងារសម្រាប់ការដាក់ពិន្ទុ", - "addon.mod_assign.markingworkflowstateinmarking": "កំពុងដាក់ពិន្ទុ", - "addon.mod_assign.markingworkflowstateinreview": "កំពុងពិនិត្យ", - "addon.mod_assign.markingworkflowstatenotmarked": "មិនទាន់ដាក់ពិន្ទុ", - "addon.mod_assign.markingworkflowstatereadyforrelease": "ត្រៀមសម្រាប់ប្រកាស", - "addon.mod_assign.markingworkflowstatereadyforreview": "ការដាក់ពិន្ទុបានបញ្ចប់", - "addon.mod_assign.markingworkflowstatereleased": "បានប្រកាស", - "addon.mod_assign.modulenameplural": "កិច្ចការ", - "addon.mod_assign.multipleteams": "សមាជិកក្នុងក្រុមលើសពីមួយ", - "addon.mod_assign.multipleteams_desc": "កិច្ចការនេះទាមទារការប្រគល់កិច្ចការជាក្រុម។ អ្នកជាសមាជិកក្នុងក្រុមច្រើនលើសពីមួយ។ ដើម្បីអាចប្រគល់កិច្ចការបាន អ្នកត្រូវតែជាសមាជិកក្នុងក្រុមតែមួយប៉ុណ្ណោះ។ សូមទាក់ទងគ្រូរបស់អ្នកដើម្បីប្តូរសមាជិកភាពរបស់អ្នក។", - "addon.mod_assign.noattempt": "មិនបានព្យាយាម", - "addon.mod_assign.nomoresubmissionsaccepted": "អនុញ្ញាតតែអ្នកដែលទទួលបានការពន្យារពេលប៉ុណ្ណោះ", - "addon.mod_assign.noonlinesubmissions": "កិច្ចការនេះមិនទាមទារឲ្យអ្នកប្រគល់អ្វីនៅលើបណ្តាញទេ", - "addon.mod_assign.nosubmission": "មិនមានអ្វីត្រូវបានប្រគល់សម្រាប់កិច្ចការនេះទេ", - "addon.mod_assign.notallparticipantsareshown": "អ្នកចូលរួមដែលមិនទាន់បានប្រគល់កិច្ចការ មិនត្រូវបានបង្ហាញទេ។", - "addon.mod_assign.noteam": "មិនជាសមាជិកក្នុងក្រុមណាឡើយ", - "addon.mod_assign.noteam_desc": "កិច្ចការនេះទាមទារការប្រគល់កិច្ចការជាក្រុម។ អ្នកមិនជាសមាជិកក្នុងក្រុមណាមួយឡើយ។ ដូច្នេះអ្នកមិនអាចប្រគល់កិច្ចការទេ។ សូមទាក់ទងគ្រូរបស់អ្នកដើម្បីប្តូរសមាជិកភាពរបស់អ្នក។", - "addon.mod_assign.notgraded": "មិនបានដាក់ពិន្ទុ", - "addon.mod_assign.numberofdraftsubmissions": "សេចក្តីព្រាង", - "addon.mod_assign.numberofparticipants": "អ្នកចូលរួម", - "addon.mod_assign.numberofsubmissionsneedgrading": "ត្រូវដាក់ពិន្ទុ", - "addon.mod_assign.numberofsubmittedassignments": "បានប្រគល់", - "addon.mod_assign.numberofteams": "ក្រុម", - "addon.mod_assign.numwords": "{{$a}} ពាក្យ", - "addon.mod_assign.outof": "{{$a.current}} លើ {{$a.total}}", - "addon.mod_assign.overdue": "កិច្ចការផុតកំណត់: {{$a}}", - "addon.mod_assign.submission": "ការដាក់ស្នើ", - "addon.mod_assign.submissioneditable": "សិស្សអាចកែសម្រួលកិច្ចការនេះ", - "addon.mod_assign.submissionnoteditable": "សិស្សមិនអាចកែសម្រួលកិច្ចការនេះទេ", - "addon.mod_assign.submissionnotsupported": "កិច្ចការនេះមិនអាចប្រើបានក្នុងកម្មវិធីទូរស័ព្ទទេ ហើយ អាចមិនមានព័ត៌មានទាំងអស់ទេ ។", - "addon.mod_assign.submissionslocked": "កិច្ចការនេះមិនទាមទារការប្រគល់កិច្ចការទេ។", - "addon.mod_assign.submissionstatus": "ស្ថានភាពនៃកិច្ចការ", - "addon.mod_assign.submissionstatus_": "មិនមានកិច្ចការបានប្រគល់ទេ", - "addon.mod_assign.submissionstatus_draft": "ព្រាង (មិនបានប្រគល់)", - "addon.mod_assign.submissionstatus_marked": "បានដាក់ពិន្ទុ", - "addon.mod_assign.submissionstatus_new": "មិនមានកិច្ចការបានប្រគល់ទេ", - "addon.mod_assign.submissionstatus_reopened": "បានបើកឡើងវិញ", - "addon.mod_assign.submissionstatus_submitted": "បានប្រគល់សម្រាប់ដាក់ពិន្ទុ", - "addon.mod_assign.submissionstatusheading": "ស្ថានភាពនៃកិច្ចការ", - "addon.mod_assign.submissionteam": "ក្រុម", - "addon.mod_assign.submitassignment": "ដាក់ស្នើកិច្ចការ", - "addon.mod_assign.submitassignment_help": "នៅពេលដែលអ្នកប្រគល់កិច្ចការនេះហើយ អ្នកនឹងមិនអាចកែវាបានទៀតទេ។", - "addon.mod_assign.submittedearly": "កិច្ចការត្រូវបានប្រគល់ {{$a}} មុនពេលកំណត់", - "addon.mod_assign.submittedlate": "កិច្ចការត្រូវបានប្រគល់ {{$a}} ក្រោយពេលកំណត់", - "addon.mod_assign.timemodified": "កែប្រែចុងក្រោយ", - "addon.mod_assign.timeremaining": "ពេលនៅសល់", - "addon.mod_assign.ungroupedusers": "ការកំណត់ \"ទាមទារឲ្យមានក្រុមសម្រាប់ប្រគល់កិច្ចការ\" ត្រូវបានបើក ហើយអ្នកប្រើខ្លះមិនជាសមាជិកក្នុងក្រុមណាមួយទេ ហើយខ្លះជាសមាជិកក្នុងក្រុមលើសពីមួយ។ ដូច្នេះគេមិនអាចប្រគល់កិច្ចការទេ។", - "addon.mod_assign.unlimitedattempts": "មិនកំណត់", - "addon.mod_assign.userswhoneedtosubmit": "អ្នកដែលត្រូវប្រគល់កិច្ចការ៖ {{$a}}", - "addon.mod_assign.userwithid": "អ្នកប្រើប្រាស់ដែលមានលេខសម្គាល់ {{id}}", - "addon.mod_assign.viewsubmission": "មើលកិច្ចការដែលបានប្រគល់", - "addon.mod_assign.warningsubmissiongrademodified": "ពិន្ទុសម្រាប់កិច្ចការត្រូវបានកែប្រែនៅលើបណ្ដាញ។", - "addon.mod_assign.warningsubmissionmodified": "កិច្ចការរបស់អ្នកប្រើប្រាស់ត្រូវបានកែប្រែនៅលើបណ្ដាញ។", - "addon.mod_assign.wordlimit": "កំណត់ចំនួនពាក្យ", - "addon.mod_assign_feedback_comments.pluginname": "មតិមូលវិចារណ៍", - "addon.mod_assign_feedback_editpdf.pluginname": "ដាក់ចំណារពន្យល់លើ PDF", - "addon.mod_assign_feedback_file.pluginname": "មូលវិចារណ៍ជាឯកសារ", - "addon.mod_assign_submission_comments.pluginname": "មតិសម្រាប់កិច្ចការ", - "addon.mod_assign_submission_file.pluginname": "ប្រគល់ឯកសារ", - "addon.mod_assign_submission_onlinetext.pluginname": "ការប្រគល់កិច្ចការដោយការសរសេរអត្ថបទ", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "ចំនួនពាក្យកំណត់សម្រាប់កិច្ចការនេះគឺ {{$a.limit}} ពាក្យ។ អ្នកកំពុងព្យាយាមប្រគល់កិច្ចការដែលមានចំនួនពាក្យ {{$a.count}} ពាក្យ។ សូមពិនិត្យកិច្ចការអ្នកឡើងវិញ រួចសូមព្យាយាមម្តងទៀត។", - "addon.mod_book.errorchapter": "មានកំហុសក្នុងការអានជំពូកក្នុងសៀវភៅ។", - "addon.mod_book.modulenameplural": "សៀវភៅ", - "addon.mod_book.navnexttitle": "បន្ទាប់៖ {{$a}}", - "addon.mod_book.navprevtitle": "មុន៖ {{$a}}", - "addon.mod_book.tagarea_book_chapters": "ជំពូកក្នុងសៀវភៅ", - "addon.mod_book.toc": "តារាងមាតិកា", - "addon.mod_chat.beep": "បន្លឺសំឡេង", - "addon.mod_chat.chatreport": "សម័យជជែកកំសាន្ដ", - "addon.mod_chat.currentusers": "អ្នកប្រើបច្ចុប្បន្ន", - "addon.mod_chat.enterchat": "ចុចទីនេះដើម្បីចូលការជជែកកំសាន្ដនៅពេលឥឡូវ", - "addon.mod_chat.entermessage": "បញ្ចូលសាររបស់អ្នក", - "addon.mod_chat.errorwhileconnecting": "មានកំហុសអំឡុងពេលភ្ជាប់ទៅកាន់ការជជែក។", - "addon.mod_chat.errorwhilegettingchatdata": "មានកំហុសអំឡុងពេលទាញយកទិន្នន័យការជជែក។", - "addon.mod_chat.errorwhilegettingchatusers": "មានកំហុសអំឡុងពេលទាញយកអ្នកប្រើប្រាស់សារជជែក។", - "addon.mod_chat.errorwhileretrievingmessages": "មានកំហុសអំឡុងពេលទាញយកសារពីម៉ាស៊ីនបម្រើ។", - "addon.mod_chat.errorwhilesendingmessage": "មានកំហុសអំឡុងពេលផ្ញើសារ។", - "addon.mod_chat.messagebeepseveryone": "{{$a}} បន្លឺសំឡេងដល់អ្នកគ្រប់គ្នា !", - "addon.mod_chat.messagebeepsyou": "{{$a}} ទើបតែបានបន្លឺសំឡេងដល់អ្នក !", - "addon.mod_chat.messageenter": "{{$a}} បានចូលការជជែកកំសាន្ដនេះ", - "addon.mod_chat.messageexit": "{{$a}} បានចេញពីការជជែកនេះ", - "addon.mod_chat.messages": "សារ", - "addon.mod_chat.messageyoubeep": "អ្នកបានបន្លឺសំឡេង", - "addon.mod_chat.modulenameplural": "ជជែក", - "addon.mod_chat.mustbeonlinetosendmessages": "អ្នកត្រូវតែភ្ជាប់ទៅកាន់បណ្ដាញអ៊ីនធឺណិតដើម្បីផ្ញើសារ។", - "addon.mod_chat.nomessages": "គ្មានសារនៅឡើយទេ", - "addon.mod_chat.saidto": "និយាយទៅកាន់", - "addon.mod_chat.send": "ផ្ញើ", - "addon.mod_chat.sessionstart": "សម័យជជែកបន្ទាប់នឹងចាប់ផ្តើមនៅ {{$a.date}}, ({{$a.fromnow}} ពីពេលនេះ)", - "addon.mod_chat.talk": "និយាយ", - "addon.mod_chat.viewreport": "មើលសម័យជជែកមុន", - "addon.mod_choice.cannotsubmit": "សូមអភ័យទោស! មានបញ្ហាក្នុងការផ្ញើជម្រើសរបស់អ្នក។ សូមព្យាយាមម្តងទៀត។", - "addon.mod_choice.choiceoptions": "ជម្រើសសម្រាប់ជ្រើស", - "addon.mod_choice.errorgetchoice": "មានកំហុសក្នុងការទាញយកទិន្នន័យជម្រើស។", - "addon.mod_choice.expired": "សូមអភ័យទោស, សកម្មភាពនេះត្រូវបានបិទនៅ {{$a}} និងមិនមានទៀតទេ", - "addon.mod_choice.full": "(ពេញលេញ)", - "addon.mod_choice.modulenameplural": "ជម្រើស", - "addon.mod_choice.noresultsviewable": "បច្ចុប្បន្ន លទ្ធផលមិនអាចមើលបានឡើយ ។", - "addon.mod_choice.notopenyet": "សូមអភ័យទោស សកម្មភាពនេះគឺមិនមានរហូតដល់ {{$a}}", - "addon.mod_choice.numberofuser": "ចំនួនចម្លើយ", - "addon.mod_choice.numberofuserinpercentage": "ភាគរយនៃចម្លើយ", - "addon.mod_choice.previewonly": "នេះគ្រាន់តែការមើលទុកជាមុននៃជម្រើសដែលមានសម្រាប់សកម្មភាពនេះប៉ុណ្ណោះ។ អ្នកមិនអាចផ្ញើជម្រើសរបស់អ្នករហូតដល់ {{$a}}។", - "addon.mod_choice.removemychoice": "យកជម្រើសរបស់ខ្ញុំចេញ", - "addon.mod_choice.responses": "ចម្លើយ", - "addon.mod_choice.responsesresultgraphdescription": "អ្នកប្រើប្រាស់ {{number}}% បានជ្រើសយកជម្រើស: {{text}}។", - "addon.mod_choice.responsesresultgraphheader": "ការបង្ហាញក្រាប", - "addon.mod_choice.resultsnotsynced": "ចម្លើយចុងក្រោយរបស់អ្នកត្រូវតែបានបញ្ជូនអោយរួចរាល់មុនពេលដែលវាត្រូវបានដាក់ចូលទៅក្នុងលទ្ធផល។", - "addon.mod_choice.savemychoice": "រក្សាទុកជម្រើសរបស់ខ្ញុំ", - "addon.mod_choice.userchoosethisoption": "អ្នកដែលបានជ្រើសជម្រើសនេះ", - "addon.mod_choice.yourselection": "ជម្រើសរបស់អ្នក", - "addon.mod_data.addentries": "បន្ថែមធាតុ", - "addon.mod_data.advancedsearch": "ស្វែងរកកម្រិតខ្ពស់", - "addon.mod_data.alttext": "អត្ថបទជំនួស", - "addon.mod_data.approve": "អនុម័ត", - "addon.mod_data.approved": "បានអនុម័ត", - "addon.mod_data.ascending": "លំដាប់ឡើង", - "addon.mod_data.authorfirstname": "នាមខ្លួនអ្នកនិពន្ធ", - "addon.mod_data.authorlastname": "នាមត្រកូលអ្នកនិពន្ធ", - "addon.mod_data.confirmdeleterecord": "តើអ្នកពិតជាចង់លុបធាតុនេះឬ ?", - "addon.mod_data.descending": "លំដាប់ចុះ", - "addon.mod_data.disapprove": "ឈប់អនុម័តវិញ", - "addon.mod_data.emptyaddform": "អ្នកមិនបានបំពេញវាលណាមួយឡើយ !", - "addon.mod_data.entrieslefttoadd": "អ្នកត្រូវតែបន្ថែម {{$a.entriesleft}} ធាតុបន្ថែមដើម្បីបញ្ចប់សកម្មភាពនេះ", - "addon.mod_data.entrieslefttoaddtoview": "អ្នកត្រូវតែបន្ថែម {{$a.entrieslefttoview}} ធាតុបន្ថែម មុនពេលអ្នកអាចមើលធាតុរបស់អ្នកចូលរួមផ្សេងទៀត ។", - "addon.mod_data.errorapproving": "មានកំហុសក្នុងការទទួលយក ឬ ឈប់ទទួលយកទិន្នន័យដែលវាយចូល។", - "addon.mod_data.errordeleting": "មានកំហុសក្នុងការលុបធាតុដែលបានបញ្ចូល។", - "addon.mod_data.errormustsupplyvalue": "អ្នកត្រូវតែផ្ដល់ព័ត៌មាននៅទីនេះ ។", - "addon.mod_data.expired": "សូមអភ័យទោស! សកម្មភាពនេះបានបិទនៅ {{$a}} ហើយមិនមានទៀតទេ។", - "addon.mod_data.fields": "វាល", - "addon.mod_data.foundrecords": "រកឃើញកំណត់ត្រា ៖ {{$a.num}}/{{$a.max}} (កំណត់តម្រងឡើងវិញ)", - "addon.mod_data.latlongboth": "ទាមទារទាំងរយៈទទឹង និងរយៈបណ្ដោយ។", - "addon.mod_data.menuchoose": "ជ្រើស...", - "addon.mod_data.modulenameplural": "មូលដ្ឋានទិន្នន័យ", - "addon.mod_data.more": "ច្រើនទៀត", - "addon.mod_data.nomatch": "រកមិនឃើញធាតុដែលផ្គូផ្គង !", - "addon.mod_data.norecords": "គ្មានធាតុនៅក្នុងមូលដ្ឋានទិន្នន័យ", - "addon.mod_data.notapproved": "ធាតុមិនត្រូវបានអនុម័តនៅឡើយទេ។", - "addon.mod_data.notopenyet": "សូមអភ័យទោស! សកម្មភាពនេះនឹងមិនអាចចូលបានរហូតដល់ {{$a}}", - "addon.mod_data.numrecords": "{{$a}} ធាតុ", - "addon.mod_data.other": "ផ្សេងទៀត", - "addon.mod_data.recordapproved": "បានអនុម័តធាតុ", - "addon.mod_data.recorddeleted": "បានលុបធាតុ", - "addon.mod_data.recorddisapproved": "មិនបានអនុម័តធាតុ", - "addon.mod_data.resetsettings": "កំណត់វាលឡើងវិញ", - "addon.mod_data.search": "ស្វែងរក", - "addon.mod_data.selectedrequired": "ទាមទារដែលជ្រើសទាំងអស់", - "addon.mod_data.single": "មើលតែមួយ", - "addon.mod_data.tagarea_data_records": "កំណត់ត្រាទិន្នន័យ", - "addon.mod_data.timeadded": "ពេលវេលាបន្ថែម", - "addon.mod_data.timemodified": "ពេលវេលាកែប្រែ", - "addon.mod_data.usedate": "បញ្ចូលក្នុងការស្វែងរក។", - "addon.mod_feedback.analysis": "ការវិភាគ", - "addon.mod_feedback.anonymous": "អនាមិក", - "addon.mod_feedback.anonymous_entries": "ធាតុអនាមិក ({{$a}})", - "addon.mod_feedback.average": "មធ្យមភាគ", - "addon.mod_feedback.captchaofflinewarning": "សកម្មភាពមូលវិចារដោយមាន CAPTCHA មិនអាចបញ្ចប់នៅក្រៅបណ្តាញ ឬនៅពេលមិនទាន់បានកំណត់រចនាសម្ព័ន្ធ ឬនៅពេលម៉ាស៊ីនបម្រើមានបញ្ហាបានទេ។", - "addon.mod_feedback.complete_the_form": "ឆ្លើយសំណួរ...", - "addon.mod_feedback.completed_feedbacks": "បានប្រគល់ចម្លើយ", - "addon.mod_feedback.continue_the_form": "បន្តឆ្លើយសំណួរ...", - "addon.mod_feedback.feedback_is_not_open": "មូលវិចារណ៍មិនត្រូវបានបើកទេ", - "addon.mod_feedback.feedback_submitted_offline": "មូលវិចារនេះត្រូវបានរក្សាទុកដើម្បីប្រគល់ពេលក្រោយ។", - "addon.mod_feedback.feedbackclose": "អនុញ្ញាតឲ្យឆ្លើយដល់", - "addon.mod_feedback.feedbackopen": "អនុញ្ញាតឲ្យឆ្លើយដល់", - "addon.mod_feedback.mapcourses": "ផ្គូផ្គងមូលវិចារណ៍ទៅកាន់វគ្គសិក្សា", - "addon.mod_feedback.maximal": "អតិបរមា", - "addon.mod_feedback.minimal": "អប្បបរមា", - "addon.mod_feedback.mode": "ម៉ូត", - "addon.mod_feedback.modulenameplural": "មូលវិចារណ៍", - "addon.mod_feedback.next_page": "ទំព័របន្ទាប់", - "addon.mod_feedback.non_anonymous": "ឈ្មោះអ្នកប្រើនឹងត្រូវបានកត់ត្រាទុកនិងបង្ហាញជាមួយនឹងចម្លើយ", - "addon.mod_feedback.non_anonymous_entries": "ធាតុមិនអនាមិក ({{$a}})", - "addon.mod_feedback.not_selected": "មិនបានជ្រើស", - "addon.mod_feedback.not_started": "មិនបានចាប់ផ្តើម", - "addon.mod_feedback.overview": "ទិដ្ឋភាពទូទៅ", - "addon.mod_feedback.page_after_submit": "សារបញ្ចប់", - "addon.mod_feedback.preview": "មើលជាមុន", - "addon.mod_feedback.previous_page": "ទំព័រមុន", - "addon.mod_feedback.questions": "សំណួរ", - "addon.mod_feedback.response_nr": "ចំនួនចម្លើយតប", - "addon.mod_feedback.responses": "ចម្លើយតប", - "addon.mod_feedback.save_entries": "ប្រគល់ចម្លើយរបស់អ្នក", - "addon.mod_feedback.show_entries": "បង្ហាញចម្លើយតប", - "addon.mod_feedback.show_nonrespondents": "បង្ហាញអ្នកមិនបានឆ្លើយ", - "addon.mod_feedback.started": "បានចាប់ផ្តើម", - "addon.mod_feedback.this_feedback_is_already_submitted": "អ្នកបានបញ្ចប់សកម្មភាពនេះរួចហើយ។", - "addon.mod_folder.emptyfilelist": "មិនមានឯកសារសម្រាប់បង្ហាញទេ។", - "addon.mod_folder.modulenameplural": "ថតឯកសារ", - "addon.mod_forum.addanewdiscussion": "បន្ថែមប្រធានបទកិច្ចពិភាក្សាថ្មី", - "addon.mod_forum.addanewquestion": "បន្ថែមសំណួរថ្មី", - "addon.mod_forum.addanewtopic": "បន្ថែមប្រធានបទថ្មី", - "addon.mod_forum.addtofavourites": "ដាក់ផ្កាយការពិភាក្សានេះ", - "addon.mod_forum.advanced": "កម្រិតខ្ពស់", - "addon.mod_forum.cannotadddiscussion": "ការបន្ថែមកិច្ចពិភាក្សាទៅវេទិកានេះ ត្រូវការសមាជិកភាពក្រុម ។", - "addon.mod_forum.cannotadddiscussionall": "អ្នកមិនមានសិទ្ធិដើម្បីបន្ថែមប្រធានបទពិភាក្សាថ្មីសម្រាប់អ្នកចូលរួមទាំងអស់ទេ ។", - "addon.mod_forum.cannotcreatediscussion": "មិនអាចបង្កើតកិច្ចពិភាក្សាថ្មីបានទេ", - "addon.mod_forum.couldnotadd": "មិនអាចបន្ថែមប្រកាសរបស់អ្នកបានឡើយ ដោយសារមានកំហុសមិនស្គាល់", - "addon.mod_forum.couldnotupdate": "មិនអាចធ្វើឲ្យប្រកាសរបស់អ្នកទាន់សម័យបានឡើយ ដោយសារមានកំហុសមិនស្គាល់", - "addon.mod_forum.cutoffdatereached": "កាលបរិច្ឆេទឈប់ទទួលប្រកាសបានដល់ហើយ ដូច្នេះអ្នកមិនអាចដាក់ប្រកាសក្នុងវេទិកាទៀតទេ។", - "addon.mod_forum.delete": "លុប", - "addon.mod_forum.deletedpost": "ប្រកាសត្រូវបានលុប", - "addon.mod_forum.deletesure": "តើអ្នកពិតជាចង់លុបប្រកាសនេះឬ ?", - "addon.mod_forum.discussion": "កិច្ចពិភាក្សា", - "addon.mod_forum.discussionlistsortbycreatedasc": "តម្រៀបបញ្ជីកិច្ចពិភាក្សាដោយកាលបរិច្ឆេទបង្កើតក្នុងលំដាប់កើនឡើង", - "addon.mod_forum.discussionlistsortbycreateddesc": "តម្រៀបបញ្ជីកិច្ចពិភាក្សាដោយកាលបរិច្ឆេទបង្កើតក្នុងលំដាប់ថយចុះ", - "addon.mod_forum.discussionlistsortbylastpostasc": "តម្រៀបបញ្ជីកិច្ចពិភាក្សាដោយកាលបរិច្ឆេទបង្កើតប្រកាសចុងក្រោយក្នុងលំដាប់កើនឡើង", - "addon.mod_forum.discussionlistsortbylastpostdesc": "តម្រៀបបញ្ជីកិច្ចពិភាក្សាដោយកាលបរិច្ឆេទបង្កើតប្រកាសចុងក្រោយក្នុងលំដាប់ថយចុះ", - "addon.mod_forum.discussionlistsortbyrepliesasc": "តម្រៀបបញ្ជីកិច្ចពិភាក្សាដោយចំនួនការឆ្លើយតបក្នុងលំដាប់កើនឡើង", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "តម្រៀបបញ្ជីកិច្ចពិភាក្សាដោយចំនួនការឆ្លើយតបក្នុងលំដាប់ថយចុះ", - "addon.mod_forum.discussionlocked": "ការពិភាក្សាត្រូវបានបិទ ដូច្នេះអ្នកមិនអាចឆ្លើយតបទៀតទេ។", - "addon.mod_forum.discussionpinned": "បានដាក់ខ្ទាស់", - "addon.mod_forum.discussionsubscription": "ការតាមការពិភាក្សា", - "addon.mod_forum.edit": "កែសម្រួល", - "addon.mod_forum.erroremptymessage": "ខ្លឹមសារប្រកាសមិនអាចទទេឡើយ", - "addon.mod_forum.erroremptysubject": "ប្រធានបទប្រកាសមិនអាចទទេឡើយ។", - "addon.mod_forum.errorgetforum": "មានកំហុសក្នុងការទាញយកទិន្នន័យក្នុងវេទិកា។", - "addon.mod_forum.errorgetgroups": "មានកំហុសក្នុងទាញយកការកំណត់ក្រុម។", - "addon.mod_forum.favouriteupdated": "ជម្រើសដាក់ផ្កាយរបស់អ្នកត្រូវបានកែសម្រួលថ្មី។", - "addon.mod_forum.forumnodiscussionsyet": "មិនទាន់មានការពិភាក្សានៅឡើយទេនៅក្នុងវេទិកានេះ។", - "addon.mod_forum.group": "ក្រុម", - "addon.mod_forum.lastpost": "ប្រកាសចុងក្រោយ", - "addon.mod_forum.lockdiscussion": "បិទកិច្ចពិភាក្សានេះ", - "addon.mod_forum.lockupdated": "ជម្រើសបិទត្រូវបានកែសម្រួលថ្មី។", - "addon.mod_forum.message": "សារ", - "addon.mod_forum.modeflatnewestfirst": "បង្ហាញការឆ្លើយតបស្មើ ថ្មីៗបង្ហាញមុន", - "addon.mod_forum.modeflatoldestfirst": "បង្ហាញការឆ្លើយតបស្មើ ចាស់ៗបង្ហាញមុន", - "addon.mod_forum.modenested": "បង្ហាញការឆ្លើយតបជាទម្រង់ក្នុងគ្នា", - "addon.mod_forum.modulenameplural": "វេទិកា", - "addon.mod_forum.numdiscussions": "ការពិភាក្សា {{numdiscussions}}", - "addon.mod_forum.numreplies": "ការឆ្លើយតប {{numreplies}}", - "addon.mod_forum.pindiscussion": "ដាក់ខ្ទាស់កិច្ចពិភាក្សានេះ", - "addon.mod_forum.pinupdated": "ជម្រើសដាក់ខ្ទាស់ត្រូវបានកែសម្រួលថ្មី។", - "addon.mod_forum.postisprivatereply": "នេះជាចម្លើយតបឯកជន។ អ្នកដទៃមិនអាចមើលឃើញវាទេ។", - "addon.mod_forum.posttoforum": "ប្រកាសទៅវេទិកា", - "addon.mod_forum.posttomygroups": "ប្រកាសច្បាប់ចម្លងមួយទៅក្រុមទាំងអស់", - "addon.mod_forum.privatereply": "តបលក្ខណៈឯកជន", - "addon.mod_forum.re": "ឆ្លើយតប ៖", - "addon.mod_forum.refreshdiscussions": "ដំណើរការកិច្ចពិភាក្សាសាជាថ្មី", - "addon.mod_forum.refreshposts": "ដំណើរការប្រកាសសាជាថ្មី", - "addon.mod_forum.removefromfavourites": "ដកផ្កាយពីកិច្ចពិភាក្សានេះ", - "addon.mod_forum.reply": "ឆ្លើយតប", - "addon.mod_forum.replyplaceholder": "សរសេរចម្លើយតបរបស់អ្នក...", - "addon.mod_forum.subject": "ប្រធានបទ", - "addon.mod_forum.tagarea_forum_posts": "ប្រកាសក្នុងវេទិកា", - "addon.mod_forum.thisforumhasduedate": "កាលបរិច្ឆេទដល់កំណត់សម្រាប់ការដាក់ប្រកាសទៅកាន់វេទិកានេះគឺ {{$a}}។", - "addon.mod_forum.thisforumisdue": "កាលបរិច្ឆេទដល់កំណត់សម្រាប់ការដាក់ប្រកាសទៅកាន់វេទិកានេះគឺ {{$a}}។", - "addon.mod_forum.unlockdiscussion": "បើកកិច្ចពិភាក្សានេះ", - "addon.mod_forum.unpindiscussion": "ឈប់ដាក់ខ្ទាស់កិច្ចពិភាក្សានេះ", - "addon.mod_forum.unread": "មិនបានអាន", - "addon.mod_forum.unreadpostsnumber": "ប្រកាសដែលមិនបានអាន {{$a}}", - "addon.mod_forum.yourreply": "ការឆ្លើយតបរបស់អ្នក", - "addon.mod_glossary.addentry": "បន្ថែមធាតុថ្មី", - "addon.mod_glossary.aliases": "ពាក្យគន្លឹះ", - "addon.mod_glossary.attachment": "ឯកសារភ្ជាប់", - "addon.mod_glossary.browsemode": "ស្វែងរកធាតុ", - "addon.mod_glossary.byalphabet": "តាមលំដាប់អក្ខរក្រម", - "addon.mod_glossary.byauthor": "ចាត់ជាក្រុមតាមអ្នកនិពន្ធ", - "addon.mod_glossary.bycategory": "ចាត់ជាក្រុមតាមប្រភេទ", - "addon.mod_glossary.bynewestfirst": "ថ្មីបំផុតមុន", - "addon.mod_glossary.byrecentlyupdated": "បានកែប្រែថ្មីៗ", - "addon.mod_glossary.bysearch": "ស្វែងរក", - "addon.mod_glossary.cannoteditentry": "មិនអាចកែប្រែធាតុបានទេ", - "addon.mod_glossary.casesensitive": "ធាតុនេះគឺប្រកាន់អក្សរតូចធំ", - "addon.mod_glossary.categories": "ប្រភេទ", - "addon.mod_glossary.concept": "គំនិត", - "addon.mod_glossary.definition": "ការកំណត់", - "addon.mod_glossary.entriestobesynced": "ធាតុដែលត្រូវធ្វើសមកាលកម្ម", - "addon.mod_glossary.entrypendingapproval": "ធាតុនេះគឺកំពុងរង់ចាំការយល់ព្រម។", - "addon.mod_glossary.entryusedynalink": "ធាតុនេះគួរតែត្រូវបានតភ្ជាប់ដោយស្វ័យប្រវត្តិ", - "addon.mod_glossary.errconceptalreadyexists": "មានគំនិតនេះរួចហើយ ។ គ្មានច្បាប់ចម្លងណាដែលបានអនុញ្ញាតនៅក្នុងសទ្ទានុក្រមនោះទេ ។", - "addon.mod_glossary.errorloadingentries": "មានកំហុសមួយបានកើតឡើងនៅពេលបង្ហាញធាតុ។", - "addon.mod_glossary.errorloadingentry": "មានកំហុសមួយបានកើតឡើងនៅពេលបង្ហាញធាតុ។", - "addon.mod_glossary.errorloadingglossary": "មានកំហុសមួយបានកើតឡើងនៅពេលផ្ទុកសន្ទានុក្រម។", - "addon.mod_glossary.fillfields": "គំនិត និងការកំណត់គឺជាវាលចាំបាច់ ។", - "addon.mod_glossary.fullmatch": "ផ្គូផ្គងតែពាក្យទាំងមូលប៉ុណ្ណោះ", - "addon.mod_glossary.linking": "ការតភ្ជាប់ដោយស្វ័យប្រវត្តិ", - "addon.mod_glossary.modulenameplural": "សទ្ទានុក្រម", - "addon.mod_glossary.noentriesfound": "រកមិនឃើញធាតុទេ។", - "addon.mod_glossary.searchquery": "ពាក្យស្វែងរក", - "addon.mod_glossary.tagarea_glossary_entries": "ធាតុសទ្ទានុក្រម", - "addon.mod_imscp.deploymenterror": "កំហុសទាក់ទងនឹងកញ្ចប់មាតិកា", - "addon.mod_imscp.modulenameplural": "កញ្ចប់មាតិកា IMS", - "addon.mod_imscp.showmoduledescription": "បង្ហាញពីការពិពណ៍នា", - "addon.mod_imscp.toc": "តារាងមាតិកា", - "addon.mod_lesson.answer": "ចម្លើយ", - "addon.mod_lesson.attempt": "ការឆ្លើយ ៖ {{$a}}", - "addon.mod_lesson.attemptheader": "ការសាកល្បង", - "addon.mod_lesson.attemptsremaining": "អ្នកនៅសល់ការឆ្លើយ {{$a}} ដងទៀត", - "addon.mod_lesson.averagescore": "ពិន្ទុមធ្យម", - "addon.mod_lesson.averagetime": "ពេលវេលាមធ្យម", - "addon.mod_lesson.branchtable": "តារាងមែកធាង", - "addon.mod_lesson.cannotfindattempt": "កំហុស៖ រកមិនឃើញការសាកល្បងទេ", - "addon.mod_lesson.cannotfinduser": "កំហុស៖ រកមិនឃើញអ្នកប្រើទេ", - "addon.mod_lesson.clusterjump": "សំណួរមិនឃើញក្នុងក្រុម", - "addon.mod_lesson.completed": "បញ្ចប់ទាំងស្រុង", - "addon.mod_lesson.congratulations": "អបអរសាទរ បានមកដល់ចុងបញ្ចប់នៃមេរៀនហើយ", - "addon.mod_lesson.continue": "បន្ត", - "addon.mod_lesson.continuetonextpage": "បន្តទៅទំព័របន្ទាប់។", - "addon.mod_lesson.defaultessayresponse": "តែងសេចក្ដីរបស់អ្នកនឹងត្រូវបានដាក់ពិន្ទុដោយគ្រូវគ្គសិក្សា ។", - "addon.mod_lesson.detailedstats": "ស្ថិតិលម្អិត", - "addon.mod_lesson.didnotanswerquestion": "កុំឆ្លើយសំណួរនេះ ។", - "addon.mod_lesson.displayofgrade": "ការបង្ហាញពិន្ទុ (សម្រាប់តែសិស្សប៉ុណ្ណោះ)", - "addon.mod_lesson.displayscorewithessays": "អ្នកទទួលបានពិន្ទុ {{$a.score}} នៃ {{$a.tempmaxgrade}} សម្រាប់សំណួរដែលដាក់ពិន្ទុដោយស្វ័យប្រវត្តិ ។
                សំណួរតែងសេចក្ដី {{$a.essayquestions}} របស់អ្នកនឹងត្រូវបានដាក់ពិន្ទុ និងបន្ថែម
                ទៅក្នុងពិន្ទុចុងក្រោយរបស់អ្នកនៅពេលក្រោយ ។

                ពិន្ទុបច្ចុប្បន្នរបស់អ្នកដោយមិនរាប់សំណួរតែងសេចក្ដីគឺ {{$a.score}} នៃ {{$a.grade}}", - "addon.mod_lesson.displayscorewithoutessays": "ពិន្ទុរបស់អ្នកគឺ {{$a.score}} (នៃ {{$a.grade}}) ។", - "addon.mod_lesson.emptypassword": "ពាក្យសម្ងាត់មិនអាចទទេទេ", - "addon.mod_lesson.enterpassword": "សូមបញ្ចូលពាក្យសម្ងាត់ ៖", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "អ្នកមិនបានឆ្លើយសំណួរមួយទេ ។ អ្នកបានទទួលពិន្ទុ ០ សម្រាប់មេរៀននេះ ។", - "addon.mod_lesson.errorprefetchrandombranch": "មេរៀននេះមានតំណលោតទៅកាន់ទំព័រមាតិកាចៃដន្យមួយ។ អ្នកមិនអាចសាកល្បងធ្វើមេរៀននេះនៅក្នុងកម្មវិធីទូរស័ព្ទទេ លុះត្រាតែវាត្រូវបានចាប់ផ្តើមដោយប្រើកម្មវិធីរុករកសិន។", - "addon.mod_lesson.errorreviewretakenotlast": "ការសាកល្បងនេះលែងអាចត្រួតពិនិត្យបានទៀតហើយ", - "addon.mod_lesson.finish": "បញ្ចប់", - "addon.mod_lesson.finishretakeoffline": "ការព្យាយាមនេះគឺត្រូវបានបញ្ឈប់នៅពេលក្រៅបណ្តាញ។", - "addon.mod_lesson.firstwrong": "ជាអកុសល អ្នកមិនអាចទទួលបានពិន្ទុមួយនេះទេ ពីព្រោះចម្លើយរបស់អ្នកមិនត្រឹមត្រូវ ។ តើអ្នកនឹងបន្តការទាយទៀតឬ គ្រាន់តែសម្រាប់ការកំសាន្តនៃការរៀនប៉ុណ្ណោះ (ប៉ុន្តែគ្មានក្រេឌីតពិន្ទុទេ) ?", - "addon.mod_lesson.gotoendoflesson": "ទៅកាន់ចុងនៃមេរៀន", - "addon.mod_lesson.grade": "ពិន្ទុ", - "addon.mod_lesson.highscore": "ពិន្ទុខ្ពស់", - "addon.mod_lesson.hightime": "ពេលវេលាច្រើន", - "addon.mod_lesson.leftduringtimed": "អ្នកបានចាកចេញនៅកំឡុងពេលមេរៀនដែលបានកំណត់ពេល ។
                សូមចុចលើបន្តដើម្បីចាប់ផ្ដើមមេរៀនឡើងវិញ ។", - "addon.mod_lesson.leftduringtimednoretake": "អ្នកបានចាកចេញនៅកំឡុងពេលមេរៀនដែលបានកំណត់ពេល ហើយអ្នក
                មិនត្រូវបានអនុញ្ញាតឲ្យយកឡើងវិញ ឬបន្ទមេរៀនឡើយ ។", - "addon.mod_lesson.lessonmenu": "ម៉ឺនុយមេរៀន", - "addon.mod_lesson.lessonstats": "ស្ថិតិមេរៀន", - "addon.mod_lesson.linkedmedia": "បានតភ្ជាប់មេឌៀ", - "addon.mod_lesson.loginfail": "ការចូលបានបរាជ័យ សូមព្យាយាមម្ដងទៀត...", - "addon.mod_lesson.lowscore": "ពិន្ទុទាប", - "addon.mod_lesson.lowtime": "ពេលវេលាតិច", - "addon.mod_lesson.maximumnumberofattemptsreached": "បានមកដល់ចំនួនការឆ្លើយអតិបរមាហើយ - ផ្លាស់ទីទៅទំព័របន្ទាប់", - "addon.mod_lesson.modattemptsnoteacher": "សិស្សពិនិត្យឡើងវិញតែការងារសម្រាប់សិស្សតែប៉ុណ្ណោះ ។", - "addon.mod_lesson.modulenameplural": "មេរៀន", - "addon.mod_lesson.noanswer": "មិនបានផ្ដល់ចម្លើយទេ ។ សូមថយក្រោយវិញ ហើយដាក់ស្នើចម្លើយ ។", - "addon.mod_lesson.nolessonattempts": "មិនបានឆ្លើយលើមេរៀននេះទេ ។", - "addon.mod_lesson.nolessonattemptsgroup": "មិនមានការសាកល្បងលើមេរៀននេះដោយសមាជិកក្នុងក្រុម {{$a}} ទេ ។", - "addon.mod_lesson.notcompleted": "មិនបានបញ្ចប់ទាំងស្រុងទេ", - "addon.mod_lesson.numberofcorrectanswers": "ចំនួនចម្លើយត្រឹមត្រូវ ៖ {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "ចំនួនសំណួរដែលបានឆ្លើយ ៖ {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "ចំនួនសំណួរដែលបានឆ្លើយ ៖ {{$a.nquestions}}", - "addon.mod_lesson.ongoingcustom": "មកដល់ពេលនេះ អ្នកបានទទួលពិន្ទុ {{$a.score}} នៃពិន្ទុ {{$a.currenthigh}} ។", - "addon.mod_lesson.ongoingnormal": "អ្នកបានឆ្លើយត្រឹមត្រូវចំនួន {{$a.correct}} នៃការឆ្លើយ {{$a.viewed}} ។", - "addon.mod_lesson.or": "ឬ", - "addon.mod_lesson.overview": "ទិដ្ឋភាពទូទៅ", - "addon.mod_lesson.preview": "មើលជាមុន", - "addon.mod_lesson.progressbarteacherwarning2": "អ្នកនឹងមិនឃើញរបារវឌ្ឍនភាពទេព្រោះអ្នកអាចកែសម្រួលមេរៀននេះ", - "addon.mod_lesson.progresscompleted": "អ្នកបានបំពេញមេរៀន {{$a}}% ហើយ", - "addon.mod_lesson.question": "សំណួរ", - "addon.mod_lesson.rawgrade": "ពិន្ទុសុទ្ធ", - "addon.mod_lesson.reports": "របាយការណ៍", - "addon.mod_lesson.response": "ចម្លើយ", - "addon.mod_lesson.retakefinishedinsync": "ការព្យាយាមនៅក្រៅបណ្តាញគឺត្រូវបានធ្វើសមកាលកម្ម។ តើអ្នកចង់ពីនិត្យមើលវាឡើងវិញដែរឬទេ?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "ពិនិត្យឡើងវិញ", - "addon.mod_lesson.reviewlesson": "ពិនិត្យមេរៀនឡើងវិញ", - "addon.mod_lesson.reviewquestionback": "បាទ/ចាស ខ្ញុំចង់ព្យាយាមម្ដងទៀត", - "addon.mod_lesson.reviewquestioncontinue": "ទេ ខ្ញុំគ្រាន់តែចង់បន្តទៅសំណួរបន្ទាប់តែប៉ុណ្ណោះ", - "addon.mod_lesson.secondpluswrong": "មិនចាកចេញ ។ តើអ្នកចង់ព្យាយាមម្ដងទៀតឬ ?", - "addon.mod_lesson.submit": "ប្រគល់", - "addon.mod_lesson.teacherjumpwarning": "ការលោត {{$a.cluster}} ឬការលោត {{$a.unseen}} ត្រូវបានប្រើក្នុងមេរៀននេះ ។ ការលោតទំព័របន្ទាប់នឹងត្រូវបានប្រើជំនួសវិញ ។ ចូលជាសិស្សដើម្បីសាកល្បងការលោតទាំងនេះ ។", - "addon.mod_lesson.teacherongoingwarning": "ពិន្ទុដែលកំពុងកើតមានត្រូវបានបង្ហាញតែសម្រាប់សិស្សប៉ុណ្ណោះ ។ ចូលជាសិស្សដើម្បីសាកល្បងពិន្ទុដែលកំពុងកើតឡើង", - "addon.mod_lesson.teachertimerwarning": "ឧបករណ៍កំណត់ពេលគឺធ្វើការតែសម្រាប់សិស្សប៉ុណ្ណោះ ។ សាកល្បងឧបករណ៍កំណត់ពេលដោយចូលជាសិស្ស ។", - "addon.mod_lesson.thatsthecorrectanswer": "នោះជាចម្លើយត្រឹមត្រូវ", - "addon.mod_lesson.thatsthewronganswer": "នោះជាចម្លើយមិនត្រឹមត្រូវ", - "addon.mod_lesson.timeremaining": "ពេលវេលានៅសល់", - "addon.mod_lesson.timetaken": "ពេលវេលាដែលជ្រើស", - "addon.mod_lesson.unseenpageinbranch": "សំណួរដែលមិនឃើញនៅក្នុងមែកធាង", - "addon.mod_lesson.warningretakefinished": "ការព្យាយាមគឺបានបញ្ចប់នៅក្នុងគេហទំព័រ", - "addon.mod_lesson.welldone": "ធ្វើបានល្អណាស់ !", - "addon.mod_lesson.youhaveseen": "អ្នកបានឃើញទំព័រមេរៀននេះច្រើនរួចហើយ ។
                តើអ្នកចង់ចាប់ផ្ដើមនៅទំព័រចុងក្រោយដែលអ្នកបានឃើញឬ ?", - "addon.mod_lesson.youranswer": "ចម្លើយរបស់អ្នក", - "addon.mod_lesson.yourcurrentgradeisoutof": "ពិន្ទុបច្ចុប្បន្នរបស់អ្នកគឺ {{$a.grade}} នៃ {{$a.total}}", - "addon.mod_lesson.youshouldview": "អ្នកគួរតែឆ្លើយយ៉ាងហោច ៖ {{$a}}", - "addon.mod_lti.errorgetlti": "មានកំហុសក្នុងការទាញយកទិន្នន័យម៉ូឌុល", - "addon.mod_lti.errorinvalidlaunchurl": "URL នេះមិនត្រឹមត្រូវ", - "addon.mod_lti.launchactivity": "បង្ហាញសកម្មភាព", - "addon.mod_lti.modulenameplural": "ឧបករណ៍ខាងក្រៅ", - "addon.mod_page.errorwhileloadingthepage": "មានកំហុសក្នុងការទាញយកខ្លឹមសារមកលើទំព័រនេះ", - "addon.mod_page.modulenameplural": "ទំព័រ", - "addon.mod_quiz.answercolon": "ចម្លើយ៖", - "addon.mod_quiz.attemptfirst": "ការឆ្លើយលើកទីមួយ", - "addon.mod_quiz.attemptlast": "ការឆ្លើយចុងក្រោយ", - "addon.mod_quiz.attemptnumber": "ការសាកល្បង", - "addon.mod_quiz.attemptquiznow": "ឆ្លើយកម្រងសំណួរឥឡូវ", - "addon.mod_quiz.attemptstate": "សភាព", - "addon.mod_quiz.cannotsubmitquizdueto": "ការឆ្លើយសំណួរនេះមិនអាចបញ្ជូនទៅដោយហេតុផលខាងក្រោម៖", - "addon.mod_quiz.comment": "មតិយោបល់", - "addon.mod_quiz.completedon": "បានបញ្ចប់នៅ", - "addon.mod_quiz.confirmclose": "អ្នករៀបនឹងបញ្ចប់ការឆ្លើយនេះហើយ ។ នៅពេលដែលអ្នកបញ្ចប់ការឆ្លើយ អ្នកនឹងមិនអាចផ្លាស់ប្ដូរចម្លើយរបស់អ្នកបានទេ ។", - "addon.mod_quiz.confirmcontinueoffline": "ការឆ្លើយនេះមិនត្រូវបានធ្វើសមកាលកម្ម តាំងពី {{$a}} ។ ប្រសិនជាអ្នកបានបន្តឆ្លើយនៅក្នុងឧបករណ៏ផ្សេងទៀត នោះ អ្នកប្រហែលនឹងបាត់បង់ទិន្នន័យនោះ។", - "addon.mod_quiz.confirmleavequizonerror": "មានកំហុសមួយអំឡុងពេលអ្នកកំពុងរក្សាទុកចម្លើយ ។ តើអ្នកប្រាកដឬថាអ្នកចង់ចេញពីសំណួរនេះឬទេ?", - "addon.mod_quiz.connectionerror": "បានដាច់តំណភ្ជាប់បណ្តាញ។ (ការរក្សាទុកដោយស្វ័យប្រវត្តិបានបរាជ័យ)។\n\nចូរកត់ចំណាំនូវចម្លើយដែលបានបញ្ចូលក្នុងទំព័រនេះកាលពីពេលពីរ បីនាទីមុន។ បន្ទាប់ព្យាយាមតភ្ជាប់ម្តងទៀត។\n\nខណៈដែលការតភ្ជាប់បណ្តាញបានត្រឡប់មកវិញ នោះចម្លើយរបស់អ្នកគួរតែត្រូវបានរក្សាទុក ហើយសារនេះនឹងបាត់។", - "addon.mod_quiz.continueattemptquiz": "បន្តការឆ្លើយចុងក្រោយ", - "addon.mod_quiz.continuepreview": "បន្តការមើលជាមុនចុងក្រោយ", - "addon.mod_quiz.errorbehaviournotsupported": "សំណួរនេះគឺមិនអាចឆ្លើយបាននៅក្នុងកម្មវិធីនេះទេ ព្រោះតែសំណួរបែបនេះគឺមិនត្រូវបានគាំទ្រដោយកម្មវិធី", - "addon.mod_quiz.errordownloading": "មានកំហុសក្នុងការ ទាញយកទិន្នន័យដែលត្រូវការ", - "addon.mod_quiz.errorgetattempt": "មានកំហុស ក្នុងការទាញយកទិន្នន័យដែលឆ្លើយ", - "addon.mod_quiz.errorgetquestions": "មានកំហុស ក្នុងការទាញយកសំណួរ", - "addon.mod_quiz.errorgetquiz": "មានកំហុស ក្នុងការទាញយកទិន្នន័យសំណួរ", - "addon.mod_quiz.errorparsequestions": "មានកំហុសមួយអំឡុងពេលអ្នកកំពុងអានសំណួរ។ សូម ឆ្លើយសំណួរនេះនៅក្នុងកម្មវិធីរុកវិញ", - "addon.mod_quiz.errorquestionsnotsupported": "សំណួរនេះគឺមិនអាចឆ្លើយបាននៅក្នុងកម្មវិធីនេះទេ ព្រោះតែមានសំណួរដែលមិនត្រូវបានគាំទ្រដោយកម្មវិធី", - "addon.mod_quiz.errorrulesnotsupported": "សំណួរនេះគឺមិនអាចឆ្លើយបាននៅក្នុង កម្មវិធីនេះទេ ព្រោះតែសិទ្ធិក្នុងការប្រើប្រាស់នេះ មិនត្រូវបានគាំទ្រដោយកម្មវិធី។", - "addon.mod_quiz.errorsaveattempt": "មានកំហុស អំឡុងពេលរក្សាទុកទិន្នន័យនៃចម្លើយ", - "addon.mod_quiz.feedback": "មតិយោបល់", - "addon.mod_quiz.finishattemptdots": "បញ្ចប់ការសាកល្បង...", - "addon.mod_quiz.finishnotsynced": "បានបញ្ចប់ប៉ុន្តែមិនបានធ្វើសមកាលកម្មទេ", - "addon.mod_quiz.grade": "ដាក់ពិន្ទុ", - "addon.mod_quiz.gradeaverage": "ពិន្ទុមធ្យម", - "addon.mod_quiz.gradehighest": "ពិន្ទុខ្ពស់បំផុត", - "addon.mod_quiz.grademethod": "វិធីសាស្ដ្រដាក់ពិន្ទុ", - "addon.mod_quiz.gradesofar": "{{$a.method}} ៖ {{$a.mygrade}} / {{$a.quizgrade}} ។", - "addon.mod_quiz.marks": "ពិន្ទុ", - "addon.mod_quiz.modulenameplural": "កម្រងសំណួរ", - "addon.mod_quiz.mustbesubmittedby": "ការសាកល្បងនេះត្រូវតែប្រគល់ត្រឹម {{$a}}។", - "addon.mod_quiz.noquestions": "គ្មានសំណួរត្រូវបានបន្ថែមនៅឡើយទេ", - "addon.mod_quiz.noreviewattempt": "អ្នកមិនត្រូវបានអនុញ្ញាតឲ្យពិនិត្យការសាកល្បងនេះឡើងវិញឡើយ។", - "addon.mod_quiz.notyetgraded": "មិនទាន់បានដាក់ពិន្ទុ", - "addon.mod_quiz.opentoc": "បើកម៉ឺនុយរុករកដែលលេចចេញមក", - "addon.mod_quiz.outof": "{{$a.grade}} លើពិន្ទុអតិបរមានៃ {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} លើពិន្ទុអតិបរមានៃ {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "មតិយោបល់ទូទៅ", - "addon.mod_quiz.overdue": "ហួសពេលកំណត់", - "addon.mod_quiz.overduemustbesubmittedby": "ការសាកល្បងនេះហួសពេលកំណត់ហើយ។ ការពិតទៅ អ្នកគួរតែបានប្រគល់ចម្លើយឲ្យរួចរាល់។ ប្រសិនបើអ្នកចង់បានពិន្ទុសម្រាប់កម្រងសំណួរនេះ អ្នកត្រូវប្រគល់វាឲ្យបានត្រឹម {{$a}}។ ប្រសិនបើអ្នកមិនប្រគល់វាឲ្យបានត្រឹមពេលនេះទេ នោះពិន្ទុពីការសាកល្បងនេះនឹងត្រូវចាត់ទុកជាមោឃៈ។", - "addon.mod_quiz.preview": "មើលជាមុន", - "addon.mod_quiz.previewquiznow": "មើលសំណួរជាមុនក្នុងពេលឥឡូវ", - "addon.mod_quiz.question": "សំណួរ", - "addon.mod_quiz.quiznavigation": "ការផ្លាស់ទីក្នុងកម្រងសំណួរ", - "addon.mod_quiz.reattemptquiz": "ឆ្លើយកម្រងសំណួរម្ដងទៀត", - "addon.mod_quiz.requirepasswordmessage": "ដើម្បីឆ្លើយកម្រងសំណួរនេះ អ្នកត្រូវដឹងពាក្យសម្ងាត់របស់កម្រងសំណួរ", - "addon.mod_quiz.returnattempt": "ត្រឡប់ទៅការសាកល្បងវិញ", - "addon.mod_quiz.review": "ពិនិត្យឡើងវិញ", - "addon.mod_quiz.reviewofattempt": "ពិនិត្យមើលចម្លើយឡើងវិញ {{$a}}", - "addon.mod_quiz.reviewofpreview": "ពិនិត្យការមើលជាមុនឡើងវិញ", - "addon.mod_quiz.showall": "បង្ហាញសំណួរទាំងអស់នៅលើមួយទំព័រ", - "addon.mod_quiz.showeachpage": "បង្ហាញមួយទំព័រម្តង", - "addon.mod_quiz.startattempt": "ចាប់ផ្តើមការសាកល្បង", - "addon.mod_quiz.startedon": "បានចាប់ផ្ដើមនៅ", - "addon.mod_quiz.stateabandoned": "មិនដែលបានប្រគល់", - "addon.mod_quiz.statefinished": "បានបញ្ចប់", - "addon.mod_quiz.statefinisheddetails": "បានប្រគល់ {{$a}}", - "addon.mod_quiz.stateinprogress": "កំពុងដំណើរការ", - "addon.mod_quiz.stateoverdue": "ហួសកំណត់", - "addon.mod_quiz.stateoverduedetails": "ត្រូវប្រគល់ត្រឹមថ្ងៃ {{$a}}", - "addon.mod_quiz.status": "ស្ថានភាព", - "addon.mod_quiz.submitallandfinish": "ដាក់ស្នើទាំងអស់ ហើយបញ្ចប់", - "addon.mod_quiz.summaryofattempt": "សេចក្តីសង្ខេបនៃការសាកល្បង", - "addon.mod_quiz.summaryofattempts": "សង្ខេបនៃចម្លើយមុនរបស់អ្នក", - "addon.mod_quiz.timeleft": "ពេលវេលានៅសល់", - "addon.mod_quiz.timetaken": "ពេលវេលាចំណាយ", - "addon.mod_quiz.warningattemptfinished": "កាឆ្លើយក្រៅបណ្ដាញត្រូវបានបោះបង់ចោលនៅពេលវា បានបញ្ចប់នៅលើគេហទំព័រ ឬមិនត្រូវបានរកឃើញ", - "addon.mod_quiz.warningdatadiscarded": "ចម្លើយក្រៅបណ្តាញមួយចំនួនត្រូវបានបោះបង់ដោយសារតែសំណួរត្រូវបានកែប្រែលើបណ្តាញ", - "addon.mod_quiz.warningdatadiscardedfromfinished": "ការឆ្លើយមិនទាន់បញ្ចប់ដោយសារតែចម្លើយក្រៅបណ្ដាញមួយចំនួនត្រូវបានបោះបង់។ សូមពិនិត្យឡើងវិញនូវចម្លើយរបស់អ្នកហើយស្នើសុំឡើងវិញ", - "addon.mod_quiz.yourfinalgradeis": "ពិន្ទុចុងក្រោយរបស់អ្នកសម្រាប់កម្រងសំណួរនេះគឺ {{$a}} ។", - "addon.mod_resource.errorwhileloadingthecontent": "មានកំហុសនៅអំឡុងពេលទាញយកអត្ថបទ", - "addon.mod_resource.modifieddate": "បានកែប្រែ {{$a}}", - "addon.mod_resource.modulenameplural": "ធនធាន", - "addon.mod_resource.openthefile": "បើកឯកសារ", - "addon.mod_resource.uploadeddate": "បានផ្ទុកឡើង {{$a}}", - "addon.mod_scorm.asset": "ធនធាន", - "addon.mod_scorm.assetlaunched": "ធនធាន - បានមើល", - "addon.mod_scorm.attempts": "ឆ្លើយ", - "addon.mod_scorm.averageattempt": "ការឆ្លើយជាមធ្យម", - "addon.mod_scorm.browse": "មើលជាមុន", - "addon.mod_scorm.browsed": "បានរកមើល", - "addon.mod_scorm.browsemode": "របៀបមើលជាមុន", - "addon.mod_scorm.cannotcalculategrade": "ពិន្ទុមិនត្រូវបានគណនា", - "addon.mod_scorm.completed": "បានបញ្ចប់", - "addon.mod_scorm.contents": "មាតិកា", - "addon.mod_scorm.dataattemptshown": "ទិន្នន័យនេះគឺបានឆ្លើយរួចនៅ លេខ {{number}}", - "addon.mod_scorm.enter": "ចូល", - "addon.mod_scorm.errorcreateofflineattempt": "មានកំហុសអំឡុងពេលបង្កើតការឆ្លើយក្រៅបណ្តាញ។ សូមព្យាយាមម្ដងទៀត", - "addon.mod_scorm.errordownloadscorm": "មានកំហុសពេលកំពុងទាញយក SCORM: \"{{name}}\"", - "addon.mod_scorm.errorgetscorm": "មានកំហុសពេលកំពុងទទួលយក SCORM", - "addon.mod_scorm.errorinvalidversion": "សូមអភ័យទោស កម្មវិធីប្រើបានតែ SCORM 1.2", - "addon.mod_scorm.errornotdownloadable": "ការទាញយកកញ្ចប់ SCORM គឺត្រូវបានបិទ សូមទាក់ទងទៅកាន់ អ្នកគ្រប់គ្រង គេហទំព័ររបស់អ្នក", - "addon.mod_scorm.errornovalidsco": "កញ្ចប់ SCORM នេះ មិនអាចមើលឃើញ SCOទេ", - "addon.mod_scorm.errorpackagefile": "សូមអភ័យទោស កម្មវិធី នេះប្រើបានតែ កញ្ចប់ ZIP តែប៉ុណ្ណោះ", - "addon.mod_scorm.errorsyncscorm": "មានកំហុសមួយអំឡុងពេលធ្វើសមកាលកម្ម។ ទិន្ន សូមព្យាយាមម្ដងទៀត", - "addon.mod_scorm.failed": "បានបរាជ័យ", - "addon.mod_scorm.firstattempt": "ការឆ្លើយលើកទីមួយ", - "addon.mod_scorm.gradeaverage": "ពិន្ទុមធ្យម", - "addon.mod_scorm.gradehighest": "ពិន្ទុខ្ពស់បំផុត", - "addon.mod_scorm.grademethod": "វិធីសាស្ដ្រក្នុងការដាក់ពិន្ទុ", - "addon.mod_scorm.gradescoes": "គោលបំណងសិក្សា", - "addon.mod_scorm.gradesum": "បូកពិន្ទុ", - "addon.mod_scorm.highestattempt": "ការឆ្លើយខ្ពស់បំផុត", - "addon.mod_scorm.incomplete": "មិនពេញលេញ", - "addon.mod_scorm.lastattempt": "ការឆ្លើយចុងក្រោយ", - "addon.mod_scorm.modulenameplural": "SCORMs/AICCs", - "addon.mod_scorm.newattempt": "ចាប់ផ្ដើមការឆ្លើយថ្មី", - "addon.mod_scorm.noattemptsallowed": "ចំនួនលើកដែលអនុញ្ញាតឲ្យឆ្លើយ", - "addon.mod_scorm.notattempted": "មិនបានឆ្លើយ", - "addon.mod_scorm.offlineattemptnote": "ការឆ្លើយនេះមិនទាន់ត្រូវបានធ្វើសមកាលកម្មទេ", - "addon.mod_scorm.offlineattemptovermax": "ការឆ្លើយនេះមិនអាចផ្ញើចេញទេ ព្រោះការឆ្លើយលើសចំនួន កំណត់", - "addon.mod_scorm.organizations": "ការរៀបចំ", - "addon.mod_scorm.passed": "បានហុច", - "addon.mod_scorm.reviewmode": "របៀបពិនិត្យឡើងវិញ", - "addon.mod_scorm.score": "ពិន្ទុ", - "addon.mod_scorm.scormstatusnotdownloaded": "កញ្ចប់ SCORM នេះគឺមិនត្រុវបានទាញយក។ វានឹងទាញយកដោយស្វ័យប្រវត្តិនៅពេលដែលអ្នកបើកវា", - "addon.mod_scorm.scormstatusoutdated": "កញ្ចប់ SCORM គឺត្រូវបានបញ្ចាក់តាំងពីកាទាញយកលើកចុងក្រោយ។ វានឹងទាញយកដោយស្វ័យប្រវត្តនៅពេលដែលអោយបើវា", - "addon.mod_scorm.suspended": "បានផ្អាក", - "addon.mod_scorm.warningofflinedatadeleted": "ទិន្នន័យនៃចម្លើយក្រៅបណ្តាញចំនួន {{number}} ត្រូវបានបោះបង់ព្រោះវាមិនអាចរាប់ជាចម្លើយ ថ្មីនោះទេ", - "addon.mod_scorm.warningsynconlineincomplete": "ចម្លើយមួយចំនួនមិនអាចធ្វើសមកាលកម្មនៅលើគេហទំព័រនេះទេពីព្រោះចម្លើយចុងក្រោយមិនទាន់បានបញ្ចប់។ សូមបញ្ចប់ការឆ្លើយនេះជាមុនសិន។", - "addon.mod_survey.cannotsubmitsurvey": "សូមទោស មានបញ្ហាក្នុងបញ្ចូនចម្លើយនៃស្ទង់មតិរបស់អ្នក។ សូមព្យាយាមម្តងទៀត។", - "addon.mod_survey.errorgetsurvey": "មានកំហុសក្នុងការទទួលទិន្នន័យស្ទង់មតិ។", - "addon.mod_survey.ifoundthat": "ខ្ញុំយល់ថា", - "addon.mod_survey.ipreferthat": "ខ្ញុំចូលចិត្តដូចនោះ", - "addon.mod_survey.modulenameplural": "ការស្ទង់មតិ", - "addon.mod_survey.responses": "ចម្លើយ", - "addon.mod_survey.results": "លទ្ធផល", - "addon.mod_survey.surveycompletednograph": "អ្នកបានបញ្ចប់ការស្ទង់មតិនេះ។", - "addon.mod_url.accessurl": "ចូលប្រើURL", - "addon.mod_url.modulenameplural": "តំណ URL", - "addon.mod_url.pointingtourl": "URLដែលធនធានចង្អុលទៅ", - "addon.mod_wiki.cannoteditpage": "អ្នកមិនអាចកែសម្រួលទំព័រនេះទេ។", - "addon.mod_wiki.createpage": "បង្កើតទំព័រ", - "addon.mod_wiki.editingpage": "កែសម្រួលទំព័រ '{{$a}}' នេះ", - "addon.mod_wiki.errorloadingpage": "មានកំហុសមួយបានកើតឡើងនៅពេលបង្ហាញគេហទំព័រ", - "addon.mod_wiki.errornowikiavailable": "វីគីនេះមិនទាន់មានខ្លឹមសារនៅឡើយទេ។", - "addon.mod_wiki.gowikihome": "ទៅទំព័រដំបូងនៃវីគី", - "addon.mod_wiki.map": "ផែនទី", - "addon.mod_wiki.modulenameplural": "វិគី", - "addon.mod_wiki.newpagehdr": "ទំព័រថ្មី", - "addon.mod_wiki.newpagetitle": "ចំណងជើងទំព័រថ្មី", - "addon.mod_wiki.nocontent": "មិនមានមាតិកាសម្រាប់ទំព័រនេះទេ", - "addon.mod_wiki.notingroup": "មិននៅក្នុងក្រុម", - "addon.mod_wiki.pageexists": "ទំព័រនេះមានរួចហើយ។", - "addon.mod_wiki.pagename": "ឈ្មោះទំព័រ", - "addon.mod_wiki.subwiki": "ទំព័ររងរបស់វីគី", - "addon.mod_wiki.tagarea_wiki_pages": "ទំព័រវីគី", - "addon.mod_wiki.titleshouldnotbeempty": "ចំណងជើងត្រូវមាន", - "addon.mod_wiki.viewpage": "មើលទំព័រ", - "addon.mod_wiki.wikipage": "ទំព័រវីគី", - "addon.mod_wiki.wrongversionlock": "អ្នកប្រើផ្សេងទៀតបានកែសម្រួលទំព័រនេះខណៈដែលអ្នកកំពុងកែសម្រួល ហើយមាតិការបស់អ្នកហួសសម័យ។", - "addon.mod_workshop.alreadygraded": "បានដាក់ពិន្ទុហើយ", - "addon.mod_workshop.areainstructauthors": "សេចក្តីណែនាំសម្រាប់ការប្រគល់កិច្ចការ", - "addon.mod_workshop.areainstructreviewers": "សេចក្តីណែនាំសម្រាប់ការវាយតម្លៃ", - "addon.mod_workshop.assess": "វាយតម្លៃ", - "addon.mod_workshop.assessmentform": "ទម្រង់បែបបទសម្រាប់ការវាយតម្លៃ", - "addon.mod_workshop.assessmentsettings": "ការកំណត់សម្រាប់ការវាយតម្លៃ", - "addon.mod_workshop.assessmentstrategynotsupported": "យុទ្ធសាស្រ្ដវាយតម្លៃ {{$a}} មិនគាំទ្រ", - "addon.mod_workshop.assignedassessments": "កិច្ចការដែលបានផ្តល់ឲ្យវាយតម្លៃ", - "addon.mod_workshop.assignedassessmentsnone": "អ្នកមិនមានកិច្ចការដែលបានផ្តល់ឲ្យវាយតម្លៃ", - "addon.mod_workshop.conclusion": "សេចក្តីសន្និដ្ឋាន", - "addon.mod_workshop.createsubmission": "ចាប់ផ្តើមរៀបចំកិច្ចការរបស់អ្នក", - "addon.mod_workshop.deletesubmission": "លុបកិច្ចការ", - "addon.mod_workshop.editsubmission": "កែសម្រួលការដាក់ស្នើ", - "addon.mod_workshop.feedbackauthor": "មូលវិចារណ៍សម្រាប់ម្ចាស់កិច្ចការ", - "addon.mod_workshop.feedbackby": "មូលវិចារណ៍ដោយ {{$a}}", - "addon.mod_workshop.feedbackreviewer": "មូលវិចារណ៍សម្រាប់អ្នកវាយតម្លៃ", - "addon.mod_workshop.givengrades": "ពិន្ទុដែលបានឲ្យ", - "addon.mod_workshop.gradecalculated": "ពិន្ទុដែលបានគណនាសម្រាប់កិច្ចការ", - "addon.mod_workshop.gradeinfo": "ពិន្ទុ៖ {{$a.received}} លើ {{$a.max}}", - "addon.mod_workshop.gradeover": "ជំនួសពិន្ទុសម្រាប់កិច្ចការ", - "addon.mod_workshop.gradesreport": "របាយការណ៍ពិន្ទុសម្រាប់សិក្ខាសិលា", - "addon.mod_workshop.gradinggrade": "ដាក់ពិន្ទុ", - "addon.mod_workshop.gradinggradecalculated": "ពិន្ទុដែលបានគណនាសម្រាប់ការវាយតម្លៃ", - "addon.mod_workshop.gradinggradeof": "ពិន្ទុសម្រាប់ការវាយតម្លៃ (លើ {{$a}})", - "addon.mod_workshop.gradinggradeover": "ជំនួសពិន្ទុសម្រាប់ការវាយតម្លៃ", - "addon.mod_workshop.modulenameplural": "សិក្ខាសាលា", - "addon.mod_workshop.nogradeyet": "មិនទាន់មានពិន្ទុ", - "addon.mod_workshop.notassessed": "មិនទាន់បានវាយតម្លៃទេ", - "addon.mod_workshop.notoverridden": "មិនត្រូវបានជំនួស", - "addon.mod_workshop.noyoursubmission": "អ្នកមិនទាន់បានប្រគល់កិច្ចការរបស់អ្នកនៅឡើយទេ", - "addon.mod_workshop.overallfeedback": "មូលវិចារណ៍ទូទៅ", - "addon.mod_workshop.publishedsubmissions": "កិច្ចការដែលបានប្រកាស", - "addon.mod_workshop.publishsubmission": "ប្រកាសកិច្ចការ", - "addon.mod_workshop.publishsubmission_help": "កិច្ចការដែលបានប្រកាសនឹងអាចមើលបានដោយអ្នកដទៃនៅពេលសិក្ខាសិលាបានបិទ។", - "addon.mod_workshop.reassess": "វាយតម្លៃឡើងវិញ", - "addon.mod_workshop.receivedgrades": "ពិន្ទុទទួលបាន", - "addon.mod_workshop.submissionattachment": "ឯកសារភ្ជាប់", - "addon.mod_workshop.submissioncontent": "មាតិកានៃកិច្ចការ", - "addon.mod_workshop.submissiondeleteconfirm": "តើអ្នកពិតជាចង់លុបកិច្ចការខាងក្រោមមែនទេ?", - "addon.mod_workshop.submissiongrade": "ពិន្ទុសម្រាប់កិច្ចការ", - "addon.mod_workshop.submissiongradeof": "ពិន្ទុសម្រាប់កិច្ចការ (លើ {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "អ្នកត្រូវបញ្ចូលអត្ថបទ ឬបន្ថែមឯកសារមួយ។", - "addon.mod_workshop.submissionrequiredtitle": "អ្នកត្រូវតែបំពេញចំណងជើង", - "addon.mod_workshop.submissionsreport": "របាយការណ៍ប្រគល់កិច្ចការក្នុងសិក្ខាសាលា", - "addon.mod_workshop.submissiontitle": "ចំណងជើង", - "addon.mod_workshop.switchphase10": "ប្តូរទៅដំណាក់កាលរៀបចំ", - "addon.mod_workshop.switchphase20": "ប្តូរទៅដំណាក់កាលប្រគល់កិច្ចការ", - "addon.mod_workshop.switchphase30": "ប្ដូរទៅដំណាក់កាលវាយតម្លៃ", - "addon.mod_workshop.switchphase40": "ប្តូរទៅដំណាក់កាលដាក់ពិន្ទុការវាយតម្លៃ", - "addon.mod_workshop.switchphase50": "បិទសិក្ខាសាលា", - "addon.mod_workshop.userplan": "ឧបករណ៍រៀបចំសិក្ខាសាលា", - "addon.mod_workshop.userplancurrentphase": "ដំណាក់កាលបច្ចុប្បន្ន", - "addon.mod_workshop.warningassessmentmodified": "ការដាក់ស្នើត្រូវបានកែប្រែនៅលើគេហទំព័រ", - "addon.mod_workshop.warningsubmissionmodified": "ការវាយតម្លៃត្រូវបានកែប្រែនៅលើគេហទំព័រ", - "addon.mod_workshop.weightinfo": "ទម្ងន់៖ {{$a}}", - "addon.mod_workshop.yourassessment": "ការវាយតម្លៃរបស់អ្នក", - "addon.mod_workshop.yourassessmentfor": "ការវាយតម្លៃរបស់អ្នកសម្រាប់ {{$a}}", - "addon.mod_workshop.yourgrades": "ពិន្ទុរបស់អ្នក", - "addon.mod_workshop.yoursubmission": "កិច្ចការរបស់អ្នក", - "addon.notes.addnewnote": "បន្ថែមចំណាំថ្មី", - "addon.notes.coursenotes": "ចំណាំនៃវគ្គសិក្សា", - "addon.notes.deleteconfirm": "លុបចំណាំនេះឬ ?", - "addon.notes.nonotes": "គ្មានចំណាំទេ ។", - "addon.notes.note": "ចំណាំ", - "addon.notes.notes": "ចំណាំ", - "addon.notes.personalnotes": "ចំណាំផ្ទាល់ខ្លួន", - "addon.notes.publishstate": "ស្ថានភាព", - "addon.notes.sitenotes": "ចំណាំនៃតំបន់បណ្ដាញ", - "addon.notes.userwithid": "អ្នកប្រើដែលមានលេខសម្គាល់ {{id}}", - "addon.notes.warningnotenotsent": "មិនអាចបន្ថែមការកត់ចំណាំលើវគ្គសិក្សា {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "មានកំហុសក្នុងការទទួលការជូនដំណឹង", - "addon.notifications.markallread": "ដាក់សម្គាល់ទាំងអស់ថាបានអាន", - "addon.notifications.notificationpreferences": "ការកំណត់ផ្ទាល់ខ្លួនសម្រាប់ដំណឹង", - "addon.notifications.notifications": "ការជូនដំណឹង", - "addon.notifications.playsound": "សំឡេង", - "addon.notifications.therearentnotificationsyet": "គ្មានការជូនដំណឹង", - "assets.countries.AD": "អង់ដូរ៉ា", - "assets.countries.AE": "អារ៉ាប់រួម", - "assets.countries.AF": "អាហ្គានីស្ថាន", - "assets.countries.AG": "អង់ទីហ្គា និងបារប៊ុយដា", - "assets.countries.AI": "អង់ហ្ស៊ីឡា", - "assets.countries.AL": "អាល់បានី", - "assets.countries.AM": "អាមេនី", - "assets.countries.AO": "អង់ហ្គោឡា", - "assets.countries.AQ": "អង់តាក់ទិក", - "assets.countries.AR": "អាហ្សង់ទីន", - "assets.countries.AS": "សាមូអា អាមេរិក", - "assets.countries.AT": "អូទ្រីស", - "assets.countries.AU": "អូស្ត្រាលី", - "assets.countries.AW": "អារូបា", - "assets.countries.AX": "កោះអាឡាំង", - "assets.countries.AZ": "អាហ្ស៊ែរបែហ្សង់", - "assets.countries.BA": "បូស្ន៊ី និងហឺហ្ស៊េហ្គោវីណា", - "assets.countries.BB": "បារបាដូស", - "assets.countries.BD": "បង់ក្លាដេស", - "assets.countries.BE": "ប៊ែលហ្ស៊ិក", - "assets.countries.BF": "ប៊ូរគីណាហ្វាសូ", - "assets.countries.BG": "ប៊ុលហ្គារី", - "assets.countries.BH": "បារ៉ែន", - "assets.countries.BI": "ប៊ូរុនឌី", - "assets.countries.BJ": "បេណាំង", - "assets.countries.BL": "សង់បាថេឡេមី", - "assets.countries.BM": "ប៊េរមូដា", - "assets.countries.BN": "ប្រ៊ុយណេ", - "assets.countries.BO": "បូលីវី", - "assets.countries.BQ": "ប៊ូរី, ស៊ីនអ៊ូស្តាតសនិងសាបា", - "assets.countries.BR": "ប្រេស៊ីល", - "assets.countries.BS": "បាហាម៉ាស", - "assets.countries.BT": "ប៊ូតាន", - "assets.countries.BV": "កោះប៊ូវ៉ែត", - "assets.countries.BW": "បុតស្វាណា", - "assets.countries.BY": "បេឡារុស្ស", - "assets.countries.BZ": "បេលីហ្ស", - "assets.countries.CA": "កាណាដា", - "assets.countries.CC": "កោះកូកូស (Keeling)", - "assets.countries.CD": "កុងហ្គោ សាធារណៈរដ្ឋប្រជាធិប្បតេយនៃ", - "assets.countries.CF": "សាធារណរដ្ឋអាហ្វ្រិកកណ្ដាល", - "assets.countries.CG": "កុងហ្គោ", - "assets.countries.CH": "ស្វ៊ីស", - "assets.countries.CI": "កូតេឌីវ័រ", - "assets.countries.CK": "កោះឃុគ", - "assets.countries.CL": "ឈីលី", - "assets.countries.CM": "កាមេរ៉ូន", - "assets.countries.CN": "ចិន", - "assets.countries.CO": "កូឡុំប៊ី", - "assets.countries.CR": "កូស្តារីកា", - "assets.countries.CU": "គុយប៉ា", - "assets.countries.CV": "កាបូវែដេ", - "assets.countries.CW": "កូរ៉ាវៅ", - "assets.countries.CX": "កោះគ្រីស្តម៉ាស", - "assets.countries.CY": "ស៊ីពរ៍", - "assets.countries.CZ": "ឆែក", - "assets.countries.DE": "អាល្លឺម៉ង់", - "assets.countries.DJ": "ហ្ស៊ីបូទី", - "assets.countries.DK": "ដាណឺម៉ាក", - "assets.countries.DM": "ដូមីនីកា", - "assets.countries.DO": "សាធារណរដ្ឋដូមីនីកែន", - "assets.countries.DZ": "អាល់ហ្សេរី", - "assets.countries.EC": "អេក្វាឌ័រ", - "assets.countries.EE": "អេស្តូនី", - "assets.countries.EG": "អេហ្ស៊ីប", - "assets.countries.EH": "សាហារ៉ាខាងលិច", - "assets.countries.ER": "អេរីទ្រា", - "assets.countries.ES": "អេស្ប៉ាញ", - "assets.countries.ET": "អេត្យូពី", - "assets.countries.FI": "ហ្វាំងឡង់", - "assets.countries.FJ": "ហ្វីហ្ស៊ី", - "assets.countries.FK": "កោះហ្វ៉កឡង់ (ម៉ាល់វីណា)", - "assets.countries.FM": "មីក្រូនេស៊ី (រដ្ឋសហព័ន្ធ)", - "assets.countries.FO": "កោះហ្វារ៉ូ", - "assets.countries.FR": "បារាំង", - "assets.countries.GA": "ហ្គាបុង", - "assets.countries.GB": "ចក្រភពអង់គ្លេស", - "assets.countries.GD": "ហ្គ្រីណាដា", - "assets.countries.GE": "ហ្សកហ្ស៊ី", - "assets.countries.GF": "បារាំង ហ្គូអ៊ីយ៉ាណា", - "assets.countries.GG": "ហ្គើនសី", - "assets.countries.GH": "ហ្គាណា", - "assets.countries.GI": "ជីប្រាលតា", - "assets.countries.GL": "ហ្គ្រីនលែន", - "assets.countries.GM": "ហ្គាំប៊ី", - "assets.countries.GN": "ហ្គីណេ", - "assets.countries.GP": "ហ្គាដឺលូប", - "assets.countries.GQ": "ហ្គីណេអេក្វាទ័រ", - "assets.countries.GR": "ក្រិក", - "assets.countries.GS": "ហ្សកហ្ស៊ីខាងត្បូង និងកោះសង់វ៉ិចខាងត្បូង", - "assets.countries.GT": "ហ្គាតេម៉ាឡា", - "assets.countries.GU": "ហ្គាម", - "assets.countries.GW": "ហ្គីណេប៊ីសៅ", - "assets.countries.GY": "ហ្គីយ៉ាណា", - "assets.countries.HK": "ហុងកុង", - "assets.countries.HM": "កោះហាដ និងម៉ាកដូណាល់", - "assets.countries.HN": "ហុងឌូរ៉ាស់", - "assets.countries.HR": "ក្រូអាត (Hrvatska)", - "assets.countries.HT": "ហៃទី", - "assets.countries.HU": "ហុងគ្រី", - "assets.countries.ID": "ឥណ្ឌូនេស៊ី", - "assets.countries.IE": "អៀរឡង់", - "assets.countries.IL": "អ៊ីស្រាអែល", - "assets.countries.IM": "កោះមនុស្ស", - "assets.countries.IN": "ឥណ្ឌា", - "assets.countries.IO": "អាណាចក្រមហាសមុទ្រឥណ្ឌា ចក្រភពអង់គ្លេស", - "assets.countries.IQ": "អ៊ីរ៉ាក់", - "assets.countries.IR": "អ៊ីរ៉ង់ (សាធារណរដ្ឋអ៊ីស្លាម)", - "assets.countries.IS": "អ៊ីស្លង់", - "assets.countries.IT": "អ៊ីតាលី", - "assets.countries.JE": "ជើសី", - "assets.countries.JM": "ហ្សាម៉ាអ៊ិគ", - "assets.countries.JO": "ហ្ស៊កដង់", - "assets.countries.JP": "ជប៉ុន", - "assets.countries.KE": "កេនយ៉ា", - "assets.countries.KG": "គៀរហ្គីស្តង់", - "assets.countries.KH": "កម្ពុជា", - "assets.countries.KI": "គិរិបាទី", - "assets.countries.KM": "កុំម៉ូរ៉ូស", - "assets.countries.KN": "សង់ឃីត និងនេវីស", - "assets.countries.KP": "កូរ៉េ (សាធារណរដ្ឋប្រជាមានិតប្រជាធិបតេយ្យ)", - "assets.countries.KR": "កូរ៉េ (សាធារណរដ្ឋ)", - "assets.countries.KW": "គុយវ៉ែត", - "assets.countries.KY": "កោះកៃម៉ាន", - "assets.countries.KZ": "កាហ្សាក់ស្តង់", - "assets.countries.LA": "សាធារណរដ្ឋប្រជាមានិតឡាវ", - "assets.countries.LB": "លីបង់", - "assets.countries.LC": "សង់លូស៊ីយ៉ា", - "assets.countries.LI": "លិចទេនស្តែន", - "assets.countries.LK": "ស្រីលង្កា", - "assets.countries.LR": "លីបេរីយ៉ា", - "assets.countries.LS": "ឡេសូតូ", - "assets.countries.LT": "លីទុយអានី", - "assets.countries.LU": "លុចហ្សំបួរ", - "assets.countries.LV": "ឡាតវីយ៉ា", - "assets.countries.LY": "លីប៊ី", - "assets.countries.MA": "ម៉ារ៉ុក", - "assets.countries.MC": "ម៉ូណាកូ", - "assets.countries.MD": "ម៉ុលដូវ៉ា (សាធារណរដ្ឋ)", - "assets.countries.ME": "ម៉ុងតេណេក្រូ", - "assets.countries.MF": "សង់ម៉ាទីន (បារាំង)", - "assets.countries.MG": "ម៉ាដាហ្គាស្ការ", - "assets.countries.MH": "កោះម៉ាស្យល", - "assets.countries.MK": "ម៉ាសេដូនីខាងជើង", - "assets.countries.ML": "ម៉ាលី", - "assets.countries.MM": "ភូមា", - "assets.countries.MN": "ម៉ុងហ្គោលី", - "assets.countries.MO": "ម៉ាកាវ", - "assets.countries.MP": "កោះម៉ារៀណាភាគខាងជើង", - "assets.countries.MQ": "ម៉ារទីនីគ", - "assets.countries.MR": "ម៉ូរីតានី", - "assets.countries.MS": "ម៉ុងសេរ៉ា", - "assets.countries.MT": "ម៉ាល់តា", - "assets.countries.MU": "ម៉ូរីទុស", - "assets.countries.MV": "ម៉ាល់ឌីវ", - "assets.countries.MW": "ម៉ាឡាវី", - "assets.countries.MX": "ម៉ិចស៊ិក", - "assets.countries.MY": "ម៉ាឡេស៊ី", - "assets.countries.MZ": "ម៉ូហ្សាំប៊ិក", - "assets.countries.NA": "ណាមីប៊ី", - "assets.countries.NC": "ញូវ កាលេដូនី", - "assets.countries.NE": "នីហ្សេរ", - "assets.countries.NF": "កោះណរហ្វក", - "assets.countries.NG": "នីហ្សេរីយ៉ា", - "assets.countries.NI": "នីការ៉ាហ្គ័រ", - "assets.countries.NL": "ហូល្លង់", - "assets.countries.NO": "ន័រវែស", - "assets.countries.NP": "នេប៉ាល់", - "assets.countries.NR": "ណូរុ", - "assets.countries.NU": "នីវ", - "assets.countries.NZ": "ញូវហ្សេលែន", - "assets.countries.OM": "អូម៉ង់", - "assets.countries.PA": "ប៉ាណាម៉ា", - "assets.countries.PE": "ប៉េរូ", - "assets.countries.PF": "ប៉ូលីនេស៊ីបារាំង", - "assets.countries.PG": "ប៉ាពូញូវហ្គីណេ", - "assets.countries.PH": "ហ្វីលីពីន", - "assets.countries.PK": "ប៉ាគីស្ថាន", - "assets.countries.PL": "ប៉ូឡូញ", - "assets.countries.PM": "សង់ព្យែរ និងមីគុយអេឡុង", - "assets.countries.PN": "ពីតខាយរិន", - "assets.countries.PR": "ព័រតូរីកូ", - "assets.countries.PS": "ប៉ាលេស្ទីន (រដ្ឋ)", - "assets.countries.PT": "ព័រទុយហ្គាល់", - "assets.countries.PW": "ប៉ាឡូ", - "assets.countries.PY": "ប៉ារ៉ាហ្គាយ", - "assets.countries.QA": "កាតារ", - "assets.countries.RE": "រេអុយញ៉ុង", - "assets.countries.RO": "រូម៉ានី", - "assets.countries.RS": "សែប៊ី", - "assets.countries.RU": "សហព័ន្ធរុស្ស៊ី", - "assets.countries.RW": "រវ៉ាន់ដា", - "assets.countries.SA": "អារ៉ាប៊ីសាអ៊ូឌីត", - "assets.countries.SB": "កោះសូឡូម៉ូន", - "assets.countries.SC": "សីស្ហែល", - "assets.countries.SD": "ស៊ូដង់", - "assets.countries.SE": "ស៊ុយអែត", - "assets.countries.SG": "សឹង្ហបុរី", - "assets.countries.SH": "សង់ហេឡេណា", - "assets.countries.SI": "ស្លូវ៉ានី", - "assets.countries.SJ": "កោះស្វាល់បាត និងហ្សង់ម៉ាយេន", - "assets.countries.SK": "ស្លូវ៉ាគី", - "assets.countries.SL": "សេរ៉ាឡេអូន", - "assets.countries.SM": "សាន់ម៉ារីណូ", - "assets.countries.SN": "សេណេហ្គាល់", - "assets.countries.SO": "សូម៉ាលី", - "assets.countries.SR": "ស៊ូរីណាមី", - "assets.countries.SS": "ស៊ូដង់ខាងត្បូង", - "assets.countries.ST": "សៅតូម និងព្រីនស៊ីព", - "assets.countries.SV": "អែលសាល់វ៉ាឌ័រ", - "assets.countries.SX": "ស៊ីនម៉ាទីន", - "assets.countries.SY": "សាធារណរដ្ឋស៊ីរីយ៉ាអារាប់", - "assets.countries.SZ": "ស្វាហ្ស៊ីឡង់", - "assets.countries.TC": "កោះទួក និងកៃកូស", - "assets.countries.TD": "ឆាដ", - "assets.countries.TF": "អាណាចក្រខាងត្បូងបារាំង", - "assets.countries.TG": "តូហ្គោ", - "assets.countries.TH": "ថៃ", - "assets.countries.TJ": "តាហ្ស៊ីគីស្តង់", - "assets.countries.TK": "តូកេឡាអ៊ូ", - "assets.countries.TL": "ទីម័រ ឡេសតេ", - "assets.countries.TM": "ទួគមេនីស្តង់", - "assets.countries.TN": "ទុយណេស៊ី", - "assets.countries.TO": "តុងហ្គា", - "assets.countries.TR": "ទួរគី", - "assets.countries.TT": "ទ្រីនីដាដ និងតូបាហ្គោ", - "assets.countries.TV": "ទុយវ៉ាលុយ", - "assets.countries.TW": "តៃវ៉ាន់", - "assets.countries.TZ": "តង់ហ្សានី (សាធារណរដ្ឋរួម)", - "assets.countries.UA": "អ៊ុយក្រែន", - "assets.countries.UG": "អ៊ូហ្គង់ដា", - "assets.countries.UM": "កោះតូចដែលនៅឆ្ងាយរបស់សហរដ្ឋអាមេរិច", - "assets.countries.US": "សហរដ្ឋអាមេរិច", - "assets.countries.UY": "អ៊ុយរ៉ាហ្គាយ", - "assets.countries.UZ": "អ៊ូហ្សបេគីស្តង់", - "assets.countries.VA": "ហូលីស៊ី", - "assets.countries.VC": "សង់វាំងសង់ និងហ្គ្រីណាឌីន", - "assets.countries.VE": "វេណេហ្សុយអេឡា", - "assets.countries.VG": "កោះវើជីន (ចក្រភពអង់គ្លេស)", - "assets.countries.VI": "កោះវើជីន (ស.អ.រ.)", - "assets.countries.VN": "វៀតណាម", - "assets.countries.VU": "វ៉ានុយអាទុយ", - "assets.countries.WF": "កោះវ៉ាលីស និងហ្វុយទុយណា", - "assets.countries.WS": "សាមូអាន", - "assets.countries.YE": "យេមែន", - "assets.countries.YT": "ម៉ាយុត", - "assets.countries.ZA": "អាហ្វ្រិកខាងត្បូង", - "assets.countries.ZM": "ហ្សាំប៊ី", - "assets.countries.ZW": "ហ្ស៊ីមបាវ៉េ", - "assets.mimetypes.application/epub_zip": "សៀវភៅ EPUB", - "assets.mimetypes.application/msword": "ឯកសារ Word", - "assets.mimetypes.application/pdf": "ឯកសារ PDF", - "assets.mimetypes.application/vnd.moodle.backup": "ឯកសារបម្រុង Moodle", - "assets.mimetypes.application/vnd.ms-excel": "សៀវភៅបញ្ជី Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "សៀវភៅលំហាត់ Excel 2007 ដែលមាន macro", - "assets.mimetypes.application/vnd.ms-powerpoint": "ការបង្ហាញ Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "សៀវភៅបញ្ជី OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "គំរូសៀវភៅបញ្ជី OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "ឯកសារអត្ថបទ OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "គំរូអត្ថបទ OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "គំរូគេហទំព័រ OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "បទបង្ហាញ Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "ស្លាយ Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "សៀវភៅបញ្ជី Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "គំរូ Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "ឯកសារ Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "បទបង្ហាញ iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "សៀវភៅបញ្ជី iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "ឯកសារ iWork Pages", - "assets.mimetypes.application/x-javascript": "កូដ Javascript", - "assets.mimetypes.application/x-mspublisher": "ឯកសារ Publisher", - "assets.mimetypes.application/x-shockwave-flash": "ឯកសារ Flash", - "assets.mimetypes.application/xhtml_xml": "ឯកសារ XHTML", - "assets.mimetypes.archive": "កញ្ចប់ ({{$a.EXT}})", - "assets.mimetypes.audio": "ឯកសារអូឌីយ៉ូ ({{$a.EXT}})", - "assets.mimetypes.document/unknown": "ឯកសារ", - "assets.mimetypes.group:archive": "ឯកសារកញ្ចប់", - "assets.mimetypes.group:audio": "ឯកសារអូឌីយ៉ូ", - "assets.mimetypes.group:document": "ឯកសារអត្ថបទ", - "assets.mimetypes.group:html_audio": "ឯកសារអូឌីយ៉ូដែលគាំទ្រស្រាប់ដោយកម្មវិធីរុករក", - "assets.mimetypes.group:html_video": "ឯកសារវីដេអូដែលគាំទ្រស្រាប់ដោយកម្មវិធីរុករក", - "assets.mimetypes.group:image": "ឯកសាររូបភាព", - "assets.mimetypes.group:presentation": "ឯកសារបទបង្ហាញ", - "assets.mimetypes.group:sourcecode": "កូដ", - "assets.mimetypes.group:spreadsheet": "ឯកសារសៀវភៅបញ្ជី", - "assets.mimetypes.group:video": "ឯកសារវីដេអូ", - "assets.mimetypes.group:web_audio": "ឯកសារអូឌីយ៉ូប្រើនៅលើវែប", - "assets.mimetypes.group:web_file": "ឯកសារវែប", - "assets.mimetypes.group:web_image": "ឯកសាររូបភាពប្រើនៅលើវែប", - "assets.mimetypes.group:web_video": "ឯកសារវីដេអូប្រើនៅលើវែប", - "assets.mimetypes.image": "រូបភាព ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "រូបតំណាងក្នុង Windows", - "assets.mimetypes.text/css": "ឯកសារ CSS", - "assets.mimetypes.text/csv": "ឯកសារ CVS", - "assets.mimetypes.text/html": "ឯកសារ HTML", - "assets.mimetypes.text/plain": "ឯកសារអត្ថបទ", - "assets.mimetypes.text/rtf": "ឯកសារ RTF", - "assets.mimetypes.video": "ឯកសារវីដេអូ ({{$a.EXT}})", - "core.accounts": "គណនី", - "core.add": "បន្ថែម", - "core.ago": "{{$a}} កន្លងទៅ", - "core.all": "ទាំងអស់", - "core.allgroups": "ក្រុមទាំងអស់", - "core.allparticipants": "អ្នកចូលរួមទាំងអស់", - "core.answer": "ចម្លើយ", - "core.answered": "បានឆ្លើយ", - "core.areyousure": "តើអ្នកប្រាកដទេ?", - "core.back": "ត្រឡប់", - "core.block.blocks": "ប្លុក", - "core.cancel": "បោះបង់", - "core.cannotconnect": "មិនអាចតភ្ជាប់៖ ផ្ទៀងផ្ទាត់ថាអ្នកបានវាយ URL ត្រឹមត្រូវហើយគេហទំព័ររបស់អ្នកប្រើលើMoodle {{$a}} ឬជំនាន់ថ្មីជាងនេះ។", - "core.cannotdownloadfiles": "ការទាញយកឯកសារត្រូវបានបិទ។ សូមទាក់ទងអ្នកគ្រប់គ្រងគេហទំព័ររបស់អ្នក។", - "core.captureaudio": "ថតសំឡេង", - "core.capturedimage": "រូបភាពដែលបានថត", - "core.captureimage": "ថតរូបភាព", - "core.capturevideo": "ថតវីដេអូ", - "core.category": "ប្រភេទ", - "core.choose": "ជ្រើស", - "core.choosedots": "ជ្រើសរើស...", - "core.clearsearch": "លុបចោលការស្វែងរក", - "core.clicktohideshow": "ចុចដើម្បីពន្លាឬវេញ", - "core.clicktoseefull": "ចុចដើម្បីមើលខ្លឹមសារពេញ", - "core.comments": "មតិ", - "core.comments.addcomment": "បន្ថែមមតិយោបល់", - "core.comments.comments": "មតិ", - "core.comments.commentscount": "មតិ ({{$a}})", - "core.commentscount": "មតិ ({{$a}})", - "core.completion-alt-auto-fail": "បានបញ្ចប់៖ {{$a}} (មិនបានទទួលពិន្ទុជាប់)", - "core.completion-alt-auto-n": "មិនបានបញ្ចប់៖ {{$a}}", - "core.completion-alt-auto-n-override": "មិនបានបញ្ចប់៖ {{$a.modname}} (បានកំណត់ដោយ {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "បានបញ្ចប់៖ {{$a}} (បានទទួលពិន្ទុជាប់)", - "core.completion-alt-auto-y": "បានបញ្ចប់៖ {{$a}}", - "core.completion-alt-auto-y-override": "បានបញ្ចប់៖ {{$a.modname}} (បានកំណត់ដោយ {{$a.overrideuser}})", - "core.completion-alt-manual-n": "មិនបានបញ្ចប់៖ {{$a}}។ ជ្រើសដើម្បីដាក់សម្គាល់ថាបានបញ្ចប់។", - "core.completion-alt-manual-n-override": "មិនបានបញ្ចប់៖ {{$a.modname}} (បានកំណត់ដោយ {{$a.overrideuser}})។ ជ្រើសដើម្បីដាក់សម្គាល់ថាបានបញ្ចប់។", - "core.completion-alt-manual-y": "បានបញ្ចប់៖ {{$a}}។ ជ្រើសដើម្បីដាក់សម្គាល់ថាមិនបានបញ្ចប់។", - "core.completion-alt-manual-y-override": "បានបញ្ចប់៖ {{$a.modname}} (បានកំណត់ដោយ {{$a.overrideuser}})។ ជ្រើសដើម្បីដាក់សម្គាល់ថាមិនបានបញ្ចប់។", - "core.confirmcanceledit": "តើអ្នកប្រាកដថាចង់ចាកចេញពីទំព័រនេះឬទេ? ការផ្លាស់ប្ដូរទាំងអស់នឹងត្រូវបាត់បង់។", - "core.confirmloss": "តើអ្នកប្រាកដឬទេ? ការផ្លាស់ប្ដូរទាំងអស់នឹងត្រូវបាត់បង់។", - "core.confirmopeninbrowser": "តើអ្នកចង់បើកវានៅក្នុងកម្មវិធីរុករកដែរឬទេ?", - "core.content": "មាតិកា", - "core.contenteditingsynced": "ខ្លឹមសារដែលអ្នកកំពុងកែត្រូវបានធ្វើសមកាលកម្ម។", - "core.contentlinks.chooseaccount": "ជ្រើសរើសគណនី", - "core.contentlinks.chooseaccounttoopenlink": "ជ្រើសរើសគណនីដើម្បីបើកតំណនេះ", - "core.contentlinks.confirmurlothersite": "តំណភ្ជាប់នេះជាកម្មសិទ្ធិរបស់គេហទំព័រផ្សេងទៀត។ តើអ្នកចង់បើកវាទេ?", - "core.contentlinks.errornoactions": "មិនអាចរកឃើញសកម្មភាពដើម្បីអនុវត្តជាមួយតំណនេះទេ។", - "core.contentlinks.errornosites": "មិនអាចរកឃើញគេហទំព័រដើម្បីគ្រប់គ្រងជាមួយតំណនេះទេ។", - "core.continue": "បន្ត", - "core.copiedtoclipboard": "អត្ថបទត្រូវបានចម្លងទៅclipboard", - "core.course": "វគ្គសិក្សា", - "core.course.activitydisabled": "ស្ថាប័នរបស់អ្នកបានបិទដំណើរការសកម្មភាពនេះនៅក្នុងកម្មវិធីទូរសព្ទ", - "core.course.activitynotyetviewableremoteaddon": "ស្ថាប័នរបស់អ្នកបានដំឡើងកម្មវិធីជំនួយដែលមិនទាន់ត្រូវបានគាំទ្រ។", - "core.course.activitynotyetviewablesiteupgradeneeded": "ការដំឡើង Moodle ដោយស្ថាប័ន របស់អ្នកត្រូវការធ្វើបច្ចុប្បន្នភាព។", - "core.course.allsections": "ផ្នែកទាំងអស់", - "core.course.askadmintosupport": "ទាក់ទងអ្នកគ្រប់គ្រងគេហទំព័រនេះហើយប្រាប់ ថាអ្នកចង់ប្រើសកម្មភាពនេះជាមួយកម្មវិធី Moodle Mobile", - "core.course.confirmdeletemodulefiles": "តើអ្នកប្រាកដថាចង់លុបឯកសារទាំងនេះឬ?", - "core.course.confirmdownload": "អ្នកដូចជាចង់ទាញយកឯកសារទំហំ {{size}} នេះ ។ តើអ្នកប្រាកដថាអ្នកចង់បន្តឬទេ?", - "core.course.confirmdownloadunknownsize": "មិនអាចគណនាទំហំនៃការទាញយកបានទេ។ តើអ្នកប្រាកដថាអ្នកចង់បន្តឬទេ?", - "core.course.confirmpartialdownloadsize": "អ្នកដូចជាចង់នឹងទាញយក យ៉ាងហោចណាស់ {{size}} ។ តើអ្នកប្រាកដថាអ្នកចង់បន្តឬទេ?", - "core.course.contents": "ខ្លឹមសារ", - "core.course.couldnotloadsectioncontent": "មិនអាចផ្ទុកមាតិកាផ្នែកបានទេ។ សូមព្យាយាមម្ដងទៀតនៅពេលក្រោយ។", - "core.course.couldnotloadsections": "មិនអាចផ្ទុកផ្នែកមាតិកាបានទេ។ សូមព្យាយាមម្ដងទៀតនៅពេលក្រោយ។", - "core.course.coursesummary": "សេចក្ដីសង្ខេបអំពីវគ្គសិក្សា", - "core.course.downloadcourse": "ទាញយកវគ្គសិក្សា", - "core.course.errordownloadingcourse": "កំហុសក្នុងការទាញយកវគ្គសិក្សា", - "core.course.errordownloadingsection": "កំហុសក្នុងការទាញយកផ្នែក", - "core.course.errorgetmodule": "កំហុសក្នុងការទទួលសកម្មភាពទិន្នន័យ", - "core.course.hiddenfromstudents": "លាក់ពីសិស្ស", - "core.course.hiddenoncoursepage": "អាចចូលមើលបានតែមិនបង្ហាញក្នុងទំព័រវគ្គសិក្សា", - "core.course.manualcompletionnotsynced": "ការបំពេញដោយមិនស្វ័យប្រវត្តិមិនត្រូវបានធ្វើសមកាលកម្ម", - "core.course.nocontentavailable": "មិនមានខ្លឹមសារនៅពេលនេះទេ", - "core.course.overriddennotice": "ពិន្ទុចុងក្រោយរបស់អ្នកពីសកម្មភាពនេះ ត្រូវបានលៃតម្រូវដោយដៃ ។", - "core.course.refreshcourse": "ធ្វើឱ្យវគ្គសិក្សាថ្មីឡើងវិញ", - "core.course.sections": "ផ្នែក", - "core.course.useactivityonbrowser": "ប្រភេទជ្រៅជាងកម្រិត {{$a}} មិនអាចទាញយកបានទេ", - "core.course.warningmanualcompletionmodified": "ការបំពេញដោយមិនស្វ័យប្រវត្តិត្រូវបានកែតម្រូវនៅលើគេហទំព័រ", - "core.course.warningofflinemanualcompletiondeleted": "ការបំពេញដោយដៃនៅក្រៅបណ្តាញខ្លះនៃវគ្គសិក្សា '{{name}}' បានលុប{{error}}", - "core.coursedetails": "ព័ត៌មានលម្អិតអំពីវគ្គសិក្សា", - "core.courses.addtofavourites": "ដាក់ផ្កាយវគ្គសិក្សានេះ", - "core.courses.allowguests": "វគ្គសិក្សានេះអនុញ្ញាតឲ្យអ្នកប្រើជាភ្ញៀវចូលបាន", - "core.courses.availablecourses": "វគ្គសិក្សាដែលមាន", - "core.courses.cannotretrievemorecategories": "ប្រភេទនៅជ្រៅជាងកម្រិត {{$a}} ដូច្នេះមិនអាចទាញយកបានទេ", - "core.courses.categories": "ប្រភេទវគ្គសិក្សា", - "core.courses.confirmselfenrol": "តើអ្នកប្រាកដថាអ្នកចង់ចុះឈ្មោះចូលរៀនវគ្គសិក្សានេះដែរឬទេ?", - "core.courses.courses": "វគ្គសិក្សា", - "core.courses.downloadcourses": "ទាញយកវគ្គសិក្សា", - "core.courses.enrolme": "ចុះឈ្មោះខ្ញុំ", - "core.courses.errorloadcategories": "កំហុសមួយបានកើតឡើងនៅពេលផ្ទុកប្រភេទ", - "core.courses.errorloadcourses": "កំហុសមួយបានកើតឡើងនៅពេលផ្ទុកវគ្គសិក្សា", - "core.courses.errorsearching": "កំហុសមួយបានកើតឡើងនៅពេលកំពុងស្វែងរក", - "core.courses.errorselfenrol": "កំហុសមួយបានកើតឡើងនៅពេលចុះឈ្មោះដោយខ្លួនឯង", - "core.courses.filtermycourses": "ត្រងវគ្គសិក្សារបស់ខ្ញុំ", - "core.courses.frontpage": "ទំព័រមុខ", - "core.courses.hidecourse": "លាក់ពីទិដ្ឋភាព", - "core.courses.ignore": "រំលង", - "core.courses.mycourses": "វគ្គសិក្សារបស់ខ្ញុំ", - "core.courses.mymoodle": "ផ្ទៃតាប្លូ", - "core.courses.nocourses": "គ្មានព័ត៌មានវគ្គសិក្សាត្រូវបង្ហាញឡើយ ។", - "core.courses.nocoursesyet": "គ្មានវគ្គសិក្សាក្នុងប្រភេទនេះទេ", - "core.courses.nosearchresults": "គ្មានលទ្ធផល", - "core.courses.notenroled": "អ្នកមិនបានចុះឈ្មោះចូលរៀនក្នុងវគ្គសិក្សានេះទេ។", - "core.courses.notenrollable": "អ្នកមិនអាចចុះឈ្មោះខ្លួនឯងក្នុងវគ្គនេះទេ", - "core.courses.password": "កូនសោចុះឈ្មោះ", - "core.courses.paymentrequired": "វគ្គសិក្សានេះទាមទារឲ្យអ្នកបង់ប្រាក់សម្រាប់ធាតុនីមួយៗ ។", - "core.courses.paypalaccepted": "បានព្រមទទួលការបង់ប្រាក់តាម PayPal", - "core.courses.reload": "ផ្ទុកឡើងវិញ", - "core.courses.removefromfavourites": "ដកផ្កាយពីវគ្គសិក្សានេះ", - "core.courses.search": "ស្វែងរក", - "core.courses.searchcourses": "ស្វែងរកវគ្គសិក្សា", - "core.courses.searchcoursesadvice": "អ្នកអាចប្រើប៊ូតុងស្វែងរកវគ្គសិក្សាដើម្បី ចូលជាភ្ញៀវឬក៏ចុះឈ្មោះរបស់អ្នកទៅក្នុងវគ្គសិក្សាដែលបានអនុញ្ញាតអោយ។", - "core.courses.selfenrolment": "ការចុះឈ្មោះដោយខ្លួនឯង", - "core.courses.sendpaymentbutton": "ផ្ញើការបង់ប្រាក់តាមរយៈ PayPal", - "core.courses.show": "បង្ហាញវគ្គសិក្សានេះ", - "core.courses.totalcoursesearchresults": "វគ្គសិក្សាសរុប៖ {{$a}}", - "core.currentdevice": "ឧបករណ៍បច្ចុប្បន្ន", - "core.datastoredoffline": "ទិន្នន័យត្រូវបានផ្ទុកក្នុងឧបករណ៍ព្រោះតែវាមិនអាចបញ្ជូនបាន។ វានឹងបញ្ជូនដោយស្វ័យប្រវត្តិនៅពេលក្រោយ។", - "core.date": "កាលបរិច្ឆេទ", - "core.day": "ថ្ងៃ", - "core.days": "ថ្ងៃ", - "core.decsep": ".", - "core.defaultvalue": "លំនាំដើម ({{$a}})", - "core.delete": "លុប", - "core.deletedoffline": "ត្រូវបានលុបក្រៅបណ្តាញ", - "core.deleteduser": "អ្នកប្រើដែលបានលុប", - "core.deleting": "កំពុងលុប", - "core.description": "ពិពណ៌នា", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.discard": "បោះបង់", - "core.dismiss": "បដិសេធ", - "core.displayoptions": "ជម្រើសក្នុងការបង្ហាញ", - "core.done": "ធ្វើរួច", - "core.download": "ទាញយក", - "core.downloading": "កំពុងទាញយក", - "core.edit": "កែសម្រួល", - "core.editor.autosavesucceeded": "ឯកសារព្រាងបានរក្សាទុក", - "core.editor.bold": "ដិត", - "core.editor.clear": "សម្អាតការធ្វើទ្រង់ទ្រាយ", - "core.editor.h3": "ចំណងជើង (ធំ)", - "core.editor.h4": "ចំណងជើង (មធ្យម)", - "core.editor.h5": "ចំណងជើង (តូច)", - "core.editor.italic": "ទ្រេត", - "core.editor.orderedlist": "បញ្ជីមានលំដាប់", - "core.editor.p": "កថាខណ្ឌ", - "core.editor.strike": "បន្ទាត់ឆូត", - "core.editor.textrecovered": "សេចក្តីព្រាងនៃអត្ថបទនេះត្រូវបានស្ដារឡើងវិញដោយស្វ័យប្រវត្តិ។", - "core.editor.underline": "បន្ទាត់ក្រោម", - "core.editor.unorderedlist": "បញ្ជីគ្មានលំដាប់", - "core.emptysplit": "ទំព័រនេះនឹងបង្ហាញទំព័រទទេរបើសិនជាផ្ទាំងខាងឆ្វេងមិនមានអ្វីឬក៏កំពុងដំណើរការ", - "core.error": "កំហុស", - "core.errorchangecompletion": "Aកំហុសបានកើតឡើងខណៈពេលដែលកំពុងផ្លាស់ប្តូរស្ថានភាពបញ្ចប់។ សូមព្យាយាមម្តងទៀត", - "core.errordeletefile": "កំហុសក្នុងការលុបឯកសារ។ សូមព្យាយាមម្តងទៀត។", - "core.errordownloading": "កំហុសក្នុងកាទាញយកឯកសារ", - "core.errordownloadingsomefiles": "កំហុសក្នុងកាទាញយកឯកសារ។ ឯកសារខ្លះប្រហែលជាបាត់បង់។", - "core.errorfileexistssamename": "ឯកសារដែលមានឈ្មោះនេះមានរួចហើយ។", - "core.errorinvalidform": "សំណុំបែបបទផ្ទុកទិន្នន័យមិនត្រឹមត្រូវ។ សូមពិនិត្យមើលថាវាលដែលត្រូវការទាំងអស់ត្រូវបានបំពេញហើយទិន្នន័យគឺត្រឹមត្រូវ។", - "core.errorinvalidresponse": "ចម្លើយនេះមិនត្រឹមត្រូវ ។ សូមទាក់ទងអ្នកគ្រប់គ្រងគេហទំព័ររបស់អ្នកបើសិនជានៅតែមានកំហុសកើតឡើង។", - "core.errorloadingcontent": "កំហុសក្នុងការផ្ទុកខ្លឹមសារ។", - "core.errorofflinedisabled": "ការរុករកក្រៅបណ្តាញត្រូវបានបិទនៅក្នុងគេហទំព័ររបស់អ្នក។ អ្នកត្រូវភ្ជាប់ទៅអ៊ិនធឺណេតដើម្បីប្រើប្រាស់កម្មវិធីនេះ។", - "core.erroropenfilenoapp": "កំហុសក្នុងការបើកឯកសារ “មិនមានកម្មវិធីសម្រាប់បើកឯកសារនេះទេ”", - "core.erroropenfilenoextension": "កំហុសក្នុងការបើកឯកសារ “ឯកសារនេះមិនមានផ្នែកបន្ថែម”", - "core.erroropenpopup": "សកម្មភាពនេះកំពុងតែព្យាយាមបើកសារពន្យល់មួយ។ វាមិនត្រូវបានគាំទ្រក្នុងកម្មវិធីទេ។", - "core.errorrenamefile": "កំហុសក្នុងការប្តូរឈ្មោះឯកសារ។ សូមព្យាយាមម្តងទៀត", - "core.errorsync": "កំហុសពេលកំពុងធ្វើសមកាលកម្ម។ សូមព្យាយាមម្តងទៀត", - "core.errorsyncblocked": "{{$a}}នេះមិនអាចធ្វើសមកាលកម្មបានទេនៅពេលនេះដោយសារតែដំណើរការដែលកំពុងកើតឡើង។ សូមព្យាយាមម្តងទៀតនៅពេលក្រោយ។ បើសិនជាបញ្ហានៅតែកើតមានឡើង សូមសាកល្បងចាប់ផ្តើមកម្មវិធីម្តងទៀត។", - "core.favourites": "បានដាក់ផ្កាយ", - "core.filenameexist": "ឈ្មោះនៃឯកសារនេះធ្លាប់មានរួចហើយ: {{$a}}", - "core.filenotfound": "ឯកសាររកមិនឃើញទេ។ សូមទោស។", - "core.fileuploader.addfiletext": "បន្ថែមឯកសារ", - "core.fileuploader.audio": "សំឡេង", - "core.fileuploader.camera": "កាមេរ៉ា", - "core.fileuploader.confirmuploadfile": "អ្នកកំពុងតែនឹងផ្ទុកឡើងនូវឯកសារ{{size}}។ តើអ្នកប្រាកដទេថាចង់បន្តឬទេ?", - "core.fileuploader.confirmuploadunknownsize": "Iទំហំដែលផ្ទុកមិនអាចធ្វើការគណនាបានទេ។ តើអ្នកប្រាកដទេថាចង់បន្តឬទេ?", - "core.fileuploader.errorcapturingaudio": "កំហុសក្នុងការថតសំឡេង", - "core.fileuploader.errorcapturingimage": "កំហុសក្នុងការថតរូបភាព", - "core.fileuploader.errorcapturingvideo": "កំហុសក្នុងការថតវីដេអូ", - "core.fileuploader.errorgettingimagealbum": "កំហុសក្នុងការទាញយករូបភាពពីអាល់ប៊ុម", - "core.fileuploader.errormustbeonlinetoupload": "អ្នកត្រូវស្ថិតនៅក្នុងបណ្តាញដើម្បីដាក់បញ្ចូលឯកសារ", - "core.fileuploader.errornoapp": "អ្នកមិនបានដំឡើងកម្មវិធីដើម្បីដំណើរការសកម្មភាពនេះ។", - "core.fileuploader.errorreadingfile": "កំហុសក្នុងការអានឯកសារ", - "core.fileuploader.errorwhileuploading": "កំហុសពេលកំពុងផ្ទុកឯកសារ", - "core.fileuploader.file": "ឯកសារ", - "core.fileuploader.filesofthesetypes": "ប្រភេទឯកសារដែលទទួលយកបាន៖", - "core.fileuploader.fileuploaded": "ឯកសារត្រូវបានផ្ទុកដោយជោគជ័យ", - "core.fileuploader.maxbytesfile": "ឯកសារ{{$a.file}}ធំពេក។ ទំហំអតិប្បរមាដែលអ្នកអាចផ្ទុកបានគឺ{{$a.size}}", - "core.fileuploader.more": "ច្រើនទៀត", - "core.fileuploader.photoalbums": "អាល់ប៊ុមរូបភាព", - "core.fileuploader.readingfile": "ឯកសារកំពុងអាន", - "core.fileuploader.readingfileperc": "កំពុងអានឯកសារ: {{$a}}%", - "core.fileuploader.selectafile": "ជ្រើសរើសឯកសារ", - "core.fileuploader.uploadafile": "ផ្ទុកឯកសារឡើង", - "core.fileuploader.uploading": "កំពុងផ្ទុកឡើង", - "core.fileuploader.uploadingperc": "កំពុងផ្ទុកឡើង: {{$a}}%", - "core.fileuploader.video": "វីដេអូ", - "core.filter": "កម្មវិធីចម្រាញ់", - "core.folder": "ថត", - "core.forcepasswordchangenotice": "អ្នកត្រូវតែផ្លាស់ប្ដូរពាក្យសម្ងាត់របស់អ្នកដើម្បីបន្ត ។", - "core.fulllistofcourses": "វគ្គសិក្សាទាំងអស់", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "មធ្យមភាគ", - "core.grades.badgrade": "ពិន្ទុដែលផ្ដល់មិនត្រឹមត្រូវ", - "core.grades.contributiontocoursetotal": "ចំណែកក្នុងពិន្ទុសរុបក្នុងវគ្គសិក្សា", - "core.grades.feedback": "មតិយោបល់", - "core.grades.grade": "ពិន្ទុ", - "core.grades.gradeitem": "ធាតុពិន្ទុ", - "core.grades.grades": "ពិន្ទុ", - "core.grades.lettergrade": "ពិន្ទុតួអក្សរ", - "core.grades.nogradesreturned": "គ្មានពិន្ទុត្រូវបានត្រឡប់ទេ", - "core.grades.nooutcome": "គ្មានលទ្ធផល", - "core.grades.percentage": "ចំនួនភាគរយ", - "core.grades.range": "ជួរ", - "core.grades.rank": "ចំណាត់ថ្នាក់", - "core.grades.weight": "ភាពសំខាន់", - "core.group": "ក្រុម", - "core.groupsseparate": "ក្រុមផ្សេងគ្នា", - "core.groupsvisible": "មើលឃើញក្រុម", - "core.hasdatatosync": "{{$a}}នេះត្រូវបានធ្វើសមកាលកម្មនៅក្រៅបណ្តាញ", - "core.help": "ជំនួយ", - "core.hide": "លាក់", - "core.hour": "ម៉ោង", - "core.hours": "ម៉ោង", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "រូបភាព", - "core.imageviewer": "មើលរូបភាព", - "core.info": "ព័ត៌មាន", - "core.labelsep": ":", - "core.lastaccess": "ចូលដំណើរការចុងក្រោយ", - "core.lastdownloaded": "ការទាញយកចុងក្រោយ", - "core.lastmodified": "កែប្រែចុងក្រោយ", - "core.lastsync": "ការធ្វើសមកាលកម្មចុងក្រោយ", - "core.list": "បញ្ជី", - "core.listsep": ",", - "core.loading": "កំពុងដំណើរការ", - "core.loadmore": "ផ្ទុកបន្ថែម", - "core.location": "ទីតាំង", - "core.login.auth_email": "ចុះឈ្មោះខ្លួនឯង តាមអ៊ីមែល", - "core.login.authenticating": "កំពុងផ្ទៀងផ្ទាត់", - "core.login.cancel": "បោះបង់", - "core.login.changepassword": "ផ្លាស់ប្ដូរពាក្យសម្ងាត់", - "core.login.confirmdeletesite": "តើអ្នកប្រាកដទេថាអ្នកចង់លុបគេហទំព័រគេហទំព័រនេះ {{sitename}}?", - "core.login.connect": "ភ្ជាប់!", - "core.login.connecttomoodle": "ភ្ជាប់ទៅ Moodle", - "core.login.contactyouradministrator": "ទាក់ទងទៅអ្នកគ្រប់គ្រងគេហទំព័ររបស់អ្នកដើម្បីជំនួយផ្សេងៗទៀត", - "core.login.contactyouradministratorissue": "សុំជំនួយពីអ្នកគ្រប់គ្រងគេហទំព័រ របស់អ្នកដើម្បីពិនិត្យមើលបញ្ហានេះ៖ {{$a}}", - "core.login.createaccount": "បង្កើតគណនីថ្មីរបស់ខ្ញុំ", - "core.login.createuserandpass": "ជ្រើសឈ្មោះអ្នកប្រើ និងពាក្យសម្ងាត់របស់អ្នក", - "core.login.credentialsdescription": "សូមបញ្ជូលឈ្មោះអ្នកប្រើនិងពាក្យសម្ងាត់របស់អ្នកដើម្បីចូលទៅក្នុងគណនី", - "core.login.emailconfirmsent": "

                អ៊ីមែលមួយត្រូវបានផ្ញើទៅកាន់អាសយដ្ឋានរបស់អ្នកនៅ {{$a}}

                \n

                វាមានសេចក្ដីណែនាំងាយៗ ដើម្បីបញ្ចប់ការចុះឈ្មោះរបស់អ្នក ។

                \n

                ប្រសិនបើអ្នកនៅតែមានការលំបាក សូមទាក់ទងអ្នកគ្រប់គ្រងតំបន់បណ្ដាញ ។

                ", - "core.login.emailconfirmsentnoemail": "

                មានអ៊ីមែលមួយបានផ្ញើទៅកាន់អសយដ្ឋានរបស់អ្នកគឺ{{$a}}

                វាមានការណែនាំងាយៗដើម្បីបញ្ចប់ការចុះឈ្មោះរបស់អ្នក។

                បើអ្នកបន្តជួបការលំបាកសូមទាក់ទងអ្នកគ្រប់គ្រងគេហទំព័រ។

                '", - "core.login.emailnotmatch": "អាសយដ្ឋានអ៊ីមែលមិនត្រូវគ្នា", - "core.login.erroraccesscontrolalloworigin": "ការទូរសព្ទហៅឆ្លងដោយផ្ទាល់ពីអ្នកម្នាក់ ត្រូវបានបដិសេធ។ សូមពិនិត្យមើលឯកសារណែនាំ https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "កំហុសមួយបានកើតឡើងនៅពេលលុបគេហទំព័រនេះ។ សូមព្យាយាមម្តងទៀត", - "core.login.errorupdatesite": "កំហុសមួយបានកើតឡើងខណៈពេលធ្វើបច្ចុប្បន្នភាពនៅលើគេហទំព័រ", - "core.login.findyoursite": "ស្វែងរកគេហទំព័ររបស់អ្នក", - "core.login.firsttime": "តើអ្នកមកទីនេះជាលើកដំបូងឬ ?", - "core.login.forcepasswordchangenotice": "អ្នកត្រូវតែផ្លាស់ប្ដូរពាក្យសម្ងាត់របស់អ្នកដើម្បីបន្ត ។", - "core.login.forgotten": "ភ្លេចពាក្យសម្ងាត់ ឬឈ្មោះអ្នកប្រើរបស់អ្នកឬ ?", - "core.login.help": "ជំនួយ", - "core.login.helpmelogin": "

                មានគេហទំព័រ Moodle រាប់ពាន់នៅជុំវិញពិភពលោក។ កម្មវិធីនេះអាចភ្ជាប់ទៅតែគេហទំព័រ Moodle ដែលបានបើកការចូលដំណើរការកម្មវិធីទូរសព្ទ។

                ប្រសិនបើអ្នកមិនអាចភ្ជាប់ទៅគេហទំព័រ Moodle របស់អ្នកបានទេអ្នកចាំបាច់ត្រូវទាក់ទងអ្នកគ្រប់គ្រងតំបន់បណ្ដាញរបស់អ្នកហើយសុំឱ្យពួកគេអានhttp://docs.moodle.org/en/Mobile_app

                ដើម្បីសាកប្រើកម្មវិធី Moodle demo គ្រូ or សិស្ស in the អាសយដ្ឋានគេហទំព័រ ចន្លោះ ចុច ភ្ជាប់.

                ", - "core.login.instructions": "សេចក្ដីណែនាំ", - "core.login.invalidaccount": "សូមពិនិត្យមើលព័ត៌មានលំអិតអំពីការចូលប្រើឬសុំគេហទំព័រជំនួយពីអ្នកគ្រប់គ្រងគេហទំព័ររបស់អ្នកដើម្បិពិនិត្យមើលការកំណត់ឡើងវិញ។", - "core.login.invaliddate": "កាលបរិច្ឆេទមិនត្រឹមត្រូវ", - "core.login.invalidemail": "អាសយដ្ឋានអ៊ីមែលមិនត្រឹមត្រូវ", - "core.login.invalidmoodleversion": "កំណែកម្មវិធី Moodle មិនត្រឹមត្រូវ។ យ៉ាងហោចណាស់ក៏ត្រឹម គឺ {{$a}}ដែរ", - "core.login.invalidsite": "អាសយដ្ឋាន URL មិនត្រឹមត្រូវ", - "core.login.invalidtime": "ពេលវេលាមិនត្រឹមត្រូវ", - "core.login.invalidvaluemax": "តម្លៃអតិបរមាគឺ {{$a}}", - "core.login.invalidvaluemin": "តម្លៃអប្បបរមាគឺ {{$a}}", - "core.login.localmobileunexpectedresponse": "ការត្រួតពិនិត្យលក្ខណៈពិសេសបន្ថែមរបស់ Moodle Mobile បានត្រឡប់មកវិញនូវការឆ្លើយតបដែលមិនបានរំពឹងទុក។ អ្នកនឹងត្រូវបានផ្ទៀងផ្ទាត់ភាពត្រឹមត្រូវដោយប្រើសេវាទូរសព្ទស្តង់ដារ។", - "core.login.loggedoutssodescription": "អ្នកត្រូវតែផ្ទៀងផ្ទាត់ម្តងទៀត។ អ្នកចាំបាច់ត្រូវចូលទៅក្នុងគេហទំព័រនៅក្នុងផ្ទាំងបើកកម្មវិធីអ៊ីនធឺណិត។", - "core.login.login": "ចូល", - "core.login.loginbutton": "ចូល", - "core.login.logininsiterequired": "អ្នកចាំបាច់ត្រូវចូលទៅក្នុងគេហទំព័រនៅក្នុងផ្ទាំងកម្មវិធីអ៊ីនធឺណិត។", - "core.login.loginsteps": "សួស្ដី ! ដើម្បីអាចចូលដំណើរការវគ្គសិក្សាបានពេញលេញបាន\nអ្នកត្រូវចំណាយពេលបន្តិច ដើម្បីបង្កើតគណនីថ្មីសម្រាប់ខ្លួនអ្នក\nនៅលើតំបន់បណ្ដាញនេះ ។ វគ្គសិក្សានីមួយៗក៏អាចមាន\n\"កូនសោចុះឈ្មោះ\" របស់វាផងដែរ ប៉ុន្តែពេលនេះអ្នកមិនទាន់\nត្រូវការវានៅឡើយទេ ។ សូមអនុវត្តតាមជំហានខាងក្រោម\nដើម្បីបង្កើតគណនីថ្មី ៖\n
                  \n
                1. បំពេញព័ត៌មានលម្អិតរបស់អ្នក នៅក្នុងសំណុំបែបបទ គណនីថ្មី
                2. \n
                3. សារមួយនឹងត្រូវបានផ្ញើទៅអាសយដ្ឋានអ៊ីមែលរបស់អ្នក ។
                4. \n
                5. អានអ៊ីមែលរបស់អ្នក ហើយចុចលើតំណបណ្ដាញ នៅក្នុងអ៊ីមែលនោះ ។
                6. \n
                7. គណនីរបស់អ្នកនឹងត្រូវបានអះអាង ហើយអ្នកនឹងអាចចូលបាន ។
                8. \n
                9. ឥឡូវ ជ្រើសវគ្គសិក្សាដែលអ្នកចង់ចូលរួម ។
                10. \n
                11. ប្រសិនបើវាទាមទារ \"កូនសោចុះឈ្មោះ\" សូមប្រើកូនសោដែលគ្រូរបស់អ្នកឲ្យ ។ អ្នកនឹងត្រូវបាន \"ចុះឈ្មោះ\" នៅក្នុងវគ្គសិក្សាហើយ ។
                12. \n
                13. ឥឡូវ អ្នកអាចចូលដំណើរការវគ្គសិក្សាដោយពេញលេញបាន ។ ចាប់ពីពេលនេះទៅ អ្នកនឹងត្រូវបញ្ចូលឈ្មោះអ្នកប្រើ និងពាក្យសម្ងាត់ផ្ទាល់ខ្លួនរបស់អ្នក (នៅក្នុងសំណុំបែបបទលើទំព័រនេះ) ដើម្បីចូល និងចូលដំណើរការវគ្គសិក្សាណាមួយដែលអ្នកបានចុះឈ្មោះ ។
                14. \n
                ", - "core.login.missingemail": "បាត់អាសយដ្ឋានអ៊ីមែល", - "core.login.missingfirstname": "បាត់នាមខ្លួន", - "core.login.missinglastname": "បាត់នាមត្រកូល", - "core.login.mobileservicesnotenabled": "ការចូលប្រើទូរសព្ទមិនត្រូវបានបើកនៅលើគេហទំព័ររបស់អ្នកទេ។ សូមទាក់ទងអ្នកគ្រប់គ្រងគេហទំព័ររបស់អ្នកប្រសិនបើអ្នកគិតថាវាគួរត្រូវបានបើក។", - "core.login.mustconfirm": "អ្នកត្រូវអះអាងការចូលរបស់អ្នក", - "core.login.newaccount": "គណនីថ្មី", - "core.login.notloggedin": "អ្នកតម្រូវអោយចូលប្រើ", - "core.login.password": "ពាក្យសម្ងាត់", - "core.login.passwordforgotten": "ភ្លេចពាក្យសម្ងាត់", - "core.login.passwordforgotteninstructions2": "ដើម្បីកំណត់ពាក្យសម្ងាត់របស់អ្នកជាថ្មី សូមបញ្ចូលឈ្មោះអ្នកប្រើ ឬអាសយដ្ឋានអ៊ីមែលរបស់អ្នកនៅខាងក្រោម។ ប្រសិនបើយើងអាចរកឃើញអ្នកនៅក្នុងមូលដ្ឋានទិន្នន័យ នោះអ៊ីម៉ែលមួយនឹងត្រូវបានផ្ញើទៅកាន់អាសយដ្ឋានអ៊ីម៉ែលរបស់អ្នកជាមួយនឹងការណែនាំអំពីរបៀបចូលក្នុងគណនីអ្នកប្រើម្តងទៀត។", - "core.login.passwordrequired": "តម្រូវឱ្យមានពាក្យសម្ងាត់", - "core.login.policyaccept": "ខ្ញុំយល់ និងយល់ព្រម", - "core.login.policyagree": "អ្នកត្រូវតែយល់ព្រមចំពោះគោលការណ៍នេះ ដើម្បីបន្តប្រើតំបន់បណ្ដាញនេះ ។ តើអ្នកយល់ព្រមឬទេ ?", - "core.login.policyagreement": "កិច្ចព្រមព្រៀងលើគោលការណ៍តំបន់បណ្ដាញ", - "core.login.policyagreementclick": "ចុចទីនេះ ដើម្បីអានកិច្ចព្រមព្រៀងលើគោលការណ៍តំបន់បណ្ដាញ", - "core.login.potentialidps": "ចូលដោយប្រើគណនីរបស់អ្នកនៅ", - "core.login.profileinvaliddata": "តម្លៃមិនត្រឹមត្រូវ", - "core.login.recaptchachallengeimage": "reCAPTCHA ជាមួយរូបភាព", - "core.login.recaptchaexpired": "ការផ្ទៀងផ្ទាត់បានផុតកំណត់។ ឆ្លើយសំណួរសុវត្ថិភាពម្តងទៀត", - "core.login.recaptchaincorrect": "ការឆ្លើយចំពោះសំណួរសុវត្ថិភាពមិនត្រឹមត្រូវ", - "core.login.reconnect": "ភ្ជាប់ឡើងវិញ", - "core.login.reconnectdescription": "និមិត្តសញ្ញាផ្ទៀងផ្ទាត់របស់អ្នកមិនត្រឹមត្រូវឬផុតកំណត់។ អ្នកត្រូវភ្ជាប់ប្រព័ន្ធគេហទំព័រឡើងវិញ។", - "core.login.reconnectssodescription": "និមិត្តសញ្ញាផ្ទៀងផ្ទាត់របស់អ្នកមិនត្រឹមត្រូវឬផុតកំណត់។ អ្នកត្រូវភ្ជាប់ប្រព័ន្ធគេហទំព័រឡើងវិញ។ អ្នកចាំបាច់ត្រូវចូលទៅក្នុងគេហទំព័រនៅក្នុងផ្ទាំងកម្មវិធីអ៊ីនធឺណិត។", - "core.login.searchby": "ស្វែងរកតាម៖", - "core.login.security_question": "សំណួរសុវត្ថិភាព", - "core.login.selectacountry": "ជ្រើសប្រទេស", - "core.login.selectsite": "សូមជ្រើសរើសគេហទំព័ររបស់អ្នក៖", - "core.login.signupplugindisabled": "{{$a}} មិនត្រូវបានបើក", - "core.login.siteaddress": "អាសយដ្ឋាននៃគេហទំព័រ", - "core.login.sitehasredirect": "Your site contains at least one HTTP redirect. The app cannot follow redirects, this could be the issue that's preventing the app from connecting to your site.", - "core.login.siteinmaintenance": "គេហទំព័ររបស់អ្នកស្ថិតនៅក្នុងស្ថានភាពជួសជុល", - "core.login.sitepolicynotagreederror": "គោលការណ៍តំបន់បណ្ដាញមិនត្រូវបានយល់ព្រម", - "core.login.siteurl": "អាសយដ្ឋានគេហទំព័រ", - "core.login.siteurlrequired": "ទាមទារការបញ្ជូលអាសយដ្ឋាននៃគេហទំព័រ។ ឧទារហរណ៏ http://www.yourmoodlesite.org", - "core.login.startsignup": "បង្កើតគណនីថ្មី", - "core.login.stillcantconnect": "នៅតែមិនអាចភ្ជាប់ទេឬ?", - "core.login.supplyinfo": "ព័ត៌មានលម្អិតបន្ថែម", - "core.login.username": "ឈ្មោះអ្នកប្រើ", - "core.login.usernameoremail": "បញ្ចូលឈ្មោះអ្នកប្រើ ឬអាសយដ្ឋានអ៊ីមែល", - "core.login.usernamerequired": "ត្រូវការឈ្មោះអ្នកប្រើប្រាស់", - "core.login.usernotaddederror": "មិនបានបន្ថែមអ្នកប្រើ \"{{$a}}\" ឡើយ - មិនស្គាល់កំហុស", - "core.login.visitchangepassword": "តើអ្នកចង់ទៅគេហទំព័រដើម្បីប្តូរពាក្យសម្ងាត់ទេ?", - "core.login.webservicesnotenabled": "សេវានៃការប្រើបណ្តាញមិនត្រូវបានបើកនៅក្នុងគេហទំព័ររបស់អ្នកទេ។ សូមទាក់ទងអ្នកគ្រប់គ្រងគេហទំព័ររបស់អ្នកប្រសិនបើអ្នកគិតថាគួរតែត្រូវអនុញ្ញាតឱ្យប្រើ។", - "core.lostconnection": "និមិត្តសញ្ញាផ្ទៀងផ្ទាត់របស់អ្នកមិនត្រឹមត្រូវឬផុតកំណត់។ អ្នកនឹងត្រូវភ្ជាប់ឡើងវិញជាមួយគេហទំព័រនេះ។", - "core.mainmenu.changesite": "ផ្លាស់ប្តូរគេហទំព័រ", - "core.mainmenu.help": "ជំនួយ", - "core.mainmenu.logout": "ចេញ", - "core.mainmenu.website": "គេហទំព័រ", - "core.maxsizeandattachments": "ទំហំអតិបរមាសម្រាប់ឯកសារថ្មីៈ {{$a.size}} ឯកសារភ្ជាប់អតិបរមា: {{$a.attachments}}", - "core.min": "នាទី", - "core.mins": "នាទី", - "core.misc": "ផ្សេងៗ", - "core.mod_assign": "កិច្ចការ", - "core.mod_assignment": "កិច្ចការ (2.2)", - "core.mod_book": "សៀវភៅ", - "core.mod_chat": "ជជែក", - "core.mod_choice": "ជម្រើស", - "core.mod_data": "មូលដ្ឋានទិន្នន័យ", - "core.mod_database": "មូលដ្ឋានទិន្នន័យ", - "core.mod_external-tool": "ឧបករណ៍ខាងក្រៅ", - "core.mod_feedback": "ការស្ទង់មតិ", - "core.mod_file": "ឯកសារ", - "core.mod_folder": "ថតឯកសារ", - "core.mod_forum": "វេទិកា", - "core.mod_glossary": "សទ្ទានុក្រម", - "core.mod_ims": "កញ្ចប់មាតិកា IMS", - "core.mod_imscp": "កញ្ចប់មាតិកា IMS", - "core.mod_label": "ស្លាក", - "core.mod_lesson": "មេរៀន", - "core.mod_lti": "ឧបករណ៍ខាងក្រៅ", - "core.mod_page": "ទំព័រ", - "core.mod_quiz": "កម្រងសំណួរ", - "core.mod_resource": "ធនធាន", - "core.mod_survey": "ការស្ទង់មតិ", - "core.mod_url": "តំណ URL", - "core.mod_wiki": "វិគី", - "core.mod_workshop": "សិក្ខាសាលា", - "core.moduleintro": "សេចក្តីពិពណ៌នា", - "core.more": "ច្រើនទៀត", - "core.mygroups": "ក្រុមរបស់ខ្ញុំ", - "core.name": "ឈ្មោះ", - "core.networkerroriframemsg": "មិនអាចធ្វើនៅក្រៅបណ្តាញទេ។ សូមភ្ជាប់អ៊ីនធឺណិតហើយព្យាយាមម្តងទៀត", - "core.networkerrormsg": "មានបញ្ហាក្នុងការភ្ជាប់ទៅគេហទំព័រ។ សូមពិនិត្យមើលការតភ្ជាប់របស់អ្នកហើយព្យាយាមម្តងទៀត។", - "core.never": "មិនធ្លាប់", - "core.next": "បន្ទាប់", - "core.no": "ទេ", - "core.nograde": "គ្មានពិន្ទុ", - "core.none": "គ្មាន", - "core.nopasswordchangeforced": "អ្នកមិនអាចបន្តដោយគ្មានការផ្លាស់ប្តូរពាក្យសម្ងាត់របស់អ្នក។", - "core.nopermissionerror": "សូមទោស! អ្នកពុំមានសិទ្ធិធ្វើវាទេ!", - "core.nopermissions": "សូមអភ័យទោស បច្ចុប្បន្នអ្នកគ្មានសិទ្ធិក្នុងការធ្វើដូចនោះទេ ({{$a}})", - "core.noresults": "គ្មានលទ្ធផល", - "core.noselection": "មិនបានជ្រើសរើស", - "core.notapplicable": "មិនមាន", - "core.notenrolledprofile": "មិនមានទម្រង់នេះទេ ពីព្រោះអ្នកប្រើនេះមិនត្រូវបានចុះឈ្មោះក្នុងវគ្គសិក្សានេះ ។", - "core.notice": "ចំណាំ", - "core.notingroup": "សូមអភ័យទោស អ្នកត្រូវតែជាផ្នែករបស់ក្រុម ដើម្បីមើលសកម្មភាពនេះ ។", - "core.notsent": "មិនបានផ្ញើ", - "core.now": "ឥឡូវ", - "core.numwords": "{{$a}} ពាក្យ", - "core.offline": "ក្រៅបណ្ដាញ", - "core.ok": "យល់ព្រម", - "core.online": "លើបណ្ដាញ", - "core.openfullimage": "សូមចុចនៅទីនេះដើម្បីបង្ហាញរូបភាពទំហំពេញ", - "core.openinbrowser": "បើកក្នុងកម្មវិធីរុករក", - "core.othergroups": "ក្រុមផ្សេងទៀត", - "core.paymentinstant": "ប្រើប៊ូតុងខាងក្រោម ដើម្បីបង់ប្រាក់ និងចុះឈ្មោះក្នុងរយៈពេលតែប៉ុន្មាននាទីប៉ុណ្ណោះ !", - "core.percentagenumber": "{{$a}}%", - "core.phone": "ទូរស័ព្ទ", - "core.pictureof": "រូបភាព {{$a}}", - "core.previous": "មុន", - "core.pulltorefresh": "ទាញដើម្បីដំណើរការឡើងវិញ", - "core.question.answer": "ចម្លើយ", - "core.question.answersaved": "ចម្លើយបានរក្សាទុក", - "core.question.cannotdeterminestatus": "មិនអាចបង្ហាញពីស្ថានភាពបានទេ", - "core.question.complete": "បញ្ចប់", - "core.question.correct": "ត្រូវ", - "core.question.errorattachmentsnotsupported": "កម្មវិធីមិនគាំទ្រការភ្ជាប់ឯកសារទៅកាន់ចម្លើយបានទេ។", - "core.question.errorinlinefilesnotsupported": "កម្មវិធីមិនអាចកែសំណេរបន្ទាតជាប់គ្នាបានទេ។", - "core.question.errorquestionnotsupported": "ប្រភេទសំណួរនេះមិនត្រូវបានគាំទ្រដោយកម្មវិធីទេ {{$a}}", - "core.question.feedback": "មូលវិចារណ៍", - "core.question.howtodraganddrop": "ចុចលើ”ទម្លាក់”", - "core.question.incorrect": "មិនត្រឹមត្រូវ", - "core.question.information": "ព័ត៌មាន", - "core.question.invalidanswer": "ចម្លើយមិនពេញលេញ", - "core.question.notanswered": "មិនបានឆ្លើយ", - "core.question.notyetanswered": "មិនទាន់បានឆ្លើយ", - "core.question.partiallycorrect": "ត្រូវមួយផ្នែក", - "core.question.questionmessage": "សំណួរ{{$a}}: {{$b}}", - "core.question.questionno": "សំណួរ {{$a}}", - "core.question.requiresgrading": "ទាមទារការដាក់ពិន្ទុ", - "core.rating.aggregateavg": "មធ្យមភាគនៃរង្វាយតម្លៃ", - "core.rating.aggregatecount": "ចំនួនដងនៃរង្វាយតម្លៃ", - "core.rating.aggregatemax": "រង្វាយតម្លៃអតិបរមា", - "core.rating.aggregatemin": "រង្វាយតម្លៃអប្បបរមា", - "core.rating.aggregatesum": "ផលបូកនៃរង្វាយតម្លៃ", - "core.rating.noratings": "គ្មានរង្វាយតម្លៃត្រូវបានប្រគល់ទេ", - "core.rating.ratings": "រង្វាយតម្លៃ", - "core.redirectingtosite": "អ្នកនឹងត្រូវបានប្ដូរទិសទៅគេហទំព័រនេះ។", - "core.refresh": "ធ្វើឲ្យស្រស់", - "core.remove": "យកចេញ", - "core.required": "ទាមទារ", - "core.requireduserdatamissing": "អ្នកប្រើប្រាស់នេះខ្វះទិន្នន័យប្រវត្តិរូបដែលត្រូវការ។ សូមបញ្ចូលទិន្នន័យនៅក្នុងគេហទំព័ររបស់អ្នកហើយព្យាយាមម្តងទៀត។
                {{$a}}", - "core.resources": "ធនធាន", - "core.restore": "ស្ដារ", - "core.restricted": "បានដាក់កម្រិត", - "core.retry": "ព្យាយាមម្តងទៀត", - "core.save": "រក្សាទុក", - "core.savechanges": "រក្សាទុកការផ្លាស់ប្ដូរ", - "core.search": "ស្វែងរក", - "core.searching": "កំពុងស្វែងរក", - "core.searchresults": "លទ្ធផលស្វែងរក", - "core.sec": "វិ.", - "core.secs": "វិ.", - "core.seemoredetail": "ចុចទីនេះដើម្បីមើលសេចក្ដីលម្អិតបន្ថែម", - "core.send": "ផ្ញើ", - "core.sending": "កំពុងផ្ញើ", - "core.settings.about": "អំពី", - "core.settings.cannotsyncoffline": "មិនអាចធ្វើសមកាលកម្មខណៈនៅក្រៅបណ្ដាញ", - "core.settings.cannotsyncwithoutwifi": "មិនអាចធ្វើសមកាលកម្មបានទេព្រោះការកំណត់បច្ចុប្បន្នអនុញ្ញាតឱ្យធ្វើសមកាលកម្មនៅពេលភ្ជាប់Wi-Fi។ សូមភ្ជាប់ទៅបណ្តាញ Wi-Fi", - "core.settings.compilationinfo": "ការចងក្រងព័ត៌មាន", - "core.settings.cordovadevicemodel": "ម៉ូដែលឧបករណ៍ Cordova", - "core.settings.cordovadeviceosversion": "កំណែប្រព័ន្ធប្រតិបត្តិការរបស់ឧបករណ៏ Cordova", - "core.settings.cordovadeviceplatform": "កំណែប្រព័ន្ធវេទិការរបស់ឧបករណ៏ Cordova", - "core.settings.cordovadeviceuuid": "Cordova device UUID", - "core.settings.cordovaversion": "កំណែរ Cordova", - "core.settings.currentlanguage": "ភាសាបច្ចុប្បន្ន", - "core.settings.debugdisplay": "បង្ហាញសារបំបាត់កំហុស", - "core.settings.debugdisplaydescription": "ប្រសិនបើអ្នកជ្រើសរើសបើកវា នោះវានឹងបង្ហាញព័ត៌មាននៃបញ្ហាដែលអាចកើតឡើង!", - "core.settings.deletesitefiles": "តើអ្នកប្រាកដទេថាអ្នកចង់លុបឯកសារមកពីគេហទំព័រ{sitename}}ឬទេ?", - "core.settings.deletesitefilestitle": "កន្លែងលុបឯកសារនៅគេហទំព័រ", - "core.settings.deviceinfo": "ព័ត៌មានឧបករណ៍", - "core.settings.deviceos": "ប្រព័ន្ធប្រតិបត្តិរបស់ឧបករណ៍", - "core.settings.disableall": "បិទការជូនដំណឹង", - "core.settings.disabled": "បានបិទ", - "core.settings.displayformat": "ទម្រង់នៃការបង្ហាញ", - "core.settings.enabledownloadsection": "បើកដំណើរការផ្នែកទាញយក", - "core.settings.enablerichtexteditor": "បើកកម្មវិធីកែសម្រួលអត្ថបទ", - "core.settings.enablerichtexteditordescription": "ប្រសិនបើបើកកម្មវិធីកែសម្រួលអត្ថបទ នោះអ្នកអាចកែតម្រូវខ្លឹមសារបាន", - "core.settings.enablesyncwifi": "អនុញ្ញាតឱ្យធ្វើសមកាលកម្មនៅពេលតែមាន Wi-Fi ប៉ុណ្ណោះ", - "core.settings.errordeletesitefiles": "មានកំហុសក្នុងការលុបឯកសារនៅគេហទំព័រ", - "core.settings.errorsyncsite": "កំហុសក្នុងការធ្វើសមកាលកម្មទិន្នន័យនៅគេហទំព័រ។ សូមពិនិត្យការភ្ជាប់អ៊ីនធឺណិតរបស់អ្នកហើយព្យាយាមម្តងទៀត", - "core.settings.estimatedfreespace": "ប៉ាន់ស្មានទំហំដែលនៅទំនេរ", - "core.settings.filesystemroot": "ឯកសារក្នុងប្រព័ន្ធត្រូវបានដាក់បញ្ចូល", - "core.settings.general": "ទូទៅ", - "core.settings.language": "ភាសា", - "core.settings.license": "អាជ្ញាបណ្ណ GPL", - "core.settings.localnotifavailable": "ការជូនដំណឹងក្នុងតំបន់អាចប្រើបាន", - "core.settings.locationhref": "មើល URLនៅលើបណ្តាញ", - "core.settings.locked": "បិទ", - "core.settings.loggedin": "ក្នុងបណ្តាញ", - "core.settings.loggedoff": "ក្រៅបណ្តាញ", - "core.settings.navigatorlanguage": "ភាសាកម្មវិធីរុករក", - "core.settings.navigatoruseragent": "កម្មវិធីរុករកអ្នកប្រើប្រាស់", - "core.settings.networkstatus": "ស្ថានភាពតភ្ជាប់អ៊ីនធឺណិត", - "core.settings.preferences": "ចំណង់ចំណូលចិត្ត", - "core.settings.privacypolicy": "គោលការណ៍ឯកជន", - "core.settings.reportinbackground": "រាយការណ៍កំហុសដោយស្វ័យប្រវត្តិ", - "core.settings.settings": "ការកំណត់", - "core.settings.showdownloadoptions": "បង្ហាញជម្រើសដែលអាចទាញយក", - "core.settings.sites": "តំបន់បណ្ដាញ", - "core.settings.spaceusage": "ទំហំនៃការប្រើប្រាស់", - "core.settings.synchronization": "ការធ្វើសមកាលកម្ម", - "core.settings.synchronizenow": "ធ្វើសមកាលកម្ម ឥឡូវនេះ", - "core.settings.syncsettings": "កំណត់ការ សមកាលកម្ម", - "core.settings.total": "សរុប", - "core.settings.wificonnection": "ការភ្ជាប់ Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "ជ្រើសរើសគណនីមួយដើម្បីរក្សាទុកឯកសារ", - "core.sharedfiles.chooseactionrepeatedfile": "ឯកសារដែលមានឈ្មោះនេះមានរួចហើយ។ តើអ្នកចង់ជំនួសឯកសារដែលមានស្រាបសូមប្តូរឈ្មោះវា{{$a}}", - "core.sharedfiles.errorreceivefilenosites": "មិនមានគេហទំព័រត្រូវបានរក្សាទុកទេ។ សូមបន្ថែមគេហទំព័រមួយមុនពេលចែករំលែកឯកសារជាមួយកម្មវិធី", - "core.sharedfiles.nosharedfiles": "មិនមានឯកសារដែលបានចែករំលែកនៅលើគេហទំព័រនេះទេ", - "core.sharedfiles.nosharedfilestoupload": "អ្នកមិនមានឯកសារដើម្បីផ្ទុកឡើងនៅទីនេះទេ។ ប្រសិនបើអ្នកចង់ផ្ទុកឡើងនូវឯកសារមួយពីកម្មវិធី ផ្សេងទៀត សូមស្វែងរកទីតាំងឯកសារហើយចុចលើប៊ូតុង Open in", - "core.sharedfiles.rename": "ប្តូរឈ្មោះ", - "core.sharedfiles.replace": "ជំនួស", - "core.sharedfiles.sharedfiles": "ចែករំលែកឯកសារ", - "core.sharedfiles.successstorefile": "ឯកសារត្រូវបានរក្សាទុកដោយជោគជ័យ។ ជ្រើសរើសឯកសារដើម្បីផ្ទុកឡើងទៅជាឯកសារឯកជនរបស់អ្នក ឬប្រើក្នុងសកម្មភាពណាមួយ", - "core.show": "បង្ហាញ", - "core.showless": "បង្ហាញតិច...", - "core.showmore": "បង្ហាញច្រើន...", - "core.site": "តំបន់បណ្ដាញ", - "core.sitehome.sitehome": "ទំព័រដើម", - "core.sitehome.sitenews": "ព័ត៌មានតំបន់បណ្ដាញ", - "core.sitemaintenance": "តំបន់បណ្ដាញកំពុងស្ថិតក្នុងការថែទាំ ហើយបច្ចុប្បន្ននេះមិនអាចប្រើបានឡើយ", - "core.sizeb": "បៃ", - "core.sizegb": "ជ.ប.", - "core.sizekb": "គ.ប.", - "core.sizemb": "ម.ប.", - "core.sizetb": "TB", - "core.skip": "រំលង", - "core.sorry": "សូមទោស...", - "core.sort": "តម្រៀប", - "core.sortby": "តម្រៀបតាម", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedatetimeshort": "%d/%m/%y, %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.submit": "ដាក់ស្នើ", - "core.success": "ជោគជ័យ", - "core.tablet": "ថេប្លេត", - "core.tag.defautltagcoll": "កម្រងលំនាំដើម", - "core.tag.inalltagcoll": "គ្រប់កន្លែង", - "core.tag.itemstaggedwith": "{{$a.tagarea}} ដែលបានដាក់ស្លាក \"{{$a.tag}}\"", - "core.tag.noresultsfor": "គ្មានលទ្ធផលសម្រាប់ \"{{$a}}\"", - "core.tag.notagsfound": "រកមិនឃើញស្លាកដែលផ្គូផ្គងនឹង \"{{$a}}\"", - "core.tag.searchtags": "ស្វែងរកស្លាក", - "core.tag.showingfirsttags": "បង្ហាញស្លាក {{$a}} ដែលពេញនិយមបំផុត", - "core.tag.tag": "ស្លាក", - "core.tag.tagarea_course": "វគ្គសិក្សា", - "core.tag.tagarea_course_modules": "សកម្មភាព និងធនធាន", - "core.tag.tagarea_post": "ប្រកាសនៃកំណត់ហេតុបណ្តាញ", - "core.tag.tagarea_user": "ចំណូលចិត្តរបស់អ្នកប្រើ", - "core.tag.tags": "ស្លាក", - "core.teachers": "គ្រូ", - "core.thereisdatatosync": "គ្មានអ៊ីនធឺណិត{{$a}} ដែលត្រូវធ្វើសមកាលកម្ម", - "core.thisdirection": "ltr", - "core.time": "ពេលវេលា", - "core.timesup": "ដល់ពេលហើយ !", - "core.today": "ថ្ងៃនេះ", - "core.tryagain": "ព្យាយាមម្ដងទៀត", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "អូ ហូ!!", - "core.unexpectederror": "កំហុសដែលមិននឹកស្មានដល់។ សូមបិទនិងបើកកម្មវិធីម្តងទៀតបន្ទាប់មកព្យាយាមម្តងទៀត", - "core.unicodenotsupported": "Emojis មួយចំនួនមិនត្រូវបានគាំទ្រនៅលើគេហទំព័រនេះទេ។ រូបនេះនឹងត្រូវបានដកចេញនៅពេលផ្ញើសារ", - "core.unicodenotsupportedcleanerror": "អត្ថបទទទេត្រូវបានរកឃើញនៅពេលសម្អាតអក្សរយូនីកូដ.", - "core.unknown": "មិនស្គាល់", - "core.unlimited": "មិនកំណត់", - "core.unzipping": "ពន្លាត", - "core.user": "អ្នកប្រើ", - "core.user.address": "អាសយដ្ឋាន", - "core.user.city": "ទីក្រុង", - "core.user.contact": "ទំនាក់ទំនង", - "core.user.country": "ប្រទេស", - "core.user.description": "ពិពណ៌នា", - "core.user.details": "សេចក្ដីលម្អិត", - "core.user.detailsnotavailable": "សេចក្ដីលម្អិតរបស់អ្នកប្រើនេះមិនមានសម្រាប់អ្នកទេ", - "core.user.editingteacher": "គ្រូ", - "core.user.email": "អាសយដ្ឋានអ៊ីមែល", - "core.user.emailagain": "អ៊ីមែល (ម្ដងទៀត)", - "core.user.errorloaduser": "កំហុសក្នុងការផ្ទុកអ្នកប្រើ", - "core.user.firstname": "នាមខ្លួន", - "core.user.interests": "ចំណាប់អារម្មណ៍", - "core.user.lastname": "នាមត្រកូល", - "core.user.manager": "អ្នកគ្រប់គ្រង", - "core.user.newpicture": "រូបភាពថ្មី", - "core.user.participants": "អ្នកចូលរួម", - "core.user.phone1": "ទូរស័ព្ទ", - "core.user.phone2": "ទូរស័ព្ទចល័ត", - "core.user.roles": "តួនាទី", - "core.user.sendemail": "អ៊ីមែល", - "core.user.student": "សិស្ស", - "core.user.teacher": "គ្រូមិនកែសម្រួល", - "core.user.webpage": "ទំព័របណ្ដាញ", - "core.userdeleted": "គណនីអ្នកប្រើនេះត្រូវបានលុប", - "core.userdetails": "ព័ត៌មានលម្អិតអំពីអ្នកប្រើ", - "core.users": "អ្នកប្រើ", - "core.view": "មើល", - "core.viewcode": "មើលកូដ", - "core.vieweditor": "មើលអ្នកកែសម្រួល", - "core.viewembeddedcontent": "បង្ហាញខ្លឹមសារដែលបានដាក់បញ្ចូល", - "core.warningofflinedatadeleted": "ទិន្នន័យក្រៅបណ្ដាញពី{{component}} '{{name}}\\ត្រូវបានលុប។ {{error}}", - "core.whoops": "អូស!", - "core.whyisthishappening": "ហេតុអ្វីរឿងនេះកើតឡើង?", - "core.wsfunctionnotavailable": "មុខងារសេវាបណ្ដាញមិនមានទេ", - "core.year": "ឆ្នាំ", - "core.years": "ឆ្នាំ", - "core.yes": "បាទ/ចាស" -} \ No newline at end of file diff --git a/src/assets/lang/kn.json b/src/assets/lang/kn.json deleted file mode 100644 index b14b76daf..000000000 --- a/src/assets/lang/kn.json +++ /dev/null @@ -1,486 +0,0 @@ -{ - "addon.badges.badges": "ಲಾಂಛನಗಳು", - "addon.block_blogtags.pluginname": "ಬ್ಲಾಗಿನ ಕುಣಿಕೆ", - "addon.block_calendarmonth.pluginname": "ಪಂಚಾಂಗ", - "addon.block_calendarupcoming.pluginname": "ಮುಂದೆ ಬರುವ ಕಾರ್ಯಕ್ರಮಗಳು", - "addon.block_comments.pluginname": "ಟಿಪ್ಪಣಿಗಳು", - "addon.block_glossaryrandom.pluginname": "ಯಾದೃಚ್ಛಿಕ ಶಬ್ಧಸಂಗ್ರಹದ ನೋಂದಣಿ", - "addon.block_myoverview.future": "ಭವಿಷ್ಯ", - "addon.block_myoverview.inprogress": "ಪ್ರಗತಿಯಲ್ಲಿದೆ", - "addon.block_myoverview.past": "ಹಳತು", - "addon.block_myoverview.pluginname": "ಅಭ್ಯಾಸಕ್ರಮದ ಪಕ್ಷಿನೋಟ", - "addon.block_newsitems.pluginname": "ಇತ್ತೀಚಿನ ಪ್ರಕಟಣೆಗಳು", - "addon.block_onlineusers.pluginname": "ನೇರಜಾಲದಲ್ಲಿರುವ ಬಳಕೆದಾರರು", - "addon.block_privatefiles.pluginname": "ಖಾಸಗಿ ಕಡತಗಳು", - "addon.block_recentactivity.pluginname": "ಇತ್ತೀಚಿನ ಚಟುವಟಿಕೆಗಳು", - "addon.block_sitemainmenu.pluginname": "ಮುಖ್ಯ ಪರಿವಿಡಿ", - "addon.block_tags.pluginname": "ಕುಣಿಕೆಗಳು", - "addon.block_timeline.pluginname": "ಕಾಲರೇಖೆ", - "addon.blog.blog": "ಬ್ಲಾಗ್", - "addon.blog.blogentries": "ಬ್ಲಾಗ್ ನಮೂದುಗಳು", - "addon.blog.linktooriginalentry": "ಮೂಲ ಬ್ಲಾಗ್ ನಮೂದುಗೆ ಲಿಂಕ್ ಮಾಡಿ", - "addon.blog.noentriesyet": "ಗೋಚರಿಸುವ ನಮೂದುಗಳು ಇಲ್ಲಿಲ್ಲ", - "addon.calendar.calendar": "ಪಂಚಾಂಗ", - "addon.calendar.calendarevents": "ಪಂಚಾಂಗದ ಘಟನೆಗಳು", - "addon.calendar.courseevents": "Course ಕಾರ್ಯಕ್ರಮಗಳು", - "addon.calendar.errorloadevent": "ಘಟನೆಯನ್ನು ತೋರಿಸುವಲ್ಲಿ ದೋಷವಿದೆ", - "addon.calendar.errorloadevents": "ಘಟನೆಗಳನ್ನು ತೋರಿಸುವಲ್ಲಿ ದೋಷವಿದೆ", - "addon.calendar.groupevents": "ಗುಂಪಿನ ಕಾರ್ಯಕ್ರಮಗಳು", - "addon.calendar.noevents": "ಯಾವುದೇ ಘಟನೆಗಳಿಲ್ಲ", - "addon.calendar.upcomingevents": "ಮುಂದೆಬರುವ ಕಾರ್ಯಕ್ರಮಗಳು", - "addon.calendar.userevents": "ಬಳಕೆದಾರರ ಕಾರ್ಯಕ್ರಮಗಳು", - "addon.competency.competencies": "ಸಾಮರ್ಥ್ಯಗಳು", - "addon.competency.coursecompetencies": "ಅಭ್ಯಾಸಕ್ರಮದ ಸಾಮರ್ಥ್ಯಗಳು", - "addon.competency.errornocompetenciesfound": "ಸಾಮರ್ಥ್ಯಗಳು ದೊರಕಿಲ್ಲ", - "addon.competency.nocompetencies": "ಸಾಮರ್ಥ್ಯಗಳಿಲ್ಲ", - "addon.competency.userplans": "ಕಲಿಕಾ ಯೋಜನೆಗಳು", - "addon.coursecompletion.complete": "ಮುಕ್ತಾಯ", - "addon.coursecompletion.completed": "ಮುಗಿದಿದೆ", - "addon.coursecompletion.completiondate": "ಮುಗಿದ ದಿನಾಂಕ", - "addon.coursecompletion.coursecompletion": "ಅಭ್ಯಾಸಕ್ರಮದ ಪ್ರಗತಿ", - "addon.coursecompletion.criteria": "ಮಾನದಂಡ", - "addon.coursecompletion.criteriagroup": "ಮಾನದಂಡದ ಗುಂಪು", - "addon.coursecompletion.criteriarequiredall": "ಕೆಳಗಿನ ಎಲ್ಲಾ ಮಾನದಂಡಗಳು ಬೇಕಿದೆ", - "addon.coursecompletion.criteriarequiredany": "ಕೆಳಗಿನ ಎಲ್ಲಾ ಮಾನದಂಡಗಳು ಬೇಕಿದೆ", - "addon.coursecompletion.inprogress": "ಪ್ರಗತಿಯಲ್ಲಿದೆ", - "addon.coursecompletion.manualselfcompletion": "ಕೈಯಾರೆ ಮುಕ್ತಾಯ ಮಾಡಿ", - "addon.coursecompletion.notyetstarted": "ಇನ್ನೂ ಪ್ರಾರಂಭಿಸಿಲ್ಲ", - "addon.coursecompletion.pending": "ಬಾಕಿ ಇದೆ", - "addon.coursecompletion.required": "ಬೇಕಾಗಿದೆ", - "addon.coursecompletion.requiredcriteria": "ಬೇಕಿರುವ ಮಾನದಂಡ", - "addon.coursecompletion.status": "ಸ್ಥಿತಿಗತಿ", - "addon.coursecompletion.viewcoursereport": "ಅಭ್ಯಾಸಕ್ರಮದ ವರದಿಯನ್ನು ನೋಡಿ", - "addon.files.couldnotloadfiles": "ಕಡತಗಳ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಲಾಗುತ್ತಿಲ್ಲ", - "addon.files.emptyfilelist": "ತೋರಿಸಲು ಯಾವುದೇ ಕಡತಗಳಿಲ್ಲ", - "addon.files.erroruploadnotworking": "ನಿಮ್ಮ ತಾಣಕ್ಕೆ ಯಾವುದೇ ಕಡತಗಳನ್ನು ಈಗ ಸೇರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", - "addon.files.privatefiles": "ಖಾಸಗಿ ಕಡತಗಳು", - "addon.messageoutput_airnotifier.processorsettingsdesc": "ಸಾಧನಗಳನ್ನು ಸಂರಚಿಸಿ", - "addon.messages.contactlistempty": "ಸಂಪರ್ಕ ಪಟ್ಟಿ ಖಾಲಿ ಇದೆ", - "addon.messages.contactname": "ಸಂಪರ್ಕದ ಹೆಸರು", - "addon.messages.errordeletemessage": "ಸಂದೇಶವನ್ನು ಅಳಿಸುವ ವೇಳೆ ತಪ್ಪಿದೆ", - "addon.messages.errorwhileretrievingcontacts": "ಪರಿಚಾರಕದಿಂದ ಸಂಪರ್ಕಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.messages.errorwhileretrievingdiscussions": "ಪರಿಚಾರಕದಿಂದ ಚರ್ಚೆಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.messages.errorwhileretrievingmessages": "ಪರಿಚಾರಕದಿಂದ ಸಂದೇಶಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.messages.messagenotsent": "ಸಂದೇಶವನ್ನು ಕಳಿಸಲಾಗಿಲ್ಲ. ದಯಮಾಡಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ", - "addon.messages.messages": "ಸಂದೇಶಗಳು", - "addon.messages.newmessages": "ಹೊಸ ಸಂದೇಶಗಳು", - "addon.messages.nousersfound": "ಯಾವ ಬಳಕೆದಾರರು ಕಂಡುಬಂದಿಲ್ಲ", - "addon.messages.removecontactconfirm": "ನಿಮ್ಮ ಸಂಪರ್ಕ ಪಟ್ಟಿಯಿಂದ ಸಂಪರ್ಕವನ್ನು ತೆಗೆಯಲಾಗುವುದು", - "addon.messages.type_blocked": "ತಡೆಹಿಡಿಯಲಾಗಿದೆ", - "addon.messages.type_offline": "ಆಫ್‌ಲೈನ್", - "addon.messages.type_online": "ಆನ್‌ಲೈನ್‌", - "addon.messages.type_search": "ಹುಡುಕಾಟದ ಫಲಿತಾಂಶಗಳು", - "addon.messages.type_strangers": "ಇತರರು", - "addon.messages.warningmessagenotsent": "{{user}}. {{error}} ಬಳಕೆದಾರರಿಗೆ ಸಂದೇಶವನ್ನು(ಗಳನ್ನು) ಕಳಿಸಲಾಗಿಲ್ಲ", - "addon.mod_assign.acceptsubmissionstatement": "ನಿವೇದನೆಯ ಹೇಳಿಕೆಯನ್ನು ದಯಮಾಡಿ ಸ್ವೀಕರಿಸಿ", - "addon.mod_assign.cannoteditduetostatementsubmission": "ನೀವು ಅನ್ವಯಕದಲ್ಲಿ ನಿವೇದನೆಯನ್ನು ಸೇರಿಸಲು ಅಥವಾ ಬದಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ ಯಾಕೆಂದರೆ ನಿವೇದನೆಯ ಹೇಳಿಕೆಯನ್ನು ತಾಣದಿಂದ ತೆಗೆದುಕೊಳ್ಳಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ", - "addon.mod_assign.cannotgradefromapp": "ಕೆಲವು ಶ್ರೇಣಿಯ ವಿಧಾನಗಳನ್ನು ಅನ್ವಯಕವು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ ಹಾಗಾಗಿ ಬದಲಿಸಲಾಗುವುದಿಲ್ಲ", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "ನೀವು ಅನ್ವಯಕದಲ್ಲಿ ನಿವೇದನೆಯನ್ನು ಸಲ್ಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ ಯಾಕೆಂದರೆ ನಿವೇದನೆಯ ಹೇಳಿಕೆಯನ್ನು ತಾಣದಿಂದ ತೆಗೆದುಕೊಳ್ಳಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ", - "addon.mod_assign.erroreditpluginsnotsupported": "ನೀವು ಅನ್ವಯಕದಲ್ಲಿ ನಿವೇದನೆಯನ್ನು ಸೇರಿಸಲು ಅಥವಾ ಬದಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ ಯಾಕೆಂದರೆ ಕೆಲವು ಪ್ಲಗ್‌ಇನ್‌ಗಳು ಇನ್ನೂ ಬದಲಾವಣೆಗೆ ದೊರಕಿಲ್ಲ", - "addon.mod_assign.errorshowinginformation": "ನಿವೇದನೆಯ ಮಾಹಿತಿಯನ್ನು ತೋರಿಸಲಾಗುತ್ತಿಲ್ಲ", - "addon.mod_assign.feedbacknotsupported": "ಈ ಮರುಮಾಹಿತಿ ಅನ್ವಯಕದಲ್ಲಿ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ ಹಾಗು ಸಂಪೂರ್ಣ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿಲ್ಲದಿರಬಹುದು", - "addon.mod_assign.gradenotsynced": "ಶ್ರೇಣಿ ನವೀಕರಣಗೊಂಡಿಲ್ಲ", - "addon.mod_assign.modulenameplural": "ಕಾರ್ಯ ನಿಯೋಜನೆ", - "addon.mod_assign.notallparticipantsareshown": "ನಿವೇದನೆಯನ್ನು ಮಾಡಿರದ ಭಾಗಿದಾರರನ್ನು ತೋರಿಸಲಾಗುವುದಿಲ್ಲ", - "addon.mod_assign.numberofteams": "ಗುಂಪುಗಳು", - "addon.mod_assign.numwords": "{{$a}} ಪದಗಳು", - "addon.mod_assign.submissionnotsupported": "ಈ ನಿವೇದನೆ ಅನ್ವಯಕದಲ್ಲಿ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ ಹಾಗು ಸಂಪೂರ್ಣ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿಲ್ಲದಿರಬಹುದು", - "addon.mod_assign.userwithid": "{{id}} ಗುರುತಿರುವ ಬಳಕೆದಾರರು", - "addon.mod_assign.warningsubmissiongrademodified": "ನಿವೇದನೆಯ ಶ್ರೇಣಿಯನ್ನು ಈ ತಾಣದಲ್ಲಿ ಬದಲಿಸಿದೆ", - "addon.mod_assign.warningsubmissionmodified": "ಬಳಕೆದಾರರ ನಿವೇದನೆಯನ್ನು ಈ ತಾಣದಲ್ಲಿ ಬದಲಿಸಿದೆ", - "addon.mod_assign_feedback_comments.pluginname": "ಮರುಮಾಹಿತಿಯ ಟಿಪ್ಪಣಿ", - "addon.mod_chat.errorwhileconnecting": "ಮಾತುಕತೆಗೆ ಸಂಪರ್ಕಿಸುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_chat.errorwhilegettingchatdata": "ಮಾತುಕತೆಯ ದತ್ತಾಂಶವನ್ನು ಪಡೆಯುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_chat.errorwhilegettingchatusers": "ಮಾತುಕತೆಯ ಬಳಕೆದಾರರನ್ನು ಪಡೆಯುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_chat.errorwhileretrievingmessages": "ಪರಿಚಾರಕದಿಂದ ಸಂದೇಶಗಳನ್ನು ಪಡೆಯುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_chat.errorwhilesendingmessage": "ಸಂದೇಶವನ್ನು ಕಳಿಸುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_chat.messages": "ಸಂದೇಶಗಳು", - "addon.mod_chat.mustbeonlinetosendmessages": "ನೀವು ಸಂದೇಶಗಳನ್ನು ಕಳಿಸಲು ಆನ್‌ಲೈನ್‌ ಅಲ್ಲಿರಬೇಕು", - "addon.mod_chat.nomessages": "ಯಾವುದೇ ಸಂದೇಶಗಳಿಲ್ಲ", - "addon.mod_chat.viewreport": "ಹಳೆಯ ಚಾಟ್‌ ಅಧಿವೇಶನಗಳನ್ನು ನೋಡಿ", - "addon.mod_choice.errorgetchoice": "ಆಯ್ಕೆಯ ದತ್ತಾಂಶವನ್ನು ಪಡೆಯುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_choice.modulenameplural": "ಆಯ್ಕೆಗಳು", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% ಬಳಕೆದಾರರು ಈ ಆಯ್ಕೆಯನ್ನು ಮಾಡಿದ್ದಾರೆ: {{text}}.", - "addon.mod_choice.resultsnotsynced": "ನಿಮ್ಮ ಕೊನೆಯ ಪ್ರತಿಕ್ರಿಯೆ ಫಲಿತಾಂಶಕ್ಕೆ ಸೇರ್ಪಡೆಗೊಳ್ಳುವ ಮುನ್ನ ನವೀಕರಣಗೊಂಡಿರಬೇಕು", - "addon.mod_data.errorapproving": "ಪ್ರವೇಶವನ್ನು ಅನುಮೋದಿಸುವ ಅಥವಾ ಅನುಮೋದಿಸದಿರುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_data.errordeleting": "ಪ್ರವೇಶವನ್ನು ಅಳಿಸುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_feedback.captchaofflinewarning": "ಆಫ್‌ಲೈನ್‌ ಅಲ್ಲಿ ಅಥವಾ ಸಂರಚನೆಯಾಗಿರದಿದ್ದರೆ ಅಥವಾ ಪರಿಚಾರಕ ಕಾರ್ಯ ನಿರ್ವಹಿಸದಿದ್ದರೆ ಕ್ಯಾಪ್‌ಚಾ ಮೂಲಕ ಮರುಮಾಹಿತಿಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಲಾಗುವುದಿಲ್ಲ", - "addon.mod_feedback.feedback_submitted_offline": "ಈ ಮರುಮಾಹಿತಿಯನ್ನು ನಂತರ ಮಂಡಿಸಲು ಉಳಿಸಿಕೊಳ್ಳಲಾಗಿದೆ", - "addon.mod_folder.emptyfilelist": "ತೋರಿಸಲು ಯಾವುದೇ ಕಡತಗಳು ಇಲ್ಲ", - "addon.mod_forum.addanewdiscussion": "ಹೊಸ ಚರ್ಚಾವಿಷಯವನ್ನು ಸೇರಿಸಿ", - "addon.mod_forum.cannotadddiscussion": "ಚರ್ಚೆಯನ್ನು ಸೇರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", - "addon.mod_forum.cannotadddiscussionall": "ಚರ್ಚೆಯನ್ನು ಸೇರಿಸಲು ಸಾಧ್ಯವೇ ಇಲ್ಲ", - "addon.mod_forum.cannotcreatediscussion": "ಚರ್ಚೆಯನ್ನು ಸೃಷ್ಟಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", - "addon.mod_forum.errorgetforum": "ಚರ್ಚಾವೇದಿಕೆಯ ದತ್ತಾಂಶವನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_forum.errorgetgroups": "ಗುಂಪಿನ ವ್ಯವಸ್ಥೆಯನ್ನು ಪಡೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_forum.forumnodiscussionsyet": "ಈ ಚರ್ಚಾ ವೇದಿಕೆಯಲ್ಲಿ ಈವರೆಗೂ ಯಾವುದೇ ಚರ್ಚೆಗಳಿಲ್ಲ", - "addon.mod_forum.group": "ಗುಂಪು", - "addon.mod_forum.modulenameplural": "ವೇದಿಕೆಗಳು", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} ಚರ್ಚೆಗಳು", - "addon.mod_forum.numreplies": "{{numreplies}} ಹಿಮ್ಮಾಹಿತಿಗಳು", - "addon.mod_forum.posttomygroups": "ಎಲ್ಲಾ ಗುಂಪುಗಳಿಗೂ ಒಂದು ನಕಲನ್ನು ಕಳಿಸಿರಿ", - "addon.mod_forum.refreshdiscussions": "ಚರ್ಚೆಗಳನ್ನು ನವೀಕರಿಸಿ", - "addon.mod_forum.refreshposts": "ಉತ್ತರಗಳನ್ನು ನವೀಕರಿಸಿ", - "addon.mod_glossary.browsemode": "ಪ್ರವೇಶಿಸಿರುವುದರಲ್ಲಿ ಹುಡುಕಿ", - "addon.mod_glossary.byalphabet": "ಅಕ್ಷರಕ್ಕನುಗುಣವಾಗಿ", - "addon.mod_glossary.byauthor": "ಲೇಖಕರಿಗೆ ಅನುಗುಣವಾಗಿ ಗುಂಪುಗಳನ್ನು ಮಾಡಿ", - "addon.mod_glossary.bycategory": "ಪ್ರವರ್ಗಕ್ಕೆ ಅನುಗುಣವಾಗಿ ಗುಂಪುಗಳನ್ನು ಮಾಡಿ", - "addon.mod_glossary.bynewestfirst": "ಹೊಸದು ಮೊದಲು", - "addon.mod_glossary.byrecentlyupdated": "ಇತ್ತೀಚೆಗೆ ನವೀಕರಿಸಿದ್ದು", - "addon.mod_glossary.bysearch": "ಹುಡುಕು", - "addon.mod_glossary.cannoteditentry": "ಪ್ರವೇಶವನ್ನು ಬದಲಿಸಲಾಗದು", - "addon.mod_glossary.entriestobesynced": "ನವೀಕರಿಸಲಿರುವ ಪ್ರವೇಶಗಳು", - "addon.mod_glossary.entrypendingapproval": "ಈ ಪ್ರವೇಶವು ಅನುಮೋದನೆಯಾಗಬೇಕಿದೆ", - "addon.mod_glossary.errorloadingentries": "ಪ್ರವೇಶಗಳನ್ನು ತೋರಿಸುವಾಗ ತಪ್ಪಾಗಿದೆ", - "addon.mod_glossary.errorloadingentry": "ಪ್ರವೇಶವನ್ನು ತೋರಿಸುವಾಗ ತಪ್ಪಾಗಿದೆ", - "addon.mod_glossary.errorloadingglossary": "ಶಬ್ದಕೋಶವನ್ನು ತೋರಿಸುವಾಗ ತಪ್ಪಾಗಿದೆ", - "addon.mod_glossary.noentriesfound": "ಯಾವುದೇ ಪ್ರವೇಶಗಳು ದೊರಕಿಲ್ಲ", - "addon.mod_glossary.searchquery": "ಪ್ರಶ್ನೆಯನ್ನು ಹುಡುಕಿ", - "addon.mod_imscp.showmoduledescription": "ವಿವರಣೆಯನ್ನು ತೋರಿಸಿ", - "addon.mod_lesson.errorprefetchrandombranch": "ಈ ಪಾಠವು ಒಂದು ಯಾದೃಚ್ಛಿಕ ವಿಷಯದ ಪುಟಕ್ಕೆ ನೆಗೆಯುತ್ತಿದೆ. ಇದನ್ನು ವೆಬ್‌ ಬ್ರೌಸರ್‌ನಲ್ಲಿ ಪ್ರಾರಂಭಿಸದ ಹೊರತು ಅನ್ವಯಕದ ಒಳಗೆ ಪ್ರಯತ್ನಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", - "addon.mod_lesson.errorreviewretakenotlast": "ಈ ಪ್ರಯತ್ನವನ್ನು ಇನ್ನು ಪುನರ್ವಿಮರ್ಶೆ ಮಾಡಲಾಗುವುದಿಲ್ಲ ಯಾಕೆಂದರೆ ಇನ್ನೊಂದು ಪ್ರಯತ್ನವನ್ನು ಮುಗಿಸಲಾಗುತ್ತಿದೆ", - "addon.mod_lesson.finishretakeoffline": "ಈ ಪ್ರಯತ್ನವನ್ನು ಆಫ್‌ಲೈನಲ್ಲಿ ಮುಗಿಸಲಾಯಿತು", - "addon.mod_lesson.reports": "ವರದಿಗಳು", - "addon.mod_lesson.retakefinishedinsync": "ಆಫ್‌ಲೈನ್ ಪ್ರಯತ್ನವನ್ನು ನವೀಕರಿಸಲಾಯಿತು. ನೀವು ಪುನರ್ವಿಮರ್ಶೆ ಮಾಡುವಿರೆ?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.warningretakefinished": "ಪ್ರಯತ್ನವನ್ನು ತಾಣದಲ್ಲಿ ಮುಗಿಸಲಾಯಿತು", - "addon.mod_lti.errorgetlti": "ಮಾಡ್ಯೂಲ್‌ ದತ್ತಾಂಶವನ್ನು ಪಡೆಯುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_lti.errorinvalidlaunchurl": "ಈ ಕೊಂಡಿಯು ಸರಿಯಾಗಿಲ್ಲ", - "addon.mod_lti.launchactivity": "ಚಟುವಟಿಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ", - "addon.mod_page.errorwhileloadingthepage": "ಪುಟದ ವಿಷಯವನ್ನು ಪ್ರಾರಂಭಿಸುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_quiz.cannotsubmitquizdueto": "ಈ ರಸಪ್ರಶ್ನೆಯ ಪ್ರಯತ್ನವನ್ನು ಕೆಳಗಿನ ಕಾರಣದಿಂದ ಸಲ್ಲಿಸಲಾಗುವುದಿಲ್ಲ:", - "addon.mod_quiz.confirmcontinueoffline": "ಈ ಪ್ರಯತ್ನವನ್ನು {{$a}} ಇಂದ ನವೀಕರಿಸಿಲ್ಲ. ನೀವು ಬೇರೆ ಸಾಧನದಲ್ಲಿ ಮುಂದುವರಿಸಿದ್ದರೆ, ಆ ದತ್ತಾಂಶವನ್ನು ಕಳೆದುಕೊಳ್ಳಬಹುದು", - "addon.mod_quiz.confirmleavequizonerror": "ಉತ್ತರಗಳನ್ನು ಉಳಿಸುವಾಗ ತಪ್ಪಾಯಿತು. ರಸಪ್ರಶ್ನೆಯಿಂದ ಹೊರಬೀಳಲು ಇಚ್ಛಿಸುತ್ತೀರೆ?", - "addon.mod_quiz.errorbehaviournotsupported": "ಈ ರಸಪ್ರಶ್ನೆಯನ್ನು ಅನ್ವಯಕದಲ್ಲಿ ಪ್ರಯತ್ನಿಸಲಾಗುವುದಿಲ್ಲ ಯಾಕೆಂದರೆ ಪ್ರಶ್ನೆಯ ನಡವಳಿಕೆಯನ್ನು ಅನ್ವಯಕವು ಪ್ರೋತ್ಸಾಹಿಸುವುದಿಲ್ಲ :", - "addon.mod_quiz.errordownloading": "ಬೇಕಿರುವ ದತ್ತಾಂಶವನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_quiz.errorgetattempt": "ಪ್ರಯತ್ನದ ದತ್ತಾಂಶವನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_quiz.errorgetquestions": "ಪ್ರಶ್ನೆಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_quiz.errorgetquiz": "ರಸಪ್ರಶ್ನೆಯ ದತ್ತಾಂಶವನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_quiz.errorparsequestions": "ಈ ಪ್ರಶ್ನೆಯನ್ನು ಓದುವಲ್ಲಿ ತಪ್ಪಾಯಿತು. ದಯಮಾಡಿ ಈ ರಸಪ್ರಶ್ನೆಯನ್ನು ವೆಬ್ ಬ್ರೌಸರಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ", - "addon.mod_quiz.errorquestionsnotsupported": "ಈ ರಸಪ್ರಶ್ನೆಯನ್ನು ಅನ್ವಯಕದಲ್ಲಿ ಪ್ರಯತ್ನಿಸಲಾಗುವುದಿಲ್ಲ ಯಾಕೆಂದರೆ ಇಲ್ಲಿರುವ ಪ್ರಶ್ನೆಗಳನ್ನು ಅನ್ವಯಕವು ಪ್ರೋತ್ಸಾಹಿಸುವುದಿಲ್ಲ :", - "addon.mod_quiz.errorrulesnotsupported": "ಈ ರಸಪ್ರಶ್ನೆಯನ್ನು ಅನ್ವಯಕದಲ್ಲಿ ಪ್ರಯತ್ನಿಸಲಾಗುವುದಿಲ್ಲ ಯಾಕೆಂದರೆ ಇಲ್ಲಿರುವ ಪ್ರವೇಶದ ನಿಯಮಗಳನ್ನು ಅನ್ವಯಕವು ಪ್ರೋತ್ಸಾಹಿಸುವುದಿಲ್ಲ :", - "addon.mod_quiz.errorsaveattempt": "ಪ್ರಯತ್ನದ ದತ್ತಾಂಶಗಳನ್ನು ಉಳಿಸುವಾಗ ತಪ್ಪಾಯಿತು.", - "addon.mod_quiz.finishnotsynced": "ಮುಗಿದಿದೆ ಆದರೆ ನವೀಕರಣವಾಗಿಲ್ಲ.", - "addon.mod_quiz.opentoc": "ಸಂಚರಣೆಯ ಮೇಲುಕಿಟಕಿಯನ್ನು ತೆರೆಯಿರಿ", - "addon.mod_quiz.stateinprogress": "ಪ್ರಗತಿಯಲ್ಲಿದೆ", - "addon.mod_quiz.warningattemptfinished": "ಆಫ್‌ಲೈನ್‌ ಪ್ರಯತ್ನವನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ, ಇದು ತಾಣದಲ್ಲಿ ಮುಗಿದಿದೆ ಅಥವಾ ಸಿಗುತ್ತಿಲ್ಲ", - "addon.mod_quiz.warningdatadiscarded": "ಕೆಲವು ಆಫ್‌ಲೈನ್‌ ಉತ್ತರವನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ, ಯಾಕೆಂದರೆ ಪ್ರಶ್ನೆಗಳನ್ನು ಆನ್‌ಲೈನಲ್ಲಿ ಬದಲಿಸಲಾಗಿದೆ.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "ಪ್ರಯತ್ನ ಸಂಪೂರ್ಣವಾಗಿಲ್ಲ ಯಾಕೆಂದರೆ ಆಫ್‌ಲೈನ್‌ ಉತ್ತರಗಳನ್ನು ನಿರಾಕರಿಸಲಾಯಿತು. ದಯಮಾಡಿ ನಿಮ್ಮ ಉತ್ತರಗಳನ್ನು ಪನರ್ವಿಮರ್ಶಿಸಿ ನಂತರ ಮತ್ತೆ ಮಂಡಿಸಿ.", - "addon.mod_resource.errorwhileloadingthecontent": "ವಿಷಯವಸ್ತುವನ್ನು ತೋರಿಸುವಾಗ ತಪ್ಪಾಯಿತು", - "addon.mod_resource.openthefile": "ಕಡತವನ್ನು ತೆರೆ", - "addon.mod_scorm.cannotcalculategrade": "ಶ್ರೇಣಿಯನ್ನು ಲೆಕ್ಕ ಮಾಡಲಾಗಿಲ್ಲ", - "addon.mod_scorm.dataattemptshown": "ಈ ದತ್ತಾಂಶವು {{number}} ಪ್ರಯತ್ನದ ಅಂಕಿಗೆ ಸೇರಿದ್ದು.", - "addon.mod_scorm.errorcreateofflineattempt": "ಹೊಸ ಆಫ್‌ಲೈನ್‌ ಪ್ರಯತ್ನವನ್ನು ಸೃಷ್ಟಿಸುವಾಗ ತಪ್ಪಾಗಿದೆ. ದಯಮಾಡಿ ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ.", - "addon.mod_survey.cannotsubmitsurvey": "ಕ್ಷಮಿಸಿ, ನಿಮ್ಮ ಸಮೀಕ್ಷೆಯನ್ನು ಸಲ್ಲಿಸುವಲ್ಲಿ ಸಮಸ್ಯೆಯಾಯಿತು. ದಯಮಾಡಿ ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ.", - "addon.mod_survey.errorgetsurvey": "ಸಮೀಕ್ಷೆಯ ದತ್ತಾಂಶವನ್ನು ಪಡೆಯುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.mod_survey.results": "ಫಲಿತಾಂಶಗಳು", - "addon.mod_url.accessurl": "ತಾಣಕ್ಕೆ ಪ್ರವೇಶ", - "addon.mod_url.pointingtourl": "ಸಂಪನ್ಮೂಲಗಳು ತೋರುತ್ತಿರುವ ತಾಣವಿದು.", - "addon.mod_wiki.errorloadingpage": "ಪುಟವನ್ನು ತೋರಿಸುವಲ್ಲಿ ತಪ್ಪಾಯಿತು", - "addon.mod_wiki.errornowikiavailable": "ಈ ವಿಕಿಗೆ ಯಾವುದೇ ವಿಷಯವಸ್ತುವಿಲ್ಲ", - "addon.mod_wiki.gowikihome": "ವಿಕಿಯ ಮೊದಲನೇ ಪುಟಕ್ಕೆ ಹೋಗಿ", - "addon.mod_wiki.subwiki": "ಉಪ-ವಿಕಿ", - "addon.mod_wiki.titleshouldnotbeempty": "ಶೀರ್ಷಿಕೆಯು ಕಾಲಿ ಇರುವಂತಿಲ್ಲ", - "addon.mod_wiki.viewpage": "ಪುಟವನ್ನು ನೋಡಿ", - "addon.mod_wiki.wikipage": "ವಿಕಿ ಪುಟ", - "addon.mod_workshop.assessmentstrategynotsupported": "ನಿರ್ಧಾರಣೆಯ ಕಾರ್ಯತಂತ್ರ {{$a}} ಕ್ಕೆ ಪ್ರೋತ್ಸಾಹವಿಲ್ಲ", - "addon.mod_workshop.warningassessmentmodified": "ನಿಯೋಜನೆಯನ್ನು ತಾಣದಲ್ಲಿ ಬದಲಿಸಲಾಯಿತು.", - "addon.mod_workshop.warningsubmissionmodified": "ನಿರ್ಧಾರಣೆಯನ್ನು ತಾಣದಲ್ಲಿ ಬದಲಿಸಲಾಯಿತು.", - "addon.notes.coursenotes": "ಅಭ್ಯಾಸಕ್ರಮದ ಟಿಪ್ಪಣಿಗಳು", - "addon.notes.notes": "ಟಿಪ್ಪಣಿಗಳು", - "addon.notes.personalnotes": "ಸ್ವಂತದ ಟಿಪ್ಪಣಿಗಳು", - "addon.notes.sitenotes": "ತಾಣದ ಟಿಪ್ಪಣಿಗಳು", - "addon.notes.userwithid": "{{id}} ಗುರುತಿರುವ ಬಳಕೆದಾರರು", - "addon.notes.warningnotenotsent": "{{course}} ಅಭ್ಯಾಸಕ್ರಮಕ್ಕೆ ಕೈಪಿಡಿಗಳನ್ನು ಸೇರಿಸಲಾಗುತ್ತಿಲ್ಲ. {{error}}", - "addon.notifications.errorgetnotifications": "ಅಧಿಸೂಚನೆಗಳನ್ನು ತರುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "addon.notifications.notifications": "ಅಧಿಸೂಚನೆಗಳು", - "addon.notifications.playsound": "ಧ್ವನಿಗಳನ್ನು ನುಡಿಸಿ", - "addon.notifications.therearentnotificationsyet": "ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ", - "core.accounts": "ಖಾತೆಗಳು", - "core.add": "ಸೇರಿಸು", - "core.all": "ಎಲ್ಲ", - "core.allgroups": "ಎಲ್ಲಾ ಗುಂಪುಗಳು", - "core.answer": "ಉತ್ತರ", - "core.cannotconnect": "ಸಂಪರ್ಕಗೋಳಿಸಲು ಆಗುತ್ತಿಲ್ಲ: ಕೊಂಡಿಯ ಹೆಸರನ್ನು ಸರಿಯಾಗಿ ಬರೆದಿದ್ದೀರೆಂದು ಪರಿಶೀಲಿಸಿ ಹಾಗು ನಿಮ್ಮ ಮೂಡಲ್‌ ೨.೪ ಅಥವಾ ನಂತರದ್ದೆಂದು ಖಾತ್ರಿಪಡಿಸಿಕೊಳ್ಳಿ", - "core.cannotdownloadfiles": "ಕಡತವನ್ನು ತೆಗೆದುಕೊಳ್ಳದಂತೆ ತಡೆಯಲಾಗಿದೆ. ದಯಮಾಡಿ ನಿಮ್ಮ ತಾಣದ ಆಡಳಿತಾಧಿಕಾರಿಯನ್ನು ಸಂಪರ್ಕಿಸಿ", - "core.captureaudio": "ಧ್ವನಿಯನ್ನು ಮುದ್ರಿಸಿ", - "core.capturedimage": "ತೆಗೆದುಕೊಂಡ ಚಿತ್ರ", - "core.captureimage": "ಚಿತ್ರ ತೆಗೆದುಕೊಳ್ಳಿ", - "core.capturevideo": "ದೃಶ್ಯಾವಳಿಯನ್ನು ಸೆರೆಹಿಡಿಯಿರಿ", - "core.category": "ವರ್ಗ", - "core.choose": "ಆಯ್ಕೆಮಾಡು", - "core.choosedots": "ಆಯ್ಕೆಮಾಡು...", - "core.clearsearch": "ಹುಡುಕಾಟವನ್ನು ತೀರಿಸಿ", - "core.clicktoseefull": "ಸಂಪೂರ್ಣ ವಿಷಯಕ್ಕಾಗಿ ಇಲ್ಲಿ ಒತ್ತಿ", - "core.comments": "ಟಿಪ್ಪಣಿಗಳು", - "core.comments.addcomment": "ಟಿಪ್ಪಣಿ ಯನ್ನು ಸೇರಿಸಿ", - "core.comments.comments": "ಟಿಪ್ಪಣಿಗಳು", - "core.comments.commentscount": "ಟಿಪ್ಪಣಿಗಳು({{$a}})", - "core.comments.nocomments": "ಟಿಪ್ಪಣಿಗಳಿಲ್ಲ", - "core.commentscount": "ಟಿಪ್ಪಣಿಗಳು({{$a}})", - "core.confirmcanceledit": "ನೀವು ಈ ಪುಟದಿಂದ ಹೊರಹೋಗಲು ಇಚ್ಛಿಸುತ್ತೀರೆ? ಎಲ್ಲಾ ಬದಲಾವಣೆಗಳು ಅಳಿಸಿಹೋಗುತ್ತವೆ.", - "core.confirmloss": "ನೀವು ಈ ಪುಟದಿಂದ ಹೊರಹೋಗಲು ಇಚ್ಛಿಸುತ್ತೀರೆ? ಎಲ್ಲಾ ಬದಲಾವಣೆಗಳು ಅಳಿಸಿಹೋಗುತ್ತವೆ.", - "core.confirmopeninbrowser": "ನೀವು ಇದನ್ನು ವೆಬ್‌ ಬ್ರೌಸರ್‌ನಲ್ಲಿ ತೆರೆಯಲು ಇಚ್ಛಿಸುತ್ತೀರೆ?", - "core.content": "ವಿಷಯ", - "core.contenteditingsynced": "ನೀವು ಬರೆಯುತ್ತಿರುವ ವಿಷಯವನ್ನು ಈಗಾಗಲೇ ನವೀಕರಿಸಲಾಗಿದೆ.", - "core.contentlinks.chooseaccount": "ಖಾತೆಯನ್ನು ಆರಿಸಿ", - "core.contentlinks.chooseaccounttoopenlink": "ಕೊಂಡಿಯನ್ನು ತೆರೆಯಲು ಖಾತೆಯನ್ನು ಆರಿಸಿ", - "core.contentlinks.confirmurlothersite": "ಈ ಕೊಂಡಿ ಬೇರೆಯ ತಾಣದ್ದಾಗಿದೆ. ನೀವು ಅದನ್ನು ತೆರೆಯಲು ಇಚ್ಛಿಸುವಿರೆ?", - "core.contentlinks.errornoactions": "ಈ ಕೊಂಡಿಯನ್ನು ಹೇಗೆ ಬಳಸುವುದೆಂದು ತಿಳಿದಿಲ್ಲ.", - "core.contentlinks.errornosites": "ಈ ಕೊಂಡಿಯನ್ನು ಉಪಯೋಗಿಸುವ ಯಾವುದೇ ತಾಣವಿಲ್ಲ", - "core.continue": "ಮುಂದುವರೆ", - "core.copiedtoclipboard": "ಪಠ್ಯವನ್ನು ನಿಮ್ಮ ಕ್ಲಿಪ್‌ಬೊರ್ಡ್‌ಗೆ ನಕಲು ಮಾಡಿಕೊಳ್ಳಲಾಗಿದೆ", - "core.course": "ಅಭ್ಯಾಸ ವಿಷಯ", - "core.course.activitydisabled": "ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಚಟುವಟಿಕೆಯನ್ನು ಅನ್ವಯಕದಲ್ಲಿ ಅಸಮರ್ಥಗೊಳಿಸಿದೆ.", - "core.course.activitynotyetviewableremoteaddon": "ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಒಂದು ಪ್ಲಗ್‌ಇನ್‌ ಅನ್ನು ಅನುಷ್ಟಾನಗೊಳಿಸಿದೆ ಆದರೆ ಅದು ಇನ್ನು ಬೆಂಬಲಿತವಾಗಿಲ್ಲ.", - "core.course.activitynotyetviewablesiteupgradeneeded": "ನಿಮ್ಮ ಸಂಸ್ಥೆಯ ಮೂಡಲ್‌ ಅನುಸ್ಥಾಪನೆಯನ್ನು ನವೀಕರಿಸಬೇಕಿದೆ.", - "core.course.allsections": "ಎಲ್ಲಾ ವರ್ಗಗಳು", - "core.course.askadmintosupport": "ತಾಣದ ಆಡಳಿತಾಧಿಕಾರಿಯನ್ನು ಸಂಪರ್ಕಿಸಿ ಹಾಗು ಮೂಡಲ್‌ ಅನ್ವಯಕದಲ್ಲಿ ಈ ಚಟುವಟಿಕೆಯನ್ನು ಉಪಯೋಗಿಸಲು ಇಚ್ಛಿಸುತ್ತೀರೆಂದು ಹೇಳಿ.", - "core.course.confirmdeletemodulefiles": "ಈ ಕಡತಗಳನ್ನು ಅಳಿಸಲು ಬಯಸುವಿರೇ?", - "core.course.confirmdownload": "ನೀವು {{size}} ಮೊತ್ತದ ಕಡತಕೋಶವನ್ನು ತೆಗೆದುಕೊಳ್ಳಲಿದ್ದೀರಿ. ಮುಂದುವರಿಯಲು ಇಚ್ಛಿಸುವಿರೇ?", - "core.course.confirmdownloadunknownsize": "ನೀವು ತೆಗೆದುಕೊಳ್ಳುವ ಕಡತಕೋಶದ ಮೊತ್ತವನ್ನು ಅಳೆಯಲಾಗಲಿಲ್ಲ. ಆದರೂ ಮುಂದುವರಿಯಲು ಇಚ್ಛಿಸುವಿರೇ?", - "core.course.confirmpartialdownloadsize": "ನೀವು ಕನಿಷ್ಟ {{size}} ಮೊತ್ತದ ಕಡತಕೋಶವನ್ನು ತೆಗೆದುಕೊಳ್ಳಲಿದ್ದೀರಿ. ಮುಂದುವರಿಯಲು ಇಚ್ಛಿಸುವಿರೇ?", - "core.course.contents": "ಪರಿವಿಡಿ", - "core.course.couldnotloadsectioncontent": "ವಿಭಾಗದ ವಿಷಯವಸ್ತುವನ್ನು ತೋರಿಸಲಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ನಂತರ ಪ್ರಯತ್ನಿಸಿ.", - "core.course.couldnotloadsections": "ವಿಭಾಗಗಳನ್ನು ತೋರಿಸಲಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ನಂತರ ಪ್ರಯತ್ನಿಸಿ.", - "core.course.errordownloadingcourse": "ಅಭ್ಯಾಸಕ್ರಮವನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "core.course.errordownloadingsection": "ವಿಭಾಗವನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "core.course.errorgetmodule": "ಚಟುವಟಿಕೆಯ ದತ್ತಾಂಶವನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಲ್ಲಿ ತಪ್ಪಿದೆ", - "core.course.nocontentavailable": "ಈ ಕ್ಷಣದಲ್ಲಿ ವಿಷಯವಸ್ತುಸ್ತು ದೊರಕುತ್ತಿಲ್ಲ.", - "core.course.useactivityonbrowser": "ಇದನ್ನು ನೀವು ನಿಮ್ಮ ಸಾಧನದ ವೆಬ್ ಬ್ರೌಸರ್‌ನಿಂದಲೂ ಉಪಯೋಗಿಸಬಹುದು.", - "core.courses.cannotretrievemorecategories": "{{$a}} ಈ ಹಂತಕ್ಕೂ ಆಳದಲ್ಲಿರುವ ವಿಭಾಗಗಳನ್ನು ಪುನಃ ಸಂಪಾದಿಸಲಾಗುವುದಿಲ್ಲ", - "core.courses.confirmselfenrol": "ನೀವು ಈ ಅಭ್ಯಾಸಕ್ರಮದಕ್ಕೆ ನೋಂದಾಯಿಸಿಕೊಳ್ಳಲು ಇಚ್ಛಿಸುವಿರೆ?", - "core.courses.courses": "ವಿಷಯಗಳು", - "core.courses.downloadcourses": "ಅಭ್ಯಾಸಕ್ರಮಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಿ", - "core.courses.enrolme": "ನನ್ನನ್ನು ನೋಂದಾಯಿಸಿ", - "core.courses.errorloadcategories": "ವಿಭಾಗಗಳನ್ನು ತೋರಿಸುವಾಗ ತಪ್ಪಾಯಿತು", - "core.courses.errorloadcourses": "ಅಭ್ಯಾಸಕ್ರಮಗಳನ್ನು ತೋರಿಸುವಾಗ ತಪ್ಪಾಯಿತು", - "core.courses.errorsearching": "ಹುಡುಕುವಾಗ ತಪ್ಪಾಯಿತು", - "core.courses.errorselfenrol": "ನೋಂದಾಯಿಸುವಾಗ ತಪ್ಪಾಯಿತು", - "core.courses.filtermycourses": "ನನ್ನ ಅಭ್ಯಾಸಕ್ರಮಗಳನ್ನು ಸೋಸಿ", - "core.courses.frontpage": "ಮುಖಪುಟ", - "core.courses.mycourses": "ನನ್ನ ಅಭ್ಯಾಸಕ್ರಮಗಳು", - "core.courses.mymoodle": "ತಡೆತೆರೆ", - "core.courses.nocourses": "ತೋರಿಸಲು ಯಾವುದೇ ಮಾಹಿತಿ ಇಲ್ಲ", - "core.courses.notenrollable": "ನೀವು ಈ ಅಭ್ಯಾಸಕ್ರಮಕ್ಕೆ ನೋಂದಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", - "core.courses.password": "ನೋಂದಣಿ ಕೀಲಿ", - "core.courses.searchcoursesadvice": "ನೀವು 'ಅಭ್ಯಾಸಕ್ರಮಗಳನ್ನು ಹುಡುಕುವ' ಗುಂಡಿಯನ್ನು ಬಳಸಿ ಯಾವುದೇ ಅಭ್ಯಾಸಕ್ರಮಗಳನ್ನು ಹುಡುಕಬಹುದು. ಅವುಗಳಲ್ಲಿ ನೀವು ನಿಮ್ಮ ಹೆಸರಲ್ಲಿ ಅಥವಾ ಅತಿಥಿ ಬಳಕೆದಾರರಾಗಿ ನೋಂದಾಯಿಸಿ ಅಭ್ಯಾಸ ಮಾಡಬಹುದು.", - "core.courses.selfenrolment": "ಸ್ವ-ನೋಂದಣಿ", - "core.courses.totalcoursesearchresults": "ಒಟ್ಟಾರೆ ಅಭ್ಯಾಸಕ್ರಮಗಳು: {{$a}}", - "core.currentdevice": "ಈಗಿನ ಸಾಧನ", - "core.datastoredoffline": "ದತ್ತಾಂಶವನ್ನು ಕಳಿಸಲು ಸಾಧ್ಯವಾಗದಿರುವ ಕಾರಣ ಸಾಧನದಲ್ಲಿಯೇ ಕಾಪಿಡಲಾಗಿದೆ. ಅದನ್ನು ತಾನಾಗಿಯೇ ನಂತರ ಕಳಿಸಲಾಗುವುದು.", - "core.decsep": ".", - "core.deletedoffline": "ಆಫ್‌ಲೈನ್‌ ಅಲ್ಲಿ ಅಳಿಸಲಾಗಿದೆ", - "core.deleting": "ಅಳಿಸಲಾಗುತ್ತಿದೆ", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.discard": "ತಿರಸ್ಕರಿಸು", - "core.dismiss": "ನಿರ್ಲಕ್ಷಿಸು", - "core.downloading": "ತೆಗೆದುಕೊಳ್ಳಲಾಗುತ್ತಿದೆ", - "core.edit": "ಪರಿಷ್ಕರಣ", - "core.emptysplit": "ಎಡಗಡೆಯ ಬಾಗವು ಖಾಲಿಯಾಗಿದ್ದರೆ ಅಥವಾ ತುಂಬುತ್ತಿದ್ದರೆ ಈ ಪುಟ ಖಾಲಿಯಾಗೇ ಕಾಣುತ್ತದೆ", - "core.errorchangecompletion": "ಪೂರ್ಣ ಮಾಹಿತಿಯನ್ನು ಬದಲಿಸುವಾಗ ತಪ್ಪಾಯಿತು. ದಯಮಾಡಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", - "core.errordeletefile": "ಕಡತವನ್ನು ಅಳಿಸುವಲ್ಲಿ ತಪ್ಪಾಯಿತು. ದಯಮಾಡಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", - "core.errordownloading": "ಕಡತವನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಾಗ ತಪ್ಪಾಯಿತು.", - "core.errordownloadingsomefiles": "ಕಡತಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವಾಗ ತಪ್ಪಾಯಿತು.ಳ ಕೆಲವು ಕಡತಗಳು ಕಾಣೆಯಾಗಿವೆ.", - "core.errorfileexistssamename": "ಈ ಹೆಸರಿನ ಕಡತ ಈಗಾಗಲೇ ಇದೆ.", - "core.errorinvalidform": "ಅರ್ಜಿಯಲ್ಲಿ ಅಸಿಂಧು ದತ್ತಾಂಶವಿದೆ. ಮತ್ತೊಮ್ಮೆ ನೀವು ತುಂಬಿರುವ ಎಲ್ಲಾ ಮಾಹಿತಿಗಳು ಸರಿಯಿವೆಯೇ ಹಾಗು ದತ್ತಾಂಶವು ಸಿಂಧುವೇ ಎಂದು ಪರಿಶೀಲಿಸಿ.", - "core.errorinvalidresponse": "ಅಸಿಂಧು ಹಿಮ್ಮಾಹಿತಿ ಸ್ವೀಕರಿಸಿದೆ. ಸಮಸ್ಯೆ ಮುಂದುವರೆದರೆ, ನಿಮ್ಮ ತಾಣದ ಆಡಳಿತಾಧಿಕಾರಿಯನ್ನು ಸಂಪರ್ಕಿಸಿ.", - "core.errorloadingcontent": "ವಿಷಯವಸ್ತು ತೋರಿಸುವಲ್ಲಿ ತಪ್ಪಿದೆ.", - "core.erroropenfilenoapp": "ಕಡತವನ್ನು ತೆರೆಯುವಲ್ಲಿ ತಪ್ಪಿದೆ : ಈ ಕಡತವನ್ನು ತೆರೆಯುವ ಯಾವುದೇ ಅನ್ವಯಕ ದೊರಕುತ್ತಿಲ್ಲ.", - "core.erroropenfilenoextension": "ಕಡತವನ್ನು ತೆರೆಯುವಲ್ಲಿ ತಪ್ಪಿದೆ : ಕಡತಕ್ಕೆ ಯಾವುದೇ ವಿಸ್ತಾರಣೆ ಇಲ್ಲ.", - "core.erroropenpopup": "ಈ ಚಟುವಟಿಕೆ ಒಂದು ಮೇಲ್‌ತೆರೆಯನ್ನು ತೆರೆಯಲು ಪ್ರಯತ್ನಿಸುತ್ತಿದೆ. ಇದನ್ನು ಅನ್ವಯಕವು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ.", - "core.errorrenamefile": "ಕಡತವನ್ನು ಮರುನಾಮಕರಣ ಮಾಡುವಾಗ ತಪ್ಪಾಯಿತು. ದಯಮಾಡಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", - "core.errorsync": "ನವೀಕರಿಸುವಾಗ ತಪ್ಪಾಯಿತು. ದಯಮಾಡಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", - "core.errorsyncblocked": "{{$a}} ಇದನ್ನು ನವೀಕರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ ಯಾಕೆಂದರೆ ಒನದು ಕಾರ್ಯವು ನೆಡೆಯುತ್ತಿದೆ. ದಯಮಾಡಿ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. ಸಮಸ್ಯೆ ಇನ್ನೂ ಇದ್ದರೆ, ಅನ್ವಯಕವನ್ನು ಮತ್ತೆ ಪ್ರಾರಂಭಿಸಿ.", - "core.filenameexist": "{{$a}} : ಈ ಹೆಸರಿನ ಕಡತವು ಈಗಾಗಲೇ ಇದೆ.", - "core.fileuploader.addfiletext": "ಕಟತವನ್ನು ಸೇರಿಸು", - "core.fileuploader.audio": "ಧ್ವನಿ", - "core.fileuploader.camera": "ಕ್ಯಾಮೆರ", - "core.fileuploader.confirmuploadfile": "ನೀವು ಈಗ {{size}} ಮೊತ್ತದ ಕಡತವನ್ನು ಸೇರಿಸುತ್ತಿರುವಿರಿ. ಮುದುವರಿಯಲು ಇಚ್ಛಿಸುವಿರೆ?", - "core.fileuploader.confirmuploadunknownsize": "ನೀವು ಸೇರಿಸುತ್ತಿರುವ ಕಡತಕೋಶದ ಮೊತ್ತವನ್ನು ಈಅಳೆಯಲಾಗಲಿಲ್ಲ. ಮುದುವರಿಯಲು ಇಚ್ಛಿಸುವಿರೆ?", - "core.fileuploader.errorcapturingaudio": "ಧ್ವನಿಯನ್ನು ಹಿಡಿಯಲಾಗಲಿಲ್ಲ.", - "core.fileuploader.errorcapturingimage": "ಚಿತ್ರವನ್ನು ಹಿಡಿಯಲಾಗಲಿಲ್ಲ.", - "core.fileuploader.errorcapturingvideo": "ದೃಶ್ಯಾವಳಿಯನ್ನು ಹಿಡಿಯಲಾಗಲಿಲ್ಲ.", - "core.fileuploader.errorgettingimagealbum": "ಚಿತ್ರಕೋಶದಿಂದ ಚಿತ್ರನ್ನು ತೆಗೆಯಲಾಗಲಿಲ್ಲ", - "core.fileuploader.errormustbeonlinetoupload": "ನೀವು ಕಡತಗಳನ್ನು ಸೇರಿಸಲು ಆನ್‌ಲೈನ್‌ ಇರಬೇಕು.", - "core.fileuploader.errornoapp": "ಈ ಕೆಲಸವನ್ನು ಮಾಡಲು ನೀವು ಯಾವುದೇ ಅನ್ವಯಕವನ್ನು ಅನುಸ್ಥಾಪಿಸಿಲ್ಲ.", - "core.fileuploader.errorreadingfile": "ಕಡತವನ್ನು ಓದುವಲ್ಲಿ ತಪ್ಪಾಯಿತು.", - "core.fileuploader.errorwhileuploading": "ಕಡತವನ್ನು ಸೇರಿಸುವಲ್ಲಿ ತಪ್ಪಾಯಿತು.", - "core.fileuploader.file": "ಕಡತ", - "core.fileuploader.fileuploaded": "ಕಡತವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಸೇರಿಸಲಾಯಿತು.", - "core.fileuploader.maxbytesfile": "{{$a.file}} ಕಡತವು ತುಂಬಾ ದೊಡ್ಡದಾಯಿತು. ನೀವು ಸೇರಿಸಬಹುದಾದ ಬಹುದೊಡ್ಡ ಕಡತದ ಮಿತಿಯು ಇಷ್ಟಿದೆ {{$a.size}}.", - "core.fileuploader.photoalbums": "ಚಿತ್ರಕೋಶಗಳು", - "core.fileuploader.readingfile": "ಕಡತವನ್ನು ಓದಲಾಗುತ್ತಿದೆ", - "core.fileuploader.selectafile": "ಕಡತವನ್ನು ಆಯ್ದುಕೊಳ್ಳಿ", - "core.fileuploader.uploadafile": "ಕಡತವನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ", - "core.fileuploader.uploading": "ಅಪ್ಲೋಡ್ ಆಗುತ್ತಲಿದೆ", - "core.fileuploader.uploadingperc": "ಅಪ್ಲೋಡ್ ಆಗುತ್ತಲಿದೆ: {{$a}}%", - "core.fileuploader.video": "ದೃಶ್ಯಾವಳಿ", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.groupsseparate": "ಗುಂಪುಗಳನ್ನು ಬೇರ್ಪಡಿಸಿ", - "core.groupsvisible": "ಕಾಣುತ್ತಿರುವ ಗುಂಪುಗಳು", - "core.hasdatatosync": "ಈ {{$a}} ನವೀಕರಿಸಬಹುದಾದ ಆಫ್‌ಲೈನ್‌ ದತ್ತಾಂಶವನ್ನು ಹೊಂದಿದೆ", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "ಚಿತ್ರ", - "core.imageviewer": "ಚಿತ್ರ ವೀಕ್ಷಕ", - "core.info": "ಮಾಹಿತಿ", - "core.lastdownloaded": "ಕೊನೆಯದಾಗಿ ತೆಗೆದುಕೊಂಡಿದ್ದು", - "core.lastsync": "ಕೊನೆಯದಾಗಿ ನವೀಕರಿಸಿದುದು", - "core.list": "ಪಟ್ಟಿ", - "core.listsep": ";", - "core.loadmore": "ಹೆಚ್ಚು ತೋರಿಸಿ", - "core.location": "ಕ್ಷೇತ್ರ", - "core.login.authenticating": "ದೃಢೀಕರಿಸಲಾಗುತ್ತಿದೆ", - "core.login.changepassword": "ಗುಪ್ತಪದವನ್ನು ಬದಲಾಯಿಸು", - "core.login.confirmdeletesite": "ನೀವು {{sitename}} ತಾಣವನ್ನು ಅಳಿಸಲು ಇಚ್ಛಿಸುತ್ತೀರೆ?", - "core.login.connect": "ಸಂಪರ್ಕ !", - "core.login.connecttomoodle": "ಮೂಡಲ್‌ಗೆ ಸಂಪರ್ಕಿಸಿ", - "core.login.contactyouradministrator": "ಹೆಚ್ಚಿನ ಸಹಾಯಕ್ಕಾಗಿ ನಿಮ್ಮ ತಾಣದ ಆಡಳಿತಗಾರರನ್ನು ಸಂಪರ್ಕಿಸಿ.", - "core.login.contactyouradministratorissue": "ಈ ಸಮಸ್ಯೆಯನ್ನು ಪರೀಕ್ಷಿಸಲು ನಿಮ್ಮ ತಾಣದ ಆಡಳಿತಗಾರರಿಗೆ ತಿಳಿಸಿ : {{$a}}", - "core.login.credentialsdescription": "ಒಳಹೋಗಲು ದಯಮಾಡಿ ನಿಮ್ಮ ಬಳಕೆದಾರರ ಹೆಸರು ಹಾಗು ಗುಪ್ತಪದವನ್ನು ನೀಡಿರಿ", - "core.login.emailnotmatch": "ಮಿಂಚಂಚೆಗಳು ಹೊಂದಿಕೆಯಾಗುತ್ತಿಲ್ಲ", - "core.login.errordeletesite": "ಈ ತಾಣವನ್ನು ಅಳಿಸುವಾಗ ತಪ್ಪಾಯಿತು. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", - "core.login.errorupdatesite": "ತಾಣದ ಟೋಕನ್‌ ಅನ್ನು ನವೀಕರಿಸುವಾಗ ತಪ್ಪಾಯಿತು.", - "core.login.invalidaccount": "ನಿಮ್ಮ ಲಾಗ್‌ಇನ್‌ ಮಾಹಿತಿಯನ್ನು ಪರೀಕ್ಷಿಸಿ ಅಥವಾ ನಿಮ್ಮ ತಾಣದ ಆಡಳಿತಗಾರರನ್ನು ತಾಣದ ಆಕೃತಿಯನ್ನು ಪರೀಕ್ಷಿಸಲು ಹೇಳಿ.", - "core.login.invaliddate": "ಅಸಿಂಧು ದಿನಾಂಕ", - "core.login.invalidmoodleversion": "ಅಸಿಂಧುವಾದ ಮೂಡಲ್ ಆವೃತ್ತಿ. ಕನಿಷ್ಠ ಆವೃತ್ತಿ ೨.೪ ಇರಬೇಕು.", - "core.login.invalidsite": "ಈ ತಾಣದ URL ಅಸಿಂಧುವಾಗಿದೆ.", - "core.login.invalidtime": "ಅಮಾನ್ಯವಾದ ಸಮಯ", - "core.login.invalidvaluemax": "{{$a}} ಗರಿಷ್ಠ ಮೌಲ್ಯ", - "core.login.invalidvaluemin": "{{$a}} ಕನಿಷ್ಠ ಮೌಲ್ಯ", - "core.login.loggedoutssodescription": "ನೀವು ಇನ್ನೊಮ್ಮೆ ದೃಢೀಕರಿಸಬೇಕು. ನೀವು ತಾಣಕ್ಕೆ ಬ್ರೌಸರ್‌ ಕಿಟಕಿಯ ಮೂಲಕ ಲಾಗ್‌ಇನ್‌ ಆಗಬೇಕು.", - "core.login.loginbutton": "ಒಳ ಬನ್ನಿ", - "core.login.logininsiterequired": "ನೀವು ತಾಣದ ಒಳಬರಲು ಬ್ರೌಸರ್‌ನ ಕಿಟಕಿಯನ್ನು ಬಳಸಿ", - "core.login.mobileservicesnotenabled": "ಮೋಬೈಲ್‌ ಪ್ರವೇಶವನ್ನು ನಿಮ್ಮ ತಾಣಕ್ಕೆ ನೀಡಲಾಗಿಲ್ಲ. ಈ ಪ್ರವೇಶವನ್ನು ನೀಡಬೇಕೆಂದರೆ, ದಯಮಾಡಿ ನಿಮ್ಮ ತಾಣದ ಆಡಳಿತಗಾರರನ್ನು ಸಂಪರ್ಕಿಸಿ.", - "core.login.newaccount": "ಹೊಸ ಖಾತೆ", - "core.login.notloggedin": "ನೀವು ಒಳಬರಲು ಲಾಗಿನ್ ಆಗಬೇಕು.", - "core.login.password": "ಗುಪ್ತಪದ", - "core.login.passwordforgotten": "ಮರೆತ ಗುಪ್ತಪದ", - "core.login.passwordrequired": "ಗುಪ್ತಪದವು ಬೇಕಾಗಿದೆ", - "core.login.recaptchachallengeimage": "ಮರು CAPTCHA ದ ಸವಾಲಿನ ಚಿತ್ರ", - "core.login.reconnect": "ಮರುಸಂಪರ್ಕ", - "core.login.reconnectdescription": "ನಿಮ್ಮ ದೃಢಿಕರಣದ ಟೊಕನ್ ಅಸಿಂಧುವಾಗಿದೆ ಅಥವಾ ಕೊನೆಯಾಗಿದೆ. ನೀವು ತಾಣಕ್ಕೆ ಮರುಸಂಪರ್ಕಿಸಬೇಕು", - "core.login.searchby": "ಹೀಗೆ ಹುಡುಕಿ:", - "core.login.selectsite": "ದಯವಿಟ್ಟು ನಿಮ್ಮ ತಾಣವನ್ನು ಆರಿಸಿರಿ:", - "core.login.signupplugindisabled": "{{$a}} ವು ಸಕ್ರಿಯವಾಗಿಲ್ಲ.", - "core.login.siteaddress": "ಜಾಲತಾಣದ ವಿಳಾಸ", - "core.login.siteinmaintenance": "ನಿಮ್ಮ ಜಾಲತಾಣವು ನಿರ್ವಹಣಾ ಸ್ಥಿತಿಯಲ್ಲಿದೆ", - "core.login.sitepolicynotagreederror": "ತಾಣದ ಧೋರಣೆಗೆ ಸಮ್ಮತವಿಲ್ಲ.", - "core.login.siteurl": "ತಾಣದ URL", - "core.login.siteurlrequired": "ತಾಣದ URL ಹೀಗೆ ಬೇಕಿದೆ. http://www.yourmoodlesite.org", - "core.login.stillcantconnect": "ಇನ್ನು ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲವೇ?", - "core.login.username": "ಬಳಕೆದಾರ", - "core.login.usernamerequired": "ಸದಸ್ಯನಾಮ ಬೇಕಾಗಿದೆ", - "core.login.visitchangepassword": "ನೀವು ಗುಪ್ತಪದವನ್ನು ಬದಲಿಸಲು ತಾಣಕ್ಕೆ ಭೇಟಿ ನೀಡಲು ಬಯಸುವಿರೆ?", - "core.lostconnection": "ನಿಮ್ಮ ದೃಢೀಕರಣದ ಟೋಕನ್‌ ಅಸಿಂಧುವಾಗಿದೆ ಅಥವಾ ಮುಗಿದಿದೆ. ನೀವು ತಾಣಕ್ಕೆ ಮರುಸಂಪರ್ಕ ಹೊಂದಿರಿ.", - "core.mainmenu.changesite": "ಜಾಲತಾಣವನ್ನು ಬದಲಿಸು", - "core.mainmenu.website": "ಜಾಲತಾಣ", - "core.mygroups": "ನನ್ನ ಗುಂಪುಗಳು", - "core.networkerrormsg": "ತಾಣಕ್ಕೆ ಸಂಪರ್ಕಿಸುವಲ್ಲಿ ಸಮಸ್ಯೆಯೊಂದಿದೆ. ದಯಮಾಡಿ ನಿಮ್ಮ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಹಾಗು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", - "core.next": "ಮುಂದಿನ", - "core.no": "ಇಲ್ಲ", - "core.nocomments": "ಟಿಪ್ಪಣಿಗಳಿಲ್ಲ", - "core.nopasswordchangeforced": "ಗುಪ್ತಪದವನ್ನು ಬದಲಿಸದೆ ನೀವು ಮುಂದುವರಿಯುವಂತಿಲ್ಲ.", - "core.notapplicable": "ದೊರಕುತ್ತಿಲ್ಲ", - "core.notsent": "ಕಳಿಸಲಾಗಿಲ್ಲ", - "core.numwords": "{{$a}} ಪದಗಳು", - "core.ok": "ಆಯ್ತು", - "core.openfullimage": "ಸಂಪೂರ್ಣ ಚಿತ್ರಕ್ಕಾಗಿ ಇಲ್ಲಿ ಒತ್ತಿ", - "core.openinbrowser": "ಬ್ರೌಸರ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ", - "core.othergroups": "ಬೇರೆ ಗುಂಪುಗಳು", - "core.pagea": "ಪುಟ {{$a}}", - "core.percentagenumber": "{{$a}}%", - "core.phone": "ದೂರವಾಣಿ", - "core.pictureof": "{{$a}}ನ ಚಿತ್ರ", - "core.pulltorefresh": "ಪುನರ್‌ತುಂಬಲು ಎಳೆಯಿರಿ", - "core.question.errorattachmentsnotsupported": "ಈ ಅನ್ವಯವು ಉತ್ತರಗಳಿಗೆ ಕಡತಗಳನ್ನು ಸೇರಿಸುವ ಲಕ್ಷಣಗಳನ್ನು ಒಳಗೊಂಡಿಲ್ಲ.", - "core.question.errorinlinefilesnotsupported": "ಈ ಅನ್ವಯವು ಕಡತಗಳನ್ನು ಅದೇಸಾಲಿನಲ್ಲಿ ಬದಲಿಸುವ ಲಕ್ಷಣಗಳನ್ನು ಒಳಗೊಂಡಿಲ್ಲ.", - "core.question.errorquestionnotsupported": "ಈ ಪ್ರಶ್ನೆಯ ರೀತಿಯನ್ನು ಅನ್ವಯಕವು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ: {{$a}}", - "core.question.howtodraganddrop": "ಆಯ್ಕೆ ಮಾಡಲು ಹಾಗು ಸೇರಿಸಲು ತಟ್ಟಿರಿ.", - "core.question.questionmessage": "ಪ್ರಶ್ನೆ {{$a}}: {{$b}}", - "core.redirectingtosite": "ನೀವು ಈ ತಾಣಕ್ಕೆ ಮರುನಿರ್ದೇಶನಗೊಳ್ಳುತ್ತೀರಿ", - "core.requireduserdatamissing": "ಈ ಬಳಕೆದಾರನಿಗೆ ಕೆಲವು ವ್ಯಕ್ತಿಚಿತ್ರದ ದತ್ತಾಂಶದ ಕೊರತೆಯಿದೆ. ನಿಮ್ಮ ತಾಣದಲ್ಲಿ ಈ ದತ್ತಾಂಶವನ್ನು ತುಂಬಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.
                {{$a}}", - "core.resources": "ಸಂಪನ್ಮೂಲಗಳು", - "core.restore": "ಪುನಃಸ್ಥಾಪನೆ", - "core.retry": "ಮರುಪ್ರಯತ್ನಿಸಿ", - "core.searching": "ಹುಡುಕಲಾಗುತ್ತಿದೆ", - "core.settings.about": "ಬಗ್ಗೆ", - "core.settings.cannotsyncoffline": "ಆಫ್‌ಲೈನ್‌ನಲ್ಲಿ ನವೀಕರಿಸಲಾಗುವುದಿಲ್ಲ.", - "core.settings.deletesitefiles": "ನೀವು '{{sitename}}' ತಾಣದಿಂದ ತೆಗೆದುಕೊಂಡ ಕಡತಗಳನ್ನು ಅಳಿಸಲು ಇಚ್ಛಿಸುತ್ತೀರೆ?", - "core.settings.deletesitefilestitle": "ತಾಣದ ಕಡತಗಳನ್ನು ಅಳಿಸಿ", - "core.settings.deviceinfo": "ಸಾಧನದ ಮಾಹಿತಿ", - "core.settings.displayformat": "ತೋರಿಕೆಯ ರೀತಿ", - "core.settings.enablerichtexteditor": "ಪಠ್ಯ ಸಂರಚಕವನ್ನು ಶಕ್ತಗೊಳಿಸಿ", - "core.settings.language": "ಭಾಷೆ", - "core.settings.preferences": "ಆದ್ಯತೆಗಳು", - "core.settings.spaceusage": "ಜಾಗದ ಬಳಕೆ", - "core.settings.synchronization": "ನವೀಕರಣ", - "core.settings.synchronizenow": "ಈಗ ನವೀಕರಿಸಿ", - "core.settings.syncsettings": "ನವೀಕರಣದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು", - "core.sharedfiles.rename": "ಮರುಹೆಸರಿಸು", - "core.sharedfiles.replace": "ಬದಲಿಸು", - "core.sharedfiles.sharedfiles": "ಹಂಚಿಕೊಂಡ ಕಡತಗಳು", - "core.sharedfiles.successstorefile": "ಕಡತವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಕಾಪಿಡಲಾಗಿದೆ. ನಿಮ್ಮ ಖಾಸಗಿ ಕಡತಗಳನ್ನು ಸೇರಿಸಲು ಅಥವಾ ಚಟುವಟಿಕೆಯಲ್ಲಿ ಬಳಸಲು ಕಡತಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಿ.", - "core.sitehome.sitehome": "ತಾಣದ ಮನೆ", - "core.sitehome.sitenews": "ತಾಣದ ವಾರ್ತೆಗಳು", - "core.sizetb": "TB", - "core.sorry": "ಕ್ಷಮಿಸಿ...", - "core.strftimedate": "%d %B %Y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.tablet": "ಟ್ಯಾಬ್ಲೆಟ್", - "core.tag.tagarea_course_modules": "ಚಟುವಟಿಕೆಗಳು ಮತ್ತು ಸಂಪನ್ಮೂಲಗಳು", - "core.tag.tags": "ಕುಣಿಕೆಗಳು", - "core.teachers": "ಶಿಕ್ಷಕರು", - "core.thereisdatatosync": "ಕೆಲವು ಆಫ್‌ಲೈನ್‌ {{$a}} ನವೀಕರಿಸಬೇಕು", - "core.thisdirection": "ltr", - "core.tryagain": "ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "ಓ ಹೊ!", - "core.unexpectederror": "ಅನಿರೀಕ್ಷಿತವಾದ ತಪ್ಪಾಯಿತು. ದಯಮಾಡಿ ಅನ್ವಯಕವನ್ನು ಮುಚ್ಚಿ ಹಾಗು ಮತ್ತೆ ತೆರೆಯಿರಿ ನಂತರ ಪ್ರಯತ್ನಿಸಿ.", - "core.unicodenotsupported": "ಕೆಲವು ಈಮೋಜಿಗಳು ಈ ತಾಣದಲ್ಲಿ ತೋರಿಸುವುದಿಲ್ಲ. ಅಂತಹ ಚಿಹ್ನೆಗಳನ್ನು ಸಂದೇಶಗಳನ್ನು ಕಳಿಸುವಾಗ ತೆಗೆಯಲಾಗುವುದು.", - "core.unicodenotsupportedcleanerror": "ಯುನಿಕೋಡ್‌ ಚಿಹ್ನೆಗಳನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸುವಾಗ ಖಾಲಿ ಪಠ್ಯ ದೊರಕಿದೆ.", - "core.unknown": "ಅಪರಿಚಿತ", - "core.unzipping": "ಕಟ್ಟಿನಿಂದ ತೆರೆಯಲಾಗುತ್ತಿದೆ", - "core.user": "ಬಳಕೆದಾರ", - "core.user.address": "ವಿಳಾಸ", - "core.user.city": "ಪಟ್ಟಣ/ನಗರ", - "core.user.contact": "ಸಂಪರ್ಕ", - "core.user.country": "ದೇಶ", - "core.user.detailsnotavailable": "ಈ ಬಳಕೆದಾರರ ವಿವರ ನಿಮಗೆ ಲಭ್ಯವಿಲ್ಲ", - "core.user.editingteacher": "ಶಿಕ್ಷಕರು", - "core.user.manager": "ನಿರ್ವಾಹಕ", - "core.user.newpicture": "ಹೊಸ ಚಿತ್ರ", - "core.user.participants": "ಭಾಗಿಗಳು", - "core.user.phone1": "ದೂರವಾಣಿ", - "core.user.phone2": "ಜಂಗಮವಾಣಿ", - "core.user.sendemail": "ಮಿಂಚಂಚೆ", - "core.user.student": "ವಿದ್ಯಾರ್ಥಿ", - "core.user.teacher": "ಪರಿಷ್ಕರಣೆ ಮಾಡಲಾಗದ ಶಿಕ್ಷಕರು", - "core.user.webpage": "ಅಂತರ್ಜಾಲ ಪುಟ", - "core.userdetails": "ಬಳಕೆದಾರ ಮಾಹಿತಿ", - "core.users": "ಬಳಕೆದಾರರು", - "core.view": "ನೋಟ", - "core.viewprofile": "ಕಿರುಪರಿಚಯವನ್ನು ವೀಕ್ಷಿಸಿ", - "core.warningofflinedatadeleted": "{{component}} '{{name}}' ನಿಂದ ಆಫ್‌ಲೈನ್‌ ದತ್ತಾಂಶವನ್ನು ಅಳಿಸಲಾಗಿದೆ. {{error}}", - "core.whoops": "ಅಯ್ಯೋ!", - "core.whyisthishappening": "ಏಕೆ ಈಗಾಗುತ್ತಿದೆ?", - "core.wsfunctionnotavailable": "ವೆಬ್‌ ಸೇವಾ ಕಾರ್ಯವು ಈಗ ದೊರಕುತ್ತಿಲ್ಲ", - "core.year": "ವರ್ಷ", - "core.years": "ವರ್ಷಗಳು", - "core.yes": "ಹೌದು" -} \ No newline at end of file diff --git a/src/assets/lang/ko.json b/src/assets/lang/ko.json deleted file mode 100644 index 5e94546a0..000000000 --- a/src/assets/lang/ko.json +++ /dev/null @@ -1,1593 +0,0 @@ -{ - "addon.badges.badgedetails": "뱃지 세부사항", - "addon.badges.badges": "뱃지", - "addon.badges.contact": "연락처", - "addon.badges.dateawarded": "발행일", - "addon.badges.expired": "만료됨", - "addon.badges.expirydate": "만료일", - "addon.badges.issuancedetails": "뱃지 만료기한", - "addon.badges.issuerdetails": "발행자 세부정보", - "addon.badges.issuername": "발행자 이름", - "addon.badges.issuerurl": "발행자 URL", - "addon.badges.language": "언어", - "addon.badges.nobadges": "사용가능한 뱃지가 없습니다.", - "addon.badges.recipientdetails": "수신자 세부사항", - "addon.badges.warnexpired": "(이 뱃지는 만료되었습니다)", - "addon.block_activitymodules.pluginname": "학습활동", - "addon.block_badges.pluginname": "나의 최근 뱃지", - "addon.block_blogmenu.pluginname": "블로그 메뉴", - "addon.block_blogrecent.pluginname": "최근 블로그 기록", - "addon.block_blogtags.pluginname": "블로그 태그", - "addon.block_calendarmonth.pluginname": "달력", - "addon.block_calendarupcoming.pluginname": "예정된 행사", - "addon.block_comments.pluginname": "덧글", - "addon.block_completionstatus.pluginname": "강좌이수완료 상황", - "addon.block_glossaryrandom.pluginname": "무작위 용어집 입력항목", - "addon.block_newsitems.pluginname": "최신 뉴스", - "addon.block_onlineusers.pluginname": "온라인 사용자", - "addon.block_privatefiles.pluginname": "개인 파일", - "addon.block_recentactivity.pluginname": "최근 활동", - "addon.block_rssclient.pluginname": "RSS 수신자", - "addon.block_selfcompletion.pluginname": "자기주도 완료", - "addon.block_sitemainmenu.pluginname": "주 메뉴", - "addon.block_tags.pluginname": "태그", - "addon.blog.blog": "블로그", - "addon.blog.blogentries": "블로그 항목", - "addon.blog.linktooriginalentry": "원본 블로그로 연결", - "addon.blog.noentriesyet": "볼수있는 게시물이 없습니다.", - "addon.blog.publishtonoone": "자신에게만(초안)", - "addon.blog.publishtosite": "이 사이트에 있는 누구에게나", - "addon.blog.publishtoworld": "누구에게나", - "addon.calendar.allday": "모든 날", - "addon.calendar.calendar": "달력", - "addon.calendar.calendarevents": "달력 일정", - "addon.calendar.confirmeventdelete": "이 일정을 삭제할까요?", - "addon.calendar.courseevents": "강좌일정", - "addon.calendar.defaultnotificationtime": "기본 알림 시간", - "addon.calendar.deleteevent": "일정 삭제하기", - "addon.calendar.durationminutes": "유지 시간(분으로)", - "addon.calendar.durationnone": "기간 제한없이", - "addon.calendar.durationuntil": "까지", - "addon.calendar.editevent": "일정 편집", - "addon.calendar.errorloadevent": "이벤트 올리기 오류", - "addon.calendar.errorloadevents": "이벤트 올리기 오류", - "addon.calendar.eventcalendareventdeleted": "달력 이벤트가 삭제되었습니다.", - "addon.calendar.eventduration": "기간", - "addon.calendar.eventendtime": "끝나는 시간", - "addon.calendar.eventkind": "행사 유형", - "addon.calendar.eventname": "이름", - "addon.calendar.eventstarttime": "시작 시간", - "addon.calendar.fri": "금", - "addon.calendar.friday": "금요일", - "addon.calendar.groupevents": "모듬 일정", - "addon.calendar.invalidtimedurationminutes": "입력한 지속 시간(분)이 정확하지 않습니다. 0 이상의 값을 넣거나 지속시간 없음을 선택하세요.", - "addon.calendar.invalidtimedurationuntil": "행사 종료 일시가 행사 시작 일시보다 이릅니다. 계속하기 전에 이를 바로잡기 바랍니다.", - "addon.calendar.mon": "월", - "addon.calendar.monday": "월요일", - "addon.calendar.monthlyview": "월별로 보기", - "addon.calendar.newevent": "새로운 일정", - "addon.calendar.noevents": "이벤트 없음", - "addon.calendar.nopermissiontoupdatecalendar": "죄송합니다. 달력 일정을 업데이트 할 권한을 가지고 있지 않습니다.", - "addon.calendar.repeatedevents": "반복된 일정", - "addon.calendar.repeateditall": "반복된 일련의 모든 {{$a}} 일정 변경", - "addon.calendar.repeateditthis": "이 일정만 변경", - "addon.calendar.repeatevent": "이 일정 반복", - "addon.calendar.repeatweeksl": "매주 반복, 모두 생성함", - "addon.calendar.sat": "토", - "addon.calendar.saturday": "토요일", - "addon.calendar.siteevents": "사이트 이벤트", - "addon.calendar.sun": "일", - "addon.calendar.sunday": "일요일", - "addon.calendar.thu": "목", - "addon.calendar.thursday": "목요일", - "addon.calendar.today": "오늘", - "addon.calendar.tomorrow": "내일", - "addon.calendar.tue": "화", - "addon.calendar.tuesday": "화요일", - "addon.calendar.typecourse": "강좌 일정", - "addon.calendar.typegroup": "모둠 일정", - "addon.calendar.typesite": "사이트 일정", - "addon.calendar.typeuser": "사용자 일정", - "addon.calendar.upcomingevents": "예정된 행사", - "addon.calendar.userevents": "사용자 일정", - "addon.calendar.wed": "수", - "addon.calendar.wednesday": "수요일", - "addon.calendar.yesterday": "어제", - "addon.competency.errornocompetenciesfound": "역량을 찾지 못했습니다.", - "addon.competency.nocompetencies": "역량 없음", - "addon.coursecompletion.complete": "완전한", - "addon.coursecompletion.completecourse": "강좌 완료", - "addon.coursecompletion.completed": "완료됨", - "addon.coursecompletion.completiondate": "이수 날짜", - "addon.coursecompletion.completionmenuitem": "이수", - "addon.coursecompletion.couldnotloadreport": "강좌 완료 보고서를 로드 할 수 없습니다. 나중에 다시 시도 해주십시오.", - "addon.coursecompletion.coursecompletion": "강좌 완료", - "addon.coursecompletion.criteria": "기준", - "addon.coursecompletion.criteriagroup": "기준 모둠", - "addon.coursecompletion.criteriarequiredall": "아래의 모든 기준이 필요합니다.", - "addon.coursecompletion.criteriarequiredany": "아래의 어떤 기준도 필요합니다,", - "addon.coursecompletion.inprogress": "진행 중", - "addon.coursecompletion.manualselfcompletion": "강좌이수 수동확인", - "addon.coursecompletion.nottracked": "당신은 현재 이 강좌에서 완료에 의해 추적되고 있지 않습니다", - "addon.coursecompletion.notyetstarted": "아직 시작 안했습니다.", - "addon.coursecompletion.pending": "유예", - "addon.coursecompletion.required": "필수사항", - "addon.coursecompletion.requiredcriteria": "필수 기준", - "addon.coursecompletion.requirement": "필수", - "addon.coursecompletion.status": "상태", - "addon.coursecompletion.viewcoursereport": "강좌 보고서 보기", - "addon.files.couldnotloadfiles": "파일 목록을 로드할 수 없습니다.", - "addon.files.emptyfilelist": "표시 할 파일이 없습니다.", - "addon.files.erroruploadnotworking": "현재 귀하의 사이트에 파일을 업로드하는 것은 불가능합니다.", - "addon.files.files": "파일", - "addon.files.sitefiles": "파일 창고", - "addon.messageoutput_airnotifier.processorsettingsdesc": "장치 구성", - "addon.messages.acceptandaddcontact": "수락하고 연락처에 추가", - "addon.messages.addcontact": "연락 추가", - "addon.messages.addtoyourcontacts": "연락처에 추가", - "addon.messages.blocknoncontacts": "연락처에 없는 사람들이 나에게 메세지 보내는 것 방지", - "addon.messages.blockuser": "사용자 차단", - "addon.messages.blockuserconfirm": "{{$a)님을 차단하시겠습니까?", - "addon.messages.contactableprivacy": "메시지 수락할 대상:", - "addon.messages.contactableprivacy_coursemember": "내 연락처 및 내 강좌 등록생", - "addon.messages.contactableprivacy_onlycontacts": "내 연락처만 표시", - "addon.messages.contactblocked": "차단된 연락처", - "addon.messages.contactlistempty": "연락처가 비어 있습니다.", - "addon.messages.contactname": "연락처 이름", - "addon.messages.contactrequestsent": "요청 연락처 보냄", - "addon.messages.contacts": "연락처", - "addon.messages.conversationactions": "대화 실행 메뉴", - "addon.messages.decline": "거절", - "addon.messages.deleteallconfirm": "정말 이 모든 대화를 삭제하시겠습니까? 다른 참석자들한테는 적용되지 않을 것입니다.", - "addon.messages.deleteallselfconfirm": "이 모든 사적인 대화를 삭제하시겠습니까?", - "addon.messages.deleteconversation": "대화내용 삭제", - "addon.messages.deleteforeveryone": "나와 다른 모든 사람에게서 삭제", - "addon.messages.errordeletemessage": "메시지를 지우는 중 오류 발생", - "addon.messages.errorwhileretrievingcontacts": "서버에서 연락처를 검색하는 동안 오류 발생", - "addon.messages.errorwhileretrievingdiscussions": "서버에서 토론을 가져 오는 중에 오류 발생", - "addon.messages.errorwhileretrievingmessages": "서버에서 메시지를 검색하는 중 오류 발생", - "addon.messages.groupconversations": "그룹", - "addon.messages.groupinfo": "그룹 정보", - "addon.messages.info": "사용자 정보", - "addon.messages.isnotinyourcontacts": "{{$a}}님은 연락처에 없습니다.", - "addon.messages.message": "메세지", - "addon.messages.messagenotsent": "메시지가 전송되지 않았습니다. 다시 시도해 주세요.", - "addon.messages.messagepreferences": "메세지 설정", - "addon.messages.messages": "메세지", - "addon.messages.muteconversation": "소리 죽임", - "addon.messages.newmessage": "새 메세지", - "addon.messages.newmessages": "새로운 메시지", - "addon.messages.nomessagesfound": "발견된 메세지 없음", - "addon.messages.nousersfound": "사용자가 없습니다.", - "addon.messages.numparticipants": "참가자 {{$a}}", - "addon.messages.removecontact": "연락처 제거", - "addon.messages.removecontactconfirm": "연락처에서 {{$a}}님을 삭제하시겠습니까?", - "addon.messages.removefromyourcontacts": "연락처에서 삭제", - "addon.messages.requirecontacttomessage": "{{$a}}님에게 메세지를 보내려면 미리 내 연락처 추가를 요청해야 합니다.", - "addon.messages.searchcombined": "복합 검색", - "addon.messages.type_blocked": "차단된", - "addon.messages.type_offline": "오프라인", - "addon.messages.type_online": "온라인", - "addon.messages.type_search": "검색 결과", - "addon.messages.type_strangers": "기타", - "addon.messages.warningmessagenotsent": "{{user}} 사용자에게 메시지를 보낼 수 없습니다. {{error}}", - "addon.mod_assign.acceptsubmissionstatement": "제출 명세서를 수락하십시오.", - "addon.mod_assign.addattempt": "또 다른 시도 추가", - "addon.mod_assign.addnewattempt": "새 시도 추가", - "addon.mod_assign.addnewattemptfromprevious": "이전 제출에 기반한 새 시도 추가", - "addon.mod_assign.addsubmission": "제출 추가", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "과제 세부사항과 제출 양식이 {{$a}} 부터 사용가능합니다.", - "addon.mod_assign.allowsubmissionsfromdate": "제출 시작일:", - "addon.mod_assign.allowsubmissionsfromdatesummary": "이 과제는 {{$a}}부터 제출이 가능합니다.", - "addon.mod_assign.applytoteam": "모둠 전체에 성적과 피드백 적용", - "addon.mod_assign.assignmentisdue": "과제 제출 마감 시한", - "addon.mod_assign.attemptnumber": "시도 수", - "addon.mod_assign.attemptreopenmethod": "시도 재개", - "addon.mod_assign.attemptreopenmethod_manual": "수동으로", - "addon.mod_assign.attemptreopenmethod_untilpass": "통과할때까지 자동으로", - "addon.mod_assign.attemptsettings": "시도 설정", - "addon.mod_assign.cannoteditduetostatementsubmission": "제출 문을 사이트에서 검색 할 수 없으므로 앱에서 제출을 추가하거나 편집 할 수 없습니다.", - "addon.mod_assign.cannotgradefromapp": "특정 채점 방법은 아직 앱에서 지원되지 않으며 수정할 수 없습니다.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "제출 명세서를 사이트에서 검색 할 수 없기 때문에 앱에서 제출할 수 없습니다.", - "addon.mod_assign.confirmsubmission": "채점을 위해 과제를 제출 하시겠습니까? 제출하면 이상 변경할 수 없습니다.", - "addon.mod_assign.currentattempt": "시도 {{$a}}입니다.", - "addon.mod_assign.currentattemptof": "시도 {{$a.attemptnumber}} ({{$a.maxattempts}} 시도가 허용됩니다.)", - "addon.mod_assign.currentgrade": "성적부에서 현재 성적", - "addon.mod_assign.cutoffdate": "최종 마감일", - "addon.mod_assign.defaultteam": "기본 모둠", - "addon.mod_assign.duedate": "마감 일시", - "addon.mod_assign.duedateno": "무기한", - "addon.mod_assign.duedatereached": "이 과제 제출 마감일이 지났습니다.", - "addon.mod_assign.editingstatus": "상태 편집", - "addon.mod_assign.editsubmission": "제출물 편집", - "addon.mod_assign.erroreditpluginsnotsupported": "특정 플러그인이 아직 편집을 지원하지 않기 때문에 앱에서 제출을 추가하거나 편집 할 수 없습니다.", - "addon.mod_assign.errorshowinginformation": "제출 정보를 표시 할 수 없습니다.", - "addon.mod_assign.extensionduedate": "제출일 연장", - "addon.mod_assign.feedbacknotsupported": "이 의견은 앱에서 지원하지 않으며 모든 정보가 포함되어 있지 않을 수도 있습니다.", - "addon.mod_assign.grade": "성적", - "addon.mod_assign.graded": "채점됨", - "addon.mod_assign.gradedby": "채점자:", - "addon.mod_assign.gradedfollowupsubmit": "채점됨 - 추후 제출 수령됨", - "addon.mod_assign.gradedon": "채점일:", - "addon.mod_assign.gradelocked": "해당 점수가 채점표에서 잠겼거나 덮어 써졌습니다.", - "addon.mod_assign.gradenotsynced": "성적이 동기화 안됨", - "addon.mod_assign.gradeoutof": "{{$a}} 중 채점", - "addon.mod_assign.gradingstatus": "채점 상태", - "addon.mod_assign.groupsubmissionsettings": "모둠 제출 설정", - "addon.mod_assign.hiddenuser": "참가자", - "addon.mod_assign.latesubmissions": "늦은 제출", - "addon.mod_assign.latesubmissionsaccepted": "연장 허가를 받은 학생들만 아직 과제를 제출할 수 있습니다", - "addon.mod_assign.markingworkflowstate": "표시 진행 상태", - "addon.mod_assign.markingworkflowstateinmarking": "표시 중", - "addon.mod_assign.markingworkflowstateinreview": "검토 중", - "addon.mod_assign.markingworkflowstatenotmarked": "표시되지 않음", - "addon.mod_assign.markingworkflowstatereadyforrelease": "공개 준비 완료", - "addon.mod_assign.markingworkflowstatereadyforreview": "채점 완료", - "addon.mod_assign.markingworkflowstatereleased": "공개됨", - "addon.mod_assign.modulenameplural": "과제물들", - "addon.mod_assign.multipleteams": "한 그룹 이상의 회원", - "addon.mod_assign.multipleteams_desc": "본 과제는 그룹 제출 과제입니다. 당신은 두 그룹 이상의 회원입니다. 과제 제출을 위해서는 반드시 한 곳에만 속해 있어야 합니다. 가입된 그룹을 변경 하려면 담당 선생님께 문의하시기 바랍니다.", - "addon.mod_assign.noattempt": "시도 하지 않음", - "addon.mod_assign.nomoresubmissionsaccepted": "기한 연장이 인정된 참가자들에게만 허용됨", - "addon.mod_assign.noonlinesubmissions": "이 과제는 온라인으로 제출하는 것을 요구하지 않습니다.", - "addon.mod_assign.nosubmission": "이 과제에 대해 제출된 것이 없습니다.", - "addon.mod_assign.notallparticipantsareshown": "제출하지 않은 참가자는 표시되지 않습니다.", - "addon.mod_assign.noteam": "어느 그룹의 회원도 아님", - "addon.mod_assign.noteam_desc": "본 과제는 그룹 제출 과제입니다. 당신은 어떤 그룹에도 속해 있지 않기 때문에, 과제물을 제출할 수 없습니다. 그룹에 참여하려면 담당 선생님에게 연락하시기 바랍니다.", - "addon.mod_assign.notgraded": "채점되지 않음", - "addon.mod_assign.numberofdraftsubmissions": "초안", - "addon.mod_assign.numberofparticipants": "참가자들", - "addon.mod_assign.numberofsubmissionsneedgrading": "채점이 필요합니다.", - "addon.mod_assign.numberofsubmittedassignments": "제출함", - "addon.mod_assign.numberofteams": "그룹들", - "addon.mod_assign.numwords": "{{$a}} 단어", - "addon.mod_assign.outof": "{{$a.total}}중 {{$a.current}}", - "addon.mod_assign.overdue": "과제 제출 기한이 {{$a}} 지났습니다, ", - "addon.mod_assign.submission": "제출물", - "addon.mod_assign.submissioneditable": "학생들은 이 제출물을 편집할 수 있습니다.", - "addon.mod_assign.submissionnoteditable": "학생들은 이 제출물을 편집할 수 없습니다.", - "addon.mod_assign.submissionnotsupported": "이 제출은 앱에서 지원하지 않으며 모든 정보가 포함되어 있지 않을 수도 있습니다.", - "addon.mod_assign.submissionslocked": "이 과제는 제출을 받지 않습니다.", - "addon.mod_assign.submissionstatus": "제출 상태", - "addon.mod_assign.submissionstatus_": "제출이 없습니다.", - "addon.mod_assign.submissionstatus_draft": "초안(제출 되지 않았음)", - "addon.mod_assign.submissionstatus_marked": "채점됨", - "addon.mod_assign.submissionstatus_new": "제출물 없음", - "addon.mod_assign.submissionstatus_reopened": "다시 오픈됨", - "addon.mod_assign.submissionstatus_submitted": "채점을 위해 제출되었습니다.", - "addon.mod_assign.submissionstatusheading": "제출 상태", - "addon.mod_assign.submissionteam": "그룹", - "addon.mod_assign.submitassignment": "과제 제출", - "addon.mod_assign.submitassignment_help": "과제가 제출되면 더 이상 변경할 수 없습니다.", - "addon.mod_assign.submittedearly": "과제가 {{$a}} 일찍 제출되었습니다.", - "addon.mod_assign.submittedlate": "과제가 {{$a}} 늦게 제출되었습니다.", - "addon.mod_assign.timemodified": "마지막 수정", - "addon.mod_assign.timeremaining": "남은 시간", - "addon.mod_assign.ungroupedusers": "'제출하기 위해서는 그룹이 요구됨'이란 설정이 활성화되어 있는데, 사용자가 어느 그룹의 회원도 아니거나 또 두 그룹 이상의 회원이라면 제출할 수 없습니다.", - "addon.mod_assign.ungroupedusersoptional": "'학생들은 그룹으로 제출할 것'이라는 설정이 활성화되어 있는데 일부 사용자가 그룹의 구성원이 아니거나 둘 이상의 그룹의 구성원입니다. 이런 학생들은 '기본 그룹'의 회원으로 제출됨을 유념하십시오.", - "addon.mod_assign.unlimitedattempts": "무제한", - "addon.mod_assign.userswhoneedtosubmit": "제출이 필요한 사용자 : {{$a}}", - "addon.mod_assign.userwithid": "ID가 {{id}} 인 사용자", - "addon.mod_assign.viewsubmission": "제출 보기", - "addon.mod_assign.warningsubmissiongrademodified": "제출 등급이 사이트에서 수정되었습니다.", - "addon.mod_assign.warningsubmissionmodified": "사용자 제출이 사이트에서 수정되었습니다.", - "addon.mod_assign_feedback_comments.pluginname": "피드백 코멘트", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF 주석추가", - "addon.mod_assign_feedback_file.pluginname": "파일 피드백", - "addon.mod_assign_submission_comments.pluginname": "제출 코멘트", - "addon.mod_assign_submission_file.pluginname": "파일 제출", - "addon.mod_assign_submission_onlinetext.pluginname": "온라인 텍스트 제출", - "addon.mod_book.errorchapter": "책의 장을 읽는데 오류", - "addon.mod_book.modulenameplural": "책", - "addon.mod_book.toc": "목차", - "addon.mod_chat.beep": "호출", - "addon.mod_chat.chatreport": "대화방 보고서", - "addon.mod_chat.currentusers": "현재 참여자", - "addon.mod_chat.enterchat": "대화에 참여하려면 여기를 클릭!", - "addon.mod_chat.entermessage": "메세지를 입력하세요.", - "addon.mod_chat.errorwhileconnecting": "채팅에 연결하는 중에 오류가 발생했습니다.", - "addon.mod_chat.errorwhilegettingchatdata": "채팅 데이터를 가져 오는 중 오류가 발생했습니다.", - "addon.mod_chat.errorwhilegettingchatusers": "채팅 사용자를 받는 동안 오류가 발생했습니다.", - "addon.mod_chat.errorwhileretrievingmessages": "서버에서 메시지를 검색하는 중 오류가 발생했습니다.", - "addon.mod_chat.errorwhilesendingmessage": "메시지를 보내는 중 오류가 발생했습니다.", - "addon.mod_chat.messagebeepseveryone": "{{$a}}가 모든 사람을 호출했음!", - "addon.mod_chat.messagebeepsyou": "{{$a}}가 나를 호출했음!", - "addon.mod_chat.messageenter": "{{$a}} 대화방에 들어옴", - "addon.mod_chat.messageexit": "{{$a}} 대화방을 나감", - "addon.mod_chat.messages": "메시지", - "addon.mod_chat.messageyoubeep": "{{$a}}를 호출", - "addon.mod_chat.modulenameplural": "대화모음", - "addon.mod_chat.mustbeonlinetosendmessages": "메시지를 보내려면 온라인 상태 여야합니다.", - "addon.mod_chat.nomessages": "아직 메시지 없음", - "addon.mod_chat.saidto": "말함", - "addon.mod_chat.send": "전송", - "addon.mod_chat.sessionstart": "대화방 세션이 {{$a}} 에 시작할 것입니다.", - "addon.mod_chat.talk": "말하기", - "addon.mod_chat.viewreport": "지난 대화 보기", - "addon.mod_choice.choiceoptions": "설문 요건", - "addon.mod_choice.errorgetchoice": "선택 데이터를 가져 오는 중 오류가 발생했습니다.", - "addon.mod_choice.expired": "죄송합니다. 이 활동은 {{$a}} 에 종료되어서 더 이상 사용할 수 없습니다.", - "addon.mod_choice.full": "(마감됨)", - "addon.mod_choice.modulenameplural": "간편설문", - "addon.mod_choice.noresultsviewable": "지금은 결과를 볼 수 없습니다.", - "addon.mod_choice.notopenyet": "죄송합니다만, {{$a}} 까지는 이용할 수 없습니다.", - "addon.mod_choice.numberofuser": "사용자", - "addon.mod_choice.numberofuserinpercentage": "백분율로 환산한 사용자 수", - "addon.mod_choice.removemychoice": "기존 응답 취소", - "addon.mod_choice.responses": "응답", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}%의 사용자가 {{text}} 옵션을 선택했습니다.", - "addon.mod_choice.responsesresultgraphheader": "그래프 표시", - "addon.mod_choice.resultsnotsynced": "최종 응답은 결과에 포함되기 전에 동기화 되어야 합니다.", - "addon.mod_choice.savemychoice": "응답내용 저장", - "addon.mod_choice.userchoosethisoption": "사용자가 이 옵션을 선택했습니다.", - "addon.mod_choice.yourselection": "당신의 선택", - "addon.mod_data.addentries": "내용 추가", - "addon.mod_data.advancedsearch": "고급 검색", - "addon.mod_data.alttext": "상응 문구", - "addon.mod_data.approve": "승인", - "addon.mod_data.approved": "승인됨", - "addon.mod_data.ascending": "오름차순", - "addon.mod_data.authorfirstname": "저자의 이름", - "addon.mod_data.authorlastname": "저자의 성", - "addon.mod_data.confirmdeleterecord": "이 게시물을 삭제하려고 하는 것이 확실합니까?", - "addon.mod_data.descending": "내림차순", - "addon.mod_data.disapprove": "승인 취소", - "addon.mod_data.emptyaddform": "어떤 항목도 기입하지 않았습니다!", - "addon.mod_data.entrieslefttoadd": "이 활동을 완료하려면 {{$a.entriesleft}} 개 이상의 항목을 입력해야만 합니다.", - "addon.mod_data.entrieslefttoaddtoview": "다른 참여자의 내용을 보기 전에 {{$a.entrieslefttoview}} 항목을 더 추가해야만 합니다.", - "addon.mod_data.errorapproving": "입력을 승인 또는 비 승인하는 중에 오류가 발생했습니다.", - "addon.mod_data.errordeleting": "항목을 삭제하는 중 오류가 발생했습니다.", - "addon.mod_data.expired": "죄송합니다만, 이 활동은 {{$a}}에 종료되었으므로 더 이상 이용할 수 없습니다.", - "addon.mod_data.fields": "항목들", - "addon.mod_data.foundrecords": "기록 발견 : {{$a.num}}/{{$a.max}} (필터 초기화)", - "addon.mod_data.menuchoose": "선택...", - "addon.mod_data.modulenameplural": "데이터베이스", - "addon.mod_data.more": "더 이상", - "addon.mod_data.nomatch": "해당되는 게시물이 없음!", - "addon.mod_data.norecords": "데이터베이스에 입력된 내용 없음", - "addon.mod_data.notapproved": "아직 입력을 받을 수 없음", - "addon.mod_data.notopenyet": "죄송합니다만 이 활동은 {{$a}} 가 될 때까지 이용할 수 없습니다.", - "addon.mod_data.numrecords": "{{$a}} 게시물", - "addon.mod_data.other": "기타", - "addon.mod_data.recordapproved": "게시물이 허용됨", - "addon.mod_data.recorddeleted": "게시물이 삭제됨", - "addon.mod_data.resetsettings": "필터 초기화", - "addon.mod_data.search": "검색", - "addon.mod_data.selectedrequired": "모든 선택사항 필요", - "addon.mod_data.single": "한개 보기", - "addon.mod_data.timeadded": "추가된 시간", - "addon.mod_data.timemodified": "변경된 시간", - "addon.mod_data.usedate": "검색에 포함합니다.", - "addon.mod_feedback.analysis": "분석", - "addon.mod_feedback.anonymous": "익명", - "addon.mod_feedback.anonymous_entries": "익명 응답", - "addon.mod_feedback.average": "평균", - "addon.mod_feedback.captchaofflinewarning": "보안 문자로 인한 피드백은 오프라인이거나, 구성되지 않은 경우 또는 서버가 다운 된 경우 완료할 수 없습니다.", - "addon.mod_feedback.complete_the_form": "질문에 답하세요", - "addon.mod_feedback.completed_feedbacks": "제출된 답", - "addon.mod_feedback.continue_the_form": "양식 계속", - "addon.mod_feedback.feedback_is_not_open": "피드백이 아직 시작되지 않았음", - "addon.mod_feedback.feedback_submitted_offline": "이 피드백은 나중에 제출되도록 저장되었습니다.", - "addon.mod_feedback.feedbackclose": "응답 허용", - "addon.mod_feedback.feedbackopen": "답안 입력 시작 시간", - "addon.mod_feedback.mapcourses": "피드백을 강좌에 연결", - "addon.mod_feedback.maximal": "최대의", - "addon.mod_feedback.mode": "모드", - "addon.mod_feedback.modulenameplural": "피드백 활동", - "addon.mod_feedback.next_page": "다음 페이지", - "addon.mod_feedback.non_anonymous": "기명, 응답내용 공개", - "addon.mod_feedback.non_anonymous_entries": "익명 기록 없음", - "addon.mod_feedback.non_respondents_students": "응답한 학생 없음", - "addon.mod_feedback.not_selected": "선택되지 않았음", - "addon.mod_feedback.not_started": "개시하지 않음", - "addon.mod_feedback.overview": "요약", - "addon.mod_feedback.page_after_submit": "완료 메세지", - "addon.mod_feedback.preview": "미리보기", - "addon.mod_feedback.previous_page": "이전 페이지", - "addon.mod_feedback.questions": "질문들", - "addon.mod_feedback.response_nr": "응답 수", - "addon.mod_feedback.responses": "응답들", - "addon.mod_feedback.save_entries": "응답 제출", - "addon.mod_feedback.show_entries": "응답 보기", - "addon.mod_feedback.show_nonrespondents": "응답 안한 사람 보기", - "addon.mod_feedback.started": "시작되었음", - "addon.mod_feedback.this_feedback_is_already_submitted": "당신은 이미 이 활동을 완료하였습니다.", - "addon.mod_folder.emptyfilelist": "표시할 파일이 없습니다.", - "addon.mod_folder.modulenameplural": "폴더", - "addon.mod_forum.addanewdiscussion": "새 토론 주제 추가", - "addon.mod_forum.addanewquestion": "새 질문 추가", - "addon.mod_forum.addanewtopic": "새로운 주제 추가", - "addon.mod_forum.advanced": "고급", - "addon.mod_forum.cannotadddiscussion": "포럼에 의견을 제시하려면 모둠의 구성원이어야 합니다.", - "addon.mod_forum.cannotadddiscussionall": "공동의 토론 주제 추가 권한이 없습니다.", - "addon.mod_forum.cannotcreatediscussion": "새 토론을 생성할 수 없음", - "addon.mod_forum.couldnotadd": "알 수 없는 오류로 인해 게시할 수 없음", - "addon.mod_forum.couldnotupdate": "알 수 없는 오류로 인해 업데이트 할 수 없음", - "addon.mod_forum.delete": "삭제", - "addon.mod_forum.deletedpost": "그 게시물은 삭제되었음", - "addon.mod_forum.deletesure": "정말 이 게시물을 삭제하겠습니까?", - "addon.mod_forum.discussion": "제목", - "addon.mod_forum.edit": "수정", - "addon.mod_forum.erroremptymessage": "게시 메세지는 비어 있을 수 없습니다.", - "addon.mod_forum.erroremptysubject": "제목이 없으면 안됩니다.", - "addon.mod_forum.errorgetforum": "포럼 데이터를 가져 오는 중 오류가 발생했습니다.", - "addon.mod_forum.errorgetgroups": "그룹 설정을 가져 오는 중 오류가 발생했습니다.", - "addon.mod_forum.forumnodiscussionsyet": "이 포럼에는 아직 토론이 없습니다.", - "addon.mod_forum.group": "그룹", - "addon.mod_forum.lastpost": "최근 게시", - "addon.mod_forum.message": "메세지", - "addon.mod_forum.modeflatnewestfirst": "새 답글부터 내용 보기", - "addon.mod_forum.modeflatoldestfirst": "옛 답글부터 내용 보기", - "addon.mod_forum.modenested": "주제 중심으로 답글 보기", - "addon.mod_forum.modulenameplural": "포럼모음", - "addon.mod_forum.numdiscussions": "{{numdiscussions}}개의 토론", - "addon.mod_forum.numreplies": "{{numreplies}}개의 답글", - "addon.mod_forum.posttoforum": "포럼에 올리기", - "addon.mod_forum.re": "회신:", - "addon.mod_forum.refreshdiscussions": "토론 새로 고침", - "addon.mod_forum.refreshposts": "소식 새로 고침", - "addon.mod_forum.reply": "답글", - "addon.mod_forum.subject": "제목", - "addon.mod_forum.unread": "읽지 않음", - "addon.mod_forum.unreadpostsnumber": "{{$a}} 개의 읽지 않은 글", - "addon.mod_forum.yourreply": "당신의 답글", - "addon.mod_glossary.addentry": "새 항목 쓰기", - "addon.mod_glossary.aliases": "검색어", - "addon.mod_glossary.attachment": "붙임파일", - "addon.mod_glossary.browsemode": "항목 찾아보기", - "addon.mod_glossary.byalphabet": "알파벳순", - "addon.mod_glossary.byauthor": "저자별 그룹화", - "addon.mod_glossary.bycategory": "카테고리별 그룹화", - "addon.mod_glossary.bynewestfirst": "최신순", - "addon.mod_glossary.byrecentlyupdated": "최근업데이트순", - "addon.mod_glossary.bysearch": "검색", - "addon.mod_glossary.cannoteditentry": "항목을 편집할 수 없습니다.", - "addon.mod_glossary.casesensitive": "대,소문자 구분", - "addon.mod_glossary.categories": "범주", - "addon.mod_glossary.concept": "개념", - "addon.mod_glossary.definition": "정의", - "addon.mod_glossary.entriestobesynced": "동기화된 항목들", - "addon.mod_glossary.entrypendingapproval": "이 항목은 승인 대기중입니다.", - "addon.mod_glossary.entryusedynalink": "이 항목은 자동적으로 링크됨", - "addon.mod_glossary.errconceptalreadyexists": "이미 있는 내용입니다. 이 용어집에서는 중복입력을 허용하지 않습니다.", - "addon.mod_glossary.errorloadingentries": "항목을 로드하는 중 오류가 발생했습니다.", - "addon.mod_glossary.errorloadingentry": "항목을 로드하는 중 오류가 발생했습니다.", - "addon.mod_glossary.errorloadingglossary": "용어집을 로드하는 중 오류가 발생했습니다.", - "addon.mod_glossary.fillfields": "필수적으로 채워넣어야 하는 항목", - "addon.mod_glossary.fullmatch": "완전히 일치하는 단어만", - "addon.mod_glossary.linking": "자동 연결", - "addon.mod_glossary.modulenameplural": "용어활용", - "addon.mod_glossary.noentriesfound": "항목을 찾을 수 없습니다.", - "addon.mod_glossary.searchquery": "질문 검색", - "addon.mod_imscp.deploymenterror": "콘텐츠 패키지 오류!", - "addon.mod_imscp.modulenameplural": "IMS 콘덴츠팩키지", - "addon.mod_imscp.showmoduledescription": "설명 표시", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "답안", - "addon.mod_lesson.attempt": "{{$a}} 번째 시도", - "addon.mod_lesson.attemptsremaining": "{{$a}} 번의 시도 남음", - "addon.mod_lesson.averagescore": "평균 점수", - "addon.mod_lesson.averagetime": "평균 시간", - "addon.mod_lesson.branchtable": "콘텐츠", - "addon.mod_lesson.cannotfindattempt": "오류: 시도를 찾을 수 없음", - "addon.mod_lesson.cannotfinduser": "오류: 사용자를 찾을 수 없음", - "addon.mod_lesson.clusterjump": "질문묶음에서 보지 않은 질문", - "addon.mod_lesson.completed": "이수", - "addon.mod_lesson.congratulations": "학습의 끝입니다 - 축하합니다", - "addon.mod_lesson.continue": "계속", - "addon.mod_lesson.continuetonextpage": "다음 페이지로 가기", - "addon.mod_lesson.defaultessayresponse": "선생님이 당신의 에세이를 평가할 것입니다.", - "addon.mod_lesson.detailedstats": "자세한 통계", - "addon.mod_lesson.didnotanswerquestion": "이 질문에 답하지 않았음", - "addon.mod_lesson.displayofgrade": "성적 표시 (학생만)", - "addon.mod_lesson.displayscorewithessays": "자동으로 채점되는 질문에 대해 {{$a.tempmaxgrade}} 점 중 {{$a.score}} 점을 얻었습니다.
                당신의{{$a.essayquestions}} 에세이 질문(들)은 추후에 채점될 것이며 최종 점수에 추가될 것입니다.
                에세이 질문(들)을 제외한 점수는 현재 {{$a.grade}} 점 중에서 {{$a.score}} 점을 받았습니다.", - "addon.mod_lesson.displayscorewithoutessays": "당신의 점수는 {{$a.score}} 점 입니다.({{$a.grade}} 점 만점)", - "addon.mod_lesson.emptypassword": "암호는 공백일 수 없습니다.", - "addon.mod_lesson.enterpassword": "비밀번호를 입력하세요 :", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "당신은 답변을 전혀 하지 않았습니다.\n이번 학습에서 0점을 얻게 되었습니다.", - "addon.mod_lesson.errorprefetchrandombranch": "이 강의에는 임의의 콘텐츠 페이지로 이동하는 내용이 포함되어 있습니다. 웹 브라우저에서 시작될 때까지 앱에서 시도 할 수 없습니다.", - "addon.mod_lesson.errorreviewretakenotlast": "이 시도는 다른 시도가 끝났기 때문에 더 이상 검토 할 수 없습니다.", - "addon.mod_lesson.finish": "종료", - "addon.mod_lesson.finishretakeoffline": "이 시도는 오프라인으로 완료되었습니다.", - "addon.mod_lesson.firstwrong": "답이 틀렸기 때문에 점수를 얻을 수 없습니다. 그냥 재미로 계속 해보겠습니까?(맞아도 점수 추가는 없습니다.)", - "addon.mod_lesson.gotoendoflesson": "완전학습의 끝으로 가기", - "addon.mod_lesson.grade": "성적", - "addon.mod_lesson.highscore": "고득점", - "addon.mod_lesson.hightime": "최장 시간", - "addon.mod_lesson.leftduringtimed": "당신은 규정된 학습시간에 자리를 비웠습니다.
                \n학습을 다시 시작하려면 계속 버튼을 눌러주세요.", - "addon.mod_lesson.leftduringtimednoretake": "당신은 규정된 학습시간에 자리를 비웠기 때문에
                재학습을 하거나 계속할 수 없습니다.", - "addon.mod_lesson.lessonmenu": "완전 학습 메뉴", - "addon.mod_lesson.lessonstats": "완전학습 통계", - "addon.mod_lesson.linkedmedia": "연결된 매체", - "addon.mod_lesson.loginfail": "로그인에 실패했습니다, 다시 시도하세요.", - "addon.mod_lesson.lowscore": "낮은 점수", - "addon.mod_lesson.lowtime": "최단 시간", - "addon.mod_lesson.maximumnumberofattemptsreached": "최대 허용 시도횟수에 도달하였습니다. 다음 페이지로 갑니다.", - "addon.mod_lesson.modattemptsnoteacher": "검토과정은 학생에게만 해당됨", - "addon.mod_lesson.modulenameplural": "완전학습", - "addon.mod_lesson.noanswer": "답을 하지 않았습니다. 되돌아 가서 답을 입력하세요.", - "addon.mod_lesson.nolessonattempts": "이 학습에 대해 아무런 시도도 없었음.", - "addon.mod_lesson.notcompleted": "완료하지 않았음", - "addon.mod_lesson.numberofcorrectanswers": "정답 수: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "응답한 질문의 수: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "응답한 질문수 : {{$a.nquestions}} (최소한 {{$a.minquestions}} 개 답해야 합니다.)", - "addon.mod_lesson.ongoingcustom": "당신은 {{$a.currenthigh}} (최고)점 중 {{$a.score}} 점입니다.", - "addon.mod_lesson.ongoingnormal": "당신은 {{$a.viewed}} 개의 질문 중 {{$a.correct}} 질문에 정확한 답을 했습니다.", - "addon.mod_lesson.or": "또는", - "addon.mod_lesson.overview": "개요", - "addon.mod_lesson.preview": "미리보기", - "addon.mod_lesson.progressbarteacherwarning2": "본 학습을 편집할 수 있으므로 진척상황막대는 볼 수 없음", - "addon.mod_lesson.progresscompleted": "완전 학습의 {{$a}}를 완료하였습니다.", - "addon.mod_lesson.question": "질문", - "addon.mod_lesson.rawgrade": "원 성적", - "addon.mod_lesson.reports": "보고서", - "addon.mod_lesson.response": "반응", - "addon.mod_lesson.retakefinishedinsync": "오프라인 시도가 동기화 되었습니다. 검토하겠습니까?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "검토", - "addon.mod_lesson.reviewlesson": "학습 검토하기", - "addon.mod_lesson.reviewquestionback": "예, 다시하겠습니다.", - "addon.mod_lesson.reviewquestioncontinue": "아니오, 다음 질문으로 넘어가겠습니다.", - "addon.mod_lesson.secondpluswrong": "정확하지 않습니다. 다시 하시겠습니까?", - "addon.mod_lesson.submit": "제출", - "addon.mod_lesson.teacherjumpwarning": "이 완전학습에서 {{$a.cluster}}나 {{$a.unseen}} 으로의 이동 과정이 사용되고 있습니다. 다음 페이지 이동이 대신 사용될 수 있습니다. 이들 이동을 점검하기 위해서는 학생으로 로그인하십시요.", - "addon.mod_lesson.teacherongoingwarning": "현재 점수는 학생들에게만 보여집니다. 현재 점수를 확인하기 위해서는 학생으로 로그인하십시오.", - "addon.mod_lesson.teachertimerwarning": "타이머는 학생들을 위해서만 작동됩니다. 학생으로 로그인 하여 타이머를 점검하세요.", - "addon.mod_lesson.thatsthecorrectanswer": "올바른 답 입니다.", - "addon.mod_lesson.thatsthewronganswer": "잘못된 답 입니다.", - "addon.mod_lesson.timeremaining": "남은 시간", - "addon.mod_lesson.timetaken": "시간이 걸렸음", - "addon.mod_lesson.unseenpageinbranch": "콘텐츠 페이지 내 보지 않은 질문", - "addon.mod_lesson.warningretakefinished": "사이트에서 시도가 완료되었습니다.", - "addon.mod_lesson.welldone": "잘했어요!", - "addon.mod_lesson.youhaveseen": "당신은 이미 이 학습을 시도한 적이 있습니다.
                도중에 끝마쳤던 부분부터 시작하길 원합니까?", - "addon.mod_lesson.youranswer": "당신의 대답", - "addon.mod_lesson.yourcurrentgradeisoutof": "현재 성적은 {{$a.total}} 중 {{$a.grade}} 입니다.", - "addon.mod_lesson.youshouldview": "당신은 적어도 {{$a}} 에 답해야만 합니다.", - "addon.mod_lti.errorgetlti": "모듈 데이터를 가져 오는 중 오류가 발생했습니다.", - "addon.mod_lti.errorinvalidlaunchurl": "시작 URL이 유효하지 않습니다.", - "addon.mod_lti.launchactivity": "활동 시작", - "addon.mod_lti.modulenameplural": "기본 LTI", - "addon.mod_page.errorwhileloadingthepage": "페이지 내용을 로드하는 중 오류가 발생했습니다.", - "addon.mod_page.modulenameplural": "웹페이지", - "addon.mod_quiz.answercolon": "답:", - "addon.mod_quiz.attemptfirst": "첫번째 시도", - "addon.mod_quiz.attemptlast": "마지막 시도", - "addon.mod_quiz.attemptnumber": "시도", - "addon.mod_quiz.attemptquiznow": "퀴즈 풀기 시작", - "addon.mod_quiz.attemptstate": "상태", - "addon.mod_quiz.cannotsubmitquizdueto": "이 퀴즈 시도는 다음과 같은 이유로 제출될 수 없습니다 :", - "addon.mod_quiz.comment": "덧글", - "addon.mod_quiz.completedon": "완료됨", - "addon.mod_quiz.confirmclose": "당신은 이 시도를 끝내려고 합니다. 일단 시도를 종료하면 더 이상 답을 고칠 수 없습니다.", - "addon.mod_quiz.confirmcontinueoffline": "이 시도는 {{$a}} 이후로 동기화되지 않았습니다. 그 이후로 다른 장치에서 이 시도를 계속하면 데이터가 손실될 수 있습니다.", - "addon.mod_quiz.confirmleavequizonerror": "답변을 저장하는 중 오류가 발생했습니다. 퀴즈를 종료 하시겠습니까?", - "addon.mod_quiz.continueattemptquiz": "지난번 시도 계속", - "addon.mod_quiz.continuepreview": "미리보기 계속", - "addon.mod_quiz.errorbehaviournotsupported": "앱에서 질문 동작을 지원하지 않으므로 앱에서 이 퀴즈를 시도할 수 없습니다.", - "addon.mod_quiz.errordownloading": "필수 데이터를 다운로드하는 중 오류가 발생했습니다.", - "addon.mod_quiz.errorgetattempt": "시도 데이터를 가져 오는 중 오류가 발생했습니다.", - "addon.mod_quiz.errorgetquestions": "질문을 가져 오는 중에 오류가 발생했습니다.", - "addon.mod_quiz.errorgetquiz": "퀴즈 데이터를 가져 오는 중 오류가 발생했습니다.", - "addon.mod_quiz.errorparsequestions": "질문을 읽는 중에 오류가 발생했습니다. 웹 브라우저에서 이 퀴즈를 시도하십시오.", - "addon.mod_quiz.errorquestionsnotsupported": "이 퀴즈는 앱에서 지원하지 않는 질문을 포함하고 있기 때문에 앱에서 시도 할 수 없습니다.", - "addon.mod_quiz.errorrulesnotsupported": "이 퀴즈는 앱에서 지원하지 않는 액세스 규칙을 가지고 있기 때문에 앱에서 시도 할 수 없습니다.", - "addon.mod_quiz.errorsaveattempt": "시도 데이터를 저장하는 중 오류가 발생했습니다.", - "addon.mod_quiz.feedback": "피드백", - "addon.mod_quiz.finishattemptdots": "시도 종료", - "addon.mod_quiz.finishnotsynced": "완료되었지만 동기화되지 않았습니다.", - "addon.mod_quiz.grade": "성적", - "addon.mod_quiz.gradeaverage": "평균 점수", - "addon.mod_quiz.gradehighest": "최고 점수", - "addon.mod_quiz.grademethod": "채점 방법", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "점수", - "addon.mod_quiz.modulenameplural": "퀴즈", - "addon.mod_quiz.mustbesubmittedby": "이 시도는 {{$a}}가 제출해야 합니다.", - "addon.mod_quiz.noquestions": "아직 퀴즈가 추가되지 않음", - "addon.mod_quiz.noreviewattempt": "이 시도를 검토하도록 허용되지 않았습니다.", - "addon.mod_quiz.notyetgraded": "아직 채점되지 않음", - "addon.mod_quiz.opentoc": "탐색 메뉴 열기", - "addon.mod_quiz.outof": "최대 {{$a.maxgrade}} 중 {{$a.grade}}", - "addon.mod_quiz.outofpercent": "최대 {{$a.maxgrade}} 중 {{$a.grade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "전반적인 피드백", - "addon.mod_quiz.overdue": "기한초과", - "addon.mod_quiz.preview": "미리보기", - "addon.mod_quiz.previewquiznow": "지금 퀴즈 미리보기", - "addon.mod_quiz.question": "질문", - "addon.mod_quiz.quiznavigation": "퀴즈 찾아가기", - "addon.mod_quiz.quizpassword": "퀴즈 암호", - "addon.mod_quiz.reattemptquiz": "퀴즈에 재도전", - "addon.mod_quiz.requirepasswordmessage": "이 퀴즈를 풀려면 비밀번호를 알아야 함", - "addon.mod_quiz.returnattempt": "시도로 돌아가기", - "addon.mod_quiz.review": "재검토하기", - "addon.mod_quiz.reviewofattempt": "{{$a}} 차 시도 검토", - "addon.mod_quiz.reviewofpreview": "미리보기 검토", - "addon.mod_quiz.showall": "한 페이지에 모든 질문 보기", - "addon.mod_quiz.showeachpage": "한 페이지를 한꺼번에 보이기", - "addon.mod_quiz.startattempt": "시도 시작", - "addon.mod_quiz.startedon": "시작", - "addon.mod_quiz.stateabandoned": "제출되지 않았습니다.", - "addon.mod_quiz.statefinished": "종료됨", - "addon.mod_quiz.statefinisheddetails": "{{$a}}를 제출함", - "addon.mod_quiz.stateinprogress": "진행중", - "addon.mod_quiz.stateoverdue": "기한 만료", - "addon.mod_quiz.stateoverduedetails": "{{$a}}가 제출해야 합니다.", - "addon.mod_quiz.status": "현황", - "addon.mod_quiz.submitallandfinish": "모두 제출하고 끝냄", - "addon.mod_quiz.summaryofattempt": "시도 개요", - "addon.mod_quiz.summaryofattempts": "이전 시도들에 대한 요약", - "addon.mod_quiz.timeleft": "남은 시간", - "addon.mod_quiz.timetaken": "걸린 시간", - "addon.mod_quiz.warningattemptfinished": "사이트에서 완료되었거나 찾을 수 없으므로 오프라인 시도가 취소되었습니다.", - "addon.mod_quiz.warningdatadiscarded": "질문이 온라인으로 수정 되었기 때문에 일부 오프라인 응답이 삭제되었습니다.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "일부 오프라인 응답이 삭제 되었기 때문에 미완료 상태로 시도하십시오. 답변을 검토 한 다음 다시 제출하십시오.", - "addon.mod_quiz.yourfinalgradeis": "이번 퀴즈의 최종 점수는 {{$a}} 입니다.", - "addon.mod_resource.errorwhileloadingthecontent": "콘텐츠를 로드하는 중 오류가 발생했습니다.", - "addon.mod_resource.modulenameplural": "파일", - "addon.mod_resource.openthefile": "파일 열기", - "addon.mod_scorm.asset": "에셋", - "addon.mod_scorm.assetlaunched": "에셋 - 보았음", - "addon.mod_scorm.attempts": "시도들", - "addon.mod_scorm.averageattempt": "평균시도들", - "addon.mod_scorm.browse": "미리보기", - "addon.mod_scorm.browsed": "보았음", - "addon.mod_scorm.browsemode": "미리보기 모드", - "addon.mod_scorm.cannotcalculategrade": "등급을 계산할 수 없습니다.", - "addon.mod_scorm.completed": "완료됨", - "addon.mod_scorm.contents": "목차", - "addon.mod_scorm.dataattemptshown": "이 데이터는 시도 번호 {{number}}에 속합니다.", - "addon.mod_scorm.enter": "입력", - "addon.mod_scorm.errorcreateofflineattempt": "새 오프라인 시도를 만드는 동안 오류가 발생했습니다. 다시 시도하십시오.", - "addon.mod_scorm.errordownloadscorm": "SCORM을 다운로드하는 중 오류가 발생했습니다 : \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "SCORM 데이터를 가져 오는 중 오류가 발생했습니다.", - "addon.mod_scorm.errorinvalidversion": "죄송합니다. 이 애플리케이션은 SCORM 1.2 만 지원합니다.", - "addon.mod_scorm.errornotdownloadable": "SCORM 패키지 다운로드가 사용 중지되었습니다. 사이트 관리자에게 문의하십시오.", - "addon.mod_scorm.errornovalidsco": "이 SCORM 패키지에는 로드 할 수있는 SCO가 없습니다.", - "addon.mod_scorm.errorpackagefile": "죄송합니다. 이 애플리케이션은 ZIP 패키지만 지원합니다.", - "addon.mod_scorm.errorsyncscorm": "동기화 중 오류가 발생했습니다. 다시 시도하십시오.", - "addon.mod_scorm.exceededmaxattempts": "최대 시도 한계에 도달", - "addon.mod_scorm.failed": "실패함", - "addon.mod_scorm.firstattempt": "처음 시도", - "addon.mod_scorm.gradeaverage": "평균 성적", - "addon.mod_scorm.gradeforattempt": "시도 성적", - "addon.mod_scorm.gradehighest": "최고 성적", - "addon.mod_scorm.grademethod": "채점 방법", - "addon.mod_scorm.gradereported": "보고된 성적", - "addon.mod_scorm.gradescoes": "학습 객체", - "addon.mod_scorm.gradesum": "성적 합계", - "addon.mod_scorm.highestattempt": "최고 시도", - "addon.mod_scorm.incomplete": "미완성됨", - "addon.mod_scorm.lastattempt": "마지막 시도", - "addon.mod_scorm.modulenameplural": "스콤 패키지", - "addon.mod_scorm.newattempt": "새로운 시도 시작하기", - "addon.mod_scorm.noattemptsallowed": "허용된 시도 수", - "addon.mod_scorm.noattemptsmade": "시도한 횟수", - "addon.mod_scorm.notattempted": "시도하지 않았음", - "addon.mod_scorm.offlineattemptnote": "이 시도에는 동기화되지 않은 데이터가 있습니다.", - "addon.mod_scorm.offlineattemptovermax": "최대 시도 횟수를 초과했기 때문에이 시도를 보낼 수 없습니다.", - "addon.mod_scorm.organizations": "조직들", - "addon.mod_scorm.passed": "통과됨", - "addon.mod_scorm.reviewmode": "재검토 모드", - "addon.mod_scorm.score": "점수", - "addon.mod_scorm.scormstatusnotdownloaded": "이 SCORM 패키지가 다운로드되지 않았습니다. 열 때 자동으로 다운로드됩니다.", - "addon.mod_scorm.scormstatusoutdated": "이 SCORM 패키지는 마지막 다운로드 이후 수정되었습니다. 열 때 자동으로 다운로드됩니다.", - "addon.mod_scorm.suspended": "보류됨", - "addon.mod_scorm.toc": "강좌구조", - "addon.mod_scorm.warningofflinedatadeleted": "{{number}} 시도의 일부 오프라인 데이터는 새로운 시도로 계산할 수 없으므로 삭제되었습니다.", - "addon.mod_scorm.warningsynconlineincomplete": "마지막 시도가 아직 완료되지 않았기 때문에 일부 시도가 사이트와 동기화 될 수 없습니다. 먼저 온라인 시도를 완료하십시오.", - "addon.mod_survey.cannotsubmitsurvey": "죄송합니다. 설문 조사를 제출하는 중에 문제가 발생했습니다. 다시 시도하십시오.", - "addon.mod_survey.errorgetsurvey": "설문 데이터를 가져 오는 중 오류가 발생했습니다.", - "addon.mod_survey.ifoundthat": "을 발견하다.", - "addon.mod_survey.ipreferthat": "을 더 좋아하다.", - "addon.mod_survey.modulenameplural": "설문조사", - "addon.mod_survey.responses": "응답", - "addon.mod_survey.results": "결과", - "addon.mod_url.accessurl": "URL 접속", - "addon.mod_url.modulenameplural": "URLs", - "addon.mod_url.pointingtourl": "리소스가 가리키는 URL입니다.", - "addon.mod_wiki.cannoteditpage": "이 페이지를 편집 할 수 없습니다.", - "addon.mod_wiki.createpage": "페이지 만들기", - "addon.mod_wiki.editingpage": "페이지 '{{$a}}' 편집 중", - "addon.mod_wiki.errorloadingpage": "페이지를 로드하는 중 오류가 발생했습니다.", - "addon.mod_wiki.errornowikiavailable": "이 위키는 아직 내용이 없습니다.", - "addon.mod_wiki.gowikihome": "위키 첫 페이지로 이동", - "addon.mod_wiki.map": "맵", - "addon.mod_wiki.modulenameplural": "위키문서", - "addon.mod_wiki.newpagehdr": "세 페이지", - "addon.mod_wiki.newpagetitle": "새 페이지 제목", - "addon.mod_wiki.nocontent": "이 페이지에 내용이 없습니다.", - "addon.mod_wiki.notingroup": "모둠에 없음", - "addon.mod_wiki.pageexists": "이 페이지는 이미 존재합니다. 그곳으로 넘어갑니다.", - "addon.mod_wiki.pagename": "페이지 이름", - "addon.mod_wiki.subwiki": "하위 위키", - "addon.mod_wiki.titleshouldnotbeempty": "제목은 반드시 입력해야 합니다.", - "addon.mod_wiki.viewpage": "페이지 보기", - "addon.mod_wiki.wikipage": "위키 페이지", - "addon.mod_wiki.wrongversionlock": "당신이 편집하는 동안 다른 사용자가 이 페이지를 편집하였으며 당신이 편집한 내용은 쓸모없게 되었습니다.", - "addon.mod_workshop.alreadygraded": "이미 채점되었습니다.", - "addon.mod_workshop.areainstructauthors": "제출 요령", - "addon.mod_workshop.areainstructreviewers": "평가 요령", - "addon.mod_workshop.assess": "평가", - "addon.mod_workshop.assessedsubmission": "평가된 제출물", - "addon.mod_workshop.assessmentform": "평가 양식", - "addon.mod_workshop.assessmentsettings": "평가 설정", - "addon.mod_workshop.assessmentstrategynotsupported": "평가 전략 {{$a}}이(가) 지원되지 않습니다.", - "addon.mod_workshop.assessmentweight": "평가 가중치", - "addon.mod_workshop.assignedassessments": "평가해야할 제출물", - "addon.mod_workshop.assignedassessmentsnone": "평가해야할 제출물이 없음", - "addon.mod_workshop.conclusion": "결론", - "addon.mod_workshop.createsubmission": "제출", - "addon.mod_workshop.editsubmission": "제출 수정", - "addon.mod_workshop.feedbackauthor": "저자에 대한 피드백", - "addon.mod_workshop.feedbackby": "{{$a}}에 의한 피드백", - "addon.mod_workshop.feedbackreviewer": "평가자에 대한 피드백", - "addon.mod_workshop.givengrades": "부여된 성적", - "addon.mod_workshop.gradecalculated": "제출물 성적", - "addon.mod_workshop.gradeinfo": "성적 : {{$a.max}} 중 {{$a.received}} ", - "addon.mod_workshop.gradeover": "제출 성적 덮어쓰기", - "addon.mod_workshop.gradesreport": "상호평가 성적 보고서", - "addon.mod_workshop.gradinggrade": "평가 성적", - "addon.mod_workshop.gradinggradecalculated": "계산완료된 자기평가 성적", - "addon.mod_workshop.gradinggradeof": "자기평가 성적 ({{$a}}) ", - "addon.mod_workshop.gradinggradeover": "평가 성적 덮어쓰기", - "addon.mod_workshop.modulenameplural": "상호평가", - "addon.mod_workshop.nogradeyet": "아직 성적 없음", - "addon.mod_workshop.notassessed": "아직 평가하지 않음", - "addon.mod_workshop.notoverridden": "덮어쓰여지지 않음", - "addon.mod_workshop.noyoursubmission": "아직 제출한 과제가 없음", - "addon.mod_workshop.overallfeedback": "전반적인 피드백", - "addon.mod_workshop.publishedsubmissions": "공개된 제출물", - "addon.mod_workshop.publishsubmission": "제출물 공개", - "addon.mod_workshop.publishsubmission_help": "공개된 제출물은 상호평가가 종료되면 다른 사람들에게 제공됩니다.", - "addon.mod_workshop.reassess": "재평가", - "addon.mod_workshop.receivedgrades": "부여받은 성적", - "addon.mod_workshop.submissionattachment": "첨부", - "addon.mod_workshop.submissioncontent": "제출 내역", - "addon.mod_workshop.submissiongrade": "제출 성적", - "addon.mod_workshop.submissiongradeof": "({{$a}} 의) 제출 성적", - "addon.mod_workshop.submissiontitle": "제목", - "addon.mod_workshop.userplan": "상호평가 설계자", - "addon.mod_workshop.warningassessmentmodified": "사이트에서 제출이 수정되었습니다.", - "addon.mod_workshop.warningsubmissionmodified": "평가가 사이트에서 수정되었습니다.", - "addon.mod_workshop.weightinfo": "가중치: {{$a}}", - "addon.mod_workshop.yourassessment": "자체 평가", - "addon.mod_workshop.yourgrades": "성적", - "addon.mod_workshop.yoursubmission": "내 제출물", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "관점 {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "이 관점에 대한 성적을 선택해야 합니다.", - "addon.mod_workshop_assessment_comments.dimensionnumber": "\t\n관점 {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "주장 {{$a}} ", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "기준 {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "이 항목중 하나를 골라야만 함", - "addon.notes.addnewnote": "새 학습관찰 추가", - "addon.notes.coursenotes": "강좌별 학습관찰", - "addon.notes.deleteconfirm": "이 내용을 삭제할까요?", - "addon.notes.eventnotecreated": "노트가 작성됨", - "addon.notes.eventnotedeleted": "노트가 삭제됨", - "addon.notes.nonotes": "내용이 없습니다", - "addon.notes.note": "학습관찰", - "addon.notes.notes": "학습관찰", - "addon.notes.personalnotes": "개인적 학습관찰", - "addon.notes.publishstate": "문맥", - "addon.notes.sitenotes": "사이트 학습관찰", - "addon.notes.userwithid": "ID가 {{id}}인 사용자", - "addon.notes.warningnotenotsent": "{{course}} 강좌에 메모를 추가 할 수 없습니다. {{error}}", - "addon.notifications.errorgetnotifications": "알림을 가져 오는 중 오류가 발생했습니다.", - "addon.notifications.notifications": "알림", - "addon.notifications.playsound": "소리 재생", - "addon.notifications.therearentnotificationsyet": "알림이 없습니다.", - "assets.countries.AD": "안도라", - "assets.countries.AE": "아랍 에미리트 연합국", - "assets.countries.AF": "아프가니스탄", - "assets.countries.AG": "앤티가 바부다", - "assets.countries.AI": "안귈라", - "assets.countries.AL": "알바니아", - "assets.countries.AM": "아르메니아", - "assets.countries.AO": "앙골라", - "assets.countries.AQ": "남극 대륙", - "assets.countries.AR": "아르헨티나", - "assets.countries.AS": "아메리칸 사모아", - "assets.countries.AT": "오스트리아", - "assets.countries.AU": "오스트레일리아(호주)", - "assets.countries.AW": "아루바", - "assets.countries.AX": "올란드 제도", - "assets.countries.AZ": "아제르바이잔", - "assets.countries.BA": "보스니아 헤르체코비나", - "assets.countries.BB": "바베이도스", - "assets.countries.BD": "방글라데시", - "assets.countries.BE": "벨기에", - "assets.countries.BF": "부르키나 파소", - "assets.countries.BG": "불가리아", - "assets.countries.BH": "바레인", - "assets.countries.BI": "부룬디", - "assets.countries.BJ": "베닌", - "assets.countries.BL": "세인트 바르텔미", - "assets.countries.BM": "버뮤다", - "assets.countries.BN": "브루나이 다루살램", - "assets.countries.BO": "볼리비아", - "assets.countries.BQ": "보네르, 신트 유스타티우스 및 사바", - "assets.countries.BR": "브라질", - "assets.countries.BS": "바하마", - "assets.countries.BT": "부탄", - "assets.countries.BV": "뷔벳 섬", - "assets.countries.BW": "보츠와나", - "assets.countries.BY": "벨로루시", - "assets.countries.BZ": "벨리즈", - "assets.countries.CA": "캐나다", - "assets.countries.CC": "코코스 섬", - "assets.countries.CD": "콩고 민주 공화국", - "assets.countries.CF": "중앙 아프리카 공화국", - "assets.countries.CG": "콩고", - "assets.countries.CH": "스위스", - "assets.countries.CI": "코트디부아르", - "assets.countries.CK": "쿡 섬", - "assets.countries.CL": "칠레", - "assets.countries.CM": "카메룬", - "assets.countries.CN": "중국", - "assets.countries.CO": "콜롬비아", - "assets.countries.CR": "코스타리카", - "assets.countries.CU": "쿠바", - "assets.countries.CV": "카보베르데", - "assets.countries.CW": "큐라", - "assets.countries.CX": "크리스마스 섬", - "assets.countries.CY": "키프로스", - "assets.countries.CZ": "체코 공화국", - "assets.countries.DE": "독일", - "assets.countries.DJ": "지부티", - "assets.countries.DK": "덴마크", - "assets.countries.DM": "도미니카", - "assets.countries.DO": "도미니카 공화국", - "assets.countries.DZ": "알제리", - "assets.countries.EC": "에콰도르", - "assets.countries.EE": "에스토니아", - "assets.countries.EG": "이집트", - "assets.countries.EH": "사하라 서부지역", - "assets.countries.ER": "에리트레아", - "assets.countries.ES": "스페인", - "assets.countries.ET": "에디오피아", - "assets.countries.FI": "핀란드", - "assets.countries.FJ": "피지", - "assets.countries.FK": "포클랜드 제도", - "assets.countries.FM": "미크로네시아 연방주립국", - "assets.countries.FO": "페로스 제도", - "assets.countries.FR": "프랑스", - "assets.countries.GA": "가봉", - "assets.countries.GB": "영국", - "assets.countries.GD": "그레나다", - "assets.countries.GE": "그루지아", - "assets.countries.GF": "프랑스령 기아나", - "assets.countries.GG": "건지", - "assets.countries.GH": "가나", - "assets.countries.GI": "지브롤터", - "assets.countries.GL": "그린란드", - "assets.countries.GM": "감비아", - "assets.countries.GN": "기니", - "assets.countries.GP": "구아델로프", - "assets.countries.GQ": "적도 기니", - "assets.countries.GR": "그리스", - "assets.countries.GS": "남조지아사우스샌드위치제도", - "assets.countries.GT": "과테말라", - "assets.countries.GU": "괌", - "assets.countries.GW": "기니비사우", - "assets.countries.GY": "가이아나", - "assets.countries.HK": "홍콩", - "assets.countries.HM": "허드, 맥 도날드 섬", - "assets.countries.HN": "온두라스", - "assets.countries.HR": "크로아티아", - "assets.countries.HT": "아이티", - "assets.countries.HU": "헝가리", - "assets.countries.ID": "인도네시아", - "assets.countries.IE": "아일랜드", - "assets.countries.IL": "이스라엘", - "assets.countries.IM": "아일 오브 맨", - "assets.countries.IN": "인디아", - "assets.countries.IO": "영국령 인도양 식민지", - "assets.countries.IQ": "이라크", - "assets.countries.IR": "이란", - "assets.countries.IS": "아이슬란드", - "assets.countries.IT": "이탈리아", - "assets.countries.JE": "저지", - "assets.countries.JM": "자메이카", - "assets.countries.JO": "요르단", - "assets.countries.JP": "일본", - "assets.countries.KE": "케냐", - "assets.countries.KG": "키르기스스탄", - "assets.countries.KH": "캄보디아", - "assets.countries.KI": "키리바시", - "assets.countries.KM": "코모로", - "assets.countries.KN": "세인트크리스토퍼 네비스", - "assets.countries.KP": "북한", - "assets.countries.KR": "대한민국", - "assets.countries.KW": "쿠웨이트", - "assets.countries.KY": "케이맨 제도", - "assets.countries.KZ": "카자흐스탄", - "assets.countries.LA": "라오스 인민 민주주의 공화국", - "assets.countries.LB": "레바논", - "assets.countries.LC": "세인트루시아", - "assets.countries.LI": "리히텐슈타인", - "assets.countries.LK": "스리랑카", - "assets.countries.LR": "라이베리아", - "assets.countries.LS": "레소토", - "assets.countries.LT": "리투아니아", - "assets.countries.LU": "룩셈부르크", - "assets.countries.LV": "라트비아", - "assets.countries.LY": "리비아 아랍 자매히리아", - "assets.countries.MA": "모로코", - "assets.countries.MC": "모나코", - "assets.countries.MD": "몰도바", - "assets.countries.ME": "몬테네그로", - "assets.countries.MF": "생마르탱", - "assets.countries.MG": "마다가스카르", - "assets.countries.MH": "마샬 군도", - "assets.countries.MK": "마케도니아", - "assets.countries.ML": "말리", - "assets.countries.MM": "미얀마", - "assets.countries.MN": "몽골", - "assets.countries.MO": "마카우", - "assets.countries.MP": "마리아나 제도", - "assets.countries.MQ": "마티니크", - "assets.countries.MR": "모리타니아", - "assets.countries.MS": "몬크세랫", - "assets.countries.MT": "몰타", - "assets.countries.MU": "모리셔스", - "assets.countries.MV": "몰디브", - "assets.countries.MW": "말라위", - "assets.countries.MX": "멕시코", - "assets.countries.MY": "말레이시아", - "assets.countries.MZ": "모잠비크", - "assets.countries.NA": "나미비아", - "assets.countries.NC": "뉴칼레도니아", - "assets.countries.NE": "니제르", - "assets.countries.NF": "노퍽 섬", - "assets.countries.NG": "나이지리아", - "assets.countries.NI": "니카라과", - "assets.countries.NL": "네덜란드", - "assets.countries.NO": "노르웨이", - "assets.countries.NP": "네팔", - "assets.countries.NR": "나우루", - "assets.countries.NU": "뉘에", - "assets.countries.NZ": "뉴질랜드", - "assets.countries.OM": "오만", - "assets.countries.PA": "파나마", - "assets.countries.PE": "페루", - "assets.countries.PF": "프랑스 폴리네시아", - "assets.countries.PG": "파푸아뉴기니", - "assets.countries.PH": "필리핀", - "assets.countries.PK": "파키스탄", - "assets.countries.PL": "폴란드", - "assets.countries.PM": "세인트 피에르, 미퀴론", - "assets.countries.PN": "피타카이른", - "assets.countries.PR": "푸에르토리코", - "assets.countries.PS": "팔레스티나", - "assets.countries.PT": "포르투갈", - "assets.countries.PW": "팔라우", - "assets.countries.PY": "파라과이", - "assets.countries.QA": "카타르", - "assets.countries.RE": "리유니온", - "assets.countries.RO": "루마니아", - "assets.countries.RS": "세르비아", - "assets.countries.RU": "러시아 연방", - "assets.countries.RW": "르완다", - "assets.countries.SA": "사우디 아라비아", - "assets.countries.SB": "솔로몬 제도", - "assets.countries.SC": "세이셸", - "assets.countries.SD": "수단", - "assets.countries.SE": "스웨덴", - "assets.countries.SG": "싱가포르", - "assets.countries.SH": "세인트헬레나", - "assets.countries.SI": "슬로베니아 공화국", - "assets.countries.SJ": "스발바드, 잔 메이엔 섬", - "assets.countries.SK": "슬로바키아", - "assets.countries.SL": "시에라리온", - "assets.countries.SM": "산마리노", - "assets.countries.SN": "세네갈", - "assets.countries.SO": "소말리아", - "assets.countries.SR": "수리남", - "assets.countries.SS": "남 수단", - "assets.countries.ST": "상투메 프린시페", - "assets.countries.SV": "엘살바도르", - "assets.countries.SX": "신트 마틴 (네덜란드령)", - "assets.countries.SY": "실리아 아랍 공화국", - "assets.countries.SZ": "스와질란드", - "assets.countries.TC": "턱스, 카이코스 섬", - "assets.countries.TD": "차드", - "assets.countries.TF": "프랑스 서부지역", - "assets.countries.TG": "토고", - "assets.countries.TH": "타이(태국)", - "assets.countries.TJ": "타지키스탄", - "assets.countries.TK": "토켈라우", - "assets.countries.TL": "동티모르", - "assets.countries.TM": "투르크메니스탄", - "assets.countries.TN": "튀니지", - "assets.countries.TO": "통가", - "assets.countries.TR": "터키", - "assets.countries.TT": "트리니다드토바고", - "assets.countries.TV": "투발루", - "assets.countries.TW": "타이완(대만)", - "assets.countries.TZ": "탄자니아", - "assets.countries.UA": "우크라이나", - "assets.countries.UG": "우간다", - "assets.countries.UM": "미국령 소군도", - "assets.countries.US": "미국", - "assets.countries.UY": "우루과이", - "assets.countries.UZ": "우즈베키스탄", - "assets.countries.VA": "바티칸 국가도시", - "assets.countries.VC": "세인트빈센트 그레나딘", - "assets.countries.VE": "베네수엘라", - "assets.countries.VG": "버진 제도(영국)", - "assets.countries.VI": "버진 제도(미국)", - "assets.countries.VN": "베트남", - "assets.countries.VU": "바누아투", - "assets.countries.WF": "월리스, 푸투나 섬", - "assets.countries.WS": "사모아", - "assets.countries.YE": "예멘", - "assets.countries.YT": "메이오티", - "assets.countries.ZA": "남아프리카 공화국", - "assets.countries.ZM": "잠비아", - "assets.countries.ZW": "짐바브웨", - "assets.mimetypes.application/epub_zip": "EPUB 전자책", - "assets.mimetypes.application/msword": "워드 문서", - "assets.mimetypes.application/pdf": "PDF 문서", - "assets.mimetypes.application/vnd.moodle.backup": "무들 백업", - "assets.mimetypes.application/vnd.ms-excel": "엑셀 문서", - "assets.mimetypes.application/vnd.ms-powerpoint": "파워포인트 문서", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "파워포인트 발표", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "파워포인트 슬라이드쇼", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "엑셀 스프레드쉬트", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "엑셀 템플릿", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "워드 문서", - "assets.mimetypes.archive": "아카이브 ({{$a.EXT}})", - "assets.mimetypes.audio": "오디오 파일 ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "파일", - "assets.mimetypes.image": "이미지 ({{$a.MIMETYPE2}})", - "assets.mimetypes.text/html": "HTML 문서", - "assets.mimetypes.text/plain": "텍스트 파일", - "assets.mimetypes.text/rtf": "RTF 문서", - "core.accounts": "계정", - "core.add": "추가", - "core.ago": "{{$a}} 이전", - "core.all": "모두", - "core.allgroups": "모든 모둠", - "core.allparticipants": "모든 참가자", - "core.answer": "대답", - "core.answered": "응답했음", - "core.areyousure": "계속하시겠습니까?", - "core.back": "뒤로", - "core.block.blocks": "블록", - "core.cancel": "취소", - "core.cannotconnect": "연결할 수 없음: URL을 정확히 입력했는지 그리고 사이트가 Moodle {{$a}} 이상을 사용하고 있는지 확인하십시오.", - "core.cannotdownloadfiles": "파일 다운로드가 비활성화되었습니다. 사이트 관리자에게 문의하십시오.", - "core.captureaudio": "오디오 녹음", - "core.capturedimage": "사진을 찍었습니다.", - "core.captureimage": "사진 촬영", - "core.capturevideo": "비디오 녹화", - "core.category": "범주", - "core.choose": "선택", - "core.choosedots": "선택...", - "core.clearsearch": "명확한 검색", - "core.clicktohideshow": "펴거나 접으려면 클릭", - "core.clicktoseefull": "전체 내용을 보려면 클릭하십시오.", - "core.close": "닫기", - "core.comments": "덧글", - "core.comments.addcomment": "덧글 추가 ...", - "core.comments.comments": "덧글", - "core.comments.commentscount": "댓글 ({{$a}})", - "core.comments.eventcommentcreated": "코멘트 작성됨", - "core.comments.eventcommentdeleted": "코멘트 삭제됨", - "core.comments.nocomments": "덧글 없음", - "core.comments.savecomment": "덧글 저장", - "core.commentscount": "댓글 ({{$a}})", - "core.completion-alt-auto-fail": "이수함(통과 성적을 획득하지 못함)", - "core.completion-alt-auto-n": "미이수", - "core.completion-alt-auto-pass": "이수함(통과 성적 획득)", - "core.completion-alt-auto-y": "이수함", - "core.completion-alt-manual-n": "완료하지 않음; 완료된것으로 표시하려면 선택하에요.", - "core.completion-alt-manual-y": "완료함; 완료되지 않은것으로 표시하려면 선택하에요.", - "core.confirmcanceledit": "이 페이지에서 나가시겠습니까? 모든 변경 사항이 손실됩니다.", - "core.confirmdeletefile": "이 파일을 정말 지우겠습니까?", - "core.confirmloss": "확실합니까? 모든 변경 사항이 손실됩니다.", - "core.confirmopeninbrowser": "웹 브라우저에서 여시겠습니까?", - "core.content": "내용", - "core.contenteditingsynced": "수정중인 콘텐츠가 동기화되었습니다.", - "core.contentlinks.chooseaccount": "계정 선택", - "core.contentlinks.chooseaccounttoopenlink": "링크를 열 계정을 선택하십시오.", - "core.contentlinks.confirmurlothersite": "이 링크는 다른 사이트에 속합니다. 열어 보시겠습니까?", - "core.contentlinks.errornoactions": "이 링크로 수행 할 작업을 찾을 수 없습니다.", - "core.contentlinks.errornosites": "이 링크를 처리 할 사이트를 찾을 수 없습니다.", - "core.continue": "계속", - "core.copiedtoclipboard": "클립 보드에 복사 된 텍스트", - "core.course": "강좌", - "core.course.activitydisabled": "당신의 조직에서 모바일 앱에서이 활동을 사용 중지했습니다.", - "core.course.activitynotyetviewableremoteaddon": "조직에서 아직 지원되지 않는 플러그인을 설치했습니다.", - "core.course.activitynotyetviewablesiteupgradeneeded": "귀사의 무들 설치를 업데이트 해야합니다.", - "core.course.allsections": "모든 섹션", - "core.course.askadmintosupport": "사이트 관리자에게 연락하여 Moodle Mobile 앱에서 이 활동을 사용하길 원한다고 말씀하세요.", - "core.course.confirmdeletemodulefiles": "이 파일을 정말로 삭제 하시겠습니까?", - "core.course.confirmdownload": "{{size}}를 다운로드하려고 합니다. 계속하시겠습니까?", - "core.course.confirmdownloadunknownsize": "다운로드 크기를 계산할 수 없었습니다. 계속하시겠습니까?", - "core.course.confirmpartialdownloadsize": "적어도 {{size}}를 다운로드하려고 합니다. 계속하시겠습니까?", - "core.course.contents": "콘텐츠", - "core.course.couldnotloadsectioncontent": "섹션 콘텐츠를 로드할 수 없습니다. 나중에 다시 시도해 주십시오.", - "core.course.couldnotloadsections": "섹션을 로드 할 수 없습니다. 나중에 다시 시도 해주십시오.", - "core.course.coursesummary": "강좌 요약", - "core.course.downloadcourse": "강좌 다운로드", - "core.course.errordownloadingcourse": "강좌를 다운로드 하는 중 오류가 발생했습니다.", - "core.course.errordownloadingsection": "섹션을 다운로드 하는 중 오류가 발생했습니다.", - "core.course.errorgetmodule": "활동 데이터를 가져 오는 중 오류가 발생했습니다.", - "core.course.hiddenfromstudents": "학생에게 비공개", - "core.course.nocontentavailable": "현재 사용할 수 있는 콘텐츠가 없습니다.", - "core.course.overriddennotice": "이 활동에 대한 최종 성적처리가 수동으로 조정되었습니다.", - "core.course.sections": "영역", - "core.course.useactivityonbrowser": "기기의 웹 브라우저를 사용하여 계속 사용할 수 있습니다.", - "core.coursedetails": "강좌 세부내용", - "core.courses.allowguests": "이 강좌는 손님 계정을 허용함", - "core.courses.availablecourses": "이용할 수 있는 강좌", - "core.courses.cannotretrievemorecategories": "{{$a}} 레벨보다 더 깊은 카테고리는 검색 할 수 없습니다.", - "core.courses.categories": "강좌 범주", - "core.courses.confirmselfenrol": "이 강좌에 자신을 등록 하시겠습니까?", - "core.courses.courses": "강좌", - "core.courses.downloadcourses": "다운로드된 강좌", - "core.courses.enrolme": "등록하기", - "core.courses.errorloadcategories": "카테고리를 로드하는 중에 오류가 발생했습니다.", - "core.courses.errorloadcourses": "강좌를 로드하는 중에 오류가 발생했습니다.", - "core.courses.errorsearching": "검색하는 동안 오류가 발생했습니다.", - "core.courses.errorselfenrol": "자체 등록 중 오류가 발생했습니다.", - "core.courses.filtermycourses": "내 강좌 필터링", - "core.courses.frontpage": "시작 페이지", - "core.courses.ignore": "무시", - "core.courses.mycourses": "내 강좌", - "core.courses.mymoodle": "내 공부방", - "core.courses.nocourses": "볼 수 있는 강좌 정보가 없습니다.", - "core.courses.nocoursesyet": "검색된 결과가 없습니다.", - "core.courses.nosearchresults": "결과 없음", - "core.courses.notenroled": "이 강좌에 등록되지 않았습니다.", - "core.courses.notenrollable": "이 강좌에 등록 할 수 없습니다.", - "core.courses.password": "등록키", - "core.courses.paymentrequired": "이 강좌는 수강하기 위해 등록금이 필요합니다.", - "core.courses.paypalaccepted": "PayPal 지불 접수됨", - "core.courses.reload": "다시 로딩", - "core.courses.search": "검색", - "core.courses.searchcourses": "강좌 찾기", - "core.courses.searchcoursesadvice": "강좌 찾기 버튼을 사용하여 손님으로 액세스 할 수있는 강좌를 찾거나 수강 할 수있는 강좌에 등록 할 수 있습니다.", - "core.courses.selfenrolment": "스스로 등록", - "core.courses.sendpaymentbutton": "페이팔을 통해 송금하기", - "core.courses.totalcoursesearchresults": "총 과정 수: {{$a}}", - "core.currentdevice": "현재 장치", - "core.datastoredoffline": "데이터를 전송할 수 없기 때문에 장치에 데이터가 저장되었습니다. 나중에 자동으로 전송됩니다.", - "core.date": "날짜", - "core.day": "일", - "core.days": "일", - "core.decsep": ".", - "core.delete": "삭제", - "core.deletedoffline": "오프라인에서 삭제됨", - "core.deleting": "삭제 중", - "core.description": "설명", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.discard": "포기", - "core.dismiss": "버리다", - "core.done": "완료", - "core.download": "내려받기", - "core.downloading": "다운로드 중", - "core.edit": "고치기", - "core.editor.bold": "굵게", - "core.editor.h3": "헤딩 1", - "core.editor.h4": "헤딩 2", - "core.editor.h5": "헤딩 3", - "core.editor.p": "문단", - "core.editor.strike": "가운데 줄긋기", - "core.editor.underline": "밑줄", - "core.editor.unorderedlist": "정렬안된 목록", - "core.emptysplit": "왼쪽 패널이 비어 있거나 로드 중인 경우이 페이지는 공백으로 표시됩니다.", - "core.error": "오류", - "core.errorchangecompletion": "완료 상태를 변경하는 중에 오류가 발생했습니다. 다시 시도하십시오.", - "core.errordeletefile": "파일을 삭제하는 중 오류가 발생했습니다. 다시 시도하십시오.", - "core.errordownloading": "파일 다운로드 중 오류가 발생했습니다.", - "core.errordownloadingsomefiles": "파일을 다운로드하는 중 오류가 발생했습니다. 일부 파일이 누락되었을 수 있습니다.", - "core.errorfileexistssamename": "이 이름의 파일이 이미 있습니다.", - "core.errorinvalidform": "양식에 잘못된 데이터가 있습니다. 모든 필수 입력란이 채워져 있고 데이터가 유효한지 확인하십시오.", - "core.errorinvalidresponse": "잘못된 응답이 수신 되었습니다. 오류가 계속되면 사이트 관리자에게 문의하십시오.", - "core.errorloadingcontent": "내용을 로드하는 중 오류가 발생했습니다.", - "core.erroropenfilenoapp": "파일 열기 중 오류: 이 유형의 파일을 여는 앱이 없습니다.", - "core.erroropenfilenoextension": "파일을 여는 중 오류가 발생했습니다. 파일에 확장명이 없습니다.", - "core.erroropenpopup": "이 활동은 팝업을 열려고 합니다. 앱에서는 지원되지 않습니다.", - "core.errorrenamefile": "파일의 이름을 바꾸는 중 오류가 발생했습니다. 다시 시도하십시오.", - "core.errorsync": "동기화 중 오류가 발생했습니다. 다시 시도하십시오.", - "core.errorsyncblocked": "이 {{$a}}은(는) 진행 중인 프로세스로 인해 지금 동기화 할 수 없습니다. 나중에 다시 시도 해주십시오. 문제가 지속되면 앱을 다시 시작하십시오.", - "core.filename": "파일명", - "core.filenameexist": "파일 이름이 이미 존재합니다: {{$a}}", - "core.filenotfound": "파일 없음", - "core.fileuploader.addfiletext": "파일 추가", - "core.fileuploader.audio": "오디오", - "core.fileuploader.camera": "카메라", - "core.fileuploader.confirmuploadfile": "{{size}}를 업로드하려고 합니다. 계속 하겠습니까?", - "core.fileuploader.confirmuploadunknownsize": "업로드 크기를 계산할 수 없습니다. 계속 진행하겠습니까?", - "core.fileuploader.errorcapturingaudio": "오디오 캡처 오류", - "core.fileuploader.errorcapturingimage": "이미지 캡처 오류", - "core.fileuploader.errorcapturingvideo": "비디오 캡처 오류", - "core.fileuploader.errorgettingimagealbum": "앨범에서 이미지를 가져 오는 중 오류가 발생했습니다.", - "core.fileuploader.errormustbeonlinetoupload": "파일을 업로드하려면 온라인 상태여야 합니다.", - "core.fileuploader.errornoapp": "이 작업을 수행하기 위해 앱이 설치되어 있지 않습니다.", - "core.fileuploader.errorreadingfile": "파일을 읽는 중 오류가 발생했습니다.", - "core.fileuploader.errorwhileuploading": "파일 업로드 중에 오류가 발생했습니다.", - "core.fileuploader.file": "파일", - "core.fileuploader.filesofthesetypes": "허용되는 파일 형식 :", - "core.fileuploader.fileuploaded": "파일이 성공적으로 업로드되었습니다.", - "core.fileuploader.invalidfiletype": "{{$a}} 파일 형식은 쓸 수 없음", - "core.fileuploader.maxbytesfile": "{{$a.file}} 파일이 너무 큽니다. 업로드 할 수있는 최대 크기는 {{$a.size}}입니다.", - "core.fileuploader.more": "더 이상", - "core.fileuploader.photoalbums": "사진 앨범", - "core.fileuploader.readingfile": "파일을 읽는 중", - "core.fileuploader.selectafile": "파일 선택", - "core.fileuploader.uploadafile": "파일 업로드", - "core.fileuploader.uploading": "업로드 중", - "core.fileuploader.uploadingperc": "업로드 중 : {{$a}} %", - "core.fileuploader.video": "비디오", - "core.filter": "필터", - "core.folder": "폴더", - "core.forcepasswordchangenotice": "계속하려면 비밀번호를 바꿔야만 함", - "core.fulllistofcourses": "강좌목록", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "평균", - "core.grades.badgrade": "제공된 성적이 잘못 되었음", - "core.grades.feedback": "피드백", - "core.grades.grade": "성적", - "core.grades.gradeitem": "성적 항목", - "core.grades.grades": "성적들", - "core.grades.lettergrade": "문자 성적", - "core.grades.nogradesreturned": "돌아온 성적이 없습니다.", - "core.grades.nooutcome": "학습성과 없음", - "core.grades.percentage": "백분율", - "core.grades.range": "범위", - "core.grades.rank": "등위", - "core.grades.weight": "가중치", - "core.group": "모둠", - "core.groupsseparate": "분리된 모둠", - "core.groupsvisible": "열린 모둠", - "core.hasdatatosync": "이 {{$a}}에 동기화 할 오프라인 데이터가 있습니다.", - "core.help": "도움", - "core.hide": "감추기", - "core.hour": "시", - "core.hours": "시간", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "이미지", - "core.imageviewer": "이미지 뷰어", - "core.info": "정보", - "core.invalidformdata": "잘못된 양식 데이터", - "core.labelsep": " :", - "core.lastaccess": "최근의 접속", - "core.lastdownloaded": "마지막으로 다운로드 한 파일", - "core.lastmodified": "마지막 수정됨", - "core.lastsync": "마지막 동기화", - "core.layoutgrid": "그리드", - "core.list": "목록", - "core.listsep": ",", - "core.loadmore": "더 많은 로드", - "core.location": "위치", - "core.login.auth_email": "이메일 기반 인증", - "core.login.authenticating": "인증 중", - "core.login.cancel": "취소", - "core.login.changepassword": "비밀번호 변경", - "core.login.confirmdeletesite": "{{sitename}} 사이트를 삭제 하시겠습니까?", - "core.login.connect": "연결", - "core.login.connecttomoodle": "무들 연결", - "core.login.contactyouradministrator": "추가 도움이 필요하면 사이트 관리자에게 문의하십시오.", - "core.login.contactyouradministratorissue": "사이트 관리자에게 다음 문제를 확인하도록 요청하십시오: {{$a}}", - "core.login.createaccount": "새 계정 만들기", - "core.login.createuserandpass": "아이디와 비밀번호 생성", - "core.login.credentialsdescription": "로그인하려면 사용자 이름과 비밀번호를 입력하십시오.", - "core.login.emailconfirmsent": "

                당신의 이메일 주소인 {{$a}}로 메일이 갔습니다.

                \n

                등록을 마치기 위한 간단한 안내문이 포함되어 있습니다.

                ", - "core.login.emailnotmatch": "이메일이 일치하지 않습니다.", - "core.login.erroraccesscontrolalloworigin": "수행하려는 교차 원점 호출이 거부되었습니다. https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium을 확인하십시오.", - "core.login.errordeletesite": "이 사이트를 삭제하는 중에 오류가 발생했습니다. 다시 시도하십시오.", - "core.login.errorupdatesite": "사이트 토큰을 업데이트하는 중 오류가 발생했습니다.", - "core.login.firsttime": "이곳에 처음 오셨나요?", - "core.login.forcepasswordchangenotice": "계속하려면 비밀번호를 바꿔야만 함", - "core.login.forgotten": "사용자 아이디나 비밀번호를 잊으셨습니까?", - "core.login.help": "도움", - "core.login.helpmelogin": "

                전 세계에는 수천 개의 무들 사이트가 있습니다. 이 앱은 모바일 앱 액세스가 가능한 무들 사이트에만 연결할 수 있습니다.

                무들 사이트에 연결할 수 없는 경우 사이트 관리자에게 문의하여 다음을 읽어보도록 해주세요. http://docs.moodle.org/ko/Mobile_app

                앱을 테스트하려면 사이트 주소 입력란에 무들 데모 사이트 유형 선생님 또는 학생 을 입력하고 연결 버튼 을 클릭하십시오.

                >", - "core.login.instructions": "안내문", - "core.login.invalidaccount": "로그인 세부 정보를 확인하거나 사이트 관리자에게 사이트 구성을 확인하십시오.", - "core.login.invaliddate": "유효하지 않은 날짜", - "core.login.invalidemail": "쓸 수 없는 이메일 주소", - "core.login.invalidmoodleversion": "잘못된 무들 버전입니다. 최소 버전은 {{$a}}입니다.", - "core.login.invalidsite": "사이트 URL이 유효하지 않습니다.", - "core.login.invalidtime": "잘못된 시간", - "core.login.invalidurl": "지정된 URL이 잘못되었습니다", - "core.login.invalidvaluemax": "최대 값은 {{$a}}입니다.", - "core.login.invalidvaluemin": "최소 값은 {{$a}}입니다.", - "core.login.localmobileunexpectedresponse": "Moodle Mobile 추가 기능 check가 예기치 않은 응답을 보냈습니다. 표준 모바일 서비스를 사용하여 인증됩니다.", - "core.login.loggedoutssodescription": "다시 인증 해야 합니다. 브라우저 창에서 사이트에 로그인 해야 합니다.", - "core.login.login": "로그인", - "core.login.loginbutton": "로그인", - "core.login.logininsiterequired": "브라우저 창에서 사이트에 로그인 해야 합니다.", - "core.login.loginsteps": "이 사이트를 자유롭게 접근하기 위해서는, 계정을 생성해 주십시요.", - "core.login.missingemail": "빠짐: 이메일 주소", - "core.login.missingfirstname": "빠짐: 성", - "core.login.missinglastname": "빠짐: 이름", - "core.login.mobileservicesnotenabled": "사이트에서 모바일 액세스를 사용하도록 설정하지 않았습니다. 활성화해야 한다고 생각되면 사이트 관리자에게 문의하십시오.", - "core.login.mustconfirm": "로그인 계정을 확인하세요.", - "core.login.newaccount": "새 계정", - "core.login.notloggedin": "로그인 해야 합니다.", - "core.login.password": "비밀번호", - "core.login.passwordforgotten": "비밀번호 잊어버림", - "core.login.passwordforgotteninstructions2": "비밀번호를 재설정하기 위해서는 아래에 사용자 아이디나 이메일 주소를 입력하세요. 데이터베이스에서 확인이 되면 추후 과정을 안내할 이메일을 확인된 이메일 주소로 발송할 것입니다.", - "core.login.passwordrequired": "비밀번호 필요", - "core.login.policyaccept": "약관을 이해하였으며 이에 동의합니다.", - "core.login.policyagree": "이 사이트를 계속 이용하시려면 약관에 동의하셔야 합니다. 동의하십니까?", - "core.login.policyagreement": "사이트 정책 동의", - "core.login.policyagreementclick": "사이트 정책 동의로의 링크", - "core.login.potentialidps": "이전에 로그아웃한 곳과 다른 곳에서 접속을 자주하십니까?
                다음 중 접속하는 곳의 위치를 선택하세요: ", - "core.login.profileinvaliddata": "옳지 않은 값", - "core.login.recaptchachallengeimage": "reCAPTCHA 챌린지 이미지", - "core.login.reconnect": "재연결", - "core.login.reconnectdescription": "인증 토큰이 유효하지 않거나 만료되었습니다. 사이트에 다시 연결해야 합니다.", - "core.login.reconnectssodescription": "인증 토큰이 유효하지 않거나 만료되었습니다. 사이트에 다시 연결해야 합니다. 브라우저 창에서 사이트에 로그인해야 합니다.", - "core.login.searchby": "검색 기준 :", - "core.login.security_question": "보안 질문", - "core.login.selectacountry": "국가 선택", - "core.login.selectsite": "귀하의 사이트를 선택하십시오 :", - "core.login.signupplugindisabled": "{{$a}}이 사용 설정되지 않았습니다.", - "core.login.siteaddress": "사이트 주소", - "core.login.siteinmaintenance": "사이트가 유지 관리 모드에 있습니다.", - "core.login.sitepolicynotagreederror": "사이트 정책이 동의되지 않았습니다.", - "core.login.siteurl": "사이트 URL", - "core.login.siteurlrequired": "사이트 URL 필요. 예) http://www.yourmoodlesite.org ", - "core.login.startsignup": "새 계정 만들기", - "core.login.stillcantconnect": "아직도 연결할 수 없습니까?", - "core.login.supplyinfo": "추가 정보", - "core.login.username": "사용자 아이디", - "core.login.usernameoremail": "사용자 아이디나 이메일 주소를 입력", - "core.login.usernamerequired": "사용자 이름 필요", - "core.login.usernotaddederror": "사용자 추가되지 않음 - 오류", - "core.login.visitchangepassword": "사이트를 방문하여 암호를 변경 하시겠습니까?", - "core.login.webservicesnotenabled": "사이트에서 웹 서비스를 사용할 수 없습니다. 사용하도록 설정하려면 사이트 관리자에게 문의하십시오.", - "core.lostconnection": "인증 토큰이 유효하지 않거나 만료되었습니다. 사이트에 다시 연결해야 합니다.", - "core.mainmenu.changesite": "사이트 변경", - "core.mainmenu.help": "도움", - "core.mainmenu.logout": "로그아웃", - "core.mainmenu.website": "웹 사이트", - "core.maxsizeandattachments": "파일의 최대 크기: {{$a.size}}, 최대 첨부 파일 갯수: {{$a.attachments}}", - "core.min": "분", - "core.mins": "분", - "core.misc": "기타", - "core.mod_assign": "과제", - "core.mod_assignment": "과제 (2.2)", - "core.mod_book": "책", - "core.mod_chat": "대화방", - "core.mod_choice": "간편설문", - "core.mod_data": "데이터베이스", - "core.mod_database": "데이터베이스", - "core.mod_external-tool": "LTI", - "core.mod_feedback": "피드백(설문)", - "core.mod_file": "파일", - "core.mod_folder": "폴더", - "core.mod_forum": "포럼", - "core.mod_glossary": "용어집", - "core.mod_ims": "IMS 콘덴츠팩키지", - "core.mod_imscp": "IMS 콘덴츠팩키지", - "core.mod_label": "표지", - "core.mod_lesson": "완전학습", - "core.mod_lti": "LTI", - "core.mod_page": "웹페이지", - "core.mod_quiz": "퀴즈", - "core.mod_resource": "파일", - "core.mod_scorm": "스콤 패키지", - "core.mod_survey": "조사", - "core.mod_url": "URL", - "core.mod_wiki": "위키", - "core.mod_workshop": "상호평가", - "core.moduleintro": "모듈 소개", - "core.more": "좀 더", - "core.name": "이름", - "core.networkerrormsg": "사이트에 연결하는 중에 문제가 발생했습니다. 연결을 확인하고 다시 시도하십시오.", - "core.never": "접속안함", - "core.next": "다음", - "core.no": "아니오", - "core.nocomments": "덧글 없음", - "core.nograde": "성적 없음", - "core.none": "없음", - "core.nopasswordchangeforced": "암호를 변경하지 않고 계속 진행할 수 없습니다.", - "core.nopermissions": "죄송합니다만 그 ({{$a}})를 할만한 권한이 없습니다.", - "core.noresults": "결과 없음", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "이 사용자는 강좌의 구성원이 아니므로 개인정보를 볼 수 없습니다.", - "core.notice": "알림", - "core.notingroup": "죄송합니다만, 이곳을 보려면 모둠의 구성원이 되어야 합니다.", - "core.notsent": "전송되지 않음", - "core.now": "지금", - "core.numwords": "{{$a}} 단어", - "core.offline": "오프라인", - "core.ok": "OK", - "core.online": "온라인", - "core.openfullimage": "전체 크기 이미지를 보려면 여기를 클릭하십시오.", - "core.openinbrowser": "브라우저에서 열기", - "core.pagea": "페이지 {{$a}}", - "core.paymentinstant": "신속하게 등록금 지불 및 등록을 마치려면 아래의 버튼을 사용하시오!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "전화", - "core.pictureof": "{{$a}} 사진", - "core.previous": "이전으로", - "core.pulltorefresh": "당겨서 새로 고침", - "core.question.answer": "답", - "core.question.answersaved": "답이 저장되었습니다.", - "core.question.complete": "완료", - "core.question.correct": "맞음", - "core.question.errorattachmentsnotsupported": "응용 프로그램은 아직 답변을 위한 파일 첨부를 지원하지 않습니다.", - "core.question.errorinlinefilesnotsupported": "응용 프로그램은 아직 인라인 파일 편집을 지원하지 않습니다.", - "core.question.errorquestionnotsupported": "이 질문 유형은 앱에서 지원하지 않습니다: {{$a}}.", - "core.question.feedback": "피드백", - "core.question.howtodraganddrop": "눌러서 선택하고 눌러서 놓으세요.", - "core.question.incorrect": "틀림", - "core.question.information": "정보", - "core.question.invalidanswer": "불완전한 답", - "core.question.notanswered": "답하지 않음", - "core.question.notyetanswered": "아직 답하지 않음", - "core.question.partiallycorrect": "부분적으로 맞음", - "core.question.questionmessage": "질문 {{$a}}: {{$b}}", - "core.question.questionno": "질문 {{$a}}", - "core.question.requiresgrading": "채점 필요", - "core.rating.aggregateavg": "평균 등급", - "core.rating.aggregatecount": "등급 갯수", - "core.rating.aggregatemax": "최고 등급", - "core.rating.aggregatemin": "최소 등급", - "core.rating.aggregatesum": "등급의 합계", - "core.rating.noratings": "제출된 등급이 없습니다.", - "core.rating.rating": "추천", - "core.rating.ratings": "추천", - "core.redirectingtosite": "사이트로 리디렉션됩니다.", - "core.refresh": "새로고침", - "core.remove": "삭제", - "core.required": "필수사항", - "core.requireduserdatamissing": "이 사용자에게는 필요한 프로필 데이터가 없습니다. 사이트에 데이터를 입력하고 다시 시도하십시오.
                {{$a}}", - "core.resourcedisplayopen": "열기", - "core.resources": "참고자료", - "core.restore": "복구", - "core.restricted": "제한됨", - "core.retry": "다시 시도", - "core.save": "저장", - "core.savechanges": "변경사항 저장", - "core.search": "검색", - "core.searching": "검색 중", - "core.searchresults": "검색 결과", - "core.sec": "초", - "core.secs": "초", - "core.seemoredetail": "더 많은 정보를 보기 원하시면 이곳을 클릭하시오.", - "core.selectacategory": "범주를 선택하세요.", - "core.selectacourse": "강좌 선택", - "core.selectagroup": "모둠 선택", - "core.send": "보내기", - "core.sending": "보내는 중", - "core.serverconnection": "서버 접속 오류", - "core.settings.about": "대해서", - "core.settings.cannotsyncoffline": "오프라인으로 동기화 할 수 없습니다.", - "core.settings.cannotsyncwithoutwifi": "현재 설정이 Wi-Fi에 연결된 경우에만 동기화 할 수 있기 때문에 동기화 할 수 없습니다. Wi-Fi 네트워크에 연결하십시오.", - "core.settings.cordovadevicemodel": "코르도바 장치 모델", - "core.settings.currentlanguage": "현재의 언어", - "core.settings.debugdisplay": "디버그 메시지 표시", - "core.settings.deletesitefilestitle": "사이트 파일 삭제", - "core.settings.disableall": "통지 비활성", - "core.settings.enabledownloadsection": "섹션 다운로드 활성화", - "core.settings.enablerichtexteditor": "텍스트 편집기 사용", - "core.settings.enablerichtexteditordescription": "사용 설정하면 콘텐츠를 입력 할 때 텍스트 편집기를 사용할 수 있습니다.", - "core.settings.enablesyncwifi": "Wi-Fi 일 때만 동기화 허용", - "core.settings.errordeletesitefiles": "사이트 파일 삭제 중 오류가 발생했습니다.", - "core.settings.estimatedfreespace": "예상 여유 공간", - "core.settings.general": "기본", - "core.settings.language": "언어", - "core.settings.license": "사용허가", - "core.settings.localnotifavailable": "로컬 알림 사용 가능", - "core.settings.locked": "잠김", - "core.settings.loggedin": "온라인", - "core.settings.loggedoff": "온라인 아님", - "core.settings.preferences": "사용자 선택사항", - "core.settings.settings": "설정", - "core.settings.sites": "사이트", - "core.settings.synchronization": "동기화", - "core.settings.synchronizenow": "지금 동기화", - "core.settings.syncsettings": "동기화 설정", - "core.settings.total": "전체", - "core.sharedfiles.chooseaccountstorefile": "파일을 저장할 계정을 선택하십시오.", - "core.sharedfiles.nosharedfilestoupload": "여기에 업로드 할 파일이 없습니다. 다른 앱에서 파일을 업로드하려면 파일을 찾은 다음 '열기'버튼을 클릭하십시오.", - "core.sharedfiles.rename": "이름 바꾸기", - "core.sharedfiles.sharedfiles": "공유 파일", - "core.show": "보기", - "core.showless": "덜 보기", - "core.showmore": "더 보기", - "core.site": "사이트", - "core.sitehome.sitehome": "사이트 홈", - "core.sitehome.sitenews": "사이트 뉴스", - "core.sitemaintenance": "이 사이트는 점검 중이며 현재 사용할 수 없습니다.", - "core.sizeb": "바이트", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.sorry": "죄송합니다.", - "core.sort": "정렬", - "core.sortby": "정렬", - "core.strftimedate": "%Y년 %B %d일", - "core.strftimedatefullshort": "%y/%m/%d", - "core.strftimedateshort": "%B%d일", - "core.strftimedatetime": "%Y년 %B %d일, %p %I:%M", - "core.strftimedatetimeshort": "%Y/%m/%d %H:%M", - "core.strftimedaydate": "%Y년 %B %d일, %A", - "core.strftimedaydatetime": "%Y년 %B %d일, %A, %p %I:%M", - "core.strftimedayshort": "%B %d일, %A", - "core.strftimedaytime": "%a,%H:%M", - "core.strftimemonthyear": "%Y년 %B", - "core.strftimerecent": "%b %d일, %H:%M", - "core.strftimerecentfull": "%Y년 %b %d일, %a, %p %I:%M", - "core.strftimetime": "%p %I:%M", - "core.submit": "제출", - "core.success": "성공", - "core.tablet": "태블릿", - "core.tag.noresultsfor": "\"{{$a}}\"에 대한 결과 없음", - "core.tag.searchtags": "태그 검색", - "core.tag.tag": "태그", - "core.tag.tags": "태그", - "core.teachers": "선생님", - "core.thereisdatatosync": "동기화 할 오프라인 {{$a}}이 있습니다.", - "core.thisdirection": "ltr", - "core.time": "시", - "core.timesup": "시간이 다 되었습니다!", - "core.today": "오늘", - "core.tryagain": "다시 시도하십시오.", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "오!", - "core.unexpectederror": "예기치 않은 오류. 애플리케이션을 닫았다가 다시 연 다음 다시 시도하십시오.", - "core.unicodenotsupported": "일부 이모티콘은이 사이트에서 지원되지 않습니다. 이러한 문자는 메시지를 보낼 때 제거됩니다.", - "core.unicodenotsupportedcleanerror": "유니 코드 문자를 지울 때 빈 텍스트가 발견되었습니다.", - "core.unknown": "알 수 없는", - "core.unlimited": "제한없음", - "core.unzipping": "압축을 푸는 중", - "core.upgraderunning": "사이트가 판올림 중이므로, 나중에 다시 시도하기 바랍니다.", - "core.user": "사용자", - "core.user.address": "주소", - "core.user.city": "도시", - "core.user.contact": "연락처", - "core.user.country": "국가", - "core.user.description": "설명", - "core.user.details": "세부사항", - "core.user.detailsnotavailable": "이 사용자의 세부 정보는 사용할 수 없습니다.", - "core.user.editingteacher": "선생님", - "core.user.email": "이메일 주소", - "core.user.emailagain": "이메일 (다시)", - "core.user.firstname": "성", - "core.user.interests": "관심분야", - "core.user.lastname": "이름", - "core.user.manager": "관리자", - "core.user.newpicture": "새 사진", - "core.user.noparticipants": "강좌에 참여자가 아무도 없음", - "core.user.participants": "참여자", - "core.user.phone1": "전화", - "core.user.phone2": "휴대 전화", - "core.user.roles": "역할", - "core.user.sendemail": "이메일", - "core.user.student": "학생", - "core.user.teacher": "편집권한이 없는 선생님", - "core.user.webpage": "웹 페이지", - "core.userdeleted": "이 사용자 계정은 삭제되었습니다.", - "core.userdetails": "사용자 세부사항", - "core.users": "사용자", - "core.view": "보기", - "core.viewprofile": "개인정보 보기", - "core.warningofflinedatadeleted": "{{component}} '{{name}}'의 오프라인 데이터가 삭제되었습니다. {{error}}", - "core.whatisyourage": "나이가 어떻게 됩니까?", - "core.wheredoyoulive": "어느 나라에 삽니까?", - "core.whoops": "이런!", - "core.whyisthishappening": "왜 이런 일이 일어나는 걸까요?", - "core.whyisthisrequired": "이것이 왜 필요합니까?", - "core.wsfunctionnotavailable": "웹 서비스 기능을 사용할 수 없습니다.", - "core.year": "년", - "core.years": "년", - "core.yes": "예" -} \ No newline at end of file diff --git a/src/assets/lang/lt.json b/src/assets/lang/lt.json deleted file mode 100644 index 99cbe3070..000000000 --- a/src/assets/lang/lt.json +++ /dev/null @@ -1,1851 +0,0 @@ -{ - "addon.badges.alignment": "Lygiavimas", - "addon.badges.badgedetails": "Pasiekimo detalės", - "addon.badges.badges": "Pasiekimai", - "addon.badges.bendorsement": "Patvirtinimas", - "addon.badges.claimcomment": "Patvirtinimo komentaras", - "addon.badges.contact": "Kontaktas", - "addon.badges.dateawarded": "Suteikimo data", - "addon.badges.expired": "Nebegalioja", - "addon.badges.expirydate": "Galiojimo laikas", - "addon.badges.imageauthoremail": "Paveiksliuko autoriaus el. pašto adresas", - "addon.badges.imageauthorname": "Paveiksliuko autoriaus vardas", - "addon.badges.imageauthorurl": "Paveiksliuko autoriaus URL", - "addon.badges.imagecaption": "Paveiksliuko antraštė", - "addon.badges.issuancedetails": "Pasiekimo galiojimas", - "addon.badges.issuerdetails": "Suteikėjo detalesnė informacija", - "addon.badges.issueremail": "El. paštas", - "addon.badges.issuername": "Suteikėjo vardas", - "addon.badges.issuerurl": "Suteikėjo URL", - "addon.badges.language": "Kalba", - "addon.badges.nobadges": "Nėra sukurtų pasiekimų.", - "addon.badges.recipientdetails": "Informacija apie gavėją", - "addon.badges.relatedbages": "Susiję pasiekimai", - "addon.badges.version": "Versija", - "addon.badges.warnexpired": "(Šis pasiekimas baigė galioti!)", - "addon.block_activitymodules.pluginname": "Veiklos", - "addon.block_activityresults.pluginname": "Veiklos rezultatai", - "addon.block_badges.pluginname": "Naujausi pasiekimai", - "addon.block_blogmenu.pluginname": "Tinklaraščio meniu", - "addon.block_blogrecent.pluginname": "Naujausi tinklaraščio įrašai", - "addon.block_blogtags.pluginname": "Tinklaraščio žymės", - "addon.block_calendarmonth.pluginname": "Kalendorius", - "addon.block_calendarupcoming.pluginname": "Artimiausi įvykiai", - "addon.block_comments.pluginname": "Komentarai", - "addon.block_completionstatus.pluginname": "Kursų baigimo statusas", - "addon.block_glossaryrandom.pluginname": "Atsitiktinis žodyno įrašas", - "addon.block_learningplans.pluginname": "Mokymosi planai", - "addon.block_myoverview.all": "Visi (išskyrus paslėptus)", - "addon.block_myoverview.allincludinghidden": "Visi", - "addon.block_myoverview.favourites": "Pažymėti žvaigždute", - "addon.block_myoverview.future": "Būsimi", - "addon.block_myoverview.hiddencourses": "Paslėpti", - "addon.block_myoverview.inprogress": "Vykstantys", - "addon.block_myoverview.lastaccessed": "Paskutinis prisijungimas", - "addon.block_myoverview.morecourses": "Daugiau kursų", - "addon.block_myoverview.nocourses": "Nėra kursų", - "addon.block_myoverview.past": "Pasibaigę", - "addon.block_myoverview.pluginname": "Kursų apžvalga", - "addon.block_myoverview.title": "Kurso pavadinimas", - "addon.block_newsitems.pluginname": "Paskutiniai skelbimai", - "addon.block_onlineusers.pluginname": "Prisijungę naudotojai", - "addon.block_privatefiles.pluginname": "Asmeniniai failai", - "addon.block_recentactivity.pluginname": "Naujausia veikla", - "addon.block_recentlyaccessedcourses.nocourses": "Nėra paskutinių kursų", - "addon.block_recentlyaccessedcourses.pluginname": "Neseniai aplankyti kursai", - "addon.block_recentlyaccesseditems.noitems": "Nėra paskutinių elementų", - "addon.block_recentlyaccesseditems.pluginname": "Paskutiniai peržiūrėti elementai", - "addon.block_rssclient.pluginname": "RSS klientas", - "addon.block_selfcompletion.pluginname": "Savarankiškas užbaigimas", - "addon.block_sitemainmenu.pluginname": "Pagrindinis meniu", - "addon.block_starredcourses.nocourses": "Nėra pažymėtų kursų", - "addon.block_starredcourses.pluginname": "Pažymėti kursai", - "addon.block_tags.pluginname": "Žymės", - "addon.block_timeline.duedate": "Terminas", - "addon.block_timeline.next30days": "Sekančios 30 dienų", - "addon.block_timeline.next3months": "Sekantys 3 mėnesiai", - "addon.block_timeline.next6months": "Sekantys 6 mėnesiai", - "addon.block_timeline.next7days": "Sekančios 7 dienos", - "addon.block_timeline.nocoursesinprogress": "Nėra vykstančių kursų", - "addon.block_timeline.noevents": "Nėra jokių būsimų veiklų", - "addon.block_timeline.overdue": "Pavėluoti", - "addon.block_timeline.pluginname": "Laiko juosta", - "addon.block_timeline.sortbycourses": "Rikiuoti pagal kursus", - "addon.block_timeline.sortbydates": "Rikiuoti pagal datas", - "addon.blog.blog": "Tinklaraštis", - "addon.blog.blogentries": "Tinklaraščio įrašai", - "addon.blog.linktooriginalentry": "Saitas su pradiniu tinklaraščio įrašu", - "addon.blog.noentriesyet": "Čia nėra matomų įrašų", - "addon.blog.publishtonoone": "Tik aš (juodraštis)", - "addon.blog.publishtosite": "Visi šios svetainės naudotojai", - "addon.blog.publishtoworld": "Visi", - "addon.blog.siteblogheading": "Svetainės tinklaraštis", - "addon.calendar.allday": "Visą dieną", - "addon.calendar.calendar": "Kalendorius", - "addon.calendar.calendarevents": "Renginių kalendorius", - "addon.calendar.categoryevents": "Kategorijos įvykiai", - "addon.calendar.confirmeventdelete": "Ar tikrai norite pašalinti šį \"{{$a}}\" įvykį?", - "addon.calendar.courseevents": "Kurso įvykiai", - "addon.calendar.daynext": "Kita diena", - "addon.calendar.dayprev": "Ankstesnė diena", - "addon.calendar.deleteallevents": "Pašalinti visus įvykius", - "addon.calendar.deleteevent": "Ištrinti įvykį", - "addon.calendar.deleteoneevent": "Pašalinti šį įvykį", - "addon.calendar.durationminutes": "Trukmė minutėmis", - "addon.calendar.durationnone": "Neribota", - "addon.calendar.durationuntil": "Iki", - "addon.calendar.editevent": "Redaguojamas įvykis", - "addon.calendar.errorloadevent": "Klaida įkeliant renginį.", - "addon.calendar.errorloadevents": "Klaida įkeliant renginius.", - "addon.calendar.eventcalendareventdeleted": "Kalendoriaus įvykis ištrintas", - "addon.calendar.eventduration": "Trukmė", - "addon.calendar.eventendtime": "Pabaiga", - "addon.calendar.eventkind": "Įvykio tipas", - "addon.calendar.eventname": "Įvykio pavadinimas", - "addon.calendar.eventstarttime": "Pradžia", - "addon.calendar.eventtype": "Įvykio tipas", - "addon.calendar.fri": "Pn", - "addon.calendar.friday": "Penktadienis", - "addon.calendar.gotoactivity": "Pereiti į veiklą", - "addon.calendar.groupevents": "Grupės įvykiai", - "addon.calendar.invalidtimedurationminutes": "Jūsų įvesta trukmė minutėmis yra neteisinga. Prašome įvesti trukmę didesnę nei 0 arba nepasirinkti trukmės.", - "addon.calendar.invalidtimedurationuntil": "Jūsų trukmei pasirinkta data ir laikas yra prieš prasidedant įvykiui. Prieš tęsiant prašome tai pataisyti.", - "addon.calendar.mon": "Pr", - "addon.calendar.monday": "Pirmadienis", - "addon.calendar.monthlyview": "Mėnesio peržiūra", - "addon.calendar.newevent": "Naujas įvykis", - "addon.calendar.noevents": "Renginių nėra", - "addon.calendar.nopermissiontoupdatecalendar": "Atsiprašome, tačiau jūs neturite teisių, leidžiančių atnaujinti kalendoriaus įvykį.", - "addon.calendar.repeatedevents": "Pasikartojantys įvykiai", - "addon.calendar.repeateditall": "Taip pat pritaikyti pakeitimus visiems {{$a}} įvykiams šiose pasikartojinčiose serijose", - "addon.calendar.repeateditthis": "Pritaikyti pakeitimus tik šiam įvykiui", - "addon.calendar.repeatevent": "Kartoti šį įvykį", - "addon.calendar.repeatweeksl": "Kartoti kas savaitę, sukurti kartu", - "addon.calendar.sat": "Št", - "addon.calendar.saturday": "Šeštadienis", - "addon.calendar.siteevents": "Svetainės įvykiai", - "addon.calendar.sun": "Sk", - "addon.calendar.sunday": "Sekmadienis", - "addon.calendar.thu": "Kt", - "addon.calendar.thursday": "Ketvirtadienis", - "addon.calendar.today": "Šiandien", - "addon.calendar.tomorrow": "Rytoj", - "addon.calendar.tue": "An", - "addon.calendar.tuesday": "Antradienis", - "addon.calendar.typecategory": "Kategorijos įvykis", - "addon.calendar.typeclose": "Uždarymo įvykis", - "addon.calendar.typecourse": "Kurso įvykis", - "addon.calendar.typedue": "Numatytas įvykis", - "addon.calendar.typegradingdue": "Įvertinamas numatytas įvykis", - "addon.calendar.typegroup": "Grupės įvykis", - "addon.calendar.typeopen": "Atidaryti įvykį", - "addon.calendar.typesite": "Svetainės įvykis", - "addon.calendar.typeuser": "Vartotojo įvykis", - "addon.calendar.upcomingevents": "Artimiausi įvykiai", - "addon.calendar.userevents": "Vartotojo įvykiai", - "addon.calendar.wed": "Tr", - "addon.calendar.wednesday": "Trečiadienis", - "addon.calendar.when": "Kada", - "addon.calendar.yesterday": "Vakar", - "addon.competency.activities": "Veiklos", - "addon.competency.competencies": "Kompetencijos", - "addon.competency.coursecompetencies": "Kurso kompetencijos", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Kompetencijų reitingai šiame kurse neturi įtakos mokymosi planams.", - "addon.competency.crossreferencedcompetencies": "Kryžminės kompetencijos", - "addon.competency.duedate": "Terminas", - "addon.competency.errornocompetenciesfound": "Kompetencijų nerasta", - "addon.competency.evidence": "Įrodymas", - "addon.competency.evidence_coursecompleted": "Kursas '{{$a}}' buvo užbaigtas.", - "addon.competency.evidence_coursemodulecompleted": "Veikla '{{$a}}' buvo užbaigta.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Ankstesnio mokymosi įrodymas '{{$a}}' buvo susietas.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Ankstesnio mokymosi įrodymas '{{$a}}' buvo atsietas.", - "addon.competency.learningplancompetencies": "Mokymosi plano kompetencijos", - "addon.competency.learningplans": "Mokymosi planai", - "addon.competency.myplans": "Mano mokymosi planai", - "addon.competency.noactivities": "Nėra veiklų", - "addon.competency.nocompetencies": "Nėra kompetencijų", - "addon.competency.nocompetenciesincourse": "Šiame kurse nebuvo susieta kompetencijų.", - "addon.competency.nocrossreferencedcompetencies": "Jokios kitos kompetencijos nebuvo susietos kryžmine nuoroda su šia kompetencija.", - "addon.competency.noevidence": "Nėra įrodymų", - "addon.competency.noplanswerecreated": "Nebuvo sukurta mokymosi planų.", - "addon.competency.planstatusactive": "Aktyvus", - "addon.competency.planstatuscomplete": "Baigta", - "addon.competency.planstatusdraft": "Juodraštis", - "addon.competency.planstatusinreview": "Peržiūrima", - "addon.competency.planstatuswaitingforreview": "Laukiama peržiūros", - "addon.competency.proficient": "Įgūdis", - "addon.competency.progress": "Progresas", - "addon.competency.rating": "Reitingas", - "addon.competency.reviewstatus": "Peržiūros būsena", - "addon.competency.status": "Būsena", - "addon.competency.template": "Mokymosi plano šablonas", - "addon.competency.uponcoursecompletion": "Po kurso užbaigimo:", - "addon.competency.usercompetencystatus_idle": "Nenaudojamas", - "addon.competency.usercompetencystatus_inreview": "Peržiūrima", - "addon.competency.usercompetencystatus_waitingforreview": "Laukiama peržiūros", - "addon.competency.userplans": "Mokymosi planai", - "addon.competency.xcompetenciesproficientoutofyincourse": "Įgijote {{$a.x}} iš {{$a.y}} kompetenciją šiame kurse.", - "addon.coursecompletion.complete": "Visas", - "addon.coursecompletion.completecourse": "Užbaigti kursus", - "addon.coursecompletion.completed": "Baigta", - "addon.coursecompletion.completiondate": "Baigtumo data", - "addon.coursecompletion.completionmenuitem": "Užbaigimas", - "addon.coursecompletion.couldnotloadreport": "Nepavyko įkelti kursų baigimo ataskaitos, prašome pabandyti vėliau.", - "addon.coursecompletion.coursecompletion": "Kurso baigimas", - "addon.coursecompletion.criteria": "Kriterijai", - "addon.coursecompletion.criteriagroup": "Kriterijų grupė", - "addon.coursecompletion.criteriarequiredall": "Visi žemiau pateikti kriterijai yra būtini", - "addon.coursecompletion.criteriarequiredany": "Bet kuris žemiau pateiktas kriterijus yra būtinas", - "addon.coursecompletion.inprogress": "Nebaigta", - "addon.coursecompletion.manualselfcompletion": "Savas užbaigimas neautomatiniu būdu", - "addon.coursecompletion.nottracked": "Šiuo metu kurse užbaigimas netaikomas", - "addon.coursecompletion.notyetstarted": "Dar nepradėta", - "addon.coursecompletion.pending": "Laukiama", - "addon.coursecompletion.required": "Būtina", - "addon.coursecompletion.requiredcriteria": "Būtini kriterijai", - "addon.coursecompletion.requirement": "Reikalavimas", - "addon.coursecompletion.status": "Būsena", - "addon.coursecompletion.viewcoursereport": "Peržiūrėti kurso ataskaitą", - "addon.files.couldnotloadfiles": "Negalima užkrauti failų sąrašo.", - "addon.files.emptyfilelist": "Nėra ką rodyti.", - "addon.files.erroruploadnotworking": "Deja, failo į pasirinktą svetainę įkelti negalima.", - "addon.files.files": "Failai", - "addon.files.privatefiles": "Asmeniniai failai", - "addon.files.sitefiles": "Svetainės failai", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Prietaisų konfigūravimas", - "addon.messages.acceptandaddcontact": "Priimti ir įtraukti į kontaktus", - "addon.messages.addcontact": "Įtraukti kontaktą", - "addon.messages.addcontactconfirm": "Ar tikrai norite įtraukti {{$a}} į savo kontaktus?", - "addon.messages.addtofavourites": "Pažymėti pokalbį žvaigždute", - "addon.messages.addtoyourcontacts": "Įtraukti į kontaktus", - "addon.messages.blocknoncontacts": "Neleisti neįtrauktiems į kontaktų sąrašą asmenims siųsti man žinutes", - "addon.messages.blockuser": "Blokuoti naudotoją", - "addon.messages.blockuserconfirm": "Ar tikrai norite užblokuoti {{$a}}?", - "addon.messages.contactableprivacy": "Priimti žinutes nuo:", - "addon.messages.contactableprivacy_coursemember": "Mano kontaktai ir visi mano kursuose", - "addon.messages.contactableprivacy_onlycontacts": "Tik mano kontaktai", - "addon.messages.contactableprivacy_site": "Kiekvienas svetainėje", - "addon.messages.contactblocked": "Užblokuotas kontaktas", - "addon.messages.contactlistempty": "Kontaktų sąrašas tuščias", - "addon.messages.contactname": "Kontaktas", - "addon.messages.contactrequestsent": "Kontakto prašymas išsiųstas", - "addon.messages.contacts": "Kontaktai", - "addon.messages.conversationactions": "Pokalbių veiksmų meniu", - "addon.messages.decline": "Atmesti", - "addon.messages.deleteallconfirm": "Ar tikrai norite ištrinti visą šį pokalbį? Tai nebus ištrinta kitiems pokalbių dalyviams.", - "addon.messages.deleteallselfconfirm": "Ar tikrai norite ištrinti šį asmeninį pokalbį?", - "addon.messages.deleteconversation": "Naikinti pokalbį", - "addon.messages.deleteforeveryone": "Ištrinti man ir visiems kitiems", - "addon.messages.errordeletemessage": "Klaida trinant žinutes.", - "addon.messages.errorwhileretrievingcontacts": "Klaida nuskaitant kontaktus iš serverio.", - "addon.messages.errorwhileretrievingdiscussions": "Klaida nuskaitant diskusijas iš serverio.", - "addon.messages.errorwhileretrievingmessages": "Klaida nuskaitant pranešimus iš serverio.", - "addon.messages.groupconversations": "Grupė", - "addon.messages.groupinfo": "Grupės info", - "addon.messages.individualconversations": "Asmeninės", - "addon.messages.info": "Naudotojo info", - "addon.messages.isnotinyourcontacts": "{{$a}} nėra Jūsų kontaktuose", - "addon.messages.message": "Žinutė", - "addon.messages.messagenotsent": "Žinutė nebuvo išsiųsta, pabandykite vėliau.", - "addon.messages.messagepreferences": "Žinučių nuostatos", - "addon.messages.messages": "Žinutės", - "addon.messages.muteconversation": "Nutildyti", - "addon.messages.mutedconversation": "Nutildytas pokalbis", - "addon.messages.newmessage": "Nauja žinutė", - "addon.messages.nocontactrequests": "Nėra kontaktų užklausų", - "addon.messages.nocontactsgetstarted": "Nėra kontaktų", - "addon.messages.nofavourites": "Nėra žvaigždute pažymėtų pokalbių", - "addon.messages.nogroupconversations": "Nėra pokalbių grupėse", - "addon.messages.noindividualconversations": "Nėra asmeninių pokalbių", - "addon.messages.nomessagesfound": "Nerasta žinučių", - "addon.messages.noncontacts": "Neįtraukti į kontaktus", - "addon.messages.nousersfound": "Vartotojas nerastas", - "addon.messages.numparticipants": "{{$a}} dalyviai", - "addon.messages.removecontact": "Pašalinti kontaktą", - "addon.messages.removecontactconfirm": "Ar tikrai norite pašalinti {{$a}} iš savo kontaktų?", - "addon.messages.removefromfavourites": "Nebežymėti pokalbio žvaigždute", - "addon.messages.removefromyourcontacts": "Pašalinti iš kontaktų", - "addon.messages.requests": "Užklausos", - "addon.messages.requirecontacttomessage": "Turite pateikti užklausą {{$a}}, kad pridėtumėte jus kaip kontaktą ir informuotume juos.", - "addon.messages.searchcombined": "Ieškoti žmonių ir žinučių", - "addon.messages.selfconversation": "Asmeninė erdvė", - "addon.messages.sendcontactrequest": "Siųsti kontakto užklausą", - "addon.messages.type_blocked": "Užblokuota", - "addon.messages.type_offline": "Neprisjungęs", - "addon.messages.type_online": "Prisijungęs", - "addon.messages.type_search": "Paieška", - "addon.messages.type_strangers": "Kita", - "addon.messages.unabletomessage": "Negalite siųsti pranešimo šiam naudotojui", - "addon.messages.unblockuser": "Nebeblokuoti naudotojo", - "addon.messages.unblockuserconfirm": "Ar tikrai norite atblokuoti {{$a}}?", - "addon.messages.unmuteconversation": "Nebetildyti", - "addon.messages.useentertosend": "Naudoti ENTER, kad išsiųsti", - "addon.messages.userwouldliketocontactyou": "{{$a}} norėtų su jumis susisiekti", - "addon.messages.warningmessagenotsent": "Žinutė {{user}} vartotojui neišsiųsta. {{error}}", - "addon.messages.wouldliketocontactyou": "Norėtų su jumis susisiekti", - "addon.messages.you": "Jūs:", - "addon.messages.youhaveblockeduser": "Užblokavote šį naudotoją.", - "addon.messages.yourcontactrequestpending": "Jūsų kontaktų užklausa laukiama {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Priimkite pateiktą pranešimą.", - "addon.mod_assign.addattempt": "Leisti dar vieną bandymą", - "addon.mod_assign.addnewattempt": "Pridėti naują bandymą", - "addon.mod_assign.addnewattemptfromprevious": "Pridėti naują bandymą remiantis ankstesniu pateikimu", - "addon.mod_assign.addsubmission": "Įkelti darbus", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Užduoties detalės ir darbo įkėlimo forma bus galima nuo {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Leisti įkelti darbus nuo", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Užduotis priims darbus nuo {{$a}}", - "addon.mod_assign.applytoteam": "Patvirtinti įvertinimus ir atsaką visai grupei", - "addon.mod_assign.assignmentisdue": "Užduoties atlikimo laikas baigės", - "addon.mod_assign.attemptnumber": "Bandymo numeris", - "addon.mod_assign.attemptreopenmethod": "Pateikimų pakartotinis atidarymas", - "addon.mod_assign.attemptreopenmethod_manual": "Rankiniu būdu", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatiškai, kol negaunamas teigiamas įvertinimas", - "addon.mod_assign.attemptsettings": "Bandymo nustatymai", - "addon.mod_assign.cannoteditduetostatementsubmission": "Negalite pridėti ar redaguoti pateikiamos programėlės, nes negavome svetainės patvirtinimo.", - "addon.mod_assign.cannotgradefromapp": "Keli vertinimo metodai nėra palaikomi ir negali būti pakeisti programėlėje.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Negalima pradėti vertinti, nes negautas pareiškimas dėl pateikimo .", - "addon.mod_assign.confirmsubmission": "Ar Jūs tikrai norite pateikti darbą vertinimui? Jūs nebegalėsite atlikti jokių pakeitimų.", - "addon.mod_assign.currentattempt": "Čia yra {{$a}} bandymas.", - "addon.mod_assign.currentattemptof": "Čia yra bandymas {{$a.attemptnumber}} ( {{$a.maxattempts}} attempts allowed ).", - "addon.mod_assign.currentgrade": "Dabartinis įvertis įverčių knygelėje", - "addon.mod_assign.cutoffdate": "Paskutinė diena pristatymui", - "addon.mod_assign.defaultteam": "Numatyta grupė", - "addon.mod_assign.duedate": "Data pristatymui", - "addon.mod_assign.duedateno": "Nėra termino", - "addon.mod_assign.duedatereached": "Vėliausia įkėlimo data šiai užduočiai jau praėjo.", - "addon.mod_assign.editingstatus": "Darbo keitimo galimybė", - "addon.mod_assign.editsubmission": "Keisti įkeltus darbus", - "addon.mod_assign.erroreditpluginsnotsupported": "Programėlėje pateikimo negalima pridėti ar redaguoti, nes redagavimo papildiniai nėra palaikomi:", - "addon.mod_assign.errorshowinginformation": "Pateikta informacija negali būti rodoma", - "addon.mod_assign.extensionduedate": "Pratęsta iki", - "addon.mod_assign.feedbacknotsupported": "Programėlė grįžtamojo ryšio nepalaiko ir negali pateikti visos informacijos", - "addon.mod_assign.grade": "Įvertis", - "addon.mod_assign.graded": "Įvertinta", - "addon.mod_assign.gradedby": "Įvertino", - "addon.mod_assign.gradedon": "Įvertinta", - "addon.mod_assign.gradelocked": "Šis įvertis yra užrakintas arba pakeistas įverčių knygelėje.", - "addon.mod_assign.gradeoutof": "Įvertinimas (iš galimų {{$a}})", - "addon.mod_assign.gradingstatus": "Vertinimo būsena", - "addon.mod_assign.groupsubmissionsettings": "Grupinio pateikimo nustatymai", - "addon.mod_assign.hiddenuser": "Dalyvis", - "addon.mod_assign.latesubmissions": "Vėluojantys įkelti darbus", - "addon.mod_assign.latesubmissionsaccepted": "Leidžiama iki {{$a}}", - "addon.mod_assign.markingworkflowstate": "Vertinimo būsena", - "addon.mod_assign.markingworkflowstateinmarking": "Vertinama", - "addon.mod_assign.markingworkflowstateinreview": "Peržiūrima", - "addon.mod_assign.markingworkflowstatenotmarked": "Neįvertinta", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Paruošta pateikimui", - "addon.mod_assign.markingworkflowstatereadyforreview": "Vertinimas baigtas", - "addon.mod_assign.markingworkflowstatereleased": "Pateikta", - "addon.mod_assign.modulenameplural": "Užduotis", - "addon.mod_assign.multipleteams": "Daugiau nei vienos grupės narys", - "addon.mod_assign.noattempt": "Nebandyta", - "addon.mod_assign.nomoresubmissionsaccepted": "Tik besimokantieji, kuriems pratęstas įkėlimas vis dar gali įkelti savo darbus", - "addon.mod_assign.noonlinesubmissions": "Ši užduotis nereikalauja nieko įkelti į svetainę", - "addon.mod_assign.nosubmission": "Nebuvo pateikta darbų šiai užduočiai", - "addon.mod_assign.notallparticipantsareshown": "Negalima rodyti nepatvirtintų dalyvių", - "addon.mod_assign.noteam": "Nesate jokios grupės narys", - "addon.mod_assign.notgraded": "Neįvertinta", - "addon.mod_assign.numberofdraftsubmissions": "Juodraščiai", - "addon.mod_assign.numberofparticipants": "Dalyviai", - "addon.mod_assign.numberofsubmissionsneedgrading": "Reikia įvertinti", - "addon.mod_assign.numberofsubmittedassignments": "Pateikta", - "addon.mod_assign.numberofteams": "Grupės", - "addon.mod_assign.numwords": "Žodžių: {{$a}}", - "addon.mod_assign.outof": "{{$a.current}} iš {{$a.total}}", - "addon.mod_assign.overdue": "Vėluojama įkelti darbą: {{$a}}", - "addon.mod_assign.submission": "Įkeltas darbas", - "addon.mod_assign.submissioneditable": "Besimokantysis gali keisti savo darbą", - "addon.mod_assign.submissionnoteditable": "Besimokantysis negali keisti šio įkelto darbo", - "addon.mod_assign.submissionnotsupported": "Programėlė negali patvirtinti ir pateikti visos informacijos", - "addon.mod_assign.submissionslocked": "Užduotis nebeleidžia įkelti darbų", - "addon.mod_assign.submissionstatus": "Įkelto darbo būsena", - "addon.mod_assign.submissionstatus_": "Nėra darbų", - "addon.mod_assign.submissionstatus_draft": "Juodraštis (nepateiktas)", - "addon.mod_assign.submissionstatus_marked": "Įvertinta", - "addon.mod_assign.submissionstatus_new": "Nėra pateikimo", - "addon.mod_assign.submissionstatus_reopened": "Vėl atidarytas", - "addon.mod_assign.submissionstatus_submitted": "Pateikta vertinimui", - "addon.mod_assign.submissionstatusheading": "Vertinimo būsena", - "addon.mod_assign.submissionteam": "Grupė", - "addon.mod_assign.submitassignment": "Pateikti darbą", - "addon.mod_assign.submitassignment_help": "Kai darbas bus pateiktas, Jūs negalėsite padaryti jokių pakeitimų.", - "addon.mod_assign.submittedearly": "Užduotis buvo įkelta {{$a}} anksčiau", - "addon.mod_assign.submittedlate": "Užduotis buvo įkelta vėluojant {{$a}}", - "addon.mod_assign.timemodified": "Paskutinį kartą keista", - "addon.mod_assign.timeremaining": "Liko laiko", - "addon.mod_assign.ungroupedusers": "Nuostata \"Reikalinga grupė, kad pateikti darbus\" yra įjungta, tačiau kai kurie dalyviai nėra grupės nariai ar priklauso daugiau nei vienai grupei, todėl jie negali pateikti savo pateikimo.", - "addon.mod_assign.unlimitedattempts": "Neribotai", - "addon.mod_assign.userswhoneedtosubmit": "Naudotojai, kurie turi pateikti darbą: {{$a}}", - "addon.mod_assign.userwithid": "Naudotojas su ID {{id}}", - "addon.mod_assign.viewsubmission": "Peržiūrėti darbą", - "addon.mod_assign.warningsubmissiongrademodified": "Pakeistas vertinimas.", - "addon.mod_assign.warningsubmissionmodified": "Pakeistas vartotojo pateikimas.", - "addon.mod_assign.wordlimit": "Žodžių limitas", - "addon.mod_assign_feedback_comments.pluginname": "Grįžtamojo ryšio komentarai", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF komentarai", - "addon.mod_assign_feedback_file.pluginname": "Failo atsiliepimas", - "addon.mod_assign_submission_comments.pluginname": "Pateikimo komentarai", - "addon.mod_assign_submission_file.pluginname": "Failų pateikimai", - "addon.mod_assign_submission_onlinetext.pluginname": "Internetinio teksto pateikimai", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Žodžių skaičius šiai užduočiai yra {{$a.limit}} žodžiai, o Jūs bandote pateikti {{$a.count}} žodžius. Prašome peržiūrėti savo pateikimą ir bandyti dar kartą.", - "addon.mod_book.errorchapter": "Knygos skyriaus skaitymo klaida.", - "addon.mod_book.modulenameplural": "Knygos", - "addon.mod_book.navnexttitle": "Kitas: {{$a}}", - "addon.mod_book.navprevtitle": "Ankstesnis: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Knygos skyriai", - "addon.mod_book.toc": "Turinys", - "addon.mod_chat.beep": "Pyp", - "addon.mod_chat.chatreport": "Pokalbių seansai", - "addon.mod_chat.currentusers": "Dabartiniai naudotojai", - "addon.mod_chat.enterchat": "Spustelėkite čia, kad prisijungtumėte prie pokalbio dabar", - "addon.mod_chat.entermessage": "Įveskite savo žinutę", - "addon.mod_chat.errorwhileconnecting": "Įvyko klaida jungiantis prie pokalbio.", - "addon.mod_chat.errorwhilegettingchatdata": "Klaida gaunant pokalbio duomenis.", - "addon.mod_chat.errorwhilegettingchatusers": "Klaida gaunant pokalbio dalyvių informaciją.", - "addon.mod_chat.errorwhileretrievingmessages": "Klaida gaunant žinutes iš serverio.", - "addon.mod_chat.errorwhilesendingmessage": "Klaida siunčiant žinutę.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} pyptelėjo visiems!", - "addon.mod_chat.messagebeepsyou": "{{$a}} ką tik pyptelėjo jums!", - "addon.mod_chat.messageenter": "{{$a}} ką tik prisijungė prie šio pokalbio", - "addon.mod_chat.messageexit": "{{$a}} ką tik paliko šį pokalbį", - "addon.mod_chat.messages": "Žinutės", - "addon.mod_chat.messageyoubeep": "Jūs pyptelėjote {{$a}}", - "addon.mod_chat.modulenameplural": "Pokalbiai", - "addon.mod_chat.mustbeonlinetosendmessages": "Norėdamas išsiųsti žinutę, turite būti prisijungęs.", - "addon.mod_chat.nomessages": "Žinučių dar nėra", - "addon.mod_chat.saidto": "pasakė", - "addon.mod_chat.send": "Siųsti", - "addon.mod_chat.sessionstart": "Kitas pokalbio seansas prasidės {{$a.date}}, ({{$a.fromnow}} nuo dabar)", - "addon.mod_chat.talk": "Kalbėti", - "addon.mod_chat.viewreport": "Peržiūrėti ankstesnius pokalbių seansus", - "addon.mod_choice.cannotsubmit": "Atsiprašome, buvo problema pateikiant tavo pasirinkimą. Prašau, pabandykite dar kartą.", - "addon.mod_choice.choiceoptions": "Pasirinkimo parinktys", - "addon.mod_choice.errorgetchoice": "Klaida gaunant alternatyvius duomenis.", - "addon.mod_choice.expired": "Ši veikla uždaryta {{$a}}.", - "addon.mod_choice.full": "(Visas)", - "addon.mod_choice.modulenameplural": "Pasirinkimai", - "addon.mod_choice.noresultsviewable": "Rezultatai dabar nematomi.", - "addon.mod_choice.notopenyet": "Ši veikla negalima iki {{$a}}.", - "addon.mod_choice.numberofuser": "Atsakymų skaičius", - "addon.mod_choice.numberofuserinpercentage": "Atsakymų procentas", - "addon.mod_choice.previewonly": "Tai yra tik šios veiklos galimų variantų peržiūra. Savo pasirinkimo negalėsite pateikti iki {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anoniminiai rezultatai bus paskelbti atsakius.", - "addon.mod_choice.publishinfoanonclose": "Anoniminiai rezultatai bus paskelbti, kai veikla bus uždaryta.", - "addon.mod_choice.publishinfofullafter": "Visi rezultatai, parodantys kiekvieno pasirinkimą, bus paskelbti atsakius.", - "addon.mod_choice.publishinfofullclose": "Visi rezultatai, parodantys kiekvieno pasirinkimą, bus paskelbti kai veikla yra uždaryta.", - "addon.mod_choice.publishinfonever": "Veiklos rezultatai nebus skelbiami atsakius.", - "addon.mod_choice.removemychoice": "Pašalinti mano pasirinkimą", - "addon.mod_choice.responses": "Atsakymai", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% dalyvių pasirinkusių pasirinkimą: {{tekstas}}", - "addon.mod_choice.responsesresultgraphheader": "Pavaizduota grafiku", - "addon.mod_choice.resultsnotsynced": "Galutinis atsakymas neužfiksuotas. Prašome sinchronizuotai atnaujinti duomenis.", - "addon.mod_choice.savemychoice": "Įrašyti mano pasirinkimą", - "addon.mod_choice.userchoosethisoption": "Naudotojas, kuris pasirenka šią parinktį", - "addon.mod_choice.yourselection": "Jūsų pasirinkimas", - "addon.mod_data.addentries": "Įtraukti įrašus", - "addon.mod_data.advancedsearch": "Išplėstinė paieška", - "addon.mod_data.alttext": "Alternatyvusis tekstas", - "addon.mod_data.approve": "Patvirtinti", - "addon.mod_data.approved": "Patvirtinta", - "addon.mod_data.ascending": "Didėjimo tvarka", - "addon.mod_data.authorfirstname": "Autoriaus vardas", - "addon.mod_data.authorlastname": "Autoriaus pavardė", - "addon.mod_data.confirmdeleterecord": "Ar tikrai norite naikinti šį įrašą?", - "addon.mod_data.descending": "Mažėjimo tvarka", - "addon.mod_data.disapprove": "Atšaukti patvirtinimą", - "addon.mod_data.emptyaddform": "Neužpildėte jokių laukų!", - "addon.mod_data.entrieslefttoadd": "Turite įtraukti dar {{$a.entriesleft}} įrašus (-ų), kad galėtumėte užbaigti šią veiklą", - "addon.mod_data.entrieslefttoaddtoview": "Turite įtraukti dar {{$a.entrieslefttoview}} įrašus (-ų), kad galėtumėte peržiūrėti kitų dalyvių įrašus.", - "addon.mod_data.errormustsupplyvalue": "Privalote pateikti reikšmę čia.", - "addon.mod_data.expired": "Apgailestaujame, ši veikla uždaryta {{$a}} ir nebegalima", - "addon.mod_data.fields": "Laukai", - "addon.mod_data.foundrecords": "Rasta įrašų: {{$a.num}}/{{$a.max}} (Nustatyti filtrus iš naujo)", - "addon.mod_data.latlongboth": "Abu, platuma ir ilguma, yra privalomi.", - "addon.mod_data.menuchoose": "Pasirinkite...", - "addon.mod_data.modulenameplural": "Duomenų bazės", - "addon.mod_data.more": "Daugiau", - "addon.mod_data.nomatch": "Nerasta atitinkančių įrašų!", - "addon.mod_data.norecords": "Duomenų bazėje nėra įrašų", - "addon.mod_data.notapproved": "Įrašas dar nepatvirtintas.", - "addon.mod_data.notopenyet": "Apgailestaujame, ši veikla negalima iki {{$a}}", - "addon.mod_data.numrecords": "{{$a}} įrašai (-ų)", - "addon.mod_data.other": "Kita", - "addon.mod_data.recordapproved": "Įrašas patvirtintas", - "addon.mod_data.recorddeleted": "Įrašas panaikintas", - "addon.mod_data.recorddisapproved": "Įrašas nepatvirtintas", - "addon.mod_data.resetsettings": "Nustatyti filtrus iš naujo", - "addon.mod_data.search": "Ieškoti", - "addon.mod_data.selectedrequired": "Visi pasirinkti būtini", - "addon.mod_data.single": "Peržiūrėti vieną elementą", - "addon.mod_data.tagarea_data_records": "Duomenų įrašai", - "addon.mod_data.timeadded": "Įtraukimo laikas", - "addon.mod_data.timemodified": "Modifikavimo laikas", - "addon.mod_data.usedate": "Įtraukti į paiešką.", - "addon.mod_feedback.analysis": "Analizė", - "addon.mod_feedback.anonymous": "Anonimiškai", - "addon.mod_feedback.anonymous_entries": "Anoniminiai įrašai ({{$a}})", - "addon.mod_feedback.average": "Vidurkis", - "addon.mod_feedback.complete_the_form": "Atsakyti į klausimus", - "addon.mod_feedback.completed_feedbacks": "Pateikti atsakymai", - "addon.mod_feedback.continue_the_form": "Tęsti atsakinėjimą į klausimus", - "addon.mod_feedback.feedback_is_not_open": "Atsiliepimas neatidarytas", - "addon.mod_feedback.feedbackclose": "Uždaryti atsiliepimą", - "addon.mod_feedback.feedbackopen": "Atidaryti atsiliepimą", - "addon.mod_feedback.mapcourses": "Susieti atsiliepimą su kursais", - "addon.mod_feedback.maximal": "Didžiausias", - "addon.mod_feedback.minimal": "Mažiausias", - "addon.mod_feedback.mode": "Režimas", - "addon.mod_feedback.modulenameplural": "Atsiliepimas", - "addon.mod_feedback.next_page": "Kitas puslapis", - "addon.mod_feedback.non_anonymous": "Naudotojo vardas bus užregistruotas ir rodomas su atsakymais", - "addon.mod_feedback.non_anonymous_entries": "Neanoniminiai įrašai ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Ne respondentai besimokantieji ({{$a}})", - "addon.mod_feedback.not_selected": "Nepasirinkta", - "addon.mod_feedback.not_started": "Nepradėta", - "addon.mod_feedback.numberoutofrange": "Skaičius nėra iš intervalo", - "addon.mod_feedback.overview": "Apžvalga", - "addon.mod_feedback.page_after_submit": "Puslapis po pateikimo", - "addon.mod_feedback.preview": "Peržiūra", - "addon.mod_feedback.previous_page": "Ankstesnis puslapis", - "addon.mod_feedback.questions": "Klausimai", - "addon.mod_feedback.response_nr": "Atsakymo numeris", - "addon.mod_feedback.responses": "Atsakymai", - "addon.mod_feedback.save_entries": "Pateikite savo atsakymus", - "addon.mod_feedback.show_entries": "Rodyti atsakymus", - "addon.mod_feedback.show_nonrespondents": "Rodyti ne respondentus", - "addon.mod_feedback.started": "Pradėjo", - "addon.mod_feedback.this_feedback_is_already_submitted": "Jau užbaigėte šią veiklą.", - "addon.mod_folder.emptyfilelist": "Nėra ką rodyti.", - "addon.mod_folder.modulenameplural": "Aplankai", - "addon.mod_forum.addanewdiscussion": "Įtraukti naują diskusijų temą", - "addon.mod_forum.addanewquestion": "Įtraukti naują klausimą", - "addon.mod_forum.addanewtopic": "Įtraukti naują temą", - "addon.mod_forum.addtofavourites": "Pažymėti šią diskusiją", - "addon.mod_forum.advanced": "Papildomai", - "addon.mod_forum.cannotadddiscussion": "Norint įtraukti diskusijų į šį forumą, būtina grupės narystė.", - "addon.mod_forum.cannotadddiscussionall": "Neturite teisės įtraukti naujos diskusijų temos, skirtos visiems dalyviams.", - "addon.mod_forum.cannotcreatediscussion": "Nepavyko sukurti naujos diskusijos", - "addon.mod_forum.couldnotadd": "Jūsų skelbimo nepavyko įtraukti dėl nežinomos klaidos", - "addon.mod_forum.couldnotupdate": "Jūsų skelbimo nepavyko atnaujinti dėl nežinomos klaidos", - "addon.mod_forum.cutoffdatereached": "Šiame diskusijų forume yra pasiekta įrašų skelbimo galutinė data, todėl nebegalite daugiau skelbti įrašų.", - "addon.mod_forum.delete": "Naikinti", - "addon.mod_forum.deletedpost": "Skelbimas panaikintas", - "addon.mod_forum.deletesure": "Ar tikrai norite naikinti šį skelbimą?", - "addon.mod_forum.discussion": "Diskusijos tema", - "addon.mod_forum.discussionlistsortbycreatedasc": "Rikiuoti didėjimo tvarka pagal sukūrimo datą", - "addon.mod_forum.discussionlistsortbycreateddesc": "Rikiuoti mažėjimo tvarka pagal sukūrimo datą", - "addon.mod_forum.discussionlistsortbylastpostasc": "Rikiuoti didėjimo tvarka pagal paskutinio įrašo sukūrimo datą", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Rikiuoti mažėjimo tvarka pagal paskutinio įrašo sukūrimo datą", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Rikiuoti didėjimo tvarka pagal atsakymų skaičių", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Rikiuoti mažėjimo tvarka pagal atsakymų skaičių", - "addon.mod_forum.discussionlocked": "Ši diskusija buvo užrakinta, todėl jūs negalite pateikti atsakymus joje.", - "addon.mod_forum.discussionpinned": "Prisegta", - "addon.mod_forum.discussionsubscription": "Diskusijos prenumerata", - "addon.mod_forum.edit": "Redaguoti", - "addon.mod_forum.erroremptymessage": "Skelbimo žinutė negali būti tuščia", - "addon.mod_forum.erroremptysubject": "Skelbimo tema negali būti tuščia.", - "addon.mod_forum.errorgetforum": "Klaida gaunant forumo duomenis.", - "addon.mod_forum.errorgetgroups": "Klaida gaunant grupės nustatymus.", - "addon.mod_forum.favouriteupdated": "Pažymėjimo parinktis atnaujinta.", - "addon.mod_forum.forumnodiscussionsyet": "Pokalbio temų forume dar nėra.", - "addon.mod_forum.group": "Grupė", - "addon.mod_forum.lastpost": "Paskutinis skelbimas", - "addon.mod_forum.lockdiscussion": "Užrakinti šią diskusiją", - "addon.mod_forum.lockupdated": "Užrakto parinktis atnaujinta.", - "addon.mod_forum.message": "Žinutės tekstas", - "addon.mod_forum.modeflatnewestfirst": "Rodyti atsakymus standartiškai, pirmiausia rodant naujausią", - "addon.mod_forum.modeflatoldestfirst": "Rodyti atsakymus standartiškai, pirmiausia rodant seniausią", - "addon.mod_forum.modenested": "Rodyti atsakymus įdėtuoju formatu", - "addon.mod_forum.modulenameplural": "Forumai", - "addon.mod_forum.numdiscussions": "Pokalbių {{numdiscussions}}", - "addon.mod_forum.numreplies": "Atsakymų {{numreplies}}", - "addon.mod_forum.pindiscussion": "Prisegti šią diskusiją", - "addon.mod_forum.postisprivatereply": "Tai yra privatus atsakymas. Jis nėra matomas kitiems dalyviams.", - "addon.mod_forum.posttoforum": "Skelbti forume", - "addon.mod_forum.posttomygroups": "Skelbti kopiją visoms grupėms", - "addon.mod_forum.privatereply": "Atsakyti privačiai", - "addon.mod_forum.re": "Ats.:", - "addon.mod_forum.refreshdiscussions": "Atnaujinti pokalbius", - "addon.mod_forum.refreshposts": "Atnaujinti pokalbių įrašus", - "addon.mod_forum.removefromfavourites": "Nebežymėti šios diskusijos", - "addon.mod_forum.reply": "Atsakyti", - "addon.mod_forum.replyplaceholder": "Parašyti atsakymą ...", - "addon.mod_forum.subject": "Temos pavadinimas", - "addon.mod_forum.tagarea_forum_posts": "Forumo skelbimai", - "addon.mod_forum.unlockdiscussion": "Neberakinti šios diskusijos", - "addon.mod_forum.unpindiscussion": "Nebesegti šios diskusijos", - "addon.mod_forum.unread": "Neperskaityta", - "addon.mod_forum.unreadpostsnumber": "Neperskaitytų skelbimų: {{$a}}", - "addon.mod_forum.yourreply": "Jūsų atsakymas", - "addon.mod_glossary.addentry": "Įtraukti naują įrašą", - "addon.mod_glossary.aliases": "Raktažodis (-džiai)", - "addon.mod_glossary.attachment": "Priedas", - "addon.mod_glossary.browsemode": "Peržiūrėti įrašus", - "addon.mod_glossary.byalphabet": "Abėcėlės tvarka", - "addon.mod_glossary.byauthor": "Pagal autorių", - "addon.mod_glossary.bynewestfirst": "Naujausi", - "addon.mod_glossary.byrecentlyupdated": "Neseniai atnaujinti", - "addon.mod_glossary.bysearch": "Paieška", - "addon.mod_glossary.casesensitive": "Įvestis skiria didžiąsias ir mažąsias raides", - "addon.mod_glossary.categories": "Kategorijos", - "addon.mod_glossary.concept": "Sąvoka", - "addon.mod_glossary.definition": "Apibrėžimas", - "addon.mod_glossary.entrypendingapproval": "Patvirtinti įrašą.", - "addon.mod_glossary.entryusedynalink": "Šis įrašas turėtų būti automatiškai susietas", - "addon.mod_glossary.errconceptalreadyexists": "Tokia sąvoka jau yra. Dubliuoti šiame žodyne neleidžiama.", - "addon.mod_glossary.errorloadingentries": "Klaida keliant įrašus.", - "addon.mod_glossary.errorloadingentry": "Klaida įkeliant įrašą.", - "addon.mod_glossary.errorloadingglossary": "Klaida įkeliant žodynėlį.", - "addon.mod_glossary.fillfields": "Sąvokos ir apibrėžimo laukus būtina užpildyti.", - "addon.mod_glossary.fullmatch": "Sieti tik visiškai atitinkančią žodžio formą", - "addon.mod_glossary.linking": "Automatinis susiejimas", - "addon.mod_glossary.modulenameplural": "Žodynai", - "addon.mod_glossary.noentriesfound": "Įrašų nėra.", - "addon.mod_glossary.searchquery": "Paieškos eilutė", - "addon.mod_glossary.tagarea_glossary_entries": "Žodyno įrašai", - "addon.mod_imscp.deploymenterror": "Turinio paketo klaida!", - "addon.mod_imscp.modulenameplural": "IMS turinio paketai", - "addon.mod_imscp.showmoduledescription": "Apibūdinimas", - "addon.mod_imscp.toc": "Turinys", - "addon.mod_lesson.answer": "Atsakyti", - "addon.mod_lesson.attempt": "Bandymas: {{$a}}", - "addon.mod_lesson.attemptheader": "Bandymas", - "addon.mod_lesson.attemptsremaining": "Turite likusių bandymų: {{$a}}", - "addon.mod_lesson.averagescore": "Vidutinis balas", - "addon.mod_lesson.averagetime": "Vidutinis laikas", - "addon.mod_lesson.branchtable": "Turinys", - "addon.mod_lesson.cannotfindattempt": "Klaida: nepavyko rasti bandymo", - "addon.mod_lesson.cannotfinduser": "Klaida: nepavyko rasti naudotojų", - "addon.mod_lesson.clusterjump": "Telkinyje nematomas klausimas", - "addon.mod_lesson.completed": "Baigta", - "addon.mod_lesson.congratulations": "Sveikiname. Pasiekta pamokos pabaiga.", - "addon.mod_lesson.continue": "Tęsti", - "addon.mod_lesson.continuetonextpage": "Toliau į sekantį puslapį.", - "addon.mod_lesson.defaultessayresponse": "Jūsų esė įvertins kurų dėstytojas.", - "addon.mod_lesson.detailedstats": "Išsamūs statistiniai duomenys", - "addon.mod_lesson.didnotanswerquestion": "Neatsakė į šį klausimą.", - "addon.mod_lesson.displayofgrade": "Įverčių rodymas (tik besimokantiesiems)", - "addon.mod_lesson.displayscorewithessays": "Surinkote {{$a.score}} iš {{$a.tempmaxgrade}} už automatiškai vertinamus klausimus.
                Vėliau jūsų {{$a.essayquestions}} esė klausimas (-ai) bus įvertintas (-i) ir įtrauktas (-i)
                į galutinį balą.

                Dabartinis jūsų įvertis be esė klausimo (-ų) yra {{$a.score}} iš {{$a.grade}}.", - "addon.mod_lesson.displayscorewithoutessays": "Jūsų balas yra {{$a.score}} (iš {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Slaptažodis negali būti tuščias", - "addon.mod_lesson.enterpassword": "Įveskite slaptažodį:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Neatsakėte nė į vieną klausimą. Už šią pamoką surinkote 0 balų.", - "addon.mod_lesson.finish": "Pabaigti", - "addon.mod_lesson.firstwrong": "Jūs atsakėte neteisingai. Ar norėtumėte dar kartą bandyti klausimą? (Jei dabar jūs teisingai atsakysite į klausimą, tai galutinio rezultato nepakeis.)", - "addon.mod_lesson.gotoendoflesson": "Eiti į pamokos pabaigą", - "addon.mod_lesson.grade": "Įvertis", - "addon.mod_lesson.highscore": "Aukštas balas", - "addon.mod_lesson.hightime": "Ilga trukmė", - "addon.mod_lesson.leftduringtimed": "Išėjote vykstant nustatyto laiko pamokai.
                Spustelėkite Tęsti ir pradėkite pamoką iš naujo.", - "addon.mod_lesson.leftduringtimednoretake": "Išėjote vykstant nustatyto laiko pamokai. Jums
                neleidžiama bandyti iš naujo arba tęsti pamokos.", - "addon.mod_lesson.lessonmenu": "Pamokos meniu", - "addon.mod_lesson.lessonstats": "Pamokų statistiniai duomenys", - "addon.mod_lesson.linkedmedia": "Susietoji medija", - "addon.mod_lesson.loginfail": "Nepavyko prisijungti, bandykite dar kartą.", - "addon.mod_lesson.lowscore": "Žemas balas", - "addon.mod_lesson.lowtime": "Trumpa trukmė", - "addon.mod_lesson.maximumnumberofattemptsreached": "Pasiektas maksimalus bandymų skaičius. Perkeliama į kitą puslapį.", - "addon.mod_lesson.modattemptsnoteacher": "Besimokančiųjų peržiūrą gali naudoti tik besimokantieji.", - "addon.mod_lesson.modulenameplural": "Pamokos", - "addon.mod_lesson.noanswer": "Nepateiktas atsakymas. Grįžkite atgal ir pateikite atsakymą.", - "addon.mod_lesson.nolessonattempts": "Šioje pamokoje dar nebuvo bandymų.", - "addon.mod_lesson.nolessonattemptsgroup": "{{$a}} grupės nariai/ių šioje pamokoje nieko nebandė atlikti.", - "addon.mod_lesson.notcompleted": "Nebaigta", - "addon.mod_lesson.numberofcorrectanswers": "Teisingų atsakymų skaičius: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Atsakytų klausimų skaičius: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Atsakytų klausimų skaičius: {{$a.nquestions}} (Turite atsakyti ne mažiau kaip {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Iki šiol esate surinkę taškų: {{$a.score}} iš {{$a.currenthigh}}.", - "addon.mod_lesson.ongoingnormal": "Teisingai atsakėte {{$a.correct}} iš {{$a.viewed}} bandymų.", - "addon.mod_lesson.or": "ARBA", - "addon.mod_lesson.overview": "Apžvalga", - "addon.mod_lesson.preview": "Peržiūra", - "addon.mod_lesson.progressbarteacherwarning2": "Eigos juostos nematysite, kadangi galite redaguoti šią pamoką.", - "addon.mod_lesson.progresscompleted": "Jūs užbaigėte {{$a}}% pamokos", - "addon.mod_lesson.question": "Klausimas", - "addon.mod_lesson.rawgrade": "Neapdorotas įvertis", - "addon.mod_lesson.reports": "Ataskaitos", - "addon.mod_lesson.response": "Atsakymas", - "addon.mod_lesson.review": "Peržiūrėti", - "addon.mod_lesson.reviewlesson": "Peržiūrėti pamoką", - "addon.mod_lesson.reviewquestionback": "Taip, norėčiau bandyti dar kartą", - "addon.mod_lesson.reviewquestioncontinue": "Ne, norėčiau pereiti prie kito klausimo", - "addon.mod_lesson.secondpluswrong": "Ne visai. Ar norėtumėte bandyti dar kartą?", - "addon.mod_lesson.submit": "Pateikti", - "addon.mod_lesson.teacherjumpwarning": "Šioje pamokoje naudojamas { $a.cluster}} saitas arba {{$a.unseen}} saitas. Vietoj jo bus naudojamas kito puslapio saitas. Prisijunkite kaip besimokantysis ir patikrinkite šiuos saitus.", - "addon.mod_lesson.teacherongoingwarning": "Esamas balas rodomas tik besimokančiajam. Prisijunkite kaip besimokantysis ir patikrinkite esamą balą.", - "addon.mod_lesson.teachertimerwarning": "Laikmatį gali naudoti tik besimokantieji. Prisijunkite kaip besimokantysis ir patikrinkite laikmatį.", - "addon.mod_lesson.thatsthecorrectanswer": "Tai yra teisingas atsakymas", - "addon.mod_lesson.thatsthewronganswer": "Tai yra klaidingas atsakymas", - "addon.mod_lesson.timeremaining": "Liko laiko", - "addon.mod_lesson.timetaken": "Sugaišta laiko", - "addon.mod_lesson.unseenpageinbranch": "Nematytas klausimas turinio puslapyje", - "addon.mod_lesson.welldone": "Gerai!", - "addon.mod_lesson.youhaveseen": "Jau matėte kelis šios pamokos puslapius.
                Ar norite pradėti nuo paskutinio jūsų matyto puslapio?", - "addon.mod_lesson.youranswer": "Jūsų atsakymas", - "addon.mod_lesson.yourcurrentgradeisoutof": "Dabartinis jūsų įvertis yra {{$a.grade}} iš {{$a.total}}", - "addon.mod_lesson.youshouldview": "Mažiausiai turite atsakyti: {{$a}}", - "addon.mod_lti.errorgetlti": "Klaida gaunant modulio duomenis.", - "addon.mod_lti.errorinvalidlaunchurl": "Pateikras URL negalioja.", - "addon.mod_lti.launchactivity": "Pateikti veiksmus", - "addon.mod_lti.modulenameplural": "Išoriniai įrankiai", - "addon.mod_page.errorwhileloadingthepage": "Klaiuda užkraunant puslapį.", - "addon.mod_page.modulenameplural": "Puslapiai", - "addon.mod_quiz.answercolon": "Atsakymas:", - "addon.mod_quiz.attemptfirst": "Pirmasis bandymas", - "addon.mod_quiz.attemptlast": "Paskutinis bandymas", - "addon.mod_quiz.attemptnumber": "Bandymas", - "addon.mod_quiz.attemptquiznow": "Bandyti testą dabar", - "addon.mod_quiz.attemptstate": "Būsena", - "addon.mod_quiz.cannotsubmitquizdueto": "Testas negali būti pateiktas dėl šių priežasčių:", - "addon.mod_quiz.clearchoice": "Valyti mano pasirinkimą", - "addon.mod_quiz.comment": "Komentaras", - "addon.mod_quiz.completedon": "Baigta", - "addon.mod_quiz.confirmclose": "Pateikę bandymą, savo atsakymų keisti nebegalėsite.", - "addon.mod_quiz.confirmcontinueoffline": "Bandymas nesinchronizuojamas nuo {{$a}}. Jeigu mėginsite per kitą įrenginį, duomenys bus prarasti.", - "addon.mod_quiz.confirmleavequizonerror": "Klaida išsaugant atsakymus. Ar norite išeiti iš testo?", - "addon.mod_quiz.confirmstart": "Testas turi {{$a}} laiko limitą. Kai pasirinksite Pradėti, laikas pradedamas skaičiuoti ir turite pateikti savo sprendimą iki laiko pabaigos. Ar tikrai norite pradėti dabar?", - "addon.mod_quiz.confirmstartheader": "Laiko apribojimas testui", - "addon.mod_quiz.connectionerror": "Tinklo ryšys prarastas. (Automatiškai neišsaugota).\n\nPasižymėkite per paskutines kelias minutes įvestus atsakymus šiame puslapyje, tada bandykite dar kartą prisijungti.\n\nKai ryšys bus atkurtas, Jūsų atsakymai turi būti išsaugojami ir šis pranešimas pranyks.", - "addon.mod_quiz.continueattemptquiz": "Tęsti paskutinį bandymą", - "addon.mod_quiz.continuepreview": "Tęsti paskutinę peržiūrą", - "addon.mod_quiz.errorbehaviournotsupported": "Negalima pildyti testo, nes programėlė nesupranta veiksmų:", - "addon.mod_quiz.errordownloading": "Klaida siunčiant privalomus duomenis.", - "addon.mod_quiz.errorgetattempt": "Klaida gaunant bandymo duomenis.", - "addon.mod_quiz.errorgetquestions": "Klaida gaunant klausimus.", - "addon.mod_quiz.errorgetquiz": "Klaida gaunant testo duomenis.", - "addon.mod_quiz.errorparsequestions": "Klaida nuskaitant klausimus. Prašome mėginti užpildyti testą naršyklėje.", - "addon.mod_quiz.errorquestionsnotsupported": "Negalima pildyti testo, nes gali būti klausimų, kurių nėra programėlėje:", - "addon.mod_quiz.errorrulesnotsupported": "Negalima pildyti testo, nes programėlė neatpažįsta prieigos taisyklių:", - "addon.mod_quiz.errorsaveattempt": "Klaida saugant duomenis.", - "addon.mod_quiz.feedback": "Atsiliepimas", - "addon.mod_quiz.finishattemptdots": "Baigti bandymą...", - "addon.mod_quiz.finishnotsynced": "Baigta, bet nesinchronizuota", - "addon.mod_quiz.grade": "Įvertis", - "addon.mod_quiz.gradeaverage": "Vidutinis įvertis", - "addon.mod_quiz.gradehighest": "Aukščiausias įvertis", - "addon.mod_quiz.grademethod": "Vertinimo metodas", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Balai", - "addon.mod_quiz.modulenameplural": "Testai", - "addon.mod_quiz.mustbesubmittedby": "Atsakymą turi pateikti {{$a}}.", - "addon.mod_quiz.noquestions": "Dar neįtraukta jokių klausimų", - "addon.mod_quiz.noreviewattempt": "Jūs negalite peržiūrėti šio bandymo.", - "addon.mod_quiz.notyetgraded": "Dar neįvertinta", - "addon.mod_quiz.opentoc": "Atidaryti naršymo skirtuką.", - "addon.mod_quiz.outof": "{{$a.grade}} iš maks. {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} iš maks. {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Bendras atsiliepimas", - "addon.mod_quiz.overdue": "Uždelstas", - "addon.mod_quiz.overduemustbesubmittedby": "Šis bandymas jau pavėluotas. Jis jau turėjo būti pateiktas. Jei norite, kad šis testas būtų įvertintas, turite jį patvirtinti {{$a}}. Jei nepateiksite, tada balas šiam bandymui nebus skaičiuojamas.", - "addon.mod_quiz.preview": "Peržiūra", - "addon.mod_quiz.previewquiznow": "Peržiūrėti testą dabar", - "addon.mod_quiz.question": "Klausimas", - "addon.mod_quiz.quiznavigation": "Testo naršymas", - "addon.mod_quiz.quizpassword": "Testo slaptažodis", - "addon.mod_quiz.reattemptquiz": "Dar kartą bandyti testą", - "addon.mod_quiz.requirepasswordmessage": "Norint išmėginti testą, reikalingas slaptažodis", - "addon.mod_quiz.returnattempt": "Grįžti atgal", - "addon.mod_quiz.review": "Peržiūrėti", - "addon.mod_quiz.reviewofattempt": "Bandymo {{$a}} peržiūra", - "addon.mod_quiz.reviewofpreview": "Peržiūros peržiūra", - "addon.mod_quiz.showall": "Rodyti visus klausimus viename puslapyje", - "addon.mod_quiz.showeachpage": "Tuo pat metu rodyti tik vieną puslapį", - "addon.mod_quiz.startattempt": "Pradėti bandymą", - "addon.mod_quiz.startedon": "Pradėta", - "addon.mod_quiz.stateabandoned": "Dar nepateikta", - "addon.mod_quiz.statefinished": "Baigta", - "addon.mod_quiz.statefinisheddetails": "Registruota {{$a}}", - "addon.mod_quiz.stateinprogress": "Nebaigta", - "addon.mod_quiz.stateoverdue": "Pavėluotas", - "addon.mod_quiz.stateoverduedetails": "Turi būti pateiktas {{$a}}", - "addon.mod_quiz.status": "Būsena", - "addon.mod_quiz.submitallandfinish": "Pateikti viską ir baigti", - "addon.mod_quiz.summaryofattempt": "Bandymo suvestinė", - "addon.mod_quiz.summaryofattempts": "Ankstesnių bandymų suvestinė", - "addon.mod_quiz.timeleft": "Likęs laikas", - "addon.mod_quiz.timetaken": "Sugaišta laiko", - "addon.mod_quiz.warningattemptfinished": "Bandymas neprisijungus atmestas nes buvo baigtas arba neaptiktas.", - "addon.mod_quiz.warningdatadiscarded": "Atsakymai suformuluoti neprisijungus buvo atmesti, nes klausimai formuoti prisijungus.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Bandymas nebaigtas dėl atmestų atsakymų. Prašome juos peržiūrėti ir bandyti dar kartą.", - "addon.mod_quiz.yourfinalgradeis": "Jūsų galutinis šio testo įvertis yra {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Klaida užkraunant turinį.", - "addon.mod_resource.modifieddate": "Atnaujinta {{$a}}", - "addon.mod_resource.modulenameplural": "Failai", - "addon.mod_resource.openthefile": "Atidaryti failą", - "addon.mod_resource.uploadeddate": "Įkelta {{$a}}", - "addon.mod_scorm.asset": "Turtas", - "addon.mod_scorm.assetlaunched": "Turtas – peržiūrėta", - "addon.mod_scorm.attempts": "Bandymai", - "addon.mod_scorm.averageattempt": "Vidutiniškai bandymų", - "addon.mod_scorm.browse": "Peržiūra", - "addon.mod_scorm.browsed": "Naršyta", - "addon.mod_scorm.browsemode": "Peržiūros režimas", - "addon.mod_scorm.cannotcalculategrade": "Negalima suskaičiuoti įvertinimo.", - "addon.mod_scorm.completed": "Užbaigta", - "addon.mod_scorm.contents": "Turinys", - "addon.mod_scorm.dataattemptshown": "Duomenys priklausom bandymui Nr. {{number}}.", - "addon.mod_scorm.enter": "Įvesti", - "addon.mod_scorm.errorcreateofflineattempt": "Klaida bandant pildyti neprisijungus. Bandykite dar kartą.", - "addon.mod_scorm.errordownloadscorm": "Klaida siunčiant SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Klaida gaunant SCORM duomenis.", - "addon.mod_scorm.errorinvalidversion": "Atsiprašome, programėlė palaiko tik SCROM 1.2. versiją.", - "addon.mod_scorm.errornotdownloadable": "SCORM paketų siųsti Moodle sistema negalima. Prašome susisiekti su Moodle svetainės administratoriumi.", - "addon.mod_scorm.errornovalidsco": "SCORM neturi matomo SCO įkelti.", - "addon.mod_scorm.errorpackagefile": "Atsiprašome, palaikomi tik ZIP paketai.", - "addon.mod_scorm.errorsyncscorm": "Klaida sinchronizuojant. Pabandykite dar kartą.", - "addon.mod_scorm.exceededmaxattempts": "Pasiekėte didžiausią leistiną bandymų skaičių.", - "addon.mod_scorm.failed": "Nepavyko", - "addon.mod_scorm.firstattempt": "Pirmasis bandymas", - "addon.mod_scorm.gradeaverage": "Vidutinis įvertis", - "addon.mod_scorm.gradeforattempt": "Bandymo įvertis", - "addon.mod_scorm.gradehighest": "Aukščiausias įvertis", - "addon.mod_scorm.grademethod": "Vertinimo metodas", - "addon.mod_scorm.gradereported": "Įvertis užregistruotas", - "addon.mod_scorm.gradescoes": "Mokymosi objektai", - "addon.mod_scorm.gradesum": "Įverčių suma", - "addon.mod_scorm.highestattempt": "Aukščiausiai įvertintas bandymas", - "addon.mod_scorm.incomplete": "Neužbaigta", - "addon.mod_scorm.lastattempt": "Paskutinis bandymas", - "addon.mod_scorm.modulenameplural": "SCORM paketai", - "addon.mod_scorm.newattempt": "Pradėti naują bandymą", - "addon.mod_scorm.noattemptsallowed": "Leidžiamų bandymų skaičius", - "addon.mod_scorm.noattemptsmade": "Išnaudotų bandymų skaičius", - "addon.mod_scorm.notattempted": "Nebandyta", - "addon.mod_scorm.offlineattemptnote": "Yra nesinchronizuotų duomenų.", - "addon.mod_scorm.offlineattemptovermax": "Bandymas nepatvirtintas, nes viršytas jų limitas.", - "addon.mod_scorm.organizations": "Organizacijos", - "addon.mod_scorm.passed": "Pavyko", - "addon.mod_scorm.reviewmode": "Peržiūros režimas", - "addon.mod_scorm.score": "Balas", - "addon.mod_scorm.scormstatusnotdownloaded": "SCORM neatsiųsta. Programa automatiškai atsisiųs ją atidarius.", - "addon.mod_scorm.scormstatusoutdated": "SCORM pakeista nuo paskutiniojo atsisiuntimo. Programa automatiškai atsisiųs ją atidarius.", - "addon.mod_scorm.suspended": "Sulaikyta", - "addon.mod_scorm.toc": "Turinys (Table Of Contents)", - "addon.mod_scorm.warningofflinedatadeleted": "Kai kurie neprijungti bandymo {{number}} duomenys buvo ištrinti, nes jie negalėjo būti perkelti į naują bandymą.", - "addon.mod_scorm.warningsynconlineincomplete": "Kaikurie bandymai negali būti sinchronizuoti su svetaine dėl to, kad paskutiniai prisijungimo duomenys nebuvo pabaigti. Prašome pabaigti.", - "addon.mod_survey.cannotsubmitsurvey": "Iškilo problema teikiant apklausą. Pabandykite dar kartą.", - "addon.mod_survey.errorgetsurvey": "Klaida gaunant apklausos duomenis.", - "addon.mod_survey.ifoundthat": "Sužinojau", - "addon.mod_survey.ipreferthat": "Teikiu pirmenybę", - "addon.mod_survey.modulenameplural": "Apklausos", - "addon.mod_survey.responses": "Atsakymai", - "addon.mod_survey.results": "Rezultatai", - "addon.mod_survey.surveycompletednograph": "Jūs baigėte šią apklausą.", - "addon.mod_url.accessurl": "URL prieiga", - "addon.mod_url.modulenameplural": "URL", - "addon.mod_url.pointingtourl": "URL šaltinis nurodo", - "addon.mod_wiki.cannoteditpage": "Jūs negalite redaguoti šio puslapio.", - "addon.mod_wiki.createpage": "Kurti puslapį", - "addon.mod_wiki.editingpage": "Redaguojamas puslapis '{{$a}}", - "addon.mod_wiki.errorloadingpage": "Klaida užkraunant puslapį.", - "addon.mod_wiki.errornowikiavailable": "Nėra Wiki turinio.", - "addon.mod_wiki.gowikihome": "Eiti į Wiki", - "addon.mod_wiki.map": "Susieti", - "addon.mod_wiki.modulenameplural": "Vikiai", - "addon.mod_wiki.newpagehdr": "Naujas puslapis", - "addon.mod_wiki.newpagetitle": "Naujo puslapio pavadinimas", - "addon.mod_wiki.nocontent": "Nėra šio puslapio turinio", - "addon.mod_wiki.notingroup": "Nėra grupėje", - "addon.mod_wiki.pageexists": "Šis puslapis jau yra.", - "addon.mod_wiki.pagename": "Puslapio pavadinimas", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Viki puslapiai", - "addon.mod_wiki.titleshouldnotbeempty": "Pavadinimo eilutė neturi būti tuščia", - "addon.mod_wiki.viewpage": "Peržiūrėti puslapį", - "addon.mod_wiki.wikipage": "Wiki puslapis", - "addon.mod_wiki.wrongversionlock": "Jums redaguojant kitas naudotojas redagavo šį puslapį, taigi jūsų turinys nebegalioja.", - "addon.mod_workshop.alreadygraded": "Jau įvertinta", - "addon.mod_workshop.areainstructauthors": "Pateikimo instrukcijos", - "addon.mod_workshop.areainstructreviewers": "Įvertinimo instrukcijos", - "addon.mod_workshop.assess": "Įvertinti", - "addon.mod_workshop.assessedsubmission": "Įvertintas pateiktas turinys", - "addon.mod_workshop.assessmentform": "Vertinimo forma", - "addon.mod_workshop.assessmentsettings": "Įvertinimo parametrai", - "addon.mod_workshop.assessmentweight": "Įvertinimo svarba", - "addon.mod_workshop.assignedassessments": "Įvertinti priskirtas pateiktas turinys", - "addon.mod_workshop.assignedassessmentsnone": "Nėra jums priskirto įvertinti turinio", - "addon.mod_workshop.conclusion": "Išvada", - "addon.mod_workshop.createsubmission": "Pateikti", - "addon.mod_workshop.deletesubmission": "Ištrinti pateikimą", - "addon.mod_workshop.editsubmission": "Redaguoti pateiktą turinį", - "addon.mod_workshop.feedbackauthor": "Autoriui skirtas atsiliepimas", - "addon.mod_workshop.feedbackby": "Atsakas iš {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Peržiūrėtojui skirtas atsiliepimas", - "addon.mod_workshop.givengrades": "Gauti įverčiai", - "addon.mod_workshop.gradecalculated": "Apskaičiuotas pateikto turinio įvertis", - "addon.mod_workshop.gradeinfo": "Įvertis: {{$a.received}} iš {{$a.max}}", - "addon.mod_workshop.gradeover": "Pakeisti pateikto turinio įvertį", - "addon.mod_workshop.gradesreport": "Seminaro įverčių ataskaita", - "addon.mod_workshop.gradinggrade": "Įvertis už įvertinimą", - "addon.mod_workshop.gradinggradecalculated": "Apskaičiuotas įvertis už įvertinimą", - "addon.mod_workshop.gradinggradeof": "Įvertis už įvertinimą (iš {{$a}})", - "addon.mod_workshop.gradinggradeover": "Pakeisti įvertį už įvertinimą", - "addon.mod_workshop.modulenameplural": "Seminarai", - "addon.mod_workshop.nogradeyet": "Dar nėra įverčio", - "addon.mod_workshop.notassessed": "Dar neįvertinta", - "addon.mod_workshop.notoverridden": "Nepakeistas", - "addon.mod_workshop.noyoursubmission": "Dar nepateikėte savo darbo", - "addon.mod_workshop.overallfeedback": "Bendras atsiliepimas", - "addon.mod_workshop.publishedsubmissions": "Publikuotas pateiktas turinys", - "addon.mod_workshop.publishsubmission": "Publikuoti pateiktą turinį", - "addon.mod_workshop.publishsubmission_help": "Publikuotas pateiktas turinys yra pasiekiamas kitiems uždarius seminarą.", - "addon.mod_workshop.reassess": "Įvertinti iš naujo", - "addon.mod_workshop.receivedgrades": "Gauti įverčiai", - "addon.mod_workshop.submissionattachment": "Priedas", - "addon.mod_workshop.submissioncontent": "Pateiktas turinys", - "addon.mod_workshop.submissiondeleteconfirm": "Ar tikrai norite ištrinti šį pateikimą?", - "addon.mod_workshop.submissiongrade": "Pateikto turinio įvertis", - "addon.mod_workshop.submissiongradeof": "Pateikto turinio įvertis (iš {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Jums reikia įvesti kokį nors tekstą ar pridėti failą.", - "addon.mod_workshop.submissionsreport": "Seminaro pateikimų ataskaita", - "addon.mod_workshop.submissiontitle": "Pavadinimas", - "addon.mod_workshop.switchphase10": "Perjungti į nustatymo etapą", - "addon.mod_workshop.switchphase20": "Perjungti į pateikimo etapą", - "addon.mod_workshop.switchphase30": "Perjungti į įvertinimo etapą", - "addon.mod_workshop.switchphase40": "Perjungti į vertinimo etapą", - "addon.mod_workshop.switchphase50": "Uždaryti seminarą", - "addon.mod_workshop.userplan": "Seminaro planavimo priemonė", - "addon.mod_workshop.userplancurrentphase": "Dabartinis etapas", - "addon.mod_workshop.weightinfo": "Svarba: {{$a}}", - "addon.mod_workshop.yourassessment": "Jūsų pačių įvertinimas", - "addon.mod_workshop.yourassessmentfor": "Jūsų įvertinimas {{$a}}", - "addon.mod_workshop.yourgrades": "Jūsų įverčiai", - "addon.mod_workshop.yoursubmission": "Jūsų pateiktas turinys", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Komentaras {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Įvertis {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspektas {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Jūs turite pasirinkti vertinimą pasirinktam aspektui", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Komentaras {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspektas {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Komentaras {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Įvertis {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Teiginys {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kriterijus {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Turite pasirinkti vieną iš šių elementų", - "addon.notes.addnewnote": "Įtraukti naują pastabą", - "addon.notes.coursenotes": "Kursų pastabos", - "addon.notes.deleteconfirm": "Naikinti šią pastabą?", - "addon.notes.eventnotecreated": "Pastaba sukurta", - "addon.notes.eventnotedeleted": "Pastaba ištrinta", - "addon.notes.nonotes": "Dar nėra šio tipo pastabų", - "addon.notes.note": "Pastaba", - "addon.notes.notes": "Pastabos", - "addon.notes.personalnotes": "Asmeninės pastabos", - "addon.notes.publishstate": "Kontekstas", - "addon.notes.sitenotes": "Svetainės pastabos", - "addon.notes.userwithid": "Vartotojo Id {{id}}", - "addon.notes.warningnotenotsent": "Negalima pridėto užrašo(-ų) šiems kursams {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Klaida gaunant pranešimus", - "addon.notifications.markallread": "Pažymėti visus kaip skaitytus", - "addon.notifications.notificationpreferences": "Pranešimų nuostatos", - "addon.notifications.notifications": "Pranešimai", - "addon.notifications.therearentnotificationsyet": "Nėra jokių pranešimų", - "assets.countries.AD": "Andora", - "assets.countries.AE": "Jungtiniai Arabų Emyratai", - "assets.countries.AF": "Afganistanas", - "assets.countries.AG": "Antigva ir Barbuda", - "assets.countries.AI": "Angilija", - "assets.countries.AL": "Albanija", - "assets.countries.AM": "Armėnija", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktida", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Amerikos Samoa", - "assets.countries.AT": "Austrija", - "assets.countries.AU": "Australija", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Alandų Salos", - "assets.countries.AZ": "Azerbaidžanas", - "assets.countries.BA": "Bosnija ir Hercegovina", - "assets.countries.BB": "Barbadosas", - "assets.countries.BD": "Bangladešas", - "assets.countries.BE": "Belgija", - "assets.countries.BF": "Burkina Fasas", - "assets.countries.BG": "Bulgarija", - "assets.countries.BH": "Bahreinas", - "assets.countries.BI": "Burundis", - "assets.countries.BJ": "Beninas", - "assets.countries.BL": "Sen Bartelemi", - "assets.countries.BM": "Bermudai", - "assets.countries.BN": "Brunėjaus Darusalamas", - "assets.countries.BO": "Bolivija", - "assets.countries.BR": "Brazilija", - "assets.countries.BS": "Bahamos", - "assets.countries.BT": "Butanas", - "assets.countries.BV": "Buvė Sala", - "assets.countries.BW": "Botsvana", - "assets.countries.BY": "Baltarusija", - "assets.countries.BZ": "Belizas", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Kokosų (Kilingo) Salos", - "assets.countries.CD": "Kongo Demokratinė Respublika", - "assets.countries.CF": "Centrinės Afrikos Respublika", - "assets.countries.CG": "Kongas", - "assets.countries.CH": "Šveicarija", - "assets.countries.CI": "Dramblio Kaulo Krantas", - "assets.countries.CK": "Kuko Salos", - "assets.countries.CL": "Čilė", - "assets.countries.CM": "Kamerūnas", - "assets.countries.CN": "Kinija", - "assets.countries.CO": "Kolumbija", - "assets.countries.CR": "Kosta Rika", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Žaliasis Kyšulys", - "assets.countries.CX": "Kalėdų Sala", - "assets.countries.CY": "Kipras", - "assets.countries.CZ": "Čekija", - "assets.countries.DE": "Vokietija", - "assets.countries.DJ": "Džibutis", - "assets.countries.DK": "Danija", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Dominikos Respublika", - "assets.countries.DZ": "Alžyras", - "assets.countries.EC": "Ekvadoras", - "assets.countries.EE": "Estija", - "assets.countries.EG": "Egiptas", - "assets.countries.EH": "Vakarų Sachara", - "assets.countries.ER": "Eritrėja", - "assets.countries.ES": "Ispanija", - "assets.countries.ET": "Etiopija", - "assets.countries.FI": "Suomija", - "assets.countries.FJ": "Fidžis", - "assets.countries.FK": "Folklando (Malvinų) Salos", - "assets.countries.FM": "Mikronezijos Federacinės Valstijos", - "assets.countries.FO": "Farerų Salos", - "assets.countries.FR": "Prancūzija", - "assets.countries.GA": "Gabonas", - "assets.countries.GB": "Jungtinė Karalystė", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Gruzija", - "assets.countries.GF": "Prancūzijos Gviana", - "assets.countries.GG": "Gernsis", - "assets.countries.GH": "Gana", - "assets.countries.GI": "Gibraltaras", - "assets.countries.GL": "Grenlandija", - "assets.countries.GM": "Gambija", - "assets.countries.GN": "Gvinėja", - "assets.countries.GP": "Gvadelupa", - "assets.countries.GQ": "Pusiaujo Gvinėja", - "assets.countries.GR": "Graikija", - "assets.countries.GS": "Pietų Džordžijos ir Pietų Sandvičo Salos", - "assets.countries.GT": "Gvatemala", - "assets.countries.GU": "Guamas", - "assets.countries.GW": "Bisau Gvinėja", - "assets.countries.GY": "Gajana", - "assets.countries.HK": "Honkongas", - "assets.countries.HM": "Herdo ir Makdonaldo Salos", - "assets.countries.HN": "Hondūras", - "assets.countries.HR": "Kroatija", - "assets.countries.HT": "Haitis", - "assets.countries.HU": "Vengrija", - "assets.countries.ID": "Indonezija", - "assets.countries.IE": "Airija", - "assets.countries.IL": "Izraelis", - "assets.countries.IM": "Meno Sala", - "assets.countries.IN": "Indija", - "assets.countries.IO": "Indijos Vandenyno Britų Sritis", - "assets.countries.IQ": "Irakas", - "assets.countries.IR": "Irano Islamo Respublika", - "assets.countries.IS": "Islandija", - "assets.countries.IT": "Italija", - "assets.countries.JE": "Džersis", - "assets.countries.JM": "Jamaika", - "assets.countries.JO": "Jordanija", - "assets.countries.JP": "Japonija", - "assets.countries.KE": "Kenija", - "assets.countries.KG": "Kirgizija", - "assets.countries.KH": "Kambodža", - "assets.countries.KI": "Kiribatis", - "assets.countries.KM": "Komorai", - "assets.countries.KN": "Sent Kitsas ir Nevis", - "assets.countries.KP": "Korėjos Liaudies Demokratinė Respublika", - "assets.countries.KR": "Korėjos Respublika", - "assets.countries.KW": "Kuveitas", - "assets.countries.KY": "Kaimanų Salos", - "assets.countries.KZ": "Kazachija", - "assets.countries.LA": "Laoso Liaudies Demokratinė Respublika", - "assets.countries.LB": "Lebanonas", - "assets.countries.LC": "Sent Lusija", - "assets.countries.LI": "Lichtenšteinas", - "assets.countries.LK": "Šri Lanka", - "assets.countries.LR": "Liberija", - "assets.countries.LS": "Lesotas", - "assets.countries.LT": "Lietuva", - "assets.countries.LU": "Liuksemburgas", - "assets.countries.LV": "Latvija", - "assets.countries.LY": "Libija", - "assets.countries.MA": "Marokas", - "assets.countries.MC": "Monakas", - "assets.countries.MD": "Moldovos Respublika", - "assets.countries.ME": "Montenegras", - "assets.countries.MF": "Sen Marten", - "assets.countries.MG": "Madagaskaras", - "assets.countries.MH": "Maršalo Salos", - "assets.countries.MK": "Buvusioji Jugoslavijos Respublika Makedonija", - "assets.countries.ML": "Malis", - "assets.countries.MM": "Mianmaras", - "assets.countries.MN": "Mongolija", - "assets.countries.MO": "Makao", - "assets.countries.MP": "Marianos Šiaurinės Salos", - "assets.countries.MQ": "Martinika", - "assets.countries.MR": "Mauritanija", - "assets.countries.MS": "Montseratas", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauricijus", - "assets.countries.MV": "Maldyvai", - "assets.countries.MW": "Malavis", - "assets.countries.MX": "Meksika", - "assets.countries.MY": "Malaizija", - "assets.countries.MZ": "Mozambikas", - "assets.countries.NA": "Namibija", - "assets.countries.NC": "Naujoji Kaledonija", - "assets.countries.NE": "Nigeris", - "assets.countries.NF": "Norfolko Sala", - "assets.countries.NG": "Nigerija", - "assets.countries.NI": "Nikaragva", - "assets.countries.NL": "Nyderlandai", - "assets.countries.NO": "Norvegija", - "assets.countries.NP": "Nepalas", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niujė", - "assets.countries.NZ": "Naujoji Zelandija", - "assets.countries.OM": "Omanas", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Prancūzijos Polinezija", - "assets.countries.PG": "Papua Naujoji Gvinėja", - "assets.countries.PH": "Filipinai", - "assets.countries.PK": "Pakistanas", - "assets.countries.PL": "Lenkija", - "assets.countries.PM": "Sen Pjeras ir Mikelonas", - "assets.countries.PN": "Pitkernas", - "assets.countries.PR": "Puerto Rikas", - "assets.countries.PS": "Okupuotoji Palestinos Teritorija", - "assets.countries.PT": "Portugalija", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paragvajus", - "assets.countries.QA": "Kataras", - "assets.countries.RE": "Reunjonas", - "assets.countries.RO": "Rumunija", - "assets.countries.RS": "Serbija", - "assets.countries.RU": "Rusija", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Saudo Arabija", - "assets.countries.SB": "Saliamono Salos", - "assets.countries.SC": "Seišeliai", - "assets.countries.SD": "Sudanas", - "assets.countries.SE": "Švedija", - "assets.countries.SG": "Singapūras", - "assets.countries.SH": "Šv. Elenos Sala", - "assets.countries.SI": "Slovėnija", - "assets.countries.SJ": "Svalbardas ir Janas Majenas", - "assets.countries.SK": "Slovakija", - "assets.countries.SL": "Siera Leonė", - "assets.countries.SM": "San Marinas", - "assets.countries.SN": "Senegalas", - "assets.countries.SO": "Somalis", - "assets.countries.SR": "Surinamas", - "assets.countries.ST": "San Tomė ir Prinsipė", - "assets.countries.SV": "Salvadoras", - "assets.countries.SY": "Sirijos Arabų Respublika", - "assets.countries.SZ": "Svazilandas", - "assets.countries.TC": "Terkso ir Kaikoso Salos", - "assets.countries.TD": "Čadas", - "assets.countries.TF": "Prancūzijos Pietų Sritys", - "assets.countries.TG": "Togas", - "assets.countries.TH": "Tailandas", - "assets.countries.TJ": "Tadžikija", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Rytų Timoras", - "assets.countries.TM": "Turkmėnija", - "assets.countries.TN": "Tunisas", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turkija", - "assets.countries.TT": "Trinidadas ir Tobagas", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taivanas", - "assets.countries.TZ": "Tanzanijos Jungtinė Respublika", - "assets.countries.UA": "Ukraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Jungtinių Valstijų Mažosios Tolimosios Salos", - "assets.countries.US": "Jungtinės Valstijos", - "assets.countries.UY": "Urugvajus", - "assets.countries.UZ": "Uzbekistanas", - "assets.countries.VA": "Šventasis Sostas (Vatikano Miesto Valstybė)", - "assets.countries.VC": "Sent Vinsentas ir Grenadinai", - "assets.countries.VE": "Venesuela", - "assets.countries.VG": "Mergelių Salos (Didžioji Britanija)", - "assets.countries.VI": "Mergelių Salos (JAV)", - "assets.countries.VN": "Vietnamas", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Volisas ir Futūna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemenas", - "assets.countries.YT": "Majotas", - "assets.countries.ZA": "Pietų Afrika", - "assets.countries.ZM": "Zambija", - "assets.countries.ZW": "Zimbabvė", - "assets.mimetypes.application/epub_zip": "„EPUB“ e.knyga", - "assets.mimetypes.application/msword": "„Word“ dokumentas", - "assets.mimetypes.application/pdf": "PDF dokumentas", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle atsarginė kopija", - "assets.mimetypes.application/vnd.ms-excel": "„Excel“ skaičiuoklė", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "„Excel 2007“ darbaknygė su makrokomandomis", - "assets.mimetypes.application/vnd.ms-powerpoint": "PowerPoint pateiktis", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "„OpenDocument“ skaičiuoklė", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "„OpenDocument“ skaičiuoklės šablonas", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument Teksto dokumentas", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument Teksto šablonas", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument Tinklalapio šablonas", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "PowerPoint 2007 pateiktis", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "„Powerpoint“ 2007 prezentacija", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "„Excel“ 2007 skaičiuoklė", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 šablonas", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 dokumentas", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "„iWork Keynote“ pateiktis", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "„iWork Numbers“ skaičiuoklė", - "assets.mimetypes.application/x-iwork-pages-sffpages": "„iWork Pages“ dokumentas", - "assets.mimetypes.application/x-javascript": "„JavaScript“ šaltinis", - "assets.mimetypes.application/x-mspublisher": "„Publisher\" dokumentas", - "assets.mimetypes.application/x-shockwave-flash": "„Flash\" animacija", - "assets.mimetypes.application/xhtml_xml": "XHTML dokumentas", - "assets.mimetypes.archive": "Archyvas ({{$a.EXT}})", - "assets.mimetypes.audio": "Garso failas ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "failas", - "assets.mimetypes.group:archive": "Archyvavimo failai", - "assets.mimetypes.group:audio": "Garso failai", - "assets.mimetypes.group:document": "Tekstinių dokumentų failai", - "assets.mimetypes.group:html_audio": "Garso failai palaikomi naršyklėse", - "assets.mimetypes.group:html_track": "HTML titrų failai", - "assets.mimetypes.group:html_video": "Vaizdo įrašų failai palaikomi naršyklėse", - "assets.mimetypes.group:image": "Paveikslėlių failai", - "assets.mimetypes.group:presentation": "Pateikčių failai", - "assets.mimetypes.group:sourcecode": "Šaltinio kodas", - "assets.mimetypes.group:spreadsheet": "Skaičiuoklės failai", - "assets.mimetypes.group:video": "Vaizdo įrašų failai", - "assets.mimetypes.group:web_audio": "Garso failai naudojami žiniatiklyje", - "assets.mimetypes.group:web_file": "Tinklalapio failai", - "assets.mimetypes.group:web_image": "Paveikslėlių failai naudojami žiniatinklyje", - "assets.mimetypes.group:web_video": "Vaizdo įrašų failai naudojami žiniatinklyje", - "assets.mimetypes.image": "Paveiksliukas ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows piktograma", - "assets.mimetypes.text/css": "Pakopinių stilių šablonas", - "assets.mimetypes.text/csv": "Kableliais atskirtos reikšmės", - "assets.mimetypes.text/html": "HTML dokumentas", - "assets.mimetypes.text/plain": "tekstinis failas", - "assets.mimetypes.text/rtf": "RTF dokumentas", - "assets.mimetypes.text/vtt": "Žiniatinklio vaizdo titrų failas", - "assets.mimetypes.video": "Vaizdo įrašo failas ({{$a.EXT}})", - "core.accounts": "Paskyros", - "core.add": "Įtraukti", - "core.agelocationverification": "Amžiaus ir vietovės patvirtinimas", - "core.ago": "Prieš {{$a}}", - "core.all": "Viskas", - "core.allgroups": "Visos grupės", - "core.allparticipants": "Visi dalyviai", - "core.answer": "Atsakymas", - "core.answered": "Atsakyta", - "core.areyousure": "Ar Jūs tikras?", - "core.back": "Grįžti", - "core.block.blocks": "Blokai", - "core.cancel": "Atšaukti", - "core.cannotconnect": "Negalima prisijungti: patikrinkite, ar teisingai įvedėte URL adresą ir ar Jūsų svetainė naudoja Moodle {{$a}} ar vėlesnę versiją.", - "core.cannotdownloadfiles": "Jūsų mobiliuoju ryšiu negalima atsisiųsti failo. Prašome susisiekti su svetainės administratoriumi.", - "core.category": "Kursų kategorija", - "core.choose": "Pasirinkite", - "core.choosedots": "Pasirinkite...", - "core.clearsearch": "Išvalyti peiškos laukelį", - "core.clicktohideshow": "Spustelėkite, kad išplėstumėte ar sutrauktumėte", - "core.clicktoseefull": "Paspauskite norėdami pamatyti visą turinį.", - "core.close": "Uždaryti", - "core.comments": "Komentarai", - "core.comments.addcomment": "Įtraukti komentarą...", - "core.comments.comments": "Komentarai", - "core.comments.commentscount": "Komentarai ({{$a}})", - "core.comments.deletecommentbyon": "Ištrinti komentarą paskelbtą {{$a.user}} {{$a.time}}", - "core.comments.eventcommentcreated": "Komentaras sukurtas", - "core.comments.eventcommentdeleted": "Komentaras ištrintas", - "core.comments.nocomments": "Jokių komentarų", - "core.comments.savecomment": "Įrašyti komentarą", - "core.commentscount": "Komentarai ({{$a}})", - "core.completion-alt-auto-fail": "Baigta: {{$a}} (negautas išlaikymo įvertis)", - "core.completion-alt-auto-n": "Nebaigta: {{$a}}", - "core.completion-alt-auto-n-override": "Nebaigta: {{$a.modname}} (pažymėjo {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Baigta: {{$a}} (gautas išlaikymo įvertis)", - "core.completion-alt-auto-y": "Baigta: {{$a}}", - "core.completion-alt-auto-y-override": "Užbaigta: {{$a.modname}} (pažymėjo {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Dar neužbaigta: {{$a}}. Pasirinkti pažymėti kaip užbaigtą", - "core.completion-alt-manual-n-override": "Dar neužbaigta: {{$a.modname}} (pažymėjo {{$a.overrideuser}}). Pasirinkti pažymėti kaip užbaigtą.", - "core.completion-alt-manual-y": "Užbaigta: {{$a}}. Pasirinkti pažymėti kaip nebaigtą", - "core.completion-alt-manual-y-override": "Užbaigta: {{$a.modname}} (pažymėjo {{$a.overrideuser}}). Pasirinkti pažymėti kaip neužbaigtą.", - "core.confirmcanceledit": "Ar tikrai norite išeiti iš šio puslapio? Visi pakeitimai bus prarasti.", - "core.confirmdeletefile": "Ar tikrai norite naikinti šį failą?", - "core.confirmopeninbrowser": "Ar norite tai atidaryti naršyklėje?", - "core.considereddigitalminor": "Esate per jaunas, kad galėtumėte sukurti paskyrą šioje svetainėje.", - "core.content": "Turinys", - "core.contenteditingsynced": "Turinys, kurį taisote, sinchronizuojamas.", - "core.contentlinks.chooseaccount": "Pasirinkti paskyrą", - "core.contentlinks.chooseaccounttoopenlink": "Pasirinkite paskyrą, su kuria atidarysite nuorodą.", - "core.contentlinks.confirmurlothersite": "Nuoroda priklauso kitai svetainei. Ar norite ją atidaryti?", - "core.contentlinks.errornoactions": "Nepavyko atlikti veiksmo su šia nuoroda.", - "core.contentlinks.errornosites": "Nepavyko rasti svetainės šiuo adresu.", - "core.continue": "Tęsti", - "core.course": "Kursai", - "core.course.activitynotyetviewableremoteaddon": "Jūsų įstaiga įdiegė papildinį, kuris dar nėra palaikomas.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Jūsų įstaigos Moodle versija turi būti atnaujinta.", - "core.course.allsections": "Visi pasirinkimai", - "core.course.askadmintosupport": "Susisiekite su svetainės administratoriumi, jeigu norite naudoti šią veiklą Moodle mobiliojoje programėlėje.", - "core.course.confirmdeletemodulefiles": "Ar tikrai norite ištrinti šiuos failus?", - "core.course.confirmdownload": "Norite atsisiųsti {{size}}. Ar norite tęsti?", - "core.course.confirmdownloadunknownsize": "Negalima apskaičiuoti failo, kurį norite atsisiųsti, dydžio. Ar tikrai norite atsisiųsti?", - "core.course.confirmpartialdownloadsize": "Norite atsisiųsti mažiausiai {{size}}. Ar tikrai norite tęsti?", - "core.course.contents": "Turiniai", - "core.course.couldnotloadsectioncontent": "Negalima užkrauti pasirinkto turinio, prašome pamėginti vėliau.", - "core.course.couldnotloadsections": "Negalima užkrauti pasirinkto turinio, prašome pamėginti vėliau.", - "core.course.coursesummary": "Kursų suvestinė", - "core.course.downloadcourse": "Atsisiųsti kursą", - "core.course.errordownloadingsection": "Klaida atsisiunčiant.", - "core.course.errorgetmodule": "Klaida gaunant kurso duomenis.", - "core.course.hiddenfromstudents": "Paslėpta nuo besimokančiųjų", - "core.course.hiddenoncoursepage": "Prieinama, bet nerodoma kurso puslapyje", - "core.course.nocontentavailable": "Turinys šiuo metu nepasiekiamas.", - "core.course.overriddennotice": "Šios veiklos galutinis įvertis buvo koreguotas rankiniu būdu.", - "core.course.sections": "Dalys", - "core.course.useactivityonbrowser": "Galite naudoti savo naršyklėje.", - "core.coursedetails": "Kurso informacija", - "core.courses.addtofavourites": "Pažymėti šį kursą", - "core.courses.allowguests": "Į šiuos kursus leidžiama įeiti svečiui", - "core.courses.availablecourses": "Kursai", - "core.courses.categories": "Kursų kategorijos", - "core.courses.confirmselfenrol": "Ar Jūs tikrai norite įsirašyti į šiuos mokymo kursus?", - "core.courses.courses": "Kursai", - "core.courses.enrolme": "Įrašyti mane", - "core.courses.errorloadcourses": "Klaida kraunant kursus.", - "core.courses.errorsearching": "Paieškos klaida.", - "core.courses.errorselfenrol": "Klaida įrašant.", - "core.courses.filtermycourses": "Filtruoti mano kursus", - "core.courses.frontpage": "Pirmas puslapis", - "core.courses.hidecourse": "Slėpti peržiūroje", - "core.courses.ignore": "Nepaisyti", - "core.courses.mycourses": "Mano kursai", - "core.courses.mymoodle": "Mano pagrindinis", - "core.courses.nocourses": "Nėra rodytinos kursų informacijos", - "core.courses.nocoursesyet": "Nėra šios kategorijos kursų", - "core.courses.nosearchresults": "Nėra rezultatų", - "core.courses.notenroled": "Nesate įsiregistravęs kaip šių kursų besimokantysis", - "core.courses.notenrollable": "Negalite įrašyti savęs į šį kursą.", - "core.courses.password": "Įrašymo raktas", - "core.courses.paymentrequired": "Už šiuos kursus reikia mokėti.", - "core.courses.paypalaccepted": "„PayPal“ mokėjimai priimami", - "core.courses.reload": "Įkelti iš naujo", - "core.courses.removefromfavourites": "Nebežymėti šio kurso", - "core.courses.search": "Ieškoti", - "core.courses.searchcourses": "Ieškoti kursų", - "core.courses.searchcoursesadvice": "Jūs galite naudoti kursų paieškos mygtuką ieškant kursų su svečio prieiga arba įrašyti save į kursus, kurie yra prieinami.", - "core.courses.selfenrolment": "Savarankiška registracija", - "core.courses.sendpaymentbutton": "Siųsti mokėjimą per „PayPal“", - "core.courses.show": "Rodyti šį kursą", - "core.courses.totalcoursesearchresults": "Visi kursai: {{$a}}", - "core.currentdevice": "Dabartinis prietaisas", - "core.datastoredoffline": "Duomenys saugomi įrenginyje, nes šiuo metu negalima išsiųsti. Bus vėlaiu išsiųsti automatiškai.", - "core.date": "Data", - "core.day": "diena", - "core.days": "dienos", - "core.decsep": ".", - "core.defaultvalue": "Numatyta ({{$a}})", - "core.delete": "Naikinti", - "core.deleteduser": "Panaikintas naudotojas", - "core.deleting": "Trinama", - "core.description": "Aprašas", - "core.dfdaymonthyear": "MM-DD-MMMM", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "v:mm", - "core.digitalminor_desc": "Paprašykite savo tėvo/globėjo susisiekti:", - "core.discard": "Atmesti", - "core.dismiss": "Praleisti", - "core.displayoptions": "Rodymo parinktys", - "core.done": "Atlikta", - "core.download": "Atsisiųsti", - "core.downloading": "Siunčiama", - "core.edit": "Redaguoti", - "core.editor.autosavesucceeded": "Juodraštis išsaugotas.", - "core.editor.bold": "Pajuodintas", - "core.editor.clear": "Išvalyti formatavimą", - "core.editor.h3": "Antraštė (didelė)", - "core.editor.h4": "Antraštė (vidutinė)", - "core.editor.h5": "Antraštė (maža)", - "core.editor.italic": "Pasviręs", - "core.editor.orderedlist": "Numeruotas sąrašas", - "core.editor.p": "Pastraipa", - "core.editor.strike": "Perbrauktas", - "core.editor.textrecovered": "Šio teksto juodraščio versija automatiškai atstatyta.", - "core.editor.underline": "Pabraukti", - "core.editor.unorderedlist": "Nerūšiuotas sąrašas", - "core.error": "Klaida", - "core.errorchangecompletion": "Klaida keičiant baigimo būseną. Pabandykite dar kartą.", - "core.errordeletefile": "Klaida trinant failą. Pabandykite dar kartą.", - "core.errordownloading": "Klaida siunčiant failą.", - "core.errordownloadingsomefiles": "Klaida siunčiant kursų failus. Jų gali trūkti.", - "core.errorfileexistssamename": "Failas tokiu pavadinimu jau yra.", - "core.errorinvalidform": "Formoje trūksta duomenų. Prašome užpildyti visus būtinus laukus galiojančia informacija.", - "core.errorinvalidresponse": "Gautas neteisingas atsakas. Susisiekite su Moodle administratoriumi jeigu tai kartojasi.", - "core.erroropenfilenoapp": "Klaida atidarant failą: nėra tinkamos programėlės jam atidaryti.", - "core.erroropenfilenoextension": "Klaida atidarant failą: jis neturi plėtinio.", - "core.erroropenpopup": "Šis veiksmas bus atidarytas atskirame lange. Programėlė to nepalaiko.", - "core.errorrenamefile": "Klaida pervadinant failą. Bandykite dar kartą.", - "core.errorsync": "Klaida sinchronizuojant. Bandykite dar kartą.", - "core.errorsyncblocked": "Dėl tebevykstančių procesų {{$a}} negali būti sinchronizuojama dabar. Bandykite dar kartą. Jei nepavyksta, bandykite iš naujo paleisti programą.", - "core.favourites": "Pažymėti žvaigždute", - "core.filename": "Failo pavadinimas", - "core.filenameexist": "Failas tokiu pavadinimu jau yra: {{$a}}", - "core.filenotfound": "Apgailestaujame, failas nerastas.", - "core.fileuploader.addfiletext": "Pridėti failą", - "core.fileuploader.audio": "Garsas", - "core.fileuploader.camera": "Fotoaparatas", - "core.fileuploader.confirmuploadfile": "Ketinate įkelti {{size}}. Ar norite tęsti?", - "core.fileuploader.confirmuploadunknownsize": "Negalima apskaičiuoti failo, kurį norite įkelti, dydžio. Ar tikrai norite tęsti?", - "core.fileuploader.errorcapturingaudio": "Klaida fiksuojant garsą.", - "core.fileuploader.errorcapturingimage": "Klaida fiksuojant paveiksliuką.", - "core.fileuploader.errorcapturingvideo": "Klaida fiksuojant vaizdinę medžiagą.", - "core.fileuploader.errorgettingimagealbum": "Klaida gaunant paveiksliuką iš albumo.", - "core.fileuploader.errormustbeonlinetoupload": "Norint įkelti failą reikia prisijungti.", - "core.fileuploader.errornoapp": "Jūs neturite programėlės šiam veiksmui atlikti.", - "core.fileuploader.errorreadingfile": "Klaida nuskaitant failą.", - "core.fileuploader.errorwhileuploading": "Klaida įkeliant failą.", - "core.fileuploader.file": "Failas", - "core.fileuploader.filesofthesetypes": "Priimami failų tipai:", - "core.fileuploader.fileuploaded": "Failas sėkmingai įkeltas.", - "core.fileuploader.invalidfiletype": "Failo tipas {{$a}} nepriimtinas.", - "core.fileuploader.maxbytesfile": "Failas {{$a.file}} ) per didelis. Galite įkelti failus iki {{$a.size}}.", - "core.fileuploader.more": "Daugiau", - "core.fileuploader.photoalbums": "Nuotraukų albumai", - "core.fileuploader.readingfile": "Skaitomas failas", - "core.fileuploader.selectafile": "Pasirinkite failą", - "core.fileuploader.uploadafile": "Įkelkite failą", - "core.fileuploader.uploading": "Įkeliama", - "core.fileuploader.uploadingperc": "Įkeliama: {{$a}}%", - "core.fileuploader.video": "Vaizdinė medžiaga", - "core.filter": "Filtras", - "core.folder": "Aplankas", - "core.forcepasswordchangenotice": "Norėdami tęsti, turite pakeisti slaptažodį.", - "core.fulllistofcourses": "Visi kursai", - "core.fullnameandsitename": "{{vardas, pavardė}} ({{svetainės pavadinimas}})", - "core.grades.average": "Vidurkis", - "core.grades.badgrade": "Pateiktas netinkamas įvertis", - "core.grades.contributiontocoursetotal": "Indėlis į kurso bendrą", - "core.grades.feedback": "Atsiliepimas", - "core.grades.grade": "Įvertis", - "core.grades.gradeitem": "Įverčio elementas", - "core.grades.grades": "Įverčiai", - "core.grades.lettergrade": "Raidinis įvertis", - "core.grades.nogradesreturned": "Įverčių nepateikta", - "core.grades.nooutcome": "Rezultato nėra", - "core.grades.percentage": "Procentai", - "core.grades.range": "Intervalas", - "core.grades.rank": "Rangas", - "core.grades.weight": "Svarba", - "core.group": "Grupė", - "core.groupsseparate": "Atskiros grupės", - "core.groupsvisible": "Matomos grupės", - "core.h5p.author": "Autorius", - "core.h5p.authorcomments": "Autoriaus komentarai", - "core.h5p.authorname": "Autoriaus vardas", - "core.h5p.authorrole": "Autoriaus vaidmuo", - "core.h5p.cancellabel": "Atšaukti", - "core.h5p.changedby": "Pakeista", - "core.h5p.close": "Uždaryti", - "core.h5p.confirmdialogheader": "Patvirtinti veiksmą", - "core.h5p.confirmlabel": "Patvirtinti", - "core.h5p.contenttype": "Turinio tipas", - "core.h5p.copyrightinfo": "Autorių teisių informacija", - "core.h5p.copyrightstring": "Autorių teisės", - "core.h5p.copyrighttitle": "Rodyti autorių teisių informaciją šiam turiniui.", - "core.h5p.date": "Data", - "core.h5p.editor": "Redaktorius", - "core.h5p.h5ptitle": "Aplankykite h5p.org kad pamatytumėte daugiau turinio.", - "core.h5p.hideadvanced": "Slėpti išplėstinius", - "core.h5p.license": "Licencija", - "core.h5p.licenseV1": "Versija 1", - "core.h5p.licenseV2": "Versija 2", - "core.h5p.licenseV3": "Versija 3", - "core.h5p.licenseversion": "Licencijos versija", - "core.h5p.nocopyright": "Nėra autorių teisių informacijos šiam turiniui.", - "core.hasdatatosync": "{{$a}} turi duomenis, kuriuos reikia sinchronizuoti.", - "core.help": "Žinynas", - "core.hide": "Slėpti", - "core.hour": "valanda", - "core.hours": "valandos", - "core.humanreadablesize": "{{dydis}} {{vienetai}}", - "core.image": "Paveiksliukas", - "core.imageviewer": "Paveiksliukų peržiūra", - "core.info": "Informacija", - "core.invalidformdata": "Neteisingi formos duomenys", - "core.labelsep": ":", - "core.lastaccess": "Paskutinė prieiga", - "core.lastmodified": "Paskutinį kartą keista", - "core.lastsync": "Paskutinis sinchronizavimas", - "core.layoutgrid": "Tinklelis", - "core.list": "Sąrašas", - "core.listsep": ";", - "core.loading": "Kraunasi", - "core.location": "Vieta", - "core.login.auth_email": "Registravimasis naudojant el. paštą", - "core.login.authenticating": "Autentifikuojama", - "core.login.cancel": "Atšaukti", - "core.login.changepassword": "Pakeisti slaptažodį", - "core.login.confirmdeletesite": "Ar tikrai norite ištrinti svetainę {{sitename}}?", - "core.login.connect": "Prisijungta!", - "core.login.connecttomoodle": "Prisijungti prie Moodle", - "core.login.contactyouradministrator": "Susisiekite su svetainės administratoriumi, jei reikalinga pagalba.", - "core.login.contactyouradministratorissue": "Dėl šių klausimų prašome susisiekti su administratoriumi: {{$a}}", - "core.login.createaccount": "Kurti naują mano paskyrą", - "core.login.createuserandpass": "Pasirinkite savo naudotojo vardą ir slaptažodį", - "core.login.credentialsdescription": "Prisijungti naudojant vartotojo vardą ir slaptažodį.", - "core.login.emailconfirmsent": "

                El. laiškas išsiųstas jūsų adresu {{$a}}

                .

                Jame pateikti paprasti nurodymai, kaip užbaigti registraciją.

                Jei iškils kokių sunkumų, kreipkitės į svetainės administratorių.

                ", - "core.login.emailconfirmsentsuccess": "Patvirtinimo el. laiškas išsiųstas sėkmingai", - "core.login.emailnotmatch": "El. paštas nesutampa", - "core.login.erroraccesscontrolalloworigin": "Kryžminis veiksmas buvo atmestas. Patikrinkite: https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Klaida trinant šią svetainę. Prašome mėginti vėl.", - "core.login.errorupdatesite": "Klaida atnaujinant svetainės kodą.", - "core.login.firsttime": "Ar jūs čia pirmą kartą?", - "core.login.forcepasswordchangenotice": "Norėdami tęsti, turite pakeisti slaptažodį.", - "core.login.forgotten": "Pamiršote savo naudotojo vardą ar slaptažodį?", - "core.login.help": "Žinynas", - "core.login.helpmelogin": "

                Visame pasaulyje Moodle svetainių yra labai daug. Ši programėlė gali prisijungti prie Moodle svetainių, turinčių specialią Moodle programėlių prieigą.

                Jeigu negalite prisijungti, praneškite apie tai Moodle administratoriui ir paprašykite perskaityti http://docs.moodle.org/en/Mobile_app

                Norėdami išbandyti programėlės demo versiją surinkite teacher (dėstytojui) ar student (besimokančiajam) Svetainės adreso lauke ir paspauskite Prisijungti mygtuką.

                ", - "core.login.instructions": "Instrukcijos", - "core.login.invalidaccount": "Prašome patikrinti prisijungimo duomenis arba paprašyti administratoriaus patikrinti svetainės nustatymus.", - "core.login.invaliddate": "Neteisinga data", - "core.login.invalidemail": "Neteisingas el. pašto adresas", - "core.login.invalidmoodleversion": "Negaliojanti Moodle versija. Turi būt ne senesnė nei {{$a}}.", - "core.login.invalidsite": "URL adresas netinkamas.", - "core.login.invalidtime": "Netinkamas laikas", - "core.login.invalidurl": "Nurodytas negaliojantis URL", - "core.login.invalidvaluemax": "Didžiausia vertė {{$a}}", - "core.login.invalidvaluemin": "Mažiausia vertė {{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle Mobilių Papildomų Funkcijų patikra gavo netikėtą atsakymą, būsite autentifikuojamas naudojant standartines mobilias paslaugas.", - "core.login.login": "Prisijungti", - "core.login.loginbutton": "Prisijungti", - "core.login.logininsiterequired": "Prisijunkite prie svetainės naršyklės lange.", - "core.login.loginsteps": "Jei norite gauti visą prieigą prie svetainės, turite pirmiausiai sukurti savo paskyrą.", - "core.login.missingemail": "Nėra el. pašto adreso", - "core.login.missingfirstname": "Nėra pavadinimo", - "core.login.missinglastname": "Nėra pavardės", - "core.login.mobileservicesnotenabled": "Mobilios paslaugos neįgalintos. Susisiekite su Moodle svetainės administratoriumi, jei šios paslaugos reikalingos.", - "core.login.mustconfirm": "Turite patvirtinti savo paskyrą", - "core.login.newaccount": "Nauja paskyra", - "core.login.notloggedin": "Turite prisijungti", - "core.login.password": "Slaptažodis", - "core.login.passwordforgotten": "Pamirštas slaptažodis", - "core.login.passwordforgotteninstructions2": "Jei norite iš naujo nustatyti slaptažodį, toliau pateikite savo naudotojo vardą arba el. pašto adresą. Jei jus rasime duomenų bazėje, jūsų el. pašto adresu bus išsiųstas el. laiškas su nurodymais, kaip vėl gauti prieigą.", - "core.login.passwordrequired": "Reikalingas slaptažodis", - "core.login.policyaccept": "Suprantu ir sutinku", - "core.login.policyagree": "Norėdami toliau naudotis šia svetaine, turite sutikti su šia strategija. Ar sutinkate?", - "core.login.policyagreement": "Svetainės strategijos sutartis", - "core.login.policyagreementclick": "Saitas su svetainės strategijos sutartimi", - "core.login.potentialidps": "Prisijungti su išorine paskyra:", - "core.login.profileinvaliddata": "Neleistina reikšmė", - "core.login.recaptchachallengeimage": "reCAPTCHA išbandymo paveiksliukas", - "core.login.reconnect": "Jungtis pakartotinai", - "core.login.reconnectdescription": "Jūsų atpažinimo kodas neteisingas arba negalioja, turėsite vėl prisijungti prie svetainės.", - "core.login.reconnectssodescription": "Jūsų atpažinimo kodas neteisingas arba negalioja, turėsite vėl prisijungti prie svetainės. Prisijunkite prie svetainės naršyklės lange.", - "core.login.resendemail": "Siųsti el. laišką dar kartą", - "core.login.security_question": "Saugumo klausimas", - "core.login.selectacountry": "Pasirinkti šalį", - "core.login.signupplugindisabled": "{{$a}} neįjungtas.", - "core.login.siteaddress": "Svetainės adresas", - "core.login.siteinmaintenance": "Svetainė palaikymo rėžime", - "core.login.siteurl": "URL adresas", - "core.login.siteurlrequired": "Reikalingas URL adresas, pvz., http://www.yourmoodlesite.abc arba https://www.yourmoodlesite.efg", - "core.login.startsignup": "Kurti naują paskyrą", - "core.login.stillcantconnect": "Vis dar negalite prisijungti?", - "core.login.supplyinfo": "Daugiau informacijos", - "core.login.username": "Naudotojo vardas", - "core.login.usernameoremail": "Įveskite naudotojo vardą ar el. pašto adresą", - "core.login.usernamerequired": "Būtinas vartotojo vardas", - "core.login.usernotaddederror": "Naudotojas neįtrauktas – klaida", - "core.login.visitchangepassword": "Ar norite pakeisti slaptažodį?", - "core.login.webservicesnotenabled": "Tinklo paslaugos neįgalintos. Susisiekite su Moodle administratoriumi jeigu manote, kad reikalinga įgalinti.", - "core.lostconnection": "Jūsų atpažinimo kodas neteisingas arba negalioja, turėsite vėl prisijungti prie svetainės.", - "core.mainmenu.changesite": "Pakeisti svetainę", - "core.mainmenu.help": "Žinynas", - "core.mainmenu.logout": "Atsijungti", - "core.mainmenu.website": "Interneto svetainė", - "core.maxsizeandattachments": "Maksimalus naujo failo dydis: {{$a.size}}, maksimalus priedų skaičius: {{$a.attachments}}", - "core.min": "min.", - "core.mins": "min.", - "core.misc": "Kita", - "core.mod_assign": "Užduotis", - "core.mod_assignment": "Užduotis (2.2) (Išjungta)", - "core.mod_book": "Knyga", - "core.mod_chat": "Pokalbis", - "core.mod_choice": "Pasirinkimas", - "core.mod_data": "Duomenų bazė", - "core.mod_database": "Duomenų bazė", - "core.mod_external-tool": "Išorinis įrankis", - "core.mod_feedback": "Atsiliepimas", - "core.mod_file": "Failas", - "core.mod_folder": "Aplankas", - "core.mod_forum": "Diskusijos", - "core.mod_glossary": "Žodynas", - "core.mod_ims": "IMS turinio paketas", - "core.mod_imscp": "IMS turinio paketas", - "core.mod_label": "Žyma", - "core.mod_lesson": "Pamoka", - "core.mod_lti": "Išorinis įrankis", - "core.mod_page": "Puslapis", - "core.mod_quiz": "Testas", - "core.mod_resource": "Failas", - "core.mod_scorm": "SCORM paketas", - "core.mod_survey": "Apklausa", - "core.mod_url": "Asmenys", - "core.mod_wiki": "Vikis", - "core.mod_workshop": "Seminaras", - "core.moduleintro": "Aprašas", - "core.more": "daugiau", - "core.mygroups": "Mano grupės", - "core.name": "Vardas", - "core.networkerrormsg": "Tinklas nepasiekiamas arba neveikia.", - "core.never": "Niekada", - "core.next": "Pirmyn", - "core.no": "Ne", - "core.nocomments": "Jokių komentarų", - "core.nograde": "Nėra įverčio", - "core.none": "Nėra", - "core.nopasswordchangeforced": "Nepakeitus slaptažodžio, negalima tęsti.", - "core.nopermissions": "Atsiprašome, tačiau šiuo metu jūs neturite teisės atlikti šio veiksmo ({{$a}}).", - "core.noresults": "Nėra rezultatų", - "core.noselection": "Nėra pasirinkimo", - "core.notapplicable": "netaikoma", - "core.notenrolledprofile": "Profilis nepasiekiamas, nes šis naudotojas neįsiregistravęs į šiuos kursus.", - "core.notice": "Įspėjimas", - "core.notingroup": "Atsiprašome, bet turite priklausyti grupei, kad matytumėte šį puslapį.", - "core.notsent": "Neišsiųsta", - "core.now": "dabar", - "core.numwords": "Žodžių: {{$a}}", - "core.offline": "Neprisijungęs", - "core.ok": "Gerai", - "core.online": "Prisijungęs", - "core.openfullimage": "Paspauskite, norėdami matyti visą vaizdą", - "core.openinbrowser": "Atidaryti naršyklėje", - "core.othergroups": "Kitos grupės", - "core.pagea": "{{$a}} puslapis", - "core.paymentinstant": "Naudokite toliau pateiktą mygtuką, kad sumokėtumėte ir būtumėte įregistruoti per kelias minutes.", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefonas", - "core.pictureof": "{{$a}} paveikslėlis", - "core.previous": "Ankstesnis", - "core.pulltorefresh": "Atnaujinti", - "core.question.answer": "Atsakymas", - "core.question.answersaved": "Atsakymas išsaugotas", - "core.question.complete": "Baigta", - "core.question.correct": "Teisinga", - "core.question.errorattachmentsnotsupported": "Programėlė nepalaiko pridėtų failų.", - "core.question.errorinlinefilesnotsupported": "Programėlė nepalaiko redaguojamų failų", - "core.question.errorquestionnotsupported": "Šis klausimo tipas programėlėje nepalaikomas: {{$a}}.", - "core.question.feedback": "Grįžtamasis ryšys", - "core.question.howtodraganddrop": "Paspauskite pasirinkimui, tada perneškite.", - "core.question.incorrect": "Neteisinga", - "core.question.information": "Informacija", - "core.question.invalidanswer": "Nepilnas atsakymas", - "core.question.notanswered": "Neatsakyta", - "core.question.notyetanswered": "Neatsakyta", - "core.question.partiallycorrect": "Iš dalies teisingas", - "core.question.questionmessage": "Klausimas {{$a}}: {{$b}}", - "core.question.questionno": "Klausimas {{$a}}", - "core.question.requiresgrading": "Reikia vertinimo", - "core.quotausage": "Dabar išnaudojama {{$a.used}} Jūsų turimo {{$a.total}} limito.", - "core.rating.aggregateavg": "Pažymių vidurkis", - "core.rating.aggregatecount": "Įverčių kiekis", - "core.rating.aggregatemax": "Maksimalus pažymys", - "core.rating.aggregatemin": "Minimalus pažymys", - "core.rating.aggregatesum": "Įverčių suma", - "core.rating.noratings": "Pažymių nepateikta", - "core.rating.rating": "Pažymys", - "core.rating.ratings": "Pažymiai", - "core.redirectingtosite": "Būsite nukreiptas į svetainę.", - "core.refresh": "Atnaujinti", - "core.remove": "Šalinti", - "core.required": "Būtina", - "core.requireduserdatamissing": "Trūkta vartotojo duomenų. Prašome užpildyti duomenis Moodle ir pabandyti dar kartą.
                {{$a}}", - "core.resourcedisplayopen": "Atidaryti", - "core.resources": "Ištekliai", - "core.restore": "Atkurti", - "core.restricted": "Apribota", - "core.retry": "Bandykite dar kartą", - "core.save": "Įrašyti", - "core.savechanges": "Išsaugoti pakeitimus", - "core.search": "Ieškoti", - "core.searching": "Ieškoma", - "core.searchresults": "Ieškos rezultatai", - "core.sec": "sek.", - "core.secs": "sek.", - "core.seemoredetail": "Spustelėkite čia, kad pamatytumėte daugiau informacijos", - "core.selectacategory": "Prašome pasirinkti kategoriją", - "core.selectacourse": "Pasirinkti paskaitą", - "core.selectagroup": "Pasirinkti grupę", - "core.send": "Siųsti", - "core.sending": "Siunčiama", - "core.serverconnection": "Klaida jungiantis į serverį", - "core.settings.about": "Apie", - "core.settings.cannotsyncoffline": "Negalima sinchronizuoti neprisijungus.", - "core.settings.cannotsyncwithoutwifi": "Negalima sinchronizuoti, nes nustatymai leidžia sinchronizuoti, kai prisijungta prie Wi-Fi tinklo. Prašome prisijungti.", - "core.settings.cordovadevicemodel": "Cordova įrenginio modelis", - "core.settings.cordovadeviceosversion": "Cordova įrenginio OS versija", - "core.settings.cordovadeviceplatform": "Cordova įrenginio platforma", - "core.settings.cordovadeviceuuid": "Cordova įrenginio uuid", - "core.settings.cordovaversion": "Cordova versija", - "core.settings.currentlanguage": "Vartojama kalba", - "core.settings.debugdisplay": "Rodyti derinimo žinutes", - "core.settings.deletesitefiles": "Ar esate tikri, kad norite ištrinti atsisiųstus failus iš svetainės '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Ištrinti svetainės failus", - "core.settings.deviceinfo": "Informacija apie įrenginį", - "core.settings.deviceos": "Įrenginio OS", - "core.settings.disableall": "Išjungti pranešimus", - "core.settings.disabled": "Išjungtas", - "core.settings.displayformat": "Rodyti formatą", - "core.settings.enabledownloadsection": "Įgalinti sekciją parsiuntimą", - "core.settings.enablerichtexteditor": "Įgalinti teksto redaktorių", - "core.settings.enablerichtexteditordescription": "Jeigu įgalinta, teksto redaktorius bus rodomas leidžiamose vietose.", - "core.settings.enablesyncwifi": "Sinchronizavimas galimas esant Wi-Fi ryšiui", - "core.settings.errordeletesitefiles": "Klaida ištrinant svetainės failus.", - "core.settings.errorsyncsite": "Klaida sinchronizuojant svetainės duomenis, prašome patikrinti interneto ryšį ir pabandyti dar kartą.", - "core.settings.estimatedfreespace": "Laisva vieta", - "core.settings.filesystemroot": "Failų sistema pradžia", - "core.settings.general": "Bendra", - "core.settings.language": "Kalba", - "core.settings.license": "Licencija", - "core.settings.localnotifavailable": "Prieinami vietos pranešimai", - "core.settings.locationhref": "URL peržiūra", - "core.settings.locked": "Užrakinta", - "core.settings.loggedin": "Prisijungęs", - "core.settings.loggedoff": "Neprisijungęs", - "core.settings.navigatorlanguage": "Navigatoriaus kalba", - "core.settings.navigatoruseragent": "Navigatorius userAgent", - "core.settings.networkstatus": "Interneto ryšio būsena", - "core.settings.preferences": "Nuostatos", - "core.settings.reportinbackground": "Automatiškai pranešti apie klaidas", - "core.settings.settings": "Parametrai", - "core.settings.sites": "Svetainės", - "core.settings.spaceusage": "Vietos naudojimas", - "core.settings.synchronization": "Sinchronizavimas", - "core.settings.synchronizenow": "Sinchronizuoti dabar", - "core.settings.syncsettings": "Sinchronizavimo nustatymai", - "core.settings.total": "Bendroji suma", - "core.settings.wificonnection": "WiFi ryšys", - "core.sharedfiles.chooseaccountstorefile": "Pasirinkite paskyrą, kurioje norite talpinti failą.", - "core.sharedfiles.chooseactionrepeatedfile": "Failas tokiu pavadinimu jau yra. Ar norite pakeisti esantį ar jį pervadinti \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Svetainės nėra saugomos. Prieš dalintis failu, pridėkite svetainės nuorodą.", - "core.sharedfiles.nosharedfiles": "Šioje svetainėje nepatalpinti bendro naudojimo failai.", - "core.sharedfiles.nosharedfilestoupload": "Nėra įkeltų failų. Jeigu norite juos įkelti iš kitos programėlės, pažymėkite ir paspauskite mygtuką „Atidaryti“.", - "core.sharedfiles.rename": "Pervadinti", - "core.sharedfiles.replace": "Pakeisti", - "core.sharedfiles.sharedfiles": "Bendro naudojimo failai", - "core.sharedfiles.successstorefile": "Failas sėkmingai patalpintas. Galite jį perkelti į savo asmeninį aplanką arba įkelti tam tikrosiose veiklose.", - "core.show": "Rodyti", - "core.showless": "Rodyti mažiau...", - "core.showmore": "Rodyti daugiau...", - "core.site": "Svetainė", - "core.sitehome.sitehome": "Pagrindinis svetainės puslapis", - "core.sitehome.sitenews": "Svetainės skelbimai", - "core.sitemaintenance": "Šiuo metu vykdoma svetainės priežiūra, todėl svetainė neprieinama", - "core.sizeb": "baitai", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Praleisti", - "core.sorry": "Atsiprašome...", - "core.sort": "Rūšiuoti", - "core.sortby": "Rūšiuoti pagal", - "core.start": "Pradėti", - "core.strftimedate": "%Y %B %d", - "core.strftimedatefullshort": "%y/%m/%d", - "core.strftimedateshort": "%B %d", - "core.strftimedatetime": "%Y %B %d, %H:%M", - "core.strftimedatetimeshort": "%y/%m/%d, %H:%M", - "core.strftimedaydate": "%A, %Y %B %d", - "core.strftimedaydatetime": "%A, %Y %B %d, %H:%M", - "core.strftimedayshort": "%A, %B %d", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%Y %B", - "core.strftimerecent": "%b %d, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%H:%M", - "core.strftimetime24": "%H:%M", - "core.submit": "Pateikti", - "core.success": "Sėkmingai", - "core.tablet": "Planšetė", - "core.tag.defautltagcoll": "Numatytas rinkinys", - "core.tag.inalltagcoll": "Visur", - "core.tag.itemstaggedwith": "{{$a.tagarea}} pažymėta su \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Rezultatų užklausai „{{$a}}“ nėra", - "core.tag.notagsfound": "Nerasta jokių žymių atitiktimi \"{{$a}}\"", - "core.tag.searchtags": "Ieškoti žymių", - "core.tag.showingfirsttags": "Rodomos {{$a}} populiariausios žymės", - "core.tag.tag": "Žymė", - "core.tag.tagarea_course": "Kursai", - "core.tag.tagarea_course_modules": "Veiklos ir ištekliai", - "core.tag.tagarea_post": "Tinklaraščio įrašai", - "core.tag.tagarea_user": "Naudotojo interesai", - "core.tag.tags": "Žymės", - "core.teachers": "Dėstytojai", - "core.thereisdatatosync": "Neprijungtas {{$a}}, kad sinchronizuotų.", - "core.thisdirection": "ltr", - "core.time": "Laikas", - "core.timesup": "Laikas baigėsi!", - "core.today": "Šiandien", - "core.tryagain": "Pabandykite dar kartą", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "Uh oh!", - "core.unexpectederror": "Klaida. Uždarykite programėlę ir bandykite atidaryti dar kartą", - "core.unknown": "Nežinomas", - "core.unlimited": "Neribota", - "core.unzipping": "Išskleidžiama", - "core.upgraderunning": "Naujinama svetainės versija, bandykite vėliau.", - "core.user": "Naudotojas", - "core.user.address": "Adresas", - "core.user.city": "Miestas / miestelis", - "core.user.contact": "Kontaktai", - "core.user.country": "Šalis", - "core.user.description": "Aprašas", - "core.user.details": "Išsami informacija", - "core.user.detailsnotavailable": "Šio vartotojo duomenys jums nepasiekiami.", - "core.user.editingteacher": "Dėstytojas", - "core.user.email": "El. pašto adresas", - "core.user.emailagain": "El. paštas (dar kartą)", - "core.user.firstname": "Vardas", - "core.user.interests": "Pomėgiai", - "core.user.lastname": "Pavardė", - "core.user.manager": "Tvarkytojas", - "core.user.newpicture": "Naujas paveikslėlis", - "core.user.noparticipants": "Nerasta šių kursų dalyvių", - "core.user.participants": "Dalyviai", - "core.user.phone1": "Telefonas", - "core.user.phone2": "Mobilusis telefonas", - "core.user.roles": "Vaidmenys", - "core.user.student": "Besimokantysis", - "core.user.teacher": "Dėstytojas be redagavimo teisės", - "core.user.webpage": "Tinklalapis", - "core.userdeleted": "Ši naudotojo paskyra buvo panaikinta", - "core.userdetails": "Naudotojo informacija", - "core.usernotfullysetup": "Naudotojas nėra visiškai nustatytas", - "core.users": "Naudotojai", - "core.view": "Peržiūrėti", - "core.viewprofile": "Peržiūrėti profilį", - "core.warningofflinedatadeleted": "{{component}} {{name}} neprijungti duomenys panaikinti. {{error}}", - "core.whatisyourage": "Kiek Jums metų?", - "core.wheredoyoulive": "Kurioje šalyje gyvenate?", - "core.whoops": "Oi!", - "core.whyisthishappening": "Kodėl tai nutiko?", - "core.whyisthisrequired": "Kodėl yra reikalinga?", - "core.wsfunctionnotavailable": "Interneto paslaugų funkcija nepasiekiama.", - "core.year": "metai", - "core.years": "metai", - "core.yes": "Taip" -} \ No newline at end of file diff --git a/src/assets/lang/lv.json b/src/assets/lang/lv.json deleted file mode 100644 index 4439e793d..000000000 --- a/src/assets/lang/lv.json +++ /dev/null @@ -1,1755 +0,0 @@ -{ - "addon.badges.badgedetails": "Žetona detaļas", - "addon.badges.badges": "Žetoni", - "addon.badges.contact": "Kontakts", - "addon.badges.dateawarded": "Izsniegšanas datums", - "addon.badges.expired": "Beidzies", - "addon.badges.expirydate": "Beigu datums", - "addon.badges.issuancedetails": "Žetona beigas", - "addon.badges.issuerdetails": "Piešķīrēja detaļas", - "addon.badges.issuername": "Piešķīrēja nosaukums", - "addon.badges.issuerurl": "Piešķirēja URL", - "addon.badges.nobadges": "Nav pieejamu žetonu.", - "addon.badges.recipientdetails": "Saņēmēju detaļas", - "addon.badges.warnexpired": "(Šim žetonam beidzies derīgums!)", - "addon.block_activitymodules.pluginname": "Aktivitātes", - "addon.block_activityresults.pluginname": "Aktivitāšu rezultāti", - "addon.block_badges.pluginname": "Jaunākie žetoni", - "addon.block_blogmenu.pluginname": "Blogu izvēlne", - "addon.block_blogrecent.pluginname": "Neseni bloga ieraksti", - "addon.block_blogtags.pluginname": "Blogu tagi", - "addon.block_calendarmonth.pluginname": "Kalendārs", - "addon.block_calendarupcoming.pluginname": "Gaidāmie notikumi", - "addon.block_comments.pluginname": "Komentāri", - "addon.block_completionstatus.pluginname": "Kursa izpildes statuss", - "addon.block_glossaryrandom.pluginname": "Gadījuma vārdnīcas šķirklis", - "addon.block_learningplans.pluginname": "Mācību plāni", - "addon.block_myoverview.allincludinghidden": "Visi", - "addon.block_myoverview.favourites": "Atzīmēti", - "addon.block_myoverview.future": "Nākotnē", - "addon.block_myoverview.hiddencourses": "Paslēpti", - "addon.block_myoverview.inprogress": "Uzsākti", - "addon.block_myoverview.lastaccessed": "Pēdējais apmeklējums", - "addon.block_myoverview.morecourses": "Vairāk kursu", - "addon.block_myoverview.nocourses": "Nav kursu", - "addon.block_myoverview.past": "Pagātnē", - "addon.block_myoverview.pluginname": "Kursu pārskats", - "addon.block_myoverview.title": "Kursa nosaukums", - "addon.block_newsitems.pluginname": "Jaunākās ziņas", - "addon.block_onlineusers.pluginname": "Tiešsaistes lietotāji", - "addon.block_privatefiles.pluginname": "Lietotāja privātie faili", - "addon.block_recentactivity.pluginname": "Pēdējās aktivitātes", - "addon.block_recentlyaccessedcourses.nocourses": "Nav nesenu kursu", - "addon.block_recentlyaccessedcourses.pluginname": "Nesen apmeklētie kursi", - "addon.block_rssclient.pluginname": "RSS klients", - "addon.block_selfcompletion.pluginname": "Pašizpilde", - "addon.block_sitemainmenu.pluginname": "Galvenā izvēlne", - "addon.block_starredcourses.pluginname": "Atzīmēts kurss", - "addon.block_tags.pluginname": "Tagi", - "addon.block_timeline.duedate": "Izpildes datums", - "addon.block_timeline.next30days": "Nākamās 30 dienas", - "addon.block_timeline.next3months": "Nākamie 3 mēneši", - "addon.block_timeline.next6months": "Nākamie 6 mēneši", - "addon.block_timeline.next7days": "Nākamās 7 dienas", - "addon.block_timeline.nocoursesinprogress": "Nav uzsāktu kursu", - "addon.block_timeline.noevents": "Nav gaidāmu aktivitāšu", - "addon.block_timeline.overdue": "Nokavēts", - "addon.block_timeline.pluginname": "Laika skala", - "addon.block_timeline.sortbycourses": "Kārtot pēc kursiem", - "addon.block_timeline.sortbydates": "Kārtot pēc datuma", - "addon.blog.blog": "Blogs", - "addon.blog.blogentries": "Bloga ieraksti", - "addon.blog.errorloadentries": "Ielādējot bloga ierakstus, radās kļūda.", - "addon.blog.linktooriginalentry": "Saite uz orģinālo bloga ierakstu", - "addon.blog.noentriesyet": "Šeit nav redzamu ierakstu", - "addon.blog.publishtonoone": "Sev (melnraksts)", - "addon.blog.publishtosite": "Jebkuram vietnes lietotājam", - "addon.blog.publishtoworld": "Visiem", - "addon.blog.showonlyyourentries": "Rādīt tikai savus ierakstus", - "addon.calendar.allday": "Visa diena", - "addon.calendar.calendar": "Kalendārs", - "addon.calendar.calendarevents": "Kalendāra notikumi", - "addon.calendar.calendarreminders": "Kalendāra atgādinājumi", - "addon.calendar.categoryevents": "Kategorijas notikumi", - "addon.calendar.confirmeventdelete": "Vai tiešām vēlaties izdzēst notikumu \"{{$a}}\"?", - "addon.calendar.courseevents": "Kursa notikumi", - "addon.calendar.daynext": "Nākamā diena", - "addon.calendar.dayprev": "Iepriekšējā diena", - "addon.calendar.defaultnotificationtime": "Noklusējuma atgādinājuma laiks", - "addon.calendar.deleteallevents": "Dzēst visus notikumus", - "addon.calendar.deleteevent": "Dzēst notikumu", - "addon.calendar.deleteoneevent": "Dzēst šo notikumu", - "addon.calendar.durationminutes": "Ilgums minūtēs", - "addon.calendar.durationnone": "Bez ilguma", - "addon.calendar.durationuntil": "Līdz", - "addon.calendar.editevent": "Notikuma rediģēšana", - "addon.calendar.errorloadevent": "Ielādējot notikumu, radās kļūda.", - "addon.calendar.errorloadevents": "Ielādējot notikumus, radās kļūda.", - "addon.calendar.eventcalendareventdeleted": "Kalendāra notikums izdzēsts", - "addon.calendar.eventduration": "Ilgums", - "addon.calendar.eventendtime": "Beigu laiks", - "addon.calendar.eventkind": "Notikuma tips", - "addon.calendar.eventname": "Notikuma nosaukums", - "addon.calendar.eventstarttime": "Sākuma laiks", - "addon.calendar.eventtype": "Notikuma tips", - "addon.calendar.fri": "Pk", - "addon.calendar.friday": "Piektdiena", - "addon.calendar.gotoactivity": "Atvērt aktivitāti", - "addon.calendar.groupevents": "Grupas notikumi", - "addon.calendar.invalidtimedurationminutes": "Jūsu ievadītais ilgums minūtēs ir nederīgs. Lūdzu, ievadiet ilgumu minūtēs lielāku par 0, vai arī izvēlieties bez ilguma.", - "addon.calendar.invalidtimedurationuntil": "Datums un laiks, kurus jūs norādījāt ilgumam Līdz, ir pirms notikuma sākuma laika. Lūdzu, koriģējet to pirms turpināt.", - "addon.calendar.mon": "Pr", - "addon.calendar.monday": "Pirmdiena", - "addon.calendar.monthlyview": "Mēneša skats", - "addon.calendar.newevent": "Jauns notikums", - "addon.calendar.noevents": "Nav notikumu", - "addon.calendar.nopermissiontoupdatecalendar": "Atvainojiet, šobrīd jums nav pilnvaru atjaunināt kalendāra notikumu", - "addon.calendar.reminders": "Atgādinājumi", - "addon.calendar.repeatedevents": "Atkārtoti notikumi", - "addon.calendar.repeateditall": "Lietot izmaiņas visiem {{$a}} šīs atkārtojumu sērijas notikumiem", - "addon.calendar.repeateditthis": "Lietot izmaiņas tikai šim notikumam", - "addon.calendar.repeatevent": "Atkārtot šo notikumu", - "addon.calendar.repeatweeksl": "Atkārtot katru nedēļu, izveidojot kopumā", - "addon.calendar.sat": "Se", - "addon.calendar.saturday": "Sestdiena", - "addon.calendar.setnewreminder": "Izveidot jaunu atgādinājumu", - "addon.calendar.siteevents": "Vietnes notikumi", - "addon.calendar.sun": "Sv", - "addon.calendar.sunday": "Svētdiena", - "addon.calendar.thu": "Ce", - "addon.calendar.thursday": "Ceturtdiena", - "addon.calendar.today": "Šodien", - "addon.calendar.tomorrow": "Rīt", - "addon.calendar.tue": "Ot", - "addon.calendar.tuesday": "Otrdiena", - "addon.calendar.typecategory": "Kategorijas notikums", - "addon.calendar.typeclose": "Slēgt notikumu", - "addon.calendar.typecourse": "Kursa notikums", - "addon.calendar.typegroup": "Grupas notikums", - "addon.calendar.typeopen": "Atvērt notikumu", - "addon.calendar.typesite": "Vietnes notikums", - "addon.calendar.typeuser": "Lietotāja notikums", - "addon.calendar.upcomingevents": "Gaidāmie notikumi", - "addon.calendar.userevents": "Lietotāja notikumi", - "addon.calendar.wed": "Tr", - "addon.calendar.wednesday": "Trešdiena", - "addon.calendar.when": "Kad", - "addon.calendar.yesterday": "Vakar", - "addon.competency.activities": "Aktivitātes", - "addon.competency.competencies": "Kompetences", - "addon.competency.competenciesmostoftennotproficientincourse": "Šajā kursā visbiežāk nesasniegtās kompetences", - "addon.competency.coursecompetencies": "Kursa kompetences", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Kompetenču vērtējumi šajā kursā neietekmē mācību plānus.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Kompetenču vērtējumi šajā kursā tiek nekavējoties atjaunināti mācību plānos.", - "addon.competency.crossreferencedcompetencies": "Mijsaistītas komptences", - "addon.competency.duedate": "Izpildes datums", - "addon.competency.errornocompetenciesfound": "Kompetences nav atrastas", - "addon.competency.evidence": "Pierādījums", - "addon.competency.evidence_competencyrule": "Kompetenču kārtulas prasības ir izpildītas.", - "addon.competency.evidence_coursecompleted": "Kurss '{{$a}}' ir pabeigts.", - "addon.competency.evidence_coursemodulecompleted": "Aktivitāte '{{$a}}' ir pabeigta.", - "addon.competency.evidence_manualoverride": "Kompetence novērtēta manuāli.", - "addon.competency.learningplancompetencies": "Mācību plāna kompetences", - "addon.competency.learningplans": "Mācību plāni", - "addon.competency.myplans": "Mani mācību plāni", - "addon.competency.noactivities": "Nav aktivitāšu", - "addon.competency.nocompetencies": "Nav kompetenču", - "addon.competency.nocompetenciesincourse": "Šim kursam nav piesaistīta neviena kompetence.", - "addon.competency.nocrossreferencedcompetencies": "Šai kompetencei nav mijsaistītu kompetenču", - "addon.competency.noevidence": "Nav pierādījumu", - "addon.competency.noplanswerecreated": "Nav izveidots neviens mācību plāns.", - "addon.competency.path": "Ceļš:", - "addon.competency.planstatusactive": "Aktīvs", - "addon.competency.planstatuscomplete": "Jāpabeidz", - "addon.competency.planstatusdraft": "Melnraksts", - "addon.competency.planstatusinreview": "Tiek pārskatīta", - "addon.competency.planstatuswaitingforreview": "Gaida pārskatīšanu", - "addon.competency.proficient": "Lietpratīgs", - "addon.competency.progress": "Progress", - "addon.competency.rating": "Vērtējums", - "addon.competency.reviewstatus": "Pārskatīšanas statuss", - "addon.competency.status": "Statuss", - "addon.competency.template": "Mācību plāna veidne", - "addon.competency.uponcoursecompletion": "Pēc kursa izpildes:", - "addon.competency.usercompetencystatus_idle": "Neaktīvs", - "addon.competency.usercompetencystatus_inreview": "Tiek pārskatīta", - "addon.competency.usercompetencystatus_waitingforreview": "Gaida pārskatīšanu", - "addon.competency.userplans": "Mācību plāni", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} no {{$a.y}} kompetences ir sasniegtas", - "addon.competency.xcompetenciesproficientoutofyincourse": "Jūs sasniedzāt {{$a.x}} no {{$a.y}} kompetencēm šajā kursā.", - "addon.coursecompletion.complete": "Pabeigts", - "addon.coursecompletion.completecourse": "Pabeigt kursu", - "addon.coursecompletion.completed": "Pabeigts", - "addon.coursecompletion.completiondate": "Izpildes datums", - "addon.coursecompletion.completionmenuitem": "Izpilde", - "addon.coursecompletion.couldnotloadreport": "Nevarēja ielādēt kursa pabeigšanas atskaiti. Lūdzu, pamēģiniet vēlreiz vēlāk.", - "addon.coursecompletion.coursecompletion": "Kursa izpilde", - "addon.coursecompletion.criteria": "Kritērijs", - "addon.coursecompletion.criteriagroup": "Kritēriju grupa", - "addon.coursecompletion.criteriarequiredall": "Visi šie nosacījumi ir nepieciešami", - "addon.coursecompletion.criteriarequiredany": "Jebkurš no šiem nosacījumiem ir nepieciešams", - "addon.coursecompletion.inprogress": "Progresā", - "addon.coursecompletion.manualselfcompletion": "Manuālā pašizpilde", - "addon.coursecompletion.nottracked": "Šobrīd šajā kursā netiek veikta jūsu aktivitāšu izpildes izsekošana", - "addon.coursecompletion.notyetstarted": "Nav uzsākts", - "addon.coursecompletion.required": "Nepieciešams", - "addon.coursecompletion.requiredcriteria": "Nepieciešamais kritērijs", - "addon.coursecompletion.requirement": "Prasība", - "addon.coursecompletion.status": "Statuss", - "addon.coursecompletion.viewcoursereport": "Skatīt kursa atskaiti", - "addon.files.couldnotloadfiles": "Failu sarakstu nevar ielādēt.", - "addon.files.emptyfilelist": "Nav failu, ko parādīt.", - "addon.files.erroruploadnotworking": "Diemžēl jūs vietnē šobrīd nav iespējams augšupielādēt failus.", - "addon.files.files": "Faili", - "addon.files.privatefiles": "Privātie faili", - "addon.files.sitefiles": "Vietnes faili", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Konfigurēt ierīces", - "addon.messages.acceptandaddcontact": "Pieņemt un pievienot kontaktiem", - "addon.messages.addcontact": "Pievienot adresātu", - "addon.messages.addcontactconfirm": "Vai esat pārliecināts, ka vēlaties pievienot {{$a}} saviem kontaktiem?", - "addon.messages.addtoyourcontacts": "Pievienot jūsu kontaktiem", - "addon.messages.blocknoncontacts": "Bloķēt visas jaunās ziņas, kas tiek saņemtas no personām, kuras nav iekļautas manā adresātu sarakstā", - "addon.messages.blockuser": "Bloķēt lietotāju", - "addon.messages.blockuserconfirm": "Vai esat pārliecināts, ka vēlaties bloķēt {{$a}}?", - "addon.messages.contactableprivacy_coursemember": "Mani kontakti un ikviens manos kursos", - "addon.messages.contactableprivacy_onlycontacts": "Tikai mani kontakti", - "addon.messages.contactlistempty": "Kontaktu saraksts ir tukšs", - "addon.messages.contactname": "Kontakta nosaukums", - "addon.messages.contactrequestsent": "Kontaktu pieprasījums nosūtīts", - "addon.messages.contacts": "Adresāti", - "addon.messages.decline": "Noraidīt", - "addon.messages.deleteconversation": "Izdzēst sarunu", - "addon.messages.deletemessage": "Izdzēst ziņu", - "addon.messages.deletemessageconfirmation": "Vai tiešām vēlaties dzēst šo ziņu? Tā tiks dzēsta tikai no ziņojumu vēstures un to varēs redzēt lietotājs, kurš sūtīja vai saņēma šo ziņu.", - "addon.messages.errordeletemessage": "Dzēšot ziņu, radās kļūda.", - "addon.messages.errorwhileretrievingcontacts": "Saņemot kontaktus no servera, radās kļūda.", - "addon.messages.errorwhileretrievingdiscussions": "Saņemot diskusijas no servera, radās kļūda.", - "addon.messages.errorwhileretrievingmessages": "Saņemot ziņas no servera, radās kļūda.", - "addon.messages.errorwhileretrievingusers": "Saņemot lietotājus no servera, radās kļūda.", - "addon.messages.groupconversations": "Grupa", - "addon.messages.groupinfo": "Grupas informācija", - "addon.messages.individualconversations": "Privātas", - "addon.messages.info": "Informācija", - "addon.messages.message": "Ziņa", - "addon.messages.messagenotsent": "Ziņa netika nosūtīta. Lūdzu mēģiniet vēlreiz vēlāk.", - "addon.messages.messagepreferences": "Ziņojumu iestatījumi", - "addon.messages.messages": "Ziņas", - "addon.messages.newmessage": "Jauns ziņojums", - "addon.messages.newmessages": "Jaunas ziņas", - "addon.messages.nocontactrequests": "Nav kontaktu pieprasījumu", - "addon.messages.nocontactsgetstarted": "Nav kontaktu", - "addon.messages.nofavourites": "Nav atzīmētu sarunu", - "addon.messages.nogroupconversations": "Nav grupu sarunu", - "addon.messages.noindividualconversations": "Nav privātu sarunu", - "addon.messages.nomessagesfound": "Nav atrasta neviena ziņa.", - "addon.messages.noncontacts": "Nav kontaktos", - "addon.messages.nousersfound": "Lietotāji nav atrasti", - "addon.messages.removecontact": "Dzēst adresātu", - "addon.messages.removecontactconfirm": "Vai esat pārliecināts, ka vēlaties izņemt {{$a}} no saviem kontaktiem?", - "addon.messages.removefromyourcontacts": "Noņemt no kontaktiem", - "addon.messages.requests": "Pieprasījumi", - "addon.messages.searchcombined": "Meklēt personas un ziņas", - "addon.messages.showdeletemessages": "Parādīt dzēstās ziņas", - "addon.messages.type_blocked": "Bloķēts", - "addon.messages.type_offline": "Bezsaistē", - "addon.messages.type_online": "Tiešsaistē", - "addon.messages.type_search": "Meklēšanas rezultāti", - "addon.messages.type_strangers": "Citi", - "addon.messages.unblockuserconfirm": "Vai esat pārliecināts, ka vēlaties atbloķēt {{$a}}?", - "addon.messages.useentertosend": "Nosūtīt ar Enter", - "addon.messages.useentertosenddescdesktop": "Ja atspējots, jūs varat izmantot Ctrl+Enter, lai nosūtītu ziņu.", - "addon.messages.useentertosenddescmac": "Ja atspējots, jūs varat izmantot Ctmd+Enter, lai nosūtītu ziņu.", - "addon.messages.userwouldliketocontactyou": "{{$a}} vēlas ar jums sazināties", - "addon.messages.warningconversationmessagenotsent": "Nevar nosūtīt ziņu (-as) sarunai {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Nevar nosūtīt ziņu (-as) lietotājam{{user}}. {{error}}", - "addon.messages.you": "Jūs:", - "addon.messages.yourcontactrequestpending": "Jūsu kontaktu pieprasījums gaida apstiprinājumu no {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Lūdzu apstipriniet iesnieguma apgalvojumu.", - "addon.mod_assign.addattempt": "Atļaut citu mēģinājumu", - "addon.mod_assign.addnewattempt": "Pievienot jaunu mēģinājumu", - "addon.mod_assign.addnewattemptfromprevious": "Pievienot jaunu mēģinājumu, balstoties uz iepriekšējo iesniegumu", - "addon.mod_assign.addsubmission": "Pievienot iesniegumu", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Uzdevuma detaļas un iesniegšanas forma būs pieejama no {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Atļaut iesniegumus no", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Šis uzdevums pieņems iesniegumus no {{$a}}", - "addon.mod_assign.applytoteam": "Piemērot vērtējumus un atsauksmes visai grupai", - "addon.mod_assign.assignmentisdue": "Uzdevuma termiņš", - "addon.mod_assign.attemptreopenmethod": "Atkārtoti mēģinājumi", - "addon.mod_assign.attemptreopenmethod_manual": "Manuāli", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automātiski līdz izpildīts", - "addon.mod_assign.cannoteditduetostatementsubmission": "Nevar pievienot vai rediģēt iesniegumu lietotnē, jo no vietnes nevarēja iegūt iesniegšanas paziņojumu.", - "addon.mod_assign.cannotgradefromapp": "Lietotne vēl neatbalsta noteiktas vērtēšanas metodes, un tās nevar mainīt.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Jūs nevarat iesniegt iesniegumu lietotnē, jo no vietnes nevarēja iegūt iesniegšanas paziņojumu.", - "addon.mod_assign.confirmsubmission": "Vai esat pārliecināts, ka gribat nosūtīt savu darbu vērtēšanai? Pēc tam jūs vairs nevarēsiet veikt nekādas izmaiņas.", - "addon.mod_assign.currentgrade": "Pašreizējais vērtējums vērtējumu grāmatā", - "addon.mod_assign.cutoffdate": "Pārtraukšanas datums", - "addon.mod_assign.defaultteam": "Grupa pēc noklusējuma", - "addon.mod_assign.duedate": "Izpildes termiņš", - "addon.mod_assign.duedateno": "Bez izpildes termiņa", - "addon.mod_assign.duedatereached": "Šī uzdevuma izpildes datums ir pagājis.", - "addon.mod_assign.editingstatus": "Statusa labošana", - "addon.mod_assign.editsubmission": "Rediģēt iesniegumu", - "addon.mod_assign.erroreditpluginsnotsupported": "Lietotnē nevar pievienot vai rediģēt iesniegumu, jo daži spraudņi vēl neatbalsta rediģēšanu.", - "addon.mod_assign.errorshowinginformation": "Nevar parādīt iesnieguma informāciju.", - "addon.mod_assign.extensionduedate": "Pagarinājuma beigu datums", - "addon.mod_assign.feedbacknotsupported": "Šī lietotne neatbalsta šo atgriezenisko saiti, un tajā var nebūt visa informācija.", - "addon.mod_assign.grade": "Vērtējums", - "addon.mod_assign.graded": "Novērtēts", - "addon.mod_assign.gradedby": "Vērtētājs", - "addon.mod_assign.gradedon": "Vērtēts (kad?)", - "addon.mod_assign.gradenotsynced": "Vērtējums nav sinhronizēts", - "addon.mod_assign.gradeoutof": "Vērtējums no maksimālā {{$a}}", - "addon.mod_assign.gradingstatus": "Vērtēšanas status", - "addon.mod_assign.groupsubmissionsettings": "Grupu iesniegumu iestatījumi", - "addon.mod_assign.hiddenuser": "Dalībnieks", - "addon.mod_assign.latesubmissions": "Novēlotie iesniegumi", - "addon.mod_assign.latesubmissionsaccepted": "Tikai students/-i, kuriem ir piešķirts pagarinājums, var pievienot iesniegumus", - "addon.mod_assign.markingworkflowstate": "Vērtēšanas darbplūsmas statuss", - "addon.mod_assign.markingworkflowstatereadyforreview": "Vērtēšana pabeigta", - "addon.mod_assign.modulenameplural": "Uzdevumi", - "addon.mod_assign.noattempt": "Nav uzsākts", - "addon.mod_assign.nomoresubmissionsaccepted": "Iesniegumi vairs netiek pieņemti", - "addon.mod_assign.noonlinesubmissions": "Šim uzdevumam nav nepieciešams neko iesniegt tiešsaistē", - "addon.mod_assign.nosubmission": "Šim uzdevumam nav pievienots neviens iesniegums", - "addon.mod_assign.notallparticipantsareshown": "Netiek rādīti dalībnieki bez iesniegumiem", - "addon.mod_assign.notgraded": "Nav novērtēts", - "addon.mod_assign.numberofdraftsubmissions": "Melnraksti", - "addon.mod_assign.numberofparticipants": "Dalībnieki", - "addon.mod_assign.numberofsubmissionsneedgrading": "Nepieciešams novērtējums", - "addon.mod_assign.numberofsubmittedassignments": "Iesniegts", - "addon.mod_assign.numberofteams": "Grupas", - "addon.mod_assign.numwords": "{{$a}} vārdi", - "addon.mod_assign.overdue": "Uzdevuma termiņš ir pārsniegts par: {{$a}}", - "addon.mod_assign.submission": "Iesniegtais materiāls", - "addon.mod_assign.submissioneditable": "Students var rediģēt šo iesniegumu", - "addon.mod_assign.submissionnoteditable": "Students nevar rediģēt šo iesniegumu", - "addon.mod_assign.submissionnotsupported": "Lietotne neatbalsta šo iesniegumu, un tas var nesaturēt visu informāciju.", - "addon.mod_assign.submissionslocked": "Šim uzdevumam nav pieejama iesniegšana.", - "addon.mod_assign.submissionstatus": "Iesnieguma statuss", - "addon.mod_assign.submissionstatus_": "Nav iesniegumu", - "addon.mod_assign.submissionstatus_draft": "Melnraksts (nav iesniegts)", - "addon.mod_assign.submissionstatus_marked": "Novērtēts", - "addon.mod_assign.submissionstatus_new": "Jauns iesniegums", - "addon.mod_assign.submissionstatus_submitted": "Iesniegts vērtēšanai", - "addon.mod_assign.submissionstatusheading": "Iesnieguma statuss", - "addon.mod_assign.submissionteam": "Grupa", - "addon.mod_assign.submitassignment": "Iesniegt uzdevumu", - "addon.mod_assign.submitassignment_help": "Līdzko šis uzedvums tiks iesniegts, jūs vairs nevarēsiet veikt izmaiņas,", - "addon.mod_assign.submittedearly": "Uzdevums tika iesniegts {{$a}} par agru", - "addon.mod_assign.submittedlate": "Uzdevums tika iesniegts {{$a}} par vēlu", - "addon.mod_assign.timemodified": "Pēdējās izmaiņas", - "addon.mod_assign.timeremaining": "Atlikušais laiks", - "addon.mod_assign.unlimitedattempts": "Neierobežots", - "addon.mod_assign.userswhoneedtosubmit": "Lietotāji, kuriem ir jāveic iesniegšana: {{$a}}", - "addon.mod_assign.userwithid": "Lietotājs ar ID {{id}}", - "addon.mod_assign.viewsubmission": "Apskatīt iesniegumus", - "addon.mod_assign.warningsubmissiongrademodified": "Iesnieguma vērtējums tika izmainīts vietnē.", - "addon.mod_assign.warningsubmissionmodified": "Lietotāja iesniegums tika izmainīts vietnē.", - "addon.mod_assign.wordlimit": "Vārdu skaita ierobežojums", - "addon.mod_assign_feedback_comments.pluginname": "Atsauksmju komentāri", - "addon.mod_assign_feedback_editpdf.pluginname": "Anotēt PDF", - "addon.mod_assign_feedback_file.pluginname": "Faila atsauksme", - "addon.mod_assign_submission_comments.pluginname": "Iesnieguma komentāri", - "addon.mod_assign_submission_file.pluginname": "Failu iesniegšana", - "addon.mod_assign_submission_onlinetext.pluginname": "Tiešsaistes teksta iesniegumi", - "addon.mod_book.errorchapter": "Kļūda, nolasot grāmatas nodaļu.", - "addon.mod_book.modulenameplural": "Grāmatas", - "addon.mod_book.navnexttitle": "Nākamā nodaļa: {{$a}}", - "addon.mod_book.navprevtitle": "Iepriekšējā nodaļa: {{$a}}", - "addon.mod_book.toc": "Satura rādītājs", - "addon.mod_chat.beep": "Signāls", - "addon.mod_chat.chatreport": "Tērzēšanas sesijas", - "addon.mod_chat.currentusers": "Pašreizējie lietotāji", - "addon.mod_chat.enterchat": "Noklikšķiniet šeit, lai nekavējoties pievienotos tērzēšanai", - "addon.mod_chat.entermessage": "Ievadiet jūsu ziņu", - "addon.mod_chat.errorwhileconnecting": "Veidojot savienojumu ar tērzētavu, radās kļūda.", - "addon.mod_chat.errorwhilegettingchatdata": "Saņemot tērzēšanas datus, radās kļūda.", - "addon.mod_chat.errorwhilegettingchatusers": "Saņemot tērzēšanas lietotājus, radās kļūda.", - "addon.mod_chat.errorwhileretrievingmessages": "Saņemot ziņas no servera, radās kļūda.", - "addon.mod_chat.errorwhilesendingmessage": "Sūtot ziņu, radās kļūda.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} signalizē ikvienam!", - "addon.mod_chat.messagebeepsyou": "{{$a}} tikko jums signalizēja!", - "addon.mod_chat.messageenter": "{{$a}} tikko pievienojās šai tērzēšanai", - "addon.mod_chat.messageexit": "{{$a}} tikko pameta šo tērzēšanu", - "addon.mod_chat.messages": "Ziņojumi", - "addon.mod_chat.messageyoubeep": "Jūs signalizējāt {{$a}}", - "addon.mod_chat.modulenameplural": "Tērzēšanas", - "addon.mod_chat.mustbeonlinetosendmessages": "Lai sūtītu ziņas, jums jābūt tiešsaistē.", - "addon.mod_chat.nomessages": "Vēl nav ziņojumu", - "addon.mod_chat.nosessionsfound": "Sesijas nav atrastas", - "addon.mod_chat.saidto": "teikts", - "addon.mod_chat.send": "Sūtīt", - "addon.mod_chat.sessionstart": "Tērzēšanas sesija sāksies: {{$a}}", - "addon.mod_chat.showincompletesessions": "Parādīt nepilnās sesijas", - "addon.mod_chat.talk": "Runāt", - "addon.mod_chat.viewreport": "Skatīt iepriekšējās tērzēšanas sesijas", - "addon.mod_choice.cannotsubmit": "Atvainojiet, neizdevās iesniegt jūsu izvēli. Lūdzu, mēģiniet vēlreiz.", - "addon.mod_choice.choiceoptions": "Izvēles opcijas", - "addon.mod_choice.errorgetchoice": "Saņemot izvēles datus, radās kļūda.", - "addon.mod_choice.expired": "Atvainojiet, šī aktivitāte ir slēgta {{$a}} un vairs nav pieejama", - "addon.mod_choice.full": "(Pilns)", - "addon.mod_choice.modulenameplural": "Izvēles", - "addon.mod_choice.noresultsviewable": "Rezultāti šobrīd nav skatāmi.", - "addon.mod_choice.notopenyet": "Atvainojiet, šī aktivitāte nav pieejama līdz {{$a}}", - "addon.mod_choice.numberofuser": "Atbilžu skaits", - "addon.mod_choice.numberofuserinpercentage": "Atbilžu skaits procentos", - "addon.mod_choice.removemychoice": "Noņemt manu izvēli", - "addon.mod_choice.responses": "Atbildes", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% lietotāju izvēlējās izvēli: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Grafika attēlošana", - "addon.mod_choice.resultsnotsynced": "Jūsu pēdējo atbildi nepieciešams sinhronizēt, pirms tā tiks iekļauta rezultātos.", - "addon.mod_choice.savemychoice": "Saglabāt manu izvēli", - "addon.mod_choice.userchoosethisoption": "Lietotāji, kuri izvēlējās šo opciju", - "addon.mod_choice.yourselection": "Jūsu izvēle", - "addon.mod_data.addentries": "Pievienot ierakstus", - "addon.mod_data.advancedsearch": "Detalizētā meklēšana", - "addon.mod_data.alttext": "Alternatīvais teksts", - "addon.mod_data.approve": "Apstiprināt", - "addon.mod_data.approved": "Apstiprinats", - "addon.mod_data.ascending": "Augošā secībā", - "addon.mod_data.authorfirstname": "Autora vārds", - "addon.mod_data.authorlastname": "Autora uzvārds", - "addon.mod_data.confirmdeleterecord": "Vai tiešām vēlaties izdzēst šo ierakstu?", - "addon.mod_data.descending": "Dilstošā secībā", - "addon.mod_data.emptyaddform": "Jūs neaizpildījāt nevienu lauku!", - "addon.mod_data.entrieslefttoadd": "Pirms apskatīt pārējo dalībnieku ierakstus, jums vēl jāpievieno {{$a.entriesleft}} ieraksts/ieraksti.", - "addon.mod_data.entrieslefttoaddtoview": "Jums ir jāpievieno {{$a.entrieslefttoview}} jaunus ierakstus, pirms jūs varēsiet apskatīt citu dalībnieku ierakstus.", - "addon.mod_data.errorapproving": "Apstiprinot vai neapstiprinot ierakstu, radās kļūda.", - "addon.mod_data.errordeleting": "Dzēšot ierakstu, radās kļūda.", - "addon.mod_data.expired": "Atvainojiet, šī aktivitāte tika slēgta {{$a}} un vairs nav pieejama.", - "addon.mod_data.fields": "Lauki", - "addon.mod_data.foundrecords": "Atrasti ieraksti: {{$a.num}}/{{$a.max}} (Atiestatīt filtrus)", - "addon.mod_data.menuchoose": "Izvēlēties...", - "addon.mod_data.modulenameplural": "Datubāzes", - "addon.mod_data.more": "Vēl", - "addon.mod_data.nomatch": "Atbilstoši ieraksti nav atrasti!", - "addon.mod_data.norecords": "Datubāzē nav ierakstu", - "addon.mod_data.notapproved": "Ieraksts vēl nav apstiprināts.", - "addon.mod_data.notopenyet": "Atvainojiet, šī aktivitāte nav pieejama līdz {{$a}}.", - "addon.mod_data.numrecords": "{{$a}} ieraksti", - "addon.mod_data.other": "Citi", - "addon.mod_data.recordapproved": "Ieraksts apstiprināts", - "addon.mod_data.recorddeleted": "Ieraksts izdzēsts", - "addon.mod_data.resetsettings": "Atiestatīt filtrus", - "addon.mod_data.search": "Meklēt", - "addon.mod_data.selectedrequired": "Visi izvēlētie nepieciešami", - "addon.mod_data.single": "Skatīt vienu", - "addon.mod_data.timeadded": "Pievienošanas laiks", - "addon.mod_data.timemodified": "Modificēšanas laiks", - "addon.mod_data.usedate": "Iekļaut meklēšanā.", - "addon.mod_feedback.analysis": "Analīze", - "addon.mod_feedback.anonymous": "Anonīms", - "addon.mod_feedback.anonymous_entries": "Anonīmi ieraksti ({{$a}})", - "addon.mod_feedback.average": "Vidējais", - "addon.mod_feedback.captchaofflinewarning": "Atsauksmes ar CAPTCHA nevar pabeigt bezsaistē, vai, ja nav konfigurētas, vai arī, ja serveris nedarbojas.", - "addon.mod_feedback.complete_the_form": "Atbildēt uz jautājumiem...", - "addon.mod_feedback.completed_feedbacks": "Iesniegtās atbildes", - "addon.mod_feedback.continue_the_form": "Turpināt veidlapu", - "addon.mod_feedback.feedback_is_not_open": "Atsauksme nav atvērta", - "addon.mod_feedback.feedback_submitted_offline": "Šī atsauksme ir saglabāta, lai to iesniegtu vēlāk.", - "addon.mod_feedback.feedbackclose": "Atļaut atbildes līdz", - "addon.mod_feedback.feedbackopen": "Atļaut atbildes no", - "addon.mod_feedback.mapcourses": "Kartēt atsauksmes uz kursiem", - "addon.mod_feedback.maximal": "maksimālais", - "addon.mod_feedback.minimal": "minimālais", - "addon.mod_feedback.mode": "Režīms", - "addon.mod_feedback.modulenameplural": "Atsauksme", - "addon.mod_feedback.next_page": "Nākamā lapa", - "addon.mod_feedback.non_anonymous": "Lietotāja vārds tiks ierakstīts žurnālā un tiks rādīts pie atbildēm", - "addon.mod_feedback.non_anonymous_entries": "Ne-anonīmi ieraksti ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Studenti, kuri nav atbildējuši ({{$a}})", - "addon.mod_feedback.not_selected": "Nav izvēlēts", - "addon.mod_feedback.not_started": "Nav sākts", - "addon.mod_feedback.numberoutofrange": "Skaitlis ārpus atļautā apgabala", - "addon.mod_feedback.overview": "Pārskats", - "addon.mod_feedback.page_after_submit": "Lapa pēc iesniegšanas", - "addon.mod_feedback.preview": "Priekšskatījums", - "addon.mod_feedback.previous_page": "Iepriekšējā lapa", - "addon.mod_feedback.questions": "Jautājumi", - "addon.mod_feedback.response_nr": "Atbildes numurs", - "addon.mod_feedback.responses": "Atbildes", - "addon.mod_feedback.save_entries": "Iesniegt jūsu atbildes", - "addon.mod_feedback.show_entries": "Parādīt atbildes", - "addon.mod_feedback.show_nonrespondents": "Parādīt lietotājus, kuri nav atbildējuši", - "addon.mod_feedback.started": "Uzsākts", - "addon.mod_feedback.this_feedback_is_already_submitted": "Jūs jau esat pabeidzis šo aktivitāti.", - "addon.mod_folder.emptyfilelist": "Nav failu, ko parādīt.", - "addon.mod_folder.modulenameplural": "Mapes", - "addon.mod_forum.addanewdiscussion": "Pievienot jaunu diskusijas tēmu", - "addon.mod_forum.addanewquestion": "Pievienot jaunu jautājumu", - "addon.mod_forum.addanewtopic": "Pievienot jaunu tēmu", - "addon.mod_forum.addtofavourites": "Atzīmēt šo diskusiju", - "addon.mod_forum.advanced": "Papildiespējas", - "addon.mod_forum.cannotadddiscussion": "Lai šim forumam pievienotu diskusijas, jābūt iekļautam grupā.", - "addon.mod_forum.cannotadddiscussionall": "Jums nav pilnvaru pievienot jaunu diskusiju tēmu visiem dalībniekiem.", - "addon.mod_forum.cannotcreatediscussion": "Neizdevās izveidot jaunu diskusiju", - "addon.mod_forum.couldnotadd": "Nezināmas kļūdas dēļ jūsu ziņu nevarēja pievienot.", - "addon.mod_forum.couldnotupdate": "Nezināmas kļūdas dēļ jūsu publicēto ziņu nevarēja atjaunināt.", - "addon.mod_forum.delete": "Dzēst", - "addon.mod_forum.deletedpost": "Publicētā ziņa tika izdzēsta", - "addon.mod_forum.deletesure": "Vai tiešām vēlaties izdzēst šo publicēto ziņu?", - "addon.mod_forum.discussion": "Diskusija", - "addon.mod_forum.discussionlocked": "Šī diskusija ir slēgta, tāpēc jūs uz to vairs nevarat atbildēt.", - "addon.mod_forum.discussionpinned": "Piesprausta", - "addon.mod_forum.discussionsubscription": "Diskusiju abonēšana", - "addon.mod_forum.edit": "Rediģēt", - "addon.mod_forum.erroremptymessage": "Ieraksta ziņojums nevar būt tukšs", - "addon.mod_forum.erroremptysubject": "Ieraksta tēma nevar būt tukša", - "addon.mod_forum.errorgetforum": "Saņemot foruma datus, radās kļūda.", - "addon.mod_forum.errorgetgroups": "Saņemot grupas iestatījumus, radās kļūda.", - "addon.mod_forum.errorposttoallgroups": "Nevar izveidot jaunu diskusiju visās grupās.", - "addon.mod_forum.forumnodiscussionsyet": "Šajā forumā vēl nav diskusiju.", - "addon.mod_forum.group": "Grupa", - "addon.mod_forum.lastpost": "Pēdējā publicētā ziņa", - "addon.mod_forum.message": "Ziņojums", - "addon.mod_forum.modeflatnewestfirst": "Rādīt atbildes plakani, jaunāko vispirms", - "addon.mod_forum.modeflatoldestfirst": "Rādīt atbildes plakani, vecāko vispirms", - "addon.mod_forum.modenested": "Rādīt atbildes ligzdu veidā", - "addon.mod_forum.modulenameplural": "Forumi", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} diskusijas", - "addon.mod_forum.numreplies": "{{numreplies}} atbildes", - "addon.mod_forum.posttoforum": "Publicēt forumā", - "addon.mod_forum.posttomygroups": "Publicēt kopiju visām grupām", - "addon.mod_forum.re": "Atbilde:", - "addon.mod_forum.refreshdiscussions": "Atsvaidzināt diskusijas", - "addon.mod_forum.refreshposts": "Atsvaidzināt ziņas", - "addon.mod_forum.removefromfavourites": "Noņemt atzīmējumu šai diskusijai", - "addon.mod_forum.reply": "Atbildēt", - "addon.mod_forum.subject": "Tēma", - "addon.mod_forum.tagarea_forum_posts": "Foruma ziņas", - "addon.mod_forum.unread": "Nelasītās", - "addon.mod_forum.unreadpostsnumber": "{{$a}} nelasītas publicētās ziņas", - "addon.mod_forum.yourreply": "Jūsu atbilde", - "addon.mod_glossary.addentry": "Pievienot jaunu šķirkli", - "addon.mod_glossary.aliases": "Atslēgvārds(-i)", - "addon.mod_glossary.attachment": "Pielikums", - "addon.mod_glossary.browsemode": "Pārlūkot ierakstus", - "addon.mod_glossary.byalphabet": "Alfabētiski", - "addon.mod_glossary.byauthor": "Grupēt pēc autora", - "addon.mod_glossary.bycategory": "Grupēt pēc kategorijas", - "addon.mod_glossary.bynewestfirst": "Jaunākie vispirms", - "addon.mod_glossary.byrecentlyupdated": "Nesen atjaunināti", - "addon.mod_glossary.bysearch": "Meklēt", - "addon.mod_glossary.cannoteditentry": "Nevar rediģēt ierakstu", - "addon.mod_glossary.casesensitive": "Šis šķirklis ir reģistrjutīgs", - "addon.mod_glossary.categories": "Kategorijas", - "addon.mod_glossary.concept": "Jēdziens", - "addon.mod_glossary.definition": "Definīcija", - "addon.mod_glossary.entriestobesynced": "Sinhronizējamie ieraksti", - "addon.mod_glossary.entrypendingapproval": "Šis ieraksts gaida apstiprinājumu.", - "addon.mod_glossary.entryusedynalink": "Šis šķirklis ir jāsaista automātiski", - "addon.mod_glossary.errconceptalreadyexists": "Šis jēdziens jau pastāv. Šajā vārdnīcā nav atļauts veidot dublikātus.", - "addon.mod_glossary.errorloadingentries": "Ielādējot ierakstus, radās kļūda.", - "addon.mod_glossary.errorloadingentry": "Ielādējot ierakstu, radās kļūda.", - "addon.mod_glossary.errorloadingglossary": "Ielādējot vārdnīcu, radās kļūda.", - "addon.mod_glossary.fillfields": "Jēdziens un definīcija attiecīgajos laukos jāievada obligāti.", - "addon.mod_glossary.fullmatch": "Atrast tikai pilnus vārdus", - "addon.mod_glossary.linking": "Automātiskā saistīšana", - "addon.mod_glossary.modulenameplural": "Vārdnīcas", - "addon.mod_glossary.noentriesfound": "Ieraksti nav atrasti.", - "addon.mod_glossary.searchquery": "Meklēšanas vaicājums", - "addon.mod_imscp.deploymenterror": "Satura pakotnes kļūda!", - "addon.mod_imscp.modulenameplural": "IMS satura pakotnes", - "addon.mod_imscp.showmoduledescription": "Parādīt aprakstu", - "addon.mod_imscp.toc": "Satura rādītājs", - "addon.mod_lesson.answer": "Atbilde", - "addon.mod_lesson.attempt": "Mēģinājums: {{$a}}", - "addon.mod_lesson.attemptsremaining": "Jums vēl ir {{$a}} mēģinājums(-i).", - "addon.mod_lesson.averagescore": "Vidējais punktu skaits", - "addon.mod_lesson.averagetime": "Vidējais laiks", - "addon.mod_lesson.branchtable": "Saturs", - "addon.mod_lesson.cannotfindattempt": "Kļūda: neizdevās atrast mēģinājumu", - "addon.mod_lesson.cannotfinduser": "Kļūda: neizdevās atrast lietotājus", - "addon.mod_lesson.clusterjump": "Neapskatīts jautājums klasterī", - "addon.mod_lesson.completed": "Pabeigts", - "addon.mod_lesson.congratulations": "Apsveicam! Esat nonācis līdz nodarbības beigām.", - "addon.mod_lesson.continue": "Turpināt", - "addon.mod_lesson.continuetonextpage": "Turpināt uz nākamo lapu.", - "addon.mod_lesson.defaultessayresponse": "Jūsu eseju vērtēs kursa instruktors.", - "addon.mod_lesson.detailedstats": "Detalizēta statistika", - "addon.mod_lesson.didnotanswerquestion": "Netika sniegta atbilde uz šo jautājumu.", - "addon.mod_lesson.displayofgrade": "Vērtējuma rādījums (tikai studentiem)", - "addon.mod_lesson.displayscorewithessays": "Jūs esat ieguvis {{$a.score}} punktus no {{$a.tempmaxgrade}} par atbildēm, kuras tiek novērtētas automātiski.
                Jūsu atbildes uz {{$a.essayquestions}} esejas jautājumu(-iem) tiks novērtētas, un šis vērtējums kopējam punktu skaitam tiks pievienots vēlāk.

                . Pašreizējais vērtējums bez atbildēm uz esejas jautājumu(-iem) ir {{$a.score}} punkti no {{$a.grade}}.", - "addon.mod_lesson.displayscorewithoutessays": "Jūsu punktu skaits ir {{$a.score}} (no {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Parole nevar būt tukša", - "addon.mod_lesson.enterpassword": "Lūdzu, ievadiet paroli:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Jūs neatbildējāt ne uz vienu jautājumu. Par šo nodarbību esat nopelnījis 0 punktu.", - "addon.mod_lesson.errorprefetchrandombranch": "Šajā nodarbībā ir pāreja uz nejaušu satura lapu. To nevar veikt lietotnē, kamēr tā nav atvērta tīmekļa pārlūkprogrammā.", - "addon.mod_lesson.errorreviewretakenotlast": "Šo mēģinājumu vairs nevar pārskatīt, jo ir pabeigts vēl viens mēģinājums.", - "addon.mod_lesson.finish": "Beigas", - "addon.mod_lesson.finishretakeoffline": "Šis mēģinājums tika pabeigts bezsaistē.", - "addon.mod_lesson.firstwrong": "Diemžēl nevarat iegūt šos punktus, jo neesat atbildējis pareizi. Vai vēlaties turpināt minējumus, lai baudītu mācīšanās prieku (taču negūstot kredītpunktus)?", - "addon.mod_lesson.gotoendoflesson": "Iet uz nodarbības beigām", - "addon.mod_lesson.grade": "Novērtēt", - "addon.mod_lesson.highscore": "Lielākais punktu skaits", - "addon.mod_lesson.hightime": "Ilgākais laiks", - "addon.mod_lesson.leftduringtimed": "Esat pārtraucis piedalīšanos ierobežota ilguma nodarbībā.
                Lūdzu, noklikšķiniet uz Turpināt, lai atsāktu savu piedalīšanos nodarbībā.", - "addon.mod_lesson.leftduringtimednoretake": "Esat pārtraucis piedalīšanos ierobežota ilguma nodarbībā,
                un jums nav atļauts šo nodarbību turpināt vai sākt vēlreiz no sākuma.", - "addon.mod_lesson.lessonmenu": "Nodarbības izvēlne", - "addon.mod_lesson.lessonstats": "Nodarbības statistika", - "addon.mod_lesson.linkedmedia": "Saistītie datu nesēji", - "addon.mod_lesson.loginfail": "Pieslēgšanās neizdevās. Lūdzu, mēģiniet vēlreiz...", - "addon.mod_lesson.lowscore": "Mazākais punktu skaits", - "addon.mod_lesson.lowtime": "Īsākais laiks", - "addon.mod_lesson.maximumnumberofattemptsreached": "Ir sasniegts maksimālais mēģinājumu skaits — notiek pāreja uz nākamo lapu.", - "addon.mod_lesson.modattemptsnoteacher": "Studentu recenzijas ir atļauts rakstīt tikai studentiem.", - "addon.mod_lesson.modulenameplural": "Nodarbības", - "addon.mod_lesson.noanswer": "Nav sniegta atbilde. Lūdzu, atgriezieties un iesniedziet atbildi.", - "addon.mod_lesson.nolessonattempts": "Šajā nodarbībā nav veikts neviens mēģinājums.", - "addon.mod_lesson.notcompleted": "Nav pabeigts", - "addon.mod_lesson.numberofcorrectanswers": "Pareizo atbilžu skaits: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Atbildēto jautājumu skaits: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Atbildēto jautājumu skaits: {{$a.nquestions}}; (ir jāatbild vismaz uz {{$a.minquestions}} jautājumiem)", - "addon.mod_lesson.ongoingcustom": "Līdz šim esat ieguvis {{$a.score}} punktu(-s) no {{$a.currenthigh}}.", - "addon.mod_lesson.ongoingnormal": "{{$a.viewed}} mēģinājumos esat pareizi atbildējis {{$a.correct}} reizes.", - "addon.mod_lesson.or": "VAI", - "addon.mod_lesson.overview": "Pārskats", - "addon.mod_lesson.preview": "Priekšskatījums", - "addon.mod_lesson.progressbarteacherwarning2": "Jūs neredzēsiet norises joslu, jo jūs varat rediģēt šo nodarbību", - "addon.mod_lesson.progresscompleted": "Jūs esat pabeidzis {{$a}}% no nodarbības", - "addon.mod_lesson.question": "Jautājums", - "addon.mod_lesson.rawgrade": "Aptuvens vērtējums", - "addon.mod_lesson.reports": "Atskaites", - "addon.mod_lesson.response": "Atbilde", - "addon.mod_lesson.retakefinishedinsync": "Bezsaistes mēģinājums tika sinhronizēts. Vai vēlaties to pārskatīt?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.review": "Pārskats", - "addon.mod_lesson.reviewlesson": "Pārskatīt nodarbību", - "addon.mod_lesson.reviewquestionback": "Jā, es vēlos mēģināt vēlreiz.", - "addon.mod_lesson.reviewquestioncontinue": "Nē, es vēlos pāriet pie nākamā jautājuma.", - "addon.mod_lesson.secondpluswrong": "Ne gluži. Vai vēlaties mēģināt vēlreiz?", - "addon.mod_lesson.submit": "Iesniegt", - "addon.mod_lesson.teacherjumpwarning": "Šajā nodarbībā tiek izmantota pāriešana {{$a.cluster}} vai pāriešana {{$a.unseen}}. Tā vietā tiks izmantota pāriešana uz nākamo lapu. Pieslēdzieties kā students, lai pārbaudītu šīs pāriešanas.", - "addon.mod_lesson.teacherongoingwarning": "Pašreizējais punktu skaits tiek rādīts tikai studentam. Pieslēdzieties kā students, lai pārbaudītu pašreizējo punktu skaitu.", - "addon.mod_lesson.teachertimerwarning": "Taimeri var izmantot tikai studenti. Pārbaudiet taimeri, pieslēdzoties kā students.", - "addon.mod_lesson.thatsthecorrectanswer": "Tā ir pareizā atbilde.", - "addon.mod_lesson.thatsthewronganswer": "Tā ir nepareizā atbilde.", - "addon.mod_lesson.timeremaining": "Atlikušais laiks", - "addon.mod_lesson.timetaken": "Patērētais laiks", - "addon.mod_lesson.unseenpageinbranch": "Neapskatīts jautājums zarojumā", - "addon.mod_lesson.warningretakefinished": "Mēģinājums tika pabeigts vietnē.", - "addon.mod_lesson.welldone": "Lieliski!", - "addon.mod_lesson.youhaveseen": "Jūs jau esat skatījis vairākas šīs nodarbības lapas.
                Vai vēlaties sākt ar lapu, kuru skatījāt pēdējo?", - "addon.mod_lesson.youranswer": "Jūsu atbilde", - "addon.mod_lesson.yourcurrentgradeisoutof": "Jūsu pašreizējais vērtējums ir {{$a.grade}} no {{$a.total}}.", - "addon.mod_lesson.youshouldview": "Jāatbild vismaz uz šādu skaitu jautājumu: {{$a}}", - "addon.mod_lti.errorgetlti": "Kļūda, iegūstot moduļa datus.", - "addon.mod_lti.errorinvalidlaunchurl": "Uzsākšanas URL nav derīgs.", - "addon.mod_lti.launchactivity": "Palaist aktivitāti", - "addon.mod_lti.modulenameplural": "pamata sadarbspējas mācību rīki", - "addon.mod_page.errorwhileloadingthepage": "Ielādējot lapas saturu, radās kļūda.", - "addon.mod_page.modulenameplural": "Lapas", - "addon.mod_quiz.answercolon": "Atbilde:", - "addon.mod_quiz.attemptfirst": "Pirmais mēģinājums", - "addon.mod_quiz.attemptlast": "Pēdējais mēģinājums", - "addon.mod_quiz.attemptnumber": "Mēģinājums", - "addon.mod_quiz.attemptquiznow": "Tūlīt mēģināt izpildīt testu", - "addon.mod_quiz.attemptstate": "Stāvoklis", - "addon.mod_quiz.cannotsubmitquizdueto": "Šo testa mēģinājumu nevar iesniegt šādu iemeslu dēļ:", - "addon.mod_quiz.clearchoice": "Dzēst manu izvēli", - "addon.mod_quiz.comment": "Komentārs", - "addon.mod_quiz.completedon": "Pabeigts", - "addon.mod_quiz.confirmclose": "Tūlīt slēgsit šo mēģinājumu. Slēdzot mēģinājumu, vairs nevarēsit mainīt savas atbildes.", - "addon.mod_quiz.confirmcontinueoffline": "Šis mēģinājums nav sinhronizēts kopš {{$a}}. Ja kopš tā laika esat turpinājis šo mēģinājumu citā ierīcē, varat zaudēt datus.", - "addon.mod_quiz.confirmleavequizonerror": "Saglabājot atbildes, radās kļūda. Vai tiešām vēlaties iziet no testa?", - "addon.mod_quiz.confirmstart": "Šī testa kārtošanai atvēlētais laiks ir {{$a}}. Laika atskaite sākas no brīža, kad jūs sāksiet testa mēģinājumu. Testu jāpabeidz un jāiesniedz pirms atvēlētais laiks beidzas. Vai esat pārliecināti, ka vēlaties sākt tagad?", - "addon.mod_quiz.confirmstartheader": "Tests ar laika ierobežojumu", - "addon.mod_quiz.continueattemptquiz": "Turpināt pēdējo mēģinājumu", - "addon.mod_quiz.continuepreview": "Turpināt pēdējo priekšskatījumu", - "addon.mod_quiz.errorbehaviournotsupported": "Šo testu nevar pildīt lietotnē, jo lietotne neatbalsta jautājumu uzvedību:", - "addon.mod_quiz.errordownloading": "Lejupielādējot nepieciešamos datus, radās kļūda.", - "addon.mod_quiz.errorgetattempt": "Kļūda, mēģinot iegūt datus.", - "addon.mod_quiz.errorgetquestions": "Kļūda, mēģinot iegūt jautājumus.", - "addon.mod_quiz.errorgetquiz": "Kļūda, mēģinot iegūt testa datus.", - "addon.mod_quiz.errorparsequestions": "Nolasot jautājumus, radās kļūda. Lūdzu, pildiet šo testu tīmekļa pārlūkprogrammā.", - "addon.mod_quiz.errorquestionsnotsupported": "Šo testu nevar pildīt lietotnē, jo lietotne neatbalsta šādus jautājumus:", - "addon.mod_quiz.errorrulesnotsupported": "Šo testu nevar pildīt lietotnē, jo lietotne neatbalsta šādus piekļuves nosacījumus:", - "addon.mod_quiz.errorsaveattempt": "Saglabājot mēģinājuma datus, radās kļūda.", - "addon.mod_quiz.feedback": "Atsauksmes", - "addon.mod_quiz.finishattemptdots": "Pabeigt mēģinājumu ...", - "addon.mod_quiz.finishnotsynced": "Pabeigts, bet nav sinhronizēts", - "addon.mod_quiz.grade": "Vērtējums", - "addon.mod_quiz.gradeaverage": "Vidējais vērtējums", - "addon.mod_quiz.gradehighest": "Augstākais vērtējums", - "addon.mod_quiz.grademethod": "Vērtēšanas metode", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}}/{{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Punkti", - "addon.mod_quiz.modulenameplural": "Testi", - "addon.mod_quiz.mustbesubmittedby": "Šo mēģinājumu ir jāiesniedz līdz {{$a}}.", - "addon.mod_quiz.noquestions": "Neviens jautājums vēl nav pievienots", - "addon.mod_quiz.noreviewattempt": "Jums nav atļaujas pārskatīt šo mēģinājumu.", - "addon.mod_quiz.notyetgraded": "Vēl nav novērtēts", - "addon.mod_quiz.opentoc": "Atvērt navigācijas uznirstošo logu", - "addon.mod_quiz.outof": "{{$a.grade}} no maksimāli iespējamiem {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} no maksimuma {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Kopējās atsauksmes", - "addon.mod_quiz.overdue": "Nokavēts", - "addon.mod_quiz.overduemustbesubmittedby": "Šis mēģinājums jau kavējas. Tam jau bija jābūt iesniegtam. Ja jūs vēlaties, lai šis tests tiktu novērtēts, jums tas ir jāiesniedz līdz {{$a}}. Ja jūs to neiesniegsiet līdz tam laikam, vērtējums no šī mēģinājuma netiks ņemts vērā.", - "addon.mod_quiz.preview": "Priekšskatījums", - "addon.mod_quiz.previewquiznow": "Priekšskatīt testu tūlīt", - "addon.mod_quiz.question": "Jautājums", - "addon.mod_quiz.quiznavigation": "Pārvietošanās testā", - "addon.mod_quiz.quizpassword": "Testa parole", - "addon.mod_quiz.reattemptquiz": "Vēlreiz mēģināt izpildīt testu", - "addon.mod_quiz.requirepasswordmessage": "Lai mēģinātu izpildīt šo testu, jums ir jāzina tā parole.", - "addon.mod_quiz.returnattempt": "Atgriezties pie mēģinājuma", - "addon.mod_quiz.review": "Pārskats", - "addon.mod_quiz.reviewofattempt": "Mēģinājuma {{$a}} pārskats", - "addon.mod_quiz.reviewofpreview": "Priekškatījuma pārskats", - "addon.mod_quiz.showall": "Rādīt visus jautājumus vienā lapā", - "addon.mod_quiz.showeachpage": "Vienlaicīgi rādīt vienu lapu", - "addon.mod_quiz.startattempt": "Sākt mēģinājumu", - "addon.mod_quiz.startedon": "Sākts", - "addon.mod_quiz.stateabandoned": "Nav iesniegti", - "addon.mod_quiz.statefinished": "Pabeigts", - "addon.mod_quiz.statefinisheddetails": "Iesniegts {{$a}}", - "addon.mod_quiz.stateinprogress": "Darbībā", - "addon.mod_quiz.stateoverdue": "Nokavēti", - "addon.mod_quiz.stateoverduedetails": "Jābūt iesniegtam līdz {{$a}}", - "addon.mod_quiz.status": "Statuss", - "addon.mod_quiz.submitallandfinish": "Iesniegt visu un pabeigt", - "addon.mod_quiz.summaryofattempt": "Mēģinājuma kopsavilkums", - "addon.mod_quiz.summaryofattempts": "Jūsu iepriekšējo mēģinājumu kopsavilkums", - "addon.mod_quiz.timeleft": "Atlikušais laiks", - "addon.mod_quiz.timetaken": "Patērētais laiks", - "addon.mod_quiz.warningattemptfinished": "Bezsaistes mēģinājums tiek atcelts, jo tas tika pabeigts vietnē vai nav atrasts.", - "addon.mod_quiz.warningdatadiscarded": "Dažas bezsaistes atbildes tika atceltas, jo jautājumi tika mainīti tiešsaistē.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Mēģinājums nav pabeigts, jo dažas bezsaistes atbildes tika atceltas. Lūdzu, pārskatiet savas atbildes un pēc tam atkārtoti iesniedziet mēģinājumu.", - "addon.mod_quiz.yourfinalgradeis": "Jūsu gala vērtējums šajā testā ir {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Ielādējot saturu, radās kļūda.", - "addon.mod_resource.modulenameplural": "Faili", - "addon.mod_resource.openthefile": "Atvērt failu", - "addon.mod_resource.uploadeddate": "Augšupielādēts {{$a}}", - "addon.mod_scorm.asset": "Krājums", - "addon.mod_scorm.assetlaunched": "Krājums — apskatīts", - "addon.mod_scorm.attempts": "Mēģinājumi", - "addon.mod_scorm.averageattempt": "Vidējais mēģinājumu skaits", - "addon.mod_scorm.browse": "Priekšskatījums", - "addon.mod_scorm.browsed": "Pārlūkots", - "addon.mod_scorm.browsemode": "Priekšskatījuma režīms", - "addon.mod_scorm.cannotcalculategrade": "Vērtējumu nevarēja aprēķināt.", - "addon.mod_scorm.completed": "Pabeigts", - "addon.mod_scorm.contents": "Saturs", - "addon.mod_scorm.dataattemptshown": "Šie dati pieder pie mēģinājuma nr. {{number}}.", - "addon.mod_scorm.enter": "Atvērt", - "addon.mod_scorm.errorcreateofflineattempt": "Izveidojot jaunu bezsaistes mēģinājumu, radās kļūda. Lūdzu mēģiniet vēlreiz.", - "addon.mod_scorm.errordownloadscorm": "Lejupielādējot SCORM, radās kļūda: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Kļūda, iegūstot SCORM datus.", - "addon.mod_scorm.errorinvalidversion": "Diemžēl, šī lietotne atbalsta tikai SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "SCORM pakotņu lejupielāde ir atspējota. Lūdzu, sazinieties ar vietnes administratoru.", - "addon.mod_scorm.errornovalidsco": "Šai SCORM pakotnei nav redzama SCO ielādei.", - "addon.mod_scorm.errorpackagefile": "Diemžēl, lietotne atbalsta tikai ZIP pakotnes.", - "addon.mod_scorm.errorsyncscorm": "Sinhronizācijas laikā radās kļūda. Lūdzu mēģiniet vēlreiz.", - "addon.mod_scorm.exceededmaxattempts": "Jūs esat sasniedzis maksimālo atļauto mēģinājumu skaitu.", - "addon.mod_scorm.failed": "Neizdevās", - "addon.mod_scorm.firstattempt": "Pirmais mēģinājums", - "addon.mod_scorm.gradeaverage": "Vidējais vērtējums", - "addon.mod_scorm.gradeforattempt": "Mēģinājuma vērtējums", - "addon.mod_scorm.gradehighest": "Augstākais vērtējums", - "addon.mod_scorm.grademethod": "Vērtēšanas metode", - "addon.mod_scorm.gradereported": "Paziņotais vērtējums", - "addon.mod_scorm.gradescoes": "Mācību objekti", - "addon.mod_scorm.gradesum": "Summas vērtējums", - "addon.mod_scorm.highestattempt": "Labākais mēģinājums", - "addon.mod_scorm.incomplete": "Nepilnīgs", - "addon.mod_scorm.lastattempt": "Pēdējais pabeigtais mēģinājums", - "addon.mod_scorm.modulenameplural": "Moduļi SCORM/AICC", - "addon.mod_scorm.newattempt": "Sākt jaunu mēģinājumu", - "addon.mod_scorm.noattemptsallowed": "Atļauto mēģinājumu skaits", - "addon.mod_scorm.noattemptsmade": "Veikto mēģinājumu skaits", - "addon.mod_scorm.notattempted": "Nav mēģināts", - "addon.mod_scorm.offlineattemptnote": "Šim mēģinājumam ir dati, kas nav sinhronizēti.", - "addon.mod_scorm.offlineattemptovermax": "Šo mēģinājumu nevar nosūtīt, jo esat pārsniedzis maksimālo mēģinājumu skaitu.", - "addon.mod_scorm.organizations": "Organizācijas", - "addon.mod_scorm.passed": "Nokārtots", - "addon.mod_scorm.reviewmode": "Pārskatīšanas režīms", - "addon.mod_scorm.score": "Punktu skaits", - "addon.mod_scorm.scormstatusnotdownloaded": "Šī SCORM pakotne nav lejupielādēta. Kad to atvērsiet, tā tiks automātiski lejupielādēta.", - "addon.mod_scorm.scormstatusoutdated": "Šī SCORM pakotne ir modificēta kopš pēdējās lejupielādes. Kad to atvērsiet, tā tiks automātiski lejupielādēta.", - "addon.mod_scorm.suspended": "Atlikts", - "addon.mod_scorm.toc": "Satura rādītājs", - "addon.mod_scorm.warningofflinedatadeleted": "Daži bezsaistes dati no mēģinājuma {{number}} tika atmesti, jo tos nevar uzskatīt par jaunu mēģinājumu.", - "addon.mod_scorm.warningsynconlineincomplete": "Dažus mēģinājumus nevarēja sinhronizēt ar vietni, jo pēdējais tiešsaistes mēģinājums vēl nav pabeigts. Lūdzu, vispirms pabeidziet tiešsaistes mēģinājumu.", - "addon.mod_survey.cannotsubmitsurvey": "Diemžēl, iesniedzot aptauju, radās problēma. Lūdzu mēģiniet vēlreiz.", - "addon.mod_survey.errorgetsurvey": "Kļūda, iegūstot aptaujas datus.", - "addon.mod_survey.ifoundthat": "Es atklāju, ka", - "addon.mod_survey.ipreferthat": "Es dodu priekšroku tam, ka", - "addon.mod_survey.modulenameplural": "Anketas", - "addon.mod_survey.responses": "Atbildes", - "addon.mod_survey.results": "Rezultāti", - "addon.mod_url.accessurl": "Piekļuves URL", - "addon.mod_url.modulenameplural": "URL", - "addon.mod_url.pointingtourl": "URL, uz kuru norāda resurss.", - "addon.mod_wiki.cannoteditpage": "Jūs nevarat rediģēt šo lapu.", - "addon.mod_wiki.createpage": "Izveidot lapu", - "addon.mod_wiki.editingpage": "Šīs lapas '{{$a}}' rediģēšana", - "addon.mod_wiki.errorloadingpage": "Ielādējot lapu, radās kļūda.", - "addon.mod_wiki.errornowikiavailable": "Šim viki vēl nav satura.", - "addon.mod_wiki.gowikihome": "Doties uz viki pirmo lapu", - "addon.mod_wiki.map": "Karte", - "addon.mod_wiki.modulenameplural": "Viki", - "addon.mod_wiki.newpagetitle": "Jauns lapas nosaukums", - "addon.mod_wiki.nocontent": "Šai lapai nav satura", - "addon.mod_wiki.notingroup": "Nav grupā", - "addon.mod_wiki.pageexists": "Šī lapa jau eksistē. Novirzīties uz to.", - "addon.mod_wiki.pagename": "Lapas nosaukums", - "addon.mod_wiki.subwiki": "Apakš-viki", - "addon.mod_wiki.titleshouldnotbeempty": "Nosaukumam nevajadzētu būt tukšam", - "addon.mod_wiki.viewpage": "Skatīt lapu", - "addon.mod_wiki.wikipage": "Viki lapa", - "addon.mod_wiki.wrongversionlock": "Cits lietotājs rediģēja šo lapu, kamēr jūs to rediģējāt un jūsu saturs ir novecojis.", - "addon.mod_workshop.alreadygraded": "Jau novērtēts", - "addon.mod_workshop.areainstructauthors": "Iesniegšanas norādījumi", - "addon.mod_workshop.areainstructreviewers": "Novērtēšanas norādījumi", - "addon.mod_workshop.assess": "Novērtēt", - "addon.mod_workshop.assessedsubmission": "Novērtēts iesniegums", - "addon.mod_workshop.assessmentform": "Novērtēšanas veidlapa", - "addon.mod_workshop.assessmentsettings": "Novērtēšanas iestatījumi", - "addon.mod_workshop.assessmentstrategynotsupported": "Novērtēšanas stratēģija {{$a}} netiek atbalstīta", - "addon.mod_workshop.assessmentweight": "Novērtējuma svars", - "addon.mod_workshop.assignedassessments": "Novērtēšanai piešķirtie iesniegumi", - "addon.mod_workshop.assignedassessmentsnone": "Jums nav novērtēšanai piešķirtu iesniegumu", - "addon.mod_workshop.conclusion": "Nobeigums", - "addon.mod_workshop.createsubmission": "Iesniegt", - "addon.mod_workshop.deletesubmission": "Dzēst iesniegumu", - "addon.mod_workshop.editsubmission": "Rediģēt iesniegumu", - "addon.mod_workshop.feedbackauthor": "Atsauksme autoram", - "addon.mod_workshop.feedbackby": "{{$a}} atsauksme", - "addon.mod_workshop.feedbackreviewer": "Atsauksme recenzentam", - "addon.mod_workshop.givengrades": "Piešķirtie vērtējumi", - "addon.mod_workshop.gradecalculated": "Aprēķinātais iesnieguma novērtējums", - "addon.mod_workshop.gradeinfo": "Vērtējums: {{$a.received}} no {{$a.max}}", - "addon.mod_workshop.gradeover": "Pārmainīt iesnieguma novērtējumu", - "addon.mod_workshop.gradesreport": "Semināra vērtējumu atskaite", - "addon.mod_workshop.gradinggrade": "Novērtējuma vērtējums", - "addon.mod_workshop.gradinggradecalculated": "Novērtējuma aprēķinātais vērtējums", - "addon.mod_workshop.gradinggradeof": "Novērtējuma vērtējums (no {{$a}})", - "addon.mod_workshop.gradinggradeover": "Pārmainīt novērtējuma vērtējumu", - "addon.mod_workshop.modulenameplural": "Semināri", - "addon.mod_workshop.nogradeyet": "Vēl nav vērtējuma", - "addon.mod_workshop.notassessed": "Vēl nav novērtēts", - "addon.mod_workshop.notoverridden": "Nav pārmainīts", - "addon.mod_workshop.noyoursubmission": "Jūs vēl neesat iesniedzis savu darbu", - "addon.mod_workshop.publishedsubmissions": "Publicētie iesniegumi", - "addon.mod_workshop.publishsubmission": "Publicēt iesniegumu", - "addon.mod_workshop.publishsubmission_help": "Publicētie iesniegumi ir pieejami citiem, kad seminārs ir slēgts.", - "addon.mod_workshop.reassess": "Novērtēt atkārtoti", - "addon.mod_workshop.receivedgrades": "Saņemtie vērtējumi", - "addon.mod_workshop.submissionattachment": "Pielikums", - "addon.mod_workshop.submissioncontent": "Iesnieguma saturs", - "addon.mod_workshop.submissiongrade": "Iesnieguma novērtējums", - "addon.mod_workshop.submissiongradeof": "Iesnieguma novērtējums (no {{$a}})", - "addon.mod_workshop.submissionrequiredtitle": "Jums jāievada nosaukums.", - "addon.mod_workshop.submissiontitle": "Virsraksts", - "addon.mod_workshop.userplan": "Semināra plānotājs", - "addon.mod_workshop.warningassessmentmodified": "Iesniegums tika izmainīts vietnē.", - "addon.mod_workshop.warningsubmissionmodified": "Novērtējums tika izmainīts vietnē.", - "addon.mod_workshop.weightinfo": "Svars: {{$a}}", - "addon.mod_workshop.yourassessment": "Jūsu paša novērtējums", - "addon.mod_workshop.yourgrades": "Jūsu vērtējumi", - "addon.mod_workshop.yoursubmission": "Jūsu iesniegums", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "{{$a}} komentārs", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "{{$a}} vērtējums", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspekts {{$a}}", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "{{$a}} komentārs", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspekts {a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "{{$a}} vērtējums", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Apgalvojums {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kritērijs {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Jums ir jāizvēlas viens no šiem vienumiem", - "addon.notes.addnewnote": "Pievienot jaunu piezīmi", - "addon.notes.coursenotes": "Kursa piezīmes", - "addon.notes.deleteconfirm": "Dzēst šo piezīmi?", - "addon.notes.nonotes": "Pagaidām vēl nav šāda tipa piezīmju", - "addon.notes.note": "Piezīme", - "addon.notes.notes": "Piezīmes", - "addon.notes.personalnotes": "Personīgās piezīmes", - "addon.notes.publishstate": "Statuss", - "addon.notes.sitenotes": "Vietnes piezīmes", - "addon.notes.userwithid": "Lietotājs ar ID {{id}}", - "addon.notes.warningnotenotsent": "Kursam {{course}} nevarēja pievienot piezīmi (-es). {{error}}", - "addon.notifications.errorgetnotifications": "Saņemot paziņojumus, radās kļūda.", - "addon.notifications.markallread": "Marķēt visu kā izlasītu", - "addon.notifications.notificationpreferences": "Paziņojumu iestatījumi", - "addon.notifications.notifications": "Paziņojumi", - "addon.notifications.playsound": "Atskaņot skaņu", - "addon.notifications.therearentnotificationsyet": "Nav paziņojumu.", - "addon.storagemanager.deletecourse": "Izkraut visus kursa datus", - "addon.storagemanager.deletedatafrom": "Izkraut datus no {{name}}", - "addon.storagemanager.info": "Ierīcē saglabātie faili padara lietotni ātrāku un ļauj lietotni izmantot bezsaistē. Jūs varat droši izkraut failus, ja jums ir nepieciešams atbrīvot atmiņas vietu.", - "addon.storagemanager.managestorage": "Pārvaldiet krātuvi", - "addon.storagemanager.storageused": "Izmantotā failu krātuve:", - "assets.countries.AD": "Andora", - "assets.countries.AE": "Apvienotie Arābu Emirāti", - "assets.countries.AF": "Afganistāna", - "assets.countries.AG": "Antigva un Barbuda", - "assets.countries.AI": "Angilja", - "assets.countries.AL": "Albānija", - "assets.countries.AM": "Armēnija", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktika", - "assets.countries.AR": "Argentīna", - "assets.countries.AS": "ASV Samoa", - "assets.countries.AT": "Austrija", - "assets.countries.AU": "Austrālija", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Olande", - "assets.countries.AZ": "Azerbaidžāna", - "assets.countries.BA": "Bosnija un Hercegovina", - "assets.countries.BB": "Barbadosa", - "assets.countries.BD": "Bangladeša", - "assets.countries.BE": "Beļģija", - "assets.countries.BF": "Burkinafaso", - "assets.countries.BG": "Bulgārija", - "assets.countries.BH": "Bahreina", - "assets.countries.BI": "Burundija", - "assets.countries.BJ": "Benina", - "assets.countries.BL": "Senbartelmī", - "assets.countries.BM": "Bermudu salas", - "assets.countries.BN": "Bruneja Darusalama", - "assets.countries.BO": "Bolīvija", - "assets.countries.BR": "Brazīlija", - "assets.countries.BS": "Bahamu salas", - "assets.countries.BT": "Butāna", - "assets.countries.BV": "Buvē sala", - "assets.countries.BW": "Botsvāna", - "assets.countries.BY": "Baltkrievija", - "assets.countries.BZ": "Beliza", - "assets.countries.CA": "Kanāda", - "assets.countries.CC": "Kokosu (Kīlinga) salas", - "assets.countries.CD": "Kongo Demokrātiskā Republika", - "assets.countries.CF": "Centrālāfrikas Republika", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Šveice", - "assets.countries.CI": "Kotdivuāra", - "assets.countries.CK": "Kuka salas", - "assets.countries.CL": "Čīle", - "assets.countries.CM": "Kamerūna", - "assets.countries.CN": "Ķīna", - "assets.countries.CO": "Kolumbija", - "assets.countries.CR": "Kostarika", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Kaboverde", - "assets.countries.CX": "Ziemsvētku sala", - "assets.countries.CY": "Kipra", - "assets.countries.CZ": "Čehija", - "assets.countries.DE": "Vācija", - "assets.countries.DJ": "Džibutija", - "assets.countries.DK": "Dānija", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Dominikāna", - "assets.countries.DZ": "Alžīrija", - "assets.countries.EC": "Ekvadora", - "assets.countries.EE": "Igaunija", - "assets.countries.EG": "Ēģipte", - "assets.countries.EH": "Rietumsahāra", - "assets.countries.ER": "Eritreja", - "assets.countries.ES": "Spānija", - "assets.countries.ET": "Etiopija", - "assets.countries.FI": "Somija", - "assets.countries.FJ": "Fidži", - "assets.countries.FK": "Folklenda (Malvinu) salas", - "assets.countries.FM": "Mikronēzija", - "assets.countries.FO": "Fēru salas", - "assets.countries.FR": "Francija", - "assets.countries.GA": "Gabona", - "assets.countries.GB": "Lielbritānija", - "assets.countries.GD": "Grenāda", - "assets.countries.GE": "Gruzija", - "assets.countries.GF": "Francijas Gviāna", - "assets.countries.GG": "Gērnsija", - "assets.countries.GH": "Gana", - "assets.countries.GI": "Gibraltārs", - "assets.countries.GL": "Grenlande", - "assets.countries.GM": "Gambija", - "assets.countries.GN": "Gvineja", - "assets.countries.GP": "Gvadelupa", - "assets.countries.GQ": "Ekvatoriālā Gvineja", - "assets.countries.GR": "Grieķija", - "assets.countries.GS": "Dienviddžordžija un Dienvidsendviču Salas", - "assets.countries.GT": "Gvatemala", - "assets.countries.GU": "Guama", - "assets.countries.GW": "Gvineja-Bisava", - "assets.countries.GY": "Gajāna", - "assets.countries.HK": "Honkonga", - "assets.countries.HM": "Hērda sala un Makdonalda salas", - "assets.countries.HN": "Hondurasa", - "assets.countries.HR": "Horvātija", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Ungārija", - "assets.countries.ID": "Indonēzija", - "assets.countries.IE": "Īrija", - "assets.countries.IL": "Izraēla", - "assets.countries.IM": "Mena", - "assets.countries.IN": "Indija", - "assets.countries.IO": "Indijas okeāna Britu teritorija", - "assets.countries.IQ": "Irāka", - "assets.countries.IR": "Irāna (Islāma Republika)", - "assets.countries.IS": "Īslande", - "assets.countries.IT": "Itālija", - "assets.countries.JE": "Džērsija", - "assets.countries.JM": "Jamaika", - "assets.countries.JO": "Jordānija", - "assets.countries.JP": "Japāna", - "assets.countries.KE": "Kenija", - "assets.countries.KG": "Kirgizstāna", - "assets.countries.KH": "Kambodža", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komoru salas", - "assets.countries.KN": "Sentkitsa un Nevisa", - "assets.countries.KP": "Ziemeļkoreja", - "assets.countries.KR": "Dienvidkoreja", - "assets.countries.KW": "Kuveita", - "assets.countries.KY": "Kaimanu salas", - "assets.countries.KZ": "Kazahstāna", - "assets.countries.LA": "Laosa", - "assets.countries.LB": "Libāna", - "assets.countries.LC": "Sentlūsija", - "assets.countries.LI": "Lihtenšteina", - "assets.countries.LK": "Šrilanka", - "assets.countries.LR": "Libērija", - "assets.countries.LS": "Lesoto", - "assets.countries.LT": "Lietuva", - "assets.countries.LU": "Luksemburga", - "assets.countries.LV": "Latvija", - "assets.countries.LY": "Lībija", - "assets.countries.MA": "Maroka", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Moldova", - "assets.countries.ME": "Melnkalne", - "assets.countries.MF": "Senmartēna", - "assets.countries.MG": "Madagaskara", - "assets.countries.MH": "Māršala salas", - "assets.countries.MK": "Maķedonija", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Mjanma", - "assets.countries.MN": "Mongolija", - "assets.countries.MO": "Makao", - "assets.countries.MP": "Ziemeļu Marianas salas", - "assets.countries.MQ": "Martinika", - "assets.countries.MR": "Mauritānija", - "assets.countries.MS": "Montserrata", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Maurīcija", - "assets.countries.MV": "Maldīvija", - "assets.countries.MW": "Malāvija", - "assets.countries.MX": "Meksika", - "assets.countries.MY": "Malaizija", - "assets.countries.MZ": "Mozambika", - "assets.countries.NA": "Namībija", - "assets.countries.NC": "Jaunkaledonija", - "assets.countries.NE": "Nigēra", - "assets.countries.NF": "Norfolkas sala", - "assets.countries.NG": "Nigērija", - "assets.countries.NI": "Nikaragva", - "assets.countries.NL": "Nīderlande", - "assets.countries.NO": "Norvēģija", - "assets.countries.NP": "Nepāla", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Jaunzēlande", - "assets.countries.OM": "Omāna", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Francijas Polinēzija", - "assets.countries.PG": "Papua-Jaungvineja", - "assets.countries.PH": "Filipīnas", - "assets.countries.PK": "Pakistāna", - "assets.countries.PL": "Polija", - "assets.countries.PM": "Senpjēra un Mikelona", - "assets.countries.PN": "Pitkērna", - "assets.countries.PR": "Puertoriko", - "assets.countries.PS": "Palestīna", - "assets.countries.PT": "Portugāle", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paragvaja", - "assets.countries.QA": "Katara", - "assets.countries.RE": "Reinjona", - "assets.countries.RO": "Rumānija", - "assets.countries.RS": "Serbija", - "assets.countries.RU": "Krievija", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Saūda Arābija", - "assets.countries.SB": "Zālamana salas", - "assets.countries.SC": "Seišelu salas", - "assets.countries.SD": "Sudāna", - "assets.countries.SE": "Zviedrija", - "assets.countries.SG": "Singapūra", - "assets.countries.SH": "Sv. Helēnas sala", - "assets.countries.SI": "Slovēnija", - "assets.countries.SJ": "Svalbāras arhipelāgs un Jana Majena sala", - "assets.countries.SK": "Slovākija", - "assets.countries.SL": "Sjerraleone", - "assets.countries.SM": "Sanmarīno", - "assets.countries.SN": "Senegāla", - "assets.countries.SO": "Somālija", - "assets.countries.SR": "Surinama", - "assets.countries.ST": "Santome un Prinsipi", - "assets.countries.SV": "Salvadora", - "assets.countries.SY": "Sīrija", - "assets.countries.SZ": "Svazilenda", - "assets.countries.TC": "Tērksas un Kaikosas salas", - "assets.countries.TD": "Čada", - "assets.countries.TF": "Francijas Dienvidjūru teritorija", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Taizeme", - "assets.countries.TJ": "Tadžikistāna", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Austrumtimora", - "assets.countries.TM": "Turkmenistāna", - "assets.countries.TN": "Tunisija", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turcija", - "assets.countries.TT": "Trinidāda un Tobāgo", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taivāna", - "assets.countries.TZ": "Tanzānija", - "assets.countries.UA": "Ukraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "ASV Mazās Aizjūras Salas", - "assets.countries.US": "Amerikas Savienotās Valstis", - "assets.countries.UY": "Urugvaja", - "assets.countries.UZ": "Uzbekistāna", - "assets.countries.VA": "Vatikāns (Svētais Krēsls)", - "assets.countries.VC": "Sentvinsenta un Grenadīnas", - "assets.countries.VE": "Venecuēla", - "assets.countries.VG": "Britu Virdžīnas", - "assets.countries.VI": "ASV Virdžīnas", - "assets.countries.VN": "Vjetnama", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Volisa un Futunas salas", - "assets.countries.WS": "Rietumsamoa", - "assets.countries.YE": "Jemena", - "assets.countries.YT": "Majota", - "assets.countries.ZA": "Dienvidāfrika", - "assets.countries.ZM": "Zambija", - "assets.countries.ZW": "Zimbabve", - "assets.mimetypes.application/epub_zip": "EPUB e-grāmata", - "assets.mimetypes.application/msword": "Word dokuments", - "assets.mimetypes.application/pdf": "PDF dokuments", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle dublējums", - "assets.mimetypes.application/vnd.ms-excel": "Excel izklājlapa", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint prezentācija", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint prezentācija", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint slīdrāde", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel rēķintabula", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel veidne", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word dokuments", - "assets.mimetypes.archive": "Arhīvs ({{$a.EXT}})", - "assets.mimetypes.audio": "Audio fails ({{$a.EXT}})", - "assets.mimetypes.document/unknown": "fails", - "assets.mimetypes.group:sourcecode": "Izejas kods", - "assets.mimetypes.image": "Attēls ({{$a.MIMETYPE2}})", - "assets.mimetypes.text/csv": "Ar komatu atdalītas vērtības", - "assets.mimetypes.text/html": "HTML dokuments", - "assets.mimetypes.text/plain": "Teksta fails", - "assets.mimetypes.text/rtf": "RTF dokuments", - "core.accounts": "Konti", - "core.add": "Pievienot", - "core.ago": "Pirms {{$a}}", - "core.all": "Viss", - "core.allgroups": "Visas grupas", - "core.allparticipants": "Visi dalībnieki", - "core.answer": "Atbilde", - "core.answered": "Atbildēts", - "core.areyousure": "Vai esat pārliecināts?", - "core.back": "Atpakaļ", - "core.block.blocks": "Bloki", - "core.cancel": "Atcelt", - "core.cannotconnect": "Nevar izveidot savienojumu: pārbaudiet, vai esat pareizi ievadījis URL un ka jūsu vietne izmanto Moodle {{$a}} vai jaunāku versiju.", - "core.cannotdownloadfiles": "Failu lejupielāde ir atspējota. Lūdzu, sazinieties ar vietnes administratoru.", - "core.captureaudio": "Ierakstīt audio", - "core.capturedimage": "Uzņemtais attēls.", - "core.captureimage": "Uzņemt attēlu", - "core.capturevideo": "Ierakstīt video", - "core.category": "Kategorija", - "core.choose": "Izvēlēties", - "core.choosedots": "Izvēlēties ...", - "core.clearsearch": "Notīrīt meklēšanu", - "core.clicktohideshow": "Noklikšķināt, lai izvērstu vai savērstu", - "core.clicktoseefull": "Noklikšķiniet, lai redzētu pilnu saturu.", - "core.close": "Aizvērt", - "core.comments": "Komentāri", - "core.comments.addcomment": "Pievienot komentāru ...", - "core.comments.comments": "Komentāri", - "core.comments.commentscount": "Komentāri ({{$a}})", - "core.comments.eventcommentcreated": "Komentārs izveidots", - "core.comments.eventcommentdeleted": "Komentārs izdzēsts", - "core.comments.nocomments": "Nav komentāru", - "core.comments.savecomment": "Saglabāt komentāru", - "core.commentscount": "Komentāri ({{$a}})", - "core.completion-alt-auto-fail": "Izpildīta: {{$a}} (netika iegūts sekmīgs vērtējums)", - "core.completion-alt-auto-n": "Nav izpildīta: {{$a}}", - "core.completion-alt-auto-pass": "Izpildīta: {{$a}} (iegūts sekmīgs vērtējums)", - "core.completion-alt-auto-y": "Izpildīta: {{$a}}", - "core.completion-alt-auto-y-override": "Izpildīts: {{$a.modname}} (marķēja {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Nav izpildīts: {{$a}}. Izvēlieties, lai marķētu kā izpildītu.", - "core.completion-alt-manual-y": "Izpildīts: {{$a}}. Izvēlieties, lai marķētu kā neizpildītu.", - "core.completion-alt-manual-y-override": "Izpildīts: {{$a.modname}} (marķēja {{$a.overrideuser}}). Izvēlēties, lai atceltu izpildes marķējumu.", - "core.confirmcanceledit": "Vai tiešām vēlaties atstāt šo lapu? Visas izmaiņas tiks zaudētas.", - "core.confirmdeletefile": "Vai tiešām vēlaties dzēst šo failu?", - "core.confirmloss": "Vai tu esi pārliecināts? Visas izmaiņas tiks zaudētas.", - "core.confirmopeninbrowser": "Vai vēlaties to atvērt tīmekļa pārlūkprogrammā?", - "core.content": "Saturs", - "core.contenteditingsynced": "Rediģētais saturs ir sinhronizēts.", - "core.contentlinks.chooseaccount": "Izvēlēties kontu", - "core.contentlinks.chooseaccounttoopenlink": "Izvēlieties kontu, ar kuru atvērt saiti.", - "core.contentlinks.confirmurlothersite": "Šī saite pieder citai vietnei. Vai vēlaties to atvērt?", - "core.contentlinks.errornoactions": "Nevarēja atrast darbību, kas jāveic ar šo saiti.", - "core.contentlinks.errornosites": "Nevarēja atrast nevienu vietni, lai apstrādātu šo saiti.", - "core.contentlinks.errorredirectothersite": "Novirzīšanas URL nevar norādīt uz citu vietni.", - "core.continue": "Turpināt", - "core.copiedtoclipboard": "Teksts tiek kopēts starpliktuvē", - "core.course": "Kurss", - "core.course.activitydisabled": "Jūsu organizācija ir atspējojusi šo aktivitāti mobilajā lietotnē.", - "core.course.activitynotyetviewableremoteaddon": "Jūsu organizācija instalēja spraudni, kas vēl netiek atbalstīts.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Jūsu organizācijas Moodle instalācija ir jāatjaunina.", - "core.course.allsections": "Visas sekcijas", - "core.course.askadmintosupport": "Sazinieties ar vietnes administratoru un informējiet viņus, ka vēlaties izmantot šo aktivitāti, izmantojot Moodle Mobile lietotni.", - "core.course.availablespace": "Pašlaik jums ir aptuveni {{available}} brīvas vietas.", - "core.course.confirmdeletemodulefiles": "Vai tiešām vēlaties dzēst šos failus?", - "core.course.confirmdownload": "Jūs gatavojaties lejupielādēt {{size}}. {{AvailableSpace}} Vai tiešām vēlaties turpināt?", - "core.course.confirmdownloadunknownsize": "Nav iespējams aprēķināt lejupielādes lielumu. {{AvailableSpace}} Vai tiešām vēlaties turpināt?", - "core.course.confirmlimiteddownload": "Pašlaik neesat savienots ar Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Jūs gatavojaties lejupielādēt vismaz {{size}}. {{AvailableSpace}} Vai tiešām vēlaties turpināt?", - "core.course.contents": "Saturs", - "core.course.couldnotloadsectioncontent": "Nevarēja ielādēt sadaļas saturu. Lūdzu, pamēģiniet vēlreiz vēlāk.", - "core.course.couldnotloadsections": "Nevarēja ielādēt sadaļas. Lūdzu, pamēģiniet vēlreiz vēlāk.", - "core.course.coursesummary": "Kursa kopsavilkums", - "core.course.errordownloadingcourse": "Lejupielādējot kursu, radās kļūda.", - "core.course.errordownloadingsection": "Lejupielādējot sadaļu, radās kļūda.", - "core.course.errorgetmodule": "Kļūda, iegūstot aktivitātes datus.", - "core.course.hiddenfromstudents": "Paslēpts no studentiem", - "core.course.hiddenoncoursepage": "Pieejams, bet netiek rādīts kursa lapā", - "core.course.insufficientavailablequota": "Ierīce nevarēja piešķirt vietu šīs lejupielādes saglabāšanai. Tā var rezervēt vietu lietotnēm un sistēmas atjauninājumiem. Lūdzu, vispirms notīriet kādu uzglabāšanas vietu.", - "core.course.insufficientavailablespace": "Jūs mēģināt lejupielādēt {{size}}. Tas atstās ierīci ar nepietiekamu vietu, lai darbotos normāli. Lūdzu, vispirms notīriet kādu uzglabāšanas vietu.", - "core.course.manualcompletionnotsynced": "Manuāla pabeigšana nav sinhronizēta.", - "core.course.nocontentavailable": "Pašlaik nav pieejams nekāds saturs.", - "core.course.overriddennotice": "Šīs aktivitātes gala vērtējums tika manuāli koriģēts.", - "core.course.refreshcourse": "Atsvaidzināt kursu", - "core.course.sections": "Sadaļas", - "core.course.useactivityonbrowser": "Jūs joprojām varat to izmantot, izmantojot ierīces tīmekļa pārlūkprogrammu.", - "core.course.warningmanualcompletionmodified": "Vietnē tika mainīta aktivitātes manuāla pabeigšana.", - "core.course.warningofflinemanualcompletiondeleted": "Dažas kursa “{{name}}” bezsaistes manuālās pabeigšanas izdzēstas. {{kļūda}}", - "core.coursedetails": "Kursa detaļas", - "core.courses.addtofavourites": "Atzīmēt šo kursu", - "core.courses.allowguests": "Šis kurss ir pieejams vieslietotājiem", - "core.courses.availablecourses": "Pieejamie kursi", - "core.courses.cannotretrievemorecategories": "Kategorijas, kas ir dziļākas par līmeni {{$a}}, nevar izgūt.", - "core.courses.categories": "Kursu kategorijas", - "core.courses.confirmselfenrol": "Vai tiešām vēlaties reģistrēties šajā kursā?", - "core.courses.courses": "Kursi", - "core.courses.downloadcourses": "Lejupielādēt kursus", - "core.courses.enrolme": "Reģistrēt mani", - "core.courses.errorloadcategories": "Ielādējot kategorijas, radās kļūda.", - "core.courses.errorloadcourses": "Ielādējot kursus, radās kļūda.", - "core.courses.errorloadplugins": "Šim kursam nepieciešamos spraudņus nevar ielādēt pareizi. Lūdzu, restartējiet lietotni, lai mēģinātu vēlreiz.", - "core.courses.errorsearching": "Meklējot, radās kļūda.", - "core.courses.errorselfenrol": "Pašreģistrēšanās laikā radās kļūda.", - "core.courses.filtermycourses": "Filtrēt manus kursus", - "core.courses.frontpage": "Titullapa", - "core.courses.hidecourse": "Paslēpt no skata", - "core.courses.ignore": "Ignorēt", - "core.courses.mycourses": "Mani kursi", - "core.courses.mymoodle": "Infopanelis", - "core.courses.nocourses": "Nav nekādas informācijas par kursiem, ko parādīt.", - "core.courses.nocoursesyet": "Šajā kategorijā nav neviena kursa.", - "core.courses.nosearchresults": "Nav rezultātu", - "core.courses.notenroled": "Jūs neesat reģistrēts šajā kursā", - "core.courses.notenrollable": "Jūs nevarat reģistrēties šajā kursā.", - "core.courses.password": "Reģistrācijas atslēga", - "core.courses.paymentrequired": "Lai piedalītos šajā kursā, nepieciešams maksājums.", - "core.courses.paypalaccepted": "PayPal maksājumi akceptēti", - "core.courses.reload": "Atkārtoti ielādēt", - "core.courses.removefromfavourites": "Noņemt atzīmējumu šim kursam", - "core.courses.search": "Meklēt", - "core.courses.searchcourses": "Meklēt kursus", - "core.courses.searchcoursesadvice": "Jūs varat izmantot pogu Meklēt kursus, lai atrast kursus, kuriem piekļūt kā viesis vai reģistrēties kursos, kuros tas atļauts.", - "core.courses.selfenrolment": "Pašreģistrācija", - "core.courses.sendpaymentbutton": "Nosūtīt maksājumu, izmantojot PayPal", - "core.courses.show": "Parādīt šo kursu", - "core.courses.totalcoursesearchresults": "Kopējais kursu skaits: {{$a}}", - "core.currentdevice": "Pašreizējā ierīce", - "core.datastoredoffline": "Ierīcē saglabātie dati, jo tos nevarēja nosūtīt. Tie tiks nosūtīts automātiski vēlāk.", - "core.date": "Datums", - "core.day": "diena", - "core.days": "dienas", - "core.decsep": ",", - "core.defaultvalue": "Noklusējuma ({{$a}})", - "core.delete": "Dzēst", - "core.deletedoffline": "Izdzēsts bezsaistē", - "core.deleting": "Notiek dzēšana", - "core.description": "Apraksts", - "core.dfdaymonthyear": "DD-MM-YYYY", - "core.discard": "Atmest", - "core.dismiss": "Atlaist", - "core.displayoptions": "Parādīšanas opcijas", - "core.done": "Gatavs", - "core.download": "Lejupielādēt", - "core.downloaded": "Lejupielādēts", - "core.downloading": "Notiek lejupielāde", - "core.edit": "Rediģēt", - "core.editor.autosavesucceeded": "Melnraksts saglabāts.", - "core.editor.bold": "Treknraksts", - "core.editor.clear": "Attīrīt formatējumu", - "core.editor.h3": "Virsraksts (liels)", - "core.editor.h4": "Virsraksts (vidējs)", - "core.editor.h5": "Virsraksts (mazs)", - "core.editor.italic": "Slīpraksts", - "core.editor.orderedlist": "Numurēts saraksts", - "core.editor.p": "Rindkopa", - "core.editor.strike": "Pārsvītrojums", - "core.editor.underline": "Pasvītrojums", - "core.editor.unorderedlist": "Aizzīmju saraksts", - "core.emptysplit": "Šī lapa būs tukša, ja kreisais panelis ir tukšs vai tiek ielādēts.", - "core.error": "Kļūda", - "core.errorchangecompletion": "Mainot pabeigšanas statusu, radās kļūda. Lūdzu mēģiniet vēlreiz.", - "core.errordeletefile": "Dzēšot failu, radās kļūda. Lūdzu mēģiniet vēlreiz.", - "core.errordownloading": "Lejupielādējot failu, radās kļūda.", - "core.errordownloadingsomefiles": "Lejupielādējot failus, radās kļūda. Daži faili var nebūt pieejami.", - "core.errorfileexistssamename": "Fails ar šādu nosaukumu jau pastāv.", - "core.errorinvalidform": "Veidlapā ir nederīgi dati. Lūdzu, pārbaudiet, vai visi nepieciešamie lauki ir aizpildīti un vai dati ir derīgi.", - "core.errorinvalidresponse": "Saņemta nederīga atbilde. Ja kļūda saglabājas, lūdzu, sazinieties ar vietnes administratoru.", - "core.errorloadingcontent": "Ielādējot saturu, radās kļūda.", - "core.errorofflinedisabled": "Bezsaistes pārlūkošana jūsu vietnē ir atspējota. Lai izmantotu lietotni, jums jābūt savienotam ar internetu.", - "core.erroropenfilenoapp": "Atverot failu, radās kļūda: neviena lietotne netika atklāta, lai atvērtu šāda veida failu.", - "core.erroropenfilenoextension": "Atverot failu, radās kļūda: failam nav paplašinājuma.", - "core.erroropenpopup": "Šī darbība mēģina atvērt uznirstošo logu. Tas netiek atbalstīts lietotnē.", - "core.errorrenamefile": "Pārsaucot failu, radās kļūda. Lūdzu mēģiniet vēlreiz.", - "core.errorsync": "Sinhronizācijas laikā radās kļūda. Lūdzu mēģiniet vēlreiz.", - "core.errorsyncblocked": "Šo {{$a}} nevar sinhronizēt pašlaik notiekošā procesa dēļ. Lūdzu, mēģiniet vēlreiz vēlāk. Ja problēma saglabājas, mēģiniet pārstartēt lietotni.", - "core.favourites": "Atzīmēts", - "core.filename": "Faila nosaukums", - "core.filenameexist": "Faila nosaukums jau pastāv: {{$a}}", - "core.filenotfound": "Atvainojiet, fails nav atrasts.", - "core.fileuploader.addfiletext": "Pievienot failu", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Jūs gatavojaties augšupielādēt {{size}}. Vai tiešām vēlaties turpināt?", - "core.fileuploader.confirmuploadunknownsize": "Nav iespējams aprēķināt augšupielādes lielumu. Vai tiešām vēlaties turpināt?", - "core.fileuploader.errorcapturingaudio": "Kļūda ierakstot audio.", - "core.fileuploader.errorcapturingimage": "Notverot attēlu, radās kļūda.", - "core.fileuploader.errorcapturingvideo": "Notverot video, radās kļūda.", - "core.fileuploader.errorgettingimagealbum": "Kļūda, iegūstot attēlu no albuma.", - "core.fileuploader.errormustbeonlinetoupload": "Lai augšupielādētu failus, jums jābūt tiešsaistē.", - "core.fileuploader.errornoapp": "Jums nav instalēta lietotne, lai veiktu šo darbību.", - "core.fileuploader.errorreadingfile": "Lasot failu, radās kļūda.", - "core.fileuploader.errorwhileuploading": "Faila augšupielādes laikā radās kļūda.", - "core.fileuploader.file": "Fails", - "core.fileuploader.filesofthesetypes": "Akceptētie failu tipi:", - "core.fileuploader.fileuploaded": "Fails tika veiksmīgi augšupielādēts.", - "core.fileuploader.maxbytesfile": "Fails {{$a.file}} ir pārāk liels. Maksimālais lielums, ko varat augšupielādēt, ir {{$a.size}}.", - "core.fileuploader.more": "Vēl", - "core.fileuploader.photoalbums": "Fotoalbumi", - "core.fileuploader.readingfile": "Lasa failu", - "core.fileuploader.readingfileperc": "Lasa failu: {{$a}}%", - "core.fileuploader.selectafile": "Atlasīt failu", - "core.fileuploader.uploadafile": "Augšupielādēt failu", - "core.fileuploader.uploading": "Notiek augšupielāde", - "core.fileuploader.uploadingperc": "Notiek augšupielāde: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filtrs", - "core.folder": "Mape", - "core.forcepasswordchangenotice": "Lai turpinātu, jums ir jāmaina sava parole.", - "core.fulllistofcourses": "Visi kursi", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Vidējais", - "core.grades.badgrade": "Ievadītais vērtējums ir nederīgs", - "core.grades.contributiontocoursetotal": "Ieguldījums kursa kopējā vērtējumā", - "core.grades.feedback": "Atsauksme", - "core.grades.grade": "Vērtējums", - "core.grades.gradeitem": "Vērtējumu vienība", - "core.grades.grades": "Vērtējumi", - "core.grades.lettergrade": "Burtu vērtējums", - "core.grades.nogradesreturned": "Nav atgriezts neviens vērtējums", - "core.grades.nooutcome": "Nav rezultāta", - "core.grades.percentage": "Procenti", - "core.grades.range": "Diapazons", - "core.grades.rank": "Rangs", - "core.grades.weight": "svarīgums", - "core.group": "Grupa", - "core.groupsseparate": "Atsevišķas grupas", - "core.groupsvisible": "Redzamās grupas", - "core.hasdatatosync": "Šajā {{$a}} ir bezsaistes dati, kas jāsinhronizē.", - "core.help": "Palīdzība", - "core.hide": "Paslēpt", - "core.hour": "stunda", - "core.hours": "stundas", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Attēls", - "core.imageviewer": "Attēlu skatītājs", - "core.info": "Informācija", - "core.labelsep": ":", - "core.lastaccess": "Iepriekšējā piekļuve", - "core.lastdownloaded": "Pēdējā lejupielāde", - "core.lastmodified": "Pēdējās izmaiņas", - "core.lastsync": "Pēdējā sinhronizācija", - "core.layoutgrid": "Režģis", - "core.list": "Saraksts", - "core.listsep": ";", - "core.loading": "Notiek ielāde", - "core.loadmore": "Ielādēt vairāk", - "core.location": "Atrašanās vieta", - "core.login.auth_email": "Pašreģistrācija, izmantojot e-pastu", - "core.login.authenticating": "Autentificēšana", - "core.login.cancel": "Atcelt", - "core.login.changepassword": "Mainīt paroli", - "core.login.confirmdeletesite": "Vai tiešām vēlaties dzēst vietni {{sitename}}?", - "core.login.connect": "Savienoties!", - "core.login.connecttomoodle": "Savienoties ar Moodle", - "core.login.contactyouradministrator": "Lai saņemtu papildu palīdzību, sazinieties ar vietnes administratoru.", - "core.login.contactyouradministratorissue": "Lūdziet vietnes administratoram pārbaudīt šādu problēmu: {{$ a}}", - "core.login.createaccount": "Izveidot manu jauno kontu", - "core.login.createuserandpass": "Izvēlēties savu lietotājvārdu un paroli", - "core.login.credentialsdescription": "Lūdzu, norādiet savu lietotājvārdu un paroli, lai pieteiktos.", - "core.login.emailconfirmsent": "

                Uz jūsu adresi {{$a}} jābūt nosūtītam e-pasta ziņojumam.

                \n

                Tajā ir iekļauti vienkārši norādījumi par reģistrācijas pabeigšanu.

                \n

                Ja rodas kādas problēmas, sazinieties ar vietnes administratoru.

                ", - "core.login.emailconfirmsentnoemail": "

                Uz jūsu adresi jābūt nosūtītam e-pastam.

                Tas satur vienkāršas instrukcijas, lai pabeigtu reģistrāciju.

                Ja jums joprojām ir grūtības, sazinieties ar vietnes administratoru. p>", - "core.login.emailnotmatch": "E-pasti neatbilst", - "core.login.erroraccesscontrolalloworigin": "Zvans, kuru mēģināt veikt, ir noraidīts. Lūdzu pārbaudiet https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Dzēšot šo vietni, radās kļūda. Lūdzu mēģiniet vēlreiz.", - "core.login.errorupdatesite": "Atjauninot vietnes marķieri, radās kļūda.", - "core.login.findyoursite": "Atrodiet savu vietni", - "core.login.firsttime": "Vai esat šeit pirmoreiz?", - "core.login.forcepasswordchangenotice": "Lai turpinātu, jums ir jāmaina sava parole.", - "core.login.forgotten": "Vai aizmirsāt savu lietotājvārdu vai paroli?", - "core.login.help": "Palīdzība", - "core.login.helpmelogin": "

                Pasaulē ir daudz tūkstošu Moodle vietņu. Šī lietotne var izveidot savienojumu tikai ar Moodle vietnēm, kas ir īpaši iespējojušas mobilo lietotņu piekļuvi.

                Ja nevarat izveidot savienojumu ar savu Moodle vietni, jums ir jāsazinās ar vietnes administratoru un jāpieprasa lasīt http://docs.moodle.org/en/Mobile_app

                Lai pārbaudītu lietotni Moodle demonstrācijas vietnē, laukā Vietnes adrese ierakstiet teacher vai student un noklikšķiniet uz pogas Savienot . ", - "core.login.instructions": "Norādījumi", - "core.login.invalidaccount": "Lūdzu, pārbaudiet savu pieteikšanās informāciju vai lūdziet vietnes administratoram pārbaudīt vietnes konfigurāciju.", - "core.login.invaliddate": "Nederīgs datums", - "core.login.invalidemail": "Nederīga e-pasta adrese", - "core.login.invalidmoodleversion": "Nederīga Moodle versija. Nepieciešamā minimālā versija ir {{$a}}.", - "core.login.invalidsite": "Vietnes URL nav derīgs.", - "core.login.invalidtime": "Nederīgs laiks", - "core.login.invalidurl": "Norādīta nederīga saite", - "core.login.invalidvaluemax": "Maksimālā vērtība ir {{$a}}", - "core.login.invalidvaluemin": "Minimālā vērtība ir {{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle Mobile papildu funkciju pārbaude atgriezās negaidīti. Jūs autentificēs, izmantojot standarta mobilo pakalpojumu.", - "core.login.loggedoutssodescription": "Jums vēlreiz jāautentificējas. Jums ir jāpiesakās vietnē pārlūkprogrammas logā.", - "core.login.login": "Pieslēgties", - "core.login.loginbutton": "Pieteikties", - "core.login.logininsiterequired": "Jums ir jāpiesakās vietnē pārlūkprogrammas logā.", - "core.login.loginsteps": "Sveiki! Lai gūtu pilnu piekļuvi kursiem, veltiet\n minūti laika, lai šajā tīmekļa vietnē sev izveidotu jaunu kontu.\n Katram atsevišķajam kursam var būt arī vienreizēja\n “reģistrācijas atslēga”, kas būs nepieciešama tikai vēlāk. Lūk,\n veicamās darbības:\n

                  \n
                1. Jauna konta veidlapā norādiet savas informāciju par sevi.
                2. \n
                3. Uz jūsu e-pasta adresi nekavējoties tiks nosūtīts e-pasta ziņojums.
                4. \n
                5. Izlasiet e-pasta ziņojumu un noklikšķiniet uz tajā iekļautās tīmekļa saites.
                6. \n
                7. Jūsu konts tiks apstiprināts, un notiks jūsu pieslēgšanās.
                8. \n
                9. Tagad izvēlieties kursu, kurā vēlaties piedalīties.
                10. \n
                11. Ja tiek prasīta “reģistrācijas atslēga” — izmantojiet to,\n kuru jums iedevis pasniedzējs. Ar šo tiksit “reģistrēts” atbilstošajā\n kursā.
                12. \n
                13. Tagad varat piekļūt pilnam kursam. Turpmāk būs tikai\n jāievada savs lietotājvārds un parole (šīs lapas veidlapā),\n lai pieslēgtos un piekļūtu visiem kursiem, kuros esat reģistrējies.
                14. \n
                ", - "core.login.missingemail": "Trūkst e-pasta adreses", - "core.login.missingfirstname": "Trūkst vārda", - "core.login.missinglastname": "Trūkst uzvārda", - "core.login.mobileservicesnotenabled": "Mobilā piekļuve jūsu vietnē nav iespējota. Ja domājat, ka tas būtu iespējots, lūdzu, sazinieties ar vietnes administratoru.", - "core.login.mustconfirm": "Jums ir jāapstiprina sava pieslēgšanās.", - "core.login.newaccount": "Jauns konts", - "core.login.notloggedin": "Jums nepieciešams pieteikties.", - "core.login.password": "Parole", - "core.login.passwordforgotten": "Aizmirsta parole", - "core.login.passwordforgotteninstructions2": "Lai atiestatītu jūsu paroli, lūdzu zemāk iesniedziet jūsu lietotāja vārdu un e-pasta adresi. Ja izdosies jūs atrast datubāzē, uz jūsu e-pasta adresi tiks nosūtīts e-pasts ar norādījumiem, kā atgūt piekļuvi.", - "core.login.passwordrequired": "Nepieciešama parole", - "core.login.policyaccept": "Es saprotu un piekrītu", - "core.login.policyagree": "Lai turpinātu šīs vietnes izmantošanu, jums ir jāpiekrīt šai politikai. Vai piekrītat?", - "core.login.policyagreement": "Vietnes politikas līgums", - "core.login.policyagreementclick": "Noklikšķiniet šeit, lai izlasītu vietnes politikas līgumu", - "core.login.profileinvaliddata": "Nederīga vērtība", - "core.login.recaptchachallengeimage": "reCAPTCHA izaicinājums", - "core.login.recaptchaexpired": "Pārbaudes termiņš beidzies. Atbildiet uz drošības jautājumu vēlreiz.", - "core.login.recaptchaincorrect": "Drošības jautājuma atbilde ir nepareiza.", - "core.login.reconnect": "Atjaunot savienojumu", - "core.login.reconnectdescription": "Jūsu autentifikācijas marķieris ir nederīgs vai ir beidzies derīguma termiņš. Jums ir jāatjauno savienojums ar vietni.", - "core.login.reconnectssodescription": "Jūsu autentifikācijas marķieris ir nederīgs vai ir beidzies derīguma termiņš. Jums ir jāatjauno savienojums ar vietni. Jums ir jāpiesakās vietnē pārlūkprogrammas logā.", - "core.login.searchby": "Meklēt pēc:", - "core.login.selectacountry": "Izvēlēties valsti", - "core.login.selectsite": "Lūdzu, atlasiet savu vietni:", - "core.login.signupplugindisabled": "{{$a}} nav iespējota.", - "core.login.siteaddress": "Vietnes adrese", - "core.login.sitehasredirect": "Jūsu vietnē ir vismaz viena HTTP novirzīšana. Lietotne nevar sekot novirzīšanai un tā varētu būt problēma, kas liedz lietotnei izveidot savienojumu ar jūsu vietni.", - "core.login.siteinmaintenance": "Jūsu vietne ir uzturēšanas režīmā", - "core.login.sitepolicynotagreederror": "Vietnes politika nav saskaņota.", - "core.login.siteurl": "Vietnes URL", - "core.login.siteurlrequired": "Nepieciešams vietnes URL, piemēram, http://www.yourmoodlesite.org ", - "core.login.startsignup": "Izveidot jaunu kontu", - "core.login.stillcantconnect": "Joprojām nevar izveidot savienojumu?", - "core.login.supplyinfo": "Detalizētāk", - "core.login.username": "Lietotājvārds", - "core.login.usernameoremail": "Ievadīt lietotājvārdu vai e-pasta adresi.", - "core.login.usernamerequired": "Vajadzīgs lietotājvārds", - "core.login.usernotaddederror": "Lietotājs nav pievienots - kļūda", - "core.login.visitchangepassword": "Vai vēlaties apmeklēt vietni, lai mainītu paroli?", - "core.login.webservicesnotenabled": "Tīmekļa pakalpojumi nav iespējoti jūsu vietnē. Lūdzu, sazinieties ar vietnes administratoru, ja domājat, ka tie ir jāiespējo.", - "core.lostconnection": "Jūsu autentifikācijas marķieris ir nederīgs vai ir beidzies derīguma termiņš. Jums būs jāatjauno savienojums ar vietni.", - "core.mainmenu.changesite": "Mainīt vietni", - "core.mainmenu.help": "Palīdzība", - "core.mainmenu.logout": "Atslēgties", - "core.mainmenu.website": "Tīmekļa vietne", - "core.maxsizeandattachments": "Maksimālais jaunu failu lielums: {{$a.size}}, pielikumu skaits {{$a.attachments}}", - "core.min": "min", - "core.mins": "min.", - "core.misc": "Dažādi", - "core.mod_assign": "Uzdevums", - "core.mod_assignment": "Uzdevums 2.2 (Atspējots)", - "core.mod_book": "Grāmata", - "core.mod_chat": "Tērzēšana", - "core.mod_choice": "Izvēle", - "core.mod_data": "Datubāze", - "core.mod_database": "Datubāze", - "core.mod_external-tool": "LTI", - "core.mod_feedback": "Atsauksme", - "core.mod_file": "Fails", - "core.mod_folder": "Mape", - "core.mod_forum": "Forums", - "core.mod_glossary": "Vārdnīca", - "core.mod_ims": "IMS satura pakotne", - "core.mod_imscp": "IMS satura pakotne", - "core.mod_label": "Uzraksts", - "core.mod_lesson": "Nodarbība", - "core.mod_lti": "LTI", - "core.mod_page": "Lapa", - "core.mod_quiz": "Tests", - "core.mod_resource": "Resurss", - "core.mod_scorm": "SCORM pakotne", - "core.mod_survey": "Anketa", - "core.mod_url": "URL", - "core.mod_wiki": "Viki", - "core.mod_workshop": "Seminārs", - "core.moduleintro": "Apraksts", - "core.more": "vēl", - "core.name": "Nosaukums", - "core.networkerroriframemsg": "Šis saturs nav pieejams bezsaistē. Lūdzu, izveidojiet savienojumu ar internetu un mēģiniet vēlreiz.", - "core.networkerrormsg": "Pievienojoties vietnei, radās problēma. Lūdzu, pārbaudiet savienojumu un mēģiniet vēlreiz.", - "core.never": "Nekad", - "core.next": "Nākamais", - "core.no": "Nē", - "core.nocomments": "Nav komentāru", - "core.nograde": "Vērtējumu nav", - "core.none": "Nav", - "core.nopasswordchangeforced": "Jūs nevarat turpināt, nemainot paroli.", - "core.nopermissionerror": "Diemžēl jums pašlaik nav atļauju to darīt", - "core.nopermissions": "Atvainojiet, bet pašlaik jums nav pilnvaru šīs darbības veikšanai ({{$a}})", - "core.noresults": "Nav rezultātu", - "core.noselection": "Nekas nav izvēlēts", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Šis profils nav pieejams, jo lietotājs nav reģistrējies šajā kursā.", - "core.notice": "Paziņojums", - "core.notingroup": "Atvainojiet, bet, lai skatītu šo aktivitāti, jums jābūt iekļautam grupā.", - "core.notsent": "Nav nosūtīts", - "core.now": "tagad", - "core.numwords": "{{$a}} vārdi", - "core.offline": "Bezsaistē", - "core.ok": "Labi", - "core.online": "Tiešsaistē", - "core.openfullimage": "Noklikšķiniet šeit, lai parādītu pilna izmēra attēlu", - "core.openinbrowser": "Atvērt pārlūkprogrammā", - "core.othergroups": "Citas grupas", - "core.pagea": "Lapa {{$a}}", - "core.paymentinstant": "Izmantojiet pieejamo pogu, lai dažu minūšu laikā veiktu apmaksu un tiktu reģistrēts!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Tālrunis", - "core.pictureof": "{{$a}} attēls", - "core.previous": "Iepriekšējais", - "core.pulltorefresh": "Pavelciet, lai atsvaidzinātu", - "core.question.answer": "Atbilde", - "core.question.answersaved": "Atbilde saglabāta", - "core.question.cannotdeterminestatus": "Nevar noteikt statusu", - "core.question.complete": "Pabeigt", - "core.question.correct": "Pareizs", - "core.question.errorattachmentsnotsupported": "Lietotne vēl neatbalsta failu pievienošanu atbildēm.", - "core.question.errorinlinefilesnotsupported": "Lietotne vēl neatbalsta inline failu rediģēšanu.", - "core.question.errorquestionnotsupported": "Šī lietotne neatbalsta šo jautājumu veidu: {{$a}}.", - "core.question.feedback": "Atsauksme", - "core.question.howtodraganddrop": "Pieskarieties, lai izvēlētos, pēc tam pieskarieties, lai nomestu.", - "core.question.incorrect": "Nepareizs", - "core.question.information": "Informācija", - "core.question.invalidanswer": "Nepilnīga atbilde", - "core.question.notanswered": "Nav atbildēts", - "core.question.notyetanswered": "Nav vēl atbildēts", - "core.question.partiallycorrect": "Daļēji pareizs", - "core.question.questionmessage": "Jautājums {{$a}}: {{$b}}", - "core.question.questionno": "Jautājums {{$a}}", - "core.question.requiresgrading": "Nepieciešams vērtējums", - "core.quotausage": "Jūs šobrīd izmantojat {{$a.used}} no kopējā limita {{$a.total}}.", - "core.rating.aggregateavg": "Vidējais vērtējums", - "core.rating.aggregatecount": "Vērtējumu skaits", - "core.rating.aggregatesum": "Vērtējumu summa", - "core.rating.noratings": "Nav iesniegtu vērtējumu", - "core.rating.rating": "Vērtējums", - "core.rating.ratings": "Vērtējumi", - "core.redirectingtosite": "Jūs tiksiet novirzīts uz vietni.", - "core.refresh": "Atjaunot", - "core.remove": "Noņemt", - "core.required": "Nepieciešams", - "core.requireduserdatamissing": "Šim lietotājam nav nepieciešamo profila datu. Lūdzu, ievadiet datus savā vietnē un mēģiniet vēlreiz.
                {{$a}}", - "core.resourcedisplayopen": "Atvērt", - "core.resources": "Resursi", - "core.restore": "Atjaunot", - "core.restricted": "Ierobežots", - "core.retry": "Mēģiniet vēlreiz", - "core.save": "Saglabāt", - "core.savechanges": "Saglabāt izmaiņas", - "core.search": "Meklēt", - "core.searching": "Meklē", - "core.searchresults": "Meklēšanas rezultāti", - "core.sec": "sek.", - "core.secs": "sek.", - "core.seemoredetail": "Noklikšķiniet šeit, lai skatītu detalizētu informāciju", - "core.selectacategory": "Lūdzu, izvēlieties kategoriju", - "core.selectacourse": "Izvēlieties kursu", - "core.selectagroup": "Izvēlieties grupu", - "core.send": "Sūtīt", - "core.sending": "Sūtīšana", - "core.settings.about": "Par", - "core.settings.cannotsyncoffline": "Nevar sinhronizēt bezsaistē.", - "core.settings.cannotsyncwithoutwifi": "Nevar sinhronizēt, jo pašreizējie iestatījumi ļauj sinhronizēt tikai tad, kad ir izveidots savienojums ar Wi-Fi. Lūdzu, izveidojiet savienojumu ar Wi-Fi tīklu.", - "core.settings.compilationinfo": "Kompilācijas informācija", - "core.settings.cordovadevicemodel": "Cordova ierīces modelis", - "core.settings.cordovadeviceosversion": "Cordova ierīces OS versija", - "core.settings.cordovadeviceplatform": "Cordova ierīces platforma", - "core.settings.cordovadeviceuuid": "Cordova ierīces UUID", - "core.settings.cordovaversion": "Cordova versija", - "core.settings.currentlanguage": "Pašreizējā valoda", - "core.settings.debugdisplay": "Rādīt atkļūdošanas ziņojumus", - "core.settings.debugdisplaydescription": "Ja iespējots, kļūdas modāli parādīs vairāk datu par kļūdu, ja iespējams.", - "core.settings.deletesitefiles": "Vai tiešām vēlaties dzēst lejupielādētos failus un kešatmiņā saglabātos datus no vietnes {{sitename}} ”? Jūs nevarēsit lietot lietotni bezsaistes režīmā.", - "core.settings.deletesitefilestitle": "Dzēst vietnes failus", - "core.settings.deviceinfo": "Ierīces informācija", - "core.settings.deviceos": "Ierīces operētājsistēma", - "core.settings.disableall": "Uz laiku atslēgt paziņojumus", - "core.settings.disabled": "Atspējots", - "core.settings.displayformat": "Displeja formāts", - "core.settings.enabledownloadsection": "Iespējot lejupielādes sadaļas", - "core.settings.enablerichtexteditor": "Iespējot teksta redaktoru", - "core.settings.enablerichtexteditordescription": "Ja iespējots, ievadot saturu, būs pieejams teksta redaktors.", - "core.settings.enablesyncwifi": "Atļaut sinhronizāciju tikai tad, ja ir Wi-Fi", - "core.settings.entriesincache": "{{$a}} ieraksti kešatmiņā", - "core.settings.errordeletesitefiles": "Dzēšot vietnes failus, radās kļūda.", - "core.settings.errorsyncsite": "Sinhronizējot vietnes datus, radās kļūda. Lūdzu, pārbaudiet savu interneta savienojumu un mēģiniet vēlreiz.", - "core.settings.estimatedfreespace": "Paredzamā brīvā vieta", - "core.settings.filesystemroot": "Failu sistēmas sakne", - "core.settings.general": "Vispārējs", - "core.settings.language": "Valoda", - "core.settings.license": "Licence", - "core.settings.localnotifavailable": "Pieejami vietējie paziņojumi", - "core.settings.locationhref": "Tīmekļa skata URL", - "core.settings.locked": "Slēgts", - "core.settings.loggedin": "Tiešsaistē", - "core.settings.loggedoff": "Bezsaistē", - "core.settings.navigatorlanguage": "Navigatora valoda", - "core.settings.navigatoruseragent": "Navigatora userAgent", - "core.settings.networkstatus": "Interneta savienojuma statuss", - "core.settings.preferences": "Iestatījumi", - "core.settings.privacypolicy": "Privātuma politika", - "core.settings.pushid": "Push paziņojumu ID", - "core.settings.reportinbackground": "Ziņot par kļūdām automātiski", - "core.settings.settings": "Iestatījumi", - "core.settings.showdownloadoptions": "Rādīt lejupielādes iespējas", - "core.settings.sites": "Vietnes", - "core.settings.spaceusage": "Vietas izmantošana", - "core.settings.synchronization": "Sinhronizācija", - "core.settings.synchronizenow": "Sinhronizēt tagad", - "core.settings.syncsettings": "Sinhronizācijas iestatījumi", - "core.settings.total": "Kopā", - "core.settings.wificonnection": "Wi-Fi savienojums", - "core.sharedfiles.chooseaccountstorefile": "Izvēlieties kontu, kurā saglabāt failu.", - "core.sharedfiles.chooseactionrepeatedfile": "Fails ar šādu nosaukumu jau pastāv. Vai vēlaties nomainīt esošo failu vai pārdēvēt to par \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Nav saglabāta neviena vietne. Pirms koplietojat failu ar lietotni, lūdzu, pievienojiet vietni.", - "core.sharedfiles.nosharedfiles": "Šajā vietnē nav koplietotu failu.", - "core.sharedfiles.nosharedfilestoupload": "Šeit nav augšupielādējamu failu. Ja vēlaties augšupielādēt failu no citas lietotnes, atrodiet failu un noklikšķiniet uz pogas Atvērt.", - "core.sharedfiles.rename": "Pārsaukt", - "core.sharedfiles.replace": "Aizvietot", - "core.sharedfiles.sharedfiles": "Koplietotie faili", - "core.sharedfiles.successstorefile": "Fails veiksmīgi saglabāts. Atlasiet failu, ko augšupielādējat savos privātajos failos vai izmantojiet darbībā.", - "core.show": "Rādīt", - "core.showless": "Rādīt mazāk...", - "core.showmore": "Rādīt vairāk...", - "core.site": "Vietne", - "core.sitehome.sitehome": "Vietnes sākums", - "core.sitehome.sitenews": "Vietnes jaunumi", - "core.sitemaintenance": "Vietnē notiek uzturēšanas darbi, un šobrīd tā nav pieejama", - "core.sizeb": "baiti", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Izlaist", - "core.sorry": "Atvainojiet ...", - "core.sort": "Sakārtot", - "core.sortby": "Kārtot pēc", - "core.strftimedate": "%Y. gada %d. %B", - "core.strftimedateshort": "%d. %B", - "core.strftimedatetime": "%Y. gada %d. %B, %H:%M", - "core.strftimedatetimeshort": "%d.%m.%Y %H:%M", - "core.strftimedaydate": "%A, %Y. gada %d. %B", - "core.strftimedaydatetime": "%A, %Y. gada %d. %B, %H:%M", - "core.strftimedayshort": "%A, %d. %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d. %b., %H:%M", - "core.strftimerecentfull": "%a, %Y. gada %d. %b., %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "Iesniegt", - "core.success": "Sekmīgi", - "core.tablet": "Planšete", - "core.tag.defautltagcoll": "Noklusējuma kolekcija", - "core.tag.inalltagcoll": "Visur", - "core.tag.itemstaggedwith": "{{$a.tagarea}} tagots ar \"{{$a.tag}}\"", - "core.tag.noresultsfor": "\"{{$a}}\" nav rezultātu", - "core.tag.notagsfound": "Nav atrasti tagi, kas atbilstu \"{{$a}}\"", - "core.tag.searchtags": "Meklēt tagus", - "core.tag.showingfirsttags": "Rāda {{$a}} populārākos tagus", - "core.tag.tag": "Tags", - "core.tag.tagarea_course": "Kursi", - "core.tag.tagarea_course_modules": "Aktivitātes un resursi", - "core.tag.tagarea_post": "Blogu ieraksti", - "core.tag.tagarea_user": "Lietotāju intereses", - "core.tag.tags": "Tagi", - "core.teachers": "Pasniedzēji", - "core.thereisdatatosync": "Sinhronizācijai ir bezsaistē {{$a}}.", - "core.thisdirection": "ltr", - "core.time": "Laiks", - "core.timesup": "Laiks beidzies!", - "core.today": "Šodien", - "core.tryagain": "Mēģiniet vēlreiz", - "core.twoparagraphs": "{{p1}}

                {{p2}}", - "core.uhoh": "Ak, vai!", - "core.unexpectederror": "Negaidīta kļūda. Lūdzu, aizveriet un atveriet lietotni un mēģiniet vēlreiz.", - "core.unicodenotsupported": "Šajā vietnē netiek atbalstīti daži emoļi. Šādas rakstzīmes tiks noņemtas, kad tiks nosūtīts ziņojums.", - "core.unicodenotsupportedcleanerror": "Tīrot Unicode rakstzīmes tika atrasts tukšs teksts.", - "core.unknown": "Nezināms", - "core.unlimited": "Neierobežots", - "core.unzipping": "Atzipo", - "core.user": "Lietotājs", - "core.user.address": "Adrese", - "core.user.city": "Pilsēta", - "core.user.contact": "Kontakti", - "core.user.country": "Valsts", - "core.user.description": "Apraksts", - "core.user.detailsnotavailable": "Šī lietotāja dati jums nav pieejami.", - "core.user.editingteacher": "Pasniedzējs", - "core.user.email": "E-pasta adrese", - "core.user.emailagain": "E-pasts (vēlreiz)", - "core.user.errorloaduser": "Ielādējot lietotāju, radās kļūda.", - "core.user.firstname": "Vārds", - "core.user.interests": "Intereses", - "core.user.lastname": "Uzvārds", - "core.user.manager": "Pārvaldnieks", - "core.user.newpicture": "Jauns attēls", - "core.user.participants": "Dalībnieki", - "core.user.phone1": "Tālrunis", - "core.user.phone2": "Mobilais telefons", - "core.user.roles": "Lomas", - "core.user.sendemail": "E-pasts", - "core.user.student": "Students", - "core.user.teacher": "Nerediģējošs pasniedzējs", - "core.user.webpage": "Tīmekļa lapa", - "core.userdeleted": "Šis lietotāja konts ir izdzēsts", - "core.userdetails": "Detalizēts", - "core.users": "Lietotāji", - "core.view": "Skatīt", - "core.viewcode": "Skatīt kodu", - "core.vieweditor": "Skatīt redaktoru", - "core.viewembeddedcontent": "Skatīt iegulto saturu", - "core.viewprofile": "Skatīt profilu", - "core.warningofflinedatadeleted": "Bezsaistes dati no {{component}} '{{name}}' ir izdzēsti. {{kļūda}}", - "core.whoops": "Hmm!", - "core.whyisthishappening": "Kāpēc tas notiek?", - "core.wsfunctionnotavailable": "Tīmekļa pakalpojumu funkcija nav pieejama.", - "core.year": "gads", - "core.years": "gadi", - "core.yes": "Jā" -} \ No newline at end of file diff --git a/src/assets/lang/mn.json b/src/assets/lang/mn.json deleted file mode 100644 index 378f130e1..000000000 --- a/src/assets/lang/mn.json +++ /dev/null @@ -1,1503 +0,0 @@ -{ - "addon.block_activitymodules.pluginname": "Ажилбарууд", - "addon.block_badges.pluginname": "Саяхан авсан миний ялгарах тэмдэг", - "addon.block_blogmenu.pluginname": "Блог Меню", - "addon.block_blogrecent.pluginname": "Сүүлд оруулсан блогууд", - "addon.block_blogtags.pluginname": "Блог Хаяг", - "addon.block_calendarmonth.pluginname": "Календар", - "addon.block_calendarupcoming.pluginname": "Удахгүй болох үйл явдалууд", - "addon.block_comments.pluginname": "Сэтгэгдэлүүд", - "addon.block_completionstatus.pluginname": "Хичээл гүйцэтгэлийн статус", - "addon.block_glossaryrandom.pluginname": "Дурын тайлбар, ишлэл", - "addon.block_newsitems.pluginname": "Хамгийн сүүлийн мэдээ", - "addon.block_onlineusers.pluginname": "Холбогдсон байгаа хэрэглэгчид", - "addon.block_privatefiles.pluginname": "Миний хувийн файлууд", - "addon.block_recentactivity.pluginname": "шинэ үйл ажиллагаа", - "addon.block_rssclient.pluginname": "Rss үйлчлүүлэгч", - "addon.block_sitemainmenu.pluginname": "Үндсэн меню", - "addon.block_tags.pluginname": "Шошгууд", - "addon.blog.blog": "Блог", - "addon.blog.errorloadentries": "Блогийн оруулсан бичилтүүдийг ачаалахад алдаа гарлаа", - "addon.blog.noentriesyet": "Энд идэвхтэй биш оруулгууд байна.", - "addon.blog.publishtonoone": "Та еерее (зураг)", - "addon.blog.publishtosite": "Сайтын бүх хэрэглэгч", - "addon.blog.publishtoworld": "Ертенцийн хэн нэг нь", - "addon.blog.showonlyyourentries": "Зөвхөн оруулсан бичилтийг харуулах", - "addon.calendar.allday": "Бүх өдөр", - "addon.calendar.calendar": "Хуанли", - "addon.calendar.calendarevent": "Календарь дээрх үйл ажиллагаа", - "addon.calendar.calendarevents": "Календарь дээрх үйл ажиллагаанууд", - "addon.calendar.calendarreminders": "Календар дээрх сануулгууд", - "addon.calendar.confirmeventdelete": "Та энэ үйл явдлыг устгахыг зөвшөөрч байна уу?", - "addon.calendar.courseevents": "Курс үйл явдал", - "addon.calendar.currentmonth": "Энэ сар", - "addon.calendar.defaultnotificationtime": "Үндсэн мэдэгдлийн цаг", - "addon.calendar.deleteevent": "Үйл явдлыг устгах", - "addon.calendar.durationminutes": "Минутад үргэлжлэх хугацаа", - "addon.calendar.durationnone": "Үргэлжлэх хугацаагүй", - "addon.calendar.durationuntil": "Хүртэл", - "addon.calendar.editevent": "Үйл явдлыг засварлаж байна", - "addon.calendar.errorloadevent": "Үйл ажиллагааг ачаалахад алдаа гарлаа", - "addon.calendar.errorloadevents": "Үйл ажиллагаануудыг ачаалахад алдаа гарлаа", - "addon.calendar.eventduration": "Үргэлжлэх хугацаа", - "addon.calendar.eventendtime": "хугацааны эцэс", - "addon.calendar.eventkind": "Үйл явдлын төрөл", - "addon.calendar.eventname": "Нэр", - "addon.calendar.eventstarttime": "Хугацааны эхлэл", - "addon.calendar.fri": "Ба", - "addon.calendar.friday": "Баасан", - "addon.calendar.groupevents": "Груп үйл явдал", - "addon.calendar.mon": "Да", - "addon.calendar.monday": "Даваа", - "addon.calendar.monthlyview": "Сараар харах", - "addon.calendar.newevent": "Шинэ Үйл явдал", - "addon.calendar.noevents": "Үйл ажиллагаа байхгүй байна", - "addon.calendar.reminders": "Сануулгууд", - "addon.calendar.repeateditall": "Өөрчлөлтийг энэ давталтын цуваан дахь бүх {{$a}} үйл явдлуудад батлах", - "addon.calendar.repeateditthis": "Өөрчлөлтийг зөвхөн энэ үйл явдалд батлах", - "addon.calendar.repeatweeksl": "Долоо хоногийг давтаж бүхлээр нь үүсгэж байна", - "addon.calendar.sat": "Бя", - "addon.calendar.saturday": "Бямба", - "addon.calendar.setnewreminder": "Шинээр сануулга тавих", - "addon.calendar.siteevents": "Сайтын үйл явдалууд", - "addon.calendar.sun": "Ня", - "addon.calendar.sunday": "Ням", - "addon.calendar.thu": "Пү", - "addon.calendar.thursday": "Пүрэв", - "addon.calendar.today": "Өнөөдөр", - "addon.calendar.tomorrow": "Маргааш", - "addon.calendar.tue": "Мя", - "addon.calendar.tuesday": "Мягмар", - "addon.calendar.typecourse": "Курс үр дүн", - "addon.calendar.typegroup": "Груп үр дүн", - "addon.calendar.typesite": "Сайтын үр дүн", - "addon.calendar.typeuser": "хэрэглэгчийн үр дүн", - "addon.calendar.upcomingevents": "Удахгүй болох үйл явдалууд", - "addon.calendar.userevents": "Хэрэглэгчийн үр дүнгүүд", - "addon.calendar.wed": "Лха", - "addon.calendar.wednesday": "Лхагва", - "addon.calendar.yesterday": "Өчигдөр", - "addon.competency.errornocompetenciesfound": "Чадварууд олдсонгүй", - "addon.competency.nocompetencies": "Чадваргүй", - "addon.coursecompletion.complete": "Дуусгах", - "addon.coursecompletion.couldnotloadreport": "Хичээл дуусгасан тайланг ачаалах боломжгүй байна. Та дараа дахин оролдож үзнэ үү.", - "addon.coursecompletion.required": "шаардах", - "addon.coursecompletion.requirement": "Тавигдах шаардлага", - "addon.coursecompletion.status": "байдал", - "addon.files.couldnotloadfiles": "Файлуудын жагсаалтыг ачаалж чадахгүй байна", - "addon.files.emptyfilelist": "Үзүүлэх файл байхгүй байна.", - "addon.files.erroruploadnotworking": "Одоогоор таны хуудсанд файл хуулах боломжгүй байна", - "addon.files.files": "Файлууд", - "addon.files.privatefiles": "Хувийн файлууд", - "addon.files.sitefiles": "Сайт файлууд", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Төхөөрөмжүүдийг тохируулах", - "addon.messages.acceptandaddcontact": "Хүлээн авч, харилцагчаар нэмэх", - "addon.messages.addcontact": "Хаягны сандаа нэмэх", - "addon.messages.addcontactconfirm": "Та өөрийн харилцагчдад нэмж оруулахдаа итгэлтэй байна уу?", - "addon.messages.addtofavourites": "Одтой харилцан яриа", - "addon.messages.addtoyourcontacts": "Харилцагчид нэмэх", - "addon.messages.blocknoncontacts": "Миний хаягны санд байхгүй хаягнаас ирж буй бүх шинэ захиа, мэдээг блоклох", - "addon.messages.blockuser": "Хэрэглэгчийг блок хийх", - "addon.messages.blockuserconfirm": "Та {{$a}}-г блок хийхдээ итгэлтэй байна уу?", - "addon.messages.contactableprivacy": "Мессежүүд хүлээн авах:", - "addon.messages.contactableprivacy_coursemember": "Миний харилцагчид болон хамт хичээлд сууж буй хүн бүрд", - "addon.messages.contactableprivacy_onlycontacts": "Зөвхөн миний харилцагчид", - "addon.messages.contactableprivacy_site": "Сайт-д буй хүн бүрд", - "addon.messages.contactblocked": "Харилцагчийг блок хийлээ", - "addon.messages.contactlistempty": "Харилцагчийн жагсаалт хоосон байна", - "addon.messages.contactname": "Холбоо барих нэр", - "addon.messages.contactrequestsent": "Харилцах хүсэлт илгээгдлээ", - "addon.messages.contacts": "Харилцагчдын сан", - "addon.messages.conversationactions": "Харилцан ярианы үйлдлийн цэс", - "addon.messages.decline": "Үл зөвшөөрөх", - "addon.messages.deleteallconfirm": "\"Та энэхүү харилцан яриаг бүхэлд нь устгахдаа итгэлтэй байна уу?\nБусад харилцан ярианд оролцогч нарт устахгүй гэдгийг анхаарна уу.\"", - "addon.messages.deleteallselfconfirm": "Та энэхүү хувийн харилцан яриаг бүхэлд нь устгахдаа итгэлтэй байна уу?", - "addon.messages.deleteconversation": "Харилцан яриаг устгах", - "addon.messages.deleteforeveryone": "Надад болон бусад бүх хүмүүст устгах", - "addon.messages.deletemessage": "Зурвасыг устгах", - "addon.messages.deletemessageconfirmation": "Та энэ зурвасыг устгахдаа итгэлтэй байна уу? Энэ нь зөвхөн таны зурвасын түүхээс устгагдах бөгөөд зурвас илгээсэн эсвэл хүлээн авсан хэрэглэгч харах боломжтой хэвээр байна.", - "addon.messages.errordeletemessage": "Зурвасыг устгах үед алдаа гарлаа.", - "addon.messages.errorwhileretrievingcontacts": "Мэдээллийн сангаас харилцагчдын холбогдох мэдээллийг татах явцад алдаа гарлаа.", - "addon.messages.errorwhileretrievingdiscussions": "Мэдээллийн сангаас харилцсан яриаг татаж авах явцад алдаа гарлаа.", - "addon.messages.errorwhileretrievingmessages": "Мэдээллийн сангаас зурвасуудыг татаж авах явцад алдаа гарлаа.", - "addon.messages.errorwhileretrievingusers": "Мэдээллийн сангаас хэрэглэгчдийг татаж авах явцад алдаа гарлаа.", - "addon.messages.groupconversations": "Бүлэг", - "addon.messages.groupinfo": "Бүлгийн мэдээлэл", - "addon.messages.individualconversations": "Хувийн", - "addon.messages.info": "Хэрэглэгчийн мэдээлэл", - "addon.messages.isnotinyourcontacts": "{{$a}} таны харилцагчид дунд байхгүй байна", - "addon.messages.message": "Мессеж", - "addon.messages.messagenotsent": "Зурвас илгээгдсэнгүй. Дараа дахин илгээнэ үү", - "addon.messages.messagepreferences": "Мессеж сонголтууд", - "addon.messages.messages": "Мессежүүд", - "addon.messages.muteconversation": "Дуугүй", - "addon.messages.mutedconversation": "Дуугүй болгосон харилцан яриа", - "addon.messages.newmessage": "Шинэ мессеж", - "addon.messages.newmessages": "Шинэ зурвасууд", - "addon.messages.nocontactrequests": "Харилцагчийн хүсэлт байхгүй байна", - "addon.messages.nocontactsgetstarted": "Харилцагчид байхгүй байна", - "addon.messages.nofavourites": "Одоор тэмдэглэсэн харилцан яриа байхгүй байна", - "addon.messages.nogroupconversations": "Бүлгийн харилцан яриа байхгүй байна", - "addon.messages.noindividualconversations": "Хувийн харилцан яриа байхгүй байна", - "addon.messages.nomessagesfound": "Мессеж олдсонгүй", - "addon.messages.noncontacts": "Харилцагч бус", - "addon.messages.nousersfound": "Хэрэглэгч олдсонгүй", - "addon.messages.numparticipants": "{{$a}} оролцогчид", - "addon.messages.removecontact": "Харилцах хаягыг устгах", - "addon.messages.removecontactconfirm": "Та {{$a}}-ийг харилцагчаасаа хасахдаа итгэлтэй байна уу?", - "addon.messages.removefromfavourites": "Харилцан яриаг одоор тэмдэглэснийг арилгах", - "addon.messages.removefromyourcontacts": "Харилцагчдаас хасах", - "addon.messages.requests": "Хүсэлтүүд", - "addon.messages.requirecontacttomessage": "Та тэдэнрүү мессеж илгээх боломжтой болохын тулд {{$a}}-д харилцагчаар нэмж өгөхийг хүсэх хэрэгтэй", - "addon.messages.searchcombined": "Хүмүүс болон зурвас хайх", - "addon.messages.selfconversation": "Хувийн орон зай", - "addon.messages.selfconversationdefaultmessage": "Ноорог мессежүүд, холбоосууд, тэмдэглэл гэх мэт зүйлсийг дараа хэрэглэхийн тулд хадгалах.", - "addon.messages.sendcontactrequest": "Харилцах хүсэлт илгээх", - "addon.messages.showdeletemessages": "Устгасан зурвасыг харуулах", - "addon.messages.type_blocked": "Хаагдсан", - "addon.messages.type_offline": "холболтоос салсан", - "addon.messages.type_online": "холбогдсон", - "addon.messages.type_search": "Хайлтын үр дүн", - "addon.messages.type_strangers": "Бусад", - "addon.messages.unabletomessage": "Та энэхүү хэрэглэгч рүү зурвас илгээх боломжгүй байна", - "addon.messages.unblockuser": "Хэрэглэгчийн блокийг арилгах", - "addon.messages.unblockuserconfirm": "Та {{$a}}-ийг блок хийхдээ итгэлтэй байна уу?", - "addon.messages.unmuteconversation": "Дуу нээх", - "addon.messages.useentertosend": "Илгээхийн тулд оруулахыг ашиглах", - "addon.messages.useentertosenddescdesktop": "Хэрэв холболт салсан бол Ctrl + Enter ашиглан мессеж илгээх боломжтой.", - "addon.messages.useentertosenddescmac": "Хэрэв холболт салсан бол Ctrl + Enter ашиглан мессеж илгээх боломжтой.", - "addon.messages.userwouldliketocontactyou": "{{$a}} тантай холбоо барихыг хүсэж байна", - "addon.messages.warningconversationmessagenotsent": "Ярилцлага {{ярилцлага}} руу зурвасыг илгээж чадсангүй. {{алдаа}}", - "addon.messages.warningmessagenotsent": "Хэрэглэгч {{Хэрэглэгч}} рүү зурвас илгээх боломжгүй байна. {{алдаа}}", - "addon.messages.wouldliketocontactyou": "Тантай холбоо барихыг хүсэж байна", - "addon.messages.you": "Та:", - "addon.messages.youhaveblockeduser": "Та энэхүү хэрэглэгчийг блок хийсэн байна", - "addon.messages.yourcontactrequestpending": "Таны {{$a}}-тай холбоо барих хүсэлт хүлээгдэж байна", - "addon.mod_assign.acceptsubmissionstatement": "Мэдэгдлийг зөвшөөрнө үү.", - "addon.mod_assign.cannoteditduetostatementsubmission": "Програмд орсон мэдэгдэлд та нэмэлт засвар хийх боломжгүй учир нь уг мэдэгдлийг сайт дээрээс буцаан татах боломжгүй юм.", - "addon.mod_assign.cannotgradefromapp": "Тодорхой үнэлэх аргуудыг тус программ хараахан дэмжихгүй байгаа ба өөрчлөх боломжгүй.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Та мэдэгдлийг програмд хийх боломжгүй байна учир нь уг мэдэгдлийг сайт дээрээс буцаан татах боломжгүй юм.", - "addon.mod_assign.currentattemptof": "Энэ бол нийт зөвшөөрөгдсөн {{$a.maxattempts}} оролдлогуудын {{$a.attemptnumber}} дахь оролдлого болно.", - "addon.mod_assign.duedate": "Дуусах огноо", - "addon.mod_assign.duedateno": "Дуусах огноогүй", - "addon.mod_assign.editsubmission": "Миний оруулгыг засварла", - "addon.mod_assign.erroreditpluginsnotsupported": "Та программ дахь мэдэгдлийг нэмэх, засах боломжгүй, учир нь зарим засах өргөтгөлүүд хараахан хийгдээгүй байна.", - "addon.mod_assign.errorshowinginformation": "Таниулах мэдэгдлийг харуулах боломжгүй байна.", - "addon.mod_assign.feedbacknotsupported": "Энэ хүсэлтийг программ дэмжих боломжгүй, мэдээллийг гүйцэд бөглөсөн эсэхийг шалгана уу.", - "addon.mod_assign.graded": "Дүгнэгдсэн", - "addon.mod_assign.gradedby": "Дүгнэсэн хүн", - "addon.mod_assign.gradedon": "Дүгнэсэн огноо", - "addon.mod_assign.gradenotsynced": "Түвшин тохирсонгүй", - "addon.mod_assign.modulenameplural": "Даалгавар", - "addon.mod_assign.notallparticipantsareshown": "Таниулга илгээгээгүй оролцогчдыг харуулаагүй болно.", - "addon.mod_assign.notgraded": "Дүн тавиагүй", - "addon.mod_assign.numberofteams": "Бүлгүүд", - "addon.mod_assign.numwords": "{{$a}} үг", - "addon.mod_assign.submission": "Оруулга", - "addon.mod_assign.submissionnotsupported": "Энэ мэдэгдлийг программ дэмжихгүй байна, мэдээлэл дутуу байж болзошгүй.", - "addon.mod_assign.submissionstatus_marked": "Дүгнэгдсэн", - "addon.mod_assign.submitassignment": "Даалгаврыг хураалгах/оруулах", - "addon.mod_assign.userwithid": "ID {{id}} бүхий хэрэглэгч", - "addon.mod_assign.warningsubmissiongrademodified": "Таниулгын түвшинг цахим хуудас дээр өөрчилсөн.", - "addon.mod_assign.warningsubmissionmodified": "Хэрэглэгчийн таниулга цахим хуудас дээр өөрчлөгдсөн.", - "addon.mod_assign_feedback_comments.pluginname": "Эргэх холбоосийн сэтгэгдэлүүд", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF-т тэмдэглэгээ хийх", - "addon.mod_assign_feedback_file.pluginname": "Файлтай холбоотой санал хүсэлт", - "addon.mod_assign_submission_comments.pluginname": "Илгээх материалд тайлбар хийх", - "addon.mod_assign_submission_file.pluginname": "Файл илгээх", - "addon.mod_assign_submission_onlinetext.pluginname": "Онлайн текст илгээх", - "addon.mod_chat.beep": "сигнал", - "addon.mod_chat.chatreport": "Чат session", - "addon.mod_chat.currentusers": "Холбогдсон хэрэглэгчид", - "addon.mod_chat.enterchat": "Энд дарж Чат өрөөнд орно уу", - "addon.mod_chat.entermessage": "Мессэжээ оруул", - "addon.mod_chat.errorwhileconnecting": "Чатанд холбогдоход алдаа гарлаа.", - "addon.mod_chat.errorwhilegettingchatdata": "Чатын мэдээлэл авахад алдаа гарлаа.", - "addon.mod_chat.errorwhilegettingchatusers": "Чат хэрэглэгчдийг авахад алдаа гарлаа.", - "addon.mod_chat.errorwhileretrievingmessages": "Мэдээллийн сангаас зурвас татахад алдаа гарлаа.", - "addon.mod_chat.errorwhilesendingmessage": "Зурвасыг илгээхэд алдаа гарлаа", - "addon.mod_chat.messagebeepseveryone": "{{$a}} бүх хүнийг beep-лэж байна", - "addon.mod_chat.messagebeepsyou": "{{$a}} таныг beep-лэж байна", - "addon.mod_chat.messageenter": "{{$a}} чат өрөөнд орж ирлээ", - "addon.mod_chat.messageexit": "{{$a}} чат өрөөг орхилоо", - "addon.mod_chat.messages": "Мессежүүд", - "addon.mod_chat.messageyoubeep": "Та {{$a}}-д дохио өгсөн байна", - "addon.mod_chat.modulenameplural": "Чатууд", - "addon.mod_chat.mustbeonlinetosendmessages": "Зурвасыг илгээхэд та сүлжээнд холбогдсон байх ёстой", - "addon.mod_chat.nomessages": "Одоогоор зурвас захиа алга", - "addon.mod_chat.nosessionsfound": "Горим олдсонгүй", - "addon.mod_chat.saidto": "-д хэлсэн", - "addon.mod_chat.send": "Илгээх", - "addon.mod_chat.sessionstart": "Дараагийн чат {{$a}}-д эхлэнэ", - "addon.mod_chat.showincompletesessions": "Бүрэн бус горимыг харуул", - "addon.mod_chat.talk": "Ярих", - "addon.mod_chat.viewreport": "Өнгөрсөн session-г харах", - "addon.mod_choice.cannotsubmit": "Уучлаарай, сонголт илгээхэд хүндрэл гарлаа. Дахин оролдож үзнэ үү.", - "addon.mod_choice.choiceoptions": "Сонголттой холбоотой тохируулгууд", - "addon.mod_choice.errorgetchoice": "Сонголт мэдээлэл авахад алдаа гарлаа.", - "addon.mod_choice.expired": "Уучлаарай, энэ ажил {{$a}} -д хаагдсан бөгөөд хэрэглэх боломжгүй байна", - "addon.mod_choice.full": "(Бүтэн)", - "addon.mod_choice.modulenameplural": "Сонголтууд", - "addon.mod_choice.noresultsviewable": "Үр дүнг одоохондоо харах боломжгүй байна.", - "addon.mod_choice.notopenyet": "Уучлаарай, энэ үйл ажиллагаа {{$a}} хүртэл боломжгүй байна", - "addon.mod_choice.numberofuser": "Оролцогч нарын тоо", - "addon.mod_choice.numberofuserinpercentage": "Оролцогч нарын хувь", - "addon.mod_choice.removemychoice": "Миний хийсэн сонголтыг арилга", - "addon.mod_choice.responses": "Хариултууд", - "addon.mod_choice.responsesresultgraphdescription": "Хэрэглэгчдийн {{тоо}}% сонголтыг сонгосон байна: {{текст}}.", - "addon.mod_choice.responsesresultgraphheader": "График үзүүлэх", - "addon.mod_choice.resultsnotsynced": "Үр дүнрүү орохоос өмнө таны сүүлийн үйлдэл синхрончлогдсон байх ёстой", - "addon.mod_choice.savemychoice": "Миний сонголтыг хадгал", - "addon.mod_choice.userchoosethisoption": "Энэ сонголтыг сонгосон хэрэглэгч нар", - "addon.mod_choice.yourselection": "Таны сонголт", - "addon.mod_data.addentries": "Бичлэгүүд нэмэх", - "addon.mod_data.approve": "Дэмжиж зөвшөөр", - "addon.mod_data.ascending": "Өгсөх дарааллаар", - "addon.mod_data.confirmdeleterecord": "Та энэ бичлэгийг устгана гэдэгтээ итгэлтэй байна уу?", - "addon.mod_data.descending": "Уруудах дарааллаар", - "addon.mod_data.edittagsnotsupported": "Уучлаарай, програмд шошго засах боломжгүй.", - "addon.mod_data.emptyaddform": "Та ямар нэгэн талбар бөглөсөнгүй!", - "addon.mod_data.errorapproving": "Зөвшөөрөгдсөн эсвэл зөвшөөрөгдөөгүй бичлэгийн алдаа", - "addon.mod_data.errordeleting": "Бичлэгийг устгах үед алдаа гарлаа.", - "addon.mod_data.fields": "Талбарууд", - "addon.mod_data.gettinglocation": "Байршлыг хүлээн авч байна", - "addon.mod_data.locationpermissiondenied": "Таны байршилд нэвтрэх зөвшөөрөл байхгүй байна.", - "addon.mod_data.menuchoose": "Сонго...", - "addon.mod_data.modulenameplural": "Өгөгдлийн сангууд", - "addon.mod_data.more": "Дэлгэрэнгүй", - "addon.mod_data.mylocation": "Миний байршил", - "addon.mod_data.nomatch": "Тохирох бичлэг олдсонгүй!", - "addon.mod_data.norecords": "Датабэйсд бичлэг байхгүй байна", - "addon.mod_data.numrecords": "{{$a}} бичлэгүүд", - "addon.mod_data.recordapproved": "Бичлэг дэмжигдлээ", - "addon.mod_data.recorddeleted": "Бичлэг устгагдлаа", - "addon.mod_data.search": "Хайх", - "addon.mod_data.searchbytagsnotsupported": "Уучлаарай, шошгоор хайхыг программ дэмжихгүй байна.", - "addon.mod_data.single": "Ганцаарчилж харах", - "addon.mod_feedback.analysis": "Анализ", - "addon.mod_feedback.anonymous": "Нэргүй", - "addon.mod_feedback.anonymous_entries": "Нэргүй оруулгууд", - "addon.mod_feedback.average": "Дундаж", - "addon.mod_feedback.captchaofflinewarning": "CAPTCHA-тай хүсэлтийг офлайн байдлаар эсвэл тохируулаагүй эсвэл сервер ажиллахгүй байгаа учир дуусгах боломжгүй.", - "addon.mod_feedback.complete_the_form": "... асуултуудад хариуах", - "addon.mod_feedback.completed_feedbacks": "Илгээсэн хариунууд", - "addon.mod_feedback.continue_the_form": "Маягтыг үргэлжлүүлэх", - "addon.mod_feedback.feedback_is_not_open": "Энэ санал хүсэлт нээлттэй биш байна", - "addon.mod_feedback.feedback_submitted_offline": "Энэ хүсэлтийг дараа оруулахаар хадгалсан.", - "addon.mod_feedback.feedbackclose": "-д хариултуудыг зөвшөөрөх", - "addon.mod_feedback.feedbackopen": "-аас хариулт авахыг зөвшөөрөх", - "addon.mod_feedback.mapcourses": "Курсуудэд санал хүсэлтийг буулгах", - "addon.mod_feedback.maximal": "хамгийн олон", - "addon.mod_feedback.mode": "Горим", - "addon.mod_feedback.modulenameplural": "Санал хүсэлт", - "addon.mod_feedback.next_page": "Дараагийн хуудас", - "addon.mod_feedback.non_anonymous": "Хэрэглэгчийн нэр холбогдож, хариултуудын хамтаар үзүүлэгдэнэ", - "addon.mod_feedback.non_anonymous_entries": "Нэргүй оруулга оруулахгүй", - "addon.mod_feedback.non_respondents_students": "Хариуцагч оюутнуудыг оролцуулахгүй", - "addon.mod_feedback.not_selected": "Сонгогдоогүй", - "addon.mod_feedback.not_started": "Эхлүүлээгүй", - "addon.mod_feedback.overview": "Тойм", - "addon.mod_feedback.page_after_submit": "Гүйцээсэн тухай мессэж", - "addon.mod_feedback.preview": "Урьдчилан үзэх", - "addon.mod_feedback.previous_page": "Өмнөх хуудас", - "addon.mod_feedback.questions": "Асуултууд", - "addon.mod_feedback.response_nr": "Хариултын дугаар", - "addon.mod_feedback.responses": "Хариултууд", - "addon.mod_feedback.save_entries": "Хариултуудаа илгээх", - "addon.mod_feedback.show_entries": "Хариултуудыг үзүүлэх", - "addon.mod_feedback.show_nonrespondents": "Хариуцагч бус хүмүүсийг үзүүлэх", - "addon.mod_feedback.started": "эхлүүлсэн", - "addon.mod_feedback.this_feedback_is_already_submitted": "Та энэ ажлыг аль хэдийн гүйцээсэн байна.", - "addon.mod_folder.emptyfilelist": "Өгөгдөл байхгүй байна.", - "addon.mod_forum.addanewdiscussion": "Хэлэлцүүлгийн шинэ сэдэв нэмэх", - "addon.mod_forum.addanewquestion": "Шинэ асуулт нэмэх", - "addon.mod_forum.addanewtopic": "Шинэ сэдэв нэмэх", - "addon.mod_forum.advanced": "Дэлгэрэнгүй", - "addon.mod_forum.cannotadddiscussion": "Энэ форумд хэлэлцүүлэг нэмэхэд бүлгийн гишүүнчлэл шаардлагатай.", - "addon.mod_forum.cannotadddiscussionall": "Танд бүх оролцогч нарт зориулж хэлэлцүүлгийн шинэ сэдэв нэмэх зөвшөөрөл олгогдоогүй байна.", - "addon.mod_forum.cannotcreatediscussion": "Шинэ хэлэлцүүлэг үүсгэх боломжгүй болсон байна", - "addon.mod_forum.couldnotadd": "Ямар нэгэн алдаа гарсан учраас таны бичлэг нэмэгдэх боломжгvй.", - "addon.mod_forum.couldnotupdate": "Ямар нэгэн алдаа гарсан тул таны бичлэгийг єєрчилєх боломжгvй.", - "addon.mod_forum.delete": "Устгах", - "addon.mod_forum.deletedpost": "Бичлэг устгагдсан байна.", - "addon.mod_forum.deletesure": "Та энэ бичлэгийг устгамаар байна уу?", - "addon.mod_forum.discussion": "Хэлэлцүүлэг", - "addon.mod_forum.discussionsubscription": "Хэлэлцүүлэгт бүртгүүлэх бүртгэл", - "addon.mod_forum.edit": "Засварлах", - "addon.mod_forum.erroremptymessage": "Постын мессэж хоосон байж болохгүй", - "addon.mod_forum.erroremptysubject": "Пост гарчиггүй байж болохгүй.", - "addon.mod_forum.errorgetforum": "Форумын мэдээлэл авахад алдаа гарлаа.", - "addon.mod_forum.errorgetgroups": "Бүлгийн тохиргоог авахад алдаа гарлаа.", - "addon.mod_forum.errorposttoallgroups": "Бүх бүлэгт шинэ хэлэлцүүлэг үүсгэх боломжгүй байна.", - "addon.mod_forum.forumnodiscussionsyet": "Энэ форумд хэлэлцүүлэг алга байна.", - "addon.mod_forum.group": "Бүлэг", - "addon.mod_forum.lastpost": "Хамгийн сүүлчийн бичлэг", - "addon.mod_forum.message": "Зурвас", - "addon.mod_forum.modeflatnewestfirst": "Шинэ хариултуудыг хамгийн эхэнд харуулах", - "addon.mod_forum.modeflatoldestfirst": "Хуучин хариултуудыг хамгийн эхэнд харуулах", - "addon.mod_forum.modenested": "Бөглөсөн маягтын үр дүнг харуул", - "addon.mod_forum.modulenameplural": "Форумууд", - "addon.mod_forum.numdiscussions": "{{тоохэлэлцүүлгүүд)} хэлэлцүүлгүүд", - "addon.mod_forum.numreplies": "{{тоохариултууд}} хариултууд", - "addon.mod_forum.posttoforum": "Форумд бичлэг нэмэх", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Хэлэлцүүлгийг дахин ачаалах", - "addon.mod_forum.refreshposts": "Нийтлэлүүдийг дахин ачаалах", - "addon.mod_forum.reply": "хариу", - "addon.mod_forum.subject": "Сэдэв", - "addon.mod_forum.unread": "Уншаагүй", - "addon.mod_forum.unreadpostsnumber": "{{$a}} уншаагүй бичлэгvvд", - "addon.mod_forum.yourreply": "Таны хариу", - "addon.mod_glossary.addentry": "Бичлэх оруулах", - "addon.mod_glossary.aliases": "Түлхүүр үг", - "addon.mod_glossary.attachment": "Хавсралт", - "addon.mod_glossary.browsemode": "Бичлэгийг хайх", - "addon.mod_glossary.byalphabet": "Цагаан толгойн үсгийн дарааллаар", - "addon.mod_glossary.byauthor": "Зохиогчоор нь бүлэглэх", - "addon.mod_glossary.bycategory": "Төрлөөр нь бүлэглэх", - "addon.mod_glossary.bynewestfirst": "Шинэ нь эхэндээ", - "addon.mod_glossary.byrecentlyupdated": "Саяхан шинэчлэгдсэн", - "addon.mod_glossary.bysearch": "Хайх", - "addon.mod_glossary.cannoteditentry": "Бичлэгийг засварлах боломжгүй", - "addon.mod_glossary.casesensitive": "Энэ бичлэг нь том жижиг үсгийг ялгана", - "addon.mod_glossary.categories": "Ангиллууд", - "addon.mod_glossary.concept": "Ойлголт", - "addon.mod_glossary.definition": "Тодорхойлол", - "addon.mod_glossary.entriestobesynced": "Бичлэг цаг хугацааны хувьд зэргэцсэн", - "addon.mod_glossary.entrypendingapproval": "Энэ бичлэг баталгаажуулалт хүлээж байна.", - "addon.mod_glossary.entryusedynalink": "Энэ бичлэг автоматаар холбогдсон байх ёстой", - "addon.mod_glossary.errorloadingentries": "Бичлэгүүдийг ачааллах үед алдаа гарлаа.", - "addon.mod_glossary.errorloadingentry": "Бичлэгийг ачааллах үед алдаа гарлаа.", - "addon.mod_glossary.errorloadingglossary": "Тайлбар толийг ачааллах үед алдаа гарлаа.", - "addon.mod_glossary.fillfields": "Тайлбар, тодорхойломж нь зайлшгүй оруулах талбарууд.", - "addon.mod_glossary.fullmatch": "Бүтэн үгээр тохируулах", - "addon.mod_glossary.modulenameplural": "Түүврүүд", - "addon.mod_glossary.noentriesfound": "Бичвэр олдсонгүй.", - "addon.mod_glossary.searchquery": "Асуулгыг хайх", - "addon.mod_imscp.showmoduledescription": "Тайлбарыг харуулах", - "addon.mod_lesson.answer": "Хариулт", - "addon.mod_lesson.attempt": "{{$a}} оролдлого", - "addon.mod_lesson.attemptsremaining": "Танд {{$a}} оролдлого үлдсэн байна", - "addon.mod_lesson.averagescore": "Дундаж оноо", - "addon.mod_lesson.averagetime": "Дундаж цаг", - "addon.mod_lesson.branchtable": "Салбар хүснэгт", - "addon.mod_lesson.clusterjump": "Кластерт үзэгдээгүй асуултууд", - "addon.mod_lesson.completed": "Дууссан", - "addon.mod_lesson.congratulations": "Баяр хүргэе - хичээл дууслаа", - "addon.mod_lesson.continue": "Үргэлжлүүлэх", - "addon.mod_lesson.defaultessayresponse": "Таны эссэ зохиомжийг курсын багш дүгнэнэ.", - "addon.mod_lesson.detailedstats": "Дэлгэрэнгүй статистик", - "addon.mod_lesson.didnotanswerquestion": "Энэ асуултад хариулаагүй.", - "addon.mod_lesson.displayofgrade": "Дүн харуулах (зөвхөн оюутнууд)", - "addon.mod_lesson.displayscorewithessays": "Та автоматаар дүгнэгдэх асуултнуудад авах ёстой {{$a.tempmaxgrade}} –с {{$a.score}} оноо авлаа.
                Таны {{$a.essayquestions}} эссэний асуултнууд дараа нь дүгнэгдээд
                таны эцсийн дүнд тань нэмэгдэх болно.

                Эссэний оноог тооцохгүйгээр таны одоогийн оноо {{$a.grade}} авах ёстойгоос {{$a.score}} байна.", - "addon.mod_lesson.displayscorewithoutessays": "Таны оноо {{$a.score}} байна ({{$a.grade}} авах ёстой).", - "addon.mod_lesson.enterpassword": "Нууц үгээ оруулна уу:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Та ямар ч асуултанд хариулаагүй байна. Та энэ хичээлээс 0 авлаа.", - "addon.mod_lesson.errorprefetchrandombranch": "Энэ хичээлд агуулагдах тохиолдлын агуулга хуудас уруу үсэрч болно. Энэ вэб браузер дээр ачаалахгүйгээр программ дээр хандах боломжгүй.", - "addon.mod_lesson.errorreviewretakenotlast": "Өөр нэг оролдлого хийгдсэн тул энэ оролдлогыг өөрчлөх боломжгүй.", - "addon.mod_lesson.finishretakeoffline": "Энэ оролдлогыг офлайнаар дуусгасан.", - "addon.mod_lesson.firstwrong": "Харамсалтай нь та буруу хариулсан тул энэ оноог авч чадахгүй. Та таасан хэвээр байх уу(кредитэд хамаарахгүй)?", - "addon.mod_lesson.grade": "Дүн", - "addon.mod_lesson.highscore": "Дээд оноо", - "addon.mod_lesson.hightime": "Дээд цаг", - "addon.mod_lesson.leftduringtimed": "Та цагтай хичээл орхилоо.
                Үргэлжлүүл товчлуур дарж хичээлийг эхлүүлнэ үү.", - "addon.mod_lesson.leftduringtimednoretake": "Та цагтай хичээл орхилоо
                иймээс та ахин өгөх эсвэл үргэлжлүүлэх эрхгүй.", - "addon.mod_lesson.lessonmenu": "Хичээл цэс", - "addon.mod_lesson.lessonstats": "Хичээлийн статистик", - "addon.mod_lesson.loginfail": "Холбогдож чадсангүй, дахин оролдоно уу...", - "addon.mod_lesson.lowscore": "Доод оноо", - "addon.mod_lesson.lowtime": "Бага цаг", - "addon.mod_lesson.maximumnumberofattemptsreached": "Дараагийн хуудас руу шилжих хүртэл явсан оролдогуудын хамгийн дээд тоо", - "addon.mod_lesson.modattemptsnoteacher": "Оюутны тойм зөвхөн оюутанд л хамаарна.", - "addon.mod_lesson.modulenameplural": "Хичээлүүд", - "addon.mod_lesson.noanswer": "Хариултгүй байна. Буцаад хариултаа оруулна уу.", - "addon.mod_lesson.nolessonattempts": "Энэ хичээлд хийсэн оролдлого алга байна.", - "addon.mod_lesson.notcompleted": "Дуусаагүй", - "addon.mod_lesson.numberofcorrectanswers": "Зөв хариултын тоо: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Хариулсан асуултны тоо: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Хариулсан асуултны тоо: {{$a.nquestions}}; (Та хамгийн багадаа {{$a.minquestions}} асуултанд хариулсан байх ёстой)", - "addon.mod_lesson.ongoingcustom": "Та {{$a.currenthigh}} авхаас {{$a.score}} авлаа.", - "addon.mod_lesson.ongoingnormal": "Та {{$a.viewed}} асуултаас {{$a.correct}} зөв хариуллаа.", - "addon.mod_lesson.or": "эсвэл", - "addon.mod_lesson.overview": "Тойм", - "addon.mod_lesson.preview": "Урьдчилан харах", - "addon.mod_lesson.question": "Асуулт", - "addon.mod_lesson.rawgrade": "Бүхэлтгээгүй дүн", - "addon.mod_lesson.reports": "Тайлангууд", - "addon.mod_lesson.response": "Хариу", - "addon.mod_lesson.retakefinishedinsync": "Офлайн оролдлого синхрончлогдсон байна. Та хянаж үзмээр байна уу?", - "addon.mod_lesson.retakelabelfull": "{{дахин өгөх}}: {{түвшин}} {{цагийг эхлүүлэх}} ({{хугацаа}})", - "addon.mod_lesson.retakelabelshort": "{{дахин өгөх}}: {{түвшин}} {{цагийг эхлүүлэх}}", - "addon.mod_lesson.review": "Тойм", - "addon.mod_lesson.reviewlesson": "Хичээлийн тойм", - "addon.mod_lesson.reviewquestionback": "Тийм, би дахин оролдож үзмээр байна", - "addon.mod_lesson.reviewquestioncontinue": "Үгүй, би дараагийн асуулт руу очмоор байна", - "addon.mod_lesson.secondpluswrong": "Буруу, дахин оролдох уу?", - "addon.mod_lesson.teacherjumpwarning": "{{$a.cluster}} алгасалт эсвэл {{$a.unseen}} алгасалт энэ хичээл дээр болсон байна. Дараагийн хуудсыг алгасах оронд нь ашиглагдах болно. Эдгээр алгассан зүйлээ шалгахын тулд оюутнаар нэвтэрнэ үү.", - "addon.mod_lesson.teacherongoingwarning": "Явцын дүн зөвхөн оюутнуудад л харагдана. Оюутнаар нэвтэрч явцын дүнгээ шалгана уу.", - "addon.mod_lesson.teachertimerwarning": "Хугацаа зөвхөн оюутнуудад л хамаатай. Оюутнаар нэвтэрч хугацаагаа шалгана уу.", - "addon.mod_lesson.thatsthecorrectanswer": "Зөв хариулт", - "addon.mod_lesson.thatsthewronganswer": "Буруу хариулт", - "addon.mod_lesson.timeremaining": "Үлдсэн хугацаа", - "addon.mod_lesson.timetaken": "Зарцуулсан хугацаа", - "addon.mod_lesson.unseenpageinbranch": "Тухайн салбарт үзэгдээгүй асуулт", - "addon.mod_lesson.warningretakefinished": "Энэ оролдлого сайт дээр хийгдсэн.", - "addon.mod_lesson.welldone": "Сайн ажиллаа!", - "addon.mod_lesson.youhaveseen": "Та энэ хичээлийн нэгээс илүү хуудсыг хэдүйнээ үзсэн байна.
                do Та хамгийн сүүлд үзсэн хуудаснаасаа эхлэх үү?", - "addon.mod_lesson.youranswer": "Таны хариулт", - "addon.mod_lesson.yourcurrentgradeisoutof": "Таны одоогийн оноо {{$a.total}} байхаас {{$a.grade}} байна.", - "addon.mod_lesson.youshouldview": "Та ядаж {{$a}} хариулсан байх ёстой", - "addon.mod_lti.errorgetlti": "Модулийн мэдээлэл авахад алдаа гарлаа.", - "addon.mod_lti.errorinvalidlaunchurl": "Эхлүүлэх URL хүчингүй байна.", - "addon.mod_lti.launchactivity": "Үйл ажиллагааг эхлүүлэх", - "addon.mod_page.errorwhileloadingthepage": "Хуудасны агуулгыг ачаалах үед алдаа гарлаа.", - "addon.mod_quiz.attemptfirst": "Анхны оролдлого", - "addon.mod_quiz.attemptlast": "Хамгийн сүүлчийн оролдлого", - "addon.mod_quiz.attemptnumber": "Хариулах оролдлого", - "addon.mod_quiz.attemptquiznow": "Шалгалт өгөх", - "addon.mod_quiz.attemptstate": "Нөхцөл", - "addon.mod_quiz.canattemptbutnotsubmit": "Та энэхүү тестийг программ дээр эхлүүлж болох боловч дараах шалтгаанаар браузерт оролдлогоо баталгаажуулах шаардлагатай болно:", - "addon.mod_quiz.cannotsubmitquizdueto": "Энэхүү тестийн оролдлого дараах шалтгааны улмаас илгээгдэхгүй байна:", - "addon.mod_quiz.comment": "Тайлбар", - "addon.mod_quiz.completedon": "Дууссан", - "addon.mod_quiz.confirmclose": "Та энэ оролдлогыг хаах гэж байна. Оролдлогыг нэг удаа хаасан тохиолдолд хариултаа солих боломжгvй.", - "addon.mod_quiz.confirmcontinueoffline": "Энэ оролдлого {{$ a}} -оос хойш синхрончлол хийгдээгүй байна. Та энэ оролдлогыг өөр төхөөрөмжөөс үргэлжлүүлбэл мэдээллээ алдаж болзошгүй.", - "addon.mod_quiz.confirmleavequizonerror": "Хариултыг хадгалах явцад алдаа гарлаа. Та тестээ орхихдоо итгэлтэй байна уу?", - "addon.mod_quiz.connectionerror": "Сүлжээний холболт тасарсан байна. (Автоматаар сануулах сануулалт амжилтгүй боллоо).\n\nСүүлийн хэдэн минутад энэ хуудаст оруулсан хариултуудын аль нэгийг тэмдэглээд, дахин холбогдох гэж оролдоно уу.\n\nХолболт дахин сэргээгдэхэд, таны хариултууд хадгалагдах бөгөөд энэ мессэж алга болно.", - "addon.mod_quiz.continueattemptquiz": "Хамгийн сvvлчийн оролдлогыг үргэлжлvvлье", - "addon.mod_quiz.continuepreview": "Сүүлд урьдчилан харснаас үргэлжлүүлэх", - "addon.mod_quiz.errorbehaviournotsupported": "Асуултын хэв шинжийг программ дэмжих боломжгүй тул энэ тестийг ажиллах боломжгүй байна.", - "addon.mod_quiz.errordownloading": "Шаардлагатай мэдээллийг татаж авахад алдаа гарлаа.", - "addon.mod_quiz.errorgetattempt": "Оролдлогын мэдээллийг татаж авахад алдаа гарлаа.", - "addon.mod_quiz.errorgetquestions": "Асуултуудын мэдээллийг татаж авахад алдаа гарлаа.", - "addon.mod_quiz.errorgetquiz": "Тестийн мэдээллийг авахад алдаа гарлаа", - "addon.mod_quiz.errorparsequestions": "Асуултуудыг унших явцад алдаа гарлаа. Энэхүү тестээ вэб брaузерт ажиллана уу.", - "addon.mod_quiz.errorquestionsnotsupported": "Энэхүү тест нь тухайн программ дэмжигдэх боломжгүй асуултуудыг агуулж байгаа тул ажиллах боломжгүй байна.", - "addon.mod_quiz.errorrulesnotsupported": "Энэхүү тест нь тухайн программ дэмжигдэх боломжгүй хандалтын дүрэм агуулж байгаа тул ажиллах боломжгүй байна.", - "addon.mod_quiz.errorsaveattempt": "Оролдлогын мэдээллийг хадгалах явцад алдаа гарлаа.", - "addon.mod_quiz.feedback": "Сэтгэгдэл", - "addon.mod_quiz.finishattemptdots": "... хариултыг дуусгах", - "addon.mod_quiz.finishnotsynced": "Дууссан боловч синхрончлол хийгдээгүй", - "addon.mod_quiz.grade": "Дvн", - "addon.mod_quiz.gradeaverage": "дундаж оноо", - "addon.mod_quiz.gradehighest": "Хамгийн єндєр дvн", - "addon.mod_quiz.grademethod": "Дvгнэх арга", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "тэмдэгvvд", - "addon.mod_quiz.modulenameplural": "Шалгалтууд", - "addon.mod_quiz.mustbesubmittedby": "Энэ хариултыг {{$a}} илгээсэн байх ёстой.", - "addon.mod_quiz.noquestions": "Асуултууд хараахан нэмэгдээгvй байна.", - "addon.mod_quiz.noreviewattempt": "Танд энэ хариултыг шалгах эрх олгогдоогүй байна.", - "addon.mod_quiz.notyetgraded": "Дүн тавьж амжаагүй", - "addon.mod_quiz.opentoc": "Чиглүүлэгч программыг нээнэ үү", - "addon.mod_quiz.outof": "{{$a.maxgrade}}-н хамгийн их утгаас гадна {{$a.grade}}", - "addon.mod_quiz.outofpercent": "{{$a.maxgrade}} ({{$a.percent}}%)-с {{$a.grade}}", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Нэгдсэн санал хүсэлт", - "addon.mod_quiz.overdue": "хожимдсон", - "addon.mod_quiz.overduemustbesubmittedby": "Энэ хариултын хугацаа хэтэрсэн байна. Үүнийг аль эрт илгээсэн байх ёстой байсан. Хэрэв та энэ шалгалтыг үнэлүүлэх хүсэлтэй байгаа бол, {{$a}}-р илгээх ёстой. Хэрэв та илгээгээгүй бол, энэ хариултуудаас авсан дүнгүүдийг тооцохгүй.", - "addon.mod_quiz.preview": "Урьдчилан харах", - "addon.mod_quiz.previewquiznow": "Шалгалтанд одоо урьдчилсан хяналт хийх", - "addon.mod_quiz.question": "Асуулт", - "addon.mod_quiz.quiznavigation": "Шалгалтын навигаци", - "addon.mod_quiz.quizpassword": "Шалгалтанд нэвтрэх нууц үг", - "addon.mod_quiz.reattemptquiz": "Шалгалтад дахин оролцох", - "addon.mod_quiz.requirepasswordmessage": "Энэ шалгалтанд оролцохын тулд та шалгалтын нууц vгийг мэдсэн байх хэрэгтэй.", - "addon.mod_quiz.returnattempt": "Хариулах хэсэг руу буцах", - "addon.mod_quiz.review": "тойм", - "addon.mod_quiz.reviewofattempt": "{{$a}} оролдлогын тойм", - "addon.mod_quiz.reviewofpreview": "Урьдчилсан хяналтыг хянах", - "addon.mod_quiz.showall": "Нэг хуудсан дээр бvх асуултуудыг харуулах", - "addon.mod_quiz.showeachpage": "Нэг зэрэг нэг хуудас үзүүлэх", - "addon.mod_quiz.startattempt": "Хариултыг эхлүүлэх", - "addon.mod_quiz.startedon": "Эхлүүлсэн", - "addon.mod_quiz.stateabandoned": "Огт илгээгээгүй", - "addon.mod_quiz.statefinished": "Дуусгасан", - "addon.mod_quiz.statefinisheddetails": "Илгээсэн {{$a}}", - "addon.mod_quiz.stateinprogress": "Хариулж байгаа", - "addon.mod_quiz.stateoverdue": "Хугацаа хэтэрсэн", - "addon.mod_quiz.stateoverduedetails": "{{$a}}-р илгээсэн байх ёстой", - "addon.mod_quiz.status": "Статус", - "addon.mod_quiz.submitallandfinish": "Шалгалтыг дуусгах", - "addon.mod_quiz.summaryofattempt": "Хариултын дүгнэлт", - "addon.mod_quiz.summaryofattempts": "Таны өмнөх хариултуудын дүгнэлт", - "addon.mod_quiz.timeleft": "цаг үлдэгдэл", - "addon.mod_quiz.timetaken": "Цаг авах", - "addon.mod_quiz.warningattemptfinished": "Офлайн оролдлого нь сайт дээр хийгдсэн эсвэл олдсонгүй тул хаагдсан.", - "addon.mod_quiz.warningdatadiscarded": "Асуултуудыг онлайнаар өөрчилсөн тул зарим офлайн хариултыг хэрэгсэхгүй болгосон.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Зарим офлайн хариултуудыг хэрэгсэхгүй болгосон тул оролдлого дууссангүй. Хариултаа шалгаад дахин оролдоно уу.", - "addon.mod_quiz.warningquestionsnotsupported": "Энэхүү тестэнд программ дэмжигддэггүй асуултууд агууллагдсан байна.", - "addon.mod_quiz.yourfinalgradeis": "энэ {{$a}} шалгалтын дvн нь таны тєгсєлтийн дvн болно.", - "addon.mod_resource.errorwhileloadingthecontent": "Агуулгыг ачаалах үед алдаа гарлаа.", - "addon.mod_resource.modulenameplural": "Файлууд", - "addon.mod_resource.openthefile": "Файлыг нээнэ үү", - "addon.mod_scorm.asset": "asset", - "addon.mod_scorm.browse": "Урьдчилан харах", - "addon.mod_scorm.browsed": "Гvйлгэн харах", - "addon.mod_scorm.browsemode": "урьдчилах харах арга", - "addon.mod_scorm.cannotcalculategrade": "Түвшинг тооцоолж чадсангүй.", - "addon.mod_scorm.completed": "Дуусгасан", - "addon.mod_scorm.dataattemptshown": "Энэ мэдээлэл нь {{тоо}} оролдлогын тоонд хамаарна.", - "addon.mod_scorm.errorcreateofflineattempt": "Шинэ оффлайн оролдлогыг үүсгэх явцад алдаа гарлаа. Дахин оролдоно уу.", - "addon.mod_scorm.errordownloadscorm": "SCORM-ийг татаж авахад алдаа гарлаа: \"{{нэр}}\".", - "addon.mod_scorm.errorgetscorm": "SCORM мэдээлэл татаж авахад алдаа гарлаа.", - "addon.mod_scorm.errorinvalidversion": "Уучлаарай, програм нь зөвхөн SCORM 1.2-г дэмждэг.", - "addon.mod_scorm.errornotdownloadable": "SCORM багцийг татаж авах үйлдлийг идэвхгүй болгосон. Сайтын админтайгаа холбогдоно уу.", - "addon.mod_scorm.errornovalidsco": "Энэ SCORM багцад ачааллагдахуйц хэмжээний SCO байхгүй байна.", - "addon.mod_scorm.errorpackagefile": "Уучлаарай, програм нь зөвхөн ZIP багцуудыг дэмждэг.", - "addon.mod_scorm.errorsyncscorm": "Синхрончлол хийх явцад алдаа гарлаа. Дахин оролдоно уу.", - "addon.mod_scorm.failed": "Амжилтгvй болсон", - "addon.mod_scorm.gradeaverage": "Дундаж дvн", - "addon.mod_scorm.gradehighest": "Хамгийн єндєр дvн", - "addon.mod_scorm.grademethod": "Дvгнэх арга", - "addon.mod_scorm.gradescoes": "сурах эсэргүүцэх", - "addon.mod_scorm.gradesum": "Нийлбэр дvн", - "addon.mod_scorm.incomplete": "Гvйцэт бус", - "addon.mod_scorm.modulenameplural": "SCORMS/AICCS", - "addon.mod_scorm.newattempt": "шинэ оролдлогыг эхлэх", - "addon.mod_scorm.noattemptsallowed": "Зөвшөөрөгдсөн оролдлогын тоо", - "addon.mod_scorm.notattempted": "Оролдлогогvй", - "addon.mod_scorm.offlineattemptnote": "Энэ оролдлогод синхрончлогдоогүй өгөгдөл байна.", - "addon.mod_scorm.offlineattemptovermax": "Та энэ оролдлогыг илгээх боломжгүй, учир нь таны оролдлогын тоо дээд хязгаарыг давсан байна.", - "addon.mod_scorm.organizations": "Байгууллагууд", - "addon.mod_scorm.passed": "давах", - "addon.mod_scorm.reviewmode": "тойм арга", - "addon.mod_scorm.score": "Оноо", - "addon.mod_scorm.scormstatusnotdownloaded": "Энэ SCORM багц татагдаагүй байна. Та үүнийг нээх үед автоматаар татагдах болно.", - "addon.mod_scorm.scormstatusoutdated": "Энэ SCORM багцыг хамгийн сүүлд татаж авснаас хойш өөрчлөгдсөн байна. Та үүнийг нээх үед автоматаар татагдах болно.", - "addon.mod_scorm.warningofflinedatadeleted": "Үүнийг шинэ оролдлого гэж тооцох боломжгүй тул зарим офлайн {{дугаар}} оролдлогын мэдээлэл хасагдсан.", - "addon.mod_scorm.warningsynconlineincomplete": "Сүүлийн онлайн оролдлого дуусаагүй байгаа тул зарим оролдлогуудыг сайттай синхрончлол хийгдээгүй байна. Эхлээд онлайн оролдлогоо дуусгана уу.", - "addon.mod_survey.cannotsubmitsurvey": "Уучлаарай, таны судалгааг илгээх явцад алдаа гарлаа. Дахин оролдоно уу.", - "addon.mod_survey.errorgetsurvey": "Судалгааны мэдээлэл авахад алдаа гарлаа.", - "addon.mod_survey.ifoundthat": "Би юмаа олсон", - "addon.mod_survey.ipreferthat": "Би түүнийг илүүд үзэж байна", - "addon.mod_survey.modulenameplural": "Судалгаанууд", - "addon.mod_survey.results": "Үр дүнгүүд", - "addon.mod_url.accessurl": "URL руу нэвтрэх", - "addon.mod_url.pointingtourl": "Нөөцийг зааж байгаа URL.", - "addon.mod_wiki.cannoteditpage": "Та энэ хуудаст засвар оруулж болохгүй.", - "addon.mod_wiki.createpage": "Хуудас үүсгэх", - "addon.mod_wiki.editingpage": "Энэ '{{$a}}' хуудсыг засварлаж байна", - "addon.mod_wiki.errorloadingpage": "Хуудсыг ачаалах үед алдаа гарлаа.", - "addon.mod_wiki.errornowikiavailable": "Энэ вики дээр ямар ч агуулга алга байна.", - "addon.mod_wiki.gowikihome": "Вики эхний хуудас руу очно уу", - "addon.mod_wiki.map": "Бүтэц", - "addon.mod_wiki.modulenameplural": "Wiki-нvvд", - "addon.mod_wiki.newpagehdr": "Шинэ хуудас", - "addon.mod_wiki.newpagetitle": "Шинэ хуудасны нэр", - "addon.mod_wiki.nocontent": "Энэ хуудаст контент байхгүй байна", - "addon.mod_wiki.notingroup": "Энэ бүлэгт байхгүй", - "addon.mod_wiki.pageexists": "Ийм хуудас аль хэдийн байна. Хуудас руу чиглүүлж байна.", - "addon.mod_wiki.pagename": "Хуудасны нэр", - "addon.mod_wiki.subwiki": "Дэд-вики", - "addon.mod_wiki.titleshouldnotbeempty": "Гарчиг нь хоосон байж болохгүй", - "addon.mod_wiki.viewpage": "Хуудсыг харна уу", - "addon.mod_wiki.wikipage": "Вики хуудас", - "addon.mod_wiki.wrongversionlock": "Таныг засварлаж байх зуур өөр хэрэглэгч энэ хуудаст засвар оруулсан байна, тиймээс таны контент хуучирсан байна.", - "addon.mod_workshop.alreadygraded": "Аль хэдийн үнэлсэн", - "addon.mod_workshop.areainstructauthors": "Илгээх зааварчилгаа", - "addon.mod_workshop.areainstructreviewers": "Үнэлгээ хийх зааварчилгаа", - "addon.mod_workshop.assess": "Үнэлэх", - "addon.mod_workshop.assessedsubmission": "Үнэлгээ хийсэн материал илгээх", - "addon.mod_workshop.assessmentform": "Үнэлгээний маягт", - "addon.mod_workshop.assessmentsettings": "Үнэлгээний тохиргоо", - "addon.mod_workshop.assessmentstrategynotsupported": "Үнэлгээний стратеги {{$a}} дэмжигдэх боломжгүй байна", - "addon.mod_workshop.assessmentweight": "Үнэлгээний ач холбогдол", - "addon.mod_workshop.assignedassessments": "Үнэлгээ гаргуулахаар өгөгдсөн материалууд", - "addon.mod_workshop.assignedassessmentsnone": "Танд үнэлгээ гаргуулахаар өсөн материал байхгүй байна", - "addon.mod_workshop.conclusion": "Дүгнэлт", - "addon.mod_workshop.createsubmission": "Таны илгээх материалыг бэлдэж эхлэж байна", - "addon.mod_workshop.editsubmission": "Оруулгыг засварлах", - "addon.mod_workshop.feedbackauthor": "Авторт өгөх санал хүсэлт", - "addon.mod_workshop.feedbackby": "{{$a}}-н санал хүсэлт", - "addon.mod_workshop.feedbackreviewer": "Хянагчид зориулсан санал хүсэлт", - "addon.mod_workshop.givengrades": "Өгсөн дүнгүүд", - "addon.mod_workshop.gradecalculated": "Илгээсэн материалын тооцоолсон дүн", - "addon.mod_workshop.gradeinfo": "Дүн: {{$a.max}}-с {{$a.received}}", - "addon.mod_workshop.gradeover": "Илгээсэн материалын дүнг арилгах", - "addon.mod_workshop.gradesreport": "Семинар ажлын дүнгүүдийн тайлан", - "addon.mod_workshop.gradinggrade": "Дүнг Дүгнэх", - "addon.mod_workshop.gradinggradecalculated": "Үнэлгээний тооцоолсон дүн", - "addon.mod_workshop.gradinggradeof": "({{$a}}-ийн) үнэлгээний дүн", - "addon.mod_workshop.gradinggradeover": "Үнэлгээний дүнг арилгах", - "addon.mod_workshop.modulenameplural": "үйлдвэрийн цех/тасаг", - "addon.mod_workshop.nogradeyet": "Одоогоор дүн байхгүй байна", - "addon.mod_workshop.notassessed": "Үнэлгээ хийгдээгүй байна", - "addon.mod_workshop.notoverridden": "Арилгагдаагүй байна", - "addon.mod_workshop.noyoursubmission": "Та ажлаа хараахан илгээгээгүй байна", - "addon.mod_workshop.overallfeedback": "Нэгдсэн санал хүсэлт", - "addon.mod_workshop.publishedsubmissions": "Нийтлэсэн материалууд", - "addon.mod_workshop.publishsubmission": "Материал нийлэх", - "addon.mod_workshop.publishsubmission_help": "Семинар ажлын хаагдахад нийтлэсэн материалуудыш бусад хүмүүс үзэж болно.", - "addon.mod_workshop.reassess": "Дахин үнэлэх", - "addon.mod_workshop.receivedgrades": "Дүнгүүдийг хүлээж авсан", - "addon.mod_workshop.submissionattachment": "Хавсралт", - "addon.mod_workshop.submissioncontent": "Илгээсэн материалын контент", - "addon.mod_workshop.submissiongrade": "Илгээсэн материалын үнэлгээ", - "addon.mod_workshop.submissiongradeof": "({{$a}}-н) илгээсэн материалын үнэлгээ", - "addon.mod_workshop.submissionrequiredtitle": "Та гарчиг оруулна уу.", - "addon.mod_workshop.submissiontitle": "Нэр", - "addon.mod_workshop.userplan": "Семинар төлөвлөгч", - "addon.mod_workshop.warningassessmentmodified": "Энэ хүсэлтийг сайтад өөрчилсөн байна.", - "addon.mod_workshop.warningsubmissionmodified": "Энэ үнэлгээг сайтад өөрчилсөн байна.", - "addon.mod_workshop.weightinfo": "Жин: {{$a}}", - "addon.mod_workshop.yourassessment": "Таны үнэлгээ", - "addon.mod_workshop.yourgrades": "Таны дүнгүүд", - "addon.mod_workshop.yoursubmission": "Таны илгээсэн материал", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Та эдгээрээс нэгийг сонгох шаардлагатай", - "addon.notes.addnewnote": "Шинэ тэмдэглэл бичих", - "addon.notes.coursenotes": "Курсын тэмдэглэл", - "addon.notes.notes": "Тэмдэглэлүүд", - "addon.notes.personalnotes": "Хувийн тэмдэглэл", - "addon.notes.sitenotes": "Сайтын тэмдэглэлүүд", - "addon.notes.userwithid": "ID {{id}}-тай хэрэглэгч", - "addon.notes.warningnotenotsent": "{{Курс}} курст тэмдэглэл (үүд) ийг нэмж чадсангүй. {{алдаа}}", - "addon.notifications.errorgetnotifications": "Мэдэгдэл авахад алдаа гарлаа.", - "addon.notifications.markallread": "Бүгдийг уншсан төлөвт оруулах", - "addon.notifications.notificationpreferences": "Мэдэгдлийн сонголтууд", - "addon.notifications.notifications": "Мэдэгдлүүд", - "addon.notifications.playsound": "Дууг тоглуулах", - "addon.notifications.therearentnotificationsyet": "Мэдэгдэл алга", - "addon.storagemanager.deletecourse": "Бүх курсийн өгөгдлийг устгах", - "addon.storagemanager.deletedatafrom": "{{Нэр}} - с мэдээлэлийг устгах", - "addon.storagemanager.info": "Таны төхөөрөмж дээр хадгалагдсан файлууд нь програмыг илүү хурдан бөгөөд офлайн ашиглах боломжтой болгодог. Хадгалах зайг чөлөөлөх шаардлагатай бол та файлуудаа аюулгүйгээр буулгаж чадна.", - "addon.storagemanager.managestorage": "Хадгалах санг удирдах", - "addon.storagemanager.storageused": "Ашиглагдсан мэдээллийн сан:", - "assets.countries.AD": "Андора", - "assets.countries.AE": "Арабын Нэгдсэн Эмират Улс", - "assets.countries.AF": "Афгинистан", - "assets.countries.AG": "Антигуа ба Барбуда", - "assets.countries.AI": "Ангуйлла", - "assets.countries.AL": "Албани", - "assets.countries.AM": "Армени", - "assets.countries.AO": "Ангол", - "assets.countries.AQ": "Антарктик", - "assets.countries.AR": "Аргентин", - "assets.countries.AS": "Америкийн Самоа", - "assets.countries.AT": "Австри", - "assets.countries.AU": "Австрали", - "assets.countries.AW": "Аруба", - "assets.countries.AZ": "Азербежани", - "assets.countries.BA": "Босни Герцеговина", - "assets.countries.BB": "Барбадос", - "assets.countries.BD": "Бангладеш", - "assets.countries.BE": "Бельги", - "assets.countries.BF": "Буркина Фасо", - "assets.countries.BG": "Болгар", - "assets.countries.BH": "Бахрайн", - "assets.countries.BI": "Бурунды", - "assets.countries.BJ": "Бенин", - "assets.countries.BM": "Бермуд", - "assets.countries.BN": "Бруней Даруссалам", - "assets.countries.BO": "Боливи", - "assets.countries.BR": "Бразили", - "assets.countries.BS": "Бахама", - "assets.countries.BT": "Бутан", - "assets.countries.BV": "Бойвет арал", - "assets.countries.BW": "Ботсвани", - "assets.countries.BY": "Беларус", - "assets.countries.BZ": "Белиз", - "assets.countries.CA": "Канад", - "assets.countries.CC": "Кокос арал", - "assets.countries.CF": "Төв Африкийн бүгд найрамдах улс", - "assets.countries.CG": "Конго", - "assets.countries.CH": "Швецари", - "assets.countries.CI": "Коте Ди'воре", - "assets.countries.CK": "тогооч арал", - "assets.countries.CL": "Чили", - "assets.countries.CM": "Камерун", - "assets.countries.CN": "Хятад", - "assets.countries.CO": "Коломбо", - "assets.countries.CR": "Коста Рико", - "assets.countries.CU": "Куба", - "assets.countries.CV": "Кайп верве", - "assets.countries.CX": "Крисмас арал", - "assets.countries.CY": "Кипр", - "assets.countries.CZ": "Чехийн Бүгд Найрамдах улс", - "assets.countries.DE": "Герман", - "assets.countries.DJ": "Джибоути", - "assets.countries.DK": "Дани улс", - "assets.countries.DM": "Доминик", - "assets.countries.DO": "Доминиканы Бүгд найрамдах улс", - "assets.countries.DZ": "Алжыр", - "assets.countries.EC": "Эквадор", - "assets.countries.EE": "Естони", - "assets.countries.EG": "Египет", - "assets.countries.EH": "Вестерн Захара", - "assets.countries.ER": "Эритри", - "assets.countries.ES": "Испани", - "assets.countries.ET": "Этиоп", - "assets.countries.FI": "Финлянд", - "assets.countries.FJ": "Фижи", - "assets.countries.FK": "Палкландын арлууд (Малвинууд)", - "assets.countries.FM": "Микрониз", - "assets.countries.FO": "Паро арал", - "assets.countries.FR": "Франц", - "assets.countries.GA": "Габон", - "assets.countries.GB": "Их Британи", - "assets.countries.GD": "Гренада", - "assets.countries.GE": "Жеорж", - "assets.countries.GF": "Франц Гуанна", - "assets.countries.GH": "Гана", - "assets.countries.GI": "Гибралтар", - "assets.countries.GL": "Грийнланд", - "assets.countries.GM": "Гамби", - "assets.countries.GN": "Гуэни", - "assets.countries.GP": "Гуаделоуп", - "assets.countries.GQ": "Екуаториал Гуэна", - "assets.countries.GR": "Грек", - "assets.countries.GT": "Гуатамал", - "assets.countries.GU": "Гуам", - "assets.countries.GW": "Гуанна-Биссау", - "assets.countries.GY": "Гуяана", - "assets.countries.HK": "Хонг Конг", - "assets.countries.HM": "Хирд ба Мак Доналдын арлууд", - "assets.countries.HN": "Гондурас", - "assets.countries.HR": "Кроати (Хрватка)", - "assets.countries.HT": "Хайти", - "assets.countries.HU": "Унгар", - "assets.countries.ID": "Индонези", - "assets.countries.IE": "Ирланд", - "assets.countries.IL": "Израиль", - "assets.countries.IN": "Энэтхэг", - "assets.countries.IO": "Английн indian далай нутаг дэвсгэр", - "assets.countries.IQ": "Ирак", - "assets.countries.IR": "Иран (Исламын Бүгд найрамдах улс)", - "assets.countries.IS": "Исланд", - "assets.countries.IT": "Итали", - "assets.countries.JM": "Яамайк", - "assets.countries.JO": "Йордан", - "assets.countries.JP": "Япон", - "assets.countries.KE": "Кени", - "assets.countries.KG": "Киргистан", - "assets.countries.KH": "Камбожи", - "assets.countries.KI": "Кирибати", - "assets.countries.KM": "Коморос", - "assets.countries.KN": "гэгээн kitts бас nevis", - "assets.countries.KP": "Бvгд Найрамдах Ардчилсан Солонгос Улс", - "assets.countries.KR": "Бvгд Найрамдах Солонгос Улс", - "assets.countries.KW": "Кувейт", - "assets.countries.KY": "Сайман", - "assets.countries.KZ": "Казакстан", - "assets.countries.LA": "Бүгд Найрамдах Ардчилсан Лаос Улс", - "assets.countries.LB": "Ливан", - "assets.countries.LC": "Сант Люис", - "assets.countries.LI": "Лихтенштейн", - "assets.countries.LK": "Шри Ланк", - "assets.countries.LR": "Либерия", - "assets.countries.LS": "Лесото", - "assets.countries.LT": "Литва", - "assets.countries.LU": "Люксембург", - "assets.countries.LV": "Латви", - "assets.countries.LY": "Ливяан Араб Жамахирияа", - "assets.countries.MA": "Морокко", - "assets.countries.MC": "Монако", - "assets.countries.MD": "Бүгд Найрамдах Молдав Улс", - "assets.countries.MG": "Мадагаскар", - "assets.countries.MH": "Маршалын арлууд", - "assets.countries.MK": "Македона", - "assets.countries.ML": "Мали", - "assets.countries.MM": "Мяанмар", - "assets.countries.MN": "Монгол", - "assets.countries.MO": "Макао", - "assets.countries.MP": "Норт Марины арлууд", - "assets.countries.MQ": "Мартиник", - "assets.countries.MR": "Мауритан", - "assets.countries.MS": "Монтсеррат", - "assets.countries.MT": "Мальт", - "assets.countries.MU": "Мауритус", - "assets.countries.MV": "Мальдив", - "assets.countries.MW": "Малави", - "assets.countries.MX": "Мексик", - "assets.countries.MY": "Малайз", - "assets.countries.MZ": "Мозамбейк", - "assets.countries.NA": "Намиба", - "assets.countries.NC": "Нью каледон", - "assets.countries.NE": "Нигер", - "assets.countries.NF": "Норполк арал", - "assets.countries.NG": "Нигер", - "assets.countries.NI": "Никарагуа", - "assets.countries.NL": "Нидерланд", - "assets.countries.NO": "Норвеги хүн", - "assets.countries.NP": "Непал", - "assets.countries.NR": "Науру", - "assets.countries.NU": "Ниюе", - "assets.countries.NZ": "Шинэ Зиланд", - "assets.countries.OM": "Оман", - "assets.countries.PA": "Панама", - "assets.countries.PE": "Перу", - "assets.countries.PF": "Францийн Полинесия", - "assets.countries.PG": "Папуа Шинэ Гвиней", - "assets.countries.PH": "Филиппин", - "assets.countries.PK": "Пакистан", - "assets.countries.PL": "Польш", - "assets.countries.PM": "st. pierre бас miquelon", - "assets.countries.PN": "Питсайрн", - "assets.countries.PR": "Пуерто Рико", - "assets.countries.PS": "Палестин", - "assets.countries.PT": "Португали", - "assets.countries.PW": "Палау", - "assets.countries.PY": "Парагвай", - "assets.countries.QA": "Катар", - "assets.countries.RE": "нэгдэх", - "assets.countries.RO": "Румын", - "assets.countries.RU": "Оросын Холбооны Улс", - "assets.countries.RW": "Рванда", - "assets.countries.SA": "Саудын Араб", - "assets.countries.SB": "Соломоны арлууд", - "assets.countries.SC": "Сейшел", - "assets.countries.SD": "Судан", - "assets.countries.SE": "Швед", - "assets.countries.SG": "Сингапур", - "assets.countries.SH": "st. helena", - "assets.countries.SI": "Словани", - "assets.countries.SJ": "svalbard бас jan mayen арал", - "assets.countries.SK": "Словак", - "assets.countries.SL": "Сери Леон", - "assets.countries.SM": "Сан Марино", - "assets.countries.SN": "Сенегал", - "assets.countries.SO": "Сомали", - "assets.countries.SR": "Суринэйм", - "assets.countries.ST": "Сан-Тома Принсипи", - "assets.countries.SV": "Эль Салавадор", - "assets.countries.SY": "Сири Арабын бүгд найрамдах улс", - "assets.countries.SZ": "Свэциланд", - "assets.countries.TC": "Турк ба Кайкос арал", - "assets.countries.TD": "Чад", - "assets.countries.TF": "Францийн өмнөд бүс нутгууд", - "assets.countries.TG": "Того", - "assets.countries.TH": "Тайланд", - "assets.countries.TJ": "Тажикстан", - "assets.countries.TK": "Токелау", - "assets.countries.TM": "Туркмистан", - "assets.countries.TN": "Тунис", - "assets.countries.TO": "Тонга", - "assets.countries.TR": "Турк", - "assets.countries.TT": "Тринадод Тобаго", - "assets.countries.TV": "Тувалу", - "assets.countries.TW": "Тайван", - "assets.countries.TZ": "Танзани", - "assets.countries.UA": "Украйн", - "assets.countries.UG": "Уганда", - "assets.countries.US": "Америкийн Нэгдсэн Улс", - "assets.countries.UY": "Урагвай", - "assets.countries.UZ": "Узбекистан", - "assets.countries.VA": "Ватикан", - "assets.countries.VC": "гэгээн vincent бас the grenadines", - "assets.countries.VE": "Венесуел", - "assets.countries.VG": "Виржиний арал (Их Британи)", - "assets.countries.VI": "Виржиний арал (Америк)", - "assets.countries.VN": "Вьетнам", - "assets.countries.VU": "Вануата", - "assets.countries.WF": "wallis бас futuna арал", - "assets.countries.WS": "Самоа", - "assets.countries.YE": "Йемен", - "assets.countries.YT": "Маяотте", - "assets.countries.ZA": "Ємнөд Африка", - "assets.countries.ZM": "Замби", - "assets.countries.ZW": "Зимбабвэ", - "assets.mimetypes.application/msword": "үг баримт бичиг", - "assets.mimetypes.application/pdf": "pdf баримт бичиг", - "assets.mimetypes.application/vnd.ms-excel": "давах spreadsheet", - "assets.mimetypes.application/vnd.ms-powerpoint": "powerpoint танилцуулга", - "assets.mimetypes.document/unknown": "файл", - "assets.mimetypes.text/plain": "текст файл", - "assets.mimetypes.text/rtf": "rtf баримт бичиг", - "core.accounts": "Хэрэглэгчийн эрх", - "core.add": "Нэмэх", - "core.agelocationverification": "Нас болон байршлын баталгаажуулалт", - "core.ago": "{{$a}} өмнө", - "core.all": "Бүгд", - "core.allgroups": "Бүх бүлгүүд", - "core.allparticipants": "Бүх оролцогч", - "core.answer": "Хариулт", - "core.answered": "Хариулсан", - "core.areyousure": "Та итгэлтэй байна уу?", - "core.back": "Буцах", - "core.block.blocks": "Блокууд", - "core.browser": "Хөтөч", - "core.cancel": "Болих", - "core.cannotconnect": "холбогдож чадахгүй байна: Сайтын хаягаа зөв оруулсан эсэхээ баталгаажуулна уу.", - "core.cannotdownloadfiles": "Файл татаж авах ажиллагаа идэвхгүй болсон. Сайтын админ/зохион байгуулагчтай холбогдоно уу.", - "core.captureaudio": "Аудио бичих", - "core.capturedimage": "Авсан зураг.", - "core.captureimage": "Зураг авах", - "core.capturevideo": "Видео бичих", - "core.category": "Ангилал", - "core.choose": "Сонгох", - "core.choosedots": "Сонголт ...", - "core.clearsearch": "Хайлтыг устга", - "core.clicktohideshow": "Хатгаад дэлгэ эсвэл хумь", - "core.clicktoseefull": "Бүрэн агуулгыг харахын тулд товшино уу.", - "core.close": "Хаах", - "core.comments": "Тайлбар", - "core.comments.addcomment": "Тайлбар нэмэх ...", - "core.comments.comments": "Тайлбар", - "core.comments.commentscount": "({{$a}}) тайлбарууд", - "core.comments.commentsnotworking": "Коммент сэргээж чадахгүй байна", - "core.comments.deletecommentbyon": "{{$a.user}} хэрэглэгчийн {{$a.time}} огноонд оруулсан сэтгэгдлийг устгах", - "core.comments.eventcommentcreated": "Тайлбар үүсгэсэн", - "core.comments.eventcommentdeleted": "Тайлбар устгасан", - "core.comments.nocomments": "Тайлбар алга", - "core.comments.savecomment": "Тайлбар хадгалах", - "core.comments.warningcommentsnotsent": "Комментийг синхрончлох боломжгүй. {{алдаа}}", - "core.commentscount": "({{$a}}) тайлбарууд", - "core.confirmcanceledit": "Та энэ хуудсаас гарахыг хүсч байна уу? Бүх өөрчлөлтүүд алдагдах болно.", - "core.confirmdeletefile": "Та энэхүү файлыг устгахдаа итгэлтэй байна уу?", - "core.confirmgotabroot": "Та {{нэр}} рүү буцахдаа итгэлтэй байна уу?", - "core.confirmgotabrootdefault": "Та энэ цонхны эхний хуудас руу очихдоо итгэлтэй байна уу?", - "core.confirmloss": "Та итгэлтэй байна уу? Бүх өөрчлөлтүүд алдагдах болно.", - "core.confirmopeninbrowser": "Та үүнийг вэб браузер дээр ачаалахыг хүсэж байна уу?", - "core.considereddigitalminor": "Та энэхүү сайт дээр бүртгэл үүсгэхэд хэт залуу байна", - "core.content": "Агуулга", - "core.contenteditingsynced": "Таны ажиллаж байгаа агуулга синхрончлогдсон байна.", - "core.contentlinks.chooseaccount": "Бүртгэлээ сонгоно уу", - "core.contentlinks.chooseaccounttoopenlink": "Холбоосыг нээхэд ашиглах бүртгэлээ сонгоно уу.", - "core.contentlinks.confirmurlothersite": "Энэ холбоос өөр сайтад хамаарна. Та үүнийг нээхийг хүсч байна уу?", - "core.contentlinks.errornoactions": "Энэ холбоосоор хийх үйлдэлийг олж чадахгүй байна.", - "core.contentlinks.errornosites": "Энэ холбоосыг удирдах сайт олдсонгүй.", - "core.contentlinks.errorredirectothersite": "Дахин чиглүүлэх URL нь өөр сайтыг зааж чадахгүй байна.", - "core.continue": "Үргэлжлүүлэх", - "core.copiedtoclipboard": "Түр санах ойруу текст хуулагдлаа", - "core.course": "Курс", - "core.course.activitydisabled": "Танай байгууллага энэхүү үйлдлийг мобайл апп дээр идэвхгүй болгосон байна.", - "core.course.activitynotyetviewableremoteaddon": "Танай байгууллага хараахан дэмжигдээгүй нэмэлт өргөтгөл суулгасан байна.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Танай байгууллагын \"Moodle\" суулгацыг шинэчлэх шаардлагатай байна.", - "core.course.allsections": "Бүх хэсгүүд", - "core.course.askadmintosupport": "Сайтын админ/зохион байгуулагчтай холбоо барьж, энэхүү үйлдлийг мобайл апп дээр ашиглахыг хүсэж байгаагаа мэдэгдээрэй.", - "core.course.availablespace": "Танд одоогоор {{боломжтой}} үнэгүй зай байна.", - "core.course.confirmdeletemodulefiles": "Та эдгээр файлуудыг устгахдаа итгэлтэй байна уу?", - "core.course.confirmdownload": "Та {{size}}. {{availableSpace}} -г татаж авах гэж байна. Үргэлжлүүлэхийг хүсэж байна уу?", - "core.course.confirmdownloadunknownsize": "Татаж авах хэмжээг тооцоолох боломжгүй байна. {{availableSpace}} - ийг үргэлжлүүлэхдээ итгэлтэй байна уу?", - "core.course.confirmdownloadzerosize": "Та татаж авах гэж байна. {{availableSpace}} Та үргэлжлүүлэхийг хүсч байна уу?", - "core.course.confirmlimiteddownload": "Та одоогоор Wi-Fi-д холбогдоогүй байна", - "core.course.confirmpartialdownloadsize": "Та at least {{size}}. {{AvailableSpace}} -г татаж авах гэж байна. Та үргэлжлүүлэхийг хүсэж байна уу?", - "core.course.contents": "Агуулга", - "core.course.couldnotloadsectioncontent": "Хэсгийн агуулгыг ачаалж чадахгүй байна. Дараа дахин оролдож үзнэ үү.", - "core.course.couldnotloadsections": "Хэсгүүдийг ачаалж чадахгүй байна. Дараа дахин оролдож үзнэ үү.", - "core.course.coursesummary": "Курсын Танилцуулга", - "core.course.errordownloadingcourse": "Хичээл татаж авахад алдаа гарлаа.", - "core.course.errordownloadingsection": "Хэсэг татаж авахад алдаа гарлаа.", - "core.course.errorgetmodule": "Үйл ажиллагааны мэдээлэл авахад алдаа гарлаа.", - "core.course.hiddenfromstudents": "Оюутанд харагдахгүй", - "core.course.hiddenoncoursepage": "Боломжтой боловч цуврал мэдээллийн хуудсанд харагдахгүй", - "core.course.insufficientavailablequota": "Таны төхөөрөмж энэхүү татан авалтыг хадгалах орон зайг хуваарилж чадсангүй. Энэ нь апп болон системийн шинэчлэлтийн зайг нөөцөлж байж магадгүй юм. Эхлээд зарим хадгалах зайг арилгана уу.", - "core.course.insufficientavailablespace": "Та {{size}} татаж авах гэж байна. Энэ нь таны төхөөрөмжийг хэвийн ажиллахад хангалттай зайгүй үлдээх болно. Эхлээд зарим хадгалах зайг арилгана уу.", - "core.course.manualcompletionnotsynced": "Синхрончлол хийж чадахгүй байна", - "core.course.nocontentavailable": "Яг одоогоор ямар ч агуулга боломжгүй байна.", - "core.course.refreshcourse": "Сургалтыг сэргээх", - "core.course.sections": "Хэсэг", - "core.course.useactivityonbrowser": "Та төхөөрөмжийнхөө веб хөтчийг ашиглан одоо ч үүнийг хэрэглэж болно.", - "core.course.warningmanualcompletionmodified": "Үйл явцын гар ажиллагааны гүйцэтгэлийг сайт дээр өөрчилсөн.", - "core.course.warningofflinemanualcompletiondeleted": "'{{name}}' сургалтын зарим оффлайн бүхий гар ажиллагаа устгагдсан байна. {{error}}", - "core.coursedetails": "Курсын дэлгэрэнгүй", - "core.coursenogroups": "Та энэхүү сургалтын аль ч бүлгийн гишүүн биш байна.", - "core.courses.allowguests": "Энэ курсд Зочин хэрэглэгч нэвтрэхийг зөвшөөрнө", - "core.courses.availablecourses": "Одоо байгаа курсууд", - "core.courses.cannotretrievemorecategories": "{{$ A}} ангиллаас гүнзгий төвшинг сэргээх боломжгүй байна.", - "core.courses.categories": "Курсийн ангиллууд", - "core.courses.confirmselfenrol": "Та энэхүү сургалтад бүртгүүлэхдээ итгэлтэй байна уу?", - "core.courses.courses": "Курсууд", - "core.courses.downloadcourses": "Сургалтуудыг татаж авах", - "core.courses.enrolme": "Намайг бүртгүүл", - "core.courses.errorloadcategories": "Ангиллыг ачаалах үед алдаа гарлаа.", - "core.courses.errorloadcourses": "Сургалтыг ачаалах үед алдаа гарлаа.", - "core.courses.errorloadplugins": "\"Энэхүү сургалтад шаардлагатай нэмэлт өргөтгөлүүдийг зөв ачаалах боломжгүй байна.\nДахин оролдохын тулд апп-ийг дахин ачаална уу.\"", - "core.courses.errorsearching": "Хайлт хийж байх явцад алдаа гарлаа", - "core.courses.errorselfenrol": "Өөрөө бүртгүүлэх үед алдаа гарлаа.", - "core.courses.filtermycourses": "Миний хичээлүүдийг шүүнэ үү", - "core.courses.frontpage": "Нүүр хуудас", - "core.courses.ignore": "Татгалзах", - "core.courses.mycourses": "Миний курсууд", - "core.courses.mymoodle": "Эхний хуудас", - "core.courses.nocourses": "гүй курс мэдээ -д show.", - "core.courses.nocoursesyet": "Энэ категорид курс байхгүй байна", - "core.courses.nosearchresults": "Үр дүн олдсонгүй", - "core.courses.notenrollable": "Та энэхүү сургалтад хамрагдах боломжгүй.", - "core.courses.password": "Хамрагдах түлхүүр", - "core.courses.paymentrequired": "энэ курс шаардах a төлбөр төлөө entry.", - "core.courses.reload": "Дахин ачаалах", - "core.courses.search": "Хайх", - "core.courses.searchcourses": "Курс хайх", - "core.courses.searchcoursesadvice": "Та сургалт хайх товчыг ашиглан зочин болон нэвтэрч эсвэл үүнийг зөвшөөрдөг сургалтуудад өөрөө бүртгүүлэн сургалт олж болно.", - "core.courses.selfenrolment": "Өөрөө бүртгүүлэлт", - "core.courses.sendpaymentbutton": "Төлбөрөө онлайн төлбөр хийх систем Paypal-р илгээх", - "core.courses.totalcoursesearchresults": "Нийт сургалтууд: {{$a}}", - "core.currentdevice": "Одоогийн төхөөрөмж", - "core.datastoredoffline": "Илгээх боломжгүй тул мэдээллийг төхөөрөмжид хадгалагдлаа. Дараа нь автоматаар илгээгдэх болно.", - "core.date": "Огноо", - "core.day": "Өдөр", - "core.days": "Өдрүүд", - "core.decsep": ".", - "core.delete": "Устгах", - "core.deletedoffline": "Оффлайныг устгалаа", - "core.deleting": "Устгаж байна", - "core.description": "Тайлбар", - "core.desktop": "Ширээний", - "core.dfdaymonthyear": "Сар-Өдөр-Жил; СС-ӨӨ-ЖЖЖЖ", - "core.dfdayweekmonth": "өөө, Ө СССС", - "core.dffulldate": "өөөө, Ө СССС ЖЖЖЖ ц[:]мм А", - "core.dflastweekdate": "өөө", - "core.dfmediumdate": "ЛЛЛ", - "core.dftimedate": "ц[:]мм А", - "core.digitalminor": "Дижитал насанд хүрээгүй", - "core.digitalminor_desc": "Эцэг эх/асран хамгаалагчаасаа холбоо барихыг асууна уу:", - "core.discard": "Болих", - "core.dismiss": "Орхих", - "core.done": "Болсон", - "core.download": "Татах", - "core.downloaded": "Татаж авсан", - "core.downloading": "Татаж авч байна", - "core.edit": "Засварлах", - "core.editor.hidetoolbar": "Хэрэгслийн мөрийг нуух", - "core.editor.toggle": "Түлхүүр засварлагч", - "core.emptysplit": "Зүүн самбар хоосон эсвэл ачаалж байгаа бол энэхүү хуудас хоосон харагдах болно.", - "core.error": "Алдаа", - "core.errorchangecompletion": "Гүйцэтгэлийн статусыг солих үед алдаа гарлаа. Дахин оролдоно уу.", - "core.errordeletefile": "Файл устгахад алдаа гарлаа. Дахин оролдоно уу.", - "core.errordownloading": "Файл татаж авахад алдаа гарлаа.", - "core.errordownloadingsomefiles": "Файл татаж авахад алдаа гарлаа. Зарим файлууд дутуу байж магадгүй.", - "core.errorfileexistssamename": "Энэхүү нэртэй файл аль хэдийн байна.", - "core.errorinvalidform": "Маягт буруу өгөгдөл агуулж байна. Шаардлагатай бүх талбарыг бөглөн, өгөгдөл зөв эсэхийг шалгана уу.", - "core.errorinvalidresponse": "Хүчингүй хариу хүлээн авлаа. Алдаа байсаар байвал сайтынхаа админтай холбоо барина уу.", - "core.errorloadingcontent": "Агуулга ачаалахад алдаа гарлаа.", - "core.errorofflinedisabled": "Таны сайт дээр оффлайн хайлт хийх боломжгүй. Энэхүү апп-ыг ашиглахын тулд та интернетэд холбогдсон байх ёстой.", - "core.erroropenfilenoapp": "Файл нээхэд алдаа гарлаа: энэ төрлийн файлыг нээх апп олдохгүй байна.", - "core.erroropenfilenoextension": "Файл нээхэд алдаа гарлаа: файл өргөтгөлгүй байна.", - "core.erroropenpopup": "Энэ үйлдэл нь ил харагдах хэсгийг нээхээр оролдож байна. Энэ нь апп-д дэмжигдэхгүй байна.", - "core.errorrenamefile": "Файлыг нэрлэхэд алдаа гарлаа. Дахин оролдоно уу.", - "core.errorsomedatanotdownloaded": "Хэрэв та энэхүү үйл ажиллагааг татаж авсан бол гүйцэтгэл болон дата ашиглалтын шалтгаанаар татаж авах явцад зарим өгөгдөл татагдаагүй болохыг анхаарна уу.", - "core.errorsync": "Синхрончлол хийх явцад алдаа гарлаа. Дахин оролдоно уу.", - "core.errorsyncblocked": "Одоогийн байдлаар энэ {{$ a}} -г синхрончлол хийх боломжгүй байна. Дараа дахин оролдож үзнэ үү. Асуудал арилахгүй бол апп-г дахин ачаалж үзнэ үү.", - "core.explanationdigitalminor": "Энэ мэдээлэл нь таны нас зөвшөөрөгдсөн дижитал наснаас хэтэрсэн эсэхийг тодорхойлоход шаардлагатай болно. Энэхүү нас бол тухайн хувь хүн нөхцөл, болзол, тэдгээрийн өгөгдлийг хууль ёсны дагуу хадгалалт, боловсруулалтыг зөвшөөрдөг нас юм.", - "core.favourites": "Одоор тэмдэглэсэн", - "core.filename": "Файлын нэр", - "core.filenameexist": "{{$a}} нэртэй файл байна.", - "core.fileuploader.addfiletext": "Файл нэмэх", - "core.fileuploader.audio": "Дуу", - "core.fileuploader.camera": "Камер", - "core.fileuploader.confirmuploadfile": "Та {{size}} байршуулах гэж байна. Та үргэлжлүүлэхдээ итгэлтэй байна уу?", - "core.fileuploader.confirmuploadunknownsize": "Байршуулах хэмжээг тооцоолох боломжгүй байна. Та үргэлжлүүлэхдээ итгэлтэй байна уу?", - "core.fileuploader.errorcapturingaudio": "Дууг оруулахад алдаа гарлаа", - "core.fileuploader.errorcapturingimage": "Зургийг оруулахад алдаа гарлаа", - "core.fileuploader.errorcapturingvideo": "Видеог оруулахад алдаа гарлаа", - "core.fileuploader.errorgettingimagealbum": "Зургийн цомгоос зураг авахад алдаа гарлаа", - "core.fileuploader.errormustbeonlinetoupload": "Та файлууд байршуулахын тулд онлайн байх хэрэгтэй.", - "core.fileuploader.errornoapp": "Та энэхүү үйлдлийг гүйцэтгэх апп-ыг суулгаагүй байна.", - "core.fileuploader.errorreadingfile": "Файлыг уншихад алдаа гарлаа", - "core.fileuploader.errorwhileuploading": "Файл байршуулах үед алдаа гарлаа.", - "core.fileuploader.file": "Файл", - "core.fileuploader.fileuploaded": "Файлыг амжилттай байршууллаа.", - "core.fileuploader.invalidfiletype": "{{$a}} файлын төрлийг хүлээн авах боломжгүй", - "core.fileuploader.maxbytesfile": "{{$ A.file}} файл хэт том байна. Байршуулах хамгийн дээд хэмжээ нь {{$ a.size}} байна.", - "core.fileuploader.more": "Дэлгэрэнгүй", - "core.fileuploader.photoalbums": "Зургийн цомгууд", - "core.fileuploader.readingfile": "Файлыг уншиж байна", - "core.fileuploader.readingfileperc": "Файлыг уншиж байна: {{$a}}%", - "core.fileuploader.selectafile": "Файл сонгох", - "core.fileuploader.uploadafile": "Файл байршуулах", - "core.fileuploader.uploading": "Байршуулж байна", - "core.fileuploader.uploadingperc": "Байршуулж байна: {{$a}}%", - "core.fileuploader.video": "Видео", - "core.filter": "Шүүлтүүр", - "core.folder": "Хавтас", - "core.forcepasswordchangenotice": "Үргэлжлүүлэхийн тулд нууц үгээ өөрчлөх шаардлагатай.", - "core.fulllistofcourses": "Бүх курс", - "core.fullnameandsitename": "{{бүтэннэр}} ({{сайтыннэр}})", - "core.grades.average": "Дундаж", - "core.grades.gradeitem": "Зүйлийг дүгнэ", - "core.grades.grades": "Дүнгүүд", - "core.grades.lettergrade": "Үсгэн дүн", - "core.grades.nogradesreturned": "Буцаагдсан дүн байхгүй", - "core.grades.weight": "Чанар", - "core.group": "Бүлэг", - "core.groupsseparate": "Бүлгийг тусгаарлах", - "core.groupsvisible": "Харагдах бүлэг", - "core.h5p.offlinedisabled": "Энэхүү сайт нь H5P багцуудыг татаж авахыг зөвшөөрдөггүй.", - "core.h5p.play": "H5P багцыг тоглуулах", - "core.hasdatatosync": "{{$a}} синхрончлол хийхийн тулд оффлайн дататай байна.", - "core.help": "Тусламж", - "core.hide": "Нуух", - "core.hour": "Цаг", - "core.hours": "Цаг", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Зураг", - "core.imageviewer": "Зураг харах", - "core.info": "Мэдээлэл", - "core.lastaccess": "Сүүлд хандсан", - "core.lastdownloaded": "Сүүлийн татагдсан", - "core.lastmodified": "Сүүлд засварласан", - "core.lastsync": "Сүүлийн синхрончлол хийгдсэн", - "core.list": "Жагсаалт", - "core.listsep": ";", - "core.loading": "Ачааллаж байна", - "core.loadmore": "Илүү ачаалах", - "core.location": "Байрлал", - "core.login.auth_email": "Цахим шуудангийн хаягт суурилсан бүртгэл", - "core.login.authenticating": "Баталгаажуулж байна", - "core.login.cancel": "Болих", - "core.login.changepassword": "Нууц үг өөрчлөх", - "core.login.changepasswordbutton": "Нууц үг өөрчлөх хуудсыг нээнэ үү", - "core.login.changepasswordhelp": "Хэрэв та нууц үгээ солиход асуудал гарвал, сайтынхаа админтай холбоо барина уу. \"Сайтын Зохион Байгуулагчид\" нь таны сургууль/их сургууль/, компани эсвэл сургалтын байгууллагын \"Moodle\"-г удирдаж буй хүмүүс юм. Хэрэв та тэдэнтэй хэрхэн холбоо барихаа мэдэхгүй байгаа бол багш/сургагч багштайгаа холбоо барина уу.", - "core.login.changepasswordinstructions": "Та апп-д өөрийн нууц үгийг өөрчлөх боломжгүй. Нууц үгээ өөрчлөхийн тулд дараах товч дээр дарж сайтыг веб хөтчөөр нээнэ үү. Апп-руу дахин чиглүүлэхгүй тул нууц үгээ сольсны дараагаар хөтчийг хаах хэрэгтэй гэдгийг анхаарна уу.", - "core.login.changepasswordlogoutinstructions": "Хэрэв та сайтыг өөрчлөх эсвэл гарахыг хүсвэл дараах товчийг дарна уу:", - "core.login.changepasswordreconnectinstructions": "Дараах товчлуур дээр дарж сайт руу дахин холбогдоно уу. (Хэрэв та нууц үгээ амжилттай солиогүй бол өмнөх дэлгэц рүү буцаж очихыг анхаарна уу).", - "core.login.confirmdeletesite": "Та {{sitename}} сайтыг устгахдаа итгэлтэй байна уу.", - "core.login.connect": "Холбогдох!", - "core.login.connecttomoodle": "\"Moodle\"-тэй холбогдох.", - "core.login.connecttomoodleapp": "Та ердийн \"Moodle\" сайт руу холбогдох гэж байна. Энэхүү сайтад хандахын тулд албан ёсны \"Moodle\" апп-ыг татаж авна уу.", - "core.login.connecttoworkplaceapp": "Та \"Moodle Workplace\" сайт руу холбогдох гэж байна. Энэ сайтад хандахын тулд \"Moodle Workplace\" програмыг татаж авна уу.", - "core.login.contactyouradministrator": "Цаашдын тусламжийн талаар сайтын админтай холбоо барина уу.", - "core.login.contactyouradministratorissue": "Өөрийн сайтын зохион байгуулагчдаас дараах асуудлыг шалгахыг хүснэ үү: {{$ A}}", - "core.login.createaccount": "Шинэ хэрэглэгч үүсгэх", - "core.login.createuserandpass": "Нэвтрэх шинэ хэрэглэгч нэр, нууц үг үүсгэх", - "core.login.credentialsdescription": "Нэвтрэхийн тулд хэрэглэгчийн нэр, нууц үгээ оруулна уу.", - "core.login.emailconfirmsent": "

                Таны {{$a}} хаяг руу е-мэйл явууллаа.

                Уг е-мэйлд таны бүртгүүлэх ажиллагааны талаар зааварыг илгээсэн.

                Хэрэв таньд асуудал гарвал сайтын админтай холбоо барина уу.

                ", - "core.login.emailconfirmsentnoemail": "

                Таны хаягаар имэйл илгээсэн байх ёстой.

                Таны бүртгэлийг дуусгахад энэ нь хялбар заавар агуулдаг.

                Хэрэв танд асуудал тулгарвал сайтын зохион байгуулагч админтай холбогдоорой.

                ", - "core.login.emailconfirmsentsuccess": "Баталгаажуулах имэйл амжилттай илгээгдсэн", - "core.login.emailnotmatch": "Имэйл таарахгүй байна.", - "core.login.erroraccesscontrolalloworigin": "Таны гүйцэтгэх гэж буй хөндлөн гарал үүсэл бүхий дуудлага цуцлагдлаа. https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium -г шалгана уу.", - "core.login.errordeletesite": "Энэхүү сайтыг устгах үед алдаа гарлаа. Дахин оролдоно уу.", - "core.login.errorexampleurl": "https://campus.example.edu URL нь зөвхөн жишээ URL юм, энэ бол жинхэнэ сайт биш юм. Сургууль эсвэл байгууллагынхаа сайтын URL хаягийг ашиглана уу. ", - "core.login.errorupdatesite": "Сайтын токенийг шинэчлэх үед алдаа гарлаа.", - "core.login.faqcannotconnectanswer": "Та сайтын зохион байгуулагч админтай уулзана уу.", - "core.login.faqcannotconnectquestion": "Би сайтын хаягийг зөв бичсэн боловч холбогдож чадахгүй байна.", - "core.login.faqsetupsiteanswer": "Өөрийн \"Moodle\" сайтыг үүсгэх өөр сонголт байгаа эсэхийг шалгахын тулд {{$ link}} сайт руу орно уу.", - "core.login.faqsetupsitelinktitle": "Эхэлцгээе.", - "core.login.faqsetupsitequestion": "Би өөрийн \"Moodle\" сайтаа оруулах хүсэлтэй байна.", - "core.login.faqtestappanswer": "Апп-ыг Moodle Demo сайт дээр туршиж үзэхийн тулд \"таны сайтын хаяг\" талбарт \"багш\" эсвэл \"оюутан\" гэж бичээд \"Холбогдох!\" товчийг дарна уу.", - "core.login.faqtestappquestion": "Би зүгээр л апп туршиж үзмээр байна, би юу хийж чадах вэ?", - "core.login.faqwhatisurlanswer": "

                Байгууллага, сургууль болгон өөрийн \"Moodle\" сайтын хувийн хаягтай.

                Холбогдохыг хүсэж буй \"Moodle\" сайтын хаягийг олохын тулд дараах зүйлсийг хийнэ үү:

                  < li> Веб хөтчийг нээгээд сургуулийн эсвэл байгууллагын \"Moodle\" сайтын нэвтрэх хуудас руу очно уу.
                1. Хуудасны дээд хэсэгт байрлах хаягийн талбар дээрээс та \"Moodle\" сайтын URL-г харах болно. Жишээ нь. \"campus.example.edu\". {{$ image}}
                2. хаягийг хуулаад (нэвтрэх хаяг/дараа нь гарч ирэх зүйлийг хуулсны хэрэггүй) үүнийг Moodle програм дээр буулгаад \"Холбоно уу!\" гэсэн товч дээр дарна уу.
                3. Одоо та хэрэглэгчийн нэр, нууц үгээ ашиглан сайтад нэвтрэх боломжтой
                4. ", - "core.login.faqwhatisurlquestion": "Миний \"Moodle\" сайтын URL нь юу вэ? Сургуулийнхаа сайтыг хэрхэн олох вэ?", - "core.login.findyoursite": "Өөрийн сайтаа олох", - "core.login.firsttime": "Анх удаа үзэж байна уу?", - "core.login.forcepasswordchangenotice": "Үргэлжлүүлэхийн тулд нууц үгээ өөрчлөх шаардлагатай.", - "core.login.forgotten": "Холбогдох нэр эсвэл нууц үгээ мартсан уу?", - "core.login.help": "Тусламж", - "core.login.helpmelogin": "

                  Дэлхий даяар олон мянган \"Moodle\" сайтууд байдаг. Энэхүү апп нь зөвхөн Мобайл апп-ын хандалтыг идэвхжүүлсэн \"Moodle\" сайтуудад холбогдох боломжтой.

                  Хэрэв та \"Moodle\" сайт руугаа холбогдож чадахгүй байгаа бол сайтынхаа зохион байгуулагч админтай холбоо барьж -г уншихыг хүсэх хэрэгтэй. \"http://docs.moodle.org/en/Mobile_app\" target = \"_blank\"> http://docs.moodle.org/en/Mobile_app

                  Апп-ыг шалгахын тулд \"Moodle\" demo сайтын төрөл багш эсвэл оюутан сайтын хаяг талбар дээр дараад Connect товчийг дарна уу..

                  ", - "core.login.instructions": "Заавар", - "core.login.invalidaccount": "Өөрийн нэвтрэх мэдээллээ шалгах эсвэл сайтын зохион байгуулагчаас сайтын тохиргоог шалгахыг хүснэ үү.", - "core.login.invaliddate": "Тохирохгүй огноо", - "core.login.invalidemail": "Буруу е-мэйл хаяг", - "core.login.invalidmoodleversion": "

                  \"Moodle\" сайтын хувилбар буруу байна. \"Moodle\" програм нь зөвхөн \"Moodle\" системийг {{$ a}} дэмждэг.

                  Та сайтынхаа зохион байгуулаг админуудтай холбоо барьж, \"Moodle\" системээ шинэчлэхийг тэднээс хүсэж болно.

                  \"Сайтын админ\" нь танай сургууль /их сургууль/ компани эсвэл сургалтын байгууллагын \"Moodle\"-г удирдаж буй хүмүүс юм. Хэрэв та тэдэнтэй хэрхэн холбоо барихаа мэдэхгүй байгаа бол багш, дасгалжуулагч нартайгаа холбоо барина уу.

                  ", - "core.login.invalidsite": "Сайтын URL буруу байна.", - "core.login.invalidtime": "Цаг буруу байна", - "core.login.invalidvaluemax": "Хамгийн их утга нь {{$ a}} байна", - "core.login.invalidvaluemin": "Хамгийн бага утга нь {{$ a}} байна", - "core.login.localmobileunexpectedresponse": "\"Moodle\" гар утасны нэмэлт функцуудыг шалгалт гэнэтийн хариу ирүүлсэн байна. Таны стандарт гар утасны үйлчилгээг ашиглан баталгаажуулах болно.", - "core.login.loggedoutssodescription": "Та дахин баталгаажуулах шаардлагатай. Та сайт руу хөтчийн цонхоор нэвтрэх хэрэгтэй.", - "core.login.login": "Холбогдох", - "core.login.loginbutton": "Нэвтрэх", - "core.login.logininsiterequired": "Та сайт руу хөтчийн цонхоор нэвтрэх хэрэгтэй.", - "core.login.loginsteps": "Сайн уу,\nОнлайн хичээлүүдийг үзэхийн тулд та системд хэрэглэгчээр бүртгүүлсэн байх шаардлагатай.\n\nХэрэглэгчээр бүртгүүлэхэд шаардлагатай алхамууд:\n
                    \n
                  1. Шинэ хэрэглэгч маягтыг өөрийн талаархи мэдээллээр бөглөнө.
                  2. \n
                  3. Таны цахим шуудангийн хаягт системээс захиа илгээгдэнэ.
                  4. \n
                  5. Та Таны цахим шуудангаа шалгаад зурвас доторхи холбоос дээр дарна.
                  6. \n
                  7. Таны бүртгүүлсэн хэрэглэгч нэвтрэх эрх идэвхижих ба та системд автоматаар холбогдсон байх болно.
                  8. \n
                  9. Одоо хамрагдах гэж байгаа курсээ сонгоно.
                  10. \n
                  11. Хэрэв \"бүртгэлийн түлхүүр\" асуувал тус курсын багшаас өгсөн түлхүүрийг өгнө. Ингэснээр уг курст бүртгэгдэнэ.
                  12. \n
                  13. Хэрэв \"бүртгэлийн түлхүүр\" асуухгүй бол танай багш таныг онлайн курсд өөрөө бүртгэх болно.
                  14. \n
                  15. Одооноос эхлэн та холбогдох нэр, нууц үгээ ашиглан хамрагдсан курс руугаа бүрэн хандалтаар холбогдоно.
                  16. \n
                  ", - "core.login.missingemail": "Е-мэйл хаяг дутуу байна", - "core.login.missingfirstname": "Оноосон нэр дутуу байна", - "core.login.missinglastname": "Эцэг/эхийн нэр бичээгүй байна", - "core.login.mobileservicesnotenabled": "Таны сайт дээр мобайл хандалтыг идэвхжүүлээгүй байна. Үүнийг идэвхжүүлсэн байх шаардлагатай гэж та үзэж байгаа бол сайтынхаа админтай холбоо барина уу.", - "core.login.mustconfirm": "Таныг дахин шалгах хэрэгтэй байна", - "core.login.newaccount": "Шинэ хэрэглэгч", - "core.login.notloggedin": "Та нэвтрэх шаардлагатай.", - "core.login.password": "нууц үг", - "core.login.passwordforgotten": "Мартагдсан нууц үг", - "core.login.passwordforgotteninstructions2": "Өөрийн нууц үгээ дахин шинэчлэхэд, өөрийн хэрэглэгчийн нэр эсвэл имэйл хаягийг доор оруулна уу. Бид таныг мэдээллийн сангаас олж чадвал, хэрхэн дахин нэвтрэх эрхтэй болох талаар зааварчилгаа бүхий имэйлийг таны имэйл хаяг руу илгээнэ.", - "core.login.passwordrequired": "Нууц үг шаардлагатай", - "core.login.policyaccept": "Би ойлгож, зөвшөөрч байна", - "core.login.policyagree": "Энэ сайтыг хэрэглэхийн тулд эдгээр нөхцөлүүдийг зөвшөөрөх ёстой. Та зөвшөөрч байна уу?", - "core.login.policyagreement": "Үйлчилгээний нөхцөлүүд", - "core.login.policyagreementclick": "Та эндээс Үйлчилгээний нөхцөлүүдийг уншина уу", - "core.login.profileinvaliddata": "Буруу утга", - "core.login.recaptchachallengeimage": "reCAPTCHA сорилтын зураг", - "core.login.recaptchaexpired": "Баталгаажуулах хугацаа дууссан байна. Аюулгүй байдлын асуултад дахин хариулна уу.", - "core.login.recaptchaincorrect": "Нууцлалын асуултын хариулт буруу байна.", - "core.login.reconnect": "Дахин холбогдох", - "core.login.reconnectdescription": "Таны баталгаажуулах токен хүчингүй эсвэл хугацаа нь дууссан байна. Та сайтад дахин холбогдох хэрэгтэй.", - "core.login.reconnectssodescription": "Таны баталгаажуулах токен хүчингүй эсвэл хугацаа нь дууссан байна. Та сайтад дахин холбогдох хэрэгтэй. Та сайт руу хөтчийн цонхоор нэвтрэх хэрэгтэй.", - "core.login.resendemail": "Имэйл дахин явуулах", - "core.login.searchby": "Хайх:", - "core.login.security_question": "Нууц асуулт", - "core.login.selectacountry": "Улс сонгох", - "core.login.selectsite": "Сайтаа сонгоно уу:", - "core.login.signupplugindisabled": "{{$ a}} нь идэвхжээгүй байна.", - "core.login.siteaddress": "Таны сайтын хаяг", - "core.login.sitehasredirect": "Таны сайт дор хаяж нэг HTTP дахин чиглүүлэлтийг агуулж байна. Апп дахин чиглүүлэлтийг дагаж чадахгүй байна. Энэ нь апп-ыг таны сайтад холбогдоход саад болж байж болох юм.", - "core.login.siteinmaintenance": "Таны сайт засварын горимд байна", - "core.login.sitepolicynotagreederror": "Сайтын бодлого тохирохгүй байна.", - "core.login.siteurl": "Сайтын URL", - "core.login.siteurlrequired": "Сайтын URL шаардлагатай жишээ нь http://www.yourmoodlesite.org ", - "core.login.startsignup": "Шинэ хэрэглэгч үүсгэх", - "core.login.stillcantconnect": "Холбогдож чадахгүй байна уу?", - "core.login.supplyinfo": "Өөрийнхөө тухай мэдээлэл өгнө үү", - "core.login.username": "Холбогдох нэр", - "core.login.usernameoremail": "Хэрэглэгчийн нэр эсвэл имэйл хаягийн аль нэгийг оруул", - "core.login.usernamerequired": "Хэрэглэгчийн нэр шаардлагатай", - "core.login.usernotaddederror": "Хэрэглэгч \"{{$a}}\" нэмэгдсэнгүй. Үл таних алдаа", - "core.login.visitchangepassword": "Та нууц үгээ солихын тулд сайтад зочилчхыг хүсэж байна уу?", - "core.login.webservicesnotenabled": "Таны сайтад веб үйлчилгээ идэвхжээгүй байна. Үүнийг идэвхжүүлсэн байх шаардлагатай бол сайтынхаа админтай холбоо барина уу.", - "core.lostconnection": "Таны баталгаажуулах токен хүчингүй эсвэл хугацаа нь дууссан байна. Та сайт руу дахин холбогдох хэрэгтэй болно.", - "core.mainmenu.changesite": "Сайт солих", - "core.mainmenu.help": "Тусламж", - "core.mainmenu.logout": "Гарах", - "core.mainmenu.website": "Вебсайт", - "core.maxsizeandattachments": "Шинэ файлын зөвшөөрөгдөх хамгийн том хэмжээ: {{$a.size}}, хавсаргаж оруулах файлын хязгаар: {{$a.attachments}}", - "core.min": "минут", - "core.mins": "минут", - "core.misc": "Бусад", - "core.mod_assignment": "Даалгавар (2.2)", - "core.mod_chat": "Чат", - "core.mod_choice": "Сонголт", - "core.mod_data": "Өгөгдлийн сан", - "core.mod_database": "Өгөгдлийн сан", - "core.mod_feedback": "Санал хүсэлт", - "core.mod_file": "Файл", - "core.mod_forum": "Форум", - "core.mod_glossary": "Түүвэр", - "core.mod_label": "Шошго", - "core.mod_lesson": "Хичээл", - "core.mod_quiz": "Шалгалт", - "core.mod_resource": "нөөц", - "core.mod_survey": "Судалгаа", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "үйлдвэрийн цех/тасаг", - "core.moduleintro": "Тодорхойлолт", - "core.more": "илүү", - "core.mygroups": "Миний бүлгүүд", - "core.name": "Нэр", - "core.needhelp": "Тусламж хэрэгтэй юу?", - "core.networkerroriframemsg": "Энэ агуулга нь оффлайн хэлбэрээр боломжгүй. Интернетэд холбогдоод дахин оролдоно уу.", - "core.networkerrormsg": "Сайттай холбогдоход асуудал гарсан. Холболтоо шалгаад дахин оролдоно уу.", - "core.never": "Ер үгүй", - "core.next": "Дараагийн", - "core.no": "Үгүй", - "core.nocomments": "Тайлбар алга", - "core.nograde": "Үнэлгээ байхгүй байна", - "core.none": "Аль нь ч биш", - "core.nooptionavailable": "Сонголт байхгүй байна", - "core.nopasswordchangeforced": "Та нууц үгээ өөрчлөхгүйгээр үргэлжлүүлэх боломжгүй.", - "core.nopermissionerror": "Уучлаарай, танд одоогоор үүнийг хийх зөвшөөрөл байхгүй байна", - "core.nopermissions": "Уучлаарай, танд одоохондоо ({{$a}}) үүнийг гүйцэтгэх эрх алга.", - "core.noresults": "Үр дүн байхгүй", - "core.notapplicable": "Үгүй", - "core.notenrolledprofile": "Энэ хэрэглэгч энэ курст элсээгүй тул энэ ийм товч намтар байхгүй байна.", - "core.notice": "Сануулга", - "core.notingroup": "Энэ үйл ажиллагааг харахын тулд та бүлэгийг гишүүн байх ёстой.", - "core.notsent": "Илгээгдээгүй байна", - "core.now": "одоо", - "core.nummore": "{{$ a}} ба бусад", - "core.numwords": "{{$a}} үг", - "core.offline": "Холбогдоогүй", - "core.ok": "ok", - "core.online": "Холбогдсон", - "core.openfullimage": "Бүрэн хэмжээний зургийг харуулахын тулд энд дарна уу", - "core.openinbrowser": "Хөтөч дээр нээх", - "core.pagea": "{{$a}} хуудас", - "core.paymentinstant": "хэрэглэх the товч доор -д цалин бас байх enrolled дотор minutes!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Утас", - "core.pictureof": "{{$a}}-н зураг", - "core.previous": "Өмнөх", - "core.proceed": "Үргэлжлүүлэх", - "core.pulltorefresh": "Шинэчлэхийн тулд татна уу", - "core.question.cannotdeterminestatus": "Статусыг тодорхойлж чадахгүй байна", - "core.question.errorattachmentsnotsupported": "Аппликешн хараахан хариултын файлд хавсаргахыг дэмжихгүй байна.", - "core.question.errorinlinefilesnotsupported": "Аппликешн нь шугаман дахь файлуудыг засварлахыг хараахан дэмждэггүй.", - "core.question.errorquestionnotsupported": "Энэхүү асуултын төрлийг апп дэмждэггүй: {{$ a}}.", - "core.question.howtodraganddrop": "Сонгохын тулд дараад дараа нь чирж оруулна уу.", - "core.question.partiallycorrect": "Хэсэг нь зөв", - "core.question.questionmessage": "Асуулт {{$a}}: {{$b}}", - "core.question.questionno": "{{$a}} асуулт", - "core.quotausage": "Та одоогоор {$ a-> total}} хязгаарыг {$ a-> ашигласан}} ашиглаж байна.", - "core.redirectingtosite": "Таныг сайтад чиглүүлэх болно.", - "core.refresh": "Дахин ачаалах", - "core.remove": "Устгах", - "core.removefiles": "Файлуудыг устгах {{$ a}}", - "core.required": "шаардах", - "core.requireduserdatamissing": "Энэхүү хэрэглэгч шаардлагатай профайлын мэдээлэл агуулаагүй байна. Өөрийн сайтын датаг оруулаад дахин оролдоно уу.
                  {{$ a}}", - "core.resourcedisplayopen": "Нээх", - "core.resources": "Нөөц материалууд", - "core.restore": "Сэргээх", - "core.restricted": "Хориотой", - "core.retry": "Дахин оролдоно уу", - "core.save": "Хадгалах", - "core.savechanges": "Өөрчлөлтийг хадгалах", - "core.search": "Хайх", - "core.searching": "Хайж байна", - "core.searchresults": "Хайх үр дүн", - "core.sec": "сек", - "core.secs": "secs", - "core.seemoredetail": "Энд дарж өшөө дэлгэрүүлэн үзнэ үү", - "core.selectacategory": "Категорио сонгоно уу", - "core.selectacourse": "Курс сонгох", - "core.selectagroup": "Бүлэг сонгох", - "core.send": "Илгээх", - "core.sending": "Илгээж байна", - "core.settings.about": "Тухай", - "core.settings.appsettings": "Апп тохиргоо", - "core.settings.appversion": "Апп хувилбар", - "core.settings.cannotsyncoffline": "оффлайн синк хийх боломжгүй.", - "core.settings.cannotsyncwithoutwifi": "Одоогийн тохиргоо нь зөвхөн Wi-Fi холбогдсон үед синк хийх боломжийг олгодог тул синк хийх боломжгүй байна. Wi-Fi сүлжээнд холбогдоно уу.", - "core.settings.colorscheme": "Өнгөний схем", - "core.settings.colorscheme-auto": "Автомат (системийн тохиргоо дээр үндэслэсэн)", - "core.settings.colorscheme-dark": "Бараан", - "core.settings.colorscheme-light": "Гэрэлтэй", - "core.settings.compilationinfo": "Эмхтгэсэн мэдээлэл", - "core.settings.copyinfo": "Төхөөрөмжийн мэдээллийг санах ойд хуулах", - "core.settings.cordovadevicemodel": "Кордова төхөөрөмжийн загвар", - "core.settings.cordovadeviceosversion": "Кордова төхөөрөмжийн OS хувилбар", - "core.settings.cordovadeviceplatform": "Кордова төхөөрөмжийн платформ", - "core.settings.cordovadeviceuuid": "Кордова төхөөрөмж UUID", - "core.settings.cordovaversion": "Кордова хувилбар", - "core.settings.currentlanguage": "Сонгосон хэл", - "core.settings.debugdisplaydescription": "Хэрэв идэвхжүүлсэн бол, алдааны модулиуд боломжтой бол алдааны тухай нэмэлт мэдээллийг харуулах болно.", - "core.settings.deletesitefiles": "Та '{{sitename}}' сайтаас татаж авсан файл, болон хадгалагдсан мэдээллийг устгахдаа итгэлтэй байна уу? Та апп-ыг оффлайн горимд ашиглах боломжгүй.", - "core.settings.deletesitefilestitle": "Сайтын файлыг устгах", - "core.settings.deviceinfo": "Төхөөрөмжийн мэдээлэл", - "core.settings.deviceos": "Төхөөрөмжийн үйлдлийн систем", - "core.settings.disableall": "Аливаа мэдэгдэлүүдийг түр хугацаанд хаах", - "core.settings.displayformat": "Дэлгэцийн формат", - "core.settings.enabledownloadsection": "Татаж авах хэсгийг идэвхжүүлэх", - "core.settings.enablefirebaseanalytics": "\"Firebase\" анализыг идэвхжүүлэх", - "core.settings.enablefirebaseanalyticsdescription": "Хэрэв идэвхжүүлсэн бол энэхүү апп нь үл мэдэгдэх өгөгдөл ашиглалтын мэдээллийг цуглуулах болно.", - "core.settings.enablerichtexteditor": "Текст засварлагчийг идэвхжүүлэх", - "core.settings.enablerichtexteditordescription": "Хэрэв идэвхжүүлсэн бол агуулга оруулах үед текст засварлагч бэлэн байх болно.", - "core.settings.enablesyncwifi": "Зөвхөн Wi-Fi холболттой үед синхрончлохыг зөвшөөрөх", - "core.settings.entriesincache": "Нөөц дөх {{$ a}} оролтууд", - "core.settings.errordeletesitefiles": "Сайтын файлыг устгах үед алдаа гарлаа.", - "core.settings.errorsyncsite": "Сайтын өгөгдлийг синхрончлол хийх үед алдаа гарлаа. Интернет холболтоо шалгаад дахин оролдоно уу.", - "core.settings.estimatedfreespace": "Тооцоолсон чөлөөт орон зай", - "core.settings.filesystemroot": "Файлын системийн үндэс", - "core.settings.fontsize": "Текстийн хэмжээ", - "core.settings.forcedsetting": "Энэ тохиргоог таны сайтын тохиргоогоор хүчээр оруулсан байна.", - "core.settings.general": "Ерөнхий", - "core.settings.language": "Хэл", - "core.settings.license": "GPL лиценз", - "core.settings.localnotifavailable": "Орон нутгийн мэдэгдэлүүд боломжтой", - "core.settings.locationhref": "Вебээр харах URL", - "core.settings.locked": "Цоожтой", - "core.settings.loggedin": "Онлайн", - "core.settings.loggedoff": "Онлайн биш", - "core.settings.navigatorlanguage": "Навигацын системийн хэл", - "core.settings.navigatoruseragent": "Навигацын системийн хэрэглэгчийн агент", - "core.settings.networkstatus": "Интернэт холболтын статус", - "core.settings.opensourcelicenses": "Нээлттэй эх үүсвэрийн лиценз", - "core.settings.preferences": "Тохиргоо", - "core.settings.privacypolicy": "Нууцлалын бодлого", - "core.settings.publisher": "Нийтлэгч", - "core.settings.pushid": "Дэлгэц дээр гарч ирэх мэдэгдлийн ID-г оруулна уу", - "core.settings.reportinbackground": "Алдааг автоматаар мэдээлэх", - "core.settings.screen": "Дэлгэцийн мэдээлэл", - "core.settings.settings": "Тохиргоо", - "core.settings.showdownloadoptions": "Татаж авах сонголтыг харуул", - "core.settings.siteinfo": "Сайтын мэдээлэл", - "core.settings.sites": "Сайт", - "core.settings.spaceusage": "Зай багтаамжийн хэрэглээ", - "core.settings.spaceusagehelp": "Сайтын хадгалагдсан мэдээллийг устгаснаар бүх сайтын оффлайн өгөгдлийг устгах болно. Энэхүү мэдээлэл нь танд оффлайн үед апп ашиглах боломжийг олгоно.", - "core.settings.synchronization": "Синхрончлол", - "core.settings.synchronizenow": "Одоо синхрончлол хийх", - "core.settings.synchronizenowhelp": "Сайтыг синхрончлол хийх нь хүлээгдэж буй өөрчлөлтүүд болон төхөөрөмж дээр хадгалагдсан бүх оффлайн үйлдлүүдийг илгээх бөгөөд мессеж, мэдэгдэл гэх мэт зарим өгөгдлийг синхрончлол хийх болно.", - "core.settings.syncsettings": "Синхрончлол хийх тохиргоо", - "core.settings.total": "Нийлбэр", - "core.settings.wificonnection": "Wi-Fi холболт", - "core.sharedfiles.chooseaccountstorefile": "Энэ файлыг хадгалахын тулд данс сонгоно уу.", - "core.sharedfiles.chooseactionrepeatedfile": "Ийм нэртэй файл байна. Та байгаа файлыг солих эсвэл \"{{$ a}}\" болгож нэрийг нь өөрчлөхийг хүсч байна уу?", - "core.sharedfiles.errorreceivefilenosites": "Хадгалсан сайт байхгүй байна. Апп-тай файл хуваалцахаас өмнө сайт нэмнэ үү.", - "core.sharedfiles.nosharedfiles": "Энэхүү сайтад хуваалцсан файл байхгүй байна.", - "core.sharedfiles.nosharedfilestoupload": "Энд байршуулах файл байхгүй байна. Хэрэв та өөр апп-аас файл байршуулахыг хүсэж байвал файлыг олоод \"Нээх\" товчийг дарна уу.", - "core.sharedfiles.rename": "Дахин нэрлэх", - "core.sharedfiles.replace": "Орлуулах", - "core.sharedfiles.sharedfiles": "Хуваалцсан файлууд", - "core.sharedfiles.successstorefile": "Файлыг амжилттай хадгалсан. Хувийн файлуудаа байршуулах эсвэл үйл ажиллагаанд ашиглах файлыг сонгоно уу.", - "core.show": "Харуулах", - "core.site": "Сайт", - "core.sitehome.sitehome": "Сайтын нүүр хуудас", - "core.sitehome.sitenews": "Сайтын мэдээ", - "core.sitemaintenance": "Энэ сайт засвартай байна", - "core.sizeb": "bytes", - "core.sizegb": "gb", - "core.sizekb": "kb", - "core.sizemb": "mb", - "core.sizetb": "ТВ", - "core.sorry": "Уучлаарай ...", - "core.sort": "Ангилах", - "core.sortby": "Ангилах", - "core.strftimedate": "%d %B %Y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.submit": "Илгээх", - "core.success": "Амжилт", - "core.tablet": "Таблет", - "core.tag.errorareanotsupported": "Энэхүү тэмдэглэгдсэн бүсийг апп дэмжихгүй байна.", - "core.tag.searchtags": "Шошгуудаас хайх", - "core.tag.tag": "Шошго", - "core.tag.tags": "Шошгууд", - "core.tag.warningareasnotsupported": "Апп-ууд дэмжигдэхгүй байгаа тул зарим тэмдэглэгдсэн талбайг харуулаагүй.", - "core.teachers": "Багш", - "core.thereisdatatosync": "Синхрончлол хийх оффлайн {{$a}} байгаа.", - "core.thisdirection": "ltr", - "core.time": "Цаг", - "core.timesup": "Цаг дууссан!", - "core.today": "Өнөөдөр", - "core.tryagain": "Дахин оролдох", - "core.twoparagraphs": "{{p1}}

                  {{p2}}", - "core.uhoh": "Өө!", - "core.unexpectederror": "Гэнэтийн алдаа. Аппликешнийг хаагаад дахин оролдоно уу.", - "core.unicodenotsupported": "Энэ сайт дээр зарим эможи дэмжигддэггүй. Зурвас илгээхэд ийм тэмдэгтүүд устах болно.", - "core.unicodenotsupportedcleanerror": "Юникодын тохиолдлыг цэвэрлэхэд хоосон текст олдсон байна", - "core.unknown": "Үл мэдэгдэх", - "core.unlimited": "Хязгааргүй", - "core.unzipping": "Задалж байна", - "core.updaterequired": "Апп-ын шинэчлэлт хийх шаардлагатай байна", - "core.updaterequireddesc": "Апп-аа {{$a}} хувилбар болгож шинэчилнэ үү", - "core.user": "Хэрэглэгч", - "core.user.address": "Хаяг", - "core.user.city": "Хот/дүүрэг", - "core.user.contact": "Харилцагч", - "core.user.country": "Улс", - "core.user.description": "Тайлбар", - "core.user.detailsnotavailable": "Энэхүү хэрэглэгчийн талаарх мэдээллийг та авах боломжгүй байна.", - "core.user.editingteacher": "Багш", - "core.user.email": "Цахим шуудангын хаяг", - "core.user.emailagain": "Цахим шуудан (дахин бичих)", - "core.user.errorloaduser": "Хэрэглэгчийг ачаалахад алдаа гарлаа.", - "core.user.firstname": "Нэр", - "core.user.interests": "Сонирхдог зүйлс", - "core.user.lastname": "Эцэг/эхийн нэр", - "core.user.manager": "Зохицуулагч ", - "core.user.newpicture": "Шинэ зураг", - "core.user.participants": "Оролцогч", - "core.user.phone1": "Утас", - "core.user.phone2": "Гар утас", - "core.user.roles": "Үүргүүд", - "core.user.sendemail": "И-мэйл", - "core.user.student": "Оюутан", - "core.user.teacher": "Засвар оруулдаггүй багш", - "core.user.webpage": "Вэб хуудас", - "core.userdeleted": "Энэ хэрэглэгч устгагдсан", - "core.userdetails": "Хэрэглэгчийн тухай дэлгэрэнгүй мэдээлэл", - "core.users": "Хэрэглэгч", - "core.view": "Харагдац", - "core.viewcode": "Код харах", - "core.vieweditor": "Засварлагчийг харах", - "core.viewembeddedcontent": "Суулгагдсан агуулгыг харах", - "core.viewprofile": "Хувийн мэдээлэл харах", - "core.warningofflinedatadeleted": "{{component}} '{{name}}' -с оффлайн өгөгдлийг устгав. {{error}}", - "core.whatisyourage": "Та хэдэн настай вэ?", - "core.wheredoyoulive": "Та аль оронд амьдардаг вэ?", - "core.whoissiteadmin": "Сайтын админ/зохион байгуулагч нар нь таны сургууль/их сургууль/компани эсвэл сургалтын байгууллагын \"Moodle\"-г удирдаж буй хүмүүс юм. Хэрэв та тэдэнтэй хэрхэн холбоо барихаа мэдэхгүй байгаа бол багш, сургагч багштайгаа холбоо барина уу.", - "core.whoops": "Хөөе!", - "core.whyisthishappening": "Яагаад ийм зүйл болж байна вэ?", - "core.whyisthisrequired": "Энэ яагаад шаардлагатай вэ?", - "core.wsfunctionnotavailable": "Веб үйлчилгээний функц боломжгүй байна.", - "core.year": "жил", - "core.years": "жилүүд", - "core.yes": "Тийм", - "core.youreoffline": "Та оффлайн горимд байна", - "core.youreonline": "Та онлайн эргэн ирлээ" -} \ No newline at end of file diff --git a/src/assets/lang/mr.json b/src/assets/lang/mr.json deleted file mode 100644 index 176c840d9..000000000 --- a/src/assets/lang/mr.json +++ /dev/null @@ -1,1073 +0,0 @@ -{ - "addon.block_activitymodules.pluginname": "सर्व एक्टिव्हीटी", - "addon.block_blogmenu.pluginname": "ब्लॉग मेनु", - "addon.block_blogtags.pluginname": "ब्लॉग टॅग", - "addon.block_calendarmonth.pluginname": "दिनदर्शिका", - "addon.block_calendarupcoming.pluginname": "येणारे प्रसंग", - "addon.block_newsitems.pluginname": "हल्लीची बातमी", - "addon.block_onlineusers.pluginname": "ऑनलाईन युजर्स", - "addon.block_recentactivity.pluginname": "चालू ऍक्टिविटी", - "addon.block_rssclient.pluginname": "RSS क्लायंट", - "addon.block_sitemainmenu.pluginname": "मुख्य मेनू", - "addon.block_tags.pluginname": "टॅग", - "addon.blog.blog": "ब्लॉग", - "addon.blog.noentriesyet": "येथील प्रवेशिका दृश्य नाहीत.", - "addon.blog.publishtonoone": "आपला(ड्राफ्ट)", - "addon.blog.publishtosite": "या साईटवरील कोणीही", - "addon.blog.publishtoworld": "या जगातील कोणीही", - "addon.calendar.allday": "संपुर्ण दिवस", - "addon.calendar.calendar": "दिनदर्शिका", - "addon.calendar.calendarevents": "दिनदर्शिका कार्यक्रम", - "addon.calendar.confirmeventdelete": "खात्रीपुर्वक प्रसंग मिटवायचा आहे का?", - "addon.calendar.courseevents": "कोर्सचा प्रसंग", - "addon.calendar.defaultnotificationtime": "सूचना वेळ", - "addon.calendar.deleteevent": "प्रसंग मिटवा", - "addon.calendar.durationminutes": "वेळ मिनिटांमध्ये", - "addon.calendar.durationnone": "कालमर्यादा शिवाय", - "addon.calendar.durationuntil": "पर्यंत", - "addon.calendar.editevent": "सपांदक प्रसंग", - "addon.calendar.errorloadevent": "कार्यक्रम लोड करताना त्रुटी.", - "addon.calendar.errorloadevents": "कार्यक्रम लोड करताना त्रुटी.", - "addon.calendar.eventduration": "काळ", - "addon.calendar.eventendtime": "बंद करण्याचा वेळ", - "addon.calendar.eventkind": "प्रसंगाचा प्रकार", - "addon.calendar.eventname": "नांव", - "addon.calendar.eventstarttime": "चालु करण्याचा वेळ", - "addon.calendar.fri": "शुक्र", - "addon.calendar.friday": "शुक्रवार", - "addon.calendar.groupevents": "एकत्र प्रसंग", - "addon.calendar.mon": "सोम", - "addon.calendar.monday": "सोमवार", - "addon.calendar.monthlyview": "महिने दाखवा", - "addon.calendar.newevent": "नविन प्रसंग", - "addon.calendar.noevents": "कोणतेही कार्यक्रम नाहीत", - "addon.calendar.repeateditall": "हा बदल सगल्यासाठी लागु करा", - "addon.calendar.repeateditthis": "बदल फक्त ह्या घटनेसाठी लागु करा", - "addon.calendar.repeatweeksl": "आठवडया नंतर येणारा,पुर्नपणे तयार असलेला", - "addon.calendar.sat": "शनि", - "addon.calendar.saturday": "शनिवार", - "addon.calendar.sun": "रवि", - "addon.calendar.sunday": "रविवार", - "addon.calendar.thu": "गुरु", - "addon.calendar.thursday": "गुरुवार", - "addon.calendar.today": "आज", - "addon.calendar.tomorrow": "उदया", - "addon.calendar.tue": "मंग", - "addon.calendar.tuesday": "मंगवार", - "addon.calendar.typecourse": "कोर्सेचा प्रसंग", - "addon.calendar.typegroup": "एकत्रित प्रसंग", - "addon.calendar.typeuser": "उपभोक्तयाचा प्रसंग", - "addon.calendar.upcomingevents": "येणारे प्रसंग", - "addon.calendar.userevents": "उपभोक्ताचे प्रसंग", - "addon.calendar.wed": "बुध", - "addon.calendar.wednesday": "बुधवार", - "addon.calendar.yesterday": "काल", - "addon.competency.errornocompetenciesfound": "कोणतीही कौशल्यं आढळली नाहीत", - "addon.competency.nocompetencies": "कोणतीही क्षमता नाहीत", - "addon.coursecompletion.complete": "पूर्ण", - "addon.coursecompletion.couldnotloadreport": "अभ्यासक्रम पूर्ण केल्याचे अहवाल लोड करणे शक्य नाही, कृपया नंतर पुन्हा प्रयत्न करा.", - "addon.coursecompletion.required": "गरजेचे आहे.", - "addon.coursecompletion.status": "दर्जा", - "addon.files.couldnotloadfiles": "फायलींची सूची लोड करणे शक्य नाही .", - "addon.files.emptyfilelist": "दर्शविण्यासाठी कोणतीही फाईल नाहीत.", - "addon.files.erroruploadnotworking": "दुर्दैवाने सध्या आपल्या साइटवर फायली अपलोड करणे शक्य नाही.", - "addon.files.files": "अनेक फाइल", - "addon.files.sitefiles": "साईटवरील फाईल्स", - "addon.messageoutput_airnotifier.processorsettingsdesc": "डिव्हाइसेस कॉन्फिगर करा", - "addon.messages.addcontact": "संपर्क भरा", - "addon.messages.blocknoncontacts": "माझ्या संपर्क यादीमध्ये नसलेल्या लोकांकडून येणारे सर्व नविन संदेश थांबवा", - "addon.messages.contactlistempty": "संपर्क यादी रिक्त आहे", - "addon.messages.contactname": "संपर्क नाव", - "addon.messages.contacts": "संपर्क", - "addon.messages.errordeletemessage": "संदेश हटवताना त्रुटी.", - "addon.messages.errorwhileretrievingcontacts": "सर्व्हरवरून संपर्क पुनर्प्राप्त करताना त्रुटी.", - "addon.messages.errorwhileretrievingdiscussions": "सर्व्हरवरून चर्चा पुनर्प्राप्त करताना त्रुटी.", - "addon.messages.errorwhileretrievingmessages": "सर्व्हरवरून संदेश पुनर्प्राप्त करताना त्रुटी.", - "addon.messages.message": "संदेश", - "addon.messages.messagenotsent": "संदेश पाठविला गेला नाही, कृपया नंतर पुन्हा प्रयत्न करा.", - "addon.messages.messages": "संदेश", - "addon.messages.newmessages": "नवीन संदेश", - "addon.messages.nomessagesfound": "संदेश सापडले नाहीत", - "addon.messages.nousersfound": "कोणतेही वापरकर्ते आढळले नाहीत", - "addon.messages.removecontact": "संपर्क काढुन टाका", - "addon.messages.removecontactconfirm": "आपल्या संपर्क यादीतून संपर्क काढला जाईल.", - "addon.messages.type_blocked": "अवरोधित केले", - "addon.messages.type_offline": "ऑफलाइन", - "addon.messages.type_online": "ऑनलाइन", - "addon.messages.type_search": "शोध परिणाम", - "addon.messages.type_strangers": "इतर", - "addon.messages.warningmessagenotsent": "वापरकर्ता {{user}} ला संदेश पाठवू शकला नाही. {{error}}", - "addon.mod_assign.acceptsubmissionstatement": "सबमिशन स्टेटमेंट स्वीकार करा.", - "addon.mod_assign.cannoteditduetostatementsubmission": "आपण अनुप्रयोगामध्ये एक सबमिशन जोडू किंवा संपादित करू शकत नाही कारण आम्ही साइटवरून सबमिशन स्टेटमेंट पुनर्प्राप्त करू शकलो नाही.", - "addon.mod_assign.cannotgradefromapp": "अॅपद्वारे काही ग्रेडिंग पद्धती अद्याप समर्थित नाहीत आणि सुधारल्या जाऊ शकत नाहीत.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "आपण अनुप्रयोगामध्ये ग्रेडिंगसाठी सबमिट करू शकत नाही कारण आम्ही साइटवरून सबमिशन स्टेटमेंट पुनर्प्राप्त करू शकलो नाही.", - "addon.mod_assign.erroreditpluginsnotsupported": "आपण अॅपमध्ये सबमिशन जोडू किंवा संपादित करू शकत नाही कारण काही प्लगिन संपादनासाठी समर्थित नाहीत:", - "addon.mod_assign.errorshowinginformation": "आम्ही सबमिशन माहिती प्रदर्शित करू शकत नाही", - "addon.mod_assign.feedbacknotsupported": "हा अभिप्राय अॅप्सद्वारे समर्थित नाही आणि यात सर्व माहिती असू शकत नाही", - "addon.mod_assign.grade": "श्रेणी", - "addon.mod_assign.gradenotsynced": "ग्रेड संकलित नाही", - "addon.mod_assign.notallparticipantsareshown": "सबमिशनशिवाय सहभागी नाहीत दर्शविलेले नाहीत", - "addon.mod_assign.submissionnotsupported": "हे सबमिशन अॅपद्वारे समर्थित नाही आणि यात सर्व माहिती असू शकत नाही", - "addon.mod_assign.userwithid": "आयडी वापरकर्ता {{id}}", - "addon.mod_assign.warningsubmissiongrademodified": "सबमिशन ग्रेड साइटवर सुधारित करण्यात आली.", - "addon.mod_assign.warningsubmissionmodified": "वापरकर्ता सबमिशन साइटवर सुधारित करण्यात आली.", - "addon.mod_chat.beep": "आवाज", - "addon.mod_chat.chatreport": "संभाषण सत्र", - "addon.mod_chat.currentusers": "सध्याचा उपभोक्ता", - "addon.mod_chat.enterchat": "संभाषणासाठी प्रवेश करा", - "addon.mod_chat.errorwhileconnecting": "चॅटशी कनेक्ट करताना त्रुटी.", - "addon.mod_chat.errorwhilegettingchatdata": "गप्पा डेटा मिळवताना त्रुटी", - "addon.mod_chat.errorwhilegettingchatusers": "गप्पा वापरकर्ते मिळविताना त्रुटी.", - "addon.mod_chat.errorwhileretrievingmessages": "सर्व्हरवरून संदेश पुनर्प्राप्त करताना त्रुटी.", - "addon.mod_chat.errorwhilesendingmessage": "संदेश पाठवताना त्रुटी.", - "addon.mod_chat.messages": "संदेश", - "addon.mod_chat.modulenameplural": "संभाषण", - "addon.mod_chat.mustbeonlinetosendmessages": "आपल्याला संदेश पाठविण्यासाठी ऑनलाइन असणे आवश्यक आहे", - "addon.mod_chat.nomessages": "आजुन पर्यत संदेश नाही", - "addon.mod_chat.viewreport": "पाठीमागील संभाषण सत्र दाखवण्यासाठी", - "addon.mod_choice.errorgetchoice": "निवड डेटा मिळवताना त्रुटी.", - "addon.mod_choice.expired": "क्षमा करा,ही कार्यक्षमता बंद आहे", - "addon.mod_choice.full": "पुर्णपणे", - "addon.mod_choice.modulenameplural": "निवडने", - "addon.mod_choice.noresultsviewable": "निकाल उपलब्द नाही", - "addon.mod_choice.notopenyet": "क्षमा करा,ही कार्यक्षमता आजुन पर्यत बंद आहे", - "addon.mod_choice.removemychoice": "माझी निवड रद्द करा", - "addon.mod_choice.responses": "प्रतिक्रीया", - "addon.mod_choice.responsesresultgraphdescription": "{{Number}}% वापरकर्त्यांनी पर्याय निवडला: {{text}}.", - "addon.mod_choice.resultsnotsynced": "परिणामांमध्ये आपला अंतिम प्रतिसाद समाविष्ट नाही कृपया त्यांना अद्यतनित करण्यासाठी समक्रमित करा.", - "addon.mod_choice.savemychoice": "निवड्लेले साठ्वा", - "addon.mod_choice.yourselection": "तुम्ही निवडलेले", - "addon.mod_data.addentries": "संक्षिप्त नोंदी", - "addon.mod_data.advancedsearch": "प्रगत शोध", - "addon.mod_data.alttext": "पर्यायी मजकूर", - "addon.mod_data.approve": "मान्यता", - "addon.mod_data.approved": "मान्यता", - "addon.mod_data.ascending": "उत्तरत्या क्रमाने", - "addon.mod_data.authorfirstname": "प्राधिकारी प्रथम नाव", - "addon.mod_data.authorlastname": "प्राधिकारी आंडनांव", - "addon.mod_data.confirmdeleterecord": "नोंद मिटवायचे आहे का?", - "addon.mod_data.descending": "चढत्या क्रमाने", - "addon.mod_data.emptyaddform": "तुम्हाला एकही क्षेत्र भरावयाची गरज नाही", - "addon.mod_data.errorapproving": "प्रविष्टी मंजूर किंवा अमान्य करण्यामध्ये त्रुटी", - "addon.mod_data.errordeleting": "नोंद हटविताना त्रुटी.", - "addon.mod_data.fields": "क्षेत्रे", - "addon.mod_data.menuchoose": "निवडा", - "addon.mod_data.modulenameplural": "डेटाबेसेस", - "addon.mod_data.more": "आधिक", - "addon.mod_data.nomatch": "जुळवणी सापडली नाही", - "addon.mod_data.norecords": "नोंद डेटाबेस नाही", - "addon.mod_data.other": "इतर", - "addon.mod_data.recordapproved": "नोंद मान्य आहे", - "addon.mod_data.recorddeleted": "नोंद मिटवा", - "addon.mod_data.resetsettings": "पुर्वत्तत फिल्टर", - "addon.mod_data.search": "शोध", - "addon.mod_data.selectedrequired": "सगळे निवडलेले आवश्यक आहे", - "addon.mod_data.single": "एकच पहा", - "addon.mod_data.timeadded": "वेळेमध्ये अधिक करा", - "addon.mod_data.timemodified": "वेळेला दुरुस्त करा", - "addon.mod_feedback.captchaofflinewarning": "कॅप्चासह अभिप्राय ऑफलाइन पूर्ण केले जाऊ शकत नाही, किंवा कॉन्फिगर केले जात नाही किंवा सर्व्हर बंद असल्यास.", - "addon.mod_feedback.feedback_submitted_offline": "हे अभिप्राय नंतर सबमिट करण्यासाठी जतन केले गेले आहे.", - "addon.mod_feedback.preview": "पुर्वावलोकन", - "addon.mod_folder.emptyfilelist": "दर्शविण्यासाठी कोणतीही फाईल नाहीत.", - "addon.mod_forum.advanced": "प्रगत", - "addon.mod_forum.errorgetforum": "फोरम डेटा मिळवताना त्रुटी", - "addon.mod_forum.errorgetgroups": "गट सेटिंग्ज प्राप्त करताना त्रुटी.", - "addon.mod_forum.forumnodiscussionsyet": "या फोरममध्ये अद्याप चर्चा झालेले कोणतेही मुद्दे नाहीत", - "addon.mod_forum.group": "गट", - "addon.mod_forum.numdiscussions": "{{Numdiscussions}} चर्चा", - "addon.mod_forum.numreplies": "{{Numreplies}} प्रत्युत्तरे", - "addon.mod_forum.refreshdiscussions": "चर्चा रीफ्रेश करा", - "addon.mod_forum.refreshposts": "चर्चा पोस्ट रीफ्रेश करा", - "addon.mod_forum.yourreply": "तुमचे उत्तर", - "addon.mod_glossary.browsemode": "नोंदी ब्राउझ करा", - "addon.mod_glossary.byalphabet": "वर्णानुक्रमाने", - "addon.mod_glossary.byauthor": "लेखकानुसार गट", - "addon.mod_glossary.bycategory": "श्रेणीनुसार गट", - "addon.mod_glossary.bynewestfirst": "नवीनतम प्रथम", - "addon.mod_glossary.byrecentlyupdated": "अलीकडेच अद्यन्वित", - "addon.mod_glossary.bysearch": "शोधा", - "addon.mod_glossary.cannoteditentry": "प्रविष्टी संपादित करू शकत नाही", - "addon.mod_glossary.entriestobesynced": "सिंक केलेल्या प्रविष्ट्या", - "addon.mod_glossary.entrypendingapproval": "ही प्रविष्टी मंजूरीसाठी प्रलंबित आहे.", - "addon.mod_glossary.errorloadingentries": "नोंदी लोड करताना त्रुटी आली", - "addon.mod_glossary.errorloadingentry": "नोंद लोड करताना एक त्रुटी आली.", - "addon.mod_glossary.errorloadingglossary": "शब्दकोशाचे लोड करताना त्रुटी आली.", - "addon.mod_glossary.noentriesfound": "नोंदी सापडल्या नाहीत.", - "addon.mod_glossary.searchquery": "शोध क्वेरी", - "addon.mod_imscp.showmoduledescription": "वर्णन दर्शवा", - "addon.mod_lesson.answer": "उत्तर", - "addon.mod_lesson.averagescore": "सरासरी गुण", - "addon.mod_lesson.averagetime": "सरासरी वेळ", - "addon.mod_lesson.branchtable": "शाखा टेबल", - "addon.mod_lesson.clusterjump": "संचातील न पाहीलेले प्रश्न", - "addon.mod_lesson.completed": "पुर्ण झाली.", - "addon.mod_lesson.congratulations": "आभारी आहोत-पाठाच्या शेवटी पोहोचलो.", - "addon.mod_lesson.continue": "चालू रहाणे.", - "addon.mod_lesson.defaultessayresponse": "कोर्सच्या सूचना देणार्‍याकडून तुमच्या निबंधाला श्रेणी देण्यात येईल.", - "addon.mod_lesson.detailedstats": "सविस्तर अंकी माहिती", - "addon.mod_lesson.didnotanswerquestion": "या प्रश्नाचे उत्तर दिलेले नाही.", - "addon.mod_lesson.displayofgrade": "श्रेणी दाखवा(विद्यार्थ्यांसाठी).", - "addon.mod_lesson.enterpassword": "कृपया,पासवर्ड द्या.", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "तुम्ही कोणत्याही प्रश्नाचे उत्तर दिलेले नाही.तुम्हाला या पाठासाठी 0 गुण मिळाले आहेते.", - "addon.mod_lesson.errorprefetchrandombranch": "हा पाठ यादृच्छिक सामग्री पृष्ठावर उडी मारतो, वेबवर सुरू होईपर्यंत अॅपमध्ये प्रयत्न करणे शक्य नाही.", - "addon.mod_lesson.errorreviewretakenotlast": "हा प्रयत्न यापुढे पुनरावलोकन केला जाऊ शकत नाही कारण दुसरा प्रयत्न संपला आहे", - "addon.mod_lesson.finishretakeoffline": "हा प्रयत्न ऑफलाइन संपला आहे", - "addon.mod_lesson.firstwrong": "दुर्दैवीपणे हा गुण तुम्हाला मिळू शकणार नाही कारण तुमचे उत्तर अयोग्य होते.शिकण्याचा आस्वाद घेण्यासाठी तुम्हाला तर्क करायला आवडेल का(पण त्याचे काहीही गुण जमा होणार नाहीत)?", - "addon.mod_lesson.grade": "श्रेणी", - "addon.mod_lesson.highscore": "उच्चतम गुण", - "addon.mod_lesson.hightime": "उच्चतम वेळ", - "addon.mod_lesson.leftduringtimed": "तुमच्या या पाठातील काही गोष्टी राहील्या आहेत.
                  कृपया पाठ पुन्हा सुरू करण्यासाठी 'सुरू करा' वर क्लिक करा.", - "addon.mod_lesson.leftduringtimednoretake": "तुमच्या या पाठातील काही गोष्टी राहील्या आहेत.तुम्हाला
                  पाठ पुन्हा सुरू करण्यासाठी परवानगी नाही.", - "addon.mod_lesson.lessonmenu": "पाठाचा मेनु", - "addon.mod_lesson.lessonstats": "पाठाची सांख्यिक माहिती", - "addon.mod_lesson.loginfail": "लॉग-ईन चुकले आहे,कृपया पुन्हा प्रयत्न करा.", - "addon.mod_lesson.lowscore": "कमी गुण", - "addon.mod_lesson.lowtime": "कमी काळ", - "addon.mod_lesson.maximumnumberofattemptsreached": "अधिकाधीक प्रयत्न केले-पुढच्या पानावर जात आहे.", - "addon.mod_lesson.modattemptsnoteacher": "विद्यार्थ्यांच्या आढावा फक्त विद्यार्थ्यांसाठीच काम करतो.", - "addon.mod_lesson.modulenameplural": "पाठ", - "addon.mod_lesson.noanswer": "उत्तर दीलेले नाही,कृपया पाठीमागे जाऊन उत्तर दाखल करा.", - "addon.mod_lesson.nolessonattempts": "ह्या पाठासाठी प्रयत्न केले गेले नाहीत.", - "addon.mod_lesson.notcompleted": "पुर्ण केलेले नाही", - "addon.mod_lesson.or": "किंवा", - "addon.mod_lesson.overview": "आढावा", - "addon.mod_lesson.preview": "आढावा", - "addon.mod_lesson.question": "प्रश्न", - "addon.mod_lesson.rawgrade": "कच्ची श्रेणी", - "addon.mod_lesson.reports": "अहवाल", - "addon.mod_lesson.response": "प्रतिसाद", - "addon.mod_lesson.retakefinishedinsync": "ऑफलाइन प्रयत्न सिंक्रोनाईज झाला. आपण याचे पुनरावलोकन करू इच्छिता?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "आढावा", - "addon.mod_lesson.reviewlesson": "पाठाचा आढावा", - "addon.mod_lesson.reviewquestionback": "होय,मला पुन्हा प्रयत्न करायला आवडेल.", - "addon.mod_lesson.reviewquestioncontinue": "नाही,मला फक्त पुढच्या प्रश्नाकडे जायचे आहे.", - "addon.mod_lesson.secondpluswrong": "पुर्णपणे नाही.तुम्हाला पुन्हा प्रयत्न करायला आवडेल का?", - "addon.mod_lesson.teacherongoingwarning": "मिळणारे गुण फक्त विद्यार्थ्यासाठीच दाखविण्यात येतील.मिळालेले गुण पाहाण्यासाठी विद्यार्थी म्हणून लॉग-ईन व्हा.", - "addon.mod_lesson.teachertimerwarning": "टायमर फक्त विद्यार्थ्यासाठीच काम करते ते पाहाण्यासाठी विद्यार्थी म्हणून लॉग-ईन व्हा.", - "addon.mod_lesson.thatsthecorrectanswer": "हेच बरोबर उत्तर आहे.", - "addon.mod_lesson.thatsthewronganswer": "हे चुकिचे उत्तर आहे.", - "addon.mod_lesson.timeremaining": "शिल्लक वेळ", - "addon.mod_lesson.timetaken": "वेळ घेतला.", - "addon.mod_lesson.unseenpageinbranch": "शाखेतील न पाहीलेला प्रश्न", - "addon.mod_lesson.warningretakefinished": "प्रयत्न वेबसाइटवर पूर्ण झाले.", - "addon.mod_lesson.welldone": "कौशल्यपुर्ण केले!", - "addon.mod_lesson.youhaveseen": "तुम्ही ह्या पाठाची एकापेक्षा अधिक पाने पाहिली आहेत.
                  तुम्ही पाहिलेल्या शेवटच्या पानापासून तुम्हाला सुरूवात करायची आहे का?", - "addon.mod_lesson.youranswer": "तुमचे उत्तर", - "addon.mod_lti.errorgetlti": "मॉड्यूल डेटा प्राप्त करताना त्रुटी.", - "addon.mod_lti.errorinvalidlaunchurl": "लाँच URL वैध नाही.", - "addon.mod_lti.launchactivity": "क्रियाकलाप सुरू करा", - "addon.mod_page.errorwhileloadingthepage": "पृष्ठ सामग्री लोड करताना त्रुटी.", - "addon.mod_quiz.attemptfirst": "पहीला प्रयत्न", - "addon.mod_quiz.attemptlast": "शेवटचा प्रयत्न", - "addon.mod_quiz.attemptquiznow": "आता चाचणी परीक्षा द्या", - "addon.mod_quiz.cannotsubmitquizdueto": "या क्विझ प्रयत्नांना खालील कारणांमुळे सादर करता येत नाही:", - "addon.mod_quiz.comment": "टिप्पणी", - "addon.mod_quiz.completedon": "यावेळी पूर्ण झाले", - "addon.mod_quiz.confirmclose": "तुम्ही हा प्रयत्न बंद करत आहात. तुम्ही एकदा हा प्रयत्न बंद केला की नंतर उत्तरे बदलू शकणार नाही.", - "addon.mod_quiz.confirmcontinueoffline": "हा प्रयत्न {{$ a}} पासून सिंक्रोनाईज केला गेला नाही. तेव्हापासून आपण दुसर्या डिव्हाइसमध्ये हा प्रयत्न चालू ठेवल्यास, आपण डेटा गमावू शकता.", - "addon.mod_quiz.confirmleavequizonerror": "उत्तरे जतन करताना त्रुटी आली आपल्याला खात्री आहे की आपण क्विझ सोडू इच्छिता?", - "addon.mod_quiz.continueattemptquiz": "शेवटचा प्रयत्न चालू ठेवा", - "addon.mod_quiz.continuepreview": "शेवटचा प्रेव्ह्यु चालू ठेवा", - "addon.mod_quiz.errorbehaviournotsupported": "अॅपमध्ये वर्तन समर्थित नाही कारण या क्विझला अॅप्लीकेशनमध्ये वापरता येणार नाही:", - "addon.mod_quiz.errordownloading": "आवश्यक डेटा डाउनलोड करताना त्रुटी", - "addon.mod_quiz.errorgetattempt": "प्रयत्न डेटा प्राप्त करताना त्रुटी.", - "addon.mod_quiz.errorgetquestions": "प्रश्न मिळवताना त्रुटी", - "addon.mod_quiz.errorgetquiz": "प्रश्नोत्तर डेटा मिळविताना त्रुटी.", - "addon.mod_quiz.errorparsequestions": "प्रश्न वाचताना एक त्रुटी आली कृपया एका वेब ब्राउझरमध्ये हा प्रश्न प्रयत्न करा.", - "addon.mod_quiz.errorquestionsnotsupported": "या क्विझला अॅप्लिकेटमध्ये प्रयत्न करणे शक्य नाही कारण त्यात अॅप्लिकेशन्सचे प्रश्न नाहीत.", - "addon.mod_quiz.errorrulesnotsupported": "या क्विझला अॅप्लिकेटमध्ये प्रयत्न करणे शक्य नाही कारण याचे ऍप्लिकेशन्स अॅप्लिकेशन्सला समर्थित नाही.", - "addon.mod_quiz.errorsaveattempt": "प्रयत्न डेटा जतन करताना त्रुटी आली", - "addon.mod_quiz.feedback": "प्रतीसाद", - "addon.mod_quiz.finishnotsynced": "समाप्त परंतु सिंक्रोनाइझ केलेले नाही", - "addon.mod_quiz.grade": "श्रेणी", - "addon.mod_quiz.gradeaverage": "सरासर श्रेणी", - "addon.mod_quiz.gradehighest": "उच्चतम श्रेणी", - "addon.mod_quiz.grademethod": "श्रेणीची पद्धत", - "addon.mod_quiz.marks": "गुण", - "addon.mod_quiz.modulenameplural": "चाचणी परीक्षा", - "addon.mod_quiz.noquestions": "आतापर्यंत कोणतेही प्रश्न भरलेले नाहीत", - "addon.mod_quiz.opentoc": "नेव्हिगेशन पॉप उघडा", - "addon.mod_quiz.overallfeedback": "एकूण प्रतीसाद", - "addon.mod_quiz.overdue": "थकलेले", - "addon.mod_quiz.preview": "प्रीव्ह्यु", - "addon.mod_quiz.previewquiznow": "चाचणी बघा", - "addon.mod_quiz.question": "प्रश्न", - "addon.mod_quiz.reattemptquiz": "चाचणी परीक्षा पून्हा द्या", - "addon.mod_quiz.requirepasswordmessage": "ही चाचणी परीक्षा देण्यासाठी तुम्हाला परीक्षेचा पासवर्ड माहीत असने गरजेचे आहे", - "addon.mod_quiz.review": "रीव्ह्यु", - "addon.mod_quiz.showall": "एका पानावरती सर्व प्रश्न दाखवा", - "addon.mod_quiz.startedon": "यावेळी सुरू केले", - "addon.mod_quiz.submitallandfinish": "सर्व भरा आणि शेवट करा", - "addon.mod_quiz.summaryofattempts": "तुमच्या अगोदरच्या प्रयत्नांचा सारांश", - "addon.mod_quiz.timeleft": "शिल्लक वेळ", - "addon.mod_quiz.timetaken": "लागलेला वेळ", - "addon.mod_quiz.warningattemptfinished": "ऑफलाइन प्रयत्न टाकून साइटवर पूर्ण केल्यानुसार काढून टाकला किंवा आढळला नाही.", - "addon.mod_quiz.warningdatadiscarded": "काही ऑफलाइन उत्तरे टाकण्यात आली कारण प्रश्न ऑनलाइन सुधारित करण्यात आले होते.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "प्रयत्न अपूर्ण आहे कारण काही ऑफलाइन उत्तरे काढून टाकण्यात आली. कृपया आपल्या उत्तरांचे पुनरावलोकन करा आणि पुन्हा प्रयत्न पुन्हा सबमिट करा.", - "addon.mod_resource.errorwhileloadingthecontent": "सामग्री लोड करताना त्रुटी.", - "addon.mod_resource.modulenameplural": "साधने", - "addon.mod_resource.openthefile": "फाईल उघडा", - "addon.mod_scorm.asset": "मौल्यवान वस्तू", - "addon.mod_scorm.assetlaunched": "मौल्यवान वस्तू बघीतल्या", - "addon.mod_scorm.attempts": "प्रयत्न", - "addon.mod_scorm.averageattempt": "साधारण प्रयत्न", - "addon.mod_scorm.browse": "आयोग", - "addon.mod_scorm.browsed": "ब्राउज केलेले", - "addon.mod_scorm.browsemode": "आयोग पद्धती", - "addon.mod_scorm.cannotcalculategrade": "ग्रेड मोजता आले नाही.", - "addon.mod_scorm.completed": "पूर्ण झालेले", - "addon.mod_scorm.contents": "घटक", - "addon.mod_scorm.dataattemptshown": "हा डेटा प्रयत्न क्रमांक {{number}} च्या मालकीचा असतो.", - "addon.mod_scorm.enter": "प्रवेश करा", - "addon.mod_scorm.errorcreateofflineattempt": "एक नवीन ऑफलाइन प्रयत्न तयार करताना त्रुटी आली कृपया पुन्हा प्रयत्न करा.", - "addon.mod_scorm.errordownloadscorm": "SCORM डाउनलोड करताना त्रुटी: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "SCORM डेटा मिळवताना त्रुटी.", - "addon.mod_scorm.errorinvalidversion": "क्षमस्व, अनुप्रयोग केवळ SCORM 1.2 चे समर्थन करतो.", - "addon.mod_scorm.errornotdownloadable": "आपल्या मूडल साइटमध्ये SCORM पॅकेजचे डाउनलोड अक्षम केले आहे. कृपया आपल्या मूडल साइट प्रशासकाशी संपर्क साधा.", - "addon.mod_scorm.errornovalidsco": "या SCORM ला लोड करण्यासाठी एक दृश्यमान SCO नाही.", - "addon.mod_scorm.errorpackagefile": "क्षमस्व, अनुप्रयोग केवळ ZIP पॅकेजचे समर्थन करतो", - "addon.mod_scorm.errorsyncscorm": "सिंक्रोनाइझ करताना त्रुटी आली कृपया पुन्हा प्रयत्न करा.", - "addon.mod_scorm.failed": "अपयशी", - "addon.mod_scorm.firstattempt": "पहीला प्रयत्न", - "addon.mod_scorm.gradeaverage": "सरासर श्रेणी", - "addon.mod_scorm.gradehighest": "उच्चतम श्रेणी", - "addon.mod_scorm.grademethod": "श्रेणीची पद्धत", - "addon.mod_scorm.gradesum": "श्रेणीची बेरीज", - "addon.mod_scorm.highestattempt": "जास्तित जास्त प्रयत्न", - "addon.mod_scorm.incomplete": "अपूर्ण", - "addon.mod_scorm.lastattempt": "शेवटचा प्रयत्न", - "addon.mod_scorm.modulenameplural": "SCORMs/AICCs", - "addon.mod_scorm.newattempt": "नविन प्रयत्न सुरू करा", - "addon.mod_scorm.notattempted": "प्रयत्न न केलेले", - "addon.mod_scorm.offlineattemptnote": "या प्रयत्नात डेटा समक्रमित केला गेला नाही.", - "addon.mod_scorm.offlineattemptovermax": "हा प्रयत्न पाठविला जाऊ शकत नाही कारण आपण जास्तीत जास्त प्रयत्नांना मागे टाकले आहे", - "addon.mod_scorm.organizations": "संघटना", - "addon.mod_scorm.passed": "यशस्वी", - "addon.mod_scorm.reviewmode": "रीव्ह्यु पद्धती", - "addon.mod_scorm.score": "गुण", - "addon.mod_scorm.scormstatusnotdownloaded": "हे SCORM डाउनलोड झाले नाही. आपण ते उघडता तेव्हा ते स्वयंचलितपणे डाउनलोड केले जाईल.", - "addon.mod_scorm.scormstatusoutdated": "शेवटचे डाउनलोड झाल्यापासून हे SCORM सुधारित केले गेले आहे. आपण ते उघडता तेव्हा ते स्वयंचलितपणे डाउनलोड केले जाईल.", - "addon.mod_scorm.suspended": "स्थगित", - "addon.mod_scorm.warningofflinedatadeleted": "प्रयत्न {{number}} चा काही ऑफलाइन डेटा हटविला गेला आहे कारण तो नवीन प्रयत्नात तयार करता आला नाही.", - "addon.mod_scorm.warningsynconlineincomplete": "काही प्रयत्नांना साइटसह समक्रमित करणे शक्य नाही कारण शेवटचे ऑनलाइन प्रयत्न संपले नाहीत. कृपया प्रथम ऑनलाइन प्रयत्न समाप्त करा.", - "addon.mod_survey.cannotsubmitsurvey": "क्षमस्व, आपले सर्वेक्षण सबमिट करताना समस्या आली कृपया पुन्हा प्रयत्न करा.", - "addon.mod_survey.errorgetsurvey": "सर्वेक्षण डेटा मिळवताना त्रुटी.", - "addon.mod_survey.results": "परिणाम", - "addon.mod_url.accessurl": "URL मध्ये प्रवेश करा", - "addon.mod_url.pointingtourl": "या स्रोताचे URL", - "addon.mod_wiki.errorloadingpage": "पृष्ठ लोड करताना त्रुटी आली", - "addon.mod_wiki.errornowikiavailable": "या विकीकडे अद्याप कोणतीही सामग्री नाही.", - "addon.mod_wiki.gowikihome": "विकीच्या होमला जा", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.subwiki": "उपविकि", - "addon.mod_wiki.titleshouldnotbeempty": "शीर्षक रिक्त असू नये", - "addon.mod_wiki.viewpage": "पृष्ठ पहा", - "addon.mod_wiki.wikipage": "विकी पृष्ठ", - "addon.notes.addnewnote": "नविन टिप्पणी भरा", - "addon.notes.coursenotes": "कोर्ससाठीच्या टिप्पणी", - "addon.notes.deleteconfirm": "ही टिप्पणी काढुन टाकायची का?", - "addon.notes.nonotes": "या प्रकारच्या टिप्पणी अजूनपर्यंत दिलेल्या नाहीत", - "addon.notes.note": "टिप्पणी", - "addon.notes.notes": "टिप्पण्या", - "addon.notes.personalnotes": "वैयक्तिक टिप्पणी", - "addon.notes.publishstate": "स्थीती", - "addon.notes.sitenotes": "साइटवरील टिप्पणी", - "addon.notes.userwithid": "वापरकर्ता या आयडी {{id}} सह", - "addon.notes.warningnotenotsent": "{{Course}} या कोर्ससाठी टीप (नोट्स) जोडू शकले नाहीत {{error}}", - "addon.notifications.errorgetnotifications": "सूचना मिळवताना त्रुटी", - "addon.notifications.notifications": "सूचना", - "addon.notifications.playsound": "ध्वनी प्ले करा", - "addon.notifications.therearentnotificationsyet": "कोणत्याही सूचना नाहीत", - "assets.countries.AD": "आंदोरा", - "assets.countries.AE": "अरब इमिरेटस समूह", - "assets.countries.AF": "अफगाणिस्तान", - "assets.countries.AG": "अंटिगुआ आणि बरबूडा", - "assets.countries.AI": "अंगुलिया", - "assets.countries.AL": "आल्बेनिया", - "assets.countries.AM": "आर्मेनिया", - "assets.countries.AO": "अँगोला", - "assets.countries.AQ": "अनटार्टिका", - "assets.countries.AR": "अर्जेटाईना", - "assets.countries.AS": "अमेरिकन समूह", - "assets.countries.AT": "ऑस्ट्रिया", - "assets.countries.AU": "ऑस्टेलिंया", - "assets.countries.AW": "अरूबा", - "assets.countries.AX": "आयलैड इसलैंड", - "assets.countries.AZ": "अझरबैजान", - "assets.countries.BA": "बोस्निया आणि हर्जेगोविना", - "assets.countries.BB": "बार्बाडोस", - "assets.countries.BD": "बांगलादेश", - "assets.countries.BE": "बेल्जियम", - "assets.countries.BF": "बर्किना फासो", - "assets.countries.BG": "बल्गेरिया", - "assets.countries.BH": "बहरैन", - "assets.countries.BI": "बुरुंडी", - "assets.countries.BJ": "बेनिन", - "assets.countries.BL": "संत बरटेलिमी", - "assets.countries.BM": "बर्म्युडा", - "assets.countries.BN": "ब्रुनेइ", - "assets.countries.BO": "बोलिव्हिया", - "assets.countries.BR": "ब्राझिल", - "assets.countries.BS": "ब्रम्हामण", - "assets.countries.BT": "भूतान", - "assets.countries.BV": "बहुत आइसलैड", - "assets.countries.BW": "बोत्स्वाना", - "assets.countries.BY": "बेलारूस", - "assets.countries.BZ": "बेलीझ", - "assets.countries.CA": "कॅनडा", - "assets.countries.CC": "कोकस आइसलैड", - "assets.countries.CD": "काँगो", - "assets.countries.CF": "मध्य आफ्रिकेचे प्रजासत्ताक", - "assets.countries.CG": "काँगो", - "assets.countries.CH": "स्वाझिलँड", - "assets.countries.CI": "कोट डवोरे", - "assets.countries.CK": "कूक आइसलैड", - "assets.countries.CL": "चिली", - "assets.countries.CM": "कमरून", - "assets.countries.CN": "चीन", - "assets.countries.CO": "कोलंबिया", - "assets.countries.CR": "कोस्टा रिका", - "assets.countries.CU": "क्युबा", - "assets.countries.CV": "केप व्हर्दे", - "assets.countries.CX": "ख्रिरसमस आइसलैड", - "assets.countries.CY": "सायप्रस", - "assets.countries.CZ": "चेक प्रजासत्ताक", - "assets.countries.DE": "जर्मनी", - "assets.countries.DJ": "द्जिबौती", - "assets.countries.DK": "डेन्मार्क", - "assets.countries.DM": "डॉमिनिका", - "assets.countries.DO": "डॉमिनिकन प्रजासत्ताक", - "assets.countries.DZ": "अल्जीरिया", - "assets.countries.EC": "इक्वेडोर", - "assets.countries.EE": "एस्टोनिया", - "assets.countries.EG": "इजिप्त", - "assets.countries.EH": "पश्चिम सहारा", - "assets.countries.ER": "इरिट्रिया", - "assets.countries.ES": "स्पेन", - "assets.countries.ET": "इथियोपिया", - "assets.countries.FI": "फिनलंड", - "assets.countries.FJ": "फिजी", - "assets.countries.FK": "फकलैड आइसलैड (मालदिव)", - "assets.countries.FM": "माक्रोनशिया, फेडेरेटस राज्य", - "assets.countries.FO": "फरोइ आइसलैड", - "assets.countries.FR": "फ्रान्स", - "assets.countries.GA": "गियाना", - "assets.countries.GB": "अमेरिकेची संयुक्त संस्थाने", - "assets.countries.GD": "ग्रेनेडा", - "assets.countries.GE": "जॉर्जिया", - "assets.countries.GF": "फेन्स गियाना", - "assets.countries.GG": "गुर्नेसी", - "assets.countries.GH": "घाना", - "assets.countries.GI": "गिब्रालतर", - "assets.countries.GL": "ग्रीनलैंड", - "assets.countries.GM": "गांबिया", - "assets.countries.GN": "गिनी", - "assets.countries.GP": "गुडेलोप", - "assets.countries.GQ": "इक्वेशियल गुनिया", - "assets.countries.GR": "ग्रीस", - "assets.countries.GS": "दक्षिण गोरिया आणि दक्षिण सैंनविश्य आइसलैंड", - "assets.countries.GT": "ग्वाटेमाला", - "assets.countries.GU": "गुआम", - "assets.countries.GW": "गिनी बिसाउ", - "assets.countries.GY": "गियाना", - "assets.countries.HK": "हाँगकाँग", - "assets.countries.HM": "ह्रड आइसलैंड आणि मॅडोनल्ड आइसलैंड", - "assets.countries.HN": "होनडोरस", - "assets.countries.HR": "क्रो‌एशिया", - "assets.countries.HT": "हैती", - "assets.countries.HU": "हंगेरी", - "assets.countries.ID": "इंडोनेशिया", - "assets.countries.IE": "आयर्लंड", - "assets.countries.IL": "इस्रायल", - "assets.countries.IM": "इसर्ले ऑफ मॅन", - "assets.countries.IN": "भारत", - "assets.countries.IO": "बिटिश भारतीय ओसीयन", - "assets.countries.IQ": "इराक", - "assets.countries.IR": "इराण", - "assets.countries.IS": "आइसलँड", - "assets.countries.IT": "इटली", - "assets.countries.JE": "जरसी", - "assets.countries.JM": "जमैंका", - "assets.countries.JO": "जॉर्डन", - "assets.countries.JP": "जपान", - "assets.countries.KE": "केनिया", - "assets.countries.KG": "कझाकस्तान", - "assets.countries.KH": "कोमोडीया", - "assets.countries.KI": "किरिबति", - "assets.countries.KM": "कोमोरोस", - "assets.countries.KN": "संत किटटस आणि नेविस", - "assets.countries.KP": "कोरिया", - "assets.countries.KR": "कोरिया", - "assets.countries.KW": "कुवैत", - "assets.countries.KY": "सायमन आसलैंड", - "assets.countries.KZ": "कझाकस्तान", - "assets.countries.LA": "लिओ", - "assets.countries.LB": "लेबेनॉन", - "assets.countries.LC": "संत लुकिया", - "assets.countries.LI": "लिश्टनस्टाइन", - "assets.countries.LK": "श्रीलंका", - "assets.countries.LR": "लायबेरिया", - "assets.countries.LS": "लेसोथो", - "assets.countries.LT": "लिथुएनिया", - "assets.countries.LU": "लक्झेंबर्ग", - "assets.countries.LV": "लात्व्हिया", - "assets.countries.LY": "लिबयन अरब जमहिरिया", - "assets.countries.MA": "मोरोक्को", - "assets.countries.MC": "मोनॅको", - "assets.countries.MD": "मोल्दोव्हा", - "assets.countries.ME": "मॉन्टेनिग्रो", - "assets.countries.MF": "संत मार्टिंन", - "assets.countries.MG": "मादागास्कर", - "assets.countries.MH": "मर्शाल आइसलैंड", - "assets.countries.MK": "मॅकेडोनियो", - "assets.countries.ML": "माली", - "assets.countries.MM": "म्यानमार", - "assets.countries.MN": "मंगोलिया", - "assets.countries.MO": "मकाऊ", - "assets.countries.MP": "उत्तर मारिना आइसलैंड", - "assets.countries.MQ": "मार्टिनिक्यू", - "assets.countries.MR": "मॉरिटानिया", - "assets.countries.MS": "मांटसेरेट", - "assets.countries.MT": "माल्टा", - "assets.countries.MU": "मारिटियस", - "assets.countries.MV": "मालदीव", - "assets.countries.MW": "मलावी", - "assets.countries.MX": "मेक्सिको", - "assets.countries.MY": "मलेशिया", - "assets.countries.MZ": "मोझांबिक", - "assets.countries.NA": "नामिबिया", - "assets.countries.NC": "न्यु कॅलेडोनिया", - "assets.countries.NE": "निगार", - "assets.countries.NF": "नोरर्फोक्स आइसलैड", - "assets.countries.NG": "नायझेरीया", - "assets.countries.NI": "निकारगुआ", - "assets.countries.NL": "नैंदरलैड", - "assets.countries.NO": "नार्वे", - "assets.countries.NP": "नेपाळ", - "assets.countries.NR": "नेरु", - "assets.countries.NU": "निउ", - "assets.countries.NZ": "न्यूझीलैंड", - "assets.countries.OM": "ओसमन", - "assets.countries.PA": "पनामा", - "assets.countries.PE": "पेरु", - "assets.countries.PF": "फेन्स पालिनेशिया", - "assets.countries.PG": "पापुआ न्यू गिनी", - "assets.countries.PH": "फिलिपाईन्स", - "assets.countries.PK": "पाकिस्तान", - "assets.countries.PL": "पोलंड", - "assets.countries.PM": "संत पिआरो आणि मीक्युलेन", - "assets.countries.PN": "पिटकेम", - "assets.countries.PR": "पोर्तोरिको", - "assets.countries.PS": "पॅलेस्टाईन", - "assets.countries.PT": "पोर्तुगाल", - "assets.countries.PW": "पलाऊ", - "assets.countries.PY": "पेराग्वे", - "assets.countries.QA": "क्यॅटर", - "assets.countries.RE": "एकत्र", - "assets.countries.RO": "रोमेनिया", - "assets.countries.RS": "सर्बिया", - "assets.countries.RU": "रशिया", - "assets.countries.RW": "र्‍वान्डा", - "assets.countries.SA": "सौदी अरेबिया", - "assets.countries.SB": "सोलोमन आइसलैड", - "assets.countries.SC": "सेशेल्स", - "assets.countries.SD": "सुदान", - "assets.countries.SE": "स्वीडन", - "assets.countries.SG": "सिंगापूर", - "assets.countries.SH": "संत हेलेनी", - "assets.countries.SI": "स्लोव्हेनिया", - "assets.countries.SJ": "स्वालब्रड आणि जान मायेन", - "assets.countries.SK": "स्लोव्हेकिया", - "assets.countries.SL": "सियेरा लिओन", - "assets.countries.SM": "सान मारिनो", - "assets.countries.SN": "सेनेगाल", - "assets.countries.SO": "सोमालिया", - "assets.countries.SR": "सुरिनाम", - "assets.countries.ST": "साओ टोमे व प्रिन्सिप", - "assets.countries.SV": "इआय सालवडर", - "assets.countries.SY": "सायरियन अरब सत्ताक", - "assets.countries.SZ": "स्वित्झर्लंड", - "assets.countries.TC": "तुर्क आणि कोकोस आइसलैंड", - "assets.countries.TD": "चाड", - "assets.countries.TF": "फ्रेन्च दक्षिणेत्तर", - "assets.countries.TG": "टोगो", - "assets.countries.TH": "थायलंड", - "assets.countries.TJ": "ताजिकिस्तान", - "assets.countries.TK": "तोकेलू", - "assets.countries.TL": "तिमार-लेस्टे", - "assets.countries.TM": "तुर्कमेनिस्तान", - "assets.countries.TN": "ट्युनिसिया", - "assets.countries.TO": "टोनगा", - "assets.countries.TR": "तुर्की", - "assets.countries.TT": "त्रिनिदाद आणि टोबॅगो", - "assets.countries.TV": "तुवालु", - "assets.countries.TW": "तायवान", - "assets.countries.TZ": "टांझानिया", - "assets.countries.UA": "युक्रेन", - "assets.countries.UG": "युगांडा", - "assets.countries.UM": "अमेरिका समुह बाजुची आइसलैड", - "assets.countries.US": "अमेरिकेची संयुक्त संस्थाने", - "assets.countries.UY": "उरुग्वे", - "assets.countries.UZ": "उझबेकिस्तान", - "assets.countries.VA": "हॉली सी (वटिकन शहर )", - "assets.countries.VC": "संत विंनसन्ट आणि ग्रेनाडियन्स", - "assets.countries.VE": "व्हेनेझुएला", - "assets.countries.VG": "विरगिन आइसलैड बिरटिश", - "assets.countries.VI": "विरगिन आइसलैड", - "assets.countries.VN": "व्हियेतनाम", - "assets.countries.VU": "वनातु", - "assets.countries.WF": "वॅलीस आणि फुटूना", - "assets.countries.WS": "समूह", - "assets.countries.YE": "येमन", - "assets.countries.YT": "मयोटे", - "assets.countries.ZA": "दक्षिण आफिका", - "assets.countries.ZM": "झांबिया", - "assets.countries.ZW": "झिम्बाब्वे", - "assets.mimetypes.application/msword": "वर्डचे माहीतीपत्र", - "assets.mimetypes.application/pdf": "PDF माहीतीपत्र", - "assets.mimetypes.application/vnd.ms-excel": "एक्सेलची स्प्रेडशीट", - "assets.mimetypes.application/vnd.ms-powerpoint": "पावरपॉइंटमधील सादरीकरण", - "assets.mimetypes.document/unknown": "फाइल", - "assets.mimetypes.text/plain": "टेक्स्ट फाइल", - "assets.mimetypes.text/rtf": "RTFचे माहीतीपत्र", - "core.accounts": "खाते", - "core.add": "जतन करा", - "core.all": "सर्व", - "core.allgroups": "सर्व समूह", - "core.allparticipants": "सर्व सहभागी", - "core.answer": "उत्तर", - "core.back": "पाठीमागे", - "core.block.blocks": "विभाग्", - "core.cancel": "रद्द करा", - "core.cannotconnect": "कनेक्ट करू शकत नाही: आपण योग्यरित्या URL टाइप केला असल्याचे आणि आपली साइट Moodle {{$a}} किंवा नंतर वापरत असल्याचे सत्यापित करा.", - "core.cannotdownloadfiles": "आपल्या मोबाईल सेवेमध्ये फाइल डाउनलोड करणे अक्षम केले आहे. कृपया, आपल्या साइट प्रशासकाशी संपर्क साधा.", - "core.captureaudio": "ऑडिओ रेकॉर्ड करा", - "core.capturedimage": "चित्र घेतले", - "core.captureimage": "छायाचित्र काढा", - "core.capturevideo": "व्हिडिओ रेकॉर्ड करा", - "core.category": "गट", - "core.choose": "निवडा", - "core.clearsearch": "शोध साफ करा", - "core.clicktoseefull": "संपूर्ण सामग्री पाहण्यासाठी क्लिक करा.", - "core.confirmcanceledit": "आपली खात्री आहे की आपण हे पृष्ठ सोडू इच्छिता? सर्व बदल गमावले जातील.", - "core.confirmloss": "तुम्हाला खात्री आहे? सर्व बदल गमावले जातील.", - "core.confirmopeninbrowser": "आपण ते ब्राउझरमध्ये उघडू इच्छिता?", - "core.contenteditingsynced": "आपण संपादित करत असलेली सामग्री समक्रमित केली गेली आहे.", - "core.contentlinks.chooseaccount": "खाते निवडा", - "core.contentlinks.chooseaccounttoopenlink": "यासह दुवा उघडण्यासाठी एक खाते निवडा.", - "core.contentlinks.confirmurlothersite": "हा दुवा दुसर्या साइटशी संबंधित आहे. आपण ते उघडू इच्छिता?", - "core.contentlinks.errornoactions": "या दुव्यासह कार्य करण्यासाठी क्रिया सापडली नाही.", - "core.contentlinks.errornosites": "हा दुवा हाताळण्यासाठी कोणतीही साइट आढळू शकली नाही.", - "core.continue": "चालु ठेवा", - "core.copiedtoclipboard": "क्लिपबोर्डवर मजकूर कॉपी केला", - "core.course": "कोर्स", - "core.course.activitydisabled": "आपल्या संस्थेने मोबाईल अॅपमध्ये ही गतिविधी अक्षम केली आहे.", - "core.course.activitynotyetviewableremoteaddon": "आपल्या संस्थेने एक प्लगइन स्थापित केला जो अद्याप समर्थित नाही.", - "core.course.activitynotyetviewablesiteupgradeneeded": "आपल्या संस्थेच्या मूडलच्या स्थापनेची आवश्यकता आहे.", - "core.course.allsections": "सर्व विभाग", - "core.course.askadmintosupport": "साइट प्रशासकाशी संपर्क साधा आणि त्यांना सांगा की आपण हे क्रियाकलाप मूडल मोबाईल अॅपसह वापरू इच्छिता.", - "core.course.confirmdeletemodulefiles": "आपली खात्री आहे की आपण या मॉड्यूल फायली हटवू इच्छिता?", - "core.course.confirmdownload": "आपण {{size}} डाउनलोड करणार आहात आपल्याला खात्री आहे की आपण सुरू ठेवू इच्छिता?", - "core.course.confirmdownloadunknownsize": "आम्ही डाउनलोडचे आकार गणना करण्यात अक्षम आहोत. आपण डाउनलोड करू इच्छिता याची आपल्याला खात्री आहे?", - "core.course.confirmpartialdownloadsize": "आपण कमीत कमी {{size}} डाउनलोड करणार आहात आपल्याला खात्री आहे की आपण सुरू ठेवू इच्छिता?", - "core.course.contents": "सामग्री", - "core.course.couldnotloadsectioncontent": "विभाग सामग्री लोड करणे शक्य नाही, कृपया नंतर पुन्हा प्रयत्न करा.", - "core.course.couldnotloadsections": "विभाग लोड करणे शक्य झाले नाही, कृपया नंतर पुन्हा प्रयत्न करा.", - "core.course.errordownloadingsection": "विभाग डाउनलोड करताना त्रुटी.", - "core.course.errorgetmodule": "मॉड्यूल डेटा प्राप्त करताना त्रुटी.", - "core.course.nocontentavailable": "याक्षणी कोणतीही सामग्री उपलब्ध नाही.", - "core.course.overriddennotice": "या क्रियेसाठी तुमची शेवटची श्रेणी ही स्वतः जूळॅविली होती", - "core.course.sections": "सेक्शनस", - "core.course.useactivityonbrowser": "आपण तरीही आपला डिव्हाइसेस ब्राउझर वापरून ते वापरू शकता", - "core.courses.allowguests": "हा कोर्स गेस्ट युजरसाठी प्रवेश देते", - "core.courses.availablecourses": "उपलब्ध असलेले कोर्स", - "core.courses.cannotretrievemorecategories": "{{$a}} स्तरापेक्षा अधिक श्रेणी सुधारली जाऊ शकत नाहीत.", - "core.courses.categories": "कोर्सचे गट", - "core.courses.confirmselfenrol": "आपली खात्री आहे की आपण या अभ्यासक्रमात आपली नोंदणी करू इच्छिता?", - "core.courses.courses": "कोर्सेस्", - "core.courses.enrolme": "मला नोंदवा", - "core.courses.errorloadcategories": "श्रेण्या लोड करताना त्रुटी आली", - "core.courses.errorloadcourses": "अभ्यासक्रम लोड करताना त्रुटी आली", - "core.courses.errorsearching": "शोधताना एक त्रुटी आली.", - "core.courses.errorselfenrol": "स्वतःवर नियंत्रण करताना त्रुटी आली", - "core.courses.filtermycourses": "माझे अभ्यासक्रम फिल्टर करा", - "core.courses.frontpage": "पहीले पान", - "core.courses.ignore": "दुर्लक्ष करा", - "core.courses.mycourses": "माझे कोर्सेस", - "core.courses.mymoodle": "माझ मूडल", - "core.courses.nocourses": "कोर्सबद्दलची माहीती दाखविण्यासाठी उपलब्ध नाही", - "core.courses.nocoursesyet": "ह्या वर्गासाठी कोर्सेस नाहीत", - "core.courses.notenrollable": "आपण या अभ्यासक्रमात आपली नोंदणी करू शकत नाही", - "core.courses.password": "नामांकन की", - "core.courses.paymentrequired": "या कोर्सच्या प्रवेशासाठी पैसे लागतील.", - "core.courses.paypalaccepted": "पेपल फीसची रक्कम स्वीकारली जाईल", - "core.courses.reload": "परत लोड करा.", - "core.courses.search": "शोध", - "core.courses.searchcourses": "कोर्सेस शोधा", - "core.courses.searchcoursesadvice": "आपण अतिथी म्हणून प्रवेश करण्यासाठी शोध अभ्यासक्रम बटण वापरू शकता किंवा त्यास अनुमती असलेल्या अभ्यासक्रमात प्रवेश करू शकता.", - "core.courses.selfenrolment": "स्वत: ची नोंदणी", - "core.courses.sendpaymentbutton": "फीसची रक्कम तुम्ही पेपल व्दारे पाठवा", - "core.courses.totalcoursesearchresults": "एकूण अभ्यासक्रम: {{$a}}", - "core.currentdevice": "वर्तमान डिव्हाइस", - "core.datastoredoffline": "डिव्हाइसमध्ये डेटा संचयित केला कारण तो पाठविला जाऊ शकत नाही हे नंतर स्वयंचलितपणे पाठविले जाईल.", - "core.date": "तारीख", - "core.day": "दिवस", - "core.days": "दिवस", - "core.decsep": ".", - "core.delete": "काढुन टाका", - "core.deleting": "हटवत आहे", - "core.description": "वर्णन", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.discard": "टाकून द्या", - "core.dismiss": "डिसमिस करा", - "core.downloading": "डाऊनलोड करीत आहे", - "core.emptysplit": "जर डावीकडील पॅनेल रिक्त असेल किंवा लोड होत असेल तर हे पृष्ठ रिक्त दिसून येईल", - "core.error": "चुका", - "core.errorchangecompletion": "पूर्ण स्थिती बदलताना त्रुटी आली. कृपया पुन्हा प्रयत्न करा.", - "core.errordeletefile": "फाइल हटवताना त्रुटी. कृपया पुन्हा प्रयत्न करा.", - "core.errordownloading": "फाइल डाउनलोड करताना त्रुटी", - "core.errordownloadingsomefiles": "मॉड्युल फायली डाउनलोड करताना त्रुटी. काही फायली गहाळ असू शकतात.", - "core.errorfileexistssamename": "या नावाची एक फाईल आधीपासून आहे", - "core.errorinvalidform": "फॉर्ममध्ये अवैध डेटा आहे कृपया सर्व आवश्यक फील्ड भरत असल्याचे आणि डेटा वैध असल्याचे सुनिश्चित करा.", - "core.errorinvalidresponse": "अवैध प्रतिसाद प्राप्त झाला त्रुटी कायम राहिल्यास कृपया आपल्या मूडल साइट प्रशासकाशी संपर्क साधा.", - "core.errorloadingcontent": "सामग्री लोड करताना त्रुटी.", - "core.erroropenfilenoapp": "फाइल उघडताना त्रुटी: या प्रकारची फाईल उघडण्यासाठी कोणताही अॅप आढळला नाही", - "core.erroropenfilenoextension": "फाइल उघडताना त्रुटी: फाईलमध्ये विस्तार नाही.", - "core.erroropenpopup": "हा क्रियाकलाप पॉपअप उघडण्याचा प्रयत्न करीत आहे. या अॅपमध्ये हे समर्थित नाही.", - "core.errorrenamefile": "फाइल पुनर्नामित करताना त्रुटी. कृपया पुन्हा प्रयत्न करा.", - "core.errorsync": "सिंक्रोनाइझ करताना त्रुटी आली कृपया पुन्हा प्रयत्न करा.", - "core.errorsyncblocked": "चालू असलेल्या प्रक्रियेमुळे हे {{$ a}} आता समक्रमित केले जाऊ शकत नाही. कृपया पुन्हा प्रयत्न करा. समस्या कायम राहिल्यास, अॅप रीस्टार्ट करून पहा.", - "core.filenameexist": "फाइल नाव आधीपासून अस्तित्वात आहे: {{$ a}}", - "core.fileuploader.audio": "ऑडिओ", - "core.fileuploader.camera": "कॅमेरा", - "core.fileuploader.confirmuploadfile": "आपण {{size}} अपलोड करणार आहात आपल्याला खात्री आहे की आपण सुरू ठेवू इच्छिता?", - "core.fileuploader.confirmuploadunknownsize": "आम्ही अपलोडचे आकार काढण्यास अक्षम आहोत. आपल्याला खात्री आहे की आपण सुरू ठेवू इच्छिता?", - "core.fileuploader.errorcapturingaudio": "ऑडिओ कॅप्चर करताना त्रुटी", - "core.fileuploader.errorcapturingimage": "प्रतिमा कॅप्चर करताना त्रुटी.", - "core.fileuploader.errorcapturingvideo": "व्हिडिओ कॅप्चर करताना त्रुटी.", - "core.fileuploader.errorgettingimagealbum": "अल्बममधून प्रतिमा मिळवताना त्रुटी.", - "core.fileuploader.errormustbeonlinetoupload": "तुम्हाला फाइल्स अपलोड करण्यासाठी ऑनलाईन असणे आवश्यक आहे.", - "core.fileuploader.errornoapp": "आपण ही क्रिया करण्यासाठी अनुप्रयोग स्थापित केलेले नाही", - "core.fileuploader.errorreadingfile": "फाईल वाचताना त्रुटी.", - "core.fileuploader.errorwhileuploading": "फाईल अपलोड करताना एक त्रुटी आली.", - "core.fileuploader.file": "फाईल", - "core.fileuploader.fileuploaded": "फाइल यशस्वीरित्या अपलोड केली गेली.", - "core.fileuploader.maxbytesfile": "{{$a.file}} फाइल खूप मोठी आहे. आपण अपलोड करू शकता ती कमाल आकार {{$a.size}} आहे.", - "core.fileuploader.more": "आधिक", - "core.fileuploader.photoalbums": "फोटो अल्बम", - "core.fileuploader.readingfile": "फाईल वाचत आहे", - "core.fileuploader.selectafile": "एक फाईल निवडा", - "core.fileuploader.uploadafile": "फाइल अपलोड करा", - "core.fileuploader.uploading": "अपलोड करत आहे", - "core.fileuploader.uploadingperc": "अपलोडहोत आहे : {{$a}}%", - "core.fileuploader.video": "व्हिडिओ", - "core.folder": "फोल्डर", - "core.forcepasswordchangenotice": "पुढे जाण्यासाठी तुम्ही तुमचा पासवर्ड बदलला पाहीजे", - "core.fulllistofcourses": "सर्व कोर्सेस", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "सरासर", - "core.grades.badgrade": "दिलेली श्रेणी अयोग्य आहे.", - "core.grades.feedback": "अभिप्राय", - "core.grades.grade": "श्रेणी", - "core.grades.gradeitem": "श्रेणी घटक", - "core.grades.grades": "श्रेणी", - "core.grades.lettergrade": "अक्षर श्रेणी", - "core.grades.nogradesreturned": "श्रेणी परत दिलेल्या नाहीत", - "core.grades.nooutcome": "उत्पादित नाही", - "core.grades.percentage": "टक्केवारी", - "core.grades.range": "मर्यादा", - "core.grades.rank": "क्रमांक", - "core.grades.weight": "वेट", - "core.group": "ग्रुप्", - "core.groupsseparate": "वेगळे ग्रुप", - "core.groupsvisible": "दिसणारे ग्रुप", - "core.hasdatatosync": "हे {{$ a}} मध्ये ऑफलाइन डेटा समक्रमित करणे आहे", - "core.help": "मदत", - "core.hide": "लपवा", - "core.hour": "तास", - "core.hours": "अनेक तास", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "प्रतिमा", - "core.imageviewer": "प्रतिमा दर्शक", - "core.info": "माहीती", - "core.lastaccess": "शेवटचा प्रवेश", - "core.lastdownloaded": "अंतिम डाउनलोड केलेले", - "core.lastmodified": "शेवटचा बदललेले", - "core.lastsync": "अंतिम संकालन", - "core.list": "यादी", - "core.listsep": ",", - "core.loadmore": "अजून लोड करा", - "core.location": "ठिकाण", - "core.login.auth_email": "ई-मेलवर आधरीत रेजीस्ट्रेशन", - "core.login.authenticating": "प्रमाणीकरण करीत आहे", - "core.login.cancel": "रद्द करा", - "core.login.changepassword": "पासवर्ड बदला", - "core.login.confirmdeletesite": "Are you sure you want to delete the site {{sitename}}?", - "core.login.connect": "कनेक्ट व्हा!", - "core.login.connecttomoodle": "मूडलला जोडणी करा", - "core.login.contactyouradministrator": "अधिक मदतीसाठी आपल्या साइट प्रशासकाशी संपर्क साधा", - "core.login.contactyouradministratorissue": "कृपया खालील समस्येची तपासणी करण्यासाठी प्रशासकाला विचारा: {{$a}}", - "core.login.createaccount": "माझे नविन खाते बनवा", - "core.login.createuserandpass": "तुमचा युजरनेम व पासवर्ड निवडा", - "core.login.credentialsdescription": "कृपया लॉग इन करण्यासाठी आपले वापरकर्तानाव आणि संकेतशब्द प्रदान करा.", - "core.login.emailconfirmsent": "

                  आपल्या पत्त्यावर ईमेल {{$a}}

                  येथे पाठवला गेला पाहीजे कारण त्यात आपली नोंदणी पूर्ण करण्यासाठी सुलभ सूचना आहेत.

                  जर आपल्याला समस्या येणे चालूच राहते, साइट प्रशासकाशी संपर्क साधा. ", - "core.login.emailnotmatch": "ईमेल जुळत नाहीत", - "core.login.erroraccesscontrolalloworigin": "आपण पार करण्याचा प्रयत्न करत असलेला क्रॉस-ओरिजिन कॉल नाकारला गेला आहे. कृपया https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium तपासा", - "core.login.errordeletesite": "ही साइट हटविताना त्रुटी आली. कृपया पुन्हा प्रयत्न करा.", - "core.login.errorupdatesite": "साइटचे टोकन अद्यतनित करताना एक त्रुटी आली.", - "core.login.firsttime": "तुम्ही इथे पहील्यांदा आहात का?", - "core.login.forcepasswordchangenotice": "पुढे जाण्यासाठी तुम्ही तुमचा पासवर्ड बदलला पाहीजे", - "core.login.forgotten": "युजरनेम किंवा पासवर्ड विसरला आहात?", - "core.login.help": "मदत", - "core.login.helpmelogin": "

                  जगभरातील हजारो मूडल साइट्स आहेत. हा अॅप केवळ मूडल साइटशी कनेक्ट करु शकतो ज्याने विशेषतः मोबाईल अॅप्स प्रवेश सक्षम केला आहे.

                  आपण आपल्या मूडल साइटशी कनेक्ट करू शकत नसल्यास आपण जिथे कनेक्ट करू इच्छिता त्या स्थानावर मूडल प्रशासकाशी संपर्क साधण्याची आणि http://docs.moodle.org/en/Mobile_app

                  वाचण्यासाठी त्यांना विचारा साइट पत्ता फील्डमध्ये मूडलच्या डेमो साइट प्रकार teacher किंवा student मध्ये site address कनेक्ट बटण क्लिक करा . ", - "core.login.instructions": "सूचना", - "core.login.invalidaccount": "कृपया आपले लॉगिन तपशील तपासा किंवा साइट कॉन्फिगरेशन तपासण्यासाठी आपल्या साइट प्रशासकास विचारा.", - "core.login.invaliddate": "चुकीचा दिनांक", - "core.login.invalidemail": "अयोग्य ई-मेल पत्ता", - "core.login.invalidmoodleversion": "अवैध मूडल आवृत्ती आवश्यक किमान आवृत्ती {{$a}} आहे.", - "core.login.invalidsite": "साइट URL अवैध आहे", - "core.login.invalidtime": "अवैध वेळ", - "core.login.invalidvaluemax": "The maximum value is {{$a}}", - "core.login.invalidvaluemin": "किमान मूल्य {{$a}} आहे", - "core.login.localmobileunexpectedresponse": "मूडल मोबाईल अतिरिक्त वैशिष्ट्ये तपासा अनपेक्षित प्रतिसाद परत केला, मानक मोबाइल सेवेचा वापर करून आपल्याला प्रमाणीकृत केले जाईल", - "core.login.loggedoutssodescription": "आपल्याला पुन्हा प्रमाणीकृत करावे लागेल आपल्याला ब्राउझर विंडोमध्ये साइटवर लॉग इन करणे आवश्यक आहे.", - "core.login.login": "लॉग-इन", - "core.login.loginbutton": "प्रवेश करा", - "core.login.logininsiterequired": "आपल्याला ब्राउझर विंडोमध्ये साइटवर लॉग इन करणे आवश्यक आहे.", - "core.login.missingemail": "ई-मेल पत्ता दिलेला नाही", - "core.login.missingfirstname": "नाव दिलेले नाही", - "core.login.missinglastname": "आडनाव दिलेले नाही", - "core.login.mobileservicesnotenabled": "मोबाइल सेवा आपल्या साइटवर सक्षम नाहीत. कृपया मोबाइल प्रवेश सक्षम असावा असे आपल्याला वाटत असल्यास आपल्या मूडल साइट प्रशासकाशी संपर्क साधा", - "core.login.mustconfirm": "तुम्हाला तुमच्या लॉग-ईनची खात्री करावी लागेल.", - "core.login.newaccount": "नवीन", - "core.login.notloggedin": "आपल्याला लॉग इन करणे आवश्यक आहे", - "core.login.password": "पासवर्ड", - "core.login.passwordforgotten": "विसरलेला पासवर्ड", - "core.login.passwordrequired": "पासवर्ड आवश्यक", - "core.login.policyaccept": "मला समजले आहे आणि मी सहमत आहे.", - "core.login.policyagree": "या साईटचा वापर करण्यासाठी तुम्हाला ह्या धोरणाशी सहमत असावेच लागेल.", - "core.login.policyagreement": "साईटच्या धोरणांचा करार", - "core.login.policyagreementclick": "साईटच्या धोरणांच्या करारांसाठी लिंक.", - "core.login.profileinvaliddata": "अयोग्य युजर", - "core.login.recaptchachallengeimage": "रीकॅप्चा आव्हान प्रतिमा", - "core.login.reconnect": "रीकनेक्ट करा", - "core.login.reconnectdescription": "आपले प्रमाणीकरण टोकन अवैध आहे किंवा कालबाह्य झाले आहे, आपल्याला साइटवर पुन्हा कनेक्ट करण्याची आवश्यकता आहे.", - "core.login.reconnectssodescription": "आपले प्रमाणीकरण टोकन अवैध आहे किंवा कालबाह्य झाले आहे, आपल्याला साइटवर पुन्हा कनेक्ट करण्याची आवश्यकता आहे. आपल्याला ब्राउझर विंडोमध्ये साइटवर लॉग इन करणे आवश्यक आहे.", - "core.login.selectacountry": "देश निवडा.", - "core.login.signupplugindisabled": "{{$a}} सक्षम नाही.", - "core.login.siteaddress": "साइट पत्ता", - "core.login.siteinmaintenance": "आपली साइट देखरेख मोडमध्ये आहे", - "core.login.sitepolicynotagreederror": "साइट धोरण मान्य नाही.", - "core.login.siteurl": "साइट URL", - "core.login.siteurlrequired": "साइट URL आवश्यक आहे, i.e http://www.yourmoodlesite.abc किंवा https: //www.yourmoodlesite.efg ", - "core.login.startsignup": "नविन खाते तयार करा", - "core.login.stillcantconnect": "तरीही कनेक्ट करू शकत नाही?", - "core.login.supplyinfo": "अधिक तपशील", - "core.login.username": "युजरनेम", - "core.login.usernameoremail": "युजरनेम किंवा ई-मेल पत्ता द्या", - "core.login.usernamerequired": "वापरकर्तानाव आवश्यक", - "core.login.usernotaddederror": "उपभोक्ताचा बनवता येणार नाही - चुक", - "core.login.visitchangepassword": "आपण संकेतशब्द बदलण्यासाठी साइटला भेट देऊ इच्छिता?", - "core.login.webservicesnotenabled": "आपल्या साइटवर वेब सेवा सक्षम नाहीत. कृपया मोबाइल प्रवेश सक्षम असावा असे आपल्याला वाटत असल्यास आपल्या मूडल साइट प्रशासकाशी संपर्क साधा.", - "core.lostconnection": "आपले प्रमाणीकरण टोकन अवैध आहे किंवा कालबाह्य झाले आहे, आपल्याला साइटशी पुन्हा कनेक्ट करणे आवश्यक आहे.", - "core.mainmenu.changesite": "साइट बदला", - "core.mainmenu.help": "मदत", - "core.mainmenu.logout": "लॉग-आउट", - "core.mainmenu.website": "वेबसाइट", - "core.min": "लहानान लहान", - "core.mins": "मिनस", - "core.misc": "वगळलेले", - "core.mod_assignment": "पेपर (2.2)", - "core.mod_chat": "संभाषण", - "core.mod_choice": "निवड", - "core.mod_data": "डेटाबेस", - "core.mod_database": "डेटाबेस", - "core.mod_file": "फाइल", - "core.mod_label": "शिक्का", - "core.mod_lesson": "पाठ", - "core.mod_quiz": "चाचणी परीक्षा", - "core.mod_resource": "साधन", - "core.mod_wiki": "Wiki", - "core.more": "आणखी", - "core.name": "नाव", - "core.networkerrormsg": "साइटवर कनेक्ट करताना समस्या आली. कृपया आपले कनेक्शन तपासा आणि पुन्हा प्रयत्न करा.", - "core.never": "नाही", - "core.next": "पुढचा", - "core.no": "नाही", - "core.nograde": "श्रेणी दिलेली नाहि", - "core.none": "काहिच नाही.", - "core.nopasswordchangeforced": "आपण आपला पासवर्ड न बदलता पुढे जाऊ शकत नाही.", - "core.noresults": "निकाल नाही.", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "युजर ह्या कोर्सचा सदस्य नसल्यामुळे त्याचे प्रोफाइल उपलब्ध नाही.", - "core.notice": "पूर्वसुचना", - "core.notingroup": "माफ करा, ही क्रिया बघण्यासाठी तुम्ही या ग्रुपचा भाग असणे गरजेचे आहे", - "core.notsent": "पाठविले नाही", - "core.now": "आता", - "core.offline": "ऑफलाइन", - "core.ok": "ठीक आहे.", - "core.online": "ऑनलाइन", - "core.openfullimage": "पूर्ण आकारात प्रतिमा प्रदर्शित करण्यासाठी येथे क्लिक करा", - "core.openinbrowser": "ब्राउझरमध्ये उघडा", - "core.paymentinstant": "पैसे भरण्यासाठी खाली दिलेले बटण वापरल्यानंतर तुम्ही काही मिनीटातच सदस्य व्हाल.", - "core.percentagenumber": "{{$a}}%", - "core.phone": "फोन", - "core.previous": "आधीचा", - "core.pulltorefresh": "रीफ्रेश करण्यासाठी खेचा", - "core.question.errorattachmentsnotsupported": "अर्ज अद्याप फाइल्स संलग्न करण्यास समर्थन देत नाही.", - "core.question.errorinlinefilesnotsupported": "अनुप्रयोग अद्याप इनलाइन फायली संपादित करण्यास समर्थन देत नाही", - "core.question.errorquestionnotsupported": "हा प्रश्न प्रकार अॅपद्वारे समर्थित नाही: {{$a}}", - "core.question.howtodraganddrop": "निवडण्यासाठी टॅप करा नंतर ड्रॉप करण्यासाठी टॅप करा.", - "core.question.questionmessage": "प्रश्न {{$a}}: {{$b}}", - "core.redirectingtosite": "आपल्याला साइटवर पुनर्निर्देशित केले जाईल.", - "core.refresh": "रिफ्रेश", - "core.remove": "काढून टाका.", - "core.required": "गरजेचे आहे.", - "core.requireduserdatamissing": "या वापरकर्त्याकडे काही आवश्यक प्रोफाइल डेटा नसतो. कृपया आपल्या मूडलमध्ये हा डेटा भरा आणि पुन्हा प्रयत्न करा.
                  {{$ a}}", - "core.resources": "साधने", - "core.restore": "पुन्हा साठवून ठेवणे", - "core.restricted": "मर्यादा आहेत.", - "core.retry": "पुन्हा प्रयत्न करा", - "core.savechanges": "बदल साठवून ठेवा", - "core.search": "शोध", - "core.searching": "शोधत आहे", - "core.searchresults": "निकाल शोधा.", - "core.sec": "सेकंद", - "core.secs": "सेकन्दस", - "core.settings.about": "विषयी", - "core.settings.cannotsyncoffline": "ऑफलाइन समक्रमित करू शकत नाही", - "core.settings.cannotsyncwithoutwifi": "समक्रमित करणे शक्य नाही कारण वर्तमान सेटिंग्ज केवळ वाय-फाय शी कनेक्ट केलेले असताना सिंक्रोनाइझ करण्याची अनुमती देतात. कृपया एका Wi-Fi नेटवर्कशी कनेक्ट करा", - "core.settings.cordovadevicemodel": "कॉर्डोव्हा डिव्हाइस मॉडेल", - "core.settings.cordovadeviceosversion": "कॉर्डोव्हा डिव्हाइस OS आवृत्ती", - "core.settings.cordovadeviceplatform": "कॉर्डोव्हा डिव्हाइस प्लॅटफॉर्म", - "core.settings.cordovadeviceuuid": "वेबसाइट uuid", - "core.settings.cordovaversion": "कॉर्डोवा आवृत्ती", - "core.settings.currentlanguage": "सध्याची भाषा", - "core.settings.debugdisplay": "चुकांचा शोध करताणाचे संदेश दाखवा.", - "core.settings.deletesitefiles": "आपली खात्री आहे की आपण साइट {{sitename}} 'वरून डाउनलोड केलेल्या फायली हटवू इच्छिता", - "core.settings.deletesitefilestitle": "साइट फायली हटवा", - "core.settings.deviceinfo": "डिव्हाइस माहिती", - "core.settings.deviceos": "डिव्हाइस OS", - "core.settings.displayformat": "प्रदर्शन स्वरूप", - "core.settings.enabledownloadsection": "डाउनलोड विभाग सक्षम करा", - "core.settings.enablerichtexteditor": "रिच टेक्स्ट एडिटर सक्षम करा", - "core.settings.enablerichtexteditordescription": "सक्षम असल्यास, एक रिच टेक्स्ट एडिटर त्या ठिकाणी दर्शविला जाईल ज्या त्यांना परवानगी देतात.", - "core.settings.enablesyncwifi": "केवळ तेव्हाच WiFi वर संकालनास अनुमती द्या", - "core.settings.errordeletesitefiles": "साइट फायली हटविताना त्रुटी.", - "core.settings.errorsyncsite": "साइट डेटा समक्रमित करताना त्रुटी, कृपया आपले इंटरनेट कनेक्शन तपासा आणि पुन्हा प्रयत्न करा.", - "core.settings.estimatedfreespace": "अंदाजे रिकामी जागा", - "core.settings.filesystemroot": "फाइलसिस्टम रूट", - "core.settings.general": "सामान्य", - "core.settings.language": "भाषा", - "core.settings.license": "GPL लायसेंस", - "core.settings.localnotifavailable": "स्थानिक सूचना उपलब्ध", - "core.settings.locationhref": "वेबदृश्य URL", - "core.settings.navigatorlanguage": "नेव्हिगेटर भाषा", - "core.settings.navigatoruseragent": "नेव्हिगेटर userAgent", - "core.settings.networkstatus": "इंटरनेट कनेक्शन स्थिती", - "core.settings.preferences": "पसंती", - "core.settings.privacypolicy": "गोपनीयता धोरण", - "core.settings.reportinbackground": "आपोआप त्रुटी कळवा", - "core.settings.settings": "सेटिंगस", - "core.settings.sites": "साईटस", - "core.settings.spaceusage": "स्पेसचा उपयोग", - "core.settings.synchronization": "समक्रमण", - "core.settings.synchronizenow": "आता समक्रमित करा", - "core.settings.syncsettings": "समक्रमण सेटिंग्ज", - "core.settings.total": "एकूण", - "core.settings.wificonnection": "WiFi कनेक्शन", - "core.sharedfiles.chooseaccountstorefile": "Choose an account to store the file in.", - "core.sharedfiles.chooseactionrepeatedfile": "या नावाची एक फाईल आधीपासून आहे आपण विद्यमान फाइल पुनर्स्थित किंवा \"{{$a}}\" वर पुनर्नामित करू इच्छिता?", - "core.sharedfiles.errorreceivefilenosites": "संचयित केलेल्या कोणत्याही साइट नाहीत कृपया अॅपसह फाइल सामायिक करण्यापूर्वी साइट जोडा", - "core.sharedfiles.nosharedfiles": "या साइटवर संचयित केलेल्या कोणत्याही सामायिक केलेल्या फायली नाहीत.", - "core.sharedfiles.nosharedfilestoupload": "येथे अपलोड करण्यासाठी आपल्याकडे एकही फाइल नाही. आपण दुसर्या अॅपमधून फाइल अपलोड करू इच्छित असाल तर ती फाइल शोधा आणि 'इन-इन' बटणावर क्लिक करा.", - "core.sharedfiles.rename": "पुनर्नामित करा", - "core.sharedfiles.replace": "पुनर्स्थित करा", - "core.sharedfiles.sharedfiles": "सामायिक केलेल्या फायली", - "core.sharedfiles.successstorefile": "फाइल यशस्वीरित्या संग्रहित केली. आता आपण ही फाईल आपल्या खाजगी फाइल्सवर अपलोड करण्यासाठी किंवा काही क्रियाकलापांमध्ये संलग्न करण्यासाठी ती निवडू शकता.", - "core.show": "दाखवा.", - "core.site": "साईट", - "core.sitehome.sitenews": "साईटवरील बातम्या", - "core.sitemaintenance": "साईट दुरुस्त केली जात आहे व सध्या अस्तित्त्वात नाही", - "core.sizeb": "बाईटस", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.sorry": "क्षमस्व ...", - "core.sortby": "संकलन", - "core.strftimedate": "%d %B %Y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedatetimeshort": "%d/%m/%y, %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.submit": "सबमीट", - "core.success": "यश", - "core.tablet": "टॅब्लेट", - "core.tag.searchtags": "टॅग शोधा", - "core.tag.tag": "टॅग", - "core.tag.tags": "टॅगस", - "core.teachers": "शिक्षक", - "core.thereisdatatosync": "समक्रमित करण्यासाठी ऑफलाइन {{$ a}} आहेत", - "core.thisdirection": "ltr", - "core.time": "वेळ", - "core.timesup": "वेळ संपली आहे", - "core.today": "आज", - "core.tryagain": "पुन्हा प्रयत्न करा", - "core.twoparagraphs": "{{p1}}

                  {{p2}}", - "core.uhoh": "ओह ओह!", - "core.unexpectederror": "अनपेक्षित त्रुटी कृपया पुन्हा प्रयत्न करण्यासाठी अनुप्रयोग बंद करा आणि पुन्हा उघडा", - "core.unicodenotsupported": "या साइटवर काही इमोजी समर्थित नाहीत. जेव्हा संदेश पाठविला जातो तेव्हा असे अक्षरे काढून टाकले जातील.", - "core.unicodenotsupportedcleanerror": "युनिकोड वर्ण साफ करताना रिक्त मजकूर सापडला.", - "core.unknown": "अज्ञात", - "core.unlimited": "अमर्याद", - "core.unzipping": "अनझिप चालू आहे", - "core.user": "युजर.", - "core.user.address": "पत्ता", - "core.user.city": "शहर/नगर्", - "core.user.contact": "संपर्क साधा", - "core.user.country": "देश", - "core.user.description": "वर्णन", - "core.user.details": "तपशील", - "core.user.detailsnotavailable": "या वापरकर्त्याचे तपशील आपल्यासाठी उपलब्ध नाहीत.", - "core.user.editingteacher": "शिक्षक", - "core.user.email": "ई-मेल पत्ता", - "core.user.emailagain": "ई-मेल(पुन्हा)", - "core.user.firstname": "पहीले नाव", - "core.user.interests": "आवडी", - "core.user.lastname": "आडनाव", - "core.user.manager": "व्यवस्थापक", - "core.user.newpicture": "नवीन चित्र", - "core.user.participants": "सहभागी", - "core.user.phone1": "फोन", - "core.user.phone2": "मोबाईल फोन", - "core.user.roles": "रोल्स", - "core.user.sendemail": "ईमेल", - "core.user.student": "विद्यार्थी", - "core.user.teacher": "शिक्षक जे काहिच बदल करू शकत नाहित", - "core.user.webpage": "वेब पान", - "core.userdeleted": "ह्या युजरचे खाते काढून टाकण्यात आले आहे.", - "core.users": "सर्व युजर", - "core.view": "पाहा.", - "core.warningofflinedatadeleted": "{{Component}} '{{name}}' चा ऑफलाइन डेटा हटविला गेला आहे. {{error}}", - "core.whoops": "अरेरे!", - "core.whyisthishappening": "असे का होत आहे?", - "core.wsfunctionnotavailable": "Webservice फंक्शन उपलब्ध नाही.", - "core.year": "वर्ष", - "core.years": "वर्षे", - "core.yes": "होय" -} \ No newline at end of file diff --git a/src/assets/lang/nl.json b/src/assets/lang/nl.json deleted file mode 100644 index 712e09ee8..000000000 --- a/src/assets/lang/nl.json +++ /dev/null @@ -1,2175 +0,0 @@ -{ - "addon.badges.alignment": "Uitlijning", - "addon.badges.badgedetails": "Badgedetails", - "addon.badges.badges": "Badges", - "addon.badges.bendorsement": "Goedkeuring", - "addon.badges.claimcomment": "Opmerking bij de goedkeuring", - "addon.badges.claimid": "URL claimen", - "addon.badges.contact": "Contact", - "addon.badges.dateawarded": "Uitgavedatum", - "addon.badges.expired": "Verlopen", - "addon.badges.expirydate": "Vervaldatum", - "addon.badges.imageauthoremail": "E-mail van afbeelding auteur", - "addon.badges.imageauthorname": "Naam van afbeelding auteur", - "addon.badges.imageauthorurl": "URL van afbeelding auteur", - "addon.badges.imagecaption": "Afbeeldingsbijschrift", - "addon.badges.issuancedetails": "Badge verloopt", - "addon.badges.issuerdetails": "Details uitgever", - "addon.badges.issueremail": "E-mail", - "addon.badges.issuername": "Naam uitgever", - "addon.badges.issuerurl": "URL uitgever", - "addon.badges.language": "Taal", - "addon.badges.noalignment": "Deze badge heeft geen externe vaardigheden of standaarden opgegeven.", - "addon.badges.nobadges": "Er zijn geen badges beschikbaar.", - "addon.badges.norelated": "Deze badge heeft geen bijbehorende badges.", - "addon.badges.recipientdetails": "Details ontvanger", - "addon.badges.relatedbages": "Gerelateerde badges", - "addon.badges.version": "Versie", - "addon.badges.warnexpired": "(deze badge is verlopen!)", - "addon.block_activitymodules.pluginname": "Activiteiten", - "addon.block_activityresults.pluginname": "Activiteitsresultaten", - "addon.block_badges.pluginname": "Nieuwe badges", - "addon.block_blogmenu.pluginname": "Blogmenu", - "addon.block_blogrecent.pluginname": "Recente blogitems", - "addon.block_blogtags.pluginname": "Blogtags", - "addon.block_calendarmonth.pluginname": "Kalender", - "addon.block_calendarupcoming.pluginname": "Binnenkort", - "addon.block_comments.pluginname": "Commentaar", - "addon.block_completionstatus.pluginname": "Status voltooien cursus", - "addon.block_glossaryrandom.pluginname": "Willekeurig woordenlijstitem", - "addon.block_learningplans.pluginname": "Studieplannen", - "addon.block_myoverview.all": "Alles (behalve verborgen)", - "addon.block_myoverview.allincludinghidden": "Alle", - "addon.block_myoverview.favourites": "Favoriet", - "addon.block_myoverview.future": "Toekomst", - "addon.block_myoverview.hiddencourses": "Verborgen", - "addon.block_myoverview.inprogress": "Actief", - "addon.block_myoverview.lastaccessed": "Laatst geopend", - "addon.block_myoverview.morecourses": "Meer cursussen", - "addon.block_myoverview.nocourses": "Geen cursussen", - "addon.block_myoverview.past": "Voltooid", - "addon.block_myoverview.pluginname": "Cursusoverzicht", - "addon.block_myoverview.shortname": "Korte naam", - "addon.block_myoverview.title": "Cursusnaam", - "addon.block_newsitems.pluginname": "Laatste mededelingen", - "addon.block_onlineusers.pluginname": "Gebruikers online", - "addon.block_privatefiles.pluginname": "Privébestanden", - "addon.block_recentactivity.pluginname": "Recente wijzigingen", - "addon.block_recentlyaccessedcourses.nocourses": "Geen onlangs geopende cursussen", - "addon.block_recentlyaccessedcourses.pluginname": "Onlangs geopende cursussen", - "addon.block_recentlyaccesseditems.noitems": "Geen onlangs geopende items", - "addon.block_recentlyaccesseditems.pluginname": "Onlangs geopende items", - "addon.block_rssclient.pluginname": "Externe RSS-feeds", - "addon.block_selfcompletion.pluginname": "Zelf voltooien", - "addon.block_sitemainmenu.pluginname": "Hoofdmenu", - "addon.block_starredcourses.nocourses": "Geen favoriete cursussen", - "addon.block_starredcourses.pluginname": "Favoriete cursussen", - "addon.block_tags.pluginname": "Tags", - "addon.block_timeline.duedate": "Uiterste inleverdatum", - "addon.block_timeline.next30days": "Volgende 30 dagen", - "addon.block_timeline.next3months": "Volgende 3 maanden", - "addon.block_timeline.next6months": "Volgende 6 maanden", - "addon.block_timeline.next7days": "Volgende 7 dagen", - "addon.block_timeline.nocoursesinprogress": "Geen lopende cursussen", - "addon.block_timeline.noevents": "Er zijn geen verwachte activiteiten", - "addon.block_timeline.overdue": "Achterstallig", - "addon.block_timeline.pluginname": "Tijdlijn", - "addon.block_timeline.sortbycourses": "Sorteren op cursussen", - "addon.block_timeline.sortbydates": "Sorteer op datums", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Blogitems", - "addon.blog.errorloadentries": "Fout bij het laden van blog-items.", - "addon.blog.linktooriginalentry": "Link naar het originele blogitem", - "addon.blog.noentriesyet": "Hier zijn geen zichtbare teksten", - "addon.blog.publishtonoone": "jezelf (klad)", - "addon.blog.publishtosite": "iedereen op deze site", - "addon.blog.publishtoworld": "de hele wereld", - "addon.blog.showonlyyourentries": "Toon enkel eigen items", - "addon.blog.siteblogheading": "Site blog", - "addon.calendar.allday": "Hele dag", - "addon.calendar.calendar": "Kalender", - "addon.calendar.calendarevent": "Kalendergebeurtenis", - "addon.calendar.calendarevents": "Kalendergebeurtenissen", - "addon.calendar.calendarreminders": "Kalendernotificaties", - "addon.calendar.categoryevents": "Categoriegebeurtenissen", - "addon.calendar.confirmeventdelete": "Wil je de activiteit {{$a}} echt wissen?", - "addon.calendar.confirmeventseriesdelete": "De gebeurtenis \"{{$a.name}}\" maakt deel uit van een reeks. Wil je alleen deze gebeurtenis verwijderen of alle {{$a.count}} gebeurtenissen in de reeks?", - "addon.calendar.courseevents": "Cursusactiviteiten", - "addon.calendar.currentmonth": "Deze maand", - "addon.calendar.daynext": "Volgende dag", - "addon.calendar.dayprev": "Vorige dag", - "addon.calendar.defaultnotificationtime": "Standaard notificatietijd", - "addon.calendar.deleteallevents": "Alle gebeurtenissen verwijderen", - "addon.calendar.deleteevent": "Wis activiteit", - "addon.calendar.deleteoneevent": "Deze gebeurtenis verwijderen", - "addon.calendar.durationminutes": "Duur in minuten", - "addon.calendar.durationnone": "Geen tijdsduur", - "addon.calendar.durationuntil": "Tot en met", - "addon.calendar.editevent": "Bewerk activiteit", - "addon.calendar.errorloadevent": "Fout bij het laden van de gebeurtenis.", - "addon.calendar.errorloadevents": "Fout bij het laden van de gebeurtenissen.", - "addon.calendar.eventcalendareventdeleted": "Kalendergebeurtenis verwijderd", - "addon.calendar.eventduration": "Tijdsduur", - "addon.calendar.eventendtime": "Eindigt", - "addon.calendar.eventkind": "Soort activiteit", - "addon.calendar.eventname": "Gebeurtenis titel", - "addon.calendar.eventstarttime": "Startuur", - "addon.calendar.eventtype": "Gebeurtenistype", - "addon.calendar.fri": "Vr", - "addon.calendar.friday": "Vrijdag", - "addon.calendar.gotoactivity": "Ga naar activiteit", - "addon.calendar.groupevents": "Groep", - "addon.calendar.invalidtimedurationminutes": "De tijdsduur in minuten is ongeldig. Geef een getal in groter dan nul of kies geen tijdsduur", - "addon.calendar.invalidtimedurationuntil": "De datum en tijd die je selecteerde voor de tijdsduur is voor de begintijd van de gebeurtenis. Verbeter dit voor je verdergaat.", - "addon.calendar.mon": "Ma", - "addon.calendar.monday": "Maandag", - "addon.calendar.monthlyview": "Per maand", - "addon.calendar.newevent": "Nieuwe activiteit", - "addon.calendar.noevents": "Er zijn geen gebeurtenissen", - "addon.calendar.nopermissiontoupdatecalendar": "Je hebt niet het recht om activiteiten in de kalender aan te passen.", - "addon.calendar.reminders": "Notificaties", - "addon.calendar.repeatedevents": "Herhaalde gebeurtenissen", - "addon.calendar.repeateditall": "Pas de wijzigingen ook toe op de andere {{$a}} gebeurtenissen in deze reeks herhalingen", - "addon.calendar.repeateditthis": "Pas de wijzigingen alleen op deze gebeurtenis toe", - "addon.calendar.repeatevent": "Herhaal deze gebeurtenis", - "addon.calendar.repeatweeksl": "Herhaal deze activiteit", - "addon.calendar.sat": "Za", - "addon.calendar.saturday": "Zaterdag", - "addon.calendar.setnewreminder": "Nieuwe notificatie instellen", - "addon.calendar.siteevents": "Site-gebeurtenissen", - "addon.calendar.sun": "Zo", - "addon.calendar.sunday": "Zondag", - "addon.calendar.thu": "Do", - "addon.calendar.thursday": "Donderdag", - "addon.calendar.today": "Vandaag", - "addon.calendar.tomorrow": "Morgen", - "addon.calendar.tue": "Di", - "addon.calendar.tuesday": "Dinsdag", - "addon.calendar.typecategory": "Categoriegebeurtenis", - "addon.calendar.typeclose": "Sluit gebeurtenis", - "addon.calendar.typecourse": "Cursus", - "addon.calendar.typedue": "Einde gebeurtenis", - "addon.calendar.typegradingdue": "Te beoordelen gebeurtenis", - "addon.calendar.typegroup": "Groep", - "addon.calendar.typeopen": "Open gebeurtenis", - "addon.calendar.typesite": "Site", - "addon.calendar.typeuser": "Persoonlijk", - "addon.calendar.upcomingevents": "Binnenkort", - "addon.calendar.userevents": "Persoonlijk", - "addon.calendar.wed": "Wo", - "addon.calendar.wednesday": "Woensdag", - "addon.calendar.when": "Wanneer", - "addon.calendar.yesterday": "Gisteren", - "addon.competency.activities": "Activiteiten", - "addon.competency.competencies": "Competenties", - "addon.competency.competenciesmostoftennotproficientincourse": "Meestal nog niet bekwaam voor competenties in deze cursus", - "addon.competency.coursecompetencies": "Cursuscompetenties", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Competentiebeoordelingen in deze cursus hebben geen invloed op studieplannnen.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Competentiebeoordelingen in deze cursus worden onmiddellijk aangepast in studieplannen.", - "addon.competency.crossreferencedcompetencies": "Competenties met kruisverwijzingen", - "addon.competency.duedate": "Einddatum", - "addon.competency.errornocompetenciesfound": "Geen competenties gevonden", - "addon.competency.evidence": "Bewijs", - "addon.competency.evidence_competencyrule": "De competentieregel werd behaald.", - "addon.competency.evidence_coursecompleted": "Cursus '{{$a}}' werd voltooid", - "addon.competency.evidence_coursemodulecompleted": "Activiteit '{{$a}}' werd voltooid.", - "addon.competency.evidence_courserestored": "De waardering was hersteld, samen met cursus '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Bewijs van leren '{{$a}}' werd gelinkt.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Bewijs van leren '{{$a}}' link verwijderd", - "addon.competency.evidence_manualoverride": "De competentiebeoordeling werd manueel ingesteld.", - "addon.competency.evidence_manualoverrideincourse": "De competentiebeoordeling werd manueel ingesteld in cursus '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "De competentiebeoordeling werd manueel ingesteld in studieplan '{{$a}}'.", - "addon.competency.learningplancompetencies": "Studieplan competenties", - "addon.competency.learningplans": "Studieplannen", - "addon.competency.myplans": "Mijn studieplannen", - "addon.competency.noactivities": "Geen activiteiten", - "addon.competency.nocompetencies": "Geen competenties", - "addon.competency.nocompetenciesincourse": "Er zijn nog geen competenties gelinkt aan deze cursus.", - "addon.competency.nocrossreferencedcompetencies": "Er zijn geen andere competenties met een kruisverwijzing naar deze competentie.", - "addon.competency.noevidence": "Geen bewijs", - "addon.competency.noplanswerecreated": "Er zijn nog geen studieplannen gemaakt", - "addon.competency.nouserplanswithcompetency": "Geen leerplannen bevatten deze competentie.", - "addon.competency.path": "Pad:", - "addon.competency.planstatusactive": "Actief", - "addon.competency.planstatuscomplete": "Volledig", - "addon.competency.planstatusdraft": "Klad", - "addon.competency.planstatusinreview": "Wordt beoordeeld", - "addon.competency.planstatuswaitingforreview": "Wacht op beoordeling", - "addon.competency.proficient": "Geslaagd", - "addon.competency.progress": "Vordering", - "addon.competency.rating": "Beoordeling", - "addon.competency.reviewstatus": "Beoordelingsstatus", - "addon.competency.status": "Status", - "addon.competency.template": "Studieplansjabloon", - "addon.competency.uponcoursecompletion": "Bij cursusvoltooiing:", - "addon.competency.usercompetencystatus_idle": "Niet aan het werk", - "addon.competency.usercompetencystatus_inreview": "Wordt beoordeeld", - "addon.competency.usercompetencystatus_waitingforreview": "Wacht op beoordeling", - "addon.competency.userplans": "Studieplan", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} van de {{$a.y}} competenties zijn bekwaam", - "addon.competency.xcompetenciesproficientoutofyincourse": "Je bent bekwaam in {{$a.x}} van de {{$a.y}} competenties in deze cursus.", - "addon.coursecompletion.complete": "Voltooi", - "addon.coursecompletion.completecourse": "Voltooi cursus", - "addon.coursecompletion.completed": "Voltooid", - "addon.coursecompletion.completiondate": "Voltooiingsdatum", - "addon.coursecompletion.completionmenuitem": "Voltooien", - "addon.coursecompletion.couldnotloadreport": "Kon het voltooiingsrapport van de cursus niet laden. Probeer later opnieuw.", - "addon.coursecompletion.coursecompletion": "Cursus voltooien", - "addon.coursecompletion.criteria": "Criteria", - "addon.coursecompletion.criteriagroup": "Criteria groep", - "addon.coursecompletion.criteriarequiredall": "Alle onderstaande criteria zijn vereist", - "addon.coursecompletion.criteriarequiredany": "Al onderstaande criteria zijn vereist", - "addon.coursecompletion.inprogress": "Bezig", - "addon.coursecompletion.manualselfcompletion": "Manueel voltooien", - "addon.coursecompletion.nottracked": "Het voltooien van activiteiten wordt voor deze cursus niet bijgehouden.", - "addon.coursecompletion.notyetstarted": "Nog niet begonnen", - "addon.coursecompletion.pending": "Bezig", - "addon.coursecompletion.required": "Verplicht", - "addon.coursecompletion.requiredcriteria": "Vereiste criteria", - "addon.coursecompletion.requirement": "Vereiste", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Bekijk cursusrapport", - "addon.files.couldnotloadfiles": "De bestandenlijst kon niet geladen worden.", - "addon.files.emptyfilelist": "Er zijn geen bestanden te tonen.", - "addon.files.erroruploadnotworking": "Jammer genoeg kun je op dit ogenblik geen bestanden uploaden naar de site.", - "addon.files.files": "Bestanden", - "addon.files.privatefiles": "Privébestanden", - "addon.files.sitefiles": "Sitebestanden", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Apparaten configureren", - "addon.messages.acceptandaddcontact": "Accepteren en toevoegen aan contactpersonen", - "addon.messages.addcontact": "Voeg contactpersoon toe", - "addon.messages.addcontactconfirm": "Weet je zeker dat je {{$a}} aan je contacten wilt toevoegen?", - "addon.messages.addtofavourites": "Conversatie als favoriet markeren", - "addon.messages.addtoyourcontacts": "Voeg toe aan contactpersonen", - "addon.messages.blocknoncontacts": "Blokkeer gebruikers die niet op mijn contactenlijst staan", - "addon.messages.blockuser": "Blokkeer gebruiker", - "addon.messages.blockuserconfirm": "Weet je zeker dat je {{$a}} wilt blokkeren?", - "addon.messages.contactableprivacy": "Accepteer berichten van:", - "addon.messages.contactableprivacy_coursemember": "Mijn contacten en iedereen in mijn cursussen", - "addon.messages.contactableprivacy_onlycontacts": "Alleen mijn contacten", - "addon.messages.contactableprivacy_site": "Iedereen op de site", - "addon.messages.contactblocked": "Blokkeer contactpersoon", - "addon.messages.contactlistempty": "De contactenlijst is leeg", - "addon.messages.contactname": "Naam", - "addon.messages.contactrequestsent": "Contactverzoek verzonden", - "addon.messages.contacts": "Contactpersonen", - "addon.messages.conversationactions": "Gespreksacties menu", - "addon.messages.decline": "Afwijzen", - "addon.messages.deleteallconfirm": "Weet je zeker, dat je dit gehele gesprek wilt verwijderen? Dit zal het niet verwijderen voor andere deelnemers van dit gesprek.", - "addon.messages.deleteallselfconfirm": "Weet je zeker dat je dit hele persoonlijke gesprek wilt verwijderen?", - "addon.messages.deleteconversation": "Verwijder gesprek", - "addon.messages.deleteforeveryone": "Verwijder voor mij en voor alle anderen", - "addon.messages.deletemessage": "Verwijder bericht", - "addon.messages.deletemessageconfirmation": "Weet je zeker dat je dit bericht wil verwijderen? Het zal alleen van je berichtengeschiedenis gewist worden en zichtbaar blijven voor de gebruiker naar wie je het gestuurd hebt.", - "addon.messages.errordeletemessage": "Fout bij het verwijderen van het bericht.", - "addon.messages.errorwhileretrievingcontacts": "Fout bij het ophalen van contacten van de server.", - "addon.messages.errorwhileretrievingdiscussions": "Fout bij het ophalen van discussies van de server.", - "addon.messages.errorwhileretrievingmessages": "Fout bij het ophalen van berichten van de server.", - "addon.messages.errorwhileretrievingusers": "Fout bij het ophalen van gebruikers van de server", - "addon.messages.groupconversations": "Groep", - "addon.messages.groupinfo": "Groepsinfo", - "addon.messages.individualconversations": "Privé", - "addon.messages.info": "Gebruikersinfo", - "addon.messages.isnotinyourcontacts": "{{$a}} staat niet in je contacten", - "addon.messages.message": "Bericht", - "addon.messages.messagenotsent": "Het bericht is niet verzonden. Probeer het later opnieuw.", - "addon.messages.messagepreferences": "Berichten voorkeuren", - "addon.messages.messages": "Berichten", - "addon.messages.muteconversation": "Dempen", - "addon.messages.mutedconversation": "Gedempt gesprek", - "addon.messages.newmessage": "Nieuw bericht", - "addon.messages.newmessages": "Nieuwe berichten", - "addon.messages.nocontactrequests": "Geen contactverzoeken", - "addon.messages.nocontactsgetstarted": "Geen contacten", - "addon.messages.nofavourites": "Geen berichten gemarkeerd met een ster", - "addon.messages.nogroupconversations": "Geen groepsberichten", - "addon.messages.noindividualconversations": "Geen privé-berichten", - "addon.messages.nomessagesfound": "Geen berichten gevonden", - "addon.messages.noncontacts": "Geen contactpersoon", - "addon.messages.nousersfound": "Geen gebruikers gevonden", - "addon.messages.numparticipants": "{{$a}} deelnemers", - "addon.messages.removecontact": "Verwijder contactpersoon", - "addon.messages.removecontactconfirm": "Weet je zeker dat je {{$a}} uit je contacten wilt verwijderen?", - "addon.messages.removefromfavourites": "Verwijder \"favoriet\"-markering", - "addon.messages.removefromyourcontacts": "Verwijder uit je contacten", - "addon.messages.requests": "Verzoeken", - "addon.messages.requirecontacttomessage": "Je moet {{$a}} vragen om je als contact toe te voegen om berichten te kunnen sturen.", - "addon.messages.searchcombined": "Zoek mensen en berichten", - "addon.messages.selfconversation": "Persoonlijke ruimte", - "addon.messages.selfconversationdefaultmessage": "Bewaar conceptberichten, links, notities enz. Om later toegang te krijgen.", - "addon.messages.sendcontactrequest": "Verzend contact-verzoek", - "addon.messages.showdeletemessages": "Toon verwijderde berichten", - "addon.messages.type_blocked": "Geblokkeerd", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Zoekresultaten", - "addon.messages.type_strangers": "Anderen", - "addon.messages.unabletomessage": "Je kunt geen berichten sturen naar deze gebruiker", - "addon.messages.unblockuser": "Deblokkeer gebruiker", - "addon.messages.unblockuserconfirm": "Weet je zeker dat je {{$a}} wilt deblokkeren?", - "addon.messages.unmuteconversation": "Dempen opheffen", - "addon.messages.useentertosend": "Gebruik entertoets om te verzenden", - "addon.messages.useentertosenddescdesktop": "Indien uitgeschakeld kan je Ctrl+enter gebruiken om een bericht te versturen.", - "addon.messages.useentertosenddescmac": "Indien uitgeschakeld kan je Cmd+enter gebruiken om een bericht te versturen.", - "addon.messages.userwouldliketocontactyou": "{{$a}} wil graag contact met je opnemen", - "addon.messages.warningconversationmessagenotsent": "Kon bericht(en) niet verzenden naar conversatie {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Kon bericht(en) niet versturen naar gebruiker {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Zou graag contact met je opnemen", - "addon.messages.you": "Jij:", - "addon.messages.youhaveblockeduser": "Je hebt deze gebruiker geblokkeerd.", - "addon.messages.yourcontactrequestpending": "Je contactverzoek is in behandeling bij {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Accepteer de uploadvoorwaarden.", - "addon.mod_assign.addattempt": "Nog een poging toestaan", - "addon.mod_assign.addnewattempt": "Een nieuwe poging toevoegen", - "addon.mod_assign.addnewattemptfromprevious": "Een nieuwe poging toevoegen, gebaseerd op de vorige inzending", - "addon.mod_assign.addsubmission": "Inzending toevoegen", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "De opdrachtdetails en het instuurformulier zullen beschikbaar zijn vanaf {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Insturen toestaan vanaf", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Deze opdracht zal inzendingen ontvangen vanaf {{$a}}", - "addon.mod_assign.applytoteam": "Cijfers en feedback aan de hele groep geven", - "addon.mod_assign.assignmentisdue": "Opdracht moet worden ingeleverd", - "addon.mod_assign.attemptnumber": "Pogingnummer", - "addon.mod_assign.attemptreopenmethod": "Heropende pogingen", - "addon.mod_assign.attemptreopenmethod_manual": "Manueel", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatisch tot geslaagd", - "addon.mod_assign.attemptsettings": "Poginginstellingen", - "addon.mod_assign.cannoteditduetostatementsubmission": "Je kunt geen inzending toevoegen of bewerken in de app, omdat we de uploadvoorwaarden niet konden ophalen van de site.", - "addon.mod_assign.cannotgradefromapp": "Some beoordelingsmethodes worden nog niet door de app ondersteund en kunnen niet worden aangepast.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Je kunt niet insturen voor beoordeling omdat we de uploadvoorwaarden niet konden ophalen van de site.", - "addon.mod_assign.confirmsubmission": "Weet je zeker dat je je werk wil insturen voor beoordeling? Je zult geen wijzigingen meer kunnen aanbrengen.", - "addon.mod_assign.currentattempt": "Deze poging {{$a}}", - "addon.mod_assign.currentattemptof": "Dit is poging {{$a.attemptnumber}} ({{$a.maxattempts}} pogingen zijn toegestaan).", - "addon.mod_assign.currentgrade": "Huidig cijfer in de cijferlijst", - "addon.mod_assign.cutoffdate": "Afsluitdatum", - "addon.mod_assign.defaultteam": "Standaard groep", - "addon.mod_assign.duedate": "Uiterste inleverdatum", - "addon.mod_assign.duedateno": "Geen uiterste inleverdatum", - "addon.mod_assign.duedatereached": "De datum waarop deze opdracht moest afgegeven worden is nu voorbij.", - "addon.mod_assign.editingstatus": "Status bewerken", - "addon.mod_assign.editsubmission": "Bewerk inzending", - "addon.mod_assign.erroreditpluginsnotsupported": "Je kunt geen inzending toesturen of bewerken in de app omdat sommige plugins bewerken nog niet ondersteunen.", - "addon.mod_assign.errorshowinginformation": "We kunnen de instuurinformatie niet tonen.", - "addon.mod_assign.extensionduedate": "Extra tijd einddatum", - "addon.mod_assign.feedbacknotsupported": "Deze feedback wordt niet ondersteund door de app en daarom is de informatie mogelijk onvolledig.", - "addon.mod_assign.grade": "Beoordeling", - "addon.mod_assign.graded": "Beoordeeld", - "addon.mod_assign.gradedby": "Beoordeeld door", - "addon.mod_assign.gradedfollowupsubmit": "Beoordeeld - opvolgingsinzending ontvangen", - "addon.mod_assign.gradedon": "Beoordeeld op", - "addon.mod_assign.gradelocked": "Dit cijfer is geblokkeerd of overschreven in de cijferlijst.", - "addon.mod_assign.gradenotsynced": "Cijfer niet gesynchroniseerd", - "addon.mod_assign.gradeoutof": "Cijfer op {{$a}}", - "addon.mod_assign.gradingstatus": "Beoordelingsstatus", - "addon.mod_assign.groupsubmissionsettings": "Instellingen groepswerk", - "addon.mod_assign.hiddenuser": "Deelnemer", - "addon.mod_assign.latesubmissions": "Te late inzendingen", - "addon.mod_assign.latesubmissionsaccepted": "Toegestaan tot {{$a}}", - "addon.mod_assign.markingworkflowstate": "Status van de beoordelings-workflow", - "addon.mod_assign.markingworkflowstateinmarking": "Wordt beoordeeld", - "addon.mod_assign.markingworkflowstateinreview": "Wordt nagelezen", - "addon.mod_assign.markingworkflowstatenotmarked": "Niet beoordeeld", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Klaar om vrijgegeven te worden", - "addon.mod_assign.markingworkflowstatereadyforreview": "Beoordeling klaar", - "addon.mod_assign.markingworkflowstatereleased": "Vrijgegeven", - "addon.mod_assign.modulenameplural": "Opdrachten", - "addon.mod_assign.multipleteams": "Lid van meer dan één groep", - "addon.mod_assign.multipleteams_desc": "De opdracht vereist insturen in groep. Je bent lid van meer dan één groep. Om te kunnen insturen mag je slechts lid zijn van één groep. Neem contact op met je leraar om je groepslidmaatschap aan te passen.", - "addon.mod_assign.noattempt": "Geen poging", - "addon.mod_assign.nomoresubmissionsaccepted": "Enkel toegestaan voor deelnemers die extra tijd gekregen hebben.", - "addon.mod_assign.noonlinesubmissions": "Voor deze opdracht hoef je niets online in te dienen", - "addon.mod_assign.nosubmission": "Er is nog niets ingestuurd voor deze opdracht", - "addon.mod_assign.notallparticipantsareshown": "Deelnemers die niets ingestuurd hebben, worden niet getoond.", - "addon.mod_assign.noteam": "Lid van geen enkele groep", - "addon.mod_assign.noteam_desc": "De opdracht vereist insturen in groep. Je bent van geen enkele groep lid, dus je kan niets insturen. Neem contact op met je leraar om in een groep gezet te worden.", - "addon.mod_assign.notgraded": "Niet beoordeeld", - "addon.mod_assign.numberofdraftsubmissions": "Kladwerken", - "addon.mod_assign.numberofparticipants": "Deelnemers", - "addon.mod_assign.numberofsubmissionsneedgrading": "Beoordeling nodig", - "addon.mod_assign.numberofsubmittedassignments": "Ingestuurd", - "addon.mod_assign.numberofteams": "Groepen", - "addon.mod_assign.numwords": "{{$a}} woorden", - "addon.mod_assign.outof": "{{$a.current}} van {{$a.total}}", - "addon.mod_assign.overdue": "Opdracht {{$a}} te laat ingestuurd", - "addon.mod_assign.submission": "Ingestuurde opdracht", - "addon.mod_assign.submissioneditable": "Leerling kan zijn ingestuurde opdracht bewerken", - "addon.mod_assign.submissionnoteditable": "Leerling kan ingestuurde opdracht niet bewerken", - "addon.mod_assign.submissionnotsupported": "Deze ingestuurde opdracht wordt niet ondersteund door de app en bevat mogelijk niet alle informatie.", - "addon.mod_assign.submissionslocked": "Voor deze opdracht kan nu niets ingestuurd worden.", - "addon.mod_assign.submissionstatus": "Status ingestuurde opdracht", - "addon.mod_assign.submissionstatus_": "Geen ingestuurde opdracht", - "addon.mod_assign.submissionstatus_draft": "Kladwerk (nog niet ingestuurd)", - "addon.mod_assign.submissionstatus_marked": "Beoordeeld", - "addon.mod_assign.submissionstatus_new": "Geen inzending", - "addon.mod_assign.submissionstatus_reopened": "Heropend", - "addon.mod_assign.submissionstatus_submitted": "Opdracht ingestuurd om te beoordelen", - "addon.mod_assign.submissionstatusheading": "Status ingestuurde opdracht", - "addon.mod_assign.submissionteam": "Groep", - "addon.mod_assign.submitassignment": "Stuur opdracht in", - "addon.mod_assign.submitassignment_help": "Als je deze opdracht hebt ingestuurd, dan zul je geen wijzigingen meer kunnen aanbrengen.", - "addon.mod_assign.submittedearly": "Opdracht was {{$a}} te vroeg ingestuurd", - "addon.mod_assign.submittedlate": "Opdracht was {{$a}} te laat ingestuurd", - "addon.mod_assign.timemodified": "Laatst gewijzigd", - "addon.mod_assign.timeremaining": "Resterende tijd", - "addon.mod_assign.ungroupedusers": "De instelling \"Groeplidmaatschap vereist om een inzending te maken\" is ingeschakeld en sommige gebruikers zijn niet toegewezen aan een groep of zijn lid van meerdere groepen. Dit zal het onmogelijk maken hun opdracht in te sturen.", - "addon.mod_assign.ungroupedusersoptional": "De instelling 'Leerlingen sturen groepsopdracht in' is ingeschakeld en sommige gebruikers maken geen deel uit van een groep of zijn lid van meer dan één groep. Houd er rekening mee dat deze leerlingen zich aanmelden als leden van de 'Standaardgroep'.", - "addon.mod_assign.unlimitedattempts": "Onbeperkt", - "addon.mod_assign.userswhoneedtosubmit": "Leerlingen die nog moeten insturen: {{$a}}", - "addon.mod_assign.userwithid": "Gebruiker met Id {{id}}", - "addon.mod_assign.viewsubmission": "Bekijk ingestuurde opdracht", - "addon.mod_assign.warningsubmissiongrademodified": "Het cijfer van je ingeleverde opdracht is op de site gewijzigd.", - "addon.mod_assign.warningsubmissionmodified": "Het door de gebruiker ingestuurde bestand is gewijzigd op de site.", - "addon.mod_assign.wordlimit": "Woordenlimiet", - "addon.mod_assign_feedback_comments.pluginname": "Feedback tekstvak", - "addon.mod_assign_feedback_editpdf.pluginname": "Annoteer PDF", - "addon.mod_assign_feedback_file.pluginname": "Bestandsfeedback", - "addon.mod_assign_submission_comments.pluginname": "Opmerkingen bij ingestuurde opdrachten", - "addon.mod_assign_submission_file.pluginname": "Bestanden insturen", - "addon.mod_assign_submission_onlinetext.pluginname": "Online tekst ingestuurde opdrachten", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Het aantal woorden voor deze opdracht is beperkt tot {{$a.limit}} woorden en je probeert een opdracht met {{$a.count}} woorden in te sturen. Herbekijk je werk en probeer opnieuw.", - "addon.mod_book.errorchapter": "Fout bij het lezen van het hoofdstuk", - "addon.mod_book.modulenameplural": "Boeken", - "addon.mod_book.navnexttitle": "Volgende: {{$a}}", - "addon.mod_book.navprevtitle": "Vorige: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Boek hoofdstukken", - "addon.mod_book.toc": "Inhoudsopgave", - "addon.mod_chat.beep": "Piep", - "addon.mod_chat.chatreport": "Chat-sessies", - "addon.mod_chat.currentusers": "Huidige gebruikers", - "addon.mod_chat.enterchat": "Klik hier om de chat nu binnen te gaan", - "addon.mod_chat.entermessage": "Geef je bericht", - "addon.mod_chat.errorwhileconnecting": "Fout bij het verbinden met de chat.", - "addon.mod_chat.errorwhilegettingchatdata": "Fout bij het ophalen van de chatgegevens.", - "addon.mod_chat.errorwhilegettingchatusers": "Fout bij het ophalen van de chatgebruikers.", - "addon.mod_chat.errorwhileretrievingmessages": "Fout bij het ophalen van de berichten van de server.", - "addon.mod_chat.errorwhilesendingmessage": "Fout tijdens het versturen van het bericht.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} piept iedereen!", - "addon.mod_chat.messagebeepsyou": "{{$a}} heeft je net gepiept!", - "addon.mod_chat.messageenter": "{{$a}} is net deze chat binnen gekomen", - "addon.mod_chat.messageexit": "{{$a}} heeft deze chat verlaten", - "addon.mod_chat.messages": "Berichten", - "addon.mod_chat.messageyoubeep": "Je biepte {{$a}}", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "Je moet online zijn om berichten te versturen.", - "addon.mod_chat.nomessages": "Nog geen berichten", - "addon.mod_chat.nosessionsfound": "Geen sessies gevonden", - "addon.mod_chat.saidto": "zei tegen", - "addon.mod_chat.send": "Stuur", - "addon.mod_chat.sessionstart": "De volgende chatsessie zal beginnen op {{$a.date}}, ({{$a.fromnow}} vanaf nu)", - "addon.mod_chat.showincompletesessions": "Toon onvolledige sessies", - "addon.mod_chat.talk": "Spreek", - "addon.mod_chat.viewreport": "Bekijk voorbije chatsessies", - "addon.mod_choice.cannotsubmit": "Er was een probleem bij het insturen van je keuze. Probeer opnieuw", - "addon.mod_choice.choiceoptions": "Keuze-opties", - "addon.mod_choice.errorgetchoice": "Fout bij het ophalen van de keuzegegevens.", - "addon.mod_choice.expired": "Deze activiteit is afgesloten op {{$a}}.", - "addon.mod_choice.full": "(volledig)", - "addon.mod_choice.modulenameplural": "Keuzes", - "addon.mod_choice.noresultsviewable": "De resultaten zijn nu niet zichtbaar.", - "addon.mod_choice.notopenyet": "Deze activiteit is niet beschikbaar tot {{$a}}.", - "addon.mod_choice.numberofuser": "Aantal antwoorden", - "addon.mod_choice.numberofuserinpercentage": "Percentage antwoorden", - "addon.mod_choice.previewonly": "Dit is slechts een voorbeeld van de beschikbare opties voor deze activiteit. Je zult je keuze niet kunnen maken voor {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anonieme resultaten worden gepubliceerd nadat je antwoordt.", - "addon.mod_choice.publishinfoanonclose": "Anonieme resultaten worden gepubliceerd nadat de activiteit is gesloten.", - "addon.mod_choice.publishinfofullafter": "Volledige resultaten, met ieders keuzes, worden gepubliceerd nadat je hebt geantwoord.", - "addon.mod_choice.publishinfofullclose": "De volledige resultaten, die de keuzes van iedereen tonen, worden gepubliceerd nadat de activiteit is afgesloten.", - "addon.mod_choice.publishinfonever": "De resultaten van deze activiteit zullen niet getoond worden nadat je geantwoord hebt.", - "addon.mod_choice.removemychoice": "Verwijder mijn keuze", - "addon.mod_choice.responses": "Antwoorden", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% van de gebruikers kozen optie: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Grafiek tonen", - "addon.mod_choice.resultsnotsynced": "Jouw laatste reactie moet gesynchroniseerd worden voor ze mee in de resultaten zit.", - "addon.mod_choice.savemychoice": "Bewaar mijn antwoord", - "addon.mod_choice.userchoosethisoption": "Gebruikers die deze optie kozen", - "addon.mod_choice.yourselection": "Jouw selectie", - "addon.mod_data.addentries": "Items goedkeuren", - "addon.mod_data.advancedsearch": "Geavanceerd zoeken", - "addon.mod_data.alttext": "Alternatieve tekst", - "addon.mod_data.approve": "Goedkeuren", - "addon.mod_data.approved": "Goedgekeurd", - "addon.mod_data.ascending": "Oplopend", - "addon.mod_data.authorfirstname": "Voornaam auteur", - "addon.mod_data.authorlastname": "Achternaam auteur", - "addon.mod_data.confirmdeleterecord": "Je gaat deze record verwijderen. Ben je zeker?", - "addon.mod_data.descending": "Aflopend", - "addon.mod_data.disapprove": "Verwerp", - "addon.mod_data.edittagsnotsupported": "Sorry, het bewerken van tags wordt niet ondersteund door de app.", - "addon.mod_data.emptyaddform": "Je hebt geen velden ingevuld!", - "addon.mod_data.entrieslefttoadd": "Je moet {{$a.entriesleft}} meer item(s) ingeven voor je de items van anderen kunt zien.", - "addon.mod_data.entrieslefttoaddtoview": "je moet {{$a.entrieslefttoview}} items meer toevoegen voor je de items van anderen kan zien.", - "addon.mod_data.errorapproving": "Fout bij het goedkeuren of verwerpen van het nieuwe item.", - "addon.mod_data.errordeleting": "Fout bij het verwijderen van het item.", - "addon.mod_data.errormustsupplyvalue": "Je moet hier een waarde geven.", - "addon.mod_data.expired": "Deze activiteit is gesloten op {{$a}} en is niet langer beschikbaar", - "addon.mod_data.fields": "Velden", - "addon.mod_data.foundrecords": "Gevonden records: {{$a.num}}/{{$a.max}} (Reset filters)", - "addon.mod_data.gettinglocation": "Locatie ophalen", - "addon.mod_data.latlongboth": "Zowel de breedtegraad als de lengtegraad zijn vereist.", - "addon.mod_data.locationpermissiondenied": "Toestemming voor toegang tot uw locatie is geweigerd", - "addon.mod_data.menuchoose": "Kies...", - "addon.mod_data.modulenameplural": "Databanken", - "addon.mod_data.more": "Meer", - "addon.mod_data.mylocation": "Mijn locatie", - "addon.mod_data.nomatch": "Geen overeenkomende items gevonden", - "addon.mod_data.norecords": "Geen items in de databank", - "addon.mod_data.notapproved": "Item is nog niet goedgekeurd", - "addon.mod_data.notopenyet": "Deze actieviteit is niet beschikbaar tot {{$a}}", - "addon.mod_data.numrecords": "{{$a}} records", - "addon.mod_data.other": "Andere", - "addon.mod_data.recordapproved": "Item goedgekeurd", - "addon.mod_data.recorddeleted": "Record verwijderd", - "addon.mod_data.recorddisapproved": "Item verworpen", - "addon.mod_data.resetsettings": "Reset filters", - "addon.mod_data.search": "Zoek", - "addon.mod_data.searchbytagsnotsupported": "Sorry, zoeken op tags wordt niet ondersteund door de app.", - "addon.mod_data.selectedrequired": "Alle geselecteerde vereist", - "addon.mod_data.single": "Itemweergave", - "addon.mod_data.tagarea_data_records": "Data records", - "addon.mod_data.timeadded": "Toegevoegd op", - "addon.mod_data.timemodified": "Gewijzigd op", - "addon.mod_data.usedate": "Ook zoeken", - "addon.mod_feedback.analysis": "Analyse", - "addon.mod_feedback.anonymous": "Anoniem", - "addon.mod_feedback.anonymous_entries": "Anoniem ingevulde formulieren ({{$a}})", - "addon.mod_feedback.average": "Gemiddelde", - "addon.mod_feedback.captchaofflinewarning": "Feedback met CAPTCHA kan niet offline voltooid worden of wanneer dat niet geconfigureerd is of wanneer de server onbereikbaar is.", - "addon.mod_feedback.complete_the_form": "Beantwoord de vragen", - "addon.mod_feedback.completed_feedbacks": "Ingevulde antwoorden", - "addon.mod_feedback.continue_the_form": "Ga verder met het beantwoorden van de vragen", - "addon.mod_feedback.feedback_is_not_open": "De feedback is niet open", - "addon.mod_feedback.feedback_submitted_offline": "Deze feedback is bewaard om later in te sturen.", - "addon.mod_feedback.feedbackclose": "Antwoorden toestaan tot", - "addon.mod_feedback.feedbackopen": "Antwoorden toestaan van", - "addon.mod_feedback.mapcourses": "Koppel feedback aan cursussen", - "addon.mod_feedback.maximal": "Maximum", - "addon.mod_feedback.minimal": "Minimaal", - "addon.mod_feedback.mode": "Modus", - "addon.mod_feedback.modulenameplural": "Feedbackformulieren", - "addon.mod_feedback.next_page": "Volgende pagina", - "addon.mod_feedback.non_anonymous": "De gebruikersnamen zullen bewaard worden en samen met de antwoorden getoond worden", - "addon.mod_feedback.non_anonymous_entries": "Niet-anonieme items ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Leerlingen die niet ingestuurd hebben ({{$a}})", - "addon.mod_feedback.not_selected": "Niet geselecteerd", - "addon.mod_feedback.not_started": "Niet begonnen", - "addon.mod_feedback.numberoutofrange": "Aantal buiten bereik", - "addon.mod_feedback.overview": "Overzicht", - "addon.mod_feedback.page_after_submit": "Bericht na voltooien", - "addon.mod_feedback.preview": "Bekijk", - "addon.mod_feedback.previous_page": "Vorige pagina", - "addon.mod_feedback.questions": "Vragen", - "addon.mod_feedback.response_nr": "Antwoordnummer", - "addon.mod_feedback.responses": "Antwoorden", - "addon.mod_feedback.save_entries": "Verzend je antwoorden", - "addon.mod_feedback.show_entries": "Toon antwoorden", - "addon.mod_feedback.show_nonrespondents": "Niet geantwoord", - "addon.mod_feedback.started": "Gestart", - "addon.mod_feedback.this_feedback_is_already_submitted": "Je hebt dit feedbackformulier is al ingestuurd.", - "addon.mod_folder.emptyfilelist": "Geen bestanden.", - "addon.mod_folder.modulenameplural": "Mappen", - "addon.mod_forum.addanewdiscussion": "Voeg een nieuw discussieonderwerp toe", - "addon.mod_forum.addanewquestion": "Voeg een nieuwe vraag toe", - "addon.mod_forum.addanewtopic": "Voeg een nieuw onderwerp toe", - "addon.mod_forum.addtofavourites": "Markeer deze discussie als favoriet", - "addon.mod_forum.advanced": "Geavanceerd", - "addon.mod_forum.cannotadddiscussion": "Om discussies aan dit forum te kunnen toevoegen, moet je lid zijn van deze groep", - "addon.mod_forum.cannotadddiscussionall": "Je hebt het recht niet om een nieuw discussieonderwerp te starten voor alle deelnemers.", - "addon.mod_forum.cannotcreatediscussion": "Kon geen nieuwe discussie starten", - "addon.mod_forum.couldnotadd": "Door een onbekende fout was het niet mogelijk om je bericht toe te voegen .", - "addon.mod_forum.couldnotupdate": "Kon je bericht niet bijwerken door een onbekende fout.", - "addon.mod_forum.cutoffdatereached": "Dit forum is gesloten omdat de afsluitdatum voorbij is. Je kunt hier niet langer posten.", - "addon.mod_forum.delete": "Verwijder", - "addon.mod_forum.deletedpost": "Het bericht is verwijderd", - "addon.mod_forum.deletesure": "Weet je zeker dat je dit bericht wilt verwijderen?", - "addon.mod_forum.discussion": "Discussie", - "addon.mod_forum.discussionlistsortbycreatedasc": "Sorteer op aanmaakdatum in oplopende volgorde.", - "addon.mod_forum.discussionlistsortbycreateddesc": "Sorteer op aanmaakdatum in aflopende volgorde", - "addon.mod_forum.discussionlistsortbylastpostasc": "Sorteer op aanmaakdatum van het laatste bericht in oplopende volgorde", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Sorteer op aanmaakdatum van het laatste bericht in aflopende volgorde", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Sorteer op het aantal antwoorden in oplopende volgorde", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Sorteer op het aantal antwoorden in aflopende volgorde", - "addon.mod_forum.discussionlocked": "Deze discussie is geblokkeerd, zodat u niet meer kan antwoorden.", - "addon.mod_forum.discussionpinned": "Vastgepind", - "addon.mod_forum.discussionsubscription": "Inschrijving discussie", - "addon.mod_forum.edit": "Wijzig", - "addon.mod_forum.erroremptymessage": "Bericht kan niet leeg zijn", - "addon.mod_forum.erroremptysubject": "Onderwerp kan niet leeg zijn", - "addon.mod_forum.errorgetforum": "Fout bij het ophalen van forumgegevens", - "addon.mod_forum.errorgetgroups": "Fout bij het ophalen van groepinstellingen.", - "addon.mod_forum.errorposttoallgroups": "Kon geen nieuwe discussie starten in alle groepen", - "addon.mod_forum.favouriteupdated": "Uw favoriet-optie is bijgewerkt.", - "addon.mod_forum.forumnodiscussionsyet": "Er zijn nog geen discussies in dit forum.", - "addon.mod_forum.group": "Groep", - "addon.mod_forum.lastpost": "Laatste bericht", - "addon.mod_forum.lockdiscussion": "Vergrendel deze discussie", - "addon.mod_forum.lockupdated": "De vergrendelingsoptie is bijgewerkt.", - "addon.mod_forum.message": "Bericht", - "addon.mod_forum.modeflatnewestfirst": "Laat de antwoorden in één lijst zien, met de nieuwste eerst", - "addon.mod_forum.modeflatoldestfirst": "Laat de antwoorden in één lijst zien, met de oudste eerst", - "addon.mod_forum.modenested": "Laat de antwoorden 'genest' zien", - "addon.mod_forum.modulenameplural": "Forums", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discussies", - "addon.mod_forum.numreplies": "{{numreplies}} antwoorden", - "addon.mod_forum.pindiscussion": "Discussie pinnen", - "addon.mod_forum.pinupdated": "De pin-optie is aangepast.", - "addon.mod_forum.postisprivatereply": "Dit is een privé-antwoord. Het is niet zichtbaar voor andere deelnemers.", - "addon.mod_forum.posttoforum": "Plaats op het forum", - "addon.mod_forum.posttomygroups": "Stuur een kopie naar alle groepen", - "addon.mod_forum.privatereply": "Beantwoord privé", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Discussie verversen", - "addon.mod_forum.refreshposts": "Discussieberichten verversen", - "addon.mod_forum.removefromfavourites": "Discussie niet meer favoriet", - "addon.mod_forum.reply": "Antwoord", - "addon.mod_forum.replyplaceholder": "Schrijf je antwoord ...", - "addon.mod_forum.subject": "Onderwerp", - "addon.mod_forum.tagarea_forum_posts": "Forumberichten", - "addon.mod_forum.thisforumhasduedate": "De vervaldatum voor het plaatsen van berichten op dit forum is {{$a}}.", - "addon.mod_forum.thisforumisdue": "De vervaldatum voor het plaatsen van berichten op dit forum was {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Ontgrendel deze discussie", - "addon.mod_forum.unpindiscussion": "Discussie niet meer gepind", - "addon.mod_forum.unread": "Niet gelezen", - "addon.mod_forum.unreadpostsnumber": "{{$a}} ongelezen berichten", - "addon.mod_forum.yourreply": "Jouw antwoord", - "addon.mod_glossary.addentry": "Voeg een item toe", - "addon.mod_glossary.aliases": "Alias(sen)", - "addon.mod_glossary.attachment": "Bijlage", - "addon.mod_glossary.browsemode": "Blader door items", - "addon.mod_glossary.byalphabet": "Alfabetisch", - "addon.mod_glossary.byauthor": "Groepeer per auteur", - "addon.mod_glossary.bycategory": "Groepeer per categorie", - "addon.mod_glossary.bynewestfirst": "Nieuwste eerst", - "addon.mod_glossary.byrecentlyupdated": "Onlangs gewijzigd", - "addon.mod_glossary.bysearch": "Zoek", - "addon.mod_glossary.cannoteditentry": "Kan item niet bewerken", - "addon.mod_glossary.casesensitive": "Dit woord is hoofdlettergevoelig", - "addon.mod_glossary.categories": "Categorieën", - "addon.mod_glossary.concept": "Concept", - "addon.mod_glossary.definition": "Definitie", - "addon.mod_glossary.entriestobesynced": "Items niet gesynchroniseerd", - "addon.mod_glossary.entrypendingapproval": "Dit item wacht op goedkeuring.", - "addon.mod_glossary.entryusedynalink": "Dit item wordt
                  automatisch gelinkt", - "addon.mod_glossary.errconceptalreadyexists": "Dit item bestaat al. In deze woordenlijst mogen geen dubbels voorkomen.", - "addon.mod_glossary.errorloadingentries": "Fout bij het laden van de items.", - "addon.mod_glossary.errorloadingentry": "Fout tijdens het laden van dit item.", - "addon.mod_glossary.errorloadingglossary": "Fout tijdens het laden van de woordenlijst.", - "addon.mod_glossary.fillfields": "Concept en definitie zijn verplichte velden.", - "addon.mod_glossary.fullmatch": "Alleen volledige woorden gebruiken
                  (wanneer automatisch gelinkt)", - "addon.mod_glossary.linking": "Automatisch linken", - "addon.mod_glossary.modulenameplural": "Woordenlijsten", - "addon.mod_glossary.noentriesfound": "Geen items gevonden.", - "addon.mod_glossary.searchquery": "Zoekopdracht", - "addon.mod_glossary.tagarea_glossary_entries": "Woordenlijstitems", - "addon.mod_h5pactivity.all_attempts": "Alle gebruikerspogingen", - "addon.mod_h5pactivity.answer_checked": "Antwoord gecontroleerd", - "addon.mod_h5pactivity.answer_correct": "Jouw antwoord is juist", - "addon.mod_h5pactivity.answer_fail": "Fout antwoord", - "addon.mod_h5pactivity.answer_incorrect": "Jouw antwoord is fout", - "addon.mod_h5pactivity.answer_pass": "Juist antwoord", - "addon.mod_h5pactivity.attempt": "Poging", - "addon.mod_h5pactivity.attempt_completion_no": "Deze poging is niet als voltooid gemarkeerd", - "addon.mod_h5pactivity.attempt_completion_yes": "Deze poging is voltooid", - "addon.mod_h5pactivity.attempt_success_fail": "Mislukt", - "addon.mod_h5pactivity.attempt_success_pass": "Geslaagd", - "addon.mod_h5pactivity.attempt_success_unknown": "Niet gerapporteerd", - "addon.mod_h5pactivity.attempts_none": "Deze gebruiker heeft nog geen pogingen om te tonen.", - "addon.mod_h5pactivity.completion": "Voltooien", - "addon.mod_h5pactivity.downloadh5pfile": "Download H5P-bestand", - "addon.mod_h5pactivity.duration": "Duur", - "addon.mod_h5pactivity.errorgetactivity": "Fout bij het ophalen van H5P activiteitsgegevens.", - "addon.mod_h5pactivity.filestatenotdownloaded": "Het H5P-pakket is niet gedownload. Je moet het downloaden om het te kunnen gebruiken.", - "addon.mod_h5pactivity.filestateoutdated": "Het H5P-pakket is gewijzigd sinds de laatste download. Je moet het opnieuw downloaden om het kunnen te gebruiken.", - "addon.mod_h5pactivity.maxscore": "Hoogste cijfer", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Mijn pogingen", - "addon.mod_h5pactivity.no_compatible_track": "Deze interactie ({{$a}}) geeft geen opvolgingsinformatie of de gegeven informatie is niet compatibel met de huidige versie van de activiteit.", - "addon.mod_h5pactivity.offlinedisabledwarning": "Je moet online zijn om het H5P-pakket te kunnen bekijken.", - "addon.mod_h5pactivity.outcome": "Competenties", - "addon.mod_h5pactivity.previewmode": "Deze inhoud wordt weergegeven in de voorbeeldmodus. Het traceren van pogingen wordt niet opgeslagen.", - "addon.mod_h5pactivity.result_fill-in": "Tekst invullen", - "addon.mod_h5pactivity.result_other": "Onbekend interactietype", - "addon.mod_h5pactivity.review_my_attempts": "Bekijk mijn pogingen", - "addon.mod_h5pactivity.score": "Cijfer", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} op {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Startdatum", - "addon.mod_h5pactivity.totalscore": "Totaalcijfer", - "addon.mod_h5pactivity.viewattempt": "Bekijk poging {{$a}}", - "addon.mod_imscp.deploymenterror": "Inhoud pakket fout!", - "addon.mod_imscp.modulenameplural": "IMS inhoudspakketten", - "addon.mod_imscp.showmoduledescription": "Toon beschrijving", - "addon.mod_imscp.toc": "Inhoud", - "addon.mod_lesson.answer": "Antwoord", - "addon.mod_lesson.attempt": "Poging: {{$a}}", - "addon.mod_lesson.attemptheader": "Poging", - "addon.mod_lesson.attemptsremaining": "Je kan nog {{$a}} poging(en) doen", - "addon.mod_lesson.averagescore": "Gemiddelde score", - "addon.mod_lesson.averagetime": "Gemiddelde tijd", - "addon.mod_lesson.branchtable": "Inhoud", - "addon.mod_lesson.cannotfindattempt": "Fout: kon poging niet vinden", - "addon.mod_lesson.cannotfinduser": "Fout: kon geen gebruikers vinden", - "addon.mod_lesson.clusterjump": "Ongeziene vraag binnen een cluster", - "addon.mod_lesson.completed": "Voltooid", - "addon.mod_lesson.congratulations": "Proficiat - je hebt het einde van de les bereikt", - "addon.mod_lesson.continue": "Ga verder", - "addon.mod_lesson.continuetonextpage": "Ga naar volgende pagina", - "addon.mod_lesson.defaultessayresponse": "Je antwoord op deze open vraag zal door de leraar beoordeeld worden.", - "addon.mod_lesson.detailedstats": "Gedetailleerde statistieken", - "addon.mod_lesson.didnotanswerquestion": "Deze vraag niet beantwoord", - "addon.mod_lesson.displayofgrade": "Tonen van het cijfer (voor de leerling)", - "addon.mod_lesson.displayscorewithessays": "

                  Je hebt {{$a.score}} punten op {{$a.tempmaxgrade}} behaald voor de automatisch beoordeelde vragen.

                  Je {{$a.essayquestions}} open vragen zullen beoordeeld worden op een later moment toegevoegd worden bij je totaalcijfer.

                  Je resultaat op dit ogenblik, zonder de open vragen is {{$a.score}} op {{$a.grade}}.

                  ", - "addon.mod_lesson.displayscorewithoutessays": "Je cijfer is {{$a.score}} (op {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Wachtwoord kan niet leeg zijn", - "addon.mod_lesson.enterpassword": "Geef het wachtwoord:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Je hebt geen enkele vraag beantwoord. Je hebt een 0 voor deze les", - "addon.mod_lesson.errorprefetchrandombranch": "Deze les bevat een sprong naar een willekeurige inhoudspagina. De les kan niet gestart worden in de app zonder dat die gestart is op het web.", - "addon.mod_lesson.errorreviewretakenotlast": "Deze poging kan niet meer nagekeken worden omdat er al een andere poging voltooid is.", - "addon.mod_lesson.finish": "Einde", - "addon.mod_lesson.finishretakeoffline": "Deze poging is voltooid in offline modus.", - "addon.mod_lesson.firstwrong": "Je antwoord is fout. Wil je de vraag opnieuw proberen te beantwoorden? Als je het antwoord weet, zul je er geen punten meer mee verdienen.", - "addon.mod_lesson.gotoendoflesson": "Ga naar het einde van de les", - "addon.mod_lesson.grade": "Cijfer", - "addon.mod_lesson.highscore": "Hoogste score", - "addon.mod_lesson.hightime": "Langste duur", - "addon.mod_lesson.leftduringtimed": "Je bent weggegaan tijdens een getimede les.
                  Klik op ga verder om de les te hervatten.", - "addon.mod_lesson.leftduringtimednoretake": "Je bent weggegaan tijdens een getimede les.
                  Je mag niet verder werken.", - "addon.mod_lesson.lessonmenu": "Lesmenu", - "addon.mod_lesson.lessonstats": "Statistieken van de les", - "addon.mod_lesson.linkedmedia": "Gelinkte media", - "addon.mod_lesson.loginfail": "Login mislukt. Probeer nog eens...", - "addon.mod_lesson.lowscore": "Laagste score", - "addon.mod_lesson.lowtime": "kortste duur", - "addon.mod_lesson.maximumnumberofattemptsreached": "Maximum aantal pogingen bereikt - we gaan verder naar de volgende pagina.", - "addon.mod_lesson.modattemptsnoteacher": "Nalezen werkt alleen voor leerlingen", - "addon.mod_lesson.modulenameplural": "Lessen", - "addon.mod_lesson.noanswer": "Eén of meerdere vragen zijn niet beantwoord. Ga terug en geef een antwoord.", - "addon.mod_lesson.nolessonattempts": "Niemand heeft deze les gemaakt.", - "addon.mod_lesson.nolessonattemptsgroup": "Er zijn geen pogingen gedaan door {{$a}} groepsleden voor deze les.", - "addon.mod_lesson.notcompleted": "Nog niet voltooid", - "addon.mod_lesson.numberofcorrectanswers": "Aantal juiste antwoorden: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Aantal beantwoorde vragen: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Aantal beantwoorde vragen: {{$a.nquestions}} (je moet minstens {{$a.minquestions}} vragen beantwoorden)", - "addon.mod_lesson.ongoingcustom": "Dit is een les op {{$a.score}} punten. Je hebt nu al {{$a.score}} punten verdiend van de {{$a.currenthigh}} punten die er tot nu toe te verdienen waren.", - "addon.mod_lesson.ongoingnormal": "Je hebt {{$a.correct}} vragen van de {{$a.viewed}} juist beantwoord.", - "addon.mod_lesson.or": "OF", - "addon.mod_lesson.overview": "Overzicht", - "addon.mod_lesson.preview": "Voorbeeld", - "addon.mod_lesson.progressbarteacherwarning2": "Je zult de vorderingsbalk niet zien omdat je deze les kunt bewerken", - "addon.mod_lesson.progresscompleted": "Je hebt {{$a}}% van de les beëindigd", - "addon.mod_lesson.question": "Vraag", - "addon.mod_lesson.rawgrade": "Ruwe score", - "addon.mod_lesson.reports": "Rapporten", - "addon.mod_lesson.response": "Feedback", - "addon.mod_lesson.retakefinishedinsync": "Er werd een offline poging gesynchroniseerd. Wil je ze bekijken?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Nalezen", - "addon.mod_lesson.reviewlesson": "Les nalezen", - "addon.mod_lesson.reviewquestionback": "Ja, ik wil nog eens proberen", - "addon.mod_lesson.reviewquestioncontinue": "Nee, ik wil naar de volgende vraag gaan", - "addon.mod_lesson.secondpluswrong": "Niet echt. Wil je nog eens proberen?", - "addon.mod_lesson.submit": "Insturen", - "addon.mod_lesson.teacherjumpwarning": "Er wordt een {{$a.cluster}} sprong of een {{$a.unseen}} sprong gebruikt in deze les. De sprong naar de volgende pagina zal in de plaats gebruikt worden. Meld je aan als leerling om deze sprongen te testen.", - "addon.mod_lesson.teacherongoingwarning": "De score tijdens de les wordt alleen aan leerlingen getoond. Meld je aan als leerling om deze optie te testen", - "addon.mod_lesson.teachertimerwarning": "De timer werkt enkel voor leerlingen. Test de timer door je als leerling aan te melden.", - "addon.mod_lesson.thatsthecorrectanswer": "Dat is het juiste antwoord", - "addon.mod_lesson.thatsthewronganswer": "Dat is het verkeerde antwoord", - "addon.mod_lesson.timeremaining": "Resterende tijd", - "addon.mod_lesson.timetaken": "Gebruikte tijd", - "addon.mod_lesson.unseenpageinbranch": "Ongeziene vraag binnen een inhoudspagina", - "addon.mod_lesson.warningretakefinished": "De poging is voltooid via de site.", - "addon.mod_lesson.welldone": "Goed gedaan!", - "addon.mod_lesson.youhaveseen": "Je hebt al meer dan één pagina van deze les bekeken.
                  Wil je beginnen bij de laatste pagina die je vorige keer bekeken hebt?", - "addon.mod_lesson.youranswer": "Jouw antwoord", - "addon.mod_lesson.yourcurrentgradeisoutof": "Je huidige cijfer is {{$a.grade}} op {{$a.total}}", - "addon.mod_lesson.youshouldview": "Je moet een antwoord geven op minstens: {{$a}}", - "addon.mod_lti.errorgetlti": "Fout bij het ophalen van moduledata.", - "addon.mod_lti.errorinvalidlaunchurl": "De start-URL is niet geldig", - "addon.mod_lti.launchactivity": "Start de activiteit", - "addon.mod_lti.modulenameplural": "Externe tools", - "addon.mod_page.errorwhileloadingthepage": "Fout bij het laden van de pagina-inhoud.", - "addon.mod_page.modulenameplural": "Pagina's", - "addon.mod_quiz.answercolon": "Antwoord:", - "addon.mod_quiz.attemptfirst": "Eerste poging", - "addon.mod_quiz.attemptlast": "Laatste poging", - "addon.mod_quiz.attemptnumber": "Poging", - "addon.mod_quiz.attemptquiznow": "Start de test nu", - "addon.mod_quiz.attemptstate": "Status", - "addon.mod_quiz.canattemptbutnotsubmit": "Je kunt deze test in de app proberen, maar je moet de poging om de volgende redenen in de browser indienen:", - "addon.mod_quiz.cannotsubmitquizdueto": "De test can niet ingestuurd worden om volgende redenen:", - "addon.mod_quiz.clearchoice": "Wis mijn keuze", - "addon.mod_quiz.comment": "Commentaar", - "addon.mod_quiz.completedon": "Voltooid op", - "addon.mod_quiz.confirmclose": "Je gaat deze poging afsluiten. Eens afgesloten kun je je antwoorden voor deze poging niet meer wijzigen.", - "addon.mod_quiz.confirmcontinueoffline": "Deze poging is niet meer gesynchroniseerd sinds {{$a}}. Als je verder gewerkt hebt aan deze poging met een ander toestel na dit moment, dan kun je data verliezen.", - "addon.mod_quiz.confirmleavequizonerror": "Er is een fout opgetreden tijdens het bewaren van je antwoorden. Weet je zeker dat je de test wil verlaten?", - "addon.mod_quiz.confirmstart": "De test heeft een tijdslimiet van {{$a}}. De tijd begint te lopen vanaf het moment dat je je poging start en je moet insturen voor de tijd afloopt. Weet je zeker dat je nu wil beginnen?", - "addon.mod_quiz.confirmstartheader": "Tijdslimiet", - "addon.mod_quiz.connectionerror": "Netwerkverbinding verloren (Automatisch bewaren mislukt).\n\nNeem nota van wat je op deze pagina ingetikt hebt tijdens de laatste minuten en probeer opnieuw te verbinden.\n\nAls de connectie terug lukt, dan zou je antwoord bewaard moeten worden en zal dit bericht verdwijnen.", - "addon.mod_quiz.continueattemptquiz": "Ga verder met de laatste poging", - "addon.mod_quiz.continuepreview": "Ga verder met controleren", - "addon.mod_quiz.errorbehaviournotsupported": "Deze test kan niet gemaakt worden in de app omdat het gedrag ervan niet ondersteund wordt door de app:", - "addon.mod_quiz.errordownloading": "Fout bij het downloaden van vereiste data", - "addon.mod_quiz.errorgetattempt": "Fout bij het ophalen van de poginggegevens.", - "addon.mod_quiz.errorgetquestions": "Fout bij het ophalen van de vragen", - "addon.mod_quiz.errorgetquiz": "Fout bij het ophalen van de testgegevens.", - "addon.mod_quiz.errorparsequestions": "Er is een fout opgetreden bij het lezen van de vragen. Probeer deze test in een browser.", - "addon.mod_quiz.errorquestionsnotsupported": "Deze test kan niet gemaakt worden in de app omdat die enkel vragen bevat die door de app niet ondersteund worden:", - "addon.mod_quiz.errorrulesnotsupported": "Deze test kan niet gemaakt worden in de app omdat er toegangsregels in zitten die niet door de app ondersteund worden:", - "addon.mod_quiz.errorsaveattempt": "Er is een fout opgetreden bij het bewaren van de gegevens van de poging.", - "addon.mod_quiz.feedback": "Feedback", - "addon.mod_quiz.finishattemptdots": "Beëindig poging...", - "addon.mod_quiz.finishnotsynced": "Klaar maar niet gesynchroniseerd", - "addon.mod_quiz.grade": "Cijfer", - "addon.mod_quiz.gradeaverage": "Gemiddelde cijfer", - "addon.mod_quiz.gradehighest": "Hoogste cijfer", - "addon.mod_quiz.grademethod": "Beoordelingsmethode", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}", - "addon.mod_quiz.marks": "Punten", - "addon.mod_quiz.modulenameplural": "Testen", - "addon.mod_quiz.mustbesubmittedby": "Deze poging moet ingestuurd worden door {{$a}}.", - "addon.mod_quiz.noquestions": "Er zijn nog geen vragen toegevoegd", - "addon.mod_quiz.noreviewattempt": "Je mag deze poging niet nakijken", - "addon.mod_quiz.notyetgraded": "Nog niet beoordeeld", - "addon.mod_quiz.opentoc": "Open navigatie.", - "addon.mod_quiz.outof": "{{$a.grade}} op een maximum van {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} op een maximum van {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Algemene feedback test", - "addon.mod_quiz.overdue": "Te laat", - "addon.mod_quiz.overduemustbesubmittedby": "Deze poging is nu over tijd. Ze zou al moeten ingestuurd geweest zijn. Als je wil dat deze poging beoordeeld wordt, dan moet je ze insturen voor {{$a}}. Als je dit niet doet, dan zullen er voor deze poging geen punten gegeven worden.", - "addon.mod_quiz.preview": "Controleer test", - "addon.mod_quiz.previewquiznow": "Controleer test", - "addon.mod_quiz.question": "Vraag", - "addon.mod_quiz.quiznavigation": "Testnavigatie", - "addon.mod_quiz.quizpassword": "Wachtwoord voor de test", - "addon.mod_quiz.reattemptquiz": "Herkans de test", - "addon.mod_quiz.requirepasswordmessage": "Je hebt het wachtwoord voor deze test nodig om de test te kunnen maken", - "addon.mod_quiz.returnattempt": "Terug naar poging", - "addon.mod_quiz.review": "Lees na", - "addon.mod_quiz.reviewofattempt": "Kijk poging {{$a}} na", - "addon.mod_quiz.reviewofpreview": "Voorbeeld nakijken", - "addon.mod_quiz.showall": "Toon alle vragen op één pagina", - "addon.mod_quiz.showeachpage": "Toon één pagina tegelijk", - "addon.mod_quiz.startattempt": "Start poging", - "addon.mod_quiz.startedon": "Gestart op", - "addon.mod_quiz.stateabandoned": "Nooit ingestuurd", - "addon.mod_quiz.statefinished": "Beëindigd", - "addon.mod_quiz.statefinisheddetails": "Ingestuurd {{$a}}", - "addon.mod_quiz.stateinprogress": "Bezig", - "addon.mod_quiz.stateoverdue": "Te laat", - "addon.mod_quiz.stateoverduedetails": "Moet ingestuurd worden door {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Bewaar alles en beëindig test", - "addon.mod_quiz.summaryofattempt": "Samenvatting van poging", - "addon.mod_quiz.summaryofattempts": "Samenvatting van al je pogingen", - "addon.mod_quiz.timeleft": "Resterende tijd", - "addon.mod_quiz.timetaken": "Gebruikte tijd", - "addon.mod_quiz.warningattemptfinished": "Offline poging is verwijderd omdat ze op de site voltooid was of niet gevonden.", - "addon.mod_quiz.warningdatadiscarded": "Sommige offline antwoorden werden verwijderd omdat te vragen online gewijzigd werden.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "De poging is niet beëindigd omdat de offline antwoorden geweigerd werden. Bekijk je antwoorden opnieuw en stuur de poging opnieuw in.", - "addon.mod_quiz.warningquestionsnotsupported": "Deze test bevat vragen die niet door de app worden ondersteund:", - "addon.mod_quiz.yourfinalgradeis": "Je eindcijfer voor deze test is {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Fout bij het laden van de inhoud.", - "addon.mod_resource.modifieddate": "Gewijzigd {{$a}}", - "addon.mod_resource.modulenameplural": "Bestanden", - "addon.mod_resource.openthefile": "Open het bestand", - "addon.mod_resource.uploadeddate": "Geüpload {{$a}}", - "addon.mod_scorm.asset": "Set", - "addon.mod_scorm.assetlaunched": "Set - bekeken", - "addon.mod_scorm.attempts": "Pogingen", - "addon.mod_scorm.averageattempt": "Gemiddelde van pogingen", - "addon.mod_scorm.browse": "Bekijken", - "addon.mod_scorm.browsed": "Voorgeproefd", - "addon.mod_scorm.browsemode": "Probeermodus", - "addon.mod_scorm.cannotcalculategrade": "Cijfer kon niet berekend worden.", - "addon.mod_scorm.completed": "Volledig", - "addon.mod_scorm.contents": "Inhoud", - "addon.mod_scorm.dataattemptshown": "Deze gegevens horen bij poging nummer {{number}}.", - "addon.mod_scorm.enter": "Start", - "addon.mod_scorm.errorcreateofflineattempt": "Er is een fout opgetreden tijdens het maken van een offline poging. Probeer opnieuw.", - "addon.mod_scorm.errordownloadscorm": "Fout bij het downloaden van SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Fout bij het ophalen van SCORM-gegevens.", - "addon.mod_scorm.errorinvalidversion": "Sorry, de applicatie ondersteund SCORM tot versie 1.2.", - "addon.mod_scorm.errornotdownloadable": "Het downloaden van SCORM-pakketten is uitgeschakeld. Neem contact op met je site-beheerder.", - "addon.mod_scorm.errornovalidsco": "Deze SCORM heeft geen zichtbare SCO om te laden.", - "addon.mod_scorm.errorpackagefile": "Sorry, de applicatie ondersteunt enkel ZIP-pakketten.", - "addon.mod_scorm.errorsyncscorm": "Er is een fout opgetreden tijdens het synchroniseren. Probeer opnieuw.", - "addon.mod_scorm.exceededmaxattempts": "Je hebt het maximale aantal pogingen bereikt.", - "addon.mod_scorm.failed": "Mislukt", - "addon.mod_scorm.firstattempt": "Eerste poging", - "addon.mod_scorm.gradeaverage": "Gemiddeld cijfer", - "addon.mod_scorm.gradeforattempt": "Beoordeling van de poging", - "addon.mod_scorm.gradehighest": "Hoogste cijfer", - "addon.mod_scorm.grademethod": "Beoordelingsmethode", - "addon.mod_scorm.gradereported": "Beoordeling gerapporteerd", - "addon.mod_scorm.gradescoes": "Leerobjecten", - "addon.mod_scorm.gradesum": "Totaalcijfer", - "addon.mod_scorm.highestattempt": "Beste poging", - "addon.mod_scorm.incomplete": "Onvolledig", - "addon.mod_scorm.lastattempt": "Laatste voltooide poging", - "addon.mod_scorm.modulenameplural": "SCORM-pakketten", - "addon.mod_scorm.newattempt": "Begin een nieuwe poging", - "addon.mod_scorm.noattemptsallowed": "Aantal toegelaten pogingen", - "addon.mod_scorm.noattemptsmade": "Aantal pogingen die je gedaan hebt", - "addon.mod_scorm.notattempted": "Niet geprobeerd", - "addon.mod_scorm.offlineattemptnote": "Deze poging bevat gegevens die nog niet gesynchroniseerd zijn.", - "addon.mod_scorm.offlineattemptovermax": "Deze poging kan niet verzonden worden omdat je het maximale aantal pogingen overschreden hebt.", - "addon.mod_scorm.organizations": "Organisaties", - "addon.mod_scorm.passed": "Geslaagd", - "addon.mod_scorm.reviewmode": "Nalezen", - "addon.mod_scorm.score": "Score", - "addon.mod_scorm.scormstatusnotdownloaded": "Deze SCORM is niet gedownload. Het zal automatisch gedownload worden als je het opent.", - "addon.mod_scorm.scormstatusoutdated": "Deze SCORM is gewijzigd sinds de laatste download. Het zal automatisch gedownload worden wanneer je het opent.", - "addon.mod_scorm.suspended": "Onderbroken", - "addon.mod_scorm.toc": "Inhoudsopgave", - "addon.mod_scorm.warningofflinedatadeleted": "Sommige offline gegevens van poging {{number}} zijn verwijderd omdat ze niet in een nieuwe poging konden omgezet worden.", - "addon.mod_scorm.warningsynconlineincomplete": "Sommige pogingen konden niet gesynchroniseerd worden met de site omdat de laatste online-poging niet beëindigd was. Beëindig eerst de online-poging.", - "addon.mod_survey.cannotsubmitsurvey": "Sorry, er was een probleem bij het insturen van je onderzoek. Probeer opnieuw.", - "addon.mod_survey.errorgetsurvey": "Fout bij het ophalen van onderzoeksdata.", - "addon.mod_survey.ifoundthat": "Ik vond dat", - "addon.mod_survey.ipreferthat": "Mijn voorkeur zou zijn", - "addon.mod_survey.modulenameplural": "Onderzoeken", - "addon.mod_survey.responses": "Antwoorden", - "addon.mod_survey.results": "Resultaten", - "addon.mod_survey.surveycompletednograph": "Je hebt dit onderzoek afgerond.", - "addon.mod_url.accessurl": "Ga naar de URL", - "addon.mod_url.modulenameplural": "URL's", - "addon.mod_url.pointingtourl": "URL waarnaar deze bron verwijst.", - "addon.mod_wiki.cannoteditpage": "Je kunt deze pagina niet bewerken.", - "addon.mod_wiki.createpage": "Maak pagina", - "addon.mod_wiki.editingpage": "Pagina '{{$a}}' aan het bewerken", - "addon.mod_wiki.errorloadingpage": "Er is een fout opgetreden tijdens het laden van de pagina.", - "addon.mod_wiki.errornowikiavailable": "De wiki heeft nog geen inhoud", - "addon.mod_wiki.gowikihome": "Ga naar de Wiki startpagina", - "addon.mod_wiki.map": "Map", - "addon.mod_wiki.modulenameplural": "Wiki's", - "addon.mod_wiki.newpagehdr": "Nieuwe pagina", - "addon.mod_wiki.newpagetitle": "Nieuwe paginatitel", - "addon.mod_wiki.nocontent": "Er is geen inhoud voor deze pagina", - "addon.mod_wiki.notingroup": "Niet in groep", - "addon.mod_wiki.pageexists": "Deze pagina bestaat al.", - "addon.mod_wiki.pagename": "Paginanaam", - "addon.mod_wiki.subwiki": "Sub-wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki pagina's", - "addon.mod_wiki.titleshouldnotbeempty": "Titel mag niet leeg zijn", - "addon.mod_wiki.viewpage": "Bekijk pagina", - "addon.mod_wiki.wikipage": "Wiki pagina", - "addon.mod_wiki.wrongversionlock": "Een andere gebruiker heeft deze pagina bewerkt terwijl jij er ook aan bezig was en jouw inhoud is verloren gegaan.", - "addon.mod_workshop.alreadygraded": "Al beoordeeld", - "addon.mod_workshop.areainstructauthors": "Instructies voor insturen", - "addon.mod_workshop.areainstructreviewers": "Instructies voor evaluatie", - "addon.mod_workshop.assess": "Evalueer", - "addon.mod_workshop.assessedsubmission": "Geëvalueerde inzending", - "addon.mod_workshop.assessmentform": "Evaluatievorm", - "addon.mod_workshop.assessmentsettings": "Beoordelingsinstellingen", - "addon.mod_workshop.assessmentstrategynotsupported": "Opdrachtstrategie {{$a}} wordt niet ondersteund", - "addon.mod_workshop.assessmentweight": "Weging evaluatie", - "addon.mod_workshop.assignedassessments": "Toegewezen inzendingen om te beoordelen", - "addon.mod_workshop.assignedassessmentsnone": "Je hebt geen inzending toegewezen om te evalueren", - "addon.mod_workshop.conclusion": "Conclusie", - "addon.mod_workshop.createsubmission": "Voeg inzending toe", - "addon.mod_workshop.deletesubmission": "Verwijder inzending", - "addon.mod_workshop.editsubmission": "Bewerk inzending", - "addon.mod_workshop.feedbackauthor": "Feedback voor de auteur", - "addon.mod_workshop.feedbackby": "Feedback door {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Feedback voor de beoordeler", - "addon.mod_workshop.givengrades": "Gegeven cijfers", - "addon.mod_workshop.gradecalculated": "Berekende cijfers voor de taak", - "addon.mod_workshop.gradeinfo": "Cijfer: {{$a.received}} op {{$a.max}}", - "addon.mod_workshop.gradeover": "Cijfer voor taak overschrijven", - "addon.mod_workshop.gradesreport": "Workshop cijfer rapport", - "addon.mod_workshop.gradinggrade": "Cijfer voor evaluatie", - "addon.mod_workshop.gradinggradecalculated": "Berekend cijfer voor beoordeling", - "addon.mod_workshop.gradinggradeof": "Cijfer voor beoordeling (van {{$a}})", - "addon.mod_workshop.gradinggradeover": "Overschrijf cijfer voor beoordeling", - "addon.mod_workshop.modulenameplural": "Workshops", - "addon.mod_workshop.nogradeyet": "Nog geen cijfer", - "addon.mod_workshop.notassessed": "Nog niet beoordeeld", - "addon.mod_workshop.notoverridden": "Niet overschreven", - "addon.mod_workshop.noyoursubmission": "Je hebt nog geen werk ingestuurd", - "addon.mod_workshop.overallfeedback": "Algemene feedback", - "addon.mod_workshop.publishedsubmissions": "Gepubliceerde taken", - "addon.mod_workshop.publishsubmission": "Publiceer taak", - "addon.mod_workshop.publishsubmission_help": "Gepubliceerde taken zijn beschikbaar voor anderen wanneer de workshop gesloten is.", - "addon.mod_workshop.reassess": "Evalueer opnieuw", - "addon.mod_workshop.receivedgrades": "Behaalde cijfers", - "addon.mod_workshop.submissionattachment": "Bijlage", - "addon.mod_workshop.submissioncontent": "Taakinhoud", - "addon.mod_workshop.submissiondeleteconfirm": "Weet je zeker dat je volgende inzending wil verwijderen?", - "addon.mod_workshop.submissiongrade": "Cijfer voor taak", - "addon.mod_workshop.submissiongradeof": "Cijfer voor taak (op {{$a}}", - "addon.mod_workshop.submissionrequiredcontent": "Je moet wat tekst of een bestand toevoegen.", - "addon.mod_workshop.submissionrequiredtitle": "Je moet een titel ingeven.", - "addon.mod_workshop.submissionsreport": "Workshop insturingsrapport", - "addon.mod_workshop.submissiontitle": "Titel", - "addon.mod_workshop.switchphase10": "Schakel naar opstartfase", - "addon.mod_workshop.switchphase20": "Schakel naar instuurfase", - "addon.mod_workshop.switchphase30": "Schakel naar beoordelingsfase", - "addon.mod_workshop.switchphase40": "Schakel naar beoordelingsfase", - "addon.mod_workshop.switchphase50": "Sluit workshop", - "addon.mod_workshop.userplan": "Workshop planner", - "addon.mod_workshop.userplancurrentphase": "Huidige fase", - "addon.mod_workshop.warningassessmentmodified": "De inzending is gewijzigd op de site.", - "addon.mod_workshop.warningsubmissionmodified": "De beoordeling is gewijzigd op de site.", - "addon.mod_workshop.weightinfo": "Weging: {{$a}}", - "addon.mod_workshop.yourassessment": "Jouw evaluatie", - "addon.mod_workshop.yourassessmentfor": "Jouw beoordeling voor {{$a}}", - "addon.mod_workshop.yourgrades": "Jouw cijfers", - "addon.mod_workshop.yoursubmission": "Jouw taak", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Opmerking voor {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Cijfer voor {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspect {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Je moet voor dit aspect een cijfer kiezen", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Opmerking voor {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspect {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Opmerking voor {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Cijfer voor {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Stelling {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Criterium {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Je moet één van deze items selecteren", - "addon.notes.addnewnote": "Nieuwe notitie toevoegen", - "addon.notes.coursenotes": "Cursusnotities", - "addon.notes.deleteconfirm": "Deze notitie verwijderen?", - "addon.notes.eventnotecreated": "Notitie gemaakt", - "addon.notes.eventnotedeleted": "Notitie verwijderd", - "addon.notes.nonotes": "Er zijn nog geen notities van dit type", - "addon.notes.note": "Notitie", - "addon.notes.notes": "Notities", - "addon.notes.personalnotes": "Persoonlijke notities", - "addon.notes.publishstate": "Context", - "addon.notes.sitenotes": "Sitenotities", - "addon.notes.userwithid": "Gebruiker met ID {{$a}}", - "addon.notes.warningnotenotsent": "Kon notities niet toevoegen aan cursus {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Fout bij het ophalen van meldingen.", - "addon.notifications.markallread": "Alles als gelezen markeren", - "addon.notifications.notificationpreferences": "Meldingen voorkeuren", - "addon.notifications.notifications": "Meldingen", - "addon.notifications.playsound": "Speel geluid", - "addon.notifications.therearentnotificationsyet": "Er zijn geen meldingen.", - "addon.storagemanager.deletecourse": "Verwijder alle cursusgegevens van je toestel", - "addon.storagemanager.deletecourses": "All cursusgegevens wissen", - "addon.storagemanager.deletedatafrom": "Verwijder gegevens van {{name}}", - "addon.storagemanager.info": "Bestanden die op je apparaat bewaard worden maken de app sneller en zorgen ervoor dat de app offline kan gebruikt worden. Je kunt deze bestanden veilig verwijderen als je ruimte op je toestel moet vrijmaken.", - "addon.storagemanager.managestorage": "Beheer opslag", - "addon.storagemanager.storageused": "Gebruikte opslagruimte:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Verenigde Arabische Emiraten", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua en Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albanië", - "assets.countries.AM": "Armenië", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarctica", - "assets.countries.AR": "Argentinië", - "assets.countries.AS": "Samoa-eilanden", - "assets.countries.AT": "Oostenrijk", - "assets.countries.AU": "Australië", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland Eilanden", - "assets.countries.AZ": "Azerbaidzjan", - "assets.countries.BA": "Bosnië-Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "België", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgarije", - "assets.countries.BH": "Bahrein", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Bolivia", - "assets.countries.BQ": "Bonaire, Sint Eustatius en Saba", - "assets.countries.BR": "Brazilië", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvet", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Wit-Rusland", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canada", - "assets.countries.CC": "Cocoseilanden", - "assets.countries.CD": "Congo (Democratische Republiek)", - "assets.countries.CF": "Centrafrika", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Zwitserland", - "assets.countries.CI": "Ivoorkust", - "assets.countries.CK": "Cook Eilanden", - "assets.countries.CL": "Chili", - "assets.countries.CM": "Kameroon", - "assets.countries.CN": "China", - "assets.countries.CO": "Colombia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Kaap Verdië", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Christmaseilanden", - "assets.countries.CY": "Cyprus", - "assets.countries.CZ": "Tsjechië", - "assets.countries.DE": "Duitsland", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Denemarken", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Dominikaanse Republiek", - "assets.countries.DZ": "Algerije", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estland", - "assets.countries.EG": "Egypte", - "assets.countries.EH": "Westelijke Sahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spanje", - "assets.countries.ET": "Ethiopië", - "assets.countries.FI": "Finland", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Falklandeilanden", - "assets.countries.FM": "Micronesië", - "assets.countries.FO": "Faroe eilanden", - "assets.countries.FR": "Frankrijk", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Verenigd Koninkrijk", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgië", - "assets.countries.GF": "Frans Guiana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Groenland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Equatoriaal Guinea", - "assets.countries.GR": "Griekenland", - "assets.countries.GS": "Zuid-Georgië en de Zuidelijke Sandwicheilanden", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinee-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Heard en McDonald Eilanden", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Kroatië (Hrvatska)", - "assets.countries.HT": "Haïti", - "assets.countries.HU": "Hongarije", - "assets.countries.ID": "Indonesië", - "assets.countries.IE": "Ierland", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Isle Of Man", - "assets.countries.IN": "India", - "assets.countries.IO": "Brits Indische Oceaan Territorium", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran", - "assets.countries.IS": "Ysland", - "assets.countries.IT": "Italië", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordanië", - "assets.countries.JP": "Japan", - "assets.countries.KE": "Kenië", - "assets.countries.KG": "Kirgizië", - "assets.countries.KH": "Cambodia", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comoren", - "assets.countries.KN": "Saint Kitts en Nevis", - "assets.countries.KP": "Korea (Democratische Republiek)", - "assets.countries.KR": "Korea (Republiek)", - "assets.countries.KW": "Koeweit", - "assets.countries.KY": "Kaaimaneilanden", - "assets.countries.KZ": "Kazachstan", - "assets.countries.LA": "Laos (Democratische Volksrepubliek)", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Saint lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberië", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litouwen", - "assets.countries.LU": "Luxemburg", - "assets.countries.LV": "Letland", - "assets.countries.LY": "Libië", - "assets.countries.MA": "Marokko", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldavië", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin (Frans deel)", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Marchall eilanden", - "assets.countries.MK": "Noord-Macedonië", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolië", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Marianen", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauritius", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Maladiven", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexico", - "assets.countries.MY": "Maleisië", - "assets.countries.MZ": "Mozambique", - "assets.countries.NA": "Namibië", - "assets.countries.NC": "Nieuw Caledonië", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolk", - "assets.countries.NG": "Nigerië", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Nederland", - "assets.countries.NO": "Noorwegen", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nieuw Zeeland", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Frans Polinesië", - "assets.countries.PG": "Papua Nieuw Guinea", - "assets.countries.PH": "Filipijnen", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polen", - "assets.countries.PM": "Saint-Pierre en Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestina", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Reunion", - "assets.countries.RO": "Roemenië", - "assets.countries.RS": "Servië", - "assets.countries.RU": "Russische Federatie", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Saudi Arabië", - "assets.countries.SB": "Solomoneilanden", - "assets.countries.SC": "Seychellen", - "assets.countries.SD": "Soedan", - "assets.countries.SE": "Zweden", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "Sint Helena, Ascension en Tristan da Cunha", - "assets.countries.SI": "Slovenië", - "assets.countries.SJ": "Spitsbergen en Jan Mayen-eilanden", - "assets.countries.SK": "Slovakije", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalië", - "assets.countries.SR": "Suriname", - "assets.countries.SS": "Zuid-Soedan, Republiek", - "assets.countries.ST": "Sao Tome en Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (Nederlands deel)", - "assets.countries.SY": "Syrië", - "assets.countries.SZ": "Swaziland", - "assets.countries.TC": "Turks- en Caicoseilanden", - "assets.countries.TD": "Tsjaad", - "assets.countries.TF": "Frankrijk, overzeese gebieden", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailand", - "assets.countries.TJ": "Tadzjikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunesië", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turkije", - "assets.countries.TT": "Trinidad en Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzania", - "assets.countries.UA": "Oekraïne", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Kleine Pacifische eilanden van de Verenigde Staten", - "assets.countries.US": "Verenigde Staten", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Vaticaanstad", - "assets.countries.VC": "Sint Vincent en de Grenadines", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Maagdeneilanden (Brits)", - "assets.countries.VI": "Maagdeneilanden (VS)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis en Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Zuid-Afrika", - "assets.countries.ZM": "Zambië", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB e-book", - "assets.mimetypes.application/msword": "Word-document", - "assets.mimetypes.application/pdf": "PDF-document", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle-back-up", - "assets.mimetypes.application/vnd.ms-excel": "Excell rekenblad", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 werkboek met macro's ingeschakeld", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint presentatie", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument rekenblad", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument rekenbladsjabloon", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument tekstdocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument tekstsjabloon", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument webpaginasjabloon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 presentatie", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 slideshow", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 rekenblad", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 sjabloon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 document", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Iwork Keynote-presentatie", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers-rekenblad", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWorks Pages-document", - "assets.mimetypes.application/x-javascript": "JavaScript-bron", - "assets.mimetypes.application/x-mspublisher": "Publisher-document", - "assets.mimetypes.application/x-shockwave-flash": "Flash-animatie", - "assets.mimetypes.application/xhtml_xml": "XHTML-document", - "assets.mimetypes.archive": "Archief ({{$a.EXT}})", - "assets.mimetypes.audio": "Audiobestand ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Bestand", - "assets.mimetypes.group:archive": "Archiefbestanden", - "assets.mimetypes.group:audio": "Audiobestanden", - "assets.mimetypes.group:document": "Documentbestanden", - "assets.mimetypes.group:html_audio": "Door browsers ondersteunde audiobestanden", - "assets.mimetypes.group:html_track": "HTML track-bestanden", - "assets.mimetypes.group:html_video": "Door browsers ondersteunde videobestanden", - "assets.mimetypes.group:image": "Afbeeldingsbestanden", - "assets.mimetypes.group:presentation": "Presentatiebestanden", - "assets.mimetypes.group:sourcecode": "Broncode", - "assets.mimetypes.group:spreadsheet": "Rekenbladbestanden", - "assets.mimetypes.group:video": "Videobestanden", - "assets.mimetypes.group:web_audio": "Audiobestanden gebruikt op het web", - "assets.mimetypes.group:web_file": "Webbestanden", - "assets.mimetypes.group:web_image": "Afbeeldingsbestanden gebruikt op het web", - "assets.mimetypes.group:web_video": "Videobestanden gebruikt op het web", - "assets.mimetypes.image": "Afbeelding ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windowsicoon", - "assets.mimetypes.text/css": "Cascading Style-Sheet", - "assets.mimetypes.text/csv": "Kommagescheiden waarden", - "assets.mimetypes.text/html": "HTML-document", - "assets.mimetypes.text/plain": "Tekstbestand", - "assets.mimetypes.text/rtf": "RTF-document", - "assets.mimetypes.text/vtt": "Web video tekstspoor", - "assets.mimetypes.video": "Videobestand ({{$a.EXT}})", - "core.accounts": "Gebruikers", - "core.add": "Voeg toe", - "core.agelocationverification": "Leeftijd- en locatieverificatie", - "core.ago": "{{$a}} geleden", - "core.all": "Alle", - "core.allgroups": "Alle groepen", - "core.allparticipants": "Alle deelnemers", - "core.answer": "Antwoord", - "core.answered": "Beantwoord", - "core.areyousure": "Weet je het zeker?", - "core.back": "Terug", - "core.block.blocks": "Blokken", - "core.browser": "Browser", - "core.cancel": "Annuleer", - "core.cannotconnect": "Kan niet verbinden", - "core.cannotconnecttrouble": "Er zijn moeilijkheden om verbinding te maken met je site.", - "core.cannotconnectverify": "Controleer of het adres juist is.", - "core.cannotdownloadfiles": "Bestanden downloaden is uitgeschakeld voor jouw mobiele service. Neem contact op met je systeembeheerder.", - "core.captureaudio": "Audio opnemen", - "core.capturedimage": "Foto genomen.", - "core.captureimage": "Maak foto", - "core.capturevideo": "Film opnemen", - "core.category": "Categorie", - "core.choose": "Kies", - "core.choosedots": "Kies...", - "core.clearsearch": "Zoekresultaten leegmaken", - "core.clicktohideshow": "Klik om te vergroten of te verkleinen", - "core.clicktoseefull": "Klik hier om de volledige inhoud te zien", - "core.close": "Sluit", - "core.comments": "Notities", - "core.comments.addcomment": "Notitie toevoegen", - "core.comments.comments": "Notities", - "core.comments.commentscount": "Opmerkingen ({{$a}})", - "core.comments.commentsnotworking": "Opmerkingen kunnen niet worden opgehaald", - "core.comments.deletecommentbyon": "Verwijder opmerking gepost door {{$a.user}} op {{$a.time}}", - "core.comments.eventcommentcreated": "Opmerking gemaakt", - "core.comments.eventcommentdeleted": "Opmerking verwijderd", - "core.comments.nocomments": "Geen commentaren", - "core.comments.savecomment": "Bewaar notitie", - "core.comments.warningcommentsnotsent": "Kan reacties niet synchroniseren. {{error}}", - "core.commentscount": "Opmerkingen ({{$a}})", - "core.completion-alt-auto-fail": "Voltooid: {{$a}} (bereikte het cijfer voor geslaagd niet)", - "core.completion-alt-auto-n": "Niet voltooid: {{$a}}", - "core.completion-alt-auto-n-override": "Niet voltooid: {{$a.modname}} (ingesteld door {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Voltooid: {{$a}} (slaagcijfer bereikt)", - "core.completion-alt-auto-y": "Voltooid: {{$a}}", - "core.completion-alt-auto-y-override": "Voltooid: {{$a.modname}} (ingesteld door {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Niet voltooid: {{$a}}. Selecteer om te markeren als voltooid", - "core.completion-alt-manual-n-override": "Niet voltooid: {{$a.modname}} (ingesteld door {{$a.overrideuser}}). Selecteer om als voltooid te markeren.", - "core.completion-alt-manual-y": "voltooid: {{$a}}. Selecteer om als niet voltooid te markeren.", - "core.completion-alt-manual-y-override": "Voltooid: {{$a.modname}} (ingesteld door {{$a.overrideuser}}). Selecteer om als niet voltooid te markeren.", - "core.confirmcanceledit": "Weet je zeker dat je deze pagina wil verlaten? Alle wijzigingen zullen verloren gaan.", - "core.confirmdeletefile": "Wil je dit bestand echt verwijderen?", - "core.confirmgotabroot": "Weet je zeker dat je terug wilt naar {{name}}?", - "core.confirmgotabrootdefault": "Weet je zeker dat je naar de eerste pagina van het huidige tabblad wil gaan?", - "core.confirmleaveunknownchanges": "Weet je zeker dat je deze pagina wil verlaten? Wijzigingen die niet bewaard zijn zullen verloren gaan.", - "core.confirmloss": "Ben je zeker? Alle wijzigingen zullen verloren gaan.", - "core.confirmopeninbrowser": "Wil je het openen in je browser?", - "core.considereddigitalminor": "Je bent te jong om een account te maken op deze site.", - "core.content": "Inhoud", - "core.contenteditingsynced": "De inhoud die je aan het bewerken bent, is gesynchroniseerd.", - "core.contentlinks.chooseaccount": "Kies account", - "core.contentlinks.chooseaccounttoopenlink": "Kies een account om de link mee te openen.", - "core.contentlinks.confirmurlothersite": "Deze link verwijst naar een andere site. Wil je dit openen?", - "core.contentlinks.errornoactions": "Kon geen actie vinden om uit te voeren met deze link.", - "core.contentlinks.errornosites": "Kon geen site vinden om deze link te behandelen.", - "core.contentlinks.errorredirectothersite": "De redirect URL kan niet naar een andere site wijzen.", - "core.continue": "Ga door", - "core.copiedtoclipboard": "Tekst gekopieerd naar klembord", - "core.copytoclipboard": "Kopieer naar klembord", - "core.course": "Cursus", - "core.course.activitydisabled": "Jouw organisatie heeft deze activiteit uitgeschakeld in de mobiele app.", - "core.course.activitynotyetviewableremoteaddon": "Jouw organisatie heeft een plugin geïnstalleerd die nog niet ondersteund wordt.", - "core.course.activitynotyetviewablesiteupgradeneeded": "De site gebruikt de niet laatste Moodle-versie en moet worden geüpdatet. Neem contact op met de site-beheerder.", - "core.course.allsections": "Alle secties", - "core.course.askadmintosupport": "Neem contact op met de sitebeheerder en geef aan dat je deze activiteit met de Moodle mobile app wilt gebruiken.", - "core.course.availablespace": "Je hebt ongeveer {{available}} vrije ruimte.", - "core.course.cannotdeletewhiledownloading": "Bestanden kunnen niet verwijderd worden terwijl de activiteit gedownload wordt. Wacht tot de download klaar is.", - "core.course.confirmdeletemodulefiles": "Weet je zeker dat je deze modulebestanden wil verwijderen?", - "core.course.confirmdownload": "Je gaat {{size}}.{{availableSpace}} downloaden. Weet je zeker dat je verder wil gaan?", - "core.course.confirmdownloadunknownsize": "We konden de grootte van de download.{{availableSpace}} niet berekenen. Weet je zeker dat je toch wil downloaden?", - "core.course.confirmdownloadzerosize": "Je gaat beginnen met downloaden. {{AvailableSpace}} Weet je zeker dat je wilt doorgaan?", - "core.course.confirmlimiteddownload": "Je bent nu niet via Wi-Fi verbonden.", - "core.course.confirmpartialdownloadsize": "Je staat op het punt om minstens {{size}}.{{availableSpace}} te downloaden. Weet je zeker dat je verder wil gaan?", - "core.course.contents": "Inhoud", - "core.course.couldnotloadsectioncontent": "Kon sectie-inhoud niet laden. Probeer later opnieuw.", - "core.course.couldnotloadsections": "Kon secties niet laden. Probeer later opnieuw.", - "core.course.coursesummary": "Samenvatting cursus", - "core.course.downloadcourse": "Download cursus", - "core.course.errordownloadingcourse": "Fout bij het downloaden van de cursus", - "core.course.errordownloadingsection": "Fout bij het downloaden van de sectie.", - "core.course.errorgetmodule": "Fout bij het ophalen van activiteitsgegevens.", - "core.course.hiddenfromstudents": "Verborgen voor leerlingen", - "core.course.hiddenoncoursepage": "Beschikbaar, maar niet getoond op de cursuspagina", - "core.course.insufficientavailablequota": "Je toestel kon geen ruimte toewijzen om deze download te bewaren. Misschien reserveert het ruimte voor app en systeem-updates. Maak eerst wat ruimte vrij.", - "core.course.insufficientavailablespace": "Je probeert {{size}} te downloaden. Hierdoor zal je toestel onvoldoende vrije ruimte overhouden om normaal te functioneren. Maak eerst wat ruimte vrij.", - "core.course.manualcompletionnotsynced": "Manueel voltooien niet gesynchroniseerd.", - "core.course.nocontentavailable": "Geen inhoud beschikbaar.", - "core.course.overriddennotice": "Je totaalcijfer voor deze activiteit is manueel aangepast.", - "core.course.refreshcourse": "Cursus vernieuwen", - "core.course.sections": "Secties", - "core.course.useactivityonbrowser": "Je kunt de activiteit nog steeds via de browser van je apparaat gebruiken.", - "core.course.warningmanualcompletionmodified": "Het manueel voltooien van een activiteit werd gewijzigd op de site.", - "core.course.warningofflinemanualcompletiondeleted": "Sommige van de offline manueel aangeduide voltooiingen van cursus '{{name}}' zijn verwijderd. {{error}}", - "core.coursedetails": "Cursusdetails", - "core.coursenogroups": "Je bent geen lid van een groep van deze cursus.", - "core.courses.addtofavourites": "Toevoegen aan favorieten", - "core.courses.allowguests": "In deze cursus zijn gasten toegestaan", - "core.courses.availablecourses": "Beschikbare cursussen", - "core.courses.cannotretrievemorecategories": "Categorieën dieper dan niveau {{$a}} kunnen niet opgehaald worden.", - "core.courses.categories": "Cursuscategorieën", - "core.courses.confirmselfenrol": "Weet je zeker dat je je wil aanmelden in deze cursus?", - "core.courses.courses": "Cursussen", - "core.courses.downloadcourses": "Download cursussen", - "core.courses.enrolme": "Meld me aan", - "core.courses.errorloadcategories": "Er is een fout opgetreden bij het ophalen van categorieën.", - "core.courses.errorloadcourses": "Er is een fout opgetreden tijdens het laden van de cursussen.", - "core.courses.errorloadplugins": "De plugins, vereist door deze cursus, konden niet correct geladen worden. Herstart de app en probeer opnieuw.", - "core.courses.errorsearching": "Er is een fout opgetreden tijdens het zoeken.", - "core.courses.errorselfenrol": "Er is een fout opgetreden tijdens het zelf aanmelden.", - "core.courses.filtermycourses": "Filter mijn cursussen", - "core.courses.frontpage": "Startpagina", - "core.courses.hidecourse": "Verberg", - "core.courses.ignore": "Negeer", - "core.courses.mycourses": "Mijn cursussen", - "core.courses.mymoodle": "Mijn startpagina", - "core.courses.nocourses": "Geen cursusinformatie", - "core.courses.nocoursesyet": "Er zijn geen cursussen in deze categorie", - "core.courses.nosearchresults": "Geen resultaten", - "core.courses.notenroled": "Je bent niet aangemeld in deze cursus", - "core.courses.notenrollable": "Je kunt jezelf niet aanmelden in deze cursus.", - "core.courses.password": "Aanmeldingssleutel", - "core.courses.paymentrequired": "Betaling vereist om deze cursus binnen te gaan.", - "core.courses.paypalaccepted": "Paypal-betalingen aanvaard", - "core.courses.reload": "Opnieuw laden", - "core.courses.removefromfavourites": "Verwijderen uit favorieten", - "core.courses.search": "Zoek", - "core.courses.searchcourses": "Zoek cursussen", - "core.courses.searchcoursesadvice": "Je kunt via de cursus zoekknop toegang krijgen als gast of jezelf aanmelden in cursussen die dit toestaan.", - "core.courses.selfenrolment": "Zelf aanmelden", - "core.courses.sendpaymentbutton": "Stuur betaling via Paypal", - "core.courses.show": "Toon deze cursus", - "core.courses.totalcoursesearchresults": "Totaal aantal cursusen: {{$a}}", - "core.currentdevice": "Huidig apparaat", - "core.datastoredoffline": "Gegevens die bewaard werden op het toestel konden niet verstuurd worden. Ze zullen later automatisch verzonden worden.", - "core.date": "Datum", - "core.day": "dag", - "core.days": "dagen", - "core.decsep": ",", - "core.defaultvalue": "Standaard ({{$a}})", - "core.delete": "Verwijder", - "core.deletedoffline": "Offline verwijderd", - "core.deleteduser": "Verwijderde gebruiker", - "core.deleting": "Verwijderen.", - "core.description": "Beschrijving", - "core.desktop": "Bureaublad", - "core.dfdaymonthyear": "MM-DD-JJJJ", - "core.dfdayweekmonth": "ddd, D, MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Digitale minderjarige.", - "core.digitalminor_desc": "Vraag je ouders / voogd om contact op te nemen met:", - "core.discard": "Verwerpen", - "core.dismiss": "Verwijderen", - "core.displayoptions": "Toon-opties", - "core.done": "Voltooid", - "core.download": "Download", - "core.downloaded": "Gedownload", - "core.downloading": "Downloaden", - "core.edit": "Bewerk", - "core.editor.autosavesucceeded": "Bewaard.", - "core.editor.bold": "Vet", - "core.editor.clear": "Opmaak verwijderen", - "core.editor.h3": "Kop (groot)", - "core.editor.h4": "Kop (midden)", - "core.editor.h5": "Kop (klein)", - "core.editor.hidetoolbar": "Verberg Toolbar", - "core.editor.italic": "Cursief", - "core.editor.orderedlist": "Geordende lijst", - "core.editor.p": "Alinea", - "core.editor.strike": "Doorhalen", - "core.editor.textrecovered": "Een bewaarde versie van deze tekst werd automatisch teruggezet.", - "core.editor.toggle": "Toggle-editor", - "core.editor.underline": "Onderlijn", - "core.editor.unorderedlist": "Ongeordende lijst", - "core.emptysplit": "Deze pagina zal leeg verschijnen als het linker paneel leeg of aan het laden is.", - "core.error": "Fout", - "core.errorchangecompletion": "Er is een fout opgetreden tijdens het wijzigen van de voltooiingsstatus. Probeer opnieuw.", - "core.errordeletefile": "Fout tijdens het verwijderen van het bestand. Probeer opnieuw.", - "core.errordownloading": "Fout bij downloaden bestand", - "core.errordownloadingsomefiles": "Fout bij het downloaden van modulebestanden. Sommige bestanden kunnen ontbreken.", - "core.errorfileexistssamename": "Er is al een bestand met deze naam.", - "core.errorinvalidform": "Het formulier bevat ongeldige gegevens. Zorg ervoor dat je alle vereiste velden hebt ingevuld en dat de gegevens geldig zijn.", - "core.errorinvalidresponse": "Ongeldig antwoord gekregen. Contacteer je Moodle site-beheerder als het probleem zich blijft voordoen.", - "core.errorloadingcontent": "Fout bij het laden van inhoud.", - "core.errorofflinedisabled": "Offline browsen is uitgeschakeld op je site. Je moet verbinding hebben met het internet om de app te kunnen gebruiken.", - "core.erroropenfilenoapp": "Fout bij het openen van het bestand: geen enkele app gevonden om dit soort bestand te openen.", - "core.erroropenfilenoextension": "Fout bij het openen van het bestand: het bestand heeft geen extentie.", - "core.erroropenpopup": "Deze activiteit probeert een popup te openen. Dit wordt niet ondersteund in deze app.", - "core.errorrenamefile": "Fout bij het hernoemen van het bestand. Probeer opnieuw.", - "core.errorsomedatanotdownloaded": "Als je deze activiteit hebt gedownload, hou er dan rekening mee dat sommige gegevens tijdens het downloadproces niet worden gedownload om redenen van prestaties en gegevensgebruik.", - "core.errorsync": "Er is een fout opgetreden tijdens het synchroniseren. Probeer opnieuw.", - "core.errorsyncblocked": "Deze {{$a}} kan nu niet gesynchroniseerd worden omdat er nog een ander proces anders bezig is. Probeer later opnieuw. Als het probleem blijft aanhouden, probeer dan de app te herstarten.", - "core.explanationdigitalminor": "Deze informatie is vereist om te bepalen of je leeftijd hoger is dan de digitale leeftijd die vereist is. Dit is de leeftijd waarop een persoon kan instemmen met de algemene voorwaarden en dat zijn gegevens legaal worden opgeslagen en verwerkt.", - "core.favourites": "Met ster", - "core.filename": "Bestandsnaam", - "core.filenameexist": "Bestandsnaam bestaat al: {{$a}}", - "core.filenotfound": "Bestand niet gevonden.", - "core.fileuploader.addfiletext": "Bestand toevoegen", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Camera", - "core.fileuploader.confirmuploadfile": "Je gaat {{size}} uploaden. Weet je zeker dat je wil verder gaan?", - "core.fileuploader.confirmuploadunknownsize": "We konden de grootte van de upload niet berekenen. Weet je zeker dat je wil verder gaan?", - "core.fileuploader.errorcapturingaudio": "Fout tijdens het opnemen van audio.", - "core.fileuploader.errorcapturingimage": "Fout bij het nemen van een foto.", - "core.fileuploader.errorcapturingvideo": "Fout bij het maken van een video.", - "core.fileuploader.errorgettingimagealbum": "Fout bij het ophalen van een foto uit het album.", - "core.fileuploader.errormustbeonlinetoupload": "Je moet online zijn om bestanden te kunnen uploaden.", - "core.fileuploader.errornoapp": "Je hebt geen app geïnstalleerd om dit te kunnen doen.", - "core.fileuploader.errorreadingfile": "Fout bij het lezen van het bestand.", - "core.fileuploader.errorwhileuploading": "Er is een fout opgetreden tijdens het uploaden van het bestand.", - "core.fileuploader.file": "Bestand", - "core.fileuploader.filesofthesetypes": "Geaccepteerde bestandstypes:", - "core.fileuploader.fileuploaded": "Het uploaden van het bestand is gelukt.", - "core.fileuploader.invalidfiletype": "{{$a}} bestandstype wordt niet aanvaard", - "core.fileuploader.maxbytesfile": "Het bestand {{$a.file}} is te groot. De maximale uploadgrootte is {{$a.size}}.", - "core.fileuploader.more": "Meer", - "core.fileuploader.photoalbums": "Foto-albums", - "core.fileuploader.readingfile": "Bestand lezen", - "core.fileuploader.readingfileperc": "Lezen bestand: {{$a}}%", - "core.fileuploader.selectafile": "Selecteer een bestand", - "core.fileuploader.uploadafile": "Bestand uploaden", - "core.fileuploader.uploading": "Aan het uploaden", - "core.fileuploader.uploadingperc": "Uploaden: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filter", - "core.folder": "Map", - "core.forcepasswordchangenotice": "Je moet je wachtwoord wijzigen om verder te kunnen gaan", - "core.fulllistofcourses": "Alle cursussen", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Gemiddelde", - "core.grades.badgrade": "Beoordeling ongeldig", - "core.grades.contributiontocoursetotal": "Aandeel in cursustotaal", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Beoordeling", - "core.grades.gradeitem": "Beoordelingsitem", - "core.grades.grades": "Cijfers", - "core.grades.lettergrade": "Letterbeoordeling", - "core.grades.nogradesreturned": "Geen cijfers", - "core.grades.nooutcome": "Geen competentie", - "core.grades.percentage": "Percentage", - "core.grades.range": "Marge", - "core.grades.rank": "Ranglijst", - "core.grades.weight": "Weging", - "core.group": "Groep", - "core.groupsseparate": "Gescheiden groepen", - "core.groupsvisible": "Zichtbare groepen", - "core.h5p.additionallicenseinfo": "Bijkomende informatie over de licentie", - "core.h5p.author": "Auteur", - "core.h5p.authorcomments": "Opmerkingen auteur", - "core.h5p.authorcommentsdescription": "Opmerkingen voor wie de inhoud bewerkt. (Deze tekst zal niet gepubliceerd worden als deel van de kopieerrecht-informatie.)", - "core.h5p.authorname": "Naam auteur", - "core.h5p.authorrole": "Rol auteur", - "core.h5p.by": "door", - "core.h5p.cancellabel": "Annuleer", - "core.h5p.ccattribution": "Naamsvermelding (CC BY)", - "core.h5p.ccattributionnc": "Naamsvermelding - Niet-commercieel (CC BY-NC)", - "core.h5p.ccattributionncnd": "Naamsvermelding - Niet-commercieel - GeenAfgeleideWerken (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Naamsvermelding - Niet-commercieel - GelijkDelen (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Naamsvermelding - GeenAfgeleideWerken (CC BY-ND)", - "core.h5p.ccattributionsa": "Naamsvermelding - GelijkDelen (CC BY-SA)", - "core.h5p.ccpdd": "Publiek Domein Verklaring (CCO)", - "core.h5p.changedby": "Gewijzigd door", - "core.h5p.changedescription": "Beschrijving van de wijziging", - "core.h5p.changelog": "Wijzigingslog", - "core.h5p.changeplaceholder": "Foto bijgesneden, tekst gewijzigd enz.", - "core.h5p.close": "Sluit", - "core.h5p.confirmdialogbody": "Bevestig dat je wil verder gaan. Deze actie is onomkeerbaar.", - "core.h5p.confirmdialogheader": "Bevestig actie", - "core.h5p.confirmlabel": "Bevestig", - "core.h5p.connectionLost": "Verbinding verbroken. De resultaten zullen bewaard worden en verstuurd worden wanneer je terug een verbinding hebt.", - "core.h5p.connectionReestablished": "Verbinding hersteld.", - "core.h5p.contentCopied": "Inhoud gekopieerd naar het klembord", - "core.h5p.contentchanged": "Deze inhoud is gewijzigd sinds de laatste keer dat je die gebruikt hebt.", - "core.h5p.contenttype": "Inhoudstype", - "core.h5p.copyright": "Gebruiksrechten", - "core.h5p.copyrightinfo": "Kopieerrechtinformatie", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "Bekijk het kopieerrecht voor deze inhoud.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Datum", - "core.h5p.disablefullscreen": "Volledig scherm uitschakelen", - "core.h5p.download": "Download", - "core.h5p.downloadtitle": "Download de inhoud als een H5P-bestand.", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Inbedden", - "core.h5p.embedtitle": "Bekijk de embed-code voor deze inhoud.", - "core.h5p.errorgetemail": "Fout bij het verkrijgen van de e-mail van de gebruiker. Controleer je verbinding en probeer opnieuw.", - "core.h5p.fullscreen": "Volledig scherm", - "core.h5p.gpl": "General Public License v3", - "core.h5p.h5ptitle": "Bezoek H5P.org om meer inhoud te bekijken.", - "core.h5p.hideadvanced": "Geavanceerd verbergen", - "core.h5p.license": "Licentie", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Universeel", - "core.h5p.licenseCC20": "2.0 Universeel", - "core.h5p.licenseCC25": "2.5 Universeel", - "core.h5p.licenseCC30": "3.0 Unported", - "core.h5p.licenseCC40": "4.0 Internationaal", - "core.h5p.licenseGPL": "General Public License", - "core.h5p.licenseV1": "Versie 1", - "core.h5p.licenseV2": "Versie 2", - "core.h5p.licenseV3": "Versie 3", - "core.h5p.licensee": "Licentiehouder", - "core.h5p.licenseextras": "Licentie-extra's", - "core.h5p.licenseversion": "Licentieversie", - "core.h5p.nocopyright": "Geen auteursrechtinformatie beschikbaar voor deze inhoud.", - "core.h5p.offlineDialogBody": "We konden geen informatie verzenden over uw voltooiing van deze taak. Controleer je internetverbinding.", - "core.h5p.offlineDialogHeader": "Je verbinding met de server is verbroken", - "core.h5p.offlineDialogRetryButtonLabel": "Probeer het nu opnieuw", - "core.h5p.offlineDialogRetryMessage": "Opnieuw proberen: aantal ....", - "core.h5p.offlineSuccessfulSubmit": "Resultaten succesvol ingediend.", - "core.h5p.offlinedisabled": "De site staat het downloaden van H5P-pakketten niet toe.", - "core.h5p.originator": "Oprichter", - "core.h5p.pd": "Publiek Domein", - "core.h5p.pddl": "Publiek Domein Verklaring en Licentie", - "core.h5p.pdm": "Public Domain Mark (PDM)", - "core.h5p.play": "H5P afspelen", - "core.h5p.resizescript": "Neem dit script op in je website als je dynamische grootte van de ingesloten inhoud wil:", - "core.h5p.resubmitScores": "Poging om opgeslagen resultaten in te dienen.", - "core.h5p.reuse": "Hergebruik", - "core.h5p.reuseContent": "Inhoud opnieuw gebruiken", - "core.h5p.reuseDescription": "Hergebruik deze inhoud.", - "core.h5p.showadvanced": "Toon geavanceerd", - "core.h5p.showless": "Toon minder", - "core.h5p.showmore": "Toon meer", - "core.h5p.size": "Grootte", - "core.h5p.source": "Bron", - "core.h5p.startingover": "Je zult opnieuw beginnen.", - "core.h5p.sublevel": "Subniveau", - "core.h5p.thumbnail": "Miniatuurafbeelding", - "core.h5p.title": "Titel", - "core.h5p.undisclosed": "Niet openbaar", - "core.h5p.year": "Jaar", - "core.h5p.years": "Jaren", - "core.h5p.yearsfrom": "Jaren (vanaf)", - "core.h5p.yearsto": "Jaren (tot)", - "core.hasdatatosync": "Dit {{$a}} heeft offline data om te synchroniseren.", - "core.help": "Help", - "core.hide": "Verberg", - "core.hour": "uur", - "core.hours": "uren", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Afbeelding", - "core.imageviewer": "Afbeeldingsviewer", - "core.info": "Informatie", - "core.invalidformdata": "Foute formuliergegevens", - "core.labelsep": ": ", - "core.lastaccess": "Laatste bezoek", - "core.lastdownloaded": "Laatste download", - "core.lastmodified": "Laatste wijziging", - "core.lastsync": "Laatste synchronisatie", - "core.layoutgrid": "Tabel", - "core.list": "Lijst", - "core.listsep": ";", - "core.loading": "Aan het laden", - "core.loadmore": "Meer laden", - "core.location": "Plaats", - "core.login.auth_email": "E-mail zelfregistratie", - "core.login.authenticating": "Authenticeren", - "core.login.cancel": "Annuleer", - "core.login.changepassword": "Wijzig wachtwoord", - "core.login.changepasswordbutton": "Open de wijzig wachtwoordpagina", - "core.login.changepasswordhelp": "Contacteer je site-beheerder als je problemen hebt met het wijzigen van je wachtwoord. \"Site-beheerders\" zijn mensen die Moodle beheren op jouw school / universiteit / bedrijf of scholingsorganisatie. Als je niet weet wie te contacteren, spreek dan je leraar/trainer aan.", - "core.login.changepasswordinstructions": "Je kunt je wachtwoord niet wijzigen in de app. Klik op onderstaande knop om de site te openen in een browser om je wachtwoord te wijzigen. Sluit je browser na het wijzigen van je wachtwoord, want je zult niet automatisch terug naar de app gestuurd worden.", - "core.login.changepasswordlogoutinstructions": "Als je naar een andere site wil of wil afmelden, klik dan volgende knop:", - "core.login.changepasswordreconnectinstructions": "Klik op volgende knop om opnieuw te verbinden met de site. (Je zult terug naar het vorige scherm gebracht worden als je je wachtwoord fout ingeeft.)", - "core.login.confirmdeletesite": "Weet je zeker dat je de site {{sitename}} wil verwijderen?", - "core.login.connect": "Verbinden!", - "core.login.connecttomoodle": "Verbinden met Moodle", - "core.login.connecttomoodleapp": "Je probeert verbinding te maken met een gewone Moodle-site. Download de officiële Moodle-app om toegang te krijgen tot deze site.", - "core.login.connecttoworkplaceapp": "Je probeert verbinding te maken met een Moodle Workplace-site. Download de Moodle Workplace-app om toegang te krijgen tot deze site.", - "core.login.contactyouradministrator": "Neem contact op met je site-beheerder voor meer hulp.", - "core.login.contactyouradministratorissue": "Vraag aan je site-beheerder om volgend probleem te onderzoeken: {{$a}}", - "core.login.createaccount": "Maak mijn nieuwe account aan", - "core.login.createuserandpass": "Kies een gebruikersnaam en wachtwoord", - "core.login.credentialsdescription": "Geef je gebruikersnaam en wachtwoord op om je aan te melden", - "core.login.emailconfirmsent": "

                  Als het goed is, is er een e-mail verzonden naar {{$a}}

                  \n

                  Daarin staan eenvoudige instructies voor het voltooien van de registratie.

                  \n

                  Indien je moeilijkheden blijft ondervinden, neem dan contact op met je sitebeheerder.

                  ", - "core.login.emailconfirmsentnoemail": "

                  Er zou een e-mail naar jouw adres verstuurd moeten zijn.

                  Die bevat makkelijke instructies om je registratie te voltooien.

                  Als je blijft problemen ondervinden, neem dan contact op met je site-beheerder.

                  ", - "core.login.emailconfirmsentsuccess": "Bevestigingsmail is verstuurd", - "core.login.emailnotmatch": "E-mailadressen komen niet overeen", - "core.login.erroraccesscontrolalloworigin": "De Cross-Origin call die je probeerde uit te voeren, werd geweigerd. Controleer https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Er is een fout opgetreden bij het verwijderen van deze site. Probeer opnieuw.", - "core.login.errorexampleurl": "De URL https://campus.example.edu is slechts een voorbeeld-URL, het is geen echte site. Gebruik de URL van de site van jouw school of organisatie. ", - "core.login.errorupdatesite": "Er is een fout opgetreden bij het updaten van het token van deze site.", - "core.login.faqcannotconnectanswer": "Neem contact op met je site-beheerder.", - "core.login.faqcannotconnectquestion": "Ik heb mijn site-adres correct getypt, maar ik kan nog steeds geen verbinding maken.", - "core.login.faqcannotfindmysiteanswer": "Heb je de naam juist ingegeven? Het is ook mogelijk dat je site is niet opgenomen in onze lijst met publieke sites. Als je de naam niet kan vinden, geef dan je site adres in.", - "core.login.faqcannotfindmysitequestion": "Ik kan mijn site niet vinden.", - "core.login.faqsetupsiteanswer": "Bezoek {{$link}} om de verschillende opties te bekijken die je hebt om je eigen Moodle-site te maken.", - "core.login.faqsetupsitelinktitle": "Begin.", - "core.login.faqsetupsitequestion": "Ik wil mijn eigen Moodle-site opzetten.", - "core.login.faqtestappanswer": "Om de app te testen op een Moodle-demosite, typ je \"leraar\" of \"leerling\" in het veld \"Jouw site-adres\" en klik je op de \"Verbinden met je site\" knop.", - "core.login.faqtestappquestion": "Ik wil gewoon de app testen, wat kan ik doen?", - "core.login.faqwhatisurlanswer": "

                  Elke organisatie of school heeft een eigen aangepast adres voor hun Moodle-site.

                  Om het adres te vinden:

                    < li> Open een webbrowser en ga naar de inlogpagina van jouw Moodle-site
                  1. Bovenaan de pagina, in de adresbalk, zie je de URL van je Moodle-site. Bijv. \"campus.example.edu\". {{$image}}
                  2. Kopieer het adres (kopieer niet de / login en wat daarna komt), plak het in de Moodle-app en klik op \"Verbinden met je site\"
                  3. Je kunt nu inloggen op je site met je gebruikersnaam en wachtwoord
                  4. ", - "core.login.faqwhatisurlquestion": "Wat is mijn site-adres? Hoe kan mijn site-URL vinden?", - "core.login.faqwhereisqrcode": "Waar vind ik de QR-code?", - "core.login.faqwhereisqrcodeanswer": "

                    Als je organistatie dit ingeschakeld heeft, dan zul je een QR-code vinden onderaan je gebruikersprofielpagina.

                    {{$image}}", - "core.login.findyoursite": "Zoek je site", - "core.login.firsttime": "Is dit de eerste keer dat je hier bent?", - "core.login.forcepasswordchangenotice": "Je moet je wachtwoord wijzigen om verder te kunnen gaan", - "core.login.forgotten": "Ben je je gebruikersnaam of wachtwoord vergeten?", - "core.login.help": "Help", - "core.login.helpmelogin": "

                    Deze app kan alleen verbinding maken met Moodle sites die mobiele toegang ingeschakeld hebben.

                    Als je niet kunt verbinden met je Moodle-site, neem dan contact op met je site-beheerder en vraag hem om http://docs.moodle.org/en/Mobile_app te lezen.

                    Om de app in een Moodle demo site te testen, kun je teacher of student in het Site-adres-veld typen en op de verbind-knop klikken.

                    e", - "core.login.instructions": "Instructies", - "core.login.invalidaccount": "Controleer je logingegevens of vraag je site-beheerder om de site-configuratie te controleren.", - "core.login.invaliddate": "Ongeldige datum", - "core.login.invalidemail": "Ongeldig e-mailadres", - "core.login.invalidmoodleversion": "

                    Ongeldige Moodle-versie. De Moodle app ondersteunt enkel Moodle sites {{$a}} en hoger.

                    \n

                    Je kunt contact opnemen met je site-beheerders en hen vragen om hun Moodle site te upgraden.

                    \n

                    \"Site-beheerders\" zijn de mensen die Moodle beheren voor jouw school/universiteit/bedrijf of organisatie. Als je niet weet hoe ze te bereiken, contacteer dan je leraren/trainers.

                    ", - "core.login.invalidsite": "Deze site-URL is niet geldig.", - "core.login.invalidtime": "Ongeldige tijd", - "core.login.invalidurl": "Ongeldige URL opgegeven", - "core.login.invalidvaluemax": "De maximum waarde is {{$a}}", - "core.login.invalidvaluemin": "De minimum waard eis {{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle Mobile Additional Features Check gaf een onverwacht antwoord. Je zult aanmelden via de standaard Mobile service.", - "core.login.loggedoutssodescription": "Je moet opnieuw aanmelden. Je moet aanmelden met een browservenster.", - "core.login.login": "Login", - "core.login.loginbutton": "Inloggen", - "core.login.logininsiterequired": "Je dient via een browser in te loggen op je Moodle site", - "core.login.loginsteps": "Om volledige toegang tot deze site te krijgen, moet je een account maken.", - "core.login.missingemail": "e-mailadres ontbreekt", - "core.login.missingfirstname": "Voornaam ontbreekt", - "core.login.missinglastname": "Achternaam ontbreekt", - "core.login.mobileservicesnotenabled": "Mobiele diensten zijn niet geactiveerd voor jouw Moodle site. Neem contact op met de sitebeheerder als je denkt dat die zouden moeten ingeschakeld zijn.", - "core.login.mustconfirm": "Je moet je account bevestigen", - "core.login.newaccount": "Nieuwe account", - "core.login.notloggedin": "Je moet ingelogd zijn.", - "core.login.onboardingcreatemanagecourses": "Maak en beheer je cursussen", - "core.login.onboardingenrolmanagestudents": "Leerlingen aanmelden en beheren", - "core.login.onboardinggetstarted": "Beginnen met Moodle", - "core.login.onboardingialreadyhaveasite": "Ik heb al een Moodle site", - "core.login.onboardingimalearner": "Ik ben een leerling", - "core.login.onboardingimaneducator": "Ik ben een leraar", - "core.login.onboardingineedasite": "Ik heb een Moodle site nodig", - "core.login.onboardingprovidefeedback": "Geef me tijdig feefdback", - "core.login.onboardingtoconnect": "Om met de Moodle App te kunnen verbinden, heb je een Moodle site nodig", - "core.login.onboardingwelcome": "Welkom bij de Moodle App", - "core.login.or": "OF", - "core.login.password": "Wachtwoord", - "core.login.passwordforgotten": "Vergeten wachtwoord", - "core.login.passwordforgotteninstructions2": "Stuur je gebruikersnaam of je e-mailadres in om je wachtwoord te resetten. Als we je terugvinden in de databank sturen we je een mail met instructies, zodat je je opnieuw kunt aanmelden.", - "core.login.passwordrequired": "Wachtwoord vereist", - "core.login.policyaccept": "Ik begrijp het en ga akkoord", - "core.login.policyagree": "Je moet akkoord gaan met deze overeenkomst voor je verder kunt gaan met het gebruiken van deze site. Ga je akkoord?", - "core.login.policyagreement": "Overeenkomst voor het gebruik van deze site", - "core.login.policyagreementclick": "Link naar de gebruiksovereenkomst voor deze site", - "core.login.potentialidps": "Login met je account op:", - "core.login.profileinvaliddata": "Ongeldige waarde", - "core.login.recaptchachallengeimage": "reCAPTCHA challenge afbeelding", - "core.login.recaptchaexpired": "Verificatie verlopen. Beantwoord de beveiligingsvraag opnieuw.", - "core.login.recaptchaincorrect": "Het antwoord op de beveiligingsvraag is fout.", - "core.login.reconnect": "Opnieuw verbinden", - "core.login.reconnectdescription": "Je authenticatietoken is niet geldig of is verlopen. Je moet opnieuw verbinden met de site.", - "core.login.reconnectssodescription": "Je authenticatietoken is niet geldig of is verlopen. Je moet opnieuw verbinden met de site. Je moet je op de site aanmelden via een browservenster.", - "core.login.resendemail": "E-mail opnieuw versturen", - "core.login.searchby": "Zoeken op:", - "core.login.security_question": "Beveiligingsvraag", - "core.login.selectacountry": "Kies een land", - "core.login.selectsite": "Selecteer je site:", - "core.login.signupplugindisabled": "{{$a}} is niet geactiveerd", - "core.login.siteaddress": "Jouw site", - "core.login.sitehasredirect": "Je site bevat minstens één HTTP redirect. De app kan geen redirects volgen. Dit kan het probleem zijn dat je app verhindert om je site te connecteren.", - "core.login.siteinmaintenance": "Je Moodle site staat in onderhoudsmodus", - "core.login.sitepolicynotagreederror": "Site-beleid niet goedgekeurd.", - "core.login.siteurl": "Site URL", - "core.login.siteurlrequired": "De site URL is vereist, bijvoorbeeld http://www.jouwmoodlesite.org", - "core.login.startsignup": "Nieuw account maken", - "core.login.stillcantconnect": "Kun je nog steeds niet verbinden?", - "core.login.supplyinfo": "Meer details", - "core.login.username": "Gebruikersnaam", - "core.login.usernameoremail": "Geef gebruikersnaam of e-mailadres", - "core.login.usernamerequired": "Gebruikersnaam vereist", - "core.login.usernotaddederror": "Gebruiker niet toegevoegd - fout", - "core.login.visitchangepassword": "Wil je de site bezoeken om je wachtwoord te wijzigen?", - "core.login.webservicesnotenabled": "Web services zijn niet geactiveerd op jouw Moodle site. Neem contact op met de sitebeheerder voor hulp.", - "core.login.youcanstillconnectwithcredentials": "Je kunt nog steeds verbinden met de site door je gebruikersnaam en wachtwoord in te geven.", - "core.login.yourenteredsite": "Verbinden met je site", - "core.lostconnection": "Je token is ongeldig of verlopen. Je zult opnieuw moeten verbinden met de site.", - "core.mainmenu.changesite": "Naar andere site", - "core.mainmenu.help": "Help", - "core.mainmenu.logout": "Log uit", - "core.mainmenu.website": "Website", - "core.maxsizeandattachments": "Maximale bestandsgrootte: {{$a.size}}, maximum aantal bestanden: {{$a.attachments}}", - "core.min": "minuut", - "core.mins": "minuten", - "core.misc": "Andere", - "core.mod_assign": "Opdracht", - "core.mod_assignment": "Opdracht 2.2 (uitgeschakeld)", - "core.mod_book": "Boek", - "core.mod_chat": "Chat", - "core.mod_choice": "Keuze", - "core.mod_data": "Databank", - "core.mod_database": "Databank", - "core.mod_external-tool": "Externe tool", - "core.mod_feedback": "Feedbackformulier", - "core.mod_file": "Bestand", - "core.mod_folder": "Map", - "core.mod_forum": "Forum", - "core.mod_glossary": "Woordenlijst", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS inhoudspakket", - "core.mod_imscp": "IMS inhoudspakket", - "core.mod_label": "Label", - "core.mod_lesson": "Les", - "core.mod_lti": "Externe tool", - "core.mod_page": "Pagina", - "core.mod_quiz": "Test", - "core.mod_resource": "Bestand", - "core.mod_scorm": "SCORM-pakket", - "core.mod_survey": "Onderzoek", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Workshop", - "core.moduleintro": "Beschrijving", - "core.more": "meer", - "core.mygroups": "Mijn groepen", - "core.name": "Naam", - "core.needhelp": "Hulp nodig?", - "core.networkerroriframemsg": "Deze inhoud is niet beschikbaar offline. Maak verbinding met het internet en probeer opnieuw.", - "core.networkerrormsg": "Er was een probleem met het verbinden met de site. Controleer je verbinding en probeer opnieuw.", - "core.never": "Nooit", - "core.next": "Volgende", - "core.no": "Nee", - "core.nocomments": "Geen commentaren", - "core.nograde": "Nog geen cijfer", - "core.none": "Geen", - "core.nooptionavailable": "Geen optie beschikbaar", - "core.nopasswordchangeforced": "Je kunt niet verdergaan zonder je wachtwoord te veranderen.", - "core.nopermissionerror": "Sorry, maar je hebt nu niet het recht om dit te doen.", - "core.nopermissions": "Sorry, maar je hebt nu niet het recht om dat te doen ({{$a}}).", - "core.noresults": "Geen resultaten", - "core.noselection": "Geen selectie", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Dit profiel kan niet bekeken worden omdat de gebruiker niet aangemeld is in deze cursus.", - "core.notice": "Opmerking", - "core.notingroup": "Om deze pagina te kunnen zien moet je lid zijn van een groep.", - "core.notsent": "Niet verstuurd", - "core.now": "Nu", - "core.nummore": "{{$a}} meer", - "core.numwords": "{{$a}} woorden", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Klik hier om de afbeelding op volledige grootte weer te geven.", - "core.openinbrowser": "Open in browser", - "core.openmodinbrowser": "Open {{$a}} in browser", - "core.othergroups": "Andere groepen", - "core.pagea": "Pagina {{$a}}", - "core.paymentinstant": "Gebruik de knop om te betalen en je zult binnen enkele minuten aangemeld zijn!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefoon", - "core.pictureof": "Foto van {{$a}}", - "core.previous": "Vorige", - "core.proceed": "Doorgaan", - "core.pulltorefresh": "Slepen om te verversen", - "core.qrscanner": "QR-scanner", - "core.question.answer": "Antwoord", - "core.question.answersaved": "Antwoord bewaard", - "core.question.cannotdeterminestatus": "Kan status niet bepalen", - "core.question.certainty": "Zekerheid", - "core.question.complete": "Volledig", - "core.question.correct": "Juist", - "core.question.errorattachmentsnotsupported": "De applicatie ondersteunt nog geen blijlagen bij antwoorden.", - "core.question.errorinlinefilesnotsupported": "Deze applicatie ondersteunt het inline bewerken van bestanden nog niet.", - "core.question.errorquestionnotsupported": "Dit vraagtype wordt nog niet ondersteund door de app: {{$a}}.", - "core.question.feedback": "Feedback", - "core.question.howtodraganddrop": "Tik om te selecteren en tik om neer te zetten.", - "core.question.incorrect": "Fout", - "core.question.information": "Informatie", - "core.question.invalidanswer": "Onvolledig antwoord", - "core.question.notanswered": "Niet beantwoord", - "core.question.notyetanswered": "Nog niet beantwoord", - "core.question.partiallycorrect": "Gedeeltelijk juist", - "core.question.questionmessage": "Vraag {{$a}}: {{$b}}", - "core.question.questionno": "Vraag {{$a}}", - "core.question.requiresgrading": "Beoordelen vereist", - "core.quotausage": "Je hebt {{$a.used}} gebruikt van je totale limiet van {{$a.total}}.", - "core.rating.aggregateavg": "Gemiddelde van de beoordelingen", - "core.rating.aggregatecount": "Aantal beoordelingen", - "core.rating.aggregatemax": "Maximum beoordeling", - "core.rating.aggregatemin": "Minimumbeoordeling", - "core.rating.aggregatesum": "Som van beoordelingen", - "core.rating.noratings": "Geen beoordelingen ingestuurd", - "core.rating.rating": "Beoordeling", - "core.rating.ratings": "Beoordelingen", - "core.redirectingtosite": "Je wordt doorgestuurd naar de site.", - "core.refresh": "Vernieuw", - "core.remove": "Verwijder", - "core.removefiles": "Verwijder bestanden {{$a}}", - "core.required": "Verplicht", - "core.requireduserdatamissing": "Er ontbreken vereiste gegevens in het profiel van deze gebruiker. Vul deze gegevens in op je Moodle-site en probeer opnieuw.
                    {{$a}}", - "core.resourcedisplayopen": "Open ", - "core.resources": "Bronnen", - "core.restore": "Terugzetten", - "core.restricted": "Beperkt", - "core.retry": "Probeer opnieuw", - "core.save": "Bewaar", - "core.savechanges": "Bewaar de wijzigingen", - "core.scanqr": "Scan QR-code", - "core.search": "Zoek", - "core.searching": "Zoeken", - "core.searchresults": "Zoekresultaten", - "core.sec": "seconde", - "core.secs": "seconden", - "core.seemoredetail": "Klik hier om meer details te zien", - "core.selectacategory": "Selecteer een categorie", - "core.selectacourse": "Selecteer een cursus", - "core.selectagroup": "Selecteer een groep", - "core.send": "Verstuur", - "core.sending": "Versturen", - "core.serverconnection": "Fout bij het verbinden met de server", - "core.settings.about": "Over", - "core.settings.appsettings": "App instellingen", - "core.settings.appversion": "App versie", - "core.settings.cannotsyncoffline": "Kan offline niet synchroniseren.", - "core.settings.cannotsyncwithoutwifi": "Kan nu niet synchroniseren omdat er volgens je huidige instellingen alleen over Wi-Fi gesynchroniseerd mag worden. Maak verbinding met een Wi-Fi-netwerk.", - "core.settings.colorscheme": "Kleurschema", - "core.settings.colorscheme-auto": "Automatisch (gebaseerd op systeeminstellingen)", - "core.settings.colorscheme-dark": "Donker", - "core.settings.colorscheme-light": "Licht", - "core.settings.compilationinfo": "Compilatie info", - "core.settings.copyinfo": "Kopieer apparaatinformatie op het klembord", - "core.settings.cordovadevicemodel": "Cordova device model", - "core.settings.cordovadeviceosversion": "Cordova device OS versie", - "core.settings.cordovadeviceplatform": "Cordova device platform", - "core.settings.cordovadeviceuuid": "Cordova device UUID", - "core.settings.cordovaversion": "Cordova versie", - "core.settings.currentlanguage": "Huidige taal", - "core.settings.debugdisplay": "Toon meldingen voor foutopsporing", - "core.settings.debugdisplaydescription": "Indien ingeschakeld zal er indien mogelijk meer informatie over de fout getoond worden.", - "core.settings.deletesitefiles": "Weet je zeker dat je de gedownloade en gecachede gegevens van de site {{sitename}} wil verwijderen? Je zult de app niet meer in offline modus kunnen gebruiken.", - "core.settings.deletesitefilestitle": "Verwijder site-bestanden", - "core.settings.deviceinfo": "Toestel info", - "core.settings.deviceos": "Toestel OS", - "core.settings.disableall": "Meldingen uitschakelen", - "core.settings.disabled": "Uitgeschakeld", - "core.settings.displayformat": "Schermformaat", - "core.settings.enabledownloadsection": "Downloadsecties inschakelen", - "core.settings.enablefirebaseanalytics": "Schakel Firebase-analyse in", - "core.settings.enablefirebaseanalyticsdescription": "Indien ingeschakeld, verzamelt de app anoniem gegevensgebruik.", - "core.settings.enablerichtexteditor": "Editor voor opgemaakte tekst inschakelen", - "core.settings.enablerichtexteditordescription": "Indien ingeschakeld, zal er een tekst editor voor opgemaakte tekst getoond worden op plaatsen waar het kan.", - "core.settings.enablesyncwifi": "Synchronisatie alleen over WI-FI toestaan.", - "core.settings.entriesincache": "{{$a}} items in cache", - "core.settings.errordeletesitefiles": "Fout bij het verwijderen van bestanden", - "core.settings.errorsyncsite": "Fout bij het synchroniseren van sitegegevens. Controleer je internetverbinding en probeer opnieuw.", - "core.settings.estimatedfreespace": "Geschatte vrije ruimte", - "core.settings.filesystemroot": "Root bestandssysteem", - "core.settings.fontsize": "Lettergrootte", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "Deze instelling is afgedwongen door uw siteconfiguratie.", - "core.settings.general": "Algemeen", - "core.settings.language": "Taal", - "core.settings.license": "Licentie", - "core.settings.localnotifavailable": "Lokale meldingen beschikbaar", - "core.settings.locationhref": "Webview URL", - "core.settings.locked": "Geblokkeerd", - "core.settings.loggedin": "Online", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Navigatietaal", - "core.settings.navigatoruseragent": "Navigatie userAgent", - "core.settings.networkstatus": "Internet connectiestatus", - "core.settings.opensourcelicenses": "Open source-licentie", - "core.settings.preferences": "Voorkeuren", - "core.settings.privacypolicy": "Privacy-beleid", - "core.settings.publisher": "Uitgever", - "core.settings.pushid": "Push-notificaties ID", - "core.settings.reportinbackground": "Fouten automatisch rapporteren", - "core.settings.screen": "Scherminformatie", - "core.settings.settings": "Instellingen", - "core.settings.showdownloadoptions": "Toon download-opties", - "core.settings.siteinfo": "Site info", - "core.settings.sites": "Sites", - "core.settings.spaceusage": "Gebruikte ruimte", - "core.settings.spaceusagehelp": "Als u de opgeslagen informatie van de site verwijdert, worden alle offline gegevens van de site verwijderd. Met deze informatie kunt u de app offline gebruiken.", - "core.settings.synchronization": "Synchronisatie", - "core.settings.synchronizenow": "Nu synchroniseren", - "core.settings.synchronizenowhelp": "Het synchroniseren van een site verzendt in afwachting van wijzigingen en alle offline activiteiten die op het apparaat zijn opgeslagen en synchroniseert bepaalde gegevens zoals berichten en meldingen.", - "core.settings.syncsettings": "Synchronisatie-instellingen", - "core.settings.total": "Totaal", - "core.settings.wificonnection": "Wifi-verbinding", - "core.sharedfiles.chooseaccountstorefile": "Kies een account om het bestand in te bewaren.", - "core.sharedfiles.chooseactionrepeatedfile": "Er is al een bestand met die naam. Wil je het bestaande bestand vervangen of wil je het hernoemen naar \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Er zijn geen sites bewaard. Voeg een site toe voor je een bestand deelt via de app.", - "core.sharedfiles.nosharedfiles": "Er zijn geen gedeelde bestanden bewaard op deze site.", - "core.sharedfiles.nosharedfilestoupload": "Je hebt geen bestanden om hier te uploaden. Als je een bestand wil uploaden vanuit een andere app, zoek dan dat bestand en klik op de 'Open in'-knop.", - "core.sharedfiles.rename": "Hernoem", - "core.sharedfiles.replace": "Vervang", - "core.sharedfiles.sharedfiles": "Gedeelde bestanden", - "core.sharedfiles.successstorefile": "Bestand bewaard. Je kunt dit bestand nu selecteren om het te uploaden naar je privé-bestanden of het als bijlage gebruiken in activiteiten.", - "core.show": "Toon", - "core.showless": "Toon minder...", - "core.showmore": "Toon meer...", - "core.site": "Site", - "core.sitehome.sitehome": "Site startpagina", - "core.sitehome.sitenews": "Site-mededelingen", - "core.sitemaintenance": "De site is in onderhoud en is op dit ogenblik niet beschikbaar", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Sla over", - "core.sorry": "Sorry...", - "core.sort": "Sorteer", - "core.sortby": "Sorteer volgens", - "core.start": "Start", - "core.storingfiles": "Bestanden bewaren", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M %p", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M %p", - "core.strftimedayshort": "%A %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M %p", - "core.strftimetime": "%H:%M %p", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Insturen", - "core.success": "Succes", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Standaard collectie", - "core.tag.errorareanotsupported": "Dit taggebied wordt niet ondersteund door de app.", - "core.tag.inalltagcoll": "Overal", - "core.tag.itemstaggedwith": "{{$a.tagarea}} getagged met \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Geen resultaten voor \"{{$a}}\"", - "core.tag.notagsfound": "Geen tags die overeenkomen met \"{{$a}}\" gevonden", - "core.tag.searchtags": "Zoek tags", - "core.tag.showingfirsttags": "Toon {{$a}} populairste tags", - "core.tag.tag": "Tag", - "core.tag.tagarea_course": "Cursussen", - "core.tag.tagarea_course_modules": "Activiteiten en bronnen", - "core.tag.tagarea_post": "Blogberichten", - "core.tag.tagarea_user": "Gebruikersinteresses", - "core.tag.tags": "Tags", - "core.tag.warningareasnotsupported": "Sommige taggebieden worden niet weergegeven omdat ze niet worden ondersteund door de app.", - "core.teachers": "leraren", - "core.thereisdatatosync": "Er zijn offline {{$a}} die moeten worden gesynchroniseerd.", - "core.thisdirection": "ltr", - "core.time": "Tijd", - "core.timesup": "Tijd is op!", - "core.today": "Vandaag", - "core.tryagain": "Probeer opnieuw", - "core.twoparagraphs": "{{p1}}

                    {{p2}}", - "core.uhoh": "Ojee!", - "core.unexpectederror": "Onverwachte fout. Sluit en heropen de applicatie om opnieuw te proberen.", - "core.unicodenotsupported": "Sommige emojis worden niet ondersteund op deze site. Deze tekens zullen verwijderd worden wanneer het bericht verstuurd wordt.", - "core.unicodenotsupportedcleanerror": "Er is lege tekst gevonden tijdens het opschonen van Unicode-tekens.", - "core.unknown": "Onbekend", - "core.unlimited": "Onbeperkt", - "core.unzipping": "Unzippen", - "core.updaterequired": "App update vereist", - "core.updaterequireddesc": "Update je app naar versie {{$a}}", - "core.upgraderunning": "Site wordt geüpgraded. Probeer later nog eens.", - "core.user": "Gebruiker", - "core.user.address": "Adres", - "core.user.city": "Plaats", - "core.user.contact": "Contact", - "core.user.country": "Land", - "core.user.description": "Beschrijving", - "core.user.details": "Details", - "core.user.detailsnotavailable": "Je kunt de details voor deze gebruiker niet bekijken.", - "core.user.editingteacher": "Leraar", - "core.user.email": "E-mailadres", - "core.user.emailagain": "E-mail (nogmaals)", - "core.user.errorloaduser": "Fout bij het laden van de gebruiker.", - "core.user.firstname": "Voornaam", - "core.user.interests": "Interesses", - "core.user.lastname": "Achternaam", - "core.user.manager": "Beheerder", - "core.user.newpicture": "Nieuwe foto", - "core.user.noparticipants": "Geen gebruikers gevonden in deze cursus", - "core.user.participants": "Deelnemers", - "core.user.phone1": "Telefoon", - "core.user.phone2": "Mobiele telefoon", - "core.user.roles": "Rollen", - "core.user.sendemail": "E-mail", - "core.user.student": "Leerling", - "core.user.teacher": "Leraar zonder bewerken", - "core.user.webpage": "Website", - "core.userdeleted": "De account van deze gebruiker is verwijderd", - "core.userdetails": "Gebruikersdetails", - "core.usernotfullysetup": "Gebruiker niet volledig ingesteld", - "core.users": "Gebruikers", - "core.view": "Bekijken", - "core.viewcode": "Bekijk code", - "core.vieweditor": "Bekijk editor", - "core.viewembeddedcontent": "Bekijk ingebedde inhoud", - "core.viewprofile": "Bekijk profiel", - "core.warningofflinedatadeleted": "Offline data van {{component}} '{{name}}' is verwijderd. {{error}}", - "core.whatisyourage": "Wat is je leeftijd?", - "core.wheredoyoulive": "In welk land woon jij?", - "core.whoissiteadmin": "\"Site-beheerders\" zijn de mensen die de Moodle beheren op jouw school / universiteit / bedrijf of lerende organisatie. Als je niet weet hoe je contact met hen kunt opnemen, neem dan contact op met je leraren / docenten / trainers.", - "core.whoops": "Oei!", - "core.whyisthishappening": "Waarom gebeurt dit?", - "core.whyisthisrequired": "Waarom is dit vereist?", - "core.wsfunctionnotavailable": "De webservice is niet beschikbaar", - "core.year": "Jaar", - "core.years": "Jaren", - "core.yes": "Ja", - "core.youreoffline": "Je bent offline", - "core.youreonline": "Je bent terug online" -} \ No newline at end of file diff --git a/src/assets/lang/no.json b/src/assets/lang/no.json deleted file mode 100644 index 99e85650c..000000000 --- a/src/assets/lang/no.json +++ /dev/null @@ -1,1728 +0,0 @@ -{ - "addon.badges.alignment": "Justering", - "addon.badges.badgedetails": "Utmerkelsesdetaljer", - "addon.badges.badges": "Utmerkelser", - "addon.badges.bendorsement": "Anbefaling", - "addon.badges.claimcomment": "Anbefaling", - "addon.badges.claimid": "KravURL", - "addon.badges.contact": "Kontakt", - "addon.badges.dateawarded": "Dato tildelt", - "addon.badges.expired": "Utløpt", - "addon.badges.expirydate": "Utløpsdato", - "addon.badges.imageauthoremail": "Bildeforfatters epost", - "addon.badges.imageauthorname": "Bildeforfatters navn", - "addon.badges.imageauthorurl": "Bildeforfatterens hjemmeside", - "addon.badges.imagecaption": "Bildetekst", - "addon.badges.issuancedetails": "Utløpsdato på utmerkelse", - "addon.badges.issuerdetails": "Utstederdetaljer", - "addon.badges.issueremail": "Epost", - "addon.badges.issuername": "Navn på utsteder", - "addon.badges.issuerurl": "Utsteders URL", - "addon.badges.language": "Språk", - "addon.badges.noalignment": "Utmerkelsen har ingen spesifiserte kompetanser.", - "addon.badges.nobadges": "Det er ingen tilgjengelige utmerkelser.", - "addon.badges.norelated": "Utmerkelsen har ingen relaterte utmerkelser", - "addon.badges.recipientdetails": "Mottakerdetaljer", - "addon.badges.relatedbages": "Relaterte anbefalinger", - "addon.badges.version": "Versjon", - "addon.badges.warnexpired": "(Denne utmerkelsen har gått ut på dato!)", - "addon.block_activitymodules.pluginname": "Aktiviteter", - "addon.block_activityresults.pluginname": "Aktivitetens resultater", - "addon.block_badges.pluginname": "Siste utmerkelser", - "addon.block_blogmenu.pluginname": "Bloggmeny", - "addon.block_blogrecent.pluginname": "Nylige blogg innlegg", - "addon.block_blogtags.pluginname": "Blogg tagger", - "addon.block_calendarmonth.pluginname": "Kalender", - "addon.block_calendarupcoming.pluginname": "Nærtstående hendelser", - "addon.block_comments.pluginname": "Kommentarer", - "addon.block_completionstatus.pluginname": "Progresjon", - "addon.block_glossaryrandom.pluginname": "Tilfeldig valgt element", - "addon.block_learningplans.pluginname": "Opplæringsplaner", - "addon.block_myoverview.all": "Alle (unntatt skjulte)", - "addon.block_myoverview.allincludinghidden": "Alle", - "addon.block_myoverview.favourites": "Favoritt", - "addon.block_myoverview.future": "Fremtidige", - "addon.block_myoverview.hiddencourses": "Fjernet fra visning", - "addon.block_myoverview.inprogress": "Aktive", - "addon.block_myoverview.lastaccessed": "Sist besøkt", - "addon.block_myoverview.morecourses": "Flere kurs", - "addon.block_myoverview.nocourses": "Ingen kurs", - "addon.block_myoverview.past": "Avsluttede", - "addon.block_myoverview.pluginname": "Kursoversikt", - "addon.block_myoverview.shortname": "Kortnavn", - "addon.block_myoverview.title": "Kursnavn", - "addon.block_newsitems.pluginname": "Siste nyheter", - "addon.block_onlineusers.pluginname": "Brukere på nett", - "addon.block_privatefiles.pluginname": "Private filer", - "addon.block_recentactivity.pluginname": "Siste aktivitet", - "addon.block_recentlyaccessedcourses.nocourses": "Ingen nylig besøkte kurs", - "addon.block_recentlyaccessedcourses.pluginname": "Nylig besøkte kurs", - "addon.block_recentlyaccesseditems.noitems": "Ingen nylige elementer", - "addon.block_recentlyaccesseditems.pluginname": "Nylig besøkte elementer", - "addon.block_rssclient.pluginname": "RSS klient", - "addon.block_selfcompletion.pluginname": "Egenregistrering av fullføring", - "addon.block_sitemainmenu.pluginname": "Hovedmeny", - "addon.block_starredcourses.nocourses": "Ingen favoritter", - "addon.block_starredcourses.pluginname": "Favoritter", - "addon.block_tags.pluginname": "Tagger", - "addon.block_timeline.duedate": "Frist", - "addon.block_timeline.next30days": "Neste 30 dager", - "addon.block_timeline.next3months": "Neste 3 måneder", - "addon.block_timeline.next6months": "Neste 6 måneder", - "addon.block_timeline.next7days": "Neste 7 dager", - "addon.block_timeline.nocoursesinprogress": "Ingen aktive kurs", - "addon.block_timeline.noevents": "Ingen forestående aktiviteter", - "addon.block_timeline.overdue": "Forsinket", - "addon.block_timeline.pluginname": "Tidslinje", - "addon.block_timeline.sortbycourses": "Sorter på kurs", - "addon.block_timeline.sortbydates": "Sorter på datoer", - "addon.blog.blog": "Blogg", - "addon.blog.blogentries": "Blogginnlegg", - "addon.blog.linktooriginalentry": "Lenke til det originale innlegget", - "addon.blog.noentriesyet": "Ingen synlige innlegg her", - "addon.blog.publishtonoone": "Meg selv (kladd)", - "addon.blog.publishtosite": "Alle på denne portalen", - "addon.blog.publishtoworld": "Alle på kloden", - "addon.blog.siteblogheading": "Portalens blogg", - "addon.calendar.allday": "Hele dagen", - "addon.calendar.calendar": "Kalender", - "addon.calendar.calendarevents": "Kalenderhendelser", - "addon.calendar.categoryevents": "Kategorihendelser", - "addon.calendar.confirmeventdelete": "Er du sikker på at du ønsker å sletter dette \"{{$a}}\" arrangementet?", - "addon.calendar.confirmeventseriesdelete": "Hendelsen \"{{$a.name}}\" er en del av en serie. Ønsker du kun å slette denne hendelsen eller alle de {{$a.count}} hendelsene i serien?", - "addon.calendar.courseevents": "Kurshendelser", - "addon.calendar.daynext": "Neste dag", - "addon.calendar.dayprev": "Forrige dag", - "addon.calendar.defaultnotificationtime": "Standard varslingstid", - "addon.calendar.deleteallevents": "Slett alle hendelser", - "addon.calendar.deleteevent": "Slett hendelse", - "addon.calendar.deleteoneevent": "Slett denne hendelsen", - "addon.calendar.durationminutes": "Varighet i minutter", - "addon.calendar.durationnone": "Uten varighet", - "addon.calendar.durationuntil": "Til", - "addon.calendar.editevent": "Redigerer hendelse", - "addon.calendar.errorloadevent": "Feil ved lasting av hendelse", - "addon.calendar.errorloadevents": "Feil ved lasting av hendelser", - "addon.calendar.eventcalendareventdeleted": "Kalenderhendelse slettet", - "addon.calendar.eventduration": "Varighet", - "addon.calendar.eventendtime": "Avsluttes", - "addon.calendar.eventkind": "Type hendelse", - "addon.calendar.eventname": "Navn på hendelse", - "addon.calendar.eventstarttime": "Starttid", - "addon.calendar.eventtype": "Hendelsestype", - "addon.calendar.fri": "Fre", - "addon.calendar.friday": "Fredag", - "addon.calendar.gotoactivity": "Gå til aktivitet", - "addon.calendar.groupevents": "Gruppehendelser", - "addon.calendar.invalidtimedurationminutes": "Du skrev inn en ugyldig verdi for varigheten (i minutter). Vennligst bruk en verdi større enn 0 eller velg \"Ingen varighet\".", - "addon.calendar.invalidtimedurationuntil": "Dato og tid du valgte for varighet er før starttiden for hendelsen. Vennligst korriger dette før du fortsetter.", - "addon.calendar.mon": "Man", - "addon.calendar.monday": "Mandag", - "addon.calendar.monthlyview": "Månedsoversikt", - "addon.calendar.newevent": "Ny hendelse", - "addon.calendar.noevents": "Det er ingen hendelser", - "addon.calendar.nopermissiontoupdatecalendar": "Beklager, men du har ikke tillatelse til å oppdatere kalenderhendelser", - "addon.calendar.repeatedevents": "Gjentatte hendelser", - "addon.calendar.repeateditall": "Utfør endringene på de andre {{$a}}hendelsene i denne serien", - "addon.calendar.repeateditthis": "Bruk endringene bare på denne hendelsen", - "addon.calendar.repeatevent": "Gjenta denne hendelsern", - "addon.calendar.repeatweeksl": "Gjenta ukentlig slik at det i alt blir", - "addon.calendar.sat": "Lør", - "addon.calendar.saturday": "Lørdag", - "addon.calendar.siteevents": "Portalhendelser", - "addon.calendar.sun": "Søn", - "addon.calendar.sunday": "Søndag", - "addon.calendar.thu": "Tor", - "addon.calendar.thursday": "Torsdag", - "addon.calendar.today": "I dag", - "addon.calendar.tomorrow": "I morgen", - "addon.calendar.tue": "Tir", - "addon.calendar.tuesday": "Tirsdag", - "addon.calendar.typecategory": "Kategorihendelse", - "addon.calendar.typeclose": "Steng hendelse", - "addon.calendar.typecourse": "Kurshendelser", - "addon.calendar.typedue": "Nåværende hendelse", - "addon.calendar.typegradingdue": "Vurderingshendelse", - "addon.calendar.typegroup": "Gruppehendelser", - "addon.calendar.typeopen": "Åpne hendelse", - "addon.calendar.typesite": "Hendelser på nettstedet", - "addon.calendar.typeuser": "Brukerhendelse", - "addon.calendar.upcomingevents": "Nærtstående hendelser", - "addon.calendar.userevents": "Brukerhendelser", - "addon.calendar.wed": "Ons", - "addon.calendar.wednesday": "Onsdag", - "addon.calendar.when": "Når", - "addon.calendar.yesterday": "I går", - "addon.competency.activities": "Aktiviteter", - "addon.competency.competencies": "Kompetanser", - "addon.competency.competenciesmostoftennotproficientincourse": "Læringsmål som sjeldnest er oppnådd i dette kurset", - "addon.competency.coursecompetencies": "Kursets læringsmål", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Læringsmålvurderinger i dette kurset har ingen innvirkning på opplæringsplaner.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Læringsmålvurderinger i dette kurset vil automatisk oppdatere opplæringsplaner.", - "addon.competency.crossreferencedcompetencies": "Kryssrefererte læringsmål", - "addon.competency.duedate": "Utløpsdato", - "addon.competency.errornocompetenciesfound": "Ingen kompetansemål funnet", - "addon.competency.evidence": "Bevis", - "addon.competency.evidence_competencyrule": "Læringsmålregelen ble møtt", - "addon.competency.evidence_coursecompleted": "Kurset '{{$a}}' ble fullført.", - "addon.competency.evidence_coursemodulecompleted": "Aktiviteten '{{$a}}' ble fullført.", - "addon.competency.evidence_courserestored": "Vurderingen ble gjenopprettet sammen med kurset '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Beviset '{{$a}}' på tidligere læring ble lenket.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Lenken til beviset '{{$a}}' på tidligere læring ble fjernet.", - "addon.competency.evidence_manualoverride": "Kompetansevurderingen ble satt manuelt.", - "addon.competency.evidence_manualoverrideincourse": "Kompetansevurderingen ble satt manuelt i kurset '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "Kompetansevurderingen ble satt manuelt i opplringsplanen '{{$a}}'.", - "addon.competency.learningplancompetencies": "Opplæringsplanens læringsmål", - "addon.competency.learningplans": "Opplæringsplaner", - "addon.competency.myplans": "Mine opplæringsplaner", - "addon.competency.noactivities": "Ingen aktiviteter", - "addon.competency.nocompetencies": "Ingen kompetansemål", - "addon.competency.nocompetenciesincourse": "Ingen læringsmål har blitt lenket til dette kurset", - "addon.competency.nocrossreferencedcompetencies": "Ingen andre læringsmål har en kryssreferanse til dette læringsmålet.", - "addon.competency.noevidence": "Ingen bevis", - "addon.competency.noplanswerecreated": "Ingen opplæringsplaner ble opprettet", - "addon.competency.nouserplanswithcompetency": "Ingen opplæringsplaner inneholder dette læringsmålet.", - "addon.competency.path": "Sti:", - "addon.competency.planstatusactive": "Aktiv", - "addon.competency.planstatuscomplete": "Fullført", - "addon.competency.planstatusdraft": "Utkast", - "addon.competency.planstatusinreview": "Under vurdering", - "addon.competency.planstatuswaitingforreview": "Venter på vurdering", - "addon.competency.proficient": "Dyktighet", - "addon.competency.progress": "Fremdrift", - "addon.competency.rating": "Vurdering", - "addon.competency.reviewstatus": "Vurderingsstatus", - "addon.competency.status": "Status", - "addon.competency.template": "Opplæringsplanmal", - "addon.competency.uponcoursecompletion": "Ved kursfullføring:", - "addon.competency.usercompetencystatus_idle": "Uvirksom", - "addon.competency.usercompetencystatus_inreview": "Under vurdering", - "addon.competency.usercompetencystatus_waitingforreview": "Venterpå vurdering", - "addon.competency.userplans": "Opplæringsplaner", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} av {{$a.y}} læringsmål er oppnådd", - "addon.competency.xcompetenciesproficientoutofyincourse": "Du har oppnådd {{$a.x}} av {{$a.y}} læringsmål i dette kurset.", - "addon.coursecompletion.complete": "Fullfør", - "addon.coursecompletion.completecourse": "Fullfør kurset", - "addon.coursecompletion.completed": "Fullført", - "addon.coursecompletion.completiondate": "Fullføringsdato", - "addon.coursecompletion.completionmenuitem": "Fullføring", - "addon.coursecompletion.couldnotloadreport": "Kunne ikke laste kursets avslutningsrapport. Prøv igjen senere.", - "addon.coursecompletion.coursecompletion": "Kursfullføring", - "addon.coursecompletion.criteria": "Kriterie", - "addon.coursecompletion.criteriagroup": "Kriteriegruppe", - "addon.coursecompletion.criteriarequiredall": "Alle kriteriene under er obligatoriske", - "addon.coursecompletion.criteriarequiredany": "Ethvert kriterium under er obligatorisk", - "addon.coursecompletion.inprogress": "Pågår", - "addon.coursecompletion.manualselfcompletion": "Manuell egenregistrering av fullføring", - "addon.coursecompletion.nottracked": "For øyeblikket blir ikke din progresjon registrert av fullførtsporingen i dette kurset", - "addon.coursecompletion.notyetstarted": "Ikke startet ennå", - "addon.coursecompletion.pending": "Behandles", - "addon.coursecompletion.required": "Obligatorisk", - "addon.coursecompletion.requiredcriteria": "Påkrevde kriterier", - "addon.coursecompletion.requirement": "Forutsetninger", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Vis kursrapport", - "addon.files.couldnotloadfiles": "Liste med filer kunne ikke vises", - "addon.files.emptyfilelist": "Det er ingen filer å vise", - "addon.files.erroruploadnotworking": "Det er for øyeblikket ikke mulig å laste opp filer til din Moodle-side", - "addon.files.files": "Filer", - "addon.files.privatefiles": "Private filer", - "addon.files.sitefiles": "Nettstedfiler", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Konfigurer enheter", - "addon.messages.acceptandaddcontact": "Godta og legg til i kontakter", - "addon.messages.addcontact": "Legg til kontakt", - "addon.messages.addcontactconfirm": "Er du sikker på at du ønsker å legge til {{$a}} i dine kontakter?", - "addon.messages.addtofavourites": "Merk som favoritt", - "addon.messages.addtoyourcontacts": "Legg til dine kontakter", - "addon.messages.blocknoncontacts": "Blokker alle beskjeder fra folk som ikke er på kontaklisten min", - "addon.messages.blockuser": "Blokker bruker", - "addon.messages.blockuserconfirm": "Er du sikker på at du ønsker å blokkere {{$a}}?", - "addon.messages.contactableprivacy": "Aksepter meldinger fra:", - "addon.messages.contactableprivacy_coursemember": "Mine kontakter og deltakere i mine kurs", - "addon.messages.contactableprivacy_onlycontacts": "Kun kontakter", - "addon.messages.contactableprivacy_site": "Alle registrert brukere", - "addon.messages.contactblocked": "Kontakt blokkert", - "addon.messages.contactlistempty": "Kontaktlisten er tom", - "addon.messages.contactname": "Navn på kontakt", - "addon.messages.contactrequestsent": "Kontaktforespørsel sendt", - "addon.messages.contacts": "Kontakter", - "addon.messages.conversationactions": "Handlingsmeny", - "addon.messages.decline": "Avslå", - "addon.messages.deleteallconfirm": "Er du sikker på at du ønsker å slette hele denne samtalen? Den vil ikke slettes for andre deltakere i samtalen.", - "addon.messages.deleteallselfconfirm": "Er du sikker på at du ønsker å slette hele denne private samtalen?", - "addon.messages.deleteconversation": "Slett samtale", - "addon.messages.deleteforeveryone": "Slett for meg og alle andre", - "addon.messages.errordeletemessage": "Feil ved sletting av melding", - "addon.messages.errorwhileretrievingcontacts": "Feil ved henting av kontakter fra server", - "addon.messages.errorwhileretrievingdiscussions": "Feil ved henting av diskusjoner fra server", - "addon.messages.errorwhileretrievingmessages": "Feil ved henting av meldinger fra server", - "addon.messages.groupconversations": "Gruppe", - "addon.messages.groupinfo": "Gruppeinfo", - "addon.messages.individualconversations": "Privat", - "addon.messages.info": "Brukerinfo", - "addon.messages.isnotinyourcontacts": "{{$a}} er ikke i dine kontakter", - "addon.messages.message": "Melding", - "addon.messages.messagenotsent": "Meldingen ble ikke sendt. Prøv igjen senere", - "addon.messages.messagepreferences": "Meldingspreferanser", - "addon.messages.messages": "Meldinger", - "addon.messages.muteconversation": "Demp", - "addon.messages.mutedconversation": "Stille samtale", - "addon.messages.newmessage": "Ny melding", - "addon.messages.newmessages": "Nye meldinger", - "addon.messages.nocontactrequests": "Ingen kontaktforespørsler", - "addon.messages.nocontactsgetstarted": "Ingen kontakter", - "addon.messages.nofavourites": "Ingen merkede samtaler", - "addon.messages.nogroupconversations": "Ingen gruppesamtaler", - "addon.messages.noindividualconversations": "Ingen private samtaler", - "addon.messages.nomessagesfound": "Ingen meldinger ble funnet", - "addon.messages.noncontacts": "Ikke-kontakter", - "addon.messages.nousersfound": "Ingen brukere funnet", - "addon.messages.numparticipants": "{{$a}} deltakere", - "addon.messages.removecontact": "Fjern kontakt", - "addon.messages.removecontactconfirm": "Er du sikker på at du ønsker å fjerne {{$a}} fra dine kontakter?", - "addon.messages.removefromfavourites": "Fjern fra favoritter", - "addon.messages.removefromyourcontacts": "Fjern fra dine kontakter", - "addon.messages.requests": "Forespørsler", - "addon.messages.requirecontacttomessage": "Du må sende en kontaktforespørsel til {{$a}} for å kunne sende de en melding.", - "addon.messages.searchcombined": "Søk etter mennesker og meldinger", - "addon.messages.selfconversation": "Personlig område", - "addon.messages.selfconversationdefaultmessage": "Lagre utkast, lenke, notater osv. her.", - "addon.messages.sendcontactrequest": "Send kontaktforespørsel", - "addon.messages.type_blocked": "Blokkert", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Søkeresultat", - "addon.messages.type_strangers": "Andre", - "addon.messages.unabletomessage": "Du kan ikke sende meldinger til denne brukeren.", - "addon.messages.unblockuser": "Avblokker bruker", - "addon.messages.unblockuserconfirm": "Er du sikker på at du ønsker å avblokkerer {{$a}}?", - "addon.messages.unmuteconversation": "Lyd på", - "addon.messages.useentertosend": "Bruk enter for å sende", - "addon.messages.userwouldliketocontactyou": "{{$a}} ønsker å kontakte deg", - "addon.messages.warningmessagenotsent": "Kunne ikke sende melding(er) til {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Ønsker å kontakte deg", - "addon.messages.you": "Du:", - "addon.messages.youhaveblockeduser": "Du har blokkert denne brukeren", - "addon.messages.yourcontactrequestpending": "Ventende kontaktforespørsel med {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Bekreft betingelsene", - "addon.mod_assign.addattempt": "Tillat et nytt forsøk", - "addon.mod_assign.addnewattempt": "Legg til et nytt forsøk", - "addon.mod_assign.addnewattemptfromprevious": "Legg til et nytt forsøk basert på forrige innlevering", - "addon.mod_assign.addsubmission": "Legg til innlevering", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Informasjon om innleveringen og innsendingsskjemaet finner du her: {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Tillat innsending fra", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Denne innleveringsoppgaven godtar innleveringer som er sendt inn fra {{$a}}", - "addon.mod_assign.applytoteam": "Gjør karakterer og tilbakemeldinger gjeldende for helle gruppen", - "addon.mod_assign.assignmentisdue": "Frist for innlevering", - "addon.mod_assign.attemptnumber": "Forsøk nummer", - "addon.mod_assign.attemptreopenmethod": "Forsøk gjenåpnet", - "addon.mod_assign.attemptreopenmethod_manual": "Manuelt", - "addon.mod_assign.attemptreopenmethod_untilpass": "Tillat nytt forsøk helt til innleveringen er bestått", - "addon.mod_assign.attemptsettings": "Forsøksinnstillinger", - "addon.mod_assign.cannoteditduetostatementsubmission": "Du kan ikke redigere et bidrag fordi vi ikke kunne hente betingelsene fra siden", - "addon.mod_assign.cannotgradefromapp": "Noen karaktermetoder er ikke støttet av appen og kan ikke endres", - "addon.mod_assign.confirmsubmission": "Er du sikker på at du vil levere inn arbeidet ditt til vurdering? Da kan du ikke gjøre flere endringer.", - "addon.mod_assign.currentattempt": "Dette er forsøk {{$a}}.", - "addon.mod_assign.currentattemptof": "Dette er forsøk {{$a.attemptnumber}} ( {{$a.maxattempts}} forsøk tillatt ).", - "addon.mod_assign.currentgrade": "Nåværende karakter i karakterboka", - "addon.mod_assign.cutoffdate": "Siste dato for innlevering", - "addon.mod_assign.defaultteam": "Standardgruppe", - "addon.mod_assign.duedate": "Innleveringsfrist", - "addon.mod_assign.duedateno": "Ingen innleveringsfrist", - "addon.mod_assign.duedatereached": "Fristen for denne innleveringen har passert.", - "addon.mod_assign.editingstatus": "Redigerer status", - "addon.mod_assign.editsubmission": "Rediger innleveringen", - "addon.mod_assign.erroreditpluginsnotsupported": "Du kan ikke legge til eller endre bidrag i appen fordi noen plugins ikke støtter redigering:", - "addon.mod_assign.extensionduedate": "Frist for forlengelse", - "addon.mod_assign.grade": "Karakter", - "addon.mod_assign.graded": "Karaktersatt", - "addon.mod_assign.gradedby": "Vurdert av", - "addon.mod_assign.gradedfollowupsubmit": "Vurdert - oppfølgingsinnlevering mottatt", - "addon.mod_assign.gradedon": "Vurdert den", - "addon.mod_assign.gradelocked": "Denne karakteren er låst eller overstyrt i karakterboken.", - "addon.mod_assign.gradeoutof": "Karakter av {{$a}} mulige.", - "addon.mod_assign.gradingstatus": "Karaktersettingsstatus", - "addon.mod_assign.groupsubmissionsettings": "Innstillinger for gruppeinnleveringer", - "addon.mod_assign.hiddenuser": "Deltaker", - "addon.mod_assign.latesubmissions": "Sene innleveringer", - "addon.mod_assign.latesubmissionsaccepted": "Tillat inntil {{$a}}", - "addon.mod_assign.markingworkflowstate": "Status for arbeidsflyt", - "addon.mod_assign.markingworkflowstateinmarking": "Under retting", - "addon.mod_assign.markingworkflowstateinreview": "Blir vurdert", - "addon.mod_assign.markingworkflowstatenotmarked": "Ikke vurdert", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Klar for publisering", - "addon.mod_assign.markingworkflowstatereadyforreview": "Karaktersetting fullført", - "addon.mod_assign.markingworkflowstatereleased": "Publisert", - "addon.mod_assign.modulenameplural": "Innlevering", - "addon.mod_assign.multipleteams": "Medlem av mer enn en gruppe", - "addon.mod_assign.multipleteams_desc": "Denne innleveringen krever gruppeinnlevering. Du er medlem av mer enn en gruppe. For å kunne levere kan du kun være medlem av en enkelt gruppe. Vennligst kontakt læreren din for å endre gruppemedlemsskapene dine.", - "addon.mod_assign.noattempt": "Ingen forsøk", - "addon.mod_assign.nomoresubmissionsaccepted": "Kun for deltakere som har fått innvilget en utsettelse", - "addon.mod_assign.noonlinesubmissions": "Denne innleveringen krever ikke at du skal levere inn noe på nett.", - "addon.mod_assign.nosubmission": "Ingenting har blitt levert inn til denne innleveringen.", - "addon.mod_assign.noteam": "Ikke medlem av noen grupper.", - "addon.mod_assign.noteam_desc": "Denne innleveringen krever gruppeinnlevering. Du er ikke medlem av noen gruppe, og kan dermed ikke levere inn noe. Vennligst kontakt læreren din for å bli lagt til i en gruppe.", - "addon.mod_assign.notgraded": "Ikke vurdert", - "addon.mod_assign.numberofdraftsubmissions": "Utkast", - "addon.mod_assign.numberofparticipants": "Deltakere", - "addon.mod_assign.numberofsubmissionsneedgrading": "Må vurderes", - "addon.mod_assign.numberofsubmittedassignments": "Levert", - "addon.mod_assign.numberofteams": "Grupper", - "addon.mod_assign.numwords": "{{$a}} ord", - "addon.mod_assign.outof": "{{$a.current}} av {{$a.total}}", - "addon.mod_assign.overdue": "Innleveringen er forsinket med: {{$a}}", - "addon.mod_assign.submission": "Innlevering", - "addon.mod_assign.submissioneditable": "Studenter kan redigere denne innleveringen", - "addon.mod_assign.submissionnoteditable": "Studenter kan ikke redigere denne innleveringen", - "addon.mod_assign.submissionslocked": "Denne innleveringen tar ikke imot innleveringer", - "addon.mod_assign.submissionstatus": "Status på innlevering", - "addon.mod_assign.submissionstatus_": "Ingen innlevering", - "addon.mod_assign.submissionstatus_draft": "Utkast (ikke levert)", - "addon.mod_assign.submissionstatus_marked": "Karaktersatt", - "addon.mod_assign.submissionstatus_new": "Ingen innlevering", - "addon.mod_assign.submissionstatus_reopened": "Gjenåpnet", - "addon.mod_assign.submissionstatus_submitted": "Levert til vurdering", - "addon.mod_assign.submissionstatusheading": "Status på innlevering", - "addon.mod_assign.submissionteam": "Gruppe", - "addon.mod_assign.submitassignment": "Send inn oppgavesvaret", - "addon.mod_assign.submitassignment_help": "Når du har levert denne oppgavebesvarelsen, vil du ikke kunne gjøre flere endringer.", - "addon.mod_assign.submittedearly": "Oppgavebesvarelsen ble levert {{$a}} for tidlig.", - "addon.mod_assign.submittedlate": "Oppgavebesvarelsen ble levert {{$a}} for sent.", - "addon.mod_assign.timemodified": "Sist endret", - "addon.mod_assign.timeremaining": "Tid som gjenstår", - "addon.mod_assign.ungroupedusers": "Innstillingen 'Krev gruppe for å levere' er slått på, men noen brukere er enten ikke medlem av noen grupper eller medlem i flere grupper. Dette vil forhindre dem fra å levere inn noe.", - "addon.mod_assign.ungroupedusersoptional": "Innstillingen 'Studenter kan levere i grupper' er aktivert og noen brukere er enten ikke medlemmer av noen gruppe eller i mer enn en gruppe. Vennligst vær klar over at disse studentene vil foreta innleveringen som medlem av 'Standard gruppe'.", - "addon.mod_assign.unlimitedattempts": "Ubegrenset", - "addon.mod_assign.userswhoneedtosubmit": "Brukere som må levere: {{$a}}", - "addon.mod_assign.viewsubmission": "Vis innlevering", - "addon.mod_assign.wordlimit": "Ordgrense", - "addon.mod_assign_feedback_comments.pluginname": "Tilbakemeldinger/kommentarer", - "addon.mod_assign_feedback_editpdf.pluginname": "Kommenter i PDF-filen.", - "addon.mod_assign_feedback_file.pluginname": "Tilbakemeldinger i fil", - "addon.mod_assign_submission_comments.pluginname": "Kommentarer til innleveringen", - "addon.mod_assign_submission_file.pluginname": "Filinnleveringer", - "addon.mod_assign_submission_onlinetext.pluginname": "Online innlevering", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Ordgrensen for denne innleveringen er {{$a.limit}} ord, og du prøver å levere en besvarelsen med {{$a.count}} ord. Vennligst se over besvarelsen din og prøv på nytt.", - "addon.mod_book.errorchapter": "Feil ved lesing av bokkapitlet", - "addon.mod_book.modulenameplural": "Bøker", - "addon.mod_book.navnexttitle": "Neste: {{$a}}", - "addon.mod_book.navprevtitle": "Forrige: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Bokkapitler", - "addon.mod_book.toc": "Innholdsfortegnelse", - "addon.mod_chat.beep": "tut", - "addon.mod_chat.chatreport": "Praterunder", - "addon.mod_chat.currentusers": "Nåværende brukere", - "addon.mod_chat.enterchat": "Start praten", - "addon.mod_chat.entermessage": "Skriv inn din melding", - "addon.mod_chat.errorwhileretrievingmessages": "Feil ved henting av melding fra server.", - "addon.mod_chat.errorwhilesendingmessage": "Melding kunne ikke sendes.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} tut til alle", - "addon.mod_chat.messagebeepsyou": "{{$a}} har tutet på deg", - "addon.mod_chat.messageenter": "{{$a}} har akkurat kommet", - "addon.mod_chat.messageexit": "{{$a}} har forlatt denne praten", - "addon.mod_chat.messages": "Beskjeder", - "addon.mod_chat.messageyoubeep": "Du pep {{$a}}", - "addon.mod_chat.modulenameplural": "Praterom", - "addon.mod_chat.mustbeonlinetosendmessages": "Du må være på nett for å sende meldinger.", - "addon.mod_chat.nomessages": "Ingen beskjeder ennå", - "addon.mod_chat.saidto": "sa til", - "addon.mod_chat.send": "Send", - "addon.mod_chat.sessionstart": "Prateøkten vil starte: {{$a.date}}, ({{$a.fromnow}} fra nå)", - "addon.mod_chat.talk": "Snakk", - "addon.mod_chat.viewreport": "Vis tidligere praterunder", - "addon.mod_choice.cannotsubmit": "Beklager, det var et problem med å sende valget ditt. Vennligst prøv på nytt.", - "addon.mod_choice.choiceoptions": "Valginnstillinger", - "addon.mod_choice.expired": "Beklager, denne aktiviteten ble stengt {{$a}}.", - "addon.mod_choice.full": "(full)", - "addon.mod_choice.modulenameplural": "Galluper", - "addon.mod_choice.noresultsviewable": "Resultatene er ikke synlige akkurat nå.", - "addon.mod_choice.notopenyet": "Denne aktiviteten er ikke tilgjengelig før {{$a}}", - "addon.mod_choice.numberofuser": "Antall svar", - "addon.mod_choice.numberofuserinpercentage": "Antall svar i prosent", - "addon.mod_choice.previewonly": "Dette er bare en forhåndsvisning av de mulige valgene du kan gjøre i denne gallupen. Du kan ikke besvare den før {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anonyme resulter vil vises etter at du har svart.", - "addon.mod_choice.publishinfoanonclose": "Anonyme resulter vil vises etter at aktiviteten stenges.", - "addon.mod_choice.publishinfofullafter": "Fulle resultater som viser alle svar vil vises etter at du har svart.", - "addon.mod_choice.publishinfofullclose": "Fulle resultater som viser alle svar vil vises etter at aktiviteten stenges.", - "addon.mod_choice.publishinfonever": "Resultatene i denne aktiviteten vil ikke publiseres etter at du har svart.", - "addon.mod_choice.removemychoice": "Fjern valget mitt", - "addon.mod_choice.responses": "Svar", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% av brukerne velger dette alternativet: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Visning av diagram", - "addon.mod_choice.resultsnotsynced": "Resultatene viser ikke din siste respons. Synkroniser for å oppdatere dem.", - "addon.mod_choice.savemychoice": "Lagre mitt svar", - "addon.mod_choice.userchoosethisoption": "Brukere som velger dette alternativet", - "addon.mod_choice.yourselection": "Ditt valg", - "addon.mod_data.addentries": "Legg til", - "addon.mod_data.advancedsearch": "Avansert søk", - "addon.mod_data.alttext": "Alternativ tekst", - "addon.mod_data.approve": "Godkjenn", - "addon.mod_data.approved": "Godkjent", - "addon.mod_data.ascending": "Stigende", - "addon.mod_data.authorfirstname": "Forfatterens fornavn", - "addon.mod_data.authorlastname": "Forfatterens etternavn", - "addon.mod_data.confirmdeleterecord": "Er du sikker på at du vil slette denne oppføringen?", - "addon.mod_data.descending": "Synkende", - "addon.mod_data.disapprove": "Underkjenn", - "addon.mod_data.emptyaddform": "Du har ikke fylt ut noen felter!", - "addon.mod_data.entrieslefttoadd": "Du må legge til {{$a.entriesleft}} flere oppføringer før du kan fullføre denne aktiviteten.", - "addon.mod_data.entrieslefttoaddtoview": "Du må legge til {{$a.entrieslefttoview}} flere poster/innlegg før du kan se andre deltakeres innlegg.", - "addon.mod_data.errormustsupplyvalue": "Du må skrive inn noe her", - "addon.mod_data.expired": "Beklager, denne aktiviteten ble stengt {{$a}} og er ikke lenger tilgjengelig", - "addon.mod_data.fields": "Felter", - "addon.mod_data.foundrecords": "Funnet poster: {{$a.num}}/{{$a.max}} (Nullstill filter)", - "addon.mod_data.latlongboth": "Både lengdegrad og breddegrad må oppgis", - "addon.mod_data.menuchoose": "Velg...", - "addon.mod_data.modulenameplural": "Databaser", - "addon.mod_data.more": "Mer", - "addon.mod_data.nomatch": "Ingen treff blant oppføringene!", - "addon.mod_data.norecords": "Ingen oppføringer i databasen", - "addon.mod_data.notapproved": "Oppføringen er ikke godkjent ennå.", - "addon.mod_data.notopenyet": "Beklager, men dette aktiviteten er ikke tilgjengelig før {{$a}}", - "addon.mod_data.numrecords": "{{$a}} oppføringer", - "addon.mod_data.other": "Andre", - "addon.mod_data.recordapproved": "Oppføringen er godkjent", - "addon.mod_data.recorddeleted": "Oppføringen er slettet", - "addon.mod_data.recorddisapproved": "Innlegg underkjent", - "addon.mod_data.resetsettings": "Nullstill filtre", - "addon.mod_data.search": "Søk", - "addon.mod_data.selectedrequired": "Velg alle er påkrevd", - "addon.mod_data.single": "Enkeltvisning", - "addon.mod_data.tagarea_data_records": "Dataoppføringer", - "addon.mod_data.timeadded": "Tid lagt til", - "addon.mod_data.timemodified": "Tid endret", - "addon.mod_data.usedate": "Inkludér i søket.", - "addon.mod_feedback.analysis": "Analyse", - "addon.mod_feedback.anonymous": "Anonym", - "addon.mod_feedback.anonymous_entries": "Anonyme svar ({{$a}})", - "addon.mod_feedback.average": "Gjennomsnitt", - "addon.mod_feedback.complete_the_form": "Svar på spørsmålene", - "addon.mod_feedback.completed_feedbacks": "Innsendte svar", - "addon.mod_feedback.continue_the_form": "Fortsett med å svare på spørsmålene", - "addon.mod_feedback.feedback_is_not_open": "Undersøkelsen er ikke åpen", - "addon.mod_feedback.feedbackclose": "Steng undersøkelsen", - "addon.mod_feedback.feedbackopen": "Åpne undersøkelsen", - "addon.mod_feedback.mapcourses": "Koble Feedback til kurs", - "addon.mod_feedback.maximal": "Maksimalt", - "addon.mod_feedback.minimal": "Minimum", - "addon.mod_feedback.mode": "Modus", - "addon.mod_feedback.modulenameplural": "Feedback", - "addon.mod_feedback.next_page": "Neste side", - "addon.mod_feedback.non_anonymous": "Brukerens navn vil bli lagret og vist sammen med svaret.", - "addon.mod_feedback.non_anonymous_entries": "Ingen anonyme svar ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Studenter som ikke har svart ({{$a}})", - "addon.mod_feedback.not_selected": "Ikke valgt", - "addon.mod_feedback.not_started": "Ikke startet", - "addon.mod_feedback.numberoutofrange": "Dette tallet er utenfor det gyldige intervallet", - "addon.mod_feedback.overview": "Oversikt", - "addon.mod_feedback.page_after_submit": "Visningsside etter fullført", - "addon.mod_feedback.preview": "Forhåndsvis", - "addon.mod_feedback.previous_page": "Forrige side", - "addon.mod_feedback.questions": "Spørsmål", - "addon.mod_feedback.response_nr": "Respons nr:", - "addon.mod_feedback.responses": "Responser", - "addon.mod_feedback.save_entries": "Send inn dine svar", - "addon.mod_feedback.show_entries": "Vis responser", - "addon.mod_feedback.show_nonrespondents": "Vis brukere som ikke har svart", - "addon.mod_feedback.started": "Startet", - "addon.mod_feedback.this_feedback_is_already_submitted": "Du har allerede fullført denne aktiviteten.", - "addon.mod_folder.emptyfilelist": "Det er ingen filer å vise.", - "addon.mod_folder.modulenameplural": "Mapper", - "addon.mod_forum.addanewdiscussion": "Skriv i dette forumet", - "addon.mod_forum.addanewquestion": "Legg til et nytt spørsmål", - "addon.mod_forum.addanewtopic": "Skriv i dette forumet", - "addon.mod_forum.addtofavourites": "Merk diskusjonen som favoritt", - "addon.mod_forum.advanced": "Avansert", - "addon.mod_forum.cannotadddiscussion": "Du må være medlem av en gruppe for å legge til ny diskusjon i dette forumet.", - "addon.mod_forum.cannotadddiscussionall": "Du har ikke tillatelse til å legge til et nytt diskusjonsemne for alle deltakerne.", - "addon.mod_forum.cannotcreatediscussion": "Kan ikke lage ny diskusjon", - "addon.mod_forum.couldnotadd": "Kunne ikke legge ut innlegget ditt pga. en ukjent feil", - "addon.mod_forum.couldnotupdate": "Kunne ikke oppdatere innlegget pga. en ukjent feil", - "addon.mod_forum.cutoffdatereached": "Det er etter forumets avslutningsdato og du kan ikke lengre skrive innlegg i", - "addon.mod_forum.delete": "Slett", - "addon.mod_forum.deletedpost": "Innlegget er slettet", - "addon.mod_forum.deletesure": "Er du sikker på at du vil slette dette innlegget?", - "addon.mod_forum.discussion": "Diskusjon", - "addon.mod_forum.discussionlistsortbycreatedasc": "Sorter etter opprettelsesdato i stigende rekkefølge", - "addon.mod_forum.discussionlistsortbycreateddesc": "Sorter etter opprettelsesdato i synkende rekkefølge", - "addon.mod_forum.discussionlistsortbylastpostasc": "Sorter etter siste opprettingsdato i stigende rekkefølge", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Sorter etter siste opprettingsdato i synkende rekkefølge", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Sorter diskusjonslisten stigende etter antall innlegg", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Sorter diskusjonslisten synkende etter antall innlegg", - "addon.mod_forum.discussionlocked": "Denne diskusjonen har blitt stengt, og du kan derfor ikke svare i den lengre.", - "addon.mod_forum.discussionpinned": "Flagget", - "addon.mod_forum.discussionsubscription": "Diskusjonsabonnement", - "addon.mod_forum.edit": "Rediger", - "addon.mod_forum.erroremptymessage": "Innleggsmeldingen kan ikke være tom", - "addon.mod_forum.erroremptysubject": "Innleggstittel kan ikke være tom.", - "addon.mod_forum.errorgetforum": "Feil ved henting av forumdata", - "addon.mod_forum.errorgetgroups": "Feil ved henting av gruppeinnstillinger.", - "addon.mod_forum.favouriteupdated": "Ditt favorittvalg har blitt oppdatert", - "addon.mod_forum.forumnodiscussionsyet": "Det er foreløpig ingen diskusjonsemner i dette forumet.", - "addon.mod_forum.group": "Gruppe", - "addon.mod_forum.lastpost": "Siste innlegg", - "addon.mod_forum.lockdiscussion": "Lås denne diskusjonen", - "addon.mod_forum.lockupdated": "Låsevalget har blitt oppdatert", - "addon.mod_forum.message": "Melding", - "addon.mod_forum.modeflatnewestfirst": "Vis svar flatt, nyeste innlegg først.", - "addon.mod_forum.modeflatoldestfirst": "Vis svar flatt, eldste innlegg først.", - "addon.mod_forum.modenested": "Vis svar nøstet (Overskrift og innhold)", - "addon.mod_forum.modulenameplural": "Forum", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} diskusjoner", - "addon.mod_forum.numreplies": "{{numreplies}} svar", - "addon.mod_forum.pindiscussion": "Fest denne diskusjonen", - "addon.mod_forum.pinupdated": "Festevalget har blitt oppdatert", - "addon.mod_forum.postisprivatereply": "Dette innlegget er privat og er ikke synlig for andre brukere.", - "addon.mod_forum.posttoforum": "Legg inn i forumet", - "addon.mod_forum.posttomygroups": "Send kopi til alle grupper", - "addon.mod_forum.privatereply": "Privat svar", - "addon.mod_forum.re": "Svar:", - "addon.mod_forum.refreshdiscussions": "Oppdater diskusjon", - "addon.mod_forum.refreshposts": "Oppdater innlegg", - "addon.mod_forum.removefromfavourites": "Fjern favorittmerking for denne diskusjonen", - "addon.mod_forum.reply": "Svar", - "addon.mod_forum.replyplaceholder": "Skriv ditt svar...", - "addon.mod_forum.subject": "Overskrift", - "addon.mod_forum.tagarea_forum_posts": "Foruminnlegg", - "addon.mod_forum.thisforumhasduedate": "Fristen for å skrive innlegg i dette forumet er {{$a}}.", - "addon.mod_forum.thisforumisdue": "Fristen for å skrive innlegg i dette forumet var {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Åpne denne diskusjonen", - "addon.mod_forum.unpindiscussion": "Fjern festing av denne diskusjonen", - "addon.mod_forum.unread": "Ulest", - "addon.mod_forum.unreadpostsnumber": "{{$a}} uleste innlegg", - "addon.mod_forum.yourreply": "Ditt svar", - "addon.mod_glossary.addentry": "Legg til ny oppføring", - "addon.mod_glossary.aliases": "Synonym(er)", - "addon.mod_glossary.attachment": "Vedlegg", - "addon.mod_glossary.browsemode": "Bla i oppføringer", - "addon.mod_glossary.byalphabet": "Alfabetisk", - "addon.mod_glossary.byauthor": "Gruppér etter forfatter", - "addon.mod_glossary.bycategory": "Gruppér etter kategori", - "addon.mod_glossary.bynewestfirst": "Nyeste først", - "addon.mod_glossary.byrecentlyupdated": "Nylig oppdatert", - "addon.mod_glossary.bysearch": "Søk", - "addon.mod_glossary.cannoteditentry": "Kan ikke redigere oppføring", - "addon.mod_glossary.casesensitive": "Store og små bokstaver må stemme i denne oppføringen", - "addon.mod_glossary.categories": "Kategorier", - "addon.mod_glossary.concept": "Begrep", - "addon.mod_glossary.definition": "Definisjon", - "addon.mod_glossary.entriestobesynced": "Oppføringer som skal synkroniseres", - "addon.mod_glossary.entrypendingapproval": "Denne oppføringen venter på godkjenning", - "addon.mod_glossary.entryusedynalink": "Lag automatisk lenker til denne oppføringen", - "addon.mod_glossary.errconceptalreadyexists": "Dette begrepet eksisterer allerede. Ingen duplikater tillatt i denne ordboka.", - "addon.mod_glossary.errorloadingentries": "Feil ved lasting av oppføringer.", - "addon.mod_glossary.errorloadingentry": "Feil ved lasting av oppføringen.", - "addon.mod_glossary.errorloadingglossary": "Feil ved lasting av ordliste", - "addon.mod_glossary.fillfields": "'Begrep' og 'Definisjon' er obligatoriske felter.", - "addon.mod_glossary.fullmatch": "Bare hele ord blir lenket
                    (når det lenkes automatisk)", - "addon.mod_glossary.linking": "Autolenking", - "addon.mod_glossary.modulenameplural": "Ordbøker", - "addon.mod_glossary.noentriesfound": "Ingen oppføringer funnet", - "addon.mod_glossary.searchquery": "Søkeord", - "addon.mod_glossary.tagarea_glossary_entries": "Ordbokoppføringer", - "addon.mod_h5pactivity.all_attempts": "Alle forsøk", - "addon.mod_h5pactivity.answer_checked": "Svar sjekket", - "addon.mod_h5pactivity.answer_correct": "Svaret er korrekt", - "addon.mod_h5pactivity.answer_fail": "Feil svar", - "addon.mod_h5pactivity.answer_incorrect": "Svaret er feil", - "addon.mod_h5pactivity.answer_pass": "Riktig svar", - "addon.mod_h5pactivity.attempt": "Forsøk", - "addon.mod_h5pactivity.attempt_completion_no": "Forsøket er ikke markert som fullført", - "addon.mod_h5pactivity.attempt_completion_yes": "Dette forsøket er fullført", - "addon.mod_h5pactivity.attempt_success_fail": "Ikke bestått", - "addon.mod_h5pactivity.attempt_success_pass": "Bestått", - "addon.mod_h5pactivity.attempt_success_unknown": "Ikke rapportert", - "addon.mod_h5pactivity.attempts_none": "Denne brukeren har ingen forsøk som kan vises", - "addon.mod_h5pactivity.completion": "Fullføring", - "addon.mod_h5pactivity.duration": "Varighet", - "addon.mod_h5pactivity.maxscore": "Maks score", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Mine forsøk", - "addon.mod_h5pactivity.no_compatible_track": "Denne interaksjonen ({{$a}}) gir ikke noen sporingsinformasjon eller så er sporingen ikke kompatibel med nåværende aktivitetsversjon.", - "addon.mod_h5pactivity.outcome": "Resultat", - "addon.mod_h5pactivity.previewmode": "Innholdet vises i forhåndsvisningsmodus. Ingen forsøkssporing vil lagres.", - "addon.mod_h5pactivity.result_fill-in": "Utfyllingstekst", - "addon.mod_h5pactivity.result_other": "Ukjent interaksjonstype", - "addon.mod_h5pactivity.review_my_attempts": "Vis mine forsøk", - "addon.mod_h5pactivity.score": "Poeng", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} av maksimalt {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Startdato", - "addon.mod_h5pactivity.totalscore": "Total poengsum", - "addon.mod_imscp.deploymenterror": "Feil i pakkearkivet!", - "addon.mod_imscp.modulenameplural": "IMS innholdspakker", - "addon.mod_imscp.showmoduledescription": "Vis beskrivelse", - "addon.mod_imscp.toc": "Innholdsoversikt", - "addon.mod_lesson.answer": "Svaralternativ", - "addon.mod_lesson.attempt": "Forsøk: {{$a}}", - "addon.mod_lesson.attemptheader": "Forsøk", - "addon.mod_lesson.attemptsremaining": "Du har {{$a}} forsøk igjen", - "addon.mod_lesson.averagescore": "Gjennomsnittspoengsum", - "addon.mod_lesson.averagetime": "Gjenomsnittstid", - "addon.mod_lesson.branchtable": "Innholdsside", - "addon.mod_lesson.cannotfindattempt": "Feil; kunne ikke finne forsøket", - "addon.mod_lesson.cannotfinduser": "Feil: kunne ikke finne brukere", - "addon.mod_lesson.clusterjump": "Usett spørsmål inne i ei klynge", - "addon.mod_lesson.completed": "Fullført", - "addon.mod_lesson.congratulations": "Gratulerer - du er ferdig med leksjonen", - "addon.mod_lesson.continue": "Fortsett", - "addon.mod_lesson.continuetonextpage": "Fortsett til neste side.", - "addon.mod_lesson.defaultessayresponse": "Læreren din vil sette karakter på innleveringen din.", - "addon.mod_lesson.detailedstats": "Detaljert statistikk", - "addon.mod_lesson.didnotanswerquestion": "Svarte ikke på dette spørsmålet", - "addon.mod_lesson.displayofgrade": "Visning av karakter (kun for studenter)", - "addon.mod_lesson.displayscorewithessays": "

                    Du fikk {{$a.score}} av {{$a.tempmaxgrade}} på de spørsmålene som ble regnet ut automatisk.

                    Teksten(e) du skrev som svar på {{$a.essayquestions}} vil bli vurdert av en lærer og lagt til den endelige karakteren din senere.

                    Karakteren din uten tekstvurdering er nå {{$a.score}} av {{$a.grade}} mulige.

                    ", - "addon.mod_lesson.displayscorewithoutessays": "Du har {{$a.score}} poeng (av {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Passorfeltet kan ikke være tomt", - "addon.mod_lesson.enterpassword": "Skriv inn passordet", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Du svarte ikke på noen spørsmål. Du fikk 0 poeng på denne leksjonen.", - "addon.mod_lesson.finish": "Avslutt", - "addon.mod_lesson.firstwrong": "Du har svart feil. Ønsker du å prøve igjen? (Hvis du svarer korrekt, vil det ikke være tellende for resultatet på leksjonen)", - "addon.mod_lesson.gotoendoflesson": "Gå til slutten av leksjonen", - "addon.mod_lesson.grade": "Karakter", - "addon.mod_lesson.highscore": "Beste poengsum", - "addon.mod_lesson.hightime": "Beste tider", - "addon.mod_lesson.leftduringtimed": "Du har gått ut under en leksjon med tidtaking.
                    Klikk på \"Fortsett\" for å starte på nytt", - "addon.mod_lesson.leftduringtimednoretake": "Du har gått ut under en leksjon med tidtaking, og du får ikke begynne på nytt eller ta leksjonen om igjen.", - "addon.mod_lesson.lessonmenu": "Leksjonsmeny", - "addon.mod_lesson.lessonstats": "Leksjonstatistikk", - "addon.mod_lesson.linkedmedia": "Lenket media", - "addon.mod_lesson.loginfail": "Innlogging mislykka. Prøv igjen...", - "addon.mod_lesson.lowscore": "Dårlige resultater", - "addon.mod_lesson.lowtime": "Svak tid", - "addon.mod_lesson.maximumnumberofattemptsreached": "Maks antall forsøk er oppbrukt. Leksjonen fortsetter på neste side.", - "addon.mod_lesson.modattemptsnoteacher": "Studenten kan bare lese andre studenters bidrag", - "addon.mod_lesson.modulenameplural": "Leksjoner", - "addon.mod_lesson.noanswer": "Ingen svar gitt. Vennligst gå tilbake og avgi et svar.", - "addon.mod_lesson.nolessonattempts": "Det finnes ingen forsøk for denne leksjonen", - "addon.mod_lesson.nolessonattemptsgroup": "Ingen forsøk har blitt gjort av {{$a}} gruppemedlemmer i denne leksjonen.", - "addon.mod_lesson.notcompleted": "Ikke fullført", - "addon.mod_lesson.numberofcorrectanswers": "Antallet riktige svar: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Antallet spørsmål besvart: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Antall spørsmål besvart: {{$a.nquestions}}; (Minstegrense for besvarte spørsmål: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Du har fått {{$a.score}} poeng av {{$a.currenthigh}} mulige så langt.", - "addon.mod_lesson.ongoingnormal": "Du har svart riktig på {{$a.correct}} av de {{$a.viewed}} spørsmålene du har sett så langt.", - "addon.mod_lesson.or": "ELLER", - "addon.mod_lesson.overview": "Oversikt", - "addon.mod_lesson.preview": "Forhåndsvisning", - "addon.mod_lesson.progressbarteacherwarning2": "Fremdriftsindikatoren vises ikke for deg siden du har rettighet til å redigere denne leksjonen. (Vises bare for studenter)", - "addon.mod_lesson.progresscompleted": "Du har fullført {{$a}}% av leksjonen", - "addon.mod_lesson.question": "Legg til en spørsmålsside", - "addon.mod_lesson.rawgrade": "Ubearbeidet karakter", - "addon.mod_lesson.reports": "Rapporter", - "addon.mod_lesson.response": "Tilbakemelding", - "addon.mod_lesson.review": "Bedømmelse", - "addon.mod_lesson.reviewlesson": "Se leksjonen på nytt", - "addon.mod_lesson.reviewquestionback": "Ja, jeg vil gjerne prøve igjen", - "addon.mod_lesson.reviewquestioncontinue": "Nei, jeg vil bare gå videre til neste spørsmål", - "addon.mod_lesson.secondpluswrong": "Ikke helt. Kunne du tenke deg å prøve igjen?", - "addon.mod_lesson.submit": "Send", - "addon.mod_lesson.teacherjumpwarning": "En inndeling av typen {{$a.cluster}} eller {{$a.unseen}} inndeling blir brukt i denne leksjonen. En lenke til neste side vil bli brukt i steden. Logg på som en student for å teste disse inndelingene.", - "addon.mod_lesson.teacherongoingwarning": "Poeng underveis vises bare for studenter. Logg på som en student for å se denne typen visning.", - "addon.mod_lesson.teachertimerwarning": "Tidtaking virker bare for studenter. Test stoppeklokka ved å logge på som en student.", - "addon.mod_lesson.thatsthecorrectanswer": "Det er det riktige svaret", - "addon.mod_lesson.thatsthewronganswer": "Det er feil svar", - "addon.mod_lesson.timeremaining": "Tid som gjenstår", - "addon.mod_lesson.timetaken": "Brukt tid", - "addon.mod_lesson.unseenpageinbranch": "Usette spørsmål i en gren", - "addon.mod_lesson.welldone": "Bra!", - "addon.mod_lesson.youhaveseen": "Du har sett mer enn en side i denne leksjonen allerede.
                    Vil du starte på den siste siden du var inne på?", - "addon.mod_lesson.youranswer": "Ditt svar", - "addon.mod_lesson.yourcurrentgradeisoutof": "Gjeldende karakter er {{$a.grade}} av {{$a.total}}", - "addon.mod_lesson.youshouldview": "Du bør besvare minst: {{$a}}", - "addon.mod_lti.modulenameplural": "Eksterne verktøy", - "addon.mod_page.modulenameplural": "Sider", - "addon.mod_quiz.answercolon": "Svar:", - "addon.mod_quiz.attemptfirst": "Første forsøk", - "addon.mod_quiz.attemptlast": "Siste forsøk", - "addon.mod_quiz.attemptnumber": "Forsøk", - "addon.mod_quiz.attemptquiznow": "Ta quizen nå", - "addon.mod_quiz.attemptstate": "Status", - "addon.mod_quiz.clearchoice": "Fjern valg", - "addon.mod_quiz.comment": "Kommentar", - "addon.mod_quiz.completedon": "Fullført den", - "addon.mod_quiz.confirmclose": "Etter at du har sendt inn besvarelsen kan du ikke lenger forandre svarene dine for dette forsøket.", - "addon.mod_quiz.confirmstart": "Denne quizen har en tidsgrense på {{$a}}. Tiden vil telles ned fra det øyeblikket du starter forsøket og du må levere før tiden utløper. Er du sikker på at du ønsker å starte nå?", - "addon.mod_quiz.confirmstartheader": "Tidsgrense", - "addon.mod_quiz.connectionerror": "Nettverksforbindelsen er brutt. (Autolagring mislykkes).\nTa et notat på kommentarene du la inn på denne siden de siste minuttene og prøv igjen.\n\nStraks forbindelsen er gjenopprettet, bør kommentarene dine bli lagret og da forsvinner denne meldingen.", - "addon.mod_quiz.continueattemptquiz": "Fortsett med forrige forsøk", - "addon.mod_quiz.continuepreview": "Fortsett siste forhåndsvisning", - "addon.mod_quiz.feedback": "Tilbakesvar", - "addon.mod_quiz.finishattemptdots": "Avslutt forsøket...", - "addon.mod_quiz.grade": "Karakter", - "addon.mod_quiz.gradeaverage": "Gjennomsnittlig karakter", - "addon.mod_quiz.gradehighest": "Høyeste karakter", - "addon.mod_quiz.grademethod": "Karaktermetode", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Poeng", - "addon.mod_quiz.modulenameplural": "Quizer", - "addon.mod_quiz.mustbesubmittedby": "Dette forsøket må sendes innen: {{$a}}.", - "addon.mod_quiz.noquestions": "Ingen spørsmål er lagt til enda", - "addon.mod_quiz.noreviewattempt": "Du har ikke tillatelse til å se gjennom dette forsøket.", - "addon.mod_quiz.notyetgraded": "Karakter foreløpig ikke satt", - "addon.mod_quiz.outof": "{{$a.grade}} av maksimalt {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} av maksimum {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Tilbakemelding på testen", - "addon.mod_quiz.overdue": "For sent", - "addon.mod_quiz.overduemustbesubmittedby": "Tiden har nå gått ut for dette forsøket. Besvarelsen skulle allerede ha vært levert inn. Hvis du vil ha karakter på dette forsøket, må du sende det inn innen {{$a}}. Hvis du ikke sender det inn innen dette tidspunktet, vil ikke noen karakter fra dette forsøket telles med.", - "addon.mod_quiz.preview": "Forhåndsvisning", - "addon.mod_quiz.previewquiznow": "Ta quizen nå", - "addon.mod_quiz.question": "Spørsmål", - "addon.mod_quiz.quiznavigation": "Oversikt", - "addon.mod_quiz.quizpassword": "Quizpassord", - "addon.mod_quiz.reattemptquiz": "Ta quizen på nytt", - "addon.mod_quiz.requirepasswordmessage": "Denne quizen krever at du har et passord", - "addon.mod_quiz.returnattempt": "Gå tilbake til forsøket", - "addon.mod_quiz.review": "Sammendrag", - "addon.mod_quiz.reviewofattempt": "Sammendrag av forsøk {{$a}}", - "addon.mod_quiz.reviewofpreview": "Gjennomgang av forhåndsvisning", - "addon.mod_quiz.showall": "Vis alle spørsmålene på samme side", - "addon.mod_quiz.showeachpage": "Vis en side om gangen", - "addon.mod_quiz.startattempt": "Start forsøk", - "addon.mod_quiz.startedon": "Begynt den", - "addon.mod_quiz.stateabandoned": "Aldri sendt inn", - "addon.mod_quiz.statefinished": "Ferdig", - "addon.mod_quiz.statefinisheddetails": "Levert {{$a}}", - "addon.mod_quiz.stateinprogress": "Pågår", - "addon.mod_quiz.stateoverdue": "På overtid", - "addon.mod_quiz.stateoverduedetails": "Må sendes inn innen {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Send svarene og avslutt", - "addon.mod_quiz.summaryofattempt": "Oppsummering av forsøk", - "addon.mod_quiz.summaryofattempts": "Oppsummering av dine tidligere forsøk", - "addon.mod_quiz.timeleft": "Gjenstående tid", - "addon.mod_quiz.timetaken": "Totalt brukt tid", - "addon.mod_quiz.yourfinalgradeis": "Sluttkarakter på denne quizen ble {{$a}}.", - "addon.mod_resource.modifieddate": "Siste endret {{$a}}", - "addon.mod_resource.modulenameplural": "Filer", - "addon.mod_resource.uploadeddate": "Lastet opp {{$a}}", - "addon.mod_scorm.asset": "Ressurs", - "addon.mod_scorm.assetlaunched": "Ressurs - Sett", - "addon.mod_scorm.attempts": "Forsøk", - "addon.mod_scorm.averageattempt": "Gjennomsnittsforsøk", - "addon.mod_scorm.browse": "Forhåndsvis", - "addon.mod_scorm.browsed": "Lest", - "addon.mod_scorm.browsemode": "Forhåndsvisningsmodus", - "addon.mod_scorm.completed": "Fullført", - "addon.mod_scorm.contents": "Innhold", - "addon.mod_scorm.enter": "Start", - "addon.mod_scorm.exceededmaxattempts": "Du har nådd maksimalt antall forsøk.", - "addon.mod_scorm.failed": "Feilet", - "addon.mod_scorm.firstattempt": "Første forsøk", - "addon.mod_scorm.gradeaverage": "Gjennomsnittlig karakter", - "addon.mod_scorm.gradeforattempt": "Karakter på forsøket", - "addon.mod_scorm.gradehighest": "Høyeste karakter", - "addon.mod_scorm.grademethod": "Metode for karaktersetting", - "addon.mod_scorm.gradereported": "Karakter rapportert", - "addon.mod_scorm.gradescoes": "Læringsobjekter", - "addon.mod_scorm.gradesum": "Sum karakter", - "addon.mod_scorm.highestattempt": "Beste forsøk", - "addon.mod_scorm.incomplete": "Ufullstendig", - "addon.mod_scorm.lastattempt": "Siste fullførte forsøk", - "addon.mod_scorm.modulenameplural": "SCORM/AICC-pakker", - "addon.mod_scorm.newattempt": "Begynn et nytt forsøk", - "addon.mod_scorm.noattemptsallowed": "Maksimum antall forsøk", - "addon.mod_scorm.noattemptsmade": "Antall forsøk du har gjort til nå", - "addon.mod_scorm.notattempted": "Ikke forsøkt", - "addon.mod_scorm.organizations": "Organisasjoner", - "addon.mod_scorm.passed": "Bestått", - "addon.mod_scorm.reviewmode": "Se over modus", - "addon.mod_scorm.score": "Resultat", - "addon.mod_scorm.suspended": "Avbrutt", - "addon.mod_scorm.toc": "TOC", - "addon.mod_survey.ifoundthat": "Jeg fant at", - "addon.mod_survey.ipreferthat": "Jeg vil foretrekke at", - "addon.mod_survey.modulenameplural": "Undersøkelser", - "addon.mod_survey.responses": "Besvareslser", - "addon.mod_survey.surveycompletednograph": "Du har fullført denne undersøkelsen", - "addon.mod_url.modulenameplural": "URL-er", - "addon.mod_wiki.cannoteditpage": "Du kan ikke redigere denne siden", - "addon.mod_wiki.createpage": "Lag side", - "addon.mod_wiki.editingpage": "Endrer denne siden '{{$a}}'", - "addon.mod_wiki.map": "Kart", - "addon.mod_wiki.modulenameplural": "Wikier", - "addon.mod_wiki.newpagehdr": "Ny side", - "addon.mod_wiki.newpagetitle": "Ny sidetittel", - "addon.mod_wiki.nocontent": "Denne siden har ikke innhold", - "addon.mod_wiki.notingroup": "Ikke i gruppen", - "addon.mod_wiki.pageexists": "Denne siden eksisterer allerede.", - "addon.mod_wiki.pagename": "Sidenavn", - "addon.mod_wiki.tagarea_wiki_pages": "Wikisider", - "addon.mod_wiki.titleshouldnotbeempty": "Tittel kan ikke være blank", - "addon.mod_wiki.viewpage": "Vis side", - "addon.mod_wiki.wikipage": "Wikiside", - "addon.mod_wiki.wrongversionlock": "En annen bruker endret denne siden du endrer og ditt innhold er derfor utdatert.", - "addon.mod_workshop.alreadygraded": "Allerede vurdert", - "addon.mod_workshop.areainstructauthors": "Innleveringsinstruksjoner", - "addon.mod_workshop.areainstructreviewers": "Vurderingsinstruksjoner", - "addon.mod_workshop.assess": "Vurdér", - "addon.mod_workshop.assessedsubmission": "Vurderte innleveringer", - "addon.mod_workshop.assessmentform": "Evalueringsskjema", - "addon.mod_workshop.assessmentsettings": "Vurderingsinnstillinger", - "addon.mod_workshop.assessmentweight": "Vurderingsvekting", - "addon.mod_workshop.assignedassessments": "Tildelte innleveringer for vurdering", - "addon.mod_workshop.assignedassessmentsnone": "Du har ingen tildelte innleveringer å vurdere", - "addon.mod_workshop.conclusion": "Konklusjon", - "addon.mod_workshop.createsubmission": "Legg til innsending", - "addon.mod_workshop.deletesubmission": "Slett innlevering", - "addon.mod_workshop.editsubmission": "Rediger innlevering", - "addon.mod_workshop.feedbackauthor": "Tilbakemelding til forfatteren", - "addon.mod_workshop.feedbackby": "Tilbakemelding fra {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Tilbakemelding fra den som vurderer", - "addon.mod_workshop.givengrades": "Gitte karakterer", - "addon.mod_workshop.gradecalculated": "Beregn karakter for innlevering", - "addon.mod_workshop.gradeinfo": "Karakter: {{$a.received}} av {{$a.max}}", - "addon.mod_workshop.gradeover": "Overstyr innleveringskarakter", - "addon.mod_workshop.gradesreport": "Workshop karakterrapport", - "addon.mod_workshop.gradinggrade": "Karakter for vurdering", - "addon.mod_workshop.gradinggradecalculated": "Beregnet karakter for vurdering", - "addon.mod_workshop.gradinggradeof": "Karakter for vurdering (av {{$a}})", - "addon.mod_workshop.gradinggradeover": "Overstyr karakter for vurdering", - "addon.mod_workshop.modulenameplural": "Workshoper", - "addon.mod_workshop.nogradeyet": "Ingen karakter ennå", - "addon.mod_workshop.notassessed": "Ingen vurdert ennå", - "addon.mod_workshop.notoverridden": "Ikke overstyrt", - "addon.mod_workshop.noyoursubmission": "Du har ikke levert inn arbeidet ditt ennå", - "addon.mod_workshop.overallfeedback": "Generell tilbakemelding", - "addon.mod_workshop.publishedsubmissions": "Publiserte innleveringer", - "addon.mod_workshop.publishsubmission": "Publiser innlevering", - "addon.mod_workshop.publishsubmission_help": "Publiserte innleveringer er tilgjengelige for andre når workshopen er stengt.", - "addon.mod_workshop.reassess": "Vurder på nytt", - "addon.mod_workshop.receivedgrades": "Mottatte karakterer", - "addon.mod_workshop.submissionattachment": "Vedlegg", - "addon.mod_workshop.submissioncontent": "Innleveringsinnhold", - "addon.mod_workshop.submissiondeleteconfirm": "Er du sikker på at du ønsker å slette følgende innlevering?", - "addon.mod_workshop.submissiongrade": "Karakter for innlevering", - "addon.mod_workshop.submissiongradeof": "Karakter for innlevering (av {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Du må enten skrive inn tekst eller legge ved en fil", - "addon.mod_workshop.submissionsreport": "Workshop innleveringsrapport", - "addon.mod_workshop.submissiontitle": "Tittel", - "addon.mod_workshop.switchphase10": "Bytt til oppsettsfasen", - "addon.mod_workshop.switchphase20": "Bytt til innleveringsfasen", - "addon.mod_workshop.switchphase30": "Bytt til vurderingsfasen", - "addon.mod_workshop.switchphase40": "Bytt til evalueringsfasen", - "addon.mod_workshop.switchphase50": "Steng workshop", - "addon.mod_workshop.userplan": "Workshop planlegger", - "addon.mod_workshop.userplancurrentphase": "Nåværende fase", - "addon.mod_workshop.weightinfo": "Vekting: {{$a}}", - "addon.mod_workshop.yourassessment": "Din vurdering", - "addon.mod_workshop.yourassessmentfor": "Din vurdering for {{$a}}", - "addon.mod_workshop.yourgrades": "Dine karakterer", - "addon.mod_workshop.yoursubmission": "Din innlevering", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Kommenter for {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Vurder for {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspekt {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Du må velge en karakter for denne delen", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Kommenter for {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspekt {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Kommenter for {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Vurder for {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Stadfesting {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kriterie {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Du må velge ett av disse elementene", - "addon.notes.addnewnote": "Legg til nytt notat", - "addon.notes.coursenotes": "Kursnotater", - "addon.notes.deleteconfirm": "Slett dette notatet?", - "addon.notes.eventnotecreated": "Notat opprettet", - "addon.notes.eventnotedeleted": "Notat slettet", - "addon.notes.nonotes": "Ingen notater av denne typen finnes ennå", - "addon.notes.note": "Notat", - "addon.notes.notes": "Notater", - "addon.notes.personalnotes": "Personlige notater", - "addon.notes.publishstate": "Kontekst", - "addon.notes.sitenotes": "nettstedsnotater", - "addon.notes.userwithid": "Bruker med id {{id}}", - "addon.notes.warningnotenotsent": "Kunne ikke legge til notat(er) på kurs {{course}}. {{error}}", - "addon.notifications.markallread": "Merk alle som lest", - "addon.notifications.notificationpreferences": "Varslingspreferanser", - "addon.notifications.playsound": "Spill lyd", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "De forente arabiske emiratene", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua og Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktis", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Amerikansk Samoa", - "assets.countries.AT": "Østerrike", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland", - "assets.countries.AZ": "Aserbajdsjan", - "assets.countries.BA": "Bosnia-Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgia", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei Darussalam", - "assets.countries.BO": "Bolivia", - "assets.countries.BQ": "Bonaire, Sint Eustatius og Saba", - "assets.countries.BR": "Brasil", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvetøya", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Hviterussland", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canada", - "assets.countries.CC": "Kokosøyene", - "assets.countries.CD": "Kongo, Den demokratiske republikken", - "assets.countries.CF": "Den sentralafrikanske republikken", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Sveits", - "assets.countries.CI": "Elfenbenskysten", - "assets.countries.CK": "Cookøyene", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Kina", - "assets.countries.CO": "Colombia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Kapp Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Christmasøya", - "assets.countries.CY": "Kypros", - "assets.countries.CZ": "Tsjekkia", - "assets.countries.DE": "Tyskland", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Danmark", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Den dominikanske republikken", - "assets.countries.DZ": "Algerie", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estland", - "assets.countries.EG": "Egypt", - "assets.countries.EH": "Vest-Sahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spania", - "assets.countries.ET": "Etiopia", - "assets.countries.FI": "Finland", - "assets.countries.FJ": "Fijii", - "assets.countries.FK": "Falklandsøyene", - "assets.countries.FM": "Mikronesiaføderasjonen", - "assets.countries.FO": "Færøyene", - "assets.countries.FR": "Frankrike", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Storbritannia", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Fransk Guyana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grønland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Ekvatorial-Guinea", - "assets.countries.GR": "Hellas", - "assets.countries.GS": "Sør-Georgia og Sør-Sandwichøyene", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Heard- og McDonaldøyene", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Kroatia", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Ungarn", - "assets.countries.ID": "Indonesia", - "assets.countries.IE": "Irland", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Isle of Man", - "assets.countries.IN": "India", - "assets.countries.IO": "Det britiske territoriet i Indiahav", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran", - "assets.countries.IS": "Island", - "assets.countries.IT": "Italia", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordan", - "assets.countries.JP": "Japan", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirgisistan", - "assets.countries.KH": "Kambodsja", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komorene", - "assets.countries.KN": "Saint Kitts og Nevis", - "assets.countries.KP": "Nord-Korea", - "assets.countries.KR": "Sør-Korea", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Caymanøyene", - "assets.countries.KZ": "Kasakhstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litauen", - "assets.countries.LU": "Luxembourg", - "assets.countries.LV": "Latvia", - "assets.countries.LY": "Libya", - "assets.countries.MA": "Marokko", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldova", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin (fransk del)", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshalløyene", - "assets.countries.MK": "Makedonia", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Macao", - "assets.countries.MP": "De nordre Marianene", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Maldivene", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexico", - "assets.countries.MY": "Malaysia", - "assets.countries.MZ": "Mosambik", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Ny-Caledonia", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolkøya", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Nederland", - "assets.countries.NO": "Norge", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "New Zealand", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Fransk Polynesia", - "assets.countries.PG": "Papua Ny-Guinea", - "assets.countries.PH": "Filippinene", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polen", - "assets.countries.PM": "Saint-Pierre og Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestina", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Quatar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Romania", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Russland", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Saudi-Arabia", - "assets.countries.SB": "Salomonøyene", - "assets.countries.SC": "Seychellene", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Sverige", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "Saint Helena, Ascension og Tristan Da Cunha", - "assets.countries.SI": "Slovenia", - "assets.countries.SJ": "Svalbard og Jan Mayen", - "assets.countries.SK": "Slovakia", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Sør-Sudan", - "assets.countries.ST": "São Tomé og Príncipe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (nederlandsk del)", - "assets.countries.SY": "Syria", - "assets.countries.SZ": "Swaziland", - "assets.countries.TC": "Turks- og Caicosøyene", - "assets.countries.TD": "Tchad", - "assets.countries.TF": "De franske territoriene i sør", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailand", - "assets.countries.TJ": "Tadsjikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunisia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Tyrkia", - "assets.countries.TT": "Trinidad og Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzania", - "assets.countries.UA": "Ukraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "USAs ytre småøyer", - "assets.countries.US": "USA", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Usbekistan", - "assets.countries.VA": "Vatikanstaten", - "assets.countries.VC": "Saint Vincent og Grenadinene", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "De britiske jomfruøyene", - "assets.countries.VI": "Jomfruøyene", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis- og Futunaøyene", - "assets.countries.WS": "Vest-Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Majotte", - "assets.countries.ZA": "Sør-Afrika", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB e-bok", - "assets.mimetypes.application/msword": "Word-dokument", - "assets.mimetypes.application/pdf": "PDF-dokument", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle sikkerhetskopiering", - "assets.mimetypes.application/vnd.ms-excel": "Excel regneark", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 regneark med makroer aktivert", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint presentasjon", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument regneark", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument regnearksmal", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument tekstdokument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument tekstdokumentmal", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument websidemal", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 presentasjon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 lysbildefremvisning", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 regneark", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 mal", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 dokument", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote presentasjon", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers regneark", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages dokument", - "assets.mimetypes.application/x-javascript": "JavaScript kilde", - "assets.mimetypes.application/x-mspublisher": "Utgiverdokument", - "assets.mimetypes.application/x-shockwave-flash": "Flashanimasjon", - "assets.mimetypes.application/xhtml_xml": "XHTML dokument", - "assets.mimetypes.archive": "Arkiv ({{$a.EXT}})", - "assets.mimetypes.audio": "Lydfil ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Fil", - "assets.mimetypes.group:archive": "Arkivfiler", - "assets.mimetypes.group:audio": "Lydfiler", - "assets.mimetypes.group:document": "Dokumentfiler", - "assets.mimetypes.group:html_audio": "Lydfilformater som støttes av nettlesere", - "assets.mimetypes.group:html_track": "HTML-sporfiler", - "assets.mimetypes.group:html_video": "Videofilformater som støttes av nettlesere", - "assets.mimetypes.group:image": "Bildefiler", - "assets.mimetypes.group:presentation": "Presentasjonsfiler", - "assets.mimetypes.group:sourcecode": "Kildekode", - "assets.mimetypes.group:spreadsheet": "Regnearkfiler", - "assets.mimetypes.group:video": "Videofiler", - "assets.mimetypes.group:web_audio": "Lydfiler brukt på internett", - "assets.mimetypes.group:web_file": "Webfiler", - "assets.mimetypes.group:web_image": "Bildefiler brukt på internett", - "assets.mimetypes.group:web_video": "Videofiler brukt på internett", - "assets.mimetypes.image": "Bilde ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows-ikon", - "assets.mimetypes.text/css": "Cascading Style-Sheet", - "assets.mimetypes.text/csv": "Kommaseparert liste", - "assets.mimetypes.text/html": "HTML-dokument", - "assets.mimetypes.text/plain": "Tekstfil", - "assets.mimetypes.text/rtf": "RTF-dokument", - "assets.mimetypes.text/vtt": "Web Video Text Track", - "assets.mimetypes.video": "Videofil ({{$a.EXT}})", - "core.accounts": "Kontoer", - "core.add": "Legg til", - "core.agelocationverification": "Bekreftelse av alder og sted", - "core.ago": "for {{$a}} siden", - "core.all": "Alle", - "core.allgroups": "Alle grupper", - "core.allparticipants": "Alle deltakere", - "core.answer": "Svar", - "core.answered": "Besvart", - "core.areyousure": "Er du sikker?", - "core.back": "Tilbake", - "core.block.blocks": "Blokker", - "core.cancel": "Avbryt", - "core.captureaudio": "Spill inn lyd", - "core.capturedimage": "Bilde tatt.", - "core.captureimage": "Ta bilde", - "core.capturevideo": "Ta opp video", - "core.category": "Kategori", - "core.choose": "Velg", - "core.choosedots": "Velg...", - "core.clearsearch": "Nullstill søk", - "core.clicktohideshow": "Klikk for å utvide/skjule", - "core.clicktoseefull": "Klikk for å se hele innholdet.", - "core.close": "Steng", - "core.comments": "Kommentarer", - "core.comments.addcomment": "Legg til en kommentar ...", - "core.comments.comments": "Kommentarer", - "core.comments.commentscount": "Kommentarer ({{$a}})", - "core.comments.deletecommentbyon": "Slett kommentaren som ble skrevet {{$a.time}} av {{$a.user}}", - "core.comments.eventcommentcreated": "Kommentar skrevet", - "core.comments.eventcommentdeleted": "Kommentar slettet", - "core.comments.nocomments": "Ingen kommentarer", - "core.comments.savecomment": "Lagre kommentar", - "core.commentscount": "Kommentarer ({{$a}})", - "core.completion-alt-auto-fail": "Gjennomført: {{$a}} (uten godkjent karakter)", - "core.completion-alt-auto-n": "Ikke fullført: {{$a}}", - "core.completion-alt-auto-n-override": "Ikke fullført: {{$a.modname}} (satt av {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Fullført: {{$a}} (med godkjent karakter)", - "core.completion-alt-auto-y": "Fullført: {{$a}}", - "core.completion-alt-auto-y-override": "Fullført: {{$a.modname}} (satt av {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Ikke fullført: {{$a}} Velg for å merke som fullført", - "core.completion-alt-manual-n-override": "Ikke fullført: {{$a.modname}} (set by {{$a.overrideuser}}). Velg for å markere som fullført.", - "core.completion-alt-manual-y": "Fullført: {{$a}} Velg for å merke som ikke fullført.", - "core.completion-alt-manual-y-override": "Fullført: {{$a.modname}} (set by {{$a.overrideuser}}). Velg for å markere som ikke fullført.", - "core.confirmcanceledit": "Er du sikker på at du vil forlate siden? Du vil miste alle endringer.", - "core.confirmdeletefile": "Er du sikker på at du vil slette denne fila?", - "core.confirmloss": "Er du sikker? Du vil miste alle endringer.", - "core.confirmopeninbrowser": "Vil du åpne den i nettleser?", - "core.considereddigitalminor": "Du er ikke gammel nok til å lage en bruker på denne portalen.", - "core.content": "Innhold", - "core.contentlinks.chooseaccount": "Velg konto", - "core.contentlinks.chooseaccounttoopenlink": "Velg en konto å åpne lenken med", - "core.continue": "Fortsett", - "core.course": "Kurs", - "core.course.contents": "Innhold", - "core.course.coursesummary": "Kurssammendrag", - "core.course.downloadcourse": "Last ned kurs", - "core.course.hiddenfromstudents": "Skjult for studenter", - "core.course.hiddenoncoursepage": "Tilgjengelig, men ikke vist på kurssiden.", - "core.course.overriddennotice": "Den endelige karakteren fra denne aktiviteten ble redigert manuelt.", - "core.course.sections": "Kategorier", - "core.coursedetails": "Kursdetaljer", - "core.courses.addtofavourites": "Merk som favoritt", - "core.courses.allowguests": "Dette kurset tillater besøk av gjester", - "core.courses.availablecourses": "Tilgjengelige kurs", - "core.courses.categories": "Kurskategorier", - "core.courses.courses": "Kurs", - "core.courses.frontpage": "Hovedside", - "core.courses.hidecourse": "Fjernet fra visning", - "core.courses.ignore": "Ignorér", - "core.courses.mycourses": "Mine kurs", - "core.courses.mymoodle": "Min startside", - "core.courses.nocourses": "Ingen kursinformasjon å vise.", - "core.courses.nocoursesyet": "Ingen kurs i denne kategorien", - "core.courses.nosearchresults": "Ingen resultater", - "core.courses.notenroled": "Du er ikke påmeldt dette kurset", - "core.courses.paymentrequired": "Dette er et kurs som krever betaling", - "core.courses.paypalaccepted": "PayPal betalingsformer som er godkjent", - "core.courses.reload": "Last på nytt", - "core.courses.removefromfavourites": "Fjern fra favoritter", - "core.courses.search": "Søk", - "core.courses.searchcourses": "Søk etter studiesider", - "core.courses.sendpaymentbutton": "Send betaling via PayPal", - "core.courses.show": "Gjenopprett til visning", - "core.date": "Dato", - "core.day": "dag", - "core.days": "dager", - "core.decsep": ",", - "core.defaultvalue": "Standard ({{$a}})", - "core.delete": "Slett", - "core.deleteduser": "Slettet bruker", - "core.description": "Beskrivelse", - "core.digitalminor": "Mindreårig", - "core.digitalminor_desc": "For å opprette en konto på denne portalen må dine foresatte kontakte følgede person.", - "core.displayoptions": "Visningsvalg", - "core.done": "Ferdig", - "core.download": "Last ned", - "core.edit": "Rediger", - "core.editor.autosavesucceeded": "Utkast lagret", - "core.editor.bold": "Uthevet", - "core.editor.clear": "Slett formatering", - "core.editor.h3": "Overskrift 1", - "core.editor.h4": "Overskrift 2", - "core.editor.h5": "Overskrift 3", - "core.editor.italic": "Kursiv", - "core.editor.orderedlist": "Nummerert liste", - "core.editor.p": "Avsnitt", - "core.editor.strike": "Gjennomstreket", - "core.editor.textrecovered": "Et utkast for denne teksten ble automatisk gjenopprettet", - "core.editor.underline": "Understreket", - "core.editor.unorderedlist": "Kulepunktliste", - "core.error": "Feil", - "core.explanationdigitalminor": "Denne informasjonen er påkrevd for å bestemme om din aldere er over lavalderen for samtykke. Dette er alderen for når en person kan samtykke til vilkår og betingelser og at dataene deres på lovlig vis blir lagret og behandlet.", - "core.favourites": "Favoritter", - "core.filename": "Filnavn", - "core.filenotfound": "Beklager, men fant ikke filen.", - "core.fileuploader.addfiletext": "Legg til fil", - "core.fileuploader.filesofthesetypes": "Aksepterte filtyper:", - "core.fileuploader.invalidfiletype": "{{$a}} filtypen aksepteres ikke", - "core.fileuploader.more": "Mer", - "core.filter": "Filter", - "core.folder": "Mappe", - "core.forcepasswordchangenotice": "Du må endre passordet for å fortsette", - "core.fulllistofcourses": "Alle kurs", - "core.grades.average": "Gjennomsnitt", - "core.grades.badgrade": "Angitte karakter er ugyldig", - "core.grades.contributiontocoursetotal": "Bidrag til kurstotal", - "core.grades.feedback": "Tilbakemelding", - "core.grades.grade": "Karakter", - "core.grades.gradeitem": "Karakterenhet", - "core.grades.grades": "Karakterer", - "core.grades.lettergrade": "Bokstavkarakter", - "core.grades.nogradesreturned": "No grades returned", - "core.grades.nooutcome": "Ikke noe resultat", - "core.grades.percentage": "Prosentandel", - "core.grades.range": "Skala", - "core.grades.rank": "Rank", - "core.grades.weight": "Vekting", - "core.group": "Gruppe", - "core.groupsseparate": "Separate grupper", - "core.groupsvisible": "Synlige grupper", - "core.h5p.additionallicenseinfo": "Mer informasjon om lisensen", - "core.h5p.author": "Forfatter", - "core.h5p.authorcomments": "Forfatterkommentarer", - "core.h5p.authorcommentsdescription": "Kommentarer fra innholdets forfatter. (Denne teksten vil ikkepubliseres som en del av opphavsrettinformasjonen.)", - "core.h5p.authorname": "Forfatters navn", - "core.h5p.authorrole": "Forfatters rolle", - "core.h5p.by": "av", - "core.h5p.cancellabel": "Avbryt", - "core.h5p.ccattribution": "Attribution (CC BY)", - "core.h5p.ccattributionnc": "Attribution-NonCommercial (CC BY-NC)", - "core.h5p.ccattributionncnd": "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Attribution-NoDerivs (CC BY-ND)", - "core.h5p.ccattributionsa": "Attribution-ShareAlike (CC BY-SA)", - "core.h5p.ccpdd": "Public Domain Dedication (CC0)", - "core.h5p.changedby": "Endret av", - "core.h5p.changedescription": "Endringsbeskrivelse", - "core.h5p.changelog": "Endringslogg", - "core.h5p.changeplaceholder": "Foto beskåret, tekst endret, etc.", - "core.h5p.close": "Lukk", - "core.h5p.confirmdialogbody": "Vennligst bekreft at du ønsker å fortsette. Denne handlingen kan ikke angr", - "core.h5p.confirmdialogheader": "Bekreft handling", - "core.h5p.confirmlabel": "Bekreft", - "core.h5p.connectionLost": "Tilkobling tapt. Resultatene blir lagret og sendt når forbindelsen er gjenopprettet.", - "core.h5p.connectionReestablished": "Tilkoblingen gjenopprettet.", - "core.h5p.contentCopied": "Innhold kopiert til utklippstavla", - "core.h5p.contentchanged": "Innholdet har blitt endret siden sist visning.", - "core.h5p.contenttype": "Innholdstype", - "core.h5p.copyright": "Bruksrettigheter", - "core.h5p.copyrightinfo": "Opphavsrettinformasjon", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.copyrighttitle": "Vis informasjon om opphavsrett for innholdet.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Dato", - "core.h5p.disablefullscreen": "Deaktiver fullskjerm", - "core.h5p.download": "Last ned", - "core.h5p.downloadtitle": "Last ned dette innholdet som en H5P-fil.", - "core.h5p.editor": "Redaktør", - "core.h5p.embed": "Bygg inn", - "core.h5p.embedtitle": "Vis innbyggingskoden for dette innholde", - "core.h5p.fullscreen": "Fullskjerm", - "core.h5p.gpl": "General Public License v3", - "core.h5p.h5ptitle": "Besøk h5p.org for å sjekke ut mer innhold.", - "core.h5p.hideadvanced": "Skjul avansert", - "core.h5p.license": "Lisens", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) dedikasjon til offentlig domene", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Generisk", - "core.h5p.licenseCC20": "2.0 Generisk", - "core.h5p.licenseCC25": "2.5 Generisk", - "core.h5p.licenseCC30": "3.0 Uportert", - "core.h5p.licenseCC40": "4.0 Internasjonalt", - "core.h5p.licenseGPL": "General Public License", - "core.h5p.licenseV1": "Versjon 1", - "core.h5p.licenseV2": "Versjon 2", - "core.h5p.licenseV3": "Versjon 3", - "core.h5p.licensee": "Lisensholder", - "core.h5p.licenseextras": "Lisens tillegg", - "core.h5p.licenseversion": "Lisensversjon", - "core.h5p.nocopyright": "Ingen opphavsrettinformasjon tilgjengelige for dette innholdet.", - "core.h5p.offlineDialogBody": "Vi kunne ikke sende informasjon om hvordan du fullførte denne oppgaven. Vennligst sjekk internettforbindelsen din.", - "core.h5p.offlineDialogHeader": "Forbindelsen din til serveren gikk tapt", - "core.h5p.offlineDialogRetryButtonLabel": "Prøv på nytt nå", - "core.h5p.offlineDialogRetryMessage": "Prøv på nytt :num ....", - "core.h5p.offlineSuccessfulSubmit": "Resultater sendt.", - "core.h5p.originator": "Opphavsmann", - "core.h5p.pd": "Offentlig domene", - "core.h5p.pddl": "Dedikasjon og lisens for offentlig domene", - "core.h5p.pdm": "Offentlig domene merke (PDM)", - "core.h5p.resizescript": "Inkluder dette ksriptet på nettsiden din om du ønsker dynamisk størrelse på det innebygde inn", - "core.h5p.resubmitScores": "Forsøker å levere lagrede resultater.", - "core.h5p.reuse": "Bruk på nytt", - "core.h5p.reuseContent": "Bruk innhold på nytt", - "core.h5p.reuseDescription": "Bruk dette innholdet på nytt.", - "core.h5p.showadvanced": "Vis avansert", - "core.h5p.showless": "Vis mindre", - "core.h5p.showmore": "Vis mer", - "core.h5p.size": "Størrelse", - "core.h5p.source": "Kilde", - "core.h5p.startingover": "Du må starte på nytt", - "core.h5p.sublevel": "Undernivå", - "core.h5p.thumbnail": "Thumbnail", - "core.h5p.title": "Tittel", - "core.h5p.undisclosed": "Ikke oppgitt", - "core.h5p.year": "År", - "core.h5p.years": "År", - "core.h5p.yearsfrom": "År (fra)", - "core.h5p.yearsto": "År (til)", - "core.help": "Hjelp", - "core.hide": "Skjul", - "core.hour": "time", - "core.hours": "timer", - "core.info": "Informasjon", - "core.invalidformdata": "Feil skjemadata", - "core.labelsep": ":", - "core.lastaccess": "Sist innlogget", - "core.lastmodified": "Sist endret", - "core.layoutgrid": "Rutenett", - "core.list": "Liste", - "core.listsep": ";", - "core.loading": "Laster", - "core.location": "Sted", - "core.login.auth_email": "E-postbasert autentisering", - "core.login.authenticating": "Autentiserer", - "core.login.cancel": "Avbryt", - "core.login.changepassword": "Endre passord", - "core.login.createaccount": "Lag en ny brukerkonto", - "core.login.createuserandpass": "Lag et brukernavn og passord for innlogging", - "core.login.emailconfirmsent": "

                    En e-post er nå sendt til adressen {{$a}}

                    \n

                    Den inneholder informasjon om hvordan du fullfører registreringen.


                    \nOBS! Bekreft via URL straks du har fått e-posten. Går det for lang tid vil bekreftelsesprosessen bli automatisk annulert og du må begynne på nytt.\n

                    Hvis du fortsatt har problemer, vennligst ta kontakt med administrator for portalen.

                    ", - "core.login.emailconfirmsentsuccess": "Vellykket sending av bekreftelsesepost", - "core.login.firsttime": "Er det første gang du er her?", - "core.login.forcepasswordchangenotice": "Du må endre passordet for å fortsette", - "core.login.forgotten": "Har du glemt brukernavn eller passord?", - "core.login.help": "Hjelp", - "core.login.instructions": "Instruksjoner", - "core.login.invaliddate": "Ugyldig dato", - "core.login.invalidemail": "Feil e-postadresse", - "core.login.invalidurl": "Ugyldig URL angitt", - "core.login.login": "Logg inn", - "core.login.loginsteps": "Hei! For å få full tilgang til dette nettstedet må du først opprette en brukerkonto.", - "core.login.missingemail": "Mangler e-postadresse", - "core.login.missingfirstname": "Mangler fornavn", - "core.login.missinglastname": "Mangler etternavn", - "core.login.mustconfirm": "Du må godkjenne brukerkontoen din", - "core.login.newaccount": "Ny brukerkonto", - "core.login.password": "Passord", - "core.login.passwordforgotten": "Glemt passord", - "core.login.passwordforgotteninstructions2": "For å tilbakestille passordet må du fylle inn ENTEN brukernavnet ELLER epostadressen ditt.\n\nDersom vi finner deg i databasen får du en e-post med instruksjoner om hvordan du skal få logget inn.\n\nFår du ingen e-post innen rimelig tid betyr det at e-informasjonen du skrev inn var feil. Har du flere e-postadresser bør du sjekke alle før du kontakter Support.", - "core.login.policyaccept": "Jeg forstår og godtar", - "core.login.policyagree": "Du må godta avtalen for å bruke dette nettstedet. Godtar du den?", - "core.login.policyagreement": "Avtale for bruk av portalen", - "core.login.policyagreementclick": "Klikk her for å lese bruksavtalen", - "core.login.potentialidps": "Klikk på knappen for å logge inn:", - "core.login.profileinvaliddata": "Ugyldig verdi", - "core.login.resendemail": "Send epost på nytt", - "core.login.security_question": "Sikkerhetsspørmål", - "core.login.selectacountry": "Velg et land", - "core.login.startsignup": "Registrer deg", - "core.login.supplyinfo": "Fortell litt om deg selv", - "core.login.username": "Brukernavn", - "core.login.usernameoremail": "Enten brukernavn eller e-postadresse", - "core.login.usernotaddederror": "Bruker ble ikke lagt til - ukjent feil", - "core.mainmenu.help": "Hjelp", - "core.mainmenu.logout": "Logg ut", - "core.maxsizeandattachments": "Maks størrelse for nye filer: {{$a.size}}, maks antall vedlegg: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Forskjellig", - "core.mod_assign": "Innlevering", - "core.mod_assignment": "Innlevering 2.2 (Deaktivert)", - "core.mod_book": "Bok", - "core.mod_chat": "Nettprat", - "core.mod_choice": "Gallup", - "core.mod_data": "Database", - "core.mod_database": "Database", - "core.mod_external-tool": "LTI-verktøy", - "core.mod_feedback": "Feedback", - "core.mod_file": "Fil", - "core.mod_folder": "Mappe", - "core.mod_forum": "Forum", - "core.mod_glossary": "Ordbok", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS Innholdspakke", - "core.mod_imscp": "IMS Innholdspakke", - "core.mod_label": "Informasjonsfelt", - "core.mod_lesson": "Leksjon", - "core.mod_lti": "LTI-verktøy", - "core.mod_page": "Side", - "core.mod_quiz": "Quiz", - "core.mod_resource": "Ressurs", - "core.mod_scorm": "SCORM-pakke", - "core.mod_survey": "Undersøkelse", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Workshop", - "core.moduleintro": "Beskrivelse", - "core.more": "mer", - "core.mygroups": "Mine grupper", - "core.name": "Navn", - "core.never": "Aldri", - "core.next": "Neste", - "core.no": "Nei", - "core.nocomments": "Ingen kommentarer", - "core.nograde": "Ingen karakter", - "core.none": "Ingen", - "core.nopermissions": "Beklager, men du har ikke rettighet til å gjøre dette ({{$a}})", - "core.noresults": "Ingen resultater", - "core.noselection": "Ingen valgt", - "core.notenrolledprofile": "Denne profilen er ikke tilgjengelig siden aktuell bruker ikke er påmeldt i dette kurset.", - "core.notice": "Merknad", - "core.notingroup": "Beklager, du må være med i en gruppe for å se denne siden.", - "core.now": "nå", - "core.numwords": "{{$a}} ord", - "core.offline": "Ikke på nett", - "core.ok": "OK", - "core.online": "På nett", - "core.othergroups": "Andre grupper", - "core.pagea": "Side {{$a}}", - "core.paymentinstant": "Bruk knappen under for å betale og melde deg på kurset.", - "core.phone": "Telefon", - "core.pictureof": "Bilde av {{$a}}", - "core.previous": "Forrige", - "core.proceed": "Fortsett", - "core.question.answer": "Svar", - "core.question.answersaved": "Svar lagret", - "core.question.certainty": "Grad av sikkerhet", - "core.question.complete": "Fullført", - "core.question.correct": "Riktig", - "core.question.feedback": "Tilbakemelding", - "core.question.incorrect": "Feil", - "core.question.information": "Informasjon", - "core.question.invalidanswer": "Ufullstendig svar", - "core.question.notanswered": "Ikke besvart", - "core.question.notyetanswered": "Ikke besvart ennå", - "core.question.partiallycorrect": "Delvis korrekt", - "core.question.questionno": "Spørsmål {{$a}}", - "core.question.requiresgrading": "Karaktersetting påkrevet", - "core.quotausage": "Du har brukt {{$a.used}} av din totale grense på {{$a.limit}}", - "core.rating.aggregateavg": "Gjennomsnittlig vurdering", - "core.rating.aggregatecount": "Antall vurderinger", - "core.rating.aggregatemax": "Maksimum vurdering", - "core.rating.aggregatemin": "Minimum vurdering", - "core.rating.aggregatesum": "Sum av vurderinger", - "core.rating.noratings": "Ingen vurderinger innsendt", - "core.rating.rating": "Vurdering", - "core.rating.ratings": "Vurderinger", - "core.refresh": "Oppdatér", - "core.remove": "Fjern", - "core.required": "Obligatorisk", - "core.resourcedisplayopen": "Åpne", - "core.resources": "Ressurser", - "core.restore": "Gjenoppretting", - "core.restricted": "Begrenset", - "core.save": "Lagre", - "core.savechanges": "Lagre endringer", - "core.search": "Søk", - "core.searchresults": "Søkeresultater", - "core.sec": "sek", - "core.secs": "sek", - "core.seemoredetail": "Klikk her for detaljer", - "core.selectacategory": "Vennligst velg en kategori", - "core.selectacourse": "Velg et kurs", - "core.selectagroup": "Velg en gruppe", - "core.send": "Send", - "core.sending": "Sender", - "core.serverconnection": "Feil ved tilkobling til server", - "core.settings.about": "Om", - "core.settings.currentlanguage": "Gjeldende språk", - "core.settings.debugdisplay": "Vis debug meldinger", - "core.settings.disableall": "Deaktiver varslinger", - "core.settings.disabled": "Deaktivert", - "core.settings.general": "Generell", - "core.settings.language": "Språk", - "core.settings.license": "(C) Lisens", - "core.settings.locked": "Låst", - "core.settings.loggedin": "Innlogget", - "core.settings.loggedoff": "Utlogget", - "core.settings.preferences": "Preferanser", - "core.settings.settings": "Innstillinger", - "core.settings.sites": "Nettsteder", - "core.settings.total": "Totalt", - "core.show": "Vis", - "core.showless": "Vis mindre...", - "core.showmore": "Vis mer...", - "core.site": "Nettsted", - "core.sitehome.sitehome": "Portalens startside", - "core.sitehome.sitenews": "Portalnyheter", - "core.sitemaintenance": "Nettstedet er midlertidig nede p.g.a. vedlikehold", - "core.sizeb": "bytes", - "core.sizegb": "Gb", - "core.sizekb": "Kb", - "core.sizemb": "Mb", - "core.skip": "Hopp over", - "core.sort": "Sorter", - "core.sortby": "Sorter etter", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Send", - "core.success": "Vellykket", - "core.tag.defautltagcoll": "Standardsamling", - "core.tag.inalltagcoll": "Over alt", - "core.tag.itemstaggedwith": "{{$a.tagarea}} tagget med \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Ikke noe resultat for \"{{$a}}\"", - "core.tag.notagsfound": "Fant ingen tagger som lignet på \"{{$a}}\"", - "core.tag.searchtags": "Søk i tagger", - "core.tag.showingfirsttags": "Viser {{$a}} mest populære tagger", - "core.tag.tag": "Tagg", - "core.tag.tagarea_course": "Kurs", - "core.tag.tagarea_course_modules": "Aktiviteter og ressurser", - "core.tag.tagarea_post": "Blogginnlegg", - "core.tag.tagarea_user": "Interesser", - "core.tag.tags": "Tagger", - "core.teachers": "Lærere", - "core.thisdirection": "ltr", - "core.time": "Tid", - "core.timesup": "Tida er over!", - "core.today": "I dag", - "core.unlimited": "Ubegrenset", - "core.upgraderunning": "Portalen oppgraderes, vennligst prøv igjen senere.", - "core.user": "Bruker", - "core.user.address": "Adresse", - "core.user.city": "Sted", - "core.user.contact": "Kontakt", - "core.user.country": "Land", - "core.user.description": "Beskrivelse", - "core.user.details": "Detaljer", - "core.user.editingteacher": "Lærer", - "core.user.email": "E-postadresse", - "core.user.emailagain": "E-post (igjen)", - "core.user.firstname": "Fornavn", - "core.user.interests": "Interesser", - "core.user.lastname": "Etternavn", - "core.user.manager": "Superbruker", - "core.user.newpicture": "Nytt bilde", - "core.user.noparticipants": "Fant ingen deltakere for dette kurset", - "core.user.participants": "Deltagere", - "core.user.phone1": "Telefon", - "core.user.phone2": "Mobiltelefon", - "core.user.roles": "Roller", - "core.user.student": "Student", - "core.user.teacher": "Lærer uten editor-rettigheter", - "core.user.webpage": "Nettside", - "core.userdeleted": "Denne brukeren er slettet", - "core.userdetails": "Brukerdetaljer", - "core.usernotfullysetup": "Bruker ikke ferdig satt opp", - "core.users": "Brukere", - "core.view": "Visning", - "core.viewprofile": "Vis profilen", - "core.whatisyourage": "Hvor gammel er du?", - "core.wheredoyoulive": "I hvilket land bor du?", - "core.whyisthisrequired": "Hvorfor er dette påkrevd?", - "core.year": "år", - "core.years": "år", - "core.yes": "Ja" -} \ No newline at end of file diff --git a/src/assets/lang/pl.json b/src/assets/lang/pl.json deleted file mode 100644 index 23497602e..000000000 --- a/src/assets/lang/pl.json +++ /dev/null @@ -1,1723 +0,0 @@ -{ - "addon.badges.alignment": "Wyrównanie", - "addon.badges.badgedetails": "Szczegóły odznaki", - "addon.badges.badges": "Odznaki", - "addon.badges.bendorsement": "Adnotacja", - "addon.badges.claimcomment": "Komentarz do zatwierdzenia", - "addon.badges.claimid": "URL wniosku", - "addon.badges.contact": "Kontakt", - "addon.badges.dateawarded": "Data wydania", - "addon.badges.expired": "Wygasła", - "addon.badges.expirydate": "Data ważności", - "addon.badges.imageauthoremail": "E-mail autora grafiki", - "addon.badges.imageauthorname": "Imię i nazwisko autora grafiki", - "addon.badges.imageauthorurl": "Adres URL autora grafiki", - "addon.badges.imagecaption": "Podpis grafiki", - "addon.badges.issuancedetails": "Wygaśnięcie odznaki", - "addon.badges.issuerdetails": "Dane wystawcy", - "addon.badges.issueremail": "E-mail", - "addon.badges.issuername": "Nazwa wydawcy", - "addon.badges.issuerurl": "URL wydawcy", - "addon.badges.language": "Język", - "addon.badges.noalignment": "Ta odznaka nie ma żadnych zewnętrznych umiejętności ani standardów.", - "addon.badges.nobadges": "Brak dostępnych odznak", - "addon.badges.norelated": "Ta odznaka nie ma żadnych powiązanych odznak.", - "addon.badges.recipientdetails": "Dane odbiorcy", - "addon.badges.relatedbages": "Powiązane odznaki", - "addon.badges.version": "Wersja", - "addon.badges.warnexpired": "(Ta odznaka wygasła!)", - "addon.block_activitymodules.pluginname": "Aktywności", - "addon.block_activityresults.pluginname": "Rezultaty aktywności", - "addon.block_badges.pluginname": "Ostatnie odznaki", - "addon.block_blogmenu.pluginname": "Menu blogu", - "addon.block_blogrecent.pluginname": "Ostatnie wpisy w blogu", - "addon.block_blogtags.pluginname": "Tagi blogu", - "addon.block_calendarmonth.pluginname": "Kalendarz", - "addon.block_calendarupcoming.pluginname": "Nadchodzące wydarzenia", - "addon.block_comments.pluginname": "Komentarze", - "addon.block_completionstatus.pluginname": "Status ukończenia kursu", - "addon.block_glossaryrandom.pluginname": "Losowe pojęcie ze słownika", - "addon.block_learningplans.pluginname": "Plany nauki", - "addon.block_myoverview.all": "Wszystkie (bez schowanych)", - "addon.block_myoverview.allincludinghidden": "Wszystkie", - "addon.block_myoverview.favourites": "Oznaczone gwiazdką", - "addon.block_myoverview.future": "Nadchodzące", - "addon.block_myoverview.hiddencourses": "Schowane", - "addon.block_myoverview.inprogress": "Aktualne", - "addon.block_myoverview.lastaccessed": "Ostatnio przeglądane", - "addon.block_myoverview.morecourses": "Więcej kursów", - "addon.block_myoverview.nocourses": "Brak kursów", - "addon.block_myoverview.past": "Zakończone", - "addon.block_myoverview.pluginname": "Przegląd kursów", - "addon.block_myoverview.title": "Nazwa kursu", - "addon.block_newsitems.pluginname": "Najnowsze ogłoszenia", - "addon.block_onlineusers.pluginname": "Użytkownicy online", - "addon.block_privatefiles.pluginname": "Prywatne pliki", - "addon.block_recentactivity.pluginname": "Co się ostatnio działo?", - "addon.block_recentlyaccessedcourses.nocourses": "Brak ostatnich kursów", - "addon.block_recentlyaccessedcourses.pluginname": "Ostatnio przeglądane kursy", - "addon.block_recentlyaccesseditems.noitems": "Brak ostatnich elementów", - "addon.block_recentlyaccesseditems.pluginname": "Ostatnio przeglądane elementy", - "addon.block_rssclient.pluginname": "Klient RSS", - "addon.block_selfcompletion.pluginname": "Ukończenie ręczne", - "addon.block_sitemainmenu.pluginname": "Menu główne", - "addon.block_starredcourses.nocourses": "Brak kursów oznaczonych gwiazdką", - "addon.block_starredcourses.pluginname": "Kursy oznaczone gwiazdką", - "addon.block_tags.pluginname": "Tagi", - "addon.block_timeline.duedate": "Termin oddania", - "addon.block_timeline.next30days": "Kolejne 30 dni", - "addon.block_timeline.next3months": "Kolejne 3 miesiące", - "addon.block_timeline.next6months": "Kolejne 6 miesięcy", - "addon.block_timeline.next7days": "Kolejne 7 dni", - "addon.block_timeline.nocoursesinprogress": "Brak aktualnych kursów", - "addon.block_timeline.noevents": "Brak aktywności z terminem oddania", - "addon.block_timeline.overdue": "Zaległe", - "addon.block_timeline.pluginname": "Oś czasu", - "addon.block_timeline.sortbycourses": "Sortuj wg kursów", - "addon.block_timeline.sortbydates": "Sortuj wg dat", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Wpisy w blogu", - "addon.blog.linktooriginalentry": "Link do oryginalnego wpisu na blogu", - "addon.blog.noentriesyet": "Brak widocznych wpisów w tym miejscu", - "addon.blog.publishtonoone": "Wpis prywatny (szkic)", - "addon.blog.publishtosite": "Wpis widoczny dla wszystkich", - "addon.blog.publishtoworld": "Wpis widoczny dla wszystkich na świecie", - "addon.blog.showonlyyourentries": "Pokaż tylko swoje wpisy", - "addon.blog.siteblogheading": "Strona blogu", - "addon.calendar.allday": "Wszystkie dni", - "addon.calendar.calendar": "Kalendarz", - "addon.calendar.calendarevents": "Wydarzenia w kalendarzu", - "addon.calendar.calendarreminders": "Przypomnienia kalendarza", - "addon.calendar.categoryevents": "Kategoria wydarzeń", - "addon.calendar.confirmeventdelete": "Czy jesteś pewien, że chcesz usunąć wydarzenie: \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "Termin \"{{$a.name}}\" jest częścią serii. Czy chcesz usunąć tylko ten termin, czy wszystkie {{$a.count}} terminy w tej serii?", - "addon.calendar.courseevents": "Terminy kursu", - "addon.calendar.daynext": "Następny dzień", - "addon.calendar.dayprev": "Poprzedni dzień", - "addon.calendar.defaultnotificationtime": "Domyślny czas powiadomienia", - "addon.calendar.deleteallevents": "Usuń wszystkie wydarzenia", - "addon.calendar.deleteevent": "Usuń termin", - "addon.calendar.deleteoneevent": "Usuń to wydarzenie", - "addon.calendar.durationminutes": "Czas w minutach", - "addon.calendar.durationnone": "Bez okresu trwania", - "addon.calendar.durationuntil": "Aż do", - "addon.calendar.editevent": "Edycja terminu", - "addon.calendar.eventcalendareventdeleted": "Usunięto wydarzenie w kalendarzu", - "addon.calendar.eventduration": "Okres", - "addon.calendar.eventendtime": "Data końcowa", - "addon.calendar.eventkind": "Typ wydarzenia", - "addon.calendar.eventname": "Nazwa wydarzenia", - "addon.calendar.eventstarttime": "Czas początkowy", - "addon.calendar.eventtype": "Typ wydarzenia", - "addon.calendar.fri": "Pi.", - "addon.calendar.friday": "Piątek", - "addon.calendar.gotoactivity": "Idź do aktywności", - "addon.calendar.groupevents": "Terminy grupowe", - "addon.calendar.invalidtimedurationminutes": "Wprowadzony czas trwania w minutach jest niepoprawny. Wprowadź czas trwania w minutach dłuższy niż 0 lub wskaż brak czasu trwania.", - "addon.calendar.invalidtimedurationuntil": "Data i czas końca okresu są wcześniejsze niż początku. Popraw to zanim kontynuujesz.", - "addon.calendar.mon": "Pn.", - "addon.calendar.monday": "Poniedziałek", - "addon.calendar.monthlyview": "Widok miesiąca", - "addon.calendar.newevent": "Nowe wydarzenie", - "addon.calendar.noevents": "Brak wydarzeń", - "addon.calendar.nopermissiontoupdatecalendar": "Przepraszamy, ale obecnie nie masz uprawnień do aktualizacji wydarzenia w kalendarzu", - "addon.calendar.reminders": "Przypomnienie", - "addon.calendar.repeatedevents": "Powtórzone wydarzenia", - "addon.calendar.repeateditall": "Zachowaj zmiany dla wszystkich {{$a}} terminów", - "addon.calendar.repeateditthis": "Zachowaj zmiany", - "addon.calendar.repeatevent": "Powtórz to wydarzenie", - "addon.calendar.repeatweeksl": "Powtórz co tydzień", - "addon.calendar.sat": "So.", - "addon.calendar.saturday": "Sobota", - "addon.calendar.siteevents": "Wydarzenia strony", - "addon.calendar.sun": "Ni.", - "addon.calendar.sunday": "Niedziela", - "addon.calendar.thu": "Cz.", - "addon.calendar.thursday": "Czwartek", - "addon.calendar.today": "Dziś", - "addon.calendar.tomorrow": "Jutro", - "addon.calendar.tue": "Wt.", - "addon.calendar.tuesday": "Wtorek", - "addon.calendar.typecategory": "Kategoria wydarzenia", - "addon.calendar.typeclose": "Zamknij termin", - "addon.calendar.typecourse": "Termin kursu", - "addon.calendar.typedue": "Nadchodzący termin", - "addon.calendar.typegradingdue": "Termin oceny zadania", - "addon.calendar.typegroup": "Termin grupowy", - "addon.calendar.typeopen": "Otwórz termin", - "addon.calendar.typesite": "Termin strony", - "addon.calendar.typeuser": "Termin użytkownika", - "addon.calendar.upcomingevents": "Nadchodzące wydarzenia", - "addon.calendar.userevents": "Terminy użytkownika", - "addon.calendar.wed": "Śr.", - "addon.calendar.wednesday": "Środa", - "addon.calendar.when": "Kiedy", - "addon.calendar.yesterday": "Wczoraj", - "addon.competency.activities": "Aktywności", - "addon.competency.competencies": "Kompetencje", - "addon.competency.coursecompetencies": "Kompetencje kursu", - "addon.competency.duedate": "Termin", - "addon.competency.evidence": "Dowód", - "addon.competency.evidence_competencyrule": "Zasada kompetencji została spełniona.", - "addon.competency.evidence_coursecompleted": "Kurs '{{$a}}' został ukończony.", - "addon.competency.evidence_coursemodulecompleted": "Aktywność '{{$a}}' została ukończona.", - "addon.competency.evidence_courserestored": "Ocena została przywrócona wraz z kursem '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Dokumentacja wcześniejszej nauki '{{$a}}' została podłączona.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Dokumentacja wcześniejszej nauki '{{$a}}' została odłączona.", - "addon.competency.evidence_manualoverride": "Ocena kompetencji została ustawiona ręcznie.", - "addon.competency.evidence_manualoverrideincourse": "Ocena kompetencji została ustawiona ręcznie dla kursu '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "Ocena kompetencji została ustawiona ręcznie dla planu uczenia się '{{$a}}'.", - "addon.competency.learningplancompetencies": "Kompetencje planu nauczania", - "addon.competency.learningplans": "Plany nauczania", - "addon.competency.myplans": "Moje plany uczenia się.", - "addon.competency.noactivities": "Brak aktywności", - "addon.competency.nocompetenciesincourse": "Nie połączono żadnych kompetencji do tego kursu.", - "addon.competency.noevidence": "Brak dowodów", - "addon.competency.noplanswerecreated": "Nie utworzono planów uczenia się.", - "addon.competency.nouserplanswithcompetency": "Żaden plan uczenia się nie zawiera tej kompetencji.", - "addon.competency.path": "Ścieżka:", - "addon.competency.planstatusactive": "Aktywne", - "addon.competency.planstatuscomplete": "Ukończone", - "addon.competency.planstatusdraft": "Szkic", - "addon.competency.planstatusinreview": "W trakcie przeglądu", - "addon.competency.planstatuswaitingforreview": "Oczekuje na przegląd", - "addon.competency.progress": "Postęp", - "addon.competency.reviewstatus": "Status przeglądu", - "addon.competency.status": "Status", - "addon.competency.template": "Szablon planu uczenia się.", - "addon.competency.usercompetencystatus_idle": "Bezczynny", - "addon.competency.usercompetencystatus_inreview": "W przeglądzie", - "addon.competency.usercompetencystatus_waitingforreview": "Oczekuje na przegląd", - "addon.competency.userplans": "Plany uczenia się", - "addon.coursecompletion.completecourse": "Zaznacz kurs jako ukończony", - "addon.coursecompletion.completed": "Ukończony", - "addon.coursecompletion.completiondate": "Data ukończenia", - "addon.coursecompletion.completionmenuitem": "Ukończenie", - "addon.coursecompletion.coursecompletion": "Ukończenie kursu", - "addon.coursecompletion.criteria": "Kryteria", - "addon.coursecompletion.criteriagroup": "Grupa kryteriów", - "addon.coursecompletion.criteriarequiredall": "Wszystkie poniższe kryteria są wymagane", - "addon.coursecompletion.criteriarequiredany": "Wszystkie poniższe kryteria są wymagane", - "addon.coursecompletion.inprogress": "W toku", - "addon.coursecompletion.manualselfcompletion": "Samodzielne oznaczenie ukończenia", - "addon.coursecompletion.nottracked": "Obecnie nie jesteś śledzony przez mechanizm śledzenie postępów w tym kursie", - "addon.coursecompletion.notyetstarted": "Jeszcze nie rozpoczęto", - "addon.coursecompletion.pending": "Oczekujący", - "addon.coursecompletion.required": "Wymagane", - "addon.coursecompletion.requiredcriteria": "Wymagane kryteria", - "addon.coursecompletion.requirement": "Wymagania", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Zobacz raport kursu", - "addon.files.files": "Pliki", - "addon.files.privatefiles": "Prywatne pliki", - "addon.files.sitefiles": "Pliki serwisu", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Skonfiguruj urządzenia", - "addon.messages.acceptandaddcontact": "Zaakceptuj i dodaj do kontaktów", - "addon.messages.addcontact": "Dodaj kontakt", - "addon.messages.addcontactconfirm": "Czy na pewno chcesz dodać {{$a}} do swoich kontaktów?", - "addon.messages.addtofavourites": "Oznacz rozmowę gwiazdką", - "addon.messages.addtoyourcontacts": "Dodaj do kontaktów", - "addon.messages.blocknoncontacts": "Blokuj wiadomości od użytkowników spoza listy kontaktów", - "addon.messages.blockuser": "Zablokuj użytkownika", - "addon.messages.blockuserconfirm": "Czy na pewno chcesz zablokować {{$a}}?", - "addon.messages.contactableprivacy": "Akceptuj wiadomości od:", - "addon.messages.contactableprivacy_coursemember": "Moje kontakty i każdy, kto uczestniczy w moich kursach", - "addon.messages.contactableprivacy_onlycontacts": "Tylko moje kontakty", - "addon.messages.contactableprivacy_site": "Każdy na tej stronie", - "addon.messages.contactblocked": "Kontakt zablokowany", - "addon.messages.contactrequestsent": "Wysłano prośbę o kontakt", - "addon.messages.contacts": "Kontakty", - "addon.messages.conversationactions": "Menu rozmowy", - "addon.messages.decline": "Odmów", - "addon.messages.deleteallconfirm": "Czy na pewno chcesz usunąć tę rozmowę? Nie spowoduje to usunięcia jej dla innych uczestników.", - "addon.messages.deleteallselfconfirm": "Czy na pewno chcesz usunąć całą osobistą rozmowę?", - "addon.messages.deleteconversation": "Usuń rozmowę", - "addon.messages.deleteforeveryone": "Usuń dla mnie i dla wszystkich innych", - "addon.messages.deletemessage": "Usuń wiadomość", - "addon.messages.errordeletemessage": "Błąd podczas usuwania wiadomości.", - "addon.messages.groupconversations": "Grupowe", - "addon.messages.groupinfo": "Informacje o grupie", - "addon.messages.individualconversations": "Prywatne", - "addon.messages.info": "Informacje o użytkowniku", - "addon.messages.isnotinyourcontacts": "{{$a}} nie ma w Twoich kontaktach", - "addon.messages.message": "Wiadomość", - "addon.messages.messagepreferences": "Preferencje wiadomości", - "addon.messages.messages": "Wiadomości", - "addon.messages.muteconversation": "Wycisz", - "addon.messages.mutedconversation": "Wyciszona rozmowa", - "addon.messages.newmessage": "Nowa wiadomość", - "addon.messages.newmessages": "Nowe wiadomości", - "addon.messages.nocontactrequests": "Brak próśb o kontakt", - "addon.messages.nocontactsgetstarted": "Brak kontaktów", - "addon.messages.nofavourites": "Brak rozmów oznaczonych gwiazdką", - "addon.messages.nogroupconversations": "Brak rozmów grupowych", - "addon.messages.noindividualconversations": "Brak rozmów prywatnych", - "addon.messages.nomessagesfound": "Brak wiadomości", - "addon.messages.noncontacts": "Spoza kontaktów", - "addon.messages.nousersfound": "Nie znaleziono użytkowników", - "addon.messages.numparticipants": "{{$a}} uczestników", - "addon.messages.removecontact": "Usuń kontakt", - "addon.messages.removecontactconfirm": "Czy na pewno chcesz usunąć {{$a}} ze swoich kontaktów?", - "addon.messages.removefromfavourites": "Usuń gwiazdkę", - "addon.messages.removefromyourcontacts": "Usuń z kontaktów", - "addon.messages.requests": "Prośby", - "addon.messages.requirecontacttomessage": "Musisz poprosić {{$a}}, aby dodał(a) Cię jako kontakt, żeby móc wysłać wiadomość.", - "addon.messages.searchcombined": "Szukaj osób i wiadomości", - "addon.messages.selfconversation": "Przestrzeń osobista", - "addon.messages.selfconversationdefaultmessage": "Zapisz robocze wiadomości, linki, notatki itp., aby uzyskać do nich dostęp później.", - "addon.messages.sendcontactrequest": "Wyślij prośbę o kontakt", - "addon.messages.type_blocked": "Zablokowane", - "addon.messages.type_strangers": "Inne", - "addon.messages.unabletomessage": "Nie możesz wysłać wiadomości do tego użytkownika", - "addon.messages.unblockuser": "Odblokuj użytkownika", - "addon.messages.unblockuserconfirm": "Czy na pewno chcesz odblokować {{$a}}?", - "addon.messages.unmuteconversation": "Wyłącz wyciszenie", - "addon.messages.useentertosend": "Wciśnij Enter aby wysłać", - "addon.messages.userwouldliketocontactyou": "{{$a}} chciał(a)by się z Tobą skontaktować", - "addon.messages.wouldliketocontactyou": "Chciał(a)bym się z Tobą skontaktować", - "addon.messages.you": "Ty:", - "addon.messages.youhaveblockeduser": "Zablokowałeś tego użytkownika", - "addon.messages.yourcontactrequestpending": "Twoja prośba o kontakt oczekuje na {{$a}}", - "addon.mod_assign.addattempt": "Zezwól na kolejną próbę", - "addon.mod_assign.addnewattempt": "Dodaj nową wersję pracy", - "addon.mod_assign.addnewattemptfromprevious": "Dodaj nową wersję pracy na podstawie pracy przesłanej poprzednio", - "addon.mod_assign.addsubmission": "Dodaj zadanie", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Szczegóły zadania oraz możliwość przesłania pracy będą dostępne od {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Zezwól na przesyłanie zadań od", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Odpowiedzi na to zadanie będzie można przesyłać od {{$a}}", - "addon.mod_assign.applytoteam": "Zastosuj oceny i informacje zwrotne dla całej grupy", - "addon.mod_assign.assignmentisdue": "Zadanie ma termin", - "addon.mod_assign.attemptnumber": "Numer próby", - "addon.mod_assign.attemptreopenmethod": "Ponowne otwieranie prób", - "addon.mod_assign.attemptreopenmethod_manual": "Ręcznie", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatycznie do momentu zaliczenia", - "addon.mod_assign.attemptsettings": "Ustawienia próby", - "addon.mod_assign.confirmsubmission": "Czy na pewno chcesz wysłać swoje zadanie do oceny? Nie będziesz mógł po tej operacji dokonać żadnych zmian.", - "addon.mod_assign.currentattempt": "To jest próba nr {{$a}}.", - "addon.mod_assign.currentattemptof": "To jest próba nr {{$a.attemptnumber}} ( liczba dozwolonych prób: {{$a.maxattempts}} ).", - "addon.mod_assign.currentgrade": "Bieżąca ocena w dzienniku ocen", - "addon.mod_assign.cutoffdate": "Ostateczny termin", - "addon.mod_assign.defaultteam": "Domyślna grupa", - "addon.mod_assign.duedate": "Termin oddania", - "addon.mod_assign.duedateno": "Brak terminu oddania", - "addon.mod_assign.duedatereached": "Minął termin oddania tego zadania", - "addon.mod_assign.editingstatus": "Edycja statusu", - "addon.mod_assign.editsubmission": "Edytuj zadanie", - "addon.mod_assign.extensionduedate": "Termin przedłużenia", - "addon.mod_assign.grade": "Ocena", - "addon.mod_assign.graded": "Ocenione", - "addon.mod_assign.gradedby": "Ocenione przez", - "addon.mod_assign.gradedfollowupsubmit": "Oceniona - dodatkowe zgłoszenie dostarczone", - "addon.mod_assign.gradedon": "Ocenione dnia", - "addon.mod_assign.gradelocked": "Ta ocena jest zablokowana lub nadpisana w dzienniku ocen", - "addon.mod_assign.gradeoutof": "Ocena z max. {{$a}}", - "addon.mod_assign.gradingstatus": "Stan oceniania", - "addon.mod_assign.groupsubmissionsettings": "Ustawienia zadań grupowych", - "addon.mod_assign.hiddenuser": "Uczestnik", - "addon.mod_assign.latesubmissions": "Zadania oddane po terminie", - "addon.mod_assign.latesubmissionsaccepted": "Dozwolone aż do {{$a}}", - "addon.mod_assign.markingworkflowstate": "Stan przepływu pracy w ocenianiu", - "addon.mod_assign.markingworkflowstateinmarking": "W trakcie oceniania", - "addon.mod_assign.markingworkflowstateinreview": "W trakcie przeglądu", - "addon.mod_assign.markingworkflowstatenotmarked": "Nie ocenione", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Gotowe do publikacji", - "addon.mod_assign.markingworkflowstatereadyforreview": "Ocenianie zakończone", - "addon.mod_assign.markingworkflowstatereleased": "Ocena opublikowana", - "addon.mod_assign.modulenameplural": "Zadania", - "addon.mod_assign.multipleteams": "Członek więcej niż jednej grupy", - "addon.mod_assign.multipleteams_desc": "To jest zadanie grupowe. Jesteś członkiem więcej niż jednej grupy. By móc przesłać zadanie musisz być członkiem tylko jednej grupy. Skontaktuj się z prowadzącym w celu zmiany przynależności do grup.", - "addon.mod_assign.noattempt": "Nie próbowano", - "addon.mod_assign.nomoresubmissionsaccepted": "Żadne rozwiązania nie będą akceptowane", - "addon.mod_assign.noonlinesubmissions": "To zadanie nie wymaga wysyłania niczego online", - "addon.mod_assign.nosubmission": "Żadne rozwiązanie nie zostało przesłane do tego zadania", - "addon.mod_assign.noteam": "Nie jesteś członkiem żadnej grupy", - "addon.mod_assign.noteam_desc": "To jest zadanie grupowe. Nie jesteś członkiem żadnej grupy, więc nie możesz przesłać rozwiązania. Skontaktuj się z prowadzącym w celu dodania do grupy.", - "addon.mod_assign.notgraded": "Nie ocenione", - "addon.mod_assign.numberofdraftsubmissions": "Wersja robocza", - "addon.mod_assign.numberofparticipants": "Uczestnicy", - "addon.mod_assign.numberofsubmissionsneedgrading": "Wymaga oceny", - "addon.mod_assign.numberofsubmittedassignments": "Nadesłane", - "addon.mod_assign.numberofteams": "Grupy", - "addon.mod_assign.numwords": "{{$a}} słów", - "addon.mod_assign.outof": "{{$a.current}} z {{$a.total}}", - "addon.mod_assign.overdue": "Opóźnienie w przesłaniu: {{$a}}", - "addon.mod_assign.submission": "Oddane zadanie", - "addon.mod_assign.submissioneditable": "Student może edytować to zadanie", - "addon.mod_assign.submissionnoteditable": "Student nie może edytować tego zadania", - "addon.mod_assign.submissionslocked": "To zadanie nie akceptuje przesyłania rozwiązań", - "addon.mod_assign.submissionstatus": "Status przesłanego zadania", - "addon.mod_assign.submissionstatus_": "Brak przesłanego zadania", - "addon.mod_assign.submissionstatus_draft": "Wersja robocza", - "addon.mod_assign.submissionstatus_marked": "Ocenione", - "addon.mod_assign.submissionstatus_new": "Brak zadania", - "addon.mod_assign.submissionstatus_reopened": "Ponownie otwarte", - "addon.mod_assign.submissionstatus_submitted": "Przesłane do oceny", - "addon.mod_assign.submissionstatusheading": "Status przesłanego zadania", - "addon.mod_assign.submissionteam": "Grupa", - "addon.mod_assign.submitassignment": "Zgłoś zadanie", - "addon.mod_assign.submitassignment_help": "Po zgłoszeniu tego zadania, nie będzie można wprowadzić zmian.", - "addon.mod_assign.submittedearly": "Zadanie zostało złożone {{$a}} przed terminem", - "addon.mod_assign.submittedlate": "Zadanie zostało złożone {{$a}} po terminie", - "addon.mod_assign.timemodified": "Ostatnio modyfikowane", - "addon.mod_assign.timeremaining": "Pozostały czas", - "addon.mod_assign.ungroupedusers": "Włączona jest opcja: \"Wymaga grup do przesłania zadania\"; użytkownicy, którzy nie są członkami żadnej grupy lub są członkami więcej niż jednej grupy nie mogą przesłać zadania.", - "addon.mod_assign.unlimitedattempts": "Nieograniczony", - "addon.mod_assign.userswhoneedtosubmit": "Użytkownicy, którzy muszą przesłać zadanie: {{$a}}", - "addon.mod_assign.userwithid": "Użytkownik o identyfikatorze {{id}}", - "addon.mod_assign.viewsubmission": "Wyświetl pracę domową", - "addon.mod_assign.wordlimit": "Limit słów", - "addon.mod_assign_feedback_comments.pluginname": "Komentarz zwrotny", - "addon.mod_assign_feedback_editpdf.pluginname": "Adnotacje PDF", - "addon.mod_assign_feedback_file.pluginname": "Plik z komentarzem zwrotnym", - "addon.mod_assign_submission_comments.pluginname": "Komentarz do przesłanego zadania", - "addon.mod_assign_submission_file.pluginname": "Przesyłane pliki", - "addon.mod_assign_submission_onlinetext.pluginname": "Tekst on-line", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Pole Tekst online dla tego zadania ma wyznaczony limit {{$a.limit}} wpisanych słów. Próbujesz wpisać {{$a.count}} słów. Sprawdź swój wpis i spróbuj ponownie.", - "addon.mod_book.errorchapter": "Błąd podczas odczytu książki", - "addon.mod_book.modulenameplural": "Książki", - "addon.mod_book.navnexttitle": "Następne: {{$a}}", - "addon.mod_book.navprevtitle": "Poprzednie: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Rozdziały książki", - "addon.mod_book.toc": "Spis treści", - "addon.mod_chat.beep": "rozmawia", - "addon.mod_chat.chatreport": "Sesje czatu", - "addon.mod_chat.currentusers": "Bieżący użytkownicy", - "addon.mod_chat.enterchat": "Naciśnij tu, aby wejść na czat", - "addon.mod_chat.entermessage": "Wprowadź wiadomość", - "addon.mod_chat.messagebeepseveryone": "{{$a}} rozmawia ze wszstkimi", - "addon.mod_chat.messagebeepsyou": "{{$a}} właśnie do Ciebie napisał", - "addon.mod_chat.messageenter": "{{$a}} właśnie wszedł na czat", - "addon.mod_chat.messageexit": "{{$a}} opuścił czat", - "addon.mod_chat.messages": "Wiadomości", - "addon.mod_chat.messageyoubeep": "Zostałeś wypikany {{$a}}", - "addon.mod_chat.modulenameplural": "Czaty", - "addon.mod_chat.mustbeonlinetosendmessages": "Musisz być online, aby wysyłać wiadomości.", - "addon.mod_chat.nomessages": "Brak wiadomości", - "addon.mod_chat.saidto": "powiedział do", - "addon.mod_chat.send": "Wyślij", - "addon.mod_chat.sessionstart": "Następna sesja czatu rozpocznie się {{a->date}}, ({{$a.fromnow}} od teraz)", - "addon.mod_chat.talk": "Dyskusja", - "addon.mod_chat.viewreport": "Zobacz minione sesje czat", - "addon.mod_choice.cannotsubmit": "Wystąpił problem podczas zatwierdzania twojego wyboru. Proszę spróbuj ponownie.", - "addon.mod_choice.choiceoptions": "Opcje wyboru odpowiedzi", - "addon.mod_choice.expired": "Niestety ta aktywność została zamknięta {{$a}} i nie jest już dostępna.", - "addon.mod_choice.full": "(pełne)", - "addon.mod_choice.modulenameplural": "Głosowania", - "addon.mod_choice.noresultsviewable": "Nie ma wglądu w wyniki głosowania.", - "addon.mod_choice.notopenyet": "Ta aktywność jest niedostępna do {{$a}}.", - "addon.mod_choice.numberofuser": "Liczba użytkowników", - "addon.mod_choice.numberofuserinpercentage": "Procent odpowiedzi", - "addon.mod_choice.previewonly": "Jest to tylko podgląd dostępnych opcji dla tej aktywności. Nie możesz dokonać wyboru do {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anonimowe wyniki zostaną opublikowane po udzieleniu odpowiedzi.", - "addon.mod_choice.publishinfoanonclose": "Anonimowe wyniki zostaną opublikowane po zamknięciu aktywności.", - "addon.mod_choice.publishinfofullafter": "Pełne wyniki, pokazujące wybory wszystkich użytkowników, zostaną opublikowane po udzieleniu odpowiedzi.", - "addon.mod_choice.publishinfofullclose": "Pełne wyniki, pokazujące wybory wszystkich użytkowników, zostaną opublikowane po zamknięciu aktywności.", - "addon.mod_choice.publishinfonever": "Wyniki tej aktywności nie zostaną opublikowane po udzieleniu odpowiedzi.", - "addon.mod_choice.removemychoice": "Usuń mój wybór", - "addon.mod_choice.responses": "Odpowiedzi", - "addon.mod_choice.responsesresultgraphheader": "Wyświetl wykres", - "addon.mod_choice.savemychoice": "Zapisz mój wybór", - "addon.mod_choice.userchoosethisoption": "Użytkownicy, którzy wybrali poszczególne opcje.", - "addon.mod_choice.yourselection": "Twój wybór", - "addon.mod_data.addentries": "Dodaj wpisy", - "addon.mod_data.advancedsearch": "Wyszukiwanie zaawansowane", - "addon.mod_data.alttext": "Alternatywny tekst", - "addon.mod_data.approve": "Zatwierdź", - "addon.mod_data.approved": "Zatwierdzony", - "addon.mod_data.ascending": "Rosnąco", - "addon.mod_data.authorfirstname": "Imię autora", - "addon.mod_data.authorlastname": "Nazwisko autora", - "addon.mod_data.confirmdeleterecord": "Na pewno chcesz usunąć ten wpis?", - "addon.mod_data.descending": "Malejąco", - "addon.mod_data.disapprove": "Cofnij zatwierdzenie", - "addon.mod_data.emptyaddform": "Nie wypełniłeś wszystkich pól", - "addon.mod_data.entrieslefttoadd": "Musisz dodać {{$a.entriesleft}} więcej wpisów, aby zakończyć tą aktywność", - "addon.mod_data.entrieslefttoaddtoview": "Musisz dodać {{$a.entrieslefttoview}} więcej wpisów, aby móc wyświetlać wpisy innych użytkowników.", - "addon.mod_data.errordeleting": "Błąd podczas usuwania wpisu.", - "addon.mod_data.errormustsupplyvalue": "Musisz podać wartość tutaj.", - "addon.mod_data.expired": "Niestety, ta aktywność została zamknięta {{$a}} i nie jest już dłużej dostępna", - "addon.mod_data.fields": "Pola", - "addon.mod_data.foundrecords": "Znaleziono rekordy: {{$a.num}}/{{$a.max}} (Reset filtrów)", - "addon.mod_data.latlongboth": "Wymagana jest zarówno szerokość i długość geograficzna.", - "addon.mod_data.menuchoose": "Wybierz...", - "addon.mod_data.modulenameplural": "Bazy danych", - "addon.mod_data.more": "Więcej", - "addon.mod_data.nomatch": "Żaden wpis nie został znaleziony.", - "addon.mod_data.norecords": "Brak wpisów w bazie danych", - "addon.mod_data.notapproved": "Wpis nie jest jeszcze zatwierdzony.", - "addon.mod_data.notopenyet": "Niestety, ta aktywność jest niedostępna aż do {{$a}}", - "addon.mod_data.numrecords": "{{$a}} wpisów", - "addon.mod_data.other": "Inne", - "addon.mod_data.recordapproved": "Wpis zatwierdzony", - "addon.mod_data.recorddeleted": "Wpis usunięty", - "addon.mod_data.recorddisapproved": "Wpis odrzucony", - "addon.mod_data.resetsettings": "Resetuj pola", - "addon.mod_data.search": "Szukaj", - "addon.mod_data.selectedrequired": "Wszystkie zaznaczone są wymagane", - "addon.mod_data.single": "Pokaż pojedynczo", - "addon.mod_data.tagarea_data_records": "Rekordy danych", - "addon.mod_data.timeadded": "Dodano czas", - "addon.mod_data.timemodified": "Zmodyfikowano czas", - "addon.mod_data.usedate": "Uwzględnij w wyszukiwaniu.", - "addon.mod_feedback.analysis": "Analiza", - "addon.mod_feedback.anonymous": "Anonimowy", - "addon.mod_feedback.anonymous_entries": "Anonimowe wpisy", - "addon.mod_feedback.average": "Średnia", - "addon.mod_feedback.complete_the_form": "Odpowiedz na pytania ...", - "addon.mod_feedback.completed_feedbacks": "Zatwierdzone odpowiedzi", - "addon.mod_feedback.continue_the_form": "Kontynuuj wypełnianie formularza", - "addon.mod_feedback.feedback_is_not_open": "Informacja zwrotna nie jest otwarta", - "addon.mod_feedback.feedbackclose": "Zamknij o", - "addon.mod_feedback.feedbackopen": "Otwórz o", - "addon.mod_feedback.mapcourses": "Skojarz formularz opinii z kursami", - "addon.mod_feedback.maximal": "maksymalny", - "addon.mod_feedback.minimal": "minimum", - "addon.mod_feedback.mode": "Tryb", - "addon.mod_feedback.modulenameplural": "Opinie zwrotne", - "addon.mod_feedback.next_page": "Następna strona", - "addon.mod_feedback.non_anonymous": "Nazwa użytkownika będzie zapamiętywana i prezentowana z odpowiedziami", - "addon.mod_feedback.non_anonymous_entries": "Pozycje nie anonimowe", - "addon.mod_feedback.non_respondents_students": "Studenci niebędący respondentami*", - "addon.mod_feedback.not_selected": "Nie wybrano", - "addon.mod_feedback.not_started": "nie rozpoczęto", - "addon.mod_feedback.numberoutofrange": "Liczba poza zakresem", - "addon.mod_feedback.overview": "Przegląd", - "addon.mod_feedback.page_after_submit": "Wyświetl stronę po zatwierdzeniu", - "addon.mod_feedback.preview": "Podgląd", - "addon.mod_feedback.previous_page": "Poprzednia strona\n", - "addon.mod_feedback.questions": "Pytania", - "addon.mod_feedback.response_nr": "Liczba odpowiedzi", - "addon.mod_feedback.responses": "Odpowiedzi", - "addon.mod_feedback.save_entries": "Zatwierdź swoją odpowiedź", - "addon.mod_feedback.show_entries": "Pokaż odpowiedzi", - "addon.mod_feedback.show_nonrespondents": "Pokaż kto nie udzielił opinii*", - "addon.mod_feedback.started": "Rozpoczęto", - "addon.mod_feedback.this_feedback_is_already_submitted": "Już zakończyłeś wypełnianie tej aktywności.", - "addon.mod_folder.emptyfilelist": "Brak plików do wyświetlenia.", - "addon.mod_folder.modulenameplural": "Foldery", - "addon.mod_forum.addanewdiscussion": "Dodaj nowy temat dyskusji", - "addon.mod_forum.addanewquestion": "Dodaj nowe pytanie", - "addon.mod_forum.addanewtopic": "Dodaj nowy temat", - "addon.mod_forum.addtofavourites": "Dodaj gwiazdkę do tej dyskusji", - "addon.mod_forum.advanced": "Zaawansowane", - "addon.mod_forum.cannotadddiscussion": "Musisz być członkiem grupy aby dodać dyskusję do tego forum", - "addon.mod_forum.cannotadddiscussionall": "Nie masz uprawnień, aby dodać nową dyskusję dla wszystkich uczestników.", - "addon.mod_forum.cannotcreatediscussion": "Nie można utworzyć nowego wątku", - "addon.mod_forum.couldnotadd": "Nie można dodać twojego wpisu z powodu nieznanego błędu", - "addon.mod_forum.couldnotupdate": "Nie można zaktualizować twojego wpisu z powodu nieznanego błędu", - "addon.mod_forum.cutoffdatereached": "Termin dodawania wpisów na tym forum się zakończył, wiec nie możesz już dodawać tutaj wpisów.", - "addon.mod_forum.delete": "Usuń", - "addon.mod_forum.deletedpost": "Wpis został usunięty", - "addon.mod_forum.deletesure": "Czy masz pewność, że chcesz usunąć ten wpis?", - "addon.mod_forum.discussion": "Temat", - "addon.mod_forum.discussionlistsortbycreatedasc": "Segreguj według daty utworzenia w porządku rosnącym", - "addon.mod_forum.discussionlistsortbycreateddesc": "Segreguj według daty utworzenia w porządku malejącym", - "addon.mod_forum.discussionlistsortbylastpostasc": "Segreguj według daty utworzenia ostatniego wpisu w porządku rosnącym", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Segreguj według daty utworzenia ostatniego wpisu w porządku malejącym", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Segreguj według liczby odpowiedzi w porządku rosnącym", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Segreguj według liczby odpowiedzi w porządku malejącym", - "addon.mod_forum.discussionlocked": "Dyskusja została zablokowana, więc nie możesz już odpowiedzieć.", - "addon.mod_forum.discussionpinned": "Przypięta", - "addon.mod_forum.discussionsubscription": "Subskrypcja dyskusji", - "addon.mod_forum.edit": "Edycja", - "addon.mod_forum.erroremptymessage": "Wiadomość nie może być pusta", - "addon.mod_forum.erroremptysubject": "Tytuł wpisu nie może być pusty.", - "addon.mod_forum.favouriteupdated": "Twoja opcja gwiazdki została zapisana.", - "addon.mod_forum.group": "Grupa", - "addon.mod_forum.lastpost": "Ostatni wpis", - "addon.mod_forum.lockdiscussion": "Zablokuj ta dyskusje", - "addon.mod_forum.lockupdated": "Opcja zablokowania została zapisana.", - "addon.mod_forum.message": "Wiadomość", - "addon.mod_forum.modeflatnewestfirst": "Wyświetl odpowiedzi płasko, z najnowszymi na początku", - "addon.mod_forum.modeflatoldestfirst": "Wyświetl odpowiedzi płasko, z najdawniejszymi na początku", - "addon.mod_forum.modenested": "Wyświetl odpowiedzi tematycznie", - "addon.mod_forum.modulenameplural": "Fora dyskusyjne", - "addon.mod_forum.pindiscussion": "Przypnij tą dyskusje", - "addon.mod_forum.pinupdated": "Opcja przypięcia została zapisana.", - "addon.mod_forum.postisprivatereply": "To jest prywatna odpowiedz. Nie będzie widoczna dla innych uczestników.", - "addon.mod_forum.posttoforum": "Wyślij wpis na forum", - "addon.mod_forum.posttomygroups": "Skopiuj post do wszystkich grup", - "addon.mod_forum.privatereply": "Odpowiedz prywatnie", - "addon.mod_forum.re": "Odp:", - "addon.mod_forum.refreshdiscussions": "Odśwież dyskusje", - "addon.mod_forum.refreshposts": "Odśwież posty", - "addon.mod_forum.removefromfavourites": "Usuń gwiazdkę z tej dyskusji", - "addon.mod_forum.reply": "Odpowiedz", - "addon.mod_forum.replyplaceholder": "Napisz swoją odpowiedz...", - "addon.mod_forum.subject": "Temat", - "addon.mod_forum.tagarea_forum_posts": "Wpisy na forum", - "addon.mod_forum.thisforumhasduedate": "Termin dodawania wpisów do tego forum to {{$a}}.", - "addon.mod_forum.thisforumisdue": "Termin dodawania wpisów do tego forum był {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Odblokuj dyskusje", - "addon.mod_forum.unpindiscussion": "Odepnij dyskusje", - "addon.mod_forum.unread": "Nieprzeczytane", - "addon.mod_forum.unreadpostsnumber": "{{$a}} nieprzeczytanych wpisów", - "addon.mod_forum.yourreply": "Twoja odpowiedź", - "addon.mod_glossary.addentry": "Dodaj pojęcie", - "addon.mod_glossary.aliases": "Słowa kluczowe", - "addon.mod_glossary.attachment": "Załącznik", - "addon.mod_glossary.browsemode": "Przeglądaj wpisy", - "addon.mod_glossary.byalphabet": "Alfabetycznie", - "addon.mod_glossary.byauthor": "Grupuj wg. autora", - "addon.mod_glossary.bycategory": "Grupuj wg. kategorii", - "addon.mod_glossary.bynewestfirst": "Najnowsze pierwsze", - "addon.mod_glossary.byrecentlyupdated": "Ostatnio zaktualizowane", - "addon.mod_glossary.bysearch": "Szukaj", - "addon.mod_glossary.cannoteditentry": "Nie można edytować wpisu", - "addon.mod_glossary.casesensitive": "Ten wpis inaczej traktuje duże i małe litery", - "addon.mod_glossary.categories": "Kategorie", - "addon.mod_glossary.concept": "Termin", - "addon.mod_glossary.definition": "Definicja", - "addon.mod_glossary.entrypendingapproval": "Wpisy oczekujące na zatwierdzenie", - "addon.mod_glossary.entryusedynalink": "Ten wpis powinien zostać automatycznie linkowany.", - "addon.mod_glossary.errconceptalreadyexists": "Definicja tego pojęcia już istnieje. Ten słownik nie zezwala na duplikowanie pojęć.", - "addon.mod_glossary.fillfields": "Pola: Termin i Definicja muszą zostać wypełnione", - "addon.mod_glossary.fullmatch": "Szukaj tylko wyrazów tak jak zostały wpisane
                    (jeśli zostały automatycznie połączone)", - "addon.mod_glossary.linking": "Automatyczne linkowanie", - "addon.mod_glossary.modulenameplural": "Słowniki pojęć", - "addon.mod_glossary.noentriesfound": "Nie znaleziono wpisów", - "addon.mod_glossary.tagarea_glossary_entries": "Wpisy w słowniku", - "addon.mod_h5pactivity.modulenameplural": "Aktywności H5P", - "addon.mod_imscp.deploymenterror": "Błąd pakietu treści!", - "addon.mod_imscp.modulenameplural": "Pakiety treści IMS", - "addon.mod_imscp.showmoduledescription": "Pokaż opis", - "addon.mod_imscp.toc": "Spis treści", - "addon.mod_lesson.answer": "Odpowiedź", - "addon.mod_lesson.attempt": "Podejście: {{$a}}", - "addon.mod_lesson.attemptheader": "Próba", - "addon.mod_lesson.attemptsremaining": "Pozostało Ci {{$a}} podejść", - "addon.mod_lesson.averagescore": "Średnia liczba punktów", - "addon.mod_lesson.averagetime": "Średni czas", - "addon.mod_lesson.branchtable": "Strona z treścią", - "addon.mod_lesson.cannotfindattempt": "Błąd: nie znaleziono podejść", - "addon.mod_lesson.cannotfinduser": "Błąd: nie znaleziono użytkowników", - "addon.mod_lesson.clusterjump": "Nie widziane pytanie w obrębie klastra", - "addon.mod_lesson.completed": "Skończono", - "addon.mod_lesson.congratulations": "Gratulacje - koniec lekcji", - "addon.mod_lesson.continue": "Kontynuuj", - "addon.mod_lesson.continuetonextpage": "Kontynuuj do następnej strony.", - "addon.mod_lesson.defaultessayresponse": "Twój esej będzie oceniony przez prowadzącego", - "addon.mod_lesson.detailedstats": "Szczegółowe statystyki", - "addon.mod_lesson.didnotanswerquestion": "Nie odpowiedziałeś na pytanie", - "addon.mod_lesson.displayofgrade": "Wyświetl ocenę studentowi", - "addon.mod_lesson.displayscorewithessays": "Otrzymałeś {{$a.score}} z {{$a.tempmaxgrade}} za pytania oceniane automatycznie. Twój {{$a.essayquestions}} esej zostanie oceniony i punktu zostaną dodane
                    do oceny końcowej później.

                    Twoja aktualna ocena bez oceny z eseju jest {{$a.score}} z {{$a.grade}}", - "addon.mod_lesson.displayscorewithoutessays": "Liczba Twoich punktów jest {{$a.score}} (z {{$a.grade}})", - "addon.mod_lesson.emptypassword": "Hasło nie może być puste", - "addon.mod_lesson.enterpassword": "Wprowadź hasło:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Nie odpowiedziałeś na żadne pytanie. Otrzymujesz 0 z tej lekcji.", - "addon.mod_lesson.finish": "Koniec", - "addon.mod_lesson.firstwrong": "Niestety nie otrzymasz punktów, dlatego że odpowiedź nie była poprawna. Czy chcesz odpowiadać dalej bez otrzymania punktów?", - "addon.mod_lesson.gotoendoflesson": "Przejdź do końca lekcji", - "addon.mod_lesson.grade": "Ocena", - "addon.mod_lesson.highscore": "Najwyższa ocena", - "addon.mod_lesson.hightime": "Najdłuższy czas", - "addon.mod_lesson.leftduringtimed": "Czas lekcji minął.
                    Kliknij 'Kontynuuj', żeby zacząć lekcję od nowa.", - "addon.mod_lesson.leftduringtimednoretake": "Czas lekcji minął
                    nie możesz powtórzyć lekcji.", - "addon.mod_lesson.lessonmenu": "Menu lekcji", - "addon.mod_lesson.lessonstats": "Statystyka lekcji", - "addon.mod_lesson.linkedmedia": "Połączone media", - "addon.mod_lesson.loginfail": "Błąd logowania, spróbuj ponownie", - "addon.mod_lesson.lowscore": "Najniższa ocena", - "addon.mod_lesson.lowtime": "Najkrótszy czas", - "addon.mod_lesson.maximumnumberofattemptsreached": "Osiągnięto maksymalną liczbę prób - przejdź do następnej strony", - "addon.mod_lesson.modattemptsnoteacher": "Tylko student może zmieniać pracę.", - "addon.mod_lesson.modulenameplural": "Lekcje", - "addon.mod_lesson.noanswer": "Brak odpowiedzi", - "addon.mod_lesson.nolessonattempts": "Nikt jeszcze nie rozwiązał lekcji", - "addon.mod_lesson.nolessonattemptsgroup": "Członkowie grupy {{$a}} nie podjęli prób w tej lekcji.", - "addon.mod_lesson.notcompleted": "Niekompletne", - "addon.mod_lesson.numberofcorrectanswers": "Ilość poprawnych odpowiedzi: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Ilość obejrzanych strony: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Liczba pytań odpowiedzianych: {{$a.nquestions}}; (powinieneś odpowiedzieć na przynajmniej {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Otrzymałeś dotąd {{$a.score}} punktów z {{$a.currenthigh}}", - "addon.mod_lesson.ongoingnormal": "Odpowiedziałeś poprawnie na {{$a.correct}} z {{$a.viewed}} pytań", - "addon.mod_lesson.or": "lub", - "addon.mod_lesson.overview": "Skrócony", - "addon.mod_lesson.preview": "Podgląd", - "addon.mod_lesson.progressbarteacherwarning2": "Nie zobaczysz wskaźnika postępu, ponieważ możesz edytować tą lekcję.", - "addon.mod_lesson.progresscompleted": "Masz za sobą {{$a}}% lekcji", - "addon.mod_lesson.question": "Pytanie", - "addon.mod_lesson.rawgrade": "Surowa (nie przeliczona) ocena", - "addon.mod_lesson.reports": "Raporty", - "addon.mod_lesson.response": "Informacja zwrotna", - "addon.mod_lesson.review": "Przegląd", - "addon.mod_lesson.reviewlesson": "Przejrzyj ponownie lekcje", - "addon.mod_lesson.reviewquestionback": "Tak, chcę spróbować ponownie", - "addon.mod_lesson.reviewquestioncontinue": "Nie, chcę przejść do następnego pytania", - "addon.mod_lesson.secondpluswrong": "Niezupełnie. Chcesz spróbować ponownie?", - "addon.mod_lesson.submit": "Prześlij", - "addon.mod_lesson.teacherjumpwarning": "Skoki typu {{$a.cluster}} lub {{$a.unseen}} są używane w tej lekcji. Skok do następnej strony będzie używany zamiast tego. Zaloguj się jako student, żeby przetestować przejścia", - "addon.mod_lesson.teacherongoingwarning": "Wynik jest pokazywany tylko studentowi. Zaloguj się jako student.", - "addon.mod_lesson.teachertimerwarning": "Licznik czasu działa tylko dla studentów, Aby przetestować, zaloguj się jako student.", - "addon.mod_lesson.thatsthecorrectanswer": "To jest poprawna odpowiedź", - "addon.mod_lesson.thatsthewronganswer": "To jest błędna odpowiedź", - "addon.mod_lesson.timeremaining": "Pozostały czas", - "addon.mod_lesson.timetaken": "Łączny czas", - "addon.mod_lesson.unseenpageinbranch": "Nie wyświetlane dotąd pytanie w treści strony", - "addon.mod_lesson.welldone": "Dobrze zrobione!", - "addon.mod_lesson.youhaveseen": "Widziałeś już więcej niż jedną stronę tej lekcji.
                    Czy chciałbyś zacząć od ostatniej strony, którą przeglądałeś?", - "addon.mod_lesson.youranswer": "Twoja odpowiedź", - "addon.mod_lesson.yourcurrentgradeisoutof": "Masz obecnie {{$a.grade}} punktów z {{$a.total}}", - "addon.mod_lesson.youshouldview": "Powinieneś odpowiedzieć na co najmniej: {{$a}}", - "addon.mod_lti.launchactivity": "Uruchom aktywność", - "addon.mod_lti.modulenameplural": "Narzędzia zewnętrzne", - "addon.mod_page.modulenameplural": "Strony", - "addon.mod_quiz.answercolon": "Odpowiedź:", - "addon.mod_quiz.attemptfirst": "Pierwsze podejście", - "addon.mod_quiz.attemptlast": "Ostatnie podejście", - "addon.mod_quiz.attemptnumber": "Próba", - "addon.mod_quiz.attemptquiznow": "Spróbuj teraz rozwiązać test", - "addon.mod_quiz.attemptstate": "Stan", - "addon.mod_quiz.clearchoice": "Odznacz mój wybór", - "addon.mod_quiz.comment": "Komentarz", - "addon.mod_quiz.completedon": "Ukończono", - "addon.mod_quiz.confirmclose": "Zakończenie podejścia. Jeżeli zakończysz to podejście, nie będziesz mógł zmienić swoich odpowiedzi.", - "addon.mod_quiz.confirmstart": "Test ma limit czasu: {{$a}}.
                    \nCzas będzie odliczany od momentu, kiedy rozpoczniesz swoją próbę. Musisz ją zakończyć przed jego upływem.
                    \nCzy jesteś pewien, że chcesz rozpocząć teraz?", - "addon.mod_quiz.confirmstartheader": "Limit czasu", - "addon.mod_quiz.connectionerror": "Utracono połączenie. (Automatyczny zapis nie powiódł się).\n\nZanotuj wszystkie odpowiedzi jakich udzieliłeś na tej stronie w ciągu ostatnich kilku minut i spróbuj połączyć się z siecią ponownie.\n\nKiedy połączenie zostanie nawiązane twoje odpowiedzi zostaną zapisane a ten komunikat zniknie.", - "addon.mod_quiz.continueattemptquiz": "Kontynuuj ostatnie podejście", - "addon.mod_quiz.continuepreview": "Kontynuuj ostatni podgląd", - "addon.mod_quiz.feedback": "Informacja zwrotna", - "addon.mod_quiz.finishattemptdots": "Zakończ próbę ...", - "addon.mod_quiz.grade": "Ocena", - "addon.mod_quiz.gradeaverage": "Średnia ocena", - "addon.mod_quiz.gradehighest": "Najwyższa ocena", - "addon.mod_quiz.grademethod": "Metoda oceniania", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}", - "addon.mod_quiz.marks": "Punkty", - "addon.mod_quiz.modulenameplural": "Testy (Quizy)", - "addon.mod_quiz.mustbesubmittedby": "Podejście musi być zakończone przed: {{$a}}.", - "addon.mod_quiz.noquestions": "Nie dodano jeszcze żadnego pytania", - "addon.mod_quiz.noreviewattempt": "Nie masz uprawnień do dokonania przeglądu tej próby.", - "addon.mod_quiz.notyetgraded": "Jeszcze nie ocenione", - "addon.mod_quiz.outof": "{{$a.grade}} z {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} z możliwych do uzyskania {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Całościowa informacja zwrotna", - "addon.mod_quiz.overdue": "Zaległy", - "addon.mod_quiz.overduemustbesubmittedby": "To podejście jest opóźnione (karencja): powinno było zostać zatwierdzone i złożone. Jeśli chcesz, aby zostało ocenione, musisz je zatwierdzić i złożyć do: {{$a}} . Jeśli nie zrobisz tego do tego czasu, żadne punkty za to podejście nie zostaną przyznane.", - "addon.mod_quiz.preview": "Podgląd", - "addon.mod_quiz.previewquiznow": "Pokaż podgląd testu", - "addon.mod_quiz.question": "Pytanie", - "addon.mod_quiz.quiznavigation": "Nawigacja w teście", - "addon.mod_quiz.quizpassword": "Hasło dostępu do quizu", - "addon.mod_quiz.reattemptquiz": "Ponownie rozwiąż quiz", - "addon.mod_quiz.requirepasswordmessage": "Aby wziąć udział w tym quizie, musisz znać hasło", - "addon.mod_quiz.returnattempt": "Powrót do podejścia", - "addon.mod_quiz.review": "Przegląd", - "addon.mod_quiz.reviewofattempt": "Przerzyj próbę {{$a}}", - "addon.mod_quiz.reviewofpreview": "Przegląd podglądu", - "addon.mod_quiz.showall": "Pokaż wszystkie pytania na stronie", - "addon.mod_quiz.showeachpage": "Pokaż jedną stronę na raz", - "addon.mod_quiz.startattempt": "Rozpocznij próbę", - "addon.mod_quiz.startedon": "Rozpoczęto", - "addon.mod_quiz.stateabandoned": "Nigdy nie złożony", - "addon.mod_quiz.statefinished": "Zakończony", - "addon.mod_quiz.statefinisheddetails": "Złożony {{$a}}", - "addon.mod_quiz.stateinprogress": "W toku", - "addon.mod_quiz.stateoverdue": "Zaległe", - "addon.mod_quiz.stateoverduedetails": "Musi być przesłane przez {{$a}}", - "addon.mod_quiz.status": "Stan", - "addon.mod_quiz.submitallandfinish": "Zatwierdź wszystkie i zakończ", - "addon.mod_quiz.summaryofattempt": "Podsumowanie próby", - "addon.mod_quiz.summaryofattempts": "Podsumowanie twoich poprzednich podejść", - "addon.mod_quiz.timeleft": "Pozostały czas", - "addon.mod_quiz.timetaken": "Wykorzystany czas", - "addon.mod_quiz.yourfinalgradeis": "Twoja końcowa ocena za ten quiz wynosi {{$a}}", - "addon.mod_resource.modifieddate": "Zmodyfikowane {{$a}}", - "addon.mod_resource.modulenameplural": "Pliki", - "addon.mod_resource.openthefile": "Otwórz plik", - "addon.mod_resource.uploadeddate": "Przesłano {{$a}}", - "addon.mod_scorm.asset": "Zasób", - "addon.mod_scorm.assetlaunched": "Zasób - obejrzany", - "addon.mod_scorm.attempts": "Próby", - "addon.mod_scorm.averageattempt": "Średnia prób", - "addon.mod_scorm.browse": "Przeglądaj", - "addon.mod_scorm.browsed": "Przeglądane", - "addon.mod_scorm.browsemode": "Tryb przeglądania", - "addon.mod_scorm.completed": "Zakończone", - "addon.mod_scorm.contents": "Zawartość", - "addon.mod_scorm.enter": "Wejdź", - "addon.mod_scorm.errordownloadscorm": "Błąd pobierania pakietu SCORM: \"{{name}}\".", - "addon.mod_scorm.exceededmaxattempts": "Osiągnąłeś maksymalną liczbę podejść.", - "addon.mod_scorm.failed": "Nieudane", - "addon.mod_scorm.firstattempt": "Pierwsza próba", - "addon.mod_scorm.gradeaverage": "Średnia ocena", - "addon.mod_scorm.gradeforattempt": "Ocena za próbę", - "addon.mod_scorm.gradehighest": "Najwyższa ocena", - "addon.mod_scorm.grademethod": "Metoda oceniania", - "addon.mod_scorm.gradereported": "Ocena przekazana", - "addon.mod_scorm.gradescoes": "Liczba obiektów SCO", - "addon.mod_scorm.gradesum": "Zsumuj oceny", - "addon.mod_scorm.highestattempt": "Najwyższa próba", - "addon.mod_scorm.incomplete": "Niepełne", - "addon.mod_scorm.lastattempt": "Ostatnia zakończona próba", - "addon.mod_scorm.modulenameplural": "Pakiety SCORM", - "addon.mod_scorm.newattempt": "Rozpocznij nową próbę", - "addon.mod_scorm.noattemptsallowed": "Liczba dozwolonych prób", - "addon.mod_scorm.noattemptsmade": "Liczba wykonanych prób", - "addon.mod_scorm.notattempted": "Nie próbowano", - "addon.mod_scorm.organizations": "Organizacje", - "addon.mod_scorm.passed": "Udane", - "addon.mod_scorm.reviewmode": "Tryb przeglądu", - "addon.mod_scorm.score": "Wynik", - "addon.mod_scorm.suspended": "Zawieszone", - "addon.mod_scorm.toc": "Spis treści", - "addon.mod_survey.ifoundthat": "Stwierdziłem, że", - "addon.mod_survey.ipreferthat": "Wolę to", - "addon.mod_survey.modulenameplural": "Ankiety", - "addon.mod_survey.responses": "Odpowiedzi", - "addon.mod_survey.results": "Wyniki", - "addon.mod_survey.surveycompletednograph": "Już wypełniłeś tą ankietę.", - "addon.mod_url.modulenameplural": "Adresy URL", - "addon.mod_wiki.cannoteditpage": "Nie możesz edytować tej strony.", - "addon.mod_wiki.createpage": "Utwórz stronę", - "addon.mod_wiki.editingpage": "Edycja strony: '{{$a}}'", - "addon.mod_wiki.map": "Mapa stron", - "addon.mod_wiki.modulenameplural": "Wiki", - "addon.mod_wiki.newpagehdr": "Nowa strona", - "addon.mod_wiki.newpagetitle": "Tytuł nowej strony", - "addon.mod_wiki.nocontent": "Brak zawartości dla tej strony", - "addon.mod_wiki.notingroup": "Nie w grupie", - "addon.mod_wiki.pageexists": "Ta strona już istnieje.", - "addon.mod_wiki.pagename": "Nazwa strony", - "addon.mod_wiki.tagarea_wiki_pages": "Strony wiki", - "addon.mod_wiki.wrongversionlock": "Inny użytkownik edytował tę stronę w czasie, kiedy ty ją edytowałeś i twoja zawartość jest przestarzała.", - "addon.mod_workshop.alreadygraded": "Już oceniono", - "addon.mod_workshop.areainstructauthors": "Instrukcje wykonania prac", - "addon.mod_workshop.areainstructreviewers": "Instrukcje recenzowania", - "addon.mod_workshop.assess": "Recenzuj", - "addon.mod_workshop.assessedsubmission": "Zrecenzowana praca", - "addon.mod_workshop.assessmentform": "Formularz recenzowania", - "addon.mod_workshop.assessmentsettings": "Ustawienia recenzowania", - "addon.mod_workshop.assessmentweight": "Waga recenzji", - "addon.mod_workshop.assignedassessments": "Prace przydzielone do recenzji", - "addon.mod_workshop.assignedassessmentsnone": "Nie masz przydzielonych żadnych prac do recenzji", - "addon.mod_workshop.conclusion": "Wniosek", - "addon.mod_workshop.createsubmission": "Rozpocznij swoją pracę nad zadaniem", - "addon.mod_workshop.deletesubmission": "Usuń pracę", - "addon.mod_workshop.editsubmission": "Edytuj pracę", - "addon.mod_workshop.feedbackauthor": "Informacja zwrotna dla autora", - "addon.mod_workshop.feedbackby": "Informacja zwrotna od {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Informacja zwrotna dla recenzenta", - "addon.mod_workshop.givengrades": "Wystawiono oceny", - "addon.mod_workshop.gradecalculated": "Ocena obliczona za prace", - "addon.mod_workshop.gradeinfo": "Ocena: {{$a.received}} / {{$a.max}}", - "addon.mod_workshop.gradeover": "Nadpisz ocenę za pracę", - "addon.mod_workshop.gradesreport": "Raport z ocenami warsztatu", - "addon.mod_workshop.gradinggrade": "Ocena za recenzję", - "addon.mod_workshop.gradinggradecalculated": "Obliczona ocena za recenzje", - "addon.mod_workshop.gradinggradeof": "Ocena za recenzje (z {{$a}})", - "addon.mod_workshop.gradinggradeover": "Nadpisz ocenę za recenzje", - "addon.mod_workshop.modulenameplural": "Warsztaty", - "addon.mod_workshop.nogradeyet": "Jeszcze nie ocenione", - "addon.mod_workshop.notassessed": "Jeszcze nie ocenione", - "addon.mod_workshop.notoverridden": "Nie nadpisuj", - "addon.mod_workshop.noyoursubmission": "Jeszcze nie przesłałeś swojej pracy", - "addon.mod_workshop.publishedsubmissions": "Opublikowane prace", - "addon.mod_workshop.publishsubmission": "Opublikuj pracę", - "addon.mod_workshop.publishsubmission_help": "Opublikowane prace są widoczne dla innych po zamknięciu warsztatu.", - "addon.mod_workshop.reassess": "Recenzuj ponownie", - "addon.mod_workshop.receivedgrades": "Otrzymane oceny", - "addon.mod_workshop.submissionattachment": "Załącznik", - "addon.mod_workshop.submissioncontent": "Zawartość pracy", - "addon.mod_workshop.submissiondeleteconfirm": "Czy jesteś pewien, że chcesz usunąć następujące prace?", - "addon.mod_workshop.submissiongrade": "Ocena za pracę", - "addon.mod_workshop.submissiongradeof": "Ocena za pracę (z {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Musisz wpisać tekst lub dodać plik.", - "addon.mod_workshop.submissiontitle": "Tytuł", - "addon.mod_workshop.switchphase10": "Przełącz do fazy konfiguracji", - "addon.mod_workshop.switchphase20": "Przełącz do fazy składania prac", - "addon.mod_workshop.switchphase30": "Przełącz do fazy recenzowania", - "addon.mod_workshop.switchphase40": "Przełącz do fazy oceniania", - "addon.mod_workshop.switchphase50": "Zamknij warsztat", - "addon.mod_workshop.userplan": "Terminarz warsztatu", - "addon.mod_workshop.userplancurrentphase": "Obecna faza", - "addon.mod_workshop.weightinfo": "Waga: {{$a}}", - "addon.mod_workshop.yourassessment": "Twoja ocena", - "addon.mod_workshop.yourgrades": "Twoje oceny", - "addon.mod_workshop.yoursubmission": "Twoja praca", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Komentarz dla {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Ocena dla {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Kryterium {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Musisz wybrać ocenę dla tego kryterium", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Komentarz dla {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Kryterium {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Komentarz dla {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Ocena dla {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Kryterium {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kryterium {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Musisz wybrać jedną z tych pozycji", - "addon.notes.addnewnote": "Dodaj nową notatkę", - "addon.notes.coursenotes": "Notatki kursu", - "addon.notes.deleteconfirm": "Czy na pewno usunąć tą notatkę?", - "addon.notes.eventnotecreated": "Utworzono notatkę", - "addon.notes.eventnotedeleted": "Usunięto notatkę", - "addon.notes.nonotes": "Nie ma jeszcze notatek tego typu", - "addon.notes.note": "Notatka", - "addon.notes.notes": "Notatki", - "addon.notes.personalnotes": "Notatki osobiste", - "addon.notes.publishstate": "Kontekst", - "addon.notes.sitenotes": "Notatki strony głównej", - "addon.notifications.markallread": "Oznacz wszystko jako przeczytane", - "addon.notifications.notificationpreferences": "Preferencje powiadomień", - "addon.notifications.notifications": "Powiadomienia", - "addon.notifications.playsound": "Odtwórz dźwięk", - "addon.notifications.therearentnotificationsyet": "Brak powiadomień", - "assets.countries.AD": "Andora", - "assets.countries.AE": "Zjednoczone Emiraty Arabskie", - "assets.countries.AF": "Afganistan", - "assets.countries.AG": "Antigua i Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktyka", - "assets.countries.AR": "Argentyna", - "assets.countries.AS": "Samoa Amerykańskie", - "assets.countries.AT": "Austria", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Wyspy Alandzkie", - "assets.countries.AZ": "Azerbejdżan", - "assets.countries.BA": "Bośnia i Hercegowina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesz", - "assets.countries.BE": "Belgia", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bułgaria", - "assets.countries.BH": "Bahrajn", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint-Barthélemy", - "assets.countries.BM": "Bermudy", - "assets.countries.BN": "Brunei Darussalam", - "assets.countries.BO": "Boliwia", - "assets.countries.BQ": "Bonaire, Sint Eustatius i Saba", - "assets.countries.BR": "Brazylia", - "assets.countries.BS": "Wyspy Bahama", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvet Island", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Białoruś", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Wyspy Kokosowe", - "assets.countries.CD": "Demokratyczna Republika Konga", - "assets.countries.CF": "Republika Centralnej Afryki", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Szwajcaria", - "assets.countries.CI": "Wybrzeże Kości Słoniowej", - "assets.countries.CK": "Wyspy Cooka", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Chiny", - "assets.countries.CO": "Kolumbia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Przylądek Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Wyspy Bożego Narodzenia", - "assets.countries.CY": "Cypr", - "assets.countries.CZ": "Czechy", - "assets.countries.DE": "Niemcy", - "assets.countries.DJ": "Dżibuti", - "assets.countries.DK": "Dania", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Republika Dominikany", - "assets.countries.DZ": "Algieria", - "assets.countries.EC": "Ekwador", - "assets.countries.EE": "Estonia", - "assets.countries.EG": "Egipt", - "assets.countries.EH": "Zachodnia Sahara", - "assets.countries.ER": "Erytrea", - "assets.countries.ES": "Hiszpania", - "assets.countries.ET": "Etiopia", - "assets.countries.FI": "Finlandia", - "assets.countries.FJ": "Fidżi", - "assets.countries.FK": "Falklandy (Malwiny)", - "assets.countries.FM": "Mikronezja", - "assets.countries.FO": "Wyspy Faroe", - "assets.countries.FR": "Francja", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Wielka Brytania", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Gruzja", - "assets.countries.GF": "Francuska Gujana", - "assets.countries.GG": "Guernsey ", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grenlandia", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Gwinea", - "assets.countries.GP": "Gwadelupa", - "assets.countries.GQ": "Gwinea Równikowa", - "assets.countries.GR": "Grecja", - "assets.countries.GS": "Georgia Południowa i Sandwich Południowy", - "assets.countries.GT": "Gwatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Gwinea Bissau", - "assets.countries.GY": "Gujana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Heard and McDonald Islands", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Chorwacja", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Węgry", - "assets.countries.ID": "Indonezja", - "assets.countries.IE": "Irlandia", - "assets.countries.IL": "Izrael", - "assets.countries.IM": "Wyspa Man (Morze Irlandzkie)", - "assets.countries.IN": "Indie", - "assets.countries.IO": "Brytyjskie Terytorium Oceanu Indyjskiego", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran", - "assets.countries.IS": "Islandia", - "assets.countries.IT": "Włochy", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamajka", - "assets.countries.JO": "Jordan", - "assets.countries.JP": "Japonia", - "assets.countries.KE": "Kenia", - "assets.countries.KG": "Kirgistan", - "assets.countries.KH": "Kambodża", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komory", - "assets.countries.KN": "Święty Kitts i Nevis", - "assets.countries.KP": "Korea", - "assets.countries.KR": "Republika Korei", - "assets.countries.KW": "Kuwejt", - "assets.countries.KY": "Kajmany", - "assets.countries.KZ": "Kazachstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Liban", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litwa", - "assets.countries.LU": "Luxembourg", - "assets.countries.LV": "Łotwa", - "assets.countries.LY": "Jamahirija", - "assets.countries.MA": "Maroko", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Mołdawia", - "assets.countries.ME": "Czarnogóra", - "assets.countries.MF": "Wspólnota Saint-Martin", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Wyspy Marshalla", - "assets.countries.MK": "Macedonia", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Makau (chin.)", - "assets.countries.MP": "Northern Mariana Islands", - "assets.countries.MQ": "Martynika", - "assets.countries.MR": "Mauretania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Malediwy", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Meksyk", - "assets.countries.MY": "Malezja", - "assets.countries.MZ": "Mozambik", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Nowa Kaledonia", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Wyspy Norfolk", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nikaragua", - "assets.countries.NL": "Holandia", - "assets.countries.NO": "Norwegia", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nowa Zelandia", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Francuska Polinezja", - "assets.countries.PG": "Papua-Nowa Gwinea", - "assets.countries.PH": "Filipiny", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polska", - "assets.countries.PM": "St. Pierre and Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestyna", - "assets.countries.PT": "Portugalia", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paragwaj", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Reunion", - "assets.countries.RO": "Rumunia", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Rosja", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Arabia Saudyjska", - "assets.countries.SB": "Wyspy Salomona", - "assets.countries.SC": "Seszele", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Szwecja", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "St. Helen", - "assets.countries.SI": "Słowenia", - "assets.countries.SJ": "Wyspy Svalbard and Jan Mayen", - "assets.countries.SK": "Słowacja", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Południowy Sudan", - "assets.countries.ST": "Sao Tome and Principe", - "assets.countries.SV": "Salwador", - "assets.countries.SX": "Sint Maarten (część Holandii)", - "assets.countries.SY": "Syria", - "assets.countries.SZ": "Szwajcaria", - "assets.countries.TC": "Turks and Caicos Islands", - "assets.countries.TD": "Czad", - "assets.countries.TF": "Francuskie Terytoria Południowe", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tajlandia", - "assets.countries.TJ": "Tadżykistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor Wschodni", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunezja", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turcja", - "assets.countries.TT": "Trynidad i Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Tajwan", - "assets.countries.TZ": "Tanzania", - "assets.countries.UA": "Ukraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Dalekie Wyspy Mniejsze Stanów Zjednoczonych", - "assets.countries.US": "USA", - "assets.countries.UY": "Urugwaj", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Watykan", - "assets.countries.VC": "Saint Vincent and the Grenadines", - "assets.countries.VE": "Wenezuela", - "assets.countries.VG": "Wyspy Dziewicze (br.)", - "assets.countries.VI": "Wyspy Dziewicze (amer.)", - "assets.countries.VN": "Wietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis and Futuna Islands", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Republika Południowej Afryki", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "ebook EPUB", - "assets.mimetypes.application/msword": "Dokument programu Word", - "assets.mimetypes.application/pdf": "Dokument formatu PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Kopia zapasowa Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Skoroszyt Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 makro arkusz", - "assets.mimetypes.application/vnd.ms-powerpoint": "Prezentacja PowerPoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument arkusz kalkulacyjny", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument arkusz kalkulacyjny szablon", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument Dokument tekstowy", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument Dokument tekstowy szablon", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument Strona internetowa szablon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Prezentacja PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Pokaz PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Arkusz MS Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Szablon MS Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Dokument programu Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Prezentacja iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Arkusz iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Dokument iWork Pages", - "assets.mimetypes.application/x-javascript": "Kod źródłowy JavaScript", - "assets.mimetypes.application/x-mspublisher": "Dokument programu Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animacja Flash", - "assets.mimetypes.application/xhtml_xml": "Dokument XHTML", - "assets.mimetypes.archive": "Archiwum ({{$a.EXT}})", - "assets.mimetypes.audio": "Archiwum ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Plik", - "assets.mimetypes.group:archive": "Archiwa plików", - "assets.mimetypes.group:audio": "Pliki audio", - "assets.mimetypes.group:document": "Pliki dokumentów", - "assets.mimetypes.group:html_audio": "Pliki audio standardowo wspierane przez przeglądarki", - "assets.mimetypes.group:html_video": "Pliki wideo standardowo wspierane przez przeglądarki", - "assets.mimetypes.group:image": "Pliki obrazów", - "assets.mimetypes.group:presentation": "Pliki prezentacji", - "assets.mimetypes.group:sourcecode": "Kod źródłowy", - "assets.mimetypes.group:spreadsheet": "Pliki arkuszy kalkulacyjnych", - "assets.mimetypes.group:video": "Pliki wideo", - "assets.mimetypes.group:web_audio": "Pliki audio wykorzystane na stronie", - "assets.mimetypes.group:web_file": "Pliki strony", - "assets.mimetypes.group:web_image": "Pliki obrazów wykorzystane na stronie", - "assets.mimetypes.group:web_video": "Pliki wideo wykorzystane na stronie", - "assets.mimetypes.image": "Obraz ({{$a.MIMETYPE2}})", - "assets.mimetypes.text/css": "Kaskadowe arkusze stylów", - "assets.mimetypes.text/csv": "Wartości rozdzielone przecinkami", - "assets.mimetypes.text/html": "Dokument HTML", - "assets.mimetypes.text/plain": "Plik tekstowy", - "assets.mimetypes.text/rtf": "dokument RTF", - "assets.mimetypes.video": "Plik wideo ({{$a.EXT}})", - "core.accounts": "Konta", - "core.add": "Dodaj", - "core.agelocationverification": "Weryfikacja wieku i lokalizacji", - "core.ago": "{{$a}} temu", - "core.all": "Wszystkie", - "core.allgroups": "Wszystkie grupy", - "core.allparticipants": "Wszyscy uczestnicy", - "core.answer": "Odpowiedz", - "core.answered": "Udzielono odpowiedzi", - "core.areyousure": "Jesteś pewien?", - "core.back": "Wstecz", - "core.block.blocks": "Bloki", - "core.cancel": "Anuluj", - "core.cannotconnect": "Nie można się połączyć: Sprawdź, czy poprawnie wpisałeś adres URL i że witryna korzysta z Moodle {{$a}} lub nowszego.", - "core.captureaudio": "Zapisz audio", - "core.captureimage": "Zrób zdjęcie", - "core.capturevideo": "Zapisz wideo", - "core.category": "Kategoria", - "core.choose": "Wybierz", - "core.choosedots": "Wybierz ...", - "core.clearsearch": "Wyczyść wyszukiwanie", - "core.clicktohideshow": "Kliknij, aby rozwinąć lub zwinąć", - "core.clicktoseefull": "Kliknij, aby zobaczyć pełną zawartość.", - "core.close": "Zamknij", - "core.comments": "Komentarze", - "core.comments.addcomment": "Dodaj komentarz ...", - "core.comments.comments": "Komentarze", - "core.comments.commentscount": "Komentarze ({{$a}})", - "core.comments.deletecommentbyon": "Usuń komentarz zamieszczony przez {{$a.user}} dnia {{$a.time}}", - "core.comments.eventcommentcreated": "Utworzono komentarz", - "core.comments.eventcommentdeleted": "Usunięto komentarz", - "core.comments.nocomments": "Brak komentarzy", - "core.comments.savecomment": "Zapisz komentarz", - "core.commentscount": "Komentarze ({{$a}})", - "core.completion-alt-auto-fail": "Ukończone: {{$a}} (bez pozytywnej oceny)", - "core.completion-alt-auto-n": "Nie ukończone: {{$a}}", - "core.completion-alt-auto-n-override": "Nieukończona: {{$a.modname}} (ustawione przez {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Ukończone: {{$a}} (z pozytywną oceną)", - "core.completion-alt-auto-y": "Ukończone: {{$a}}", - "core.completion-alt-auto-y-override": "Ukończona: {{$a.modname}} (ustawione przez {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Nie ukończone: {{$a}}. Wybierz, aby oznaczyć jako zakończone.", - "core.completion-alt-manual-n-override": "Nieukończona: {{$a.modname}} (ustawione przez {{$a.overrideuser}}). Wybierz, aby oznaczyć jako ukończone.", - "core.completion-alt-manual-y": "Ukończone: {{$a}}. Wybierz, aby oznaczyć jako niezakończone", - "core.completion-alt-manual-y-override": "Ukończona: {{$a.modname}} (ustawione przez {{$a.overrideuser}}). Wybierz, aby oznaczyć jako nieukończone.", - "core.confirmdeletefile": "Czy na pewno chcesz usunąć ten plik?", - "core.confirmloss": "Jesteś pewny? Wszystkie zmiany zostaną utracone.", - "core.considereddigitalminor": "Jesteś zbyt młody aby założyć konto na tej stronie.", - "core.content": "Zawartość", - "core.contentlinks.chooseaccount": "Wybierz konto", - "core.continue": "Kontynuuj", - "core.copiedtoclipboard": "Tekst skopiowano do schowka", - "core.course": "Kurs", - "core.course.activitydisabled": "Twoja organizacja wyłączyła tę aktywność w aplikacji mobilnej.", - "core.course.allsections": "Wszystkie sekcje", - "core.course.confirmdeletemodulefiles": "Czy na pewno chcesz usunąć te pliki?", - "core.course.confirmlimiteddownload": "Nie masz obecnie połączenia z Wi-Fi.", - "core.course.contents": "Zawartość", - "core.course.coursesummary": "Podsumowanie kursu", - "core.course.downloadcourse": "Pobierz kurs", - "core.course.hiddenfromstudents": "Ukryte przed studentami", - "core.course.hiddenoncoursepage": "Dostępne, lecz nie wyświetlane na stronie kursu.", - "core.course.overriddennotice": "Twoja ocena końcowa z tej aktywności została ręcznie zmieniona", - "core.course.refreshcourse": "Odśwież kurs", - "core.course.sections": "Sekcje", - "core.coursedetails": "Dane kursowe", - "core.courses.addtofavourites": "Oznacz kurs gwiazdką", - "core.courses.allowguests": "W tym kursie mogą uczestniczyć również goście", - "core.courses.availablecourses": "Dostępne kursy", - "core.courses.categories": "Kategorie kursów", - "core.courses.courses": "Kursy", - "core.courses.downloadcourses": "Pobierz kursy", - "core.courses.enrolme": "Zapisz mnie", - "core.courses.frontpage": "Strona główna", - "core.courses.hidecourse": "Schowaj", - "core.courses.ignore": "Ignoruj", - "core.courses.mycourses": "Moje kursy", - "core.courses.mymoodle": "Kokpit", - "core.courses.nocourses": "Nie ma informacji do pokazania", - "core.courses.nocoursesyet": "Brak kursów w tej kategorii", - "core.courses.nosearchresults": "Brak wyników", - "core.courses.notenroled": "Nie jesteś zapisany w tym kursie", - "core.courses.paymentrequired": "Ten kurs wymaga opłaty", - "core.courses.paypalaccepted": "Płatności PayPal są akceptowane", - "core.courses.reload": "Odśwież", - "core.courses.removefromfavourites": "Usuń gwiazdkę z kursu", - "core.courses.search": "Wyszukaj", - "core.courses.searchcourses": "Przeszukaj kursy", - "core.courses.sendpaymentbutton": "Wyślij płatności za pośrednictwem PayPal", - "core.courses.show": "Przywróć ze schowka", - "core.currentdevice": "Bieżące urządzenie", - "core.date": "Data", - "core.day": "dzień", - "core.days": "dni", - "core.decsep": ",", - "core.defaultvalue": "Domyślne ({{$a}})", - "core.delete": "Usuń", - "core.deleteduser": "Usunięci użytkownicy", - "core.description": "Opis", - "core.digitalminor_desc": "Poproś rodzica / opiekuna o kontakt:", - "core.discard": "Odrzuć", - "core.dismiss": "Odwołaj", - "core.done": "Wykonane", - "core.download": "Pobierz", - "core.downloaded": "Pobrano", - "core.downloading": "Pobieram", - "core.edit": "Modyfikuj", - "core.editor.autosavesucceeded": "Szkic zapisany.", - "core.editor.bold": "Pogrubienie", - "core.editor.clear": "Usuń formatowanie", - "core.editor.h3": "Nagłówek (duży)", - "core.editor.h4": "Nagłówek (średni)", - "core.editor.h5": "Nagłówek (mały)", - "core.editor.italic": "Kursywa", - "core.editor.orderedlist": "Lista numerowana", - "core.editor.p": "Akapit", - "core.editor.strike": "Przekreślenie", - "core.editor.textrecovered": "Szkic tekstu został automatycznie przywrócony", - "core.editor.underline": "Podkreślenie", - "core.editor.unorderedlist": "Lista wypunktowana", - "core.error": "Błąd", - "core.errordownloading": "Błąd pobierania pliku", - "core.explanationdigitalminor": "Ta informacja jest wymagana, aby ustalić czy Twój wiek spełnia wymóg wieku cyfrowego przyzwolenia. Jest to wiek, od którego osoba może zatwierdzać regulamin i godzić się, aby jej dane były legalnie zapisywane oraz przetwarzane.", - "core.favourites": "Oznaczone gwiazdką", - "core.filename": "Nazwa pliku", - "core.filenotfound": "Nie znaleziono pliku, przepraszamy.", - "core.fileuploader.addfiletext": "Dodaj plik", - "core.fileuploader.audio": "Dźwięk", - "core.fileuploader.camera": "Aparat", - "core.fileuploader.confirmuploadfile": "Zostanie przesłane {{size}}. Czy chcesz kontynuować?", - "core.fileuploader.confirmuploadunknownsize": "Nie udało się obliczyć rozmiaru przesyłanych plików. Are you sure you want to continue?", - "core.fileuploader.errorcapturingaudio": "Błąd podczas nagrywania dźwięku.", - "core.fileuploader.errorcapturingimage": "Błąd podczas wykonywania zdjęcia.", - "core.fileuploader.errorcapturingvideo": "Błąd przechwytywania wideo", - "core.fileuploader.errorgettingimagealbum": "Błąd podczas pobierania zdjęcia z albumu.", - "core.fileuploader.errormustbeonlinetoupload": "Musisz mieć dostęp do sieci aby przesłać pliki.", - "core.fileuploader.errornoapp": "Nie zainstalowano aplikacji koniecznej do wykonania tej czynności.", - "core.fileuploader.errorreadingfile": "Błąd odczytu pliku.", - "core.fileuploader.errorwhileuploading": "Wystąpił błąd podczas przesyłania pliku.", - "core.fileuploader.file": "Plik", - "core.fileuploader.filesofthesetypes": "Akceptowane typy plików:", - "core.fileuploader.fileuploaded": "Plik został przesłany.", - "core.fileuploader.invalidfiletype": "Typ pliku nie jest akceptowany: {{$a}}", - "core.fileuploader.maxbytesfile": "Plik {{$a.file}} jest zbyt duży. Maksymalny dozwolony rozmiar przesyłanego pliku to {{$a.size}}.", - "core.fileuploader.more": "Więcej", - "core.fileuploader.photoalbums": "Galeria zdjęć", - "core.fileuploader.readingfile": "Odczytuję plik", - "core.fileuploader.selectafile": "Wybierz plik", - "core.fileuploader.uploadafile": "Prześlij plik", - "core.fileuploader.uploading": "Przesyłanie", - "core.fileuploader.uploadingperc": "Przesyłanie: {{$a}}%", - "core.fileuploader.video": "Wideo", - "core.filter": "Filtr", - "core.folder": "Folder", - "core.forcepasswordchangenotice": "W celu kontynuacji musisz zmienić swoje hasło", - "core.fulllistofcourses": "Wszystkie kursy", - "core.grades.average": "Średnia", - "core.grades.badgrade": "Wprowadzona ocena jest niepoprawna", - "core.grades.contributiontocoursetotal": "Udział w całym kursie", - "core.grades.feedback": "Informacja zwrotna", - "core.grades.grade": "Ocena", - "core.grades.gradeitem": "Oceniany element", - "core.grades.grades": "Oceny", - "core.grades.lettergrade": "Nazwy ocen", - "core.grades.nogradesreturned": "Brak ocen", - "core.grades.nooutcome": "Brak efektu kształcenia", - "core.grades.percentage": "Procentowo", - "core.grades.range": "Zakres", - "core.grades.rank": "Pozycja na tle grupy", - "core.grades.weight": "Waga", - "core.group": "Grupa", - "core.groupsseparate": "Osobne grupy", - "core.groupsvisible": "Widoczne grupy", - "core.h5p.additionallicenseinfo": "Dodatkowe informacje o licencji", - "core.h5p.author": "Autor", - "core.h5p.authorcomments": "Komentarze autora", - "core.h5p.authorcommentsdescription": "Komentarze dla redaktora treści. (Ten tekst nie zostanie opublikowany jako część informacji o prawach autorskich.)", - "core.h5p.authorname": "Nazwa autora", - "core.h5p.authorrole": "Rola autora", - "core.h5p.by": "przez", - "core.h5p.cancellabel": "Anuluj", - "core.h5p.changedby": "Zmienione przez", - "core.h5p.changedescription": "Opis zmiany", - "core.h5p.changelog": "Dziennik zmian", - "core.h5p.changeplaceholder": "Zdjęcie przycięte, tekst zmieniony, itp.", - "core.h5p.close": "Zamknij", - "core.h5p.confirmdialogbody": "Potwierdź, że chcesz kontynuować. Nie można cofnąć tego działania.", - "core.h5p.confirmdialogheader": "Potwierdź działanie", - "core.h5p.confirmlabel": "Potwierdź", - "core.h5p.connectionLost": "Utracono połączenie. Wyniki zostaną zapisane i wysłane po ponownym nawiązaniu połączenia.", - "core.h5p.connectionReestablished": "Połączenie zostało przywrócone.", - "core.h5p.contentCopied": "Treść jest kopiowana do schowka", - "core.h5p.contentchanged": "Ta zawartość uległa zmianie od ostatniego użycia.", - "core.h5p.contenttype": "Typ zawartości", - "core.h5p.copyrightinfo": "Informacje o prawach autorskich", - "core.h5p.copyrightstring": "Prawa autorskie", - "core.h5p.copyrighttitle": "Wyświetl informacje o prawach autorskich dla tej zawartości", - "core.h5p.date": "Data", - "core.h5p.disablefullscreen": "Wyłącz pełny ekran", - "core.h5p.download": "Pobierz", - "core.h5p.downloadtitle": "Pobierz tę zawartość jako plik H5P.", - "core.h5p.editor": "Edytor", - "core.h5p.embed": "Osadź", - "core.h5p.embedtitle": "Wyświetl kod do umieszczenia tej zawartości.", - "core.h5p.fullscreen": "Pełny ekran", - "core.h5p.hideadvanced": "Ukryj zaawansowane", - "core.h5p.license": "Licencja", - "core.h5p.licenseV1": "Wersja 1", - "core.h5p.licenseV2": "Wersja 2", - "core.h5p.licenseV3": "Wersja 3", - "core.h5p.licensee": "Właściciel", - "core.h5p.licenseversion": "Wersja licencji", - "core.h5p.offlineDialogHeader": "Twoje połączenie z serwerem zostało utracone", - "core.h5p.offlineDialogRetryButtonLabel": "Ponów teraz", - "core.h5p.offlineSuccessfulSubmit": "Pomyślnie przesłane wyniki.", - "core.h5p.originator": "Inicjator", - "core.h5p.reuse": "Ponowne użycie", - "core.h5p.reuseDescription": "Ponownie użyj tej treści.", - "core.h5p.showadvanced": "Pokaż zaawansowane", - "core.h5p.showless": "Pokaż mniej", - "core.h5p.showmore": "Pokaż więcej", - "core.h5p.size": "Rozmiar", - "core.h5p.source": "Źródło", - "core.h5p.startingover": "Rozpoczniesz od nowa.", - "core.h5p.thumbnail": "Miniaturka", - "core.h5p.title": "Tytuł", - "core.h5p.year": "Rok", - "core.h5p.years": "Lata", - "core.h5p.yearsfrom": "Lata (od)", - "core.h5p.yearsto": "Lata (do)", - "core.help": "Pomoc", - "core.hide": "Ukryj", - "core.hour": "godz.", - "core.hours": "godz.", - "core.image": "Obraz", - "core.info": "Informacja", - "core.invalidformdata": "Nieprawidłowe dane formularza", - "core.labelsep": ":", - "core.lastaccess": "Ostatni dostęp", - "core.lastmodified": "Ostatnia modyfikacja", - "core.layoutgrid": "siatka", - "core.list": "Lista", - "core.listsep": ";", - "core.loading": "Ładowanie", - "core.loadmore": "Wczytaj więcej", - "core.location": "Lokalizacja", - "core.login.auth_email": "Uwierzytelnienie z wykorzystaniem poczty elektronicznej", - "core.login.authenticating": "Uwierzytelnianie", - "core.login.cancel": "Anuluj", - "core.login.changepassword": "Zmień hasło", - "core.login.changepasswordbutton": "Otwórz stronę zmiany hasła", - "core.login.connect": "Połączono!", - "core.login.connecttomoodle": "Połącz się z Moodle", - "core.login.createaccount": "Utwórz moje nowe konto", - "core.login.createuserandpass": "Wybierz nazwę użytkownika oraz hasło, które będą używane do logowania", - "core.login.emailconfirmsent": "

                    Został do Ciebie wysłany e-mail pod adres {{$a}}.

                    Zawiera on prostą instrukcję, jak dokończyć rejestrację.

                    Jeżeli nadal będziesz mieć kłopoty, skontaktuj się z administratorem serwisu.

                    ", - "core.login.emailconfirmsentsuccess": "E-mail z potwierdzeniem został pomyślnie wysłany", - "core.login.firsttime": "Czy jesteś w tym serwisie po raz pierwszy?", - "core.login.forcepasswordchangenotice": "W celu kontynuacji musisz zmienić swoje hasło", - "core.login.forgotten": "Zapomniałeś(aś) nazwy użytkownika lub hasła?", - "core.login.help": "Pomoc", - "core.login.instructions": "Instrukcje", - "core.login.invaliddate": "Nieprawidłowa data", - "core.login.invalidemail": "Niewłaściwy adres e-mail", - "core.login.invalidmoodleversion": "

                    Niewłaściwa wersja platformy Moodle. Aplikacja mobilna wspiera tylko platformę Moodle w wersji {{$a}} i późniejszych.

                    \n

                    Możesz skontaktować się z administratorem i poprosić aktualizację platformy Moodle.

                    \n

                    Administratorzy są osobami, które zarządzają Moodlem w twojej organizacji. Jeśli nie wiesz jak skontaktować się z nimi, skontaktuej się ze swoim nauczycielem/wykładowcą/trenerem.

                    ", - "core.login.invalidsite": "Adres URL witryny jest nieprawidłowy.", - "core.login.invalidtime": "Niepoprawny czas", - "core.login.invalidurl": "Określono niepoprawny URL", - "core.login.invalidvaluemax": "Maksymalna wartość wynosi {{$a}}", - "core.login.invalidvaluemin": "Minimalna wartość to {{$a}}", - "core.login.login": "Zaloguj się", - "core.login.loginbutton": "Zaloguj się", - "core.login.logininsiterequired": "Musisz zalogować się do serwisu w oknie przeglądarki.", - "core.login.loginsteps": "Aby otrzymać pełny dostęp do kursów w tym serwisie, musisz najpierw utworzyć konto.", - "core.login.missingemail": "Pominięto adres e-mail", - "core.login.missingfirstname": "Pominięto imię", - "core.login.missinglastname": "Pominięto nazwisko", - "core.login.mobileservicesnotenabled": "Usługi mobilne nie są włączone w Twojej witrynie. Proszę, skontaktuj się z administratorem serwisu Moodle jeśli uważasz mobilny dostęp powinien być włączony.", - "core.login.mustconfirm": "Musisz potwierdzić swoją nazwę użytkownika", - "core.login.newaccount": "Nowe konto", - "core.login.notloggedin": "Musisz być zalogowany.", - "core.login.password": "Hasło", - "core.login.passwordforgotten": "Zapomniane hasło", - "core.login.passwordforgotteninstructions2": "Aby zresetować hasło, wprowadź poniżej swoją nazwę użytkownika lub adres e-mail. Jeżeli uda nam się znaleźć Twoje dane w bazie danych, zostanie wysłana wiadomość na Twój adres e-mail z instrukcją jak uzyskać ponownie dostęp.", - "core.login.passwordrequired": "Hasło jest wymagane", - "core.login.policyaccept": "Rozumiem i zgadzam się", - "core.login.policyagree": "Musisz zaakceptować te zasady, żeby używać strony. Czy zgadzasz się?", - "core.login.policyagreement": "Zasady użytkowania serwisu", - "core.login.policyagreementclick": "Link do strony dotyczącej polityki", - "core.login.potentialidps": "Zaloguj się używając swojego konta:", - "core.login.profileinvaliddata": "Niewłaściwa wartość", - "core.login.reconnect": "Połącz ponownie", - "core.login.resendemail": "Ponów wyslanie e-mail.", - "core.login.searchby": "Szukaj według:", - "core.login.security_question": "Pytanie zabezpieczające", - "core.login.selectacountry": "Wybierz kraj", - "core.login.selectsite": "Proszę wybrać swoją stronę:", - "core.login.siteaddress": "Adres strony", - "core.login.siteurlrequired": "Wymagany adres URL witryny, np.: http://www.twojastronamoodle.pl", - "core.login.startsignup": "Zacznij teraz od utworzenia nowego konta!", - "core.login.supplyinfo": "Więcej szczegółów", - "core.login.username": "Nazwa użytkownika", - "core.login.usernameoremail": "Wpisz nazwę użytkownika lub adres e-mail", - "core.login.usernamerequired": "Nazwa użytkownika jest wymagana", - "core.login.usernotaddederror": "Błąd - użytkownik nie został dodany.", - "core.lostconnection": "Straciliśmy połączenie musisz podłączyć się ponownie. Twój token jest teraz nieważny.", - "core.mainmenu.changesite": "Zmiana strony", - "core.mainmenu.help": "Pomoc", - "core.mainmenu.logout": "Wyloguj", - "core.mainmenu.website": "Strona internetowa", - "core.maxsizeandattachments": "Maksymalny rozmiar dla nowych plików: {{$a.size}}, maksimum załączników: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min.", - "core.misc": "Różne (Miscellaneous)", - "core.mod_assign": "Zadanie", - "core.mod_assignment": "Zadanie 2.2 (wyłączone)", - "core.mod_book": "Książka", - "core.mod_chat": "Czat", - "core.mod_choice": "Głosowanie", - "core.mod_data": "Baza Danych", - "core.mod_database": "Baza Danych", - "core.mod_external-tool": "Narzędzie zewnętrzne", - "core.mod_feedback": "Opinia zwrotna", - "core.mod_file": "Plik", - "core.mod_folder": "Folder", - "core.mod_forum": "Forum", - "core.mod_glossary": "Słownik pojęć", - "core.mod_h5pactivity": "Aktywność H5P", - "core.mod_ims": "Pakiet treści IMS", - "core.mod_imscp": "Pakiet treści IMS", - "core.mod_label": "Etykieta", - "core.mod_lesson": "Lekcja", - "core.mod_lti": "Narzędzie zewnętrzne", - "core.mod_page": "Strona", - "core.mod_quiz": "Test (Quiz )", - "core.mod_resource": "Plik", - "core.mod_scorm": "Pakiet SCORM", - "core.mod_survey": "Ankieta", - "core.mod_url": "Adres URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Warsztaty", - "core.moduleintro": "Opis", - "core.more": "więcej", - "core.mygroups": "Moje grupy", - "core.name": "Nazwa", - "core.networkerroriframemsg": "Ta treść nie jest dostępna w trybie offline. Połącz się z internetem i spróbuj ponownie.", - "core.networkerrormsg": "Sieć jest wyłączona lub nie działa.", - "core.never": "Nigdy", - "core.next": "Dalej", - "core.no": "Nie", - "core.nocomments": "Brak komentarzy", - "core.nograde": "Brak oceny", - "core.none": "Żaden", - "core.nopasswordchangeforced": "Nie można kontynuować bez zmiany hasła.", - "core.nopermissions": "Brak odpowiednich uprawnień do wykonania ({{$a}})", - "core.noresults": "Brak wyników", - "core.noselection": "Brak wyboru", - "core.notenrolledprofile": "Ten profil jest niedostępny, gdyż ten użytkownik nie jest zapisany do kursu.", - "core.notice": "Powiadomienie", - "core.notingroup": "Musisz należeć do grupy, aby przeglądać tą aktywność", - "core.now": "teraz", - "core.numwords": "{{$a}} słów", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openinbrowser": "Otwórz w przeglądarce", - "core.othergroups": "Inne grupy", - "core.pagea": "Strona {{$a}}", - "core.paymentinstant": "Użyj poniższego przycisku, aby zapłacić i zapisać się na kurs w ciągu kilku minut!", - "core.phone": "Telefon", - "core.pictureof": "Obraz {{$a}}", - "core.previous": "Wstecz", - "core.proceed": "Kontynuuj", - "core.question.answer": "Odpowiedź", - "core.question.answersaved": "Odpowiedź zapisana", - "core.question.certainty": "Pewność", - "core.question.complete": "Zakończone", - "core.question.correct": "Poprawnie", - "core.question.feedback": "Informacja zwrotna", - "core.question.incorrect": "Niepoprawny(a)", - "core.question.information": "Informacja", - "core.question.invalidanswer": "Niekompletna odpowiedź", - "core.question.notanswered": "Nie udzielono odpowiedzi", - "core.question.notyetanswered": "Nie udzielono odpowiedzi", - "core.question.partiallycorrect": "Częściowo poprawnie", - "core.question.questionmessage": "Pytanie {{$a}}: {{$b}}", - "core.question.questionno": "Pytanie {{$a}}", - "core.question.requiresgrading": "Wymaga oceny", - "core.quotausage": "Aktualnie wykorzystano {{$a.used}} z limitu {{$a.total}}.", - "core.rating.aggregateavg": "Średnia ocen", - "core.rating.aggregatecount": "Liczba ocen", - "core.rating.aggregatemax": "Najwyższa ocena", - "core.rating.aggregatemin": "Najniższa ocena", - "core.rating.aggregatesum": "Suma ocen", - "core.rating.noratings": "Brak wystawionych ocen", - "core.rating.rating": "Oceny", - "core.rating.ratings": "Ocenianie", - "core.refresh": "Odśwież", - "core.remove": "Usuń", - "core.removefiles": "Usuń pliki {{$a}}", - "core.required": "Wymagane", - "core.resourcedisplayopen": "Otwórz", - "core.resources": "Zasoby", - "core.restore": "Odtwórz", - "core.restricted": "Ograniczony", - "core.retry": "Ponów próbę", - "core.save": "Zapisz", - "core.savechanges": "Zapisz zmiany", - "core.search": "Wyszukaj", - "core.searching": "Szukam", - "core.searchresults": "Wyniki wyszukiwania", - "core.sec": "sek", - "core.secs": "sek.", - "core.seemoredetail": "Kliknij aby zobaczyć więcej szczegółów", - "core.selectacategory": "Proszę wybrać kategorię", - "core.selectacourse": "Wybierz kurs", - "core.selectagroup": "Wybierz grupę", - "core.send": "Wyślij", - "core.sending": "Wysyłanie", - "core.serverconnection": "Błąd podczas łączenia się z serwerem", - "core.settings.about": "O aplikacji", - "core.settings.cannotsyncoffline": "Nie można synchronizować w trybie offline.", - "core.settings.colorscheme": "Schemat kolorów", - "core.settings.colorscheme-auto": "Automatyczny (bazujący na ustawieniach systemu)", - "core.settings.colorscheme-dark": "Ciemny", - "core.settings.colorscheme-light": "Jasny", - "core.settings.compilationinfo": "Informacje o kompilacji", - "core.settings.currentlanguage": "Aktualny język", - "core.settings.debugdisplay": "Wyświetl komunikaty błędów", - "core.settings.deletesitefiles": "Czy jesteś pewien, że chcesz usunąć pobrane pliki z tej strony?", - "core.settings.deletesitefilestitle": "Usuń pliki strony", - "core.settings.deviceinfo": "Informacje o urządzeniu", - "core.settings.deviceos": "System operacyjny urządzenia", - "core.settings.disableall": "Wyłącz powiadomienia", - "core.settings.disabled": "Wyłączone", - "core.settings.enablerichtexteditor": "Włącz edytor tekstu", - "core.settings.enablesyncwifi": "Zezwalaj na synchronizację tylko w przypadku połączenia Wi-Fi", - "core.settings.errordeletesitefiles": "Błąd podczas usuwania plików witryny.", - "core.settings.estimatedfreespace": "Szacowana ilość wolnego miejsca", - "core.settings.fontsize": "Rozmiar tekstu", - "core.settings.fontsizecharacter": "A", - "core.settings.general": "Ogólne", - "core.settings.language": "Język", - "core.settings.license": "Licencja", - "core.settings.locked": "zablokowany", - "core.settings.loggedin": "On-line", - "core.settings.loggedoff": "Nie zalogowany", - "core.settings.networkstatus": "Status połączenia z Internetem", - "core.settings.preferences": "Preferencje", - "core.settings.privacypolicy": "Polityka prywatności", - "core.settings.settings": "Ustawienia", - "core.settings.showdownloadoptions": "Pokaż opcje pobierania", - "core.settings.sites": "Serwisy", - "core.settings.spaceusage": "Wykorzystanie przestrzeni", - "core.settings.synchronization": "Synchronizacja", - "core.settings.synchronizenow": "Synchronizuj teraz", - "core.settings.syncsettings": "Ustawienia synchronizacji", - "core.settings.total": "Razem", - "core.settings.wificonnection": "Połączenie Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "Wybierz konto do przechowywania pliku.", - "core.sharedfiles.chooseactionrepeatedfile": "Plik o tej nazwie już istnieje. Czy chcesz zastąpić istniejący plik lub zmienić jego nazwę na \"{{$a}}\"?", - "core.sharedfiles.rename": "Zmień nazwę", - "core.sharedfiles.replace": "Zastąp", - "core.sharedfiles.sharedfiles": "Współdzielone pliki", - "core.sharedfiles.successstorefile": "Plik został pomyślnie zapisany. Wybierz plik, który chcesz przesłać do swoich prywatnych plików lub użyć w wybranej aktywności.", - "core.show": "Pokaż", - "core.showless": "Pokaż mniej ...", - "core.showmore": "Pokaż więcej ...", - "core.site": "Serwis", - "core.sitehome.sitehome": "Strona główna", - "core.sitehome.sitenews": "Ogłoszenia dotyczące witryny", - "core.sitemaintenance": "Trwają pracę administracyjne. Strona jest chwilowo niedostępna.", - "core.sizeb": "bajtów", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.skip": "Pomin", - "core.sorry": "Przepraszam...", - "core.sort": "Sortuj", - "core.sortby": "Sortuj według", - "core.start": "Rozpocznij", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M %p", - "core.strftimedatetimeshort": "%d/%m/%Y, %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M %p", - "core.strftimetime": "%H:%M %p", - "core.submit": "Prześlij", - "core.success": "Gotowe", - "core.tag.defautltagcoll": "Domyślna kolekcja", - "core.tag.inalltagcoll": "Wszędzie", - "core.tag.itemstaggedwith": "{{$a.tagarea}} otagowano \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Brak wyników dla \"{{$a}}\"", - "core.tag.notagsfound": "Nie znaleziono pasujących tagów \"{{$a}}\"", - "core.tag.searchtags": "Szukaj tagów", - "core.tag.showingfirsttags": "Wyświetl {{$a}} najpopularniejszych tagów", - "core.tag.tag": "Tag", - "core.tag.tagarea_course": "Kursy", - "core.tag.tags": "Tagi", - "core.teachers": "Prowadzący", - "core.thisdirection": "ltr", - "core.time": "Czas", - "core.timesup": "Koniec czasu", - "core.today": "Dzisiaj", - "core.tryagain": "Spróbuj ponownie", - "core.uhoh": "O nie!", - "core.unknown": "Nieznane", - "core.unlimited": "Nieograniczone", - "core.unzipping": "Rozpakowuję", - "core.updaterequired": "Aplikacja wymaga aktualizacji", - "core.upgraderunning": "Strona jest uaktualniana, proszę spróbować później.", - "core.user": "Użytkownik", - "core.user.address": "Adres", - "core.user.city": "Miasto", - "core.user.contact": "Kontakt", - "core.user.country": "Kraj", - "core.user.description": "Opis", - "core.user.details": "Szczegóły", - "core.user.editingteacher": "Prowadzący", - "core.user.email": "E-mail", - "core.user.emailagain": "E-mail (jeszcze raz)", - "core.user.firstname": "Imię", - "core.user.interests": "Zainteresowania", - "core.user.lastname": "Nazwisko", - "core.user.manager": "Menedżer", - "core.user.newpicture": "Nowy obraz", - "core.user.noparticipants": "Nie znaleziono uczestników w tym kursie", - "core.user.participants": "Uczestnicy", - "core.user.phone1": "Telefon", - "core.user.phone2": "Tel. komórkowy", - "core.user.roles": "Role", - "core.user.student": "Student", - "core.user.teacher": "Prowadzący bez praw edycji", - "core.user.webpage": "Strona WWW", - "core.userdeleted": "To konto użytkownika zostało usunięte", - "core.userdetails": "Szczegóły użytkownika", - "core.usernotfullysetup": "Użytkownik nie ustawiony w pełni", - "core.users": "Użytkownicy", - "core.view": "Wyświetl", - "core.viewcode": "Wyświetl kod", - "core.vieweditor": "Wyświetl edytor", - "core.viewprofile": "Zobacz profil", - "core.whatisyourage": "Ile masz lat?", - "core.wheredoyoulive": "W jakim kraju żyjesz?", - "core.whoops": "Ups!", - "core.whyisthishappening": "Dlaczego tak się dzieje?", - "core.whyisthisrequired": "Dlaczego jest to wymagane?", - "core.year": "rok", - "core.years": "lata", - "core.yes": "Tak", - "core.youreoffline": "Jesteś offline", - "core.youreonline": "Jesteś ponownie online" -} \ No newline at end of file diff --git a/src/assets/lang/pt-br.json b/src/assets/lang/pt-br.json deleted file mode 100644 index 5500969ea..000000000 --- a/src/assets/lang/pt-br.json +++ /dev/null @@ -1,2043 +0,0 @@ -{ - "addon.badges.alignment": "Alinhamento", - "addon.badges.badgedetails": "Detalhes do emblema", - "addon.badges.badges": "Emblemas", - "addon.badges.bendorsement": "Endosso", - "addon.badges.claimcomment": "Comentário de endosso", - "addon.badges.claimid": "URL do crédito", - "addon.badges.contact": "Contato", - "addon.badges.dateawarded": "Data de emissão", - "addon.badges.expired": "Expirado", - "addon.badges.expirydate": "Data de validade", - "addon.badges.imageauthoremail": "E-mail do autor da imagem", - "addon.badges.imageauthorname": "Nome do autor da imagem", - "addon.badges.imageauthorurl": "URL do autor da imagem", - "addon.badges.imagecaption": "Legenda da imagem", - "addon.badges.issuancedetails": "Expiração do emblema", - "addon.badges.issuerdetails": "Detalhes do emissor", - "addon.badges.issueremail": "Email", - "addon.badges.issuername": "Nome do emissor", - "addon.badges.issuerurl": "URL do emissor", - "addon.badges.language": "Idioma", - "addon.badges.noalignment": "Este emblema não possui habilidades ou padrões externos especificados.", - "addon.badges.nobadges": "Não há emblemas disponíveis.", - "addon.badges.norelated": "Este emblema não possui nenhum emblema relacionado.", - "addon.badges.recipientdetails": "Detalhes do usuário a receber o emblema", - "addon.badges.relatedbages": "Emblemas relacionados", - "addon.badges.version": "Versão", - "addon.badges.warnexpired": "(Este emblema expirou!)", - "addon.block_activitymodules.pluginname": "Atividades", - "addon.block_activityresults.pluginname": "Resultados de atividade", - "addon.block_badges.pluginname": "Últimos emblemas", - "addon.block_blogmenu.pluginname": "Menu do blog", - "addon.block_blogrecent.pluginname": "Entradas recentes do blog", - "addon.block_blogtags.pluginname": "Tags do blog", - "addon.block_calendarmonth.pluginname": "Calendário", - "addon.block_calendarupcoming.pluginname": "Próximos eventos", - "addon.block_comments.pluginname": "Comentários", - "addon.block_completionstatus.pluginname": "Estado da conclusão do curso", - "addon.block_glossaryrandom.pluginname": "Entrada aleatória do glossário", - "addon.block_learningplans.pluginname": "Planos de aprendizagem", - "addon.block_myoverview.all": "Todos (exceto ocultos)", - "addon.block_myoverview.allincludinghidden": "Todos", - "addon.block_myoverview.favourites": "Favoritos", - "addon.block_myoverview.future": "Não iniciados", - "addon.block_myoverview.hiddencourses": "Oculto", - "addon.block_myoverview.inprogress": "Em andamento", - "addon.block_myoverview.lastaccessed": "Acessados por último", - "addon.block_myoverview.morecourses": "Mais cursos", - "addon.block_myoverview.nocourses": "Não há cursos", - "addon.block_myoverview.past": "Encerrados", - "addon.block_myoverview.pluginname": "Resumo dos cursos", - "addon.block_myoverview.title": "Nome do curso", - "addon.block_newsitems.pluginname": "Últimos avisos", - "addon.block_onlineusers.pluginname": "Usuários Online", - "addon.block_privatefiles.pluginname": "Arquivos privados", - "addon.block_recentactivity.pluginname": "Atividade recente", - "addon.block_recentlyaccessedcourses.nocourses": "Não há cursos acessados recentemente", - "addon.block_recentlyaccessedcourses.pluginname": "Cursos acessados recentemente", - "addon.block_recentlyaccesseditems.noitems": "Não há itens acessados recentemente", - "addon.block_recentlyaccesseditems.pluginname": "Itens acessados recentemente", - "addon.block_rssclient.pluginname": "Alimentadores RSS remotos", - "addon.block_selfcompletion.pluginname": "Auto-completar", - "addon.block_sitemainmenu.pluginname": "Menu Principal", - "addon.block_starredcourses.nocourses": "Não há cursos favoritos", - "addon.block_starredcourses.pluginname": "Cursos favoritos", - "addon.block_tags.pluginname": "Tags", - "addon.block_timeline.duedate": "Prazo", - "addon.block_timeline.next30days": "Próximos 30 dias", - "addon.block_timeline.next3months": "Próximos 3 meses", - "addon.block_timeline.next6months": "Próximos 6 meses", - "addon.block_timeline.next7days": "Próximos 7 dias", - "addon.block_timeline.nocoursesinprogress": "Não há cursos em andamento", - "addon.block_timeline.noevents": "Nenhuma atividade com prazo", - "addon.block_timeline.overdue": "Atrasado", - "addon.block_timeline.pluginname": "Linha do tempo", - "addon.block_timeline.sortbycourses": "Ordenar por cursos", - "addon.block_timeline.sortbydates": "Ordenar por data", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Mensagens do blog", - "addon.blog.errorloadentries": "Erro ao carregar registros do blog", - "addon.blog.linktooriginalentry": "Aponta para a mensagem original do blog", - "addon.blog.noentriesyet": "Nenhum ítem visível", - "addon.blog.publishtonoone": "Rascunho (você mesmo)", - "addon.blog.publishtosite": "Todos os usuários deste site", - "addon.blog.publishtoworld": "Todo o mundo", - "addon.blog.showonlyyourentries": "Mostrar somente seus registros", - "addon.blog.siteblogheading": "Blog do site", - "addon.calendar.allday": "Dia Todo", - "addon.calendar.calendar": "Calendário", - "addon.calendar.calendarevents": "Eventos do calendário", - "addon.calendar.calendarreminders": "Lembretes do calendário", - "addon.calendar.categoryevents": "Eventos de categorias", - "addon.calendar.confirmeventdelete": "Você tem certeza de que deseja excluir o evento \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "O evento \"{{$a.name}}\" é parte de uma série. Você quer deletar apenas este evento ou todos os {{$a.count}} eventos da série?", - "addon.calendar.courseevents": "Eventos do curso", - "addon.calendar.daynext": "Próximo dia", - "addon.calendar.dayprev": "Dia anterior", - "addon.calendar.defaultnotificationtime": "Tempo padrão de notificação", - "addon.calendar.deleteallevents": "Excluir todos eventos", - "addon.calendar.deleteevent": "Excluir evento", - "addon.calendar.deleteoneevent": "Excluir este evento", - "addon.calendar.durationminutes": "Duração em minutos", - "addon.calendar.durationnone": "Sem duração", - "addon.calendar.durationuntil": "Até", - "addon.calendar.editevent": "Editando evento", - "addon.calendar.errorloadevent": "Erro ao carregar evento.", - "addon.calendar.errorloadevents": "Erro ao carregar eventos.", - "addon.calendar.eventcalendareventdeleted": "Evento excluído do calendário", - "addon.calendar.eventduration": "Duração", - "addon.calendar.eventendtime": "Hora final", - "addon.calendar.eventkind": "Tipo de evento", - "addon.calendar.eventname": "Nome do evento", - "addon.calendar.eventstarttime": "Hora inicial", - "addon.calendar.eventtype": "Tipo de evento", - "addon.calendar.fri": "Sex", - "addon.calendar.friday": "Sexta-feira", - "addon.calendar.gotoactivity": "Ir à atividade", - "addon.calendar.groupevents": "Eventos do grupo", - "addon.calendar.invalidtimedurationminutes": "A duração em minutos inserida é inválida. Por favor, insira uma duração em minutos maior que zero ou selecione sem duração.", - "addon.calendar.invalidtimedurationuntil": "A data e hora selecionados para duração é anterior ao início do evento. Por favor corrija isto antes de prosseguir.", - "addon.calendar.mon": "Seg", - "addon.calendar.monday": "Segunda-feira", - "addon.calendar.monthlyview": "Visualizar mês", - "addon.calendar.newevent": "Novo evento", - "addon.calendar.noevents": "Sem eventos", - "addon.calendar.nopermissiontoupdatecalendar": "Desculpe, mas você não tem permissões para atualizar os eventos do calendário.", - "addon.calendar.reminders": "Lembretes", - "addon.calendar.repeatedevents": "Eventos repetidos", - "addon.calendar.repeateditall": "Aplicar alterações a todos os {{$a}} eventos desta série de repetições", - "addon.calendar.repeateditthis": "Aplicar mudanças apenas a este evento", - "addon.calendar.repeatevent": "Repetir este evento", - "addon.calendar.repeatweeksl": "Repetir semanalmente, criar todas de uma vez", - "addon.calendar.sat": "Sáb", - "addon.calendar.saturday": "Sábado", - "addon.calendar.setnewreminder": "Definir um novo lembrete", - "addon.calendar.siteevents": "Eventos de site", - "addon.calendar.sun": "Dom", - "addon.calendar.sunday": "Domingo", - "addon.calendar.thu": "Qui", - "addon.calendar.thursday": "Quinta-feira", - "addon.calendar.today": "Hoje", - "addon.calendar.tomorrow": "Amanhã", - "addon.calendar.tue": "Ter", - "addon.calendar.tuesday": "Terça-feira", - "addon.calendar.typecategory": "Evento de categoria", - "addon.calendar.typeclose": "Evento fechado", - "addon.calendar.typecourse": "Evento do curso", - "addon.calendar.typedue": "Evento de entrega de atividade", - "addon.calendar.typegradingdue": "Evento de avaliação", - "addon.calendar.typegroup": "Evento do grupo", - "addon.calendar.typeopen": "Abrir evento", - "addon.calendar.typesite": "Evento do site", - "addon.calendar.typeuser": "Evento do usuário", - "addon.calendar.upcomingevents": "Próximos Eventos", - "addon.calendar.userevents": "Eventos do usuário", - "addon.calendar.wed": "Qua", - "addon.calendar.wednesday": "Quarta-feira", - "addon.calendar.when": "Quando", - "addon.calendar.yesterday": "Ontem", - "addon.competency.activities": "Atividades", - "addon.competency.competencies": "Competências", - "addon.competency.competenciesmostoftennotproficientincourse": "Competências mais frequentemente improficiente neste curso", - "addon.competency.coursecompetencies": "Competências do curso", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Avaliações de competência neste curso não afetam os planos de aprendizagem.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Avaliações de competência neste curso são atualizadas imediatamente nos planos de aprendizagem.", - "addon.competency.crossreferencedcompetencies": "Competências referenciadas", - "addon.competency.duedate": "Data de entrega", - "addon.competency.errornocompetenciesfound": "Nenhuma competência encontrada", - "addon.competency.evidence": "Evidência", - "addon.competency.evidence_competencyrule": "A regra da competência foi cumprida.", - "addon.competency.evidence_coursecompleted": "O curso '{{$a}}' foi concluído.", - "addon.competency.evidence_coursemodulecompleted": "A atividade '{{$a}}' foi concluída.", - "addon.competency.evidence_courserestored": "A classificação foi restaurada juntamente com o curso '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Evidência de aprendizagem prévia '{{$a}}' ativada.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Evidência de aprendizagem prévia '{{$a}}' desativada.", - "addon.competency.evidence_manualoverride": "A classificação de competência foi definida manualmente.", - "addon.competency.evidence_manualoverrideincourse": "A classificação de competência foi definida manualmente no curso '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "A classificação de competência foi definida manualmente no plano de aprendizado '{{$a}}'.", - "addon.competency.learningplancompetencies": "Competências do plano de aprendizagem", - "addon.competency.learningplans": "Planos de aprendizagem", - "addon.competency.myplans": "Meus planos de aprendizagem", - "addon.competency.noactivities": "Sem atividades", - "addon.competency.nocompetencies": "Nenhuma competência", - "addon.competency.nocompetenciesincourse": "Nenhuma competência foi vinculada a este curso.", - "addon.competency.nocrossreferencedcompetencies": "Nenhuma outra competência foi referenciada a esta competência.", - "addon.competency.noevidence": "Nenhuma evidência", - "addon.competency.noplanswerecreated": "Nenhum plano de aprendizagem foi criado.", - "addon.competency.nouserplanswithcompetency": "Nenhum plano de aprendizagem contém essa competência.", - "addon.competency.path": "Caminho:", - "addon.competency.planstatusactive": "Ativo", - "addon.competency.planstatuscomplete": "Concluído", - "addon.competency.planstatusdraft": "Rascunho", - "addon.competency.planstatusinreview": "Em revisão", - "addon.competency.planstatuswaitingforreview": "Aguardando revisão", - "addon.competency.proficient": "Proficiente", - "addon.competency.progress": "Progresso", - "addon.competency.rating": "Avaliação", - "addon.competency.reviewstatus": "Estado da revisão", - "addon.competency.status": "Estado", - "addon.competency.template": "Modelo de plano de aprendizagem", - "addon.competency.uponcoursecompletion": "Após conclusão do curso:", - "addon.competency.usercompetencystatus_idle": "inativo", - "addon.competency.usercompetencystatus_inreview": "Em revisão", - "addon.competency.usercompetencystatus_waitingforreview": "Esperando por revisão", - "addon.competency.userplans": "Planos de aprendizado", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} de {{$a.y}} competências são proficiêntes", - "addon.competency.xcompetenciesproficientoutofyincourse": "Você é proficiente em {{$a.x}} de {{$a.y}} competências neste curso.", - "addon.coursecompletion.complete": "Concluído", - "addon.coursecompletion.completecourse": "Clique aqui para marcar o curso como completo", - "addon.coursecompletion.completed": "Concluído", - "addon.coursecompletion.completiondate": "Data de conclusão", - "addon.coursecompletion.completionmenuitem": "Conclusão", - "addon.coursecompletion.couldnotloadreport": "Não foi possível carregar o relatório de conclusão do curso, por favor tente novamente mais tarde.", - "addon.coursecompletion.coursecompletion": "Conclusão de curso", - "addon.coursecompletion.criteria": "Critérios", - "addon.coursecompletion.criteriagroup": "Grupo de critérios", - "addon.coursecompletion.criteriarequiredall": "Todos os critérios abaixo são necessários", - "addon.coursecompletion.criteriarequiredany": "Qualquer um dos critérios abaixo são necessários", - "addon.coursecompletion.inprogress": "Em andamento", - "addon.coursecompletion.manualselfcompletion": "Conclusão manual por si mesmo", - "addon.coursecompletion.nottracked": "Você não está sendo monitorado pelo sistema de conclusão neste curso", - "addon.coursecompletion.notyetstarted": "Não iniciado ainda", - "addon.coursecompletion.pending": "Pendentes", - "addon.coursecompletion.required": "Necessários", - "addon.coursecompletion.requiredcriteria": "Critérios exigidos", - "addon.coursecompletion.requirement": "Requisito", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Ver relatório do curso", - "addon.files.couldnotloadfiles": "A lista de arquivos não pode ser carregada.", - "addon.files.emptyfilelist": "Não há arquivos para mostrar.", - "addon.files.erroruploadnotworking": "Infelizmente é impossível enviar arquivos para o seu site.", - "addon.files.files": "Arquivos", - "addon.files.privatefiles": "Arquivos privados", - "addon.files.sitefiles": "Arquivos do site", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Configurar dispositivos", - "addon.messages.acceptandaddcontact": "Aceitar e adicionar aos contatos", - "addon.messages.addcontact": "Acrescentar Contato", - "addon.messages.addcontactconfirm": "Tem certeza de que deseja adicionar {{$a}} aos seus contatos?", - "addon.messages.addtofavourites": "Favoritar conversa", - "addon.messages.addtoyourcontacts": "Adicionar aos contatos", - "addon.messages.blocknoncontacts": "Bloquear todas as mensagens de quem não estiver na minha lista de contatos", - "addon.messages.blockuser": "Bloquear usuário", - "addon.messages.blockuserconfirm": "Tem certeza de que deseja bloquear {{$a}}?", - "addon.messages.contactableprivacy": "Aceitar mensagens de:", - "addon.messages.contactableprivacy_coursemember": "Meus contatos e qualquer pessoa em meus cursos", - "addon.messages.contactableprivacy_onlycontacts": "Apenas meus contatos", - "addon.messages.contactableprivacy_site": "Qualquer pessoa no site", - "addon.messages.contactblocked": "Contato bloqueado", - "addon.messages.contactlistempty": "A lista de contatos está vaiza", - "addon.messages.contactname": "Nome do contato", - "addon.messages.contactrequestsent": "Solicitação de contato enviado", - "addon.messages.contacts": "Contatos", - "addon.messages.conversationactions": "Menu de ações de conversa", - "addon.messages.decline": "Recusar", - "addon.messages.deleteallconfirm": "Você tem certeza que deseja excluir toda essa conversa? A conversa não será excluída para outros participantes.", - "addon.messages.deleteallselfconfirm": "Tem certeza de que deseja excluir toda a conversa pessoal?", - "addon.messages.deleteconversation": "Apagar conversa", - "addon.messages.deleteforeveryone": "Excluir para mim e para todos os outros", - "addon.messages.deletemessage": "Excluir mensagem", - "addon.messages.deletemessageconfirmation": "Você tem certeza que quer excluir esta mensagem? Ela será excluída somente do seu histórico de mensagens, mas ainda poderá ser visualizada pelo usuário que enviou ou recebeu a mensagem.", - "addon.messages.errordeletemessage": "Erro enquanto excluía a mensagem.", - "addon.messages.errorwhileretrievingcontacts": "Erro ao recuperar contatos a partir do servidor.", - "addon.messages.errorwhileretrievingdiscussions": "Erro ao recuperar discussão do servidor.", - "addon.messages.errorwhileretrievingmessages": "Erro ao recuperar as mensagens do servidor.", - "addon.messages.errorwhileretrievingusers": "Erro ao recuperar usuários do servidor.", - "addon.messages.groupconversations": "Grupo", - "addon.messages.groupinfo": "Informação do Grupo", - "addon.messages.individualconversations": "Privado", - "addon.messages.info": "Informação de usuário", - "addon.messages.isnotinyourcontacts": "{{$a}} não está nos seus contatos", - "addon.messages.message": "Mensagem", - "addon.messages.messagenotsent": "A mensagem não foi enviada. Por favor tente novamente mais tarde.", - "addon.messages.messagepreferences": "Preferências de mensagens", - "addon.messages.messages": "Mensagens", - "addon.messages.muteconversation": "Mudo", - "addon.messages.mutedconversation": "Conversa silenciada", - "addon.messages.newmessage": "Nova Mensagem", - "addon.messages.newmessages": "Novas mensagens", - "addon.messages.nocontactrequests": "Sem solicitações de contatos", - "addon.messages.nocontactsgetstarted": "Sem contatos", - "addon.messages.nofavourites": "Nenhuma conversa favoritada", - "addon.messages.nogroupconversations": "Nenhuma conversa em grupo", - "addon.messages.noindividualconversations": "Nenhuma conversa privada", - "addon.messages.nomessagesfound": "Não foram encontradas mensagens", - "addon.messages.noncontacts": "Não contatos", - "addon.messages.nousersfound": "Nenhum usuário encontrado", - "addon.messages.numparticipants": "{{$a}} participantes", - "addon.messages.removecontact": "Eliminar contato", - "addon.messages.removecontactconfirm": "Tem certeza de que deseja remover {{$a}} dos seus contatos?", - "addon.messages.removefromfavourites": "Remover estrela da conversa", - "addon.messages.removefromyourcontacts": "Excluir dos contatos", - "addon.messages.requests": "Solicitações", - "addon.messages.requirecontacttomessage": "Você precisa solicitar {{$a}} que te adicione como contato. Só assim você conseguirá enviar uma mensagem para ele.", - "addon.messages.searchcombined": "Pesquisar pessoas e mensagens", - "addon.messages.selfconversation": "Espaço pessoal", - "addon.messages.selfconversationdefaultmessage": "Salve rascunhos de mensagens, links, notas etc. para acessar mais tarde.", - "addon.messages.sendcontactrequest": "Enviar solicitação de contato", - "addon.messages.showdeletemessages": "Mostrar as mensagens excluídas", - "addon.messages.type_blocked": "Bloqueado", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Resultados de busca", - "addon.messages.type_strangers": "Outros", - "addon.messages.unabletomessage": "Você não consegue enviar mensagens para esse usuário", - "addon.messages.unblockuser": "Desbloquear usuário", - "addon.messages.unblockuserconfirm": "Tem certeza de que deseja desbloquear {{$a}}?", - "addon.messages.unmuteconversation": "Ativar som", - "addon.messages.useentertosend": "Use 'enter' para enviar", - "addon.messages.useentertosenddescdesktop": "Se desabilitado, Você pode utilizar Ctrl+Enter para enviar a mensagem.", - "addon.messages.useentertosenddescmac": "Se desabilitado, Você pode utilizar Command+Enter para enviar a mensagem.", - "addon.messages.userwouldliketocontactyou": "{{$a}} gostaria de entrar em contato com você", - "addon.messages.warningconversationmessagenotsent": "Não foi possível enviar as mensagens para a conversa {{conversation}}. {{erro}}", - "addon.messages.warningmessagenotsent": "Não pode enviar mensagem(s) para o usuário {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Gostaria de entrar em contato com você", - "addon.messages.you": "Você:", - "addon.messages.youhaveblockeduser": "Você bloqueou este usuário.", - "addon.messages.yourcontactrequestpending": "Sua solicitação de contato está pendente com {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Por favor aceite a declaração do envio.", - "addon.mod_assign.addattempt": "Permitir outra tentativa", - "addon.mod_assign.addnewattempt": "Adicionar uma nova tentativa", - "addon.mod_assign.addnewattemptfromprevious": "Adicionar uma nova tentativa baseada na tentativa anterior", - "addon.mod_assign.addsubmission": "Adicionar tarefa", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Os detalhes da tarefa e formulário de envio ficarão disponíveis a partir de {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Permite envios a partir de", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Esta tarefa aceitará envios a partir de {{$a}}", - "addon.mod_assign.applytoteam": "Habilitar notas e feedback para todo grupo", - "addon.mod_assign.assignmentisdue": "Tarefa encerrada", - "addon.mod_assign.attemptnumber": "Número da tentativa", - "addon.mod_assign.attemptreopenmethod": "Tentativas reabertas", - "addon.mod_assign.attemptreopenmethod_manual": "Manualmente", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automaticamente até que passe", - "addon.mod_assign.attemptsettings": "Configurações de tentativa", - "addon.mod_assign.cannoteditduetostatementsubmission": "Você não pode adicionar ou editar um envio no aplicativo porque nós não conseguimos receber a declaração de envio do site.", - "addon.mod_assign.cannotgradefromapp": "Alguns métodos de avaliação ainda não são suportadas pelo aplicativo e não podem ser modificadas.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Você não pode enviar para avaliação pelo aplicativo porque nós não conseguimos receber a declaração de envio do site.", - "addon.mod_assign.confirmsubmission": "Tem certeza de que deseja enviar seu trabalho para a classificação? Você não será capaz de fazer mais modificações.", - "addon.mod_assign.currentattempt": "Esta é a tentativa {{$a}} .", - "addon.mod_assign.currentattemptof": "Esta é a tentativa {{$a.attemptnumber}} ({{$a.maxattempts}} tentativas permitidas).", - "addon.mod_assign.currentgrade": "Nota atual no livro de notas", - "addon.mod_assign.cutoffdate": "Data limite", - "addon.mod_assign.defaultteam": "Grupo padrão", - "addon.mod_assign.duedate": "Data de entrega", - "addon.mod_assign.duedateno": "Sem data de entrega", - "addon.mod_assign.duedatereached": "A data de entrega desta tarefa já passou", - "addon.mod_assign.editingstatus": "Editar estado", - "addon.mod_assign.editsubmission": "Editar envio", - "addon.mod_assign.erroreditpluginsnotsupported": "Você não pode adicionar ou editar um envio no aplicativo por causa que alguns plugins não são suportados para edição:", - "addon.mod_assign.errorshowinginformation": "Nós não podemos exibir as informações do envio", - "addon.mod_assign.extensionduedate": "Extensão da data de entrega", - "addon.mod_assign.feedbacknotsupported": "Esse feedback não é suportado pelo aplicativo e pode não conter todas as informações", - "addon.mod_assign.grade": "Nota", - "addon.mod_assign.graded": "Avaliado", - "addon.mod_assign.gradedby": "Avaliado por", - "addon.mod_assign.gradedfollowupsubmit": "Avaliado - envio recebido posteriormente", - "addon.mod_assign.gradedon": "Avaliado em", - "addon.mod_assign.gradelocked": "Esta nota está bloqueada ou sobreposta no livro de notas.", - "addon.mod_assign.gradenotsynced": "Avaliação não sincronizada", - "addon.mod_assign.gradeoutof": "Nota até {{$a}}", - "addon.mod_assign.gradingstatus": "Status da avaliação", - "addon.mod_assign.groupsubmissionsettings": "Configurações de envio em grupo", - "addon.mod_assign.hiddenuser": "Participante", - "addon.mod_assign.latesubmissions": "Submissões atrasadas", - "addon.mod_assign.latesubmissionsaccepted": "Permitido até {{$a}}", - "addon.mod_assign.markingworkflowstate": "Estado do fluxo de avaliação", - "addon.mod_assign.markingworkflowstateinmarking": "Em avaliação", - "addon.mod_assign.markingworkflowstateinreview": "Em revisão", - "addon.mod_assign.markingworkflowstatenotmarked": "Não avaliado", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Pronto para publicação", - "addon.mod_assign.markingworkflowstatereadyforreview": "Avaliação concluída", - "addon.mod_assign.markingworkflowstatereleased": "Publicado", - "addon.mod_assign.modulenameplural": "Tarefas", - "addon.mod_assign.multipleteams": "Membro de mais de um grupo", - "addon.mod_assign.multipleteams_desc": "Esta atividade requer envio em grupos. Você é membro de mais de um grupo. Para poder realizar o envio você deve fazer parte de apenas um grupo. Por favor, contate seu professor para mudar seu grupo.", - "addon.mod_assign.noattempt": "Nenhuma tentativa", - "addon.mod_assign.nomoresubmissionsaccepted": "Só é permitido para os participantes que tenham beneficiado de uma extensão", - "addon.mod_assign.noonlinesubmissions": "Esta tarefa não requer o envio online", - "addon.mod_assign.nosubmission": "Nada foi enviado para esta tarefa", - "addon.mod_assign.notallparticipantsareshown": "Participantes sem permissão não são exibidos", - "addon.mod_assign.noteam": "Você não é membro de nenhum grupo.", - "addon.mod_assign.noteam_desc": "Esta atividade requer envio em grupos. Você não é membro de nenhum grupo, então você não pode fazer um envio. Por favor, contate seu professor para te adicionar em um grupo.", - "addon.mod_assign.notgraded": "Não há notas", - "addon.mod_assign.numberofdraftsubmissions": "Rascunhos", - "addon.mod_assign.numberofparticipants": "Participantes", - "addon.mod_assign.numberofsubmissionsneedgrading": "Precisa de avaliação", - "addon.mod_assign.numberofsubmittedassignments": "Enviado", - "addon.mod_assign.numberofteams": "Grupos", - "addon.mod_assign.numwords": "{{$a}} palavras", - "addon.mod_assign.outof": "{{$a.current}} de {{$a.total}}", - "addon.mod_assign.overdue": "A tarefa está atrasada há: {{$a}}", - "addon.mod_assign.submission": "Envio de tarefas", - "addon.mod_assign.submissioneditable": "Estudantes podem editar essa submissão", - "addon.mod_assign.submissionnoteditable": "Estudante não pode editar este envio", - "addon.mod_assign.submissionnotsupported": "Esse envio não é suportado pelo aplicativo e pode não conter todas as informações", - "addon.mod_assign.submissionslocked": "A tarefa não está aceitando envios", - "addon.mod_assign.submissionstatus": "Status de envio", - "addon.mod_assign.submissionstatus_": "Nenhum envio", - "addon.mod_assign.submissionstatus_draft": "Rascunho (não enviado)", - "addon.mod_assign.submissionstatus_marked": "Avaliado", - "addon.mod_assign.submissionstatus_new": "Nenhum envio", - "addon.mod_assign.submissionstatus_reopened": "Reaberto", - "addon.mod_assign.submissionstatus_submitted": "Enviado para avaliação", - "addon.mod_assign.submissionstatusheading": "Status de envio", - "addon.mod_assign.submissionteam": "Grupo", - "addon.mod_assign.submitassignment": "Enviar tarefa em definitivo", - "addon.mod_assign.submitassignment_help": "Uma vez que a tarefa for enviada você não poderá mais fazer alterações.", - "addon.mod_assign.submittedearly": "A tarefa foi enviada {{$a}} adiantado", - "addon.mod_assign.submittedlate": "A tarefa foi enviada {{$a}} atrasada", - "addon.mod_assign.timemodified": "Última modificação", - "addon.mod_assign.timeremaining": "Tempo restante", - "addon.mod_assign.ungroupedusers": "A configuração \"Somente membros de grupo podem fazer envio\" está habilitada e alguns usuários não são membros de nenhum grupo ou são membros de mais de um grupo, portanto, não conseguem fazer envios.", - "addon.mod_assign.unlimitedattempts": "Ilimitado", - "addon.mod_assign.userswhoneedtosubmit": "Usuários que precisam enviar: {{$a}}", - "addon.mod_assign.userwithid": "Usuário com id {{id}}", - "addon.mod_assign.viewsubmission": "Ver envio", - "addon.mod_assign.warningsubmissiongrademodified": "A nota do envio foi modificada no site.", - "addon.mod_assign.warningsubmissionmodified": "O envio do usuário foi modificado no site.", - "addon.mod_assign.wordlimit": "Limite de palavras", - "addon.mod_assign_feedback_comments.pluginname": "Comentários de feedback", - "addon.mod_assign_feedback_editpdf.pluginname": "Anotar PDF", - "addon.mod_assign_feedback_file.pluginname": "Arquivo de feedback", - "addon.mod_assign_submission_comments.pluginname": "Comentários sobre o envio", - "addon.mod_assign_submission_file.pluginname": "Envio de arquivos", - "addon.mod_assign_submission_onlinetext.pluginname": "Envio de texto online", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "O limite de palavras para esta tarefa é de {{$a.limit}} palavras e você está tentando enviar {{$a.count}} palavras. Por favor, reveja o seu envio e tente novamente.", - "addon.mod_book.errorchapter": "Erro ao ler capítulo de livro.", - "addon.mod_book.modulenameplural": "Livros", - "addon.mod_book.navnexttitle": "Próximo: {{$a}}", - "addon.mod_book.navprevtitle": "Anterior: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Capítulos do livro", - "addon.mod_book.toc": "Sumário", - "addon.mod_chat.beep": "Bip", - "addon.mod_chat.chatreport": "Sessões de chat", - "addon.mod_chat.currentusers": "Usuários atuais", - "addon.mod_chat.enterchat": "Clique aqui para entrar no chat agora", - "addon.mod_chat.entermessage": "Digite sua mensagem", - "addon.mod_chat.errorwhileconnecting": "Erro ao se conectar.", - "addon.mod_chat.errorwhilegettingchatdata": "Erro ao se conectar ao chat.", - "addon.mod_chat.errorwhilegettingchatusers": "Erro ao obter os usuários de bate-papo.", - "addon.mod_chat.errorwhileretrievingmessages": "Erro ao recuperar as mensagens do servidor.", - "addon.mod_chat.errorwhilesendingmessage": "Erro ao enviar a mensagem.", - "addon.mod_chat.messagebeepseveryone": "{{$a}}", - "addon.mod_chat.messagebeepsyou": "{{$a}} está bipando você!", - "addon.mod_chat.messageenter": "{{$a}} entrou no chat", - "addon.mod_chat.messageexit": "{{$a}} abandonou este chat", - "addon.mod_chat.messages": "Mensagens", - "addon.mod_chat.messageyoubeep": "Você chamou {{$a}}", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "Você deve estar online para enviar mensagens", - "addon.mod_chat.nomessages": "Nenhuma mensagem ainda", - "addon.mod_chat.nosessionsfound": "Nenhuma sessão encontrada", - "addon.mod_chat.saidto": "disse para", - "addon.mod_chat.send": "Enviar", - "addon.mod_chat.sessionstart": "A próxima sessão de chat irá começar em {{$a.date}}, ({{$a.fromnow}} a partir de agora)", - "addon.mod_chat.showincompletesessions": "Mostrar sessões não concluídas", - "addon.mod_chat.talk": "Falar", - "addon.mod_chat.viewreport": "Ver sessões encerradas", - "addon.mod_choice.cannotsubmit": "Desculpe, houve um problema ao enviar sua escolha. Por favor, tente novamente.", - "addon.mod_choice.choiceoptions": "Opções de escolha", - "addon.mod_choice.errorgetchoice": "Erro ao obter dados do escolha.", - "addon.mod_choice.expired": "Esta atividade foi encerrada em {{$a}}.", - "addon.mod_choice.full": "(Cheio)", - "addon.mod_choice.modulenameplural": "Escolhas", - "addon.mod_choice.noresultsviewable": "Não é possível visualizar os resultados neste momento.", - "addon.mod_choice.notopenyet": "Esta atividade não está disponível até {{$a}}", - "addon.mod_choice.numberofuser": "Número de respostas", - "addon.mod_choice.numberofuserinpercentage": "Porcentagem de respostas", - "addon.mod_choice.previewonly": "Esta é somente uma previsualização das opções dessa atividade. Você somente poderá enviar sua escolha a partir de {{$a}}", - "addon.mod_choice.publishinfoanonafter": "Resultados anônimos serão publicados após sua resposta.", - "addon.mod_choice.publishinfoanonclose": "Resultados anônimos serão publicados após a atividade ser fechada.", - "addon.mod_choice.publishinfofullafter": "Os resultados completos, mostrando as escolhas de todos, serão publicados após a resposta.", - "addon.mod_choice.publishinfofullclose": "Os resultados completos, mostrando as escolhas de todos, serão publicados após o encerramento da atividade.", - "addon.mod_choice.publishinfonever": "Os resultados dessa atividade não serão publicados após sua resposta.", - "addon.mod_choice.removemychoice": "Remover a minha resposta", - "addon.mod_choice.responses": "Respostas", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% de usuários que escolheram a opção: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Visualização do gráfico", - "addon.mod_choice.resultsnotsynced": "Sua última resposta deve ser sincronizada antes de ser incluída nos resultados.", - "addon.mod_choice.savemychoice": "Gravar a minha resposta", - "addon.mod_choice.userchoosethisoption": "Usuários que escolheram esta opção", - "addon.mod_choice.yourselection": "A sua escolha", - "addon.mod_data.addentries": "Acrescentar itens", - "addon.mod_data.advancedsearch": "Busca avançada", - "addon.mod_data.alttext": "Texto alternativo", - "addon.mod_data.approve": "Aprovar", - "addon.mod_data.approved": "Aprovado", - "addon.mod_data.ascending": "Crescente", - "addon.mod_data.authorfirstname": "Nome do autor", - "addon.mod_data.authorlastname": "Sobrenome do autor", - "addon.mod_data.confirmdeleterecord": "Tem certeza que quer excluir este item?", - "addon.mod_data.descending": "Decrescente", - "addon.mod_data.disapprove": "Desfazer aprovação", - "addon.mod_data.emptyaddform": "Você não completou nenhum campo!", - "addon.mod_data.entrieslefttoadd": "Você precisa adicionar mais {{$a.entriesleft}} item(ns) para completar esta atividade", - "addon.mod_data.entrieslefttoaddtoview": "Você precisa adicionar mais {{$a.entrieslefttoview}} item(ns) antes de poder ver os itens dos outros participantes.", - "addon.mod_data.errorapproving": "Erro ao aprovar ou desaprovar o registro.", - "addon.mod_data.errordeleting": "Erro ao excluir o registro.", - "addon.mod_data.errormustsupplyvalue": "Você precisa fornecer um valor aqui.", - "addon.mod_data.expired": "Sinto muito, mas esta atividade foi fechada em {{$a}} e não está mais disponível", - "addon.mod_data.fields": "Campos", - "addon.mod_data.foundrecords": "Encontrados: {{$a.num}}/{{$a.max}} (Reconfigurar filtros)", - "addon.mod_data.latlongboth": "Tanto a Latitude quanto a Longitude devem ser preenchidas.", - "addon.mod_data.menuchoose": "Selecionar...", - "addon.mod_data.modulenameplural": "Bases de dados", - "addon.mod_data.more": "Mais", - "addon.mod_data.nomatch": "Nenhum item correspondente encontrado!", - "addon.mod_data.norecords": "Nenhum item na base de dados", - "addon.mod_data.notapproved": "O item ainda não foi aprovado.", - "addon.mod_data.notopenyet": "Desculpe, esta atividade não está disponível até {{$a}}", - "addon.mod_data.numrecords": "{{$a}} itens", - "addon.mod_data.other": "Outro", - "addon.mod_data.recordapproved": "Item aprovado", - "addon.mod_data.recorddeleted": "Item excluído", - "addon.mod_data.recorddisapproved": "Item não aprovado", - "addon.mod_data.resetsettings": "Reconfigurar filtros", - "addon.mod_data.search": "Busca", - "addon.mod_data.selectedrequired": "Todos os itens selecionados são obrigatórios", - "addon.mod_data.single": "Ver item único", - "addon.mod_data.tagarea_data_records": "Registros de dados", - "addon.mod_data.timeadded": "Tempo adicionado", - "addon.mod_data.timemodified": "Tempo modificado", - "addon.mod_data.usedate": "Incluir na busca.", - "addon.mod_feedback.analysis": "Análise", - "addon.mod_feedback.anonymous": "Anônimo", - "addon.mod_feedback.anonymous_entries": "Entradas anônimas ({{$a}})", - "addon.mod_feedback.average": "Média", - "addon.mod_feedback.captchaofflinewarning": "A pesquisa com CAPTCHA não pode ser concluída off-line, se não estiver configurada ou se o servidor estiver inativo.", - "addon.mod_feedback.complete_the_form": "Responda as questões", - "addon.mod_feedback.completed_feedbacks": "Respostas submetidas", - "addon.mod_feedback.continue_the_form": "Continuar respondendo as questões", - "addon.mod_feedback.feedback_is_not_open": "A pesquisa não está aberta", - "addon.mod_feedback.feedback_submitted_offline": "Esta pesquisa foi salva para ser enviada posteriormente.", - "addon.mod_feedback.feedbackclose": "Permitir respostas até", - "addon.mod_feedback.feedbackopen": "Permitir respostas de", - "addon.mod_feedback.mapcourses": "Mapear pesquisa para os cursos", - "addon.mod_feedback.maximal": "Máximo", - "addon.mod_feedback.minimal": "Mínimo", - "addon.mod_feedback.mode": "Modo", - "addon.mod_feedback.modulenameplural": "Pesquisa", - "addon.mod_feedback.next_page": "Próxima página", - "addon.mod_feedback.non_anonymous": "O nome do usuário será registrado e mostrado com as respostas", - "addon.mod_feedback.non_anonymous_entries": "Entradas não anônimas ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Estudantes não respondentes ({{$a}})", - "addon.mod_feedback.not_selected": "Não selecionado", - "addon.mod_feedback.not_started": "Não iniciado", - "addon.mod_feedback.numberoutofrange": "Valor fora do intervalo", - "addon.mod_feedback.overview": "Visão geral", - "addon.mod_feedback.page_after_submit": "Mensagem de conclusão", - "addon.mod_feedback.preview": "Pré visualizar", - "addon.mod_feedback.previous_page": "Página anterior", - "addon.mod_feedback.questions": "Questões", - "addon.mod_feedback.response_nr": "Resposta numérica", - "addon.mod_feedback.responses": "Respostas", - "addon.mod_feedback.save_entries": "Submeter as suas respostas", - "addon.mod_feedback.show_entries": "Mostrar respostas", - "addon.mod_feedback.show_nonrespondents": "Mostrar não respondentes", - "addon.mod_feedback.started": "iniciado", - "addon.mod_feedback.this_feedback_is_already_submitted": "Você já terminou esta atividade.", - "addon.mod_folder.emptyfilelist": "Não há arquivos para mostrar", - "addon.mod_folder.modulenameplural": "Pastas", - "addon.mod_forum.addanewdiscussion": "Acrescentar um novo tópico de discussão", - "addon.mod_forum.addanewquestion": "Acrescentar uma nova questão", - "addon.mod_forum.addanewtopic": "Acrescentar um novo tópico", - "addon.mod_forum.addtofavourites": "Marcar esta discussão", - "addon.mod_forum.advanced": "Avançado", - "addon.mod_forum.cannotadddiscussion": "Apenas os participantes inscritos nos grupos podem escrever mensagens neste fórum.", - "addon.mod_forum.cannotadddiscussionall": "Você não tem permissão para abrir um novo tópico de discussão para todos os participantes.", - "addon.mod_forum.cannotcreatediscussion": "Não foi possível criar uma nova discussão", - "addon.mod_forum.couldnotadd": "Não foi possível publicar a sua mensagem. Infelizmente a causa do erro não foi identificada.", - "addon.mod_forum.couldnotupdate": "Não foi possível atualizar a sua mensagem. Infelizmente a causa do erro não foi identificada.", - "addon.mod_forum.cutoffdatereached": "A data limite para postagem neste fórum foi atingida, portanto, você não poderá mais postar nela.", - "addon.mod_forum.delete": "Excluir", - "addon.mod_forum.deletedpost": "A mensagem foi apagada", - "addon.mod_forum.deletesure": "Você tem certeza que quer excluir esta mensagem?", - "addon.mod_forum.discussion": "Tópico", - "addon.mod_forum.discussionlistsortbycreatedasc": "Ordenar por data de criação em ordem crescente", - "addon.mod_forum.discussionlistsortbycreateddesc": "Ordenar por data de criação por ordem descendente", - "addon.mod_forum.discussionlistsortbylastpostasc": "Ordenar pela última data de criação da postagem, por ordem crescente", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Classificar pela última data de criação da postagem em ordem decrescente", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Ordenar por número de respostas em ordem ascendente", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Ordenar por número de respostas em ordem decrescente", - "addon.mod_forum.discussionlocked": "Esta discussão foi bloqueada e já não é possível responder a ela.", - "addon.mod_forum.discussionpinned": "Destacado", - "addon.mod_forum.discussionsubscription": "Assinatura de discussão", - "addon.mod_forum.edit": "Editar", - "addon.mod_forum.erroremptymessage": "A mensagem não pode estar vazia.", - "addon.mod_forum.erroremptysubject": "O assunto da mensagem não pode ser vazio.", - "addon.mod_forum.errorgetforum": "Erro ao buscar dados de fórum.", - "addon.mod_forum.errorgetgroups": "Erro ao obter configurações do grupo.", - "addon.mod_forum.errorposttoallgroups": "Não foi possível criar nova discussão em todos os grupos.", - "addon.mod_forum.favouriteupdated": "Sua opção de favoritar foi atualizada.", - "addon.mod_forum.forumnodiscussionsyet": "Não existem tópicos neste fórum ainda", - "addon.mod_forum.group": "Grupo", - "addon.mod_forum.lastpost": "Última mensagem", - "addon.mod_forum.lockdiscussion": "Bloquear esta discussão", - "addon.mod_forum.lockupdated": "A opção de bloqueio foi atualizada.", - "addon.mod_forum.message": "Mensagem", - "addon.mod_forum.modeflatnewestfirst": "Mostrar respostas começando pela mais recente", - "addon.mod_forum.modeflatoldestfirst": "Mostrar respostas começando pela mais antiga", - "addon.mod_forum.modenested": "Mostrar respostas aninhadas", - "addon.mod_forum.modulenameplural": "Fóruns", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discussões", - "addon.mod_forum.numreplies": "{{numreplies}} respostas", - "addon.mod_forum.pindiscussion": "Fixar esta discussão", - "addon.mod_forum.pinupdated": "A opção de fixar foi atualizada.", - "addon.mod_forum.postisprivatereply": "Esta é uma resposta privada. Não é visível para outros participantes.", - "addon.mod_forum.posttoforum": "Enviar mensagem ao fórum", - "addon.mod_forum.posttomygroups": "Enviar uma cópia a todos os grupos", - "addon.mod_forum.privatereply": "Resposta privada", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Atualizar discussões", - "addon.mod_forum.refreshposts": "Atualizar as postagens da discussão", - "addon.mod_forum.removefromfavourites": "Desfavoritar esta discussão", - "addon.mod_forum.reply": "Responder", - "addon.mod_forum.replyplaceholder": "Escreva sua resposta ...", - "addon.mod_forum.subject": "Assunto", - "addon.mod_forum.tagarea_forum_posts": "Posts do fórum", - "addon.mod_forum.thisforumhasduedate": "A data limite para postagem neste fórum é {{$a}}.", - "addon.mod_forum.thisforumisdue": "A data limite para postagem neste fórum foi {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Desbloquear este tópico", - "addon.mod_forum.unpindiscussion": "Desmarcar este tópico", - "addon.mod_forum.unread": "Não lida", - "addon.mod_forum.unreadpostsnumber": "{{$a}} mensagens não lidas", - "addon.mod_forum.yourreply": "A sua resposta", - "addon.mod_glossary.addentry": "Inserir novo item", - "addon.mod_glossary.aliases": "Outras palavras que serão linkadas ao mesmo item", - "addon.mod_glossary.attachment": "Anexo", - "addon.mod_glossary.browsemode": "Navegar pelos registros", - "addon.mod_glossary.byalphabet": "Alfabeticamente", - "addon.mod_glossary.byauthor": "Agrupar por autor", - "addon.mod_glossary.bycategory": "Agrupar por categoria", - "addon.mod_glossary.bynewestfirst": "Os mais novos primeiro", - "addon.mod_glossary.byrecentlyupdated": "Recentemente atualizados", - "addon.mod_glossary.bysearch": "Pesquisa", - "addon.mod_glossary.cannoteditentry": "Não é possível editar o item", - "addon.mod_glossary.casesensitive": "Item sensível à distinção entre maiúsculas e minúsculas", - "addon.mod_glossary.categories": "Categorias", - "addon.mod_glossary.concept": "Conceito", - "addon.mod_glossary.definition": "Definição", - "addon.mod_glossary.entriestobesynced": "Itens a serem sincronizados", - "addon.mod_glossary.entrypendingapproval": "A entrada está pendente de aprovação.", - "addon.mod_glossary.entryusedynalink": "Link automático", - "addon.mod_glossary.errconceptalreadyexists": "Este conceito já existe. Não é permitida a criação de outras versões.", - "addon.mod_glossary.errorloadingentries": "Ocorreu um erro enquanto carregava entradas.", - "addon.mod_glossary.errorloadingentry": "Ocorreu um erro enquanto carregava a entrada.", - "addon.mod_glossary.errorloadingglossary": "Ocorreu um erro enquanto carregava a entrada.", - "addon.mod_glossary.fillfields": "Conceito e definição são campos obrigatórios.", - "addon.mod_glossary.fullmatch": "Criar links apenas a partir de palavras inteiras", - "addon.mod_glossary.linking": "Auto-link", - "addon.mod_glossary.modulenameplural": "Glossários", - "addon.mod_glossary.noentriesfound": "Nenhuma entrada foi encontrada", - "addon.mod_glossary.searchquery": "Consulta de pesquisa", - "addon.mod_glossary.tagarea_glossary_entries": "Itens do Glossário", - "addon.mod_imscp.deploymenterror": "Erro no conte!", - "addon.mod_imscp.modulenameplural": "Conteúdo dos pacotes IMS", - "addon.mod_imscp.showmoduledescription": "Mostrar descrição", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "Resposta", - "addon.mod_lesson.attempt": "Tentativa: {{$a}}", - "addon.mod_lesson.attemptheader": "Tentativa", - "addon.mod_lesson.attemptsremaining": "Você tem ainda {{$a}} tentativas", - "addon.mod_lesson.averagescore": "Pontuação média", - "addon.mod_lesson.averagetime": "Tempo médio", - "addon.mod_lesson.branchtable": "Painel de Navegação", - "addon.mod_lesson.cannotfindattempt": "Erro: não foi possível encontrar a tentativa", - "addon.mod_lesson.cannotfinduser": "Erro: não foi possível encontrar os usuários", - "addon.mod_lesson.clusterjump": "Questão não vista em um cluster", - "addon.mod_lesson.completed": "Concluída", - "addon.mod_lesson.congratulations": "Você chegou ao fim desta lição", - "addon.mod_lesson.continue": "Continuar", - "addon.mod_lesson.continuetonextpage": "Continuar para a próxima página.", - "addon.mod_lesson.defaultessayresponse": "Sua dissertação será avaliada pelo seu professor.", - "addon.mod_lesson.detailedstats": "Estatísticas detalhadas", - "addon.mod_lesson.didnotanswerquestion": "Não respondeu esta questão.", - "addon.mod_lesson.displayofgrade": "Visualização das notas (apenas para estudantes)", - "addon.mod_lesson.displayscorewithessays": "

                    Você recebeu {{$a.score}} de um total de {{$a.tempmaxgrade}} nas questões avaliadas automaticamente.

                    \n

                    A(s) sua(s) {{$a.essayquestions}} questão(ões) dissertativa(s) será(ão) avaliada(s) e somada(s) ao seu resultado final em uma data posterior. \n

                    Sua nota atual, sem a(s) questão(ões) dissertativa(s), é {{$a.score}} de um total de {{$a.grade}}>/p>", - "addon.mod_lesson.displayscorewithoutessays": "A sua pontuação é {{$a.score}} (de {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Senha não pode ser vazia", - "addon.mod_lesson.enterpassword": "Inserir a senha:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Você não respondeu nenhuma questão. A nota atribuída foi igual a 0 .", - "addon.mod_lesson.errorprefetchrandombranch": "Esta lição contém um salto para uma página de conteúdo aleatória. Não pode ser realizada no aplicativo até que tenha sido iniciada em um navegador da web", - "addon.mod_lesson.errorreviewretakenotlast": "Essa tentativa não pode mais ser revisada porque outra tentativa foi finalizada.", - "addon.mod_lesson.finish": "Finalizado", - "addon.mod_lesson.finishretakeoffline": "Esta tentativa foi finalizada offline.", - "addon.mod_lesson.firstwrong": "Você respondeu incorretamente. Você gostaria de tentar novamente a questão? (Se você responder a pergunta corretamente, sua pontuação final não será alterada.)", - "addon.mod_lesson.gotoendoflesson": "Ir para o final da lição", - "addon.mod_lesson.grade": "Avaliação", - "addon.mod_lesson.highscore": "Pontuação alta", - "addon.mod_lesson.hightime": "Tempo alto", - "addon.mod_lesson.leftduringtimed": "Você interrompeu uma lição com tempo de duração definido.
                    Por favor clique em Continuar para recomeçar a lição.", - "addon.mod_lesson.leftduringtimednoretake": "Você interrompeu uma lição com tempo de duração definido.
                    Não será possível continuar ou recomeçar.", - "addon.mod_lesson.lessonmenu": "Menu da lição", - "addon.mod_lesson.lessonstats": "Estatísticas da Lição", - "addon.mod_lesson.linkedmedia": "Arquivo multimídia vinculado", - "addon.mod_lesson.loginfail": "Erro de login, por favor tente novamente...", - "addon.mod_lesson.lowscore": "Pontuação baixa", - "addon.mod_lesson.lowtime": "Tempo breve", - "addon.mod_lesson.maximumnumberofattemptsreached": "Número máximo de tentativas atingido - indo para a próxima página", - "addon.mod_lesson.modattemptsnoteacher": "A revisão dos estudantes só é ativa para eles.", - "addon.mod_lesson.modulenameplural": "Lições", - "addon.mod_lesson.noanswer": "Uma ou mais questões estão sem resposta. Por favor, volte e envie uma resposta.", - "addon.mod_lesson.nolessonattempts": "Nenhuma tentativa feita nesta lição.", - "addon.mod_lesson.nolessonattemptsgroup": "Não há tentativas feitas por membros do grupo {{$a}} nesta lição.", - "addon.mod_lesson.notcompleted": "Não concluída", - "addon.mod_lesson.numberofcorrectanswers": "Número de respostas corretas: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Número de páginas vistas: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Número de questões respondidas {{$a.nquestions}}; (Você deve responder pelo menos: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Esta lição corresponde a {{$a.score}} pontos. Você recebeu {{$a.score}} ponto(s) de um total de {{$a.currenthigh}} pontos até agora.", - "addon.mod_lesson.ongoingnormal": "Você respondeu corretamente {{$a.correct}} questões de um total de {{$a.viewed}} .", - "addon.mod_lesson.or": "OU", - "addon.mod_lesson.overview": "Visão geral", - "addon.mod_lesson.preview": "Visualizar", - "addon.mod_lesson.progressbarteacherwarning2": "Você não verá a barra de progresso porque você pode editar esta lição.", - "addon.mod_lesson.progresscompleted": "Você completou {{$a}}% da lição", - "addon.mod_lesson.question": "Questão", - "addon.mod_lesson.rawgrade": "Nota não ponderada", - "addon.mod_lesson.reports": "Relatórios", - "addon.mod_lesson.response": "Retorno", - "addon.mod_lesson.retakefinishedinsync": "Uma tentativa offline foi sincronizada. Você quer revisá-la?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Revisão", - "addon.mod_lesson.reviewlesson": "Rever Lição", - "addon.mod_lesson.reviewquestionback": "Sim, eu gostaria de tentar novamente", - "addon.mod_lesson.reviewquestioncontinue": "Não, quero continuar com a próxima questão", - "addon.mod_lesson.secondpluswrong": "Não é exato. Você quer tentar novamente?", - "addon.mod_lesson.submit": "Enviar", - "addon.mod_lesson.teacherjumpwarning": "Um destino {{$a.cluster}} ou um destino {{$a.unseen}} está sendo usado nesta lição. O destino Próxima Página substituirá o anterior. Faça o login como estudante para testar estes destinos.", - "addon.mod_lesson.teacherongoingwarning": "Para testar a pontuação corrente é necessário fazer o login como estudante.", - "addon.mod_lesson.teachertimerwarning": "O temporizador funciona somente para estudantes. Teste o temporizador acessando como estudante.", - "addon.mod_lesson.thatsthecorrectanswer": "Esta é a resposta correta", - "addon.mod_lesson.thatsthewronganswer": "Esta é a resposta errada", - "addon.mod_lesson.timeremaining": "Tempo restante", - "addon.mod_lesson.timetaken": "Tempo utilizado", - "addon.mod_lesson.unseenpageinbranch": "Questão não visualizada dentro de uma página de conteúdo", - "addon.mod_lesson.warningretakefinished": "A tentativa foi finalizada no site.", - "addon.mod_lesson.welldone": "Muito bem!", - "addon.mod_lesson.youhaveseen": "Você já visitou algumas páginas desta lição.
                    Você quer iniciar a partir da última página visitada?", - "addon.mod_lesson.youranswer": "A sua resposta", - "addon.mod_lesson.yourcurrentgradeisoutof": "A sua nota atual é {{$a.grade}} sobre {{$a.total}}", - "addon.mod_lesson.youshouldview": "Você deve responder pelo menos: {{$a}}", - "addon.mod_lti.errorgetlti": "Erro ao obter os dados do módulo.", - "addon.mod_lti.errorinvalidlaunchurl": "O URL de início não é válido.", - "addon.mod_lti.launchactivity": "Iniciar a atividade", - "addon.mod_lti.modulenameplural": "Ferramentas externas", - "addon.mod_page.errorwhileloadingthepage": "Erro ao carregar o conteúdo da página.", - "addon.mod_page.modulenameplural": "Páginas", - "addon.mod_quiz.answercolon": "Resposta:", - "addon.mod_quiz.attemptfirst": "Primeira tentativa", - "addon.mod_quiz.attemptlast": "Última tentativa", - "addon.mod_quiz.attemptnumber": "Tentativa", - "addon.mod_quiz.attemptquiznow": "Tentar responder o questionário agora", - "addon.mod_quiz.attemptstate": "Estado", - "addon.mod_quiz.cannotsubmitquizdueto": "A tentativa do questionário não pode ser enviada pelas seguintes razões:", - "addon.mod_quiz.clearchoice": "Limpar minha escolha", - "addon.mod_quiz.comment": "Comentário", - "addon.mod_quiz.completedon": "Concluída em", - "addon.mod_quiz.confirmclose": "Uma vez enviada, você não poderá alterar as respostas para esta tentativa.", - "addon.mod_quiz.confirmcontinueoffline": "A tentativa não foi sincronizada desde {{$a}}. Se você continuou essa tentativa em outro dispositivo desde então, você poderá perder dados.", - "addon.mod_quiz.confirmleavequizonerror": "Ocorreu um erro ao salvar as respostas. Você tem certeza que quer sair do questionário?", - "addon.mod_quiz.confirmstart": "O questionário tem um limite de tempo de {{$a}}. O cronômetro começará a contar a partir do momento em que iniciar a tentativa e deverá submetê-la antes do tempo acabar. Tem certeza que quer iniciar a tentativa agora?", - "addon.mod_quiz.confirmstartheader": "Questionário com limite de tempo", - "addon.mod_quiz.connectionerror": "Conexão de rede perdida. (Salvamento automático falhou). Anote quaisquer respostas registradas nesta página nos últimos minutos e tente conectar-se novamente. Quando a conexão for restabelecida, suas respostas devem ser salvas e esta mensagem irá desaparecer.", - "addon.mod_quiz.continueattemptquiz": "Continuar a última tentativa", - "addon.mod_quiz.continuepreview": "Continuar a última prévia", - "addon.mod_quiz.errorbehaviournotsupported": "Esse questionário não pode ser realizado no aplicativo porque o funcionamento da questão não é suportado pelo aplicativo:", - "addon.mod_quiz.errordownloading": "Erro ao baixar dados necessários.", - "addon.mod_quiz.errorgetattempt": "Erro ao receber dados da tentativa.", - "addon.mod_quiz.errorgetquestions": "Erro ao receber as questões.", - "addon.mod_quiz.errorgetquiz": "Erro ao receber dados do questionário.", - "addon.mod_quiz.errorparsequestions": "Um erro aconteceu enquanto ocorria a leitura das questões. Por favor, tente fazer esse questionário no navegador web.", - "addon.mod_quiz.errorquestionsnotsupported": "Esse questionário não pode ser tentado no aplicativo porque possui questões que não são suportadas pelo aplicativo:", - "addon.mod_quiz.errorrulesnotsupported": "Esse questionário não pode ser tentado no aplicativo porque possui regras de acessos não suportadas pelo aplicativo:", - "addon.mod_quiz.errorsaveattempt": "Aconteceu um erro ao salvar os dados da tentativa.", - "addon.mod_quiz.feedback": "Comentários", - "addon.mod_quiz.finishattemptdots": "Tentativas encerradas", - "addon.mod_quiz.finishnotsynced": "Finalizado, mas não sincronizado", - "addon.mod_quiz.grade": "Avaliar", - "addon.mod_quiz.gradeaverage": "Nota média", - "addon.mod_quiz.gradehighest": "Nota mais alta", - "addon.mod_quiz.grademethod": "Método de avaliação", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Notas", - "addon.mod_quiz.modulenameplural": "Questionários", - "addon.mod_quiz.mustbesubmittedby": "Esta tentativa precisa ser enviada até {{$a}}.", - "addon.mod_quiz.noquestions": "Ainda não foi inserida nenhuma pergunta", - "addon.mod_quiz.noreviewattempt": "Você não tem permissão para revisar esta tentativa.", - "addon.mod_quiz.notyetgraded": "Ainda não avaliado", - "addon.mod_quiz.opentoc": "Abrir dicas de navegação.", - "addon.mod_quiz.outof": "{{$a.grade}} de um máximo de {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} de um máximo de {{$a.maxgrade}}({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Feedback geral", - "addon.mod_quiz.overdue": "Expirado", - "addon.mod_quiz.overduemustbesubmittedby": "Esta tentativa está vencida. Ela já deveria ter sido enviada. Se você quiser que este questionário seja avaliado, você deve enviá-lo até {{$a}}. Se você não enviar até até lá, nenhuma nota dessa tentativa será levada em conta.", - "addon.mod_quiz.preview": "Visualização prévia", - "addon.mod_quiz.previewquiznow": "Pré-visualizar questionário agora", - "addon.mod_quiz.question": "Pergunta", - "addon.mod_quiz.quiznavigation": "Navegação do questionário", - "addon.mod_quiz.quizpassword": "Senha do questionário", - "addon.mod_quiz.reattemptquiz": "Fazer uma outra tentativa", - "addon.mod_quiz.requirepasswordmessage": "Este questionário requer o uso de uma senha específica", - "addon.mod_quiz.returnattempt": "Retornar à tentativa", - "addon.mod_quiz.review": "Revisão", - "addon.mod_quiz.reviewofattempt": "Revisão da tentativa {{$a}}", - "addon.mod_quiz.reviewofpreview": "Revisão da prévia", - "addon.mod_quiz.showall": "Mostrar todas as questões em uma página", - "addon.mod_quiz.showeachpage": "Mostrar uma página por vez", - "addon.mod_quiz.startattempt": "Iniciar tentativa", - "addon.mod_quiz.startedon": "Iniciado em", - "addon.mod_quiz.stateabandoned": "Nunca enviadas", - "addon.mod_quiz.statefinished": "Finalizada", - "addon.mod_quiz.statefinisheddetails": "Enviada(s) {{$a}}", - "addon.mod_quiz.stateinprogress": "Em progresso", - "addon.mod_quiz.stateoverdue": "Vencidas", - "addon.mod_quiz.stateoverduedetails": "Precisam ser enviadas até {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Enviar tudo e terminar", - "addon.mod_quiz.summaryofattempt": "Resumo de tentativas", - "addon.mod_quiz.summaryofattempts": "Resumo das suas tentativas anteriores", - "addon.mod_quiz.timeleft": "Tempo restante", - "addon.mod_quiz.timetaken": "Tempo empregado", - "addon.mod_quiz.warningattemptfinished": "A tentativa offline foi descartada pois já foi finalizada no site ou não foi encontrada.", - "addon.mod_quiz.warningdatadiscarded": "Algumas respostas offline foram descartadas por que as questões foram modificadas online.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Tentativa não foi finalizada porque algumas respostas offline foram descartadas. Por favor, revise suas respostas e depois entregue novamente a tentativa.", - "addon.mod_quiz.yourfinalgradeis": "A sua nota final neste questionário é {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Erro ao carregar conteúdo.", - "addon.mod_resource.modifieddate": "Modificado {{$a}}", - "addon.mod_resource.modulenameplural": "Arquivos", - "addon.mod_resource.openthefile": "Abrir o arquivo", - "addon.mod_resource.uploadeddate": "Carregado {{$a}}", - "addon.mod_scorm.asset": "Recurso", - "addon.mod_scorm.assetlaunched": "Recurso - Visto", - "addon.mod_scorm.attempts": "Tentativas", - "addon.mod_scorm.averageattempt": "Média de tentativas", - "addon.mod_scorm.browse": "Visitar", - "addon.mod_scorm.browsed": "Visto", - "addon.mod_scorm.browsemode": "Prévia", - "addon.mod_scorm.cannotcalculategrade": "Nota não pôde ser calculada.", - "addon.mod_scorm.completed": "Concluído", - "addon.mod_scorm.contents": "Conteúdo", - "addon.mod_scorm.dataattemptshown": "Estes dados pertence a tentativa número {{number}}.", - "addon.mod_scorm.enter": "Entrar", - "addon.mod_scorm.errorcreateofflineattempt": "Ocorreu um erro ao criar uma nova tentativa offline. Por favor, tente novamente.", - "addon.mod_scorm.errordownloadscorm": "Erro baixar SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Erro ao obter dados SCORM.", - "addon.mod_scorm.errorinvalidversion": "Infelizmente, o aplicativo só suporta SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "O download de pacotes SCORM está desabilitado no seu site Moodle. Por favor, contate o administrador do site Moodle.", - "addon.mod_scorm.errornovalidsco": "Este SCORM não tem um SCO visível para carregar.", - "addon.mod_scorm.errorpackagefile": "Infelizmente, o aplicativo suporta apenas pacotes ZIP.", - "addon.mod_scorm.errorsyncscorm": "Ocorreu um erro ao sincronizar. Por favor, tente novamente.", - "addon.mod_scorm.exceededmaxattempts": "Você atingiu a número máximo de tentativas.", - "addon.mod_scorm.failed": "Falhou", - "addon.mod_scorm.firstattempt": "Primeira tentativa", - "addon.mod_scorm.gradeaverage": "Média das notas", - "addon.mod_scorm.gradeforattempt": "Avaliação por tentativa", - "addon.mod_scorm.gradehighest": "Nota mais alta", - "addon.mod_scorm.grademethod": "Método de avaliação", - "addon.mod_scorm.gradereported": "Nota recebida", - "addon.mod_scorm.gradescoes": "Objetos de aprendizagem", - "addon.mod_scorm.gradesum": "Soma de notas", - "addon.mod_scorm.highestattempt": "Tentativa mais alta", - "addon.mod_scorm.incomplete": "Incompleto", - "addon.mod_scorm.lastattempt": "Última tentativa finalizada", - "addon.mod_scorm.modulenameplural": "SCORMs/AICCs", - "addon.mod_scorm.newattempt": "Começar uma nova tentativa", - "addon.mod_scorm.noattemptsallowed": "Número de tentativas permitidas", - "addon.mod_scorm.noattemptsmade": "Número de tentativas feitas", - "addon.mod_scorm.notattempted": "Nenhuma tentativa", - "addon.mod_scorm.offlineattemptnote": "Esta tentativa tem dados que não tem sido sincronizado.", - "addon.mod_scorm.offlineattemptovermax": "Esta tentativa não pode ser enviada porque você ultrapassou o número máximo de tentativas.", - "addon.mod_scorm.organizations": "Organizações", - "addon.mod_scorm.passed": "Aprovado", - "addon.mod_scorm.reviewmode": "Modalidade de revisão", - "addon.mod_scorm.score": "Resultado", - "addon.mod_scorm.scormstatusnotdownloaded": "Este SCORM não é baixado. Ele será baixado automaticamente quando você abri-lo.", - "addon.mod_scorm.scormstatusoutdated": "Este SCORM foi modificado desde o último download. Ele será baixado automaticamente quando você abri-lo.", - "addon.mod_scorm.suspended": "Suspenso", - "addon.mod_scorm.toc": "TOC", - "addon.mod_scorm.warningofflinedatadeleted": "Alguns dados offline da tentativa {{number}} foram excluídas porque ele não pôde ser criado em uma nova tentativa.", - "addon.mod_scorm.warningsynconlineincomplete": "Algumas tentativas não poderiam ser sincronizadas com o site porque a última tentativa não foi finalizada. Por favor termine a tentativa online primeiro.", - "addon.mod_survey.cannotsubmitsurvey": "Desculpe, houve um problema enviar a sua pesquisa. Por favor, tente novamente.", - "addon.mod_survey.errorgetsurvey": "Erro ao obter dados da pesquisa.", - "addon.mod_survey.ifoundthat": "Freqüência atual", - "addon.mod_survey.ipreferthat": "Freqüência desejada", - "addon.mod_survey.modulenameplural": "Pesquisas de avaliação", - "addon.mod_survey.responses": "Respostas", - "addon.mod_survey.results": "Resultados", - "addon.mod_survey.surveycompletednograph": "Você completou essa pesquisa.", - "addon.mod_url.accessurl": "Acessar URL", - "addon.mod_url.modulenameplural": "URLs", - "addon.mod_url.pointingtourl": "A URL desse recurso direciona para", - "addon.mod_wiki.cannoteditpage": "Você não pode editar esta página.", - "addon.mod_wiki.createpage": "Criar página", - "addon.mod_wiki.editingpage": "Editando a página '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Aconteceu um erro ao carregar a página.", - "addon.mod_wiki.errornowikiavailable": "Essa wiki não tem nenhum conteúdo ainda.", - "addon.mod_wiki.gowikihome": "Ir para o ínicio da Wiki", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Nova página", - "addon.mod_wiki.newpagetitle": "Novo título da página", - "addon.mod_wiki.nocontent": "Não existe conteúdo para esta página", - "addon.mod_wiki.notingroup": "Não existe no grupo", - "addon.mod_wiki.pageexists": "Esta página já existe.", - "addon.mod_wiki.pagename": "Nome da página", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Páginas wiki", - "addon.mod_wiki.titleshouldnotbeempty": "O título não pode ser vazio", - "addon.mod_wiki.viewpage": "Ver página", - "addon.mod_wiki.wikipage": "Página do Wiki", - "addon.mod_wiki.wrongversionlock": "Outro usuário editou esta página enquanto você estava editando e seu conteúdo é obsoleto.", - "addon.mod_workshop.alreadygraded": "Já foi avaliada", - "addon.mod_workshop.areainstructauthors": "Instruções para envio", - "addon.mod_workshop.areainstructreviewers": "Instruções para avaliação", - "addon.mod_workshop.assess": "Avaliar", - "addon.mod_workshop.assessedsubmission": "Envio avaliado", - "addon.mod_workshop.assessmentform": "Formulário de avaliação", - "addon.mod_workshop.assessmentsettings": "Configurações da avaliação", - "addon.mod_workshop.assessmentstrategynotsupported": "Estratégia de avaliação {{$a}} não suportada", - "addon.mod_workshop.assessmentweight": "Peso da avaliação", - "addon.mod_workshop.assignedassessments": "Envios atribuídos para avaliação", - "addon.mod_workshop.assignedassessmentsnone": "Você não recebeu nenhum envio para avaliar", - "addon.mod_workshop.conclusion": "Conclusão", - "addon.mod_workshop.createsubmission": "Adicionar envio", - "addon.mod_workshop.deletesubmission": "Excluir envio", - "addon.mod_workshop.editsubmission": "Editar envio", - "addon.mod_workshop.feedbackauthor": "Retorno para o autor", - "addon.mod_workshop.feedbackby": "Comentários por {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Retorno para o revisor", - "addon.mod_workshop.givengrades": "Notas dadas", - "addon.mod_workshop.gradecalculated": "Nota calculada para envio", - "addon.mod_workshop.gradeinfo": "Nota: {{$a.received}} de {{$a.max}}", - "addon.mod_workshop.gradeover": "Sobrescrever nota para envio", - "addon.mod_workshop.gradesreport": "Relatório de notas do laboratório de avaliação", - "addon.mod_workshop.gradinggrade": "Nota para avaliação", - "addon.mod_workshop.gradinggradecalculated": "Nota calculada para avaliação", - "addon.mod_workshop.gradinggradeof": "Nota para avaliação (de{{$a}})", - "addon.mod_workshop.gradinggradeover": "Sobrescrever nota para avaliação", - "addon.mod_workshop.modulenameplural": "Laboratórios de Avaliação", - "addon.mod_workshop.nogradeyet": "Nenhuma nota ainda", - "addon.mod_workshop.notassessed": "Nada avaliado ainda", - "addon.mod_workshop.notoverridden": "Não sobrescrito", - "addon.mod_workshop.noyoursubmission": "Você não enviou seu trabalho ainda", - "addon.mod_workshop.overallfeedback": "Feedback geral", - "addon.mod_workshop.publishedsubmissions": "Envios publicados", - "addon.mod_workshop.publishsubmission": "Publicar envios", - "addon.mod_workshop.publishsubmission_help": "Envios publicados ficam disponíveis aos outros quando o laboratório de avaliação for fechado.", - "addon.mod_workshop.reassess": "Reavaliar", - "addon.mod_workshop.receivedgrades": "Notas recebidas", - "addon.mod_workshop.submissionattachment": "Anexo", - "addon.mod_workshop.submissioncontent": "Conteúdo enviado", - "addon.mod_workshop.submissiondeleteconfirm": "Você tem certeza de que deseja excluir o envio a seguir?", - "addon.mod_workshop.submissiongrade": "Nota para envio", - "addon.mod_workshop.submissiongradeof": "Notar para envio (de{{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Você precisa inserir algum texto ou adicionar um arquivo.", - "addon.mod_workshop.submissionrequiredtitle": "Você precisa digitar um título.", - "addon.mod_workshop.submissionsreport": "Relatório de envios do laboratório de avaliação", - "addon.mod_workshop.submissiontitle": "Título", - "addon.mod_workshop.switchphase10": "Mudar para a fase de configuração", - "addon.mod_workshop.switchphase20": "Mudar para a fase de envio", - "addon.mod_workshop.switchphase30": "Mudar para a fase de avaliação", - "addon.mod_workshop.switchphase40": "Mudar para fase de cálculo da nota da avaliação", - "addon.mod_workshop.switchphase50": "Fechar o laboratório de avaliação", - "addon.mod_workshop.userplan": "Planejador do laboratório de avaliação", - "addon.mod_workshop.userplancurrentphase": "Fase atual", - "addon.mod_workshop.warningassessmentmodified": "O envio foi modificado no site.", - "addon.mod_workshop.warningsubmissionmodified": "A avaliação foi modificada no site.", - "addon.mod_workshop.weightinfo": "Peso: {{$a}}", - "addon.mod_workshop.yourassessment": "Sua avaliação", - "addon.mod_workshop.yourassessmentfor": "Sua avaliação para {{$a}}", - "addon.mod_workshop.yourgrades": "Suas notas", - "addon.mod_workshop.yoursubmission": "Seu envio", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Comentário para {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Nota para {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspecto {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Você deve selecionar uma nota para este aspecto", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Comentário para {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspecto {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Comentário para {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Nota para {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Afirmação {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Critério {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Você deve selecionar um desses itens", - "addon.notes.addnewnote": "Escrever uma nova anotação", - "addon.notes.coursenotes": "Anotações do Curso", - "addon.notes.deleteconfirm": "Excluir esta anotação?", - "addon.notes.eventnotecreated": "Anotação criada", - "addon.notes.eventnotedeleted": "Anotação excluída", - "addon.notes.nonotes": "Ainda não existe nenhuma anotação deste tipo", - "addon.notes.note": "Anotação", - "addon.notes.notes": "Anotações", - "addon.notes.personalnotes": "Anotações individuais", - "addon.notes.publishstate": "Estado", - "addon.notes.sitenotes": "Anotações do site", - "addon.notes.userwithid": "Usuário com o id {{id}}", - "addon.notes.warningnotenotsent": "Não foi possível adicionar anotação(s) ao curso {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Erro ao receber notificações", - "addon.notifications.markallread": "Marcar tudo como lido", - "addon.notifications.notificationpreferences": "Preferências de notificação", - "addon.notifications.notifications": "Notificação", - "addon.notifications.playsound": "Tocar som", - "addon.notifications.therearentnotificationsyet": "Não há notificações", - "addon.storagemanager.deletecourse": "Limpar dados de todos os cursos", - "addon.storagemanager.deletedatafrom": "Limpar dados de {{name}}", - "addon.storagemanager.info": "Os arquivos armazenados no seu dispositivo tornam o aplicativo mais rápido e permitem que o aplicativo seja usado off-line. Você pode limpar estes arquivos com segurança se precisar liberar espaço de armazenamento.", - "addon.storagemanager.managestorage": "Gerenciar armazenamento", - "addon.storagemanager.storageused": "Armazenamento de arquivos utilizado:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Emirados Árabes Unidos", - "assets.countries.AF": "Afeganistão", - "assets.countries.AG": "Antígua e Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albânia", - "assets.countries.AM": "Armênia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antártida", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa Americana", - "assets.countries.AT": "Áustria", - "assets.countries.AU": "Austrália", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "ilhas Åland", - "assets.countries.AZ": "Azerbaijão", - "assets.countries.BA": "Bósnia Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Bélgica", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgária", - "assets.countries.BH": "Barein", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "São Bartolomeu", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Bolívia, Estado Plurinacional da", - "assets.countries.BQ": "Bonaire, Santo Eustáquio e Saba", - "assets.countries.BR": "Brasil", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Butão", - "assets.countries.BV": "Ilhas Bouvet", - "assets.countries.BW": "Botsuana", - "assets.countries.BY": "Belarus", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canadá", - "assets.countries.CC": "Ilhas (Keeling) cocos", - "assets.countries.CD": "Congo, República Democrática do", - "assets.countries.CF": "República Centro-Africana", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Suíça", - "assets.countries.CI": "Costa do Marfim", - "assets.countries.CK": "Ilhas Cook", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Camarões", - "assets.countries.CN": "China", - "assets.countries.CO": "Colômbia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Cabo Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Ilhas Christmas", - "assets.countries.CY": "Chipre", - "assets.countries.CZ": "República Tcheca", - "assets.countries.DE": "Alemanha, República Federal da", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Dinamarca", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "República Dominicana", - "assets.countries.DZ": "Argélia", - "assets.countries.EC": "Equador", - "assets.countries.EE": "Estônia", - "assets.countries.EG": "Egito", - "assets.countries.EH": "Saara Ocidental", - "assets.countries.ER": "Eritréia", - "assets.countries.ES": "Espanha", - "assets.countries.ET": "Etiópia", - "assets.countries.FI": "Finlandia", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Ilhas Falkland (Malvinas)", - "assets.countries.FM": "Micronésia, Estados Federados da", - "assets.countries.FO": "Ilhas Faeroes", - "assets.countries.FR": "França", - "assets.countries.GA": "Gabão", - "assets.countries.GB": "Reino Unido da Grã-Bretanha e da Irlanda do Norte", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Geórgia", - "assets.countries.GF": "Guiana francesa", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Gana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Groenlândia", - "assets.countries.GM": "Gâmbia", - "assets.countries.GN": "Guiné", - "assets.countries.GP": "Guadalupe", - "assets.countries.GQ": "Guiné equatorial", - "assets.countries.GR": "Grécia", - "assets.countries.GS": "Ilhas Geórgia do Sul e Sandwich do Sul", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guiné Bissau", - "assets.countries.GY": "Guiana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Ilhas Heard e Mc Donald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Croácia", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Hungria", - "assets.countries.ID": "Indonésia", - "assets.countries.IE": "Irlanda (Eire)", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Ilha do Homem", - "assets.countries.IN": "Índia", - "assets.countries.IO": "Território Britânico do Oceano Índico", - "assets.countries.IQ": "Iraque", - "assets.countries.IR": "Irã (República Islâmica do)", - "assets.countries.IS": "Islândia", - "assets.countries.IT": "Itália", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordânia", - "assets.countries.JP": "Japão", - "assets.countries.KE": "Quênia", - "assets.countries.KG": "Quirguistão", - "assets.countries.KH": "Camboja", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comoros", - "assets.countries.KN": "São Cristovão e Nevis", - "assets.countries.KP": "Coréia do Norte (República Democrática Popular da Coréia)", - "assets.countries.KR": "Coreia do Sul (República da)", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Ilhas Cayman", - "assets.countries.KZ": "Casaquistão", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Líbano", - "assets.countries.LC": "Santa Lúcia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Libéria", - "assets.countries.LS": "Lesoto", - "assets.countries.LT": "Lituânia", - "assets.countries.LU": "Luxemburgo", - "assets.countries.LV": "Latvia", - "assets.countries.LY": "Líbia", - "assets.countries.MA": "Marrocos", - "assets.countries.MC": "Mônaco", - "assets.countries.MD": "Moldova (República da)", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "São Martinho (parte francesa)", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Ilhas Marshall", - "assets.countries.MK": "Macedônia do Norte", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar (Burma)", - "assets.countries.MN": "Mongólia", - "assets.countries.MO": "Macau", - "assets.countries.MP": "Ilhas Marianas do Norte", - "assets.countries.MQ": "Martinica", - "assets.countries.MR": "Mauritânia", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Maurício", - "assets.countries.MV": "Maldivas", - "assets.countries.MW": "Malavi", - "assets.countries.MX": "Mexico", - "assets.countries.MY": "Malásia", - "assets.countries.MZ": "Moçambique", - "assets.countries.NA": "Namíbia", - "assets.countries.NC": "Nova Caledônia", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Ilhas Norfolk", - "assets.countries.NG": "Nigéria", - "assets.countries.NI": "Nicarágua", - "assets.countries.NL": "Holanda", - "assets.countries.NO": "Noruega", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nova Zelândia", - "assets.countries.OM": "Omã", - "assets.countries.PA": "Panamá", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Polinésia Francesa", - "assets.countries.PG": "Papua Nova Guiné", - "assets.countries.PH": "Filipinas", - "assets.countries.PK": "Paquistão", - "assets.countries.PL": "Polônia", - "assets.countries.PM": "Saint-Pierre e Miquelon", - "assets.countries.PN": "Ilha Pitcairn", - "assets.countries.PR": "Porto Rico", - "assets.countries.PS": "Palestina, Estado de", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "República di Belau (Palau)", - "assets.countries.PY": "Paraguai", - "assets.countries.QA": "Quatar", - "assets.countries.RE": "Reunião", - "assets.countries.RO": "Romênia", - "assets.countries.RS": "Sérvia", - "assets.countries.RU": "Rússia (Federação Russa)", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Arábia Saudita", - "assets.countries.SB": "Ilhas Salomão", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudão", - "assets.countries.SE": "Suécia", - "assets.countries.SG": "Cingapura", - "assets.countries.SH": "Santa Helena, Ascensão e Tristão da Cunha", - "assets.countries.SI": "Eslovênia", - "assets.countries.SJ": "Ilhas Svalbard e Jan Mayen", - "assets.countries.SK": "Eslováquia", - "assets.countries.SL": "Serra Leoa", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somália", - "assets.countries.SR": "Suriname", - "assets.countries.SS": "Sudão do Sul", - "assets.countries.ST": "São Tomé e Príncipe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "São Martinho (parte holandesa)", - "assets.countries.SY": "Siria", - "assets.countries.SZ": "Eswatini", - "assets.countries.TC": "Ilhas Turcas e Caicos", - "assets.countries.TD": "Chade", - "assets.countries.TF": "Território Ultramarino das Terras Austrais e Antárticas Francesas", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tailandia", - "assets.countries.TJ": "Tadjiquistão", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turcomenistão", - "assets.countries.TN": "Tunísia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turquia", - "assets.countries.TT": "Trinidade e Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "República Unida da Tanzânia", - "assets.countries.UA": "Ucrânia", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Ilhas Menores Distantes dos Estados Unidos", - "assets.countries.US": "Estados Unidos da América", - "assets.countries.UY": "Uruguai", - "assets.countries.UZ": "Uzbequistão", - "assets.countries.VA": "Vaticano", - "assets.countries.VC": "São Vicente e Granadinas", - "assets.countries.VE": "Venezuela (República Bolivariana da)", - "assets.countries.VG": "Ilhas Virgens Britânicas", - "assets.countries.VI": "Ilhas Virgens Americanas", - "assets.countries.VN": "Vietnã", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Ilhas Wallis e Futuna", - "assets.countries.WS": "Samoa ocidental", - "assets.countries.YE": "Iêmen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "África do Sul", - "assets.countries.ZM": "Zâmbia", - "assets.countries.ZW": "Zimbábue", - "assets.mimetypes.application/epub_zip": "Ebook EPUB", - "assets.mimetypes.application/msword": "Documento Word", - "assets.mimetypes.application/pdf": "Documento PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Backup do Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Planilha Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Livro de trabalho habilitado para macro do Excel 2007", - "assets.mimetypes.application/vnd.ms-powerpoint": "Apresentação Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Planilha OpenDocument (.ods)", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Modelo de planilha OpenDocument (.ots)", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Documento de texto OpenDocument (.odt)", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Modelo de documento de texto OpenDocument (.ott)", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Modelo de página Web OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Apresentação Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Slideshow do Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Planilha do Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Template do Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Documento do Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Apresentação do iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Planilha do iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Documento do iWork Pages", - "assets.mimetypes.application/x-javascript": "Código JavaScript", - "assets.mimetypes.application/x-mspublisher": "Documento do Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animação Flash", - "assets.mimetypes.application/xhtml_xml": "Documento XHTML", - "assets.mimetypes.archive": "Archive ({{$a.EXT}})", - "assets.mimetypes.audio": "Arquivo de áudio ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Arquivo", - "assets.mimetypes.group:archive": "Tipos de arquivo", - "assets.mimetypes.group:audio": "Arquivos de áudio", - "assets.mimetypes.group:document": "Arquivos de documentos de texto", - "assets.mimetypes.group:html_audio": "Arquivos de áudio suportados nativamente pelos navegadores", - "assets.mimetypes.group:html_track": "Arquivos de faixa HTML", - "assets.mimetypes.group:html_video": "Arquivos de vídeo suportados nativamente pelos navegadores", - "assets.mimetypes.group:image": "Arquivos de imagem", - "assets.mimetypes.group:presentation": "Arquivos de apresentação", - "assets.mimetypes.group:sourcecode": "Código fonte", - "assets.mimetypes.group:spreadsheet": "Arquivos de planilha", - "assets.mimetypes.group:video": "Arquivos de vídeo", - "assets.mimetypes.group:web_audio": "Arquivos de áudio usados na Web", - "assets.mimetypes.group:web_file": "Arquivos Web", - "assets.mimetypes.group:web_image": "Arquivos de imagem usados na Web", - "assets.mimetypes.group:web_video": "Arquivos de vídeo usados na Web", - "assets.mimetypes.image": "Imagem ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Ícone do Windows", - "assets.mimetypes.text/css": "Arquivo CSS", - "assets.mimetypes.text/csv": "Arquivos CSV", - "assets.mimetypes.text/html": "Documento HTML", - "assets.mimetypes.text/plain": "Arquivo de texto", - "assets.mimetypes.text/rtf": "Documento RTF", - "assets.mimetypes.text/vtt": "Faixa de texto de vídeo da Web", - "assets.mimetypes.video": "Arquivo de vídeo ({{$a.EXT}})", - "core.accounts": "Contas", - "core.add": "Adicionar", - "core.agelocationverification": "Verificação de idade e localização", - "core.ago": "{{$a}} atrás", - "core.all": "Todos", - "core.allgroups": "Todos os grupos", - "core.allparticipants": "Todos os participantes", - "core.answer": "Resposta", - "core.answered": "Respondido", - "core.areyousure": "Você tem certeza?", - "core.back": "Voltar", - "core.block.blocks": "Blocos", - "core.cancel": "Cancelar", - "core.cannotconnect": "Não foi possível conectar: Verifique se você digitou a URL corretamente e se seu site usa o Moodle {{$a}} ou posterior.", - "core.cannotdownloadfiles": "Download de arquivos está desabilitado no seu serviço Mobile. Por favor, contate o administrador do site.", - "core.captureaudio": "Gravar áudio", - "core.capturedimage": "Fotografado", - "core.captureimage": "Fotografar", - "core.capturevideo": "Gravar vídeo", - "core.category": "Categoria", - "core.choose": "Escolher", - "core.choosedots": "Escolher...", - "core.clearsearch": "Limpar busca", - "core.clicktohideshow": "Clique para expandir ou contrair", - "core.clicktoseefull": "Clique para ver o conteúdo completo.", - "core.close": "Fechar", - "core.comments": "Comentários", - "core.comments.addcomment": "Adicionar um comentário...", - "core.comments.comments": "Comentários", - "core.comments.commentscount": "Comentários ({{$a}})", - "core.comments.deletecommentbyon": "Excluír o comentário publicado por {{$a.user}} em {{$a.time}}", - "core.comments.eventcommentcreated": "Comentário criado", - "core.comments.eventcommentdeleted": "Comentário excluído", - "core.comments.nocomments": "Nenhum comentário", - "core.comments.savecomment": "Salvar comentário", - "core.commentscount": "Comentários ({{$a}})", - "core.completion-alt-auto-fail": "Concluído: {{$a}} (não obteve nota para aprovação)", - "core.completion-alt-auto-n": "Não concluído(s): {{$a}}", - "core.completion-alt-auto-n-override": "Não concluído: {{$a.modname}} (por {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Concluído: {{$a}} (foi atingida a nota de aprovação)", - "core.completion-alt-auto-y": "Concluído: {{$a}}", - "core.completion-alt-auto-y-override": "Concluído: {{$a.modname}} (por {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Não concluído(s): {{$a}}. Selecione para marcar como concluído.", - "core.completion-alt-manual-n-override": "Não concluído: {{$a.modname}} (por {{$a.overrideuser}}). Selecione para marcar como concluído.", - "core.completion-alt-manual-y": "Concluído(s): {{$a}}. Selecione para marcar como não concluído.", - "core.completion-alt-manual-y-override": "Concluído: {{$a.modname}} (por {{$a.overrideuser}}). Selecione para marcar não concluído.", - "core.confirmcanceledit": "Você tem certeza que quer sair dessa página? Todas as mudanças serão perdidas.", - "core.confirmdeletefile": "Você tem certeza que quer excluir este arquivo?", - "core.confirmloss": "Você tem certeza? Todas as alterações serão perdidas.", - "core.confirmopeninbrowser": "Você quer abri-lo no navegador?", - "core.considereddigitalminor": "Você é muito jovem para criar uma conta neste site.", - "core.content": "Conteúdo", - "core.contenteditingsynced": "O conteúdo que você está editando foi sincronizado.", - "core.contentlinks.chooseaccount": "Escolha a conta", - "core.contentlinks.chooseaccounttoopenlink": "Escolha a conta que irá abrir o link", - "core.contentlinks.confirmurlothersite": "O link pertence a outro site. Você quer abrir-lo?", - "core.contentlinks.errornoactions": "Não foi possível encontrar uma ação para executar com este link.", - "core.contentlinks.errornosites": "Não foi possível encontrar qualquer site para lidar com este link.", - "core.contentlinks.errorredirectothersite": "O URL de redirecionamento não pode apontar para um site diferente.", - "core.continue": "Continuar", - "core.copiedtoclipboard": "Texto copiado para a área de transferência", - "core.course": "Curso", - "core.course.activitydisabled": "Sua organização desativou essa atividade no aplicativo para dispositivos móveis.", - "core.course.activitynotyetviewableremoteaddon": "Sua organização instalou um plugin que não é suportado ainda.", - "core.course.activitynotyetviewablesiteupgradeneeded": "O Moodle de sua organização precisa ser atualizado.", - "core.course.allsections": "Todas as seções", - "core.course.askadmintosupport": "Contate o administrador do site e diga que você deseja usar essa atividade no aplicativo Moodle Mobile.", - "core.course.availablespace": "Atualmente, você tem cerca de {{available}} de espaço livre.", - "core.course.confirmdeletemodulefiles": "Você tem certeza que quer excluir esse modulo arquivos?", - "core.course.confirmdownload": "Você está prestes a baixar {{size}}. Você tem certeza que quer continuar?", - "core.course.confirmdownloadunknownsize": "Não fomos capazes de calcular o tamanho do download. Tem certeza de que deseja fazer o download?", - "core.course.confirmlimiteddownload": "Atualmente, você não está conectado ao Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Você está prestes a baixar pelo menos {{size}}. Você tem certeza que quer continuar?", - "core.course.contents": "Conteúdos", - "core.course.couldnotloadsectioncontent": "Não foi possível carregar o conteúdo da seção, por favor tente mais tarde.", - "core.course.couldnotloadsections": "Não foi possível carregar a seção, por favor tente mais tarde.", - "core.course.coursesummary": "Sumário do curso", - "core.course.downloadcourse": "Download do curso", - "core.course.errordownloadingcourse": "Erro ao baixar o curso.", - "core.course.errordownloadingsection": "Erro ao baixar a seção.", - "core.course.errorgetmodule": "Erro ao obter os dados do módulo.", - "core.course.hiddenfromstudents": "Oculto para estudantes", - "core.course.hiddenoncoursepage": "Disponível mas não mostrado na página do curso", - "core.course.insufficientavailablequota": "Seu dispositivo não pôde alocar espaço para salvar este download. Reserve espaço para atualizações de aplicativos e do sistema. Por favor, limpe primeiro o espaço de armazenamento.", - "core.course.insufficientavailablespace": "Você está tentando baixar {{size}}. Isso deixará o dispositivo com espaço insuficiente para funcionar normalmente. Por favor, limpe primeiro o espaço de armazenamento.", - "core.course.manualcompletionnotsynced": "Conclusão manual não sincronizada.", - "core.course.nocontentavailable": "Nenhum conteúdo disponível nesse momento.", - "core.course.overriddennotice": "Sua nota final para esta atividade foi ajustada manualmente.", - "core.course.refreshcourse": "Atualizar curso", - "core.course.sections": "Seções", - "core.course.useactivityonbrowser": "Você ainda pode usar isso no navegador do seu dispositivo", - "core.course.warningmanualcompletionmodified": "A conclusão manual de uma atividade foi modificada no site.", - "core.course.warningofflinemanualcompletiondeleted": "Alguma conclusão manual off-line do curso '{{name}}' foi excluída. {{erro}}", - "core.coursedetails": "Detalhes do curso", - "core.courses.addtofavourites": "Favoritar esse curso", - "core.courses.allowguests": "Visitantes podem entrar neste curso", - "core.courses.availablecourses": "Cursos disponíveis", - "core.courses.cannotretrievemorecategories": "Categorias mais profundas que o nível {{$a}} não podem ser recuperadas.", - "core.courses.categories": "Categorias de Cursos", - "core.courses.confirmselfenrol": "Tem certeza de que deseja se inscrever neste curso?", - "core.courses.courses": "Cursos", - "core.courses.downloadcourses": "Baixar cursos", - "core.courses.enrolme": "Inscreva-me", - "core.courses.errorloadcategories": "Ocorreu um erro ao carregar categorias.", - "core.courses.errorloadcourses": "Um erro ocorreu enquanto os cursos estavam sendo carregados.", - "core.courses.errorloadplugins": "Os plugins necessários para este curso, não foram carregados corretamente. Por favor reinicie o aplicativo e tente novamente.", - "core.courses.errorsearching": "Ocorreu um erro durante a pesquisa.", - "core.courses.errorselfenrol": "Ocorreu um erro ao auto registo.", - "core.courses.filtermycourses": "Filtrar meus cursos", - "core.courses.frontpage": "Página principal", - "core.courses.hidecourse": "Ocultar a visualização", - "core.courses.ignore": "Ignorar", - "core.courses.mycourses": "Meus cursos", - "core.courses.mymoodle": "Painel", - "core.courses.nocourses": "Nenhuma informação disponível sobre o curso.", - "core.courses.nocoursesyet": "Nenhum curso nesta categoria", - "core.courses.nosearchresults": "Nenhum resultado", - "core.courses.notenroled": "Você não está inscrito como estudante neste curso", - "core.courses.notenrollable": "Você não pode inscrever-se neste curso.", - "core.courses.password": "Chave de inscrição", - "core.courses.paymentrequired": "Este curso requer o pagamento da taxa de inscrição antes do acesso.", - "core.courses.paypalaccepted": "Aceitamos pagamentos com PayPal", - "core.courses.reload": "Recarregar", - "core.courses.removefromfavourites": "Remover este curso dos favoritos", - "core.courses.search": "Buscar", - "core.courses.searchcourses": "Buscar cursos", - "core.courses.searchcoursesadvice": "Você pode usar o botão de busca para encontrar cursos a serem acessados como convidado ou se inscrever em cursos que permitam inscrição.", - "core.courses.selfenrolment": "Autoinscrição", - "core.courses.sendpaymentbutton": "Pagamento via Paypal", - "core.courses.show": "Mostrar este curso", - "core.courses.totalcoursesearchresults": "Total de cursos: {{$a}}", - "core.currentdevice": "Dispositivo atual", - "core.datastoredoffline": "Os dados foram guardados no dispositivo porque não foi possível enviar agora. Os dados serão automaticamente enviados mais tarde.", - "core.date": "Data", - "core.day": "dia", - "core.days": "dias", - "core.decsep": ",", - "core.defaultvalue": "Padrão ({{$a}})", - "core.delete": "Excluir", - "core.deletedoffline": "Offline excluído", - "core.deleteduser": "Usuário excluído", - "core.deleting": "Excluindo", - "core.description": "Descrição", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Menor digital", - "core.digitalminor_desc": "Por favor peça para seus pais/responsáveis contatarem:", - "core.discard": "Descartar", - "core.dismiss": "Dispersar", - "core.displayoptions": "Opções de exibição", - "core.done": "Feito", - "core.download": "Download", - "core.downloaded": "Baixado", - "core.downloading": "Baixando", - "core.edit": "Editar", - "core.editor.autosavesucceeded": "Rascunho salvo.", - "core.editor.bold": "Negrito", - "core.editor.clear": "Limpar formatação", - "core.editor.h3": "Cabeçalho (grande)", - "core.editor.h4": "Cabeçalho (médio)", - "core.editor.h5": "Cabeçalho (pequeno)", - "core.editor.italic": "Itálico", - "core.editor.orderedlist": "Lista ordenada", - "core.editor.p": "Parágrafo", - "core.editor.strike": "Riscar através", - "core.editor.textrecovered": "Uma versão anterior deste texto foi automaticamente restabelecida.", - "core.editor.underline": "Sublinhado", - "core.editor.unorderedlist": "Lista não ordenada", - "core.emptysplit": "Esta página aparecerá em branco se o painel esquerdo estiver vazio ou estiver carregando.", - "core.error": "Erro", - "core.errorchangecompletion": "Ocorreu um erro ao alterar o status de conclusão. Por favor, tente novamente.", - "core.errordeletefile": "Erro ao excluir o arquivo. Por favor tente novamente.", - "core.errordownloading": "Erro ao baixar o arquivo", - "core.errordownloadingsomefiles": "Erro ao transferir arquivos de módulo. Alguns arquivos podem estar faltando.", - "core.errorfileexistssamename": "Já existe um arquivo com esse nome.", - "core.errorinvalidform": "O formulário contém dados inválidos. Por favor tenha certeza de preencher todos os campos necessários e se os dados estão válidos.", - "core.errorinvalidresponse": "Resposta inválida recebida. Por favor, contate o administrador do site Moodle se o erro persistir.", - "core.errorloadingcontent": "Erro ao carregar o conteúdo.", - "core.errorofflinedisabled": "A navegação offline está desativada em seu site. Você precisa estar conectado à internet para utilizar o aplicativo.", - "core.erroropenfilenoapp": "Erro ao abrir o arquivo: nenhum aplicativo encontrado para abrir esse tipo de arquivo.", - "core.erroropenfilenoextension": "Erro ao abrir o arquivo: o arquivo não tem extensão.", - "core.erroropenpopup": "Esta atividade está tentando abrir um pop-up. Isto não é suportado neste app.", - "core.errorrenamefile": "Erro ao renomear o arquivo. Por favor, tente novamente.", - "core.errorsync": "Um erro aconteceu quando estava sincronizando. Por favor tente novamente.", - "core.errorsyncblocked": "Esse {{$a}} não pode ser sincronizado agora por causa de um processo em andamento. Por favor tente mais tarde. Se o problema persistir, tente reiniciar o aplicativo.", - "core.explanationdigitalminor": "Essas informações são necessárias para determinar se sua idade é superior a idade digital de consentimento. Essa é a idade em que um indivíduo pode consentir que os termos e condições e seus dados sejam legalmente armazenados e processados.", - "core.favourites": "Favoritos", - "core.filename": "Nome do arquivo", - "core.filenameexist": "O nome do arquivo já existe: {{$a}}", - "core.filenotfound": "Arquivo não encontrado, desculpe.", - "core.fileuploader.addfiletext": "Adicionar arquivo", - "core.fileuploader.audio": "Áudio", - "core.fileuploader.camera": "Câmera", - "core.fileuploader.confirmuploadfile": "Você está prestes a enviar {{size}}. Você tem certeza que quer continuar?", - "core.fileuploader.confirmuploadunknownsize": "Não conseguimos calcular o tamanho do que será enviado. Você tem certeza que quer continuar?", - "core.fileuploader.errorcapturingaudio": "Erro ao capturar áudio", - "core.fileuploader.errorcapturingimage": "Erro ao capturar imagem.", - "core.fileuploader.errorcapturingvideo": "Erro ao capturar vídeo.", - "core.fileuploader.errorgettingimagealbum": "Erro ao pegar imagem a partir do álbum.", - "core.fileuploader.errormustbeonlinetoupload": "Você precisa estar online para enviar arquivos.", - "core.fileuploader.errornoapp": "Você não tem um aplicativo instalado para fazer essa ação.", - "core.fileuploader.errorreadingfile": "Erro ao ler arquivo.", - "core.fileuploader.errorwhileuploading": "Houve um erro durante o envio do arquivo.", - "core.fileuploader.file": "Arquivo", - "core.fileuploader.filesofthesetypes": "Tipos de arquivos aceitos:", - "core.fileuploader.fileuploaded": "Arquivo enviado", - "core.fileuploader.invalidfiletype": "{{$a}} não é um tipo de arquivo aceito.", - "core.fileuploader.maxbytesfile": "O arquivo {{$a.file}} é muito grande. O tamanho máximo que você pode enviar é {{$a.size}}.", - "core.fileuploader.more": "Mais", - "core.fileuploader.photoalbums": "Álbuns de fotos", - "core.fileuploader.readingfile": "Lendo arquivo", - "core.fileuploader.readingfileperc": "Lendo o arquivo: {{$a}}%", - "core.fileuploader.selectafile": "Selecione um arquivo", - "core.fileuploader.uploadafile": "Enviando um arquivo", - "core.fileuploader.uploading": "Enviando", - "core.fileuploader.uploadingperc": "Enviando: {{$a}}%", - "core.fileuploader.video": "Vídeo", - "core.filter": "Filtrar", - "core.folder": "Pasta", - "core.forcepasswordchangenotice": "Você tem que mudar a senha antes de continuar", - "core.fulllistofcourses": "Todos os cursos", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Média", - "core.grades.badgrade": "Nota fornecida é inválida", - "core.grades.contributiontocoursetotal": "Contribuição para o total do curso", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Nota", - "core.grades.gradeitem": "Item de nota", - "core.grades.grades": "Notas", - "core.grades.lettergrade": "Notas por letras", - "core.grades.nogradesreturned": "Nenhuma avaliação calculada", - "core.grades.nooutcome": "Nenhum resultado de aprendizagem", - "core.grades.percentage": "Porcentagem", - "core.grades.range": "Intervalo", - "core.grades.rank": "Classificação", - "core.grades.weight": "Peso", - "core.group": "Grupo", - "core.groupsseparate": "Grupos separados", - "core.groupsvisible": "Grupos visíveis", - "core.h5p.additionallicenseinfo": "Qualquer informação adicional sobre a licença", - "core.h5p.author": "Autor", - "core.h5p.authorcomments": "Comentários do autor", - "core.h5p.authorcommentsdescription": "Comentários para o editor do conteúdo. (Este texto não será publicado como parte das informações de direitos autorais.)", - "core.h5p.authorname": "Nome do autor", - "core.h5p.authorrole": "Papel do autor", - "core.h5p.by": "por", - "core.h5p.cancellabel": "Cancelar", - "core.h5p.ccattribution": "Atribuição (CC BY)", - "core.h5p.ccattributionnc": "Atribuição-NãoComercial (CC BY-NC)", - "core.h5p.ccattributionncnd": "Atribuição-SemDerivações-SemDerivados (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Atribuição-NãoComercial-CompartilhaIgual (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Atribuição-SemDerivações (CC BY-ND)", - "core.h5p.ccattributionsa": "Atribuição-CompartilhaIgual (CC BY-SA)", - "core.h5p.ccpdd": "Domínio Público (CC0)", - "core.h5p.changedby": "Alterado por", - "core.h5p.changedescription": "Descrição da alteração", - "core.h5p.changelog": "Log de alterações", - "core.h5p.changeplaceholder": "Foto cortada, texto alterado etc.", - "core.h5p.close": "Fechar", - "core.h5p.confirmdialogbody": "Por favor, confirme que você deseja prosseguir. Essa ação não pode ser desfeita.", - "core.h5p.confirmdialogheader": "Confirmar ação", - "core.h5p.confirmlabel": "Confirmar", - "core.h5p.connectionLost": "Conexão perdida. Os resultados serão armazenados e enviados quando a conexão for restabelecida.", - "core.h5p.connectionReestablished": "Conexão restabelecida.", - "core.h5p.contentCopied": "O conteúdo é copiado para a área de transferência", - "core.h5p.contentchanged": "Este conteúdo foi alterado desde a última vez que você o usou.", - "core.h5p.contenttype": "Tipo de conteúdo", - "core.h5p.copyright": "Direitos de uso", - "core.h5p.copyrightinfo": "Informações sobre direitos autorais", - "core.h5p.copyrightstring": "Direitos autorais", - "core.h5p.copyrighttitle": "Ver informações de direitos autorais deste conteúdo.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Data", - "core.h5p.disablefullscreen": "Desabilitar tela cheia", - "core.h5p.download": "Baixar", - "core.h5p.downloadtitle": "Baixar esse conteúdo como um arquivo H5P.", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Incorporar", - "core.h5p.embedtitle": "Ver o código de incorporação para este conteúdo.", - "core.h5p.fullscreen": "Tela cheia", - "core.h5p.gpl": "Licença Pública Geral GPLv3", - "core.h5p.h5ptitle": "Visite h5p.org para conferir mais conteúdos.", - "core.h5p.hideadvanced": "Ocultar avançado", - "core.h5p.license": "Licença", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) Domínio Público", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Genérico", - "core.h5p.licenseCC20": "2.0 Genérico", - "core.h5p.licenseCC25": "2.5 Genérico", - "core.h5p.licenseCC30": "3.0 Não portado", - "core.h5p.licenseCC40": "4.0 Internacional", - "core.h5p.licenseGPL": "Licença Pública Geral", - "core.h5p.licenseV1": "Versão 1", - "core.h5p.licenseV2": "Versão 2", - "core.h5p.licenseV3": "Versão 3", - "core.h5p.licensee": "Licenciado", - "core.h5p.licenseextras": "Extras licenças", - "core.h5p.licenseversion": "Versão da licença", - "core.h5p.nocopyright": "Nenhuma informação de direitos autorais disponível para este conteúdo.", - "core.h5p.offlineDialogBody": "Não foi possível enviar as informações sobre sua conclusão dessa tarefa. Por favor, verifique sua conexão à internet.", - "core.h5p.offlineDialogHeader": "Sua conexão com o servidor foi perdida", - "core.h5p.offlineDialogRetryButtonLabel": "Tentar novamente agora", - "core.h5p.offlineDialogRetryMessage": "Tentando novamente em :num....", - "core.h5p.offlineSuccessfulSubmit": "Resultados enviados com sucesso.", - "core.h5p.originator": "Criador", - "core.h5p.pd": "Domínio Público", - "core.h5p.pddl": "Dedicação e Licença de Domínio Público", - "core.h5p.pdm": "Marca de domínio público (PDM)", - "core.h5p.resizescript": "Inclua este script no seu site se você desejar o dimensionamento dinâmico do conteúdo incorporado:", - "core.h5p.resubmitScores": "Tentando enviar os resultados armazenados.", - "core.h5p.reuse": "Reusar", - "core.h5p.reuseContent": "Reusar conteúdo", - "core.h5p.reuseDescription": "Reusar este conteúdo", - "core.h5p.showadvanced": "Mostrar avançado", - "core.h5p.showless": "Mostrar menos", - "core.h5p.showmore": "Mostrar mais", - "core.h5p.size": "Tamanho", - "core.h5p.source": "Fonte", - "core.h5p.startingover": "Você começará denovo", - "core.h5p.sublevel": "Subnível", - "core.h5p.thumbnail": "Miniatura", - "core.h5p.title": "Título", - "core.h5p.undisclosed": "Não divulgado", - "core.h5p.year": "Ano", - "core.h5p.years": "Ano(s)", - "core.h5p.yearsfrom": "Anos (de)", - "core.h5p.yearsto": "Anos (para)", - "core.hasdatatosync": "Esse {{$a}} tem informação offline que precisar ser sincronizada.", - "core.help": "Ajuda", - "core.hide": "Ocultar", - "core.hour": "hora", - "core.hours": "horas", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Imagem", - "core.imageviewer": "Visualizador de imagens", - "core.info": "Informação", - "core.invalidformdata": "Dados em forma incorreta", - "core.labelsep": ": ", - "core.lastaccess": "Último acesso", - "core.lastdownloaded": "Último baixado", - "core.lastmodified": "Última atualização", - "core.lastsync": "Última sincronização", - "core.layoutgrid": "Grade", - "core.list": "Lista", - "core.listsep": ";", - "core.loading": "Carregando", - "core.loadmore": "Ler mais", - "core.location": "Localização", - "core.login.auth_email": "Autoinscrição por email", - "core.login.authenticating": "Autenticação", - "core.login.cancel": "Cancelar", - "core.login.changepassword": "Mudar a senha", - "core.login.confirmdeletesite": "Você tem certeza que quer excluir o site {{sitename}}?", - "core.login.connect": "Conectar!", - "core.login.connecttomoodle": "Conectar ao moodle", - "core.login.contactyouradministrator": "Contate o administrador do site para ter mais ajuda.", - "core.login.contactyouradministratorissue": "Por favor, pergunte ao administrador para verificar o seguinte problema: {{$a}}", - "core.login.createaccount": "Criar minha conta", - "core.login.createuserandpass": "Escolha seu usuário e senha", - "core.login.credentialsdescription": "Por favor, informe seu nome de usuário e senha para efetuar o login", - "core.login.emailconfirmsent": "

                    Uma mensagem foi enviada para o seu endereço {{$a}}

                    Esta mensagem contém instruções para completar o seu cadastro.

                    Se você encontrar dificuldades contate o administrador.

                    ", - "core.login.emailconfirmsentnoemail": "

                    Um e-mail deve ter sido enviado para o seu endereço.

                    Ele contém instruções simplificadas para concluir seu registro.

                    Se você continuar com dificuldades, entre em contato com o administrador do site.

                    ", - "core.login.emailconfirmsentsuccess": "E-mail de confirmação enviado com sucesso", - "core.login.emailnotmatch": "Os e-mail não coincidem", - "core.login.erroraccesscontrolalloworigin": "A chamada de Cross-Origin que você está tentando executar foi rejeitada. Por favor, verifique https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Um erro ocorreu ao excluir esse site. Por favor tente novamente.", - "core.login.errorupdatesite": "Um erro ocorreu quando atualizava o token do site.", - "core.login.findyoursite": "Encontre seu site", - "core.login.firsttime": "Esta é a sua primeira vez aqui?", - "core.login.forcepasswordchangenotice": "Você tem que mudar a senha antes de continuar", - "core.login.forgotten": "Esqueceu o seu usuário ou senha?", - "core.login.help": "Ajuda", - "core.login.helpmelogin": "

                    Existem milhares de sites Moodle em todo o mundo. Este aplicativo pode se conectar somente a sites Moodle que habilitaram o acesso para dispositivos móveis.

                    Se você não conseguir se conectar ao seu site Moodle, entre em contato com o administrador do site e peça a ele para ler http://docs.moodle.org/en/Mobile_app

                    Para testar o aplicativo em um site de demonstração do Moodle digite teacher ou student no campoEndereço do site e clique no botão Conectar.

                    ", - "core.login.instructions": "Instruções", - "core.login.invalidaccount": "Por favor, verifique os detalhes de seu Usuário ou peça ao administrador do site para verificar a configuração do site.", - "core.login.invaliddate": "Data não válida", - "core.login.invalidemail": "Endereço de email inválido", - "core.login.invalidmoodleversion": "Versão do Moodle inválida. A versão mínima requerida é:", - "core.login.invalidsite": "A URL do siteé inválida.", - "core.login.invalidtime": "Tempo inválido", - "core.login.invalidurl": "URL inválida especificada", - "core.login.invalidvaluemax": "O valor máximo é {{$a}}", - "core.login.invalidvaluemin": "O valor minimo é{{$a}}", - "core.login.localmobileunexpectedresponse": "Verificação do Moodle Mobile Additional Features retornou uma resposta inesperada, você ira se autenticar usando o serviço Mobile padrão", - "core.login.loggedoutssodescription": "Você tem que autenticar novamente. Você precisa fazer login no site em uma janela do navegador.", - "core.login.login": "Acessar", - "core.login.loginbutton": "Entrar", - "core.login.logininsiterequired": "É preciso logar no site num navegador.", - "core.login.loginsteps": "Para ter acesso completo a este site, você primeiro precisa criar uma conta.", - "core.login.missingemail": "Está faltando o endereço de email", - "core.login.missingfirstname": "Está faltando o primeiro nome", - "core.login.missinglastname": "Está faltando o sobrenome", - "core.login.mobileservicesnotenabled": "Os serviços móveis não estão habilitados no seu Moodle. Por favor contate a administradora do seu Moodle se achar que o acesso móvel deveria estar habilitado", - "core.login.mustconfirm": "Você precisa confirmar sua conta", - "core.login.newaccount": "Nova conta", - "core.login.notloggedin": "Você precisa estar logado.", - "core.login.password": "Senha", - "core.login.passwordforgotten": "Senha esquecida", - "core.login.passwordforgotteninstructions2": "Para redefinir sua senha, preencha seu usuário ou seu email abaixo. Se sua conta for encontrada no banco de dados, um email será enviado para seu endereço de email, com as instruções sobre como restabelecer seu acesso.", - "core.login.passwordrequired": "Senha necessária", - "core.login.policyaccept": "Eu compreendo e concordo", - "core.login.policyagree": "Para utilizar este site você precisa aceitar o acordo sobre a política de uso do site. Você aceita os termos deste acordo?", - "core.login.policyagreement": "Acordo de política do site", - "core.login.policyagreementclick": "Link para o acordo de política do site", - "core.login.potentialidps": "Autenticar usando sua conta em:", - "core.login.profileinvaliddata": "Valor inválido", - "core.login.recaptchachallengeimage": "Desafio de imagem do reCAPTCHA", - "core.login.recaptchaexpired": "Verificação expirada. Responda a pergunta de segurança novamente.", - "core.login.recaptchaincorrect": "A resposta da pergunta de segurança está incorreta.", - "core.login.reconnect": "Reconectar", - "core.login.reconnectdescription": "Seu token de autenticação é inválido ou expirou, você tem que reconectar com o site.", - "core.login.reconnectssodescription": "Seu token de autenticação é inválido ou expirou, você tem que reconectar com o site. Você precisa efetuar login no site em uma janela do navegador.", - "core.login.resendemail": "Reenviar email", - "core.login.searchby": "Buscar por:", - "core.login.security_question": "Pergunta de segurança", - "core.login.selectacountry": "Selecione um país", - "core.login.selectsite": "Por favor selecione seu site:", - "core.login.signupplugindisabled": "{{$a}} não está habilitado.", - "core.login.siteaddress": "Endereço do site", - "core.login.sitehasredirect": "Seu site contém pelo menos um redirecionamento de HTTP. O aplicativo não pode seguir redirecionamentos. Esse pode ser o problema que impede o aplicativo de se conectar ao seu site.", - "core.login.siteinmaintenance": "O site está em modo de manutenção", - "core.login.sitepolicynotagreederror": "Não concordou com a política do site.", - "core.login.siteurl": "URL do site", - "core.login.siteurlrequired": "URL do site é obrigatória, por exemplo http://www.yourmoodlesite.abc ou https://www.yourmoodlesite.efg", - "core.login.startsignup": "Criar uma conta", - "core.login.stillcantconnect": "Ainda não consegue conectar?", - "core.login.supplyinfo": "Mais detalhes", - "core.login.username": "Identificação de usuário", - "core.login.usernameoremail": "Insira a identificação de usuário ou endereço de e-mail", - "core.login.usernamerequired": "Nome de usuário exigido", - "core.login.usernotaddederror": "Usuário não adicionado - erro", - "core.login.visitchangepassword": "Você quer visitar o site para mudar sua senha?", - "core.login.webservicesnotenabled": "Web Services não estão ativados em seu site. Por favor, contate a administradora do seu Moodle se você achar que o acesso móvel deve ser ativado.", - "core.lostconnection": "Perdemos conexão. Você precisa se reconectar. Seu token agora está inválido.", - "core.mainmenu.changesite": "Mudar site", - "core.mainmenu.help": "Ajuda", - "core.mainmenu.logout": "Sair", - "core.mainmenu.website": "Site", - "core.maxsizeandattachments": "Tamanho máximo para arquivos: {{$a.size}}, número máximo de anexos: {{$a.attachments}}", - "core.min": "minuto", - "core.mins": "minutos", - "core.misc": "Diversidade", - "core.mod_assign": "Tarefa", - "core.mod_assignment": "Tarefa 2.2 (Desabilitado)", - "core.mod_book": "Livro", - "core.mod_chat": "Chat", - "core.mod_choice": "Escolha", - "core.mod_data": "Base de dados", - "core.mod_database": "Base de dados", - "core.mod_external-tool": "Ferramenta externa - LTI", - "core.mod_feedback": "Pesquisa", - "core.mod_file": "Arquivo", - "core.mod_folder": "Pasta", - "core.mod_forum": "Fórum", - "core.mod_glossary": "Glossário", - "core.mod_ims": "Conteúdo do pacote IMS", - "core.mod_imscp": "Conteúdo do pacote IMS", - "core.mod_label": "Rótulo", - "core.mod_lesson": "Lição", - "core.mod_lti": "Ferramenta externa - LTI", - "core.mod_page": "Página", - "core.mod_quiz": "Questionário", - "core.mod_resource": "Arquivo", - "core.mod_scorm": "Pacote SCORM", - "core.mod_survey": "Pesquisa de avaliação", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Laboratório de Avaliação", - "core.moduleintro": "Descrição", - "core.more": "mais", - "core.mygroups": "Meus grupos", - "core.name": "Nome", - "core.networkerroriframemsg": "Este conteúdo não está disponível offline. Por favor, conecte-se à internet e tente novamente.", - "core.networkerrormsg": "Rede não habilitada ou não está funcionado", - "core.never": "Nunca", - "core.next": "Próximo", - "core.no": "Não", - "core.nocomments": "Nenhum comentário", - "core.nograde": "Nenhuma nota", - "core.none": "Nenhum", - "core.nopasswordchangeforced": "Você não pode proceder sem mudar sua senha.", - "core.nopermissionerror": "Desculpe, mas você não tem permissões para fazer isso", - "core.nopermissions": "Desculpe, mas atualmente você não tem permissão para {{$a}}", - "core.noresults": "Nenhum resultado", - "core.noselection": "Sem seleção", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Este perfil não é disponível porque este usuário não está inscrito neste curso.", - "core.notice": "Notar", - "core.notingroup": "Desculpe, mas você precisa ser participante de um grupo para ver esta página.", - "core.notsent": "Não enviado", - "core.now": "agora", - "core.numwords": "{{$a}} palavras", - "core.offline": "Desconectado", - "core.ok": "OK", - "core.online": "Conectado", - "core.openfullimage": "Clique aqui para exibir a imagem no tamanho completo", - "core.openinbrowser": "Abrir no navegador", - "core.othergroups": "Outros grupos", - "core.pagea": "Página {{$a}}", - "core.parentlanguage": "pt", - "core.paymentinstant": "Clique o botão abaixo para efetuar o pagamento e fazer a sua inscrição em poucos minutos!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Fone", - "core.pictureof": "Imagem de {{$a}}", - "core.previous": "Anterior", - "core.proceed": "Continuar", - "core.pulltorefresh": "Puxe para atualizar", - "core.question.answer": "Resposta", - "core.question.answersaved": "Resposta salva", - "core.question.cannotdeterminestatus": "Não é possível determinar o status", - "core.question.certainty": "Certeza", - "core.question.complete": "Completo", - "core.question.correct": "Correto", - "core.question.errorattachmentsnotsupported": "O aplicativo ainda não suporta anexar arquivos à resposta.", - "core.question.errorinlinefilesnotsupported": "O aplicativo ainda não suporta a edição direta de arquivos.", - "core.question.errorquestionnotsupported": "Esse tipo de questão não é suportada pelo aplicativo: {{$a}}.", - "core.question.feedback": "Feedback", - "core.question.howtodraganddrop": "Toque para selecionar e então toque para soltar.", - "core.question.incorrect": "Incorreto", - "core.question.information": "Informação", - "core.question.invalidanswer": "Resposta incompleta", - "core.question.notanswered": "Não respondido", - "core.question.notyetanswered": "Ainda não respondida", - "core.question.partiallycorrect": "Parcialmente correto", - "core.question.questionmessage": "Questão {{$a}}: {{$b}}", - "core.question.questionno": "Questão {{$a}}", - "core.question.requiresgrading": "Requer avaliação", - "core.quotausage": "Você utilizou {{$a.used}} de seu limite de {{$a.total}}.", - "core.rating.aggregateavg": "Média das avaliações", - "core.rating.aggregatecount": "Contagem das avaliações", - "core.rating.aggregatemax": "Avaliação máxima", - "core.rating.aggregatemin": "Avaliação mínima", - "core.rating.aggregatesum": "Soma das avaliações", - "core.rating.noratings": "Nenhuma avaliação enviada", - "core.rating.rating": "Avaliação", - "core.rating.ratings": "Avaliações", - "core.redirectingtosite": "Você será redirecionado para o site.", - "core.refresh": "Atualizar", - "core.remove": "Remover", - "core.required": "Necessários", - "core.requireduserdatamissing": "Este usuário não possui alguns dados de perfil exigidos. Por favor, preencha estes dados em seu Moodle e tente novamente.
                    {{$a}}", - "core.resourcedisplayopen": "Abrir", - "core.resources": "Recursos", - "core.restore": "Restaurar", - "core.restricted": "Restrito", - "core.retry": "Tentar novamente", - "core.save": "Salvar", - "core.savechanges": "Salvar mudanças", - "core.search": "Buscar", - "core.searching": "Procurando", - "core.searchresults": "Resultados da busca", - "core.sec": "segundo", - "core.secs": "segundos", - "core.seemoredetail": "Clique aqui para mais detalhes", - "core.selectacategory": "Por favor seleccione uma categoria", - "core.selectacourse": "Selecione um curso", - "core.selectagroup": "Selecione um grupo", - "core.send": "Enviar", - "core.sending": "Enviando", - "core.serverconnection": "Erro ao conectar ao servidor", - "core.settings.about": "Sobre", - "core.settings.cannotsyncoffline": "Não pode sincronizar offline.", - "core.settings.cannotsyncwithoutwifi": "Não foi possível sincronizar por causa que as configurações atuais somente permite a sincronização em quando estiver conectado ao Wi-Fi. Por favor conecte-se a uma rede Wi-Fi.", - "core.settings.compilationinfo": "Informação de compilação", - "core.settings.cordovadevicemodel": "Cordova do modelo do dispositivo", - "core.settings.cordovadeviceosversion": "Cordova Device OS versão", - "core.settings.cordovadeviceplatform": "Cordova Device plataforma", - "core.settings.cordovadeviceuuid": "Cordova Device uuid", - "core.settings.cordovaversion": "Cordova versão", - "core.settings.currentlanguage": "Idioma em uso", - "core.settings.debugdisplay": "Mostre mensagens de debug", - "core.settings.debugdisplaydescription": "Se habilitado, os modais de erro mostrarão mais dados sobre o erro, se possível.", - "core.settings.deletesitefiles": "Você tem certeza que quer excluir os arquivos baixados do site '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Excluir os arquivos do site", - "core.settings.deviceinfo": "Informação do dispositivo", - "core.settings.deviceos": "Dispositivo OS", - "core.settings.disableall": "Desabilitar notificações", - "core.settings.disabled": "Desabilitado", - "core.settings.displayformat": "Formato de exibição", - "core.settings.enabledownloadsection": "Ativar seções de download", - "core.settings.enablerichtexteditor": "Habilitar editor de texto rico", - "core.settings.enablerichtexteditordescription": "Se habilitado, o editor de texto rico será exibido em lugares em que ele é permitido.", - "core.settings.enablesyncwifi": "Sincronizar somente em Wi-Fi", - "core.settings.entriesincache": "{{$a}} registros em cache", - "core.settings.errordeletesitefiles": "Erro ao excluir os arquivos do site.", - "core.settings.errorsyncsite": "Erro ao sincronizar os dados do site, por favor verifique sua conexão de internet e tente novamente.", - "core.settings.estimatedfreespace": "Espaço livre estimado", - "core.settings.filesystemroot": "Raiz do sistema de arquivos", - "core.settings.fontsizecharacter": "A", - "core.settings.general": "Geral", - "core.settings.language": "Idioma", - "core.settings.license": "Licença", - "core.settings.localnotifavailable": "Notificações locais disponíveis", - "core.settings.locationhref": "Webview URL", - "core.settings.locked": "Travado", - "core.settings.loggedin": "Conectado", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Idioma de navegação", - "core.settings.navigatoruseragent": "Navegador userAgent", - "core.settings.networkstatus": "O status da conexão Internet", - "core.settings.preferences": "Preferências", - "core.settings.privacypolicy": "Política de privacidade", - "core.settings.pushid": "ID de notificações push", - "core.settings.reportinbackground": "Relatar erros automaticamente", - "core.settings.settings": "Configurações", - "core.settings.showdownloadoptions": "Mostrar opções para baixar", - "core.settings.sites": "Sites", - "core.settings.spaceusage": "Uso do espaço", - "core.settings.synchronization": "Sincronização", - "core.settings.synchronizenow": "Sincronizar agora", - "core.settings.syncsettings": "Configurações de sincronização", - "core.settings.total": "Total", - "core.settings.wificonnection": "Conexão Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "Escolha uma conta para armazenar os arquivos.", - "core.sharedfiles.chooseactionrepeatedfile": "Já existe um arquivo com esse nome. Você deseja substituir o arquivo existente ou renomear ele para \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Não existem sites armazenados. Por favor, adicione um site antes de compartilhar o arquivo com o aplicativo.", - "core.sharedfiles.nosharedfiles": "Não existem arquivos compartilhados guardados nesse site.", - "core.sharedfiles.nosharedfilestoupload": "Você não tem arquivos para enviar aqui. Se você quer enviar um arquivo de um outro aplicativo, localiza aquele arquivo e clique no botão 'Abra em'.", - "core.sharedfiles.rename": "Renomear", - "core.sharedfiles.replace": "Substituir", - "core.sharedfiles.sharedfiles": "Arquivos compartilhados", - "core.sharedfiles.successstorefile": "Arquivo armazenado com sucesso. Agora você pode selecionar esse arquivo para enviar para seus \"arquivos privados\" ou anexar em atividades.", - "core.show": "Mostrar", - "core.showless": "Mostrar menos ...", - "core.showmore": "Mostrar mais ...", - "core.site": "Site", - "core.sitehome.sitehome": "Página inicial do site", - "core.sitehome.sitenews": "Avisos do site", - "core.sitemaintenance": "O sistema está em manutenção e não está disponível no momento.", - "core.sizeb": "bytes", - "core.sizegb": "Gb", - "core.sizekb": "Kb", - "core.sizemb": "Mb", - "core.sizetb": "TB", - "core.skip": "Pular", - "core.sorry": "Desculpe...", - "core.sort": "Ordenar", - "core.sortby": "Ordenar por", - "core.strftimedate": "%d de %B de %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M %p", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d de %B de %Y", - "core.strftimedaydatetime": "%A, %d %b %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M %p", - "core.strftimetime": "%H:%M %p", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Enviar", - "core.success": "Sucesso", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Coleção padrão", - "core.tag.inalltagcoll": "Em toda parte", - "core.tag.itemstaggedwith": "{{$a.tagarea}} marcada com \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Nenhum resultado para \"{{$a}}\"", - "core.tag.notagsfound": "Nenhuma tag correspondente a \"{{$a}}\" encontrado", - "core.tag.searchtags": "Pesquisar tags", - "core.tag.showingfirsttags": "Exibindo {{$a}} tags mais populares", - "core.tag.tag": "Tag", - "core.tag.tagarea_course": "Cursos", - "core.tag.tagarea_course_modules": "Atividades e recursos", - "core.tag.tagarea_post": "Postagens de blog", - "core.tag.tagarea_user": "Interesses do usuário", - "core.tag.tags": "Tags", - "core.teachers": "Professores", - "core.thereisdatatosync": "Existem {{$a}} offline para ser sincronizados.", - "core.thisdirection": "ltr", - "core.time": "Hora", - "core.timesup": "Acabou o tempo de duração!", - "core.today": "Hoje", - "core.tryagain": "Tente de novo", - "core.twoparagraphs": "{{p1}}

                    {{p2}}", - "core.uhoh": "Uh oh!", - "core.unexpectederror": "Erro inesperado. Por favor, feche e abra novamente o aplicativo para tentar novamente", - "core.unicodenotsupported": "Texto Unicode como emojis não são suportados neste site, o texto será enviado removendo esses caracteres.", - "core.unicodenotsupportedcleanerror": "Texto vazio foi encontrado ao limpar caracteres Unicode.", - "core.unknown": "Desconhecido", - "core.unlimited": "Ilimitado", - "core.unzipping": "Descompactando", - "core.upgraderunning": "O site está sendo atualizado, por favor, tente novamente mais tarde.", - "core.user": "Usuário", - "core.user.address": "Endereço", - "core.user.city": "Cidade/Município", - "core.user.contact": "Contato", - "core.user.country": "País", - "core.user.description": "Descrição", - "core.user.details": "Detalhes", - "core.user.detailsnotavailable": "Os detalhes desse usuário não estão disponíveis para você.", - "core.user.editingteacher": "Professor", - "core.user.email": "Endereço de email", - "core.user.emailagain": "Confirmar endereço de e-mail", - "core.user.errorloaduser": "Erro ao carregar o usuário.", - "core.user.firstname": "Nome", - "core.user.interests": "Interesses", - "core.user.lastname": "Sobrenome", - "core.user.manager": "Gerente", - "core.user.newpicture": "Nova imagem", - "core.user.noparticipants": "Nenhum participante encontrado para este curso", - "core.user.participants": "Participantes", - "core.user.phone1": "Fone", - "core.user.phone2": "Telefone celular", - "core.user.roles": "Papéis", - "core.user.sendemail": "Email", - "core.user.student": "Estudante", - "core.user.teacher": "Moderador", - "core.user.webpage": "Página web", - "core.userdeleted": "Esta conta de usuário foi cancelada", - "core.userdetails": "Detalhes do usuário", - "core.usernotfullysetup": "O usuário não está totalmente configurado", - "core.users": "Usuários", - "core.view": "Ver", - "core.viewcode": "Ver o código", - "core.vieweditor": "Ver o editor", - "core.viewembeddedcontent": "Ver conteúdo incorporado", - "core.viewprofile": "Ver perfil", - "core.warningofflinedatadeleted": "Dados offline de {{component}} '{{name}}' foram excluídos. {{error}}", - "core.whatisyourage": "Qual é a sua idade?", - "core.wheredoyoulive": "Em qual país você vive?", - "core.whoops": "Oops!", - "core.whyisthishappening": "Por que isso está acontecendo?", - "core.whyisthisrequired": "Por que isso é necessário?", - "core.wsfunctionnotavailable": "A função do webservice não está disponível.", - "core.year": "ano", - "core.years": "anos", - "core.yes": "Sim" -} \ No newline at end of file diff --git a/src/assets/lang/pt.json b/src/assets/lang/pt.json deleted file mode 100644 index 97a22c0c8..000000000 --- a/src/assets/lang/pt.json +++ /dev/null @@ -1,2175 +0,0 @@ -{ - "addon.badges.alignment": "Alinhamento", - "addon.badges.badgedetails": "Detalhes da medalha", - "addon.badges.badges": "Medalhas", - "addon.badges.bendorsement": "Aval", - "addon.badges.claimcomment": "Comentário da acreditação", - "addon.badges.claimid": "URL de reivindicação", - "addon.badges.contact": "Contacto", - "addon.badges.dateawarded": "Data de emissão", - "addon.badges.expired": "Expirada", - "addon.badges.expirydate": "Data de validade", - "addon.badges.imageauthoremail": "E-mail do autor da imagem", - "addon.badges.imageauthorname": "Nome do autor da imagem", - "addon.badges.imageauthorurl": "URL do autor da imagem", - "addon.badges.imagecaption": "Legenda da imagem", - "addon.badges.issuancedetails": "Data de validade da medalha", - "addon.badges.issuerdetails": "Detalhes do emissor", - "addon.badges.issueremail": "E-mail", - "addon.badges.issuername": "Nome do emissor", - "addon.badges.issuerurl": "URL do emissor", - "addon.badges.language": "Idioma", - "addon.badges.noalignment": "Nenhuma proficiência externa ou predefinição foi especificada para esta medalha.", - "addon.badges.nobadges": "Não existem medalhas disponíveis.", - "addon.badges.norelated": "Não existem medalhas relacionadas com esta medalha.", - "addon.badges.recipientdetails": "Detalhes do condecorado", - "addon.badges.relatedbages": "Medalhas relacionadas", - "addon.badges.version": "Versão", - "addon.badges.warnexpired": "(A validade desta medalha terminou!)", - "addon.block_activitymodules.pluginname": "Atividades", - "addon.block_activityresults.pluginname": "Resultados da atividade", - "addon.block_badges.pluginname": "Minhas Medalhas recentes", - "addon.block_blogmenu.pluginname": "Menu do Blogue", - "addon.block_blogrecent.pluginname": "Entradas recentes do blogue", - "addon.block_blogtags.pluginname": "Palavras-chave do Blogue", - "addon.block_calendarmonth.pluginname": "Calendário", - "addon.block_calendarupcoming.pluginname": "Próximos eventos", - "addon.block_comments.pluginname": "Comentários", - "addon.block_completionstatus.pluginname": "Conclusão da disciplina", - "addon.block_glossaryrandom.pluginname": "Termo aleatório do glossário", - "addon.block_learningplans.pluginname": "Planos de aprendizagem", - "addon.block_myoverview.all": "Todas (exceto as removidas da visualização)", - "addon.block_myoverview.allincludinghidden": "Tudo", - "addon.block_myoverview.favourites": "Com estrela", - "addon.block_myoverview.future": "Próximos 30 dias", - "addon.block_myoverview.hiddencourses": "Removidas da visualização", - "addon.block_myoverview.inprogress": "Em progresso", - "addon.block_myoverview.lastaccessed": "Último acesso", - "addon.block_myoverview.morecourses": "Mais disciplinas", - "addon.block_myoverview.nocourses": "Sem disciplinas", - "addon.block_myoverview.past": "Histórico", - "addon.block_myoverview.pluginname": "Minhas disciplinas", - "addon.block_myoverview.shortname": "Nome curto", - "addon.block_myoverview.title": "Nome da disciplina", - "addon.block_newsitems.pluginname": "Últimos anúncios", - "addon.block_onlineusers.pluginname": "Utilizadores ativos", - "addon.block_privatefiles.pluginname": "Meus ficheiros privados", - "addon.block_recentactivity.pluginname": "Atividade recente", - "addon.block_recentlyaccessedcourses.nocourses": "Sem disciplinas recentes", - "addon.block_recentlyaccessedcourses.pluginname": "Disciplinas acedidas recentemente", - "addon.block_recentlyaccesseditems.noitems": "Sem itens recentes", - "addon.block_recentlyaccesseditems.pluginname": "Itens acedidos recentemente", - "addon.block_rssclient.pluginname": "Lista RSS", - "addon.block_selfcompletion.pluginname": "Autoconclusão", - "addon.block_sitemainmenu.pluginname": "Menu principal", - "addon.block_starredcourses.nocourses": "Nenhuma disciplina com estrela", - "addon.block_starredcourses.pluginname": "Disciplinas com estrela", - "addon.block_tags.pluginname": "Palavras-chave", - "addon.block_timeline.duedate": "Data limite", - "addon.block_timeline.next30days": "Próximos 30 dias", - "addon.block_timeline.next3months": "Próximos 3 meses", - "addon.block_timeline.next6months": "Próximos 6 meses", - "addon.block_timeline.next7days": "Próximos 7 dias", - "addon.block_timeline.nocoursesinprogress": "Não existem disciplinas em progresso", - "addon.block_timeline.noevents": "Não existem atividades com data limite próxima", - "addon.block_timeline.overdue": "Data limite ultrapassada", - "addon.block_timeline.pluginname": "Cronograma", - "addon.block_timeline.sortbycourses": "Ordenar por disciplinas", - "addon.block_timeline.sortbydates": "Ordenar por datas", - "addon.blog.blog": "Blogue", - "addon.blog.blogentries": "Mensagens do blogue", - "addon.blog.errorloadentries": "Ocorreu um erro ao carregar publicações do Blogue.", - "addon.blog.linktooriginalentry": "Ligar à mensagem original", - "addon.blog.noentriesyet": "Não existem mensagens visíveis", - "addon.blog.publishtonoone": "O próprio (rascunho)", - "addon.blog.publishtosite": "Qualquer pessoa neste site", - "addon.blog.publishtoworld": "Todos", - "addon.blog.showonlyyourentries": "Mostrar apenas as suas publicações", - "addon.blog.siteblogheading": "Blogue do site", - "addon.calendar.allday": "Todo o dia", - "addon.calendar.calendar": "Calendário", - "addon.calendar.calendarevent": "Evento do calendário", - "addon.calendar.calendarevents": "Eventos do calendário", - "addon.calendar.calendarreminders": "Lembretes do calendário", - "addon.calendar.categoryevents": "Eventos da categoria", - "addon.calendar.confirmeventdelete": "Tem a certeza de que pretende apagar o evento \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "O evento \"{{$a.name}}\" faz parte de uma série de eventos. Pretende apagar apenas este evento ou todos os {{$a>count}} eventos da série?", - "addon.calendar.courseevents": "Eventos da disciplina", - "addon.calendar.currentmonth": "Mês atual", - "addon.calendar.daynext": "Dia seguinte", - "addon.calendar.dayprev": "Dia anterior", - "addon.calendar.defaultnotificationtime": "Hora de notificação predefinida", - "addon.calendar.deleteallevents": "Apagar todos os eventos", - "addon.calendar.deleteevent": "Apagar evento", - "addon.calendar.deleteoneevent": "Apagar este evento", - "addon.calendar.durationminutes": "Duração em minutos", - "addon.calendar.durationnone": "Sem duração", - "addon.calendar.durationuntil": "Até (data e hora)", - "addon.calendar.editevent": "A editar evento", - "addon.calendar.errorloadevent": "Erro ao carregar evento.", - "addon.calendar.errorloadevents": "Erro ao carregar eventos.", - "addon.calendar.eventcalendareventdeleted": "Evento de calendário eliminado", - "addon.calendar.eventduration": "Duração", - "addon.calendar.eventendtime": "Hora de fim", - "addon.calendar.eventkind": "Tipo de evento", - "addon.calendar.eventname": "Designação do evento", - "addon.calendar.eventstarttime": "Hora de início", - "addon.calendar.eventtype": "Tipo de evento", - "addon.calendar.fri": "Sex", - "addon.calendar.friday": "Sexta", - "addon.calendar.gotoactivity": "Ir para a atividade", - "addon.calendar.groupevents": "Eventos do grupo", - "addon.calendar.invalidtimedurationminutes": "A duração indicada em minutos não é válida. Indique uma duração maior que zero ou não indique duração.", - "addon.calendar.invalidtimedurationuntil": "A data e hora que selecionou para a duração é anterior à hora de início do evento. Corrija antes de continuar.", - "addon.calendar.mon": "Seg", - "addon.calendar.monday": "Segunda", - "addon.calendar.monthlyview": "Eventos do mês", - "addon.calendar.newevent": "Novo evento", - "addon.calendar.noevents": "Sem eventos", - "addon.calendar.nopermissiontoupdatecalendar": "Não tem permissão para atualizar o evento no calendário", - "addon.calendar.reminders": "Lembretes", - "addon.calendar.repeatedevents": "Eventos repetidos", - "addon.calendar.repeateditall": "Aplicar as alterações também aos restantes {{$a}} eventos desta série de eventos repetidos", - "addon.calendar.repeateditthis": "Aplicar as alterações apenas a este evento", - "addon.calendar.repeatevent": "Repetir este evento", - "addon.calendar.repeatweeksl": "Repetição semanal (total de semanas)", - "addon.calendar.sat": "Sab", - "addon.calendar.saturday": "Sábado", - "addon.calendar.setnewreminder": "Definir um novo lembrete", - "addon.calendar.siteevents": "Eventos do site", - "addon.calendar.sun": "Dom", - "addon.calendar.sunday": "Domingo", - "addon.calendar.thu": "Qui", - "addon.calendar.thursday": "Quinta", - "addon.calendar.today": "Hoje", - "addon.calendar.tomorrow": "Amanhã", - "addon.calendar.tue": "Ter", - "addon.calendar.tuesday": "Terça", - "addon.calendar.typecategory": "Evento da categoria", - "addon.calendar.typeclose": "Fechar evento", - "addon.calendar.typecourse": "Evento da disciplina", - "addon.calendar.typedue": "Evento expirado", - "addon.calendar.typegradingdue": "Avaliar evento expirado", - "addon.calendar.typegroup": "Evento do grupo", - "addon.calendar.typeopen": "Abrir evento", - "addon.calendar.typesite": "Evento do site", - "addon.calendar.typeuser": "Evento do utilizador", - "addon.calendar.upcomingevents": "Próximos eventos", - "addon.calendar.userevents": "Eventos do utilizador", - "addon.calendar.wed": "Qua", - "addon.calendar.wednesday": "Quarta", - "addon.calendar.when": "Quando", - "addon.calendar.yesterday": "Ontem", - "addon.competency.activities": "Atividades", - "addon.competency.competencies": "Competências", - "addon.competency.competenciesmostoftennotproficientincourse": "Competências sem proficiência nesta disciplina", - "addon.competency.coursecompetencies": "Competências da disciplina", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "As avaliações das competências nesta disciplina não afetam os planos de aprendizagem.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "As avaliações das competências nesta disciplina são automaticamente atualizadas nos planos de aprendizagem.", - "addon.competency.crossreferencedcompetencies": "Competências referenciadas", - "addon.competency.duedate": "Data limite", - "addon.competency.errornocompetenciesfound": "Competências não encontradas", - "addon.competency.evidence": "Comprovativo", - "addon.competency.evidence_competencyrule": "A regra da competência foi cumprida.", - "addon.competency.evidence_coursecompleted": "A disciplina '{{$a}}' está concluída.", - "addon.competency.evidence_coursemodulecompleted": "A atividade '{{$a}}' está concluída.", - "addon.competency.evidence_courserestored": "A avaliação foi restaurada em conjunto com a disciplina '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "O comprovativo de aprendizagem anterior '{{$a}}' foi associado à competência.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "O comprovativo de aprendizagem anterior '{{$a}}' foi desassociado da competência.", - "addon.competency.evidence_manualoverride": "A avaliação da competência foi configurada manualmente.", - "addon.competency.evidence_manualoverrideincourse": "A avaliação da competência foi configurada manualmente na disciplina '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "A avaliação da competência foi configurada manualmente no plano de aprendizagem '{{$a}}'.", - "addon.competency.learningplancompetencies": "Competências do plano de aprendizagem", - "addon.competency.learningplans": "Planos de aprendizagem", - "addon.competency.myplans": "Os meus planos de aprendizagem", - "addon.competency.noactivities": "Nenhuma atividade associada", - "addon.competency.nocompetencies": "Sem competências", - "addon.competency.nocompetenciesincourse": "Ainda não foram associadas competências a esta disciplina.", - "addon.competency.nocrossreferencedcompetencies": "Nenhuma competência foi referenciada a esta competência.", - "addon.competency.noevidence": "Não foi adicionado nenhum comprovativo", - "addon.competency.noplanswerecreated": "Nenhum plano de aprendizagem foi criado.", - "addon.competency.nouserplanswithcompetency": "Nenhum plano de aprendizagem contém esta competência.", - "addon.competency.path": "Localização:", - "addon.competency.planstatusactive": "Ativo", - "addon.competency.planstatuscomplete": "Concluído", - "addon.competency.planstatusdraft": "Rascunho", - "addon.competency.planstatusinreview": "Em revisão", - "addon.competency.planstatuswaitingforreview": "À espera de revisão", - "addon.competency.proficient": "Proficiente", - "addon.competency.progress": "Progresso", - "addon.competency.rating": "Avaliação", - "addon.competency.reviewstatus": "Estado da revisão", - "addon.competency.status": "Estado", - "addon.competency.template": "Modelo de plano de aprendizagem", - "addon.competency.uponcoursecompletion": "Após a conclusão da disciplina:", - "addon.competency.usercompetencystatus_idle": "Parado", - "addon.competency.usercompetencystatus_inreview": "Em revisão", - "addon.competency.usercompetencystatus_waitingforreview": "À espera de revisão", - "addon.competency.userplans": "Planos de aprendizagem", - "addon.competency.xcompetenciesproficientoutofy": "Tem proficiência em {{$a.x}} de {{$a.y}} competências", - "addon.competency.xcompetenciesproficientoutofyincourse": "Tem proficiência em {{$a.x}} de {{$a.y}} competências nesta disciplina", - "addon.coursecompletion.complete": "Concluído", - "addon.coursecompletion.completecourse": "Já concluí a disciplina", - "addon.coursecompletion.completed": "Concluída", - "addon.coursecompletion.completiondate": "Data da conclusão", - "addon.coursecompletion.completionmenuitem": "Conclusão", - "addon.coursecompletion.couldnotloadreport": "Não foi possível carregar o relatório de conclusão da disciplina. Por favor, tente mais tarde.", - "addon.coursecompletion.coursecompletion": "Conclusão da disciplina", - "addon.coursecompletion.criteria": "Critérios", - "addon.coursecompletion.criteriagroup": "Grupo de critérios", - "addon.coursecompletion.criteriarequiredall": "Todos os critérios abaixo são exigidos", - "addon.coursecompletion.criteriarequiredany": "Qualquer dos critérios abaixo é necessário", - "addon.coursecompletion.inprogress": "Em progresso", - "addon.coursecompletion.manualselfcompletion": "Conclusão manual pelo próprio", - "addon.coursecompletion.nottracked": "Atualmente não está a ser monitorizado pela verificação da conclusão nesta disciplina", - "addon.coursecompletion.notyetstarted": "Ainda não iniciou", - "addon.coursecompletion.pending": "Pendente", - "addon.coursecompletion.required": "Obrigatório", - "addon.coursecompletion.requiredcriteria": "Critério obrigatório", - "addon.coursecompletion.requirement": "Requisito", - "addon.coursecompletion.status": "Estado", - "addon.coursecompletion.viewcoursereport": "Ver relatório da disciplina", - "addon.files.couldnotloadfiles": "Não foi possível carregar a lista de ficheiros", - "addon.files.emptyfilelist": "Não existem ficheiros", - "addon.files.erroruploadnotworking": "Infelizmente não é possível carregar ficheiros para o seu site.", - "addon.files.files": "Ficheiros", - "addon.files.privatefiles": "Ficheiros privados", - "addon.files.sitefiles": "Ficheiros do site", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Configurar dispositivos", - "addon.messages.acceptandaddcontact": "Aceitar e adicionar aos contactos", - "addon.messages.addcontact": "Adicionar contacto", - "addon.messages.addcontactconfirm": "Tem a certeza de que pretende adicionar {{$a}} aos seus contactos?", - "addon.messages.addtofavourites": "Marcar com estrela", - "addon.messages.addtoyourcontacts": "Adicionar aos contactos", - "addon.messages.blocknoncontacts": "Bloquear mensagens de pessoas que não estão na minha lista de contactos", - "addon.messages.blockuser": "Bloquear utilizador", - "addon.messages.blockuserconfirm": "Tem a certeza de que pretende bloquear {{$a}}?", - "addon.messages.contactableprivacy": "Aceitar mensagens de:", - "addon.messages.contactableprivacy_coursemember": "Os Meus contactos e qualquer um das minhas disciplinas", - "addon.messages.contactableprivacy_onlycontacts": "Apenas os Meus contactos", - "addon.messages.contactableprivacy_site": "Qualquer utilizador do site", - "addon.messages.contactblocked": "Contacto bloqueado", - "addon.messages.contactlistempty": "A lista de contactos está vazia", - "addon.messages.contactname": "Nome do contacto", - "addon.messages.contactrequestsent": "Pedido de contacto enviado", - "addon.messages.contacts": "Contactos", - "addon.messages.conversationactions": "Menu de ações das conversações", - "addon.messages.decline": "Rejeitar", - "addon.messages.deleteallconfirm": "Tem a certeza de que pretende apagar esta conversação por completo? Não será apagada para os outros participantes da conversação.", - "addon.messages.deleteallselfconfirm": "Tem a certeza de que pretende apagar toda esta conversação pessoal?", - "addon.messages.deleteconversation": "Apagar conversação", - "addon.messages.deleteforeveryone": "Apagar para mim e para todos", - "addon.messages.deletemessage": "Apagar mensagem", - "addon.messages.deletemessageconfirmation": "Tem a certeza de que pretende apagar esta mensagem? Apenas será excluída do seu histórico de mensagens. Poderá ser visualizada pelo utilizador que enviou ou recebeu a mensagem.", - "addon.messages.errordeletemessage": "Erro ao eliminar a mensagem.", - "addon.messages.errorwhileretrievingcontacts": "Erro ao obter contactos do servidor.", - "addon.messages.errorwhileretrievingdiscussions": "Erro ao obter tópicos de discussão do servidor.", - "addon.messages.errorwhileretrievingmessages": "Erro ao obter mensagens do servidor.", - "addon.messages.errorwhileretrievingusers": "Erro ao obter utilizadores do servidor.", - "addon.messages.groupconversations": "Grupo", - "addon.messages.groupinfo": "Informação do grupo", - "addon.messages.individualconversations": "Privada", - "addon.messages.info": "Informação do utilizador", - "addon.messages.isnotinyourcontacts": "{{$a}} não pertence aos seus contactos", - "addon.messages.message": "Mensagem", - "addon.messages.messagenotsent": "A mensagem não foi enviada. Tente novamente mais tarde.", - "addon.messages.messagepreferences": "Preferências das mensagens", - "addon.messages.messages": "Mensagens", - "addon.messages.muteconversation": "Ignorar", - "addon.messages.mutedconversation": "Conversação ignorada", - "addon.messages.newmessage": "Nova mensagem", - "addon.messages.newmessages": "Novas mensagens", - "addon.messages.nocontactrequests": "Sem pedidos de contacto", - "addon.messages.nocontactsgetstarted": "Sem contactos", - "addon.messages.nofavourites": "Nenhuma conversação com estrela", - "addon.messages.nogroupconversations": "Nenhuma conversação de grupo", - "addon.messages.noindividualconversations": "Nenhuma conversação privada", - "addon.messages.nomessagesfound": "Não foram encontradas mensagens", - "addon.messages.noncontacts": "Outros contactos", - "addon.messages.nousersfound": "Nenhum utilizador encontrado", - "addon.messages.numparticipants": "{{$a}} participantes", - "addon.messages.removecontact": "Remover contacto", - "addon.messages.removecontactconfirm": "Tem a certeza de que pretende remover {{$a}} dos seus contactos?", - "addon.messages.removefromfavourites": "Remover estrela", - "addon.messages.removefromyourcontacts": "Remover dos contactos", - "addon.messages.requests": "Pedidos", - "addon.messages.requirecontacttomessage": "Tem de pedir a {{$a}} que o adicione como um contacto para lhe(s) poder enviar mensagens.", - "addon.messages.searchcombined": "Procurar pessoas e mensagens", - "addon.messages.selfconversation": "Área pessoal", - "addon.messages.selfconversationdefaultmessage": "Guarde rascunhos de mensagens, hiperligações, notas, etc. para aceder mais tarde.", - "addon.messages.sendcontactrequest": "Enviar pedido de contacto", - "addon.messages.showdeletemessages": "Mostrar o apagar mensagens", - "addon.messages.type_blocked": "Bloqueado", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Resultados da pesquisa", - "addon.messages.type_strangers": "Outros", - "addon.messages.unabletomessage": "Não é permitido enviar mensagens para este utilizador", - "addon.messages.unblockuser": "Utilizador desbloqueado", - "addon.messages.unblockuserconfirm": "Tem a certeza de que pretende desbloquear {{$a}}?", - "addon.messages.unmuteconversation": "Não ignorar", - "addon.messages.useentertosend": "Usar 'Enter' para enviar", - "addon.messages.useentertosenddescdesktop": "Se desativar esta opção, pode utilizar Ctrl+Enter para enviar a mensagem.", - "addon.messages.useentertosenddescmac": "Se desativar esta opção, pode utilizar Cmd+Enter para enviar a mensagem.", - "addon.messages.userwouldliketocontactyou": "{{$a}} gostaria de entrar em contacto consigo", - "addon.messages.warningconversationmessagenotsent": "Não foi possível enviar mensagens para a conversação {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Não foi possível enviar mensagens ao utilizador {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Gostaria de entrar em contacto consigo", - "addon.messages.you": "Eu:", - "addon.messages.youhaveblockeduser": "Bloqueou este utilizador.", - "addon.messages.yourcontactrequestpending": "O seu pedido de contacto com {{$a}} está pendente", - "addon.mod_assign.acceptsubmissionstatement": "Por favor, aceite a declaração de submissão.", - "addon.mod_assign.addattempt": "Permitir nova tentativa", - "addon.mod_assign.addnewattempt": "Adicionar nova tentativa", - "addon.mod_assign.addnewattemptfromprevious": "Adicionar nova tentativa baseada na submissão anterior", - "addon.mod_assign.addsubmission": "Enviar trabalho", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "O enunciado do trabalho apenas estará disponível a partir de {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Aceitar trabalhos a partir de", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Pode submeter o trabalho a partir de {{$a}}", - "addon.mod_assign.applytoteam": "Atribuir notas e comentários a todo o grupo", - "addon.mod_assign.assignmentisdue": "Já terminou o prazo para submeter trabalhos", - "addon.mod_assign.attemptnumber": "Número da tentativa", - "addon.mod_assign.attemptreopenmethod": "Tentativas reabertas", - "addon.mod_assign.attemptreopenmethod_manual": "Manualmente", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automaticamente até obter aproveitamento", - "addon.mod_assign.attemptsettings": "Configurações da tentativa", - "addon.mod_assign.cannoteditduetostatementsubmission": "Não pode adicionar ou editar uma submissão na aplicação porque não foi possível recuperar a declaração de submissão desde o site.", - "addon.mod_assign.cannotgradefromapp": "Alguns métodos de avaliação ainda não são suportados pela aplicação e não podem ser modificados.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Não pode submeter o trabalho na aplicação porque não foi possível obter a declaração de submissão desde o site.", - "addon.mod_assign.confirmsubmission": "Tem a certeza de que pretende submeter o seu trabalho para avaliação? Não poderá realizar mais alterações.", - "addon.mod_assign.currentattempt": "Tentativa {{$a}}", - "addon.mod_assign.currentattemptof": "Esta é a {{$a.attemptnumber}}ª tentativa (em {{$a.maxattempts}} tentativas permitidas).", - "addon.mod_assign.currentgrade": "Nota atual na pauta", - "addon.mod_assign.cutoffdate": "Data de fecho", - "addon.mod_assign.defaultteam": "Grupo predefinido", - "addon.mod_assign.duedate": "Data limite para submeter trabalhos", - "addon.mod_assign.duedateno": "Sem data limite", - "addon.mod_assign.duedatereached": "A data limite de submissão deste trabalho já foi ultrapassada.", - "addon.mod_assign.editingstatus": "Editar o estado", - "addon.mod_assign.editsubmission": "Editar submissão", - "addon.mod_assign.erroreditpluginsnotsupported": "Não pode adicionar ou editar uma submissão na aplicação porque alguns módulos ainda não são suportados para edição.", - "addon.mod_assign.errorshowinginformation": "Não é possível mostrar informações da submissão", - "addon.mod_assign.extensionduedate": "Prolongamento do prazo", - "addon.mod_assign.feedbacknotsupported": "Este feedback não é suportado pela aplicação e pode não conter toda a informação.", - "addon.mod_assign.grade": "Nota", - "addon.mod_assign.graded": "Avaliado", - "addon.mod_assign.gradedby": "Avaliado por", - "addon.mod_assign.gradedfollowupsubmit": "Avaliada - seguir a submissão recebida", - "addon.mod_assign.gradedon": "Avaliado em", - "addon.mod_assign.gradelocked": "Esta nota está bloqueada ou foi ajustada na pauta.", - "addon.mod_assign.gradenotsynced": "Nota não está sincronizada", - "addon.mod_assign.gradeoutof": "Nota (de 0 a {{$a}})", - "addon.mod_assign.gradingstatus": "Estado da avaliação", - "addon.mod_assign.groupsubmissionsettings": "Configurações das submissões em grupo", - "addon.mod_assign.hiddenuser": "Participante", - "addon.mod_assign.latesubmissions": "Submissões com atraso", - "addon.mod_assign.latesubmissionsaccepted": "Disponível até {{$a}}", - "addon.mod_assign.markingworkflowstate": "Estado do processo de avaliação", - "addon.mod_assign.markingworkflowstateinmarking": "Em avaliação", - "addon.mod_assign.markingworkflowstateinreview": "Em validação", - "addon.mod_assign.markingworkflowstatenotmarked": "Não avaliado", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Avaliação validada", - "addon.mod_assign.markingworkflowstatereadyforreview": "Avaliação concluída", - "addon.mod_assign.markingworkflowstatereleased": "Avaliação publicada", - "addon.mod_assign.modulenameplural": "Trabalhos", - "addon.mod_assign.multipleteams": "Inscrito em mais do que um grupo", - "addon.mod_assign.multipleteams_desc": "Este trabalho requer submissão em grupos. Você é membro de mais do que um grupo. Para conseguir submeter deve ser membro de apenas um grupo. Por favor, contacte o seu professor para alterar a sua participação nos grupos.", - "addon.mod_assign.noattempt": "Nenhuma submissão", - "addon.mod_assign.nomoresubmissionsaccepted": "Apenas disponível aos alunos a quem tenha sido concedida uma extensão do prazo.", - "addon.mod_assign.noonlinesubmissions": "Este trabalho não requer que submeta nada online.", - "addon.mod_assign.nosubmission": "Sem submissão", - "addon.mod_assign.notallparticipantsareshown": "Não são mostrados os participantes sem submissão.", - "addon.mod_assign.noteam": "Não está inscrito em nenhum grupo", - "addon.mod_assign.noteam_desc": "Este trabalho requer submissão em grupos. Como não é membro de qualquer grupo, não é possível criar uma submissão. Por favor, contacte o seu professor para ser adicionado a um grupo.", - "addon.mod_assign.notgraded": "Sem avaliação", - "addon.mod_assign.numberofdraftsubmissions": "Trabalhos em curso", - "addon.mod_assign.numberofparticipants": "Número de alunos", - "addon.mod_assign.numberofsubmissionsneedgrading": "Requerem avaliação", - "addon.mod_assign.numberofsubmittedassignments": "Número de trabalhos submetidos", - "addon.mod_assign.numberofteams": "Grupos", - "addon.mod_assign.numwords": "{{$a}} palavras", - "addon.mod_assign.outof": "{{$a.current}} de {{$a.total}}", - "addon.mod_assign.overdue": "Trabalho está '{{$a}}' atrasado", - "addon.mod_assign.submission": "Trabalho", - "addon.mod_assign.submissioneditable": "Os alunos podem editar esta submissão", - "addon.mod_assign.submissionnoteditable": "O aluno não pode editar esta submissão", - "addon.mod_assign.submissionnotsupported": "Esta submissão não é suportada pela aplicação e pode não conter toda a informação.", - "addon.mod_assign.submissionslocked": "Este trabalho não está a aceitar submissões", - "addon.mod_assign.submissionstatus": "Estado da submissão", - "addon.mod_assign.submissionstatus_": "Não submetido", - "addon.mod_assign.submissionstatus_draft": "Trabalho em curso (não submetido)", - "addon.mod_assign.submissionstatus_marked": "Avaliado", - "addon.mod_assign.submissionstatus_new": "Nenhuma submissão", - "addon.mod_assign.submissionstatus_reopened": "Reaberta", - "addon.mod_assign.submissionstatus_submitted": "Submetido para avaliação", - "addon.mod_assign.submissionstatusheading": "Estado do trabalho", - "addon.mod_assign.submissionteam": "Grupo", - "addon.mod_assign.submitassignment": "Submeter o trabalho", - "addon.mod_assign.submitassignment_help": "Assim que este trabalho for submetido não poderá fazer mais alterações.", - "addon.mod_assign.submittedearly": "O trabalho foi submetido '{{$a}}' antes do prazo", - "addon.mod_assign.submittedlate": "O trabalho foi submetido '{{$a}}' depois do prazo", - "addon.mod_assign.timemodified": "Última modificação", - "addon.mod_assign.timeremaining": "Tempo restante", - "addon.mod_assign.ungroupedusers": "A configuração 'Requer grupo para submeter trabalho' está ativa e existem utilizadores que não pertencem a nenhum grupo, ou que se encontram inscritos em mais do que um, o que fará com que estes fiquem impossibilitados de submeter os seus trabalhos.", - "addon.mod_assign.ungroupedusersoptional": "A configuração 'Os alunos submetem em grupos' está ativada e alguns utilizadores não são membros de nenhum grupo ou são membros de mais de um grupo. Tenha em atenção de que estes alunos submeterão como membros do 'Grupo predefinido'.", - "addon.mod_assign.unlimitedattempts": "Ilimitado", - "addon.mod_assign.userswhoneedtosubmit": "Utilizadores que têm de submeter: {{$a}}", - "addon.mod_assign.userwithid": "Utilizador com ID {{id}}", - "addon.mod_assign.viewsubmission": "Ver trabalho", - "addon.mod_assign.warningsubmissiongrademodified": "A nota da submissão foi modificada no site.", - "addon.mod_assign.warningsubmissionmodified": "A submissão do utilizador foi modificada no site.", - "addon.mod_assign.wordlimit": "Limite de palavras", - "addon.mod_assign_feedback_comments.pluginname": "Comentários de feedback", - "addon.mod_assign_feedback_editpdf.pluginname": "Anotar PDF", - "addon.mod_assign_feedback_file.pluginname": "Ficheiro de feedback", - "addon.mod_assign_submission_comments.pluginname": "Comentários à submissão", - "addon.mod_assign_submission_file.pluginname": "Submissão de ficheiros", - "addon.mod_assign_submission_onlinetext.pluginname": "Submissões de texto online", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "O limite de palavras para este trabalho é de {{$a.limit}} palavras e você está a tentar submeter {{$a.count}} palavras. Por favor, reveja a sua submissão e tente novamente.", - "addon.mod_book.errorchapter": "Ocorreu um erro ao ler o capítulo do livro", - "addon.mod_book.modulenameplural": "Livros", - "addon.mod_book.navnexttitle": "Próximo: {{$a}}", - "addon.mod_book.navprevtitle": "Anterior: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Capítulos do livro", - "addon.mod_book.toc": "Índice", - "addon.mod_chat.beep": "Chamar", - "addon.mod_chat.chatreport": "Sessões de chat", - "addon.mod_chat.currentusers": "Utilizadores no chat", - "addon.mod_chat.enterchat": "Clique aqui para entrar no chat", - "addon.mod_chat.entermessage": "Introduza a sua mensagem", - "addon.mod_chat.errorwhileconnecting": "Erro ao entrar no chat.", - "addon.mod_chat.errorwhilegettingchatdata": "Erro ao obter os dados do chat.", - "addon.mod_chat.errorwhilegettingchatusers": "Erro ao obter os utilizadores do chat.", - "addon.mod_chat.errorwhileretrievingmessages": "Erro ao obter mensagens do servidor.", - "addon.mod_chat.errorwhilesendingmessage": "Erro ao enviar a mensagem.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} chama toda a gente!", - "addon.mod_chat.messagebeepsyou": "{{$a}} acabou de o chamar!", - "addon.mod_chat.messageenter": "{{$a}} acabou de entrar neste chat", - "addon.mod_chat.messageexit": "{{$a}} acabou de sair deste chat", - "addon.mod_chat.messages": "Mensagens", - "addon.mod_chat.messageyoubeep": "Você chamou {{$a}}", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "Tem de estar online para poder enviar mensagens.", - "addon.mod_chat.nomessages": "Ainda não há mensagens", - "addon.mod_chat.nosessionsfound": "Nenhuma sessão encontrada", - "addon.mod_chat.saidto": "disse a", - "addon.mod_chat.send": "Enviar", - "addon.mod_chat.sessionstart": "A próxima sessão de chat será {{$a.date}} (daqui a {{$a.fromnow}})", - "addon.mod_chat.showincompletesessions": "Mostrar sessões incompletas", - "addon.mod_chat.talk": "Falar", - "addon.mod_chat.viewreport": "Ver gravações de sessões anteriores", - "addon.mod_choice.cannotsubmit": "Desculpe, surgiu um problema ao submeter a sua escolha. Por favor, tente novamente.", - "addon.mod_choice.choiceoptions": "Opções de resposta", - "addon.mod_choice.errorgetchoice": "Erro ao obter os dados da sondagem.", - "addon.mod_choice.expired": "Esta atividade terminou {{$a}}.", - "addon.mod_choice.full": "Completa", - "addon.mod_choice.modulenameplural": "Sondagens", - "addon.mod_choice.noresultsviewable": "Os resultados da sondagem ainda não estão disponíveis.", - "addon.mod_choice.notopenyet": "Esta atividade só estará disponível {{$a}}", - "addon.mod_choice.numberofuser": "Número de respostas", - "addon.mod_choice.numberofuserinpercentage": "Percentagem de respostas", - "addon.mod_choice.previewonly": "Esta é apenas uma pré-visualização das opções disponíveis para esta atividade. Não poderá submeter a sua escolha até {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Resultados anónimos serão publicados a seguir à sua resposta.", - "addon.mod_choice.publishinfoanonclose": "Resultados anónimos serão publicados após a atividade ser encerrada.", - "addon.mod_choice.publishinfofullafter": "Resultados completos com as escolhas de todos. Serão publicados a seguir à sua resposta.", - "addon.mod_choice.publishinfofullclose": "Resultados completos com as escolhas de todos. Serão publicados após a atividade ser encerrada.", - "addon.mod_choice.publishinfonever": "Os resultados desta atividade não serão publicados a seguir à sua resposta.", - "addon.mod_choice.removemychoice": "Apagar a minha resposta", - "addon.mod_choice.responses": "Respostas", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% dos utilizadores escolheram esta opção: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Gráfico", - "addon.mod_choice.resultsnotsynced": "A sua última resposta tem de ser sincronizada para estar incluída nos resultados.", - "addon.mod_choice.savemychoice": "Guardar a minha resposta", - "addon.mod_choice.userchoosethisoption": "Utilizadores que escolheram esta opção", - "addon.mod_choice.yourselection": "A sua resposta", - "addon.mod_data.addentries": "Adicionar registos", - "addon.mod_data.advancedsearch": "Pesquisa avançada", - "addon.mod_data.alttext": "Texto alternativo", - "addon.mod_data.approve": "Aprovar", - "addon.mod_data.approved": "Aprovado", - "addon.mod_data.ascending": "Ascendente", - "addon.mod_data.authorfirstname": "Primeiro nome do autor", - "addon.mod_data.authorlastname": "Apelido do autor", - "addon.mod_data.confirmdeleterecord": "Tem a certeza de que pretende apagar este registo?", - "addon.mod_data.descending": "Descendente", - "addon.mod_data.disapprove": "Anular aprovação", - "addon.mod_data.edittagsnotsupported": "A edição de palavras-chave não é suportada pela aplicação.", - "addon.mod_data.emptyaddform": "Não preencheu nenhum campo!", - "addon.mod_data.entrieslefttoadd": "Tem que adicionar mais {{$a.entriesleft}} registo(s) para concluir esta atividade", - "addon.mod_data.entrieslefttoaddtoview": "Tem que adicionar mais {{$a.entrieslefttoview}} registo(s) para conseguir visualizar as entradas dos outros participantes.", - "addon.mod_data.errorapproving": "Erro ao aprovar ou desaprovar uma entrada.", - "addon.mod_data.errordeleting": "Erro ao apagar a entrada.", - "addon.mod_data.errormustsupplyvalue": "Indique aqui um valor.", - "addon.mod_data.expired": "A atividade terminou em {{$a}} e já não está disponível", - "addon.mod_data.fields": "Campos", - "addon.mod_data.foundrecords": "Registos encontrados: {{$a.num}}/{{$a.max}} (Nova pesquisa)", - "addon.mod_data.gettinglocation": "A obter localização", - "addon.mod_data.latlongboth": "É necessário a latitude e a longitude.", - "addon.mod_data.locationpermissiondenied": "Foi negada a permissão para aceder à sua localização.", - "addon.mod_data.menuchoose": "Selecione...", - "addon.mod_data.modulenameplural": "Bases de dados", - "addon.mod_data.more": "Mais", - "addon.mod_data.mylocation": "Minha localização", - "addon.mod_data.nomatch": "Não foram encontrados registos correspondentes!", - "addon.mod_data.norecords": "Não existem registos na base de dados", - "addon.mod_data.notapproved": "O registo ainda não foi aprovado.", - "addon.mod_data.notopenyet": "A atividade apenas será disponibilizada em {{$a}}", - "addon.mod_data.numrecords": "{{$a}} registos", - "addon.mod_data.other": "Outro", - "addon.mod_data.recordapproved": "Registo aprovado", - "addon.mod_data.recorddeleted": "Registo apagado", - "addon.mod_data.recorddisapproved": "Entrada não aprovada", - "addon.mod_data.resetsettings": "Reiniciar filtros", - "addon.mod_data.search": "Pesquisar", - "addon.mod_data.searchbytagsnotsupported": "Pesquisar por palavras-chave não é suportado pela aplicação.", - "addon.mod_data.selectedrequired": "Todos os selecionados são obrigatórios", - "addon.mod_data.single": "Ver registo", - "addon.mod_data.tagarea_data_records": "Registos de dados", - "addon.mod_data.timeadded": "Data e hora de criação", - "addon.mod_data.timemodified": "Data e hora da última modificação", - "addon.mod_data.usedate": "Incluir na pesquisa.", - "addon.mod_feedback.analysis": "Análise", - "addon.mod_feedback.anonymous": "Anónimo", - "addon.mod_feedback.anonymous_entries": "Respostas anónimas ({{$a}})", - "addon.mod_feedback.average": "Média", - "addon.mod_feedback.captchaofflinewarning": "Inquérito com CAPTCHA não pode ser concluído em modo offline, ou se não estiver configurado, ou o servidor não está acessível.", - "addon.mod_feedback.complete_the_form": "Responder às perguntas", - "addon.mod_feedback.completed_feedbacks": "Respostas submetidas", - "addon.mod_feedback.continue_the_form": "Continuar a responder às perguntas", - "addon.mod_feedback.feedback_is_not_open": "O inquérito não está aberto", - "addon.mod_feedback.feedback_submitted_offline": "O Inquérito foi gravado para ser enviado mais tarde.", - "addon.mod_feedback.feedbackclose": "Permitir respostas até", - "addon.mod_feedback.feedbackopen": "Permitir respostas a partir de", - "addon.mod_feedback.mapcourses": "Associar o inquérito a disciplinas", - "addon.mod_feedback.maximal": "Máximo", - "addon.mod_feedback.minimal": "Mínimo", - "addon.mod_feedback.mode": "Modo", - "addon.mod_feedback.modulenameplural": "Inquéritos", - "addon.mod_feedback.next_page": "Página seguinte", - "addon.mod_feedback.non_anonymous": "O nome do utilizador será registado e identificado com as respostas", - "addon.mod_feedback.non_anonymous_entries": "Respostas não anónimas ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Alunos que não responderam ({{$a}})", - "addon.mod_feedback.not_selected": "Não respondido", - "addon.mod_feedback.not_started": "Por iniciar", - "addon.mod_feedback.numberoutofrange": "Valor fora do intervalo", - "addon.mod_feedback.overview": "Visão global", - "addon.mod_feedback.page_after_submit": "Mensagem de conclusão", - "addon.mod_feedback.preview": "Pré-visualização", - "addon.mod_feedback.previous_page": "Página anterior", - "addon.mod_feedback.questions": "Questões", - "addon.mod_feedback.response_nr": "Número da resposta", - "addon.mod_feedback.responses": "Respostas", - "addon.mod_feedback.save_entries": "Submeter respostas", - "addon.mod_feedback.show_entries": "Respostas", - "addon.mod_feedback.show_nonrespondents": "Utilizadores que não responderam", - "addon.mod_feedback.started": "Iniciado", - "addon.mod_feedback.this_feedback_is_already_submitted": "Já concluiu esta atividade", - "addon.mod_folder.emptyfilelist": "Não existem ficheiros para mostrar.", - "addon.mod_folder.modulenameplural": "Pastas", - "addon.mod_forum.addanewdiscussion": "Criar um novo tópico", - "addon.mod_forum.addanewquestion": "Criar uma nova pergunta", - "addon.mod_forum.addanewtopic": "Criar um novo tópico", - "addon.mod_forum.addtofavourites": "Marcar este tópico com estrela", - "addon.mod_forum.advanced": "Avançado", - "addon.mod_forum.cannotadddiscussion": "Criar novos tópicos neste fórum requer adesão a grupo.", - "addon.mod_forum.cannotadddiscussionall": "Não tem permissão para criar um novo tópico disponível para todos os participantes", - "addon.mod_forum.cannotcreatediscussion": "Não foi possível criar o novo tópico de discussão", - "addon.mod_forum.couldnotadd": "Não foi possível inserir a mensagem que submeteu devido a um erro desconhecido", - "addon.mod_forum.couldnotupdate": "Não foi possível atualizar a mensagem devido a um erro desconhecido", - "addon.mod_forum.cutoffdatereached": "A data de fecho do fórum já foi ultrapassada, por isso, não é possível publicar neste fórum.", - "addon.mod_forum.delete": "Apagar", - "addon.mod_forum.deletedpost": "A mensagem foi apagada", - "addon.mod_forum.deletesure": "Tem a certeza de que pretende apagar esta mensagem?", - "addon.mod_forum.discussion": "Tópico", - "addon.mod_forum.discussionlistsortbycreatedasc": "Ordenar a lista de tópicos por ordem crescente da data de criação", - "addon.mod_forum.discussionlistsortbycreateddesc": "Ordenar a lista de tópicos por ordem decrescente da data de criação", - "addon.mod_forum.discussionlistsortbylastpostasc": "Ordenar a lista de tópicos por ordem crescente da data da última mensagem", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Ordenar a lista de tópicos por ordem decrescente da data da última mensagem", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Ordenar a lista de tópicos por ordem crescente do número de respostas", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Ordenar a lista de tópicos por ordem decrescente do número de respostas", - "addon.mod_forum.discussionlocked": "Este tópico de discussão está bloqueado. Já não pode responder ao mesmo.", - "addon.mod_forum.discussionpinned": "Destacado", - "addon.mod_forum.discussionsubscription": "Subscrição da discussão", - "addon.mod_forum.edit": "Editar", - "addon.mod_forum.erroremptymessage": "O texto da mensagem não pode estar em branco", - "addon.mod_forum.erroremptysubject": "A assunto da mensagem não pode estar em branco", - "addon.mod_forum.errorgetforum": "Erro ao obter dados do fórum.", - "addon.mod_forum.errorgetgroups": "Erro ao obter as configurações do grupo.", - "addon.mod_forum.errorposttoallgroups": "Não foi possível criar nova conversação em todos os grupos.", - "addon.mod_forum.favouriteupdated": "A opção de marcar com estrela foi atualizada.", - "addon.mod_forum.forumnodiscussionsyet": "Ainda não existem tópicos de discussão neste fórum.", - "addon.mod_forum.group": "Grupo", - "addon.mod_forum.lastpost": "Última mensagem", - "addon.mod_forum.lockdiscussion": "Bloquear este tópico", - "addon.mod_forum.lockupdated": "A opção de bloquear foi atualizada.", - "addon.mod_forum.message": "Mensagem", - "addon.mod_forum.modeflatnewestfirst": "Mostrar respostas por ordem, a começar pela mais recente", - "addon.mod_forum.modeflatoldestfirst": "Mostrar respostas por ordem, a começar pela mais antiga", - "addon.mod_forum.modenested": "Mostrar respostas em lista encadeada", - "addon.mod_forum.modulenameplural": "Fóruns", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} tópicos de discussão", - "addon.mod_forum.numreplies": "{{numreplies}} respostas", - "addon.mod_forum.pindiscussion": "Destacar este tópico", - "addon.mod_forum.pinupdated": "A opção de destacar a publicação foi atualizada.", - "addon.mod_forum.postisprivatereply": "Esta mensagem foi enviada em privado e não está visível para outros participantes.", - "addon.mod_forum.posttoforum": "Submeter tópico", - "addon.mod_forum.posttomygroups": "Submeter uma cópia em todos os grupos", - "addon.mod_forum.privatereply": "Responder em privado", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Atualizar mensagens", - "addon.mod_forum.refreshposts": "Atualizar tópicos de discussão", - "addon.mod_forum.removefromfavourites": "Remover estrela deste tópico", - "addon.mod_forum.reply": "Responder", - "addon.mod_forum.replyplaceholder": "Escreva a sua resposta...", - "addon.mod_forum.subject": "Assunto", - "addon.mod_forum.tagarea_forum_posts": "Mensagens do fórum", - "addon.mod_forum.thisforumhasduedate": "A data limite para publicar neste fórum é {{$a}}.", - "addon.mod_forum.thisforumisdue": "A data limite para publicar neste fórum era {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Desbloquear este tópico", - "addon.mod_forum.unpindiscussion": "Remover destaque deste tópico", - "addon.mod_forum.unread": "Não lidas", - "addon.mod_forum.unreadpostsnumber": "{{$a}} mensagens não lidas", - "addon.mod_forum.yourreply": "A sua resposta", - "addon.mod_glossary.addentry": "Adicionar novo termo", - "addon.mod_glossary.aliases": "Palavra(s)-chave", - "addon.mod_glossary.attachment": "Anexo", - "addon.mod_glossary.browsemode": "Ver entradas", - "addon.mod_glossary.byalphabet": "Alfabeticamente", - "addon.mod_glossary.byauthor": "Agrupar por autor", - "addon.mod_glossary.bycategory": "Agrupar por categoria", - "addon.mod_glossary.bynewestfirst": "Ordenar por mais recentes", - "addon.mod_glossary.byrecentlyupdated": "Recentemente atualizados", - "addon.mod_glossary.bysearch": "Pesquisar", - "addon.mod_glossary.cannoteditentry": "Não é possível editar a entrada", - "addon.mod_glossary.casesensitive": "Respeitar maiúsculas/minúsculas", - "addon.mod_glossary.categories": "Categorias", - "addon.mod_glossary.concept": "Conceito", - "addon.mod_glossary.definition": "Descrição", - "addon.mod_glossary.entriestobesynced": "Entradas a ser sincronizadas", - "addon.mod_glossary.entrypendingapproval": "Este termo aguarda aprovação.", - "addon.mod_glossary.entryusedynalink": "Criar automaticamente hiperligações para este termo", - "addon.mod_glossary.errconceptalreadyexists": "Este termo já existe. Este glossário não permite termos repetidos.", - "addon.mod_glossary.errorloadingentries": "Ocorreu um erro ao carregar os termos.", - "addon.mod_glossary.errorloadingentry": "Ocorreu um erro ao carregar o termo.", - "addon.mod_glossary.errorloadingglossary": "Ocorreu um erro ao carregar o glossário.", - "addon.mod_glossary.fillfields": "Os campos termo e descrição são obrigatórios.", - "addon.mod_glossary.fullmatch": "Localizar apenas Palavras inteiras", - "addon.mod_glossary.linking": "Hiperligações automáticas", - "addon.mod_glossary.modulenameplural": "Glossários", - "addon.mod_glossary.noentriesfound": "Não foi encontrado nenhum termo.", - "addon.mod_glossary.searchquery": "Pesquisa", - "addon.mod_glossary.tagarea_glossary_entries": "Entrada do glóssário", - "addon.mod_h5pactivity.all_attempts": "Todas as tentativas do utilizador", - "addon.mod_h5pactivity.answer_checked": "Resposta verificada", - "addon.mod_h5pactivity.answer_correct": "A sua resposta está correta", - "addon.mod_h5pactivity.answer_fail": "Resposta incorreta", - "addon.mod_h5pactivity.answer_incorrect": "A sua resposta está incorreta", - "addon.mod_h5pactivity.answer_pass": "Resposta correta", - "addon.mod_h5pactivity.attempt": "Tentativa", - "addon.mod_h5pactivity.attempt_completion_no": "Esta tentativa não está como concluída", - "addon.mod_h5pactivity.attempt_completion_yes": "Esta tentativa está concluída", - "addon.mod_h5pactivity.attempt_success_fail": "Reprovado", - "addon.mod_h5pactivity.attempt_success_pass": "Aprovado", - "addon.mod_h5pactivity.attempt_success_unknown": "Não reportado", - "addon.mod_h5pactivity.attempts_none": "Não existem tentativas deste utilizador para mostrar.", - "addon.mod_h5pactivity.completion": "Conclusão", - "addon.mod_h5pactivity.downloadh5pfile": "Descarregar ficheiro H5P", - "addon.mod_h5pactivity.duration": "Duração", - "addon.mod_h5pactivity.errorgetactivity": "Erro ao obter dados da atividade do H5P.", - "addon.mod_h5pactivity.filestatenotdownloaded": "O pacote H5P ainda não foi descarregado. Tem de descarregar primeiro para poder usá-lo.", - "addon.mod_h5pactivity.filestateoutdated": "O pacote H5P foi modificado desde a última vez que foi descarregado. Tem de o descarregar novamente para poder usá-lo.", - "addon.mod_h5pactivity.maxscore": "Pontuação máxima", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "Minhas tentativas", - "addon.mod_h5pactivity.no_compatible_track": "Essa interação ({{$a}}) não fornece informações de monitorização ou a monitorização fornecida não é compatível com a versão atual da atividade.", - "addon.mod_h5pactivity.offlinedisabledwarning": "Tem de estar online para visualizar o pacote H5P.", - "addon.mod_h5pactivity.outcome": "Resultado da aprendizagem", - "addon.mod_h5pactivity.previewmode": "Este conteúdo está a ser exibido no modo de visualização. Nenhuma monitorização da tentativa será armazenada.", - "addon.mod_h5pactivity.result_fill-in": "Preencher espaços", - "addon.mod_h5pactivity.result_other": "Tipo de interação desconhecido", - "addon.mod_h5pactivity.review_my_attempts": "Ver as minhas tentativas", - "addon.mod_h5pactivity.score": "Pontuação", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} em {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "Data/hora de início", - "addon.mod_h5pactivity.totalscore": "Pontuação total", - "addon.mod_h5pactivity.viewattempt": "Ver a tentativa {{$a}}", - "addon.mod_imscp.deploymenterror": "Erro no pacote de conteúdo!", - "addon.mod_imscp.modulenameplural": "Pacotes IMS", - "addon.mod_imscp.showmoduledescription": "Mostrar descrição", - "addon.mod_imscp.toc": "Índice", - "addon.mod_lesson.answer": "Resposta", - "addon.mod_lesson.attempt": "Tentativa: {{$a}}", - "addon.mod_lesson.attemptheader": "Tentativa", - "addon.mod_lesson.attemptsremaining": "Ainda pode realizar {{$a}} tentativa(s)", - "addon.mod_lesson.averagescore": "Pontuação média", - "addon.mod_lesson.averagetime": "Tempo médio", - "addon.mod_lesson.branchtable": "Página de conteúdo", - "addon.mod_lesson.cannotfindattempt": "Erro: não foi possível encontrar a tentativa", - "addon.mod_lesson.cannotfinduser": "Erro: não foi possível encontrar utilizadores", - "addon.mod_lesson.clusterjump": "Pergunta não vista dentro do grupo", - "addon.mod_lesson.completed": "Completou", - "addon.mod_lesson.congratulations": "Parabéns - chegou ao fim da lição", - "addon.mod_lesson.continue": "Continuar", - "addon.mod_lesson.continuetonextpage": "Continuar para a página seguinte", - "addon.mod_lesson.defaultessayresponse": "A sua resposta à pergunta de desenvolvimento será avaliada pelo professor.", - "addon.mod_lesson.detailedstats": "Estatísticas detalhadas", - "addon.mod_lesson.didnotanswerquestion": "Não respondeu a esta pergunta", - "addon.mod_lesson.displayofgrade": "Exibição da nota (apenas para alunos)", - "addon.mod_lesson.displayscorewithessays": "

                    Obteve {{$a.score}} num máximo de {{$a.tempmaxgrade}} nas respostas avaliadas automaticamente.

                    \n

                    As suas respostas às perguntas de desenvolvimento ({{$a.essayquestions}}) serão avaliadas posteriormente e a pontuação correspondente será adicionada à pontuação final.

                    \n

                    A sua nota atual, sem as perguntas que faltam avaliar, é de {{$a.score}} num máximo de {{$a.grade}}.

                    ", - "addon.mod_lesson.displayscorewithoutessays": "Obteve uma pontuação de {{$a.score}} num máximo de {{$a.grade}} pontos.", - "addon.mod_lesson.emptypassword": "A palava-chave não pode estar em branco", - "addon.mod_lesson.enterpassword": "Introduza a senha:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Não respondeu a nenhuma pergunta. Obteve uma classificação de 0 para esta lição.", - "addon.mod_lesson.errorprefetchrandombranch": "Esta lição contém um salto para uma página de conteúdo aleatório. Não pode ser tentada na aplicação antes de ser iniciada no site.", - "addon.mod_lesson.errorreviewretakenotlast": "Já não é possível rever esta tentativa porque já foi terminada outra tentativa.", - "addon.mod_lesson.finish": "Terminar", - "addon.mod_lesson.finishretakeoffline": "Esta tentativa foi concluída em modo offline.", - "addon.mod_lesson.firstwrong": "Não respondeu corretamente. Gostaria de tentar responder novamente? (se responder corretamente não contará para a sua nota final.)", - "addon.mod_lesson.gotoendoflesson": "Ir para o fim da lição", - "addon.mod_lesson.grade": "Nota", - "addon.mod_lesson.highscore": "Melhor nota", - "addon.mod_lesson.hightime": "Tempo máximo", - "addon.mod_lesson.leftduringtimed": "Interrompeu uma lição com tempo limite.
                    Clique em Continuar para reiniciar à lição.", - "addon.mod_lesson.leftduringtimednoretake": "Interrompeu uma lição com tempo limite
                    e não é permitido repetir ou continuar a lição.", - "addon.mod_lesson.lessonmenu": "Menu da lição", - "addon.mod_lesson.lessonstats": "Estatísticas da lição", - "addon.mod_lesson.linkedmedia": "Conteúdo de suporte", - "addon.mod_lesson.loginfail": "A autenticação falhou. Tente de novo...", - "addon.mod_lesson.lowscore": "Nota mais baixa", - "addon.mod_lesson.lowtime": "Tempo mínimo", - "addon.mod_lesson.maximumnumberofattemptsreached": "Atingiu o número máximo de tentativas permitido - A lição vai avançar para a página seguinte", - "addon.mod_lesson.modattemptsnoteacher": "A possibilidade de revisão só funciona para os alunos.", - "addon.mod_lesson.modulenameplural": "Lições", - "addon.mod_lesson.noanswer": "Uma ou mais perguntas estão ainda por responder. Por favor, volte atrás e submeta uma resposta.", - "addon.mod_lesson.nolessonattempts": "Não houve tentativas de realização desta lição.", - "addon.mod_lesson.nolessonattemptsgroup": "Não foram realizadas tentativas por membros do grupo {{$a}} nesta lição.", - "addon.mod_lesson.notcompleted": "Incompleta", - "addon.mod_lesson.numberofcorrectanswers": "Número de respostas corretas: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Perguntas respondidas: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Número de questões respondidas: {{$a.nquestions}} (Tem de responder pelo menos a {{$a.minquestions}} questões)", - "addon.mod_lesson.ongoingcustom": "Até agora obteve {{$a.score}} num máximo de {{$a.currenthigh}} pontos.", - "addon.mod_lesson.ongoingnormal": "Respondeu corretamente a {{$a.correct}} de {{$a.viewed}} perguntas.", - "addon.mod_lesson.or": "OU", - "addon.mod_lesson.overview": "Perspetiva global", - "addon.mod_lesson.preview": "Pré-visualização", - "addon.mod_lesson.progressbarteacherwarning2": "Não vê a barra de progresso porque pode editar esta lição", - "addon.mod_lesson.progresscompleted": "Completou {{$a}}% da lição", - "addon.mod_lesson.question": "Inserir página de pergunta", - "addon.mod_lesson.rawgrade": "Nota bruta", - "addon.mod_lesson.reports": "Relatórios", - "addon.mod_lesson.response": "Feedback", - "addon.mod_lesson.retakefinishedinsync": "Uma tentativa offline foi sincronizada. Pretende rever a tentativa?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Revisão", - "addon.mod_lesson.reviewlesson": "Rever lição", - "addon.mod_lesson.reviewquestionback": "Sim, quero tentar novamente", - "addon.mod_lesson.reviewquestioncontinue": "Não, quero avançar para a pergunta seguinte", - "addon.mod_lesson.secondpluswrong": "Resposta incorreta. Quer voltar a tentar?", - "addon.mod_lesson.submit": "Submeter", - "addon.mod_lesson.teacherjumpwarning": "Nesta lição existem páginas que seguem para {{$a.cluster}} ou para {{$a.unseen}}. Esta sequência será ignorada e a lição seguirá para a página seguinte. Para testar a sequência das páginas, assuma o papel de aluno.", - "addon.mod_lesson.teacherongoingwarning": "A exibição da pontuação no decorrer da lição só é visível para os alunos. Para ver a pontuação no decorrer da lição entre como aluno.", - "addon.mod_lesson.teachertimerwarning": "O cronómetro só é visível para os alunos. Para testar esta funcionalidade, entre como aluno.", - "addon.mod_lesson.thatsthecorrectanswer": "A sua resposta está correta.", - "addon.mod_lesson.thatsthewronganswer": "A sua resposta não está correta.", - "addon.mod_lesson.timeremaining": "Tempo restante", - "addon.mod_lesson.timetaken": "Tempo gasto", - "addon.mod_lesson.unseenpageinbranch": "Pergunta não vista da sequência", - "addon.mod_lesson.warningretakefinished": "A tentativa foi terminada no site.", - "addon.mod_lesson.welldone": "Muito bem!", - "addon.mod_lesson.youhaveseen": "Já viu mais do que uma página desta lição.
                    Quer continuar a partir da última página que viu?", - "addon.mod_lesson.youranswer": "A sua resposta", - "addon.mod_lesson.yourcurrentgradeisoutof": "A sua nota atual é {{$a.grade}} em {{$a.total}}", - "addon.mod_lesson.youshouldview": "Deve responder a pelo menos: {{$a}}", - "addon.mod_lti.errorgetlti": "Erro ao obter os dados do módulo.", - "addon.mod_lti.errorinvalidlaunchurl": "O URL não é válido.", - "addon.mod_lti.launchactivity": "Iniciar a atividade", - "addon.mod_lti.modulenameplural": "Ferramentas externas", - "addon.mod_page.errorwhileloadingthepage": "Erro ao carregar o conteúdo da página.", - "addon.mod_page.modulenameplural": "Páginas", - "addon.mod_quiz.answercolon": "Resposta:", - "addon.mod_quiz.attemptfirst": "Primeira tentativa", - "addon.mod_quiz.attemptlast": "Última tentativa", - "addon.mod_quiz.attemptnumber": "Tentativa", - "addon.mod_quiz.attemptquiznow": "Responder ao teste agora", - "addon.mod_quiz.attemptstate": "Estado", - "addon.mod_quiz.canattemptbutnotsubmit": "Pode tentar responder a este teste na aplicação, mas precisará de enviar a tentativa através do navegador pelos seguintes motivos:", - "addon.mod_quiz.cannotsubmitquizdueto": "Esta tentativa de resolução do teste não pode ser submetida pelas seguintes razões:", - "addon.mod_quiz.clearchoice": "Limpar a minha escolha", - "addon.mod_quiz.comment": "Comentário", - "addon.mod_quiz.completedon": "Terminada em", - "addon.mod_quiz.confirmclose": "Está prestes a concluir esta tentativa. Uma vez concluída, não poderá alterar as suas respostas.", - "addon.mod_quiz.confirmcontinueoffline": "Esta tentativa não foi sincronizada desde {{$a}}. Se continuou esta tentativa noutro dispositivo desde então, pode perder dados.", - "addon.mod_quiz.confirmleavequizonerror": "Ocorreu um erro ao guardar as respostas. Tem a certeza de que pretende sair do teste?", - "addon.mod_quiz.confirmstart": "A sua tentativa tem um limite de tempo de {{$a}}. Quando começar, o cronómetro começará a contar e não é possível parar ou fazer pausa. Tem de terminar a tentativa antes do tempo acabar. Tem a certeza de que pretende iniciar agora a tentativa de realização do teste?", - "addon.mod_quiz.confirmstartheader": "Tempo limite", - "addon.mod_quiz.connectionerror": "Ligação à rede perdida (gravação automática falhou).\n\nAnote quaisquer respostas inseridas nesta página nos últimos minutos e tente ligar novamente à rede.\n\nQuando a ligação for restabelecida, as suas respostas devem ser guardadas e esta mensagem irá desaparecer.", - "addon.mod_quiz.continueattemptquiz": "Continuar a última tentativa", - "addon.mod_quiz.continuepreview": "Continuar a última pré-visualização", - "addon.mod_quiz.errorbehaviournotsupported": "Este teste não pode ser respondido na aplicação porque o comportamento das questões não é suportado:", - "addon.mod_quiz.errordownloading": "Erro ao descarregar os dados necessários.", - "addon.mod_quiz.errorgetattempt": "Erro ao obter dados da tentativa.", - "addon.mod_quiz.errorgetquestions": "Erro ao obter perguntas.", - "addon.mod_quiz.errorgetquiz": "Erro ao obter dados de teste.", - "addon.mod_quiz.errorparsequestions": "Ocorreu um erro ao ler as perguntas. Tente o teste num navegador Web.", - "addon.mod_quiz.errorquestionsnotsupported": "Este teste não pode ser tentado na aplicação porque apenas contém tipos de perguntas não suportados pela aplicação:", - "addon.mod_quiz.errorrulesnotsupported": "Este teste não pode ser tentado na aplicação porque tem regras de acesso não suportadas pela aplicação:", - "addon.mod_quiz.errorsaveattempt": "Ocorreu um erro ao guardar os dados da tentativa.", - "addon.mod_quiz.feedback": "Feedback", - "addon.mod_quiz.finishattemptdots": "Terminar tentativa", - "addon.mod_quiz.finishnotsynced": "Terminado mas não sincronizado", - "addon.mod_quiz.grade": "Nota", - "addon.mod_quiz.gradeaverage": "Nota média", - "addon.mod_quiz.gradehighest": "Nota mais alta", - "addon.mod_quiz.grademethod": "Método de avaliação", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}", - "addon.mod_quiz.marks": "Nota", - "addon.mod_quiz.modulenameplural": "Testes", - "addon.mod_quiz.mustbesubmittedby": "Esta tentativa deve ser submetida até {{$a}}.", - "addon.mod_quiz.noquestions": "Ainda não foi adicionada nenhuma pergunta", - "addon.mod_quiz.noreviewattempt": "Não pode rever esta tentativa", - "addon.mod_quiz.notyetgraded": "Por avaliar", - "addon.mod_quiz.opentoc": "Abrir navegação", - "addon.mod_quiz.outof": "{{$a.grade}} num máximo de {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} num máximo de {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Feedback global", - "addon.mod_quiz.overdue": "Atingiu o tempo limite", - "addon.mod_quiz.overduemustbesubmittedby": "Esta tentativa atingiu o tempo limite. Já devia ter submetido a mesma. Se pretende ter avaliação nesta tentativa, deve submetê-la até {{$a}}. Se não submeter até lá, não irá obter nota para esta tentativa.", - "addon.mod_quiz.preview": "Pré-visualização", - "addon.mod_quiz.previewquiznow": "Pré-visualizar Teste", - "addon.mod_quiz.question": "Pergunta", - "addon.mod_quiz.quiznavigation": "Navegação do teste", - "addon.mod_quiz.quizpassword": "Senha do teste", - "addon.mod_quiz.reattemptquiz": "Iniciar uma nova tentativa de resolução do teste", - "addon.mod_quiz.requirepasswordmessage": "Para responder ao teste necessita da senha", - "addon.mod_quiz.returnattempt": "Voltar à tentativa", - "addon.mod_quiz.review": "Rever", - "addon.mod_quiz.reviewofattempt": "Revisão da tentativa {{$a}}", - "addon.mod_quiz.reviewofpreview": "Revisão da pré-visualização", - "addon.mod_quiz.showall": "Mostrar todas as perguntas numa página", - "addon.mod_quiz.showeachpage": "Mostrar uma página de cada vez", - "addon.mod_quiz.startattempt": "Iniciar tentativa", - "addon.mod_quiz.startedon": "Iniciada", - "addon.mod_quiz.stateabandoned": "Nunca submetidas", - "addon.mod_quiz.statefinished": "Terminada", - "addon.mod_quiz.statefinisheddetails": "Submetida {{$a}}", - "addon.mod_quiz.stateinprogress": "Em progresso", - "addon.mod_quiz.stateoverdue": "Tempo limite ultrapassado", - "addon.mod_quiz.stateoverduedetails": "Deve ser submetido até {{$a}}", - "addon.mod_quiz.status": "Estado", - "addon.mod_quiz.submitallandfinish": "Submeter tudo e terminar", - "addon.mod_quiz.summaryofattempt": "Síntese da tentativa", - "addon.mod_quiz.summaryofattempts": "Síntese das tentativas anteriores", - "addon.mod_quiz.timeleft": "Tempo restante", - "addon.mod_quiz.timetaken": "Tempo gasto", - "addon.mod_quiz.warningattemptfinished": "Esta tentativa offline foi descartada por já estar concluída no site ou não ter sido encontrada.", - "addon.mod_quiz.warningdatadiscarded": "Algumas respostas offline foram descartadas porque as perguntas foram modificadas no site.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Tentativa inacabada porque algumas respostas offline foram descartadas. Por favor reveja as suas respostas e reenvie a tentativa.", - "addon.mod_quiz.warningquestionsnotsupported": "Este teste contém tipos de perguntas não suportados pela aplicação:", - "addon.mod_quiz.yourfinalgradeis": "A sua nota final do teste é {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Erro ao carregar o conteúdo.", - "addon.mod_resource.modifieddate": "Modificado {{$a}}", - "addon.mod_resource.modulenameplural": "Ficheiros", - "addon.mod_resource.openthefile": "Abrir o ficheiro", - "addon.mod_resource.uploadeddate": "Carregado {{$a}}", - "addon.mod_scorm.asset": "Recurso", - "addon.mod_scorm.assetlaunched": "Recursos - Vistos", - "addon.mod_scorm.attempts": "Tentativas", - "addon.mod_scorm.averageattempt": "Média das tentativas", - "addon.mod_scorm.browse": "Pré-visualização", - "addon.mod_scorm.browsed": "Consultado", - "addon.mod_scorm.browsemode": "Modo de pré-visualização", - "addon.mod_scorm.cannotcalculategrade": "Não foi possível calcular a nota.", - "addon.mod_scorm.completed": "Concluído", - "addon.mod_scorm.contents": "Conteúdos", - "addon.mod_scorm.dataattemptshown": "Estes dados pertencem à tentativa número {{number}}.", - "addon.mod_scorm.enter": "Entrar", - "addon.mod_scorm.errorcreateofflineattempt": "Ocorreu um erro ao criar uma nova tentativa offline. Por favor, tente novamente.", - "addon.mod_scorm.errordownloadscorm": "Erro ao descarregar o SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Erro ao obter os dados do SCORM.", - "addon.mod_scorm.errorinvalidversion": "A aplicação apenas suporta a versão SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Não é permitido descarregar pacotes SCORM. Por favor, contacte o administrador do site.", - "addon.mod_scorm.errornovalidsco": "Este pacote SCORM não possui um SCO visível para carregar.", - "addon.mod_scorm.errorpackagefile": "A aplicação apenas suporta pacotes ZIP.", - "addon.mod_scorm.errorsyncscorm": "Ocorreu um erro durante o processo de sincronização. Por favor, tente novamente.", - "addon.mod_scorm.exceededmaxattempts": "Atingiu o número máximo de tentativas permitidas", - "addon.mod_scorm.failed": "Reprovado", - "addon.mod_scorm.firstattempt": "Primeira tentativa", - "addon.mod_scorm.gradeaverage": "Nota média", - "addon.mod_scorm.gradeforattempt": "Nota da tentativa", - "addon.mod_scorm.gradehighest": "Nota mais alta", - "addon.mod_scorm.grademethod": "Método de avaliação", - "addon.mod_scorm.gradereported": "Nota reportada", - "addon.mod_scorm.gradescoes": "Objetos de aprendizagem", - "addon.mod_scorm.gradesum": "Nota da soma", - "addon.mod_scorm.highestattempt": "Tentativa com melhor nota", - "addon.mod_scorm.incomplete": "Incompleto", - "addon.mod_scorm.lastattempt": "Última tentativa concluída", - "addon.mod_scorm.modulenameplural": "Pacotes SCORM", - "addon.mod_scorm.newattempt": "Iniciar uma nova tentativa", - "addon.mod_scorm.noattemptsallowed": "Número de tentativas permitidas", - "addon.mod_scorm.noattemptsmade": "Número de tentativas realizadas", - "addon.mod_scorm.notattempted": "Sem tentativas", - "addon.mod_scorm.offlineattemptnote": "Esta tentativa possui dados que ainda não foram sincronizados.", - "addon.mod_scorm.offlineattemptovermax": "Esta tentativa não pode ser enviada porque já excedeu o número máximo de tentativas.", - "addon.mod_scorm.organizations": "Organizações", - "addon.mod_scorm.passed": "Aprovado", - "addon.mod_scorm.reviewmode": "Modo de revisão", - "addon.mod_scorm.score": "Nota", - "addon.mod_scorm.scormstatusnotdownloaded": "Este pacote SCORM não foi descarregado. Será descarregado assim que o abrir.", - "addon.mod_scorm.scormstatusoutdated": "Este pacote SCORM foi modificado desde a última vez que o descarregou. A versão mais recente do mesmo será descarregada automaticamente assim que o abrir.", - "addon.mod_scorm.suspended": "Suspenso", - "addon.mod_scorm.toc": "Índice", - "addon.mod_scorm.warningofflinedatadeleted": "Alguns dados offline da tentativa {{number}} foram excluídos porque não podem ser considerados numa nova tentativa.", - "addon.mod_scorm.warningsynconlineincomplete": "Algumas tentativas ainda não foram sincronizadas com o site porque a última tentativa ainda não se está finalizada. Por favor, termine a tentativa online primeiro.", - "addon.mod_survey.cannotsubmitsurvey": "Ocorreu um problema durante a submissão do seu inquérito. Por favor, tente novamente.", - "addon.mod_survey.errorgetsurvey": "Erro ao obter os dados do inquérito.", - "addon.mod_survey.ifoundthat": "Achei isso", - "addon.mod_survey.ipreferthat": "Prefiro isso", - "addon.mod_survey.modulenameplural": "Inquéritos predefinidos", - "addon.mod_survey.responses": "Respostas", - "addon.mod_survey.results": "Resultados", - "addon.mod_survey.surveycompletednograph": "Completou o presente inquérito.", - "addon.mod_url.accessurl": "Aceder ao URL", - "addon.mod_url.modulenameplural": "URLs", - "addon.mod_url.pointingtourl": "O URL para onde aponta este recurso.", - "addon.mod_wiki.cannoteditpage": "Não pode editar esta página.", - "addon.mod_wiki.createpage": "Criar página", - "addon.mod_wiki.editingpage": "A editar a página '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Ocorreu um erro ao carregar a página.", - "addon.mod_wiki.errornowikiavailable": "Este Wiki ainda não possui conteúdo.", - "addon.mod_wiki.gowikihome": "Ir para página principal do Wiki", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Nova página", - "addon.mod_wiki.newpagetitle": "Novo título da página", - "addon.mod_wiki.nocontent": "Não há nenhum conteúdo nesta página", - "addon.mod_wiki.notingroup": "Não está em nenhum grupo", - "addon.mod_wiki.pageexists": "Esta página já existe.", - "addon.mod_wiki.pagename": "Nome da página", - "addon.mod_wiki.subwiki": "Sub-Wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Páginas do wiki", - "addon.mod_wiki.titleshouldnotbeempty": "O título não deve estar vazio", - "addon.mod_wiki.viewpage": "Visualizar página", - "addon.mod_wiki.wikipage": "Página Wiki", - "addon.mod_wiki.wrongversionlock": "Outro participante editou esta página enquanto estava simultaneamente a editá-la e o seu conteúdo está desatualizado.", - "addon.mod_workshop.alreadygraded": "Já avaliado", - "addon.mod_workshop.areainstructauthors": "Instruções para o envio dos trabalhos", - "addon.mod_workshop.areainstructreviewers": "Instruções para avaliação", - "addon.mod_workshop.assess": "Avaliar", - "addon.mod_workshop.assessedsubmission": "Trabalho avaliado", - "addon.mod_workshop.assessmentform": "Grelha de avaliação", - "addon.mod_workshop.assessmentsettings": "Configuração da avaliação", - "addon.mod_workshop.assessmentstrategynotsupported": "Estratégia de avaliação {{$a}} não suportada", - "addon.mod_workshop.assessmentweight": "Peso da avaliação", - "addon.mod_workshop.assignedassessments": "Trabalhos que lhe foram atribuídos para avaliação", - "addon.mod_workshop.assignedassessmentsnone": "Não tem trabalhos atribuídos para avaliar", - "addon.mod_workshop.conclusion": "Conclusão", - "addon.mod_workshop.createsubmission": "Adicionar submissão", - "addon.mod_workshop.deletesubmission": "Apagar submissão", - "addon.mod_workshop.editsubmission": "Editar submissão", - "addon.mod_workshop.feedbackauthor": "Comentário para o autor do trabalho", - "addon.mod_workshop.feedbackby": "Feedback por {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Comentário para o avaliador", - "addon.mod_workshop.givengrades": "Notas atribuídas", - "addon.mod_workshop.gradecalculated": "Nota do trabalho", - "addon.mod_workshop.gradeinfo": "Nota: {{$a.received}} em {{$a.max}}", - "addon.mod_workshop.gradeover": "Substituir nota do trabalho", - "addon.mod_workshop.gradesreport": "Relatório de notas do workshop", - "addon.mod_workshop.gradinggrade": "Nota máxima da avaliação", - "addon.mod_workshop.gradinggradecalculated": "Nota da avaliação", - "addon.mod_workshop.gradinggradeof": "Nota da avaliação (em {{$a}})", - "addon.mod_workshop.gradinggradeover": "Substituir nota da avaliação", - "addon.mod_workshop.modulenameplural": "Workshops", - "addon.mod_workshop.nogradeyet": "Ainda não tem nota", - "addon.mod_workshop.notassessed": "Ainda não foi avaliado", - "addon.mod_workshop.notoverridden": "Não substituir", - "addon.mod_workshop.noyoursubmission": "Ainda não submeteu o seu trabalho", - "addon.mod_workshop.overallfeedback": "Feedback global", - "addon.mod_workshop.publishedsubmissions": "Trabalhos publicados", - "addon.mod_workshop.publishsubmission": "Publicar trabalho", - "addon.mod_workshop.publishsubmission_help": "Os trabalhos publicados ficam disponíveis para todos após o fecho do workshop", - "addon.mod_workshop.reassess": "Reavaliar", - "addon.mod_workshop.receivedgrades": "Notas obtidas", - "addon.mod_workshop.submissionattachment": "Anexo", - "addon.mod_workshop.submissioncontent": "Conteúdo submetido", - "addon.mod_workshop.submissiondeleteconfirm": "Tem a certeza de que pretende apagar a seguinte submissão?", - "addon.mod_workshop.submissiongrade": "Nota máxima do trabalho", - "addon.mod_workshop.submissiongradeof": "Nota do trabalho (em {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Tem de inserir texto ou adicionar um ficheiro", - "addon.mod_workshop.submissionrequiredtitle": "É necessário indicar um título.", - "addon.mod_workshop.submissionsreport": "Relatório de submissões do workshop", - "addon.mod_workshop.submissiontitle": "Titulo", - "addon.mod_workshop.switchphase10": "Mudar para a fase de configuração", - "addon.mod_workshop.switchphase20": "Mudar para a fase de submissão", - "addon.mod_workshop.switchphase30": "Mudar para a fase de avaliação", - "addon.mod_workshop.switchphase40": "Mudar para a fase de apreciação", - "addon.mod_workshop.switchphase50": "Fechar Workshop", - "addon.mod_workshop.userplan": "Planificação do workshop", - "addon.mod_workshop.userplancurrentphase": "Fase atual", - "addon.mod_workshop.warningassessmentmodified": "A submissão foi alterada no site.", - "addon.mod_workshop.warningsubmissionmodified": "O trabalho foi modificado no site.", - "addon.mod_workshop.weightinfo": "Peso: {{$a}}", - "addon.mod_workshop.yourassessment": "Avaliação do próprio", - "addon.mod_workshop.yourassessmentfor": "Avaliação para {{$a}}", - "addon.mod_workshop.yourgrades": "As suas notas", - "addon.mod_workshop.yoursubmission": "O seu trabalho", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Comentário para {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Nota para {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Critério {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Tem de selecionar uma nota para este critério", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Comentário para {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspeto {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Comentário para {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Nota para {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Proposição {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Critério {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Tem de selecionar um dos itens", - "addon.notes.addnewnote": "Adicionar anotação", - "addon.notes.coursenotes": "Anotações da disciplina", - "addon.notes.deleteconfirm": "Apagar esta anotação?", - "addon.notes.eventnotecreated": "Nota criada", - "addon.notes.eventnotedeleted": "Nota apagada", - "addon.notes.nonotes": "Ainda não há anotações deste tipo", - "addon.notes.note": "Anotação", - "addon.notes.notes": "Anotações", - "addon.notes.personalnotes": "Anotações pessoais", - "addon.notes.publishstate": "Contexto", - "addon.notes.sitenotes": "Anotações do site", - "addon.notes.userwithid": "Utilizador com o ID {{id}}", - "addon.notes.warningnotenotsent": "Não foi possível adicionar anotações à disciplina {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Erro ao obter notificações.", - "addon.notifications.markallread": "Marcar todas como lidas", - "addon.notifications.notificationpreferences": "Preferências das notificações", - "addon.notifications.notifications": "Notificações", - "addon.notifications.playsound": "Reproduzir som", - "addon.notifications.therearentnotificationsyet": "Não existem notificações.", - "addon.storagemanager.deletecourse": "Carregar todos os dados da disciplina", - "addon.storagemanager.deletecourses": "Descarregar todos os dados das disciplinas", - "addon.storagemanager.deletedatafrom": "Carregar dados de {{name}}", - "addon.storagemanager.info": "Os ficheiros armazenados no seu dispositivo tornam a aplicação mais rápida e permitem que possa ser usada offline. Pode carregar ficheiros em segurança se precisar de libertar espaço de armazenamento.", - "addon.storagemanager.managestorage": "Gerir armazenamento", - "addon.storagemanager.storageused": "Armazenamento usado por ficheiros:", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Emirados Árabes Unidos", - "assets.countries.AF": "Afeganistão", - "assets.countries.AG": "Antígua e Barbuda", - "assets.countries.AI": "Anguila", - "assets.countries.AL": "Albânia", - "assets.countries.AM": "Arménia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antártida", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa Americana", - "assets.countries.AT": "Áustria", - "assets.countries.AU": "Austrália", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Ilhas Aland", - "assets.countries.AZ": "Azerbaijão", - "assets.countries.BA": "Bósnia e Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Bélgica", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgária", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benim", - "assets.countries.BL": "São Bartolomeu", - "assets.countries.BM": "Bermudas", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Bolívia (Estado Plurinacional da)", - "assets.countries.BQ": "Bonaire, Santo Eustáquio e Saba", - "assets.countries.BR": "Brasil", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Butão", - "assets.countries.BV": "Ilha Bouvet", - "assets.countries.BW": "Botsuana", - "assets.countries.BY": "Bielorrússia", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canadá", - "assets.countries.CC": "Ilhas Cocos", - "assets.countries.CD": "Congo (República Democrática do)", - "assets.countries.CF": "República Centro-Africana", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Suíça", - "assets.countries.CI": "Costa do Marfim", - "assets.countries.CK": "Ilhas Cook", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Camarões", - "assets.countries.CN": "China", - "assets.countries.CO": "Colômbia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Cabo Verde", - "assets.countries.CW": "Curaçao, Ilha de (costa venezuelana)", - "assets.countries.CX": "Ilhas Natal", - "assets.countries.CY": "Chipre", - "assets.countries.CZ": "República Checa", - "assets.countries.DE": "Alemanha", - "assets.countries.DJ": "Jibuti", - "assets.countries.DK": "Dinamarca", - "assets.countries.DM": "Domínica", - "assets.countries.DO": "República Dominicana", - "assets.countries.DZ": "Argélia", - "assets.countries.EC": "Equador", - "assets.countries.EE": "Estónia", - "assets.countries.EG": "Egito", - "assets.countries.EH": "Sahara Ocidental", - "assets.countries.ER": "Eritreia", - "assets.countries.ES": "Espanha", - "assets.countries.ET": "Etiópia", - "assets.countries.FI": "Finlândia", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Malvinas", - "assets.countries.FM": "Micronésia (Estados Federados da)", - "assets.countries.FO": "Ilhas Faroé", - "assets.countries.FR": "França", - "assets.countries.GA": "Gabão", - "assets.countries.GB": "Reino Unido", - "assets.countries.GD": "Granada", - "assets.countries.GE": "Geórgia", - "assets.countries.GF": "Guiana Francesa", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Gana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Gronelândia", - "assets.countries.GM": "Gâmbia", - "assets.countries.GN": "Guiné", - "assets.countries.GP": "Guadalupe", - "assets.countries.GQ": "Guiné Equatorial", - "assets.countries.GR": "Grécia", - "assets.countries.GS": "Geórgia do Sul e as Ilhas Sandwich do Sul", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guiné-Bissau", - "assets.countries.GY": "Guiana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Ilhas Heard e McDonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Croácia", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Hungria", - "assets.countries.ID": "Indonésia", - "assets.countries.IE": "Irlanda", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Ilha de Man", - "assets.countries.IN": "Índia", - "assets.countries.IO": "Território Britânico no Oceano Índico", - "assets.countries.IQ": "Iraque", - "assets.countries.IR": "Irão (República Islâmica do)", - "assets.countries.IS": "Islândia", - "assets.countries.IT": "Itália", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordânia", - "assets.countries.JP": "Japão", - "assets.countries.KE": "Quénia", - "assets.countries.KG": "Quirguistão", - "assets.countries.KH": "Camboja", - "assets.countries.KI": "Quiribati", - "assets.countries.KM": "Comores", - "assets.countries.KN": "São Cristóvão e Neves", - "assets.countries.KP": "Coreia (República Popular Democrática da)", - "assets.countries.KR": "Coreia (República da)", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Ilhas Caimão", - "assets.countries.KZ": "Cazaquistão", - "assets.countries.LA": "República Popular Democrática do Laos", - "assets.countries.LB": "Líbano", - "assets.countries.LC": "Santa Lúcia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Libéria", - "assets.countries.LS": "Lesoto", - "assets.countries.LT": "Lituânia", - "assets.countries.LU": "Luxemburgo", - "assets.countries.LV": "Letónia", - "assets.countries.LY": "Líbia", - "assets.countries.MA": "Marrocos", - "assets.countries.MC": "Mónaco", - "assets.countries.MD": "Moldávia (República da)", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin (parte francesa)", - "assets.countries.MG": "Madagáscar", - "assets.countries.MH": "Ilhas Marshal", - "assets.countries.MK": "Macedónia do Norte", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Birmânia", - "assets.countries.MN": "Mongólia", - "assets.countries.MO": "Macau", - "assets.countries.MP": "Marianas do Norte", - "assets.countries.MQ": "Martinica", - "assets.countries.MR": "Mauritânia", - "assets.countries.MS": "Monserrate", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Ilhas Maurícias", - "assets.countries.MV": "Maldivas", - "assets.countries.MW": "Malavi", - "assets.countries.MX": "México", - "assets.countries.MY": "Malásia", - "assets.countries.MZ": "Moçambique", - "assets.countries.NA": "Namíbia", - "assets.countries.NC": "Nova Caledónia", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Ilha de Norfolk", - "assets.countries.NG": "Nigéria", - "assets.countries.NI": "Nicarágua", - "assets.countries.NL": "Holanda", - "assets.countries.NO": "Noruega", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nova Zelândia", - "assets.countries.OM": "Omã", - "assets.countries.PA": "Panamá", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Polinésia Francesa", - "assets.countries.PG": "Papua - Nova Guiné", - "assets.countries.PH": "Filipinas", - "assets.countries.PK": "Paquistão", - "assets.countries.PL": "Polónia", - "assets.countries.PM": "São Pedro e Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Porto Rico", - "assets.countries.PS": "Palestina, Estado da", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguai", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Reunião", - "assets.countries.RO": "Roménia", - "assets.countries.RS": "Sérvia", - "assets.countries.RU": "Federação Russa", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Arábia Saudita", - "assets.countries.SB": "Ilhas Salomão", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudão", - "assets.countries.SE": "Suécia", - "assets.countries.SG": "Singapura", - "assets.countries.SH": "Santa Helena, Ascensão e Tristão da Cunha", - "assets.countries.SI": "Eslovénia", - "assets.countries.SJ": "Svalbard e Jan Mayen", - "assets.countries.SK": "Eslováquia", - "assets.countries.SL": "Serra Leoa", - "assets.countries.SM": "São Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somália", - "assets.countries.SR": "Suriname", - "assets.countries.SS": "Sudão do Sul", - "assets.countries.ST": "São Tomé e Príncipe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (parte holandesa)", - "assets.countries.SY": "Síria", - "assets.countries.SZ": "Essuatíni", - "assets.countries.TC": "Ilhas Turcas e Caicos", - "assets.countries.TD": "Chade", - "assets.countries.TF": "Territórios Franceses do Sul", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tailândia", - "assets.countries.TJ": "Tajiquistão", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turquemenistão", - "assets.countries.TN": "Tunísia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turquia", - "assets.countries.TT": "Trindade e Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Formosa", - "assets.countries.TZ": "Tanzânia, República Unida da", - "assets.countries.UA": "Ucrânia", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Estados Unidos das Pequenas Ilhas Periféricas", - "assets.countries.US": "Estados Unidos da América", - "assets.countries.UY": "Uruguai", - "assets.countries.UZ": "Usbequistão", - "assets.countries.VA": "Vaticano", - "assets.countries.VC": "São Vicente e Granadinas", - "assets.countries.VE": "Venezuela (República Bolivariana da)", - "assets.countries.VG": "Ilhas Virgens (Britânicas)", - "assets.countries.VI": "Ilhas Virgens (Americanas)", - "assets.countries.VN": "Vietname", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis e Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Iémen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "África do Sul", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabué", - "assets.mimetypes.application/epub_zip": "Ebook EPUB", - "assets.mimetypes.application/msword": "Documento Word", - "assets.mimetypes.application/pdf": "Documento PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Cópia de segurança do Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Folha de cálculo EXCEL", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Livro Excel 2007 com permissão para macros", - "assets.mimetypes.application/vnd.ms-powerpoint": "Apresentação PowerPoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Folha de Cálculo OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Modelo de Folha de Cálculo OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Documento de Texto OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Modelo de Documento de Texto OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Modelo de Página Web OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Apresentação PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Slideshow PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Folha de cálculo Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Modelo Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Documento Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Apresentação iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Folha de cálculo iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Documento iWork Pages", - "assets.mimetypes.application/x-javascript": "Código fonte JavaScript", - "assets.mimetypes.application/x-mspublisher": "Documento do Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animação Flash", - "assets.mimetypes.application/xhtml_xml": "Documento XHTML", - "assets.mimetypes.archive": "Arquivo ({{$a.EXT}})", - "assets.mimetypes.audio": "Ficheiro Áudio ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Ficheiro", - "assets.mimetypes.group:archive": "Ficheiros de Arquivo", - "assets.mimetypes.group:audio": "Ficheiros Áudio", - "assets.mimetypes.group:document": "Ficheiros de Documento", - "assets.mimetypes.group:html_audio": "Ficheiros de Áudio suportado de modo nativo pelos navegadores", - "assets.mimetypes.group:html_track": "Ficheiros de Faixa HTML", - "assets.mimetypes.group:html_video": "Ficheiros de Vídeo suportado de modo nativo pelos navegadores", - "assets.mimetypes.group:image": "Ficheiros de Imagem", - "assets.mimetypes.group:presentation": "Ficheiros de Apresentação", - "assets.mimetypes.group:sourcecode": "Código fonte", - "assets.mimetypes.group:spreadsheet": "Ficheiros de Folha de Cálculo", - "assets.mimetypes.group:video": "Ficheiros de Vídeo", - "assets.mimetypes.group:web_audio": "Ficheiros de Áudio usados na web", - "assets.mimetypes.group:web_file": "Ficheiros Web", - "assets.mimetypes.group:web_image": "Ficheiros de imagem usados na web", - "assets.mimetypes.group:web_video": "Ficheiros de vídeo usados na web", - "assets.mimetypes.image": "Imagem ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Ícone Windows", - "assets.mimetypes.text/css": "Ficheiros CSS", - "assets.mimetypes.text/csv": "Valores separados por vírgulas", - "assets.mimetypes.text/html": "Documento HTML", - "assets.mimetypes.text/plain": "Ficheiro de texto", - "assets.mimetypes.text/rtf": "Documento RTF", - "assets.mimetypes.text/vtt": "Faixa de texto de vídeo da Web", - "assets.mimetypes.video": "Ficheiro de vídeo ({{$a.EXT}})", - "core.accounts": "Contas", - "core.add": "Adicionar", - "core.agelocationverification": "Verificação de idade e localização", - "core.ago": "{{$a}} atrás", - "core.all": "Todos", - "core.allgroups": "Todos os grupos", - "core.allparticipants": "Todos", - "core.answer": "Resposta", - "core.answered": "Respondido", - "core.areyousure": "Tem a certeza?", - "core.back": "Voltar", - "core.block.blocks": "Blocos", - "core.browser": "Navegador", - "core.cancel": "Cancelar", - "core.cannotconnect": "Não é possível estabelecer a ligação", - "core.cannotconnecttrouble": "Estamos com problemas para estabelecer a ligação ao seu site.", - "core.cannotconnectverify": "Por favor, verifique se o endereço está correto.", - "core.cannotdownloadfiles": "A descarga de ficheiros está desativada. Por favor, contacte o administrador do site.", - "core.captureaudio": "Gravar áudio", - "core.capturedimage": "Fotografia tirada.", - "core.captureimage": "Tirar fotografia", - "core.capturevideo": "Gravar vídeo", - "core.category": "Categoria", - "core.choose": "Selecionar", - "core.choosedots": "Escolha…", - "core.clearsearch": "Limpar pesquisa", - "core.clicktohideshow": "Clique para expandir ou contrair", - "core.clicktoseefull": "Clique para ver todos os conteúdos.", - "core.close": "Fechar", - "core.comments": "Comentários", - "core.comments.addcomment": "Adicionar comentário…", - "core.comments.comments": "Comentários", - "core.comments.commentscount": "Comentários ({{$a}})", - "core.comments.commentsnotworking": "Comentários não podem ser recuperados", - "core.comments.deletecommentbyon": "Eliminar o comentário publicado por {{$a.user}} em {{$a.time}}", - "core.comments.eventcommentcreated": "Comentário criado", - "core.comments.eventcommentdeleted": "Comentário apagado", - "core.comments.nocomments": "Sem comentários", - "core.comments.savecomment": "Guardar comentário", - "core.comments.warningcommentsnotsent": "Não foi possível sincronizar os comentários. {{error}}", - "core.commentscount": "Comentários ({{$a}})", - "core.completion-alt-auto-fail": "Concluída: {{$a}} (não atingiu nota de aprovação)", - "core.completion-alt-auto-n": "Não concluída: {{$a}}", - "core.completion-alt-auto-n-override": "Não concluída: {{$a.modname}} (marcada por {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Concluída: {{$a}} (atingiu nota de aprovação)", - "core.completion-alt-auto-y": "Concluída: {{$a}}", - "core.completion-alt-auto-y-override": "Concluída: {{$a.modname}} (marcada por {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Não concluída: '{{$a}}'. Clique para assinalar como concluída", - "core.completion-alt-manual-n-override": "Não completo: {{$a- modname}} (definido por {{$a.overrideuser}}). Selecione para marcar como completo.", - "core.completion-alt-manual-y": "Concluída: '{{$a}}'. Clique para assinalar como não concluída", - "core.completion-alt-manual-y-override": "Completo: {{$a- modname}} (definido por {{$a.overrideuser}}). Selecione para marcar como não completo.", - "core.confirmcanceledit": "Tem a certeza de que pretende sair desta página? Todas as alterações serão perdidas.", - "core.confirmdeletefile": "Tem a certeza de que pretende apagar este ficheiro?", - "core.confirmgotabroot": "Tem a certeza de que pretende voltar para {{name}}?", - "core.confirmgotabrootdefault": "Tem a certeza de que pretende ir para a página inicial do separador atual?", - "core.confirmleaveunknownchanges": "Tem a certeza de que pretende sair desta página? Quaisquer alterações não guardadas serão perdidas.", - "core.confirmloss": "Tem a certeza absoluta? Todas as alterações serão perdidas.", - "core.confirmopeninbrowser": "Pretende abrir a ligação no navegador de internet?", - "core.considereddigitalminor": "Ainda não tem idade para criar uma conta neste site", - "core.content": "Conteúdo", - "core.contenteditingsynced": "O conteúdo que está a editar foi sincronizado.", - "core.contentlinks.chooseaccount": "Escolha a conta", - "core.contentlinks.chooseaccounttoopenlink": "Escolha uma conta para abrir a hiperligação.", - "core.contentlinks.confirmurlothersite": "Esta hiperligação pertence a outro site. Pretende abri-la?", - "core.contentlinks.errornoactions": "Não foi possível encontrar uma ação para executar com esta hiperligação.", - "core.contentlinks.errornosites": "Não foi possível encontrar um site que suporte esta hiperligação.", - "core.contentlinks.errorredirectothersite": "O URL de redirecionamento não pode apontar para um site diferente.", - "core.continue": "Continuar", - "core.copiedtoclipboard": "Texto copiado para a área de transferência", - "core.copytoclipboard": "Copiar para área de transferência", - "core.course": "Disciplina", - "core.course.activitydisabled": "A sua organização desativou esta atividade na aplicação para dispositivos móveis.", - "core.course.activitynotyetviewableremoteaddon": "A sua organização instalou um módulo que ainda não é suportado.", - "core.course.activitynotyetviewablesiteupgradeneeded": "A instalação do Moodle da sua organização precisa ser atualizada.", - "core.course.allsections": "Todas as secções", - "core.course.askadmintosupport": "Contacte o administrador do site e informe-lhes que pretende usar esta atividade com a aplicação móvel Moodle.", - "core.course.availablespace": "Atualmente, dispõe aproximadamente {{available}} de espaço livre.", - "core.course.cannotdeletewhiledownloading": "Os ficheiros não podem ser apagados enquanto a atividade está a ser descarregada. Por favor, aguarde que termine o descarregamento.", - "core.course.confirmdeletemodulefiles": "Tem a certeza de que pretende apagar estes ficheiros?", - "core.course.confirmdownload": "Está prestes a descarregar {{size}}.{{availableSpace}} Tem a certeza de que pretende continuar?", - "core.course.confirmdownloadunknownsize": "Não foi possível calcular o tamanho da descarga.{{availableSpace}} Tem a certeza de que pretende continuar?", - "core.course.confirmdownloadzerosize": "Está prestes a iniciar a descarga. {{AvailableSpace}} Tem a certeza de que pretende continuar?", - "core.course.confirmlimiteddownload": "Não está ligado a qualquer rede Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Está prestes a descarregar pelo menos {{size}}. {{availableSpace}} Tem a certeza de que pretende continuar?", - "core.course.contents": "Conteúdos", - "core.course.couldnotloadsectioncontent": "Não foi possível carregar o conteúdo da secção. Por favor, tente novamente mais tarde.", - "core.course.couldnotloadsections": "Não foi possível carregar as secções. Por favor, tente novamente mais tarde.", - "core.course.coursesummary": "Descrição da disciplina", - "core.course.downloadcourse": "Descarregar disciplina", - "core.course.errordownloadingcourse": "Erro ao descarregar a disciplina.", - "core.course.errordownloadingsection": "Erro ao descarregar a secção.", - "core.course.errorgetmodule": "Erro ao obter os dados da atividade.", - "core.course.hiddenfromstudents": "Oculto para os alunos", - "core.course.hiddenoncoursepage": "Disponível mas não mostrar na página da disciplina", - "core.course.insufficientavailablequota": "O seu dispositivo não conseguiu libertar espaço para guardar o que pretende descarregar. Pode estar a reservar espaço para atualizações de aplicações e do sistema. Por favor, liberte espaço livre para armazenamento.", - "core.course.insufficientavailablespace": "Está a tentar descarregar {{size}}. Se prosseguir, o dispositivo deixará de ter espaço suficiente para funcionar normalmente. Por favor, liberte espaço livre para armazenamento.", - "core.course.manualcompletionnotsynced": "Conclusão manual não sincronizada.", - "core.course.nocontentavailable": "Neste momento não existe conteúdo disponível.", - "core.course.overriddennotice": "A sua nota final nesta atividade foi alterada manualmente na pauta.", - "core.course.refreshcourse": "Atualizar disciplina", - "core.course.sections": "Secções", - "core.course.useactivityonbrowser": "No entanto, pode usá-lo no navegador do seu dispositivo.", - "core.course.warningmanualcompletionmodified": "A conclusão manual de uma atividade foi modificada no site.", - "core.course.warningofflinemanualcompletiondeleted": "Uma conclusão manual offline da disciplina '{{name}}' foi apagada. {{error}}", - "core.coursedetails": "Informação sobre disciplinas", - "core.coursenogroups": "Não é membro de qualquer grupo desta disciplina.", - "core.courses.addtofavourites": "Marcar disciplina com estrela", - "core.courses.allowguests": "Esta disciplina permite o acesso a visitantes", - "core.courses.availablecourses": "Disciplinas disponíveis", - "core.courses.cannotretrievemorecategories": "Categorias abaixo do nível {{$a}} não podem ser recuperadas.", - "core.courses.categories": "Categorias de disciplinas", - "core.courses.confirmselfenrol": "Tem a certeza de que deseja inscrever-se nesta disciplina?", - "core.courses.courses": "Disciplinas", - "core.courses.downloadcourses": "Descarregar disciplinas", - "core.courses.enrolme": "Inscreva-me", - "core.courses.errorloadcategories": "Ocorreu um erro ao carregar as categorias.", - "core.courses.errorloadcourses": "Ocorreu um erro ao carregar as disciplinas.", - "core.courses.errorloadplugins": "Não foram carregados corretamente os módulos exigidos por esta disciplina. Por favor, recarregue a aplicação para tentar novamente.", - "core.courses.errorsearching": "Ocorreu um erro durante a pesquisa.", - "core.courses.errorselfenrol": "Ocorreu um erro na autoinscrição.", - "core.courses.filtermycourses": "Filtrar as minhas disciplinas", - "core.courses.frontpage": "Página principal", - "core.courses.hidecourse": "Remover da visualização", - "core.courses.ignore": "Ignorar", - "core.courses.mycourses": "As minhas disciplinas", - "core.courses.mymoodle": "Painel do utilizador", - "core.courses.nocourses": "Sem informação das suas disciplinas para mostrar.", - "core.courses.nocoursesyet": "Nenhuma disciplina nesta categoria", - "core.courses.nosearchresults": "Sem resultados", - "core.courses.notenroled": "Não está inscrito como aluno nesta disciplina", - "core.courses.notenrollable": "Não pode inscrever-se nesta disciplina.", - "core.courses.password": "Senha de inscrição", - "core.courses.paymentrequired": "Esta disciplina exige uma taxa de inscrição.", - "core.courses.paypalaccepted": "Tipos de pagamentos aceites", - "core.courses.reload": "Recarregar", - "core.courses.removefromfavourites": "Desmarcar a estrela desta disciplina", - "core.courses.search": "Procurar", - "core.courses.searchcourses": "Procurar disciplinas", - "core.courses.searchcoursesadvice": "Pode usar o botão Procurar disciplinas para encontrar disciplinas com acesso permitido a visitantes ou inscrever-se em disciplinas que o permitam.", - "core.courses.selfenrolment": "Autoinscrição", - "core.courses.sendpaymentbutton": "Realizar pagamento através de \"Paypal\"", - "core.courses.show": "Repor na visualização", - "core.courses.totalcoursesearchresults": "Total de disciplinas: {{$a}}", - "core.currentdevice": "Dispositivo atual", - "core.datastoredoffline": "Dados armazenados no dispositivo por não ter sido possível enviar. Serão automaticamente enviados mais tarde.", - "core.date": "Data", - "core.day": "dia", - "core.days": "dias", - "core.decsep": ",", - "core.defaultvalue": "Valor predefinido ({{$a}})", - "core.delete": "Apagar", - "core.deletedoffline": "Apagada em modo offline", - "core.deleteduser": "Utilizador apagado", - "core.deleting": "A apagar", - "core.description": "Descrição", - "core.desktop": "PC de secretária", - "core.dfdaymonthyear": "DD-MM-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "hh[:]mm", - "core.digitalminor": "Criança", - "core.digitalminor_desc": "Peça aos seus pais/responsáveis para contactarem:", - "core.discard": "Descartar", - "core.dismiss": "Dispensar", - "core.displayoptions": "Opções de visualização", - "core.done": "Concluído", - "core.download": "Descarregar", - "core.downloaded": "Descarregada", - "core.downloading": "A descarregar", - "core.edit": "Editar", - "core.editor.autosavesucceeded": "Rascunho guardado", - "core.editor.bold": "Negrito", - "core.editor.clear": "Limpar formatação", - "core.editor.h3": "Cabeçalho (grande)", - "core.editor.h4": "Cabeçalho (médio)", - "core.editor.h5": "Cabeçalho (pequeno)", - "core.editor.hidetoolbar": "Ocultar barra de ferramentas", - "core.editor.italic": "Itálico", - "core.editor.orderedlist": "Lista ordenada", - "core.editor.p": "Parágrafo", - "core.editor.strike": "Rasurado", - "core.editor.textrecovered": "Uma versão de rascunho deste texto foi automaticamente restaurada.", - "core.editor.toggle": "Alternar editor", - "core.editor.underline": "Sublinhado", - "core.editor.unorderedlist": "Lista não ordenada", - "core.emptysplit": "Esta página aparecerá em branco se o painel esquerdo estiver vazio ou enquanto estiver a ser carregado.", - "core.error": "Erro", - "core.errorchangecompletion": "Ocorreu um erro ao alterar o estado da conclusão. Por favor, tente novamente.", - "core.errordeletefile": "Erro ao apagar o ficheiro. Por favor, tente novamente.", - "core.errordownloading": "Erro ao descarregar o ficheiro.", - "core.errordownloadingsomefiles": "Erro ao descarregar os ficheiros. Alguns ficheiros poderão estar em falta.", - "core.errorfileexistssamename": "Já existe um ficheiro com este nome.", - "core.errorinvalidform": "O formulário contém dados inválidos. Por favor, verifique se todos os campos obrigatórios estão preenchidos e que os dados são válidos.", - "core.errorinvalidresponse": "Foi recebida uma resposta inválida. Se o erro persistir, por favor, contacte o administrador do site.", - "core.errorloadingcontent": "Erro ao carregar o conteúdo.", - "core.errorofflinedisabled": "A navegação offline está desativada no site. Precisa de estar ligado à internet para usar a aplicação.", - "core.erroropenfilenoapp": "Erro ao abrir o ficheiro: não foi encontrada nenhuma aplicação compatível com este tipo de ficheiro.", - "core.erroropenfilenoextension": "Erro ao abrir o ficheiro: o ficheiro não possui uma extensão.", - "core.erroropenpopup": "Esta atividade está a tentar abrir uma janela pop-up. Esta funcionalidade não é suportada na aplicação.", - "core.errorrenamefile": "Erro ao mudar o nome do ficheiro. Por favor, tente novamente.", - "core.errorsomedatanotdownloaded": "Se descarregou esta atividade, tenha em atenção que alguns dados não são descarregados durante o processo de descarregamento por motivos de desempenho e uso de dados.", - "core.errorsync": "Ocorreu um erro durante a sincronização. Por favor, tente novamente.", - "core.errorsyncblocked": "Não é possível sincronizar agora este {{$a}} devido a outro processo já em andamento. Por favor, tente novamente mais tarde. Se o problema persistir, tente reiniciar a aplicação.", - "core.explanationdigitalminor": "Esta informação é necessária para determinar se a sua idade excede a idade mínima de consentimento. Esta é a idade em que um indivíduo pode dar o seu consentimento aos termos e condições e ao armazenamento e tratamento dos seus dados pessoais.", - "core.favourites": "Com estrela", - "core.filename": "Nome do ficheiro", - "core.filenameexist": "O nome do ficheiro já existe: {{$a}}", - "core.filenotfound": "Ficheiro não encontrado", - "core.fileuploader.addfiletext": "Adicionar ficheiro", - "core.fileuploader.audio": "Áudio", - "core.fileuploader.camera": "Câmara", - "core.fileuploader.confirmuploadfile": "Está prestes a carregar {{size}}. Tem a certeza que pretende continuar?", - "core.fileuploader.confirmuploadunknownsize": "Não foi possível calcular o tamanho do carregamento. Tem a certeza que pretende continuar?", - "core.fileuploader.errorcapturingaudio": "Erro ao capturar áudio.", - "core.fileuploader.errorcapturingimage": "Erro ao capturar imagem.", - "core.fileuploader.errorcapturingvideo": "Erro ao capturar vídeo.", - "core.fileuploader.errorgettingimagealbum": "Erro ao obter imagem do álbum", - "core.fileuploader.errormustbeonlinetoupload": "Tem de estar online para enviar ficheiros", - "core.fileuploader.errornoapp": "Não possui a aplicação móvel necessária para executar esta ação.", - "core.fileuploader.errorreadingfile": "Erro ao ler o ficheiro", - "core.fileuploader.errorwhileuploading": "Ocorreu um erro durante o carregamento do ficheiro.", - "core.fileuploader.file": "Ficheiro", - "core.fileuploader.filesofthesetypes": "Tipos de ficheiro permitidos:", - "core.fileuploader.fileuploaded": "Ficheiro carregado com sucesso", - "core.fileuploader.invalidfiletype": "O tipo de ficheiro \"{{$a}}\" não é permitido.", - "core.fileuploader.maxbytesfile": "O ficheiro {{$a.file}} é muito grande. O tamanho máximo que pode carregar é de {{$a.size}}.", - "core.fileuploader.more": "Mais", - "core.fileuploader.photoalbums": "Álbuns de fotos", - "core.fileuploader.readingfile": "A ler ficheiro", - "core.fileuploader.readingfileperc": "A ler ficheiro: {{$a}}%", - "core.fileuploader.selectafile": "Selecione um ficheiro", - "core.fileuploader.uploadafile": "Carregar um ficheiro", - "core.fileuploader.uploading": "A carregar", - "core.fileuploader.uploadingperc": "A carrregar: {{$a}}%", - "core.fileuploader.video": "Vídeo", - "core.filter": "Filtro", - "core.folder": "Pasta", - "core.forcepasswordchangenotice": "Tem de alterar a sua senha para poder continuar.", - "core.fulllistofcourses": "Todas as disciplinas", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Média", - "core.grades.badgrade": "A nota fornecida é inválida", - "core.grades.contributiontocoursetotal": "Contribuição para o total da disciplina", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Nota", - "core.grades.gradeitem": "Item de avaliação", - "core.grades.grades": "Pauta", - "core.grades.lettergrade": "Nota Alfabética", - "core.grades.nogradesreturned": "Não foram devolvidas notas", - "core.grades.nooutcome": "Nenhum resultado da aprendizagem", - "core.grades.percentage": "Percentagem", - "core.grades.range": "Intervalo", - "core.grades.rank": "Posição relativa", - "core.grades.weight": "Peso", - "core.group": "Grupo", - "core.groupsseparate": "Grupos separados", - "core.groupsvisible": "Grupos visíveis", - "core.h5p.additionallicenseinfo": "Informações adicionais sobre a licença", - "core.h5p.author": "Autor", - "core.h5p.authorcomments": "Comentários ao autor", - "core.h5p.authorcommentsdescription": "Comentários para o editor do conteúdo. (Este texto não será publicado como parte das informações de direitos de autor.)", - "core.h5p.authorname": "Nome do autor", - "core.h5p.authorrole": "Papel do autor", - "core.h5p.by": "por", - "core.h5p.cancellabel": "Cancelar", - "core.h5p.ccattribution": "Atribuição (CC BY)", - "core.h5p.ccattributionnc": "Atribuição-NãoComercial (CC BY-NC)", - "core.h5p.ccattributionncnd": "Atribuição-NãoComercial-SemDerivações (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Atribuição-NãoComercial-PartilhaIgual (CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Atribuição-SemDerivações (CC BY-ND)", - "core.h5p.ccattributionsa": "Atribuição-PartilhaIgual (CC BY-SA)", - "core.h5p.ccpdd": "Dedicação de Domínio Público (CC0)", - "core.h5p.changedby": "Alterado por", - "core.h5p.changedescription": "Descrição da alteração", - "core.h5p.changelog": "Relatório das alterações", - "core.h5p.changeplaceholder": "Foto recortada, texto modificado, etc.", - "core.h5p.close": "Fechar", - "core.h5p.confirmdialogbody": "Por favor, confirme que pretende prosseguir. Esta ação é irreversível.", - "core.h5p.confirmdialogheader": "Confirmar ação", - "core.h5p.confirmlabel": "Confirmar", - "core.h5p.connectionLost": "Ligação perdida. Os resultados serão armazenados localmente e enviados quando a ligação for restabelecida.", - "core.h5p.connectionReestablished": "Ligação restabelecida.", - "core.h5p.contentCopied": "O conteúdo foi copiado para a área de transferência", - "core.h5p.contentchanged": "Este conteúdo foi alterado desde a última vez que o usou.", - "core.h5p.contenttype": "Tipo de conteúdo", - "core.h5p.copyright": "Direitos de utilização", - "core.h5p.copyrightinfo": "Informações sobre direitos de autor", - "core.h5p.copyrightstring": "Direitos de autor", - "core.h5p.copyrighttitle": "Visualizar informações sobre os direitos de autor referentes a este conteúdo.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Data e hora", - "core.h5p.disablefullscreen": "Desativar o modo de ecrã inteiro", - "core.h5p.download": "Descarregar", - "core.h5p.downloadtitle": "Descarregar este conteúdo como ficheiro H5P.", - "core.h5p.editor": "Editor", - "core.h5p.embed": "Incorporar", - "core.h5p.embedtitle": "Visualizar o código de incorporação deste conteúdo.", - "core.h5p.errorgetemail": "Erro ao obter o e-mail do utilizador. Por favor, verifique a sua ligação e tente novamente.", - "core.h5p.fullscreen": "Ecrã inteiro", - "core.h5p.gpl": "Licença Pública Geral v3", - "core.h5p.h5ptitle": "Visite h5p.org para mais conteúdos.", - "core.h5p.hideadvanced": "Ocultar Avançado", - "core.h5p.license": "Licença", - "core.h5p.licenseCC010": "CC0 1.0 Universal (CC0 1.0) Dedicação ao Domínio Público", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Genérica", - "core.h5p.licenseCC20": "2.0 Genérica", - "core.h5p.licenseCC25": "2.5 Genérica", - "core.h5p.licenseCC30": "3.0 Não Adaptada", - "core.h5p.licenseCC40": "4.0 Internacional", - "core.h5p.licenseGPL": "Licença Pública Geral", - "core.h5p.licenseV1": "Versão 1", - "core.h5p.licenseV2": "Versão 2", - "core.h5p.licenseV3": "Versão 3", - "core.h5p.licensee": "Licenciados", - "core.h5p.licenseextras": "Extras da Licença", - "core.h5p.licenseversion": "Versão da Licença", - "core.h5p.nocopyright": "Não existe informação sobre direitos de autor para este conteúdo.", - "core.h5p.offlineDialogBody": "Não foi possível enviar informações sobre a conclusão desta tarefa. Por favor, verifique a sua ligação à Internet.", - "core.h5p.offlineDialogHeader": "A ligação com o servidor foi perdida", - "core.h5p.offlineDialogRetryButtonLabel": "Voltar a tentar agora", - "core.h5p.offlineDialogRetryMessage": "Voltar a tentar dentro de :num...", - "core.h5p.offlineSuccessfulSubmit": "Resultados submetidos com sucesso.", - "core.h5p.offlinedisabled": "O site não permite descarregar pacotes H5P.", - "core.h5p.originator": "Criador", - "core.h5p.pd": "Domínio Público", - "core.h5p.pddl": "Dedicação ao Domínio Público e Licença", - "core.h5p.pdm": "Marca de Domínio Público (PDM)", - "core.h5p.play": "Executar H5P", - "core.h5p.resizescript": "Incluir este script no seu site se pretender redimensionamento dinâmico do conteúdo incorporado:", - "core.h5p.resubmitScores": "Tentativa de envio dos resultados armazenados.", - "core.h5p.reuse": "Reutilizar", - "core.h5p.reuseContent": "Reutilizar conteúdo", - "core.h5p.reuseDescription": "Reutilizar este conteúdo.", - "core.h5p.showadvanced": "Mostrar avançado", - "core.h5p.showless": "Mostrar menos", - "core.h5p.showmore": "Mostrar mais", - "core.h5p.size": "Tamanho", - "core.h5p.source": "Origem", - "core.h5p.startingover": "Irá começar de novo.", - "core.h5p.sublevel": "Sub-nível", - "core.h5p.thumbnail": "Miniatura", - "core.h5p.title": "Título", - "core.h5p.undisclosed": "Não divulgado", - "core.h5p.year": "Ano", - "core.h5p.years": "Anos", - "core.h5p.yearsfrom": "Anos (desde)", - "core.h5p.yearsto": "Anos (até)", - "core.hasdatatosync": "{{$a}} tem dados offline que precisam de ser sincronizados.", - "core.help": "Ajuda", - "core.hide": "Ocultar", - "core.hour": "hora", - "core.hours": "horas", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Imagem", - "core.imageviewer": "Visualizador de imagens", - "core.info": "Informação", - "core.invalidformdata": "Formulário de dados incorreto", - "core.labelsep": ": ", - "core.lastaccess": "Último acesso", - "core.lastdownloaded": "Descarregado última vez em", - "core.lastmodified": "Última alteração", - "core.lastsync": "Última sincronização", - "core.layoutgrid": "Grelha", - "core.list": "Lista", - "core.listsep": ";", - "core.loading": "A carregar", - "core.loadmore": "Ver mais", - "core.location": "Localização", - "core.login.auth_email": "Autorregisto com confirmação por e-mail", - "core.login.authenticating": "Autenticação", - "core.login.cancel": "Cancelar", - "core.login.changepassword": "Modificar senha", - "core.login.changepasswordbutton": "Abrir a página de alteração da senha", - "core.login.changepasswordhelp": "Se tiver problemas para alterar a sua senha, entre em contacto com o administrador do site. Os \"Administradores do site\" são as pessoas que gerem o Moodle na sua escola/universidade/empresa ou organização de ensino. Se não souber como contactá-los, entre em contacto com os seus professores/formadores.", - "core.login.changepasswordinstructions": "Não pode alterar a sua senha na aplicação. Clique no botão a seguir para abrir o site num navegador web e alterar a sua senha. Tenha em consideração que é necessário fechar o navegador após alterar a senha, pois não será redirecionado para a aplicação.", - "core.login.changepasswordlogoutinstructions": "Se pretender alterar o site ou sair, clique no botão:", - "core.login.changepasswordreconnectinstructions": "Clique no botão a seguir para voltar a ligar-se ao site. (Tenha em consideração que, se não alterou a sua senha com sucesso, será redirecionado para a página anterior).", - "core.login.confirmdeletesite": "Tem a certeza que pretende remover o site {{sitename}}?", - "core.login.connect": "Ligar", - "core.login.connecttomoodle": "Ligar ao Moodle", - "core.login.connecttomoodleapp": "Está a tentar ligar-se a um site normal do Moodle. Descarregue a Aplicação móvel Moodle para aceder a este site.", - "core.login.connecttoworkplaceapp": "Está a tentar ligar-se a um site do Moodle Workplace. Descarregue a Aplicação móvel Moodle para aceder a este site.", - "core.login.contactyouradministrator": "Contacte o administrador do site para obter mais ajuda.", - "core.login.contactyouradministratorissue": "Por favor, solicite ao administrador que verifique o seguinte problema: {{$a}}", - "core.login.createaccount": "Criar a minha conta", - "core.login.createuserandpass": "Escolha um nome de utilizador e senha", - "core.login.credentialsdescription": "Por favor, digite o nome de utilizador e senha para entrar", - "core.login.emailconfirmsent": "

                    Acaba de ser enviada uma mensagem para o seu endereço de e-mail {{$a}}, com instruções fáceis para completar a sua inscrição.

                    Se tiver alguma dificuldade em completar o registo, contacte o administrador do site.

                    ", - "core.login.emailconfirmsentnoemail": "

                    Foi enviado um e-mail para o seu endereço.

                    Contém instruções fáceis para concluir o seu registo.

                    Se continuar com dificuldades, entre em contacto com o administrador do site.

                    ", - "core.login.emailconfirmsentsuccess": "E-mail de confirmação enviado com sucesso", - "core.login.emailnotmatch": "Os e-mails não coincidem", - "core.login.erroraccesscontrolalloworigin": "A acção de Cross-Origin que tentou executar foi rejeitada. Por favor, consulte mais informações em https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Ocorreu um erro ao apagar este site. Por favor, tente novamente.", - "core.login.errorexampleurl": "O URL https://campus.example.edu é apenas um exemplo de URL, não é um site real. Use o URL do site da sua escola ou organização.", - "core.login.errorupdatesite": "Ocorreu um erro enquanto atualizava o token do site.", - "core.login.faqcannotconnectanswer": "Por favor, contacte um administrador do site.", - "core.login.faqcannotconnectquestion": "Digitei o endereço do meu site corretamente mas ainda não consigo ligar.", - "core.login.faqcannotfindmysiteanswer": "Digitou o nome corretamente? Também pode acontecer que o seu site não esteja incluído no nosso diretório de sites públicos. Se continua a não conseguiu encontrar, digite o endereço do site.", - "core.login.faqcannotfindmysitequestion": "Não consigo encontrar o meu site.", - "core.login.faqsetupsiteanswer": "Visite a página {{$link}} para consultar as diferentes opções que precisa para criar o seu próprio site Moodle.", - "core.login.faqsetupsitelinktitle": "Iniciar.", - "core.login.faqsetupsitequestion": "Eu pretendo criar o meu próprio site Moodle.", - "core.login.faqtestappanswer": "Para testar a aplicação num site de demonstração do Moodle, digite \"teacher\" ou \"student\" no campo \"O seu site\" e clique no botão \"Entrar\".", - "core.login.faqtestappquestion": "Eu apenas pretendo testar a aplicação? O que devo fazer?", - "core.login.faqwhatisurlanswer": "

                    Todas as organizações têm o seu endereço único para o site Moodle. Para encontrar o endereço:

                    1. Abra um navegador da Web e aceda à página de autenticação do seu site Moodle
                    2. Na parte superior da página, na barra de endereços, verá o URL do seu site Moodle, por exemplo, \"campus.example.edu\".
                      {{$image}}
                    3. Copie o endereço (não copie a autenticação e o que está depois), cole-o na Aplicação móvel Moodle e clique em \"Entrar no seu site\"
                    4. Agora pode autenticar-se no seu site usando o seu nome de utilizador e senha.
                    5. ", - "core.login.faqwhatisurlquestion": "Qual é o endereço do meu site? Como posso encontrar o URL do meu site?", - "core.login.faqwhereisqrcode": "Onde posso encontrar o código QR?", - "core.login.faqwhereisqrcodeanswer": "

                      Se a sua organização ativou o código QR, irá aparecer um código QR na parte inferior da sua página de perfil de utilizador.

                      {{$image}}", - "core.login.findyoursite": "Procurar o site", - "core.login.firsttime": "É a sua primeira visita aqui?", - "core.login.forcepasswordchangenotice": "Tem de alterar a sua senha para poder continuar.", - "core.login.forgotten": "Esqueceu-se do seu nome de utilizador ou da senha?", - "core.login.help": "Ajuda", - "core.login.helpmelogin": "

                      Existem muitos milhares de sites Moodle em todo o mundo. Esta aplicação só pode ligar-se a sites Moodle que ativaram especificamente o acesso a aplicações para dispositivos móveis.

                      Se não conseguir ligar-se ao seu site Moodle, entre em contacto com o administrador do site e peça-lhe para consultar http://docs.moodle.org/en/Mobile_app.

                      Para testar a aplicação num site de demonstração do Moodle digite professor ou aluno no campo Endereço do site e depois clique no botão Ligar.

                      ", - "core.login.instructions": "Instruções", - "core.login.invalidaccount": "Por favor, verifique os detalhes dos seus dados de utilizador ou peça ao administrador do site para verificar as configurações do site.", - "core.login.invaliddate": "Data inválida", - "core.login.invalidemail": "Endereço de e-mail inválido", - "core.login.invalidmoodleversion": "

                      Versão inválida do site Moodle. A aplicação móvel Moodle apenas suporta sistemas Moodle {{$a}} ou superior.

                      \n

                      Entre em contacto com os administradores do site e peça para atualizem o sistema Moodle.

                      \n

                      Os \"Administradores do site\" são as pessoas que gerem o Moodle na sua escola/universidade/empresa ou organização de ensino. Se não souber como contactá-los, entre em contacto com os seus professores/formadores.

                      ", - "core.login.invalidsite": "O URL do site é inválido.", - "core.login.invalidtime": "Hora inválida", - "core.login.invalidurl": "O URL é inválido", - "core.login.invalidvaluemax": "O valor máximo é {{$a}}", - "core.login.invalidvaluemin": "O valor mínimo é {{$a}}", - "core.login.localmobileunexpectedresponse": "Ocorreu um erro inesperado na verificação de Moodle Mobile Additional Features. Será autenticado através do serviço Mobile padrão.", - "core.login.loggedoutssodescription": "Tem de autenticar-se novamente. A autenticação no site tem de ser numa janela do navegador.", - "core.login.login": "Entrar", - "core.login.loginbutton": "Iniciar sessão", - "core.login.logininsiterequired": "Precisa de entrar no site através de uma janela de navegador.", - "core.login.loginsteps": "Para ter acesso completo a este site, primeiro precisa de criar uma nova conta de utilizador.", - "core.login.missingemail": "Falta o endereço de e-mail", - "core.login.missingfirstname": "Falta o nome", - "core.login.missinglastname": "Falta o apelido", - "core.login.mobileservicesnotenabled": "O seu site não permite acesso através da Mobile App. Por favor, contacte o administrador se pretender que esta funcionalidade seja ativada.", - "core.login.mustconfirm": "Precisa confirmar a sua conta", - "core.login.newaccount": "Nova conta", - "core.login.notloggedin": "Precisa de estar autenticado.", - "core.login.onboardingcreatemanagecourses": "Criar e gerir as suas disciplinas", - "core.login.onboardingenrolmanagestudents": "Inscrever e gerir os seus alunos", - "core.login.onboardinggetstarted": "Começar com o Moodle", - "core.login.onboardingialreadyhaveasite": "Eu já tenho um site Moodle", - "core.login.onboardingimalearner": "Eu sou um aluno", - "core.login.onboardingimaneducator": "Eu sou educador", - "core.login.onboardingineedasite": "Eu preciso de um site Moodle", - "core.login.onboardingprovidefeedback": "Fornecer feedback oportuno", - "core.login.onboardingtoconnect": "Para se ligar à aplicação Moodle, precisa de um site Moodle", - "core.login.onboardingwelcome": "Bem-vindo à aplicação Moodle!", - "core.login.or": "OU", - "core.login.password": "Senha", - "core.login.passwordforgotten": "Senha esquecida", - "core.login.passwordforgotteninstructions2": "Para redefinir a sua senha, insira o seu nome de utilizador ou endereço de e-mail. Se um deles for encontrado na base de dados, será enviada uma mensagem para o seu endereço de e-mail com instruções para obter acesso novamente.", - "core.login.passwordrequired": "É necessária a senha", - "core.login.policyaccept": "Compreendo e concordo", - "core.login.policyagree": "Deverá aceitar este regulamento para poder proceder a utilizar este site. Aceita o regulamento?", - "core.login.policyagreement": "Política do site", - "core.login.policyagreementclick": "Link para a política do site", - "core.login.potentialidps": "Autenticar-se usando a sua conta:", - "core.login.profileinvaliddata": "Valor inválido", - "core.login.recaptchachallengeimage": "Imagem reCAPTCHA", - "core.login.recaptchaexpired": "O prazo de verificação expirou. Responda novamente à pergunta de segurança.", - "core.login.recaptchaincorrect": "A resposta à pergunta de segurança está incorreta.", - "core.login.reconnect": "Entrar novamente", - "core.login.reconnectdescription": "O seu token de autenticação é inválido ou expirou. Tem de entrar novamente no site.", - "core.login.reconnectssodescription": "O seu token de autenticação é inválido ou expirou. Tem de entrar novamente no site. Precisa de autenticar-se no site através de uma janela de navegador.", - "core.login.resendemail": "Reenviar e-mail", - "core.login.searchby": "Procurar por:", - "core.login.security_question": "Pergunta de segurança", - "core.login.selectacountry": "Selecione um país", - "core.login.selectsite": "Selecione o seu site:", - "core.login.signupplugindisabled": "{{$a}} não está ativado.", - "core.login.siteaddress": "O seu site", - "core.login.sitehasredirect": "O seu site contém pelo menos um redirecionamento de HTTP. A aplicação não pode seguir redirecionamentos. Pode ser este o problema que impede a aplicação de se ligar ao seu site.", - "core.login.siteinmaintenance": "O seu site está em modo de manutenção", - "core.login.sitepolicynotagreederror": "Não concordou com as políticas do site.", - "core.login.siteurl": "URL do site", - "core.login.siteurlrequired": "É necessário o URL do site, por exemplo http://www.yourmoodlesite.org", - "core.login.startsignup": "Criar nova conta", - "core.login.stillcantconnect": "Continua com problemas na ligação?", - "core.login.supplyinfo": "Insira alguma informação sobre si", - "core.login.username": "Nome de utilizador", - "core.login.usernameoremail": "Introduza o nome de utilizador ou o e-mail", - "core.login.usernamerequired": "É necessário o nome de utilizador", - "core.login.usernotaddederror": "Utilizador não adicionado - erro.", - "core.login.visitchangepassword": "Pretende visitar o site para alterar a senha?", - "core.login.webservicesnotenabled": "O servidor do site talvez não tenha ativado os web services. Por favor, contacte o administrador se pretender que esta funcionalidade seja ativada.", - "core.login.youcanstillconnectwithcredentials": "Ainda pode ligar-se ao site digitando o seu nome de utilizador e a senha.", - "core.login.yourenteredsite": "Ligar-se ao seu site", - "core.lostconnection": "O seu token é inválido ou expirou. Terá de se autenticar novamente no site.", - "core.mainmenu.changesite": "Mudar de site", - "core.mainmenu.help": "Ajuda", - "core.mainmenu.logout": "Sair", - "core.mainmenu.website": "Site", - "core.maxsizeandattachments": "Tamanho máximo do ficheiro: {{$a.size}}, número máximo de ficheiros: {{$a.attachments}}", - "core.min": "minuto", - "core.mins": "minutos", - "core.misc": "Diversos", - "core.mod_assign": "Trabalho", - "core.mod_assignment": "Trabalho 2.2 (Desativado)", - "core.mod_book": "Livro", - "core.mod_chat": "Chat", - "core.mod_choice": "Sondagem", - "core.mod_data": "Base de dados", - "core.mod_database": "Base de dados", - "core.mod_external-tool": "Ferramenta externa", - "core.mod_feedback": "Inquérito", - "core.mod_file": "Ficheiro", - "core.mod_folder": "Pasta", - "core.mod_forum": "Fórum", - "core.mod_glossary": "Glossário", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "Pacotes IMS", - "core.mod_imscp": "Pacotes IMS", - "core.mod_label": "Separador", - "core.mod_lesson": "Lição", - "core.mod_lti": "Ferramenta externa", - "core.mod_page": "Página", - "core.mod_quiz": "Teste", - "core.mod_resource": "Ficheiro", - "core.mod_scorm": "Pacote SCORM", - "core.mod_survey": "Inquérito predefinido", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Workshop", - "core.moduleintro": "Descrição", - "core.more": "mais", - "core.mygroups": "Os meus grupos", - "core.name": "Designação", - "core.needhelp": "Precisa de ajuda?", - "core.networkerroriframemsg": "Este conteúdo não está disponível offline. Por favor, ligue-se à internet e tente novamente.", - "core.networkerrormsg": "Ocorreu um erro ao ligar ao site. Por favor, verifique a sua ligação e tente novamente.", - "core.never": "Nunca", - "core.next": "Seguinte", - "core.no": "Não", - "core.nocomments": "Sem comentários", - "core.nograde": "Nenhuma nota", - "core.none": "Nenhum", - "core.nooptionavailable": "Nenhuma opção disponível", - "core.nopasswordchangeforced": "Não pode prosseguir sem alterar a sua senha.", - "core.nopermissionerror": "Desculpe, mas não tem permissões para executar esta operação", - "core.nopermissions": "Atualmente, não tem permissões para realizar a operação ({{$a}})", - "core.noresults": "Sem resultados", - "core.noselection": "Sem seleção", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Este perfil não está ativo porque o utilizador não está inscrito nesta disciplina.", - "core.notice": "Aviso", - "core.notingroup": "Lamentamos, mas precisa de estar inscrito num grupo para ver esta página.", - "core.notsent": "Não enviado", - "core.now": "agora", - "core.nummore": "{{$a}} mais", - "core.numwords": "{{$a}} palavras", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Clique aqui para mostrar a imagem em tamanho real", - "core.openinbrowser": "Abrir no navegador", - "core.openmodinbrowser": "Abrir {{$a}} no navegador", - "core.othergroups": "Outros grupos", - "core.pagea": "Página {{$a}}", - "core.paymentinstant": "Use o botão abaixo para pagar e completar a inscrição!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefone", - "core.pictureof": "Fotografia de {{$a}}", - "core.previous": "Anterior", - "core.proceed": "Continuar", - "core.pulltorefresh": "Puxe para atualizar", - "core.qrscanner": "Leitor códigos QR", - "core.question.answer": "Resposta", - "core.question.answersaved": "Resposta guardada", - "core.question.cannotdeterminestatus": "Não é possível determinar o estado", - "core.question.certainty": "Certeza", - "core.question.complete": "Respondida", - "core.question.correct": "Correta", - "core.question.errorattachmentsnotsupported": "A aplicação ainda não suporta anexar ficheiros a respostas.", - "core.question.errorinlinefilesnotsupported": "A aplicação ainda não suporta a edição de ficheiros online.", - "core.question.errorquestionnotsupported": "Este tipo de pergunta não é suportado pela aplicação: {{$a}}.", - "core.question.feedback": "Feedback", - "core.question.howtodraganddrop": "Toque para selecionar e depois toque para largar.", - "core.question.incorrect": "Incorreta", - "core.question.information": "Informação", - "core.question.invalidanswer": "Resposta incompleta", - "core.question.notanswered": "Não respondida", - "core.question.notyetanswered": "Por responder", - "core.question.partiallycorrect": "Parcialmente correta", - "core.question.questionmessage": "Pergunta {{$a}}: {{$b}}", - "core.question.questionno": "Pergunta {{$a}}", - "core.question.requiresgrading": "Requer avaliação", - "core.quotausage": "Está atualmente a usar {{$a.used}} do máximo de {{$a.total}}.", - "core.rating.aggregateavg": "Nota média", - "core.rating.aggregatecount": "Número de avaliações", - "core.rating.aggregatemax": "Nota máxima", - "core.rating.aggregatemin": "Nota mínima", - "core.rating.aggregatesum": "Soma das notas", - "core.rating.noratings": "Não foram submetidas avaliações", - "core.rating.rating": "Avaliação", - "core.rating.ratings": "Avaliações", - "core.redirectingtosite": "Irá ser redirecionado para o site.", - "core.refresh": "Atualizar", - "core.remove": "Remover", - "core.removefiles": "Remover ficheiros {{$a}}", - "core.required": "Obrigatório", - "core.requireduserdatamissing": "Este utilizador não possui todos os dados de perfil obrigatórios. Por favor, preencha os dados em falta no seu site e tente novamente.
                      {{$a}}", - "core.resourcedisplayopen": "Mesma janela", - "core.resources": "Recursos", - "core.restore": "Restaurar", - "core.restricted": "Acesso vedado", - "core.retry": "Tentar novamente", - "core.save": "Guardar", - "core.savechanges": "Guardar alterações", - "core.scanqr": "Ler código QR", - "core.search": "Procurar", - "core.searching": "A procurar", - "core.searchresults": "Resultado da procura", - "core.sec": "segundo", - "core.secs": "segundos", - "core.seemoredetail": "Clique aqui para ver mais detalhes", - "core.selectacategory": "Por favor, selecione uma categoria", - "core.selectacourse": "Selecione uma disciplina", - "core.selectagroup": "Selecione um grupo", - "core.send": "Enviar", - "core.sending": "A enviar", - "core.serverconnection": "Ocorreu um erro ao conectar com o servidor", - "core.settings.about": "Acerca", - "core.settings.appsettings": "Configurações da aplicação", - "core.settings.appversion": "Versão da aplicação", - "core.settings.cannotsyncoffline": "Não é possível sincronizar offline.", - "core.settings.cannotsyncwithoutwifi": "Não é possível sincronizar porque as configurações atuais só permitem sincronizar quando ligado a uma rede sem fios. Por favor, ligue-se a uma rede Wi-Fi.", - "core.settings.colorscheme": "Esquema de cores", - "core.settings.colorscheme-auto": "Automático (basedo nas configurações do sistema)", - "core.settings.colorscheme-dark": "Escuro", - "core.settings.colorscheme-light": "Claro", - "core.settings.compilationinfo": "Informação da compilação", - "core.settings.copyinfo": "Copiar informações do dispositivo para a área de transferência", - "core.settings.cordovadevicemodel": "Modelo do dispositivo Cordova", - "core.settings.cordovadeviceosversion": "Versão do OS do dispositivo Cordova", - "core.settings.cordovadeviceplatform": "Plataforma do dispositivo Cordova", - "core.settings.cordovadeviceuuid": "UUID do dispositivo Cordova device UUID", - "core.settings.cordovaversion": "Versão do Cordova", - "core.settings.currentlanguage": "Idioma usado atualmente", - "core.settings.debugdisplay": "Mostrar mensagens de erro", - "core.settings.debugdisplaydescription": "Se ativar esta opção, as janelas modais dos erros mostrarão mais informações sobre o erro, se for possível.", - "core.settings.deletesitefiles": "Tem a certeza de que pretende apagar os ficheiros descarregados e os dados guardados em cache do site '{{sitename}}'? Não poderá usar a aplicação no modo offline.", - "core.settings.deletesitefilestitle": "Apagar os ficheiros do site", - "core.settings.deviceinfo": "Informação do dispositivo", - "core.settings.deviceos": "OS do dispositivo", - "core.settings.disableall": "Desativar notificações", - "core.settings.disabled": "Desativado", - "core.settings.displayformat": "Formato de visualização", - "core.settings.enabledownloadsection": "Ativar secções de transferência", - "core.settings.enablefirebaseanalytics": "Ativar a análise Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Se ativar esta opção, a aplicação guardará o uso de dados anónimos.", - "core.settings.enablerichtexteditor": "Ativar o editor de texto", - "core.settings.enablerichtexteditordescription": "Se ativar esta opção, um editor de texto será mostrado ao inserir conteúdos.", - "core.settings.enablesyncwifi": "Permitir sincronização apenas através de Wi-Fi", - "core.settings.entriesincache": "{{$a}} entradas na cache", - "core.settings.errordeletesitefiles": "Erro ao apagar os ficheiros do site.", - "core.settings.errorsyncsite": "Erro ao sincronizar os dados do site. Por favor, verifique a sua ligação à Internet e tente novamente.", - "core.settings.estimatedfreespace": "Espaço livre estimado", - "core.settings.filesystemroot": "Raíz dos ficheiros do sistema", - "core.settings.fontsize": "Tamanho do texto", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "Esta configuração foi forçada pela configuração do seu site.", - "core.settings.general": "Geral", - "core.settings.language": "Idioma", - "core.settings.license": "Licença", - "core.settings.localnotifavailable": "Notificações locais disponíveis", - "core.settings.locationhref": "URL da vista Web", - "core.settings.locked": "Bloqueado", - "core.settings.loggedin": "Online", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Idioma do navegador", - "core.settings.navigatoruseragent": "UserAgent do navegador", - "core.settings.networkstatus": "Estado da ligação à Internet", - "core.settings.opensourcelicenses": "Licenças de Código Aberto", - "core.settings.preferences": "Preferências", - "core.settings.privacypolicy": "Politica de privacidade", - "core.settings.publisher": "Editor", - "core.settings.pushid": "Enviar ID das notificações", - "core.settings.reportinbackground": "Reportar erros automaticamente", - "core.settings.screen": "Informação do ecrã", - "core.settings.settings": "Configurações", - "core.settings.showdownloadoptions": "Mostrar opções de descarga", - "core.settings.siteinfo": "Informação do site", - "core.settings.sites": "Sites", - "core.settings.spaceusage": "Utilização do espaço", - "core.settings.spaceusagehelp": "Ao apagar as informações armazenadas do site removerá todos os dados offline do site. Esta informação permite que use o aplicativo quando estiver offline.", - "core.settings.synchronization": "Sincronização", - "core.settings.synchronizenow": "Sincronizar agora", - "core.settings.synchronizenowhelp": "A sincronização de um site enviará alterações pendentes e todas as atividades offline armazenadas no dispositivo e sincronizará alguns dados, como por exemplo, mensagens e notificações.", - "core.settings.syncsettings": "Configurações da sincronização", - "core.settings.total": "Total", - "core.settings.wificonnection": "Ligação Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "Escolha uma conta para onde armazenar o ficheiro.", - "core.sharedfiles.chooseactionrepeatedfile": "Já existe um ficheiro com este nome. Deseja substituir o ficheiro existente ou renomeá-lo para \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Não existem sites armazenados. Adicione um site antes de partilhar um ficheiro com a aplicação.", - "core.sharedfiles.nosharedfiles": "Não existem quaisquer ficheiros partilhados armazenados neste site.", - "core.sharedfiles.nosharedfilestoupload": "Não existem ficheiros para fazer o carregamento. Se pretender carregar um ficheiro de outra aplicação, localize o ficheiro e clique no botão 'Abrir em'.", - "core.sharedfiles.rename": "Mudar nome", - "core.sharedfiles.replace": "Substituir", - "core.sharedfiles.sharedfiles": "Ficheiros partilhados", - "core.sharedfiles.successstorefile": "Ficheiro armazenado com sucesso. Selecione este ficheiro para enviá-lo para os seus ficheiros privados ou usá-lo em atividades.", - "core.show": "Mostrar", - "core.showless": "Mostrar menos…", - "core.showmore": "Mostrar mais…", - "core.site": "Site", - "core.sitehome.sitehome": "Página inicial do site", - "core.sitehome.sitenews": "Anúncios do site", - "core.sitemaintenance": "O site está em manutenção e não está disponível de momento", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Saltar", - "core.sorry": "Desculpe...", - "core.sort": "Ordenar", - "core.sortby": "Ordenar por", - "core.start": "Executar", - "core.storingfiles": "A armazenar ficheiros", - "core.strftimedate": "%d de %B de %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d de %B", - "core.strftimedatetime": "%d de %B de %Y às %H:%M", - "core.strftimedatetimeshort": "%d/%m/%y às %H:%M", - "core.strftimedaydate": "%A, %d de %B de %Y", - "core.strftimedaydatetime": "%A, %d de %B de %Y às %H:%M", - "core.strftimedayshort": "%A, %d de %B", - "core.strftimedaytime": "%a às %H:%M", - "core.strftimemonthyear": "%B de %Y", - "core.strftimerecent": "%d de %b às %H:%M", - "core.strftimerecentfull": "%a, %d de %b de %Y às %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Submeter", - "core.success": "Operação realizada com sucesso", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Coleção predefinida", - "core.tag.errorareanotsupported": "Esta área de palavras-chave não é suportada pela aplicação.", - "core.tag.inalltagcoll": "Em qualquer local", - "core.tag.itemstaggedwith": "{{$a.tagarea}} com a palavra-chave \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Não foram devolvidos resultados para \"{{$a}}\"", - "core.tag.notagsfound": "Não foi encontrada a palavra-chave \"{{$a}}\"", - "core.tag.searchtags": "Procurar palavras-chave", - "core.tag.showingfirsttags": "A mostrar as {{$a}} palavras-chave mais populares", - "core.tag.tag": "Palavra-chave", - "core.tag.tagarea_course": "Disciplinas", - "core.tag.tagarea_course_modules": "Atividades e recursos", - "core.tag.tagarea_post": "Entradas de blogue", - "core.tag.tagarea_user": "Interesses do utilizador", - "core.tag.tags": "Palavras-chave", - "core.tag.warningareasnotsupported": "Algumas das áreas de palavras-chave não são mostraas porque não são suportadas pela aplicação.", - "core.teachers": "Professores", - "core.thereisdatatosync": "Existem {{$a}} offline que têm de ser sincronizados.", - "core.thisdirection": "ltr", - "core.time": "Data e hora", - "core.timesup": "O tempo terminou!", - "core.today": "Hoje", - "core.tryagain": "Tente novamente", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.uhoh": "Ocorreu um erro!", - "core.unexpectederror": "Erro inesperado. Por favor, feche e abra a aplicação e tente de novo.", - "core.unicodenotsupported": "Alguns emojis não são suportados neste site. Estes caracteres serão removidos quando a mensagem for enviada.", - "core.unicodenotsupportedcleanerror": "Foi encontrado texto vazio ao limpar caracteres Unicode.", - "core.unknown": "Desconhecido", - "core.unlimited": "Ilimitado", - "core.unzipping": "A descomprimir", - "core.updaterequired": "É necessário atualizar a aplicação", - "core.updaterequireddesc": "Por favor, atualize a aplicação para a versão {{$a}}", - "core.upgraderunning": "O site está em processo de atualização. Por favor, tente novamente mais tarde.", - "core.user": "Utilizador", - "core.user.address": "Morada", - "core.user.city": "Cidade/Estado", - "core.user.contact": "Contacto", - "core.user.country": "País", - "core.user.description": "Descrição", - "core.user.details": "Detalhes", - "core.user.detailsnotavailable": "Não tem acesso aos detalhes deste utilizador.", - "core.user.editingteacher": "Professor", - "core.user.email": "Endereço de e-mail", - "core.user.emailagain": "E-mail (novamente)", - "core.user.errorloaduser": "Erro ao carregar o utilizador", - "core.user.firstname": "Nome", - "core.user.interests": "Interesses", - "core.user.lastname": "Apelido", - "core.user.manager": "Gestor", - "core.user.newpicture": "Nova imagem", - "core.user.noparticipants": "Não foram encontrados participantes para esta disciplina", - "core.user.participants": "Participantes", - "core.user.phone1": "Telefone", - "core.user.phone2": "Telemóvel", - "core.user.roles": "Papéis atribuídos", - "core.user.sendemail": "E-mail", - "core.user.student": "Aluno", - "core.user.teacher": "Professor não editor", - "core.user.webpage": "Página Web", - "core.userdeleted": "Este utilizador foi apagado", - "core.userdetails": "Mais detalhes", - "core.usernotfullysetup": "O utilizador não está totalmente configurado", - "core.users": "Utilizadores", - "core.view": "Ver", - "core.viewcode": "Ver código", - "core.vieweditor": "Ver editor", - "core.viewembeddedcontent": "Ver conteúdo incorporado", - "core.viewprofile": "Ver perfil", - "core.warningofflinedatadeleted": "Os dados offline de {{component}} '{{name}}' foram apagados. {{error}}", - "core.whatisyourage": "Qual é a sua idade?", - "core.wheredoyoulive": "Em que país vive?", - "core.whoissiteadmin": "\"Administradores do Site\" são os utilizadores que gerem o Moodle na sua escola/universidade/empresa ou organização de ensino. Se não souber como contactá-los, fale com os seus professores/treinadores.", - "core.whoops": "Oops!", - "core.whyisthishappening": "Por que é que isto está a acontecer?", - "core.whyisthisrequired": "Porque é que isto é obrigatório?", - "core.wsfunctionnotavailable": "A função do webservice não está disponível.", - "core.year": "ano", - "core.years": "anos", - "core.yes": "Sim", - "core.youreoffline": "Está offline", - "core.youreonline": "Está online novamente" -} \ No newline at end of file diff --git a/src/assets/lang/ro.json b/src/assets/lang/ro.json deleted file mode 100644 index 03ea43dfa..000000000 --- a/src/assets/lang/ro.json +++ /dev/null @@ -1,1519 +0,0 @@ -{ - "addon.badges.badgedetails": "Detalii ecuson", - "addon.badges.badges": "Ecusoane", - "addon.badges.bendorsement": "Susținere", - "addon.badges.contact": "Contact", - "addon.badges.dateawarded": "Data emiterii", - "addon.badges.expired": "Expirat", - "addon.badges.expirydate": "Dată de expirare", - "addon.badges.issuancedetails": "Expirare ecuson", - "addon.badges.issuerdetails": "Detalii emitent", - "addon.badges.issueremail": "Email", - "addon.badges.issuername": "Nume emitent", - "addon.badges.issuerurl": "URL emitent", - "addon.badges.nobadges": "Nu există ecusoane disponibile", - "addon.badges.recipientdetails": "Detalii recipient", - "addon.badges.warnexpired": "(Acest ecuson a expirat!)", - "addon.block_activitymodules.pluginname": "Activităţi", - "addon.block_activityresults.pluginname": "Rezultate activitate", - "addon.block_badges.pluginname": "Ultimele ecusoane", - "addon.block_blogmenu.pluginname": "Meniu blog", - "addon.block_blogrecent.pluginname": "Articolele recente din blog", - "addon.block_blogtags.pluginname": "Etichete blog", - "addon.block_calendarmonth.pluginname": "Calendar", - "addon.block_calendarupcoming.pluginname": "Evenimente următoare", - "addon.block_comments.pluginname": "Comentarii", - "addon.block_completionstatus.pluginname": "Stadiu completare curs", - "addon.block_glossaryrandom.pluginname": "Termeni aleatori din Glosar", - "addon.block_learningplans.pluginname": "Planuri de învățare", - "addon.block_myoverview.all": "Toate", - "addon.block_myoverview.allincludinghidden": "Toate", - "addon.block_myoverview.future": "Viitoare", - "addon.block_myoverview.hiddencourses": "Ascuns", - "addon.block_myoverview.inprogress": "În desfășurare", - "addon.block_myoverview.lastaccessed": "Accesat ultima dată", - "addon.block_myoverview.morecourses": "Mai multe cursuri", - "addon.block_myoverview.nocourses": "Nu există cursuri", - "addon.block_myoverview.past": "În trecut", - "addon.block_myoverview.title": "Numele cursului", - "addon.block_newsitems.pluginname": "Ultimele știri", - "addon.block_onlineusers.pluginname": "Utilizatori online", - "addon.block_privatefiles.pluginname": "Fișierele mele personale", - "addon.block_recentactivity.pluginname": "Activităţi recente", - "addon.block_recentlyaccessedcourses.nocourses": "Nu există cursuri recente", - "addon.block_recentlyaccessedcourses.pluginname": "Cursuri accesate recent", - "addon.block_rssclient.pluginname": "Fluxuri RSS la distanță", - "addon.block_selfcompletion.pluginname": "Auto finalizare", - "addon.block_sitemainmenu.pluginname": "Meniu principal", - "addon.block_starredcourses.nocourses": "Nu există cursuri importante", - "addon.block_starredcourses.pluginname": "Cursuri importante", - "addon.block_tags.pluginname": "Etichete", - "addon.block_timeline.next30days": "Următoarele 30 de zile", - "addon.block_timeline.next3months": "Următoarele 3 luni", - "addon.block_timeline.next6months": "Următoarele 6 luni", - "addon.block_timeline.next7days": "Următoarele 7 zile", - "addon.block_timeline.pluginname": "Cronologie", - "addon.block_timeline.sortbycourses": "Sortează după cursuri", - "addon.block_timeline.sortbydates": "Sortează după dată", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Postări blog", - "addon.blog.linktooriginalentry": "Link către postarea originală pe blog", - "addon.blog.noentriesyet": "Nu există postări vizibile", - "addon.blog.publishtonoone": "Autor (ciornă)", - "addon.blog.publishtosite": "Oricine are acces la acest sit", - "addon.blog.publishtoworld": "Oricine", - "addon.blog.siteblogheading": "Site blog", - "addon.calendar.allday": "Toată ziua", - "addon.calendar.calendar": "Calendar", - "addon.calendar.calendarevents": "Evenimente din calendar", - "addon.calendar.categoryevents": "Evenimente din categorie", - "addon.calendar.confirmeventdelete": "Sigur doriţi să ştergeţi evenimentul \"{{$a}}\"?", - "addon.calendar.courseevents": "Evenimente la curs", - "addon.calendar.daynext": "Ziua următoare", - "addon.calendar.dayprev": "Ziua precedentă", - "addon.calendar.deleteallevents": "Șterge toate evenimentele", - "addon.calendar.deleteevent": "Şterge eveniment", - "addon.calendar.deleteoneevent": "Șterge acest eveniment", - "addon.calendar.durationminutes": "Durată în minute", - "addon.calendar.durationnone": "Durată nespecificată", - "addon.calendar.durationuntil": "Până la", - "addon.calendar.editevent": "Modificare eveniment", - "addon.calendar.errorloadevent": "Eroare la încărcarea unui eveniment.", - "addon.calendar.errorloadevents": "Eroare la încărcarea unor evenimente.", - "addon.calendar.eventcalendareventdeleted": "Eveniment din calendar șters", - "addon.calendar.eventduration": "Durata", - "addon.calendar.eventendtime": "Ora încheierii", - "addon.calendar.eventkind": "Tipul evenimentului", - "addon.calendar.eventname": "Titlul evenimentului", - "addon.calendar.eventstarttime": "Ora începerii", - "addon.calendar.eventtype": "Tipul evenimentului", - "addon.calendar.fri": "Vi", - "addon.calendar.friday": "Vineri", - "addon.calendar.gotoactivity": "Mergeți la activitate", - "addon.calendar.groupevents": "Evenimente de grup", - "addon.calendar.invalidtimedurationminutes": "Durata în minute pe care ați introdus-o nu este validă. Vă rugăm să introduceți o durată în minute mai mare decât 0 sau selectați nu există o durată.", - "addon.calendar.invalidtimedurationuntil": "Data și ora selectată pentru durata până la este înainte de ora de începere a evenimentului. Vă rugăm să corectați înainte de a continua.", - "addon.calendar.mon": "Lu", - "addon.calendar.monday": "Luni", - "addon.calendar.monthlyview": "Vizualizare lunară", - "addon.calendar.newevent": "Eveniment nou", - "addon.calendar.noevents": "Nu sunt evenimente noi", - "addon.calendar.nopermissiontoupdatecalendar": "Ne pare rău, dar nu aveți în prezent permisiunea de a actualiza evenimente din calendar", - "addon.calendar.repeatedevents": "Evenimente repetate", - "addon.calendar.repeateditall": "Aplică modificări și tuturor celor {{$a}} evenimente din această serie de repetiţii", - "addon.calendar.repeateditthis": "Se aplică modificările doar acestui eveniment", - "addon.calendar.repeatevent": "Repetați acest eveniment", - "addon.calendar.repeatweeksl": "Repetă săptămânal, creează de la zero", - "addon.calendar.sat": "Sâ", - "addon.calendar.saturday": "Sâmbătă", - "addon.calendar.siteevents": "Evenimente pe site", - "addon.calendar.sun": "Du", - "addon.calendar.sunday": "Duminică", - "addon.calendar.thu": "Jo", - "addon.calendar.thursday": "Joi", - "addon.calendar.today": "Azi", - "addon.calendar.tomorrow": "Mâine", - "addon.calendar.tue": "Ma", - "addon.calendar.tuesday": "Marți", - "addon.calendar.typecategory": "Categoria eveniment", - "addon.calendar.typeclose": "Închide evenimentul", - "addon.calendar.typecourse": "Eveniment al cursului", - "addon.calendar.typegroup": "Eveniment al grupului", - "addon.calendar.typeopen": "Deschide evenimentul", - "addon.calendar.typesite": "Eveniment al sit-ului", - "addon.calendar.typeuser": "Eveniment al utilizatorului", - "addon.calendar.upcomingevents": "Evenimente următoare", - "addon.calendar.userevents": "Evenimente utilizator", - "addon.calendar.wed": "Mi", - "addon.calendar.wednesday": "Miercuri", - "addon.calendar.when": "Când", - "addon.calendar.yesterday": "Ieri", - "addon.competency.activities": "Activități", - "addon.competency.competencies": "Competențe", - "addon.competency.evidence_competencyrule": "Nu a fost îndeplinită regula competenței.", - "addon.competency.evidence_coursecompleted": "Cursul '{{$a}}' a fost completat", - "addon.competency.evidence_coursemodulecompleted": "Activitatea '{{$a}}' a fost completată", - "addon.competency.learningplans": "Planuri de învățare", - "addon.competency.myplans": "Planurile mele de învățare", - "addon.competency.noactivities": "Nu sunt activități", - "addon.competency.nouserplanswithcompetency": "Nu există planuri de învățare care să conțină această competență.", - "addon.competency.planstatusactive": "Activ", - "addon.competency.planstatuscomplete": "Complet", - "addon.competency.planstatusdraft": "Draft", - "addon.competency.planstatusinreview": "În revizuire", - "addon.competency.planstatuswaitingforreview": "Se așteaptă recenzia", - "addon.competency.status": "Status", - "addon.competency.usercompetencystatus_idle": "Pauză", - "addon.competency.usercompetencystatus_inreview": "În revizuire", - "addon.competency.usercompetencystatus_waitingforreview": "Se așteaptă revizuirea", - "addon.competency.userplans": "Planuri de învățare", - "addon.coursecompletion.complete": "Complet", - "addon.coursecompletion.completecourse": "Curs complet", - "addon.coursecompletion.completed": "Finalizare", - "addon.coursecompletion.completiondate": "Data limita până la completarea acțiunii", - "addon.coursecompletion.completionmenuitem": "Finalizare", - "addon.coursecompletion.couldnotloadreport": "Raportul cu privire la situația completării cursului nu se poate încărca, încercați mai târziu.", - "addon.coursecompletion.coursecompletion": "Completare curs", - "addon.coursecompletion.criteria": "Criterii", - "addon.coursecompletion.criteriagroup": "Grup criterii", - "addon.coursecompletion.criteriarequiredall": "Toate criteriile de mai jos sunt necesare", - "addon.coursecompletion.criteriarequiredany": "Oricare dintre criteriile de mai jos sunt necesare", - "addon.coursecompletion.inprogress": "În curs", - "addon.coursecompletion.manualselfcompletion": "Auto-finalizare manuală", - "addon.coursecompletion.nottracked": "Statusul activității dumneavoastră nu este urmărit în acest curs", - "addon.coursecompletion.notyetstarted": "Nu a fost încă început", - "addon.coursecompletion.pending": "În așteptare", - "addon.coursecompletion.required": "Obligatoriu", - "addon.coursecompletion.requiredcriteria": "Criteriu necesar", - "addon.coursecompletion.requirement": "Cerință", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Vezi raportul cursului", - "addon.files.couldnotloadfiles": "Lista fișierelor nu a putut fi încărcată.", - "addon.files.emptyfilelist": "Nu sunt fișiere disponibile.", - "addon.files.files": "Fişiere", - "addon.files.privatefiles": "Fișiere private", - "addon.files.sitefiles": "Fişiere site", - "addon.messages.acceptandaddcontact": "Acceptă și adaugă la contacte", - "addon.messages.addcontact": "Adaugă prieten", - "addon.messages.addcontactconfirm": "Sunteți sigur că doriți să adăugați {{$a}} la contactele dumneavoastră?", - "addon.messages.addtofavourites": "Evidențiați conversația", - "addon.messages.addtoyourcontacts": "Adaugă la contacte", - "addon.messages.blocknoncontacts": "Blochează toate mesajele noi de la persoane care nu se află în lista mea de prieteni", - "addon.messages.blockuser": "Blochează utilizatorul", - "addon.messages.blockuserconfirm": "Sunteți sigur că doriți să blocați pe {{$a}}?", - "addon.messages.contactableprivacy": "Acceptă mesaje de la:", - "addon.messages.contactableprivacy_coursemember": "Contactele mele și ceilalți participanți la curs", - "addon.messages.contactableprivacy_onlycontacts": "Doar contactele mele", - "addon.messages.contactblocked": "Contactul a fost blocat", - "addon.messages.contactlistempty": "Lista de contacte este goală", - "addon.messages.contactname": "Nume contact", - "addon.messages.contacts": "Prieteni", - "addon.messages.decline": "Respinge", - "addon.messages.deleteallselfconfirm": "Sunteți sigur că doriți să stergeți toată această conversație personală?", - "addon.messages.deleteconversation": "Șterge conversația", - "addon.messages.errordeletemessage": "Eroare la ștergerea mesajului.", - "addon.messages.errorwhileretrievingcontacts": "A apărut o eroare în găsirea contactelor pe server.", - "addon.messages.errorwhileretrievingdiscussions": "A apărut o eroare în găsirea conversațiilor de pe server.", - "addon.messages.errorwhileretrievingmessages": "A apărut o eroare în găsirea mesajelor de pe server.", - "addon.messages.groupconversations": "Grup", - "addon.messages.info": "Informații despre utilizator", - "addon.messages.isnotinyourcontacts": "{{$a}} nu este în contactele dumneavoastră", - "addon.messages.message": "Mesaj", - "addon.messages.messagenotsent": "Mesajul nu a fost expediat, vă rugăm să încercați mai târziu.", - "addon.messages.messages": "Mesaje", - "addon.messages.newmessage": "Mesaj nou", - "addon.messages.nofavourites": "Nu există conversații evidențiate", - "addon.messages.noindividualconversations": "Nu există conversații private", - "addon.messages.nomessagesfound": "Nu s-a găsit niciun mesaj", - "addon.messages.noncontacts": "Nu este în lista de prieteni", - "addon.messages.nousersfound": "Nu au fost găsiți utilizatori", - "addon.messages.numparticipants": "{{$a}} participanți", - "addon.messages.removecontact": "Şterge prieten din listă", - "addon.messages.requests": "Solicitări", - "addon.messages.searchcombined": "Caută persoane și mesaje", - "addon.messages.type_blocked": "Blocat", - "addon.messages.type_offline": "Deconectat", - "addon.messages.type_online": "Conectat", - "addon.messages.type_search": "Caută rezultatele", - "addon.messages.type_strangers": "Alții", - "addon.messages.unabletomessage": "Nu puteți trimite mesaje acestui utilizator", - "addon.messages.unblockuser": "Deblocați utilizatorul", - "addon.messages.you": "Dumneavoastră:", - "addon.messages.youhaveblockeduser": "Ați blocat acest utilizator", - "addon.mod_assign.addattempt": "Permite altă încercare", - "addon.mod_assign.addnewattempt": "Adaugă o nouă încercare", - "addon.mod_assign.addsubmission": "Adaugă lucrare", - "addon.mod_assign.allowsubmissionsfromdate": "Acceptați lucrări de la", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Se acceptă lucrări de la {{$a}}", - "addon.mod_assign.applytoteam": "Aplică notele și feedback-ul întregului grup", - "addon.mod_assign.assignmentisdue": "Data predării", - "addon.mod_assign.attemptnumber": "Numărul încercării", - "addon.mod_assign.attemptreopenmethod": "Încercările sunt disponibile din nou", - "addon.mod_assign.attemptreopenmethod_manual": "Manual", - "addon.mod_assign.attemptsettings": "Setări încerecre", - "addon.mod_assign.currentattempt": "Aceasta este încercarea {{$a}}.", - "addon.mod_assign.currentattemptof": "Aceasta este încercarea {{$a.attemptnumber}} ( {{$a.maxattempts}} încercări permise ).", - "addon.mod_assign.currentgrade": "Nota actuală în catalog", - "addon.mod_assign.defaultteam": "Grup implicit", - "addon.mod_assign.duedate": "Termen de predare", - "addon.mod_assign.duedateno": "Fără termen de predare", - "addon.mod_assign.duedatereached": "Data predării acestei lucrări este depășită", - "addon.mod_assign.editingstatus": "Se editează statusul", - "addon.mod_assign.editsubmission": "Editare temă trimisă", - "addon.mod_assign.extensionduedate": "Termen de predare extins", - "addon.mod_assign.grade": "Notă", - "addon.mod_assign.graded": "Notat", - "addon.mod_assign.gradedby": "Notat de", - "addon.mod_assign.gradedon": "Notat în data de", - "addon.mod_assign.gradelocked": "Această notă este blocată sau suprascrisă în catalog.", - "addon.mod_assign.gradeoutof": "O notă din {{$a}}", - "addon.mod_assign.hiddenuser": "Participant", - "addon.mod_assign.latesubmissionsaccepted": "Permis până în {{$a}}", - "addon.mod_assign.markingworkflowstateinmarking": "Se notează", - "addon.mod_assign.markingworkflowstateinreview": "Se revizuiește", - "addon.mod_assign.markingworkflowstatenotmarked": "Nu este notat", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Gata de lansare", - "addon.mod_assign.markingworkflowstatereadyforreview": "Notare completă", - "addon.mod_assign.markingworkflowstatereleased": "Lansat", - "addon.mod_assign.modulenameplural": "Teme", - "addon.mod_assign.multipleteams": "Membru al mai mult de un grup", - "addon.mod_assign.noattempt": "Fără încercare", - "addon.mod_assign.nosubmission": "Nu s-a primit răspuns la această sarcină de lucru", - "addon.mod_assign.noteam": "Nu este un membru al unui grup", - "addon.mod_assign.notgraded": "Fără notă", - "addon.mod_assign.numberofdraftsubmissions": "Draft-uri", - "addon.mod_assign.numberofparticipants": "Participanți", - "addon.mod_assign.numberofsubmissionsneedgrading": "Trebuie să fie notat", - "addon.mod_assign.numberofsubmittedassignments": "Trimis", - "addon.mod_assign.numberofteams": "Grupuri", - "addon.mod_assign.numwords": "{{$a}} cuvinte", - "addon.mod_assign.outof": "{{$a.current}} din {{$a.total}}", - "addon.mod_assign.submission": "Temă trimisă", - "addon.mod_assign.submissioneditable": "Cursanții își pot edita lucrările trimise", - "addon.mod_assign.submissionnoteditable": "Cursanții nu pot să editeze aceastpă lucrare", - "addon.mod_assign.submissionstatus_": "Nu există răspunsuri", - "addon.mod_assign.submissionstatus_draft": "Draft (netrimis)", - "addon.mod_assign.submissionstatus_marked": "Notat", - "addon.mod_assign.submissionstatus_new": "nu există răspunsuri", - "addon.mod_assign.submissionstatus_reopened": "Redeschis", - "addon.mod_assign.submissionteam": "Grup", - "addon.mod_assign.submitassignment": "Trimite răspuns corespunzător sarcinii de lucru", - "addon.mod_assign.submittedearly": "Sarcina de lucru a fost rezolvată {{$a}} mai repede", - "addon.mod_assign.submittedlate": "Sarcina de lucru a fost rezolvată {{$a}} mai târziu", - "addon.mod_assign.timeremaining": "Timp rămas", - "addon.mod_assign.unlimitedattempts": "Nelimitat", - "addon.mod_assign.userwithid": "Utilizatorul cu ID-ul {{id}}", - "addon.mod_assign.viewsubmission": "Vezi răspunsurile", - "addon.mod_assign.wordlimit": "Limită de cuvinte", - "addon.mod_assign_feedback_comments.pluginname": "Comentarii de feedback", - "addon.mod_assign_feedback_editpdf.pluginname": "Adnotați PDF", - "addon.mod_assign_feedback_file.pluginname": "Fișier de feedback", - "addon.mod_assign_submission_comments.pluginname": "Comentarii la lucrare", - "addon.mod_assign_submission_file.pluginname": "Fișier aplicare", - "addon.mod_assign_submission_onlinetext.pluginname": "Trimiteri online ale textului", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Limita de cuvinte pentru această lucrare este {{$a.limit}} cuvinte iar dumneavoastră încercați să trimiteți {{$a.count}} cuvinte. Examinați prezentarea și încercați din nou.", - "addon.mod_book.modulenameplural": "Cărți", - "addon.mod_book.toc": "Cuprins", - "addon.mod_chat.beep": "beep", - "addon.mod_chat.chatreport": "Sesiuni de discuţii", - "addon.mod_chat.currentusers": "Utilizatori logaţi", - "addon.mod_chat.enterchat": "Clic pentru a intra în conversaţie", - "addon.mod_chat.entermessage": "Inserați mesajul dumneavoastră", - "addon.mod_chat.errorwhileconnecting": "A apărut o eroare la conectarea în chat.", - "addon.mod_chat.errorwhilegettingchatdata": "A apărut o eroare la primirea de informații din chat.", - "addon.mod_chat.errorwhilegettingchatusers": "A apărut o eroare la obținerea listei cu utilizatorii din chat.", - "addon.mod_chat.errorwhileretrievingmessages": "A apărut o eroare la primirea mesajelor de pe server.", - "addon.mod_chat.errorwhilesendingmessage": "A apărut o eroare la trimiterea mesajului.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} trimite o atenţionare la toţi utilizatorii!", - "addon.mod_chat.messagebeepsyou": "{{$a}} v-a trimis o atenţionare!", - "addon.mod_chat.messageenter": "{{$a}} a intrat în această cameră de discuţii", - "addon.mod_chat.messageexit": "{{$a}} a ieşit din această cameră de discuţii", - "addon.mod_chat.messages": "Mesaje", - "addon.mod_chat.modulenameplural": "Chats", - "addon.mod_chat.mustbeonlinetosendmessages": "Trebuie să fiți conectat pentru a trimite mesaje", - "addon.mod_chat.nomessages": "Nu a fost trimis încă niciun mesaj", - "addon.mod_chat.send": "Trimis", - "addon.mod_chat.sessionstart": "Următoarea sesiune de chat va începe în {{$a}}", - "addon.mod_chat.viewreport": "Vizualizează sesiuni de discuţii anterioare", - "addon.mod_choice.errorgetchoice": "Eroare în obținerea de date pentru alegere", - "addon.mod_choice.expired": "Ne pare rău, această activitate s-a închis la {{$a}} şi nu mai este disponibilă", - "addon.mod_choice.full": "(plin)", - "addon.mod_choice.modulenameplural": "Alegeri", - "addon.mod_choice.noresultsviewable": "În acest moment rezultatele nu pot fi vizualizate.", - "addon.mod_choice.notopenyet": "Ne pare rău, această activitate nu este disponibilă mai devreme de {{$a}}", - "addon.mod_choice.numberofuser": "Numărul de răspunsuri", - "addon.mod_choice.numberofuserinpercentage": "Procentajul răspunsurilor", - "addon.mod_choice.removemychoice": "Șterge alegerea mea", - "addon.mod_choice.responses": "Răspunsuri", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% dintre utilizatori au ales varianta: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Afișează graficul", - "addon.mod_choice.savemychoice": "Salvează formularul meu de evaluare", - "addon.mod_choice.userchoosethisoption": "Utilizatorii care au ales această opțiune", - "addon.mod_choice.yourselection": "Selecția ta", - "addon.mod_data.addentries": "Adaugă articole", - "addon.mod_data.advancedsearch": "Căutare complexă", - "addon.mod_data.alttext": "Text alternativ", - "addon.mod_data.approve": "Aprobă", - "addon.mod_data.approved": "Aprobat", - "addon.mod_data.ascending": "Crescător", - "addon.mod_data.authorfirstname": "Prenume autor", - "addon.mod_data.authorlastname": "Nume autor", - "addon.mod_data.confirmdeleterecord": "Sunteţi sigur că doriţi să ştergeţi acest articol?", - "addon.mod_data.descending": "Descrescător", - "addon.mod_data.disapprove": "Anulați aprobarea", - "addon.mod_data.emptyaddform": "Nu aţi completat niciun câmp!", - "addon.mod_data.entrieslefttoadd": "Pentru a finaliza această activitate trebuie să mai adăugaţi {{$a.entriesleft}} more entry/entries articole", - "addon.mod_data.entrieslefttoaddtoview": "Pentru a vedea articolele postate de ceilalţi participanţi trebuie să mai adăugaţi {{$a.entrieslefttoview}} articole.", - "addon.mod_data.errormustsupplyvalue": "Trebuie să inserați o valoare aici.", - "addon.mod_data.expired": "Ne pare rău, activitatea s-a închis în {{$a}} și nu mai este disponibilă", - "addon.mod_data.fields": "Câmpuri", - "addon.mod_data.foundrecords": "Au fost găsite următoarele înregistrări: {{$a.num}}/{{$a.max}} (Reset filters)", - "addon.mod_data.latlongboth": "Sunt necesare atât latitudinea, cât și longitudinea.", - "addon.mod_data.menuchoose": "Alegeţi...", - "addon.mod_data.modulenameplural": "Baze de date", - "addon.mod_data.more": "Detalii suplimentare", - "addon.mod_data.nomatch": "Nu s-au găsit articole care să corespundă criteriilor selectate!", - "addon.mod_data.norecords": "Nu s-au găsit articole în baza de date", - "addon.mod_data.notapproved": "Acest articol nu a fost încă aprobat.", - "addon.mod_data.notopenyet": "Ne pare rău, această activitate nu este disponibilă până în {{$a}}", - "addon.mod_data.numrecords": "{{$a}} articole", - "addon.mod_data.other": "Altele", - "addon.mod_data.recordapproved": "Articol aprobat", - "addon.mod_data.recorddeleted": "Articol şters", - "addon.mod_data.recorddisapproved": "Postare neaprobată", - "addon.mod_data.resetsettings": "Resetează filtre", - "addon.mod_data.search": "Căutare", - "addon.mod_data.selectedrequired": "Toate elementele selectate sunt obligatorii", - "addon.mod_data.single": "Afişează unul singur", - "addon.mod_data.timeadded": "Ora la care a fost adăugat", - "addon.mod_data.timemodified": "Ora la care a fost modificat", - "addon.mod_data.usedate": "Include în căutare", - "addon.mod_feedback.analysis": "Analiză", - "addon.mod_feedback.anonymous": "Anonim", - "addon.mod_feedback.anonymous_entries": "Postări anonime", - "addon.mod_feedback.average": "Medie", - "addon.mod_feedback.complete_the_form": "Răspunde la întrebare", - "addon.mod_feedback.completed_feedbacks": "Răspunsuri trimise", - "addon.mod_feedback.feedback_is_not_open": "Feedback-ul nu este disponibil", - "addon.mod_feedback.feedbackopen": "Permite răspunsuri de la", - "addon.mod_feedback.maximal": "maximum", - "addon.mod_feedback.minimal": "minimum", - "addon.mod_feedback.mode": "Mod", - "addon.mod_feedback.modulenameplural": "Feedback", - "addon.mod_feedback.next_page": "Următoarea pagină", - "addon.mod_feedback.not_selected": "Nu este selectat", - "addon.mod_feedback.not_started": "neînceput", - "addon.mod_feedback.numberoutofrange": "Număr în afara intervalului", - "addon.mod_feedback.overview": "Privire generală", - "addon.mod_feedback.page_after_submit": "Pagina după trimitere", - "addon.mod_feedback.preview": "Pre-vizualizează", - "addon.mod_feedback.previous_page": "Pagina precedentă", - "addon.mod_feedback.questions": "Întrebări", - "addon.mod_feedback.response_nr": "Numărul răspunsuluii", - "addon.mod_feedback.responses": "Răspunsuri", - "addon.mod_feedback.save_entries": "Trimiteți răspunsurile", - "addon.mod_feedback.show_entries": "Afișează răspunsuri", - "addon.mod_feedback.started": "început", - "addon.mod_feedback.this_feedback_is_already_submitted": "Ați completat deja această activitate.", - "addon.mod_folder.emptyfilelist": "Nu sunt fișiere disponibile pentru vizualizare.", - "addon.mod_folder.modulenameplural": "Dosare", - "addon.mod_forum.addanewdiscussion": "Adaugă o nouă intervenţie", - "addon.mod_forum.addanewquestion": "Adaugă o întrebare", - "addon.mod_forum.addanewtopic": "Adaugă temă", - "addon.mod_forum.advanced": "Avansat", - "addon.mod_forum.cannotadddiscussion": "Pentru a putea discuta pe acest forum trebuie să fiţi membru al unui grup.", - "addon.mod_forum.cannotadddiscussionall": "Nu aveţi permisiunea de a adăuga o temă de discuţii pentru toţi participanţii.", - "addon.mod_forum.cannotcreatediscussion": "Nu se poate crea discuție nouă", - "addon.mod_forum.couldnotadd": "Intervenţia ta nu a fost publicată datorită unei erori necunoscute", - "addon.mod_forum.couldnotupdate": "Intervenţia ta nu a putut fi actualizată datorită unei erori necunoscute", - "addon.mod_forum.delete": "Şterge", - "addon.mod_forum.deletedpost": "Intervenţia a fost ştearsă", - "addon.mod_forum.deletesure": "Eşti sigur că vrei să ştergi această intervenţie?", - "addon.mod_forum.discussion": "Discuție", - "addon.mod_forum.edit": "Modifică", - "addon.mod_forum.erroremptymessage": "Mesajul nu poate fi necompletat", - "addon.mod_forum.erroremptysubject": "Subiectul nu poate fi necompletat", - "addon.mod_forum.errorgetforum": "A apărut o eroare la obținerea datelor despre forum.", - "addon.mod_forum.errorgetgroups": "Eroare la obținerea setărilor pentru grup.", - "addon.mod_forum.forumnodiscussionsyet": "Nu există subiecte deschise în acest forum.", - "addon.mod_forum.group": "Grup", - "addon.mod_forum.lastpost": "Ultima intervenţie", - "addon.mod_forum.message": "Mesaj", - "addon.mod_forum.modeflatnewestfirst": "Arată replicile liniar, mai întâi cele recente", - "addon.mod_forum.modeflatoldestfirst": "Arată replicile liniar, mai întâi cele vechi", - "addon.mod_forum.modenested": "Arată replicile într-o formă de cuib", - "addon.mod_forum.modulenameplural": "Forumuri", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} discuții", - "addon.mod_forum.numreplies": "{{numreplies}} răspunsuri", - "addon.mod_forum.posttoforum": "Trimite intervenţie pe forum", - "addon.mod_forum.posttomygroups": "Postează o copie în toate grupurile", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.reply": "Răspunde", - "addon.mod_forum.subject": "Subiect", - "addon.mod_forum.unread": "Necitit", - "addon.mod_forum.unreadpostsnumber": "{{$a}} intervenţii necitite", - "addon.mod_forum.yourreply": "Răspunsul dumneavoastră", - "addon.mod_glossary.addentry": "Adaugă o intrare nouă", - "addon.mod_glossary.aliases": "Cuvinte cheie", - "addon.mod_glossary.attachment": "Atașament", - "addon.mod_glossary.browsemode": "Căutați în datele introduse", - "addon.mod_glossary.byalphabet": "Alfabetic", - "addon.mod_glossary.byauthor": "Grupare după autor", - "addon.mod_glossary.bynewestfirst": "Cele mai noi sunt dispuse primele", - "addon.mod_glossary.byrecentlyupdated": "Actualizări recente", - "addon.mod_glossary.bysearch": "Căutare", - "addon.mod_glossary.casesensitive": "Se verifică existenţa majusculelor", - "addon.mod_glossary.categories": "Categorii", - "addon.mod_glossary.concept": "Concept", - "addon.mod_glossary.definition": "Definiție", - "addon.mod_glossary.entrypendingapproval": "Această", - "addon.mod_glossary.entryusedynalink": "Acestui articol i se asociază automat şi obligatoriu un link", - "addon.mod_glossary.errconceptalreadyexists": "Acest concept există deja. În cadrul acestui glosar nu sunt permise articolele duplicate.", - "addon.mod_glossary.errorloadingentries": "A apărut o eroare la încărcarea intrărilor.", - "addon.mod_glossary.errorloadingentry": "A apărut o eroare la încărcarea intrărilor.", - "addon.mod_glossary.errorloadingglossary": "A apărut o eroare la încărcarea glosarului.", - "addon.mod_glossary.fillfields": "Câmpurile \"concept\" şi \"definiţie\" sunt obligatorii", - "addon.mod_glossary.fullmatch": "Doar cuvinte întregi", - "addon.mod_glossary.linking": "Auto-linking", - "addon.mod_glossary.modulenameplural": "Glosare", - "addon.mod_glossary.noentriesfound": "Nu au fost găsite intrări.", - "addon.mod_glossary.searchquery": "Căutare", - "addon.mod_glossary.tagarea_glossary_entries": "Tab-uri vizibile Postări în glosar", - "addon.mod_imscp.modulenameplural": "Conținutul pachetelor IMS", - "addon.mod_imscp.showmoduledescription": "Arată descrierea", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "Răspuns", - "addon.mod_lesson.attempt": "Încercarea cu numărul: {{$a}}", - "addon.mod_lesson.attemptheader": "Încercare", - "addon.mod_lesson.attemptsremaining": "Mai aveţi {{$a}} încercări disponibile", - "addon.mod_lesson.averagescore": "Punctaj mediu obţinut", - "addon.mod_lesson.averagetime": "Timp mediu", - "addon.mod_lesson.branchtable": "Conținut", - "addon.mod_lesson.cannotfinduser": "Eroare: nu s-au putut găsi utilizatorii", - "addon.mod_lesson.clusterjump": "Întrebare ascunsă în cadrul unui cluster", - "addon.mod_lesson.completed": "Finalizat", - "addon.mod_lesson.congratulations": "Felicitări - aţi ajuns la finalul lecţiei", - "addon.mod_lesson.continue": "Mai departe", - "addon.mod_lesson.continuetonextpage": "Continuați la pagina următoare", - "addon.mod_lesson.defaultessayresponse": "Eseul dumneavoastră va fi notat de unul din instructorii cursului.", - "addon.mod_lesson.detailedstats": "Date statistice detaliate", - "addon.mod_lesson.didnotanswerquestion": "Nu a răspuns la această întrebare.", - "addon.mod_lesson.displayofgrade": "Afişarea notei (doar pentru cursanţi)", - "addon.mod_lesson.displayscorewithoutessays": "Punctajul dumneavoastră este de {{$a.score}} (din nota {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Parola nu poate fi lăsată necompletată", - "addon.mod_lesson.enterpassword": "Vă rugăm introduceţi parola:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Nu aţi răspuns la nicio întrebare. Aţi primit 0 puncte la această lecţie.", - "addon.mod_lesson.finish": "Terminat", - "addon.mod_lesson.gotoendoflesson": "Mergi la sfârșitul lecției", - "addon.mod_lesson.grade": "Notă", - "addon.mod_lesson.highscore": "Notă mare", - "addon.mod_lesson.leftduringtimed": "Aţi abandonat o lecţie cronometrată.
                      Vă rugăm apăsaţi Continuare pentru a începe lecţia de la început.", - "addon.mod_lesson.leftduringtimednoretake": "Aţi abandonat o lecţie cronometrată.
                      nu aveţi permisiunea de a continua sau de a începe de la început.", - "addon.mod_lesson.lessonmenu": "Meniu Lecţie", - "addon.mod_lesson.lessonstats": "Statistici lecţie", - "addon.mod_lesson.linkedmedia": "Linked media", - "addon.mod_lesson.loginfail": "Operaţiunea de logare nu a reuşit, vă rugăm mai încercaţi...", - "addon.mod_lesson.lowscore": "Punctaj minim", - "addon.mod_lesson.maximumnumberofattemptsreached": "Numărul maxim de încercări a fost epuizat - se trece la pagina următoare", - "addon.mod_lesson.modulenameplural": "Lecţii", - "addon.mod_lesson.noanswer": "Nu aţi dat niciun răspuns. Vă rugăm mergeţi la pagina precedentă şi daţi un răspuns.", - "addon.mod_lesson.nolessonattempts": "Nu s-a înregistrat nicio încercare de parcurgere a acestei lecţii", - "addon.mod_lesson.notcompleted": "Nu a fost completat.", - "addon.mod_lesson.numberofcorrectanswers": "Numărul de răspunsuri corecte: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Numărul de întrebări la care s-a răspuns: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Aţi răspuns la {{$a.nquestions}} întrebări; (Trebuie să răspundeţi la cel puţin {{$a.minquestions}} întrebări)", - "addon.mod_lesson.ongoingcustom": "Până în acest moment aţi obţinut {{$a.score}} point(s) puncte din {{$a.currenthigh}} point(s) posibile.", - "addon.mod_lesson.or": "SAU", - "addon.mod_lesson.overview": "Privire de ansamblu", - "addon.mod_lesson.preview": "Previzualizare", - "addon.mod_lesson.progresscompleted": "Ați completat {{$a}}% din lecție", - "addon.mod_lesson.question": "Întrebare", - "addon.mod_lesson.reports": "Rapoarte", - "addon.mod_lesson.response": "Răspuns", - "addon.mod_lesson.reviewquestionback": "Da, doresc să mai încerc o dată", - "addon.mod_lesson.reviewquestioncontinue": "Nu, vreau să trec la următoarea întrebare", - "addon.mod_lesson.secondpluswrong": "Nu este corect. Doriţi să mai încercaţi o dată?", - "addon.mod_lesson.teacherongoingwarning": "Punctajul curent este afişat doar cursanţilor. Dacă doriţi să vizualizaţi punctajul curent trebuie să intraţi în contul de cursant.", - "addon.mod_lesson.thatsthecorrectanswer": "Răspuns corect", - "addon.mod_lesson.thatsthewronganswer": "Răspuns incorect", - "addon.mod_lesson.timeremaining": "Timp rămas", - "addon.mod_lesson.timetaken": "Timp necesar", - "addon.mod_lesson.welldone": "Foarte bine!", - "addon.mod_lesson.youhaveseen": "Aţi vizualizat deja mai mult de o pagină din această lecţie.
                      Doriţi să reveniţi la prima pagină pe care aţi văzut-o?", - "addon.mod_lesson.youranswer": "Răspunsul dumneavoastră", - "addon.mod_lesson.yourcurrentgradeisoutof": "În prezent nota dumneavoastră este {{$a.grade}}, nota maximă posibilă este {{$a.total}}.", - "addon.mod_lesson.youshouldview": "Trebuie să răspundeţi la cel puţin {{$a}} întrebări.", - "addon.mod_lti.errorgetlti": "A apărut o eroare la încărcarea modulului de date.", - "addon.mod_lti.errorinvalidlaunchurl": "URLul lansat nu este valid.", - "addon.mod_lti.launchactivity": "Lansați activitatea", - "addon.mod_page.errorwhileloadingthepage": "A apărut o eroare la încărcarea paginii.", - "addon.mod_page.modulenameplural": "Pagini", - "addon.mod_quiz.answercolon": "Răspuns:", - "addon.mod_quiz.attemptfirst": "Prima încercare", - "addon.mod_quiz.attemptlast": "Ultima încercare", - "addon.mod_quiz.attemptnumber": "Încercare", - "addon.mod_quiz.attemptquiznow": "Lansează testul acum", - "addon.mod_quiz.comment": "Comentariu", - "addon.mod_quiz.completedon": "Completat la", - "addon.mod_quiz.confirmclose": "Sunteţi pe cale de a închide această încercare. După închiderea încercării nu veţi mai putea modifica răspunsurile la întrebări.", - "addon.mod_quiz.confirmstartheader": "Chestionar temporizat", - "addon.mod_quiz.continueattemptquiz": "Continuă ultima încercare", - "addon.mod_quiz.continuepreview": "Continuă ultima vizualizare", - "addon.mod_quiz.feedback": "Feedback", - "addon.mod_quiz.grade": "Notează", - "addon.mod_quiz.gradeaverage": "Nota medie", - "addon.mod_quiz.gradehighest": "Nota maximă", - "addon.mod_quiz.grademethod": "Metoda de notare", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Puncte", - "addon.mod_quiz.modulenameplural": "Teste", - "addon.mod_quiz.noquestions": "Nici o întrebare nu a fost adăugată încă", - "addon.mod_quiz.noreviewattempt": "Nu aveți permisiunea să revizuiți această încercere.", - "addon.mod_quiz.notyetgraded": "Nu este notat încă", - "addon.mod_quiz.outof": "{{$a.grade}} din maxim {{$a.maxgrade}} posibil", - "addon.mod_quiz.outofpercent": "{{$a.grade}} din maxim {{$a.maxgrade}} ({{$a.percent}}%) posibil", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Feedback global", - "addon.mod_quiz.preview": "Afişare", - "addon.mod_quiz.previewquiznow": "Afişare test acum", - "addon.mod_quiz.question": "Întrebare", - "addon.mod_quiz.quizpassword": "Parola chestionarului", - "addon.mod_quiz.reattemptquiz": "Încearcă testul din nou", - "addon.mod_quiz.requirepasswordmessage": "Pentru a putea răspunde la acest test aveţi nevoie de parolă", - "addon.mod_quiz.review": "Verificare", - "addon.mod_quiz.showall": "Afişează toate întrebările pe o singură pagină", - "addon.mod_quiz.startedon": "Început la", - "addon.mod_quiz.statefinished": "Terminat", - "addon.mod_quiz.stateinprogress": "În desfășurare", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Trimite tot şi termină", - "addon.mod_quiz.summaryofattempts": "Rezumatul încercărilor anterioare efectuate de dumneavoastră", - "addon.mod_quiz.timeleft": "Timp rămas", - "addon.mod_quiz.timetaken": "Timp luat", - "addon.mod_quiz.yourfinalgradeis": "Nota ta finală la acest test este {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "A apărut o eroare la încărcarea conținutului.", - "addon.mod_resource.modifieddate": "Modificat {{$a}}", - "addon.mod_resource.modulenameplural": "Fișiere", - "addon.mod_resource.openthefile": "Deschideți fisierul", - "addon.mod_resource.uploadeddate": "Încărcat {{$a}}", - "addon.mod_scorm.asset": "Fişier curs", - "addon.mod_scorm.assetlaunched": "Fişier - Afişat", - "addon.mod_scorm.attempts": "Încercări de parcurgere", - "addon.mod_scorm.averageattempt": "Numărul mediu de încercări de parcurgere", - "addon.mod_scorm.browse": "Caută", - "addon.mod_scorm.browsed": "Căutat", - "addon.mod_scorm.browsemode": "Mod Căutare", - "addon.mod_scorm.cannotcalculategrade": "Nota nu a putut fi calculată.", - "addon.mod_scorm.completed": "Finalizat", - "addon.mod_scorm.contents": "Conţinut", - "addon.mod_scorm.dataattemptshown": "Aceste date aparțin încercării cu numărul {{number}}.", - "addon.mod_scorm.enter": "Intră", - "addon.mod_scorm.errorcreateofflineattempt": "A apărut o eroare la crearea încercării offline. Încercați din nou!", - "addon.mod_scorm.errordownloadscorm": "Eroare la descărcarea SCORM cu numele: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Eroare la obținerea de date pentru acest SCORM", - "addon.mod_scorm.errorinvalidversion": "Această aplicație suporta doar versiunea de SCORM 1.2", - "addon.mod_scorm.errornotdownloadable": "Descărcarea pachetelor SCORM este dezactivată pe acest site. Contactați administratorul siteului.", - "addon.mod_scorm.errornovalidsco": "Acest SCORM nu are un SCO vizibil pentru a fi încărcat.", - "addon.mod_scorm.errorpackagefile": "Această aplicație suportă doar fișiere de tip ZIP.", - "addon.mod_scorm.errorsyncscorm": "A apărut o eroare la sincronizare. Vă rugăm să reîncercați.", - "addon.mod_scorm.exceededmaxattempts": "Ați atins numărul maxim de încercări", - "addon.mod_scorm.failed": "Eşuat", - "addon.mod_scorm.firstattempt": "Prima încercare parcurgere", - "addon.mod_scorm.gradeaverage": "Punctaj mediu", - "addon.mod_scorm.gradehighest": "Punctaj maxim", - "addon.mod_scorm.grademethod": "Metodă punctare", - "addon.mod_scorm.gradescoes": "Modul de studiu", - "addon.mod_scorm.gradesum": "Sumă punctaje", - "addon.mod_scorm.highestattempt": "Cea mai completă încercare de parcurgere", - "addon.mod_scorm.incomplete": "Incomplet", - "addon.mod_scorm.lastattempt": "Ultima încercare de parcurgere", - "addon.mod_scorm.modulenameplural": "SCORMs/AICCs", - "addon.mod_scorm.newattempt": "Începe o nouă încercare de parcurgere", - "addon.mod_scorm.noattemptsallowed": "Numărul de încercări permise", - "addon.mod_scorm.noattemptsmade": "Numărul de încercări pe care le-ați făcut", - "addon.mod_scorm.notattempted": "Nu s-a încercat parcurgerea", - "addon.mod_scorm.offlineattemptnote": "Această încercare conține date care nu au fost sincronizate.", - "addon.mod_scorm.offlineattemptovermax": "Această încercare nu poate fi trimisă deoarece a fost depășit numărul maxim de încercări.", - "addon.mod_scorm.organizations": "Organizaţii", - "addon.mod_scorm.passed": "Admis", - "addon.mod_scorm.reviewmode": "Mod Verificare", - "addon.mod_scorm.score": "Punctaj", - "addon.mod_scorm.scormstatusnotdownloaded": "Acest SCORM nu a fost descărcat. Va fi descărcat automat când îl veți deschide.", - "addon.mod_scorm.scormstatusoutdated": "Acest SCORM a fost modificat de la ultima descărcare. Va fi redescărcat automat când îl veți deschide.", - "addon.mod_scorm.suspended": "Suspendat", - "addon.mod_scorm.toc": "TOC", - "addon.mod_scorm.warningofflinedatadeleted": "Unele date ale încercării offline cu numărul {{number}} au fost șterse deoarece nu s-au putut constitui într-o nouă încercare.", - "addon.mod_scorm.warningsynconlineincomplete": "Această încercare nu poate fi sincronizată cu siteul pentru că ultima încercare efectuată nu a fost finalizată. Vă rugăm să o finalizați.", - "addon.mod_survey.cannotsubmitsurvey": "A apărut o eroare la trimiterea sondajului. Vă rugăm să reîncercați.", - "addon.mod_survey.errorgetsurvey": "A apărut o eroare la primirea datelor sondajului.", - "addon.mod_survey.ifoundthat": "Am descoperit că", - "addon.mod_survey.ipreferthat": "Prefer ca", - "addon.mod_survey.modulenameplural": "Sondaje", - "addon.mod_survey.responses": "Răspunsuri", - "addon.mod_survey.results": "Rezultate", - "addon.mod_survey.surveycompletednograph": "Ați completat acest sondaj.", - "addon.mod_url.accessurl": "Accesați adresa URL", - "addon.mod_url.modulenameplural": "URL-uri", - "addon.mod_url.pointingtourl": "Adresa URL a acestei resurse duce la", - "addon.mod_wiki.cannoteditpage": "Nu puteți edita această pagină.", - "addon.mod_wiki.createpage": "Creează pagină", - "addon.mod_wiki.editingpage": "Se editează această pagină '{{$a}}'", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Pagină nouă", - "addon.mod_wiki.newpagetitle": "Un nou titlu de pagină", - "addon.mod_wiki.nocontent": "Nu există conținut pentru această pagină", - "addon.mod_wiki.notingroup": "Nu este în grup", - "addon.mod_wiki.pageexists": "Această pagină există deja.", - "addon.mod_wiki.pagename": "Nume pagină", - "addon.mod_wiki.tagarea_wiki_pages": "pagini wiki", - "addon.mod_workshop.alreadygraded": "Notat deja", - "addon.mod_workshop.assess": "Evaluare", - "addon.mod_workshop.conclusion": "Concluzii", - "addon.mod_workshop.editsubmission": "Modifică propunere", - "addon.mod_workshop.gradinggrade": "Grading Grade", - "addon.mod_workshop.modulenameplural": "Seminarii", - "addon.mod_workshop.notoverridden": "Nu este suprascris", - "addon.mod_workshop.reassess": "Re-evaluare", - "addon.mod_workshop.receivedgrades": "Note primite", - "addon.mod_workshop.submissionattachment": "Atașament", - "addon.mod_workshop.submissioncontent": "Conținutul lucrării", - "addon.mod_workshop.submissiontitle": "Titlu", - "addon.mod_workshop.yourgrades": "Notele dumneavoastră", - "addon.mod_workshop.yoursubmission": "Lucrările dumneavoastră", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Comentariu pentru {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Notă pentru {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspect {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Trebuie să selectați o notă pentru acest aspect", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Comentariu pentru {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspect {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Comentariu pentru {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Notă pentru {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Afirmarea {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Criteriul {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Trebuie să selectați unul dintre aceste elemente", - "addon.notes.addnewnote": "Adaugă observaţie", - "addon.notes.coursenotes": "Observaţii curs", - "addon.notes.deleteconfirm": "Şterg această observaţie?", - "addon.notes.eventnotecreated": "Observație creată", - "addon.notes.eventnotedeleted": "Observație ștearsă", - "addon.notes.nonotes": "Încă nu există astfel de observaţii", - "addon.notes.note": "Observaţie", - "addon.notes.notes": "Observaţii", - "addon.notes.personalnotes": "Observaţii personale", - "addon.notes.publishstate": "Context", - "addon.notes.sitenotes": "Observaţii sit", - "addon.notes.userwithid": "Utilizatorul cu ID-ul {{id}}", - "addon.notifications.errorgetnotifications": "A apărut o eroare la verificarea notificărilor", - "addon.notifications.markallread": "Marchează tot ca fiind citit", - "addon.notifications.notifications": "Notificări", - "addon.notifications.therearentnotificationsyet": "Nu sunt notificări", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Emiratele Arabe Unite", - "assets.countries.AF": "Afghanistan", - "assets.countries.AG": "Antigua şi Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albania", - "assets.countries.AM": "Armenia", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarctica", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Samoa Americană", - "assets.countries.AT": "Austria", - "assets.countries.AU": "Australia", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Insulele Åland", - "assets.countries.AZ": "Azerbaijan", - "assets.countries.BA": "Bosnia și Herțegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgia", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaria", - "assets.countries.BH": "Bahrain", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Sfântul Barthélemy", - "assets.countries.BM": "Insulele Bermude", - "assets.countries.BN": "Brunei Darussalam", - "assets.countries.BO": "Bolivia", - "assets.countries.BQ": "Bonaire, Sint Eustatius și Saba", - "assets.countries.BR": "Brazilia", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Insula Bouvet", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Belarus", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Canada", - "assets.countries.CC": "Insulele Cocos", - "assets.countries.CD": "Republica Democrată Congo", - "assets.countries.CF": "Republica Centrafricană", - "assets.countries.CG": "Congo", - "assets.countries.CH": "Elveția", - "assets.countries.CI": "Coasta de Fildeş", - "assets.countries.CK": "Insulele Cook", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Camerun", - "assets.countries.CN": "China", - "assets.countries.CO": "Columbia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Cuba", - "assets.countries.CV": "Capul Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Insula Crăciunului", - "assets.countries.CY": "Cipru", - "assets.countries.CZ": "Cehia", - "assets.countries.DE": "Germania", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Danemarca", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Republica Dominicană", - "assets.countries.DZ": "Algeria", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estonia", - "assets.countries.EG": "Egipt", - "assets.countries.EH": "Sahara de Vest", - "assets.countries.ER": "Eritreea", - "assets.countries.ES": "Spania", - "assets.countries.ET": "Etiopia", - "assets.countries.FI": "Finlanda", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Insulele Malvine", - "assets.countries.FM": "Statele Federale ale Microneziei", - "assets.countries.FO": "Insulele Feroe", - "assets.countries.FR": "Franța", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Marea Britanie", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgia", - "assets.countries.GF": "Guiana Franceză", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Groenlanda", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guineea", - "assets.countries.GP": "Guadelupa", - "assets.countries.GQ": "Guineea Ecuatorială", - "assets.countries.GR": "Grecia", - "assets.countries.GS": "Insulele Georgia de Sud și Sandwich de Sud", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guineea-Bissau", - "assets.countries.GY": "Guiana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Insula Heard și Insulele McDonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Croația", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Ungaria", - "assets.countries.ID": "Indonezia", - "assets.countries.IE": "Irlanda", - "assets.countries.IL": "Israel", - "assets.countries.IM": "Insula Man", - "assets.countries.IN": "India", - "assets.countries.IO": "Teritoriul Britanic din Oceanul Indian", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran", - "assets.countries.IS": "Islanda", - "assets.countries.IT": "Italia", - "assets.countries.JE": "Insula Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Iordania", - "assets.countries.JP": "Japonia", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kârgâzstan", - "assets.countries.KH": "Cambodgia", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Insulele Comore", - "assets.countries.KN": "Sfântul Kitts și Nevis", - "assets.countries.KP": "Coreea de Nord", - "assets.countries.KR": "Coreea de Sud", - "assets.countries.KW": "Kuweit", - "assets.countries.KY": "Insulele Cayman", - "assets.countries.KZ": "Kazahstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Liban", - "assets.countries.LC": "Sfânta Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Lituania", - "assets.countries.LU": "Luxemburg", - "assets.countries.LV": "Letonia", - "assets.countries.LY": "Libia", - "assets.countries.MA": "Maroc", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Republica Moldova", - "assets.countries.ME": "Muntenegru", - "assets.countries.MF": "Sfântul Martin", - "assets.countries.MG": "Madagascar", - "assets.countries.MH": "Insulele Marshall", - "assets.countries.MK": "Macedonia", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongolia", - "assets.countries.MO": "Macao", - "assets.countries.MP": "Insulele Mariane de Nord", - "assets.countries.MQ": "Martinica", - "assets.countries.MR": "Mauritania", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Maurițius", - "assets.countries.MV": "Maldive", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexic", - "assets.countries.MY": "Malaezia", - "assets.countries.MZ": "Mozambic", - "assets.countries.NA": "Namibia", - "assets.countries.NC": "Noua Caledonie", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Insula Norfolk", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Olanda", - "assets.countries.NO": "Norvegia", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Noua Zeelandă", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Polinezia Franceză", - "assets.countries.PG": "Papua Noua Guinee", - "assets.countries.PH": "Filipine", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polonia", - "assets.countries.PM": "Sfinții Pierre și Miquelon", - "assets.countries.PN": "Insulele Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Teritorile Ocupate ale Palestinei", - "assets.countries.PT": "Portugalia", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "România", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Rusia", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Arabia Saudită", - "assets.countries.SB": "Insulele Solomon", - "assets.countries.SC": "Seychelles", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Suedia", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "Sfânta Elena", - "assets.countries.SI": "Slovenia", - "assets.countries.SJ": "Insulele Svalbard și Jan Mayen", - "assets.countries.SK": "Slovacia", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Sudanul de Sud", - "assets.countries.ST": "Sao Tome și Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (zona olandeză)", - "assets.countries.SY": "Siria", - "assets.countries.SZ": "Swaziland", - "assets.countries.TC": "Insulele Turks și Caicos", - "assets.countries.TD": "Ciad", - "assets.countries.TF": "Teritoriile australe și antarctice franceze", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tailanda", - "assets.countries.TJ": "Tadjikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timorul de Est", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunisia", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turcia", - "assets.countries.TT": "Trinidad-Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzania", - "assets.countries.UA": "Ucraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Teritoriile Insulare ale Statelor Unite ale Americii", - "assets.countries.US": "Statele Unite ale Americii", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Vatican", - "assets.countries.VC": "Sfântul Vicent și Grenadines", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Insulele Virgine Britanice", - "assets.countries.VI": "Insulele Virgine Americane", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis și Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Afirca de Sud", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "ebook EPUB", - "assets.mimetypes.application/msword": "Document Word", - "assets.mimetypes.application/pdf": "Document PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Backup Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Foaie de calcul Excel", - "assets.mimetypes.application/vnd.ms-powerpoint": "Prezentare Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Șablon OpenDocument Spreadsheet", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Prezentare Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Sildeshow Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Foaie Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Șablon Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Document Word", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Document iWork Pages", - "assets.mimetypes.application/x-javascript": "Sursă JavaScript", - "assets.mimetypes.application/x-mspublisher": "Document Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Animație Flash", - "assets.mimetypes.application/xhtml_xml": "Document XHTML", - "assets.mimetypes.archive": "Arhivă ({{$a.EXT}})", - "assets.mimetypes.audio": "Fișier audio ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Fișier", - "assets.mimetypes.group:audio": "Fișiere audio", - "assets.mimetypes.group:sourcecode": "Codul sursă", - "assets.mimetypes.group:video": "Fișiere video", - "assets.mimetypes.group:web_file": "Fișiere web", - "assets.mimetypes.image": "Imagine ({{$a.MIMETYPE2}})", - "assets.mimetypes.text/csv": "Valori separate prin virgulă", - "assets.mimetypes.text/html": "Document HTML", - "assets.mimetypes.text/plain": "fișier text", - "assets.mimetypes.text/rtf": "Document RTF", - "assets.mimetypes.text/vtt": "Web Video Text Track", - "assets.mimetypes.video": "Fișier video ({{$a.EXT}})", - "core.accounts": "Conturi", - "core.add": "Adaugă", - "core.agelocationverification": "Verificarea vârstei și a locației", - "core.ago": "în urmă cu {{$a}}", - "core.all": "Toate", - "core.allgroups": "Toate grupurile", - "core.allparticipants": "Toţi participanţii", - "core.answer": "Răspunde", - "core.answered": "S-a răspuns", - "core.areyousure": "Ești sigur?", - "core.back": "Înapoi", - "core.block.blocks": "Blocuri", - "core.cancel": "Anulează", - "core.cannotconnect": "A apărut o eroare le conectare: verificați dacă ați scris corect adresa URL căutată și dacă siteul folosește aplicația Moodle {{$a}}", - "core.cannotdownloadfiles": "Descărcarea de fișiere este dezactivată pentru serviciul mobil. Contactați administratorul siteului.", - "core.category": "Categorie", - "core.choose": "Alege", - "core.choosedots": "Alege...", - "core.clearsearch": "Curățați căutările", - "core.clicktohideshow": "Click pentru maximizare sau minimizare", - "core.clicktoseefull": "Apăsați pentru a vedea întregul conținut", - "core.close": "Închide", - "core.comments": "Comentarii", - "core.comments.addcomment": "Adaugă un comentariu...", - "core.comments.comments": "Comentarii", - "core.comments.commentscount": "Comentarii ({{$a}})", - "core.comments.deletecommentbyon": "Șterge comentariul postat de {{$a.user}} în {{$a.time}}", - "core.comments.eventcommentcreated": "Comentariu creat", - "core.comments.eventcommentdeleted": "Comentariu șters", - "core.comments.nocomments": "Nu sunt comentarii", - "core.comments.savecomment": "Salvează comentariu", - "core.commentscount": "Comentarii ({{$a}})", - "core.completion-alt-auto-fail": "Finalizat: {{$a}} (nu a obținut notă de trecere)", - "core.completion-alt-auto-n": "Nu s-a finalizat: {{$a}}", - "core.completion-alt-auto-n-override": "Necompletat: {{$a.modname}} (set by {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Finalizat: {{$a}} (s-a obținut notă de trecere)", - "core.completion-alt-auto-y": "Finalizat: {{$a}}", - "core.completion-alt-auto-y-override": "Completat: {{$a.modname}} (set by {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Necompletat: {{$a}}. Selectați pentru a-l seta ca fiind completat.", - "core.completion-alt-manual-n-override": "Nu este complet: {{$a.modname}} (set by {{$a.overrideuser}}). Selectați pentru a-l seta ca fiind complet.", - "core.completion-alt-manual-y": "Completat: {{$a}}. Selectați pentru a-l seta ca fiind necompletat.", - "core.confirmdeletefile": "Sunteți sigur că doriți să ștergeți acest fișier?", - "core.confirmopeninbrowser": "Doriți să deschideți într-un browser?", - "core.considereddigitalminor": "Nu aveți vârsta necesară pentru a putea crea un cont pe acest site.", - "core.content": "Conţinut", - "core.contentlinks.chooseaccount": "Alegeți contul", - "core.contentlinks.chooseaccounttoopenlink": "Alegeți contul pentru a putea deschide linkul.", - "core.contentlinks.confirmurlothersite": "acest link aparține unui alt site. Doriți să îl deschideți?", - "core.contentlinks.errornoactions": "Nu a fost găsită o aplicație pentru a deschide acest link.", - "core.contentlinks.errornosites": "Nu a fost găsit niciun site pentru acest link.", - "core.continue": "Continuă", - "core.course": "Curs", - "core.course.allsections": "Toate secțiunile", - "core.course.confirmdownload": "Porniți o descărcare de {{size}}. Sunteți sigur ca doriți să continuați?", - "core.course.confirmdownloadunknownsize": "Nu putem calcula dimensiunea fișierului pe care doriți să îl descărcați. Sunteți sigur că doriți să descărcați?", - "core.course.contents": "Conținut", - "core.course.couldnotloadsectioncontent": "Nu se poate încărca conținutul acestei secțiuni, încercați mai târziu.", - "core.course.couldnotloadsections": "Nu se pot încărca secțiunile, încercați mai târziu.", - "core.course.coursesummary": "Sumar curs", - "core.course.errordownloadingsection": "A apărut o eroare la descărcarea secțiunii.", - "core.course.errorgetmodule": "A apărut o eroare la preluarea modului de date.", - "core.course.hiddenfromstudents": "Ascuns de cursanţi", - "core.course.hiddenoncoursepage": "Este disponibil, dar nu este afișat pe pagina cursului", - "core.course.nocontentavailable": "Pentru moment nu există conținut.", - "core.course.overriddennotice": "Nota finală obţinută de dumneavoastră pentru această activitate a fost ajustată manual.", - "core.course.sections": "Secţiuni", - "core.coursedetails": "Detalii curs", - "core.courses.allowguests": "Acest curs permite accesul vizitatorilor", - "core.courses.availablecourses": "Cursuri disponibile", - "core.courses.categories": "Categorii de cursuri", - "core.courses.confirmselfenrol": "Sunteți sigur/ă că doriți să vă înregistrați la acest curs?", - "core.courses.courses": "Cursuri", - "core.courses.enrolme": "Înregistrează-mă", - "core.courses.errorloadcourses": "A apărut o eroare la încărcarea cursurilor.", - "core.courses.errorsearching": "A apărut o eroare în procesul de căutare.", - "core.courses.errorselfenrol": "A apărut o eroare în procesul de auto-înregistrare.", - "core.courses.filtermycourses": "Filtrează cursurile", - "core.courses.frontpage": "Pagina principală", - "core.courses.ignore": "Ignoră", - "core.courses.mycourses": "Cursurile mele", - "core.courses.mymoodle": "Dashboard", - "core.courses.nocourses": "Nicio informație legată de cursuri este disponibilă", - "core.courses.nocoursesyet": "Nu s-au găsit cursuri în această categorie", - "core.courses.nosearchresults": "Nu există rezultate", - "core.courses.notenroled": "Nu sunteți înregistrat în acest curs", - "core.courses.notenrollable": "Nu vă puteți auto-înregistra la acest curs.", - "core.courses.password": "Parola de înregistrare", - "core.courses.paymentrequired": "Acest curs necesită plata unei taxe pentru acces.", - "core.courses.paypalaccepted": "Se acceptă plăţi PayPal", - "core.courses.reload": "Reîncarcă", - "core.courses.search": "Caută", - "core.courses.searchcourses": "Caută cursuri", - "core.courses.searchcoursesadvice": "Puteți folosi butonul pentru căutarea cursurilor pentru a cauta și accesa ca vizitator sau pentru a vă auto-înregistra la acele cursuri care permit aceste opțiuni.", - "core.courses.selfenrolment": "Auto înscriere", - "core.courses.sendpaymentbutton": "Trimite plată prin PayPal", - "core.courses.show": "Afișați acest curs", - "core.courses.totalcoursesearchresults": "Totalul cursurilor: {{$a}}", - "core.date": "Dată", - "core.day": "zi", - "core.days": "zile", - "core.decsep": ",", - "core.defaultvalue": "({{$a}}) implicit", - "core.delete": "Şterge", - "core.deleteduser": "Utilizator eliminat", - "core.deleting": "Se șterge", - "core.description": "Descriere", - "core.dfdayweekmonth": "zzz, Z LLL", - "core.dflastweekdate": "zzz", - "core.dfmediumdate": "LLL", - "core.dftimedate": "o[:]mm A", - "core.digitalminor_desc": "Pentru a crea un cont pe acest site, părintele/tutorele dumneavoastră trebuie să contacteze următoarea persoană.", - "core.displayoptions": "Opțiuni pentru afișare", - "core.done": "Terminat", - "core.download": "Descarcă", - "core.downloading": "Se descarcă", - "core.edit": "Editează", - "core.editor.autosavesucceeded": "Draft salvat", - "core.editor.bold": "Bold", - "core.editor.clear": "Șterge formatarea", - "core.editor.h3": "Titlu (mare)", - "core.editor.h4": "Titlu (mediu)", - "core.editor.h5": "Titlu (mic)", - "core.editor.italic": "Italic", - "core.editor.orderedlist": "Listă ordonată", - "core.editor.p": "Paragraf", - "core.editor.strike": "Strike through", - "core.editor.textrecovered": "O versiune draft a acestui text a fost restablit în mod automat.", - "core.editor.underline": "Subliniază", - "core.editor.unorderedlist": "Listă neordonată", - "core.error": "Eroare", - "core.errorchangecompletion": "A apărut o eroare în timpul schimbării nivelului de completare a cursului. Încercați din nou!", - "core.errordownloading": "A apărut o eroare la descărcarea fișierului.", - "core.errordownloadingsomefiles": "A apărut o eroare la descărcarea fișierelor modulului. Unele fișiere pot lipsi.", - "core.errorinvalidresponse": "Răspuns nevalid. Contactați administratorul siteului dacă această eroare persistă.", - "core.erroropenfilenoapp": "A apărut o eroare la deschiderea fișierului: nu s-a găsit nicio aplicație pentru a deschide acest tip de fișier.", - "core.erroropenfilenoextension": "A apărut o eroare la deschiderea fișierului: acest fișier nu are o extensie valida.", - "core.erroropenpopup": "Această activitate încearcă să deschidă o fereastră popup, acțiune ce nu este suportat de aplicație.", - "core.explanationdigitalminor": "Aceste informații sunt necesare pentru a determina dacă vârsta dumneavoastră depășește vârsta la care vă puteți da consimțământul online. Aceasta este vârsta la care o persoană poate consimți la termeni și condiții, iar datele sale sunt stocate și prelucrate legal.", - "core.favourites": "Evidențiat", - "core.filename": "Nume fișier", - "core.filenotfound": "Fișierul nu a fost găsit, îmi pare rău.", - "core.fileuploader.addfiletext": "Adăugați fișier", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Camera WEB", - "core.fileuploader.confirmuploadfile": "Sunteți pe cale să încărcați {{dimensiune}}. Sunteți sigur ca doriți să continuați?", - "core.fileuploader.errorcapturingaudio": "Eroare la înregistrarea audio.", - "core.fileuploader.errorcapturingimage": "Eroare la realizarea capturii foto.", - "core.fileuploader.errorcapturingvideo": "Eroare la înregistrarea video.", - "core.fileuploader.errorgettingimagealbum": "Eroare la obținerea imaginii din album.", - "core.fileuploader.errormustbeonlinetoupload": "Pentru a putea încărca fișiere trebuie să fiți conectat la o rețea de date.", - "core.fileuploader.errornoapp": "Nu aveți instalată o aplicație dedicată realizării acestei operațiuni.", - "core.fileuploader.errorreadingfile": "Eroare la citirea fișierului.", - "core.fileuploader.errorwhileuploading": "A apărut o eroare în timpul încărcării fișierului.", - "core.fileuploader.file": "Fișier", - "core.fileuploader.filesofthesetypes": "Tipuri de fișiere acceptate:", - "core.fileuploader.fileuploaded": "Fișierul a fost încărcat cu succes.", - "core.fileuploader.invalidfiletype": "{{$a}} tipul de fișier nu poate fi acceptat.", - "core.fileuploader.more": "Detalii suplimentare", - "core.fileuploader.photoalbums": "Albume foto", - "core.fileuploader.readingfile": "Se citește fișierul", - "core.fileuploader.uploadafile": "Încărcați un fișier", - "core.fileuploader.uploading": "Se încarcă", - "core.fileuploader.video": "Video", - "core.filter": "Filtru", - "core.folder": "Folder", - "core.forcepasswordchangenotice": "Trebui să vă schimbaţi parola pentru a putea continua.", - "core.fulllistofcourses": "Toate cursurile", - "core.grades.average": "Media", - "core.grades.badgrade": "Nota furnizată nu este corectă", - "core.grades.contributiontocoursetotal": "Contribuția la totalul cursului", - "core.grades.feedback": "Feedback", - "core.grades.grade": "Notă", - "core.grades.gradeitem": "Element notă", - "core.grades.grades": "Note", - "core.grades.lettergrade": "Notă sub formă de literă", - "core.grades.nogradesreturned": "Nu au fost returnate note", - "core.grades.nooutcome": "fără rezultat", - "core.grades.percentage": "Procentaj", - "core.grades.range": "Interval", - "core.grades.rank": "Rang", - "core.grades.weight": "pondere", - "core.group": "Grup", - "core.groupsseparate": "Separă grupuri", - "core.groupsvisible": "Grupuri vizibile", - "core.h5p.additionallicenseinfo": "Orice informație suplimentară despre licență", - "core.h5p.author": "Autor", - "core.h5p.authorcomments": "Comentariile autorului", - "core.h5p.authorname": "Numele autorului", - "core.h5p.authorrole": "Rolul autorului", - "core.h5p.by": "de către", - "core.h5p.cancellabel": "Anulează", - "core.h5p.changedby": "Modificat de către", - "core.h5p.close": "Închide", - "core.h5p.confirmdialogheader": "Confirmă acțiunea", - "core.h5p.confirmlabel": "Confirmă", - "core.h5p.connectionReestablished": "Conexiune restabilită.", - "core.h5p.contenttype": "Tipul de conținut", - "core.h5p.copyrightinfo": "Informații despre copyright", - "core.h5p.copyrightstring": "Copyright", - "core.h5p.disablefullscreen": "Dezactivați fullscreen", - "core.h5p.download": "Descarcă", - "core.h5p.editor": "Editor", - "core.h5p.fullscreen": "Fullscreen", - "core.h5p.license": "Licență", - "core.h5p.licenseCC010U": "CC0 1.0 Universal", - "core.h5p.licenseCC10": "1.0 Generic", - "core.h5p.licenseCC20": "2.0 Generic", - "core.h5p.licenseCC25": "2.5 Generic", - "core.h5p.licenseCC30": "3.0 Unported", - "core.h5p.licenseCC40": "4.0 International", - "core.h5p.licenseV1": "Versiunea 1", - "core.h5p.licenseV2": "Versiunea 2", - "core.h5p.licenseV3": "Versiunea 3", - "core.h5p.offlineDialogRetryButtonLabel": "Reîncercați acum", - "core.h5p.showless": "Afișează mai puțin", - "core.h5p.showmore": "Afișează mai mult", - "core.h5p.size": "Dimensiune", - "core.h5p.title": "Titlu", - "core.h5p.year": "An", - "core.h5p.years": "An(i)", - "core.help": "Ajutor", - "core.hide": "Ascunde", - "core.hour": "oră", - "core.hours": "ore", - "core.humanreadablesize": "{{mărime}} {{unitate}}", - "core.image": "Imagine", - "core.imageviewer": "Vizualizator pentru imagini", - "core.info": "Informaţii", - "core.invalidformdata": "Date din formular incorecte", - "core.labelsep": ":", - "core.lastaccess": "Ultima accesare", - "core.lastmodified": "Ultima modificare", - "core.layoutgrid": "Grilă", - "core.list": "Listă", - "core.listsep": ";", - "core.loading": "Se încarcă", - "core.location": "Locaţie", - "core.login.auth_email": "Auto-înscriere pe baza email-ului", - "core.login.authenticating": "Autentificare", - "core.login.cancel": "Anulează", - "core.login.changepassword": "Schimbă parola", - "core.login.confirmdeletesite": "Sunteți sigur că doriți sa ștergeți siteul {{sitename}}?", - "core.login.connect": "Conectare!", - "core.login.connecttomoodle": "Conectare la Moodle", - "core.login.createaccount": "Creează noul meu cont", - "core.login.createuserandpass": "Alege un nume de utilizator şi o parolă", - "core.login.credentialsdescription": "Va rugăm să introduceți userul și parola dumneavoastră pentru a vă conecta", - "core.login.emailconfirmsent": "

                      Un email a fost trimis la adresa Dvs. {{$a}}

                      \n

                      Acest mesaj conţine instrucţiuni despre finalizarea înregistrării.

                      \n

                      În cazul în care întâmpinaţi dificultăţi contactaţi administratorul site-ului.

                      ", - "core.login.emailconfirmsentsuccess": "S-a trimis mail-ul de confirmare", - "core.login.erroraccesscontrolalloworigin": "Operațiune Cross-Origin pe care încercați să o efectuați a fost respinsă. Verificați la https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "A apărut o eroare în timpul ștergerii acestui site. Încercați din nou!", - "core.login.errorupdatesite": "A apărut o eroare în timpul actualizării tokenului pentru acest site.", - "core.login.firsttime": "Este prima oară când accesaţi această pagină?", - "core.login.forcepasswordchangenotice": "Trebui să vă schimbaţi parola pentru a putea continua.", - "core.login.forgotten": "Aţi uitat numele de utilizator sau parola?", - "core.login.help": "Ajutor", - "core.login.helpmelogin": "

                      Există mii de siteuri Moodle în întreaga lume. Această aplicație vă poate conecta doar la acele siteuri ce oferă accesul pentru aplicațiile mobile.

                      Dacă nu vă puteți conecta la siteul Moodle va trebui să contactați un administrator al acelui site și să îi indicați să citească http://docs.moodle.org/en/Mobile_app

                      Pentru a testa aplicația pe un site Moodle demonstrativ introduceți teacher sau student în câmpurile din siteul URL site și apăsați butonul Adaugă.

                      ", - "core.login.instructions": "Instrucţiuni", - "core.login.invalidaccount": "Vă rugă să verificați datele de conectare sau rugați administratorul siteului sa verifice configurația siteului.", - "core.login.invaliddate": "Dată nevalidă", - "core.login.invalidemail": "Adresă de email incorectă", - "core.login.invalidmoodleversion": "Versiunea Moodle este invalidă. Versiunea minimă este {{$a}}", - "core.login.invalidsite": "Adresa URL este invalidă.", - "core.login.invalidurl": "URL specificat nevalid", - "core.login.localmobileunexpectedresponse": "Verificarea Moodle Mobile Additional Features a returnat un răspuns neașteptat. veți fi autentificat folosind serviciul standard.", - "core.login.login": "Autentificare", - "core.login.loginbutton": "Logat!", - "core.login.logininsiterequired": "Trebuie sa vă logați pe site într-un browser.", - "core.login.loginsteps": "Pentru a avea acces complet pe acest site, va trebui mai întâi să vă creaţi un cont.", - "core.login.missingemail": "Lipseşte adresa de email", - "core.login.missingfirstname": "Lipseşte prenumele", - "core.login.missinglastname": "Lipseşte numele de familie", - "core.login.mobileservicesnotenabled": "Serviciile mobile nu sunt activate pe siteul dumneavoastră. Vă rugăm să contactați administratorul siteului dacă considerați necesară activarea accesului mobil.", - "core.login.mustconfirm": "Trebuie să confirmaţi autentificarea", - "core.login.newaccount": "Cont nou", - "core.login.notloggedin": "Trebuie să fiți logat.", - "core.login.password": "Parolă", - "core.login.passwordforgotten": "Am uitat parola", - "core.login.passwordforgotteninstructions2": "Pentru resetarea parolei, introduceţi mai jos numele Dvs. de utilizator sau adresa Dvs. de emai. Dacă vă vom găsi în baza de date, vă vom trimite un email cu instrucţiuni referitoare la recăpătarea accesului.", - "core.login.passwordrequired": "Este necesară introducerea parolei", - "core.login.policyaccept": "Înţeleg şi sunt de acord", - "core.login.policyagree": "Pentru a continua să folosiţi acest site, trebuie să fiţi de acord cu termenii de utilizare specificaţi. Sunteţi de acord?", - "core.login.policyagreement": "Condiţiile de utilizare ale site-ului", - "core.login.policyagreementclick": "Legătură la condiţiile de utilizare ale site-ului", - "core.login.potentialidps": "Autentificare folosind contul dvs. de pe:", - "core.login.profileinvaliddata": "Valoare incorectă", - "core.login.reconnect": "Reconectare", - "core.login.reconnectdescription": "Tokenul dumneavoastră de autentificare este invalid sau a expirat, trebuie să vă reconectați pe site.", - "core.login.reconnectssodescription": "Tokenul dumneavoastră de autentificare este invalid sau a expirat, trebuie să vă reconectați pe site. Logați-vă folosind un browser.", - "core.login.resendemail": "Retrimite email", - "core.login.security_question": "Întrebare de securitate", - "core.login.selectacountry": "Selectează o ţară", - "core.login.siteaddress": "Adresa siteului", - "core.login.siteinmaintenance": "Siteul dumneavoastră se află în modul de întreținere", - "core.login.siteurl": "Adresă URL", - "core.login.siteurlrequired": "Este necesară adresa URL a siteului, de exemplu http://www.yourmoodlesite.abc sau https://www.yourmoodlesite.efg", - "core.login.startsignup": "Creează cont", - "core.login.supplyinfo": "Detalii suplimentare", - "core.login.username": "Utilizator", - "core.login.usernameoremail": "Completaţi numele de utilizator sau adresa de email", - "core.login.usernamerequired": "Este necesar numele de utilizator", - "core.login.usernotaddederror": "Utilizatorul nu a fost adăugat - eroare", - "core.login.webservicesnotenabled": "Serviciul Web nu este activat pe siteul dumneavoastră. Va rugăm să contactați administratorul siteului dumneavoastră dacă considerați ca ar trebui activat accesul pentru mobil.", - "core.lostconnection": "Tokenul pentru autentificare este invalid sau a expirat; trebuie să va reconectați!", - "core.mainmenu.changesite": "Schimbați siteul", - "core.mainmenu.help": "Ajutor", - "core.mainmenu.logout": "Delogare", - "core.mainmenu.website": "Website", - "core.maxsizeandattachments": "Dimensiunea maximă pentru fișierele noi: {{$a.size}}, atașamente maxime: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Diverse", - "core.mod_assign": "Lucrare", - "core.mod_assignment": "Temă (2.2)", - "core.mod_book": "Carte", - "core.mod_chat": "Chat", - "core.mod_choice": "Alegere", - "core.mod_data": "Bază de date", - "core.mod_database": "Bază de date", - "core.mod_feedback": "Feedback", - "core.mod_file": "Fişier", - "core.mod_folder": "Dosar", - "core.mod_forum": "Forum", - "core.mod_glossary": "Glosar", - "core.mod_ims": "Conținutul pachetului IMS", - "core.mod_imscp": "Conținutul pachetului IMS", - "core.mod_label": "Etichetă", - "core.mod_lesson": "Lecţie", - "core.mod_page": "Pagină", - "core.mod_quiz": "Test", - "core.mod_resource": "Resursă", - "core.mod_scorm": "Pachet SCORM", - "core.mod_survey": "Sondaj", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Seminar", - "core.moduleintro": "Descriere", - "core.more": "mai mult", - "core.mygroups": "Grupurile mele", - "core.name": "Nume", - "core.networkerrormsg": "Rețea de date inexistentă sau nefuncțională", - "core.never": "Niciodată", - "core.next": "Următorul", - "core.no": "Nu", - "core.nocomments": "Nu sunt comentarii", - "core.nograde": "Nicio notă", - "core.none": "Niciunul", - "core.nopermissions": "Ne pare rău, dar în acest moment nu aveţi permisiunea să realizaţi această operaţiune ({{$a}})", - "core.noresults": "Niciun rezultat", - "core.noselection": "Nu există nicio selecție", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Acest profil nu este disponibil deoarece utilizatorul nu s-a înscris încă la acest curs.", - "core.notice": "Notificare", - "core.notingroup": "Ne pare rău, dar trebuie să faceţi parte dintr-un grup pentru a putea vizualiza această activitate.", - "core.now": "acum", - "core.numwords": "{{$a}} cuvinte", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Apăsați aici pentru a vizualiza imaginea la dimensiunea întreagă", - "core.openinbrowser": "Deschideți în browser", - "core.othergroups": "Alte grupuri", - "core.pagea": "Pagina {{$a}}", - "core.paymentinstant": "Folosiţi butonul de mai jos pentru a efectua plata şi a vă înscrie, totul în doar câteva minute!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pictureof": "Imaginea {{$a}}", - "core.previous": "Precedent", - "core.proceed": "Începeți", - "core.pulltorefresh": "Trageți în jos pentru actualizare", - "core.question.answer": "Răspuns", - "core.question.answersaved": "Răspuns salvat", - "core.question.certainty": "Certitudine", - "core.question.complete": "Complet", - "core.question.correct": "Corect", - "core.question.feedback": "Feedback", - "core.question.incorrect": "Incorect", - "core.question.information": "Informații", - "core.question.invalidanswer": "Răspuns incomplet", - "core.question.notanswered": "Nu a primit răspuns", - "core.question.notyetanswered": "Nu a primit răspuns încă", - "core.question.partiallycorrect": "Parțial corect", - "core.question.questionno": "Întrebare {{$a}}", - "core.question.requiresgrading": "Trebuie să fie notată", - "core.quotausage": "Ați folosit {{$a.used}} din limita de {{$a.total}}.", - "core.rating.aggregateavg": "Media notelor", - "core.rating.aggregatecount": "Numărarea notelor", - "core.rating.aggregatemax": "Nota maximă", - "core.rating.aggregatemin": "Notă minimă", - "core.rating.aggregatesum": "Suma notelor", - "core.rating.noratings": "Nu au fost trimise note", - "core.rating.rating": "Notare", - "core.rating.ratings": "Notări", - "core.refresh": "Reîncarcă", - "core.remove": "Şterge", - "core.required": "Obligatoriu", - "core.requireduserdatamissing": "Acest utilizator are unele date de profil obligatorii necompletate. Completați aceste date în contul din Moodle și încercați din nou.
                      {{$a}}", - "core.resourcedisplayopen": "Deschide", - "core.resources": "Resurse", - "core.restore": "Restaurează", - "core.restricted": "Restricţionat", - "core.save": "Salvează", - "core.savechanges": "Salvează modificări", - "core.search": "Caută", - "core.searching": "Căutare", - "core.searchresults": "Rezultate căutare", - "core.sec": "sec", - "core.secs": "secs", - "core.seemoredetail": "Apasă aici pentru mai multe detalii", - "core.selectacategory": "Vă rugăm selectați o categorie", - "core.selectacourse": "Selectați un curs", - "core.selectagroup": "Selectați un grup", - "core.send": "Trimite", - "core.sending": "Se trimite", - "core.serverconnection": "Eroare la conectarea la server", - "core.settings.about": "Despre", - "core.settings.cordovadevicemodel": "Modelul Cordova Device", - "core.settings.cordovadeviceosversion": "Versiunea OS a Cordova Device", - "core.settings.cordovadeviceplatform": "Platforma Cordova Device", - "core.settings.cordovadeviceuuid": "UUID Cordova Device", - "core.settings.cordovaversion": "Versiunea Cordova", - "core.settings.currentlanguage": "Limba curentă", - "core.settings.debugdisplay": "Afişează mesaje debug", - "core.settings.deletesitefiles": "Sunteți sigur/ă ca doriți să ștergeți fișierele descărcate de pe siteul '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Șterge fișierele de pe site", - "core.settings.deviceinfo": "Informații despre dispozitiv", - "core.settings.deviceos": "Sistemul de operare al dispozitivului", - "core.settings.disableall": "Dezactivați notificările", - "core.settings.disabled": "Dezactivat", - "core.settings.displayformat": "Dimensiunea ecranului", - "core.settings.enabledownloadsection": "Activați secțiunile pentru descărcare", - "core.settings.enablesyncwifi": "Permiteți sincronizarea doar pe o conexiune Wi-Fi", - "core.settings.errordeletesitefiles": "A apărut o eroare la ștergerea fișierelor de pe site.", - "core.settings.errorsyncsite": "A apărut o eroare la sincronizarea datelor de pe site, verificați conexiunea la internet și încercați din nou.", - "core.settings.estimatedfreespace": "Spațiu liber estimat", - "core.settings.filesystemroot": "Rădăcina sistemului de fișiere", - "core.settings.general": "General", - "core.settings.language": "Limba", - "core.settings.license": "Licenţă", - "core.settings.localnotifavailable": "Notificările locale sunt disponibile", - "core.settings.locationhref": "URL", - "core.settings.locked": "blocat", - "core.settings.loggedin": "On-line", - "core.settings.loggedoff": "Offline", - "core.settings.navigatorlanguage": "Limbă pentru navigator", - "core.settings.navigatoruseragent": "Navigator userAgent", - "core.settings.networkstatus": "Starea conexiunii la Internet", - "core.settings.preferences": "Preferinţe", - "core.settings.reportinbackground": "Erorile se raportează automat", - "core.settings.settings": "Setări", - "core.settings.sites": "Site-uri", - "core.settings.spaceusage": "Spațiu utilizat", - "core.settings.synchronization": "Sincronizare", - "core.settings.synchronizenow": "Sincronizați acum", - "core.settings.syncsettings": "Setările sincronizării", - "core.settings.total": "Total", - "core.settings.wificonnection": "Conexiune WiFi", - "core.show": "Afişare", - "core.showless": "Afișează mai puțin...", - "core.showmore": "Afișează mai mult...", - "core.site": "Site", - "core.sitehome.sitehome": "Pagina principală", - "core.sitehome.sitenews": "Anunțuri pe site", - "core.sitemaintenance": "Acest sit este în proces de depanare şi nu este disponibil la acest moment", - "core.sizeb": "bytes", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Omite", - "core.sort": "Sortează", - "core.sortby": "Sortează după", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Trimite", - "core.success": "Succes", - "core.tablet": "Tabletă", - "core.tag.defautltagcoll": "Colecție implicită", - "core.tag.inalltagcoll": "Peste tot", - "core.tag.itemstaggedwith": "{{$a.tagarea}} etichetat cu \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Niciun rezultat pentru \"{{$a}}\"", - "core.tag.searchtags": "Caută etichete", - "core.tag.showingfirsttags": "Se afișează cele mai populare {{$a}} etichete", - "core.tag.tag": "Etichetă", - "core.tag.tagarea_course": "Cursuri", - "core.tag.tagarea_course_modules": "Activități și resurse", - "core.tag.tagarea_post": "Postări pe blog", - "core.tag.tags": "Etichete", - "core.teachers": "Profesori", - "core.thisdirection": "ltr", - "core.time": "Ora", - "core.timesup": "Timpul a expirat!", - "core.today": "Azi", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.unexpectederror": "A apărut o eroare neașteptată. Închideți și redeschideți aplicația pentru a încerca din nou", - "core.unknown": "Necunoscut", - "core.unlimited": "Nelimitat", - "core.unzipping": "Dezarhivare", - "core.upgraderunning": "Site-ul se actualizează, vă rugăm să încercați mai târziu", - "core.user": "Utilizator", - "core.user.address": "Adresă", - "core.user.city": "Oraş/localitate", - "core.user.contact": "Contact", - "core.user.country": "Ţara", - "core.user.description": "Descriere", - "core.user.details": "Detalii", - "core.user.detailsnotavailable": "Detaliile acestui utilizator nu vă sunt disponibile.", - "core.user.editingteacher": "Profesor", - "core.user.email": "Adresă email", - "core.user.emailagain": "Email (reintroduceţi)", - "core.user.firstname": "Prenume", - "core.user.interests": "Interese", - "core.user.lastname": "Nume", - "core.user.manager": "Administrator", - "core.user.newpicture": "Imagine nouă", - "core.user.noparticipants": "Nu există participanți la acest curs", - "core.user.participants": "Participanţi", - "core.user.phone1": "Telefon", - "core.user.phone2": "Mobil", - "core.user.roles": "Roluri", - "core.user.student": "Cursant", - "core.user.teacher": "Profesor fără drepturi de editare", - "core.user.webpage": "Pagină Web", - "core.userdeleted": "Acest cont a fost şters", - "core.userdetails": "Detalii utilizator", - "core.users": "Utilizatori", - "core.view": "Vizualizare", - "core.viewprofile": "Vezi profilul", - "core.whatisyourage": "Ce vârstă aveți?", - "core.wheredoyoulive": "În ce țară locuiți?", - "core.whyisthisrequired": "De ce este solicitat acest lucru?", - "core.wsfunctionnotavailable": "Această funcție Web nu este disponibilă.", - "core.year": "an", - "core.years": "ani", - "core.yes": "Da" -} \ No newline at end of file diff --git a/src/assets/lang/ru.json b/src/assets/lang/ru.json deleted file mode 100644 index a24cef992..000000000 --- a/src/assets/lang/ru.json +++ /dev/null @@ -1,1875 +0,0 @@ -{ - "addon.badges.alignment": "Выравнивание", - "addon.badges.badgedetails": "Подробнее о значке", - "addon.badges.badges": "Значки", - "addon.badges.contact": "Контакты", - "addon.badges.dateawarded": "Дата выдачи", - "addon.badges.expired": "Не действительные", - "addon.badges.expirydate": "Дата окончания срока действия", - "addon.badges.imageauthoremail": "Адрес эл. почты автора изображения", - "addon.badges.imageauthorname": "Имя автора изображения", - "addon.badges.imageauthorurl": "URL-адрес автора изображения", - "addon.badges.issuancedetails": "Срок действия значка", - "addon.badges.issuerdetails": "Сведения об эмитенте", - "addon.badges.issueremail": "Адрес эл. почты", - "addon.badges.issuername": "Наименование эмитента", - "addon.badges.issuerurl": "URL-адрес эмитента", - "addon.badges.language": "Язык", - "addon.badges.nobadges": "Нет доступных значков.", - "addon.badges.recipientdetails": "Сведения о получателе", - "addon.badges.relatedbages": "Связанные значки", - "addon.badges.version": "Версия", - "addon.badges.warnexpired": "(Срок действия этого значка истек!)", - "addon.block_activitymodules.pluginname": "Элементы курса", - "addon.block_activityresults.pluginname": "Оценки за элемент курса", - "addon.block_badges.pluginname": "Последние значки", - "addon.block_blogmenu.pluginname": "Меню блога", - "addon.block_blogrecent.pluginname": "Свежие записи блога", - "addon.block_blogtags.pluginname": "Теги блога", - "addon.block_calendarmonth.pluginname": "Календарь", - "addon.block_calendarupcoming.pluginname": "Предстоящие события", - "addon.block_comments.pluginname": "Комментарии", - "addon.block_completionstatus.pluginname": "Состояние завершения курса", - "addon.block_glossaryrandom.pluginname": "Случайная запись из глоссария", - "addon.block_learningplans.pluginname": "Учебные планы", - "addon.block_myoverview.all": "Все (кроме скрытых)", - "addon.block_myoverview.allincludinghidden": "Все", - "addon.block_myoverview.favourites": "Избранное", - "addon.block_myoverview.future": "Предстоящие", - "addon.block_myoverview.hiddencourses": "Скрытые", - "addon.block_myoverview.inprogress": "Текущие", - "addon.block_myoverview.lastaccessed": "Последнее посещение", - "addon.block_myoverview.morecourses": "Больше курсов", - "addon.block_myoverview.nocourses": "Нет курсов", - "addon.block_myoverview.past": "Прошедшие", - "addon.block_myoverview.pluginname": "Сводка по курсам", - "addon.block_myoverview.title": "Название курса", - "addon.block_newsitems.pluginname": "Последние объявления", - "addon.block_onlineusers.pluginname": "Пользователи на сайте", - "addon.block_privatefiles.pluginname": "Личные файлы", - "addon.block_recentactivity.pluginname": "Последние действия", - "addon.block_recentlyaccessedcourses.nocourses": "Нет недавно посещенных курсов", - "addon.block_recentlyaccessedcourses.pluginname": "Недавно посещенные курсы", - "addon.block_recentlyaccesseditems.noitems": "Нет новых элементов", - "addon.block_recentlyaccesseditems.pluginname": "Недавние объекты", - "addon.block_rssclient.pluginname": "Внешние RSS-ленты", - "addon.block_selfcompletion.pluginname": "Самостоятельное отслеживание завершения", - "addon.block_sitemainmenu.pluginname": "Основное меню", - "addon.block_starredcourses.nocourses": "Нет избранных курсов", - "addon.block_starredcourses.pluginname": "Избранные курсы", - "addon.block_tags.pluginname": "Теги", - "addon.block_timeline.duedate": "Срок сдачи", - "addon.block_timeline.next30days": "Следующие 30 дней", - "addon.block_timeline.next3months": "Следующие 3 месяца", - "addon.block_timeline.next6months": "Следующие 6 месяцев", - "addon.block_timeline.next7days": "Следующие 7 дней", - "addon.block_timeline.nocoursesinprogress": "Нет начатых курсов", - "addon.block_timeline.noevents": "Нет элементов курса с приближающимся сроком сдачи", - "addon.block_timeline.overdue": "Просрочены", - "addon.block_timeline.pluginname": "Шкала времени", - "addon.block_timeline.sortbycourses": "Сортировать по курсам", - "addon.block_timeline.sortbydates": "Сортировать по дате", - "addon.blog.blog": "Блог", - "addon.blog.blogentries": "Записи блога", - "addon.blog.linktooriginalentry": "Ссылка на оригинальную запись блога", - "addon.blog.noentriesyet": "Нет отображаемых записей", - "addon.blog.publishtonoone": "для себя (черновик)", - "addon.blog.publishtosite": "для всех пользователей сайта", - "addon.blog.publishtoworld": "для всего мира", - "addon.blog.siteblogheading": "Блог сайта", - "addon.calendar.allday": "Все дни", - "addon.calendar.calendar": "Календарь", - "addon.calendar.calendarevents": "События календаря", - "addon.calendar.categoryevents": "События категории", - "addon.calendar.confirmeventdelete": "Вы уверены, что хотите удалить событие «{{$a}}»?", - "addon.calendar.confirmeventseriesdelete": "Событие «{{$a.name}}» является частью серии. Вы хотите удалить только это событие или все события ({{$a.count}}) в этой серии?", - "addon.calendar.courseevents": "События курса", - "addon.calendar.daynext": "Следующий день", - "addon.calendar.dayprev": "Предыдущий день", - "addon.calendar.defaultnotificationtime": "Время уведомлений по умолчанию", - "addon.calendar.deleteallevents": "Удалить все события", - "addon.calendar.deleteevent": "Удалить событие", - "addon.calendar.deleteoneevent": "Удалить это событие", - "addon.calendar.durationminutes": "Продолжительность в минутах", - "addon.calendar.durationnone": "Без продолжительности", - "addon.calendar.durationuntil": "До", - "addon.calendar.editevent": "Редактирование события", - "addon.calendar.errorloadevent": "Ошибка при загрузке события", - "addon.calendar.errorloadevents": "Ошибка при загрузке событий", - "addon.calendar.eventcalendareventdeleted": "Событие календаря удалено", - "addon.calendar.eventduration": "Продолжительность", - "addon.calendar.eventendtime": "Время окончания", - "addon.calendar.eventkind": "Тип события", - "addon.calendar.eventname": "Название события", - "addon.calendar.eventstarttime": "Время начала", - "addon.calendar.eventtype": "Тип события", - "addon.calendar.fri": "Пт", - "addon.calendar.friday": "Пятница", - "addon.calendar.gotoactivity": "Перейти к элементу курса", - "addon.calendar.groupevents": "Групповые события", - "addon.calendar.invalidtimedurationminutes": "Вы ввели неверное значение длительности в минутах. Введите значение больше 0 или не указывайте значение.", - "addon.calendar.invalidtimedurationuntil": "Установленный период для события оказывается раньше времени его начала. Пожалуйста, исправьте это перед продолжением.", - "addon.calendar.mon": "Пн", - "addon.calendar.monday": "Понедельник", - "addon.calendar.monthlyview": "Месячный обзор", - "addon.calendar.newevent": "Новое событие", - "addon.calendar.noevents": "Нет событий", - "addon.calendar.nopermissiontoupdatecalendar": "Извините, но сейчас у вас нет прав на обновление события календаря", - "addon.calendar.repeatedevents": "Повторяющиеся события", - "addon.calendar.repeateditall": "Сохранить изменения для других повторяющихся событий ({{$a}}) этой серии", - "addon.calendar.repeateditthis": "Сохранить изменения только для этого события", - "addon.calendar.repeatevent": "Повторять это событие", - "addon.calendar.repeatweeksl": "Повторять еженедельно, создать всего:", - "addon.calendar.sat": "Сб", - "addon.calendar.saturday": "Суббота", - "addon.calendar.siteevents": "События сайта", - "addon.calendar.sun": "Вс", - "addon.calendar.sunday": "Воскресенье", - "addon.calendar.thu": "Чт", - "addon.calendar.thursday": "Четверг", - "addon.calendar.today": "Сегодня", - "addon.calendar.tomorrow": "Завтра", - "addon.calendar.tue": "Вт", - "addon.calendar.tuesday": "Вторник", - "addon.calendar.typecategory": "Событие категории", - "addon.calendar.typeclose": "Закрытое событие", - "addon.calendar.typecourse": "События курса", - "addon.calendar.typedue": "События срока сдачи", - "addon.calendar.typegradingdue": "События срока оценки", - "addon.calendar.typegroup": "Событие группы", - "addon.calendar.typeopen": "Открытое событие", - "addon.calendar.typesite": "Событие сайта", - "addon.calendar.typeuser": "Событие пользователя", - "addon.calendar.upcomingevents": "Предстоящие события", - "addon.calendar.userevents": "События пользователя", - "addon.calendar.wed": "Ср", - "addon.calendar.wednesday": "Среда", - "addon.calendar.when": "Когда", - "addon.calendar.yesterday": "Вчера", - "addon.competency.activities": "Элементы", - "addon.competency.competencies": "Компетенции", - "addon.competency.competenciesmostoftennotproficientincourse": "Компетенции, которые чаще всего оказываются не освоенными в этом курсе", - "addon.competency.coursecompetencies": "Компетенции курса", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Рейтинги компетенций из этого курса не влияют на учебные планы.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Рейтинги компетенций из этого курса сразу же обновляются в учебных планах.", - "addon.competency.crossreferencedcompetencies": "Перекрестные компетенции", - "addon.competency.duedate": "Срок выполнения", - "addon.competency.errornocompetenciesfound": "Компетенций не найдено", - "addon.competency.evidence": "Доказательство", - "addon.competency.evidence_competencyrule": "Выполнены требования правила компетенции.", - "addon.competency.evidence_coursecompleted": "Курс «{{$a}}» завершен.", - "addon.competency.evidence_coursemodulecompleted": "Элемент «{{$a}}» завершен.", - "addon.competency.evidence_courserestored": "Рейтинг был восстановлен вместе с курсом «{{$a}}».", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Доказательство предыдущего обучения «{{$a}}» было привязано.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Отменена связь доказательства предыдущего обучения «{{$a}}».", - "addon.competency.evidence_manualoverride": "Рейтинг компетенции был задан вручную.", - "addon.competency.evidence_manualoverrideincourse": "Рейтинг компетенции был задан вручную для курса «{{$a}}».", - "addon.competency.evidence_manualoverrideinplan": "Рейтинг компетенции был задан вручную для учебного плана «{{$a}}».", - "addon.competency.learningplancompetencies": "Компетенции учебного плана", - "addon.competency.learningplans": "Учебные планы", - "addon.competency.myplans": "Мои учебные планы", - "addon.competency.noactivities": "Нет элементов", - "addon.competency.nocompetencies": "Нет компетенций", - "addon.competency.nocompetenciesincourse": "Нет компетенций, связанных с этим курсом.", - "addon.competency.nocrossreferencedcompetencies": "Нет других компетенций, перекрестно ссылающихся на эту компетенцию.", - "addon.competency.noevidence": "Нет доказательств", - "addon.competency.noplanswerecreated": "Учебные планы не были созданы.", - "addon.competency.path": "Путь:", - "addon.competency.planstatusactive": "Активно", - "addon.competency.planstatuscomplete": "Выполнено", - "addon.competency.planstatusdraft": "Черновик", - "addon.competency.planstatusinreview": "Проверяется", - "addon.competency.planstatuswaitingforreview": "Ожидание отзыва", - "addon.competency.proficient": "Освоено", - "addon.competency.progress": "В процессе", - "addon.competency.rating": "Рейтинг", - "addon.competency.reviewstatus": "Статус пересмотра", - "addon.competency.status": "Статус", - "addon.competency.template": "Шаблон учебного плана", - "addon.competency.uponcoursecompletion": "По завершению курса:", - "addon.competency.usercompetencystatus_idle": "Не используется", - "addon.competency.usercompetencystatus_inreview": "В процессе пересмотра", - "addon.competency.usercompetencystatus_waitingforreview": "Ожидает пересмотра", - "addon.competency.userplans": "Учебные планы", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} из {{$a.y}} компетенций освоены", - "addon.competency.xcompetenciesproficientoutofyincourse": "Вы освоили {{$a.x}} из {{$a.y}} компетенций в этом курсе.", - "addon.coursecompletion.complete": "Завершить", - "addon.coursecompletion.completecourse": "Завершить курс", - "addon.coursecompletion.completed": "Выполнено", - "addon.coursecompletion.completiondate": "Дата завершения", - "addon.coursecompletion.completionmenuitem": "Отслеживание выполнения", - "addon.coursecompletion.couldnotloadreport": "Невозможно загрузить отчёт о завершении курса. Пожалуйста, попробуйте ещё раз позже.", - "addon.coursecompletion.coursecompletion": "Завершение курса", - "addon.coursecompletion.criteria": "Критерии", - "addon.coursecompletion.criteriagroup": "Группа критериев", - "addon.coursecompletion.criteriarequiredall": "Требуются соответствие всем указанным ниже критериям", - "addon.coursecompletion.criteriarequiredany": "Требуется соответствие любому из указанных ниже критериев", - "addon.coursecompletion.inprogress": "В процессе", - "addon.coursecompletion.manualselfcompletion": "Пользователь может сам поставить отметку о выполнении", - "addon.coursecompletion.nottracked": "Вы в настоящее время не отслеживаете завершение в этом курсе", - "addon.coursecompletion.notyetstarted": "Еще не началось", - "addon.coursecompletion.pending": "Ожидается", - "addon.coursecompletion.required": "Необходимо заполнить", - "addon.coursecompletion.requiredcriteria": "Необходимые критерии", - "addon.coursecompletion.requirement": "Необходимое условие", - "addon.coursecompletion.status": "Статус", - "addon.coursecompletion.viewcoursereport": "Просмотреть отчет по курсу", - "addon.files.couldnotloadfiles": "Файлы из списка не могут быть загружены", - "addon.files.emptyfilelist": "Нет файлов для отображения", - "addon.files.erroruploadnotworking": "К сожалению, в данный момент невозможно загрузить файлы на ваш сайт.", - "addon.files.files": "Файлы", - "addon.files.privatefiles": "Личные файлы", - "addon.files.sitefiles": "Файлы сайта", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Настроить устройства", - "addon.messages.acceptandaddcontact": "Принять и добавить в контакты", - "addon.messages.addcontact": "Добавить собеседника", - "addon.messages.addcontactconfirm": "Вы уверены, что хотите добавить {{$a}} к своим контактам?", - "addon.messages.addtofavourites": "Пометить", - "addon.messages.addtoyourcontacts": "Добавить в список контактов", - "addon.messages.blocknoncontacts": "Не принимать сообщения от людей, которых нет в списке моих собеседников", - "addon.messages.blockuser": "Блокировать пользователя", - "addon.messages.blockuserconfirm": "Вы уверены, что хотите заблокировать {{$a}}?", - "addon.messages.contactableprivacy": "Принимать сообщения от:", - "addon.messages.contactableprivacy_coursemember": "Мои контакты и любой в моих курсах", - "addon.messages.contactableprivacy_onlycontacts": "Только мои контакты", - "addon.messages.contactableprivacy_site": "Любой на сайте", - "addon.messages.contactblocked": "Контакт заблокирован", - "addon.messages.contactlistempty": "Список контактов пуст", - "addon.messages.contactname": "Имя контакта", - "addon.messages.contactrequestsent": "Запрос на добавление в контакты отправлен", - "addon.messages.contacts": "Собеседники", - "addon.messages.decline": "Отказаться", - "addon.messages.deleteallconfirm": "Вы уверены, что хотите удалить всю эту беседу ? Это не удалит её для других участников разговора.", - "addon.messages.deleteallselfconfirm": "Вы уверены, что хотите удалить всю эту личную беседу?", - "addon.messages.deleteconversation": "Удалить беседу", - "addon.messages.errordeletemessage": "Ошибка при удалении сообщения.", - "addon.messages.errorwhileretrievingcontacts": "Ошибка при извлечении контактов с сервера.", - "addon.messages.errorwhileretrievingdiscussions": "Ошибка при получении обсуждений с сервера.", - "addon.messages.errorwhileretrievingmessages": "Ошибка при получении сообщений с сервера.", - "addon.messages.groupconversations": "Группа", - "addon.messages.groupinfo": "Информация о группе", - "addon.messages.individualconversations": "Личное", - "addon.messages.info": "Информация", - "addon.messages.isnotinyourcontacts": "{{$a}} нет в ваших контактах", - "addon.messages.message": "Сообщение", - "addon.messages.messagenotsent": "Сообщение не было отправлено. Пожалуйста, повторите попытку позже.", - "addon.messages.messagepreferences": "Настройки сообщений", - "addon.messages.messages": "Сообщения", - "addon.messages.newmessage": "Новое сообщение", - "addon.messages.newmessages": "Новые сообщения", - "addon.messages.nocontactrequests": "Нет запросов на добавление в контакты", - "addon.messages.nocontactsgetstarted": "Нет контактов", - "addon.messages.nofavourites": "Нет помеченных разговоров", - "addon.messages.nogroupconversations": "Нет групповых разговоров", - "addon.messages.noindividualconversations": "Нет личных разговоров", - "addon.messages.nomessagesfound": "Сообщений не найдено", - "addon.messages.noncontacts": "Собеседники отсутствуют", - "addon.messages.nousersfound": "Пользователи не найдены", - "addon.messages.numparticipants": "Участники – {{$a}}", - "addon.messages.removecontact": "Удалить собеседника из моего списка", - "addon.messages.removecontactconfirm": "Вы уверены, что хотите удалить {{$a}} из своих контактов?", - "addon.messages.removefromfavourites": "Снять пометку", - "addon.messages.removefromyourcontacts": "Удалить из контактов", - "addon.messages.requests": "Запросы", - "addon.messages.requirecontacttomessage": "Вам нужно попросить {{$a}} добавить вас в контакты, чтобы иметь возможность отправлять сообщения этому пользователю.", - "addon.messages.searchcombined": "Поиск пользователей и сообщений", - "addon.messages.selfconversation": "Личное пространство", - "addon.messages.selfconversationdefaultmessage": "Сохраните черновики сообщений, ссылок, заметок и т.п. К ним можно будет вернуться позже.", - "addon.messages.sendcontactrequest": "Отправить запрос на добавление в контакты", - "addon.messages.type_blocked": "Заблокировано", - "addon.messages.type_offline": "Вне сайта", - "addon.messages.type_online": "На сайте", - "addon.messages.type_search": "Результаты поиска", - "addon.messages.type_strangers": "Другие", - "addon.messages.unabletomessage": "Вы не можете отправить сообщение этому пользователю", - "addon.messages.unblockuser": "Разблокировать пользователя", - "addon.messages.unblockuserconfirm": "Вы уверены, что хотите разблокировать {{$a}}?", - "addon.messages.useentertosend": "Использовать Enter для отправки", - "addon.messages.userwouldliketocontactyou": "{{$a}} хотел(а) бы с вами связаться", - "addon.messages.warningmessagenotsent": "Не получилось отправить сообщение(я) пользователю {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Хотят связаться с вами", - "addon.messages.you": "Вы:", - "addon.messages.youhaveblockeduser": "Вы заблокировали этого пользователя", - "addon.messages.yourcontactrequestpending": "Ваш запрос на добавление в контакты находится на рассмотрении с {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Пожалуйста, примите заявление к предоставляемому ответу.", - "addon.mod_assign.addattempt": "Разрешить еще одну попытку", - "addon.mod_assign.addnewattempt": "Добавить новую попытку", - "addon.mod_assign.addnewattemptfromprevious": "Добавить новую попытку на основе предыдущего представления", - "addon.mod_assign.addsubmission": "Добавить ответ на задание", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Описание задания и возможность отправлять ответов доступны с {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Разрешить выполнение задания с", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Это назначение будет приниматься с {{$a}}", - "addon.mod_assign.applytoteam": "Применить оценки и отзывы для всей группы.", - "addon.mod_assign.assignmentisdue": "Задание сдано", - "addon.mod_assign.attemptnumber": "Номер попытки", - "addon.mod_assign.attemptreopenmethod": "Разрешать новые попытки", - "addon.mod_assign.attemptreopenmethod_manual": "Вручную", - "addon.mod_assign.attemptreopenmethod_untilpass": "Автоматически (до проходной оценки)", - "addon.mod_assign.attemptsettings": "Настройки попытки", - "addon.mod_assign.cannoteditduetostatementsubmission": "Вы не можете добавить или отредактировать ответ в приложении, потому что не удалось получить с сайта заявление к предоставляемому ответу.", - "addon.mod_assign.cannotgradefromapp": "Определённые методы оценки ещё не поддерживаются приложением и не могут быть изменены.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Вы не можете ответить в приложении, потому что не удалось получить с сайта заявление к предоставляемому ответу.", - "addon.mod_assign.confirmsubmission": "Вы уверены, что хотите представить свою работу для оценивания? Вы больше не сможете изменить свой ответ.", - "addon.mod_assign.currentattempt": "Попытка {{$a}}.", - "addon.mod_assign.currentattemptof": "Номер этой попытки - {{$a.attemptnumber}}. (Разрешено попыток - {{$a.maxattempts}})", - "addon.mod_assign.currentgrade": "Текущая оценка в журнале", - "addon.mod_assign.cutoffdate": "Запретить отправку с", - "addon.mod_assign.defaultteam": "Группа по умолчанию", - "addon.mod_assign.duedate": "Последний срок сдачи", - "addon.mod_assign.duedateno": "Срок сдачи не ограничен", - "addon.mod_assign.duedatereached": "Срок сдачи этого задания уже истек", - "addon.mod_assign.editingstatus": "Изменение статуса", - "addon.mod_assign.editsubmission": "Редактировать ответ", - "addon.mod_assign.erroreditpluginsnotsupported": "Вы не можете добавлять или изменять ответ в приложении, потому что определённые плагины пока не поддерживают редактирование.", - "addon.mod_assign.errorshowinginformation": "Информация об ответе не может быть отображена.", - "addon.mod_assign.extensionduedate": "Срок продления", - "addon.mod_assign.feedbacknotsupported": "Эта обратная связь не поддерживается приложением и может содержать не всю информацию.", - "addon.mod_assign.grade": "Оценка", - "addon.mod_assign.graded": "Оценено", - "addon.mod_assign.gradedby": "Оценено", - "addon.mod_assign.gradedfollowupsubmit": "Оценено - получен исправленный ответ", - "addon.mod_assign.gradedon": "Оценено в", - "addon.mod_assign.gradelocked": "Эта оценка заблокирована или изменена в журнале оценок.", - "addon.mod_assign.gradenotsynced": "Оценка не синхронизирована", - "addon.mod_assign.gradeoutof": "Оценка из {{$a}}", - "addon.mod_assign.gradingstatus": "Состояние оценивания", - "addon.mod_assign.groupsubmissionsettings": "Настройки представления работ группы", - "addon.mod_assign.hiddenuser": "Участник", - "addon.mod_assign.latesubmissions": "Поступившие представления", - "addon.mod_assign.latesubmissionsaccepted": "Разрешено до {{$a}}", - "addon.mod_assign.markingworkflowstate": "Этап процесса оценивания", - "addon.mod_assign.markingworkflowstateinmarking": "Оценивается", - "addon.mod_assign.markingworkflowstateinreview": "Проверяется", - "addon.mod_assign.markingworkflowstatenotmarked": "Еще не оценивалось", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Готово к публикации", - "addon.mod_assign.markingworkflowstatereadyforreview": "Оценивание завершено", - "addon.mod_assign.markingworkflowstatereleased": "Опубликовано", - "addon.mod_assign.modulenameplural": "Задания", - "addon.mod_assign.multipleteams": "Член нескольких групп", - "addon.mod_assign.multipleteams_desc": "Это задание требует представления в группах. Вы являетесь членом более чем одной группы. Чтобы представить ответ, вы должны быть участником только одной группы. Пожалуйста, свяжитесь со своим учителем, чтобы изменить ваше членство в группе.", - "addon.mod_assign.noattempt": "Ни одной попытки", - "addon.mod_assign.nomoresubmissionsaccepted": "Разрешено только для участников, которым было предоставлено продление срока.", - "addon.mod_assign.noonlinesubmissions": "Ответ на задание должен быть представлен вне сайта", - "addon.mod_assign.nosubmission": "Ничего не было представлено", - "addon.mod_assign.notallparticipantsareshown": "Участники, которые не дали ответ, не показаны.", - "addon.mod_assign.noteam": "Не является членом какой-либо группы", - "addon.mod_assign.noteam_desc": "Это задание требует представления в группах. Вы не являетесь членом какой-либо группы, поэтому вы не можете представить ответ. Пожалуйста, свяжитесь с вашим учителем для добавления в группу.", - "addon.mod_assign.notgraded": "Не оценено", - "addon.mod_assign.numberofdraftsubmissions": "Черновик", - "addon.mod_assign.numberofparticipants": "Участники", - "addon.mod_assign.numberofsubmissionsneedgrading": "Требуют оценки", - "addon.mod_assign.numberofsubmittedassignments": "Ответы", - "addon.mod_assign.numberofteams": "Группы", - "addon.mod_assign.numwords": "всего слов - {{$a}}", - "addon.mod_assign.outof": "{{$a.current}} из {{$a.total}}", - "addon.mod_assign.overdue": "Задание просрочено на: {{$a}}", - "addon.mod_assign.submission": "Ответ", - "addon.mod_assign.submissioneditable": "Студент может править свой ответ", - "addon.mod_assign.submissionnoteditable": "Студент не может исправлять этот ответ", - "addon.mod_assign.submissionnotsupported": "Этот ответ не поддерживается приложением и может содержать не всю информацию.", - "addon.mod_assign.submissionslocked": "Ответы на это задание не принимаются", - "addon.mod_assign.submissionstatus": "Состояние ответа на задание", - "addon.mod_assign.submissionstatus_": "Нет ответа на задание", - "addon.mod_assign.submissionstatus_draft": "Черновик (не представлен)", - "addon.mod_assign.submissionstatus_marked": "Оценено", - "addon.mod_assign.submissionstatus_new": "Ответ не представлен", - "addon.mod_assign.submissionstatus_reopened": "Возобновлено", - "addon.mod_assign.submissionstatus_submitted": "Ответы для оценки", - "addon.mod_assign.submissionstatusheading": "Состояние ответа", - "addon.mod_assign.submissionteam": "Группы", - "addon.mod_assign.submitassignment": "Отправить на проверку", - "addon.mod_assign.submitassignment_help": "Однажды представив ответ на это задание Вы больше не сможете изменить его.", - "addon.mod_assign.submittedearly": "Задание представлено заранее - {{$a}}", - "addon.mod_assign.submittedlate": "Задание представлено с опозданием - {{$a}}", - "addon.mod_assign.timemodified": "Последнее изменение", - "addon.mod_assign.timeremaining": "Оставшееся время", - "addon.mod_assign.ungroupedusers": "Включен параметр «Нужно быть в группе для отправки ответа». Некоторые пользователи не являются членами групп или являются членами нескольких групп, эти пользователи не смогут отправить ответы на задание.", - "addon.mod_assign.ungroupedusersoptional": "Параметр «Студенты отправляются в группы» включен, и некоторые пользователи либо не являются членами какой-либо группы, либо являются членами более чем одной группы. Учтите, что эти студенты будут представлены в качестве членов группы по умолчанию.", - "addon.mod_assign.unlimitedattempts": "Неограничено", - "addon.mod_assign.userswhoneedtosubmit": "Пользователи, которые должны представить ответ: {{$a}}", - "addon.mod_assign.userwithid": "Пользователь с ID {{id}}", - "addon.mod_assign.viewsubmission": "Просмотр ответов", - "addon.mod_assign.warningsubmissiongrademodified": "Оценка ответа была изменена на сайте.", - "addon.mod_assign.warningsubmissionmodified": "Ответ пользователя был изменён на сайте.", - "addon.mod_assign.wordlimit": "Лимит слов", - "addon.mod_assign_feedback_comments.pluginname": "Отзыв в виде комментария", - "addon.mod_assign_feedback_editpdf.pluginname": "Аннотирование PDF", - "addon.mod_assign_feedback_file.pluginname": "Отзыв в виде файла", - "addon.mod_assign_submission_comments.pluginname": "Комментарии к ответу", - "addon.mod_assign_submission_file.pluginname": "Ответ в виде файла", - "addon.mod_assign_submission_onlinetext.pluginname": "Ответ в виде текста", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Для этого задания задано ограничение максимального количества слов - {{$a.limit}}. Вы пытаетесь представить {{$a.count}} слово(а). Проверьте свой текст и попробуйте еще раз.", - "addon.mod_book.errorchapter": "Ошибка чтения главы книги.", - "addon.mod_book.modulenameplural": "Книги", - "addon.mod_book.navnexttitle": "Далее: {{$a}}", - "addon.mod_book.navprevtitle": "Назад: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Главы книги", - "addon.mod_book.toc": "Оглавление", - "addon.mod_chat.beep": "Сигнал", - "addon.mod_chat.chatreport": "Сессии чата", - "addon.mod_chat.currentusers": "Текущие пользователи", - "addon.mod_chat.enterchat": "Войти в чат", - "addon.mod_chat.entermessage": "Введите свое сообщение", - "addon.mod_chat.errorwhileconnecting": "Ошибка при подключении к чату", - "addon.mod_chat.errorwhilegettingchatdata": "Ошибка при получении данных из чата", - "addon.mod_chat.errorwhilegettingchatusers": "Ошибка при получении пользователей чата", - "addon.mod_chat.errorwhileretrievingmessages": "Ошибка при получении сообщения от сервера.", - "addon.mod_chat.errorwhilesendingmessage": "Ошибка при отправке сообщения", - "addon.mod_chat.messagebeepseveryone": "{{$a}} отправил сигнал всем!", - "addon.mod_chat.messagebeepsyou": "{{$a}} отправил Вам сигнал!", - "addon.mod_chat.messageenter": "{{$a}} появился в чате", - "addon.mod_chat.messageexit": "{{$a}} ушел из чата", - "addon.mod_chat.messages": "Сообщения", - "addon.mod_chat.messageyoubeep": "Вы отправили сигнал {{$a}}", - "addon.mod_chat.modulenameplural": "Чаты", - "addon.mod_chat.mustbeonlinetosendmessages": "Вы должны быть в сети, чтобы отправлять сообщения.", - "addon.mod_chat.nomessages": "Нет ни одного сообщения", - "addon.mod_chat.saidto": "сказано", - "addon.mod_chat.send": "Отправить", - "addon.mod_chat.sessionstart": "Следующий сеанс чата начнётся: {{$a.date}}, (через {{$a.fromnow}})", - "addon.mod_chat.talk": "Разговор", - "addon.mod_chat.viewreport": "Посмотреть прошлые чат-сессии", - "addon.mod_choice.cannotsubmit": "Извините, возникла проблема при отправке Вашего ответа на опрос. Пожалуйста, повторите снова.", - "addon.mod_choice.choiceoptions": "Варианты ответа", - "addon.mod_choice.errorgetchoice": "Ошибка при получении данных выбора", - "addon.mod_choice.expired": "Этот элемент курса закрыт {{$a}}.", - "addon.mod_choice.full": "(Заполнено)", - "addon.mod_choice.modulenameplural": "Опросы", - "addon.mod_choice.noresultsviewable": "Вы не можете в данный момент просматривать результаты опроса.", - "addon.mod_choice.notopenyet": "Этот элемент курса недоступен до {{$a}}.", - "addon.mod_choice.numberofuser": "Количество ответов на опрос", - "addon.mod_choice.numberofuserinpercentage": "Доля ответивших на опрос", - "addon.mod_choice.previewonly": "Это только предварительный просмотр доступных вариантов для этого опроса. Вы не можете отправить свой ответ до {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Анонимные результаты будут опубликованы после вашего ответа.", - "addon.mod_choice.publishinfoanonclose": "Анонимные результаты будут опубликованы после закрытия опроса.", - "addon.mod_choice.publishinfofullafter": "Полные результаты, показывающие выбор каждого, будут опубликованы после вашего ответа.", - "addon.mod_choice.publishinfofullclose": "Полные результаты, показывающие выбор каждого, будут опубликованы после закрытия опроса.", - "addon.mod_choice.publishinfonever": "Результаты этого опроса не будут опубликованы после вашего ответа.", - "addon.mod_choice.removemychoice": "Удалить мой выбор", - "addon.mod_choice.responses": "Ответы", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% пользователей выбрало вариант: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Отображать график", - "addon.mod_choice.resultsnotsynced": "Ваш последний ответ должен быть синхронизирован прежде, чем будет включён в результат.", - "addon.mod_choice.savemychoice": "Сохранить мой выбор", - "addon.mod_choice.userchoosethisoption": "Пользователи, которые выбрали этот вариант", - "addon.mod_choice.yourselection": "Ваш выбор", - "addon.mod_data.addentries": "Добавить записи", - "addon.mod_data.advancedsearch": "Расширенный поиск", - "addon.mod_data.alttext": "Альтернативный текст", - "addon.mod_data.approve": "Одобрить", - "addon.mod_data.approved": "Одобрено", - "addon.mod_data.ascending": "По возрастанию", - "addon.mod_data.authorfirstname": "Имя автора", - "addon.mod_data.authorlastname": "Фамилия автора", - "addon.mod_data.confirmdeleterecord": "Вы уверены, что хотите удалить эту запись?", - "addon.mod_data.descending": "По убыванию", - "addon.mod_data.disapprove": "Отменить одобрение", - "addon.mod_data.emptyaddform": "Вы не заполнили ни одного поля", - "addon.mod_data.entrieslefttoadd": "Чтобы просматривать записи других участников Вы должны добавить еще записи - ({{$a.entriesleft}})", - "addon.mod_data.entrieslefttoaddtoview": "Вы должны еще добавить записи ({{$a.entrieslefttoview}}), прежде чем сможете просматривать записи других участников.", - "addon.mod_data.errorapproving": "Ошибка подтверждения или неподтверждения записи.", - "addon.mod_data.errordeleting": "Ошибка удаления записи.", - "addon.mod_data.errormustsupplyvalue": "Вы должны здесь указать значение.", - "addon.mod_data.expired": "К сожалению, эта база данных закрыта {{$a}} и больше не доступна", - "addon.mod_data.fields": "Поля", - "addon.mod_data.foundrecords": "Найдено записей: {{$a.num}}/{{$a.max}}. (Сбросить фильтры)", - "addon.mod_data.latlongboth": "Необходимо задать и широту, и долготу.", - "addon.mod_data.menuchoose": "Выбрать...", - "addon.mod_data.modulenameplural": "Базы данных", - "addon.mod_data.more": "Просмотр записи", - "addon.mod_data.nomatch": "Соответствующих записей не найдено!", - "addon.mod_data.norecords": "Нет записей в базе данных", - "addon.mod_data.notapproved": "Запись пока не одобрена.", - "addon.mod_data.notopenyet": "Извините, этот элемент курса не доступен до {{$a}}", - "addon.mod_data.numrecords": "{{$a}} записей", - "addon.mod_data.other": "Другое", - "addon.mod_data.recordapproved": "Запись одобрена", - "addon.mod_data.recorddeleted": "Запись удалена", - "addon.mod_data.recorddisapproved": "Снято одобрение записи", - "addon.mod_data.resetsettings": "Сбросить фильтры", - "addon.mod_data.search": "Поиск", - "addon.mod_data.selectedrequired": "Требуются все выбранные", - "addon.mod_data.single": "Просмотр по одной записи", - "addon.mod_data.tagarea_data_records": "Записи данных", - "addon.mod_data.timeadded": "Время добавления", - "addon.mod_data.timemodified": "Время изменения", - "addon.mod_data.usedate": "Включить в поиск", - "addon.mod_feedback.analysis": "Анализ результатов", - "addon.mod_feedback.anonymous": "Анонимный", - "addon.mod_feedback.anonymous_entries": "Анонимные записи ({{$a}})", - "addon.mod_feedback.average": "Средний", - "addon.mod_feedback.captchaofflinewarning": "Обратная связь с CAPTCHA не может быть выполнена если вы не в сети, если не настроена или если сервер недоступен.", - "addon.mod_feedback.complete_the_form": "Ответьте на вопросы ...", - "addon.mod_feedback.completed_feedbacks": "Отправлено ответов", - "addon.mod_feedback.continue_the_form": "Продолжить ответы на вопросы...", - "addon.mod_feedback.feedback_is_not_open": "эта анкета обратной связи не открыта", - "addon.mod_feedback.feedback_submitted_offline": "Этот отзыв был сохранён, для отправки позже.", - "addon.mod_feedback.feedbackclose": "Разрешить отвечать до", - "addon.mod_feedback.feedbackopen": "Разрешить отвечать с", - "addon.mod_feedback.mapcourses": "Сопоставление Обратной связи с курсами", - "addon.mod_feedback.maximal": "максимум", - "addon.mod_feedback.minimal": "минимум", - "addon.mod_feedback.mode": "Режим", - "addon.mod_feedback.modulenameplural": "Обратная связь", - "addon.mod_feedback.next_page": "Следующая страница", - "addon.mod_feedback.non_anonymous": "Имя пользователя будет записано и показано с его ответами", - "addon.mod_feedback.non_anonymous_entries": "Не анонимные записи ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Не ответившие студенты ({{$a}})", - "addon.mod_feedback.not_selected": "Не выбрано", - "addon.mod_feedback.not_started": "Не начато", - "addon.mod_feedback.numberoutofrange": "Значение вне диапазона", - "addon.mod_feedback.overview": "Просмотр", - "addon.mod_feedback.page_after_submit": "Сообщение о завершении", - "addon.mod_feedback.preview": "Предварительный просмотр", - "addon.mod_feedback.previous_page": "Предыдущая страница", - "addon.mod_feedback.questions": "Вопросов", - "addon.mod_feedback.response_nr": "Номер ответа", - "addon.mod_feedback.responses": "Ответы", - "addon.mod_feedback.save_entries": "Отправить свои ответы", - "addon.mod_feedback.show_entries": "Показать ответивших", - "addon.mod_feedback.show_nonrespondents": "Показать не ответивших", - "addon.mod_feedback.started": "Начало", - "addon.mod_feedback.this_feedback_is_already_submitted": "Вы уже завершили этот учебный элемент", - "addon.mod_folder.emptyfilelist": "Нет файлов для отображения", - "addon.mod_folder.modulenameplural": "Папки", - "addon.mod_forum.addanewdiscussion": "Добавить тему для обсуждения", - "addon.mod_forum.addanewquestion": "Добавить новый вопрос", - "addon.mod_forum.addanewtopic": "Добавить новую тему", - "addon.mod_forum.addtofavourites": "Пометить это обсуждение", - "addon.mod_forum.advanced": "Расширенная форма ответа", - "addon.mod_forum.cannotadddiscussion": "Нужно быть участником группы, чтобы добавлять обсуждения на этот форум.", - "addon.mod_forum.cannotadddiscussionall": "У вас нет привилегий для добавления новой темы обсуждения для всех участников.", - "addon.mod_forum.cannotcreatediscussion": "Невозможно создать новое обсуждение", - "addon.mod_forum.couldnotadd": "Сообщение не добавлено из-за неизвестной ошибки", - "addon.mod_forum.couldnotupdate": "Сообщение не обновлено из-за неизвестной ошибки", - "addon.mod_forum.delete": "Удалить", - "addon.mod_forum.deletedpost": "Сообщение было удалено", - "addon.mod_forum.deletesure": "Вы уверены, что хотите удалить это сообщение?", - "addon.mod_forum.discussion": "Обсуждение", - "addon.mod_forum.discussionlocked": "Обсуждение было заблокировано, поэтому на него нельзя больше отвечать.", - "addon.mod_forum.discussionpinned": "Закреплено", - "addon.mod_forum.discussionsubscription": "Подписаться на эту тему.", - "addon.mod_forum.edit": "Редактировать", - "addon.mod_forum.erroremptymessage": "Сообщение не может быть пустым", - "addon.mod_forum.erroremptysubject": "Тема сообщения не может быть пустой", - "addon.mod_forum.errorgetforum": "Ошибка при получении данных форума", - "addon.mod_forum.errorgetgroups": "Ошибка получения параметров группы", - "addon.mod_forum.favouriteupdated": "Ваши пометки были обновлены.", - "addon.mod_forum.forumnodiscussionsyet": "В этом форуме ещё нет тем для обсуждения.", - "addon.mod_forum.group": "Группа", - "addon.mod_forum.lastpost": "Последнее сообщение", - "addon.mod_forum.message": "Сообщение", - "addon.mod_forum.modeflatnewestfirst": "Плоско, впереди новые", - "addon.mod_forum.modeflatoldestfirst": "Плоско, впереди старые", - "addon.mod_forum.modenested": "Древовидно", - "addon.mod_forum.modulenameplural": "Форумы", - "addon.mod_forum.numdiscussions": "Обсуждений - {{numdiscussions}}", - "addon.mod_forum.numreplies": "Ответов - {{numreplies}}", - "addon.mod_forum.postisprivatereply": "Это личный ответ. Он не виден другим участникам.", - "addon.mod_forum.posttoforum": "Отправить в форум", - "addon.mod_forum.posttomygroups": "Опубликовать копию для всех групп", - "addon.mod_forum.privatereply": "Личный ответ", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.refreshdiscussions": "Обновить обсуждения", - "addon.mod_forum.refreshposts": "Обновить объявления", - "addon.mod_forum.removefromfavourites": "Снять пометку с этого обсуждения", - "addon.mod_forum.reply": "Ответить", - "addon.mod_forum.replyplaceholder": "Напишите свой ответ...", - "addon.mod_forum.subject": "Тема", - "addon.mod_forum.tagarea_forum_posts": "Сообщения форума", - "addon.mod_forum.unread": "Непрочтенные", - "addon.mod_forum.unreadpostsnumber": "Непрочитанных сообщений - {{$a}}", - "addon.mod_forum.yourreply": "Ваш ответ", - "addon.mod_glossary.addentry": "Добавить новую запись", - "addon.mod_glossary.aliases": "Ключевое(ые) слово(а)", - "addon.mod_glossary.attachment": "Вложение", - "addon.mod_glossary.browsemode": "Смотреть записи", - "addon.mod_glossary.byalphabet": "Алфавитно", - "addon.mod_glossary.byauthor": "Группировать по автору", - "addon.mod_glossary.bycategory": "Группировать по категориям", - "addon.mod_glossary.bynewestfirst": "Сначала новые", - "addon.mod_glossary.byrecentlyupdated": "Недавно обновлённые", - "addon.mod_glossary.bysearch": "Поиск", - "addon.mod_glossary.cannoteditentry": "Невозможно редактировать запись", - "addon.mod_glossary.casesensitive": "Это слово чувствительно к регистру", - "addon.mod_glossary.categories": "Категории", - "addon.mod_glossary.concept": "Слово", - "addon.mod_glossary.definition": "Определение", - "addon.mod_glossary.entriestobesynced": "Записи на синзронизацию", - "addon.mod_glossary.entrypendingapproval": "Эта запись ожидает подтверждения.", - "addon.mod_glossary.entryusedynalink": "Эта запись должна автоматически связываться", - "addon.mod_glossary.errconceptalreadyexists": "Это понятие уже существует. Повторения в этом глоссарии не допускаются.", - "addon.mod_glossary.errorloadingentries": "При загрузке записей произошла ошибка.", - "addon.mod_glossary.errorloadingentry": "При загрузке записи произошла ошибка.", - "addon.mod_glossary.errorloadingglossary": "При загрузке глоссария произошла ошибка.", - "addon.mod_glossary.fillfields": "Слово и определение - поля обязательные.", - "addon.mod_glossary.fullmatch": "Определять соответствие только полным словам", - "addon.mod_glossary.linking": "Автосвязывание", - "addon.mod_glossary.modulenameplural": "Глоссарии", - "addon.mod_glossary.noentriesfound": "Записей не было найдено.", - "addon.mod_glossary.searchquery": "Запрос на поиск", - "addon.mod_glossary.tagarea_glossary_entries": "Записи глоссария", - "addon.mod_imscp.deploymenterror": "Пакет содержимого IMS с ошибкой!", - "addon.mod_imscp.modulenameplural": "Пакеты IMS содержимого", - "addon.mod_imscp.showmoduledescription": "Показать описание", - "addon.mod_imscp.toc": "Оглавление", - "addon.mod_lesson.answer": "Ответ", - "addon.mod_lesson.attempt": "Попытка: {{$a}}", - "addon.mod_lesson.attemptheader": "Попытка", - "addon.mod_lesson.attemptsremaining": "У вас осталось {{$a}} попыток", - "addon.mod_lesson.averagescore": "Средний балл", - "addon.mod_lesson.averagetime": "Среднее время", - "addon.mod_lesson.branchtable": "Список разделов", - "addon.mod_lesson.cannotfindattempt": "Ошибка: попытка не найдена", - "addon.mod_lesson.cannotfinduser": "Ошибка: не удалось найти пользователей", - "addon.mod_lesson.clusterjump": "Непросмотренный вопрос в кластере", - "addon.mod_lesson.completed": "Лекция завершена", - "addon.mod_lesson.congratulations": "Поздравления - лекция завершена", - "addon.mod_lesson.continue": "Продолжить", - "addon.mod_lesson.continuetonextpage": "Перейти к следующей странице", - "addon.mod_lesson.defaultessayresponse": "Ваше эссе будет оценено преподавателем курса.", - "addon.mod_lesson.detailedstats": "Подробная статистика", - "addon.mod_lesson.didnotanswerquestion": "Студент не ответил на этот вопрос.", - "addon.mod_lesson.displayofgrade": "Показать оценки (только для студентов)", - "addon.mod_lesson.displayscorewithessays": "

                      Вы получили {{$a.score}} баллов из {{$a.tempmaxgrade}} за вопросы, оцененные автоматически.

                      \n

                      Оценки за {{$a.essayquestions}} эссе будут поставлены и учтены позднее при выставлении итоговой оценки.

                      \n

                      Ваша текущая оценка (без учета эссе): {{$a.score}} из {{$a.grade}}

                      .", - "addon.mod_lesson.displayscorewithoutessays": "Оценка за ответы на вопросы: {{$a.score}} из {{$a.grade}}", - "addon.mod_lesson.emptypassword": "Пароль не может быть пустым", - "addon.mod_lesson.enterpassword": "Пожалуйста, введите пароль:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Вы не ответили ни на один вопрос. Вы получили 0 за эту лекцию.", - "addon.mod_lesson.errorprefetchrandombranch": "Эта лекция содержит переход на случайную страницу контента. Невозможно выполнить попытку в приложении до тех пор, пока она не будет запущена в веб-браузере.", - "addon.mod_lesson.errorreviewretakenotlast": "Эта попытка больше не может быть просмотрена, так как была сделана другая попытка.", - "addon.mod_lesson.finish": "Завершить", - "addon.mod_lesson.finishretakeoffline": "Эта попытка была завершена вне сети.", - "addon.mod_lesson.firstwrong": "Вы ответили неправильно. Хотите снова попробовать ответить на вопрос? (Даже в случае верного ответа его результат не будет засчитан в Вашей итоговой оценке.)", - "addon.mod_lesson.gotoendoflesson": "Перейти к концу лекции", - "addon.mod_lesson.grade": "Оценка", - "addon.mod_lesson.highscore": "Высший результат", - "addon.mod_lesson.hightime": "Наибольшее время", - "addon.mod_lesson.leftduringtimed": "Вы просрочили время, отведенное на прохождение лекции.
                      Щелкните по кнопке «Продолжить», чтобы начать лекцию заново.", - "addon.mod_lesson.leftduringtimednoretake": "Вы просрочили время, отведенное на прохождение лекции и не можете продолжить её или пройти заново.", - "addon.mod_lesson.lessonmenu": "Меню лекции", - "addon.mod_lesson.lessonstats": "Статистика лекции", - "addon.mod_lesson.linkedmedia": "Связанное медиа", - "addon.mod_lesson.loginfail": "Логин неверный, пожалуйста, попробуйте еще раз...", - "addon.mod_lesson.lowscore": "Низший балл", - "addon.mod_lesson.lowtime": "Наименьшее время", - "addon.mod_lesson.maximumnumberofattemptsreached": "Достигнуто максимальное количество попыток - переходим к следующей странице", - "addon.mod_lesson.modattemptsnoteacher": "Только студенты могут повторно проходить лекцию.", - "addon.mod_lesson.modulenameplural": "Лекции", - "addon.mod_lesson.noanswer": "На один или несколько вопросов не получено ответа. Пожалуйста, вернитесь и представьте ответ.", - "addon.mod_lesson.nolessonattempts": "Не было попыток пройти лекцию.", - "addon.mod_lesson.nolessonattemptsgroup": "В этой лекции члены группы ({{$a}}) не сделали ни одной попытки.", - "addon.mod_lesson.notcompleted": "Лекция не завершена", - "addon.mod_lesson.numberofcorrectanswers": "Количество правильных ответов: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Количество просмотренных страниц : {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Количество вопросов, на которые дан ответ: {{$a.nquestions}} (Вы должны ответить минимум на {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "К настоящему времени Вы заработали баллов: {{$a.score}} из {{$a.currenthigh}} возможных.", - "addon.mod_lesson.ongoingnormal": "Вы ответили правильно на {{$a.correct}} вопрос(ов) из {{$a.viewed}} просмотренных.", - "addon.mod_lesson.or": "ИЛИ", - "addon.mod_lesson.overview": "Обзор", - "addon.mod_lesson.preview": "Просмотр", - "addon.mod_lesson.progressbarteacherwarning2": "Вы не увидите индикатор выполнения, потому что можете редактировать эту лекцию.", - "addon.mod_lesson.progresscompleted": "Вы прошли {{$a}}% лекции", - "addon.mod_lesson.question": "Вопрос", - "addon.mod_lesson.rawgrade": "Предварительная оценка", - "addon.mod_lesson.reports": "Отчеты", - "addon.mod_lesson.response": "Отзыв", - "addon.mod_lesson.retakefinishedinsync": "Попытка, выполненная вне сети, была синхронизирована. Вы хотите её просмотреть?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Пересмотр", - "addon.mod_lesson.reviewlesson": "Пересмотр лекции", - "addon.mod_lesson.reviewquestionback": "Да, мне хотелось бы попробовать еще раз", - "addon.mod_lesson.reviewquestioncontinue": "Нет, я просто хочу перейти к следующему вопросу", - "addon.mod_lesson.secondpluswrong": "Не совсем правильно. Вы хотите попробовать еще раз?", - "addon.mod_lesson.submit": "Отправить", - "addon.mod_lesson.teacherjumpwarning": "В лекции используются переходы «{{$a.cluster}}» или «{{$a.unseen}}». Сейчас вместо них будет использован переход «Следующая страница». Зайдите как студент, чтобы протестировать эти переходы.", - "addon.mod_lesson.teacherongoingwarning": "Текущий балл отображается только для студентов. Чтобы протестировать текущий балл, зайдите как студент.", - "addon.mod_lesson.teachertimerwarning": "Таймер отображается только для студентов. Чтобы протестировать таймер, зайдите как студент.", - "addon.mod_lesson.thatsthecorrectanswer": "Это правильный ответ", - "addon.mod_lesson.thatsthewronganswer": "Это неправильный ответ", - "addon.mod_lesson.timeremaining": "Оставшееся время", - "addon.mod_lesson.timetaken": "Затраченное время", - "addon.mod_lesson.unseenpageinbranch": "Страница непросмотренного вопроса из раздела", - "addon.mod_lesson.warningretakefinished": "Попытка была завершена на сайте.", - "addon.mod_lesson.welldone": "Отлично!", - "addon.mod_lesson.youhaveseen": "Вы уже работали с этой лекцией.
                      Хотите продолжить с того места, на котором Вы остановились?", - "addon.mod_lesson.youranswer": "Ваш ответ", - "addon.mod_lesson.yourcurrentgradeisoutof": "Ваша текущая оценка: {{$a.grade}} из {{$a.total}}", - "addon.mod_lesson.youshouldview": "Вы должны ответить, по меньшей мере на: {{$a}}", - "addon.mod_lti.errorgetlti": "Ошибка при получении данных модуля", - "addon.mod_lti.errorinvalidlaunchurl": "Запущенный URL не действителен", - "addon.mod_lti.launchactivity": "Запустить активность", - "addon.mod_lti.modulenameplural": "Внешние приложения", - "addon.mod_page.errorwhileloadingthepage": "Ошибка при загрузке содержимого страницы", - "addon.mod_page.modulenameplural": "Страницы", - "addon.mod_quiz.answercolon": "Ответ:", - "addon.mod_quiz.attemptfirst": "Первая попытка", - "addon.mod_quiz.attemptlast": "Последняя попытка", - "addon.mod_quiz.attemptnumber": "Попытка", - "addon.mod_quiz.attemptquiznow": "Начать тестирование", - "addon.mod_quiz.attemptstate": "Состояние", - "addon.mod_quiz.cannotsubmitquizdueto": "Попытка прохождения теста не может быть отправлена по следующим причинам:", - "addon.mod_quiz.clearchoice": "Очистить мой выбор", - "addon.mod_quiz.comment": "Комментарий", - "addon.mod_quiz.completedon": "Завершен", - "addon.mod_quiz.confirmclose": "После отправки Вы больше не сможете изменить свои ответы на эту попытку.", - "addon.mod_quiz.confirmcontinueoffline": "Эта попытка не была синхронизирована с {{$a}}. Если вы продолжите попытку с другого устройства, то можете потерять данные.", - "addon.mod_quiz.confirmleavequizonerror": "При сохранении ответа возникла ошибка. Вы уверены, что хотите покинуть тест?", - "addon.mod_quiz.confirmstart": "Время на тест ограничено и равно {{$a}}. Будет идти обратный отсчет времени с момента начала вашей попытки, и вы должны завершить тест до окончания времени. Вы уверены, что хотите начать прямо сейчас?", - "addon.mod_quiz.confirmstartheader": "Тест с ограничением по времени", - "addon.mod_quiz.connectionerror": "Сетевое подключение потеряно. (Автосохранение не удалось).\n\nЗапишите все ответы, введенные на этой странице в последние несколько минут, затем попробуйте подключиться заново.\n\nПосле того, как соединение будет восстановлено, Ваши ответы должны быть сохранены и это сообщение исчезнет.", - "addon.mod_quiz.continueattemptquiz": "Продолжить последнюю попытку", - "addon.mod_quiz.continuepreview": "Продолжить последний просмотр", - "addon.mod_quiz.errorbehaviournotsupported": "Этот тест не может быть пройден в приложении, потому что поведение вопроса не поддерживается приложением.", - "addon.mod_quiz.errordownloading": "Ошибка при загрузке необходимых данных.", - "addon.mod_quiz.errorgetattempt": "Ошибка получения данных попытки.", - "addon.mod_quiz.errorgetquestions": "Ошибка получения вопросов.", - "addon.mod_quiz.errorgetquiz": "Ошибка получения данных теста.", - "addon.mod_quiz.errorparsequestions": "При чтении вопросов произошла ошибка. Попробуйте пройти этот тест в браузере.", - "addon.mod_quiz.errorquestionsnotsupported": "Данный тест не может быть пройден в приложении, потому что он содержит вопросы, не поддерживаемые приложением:", - "addon.mod_quiz.errorrulesnotsupported": "Данный тест не может быть пройден в приложении, потому что он имеет правила доступа, не поддерживаемые приложением:", - "addon.mod_quiz.errorsaveattempt": "При сохранении данных попытки возникла ошибка.", - "addon.mod_quiz.feedback": "Отзыв", - "addon.mod_quiz.finishattemptdots": "Закончить попытку...", - "addon.mod_quiz.finishnotsynced": "Завершено, но не синхронизировано", - "addon.mod_quiz.grade": "Оценка", - "addon.mod_quiz.gradeaverage": "Средняя оценка", - "addon.mod_quiz.gradehighest": "Высшая оценка", - "addon.mod_quiz.grademethod": "Метод оценивания", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Баллы", - "addon.mod_quiz.modulenameplural": "Тесты", - "addon.mod_quiz.mustbesubmittedby": "Эта попытка должна быть отправлена до {{$a}}.", - "addon.mod_quiz.noquestions": "Пока не добавлено ни одного вопроса", - "addon.mod_quiz.noreviewattempt": "Вам не разрешен просмотр этой попытки.", - "addon.mod_quiz.notyetgraded": "Еще не оценено", - "addon.mod_quiz.opentoc": "Открыть всплывающее окно с навигацией", - "addon.mod_quiz.outof": "{{$a.grade}} из {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} из {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Итоговый отзыв", - "addon.mod_quiz.overdue": "Срок закончился", - "addon.mod_quiz.overduemustbesubmittedby": "Эта попытка уже просрочена. Если Вы хотите, чтобы этот тест был оценен, необходимо отправить его {{$a}}. Если Вы не отправите попытку, то она не будет оценена.", - "addon.mod_quiz.preview": "Просмотр", - "addon.mod_quiz.previewquiznow": "Начать просмотр теста", - "addon.mod_quiz.question": "Вопрос", - "addon.mod_quiz.quiznavigation": "Навигация по тесту", - "addon.mod_quiz.quizpassword": "Пароль для доступа к тесту", - "addon.mod_quiz.reattemptquiz": "Пройти тест заново", - "addon.mod_quiz.requirepasswordmessage": "Чтобы пройти тест, вам необходимо знать пароль", - "addon.mod_quiz.returnattempt": "Вернуться к попытке", - "addon.mod_quiz.review": "Просмотр", - "addon.mod_quiz.reviewofattempt": "Просмотр попытки {{$a}}", - "addon.mod_quiz.reviewofpreview": "Обзор результатов предварительного просмотра", - "addon.mod_quiz.showall": "Отображать все вопросы на одной странице", - "addon.mod_quiz.showeachpage": "Показать одну страницу", - "addon.mod_quiz.startattempt": "Начать попытку", - "addon.mod_quiz.startedon": "Тест начат", - "addon.mod_quiz.stateabandoned": "Не отправленные", - "addon.mod_quiz.statefinished": "Завершенные", - "addon.mod_quiz.statefinisheddetails": "Отправлено {{$a}}", - "addon.mod_quiz.stateinprogress": "В процессе", - "addon.mod_quiz.stateoverdue": "Просроченные", - "addon.mod_quiz.stateoverduedetails": "Должны быть представлены {{$a}}", - "addon.mod_quiz.status": "Состояние", - "addon.mod_quiz.submitallandfinish": "Отправить всё и завершить тест", - "addon.mod_quiz.summaryofattempt": "Результат попытки", - "addon.mod_quiz.summaryofattempts": "Результаты ваших предыдущих попыток", - "addon.mod_quiz.timeleft": "Оставшееся время", - "addon.mod_quiz.timetaken": "Прошло времени", - "addon.mod_quiz.warningattemptfinished": "Попытка, совершённая вне сети, отменена, так как она была завершена на сайте или не найдена.", - "addon.mod_quiz.warningdatadiscarded": "Некоторые ответы, совершённые вне сети, были отменены, так как вопросы были изменены в сети.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Попытка не завершена, так как некоторые ответы, данные вне сети, были отменены. Пожалуйста, проверьте свои ответы и отправьте попытку заново.", - "addon.mod_quiz.yourfinalgradeis": "Ваша итоговая оценка за этот тест: {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Ошибка при загрузке контента", - "addon.mod_resource.modifieddate": "Изменено {{$a}}", - "addon.mod_resource.modulenameplural": "Файлы", - "addon.mod_resource.openthefile": "Открыть файл", - "addon.mod_resource.uploadeddate": "Загружено {{$a}}", - "addon.mod_scorm.asset": "Элемент", - "addon.mod_scorm.assetlaunched": "Элемент - Просмотрен", - "addon.mod_scorm.attempts": "Попытки", - "addon.mod_scorm.averageattempt": "Среднее попыток", - "addon.mod_scorm.browse": "Предпросмотр", - "addon.mod_scorm.browsed": "Найден", - "addon.mod_scorm.browsemode": "Режим предпросмотра", - "addon.mod_scorm.cannotcalculategrade": "Оценка не может быть посчитана", - "addon.mod_scorm.completed": "Завершено", - "addon.mod_scorm.contents": "Содержание", - "addon.mod_scorm.dataattemptshown": "Эти данные относятся к попытке номер {{number}}.", - "addon.mod_scorm.enter": "Войти", - "addon.mod_scorm.errorcreateofflineattempt": "Во время создания новой попытки вне сети возникла ошибка. Пожалуйста, попробуйте снова.", - "addon.mod_scorm.errordownloadscorm": "Ошибка загрузки SCORM: «{{name}}».", - "addon.mod_scorm.errorgetscorm": "Ошибка получения данных SCORM.", - "addon.mod_scorm.errorinvalidversion": "К сожалению, приложение поддерживает только SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Загрузка пакетов SCORM отключена. Пожалуйста, свяжитесь с администратором вашего сайта.", - "addon.mod_scorm.errornovalidsco": "Данный SCORM пакет не имеет видимого SCO для загрузки.", - "addon.mod_scorm.errorpackagefile": "К сожалению, приложение поддерживает только ZIP-архивы.", - "addon.mod_scorm.errorsyncscorm": "Во время синхронизации произошла ошибка. Пожалуйста, повторите попытку.", - "addon.mod_scorm.exceededmaxattempts": "Вы достигли максимального количества попыток.", - "addon.mod_scorm.failed": "Неудачно", - "addon.mod_scorm.firstattempt": "Первая попытка", - "addon.mod_scorm.gradeaverage": "Средняя оценка", - "addon.mod_scorm.gradeforattempt": "Оценка за попытку", - "addon.mod_scorm.gradehighest": "Высшая оценка", - "addon.mod_scorm.grademethod": "Метод оценивания", - "addon.mod_scorm.gradereported": "Оценка передана", - "addon.mod_scorm.gradescoes": "Объекты обучения", - "addon.mod_scorm.gradesum": "Суммарная оценка", - "addon.mod_scorm.highestattempt": "Лучшая попытка", - "addon.mod_scorm.incomplete": "Не завершено", - "addon.mod_scorm.lastattempt": "Завершена последняя попытка", - "addon.mod_scorm.modulenameplural": "Пакеты SCORM", - "addon.mod_scorm.newattempt": "Начать новую попытку", - "addon.mod_scorm.noattemptsallowed": "Количество попыток", - "addon.mod_scorm.noattemptsmade": "Выполнено попыток", - "addon.mod_scorm.notattempted": "Не приступал", - "addon.mod_scorm.offlineattemptnote": "В этой попытке содержатся данные, которые не были синхронизированы.", - "addon.mod_scorm.offlineattemptovermax": "Данная попытка не может быть отправлена, так как вы превысили максимальное число попыток.", - "addon.mod_scorm.organizations": "Организаций", - "addon.mod_scorm.passed": "Выполнено успешно", - "addon.mod_scorm.reviewmode": "Режим просмотра", - "addon.mod_scorm.score": "Балл", - "addon.mod_scorm.scormstatusnotdownloaded": "Данный пакет SCORM не загружен. Он загрузится автоматически, когда вы откроете его.", - "addon.mod_scorm.scormstatusoutdated": "Данный пакет SCORM был изменён с момента последней загрузки. Он будет автоматически загружен, когда вы откроете его.", - "addon.mod_scorm.suspended": "Приостановлено", - "addon.mod_scorm.toc": "Оглавление", - "addon.mod_scorm.warningofflinedatadeleted": "Некоторые данные, добавленные вне сети, из попытки {{number}} были отклонены, так как попытка не могла считаться новой.", - "addon.mod_scorm.warningsynconlineincomplete": "Некоторые попытки не могли быть синхронизированы с сайтом, так как последняя попытка в сети ещё не завершена. Пожалуйста, сначала завершите попытку в сети.", - "addon.mod_survey.cannotsubmitsurvey": "К сожалению, возникла проблема отправки вашего опроса. Пожалуйста, попробуйте еще раз", - "addon.mod_survey.errorgetsurvey": "Ошибка при получении данных опроса", - "addon.mod_survey.ifoundthat": "Я узнал, что", - "addon.mod_survey.ipreferthat": "Я предпочитаю", - "addon.mod_survey.modulenameplural": "Анкеты", - "addon.mod_survey.responses": "Ответы", - "addon.mod_survey.results": "Результаты", - "addon.mod_survey.surveycompletednograph": "Вы уже ответили на эту анкету.", - "addon.mod_url.accessurl": "Открыть URL-адрес", - "addon.mod_url.modulenameplural": "Гиперссылки", - "addon.mod_url.pointingtourl": "URL, на который указывает ресурс.", - "addon.mod_wiki.cannoteditpage": "Вы не можете редактировать эту страницу", - "addon.mod_wiki.createpage": "Создать страницу", - "addon.mod_wiki.editingpage": "Редактирование страницы «{{$a}}»", - "addon.mod_wiki.errorloadingpage": "При загрузке страницы произошла ошибка.", - "addon.mod_wiki.errornowikiavailable": "В этом модуле WIKI пока нет контента.", - "addon.mod_wiki.gowikihome": "Перейти на первую страницу wiki", - "addon.mod_wiki.map": "Карта", - "addon.mod_wiki.modulenameplural": "Вики-страницы", - "addon.mod_wiki.newpagehdr": "Новая страница", - "addon.mod_wiki.newpagetitle": "Заголовок новой страницы", - "addon.mod_wiki.nocontent": "Нет содержимого у этой страницы", - "addon.mod_wiki.notingroup": "Не в группе", - "addon.mod_wiki.pageexists": "Такая страница уже существует.", - "addon.mod_wiki.pagename": "Название страницы", - "addon.mod_wiki.subwiki": "Под-wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Страницы вики", - "addon.mod_wiki.titleshouldnotbeempty": "Заголовок не должен быть пустым", - "addon.mod_wiki.viewpage": "Просмотреть страницу", - "addon.mod_wiki.wikipage": "Страница Wiki", - "addon.mod_wiki.wrongversionlock": "Другой пользователь отредактировал страницу и Ваше содержимое устарело", - "addon.mod_workshop.alreadygraded": "Уже оценено", - "addon.mod_workshop.areainstructauthors": "Инструкции для работы", - "addon.mod_workshop.areainstructreviewers": "Инструкции по оценке", - "addon.mod_workshop.assess": "Оценить", - "addon.mod_workshop.assessedsubmission": "Оцененная работа", - "addon.mod_workshop.assessmentform": "Форма оценки", - "addon.mod_workshop.assessmentsettings": "Параметры оценки", - "addon.mod_workshop.assessmentstrategynotsupported": "Стратегия оценки {{$a}} не поддерживается", - "addon.mod_workshop.assessmentweight": "Вес оценки", - "addon.mod_workshop.assignedassessments": "Работы, представленные для оценивания", - "addon.mod_workshop.assignedassessmentsnone": "Нет работ, представленных для оценивания", - "addon.mod_workshop.conclusion": "Заключение", - "addon.mod_workshop.createsubmission": "Начало подготовки Вашей работы", - "addon.mod_workshop.deletesubmission": "Удалить ответ", - "addon.mod_workshop.editsubmission": "Редактировать работу", - "addon.mod_workshop.feedbackauthor": "Отзыв для автора", - "addon.mod_workshop.feedbackby": "Отзыв на {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Отзыв для рецензента", - "addon.mod_workshop.givengrades": "Данные оценки", - "addon.mod_workshop.gradecalculated": "Вычисленная оценка за работу", - "addon.mod_workshop.gradeinfo": "Оценка: {{$a.received}} из {{$a.max}}", - "addon.mod_workshop.gradeover": "Переопределить оценку за работу", - "addon.mod_workshop.gradesreport": "Отчет об оценках семинара", - "addon.mod_workshop.gradinggrade": "Оценка за оценивание", - "addon.mod_workshop.gradinggradecalculated": "Вычисленная оценка за оценивание", - "addon.mod_workshop.gradinggradeof": "Баллы за оценивание (из {{$a}})", - "addon.mod_workshop.gradinggradeover": "Переопределить баллы за оценивание", - "addon.mod_workshop.modulenameplural": "Семинары", - "addon.mod_workshop.nogradeyet": "Еще не оценено", - "addon.mod_workshop.notassessed": "Еще не оцененные", - "addon.mod_workshop.notoverridden": "Не переопределено", - "addon.mod_workshop.noyoursubmission": "Вы еще не отправили свою работу", - "addon.mod_workshop.overallfeedback": "Общий отзыв", - "addon.mod_workshop.publishedsubmissions": "Опубликованные работы", - "addon.mod_workshop.publishsubmission": "Публикация работы", - "addon.mod_workshop.publishsubmission_help": "Опубликованные работы доступны другим после завершения семинара.", - "addon.mod_workshop.reassess": "Переоценить", - "addon.mod_workshop.receivedgrades": "Полученные оценки", - "addon.mod_workshop.submissionattachment": "Приложение", - "addon.mod_workshop.submissioncontent": "Содержимое работы", - "addon.mod_workshop.submissiondeleteconfirm": "Вы уверены, что хотите удалить этот ответ?", - "addon.mod_workshop.submissiongrade": "Оценка за работу", - "addon.mod_workshop.submissiongradeof": "Оценка за работу (из {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Вам нужно ввести текст или присоединить файл.", - "addon.mod_workshop.submissionsreport": "Отчет об отправке ответов на семинар", - "addon.mod_workshop.submissiontitle": "Название", - "addon.mod_workshop.switchphase10": "Переключиться на фазу настройки", - "addon.mod_workshop.switchphase20": "Переключиться на фазу подачи", - "addon.mod_workshop.switchphase30": "Переключение к фазе оценивания", - "addon.mod_workshop.switchphase40": "Переключение к фазе оценивания", - "addon.mod_workshop.switchphase50": "Семинар окончен", - "addon.mod_workshop.userplan": "План семинара", - "addon.mod_workshop.userplancurrentphase": "Текущая фаза", - "addon.mod_workshop.warningassessmentmodified": "Отправка была изменена на сайте.", - "addon.mod_workshop.warningsubmissionmodified": "Оценка была изменена на сайте.", - "addon.mod_workshop.weightinfo": "Вес: {{$a}}", - "addon.mod_workshop.yourassessment": "Самооценка", - "addon.mod_workshop.yourassessmentfor": "Ваша оценка для {{$a}}", - "addon.mod_workshop.yourgrades": "Ваши оценки", - "addon.mod_workshop.yoursubmission": "Ваша работа", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Комментарий к {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Оценка для {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Критерий {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Вы должны выбрать оценку для этого аспекта", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Комментарий к {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Критерий {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Комментарий к {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Оценка для {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Утверждение {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Критерий {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Вы должны выбрать один из этих элементов", - "addon.notes.addnewnote": "Добавить заметку", - "addon.notes.coursenotes": "Заметки курса", - "addon.notes.deleteconfirm": "Удалить эту заметку?", - "addon.notes.eventnotecreated": "Заметка создана", - "addon.notes.eventnotedeleted": "Заметка удалена", - "addon.notes.nonotes": "Нет ни одной записи данного типа.", - "addon.notes.note": "Заметка", - "addon.notes.notes": "Заметки", - "addon.notes.personalnotes": "Персональные заметки", - "addon.notes.publishstate": "Контекст", - "addon.notes.sitenotes": "Заметки сайта", - "addon.notes.userwithid": "Пользователя с ID {{id}}", - "addon.notes.warningnotenotsent": "Не получилось добавить заметку(и) к курсу {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Ошибка получения уведомлений.", - "addon.notifications.markallread": "Пометить все прочитанным", - "addon.notifications.notificationpreferences": "Настройка уведомлений", - "addon.notifications.notifications": "Уведомления", - "addon.notifications.playsound": "Проигрывать звук", - "addon.notifications.therearentnotificationsyet": "Уведомлений нет.", - "assets.countries.AD": "Андорра", - "assets.countries.AE": "Объединенные Арабские Эмираты", - "assets.countries.AF": "Афганистан", - "assets.countries.AG": "Антигуа и Барбуда", - "assets.countries.AI": "Ангилья", - "assets.countries.AL": "Албания", - "assets.countries.AM": "Армения", - "assets.countries.AO": "Ангола", - "assets.countries.AQ": "Антарктида", - "assets.countries.AR": "Аргентина", - "assets.countries.AS": "Американское Самоа", - "assets.countries.AT": "Австрия", - "assets.countries.AU": "Австралия", - "assets.countries.AW": "Аруба", - "assets.countries.AX": "Аландские острова", - "assets.countries.AZ": "Азербайджан", - "assets.countries.BA": "Босния и Герцоговина", - "assets.countries.BB": "Барбадос", - "assets.countries.BD": "Бангладеш", - "assets.countries.BE": "Бельгия", - "assets.countries.BF": "Буркина-Фасо", - "assets.countries.BG": "Болгария", - "assets.countries.BH": "Бахрейн", - "assets.countries.BI": "Бурунди", - "assets.countries.BJ": "Бенин", - "assets.countries.BL": "Сен-Бартельми", - "assets.countries.BM": "Бермуды", - "assets.countries.BN": "Бруней-Даруссалам", - "assets.countries.BO": "Боливия", - "assets.countries.BQ": "Бонайре, Синт-Эстатиус и Саба", - "assets.countries.BR": "Бразилия", - "assets.countries.BS": "Багамы", - "assets.countries.BT": "Бутан", - "assets.countries.BV": "Остров Буве", - "assets.countries.BW": "Боствана", - "assets.countries.BY": "Беларусь", - "assets.countries.BZ": "Белиз", - "assets.countries.CA": "Канада", - "assets.countries.CC": "Кокосовые (Килинг) острова", - "assets.countries.CD": "Конго, Демократическая республика", - "assets.countries.CF": "Центрально-Африканская Республика", - "assets.countries.CG": "Конго", - "assets.countries.CH": "Щвейцария", - "assets.countries.CI": "Кот д'Ивуар", - "assets.countries.CK": "Острова Кука", - "assets.countries.CL": "Чили", - "assets.countries.CM": "Камерун", - "assets.countries.CN": "Китай", - "assets.countries.CO": "Колумбия", - "assets.countries.CR": "Коста-Рика", - "assets.countries.CU": "Куба", - "assets.countries.CV": "Кабо-Верде", - "assets.countries.CW": "Кюрасао", - "assets.countries.CX": "Остров Рождества", - "assets.countries.CY": "Кипр", - "assets.countries.CZ": "Чехия", - "assets.countries.DE": "Германия", - "assets.countries.DJ": "Джибути", - "assets.countries.DK": "Дания", - "assets.countries.DM": "Доминика", - "assets.countries.DO": "Доминиканская республика", - "assets.countries.DZ": "Алжир", - "assets.countries.EC": "Эквадор", - "assets.countries.EE": "Эстония", - "assets.countries.EG": "Египет", - "assets.countries.EH": "Западная Сахара", - "assets.countries.ER": "Эритрея", - "assets.countries.ES": "Испания", - "assets.countries.ET": "Эфиопия", - "assets.countries.FI": "Финляндия", - "assets.countries.FJ": "Фиджи", - "assets.countries.FK": "Фолклендские острова (Мальвинские)", - "assets.countries.FM": "Микронезия, федеративные штаты", - "assets.countries.FO": "Фарерские острова", - "assets.countries.FR": "Франция", - "assets.countries.GA": "Габон", - "assets.countries.GB": "Великобритания", - "assets.countries.GD": "Гренада", - "assets.countries.GE": "Грузия", - "assets.countries.GF": "Французская Гвиана", - "assets.countries.GG": "Гернси", - "assets.countries.GH": "Гана", - "assets.countries.GI": "Гибралтар", - "assets.countries.GL": "Гренландия", - "assets.countries.GM": "Гамбия", - "assets.countries.GN": "Гвинея", - "assets.countries.GP": "Гваделупа", - "assets.countries.GQ": "Экваториальная Гвинея", - "assets.countries.GR": "Греция", - "assets.countries.GS": "Южная Джорджия и Сандвичевы Острова", - "assets.countries.GT": "Гватемала", - "assets.countries.GU": "Гуам", - "assets.countries.GW": "Гвинея-Биссау", - "assets.countries.GY": "Гайана", - "assets.countries.HK": "Гонконг", - "assets.countries.HM": "Остров Херд и острова Макдональд", - "assets.countries.HN": "Гондурас", - "assets.countries.HR": "Хорватия", - "assets.countries.HT": "Гаити", - "assets.countries.HU": "Венгрия", - "assets.countries.ID": "Индонезия", - "assets.countries.IE": "Ирландия", - "assets.countries.IL": "Израиль", - "assets.countries.IM": "Остров Мэн", - "assets.countries.IN": "Индия", - "assets.countries.IO": "Британская территория в Индийском океане", - "assets.countries.IQ": "Ирак", - "assets.countries.IR": "Иран, исламская республика", - "assets.countries.IS": "Исландия", - "assets.countries.IT": "Италия", - "assets.countries.JE": "Джерси", - "assets.countries.JM": "Ямайка", - "assets.countries.JO": "Иордания", - "assets.countries.JP": "Япония", - "assets.countries.KE": "Кения", - "assets.countries.KG": "Киргизстан", - "assets.countries.KH": "Камбоджа", - "assets.countries.KI": "Кирибати", - "assets.countries.KM": "Коморы", - "assets.countries.KN": "Сен-Китс и Невис", - "assets.countries.KP": "Корея, народно-демократическая республика", - "assets.countries.KR": "Корея, республика", - "assets.countries.KW": "Кувейт", - "assets.countries.KY": "Каймановы острова", - "assets.countries.KZ": "Казахстан", - "assets.countries.LA": "Лаосская Народно-Демократическая Республика", - "assets.countries.LB": "Ливан", - "assets.countries.LC": "Сент-Люсия", - "assets.countries.LI": "Лихтенштейн", - "assets.countries.LK": "Шри-Ланка", - "assets.countries.LR": "Либерия", - "assets.countries.LS": "Лесото", - "assets.countries.LT": "Литва", - "assets.countries.LU": "Люксембург", - "assets.countries.LV": "Латвия", - "assets.countries.LY": "Ливия", - "assets.countries.MA": "Марокко", - "assets.countries.MC": "Монако", - "assets.countries.MD": "Молдова, республика", - "assets.countries.ME": "Черногория", - "assets.countries.MF": "Сент-Мартин", - "assets.countries.MG": "Мадагаскар", - "assets.countries.MH": "Маршалловы Острова", - "assets.countries.MK": "Северная Македония", - "assets.countries.ML": "Мали", - "assets.countries.MM": "Мьянма", - "assets.countries.MN": "Монголия", - "assets.countries.MO": "Макао", - "assets.countries.MP": "Северные Марианские острова", - "assets.countries.MQ": "Мартиника", - "assets.countries.MR": "Мавритания", - "assets.countries.MS": "Монтсеррат", - "assets.countries.MT": "Мальта", - "assets.countries.MU": "Маврикий", - "assets.countries.MV": "Мальдивы", - "assets.countries.MW": "Малави", - "assets.countries.MX": "Мексика", - "assets.countries.MY": "Малайзия", - "assets.countries.MZ": "Мозамбик", - "assets.countries.NA": "Намибия", - "assets.countries.NC": "Новая Каледония", - "assets.countries.NE": "Нигер", - "assets.countries.NF": "Остров Норфолк", - "assets.countries.NG": "Нигерия", - "assets.countries.NI": "Никарагуа", - "assets.countries.NL": "Нидерланды", - "assets.countries.NO": "Норвегия", - "assets.countries.NP": "Непал", - "assets.countries.NR": "Науру", - "assets.countries.NU": "Ниуэ", - "assets.countries.NZ": "Новая Зеландия", - "assets.countries.OM": "Оман", - "assets.countries.PA": "Панама", - "assets.countries.PE": "Перу", - "assets.countries.PF": "Французская Полинезия", - "assets.countries.PG": "Папуа — Новая Гвинея", - "assets.countries.PH": "Филиппины", - "assets.countries.PK": "Пакистан", - "assets.countries.PL": "Польша", - "assets.countries.PM": "Сент-Пьер и Микелон", - "assets.countries.PN": "Питкерн", - "assets.countries.PR": "Пуэрто-Рико", - "assets.countries.PS": "Палестина", - "assets.countries.PT": "Португалия", - "assets.countries.PW": "Палау", - "assets.countries.PY": "Парагвай", - "assets.countries.QA": "Катар", - "assets.countries.RE": "Реюньон", - "assets.countries.RO": "Румыния", - "assets.countries.RS": "Сербия", - "assets.countries.RU": "Россия", - "assets.countries.RW": "Руанда", - "assets.countries.SA": "Саудовская Аравия", - "assets.countries.SB": "Соломоновы Острова", - "assets.countries.SC": "Сейшелы", - "assets.countries.SD": "Судан", - "assets.countries.SE": "Швеция", - "assets.countries.SG": "Сингапур", - "assets.countries.SH": "Остров Святой Елены, Вознесения и Тристан-да-Кунья", - "assets.countries.SI": "Словения", - "assets.countries.SJ": "Шпицберген и Ян Майен", - "assets.countries.SK": "Словакия", - "assets.countries.SL": "Сьерра-Леоне", - "assets.countries.SM": "Сан-Марино", - "assets.countries.SN": "Сенегал", - "assets.countries.SO": "Сомали", - "assets.countries.SR": "Суринам", - "assets.countries.SS": "Южный Судан", - "assets.countries.ST": "Сан-Томе и Принсипи", - "assets.countries.SV": "Сальвадор", - "assets.countries.SX": "Синт-Маартен (Голландская часть)", - "assets.countries.SY": "Сирийская Арабская Республика", - "assets.countries.SZ": "Свазиленд", - "assets.countries.TC": "Острова Теркс и Кайкос", - "assets.countries.TD": "Чад", - "assets.countries.TF": "Французские Южные территории", - "assets.countries.TG": "Того", - "assets.countries.TH": "Таиланд", - "assets.countries.TJ": "Таджикистан", - "assets.countries.TK": "Токелау", - "assets.countries.TL": "Восточный Тимор", - "assets.countries.TM": "Туркмения", - "assets.countries.TN": "Тунис", - "assets.countries.TO": "Тонга", - "assets.countries.TR": "Турция", - "assets.countries.TT": "Тринидад и Тобаго", - "assets.countries.TV": "Тувалу", - "assets.countries.TW": "Тайвань (Китай)", - "assets.countries.TZ": "Танзания, объединенная республика", - "assets.countries.UA": "Украина", - "assets.countries.UG": "Уганда", - "assets.countries.UM": "Малые Удаленные Острова Соединенных Штатов", - "assets.countries.US": "Соединенные Штаты Америки", - "assets.countries.UY": "Уругвай", - "assets.countries.UZ": "Узбекистан", - "assets.countries.VA": "Ватикан (Папский Престол)", - "assets.countries.VC": "Сент-Винсент и Гренадины", - "assets.countries.VE": "Венесуэла (Боливарианская Республика)", - "assets.countries.VG": "Виргинские острова, Британские", - "assets.countries.VI": "Виргинские острова, США", - "assets.countries.VN": "Вьетнам", - "assets.countries.VU": "Вануату", - "assets.countries.WF": "Уоллис и Футуна", - "assets.countries.WS": "Самоа", - "assets.countries.YE": "Йемен", - "assets.countries.YT": "Майотта", - "assets.countries.ZA": "Южная Африка", - "assets.countries.ZM": "Замбия", - "assets.countries.ZW": "Зимбабве", - "assets.mimetypes.application/epub_zip": "Электронная книга EPUB", - "assets.mimetypes.application/msword": "документ Word", - "assets.mimetypes.application/pdf": "документ PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Резервная копия Moodle", - "assets.mimetypes.application/vnd.ms-excel": "электронная таблица Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Книга Excel 2007 с поддержкой макросов", - "assets.mimetypes.application/vnd.ms-powerpoint": "презентация PowerPoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Электронная таблица OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Шаблон электронной таблицы OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Текстовый документ OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Шаблон текстового документа OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Шаблон веб-страницы OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Презентация PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Слайд-шоу PowerPoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Таблица Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Шаблон Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Документ Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Презентация iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Электронная таблица iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Страницы документа iWork", - "assets.mimetypes.application/x-javascript": "Исходный код JavaScript", - "assets.mimetypes.application/x-mspublisher": "Документ Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Flash-анимация", - "assets.mimetypes.application/xhtml_xml": "Документ XHTML", - "assets.mimetypes.archive": "Архив ({{$a.EXT}})", - "assets.mimetypes.audio": "Аудио-файл ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Файл", - "assets.mimetypes.group:archive": "Архивные файлы", - "assets.mimetypes.group:audio": "Аудиофайлы", - "assets.mimetypes.group:document": "Файлы Document", - "assets.mimetypes.group:html_audio": "Аудиофайлы, поддерживаемые браузерами", - "assets.mimetypes.group:html_track": "Файлы отслеживания HTML (HTML track)", - "assets.mimetypes.group:html_video": "Видеофайлы, поддерживаемые браузерами", - "assets.mimetypes.group:image": "Файлы изображения", - "assets.mimetypes.group:presentation": "Файлы презентации", - "assets.mimetypes.group:sourcecode": "Исходный код", - "assets.mimetypes.group:spreadsheet": "Файлы Электронных таблиц", - "assets.mimetypes.group:video": "Видеофайлы", - "assets.mimetypes.group:web_audio": "Аудиофайлы из сети Интернет", - "assets.mimetypes.group:web_file": "Веб-файлы", - "assets.mimetypes.group:web_image": "Файлы изображений из сети Интернет", - "assets.mimetypes.group:web_video": "Видеофайлы из сети Интернет", - "assets.mimetypes.image": "Изображение ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Значок Windows", - "assets.mimetypes.text/css": "Каскадная таблица стилей (CSS)", - "assets.mimetypes.text/csv": "Значения, разделенные запятыми", - "assets.mimetypes.text/html": "HTML-документ", - "assets.mimetypes.text/plain": "Текстовый файл", - "assets.mimetypes.text/rtf": "документ RTF", - "assets.mimetypes.text/vtt": "Текстовая дорожка веб-видео", - "assets.mimetypes.video": "Видео файл ({{$a.EXT}})", - "core.accounts": "Учетные записи", - "core.add": "Добавить", - "core.agelocationverification": "Проверка возраста и местонахождения", - "core.ago": "{{$a}} назад", - "core.all": "Все", - "core.allgroups": "Все группы", - "core.allparticipants": "Все участники", - "core.answer": "Ответ", - "core.answered": "Отвечено", - "core.areyousure": "Вы уверены?", - "core.back": "Назад", - "core.block.blocks": "Блоки", - "core.cancel": "Отмена", - "core.cannotconnect": "Не удается подключиться: Убедитесь, что вы правильно ввели URL-адрес и что ваш сайт использует Moodle {{$a}} или более поздней версии.", - "core.cannotdownloadfiles": "Загрузка файлов отключена. Пожалуйста, свяжитесь с администратором вашего сайта.", - "core.captureaudio": "Записать аудио", - "core.capturedimage": "Сделанное изображение", - "core.captureimage": "Сделать изображение", - "core.capturevideo": "Записать видео", - "core.category": "Категория", - "core.choose": "Выбрать", - "core.choosedots": "Выберите...", - "core.clearsearch": "Очистить поиск", - "core.clicktohideshow": "Нажмите, чтобы раскрыть или скрыть", - "core.clicktoseefull": "Нажать, чтобы посмотреть полное содержимое.", - "core.close": "Закрыть", - "core.comments": "Комментарии", - "core.comments.addcomment": "Добавить комментарий...", - "core.comments.comments": "Комментарии", - "core.comments.commentscount": "Комментарии ({{$a}})", - "core.comments.deletecommentbyon": "Удалить комментарий, размещенный пользователем {{$a.user}} в {{$a.time}}", - "core.comments.eventcommentcreated": "Комментарий создан", - "core.comments.eventcommentdeleted": "Комментарий удален", - "core.comments.nocomments": "Нет комментариев", - "core.comments.savecomment": "Сохранить комментарий", - "core.commentscount": "Комментарии ({{$a}})", - "core.completion-alt-auto-fail": "Выполнено: {{$a}} (оценка ниже проходного балла)", - "core.completion-alt-auto-n": "Не выполнено: {{$a}}", - "core.completion-alt-auto-n-override": "Не выполнено: {{$a.modname}} (отметил(а) {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Выполнено: {{$a}} (оценка выше проходного балла)", - "core.completion-alt-auto-y": "Выполнено: {{$a}}", - "core.completion-alt-auto-y-override": "Выполнено: {{$a.modname}} (отметил(а) {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Не выполнено: {{$a}}. Выберите, чтобы отметить элемент как выполненный.", - "core.completion-alt-manual-n-override": "Не выполнено: {{$a.modname}} (отметил(а) {{$a.overrideuser}}). Выберите, чтобы отметить элемент как выполненный.", - "core.completion-alt-manual-y": "Выполнено: {{$a}}. Выберите, чтобы отметить элемент курса как невыполненный.", - "core.completion-alt-manual-y-override": "Выполнено: {{$a.modname}} (отметил(а) {{$a.overrideuser}}). Выберите, чтобы отметить элемент курса как невыполненный.", - "core.confirmcanceledit": "Вы уверены, что хотите покинуть эту страницу? Все изменения будут потеряны.", - "core.confirmdeletefile": "Вы уверены, что хотите удалить это файл?", - "core.confirmloss": "Вы уверены? Все изменения будут удалены.", - "core.confirmopeninbrowser": "Вы хотите открыть это в веб-браузере?", - "core.considereddigitalminor": "Вы слишком молоды, чтобы создать учетную запись на этом сайте.", - "core.content": "Содержимое", - "core.contenteditingsynced": "Содержимое, которое вы редактируете, было синхронизировано.", - "core.contentlinks.chooseaccount": "Выберите учетную запись", - "core.contentlinks.chooseaccounttoopenlink": "Выбрать учётную запись для перехода по ссылке.", - "core.contentlinks.confirmurlothersite": "Эта ссылка ведёт на другой сайт. Вы хотите перейти по ней?", - "core.contentlinks.errornoactions": "Не удалось найти действие для работы с этой ссылкой.", - "core.contentlinks.errornosites": "Не удалось найти сайт, на который ведёт ссылка.", - "core.continue": "Продолжить", - "core.copiedtoclipboard": "Текст скопирован в буфер обмена.", - "core.course": "Курс", - "core.course.activitydisabled": "Ваша организация отключила данный активный элемент в мобильном приложении.", - "core.course.activitynotyetviewableremoteaddon": "Ваша организация установила плагин, который ещё не поддерживается.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Установка Moodle вашей организации нуждается в обновлении.", - "core.course.allsections": "Все разделы", - "core.course.askadmintosupport": "Свяжитесь с администратором сайта и скажите ему, что хотите использовать активный элемент в приложении Moodle Mobile.", - "core.course.confirmdeletemodulefiles": "Вы уверены, что хотите удалить эти файлы?", - "core.course.confirmdownload": "Вы собираетесь загрузить {{size}}. Вы уверены, что хотите продолжить?", - "core.course.confirmdownloadunknownsize": "Оценить размер загружаемых данных не удалось. Вы уверены, что хотите продолжить?", - "core.course.confirmpartialdownloadsize": "Вы собираетесь загрузить по меньшей мере {{size}}. Вы уверены, что хотите продолжить?", - "core.course.contents": "Содержание", - "core.course.couldnotloadsectioncontent": "Не удалось загрузить содержимое раздела. Пожалуйста, повторите попытку позже.", - "core.course.couldnotloadsections": "Не удалось загрузить разделы. Пожалуйста, повторите попытку позже.", - "core.course.coursesummary": "Описание курса", - "core.course.downloadcourse": "Скачать курс", - "core.course.errordownloadingsection": "Ошибка загрузки раздела.", - "core.course.errorgetmodule": "Ошибка получения данных активного элемента.", - "core.course.hiddenfromstudents": "Скрыто от студентов", - "core.course.hiddenoncoursepage": "Доступно, но не показано на странице курса", - "core.course.nocontentavailable": "Нет контента, доступного в данный момент", - "core.course.overriddennotice": "Ваша итоговая оценка за этот элемента курса была скорректирована вручную.", - "core.course.sections": "Разделы", - "core.course.useactivityonbrowser": "Вы по прежнему можете пользоваться этим, используя веб-браузер своего устройства.", - "core.coursedetails": "Информация о курсе", - "core.courses.addtofavourites": "Пометить этот курс", - "core.courses.allowguests": "Курс доступен гостю", - "core.courses.availablecourses": "Доступные курсы", - "core.courses.cannotretrievemorecategories": "Категории глубже уровня {{$a}} не могут быть получены.", - "core.courses.categories": "Категории курсов", - "core.courses.confirmselfenrol": "Вы уверены, что хотите записать себя на этот курс?", - "core.courses.courses": "Курсы", - "core.courses.enrolme": "Записать меня", - "core.courses.errorloadcategories": "Во время загрузки категорий произошла ошибка.", - "core.courses.errorloadcourses": "При загрузке курсов произошла ошибка.", - "core.courses.errorsearching": "Во время поиска произошла ошибка.", - "core.courses.errorselfenrol": "Во время самостоятельной записи произошла ошибка.", - "core.courses.filtermycourses": "Поиск моих курсов", - "core.courses.frontpage": "Главная страница", - "core.courses.hidecourse": "Скрыть из вида", - "core.courses.ignore": "Игнорировать", - "core.courses.mycourses": "Мои курсы", - "core.courses.mymoodle": "Личный кабинет", - "core.courses.nocourses": "Нет информации для отображения.", - "core.courses.nocoursesyet": "В этой категории нет курсов", - "core.courses.nosearchresults": "Нет результатов", - "core.courses.notenroled": "Вы не записаны на этот курс", - "core.courses.notenrollable": "Вы не можете самостоятельно записаться на этот курс.", - "core.courses.password": "Кодовое слово", - "core.courses.paymentrequired": "Для регистрации на этом курсе требуется оплата", - "core.courses.paypalaccepted": "Принятые платежи PayPal", - "core.courses.reload": "Обновить", - "core.courses.removefromfavourites": "Снять отметку с курса", - "core.courses.search": "Найти", - "core.courses.searchcourses": "Поиск курса", - "core.courses.searchcoursesadvice": "Вы можете использовать кнопку поиска по курсам, чтобы найти курсы для гостевого доступа к ним или записать себя на курсы, которые это разрешают.", - "core.courses.selfenrolment": "Самостоятельная запись", - "core.courses.sendpaymentbutton": "Оплатить через PayPal", - "core.courses.show": "Показать этот курс", - "core.courses.totalcoursesearchresults": "Всего курсов: {{$a}}", - "core.currentdevice": "Данное устройство", - "core.datastoredoffline": "Данные сохранены на устройстве, потому что не могут быть отправлены. Они будут автоматически отправлены позже.", - "core.date": "Дата", - "core.day": "день", - "core.days": "дн.", - "core.decsep": ",", - "core.defaultvalue": "Значение по умолчанию ({{$a}})", - "core.delete": "Удалить", - "core.deletedoffline": "Удалено вне сети", - "core.deleteduser": "Удаленный пользователь", - "core.deleting": "Удаление", - "core.description": "Описание", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Цифровой возраст совершеннолетия", - "core.digitalminor_desc": "Чтобы создать учетную запись на этом сайте, пожалуйста, попросите родителя/опекуна связаться со следующим лицом:", - "core.discard": "Сбросить", - "core.dismiss": "Распустить", - "core.displayoptions": "Показать варианты", - "core.done": "Завершено", - "core.download": "Скачать", - "core.downloading": "Загрузка", - "core.edit": "Редактировать", - "core.editor.autosavesucceeded": "Черновик сохранен.", - "core.editor.bold": "Полужирный", - "core.editor.clear": "Очистить форматирование", - "core.editor.h3": "Заголовок (большой)", - "core.editor.h4": "Заголовок (средний)", - "core.editor.h5": "Заголовок (маленький)", - "core.editor.italic": "Курсив", - "core.editor.orderedlist": "Нумерованный список", - "core.editor.p": "Абзац", - "core.editor.strike": "Перечеркнутый", - "core.editor.textrecovered": "Черновой вариант этого текста был автоматически восстановлен.", - "core.editor.underline": "Подчеркивание", - "core.editor.unorderedlist": "Ненумерованный список", - "core.emptysplit": "Эта страница отобразится незаполненной, если левая панель пуста или загружается.", - "core.error": "Ошибка", - "core.errorchangecompletion": "Во время изменения статуса завершения возникла ошибка. Пожалуйста, попробуйте снова.", - "core.errordeletefile": "Ошибка при удалении файла. Пожалуйста, попробуйте снова.", - "core.errordownloading": "Ошибка загрузки файла", - "core.errordownloadingsomefiles": "Ошибка загрузки файлов. Некоторые файлы могут отсутствовать.", - "core.errorfileexistssamename": "Файл с таким именем уже существует.", - "core.errorinvalidform": "Форма содержит неверные данные. Пожалуйста, проверьте, что все требуемые поля заполнены и что данные верны.", - "core.errorinvalidresponse": "Получен некорректный ответ. Пожалуйста, свяжитесь с администратором вашего сайта, если при последующих попытках ошибка не пропадёт.", - "core.errorloadingcontent": "Ошибка загрузки содержимого.", - "core.erroropenfilenoapp": "Ошибка открытия файла: не найдено приложение для открытия данного типа файла.", - "core.erroropenfilenoextension": "Ошибка открытия файла: файл не имеет расширения.", - "core.erroropenpopup": "Это действие пытается открыть всплывающее окно. Это не поддерживается в приложении.", - "core.errorrenamefile": "Ошибка переименовывания файла. Пожалуйста, попытайтесь снова.", - "core.errorsync": "При синхронизации возникла ошибка. Пожалуйста, попробуйте снова.", - "core.errorsyncblocked": "В данный момент это {{$a}} не может быть синхронизировано по причине выполняющегося процесса. Пожалуйста, попытайтесь ещё раз позже. Если при последующих попытках ошибка не пропадёт, попробуйте перезапустить приложение.", - "core.explanationdigitalminor": "Эта информация необходима для определения того, превышает ли ваш возраст цифровой возраст согласия. Это возраст, когда человек может соглашаться с условиями и его данные будут храниться и обрабатываться на законных основаниях.", - "core.favourites": "Помеченные", - "core.filename": "Имя файла", - "core.filenameexist": "Имя файла уже существует: {{$a}}", - "core.filenotfound": "Извините, файл не найден.", - "core.fileuploader.addfiletext": "Добавить файл", - "core.fileuploader.audio": "Аудио", - "core.fileuploader.camera": "Камера", - "core.fileuploader.confirmuploadfile": "Вы собираетесь загрузить {{size}}. Вы уверены, что хотите продолжить?", - "core.fileuploader.confirmuploadunknownsize": "Оценить размер загружаемых на сервер данных не удалось. Вы уверены, что хотите продолжить?", - "core.fileuploader.errorcapturingaudio": "Ошибка захвата звука", - "core.fileuploader.errorcapturingimage": "Ошибка захвата изображения", - "core.fileuploader.errorcapturingvideo": "Ошибка захвата видео", - "core.fileuploader.errorgettingimagealbum": "Ошибка получения изображения из альбома", - "core.fileuploader.errormustbeonlinetoupload": "Вы должны быть на сайте, чтобы загружать файлы.", - "core.fileuploader.errornoapp": "У Вас не установлено приложение для выполнения этого действия.", - "core.fileuploader.errorreadingfile": "Ошибка при чтении файла", - "core.fileuploader.errorwhileuploading": "Произошла ошибка во время загрузки файла", - "core.fileuploader.file": "Файл", - "core.fileuploader.filesofthesetypes": "Поддерживаемые типы файлов:", - "core.fileuploader.fileuploaded": "Файл загружен", - "core.fileuploader.invalidfiletype": "Использование файлов типа «{{$a}}» не разрешено.", - "core.fileuploader.maxbytesfile": "Файл {{$a.file}} слишком большой. Максимальный размер, который вы можете загрузить на сервер {{$a.size}}.", - "core.fileuploader.more": "Просмотр записи", - "core.fileuploader.photoalbums": "Фотоальбомы", - "core.fileuploader.readingfile": "Чтение файла", - "core.fileuploader.selectafile": "Выбрать файл", - "core.fileuploader.uploadafile": "Загрузка файла", - "core.fileuploader.uploading": "Загрузка", - "core.fileuploader.uploadingperc": "Загрузка на сервер {{$a}}%", - "core.fileuploader.video": "Видео", - "core.filter": "Фильтр", - "core.folder": "Папка", - "core.forcepasswordchangenotice": "Вы должны изменить свой пароль.", - "core.fulllistofcourses": "Все курсы", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Средняя оценка", - "core.grades.badgrade": "Указана некорректная оценка", - "core.grades.contributiontocoursetotal": "Вклад в итог курса", - "core.grades.feedback": "Отзыв", - "core.grades.grade": "Оценка", - "core.grades.gradeitem": "Элемент оценивания", - "core.grades.grades": "Оценки", - "core.grades.lettergrade": "Буквенная оценка", - "core.grades.nogradesreturned": "Нет оценок", - "core.grades.nooutcome": "Нет показателя", - "core.grades.percentage": "Проценты", - "core.grades.range": "Диапазон", - "core.grades.rank": "Место", - "core.grades.weight": "Вес", - "core.group": "Группа", - "core.groupsseparate": "Изолированные группы", - "core.groupsvisible": "Видимые группы", - "core.hasdatatosync": "Это {{$a}} имеет данные, добавленные вне сети, для синхронизации.", - "core.help": "Справка", - "core.hide": "Скрыть", - "core.hour": "ч.", - "core.hours": "час.", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Изображение", - "core.imageviewer": "Средство отображения изображений", - "core.info": "Информация", - "core.invalidformdata": "Неверная форма данных", - "core.labelsep": ":", - "core.lastaccess": "Последний вход", - "core.lastdownloaded": "Последнее загруженное", - "core.lastmodified": "Последнее изменение", - "core.lastsync": "Последняя синхронизация", - "core.layoutgrid": "Сетка", - "core.list": "Список", - "core.listsep": ";", - "core.loading": "Загрузка", - "core.loadmore": "Загрузить больше", - "core.location": "Размещение", - "core.login.auth_email": "Самостоятельная регистрация по электронной почте", - "core.login.authenticating": "Аутентификация", - "core.login.cancel": "Отмена", - "core.login.changepassword": "Изменить пароль", - "core.login.confirmdeletesite": "Вы уверены, что хотите удалить сайт {{sitename}}?", - "core.login.connect": "Подключено!", - "core.login.connecttomoodle": "Подключение к Moodle", - "core.login.contactyouradministrator": "Свяжитесь с администратором вашего сайта для дальнейшей помощи.", - "core.login.contactyouradministratorissue": "Пожалуйста, попросите администратора вашего сайта проверить следующую проблему: {{$a}}", - "core.login.createaccount": "Создать мой новый аккаунт", - "core.login.createuserandpass": "Выберите имя пользователя и пароль", - "core.login.credentialsdescription": "Пожалуйста, укажите Ваш логин и пароль для входа.", - "core.login.emailconfirmsent": "На указанный Вами адрес электронной почты ({{$a}}) было отправлено письмо с простыми инструкциями для завершения регистрации.\n Если у вас появятся проблемы с регистрацией, свяжитесь с администратором сайта.", - "core.login.emailconfirmsentsuccess": "Письмо подтверждения отправлено успешно", - "core.login.emailnotmatch": "Адреса электронной почты не совпадают", - "core.login.erroraccesscontrolalloworigin": "Запрос «Cross-Origin», который вы пытаетесь выполнить, отклонён. Пожалуйста, проверьте https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "При удалении этой страницы произошла ошибка. Пожалуйста, попробуйте еще раз.", - "core.login.errorupdatesite": "При обновлении ключа сайта произошла ошибка.", - "core.login.firsttime": "Вы в первый раз на нашем сайте?", - "core.login.forcepasswordchangenotice": "Вы должны изменить свой пароль.", - "core.login.forgotten": "Забыли логин или пароль?", - "core.login.help": "Справка", - "core.login.helpmelogin": "

                      Во всем мире есть тысячи сайтов Moodle. Это приложение может подключаться только к тем сайтам Moodle, на которых явно разрешен доступ мобильному приложению.

                      Если Вы не можете подключиться к вашему сайту Moodle, то необходимо связаться с администратором вашего сайта и попросить его прочитать http://docs.moodle.org/en/Mobile_app

                      Чтобы проверить приложение на демонстрационном сайте Moodle, введите teacher или student в поле Адрес сайта и нажмите Кнопку подключения.

                      ", - "core.login.instructions": "Инструкции", - "core.login.invalidaccount": "Пожалуйста, проверьте свои регистрационные данные или обратитесь к администратору сайта, чтобы он проверил настройки сайта.", - "core.login.invaliddate": "Неверная дата", - "core.login.invalidemail": "Некорректный формат адреса электронной почты", - "core.login.invalidmoodleversion": "Неверная версия Moodle. Минимальная требуемая версия - {{$a}}.", - "core.login.invalidsite": "URL-адрес сайта недействителен.", - "core.login.invalidtime": "Некорректное время", - "core.login.invalidurl": "Указан неправильный URL-адрес.", - "core.login.invalidvaluemax": "Максимальное значение: {{$a}}", - "core.login.invalidvaluemin": "Минимальное значение: {{$a}}", - "core.login.localmobileunexpectedresponse": "Проверка Moodle Mobile Additional Features вернула неожиданный ответ. Вы будете аутентифицированы, используя стандартные мобильные сервисы.", - "core.login.loggedoutssodescription": "Вам необходимо аутентифицироваться заново. Вам нужно войти на сайт под своей учётной записью в окне браузера.", - "core.login.login": "Вход", - "core.login.loginbutton": "Войти", - "core.login.logininsiterequired": "Вы должны войти на сайт в окне браузера.", - "core.login.loginsteps": "Для полноценного доступа к этому сайту Вам необходимо сначала создать учетную запись.", - "core.login.missingemail": "Заполните поле", - "core.login.missingfirstname": "Заполните поле", - "core.login.missinglastname": "Заполните поле", - "core.login.mobileservicesnotenabled": "Мобильный доступ отключен на вашем сайте. Пожалуйста, обратитесь к администратору вашего сайта, если вы считаете что мобильный доступ должен быть включен.", - "core.login.mustconfirm": "Необходимо подтвердить учетную запись", - "core.login.newaccount": "Новая учетная запись", - "core.login.notloggedin": "Вы должны быть идентифицированы.", - "core.login.password": "Пароль", - "core.login.passwordforgotten": "Восстановление забытого пароля", - "core.login.passwordforgotteninstructions2": "Для сброса пароля укажите ниже Ваш логин или адрес электронной почты. Если Ваша учетная запись есть в базе данных, на Ваш адрес электронной почты будет отправлено письмо, содержащее инструкции по восстановлению доступа.", - "core.login.passwordrequired": "Требуется пароль", - "core.login.policyaccept": "Я понял(а) и согласен(на)", - "core.login.policyagree": "Чтобы продолжить работу с этим сайтом, Вы должны принять Пользовательское соглашение. Вы согласны?", - "core.login.policyagreement": "Пользовательское соглашение", - "core.login.policyagreementclick": "Ссылка на пользовательское соглашение", - "core.login.potentialidps": "Войти с использованием учетной записи:", - "core.login.profileinvaliddata": "Некорректное значение", - "core.login.recaptchachallengeimage": "картинка испытания reCAPTCHA", - "core.login.reconnect": "Переподключение", - "core.login.reconnectdescription": "Ваш ключ аутентификации недействителен или срок его действия истек. Вам придется заново подключиться к сайту.", - "core.login.reconnectssodescription": "Ваш ключ аутентификации недействителен или срок его действия истек. Вам придется заново зайти на сайт в окне браузера.", - "core.login.resendemail": "Отправить письмо еще раз", - "core.login.searchby": "Искать по:", - "core.login.security_question": "Секретный вопрос", - "core.login.selectacountry": "Выберите страну", - "core.login.selectsite": "Пожалуйста, выберите ваш сайт:", - "core.login.signupplugindisabled": "{{$a}} не включено.", - "core.login.siteaddress": "Адрес сайта", - "core.login.siteinmaintenance": "Ваш сайт находится в режиме обслуживания", - "core.login.sitepolicynotagreederror": "Политика сайта не принята.", - "core.login.siteurl": "URL-адрес сайта", - "core.login.siteurlrequired": "Требуется URL-адрес сайта, напр. http://www.yourmoodlesite.org", - "core.login.startsignup": "Создать учетную запись", - "core.login.stillcantconnect": "Всё ещё не можете подключиться?", - "core.login.supplyinfo": "Заполните информацию о себе", - "core.login.username": "Логин", - "core.login.usernameoremail": "Введите логин или адрес электронной почты", - "core.login.usernamerequired": "Требуется логин", - "core.login.usernotaddederror": "Пользователь не добавлен - ошибка", - "core.login.visitchangepassword": "Вы хотите посетить сайт, чтобы сменить пароль?", - "core.login.webservicesnotenabled": "Сетевые службы не включены на сайте. Пожалуйста, обратитесь к администратору вашего сайта, если вы считаете, что они должны быть включены.", - "core.lostconnection": "Ваш ключ аутентификации недействителен или просрочен. Вам придется повторно подключиться к сайту.", - "core.mainmenu.changesite": "Сменить сайт", - "core.mainmenu.help": "Справка", - "core.mainmenu.logout": "Выход", - "core.mainmenu.website": "Сайт", - "core.maxsizeandattachments": "Максимальный размер новых файлов: {{$a.size}}, максимальное количество прикрепленных файлов: {{$a.attachments}}", - "core.min": "мин.", - "core.mins": "мин.", - "core.misc": "Разное", - "core.mod_assign": "Задание", - "core.mod_assignment": "Задание 2.2 (Отключено)", - "core.mod_book": "Книга", - "core.mod_chat": "Чат", - "core.mod_choice": "Опрос", - "core.mod_data": "База данных", - "core.mod_database": "База данных", - "core.mod_external-tool": "Внешний инструмент", - "core.mod_feedback": "Обратная связь", - "core.mod_file": "Файл", - "core.mod_folder": "Папка", - "core.mod_forum": "Форум", - "core.mod_glossary": "Глоссарий", - "core.mod_ims": "Пакет IMS содержимого", - "core.mod_imscp": "Пакет IMS содержимого", - "core.mod_label": "Пояснение", - "core.mod_lesson": "Лекция", - "core.mod_lti": "Внешний инструмент", - "core.mod_page": "Страница", - "core.mod_quiz": "Тест", - "core.mod_resource": "Файл", - "core.mod_scorm": "Пакет SCORM", - "core.mod_survey": "Анкета", - "core.mod_url": "Гиперссылка", - "core.mod_wiki": "Вики", - "core.mod_workshop": "Семинар", - "core.moduleintro": "Описание", - "core.more": "подробнее", - "core.mygroups": "Мои группы", - "core.name": "Название", - "core.networkerrormsg": "С подключением к сайту была проблема. Пожалуйста, проверьте ваше соединение и попытайтесь снова.", - "core.never": "Никогда", - "core.next": "Далее", - "core.no": "Нет", - "core.nocomments": "Нет комментариев", - "core.nograde": "Без оценки", - "core.none": "Пусто", - "core.nopasswordchangeforced": "Вы не можете продолжить, не поменяв свой пароль.", - "core.nopermissions": "Извините, но у Вас нет прав сделать это ({{$a}})", - "core.noresults": "Нет результатов", - "core.noselection": "Ничего не выбрано", - "core.notapplicable": "н/д", - "core.notenrolledprofile": "Этот профиль не доступен, потому что пользователь не записан на этот курс.", - "core.notice": "Уведомление", - "core.notingroup": "Извините, но Вы должны быть участником группы, чтобы видеть эту страницу.", - "core.notsent": "Не отправлено", - "core.now": "сейчас", - "core.numwords": "всего слов - {{$a}}", - "core.offline": "Вне сайта", - "core.ok": "OK", - "core.online": "На сайте", - "core.openfullimage": "Нажмите здесь, чтобы отобразить полноразмерное изображение", - "core.openinbrowser": "Открыть в браузере", - "core.othergroups": "Другие группы", - "core.pagea": "Страница {{$a}}", - "core.paymentinstant": "Используйте кнопку, чтобы произвести оплату и зарегистрироваться в течение нескольких минут!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Телефон", - "core.pictureof": "Изображение пользователя {{$a}}", - "core.previous": "Назад", - "core.proceed": "Перейти", - "core.pulltorefresh": "Потяните, чтобы обновить", - "core.question.answer": "Ответ", - "core.question.answersaved": "Ответ сохранен", - "core.question.certainty": "Уверенность", - "core.question.complete": "Выполнен", - "core.question.correct": "Верно", - "core.question.errorattachmentsnotsupported": "Приложение пока не поддерживает прикрепление файлов к ответом.", - "core.question.errorinlinefilesnotsupported": "Приложение пока не поддерживает редактирование inline-файлов.", - "core.question.errorquestionnotsupported": "Данный тип вопросов пока не поддерживается приложением: {{$a}}.", - "core.question.feedback": "Отзыв", - "core.question.howtodraganddrop": "Нажмите, чтобы выбрать, затем нажмите, чтобы сбросить.", - "core.question.incorrect": "Неверно", - "core.question.information": "Информация", - "core.question.invalidanswer": "Неполный ответ", - "core.question.notanswered": "Нет ответа", - "core.question.notyetanswered": "Пока нет ответа", - "core.question.partiallycorrect": "Частично правильный", - "core.question.questionmessage": "Вопрос {{$a}}: {{$b}}", - "core.question.questionno": "Вопрос {{$a}}", - "core.question.requiresgrading": "Требуется оценивание", - "core.quotausage": "В настоящее время вы использовали {{$a.used}} из своего лимита {{$a.total}}.", - "core.rating.aggregateavg": "Средняя оценка", - "core.rating.aggregatecount": "Число оценок", - "core.rating.aggregatemax": "Максимальная оценка", - "core.rating.aggregatemin": "Минимальная оценка", - "core.rating.aggregatesum": "Сумма оценок", - "core.rating.noratings": "Оценки не выставлены", - "core.rating.rating": "Оценка", - "core.rating.ratings": "Оценивание", - "core.redirectingtosite": "Вы будете перенаправлены на сайт.", - "core.refresh": "Обновить", - "core.remove": "Удалить", - "core.required": "Необходимо заполнить", - "core.requireduserdatamissing": "У этого пользователя не хватает необходимых данных профиля. Пожалуйста, введите данные на вашем сайте и попытайтесь снова.
                      {{$a}}", - "core.resourcedisplayopen": "Открыть", - "core.resources": "Ресурсы", - "core.restore": "Восстановить", - "core.restricted": "Ограничено", - "core.retry": "Попробовать снова", - "core.save": "Сохранить", - "core.savechanges": "Сохранить", - "core.search": "Найти", - "core.searching": "Поиск", - "core.searchresults": "Результаты поиска", - "core.sec": "сек.", - "core.secs": "сек.", - "core.seemoredetail": "Детали…", - "core.selectacategory": "Пожалуйста, выберите категорию", - "core.selectacourse": "Выберите курс", - "core.selectagroup": "Выберите группу", - "core.send": "Отправить", - "core.sending": "Отправка", - "core.serverconnection": "Ошибка соединения с сервером", - "core.settings.about": "О приложении", - "core.settings.cannotsyncoffline": "Невозможно синхронизировать пока не в сети.", - "core.settings.cannotsyncwithoutwifi": "Невозможно синхронизироваться, потому что настоящие настройки разрешают синхронизироваться только когда есть подключение к Wi-Fi. Пожалуйста, подключитесь к сети Wi-Fi.", - "core.settings.cordovadevicemodel": "Модель устройства Cordova", - "core.settings.cordovadeviceosversion": "Версия ОС устройства Cordova", - "core.settings.cordovadeviceplatform": "Платформа устройства Cordova", - "core.settings.cordovadeviceuuid": "UUID устройства Cordova", - "core.settings.cordovaversion": "Версия Cordova", - "core.settings.currentlanguage": "Выбранный язык", - "core.settings.debugdisplay": "Отображать отладочные сообщения", - "core.settings.deletesitefiles": "Вы уверены, что хотите удалить файлы, скачанные с сайта «{{sitename}}»?", - "core.settings.deletesitefilestitle": "Удалить файлы сайта", - "core.settings.deviceinfo": "Информация об устройстве", - "core.settings.deviceos": "ОС устройства", - "core.settings.disableall": "Отключить уведомления", - "core.settings.disabled": "Отключено", - "core.settings.displayformat": "Формат отображения", - "core.settings.enabledownloadsection": "Показать материалы, доступные для скачивания", - "core.settings.enablerichtexteditor": "Включить текстовый редактор", - "core.settings.enablerichtexteditordescription": "При включении функции текстовый редактор будет доступен во время ввода содержимого.", - "core.settings.enablesyncwifi": "Разрешить синхронизацию только по Wi-Fi", - "core.settings.errordeletesitefiles": "Ошибка при удалении файлов сайта", - "core.settings.errorsyncsite": "Ошибка синхронизации данных сайта. Проверьте интернет-соединение и повторите попытку.", - "core.settings.estimatedfreespace": "Ориентировочное свободное место", - "core.settings.filesystemroot": "Корень файловой системы", - "core.settings.fontsizecharacter": "А", - "core.settings.general": "Основные", - "core.settings.language": "Язык", - "core.settings.license": "Лицензия", - "core.settings.localnotifavailable": "Локальные уведомления доступны", - "core.settings.locationhref": "URL компонента Web view", - "core.settings.locked": "Заблокировано", - "core.settings.loggedin": "На сайте", - "core.settings.loggedoff": "Не в сети", - "core.settings.navigatorlanguage": "Язык навигатора", - "core.settings.navigatoruseragent": "userAgent навигатора", - "core.settings.networkstatus": "Статус подключения к интернету", - "core.settings.preferences": "Настройки", - "core.settings.privacypolicy": "Политика конфиденциальности", - "core.settings.reportinbackground": "Автоматически оповещать об ошибках", - "core.settings.settings": "Настройки", - "core.settings.sites": "Сайты", - "core.settings.spaceusage": "Используемое пространство", - "core.settings.synchronization": "Синхронизация", - "core.settings.synchronizenow": "Синхронизировать сейчас", - "core.settings.syncsettings": "Настройки синхронизации", - "core.settings.total": "Итог", - "core.settings.wificonnection": "Подключение Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "Выберите учётную запись для хранения файла.", - "core.sharedfiles.chooseactionrepeatedfile": "Файл с таким именем уже существует. Вы хотите заменить существующий файл или переименовать этот файл в «{{$a}}»?", - "core.sharedfiles.errorreceivefilenosites": "Нет сохранённых сайтов. Добавьте сайт чтобы делиться файлом при помощи приложения.", - "core.sharedfiles.nosharedfiles": "На этом сайте нет открытых для доступа файлов.", - "core.sharedfiles.nosharedfilestoupload": "У Вас нет файлов для загрузки на сервер. Если Вы хотите загрузить на сервер файл из другого приложения, найдите файл и нажмите кнопку «Открыть в».", - "core.sharedfiles.rename": "Переименовать", - "core.sharedfiles.replace": "Переместить", - "core.sharedfiles.sharedfiles": "Файлы в открытом доступе", - "core.sharedfiles.successstorefile": "Файл успешно сохранён. Выберите файл для загрузки на сервер в свои личные файлы или для использования в активном элементе.", - "core.show": "Показать", - "core.showless": "Показать меньше ...", - "core.showmore": "Показать больше ...", - "core.site": "Сайт", - "core.sitehome.sitehome": "Домашняя страница", - "core.sitehome.sitenews": "Объявления сайта", - "core.sitemaintenance": "Извините, сайт находится в режиме технического обслуживания и сейчас недоступен", - "core.sizeb": "байт", - "core.sizegb": "Гбайт", - "core.sizekb": "Кбайт", - "core.sizemb": "Мбайт", - "core.sizetb": "Тб", - "core.skip": "Пропустить", - "core.sorry": "Извините...", - "core.sort": "Отсортировать", - "core.sortby": "Сортировать по", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%y, %H:%M", - "core.strftimedaydate": "%A %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Отправить", - "core.success": "Успешно", - "core.tablet": "Планшет", - "core.tag.defautltagcoll": "Коллекция по умолчанию", - "core.tag.inalltagcoll": "Везде", - "core.tag.itemstaggedwith": "Область «{{$a.tagarea}}» помечена тегом «{{$a.tag}}»", - "core.tag.noresultsfor": "Не найдено ни одного тега, содержащего «{{$a}}»", - "core.tag.notagsfound": "Не найдено тегов, совпадающих с «{{$a}}»", - "core.tag.searchtags": "Поиск тегов", - "core.tag.showingfirsttags": "Показать {{$a}} наиболее популярных тегов", - "core.tag.tag": "Тег", - "core.tag.tagarea_course": "Курсы", - "core.tag.tagarea_course_modules": "Элементы и ресурсы", - "core.tag.tagarea_post": "Записи блогов", - "core.tag.tagarea_user": "Интересы пользователей", - "core.tag.tags": "Теги", - "core.teachers": "Преподаватели", - "core.thereisdatatosync": "Есть {{$a}} для синхронизации, созданные вне сети.", - "core.thisdirection": "ltr", - "core.time": "Время", - "core.timesup": "Время закончилось!", - "core.today": "Сегодня", - "core.tryagain": "Попытаться снова", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.uhoh": "Ой, ой!", - "core.unexpectederror": "Неизвестная ошибка. Пожалуйста, закройте, затем ещё раз откройте приложение и попробуйте снова.", - "core.unicodenotsupported": "Некоторые смайлики не поддерживаются на этом сайте. Такие символы будут удалены при отправке сообщения.", - "core.unicodenotsupportedcleanerror": "Был обнаружен пустой текст при очистке символов Unicode.", - "core.unknown": "Неизвестно", - "core.unlimited": "Неограничено", - "core.unzipping": "Распаковка", - "core.upgraderunning": "Сайт обновляется, повторите попытку позже.", - "core.user": "Пользователь", - "core.user.address": "Адрес", - "core.user.city": "Город", - "core.user.contact": "Контакты", - "core.user.country": "Страна", - "core.user.description": "Описание", - "core.user.details": "Подробности", - "core.user.detailsnotavailable": "Вам не доступны подробности этого пользователя", - "core.user.editingteacher": "Учитель", - "core.user.email": "Адрес электронной почты", - "core.user.emailagain": "Адрес электронной почты (еще раз)", - "core.user.firstname": "Имя", - "core.user.interests": "Интересы", - "core.user.lastname": "Фамилия", - "core.user.manager": "Управляющий", - "core.user.newpicture": "Новое изображение", - "core.user.noparticipants": "Не найдены участники для этого курса.", - "core.user.participants": "Участники", - "core.user.phone1": "Телефон", - "core.user.phone2": "Мобильный телефон", - "core.user.roles": "Роли", - "core.user.sendemail": "Электронная почта", - "core.user.student": "Студент", - "core.user.teacher": "Ассистент (без права редактирования)", - "core.user.webpage": "Веб-страница", - "core.userdeleted": "Учетная запись пользователя была удалена", - "core.userdetails": "Подробная информация о пользователе", - "core.usernotfullysetup": "Пользователь не полностью настроен", - "core.users": "Пользователи", - "core.view": "Просмотр", - "core.viewprofile": "Просмотр профиля", - "core.warningofflinedatadeleted": "Данные, добавленные вне сети, из {{component}} «{{name}}» были удалены. {{error}}", - "core.whatisyourage": "Сколько вам лет?", - "core.wheredoyoulive": "В какой стране вы живете?", - "core.whoops": "Ой!", - "core.whyisthishappening": "Почему так происходит?", - "core.whyisthisrequired": "Зачем это нужно?", - "core.wsfunctionnotavailable": "Функция веб-службы не доступна.", - "core.year": "г.", - "core.years": "г.", - "core.yes": "Да" -} \ No newline at end of file diff --git a/src/assets/lang/sl.json b/src/assets/lang/sl.json deleted file mode 100644 index f9a4deeac..000000000 --- a/src/assets/lang/sl.json +++ /dev/null @@ -1,2099 +0,0 @@ -{ - "addon.badges.alignment": "Kompetence", - "addon.badges.badgedetails": "Podrobnosti priznanja", - "addon.badges.badges": "Priznanja", - "addon.badges.bendorsement": "Priporočilo", - "addon.badges.claimcomment": "Komentar priporočila", - "addon.badges.claimid": "Pridobi URL", - "addon.badges.contact": "Stik", - "addon.badges.dateawarded": "Datum izdaje", - "addon.badges.expired": "Poteklo", - "addon.badges.expirydate": "Datum poteka veljavnosti", - "addon.badges.imageauthoremail": "E-poštni naslov avtorja slike", - "addon.badges.imageauthorname": "Ime avtorja slike", - "addon.badges.imageauthorurl": "URL avtorja slike", - "addon.badges.imagecaption": "Opis slike", - "addon.badges.issuancedetails": "Potek priznanja", - "addon.badges.issuerdetails": "Podrobnosti podeljevalca", - "addon.badges.issueremail": "E-poštni naslov", - "addon.badges.issuername": "Ime podeljevalca", - "addon.badges.issuerurl": "URL podeljevalca", - "addon.badges.language": "Jezik", - "addon.badges.noalignment": "Ta značka nima določenih zunanjih veščin ali standardov.", - "addon.badges.nobadges": "Na voljo ni nobenega priznanja.", - "addon.badges.norelated": "Ta značka nima sorodnih značk.", - "addon.badges.recipientdetails": "Podrobnosti o prejemniku", - "addon.badges.relatedbages": "Sorodna priznanja", - "addon.badges.version": "Različica", - "addon.badges.warnexpired": "(Ta značka je potekla!)", - "addon.block_activitymodules.pluginname": "Dejavnosti", - "addon.block_activityresults.pluginname": "Rezultati dejavnosti", - "addon.block_badges.pluginname": "Zadnje značke", - "addon.block_blogmenu.pluginname": "Meni bloga", - "addon.block_blogrecent.pluginname": "Nedavni vnosi v blog", - "addon.block_blogtags.pluginname": "Oznake bloga", - "addon.block_calendarmonth.pluginname": "Koledar", - "addon.block_calendarupcoming.pluginname": "Prihajajoči dogodki", - "addon.block_comments.pluginname": "Komentarji", - "addon.block_completionstatus.pluginname": "Status dokončanja predmeta", - "addon.block_glossaryrandom.pluginname": "Naključen vnos slovarja", - "addon.block_learningplans.pluginname": "Učni načrti", - "addon.block_myoverview.all": "Vse (razen odstranjenih iz pogleda)", - "addon.block_myoverview.allincludinghidden": "Vse", - "addon.block_myoverview.favourites": "Priljubljeno", - "addon.block_myoverview.future": "Prihodnji", - "addon.block_myoverview.hiddencourses": "Odstranjeno iz pogleda", - "addon.block_myoverview.inprogress": "Se izvaja", - "addon.block_myoverview.lastaccessed": "Zadnji dostop", - "addon.block_myoverview.morecourses": "Več predmetov", - "addon.block_myoverview.nocourses": "Ni predmetov", - "addon.block_myoverview.past": "Pretekli", - "addon.block_myoverview.pluginname": "Pregled predmeta", - "addon.block_myoverview.title": "Ime predmeta", - "addon.block_newsitems.pluginname": "Zadnje novice", - "addon.block_onlineusers.pluginname": "Prisotni uporabniki", - "addon.block_privatefiles.pluginname": "Zasebne datoteke", - "addon.block_recentactivity.pluginname": "Nedavne dejavnosti", - "addon.block_recentlyaccessedcourses.nocourses": "Ni nedavnih predmetov", - "addon.block_recentlyaccessedcourses.pluginname": "Predmeti, z nedavnim dostopom", - "addon.block_recentlyaccesseditems.noitems": "Ni nedavnih elementov", - "addon.block_recentlyaccesseditems.pluginname": "Nedavno odprti elementi", - "addon.block_rssclient.pluginname": "Oddaljeni RSS viri", - "addon.block_selfcompletion.pluginname": "Avtomatsko dokončanje", - "addon.block_sitemainmenu.pluginname": "Glavni meni", - "addon.block_starredcourses.nocourses": "Ni označenih predmetov.", - "addon.block_starredcourses.pluginname": "Označeni predmeti.", - "addon.block_tags.pluginname": "Oznake", - "addon.block_timeline.duedate": "Rok", - "addon.block_timeline.next30days": "Naslednjih 30 dni", - "addon.block_timeline.next3months": "Naslednje 3 mesece", - "addon.block_timeline.next6months": "Naslednjih 6 mesecev", - "addon.block_timeline.next7days": "Naslednjih 7 dni", - "addon.block_timeline.nocoursesinprogress": "Ni predmetov v teku", - "addon.block_timeline.noevents": "Ni prihajajočih aktivnosti, ki bi zapadle", - "addon.block_timeline.overdue": "Zakasnel", - "addon.block_timeline.pluginname": "Časovnica", - "addon.block_timeline.sortbycourses": "Razvrsti po predmetih", - "addon.block_timeline.sortbydates": "Razvrsti po datumu", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Vpisi v blog", - "addon.blog.errorloadentries": "Napaka pri nalaganju vnosov v blog.", - "addon.blog.linktooriginalentry": "Povezava do originalnega vpisa v blog", - "addon.blog.noentriesyet": "Tu ni vidnih vnosov", - "addon.blog.publishtonoone": "sebe (osnutek)", - "addon.blog.publishtosite": "kogarkoli na tej strani", - "addon.blog.publishtoworld": "kogarkoli na svetu", - "addon.blog.showonlyyourentries": "Prikaži samo svoje vnose", - "addon.blog.siteblogheading": "Blog spletnega mesta", - "addon.calendar.allday": "Ves dan", - "addon.calendar.calendar": "Koledar", - "addon.calendar.calendarevent": "Koledarski dogodek", - "addon.calendar.calendarevents": "Koledarski dogodki", - "addon.calendar.calendarreminders": "Koledarski opomniki", - "addon.calendar.categoryevents": "Dogodki za kategorijo", - "addon.calendar.confirmeventdelete": "Ste prepričani, da želite izbrisati dogodek »{{$a}}«?", - "addon.calendar.confirmeventseriesdelete": "Dogodek \"{{$a.name}}\" se ponavlja. Želite izbrisati samo ta dogodek ali vse ponovitve dogodka (število ponovitev: {{$a.count}})?", - "addon.calendar.courseevents": "Dogodki predmeta", - "addon.calendar.currentmonth": "Trenutni mesec", - "addon.calendar.daynext": "Naslednji dan", - "addon.calendar.dayprev": "Prejšnji dan", - "addon.calendar.defaultnotificationtime": "Privzeti čas obveščanja", - "addon.calendar.deleteallevents": "Izbriši vse dogodke", - "addon.calendar.deleteevent": "Izbriši dogodek", - "addon.calendar.deleteoneevent": "Izbriši ta dogodek", - "addon.calendar.durationminutes": "Trajanje v minutah", - "addon.calendar.durationnone": "Brez trajanja", - "addon.calendar.durationuntil": "Do", - "addon.calendar.editevent": "Urejanje dogodka", - "addon.calendar.errorloadevent": "Napaka pri nalaganju dogodka", - "addon.calendar.errorloadevents": "Napaka pri nalaganju dogodkov.", - "addon.calendar.eventcalendareventdeleted": "Izbrisan dogodek koledarja", - "addon.calendar.eventduration": "Trajanje", - "addon.calendar.eventendtime": "Čas konca", - "addon.calendar.eventkind": "Vrsta dogodka", - "addon.calendar.eventname": "Ime dogodka", - "addon.calendar.eventstarttime": "Čas začetka", - "addon.calendar.eventtype": "Tip dogodka", - "addon.calendar.fri": "pet", - "addon.calendar.friday": "petek", - "addon.calendar.gotoactivity": "Pojdi na aktivnost", - "addon.calendar.groupevents": "Dogodki skupine", - "addon.calendar.invalidtimedurationminutes": "Trajanje v minutah, ki ste ga vnesli, ni veljavno. Vnesite čas v minutah, večji od 0, ali pa izberite \"brez trajanja\".", - "addon.calendar.invalidtimedurationuntil": "Datum in čas za trajanje do, ki ste ju izbrali, je pred začetkom dogodka. Popravite to pred nadaljevanjem.", - "addon.calendar.mon": "pon", - "addon.calendar.monday": "ponedeljek", - "addon.calendar.monthlyview": "Mesečni pogled", - "addon.calendar.newevent": "Nov dogodek", - "addon.calendar.noevents": "Ni dogodkov", - "addon.calendar.nopermissiontoupdatecalendar": "Oprostite, vendar trenutno nimate dovoljenja za posodobitev dogodka na koledarju.", - "addon.calendar.reminders": "Opomniki", - "addon.calendar.repeatedevents": "Ponavljajoči se dogodki", - "addon.calendar.repeateditall": "Uporabi spremembe za vse dogodke ({{$a}}) v tej vrsti ponovitev", - "addon.calendar.repeateditthis": "Uporabi spremembe samo za ta dogodek", - "addon.calendar.repeatevent": "Ponovi ta dogodek", - "addon.calendar.repeatweeksl": "Ponovi tedensko, ustvarjanje vseh skupaj", - "addon.calendar.sat": "sob", - "addon.calendar.saturday": "sobota", - "addon.calendar.setnewreminder": "Nastavi nov opomnik", - "addon.calendar.siteevents": "Dogodki v spletnem mestu", - "addon.calendar.sun": "ned", - "addon.calendar.sunday": "nedelja", - "addon.calendar.thu": "čet", - "addon.calendar.thursday": "četrtek", - "addon.calendar.today": "Danes", - "addon.calendar.tomorrow": "Jutri", - "addon.calendar.tue": "tor", - "addon.calendar.tuesday": "torek", - "addon.calendar.typecategory": "Dogodek kategorije", - "addon.calendar.typeclose": "Zapri dogodek", - "addon.calendar.typecourse": "Dogodek predmeta", - "addon.calendar.typedue": "Prihajajoči dogodek", - "addon.calendar.typegradingdue": "Prihajajoči dogodek ocenjevanja", - "addon.calendar.typegroup": "Dogodek skupine", - "addon.calendar.typeopen": "Odprt dogodek", - "addon.calendar.typesite": "Dogodek spletnega mesta", - "addon.calendar.typeuser": "Uporabnikov dogodek", - "addon.calendar.upcomingevents": "Bližnji dogodki", - "addon.calendar.userevents": "Uporabnikovi dogodki", - "addon.calendar.wed": "sre", - "addon.calendar.wednesday": "sreda", - "addon.calendar.when": "Ko", - "addon.calendar.yesterday": "Včeraj", - "addon.competency.activities": "Aktivnosti", - "addon.competency.competencies": "Kompetence", - "addon.competency.competenciesmostoftennotproficientincourse": "Najpogostejše nedosežene kompetence v tem predmetu", - "addon.competency.coursecompetencies": "Kompetence predmeta", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Ocene kompetenc v tem predmetu ne vplivajo na učne načrte.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Ocene kompetenc v tem predmetu se takoj odražajo v učnih načrtih.", - "addon.competency.crossreferencedcompetencies": "Sklicevalne kompetence", - "addon.competency.duedate": "Rok", - "addon.competency.errornocompetenciesfound": "Ni najdenih kompetenc", - "addon.competency.evidence": "Dokaz", - "addon.competency.evidence_competencyrule": "Skladno s pravilom kompetence.", - "addon.competency.evidence_coursecompleted": "Predmet '{{$a}}' je bil dokončan.", - "addon.competency.evidence_coursemodulecompleted": "Dejavnost '{{$a}}' je bila dokončana.", - "addon.competency.evidence_courserestored": "Ocena je bila obnovljena s predmetom '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Dokazilo o preteklem izobraževanju je bilo povezano.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Dokazilo o preteklem izobraževanju je bilo razvezano.", - "addon.competency.evidence_manualoverride": "Ocena kompetence je bila nastavljena ročno.", - "addon.competency.evidence_manualoverrideincourse": "Ocena kompetence je bila nastavljena ročno za predmet '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "Ocena kompetence je bila nastavljena ročno za učni načrt '{{$a}}'.", - "addon.competency.learningplancompetencies": "Kompetence učnega načrta", - "addon.competency.learningplans": "Učni načrti", - "addon.competency.myplans": "Moji učni načrti", - "addon.competency.noactivities": "Ni aktivnosti", - "addon.competency.nocompetencies": "Brez kompetenc", - "addon.competency.nocompetenciesincourse": "V to ogrodje ni bilo povezanih kompetenc.", - "addon.competency.nocrossreferencedcompetencies": "Nobena druga kompetenca se ne sklicuje na to kompetenco.", - "addon.competency.noevidence": "Ni dokazov", - "addon.competency.noplanswerecreated": "Noben učni načrt ni bil ustvarjen.", - "addon.competency.nouserplanswithcompetency": "Ni učnih načrtov, ki vsebujejo to kompetenco.", - "addon.competency.path": "Pot:", - "addon.competency.planstatusactive": "Aktivno", - "addon.competency.planstatuscomplete": "Dokončano", - "addon.competency.planstatusdraft": "Osnutek", - "addon.competency.planstatusinreview": "V pregledovanju", - "addon.competency.planstatuswaitingforreview": "Čaka na pregled", - "addon.competency.proficient": "Tekoč", - "addon.competency.progress": "Napredek", - "addon.competency.rating": "Ocena", - "addon.competency.reviewstatus": "Status pregleda", - "addon.competency.status": "Status", - "addon.competency.template": "Predloga učnega načrta", - "addon.competency.uponcoursecompletion": "Ob dokončanju predmeta:", - "addon.competency.usercompetencystatus_idle": "Nedejavno", - "addon.competency.usercompetencystatus_inreview": "V pregledu", - "addon.competency.usercompetencystatus_waitingforreview": "Čaka na pregled", - "addon.competency.userplans": "Učni načrti", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} od skupno {{$a.y}} kompetenc povezanih na predmete", - "addon.competency.xcompetenciesproficientoutofyincourse": "Imate dobro poznavanje v {{$a.x}} od skupno {{$a.y}} kompetenc v tem predmetu.", - "addon.coursecompletion.complete": "Končano", - "addon.coursecompletion.completecourse": "Končaj predmet", - "addon.coursecompletion.completed": "Zaključeno", - "addon.coursecompletion.completiondate": "Datum zaključka", - "addon.coursecompletion.completionmenuitem": "Zaključek", - "addon.coursecompletion.couldnotloadreport": "Poročila o zaključku predmeta ni bilo mogoče naložiti. Prosim poskusite kasneje.", - "addon.coursecompletion.coursecompletion": "Zaključek predmeta", - "addon.coursecompletion.criteria": "Kriterij", - "addon.coursecompletion.criteriagroup": "Kriterij skupine", - "addon.coursecompletion.criteriarequiredall": "Potrebni so vsi spodnji kriteriji", - "addon.coursecompletion.criteriarequiredany": "Vsi spodnji pogoji so obvezni", - "addon.coursecompletion.inprogress": "V procesu", - "addon.coursecompletion.manualselfcompletion": "ročno samo-dokončanje", - "addon.coursecompletion.nottracked": "Trenutno v tem predmetu nimate sledenja zaključevanja", - "addon.coursecompletion.notyetstarted": "Ni se še začelo", - "addon.coursecompletion.pending": "Čakam", - "addon.coursecompletion.required": "Zahtevano", - "addon.coursecompletion.requiredcriteria": "Potrebni kriterij", - "addon.coursecompletion.requirement": "Zahteva", - "addon.coursecompletion.status": "Stanje", - "addon.coursecompletion.viewcoursereport": "Ogled poročila predmeta", - "addon.files.couldnotloadfiles": "Seznama datotek ni bilo mogoče naložiti.", - "addon.files.emptyfilelist": "Datotek ni na voljo.", - "addon.files.erroruploadnotworking": "Žal trenutno ni mogoče naložiti datotek na vaše spletno mesto.", - "addon.files.files": "Datoteke", - "addon.files.privatefiles": "Zasebne datoteke", - "addon.files.sitefiles": "Datoteke spletnega mesta", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Konfigurirajte naprave", - "addon.messages.acceptandaddcontact": "Sprejmi in dodaj v svoje stike", - "addon.messages.addcontact": "Dodaj stik", - "addon.messages.addcontactconfirm": "Ste prepričani, da želite dodati {{$a}} v svoje stike?", - "addon.messages.addtofavourites": "Označi pogovor z zvezdico", - "addon.messages.addtoyourcontacts": "Dodaj v stike", - "addon.messages.blocknoncontacts": "Prepreči sporočila oseb, ki niso na mojem seznamu stikov", - "addon.messages.blockuser": "Blokiraj uporabnika", - "addon.messages.blockuserconfirm": "Ste prepričani, da želite blokirati {{$a}}?", - "addon.messages.contactableprivacy": "Sprejmi sporočila od:", - "addon.messages.contactableprivacy_coursemember": "Moji stiki in vsi v mojih predmetih", - "addon.messages.contactableprivacy_onlycontacts": "Samo moji stiki", - "addon.messages.contactableprivacy_site": "Vsi na spetnem mestu", - "addon.messages.contactblocked": "Stik je blokiran", - "addon.messages.contactlistempty": "Seznam stikov je prazen", - "addon.messages.contactname": "Ime kontakta", - "addon.messages.contactrequestsent": "Prošnja za dodajanje stika je bila poslana", - "addon.messages.contacts": "Stiki", - "addon.messages.conversationactions": "Meni dejanj za pogovore", - "addon.messages.decline": "Zavrni", - "addon.messages.deleteallconfirm": "Ste prepričani, da želite izbrisati celoten pogovor? Pogovor ne bo izbrisan pri drugih udeležencih pogovora.", - "addon.messages.deleteallselfconfirm": "Ali ste prepričani, da želite izbrisati celotni osebni pogovor?", - "addon.messages.deleteconversation": "Izbriši pogovor", - "addon.messages.deleteforeveryone": "Izbriši zame in za vse druge", - "addon.messages.deletemessage": "Izbriši sporočilo", - "addon.messages.deletemessageconfirmation": "Ali ste prepričani, da želite izbrisati to sporočilo? Izbrisana bo samo iz vaše zgodovine sporočanja, uporabnik, ki je sporočilo poslal ali prejel, pa ga bo še vedno videl.", - "addon.messages.errordeletemessage": "Napaka med brisanjem sporočila.", - "addon.messages.errorwhileretrievingcontacts": "Napaka pri pridobivanju stikov s strežnika.", - "addon.messages.errorwhileretrievingdiscussions": "Napaka pri pridobivanju razprav s strežnika.", - "addon.messages.errorwhileretrievingmessages": "Napaka pri prenosu sporočil s strežnika.", - "addon.messages.errorwhileretrievingusers": "Napaka pri pridobivanju uporabnikov iz strežnika.", - "addon.messages.groupconversations": "Skupina", - "addon.messages.groupinfo": "Informacije o skupini", - "addon.messages.individualconversations": "Zasebno", - "addon.messages.info": "Informacje o uporabniku", - "addon.messages.isnotinyourcontacts": "{{$a}} ni med vašimi stiki", - "addon.messages.message": "Sporočilo", - "addon.messages.messagenotsent": "Sporočilo ni bilo poslano. Prosim poskusite kasneje.", - "addon.messages.messagepreferences": "Nastavitve sporočil", - "addon.messages.messages": "Sporočila", - "addon.messages.muteconversation": "Utišaj", - "addon.messages.mutedconversation": "Utišan pogovor", - "addon.messages.newmessage": "Novo sporočilo", - "addon.messages.newmessages": "Nova sporočila", - "addon.messages.nocontactrequests": "Ni prošenj za sprejem stika", - "addon.messages.nocontactsgetstarted": "Ni stikov", - "addon.messages.nofavourites": "Ni z zvezdico označenih sporočil", - "addon.messages.nogroupconversations": "Ni skupinskih pogovorov", - "addon.messages.noindividualconversations": "Ni zasebnih pogovorov", - "addon.messages.nomessagesfound": "Nobeno sporočilo ni bilo najdeno", - "addon.messages.noncontacts": "Nestiki", - "addon.messages.nousersfound": "Uporabnikov ni bilo mogoče najti", - "addon.messages.numparticipants": "{{$a}} sodelujoči", - "addon.messages.removecontact": "Odstrani stik", - "addon.messages.removecontactconfirm": "Ste prepričani, da želite odstraniti {{$a}} iz stikov?", - "addon.messages.removefromfavourites": "Odznači pogovor z zvezdico", - "addon.messages.removefromyourcontacts": "Odstrani iz stikov", - "addon.messages.requests": "Zahteve", - "addon.messages.requirecontacttomessage": "{{$a}} morate prositi, da vas doda med stike, da ji lahko pošljete sporočilo.", - "addon.messages.searchcombined": "Išči ljudi in sporočila", - "addon.messages.selfconversation": "Osebni prostor", - "addon.messages.selfconversationdefaultmessage": "Shrani osnutke sporočil, povezave, zapiske itd. za poznejši dostop.", - "addon.messages.sendcontactrequest": "Pošlji prošnjo za stik", - "addon.messages.showdeletemessages": "Pokaži izbrisana sporočila", - "addon.messages.type_blocked": "Blokirano", - "addon.messages.type_offline": "Nepovezan", - "addon.messages.type_online": "Povezan", - "addon.messages.type_search": "Rezultati iskanja", - "addon.messages.type_strangers": "Drugo", - "addon.messages.unabletomessage": "Temu uporabniku ne morete poslati sporočila", - "addon.messages.unblockuser": "Odblokiraj uporabnika", - "addon.messages.unblockuserconfirm": "Ste prepričani, da želite odblokirati {{$a}}?", - "addon.messages.unmuteconversation": "Izklopi utišan način", - "addon.messages.useentertosend": "Za pošiljanje kliknite enter", - "addon.messages.useentertosenddescdesktop": "Če onemogočeno, lahko za pošiljanje sporočila uporabite Ctrl + Enter.", - "addon.messages.useentertosenddescmac": "Če onemogočeno, lahko za pošiljanje sporočila uporabite Cmd + Enter.", - "addon.messages.userwouldliketocontactyou": "{{$a}} bi rad vzpostavil stik", - "addon.messages.warningconversationmessagenotsent": "Sporočila ni bilo mogoče poslati v pogovor {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Uporabnikom {{user}} sporočila ni bilo mogoče poslati. {{error}}", - "addon.messages.wouldliketocontactyou": "Bi rad vzpostavil stik", - "addon.messages.you": "Vi:", - "addon.messages.youhaveblockeduser": "Tega uporabnika ste blokirali", - "addon.messages.yourcontactrequestpending": "Vaša prošnja za stik je bila poslana {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Sprejmite izjavo o oddaji.", - "addon.mod_assign.addattempt": "Dovoli dodatno oddajo", - "addon.mod_assign.addnewattempt": "Dodaj nov poskus", - "addon.mod_assign.addnewattemptfromprevious": "Dodaj nov poskus glede na prejšnje prispevke", - "addon.mod_assign.addsubmission": "Oddaj nalogo", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Podrobnosti naloge in obrazec za oddajo bosta na voljo od {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Dovoli začetek oddaje od", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Prispevke v nalogi lahko oddate od {{$a}}", - "addon.mod_assign.applytoteam": "Uporabi ocene in odzive za celotno skupino", - "addon.mod_assign.assignmentisdue": "Rok za oddajo je", - "addon.mod_assign.attemptnumber": "Število oddaj", - "addon.mod_assign.attemptreopenmethod": "Omogoči ponovno oddajo", - "addon.mod_assign.attemptreopenmethod_manual": "Ročno", - "addon.mod_assign.attemptreopenmethod_untilpass": "Samodejno, dokler ni uspešno opravljeno", - "addon.mod_assign.attemptsettings": "Nastavitve oddaje", - "addon.mod_assign.cannoteditduetostatementsubmission": "Oddaje v aplikaciji ne morete urediti, ker izjave o oddaji ni bilo mogoče pridobiti s spletnega mesta.", - "addon.mod_assign.cannotgradefromapp": "Določenih metod ocenjevanja aplikacija še ne podpira in jih zato ni mogoče spreminjati.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Oddaja v aplikaciji ni mogoča, ker izjave o oddaji ni bilo mogoče pridobiti s spletnega mesta.", - "addon.mod_assign.confirmsubmission": "Ali želite dokončno oddati vašo nalogo v ocenjevanje? Kasnejše spremembe ne bodo več možne.", - "addon.mod_assign.currentattempt": "To je vaš {{$a}} poskus.", - "addon.mod_assign.currentattemptof": "To je vaš {{$a.attemptnumber}}. poskus.\n(Število dovoljenih poskusov: {{$a.maxattempts}}).", - "addon.mod_assign.currentgrade": "Trenutno ocena v redovalnici", - "addon.mod_assign.cutoffdate": "Zadnji možni rok za oddajo", - "addon.mod_assign.defaultteam": "Privzeta skupina", - "addon.mod_assign.duedate": "Rok za oddajo", - "addon.mod_assign.duedateno": "Brez roka", - "addon.mod_assign.duedatereached": "Rok za oddajo naloge je potekel", - "addon.mod_assign.editingstatus": "Stanje urejanja", - "addon.mod_assign.editsubmission": "Uredi oddano nalogo", - "addon.mod_assign.erroreditpluginsnotsupported": "Oddaje v aplikaciji ne morete urediti, ker določeni vtičniki še niso podprti za urejanje.", - "addon.mod_assign.errorshowinginformation": "Podatkov o oddaji ni mogoče prikazati.", - "addon.mod_assign.extensionduedate": "Podaljšanje zapadlosti", - "addon.mod_assign.feedbacknotsupported": "Ker aplikacija te vrste povratne informacije ne podpira, morda ne vsebuje vseh informacij.", - "addon.mod_assign.grade": "Ocena", - "addon.mod_assign.graded": "Ocenjeno", - "addon.mod_assign.gradedby": "Ocenil", - "addon.mod_assign.gradedfollowupsubmit": "Ocenjeno – prejeta ponovna oddaja", - "addon.mod_assign.gradedon": "Ocenjeno v", - "addon.mod_assign.gradelocked": "Ta ocena je zaklenjena ali preglašena v redovalnici.", - "addon.mod_assign.gradenotsynced": "Ocena ni sinhronizirana", - "addon.mod_assign.gradeoutof": "Ocena od {{$a}}", - "addon.mod_assign.gradingstatus": "Stanje ocen", - "addon.mod_assign.groupsubmissionsettings": "Nastavitve oddaje za skupino", - "addon.mod_assign.hiddenuser": "Sodelujoči", - "addon.mod_assign.latesubmissions": "Zamujene oddaje", - "addon.mod_assign.latesubmissionsaccepted": "Oddaja mogoča do {{$a}}", - "addon.mod_assign.markingworkflowstate": "Stanje paketnega ocenjevanja", - "addon.mod_assign.markingworkflowstateinmarking": "Ocenjevanje v teku", - "addon.mod_assign.markingworkflowstateinreview": "Pregled", - "addon.mod_assign.markingworkflowstatenotmarked": "Ni ocenjeno", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Pripravljeno za objavo", - "addon.mod_assign.markingworkflowstatereadyforreview": "Ocenjevanje zaključeno", - "addon.mod_assign.markingworkflowstatereleased": "Objavljeno", - "addon.mod_assign.modulenameplural": "Naloge", - "addon.mod_assign.multipleteams": "Član več kot ene skupine", - "addon.mod_assign.multipleteams_desc": "Naloga zahteva oddajo v skupinah. Ker ste član več kot ene skupine, ne morete oddajati naloge. Za oddajo naloge morate biti včlanjeni v le eno skupino. Obrnite se na izvajalca predmeta, da spremeni vaše članstvo v skupinah.", - "addon.mod_assign.noattempt": "Neoddano", - "addon.mod_assign.nomoresubmissionsaccepted": "Dovoljeno le sodelujočim, ki jim je bil odobren podaljšani rok", - "addon.mod_assign.noonlinesubmissions": "Pri tej nalogi vam ni treba oddati ničesar", - "addon.mod_assign.nosubmission": "V tej nalogi še ni oddanih prispevkov", - "addon.mod_assign.notallparticipantsareshown": "Udeleženci, ki niso oddali niso prikazani.", - "addon.mod_assign.noteam": "Niste član nobene skupine", - "addon.mod_assign.noteam_desc": "Naloga zahteva oddajo v skupinah. Ker niste član nobene skupine, ne morete oddajati naloge. Obrnite se na izvajalca predmeta, da vas doda v skupino.", - "addon.mod_assign.notgraded": "Neocenjeno", - "addon.mod_assign.numberofdraftsubmissions": "Osnutki", - "addon.mod_assign.numberofparticipants": "Sodelujoči", - "addon.mod_assign.numberofsubmissionsneedgrading": "Čaka na ocenjevanje", - "addon.mod_assign.numberofsubmittedassignments": "Oddano", - "addon.mod_assign.numberofteams": "Skupine", - "addon.mod_assign.numwords": "{{$a}} besed", - "addon.mod_assign.outof": "{{$a.current}} od {{$a.total}}", - "addon.mod_assign.overdue": "Rok za oddajo je potekel pred: {{$a}}", - "addon.mod_assign.submission": "Oddaja", - "addon.mod_assign.submissioneditable": "Udeleženec lahko ureja oddano nalogo", - "addon.mod_assign.submissionnoteditable": "Udeleženec ne more urejati oddane naloge", - "addon.mod_assign.submissionnotsupported": "Ker aplikacija ne podpira oddaj, morda ne vsebuje vseh informacij.", - "addon.mod_assign.submissionslocked": "Pri tej nalogi ne morete oddati prispevkov", - "addon.mod_assign.submissionstatus": "Status oddaje naloge", - "addon.mod_assign.submissionstatus_": "Ni oddaje", - "addon.mod_assign.submissionstatus_draft": "Predloga (neoodano)", - "addon.mod_assign.submissionstatus_marked": "Ocenjeno", - "addon.mod_assign.submissionstatus_new": "Ni oddaje", - "addon.mod_assign.submissionstatus_reopened": "Ponovna oddaja omogočena", - "addon.mod_assign.submissionstatus_submitted": "Oddano v ocenjevanje", - "addon.mod_assign.submissionstatusheading": "Status oddaje naloge", - "addon.mod_assign.submissionteam": "Skupina", - "addon.mod_assign.submitassignment": "Oddaj nalogo", - "addon.mod_assign.submitassignment_help": "Ko boste dokončno oddali nalogo, je ne boste mogli več popraviti.", - "addon.mod_assign.submittedearly": "Naloga je bila oddana {{$a}} prezgodaj", - "addon.mod_assign.submittedlate": "Naloga je bila oddana {{$a}} prepozno", - "addon.mod_assign.timemodified": "Zadnja sprememba", - "addon.mod_assign.timeremaining": "Preostali čas", - "addon.mod_assign.ungroupedusers": "Nastavitev 'Zahtevaj skupino za oddajanje nalog' je omogočena in nekateri uporabniki bodisi niso člani nobene skupine bodisi so člani več kot ene skupine, zato ne morejo oddajati nalog.", - "addon.mod_assign.ungroupedusersoptional": "Nastavitev \"Udeleženci oddajajo po skupinah\" je omogočena. Če udeleženec ni član nobene skupine ali pa je član več kot ene skupine, bo oddal kot član \"privzete skupine\".", - "addon.mod_assign.unlimitedattempts": "Neomejeno", - "addon.mod_assign.userswhoneedtosubmit": "Uporabniki, ki morajo še oddati nalogo: {{$a}}", - "addon.mod_assign.userwithid": "Uporabnik z ID {{id}}", - "addon.mod_assign.viewsubmission": "Prikaži prispevke", - "addon.mod_assign.warningsubmissiongrademodified": "Ocena oddaje je bila spremenjena.", - "addon.mod_assign.warningsubmissionmodified": "Uporabnikova oddaja je bila spremenjena na strani.", - "addon.mod_assign.wordlimit": "Omejitev števila besed", - "addon.mod_assign_feedback_comments.pluginname": "Komentarji odziva", - "addon.mod_assign_feedback_editpdf.pluginname": "KomentirajPDF", - "addon.mod_assign_feedback_file.pluginname": "Datoteka odziva", - "addon.mod_assign_submission_comments.pluginname": "Komentar oddaje", - "addon.mod_assign_submission_file.pluginname": "Oddaja datotek", - "addon.mod_assign_submission_onlinetext.pluginname": "Oddaje spletnega teksta", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Število besed za to nalogo je omejeno na {{$a.limit}}. Vaša naloga vsebuje {{$a.count}}. Preglejte jo in poskusite znova.", - "addon.mod_book.errorchapter": "Napaka pri branju poglavja knjige.", - "addon.mod_book.modulenameplural": "Knjige", - "addon.mod_book.navnexttitle": "Naslednje: {{$a}}", - "addon.mod_book.navprevtitle": "Prejšnje: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Poglavja knjige", - "addon.mod_book.toc": "Kazalo vsebine", - "addon.mod_chat.beep": "Zvočni signal", - "addon.mod_chat.chatreport": "Seje klepeta", - "addon.mod_chat.currentusers": "Trenutni uporabniki", - "addon.mod_chat.enterchat": "Kliknite tu za vstop v klepet", - "addon.mod_chat.entermessage": "Vnesite svoje sporočilo", - "addon.mod_chat.errorwhileconnecting": "Napaka med povezavo s klepetom.", - "addon.mod_chat.errorwhilegettingchatdata": "Napaka med pridobivanjem podatkov klepeta.", - "addon.mod_chat.errorwhilegettingchatusers": "Napaka pri pridobivanju uporabnikov klepeta.", - "addon.mod_chat.errorwhileretrievingmessages": "Napaka med prenosom sporočil s strežnika.", - "addon.mod_chat.errorwhilesendingmessage": "Napaka pri pošiljanju sporočila.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} pozvoni vsem!", - "addon.mod_chat.messagebeepsyou": "{{$a}} vam je pravkar pozvonil/a!", - "addon.mod_chat.messageenter": "{{$a}} se je pravkar pridružil/a temu klepetu", - "addon.mod_chat.messageexit": "{{$a}} je zapustil/a ta klepet", - "addon.mod_chat.messages": "Sporočila", - "addon.mod_chat.messageyoubeep": "Pozvonili ste {{$a}}", - "addon.mod_chat.modulenameplural": "Klepeti", - "addon.mod_chat.mustbeonlinetosendmessages": "Za pošiljanje sporočil morate biti povezani na splet.", - "addon.mod_chat.nomessages": "Ni še sporočil", - "addon.mod_chat.nosessionsfound": "Tem ni bilo mogoče najti", - "addon.mod_chat.saidto": "rečeno", - "addon.mod_chat.send": "Pošlji", - "addon.mod_chat.sessionstart": "Naslednja seja klepeta se bo začela {{$a.date}}, ({{$a.fromnow}} od zdaj)", - "addon.mod_chat.showincompletesessions": "Pokaži nedokončane teme.", - "addon.mod_chat.talk": "Govori", - "addon.mod_chat.viewreport": "Ogled minulih sej klepeta", - "addon.mod_choice.cannotsubmit": "Prišlo je do težave pri nalaganju vaše možnosti. Poskusite znova.", - "addon.mod_choice.choiceoptions": "Nastavitve možnosti", - "addon.mod_choice.errorgetchoice": "Napaka pri pridobivanju podatkov izbire.", - "addon.mod_choice.expired": "Ta dejavnost je zaprta od {{$a}}.", - "addon.mod_choice.full": "(Polno)", - "addon.mod_choice.modulenameplural": "Možnosti", - "addon.mod_choice.noresultsviewable": "Rezultati trenutno niso vidni.", - "addon.mod_choice.notopenyet": "Ta dejavnost ni na voljo do {{$a}}.", - "addon.mod_choice.numberofuser": "Število odgovorov", - "addon.mod_choice.numberofuserinpercentage": "Odstotek odgovorov", - "addon.mod_choice.previewonly": "To je le predogled nastavitev, ki so na voljo za to dejavnost. Svoje izbire ne boste mogli oddati do {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anonimni rezultati bodo objavljeni po vašem odgovoru.", - "addon.mod_choice.publishinfoanonclose": "Anonimni rezultati bodo objavljeni po zaprtju dejavnosti.", - "addon.mod_choice.publishinfofullafter": "Popolni rezultati z vsemi izbranimi izbirami bodo objavljeni po vašem odgovoru.", - "addon.mod_choice.publishinfofullclose": "Popolni rezultati z vsemi izbranimi izbirami bodo objavljeni po zaprtju dejavnosti.", - "addon.mod_choice.publishinfonever": "Rezultati te dejavnosti ne bodo objavljeni po Vašem odgovoru.", - "addon.mod_choice.removemychoice": "Odstrani mojo izbiro", - "addon.mod_choice.responses": "Odgovori", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% uporabnikov je izbralo možnost: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Prikaz grafa", - "addon.mod_choice.resultsnotsynced": "Vaš zadnji odgovor mora biti sinhroniziran preden bo vključen v rezultate.", - "addon.mod_choice.savemychoice": "Shrani mojo odločitev", - "addon.mod_choice.userchoosethisoption": "Uporabniki, ki so izbrali to možnost", - "addon.mod_choice.yourselection": "Vaš izbor", - "addon.mod_data.addentries": "Dodaj vnose", - "addon.mod_data.advancedsearch": "Napredno iskanje", - "addon.mod_data.alttext": "Spremno besedilo", - "addon.mod_data.approve": "Odobri", - "addon.mod_data.approved": "Odobreno", - "addon.mod_data.ascending": "Naraščajoče", - "addon.mod_data.authorfirstname": "Ime avtorja", - "addon.mod_data.authorlastname": "Priimek avtorja", - "addon.mod_data.confirmdeleterecord": "Ste prepričani, da želite izbrisati ta vnos?", - "addon.mod_data.descending": "Padajoče", - "addon.mod_data.disapprove": "Razveljavi potrditev", - "addon.mod_data.edittagsnotsupported": "Aplikacija žal ne podpira urejanja oznak.", - "addon.mod_data.emptyaddform": "Niste izpolnili nobenega polja!", - "addon.mod_data.entrieslefttoadd": "Dodati morate še {{$a.entriesleft}} vnos/vnosov za dokončanje te dejavnosti", - "addon.mod_data.entrieslefttoaddtoview": "Pred ogledom vnosov drugih udeležencev morate dodati še {{$a.entrieslefttoview}} vnos/vnosov.", - "addon.mod_data.errorapproving": "Napaka pri odobritvi ali zavrnitvi vnosa.", - "addon.mod_data.errordeleting": "Napaka pri brisanju vnosa.", - "addon.mod_data.errormustsupplyvalue": "Tukaj morate navesti vrednost.", - "addon.mod_data.expired": "Oprostite, ta aktivnost se je zaprla na {{$a}} in ni več dostopna", - "addon.mod_data.fields": "Polja", - "addon.mod_data.foundrecords": "Najdeni zapisi: {{$a.num}}/{{$a.max}} (Ponastavi zapise)", - "addon.mod_data.gettinglocation": "Pridobivanje lokacije", - "addon.mod_data.latlongboth": "Zahtevani sta tako zemljepisna širina kot zemljepisna dolžina.", - "addon.mod_data.locationpermissiondenied": "Dovoljenje za dostop do vaše lokacije je bilo zavrnjeno.", - "addon.mod_data.menuchoose": "Izberi...", - "addon.mod_data.modulenameplural": "Podatkovne zbirke", - "addon.mod_data.more": "Več", - "addon.mod_data.mylocation": "Moja lokacija", - "addon.mod_data.nomatch": "Ni najti vnosov, ki bi se ujemali!", - "addon.mod_data.norecords": "Ni vnosov v podatkovni zbirki", - "addon.mod_data.notapproved": "Vnos še ni odobren.", - "addon.mod_data.notopenyet": "Oprostite, ta aktivnost ni na voljo do {{$a}}", - "addon.mod_data.numrecords": "{{$a}} vnosov", - "addon.mod_data.other": "Drugo", - "addon.mod_data.recordapproved": "Vnos odobren", - "addon.mod_data.recorddeleted": "Vnos izbrisan", - "addon.mod_data.recorddisapproved": "Vnos ni odobren", - "addon.mod_data.resetsettings": "Ponastavi filtre", - "addon.mod_data.search": "Išči", - "addon.mod_data.searchbytagsnotsupported": "Aplikacija žal ne podpira iskanja po oznakah.", - "addon.mod_data.selectedrequired": "Vse izbrano je zahtevano", - "addon.mod_data.single": "Ogled posameznega", - "addon.mod_data.tagarea_data_records": "Podatkovni zapis", - "addon.mod_data.timeadded": "Čas oddaje", - "addon.mod_data.timemodified": "Čas posodobitve", - "addon.mod_data.usedate": "Vključi v iskanje.", - "addon.mod_feedback.analysis": "Analiza", - "addon.mod_feedback.anonymous": "Anonimni", - "addon.mod_feedback.anonymous_entries": "Anonimni vnosi ({{$a}})", - "addon.mod_feedback.average": "Povprečje", - "addon.mod_feedback.captchaofflinewarning": "Odziva, ki vsebuje CAPTCHA, ni mogoče izpolniti brez povezave, ali če ni nastavljen, ali če strežnik ne deluje.", - "addon.mod_feedback.complete_the_form": "Odgovori na vprašanja", - "addon.mod_feedback.completed_feedbacks": "Oddani odgovori", - "addon.mod_feedback.continue_the_form": "Nadaljuj z odgovarjanjem na vprašanja", - "addon.mod_feedback.feedback_is_not_open": "Ta odziv ni odprt", - "addon.mod_feedback.feedback_submitted_offline": "Odziv je shranjen in bo oddan kasneje.", - "addon.mod_feedback.feedbackclose": "Zapri odziv pri", - "addon.mod_feedback.feedbackopen": "Odpri odziv v", - "addon.mod_feedback.mapcourses": "Preslikaj odzive na predmete", - "addon.mod_feedback.maximal": "Maksimum", - "addon.mod_feedback.minimal": "Minimum", - "addon.mod_feedback.mode": "Način", - "addon.mod_feedback.modulenameplural": "Odziv", - "addon.mod_feedback.next_page": "Naslednja stran", - "addon.mod_feedback.non_anonymous": "Uporabniško ime bo zabeleženo in prikazano z odgovori", - "addon.mod_feedback.non_anonymous_entries": "Neanonimni vnosi ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Udeleženci brez odziva ({{$a}})", - "addon.mod_feedback.not_selected": "Ni izbrano", - "addon.mod_feedback.not_started": "Ni začeto", - "addon.mod_feedback.numberoutofrange": "Število izven razpona", - "addon.mod_feedback.overview": "Pregled", - "addon.mod_feedback.page_after_submit": "Sporočilo ob zaključku", - "addon.mod_feedback.preview": "Predogled", - "addon.mod_feedback.previous_page": "Prejšnja stran", - "addon.mod_feedback.questions": "Vprašanja", - "addon.mod_feedback.response_nr": "Številka odgovora", - "addon.mod_feedback.responses": "Odgovori", - "addon.mod_feedback.save_entries": "Oddaj svoje odgovore", - "addon.mod_feedback.show_entries": "Prikaži odgovore", - "addon.mod_feedback.show_nonrespondents": "Prikaži ne-anketirance", - "addon.mod_feedback.started": "Začeto", - "addon.mod_feedback.this_feedback_is_already_submitted": "To dejavnost ste že zaključili.", - "addon.mod_folder.emptyfilelist": "Datotek ni na voljo.", - "addon.mod_folder.modulenameplural": "Mape", - "addon.mod_forum.addanewdiscussion": "Dodaj novo temo razprave", - "addon.mod_forum.addanewquestion": "Dodaj novo vprašanje", - "addon.mod_forum.addanewtopic": "Dodaj novo temo", - "addon.mod_forum.addtofavourites": "Označi to razpravo z zvezdico", - "addon.mod_forum.advanced": "Napredno", - "addon.mod_forum.cannotadddiscussion": "Dodajanje razprav na ta forum zahteva članstvo v skupini.", - "addon.mod_forum.cannotadddiscussionall": "Nimate dovoljenja za dodajanje nove teme razprave za vse udeležence.", - "addon.mod_forum.cannotcreatediscussion": "Ne morem ustvariti nove razprave", - "addon.mod_forum.couldnotadd": "Zaradi neznane napake vaše objave ni bilo možno dodati", - "addon.mod_forum.couldnotupdate": "Zaradi neznane napake vaše objave ni bilo možno posodobiti", - "addon.mod_forum.cutoffdatereached": "Zaključni datum za objavljanje na tem forumu je dosežen, zato objavljanje ni več možno.", - "addon.mod_forum.delete": "Izbriši", - "addon.mod_forum.deletedpost": "Objava je bila izbrisana", - "addon.mod_forum.deletesure": "Ali ste prepričani, da želite izbrisati to objavo?", - "addon.mod_forum.discussion": "Razprava", - "addon.mod_forum.discussionlistsortbycreatedasc": "Razvrsti po datumu nastanka v naraščajočem vrstnem redu", - "addon.mod_forum.discussionlistsortbycreateddesc": "Razvrsti po datumu nastanka v padajočem vrstnem redu", - "addon.mod_forum.discussionlistsortbylastpostasc": "Razvrsti po datumu nastanka zadnje objave v naraščajočem vrstnem redu", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Razvrsti po datumu nastanka zadnje objave v padajočem vrstnem redu", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Razvrsti po številu odgovorov v naraščajočem vrstnem redu", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Razvrsti po številu odgovorov v padajočem vrstnem redu", - "addon.mod_forum.discussionlocked": "Ta razprava je zaklenjena, zato nanjo ni več mogoče odgovarjati.", - "addon.mod_forum.discussionpinned": "Pripeto", - "addon.mod_forum.discussionsubscription": "Naročnina na razpravo", - "addon.mod_forum.edit": "Uredi", - "addon.mod_forum.erroremptymessage": "Sporočilo objave ne sme biti prazno", - "addon.mod_forum.erroremptysubject": "Zadeva objave ne sme biti prazna", - "addon.mod_forum.errorgetforum": "Napaka pri pridobivanju podatkov foruma.", - "addon.mod_forum.errorgetgroups": "Napaka pri pridobivanju nastavitev skupine.", - "addon.mod_forum.errorposttoallgroups": "Nove razprave ni bilo mogoče ustvariti v vseh skupinah.", - "addon.mod_forum.favouriteupdated": "Vaša možnost označevanja z zvezdico je bila posodobljena.", - "addon.mod_forum.forumnodiscussionsyet": "Na tem forumu še ni razprav.", - "addon.mod_forum.group": "Skupina", - "addon.mod_forum.lastpost": "Zadnja objava", - "addon.mod_forum.lockdiscussion": "Zakleni to razpravo", - "addon.mod_forum.lockupdated": "Možnost zaklepanja je bila posodobljena.", - "addon.mod_forum.message": "Sporočilo", - "addon.mod_forum.modeflatnewestfirst": "Prikaži odgovore ravno, najprej novejše", - "addon.mod_forum.modeflatoldestfirst": "Prikaži odgovore ravno, najprej starejše", - "addon.mod_forum.modenested": "Prikaži odgovore v ugnezdeni obliki", - "addon.mod_forum.modulenameplural": "Forumi", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} razprav", - "addon.mod_forum.numreplies": "{{numreplies}} odgovorov", - "addon.mod_forum.pindiscussion": "Pripni to razpravo", - "addon.mod_forum.pinupdated": "Možnost pripetja je bila posodobljena.", - "addon.mod_forum.postisprivatereply": "To je zasebni odgovor. Drugi udeleženci ga ne morejo videti.", - "addon.mod_forum.posttoforum": "Objavi v forumu", - "addon.mod_forum.posttomygroups": "Objavi kopijo v vseh skupinah", - "addon.mod_forum.privatereply": "Zasebni odgovor", - "addon.mod_forum.re": "Odg:", - "addon.mod_forum.refreshdiscussions": "Osveži razprave", - "addon.mod_forum.refreshposts": "Osveži objave", - "addon.mod_forum.removefromfavourites": "Odstrani zvezdico pri tej razpravi", - "addon.mod_forum.reply": "Odgovori", - "addon.mod_forum.replyplaceholder": "Napišite svoj odgovor …", - "addon.mod_forum.subject": "Zadeva", - "addon.mod_forum.tagarea_forum_posts": "Forumske objave", - "addon.mod_forum.thisforumhasduedate": "Datum zapadlosti za objavljanje na tem forumu je {{$a}}.", - "addon.mod_forum.thisforumisdue": "Datum zapadlosti za objavljanje na tem forumu je bil {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Odkleni razpravo", - "addon.mod_forum.unpindiscussion": "Odpni razpravo", - "addon.mod_forum.unread": "Neprebrano", - "addon.mod_forum.unreadpostsnumber": "{{$a}} neprebranih objav", - "addon.mod_forum.yourreply": "Vaš odgovor", - "addon.mod_glossary.addentry": "Dodaj nov vnos", - "addon.mod_glossary.aliases": "Ključne besede", - "addon.mod_glossary.attachment": "Priponka", - "addon.mod_glossary.browsemode": "Brskaj po vnosih", - "addon.mod_glossary.byalphabet": "Po abecedi", - "addon.mod_glossary.byauthor": "Razvrsti po avtorju", - "addon.mod_glossary.bycategory": "Razvrsti po kategoriji", - "addon.mod_glossary.bynewestfirst": "Novejši najprej", - "addon.mod_glossary.byrecentlyupdated": "Nedavno posodobljeni", - "addon.mod_glossary.bysearch": "Išči", - "addon.mod_glossary.cannoteditentry": "Vnosa ni mogoče urediti", - "addon.mod_glossary.casesensitive": "Ta vnos razlikuje velike in male črke", - "addon.mod_glossary.categories": "Kategorije", - "addon.mod_glossary.concept": "Pojem", - "addon.mod_glossary.definition": "Definicija", - "addon.mod_glossary.entriestobesynced": "Vnosi, ki jih je potrebno sinhronizirati", - "addon.mod_glossary.entrypendingapproval": "Ta vnos čaka na odobritev.", - "addon.mod_glossary.entryusedynalink": "Ta vnos naj bo samodejno povezan", - "addon.mod_glossary.errconceptalreadyexists": "Pojem že obstaja. Slovar ne dovoli podvojenih vnosov.", - "addon.mod_glossary.errorloadingentries": "Pri nalaganju vnosov je prišlo do napake.", - "addon.mod_glossary.errorloadingentry": "Pri nalaganju vnosa je prišlo do napake.", - "addon.mod_glossary.errorloadingglossary": "Pri nalaganju besednjaka je prišlo do napake.", - "addon.mod_glossary.fillfields": "Pojem in definicija sta obvezni polji.", - "addon.mod_glossary.fullmatch": "Ujemanje samo celih besed", - "addon.mod_glossary.linking": "Samodejno povezovanje", - "addon.mod_glossary.modulenameplural": "Slovarji", - "addon.mod_glossary.noentriesfound": "Vnosov ni bilo mogoče najti.", - "addon.mod_glossary.searchquery": "Iskalna poizvedba", - "addon.mod_glossary.tagarea_glossary_entries": "Slovarski vnosi", - "addon.mod_imscp.deploymenterror": "Napaka vsebinskega paketa!", - "addon.mod_imscp.modulenameplural": "IMS vsebinski paketi", - "addon.mod_imscp.showmoduledescription": "Prikaži opis", - "addon.mod_imscp.toc": "TOC", - "addon.mod_lesson.answer": "Odgovor", - "addon.mod_lesson.attempt": "Poskus: {{$a}}", - "addon.mod_lesson.attemptheader": "Poskus", - "addon.mod_lesson.attemptsremaining": "Število preostalih poskusov: {{$a}}", - "addon.mod_lesson.averagescore": "Povprečen rezultat", - "addon.mod_lesson.averagetime": "Povprečen čas", - "addon.mod_lesson.branchtable": "Vsebina", - "addon.mod_lesson.cannotfindattempt": "Napaka: poskusa ni mogoče najti", - "addon.mod_lesson.cannotfinduser": "Napaka: uporabnikov ni mogoče najti", - "addon.mod_lesson.clusterjump": "Še neogledano vprašanje v gruči", - "addon.mod_lesson.completed": "Dokončano", - "addon.mod_lesson.congratulations": "Čestitke! Konec lekcije je dosežen.", - "addon.mod_lesson.continue": "Nadaljuj", - "addon.mod_lesson.continuetonextpage": "Nadaljuj na naslednjo stran.", - "addon.mod_lesson.defaultessayresponse": "Izvajalec bo ocenil esej.", - "addon.mod_lesson.detailedstats": "Podrobna statistika", - "addon.mod_lesson.didnotanswerquestion": "Vprašanje ni bilo odgovorjeno.", - "addon.mod_lesson.displayofgrade": "Prikaz ocen (samo za udeležence)", - "addon.mod_lesson.displayscorewithessays": "

                      Prejeli ste {{$a.score}} točk od skupno {{$a.tempmaxgrade}} za samodejno ocenjene odgovore.

                      \n

                      Eseji ({{$a.essayquestions}}) bodo ocenjeni in dodani v skupni rezultat naknadno.

                      \n

                      Trenutni rezultat (brez esejev) je {{$a.score}} od {{$a.grade}}.

                      ", - "addon.mod_lesson.displayscorewithoutessays": "Trenutna ocena je {{$a.score}} (od {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Geslo ne more biti prazno", - "addon.mod_lesson.enterpassword": "Zahtevano je geslo:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Nobeno vprašanje ni bilo odgovorjeno. Prejetih 0 točk.", - "addon.mod_lesson.errorprefetchrandombranch": "Ta lekcija vsebuje skok na naključno stran z vsebino. Dokler se ne zažene v spletnem brskalniku, je v aplikaciji ni mogoče poskusiti.", - "addon.mod_lesson.errorreviewretakenotlast": "Tega poskusa ni mogoče več oddati, ker je bil končan že naslednji poskus.", - "addon.mod_lesson.finish": "Končaj", - "addon.mod_lesson.finishretakeoffline": "Ta poskus je bil končan brez povezave na splet.", - "addon.mod_lesson.firstwrong": "Odgovor je žal napačen. Bi želeli ponovno poskusiti? Tudi če bo tokrat odgovor pravilen, točk ne boste prejeli.", - "addon.mod_lesson.gotoendoflesson": "Pojdi na konec lekcije", - "addon.mod_lesson.grade": "Ocena", - "addon.mod_lesson.highscore": "Najvišji rezultat", - "addon.mod_lesson.hightime": "Najdaljši čas", - "addon.mod_lesson.leftduringtimed": "Zapustili ste časovno merjeno lekcijo.
                      Prosimo, kliknite Nadaljuj za ponovni začetek lekcije.", - "addon.mod_lesson.leftduringtimednoretake": "Zapustili ste časovno merjeno lekcijo. Ponavljanje ali nadaljevanje
                      ni dovoljeno.", - "addon.mod_lesson.lessonmenu": "Meni lekcije", - "addon.mod_lesson.lessonstats": "Statistika lekcije", - "addon.mod_lesson.linkedmedia": "Povezane medijske vsebine", - "addon.mod_lesson.loginfail": "Prijava ni bila uspešna. Prosimo, poskusite znova ...", - "addon.mod_lesson.lowscore": "Najnižji rezultat", - "addon.mod_lesson.lowtime": "Najkrajši čas", - "addon.mod_lesson.maximumnumberofattemptsreached": "Doseženo največje dovoljeno število poskusov - Premik na naslednjo stran", - "addon.mod_lesson.modattemptsnoteacher": "Pregled udeležencev deluje samo za udeležence.", - "addon.mod_lesson.modulenameplural": "Lekcije", - "addon.mod_lesson.noanswer": "Ni podanega odgovora. Prosimo, vrnite se in oddajte odgovor.", - "addon.mod_lesson.nolessonattempts": "Za to lekcijo še ni bilo opravljenih poskusov.", - "addon.mod_lesson.nolessonattemptsgroup": "Za to lekcijo še ni bilo opravljenih poskusov s strani članov skupine {{$a}}.", - "addon.mod_lesson.notcompleted": "Ni dokončano", - "addon.mod_lesson.numberofcorrectanswers": "Število pravilnih odgovorov: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Število odgovorjenih vprašanj: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Število odgovorjenih vprašanj: {{$a.nquestions}} (Odgovoriti je potrebno najmanj na {{$a.minquestions}} vprašanj)", - "addon.mod_lesson.ongoingcustom": "Do sedaj osvojenih {{$a.score}} točk od {{$a.currenthigh}} možnih.", - "addon.mod_lesson.ongoingnormal": "Pravilno ste odgovorili v {{$a.correct}} od {{$a.viewed}} poskusov.", - "addon.mod_lesson.or": "ALI", - "addon.mod_lesson.overview": "Pregled", - "addon.mod_lesson.preview": "Predogled", - "addon.mod_lesson.progressbarteacherwarning2": "Vrstice napredovanja ne boste videli, ker lahko to lekcijo urejate.", - "addon.mod_lesson.progresscompleted": "Zaključenih {{$a}} % lekcije", - "addon.mod_lesson.question": "Vprašanje", - "addon.mod_lesson.rawgrade": "Vmesna ocena", - "addon.mod_lesson.reports": "Poročila", - "addon.mod_lesson.response": "Odziv", - "addon.mod_lesson.retakefinishedinsync": "Poskus brez povezave je bil sinhroniziran. Ali ga želite pregledati?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Pregled", - "addon.mod_lesson.reviewlesson": "Pregled lekcije", - "addon.mod_lesson.reviewquestionback": "Da, želim poskusiti znova", - "addon.mod_lesson.reviewquestioncontinue": "Ne, želim nadaljevati z naslednjim vprašanjem", - "addon.mod_lesson.secondpluswrong": "Ne povsem. Želiš poskusiti znova?", - "addon.mod_lesson.submit": "Oddaj", - "addon.mod_lesson.teacherjumpwarning": "V tej lekciji je uporabljen skok {{$a.cluster}} ali {{$a.unseen}}. Namesto tega bo uporabljen skok na naslednjo stran. Prijavite se kot udeleženec za preizkus teh skokov.", - "addon.mod_lesson.teacherongoingwarning": "Trenutni rezultat je prikazan samo udeležencem. Prijavite se kot udeleženec za preizkus prikaza trenutnega rezultata.", - "addon.mod_lesson.teachertimerwarning": "Odštevalnik časa deluje samo za udeležence. Prijavite se kot udeleženec za preizkus odštevalnika.", - "addon.mod_lesson.thatsthecorrectanswer": "To je pravilen odgovor.", - "addon.mod_lesson.thatsthewronganswer": "To je napačen odgovor.", - "addon.mod_lesson.timeremaining": "Preostali čas", - "addon.mod_lesson.timetaken": "Porabljeni čas", - "addon.mod_lesson.unseenpageinbranch": "Še neogledano vprašanje v okviru veje", - "addon.mod_lesson.warningretakefinished": "Poskus je bil končan na spletnem mestu.", - "addon.mod_lesson.welldone": "Dobro opravljeno!", - "addon.mod_lesson.youhaveseen": "Videli ste že več kot eno stran te lekcije.
                      Želite nadaljevati na zadnji strani, ki ste si jo ogledali?
                      ", - "addon.mod_lesson.youranswer": "Vaš odgovor", - "addon.mod_lesson.yourcurrentgradeisoutof": "Vaša trenutna ocena je {{$a.grade}} od {{$a.total}}", - "addon.mod_lesson.youshouldview": "Odgovoriti je potrebno na vsaj: {{$a}}", - "addon.mod_lti.errorgetlti": "Napaka pri pridobivanju podatkov modula.", - "addon.mod_lti.errorinvalidlaunchurl": "Začetni URL naslov ni veljaven.", - "addon.mod_lti.launchactivity": "Začnite dejavnost", - "addon.mod_lti.modulenameplural": "Zunanja orodja", - "addon.mod_page.errorwhileloadingthepage": "Napaka med nalaganjem vsebine na strani.", - "addon.mod_page.modulenameplural": "Strani", - "addon.mod_quiz.answercolon": "Odgovor:", - "addon.mod_quiz.attemptfirst": "Prvi poskus", - "addon.mod_quiz.attemptlast": "Zadnji poskus", - "addon.mod_quiz.attemptnumber": "Poskus", - "addon.mod_quiz.attemptquiznow": "Poskusi kviz zdaj", - "addon.mod_quiz.attemptstate": "Stanje", - "addon.mod_quiz.canattemptbutnotsubmit": "Kviz lahko rešite v aplikaciji, vendar ga morate oddati v brskalniku iz naslednjih razlogov:", - "addon.mod_quiz.cannotsubmitquizdueto": "Odgovorov kviza ni mogoče oddati iz naslednjih razlogov:", - "addon.mod_quiz.clearchoice": "Počisti mojo izbiro", - "addon.mod_quiz.comment": "Komentar", - "addon.mod_quiz.completedon": "Dokončano dne", - "addon.mod_quiz.confirmclose": "Po oddaji ne boste več mogli spremeniti vaših odgovorov za ta poskus.", - "addon.mod_quiz.confirmcontinueoffline": "Ta poskus ni bil sinhroniziran od {{$a}}. Če ste z reševanjem nadaljevali v drugi napravi, lahko izgubite podatke.", - "addon.mod_quiz.confirmleavequizonerror": "Med shranjevanjem odgovorov je prišlo do napake. Ali ste prepričani, da želite zapustiti kviz?", - "addon.mod_quiz.confirmstart": "Kviz ima časovno omejitev {{$a}}. Čas se bo začel odštevati od trenutka, ko začnete z reševanjem, oddati pa morate, preden se čas izteče. Ste prepričani, da želite začeti?", - "addon.mod_quiz.confirmstartheader": "Časovno omejen kviz", - "addon.mod_quiz.connectionerror": "Ni omrežne povezave. (Samodejno shranjevanje ni uspelo.)\n\nZapomnite si vse odgovore na tej strani v zadnjih minutah, nato se poskusite ponovno povezati.\n\nKo se povezava ponovno vzpostavi, se bi morali vaši odgovoriti shraniti, to sporočilo pa bo izginilo.", - "addon.mod_quiz.continueattemptquiz": "Nadaljuj zadnji poskus", - "addon.mod_quiz.continuepreview": "Nadaljuj zadnji predogled", - "addon.mod_quiz.errorbehaviournotsupported": "Tega kviza v aplikaciji ni mogoče rešiti, ker aplikacija ne podpira teh tipov vprašanj:", - "addon.mod_quiz.errordownloading": "Napaka pri prenosu zahtevanih podatkov.", - "addon.mod_quiz.errorgetattempt": "Napaka pri pridobivanju podatkov o poskusu.", - "addon.mod_quiz.errorgetquestions": "Napaka pri pridobivanju vprašanj.", - "addon.mod_quiz.errorgetquiz": "Napaka pri pridobivanju podatkov kviza.", - "addon.mod_quiz.errorparsequestions": "Pri branju vprašanj je prišlo do napake. Poskusite odpreti kviz v spletnem brskalniku.", - "addon.mod_quiz.errorquestionsnotsupported": "Tega kviza v aplikaciji ni mogoče rešiti, ker vsebuje samo tipe vprašanj, ki jih aplikacija ne podpira:", - "addon.mod_quiz.errorrulesnotsupported": "Tega kviza v aplikaciji ni mogoče rešiti, ker ima pravila za dostop, ki jih aplikacija ne podpira:", - "addon.mod_quiz.errorsaveattempt": "Med shranjevanjem podatkov o poskusih je prišlo do napake.", - "addon.mod_quiz.feedback": "Odziv", - "addon.mod_quiz.finishattemptdots": "Končak poskus...", - "addon.mod_quiz.finishnotsynced": "Končano, vendar ne sinhronizirano", - "addon.mod_quiz.grade": "Ocena", - "addon.mod_quiz.gradeaverage": "Povprečna ocena", - "addon.mod_quiz.gradehighest": "Najvišja ocena", - "addon.mod_quiz.grademethod": "Način ocenjevanja", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Točke", - "addon.mod_quiz.modulenameplural": "Kvizi", - "addon.mod_quiz.mustbesubmittedby": "Rešitve morate oddati do {{$a}}.", - "addon.mod_quiz.noquestions": "Nobeno vprašanje še ni dodano", - "addon.mod_quiz.noreviewattempt": "Nimate dovoljenja za pregled tega poskusa.", - "addon.mod_quiz.notyetgraded": "Še neocenjeno", - "addon.mod_quiz.opentoc": "Odpri navigacijsko okno", - "addon.mod_quiz.outof": "{{$a.grade}} od možno največ {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} od možno največ {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Odziv za poskus", - "addon.mod_quiz.overdue": "Zakasnelo", - "addon.mod_quiz.overduemustbesubmittedby": "Poskus je potekel in odgovore bi že morali oddati. Če želite, da bo kviz ocenjen, ga morate oddati do {{$a}}, sicer ne boste dobili točk.", - "addon.mod_quiz.preview": "Predogled", - "addon.mod_quiz.previewquiznow": "Takojšnji predogled kviza", - "addon.mod_quiz.question": "Vprašanje", - "addon.mod_quiz.quiznavigation": "Navigacija po kvizu", - "addon.mod_quiz.quizpassword": "Geslo kviza", - "addon.mod_quiz.reattemptquiz": "Ponovni poskus kviza", - "addon.mod_quiz.requirepasswordmessage": "Ta poskus tega kviza morate poznati geslo kviza", - "addon.mod_quiz.returnattempt": "Nazaj na reševanje", - "addon.mod_quiz.review": "Pregled", - "addon.mod_quiz.reviewofattempt": "Pregled poskusa {{$a}}", - "addon.mod_quiz.reviewofpreview": "Pregled predogleda", - "addon.mod_quiz.showall": "Pokaži vsa vprašanja na eni strani", - "addon.mod_quiz.showeachpage": "Prikaži eno stran na enkrat", - "addon.mod_quiz.startattempt": "Začni z reševanjem", - "addon.mod_quiz.startedon": "Začeto dne", - "addon.mod_quiz.stateabandoned": "Nikoli oddano", - "addon.mod_quiz.statefinished": "Zaključeno", - "addon.mod_quiz.statefinisheddetails": "Oddano {{$a}}", - "addon.mod_quiz.stateinprogress": "V teku", - "addon.mod_quiz.stateoverdue": "Poteklo", - "addon.mod_quiz.stateoverduedetails": "Morate oddati do {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Oddaj vse in zaključi", - "addon.mod_quiz.summaryofattempt": "Povzetek poskusa", - "addon.mod_quiz.summaryofattempts": "Povzetek vaših predhodnih poskusov", - "addon.mod_quiz.timeleft": "Preostali čas", - "addon.mod_quiz.timetaken": "Porabljeni čas", - "addon.mod_quiz.warningattemptfinished": "Poskus brez povezave na splet je zavržen, ker je bil končan na spletnem mestu ali ni bil najden.", - "addon.mod_quiz.warningdatadiscarded": "Nekateri odgovori brez povezave na splet so bili zavrženi, ker so bila vprašanja spremenjena na spletu.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Poskus je nedokončan, ker so bili nekateri odgovori brez povezave na splet zavrženi. Preglejte svoje odgovore in nato poskusite znova.", - "addon.mod_quiz.warningquestionsnotsupported": "Ta kviz vsebuje tipe vprašanj, ki jih aplikacija ne podpira:", - "addon.mod_quiz.yourfinalgradeis": "Vaša končna ocena za ta kviz je {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Napaka med nalaganjem vsebine.", - "addon.mod_resource.modifieddate": "Spremenjeno {{$a}}", - "addon.mod_resource.modulenameplural": "Viri", - "addon.mod_resource.openthefile": "Odpri datoteko", - "addon.mod_resource.uploadeddate": "Naloženo {{$a}}", - "addon.mod_scorm.asset": "SCORM element", - "addon.mod_scorm.assetlaunched": "SCORM element - pogledan", - "addon.mod_scorm.attempts": "poskusi", - "addon.mod_scorm.averageattempt": "Povprečje poskusov", - "addon.mod_scorm.browse": "Predogled", - "addon.mod_scorm.browsed": "Pregledano", - "addon.mod_scorm.browsemode": "Način predogleda", - "addon.mod_scorm.cannotcalculategrade": "Ocene ni bilo mogoče izračunati.", - "addon.mod_scorm.completed": "Dokončano", - "addon.mod_scorm.contents": "Vsebina", - "addon.mod_scorm.dataattemptshown": "Ti podatki pripadajo poskusu številka {{number}}.", - "addon.mod_scorm.enter": "Vstopi", - "addon.mod_scorm.errorcreateofflineattempt": "Pri ustvarjanju novega poskusa brez povezave na splet je prišlo do napake. Prosim poskusite znova.", - "addon.mod_scorm.errordownloadscorm": "Napaka pri prenosu SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Napaka pri pridobivanju podatkov SCORM.", - "addon.mod_scorm.errorinvalidversion": "Žal aplikacija podpira samo SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Prenos paketov SCORM je onemogočen. Obrnite se na skrbnika spletnega mesta.", - "addon.mod_scorm.errornovalidsco": "V tem paketu SCORM ni viden SCO za nalaganje.", - "addon.mod_scorm.errorpackagefile": "Žal aplikacija podpira samo pakete ZIP.", - "addon.mod_scorm.errorsyncscorm": "Med sinhronizacijo je prišlo do napake. Prosim poskusite ponovno.", - "addon.mod_scorm.exceededmaxattempts": "Dosegli ste največje število poskusov.", - "addon.mod_scorm.failed": "Spodletelo", - "addon.mod_scorm.firstattempt": "Prvi poskus", - "addon.mod_scorm.gradeaverage": "Povprečna ocena", - "addon.mod_scorm.gradeforattempt": "Ocena za poskus", - "addon.mod_scorm.gradehighest": "Najvišja ocena", - "addon.mod_scorm.grademethod": "Način ocenjevanja", - "addon.mod_scorm.gradereported": "Ocena prijavljena", - "addon.mod_scorm.gradescoes": "Izobraževalni objekti", - "addon.mod_scorm.gradesum": "Vsota ocen", - "addon.mod_scorm.highestattempt": "Najvišji poskus", - "addon.mod_scorm.incomplete": "Nepopolno", - "addon.mod_scorm.lastattempt": "Zadnji zaključen poskus", - "addon.mod_scorm.modulenameplural": "SCORM paketi", - "addon.mod_scorm.newattempt": "Začni nov poskus", - "addon.mod_scorm.noattemptsallowed": "Število dovoljenih poskusov", - "addon.mod_scorm.noattemptsmade": "Število vaših poskusov", - "addon.mod_scorm.notattempted": "Brez udeležbe", - "addon.mod_scorm.offlineattemptnote": "Ta poskus vsebuje podatke, ki niso bili sinhronizirani.", - "addon.mod_scorm.offlineattemptovermax": "Tega poskusa ni mogoče poslati, ker ste presegli največje število poskusov.", - "addon.mod_scorm.organizations": "Organizacije", - "addon.mod_scorm.passed": "Zaključeno", - "addon.mod_scorm.reviewmode": "Način pregledovanja", - "addon.mod_scorm.score": "Rezultat", - "addon.mod_scorm.scormstatusnotdownloaded": "Ta paket SCORM ni prenesen. Ko ga odprete, bo prenesen samodejno.", - "addon.mod_scorm.scormstatusoutdated": "Ta paket SCORM je bil spremenjen od zadnjega prenosa. Ko ga odprete, bo prenesen samodejno.", - "addon.mod_scorm.suspended": "Suspendiran", - "addon.mod_scorm.toc": "TOC", - "addon.mod_scorm.warningofflinedatadeleted": "Nekateri podatki brez povezave na splet iz poskusa {{number}} so bili zavrženi, ker jih ni bilo mogoče šteti za nov poskus.", - "addon.mod_scorm.warningsynconlineincomplete": "Nekaterih poskusov ni bilo mogoče sinhronizirati s spletnim mestom, ker zadnji spletni poskus še ni končan. Pred novim poskusom zaključite spletni poskus.", - "addon.mod_survey.cannotsubmitsurvey": "Pri oddaji ankete je žal prišlo do težave. Prosim poskusite ponovno.", - "addon.mod_survey.errorgetsurvey": "Napaka pri pridobivanju podatkov ankete.", - "addon.mod_survey.ifoundthat": "Ugotavljam,", - "addon.mod_survey.ipreferthat": "Želim si,", - "addon.mod_survey.modulenameplural": "Ankete", - "addon.mod_survey.responses": "Odgovori", - "addon.mod_survey.results": "Rezultati", - "addon.mod_survey.surveycompletednograph": "Dokončali ste anketo.", - "addon.mod_url.accessurl": "Dostop do URL naslova", - "addon.mod_url.modulenameplural": "URL-ji", - "addon.mod_url.pointingtourl": "URL, na katerega kažejo viri.", - "addon.mod_wiki.cannoteditpage": "Ne morete urejati te strani.", - "addon.mod_wiki.createpage": "Ustvari stran", - "addon.mod_wiki.editingpage": "Uredi to stran '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Pri nalaganju strani je prišlo do napake.", - "addon.mod_wiki.errornowikiavailable": "Ta wiki še nima vsebine.", - "addon.mod_wiki.gowikihome": "Pojdite na prvo stran wikija", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wikiji", - "addon.mod_wiki.newpagehdr": "Nova stran", - "addon.mod_wiki.newpagetitle": "Naslov nove strani", - "addon.mod_wiki.nocontent": "Za to stran ni vsebine", - "addon.mod_wiki.notingroup": "Niste v skupini", - "addon.mod_wiki.pageexists": "Stran že obstaja.", - "addon.mod_wiki.pagename": "Ime strani", - "addon.mod_wiki.subwiki": "Pod-wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki strani", - "addon.mod_wiki.titleshouldnotbeempty": "Naslov ne sme biti prazen", - "addon.mod_wiki.viewpage": "Prikaži stran", - "addon.mod_wiki.wikipage": "Wiki stran", - "addon.mod_wiki.wrongversionlock": "Med vašim urejanjem je drug uporabnik uredil to stran in vaša vsebina je zastarala.", - "addon.mod_workshop.alreadygraded": "Že ocenjeno", - "addon.mod_workshop.areainstructauthors": "Navodila za oddajo", - "addon.mod_workshop.areainstructreviewers": "Navodila za ovrednotenje", - "addon.mod_workshop.assess": "Ovrednoti", - "addon.mod_workshop.assessedsubmission": "Ovrednotena oddaja", - "addon.mod_workshop.assessmentform": "Obrazec ovrednotenja", - "addon.mod_workshop.assessmentsettings": "Nastavitve ovrednotenja", - "addon.mod_workshop.assessmentstrategynotsupported": "Strategija ocenjevanja {{$a}} ni podprta", - "addon.mod_workshop.assessmentweight": "Teža ovrednotenja", - "addon.mod_workshop.assignedassessments": "Določene oddaje za ovrednotenje", - "addon.mod_workshop.assignedassessmentsnone": "Nimate določenih oddaj za ovrednotenje", - "addon.mod_workshop.conclusion": "Zaključek", - "addon.mod_workshop.createsubmission": "Dodaj oddajo", - "addon.mod_workshop.deletesubmission": "Zbriši oddajo", - "addon.mod_workshop.editsubmission": "Uredi oddajo", - "addon.mod_workshop.feedbackauthor": "Povratne informacije za avtorja", - "addon.mod_workshop.feedbackby": "Povratne informacije od {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Povratne informacije za pregledovalca", - "addon.mod_workshop.givengrades": "Dodeljene ocene", - "addon.mod_workshop.gradecalculated": "Sešteta ocena za oddajo", - "addon.mod_workshop.gradeinfo": "Ocena: {{$a.received}} od {{$a.max}}", - "addon.mod_workshop.gradeover": "Povozi oceno za oddajo", - "addon.mod_workshop.gradesreport": "Poročilo o ocenah delavnice", - "addon.mod_workshop.gradinggrade": "Ocena ovrednotenja", - "addon.mod_workshop.gradinggradecalculated": "Izračunana ocena za ovrednotenje", - "addon.mod_workshop.gradinggradeof": "Ocena za ovrednotenje (od {{$a}})", - "addon.mod_workshop.gradinggradeover": "Povozi oceno za ovrednotenje", - "addon.mod_workshop.modulenameplural": "Delavnice", - "addon.mod_workshop.nogradeyet": "Ni še ocene", - "addon.mod_workshop.notassessed": "Še ni bilo ovrednoteno", - "addon.mod_workshop.notoverridden": "Ni bilo povoženo", - "addon.mod_workshop.noyoursubmission": "Svojega dela še niste oddali", - "addon.mod_workshop.overallfeedback": "Skupni odziv", - "addon.mod_workshop.publishedsubmissions": "Objavljene oddaje", - "addon.mod_workshop.publishsubmission": "Objavi oddajo", - "addon.mod_workshop.publishsubmission_help": "Objavljene oddaje so na voljo ostalim, ko je delavnica zaprta.", - "addon.mod_workshop.reassess": "Ponovno ovrednoti", - "addon.mod_workshop.receivedgrades": "Prejete ocene", - "addon.mod_workshop.submissionattachment": "Priponka", - "addon.mod_workshop.submissioncontent": "Vsebina oddaje", - "addon.mod_workshop.submissiondeleteconfirm": "Ste prepričani, da želite izbrisati to oddajo?", - "addon.mod_workshop.submissiongrade": "Ocena za oddajo", - "addon.mod_workshop.submissiongradeof": "Ocena za oddajo (of {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Vnesti morate besedilo ali dodati datoteko.", - "addon.mod_workshop.submissionrequiredtitle": "Vnesti morate naslov.", - "addon.mod_workshop.submissionsreport": "Poročilo o oddajah v delavnici", - "addon.mod_workshop.submissiontitle": "Naslov", - "addon.mod_workshop.switchphase10": "Preklopi v fazo nastavitve", - "addon.mod_workshop.switchphase20": "Preklopi v fazo oddaje", - "addon.mod_workshop.switchphase30": "Preklopi v fazo ovrednotenja", - "addon.mod_workshop.switchphase40": "Preklopi v fazo ocenjevanja", - "addon.mod_workshop.switchphase50": "Zaključi delavnico", - "addon.mod_workshop.userplan": "Načrtovalec delavnice", - "addon.mod_workshop.userplancurrentphase": "Aktivna faza", - "addon.mod_workshop.warningassessmentmodified": "Oddaja je bila spremenjena na spletnem mestu.", - "addon.mod_workshop.warningsubmissionmodified": "Ocena je bila spremenjena na spletnem mestu.", - "addon.mod_workshop.weightinfo": "Teža: {{$a}}", - "addon.mod_workshop.yourassessment": "Ovrednoteno od vas", - "addon.mod_workshop.yourassessmentfor": "Vaše ovrednotenje za {{$a}}", - "addon.mod_workshop.yourgrades": "Vaše ocene", - "addon.mod_workshop.yoursubmission": "Vaša oddaja", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Ocena za {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspekt {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Izbrati morate oceno za ta kriterij", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspekt {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Ocena za {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Vstavljanje {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Merilo {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Izbrati morate enega od teh predmetov", - "addon.notes.addnewnote": "Dodaj novo opombo", - "addon.notes.coursenotes": "Opombe predmeta", - "addon.notes.deleteconfirm": "Izbrišem to opombo?", - "addon.notes.eventnotecreated": "Opomba je ustvarjena", - "addon.notes.eventnotedeleted": "Opomba je izbrisana", - "addon.notes.nonotes": "Ni še opomb te vrste", - "addon.notes.note": "Opomba", - "addon.notes.notes": "Opombe", - "addon.notes.personalnotes": "Osebne opombe", - "addon.notes.publishstate": "Okvir", - "addon.notes.sitenotes": "Opombe spletnega mesta", - "addon.notes.userwithid": "Uporabnik z ID {{id}}", - "addon.notes.warningnotenotsent": "Opombe k predmetu {{course}} ni bilo mogoče dodati. {{error}}", - "addon.notifications.errorgetnotifications": "Napaka pri pridobivanju obvestil.", - "addon.notifications.markallread": "Označi vse kot prebrano", - "addon.notifications.notificationpreferences": "Nastavitve obvestil", - "addon.notifications.notifications": "Obvestila", - "addon.notifications.playsound": "Predvajaj zvok", - "addon.notifications.therearentnotificationsyet": "Ni obvestil.", - "addon.storagemanager.deletecourse": "Naložite vse podatke predmeta", - "addon.storagemanager.deletedatafrom": "Prenos podatkov iz {{name}}", - "addon.storagemanager.info": "Datoteke, shranjene v vaši napravi, omogočajo, da aplikacija deluje hitreje in omogoča uporabo aplikacije brez povezave na splet. Če želite sprostiti prostor za shranjevanje, lahko datoteke brez skrbi odstranite.", - "addon.storagemanager.managestorage": "Upravljanje prostora za shranjevanje", - "addon.storagemanager.storageused": "Porabljen prostor:", - "assets.countries.AD": "Andora", - "assets.countries.AE": "Združeni arabski emirati", - "assets.countries.AF": "Afganistan", - "assets.countries.AG": "Antigva in Barbuda", - "assets.countries.AI": "Angvila", - "assets.countries.AL": "Albanija", - "assets.countries.AM": "Armenija", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktika", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Ameriška Samoa", - "assets.countries.AT": "Avstrija", - "assets.countries.AU": "Avstralija", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland Islands", - "assets.countries.AZ": "Azerbajdžan", - "assets.countries.BA": "Bosna in Hercegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladeš", - "assets.countries.BE": "Belgija", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgarija", - "assets.countries.BH": "Bahrajn", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermudi", - "assets.countries.BN": "Brunej", - "assets.countries.BO": "Bolivija", - "assets.countries.BQ": "Bonaire, Sint Eustatius in Saba", - "assets.countries.BR": "Brazilija", - "assets.countries.BS": "Bahami", - "assets.countries.BT": "Butan", - "assets.countries.BV": "Bouvetov otok", - "assets.countries.BW": "Bocvana", - "assets.countries.BY": "Belorusija", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Kokosovi (Keeling) otoki", - "assets.countries.CD": "Kongo, Republika", - "assets.countries.CF": "Centralnoafriška republika", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Švica", - "assets.countries.CI": "Slonokoščena obala", - "assets.countries.CK": "Cookovi otoki", - "assets.countries.CL": "Čile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Kitajska", - "assets.countries.CO": "Kolumbija", - "assets.countries.CR": "Kostarika", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Zelenortski otoki", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Božični otok", - "assets.countries.CY": "Ciper", - "assets.countries.CZ": "Češka", - "assets.countries.DE": "Nemčija", - "assets.countries.DJ": "Džibuti", - "assets.countries.DK": "Danska", - "assets.countries.DM": "Dominika", - "assets.countries.DO": "Dominikanska republika", - "assets.countries.DZ": "Alžirija", - "assets.countries.EC": "Ekvador", - "assets.countries.EE": "Estonija", - "assets.countries.EG": "Egipt", - "assets.countries.EH": "Zahodna Sahara", - "assets.countries.ER": "Eritreja", - "assets.countries.ES": "Španija", - "assets.countries.ET": "Etiopija", - "assets.countries.FI": "Finska", - "assets.countries.FJ": "Fidži", - "assets.countries.FK": "Falklandski otoki (Malvini)", - "assets.countries.FM": "Mikronezija, Federativne države", - "assets.countries.FO": "Ferski otoki", - "assets.countries.FR": "Francija", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Združeno kraljestvo Velika Britanija in Severna Irska", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Gruzija", - "assets.countries.GF": "Francoska Gvajana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Gana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grenlandija", - "assets.countries.GM": "Gambija", - "assets.countries.GN": "Gvineja", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Ekvatorialna Gvineja", - "assets.countries.GR": "Grčija", - "assets.countries.GS": "Južna Georgia in Južni Sandwichevi otoki", - "assets.countries.GT": "Gvatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Gvineja Bissau", - "assets.countries.GY": "Gvajana", - "assets.countries.HK": "Hongkong", - "assets.countries.HM": "Heardov otok in McDonaldovi otoki", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Hrvaška", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Madžarska", - "assets.countries.ID": "Indonezija", - "assets.countries.IE": "Irska", - "assets.countries.IL": "Izrael", - "assets.countries.IM": "Isle Of Man", - "assets.countries.IN": "Indija", - "assets.countries.IO": "Britansko ozemlje Indijskega oceana", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran, Islamska republika", - "assets.countries.IS": "Islandija", - "assets.countries.IT": "Italija", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamajka", - "assets.countries.JO": "Jordanija", - "assets.countries.JP": "Japonska", - "assets.countries.KE": "Kenija", - "assets.countries.KG": "Kirgizistan", - "assets.countries.KH": "Kambodža", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komori", - "assets.countries.KN": "Saint Kitts in Nevis", - "assets.countries.KP": "Koreja, Demokratična republika", - "assets.countries.KR": "Koreja, Republika", - "assets.countries.KW": "Kuvajt", - "assets.countries.KY": "Kajmanski otoki", - "assets.countries.KZ": "Kazahstan", - "assets.countries.LA": "Laos, Demokratična republika", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Lihtenštajn", - "assets.countries.LK": "Šrilanka", - "assets.countries.LR": "Liberija", - "assets.countries.LS": "Lesoto", - "assets.countries.LT": "Litva", - "assets.countries.LU": "Luksemburg", - "assets.countries.LV": "Latvija", - "assets.countries.LY": "Libija", - "assets.countries.MA": "Maroko", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Moldavija, Republika", - "assets.countries.ME": "Črna gora", - "assets.countries.MF": "Saint Martin", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshallovi otoki", - "assets.countries.MK": "Severna Makedonija", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Mjanmar", - "assets.countries.MN": "Mongolija", - "assets.countries.MO": "Macau", - "assets.countries.MP": "Severni Marianski otoki", - "assets.countries.MQ": "Martinik", - "assets.countries.MR": "Mavretanija", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Maldivi", - "assets.countries.MW": "Malavi", - "assets.countries.MX": "Mehika", - "assets.countries.MY": "Malezija", - "assets.countries.MZ": "Mozambik", - "assets.countries.NA": "Namibija", - "assets.countries.NC": "Nova Kaledonija", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolški otok", - "assets.countries.NG": "Nigerija", - "assets.countries.NI": "Nikaragva", - "assets.countries.NL": "Nizozemska", - "assets.countries.NO": "Norveška", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nova Zelandija", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Francoska Polinezija", - "assets.countries.PG": "Papua Nova Gvineja", - "assets.countries.PH": "Filipini", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Poljska", - "assets.countries.PM": "Saint Pierre in Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Portoriko", - "assets.countries.PS": "Palestina", - "assets.countries.PT": "Portugalska", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paragvaj", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Romunija", - "assets.countries.RS": "Srbija", - "assets.countries.RU": "Ruska federacija", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Saudova Arabija", - "assets.countries.SB": "Salomonovi otoki", - "assets.countries.SC": "Sejšeli", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Švedska", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Sveta Helena, Ascension in Tristan da Cunha", - "assets.countries.SI": "Slovenija", - "assets.countries.SJ": "Svalbard in otočje Jan Mayen", - "assets.countries.SK": "Slovaška", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalija", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Južni Sudan", - "assets.countries.ST": "Sao Tome in Pricipe", - "assets.countries.SV": "Salvador", - "assets.countries.SX": "Sint Maarten (nizozemski del)", - "assets.countries.SY": "Sirska arabska republika", - "assets.countries.SZ": "Svazi", - "assets.countries.TC": "Otoki Turks in Caicos", - "assets.countries.TD": "Čad", - "assets.countries.TF": "Francosko južno ozemlje", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tajska", - "assets.countries.TJ": "Tadžikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Vzhodni Timor", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunizija", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turčija", - "assets.countries.TT": "Trinidad in Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Tajvan", - "assets.countries.TZ": "Tanzanija, Združena republika", - "assets.countries.UA": "Ukrajina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "Združene države Amerike", - "assets.countries.UY": "Urugvaj", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Sveti sedež (Vatikanska mestna država)", - "assets.countries.VC": "Saint Vincent in Grenadine", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Deviški otoki (Britanski)", - "assets.countries.VI": "Deviški otoki (ZDA)", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Otoki Wallis in Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Južna Afrika", - "assets.countries.ZM": "Zambija", - "assets.countries.ZW": "Zimbabve", - "assets.mimetypes.application/epub_zip": "E-knjiga EPUB", - "assets.mimetypes.application/msword": "dokument Word", - "assets.mimetypes.application/pdf": "dokument PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Varnostna kopija Moodle", - "assets.mimetypes.application/vnd.ms-excel": "preglednica Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 delovni zvezek z makroji", - "assets.mimetypes.application/vnd.ms-powerpoint": "predstavitev PowerPoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Razpredelnica OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Predloga za razpredelnico OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Besedilni dokument OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Predloga za besedilni dokument OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Predloga za spletno stran OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Predstavitev Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Projekcija Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Preglednica Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Predloga Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word dokument", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Predstavitev iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Razpredelnica iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Dokument iWork Pages", - "assets.mimetypes.application/x-javascript": "JavaScript izvorna koda", - "assets.mimetypes.application/x-mspublisher": "Dokument Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Flash animacija", - "assets.mimetypes.application/xhtml_xml": "XHTML dokument", - "assets.mimetypes.archive": "Arhiv ({{$a.EXT}})", - "assets.mimetypes.audio": "Zvočna datoteka ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Datoteka", - "assets.mimetypes.group:archive": "Datoteke iz arhiva", - "assets.mimetypes.group:audio": "Zvočne datoteke", - "assets.mimetypes.group:document": "Dokumenti", - "assets.mimetypes.group:html_audio": "Zvočne datoteke, z nativno brskalniško podporo", - "assets.mimetypes.group:html_track": "HTML track datoteke", - "assets.mimetypes.group:html_video": "Video datoteke, z nativno brskalniško podporo", - "assets.mimetypes.group:image": "Slikovne datoteke", - "assets.mimetypes.group:presentation": "Predstavitve", - "assets.mimetypes.group:sourcecode": "Izvorna koda", - "assets.mimetypes.group:spreadsheet": "Razpredelnice", - "assets.mimetypes.group:video": "Videi", - "assets.mimetypes.group:web_audio": "Zvočne datoteke za splet", - "assets.mimetypes.group:web_file": "Spletne datoteke", - "assets.mimetypes.group:web_image": "Slikovne datoteke, uporabljene v omrežju", - "assets.mimetypes.group:web_video": "Video datoteke, uporabljene v omrežju", - "assets.mimetypes.image": "Slika ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows ikona", - "assets.mimetypes.text/css": "Prekrivni slogi", - "assets.mimetypes.text/csv": "Vrednosti, ločene z vejico", - "assets.mimetypes.text/html": "Dokument HTML", - "assets.mimetypes.text/plain": "Datoteka z besedilom", - "assets.mimetypes.text/rtf": "dokument RTF", - "assets.mimetypes.text/vtt": "Podnaslavljanje v spletnem videoposnetku", - "assets.mimetypes.video": "Video datoteka ({{$a.EXT}})", - "core.accounts": "Računi", - "core.add": "Dodaj", - "core.agelocationverification": "Preverjanje starosti in lokacije", - "core.ago": "{{$a}} nazaj", - "core.all": "Vse", - "core.allgroups": "Vse skupine", - "core.allparticipants": "Vsi sodelujoči", - "core.answer": "Odgovor", - "core.answered": "Odgovorjeno", - "core.areyousure": "Ste prepričani?", - "core.back": "Nazaj", - "core.block.blocks": "Bloki", - "core.browser": "Brskalnik", - "core.cancel": "Prekliči", - "core.cannotconnect": "Neuspešan povezava: Preverite, ali ste pravilno vnesli URL naslov in če vaše spletno mesto uporablja Moodle verzijo {{$a}} ali novejšo.", - "core.cannotdownloadfiles": "Prenos datotek je onemogočen. Obrnite se na skrbnika spletnega mesta.", - "core.captureaudio": "Snemanje zvoka", - "core.capturedimage": "Posneta slika.", - "core.captureimage": "Slikaj", - "core.capturevideo": "Snemanje videoposnetka", - "core.category": "Kategorija", - "core.choose": "Izberi", - "core.choosedots": "Izberi...", - "core.clearsearch": "Počisti iskanje", - "core.clicktohideshow": "Klikni za razširitev ali zložitev", - "core.clicktoseefull": "Kliknite za ogled celotne vsebine.", - "core.close": "Zapri", - "core.comments": "Komentarji", - "core.comments.addcomment": "Dodaj komentar...", - "core.comments.comments": "Komentarji", - "core.comments.commentscount": "Komentarji ({{$a}})", - "core.comments.commentsnotworking": "Komentarjev ni mogoče pridobiti", - "core.comments.deletecommentbyon": "Izbriši komentar, ki ga je {{$a.time}} objavil {{$a.user}}.", - "core.comments.eventcommentcreated": "Komentar ustvarjen", - "core.comments.eventcommentdeleted": "Komentar izbrisan", - "core.comments.nocomments": "Ni komentarjev", - "core.comments.savecomment": "Shrani komentar", - "core.comments.warningcommentsnotsent": "Komentarjev ni bilo mogoče sinhronizirati. {{error}}", - "core.commentscount": "Komentarji ({{$a}})", - "core.completion-alt-auto-fail": "Zaključeno: {{$a}} (ni dosežena pozitivna ocena)", - "core.completion-alt-auto-n": "Ni zaključeno: {{$a}}", - "core.completion-alt-auto-n-override": "Ni zaključeno: {{$a.modname}} (s strani {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Zaključeno: {{$a}} (dosežena pozitivna ocena)", - "core.completion-alt-auto-y": "Zaključeno: {{$a}}", - "core.completion-alt-auto-y-override": "Zaključeno {{$a.modname}} (s strani {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Ni zaključeno: {{$a}}. Izberi za označiti kot dovršeno", - "core.completion-alt-manual-n-override": "Ni zaključeno: {{$a.modname}} (s strani{{$a.overrideuser}}). Izberi za označevanje zaključka.", - "core.completion-alt-manual-y": "Dovršeno: {{$a}}. Izberi za označiti kot nedovršeno", - "core.completion-alt-manual-y-override": "Zaključeno: {{$a.modname}} (s strani {{$a.overrideuser}}). Izberi za označevanje nezaključenosti.", - "core.confirmcanceledit": "Ali ste prepričani, da želite zapustiti to stran? Vse spremembe bodo izgubljene.", - "core.confirmdeletefile": "Ste prepričani da želite izbrisati to datoteko?", - "core.confirmgotabroot": "Ali ste prepričani, da se želite vrniti na {{name}}?", - "core.confirmgotabrootdefault": "Ali ste prepričani, da želite iti na začetno stran trenutnega zavihka?", - "core.confirmloss": "Ste prepričani? Vse spremembe bodo izgubljene.", - "core.confirmopeninbrowser": "Ali želite odpreti v spletnem brskalniku?", - "core.considereddigitalminor": "Ste premladi, da bi lahko na tej strani ustvarili uporabniški račun.", - "core.content": "Vsebina", - "core.contenteditingsynced": "Vsebina, ki jo urejate, je bila sinhronizirana.", - "core.contentlinks.chooseaccount": "Izberite račun", - "core.contentlinks.chooseaccounttoopenlink": "Izberite račun, s katerim boste odprli povezavo.", - "core.contentlinks.confirmurlothersite": "Ta povezava pripada drugemu mestu. Ali jo vseeno želite odpreti?", - "core.contentlinks.errornoactions": "Dejanja, ki bi ga izvedli s to povezavo, ni bilo mogoče najti.", - "core.contentlinks.errornosites": "Za to povezavo ni bilo mogoče najti nobenega spletnega mesta.", - "core.contentlinks.errorredirectothersite": "URL naslov za preusmeritev ne sme kazati na drugo mesto.", - "core.continue": "Nadaljuj", - "core.copiedtoclipboard": "Besedilo kopirano v odložišče", - "core.course": "Predmet", - "core.course.activitydisabled": "Vaša organizacija je onemogočila to dejavnost v aplikaciji za mobilne naprave.", - "core.course.activitynotyetviewableremoteaddon": "Vaša organizacija je namestila vtičnik, ki še ni podprt.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Moodle v vaši organizaciji je potrebno posodobiti.", - "core.course.allsections": "Vsi sklopi", - "core.course.askadmintosupport": "Obrnite se na skrbnika spletnega mesta in jim sporočite, da želite uporabljati to dejavnost z aplikacijo Moodle Mobile.", - "core.course.availablespace": "Trenutno imate približno {{available}} prostega prostora.", - "core.course.confirmdeletemodulefiles": "Ali ste prepričani, da želite izbrisati te datoteke?", - "core.course.confirmdownload": "Prenesti nameravate {{size}}.{{availableSpace}} Ali ste prepričani, da želite nadaljevati?", - "core.course.confirmdownloadunknownsize": "Velikosti prenosa ni bilo mogoče izračunati. {{availableSpace}} Ali ste prepričani, da želite nadaljevati?", - "core.course.confirmdownloadzerosize": "Prenos se bo začel. {{availableSpace}} Ali ste prepričani, da želite nadaljevati?", - "core.course.confirmlimiteddownload": "Trenutno niste povezani z Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Zdaj nameravate prenesti vsaj {{size}}. {{availableSpace}} Ali ste prepričani, da želite nadaljevati?", - "core.course.contents": "Vsebina", - "core.course.couldnotloadsectioncontent": "Vsebine razdelka ni bilo mogoče naložiti. Prosim poskusite kasneje.", - "core.course.couldnotloadsections": "Poglavja ni bilo mogoče naložiti. Prosim poskusite kasneje.", - "core.course.coursesummary": "Povzetek predmeta", - "core.course.downloadcourse": "Prenos predmeta", - "core.course.errordownloadingcourse": "Napaka pri prenosu predmeta.", - "core.course.errordownloadingsection": "Napaka pri prenosu poglavja.", - "core.course.errorgetmodule": "Napaka pri pridobivanju podatkov o dejavnosti.", - "core.course.hiddenfromstudents": "Skrito za udeležence", - "core.course.hiddenoncoursepage": "Na voljo, a ni prikazano na strani predmeta", - "core.course.insufficientavailablequota": "Naprava ni mogla dodeliti prostora za shranjevanje tega prenosa. Morda je prostor rezerviran za posodobitve aplikacij in sistemov. Pred prenosom počistite nekaj prostora za shranjevanje.", - "core.course.insufficientavailablespace": "Prenesti poskušate {{size}}. Vaša naprava bo zato imela premalo prostora za normalno delovanje. Najprej počistite nekaj prostora za shranjevanje.", - "core.course.manualcompletionnotsynced": "Ročno zaključevanje ni bilo sinhronizirano.", - "core.course.nocontentavailable": "Trenutno vsebine ni na voljo.", - "core.course.overriddennotice": "Končna ocena za to dejavnost je bila ročno prilagojena.", - "core.course.refreshcourse": "Osveži predmet", - "core.course.sections": "Odseki", - "core.course.useactivityonbrowser": "Še vedno ga lahko uporabljate v spletnem brskalniku naprave.", - "core.course.warningmanualcompletionmodified": "Ročno zaključevanje dejavnosti je bilo spremenjeno na spletnem mestu.", - "core.course.warningofflinemanualcompletiondeleted": "Nekatera ročna zaključevanja predmeta \"{{name}}\" brez povezave na splet so bila izbrisana. {{error}}", - "core.coursedetails": "Podrobnosti predmeta", - "core.coursenogroups": "Niste član nobene skupine tega tečaja.", - "core.courses.addtofavourites": "Dodaj predmet med priljubljene", - "core.courses.allowguests": "Ta predmet dovoljuje vstop gostujočim uporabnikom", - "core.courses.availablecourses": "Predmeti na voljo", - "core.courses.cannotretrievemorecategories": "Kategorije globlje od ravni {{$a}} ni mogoče najti.", - "core.courses.categories": "Kategorije predmetov", - "core.courses.confirmselfenrol": "Ali ste prepričani, da se želite vpisati v ta predmet?", - "core.courses.courses": "Predmeti", - "core.courses.downloadcourses": "Prenesi predmete", - "core.courses.enrolme": "Vpiši me", - "core.courses.errorloadcategories": "Pri nalaganju kategorij je prišlo do napake.", - "core.courses.errorloadcourses": "Pri nalaganju predmetov je prišlo do napake.", - "core.courses.errorloadplugins": "Vtičnikov, ki jih zahteva ta tečaj, ni bilo mogoče pravilno naložiti. Znova zaženite aplikacijo, da poskusite znova.", - "core.courses.errorsearching": "Pri iskanju je prišlo do napake.", - "core.courses.errorselfenrol": "Pri samovpisu je prišlo do napake.", - "core.courses.filtermycourses": "Filtriraj moje predmete", - "core.courses.frontpage": "Naslovna stran", - "core.courses.hidecourse": "Odstrani iz pogleda", - "core.courses.ignore": "Prezri", - "core.courses.mycourses": "Moji predmeti", - "core.courses.mymoodle": "Pregledna plošča", - "core.courses.nocourses": "Ni podatkov o predmetu.", - "core.courses.nocoursesyet": "Ni predmetov v tej kategoriji", - "core.courses.nosearchresults": "Ni rezultatov", - "core.courses.notenroled": "V ta predmet niste vpisani", - "core.courses.notenrollable": "V ta predmet se ne morete vpisati.", - "core.courses.password": "Ključ za vpis", - "core.courses.paymentrequired": "Ta predmet zahteva plačilo za vstop.", - "core.courses.paypalaccepted": "PayPal plačila sprejeta", - "core.courses.reload": "Osveži", - "core.courses.removefromfavourites": "Odstrani oznako priljubljeno pri predmetu", - "core.courses.search": "Išči", - "core.courses.searchcourses": "Išči predmete", - "core.courses.searchcoursesadvice": "Z gumbom 'išči predmete' lahko poiščete predmete, do katerih lahko dostopate kot gost ali se vpišete na predmete, ki to omogočajo.", - "core.courses.selfenrolment": "Samovpis", - "core.courses.sendpaymentbutton": "Pošlji plačilo preko PayPal", - "core.courses.show": "Povrni v pogled", - "core.courses.totalcoursesearchresults": "Vseh tečajev: {{$a}}", - "core.currentdevice": "Trenutna naprava", - "core.datastoredoffline": "Podatkov ni bilo mogoče poslati in so shranjeni v napravi. Kasneje bodo poslani samodejno.", - "core.date": "Datum", - "core.day": "dan", - "core.days": "dni", - "core.decsep": ",", - "core.defaultvalue": "Privzeto ({{$a}})", - "core.delete": "Izbriši", - "core.deletedoffline": "Izbrisano brez povezave na splet", - "core.deleteduser": "Izbriši uporabnika", - "core.deleting": "Brisanje", - "core.description": "Opis", - "core.desktop": "Namizje", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Digitalni mladoletnik", - "core.digitalminor_desc": "Prosite starša/skrbnika da kontaktira:", - "core.discard": "Zavrzi", - "core.dismiss": "Opusti", - "core.displayoptions": "Prikaži možnosti", - "core.done": "Dokončano", - "core.download": "Prenos", - "core.downloaded": "Preneseno", - "core.downloading": "Prenos", - "core.edit": "Uredi", - "core.editor.autosavesucceeded": "Osnutek je bil shranjen.", - "core.editor.bold": "Krepko", - "core.editor.clear": "Počisti oblikovanje", - "core.editor.h3": "Naslov (veliki)", - "core.editor.h4": "Naslov (srednji)", - "core.editor.h5": "Naslov (mali)", - "core.editor.hidetoolbar": "Skrij orodno vrstico", - "core.editor.italic": "Ležeče", - "core.editor.orderedlist": "Oštevilčen seznam", - "core.editor.p": "Odstavek", - "core.editor.strike": "Prečrtano", - "core.editor.textrecovered": "Različica osnutka tega besedila je bila samodejno obnovljena.", - "core.editor.toggle": "Preklopi urejevalnik", - "core.editor.underline": "Podčrtano", - "core.editor.unorderedlist": "Urejen seznam", - "core.emptysplit": "Ta stran bo videti prazna, če je lev panel prazen ali se nalaga.", - "core.error": "Napaka", - "core.errorchangecompletion": "Pri spremembi statusa zaključevanja je prišlo do napake. Prosim poskusite ponovno.", - "core.errordeletefile": "Napaka pri brisanju datoteke. Prosim poskusite ponovno.", - "core.errordownloading": "Napaka med prenosom datoteke.", - "core.errordownloadingsomefiles": "Napaka med prenosom datotek. Morda niso prenesene vse datoteke.", - "core.errorfileexistssamename": "Datoteka s tem imenom že obstaja.", - "core.errorinvalidform": "Obrazec vsebuje neveljavne podatke. Preverite, ali so vsa obvezna polja izpolnjena in ali so podatki veljavni.", - "core.errorinvalidresponse": "Prejet je neveljaven odgovor. Če se napaka ponovi, se obrnite na skrbnika spletnega mesta.", - "core.errorloadingcontent": "Napaka pri nalaganju vsebine.", - "core.errorofflinedisabled": "Brskanje brez povezave na splet je na vašem spletnem mestu onemogočeno. Za uporabo aplikacije morate biti povezani z internetom.", - "core.erroropenfilenoapp": "Napaka pri odpiranju datoteke: za odpiranje te vrste datotek ni bilo mogoče najti nobene aplikacije.", - "core.erroropenfilenoextension": "Napaka pri odpiranju datoteke: datoteka nima pripone.", - "core.erroropenpopup": "Ta dejavnost poskuša odpreti pojavno okno. To v aplikaciji ni podprto.", - "core.errorrenamefile": "Napaka pri preimenovanju datoteke. Prosim poskusite ponovno.", - "core.errorsomedatanotdownloaded": "Če ste prenesli to aktivnost, upoštevajte, da se nekateri podatki med postopkom prenosa ne bodo prenesli zaradi učinkovitosti in porabe podatkov.", - "core.errorsync": "Med sinhronizacijo je prišlo do napake. Prosim poskusite znova.", - "core.errorsyncblocked": "{{$a}} trenutno ni mogoče sinhronizirati zaradi postopka, ki se trenutno izvaja. Prosim poskusite kasneje. Če se težava še vedno pojavlja, poskusite znova zagnati aplikacijo.", - "core.explanationdigitalminor": "Ti podatki so potrebni za določitev, ali vaša starost presega digitalno starost za privolitev. To je starost, pri kateri lahko posameznik soglaša s pogoji in se lahko njegovi podatki v skladu z zakonom hranijo in obdelujejo.", - "core.favourites": "Priljubljeno", - "core.filename": "Ime datoteke", - "core.filenameexist": "Ime datoteke že obstaja: {{$a}}", - "core.filenotfound": "Oprostite, datoteka ni bila najdena.", - "core.fileuploader.addfiletext": "Dodaj datoteko", - "core.fileuploader.audio": "Zvok", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Naložili boste {{size}}. Ali ste prepričani, da želite nadaljevati?", - "core.fileuploader.confirmuploadunknownsize": "Velikosti prenosa ni bilo mogoče določiti. Ali ste prepričani, da želite nadaljevati?", - "core.fileuploader.errorcapturingaudio": "Napaka pri zajemu zvoka.", - "core.fileuploader.errorcapturingimage": "Napaka pri zajemu slike.", - "core.fileuploader.errorcapturingvideo": "Napaka pri zajemu videoposnetka.", - "core.fileuploader.errorgettingimagealbum": "Napaka pri pridobivanju slike iz albuma.", - "core.fileuploader.errormustbeonlinetoupload": "Če želite naložiti datoteke, morate biti povezani na splet.", - "core.fileuploader.errornoapp": "Za izvajanje tega dejanja nimate nameščene potrebne aplikacije.", - "core.fileuploader.errorreadingfile": "Napaka pri branju datoteke.", - "core.fileuploader.errorwhileuploading": "Med nalaganjem datoteke je prišlo do napake.", - "core.fileuploader.file": "Datoteka", - "core.fileuploader.filesofthesetypes": "Sprejete vrste datotek:", - "core.fileuploader.fileuploaded": "Datoteka je bila uspešno naložena.", - "core.fileuploader.invalidfiletype": "Tip datoteke {{$a}} ni sprejemljiv.", - "core.fileuploader.maxbytesfile": "Datoteka {{$a.file}} je prevelika. Največja velikost, ki jo lahko naložite, je {{$a.size}}.", - "core.fileuploader.more": "Več", - "core.fileuploader.photoalbums": "Foto albumi", - "core.fileuploader.readingfile": "Branje datoteke", - "core.fileuploader.readingfileperc": "Branje datoteke: {{$a}}%", - "core.fileuploader.selectafile": "Izberite datoteko", - "core.fileuploader.uploadafile": "Naloži datoteko", - "core.fileuploader.uploading": "Nalaganje", - "core.fileuploader.uploadingperc": "Nalaganje: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filter", - "core.folder": "Mapa", - "core.forcepasswordchangenotice": "Za nadaljevanje morate spremeniti svoje geslo.", - "core.fulllistofcourses": "Vsi predmeti", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Povprečje", - "core.grades.badgrade": "Podana ocena je neveljavna", - "core.grades.contributiontocoursetotal": "Prispevek k seštevku predmeta", - "core.grades.feedback": "Odziv", - "core.grades.grade": "Ocena", - "core.grades.gradeitem": "Element ocene", - "core.grades.grades": "Ocene", - "core.grades.lettergrade": "Črkovna ocena", - "core.grades.nogradesreturned": "Ni vrnjenih ocen", - "core.grades.nooutcome": "Ni izida", - "core.grades.percentage": "Odstotek", - "core.grades.range": "Razpon", - "core.grades.rank": "Razvrstitev", - "core.grades.weight": "Utež", - "core.group": "Skupina", - "core.groupsseparate": "Ločene skupine", - "core.groupsvisible": "Vidne skupine", - "core.h5p.additionallicenseinfo": "Dodatne informacije o licenci", - "core.h5p.author": "Avtor", - "core.h5p.authorcomments": "Avtorjevi komentarji", - "core.h5p.authorcommentsdescription": "Komentarji za urednika vsebine. (besedilo ne bo objavljeno kot del informacij o avtorskih pravicah.)", - "core.h5p.authorname": "Avtorjevo ime", - "core.h5p.authorrole": "Avtorjeva vloga", - "core.h5p.by": "Od", - "core.h5p.cancellabel": "Prekliči", - "core.h5p.ccattribution": "Atribucija (CC BY)", - "core.h5p.ccattributionnc": "“priznanje avtorstva” + “nekomercialno” (CC BY NC)", - "core.h5p.ccattributionncnd": "“priznanje avtorstva” + “nekomercialno” + “brez predelav” (CC BY NC ND)", - "core.h5p.ccattributionncsa": "“priznanje avtorstva” + “nekomercialno” + “deljenje pod enakimi pogoji” (CC BY NC SA)", - "core.h5p.ccattributionnd": "“priznanje avtorstva” + “brez predelav” (CC BY ND)", - "core.h5p.ccattributionsa": "“priznanje avtorstva” + “deljenje pod enakimi pogoji” (CC BY SA)", - "core.h5p.ccpdd": "Predaja javni domeni (CC0)", - "core.h5p.changedby": "Spremenil:", - "core.h5p.changedescription": "Opis spremembe", - "core.h5p.changelog": "Dnevnik sprememb", - "core.h5p.changeplaceholder": "Slika obrezana, besedilo spremenjeno, itd.", - "core.h5p.close": "Zapri", - "core.h5p.confirmdialogbody": "Potrdite, če želite nadaljevati. Tega dejanja ni mogoče razveljaviti.", - "core.h5p.confirmdialogheader": "Potrdi dejanje", - "core.h5p.confirmlabel": "Potrdi", - "core.h5p.connectionLost": "Povezava izgubljena. Rezultati bodo shranjeni in poslani, ko bo povezava vzpostavljena.", - "core.h5p.connectionReestablished": "Povezava ponovno vzpostavljena.", - "core.h5p.contentCopied": "Vsebina skopirana v odložišče", - "core.h5p.contentchanged": "Ta vsebina se je spremenila, odkar ste si jo nazadnje ogledali.", - "core.h5p.contenttype": "Vrsta vsebine", - "core.h5p.copyright": "Pravice uporabnika", - "core.h5p.copyrightinfo": "Podatki o avtorskih pravicah", - "core.h5p.copyrightstring": "Avtorske pravice", - "core.h5p.copyrighttitle": "Oglejte si podatke o avtorskih pravicah za to vsebino.", - "core.h5p.creativecommons": "Creative Commons", - "core.h5p.date": "Datum", - "core.h5p.disablefullscreen": "Onemogoči celozaslonski način", - "core.h5p.download": "Prenesi", - "core.h5p.downloadtitle": "Prenesi kot H5P datoteko.", - "core.h5p.editor": "Urednik", - "core.h5p.embed": "Vdelano", - "core.h5p.embedtitle": "Prikaži vdelano kodo te vsebine.", - "core.h5p.fullscreen": "Celozaslonski način", - "core.h5p.gpl": "Splošna javna licenca v3", - "core.h5p.h5ptitle": "Za več informacij obiščite h5p.org.", - "core.h5p.hideadvanced": "Skrij napredno", - "core.h5p.license": "Licenca", - "core.h5p.licenseCC010": "CC0 1.0 Univerzalno (CC0 1.0) Namenjeno javni domeni", - "core.h5p.licenseCC010U": "CC0 1.0 Univerzalno", - "core.h5p.licenseCC10": "1.0 Splošno", - "core.h5p.licenseCC20": "2.0 Splošno", - "core.h5p.licenseCC25": "2.5 Splošno", - "core.h5p.licenseCC30": "3.0. Neumeščeno", - "core.h5p.licenseCC40": "4.0 Mednarodno", - "core.h5p.licenseGPL": "Splošna javna licenca", - "core.h5p.licenseV1": "1 Verzija", - "core.h5p.licenseV2": "2 Verzija", - "core.h5p.licenseV3": "3 Verzija", - "core.h5p.licensee": "Imetnik licence", - "core.h5p.licenseextras": "Dodatki za licenco", - "core.h5p.licenseversion": "Verzija licence", - "core.h5p.nocopyright": "Za to vsebino ni na voljo nobenih informacij o avtorskih pravicah.", - "core.h5p.offlineDialogBody": "Informacij o vašem zaključku te naloge ni bilo mogoče poslati. Preverite internetno povezavo.", - "core.h5p.offlineDialogHeader": "Povezava s strežnikom je bila prekinjena", - "core.h5p.offlineDialogRetryButtonLabel": "Poskusi ponovno", - "core.h5p.offlineDialogRetryMessage": "Ponoven poskus čez :num...", - "core.h5p.offlineSuccessfulSubmit": "Rezultati so bili uspešno poslani.", - "core.h5p.offlinedisabled": "Spletno mesto ne dovoljuje prenašanja paketov H5P.", - "core.h5p.originator": "Ustvarjalec", - "core.h5p.pd": "Javna domena", - "core.h5p.pddl": "Public Domain Dedication in Licenca", - "core.h5p.pdm": "Oznaka javne domene (PDM)", - "core.h5p.play": "Predvajaj H5P", - "core.h5p.resizescript": "Če želite dinamično velikost vdelane vsebine na svoje spletno mesto vključite:", - "core.h5p.resubmitScores": "Poskus oddaje shranjenih rezultatov.", - "core.h5p.reuse": "Ponovna uporaba", - "core.h5p.reuseContent": "Ponovna uporaba vsebine", - "core.h5p.reuseDescription": "To vsebino uporabi ponovno.", - "core.h5p.showadvanced": "Pokaži napredno", - "core.h5p.showless": "Pokaži manj", - "core.h5p.showmore": "Pokaži več", - "core.h5p.size": "Velikost", - "core.h5p.source": "Vir", - "core.h5p.startingover": "Začeli boste znova.", - "core.h5p.sublevel": "Podnaslov", - "core.h5p.thumbnail": "Sličica", - "core.h5p.title": "Naslov", - "core.h5p.undisclosed": "Prikrito", - "core.h5p.year": "Leto", - "core.h5p.years": "Leta", - "core.h5p.yearsfrom": "Leta (od)", - "core.h5p.yearsto": "Leta (do)", - "core.hasdatatosync": "{{$ a}} vsebuje podatke brez povezave na splet, ki jih je potrebno sinhronizirati.", - "core.help": "Pomoč", - "core.hide": "Skrij", - "core.hour": "ura", - "core.hours": "ure", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Slika", - "core.imageviewer": "Pregledovalnik slik", - "core.info": "Informacije", - "core.invalidformdata": "Napačni podatki obrazca", - "core.labelsep": ":", - "core.lastaccess": "Zadnji dostop", - "core.lastdownloaded": "Nazadnje preneseno", - "core.lastmodified": "Zadnja sprememba", - "core.lastsync": "Nazadnje sinhronizirano", - "core.layoutgrid": "Mreža", - "core.list": "Seznam", - "core.listsep": ";", - "core.loading": "Nalagam", - "core.loadmore": "Naloži več", - "core.location": "Lokacija", - "core.login.auth_email": "Preverjanje pristnosti na osnovi e-pošte", - "core.login.authenticating": "Preverjanje pristnosti", - "core.login.cancel": "Prekliči", - "core.login.changepassword": "Spremeni geslo", - "core.login.changepasswordbutton": "Odpri stran za spremembo gesla", - "core.login.changepasswordhelp": "Če imate težave pri spremembi gesla, se obrnite na skrbnika spletnega mesta. \"Administratorji spletnih mest\" upravljajo Moodle na vaši šoli / univerzi / podjetju ali učni organizaciji. Če ne veste, kako jih kontaktirati, se obrnite na učitelje.", - "core.login.changepasswordinstructions": "V aplikaciji ne morete spremeniti gesla. Kliknite spodnji gumb, če želite odpreti spletno mesto v spletnem brskalniku, kjer lahko spremenite geslo. Upoštevajte, da morate po spremembi gesla zapreti brskalnik, saj ne boste avtomatsko preusmerjeni na aplikacijo.", - "core.login.changepasswordlogoutinstructions": "Če želite spremeniti spletno mesto ali se odjaviti, kliknite gumb:", - "core.login.changepasswordreconnectinstructions": "Kliknite gumb, da se ponovno povežete s spletnim mestom. (Upoštevajte, da se v primeru, da gesla ne boste uspešno spremenili, vrnete na prejšnji zaslon).", - "core.login.confirmdeletesite": "Ali ste prepričani, da želite izbrisati spletno mesto {{sitename}}?", - "core.login.connect": "Povežite se!", - "core.login.connecttomoodle": "Povežite se na Moodle", - "core.login.connecttomoodleapp": "Poskušate se povezati z običajnim spletnim mestom Moodle. Za dostop do te strani si morate prenesti uradno aplikacijo Moodle.", - "core.login.connecttoworkplaceapp": "Poskušate se povezati s spletnim mestom Moodle Workplace. Za dostop do te strani si morate prenesti aplikacijo Moodle Workplace.", - "core.login.contactyouradministrator": "Za pomoč se obrnite na skrbnika spletnega mesta.", - "core.login.contactyouradministratorissue": "Prosite upravitelja spletnega mesta, naj preveri to težavo: {{$a}}", - "core.login.createaccount": "Ustvari moj nov račun", - "core.login.createuserandpass": "Izberite svoje uporabniško ime in geslo", - "core.login.credentialsdescription": "Za prijavo vnesite svoje uporabniško ime in geslo.", - "core.login.emailconfirmsent": "

                      Na vaš naslov {{$a}} bi moralo biti poslano elektronsko sporočilo.

                      \n

                      Vsebuje enostavna navodila za dokončanje vaše registracije.

                      \n

                      Če bi še naprej imeli težave, se obrnite na skrbnika spletnega mesta.

                      ", - "core.login.emailconfirmsentnoemail": "

                      Na vašem e-poštem naslovu imate novo sporočilo.

                      Vsebuje enostavna navodila za dokončanje registracije.

                      Če imate težave še naprej, se obrnite na skrbnika spletnega mesta. ", - "core.login.emailconfirmsentsuccess": "Potrditveno e-sporočilo uspešno poslano", - "core.login.emailnotmatch": "E-poštna naslova se ne ujemata.", - "core.login.erroraccesscontrolalloworigin": "Klic prek različnih izvorov, ki ste ga skušali izvesti, je bil zavrnjen. Preverite https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Pri brisanju tega spletnega mesta je prišlo do napake. Prosim poskusite ponovno.", - "core.login.errorupdatesite": "Pri posodabljanju žetona spletnega mesta je prišlo do napake.", - "core.login.findyoursite": "Poiščite svoje spletno mesto", - "core.login.firsttime": "Je to vaš prvi obisk?", - "core.login.forcepasswordchangenotice": "Za nadaljevanje morate spremeniti svoje geslo.", - "core.login.forgotten": "Ste pozabili vaše uporabniško ime ali geslo?", - "core.login.help": "Pomoč", - "core.login.helpmelogin": "

                      Po vsem svetu je na tisoče Moodle strani. Ta aplikacija se lahko poveže samo s spletnimi mesti Moodle, ki imajo posebej omogočen dostop do aplikacije za mobilne naprave.

                      Če se s svojim spletnim mestom Moodle ne morete povezati, se obrnite na skrbnika spletnega mesta in ga prosite, naj prebere http://docs.moodle.org/sl/Mobile_app

                      Če pa želite preizkusiti aplikacijo na Moodle demo spletni strani, v polje Site address vpišite teacher ali student in kliknite Connect button.

                      ", - "core.login.instructions": "Navodila", - "core.login.invalidaccount": "Preverite prijavne podatke ali prosite svojega skrbnika, da preveri konfiguracijo spletnega mesta.", - "core.login.invaliddate": "Neveljaven datum", - "core.login.invalidemail": "Neveljaven e-poštni naslov", - "core.login.invalidmoodleversion": "

                      Neveljavna različica spletnega mesta Moodle. Aplikacija Moodle podpira samo sisteme Moodle od {{$a}} naprej.

                      \n

                      Lahko se obrnete na skrbnike spletnega mesta in jih prosite, naj posodobijo sistem Moodle.

                      \n

                      \"Administratorji spletnega mesta\" so ljudje, ki upravljajo Moodle na vaši šoli / univerzi / podjetju ali učni organizaciji. Če ne veste, kako vzpostaviti stik z njimi, se obrnite na učitelje.

                      ", - "core.login.invalidsite": "URL naslov spletnega mesta ni veljaven.", - "core.login.invalidtime": "Neveljaven čas", - "core.login.invalidurl": "Določen je neveljavni URL", - "core.login.invalidvaluemax": "Maksimalna vrednost je {{$a}}", - "core.login.invalidvaluemin": "Minimalna vrednost je {{$a}}", - "core.login.localmobileunexpectedresponse": "Preverjanje dodatnih funkcij mobilne aplikacije Moodle je vrnilo nepričakovan odgovor. Avtentificirani boste s standardno mobilno storitvijo.", - "core.login.loggedoutssodescription": "Ponovno se morate avtenticirati. Na spletno mesto se morate prijaviti v oknu brskalnika.", - "core.login.login": "Prijava", - "core.login.loginbutton": "Prijavi se", - "core.login.logininsiterequired": "Na spletno mesto se morate prijaviti v oknu brskalnika.", - "core.login.loginsteps": "Za poln dostop do te strani morate najprej ustvariti uporabniški račun.", - "core.login.missingemail": "Manjka e-poštni naslov", - "core.login.missingfirstname": "Manjka ime", - "core.login.missinglastname": "Manjka priimek", - "core.login.mobileservicesnotenabled": "Mobilni dostop na vašem spletnem mestu ni omogočen. Če menite, da bi dostop moral biti omogočen, se obrnite na skrbnika spletnega mesta.", - "core.login.mustconfirm": "Potrditi morate svojo prijavo", - "core.login.newaccount": "Nov račun", - "core.login.notloggedin": "Biti morate prijavljeni.", - "core.login.password": "Geslo", - "core.login.passwordforgotten": "Pozabljeno geslo", - "core.login.passwordforgotteninstructions2": "Če želite ponastaviti geslo, vpišite svoje uporabniško ime ali vaš e-poštni naslov. Če vas najdemo v bazi podatkov, bo e-sporočilo z navodili za dostop poslano na vaš e-poštni naslov.", - "core.login.passwordrequired": "Potrebno je geslo", - "core.login.policyaccept": "Razumem in se strinjam", - "core.login.policyagree": "Za nadaljnjo uporabo tega spletnega mesta se morate strinjati s temi pravili. Ali se strinjate?", - "core.login.policyagreement": "Pravila spletnega mesta", - "core.login.policyagreementclick": "Povezava do pravil spletnega mesta", - "core.login.potentialidps": "Prijavite se z vašim računom na:", - "core.login.profileinvaliddata": "Neveljavna vrednost", - "core.login.recaptchachallengeimage": "Slika reCAPTCHA", - "core.login.recaptchaexpired": "Preverjanje je poteklo. Znova odgovorite na varnostno vprašanje.", - "core.login.recaptchaincorrect": "Odgovor na varnostno vprašanje ni pravilen.", - "core.login.reconnect": "Ponovno se povežite", - "core.login.reconnectdescription": "Vaš žeton za preverjanje pristnosti ni veljaven ali pa je potekel. Na spletno mesto se morate znova povezati.", - "core.login.reconnectssodescription": "Vaš žeton za preverjanje pristnosti ni veljaven ali pa je potekel. Na spletno mesto se morate znova povezati in prijaviti v oknu brskalnika.", - "core.login.resendemail": "Ponovno pošlji e-sporočilo", - "core.login.searchby": "Išči po:", - "core.login.security_question": "Varnostno vprašanje", - "core.login.selectacountry": "Izberi državo", - "core.login.selectsite": "Izberite svoje spletno mesto:", - "core.login.signupplugindisabled": "{{$a}} ni omogočen.", - "core.login.siteaddress": "Naslov spletnega mesta", - "core.login.sitehasredirect": "Vaše spletno mesto vsebuje vsaj eno preusmeritev HTTP. Aplikacija ne more slediti preusmeritvam. To lahko aplikaciji prepreči povezavo z vašim spletnim mestom.", - "core.login.siteinmaintenance": "Vaše spletno mesto je v načinu vzdrževanja.", - "core.login.sitepolicynotagreederror": "Nestrinjanje s politiko spletnega mesta.", - "core.login.siteurl": "URL naslov spletnega mesta", - "core.login.siteurlrequired": "Potreben je URL naslov spletnega mesta, tj. Http://www.yourmoodlesite.org
                      ", - "core.login.startsignup": "Ustvari nov račun", - "core.login.stillcantconnect": "Se še vedno ne morete povezati?", - "core.login.supplyinfo": "Prosimo, priskrbite nekaj informacij o sebi", - "core.login.username": "Uporabniško ime", - "core.login.usernameoremail": "Vnesite uporabniško ime ali naslov e-pošte", - "core.login.usernamerequired": "Zahtevano je uporabniško ime", - "core.login.usernotaddederror": "Uporabnik ni bil dodan - napaka", - "core.login.visitchangepassword": "Ali želite obiskati spletno mesto, kjer lahko spremenite geslo?", - "core.login.webservicesnotenabled": "Na vašem spletnem mestu spletne storitve niso omogočene. Če mislite, da bi morale biti, se obrnite na skrbnika spletnega mesta.", - "core.lostconnection": "Vaš žeton za preverjanje pristnosti ni veljaven ali pa je potekel. Na spletno mesto se boste morali znova povezati.", - "core.mainmenu.changesite": "Spremenite spletno mesto", - "core.mainmenu.help": "Pomoč", - "core.mainmenu.logout": "Odjavi", - "core.mainmenu.website": "Spletna stran", - "core.maxsizeandattachments": "Največja velikost datoteke: {{$a.size}}, največje število datotek: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Razno", - "core.mod_assign": "Naloga", - "core.mod_assignment": "Naloga 2.2 (Onemogočeno)", - "core.mod_book": "Knjiga", - "core.mod_chat": "Klepet", - "core.mod_choice": "Možnost", - "core.mod_data": "Podatkovna zbirka", - "core.mod_database": "Podatkovna zbirka", - "core.mod_external-tool": "Zunanja orodja", - "core.mod_feedback": "Odziv", - "core.mod_file": "Datoteka", - "core.mod_folder": "Mapa", - "core.mod_forum": "Forum", - "core.mod_glossary": "Slovar", - "core.mod_ims": "IMS vsebinski paket", - "core.mod_imscp": "IMS vsebinski paket", - "core.mod_label": "Oznaka", - "core.mod_lesson": "Lekcija", - "core.mod_lti": "Zunanja orodja", - "core.mod_page": "Stran", - "core.mod_quiz": "Kviz", - "core.mod_resource": "Vir", - "core.mod_scorm": "SCORM paket", - "core.mod_survey": "Anketa", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Delavnica", - "core.moduleintro": "Opis", - "core.more": "več", - "core.mygroups": "Moje skupine", - "core.name": "Ime", - "core.networkerroriframemsg": "Brez povezave na splet do te vsebine ni mogoče dostopati. Povežite se z internetom in poskusite znova.", - "core.networkerrormsg": "Pri povezovanju s spletnim mestom je prišlo do težave. Preverite povezavo in poskusite znova.", - "core.never": "Nikoli", - "core.next": "Naslednje", - "core.no": "Ne", - "core.nocomments": "Ni komentarjev", - "core.nograde": "Ni ocene", - "core.none": "Brez", - "core.nooptionavailable": "Na voljo ni nobene možnosti", - "core.nopasswordchangeforced": "Brez zamenjave gesla ne morete nadaljevati.", - "core.nopermissionerror": "Trenutno žal nimate dovoljenj za to", - "core.nopermissions": "Žal trenutno nimate dovoljenje za to dejanje ({{$a}}).", - "core.noresults": "Ni rezultata", - "core.noselection": "Ni izbire", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Profil ni dostopen, ker uporabnik ni vpisan v ta predmet.", - "core.notice": "Opomin", - "core.notingroup": "Žal morate biti del skupine, da bi videli to stran.", - "core.notsent": "Ni poslano", - "core.now": "zdaj", - "core.nummore": "Še {{$a}}", - "core.numwords": "{{$a}} besed", - "core.offline": "Ni prijavljen", - "core.ok": "V redu", - "core.online": "Prijavljen", - "core.openfullimage": "Kliknite tukaj za prikaz slike v polni velikosti", - "core.openinbrowser": "Odpri v brskalniku", - "core.othergroups": "Ostale skupine", - "core.pagea": "Stran {{$a}}", - "core.paymentinstant": "Uporabite spodnje gumbe za plačilo in vpis v nekaj minutah!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pictureof": "Slika od {{$a}}", - "core.previous": "Prejšnje", - "core.proceed": "Nadaljuj", - "core.pulltorefresh": "Za osvežitev podrsaj navzdol", - "core.question.answer": "Odgovor", - "core.question.answersaved": "Odgovor shranjen", - "core.question.cannotdeterminestatus": "Stanja ni mogoče določiti", - "core.question.certainty": "Gotovost", - "core.question.complete": "Končano", - "core.question.correct": "Pravilno", - "core.question.errorattachmentsnotsupported": "Aplikacija še ne podpira pripenjanja datotek na odgovore.", - "core.question.errorinlinefilesnotsupported": "Aplikacija še ne podpira urejanja vdelanih datotek.", - "core.question.errorquestionnotsupported": "Aplikacija te vrste vprašanj ne podpira: {{$a}}.", - "core.question.feedback": "Odziv", - "core.question.howtodraganddrop": "Tapnite za izbiro datoteke in nato še enkrat, da jo spustite.", - "core.question.incorrect": "Nepravilno", - "core.question.information": "Informacije", - "core.question.invalidanswer": "Nepopoln odgovor", - "core.question.notanswered": "Ni odgovorjeno", - "core.question.notyetanswered": "Ni še odgovorjeno", - "core.question.partiallycorrect": "Delno pravilno", - "core.question.questionmessage": "Vprašanje {{$a}}: {{$b}}", - "core.question.questionno": "Vprašanje {{$a}}", - "core.question.requiresgrading": "Potrebuje ocenjevanje", - "core.quotausage": "Trenutno ste porabili {{$a.used}} od omejitve {{$a.total}}.", - "core.rating.aggregateavg": "Povprečje ocenitev", - "core.rating.aggregatecount": "Štetje ocenitev", - "core.rating.aggregatemax": "Najvišja ocenitev", - "core.rating.aggregatemin": "Najnižja ocenitev", - "core.rating.aggregatesum": "Vsota ocenitev", - "core.rating.noratings": "Ni oddanih ocenitev", - "core.rating.rating": "Ocenitev", - "core.rating.ratings": "Ocenitve", - "core.redirectingtosite": "Preusmerjeni boste na spletno mesto.", - "core.refresh": "Osveži", - "core.remove": "Odstrani", - "core.removefiles": "Odstrani datoteke {{$a}}", - "core.required": "Zahtevano", - "core.requireduserdatamissing": "Uporabnik nima nekaterih zahtevanih podatkov o profilu. Vnesite podatke na svojem spletnem mestu in poskusite znova.
                      {{$a}}", - "core.resourcedisplayopen": "Odpri", - "core.resources": "Viri", - "core.restore": "Obnovi", - "core.restricted": "Omejeno", - "core.retry": "Ponovni poskus", - "core.save": "Shrani", - "core.savechanges": "Shrani spremembe", - "core.search": "Išči", - "core.searching": "Iskanje", - "core.searchresults": "Rezultati iskanja", - "core.sec": "s", - "core.secs": "s", - "core.seemoredetail": "Kliknite tu za ogled podrobnosti", - "core.selectacategory": "Izberite kategorijo", - "core.selectacourse": "Izberi predmet", - "core.selectagroup": "Izberite skupino", - "core.send": "Pošlji", - "core.sending": "Pošiljam", - "core.serverconnection": "Napaka pri vzpostavljanju povezave s strežnikom", - "core.settings.about": "O", - "core.settings.appsettings": "Nastavitve aplikacije", - "core.settings.appversion": "Različica aplikacije", - "core.settings.cannotsyncoffline": "Sinhronizacija ni možna brez povezave na splet.", - "core.settings.cannotsyncwithoutwifi": "Sinhronizacija ni mogoča, ker trenutne nastavitve omogočajo sinhronizacijo samo, ko ste povezani z Wi-Fi. Povežite se z omrežjem Wi-Fi.", - "core.settings.colorscheme": "Barvna shema", - "core.settings.colorscheme-auto": "Samodejno (glede na sistemske nastavitve)", - "core.settings.colorscheme-dark": "Temno", - "core.settings.colorscheme-light": "Svetlo", - "core.settings.compilationinfo": "Informacije o kodnem prevodu", - "core.settings.copyinfo": "Kopirajte podatke o napravi v odložišče", - "core.settings.cordovadevicemodel": "Cordova model naprave", - "core.settings.cordovadeviceosversion": "Cordova naprava verzija OS", - "core.settings.cordovadeviceplatform": "Platforma Cordova", - "core.settings.cordovadeviceuuid": "Cordova naprava UUID", - "core.settings.cordovaversion": "Cordova verzija", - "core.settings.currentlanguage": "Trenuten jezik", - "core.settings.debugdisplay": "Prikaži sporočila za odpravljanje napak", - "core.settings.debugdisplaydescription": "", - "core.settings.deletesitefiles": "Ali ste prepričani, da želite izbrisati prenesene datoteke in predpomnjene podatke s spletnega mesta '{{sitename}}'? Aplikacije ne boste mogli uporabljati v načinu brez povezave na splet.", - "core.settings.deletesitefilestitle": "Izbrišite datoteke spletnega mesta", - "core.settings.deviceinfo": "Informacije o napravi", - "core.settings.deviceos": "OS naprava", - "core.settings.disableall": "Onemogoči obvestila", - "core.settings.disabled": "Onemogočeno", - "core.settings.displayformat": "Oblika prikaza", - "core.settings.enabledownloadsection": "Omogoči prenose", - "core.settings.enablefirebaseanalytics": "Omogoči analitiko Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Če je omogočeno, bo aplikacija zbirala uporabo anonimnih podatkov.", - "core.settings.enablerichtexteditor": "Omogoči urejevalnik besedil", - "core.settings.enablerichtexteditordescription": "Če je omogočeno, bo ob vnosu vsebine na voljo urejevalnik besedil.", - "core.settings.enablesyncwifi": "Dovoli sinhronizacijo samo, ko je v omrežju Wi-Fi", - "core.settings.entriesincache": "{{$a}} vnosov v predpomnilnik", - "core.settings.errordeletesitefiles": "Napaka pri brisanju datotek spletnega mesta.", - "core.settings.errorsyncsite": "Napaka pri sinhronizaciji podatkov spletnega mesta. Preverite internetno povezavo in poskusite znova.", - "core.settings.estimatedfreespace": "Predvidena količina prostega prostora", - "core.settings.filesystemroot": "Korenski datotečni sistem", - "core.settings.fontsize": "Velikost besedila", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "To nastavitev je prisilila konfiguracija vašega spletnega mesta.", - "core.settings.general": "Splošno", - "core.settings.language": "Jezik", - "core.settings.license": "Licenca", - "core.settings.localnotifavailable": "Lokalna obvestila na voljo", - "core.settings.locationhref": "Odpri spletni URL naslov", - "core.settings.locked": "Zaklenjeno", - "core.settings.loggedin": "Prijavljen", - "core.settings.loggedoff": "Ni prijavljen", - "core.settings.navigatorlanguage": "Navigacijski jezik", - "core.settings.navigatoruseragent": "Navigator userAgent", - "core.settings.networkstatus": "Status internetne povezave", - "core.settings.opensourcelicenses": "Odprto kodne licence", - "core.settings.preferences": "Nastavitve", - "core.settings.privacypolicy": "Politika zasebnosti", - "core.settings.publisher": "Izdajatelj", - "core.settings.pushid": "ID potisnih obvestil", - "core.settings.reportinbackground": "Samodejno prijavi napake", - "core.settings.screen": "Informacije o zaslonu", - "core.settings.settings": "Nastavitve", - "core.settings.showdownloadoptions": "Pokaži možnosti prenosa", - "core.settings.siteinfo": "Podatki o spletnem mestu", - "core.settings.sites": "Spletna mesta", - "core.settings.spaceusage": "Poraba prostora", - "core.settings.spaceusagehelp": "Če izbrišete shranjene podatke spletnega mesta, boste s tem odstranili vse podatke spletnega mesta brez povezave na splet. Te informacije vam omogočajo uporabo aplikacije v načinu brez povezave na splet.", - "core.settings.synchronization": "Sinhronizacija", - "core.settings.synchronizenow": "Sinhroniziraj zdaj", - "core.settings.synchronizenowhelp": "S sinhronizacijo spletnega mesta bodo poslane vse spremembe in dejavnosti brez povezave na splet, ki so shranjene v napravi. Sinhronizirali se bodo tudi nekateri drugi podatki, kot so sporočila in obvestila.", - "core.settings.syncsettings": "Nastavitve za sinhronizacijo", - "core.settings.total": "Skupno", - "core.settings.wificonnection": "Povezava Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "Izberite račun, v katerega želite shraniti datoteko.", - "core.sharedfiles.chooseactionrepeatedfile": "Datoteka s tem imenom že obstaja. Želite zamenjati obstoječo datoteko ali jo preimenovati v \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Spletna mesta niso shranjena. Pred skupno rabo datoteke z aplikacijo, dodajte spletno mesto.", - "core.sharedfiles.nosharedfiles": "Na tem mestu ni shranjenih datotek v skupni rabi.", - "core.sharedfiles.nosharedfilestoupload": "Tu ni nobenih datotek. Če želite naložiti datoteko iz druge aplikacije, poiščite datoteko in kliknite gumb »Odpri v«.", - "core.sharedfiles.rename": "Preimenuj", - "core.sharedfiles.replace": "Zamenjaj", - "core.sharedfiles.sharedfiles": "Deljene datoteke", - "core.sharedfiles.successstorefile": "Datoteka je uspešno shranjena. Izberite datoteko, ki jo želite naložiti v svoje zasebne datoteke ali uporabiti v dejavnosti.", - "core.show": "Pokaži", - "core.showless": "Strni...", - "core.showmore": "Prikaži več...", - "core.site": "Spletno mesto", - "core.sitehome.sitehome": "Prva stran spletnega mesta", - "core.sitehome.sitenews": "Novice spletnega mesta", - "core.sitemaintenance": "To stran pravkar vzdržujemo in trenutno ni na voljo", - "core.sizeb": "bajtov", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Preskoči", - "core.sorry": "Oprostite...", - "core.sort": "Razvrsti", - "core.sortby": "Razvrsti po", - "core.start": "Začetek", - "core.strftimedate": "%d. %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d. %B", - "core.strftimedatetime": "%d. %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d. %B %Y", - "core.strftimedaydatetime": "%A, %d. %B %Y, %H:%M %p", - "core.strftimedayshort": "%A, %d. %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d. %b, %H:%M", - "core.strftimerecentfull": "%a, %d. %b %Y, %H:%M %p", - "core.strftimetime": "%H:%M %p", - "core.strftimetime12": "%H:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Pošlji", - "core.success": "Uspešno", - "core.tablet": "Tablični računalnik", - "core.tag.defautltagcoll": "Privzeta zbirka", - "core.tag.errorareanotsupported": "Aplikacija te oznake ne podpira.", - "core.tag.inalltagcoll": "Povsod", - "core.tag.itemstaggedwith": "{{$a.tagarea}} označeno z \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Ni rezultatov za \"{{$a}}\"", - "core.tag.notagsfound": "Nobena oznaka se ne ujema s/z \"{{$a}}\"", - "core.tag.searchtags": "Iskanje po oznakah", - "core.tag.showingfirsttags": "Prikazujem {{$a}} najpopularnejše oznake", - "core.tag.tag": "Oznaka", - "core.tag.tagarea_course": "Predmeti", - "core.tag.tagarea_course_modules": "Aktivnosti in viri", - "core.tag.tagarea_post": "Objave na blogu", - "core.tag.tagarea_user": "Uporabniški interesi", - "core.tag.tags": "Oznake", - "core.tag.warningareasnotsupported": "Nekate oznake niso prikazane, ker jih aplikacija ne podpira.", - "core.teachers": "Izvajalci", - "core.thereisdatatosync": "Obstajajo brez povezave {{$a}}, ki jih je potrebno sinhronizirati.", - "core.thisdirection": "ltr", - "core.time": "Čas", - "core.timesup": "Čas je potekel!", - "core.today": "Danes", - "core.tryagain": "Poskusi ponovno", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.uhoh": "Ojoj!", - "core.unexpectederror": "Nepričakovana napaka. Zaprite in znova odprite aplikacijo in poskusite znova.", - "core.unicodenotsupported": "Nekateri smeškoti na tej strani niso podprti. Ko bo sporočilo poslano, bodo takšni znaki odstranjeni.", - "core.unicodenotsupportedcleanerror": "Pri čiščenju znakov Unicode je bilo najdeno prazno besedilo.", - "core.unknown": "Neznano", - "core.unlimited": "Neomejeno", - "core.unzipping": "Nestisnjeno", - "core.updaterequired": "Potrebna je posodobitev aplikacije", - "core.updaterequireddesc": "Posodobite aplikacijo na različico {{$a}}", - "core.upgraderunning": "Stran je v posodabljanju, prosimo poskusite pozneje.", - "core.user": "Uporabnik", - "core.user.address": "Naslov", - "core.user.city": "Mesto/kraj", - "core.user.contact": "Kontakt", - "core.user.country": "Država", - "core.user.description": "Opis", - "core.user.details": "Podrobnosti", - "core.user.detailsnotavailable": "Podrobnosti tega uporabnika niso na voljo.", - "core.user.editingteacher": "Izvajalec", - "core.user.email": "Naslov e-pošte", - "core.user.emailagain": "E-pošta (ponovno)", - "core.user.errorloaduser": "Napaka pri nalaganju uporabnika.", - "core.user.firstname": "Ime", - "core.user.interests": "Interesi", - "core.user.lastname": "Priimek", - "core.user.manager": "Upravljalec", - "core.user.newpicture": "Nova slika", - "core.user.noparticipants": "Za ta predmet ni najdenih udeležencev", - "core.user.participants": "Sodelujoči", - "core.user.phone1": "Telefon", - "core.user.phone2": "Številka mobilnika", - "core.user.roles": "Vloge", - "core.user.sendemail": "E-poštno sporočilo", - "core.user.student": "Udeleženec", - "core.user.teacher": "Izvajalec brez pravic urejanja", - "core.user.webpage": "Spletna stran", - "core.userdeleted": "Ta uporabniški račun je bil izbrisan", - "core.userdetails": "Uporabnikove podrobnosti", - "core.usernotfullysetup": "Uporabnik ni v celoti nastavljen", - "core.users": "Uporabniki", - "core.view": "Ogled", - "core.viewcode": "Prikaži kodo", - "core.vieweditor": "Prikaži urejevalnik", - "core.viewembeddedcontent": "Prikaži vdelano vsebino", - "core.viewprofile": "Poglej profil", - "core.warningofflinedatadeleted": "Podatki brez povezave na splet od {{component}} '{{name}}' so bili izbrisani. {{error}}", - "core.whatisyourage": "Koliko ste stari?", - "core.wheredoyoulive": "V kateri državi živite?", - "core.whoops": "Ups!", - "core.whyisthishappening": "Zakaj se to dogaja?", - "core.whyisthisrequired": "Zakaj je to zahtevano?", - "core.wsfunctionnotavailable": "Funkcija spletne storitve ni na voljo.", - "core.year": "leto", - "core.years": "leta", - "core.yes": "Da", - "core.youreoffline": "Ste brez povezave na splet", - "core.youreonline": "Spet ste povezani" -} \ No newline at end of file diff --git a/src/assets/lang/sr-cr.json b/src/assets/lang/sr-cr.json deleted file mode 100644 index 3d40979a2..000000000 --- a/src/assets/lang/sr-cr.json +++ /dev/null @@ -1,1931 +0,0 @@ -{ - "addon.badges.alignment": "Компетенција", - "addon.badges.badgedetails": "Подаци о беџу", - "addon.badges.badges": "Беџеви", - "addon.badges.bendorsement": "Потврда", - "addon.badges.claimcomment": "Коментар потврде", - "addon.badges.claimid": "URL адреса захтева", - "addon.badges.contact": "Контакт", - "addon.badges.dateawarded": "Датум издавања", - "addon.badges.expired": "Истекло", - "addon.badges.expirydate": "Датум истека", - "addon.badges.imageauthoremail": "Адреса е-поште аутора слике беџа", - "addon.badges.imageauthorname": "Име аутора слике беџа", - "addon.badges.imageauthorurl": "URL адреса аутора слике беџа", - "addon.badges.imagecaption": "Потпис испод слике", - "addon.badges.issuancedetails": "Беџ истиче", - "addon.badges.issuerdetails": "Подаци о издавачу", - "addon.badges.issueremail": "Адреса е-поште", - "addon.badges.issuername": "Име/назив издавача беџа", - "addon.badges.issuerurl": "URL адреса издавача", - "addon.badges.language": "Језик", - "addon.badges.noalignment": "Овај беџ нема наведене екстерне вештине или стандарде.", - "addon.badges.nobadges": "Нема доступних беџева", - "addon.badges.norelated": "Ова беџ нема повезаних беџева.", - "addon.badges.recipientdetails": "Детаљи о примаоцу", - "addon.badges.relatedbages": "Повезани беџеви", - "addon.badges.version": "Верзија", - "addon.badges.warnexpired": "(Овај беџ је истекао!)", - "addon.block_activitymodules.pluginname": "Активности", - "addon.block_activityresults.pluginname": "Резултати активности", - "addon.block_badges.pluginname": "Најновији беџеви", - "addon.block_blogmenu.pluginname": "Мени блога", - "addon.block_blogrecent.pluginname": "Скорашњи блог чланци", - "addon.block_blogtags.pluginname": "Ознаке блогова", - "addon.block_calendarmonth.pluginname": "Календар", - "addon.block_calendarupcoming.pluginname": "Предстојећи догађаји", - "addon.block_comments.pluginname": "Коментари", - "addon.block_completionstatus.pluginname": "Статус завршетка курса", - "addon.block_glossaryrandom.pluginname": "Случајни појам из речника", - "addon.block_learningplans.pluginname": "Планови учења", - "addon.block_myoverview.all": "Сви (осим скривених)", - "addon.block_myoverview.allincludinghidden": "Сви", - "addon.block_myoverview.favourites": "Означен звездицом", - "addon.block_myoverview.future": "Будући", - "addon.block_myoverview.hiddencourses": "Скривени", - "addon.block_myoverview.inprogress": "У току", - "addon.block_myoverview.lastaccessed": "Последњем приступу", - "addon.block_myoverview.morecourses": "Више курсева", - "addon.block_myoverview.nocourses": "Нема курсева", - "addon.block_myoverview.past": "Прошли", - "addon.block_myoverview.pluginname": "Преглед курсева", - "addon.block_myoverview.title": "Назив курса", - "addon.block_newsitems.pluginname": "Најновије вести", - "addon.block_onlineusers.pluginname": "Онлајн корисници", - "addon.block_privatefiles.pluginname": "Приватне датотеке", - "addon.block_recentactivity.pluginname": "Недавне активности", - "addon.block_recentlyaccessedcourses.nocourses": "Нема курсева којима сте скоро приступали", - "addon.block_recentlyaccessedcourses.pluginname": "Скорашњи курсеви", - "addon.block_recentlyaccesseditems.noitems": "Нема активности/ресурса којима сте скоро приступали", - "addon.block_recentlyaccesseditems.pluginname": "Скорашње активности/ресурси", - "addon.block_rssclient.pluginname": "Удаљени RSS извор", - "addon.block_selfcompletion.pluginname": "Самостални завршетак", - "addon.block_sitemainmenu.pluginname": "Главни мени", - "addon.block_starredcourses.nocourses": "Нема курсева са звездицом", - "addon.block_starredcourses.pluginname": "Курсеви са звездицом", - "addon.block_tags.pluginname": "Ознаке (тагови)", - "addon.block_timeline.duedate": "Крајњи рок", - "addon.block_timeline.next30days": "Следећих 30 дана", - "addon.block_timeline.next3months": "Следећа 3 месеца", - "addon.block_timeline.next6months": "Следећих 6 месеца", - "addon.block_timeline.next7days": "Следећих 7 дана", - "addon.block_timeline.nocoursesinprogress": "Нема курсева који су у току", - "addon.block_timeline.noevents": "Нема активности које се ускоро завршавају", - "addon.block_timeline.overdue": "Истекле активности", - "addon.block_timeline.pluginname": "Временски распоред", - "addon.block_timeline.sortbycourses": "Сортирај по курсевима", - "addon.block_timeline.sortbydates": "Сортирај по датумима", - "addon.blog.blog": "Блог", - "addon.blog.blogentries": "Блог чланци", - "addon.blog.linktooriginalentry": "Линк ка изворном блог чланку", - "addon.blog.noentriesyet": "Овде нема доступних чланка", - "addon.blog.publishtonoone": "Ви (нацрт)", - "addon.blog.publishtosite": "Све на овом сајту", - "addon.blog.publishtoworld": "Било ком на свету", - "addon.blog.siteblogheading": "Блог сајта", - "addon.calendar.allday": "Цео дан", - "addon.calendar.calendar": "Календар", - "addon.calendar.calendarevents": "Догађаји у календару", - "addon.calendar.categoryevents": "Догађаји на нивоу категорије", - "addon.calendar.confirmeventdelete": "Да ли сте сигурни да желите да обришете догађај \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "Догађај \"{{$a.name}}\" је део низа. Да ли желите да обришете само овај или све догађаје (укупно: {{$a.count}}) у низу?", - "addon.calendar.courseevents": "Догађаји на нивоу курса", - "addon.calendar.daynext": "Следећи дан", - "addon.calendar.dayprev": "Претходни дан", - "addon.calendar.defaultnotificationtime": "Подразумевано време за слање обавештења", - "addon.calendar.deleteallevents": "Обриши све догађаје", - "addon.calendar.deleteevent": "Обриши догађај", - "addon.calendar.deleteoneevent": "Обриши овај догађај", - "addon.calendar.durationminutes": "Трајање у минутима", - "addon.calendar.durationnone": "Неограничено", - "addon.calendar.durationuntil": "Све до", - "addon.calendar.editevent": "Уређивање догађаја", - "addon.calendar.errorloadevent": "Грешка приликом учитавања догађаја.", - "addon.calendar.errorloadevents": "Грешка приликом учитавања догађаја.", - "addon.calendar.eventcalendareventdeleted": "Догађај у календару обрисан", - "addon.calendar.eventduration": "Време трајања", - "addon.calendar.eventendtime": "Крајње време", - "addon.calendar.eventkind": "Врста догађаја", - "addon.calendar.eventname": "Назив догађаја", - "addon.calendar.eventstarttime": "Почетно време", - "addon.calendar.eventtype": "Тип догађаја", - "addon.calendar.fri": "Пет", - "addon.calendar.friday": "Петак", - "addon.calendar.gotoactivity": "Иди на активност", - "addon.calendar.groupevents": "Групни догађаји", - "addon.calendar.invalidtimedurationminutes": "Трајање у минутима које сте унели није исправно. Молимо унесите трајање у минутима веће од 0 или изаберите 'Нема одређено трајање'.", - "addon.calendar.invalidtimedurationuntil": "Датум и време које сте одабрали за завршетак догађаја је пре датума и времена његовог почетка. Молимо исправите ово пре него што наставите.", - "addon.calendar.mon": "Пон", - "addon.calendar.monday": "Понедељак", - "addon.calendar.monthlyview": "Месечни преглед", - "addon.calendar.newevent": "Нови догађај", - "addon.calendar.noevents": "Нема догађаја", - "addon.calendar.nopermissiontoupdatecalendar": "Жао нам је, али немате овлашћења да ажурирате догађај у календару.", - "addon.calendar.repeatedevents": "Догађаји који се понављају", - "addon.calendar.repeateditall": "Примени, такође, промене на преостала/их {{$a}} догађаја у овом понављајућем низу", - "addon.calendar.repeateditthis": "Примени промене само на овај догађај", - "addon.calendar.repeatevent": "Понови овај догађај", - "addon.calendar.repeatweeksl": "Седмично понављање, потпуно креирање", - "addon.calendar.sat": "Суб", - "addon.calendar.saturday": "Субота", - "addon.calendar.siteevents": "Прикажи догађаје на нивоу сајта", - "addon.calendar.sun": "Нед", - "addon.calendar.sunday": "Недеља", - "addon.calendar.thu": "Чет", - "addon.calendar.thursday": "Четвртак", - "addon.calendar.today": "Данас", - "addon.calendar.tomorrow": "Сутра", - "addon.calendar.tue": "Уто", - "addon.calendar.tuesday": "Уторак", - "addon.calendar.typecategory": "Догађај на нивоу категорије", - "addon.calendar.typeclose": "Затворен догађај", - "addon.calendar.typecourse": "Догађај курса", - "addon.calendar.typedue": "Орочен догађај", - "addon.calendar.typegradingdue": "Рок за оцењивање", - "addon.calendar.typegroup": "Групни догађај", - "addon.calendar.typeopen": "Отворен догађај", - "addon.calendar.typesite": "Догађај на нивоу сајта", - "addon.calendar.typeuser": "Кориснички догађај", - "addon.calendar.upcomingevents": "Предстојећи догађаји", - "addon.calendar.userevents": "Кориснички догађаји", - "addon.calendar.wed": "Сре", - "addon.calendar.wednesday": "Среда", - "addon.calendar.when": "Када", - "addon.calendar.yesterday": "Јуче", - "addon.competency.activities": "Активности", - "addon.competency.competencies": "Компетенције", - "addon.competency.competenciesmostoftennotproficientincourse": "Компетенције које најчешће нису усавршене на овом курсу", - "addon.competency.coursecompetencies": "Компетенције курса", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Рангирање компетенција на овом курсу на утиче на планове учења.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Рангирање компетенција на овом курсу се аутоматски ажурира у плановима учења.", - "addon.competency.crossreferencedcompetencies": "Унакрсно повезане компетенције", - "addon.competency.duedate": "Крајњи рок", - "addon.competency.errornocompetenciesfound": "Није пронађена ниједна компетенција", - "addon.competency.evidence": "Доказ", - "addon.competency.evidence_competencyrule": "Правило компетенције испуњено.", - "addon.competency.evidence_coursecompleted": "Курс '{{$a}}' је завршен.", - "addon.competency.evidence_coursemodulecompleted": "Активност '{{$a}}' је завршена.", - "addon.competency.evidence_courserestored": "Процена компетенција је рестаурисана заједно са курсом '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Доказ о претходном учењу '{{$a}}' је повезан.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Веза са доказа о претходном учењу '{{$a}}' је уклоњена.", - "addon.competency.evidence_manualoverride": "Процена компетенције је ручно подешена", - "addon.competency.evidence_manualoverrideincourse": "Процена компетенције је ручно подешена на курсу '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "Процена компетенције је ручно подешена плану учења '{{$a}}'.", - "addon.competency.learningplancompetencies": "Компетенције плана учења", - "addon.competency.learningplans": "Планови учења", - "addon.competency.myplans": "Моји планови учења", - "addon.competency.noactivities": "Нема активности", - "addon.competency.nocompetencies": "Нема компетенција", - "addon.competency.nocompetenciesincourse": "Ниједна компетенција није повезана са овим курсом.", - "addon.competency.nocrossreferencedcompetencies": "Ниједна друга компетенција није унакрсно повезана са овом компетенцијом.", - "addon.competency.noevidence": "Нема доказа", - "addon.competency.noplanswerecreated": "Није креиран ниједан план учења.", - "addon.competency.nouserplanswithcompetency": "Ниједан план за учење не садржи ову компетенцију.", - "addon.competency.path": "Путања:", - "addon.competency.planstatusactive": "Активан", - "addon.competency.planstatuscomplete": "Испуњен", - "addon.competency.planstatusdraft": "Радна верзија", - "addon.competency.planstatusinreview": "На прегледу", - "addon.competency.planstatuswaitingforreview": "Чека на преглед", - "addon.competency.proficient": "Стручан", - "addon.competency.progress": "Напредовање", - "addon.competency.rating": "Рангирање", - "addon.competency.reviewstatus": "Прегледај статус", - "addon.competency.status": "Статус", - "addon.competency.template": "Шаблон плана учења", - "addon.competency.uponcoursecompletion": "Након завршетка курса:", - "addon.competency.usercompetencystatus_idle": "У стању мировања", - "addon.competency.usercompetencystatus_inreview": "На прегледу", - "addon.competency.usercompetencystatus_waitingforreview": "Чека на преглед", - "addon.competency.userplans": "Планови учења", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} од {{$a.y}} компетенција су на највишем нивоу стручности", - "addon.competency.xcompetenciesproficientoutofyincourse": "Стручни сте у {{$a.x}} од {{$a.y}} компетенција на овом курсу.", - "addon.coursecompletion.complete": "Заврши", - "addon.coursecompletion.completecourse": "Завршен курс", - "addon.coursecompletion.completed": "Завршено", - "addon.coursecompletion.completiondate": "Датум завршетка", - "addon.coursecompletion.completionmenuitem": "Завршетак", - "addon.coursecompletion.couldnotloadreport": "Није могуће учитати извештај о завршетку курса. Молимо вас, покушајте поново касније.", - "addon.coursecompletion.coursecompletion": "Завршетак курса", - "addon.coursecompletion.criteria": "Критеријуми", - "addon.coursecompletion.criteriagroup": "Група критеријума", - "addon.coursecompletion.criteriarequiredall": "Сви доле наведени критеријуми су неопходни", - "addon.coursecompletion.criteriarequiredany": "Било који од доле наведених критеријума је неопходан", - "addon.coursecompletion.inprogress": "У току", - "addon.coursecompletion.manualselfcompletion": "Ручни самостални завршетак", - "addon.coursecompletion.nottracked": "Тренутно се не прати ваша активност ка завршетку курса", - "addon.coursecompletion.notyetstarted": "Није још почео", - "addon.coursecompletion.pending": "На чекању", - "addon.coursecompletion.required": "Обавезно", - "addon.coursecompletion.requiredcriteria": "Обавезни критеријуми", - "addon.coursecompletion.requirement": "Захтев", - "addon.coursecompletion.status": "Статус", - "addon.coursecompletion.viewcoursereport": "Прикажи извештај са курса", - "addon.files.couldnotloadfiles": "Списак датотека не може бити учитан.", - "addon.files.emptyfilelist": "Нема датотека за приказ.", - "addon.files.erroruploadnotworking": "Нажалост, тренутно није могуће отпремити датотеке на ваш сајт.", - "addon.files.files": "Датотеке", - "addon.files.privatefiles": "Приватне датотеке", - "addon.files.sitefiles": "Датотеке сајта", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Конфигуриши уређаје", - "addon.messages.acceptandaddcontact": "Прихватите и додајте у контакте", - "addon.messages.addcontact": "Додај контакт", - "addon.messages.addcontactconfirm": "Јесте ли сигурни да желите да додате {{$a}} међу своје контакте?", - "addon.messages.addtofavourites": "Означи преписку звездицом", - "addon.messages.addtoyourcontacts": "Додај у контакте", - "addon.messages.blocknoncontacts": "Спречи све кориснике ван списка контаката да ми шаљу поруке", - "addon.messages.blockuser": "Блокирај корисника", - "addon.messages.blockuserconfirm": "Јесте ли сигурни да желите да блокирате {{$a}}?", - "addon.messages.contactableprivacy": "Прихватите поруке од:", - "addon.messages.contactableprivacy_coursemember": "Моји контакти и било ко на мојим курсевима", - "addon.messages.contactableprivacy_onlycontacts": "Само моји контакти", - "addon.messages.contactableprivacy_site": "Било ко на сајту", - "addon.messages.contactblocked": "Контак блокиран", - "addon.messages.contactlistempty": "Листа контаката је празна", - "addon.messages.contactname": "Име особе", - "addon.messages.contactrequestsent": "Захтев за контакт је послат", - "addon.messages.contacts": "Контакти", - "addon.messages.conversationactions": "Преписка - Мени акција", - "addon.messages.decline": "Одбиј", - "addon.messages.deleteallconfirm": "Да ли сте сигурни да желите да у потпуности обришете ову преписку? Ово неће избрисати преписку за друге учеснике.", - "addon.messages.deleteallselfconfirm": "Да ли сте сигурни да желите да обришете у потпуности ову личну преписку?", - "addon.messages.deleteconversation": "Обриши преписку", - "addon.messages.deleteforeveryone": "Обриши за мене и све друге", - "addon.messages.deletemessage": "Обриши поруку", - "addon.messages.deletemessageconfirmation": "Да ли сигурни да желите да обришете ову поруку? Она ће бити избрисан само из ваше историје порука али ће и даље бити видљива кориснику који је послао или примио поруку.", - "addon.messages.errordeletemessage": "Грешка приликом брисања поруке.", - "addon.messages.errorwhileretrievingcontacts": "Грешка приликом преузимања контаката са сервера.", - "addon.messages.errorwhileretrievingdiscussions": "Грешка приликом преузимања дискусија са сервера.", - "addon.messages.errorwhileretrievingmessages": "Грешка приликом преузимања порука са сервера.", - "addon.messages.errorwhileretrievingusers": "Грешка приликом преузимања корисника са сервера.", - "addon.messages.groupconversations": "Група", - "addon.messages.groupinfo": "Инфо о групи", - "addon.messages.individualconversations": "Приватна преписка", - "addon.messages.info": "Подаци о кориснику", - "addon.messages.isnotinyourcontacts": "{{$a}} није међу вашим контактима", - "addon.messages.message": "Порука", - "addon.messages.messagenotsent": "Порука није послата. Молимо, покушајте поново касније.", - "addon.messages.messagepreferences": "Параметри порука", - "addon.messages.messages": "Поруке", - "addon.messages.muteconversation": "Привремено искључи", - "addon.messages.mutedconversation": "Привремено искључена преписка", - "addon.messages.newmessage": "Нова порука", - "addon.messages.newmessages": "Нове поруке", - "addon.messages.nocontactrequests": "Нема захтева за контакт", - "addon.messages.nocontactsgetstarted": "Нема контаката", - "addon.messages.nofavourites": "Нема означених преписки", - "addon.messages.nogroupconversations": "Нема групних преписки", - "addon.messages.noindividualconversations": "Нема приватних преписки", - "addon.messages.nomessagesfound": "Није пронађена ниједна порука", - "addon.messages.noncontacts": "Ван списка контаката", - "addon.messages.nousersfound": "Није пронађен ниједан корисник", - "addon.messages.numparticipants": "{{$a}} учесника", - "addon.messages.removecontact": "Обриши контакт", - "addon.messages.removecontactconfirm": "Јесте ли сигурни да желите уклонити {{$a}} са списка својих контаката?", - "addon.messages.removefromfavourites": "Уклони звездицу са преписке", - "addon.messages.removefromyourcontacts": "Уклони из контаката", - "addon.messages.requests": "Захтеви", - "addon.messages.requirecontacttomessage": "Потребно је да пошаљете захтев кориснику {{$a}} да вас дода као контакт како бисте могли да му/јој пошаљете поруку.", - "addon.messages.searchcombined": "Претражи људе и поруке", - "addon.messages.selfconversation": "Лични простор", - "addon.messages.selfconversationdefaultmessage": "Сачувајте радну верзију порука, везе, напомене итд. како бисте им касније приступили.", - "addon.messages.sendcontactrequest": "Пошаљите захтев за контакт", - "addon.messages.showdeletemessages": "Прикажи брисање порука", - "addon.messages.type_blocked": "Блокиран", - "addon.messages.type_offline": "Офлајн", - "addon.messages.type_online": "Онлајн", - "addon.messages.type_search": "Резултати претраге", - "addon.messages.type_strangers": "Други", - "addon.messages.unabletomessage": "Не можете да пошаљете поруку овом кориснику", - "addon.messages.unblockuser": "Деблокирај корисника", - "addon.messages.unblockuserconfirm": "Јесте ли сигурни да желите да деблокирате корисника {{$a}}?", - "addon.messages.unmuteconversation": "Поново укључи", - "addon.messages.useentertosend": "Користите тастер \"Ентер\" за слање", - "addon.messages.userwouldliketocontactyou": "{{$a}} жели да вас дода међу своје контакте", - "addon.messages.warningconversationmessagenotsent": "Није могуће послати поруку(е) у преписку {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Није могуће послати поруку/е кориснику {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Жели да вас дода међу своје контакте", - "addon.messages.you": "Ви:", - "addon.messages.youhaveblockeduser": "Блокирали сте овог корисника.", - "addon.messages.yourcontactrequestpending": "Ваш захтев за контакт чека на {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Молимо вас да прихватите изјаву о предаји рада.", - "addon.mod_assign.addattempt": "Дозволи други покушај", - "addon.mod_assign.addnewattempt": "Додај нови покушај", - "addon.mod_assign.addnewattemptfromprevious": "Додај нови покушај на основу претходно предатог рада", - "addon.mod_assign.addsubmission": "Додај рад", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Детаљније информације о задатку и образац за предају радова биће доступни од {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Дозволи предају од", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Овај задатак ће прихватити предају рада од {{$a}}", - "addon.mod_assign.applytoteam": "Примените оцене и повратне информације на целу групу", - "addon.mod_assign.assignmentisdue": "Крајњи рок за предају је истекао", - "addon.mod_assign.attemptnumber": "Број покушаја", - "addon.mod_assign.attemptreopenmethod": "Поновно отварање рада", - "addon.mod_assign.attemptreopenmethod_manual": "Ручно", - "addon.mod_assign.attemptreopenmethod_untilpass": "Аутоматски док не добије прелазну оцену", - "addon.mod_assign.attemptsettings": "Подешавања покушаја", - "addon.mod_assign.cannoteditduetostatementsubmission": "Не можете да додате или мењате рад у апликацији јер нисмо могли да преузмемо са сајта изјаву о предаји рада.", - "addon.mod_assign.cannotgradefromapp": "Апликације још увек не подржава неке методе оцењивања и оне не могу да се мењају.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Не можете да предате рад на оцењивање у апликацији јер нисмо могли да преузмемо са сајта изјаву о предаји рада.", - "addon.mod_assign.confirmsubmission": "Да ли сте сигурни да желите да предате свој рад на оцењивање? Више нећете моћи да га мењате.", - "addon.mod_assign.currentattempt": "Ово је покушај {{$a}}.", - "addon.mod_assign.currentattemptof": "Ово је покушај {{$a.attemptnumber}} ( {{$a.maxattempts}} дозвољена/их покушаја ).", - "addon.mod_assign.currentgrade": "Тренутна оцена у књизи оцена", - "addon.mod_assign.cutoffdate": "Дефинитивни рок", - "addon.mod_assign.defaultteam": "Подразумевана група", - "addon.mod_assign.duedate": "Крајњи рок", - "addon.mod_assign.duedateno": "Нема крајњег рока", - "addon.mod_assign.duedatereached": "Крајњи рок за овај задатак је сада истекао", - "addon.mod_assign.editingstatus": "Статус уређивања", - "addon.mod_assign.editsubmission": "Уреди рад", - "addon.mod_assign.erroreditpluginsnotsupported": "Не можете да додате или мењате рад у апликацији јер неки додаци немају подршку за уређивање:", - "addon.mod_assign.errorshowinginformation": "Не можемо да прикажемо информације о предатом раду", - "addon.mod_assign.extensionduedate": "Продужени рок", - "addon.mod_assign.feedbacknotsupported": "Аплликација не подржава ову повратну информацију. Могуће је да она не садржи све информације.", - "addon.mod_assign.grade": "Оцена", - "addon.mod_assign.graded": "Оцењено", - "addon.mod_assign.gradedby": "Оценио/ла", - "addon.mod_assign.gradedfollowupsubmit": "Оцењено - примљен накнадно достављен рад", - "addon.mod_assign.gradedon": "Оцењено", - "addon.mod_assign.gradelocked": "Ова оцена је закључана или преписана у књизи оцена.", - "addon.mod_assign.gradenotsynced": "Оцена није синхронизована", - "addon.mod_assign.gradeoutof": "Оцена од {{$a}}", - "addon.mod_assign.gradingstatus": "Статус оцењивања", - "addon.mod_assign.groupsubmissionsettings": "Подешавања за групну предају рада", - "addon.mod_assign.hiddenuser": "Учесник", - "addon.mod_assign.latesubmissions": "Касно предати радови", - "addon.mod_assign.latesubmissionsaccepted": "Дозвољено до {{$a}}", - "addon.mod_assign.markingworkflowstate": "Стање тока оцењивања", - "addon.mod_assign.markingworkflowstateinmarking": "Оцењује се", - "addon.mod_assign.markingworkflowstateinreview": "На прегледу", - "addon.mod_assign.markingworkflowstatenotmarked": "Није оцењено", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Спремно за објаву", - "addon.mod_assign.markingworkflowstatereadyforreview": "Оцењивање завршено", - "addon.mod_assign.markingworkflowstatereleased": "Објављено", - "addon.mod_assign.modulenameplural": "Задаци", - "addon.mod_assign.multipleteams": "Члан више од једне групе", - "addon.mod_assign.multipleteams_desc": "Овај задатак тражи групну предају рада. Члан сте више од једне групе. Да бисте могли да предате рад морате да будете члан само једне групе. Контактирајте свог предавача како би променио ваше чланство у групи.", - "addon.mod_assign.noattempt": "Нема покушаја", - "addon.mod_assign.nomoresubmissionsaccepted": "Дозвољено само полазницима којима је одобрен продужетак", - "addon.mod_assign.noonlinesubmissions": "Овај задатак не тражи од вас да било штa предате онлајн", - "addon.mod_assign.nosubmission": "За овај задатак још ништа није предато", - "addon.mod_assign.notallparticipantsareshown": "Учесници који нису предали рад се не приказују", - "addon.mod_assign.noteam": "Није члан ниједне групе.", - "addon.mod_assign.noteam_desc": "Овај задатак тражи групну предају рада. Нисте члан ниједне групе, тако да не можете да предате рад. Контактирајте свог предавача како би вас додао у групу.", - "addon.mod_assign.notgraded": "Није оцењено", - "addon.mod_assign.numberofdraftsubmissions": "Нацрти", - "addon.mod_assign.numberofparticipants": "Учесници", - "addon.mod_assign.numberofsubmissionsneedgrading": "Тражи оцењивање", - "addon.mod_assign.numberofsubmittedassignments": "Предато", - "addon.mod_assign.numberofteams": "Групе", - "addon.mod_assign.numwords": "{{$a}} реч(и)", - "addon.mod_assign.outof": "{{$a.current}} од {{$a.total}}", - "addon.mod_assign.overdue": "Крајњи рок за предају рада је истекао пре: {{$a}}", - "addon.mod_assign.submission": "Предати рад", - "addon.mod_assign.submissioneditable": "Полазници могу да уређују овај задатак", - "addon.mod_assign.submissionnoteditable": "Полазници не могу да уређују овај задатак", - "addon.mod_assign.submissionnotsupported": "Апликација не подржава овај предати рад. Могуће је да рад не садржи све информације.", - "addon.mod_assign.submissionslocked": "Овај задатак не прихвата предају радова", - "addon.mod_assign.submissionstatus": "Статус предатог рада", - "addon.mod_assign.submissionstatus_": "Нема предатих радова", - "addon.mod_assign.submissionstatus_draft": "Нацрт рада (није предато)", - "addon.mod_assign.submissionstatus_marked": "Оцењено", - "addon.mod_assign.submissionstatus_new": "Нови предати рад", - "addon.mod_assign.submissionstatus_reopened": "Поново отворено", - "addon.mod_assign.submissionstatus_submitted": "Предато за оцењивање", - "addon.mod_assign.submissionstatusheading": "Статус предатог рада", - "addon.mod_assign.submissionteam": "Група", - "addon.mod_assign.submitassignment": "Predaj rad", - "addon.mod_assign.submitassignment_help": "Оног тренутка када предате овај рад више нећете моћи да га мењате.", - "addon.mod_assign.submittedearly": "Задатак је предат {{$a}} раније", - "addon.mod_assign.submittedlate": "Задатак је предат {{$a}} касније", - "addon.mod_assign.timemodified": "Последње измене", - "addon.mod_assign.timeremaining": "Преостало време", - "addon.mod_assign.ungroupedusers": "Подешавање 'Неопходна је група за предају рада' је омогућено, али неки корисници нису чланови ниједне групе, или су чланови више од једне групе, тако да не могу да предају рад.", - "addon.mod_assign.ungroupedusersoptional": "Подешавање \"Полазници предају у групама\" је омогућено а неки корисници или нису чланови ниједне групе или су чланови више група. Молимо узмите у обзир да ће ти полазници предати решење задатка као чланови \"Подразумеване групе\".", - "addon.mod_assign.unlimitedattempts": "Неограничено", - "addon.mod_assign.userswhoneedtosubmit": "Корисници који треба да предају решење: {{$a}}", - "addon.mod_assign.userwithid": "Корисник са ID ознаком {{id}}", - "addon.mod_assign.viewsubmission": "Погледај предати рад", - "addon.mod_assign.warningsubmissiongrademodified": "Оцена предатог рада је измењена на сајту.", - "addon.mod_assign.warningsubmissionmodified": "Предати рад корисника је измењен на сајту.", - "addon.mod_assign.wordlimit": "Ограничење броја речи", - "addon.mod_assign_feedback_comments.pluginname": "Повратни коментари", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF са напоменама", - "addon.mod_assign_feedback_file.pluginname": "Датотека са повратним информацијама", - "addon.mod_assign_submission_comments.pluginname": "Коментари за предати рад", - "addon.mod_assign_submission_file.pluginname": "Предаја датотека", - "addon.mod_assign_submission_onlinetext.pluginname": "Предаја онлајн текстова", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Ограничење броја речи за овај задатак је {{$a.limit}} речи, а ви покушавате да пошаљете {{$a.count}} речи. Молимо, прегледајте свој рад и покушајте поново.", - "addon.mod_book.errorchapter": "Грешка приликом учитавања поглавља књиге.", - "addon.mod_book.modulenameplural": "Књиге", - "addon.mod_book.navnexttitle": "Следеће: {{$a}}", - "addon.mod_book.navprevtitle": "Претходно: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Поглавља књиге", - "addon.mod_book.toc": "Садржај", - "addon.mod_chat.beep": "Звучни сигнал", - "addon.mod_chat.chatreport": "Разговори", - "addon.mod_chat.currentusers": "Тренутни корисници", - "addon.mod_chat.enterchat": "Кликните овде за улазак у причаоницу", - "addon.mod_chat.entermessage": "Унесите вашу поруку", - "addon.mod_chat.errorwhileconnecting": "Грешка приликом повезивања са причаоницом.", - "addon.mod_chat.errorwhilegettingchatdata": "Грешка приликом преузимања података за 'Причаоницу'.", - "addon.mod_chat.errorwhilegettingchatusers": "Грешка приликом преузимања корисника причаонице.", - "addon.mod_chat.errorwhileretrievingmessages": "Грешка приликом преузимања порука са сервера.", - "addon.mod_chat.errorwhilesendingmessage": "Грешка приликом слања поруке.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} поздравља све звучним сигналом!", - "addon.mod_chat.messagebeepsyou": "{{$a}} вас је управо поздравио/ла звучним сигналом!", - "addon.mod_chat.messageenter": "Учесник {{$a}} управо улази у причаоницу", - "addon.mod_chat.messageexit": "Учесник {{$a}} управо напушта причаоницу", - "addon.mod_chat.messages": "Поруке", - "addon.mod_chat.messageyoubeep": "Поздравили сте звучним сигналом {{$a}}", - "addon.mod_chat.modulenameplural": "Причаонице", - "addon.mod_chat.mustbeonlinetosendmessages": "Морате бити онлајн како бисте слали поруке.", - "addon.mod_chat.nomessages": "Још нема порука", - "addon.mod_chat.saidto": "речено учеснику", - "addon.mod_chat.send": "Пошаљи", - "addon.mod_chat.sessionstart": "Следећа сесија ће почети дана {{$a.date}}, ({{$a.fromnow}} од сада)", - "addon.mod_chat.talk": "Причај", - "addon.mod_chat.viewreport": "Погледај претходне сесије", - "addon.mod_choice.cannotsubmit": "Извините, дошло је до проблема приликом чувања одабраног одговора. Молимо покушајте поново.", - "addon.mod_choice.choiceoptions": "Опције за избор", - "addon.mod_choice.errorgetchoice": "Грешка приликом преузимања података за 'Избор'", - "addon.mod_choice.expired": "Ова активност је затворена дана {{$a}}.", - "addon.mod_choice.full": "(Попуњено)", - "addon.mod_choice.modulenameplural": "Избори", - "addon.mod_choice.noresultsviewable": "Резултати тренутно нису видљиви.", - "addon.mod_choice.notopenyet": "Ова активност није доступна до {{$a}}", - "addon.mod_choice.numberofuser": "Број одговора", - "addon.mod_choice.numberofuserinpercentage": "Проценат одговора", - "addon.mod_choice.previewonly": "Ово је само приказ доступних опција за ову активност. Нећете бити у могућности да извршите одабир до {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Анонимни резултати биће објављени након што одговорите.", - "addon.mod_choice.publishinfoanonclose": "Анонимни резултати биће објављени након што се активност затвори.", - "addon.mod_choice.publishinfofullafter": "Комплетни резултати, са одговорима свих учесника, биће објављени након што одговорите.", - "addon.mod_choice.publishinfofullclose": "Комплетни резултати, са одговорима свих учесника, биће објављени након што се активност затвори.", - "addon.mod_choice.publishinfonever": "Резултати ове активности неће бити објављени након што одговорите.", - "addon.mod_choice.removemychoice": "Уклони мој избор", - "addon.mod_choice.responses": "Одговори", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% корисника је изабрало опцију: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Графички приказ", - "addon.mod_choice.resultsnotsynced": "Резултати не укључује ваш последњи одговор. Покрените синхронизацију како бисте их ажурирали.", - "addon.mod_choice.savemychoice": "Сачувај мој избор", - "addon.mod_choice.userchoosethisoption": "Корисници који су изабрали ову опцију", - "addon.mod_choice.yourselection": "Ваш избор", - "addon.mod_data.addentries": "Додај уносе", - "addon.mod_data.advancedsearch": "Напредно претраживање", - "addon.mod_data.alttext": "Алтернативни текст", - "addon.mod_data.approve": "Одобри", - "addon.mod_data.approved": "Одобрено", - "addon.mod_data.ascending": "Растуће", - "addon.mod_data.authorfirstname": "Име аутора", - "addon.mod_data.authorlastname": "Презиме аутора", - "addon.mod_data.confirmdeleterecord": "Да ли сте сигурни да желите да обришете овај унос?", - "addon.mod_data.descending": "Опадајуће", - "addon.mod_data.disapprove": "Повуци одобрење", - "addon.mod_data.emptyaddform": "Нисте испунили ниједно поље!", - "addon.mod_data.entrieslefttoadd": "Морате да додате још {{$a.entriesleft}} унос(а) како бисте завршили ову активност", - "addon.mod_data.entrieslefttoaddtoview": "Морате да додате још {{$a.entrieslefttoview}} уноса пре него што будете могли да видите уносе других корисника.", - "addon.mod_data.errorapproving": "Грешка приликом одобравања или неодобравања уноса.", - "addon.mod_data.errordeleting": "Грешка приликом брисања уноса.", - "addon.mod_data.errormustsupplyvalue": "Морате овде задати вредност.", - "addon.mod_data.expired": "Нажалост, ова активност је затворена {$}} и више није доступна", - "addon.mod_data.fields": "Поља", - "addon.mod_data.foundrecords": "Пронађени записи: {{$a.num}}/{{$a.max}}(Врати стара подешавања филтера)", - "addon.mod_data.latlongboth": "Поља за географску ширина и дужина су обавезна.", - "addon.mod_data.menuchoose": "Изаберите...", - "addon.mod_data.modulenameplural": "Базе података", - "addon.mod_data.more": "Још", - "addon.mod_data.nomatch": "Нема уноса који се поклапају!", - "addon.mod_data.norecords": "Нема уноса у бази података", - "addon.mod_data.notapproved": "Унос још није одобрен", - "addon.mod_data.notopenyet": "Нажалост, ова активност није доступна до {{$a}}", - "addon.mod_data.numrecords": "{{$a}} уноса", - "addon.mod_data.other": "Друго", - "addon.mod_data.recordapproved": "Унос је одобрен", - "addon.mod_data.recorddeleted": "Унос је обрисан", - "addon.mod_data.recorddisapproved": "Унос није одобрен", - "addon.mod_data.resetsettings": "Ресетуј филтере", - "addon.mod_data.search": "Тражи", - "addon.mod_data.selectedrequired": "Све изабрано обавезно", - "addon.mod_data.single": "Прикажи појединачно", - "addon.mod_data.tagarea_data_records": "Записи података", - "addon.mod_data.timeadded": "Време додавања", - "addon.mod_data.timemodified": "Време измене", - "addon.mod_data.usedate": "Укључи у претраживање.", - "addon.mod_feedback.analysis": "Анализа", - "addon.mod_feedback.anonymous": "Анонимни упитник", - "addon.mod_feedback.anonymous_entries": "Анонимни одговори ({{$a}})", - "addon.mod_feedback.average": "Просечно", - "addon.mod_feedback.captchaofflinewarning": "Упитник са Captcha елементом не може да буде завршен ако није конфигурисан, ако сте у офлајн режиму или ако је сервер искључен.", - "addon.mod_feedback.complete_the_form": "Одговори на питања", - "addon.mod_feedback.completed_feedbacks": "Анализа одговорa", - "addon.mod_feedback.continue_the_form": "Настави са одговарањем на питања", - "addon.mod_feedback.feedback_is_not_open": "Упитник није отворен", - "addon.mod_feedback.feedback_submitted_offline": "Овај упитник је сачуван како би касније био предат.", - "addon.mod_feedback.feedbackclose": "Упитник доступан до", - "addon.mod_feedback.feedbackopen": "Упитник доступан од", - "addon.mod_feedback.mapcourses": "Повежи упитник са курсевима", - "addon.mod_feedback.maximal": "Максимално", - "addon.mod_feedback.minimal": "Минимално", - "addon.mod_feedback.mode": "Врста упитника", - "addon.mod_feedback.modulenameplural": "Упитници (Feedback Activities)", - "addon.mod_feedback.next_page": "Следећа страница", - "addon.mod_feedback.non_anonymous": "Име корисника биће записано и приказано заједно са одговорима", - "addon.mod_feedback.non_anonymous_entries": "Неанонимни одговори ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Полазници који нису одговорили на упитник ({{$a}})", - "addon.mod_feedback.not_selected": "Није изабрано", - "addon.mod_feedback.not_started": "Није започето", - "addon.mod_feedback.numberoutofrange": "Број изван опсега", - "addon.mod_feedback.overview": "Преглед", - "addon.mod_feedback.page_after_submit": "Порука која ће бити приказана кориснику након што попуни упитник", - "addon.mod_feedback.preview": "Преглед", - "addon.mod_feedback.previous_page": "Претходна страница", - "addon.mod_feedback.questions": "Питања", - "addon.mod_feedback.response_nr": "Одговор бр.", - "addon.mod_feedback.responses": "Одговори", - "addon.mod_feedback.save_entries": "Пошаљи своје одговоре", - "addon.mod_feedback.show_entries": "Прикажи одговоре", - "addon.mod_feedback.show_nonrespondents": "Прикажи кориснике које нису одговорили на упитник", - "addon.mod_feedback.started": "Започето", - "addon.mod_feedback.this_feedback_is_already_submitted": "Већ сте попунили овај упитник.", - "addon.mod_folder.emptyfilelist": "Нема датотека за приказ.", - "addon.mod_folder.modulenameplural": "Директоријуми", - "addon.mod_forum.addanewdiscussion": "Додај нову тему за дискусију", - "addon.mod_forum.addanewquestion": "Додај ново питање", - "addon.mod_forum.addanewtopic": "Додај нову тему", - "addon.mod_forum.addtofavourites": "Означи ову дискусију звездицом", - "addon.mod_forum.advanced": "Напредно", - "addon.mod_forum.cannotadddiscussion": "Додавање дискусије у оквиру овог форума захтева групно чланство.", - "addon.mod_forum.cannotadddiscussionall": "Немате дозволу да додајете нову тему за дискусију за све учеснике.", - "addon.mod_forum.cannotcreatediscussion": "Није било могуће отворити нову дискусију", - "addon.mod_forum.couldnotadd": "Нажалост, није могуће додати Вашу поруку због непознате грешке", - "addon.mod_forum.couldnotupdate": "Ваша порука не може бити ажурирана због непознате грешке", - "addon.mod_forum.cutoffdatereached": "Дефинитивни рок за слање порука на овај форум је достигнут тако да више не можете да шаљете поруке.", - "addon.mod_forum.delete": "Обриши", - "addon.mod_forum.deletedpost": "Порука је обрисана", - "addon.mod_forum.deletesure": "Јесте ли сигурни да желите да обришете ову поруку?", - "addon.mod_forum.discussion": "Дискусија", - "addon.mod_forum.discussionlistsortbycreatedasc": "Сортирајте по датуму креирања растућим редоследом", - "addon.mod_forum.discussionlistsortbycreateddesc": "Сортирајте по датуму креирања опадајућим редоследом", - "addon.mod_forum.discussionlistsortbylastpostasc": "Сортирајте по датуму креирања последње поруке растућим редоследом", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Сортирајте по датуму креирања последње поруке опадајућим редоследом", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Сортирајте по броју одговора растућим редоследом", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Сортирајте по броју одговора опадајућим редоследом", - "addon.mod_forum.discussionlocked": "Ова дискусија је закључана тако да више не можете одговарати на њој.", - "addon.mod_forum.discussionpinned": "Фиксирана", - "addon.mod_forum.discussionsubscription": "Претплата на дискусију", - "addon.mod_forum.edit": "Уреди", - "addon.mod_forum.erroremptymessage": "Тело поруке не може бити празно", - "addon.mod_forum.erroremptysubject": "Тема (наслов) поруке не може бити празна", - "addon.mod_forum.errorgetforum": "Грешка приликом преузимања података за 'Форум'", - "addon.mod_forum.errorgetgroups": "Грешка приликом преузимања подешавања група.", - "addon.mod_forum.favouriteupdated": "Ваша опција за означавање звездицом је ажурирана.", - "addon.mod_forum.forumnodiscussionsyet": "Још нема тема за дискусију на овом форуму.", - "addon.mod_forum.group": "Група", - "addon.mod_forum.lastpost": "Последња порука", - "addon.mod_forum.lockdiscussion": "Закључај ову дискусију", - "addon.mod_forum.lockupdated": "Опција за закључавања је ажурирана.", - "addon.mod_forum.message": "Порука", - "addon.mod_forum.modeflatnewestfirst": "Приказ одговора, почевши прво с најновијим", - "addon.mod_forum.modeflatoldestfirst": "Приказ одговора, почевши прво с најстаријим", - "addon.mod_forum.modenested": "Приказ одговора у угнежђеној форми", - "addon.mod_forum.modulenameplural": "Форуми", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} дискусије/а", - "addon.mod_forum.numreplies": "{{numreplies}} одговора", - "addon.mod_forum.pindiscussion": "Фиксирај ову дискусију", - "addon.mod_forum.pinupdated": "Опција за фиксирање је ажурирана.", - "addon.mod_forum.postisprivatereply": "Ово је приватни одговор. Није видљив другим учесницима.", - "addon.mod_forum.posttoforum": "Пошаљи поруку на форум", - "addon.mod_forum.posttomygroups": "Објави копију свим групама", - "addon.mod_forum.privatereply": "Одговори приватно", - "addon.mod_forum.re": "Одговор:", - "addon.mod_forum.refreshdiscussions": "Освежи дискусије", - "addon.mod_forum.refreshposts": "Освежи постове", - "addon.mod_forum.removefromfavourites": "Уклоните звездицу са ове дискусије", - "addon.mod_forum.reply": "Одговори", - "addon.mod_forum.replyplaceholder": "Напишите одговор ...", - "addon.mod_forum.subject": "Тема", - "addon.mod_forum.tagarea_forum_posts": "Постови на форуму", - "addon.mod_forum.thisforumhasduedate": "Крајњи рок за слање порука на овај форум је {{$a}}.", - "addon.mod_forum.thisforumisdue": "Крајњи рок за слање порука на овај форум био је {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Откључај ову дискусију", - "addon.mod_forum.unpindiscussion": "Уклони фиксирање са ова дискусије", - "addon.mod_forum.unread": "Непрочитано", - "addon.mod_forum.unreadpostsnumber": "Број непрочитаних порука: {{$a}}", - "addon.mod_forum.yourreply": "Ваш одговор", - "addon.mod_glossary.addentry": "Додај нови појам", - "addon.mod_glossary.aliases": "Кључне речи", - "addon.mod_glossary.attachment": "Прилог", - "addon.mod_glossary.browsemode": "Прегледај појмове", - "addon.mod_glossary.byalphabet": "Азбучним редом", - "addon.mod_glossary.byauthor": "Групиши по аутору", - "addon.mod_glossary.bycategory": "Групиши по категорији", - "addon.mod_glossary.bynewestfirst": "Најновији прво", - "addon.mod_glossary.byrecentlyupdated": "Недавно ажурирани", - "addon.mod_glossary.bysearch": "Претражи", - "addon.mod_glossary.cannoteditentry": "Не можете да уређујете појам", - "addon.mod_glossary.casesensitive": "Уз разликовање малих и великих слова", - "addon.mod_glossary.categories": "Категорије", - "addon.mod_glossary.concept": "Појам", - "addon.mod_glossary.definition": "Дефиниција", - "addon.mod_glossary.entriestobesynced": "Појмови за синхронизацију", - "addon.mod_glossary.entrypendingapproval": "Овај појам чека одобрење.", - "addon.mod_glossary.entryusedynalink": "Овај појам треба да буде аутоматски линкован", - "addon.mod_glossary.errconceptalreadyexists": "Овај појам већ постоји. Дупликати нису дозвољени у овом речнику.", - "addon.mod_glossary.errorloadingentries": "Дошло је до грешке приликом учитавања појмова.", - "addon.mod_glossary.errorloadingentry": "Дошло је до грешке приликом учитавања појма.", - "addon.mod_glossary.errorloadingglossary": "Дошло је до грешке приликом учитавања речника.", - "addon.mod_glossary.fillfields": "Појам и дефиниција су обавезна поља.", - "addon.mod_glossary.fullmatch": "Ако се подударају искључиво целе речи", - "addon.mod_glossary.linking": "Аутоматско повезивање", - "addon.mod_glossary.modulenameplural": "Речници", - "addon.mod_glossary.noentriesfound": "Није пронађен ниједан појам.", - "addon.mod_glossary.searchquery": "Упит за претрагу", - "addon.mod_glossary.tagarea_glossary_entries": "Појмови у речнику", - "addon.mod_imscp.deploymenterror": "Грешка приликом учитавања пакета!", - "addon.mod_imscp.modulenameplural": "IMS пакети", - "addon.mod_imscp.showmoduledescription": "Прикажи опис", - "addon.mod_imscp.toc": "Садржај", - "addon.mod_lesson.answer": "Одговор", - "addon.mod_lesson.attempt": "Покушај: {{$a}}", - "addon.mod_lesson.attemptheader": "Покушај", - "addon.mod_lesson.attemptsremaining": "Број покушаја који вам је преостао: {{$a}}", - "addon.mod_lesson.averagescore": "Просечан број бодова", - "addon.mod_lesson.averagetime": "Просечно време", - "addon.mod_lesson.branchtable": "Садржај", - "addon.mod_lesson.cannotfindattempt": "Грешка: није пронађен покушај", - "addon.mod_lesson.cannotfinduser": "Грешка: нису пронађени слогови у табели lesson_timer", - "addon.mod_lesson.clusterjump": "Још неприказано питање из групе питања", - "addon.mod_lesson.completed": "Завршено", - "addon.mod_lesson.congratulations": "Честитамо - стигли сте до краја лекције", - "addon.mod_lesson.continue": "Настави", - "addon.mod_lesson.continuetonextpage": "Идите на следећу страницу", - "addon.mod_lesson.defaultessayresponse": "Предавач ће оценити ваш есеј.", - "addon.mod_lesson.detailedstats": "Детаљна статистика", - "addon.mod_lesson.didnotanswerquestion": "Нисте одговорили на ово питање.", - "addon.mod_lesson.displayofgrade": "Приказ оцена (само за полазнике)", - "addon.mod_lesson.displayscorewithessays": "

                      Освојили сте {{$a.score}} од максималних {{$a.tempmaxgrade}} бодова за питања која се аутоматски оцењују.

                      \n

                      Ваш {{$a.essayquestions}} одговор на питања у форми есеја биће ускоро прегледан и оцењен, а оцена ће касније бити додата
                      у ваш финални резултат.

                      \n

                      Ваша тренутна оцена без есеја је {{$a.score}} од {{$a.grade}}

                      ", - "addon.mod_lesson.displayscorewithoutessays": "Ваш резултат је {{$a.score}} (од могућих {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Поље за лозинку не може бити празно", - "addon.mod_lesson.enterpassword": "Молимо унесите лозинку:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Нисте одговорили ни на једно питање. Ваша оцена за ову лекцију је 0.", - "addon.mod_lesson.errorprefetchrandombranch": "Ова лекција садржи прелаз на наусимично одабрану страницу са садржајем, па је није могуће урадити у апликацији док се не покрене на веб сајту.", - "addon.mod_lesson.errorreviewretakenotlast": "Ова покушај не може више бити прегледан зато што је завршен други покушај.", - "addon.mod_lesson.finish": "Заврши", - "addon.mod_lesson.finishretakeoffline": "Овај покушај је завршен у офлајн режиму.", - "addon.mod_lesson.firstwrong": "Погрешно сте одговорили на питање. Да ли желите да покушате поново? (ако сада тачно одговорите на питање, то се неће рачунати у ваш коначан резултат.)", - "addon.mod_lesson.gotoendoflesson": "Иди на крај лекције", - "addon.mod_lesson.grade": "Оцена", - "addon.mod_lesson.highscore": "Најбољи резултат", - "addon.mod_lesson.hightime": "Најдуже време", - "addon.mod_lesson.leftduringtimed": "Напустили сте лекцију која је временски ограничена.
                      Притисните тастер за наставак да бисте погледали лекцију од почетка.", - "addon.mod_lesson.leftduringtimednoretake": "Напустили сте лекцију која је временски ограничена и није Вам
                      дозвољено да наставите или почнете лекцију из почетка.", - "addon.mod_lesson.lessonmenu": "Мени лекције", - "addon.mod_lesson.lessonstats": "Статистика лекције", - "addon.mod_lesson.linkedmedia": "Повезани медији", - "addon.mod_lesson.loginfail": "Погрешна пријава, молимо покушајте поново...", - "addon.mod_lesson.lowscore": "Наслабији резултат", - "addon.mod_lesson.lowtime": "Најкраће време", - "addon.mod_lesson.maximumnumberofattemptsreached": "Достигнут је максималан број покушаја - прелази се на следећу страницу", - "addon.mod_lesson.modattemptsnoteacher": "Полазнички преглед функционише само за полазнике.", - "addon.mod_lesson.modulenameplural": "Лекције", - "addon.mod_lesson.noanswer": "На једно или више питања није дат одговор. Молимо вратите се назад и дајте свој одговор.", - "addon.mod_lesson.nolessonattempts": "Није било покушаја да се прође кроз ову лекцију.", - "addon.mod_lesson.nolessonattemptsgroup": "Није било покушаја да се прође кроз ову лекцију од стране чланова групе {{$a}}.", - "addon.mod_lesson.notcompleted": "Није завршено", - "addon.mod_lesson.numberofcorrectanswers": "Број тачних одговора: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Број прегледаних страница: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Број питања на које је дат одговор: {{$a.nquestions}} (Требало би их бити бар {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "До сада сте освојили {{$a.score}} од максимално {{$a.currenthigh}} бодова.", - "addon.mod_lesson.ongoingnormal": "Тачно сте одговорили на {{$a.correct}} од {{$a.viewed}} питања која сте видели.", - "addon.mod_lesson.or": "ИЛИ", - "addon.mod_lesson.overview": "Преглед", - "addon.mod_lesson.preview": "Преглед", - "addon.mod_lesson.progressbarteacherwarning2": "Нећете видети траку напредовања зато што можете да уређујете ову лекцију", - "addon.mod_lesson.progresscompleted": "Завршили сте {{$a}}% лекције", - "addon.mod_lesson.question": "Питање", - "addon.mod_lesson.rawgrade": "Необрађена оцена", - "addon.mod_lesson.reports": "Извештаји", - "addon.mod_lesson.response": "Повратне информације", - "addon.mod_lesson.retakefinishedinsync": "Офлајн покушај је синхронизован. Да ли желите да га прегледате?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Преглед", - "addon.mod_lesson.reviewlesson": "Преглед лекције", - "addon.mod_lesson.reviewquestionback": "Да, волео/ла бих да поново покушам", - "addon.mod_lesson.reviewquestioncontinue": "Не, желим да пређем на следеће питање", - "addon.mod_lesson.secondpluswrong": "Није баш. Да ли желите да покушате поново?", - "addon.mod_lesson.submit": "Пошаљи", - "addon.mod_lesson.teacherjumpwarning": "У овој лекцији се користе {{$a.cluster}} или {{$a.unseen}} прелаз између страница. Прелаз \"Следећа страница\" ће се користити уместо њих током овог приказа. За тестирање ових прелаза пријавите се на систем у улози полазника.", - "addon.mod_lesson.teacherongoingwarning": "Тренутни резултат током прегледа лекције се приказује само полазницима. Пријавите се на систем као полазник како бисте тестирали ову могућност.", - "addon.mod_lesson.teachertimerwarning": "Мерач времена могу да виде само полазници. Како бисте тестирали мерач времена пријавите се на систем у улози полазника.", - "addon.mod_lesson.thatsthecorrectanswer": "Тачан одговор", - "addon.mod_lesson.thatsthewronganswer": "Погрешан одговор", - "addon.mod_lesson.timeremaining": "Преостало време", - "addon.mod_lesson.timetaken": "Потрошено време", - "addon.mod_lesson.unseenpageinbranch": "Још неприказано питање унутар странице са садржајем", - "addon.mod_lesson.warningretakefinished": "Покушај је завршен на веб сајту.", - "addon.mod_lesson.welldone": "Браво!", - "addon.mod_lesson.youhaveseen": "Већ сте видели више од једне странице ове лекције.
                      Желите ли почети од последње странице коју сте видели?", - "addon.mod_lesson.youranswer": "Ваш одговор", - "addon.mod_lesson.yourcurrentgradeisoutof": "Ваша тренутна оцена је {{$a.grade}} од могућих {{$a.total}}", - "addon.mod_lesson.youshouldview": "Требало би да одговорите на барем: {{$a}}", - "addon.mod_lti.errorgetlti": "Грешка приликом преузимања података модула.", - "addon.mod_lti.errorinvalidlaunchurl": "Иницијална URL адреса није исправна.", - "addon.mod_lti.launchactivity": "Покрени активност", - "addon.mod_lti.modulenameplural": "Екстерни алати", - "addon.mod_page.errorwhileloadingthepage": "Грешка приликом учитавања садржаја странице.", - "addon.mod_page.modulenameplural": "Странице", - "addon.mod_quiz.answercolon": "Одговор:", - "addon.mod_quiz.attemptfirst": "Први покушај", - "addon.mod_quiz.attemptlast": "Последњи покушај", - "addon.mod_quiz.attemptnumber": "Покушај", - "addon.mod_quiz.attemptquiznow": "Започни тест", - "addon.mod_quiz.attemptstate": "Статус", - "addon.mod_quiz.cannotsubmitquizdueto": "Овај покушај решавања теста не може да буде предат због следећих разлога:", - "addon.mod_quiz.clearchoice": "Обриши мој одговор", - "addon.mod_quiz.comment": "Коментар", - "addon.mod_quiz.completedon": "Завршено дана", - "addon.mod_quiz.confirmclose": "Када предате тест нећете више бити у могућности да мењате своје одговоре.", - "addon.mod_quiz.confirmcontinueoffline": "Овај покушај није синхронизован од {{$a}}. Ако сте у међувремену овај покушај наставили на неком другом уређају, постоји могућност да сте изгубили податке.", - "addon.mod_quiz.confirmleavequizonerror": "Дошло је до грешке приликом покушаја да се сачувају одговори. Да ли сте сигурни да желите да напустите тест?", - "addon.mod_quiz.confirmstart": "Тест има временско ограничење ({{$a}}). Време ће се одбројавати од момента почетка покушаја и морате предати своје одговоре пре него што истекне. Да ли сте сигурни да сада желите да започнете решавање теста?", - "addon.mod_quiz.confirmstartheader": "Временски ограничен тест", - "addon.mod_quiz.connectionerror": "Мрежна веза је изгубљена. (Аутоматско чување није успело).\n\nЗапишите све одговора унетих на овој страници у последњих неколико минута, а затим покушајте поново да се повежете.\n\nКада се веза поново успостави ваши одговори би требало да буду сачувани а ова порука ће нестати.", - "addon.mod_quiz.continueattemptquiz": "Наставите последњи покушај", - "addon.mod_quiz.continuepreview": "Настави последњи преглед", - "addon.mod_quiz.errorbehaviournotsupported": "Овај тест не можете решавати у апликацији зато што она не подржава понашање питања:", - "addon.mod_quiz.errordownloading": "Грешка приликом преузимања неопходних података.", - "addon.mod_quiz.errorgetattempt": "Грешка приликом преузимања података о покушају решавања теста.", - "addon.mod_quiz.errorgetquestions": "Грешка приликом преузимања питања.", - "addon.mod_quiz.errorgetquiz": "Грешка приликом преузимања података о тесту.", - "addon.mod_quiz.errorparsequestions": "Дошло је до грешке приликом учитавања питања. Молимо, покушајте да решите овај тест у веб читачу.", - "addon.mod_quiz.errorquestionsnotsupported": "Овај тест се не може решавати у апликацији зато што садржи питања које апликација не подржава:", - "addon.mod_quiz.errorrulesnotsupported": "Овај тест се не може решавати у апликацији зато што садржи правила за приступ које апликација не подржава:", - "addon.mod_quiz.errorsaveattempt": "Дошло је до грешке приликом снимања података о покушају решавања теста.", - "addon.mod_quiz.feedback": "Повратне информације", - "addon.mod_quiz.finishattemptdots": "Заврши покушај...", - "addon.mod_quiz.finishnotsynced": "Тест је завршен, али није синхронизован", - "addon.mod_quiz.grade": "Оцена", - "addon.mod_quiz.gradeaverage": "Просечна оцена", - "addon.mod_quiz.gradehighest": "Највиша оцена", - "addon.mod_quiz.grademethod": "Метод оцењивања", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Оцене", - "addon.mod_quiz.modulenameplural": "Тестови", - "addon.mod_quiz.mustbesubmittedby": "Овај покушај мора бити предат до {{$a}}.", - "addon.mod_quiz.noquestions": "Ниједно питање још није додато", - "addon.mod_quiz.noreviewattempt": "Није Вам дозвољено да прегледате овај покушај", - "addon.mod_quiz.notyetgraded": "Још није оцењено", - "addon.mod_quiz.opentoc": "Отвори навигациони мени.", - "addon.mod_quiz.outof": "{{$a.grade}} од {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} од {{$a.maxgrade}}", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Свеобухватне повратне информације", - "addon.mod_quiz.overdue": "Закаснели", - "addon.mod_quiz.overduemustbesubmittedby": "Време за овај покушај решавање теста је истекло. Покушај је већ требало предати. Ако желите да овај тест буде оцењен морате га предати до {{$a}}. Ако то не урадите до наведеног рока, оцене за овај покушај се неће рачунати.", - "addon.mod_quiz.preview": "Преглед", - "addon.mod_quiz.previewquiznow": "Прегледај тест сада", - "addon.mod_quiz.question": "Питање", - "addon.mod_quiz.quiznavigation": "Навигација теста", - "addon.mod_quiz.quizpassword": "Лозинка теста", - "addon.mod_quiz.reattemptquiz": "Поновни покушај решавања теста", - "addon.mod_quiz.requirepasswordmessage": "Да бисте приступили решавању овог теста морате знати лозинку теста", - "addon.mod_quiz.returnattempt": "Повратак на покушај", - "addon.mod_quiz.review": "Преглед", - "addon.mod_quiz.reviewofattempt": "Преглед покушаја {{$a}}", - "addon.mod_quiz.reviewofpreview": "Преглед приказа", - "addon.mod_quiz.showall": "Прикажи сва питања на једној страници", - "addon.mod_quiz.showeachpage": "Прикажи једну по једну страницу", - "addon.mod_quiz.startattempt": "Започни покушај решавања", - "addon.mod_quiz.startedon": "Започето", - "addon.mod_quiz.stateabandoned": "Никад предато", - "addon.mod_quiz.statefinished": "Завршено", - "addon.mod_quiz.statefinisheddetails": "Предато {{$a}}", - "addon.mod_quiz.stateinprogress": "У току", - "addon.mod_quiz.stateoverdue": "Закаснела предаја", - "addon.mod_quiz.stateoverduedetails": "Мора се предати до {{$a}}", - "addon.mod_quiz.status": "Статус", - "addon.mod_quiz.submitallandfinish": "Предај све одговоре и заврши тест", - "addon.mod_quiz.summaryofattempt": "Резиме покушаја", - "addon.mod_quiz.summaryofattempts": "Резиме ваших претходних покушаја", - "addon.mod_quiz.timeleft": "Преостало време", - "addon.mod_quiz.timetaken": "Утрошено време", - "addon.mod_quiz.warningattemptfinished": "Офлајн покушај је одбачен зато што је или завршен на сајту или није пронађен.", - "addon.mod_quiz.warningdatadiscarded": "Неки офлајн одговори су одбачени зато што су питања измењена онлајн.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Покушај решавања теста је није завршен зато што су неки офлајн одговори одбачени. Прегледајте своје одговоре, а затим поново пошаљите свој покушај.", - "addon.mod_quiz.yourfinalgradeis": "Ваша коначна оцена на овом тесту је {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Грешка приликом учитавања садржаја.", - "addon.mod_resource.modifieddate": "Измењено {{$a}}", - "addon.mod_resource.modulenameplural": "Датотеке", - "addon.mod_resource.openthefile": "Отвори датотеку", - "addon.mod_resource.uploadeddate": "Постављено {{$a}}", - "addon.mod_scorm.asset": "Елемент", - "addon.mod_scorm.assetlaunched": "Елемент - Прегледано", - "addon.mod_scorm.attempts": "Покушаји", - "addon.mod_scorm.averageattempt": "Просечни број покушаја", - "addon.mod_scorm.browse": "Преглед", - "addon.mod_scorm.browsed": "Претражено", - "addon.mod_scorm.browsemode": "Режим прегледа", - "addon.mod_scorm.cannotcalculategrade": "Оцена не може да се израчуна.", - "addon.mod_scorm.completed": "Завршено", - "addon.mod_scorm.contents": "Садржај", - "addon.mod_scorm.dataattemptshown": "Ови подаци припадају покушају број {{number}}.", - "addon.mod_scorm.enter": "Уђи", - "addon.mod_scorm.errorcreateofflineattempt": "Дошло је до грешке приликом покушаја креирања новог офлајн покушаја. Молимо, покушајте поново.", - "addon.mod_scorm.errordownloadscorm": "Грешка приликом преузимања SCORM пакета: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Грешка приликом преузимања података SCORM пакета.", - "addon.mod_scorm.errorinvalidversion": "Извините, апликација подржава само SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Преузимање SCORM пакета је онемогућено на вашем сајту. Молимо, обратите се администратору вашег Moodle сајта.", - "addon.mod_scorm.errornovalidsco": "Овај SCORM пакет нема видљив SCO који би био учитан.", - "addon.mod_scorm.errorpackagefile": "Извините, апликација подржава само ZIP архиве.", - "addon.mod_scorm.errorsyncscorm": "Дошло је до грешке приликом синхронизације. Молимо, покушајте поново.", - "addon.mod_scorm.exceededmaxattempts": "Достигли сте максималан број покушаја", - "addon.mod_scorm.failed": "Неположен", - "addon.mod_scorm.firstattempt": "Први покушај", - "addon.mod_scorm.gradeaverage": "Просечна оцена", - "addon.mod_scorm.gradeforattempt": "Оцена за покушај", - "addon.mod_scorm.gradehighest": "Највиша оцена", - "addon.mod_scorm.grademethod": "Метод оцењивања", - "addon.mod_scorm.gradereported": "Извештај о оценама", - "addon.mod_scorm.gradescoes": "Објекти за учење", - "addon.mod_scorm.gradesum": "Коначна оцена", - "addon.mod_scorm.highestattempt": "Најбољи покушај", - "addon.mod_scorm.incomplete": "Непотпуно", - "addon.mod_scorm.lastattempt": "Последњи завршени покушај", - "addon.mod_scorm.modulenameplural": "SCORM пакети", - "addon.mod_scorm.newattempt": "Почни нови покушај", - "addon.mod_scorm.noattemptsallowed": "Број дозвољених покушаја", - "addon.mod_scorm.noattemptsmade": "Број покушаја који сте имали", - "addon.mod_scorm.notattempted": "Није покушавано", - "addon.mod_scorm.offlineattemptnote": "Овај покушај има податке који нису синхронизован.", - "addon.mod_scorm.offlineattemptovermax": "Овај покушај не може бити послат зато што сте премашили максималан број дозвољених покушаја.", - "addon.mod_scorm.organizations": "Организације", - "addon.mod_scorm.passed": "Положено", - "addon.mod_scorm.reviewmode": "Режим прегледа", - "addon.mod_scorm.score": "Резултат", - "addon.mod_scorm.scormstatusnotdownloaded": "Овај SCORM пакет није преузет. Биће аутоматски преузет када га отворите.", - "addon.mod_scorm.scormstatusoutdated": "Овај SCORM пакет је мењан од последњег преузимања. Биће аутоматски преузет када га отворите.", - "addon.mod_scorm.suspended": "Суспендовано", - "addon.mod_scorm.toc": "Садржај", - "addon.mod_scorm.warningofflinedatadeleted": "Неки офлајн подаци о покушају {{number}} су обрисани зато што их није могуће креирати у новом покушају.", - "addon.mod_scorm.warningsynconlineincomplete": "Неки покушаји нису могли бити синхронизовани са сајтом зато што последњи онлине покушај није завршен. Молимо вас да прво завршите онлајн покушај.", - "addon.mod_survey.cannotsubmitsurvey": "Нажалост, било је проблема са предајом вашег упитника. Молим вас, покушајте поново.", - "addon.mod_survey.errorgetsurvey": "Грешка приликом преузимања података за 'Упитник' (Survey)", - "addon.mod_survey.ifoundthat": "Открио/ла сам да", - "addon.mod_survey.ipreferthat": "Преферирам", - "addon.mod_survey.modulenameplural": "Упитници (Surveys)", - "addon.mod_survey.responses": "Одговори", - "addon.mod_survey.results": "Резултати", - "addon.mod_survey.surveycompletednograph": "Испунили сте анкету.", - "addon.mod_url.accessurl": "Приступи URL адреси", - "addon.mod_url.modulenameplural": "URL адресе", - "addon.mod_url.pointingtourl": "URL адреса са којом је овај ресурс повезан", - "addon.mod_wiki.cannoteditpage": "Не можете да уређујете ову страницу.", - "addon.mod_wiki.createpage": "Креирај страницу", - "addon.mod_wiki.editingpage": "Уређивање ове странице '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Дошло је до грешке приликом учитавања странице.", - "addon.mod_wiki.errornowikiavailable": "Овај вики још увек нема садржај.", - "addon.mod_wiki.gowikihome": "Иди на почетну страницу викија", - "addon.mod_wiki.map": "Мапа", - "addon.mod_wiki.modulenameplural": "Wikiji", - "addon.mod_wiki.newpagehdr": "Нова страница", - "addon.mod_wiki.newpagetitle": "Наслов нове странице", - "addon.mod_wiki.nocontent": "На овој страници нема садржаја", - "addon.mod_wiki.notingroup": "Није у групи", - "addon.mod_wiki.pageexists": "Ова страница већ постоји.", - "addon.mod_wiki.pagename": "Назив странице", - "addon.mod_wiki.subwiki": "Подвики", - "addon.mod_wiki.tagarea_wiki_pages": "Вики странице", - "addon.mod_wiki.titleshouldnotbeempty": "Наслов не би требало да буде празан", - "addon.mod_wiki.viewpage": "Погледај страницу", - "addon.mod_wiki.wikipage": "Вики страница", - "addon.mod_wiki.wrongversionlock": "Други корисник је уређивао ову страницу у исто време када и ви. Ваш садржај је застарео.", - "addon.mod_workshop.alreadygraded": "Већ оцењено", - "addon.mod_workshop.areainstructauthors": "Упутства за предају радова", - "addon.mod_workshop.areainstructreviewers": "Упутства за процену", - "addon.mod_workshop.assess": "Процените", - "addon.mod_workshop.assessedsubmission": "Процењен предати рад", - "addon.mod_workshop.assessmentform": "Образац за процену", - "addon.mod_workshop.assessmentsettings": "Подешавања процене", - "addon.mod_workshop.assessmentstrategynotsupported": "Стратегија процене {{$a}} није подржана", - "addon.mod_workshop.assessmentweight": "Пондер процене", - "addon.mod_workshop.assignedassessments": "Предати радови додељени за процену", - "addon.mod_workshop.assignedassessmentsnone": "Није вам додељен ниједан рад за процену", - "addon.mod_workshop.conclusion": "Закључак", - "addon.mod_workshop.createsubmission": "Започните припрему за предају свог рада", - "addon.mod_workshop.deletesubmission": "Обриши предати рад", - "addon.mod_workshop.editsubmission": "Уреди предати рад", - "addon.mod_workshop.feedbackauthor": "Повратне информације за аутора", - "addon.mod_workshop.feedbackby": "Повратне информације које је дао/ла {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Повратне информације за рецензента", - "addon.mod_workshop.givengrades": "Дате оцене", - "addon.mod_workshop.gradecalculated": "Израчуната оцена за предати рад", - "addon.mod_workshop.gradeinfo": "Оцена: {{$a.received}} од {{$a.max}}", - "addon.mod_workshop.gradeover": "Промени претходну оцену за предати рад", - "addon.mod_workshop.gradesreport": "Извештај о оценама радионице", - "addon.mod_workshop.gradinggrade": "Оцена за евалуацију", - "addon.mod_workshop.gradinggradecalculated": "Израчуната оцена за обављену процену", - "addon.mod_workshop.gradinggradeof": "Оцена за евалуацију (од {{$a}})", - "addon.mod_workshop.gradinggradeover": "Измени оцену за обављену процену", - "addon.mod_workshop.modulenameplural": "Радионице", - "addon.mod_workshop.nogradeyet": "Још нема оцена", - "addon.mod_workshop.notassessed": "Рад још није процењен", - "addon.mod_workshop.notoverridden": "Није поништено", - "addon.mod_workshop.noyoursubmission": "Још нисте предали свој рад", - "addon.mod_workshop.overallfeedback": "Свеобухватне повратне информација", - "addon.mod_workshop.publishedsubmissions": "Објављени радови", - "addon.mod_workshop.publishsubmission": "Објави рад", - "addon.mod_workshop.publishsubmission_help": "Објављени радови су доступни другим корисницима након затварања радионице", - "addon.mod_workshop.reassess": "Поновно процењивање", - "addon.mod_workshop.receivedgrades": "Добијене оцене", - "addon.mod_workshop.submissionattachment": "Прилог", - "addon.mod_workshop.submissioncontent": "Садржај предатог рада", - "addon.mod_workshop.submissiondeleteconfirm": "Да ли сте сигурни да желите да обришете следећи предати рад?", - "addon.mod_workshop.submissiongrade": "Оцена предатог рада", - "addon.mod_workshop.submissiongradeof": "Оцена за предати рад (од {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Морате унети текст или додати датотеку.", - "addon.mod_workshop.submissionrequiredtitle": "Морате да унесете наслов.", - "addon.mod_workshop.submissionsreport": "Извештај о предатим радовима", - "addon.mod_workshop.submissiontitle": "Наслов", - "addon.mod_workshop.switchphase10": "Пребаци на фазу подешавања", - "addon.mod_workshop.switchphase20": "Пребаци на фазу предаје рада", - "addon.mod_workshop.switchphase30": "Пребаци на фазу процене", - "addon.mod_workshop.switchphase40": "Пребаци на фазу евалуације", - "addon.mod_workshop.switchphase50": "Затвори радионицу", - "addon.mod_workshop.userplan": "Планер радионице", - "addon.mod_workshop.userplancurrentphase": "Тренутна фаза", - "addon.mod_workshop.warningassessmentmodified": "Предати рад је измењен на сајту.", - "addon.mod_workshop.warningsubmissionmodified": "Процена је измењена на сајту.", - "addon.mod_workshop.weightinfo": "Пондер: {{$a}}", - "addon.mod_workshop.yourassessment": "Ваша процена", - "addon.mod_workshop.yourassessmentfor": "Ваша процена за {{$a}}", - "addon.mod_workshop.yourgrades": "Ваше оцене", - "addon.mod_workshop.yoursubmission": "Ваш рад", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Коментар за {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Оцена за {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Аспект {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Морате да одаберете оцену за овај аспект", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Коментар за {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Аспект {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Коментар за {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Оцена за {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Предуслов {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Критеријум {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Морате да изаберете једну од ових ставки", - "addon.notes.addnewnote": "Додај белешку", - "addon.notes.coursenotes": "Белешке курса", - "addon.notes.deleteconfirm": "Обриши белешку", - "addon.notes.eventnotecreated": "Белешка креирана", - "addon.notes.eventnotedeleted": "Белешка обрисана", - "addon.notes.nonotes": "Још нема белешки овог типа", - "addon.notes.note": "Белешка", - "addon.notes.notes": "Белешке", - "addon.notes.personalnotes": "Личне поруке", - "addon.notes.publishstate": "Контекст", - "addon.notes.sitenotes": "Белешке са сајта", - "addon.notes.userwithid": "Корисник са ID ознаком {{id}}", - "addon.notes.warningnotenotsent": "Није могуће додати белешку/е курсу {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Грешка приликом преузимања обавештења", - "addon.notifications.markallread": "Означи све као прочитано", - "addon.notifications.notificationpreferences": "Параметри обавештења", - "addon.notifications.notifications": "Обавештења", - "addon.notifications.playsound": "Репродукуј звук", - "addon.notifications.therearentnotificationsyet": "Нема обавештења", - "assets.countries.AD": "Андора", - "assets.countries.AE": "Уједињени Арапски Емирати", - "assets.countries.AF": "Афганистан", - "assets.countries.AG": "Антигва и Барбуда", - "assets.countries.AI": "Ангвила", - "assets.countries.AL": "Албанија", - "assets.countries.AM": "Јерменија", - "assets.countries.AO": "Ангола", - "assets.countries.AQ": "Антарктик", - "assets.countries.AR": "Аргентина", - "assets.countries.AS": "Америчка Самоа", - "assets.countries.AT": "Аустрија", - "assets.countries.AU": "Аустралија", - "assets.countries.AW": "Аруба", - "assets.countries.AX": "Оландска Острва", - "assets.countries.AZ": "Азербејџан", - "assets.countries.BA": "Босна и Херцеговина", - "assets.countries.BB": "Барбадос", - "assets.countries.BD": "Бангладеш", - "assets.countries.BE": "Белгија", - "assets.countries.BF": "Буркина Фасо", - "assets.countries.BG": "Бугарска", - "assets.countries.BH": "Бахреин", - "assets.countries.BI": "Бурунди", - "assets.countries.BJ": "Бенин", - "assets.countries.BL": "Острво Свети Бартоломеј", - "assets.countries.BM": "Бермуда", - "assets.countries.BN": "Брунеј", - "assets.countries.BO": "Боливија, Вишенационална Држава", - "assets.countries.BQ": "Бонер, Свети Еустахије и Саба", - "assets.countries.BR": "Бразил", - "assets.countries.BS": "Бахами", - "assets.countries.BT": "Бутан", - "assets.countries.BV": "Буве Острво", - "assets.countries.BW": "Боцвана", - "assets.countries.BY": "Белорусија", - "assets.countries.BZ": "Белизе", - "assets.countries.CA": "Канада", - "assets.countries.CC": "Кокосова Острва", - "assets.countries.CD": "Конго, Демократска Република", - "assets.countries.CF": "Централноафричка Република", - "assets.countries.CG": "Конго", - "assets.countries.CH": "Швајцарска", - "assets.countries.CI": "Обала Слоноваче", - "assets.countries.CK": "Кукова Острва", - "assets.countries.CL": "Чиле", - "assets.countries.CM": "Камерун", - "assets.countries.CN": "Кина", - "assets.countries.CO": "Колумбија", - "assets.countries.CR": "Костарика", - "assets.countries.CU": "Куба", - "assets.countries.CV": "Зеленортска острва", - "assets.countries.CW": "Курасао", - "assets.countries.CX": "Божићна острва", - "assets.countries.CY": "Кипар", - "assets.countries.CZ": "Чешка", - "assets.countries.DE": "Немачка", - "assets.countries.DJ": "Џибути", - "assets.countries.DK": "Данска", - "assets.countries.DM": "Доминка", - "assets.countries.DO": "Доминиканска Република", - "assets.countries.DZ": "Алжир", - "assets.countries.EC": "Еквадор", - "assets.countries.EE": "Естонија", - "assets.countries.EG": "Египат", - "assets.countries.EH": "Западна Сахара", - "assets.countries.ER": "Еритреја", - "assets.countries.ES": "Шпанија", - "assets.countries.ET": "Етиопија", - "assets.countries.FI": "Финска", - "assets.countries.FJ": "Фиџи", - "assets.countries.FK": "Фокландска Острва", - "assets.countries.FM": "Савезне Државе Микронезије", - "assets.countries.FO": "Фарска Острва", - "assets.countries.FR": "Француска", - "assets.countries.GA": "Габон", - "assets.countries.GB": "Велика Британија", - "assets.countries.GD": "Гренада", - "assets.countries.GE": "Грузија", - "assets.countries.GF": "Француска Гвинеја", - "assets.countries.GG": "Гернзи", - "assets.countries.GH": "Гана", - "assets.countries.GI": "Гибралтар", - "assets.countries.GL": "Гренланд", - "assets.countries.GM": "Гамбиа", - "assets.countries.GN": "Гвинеја", - "assets.countries.GP": "Гвадалупе", - "assets.countries.GQ": "Екваторијална Гвинеја", - "assets.countries.GR": "Грчка", - "assets.countries.GS": "Јужна Џорџија и Јужна Сендвичка Острва", - "assets.countries.GT": "Гватемала", - "assets.countries.GU": "Гуам", - "assets.countries.GW": "Гвинеја-Бисао", - "assets.countries.GY": "Гвајана", - "assets.countries.HK": "Хонг Конг", - "assets.countries.HM": "Острвo Херд и архипелаг Макдоналд", - "assets.countries.HN": "Хондурас", - "assets.countries.HR": "Хрватска", - "assets.countries.HT": "Хаити", - "assets.countries.HU": "Мађарска", - "assets.countries.ID": "Индонезија", - "assets.countries.IE": "Ирска", - "assets.countries.IL": "Израел", - "assets.countries.IM": "Острво Ман", - "assets.countries.IN": "Индија", - "assets.countries.IO": "Британска Индијска Океанска Територија", - "assets.countries.IQ": "Ирак", - "assets.countries.IR": "Иран, Исламскa Република", - "assets.countries.IS": "Исланд", - "assets.countries.IT": "Италија", - "assets.countries.JE": "Џерси", - "assets.countries.JM": "Јамајка", - "assets.countries.JO": "Јордан", - "assets.countries.JP": "Јапан", - "assets.countries.KE": "Кенија", - "assets.countries.KG": "Киргистан", - "assets.countries.KH": "Камбоџа", - "assets.countries.KI": "Кирибати", - "assets.countries.KM": "Комори", - "assets.countries.KN": "Свети Кристофор и Невис", - "assets.countries.KP": "Северна Кореја, Демократска Народна Република", - "assets.countries.KR": "Јужна Кореја, Република", - "assets.countries.KW": "Кувајт", - "assets.countries.KY": "Кајманска Острва", - "assets.countries.KZ": "Казахстан", - "assets.countries.LA": "Лаос, Народна Демократска Република", - "assets.countries.LB": "Либан", - "assets.countries.LC": "Света Луција", - "assets.countries.LI": "Лихтенштајн", - "assets.countries.LK": "Шри Ланка", - "assets.countries.LR": "Либерија", - "assets.countries.LS": "Лесото", - "assets.countries.LT": "Литванија", - "assets.countries.LU": "Луксембург", - "assets.countries.LV": "Летонија", - "assets.countries.LY": "Либија", - "assets.countries.MA": "Мароко", - "assets.countries.MC": "Монако", - "assets.countries.MD": "Молдавија, Република", - "assets.countries.ME": "Црна Гора", - "assets.countries.MF": "Свети Мартин (француски део)", - "assets.countries.MG": "Мадагаскар", - "assets.countries.MH": "Маршалска Острва", - "assets.countries.MK": "Северна Македонија", - "assets.countries.ML": "Мали", - "assets.countries.MM": "Мјанмар", - "assets.countries.MN": "Монголија", - "assets.countries.MO": "Макао", - "assets.countries.MP": "Северна Маријанска Острва", - "assets.countries.MQ": "Мартиник", - "assets.countries.MR": "Мауританија", - "assets.countries.MS": "Монтсерат", - "assets.countries.MT": "Малта", - "assets.countries.MU": "Маурицијус", - "assets.countries.MV": "Малдиви", - "assets.countries.MW": "Малави", - "assets.countries.MX": "Мексико", - "assets.countries.MY": "Малезија", - "assets.countries.MZ": "Мозамбик", - "assets.countries.NA": "Намибија", - "assets.countries.NC": "Нова Каледонија", - "assets.countries.NE": "Нигер", - "assets.countries.NF": "Норфолшка Острва", - "assets.countries.NG": "Нигерија", - "assets.countries.NI": "Никарагва", - "assets.countries.NL": "Холандија", - "assets.countries.NO": "Норвешка", - "assets.countries.NP": "Непал", - "assets.countries.NR": "Науру", - "assets.countries.NU": "Ниуе", - "assets.countries.NZ": "Нови Зеланд", - "assets.countries.OM": "Оман", - "assets.countries.PA": "Панама", - "assets.countries.PE": "Перу", - "assets.countries.PF": "Француска Полинезија", - "assets.countries.PG": "Папуа Нова Гвинеја", - "assets.countries.PH": "Филипини", - "assets.countries.PK": "Пакистан", - "assets.countries.PL": "Пољска", - "assets.countries.PM": "Свети Пјер и Микелон", - "assets.countries.PN": "Питкаирн", - "assets.countries.PR": "Порторико", - "assets.countries.PS": "Палестина, Држава", - "assets.countries.PT": "Португалија", - "assets.countries.PW": "Палау", - "assets.countries.PY": "Парагвај", - "assets.countries.QA": "Катар", - "assets.countries.RE": "Реунион", - "assets.countries.RO": "Румунија", - "assets.countries.RS": "Србија", - "assets.countries.RU": "Руска Федерација", - "assets.countries.RW": "Руанда", - "assets.countries.SA": "Саудијска Арабија", - "assets.countries.SB": "Соломонова Острва", - "assets.countries.SC": "Сејшели", - "assets.countries.SD": "Судан", - "assets.countries.SE": "Шведска", - "assets.countries.SG": "Сингапур", - "assets.countries.SH": "Света Јелена, Асенсион и Тристан да Куња", - "assets.countries.SI": "Словенија", - "assets.countries.SJ": "Свалбард и Јан Мајен", - "assets.countries.SK": "Словачка", - "assets.countries.SL": "Сијера Леоне", - "assets.countries.SM": "Сан Марино", - "assets.countries.SN": "Сенегал", - "assets.countries.SO": "Сомалија", - "assets.countries.SR": "Суринам", - "assets.countries.SS": "Јужни Судан", - "assets.countries.ST": "Сао Томе и Принципе", - "assets.countries.SV": "Ел Салвадор", - "assets.countries.SX": "Свети Мартин (холандски део)", - "assets.countries.SY": "Сирија", - "assets.countries.SZ": "Свазиланд", - "assets.countries.TC": "Острва Туркс и Кајкос", - "assets.countries.TD": "Чад", - "assets.countries.TF": "Француске Јужне Територије", - "assets.countries.TG": "Того", - "assets.countries.TH": "Тајланд", - "assets.countries.TJ": "Таџикистан", - "assets.countries.TK": "Токелау", - "assets.countries.TL": "Источни Тимор", - "assets.countries.TM": "Туркменистан", - "assets.countries.TN": "Тунис", - "assets.countries.TO": "Тонга", - "assets.countries.TR": "Турска", - "assets.countries.TT": "Тринидад и Тобаго", - "assets.countries.TV": "Тувалу", - "assets.countries.TW": "Тајван", - "assets.countries.TZ": "Танзанија, Уједињена Република", - "assets.countries.UA": "Украјина", - "assets.countries.UG": "Уганда", - "assets.countries.UM": "Мала Удаљена Острва Сједињених Држава", - "assets.countries.US": "Сједињене Америчке Државе", - "assets.countries.UY": "Уругвај", - "assets.countries.UZ": "Узбекистан", - "assets.countries.VA": "Ватикан", - "assets.countries.VC": "Свети Винсент и Гренадини", - "assets.countries.VE": "Венецуела, Боливарска Република", - "assets.countries.VG": "Британска Девичанска острва", - "assets.countries.VI": "Америчка Девичанска Острва", - "assets.countries.VN": "Вијетнам", - "assets.countries.VU": "Вануату", - "assets.countries.WF": "Валис и Футуна", - "assets.countries.WS": "Самоа", - "assets.countries.YE": "Јемен", - "assets.countries.YT": "Мајоте", - "assets.countries.ZA": "Јужна Африка", - "assets.countries.ZM": "Замбија", - "assets.countries.ZW": "Зимбабве", - "assets.mimetypes.application/epub_zip": "EPUB е-књига", - "assets.mimetypes.application/msword": "Word документ", - "assets.mimetypes.application/pdf": "PDF документ", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle резервна копија", - "assets.mimetypes.application/vnd.ms-excel": "Excel радна табела", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 радна свеска са активираним макро наредбама", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint презентација", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument радна табела", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Шаблон OpenDocument радне табеле", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument текстуални документ", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument текстуални шаблон", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Шаблон OpenDocument веб странице", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 презентација", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 слајд-шоу", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 радна табела", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 шаблон", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 документ", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote презентација", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers радна табела", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages документ", - "assets.mimetypes.application/x-javascript": "JavaScript изворни кôд", - "assets.mimetypes.application/x-mspublisher": "Publisher документ", - "assets.mimetypes.application/x-shockwave-flash": "Flash анимација", - "assets.mimetypes.application/xhtml_xml": "XHTML документ", - "assets.mimetypes.archive": "Архива ({{$a.EXT}})", - "assets.mimetypes.audio": "Аудио датотека ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Датотека", - "assets.mimetypes.group:archive": "Архивиране датотеке", - "assets.mimetypes.group:audio": "Аудио датотеке", - "assets.mimetypes.group:document": "Документи", - "assets.mimetypes.group:html_audio": "Аудио датотеке оригинално подржане од веб читача", - "assets.mimetypes.group:html_track": "HTML WebVTT датотеке", - "assets.mimetypes.group:html_video": "Видео датотеке оригинално подржане од веб читача", - "assets.mimetypes.group:image": "Слике", - "assets.mimetypes.group:presentation": "Презентације", - "assets.mimetypes.group:sourcecode": "Изворни кôд", - "assets.mimetypes.group:spreadsheet": "Радне табеле", - "assets.mimetypes.group:video": "Видео датотеке", - "assets.mimetypes.group:web_audio": "Аудио датотеке које се користе на вебу", - "assets.mimetypes.group:web_file": "Веб датотеке", - "assets.mimetypes.group:web_image": "Датотеке са сликама које се користе на вебу", - "assets.mimetypes.group:web_video": "Видео датотеке које се користе на вебу", - "assets.mimetypes.image": "Слика ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows иконица", - "assets.mimetypes.text/css": "CSS (Каскадни опис стилова)", - "assets.mimetypes.text/csv": "CSV (Вредности раздвојене зарезима)", - "assets.mimetypes.text/html": "HTML документ", - "assets.mimetypes.text/plain": "Текстуална датотека", - "assets.mimetypes.text/rtf": "RTF документ", - "assets.mimetypes.text/vtt": "Web Video Text Track формат", - "assets.mimetypes.video": "Видео датотека ({{$a.EXT}})", - "core.accounts": "Налози", - "core.add": "Додај", - "core.agelocationverification": "Провера старости и места", - "core.ago": "{{$a}} пре", - "core.all": "Све", - "core.allgroups": "Све групе", - "core.allparticipants": "Сви учесници", - "core.answer": "Одговор", - "core.answered": "Одговорено", - "core.areyousure": "Да ли сте сигурни?", - "core.back": "Назад", - "core.block.blocks": "Блокови", - "core.cancel": "Одустани", - "core.cannotconnect": "Није могуће успоставити везу. Проверите да ли сте унели исправну URL адресу и да ли ваш сајт користи Moodle {{$a}} или новију верзију.", - "core.cannotdownloadfiles": "Преузимање датотека је онемогућено у подешавањима вашег мобилног сервиса. Обратите се администратору сајта.", - "core.captureaudio": "Сними аудио", - "core.capturedimage": "Снимљена слика", - "core.captureimage": "Усликај", - "core.capturevideo": "Сними видео", - "core.category": "Категорија", - "core.choose": "Изабери", - "core.choosedots": "Изабери...", - "core.clearsearch": "Обриши претрагу", - "core.clicktohideshow": "Кликните да бисте раширили или скупили", - "core.clicktoseefull": "Кликните да бисте видели комплетан садржај.", - "core.close": "Затвори", - "core.comments": "Коментари", - "core.comments.addcomment": "Додај коментар...", - "core.comments.comments": "Коментари", - "core.comments.commentscount": "Коментари ({{$a}})", - "core.comments.deletecommentbyon": "Обриши коментар који је послао/ла {{$a.user}} у {{$a.time}}", - "core.comments.eventcommentcreated": "Коментар креиран", - "core.comments.eventcommentdeleted": "Коментар обрисан", - "core.comments.nocomments": "Нема коментара", - "core.comments.savecomment": "Сачувај коментар", - "core.commentscount": "Коментари ({{$a}})", - "core.completion-alt-auto-fail": "Завршено: {{$a}} (није постигао/ла прелазну оцену)", - "core.completion-alt-auto-n": "Није завршено: {{$a}}", - "core.completion-alt-auto-n-override": "Није завршено: {{$a.modname}} (подесио/ла {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Завршено: {{$a}} (постигао/ла прелазну оцену)", - "core.completion-alt-auto-y": "Завршено: {{$a}}", - "core.completion-alt-auto-y-override": "Завршено: {{$a.modname}} (подесио/ла {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Није завршено: {{$a}}. Изабери да би означио као завршено", - "core.completion-alt-manual-n-override": "Није завршено: {{$a.modname}} (подесио/ла {{$a.overrideuser}}). Изаберите да бисте означили као завршено.", - "core.completion-alt-manual-y": "Завршено: {{$a}}. Изабери да би означио као незавршено.", - "core.completion-alt-manual-y-override": "Завршено: {{$a.modname}} (подесио/ла {{$a.overrideuser}}). Изаберите да бисте означили као незавршено.", - "core.confirmcanceledit": "Да ли сте сигурни да желите да напустите ову страницу? Све промене ће бити изгубљене.", - "core.confirmdeletefile": "Да ли сте сигурни да желите да обришете ову датотетеку?", - "core.confirmloss": "Да ли сте сигурни? Све промене ће бити изгубљене.", - "core.confirmopeninbrowser": "Да ли желите да отворите у веб читачу?", - "core.considereddigitalminor": "Превише сте млади да бисте могли да креирате налог на овом сајту.", - "core.content": "Садржај", - "core.contenteditingsynced": "Садржај који уређујете је синхронизован.", - "core.contentlinks.chooseaccount": "Изабери налог", - "core.contentlinks.chooseaccounttoopenlink": "Изаберите налог са којим треба отворити линк.", - "core.contentlinks.confirmurlothersite": "Овај линк припада другом сајту. Да ли желите да га отворите?", - "core.contentlinks.errornoactions": "Није могуће пронаћи акцију коју треба извести са овим линком.", - "core.contentlinks.errornosites": "Није могуће пронаћи било који сајт који може изаћи на крај са овим линком.", - "core.continue": "Настави", - "core.copiedtoclipboard": "Текст копиран у клипборд", - "core.course": "Курс", - "core.course.activitydisabled": "Ваша институција је онемогућила ову активност у мобилној апликацији.", - "core.course.activitynotyetviewableremoteaddon": "Ваша институција је инсталирала додатак који још увек није подржан.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Потребно је да се ажурира Moodle инсталација ваше институције.", - "core.course.allsections": "Све секције", - "core.course.askadmintosupport": "Обратите се администратору сајта и реците му да желите да користите ову активност са Moodle Mobile апликацијом.", - "core.course.confirmdeletemodulefiles": "Да ли сте сигурни да желите да избришете датотеке овог модула?", - "core.course.confirmdownload": "Намеравате да преузмете {{size}}. Да ли сте сигурни да желите да наставите?", - "core.course.confirmdownloadunknownsize": "Нисмо могли да израчунамо величину преузимање. Да ли сте сигурни да желите да преузмете?", - "core.course.confirmpartialdownloadsize": "Намеравате да преузмете најмање {{size}}. Да ли сте сигурни да желите да наставите?", - "core.course.contents": "Садржаји", - "core.course.couldnotloadsectioncontent": "Није могуће учитати садржај секције, покушајте поново касније.", - "core.course.couldnotloadsections": "Није могуће учитати секције, покушајте поново касније.", - "core.course.coursesummary": "Резиме курса", - "core.course.downloadcourse": "Преузми курс", - "core.course.errordownloadingcourse": "Грешка приликом преузимању курса.", - "core.course.errordownloadingsection": "Грешка приликом преузимања секције.", - "core.course.errorgetmodule": "Грешка приликом преузимања података модула.", - "core.course.hiddenfromstudents": "Сакривено од полазника", - "core.course.hiddenoncoursepage": "Доступно али се не приказује на страници курса", - "core.course.manualcompletionnotsynced": "Ручни завршетак није синхронизован.", - "core.course.nocontentavailable": "Никакав садржај није доступан у овом тренутку.", - "core.course.overriddennotice": "Ваша завршна оцена за ову активност је ручно подешена", - "core.course.refreshcourse": "Освежи курс", - "core.course.sections": "Секције", - "core.course.useactivityonbrowser": "Још увек можете да га користите помоћу веб читача вашег уређаја.", - "core.course.warningmanualcompletionmodified": "Ручни завршетак активности је измењен на сајту.", - "core.course.warningofflinemanualcompletiondeleted": "Неки офлајн ручни завршеци курса '{{name}}' су обрисани. {{error}}", - "core.coursedetails": "Подаци о курсeвима", - "core.courses.addtofavourites": "Означите овај курс звездицом", - "core.courses.allowguests": "Приступ курсу је дозвољен гостима", - "core.courses.availablecourses": "Доступни курсеви", - "core.courses.cannotretrievemorecategories": "Категорије које су испод нивоа {{$a}} не могу се преузети.", - "core.courses.categories": "Категорије курсева", - "core.courses.confirmselfenrol": "Да ли сте сигурни да желите да се упишете на овај курс?", - "core.courses.courses": "Курсеви", - "core.courses.downloadcourses": "Преузми курс", - "core.courses.enrolme": "Упиши ме", - "core.courses.errorloadcategories": "Дошло је до грешке приликом учитавања категорија.", - "core.courses.errorloadcourses": "Дошло је до грешке приликом учитавања курсева.", - "core.courses.errorsearching": "Дошло је до грешке приликом претраживања.", - "core.courses.errorselfenrol": "Дошло је до грешке приликом покушаја самосталног уписа.", - "core.courses.filtermycourses": "Филтрирај моје курсеве", - "core.courses.frontpage": "Насловна страница", - "core.courses.hidecourse": "Сакриј из приказа", - "core.courses.ignore": "Игнориши", - "core.courses.mycourses": "Моји курсеви", - "core.courses.mymoodle": "Контролни панел", - "core.courses.nocourses": "Нема информација о курсу за приказ.", - "core.courses.nocoursesyet": "Нема курсева у овој категорији", - "core.courses.nosearchresults": "Нема резултата", - "core.courses.notenroled": "Нисте уписани на овај курс", - "core.courses.notenrollable": "Не можете сами да се упишете на овај курс.", - "core.courses.password": "Приступна лозинка курса", - "core.courses.paymentrequired": "Овај курс није бесплатан.", - "core.courses.paypalaccepted": "Прихваћене су Paypal уплате", - "core.courses.reload": "Учитај поново", - "core.courses.removefromfavourites": "Уклони звездицу са овог курса", - "core.courses.search": "Претрага", - "core.courses.searchcourses": "Претражи курсеве", - "core.courses.searchcoursesadvice": "Можете користити дугме за претрагу курсева како бисте им приступили као гост или се уписали на курсеве који то допуштају.", - "core.courses.selfenrolment": "Самостални упис", - "core.courses.sendpaymentbutton": "Пошаљи уплату путем Paypalа", - "core.courses.show": "Покажи овај курс", - "core.courses.totalcoursesearchresults": "Укупно курсева: {{$a}}", - "core.currentdevice": "Тренутни уређај", - "core.datastoredoffline": "Подаци су сачувани у мобилном уређају, зато што не могу да се пошаљу. Аутоматски ће бити послати касније.", - "core.date": "Датум", - "core.day": "дан", - "core.days": "дан/а", - "core.decsep": ",", - "core.defaultvalue": "Подразумевано ({{$a}})", - "core.delete": "Обриши", - "core.deletedoffline": "Обрисано офлајн", - "core.deleteduser": "Обрисан корисник", - "core.deleting": "Брисање", - "core.description": "Опис", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Дигитални малолетник", - "core.digitalminor_desc": "Замолите свог родитеља/старатеља да контактира:", - "core.discard": "Одбаци", - "core.dismiss": "Обустави", - "core.displayoptions": "Опције приказа", - "core.done": "Урађено", - "core.download": "Преузми", - "core.downloading": "Преузимање", - "core.edit": "Уреди", - "core.editor.autosavesucceeded": "Радна верзија је сачувана.", - "core.editor.bold": "Подебљано", - "core.editor.clear": "Обриши форматирање", - "core.editor.h3": "Наслов (велики)", - "core.editor.h4": "Наслов (средњи)", - "core.editor.h5": "Наслов (мали)", - "core.editor.italic": "Курзив", - "core.editor.orderedlist": "Уређена листа", - "core.editor.p": "Пасус", - "core.editor.strike": "Прецртавање", - "core.editor.textrecovered": "Радна верзија овог текста је аутоматски рестаурирана.", - "core.editor.underline": "Подвучено", - "core.editor.unorderedlist": "Неуређена листа", - "core.emptysplit": "Ова страница ће се појавити празна уколико је леви панел празан или се учитава.", - "core.error": "Грешка", - "core.errorchangecompletion": "Дошло је до грешке приликом промене статуса завршетка. Молимо, покушајте поново.", - "core.errordeletefile": "Грешка приликом брисања датотеке. Молимо, покушајте поново.", - "core.errordownloading": "Грешка приликом преузимања датотеке.", - "core.errordownloadingsomefiles": "Грешка приликом преузимања датотека модула. Могуће је да неке датотеке недостају.", - "core.errorfileexistssamename": "Већ постоји датотека са овим називом.", - "core.errorinvalidform": "Образац садржи неисправне податке. Уверите се да сте попунили сва неопходна поља и да су подаци исправни.", - "core.errorinvalidresponse": "Примљен је неисправан одговор. Обратите се администратору вашег Moodle сајта ако се грешка понови.", - "core.errorloadingcontent": "Грешка при учитавању садржаја.", - "core.errorofflinedisabled": "Офлајн прегледање је онемогућено на вашем сајту.", - "core.erroropenfilenoapp": "Грешка приликом отварања датотеке: није пронађена апликација која може да отвори овај тип датотеке.", - "core.erroropenfilenoextension": "Грешка приликом отварања датотеке: датотека нема екстензију.", - "core.erroropenpopup": "Ова активност покушава да отвори искачући прозор. Ова апликација то не подржава.", - "core.errorrenamefile": "Грешка приликом покушаја промене назива датотеке. Молимо, покушајте поново.", - "core.errorsync": "Дошло је до грешке приликом синхронизацији. Молимо, покушајте поново.", - "core.errorsyncblocked": "{{$a}} тренутно не може да се синхронизује због текућег процеса. Молимо, покушајте поново касније. Ако се проблем и даље буде постојао, покушајте поново да покренете апликацију.", - "core.explanationdigitalminor": "Ова информација је неопходна да би се утврдило да ли је ваш узраст изнад неопходног дигиталног узраста у ком појединац може самостално да прихвати одредбе и услове коришћења овог сајта и дâ сагласност да се подаци о њему легално чувају и обрађују.", - "core.favourites": "Означено", - "core.filename": "Назив датотеке", - "core.filenameexist": "Назив датотеке већ постоји: {{$a}}", - "core.filenotfound": "Нажалост, датотека није пронађена", - "core.fileuploader.addfiletext": "Додај датотеку", - "core.fileuploader.audio": "Аудио", - "core.fileuploader.camera": "Камера", - "core.fileuploader.confirmuploadfile": "Намеравате да отпремите {{size}}. Да ли сте сигурни да желите да наставите?", - "core.fileuploader.confirmuploadunknownsize": "Нисмо могли да израчунамо величину датотека за отпремање. Да ли сте сигурни да желите да наставите?", - "core.fileuploader.errorcapturingaudio": "Грешка приликом снимања аудио записа.", - "core.fileuploader.errorcapturingimage": "Грешка приликом снимања слике.", - "core.fileuploader.errorcapturingvideo": "Грешка приликом снимања видео записа.", - "core.fileuploader.errorgettingimagealbum": "Грешка приликом преузимања слике из албума.", - "core.fileuploader.errormustbeonlinetoupload": "Морате бити онлајн како бисте отпремили датотеке.", - "core.fileuploader.errornoapp": "Немате инсталирану апликацију која може да изведе ову акцију.", - "core.fileuploader.errorreadingfile": "Грешка приликом учитавања датотеке.", - "core.fileuploader.errorwhileuploading": "Дошло је до грешке приликом отпремања датотеке.", - "core.fileuploader.file": "Датотека", - "core.fileuploader.filesofthesetypes": "Типови датотека који се прихватају:", - "core.fileuploader.fileuploaded": "Датотека је успешно отпремљена.", - "core.fileuploader.invalidfiletype": "Није могуће прихватити тип датотеке: {{$a}}.", - "core.fileuploader.maxbytesfile": "Датотека {{$a.file}} је превелика. Максимална величина коју можете да отпремите је {{$a.size}}.", - "core.fileuploader.more": "Још", - "core.fileuploader.photoalbums": "Фото албум", - "core.fileuploader.readingfile": "Учитавање датотеке", - "core.fileuploader.readingfileperc": "Учитавање датотеке: {{$a}}%", - "core.fileuploader.selectafile": "Изабери датотеку", - "core.fileuploader.uploadafile": "Отпреми датотеку", - "core.fileuploader.uploading": "Отпремање", - "core.fileuploader.uploadingperc": "Отпремање: {{$a}}%", - "core.fileuploader.video": "Видео", - "core.filter": "Филтер", - "core.folder": "Директоријум", - "core.forcepasswordchangenotice": "Морате променити своју лозинку да бисте наставили", - "core.fulllistofcourses": "Сви курсеви", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Просек", - "core.grades.badgrade": "Дата оцена је неисправна", - "core.grades.contributiontocoursetotal": "Допринос укупној оцени на курсу", - "core.grades.feedback": "Повратне информације", - "core.grades.grade": "Оцена", - "core.grades.gradeitem": "Ставка оцене", - "core.grades.grades": "Оцене", - "core.grades.lettergrade": "Словна оцена", - "core.grades.nogradesreturned": "Нема добијених оцена", - "core.grades.nooutcome": "Без исхода учења", - "core.grades.percentage": "Проценат", - "core.grades.range": "Опсег", - "core.grades.rank": "Ранг", - "core.grades.weight": "Пондер", - "core.group": "Група", - "core.groupsseparate": "Одвојене групе", - "core.groupsvisible": "Видљиве групе", - "core.hasdatatosync": "{{$a}} има офлајн податке које треба синхронизовати.", - "core.help": "Помоћ", - "core.hide": "Сакриј", - "core.hour": "h", - "core.hours": "сат/а/и", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Слика", - "core.imageviewer": "Приказивач слике", - "core.info": "Информација", - "core.invalidformdata": "Неисправни подаци у обрасцу", - "core.labelsep": ":", - "core.lastaccess": "Последњи приступ", - "core.lastdownloaded": "Последњи пут преузето", - "core.lastmodified": "Последња измена", - "core.lastsync": "Последња синхронизација", - "core.layoutgrid": "Мрежа", - "core.list": "Списак", - "core.listsep": ";", - "core.loading": "Учитавање", - "core.loadmore": "Учитај још", - "core.location": "Локација", - "core.login.auth_email": "Самостална регистрација путем е-поште", - "core.login.authenticating": "Провера идентитета", - "core.login.cancel": "Одустани", - "core.login.changepassword": "Промени лозинку", - "core.login.confirmdeletesite": "Да ли сте сигурни да желите да обришете сајт {{sitename}}?", - "core.login.connect": "Повежите се!", - "core.login.connecttomoodle": "Повежите се са Moodleom", - "core.login.contactyouradministrator": "Обратите се администратору вашег сајта за даљу помоћ.", - "core.login.contactyouradministratorissue": "Замолите администратора да провери следећи проблем: {{$a}}", - "core.login.createaccount": "Креирај мој нови кориснички налог", - "core.login.createuserandpass": "Изаберите своје корисничко име и лозинку за приступ систему", - "core.login.credentialsdescription": "За пријаву на систем унесите своје корисничко име и лозинку.", - "core.login.emailconfirmsent": "

                      Требало би да је послата е-порука на вашу адресу {{$a}}

                      \n

                      Порука садржи једноставна упутства о даљем поступку регистрације.

                      \n

                      Ако и даље имате проблема, контактирајте администратора.

                      ", - "core.login.emailconfirmsentnoemail": "

                      Требало би да је послата е-порука на вашу адресу.

                      Порука садржи једноставна упутства о даљем поступку регистрације.

                      Ако и даље имате проблема, контактирајте администратора.

                      ", - "core.login.emailconfirmsentsuccess": "Е-порука за потврду успешно је послата", - "core.login.emailnotmatch": "Адресе е-поште се не поклапају", - "core.login.erroraccesscontrolalloworigin": "Cross-Origin позив који покушавате да изведете је одбијен. Молимо, проверите https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Дошло је до грешке приликом брисања овог сајта. Молим, покушајте поново.", - "core.login.errorupdatesite": "Дошло је до грешке приликом ажурирању токена сајта.", - "core.login.findyoursite": "Пронађите свој сајт", - "core.login.firsttime": "Да ли сте овде први пут?", - "core.login.forcepasswordchangenotice": "Морате променити своју лозинку да бисте наставили", - "core.login.forgotten": "Заборавили сте своје корисничко име или лозинку?", - "core.login.help": "Помоћ", - "core.login.helpmelogin": "

                      Постоји више хиљада Moodle сајтова широм света. Ова апликација може да се повеже само са Moodle сајтовима који су експлицитно омогућили приступ мобилној апликацији.

                      Ако не можете да се повежете са Moodle сајтом, контактирајте администратора вашег сајта и затражите да прочита http://docs.moodle.org/en/Mobile_app

                      Да бисте тестирали апликацију на Moodle демо сајту упишите teacher или student у поље Адреса сајта и кликните дугме Повежите се!.

                      ", - "core.login.instructions": "Упутства", - "core.login.invalidaccount": "Проверите своје податке за пријаву или замолите вашег администратора да провери конфигурацију сајта.", - "core.login.invaliddate": "Неисправан датум", - "core.login.invalidemail": "Неисправна адреса електронске поште", - "core.login.invalidmoodleversion": "Неисправна Moodle верзија. Неопходна је, минимално, верзија {{$a}}.", - "core.login.invalidsite": "URL адреса сајт није исправна.", - "core.login.invalidtime": "Неисправно време", - "core.login.invalidurl": "Наведена је неисправна URL адреса", - "core.login.invalidvaluemax": "Максимална вредност је {{$a}}", - "core.login.invalidvaluemin": "Минимална вредност је {{$a}}", - "core.login.localmobileunexpectedresponse": "Провера Moodle Mobile додатних функционалности вратила је неочекиван одговор. Ваш идентитет биће проверен помоћу стандардног мобилнog сервиса.", - "core.login.loggedoutssodescription": "Морате поново да потврдите свој идентитет. Потребно је да се пријавите на сајт у прозору веб читача.", - "core.login.login": "Пријава", - "core.login.loginbutton": "Пријава", - "core.login.logininsiterequired": "Потребно је да се пријавите на сајт у прозору веб читача.", - "core.login.loginsteps": "Како бисте имали пуни приступ овом сајту морате креирати кориснички налог.", - "core.login.missingemail": "Недостаје адреса е-поште", - "core.login.missingfirstname": "Недостаје име", - "core.login.missinglastname": "Недостаје презиме", - "core.login.mobileservicesnotenabled": "Мобилни сервиси нису омогућени на вашем сајту. Обратите се администратору вашег Moodle сајта ако мислите да мобилни приступ треба да буде омогућен.", - "core.login.mustconfirm": "Морате потврдити свој налог", - "core.login.newaccount": "Нови кориснички налог", - "core.login.notloggedin": "Морате бити пријављени.", - "core.login.password": "Лозинка", - "core.login.passwordforgotten": "Заборављена лозинка", - "core.login.passwordforgotteninstructions2": "За ресетовање Ваше лозинке упишите своје корисничко име или електронску адресу. Уколико ти подаци постоје у бази података, биће Вам послата порука на електронску адресу са упутством како поново да добијете приступ.", - "core.login.passwordrequired": "Неопходна је лозинка", - "core.login.policyaccept": "Разумем и пристајем", - "core.login.policyagree": "Да би сте наставили коришћење овог сајта морате се сложити са правилима коришћења. Да ли се слажете?", - "core.login.policyagreement": "Сагласност са правилником о коришћењу сајта", - "core.login.policyagreementclick": "Линк ка правилнику о коришћењу сајта", - "core.login.potentialidps": "Пријавите се користећи свој налог на:", - "core.login.profileinvaliddata": "Неодговарајућа вредност", - "core.login.recaptchachallengeimage": "reCAPTCHA слика", - "core.login.recaptchaexpired": "Верификација је истекла. Поново одговорите на безбедносно питање.", - "core.login.recaptchaincorrect": "Одговор на безбедносно питање је нетачан.", - "core.login.reconnect": "Повежите се поново", - "core.login.reconnectdescription": "Ваш токен за проверу идентитета је неисправан или је истекао. Морате поново да успоставите везу са сајтом.", - "core.login.reconnectssodescription": "Ваш токен за проверу идентитета је неисправан или је истекао. Морате поново да успоставите везу са сајтом. Потребно је да се пријавите на сајт у прозору веб читача.", - "core.login.resendemail": "Пошаљи поново е-поруку", - "core.login.searchby": "Претрага по:", - "core.login.security_question": "Безбедносно питање", - "core.login.selectacountry": "Изабери државу", - "core.login.selectsite": "Изаберите свој сајт:", - "core.login.signupplugindisabled": "{{$a}} није омогућен.", - "core.login.siteaddress": "Адреса сајта", - "core.login.sitehasredirect": "Ваш сајт садржи најмање једну HTTP редирекцију. Апликација не може да прати редирекције. Ово би могао да буде проблем који спречава апликацију да се повеже са вашим сајтом.", - "core.login.siteinmaintenance": "Ваш сајт је у режиму одржавања", - "core.login.sitepolicynotagreederror": "Сагласност са политиком сајта није потврђена.", - "core.login.siteurl": "URL адреса сајта", - "core.login.siteurlrequired": "Неопходна је URL адреса, нпр. http://www.yourmoodlesite.abc или https://www.yourmoodlesite.efg", - "core.login.startsignup": "Креирај нови налог", - "core.login.stillcantconnect": "Још увек не можете да се повежете?", - "core.login.supplyinfo": "Више детаља", - "core.login.username": "Корисничко име", - "core.login.usernameoremail": "Унесите или корисничко име или адресу е-поште", - "core.login.usernamerequired": "Корисничко име је неопходно", - "core.login.usernotaddederror": "Корисник није додат - грешка", - "core.login.visitchangepassword": "Да ли желите да посетите сајт како бисте променили лозинку?", - "core.login.webservicesnotenabled": "Веб сервиси нису омогућени на вашем сајту. Обратите се администратору вашег Moodle сајта ако мислите да мобилни приступ треба да буде омогућен.", - "core.lostconnection": "Ваш токен за потврду идентитета је неважећи или је истекао. Мораћете поново да успоставите везу са сајтом.", - "core.mainmenu.changesite": "Промени сајт", - "core.mainmenu.help": "Помоћ", - "core.mainmenu.logout": "Одјава", - "core.mainmenu.website": "Веб сајт", - "core.maxsizeandattachments": "Максимална величина датотеке: {{$a.size}}, максималан број датотека: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Разно", - "core.mod_assign": "Задатак", - "core.mod_assignment": "Задатак 2.2 (oнемогућен)", - "core.mod_book": "Књига", - "core.mod_chat": "Причаоница", - "core.mod_choice": "Избор", - "core.mod_data": "База података", - "core.mod_database": "База података", - "core.mod_external-tool": "Екстерни алат", - "core.mod_feedback": "Упитник (Feedback)", - "core.mod_file": "Датотека", - "core.mod_folder": "Директоријум", - "core.mod_forum": "Форум", - "core.mod_glossary": "Речник", - "core.mod_ims": "IMS пакет", - "core.mod_imscp": "IMS пакет", - "core.mod_label": "Натпис", - "core.mod_lesson": "Лекција", - "core.mod_lti": "Екстерни алат", - "core.mod_page": "Страница", - "core.mod_quiz": "Тест", - "core.mod_resource": "Ресурс", - "core.mod_scorm": "SCORM пакет", - "core.mod_survey": "Упитник (Survey)", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Радионица", - "core.moduleintro": "Опис", - "core.more": "још", - "core.mygroups": "Моје групе", - "core.name": "Назив", - "core.networkerroriframemsg": "Овај садржај није доступан офлајн. Повежите се на интернет и покушајте поново.", - "core.networkerrormsg": "Било је проблема са повезивањем на сајт. Проверите везу и покушајте поново.", - "core.never": "Никад", - "core.next": "Следећи", - "core.no": "Не", - "core.nocomments": "Нема коментара", - "core.nograde": "Нема оцене", - "core.none": "Ниједан", - "core.nopasswordchangeforced": "Не можете наставити без промене своје лозинке.", - "core.nopermissionerror": "Жао нам је, али тренутно немате овлашћења да то урадите", - "core.nopermissions": "Жао нам је, али тренутно немате овлашћења да то радите ({{$a}}).", - "core.noresults": "Нема резултата", - "core.noselection": "Нема избора", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Овај профил није доступан јер корисник није пријављен за овај курс.", - "core.notice": "Напомена", - "core.notingroup": "Нажалост, морате бити члан групе како бисте видели ову страницу.", - "core.notsent": "Није послато", - "core.now": "сада", - "core.numwords": "{{$a}} реч(и)", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Кликните овде да бисте приказали слику у пуној величини", - "core.openinbrowser": "Отвори у веб читачу", - "core.othergroups": "Друге групе", - "core.pagea": "Страница {{$a}}", - "core.paymentinstant": "Употребите дугме испод како бисте извршили уплату и уписали курс у року од неколико минута!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Телефон", - "core.pictureof": "Слика {{$a}}", - "core.previous": "Претходни", - "core.proceed": "Наставите", - "core.pulltorefresh": "Повуците за освежавање", - "core.question.answer": "Одговор", - "core.question.answersaved": "Одговор је сачуван", - "core.question.cannotdeterminestatus": "Није могуће одредити статус", - "core.question.certainty": "Ниво сигурности", - "core.question.complete": "Завршено", - "core.question.correct": "Тачно", - "core.question.errorattachmentsnotsupported": "Апликација још увек не подржава могућност да се датотеке придруже уз одговоре.", - "core.question.errorinlinefilesnotsupported": "Апликација још увек не подржава уређивање унутар датотека.", - "core.question.errorquestionnotsupported": "Апликација не подржава овај тип питања: {{$a}}.", - "core.question.feedback": "Повратне информације", - "core.question.howtodraganddrop": "Додирните за избор, затим још једном за спуштање.", - "core.question.incorrect": "Нетачно", - "core.question.information": "Информација", - "core.question.invalidanswer": "Непотпун одговор", - "core.question.notanswered": "Није одговорено", - "core.question.notyetanswered": "Још није одговорено", - "core.question.partiallycorrect": "Делимично тачно", - "core.question.questionmessage": "Питање {{$a}}: {{$b}}", - "core.question.questionno": "Питање {{$a}}", - "core.question.requiresgrading": "Захтева оцењивање", - "core.quotausage": "Тренутно користите {{$a.used}} од максимално дозвољених {{$a.total}}.", - "core.rating.aggregateavg": "Просек оцена", - "core.rating.aggregatecount": "Број оцена", - "core.rating.aggregatemax": "Максимална оцена", - "core.rating.aggregatemin": "Минимална оцена", - "core.rating.aggregatesum": "Збир оцена", - "core.rating.noratings": "Нема предатих оцена", - "core.rating.rating": "Оцена", - "core.rating.ratings": "Оцене", - "core.redirectingtosite": "Бићете преусмерени на сајт.", - "core.refresh": "Освежи", - "core.remove": "Уклони", - "core.required": "Обавезно", - "core.requireduserdatamissing": "Овај корисник нема у свом профилу неке неопходне податке. Молимо вас, унесети ове податке у ваш Moodle и покушајте поново.
                      {{$a}}", - "core.resourcedisplayopen": "Отвори", - "core.resources": "Ресурси", - "core.restore": "Рестаурирање резервнe копијe", - "core.restricted": "Ограничено", - "core.retry": "Покушај поново", - "core.save": "Сачувај", - "core.savechanges": "Сачувај промене", - "core.search": "Претрага", - "core.searching": "Претраживање", - "core.searchresults": "Резултати претраге", - "core.sec": "сек", - "core.secs": "s", - "core.seemoredetail": "Кликните овде за више детеља", - "core.selectacategory": "Молимо вас да изаберете категорију", - "core.selectacourse": "Изабери курс", - "core.selectagroup": "Изабери групу", - "core.send": "Пошаљи", - "core.sending": "Шаље се", - "core.serverconnection": "Грешка у повезивању са сервером", - "core.settings.about": "О апликацији", - "core.settings.cannotsyncoffline": "Офлајн синхронизација није могућа.", - "core.settings.cannotsyncwithoutwifi": "Синхронизација није могућа зато што тренутна подешавања дозвољавају синхронизацију само када постоји веза са Wi-Fi мрежом. Повежите се на Wi-Fi мрежу.", - "core.settings.compilationinfo": "Информације о компилацији", - "core.settings.cordovadevicemodel": "Модел Cordova уређаја", - "core.settings.cordovadeviceosversion": "Верзија оперативног система Cordova уређаја", - "core.settings.cordovadeviceplatform": "Платформа Cordova уређаја", - "core.settings.cordovadeviceuuid": "UUID Cordova уређаја", - "core.settings.cordovaversion": "Cordova верзија", - "core.settings.currentlanguage": "Тренутно важећи језик", - "core.settings.debugdisplay": "Прикажи поруке за отклањање грешака", - "core.settings.debugdisplaydescription": "Ако је омогућено, порука о грешци ће приказати више података о самој грешци ако је то могуће.", - "core.settings.deletesitefiles": "Да ли сте сигурни да желите да обришете преузете датотеке са сајта '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Обриши датотеке сајта", - "core.settings.deviceinfo": "Информације о уређају", - "core.settings.deviceos": "Оперативни систем уређаја", - "core.settings.disableall": "Онемогући обавештења", - "core.settings.disabled": "Онемогућено", - "core.settings.displayformat": "Формат приказа", - "core.settings.enabledownloadsection": "Омогући преузимање секција.", - "core.settings.enablerichtexteditor": "Омогући обогаћени едитор текста", - "core.settings.enablerichtexteditordescription": "Ако је ова опција укључена обогаћени едитор текста биће приказан на местима која то дозвољавају.", - "core.settings.enablesyncwifi": "Дозволите синхронизацију само када сте повезани на Wi-Fi мрежу.", - "core.settings.errordeletesitefiles": "Грешка приликом брисања датотека сајта.", - "core.settings.errorsyncsite": "Грешка приликом синхронизације података сајта. Молимо, проверите вашу интернет везу и покушајте поново.", - "core.settings.estimatedfreespace": "Процењени слободан простора", - "core.settings.filesystemroot": "Основни директоријум система датотека", - "core.settings.fontsizecharacter": "А", - "core.settings.general": "Опште", - "core.settings.language": "Језик", - "core.settings.license": "Лиценца", - "core.settings.localnotifavailable": "Локална обавештења доступна", - "core.settings.locationhref": "Webview URL адреса", - "core.settings.locked": "Закључано", - "core.settings.loggedin": "Онлајн", - "core.settings.loggedoff": "Офлајн", - "core.settings.navigatorlanguage": "Језик навигатора", - "core.settings.navigatoruseragent": "Кориснички агент навигатора", - "core.settings.networkstatus": "Статус инернет везе", - "core.settings.preferences": "Параметри", - "core.settings.privacypolicy": "Политика приватности", - "core.settings.reportinbackground": "Пријави грешке аутоматски", - "core.settings.settings": "Подешавања", - "core.settings.showdownloadoptions": "Прикажи опције преузимања", - "core.settings.sites": "Сајтови", - "core.settings.spaceusage": "Искориштеност простора", - "core.settings.synchronization": "Синхронизација", - "core.settings.synchronizenow": "Синхронизуј сада", - "core.settings.syncsettings": "Подешавања синхронизације", - "core.settings.total": "Укупно", - "core.settings.wificonnection": "WiFi веза", - "core.sharedfiles.chooseaccountstorefile": "Изаберите налог за чување датотеке.", - "core.sharedfiles.chooseactionrepeatedfile": "Већ постоји датотека са овим називом. Да ли желите да замените постојећу датотеку или да јој назив промените у \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Не постоје меморисани сајтови. Молимо, додајте сајт пре него што покушате да поделите датотеке са апликацијом.", - "core.sharedfiles.nosharedfiles": "Не постоје дељене датотеке које се налазе на овом сајту.", - "core.sharedfiles.nosharedfilestoupload": "Овде немате датотеке које можете да отпремите. Ако желите да отпремите датотеку из друге апликације, пронађите ту датотеку и кликните на дугме 'Отвори у'.", - "core.sharedfiles.rename": "Промени назив", - "core.sharedfiles.replace": "Замени", - "core.sharedfiles.sharedfiles": "Дељене датотеке", - "core.sharedfiles.successstorefile": "Датотека је успешно сачувана. Сада можете да изаберете ову датотеку да бисте је отпремили међу своје приватне датотеке или је придружили некој активности.", - "core.show": "Прикажи", - "core.showless": "Прикажи мање...", - "core.showmore": "Прикажи више...", - "core.site": "Сајт", - "core.sitehome.sitehome": "Почетна страница сајта", - "core.sitehome.sitenews": "Обавештења сајта", - "core.sitemaintenance": "Сајт је у фази одржавања и тренутно није доступан", - "core.sizeb": "бајта", - "core.sizegb": "Gb", - "core.sizekb": "Kb", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Прескочи", - "core.sorry": "Извините...", - "core.sort": "Сортирај", - "core.sortby": "Сортирај по", - "core.strftimedate": "%d. %B %Y.", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d. %B", - "core.strftimedatetime": "%d. %B %Y., %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d. %B %Y.", - "core.strftimedaydatetime": "%A, %d. %B %Y., %H:%M", - "core.strftimedayshort": "%A, %d. %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y.", - "core.strftimerecent": "%d. %b, %H:%M", - "core.strftimerecentfull": "%a, %d. %b %Y., %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Проследи", - "core.success": "Успешно", - "core.tablet": "Таблет", - "core.tag.defautltagcoll": "Подразумевана колекција", - "core.tag.inalltagcoll": "Свугде", - "core.tag.itemstaggedwith": "{{$a.tagarea}} означена са \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Нема резултата за \"{{$a}}\"", - "core.tag.notagsfound": "Нису пронађене ознаке које се поклапају са \"{{$a}}\"", - "core.tag.searchtags": "Претражи ознаке", - "core.tag.showingfirsttags": "Број најпопуларнијих ознака које се приказују: {{$a}}", - "core.tag.tag": "Ознака", - "core.tag.tagarea_course": "Курсеви", - "core.tag.tagarea_course_modules": "Активности и ресурси", - "core.tag.tagarea_post": "Објаве на блогу", - "core.tag.tagarea_user": "Корисничка интересовања", - "core.tag.tags": "Ознаке", - "core.teachers": "Предавачи", - "core.thereisdatatosync": "Број офлајн податак које треба синхронизовати: {{$a}}", - "core.thisdirection": "ltr", - "core.time": "Време", - "core.timesup": "Време је истекло!", - "core.today": "Данас", - "core.tryagain": "Покушај поново", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.uhoh": "Ух!", - "core.unexpectederror": "Неочекивана грешка. Затворите и поново отворите апликацију како бисте покушали поново.", - "core.unicodenotsupported": "Неки емотикони нису подржани на овом сајту. Такви карактери ће бити уклоњени приликом слања поруке.", - "core.unicodenotsupportedcleanerror": "Приликом чишћења Unicode карактера пронађен је празан текст.", - "core.unknown": "Непознато", - "core.unlimited": "Неограничено", - "core.unzipping": "Распакивање", - "core.upgraderunning": "Сајт се ажурира, молимо покушајте касније", - "core.user": "Корисник", - "core.user.address": "Адреса", - "core.user.city": "Место", - "core.user.contact": "Контакт", - "core.user.country": "Држава", - "core.user.description": "Опис", - "core.user.details": "Детаљи", - "core.user.detailsnotavailable": "Подаци о овом кориснику вам нису доступни.", - "core.user.editingteacher": "Предавач", - "core.user.email": "Адреса е-поште", - "core.user.emailagain": "Адреса е-поште (поново)", - "core.user.errorloaduser": "Грешка приликом учитавања корисника.", - "core.user.firstname": "Име", - "core.user.interests": "Интересовања", - "core.user.lastname": "Презиме", - "core.user.manager": "Менаџер", - "core.user.newpicture": "Нова слика", - "core.user.noparticipants": "Није пронађен ни један учесник на овом курсу", - "core.user.participants": "Учесници", - "core.user.phone1": "Телефон", - "core.user.phone2": "Мобилни телефон", - "core.user.roles": "Улоге", - "core.user.sendemail": "Е-пошта", - "core.user.student": "Полазник", - "core.user.teacher": "Предавач без уређивачких права", - "core.user.webpage": "Веб страница", - "core.userdeleted": "Овај кориснички налог је обрисан", - "core.userdetails": "Детаљи о кориснику", - "core.usernotfullysetup": "Корисник није у потпуности подешен", - "core.users": "Корисници", - "core.view": "Приказ", - "core.viewcode": "Прикажи кôд", - "core.vieweditor": "Прикажи едитор", - "core.viewembeddedcontent": "Прикажи уграђени садржај", - "core.viewprofile": "Прегледај профил", - "core.warningofflinedatadeleted": "Офлајн подаци компоненте {{component}} '{{name}}' су обрисани. {{error}}", - "core.whatisyourage": "Колико Вам је година?", - "core.wheredoyoulive": "У којој земљи живите?", - "core.whoops": "Упс!", - "core.whyisthishappening": "Зашто се ово дешава?", - "core.whyisthisrequired": "Зашто је ово неопходно?", - "core.wsfunctionnotavailable": "Функција Webservice није доступна.", - "core.year": "година", - "core.years": "година", - "core.yes": "Да" -} \ No newline at end of file diff --git a/src/assets/lang/sr-lt.json b/src/assets/lang/sr-lt.json deleted file mode 100644 index ba31101f2..000000000 --- a/src/assets/lang/sr-lt.json +++ /dev/null @@ -1,1931 +0,0 @@ -{ - "addon.badges.alignment": "Kompetencija", - "addon.badges.badgedetails": "Podaci o bedžu", - "addon.badges.badges": "Bedževi", - "addon.badges.bendorsement": "Potvrda", - "addon.badges.claimcomment": "Komentar potvrde", - "addon.badges.claimid": "URL adresa zahteva", - "addon.badges.contact": "Kontakt", - "addon.badges.dateawarded": "Datum izdavanja", - "addon.badges.expired": "Isteklo", - "addon.badges.expirydate": "Datum isteka", - "addon.badges.imageauthoremail": "Adresa e-pošte autora slike bedža", - "addon.badges.imageauthorname": "Ime autora slike bedža", - "addon.badges.imageauthorurl": "URL adresa autora slike bedža", - "addon.badges.imagecaption": "Potpis ispod slike", - "addon.badges.issuancedetails": "Bedž ističe", - "addon.badges.issuerdetails": "Podaci o izdavaču", - "addon.badges.issueremail": "Adresa e-pošte", - "addon.badges.issuername": "Ime/naziv izdavača bedža", - "addon.badges.issuerurl": "URL adresa izdavača", - "addon.badges.language": "Jezik", - "addon.badges.noalignment": "Ovaj bedž nema navedene eksterne veštine ili standarde.", - "addon.badges.nobadges": "Nema dostupnih bedževa", - "addon.badges.norelated": "Ova bedž nema povezanih bedževa.", - "addon.badges.recipientdetails": "Detalji o primaocu", - "addon.badges.relatedbages": "Povezani bedževi", - "addon.badges.version": "Verzija", - "addon.badges.warnexpired": "(Ovaj bedž je istekao!)", - "addon.block_activitymodules.pluginname": "Aktivnosti", - "addon.block_activityresults.pluginname": "Rezultati aktivnosti", - "addon.block_badges.pluginname": "Najnoviji bedževi", - "addon.block_blogmenu.pluginname": "Meni bloga", - "addon.block_blogrecent.pluginname": "Skorašnji blog članci", - "addon.block_blogtags.pluginname": "Oznake blogova", - "addon.block_calendarmonth.pluginname": "Kalendar", - "addon.block_calendarupcoming.pluginname": "Predstojeći događaji", - "addon.block_comments.pluginname": "Komentari", - "addon.block_completionstatus.pluginname": "Status završetka kursa", - "addon.block_glossaryrandom.pluginname": "Slučajni pojam iz rečnika", - "addon.block_learningplans.pluginname": "Planovi učenja", - "addon.block_myoverview.all": "Svi (osim skrivenih)", - "addon.block_myoverview.allincludinghidden": "Svi", - "addon.block_myoverview.favourites": "Označen zvezdicom", - "addon.block_myoverview.future": "Budući", - "addon.block_myoverview.hiddencourses": "Skriveni", - "addon.block_myoverview.inprogress": "U toku", - "addon.block_myoverview.lastaccessed": "Poslednjem pristupu", - "addon.block_myoverview.morecourses": "Više kurseva", - "addon.block_myoverview.nocourses": "Nema kurseva", - "addon.block_myoverview.past": "Prošli", - "addon.block_myoverview.pluginname": "Pregled kurseva", - "addon.block_myoverview.title": "Naziv kursa", - "addon.block_newsitems.pluginname": "Najnovije vesti", - "addon.block_onlineusers.pluginname": "Onlajn korisnici", - "addon.block_privatefiles.pluginname": "Privatne datoteke", - "addon.block_recentactivity.pluginname": "Nedavne aktivnosti", - "addon.block_recentlyaccessedcourses.nocourses": "Nema kurseva kojima ste skoro pristupali", - "addon.block_recentlyaccessedcourses.pluginname": "Skorašnji kursevi", - "addon.block_recentlyaccesseditems.noitems": "Nema aktivnosti/resursa kojima ste skoro pristupali", - "addon.block_recentlyaccesseditems.pluginname": "Skorašnje aktivnosti/resursi", - "addon.block_rssclient.pluginname": "Udaljeni RSS izvor", - "addon.block_selfcompletion.pluginname": "Samostalni završetak", - "addon.block_sitemainmenu.pluginname": "Glavni meni", - "addon.block_starredcourses.nocourses": "Nema kurseva sa zvezdicom", - "addon.block_starredcourses.pluginname": "Kursevi sa zvezdicom", - "addon.block_tags.pluginname": "Oznake (tagovi)", - "addon.block_timeline.duedate": "Krajnji rok", - "addon.block_timeline.next30days": "Sledećih 30 dana", - "addon.block_timeline.next3months": "Sledeća 3 meseca", - "addon.block_timeline.next6months": "Sledećih 6 meseca", - "addon.block_timeline.next7days": "Sledećih 7 dana", - "addon.block_timeline.nocoursesinprogress": "Nema kurseva koji su u toku", - "addon.block_timeline.noevents": "Nema aktivnosti koje se uskoro završavaju", - "addon.block_timeline.overdue": "Istekle aktivnosti", - "addon.block_timeline.pluginname": "Vremenski raspored", - "addon.block_timeline.sortbycourses": "Sortiraj po kursevima", - "addon.block_timeline.sortbydates": "Sortiraj po datumima", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Blog članci", - "addon.blog.linktooriginalentry": "Link ka izvornom blog članku", - "addon.blog.noentriesyet": "Ovde nema dostupnih чланака", - "addon.blog.publishtonoone": "Vi (nacrt)", - "addon.blog.publishtosite": "Sve na ovom sajtu", - "addon.blog.publishtoworld": "Bilo kom na svetu", - "addon.blog.siteblogheading": "Blog sajta", - "addon.calendar.allday": "Ceo dan", - "addon.calendar.calendar": "Kalendar", - "addon.calendar.calendarevents": "Događaji u kalendaru", - "addon.calendar.categoryevents": "Događaji na nivou kategorije", - "addon.calendar.confirmeventdelete": "Da li ste sigurni da želite da obrišete događaj \"{{$a}}\"?", - "addon.calendar.confirmeventseriesdelete": "Događaj \"{{$a.name}}\" je deo niza. Da li želite da obrišete samo ovaj ili sve događaje (ukupno: {{$a.count}}) u nizu?", - "addon.calendar.courseevents": "Događaji na nivou kursa", - "addon.calendar.daynext": "Sledeći dan", - "addon.calendar.dayprev": "Prethodni dan", - "addon.calendar.defaultnotificationtime": "Podrazumevano vreme za slanje obaveštenja", - "addon.calendar.deleteallevents": "Obriši sve događaje", - "addon.calendar.deleteevent": "Obriši događaj", - "addon.calendar.deleteoneevent": "Obriši ovaj događaj", - "addon.calendar.durationminutes": "Trajanje u minutima", - "addon.calendar.durationnone": "Neograničeno", - "addon.calendar.durationuntil": "Sve do", - "addon.calendar.editevent": "Uređivanje događaja", - "addon.calendar.errorloadevent": "Greška prilikom učitavanja događaja.", - "addon.calendar.errorloadevents": "Greška prilikom učitavanja događaja.", - "addon.calendar.eventcalendareventdeleted": "Događaj u kalendaru obrisan", - "addon.calendar.eventduration": "Vreme trajanja", - "addon.calendar.eventendtime": "Krajnje vreme", - "addon.calendar.eventkind": "Vrsta događaja", - "addon.calendar.eventname": "Naziv događaja", - "addon.calendar.eventstarttime": "Početno vreme", - "addon.calendar.eventtype": "Tip događaja", - "addon.calendar.fri": "Pet", - "addon.calendar.friday": "Petak", - "addon.calendar.gotoactivity": "Idi na aktivnost", - "addon.calendar.groupevents": "Grupni događaji", - "addon.calendar.invalidtimedurationminutes": "Trajanje u minutima koje ste uneli nije ispravno. Molimo unesite trajanje u minutima veće od 0 ili izaberite 'Nema određeno trajanje'.", - "addon.calendar.invalidtimedurationuntil": "Datum i vreme koje ste odabrali za završetak događaja je pre datuma i vremena njegovog početka. Molimo ispravite ovo pre nego što nastavite.", - "addon.calendar.mon": "Pon", - "addon.calendar.monday": "Ponedeljak", - "addon.calendar.monthlyview": "Mesečni pregled", - "addon.calendar.newevent": "Novi događaj", - "addon.calendar.noevents": "Nema događaja", - "addon.calendar.nopermissiontoupdatecalendar": "Žao nam je, ali nemate ovlašćenja da ažurirate događaj u kalendaru.", - "addon.calendar.repeatedevents": "Događaji koji se ponavljaju", - "addon.calendar.repeateditall": "Primeni, takođe, promene na preostala/ih {{$a}} događaja u ovom ponavljajućem nizu", - "addon.calendar.repeateditthis": "Primeni promene samo na ovaj događaj", - "addon.calendar.repeatevent": "Ponovi ovaj događaj", - "addon.calendar.repeatweeksl": "Sedmično ponavljanje, potpuno kreiranje", - "addon.calendar.sat": "Sub", - "addon.calendar.saturday": "Subota", - "addon.calendar.siteevents": "Prikaži događaje na nivou sajta", - "addon.calendar.sun": "Ned", - "addon.calendar.sunday": "Nedelja", - "addon.calendar.thu": "Čet", - "addon.calendar.thursday": "Četvrtak", - "addon.calendar.today": "Danas", - "addon.calendar.tomorrow": "Sutra", - "addon.calendar.tue": "Uto", - "addon.calendar.tuesday": "Utorak", - "addon.calendar.typecategory": "Događaj na nivou kategorije", - "addon.calendar.typeclose": "Zatvoren događaj", - "addon.calendar.typecourse": "Događaj kursa", - "addon.calendar.typedue": "Oročen događaj", - "addon.calendar.typegradingdue": "Rok za ocenjivanje", - "addon.calendar.typegroup": "Grupni događaj", - "addon.calendar.typeopen": "Otvoren događaj", - "addon.calendar.typesite": "Događaj na nivou sajta", - "addon.calendar.typeuser": "Korisnički događaj", - "addon.calendar.upcomingevents": "Predstojeći događaji", - "addon.calendar.userevents": "Korisnički događaji", - "addon.calendar.wed": "Sre", - "addon.calendar.wednesday": "Sreda", - "addon.calendar.when": "Kada", - "addon.calendar.yesterday": "Juče", - "addon.competency.activities": "Aktivnosti", - "addon.competency.competencies": "Kompetencije", - "addon.competency.competenciesmostoftennotproficientincourse": "Kompetencije koje najčešće nisu usavršene na ovom kursu", - "addon.competency.coursecompetencies": "Kompetencije kursa", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Rangiranje kompetencija na ovom kursu na utiče na planove učenja.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Rangiranje kompetencija na ovom kursu se automatski ažurira u planovima učenja.", - "addon.competency.crossreferencedcompetencies": "Unakrsno povezane kompetencije", - "addon.competency.duedate": "Krajnji rok", - "addon.competency.errornocompetenciesfound": "Nije pronađena nijedna kompetencija", - "addon.competency.evidence": "Dokaz", - "addon.competency.evidence_competencyrule": "Pravilo kompetencije ispunjeno.", - "addon.competency.evidence_coursecompleted": "Kurs '{{$a}}' je završen.", - "addon.competency.evidence_coursemodulecompleted": "Aktivnost '{{$a}}' je završena.", - "addon.competency.evidence_courserestored": "Procena kompetencija je restaurisana zajedno sa kursom '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Dokaz o prethodnom učenju '{{$a}}' je povezan.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Veza sa dokaza o prethodnom učenju '{{$a}}' je uklonjena.", - "addon.competency.evidence_manualoverride": "Procena kompetencije je ručno podešena", - "addon.competency.evidence_manualoverrideincourse": "Procena kompetencije je ručno podešena na kursu '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "Procena kompetencije je ručno podešena u planu učenja '{{$a}}'.", - "addon.competency.learningplancompetencies": "Kompetencije plana učenja", - "addon.competency.learningplans": "Planovi učenja", - "addon.competency.myplans": "Moji planovi učenja", - "addon.competency.noactivities": "Nema aktivnosti", - "addon.competency.nocompetencies": "Nema kompetencija", - "addon.competency.nocompetenciesincourse": "Nijedna kompetencija nije povezana sa ovim kursom.", - "addon.competency.nocrossreferencedcompetencies": "Nijedna druga kompetencija nije unakrsno povezana sa ovom kompetencijom.", - "addon.competency.noevidence": "Nema dokaza", - "addon.competency.noplanswerecreated": "Nije kreiran nijedan plan učenja.", - "addon.competency.nouserplanswithcompetency": "Nijedan plan za učenje ne sadrži ovu kompetenciju.", - "addon.competency.path": "Putanja:", - "addon.competency.planstatusactive": "Aktivan", - "addon.competency.planstatuscomplete": "Ispunjen", - "addon.competency.planstatusdraft": "Radna verzija", - "addon.competency.planstatusinreview": "Na pregledu", - "addon.competency.planstatuswaitingforreview": "Čeka na pregled", - "addon.competency.proficient": "Stručan", - "addon.competency.progress": "Napredovanje", - "addon.competency.rating": "Rangiranje", - "addon.competency.reviewstatus": "Pregledaj status", - "addon.competency.status": "Status", - "addon.competency.template": "Šablon plana učenja", - "addon.competency.uponcoursecompletion": "Nakon završetka kursa:", - "addon.competency.usercompetencystatus_idle": "U stanju mirovanja", - "addon.competency.usercompetencystatus_inreview": "Na pregledu", - "addon.competency.usercompetencystatus_waitingforreview": "Čeka na pregled", - "addon.competency.userplans": "Planovi učenja", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} od {{$a.y}} kompetencija su na najvišem nivou stručnosti", - "addon.competency.xcompetenciesproficientoutofyincourse": "Stručni ste u {{$a.x}} od {{$a.y}} kompetencija na ovom kursu.", - "addon.coursecompletion.complete": "Završi", - "addon.coursecompletion.completecourse": "Završen kurs", - "addon.coursecompletion.completed": "Završeno", - "addon.coursecompletion.completiondate": "Datum završetka", - "addon.coursecompletion.completionmenuitem": "Završetak", - "addon.coursecompletion.couldnotloadreport": "Nije moguće učitati izveštaj o završetku kursa. Molimo vas, pokušajte ponovo kasnije.", - "addon.coursecompletion.coursecompletion": "Završetak kursa", - "addon.coursecompletion.criteria": "Kriterijumi", - "addon.coursecompletion.criteriagroup": "Grupa kriterijuma", - "addon.coursecompletion.criteriarequiredall": "Svi dole navedeni kriterijumi su neophodni", - "addon.coursecompletion.criteriarequiredany": "Bilo koji od dole navedenih kriterijuma je neophodan", - "addon.coursecompletion.inprogress": "U toku", - "addon.coursecompletion.manualselfcompletion": "Ručni samostalni završetak", - "addon.coursecompletion.nottracked": "Trenutno se ne prati vaša aktivnost ka završetku kursa", - "addon.coursecompletion.notyetstarted": "Nije još počeo", - "addon.coursecompletion.pending": "Na čekanju", - "addon.coursecompletion.required": "Obavezno", - "addon.coursecompletion.requiredcriteria": "Obavezni kriterijumi", - "addon.coursecompletion.requirement": "Zahtev", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Prikaži izveštaj sa kursa", - "addon.files.couldnotloadfiles": "Spisak datoteka ne može biti učitan.", - "addon.files.emptyfilelist": "Nema datoteka za prikaz.", - "addon.files.erroruploadnotworking": "Nažalost, trenutno nije moguće otpremiti datoteke na vaš sajt.", - "addon.files.files": "Datoteke", - "addon.files.privatefiles": "Privatne datoteke", - "addon.files.sitefiles": "Datoteke sajta", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Komfiguriši uređaj", - "addon.messages.acceptandaddcontact": "Prihvatite i dodajte u kontakte", - "addon.messages.addcontact": "Dodaj kontakt", - "addon.messages.addcontactconfirm": "Jeste li sigurni da želite da dodate {{$a}} među svoje kontakte?", - "addon.messages.addtofavourites": "Označi prepisku zvezdicom", - "addon.messages.addtoyourcontacts": "Dodaj u kontakte", - "addon.messages.blocknoncontacts": "Spreči sve korisnike van spiska kontakata da mi šalju poruke", - "addon.messages.blockuser": "Blokiraj korisnika", - "addon.messages.blockuserconfirm": "Jeste li sigurni da želite da blokirate {{$a}}?", - "addon.messages.contactableprivacy": "Prihvatite poruke od:", - "addon.messages.contactableprivacy_coursemember": "Moji kontakti i bilo ko na mojim kursevima", - "addon.messages.contactableprivacy_onlycontacts": "Samo moji kontakti", - "addon.messages.contactableprivacy_site": "Bilo ko na sajtu", - "addon.messages.contactblocked": "Kontakt blokiran", - "addon.messages.contactlistempty": "Lista kontakata je prazna", - "addon.messages.contactname": "Ime osobe", - "addon.messages.contactrequestsent": "Zahtev za kontakt je poslat", - "addon.messages.contacts": "Kontakti", - "addon.messages.conversationactions": "Prepiska - Meni akcija", - "addon.messages.decline": "Odbij", - "addon.messages.deleteallconfirm": "Da li ste sigurni da želite da u potpunosti obrišete ovu prepisku? Ovo neće izbrisati prepisku za druge učesnike.", - "addon.messages.deleteallselfconfirm": "Da li ste sigurni da želite da obrišete u potpunosti ovu ličnu prepisku?", - "addon.messages.deleteconversation": "Obriši prepisku", - "addon.messages.deleteforeveryone": "Obriši za mene i sve druge", - "addon.messages.deletemessage": "Obriši poruku", - "addon.messages.deletemessageconfirmation": "Da li sigurni da želite da obrišete ovu poruku? Ona će biti izbrisan samo iz vaše istorije poruka ali će i dalje biti vidljiva korisniku koji je poslao ili primio poruku.", - "addon.messages.errordeletemessage": "Greška prilikom brisanja poruke.", - "addon.messages.errorwhileretrievingcontacts": "Greška prilikom preuzimanja kontakata sa servera.", - "addon.messages.errorwhileretrievingdiscussions": "Greška prilikom preuzimanja diskusija sa servera.", - "addon.messages.errorwhileretrievingmessages": "Greška prilikom preuzimanja poruka sa servera.", - "addon.messages.errorwhileretrievingusers": "Greška prilikom preuzimanja korisnika sa servera.", - "addon.messages.groupconversations": "Grupa", - "addon.messages.groupinfo": "Info o grupi", - "addon.messages.individualconversations": "Privatna prepiska", - "addon.messages.info": "Podaci o korisniku", - "addon.messages.isnotinyourcontacts": "{{$a}} nije među vašim kontaktima", - "addon.messages.message": "Poruka", - "addon.messages.messagenotsent": "Poruka nije poslata. Molimo, pokušajte ponovo kasnije.", - "addon.messages.messagepreferences": "Parametri poruka", - "addon.messages.messages": "Poruke", - "addon.messages.muteconversation": "Privremeno isključi", - "addon.messages.mutedconversation": "Privremeno isključena prepiska", - "addon.messages.newmessage": "Nova poruka", - "addon.messages.newmessages": "Nove poruke", - "addon.messages.nocontactrequests": "Nema zahteva za kontakt", - "addon.messages.nocontactsgetstarted": "Nema kontakata", - "addon.messages.nofavourites": "Nema označenih prepiski", - "addon.messages.nogroupconversations": "Nema grupnih prepiski", - "addon.messages.noindividualconversations": "Nema privatnih prepiski", - "addon.messages.nomessagesfound": "Nije pronađena nijedna poruka", - "addon.messages.noncontacts": "Van spiska kontakata", - "addon.messages.nousersfound": "Nije pronađen nijedan korisnik", - "addon.messages.numparticipants": "{{$a}} učesnika", - "addon.messages.removecontact": "Obriši kontakt", - "addon.messages.removecontactconfirm": "Jeste li sigurni da želite ukloniti {{$a}} sa spiska svojih kontakata?", - "addon.messages.removefromfavourites": "Ukloni zvezdicu sa prepiske", - "addon.messages.removefromyourcontacts": "Ukloni iz kontakata", - "addon.messages.requests": "Zahtevi", - "addon.messages.requirecontacttomessage": "Potrebno je da pošaljete zahtev korisniku {{$a}} da vas doda kao kontakt kako biste mogli da mu/joj pošaljete poruku.", - "addon.messages.searchcombined": "Pretraži ljude i poruke", - "addon.messages.selfconversation": "Lični prostor", - "addon.messages.selfconversationdefaultmessage": "Sačuvajte radnu verziju poruka, veze, napomene itd. kako biste im kasnije pristupili.", - "addon.messages.sendcontactrequest": "Pošaljite zahtev za kontakt", - "addon.messages.showdeletemessages": "Prikaži brisanje poruka", - "addon.messages.type_blocked": "Blokiran", - "addon.messages.type_offline": "Oflajn", - "addon.messages.type_online": "Onlajn", - "addon.messages.type_search": "Rezultati pretrage", - "addon.messages.type_strangers": "Drugi", - "addon.messages.unabletomessage": "Ne možete da pošaljete poruku ovom korisniku", - "addon.messages.unblockuser": "Deblokiraj korisnika", - "addon.messages.unblockuserconfirm": "Jeste li sigurni da želite da deblokirate korisnika {{$a}}?", - "addon.messages.unmuteconversation": "Ponovo uključi", - "addon.messages.useentertosend": "Koristite taster \"Enter\" za slanje", - "addon.messages.userwouldliketocontactyou": "{{$a}} želi da vas doda među svoje kontakte", - "addon.messages.warningconversationmessagenotsent": "Nije moguće poslati poruku(e) u prepisku {{conversation}}. {{error}}", - "addon.messages.warningmessagenotsent": "Nije moguće poslati poruku/e korisniku {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "Želi da vas doda među svoje kontakte", - "addon.messages.you": "Vi:", - "addon.messages.youhaveblockeduser": "Blokirali ste ovog korisnika.", - "addon.messages.yourcontactrequestpending": "Vaš zahtev za kontakt čeka na {{$a}}", - "addon.mod_assign.acceptsubmissionstatement": "Molimo vas da prihvatite izjavu o predaji rada.", - "addon.mod_assign.addattempt": "Dozvoli drugi pokušaj", - "addon.mod_assign.addnewattempt": "Dodaj novi pokušaj", - "addon.mod_assign.addnewattemptfromprevious": "Dodaj novi pokušaj na osnovu prethodno predatog rada", - "addon.mod_assign.addsubmission": "Dodaj rad", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Detaljnije informacije o zadatku i obrazac za predaju radova biće dostupni od {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Dozvoli predaju od", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Ovaj zadatak će prihvatiti predaju rada od {{$a}}", - "addon.mod_assign.applytoteam": "Primenite ocene i povratne informacije na celu grupu", - "addon.mod_assign.assignmentisdue": "Krajnji rok za predaju je istekao", - "addon.mod_assign.attemptnumber": "Broj pokušaja", - "addon.mod_assign.attemptreopenmethod": "Ponovno otvaranje rada", - "addon.mod_assign.attemptreopenmethod_manual": "Ručno", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatski dok ne dobije prelaznu ocenu", - "addon.mod_assign.attemptsettings": "Podešavanja pokušaja", - "addon.mod_assign.cannoteditduetostatementsubmission": "Ne možete da dodate ili menjate rad u aplikaciji jer nismo mogli da preuzmemo sa sajta izjavu o predaji rada.", - "addon.mod_assign.cannotgradefromapp": "Aplikacije ne podržava neke metode ocenjivanja i one ne mogu da se menjaju.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Ne možete da predate rad na ocenjivanje u aplikaciji jer nismo mogli da preuzmemo sa sajta izjavu o predaji rada.", - "addon.mod_assign.confirmsubmission": "Da li ste sigurni da želite da predate svoj rad na ocenjivanje? Više nećete moći da ga menjate.", - "addon.mod_assign.currentattempt": "Ovo je pokušaj {{$a}}.", - "addon.mod_assign.currentattemptof": "Ovo je pokušaj {{$a.attemptnumber}} ( {{$a.maxattempts}} dozvoljena/ih pokušaja ).", - "addon.mod_assign.currentgrade": "Trenutna ocena u knjizi ocena", - "addon.mod_assign.cutoffdate": "Definitivni rok", - "addon.mod_assign.defaultteam": "Podrazumevana grupa", - "addon.mod_assign.duedate": "Krajnji rok", - "addon.mod_assign.duedateno": "Nema krajnjeg roka", - "addon.mod_assign.duedatereached": "Krajnji rok za ovaj zadatak je sada istekao", - "addon.mod_assign.editingstatus": "Status uređivanja", - "addon.mod_assign.editsubmission": "Uredi rad", - "addon.mod_assign.erroreditpluginsnotsupported": "Ne možete da dodate ili menjate rad u aplikaciji jer neki dodaci nemaju podršku za uređivanje:", - "addon.mod_assign.errorshowinginformation": "Ne možemo da prikažemo informacije o predatom radu", - "addon.mod_assign.extensionduedate": "Produženi rok", - "addon.mod_assign.feedbacknotsupported": "Apllikacija ne podržava ovu povratnu informaciju. Moguće je da ona ne sadrži sve informacije.", - "addon.mod_assign.grade": "Ocena", - "addon.mod_assign.graded": "Ocenjeno", - "addon.mod_assign.gradedby": "Ocenio/la", - "addon.mod_assign.gradedfollowupsubmit": "Ocenjeno - primljen naknadno dostavljen rad", - "addon.mod_assign.gradedon": "Ocenjeno", - "addon.mod_assign.gradelocked": "Ова оцена је закључана или преписана у књизи оцена.", - "addon.mod_assign.gradenotsynced": "Ocena nije sinhronizovana", - "addon.mod_assign.gradeoutof": "Ocena od {{$a}}", - "addon.mod_assign.gradingstatus": "Status ocenjivanja", - "addon.mod_assign.groupsubmissionsettings": "Podešavanja za grupnu predaju rada", - "addon.mod_assign.hiddenuser": "Učesnik", - "addon.mod_assign.latesubmissions": "Kasno predati radovi", - "addon.mod_assign.latesubmissionsaccepted": "Dozvoljeno do {{$a}}", - "addon.mod_assign.markingworkflowstate": "Stanje toka ocenjivanja", - "addon.mod_assign.markingworkflowstateinmarking": "Ocenjuje se", - "addon.mod_assign.markingworkflowstateinreview": "Na pregledu", - "addon.mod_assign.markingworkflowstatenotmarked": "Nije ocenjeno", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Spremno za objavu", - "addon.mod_assign.markingworkflowstatereadyforreview": "Ocenjivanje završeno", - "addon.mod_assign.markingworkflowstatereleased": "Objavljeno", - "addon.mod_assign.modulenameplural": "Zadaci", - "addon.mod_assign.multipleteams": "Član više od jedne grupe", - "addon.mod_assign.multipleteams_desc": "Ovaj zadatak traži grupnu predaju rada. Član ste više od jedne grupe. Da biste mogli da predate rad morate da budete član samo jedne grupe. Kontaktirajte svog predavača kako bi promenio vaše članstvo u grupi.", - "addon.mod_assign.noattempt": "Nema pokušaja", - "addon.mod_assign.nomoresubmissionsaccepted": "Dozvoljeno samo polaznicima kojima je odobren produžetak", - "addon.mod_assign.noonlinesubmissions": "Ovaj zadatak ne traži od vas da bilo šta predate onlajn", - "addon.mod_assign.nosubmission": "Za ovaj zadatak još ništa nije predato", - "addon.mod_assign.notallparticipantsareshown": "Učesnici koji nisu predali rad se ne prikazuju", - "addon.mod_assign.noteam": "Nije član nijedne grupe.", - "addon.mod_assign.noteam_desc": "Ovaj zadatak traži grupnu predaju rada. Niste član nijedne grupe, tako da ne možete da predate rad. Kontaktirajte svog predavača kako bi vas dodao u grupu.", - "addon.mod_assign.notgraded": "Nije ocenjeno", - "addon.mod_assign.numberofdraftsubmissions": "Nacrti", - "addon.mod_assign.numberofparticipants": "Učesnici", - "addon.mod_assign.numberofsubmissionsneedgrading": "Traži ocenjivanje", - "addon.mod_assign.numberofsubmittedassignments": "Predato", - "addon.mod_assign.numberofteams": "Grupe", - "addon.mod_assign.numwords": "{{$a}} reč(i)", - "addon.mod_assign.outof": "{{$a.current}} od {{$a.total}}", - "addon.mod_assign.overdue": "Krajnji rok za predaju rada je istekao pre: {{$a}}", - "addon.mod_assign.submission": "Predati rad", - "addon.mod_assign.submissioneditable": "Polaznici mogu da uređuju ovaj zadatak", - "addon.mod_assign.submissionnoteditable": "Polaznici ne mogu da uređuju ovaj zadatak", - "addon.mod_assign.submissionnotsupported": "Aplikacija ne podržava ovaj predati rad. Moguće je da rad ne sadrži sve informacije.", - "addon.mod_assign.submissionslocked": "Ovaj zadatak ne prihvata predaju radova", - "addon.mod_assign.submissionstatus": "Status predatog rada", - "addon.mod_assign.submissionstatus_": "Nema predatih radova", - "addon.mod_assign.submissionstatus_draft": "Nacrt rada (nije predato)", - "addon.mod_assign.submissionstatus_marked": "Ocenjeno", - "addon.mod_assign.submissionstatus_new": "Novi predati rad", - "addon.mod_assign.submissionstatus_reopened": "Ponovo otvoreno", - "addon.mod_assign.submissionstatus_submitted": "Predato za ocenjivanje", - "addon.mod_assign.submissionstatusheading": "Status predatog rada", - "addon.mod_assign.submissionteam": "Grupa", - "addon.mod_assign.submitassignment": "Predaj zadatak", - "addon.mod_assign.submitassignment_help": "Onog trenutka kada predate ovaj rad više nećete moći da ga menjate.", - "addon.mod_assign.submittedearly": "Zadatak je predat {{$a}} ranije", - "addon.mod_assign.submittedlate": "Zadatak je predat {{$a}} kasnije", - "addon.mod_assign.timemodified": "Poslednje izmene", - "addon.mod_assign.timeremaining": "Preostalo vreme", - "addon.mod_assign.ungroupedusers": "Podešavanje 'Neophodna je grupa za predaju rada' je omogućeno, ali neki korisnici nisu članovi nijedne grupe, ili su članovi više od jedne grupe, tako da ne mogu da predaju rad.", - "addon.mod_assign.ungroupedusersoptional": "Podešavanje \"Polaznici predaju u grupama\" je omogućeno a neki korisnici ili nisu članovi nijedne grupe ili su članovi više grupa. Molimo uzmite u obzir da će ti polaznici predati rešenje zadatka kao članovi \"Podrazumevane grupe\".", - "addon.mod_assign.unlimitedattempts": "Neograničeno", - "addon.mod_assign.userswhoneedtosubmit": "Korisnici koji treba da predaju rešenje: {{$a}}", - "addon.mod_assign.userwithid": "Korisnik sa ID oznakom {{id}}", - "addon.mod_assign.viewsubmission": "Pogledaj predati rad", - "addon.mod_assign.warningsubmissiongrademodified": "Ocena predatog rada je izmenjena na sajtu.", - "addon.mod_assign.warningsubmissionmodified": "Predati rad korisnika je izmenjen na sajtu.", - "addon.mod_assign.wordlimit": "Ograničenje broja reči", - "addon.mod_assign_feedback_comments.pluginname": "Povratni komentari", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF sa napomenama", - "addon.mod_assign_feedback_file.pluginname": "Datoteka sa povratnim informacijama", - "addon.mod_assign_submission_comments.pluginname": "Komentari za predati rad", - "addon.mod_assign_submission_file.pluginname": "Predaja datoteka", - "addon.mod_assign_submission_onlinetext.pluginname": "Predaja onlajn tekstova", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Ograničenje broja reči za ovaj zadatak je {{$a.limit}} reči, a vi pokušavate da pošaljete {{$a.count}} reči. Molimo, pregledajte svoj rad i pokušajte ponovo.", - "addon.mod_book.errorchapter": "Greška prilikom učitavanja poglavlja knjige.", - "addon.mod_book.modulenameplural": "Knjige", - "addon.mod_book.navnexttitle": "Sledeće: {{$a}}", - "addon.mod_book.navprevtitle": "Prethodno: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Poglavlja knjige", - "addon.mod_book.toc": "Sadržaj", - "addon.mod_chat.beep": "Zvučni signal", - "addon.mod_chat.chatreport": "Razgovori", - "addon.mod_chat.currentusers": "Trenutni korisnici", - "addon.mod_chat.enterchat": "Kliknite ovde za ulazak u pričaonicu", - "addon.mod_chat.entermessage": "Unesite vašu poruku", - "addon.mod_chat.errorwhileconnecting": "Greška prilikom povezivanja sa pričaonicom.", - "addon.mod_chat.errorwhilegettingchatdata": "Greška prilikom preuzimanja podataka za 'Pričaonicu'.", - "addon.mod_chat.errorwhilegettingchatusers": "Greška prilikom preuzimanja korisnika pričaonice.", - "addon.mod_chat.errorwhileretrievingmessages": "Greška prilikom preuzimanja poruka sa servera.", - "addon.mod_chat.errorwhilesendingmessage": "Greška prilikom slanja poruke.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} pozdravlja sve zvučnim signalom!", - "addon.mod_chat.messagebeepsyou": "{{$a}} vas je upravo pozdravio/la zvučnim signalom!", - "addon.mod_chat.messageenter": "Učesnik {{$a}} upravo ulazi u pričaonicu", - "addon.mod_chat.messageexit": "Učesnik {{$a}} upravo napušta pričaonicu", - "addon.mod_chat.messages": "Poruke", - "addon.mod_chat.messageyoubeep": "Pozdravili ste zvučnim signalom {{$a}}", - "addon.mod_chat.modulenameplural": "Pričaonice", - "addon.mod_chat.mustbeonlinetosendmessages": "Morate biti onlajn kako biste slali poruke.", - "addon.mod_chat.nomessages": "Još nema poruka", - "addon.mod_chat.saidto": "rečeno učesniku", - "addon.mod_chat.send": "Pošalji", - "addon.mod_chat.sessionstart": "Sledeća sesija će početi dana {{$a.date}}, ({{$a.fromnow}} od sada)", - "addon.mod_chat.talk": "Pričaj", - "addon.mod_chat.viewreport": "Pogledaj prethodne sesije", - "addon.mod_choice.cannotsubmit": "Izvinite, došlo je do problema prilikom čuvanja odabranog odgovora. Molimo pokušajte ponovo.", - "addon.mod_choice.choiceoptions": "Opcije za izbor", - "addon.mod_choice.errorgetchoice": "Greška prilikom preuzimanja podataka za 'Izbor'", - "addon.mod_choice.expired": "Ova aktivnost je zatvorena dana {{$a}}.", - "addon.mod_choice.full": "(Popunjeno)", - "addon.mod_choice.modulenameplural": "Izbori", - "addon.mod_choice.noresultsviewable": "Rezultati trenutno nisu vidljivi.", - "addon.mod_choice.notopenyet": "Ova aktivnost nije dostupna do {{$a}}", - "addon.mod_choice.numberofuser": "Broj odgovora", - "addon.mod_choice.numberofuserinpercentage": "Procenat odgovora", - "addon.mod_choice.previewonly": "Ovo je samo prikaz dostupnih opcija za ovu aktivnost. Nećete biti u mogućnosti da izvršite odabir do {{$a}}.", - "addon.mod_choice.publishinfoanonafter": "Anonimni rezultati biće objavljeni nakon što odgovorite.", - "addon.mod_choice.publishinfoanonclose": "Anonimni rezultati biće objavljeni nakon što se aktivnost zatvori.", - "addon.mod_choice.publishinfofullafter": "Kompletni rezultati, sa odgovorima svih učesnika, biće objavljeni nakon što odgovorite.", - "addon.mod_choice.publishinfofullclose": "Kompletni rezultati, sa odgovorima svih učesnika, biće objavljeni nakon što se aktivnost zatvori.", - "addon.mod_choice.publishinfonever": "Rezultati ove aktivnosti neće biti objavljeni nakon što odgovorite.", - "addon.mod_choice.removemychoice": "Ukloni moj izbor", - "addon.mod_choice.responses": "Odgovori", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% korisnika je izabralo opciju: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Grafički prikaz", - "addon.mod_choice.resultsnotsynced": "Rezultati ne uključuje vaš poslednji odgovor. Pokrenite sinhronizaciju kako biste ih ažurirali.", - "addon.mod_choice.savemychoice": "Sačuvaj moj izbor", - "addon.mod_choice.userchoosethisoption": "Korisnici koji su izabrali ovu opciju", - "addon.mod_choice.yourselection": "Vaš izbor", - "addon.mod_data.addentries": "Dodaj unose", - "addon.mod_data.advancedsearch": "Napredno pretraživanje", - "addon.mod_data.alttext": "Alternativni tekst", - "addon.mod_data.approve": "Odobri", - "addon.mod_data.approved": "Odobreno", - "addon.mod_data.ascending": "Rastuće", - "addon.mod_data.authorfirstname": "Ime autora", - "addon.mod_data.authorlastname": "Prezime autora", - "addon.mod_data.confirmdeleterecord": "Da li ste sigurni da želite da obrišete ovaj unos?", - "addon.mod_data.descending": "Opadajuće", - "addon.mod_data.disapprove": "Povuci odobrenje", - "addon.mod_data.emptyaddform": "Niste ispunili nijedno polje!", - "addon.mod_data.entrieslefttoadd": "Morate da dodate još {{$a.entriesleft}} unos(a) kako biste završili ovu aktivnost", - "addon.mod_data.entrieslefttoaddtoview": "Morate da dodate još {{$a.entrieslefttoview}} unosa pre nego što budete mogli da vidite unose drugih korisnika.", - "addon.mod_data.errorapproving": "Greška prilikom odobravanja ili neodobravanja unosa.", - "addon.mod_data.errordeleting": "Greška prilikom brisanja unosa.", - "addon.mod_data.errormustsupplyvalue": "Morate ovde zadati vrednost.", - "addon.mod_data.expired": "Nažalost, ova aktivnost je zatvorena {$}} i više nije dostupna", - "addon.mod_data.fields": "Polja", - "addon.mod_data.foundrecords": "Pronađeni zapisi: {{$a.num}}/{{$a.max}}(Vrati stara podešavanja filtera)", - "addon.mod_data.latlongboth": "Polja za geografsku širina i dužina su obavezna.", - "addon.mod_data.menuchoose": "Izaberite...", - "addon.mod_data.modulenameplural": "Baze podataka", - "addon.mod_data.more": "Još", - "addon.mod_data.nomatch": "Nema unosa koji se poklapaju!", - "addon.mod_data.norecords": "Nema unosa u bazi podataka", - "addon.mod_data.notapproved": "Unos još nije odobren", - "addon.mod_data.notopenyet": "Nažalost, ova aktivnost nije dostupna do {{$a}}", - "addon.mod_data.numrecords": "{{$a}} unosa", - "addon.mod_data.other": "Drugo", - "addon.mod_data.recordapproved": "Unos je odobren", - "addon.mod_data.recorddeleted": "Unos je obrisan", - "addon.mod_data.recorddisapproved": "Unos nije odobren", - "addon.mod_data.resetsettings": "Resetuj filtere", - "addon.mod_data.search": "Traži", - "addon.mod_data.selectedrequired": "Sve izabrano obavezno", - "addon.mod_data.single": "Prikaži pojedinačno", - "addon.mod_data.tagarea_data_records": "Zapisi podataka", - "addon.mod_data.timeadded": "Vreme dodavanja", - "addon.mod_data.timemodified": "Vreme izmene", - "addon.mod_data.usedate": "Uključi u pretraživanje.", - "addon.mod_feedback.analysis": "Analiza", - "addon.mod_feedback.anonymous": "Anonimni upitnik", - "addon.mod_feedback.anonymous_entries": "Anonimni odgovori ({{$a}})", - "addon.mod_feedback.average": "Prosečno", - "addon.mod_feedback.captchaofflinewarning": "Upitnik sa Captcha elementom ne može da bude završen ako nije konfigurisan, ako ste u oflajn režimu ili ako je server isključen.", - "addon.mod_feedback.complete_the_form": "Odgovori na pitanja", - "addon.mod_feedback.completed_feedbacks": "Analiza odgovora", - "addon.mod_feedback.continue_the_form": "Nastavi sa odgovaranjem na pitanja", - "addon.mod_feedback.feedback_is_not_open": "Upitnik nije otvoren", - "addon.mod_feedback.feedback_submitted_offline": "Ovaj upitnik je sačuvan kako bi kasnije bio predat.", - "addon.mod_feedback.feedbackclose": "Upitnik dostupan do", - "addon.mod_feedback.feedbackopen": "Upitnik dostupan od", - "addon.mod_feedback.mapcourses": "Poveži upitnik sa kursevima", - "addon.mod_feedback.maximal": "Maksimalno", - "addon.mod_feedback.minimal": "Minimalno", - "addon.mod_feedback.mode": "Vrsta upitnika", - "addon.mod_feedback.modulenameplural": "Upitnici (Feedback Activities)", - "addon.mod_feedback.next_page": "Sledeća stranica", - "addon.mod_feedback.non_anonymous": "Ime korisnika biće zapisano i prikazano zajedno sa odgovorima", - "addon.mod_feedback.non_anonymous_entries": "Neanonimni odgovori ({{$a}})", - "addon.mod_feedback.non_respondents_students": "Polaznici koji nisu odgovorili na upitnik ({{$a}})", - "addon.mod_feedback.not_selected": "Nije izabrano", - "addon.mod_feedback.not_started": "Nije započeto", - "addon.mod_feedback.numberoutofrange": "Broj izvan opsega", - "addon.mod_feedback.overview": "Pregled", - "addon.mod_feedback.page_after_submit": "Poruka koja će biti prikazana korisniku nakon što popuni upitnik", - "addon.mod_feedback.preview": "Pregled", - "addon.mod_feedback.previous_page": "Prethodna stranica", - "addon.mod_feedback.questions": "Pitanja", - "addon.mod_feedback.response_nr": "Odgovor br.", - "addon.mod_feedback.responses": "Odgovori", - "addon.mod_feedback.save_entries": "Pošalji svoje odgovore", - "addon.mod_feedback.show_entries": "Prikaži odgovore", - "addon.mod_feedback.show_nonrespondents": "Prikaži korisnike koje nisu odgovorili na upitnik", - "addon.mod_feedback.started": "Započeto", - "addon.mod_feedback.this_feedback_is_already_submitted": "Već ste popunili ovaj upitnik.", - "addon.mod_folder.emptyfilelist": "Nema datoteka za prikaz.", - "addon.mod_folder.modulenameplural": "Direktorijumi", - "addon.mod_forum.addanewdiscussion": "Dodaj novu temu za diskusiju", - "addon.mod_forum.addanewquestion": "Dodaj novo pitanje", - "addon.mod_forum.addanewtopic": "Dodaj novu temu", - "addon.mod_forum.addtofavourites": "Označi ovu diskusiju zvezdicom", - "addon.mod_forum.advanced": "Napredno", - "addon.mod_forum.cannotadddiscussion": "Dodavanje diskusije u okviru ovog foruma zahteva grupno članstvo.", - "addon.mod_forum.cannotadddiscussionall": "Nemate dozvolu da dodajete novu temu za diskusiju za sve učesnike.", - "addon.mod_forum.cannotcreatediscussion": "Nije bilo moguće otvoriti novu diskusiju", - "addon.mod_forum.couldnotadd": "Nažalost, nije moguće dodati Vašu poruku zbog nepoznate greške", - "addon.mod_forum.couldnotupdate": "Vaša poruka ne može biti ažurirana zbog nepoznate greške", - "addon.mod_forum.cutoffdatereached": "Definitivni rok za slanje poruka na ovaj forum je dostignut tako da više ne možete da šaljete poruke.", - "addon.mod_forum.delete": "Obriši", - "addon.mod_forum.deletedpost": "Poruka je obrisana", - "addon.mod_forum.deletesure": "Jeste li sigurni da želite da obrišete ovu poruku?", - "addon.mod_forum.discussion": "Diskusija", - "addon.mod_forum.discussionlistsortbycreatedasc": "Sortirajte po datumu kreiranja rastućim redosledom", - "addon.mod_forum.discussionlistsortbycreateddesc": "Sortirajte po datumu kreiranja opadajućim redosledom", - "addon.mod_forum.discussionlistsortbylastpostasc": "Sortirajte po datumu kreiranja poslednje poruke rastućim redosledom", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Sortirajte po datumu kreiranja poslednje poruke opadajućim redosledom", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Sortirajte po broju odgovora rastućim redosledom", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Sortirajte po broju odgovora opadajućim redosledom", - "addon.mod_forum.discussionlocked": "Ova diskusija je zaključana tako da više ne možete odgovarati na njoj.", - "addon.mod_forum.discussionpinned": "Fiksirana", - "addon.mod_forum.discussionsubscription": "Pretplata na diskusiju", - "addon.mod_forum.edit": "Uredi", - "addon.mod_forum.erroremptymessage": "Telo poruke ne može biti prazno", - "addon.mod_forum.erroremptysubject": "Tema (naslov) poruke ne može biti prazna", - "addon.mod_forum.errorgetforum": "Greška prilikom preuzimanja podataka za 'Forum'", - "addon.mod_forum.errorgetgroups": "Greška prilikom preuzimanja podešavanja grupa.", - "addon.mod_forum.favouriteupdated": "Vaša opcija za označavanje zvezdicom je ažurirana.", - "addon.mod_forum.forumnodiscussionsyet": "Greška prilikom preuzimanja podešavanja grupa.", - "addon.mod_forum.group": "Grupa", - "addon.mod_forum.lastpost": "Poslednja poruka", - "addon.mod_forum.lockdiscussion": "Zaključaj ovu diskusiju", - "addon.mod_forum.lockupdated": "Opcija za zaključavanja je ažurirana.", - "addon.mod_forum.message": "Poruka", - "addon.mod_forum.modeflatnewestfirst": "Prikaz odgovora, počevši prvo s najnovijim", - "addon.mod_forum.modeflatoldestfirst": "Prikaz odgovora, počevši prvo s najstarijim", - "addon.mod_forum.modenested": "Prikaz odgovora u ugnežđenoj formi", - "addon.mod_forum.modulenameplural": "Forumi", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} diskusije/a", - "addon.mod_forum.numreplies": "{{numreplies}} odgovora", - "addon.mod_forum.pindiscussion": "Fiksiraj ovu diskusiju", - "addon.mod_forum.pinupdated": "Opcija za fiksiranje je ažurirana.", - "addon.mod_forum.postisprivatereply": "Ovo je privatni odgovor. Nije vidljiv drugim učesnicima.", - "addon.mod_forum.posttoforum": "Pošalji poruku na forum", - "addon.mod_forum.posttomygroups": "Pošaljij kopiju poruke svim grupama", - "addon.mod_forum.privatereply": "Odgovori privatno", - "addon.mod_forum.re": "Odgovor:", - "addon.mod_forum.refreshdiscussions": "Osveži diskusije", - "addon.mod_forum.refreshposts": "Osveži postove", - "addon.mod_forum.removefromfavourites": "Uklonite zvezdicu sa ove diskusije", - "addon.mod_forum.reply": "Odgovori", - "addon.mod_forum.replyplaceholder": "Napišite odgovor ...", - "addon.mod_forum.subject": "Tema", - "addon.mod_forum.tagarea_forum_posts": "Postovi na forumu", - "addon.mod_forum.thisforumhasduedate": "Krajnji rok za slanje poruka na ovaj forum je {{$a}}.", - "addon.mod_forum.thisforumisdue": "Krajnji rok za slanje poruka na ovaj forum bio je {{$a}}.", - "addon.mod_forum.unlockdiscussion": "Otključaj ovu diskusiju", - "addon.mod_forum.unpindiscussion": "Ukloni fiksiranje sa ova diskusije", - "addon.mod_forum.unread": "Nepročitano", - "addon.mod_forum.unreadpostsnumber": "Broj nepročitanih poruka: {{$a}}", - "addon.mod_forum.yourreply": "Vaš odgovor", - "addon.mod_glossary.addentry": "Dodaj novi pojam", - "addon.mod_glossary.aliases": "Ključne reči", - "addon.mod_glossary.attachment": "Prilog", - "addon.mod_glossary.browsemode": "Pregledaj pojmove", - "addon.mod_glossary.byalphabet": "Abecednim redom", - "addon.mod_glossary.byauthor": "Grupiši po autoru", - "addon.mod_glossary.bycategory": "Grupiši po kategoriji", - "addon.mod_glossary.bynewestfirst": "Najnoviji prvo", - "addon.mod_glossary.byrecentlyupdated": "Nedavno ažurirani", - "addon.mod_glossary.bysearch": "Pretraži", - "addon.mod_glossary.cannoteditentry": "Ne možete da uređujete pojam", - "addon.mod_glossary.casesensitive": "Uz razlikovanje malih i velikih slova", - "addon.mod_glossary.categories": "Kategorije", - "addon.mod_glossary.concept": "Pojam", - "addon.mod_glossary.definition": "Definicija", - "addon.mod_glossary.entriestobesynced": "Pojmovi za sinhronizaciju", - "addon.mod_glossary.entrypendingapproval": "Ovaj pojam čeka odobrenje.", - "addon.mod_glossary.entryusedynalink": "Ovaj pojam treba da bude automatski linkovan", - "addon.mod_glossary.errconceptalreadyexists": "Ovaj pojam već postoji. Duplikati nisu dozvoljeni u ovom rečniku.", - "addon.mod_glossary.errorloadingentries": "Došlo je do greške prilikom učitavanja pojmova.", - "addon.mod_glossary.errorloadingentry": "Došlo je do greške prilikom učitavanja pojma.", - "addon.mod_glossary.errorloadingglossary": "Došlo je do greške prilikom učitavanja rečnika.", - "addon.mod_glossary.fillfields": "Pojam i definicija su obavezna polja.", - "addon.mod_glossary.fullmatch": "Ako se podudaraju isključivo cele reči", - "addon.mod_glossary.linking": "Automatsko povezivanje", - "addon.mod_glossary.modulenameplural": "Rečnici", - "addon.mod_glossary.noentriesfound": "Nije pronađen nijedan pojam.", - "addon.mod_glossary.searchquery": "Upit za pretragu", - "addon.mod_glossary.tagarea_glossary_entries": "Pojmovi u rečniku", - "addon.mod_imscp.deploymenterror": "Greška prilikom učitavanja paketa!", - "addon.mod_imscp.modulenameplural": "IMS paketi", - "addon.mod_imscp.showmoduledescription": "Prikaži opis", - "addon.mod_imscp.toc": "Sadržaj", - "addon.mod_lesson.answer": "Odgovor", - "addon.mod_lesson.attempt": "Pokušaj: {{$a}}", - "addon.mod_lesson.attemptheader": "Pokušaj", - "addon.mod_lesson.attemptsremaining": "Broj pokušaja koji vam je preostao: {{$a}}", - "addon.mod_lesson.averagescore": "Prosečan broj bodova", - "addon.mod_lesson.averagetime": "Prosečno vreme", - "addon.mod_lesson.branchtable": "Sadržaj", - "addon.mod_lesson.cannotfindattempt": "Greška: nije pronađen pokušaj", - "addon.mod_lesson.cannotfinduser": "Greška: nisu pronađeni slogovi u tabeli lesson_timer", - "addon.mod_lesson.clusterjump": "Još neprikazano pitanje iz grupe pitanja", - "addon.mod_lesson.completed": "Završeno", - "addon.mod_lesson.congratulations": "Čestitamo - stigli ste do kraja lekcije", - "addon.mod_lesson.continue": "Nastavi", - "addon.mod_lesson.continuetonextpage": "Idite na sledeću stranicu", - "addon.mod_lesson.defaultessayresponse": "Predavač će oceniti vaš esej.", - "addon.mod_lesson.detailedstats": "Detaljna statistika", - "addon.mod_lesson.didnotanswerquestion": "Niste odgovorili na ovo pitanje.", - "addon.mod_lesson.displayofgrade": "Prikaz ocena (samo za polaznike)", - "addon.mod_lesson.displayscorewithessays": "

                      Osvojili ste {{$a.score}} od maksimalnih {{$a.tempmaxgrade}} bodova za pitanja koja se automatski ocenjuju.

                      \n

                      Vaš {{$a.essayquestions}} odgovor na pitanja u formi eseja biće uskoro pregledan i ocenjen, a ocena će kasnije biti dodata
                      u vaš finalni rezultat.

                      \n

                      Vaša trenutna ocena bez eseja je {{$a.score}} od {{$a.grade}}

                      ", - "addon.mod_lesson.displayscorewithoutessays": "Vaš rezultat je {{$a.score}} (od mogućih {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Polje za lozinku ne može biti prazno", - "addon.mod_lesson.enterpassword": "Molimo unesite lozinku:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Niste odgovorili ni na jedno pitanje. Vaša ocena za ovu lekciju je 0.", - "addon.mod_lesson.errorprefetchrandombranch": "Ova lekcija sadrži prelaz na nausimično odabranu stranicu sa sadržajem, pa je nije moguće uraditi u aplikaciji dok se ne pokrene na veb sajtu.", - "addon.mod_lesson.errorreviewretakenotlast": "Ova pokušaj ne može više biti pregledan zato što je završen drugi pokušaj.", - "addon.mod_lesson.finish": "Završi", - "addon.mod_lesson.finishretakeoffline": "Ovaj pokušaj je završen u oflajn režimu.", - "addon.mod_lesson.firstwrong": "Pogrešno ste odgovorili na pitanje. Da li želite da pokušate ponovo? (ako sada tačno odgovorite na pitanje, to se neće računati u vaš konačan rezultat.)", - "addon.mod_lesson.gotoendoflesson": "Idi na kraj lekcije", - "addon.mod_lesson.grade": "Ocena", - "addon.mod_lesson.highscore": "Najbolji rezultat", - "addon.mod_lesson.hightime": "Najduže vreme", - "addon.mod_lesson.leftduringtimed": "Napustili ste lekciju koja je vremenski ograničena.
                      Pritisnite taster za nastavak da biste pogledali lekciju od početka.", - "addon.mod_lesson.leftduringtimednoretake": "Napustili ste lekciju koja je vremenski ograničena i nije Vam
                      dozvoljeno da nastavite ili počnete lekciju iz početka.", - "addon.mod_lesson.lessonmenu": "Meni lekcije", - "addon.mod_lesson.lessonstats": "Statistika lekcije", - "addon.mod_lesson.linkedmedia": "Povezani mediji", - "addon.mod_lesson.loginfail": "Pogrešna prijava, molimo pokušajte ponovo...", - "addon.mod_lesson.lowscore": "Naslabiji rezultat", - "addon.mod_lesson.lowtime": "Najkraće vreme", - "addon.mod_lesson.maximumnumberofattemptsreached": "Dostignut je maksimalan broj pokušaja - prelazi se na sledeću stranicu", - "addon.mod_lesson.modattemptsnoteacher": "Polaznički pregled funkcioniše samo za polaznike", - "addon.mod_lesson.modulenameplural": "Lekcije", - "addon.mod_lesson.noanswer": "Na jedno ili više pitanja nije dat odgovor. Molimo vratite se nazad i dajte svoj odgovor.", - "addon.mod_lesson.nolessonattempts": "Nije bilo pokušaja da se prođe kroz ovu lekciju.", - "addon.mod_lesson.nolessonattemptsgroup": "Nije bilo pokušaja da se prođe kroz ovu lekciju od strane članova grupe {{$a}}.", - "addon.mod_lesson.notcompleted": "Nije završeno", - "addon.mod_lesson.numberofcorrectanswers": "Broj tačnih odgovora: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Broj pregledanih stranica: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Broj pitanja na koje je dat odgovor: {{$a.nquestions}} (Trebalo bi ih biti bar {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Do sada ste osvojili {{$a.score}} od maksimalno {{$a.currenthigh}} bodova.", - "addon.mod_lesson.ongoingnormal": "Tačno ste odgovorili na {{$a.correct}} od {{$a.viewed}} pitanja koja ste videli.", - "addon.mod_lesson.or": "ILI", - "addon.mod_lesson.overview": "Pregled", - "addon.mod_lesson.preview": "Pregled", - "addon.mod_lesson.progressbarteacherwarning2": "Nećete videti traku napredovanja zato što možete da uređujete ovu lekciju", - "addon.mod_lesson.progresscompleted": "Završili ste {{$a}}% lekcije", - "addon.mod_lesson.question": "Pitanje", - "addon.mod_lesson.rawgrade": "Neobrađena ocena", - "addon.mod_lesson.reports": "Izveštaji", - "addon.mod_lesson.response": "Povratne informacije", - "addon.mod_lesson.retakefinishedinsync": "Oflajn pokušaj je sinhronizovan. Da li želite da ga pregledate?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Pregled", - "addon.mod_lesson.reviewlesson": "Pregled lekcije", - "addon.mod_lesson.reviewquestionback": "Da, voleo/la bih da ponovo pokušam", - "addon.mod_lesson.reviewquestioncontinue": "Ne, želim da pređem na sledeće pitanje", - "addon.mod_lesson.secondpluswrong": "Nije baš. Da li želite da pokušate ponovo?", - "addon.mod_lesson.submit": "Pošalji", - "addon.mod_lesson.teacherjumpwarning": "U ovoj lekciji se koriste {{$a.cluster}} ili {{$a.unseen}} prelaz između stranica. Prelaz \"Sledeća stranica\" će se koristiti umesto njih tokom ovog prikaza. Za testiranje ovih prelaza prijavite se na sistem u ulozi polaznika.", - "addon.mod_lesson.teacherongoingwarning": "Trenutni rezultat tokom pregleda lekcije se prikazuje samo polaznicima. Prijavite se na sistem kao polaznik kako biste testirali ovu mogućnost.", - "addon.mod_lesson.teachertimerwarning": "Merač vremena mogu da vide samo polaznici. Kako biste testirali merač vremena prijavite se na sistem u ulozi polaznika.", - "addon.mod_lesson.thatsthecorrectanswer": "Tačan odgovor", - "addon.mod_lesson.thatsthewronganswer": "Pogrešan odgovor", - "addon.mod_lesson.timeremaining": "Preostalo vreme", - "addon.mod_lesson.timetaken": "Potrošeno vreme", - "addon.mod_lesson.unseenpageinbranch": "Još neprikazano pitanje unutar stranice sa sadržajem", - "addon.mod_lesson.warningretakefinished": "Pokušaj je završen na veb sajtu.", - "addon.mod_lesson.welldone": "Bravo!", - "addon.mod_lesson.youhaveseen": "Već ste videli više od jedne stranice ove lekcije.
                      Želite li početi od poslednje stranice koju ste videli?", - "addon.mod_lesson.youranswer": "Vaš odgovor", - "addon.mod_lesson.yourcurrentgradeisoutof": "Vaša trenutna ocena je {{$a.grade}} od mogućih {{$a.total}}", - "addon.mod_lesson.youshouldview": "Trebalo bi da odgovorite na barem: {{$a}}", - "addon.mod_lti.errorgetlti": "Greška prilikom preuzimanja podataka modula.", - "addon.mod_lti.errorinvalidlaunchurl": "Inicijalna URL adresa nije ispravna.", - "addon.mod_lti.launchactivity": "Pokreni aktivnost", - "addon.mod_lti.modulenameplural": "Eksterni alati", - "addon.mod_page.errorwhileloadingthepage": "Greška prilikom učitavanja sadržaja stranice.", - "addon.mod_page.modulenameplural": "Stranice", - "addon.mod_quiz.answercolon": "Odgovor:", - "addon.mod_quiz.attemptfirst": "Prvi pokušaj", - "addon.mod_quiz.attemptlast": "Poslednji pokušaj", - "addon.mod_quiz.attemptnumber": "Pokušaj", - "addon.mod_quiz.attemptquiznow": "Započni test", - "addon.mod_quiz.attemptstate": "Status", - "addon.mod_quiz.cannotsubmitquizdueto": "Ovaj pokušaj rešavanja testa ne može da bude predat zbog sledećih razloga:", - "addon.mod_quiz.clearchoice": "Obriši moj odgovor", - "addon.mod_quiz.comment": "Komentar", - "addon.mod_quiz.completedon": "Završeno dana", - "addon.mod_quiz.confirmclose": "Kada predate test nećete više biti u mogućnosti da menjate svoje odgovore.", - "addon.mod_quiz.confirmcontinueoffline": "Ovaj pokušaj nije sinhronizovan od {{$a}}. Ako ste u međuvremenu ovaj pokušaj nastavili na nekom drugom uređaju, postoji mogućnost da ste izgubili podatke.", - "addon.mod_quiz.confirmleavequizonerror": "Došlo je do greške prilikom pokušaja da se sačuvaju odgovori. Da li ste sigurni da želite da napustite test?", - "addon.mod_quiz.confirmstart": "Test ima vremensko ograničenje ({{$a}}). Vreme će se odbrojavati od momenta početka pokušaja i morate predati svoje odgovore pre nego što istekne. Da li ste sigurni da sada želite da započnete rešavanje testa?", - "addon.mod_quiz.confirmstartheader": "Vremenski ograničen test", - "addon.mod_quiz.connectionerror": "Mrežna veza je izgubljena. (Automatsko čuvanje nije uspelo).\n\nZapišite sve odgovora unetih na ovoj stranici u poslednjih nekoliko minuta, a zatim pokušajte ponovo da se povežete.\n\nKada se veza ponovo uspostavi vaši odgovori bi trebalo da budu sačuvani a ova poruka će nestati.", - "addon.mod_quiz.continueattemptquiz": "Nastavite poslednji pokušaj", - "addon.mod_quiz.continuepreview": "Nastavi poslednji pregled", - "addon.mod_quiz.errorbehaviournotsupported": "Ovaj test ne možete rešavati u aplikaciji zato što ona ne podržava ponašanje pitanja:", - "addon.mod_quiz.errordownloading": "Greška prilikom preuzimanja neophodnih podataka.", - "addon.mod_quiz.errorgetattempt": "Greška prilikom preuzimanja podataka o pokušaju rešavanja testa.", - "addon.mod_quiz.errorgetquestions": "Greška prilikom preuzimanja pitanja.", - "addon.mod_quiz.errorgetquiz": "Greška prilikom preuzimanja podataka o testu.", - "addon.mod_quiz.errorparsequestions": "Došlo je do greške prilikom učitavanja pitanja. Molimo, pokušajte da rešite ovaj test u veb čitaču.", - "addon.mod_quiz.errorquestionsnotsupported": "Ovaj test se ne može rešavati u aplikaciji zato što sadrži pitanja koje aplikacija ne podržava:", - "addon.mod_quiz.errorrulesnotsupported": "Ovaj test se ne može rešavati u aplikaciji zato što sadrži pravila za pristup koje aplikacija ne podržava:", - "addon.mod_quiz.errorsaveattempt": "Došlo je do greške prilikom snimanja podataka o pokušaju rešavanja testa.", - "addon.mod_quiz.feedback": "Povratne informacije", - "addon.mod_quiz.finishattemptdots": "Završi pokušaj...", - "addon.mod_quiz.finishnotsynced": "Test je završen, ali nije sinhronizovan", - "addon.mod_quiz.grade": "Ocena", - "addon.mod_quiz.gradeaverage": "Prosečna ocena", - "addon.mod_quiz.gradehighest": "Najviša ocena", - "addon.mod_quiz.grademethod": "Metod ocenjivanja", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Ocene", - "addon.mod_quiz.modulenameplural": "Testovi", - "addon.mod_quiz.mustbesubmittedby": "Ovaj pokušaj mora biti predat do {{$a}}.", - "addon.mod_quiz.noquestions": "Nijedno pitanje još nije dodato", - "addon.mod_quiz.noreviewattempt": "Nije Vam dozvoljeno da pregledate ovaj pokušaj", - "addon.mod_quiz.notyetgraded": "Još nije ocenjeno", - "addon.mod_quiz.opentoc": "Otvori navigacioni meni.", - "addon.mod_quiz.outof": "{{$a.grade}} od {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} od {{$a.maxgrade}}", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Sveobuhvatne povratne informacije", - "addon.mod_quiz.overdue": "Zakasneli", - "addon.mod_quiz.overduemustbesubmittedby": "Vreme za ovaj pokušaj rešavanje testa je isteklo. Pokušaj je već trebalo predati. Ako želite da ovaj test bude ocenjen morate ga predati do {{$a}}. Ako to ne uradite do navedenog roka, ocene za ovaj pokušaj se neće računati.", - "addon.mod_quiz.preview": "Pregled", - "addon.mod_quiz.previewquiznow": "Pregledaj test sada", - "addon.mod_quiz.question": "Pitanje", - "addon.mod_quiz.quiznavigation": "Navigacija testa", - "addon.mod_quiz.quizpassword": "Lozinka testa", - "addon.mod_quiz.reattemptquiz": "Ponovni pokušaj rešavanja testa", - "addon.mod_quiz.requirepasswordmessage": "Da biste pristupili rešavanju ovog testa morate znati lozinku testa", - "addon.mod_quiz.returnattempt": "Povratak na pokušaj", - "addon.mod_quiz.review": "Pregled", - "addon.mod_quiz.reviewofattempt": "Pregled pokušaja {{$a}}", - "addon.mod_quiz.reviewofpreview": "Pregled prikaza", - "addon.mod_quiz.showall": "Prikaži sva pitanja na jednoj stranici", - "addon.mod_quiz.showeachpage": "Prikaži jednu po jednu stranicu", - "addon.mod_quiz.startattempt": "Započni pokušaj rešavanja", - "addon.mod_quiz.startedon": "Započeto", - "addon.mod_quiz.stateabandoned": "Nikad predato", - "addon.mod_quiz.statefinished": "Završeno", - "addon.mod_quiz.statefinisheddetails": "Predato {{$a}}", - "addon.mod_quiz.stateinprogress": "U toku", - "addon.mod_quiz.stateoverdue": "Zakasnela predaja", - "addon.mod_quiz.stateoverduedetails": "Mora se predati do {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Predaj sve odgovore i završi test", - "addon.mod_quiz.summaryofattempt": "Rezime pokušaja", - "addon.mod_quiz.summaryofattempts": "Rezime vaših prethodnih pokušaja", - "addon.mod_quiz.timeleft": "Preostalo vreme", - "addon.mod_quiz.timetaken": "Utrošeno vreme", - "addon.mod_quiz.warningattemptfinished": "Oflajn pokušaj je odbačen zato što je ili završen na sajtu ili nije pronađen.", - "addon.mod_quiz.warningdatadiscarded": "Neki oflajn odgovori su odbačeni zato što su pitanja izmenjena onlajn.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Pokušaj rešavanja testa je nije završen zato što su neki oflajn odgovori odbačeni. Pregledajte svoje odgovore, a zatim ponovo pošaljite svoj pokušaj.", - "addon.mod_quiz.yourfinalgradeis": "Vaša konačna ocena na ovom testu je {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Greška prilikom učitavanja sadržaja.", - "addon.mod_resource.modifieddate": "Izmenjeno {{$a}}", - "addon.mod_resource.modulenameplural": "Datoteke", - "addon.mod_resource.openthefile": "Otvori datoteku", - "addon.mod_resource.uploadeddate": "Postavljeno {{$a}}", - "addon.mod_scorm.asset": "Element", - "addon.mod_scorm.assetlaunched": "Element - Pregledano", - "addon.mod_scorm.attempts": "Pokušaji", - "addon.mod_scorm.averageattempt": "Prosečni broj pokušaja", - "addon.mod_scorm.browse": "Pregled", - "addon.mod_scorm.browsed": "Pretraženo", - "addon.mod_scorm.browsemode": "Režim pregleda", - "addon.mod_scorm.cannotcalculategrade": "Ocena ne može da se izračuna.", - "addon.mod_scorm.completed": "Završeno", - "addon.mod_scorm.contents": "Sadržaj", - "addon.mod_scorm.dataattemptshown": "Ovi podaci pripadaju pokušaju broj {{number}}.", - "addon.mod_scorm.enter": "Uđi", - "addon.mod_scorm.errorcreateofflineattempt": "Došlo je do greške prilikom pokušaja kreiranja novog oflajn pokušaja. Molimo, pokušajte ponovo.", - "addon.mod_scorm.errordownloadscorm": "Greška prilikom preuzimanja SCORM paketa: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Greška prilikom preuzimanja podataka SCORM paketa.", - "addon.mod_scorm.errorinvalidversion": "Izvinite, aplikacija podržava samo SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Preuzimanje SCORM paketa je onemogućeno na vašem sajtu. Molimo, obratite se administratoru vašeg Moodle sajta.", - "addon.mod_scorm.errornovalidsco": "Ovaj SCORM paket nema vidljiv SCO koji bi bio učitan.", - "addon.mod_scorm.errorpackagefile": "Izvinite, aplikacija podržava samo ZIP arhive.", - "addon.mod_scorm.errorsyncscorm": "Došlo je do greške prilikom sinhronizacije. Molimo, pokušajte ponovo.", - "addon.mod_scorm.exceededmaxattempts": "Dostigli ste maksimalan broj pokušaja", - "addon.mod_scorm.failed": "Nepoložen", - "addon.mod_scorm.firstattempt": "Prvi pokušaj", - "addon.mod_scorm.gradeaverage": "Prosečna ocena", - "addon.mod_scorm.gradeforattempt": "Ocena za pokušaj", - "addon.mod_scorm.gradehighest": "Najviša ocena", - "addon.mod_scorm.grademethod": "Metod ocenjivanja", - "addon.mod_scorm.gradereported": "Izveštaj o ocenama", - "addon.mod_scorm.gradescoes": "Objekti za učenje", - "addon.mod_scorm.gradesum": "Konačna ocena", - "addon.mod_scorm.highestattempt": "Najbolji pokušaj", - "addon.mod_scorm.incomplete": "Nepotpuno", - "addon.mod_scorm.lastattempt": "Poslednji završeni pokušaj", - "addon.mod_scorm.modulenameplural": "SCORM paketi", - "addon.mod_scorm.newattempt": "Počni novi pokušaj", - "addon.mod_scorm.noattemptsallowed": "Broj dozvoljenih pokušaja", - "addon.mod_scorm.noattemptsmade": "Broj pokušaja koji ste imali", - "addon.mod_scorm.notattempted": "Nije pokušavano", - "addon.mod_scorm.offlineattemptnote": "Ovaj pokušaj ima podatke koji nisu sinhronizovan.", - "addon.mod_scorm.offlineattemptovermax": "Ovaj pokušaj ne može biti poslat zato što ste premašili maksimalan broj dozvoljenih pokušaja.", - "addon.mod_scorm.organizations": "Organizacije", - "addon.mod_scorm.passed": "Položeno", - "addon.mod_scorm.reviewmode": "Režim prikaza", - "addon.mod_scorm.score": "Rezultat", - "addon.mod_scorm.scormstatusnotdownloaded": "Ovaj SCORM paket nije preuzet. Biće automatski preuzet kada ga otvorite.", - "addon.mod_scorm.scormstatusoutdated": "Ovaj SCORM paket je menjan od poslednjeg preuzimanja. Biće automatski preuzet kada ga otvorite.", - "addon.mod_scorm.suspended": "Suspendovano", - "addon.mod_scorm.toc": "Sadržaj", - "addon.mod_scorm.warningofflinedatadeleted": "Neki oflajn podaci o pokušaju {{number}} su obrisani zato što ih nije moguće kreirati u novom pokušaju.", - "addon.mod_scorm.warningsynconlineincomplete": "Neki pokušaji nisu mogli biti sinhronizovani sa sajtom zato što poslednji online pokušaj nije završen. Molimo vas da prvo završite onlajn pokušaj.", - "addon.mod_survey.cannotsubmitsurvey": "Nažalost, bilo je problema sa predajom vašeg upitnika. Molim vas, pokušajte ponovo.", - "addon.mod_survey.errorgetsurvey": "Greška prilikom preuzimanja podataka za 'Upitnik' (Survey)", - "addon.mod_survey.ifoundthat": "Otkrio/la sam da", - "addon.mod_survey.ipreferthat": "Preferiram", - "addon.mod_survey.modulenameplural": "Upitnici (Surveys)", - "addon.mod_survey.responses": "Odgovori", - "addon.mod_survey.results": "Rezultati", - "addon.mod_survey.surveycompletednograph": "Ispunili ste anketu.", - "addon.mod_url.accessurl": "Pristupi URL adresi", - "addon.mod_url.modulenameplural": "URL adrese", - "addon.mod_url.pointingtourl": "URL adresa sa kojom je ovaj resurs povezan", - "addon.mod_wiki.cannoteditpage": "Ne možete da uređujete ovu stranicu.", - "addon.mod_wiki.createpage": "Kreiraj stranicu", - "addon.mod_wiki.editingpage": "Uređivanje ove stranice '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Došlo je do greške prilikom učitavanja stranice.", - "addon.mod_wiki.errornowikiavailable": "Ovaj viki još uvek nema sadržaj.", - "addon.mod_wiki.gowikihome": "Idi na početnu stranicu vikija", - "addon.mod_wiki.map": "Mapa", - "addon.mod_wiki.modulenameplural": "Wikiji", - "addon.mod_wiki.newpagehdr": "Nova stranica", - "addon.mod_wiki.newpagetitle": "Naslov nove stranice", - "addon.mod_wiki.nocontent": "Na ovoj stranici nema sadržaja", - "addon.mod_wiki.notingroup": "Nije u grupi", - "addon.mod_wiki.pageexists": "Ova stranica već postoji.", - "addon.mod_wiki.pagename": "Naziv stranice", - "addon.mod_wiki.subwiki": "Podviki", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki stranice", - "addon.mod_wiki.titleshouldnotbeempty": "Naslov ne bi trebalo da bude prazan", - "addon.mod_wiki.viewpage": "Pogledaj stranicu", - "addon.mod_wiki.wikipage": "Viki stranica", - "addon.mod_wiki.wrongversionlock": "Drugi korisnik je uređivao ovu stranicu u isto vreme kada i vi. Vaš sadržaj je zastareo.", - "addon.mod_workshop.alreadygraded": "Već ocenjeno", - "addon.mod_workshop.areainstructauthors": "Uputstva za predaju radova", - "addon.mod_workshop.areainstructreviewers": "Uputstva za procenu", - "addon.mod_workshop.assess": "Procenite", - "addon.mod_workshop.assessedsubmission": "Procenjen predati rad", - "addon.mod_workshop.assessmentform": "Obrazac za procenu", - "addon.mod_workshop.assessmentsettings": "Podešavanja procene", - "addon.mod_workshop.assessmentstrategynotsupported": "Strategija procene {{$a}} nije podržana", - "addon.mod_workshop.assessmentweight": "Ponder procene", - "addon.mod_workshop.assignedassessments": "Predati radovi dodeljeni za procenu", - "addon.mod_workshop.assignedassessmentsnone": "Nije vam dodеljen nijedan rad za procenu", - "addon.mod_workshop.conclusion": "Zaključak", - "addon.mod_workshop.createsubmission": "Započnite pripremu za predaju svog radaPredaj", - "addon.mod_workshop.deletesubmission": "Obriši predati rad", - "addon.mod_workshop.editsubmission": "Uredi predati rad", - "addon.mod_workshop.feedbackauthor": "Povratne informacije za autora", - "addon.mod_workshop.feedbackby": "Povratne informacije koje je dao/la {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Povratne informacije za recenzenta", - "addon.mod_workshop.givengrades": "Date ocene", - "addon.mod_workshop.gradecalculated": "Izračunata ocena za predati rad", - "addon.mod_workshop.gradeinfo": "Ocena: {{$a.received}} od {{$a.max}}", - "addon.mod_workshop.gradeover": "Promeni prethodnu ocenu za predati rad", - "addon.mod_workshop.gradesreport": "Izveštaj o ocenama radionice", - "addon.mod_workshop.gradinggrade": "Ocena za evaluaciju", - "addon.mod_workshop.gradinggradecalculated": "Izračunata ocena za obavljenu procenu", - "addon.mod_workshop.gradinggradeof": "Ocena za evaluaciju (od {{$a}})", - "addon.mod_workshop.gradinggradeover": "Izmeni ocenu za obavljenu procenu", - "addon.mod_workshop.modulenameplural": "Radionice", - "addon.mod_workshop.nogradeyet": "Još nema ocena", - "addon.mod_workshop.notassessed": "Rad još nije procenjen", - "addon.mod_workshop.notoverridden": "Nije poništeno", - "addon.mod_workshop.noyoursubmission": "Još niste predali svoj rad", - "addon.mod_workshop.overallfeedback": "Sveobuhvatne povratne informacija", - "addon.mod_workshop.publishedsubmissions": "Objavljeni radovi", - "addon.mod_workshop.publishsubmission": "Objavi rad", - "addon.mod_workshop.publishsubmission_help": "Objavljeni radovi su dostupni drugim korisnicima nakon zatvaranja radionice", - "addon.mod_workshop.reassess": "Ponovno procenjivanje", - "addon.mod_workshop.receivedgrades": "Dobijene ocene", - "addon.mod_workshop.submissionattachment": "Prilog", - "addon.mod_workshop.submissioncontent": "Sadržaj predatog rada", - "addon.mod_workshop.submissiondeleteconfirm": "Da li ste sigurni da želite da obrišete sledeći predati rad?", - "addon.mod_workshop.submissiongrade": "Ocena predatog rada", - "addon.mod_workshop.submissiongradeof": "Ocena za predati rad (od {{$a}})", - "addon.mod_workshop.submissionrequiredcontent": "Morate uneti tekst ili dodati datoteku.", - "addon.mod_workshop.submissionrequiredtitle": "Morate da unesete naslov.", - "addon.mod_workshop.submissionsreport": "Izveštaj o predatim radovima", - "addon.mod_workshop.submissiontitle": "Naslov", - "addon.mod_workshop.switchphase10": "Prebaci na fazu podešavanja", - "addon.mod_workshop.switchphase20": "Prebaci na fazu predaje rada", - "addon.mod_workshop.switchphase30": "Prebaci na fazu procene", - "addon.mod_workshop.switchphase40": "Prebaci na fazu evaluacije", - "addon.mod_workshop.switchphase50": "Zatvori radionicu", - "addon.mod_workshop.userplan": "Planer radionice", - "addon.mod_workshop.userplancurrentphase": "Trenutna faza", - "addon.mod_workshop.warningassessmentmodified": "Predati rad je izmenjen na sajtu.", - "addon.mod_workshop.warningsubmissionmodified": "Procena je izmenjena na sajtu.", - "addon.mod_workshop.weightinfo": "Ponder: {{$a}}", - "addon.mod_workshop.yourassessment": "Vaša procena", - "addon.mod_workshop.yourassessmentfor": "Vaša procena za {{$a}}", - "addon.mod_workshop.yourgrades": "Vaše ocene", - "addon.mod_workshop.yoursubmission": "Vaš rad", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Ocena za {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Aspekt {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Morate da odaberete ocenu za ovaj aspekt", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Aspekt {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Komentar za {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Ocena za {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Preduslov {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kriterijum {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Morate da izaberete jednu od ovih stavki", - "addon.notes.addnewnote": "Dodaj belešku", - "addon.notes.coursenotes": "Beleške kursa", - "addon.notes.deleteconfirm": "Obriši belešku", - "addon.notes.eventnotecreated": "Beleška kreirana", - "addon.notes.eventnotedeleted": "Beleška obrisana", - "addon.notes.nonotes": "Još nema beleški ovog tipa", - "addon.notes.note": "Beleška", - "addon.notes.notes": "Beleške", - "addon.notes.personalnotes": "Lične poruke", - "addon.notes.publishstate": "Kontekst", - "addon.notes.sitenotes": "Beleške sa sajta", - "addon.notes.userwithid": "Korisnik sa ID oznakom {{id}}", - "addon.notes.warningnotenotsent": "Nije moguće dodati belešku/e kursu {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Greška prilikom preuzimanja obaveštenja", - "addon.notifications.markallread": "Označi sve kao pročitano", - "addon.notifications.notificationpreferences": "Parametri obaveštenja", - "addon.notifications.notifications": "Obaveštenja", - "addon.notifications.playsound": "Reprodukuj zvuk", - "addon.notifications.therearentnotificationsyet": "Nema obaveštenja", - "assets.countries.AD": "Andora", - "assets.countries.AE": "Ujedinjeni Arapski Emirati", - "assets.countries.AF": "Afganistan", - "assets.countries.AG": "Antigva i Barbuda", - "assets.countries.AI": "Angvila", - "assets.countries.AL": "Albanija", - "assets.countries.AM": "Jermenija", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktik", - "assets.countries.AR": "Argentina", - "assets.countries.AS": "Američka Samoa", - "assets.countries.AT": "Austrija", - "assets.countries.AU": "Australija", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Olandska Ostrva", - "assets.countries.AZ": "Azerbejdžan", - "assets.countries.BA": "Bosna i Hercegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladeš", - "assets.countries.BE": "Belgija", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bugarska", - "assets.countries.BH": "Bahrein", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Ostrvo Sveti Bartolomej", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunej", - "assets.countries.BO": "Bolivija, Višenacionalna Država", - "assets.countries.BQ": "Boner, Sveti Eustahije i Saba", - "assets.countries.BR": "Brazil", - "assets.countries.BS": "Bahami", - "assets.countries.BT": "Butan", - "assets.countries.BV": "Buve Ostrvo", - "assets.countries.BW": "Bocvana", - "assets.countries.BY": "Belorusija", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Kokosova Ostrva", - "assets.countries.CD": "Kongo, Demokratska Republika", - "assets.countries.CF": "Centralnoafrička Republika", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Švajcarska", - "assets.countries.CI": "Obala Slonovače", - "assets.countries.CK": "Kukova Ostrva", - "assets.countries.CL": "Čile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Kina", - "assets.countries.CO": "Kolumbija", - "assets.countries.CR": "Kostarika", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Zelenortska ostrva", - "assets.countries.CW": "Kurasao", - "assets.countries.CX": "Božićna ostrva", - "assets.countries.CY": "Kipar", - "assets.countries.CZ": "Češka", - "assets.countries.DE": "Nemačka", - "assets.countries.DJ": "Džibuti", - "assets.countries.DK": "Danska", - "assets.countries.DM": "Dominka", - "assets.countries.DO": "Dominikanska Republika", - "assets.countries.DZ": "Alžir", - "assets.countries.EC": "Ekvador", - "assets.countries.EE": "Estonija", - "assets.countries.EG": "Egipat", - "assets.countries.EH": "Zapadna Sahara", - "assets.countries.ER": "Eritreja", - "assets.countries.ES": "Španija", - "assets.countries.ET": "Etiopija", - "assets.countries.FI": "Finska", - "assets.countries.FJ": "Fidži", - "assets.countries.FK": "Foklandska Ostrva", - "assets.countries.FM": "Savezne Države Mikronezije", - "assets.countries.FO": "Farska Ostrva", - "assets.countries.FR": "Francuska", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Velika Britanija", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Gruzija", - "assets.countries.GF": "Francuska Gvineja", - "assets.countries.GG": "Gernzi", - "assets.countries.GH": "Gana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grenland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Gvineja", - "assets.countries.GP": "Gvadalupe", - "assets.countries.GQ": "Ekvatorijalna Gvineja", - "assets.countries.GR": "Grčka", - "assets.countries.GS": "Južna Džordžija i Južna Sendvička Ostrva", - "assets.countries.GT": "Gvatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Gvineja-Bisao", - "assets.countries.GY": "Gvajana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Ostrvo Herd i arhipelag Makdonald", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Hrvatska", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Mađarska", - "assets.countries.ID": "Indonezija", - "assets.countries.IE": "Irska", - "assets.countries.IL": "Izrael", - "assets.countries.IM": "Ostrvo Man", - "assets.countries.IN": "Indija", - "assets.countries.IO": "Britanska Indijska Okeanska Teritorija", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran, Islamska Republika", - "assets.countries.IS": "Island", - "assets.countries.IT": "Italija", - "assets.countries.JE": "Džersi", - "assets.countries.JM": "Jamajka", - "assets.countries.JO": "Jordan", - "assets.countries.JP": "Japan", - "assets.countries.KE": "Kenija", - "assets.countries.KG": "Kirgistan", - "assets.countries.KH": "Kambodža", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komori", - "assets.countries.KN": "Sveti Kristofor i Nevis", - "assets.countries.KP": "Severna Koreja, Demokratska Narodna Republika", - "assets.countries.KR": "Južna Koreja, Republika", - "assets.countries.KW": "Kuvajt", - "assets.countries.KY": "Kajmanska Ostrva", - "assets.countries.KZ": "Kazahstan", - "assets.countries.LA": "Laos, Narodna Demokratska Republika", - "assets.countries.LB": "Liban", - "assets.countries.LC": "Sveta Lucija", - "assets.countries.LI": "Lihtenštajn", - "assets.countries.LK": "Šri Lanka", - "assets.countries.LR": "Liberija", - "assets.countries.LS": "Lesoto", - "assets.countries.LT": "Litvanija", - "assets.countries.LU": "Luksemburg", - "assets.countries.LV": "Letonija", - "assets.countries.LY": "Libija", - "assets.countries.MA": "Maroko", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Moldavija, Republika", - "assets.countries.ME": "Crna Gora", - "assets.countries.MF": "Sveti Martin (francuski deo)", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Maršalska Ostrva", - "assets.countries.MK": "Severna Makedonija", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Mjanmar", - "assets.countries.MN": "Mongolija", - "assets.countries.MO": "Makao", - "assets.countries.MP": "Severna Marijanska Ostrva", - "assets.countries.MQ": "Martinik", - "assets.countries.MR": "Mauritanija", - "assets.countries.MS": "Montserat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauricijus", - "assets.countries.MV": "Maldivi", - "assets.countries.MW": "Malavi", - "assets.countries.MX": "Meksiko", - "assets.countries.MY": "Malezija", - "assets.countries.MZ": "Mozambik", - "assets.countries.NA": "Namibija", - "assets.countries.NC": "Nova Kaledonija", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolška Ostrva", - "assets.countries.NG": "Nigerija", - "assets.countries.NI": "Nikaragva", - "assets.countries.NL": "Holandija", - "assets.countries.NO": "Norveška", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Novi Zeland", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Francuska Polinezija", - "assets.countries.PG": "Papua Nova Gvineja", - "assets.countries.PH": "Filipini", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Poljska", - "assets.countries.PM": "Sveti Pjer i Mikelon", - "assets.countries.PN": "Pitkairn", - "assets.countries.PR": "Portoriko", - "assets.countries.PS": "Palestina, Država", - "assets.countries.PT": "Portugalija", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paragvaj", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Reunion", - "assets.countries.RO": "Rumunija", - "assets.countries.RS": "Srbija", - "assets.countries.RU": "Ruska Federacija", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Saudijska Arabija", - "assets.countries.SB": "Solomonova Ostrva", - "assets.countries.SC": "Sejšeli", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Švedska", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Sveta Jelena, Asension i Tristan da Kunja", - "assets.countries.SI": "Slovenija", - "assets.countries.SJ": "Svalbard i Jan Majen", - "assets.countries.SK": "Slovačka", - "assets.countries.SL": "Sijera Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalija", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Južni Sudan", - "assets.countries.ST": "Sao Tome i Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sveti Martin (holandski deo)", - "assets.countries.SY": "Sirija", - "assets.countries.SZ": "Svaziland", - "assets.countries.TC": "Ostrva Turks i Kajkos", - "assets.countries.TD": "Čad", - "assets.countries.TF": "Francuske Južne Teritorije", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tajland", - "assets.countries.TJ": "Tadžikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Istočni Timor", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunis", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turska", - "assets.countries.TT": "Trinidad i Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Tajvan", - "assets.countries.TZ": "Tanzanija, Ujedinjena Republika", - "assets.countries.UA": "Ukrajina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "Mala Udaljena Ostrva Sjedinjenih Država", - "assets.countries.US": "Sjedinjene Američke Države", - "assets.countries.UY": "Urugvaj", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Vatikan", - "assets.countries.VC": "Sveti Vinsent i Grenadini", - "assets.countries.VE": "Venecuela", - "assets.countries.VG": "Britanska Devičanska ostrva", - "assets.countries.VI": "Američka Devičanska Ostrva", - "assets.countries.VN": "Vijetnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Valis i Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Majote", - "assets.countries.ZA": "Južna Afrika", - "assets.countries.ZM": "Zambija", - "assets.countries.ZW": "Zimbabve", - "assets.mimetypes.application/epub_zip": "EPUB e-knjiga", - "assets.mimetypes.application/msword": "Word dokument", - "assets.mimetypes.application/pdf": "PDF dokument", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle rezervna kopija", - "assets.mimetypes.application/vnd.ms-excel": "Excel radna tabela", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 radna sveska sa aktiviranim makro naredbama", - "assets.mimetypes.application/vnd.ms-powerpoint": "PowerPoint prezentacija", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument radna tabela", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Šablon OpenDocument radne tabele", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument tekstualni dokument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument tekstualni šablon", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Šablon OpenDocument veb stranice", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 prezentacija", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 slajd-šou", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 radna tabela", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 šablon", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 dokument", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote prezentacija", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers radna tabela", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages dokument", - "assets.mimetypes.application/x-javascript": "JavaScript izvorni kôd", - "assets.mimetypes.application/x-mspublisher": "Publisher dokument", - "assets.mimetypes.application/x-shockwave-flash": "Flash animacija", - "assets.mimetypes.application/xhtml_xml": "XHTML dokument", - "assets.mimetypes.archive": "Arhiva ({{$a.EXT}})", - "assets.mimetypes.audio": "Audio datoteka ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Datoteka", - "assets.mimetypes.group:archive": "Arhivirane datoteke", - "assets.mimetypes.group:audio": "Audio datoteke", - "assets.mimetypes.group:document": "Dokumenti", - "assets.mimetypes.group:html_audio": "Audio datoteke originalno podržane od veb čitača", - "assets.mimetypes.group:html_track": "HTML WebVTT datoteke", - "assets.mimetypes.group:html_video": "Video datoteke originalno podržane od veb čitača", - "assets.mimetypes.group:image": "Slike", - "assets.mimetypes.group:presentation": "Prezentacije", - "assets.mimetypes.group:sourcecode": "Izvorni kôd", - "assets.mimetypes.group:spreadsheet": "Radne tabele", - "assets.mimetypes.group:video": "Video datoteke", - "assets.mimetypes.group:web_audio": "Audio datoteke koje se koriste na vebu", - "assets.mimetypes.group:web_file": "Veb datoteke", - "assets.mimetypes.group:web_image": "Datoteke sa slikama koje se koriste na vebu", - "assets.mimetypes.group:web_video": "Video datoteke koje se koriste na vebu", - "assets.mimetypes.image": "Slika ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows ikonica", - "assets.mimetypes.text/css": "CSS (Kaskadni opis stilova)", - "assets.mimetypes.text/csv": "CSV (Vrednosti razdvojene zarezima)", - "assets.mimetypes.text/html": "HTML dokument", - "assets.mimetypes.text/plain": "Tekstualna datoteka", - "assets.mimetypes.text/rtf": "RTF dokument", - "assets.mimetypes.text/vtt": "Web Video Text Track format", - "assets.mimetypes.video": "Video datoteka ({{$a.EXT}})", - "core.accounts": "Nalozi", - "core.add": "Dodaj", - "core.agelocationverification": "Provera starosti i mesta", - "core.ago": "{{$a}} pre", - "core.all": "Sve", - "core.allgroups": "Sve grupe", - "core.allparticipants": "Svi učesnici", - "core.answer": "Odgovor", - "core.answered": "Odgovoreno", - "core.areyousure": "Da li ste sigurni?", - "core.back": "Nazad", - "core.block.blocks": "Blokovi", - "core.cancel": "Odustani", - "core.cannotconnect": "Nije moguće uspostaviti vezu. Proverite da li ste uneli ispravnu URL adresu i da li vaš sajt koristi Moodle {{$a}} ili noviju verziju.", - "core.cannotdownloadfiles": "Preuzimanje datoteka je onemogućeno u podešavanjima vašeg mobilnog servisa. Obratite se administratoru sajta.", - "core.captureaudio": "Snimi audio", - "core.capturedimage": "Snimljena slika", - "core.captureimage": "Uslikaj", - "core.capturevideo": "Snimi video", - "core.category": "Kategorija", - "core.choose": "Izaberi", - "core.choosedots": "Izaberi...", - "core.clearsearch": "Obriši pretragu", - "core.clicktohideshow": "Kliknite da biste raširili ili skupili", - "core.clicktoseefull": "Kliknite da biste videli kompletan sadržaj.", - "core.close": "Zatvori", - "core.comments": "Komentari", - "core.comments.addcomment": "Dodaj komentar...", - "core.comments.comments": "Komentari", - "core.comments.commentscount": "Komentari ({{$a}})", - "core.comments.deletecommentbyon": "Obriši komentar koji je poslao/la {{$a.user}} u {{$a.time}}", - "core.comments.eventcommentcreated": "Komentar kreiran", - "core.comments.eventcommentdeleted": "Komentar obrisan", - "core.comments.nocomments": "Nema komentara", - "core.comments.savecomment": "Sačuvaj komentar", - "core.commentscount": "Komentari ({{$a}})", - "core.completion-alt-auto-fail": "Završeno: {{$a}} (nije postigao/la prelaznu ocenu)", - "core.completion-alt-auto-n": "Nije završeno: {{$a}}", - "core.completion-alt-auto-n-override": "Nije završeno: {{$a.modname}} (podesio/la {{$a.overrideuser}})", - "core.completion-alt-auto-pass": "Završeno: {{$a}} (postigao/la prelaznu ocenu)", - "core.completion-alt-auto-y": "Završeno: {{$a}}", - "core.completion-alt-auto-y-override": "Završeno: {{$a.modname}} (podesio/la {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Nije završeno: {{$a}}. Izaberi da bi označio kao završeno", - "core.completion-alt-manual-n-override": "Nije završeno: {{$a.modname}} (podesio/la {{$a.overrideuser}}). Izaberite da biste označili kao završeno.", - "core.completion-alt-manual-y": "Završeno: {{$a}}. Izaberi da bi označio kao nezavršeno.", - "core.completion-alt-manual-y-override": "Završeno: {{$a.modname}} (podesio/la {{$a.overrideuser}}). Izaberite da biste označili kao nezavršeno.", - "core.confirmcanceledit": "Da li ste sigurni da želite da napustite ovu stranicu? Sve promene će biti izgubljene.", - "core.confirmdeletefile": "Da li ste sigurni da želite da obrišete ovu datoteteku?", - "core.confirmloss": "Da li ste sigurni? Sve promene će biti izgubljene.", - "core.confirmopeninbrowser": "Da li želite da otvorite u veb čitaču?", - "core.considereddigitalminor": "Previše ste mladi da biste mogli da kreirate nalog na ovom sajtu.", - "core.content": "Sadržaj", - "core.contenteditingsynced": "Sadržaj koji uređujete je sinhronizovan.", - "core.contentlinks.chooseaccount": "Izaberi nalog", - "core.contentlinks.chooseaccounttoopenlink": "Izaberite nalog sa kojim treba otvoriti link.", - "core.contentlinks.confirmurlothersite": "Ovaj link pripada drugom sajtu. Da li želite da ga otvorite?", - "core.contentlinks.errornoactions": "Nije moguće pronaći akciju koju treba izvesti sa ovim linkom.", - "core.contentlinks.errornosites": "Nije moguće pronaći bilo koji sajt koji može izaći na kraj sa ovim linkom.", - "core.continue": "Nastavi", - "core.copiedtoclipboard": "Tekst kopiran u klipbord", - "core.course": "Kurs", - "core.course.activitydisabled": "Vaša institucija je onemogućila ovu aktivnost u mobilnoj aplikaciji.", - "core.course.activitynotyetviewableremoteaddon": "Vaša institucija je instalirala dodatak koji još uvek nije podržan.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Potrebno je da se ažurira Moodle instalacija vaše institucije.", - "core.course.allsections": "Sve sekcije", - "core.course.askadmintosupport": "Obratite se administratoru sajta i recite mu da želite da koristite ovu aktivnost sa Moodle Mobile aplikacijom.", - "core.course.confirmdeletemodulefiles": "Da li ste sigurni da želite da izbrišete datoteke ovog modula?", - "core.course.confirmdownload": "Nameravate da preuzmete {{size}}. Da li ste sigurni da želite da nastavite?", - "core.course.confirmdownloadunknownsize": "Nismo mogli da izračunamo veličinu preuzimanje. Da li ste sigurni da želite da preuzmete?", - "core.course.confirmpartialdownloadsize": "Nameravate da preuzmete najmanje {{size}}. Da li ste sigurni da želite da nastavite?", - "core.course.contents": "Sadržaji", - "core.course.couldnotloadsectioncontent": "Nije moguće učitati sadržaj sekcije, pokušajte ponovo kasnije.", - "core.course.couldnotloadsections": "Nije moguće učitati sekcije, pokušajte ponovo kasnije.", - "core.course.coursesummary": "Rezime kursa", - "core.course.downloadcourse": "Preuzmi kurs", - "core.course.errordownloadingcourse": "Greška prilikom preuzimanju kursa.", - "core.course.errordownloadingsection": "Greška prilikom preuzimanja sekcije.", - "core.course.errorgetmodule": "Greška prilikom preuzimanja podataka modula.", - "core.course.hiddenfromstudents": "Sakriveno od polaznika", - "core.course.hiddenoncoursepage": "Dostupno ali se ne prikazuje na stranici kursa", - "core.course.manualcompletionnotsynced": "Ručni završetak nije sinhronizovan.", - "core.course.nocontentavailable": "Nikakav sadržaj nije dostupan u ovom trenutku.", - "core.course.overriddennotice": "Vaša završna ocena za ovu aktivnost je ručno podešena", - "core.course.refreshcourse": "Osveži kurs", - "core.course.sections": "Sekcije", - "core.course.useactivityonbrowser": "Još uvek možete da ga koristite pomoću veb čitača vašeg uređaja.", - "core.course.warningmanualcompletionmodified": "Ručni završetak aktivnosti je izmenjen na sajtu.", - "core.course.warningofflinemanualcompletiondeleted": "Neki oflajn ručni završeci kursa '{{name}}' su obrisani. {{error}}", - "core.coursedetails": "Podaci o kursevima", - "core.courses.addtofavourites": "Označite ovaj kurs zvezdicom", - "core.courses.allowguests": "Pristup kursu je dozvoljen gostima", - "core.courses.availablecourses": "Dostupni kursevi", - "core.courses.cannotretrievemorecategories": "Kategorije koje su ispod nivoa {{$a}} ne mogu se preuzeti.", - "core.courses.categories": "Kategorije kurseva", - "core.courses.confirmselfenrol": "Da li ste sigurni da želite da se upišete na ovaj kurs?", - "core.courses.courses": "Kursevi", - "core.courses.downloadcourses": "Preuzmi kurs", - "core.courses.enrolme": "Upiši me", - "core.courses.errorloadcategories": "Došlo je do greške prilikom učitavanja kategorija.", - "core.courses.errorloadcourses": "Došlo je do greške prilikom učitavanja kurseva.", - "core.courses.errorsearching": "Došlo je do greške prilikom pretraživanja.", - "core.courses.errorselfenrol": "Došlo je do greške prilikom pokušaja samostalnog upisa.", - "core.courses.filtermycourses": "Filtriraj moje kurseve", - "core.courses.frontpage": "Naslovna stranica", - "core.courses.hidecourse": "Sakrij iz prikaza", - "core.courses.ignore": "Ignoriši", - "core.courses.mycourses": "Moji kursevi", - "core.courses.mymoodle": "Kontrolni panel", - "core.courses.nocourses": "Nema informacija o kursu koje bi se mogle prikazati.", - "core.courses.nocoursesyet": "Nema kurseva u ovoj kategoriji", - "core.courses.nosearchresults": "Nema rezultata", - "core.courses.notenroled": "Niste upisani na ovaj kurs", - "core.courses.notenrollable": "Ne možete sami da se upišete na ovaj kurs.", - "core.courses.password": "Pristupna lozinka kursa", - "core.courses.paymentrequired": "Ovaj kurs nije besplatan.", - "core.courses.paypalaccepted": "Prihvaćene su Paypal uplate", - "core.courses.reload": "Učitaj ponovo", - "core.courses.removefromfavourites": "Ukloni zvezdicu sa ovog kursa", - "core.courses.search": "Pretraga", - "core.courses.searchcourses": "Pretraži kurseve", - "core.courses.searchcoursesadvice": "Možete koristiti dugme za pretragu kurseva kako biste im pristupili kao gost ili se upisali na kurseve koji to dopuštaju.", - "core.courses.selfenrolment": "Samostalni upis", - "core.courses.sendpaymentbutton": "Pošalji uplatu putem Paypala", - "core.courses.show": "Pokaži ovaj kurs", - "core.courses.totalcoursesearchresults": "Ukupno kurseva: {{$a}}", - "core.currentdevice": "Trenutni uređaj", - "core.datastoredoffline": "Podaci su sačuvani u mobilnom uređaju, zato što ne mogu da se pošalju. Automatski će biti poslati kasnije.", - "core.date": "Datum", - "core.day": "dan", - "core.days": "dan/a", - "core.decsep": ",", - "core.defaultvalue": "Podrazumevano ({{$a}})", - "core.delete": "Obriši", - "core.deletedoffline": "Obrisano oflajn", - "core.deleteduser": "Obrisan korisnik", - "core.deleting": "Brisanje", - "core.description": "Opis", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "Digitalni maloletnik", - "core.digitalminor_desc": "Zamolite svog roditelja/staratelja da kontaktira:", - "core.discard": "Odbaci", - "core.dismiss": "Obustavi", - "core.displayoptions": "Opcije prikaza", - "core.done": "Urađeno", - "core.download": "Preuzmi", - "core.downloading": "Preuzimanje", - "core.edit": "Uredi", - "core.editor.autosavesucceeded": "Radna verzija je sačuvana.", - "core.editor.bold": "Podebljano", - "core.editor.clear": "Obriši formatiranje", - "core.editor.h3": "Naslov (veliki)", - "core.editor.h4": "Naslov (srednji)", - "core.editor.h5": "Naslov (mali)", - "core.editor.italic": "Kurziv", - "core.editor.orderedlist": "Uređena lista", - "core.editor.p": "Pasus", - "core.editor.strike": "Precrtavanje", - "core.editor.textrecovered": "Radna verzija ovog teksta je automatski restaurirana.", - "core.editor.underline": "Podvučeno", - "core.editor.unorderedlist": "Neuređena lista", - "core.emptysplit": "Ova stranica će se pojaviti prazna ukoliko je levi panel prazan ili se učitava.", - "core.error": "Greška", - "core.errorchangecompletion": "Došlo je do greške prilikom promene statusa završetka. Molimo, pokušajte ponovo.", - "core.errordeletefile": "Greška prilikom brisanja datoteke. Molimo, pokušajte ponovo.", - "core.errordownloading": "Greška prilikom preuzimanja datoteke.", - "core.errordownloadingsomefiles": "Greška prilikom preuzimanja datoteka modula. Moguće je da neke datoteke nedostaju.", - "core.errorfileexistssamename": "Već postoji datoteka sa ovim nazivom.", - "core.errorinvalidform": "Obrazac sadrži neispravne podatke. Uverite se da ste popunili sva neophodna polja i da su podaci ispravni.", - "core.errorinvalidresponse": "Primljen je neispravan odgovor. Obratite se administratoru vašeg Moodle sajta ako se greška ponovi.", - "core.errorloadingcontent": "Greška pri učitavanju sadržaja.", - "core.errorofflinedisabled": "Oflajn pregledanje je onemogućeno na vašem sajtu.", - "core.erroropenfilenoapp": "Greška prilikom otvaranja datoteke: nije pronađena aplikacija koja može da otvori ovaj tip datoteke.", - "core.erroropenfilenoextension": "Greška prilikom otvaranja datoteke: datoteka nema ekstenziju.", - "core.erroropenpopup": "Ova aktivnost pokušava da otvori iskačući prozor. Ova aplikacija to ne podržava.", - "core.errorrenamefile": "Greška prilikom pokušaja promene naziva datoteke. Molimo, pokušajte ponovo.", - "core.errorsync": "Došlo je do greške prilikom sinhronizaciji. Molimo, pokušajte ponovo.", - "core.errorsyncblocked": "{{$a}} trenutno ne može da se sinhronizuje zbog tekućeg procesa. Molimo, pokušajte ponovo kasnije. Ako se problem i dalje bude postojao, pokušajte ponovo da pokrenete aplikaciju.", - "core.explanationdigitalminor": "Ova informacija je neophodna da bi se utvrdilo da li je vaš uzrast iznad neophodnog digitalnog uzrasta u kom pojedinac može samostalno da prihvati odredbe i uslove korišćenja ovog sajta i dâ saglasnost da se podaci o njemu legalno čuvaju i obrađuju.", - "core.favourites": "Označeno", - "core.filename": "Naziv datoteke", - "core.filenameexist": "Naziv datoteke već postoji: {{$a}}", - "core.filenotfound": "Nažalost, datoteka nije pronađena", - "core.fileuploader.addfiletext": "Dodaj datoteku", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Nameravate da otpremite {{size}}. Da li ste sigurni da želite da nastavite?", - "core.fileuploader.confirmuploadunknownsize": "Nismo mogli da izračunamo veličinu datoteka za otpremanje. Da li ste sigurni da želite da nastavite?", - "core.fileuploader.errorcapturingaudio": "Greška prilikom snimanja audio zapisa.", - "core.fileuploader.errorcapturingimage": "Greška prilikom snimanja slike.", - "core.fileuploader.errorcapturingvideo": "Greška prilikom snimanja video zapisa.", - "core.fileuploader.errorgettingimagealbum": "Greška prilikom preuzimanja slike iz albuma.", - "core.fileuploader.errormustbeonlinetoupload": "Morate biti onlajn kako biste otpremili datoteke.", - "core.fileuploader.errornoapp": "Nemate instaliranu aplikaciju koja može da izvede ovu akciju.", - "core.fileuploader.errorreadingfile": "Greška prilikom učitavanja datoteke.", - "core.fileuploader.errorwhileuploading": "Došlo je do greške prilikom otpremanja datoteke.", - "core.fileuploader.file": "Датотека", - "core.fileuploader.filesofthesetypes": "Tipovi datoteka koji se prihvataju:", - "core.fileuploader.fileuploaded": "Datoteka je uspešno otpremljena.", - "core.fileuploader.invalidfiletype": "Nije moguće prihvatiti tip datoteke: {{$a}}.", - "core.fileuploader.maxbytesfile": "Datoteka {{$a.file}} je prevelika. Maksimalna veličina koju možete da otpremite je {{$a.size}}.", - "core.fileuploader.more": "Još", - "core.fileuploader.photoalbums": "Foto album", - "core.fileuploader.readingfile": "Učitavanje datoteke", - "core.fileuploader.readingfileperc": "Učitavanje datoteke: {{$a}}%", - "core.fileuploader.selectafile": "Izaberi datoteku", - "core.fileuploader.uploadafile": "Otpremi datoteku", - "core.fileuploader.uploading": "Otpremanje", - "core.fileuploader.uploadingperc": "Otpremanje: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Filter", - "core.folder": "Direktorijum", - "core.forcepasswordchangenotice": "Morate promeniti svoju lozinku da biste nastavili", - "core.fulllistofcourses": "Svi kursevi", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Prosek", - "core.grades.badgrade": "Data ocena je neispravna", - "core.grades.contributiontocoursetotal": "Doprinos ukupnoj oceni na kursu", - "core.grades.feedback": "Povratne informacije", - "core.grades.grade": "Ocena", - "core.grades.gradeitem": "Stavka ocene", - "core.grades.grades": "Ocene", - "core.grades.lettergrade": "Slovna ocena", - "core.grades.nogradesreturned": "Nema dobijenih ocena", - "core.grades.nooutcome": "Bez ishoda učenja", - "core.grades.percentage": "Procenat", - "core.grades.range": "Opseg", - "core.grades.rank": "Rang", - "core.grades.weight": "Ponder", - "core.group": "Grupa", - "core.groupsseparate": "Odvojene grupe", - "core.groupsvisible": "Vidljive grupe", - "core.hasdatatosync": "{{$a}} ima oflajn podatke koje treba sinhronizovati.", - "core.help": "Pomoć", - "core.hide": "Sakrij", - "core.hour": "h", - "core.hours": "sat/a/i", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Slika", - "core.imageviewer": "Prikazivač slike", - "core.info": "Informacija", - "core.invalidformdata": "Neispravni podaci u obrascu", - "core.labelsep": ":", - "core.lastaccess": "Poslednji pristup", - "core.lastdownloaded": "Poslednji put preuzeto", - "core.lastmodified": "Poslednja izmena", - "core.lastsync": "Poslednja sinhronizacija", - "core.layoutgrid": "Mreža", - "core.list": "Spisak", - "core.listsep": ";", - "core.loading": "Učitavanje", - "core.loadmore": "Učitaj još", - "core.location": "Lokacija", - "core.login.auth_email": "Samostalna registracija putem e-pošte", - "core.login.authenticating": "Provera identiteta", - "core.login.cancel": "Odustani", - "core.login.changepassword": "Promeni lozinku", - "core.login.confirmdeletesite": "Da li ste sigurni da želite da obrišete sajt {{sitename}}?", - "core.login.connect": "Povežite se!", - "core.login.connecttomoodle": "Povežite se sa Moodleom", - "core.login.contactyouradministrator": "Obratite se administratoru vašeg sajta za dalju pomoć.", - "core.login.contactyouradministratorissue": "Zamolite administratora da proveri sledeći problem: {{$a}}", - "core.login.createaccount": "Kreiraj moj novi korisnički nalog", - "core.login.createuserandpass": "Izaberite svoje korisničko ime i lozinku za pristup sistemu", - "core.login.credentialsdescription": "Za prijavu na sistem unesite svoje korisničko ime i lozinku.", - "core.login.emailconfirmsent": "

                      Trebalo bi da je poslata e-poruka na vašu adresu {{$a}}

                      \n

                      Poruka sadrži jednostavna uputstva o daljem postupku registracije.

                      \n

                      Ako i dalje imate problema, kontaktirajte administratora.

                      ", - "core.login.emailconfirmsentnoemail": "

                      Trebalo bi da je poslata e-poruka na vašu adresu.

                      Poruka sadrži jednostavna uputstva o daljem postupku registracije.

                      Ako i dalje imate problema, kontaktirajte administratora.

                      ", - "core.login.emailconfirmsentsuccess": "E-poruka za potvrdu uspešno je poslata", - "core.login.emailnotmatch": "Adrese e-pošte se ne poklapaju", - "core.login.erroraccesscontrolalloworigin": "Cross-Origin poziv koji pokušavate da izvedete je odbijen. Molimo, proverite https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Došlo je do greške prilikom brisanja ovog sajta. Molim, pokušajte ponovo.", - "core.login.errorupdatesite": "Došlo je do greške prilikom ažuriranju tokena sajta.", - "core.login.findyoursite": "Pronađite svoj sajt", - "core.login.firsttime": "Da li ste ovde prvi put?", - "core.login.forcepasswordchangenotice": "Morate promeniti svoju lozinku da biste nastavili", - "core.login.forgotten": "Zaboravili ste svoje korisničko ime ili lozinku?", - "core.login.help": "Pomoć", - "core.login.helpmelogin": "

                      Postoji više hiljada Moodle sajtova širom sveta. Ova aplikacija može da se poveže samo sa Moodle sajtovima koji su eksplicitno omogućili pristup mobilnoj aplikaciji.

                      Ako ne možete da se povežete sa Moodle sajtom, kontaktirajte administratora vašeg sajta i zatražite da pročita http://docs.moodle.org/en/Mobile_app

                      Da biste testirali aplikaciju na Moodle demo sajtu upišite teacher ili student u polje Adresa sajta i kliknite dugme Povežite se!.

                      ", - "core.login.instructions": "Uputstva", - "core.login.invalidaccount": "Proverite svoje podatke za prijavu ili zamolite vašeg administratora da proveri konfiguraciju sajta.", - "core.login.invaliddate": "Neispravan datum", - "core.login.invalidemail": "Neispravna adresa elektronske pošte", - "core.login.invalidmoodleversion": "Neispravna Moodle verzija. Neophodna je, minimalno, verzija {{$a}}.", - "core.login.invalidsite": "URL adresa sajt nije ispravna.", - "core.login.invalidtime": "Neispravno vreme", - "core.login.invalidurl": "Navedena je neispravna URL adresa", - "core.login.invalidvaluemax": "Maksimalna vrednost je {{$a}}", - "core.login.invalidvaluemin": "Minimalna vrednost je {{$a}}", - "core.login.localmobileunexpectedresponse": "Provera Moodle Mobile dodatnih funkcionalnosti vratila je neočekivan odgovor. Vaš identitet biće proveren pomoću standardnog mobilnog servisa.", - "core.login.loggedoutssodescription": "Morate ponovo da potvrdite svoj identitet. Potrebno je da se prijavite na sajt u prozoru veb čitača.", - "core.login.login": "Prijava", - "core.login.loginbutton": "Prijava", - "core.login.logininsiterequired": "Potrebno je da se prijavite na sajt u prozoru veb čitača.", - "core.login.loginsteps": "Kako biste imali puni pristup ovom sajtu morate kreirati korisnički nalog.", - "core.login.missingemail": "Nedostaje adresa e-pošte", - "core.login.missingfirstname": "Nedostaje ime", - "core.login.missinglastname": "Nedostaje prezime", - "core.login.mobileservicesnotenabled": "Mobilni servisi nisu omogućeni na vašem sajtu. Obratite se administratoru vašeg Moodle sajta ako mislite da mobilni pristup treba da bude omogućen.", - "core.login.mustconfirm": "Morate potvrditi svoj nalog", - "core.login.newaccount": "Novi korisnički nalog", - "core.login.notloggedin": "Morate biti prijavljeni.", - "core.login.password": "Lozinka", - "core.login.passwordforgotten": "Zaboravljena lozinka", - "core.login.passwordforgotteninstructions2": "Za resetovanje Vaše lozinke upišite svoje korisničko ime ili elektronsku adresu. Ukoliko ti podaci postoje u bazi podataka, biće Vam poslata poruka na elektronsku adresu sa uputstvom kako ponovo da dobijete pristup.", - "core.login.passwordrequired": "Neophodna je lozinka", - "core.login.policyaccept": "Razumem i pristajem", - "core.login.policyagree": "Da bi ste nastavili korišćenje ovog sajta morate se složiti sa pravilima korišćenja. Da li se slažete?", - "core.login.policyagreement": "Saglasnost sa pravilnikom o korišćenju sajta", - "core.login.policyagreementclick": "Link ka pravilniku o korišćenju sajta", - "core.login.potentialidps": "Prijavite se koristeći svoj nalog na:", - "core.login.profileinvaliddata": "Neodgovarajuća vrednost", - "core.login.recaptchachallengeimage": "reCAPTCHA slika", - "core.login.recaptchaexpired": "Verifikacija je istekla. Ponovo odgovorite na bezbednosno pitanje.", - "core.login.recaptchaincorrect": "Odgovor na bezbednosno pitanje je netačan.", - "core.login.reconnect": "Povežite se ponovo", - "core.login.reconnectdescription": "Vaš token za proveru identiteta je neispravan ili je istekao. Morate ponovo da uspostavite vezu sa sajtom.", - "core.login.reconnectssodescription": "Vaš token za proveru identiteta je neispravan ili je istekao. Morate ponovo da uspostavite vezu sa sajtom. Potrebno je da se prijavite na sajt u prozoru veb čitača.", - "core.login.resendemail": "Pošalji ponovo e-poruku", - "core.login.searchby": "Pretraga po:", - "core.login.security_question": "Bezbednosno pitanje", - "core.login.selectacountry": "Izaberi državu", - "core.login.selectsite": "Izaberite svoj sajt:", - "core.login.signupplugindisabled": "{{$a}} nije omogućen.", - "core.login.siteaddress": "Adresa sajta", - "core.login.sitehasredirect": "Vaš sajt sadrži najmanje jednu HTTP redirekciju. Aplikacija ne može da prati redirekcije. Ovo bi mogao da bude problem koji sprečava aplikaciju da se poveže sa vašim sajtom.", - "core.login.siteinmaintenance": "Vaš sajt je u režimu održavanja", - "core.login.sitepolicynotagreederror": "Saglasnost sa politikom sajta nije potvrđena.", - "core.login.siteurl": "URL adresa sajta", - "core.login.siteurlrequired": "Neophodna je URL adresa, npr. http://www.yourmoodlesite.abc ili https://www.yourmoodlesite.efg", - "core.login.startsignup": "Kreiraj novi nalog", - "core.login.stillcantconnect": "Još uvek ne možete da se povežete?", - "core.login.supplyinfo": "Više detalja", - "core.login.username": "Korisničko ime", - "core.login.usernameoremail": "Unesite ili korisničko ime ili adresu e-pošte", - "core.login.usernamerequired": "Korisničko ime je neophodno", - "core.login.usernotaddederror": "Korisnik nije dodat - greška", - "core.login.visitchangepassword": "Da li želite da posetite sajt kako biste promenili lozinku?", - "core.login.webservicesnotenabled": "Veb servisi nisu omogućeni na vašem sajtu. Obratite se administratoru vašeg Moodle sajta ako mislite da mobilni pristup treba da bude omogućen.", - "core.lostconnection": "Vaš token za potvrdu identiteta je nevažeći ili je istekao. Moraćete ponovo da uspostavite vezu sa sajtom.", - "core.mainmenu.changesite": "Promeni sajt", - "core.mainmenu.help": "Pomoć", - "core.mainmenu.logout": "Odjava", - "core.mainmenu.website": "Veb sajt", - "core.maxsizeandattachments": "Maksimalna veličina datoteke: {{$a.size}}, maksimalan broj datoteka: {{$a.attachments}}", - "core.min": "min", - "core.mins": "min", - "core.misc": "Razno", - "core.mod_assign": "Zadatak", - "core.mod_assignment": "Zadatak 2.2 (onemogućen)", - "core.mod_book": "Knjiga", - "core.mod_chat": "Pričaonica", - "core.mod_choice": "Izbor", - "core.mod_data": "Baza podataka", - "core.mod_database": "Baza podataka", - "core.mod_external-tool": "Eksterni alat", - "core.mod_feedback": "Upitnik (Feedback)", - "core.mod_file": "Datoteka", - "core.mod_folder": "Direktorijum", - "core.mod_forum": "Forum", - "core.mod_glossary": "Rečnik", - "core.mod_ims": "IMS paket", - "core.mod_imscp": "IMS paket", - "core.mod_label": "Natpis", - "core.mod_lesson": "Lekcija", - "core.mod_lti": "Eksterni alat", - "core.mod_page": "Stranica", - "core.mod_quiz": "Test", - "core.mod_resource": "Resurs", - "core.mod_scorm": "SCORM paket", - "core.mod_survey": "Upitnik (Survey)", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Radionica", - "core.moduleintro": "Opis", - "core.more": "još", - "core.mygroups": "Moje grupe", - "core.name": "Naziv", - "core.networkerroriframemsg": "Ovaj sadržaj nije dostupan oflajn. Povežite se na internet i pokušajte ponovo.", - "core.networkerrormsg": "Bilo je problema sa povezivanjem na sajt. Proverite vezu i pokušajte ponovo.", - "core.never": "Nikad", - "core.next": "Sledeći", - "core.no": "Ne", - "core.nocomments": "Nema komentara", - "core.nograde": "Nema ocene", - "core.none": "Nijedan", - "core.nopasswordchangeforced": "Ne možete nastaviti bez promene svoje lozinke.", - "core.nopermissionerror": "Žao nam je, ali trenutno nemate ovlašćenja da to uradite", - "core.nopermissions": "Žao nam je, ali trenutno nemate ovlašćenja da to radite ({{$a}}).", - "core.noresults": "Nema rezultata", - "core.noselection": "Nema izbora", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Ovaj profil nije dostupan jer korisnik nije prijavljen za ovaj kurs.", - "core.notice": "Napomena", - "core.notingroup": "Nažalost, morate biti član grupe kako biste videli ovu stranicu.", - "core.notsent": "Nije poslato", - "core.now": "sada", - "core.numwords": "{{$a}} reč(i)", - "core.offline": "Offline", - "core.ok": "OK", - "core.online": "Online", - "core.openfullimage": "Kliknite ovde da biste prikazali sliku u punoj veličini", - "core.openinbrowser": "Otvori u veb čitaču", - "core.othergroups": "Druge grupe", - "core.pagea": "Stranica {{$a}}", - "core.paymentinstant": "Upotrebite dugme ispod kako biste izvršili uplatu i upisali kurs u roku od nekoliko minuta!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pictureof": "Slika {{$a}}", - "core.previous": "Prethodni", - "core.proceed": "Nastavite", - "core.pulltorefresh": "Povucite za osvežavanje", - "core.question.answer": "Odgovor", - "core.question.answersaved": "Odgovor je sačuvan", - "core.question.cannotdeterminestatus": "Nije moguće odrediti status", - "core.question.certainty": "Nivo sigurnosti", - "core.question.complete": "Završeno", - "core.question.correct": "Tačno", - "core.question.errorattachmentsnotsupported": "Aplikacija još uvek ne podržava mogućnost da se datoteke pridruže uz odgovore.", - "core.question.errorinlinefilesnotsupported": "Aplikacija još uvek ne podržava uređivanje unutar datoteka.", - "core.question.errorquestionnotsupported": "Aplikacija ne podržava ovaj tip pitanja: {{$a}}.", - "core.question.feedback": "Povratne informacije", - "core.question.howtodraganddrop": "Dodirnite za izbor, zatim još jednom za spuštanje.", - "core.question.incorrect": "Netačno", - "core.question.information": "Informacija", - "core.question.invalidanswer": "Nepotpun odgovor", - "core.question.notanswered": "Nije odgovoreno", - "core.question.notyetanswered": "Još nije odgovoreno", - "core.question.partiallycorrect": "Delimično tačno", - "core.question.questionmessage": "Pitanje {{$a}}: {{$b}}", - "core.question.questionno": "Pitanje {{$a}}", - "core.question.requiresgrading": "Zahteva ocenjivanje", - "core.quotausage": "Trenutno koristite {{$a.used}} od maksimalno dozvoljenih {{$a.total}}.", - "core.rating.aggregateavg": "Prosek ocena", - "core.rating.aggregatecount": "Broj ocena", - "core.rating.aggregatemax": "Maksimalna ocena", - "core.rating.aggregatemin": "Minimalna ocena", - "core.rating.aggregatesum": "Zbir ocena", - "core.rating.noratings": "Nema predatih ocena", - "core.rating.rating": "Ocena", - "core.rating.ratings": "Ocene", - "core.redirectingtosite": "Bićete preusmereni na sajt.", - "core.refresh": "Osveži", - "core.remove": "Ukloni", - "core.required": "Obavezno", - "core.requireduserdatamissing": "Ovaj korisnik nema u svom profilu neke neophodne podatke. Molimo vas, uneseti ove podatke u vaš Moodle i pokušajte ponovo.
                      {{$a}}", - "core.resourcedisplayopen": "Otvori", - "core.resources": "Resursi", - "core.restore": "Restauriranje rezervne kopije", - "core.restricted": "Ograničeno", - "core.retry": "Pokušaj ponovo", - "core.save": "Sačuvaj", - "core.savechanges": "Sačuvaj promene", - "core.search": "Pretraga", - "core.searching": "Pretraživanje", - "core.searchresults": "Rezultati pretrage", - "core.sec": "sek", - "core.secs": "s", - "core.seemoredetail": "Kliknite ovde za više detelja", - "core.selectacategory": "Molimo vas da izaberete kategoriju", - "core.selectacourse": "Izaberi kurs", - "core.selectagroup": "Izaberi grupu", - "core.send": "Pošalji", - "core.sending": "Šalje se", - "core.serverconnection": "Greška u povezivanju sa serverom", - "core.settings.about": "O aplikaciji", - "core.settings.cannotsyncoffline": "Oflajn sinhronizacija nije moguća.", - "core.settings.cannotsyncwithoutwifi": "Sinhronizacija nije moguća zato što trenutna podešavanja dozvoljavaju sinhronizaciju samo kada postoji veza sa Wi-Fi mrežom. Povežite se na Wi-Fi mrežu.", - "core.settings.compilationinfo": "Informacije o kompilaciji", - "core.settings.cordovadevicemodel": "Model Cordova uređaja", - "core.settings.cordovadeviceosversion": "Verzija operativnog sistema Cordova uređaja", - "core.settings.cordovadeviceplatform": "Platforma Cordova uređaja", - "core.settings.cordovadeviceuuid": "UUID Cordova uređaja", - "core.settings.cordovaversion": "Cordova verzija", - "core.settings.currentlanguage": "Trenutno važeći jezik", - "core.settings.debugdisplay": "Prikaži poruke za otklanjanje grešaka", - "core.settings.debugdisplaydescription": "Ako je omogućeno, poruka o grešci će prikazati više podataka o samoj grešci ako je to moguće.", - "core.settings.deletesitefiles": "Da li ste sigurni da želite da obrišete preuzete datoteke sa sajta '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Obriši datoteke sajta", - "core.settings.deviceinfo": "Informacija o uređaju", - "core.settings.deviceos": "Operativni sistem uređaja", - "core.settings.disableall": "Onemogući obaveštenja", - "core.settings.disabled": "Onemogućeno", - "core.settings.displayformat": "Format prikaza", - "core.settings.enabledownloadsection": "Omogući preuzimanje sekcija.", - "core.settings.enablerichtexteditor": "Omogući obogaćeni editor teksta", - "core.settings.enablerichtexteditordescription": "Ako je ova opcija uključena obogaćeni editor teksta biće prikazan na mestima koja to dozvoljavaju.", - "core.settings.enablesyncwifi": "Dozvolite sinhronizaciju samo kada ste povezani na Wi-Fi mrežu.", - "core.settings.errordeletesitefiles": "Greška prilikom brisanja datoteka sajta.", - "core.settings.errorsyncsite": "Greška prilikom sinhronizacije podataka sajta. Molimo, proverite vašu internet vezu i pokušajte ponovo.", - "core.settings.estimatedfreespace": "Procenjeni slobodan prostora", - "core.settings.filesystemroot": "Osnovni direktorijum sistema datoteka", - "core.settings.fontsizecharacter": "A", - "core.settings.general": "Opšte", - "core.settings.language": "Jezik", - "core.settings.license": "Licenca", - "core.settings.localnotifavailable": "Lokalna obaveštenja dostupna", - "core.settings.locationhref": "Webview URL adresa", - "core.settings.locked": "Zaključano", - "core.settings.loggedin": "Onlajn", - "core.settings.loggedoff": "Oflajn", - "core.settings.navigatorlanguage": "Jezik navigatora", - "core.settings.navigatoruseragent": "Korisnički agent navigatora", - "core.settings.networkstatus": "Status inernet veze", - "core.settings.preferences": "Parametri", - "core.settings.privacypolicy": "Politika privatnosti", - "core.settings.reportinbackground": "Prijavi greške automatski", - "core.settings.settings": "Podešavanja", - "core.settings.showdownloadoptions": "Prikaži opcije preuzimanja", - "core.settings.sites": "Sajtovi", - "core.settings.spaceusage": "Iskorištenost prostora", - "core.settings.synchronization": "Sinhronizacija", - "core.settings.synchronizenow": "Sinhronizuj sada", - "core.settings.syncsettings": "Podešavanja sinhronizacije", - "core.settings.total": "Ukupno", - "core.settings.wificonnection": "WiFi veza", - "core.sharedfiles.chooseaccountstorefile": "Izaberite nalog za čuvanje datoteke.", - "core.sharedfiles.chooseactionrepeatedfile": "Već postoji datoteka sa ovim nazivom. Da li želite da zamenite postojeću datoteku ili da joj naziv promenite u \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Ne postoje memorisani sajtovi. Molimo, dodajte sajt pre nego što pokušate da podelite datoteke sa aplikacijom.", - "core.sharedfiles.nosharedfiles": "Ne postoje deljene datoteke koje se nalaze na ovom sajtu.", - "core.sharedfiles.nosharedfilestoupload": "Ovde nemate datoteke koje možete da otpremite. Ako želite da otpremite datoteku iz druge aplikacije, pronađite tu datoteku i kliknite na dugme 'Otvori u'.", - "core.sharedfiles.rename": "Promeni naziv", - "core.sharedfiles.replace": "Zameni", - "core.sharedfiles.sharedfiles": "Deljene datoteke", - "core.sharedfiles.successstorefile": "Datoteka je uspešno sačuvana. Sada možete da izaberete ovu datoteku da biste je otpremili među svoje privatne datoteke ili je pridružili nekoj aktivnosti.", - "core.show": "Prikaži", - "core.showless": "Prikaži manje...", - "core.showmore": "Prikaži više...", - "core.site": "Sajt", - "core.sitehome.sitehome": "Početna stranica sajta", - "core.sitehome.sitenews": "Obaveštenja sajta", - "core.sitemaintenance": "Sajt je u fazi održavanja i trenutno nije dostupan", - "core.sizeb": "bajta", - "core.sizegb": "Gb", - "core.sizekb": "Kb", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "Preskoči", - "core.sorry": "Izvinite...", - "core.sort": "Sortiraj", - "core.sortby": "Sortiraj po", - "core.strftimedate": "%d. %B %Y.", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d. %B", - "core.strftimedatetime": "%d. %B %Y., %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d. %B %Y.", - "core.strftimedaydatetime": "%A, %d. %B %Y., %H:%M", - "core.strftimedayshort": "%A, %d. %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y.", - "core.strftimerecent": "%d. %b, %H:%M", - "core.strftimerecentfull": "%a, %d. %b %Y., %H:%M", - "core.strftimetime": "%H:%M", - "core.strftimetime12": "%I:%M %p", - "core.strftimetime24": "%H:%M", - "core.submit": "Prosledi", - "core.success": "Uspešno", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Podrazumevana kolekcija", - "core.tag.inalltagcoll": "Svugde", - "core.tag.itemstaggedwith": "{{$a.tagarea}} označena sa \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Nema rezultata za \"{{$a}}\"", - "core.tag.notagsfound": "Nisu pronađene oznake koje se poklapaju sa \"{{$a}}\"", - "core.tag.searchtags": "Pretraži oznake", - "core.tag.showingfirsttags": "Broj najpopularnijih oznaka koje se prikazuju: {{$a}}", - "core.tag.tag": "Oznaka", - "core.tag.tagarea_course": "Kursevi", - "core.tag.tagarea_course_modules": "Aktivnosti i resursi", - "core.tag.tagarea_post": "Objave na blogu", - "core.tag.tagarea_user": "Korisnička interesovanja", - "core.tag.tags": "Oznake", - "core.teachers": "Predavači", - "core.thereisdatatosync": "Broj oflajn podatak koje treba sinhronizovati: {{$a}}", - "core.thisdirection": "ltr", - "core.time": "Vreme", - "core.timesup": "Vreme je isteklo!", - "core.today": "Danas", - "core.tryagain": "Pokušaj ponovo", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.uhoh": "Uh!", - "core.unexpectederror": "Neočekivana greška. Zatvorite i ponovo otvorite aplikaciju kako biste pokušali ponovo.", - "core.unicodenotsupported": "Neki emotikoni nisu podržani na ovom sajtu. Takvi karakteri će biti uklonjeni prilikom slanja poruke.", - "core.unicodenotsupportedcleanerror": "Prilikom čišćenja Unicode karaktera pronađen je prazan tekst.", - "core.unknown": "Nepoznato", - "core.unlimited": "Neograničeno", - "core.unzipping": "Raspakivanje", - "core.upgraderunning": "Sajt se ažurira, molimo pokušajte kasnije", - "core.user": "Korisnik", - "core.user.address": "Adresa", - "core.user.city": "Mesto", - "core.user.contact": "Kontakt", - "core.user.country": "Država", - "core.user.description": "Opis", - "core.user.details": "Detalji", - "core.user.detailsnotavailable": "Podaci o ovom korisniku vam nisu dostupni.", - "core.user.editingteacher": "Predavač", - "core.user.email": "Adresa e-pošte", - "core.user.emailagain": "Adresa e-pošte (ponovo)", - "core.user.errorloaduser": "Greška prilikom učitavanja korisnika.", - "core.user.firstname": "Ime", - "core.user.interests": "Interesovanja", - "core.user.lastname": "Prezime", - "core.user.manager": "Menadžer", - "core.user.newpicture": "Nova slika", - "core.user.noparticipants": "Nije pronađen ni jedan učesnik na ovom kursu", - "core.user.participants": "Učesnici", - "core.user.phone1": "Telefon", - "core.user.phone2": "Mobilni telefon", - "core.user.roles": "Uloge", - "core.user.sendemail": "E-pošta", - "core.user.student": "Polaznik", - "core.user.teacher": "Predavač bez uređivačkih prava", - "core.user.webpage": "Web stranica", - "core.userdeleted": "Ovaj korisnički nalog je obrisan", - "core.userdetails": "Detalji o korisniku", - "core.usernotfullysetup": "Korisnik nije u potpunosti podešen", - "core.users": "Korisnici", - "core.view": "Prikaz", - "core.viewcode": "Prikaži kôd", - "core.vieweditor": "Prikaži editor", - "core.viewembeddedcontent": "Prikaži ugrađeni sadržaj", - "core.viewprofile": "Pregledaj profil", - "core.warningofflinedatadeleted": "Oflajn podaci komponente {{component}} '{{name}}' su obrisani. {{error}}", - "core.whatisyourage": "Koliko Vam je godina", - "core.wheredoyoulive": "U kojoj zemlji živite?", - "core.whoops": "Ups!", - "core.whyisthishappening": "Zašto se ovo dešava?", - "core.whyisthisrequired": "Zašto je ovo neophodno?", - "core.wsfunctionnotavailable": "Funkcija Webservice nije dostupna.", - "core.year": "godina", - "core.years": "godina", - "core.yes": "Da" -} \ No newline at end of file diff --git a/src/assets/lang/sv.json b/src/assets/lang/sv.json deleted file mode 100644 index 095711cf0..000000000 --- a/src/assets/lang/sv.json +++ /dev/null @@ -1,1617 +0,0 @@ -{ - "addon.badges.badgedetails": "Detaljer för märke", - "addon.badges.badges": "Märken", - "addon.badges.contact": "Kontakt", - "addon.badges.dateawarded": "Utfärdandedatum", - "addon.badges.expired": "Förfallen", - "addon.badges.expirydate": "Förfallodatum", - "addon.badges.issuancedetails": "Förfallande av märke", - "addon.badges.issuerdetails": "Utfärdarens detaljer", - "addon.badges.issuername": "Utfärdarens namn", - "addon.badges.issuerurl": "Utfärdarens URL", - "addon.badges.nobadges": "Det finns inga märken tillgängliga.", - "addon.badges.warnexpired": "(Det här märket har förfallit!)", - "addon.block_activitymodules.pluginname": "Aktiviteter", - "addon.block_badges.pluginname": "Mina senaste märken", - "addon.block_blogmenu.pluginname": "Bloggmeny", - "addon.block_blogrecent.pluginname": "Aktuella inlägg i bloggar", - "addon.block_blogtags.pluginname": "Bloggetiketter", - "addon.block_calendarmonth.pluginname": "Kalender", - "addon.block_calendarupcoming.pluginname": "Kommande händelser", - "addon.block_comments.pluginname": "Kommentarer", - "addon.block_completionstatus.pluginname": "Status för fullföljande av kurs", - "addon.block_glossaryrandom.pluginname": "Slumpade bidrag till ordlista", - "addon.block_learningplans.pluginname": "Studieplaner", - "addon.block_myoverview.all": "Alla (förutom dolda)", - "addon.block_myoverview.allincludinghidden": "Alla", - "addon.block_myoverview.favourites": "Favoriter", - "addon.block_myoverview.future": "Kommande", - "addon.block_myoverview.hiddencourses": "Dolda", - "addon.block_myoverview.inprogress": "Pågående", - "addon.block_myoverview.lastaccessed": "Senast besökta", - "addon.block_myoverview.morecourses": "Fler kurser", - "addon.block_myoverview.nocourses": "Inga kurser", - "addon.block_myoverview.past": "Tidigare", - "addon.block_myoverview.pluginname": "Kursöversikt", - "addon.block_myoverview.title": "Kursnamn", - "addon.block_newsitems.pluginname": "Senaste nytt", - "addon.block_onlineusers.pluginname": "Användare som är
                      inloggade just nu", - "addon.block_privatefiles.pluginname": "Användares privata filer", - "addon.block_recentactivity.pluginname": "Senaste aktivitet", - "addon.block_recentlyaccessedcourses.nocourses": "Det finns inga senast besökta kurser", - "addon.block_recentlyaccessedcourses.pluginname": "Senast besökta kurser", - "addon.block_recentlyaccesseditems.pluginname": "Nyligen åtkomna objekt", - "addon.block_rssclient.pluginname": "RSS-klient", - "addon.block_selfcompletion.pluginname": "Eget fullföljande", - "addon.block_sitemainmenu.pluginname": "Huvudmeny", - "addon.block_starredcourses.nocourses": "Inga favoritmärkta kurser", - "addon.block_starredcourses.pluginname": "Favoritmarkerade kurser", - "addon.block_tags.pluginname": "Rubriker för intressen", - "addon.block_timeline.duedate": "Deadline", - "addon.block_timeline.next30days": "Kommande 30 dagar", - "addon.block_timeline.next3months": "Kommande 3 månader", - "addon.block_timeline.next6months": "Kommande 6 månader", - "addon.block_timeline.next7days": "Kommande 7 dagar", - "addon.block_timeline.nocoursesinprogress": "Inga pågående kurser", - "addon.block_timeline.noevents": "Inga kommande deadlines", - "addon.block_timeline.overdue": "Deadline passerad", - "addon.block_timeline.pluginname": "Tidslinje", - "addon.block_timeline.sortbycourses": "Sortera efter kurser", - "addon.block_timeline.sortbydates": "Sortera efter datum", - "addon.blog.blog": "Blogg", - "addon.blog.blogentries": "Inlägg i blogg/ar", - "addon.blog.linktooriginalentry": "Länk till ursprungligt inlägg i blogg", - "addon.blog.noentriesyet": "Inga synliga inlägg här", - "addon.blog.publishtonoone": "Dig själv (utkast)", - "addon.blog.publishtosite": "Vem som helst på denna sajt", - "addon.blog.publishtoworld": "Vem som helst (även för gäster)", - "addon.calendar.allday": "Hela dagen", - "addon.calendar.calendar": "Kalender", - "addon.calendar.calendarevents": "Kalenderhändelser", - "addon.calendar.categoryevents": "Händelser i kategori", - "addon.calendar.confirmeventdelete": "Är Du säker på att du vill ta bort den här händelsen?", - "addon.calendar.confirmeventseriesdelete": "Händelsen \"{{$a.name}}\" ingår i en serie av händelser. Vill du ta bort bara denna händelse, eller alla händelser i serien {{$a.count}}?", - "addon.calendar.courseevents": "Händelser i kursen", - "addon.calendar.daynext": "Nästa dag", - "addon.calendar.dayprev": "Dagen före", - "addon.calendar.deleteallevents": "Ta bort alla händelser", - "addon.calendar.deleteevent": "Ta bort händelse", - "addon.calendar.deleteoneevent": "Ta bort denna händelse", - "addon.calendar.durationminutes": "Varaktighet i minuter", - "addon.calendar.durationnone": "Utan varaktighet", - "addon.calendar.durationuntil": "Till", - "addon.calendar.editevent": "Redigerar händelse", - "addon.calendar.errorloadevent": "Fel vid inläsning av händelse.", - "addon.calendar.errorloadevents": "Fel vid inläsning av händelser", - "addon.calendar.eventcalendareventdeleted": "Kalenderhändelse har tagits bort", - "addon.calendar.eventduration": "Varaktighet", - "addon.calendar.eventendtime": "Sluttid/datum", - "addon.calendar.eventkind": "Typ av händelse", - "addon.calendar.eventname": "Namn", - "addon.calendar.eventstarttime": "Starttid/datum", - "addon.calendar.eventtype": "Typ av händelse", - "addon.calendar.fri": "Fre", - "addon.calendar.friday": "Fredag", - "addon.calendar.gotoactivity": "Gå till aktivitete", - "addon.calendar.groupevents": "Händelser för grupp", - "addon.calendar.invalidtimedurationminutes": "Varaktigheten i minuter som du har angett är ogiltig. Ange tiden i minuter större än 0 eller välj någon varaktighet", - "addon.calendar.invalidtimedurationuntil": "Datum och tid du valt för tid till är före starttiden för händelsen. Vänligen korrigera detta innan du fortsätter.", - "addon.calendar.mon": "Mån", - "addon.calendar.monday": "Måndag", - "addon.calendar.monthlyview": "Vy över månad", - "addon.calendar.newevent": "Ny händelse", - "addon.calendar.noevents": "Det finns inga händelser", - "addon.calendar.nopermissiontoupdatecalendar": "Ledsen, men du har för närvarande inte behörighet att uppdatera kalenderhändelse", - "addon.calendar.repeatedevents": "Upprepade händelser", - "addon.calendar.repeateditall": "Tillämpa ändringar för alla {{$a}} händelser i den här serien av upprepningar", - "addon.calendar.repeateditthis": "Tillämpa ändringar bara för den här händelsen", - "addon.calendar.repeatevent": "Upprepa denna händelse", - "addon.calendar.repeatweeksl": "Upprepa veckovis, skapa", - "addon.calendar.sat": "Lör", - "addon.calendar.saturday": "Lördag", - "addon.calendar.siteevents": "Händelser för webbplatsen", - "addon.calendar.sun": "Sön", - "addon.calendar.sunday": "Söndag", - "addon.calendar.thu": "Tor", - "addon.calendar.thursday": "Torsdag", - "addon.calendar.today": "Idag", - "addon.calendar.tomorrow": "Imorgon", - "addon.calendar.tue": "Tis", - "addon.calendar.tuesday": "Tisdag", - "addon.calendar.typecategory": "Händelse för kategori", - "addon.calendar.typeclose": "Stäng händelse", - "addon.calendar.typecourse": "Händelse för kurs", - "addon.calendar.typedue": "", - "addon.calendar.typegradingdue": "", - "addon.calendar.typegroup": "Händelse för grupp", - "addon.calendar.typesite": "Händelse för webbplats", - "addon.calendar.typeuser": "Händelse för användare", - "addon.calendar.upcomingevents": "Kommande händelser", - "addon.calendar.userevents": "Händelser för användare", - "addon.calendar.wed": "Ons", - "addon.calendar.wednesday": "Onsdag", - "addon.calendar.when": "När", - "addon.calendar.yesterday": "Igår", - "addon.competency.activities": "Aktiviteter", - "addon.competency.competencies": "Kompetenser", - "addon.competency.competenciesmostoftennotproficientincourse": "Kompetenser som oftast ej uppnåtts i denna kurs", - "addon.competency.coursecompetencies": "Kurskompetenser", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Bedömning av kompetenser i denna kurs kommer inte att påverka studieplaner.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Bedömning av kompetenser i denna kurs uppdateras omedelbart i studieplanerna.", - "addon.competency.crossreferencedcompetencies": "Korsrefererade kompetenser.", - "addon.competency.duedate": "Sista datum", - "addon.competency.evidence": "Verifiering", - "addon.competency.evidence_competencyrule": "Regeln för kompetensen uppfylldes.", - "addon.competency.evidence_coursecompleted": "Kursen '{{$a}}' genomfördes.", - "addon.competency.evidence_coursemodulecompleted": "Aktiviteten '{{$a}}' genomfördes.", - "addon.competency.evidence_courserestored": "Bedömningen återställdes jäms med kursen '{{$a}}'.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Verifieringen av tidigare lärande '{{$a}}' länkades.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Verifieringen av tidigare lärande '{{$a}}' avlänkades.", - "addon.competency.evidence_manualoverride": "Kompetensgraderingen sattes manuellt.", - "addon.competency.evidence_manualoverrideincourse": "Kompetensgraderingen sattes manuellt i kursen '{{$a}}'.", - "addon.competency.evidence_manualoverrideinplan": "Kompetensgraderingen sattes manuellt i studeplanen '{{$a}}'.", - "addon.competency.learningplancompetencies": "Kompetenser i studieplaner", - "addon.competency.learningplans": "Studieplaner", - "addon.competency.myplans": "Mina studieplaner", - "addon.competency.noactivities": "Inga aktiviteter", - "addon.competency.nocompetenciesincourse": "Inga kompetenser har länkats till denna kurs", - "addon.competency.nocrossreferencedcompetencies": "Inga andra kompetenser har korsrefererats till denna kompetens.", - "addon.competency.noevidence": "Inga verifieringar", - "addon.competency.noplanswerecreated": "Inga studieplaner var skapade.", - "addon.competency.path": "Sökväg:", - "addon.competency.planstatusactive": "Aktiv", - "addon.competency.planstatuscomplete": "Komplett", - "addon.competency.planstatusdraft": "Utkast", - "addon.competency.planstatusinreview": "Granskning pågår", - "addon.competency.planstatuswaitingforreview": "Väntar på granskning", - "addon.competency.proficient": "Kunnig", - "addon.competency.progress": "Utveckling", - "addon.competency.rating": "Bedömning", - "addon.competency.reviewstatus": "Granska status", - "addon.competency.status": "Status", - "addon.competency.template": "Mall för studieplan", - "addon.competency.uponcoursecompletion": "Vid genomförande av kurs:", - "addon.competency.usercompetencystatus_idle": "Overksam", - "addon.competency.usercompetencystatus_inreview": "Bedömning pågår", - "addon.competency.usercompetencystatus_waitingforreview": "Väntar på bedömning", - "addon.competency.userplans": "Studieplaner", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} av {{$a.y}} kompetenser är uppnådda", - "addon.competency.xcompetenciesproficientoutofyincourse": "Du har uppnått {{$a.x}} av {{$a.y}} kompetenser i denna kurs..", - "addon.coursecompletion.complete": "", - "addon.coursecompletion.completecourse": "Fullföljd kurs", - "addon.coursecompletion.completed": "Slutfört", - "addon.coursecompletion.completiondate": "Datum för fullföljande", - "addon.coursecompletion.completionmenuitem": "Fullföljning", - "addon.coursecompletion.couldnotloadreport": "Det gick inte att läsa in rapporten för fullföljande av kursen, vänligen försök igen senare .", - "addon.coursecompletion.coursecompletion": "Fullgörande av kurs", - "addon.coursecompletion.criteria": "Kriterier", - "addon.coursecompletion.criteriagroup": "Kriterier för grupp", - "addon.coursecompletion.criteriarequiredall": "Alla kriterier är obligatoriska", - "addon.coursecompletion.criteriarequiredany": "Alla kriterier nedan är obligatoriska", - "addon.coursecompletion.inprogress": "Pågår", - "addon.coursecompletion.manualselfcompletion": "Studenten markerar själv som fullföljd", - "addon.coursecompletion.nottracked": "Du spåras inte för genomförande i denna kurs vid detta tillfälle", - "addon.coursecompletion.notyetstarted": "Har ännu inte påbörjats", - "addon.coursecompletion.pending": "Avvaktar", - "addon.coursecompletion.required": "Obligatorisk", - "addon.coursecompletion.requiredcriteria": "Obligatoriskt kriterium", - "addon.coursecompletion.requirement": "Förhandskrav", - "addon.coursecompletion.status": "Status", - "addon.coursecompletion.viewcoursereport": "Visa kursrapport", - "addon.files.couldnotloadfiles": "Listan över filer kunde inte läsas in.", - "addon.files.emptyfilelist": "Det finns inga filer att visa.", - "addon.files.files": "Filer", - "addon.files.privatefiles": "Privata filer", - "addon.files.sitefiles": "Webbplatsens filer", - "addon.messages.acceptandaddcontact": "Acceptera och lägg till kontakter", - "addon.messages.addcontact": "Lägg till kontakt", - "addon.messages.addcontactconfirm": "Är du säker att du vill lägga {{$a}} till dina kontakter?", - "addon.messages.addtofavourites": "Markera som favorit", - "addon.messages.addtoyourcontacts": "Lägg till dina kontakter", - "addon.messages.blocknoncontacts": "Blockera alla nya meddelanden från personer som inte finns med på min kontaktlista.", - "addon.messages.blockuser": "Blockera användare", - "addon.messages.blockuserconfirm": "Är du säker att du vill blockera {{$a}}?", - "addon.messages.contactableprivacy": "Acceptera meddelanden från:", - "addon.messages.contactableprivacy_coursemember": "Mina kontaker och alla i mina kurser", - "addon.messages.contactableprivacy_onlycontacts": "Bara mina kontakter", - "addon.messages.contactableprivacy_site": "Alla på denna webbplats", - "addon.messages.contactblocked": "Kontakt blockerad", - "addon.messages.contactlistempty": "kontaktlista är tom", - "addon.messages.contactname": "Kontakt namn", - "addon.messages.contactrequestsent": "Kontaktförfrågan skickad", - "addon.messages.contacts": "Kontakter", - "addon.messages.decline": "Avböj", - "addon.messages.deleteallconfirm": "Vill du verkligen radera hela denna konversation?", - "addon.messages.deleteallselfconfirm": "Vill du verkligen radera hela denna personliga konversation?", - "addon.messages.deleteconversation": "Radera konversation", - "addon.messages.deleteforeveryone": "Ta bort för mig och alla andra", - "addon.messages.deletemessage": "Ta bort meddelande", - "addon.messages.deletemessageconfirmation": "Är du säker på att du vill ta bort detta meddelande? Det kommer bara att tas bort från din egen meddelandehistorik. Användaren som skickade eller tog emot meddelandet kommer fortfarande att kunna se det.", - "addon.messages.errordeletemessage": "Fel vid borttagning av meddelandet", - "addon.messages.errorwhileretrievingcontacts": "Fel vid hämtning av kontakter från servern.", - "addon.messages.errorwhileretrievingdiscussions": "Fel vid hämtning av diskussionerna från servern.", - "addon.messages.errorwhileretrievingmessages": "Fel vid hämtning meddelanden från servern.", - "addon.messages.groupconversations": "Grupp", - "addon.messages.groupinfo": "Gruppinformation", - "addon.messages.individualconversations": "Privat", - "addon.messages.info": "Användarinformation", - "addon.messages.isnotinyourcontacts": "{{$a}} finns inte bland dina kontakter", - "addon.messages.message": "Meddelande", - "addon.messages.messagenotsent": "Meddelandet skickades inte, försök igen senare.", - "addon.messages.messagepreferences": "Välj inställningar för meddelanden", - "addon.messages.messages": "Meddelanden", - "addon.messages.newmessage": "Nytt meddelande", - "addon.messages.newmessages": "Nya meddelanden", - "addon.messages.nocontactrequests": "Inga kontaktförfrågningar", - "addon.messages.nocontactsgetstarted": "Inga kontakter", - "addon.messages.nofavourites": "Inga favoriter konversationer", - "addon.messages.nogroupconversations": "Det finns inga gruppkonversationer", - "addon.messages.noindividualconversations": "Det finns inga privata konversationer", - "addon.messages.nomessagesfound": "Det gick inte att hitta några nya meddelanden", - "addon.messages.noncontacts": "Icke-kontakter", - "addon.messages.nousersfound": "Inga användare hittades", - "addon.messages.numparticipants": "{{$a}} deltagare", - "addon.messages.removecontact": "Ta bort kontakt", - "addon.messages.removecontactconfirm": "Är du säker på att du vill ta bort {{$a}} från dina kontaker?", - "addon.messages.removefromfavourites": "Ta bort från Favoriter", - "addon.messages.removefromyourcontacts": "Ta bort från dina kontakter", - "addon.messages.requirecontacttomessage": "Be {{$a}} att lägga till dig som kontakt för att du ska kunna skicka meddelanden till honom/henne.", - "addon.messages.searchcombined": "Sök personer och meddelanden", - "addon.messages.selfconversation": "Personlig yta", - "addon.messages.selfconversationdefaultmessage": "Spara utkast till meddelanden, länkar, anteckningar etc. för senare åtkomst", - "addon.messages.sendcontactrequest": "Skicka kontaktförfrågan", - "addon.messages.showdeletemessages": "Visa borttagna meddelanden", - "addon.messages.type_blocked": "blockerad", - "addon.messages.type_offline": "Offline", - "addon.messages.type_online": "Online", - "addon.messages.type_search": "Sökresultat", - "addon.messages.type_strangers": "Andra", - "addon.messages.unabletomessage": "Du kan inte skicka meddelanden till denna användare", - "addon.messages.unblockuser": "Avblockera användare", - "addon.messages.unblockuserconfirm": "Är du säker att du vill avblockera {{$a}}?", - "addon.messages.useentertosend": "Använd Enter för att skicka", - "addon.messages.userwouldliketocontactyou": "{{$a}} vill kontakta dig", - "addon.messages.warningmessagenotsent": "Ett eller flera meddelanden till användare {{user}} kunde inte skickas. {{error}}", - "addon.messages.wouldliketocontactyou": "Vill kontakta dig", - "addon.messages.you": "Du:", - "addon.messages.youhaveblockeduser": "Du har tidigare blockerat denna användare", - "addon.messages.yourcontactrequestpending": "Din kontaktförfrågan till {{$a}} väntar på svar", - "addon.mod_assign.addattempt": "Tillåt ett nytt försök", - "addon.mod_assign.addnewattempt": "Lägg till ett nytt försök", - "addon.mod_assign.addnewattemptfromprevious": "Lägg till ett nytt försök baserat på tidigare inlämning", - "addon.mod_assign.addsubmission": "Lägg till inskickat bidrag", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Detaljer om inlämningsuppgiften samt inlämningsformuläret kommer att vara tillgängligt från {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Tillåt inlämning från", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Denna inlämningsuppgift kommer att tillåta inlämning från {{$a}}", - "addon.mod_assign.applytoteam": "Tillämpa betyg och återkoppling för hela gruppen", - "addon.mod_assign.assignmentisdue": "Inlämningstiden har gått ut.", - "addon.mod_assign.attemptnumber": "Försök nummer", - "addon.mod_assign.attemptreopenmethod": "Försök återupptas", - "addon.mod_assign.attemptreopenmethod_manual": "Manuellt", - "addon.mod_assign.attemptreopenmethod_untilpass": "Automatiskt tills passerat", - "addon.mod_assign.attemptsettings": "Inställningar försök", - "addon.mod_assign.confirmsubmission": "Är du säker på att du vill lämna in ditt arbete för betygssättning? Du kommer inte längre att kunna göra några förändringar", - "addon.mod_assign.currentattempt": "Detta är försök {{$a}}.", - "addon.mod_assign.currentattemptof": "Detta är försök {{$a.attemptnumber}} ( {{$a.maxattempts}} försök tillåten).", - "addon.mod_assign.currentgrade": "Aktuellt betyg/omdöme i betygskatalogen", - "addon.mod_assign.cutoffdate": "Avstängningsdatum", - "addon.mod_assign.defaultteam": "Förvald grupp", - "addon.mod_assign.duedate": "Stoppdatum/tid", - "addon.mod_assign.duedateno": "Inget stoppdatum/tid", - "addon.mod_assign.duedatereached": "Stoppdatum för denna inlämningsuppgift har nu passerat", - "addon.mod_assign.editingstatus": "Redigerar status", - "addon.mod_assign.editsubmission": "Redigera min inskickade uppgiftslösning", - "addon.mod_assign.extensionduedate": "Förlängning av stoppdatum", - "addon.mod_assign.feedbacknotsupported": "Appen stödjer inte denna återkoppling och det kan hända att information saknas.", - "addon.mod_assign.grade": "Betyg", - "addon.mod_assign.graded": "Betygssatt", - "addon.mod_assign.gradedby": "Betygssatt av", - "addon.mod_assign.gradedon": "Betygssatt den", - "addon.mod_assign.gradelocked": "Detta betyg är låst eller skrevs över i betygsboken/betygsrapporten.", - "addon.mod_assign.gradenotsynced": "Betyg har inte synkats.", - "addon.mod_assign.gradeoutof": "Betyg ur {{$a}}", - "addon.mod_assign.gradingstatus": "Betygssättningsstatus", - "addon.mod_assign.groupsubmissionsettings": "Gruppinlämning inställningar", - "addon.mod_assign.hiddenuser": "Deltagare", - "addon.mod_assign.latesubmissions": "Sen inlämning", - "addon.mod_assign.latesubmissionsaccepted": "Endast elev(er) som har beviljats förlängd inlämningstid kan fortfarande lämna in uppgifter", - "addon.mod_assign.markingworkflowstate": "Markera status för arbetsflöde", - "addon.mod_assign.markingworkflowstateinmarking": "Under bedömning", - "addon.mod_assign.markingworkflowstateinreview": "Under granskning", - "addon.mod_assign.markingworkflowstatenotmarked": "Inte bedömnd", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Redo för", - "addon.mod_assign.markingworkflowstatereadyforreview": "Bedöming färdig", - "addon.mod_assign.markingworkflowstatereleased": "Released", - "addon.mod_assign.modulenameplural": "Uppgifter", - "addon.mod_assign.multipleteams": "Medlem av mer än en grupp", - "addon.mod_assign.multipleteams_desc": "Denna inlämning är en gruppuppgift. Just nu är du medlem i flera grupper. För att kunna göra en inlämning måste du ingår i endast en grupp. V.g. be din lärare att ändra din grupptillhörighet.", - "addon.mod_assign.noattempt": "Inga försök", - "addon.mod_assign.nomoresubmissionsaccepted": "Inga fler inlämningar accepteras", - "addon.mod_assign.noonlinesubmissions": "Denna inlämningsuppgift kräver inte att du lämnar in något online.", - "addon.mod_assign.nosubmission": "Inget har lämnats in för denna inlämningsuppgift", - "addon.mod_assign.notallparticipantsareshown": "Deltagare som inte har gjort någon inlämning visas inte.", - "addon.mod_assign.noteam": "Inte medlem i någon grupp", - "addon.mod_assign.noteam_desc": "Denna inlämning är en gruppuppgift. Just nu är du inte medlem i någon grupp. V.g. be din lärare att lägga in dig i en grupp.", - "addon.mod_assign.notgraded": "Ej betygssatt", - "addon.mod_assign.numberofdraftsubmissions": "Utkast", - "addon.mod_assign.numberofparticipants": "Deltagare", - "addon.mod_assign.numberofsubmissionsneedgrading": "Behöver betygssättas", - "addon.mod_assign.numberofsubmittedassignments": "Inlämnad", - "addon.mod_assign.numberofteams": "Grupper", - "addon.mod_assign.numwords": "{{$a}} ord", - "addon.mod_assign.outof": "{{$a.current}} av totalt {{$a.total}}", - "addon.mod_assign.overdue": "Inlämningsuppgiften är försenad med: {{$a}}", - "addon.mod_assign.submission": "Inskickad uppgift", - "addon.mod_assign.submissioneditable": "Elev kan ändra denna inlämning", - "addon.mod_assign.submissionnoteditable": "Elev kan inte ändra denna inlämning", - "addon.mod_assign.submissionnotsupported": "Appen stödjer inte denna inlämning och det kan hända att information saknas.", - "addon.mod_assign.submissionslocked": "Denna inlämningsuppgift accepterar inte inlämningar", - "addon.mod_assign.submissionstatus": "Status för inlämning", - "addon.mod_assign.submissionstatus_": "Ingen inlämning", - "addon.mod_assign.submissionstatus_draft": "Utkast (ej inlämnad)", - "addon.mod_assign.submissionstatus_marked": "Betygssatt", - "addon.mod_assign.submissionstatus_new": "Ingen inlämning", - "addon.mod_assign.submissionstatus_reopened": "Återöppnad", - "addon.mod_assign.submissionstatus_submitted": "Inlämnad för betygssättning", - "addon.mod_assign.submissionstatusheading": "Status för inlämning", - "addon.mod_assign.submissionteam": "Grupp", - "addon.mod_assign.submitassignment": "Skicka in uppgift", - "addon.mod_assign.submitassignment_help": "När du har lämnat in din uppgift kommer du inte längre att kunna göra ändringar i den.", - "addon.mod_assign.submittedearly": "Inlämningsuppgift lämnades in {{$a}} tidigt", - "addon.mod_assign.submittedlate": "Inlämningsuppgift lämnades in {{$a}} sent", - "addon.mod_assign.timemodified": "Senast ändrad", - "addon.mod_assign.timeremaining": "Återstående tid", - "addon.mod_assign.unlimitedattempts": "Obegränsad", - "addon.mod_assign.userswhoneedtosubmit": "Användare som behöver lämna in: {{$a}}", - "addon.mod_assign.userwithid": "Användare men Id {{id}}", - "addon.mod_assign.viewsubmission": "Visa inlämning", - "addon.mod_assign.wordlimit": "Maxgräns för antal ord", - "addon.mod_assign_feedback_comments.pluginname": "Återkoppling/kommentarer", - "addon.mod_assign_feedback_editpdf.pluginname": "Kommentera PDF", - "addon.mod_assign_feedback_file.pluginname": "Maximal filstorlek", - "addon.mod_assign_submission_comments.pluginname": "Inlämningskommentarer", - "addon.mod_assign_submission_file.pluginname": "Filinlämningar", - "addon.mod_assign_submission_onlinetext.pluginname": "Online textinlämning", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Maxgränsen i denna uppgift är {{$a.limit}} ord och du försöker lämna in {{$a.count}} ord. Var god och korta ned din uppgift och försök igen.", - "addon.mod_book.errorchapter": "Fel vid läsning av bokkapitel", - "addon.mod_book.modulenameplural": "Böcker", - "addon.mod_book.navnexttitle": "Nästa: {{$a}}", - "addon.mod_book.navprevtitle": "Föregående: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Bokkapitel", - "addon.mod_book.toc": "Innehållsförteckning", - "addon.mod_chat.beep": "pipsignal", - "addon.mod_chat.chatreport": "Sessioner för direktsamtal", - "addon.mod_chat.currentusers": "Aktuella användare", - "addon.mod_chat.enterchat": "Klicka här för att gå in i direktsamtalet nu", - "addon.mod_chat.entermessage": "Sriv ditt meddelande", - "addon.mod_chat.errorwhileconnecting": "Fel vid anslutning till chatten", - "addon.mod_chat.errorwhilegettingchatdata": "Fel vid vid hämtning av chattdata", - "addon.mod_chat.errorwhilegettingchatusers": "Fel vid vid hämtning av chattanvändare", - "addon.mod_chat.errorwhileretrievingmessages": "Fel vid hämtning av meddelanden från servern", - "addon.mod_chat.errorwhilesendingmessage": "Fel när meddelandet skickades", - "addon.mod_chat.messagebeepseveryone": "{{$a}} skickar en pipsignal till alla!", - "addon.mod_chat.messagebeepsyou": "{{$a}} har just skickat en pipsignal till Dig", - "addon.mod_chat.messageenter": "{{$a}} har precis kommit in i detta direktsamtal", - "addon.mod_chat.messageexit": "{{$a}} har lämnat det här direktsamtalet", - "addon.mod_chat.messages": "Meddelanden", - "addon.mod_chat.messageyoubeep": "Du signalerade till {{$a}}", - "addon.mod_chat.modulenameplural": "Direktsamtal", - "addon.mod_chat.mustbeonlinetosendmessages": "Du måste vara online för att skicka meddelanden", - "addon.mod_chat.nomessages": "Inga meddelanden än", - "addon.mod_chat.saidto": "sade till", - "addon.mod_chat.send": "Skicka", - "addon.mod_chat.sessionstart": "Sessionen för direktsamtal kommer att starta inom: {{$a}}", - "addon.mod_chat.talk": "Tala", - "addon.mod_chat.viewreport": "Visa de senaste sessionerna för direktsamtal", - "addon.mod_choice.cannotsubmit": "Tyvärr fanns det ett problem att lämna ditt val . Var god försök igen.", - "addon.mod_choice.choiceoptions": "Alternativ för opinonsundersökning", - "addon.mod_choice.errorgetchoice": "Fel vid hämtning av opinionsdata", - "addon.mod_choice.expired": "Den här aktiviteten är stängd på {{$a}} och den är inte längre tillgänglig.", - "addon.mod_choice.full": "(Full)", - "addon.mod_choice.modulenameplural": "Opinionsundersökningar", - "addon.mod_choice.noresultsviewable": "Det går f.n. inte att visa resultaten.", - "addon.mod_choice.notopenyet": "Den här aktiviteten är tyvärr inte tillgänglig förrän {{$a}}", - "addon.mod_choice.numberofuser": "Antal användare", - "addon.mod_choice.numberofuserinpercentage": "Antalet användare i procent.", - "addon.mod_choice.previewonly": "Detta är bara en förhandsvisning av de tillgängliga alternativen för den här aktiviteten . Du kommer inte att kunna lämna in ditt val innan {{$a}}", - "addon.mod_choice.publishinfoanonafter": "Anonyma resultat kommer att visas när du har svarat.", - "addon.mod_choice.publishinfoanonclose": "Anonyma resultat kommer att visas när aktiviteten har stängts.", - "addon.mod_choice.publishinfofullafter": "Resultaten kommer att visas i sin helhet, med allas svar, när du har svarat.", - "addon.mod_choice.publishinfofullclose": "Resultaten kommer att visas i sin helhet, när aktiviteten har stängts.", - "addon.mod_choice.publishinfonever": "Resultaten av denna aktivitet kommer inte att visas när du har svarat.", - "addon.mod_choice.removemychoice": "Ta bort mitt val", - "addon.mod_choice.responses": "Svar", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% Av användarna valde alternativet: {{text}}", - "addon.mod_choice.responsesresultgraphheader": "Visa en graf", - "addon.mod_choice.savemychoice": "Spara min opinionsundersökning", - "addon.mod_choice.userchoosethisoption": "Användaren väljer detta alternativ", - "addon.mod_choice.yourselection": "Ditt urval", - "addon.mod_data.addentries": "Lägg till bidrag", - "addon.mod_data.advancedsearch": "Avancerad sökning", - "addon.mod_data.alttext": "Alternativ text", - "addon.mod_data.approve": "Godkänn", - "addon.mod_data.approved": "Godkänd", - "addon.mod_data.ascending": "Stigande", - "addon.mod_data.authorfirstname": "Författarens förnamn", - "addon.mod_data.authorlastname": "Författarens efternamn", - "addon.mod_data.confirmdeleterecord": "Är Du säker på att Du vill ta bort det här bidraget?", - "addon.mod_data.descending": "Fallande", - "addon.mod_data.disapprove": "Ta tillbaka godkännande", - "addon.mod_data.emptyaddform": "Du fyllde inte i alla fält!", - "addon.mod_data.entrieslefttoadd": "Du måste lägga till {{$a.entriesleft}} fler bidrag för att fullfölja den här aktiviteten.", - "addon.mod_data.entrieslefttoaddtoview": "Du måste lägga till {{$a.entrieslefttoview}} fler bidrag innan Du kan få se de andra deltagarnas bidrag.", - "addon.mod_data.errormustsupplyvalue": "Du måste fylla i ett värde här.", - "addon.mod_data.expired": "Den här aktiviteten stängdes tyvärr den {{$a}} och är inte längre tillgänglig.", - "addon.mod_data.fields": "Fält", - "addon.mod_data.foundrecords": "Funna poster: {{$a.num}}/{{$a.max}} (Filter för återställning)", - "addon.mod_data.menuchoose": "Välj...", - "addon.mod_data.modulenameplural": "Databaser", - "addon.mod_data.more": "Fler", - "addon.mod_data.nomatch": "Det gick inte att hitta några matchande bidrag!", - "addon.mod_data.norecords": "Det finns inga bidrag i databasen", - "addon.mod_data.notapproved": "Bidraget är inte godkänt än", - "addon.mod_data.notopenyet": "Den här aktiviteten är tyvärr inte tillgänglig förrän {$}}", - "addon.mod_data.numrecords": "{{$a}} bidrag", - "addon.mod_data.other": "Övrigt", - "addon.mod_data.recordapproved": "Bidraget har godkänts", - "addon.mod_data.recorddeleted": "Bidraget har tagits bort", - "addon.mod_data.recorddisapproved": "Bidraget har inte godkänts", - "addon.mod_data.resetsettings": "Filter för återställning", - "addon.mod_data.search": "Sök", - "addon.mod_data.selectedrequired": "Alla de valda är obligatoriska", - "addon.mod_data.single": "Visa enskilt bidrag", - "addon.mod_data.timeadded": "Tillagd när", - "addon.mod_data.timemodified": "Modifierad när", - "addon.mod_data.usedate": "Ta med i sökning", - "addon.mod_feedback.analysis": "Analys", - "addon.mod_feedback.anonymous": "Anonym", - "addon.mod_feedback.anonymous_entries": "Anonyma inlägg", - "addon.mod_feedback.average": "Medel", - "addon.mod_feedback.complete_the_form": "Fyll i hela formuläret", - "addon.mod_feedback.completed_feedbacks": "Fullgjorda Egna enkäter", - "addon.mod_feedback.continue_the_form": "Fortsätt formuläret", - "addon.mod_feedback.feedback_is_not_open": "Denna Egen enkät är inte öppen", - "addon.mod_feedback.feedbackclose": "Stäng Egen enkät vid denna tid", - "addon.mod_feedback.feedbackopen": "Öppna denna Egen enkät vid den här tiden", - "addon.mod_feedback.mapcourses": "\"Mappa\" Egen enkät till kurser", - "addon.mod_feedback.maximal": "maximal", - "addon.mod_feedback.mode": "Läge", - "addon.mod_feedback.modulenameplural": "Egen enkät", - "addon.mod_feedback.next_page": "Nästa sida", - "addon.mod_feedback.non_anonymous": "Inte-anonym", - "addon.mod_feedback.non_anonymous_entries": "Inga anonyma bidrag", - "addon.mod_feedback.non_respondents_students": "Studenter/elever/deltagare/lärande som inte har lämnat några svar", - "addon.mod_feedback.not_selected": "Inte vald", - "addon.mod_feedback.not_started": "inte påbörjad", - "addon.mod_feedback.overview": "Översikt", - "addon.mod_feedback.page_after_submit": "Sida efter inskickning", - "addon.mod_feedback.preview": "Förhandsgranska", - "addon.mod_feedback.previous_page": "Föregående sida", - "addon.mod_feedback.questions": "Frågor", - "addon.mod_feedback.response_nr": "Antal svar", - "addon.mod_feedback.responses": "Svar", - "addon.mod_feedback.save_entries": "Spara inlägg", - "addon.mod_feedback.show_entries": "Visa inlägg", - "addon.mod_feedback.show_nonrespondents": "Visa dem som inte har lämnat några svar", - "addon.mod_feedback.started": "startad", - "addon.mod_feedback.this_feedback_is_already_submitted": "Du har redan fullföljt den här Egen enkät", - "addon.mod_folder.emptyfilelist": "Det finns inga filer att visa", - "addon.mod_folder.modulenameplural": "Mappar", - "addon.mod_forum.addanewdiscussion": "Lägg till ett nytt diskussionsämne", - "addon.mod_forum.addanewquestion": "Lägg till en ny fråga", - "addon.mod_forum.addanewtopic": "Lägg till ett nytt ämne", - "addon.mod_forum.addtofavourites": "Lägg denna diskussion till mina favoriter", - "addon.mod_forum.advanced": "Avancerad", - "addon.mod_forum.cannotadddiscussion": "För att lägga till diskussionsämnen till det här forumet krävs det att man är medlem av en grupp.", - "addon.mod_forum.cannotadddiscussionall": "Du har inte tillstånd att lägga till ett nytt diskussionsämne för alla deltagare. ", - "addon.mod_forum.cannotcreatediscussion": "Det gick inte att skapa en ny diskussion", - "addon.mod_forum.couldnotadd": "Det gick inte att lägga till Ditt inlägg på grund av okänt fel.", - "addon.mod_forum.couldnotupdate": "Det gick inte att uppdatera Ditt inlägg på grund av okänt fel.", - "addon.mod_forum.cutoffdatereached": "Stängningsdatumet för detta forum har passerats. Det är inte längre möjligt att göra inlägg.", - "addon.mod_forum.delete": "Ta bort", - "addon.mod_forum.deletedpost": "Inlägget har tagits bort", - "addon.mod_forum.deletesure": "Är Du säker på att Du vill ta bort detta inlägg?", - "addon.mod_forum.discussion": "Diskussionsämne", - "addon.mod_forum.discussionlistsortbycreatedasc": "Sortera efter skapelsedatum, äldst överst.", - "addon.mod_forum.discussionlistsortbycreateddesc": "Sortera efter skapelsedatum, nyast överst.", - "addon.mod_forum.discussionlistsortbylastpostasc": "Sortera efter skapelsedatum för det senaste inlägget, äldst överst.", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Sortera efter skapelsedatum för det senaste inlägget, nyast överst.", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Sortera efter antal svar, i stigande ordning.", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Sortera efter antal svar, i fallande ordning.", - "addon.mod_forum.discussionlocked": "Denna diskussion är stängd och du kan inte längre göra inlägg i den.", - "addon.mod_forum.discussionpinned": "Fastnålad", - "addon.mod_forum.discussionsubscription": "Diskussions prenumeration", - "addon.mod_forum.edit": "Redigera", - "addon.mod_forum.erroremptymessage": "Meddelandet i inlägget kan inte vara tomt", - "addon.mod_forum.erroremptysubject": "Ämnesrubriken för inlägget kan inte vara tom.", - "addon.mod_forum.errorgetforum": "Fel att få forum innehåll", - "addon.mod_forum.errorgetgroups": "Fel vid hämtning av gruppinställningar", - "addon.mod_forum.forumnodiscussionsyet": "Det finns inga diskussionsämnen ännu i detta forum.", - "addon.mod_forum.group": "Grupp", - "addon.mod_forum.lastpost": "Senaste inlägg", - "addon.mod_forum.lockdiscussion": "Lås denna diskussion för nya inlägg.", - "addon.mod_forum.message": "Meddelande", - "addon.mod_forum.modeflatnewestfirst": "Visa svarslista, med det senaste först", - "addon.mod_forum.modeflatoldestfirst": "Visa svarslista, med det äldsta först", - "addon.mod_forum.modenested": "Visa svar i nästlad form", - "addon.mod_forum.modulenameplural": "Forum", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} diskussioner", - "addon.mod_forum.numreplies": "{{numreplies}} svar", - "addon.mod_forum.pindiscussion": "Nåla fast denna diskussion", - "addon.mod_forum.postisprivatereply": "Detta är ett privat svar. Det är inte synligt för andra deltagare.", - "addon.mod_forum.posttoforum": "Publicera inlägget i forumet", - "addon.mod_forum.posttomygroups": "Publicera en kopia i alla grupper", - "addon.mod_forum.privatereply": "Svara privat", - "addon.mod_forum.re": "Svar:", - "addon.mod_forum.removefromfavourites": "Ta bort diskussionen från dina favoriter.", - "addon.mod_forum.reply": "Svar", - "addon.mod_forum.replyplaceholder": "Skriv ditt svar...", - "addon.mod_forum.subject": "Ämne", - "addon.mod_forum.tagarea_forum_posts": "Foruminlägg", - "addon.mod_forum.unlockdiscussion": "Lås upp diskussionen", - "addon.mod_forum.unpindiscussion": "Ta bort nålen som låser fast diskussionen högst upp", - "addon.mod_forum.unread": "Oläst", - "addon.mod_forum.unreadpostsnumber": "{{$a}} olästa inlägg", - "addon.mod_forum.yourreply": "Ditt svar", - "addon.mod_glossary.addentry": "Lägg till bidrag", - "addon.mod_glossary.aliases": "Synonymer", - "addon.mod_glossary.attachment": "Bifogad fil", - "addon.mod_glossary.browsemode": "Bläddrar bland poster", - "addon.mod_glossary.byalphabet": "Alfabetiskt", - "addon.mod_glossary.byauthor": "Sortera efter författare", - "addon.mod_glossary.bynewestfirst": "Nyaste först", - "addon.mod_glossary.byrecentlyupdated": "Nyligen uppdaterade", - "addon.mod_glossary.bysearch": "Sök", - "addon.mod_glossary.casesensitive": "Det här bidraget gör
                      gör skillnad på stor och liten bokstav", - "addon.mod_glossary.categories": "Kategorier", - "addon.mod_glossary.concept": "Begrepp", - "addon.mod_glossary.definition": "Definition", - "addon.mod_glossary.entrypendingapproval": "Detta inlägg väntar på godkännande", - "addon.mod_glossary.entryusedynalink": "Det här bidraget bör
                      vara automatiskt länkat", - "addon.mod_glossary.errconceptalreadyexists": "Det här begreppet finns redan med. Det är inte tillåtet med dubbleringar i den här ord- och begreppslistan.", - "addon.mod_glossary.errorloadingentries": "Ett fel uppstod vid inläsning av inläggen", - "addon.mod_glossary.errorloadingentry": "Ett fel uppstod vid inläsning av inlägget", - "addon.mod_glossary.errorloadingglossary": "Ett fel uppstod vid inläsning av ordboken", - "addon.mod_glossary.fillfields": "'Begrepp' och definition' är obligatoriska fält", - "addon.mod_glossary.fullmatch": "Matcha hela ord bara
                      när de är automatiskt länkade", - "addon.mod_glossary.linking": "Automatisk länkning", - "addon.mod_glossary.modulenameplural": "Ord- och begreppslistor", - "addon.mod_glossary.noentriesfound": "Inga inlägg hittades", - "addon.mod_glossary.searchquery": "", - "addon.mod_glossary.tagarea_glossary_entries": "Uppslagsord/inlägg i ordboken", - "addon.mod_imscp.deploymenterror": "Fel med innehållspaket!", - "addon.mod_imscp.modulenameplural": "Innehållspaket av typ IMS", - "addon.mod_imscp.showmoduledescription": "Visa beskrivning", - "addon.mod_imscp.toc": "Index över innehåll", - "addon.mod_lesson.answer": "Svar", - "addon.mod_lesson.attempt": "Försök: {{$a}}", - "addon.mod_lesson.attemptsremaining": "Du har {{$a}} återstående försök", - "addon.mod_lesson.averagescore": "Genomsnittsligt resultat", - "addon.mod_lesson.averagetime": "Genomsnittslig tid", - "addon.mod_lesson.branchtable": "Innehåll", - "addon.mod_lesson.cannotfindattempt": "Fel: det gick inte att hitta försök", - "addon.mod_lesson.cannotfinduser": "Fel: det gick inte att hitta användare", - "addon.mod_lesson.clusterjump": "Ej visad fråga inom ett kluster", - "addon.mod_lesson.completed": "Avslutad", - "addon.mod_lesson.congratulations": "Gratulerar - lektionen är slut här", - "addon.mod_lesson.continue": "Fortsätt", - "addon.mod_lesson.continuetonextpage": "Fortsätt till nästa sida.", - "addon.mod_lesson.defaultessayresponse": "Din essä kommer att få ett betyg/omdöme av kursens distanslärare.", - "addon.mod_lesson.detailedstats": "Detaljerad statistik", - "addon.mod_lesson.didnotanswerquestion": "Besvarade inte den här frågan", - "addon.mod_lesson.displayofgrade": "Visning av betyg/omdöme", - "addon.mod_lesson.displayscorewithessays": "Du har uppnått {{$a.score}} av {{$a.tempmaxgrade}} för de frågor som betygssätts automatiskt.
                      Din/a {{$a.essayquestions}} essäfråga/or kommer att betygsättas och läggas till
                      Ditt slutresultat vid ett senare tillfälle.

                      Ditt aktuella betyg utan essäfråga/or är {{$a.score}} av {{$a.grade}}", - "addon.mod_lesson.displayscorewithoutessays": "Ditt resultat är {{$a.score}} (av {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Textfältet för lösenord får inte vara tomt.", - "addon.mod_lesson.enterpassword": "Var snäll och skriv in lösenordet:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Du svarade inte på några frågor. Ditt resultat för den här lektionen är 0.", - "addon.mod_lesson.finish": "Avsluta", - "addon.mod_lesson.firstwrong": "Tyvärr har du inte uppnått den här poängen eftersom du inte svarade rätt. Vill du fortsätta att gissa, för lärandets skull (men inte för någon poäng)?", - "addon.mod_lesson.gotoendoflesson": "Gå till slutet av lektionen", - "addon.mod_lesson.grade": "Betyg/omdöme", - "addon.mod_lesson.highscore": "Högt resultat", - "addon.mod_lesson.hightime": "Hög tid", - "addon.mod_lesson.leftduringtimed": "Du har avbrutit under en tidsstyrd lektion.
                      Var snäll och välj \"Fortsätt\" för att starta om lektionen.", - "addon.mod_lesson.leftduringtimednoretake": "Du har avbrutit under en tidsstyrd lektion
                      och Du får inte göra om, eller gå vidare med, lektionen.", - "addon.mod_lesson.lessonmenu": "Meny för lektion", - "addon.mod_lesson.lessonstats": "Statistik för lektion", - "addon.mod_lesson.linkedmedia": "Länkade media", - "addon.mod_lesson.loginfail": "Det gick inte att logga in, vara snäll och försök igen...", - "addon.mod_lesson.lowscore": "Lågt resultat", - "addon.mod_lesson.lowtime": "Låg tid", - "addon.mod_lesson.maximumnumberofattemptsreached": "Du har uppnått maximalt antal tillåtna försök - Du flyttas nu vidare till nästa sida.", - "addon.mod_lesson.modattemptsnoteacher": "Upprepad visning för studenter/elever/deltagare/lärande fungerar bara för dem.", - "addon.mod_lesson.modulenameplural": "Lektioner", - "addon.mod_lesson.noanswer": "En eller flera frågor saknar svar. V.g. gå tillbaka och lämna ett svar.", - "addon.mod_lesson.nolessonattempts": "Det har inte gjorts några försök att genomföra den här lektionen.", - "addon.mod_lesson.nolessonattemptsgroup": "Inga försök har gjorts i denna lektion av {{$a}} gruppmedlemmar.", - "addon.mod_lesson.notcompleted": "Inte avslutad", - "addon.mod_lesson.numberofcorrectanswers": "Antal rätta svar: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Antal sidor som har visats: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Antal besvarade frågor: {{$a.nquestions}}; (Du bör besvara åtminstone: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Du har uppnått {{$a.score}} poäng av {{$a.currenthigh}} poäng så här långt.", - "addon.mod_lesson.ongoingnormal": "Du har lämnat korrekt/a svar på {{$a.correct}} fråga/or av {{$a.viewed}} fråga/or.", - "addon.mod_lesson.or": "ELLER", - "addon.mod_lesson.overview": "Överblick", - "addon.mod_lesson.preview": "Förhandsgranska", - "addon.mod_lesson.progressbarteacherwarning2": "Du kan inte se raden för fortskridande eftersom Du kan redigera den här lektionen", - "addon.mod_lesson.progresscompleted": "Du har gjort {{$a}}% av denna lektion.", - "addon.mod_lesson.question": "Fråga", - "addon.mod_lesson.rawgrade": "Grovt antaget betyg/omdöme", - "addon.mod_lesson.reports": "Rapporter", - "addon.mod_lesson.response": "Återkoppling på svar", - "addon.mod_lesson.review": "Granska", - "addon.mod_lesson.reviewlesson": "Visa lektion igen", - "addon.mod_lesson.reviewquestionback": "Ja, jag vill försöka igen", - "addon.mod_lesson.reviewquestioncontinue": "Nej, jag vill gå vidare till nästa fråga", - "addon.mod_lesson.secondpluswrong": "Inte helt. Vill Du försöka igen?", - "addon.mod_lesson.submit": "Skicka in", - "addon.mod_lesson.teacherjumpwarning": "Det finns ett {{$a.cluster}} hopp eller ett {{$a.unseen}} hopp med i den här lektionen. Hoppet \"Nästa sida\" kommer att användas istället. Logga in som en student/elev/deltagare/lärande för att testa dessa hopp.", - "addon.mod_lesson.teacherongoingwarning": "Ett pågående resultat visas bara för studenter/elever/deltagare/lärande. Logga in som en sådan för att se pågående resultat.", - "addon.mod_lesson.teachertimerwarning": "Tidtagaren fungerar bara för studenter/elever/deltagare/lärande. Logga in som en sådan för att testa tidtagaren.", - "addon.mod_lesson.thatsthecorrectanswer": "Det är rätt svar", - "addon.mod_lesson.thatsthewronganswer": "Det är ett felaktigt svar", - "addon.mod_lesson.timeremaining": "Återstående tid", - "addon.mod_lesson.timetaken": "Använd tid", - "addon.mod_lesson.unseenpageinbranch": "Ej visad fråga inom en innehållssida", - "addon.mod_lesson.welldone": "Bra gjort!", - "addon.mod_lesson.youhaveseen": "Du har redan sett mer än en sida av den här lektionen.
                      Vill Du börja med den senaste sidan som Du såg?", - "addon.mod_lesson.youranswer": "Ditt svar", - "addon.mod_lesson.yourcurrentgradeisoutof": "Ditt aktuella betyg/omdöme är {{$a.grade}} av {{$a.total}}", - "addon.mod_lesson.youshouldview": "Du bör åtminstone titta på: {{$a}}", - "addon.mod_lti.errorgetlti": "Fel vid hämtning av LTI data", - "addon.mod_lti.errorinvalidlaunchurl": "URL är ogiltig", - "addon.mod_lti.launchactivity": "Starta aktiviteten", - "addon.mod_page.errorwhileloadingthepage": "Fel vid laddning av sidans innehåll.", - "addon.mod_page.modulenameplural": "Sidor", - "addon.mod_quiz.answercolon": "Svar:", - "addon.mod_quiz.attemptfirst": "Första försök", - "addon.mod_quiz.attemptlast": "Senaste försök", - "addon.mod_quiz.attemptnumber": "Försök", - "addon.mod_quiz.attemptquiznow": "Påbörja försök av testet nu", - "addon.mod_quiz.attemptstate": "Tillstånd", - "addon.mod_quiz.clearchoice": "Rensa mitt val", - "addon.mod_quiz.comment": "Kommentar", - "addon.mod_quiz.completedon": "Fullgjord den", - "addon.mod_quiz.confirmclose": "Du håller på att avsluta det här försöket. När Du väl har avslutat försöket kommer Du inte längre att kunna ändra Dina svar.", - "addon.mod_quiz.confirmstart": "Det här testet har en tidsbegränsning på {{$a}}. Tiden startar då du öppnar testet och du måste lämna in dina svar innen tiden är slut. Är du säker på att du vill börja nu?", - "addon.mod_quiz.confirmstartheader": "Test med tidsbegränsning", - "addon.mod_quiz.connectionerror": "Nätverksanslutning förlorade (autospara misslyckades).\n\nAnteckna alla svar som angavs på webbplatsen de sista minuterna på papper, försöka sedan att återansluta.\n\nNär anslutningen har återupprättats, bör dina svar sparas och detta meddelande kommer att försvinna.", - "addon.mod_quiz.continueattemptquiz": "Fortsätt med det senaste försöket", - "addon.mod_quiz.continuepreview": "Fortsätt med den senaste förhandsgranskningen", - "addon.mod_quiz.feedback": "Återkoppling", - "addon.mod_quiz.finishattemptdots": "Avsluta försök", - "addon.mod_quiz.grade": "Betyg/omdöme", - "addon.mod_quiz.gradeaverage": "Medelbetyg", - "addon.mod_quiz.gradehighest": "Högsta betyg", - "addon.mod_quiz.grademethod": "Betygsättningsmetod", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Poäng", - "addon.mod_quiz.modulenameplural": "Test", - "addon.mod_quiz.mustbesubmittedby": "Detta försöket måste skickas in av {{$a}}.", - "addon.mod_quiz.noquestions": "Inga frågor har ännu lagts till", - "addon.mod_quiz.noreviewattempt": "Du har inte rätt att granska det här försöket. ", - "addon.mod_quiz.notyetgraded": "Ännu inte betygssatta", - "addon.mod_quiz.outof": "av ett maximum på", - "addon.mod_quiz.outofpercent": "{{$a.grade}} av ett maximum på {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "övergripande återkoppling", - "addon.mod_quiz.overdue": "förfallen", - "addon.mod_quiz.overduemustbesubmittedby": "Tiden för detta test har nu gått ut. Det borde redan ha skickats in. Om du vill att detta testförsök ska poängsättas/betygssättas måste du skicka in det senast {{$a}}. Om du inte skickar in det då, kommer inga poäng från detta försök att räknas.", - "addon.mod_quiz.preview": "Förhandsgranska", - "addon.mod_quiz.previewquiznow": "Förhandsgranska testet nu", - "addon.mod_quiz.question": "Fråga", - "addon.mod_quiz.quiznavigation": "Navigation i test", - "addon.mod_quiz.quizpassword": "Lösenord för test", - "addon.mod_quiz.reattemptquiz": "Gör om testet", - "addon.mod_quiz.requirepasswordmessage": "För att få göra testet måste Du ha tillgång tilll lösenordet", - "addon.mod_quiz.returnattempt": "Återgå till försök", - "addon.mod_quiz.review": "Granska", - "addon.mod_quiz.reviewofattempt": "Statistik över försök {{$a}}", - "addon.mod_quiz.reviewofpreview": "Granskning av förhandsgranskning", - "addon.mod_quiz.showall": "Visa alla frågor på en sida", - "addon.mod_quiz.showeachpage": "Visa en sida i taget", - "addon.mod_quiz.startattempt": "Starta försök", - "addon.mod_quiz.startedon": "Startad (när)", - "addon.mod_quiz.stateabandoned": "Aldrig inskickad", - "addon.mod_quiz.statefinished": "Slutfört", - "addon.mod_quiz.statefinisheddetails": "Inskickat {{$a}}", - "addon.mod_quiz.stateinprogress": "Pågående", - "addon.mod_quiz.stateoverdue": "Tiden har gått ut", - "addon.mod_quiz.stateoverduedetails": "Måste skickas in av {{$a}}", - "addon.mod_quiz.status": "Status", - "addon.mod_quiz.submitallandfinish": "Skicka in allt och avsluta", - "addon.mod_quiz.summaryofattempt": "Sammanfattning av försök", - "addon.mod_quiz.summaryofattempts": "Sammanfattning av dina tidigare försök", - "addon.mod_quiz.timeleft": "Återstående tid", - "addon.mod_quiz.timetaken": "Tid för genomförande", - "addon.mod_quiz.yourfinalgradeis": "Ditt slutgiltiga betyg för detta test är {{$a}}.", - "addon.mod_resource.errorwhileloadingthecontent": "Fel vid laddning av innehåll.", - "addon.mod_resource.modulenameplural": "Filer", - "addon.mod_resource.openthefile": "Öppna filen", - "addon.mod_scorm.asset": "Tillgång", - "addon.mod_scorm.assetlaunched": "Tillgång - visad", - "addon.mod_scorm.attempts": "Försök", - "addon.mod_scorm.averageattempt": "Medel av försök", - "addon.mod_scorm.browse": "Bläddra", - "addon.mod_scorm.browsed": "Genombläddrad", - "addon.mod_scorm.browsemode": "Läge för förhandsgranskning", - "addon.mod_scorm.cannotcalculategrade": "Betyg kunde inte beräknas", - "addon.mod_scorm.completed": "Slutfört", - "addon.mod_scorm.contents": "Innehåll", - "addon.mod_scorm.dataattemptshown": "Dessa data tillhör försöket nummer {{number}}", - "addon.mod_scorm.enter": "Mata in", - "addon.mod_scorm.errorcreateofflineattempt": "Ett fel uppstod. Vänligen försök igen", - "addon.mod_scorm.errordownloadscorm": "Fel vid nedladdning av SCORM \"{{name}}\"", - "addon.mod_scorm.errorgetscorm": "Fel vid hämtning av SCORM-data", - "addon.mod_scorm.errorinvalidversion": "Tyvärr, applikationen stödjer endast SCORM 1.2", - "addon.mod_scorm.errornotdownloadable": "Nedladdningen av SCORM paket är avstängt i din Moodle webbplats. Kontakta din Moodle administratör", - "addon.mod_scorm.errornovalidsco": "Denna SCORM har ingen synlig SCO att ladda", - "addon.mod_scorm.errorpackagefile": "Tyvärr, applikationen stödjer endast ZIP filer", - "addon.mod_scorm.errorsyncscorm": "Fel vid synkronisering. Vänligen försök igen", - "addon.mod_scorm.exceededmaxattempts": "Du har redan använt samtliga tillåtna försök.", - "addon.mod_scorm.failed": "Det fungerade inte", - "addon.mod_scorm.firstattempt": "Första försöket", - "addon.mod_scorm.gradeaverage": "Medelbetyg/omdöme", - "addon.mod_scorm.gradeforattempt": "Betyg/omdöme för försök", - "addon.mod_scorm.gradehighest": "Högsta betyg/omdöme", - "addon.mod_scorm.grademethod": "Metod för betyg/omdömen", - "addon.mod_scorm.gradereported": "Betyg rapporterat", - "addon.mod_scorm.gradescoes": "Lärobjekt ", - "addon.mod_scorm.gradesum": "Summera betyg/omdöme(n)", - "addon.mod_scorm.highestattempt": "Högsta försöket", - "addon.mod_scorm.incomplete": "Inte komplett", - "addon.mod_scorm.lastattempt": "Senaste försöket", - "addon.mod_scorm.modulenameplural": "Scormpaket (flera)", - "addon.mod_scorm.newattempt": "Påbörja ett nytt försök", - "addon.mod_scorm.noattemptsallowed": "Antal tillåtna försök", - "addon.mod_scorm.noattemptsmade": "Antal försök som Du har genomfört", - "addon.mod_scorm.notattempted": "Inget försök", - "addon.mod_scorm.offlineattemptnote": "Detta försök har data som inte har synkroniserats", - "addon.mod_scorm.offlineattemptovermax": "Detta försök kan inte skickas eftersom du har överstigit det maximala antalet försök", - "addon.mod_scorm.organizations": "Organisationer", - "addon.mod_scorm.passed": "Genomförd", - "addon.mod_scorm.reviewmode": "Läge för granskning", - "addon.mod_scorm.score": "Resultat", - "addon.mod_scorm.scormstatusnotdownloaded": "Detta SCORM är inte nedladdat. Det kommer att laddas ner automatiskt när du öppnar den", - "addon.mod_scorm.scormstatusoutdated": "Detta SCORM har modifierats sedan sista nedladdningen. Det kommer att laddas ner automatiskt när du öppnar den", - "addon.mod_scorm.suspended": "Avstängd", - "addon.mod_scorm.toc": "Innehåll", - "addon.mod_scorm.warningofflinedatadeleted": "Vissa offlinedata av försöket {{nummer}} har tagits bort eftersom den inte kunnat skapas in ett nytt försök .", - "addon.mod_scorm.warningsynconlineincomplete": "Vissa försök kunde inte synkroniseras med sidan, eftersom senaste online försök inte är klar än . Vänligen avsluta online- försöket först .", - "addon.mod_survey.cannotsubmitsurvey": "Tyvärr fanns det ett problem att skicka din enkät . Var god försök igen.", - "addon.mod_survey.errorgetsurvey": "Fel vid hämtning av enkätdata", - "addon.mod_survey.ifoundthat": "Jag har funnit att", - "addon.mod_survey.ipreferthat": "Jag föredrar att", - "addon.mod_survey.modulenameplural": "Färdiga enkäter", - "addon.mod_survey.responses": "Svar", - "addon.mod_survey.results": "Resultat", - "addon.mod_url.accessurl": "Gå till webbadressen", - "addon.mod_url.modulenameplural": "URLer", - "addon.mod_url.pointingtourl": "Resursens pekar till följande webbadress", - "addon.mod_wiki.cannoteditpage": "Du kan inte redigera den här sidan.", - "addon.mod_wiki.createpage": "Skapa sida", - "addon.mod_wiki.editingpage": "Redigerar den här sidan '{{$a}}'", - "addon.mod_wiki.errorloadingpage": "Ett fel uppstod när sidan laddades", - "addon.mod_wiki.errornowikiavailable": "Det finns ingen Wiki tillgänglig som kan visas", - "addon.mod_wiki.gowikihome": "Wiki hem", - "addon.mod_wiki.map": "Karta", - "addon.mod_wiki.modulenameplural": "Wikis", - "addon.mod_wiki.newpagehdr": "Ny sida", - "addon.mod_wiki.newpagetitle": "Ny titel på sida", - "addon.mod_wiki.nocontent": "Det finns inget innehåll för den här sidan", - "addon.mod_wiki.notingroup": "Inte i grupp", - "addon.mod_wiki.pageexists": "Den här sidan finns redan, länkning dit pågår", - "addon.mod_wiki.pagename": "Namn på sida", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.viewpage": "Visa sida", - "addon.mod_wiki.wrongversionlock": "En annan användare har redigerat denna sida samtidigt som du redigerade och ditt innehåll är inte längre användbart.", - "addon.mod_workshop.alreadygraded": "Redan bedömd/betygssatt", - "addon.mod_workshop.areainstructauthors": "Instruktioner för inskickning av uppgifter", - "addon.mod_workshop.areainstructreviewers": "Instruktioner för bedömning/värdering", - "addon.mod_workshop.assess": "Bedöm/värdera/betygssätt", - "addon.mod_workshop.assessedsubmission": "Inskickad uppgift som är bedömd/värderad/betygssatt", - "addon.mod_workshop.assessmentform": "Formulär för bedömningar/värderingar/betygssättningar", - "addon.mod_workshop.assessmentsettings": "Inställningar för bedömning/värdering/betygssättning", - "addon.mod_workshop.assessmentweight": "Bedömnings viktning", - "addon.mod_workshop.assignedassessments": "Tilldelade inlämningar att bedöma", - "addon.mod_workshop.assignedassessmentsnone": "Du har ingen tilldelad inlämning att bedöma", - "addon.mod_workshop.conclusion": "Slutsats", - "addon.mod_workshop.createsubmission": "Skicka in", - "addon.mod_workshop.deletesubmission": "Radera inlämning", - "addon.mod_workshop.editsubmission": "Redigera inskickad uppgiftslösning", - "addon.mod_workshop.feedbackauthor": "Återkoppling för författaren", - "addon.mod_workshop.feedbackby": "Återkoppling från {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Återkoppling för recensenten/utvärderaren", - "addon.mod_workshop.givengrades": "Betyg satta", - "addon.mod_workshop.gradecalculated": "Beräknade betyg för inlämning", - "addon.mod_workshop.gradeinfo": "Betyg /omdöme: {{$a.received}} av {{$a.max}}", - "addon.mod_workshop.gradeover": "Åsidosätt betyg för inlämning", - "addon.mod_workshop.gradesreport": "Betygsrapport för workshop", - "addon.mod_workshop.gradinggrade": "Betyg/omdöme för betygssättning/omdöme", - "addon.mod_workshop.gradinggradecalculated": "Beräknade betyg för bedömning", - "addon.mod_workshop.gradinggradeof": "Betyg för bedömning (av {{$a}})", - "addon.mod_workshop.gradinggradeover": "Åsidosätt betyg för bedömning", - "addon.mod_workshop.modulenameplural": "Workshops", - "addon.mod_workshop.nogradeyet": "Inga betyg än", - "addon.mod_workshop.notassessed": "Ännu inte bedömd", - "addon.mod_workshop.notoverridden": "Ej överskriden", - "addon.mod_workshop.noyoursubmission": "Du har inte skickat in Ditt arbete ännu", - "addon.mod_workshop.overallfeedback": "Övergripande återkoppling", - "addon.mod_workshop.publishedsubmissions": "Inskickade uppgiftslösningar som har offentliggjorts", - "addon.mod_workshop.publishsubmission": "Publicera inlämning", - "addon.mod_workshop.publishsubmission_help": "Publicerade inlämningar är tillgängliga för andra när workshopen är stängd.", - "addon.mod_workshop.reassess": "Bedöm/värdera/betygssätt igen", - "addon.mod_workshop.receivedgrades": "Betyg mottagna", - "addon.mod_workshop.submissionattachment": "Bilaga", - "addon.mod_workshop.submissioncontent": "Innehåll för inlämnad uppgift", - "addon.mod_workshop.submissiongrade": "Bedömning/värdering/betygssättning av inskickat bidrag", - "addon.mod_workshop.submissiongradeof": "Betyg för inlämning (från {{$a}})", - "addon.mod_workshop.submissiontitle": "Titel", - "addon.mod_workshop.userplan": "Workshop planerare", - "addon.mod_workshop.weightinfo": "Viktning: {{$a}}", - "addon.mod_workshop.yourassessment": "Bedömning/värdering/betygssättning av dig själv", - "addon.mod_workshop.yourassessmentfor": "Din utvärdering av {{$a}}", - "addon.mod_workshop.yourgrades": "Dina betyg", - "addon.mod_workshop.yoursubmission": "Din inlämning", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "Kommentar till {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "Betyg för {{$a}}", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "Kommentar till {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "Kommentar till {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "Betyg för {{$a}}", - "addon.notes.addnewnote": "Lägg till en ny anteckning", - "addon.notes.coursenotes": "Anteckningar för kurs", - "addon.notes.deleteconfirm": "Vill Du ta bort den här anteckningen?", - "addon.notes.nonotes": "Det finns ännu inga anteckningar av den här typen", - "addon.notes.note": "Anteckning", - "addon.notes.notes": "Anteckningar", - "addon.notes.personalnotes": "Personliga anteckningar", - "addon.notes.publishstate": "Sammanhang", - "addon.notes.sitenotes": "Anteckningar på webbplatsnivå", - "addon.notes.userwithid": "Användare med ID {{id}}", - "addon.notifications.errorgetnotifications": "Fel att få meddelanden", - "addon.notifications.notificationpreferences": "Välj inställningar för notiser", - "addon.notifications.notifications": "Meddelanden", - "addon.notifications.playsound": "Spela upp ljud", - "addon.notifications.therearentnotificationsyet": "Det finns inga meddelanden", - "assets.countries.AD": "Andorra", - "assets.countries.AE": "Förenade Arabemiraten", - "assets.countries.AF": "Afghanistan", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Albanien", - "assets.countries.AM": "Armenien", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktis", - "assets.countries.AR": "Argentina", - "assets.countries.AT": "Österrike", - "assets.countries.AU": "Australien", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland", - "assets.countries.AZ": "Azerbadjan", - "assets.countries.BA": "Bosnien och Herzegovina", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladesh", - "assets.countries.BE": "Belgien", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgarien", - "assets.countries.BH": "Bahrein", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermudas", - "assets.countries.BO": "Bolivia", - "assets.countries.BR": "Brasilien", - "assets.countries.BS": "Bahamas", - "assets.countries.BT": "Bhutan", - "assets.countries.BW": "Botswana", - "assets.countries.BY": "Vitryssland", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CF": "Centralafrikanska Republiken", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "Schweiz", - "assets.countries.CI": "Elfenbenskusten", - "assets.countries.CL": "Chile", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Kina", - "assets.countries.CO": "Colombia", - "assets.countries.CR": "Costa Rica", - "assets.countries.CU": "Kuba", - "assets.countries.CV": "Kap Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CY": "Cypern", - "assets.countries.CZ": "Tjeckien", - "assets.countries.DE": "Tyskland", - "assets.countries.DJ": "Djibouti", - "assets.countries.DK": "Danmark", - "assets.countries.DM": "Dominica", - "assets.countries.DO": "Dominikanska republiken", - "assets.countries.DZ": "Algeriet", - "assets.countries.EC": "Ecuador", - "assets.countries.EE": "Estland", - "assets.countries.EG": "Egypten", - "assets.countries.EH": "Västsahara", - "assets.countries.ER": "Eritrea", - "assets.countries.ES": "Spanien", - "assets.countries.ET": "Etiopien", - "assets.countries.FI": "Finland", - "assets.countries.FJ": "Fijiöarna", - "assets.countries.FK": "Falklandsöarna", - "assets.countries.FM": "Mikronesien", - "assets.countries.FO": "Färöarna", - "assets.countries.FR": "Frankrike", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Storbritannien", - "assets.countries.GD": "Grenada", - "assets.countries.GE": "Georgien", - "assets.countries.GF": "Franska Guyana", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Ghana", - "assets.countries.GI": "Gibraltar", - "assets.countries.GL": "Grönland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Guinea", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GR": "Grekland", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Guinea-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Kroatien", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Ungern", - "assets.countries.ID": "Indonesien", - "assets.countries.IE": "Irland", - "assets.countries.IL": "Israel", - "assets.countries.IN": "Indien", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "Iran", - "assets.countries.IS": "Island", - "assets.countries.IT": "Italien", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaica", - "assets.countries.JO": "Jordanien", - "assets.countries.JP": "Japan", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kirgistan", - "assets.countries.KH": "Kambodja", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Comoros", - "assets.countries.KN": "Saint Kitts And Nevis", - "assets.countries.KP": "Nordkorea", - "assets.countries.KR": "Sydkorea", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Cayman Islands", - "assets.countries.KZ": "Kazakhstan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Libanon", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberia", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litauen", - "assets.countries.LU": "Luxemburg", - "assets.countries.LV": "Lettland", - "assets.countries.LY": "Libyen", - "assets.countries.MA": "Marocko", - "assets.countries.MC": "Monaco", - "assets.countries.MD": "Moldavien", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshall öarna", - "assets.countries.MK": "Makedonien", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Mongoliet", - "assets.countries.MO": "Macao", - "assets.countries.MQ": "Martinique", - "assets.countries.MR": "Mauritanien", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Maldiverna", - "assets.countries.MW": "Malawi", - "assets.countries.MX": "Mexiko", - "assets.countries.MY": "Malaysia", - "assets.countries.MZ": "Mozambique", - "assets.countries.NA": "Mozambique", - "assets.countries.NC": "Nya Kaledonien", - "assets.countries.NE": "Niger", - "assets.countries.NF": "Norfolk Island", - "assets.countries.NG": "Nigeria", - "assets.countries.NI": "Nicaragua", - "assets.countries.NL": "Holland", - "assets.countries.NO": "Norge", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru", - "assets.countries.NU": "Niue", - "assets.countries.NZ": "Nya Zeeland", - "assets.countries.OM": "Oman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "French Polynesia", - "assets.countries.PG": "Papua Nya Guinea", - "assets.countries.PH": "Filipinerna", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polen", - "assets.countries.PM": "Saint Pierre And Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Puerto Rico", - "assets.countries.PS": "Palestina", - "assets.countries.PT": "Portugal", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Qatar", - "assets.countries.RE": "Réunion", - "assets.countries.RO": "Rumänien", - "assets.countries.RS": "Serbien", - "assets.countries.RU": "Ryssland", - "assets.countries.RW": "Rwanda", - "assets.countries.SA": "Saudiarabien", - "assets.countries.SB": "Solomon öarna", - "assets.countries.SC": "Seychellerna", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "Sverige", - "assets.countries.SG": "Singapore", - "assets.countries.SH": "St. Helena", - "assets.countries.SI": "Slovenien", - "assets.countries.SJ": "Svalbard och Jan Mayen-öarna", - "assets.countries.SK": "Slovakien", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somalia", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Sydsudan", - "assets.countries.ST": "Sao Tome And Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SY": "Syrien", - "assets.countries.SZ": "Swaziland", - "assets.countries.TC": "Turks And Caicos Islands", - "assets.countries.TD": "Tcad", - "assets.countries.TF": "French Southern Territories", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Thailand", - "assets.countries.TJ": "Tajikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Turkmenistan", - "assets.countries.TN": "Tunisien", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Turkiet", - "assets.countries.TT": "Trinidad ochTobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Taiwan", - "assets.countries.TZ": "Tanzania", - "assets.countries.UA": "Ukraina", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "USA", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Uzbekistan", - "assets.countries.VA": "Vatikanen", - "assets.countries.VC": "Saint Vincent And The Grenadines", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "Virgin Islands, British", - "assets.countries.VI": "Virgin Islands, U.S.", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis And Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Jemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Sydafrika", - "assets.countries.ZM": "Zambia", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB bok", - "assets.mimetypes.application/msword": "Word-dokument", - "assets.mimetypes.application/pdf": "PDF-dokument", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle säkerhetskopia", - "assets.mimetypes.application/vnd.ms-excel": "Kalkylblad Excel", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint presentation", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint presentation", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint bildspel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel-mall", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word-dokument", - "assets.mimetypes.archive": "Arkiv ({{$a.EXT}})", - "assets.mimetypes.audio": "Ljudfil ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "fil", - "assets.mimetypes.group:web_image": "Bildfiler som används på webben", - "assets.mimetypes.image": "Bild ({{$a.MIMETYPE2}})", - "assets.mimetypes.text/html": "HTML-dokument", - "assets.mimetypes.text/plain": "textfil", - "assets.mimetypes.text/rtf": "RTF-dokument", - "core.accounts": "Konton", - "core.add": "Lägg till", - "core.agelocationverification": "Ålder och platsverifiering", - "core.ago": "För {{$a}} sedan", - "core.all": "Alla", - "core.allgroups": "Alla grupper", - "core.allparticipants": "Alla deltagare", - "core.answer": "Svar", - "core.answered": "Besvarad/e", - "core.areyousure": "Är du säker?", - "core.back": "Tillbaka", - "core.block.blocks": "Block", - "core.cancel": "Avbryt", - "core.cannotconnect": "Kan inte ansluta: Kontrollera att webbadressen är korrekt och att din webbplats använder Moodle {{$a}} eller senare", - "core.cannotdownloadfiles": "Nedladdning av filer är inaktiverad. Vänligen kontakta webbsidans administratör.", - "core.category": "Kategori", - "core.choose": "Välj", - "core.choosedots": "Välj...", - "core.clearsearch": "Rensa sökning", - "core.clicktohideshow": "Klicka för att expandera eller fälla ihop", - "core.clicktoseefull": "Klicka för att se hela innehållet", - "core.close": "Stäng", - "core.comments": "Kommentarer", - "core.comments.addcomment": "Lägg till en kommentar...", - "core.comments.comments": "Kommentarer", - "core.comments.commentscount": "Kommentarer ({{$a}})", - "core.comments.deletecommentbyon": "Radera kommentarer publicerad av {{$a.user}} på {{$a.time}}", - "core.comments.eventcommentcreated": "Kommentar skapad", - "core.comments.eventcommentdeleted": "Kommentar raderad", - "core.comments.nocomments": "Inga kommentarer", - "core.comments.savecomment": "Spara kommentar", - "core.commentscount": "Kommentarer ({{$a}})", - "core.completion-alt-auto-fail": "Fullföljd (uppnådde inte godkänt resultat)", - "core.completion-alt-auto-n": "Inte avslutad: {{$a}}", - "core.completion-alt-auto-pass": "Fullföljd (godkänt resultat)", - "core.completion-alt-auto-y": "Fullföljd", - "core.completion-alt-manual-n": "Ej fullföljd; välj för att markera som fullföljd", - "core.completion-alt-manual-y": "Fullföljd; välj för att markera som ej fullföljd", - "core.confirmdeletefile": "Är Du säker på att Du vill ta bort den här filen?", - "core.confirmopeninbrowser": "Vill du öppna den i webbläsaren ?", - "core.considereddigitalminor": "Du är för ung för att skapa ett konto på denna hemsida.", - "core.content": "Innehåll", - "core.contentlinks.chooseaccount": "Välj konto", - "core.contentlinks.chooseaccounttoopenlink": "Välj ett konto för att öppna länken med", - "core.contentlinks.confirmurlothersite": "Länken tillhör en annan webbsida. Vill du öppna den?", - "core.contentlinks.errornoactions": "Det gick inte att utföra den här länken.", - "core.contentlinks.errornosites": "Hittade inge webbsida som kunde hantera den här länken", - "core.continue": "Fortsätt", - "core.course": "Kurs", - "core.course.allsections": "Alla sektioner", - "core.course.confirmdownload": "Du är på väg att ladda ner {{size}}. Är du säker att du vill fortsätta?", - "core.course.confirmdownloadunknownsize": "Det gick inte att beräkna storleken på nedladdningen . Det gick inte att beräkna storleken på nedladdningen . Är du säker att du vill fortsätta och ladda ner?", - "core.course.contents": "Innehåll", - "core.course.couldnotloadsectioncontent": "Det gick inte att läsa sektionens innehåll. Vänligen försök igen senare.", - "core.course.couldnotloadsections": "Det gick inte att läsa sektionen. Vänligen försök igen senare.", - "core.course.coursesummary": "Sammanfattning av kurs", - "core.course.errordownloadingsection": "Fel vid nedladdning av sektion", - "core.course.errorgetmodule": "Fel vid hämtning av data", - "core.course.hiddenfromstudents": "Dolt för studenter/deltagare/elever/lärande", - "core.course.hiddenoncoursepage": "Tillgängliga men inte synbar på kurssidan", - "core.course.nocontentavailable": "Inget innehåll tillgängligt för tillfället.", - "core.course.overriddennotice": "Ditt sammanfattningsbetyg för den här aktiviteten har justerats manuellt.", - "core.course.sections": "Sektioner", - "core.coursedetails": "Kursinformation", - "core.courses.addtofavourites": "Markera kursen som favorit", - "core.courses.allowguests": "Denna kurs tillåter gäster.", - "core.courses.availablecourses": "Tillgängliga kurser", - "core.courses.categories": "Kurskategorier", - "core.courses.confirmselfenrol": "Är du säker att du vill registrera dig i den här kursen ?", - "core.courses.courses": "Kurser", - "core.courses.enrolme": "Registrera mig", - "core.courses.errorloadcourses": "Ett fel uppstod vid inläsning av kursen", - "core.courses.errorsearching": "Fel uppstod vid sökning", - "core.courses.errorselfenrol": "Fel uppstod vid registrering", - "core.courses.filtermycourses": "Filtrera mina kurser", - "core.courses.frontpage": "Ingångssida", - "core.courses.hidecourse": "Dölj", - "core.courses.ignore": "Låt detta vara", - "core.courses.mycourses": "Mina kurser", - "core.courses.mymoodle": "Mitt Moodle", - "core.courses.nocourses": "Det finns ingen kursinformation att visa.", - "core.courses.nocoursesyet": "Inga kurser i denna kategori", - "core.courses.nosearchresults": "Inga resultat", - "core.courses.notenroled": "Du är inte registrerad i den här kursen", - "core.courses.notenrollable": "Du kan inte självregistrera dig i kursen", - "core.courses.password": "Kursnyckel", - "core.courses.paymentrequired": "För att få tillgång till den här kursen måste Du betala först.", - "core.courses.paypalaccepted": "Betalningar via Paypal accepterade", - "core.courses.reload": "Uppdatera", - "core.courses.removefromfavourites": "Ta bort kursen från Favoriter", - "core.courses.search": "Sök", - "core.courses.searchcourses": "Sök kurser", - "core.courses.searchcoursesadvice": "Du kan använda knappen \"Sök kurser\" för att få tillgång som gäst eller registrera dig på kurser som tillåter det .", - "core.courses.selfenrolment": "Kursnyckel (Student)", - "core.courses.sendpaymentbutton": "Skicka betalning via Paypal", - "core.courses.show": "Visa denna kurs", - "core.courses.totalcoursesearchresults": "Total antal kurser: {{$a}}", - "core.date": "Datum", - "core.day": "dag", - "core.days": "dagar", - "core.decsep": ",", - "core.delete": "Ta bort", - "core.deleting": "ta bort", - "core.description": "Beskrivning", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "", - "core.dftimedate": "h[:]mm A", - "core.digitalminor_desc": "Var snäll och fråga din förälder/vårdnadshavare att kontakta:", - "core.done": "Färdig", - "core.download": "Ladda ner", - "core.downloading": "Laddar ner", - "core.edit": "Redigera", - "core.editor.autosavesucceeded": "Utkast sparat.", - "core.editor.bold": "Fet stil", - "core.editor.clear": "Radera formatering", - "core.editor.h3": "Rubrik (stor)", - "core.editor.h4": "Rubrik (mellan)", - "core.editor.h5": "Rubrik (liten)", - "core.editor.italic": "Kursivt", - "core.editor.p": "Avsnitt", - "core.editor.strike": "Genomstruken", - "core.editor.textrecovered": "Ett utkast av denna text har återställts automatiskt.", - "core.editor.underline": "Understruken", - "core.editor.unorderedlist": "Punktlista", - "core.error": "Fel", - "core.errorchangecompletion": "Ett fel uppstod när du ändrade status för fullföljande. Var god försök igen.", - "core.errordownloading": "Fel vid nedladdning av fil", - "core.errordownloadingsomefiles": "Fel vid hämtning av modulens filer. Vissa filer kanske saknas .", - "core.errorinvalidresponse": "Fick ogiltigt svar. Kontakta din Moodleadministrator om felet kvarstår.", - "core.erroropenfilenoapp": "Fel vid öppning av filen: ingen app hittades som kan öppna denna typ av fil.", - "core.erroropenfilenoextension": "Fel vid öppning av filen: Filen saknar definition", - "core.erroropenpopup": "Aktiviteten försöker öppna en popup . Detta stöds inte i den här appen.", - "core.favourites": "Favoritmärkt", - "core.filename": "Filnamn", - "core.filenotfound": "Det gick tyvärr inte att hitta filen.", - "core.fileuploader.addfiletext": "Lägg till fil", - "core.fileuploader.audio": "Audio", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.confirmuploadfile": "Du är på väg att ladda upp {{size}}. Är du säker på att du vill fortsätta?", - "core.fileuploader.errorcapturingaudio": "Fel med att spela in ljud", - "core.fileuploader.errorcapturingimage": "Fel med att fånga in bild", - "core.fileuploader.errorcapturingvideo": "Fel vid inspelning av video", - "core.fileuploader.errorgettingimagealbum": "Fel att få bild från album.", - "core.fileuploader.errormustbeonlinetoupload": "Du måste vara online för att ladda upp filer.", - "core.fileuploader.errornoapp": "Du behöver att ha en app installerat för att utföra den här åtgärden.", - "core.fileuploader.errorreadingfile": "Fel vid läsning av fil.", - "core.fileuploader.errorwhileuploading": "Ett fel uppstod under filöverföringen.", - "core.fileuploader.file": "Fil", - "core.fileuploader.filesofthesetypes": "Accepterade filtyper:", - "core.fileuploader.fileuploaded": "Fil laddades upp", - "core.fileuploader.invalidfiletype": "filtypen {{$a}} kan inte accepteras", - "core.fileuploader.more": "Fler", - "core.fileuploader.photoalbums": "Fotoalbum", - "core.fileuploader.readingfile": "Läsa fil", - "core.fileuploader.uploadafile": "Ladda upp en fil", - "core.fileuploader.uploading": "Laddar upp", - "core.fileuploader.video": "Video", - "core.filter": "Filter", - "core.folder": "Katalog", - "core.forcepasswordchangenotice": "Du måste använda Ditt lösenord för att kunna fortsätta.", - "core.fulllistofcourses": "Alla kurser", - "core.grades.average": "Medelvärde", - "core.grades.badgrade": "Det betyg som har avgivits är ogiltigt", - "core.grades.contributiontocoursetotal": "Bidrag till totalen för kursen", - "core.grades.feedback": "Återkoppling", - "core.grades.grade": "Betyg", - "core.grades.gradeitem": "Komponent för betyg/omdöme", - "core.grades.grades": "Betyg/omdömen", - "core.grades.lettergrade": "Bokstavsbetyg/omdöme", - "core.grades.nogradesreturned": "Inga bokstavsbetyg har returnerats", - "core.grades.nooutcome": "Inget resultat", - "core.grades.percentage": "Procenttal", - "core.grades.range": "Omfång", - "core.grades.rank": "Ranking", - "core.grades.weight": "vikt", - "core.group": "Grupp", - "core.groupsseparate": "Olika grupper", - "core.groupsvisible": "Synliga grupper", - "core.help": "Hjälp", - "core.hide": "Dölj", - "core.hour": "timme", - "core.hours": "timmar", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Bild", - "core.imageviewer": "Bildvisare", - "core.info": "Information", - "core.invalidformdata": "Felaktiga data i formulär", - "core.labelsep": ":", - "core.lastaccess": "Senaste access", - "core.lastmodified": "Senast modifierad", - "core.list": "Lista", - "core.listsep": ";", - "core.loading": "Laddar", - "core.location": "Plats", - "core.login.auth_email": "E-postbaserad autenticering", - "core.login.authenticating": "Autentisera", - "core.login.cancel": "Avbryt", - "core.login.changepassword": "Ändra lösenord", - "core.login.confirmdeletesite": "Är du säker på att du vill ta bort webbsidan {{sitename}}?", - "core.login.connect": "Anslut!", - "core.login.connecttomoodle": "Anslut till Moodle", - "core.login.createaccount": "Skapa mitt nya konto", - "core.login.createuserandpass": "Skapa ett nytt användarnamn och lösenord för att logga in med.", - "core.login.credentialsdescription": "Ange ditt användarnamn och lösenord för att logga på", - "core.login.emailconfirmsent": "

                      Vi har skickat ett e-postbrev som Du bör ha fått
                      till Din adress på {{$a}}

                      \n

                      Det innehåller enkla instruktioner som hjälper Dig
                      att fullfölja Din registrering.

                      \n

                      Om Du stöter på problem, är Du välkommen att
                      kontakta den som ansvarar för webbplatsen.

                      ", - "core.login.emailconfirmsentsuccess": "Bekräftelsemejlet har skickats.", - "core.login.errordeletesite": "Ett fel inträffade vid borttagning av denna webbsida. Var god försök igen.", - "core.login.errorupdatesite": "Ett fel inträffade vid uppdatering av webbplatsens token.", - "core.login.firsttime": "Är det första gången Du är här?", - "core.login.forcepasswordchangenotice": "Du måste använda Ditt lösenord för att kunna fortsätta.", - "core.login.forgotten": "Har Du glömt Ditt användarnamn och lösenord?", - "core.login.help": "Hjälp", - "core.login.helpmelogin": "

                      För att logga in måste du kontrollera att: 1. Moodle webbplats är version 2.4 eller högre
                      2. Moodle administratören har aktiverat Mobil åtkomst

                      För att testa appen på en Moodle demo site skriv lärare eller elev i i fält för Användarnamn och klicka på Lägg till knapp . Besök Moodle webbplats för mer detaljerad Moodle information och hjälp. ", - "core.login.instructions": "Instruktioner", - "core.login.invalidaccount": "Kontrollera dina inloggningsuppgifter stämmer eller be webbplatsadministratören att kontrollera webbplatsens konfiguration", - "core.login.invaliddate": "Ogiltigt datum", - "core.login.invalidemail": "Ogiltig e-postadress", - "core.login.invalidmoodleversion": "Ogiltig Moodle version. Lägsta version som krävs är", - "core.login.invalidsite": "Den webbadress är ogiltig.", - "core.login.invalidurl": "Ogiltig URL angiven", - "core.login.localmobileunexpectedresponse": "Kontrollen för Moodle mobila funktioner returnerade ett oväntat svar. Du kommer att autentiseras med mobila standard tjänsten.", - "core.login.login": "Logga in", - "core.login.loginbutton": "Logga In!", - "core.login.logininsiterequired": "Du måste logga in på webbplatsen via en webbläsare", - "core.login.loginsteps": "Hej!\n
                      \nDu måste bl.a. skapa ett nytt användarkonto på denna webbplats för att få tillgång till de kurser som Du vill delta i. Varje individuell kurs kan också ha en engångsnyckel \"kursnyckel\". Den behöver Du dock inte förrän senare.\n Så här skapar Du Ditt konto:\n

                        \n
                      1. Fyll i formuläret på sidan \nNytt konto med de efterfrågade\nuppgifterna om Dig själv.
                      2. \n
                      3. Ett e-postmeddelande kommer därefter\nomedelbart att sändas till\nDin e-postadress.
                      4. \n
                      5. Läs din e-post, och klicka på webblänken som den innehåller.
                      6. \n
                      7. Ditt konto kommer därmed att bekräftas\noch Du kommer att loggas in.
                      8. \n
                      9. Nu kan Du välja vilken kurs Du\nvill delta i.
                      10. \n
                      11. Om Du måste ange en \"kursnyckel\" - så\nfår Du använda den som Din lärare har\ngivit Dig. Med den kan Du registrera\nDig på en sådan kurs som kräver det.
                      12. \n
                      13. Nu kan Du använda hela kursen.\nFrån och med nu behöver Du bara skriva\nin Ditt användarnamn och lösenord\n(i formuläret till vänster på denna sida)\nför att logga in och för att nå de kurser\nsom Du är registrerad på.
                      14. \n
                      \nOBS! Genom att Du bekräftar kontot så samtycker\nDu till databehandling enligt Personuppgiftslagen.\nOm Du är osäker på vad det innebär så kan Du hitta\nmer information här: 'http://www.datainspektionen.se/lagar-och-regler/personuppgiftslagen/\n'\n\nLycka till!", - "core.login.missingemail": "E-postadress saknas", - "core.login.missingfirstname": "Förnamn saknas", - "core.login.missinglastname": "Efternamn saknas", - "core.login.mobileservicesnotenabled": "Mobila tjänster är inte aktiverade på din sida. Vänligen kontakta din Moodleadministrator om du tycker att mobil åtkomst ska aktiveras.", - "core.login.mustconfirm": "Du måste bekräfta din inloggning.", - "core.login.newaccount": "Nytt konto", - "core.login.notloggedin": "Du måste vara inloggad.", - "core.login.password": "Lösenord", - "core.login.passwordforgotten": "Förlorat lösenord", - "core.login.passwordforgotteninstructions2": "För att återställa ditt lösenord, fyll i ditt användarnamn eller din e-postadress nedan. Om vi kan hitta dig i databasen kommer ett mail att skickas till din e-postadress med vidare instruktioner.", - "core.login.passwordrequired": "Lösenord krävs", - "core.login.policyaccept": "Jag förstår och accepterar", - "core.login.policyagree": "Du måste acceptera denna policy för få fortsätta att använda denna webbplats. Accepterar Du denna policy?", - "core.login.policyagreement": "Avtal angående webbplatsens policy.", - "core.login.policyagreementclick": "Klicka här för att läsa avtalet angående webbplatsens policy.", - "core.login.potentialidps": "Logga in med ditt konto på:", - "core.login.profileinvaliddata": "Ogiltigt värde", - "core.login.reconnect": "Anslut", - "core.login.reconnectdescription": "Din autentiseringsnyckel är ogiltig eller har upphört att gälla. Anslut till sidan på nytt", - "core.login.reconnectssodescription": "Din autentiseringsnyckel är ogiltig eller har upphört att gälla. Anslut till sidan på nytt. Du måste logga in på webbsidan via ett webbläsarfönster", - "core.login.resendemail": "Skicka om e-postmeddelande.", - "core.login.selectacountry": "Välj ett land", - "core.login.siteaddress": "Webbsidaadress", - "core.login.siteinmaintenance": "Din webbplats är i underhållsläge", - "core.login.siteurl": "Webbadress", - "core.login.siteurlrequired": "Site URL krävs, dvs http://www.yourmoodlesite.abc eller https://www.yourmoodlesite.efg ", - "core.login.startsignup": "Starta nu genom att skapa ett nytt konto!", - "core.login.supplyinfo": "Mer detaljer", - "core.login.username": "Användarnamn", - "core.login.usernameoremail": "Skriv antingen in användarnamn eller e-postadress", - "core.login.usernamerequired": "Användarnamn krävs", - "core.login.usernotaddederror": "Användaren har inte lagts till, detta p.g. a. ett okänt fel.", - "core.login.webservicesnotenabled": "Web Services är inte aktiverat i din webbplats. Vänligen kontakta din Moodleadministrator om du tycker att mobil åtkomst ska aktiveras.", - "core.lostconnection": "Vi förlorade anslutningen. Du måste ansluta igen. Din token är nu ogiltigt.", - "core.mainmenu.changesite": "Byt webbsida", - "core.mainmenu.help": "Hjälp", - "core.mainmenu.logout": "Logga ut", - "core.mainmenu.website": "Webbsida", - "core.maxsizeandattachments": "Maximal storlek för nya filer: {{$a.size}}, max bilagor: {{$a.attachments}}", - "core.min": "minut", - "core.mins": "minuter", - "core.misc": "Övrigt", - "core.mod_assign": "Inlämningsuppgift", - "core.mod_assignment": "Uppgift (2.2)", - "core.mod_book": "Bok", - "core.mod_chat": "Direktsamtal", - "core.mod_choice": "Opinionsundersökning", - "core.mod_data": "Databas", - "core.mod_database": "Databas", - "core.mod_feedback": "Egen enkät", - "core.mod_file": "Fil", - "core.mod_folder": "Mapp", - "core.mod_forum": "Forum", - "core.mod_glossary": "Ord- och begreppslista", - "core.mod_ims": "Innehållspaket av typ IMS", - "core.mod_imscp": "Innehållspaket av typ IMS", - "core.mod_label": "Etikett", - "core.mod_lesson": "Lektion", - "core.mod_page": "Sida", - "core.mod_quiz": "Test", - "core.mod_resource": "Resurs", - "core.mod_scorm": "SCORM-paket", - "core.mod_survey": "Färdig enkät", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Workshop", - "core.moduleintro": "Beskrivning", - "core.more": "mer", - "core.mygroups": "Mina grupper", - "core.name": "Namn", - "core.networkerrormsg": "Nätverket är inte aktiverat eller fungerar inte", - "core.never": "Aldrig", - "core.next": "Nästa", - "core.no": "Nej", - "core.nocomments": "Inga kommentarer", - "core.nograde": "Inget betyg", - "core.none": "Ingen", - "core.nopermissions": "Du har tyvärr f.n. inte tillstånd att göra detta ({{$a}})", - "core.noresults": "Inga resultat", - "core.noselection": "Inga valda", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Den här profilen är inte tillgänglig eftersom den här användaren inte är registrerad på den här kursen.", - "core.notice": "Meddelande", - "core.notingroup": "Du måste tyvärr vara registrerad i en grupp för att ta del av den här aktiviteten.", - "core.now": "nu", - "core.numwords": "{{$a}} ord", - "core.offline": "Frånkopplat läge", - "core.ok": "OK", - "core.online": "Uppkopplad", - "core.openfullimage": "Klick här för att visa bilden i full storlek", - "core.openinbrowser": "Öppna i webbläsare", - "core.othergroups": "Andra grupper", - "core.pagea": "Sida {{$a}}", - "core.paymentinstant": "Använd knappen nedan för att betala och bli registrerad inom några minuter!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Telefon", - "core.pictureof": "Bild av {{$a}}", - "core.previous": "Tidigare", - "core.proceed": "Fortsätt", - "core.pulltorefresh": "Dra för att uppdatera", - "core.question.answer": "Svar", - "core.question.answersaved": "Svar sparade", - "core.question.complete": "Komplett", - "core.question.correct": "Rätt", - "core.question.feedback": "Återkoppling", - "core.question.incorrect": "Felaktig", - "core.question.information": "Information", - "core.question.invalidanswer": "Ofullständigt svar", - "core.question.notanswered": "Ej besvarad", - "core.question.notyetanswered": "Inte besvarad än", - "core.question.partiallycorrect": "Delvis korrekt", - "core.question.questionno": "Fråga {{$a}}", - "core.question.requiresgrading": "Kräver rättning", - "core.rating.aggregateavg": "Betyg/omdömen i genomsnitt (medel)", - "core.rating.aggregatecount": "Räkning av betyg/omdömen", - "core.rating.aggregatemax": "Maximalt betyg/omdöme", - "core.rating.aggregatemin": "Minimalt betyg/omdöme", - "core.rating.aggregatesum": "Sammanlagda betyg/omdömen", - "core.rating.noratings": "Det finns inga inskickade betyg/omdömen", - "core.rating.rating": "Betyg/omdöme", - "core.rating.ratings": "Betyg/omdömen", - "core.refresh": "Uppdatera", - "core.remove": "Ta bort", - "core.required": "Obligatorisk", - "core.requireduserdatamissing": "Den här användaren saknar vissa nödvändiga profildata. Vänligen fyll i uppgifterna i din Moodle och försök igen.
                      {{$a}}", - "core.resourcedisplayopen": "Öppna", - "core.resources": "Resurser", - "core.restore": "Återställ", - "core.restricted": "Begränsad", - "core.save": "Spara", - "core.savechanges": "Spara ändringar.", - "core.search": "Sök", - "core.searching": "Söker", - "core.searchresults": "Sökresultat", - "core.sec": "Sekund", - "core.secs": "Sekunder", - "core.seemoredetail": "Klicka här för att se fler detaljer", - "core.selectacategory": "Vänligen välj en kategori", - "core.selectacourse": "Välj en kurs", - "core.selectagroup": "Välj en grupp", - "core.send": "Skicka", - "core.sending": "Skickar", - "core.serverconnection": "Fel vid anslutning till servern", - "core.settings.about": "Om", - "core.settings.cordovadevicemodel": "", - "core.settings.cordovadeviceosversion": "", - "core.settings.cordovadeviceplatform": "", - "core.settings.cordovadeviceuuid": "", - "core.settings.cordovaversion": "", - "core.settings.currentlanguage": "Nuvarande språk", - "core.settings.debugdisplay": "Visa felmeddelande (debug)", - "core.settings.deletesitefiles": "Är du säker på att du vill ta bort de nedladdade filerna från den här webbplatsen?", - "core.settings.deletesitefilestitle": "Ta bort platsfiler", - "core.settings.deviceinfo": "Info mobil enhet", - "core.settings.deviceos": "Mobil enhet operativsystem", - "core.settings.disableall": "Tillfälligt inaktivera meddelanden", - "core.settings.displayformat": "Visningsformat", - "core.settings.enabledownloadsection": "Aktivera hämtning sektioner", - "core.settings.enablesyncwifi": "Tillåt synk bara via Wi-Fi", - "core.settings.errordeletesitefiles": "Fel vid borttagning av webbplatsfiler.", - "core.settings.errorsyncsite": "Fel vid synkronisering. Kontrollera din Internetanslutning och försök igen", - "core.settings.estimatedfreespace": "Beräknad ledigt utrymme", - "core.settings.filesystemroot": "Filsystem root", - "core.settings.general": "Allmänt", - "core.settings.language": "Språk", - "core.settings.license": "Licens", - "core.settings.localnotifavailable": "Lokala meddelanden tillgängliga", - "core.settings.locationhref": "Webbvisning URL", - "core.settings.locked": "låst", - "core.settings.loggedin": "Uppkopplad", - "core.settings.loggedoff": "Inte uppkopplad", - "core.settings.navigatorlanguage": "Navigator språk", - "core.settings.navigatoruseragent": "", - "core.settings.networkstatus": "Status för Internetanslutning", - "core.settings.preferences": "Mina inställningar", - "core.settings.reportinbackground": "Rapportera fel per automatik", - "core.settings.settings": "Inställningar", - "core.settings.sites": "Webbplatser", - "core.settings.spaceusage": "Utrymmesanvändning", - "core.settings.synchronization": "Synkronisering", - "core.settings.synchronizenow": "Synkronisera nu", - "core.settings.syncsettings": "Synkronisation inställningar", - "core.settings.total": "Totalt", - "core.settings.wificonnection": "WiFi-anslutning", - "core.show": "Visa", - "core.showless": "Visa färre...", - "core.showmore": "Visa mer...", - "core.site": "Webbplats", - "core.sitehome.sitehome": "Hem för webbplats", - "core.sitehome.sitenews": "Webbplatsnyheter", - "core.sitemaintenance": "Det pågår f.n underhåll på webbplatsen och det är för tillfället inte tillgänglig.", - "core.sizeb": "bytes", - "core.sizegb": "Gb", - "core.sizekb": "Kb", - "core.sizemb": "Mb", - "core.sizetb": "Tb", - "core.sort": "Sortera", - "core.sortby": "Sortera enligt", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "Skicka", - "core.success": "Framgång", - "core.tablet": "Tablet", - "core.tag.noresultsfor": "Inget resultat för \"{{$a}}\"", - "core.tag.searchtags": "Sökrubriker", - "core.tag.tag": "Etikett", - "core.tag.tags": "etiketter", - "core.teachers": "Distanslärare/
                      handledare/
                      coacher", - "core.thisdirection": "ltr", - "core.time": "Tid", - "core.timesup": "Tiden är slut!", - "core.today": "Idag", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.unexpectederror": "Oväntad fel. Stäng och öppna programmet igen för ett nytt försök.", - "core.unknown": "Okänd", - "core.unlimited": "Obegränsad", - "core.unzipping": "Packa upp", - "core.upgraderunning": "Webbplatsen uppgraderas, försök igen senare.", - "core.user": "Användare", - "core.user.address": "Adress", - "core.user.city": "Stad/ort", - "core.user.contact": "Kontakt", - "core.user.country": "Land", - "core.user.description": "Beskrivning", - "core.user.detailsnotavailable": "Detaljerna till denna användare är inte tillgängliga för dig.", - "core.user.editingteacher": "Distanslärare/
                      handledare/
                      coach etc", - "core.user.email": "E-postadress", - "core.user.emailagain": "E-post (igen)", - "core.user.firstname": "Förnamn", - "core.user.interests": "Intressen", - "core.user.lastname": "Efternamn", - "core.user.manager": "Administratör", - "core.user.newpicture": "Ny bild", - "core.user.noparticipants": "Inga deltagare hittades för denna kurs", - "core.user.participants": "Deltagare", - "core.user.phone1": "Telefon", - "core.user.phone2": "Mobiltelefon", - "core.user.roles": "Roller", - "core.user.student": "Lärande", - "core.user.teacher": "Icke-redigerande lärare", - "core.user.webpage": "Webbsida", - "core.userdeleted": "Ditt användarkonto har tagits bort.", - "core.userdetails": "Detaljer om användare", - "core.users": "Användare", - "core.view": "Visa", - "core.viewprofile": "Visa profil", - "core.wsfunctionnotavailable": "Webbtjänstfunktion är inte tillgänglig.", - "core.year": "år", - "core.years": "år", - "core.yes": "Ja" -} \ No newline at end of file diff --git a/src/assets/lang/tg.json b/src/assets/lang/tg.json deleted file mode 100644 index 713e71dd3..000000000 --- a/src/assets/lang/tg.json +++ /dev/null @@ -1,1208 +0,0 @@ -{ - "addon.badges.badges": "Бейҷҳо", - "addon.block_activitymodules.pluginname": "Унсурҳои курс", - "addon.block_blogmenu.pluginname": "Менюи блог", - "addon.block_blogrecent.pluginname": "Сабтҳои нави блог", - "addon.block_blogtags.pluginname": "Тегҳои блог", - "addon.block_calendarmonth.pluginname": "Тақвим", - "addon.block_calendarupcoming.pluginname": "Воқеаҳои оянда", - "addon.block_comments.pluginname": "Тафсирҳо", - "addon.block_completionstatus.pluginname": "Статуси анҷомёбии курс", - "addon.block_glossaryrandom.pluginname": "Сабти тасодуфӣ аз глоссарий", - "addon.block_myoverview.future": "Оянда", - "addon.block_myoverview.inprogress": "Дар раванд", - "addon.block_myoverview.morecourses": "Курсҳои дигар", - "addon.block_myoverview.nocourses": "Курсҳо нестанд", - "addon.block_myoverview.past": "Гузашта", - "addon.block_myoverview.pluginname": "Азназаргузаронии курс", - "addon.block_newsitems.pluginname": "Xaбарҳои охирин", - "addon.block_onlineusers.pluginname": "Корбарони онлайн дар сомона", - "addon.block_privatefiles.pluginname": "Файлҳои шахсии ман", - "addon.block_recentactivity.pluginname": "Амалҳои охирин", - "addon.block_rssclient.pluginname": "RSS-наворҳои дурдаст", - "addon.block_selfcompletion.pluginname": "Назорати мустақилонаи анҷомёбӣ", - "addon.block_sitemainmenu.pluginname": "Meнюи асосӣ", - "addon.block_tags.pluginname": "Тегҳо", - "addon.blog.blog": "Блог", - "addon.blog.blogentries": "Сабтҳои блог", - "addon.blog.linktooriginalentry": "Истинод ба сабти аслии гурӯҳ", - "addon.blog.noentriesyet": "Сабтҳои инъикосёбанда нестанд", - "addon.blog.publishtonoone": "барои худ (сиёҳнавис)", - "addon.blog.publishtosite": "барои истифодабарандагони сайт", - "addon.blog.publishtoworld": "барои тамоми ҷаҳон", - "addon.calendar.allday": "Ҳамаи рӯзҳо", - "addon.calendar.calendar": "Тақвим", - "addon.calendar.calendarevents": "Чорабиниҳои тақвимӣ", - "addon.calendar.categoryevents": "Чорабиниҳои категория", - "addon.calendar.confirmeventdelete": "Оё Шумо боварӣ доред, ки ин воқеаро нест кардан даркор аст?", - "addon.calendar.confirmeventseriesdelete": "\"{{$a.name}}\" ин як қисми силсила мебошад. Оё мехоҳед, ки ин чорабинӣ ё ҳамаи {{$a.count}} чорабиниҳо дар силсила нест карда шавад?", - "addon.calendar.courseevents": "Воқеаи курс", - "addon.calendar.daynext": "Рӯзи дигар", - "addon.calendar.dayprev": "Рӯзи қаблӣ", - "addon.calendar.deleteallevents": "Ҳама чорабиниҳо нест карда шаванд", - "addon.calendar.deleteevent": "Воқеа нест карда шавад", - "addon.calendar.deleteoneevent": "Чорабинии мазкур нест карда шавад", - "addon.calendar.durationminutes": "Давомнокӣ дар дақиқаҳо", - "addon.calendar.durationnone": "Бе давомнокӣ", - "addon.calendar.durationuntil": "То", - "addon.calendar.editevent": "Таҳрири воқеаҳо", - "addon.calendar.eventcalendareventdeleted": "Чорабинии тақвим нест карда шуд", - "addon.calendar.eventduration": "Давомнокӣ", - "addon.calendar.eventendtime": "Вақти анҷомёбӣ", - "addon.calendar.eventkind": "Навъи воқеа", - "addon.calendar.eventname": "Ном", - "addon.calendar.eventstarttime": "Вақти саршавӣ", - "addon.calendar.eventtype": "Намуди чорабинӣ", - "addon.calendar.fri": "Ҷм", - "addon.calendar.friday": "Ҷумъа", - "addon.calendar.gotoactivity": "Гузаштан ба амал", - "addon.calendar.groupevents": "Воқеаҳои гурӯҳӣ", - "addon.calendar.invalidtimedurationminutes": "Шумо нишондиҳандаи нодурусти давомнокӣ дар дақиқаҳоро ворид намудаед. Нишондиҳандаи аз 0 калонтарро зикр намоед ё нишондиҳадаро зикр накунед.", - "addon.calendar.invalidtimedurationuntil": "Маълум мешавад, ки давраи муқарраршуда барои воқеа пеш аз вақти саршавии он аст. Илтимос, инро пеш аз давом додан ислоҳ кунед.", - "addon.calendar.mon": "Дш", - "addon.calendar.monday": "Душанбе", - "addon.calendar.monthlyview": "Бознигарии моҳона", - "addon.calendar.newevent": "Воқеаи нав", - "addon.calendar.nopermissiontoupdatecalendar": "Бубахшед, аммо ҳозир Шумо ҳуқуқи навсозии воқеаи тақвимро надоред", - "addon.calendar.repeatedevents": "Воқеаҳои такроршаванда", - "addon.calendar.repeateditall": "Дигаргуниҳо барои дигар воқеаҳои такроршавандаи ({{$a}}) -и ин силсила нигоҳ дошта шаванд", - "addon.calendar.repeateditthis": "Дигаргуниҳо танҳо барои ин воқеа нигоҳ дошта шаванд", - "addon.calendar.repeatevent": "Ин воқеа такрор карда шавад", - "addon.calendar.repeatweeksl": "Ҳар ҳафта такрор карда шавад, барои ҳама бунёд карда шавад", - "addon.calendar.sat": "Шб", - "addon.calendar.saturday": "Шанбе", - "addon.calendar.siteevents": "Чорабиниҳои сомона", - "addon.calendar.sun": "Як", - "addon.calendar.sunday": "Якшанбе", - "addon.calendar.thu": "Пш", - "addon.calendar.thursday": "Панҷшанбе", - "addon.calendar.today": "Имрӯз", - "addon.calendar.tomorrow": "Пагоҳ", - "addon.calendar.tue": "Сш", - "addon.calendar.tuesday": "Сешанбе", - "addon.calendar.typecategory": "Чорабинии категория", - "addon.calendar.typeclose": "Пӯшидани чорабинӣ", - "addon.calendar.typecourse": "Воқеаҳои курс", - "addon.calendar.typedue": "Чорабинии пешина", - "addon.calendar.typegradingdue": "Баҳогузории чорабини пешина", - "addon.calendar.typegroup": "Воқеаи гурӯҳ", - "addon.calendar.typeopen": "Чорабини оғоз карда шавад", - "addon.calendar.typesite": "Воқеаи сомона", - "addon.calendar.typeuser": "Воқеаи истифодабаранда", - "addon.calendar.upcomingevents": "Воқеаҳои оянда", - "addon.calendar.userevents": "Воқеаҳои истифодабаранда", - "addon.calendar.wed": "Чш", - "addon.calendar.wednesday": "Чоршанбе", - "addon.calendar.when": "Кай", - "addon.calendar.yesterday": "Дирӯз", - "addon.competency.competencies": "Салоҳият", - "addon.competency.evidence_competencyrule": "Қоидаи салоҳият мувофиқа карда шудааст.", - "addon.competency.evidence_coursecompleted": "{$ а}} курс анҷом ёфт.", - "addon.competency.evidence_coursemodulecompleted": "Фаъолияти \"{$ a}}\" ба анҷом расид.", - "addon.competency.evidence_courserestored": "Баҳоҳо бо курс \"{$ a}}\" бо барқарор карда шуд.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Далели омӯзиши аввалдараҷа \"{$ a}}\" алоқаманд буд.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Далели омӯзиши аввалдараҷа \"{$ a}}\" алоқаманд нашуд.", - "addon.competency.evidence_manualoverride": "Баҳогузории салоҳият ба таври дасти танзим карда шуд.", - "addon.competency.evidence_manualoverrideincourse": "Баҳогузории салоҳият ба таври дасти дар курси'{{$a}}' танзим карда шуд.", - "addon.competency.evidence_manualoverrideinplan": "Баҳогузории салоҳият ба таври дасти дар нақшаи таълим'{{$a}}' танзим карда шуд.", - "addon.competency.planstatusactive": "Фаъол", - "addon.competency.planstatuscomplete": "Ба анҷомрасида", - "addon.competency.planstatusdraft": "Сиёҳнавис", - "addon.competency.planstatusinreview": "Баррасӣ", - "addon.competency.planstatuswaitingforreview": "Мунтазир барои баррасӣ", - "addon.competency.usercompetencystatus_idle": "Холӣ", - "addon.competency.usercompetencystatus_inreview": "Дар баррасӣ", - "addon.competency.usercompetencystatus_waitingforreview": "Мунтазири баррасист.", - "addon.competency.userplans": "Нақшаҳои таълим", - "addon.coursecompletion.completecourse": "Курс ба анҷом расонида шавад", - "addon.coursecompletion.completed": "Иҷро шуд", - "addon.coursecompletion.completiondate": "Рӯзи иҷроиш", - "addon.coursecompletion.completionmenuitem": "Назорати иҷрокунӣ", - "addon.coursecompletion.coursecompletion": "Хатми курс", - "addon.coursecompletion.criteria": "Меъёрҳо", - "addon.coursecompletion.criteriagroup": "Гурӯҳи меъёрҳо", - "addon.coursecompletion.criteriarequiredall": "Мувофиқат ба ҳамаи меъёрҳои дар зер зикршуда талаб карда мешавад", - "addon.coursecompletion.criteriarequiredany": "Мувофиқат ба ҳар яке аз меъёрҳои дар зер зикршуда талаб карда мешавад", - "addon.coursecompletion.inprogress": "Дар раванд", - "addon.coursecompletion.manualselfcompletion": "Истифодабаранда метавонад худаш оиди иҷрокунӣ қайд кунад", - "addon.coursecompletion.nottracked": "Дар айни замон дар ин курс аз руи иҷроиш пайгирӣ нашудает", - "addon.coursecompletion.notyetstarted": "Ҳанӯз сар нашудааст", - "addon.coursecompletion.pending": "Интизор меравад", - "addon.coursecompletion.required": "Пур кардан лозим аст", - "addon.coursecompletion.requiredcriteria": "Меъёрҳои даркорӣ", - "addon.coursecompletion.requirement": "Талабот", - "addon.coursecompletion.status": "Статус", - "addon.coursecompletion.viewcoursereport": "Ҳисобот оиди курс аз назар гузаронда шавад", - "addon.files.files": "Файлҳо", - "addon.files.privatefiles": "Файлҳои шахсӣ", - "addon.files.sitefiles": "Файлҳои сомона", - "addon.messages.addcontact": "Ҳамсӯҳбат илова карда шавад", - "addon.messages.addtoyourcontacts": "Ҳамсӯҳбатони худро илова кунед", - "addon.messages.blocknoncontacts": "Мактубчаҳо аз касоне ки дар рӯйхати ҳамсӯҳбатони ман нестанд, қабул карда нашаванд", - "addon.messages.contactblocked": "Ҳамсӯҳбат масдуд карда шудааст", - "addon.messages.contacts": "Ҳамсӯҳбатон", - "addon.messages.deleteallconfirm": "Оё шумо ин гуфтугузорро пурра тоза кардан мехоҳед?", - "addon.messages.message": "Мактубча", - "addon.messages.messagepreferences": "Хусусиятҳои паём", - "addon.messages.messages": "Мактубчаҳо", - "addon.messages.newmessage": "Паёми нав", - "addon.messages.newmessages": "Паёмҳои нав", - "addon.messages.nomessagesfound": "Мактубчаҳо ёфт нашуданд", - "addon.messages.noncontacts": "Ғайри ҳамсӯҳбат", - "addon.messages.removecontact": "Ҳамсӯҳбат аз рӯйхати ман нест карда шавад", - "addon.messages.removefromyourcontacts": "Аз ҳама ҳамсӯҳбатон тоза кардан", - "addon.messages.searchcombined": "Ҷустуҷӯи истифодабарандагон ва мактубчаҳо", - "addon.messages.type_offline": "Офлайн", - "addon.messages.type_online": "Онлайн", - "addon.messages.type_search": "Натиҷаҳои ҷустуҷӯ", - "addon.messages.you": "Шумо:", - "addon.mod_assign.addattempt": "Ба дигар кӯшиш иҷозат дода шавад", - "addon.mod_assign.addnewattempt": "Кӯшиши нав илова карда шавад", - "addon.mod_assign.addnewattemptfromprevious": "Илова кардани кӯшиши нав дар асоси пешниҳоди пешина", - "addon.mod_assign.addsubmission": "Илова кардани пешниҳод", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Тафсилоти супориш ва пешниҳодҳо аз {{$a}} дастрас аст", - "addon.mod_assign.allowsubmissionsfromdate": "Иҷрои супориш иҷозат дода шавад аз вақти", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Ин супориш пешниҳодҳоро то {{$a}} қабул мекунад", - "addon.mod_assign.applytoteam": "Баҳоҳо ва тақризҳо барои ҳаммаи гурӯҳо тасдиқ карда шаванд", - "addon.mod_assign.assignmentisdue": "Супориш вобаста аст", - "addon.mod_assign.attemptnumber": "Шумораи кӯшиш", - "addon.mod_assign.attemptreopenmethod": "Кӯшишҳо аз нав кушода шудаанд", - "addon.mod_assign.attemptreopenmethod_manual": "Дастӣ", - "addon.mod_assign.attemptreopenmethod_untilpass": "Автоматӣ пас аз гузариш", - "addon.mod_assign.attemptsettings": "Танзими кӯшиш", - "addon.mod_assign.currentgrade": "Баҳодиҳии ҷорӣ дар журнал", - "addon.mod_assign.cutoffdate": "Мӯҳлати ниҳоии қабул", - "addon.mod_assign.defaultteam": "Гурғ", - "addon.mod_assign.duedate": "Мӯҳлати охирини супурдан", - "addon.mod_assign.duedateno": "Мӯҳлати супурдан маҳдуд нашудааст", - "addon.mod_assign.duedatereached": "Мӯҳлати охирин барои иҷрои ин супориш акнун гузашт", - "addon.mod_assign.editingstatus": "Таҳрири ҳолат", - "addon.mod_assign.editsubmission": "Пешниҳод таҳрир карда шавад", - "addon.mod_assign.extensionduedate": "Тамдиди мӯҳлат", - "addon.mod_assign.grade": "Баҳодиҳӣ", - "addon.mod_assign.graded": "Баҳо дода шуд", - "addon.mod_assign.gradedby": "Баҳо дода шуд аз ҷониби", - "addon.mod_assign.gradedon": "Баҳо барои", - "addon.mod_assign.gradelocked": "Ин баҳо маҳкам ва ё сбекоршуда дар журнал ба ҳисоб меравад", - "addon.mod_assign.gradingstatus": "Ҳолати баҳодиҳӣ", - "addon.mod_assign.hiddenuser": "Иштирокчӣ", - "addon.mod_assign.markingworkflowstateinmarking": "Дар раванди баҳогузорӣ аст", - "addon.mod_assign.markingworkflowstateinreview": "Дар баррасӣ қарор дорад", - "addon.mod_assign.markingworkflowstatenotmarked": "Қайд нашудааст", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Тайер барои татбиқ", - "addon.mod_assign.modulenameplural": "Супоришҳо", - "addon.mod_assign.noattempt": "Кӯшиш нест", - "addon.mod_assign.notgraded": "Баҳогузорӣ нашудааст", - "addon.mod_assign.numberofdraftsubmissions": "Сиёҳнависҳо", - "addon.mod_assign.numberofparticipants": "Иштирокчиён", - "addon.mod_assign.numberofsubmissionsneedgrading": "Баҳогузорӣ лозим аст", - "addon.mod_assign.numberofsubmittedassignments": "Қабулушуда", - "addon.mod_assign.numberofteams": "Гурӯҳҳо", - "addon.mod_assign.numwords": "Ҳамагӣ калимаҳо - {{$a}}", - "addon.mod_assign.outof": "{{$a.current}} берун аз {{$a.total}}", - "addon.mod_assign.overdue": "Мӯҳлати супориш гузаштааст: {{$a}}", - "addon.mod_assign.submission": "Ҷавоб", - "addon.mod_assign.submissioneditable": "Донишҷӯён метавонанд ин ҷавобро таҳрир кунанд", - "addon.mod_assign.submissionnoteditable": "Донишҷӯ ин ҷавобро таҳрир карда наметавонад", - "addon.mod_assign.submissionslocked": "Супориш ҷавобҳоро қабул намекунад", - "addon.mod_assign.submissionstatus": "Ҳолати супориш", - "addon.mod_assign.submissionstatus_": "Ҷавоб ба супориш нест", - "addon.mod_assign.submissionstatus_marked": "Баҳо дода шуд", - "addon.mod_assign.submissionstatus_new": "Ҷавоб ба супориш нест", - "addon.mod_assign.submissionstatus_reopened": "Аз нав кушода шудааст", - "addon.mod_assign.submissionstatusheading": "Ҳолати супориш", - "addon.mod_assign.submitassignment": "Фиристодани супориш", - "addon.mod_assign.submittedearly": "Супориш пешакӣ супурда шудааст - {{$a}}", - "addon.mod_assign.submittedlate": "Супориш бо таъхир супурда шудааст - {{$a}}", - "addon.mod_assign.timemodified": "Охирин дигаргунӣ", - "addon.mod_assign.timeremaining": "Вақти боқимонда", - "addon.mod_assign.unlimitedattempts": "Бемаҳдуд", - "addon.mod_assign.viewsubmission": "Дидани ҷавобҳо", - "addon.mod_assign_feedback_comments.pluginname": "Тафсирҳои тақриз", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF шарҳ дода шавад", - "addon.mod_assign_feedback_file.pluginname": "Тақризи файл", - "addon.mod_assign_submission_file.pluginname": "Пешниҳодҳои файл", - "addon.mod_book.errorchapter": "Хато ҳангоми хондани боби китоб.", - "addon.mod_book.modulenameplural": "Китобҳо", - "addon.mod_book.toc": "Мундариҷа", - "addon.mod_chat.beep": "сигнал", - "addon.mod_chat.chatreport": "Сессияҳои чат", - "addon.mod_chat.currentusers": "Истифодабарандагони ҳозира", - "addon.mod_chat.enterchat": "Ба чат даромадан", - "addon.mod_chat.entermessage": "Паёми худро ворид созед", - "addon.mod_chat.messagebeepseveryone": "{{$a}} ба ҳама сигнал равон кард!", - "addon.mod_chat.messagebeepsyou": "{{$a}} ба Шумо сигнал фиристод!", - "addon.mod_chat.messageenter": "{{$a}} дар чат намоён шуд", - "addon.mod_chat.messageexit": "{{$a}} аз чат рафт", - "addon.mod_chat.messages": "Паёмҳо", - "addon.mod_chat.messageyoubeep": "Шумо сигнал фиристодед {{$a}}", - "addon.mod_chat.modulenameplural": "Чатҳо", - "addon.mod_chat.nomessages": "Ҳеҷ як мактубча нест", - "addon.mod_chat.saidto": "гуфта шуд", - "addon.mod_chat.send": "Фиристода шавад", - "addon.mod_chat.sessionstart": "Сессияи чат сар мешавад: {{$a}}", - "addon.mod_chat.talk": "Гуфтугӯ", - "addon.mod_chat.viewreport": "Дида баромадани чат-сессияҳои гузашта", - "addon.mod_choice.expired": "Бубахшед,ин фаъолият маҳкам карда шудааст {{$a}} ва акнун дастрас нест", - "addon.mod_choice.full": "(Пур карда шудааст)", - "addon.mod_choice.modulenameplural": "Пурсишҳо", - "addon.mod_choice.noresultsviewable": "Шумо ҳозир натиҷаҳои пурсишро аз назар гузаронда наметавонед.", - "addon.mod_choice.notopenyet": "Бубахшед, ин амал дастрас нест то даме ки {{$a}}", - "addon.mod_choice.removemychoice": "Интихоби ман нест карда шавад", - "addon.mod_choice.responses": "Ҷавоб", - "addon.mod_choice.savemychoice": "Ҷавоб маҳфуз дошта шавад", - "addon.mod_choice.yourselection": "Интихоби Шумо", - "addon.mod_data.advancedsearch": "Ҷустуҷӯи васеъкардашуда", - "addon.mod_data.alttext": "Матни алтернативӣ", - "addon.mod_data.approve": "Тасдиқ карда шавад", - "addon.mod_data.ascending": "Аз рӯи афзуншавӣ", - "addon.mod_data.authorfirstname": "Номи муаллиф", - "addon.mod_data.authorlastname": "Фамилияи муаллиф", - "addon.mod_data.confirmdeleterecord": "Оё боварӣ доред, ки ин сабтро нест кардан мехоҳед?", - "addon.mod_data.descending": "Аз рӯ камшавӣ", - "addon.mod_data.emptyaddform": "Шумо ҳеҷ як майдонро пур накардед", - "addon.mod_data.entrieslefttoadd": "Шумо бояд боз {{$a.entriesleft}}илова кунед, то имкон дошта бошед, ки сабтҳои иштирокчиёни дигарро бинед", - "addon.mod_data.fields": "Майдонҳо", - "addon.mod_data.foundrecords": "Сабтҳо ёфт шуданд: {{$a.num}}/{{$a.max}} (Филтрҳо ба ҳолати пештара баргардонда шаванд)", - "addon.mod_data.menuchoose": "Интихоб карда шавад...", - "addon.mod_data.modulenameplural": "Базаҳои маълумотҳо", - "addon.mod_data.more": "Дида баромадани сабт", - "addon.mod_data.nomatch": "Сабтҳои мувофиқаткунанда ёфт нашуданд!", - "addon.mod_data.norecords": "Дар базаи маълумотҳо сабтҳо нетанд", - "addon.mod_data.numrecords": "{{$a}} сабтҳо", - "addon.mod_data.recordapproved": "Сабт тасдиқ карда шудааст", - "addon.mod_data.recorddeleted": "Сабт дур карда шудааст", - "addon.mod_data.single": "Яктогӣ сабтро дида баромадан", - "addon.mod_feedback.analysis": "Таҳлил", - "addon.mod_feedback.anonymous": "Махфӣ", - "addon.mod_feedback.anonymous_entries": "Воридоти махфӣ ({{$a}})", - "addon.mod_feedback.average": "Миёна", - "addon.mod_feedback.modulenameplural": "Алоқаи баргарданда", - "addon.mod_feedback.preview": "Пешакӣ дида баромадан", - "addon.mod_feedback.questions": "Саволҳо", - "addon.mod_feedback.started": "саршавӣ", - "addon.mod_folder.modulenameplural": "Папкаҳо", - "addon.mod_forum.addanewdiscussion": "Мавзӯъ барои муҳокима илова карда шавад", - "addon.mod_forum.addanewquestion": "Саволи нав илова карда шавад", - "addon.mod_forum.addanewtopic": "Мавзӯи нав илова карда шавад", - "addon.mod_forum.advanced": "Ба танзимҳои иловагӣ дахл дорад", - "addon.mod_forum.cannotadddiscussion": "Барои дар ин форум муҳокимаҳо илова кардан иштирокчии гурӯҳ будан лозим аст.", - "addon.mod_forum.cannotadddiscussionall": "Шумо барои илова намудани мавзӯи нави мубоҳиса барои ҳамаи иштирокчиён имтиёз надоред.", - "addon.mod_forum.cannotcreatediscussion": "Муҳокимаи нав бунёд кардан ғайриимкон аст", - "addon.mod_forum.couldnotadd": "Мактубча аз боиси хатои номаълум илова карда нашудааст", - "addon.mod_forum.couldnotupdate": "Ин мактубча аз боиси хатои номаълум навсозӣ карда нашудааст", - "addon.mod_forum.delete": "Нест карда шавад", - "addon.mod_forum.deletedpost": "Мактубча нест карда шудааст", - "addon.mod_forum.deletesure": "Оё боварӣ доред, ки ин хабарро нест кардан мехоҳед?", - "addon.mod_forum.discussion": "Муҳокима", - "addon.mod_forum.edit": "Таҳрир карда шавад", - "addon.mod_forum.erroremptymessage": "Мактубча холӣ буда наметавонад", - "addon.mod_forum.erroremptysubject": "Мавзӯи мактубча холӣ буда наметавонад", - "addon.mod_forum.group": "Гурӯҳ", - "addon.mod_forum.lastpost": "Хабари охирин", - "addon.mod_forum.message": "Паём", - "addon.mod_forum.modeflatnewestfirst": "Ҳамвор, дар пеш навҳо", - "addon.mod_forum.modeflatoldestfirst": "Ҳамвор, дар пеш кӯҳнаҳо", - "addon.mod_forum.modenested": "Чӯбмонанд", - "addon.mod_forum.modulenameplural": "Форумҳо", - "addon.mod_forum.numreplies": "{{numreplies}} ҷавоб", - "addon.mod_forum.posttoforum": "Ба форум фиристода шавад", - "addon.mod_forum.re": "Re:", - "addon.mod_forum.reply": "Ҷавоб дода шавад", - "addon.mod_forum.subject": "Мавзӯъ", - "addon.mod_forum.unread": "Нахонда", - "addon.mod_forum.unreadpostsnumber": "{{$a}} мактубчаҳои нахонда", - "addon.mod_forum.yourreply": "Ҷавоби Шумо", - "addon.mod_glossary.addentry": "Сабти нав илова карда шавад", - "addon.mod_glossary.aliases": "Калима(ҳо)и калидӣ", - "addon.mod_glossary.attachment": "Замима", - "addon.mod_glossary.byalphabet": "Аз рӯи алифбо", - "addon.mod_glossary.bysearch": "Ҷустуҷӯ", - "addon.mod_glossary.casesensitive": "Ин калима ба регистр ҳассос аст", - "addon.mod_glossary.categories": "Категорияҳо", - "addon.mod_glossary.concept": "Калима", - "addon.mod_glossary.definition": "Маънидод", - "addon.mod_glossary.entryusedynalink": "Ин сабт бояд ба табри автоматӣ пайваст шавад", - "addon.mod_glossary.fillfields": "Калима ва маънидод - майдонҳои ҳатмӣ мебошанд.", - "addon.mod_glossary.fullmatch": "Мувофиқат танҳо ба калимаҳои пурра муаяйн карда шавад", - "addon.mod_glossary.modulenameplural": "Глоссарийҳо", - "addon.mod_imscp.modulenameplural": "Пакетҳои IMS мӯҳтаво", - "addon.mod_lesson.answer": "Ҷавоб", - "addon.mod_lesson.attempt": "Кӯшиш: {{$a}}", - "addon.mod_lesson.attemptsremaining": "Фақат{{$a}} кӯшишатон боқӣ мондааст", - "addon.mod_lesson.averagescore": "Балли миёна", - "addon.mod_lesson.averagetime": "Вақти миёна", - "addon.mod_lesson.branchtable": "Варақча-рубрикатор (қисм)", - "addon.mod_lesson.clusterjump": "Саволи дида баромаданашуда дар кластер", - "addon.mod_lesson.completed": "Анҷом ёфт", - "addon.mod_lesson.congratulations": "Табрик мекунем - лексия анҷом ёфт", - "addon.mod_lesson.continue": "Давом дода шавад", - "addon.mod_lesson.defaultessayresponse": "Матни Шуморо муаллими курс баҳо хоҳад дод.", - "addon.mod_lesson.detailedstats": "Статистикаи муфассал", - "addon.mod_lesson.didnotanswerquestion": "Донишҷӯ ба ин савол ҷавоб надод.", - "addon.mod_lesson.displayofgrade": "Баҳоҳо нишон дода шаванд (фақат барои донишҷӯён)", - "addon.mod_lesson.displayscorewithessays": "Шумо {{$a.score}} балл аз {{$a.tempmaxgrade}} барои саволҳои автоматӣ баҳододашуда ба даст оваред.
                      Баҳоҳо барои {{$a.essayquestions}} эссе ҳангоми гузоштани баҳои натиҷавӣ дертар гузошта шуда, ба назар гирифта мешаванд.
                      Баҳои ҳозираи Шумо (бе назардошти эссе): {{$a.score}} аз {{$a.grade}}.", - "addon.mod_lesson.displayscorewithoutessays": "Баҳо барои ҷавобҳо ба саволҳо: {{$a.score}} аз {{$a.grade}}", - "addon.mod_lesson.emptypassword": "Калимаи раҳкушо холӣ буда наметавонад", - "addon.mod_lesson.enterpassword": "Илтимос, калимаи раҳкушоро ворид намоед:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Шумо ба ҳеҷ як савол ҷавоб надодед. Шумо барои ин лексия 0 гирифтед.", - "addon.mod_lesson.firstwrong": "Мутаассифона, аз боиси ҷавоби нодуруст Шумо наметавонед балл гиред. Оё мехоҳед бо мақсади омӯзиш (аммо на бо мақсади балл гирифтан) ёфтани ҷавобҳоро давом диҳед?", - "addon.mod_lesson.grade": "Баҳо", - "addon.mod_lesson.highscore": "Баландтарин натиҷа", - "addon.mod_lesson.hightime": "Вақти бештарин", - "addon.mod_lesson.leftduringtimed": "Шумо лексияро бо вақти барои гузаштани он муқарраршуда тарк кардед.
                      Илтимос, тугмаи \"Давом дода шавад\"-ро click кунед, то ки лексияро яз нав сар кунед.", - "addon.mod_lesson.leftduringtimednoretake": "Шумо лексияро бо вақти барои гузаштани он муқарраршуда тарк кардед ва онро аз нав гузаштан ё давом додан наметавонед.", - "addon.mod_lesson.lessonmenu": "Менюи лексия", - "addon.mod_lesson.lessonstats": "Статистикаи лексия", - "addon.mod_lesson.linkedmedia": "Медиаи пайвастшуда", - "addon.mod_lesson.loginfail": "Логин нодуруст аст, илтимос, бори дигар кӯшиш карда бинед...", - "addon.mod_lesson.lowscore": "Балли пасттарин", - "addon.mod_lesson.lowtime": "Вақти камтарин", - "addon.mod_lesson.maximumnumberofattemptsreached": "Шумо ба миқдори минималии кӯшишҳо расидед - ба саҳифаи навбатӣ мегузарем", - "addon.mod_lesson.modattemptsnoteacher": "Фақат донишҷӯён метавонанд ҷавобҳои худро тағйир диҳанд.", - "addon.mod_lesson.modulenameplural": "Лексияҳо", - "addon.mod_lesson.noanswer": "Ҷавоб ба даст наомадааст", - "addon.mod_lesson.nolessonattempts": "Кӯшишҳои аз лексия гузаштан набуданд.", - "addon.mod_lesson.notcompleted": "Ба анҷом расонда нашудааст", - "addon.mod_lesson.numberofcorrectanswers": "Миқдори ҷавобҳои дуруст: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Миқдори саҳифаҳои дида баромадашуда : {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Миқдори саволҳое ки ба онҳо ҷавоб дода шудааст: {{$a.nquestions}}; (Шумо бояд камаш ба ин миқдор саволҳо ҷавоб диҳед: {{$a.minquestions}})", - "addon.mod_lesson.ongoingnormal": "Шумо ба {{$a.correct}} савол аз {{$a.viewed}} саволи дида баромадашуда дуруст ҷавоб додед.", - "addon.mod_lesson.or": "Ё", - "addon.mod_lesson.overview": "Бознигарӣ", - "addon.mod_lesson.preview": "Дида баромадан", - "addon.mod_lesson.question": "Савол", - "addon.mod_lesson.reports": "Ҳисоботҳо", - "addon.mod_lesson.response": "Тафсир ба ҷавоб", - "addon.mod_lesson.review": "Аз нав дида баромадан", - "addon.mod_lesson.reviewlesson": "Ҷавобҳо дар дарс тағйир дода шаванд", - "addon.mod_lesson.reviewquestionback": "Ҳа, ман мехоҳам бори дигар кӯшиш карда бинам", - "addon.mod_lesson.reviewquestioncontinue": "Не, ман фақат мехоҳам ба саволи навбатӣ гузарам", - "addon.mod_lesson.secondpluswrong": "На он қадар дуруст. Оё мехоҳед бори дигар кӯшиш карда бинед?", - "addon.mod_lesson.submit": "Фиристода шавад", - "addon.mod_lesson.teacherjumpwarning": "Дар лексия гузаштанҳои \"{{$a.cluster}}\" ё \"{{$a.unseen}}\" истифода мешаванд. Ба ҷои онҳо гузаштани \"саҳифаи оянда\" истифода хоҳад шуд. Таҳти сабти баҳисобгирии донишҷӯ ворид шавед, то ки ин гузаштанҳоро санҷед.", - "addon.mod_lesson.teacherongoingwarning": "Балли ҳозира фақат барои донишҷӯ инъикос меёбад. Таҳти номи ягон донишҷӯ ворид шавед, то ки балли ҳозираро санҷед.", - "addon.mod_lesson.teachertimerwarning": "Таймер фақат барои донишҷӯ инъикос меёбад. Барои санҷидани таймер таҳти номи донишҷӯ ворид шавед.", - "addon.mod_lesson.thatsthecorrectanswer": "Ин ҷавоби дуруст аст", - "addon.mod_lesson.thatsthewronganswer": "Ин ҷавоби нодуруст аст", - "addon.mod_lesson.timeremaining": "Вақти боқимонда", - "addon.mod_lesson.timetaken": "Вақти сарфшуда", - "addon.mod_lesson.unseenpageinbranch": "Саволи дида баромадаНАшуда аз қисм", - "addon.mod_lesson.welldone": "Аъло!", - "addon.mod_lesson.youhaveseen": "Шумо аллакай бо ин лексия кор кардаед.
                      Оё мехоҳед аз он ҷой сар кунед, ки дар он таваққуф карда будед?", - "addon.mod_lesson.youranswer": "Ҷавоби Шумо", - "addon.mod_lesson.yourcurrentgradeisoutof": "Баҳои ҳозираи Шумо: {{$a.grade}} из {{$a.total}}", - "addon.mod_lesson.youshouldview": "Шумо бояд дида бароед ақаллан: {{$a}}", - "addon.mod_page.modulenameplural": "Саҳифаҳо", - "addon.mod_quiz.attemptfirst": "Кӯшиши якум", - "addon.mod_quiz.attemptlast": "Кӯшиши охирин", - "addon.mod_quiz.attemptnumber": "Кӯшиш", - "addon.mod_quiz.attemptquiznow": "Тест кардан сар карда шавад", - "addon.mod_quiz.attemptstate": "Ҳолат", - "addon.mod_quiz.comment": "Тафсир", - "addon.mod_quiz.completedon": "Анҷом ёфт", - "addon.mod_quiz.confirmclose": "Пас аз фиристодан Шумо дигар ҷавобҳои худро дар ин кӯшиш иваз карда наметавонед.", - "addon.mod_quiz.confirmstart": "Тест маҳдудияти вақӣ {{$a}} дорад. Вақт аз лаҳзаи оғоз намудани кӯшиши шумо ҳисоб карда мешавад ва шумо бояд пеш аз он ба итмом расонед. Оё боварӣ доред, ки мехоҳед ҳоло оғоз кунед?", - "addon.mod_quiz.confirmstartheader": "Тести бо вақт маҳдудшуда", - "addon.mod_quiz.connectionerror": "Пайвастшавӣ ба шабака қатъ гардид.(Сабти автоматӣ нашуд.)\n\nҶавобҳе,ки дар саҳифаи зерин дар давоми дақиқаҳои охир ворид намудед, дар хотир дошта бори дигар пайваст шавед.\n\nПас аз боз пайваст шудан , ҷавобҳои шумо сабт шуда , ин паём гум мешавад.", - "addon.mod_quiz.continueattemptquiz": "Кӯшиши охирин давом дода шавад", - "addon.mod_quiz.continuepreview": "Дида баромадани охирин давом дода шавад", - "addon.mod_quiz.feedback": "Тақриз", - "addon.mod_quiz.finishattemptdots": "Кӯшиши... ба охир расонда шавад", - "addon.mod_quiz.grade": "Баҳо", - "addon.mod_quiz.gradeaverage": "Баҳои миёна", - "addon.mod_quiz.gradehighest": "Баҳои миёна", - "addon.mod_quiz.grademethod": "Усули баҳодиҳӣ", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Баллҳо", - "addon.mod_quiz.modulenameplural": "Тестҳо", - "addon.mod_quiz.mustbesubmittedby": "Ин кӯшиш бояд то {{$a}} фиристода шавад.", - "addon.mod_quiz.noquestions": "Ҳанӯз ҳеҷ як савол не илова карда нашудааст", - "addon.mod_quiz.noreviewattempt": "Ба Шумо дида баромадани ин кӯшиш иҷозат дода нашудааст.", - "addon.mod_quiz.notyetgraded": "Ҳанӯз баҳо дода нашудааст", - "addon.mod_quiz.outof": "{{$a.grade}} аз максимум {{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}} аз максимум {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Тақризи натиҷавӣ", - "addon.mod_quiz.overdue": "Мӯҳлат ба охир расид", - "addon.mod_quiz.overduemustbesubmittedby": "Мӯҳлати ин кӯшиш аллакай гузаштааст. Агар Шумо хоҳед, ки ин тест баҳо дода шавад, бояд онро {{$a}} фиристед. Агар Шумо кӯшишро нафиристед, он баҳо дода намешавад.", - "addon.mod_quiz.preview": "Дида баромадан", - "addon.mod_quiz.previewquiznow": "Дида баромадани тест сар карда шавад", - "addon.mod_quiz.question": "Савол", - "addon.mod_quiz.quiznavigation": "Раҳёбӣ (навигация) аз рӯи тест", - "addon.mod_quiz.quizpassword": "Калимаи раҳкушо барои тест", - "addon.mod_quiz.reattemptquiz": "Аз нав гузаштани тест", - "addon.mod_quiz.requirepasswordmessage": "Барои аз тест гузаштан шумо бояд калимаи раҳкушои онро донед", - "addon.mod_quiz.returnattempt": "Бозгаштан ба кӯшиш", - "addon.mod_quiz.review": "Дида баромадан", - "addon.mod_quiz.reviewofattempt": "Дида баромадани кӯшишҳои {{$a}}", - "addon.mod_quiz.reviewofpreview": "Бознигарии натиҷаҳои пешакӣ дида баромадан", - "addon.mod_quiz.showall": "Ҳамаи саволҳо дар як саҳифа инъикос карда шаванд", - "addon.mod_quiz.showeachpage": "Як саҳифа нишон дода шавад", - "addon.mod_quiz.startattempt": "Кӯшиш сар карда шавад", - "addon.mod_quiz.startedon": "Тест сар карда шуд", - "addon.mod_quiz.stateabandoned": "Фиристоданашудаҳо", - "addon.mod_quiz.statefinished": "Анҷом ёфт", - "addon.mod_quiz.statefinisheddetails": "Фиристода шуд{{$a}}", - "addon.mod_quiz.stateinprogress": "Дар раванд", - "addon.mod_quiz.stateoverdue": "Мӯҳлаташон гузашта", - "addon.mod_quiz.stateoverduedetails": "Бояд {{$a}} пешкаш карда шаванд", - "addon.mod_quiz.status": "Ҳолат", - "addon.mod_quiz.submitallandfinish": "Ҳамааш фиристода шавад ва тест анҷом дода шавад", - "addon.mod_quiz.summaryofattempt": "Натиҷаи кӯшиш", - "addon.mod_quiz.summaryofattempts": "Натиҷаҳои кӯшишҳои пештараи Шумо", - "addon.mod_quiz.timeleft": "Вақти боқимонда", - "addon.mod_quiz.timetaken": "Вақт гузашт", - "addon.mod_quiz.yourfinalgradeis": "Баҳои ҷамъбастии Шумо барои ин тест: {{$a}}", - "addon.mod_resource.modulenameplural": "Файлҳо", - "addon.mod_scorm.attempts": "Кӯшишҳо", - "addon.mod_scorm.averageattempt": "Миёнаи кӯшишҳо", - "addon.mod_scorm.browse": "Пешакӣ дида баромадан", - "addon.mod_scorm.browsed": "Ёфт шуд", - "addon.mod_scorm.browsemode": "Реҷаи пешакӣ дида баромадан", - "addon.mod_scorm.completed": "Анҷом ёфт", - "addon.mod_scorm.contents": "Мундариҷа", - "addon.mod_scorm.enter": "Воридшавӣ", - "addon.mod_scorm.failed": "Нобарор", - "addon.mod_scorm.firstattempt": "Кӯшиши якум", - "addon.mod_scorm.gradeaverage": "Баҳои миёна", - "addon.mod_scorm.gradehighest": "Баҳои баландтарин", - "addon.mod_scorm.grademethod": "Усули баҳодиҳӣ", - "addon.mod_scorm.gradescoes": "Объектҳои таълимӣ", - "addon.mod_scorm.gradesum": "Баҳои ҷамъулҷамъ", - "addon.mod_scorm.highestattempt": "Беҳтарин кӯшиш", - "addon.mod_scorm.incomplete": "Анҷом наёфтааст", - "addon.mod_scorm.lastattempt": "Кӯшиши охирин", - "addon.mod_scorm.modulenameplural": "Пакетҳои SCORM", - "addon.mod_scorm.newattempt": "Кӯшиши нав сар карда шавад", - "addon.mod_scorm.noattemptsallowed": "Миқдори кӯшишҳо", - "addon.mod_scorm.noattemptsmade": "Кӯшишҳои иҷрошуда", - "addon.mod_scorm.notattempted": "Сар накардааст", - "addon.mod_scorm.passed": "Бо муқаффақият иҷро шудааст", - "addon.mod_scorm.reviewmode": "Реҷаи дида баромадани натиҷаҳо", - "addon.mod_scorm.score": "Балл", - "addon.mod_scorm.suspended": "Боздошта шудааст", - "addon.mod_survey.ifoundthat": "Ман фаҳмидам, ки", - "addon.mod_survey.ipreferthat": "Ман афзал медонам", - "addon.mod_survey.modulenameplural": "Анкетаҳо", - "addon.mod_survey.responses": "Ҷавобҳо", - "addon.mod_survey.results": "Натиҷаҳо", - "addon.mod_url.modulenameplural": "Гиперистинод", - "addon.mod_wiki.createpage": "Саҳифа бунёд карда шавад", - "addon.mod_wiki.editingpage": "Тафсир кардани саҳифаи \"{{$a}}\"", - "addon.mod_wiki.map": "Харита", - "addon.mod_wiki.modulenameplural": "Wiki саҳифаҳо", - "addon.mod_wiki.newpagetitle": "Сарлавҳаи саҳифаи нав", - "addon.mod_wiki.nocontent": "Ин саҳифа мӯҳтаво надорад", - "addon.mod_wiki.notingroup": "Берун аз гурӯҳ", - "addon.mod_wiki.pageexists": "Чунин саҳифа аллакай вуҷуд дорад. Равона кардан ба он.", - "addon.mod_wiki.wrongversionlock": "Дигар истифодабаранда саҳифаро таҳрир кард ва мӯҳтавои Шумо кӯҳна шудааст", - "addon.mod_workshop.alreadygraded": "Аллакай баҳо дода шудааст", - "addon.mod_workshop.areainstructauthors": "Дастурҳо барои кор", - "addon.mod_workshop.areainstructreviewers": "Дастурҳо оиди баҳо", - "addon.mod_workshop.assess": "Баҳо дода шавад", - "addon.mod_workshop.assessedsubmission": "Кори баҳододашуда", - "addon.mod_workshop.assessmentform": "Шакли баҳоҳо", - "addon.mod_workshop.assessmentsettings": "Параметрҳои баҳо", - "addon.mod_workshop.assessmentweight": "Вазни баҳо", - "addon.mod_workshop.assignedassessments": "Корҳое ки барои баҳодиҳӣ пешкаш шудаанд", - "addon.mod_workshop.assignedassessmentsnone": "Корҳои барои баҳодиҳӣ пешкашшуда нестанд", - "addon.mod_workshop.createsubmission": "Start preparing your submission", - "addon.mod_workshop.editsubmission": "Кор таҳрир карда шавад", - "addon.mod_workshop.feedbackauthor": "Тақриз барои муаллиф", - "addon.mod_workshop.feedbackby": "Тақриз ба{{$a}}", - "addon.mod_workshop.feedbackreviewer": "Тақриз барои баҳодиҳанда", - "addon.mod_workshop.givengrades": "Баҳоҳои додашуда", - "addon.mod_workshop.gradecalculated": "Баҳои ҳисобкардашуда барои кор", - "addon.mod_workshop.gradeinfo": "Баҳо: {{$a.received}} аз {{$a.max}}", - "addon.mod_workshop.gradeover": "Баҳо барои кор аз нав муайян карда шавад", - "addon.mod_workshop.gradesreport": "Ҳисобот оиди баҳоҳои семинар", - "addon.mod_workshop.gradinggrade": "Баҳо барои баҳодиҳӣ", - "addon.mod_workshop.gradinggradecalculated": "Баҳои ҳисобкардашуда барои баҳодиҳӣ", - "addon.mod_workshop.gradinggradeof": "Баллҳо барои баҳодиҳӣ (аз {{$a}})", - "addon.mod_workshop.gradinggradeover": "Баллҳо барои баҳодиҳӣ аз нав муайян карда шаванд", - "addon.mod_workshop.modulenameplural": "Семинарҳо", - "addon.mod_workshop.nogradeyet": "Боз баҳо дода нашудааст", - "addon.mod_workshop.notassessed": "Ҳанӯз баҳо доданашуда", - "addon.mod_workshop.notoverridden": "Аз нав муайян карда нашудааст", - "addon.mod_workshop.noyoursubmission": "Шумо ҳанӯз кори худро равона накардаед", - "addon.mod_workshop.publishedsubmissions": "Кори интишоршуда", - "addon.mod_workshop.publishsubmission": "Интишори кор", - "addon.mod_workshop.publishsubmission_help": "Корҳои интишоршуда ба дигарон пас аз анҷомёбии семинар дастрасанд.", - "addon.mod_workshop.reassess": "Аз нав баҳо додан", - "addon.mod_workshop.receivedgrades": "Баҳоҳои ба даст омада", - "addon.mod_workshop.submissionattachment": "Замима", - "addon.mod_workshop.submissioncontent": "Мӯҳтавои кор", - "addon.mod_workshop.submissiongrade": "Баҳо барои кор", - "addon.mod_workshop.submissiongradeof": "Баҳо барои кор (аз {{$a}})", - "addon.mod_workshop.submissiontitle": "Ном", - "addon.mod_workshop.userplan": "Нақшаи семинар", - "addon.mod_workshop.weightinfo": "Вазн: {{$a}}", - "addon.mod_workshop.yourassessment": "Худбаҳодиҳӣ", - "addon.mod_workshop.yoursubmission": "Кори шумо", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Меъёри {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Меъёри {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Изҳороти {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Меъёри {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Шумо бояд яке аз ин унсурҳоро интихоб намоед", - "addon.notes.addnewnote": "Ёддошт илова карда шавад", - "addon.notes.coursenotes": "Ёддоштҳои курс", - "addon.notes.deleteconfirm": "Оё ин ёддошт нест карда шавад?", - "addon.notes.nonotes": "Ҳеҷ як сабти ин навъ нест.", - "addon.notes.note": "Ёддошт", - "addon.notes.notes": "Ёддоштҳо", - "addon.notes.personalnotes": "Ёдоштҳои шахсӣ", - "addon.notes.publishstate": "Контекст", - "addon.notes.sitenotes": "Ёддоштҳои сомона", - "addon.notifications.markallread": "Хондашуда қайд карда шавад", - "addon.notifications.notificationpreferences": "Хусусияти огоҳӣ", - "assets.countries.AD": "Андорра", - "assets.countries.AE": "Aморатҳои Муттаҳидаи Араб", - "assets.countries.AF": "Афғонистон", - "assets.countries.AG": "Антигуа ва Барбуда", - "assets.countries.AI": "Ангиля", - "assets.countries.AL": "Албания", - "assets.countries.AM": "Арманистон", - "assets.countries.AO": "Ангола", - "assets.countries.AQ": "Антарктида", - "assets.countries.AR": "Аргентина", - "assets.countries.AS": "Самоаи Амрикоӣ", - "assets.countries.AT": "Австрия", - "assets.countries.AU": "Австралия", - "assets.countries.AW": "Аруба", - "assets.countries.AX": "Ҷазираҳои Аланд", - "assets.countries.AZ": "Озарбойҷон", - "assets.countries.BA": "Босния ва Ҳертсоговина", - "assets.countries.BB": "Барбадос", - "assets.countries.BD": "Бангладеш", - "assets.countries.BE": "Белгия", - "assets.countries.BF": "Буркина-Фасо", - "assets.countries.BG": "Булғория", - "assets.countries.BH": "Баҳрайн", - "assets.countries.BI": "Бурунди", - "assets.countries.BJ": "Бенин", - "assets.countries.BL": "Сен-Бартелми", - "assets.countries.BM": "Бермуда", - "assets.countries.BN": "Бруней-Доруссалом", - "assets.countries.BO": "Боливия", - "assets.countries.BR": "Бразилия", - "assets.countries.BS": "Багама", - "assets.countries.BT": "Бутан", - "assets.countries.BV": "Ҷазираи Буве", - "assets.countries.BW": "Ботсвана", - "assets.countries.BY": "Беларус", - "assets.countries.BZ": "Белиз", - "assets.countries.CA": "Канада", - "assets.countries.CC": "Ҷазираҳои Норҷил (Килинг)", - "assets.countries.CD": "Конго, Ҷумҳурии демократӣ", - "assets.countries.CF": "Ҷумҳурии Африқои Марказӣ", - "assets.countries.CG": "Конго", - "assets.countries.CH": "Швейтсария", - "assets.countries.CI": "Кот д'Ивуар", - "assets.countries.CK": "Ҷазираҳои Кук", - "assets.countries.CL": "Чили", - "assets.countries.CM": "Камерун", - "assets.countries.CN": "Хитой", - "assets.countries.CO": "Колумбия", - "assets.countries.CR": "Коста-Рика", - "assets.countries.CU": "Куба", - "assets.countries.CV": "Кабо-Верде", - "assets.countries.CX": "Ҷазираи Мавлуд", - "assets.countries.CY": "Кипр", - "assets.countries.CZ": "Ҷумҳурии Чехия", - "assets.countries.DE": "Германия", - "assets.countries.DJ": "Ҷибути", - "assets.countries.DK": "Дания", - "assets.countries.DM": "Доминика", - "assets.countries.DO": "Ҷумҳурии Доминика", - "assets.countries.DZ": "Алҷазоир", - "assets.countries.EC": "Эквадор", - "assets.countries.EE": "Эстония", - "assets.countries.EG": "Миср", - "assets.countries.EH": "Саҳрои Ғарбӣ", - "assets.countries.ER": "Эритрея", - "assets.countries.ES": "Испания", - "assets.countries.ET": "Ҳабашистон", - "assets.countries.FI": "Финландия", - "assets.countries.FJ": "Фиҷи", - "assets.countries.FK": "Ҷазираҳои Фолкленд (Малвин)", - "assets.countries.FM": "Микронезия, иёлатҳои федеративӣ", - "assets.countries.FO": "Ҷазираҳои Фарер", - "assets.countries.FR": "Фарансо", - "assets.countries.GA": "Габон", - "assets.countries.GB": "Британияи Кабир", - "assets.countries.GD": "Гренада", - "assets.countries.GE": "Гурҷистон", - "assets.countries.GF": "Гвианаи франсавӣ", - "assets.countries.GG": "Гернси", - "assets.countries.GH": "Гана", - "assets.countries.GI": "Гибралтар", - "assets.countries.GL": "Гренландия", - "assets.countries.GM": "Гамбия", - "assets.countries.GN": "Гвинея", - "assets.countries.GP": "Гваделупа", - "assets.countries.GQ": "Гвинеяи Экваторӣ", - "assets.countries.GR": "Юнон", - "assets.countries.GS": "Ҷорҷияи ҷанубӣ ба Ҷазираҳои Сандвич", - "assets.countries.GT": "Гватемала", - "assets.countries.GU": "Гуам", - "assets.countries.GW": "Гвинея-Биссау", - "assets.countries.GY": "Гайана", - "assets.countries.HK": "Ҳонконг", - "assets.countries.HM": "Ҷазираи Ҳерд ва ҷазираҳои Макдоналд", - "assets.countries.HN": "Ҳондурас", - "assets.countries.HR": "Хорватия", - "assets.countries.HT": "Гаити", - "assets.countries.HU": "Венгрия", - "assets.countries.ID": "Индонезия", - "assets.countries.IE": "Ирландия", - "assets.countries.IL": "Исроил", - "assets.countries.IM": "Ҷазираи Мэн", - "assets.countries.IN": "Ҳиндустон", - "assets.countries.IO": "Ҳудуди британӣ дар уқёнуси Ҳинд", - "assets.countries.IQ": "Ирoқ", - "assets.countries.IR": "Эрон, ҷумҳурии исломӣ", - "assets.countries.IS": "Исландия", - "assets.countries.IT": "Италия", - "assets.countries.JE": "Ҷерси", - "assets.countries.JM": "Ямайка", - "assets.countries.JO": "Урдун", - "assets.countries.JP": "Япония", - "assets.countries.KE": "Кения", - "assets.countries.KG": "Қирғизистон", - "assets.countries.KH": "Камбоҷа", - "assets.countries.KI": "Кирибати", - "assets.countries.KM": "Комор", - "assets.countries.KN": "Сен-Китс ва Невис", - "assets.countries.KP": "Корея, Ҷумҳурии Халқӣ-Демократӣ", - "assets.countries.KR": "Корея, ҷумҳурӣ", - "assets.countries.KW": "Кувайт", - "assets.countries.KY": "Ҷазираҳои Кайман", - "assets.countries.KZ": "Қазоқистон", - "assets.countries.LA": "Ҷумҳурии Халқӣ-Демократии Лаос", - "assets.countries.LB": "Лубнон", - "assets.countries.LC": "Сент-Люсия", - "assets.countries.LI": "Лихтенштейн", - "assets.countries.LK": "Шри-Ланка", - "assets.countries.LR": "Либерия", - "assets.countries.LS": "Лесото", - "assets.countries.LT": "Литва", - "assets.countries.LU": "Люксембург", - "assets.countries.LV": "Латвия", - "assets.countries.LY": "Ҷамоҳирияи Арабии Либия", - "assets.countries.MA": "Марокаш", - "assets.countries.MC": "Монако", - "assets.countries.MD": "Молдова, ҷумҳурӣ", - "assets.countries.ME": "Черногория", - "assets.countries.MF": "Сент-Мартин", - "assets.countries.MG": "Мадагаскар", - "assets.countries.MH": "Ҷазираҳои Маршалл", - "assets.countries.MK": "Македония, собиқ дар зимни Ҷумҳурии Югославия", - "assets.countries.ML": "Мали", - "assets.countries.MM": "Мянма", - "assets.countries.MN": "Муғулистон", - "assets.countries.MO": "Макао", - "assets.countries.MP": "Ҷазираҳои шимолии Мариан", - "assets.countries.MQ": "Мартиника", - "assets.countries.MR": "Мавритания", - "assets.countries.MS": "Монтсеррат", - "assets.countries.MT": "Малта", - "assets.countries.MU": "Маврикий", - "assets.countries.MV": "Малдив", - "assets.countries.MW": "Малави", - "assets.countries.MX": "Мексика", - "assets.countries.MY": "Малайзия", - "assets.countries.MZ": "Мозамбик", - "assets.countries.NA": "Намибия", - "assets.countries.NC": "Каледонияи Нав", - "assets.countries.NE": "Нигер", - "assets.countries.NF": "Ҷазираи Норфолк", - "assets.countries.NG": "Нигерия", - "assets.countries.NI": "Никарагуа", - "assets.countries.NL": "Нидерланд", - "assets.countries.NO": "Норвегия", - "assets.countries.NP": "Непал", - "assets.countries.NR": "Науру", - "assets.countries.NU": "Ниуэ", - "assets.countries.NZ": "Зеландияи Нав", - "assets.countries.OM": "Уммон", - "assets.countries.PA": "Панама", - "assets.countries.PE": "Перу", - "assets.countries.PF": "Полинезияи франсавӣ", - "assets.countries.PG": "Папуа — Гвинеяи Нав", - "assets.countries.PH": "Филиппин", - "assets.countries.PK": "Покистон", - "assets.countries.PL": "Полша", - "assets.countries.PM": "Сент-Пьер ва Микелон", - "assets.countries.PN": "Питкерн", - "assets.countries.PR": "Пуэрто-Рико", - "assets.countries.PS": "Ҳудуди Фаластин, ишғолшуда", - "assets.countries.PT": "Португалия", - "assets.countries.PW": "Палау", - "assets.countries.PY": "Парагвай", - "assets.countries.QA": "Қатар", - "assets.countries.RE": "Реюнон", - "assets.countries.RO": "Руминия", - "assets.countries.RS": "Сербия", - "assets.countries.RU": "Русия", - "assets.countries.RW": "Руанда", - "assets.countries.SA": "Арабистони Саудӣ", - "assets.countries.SB": "Ҷазираҳои Сулаймон", - "assets.countries.SC": "Сейшел", - "assets.countries.SD": "Судон", - "assets.countries.SE": "Шведсия", - "assets.countries.SG": "Сингапур", - "assets.countries.SH": "Еленаи Муқаддас", - "assets.countries.SI": "Словения", - "assets.countries.SJ": "Шпитсберген ва Ян Майен", - "assets.countries.SK": "Словакия", - "assets.countries.SL": "Сиерра-Леоне", - "assets.countries.SM": "Сан-Марино", - "assets.countries.SN": "Сенегал", - "assets.countries.SO": "Сомали", - "assets.countries.SR": "Суринам", - "assets.countries.ST": "Сан-Томе ва Принсипи", - "assets.countries.SV": "Салвадор", - "assets.countries.SY": "Ҷумҳурии Арабии Сурия", - "assets.countries.SZ": "Свазиленд", - "assets.countries.TC": "Ҷазираҳои Теркс ва Кайкос", - "assets.countries.TD": "Чад", - "assets.countries.TF": "Ҳудудҳои ҶАнубии Франсавӣ", - "assets.countries.TG": "Того", - "assets.countries.TH": "Таиланд", - "assets.countries.TJ": "Тоҷикистон", - "assets.countries.TK": "Токелау", - "assets.countries.TL": "Тимори Шарқӣ", - "assets.countries.TM": "Туркманистон", - "assets.countries.TN": "Тунис", - "assets.countries.TO": "Тонга", - "assets.countries.TR": "Туркия", - "assets.countries.TT": "Тринидад ва Тобаго", - "assets.countries.TV": "Тувалу", - "assets.countries.TW": "Тайван (Китай)", - "assets.countries.TZ": "Танзания, ҷумҳурии муттаҳида", - "assets.countries.UA": "Украина", - "assets.countries.UG": "Уганда", - "assets.countries.UM": "Ҷазираҳои Хурди Дури Иёлатҳои Муттаҳида", - "assets.countries.US": "Иёлатҳои Муттаҳидаи Амрико", - "assets.countries.UY": "Уругвай", - "assets.countries.UZ": "Ӯзбекистон", - "assets.countries.VA": "Ватикан (Тахти Папа)", - "assets.countries.VC": "Сент-Винсент ва Гренадин", - "assets.countries.VE": "Венесуэла", - "assets.countries.VG": "Ҷазираҳои Виргин, Британӣ", - "assets.countries.VI": "Ҷазираҳои Виргин, ИМА", - "assets.countries.VN": "Виетнам", - "assets.countries.VU": "Вануату", - "assets.countries.WF": "Уоллис ва Футуна", - "assets.countries.WS": "Самоа", - "assets.countries.YE": "Яман", - "assets.countries.YT": "Майотта", - "assets.countries.ZA": "Африқои Ҷанубӣ", - "assets.countries.ZM": "Замбия", - "assets.countries.ZW": "Зимбабве", - "assets.mimetypes.application/msword": "ҳуҷҷати Word", - "assets.mimetypes.application/pdf": "Ҳуҷҷати PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Нусхабардории Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Ҷадвали электронии Excel", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 бо макроси фаъолшуда", - "assets.mimetypes.application/vnd.ms-powerpoint": "Презентатсияи PowerPoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Таблисаи OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Қолаби таблисаи OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "Документи матнии OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "Қолаби", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "Қолаби Саҳифаи Web дар OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Презентатсияи Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Слайдшоу Powerpoint 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Таблисаи Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Қолаби Excel 2007", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Документи Word 2007", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "Презентатсияи iWork Keynote", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "Таблисаи iWork Numbers", - "assets.mimetypes.application/x-iwork-pages-sffpages": "Документи iWork Pages", - "assets.mimetypes.application/x-javascript": "JavaScript source", - "assets.mimetypes.application/x-mspublisher": "Документи Publisher", - "assets.mimetypes.application/x-shockwave-flash": "Аниматсияи Flash", - "assets.mimetypes.application/xhtml_xml": "Документи XHTML", - "assets.mimetypes.archive": "Архив ({{$a.EXT}})", - "assets.mimetypes.audio": "Файли аудиоӣ ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Файл", - "assets.mimetypes.group:archive": "Файлҳои архив", - "assets.mimetypes.group:audio": "Файлҳои аудиоӣ", - "assets.mimetypes.group:document": "Файлҳои ҳуҷҷатӣ", - "assets.mimetypes.group:html_audio": "Аудио файлҳо дар ҳама браузерҳо мақбул ҳастанд", - "assets.mimetypes.group:html_track": "HTML файлҳои пайгирӣ", - "assets.mimetypes.group:html_video": "Аудио файлҳо дар ҳама браузерҳо мақбул ҳастанд", - "assets.mimetypes.group:image": "Файлҳои сурат", - "assets.mimetypes.group:presentation": "Файлҳои презентатсия", - "assets.mimetypes.group:sourcecode": "Коди додашуда", - "assets.mimetypes.group:spreadsheet": "Табитсаҳо", - "assets.mimetypes.group:video": "Видеофайлҳо", - "assets.mimetypes.group:web_audio": "Аудифайлҳои дар Веб истифодашаванда", - "assets.mimetypes.group:web_file": "Веб файлҳо", - "assets.mimetypes.text/plain": "Файли матнӣ", - "assets.mimetypes.text/rtf": "Ҳуҷҷати RTF", - "core.accounts": "Сабтҳои қайдкунӣ", - "core.add": "Илова карда шавад", - "core.agelocationverification": "Тафтиши сол ва мавқеъ", - "core.ago": "{{$a}} ба ақиб", - "core.all": "Ҳама", - "core.allgroups": "Ҳамаи гурӯҳҳо", - "core.allparticipants": "Ҳамаи иштирокчиён", - "core.answer": "Ҷавоб", - "core.answered": "Ҷавоб дода шудааст", - "core.areyousure": "Мутмаин ҳастед?", - "core.back": "Ба ақиб", - "core.block.blocks": "Блокҳо", - "core.cancel": "Бекор кардан", - "core.category": "Категория", - "core.choose": "Интихоб карда шавад", - "core.choosedots": "интихоб намоед...", - "core.clicktohideshow": "Зер кунед, то ки ошкор намоед ё пинҳон кунед", - "core.close": "Пӯшонда шавад", - "core.comments": "Тафсирҳо", - "core.comments.addcomment": "Тафсир илова карда шавад...", - "core.comments.comments": "Тафсирҳо", - "core.comments.commentscount": "Тафсирҳои ({{$a}})", - "core.comments.deletecommentbyon": "Тафсилоти фиристодани {{$a.user}} дар {{$a.time}}", - "core.comments.eventcommentcreated": "Шарҳ сохта шуд", - "core.comments.eventcommentdeleted": "Шарҳ нест карда шуд", - "core.comments.nocomments": "Тафсирҳо нестанд", - "core.comments.savecomment": "Тафсир маҳфуз дошта шавад", - "core.commentscount": "Тафсирҳои ({{$a}})", - "core.completion-alt-auto-fail": "Иҷро шуд: {{$a}} (баҳо аз балли гузариш пасттар аст)", - "core.completion-alt-auto-n": "Иҷро нашуд: {{$a}}", - "core.completion-alt-auto-pass": "Иҷро шуд: {{$a}} (баҳо аз балли гузариш болотар аст)", - "core.completion-alt-auto-y": "Иҷро шуд: {{$a}}", - "core.completion-alt-auto-y-override": "Ба анҷом расида: {{$a.modname}} (set by {{$a.overrideuser}})", - "core.completion-alt-manual-n": "Иҷро нашуд: {{$a}}. Интихоб намоед, то ки унсурро ҳамчун иҷрошуда қайд кунед", - "core.completion-alt-manual-n-override": "Иҷро нашудааст. {$ A-> modname}} нестам (танзим кардаед {$ a-> overrideuser}}). Ҳамчун тамоман ба қайд гиред.", - "core.completion-alt-manual-y": "Иҷро шуд: {{$a}}. Интихоб намоед, то ки унсури курсро ҳамчун иҷронашуда қайд кунед", - "core.completion-alt-manual-y-override": "Иҷро шуд: {{a-> modname}}} {{a-> overrideuser}} -ро танзим мекунад. Нишонае, ки ба анҷом нарасидааст, интихоб кунед", - "core.confirmdeletefile": "Шумо боварӣ доред, ки ин файлро нест кардан мехоҳед?", - "core.considereddigitalminor": "Шумо ноболиғи рақамӣ шуморида мешавед.", - "core.content": "Мӯҳтаво", - "core.continue": "Давом дода шавад", - "core.course": "Курс", - "core.course.allsections": "Ҳамаи қисматҳо", - "core.course.contents": "Мундариҷа", - "core.course.coursesummary": "Тавсифи курс", - "core.course.hiddenfromstudents": "Аз донишҷӯён пинҳон карда шудааст", - "core.course.hiddenoncoursepage": "Дастрас аст , вале дар ин саҳифа нишон дода нашудааст", - "core.course.overriddennotice": "Баҳои натиҷавии Шумо барои ин унсури курс дастӣ ислоҳ карда шудааст.", - "core.course.sections": "Қисмҳо", - "core.coursedetails": "Маълумоҳои курс", - "core.courses.allowguests": "Курс ба меҳмон дастрас аст", - "core.courses.availablecourses": "Курсҳои дастрас", - "core.courses.categories": "Категорияҳои курсҳо", - "core.courses.courses": "Курсҳо", - "core.courses.frontpage": "Саҳифаи асосӣ", - "core.courses.ignore": "Сарфи назар карда шавад", - "core.courses.mycourses": "Курсҳои ман", - "core.courses.mymoodle": "Moodle -и ман", - "core.courses.nocourses": "Барои инъикос кардан иттилоот нест.", - "core.courses.nocoursesyet": "Дар ин категория курсҳо нестанд", - "core.courses.nosearchresults": "Натиҷаҳо нестанд", - "core.courses.notenroled": "Шумо ба ин курс номнависӣ карда нашудаед", - "core.courses.paymentrequired": "Барои бақайдгирӣ дар ин курс пул пардохт намудан лозим аст", - "core.courses.reload": "Навсозӣ карда шавад", - "core.courses.search": "Ёфта шавад", - "core.courses.searchcourses": "Ҷустуҷӯи курс", - "core.date": "Таърихи рӯз", - "core.day": "рӯз", - "core.days": "дн.", - "core.decsep": ",", - "core.delete": "Нест карда шавад", - "core.description": "Тавсиф", - "core.digitalminor": "Рақамӣ", - "core.digitalminor_desc": "Барои сохтани аакунт дар ин саҳифа, лутфан ба шахси масъул муроҷиат намоед.", - "core.done": "Анҷом дода шудааст", - "core.download": "Гирифта шавад", - "core.edit": "Таҳрир карда шавад", - "core.error": "Хато", - "core.filename": "Номи файл", - "core.filenotfound": "Бубахшед, файл ёфт нашуд.", - "core.fileuploader.addfiletext": "Файл илова кардан", - "core.fileuploader.audio": "Садо", - "core.fileuploader.camera": "Камера", - "core.fileuploader.file": "Файл", - "core.fileuploader.more": "Дида баромадани сабт", - "core.fileuploader.video": "Видео", - "core.filter": "Филтр", - "core.folder": "Папка", - "core.forcepasswordchangenotice": "Шумо бояд калимаи раҳкушои худро иваз намоед.", - "core.fulllistofcourses": "Ҳамаи курсҳо", - "core.grades.average": "Баҳои миёна", - "core.grades.badgrade": "Баҳои нодуруст нишон дода шудааст", - "core.grades.contributiontocoursetotal": "Нисбат ба маҷмӯъи курс", - "core.grades.feedback": "Тақриз", - "core.grades.grade": "Баҳодиҳӣ", - "core.grades.gradeitem": "Унсури баҳодиҳӣ", - "core.grades.grades": "Баҳоҳо", - "core.grades.lettergrade": "Баҳои ҳарфӣ", - "core.grades.nogradesreturned": "Баҳоҳо нестанд", - "core.grades.nooutcome": "Нишондиҳанда нест", - "core.grades.percentage": "Фоизҳо", - "core.grades.range": "Диапазон", - "core.grades.rank": "Место", - "core.grades.weight": "вазн", - "core.group": "гурӯҳ", - "core.groupsseparate": "Гурӯҳҳои ҷудогона", - "core.groupsvisible": "Гурӯҳҳои намоён", - "core.help": "Маълумотнома", - "core.hide": "Пинҳон карда шавад", - "core.hour": "с.", - "core.hours": "с.", - "core.info": "Иттилоот", - "core.invalidformdata": "Шакли нодурусти маълумотҳо", - "core.labelsep": ":", - "core.lastaccess": "Воридшавии охирин", - "core.lastmodified": "Тағйироти охирин", - "core.layoutgrid": "Шабака", - "core.list": "Рӯйхат", - "core.listsep": ";", - "core.loading": "Бор шудаистодааст", - "core.location": "Ҷойгиркунӣ", - "core.login.auth_email": "Бақайдгирии мустақилона бо почтаи электронӣ", - "core.login.authenticating": "Санҷиши ҳаққоният", - "core.login.cancel": "Бекор кардан", - "core.login.changepassword": "Калимаи раҳкушо тағйир дода шавад", - "core.login.connect": "Пайваст шавед!", - "core.login.connecttomoodle": "Ба Moodle пайваст шавед", - "core.login.createaccount": "Маҳфуз дошта шавад", - "core.login.createuserandpass": "Номи истифодабаранда ва калимаи раҳкушоро интихоб намоед", - "core.login.emailconfirmsent": "Ба адреси почтаи электронии ({{$a}}) зикркардаи Шумо нома бо дастурҳои оддӣ барои анҷом додани бақайдгирӣ фиристода шудааст. Агар дар мавриди бақайдгирӣ мушкилот ба миён оянд, бо администратори сомона робита намоед.", - "core.login.emailnotmatch": "Нишониҳои почтаи электронӣ мувофиқат намекунанд.", - "core.login.firsttime": "Шумо бори аввал дар сомонаи мо ҳастед?", - "core.login.forcepasswordchangenotice": "Шумо бояд калимаи раҳкушои худро иваз намоед.", - "core.login.forgotten": "Номи корбар ё ниҳонвожаро фаромӯш кардед?", - "core.login.help": "Маълумотнома", - "core.login.instructions": "Дастурҳо", - "core.login.invaliddate": "Таърихи нодурусти рӯз", - "core.login.invalidemail": "Формати нодурусти адреси почтаи электронӣ", - "core.login.invalidtime": "Вақти нодуруст", - "core.login.login": "Ворид шудан", - "core.login.loginbutton": "Ворид шудан", - "core.login.loginsteps": "Барои пайдо кардани дастрасии пурра ба ин сомона, шумо бояд сабти бақайдгириро эҷод намоед.", - "core.login.missingemail": "Майдонро пур кунед", - "core.login.missingfirstname": "Майдонро пур кунед", - "core.login.missinglastname": "Майдонро пур кунед", - "core.login.mustconfirm": "Сабти баҳисобгириро тасдиқ кунед", - "core.login.newaccount": "Сабти баҳисобгирии нав", - "core.login.notloggedin": "Шумо бояд ворид шавед.", - "core.login.password": "Ниҳонвожа", - "core.login.passwordforgotten": "Барқароркунии калимаи раҳкушои фаромӯшшуда", - "core.login.passwordforgotteninstructions2": "Барои партофтани калимаи раҳкушо дар зер логини худро ё адреси почтаи электронии худро зикр намоед. Агар Сабти баҳисобгирии Шумо дар базаи маълумотҳо бошад, ба адреси почтаи электронии Шумо номае фиристода мешавад, ки дастурҳоро оиди барқароркунии дастрасӣ дар бар мегирад.", - "core.login.passwordrequired": "Ниҳонвожа ҳатмӣ аст", - "core.login.policyaccept": "Ман фаҳмидам ва розӣ ҳастам", - "core.login.policyagree": "Барои он ки корро бо ин сомона давом диҳед, Шумо бояд аҳдномаи истифодабарандагиро қабул намоед. Оё розӣ ҳастед?", - "core.login.policyagreement": "Аҳдномаи истифодабарандагӣ", - "core.login.policyagreementclick": "Истинод ба аҳдномаи истифодабарандагӣ", - "core.login.potentialidps": "Дохил шудан бо истифодаи сабти баҳисобгирӣ:", - "core.login.profileinvaliddata": "Нишондиҳандаи нодуруст", - "core.login.searchby": "Ҷустуҷӯ аз рӯи:", - "core.login.security_question": "Савол барои амният", - "core.login.selectacountry": "Кишварро интихоб намоед", - "core.login.selectsite": "Лутфан, сомонаи худро интихоб намоед:", - "core.login.signupplugindisabled": "{{$a}} ғайрифаъол аст.", - "core.login.siteaddress": "Нишонии сомона", - "core.login.siteurl": "Нишонии сомона", - "core.login.startsignup": "Сабти баҳисобгирӣ бунёд карда шавад", - "core.login.supplyinfo": "Иттилоотро оиди худ пур кунед", - "core.login.username": "Номи корбар", - "core.login.usernameoremail": "Логин ё адреси почтаи электронӣ ворид намоед", - "core.login.usernotaddederror": "Истифодабарандаи \"{{$a}}\" илова карда нашудааст - хатои номаълум", - "core.mainmenu.changesite": "Ивази сомона", - "core.mainmenu.help": "Маълумотнома", - "core.mainmenu.logout": "Баромадан", - "core.mainmenu.website": "Сомона", - "core.maxsizeandattachments": "Андозаи максималии файлҳои нав: {{$a.size}}, миқдори максималии файлҳои вобастакардашуда: {{$a.attachments}}", - "core.min": "Дақ.", - "core.mins": "Дақ.", - "core.misc": "Ҳар чиз", - "core.mod_assign": "Супориш", - "core.mod_assignment": "Супориши (2.2)", - "core.mod_book": "Китоб", - "core.mod_chat": "Чат", - "core.mod_choice": "Пурсиш", - "core.mod_data": "Базаи маълумотҳо", - "core.mod_database": "Базаи маълумотҳо", - "core.mod_feedback": "Алоқаи баргарданда", - "core.mod_file": "Файл", - "core.mod_folder": "Папка", - "core.mod_forum": "Форум", - "core.mod_glossary": "Глоссарий", - "core.mod_ims": "Пакети IMS мӯҳтаво", - "core.mod_imscp": "Пакети IMS мӯҳтаво", - "core.mod_label": "Тавзеҳ", - "core.mod_lesson": "Лексия", - "core.mod_page": "Саҳифа", - "core.mod_quiz": "Тест", - "core.mod_resource": "Файл", - "core.mod_scorm": "Пакети SCORM", - "core.mod_survey": "Анкета", - "core.mod_url": "Гиперистинод", - "core.mod_wiki": "Вики", - "core.mod_workshop": "Семинар", - "core.moduleintro": "Тавсиф", - "core.more": "Муфассалтар", - "core.name": "Ном", - "core.never": "Ҳеҷ гоҳ", - "core.next": "Давомаш", - "core.no": "Не", - "core.nocomments": "Тафсирҳо нестанд", - "core.nograde": "Бе баҳоҳо", - "core.none": "Холӣ", - "core.nopermissions": "Бубахшед, аммо Шумо ба ин кор ҳақ надоред ({{$a}})", - "core.noresults": "Натиҷаҳо нестанд", - "core.noselection": "Интихоб нест", - "core.notapplicable": "вуҷуд надорад", - "core.notenrolledprofile": "Ин профиль дастрас нест, чунки истифодабаранда ба ин курс номнавис нашудааст.", - "core.notice": "Огоҳӣ", - "core.notingroup": "Бубахшед, аммо Шумо бояд иштирокчии гурӯҳ бошед, то ки ин унсурро бинед.", - "core.now": "ҳозир", - "core.numwords": "Ҳамагӣ калимаҳо - {{$a}}", - "core.offline": "Берун аз сомона", - "core.ok": "OK", - "core.online": "Дар сомона", - "core.pagea": "Саҳифаи {{$a}}", - "core.paymentinstant": "Тугмаро истифода баред, то ки пулро пардохт намоед ва дар давоми якчанд дақиқа ба қайд гирифта шавед!", - "core.phone": "Телефон", - "core.pictureof": "Тасвири истифодабарандаи {{$a}}", - "core.previous": "Ба ақиб", - "core.question.answer": "Ҷавоб", - "core.question.answersaved": "Ҷавоб маҳфуз дошта шудааст", - "core.question.complete": "Иҷро шуд", - "core.question.correct": "Дуруст", - "core.question.feedback": "тақриз", - "core.question.incorrect": "Нодуруст", - "core.question.information": "Иттилоот", - "core.question.invalidanswer": "Ҷавоби нопурра", - "core.question.notanswered": "Ҷавоб нест", - "core.question.notyetanswered": "Ҳанӯз ҷавоб нест", - "core.question.partiallycorrect": "Қисман дуруст", - "core.question.questionno": "Саволи {{$a}}", - "core.question.requiresgrading": "Баҳодиҳӣ талаб карда мешавад", - "core.rating.aggregateavg": "Баҳои миёна", - "core.rating.aggregatecount": "Миқдори баҳоҳо", - "core.rating.aggregatemax": "Баҳои максималӣ", - "core.rating.aggregatemin": "Баҳои минималӣ", - "core.rating.aggregatesum": "Ҷамъулҷамъи баҳоҳо", - "core.rating.noratings": "Баҳоҳо гузошта нашудаанд", - "core.rating.rating": "Баҳо", - "core.rating.ratings": "Баҳоҳо", - "core.refresh": "Навсозӣ карда шавад", - "core.remove": "Нест карда шавад", - "core.required": "Пур кардан лозим аст", - "core.resourcedisplayopen": "Кушодан", - "core.resources": "Захираҳо", - "core.restore": "Барқарор карда шавад", - "core.restricted": "Маҳдуд карда шудааст", - "core.save": "Сабт намудан", - "core.savechanges": "Нигоҳ дошта шавад", - "core.search": "Ёфта шавад", - "core.searching": "Дар ҳоли ҷустуҷӯ", - "core.searchresults": "Натиҷаҳои ҷустуҷӯ", - "core.sec": "сония", - "core.secs": "сония", - "core.seemoredetail": "Ҷузъиёт…", - "core.selectacategory": "Лутфан категорияро интихоб намоед", - "core.selectacourse": "Курсро интихоб намоед", - "core.selectagroup": "Гурӯҳро интихоб намоед", - "core.send": "Фиристода шавад", - "core.sending": "Фиристодан", - "core.serverconnection": "Хатои пайвастшавӣ бо сервер", - "core.settings.about": "Дар бораи барнома", - "core.settings.currentlanguage": "Забони интихобшуда", - "core.settings.debugdisplay": "мактубчаҳои дурусткунӣ инъикос карда шаванд", - "core.settings.deviceinfo": "Дар бораи дастгоҳ", - "core.settings.deviceos": "Низоми омили дастгоҳ", - "core.settings.disableall": "Огоҳиҳо муваққатан хомӯш карда шаванд", - "core.settings.general": "Асосӣ", - "core.settings.language": "Забон", - "core.settings.license": "Иҷозатнома", - "core.settings.locked": "Масдуд карда шудааст", - "core.settings.loggedin": "Дар сомона", - "core.settings.loggedoff": "Берун аз сомона", - "core.settings.preferences": "Танзимҳо", - "core.settings.privacypolicy": "Сиёсати маҳфият", - "core.settings.settings": "Танзимҳо", - "core.settings.sites": "Сoмонаҳо", - "core.settings.spaceusage": "Истифодабарии фазо", - "core.settings.syncsettings": "Танзимоти ҳамоҳангсозӣ", - "core.settings.total": "Натиҷа", - "core.settings.wificonnection": "Пайвасти Wi-Fi", - "core.sharedfiles.rename": "Ивази ном", - "core.show": "Нишон дода шавад", - "core.showless": "Мухтасар нишон деҳ....", - "core.showmore": "Муфассал нишон деҳ....", - "core.site": "Сомона", - "core.sitehome.sitehome": "Саҳифаи хонагӣ", - "core.sitehome.sitenews": "Навигариҳои сомона", - "core.sitemaintenance": "Бубахшед, сомона дар реҷаи хизматрасонии техникӣ буда, ҳозир дастрас нест", - "core.sizeb": "байт", - "core.sizegb": "Гбайт", - "core.sizekb": "Кбайт", - "core.sizemb": "Мбайт", - "core.sort": "Тартиб кардан", - "core.sortby": "Ба навъҳо ҷудо карда шавад аз рӯи", - "core.strftimedate": "%d %B %Y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedaydate": "%A %d %B %Y", - "core.strftimedaydatetime": "%A %d %B %Y, %H:%M", - "core.strftimedayshort": "%A %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b %H:%M", - "core.strftimerecentfull": "%a %d %b %Y, %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "Равона карда шавад", - "core.success": "Бо муваффақият", - "core.tag.noresultsfor": "Ҳеҷ як теги дорои \"{{$a}}\" ёфт нашудааст", - "core.tag.searchtags": "Ҷустуҷӯи тегҳо", - "core.tag.tag": "Тег", - "core.tag.tags": "Тегҳо", - "core.teachers": "Муаллимон", - "core.thisdirection": "ltr", - "core.time": "Вақт", - "core.timesup": "Вақт тамом шуд!", - "core.today": "Имрӯз", - "core.tryagain": "Аз нав кӯшиш кардан", - "core.unknown": "Номаълум", - "core.unlimited": "Номаҳдуд", - "core.upgraderunning": "Сомона навсозӣ шуда истодааст, кӯшишро дертар такрор кунед.", - "core.user": "Истифодабаранда", - "core.user.address": "Адрес", - "core.user.city": "Шаҳр", - "core.user.contact": "Барои тамос", - "core.user.country": "Кишвар", - "core.user.description": "Тавсиф", - "core.user.details": "Тафсилот", - "core.user.editingteacher": "Муаллим", - "core.user.email": "Адреси почтаи электронӣ", - "core.user.emailagain": "Адреси почтаи электронӣ (бори дигар)", - "core.user.firstname": "Ном", - "core.user.interests": "Шавқмандиҳо", - "core.user.lastname": "Фамилия", - "core.user.manager": "Идоракунанда", - "core.user.newpicture": "Тасвири нав", - "core.user.noparticipants": "Барои ин курс иштирокчиён ёфт нашуданд.", - "core.user.participants": "Иштирокчиён", - "core.user.phone1": "Телефон", - "core.user.phone2": "Телефони мобилӣ", - "core.user.roles": "Нақшҳо", - "core.user.sendemail": "Почтаи электронӣ", - "core.user.student": "Донишҷӯ", - "core.user.teacher": "Ассистент (бе ҳуқуқи таҳрир)", - "core.user.webpage": "Веб-саҳифа", - "core.userdeleted": "Сабти баҳисобгирии истифодабаранда нест карда шудааст", - "core.userdetails": "Иттилооти муфассал оиди истифодабаранда", - "core.users": "Истифодабарандаҳо", - "core.view": "Дида баромадан", - "core.viewprofile": "Дида баромадани профил", - "core.year": "сол", - "core.years": "солҳо", - "core.yes": "Ҳа" -} \ No newline at end of file diff --git a/src/assets/lang/tr.json b/src/assets/lang/tr.json deleted file mode 100644 index 414f55c86..000000000 --- a/src/assets/lang/tr.json +++ /dev/null @@ -1,1649 +0,0 @@ -{ - "addon.badges.alignment": "Hizalanma", - "addon.badges.badgedetails": "Nişan ayrıntıları", - "addon.badges.badges": "Nişanlar", - "addon.badges.bendorsement": "Onay", - "addon.badges.claimcomment": "Onay yorumu", - "addon.badges.claimid": "URL talep et", - "addon.badges.contact": "İletişim", - "addon.badges.dateawarded": "Verilen tarih", - "addon.badges.expired": "Süresi dolmuş", - "addon.badges.expirydate": "Bitiş Tarihi", - "addon.badges.imageauthoremail": "Resim yazarının e-postası", - "addon.badges.imageauthorname": "Resim yazarının adı", - "addon.badges.imageauthorurl": "Resim yazarının URL'si", - "addon.badges.imagecaption": "Resim yazısı", - "addon.badges.issuancedetails": "Rozet sona erme", - "addon.badges.issuerdetails": "çıkaran ayrıntıları", - "addon.badges.issueremail": "E-posta", - "addon.badges.issuername": "Çıkaranın adı", - "addon.badges.issuerurl": "Çıkaran URL", - "addon.badges.language": "Dil", - "addon.badges.noalignment": "Bu rozetin belirlenmiş herhangi bir dış yeteneği veya standardı yoktur.", - "addon.badges.nobadges": "Uygun nişan bulunmuyor.", - "addon.badges.norelated": "Bu rozetin ilgili rozetleri yok.", - "addon.badges.recipientdetails": "Alıcı bilgileri", - "addon.badges.relatedbages": "İlgili rozetler", - "addon.badges.version": "Versiyon", - "addon.badges.warnexpired": "(Bu nişan zaman aşımına uğramış!)", - "addon.block_activitymodules.pluginname": "Etkinlikler", - "addon.block_activityresults.pluginname": "Etkinlik sonuçları", - "addon.block_badges.pluginname": "Son rozetlerim", - "addon.block_blogmenu.pluginname": "Blog Menüsü", - "addon.block_blogrecent.pluginname": "Yakınlarda güncellenen bloglar", - "addon.block_blogtags.pluginname": "Blog Etiketleri", - "addon.block_calendarmonth.pluginname": "Takvim", - "addon.block_calendarupcoming.pluginname": "Yaklaşan etkinlikler", - "addon.block_comments.pluginname": "Yorumlar", - "addon.block_completionstatus.pluginname": "Ders tamamlama durumu", - "addon.block_glossaryrandom.pluginname": "Rastgele Sözcük", - "addon.block_learningplans.pluginname": "Öğrenme planları", - "addon.block_myoverview.all": "Tümü (görünümden kaldırılanlar hariç)", - "addon.block_myoverview.allincludinghidden": "Tümü", - "addon.block_myoverview.favourites": "Yıldızlı", - "addon.block_myoverview.future": "Gelecek", - "addon.block_myoverview.hiddencourses": "Görünümden kaldırıldı", - "addon.block_myoverview.inprogress": "Devam eden", - "addon.block_myoverview.lastaccessed": "Son erişim", - "addon.block_myoverview.morecourses": "Daha fazla ders", - "addon.block_myoverview.nocourses": "Ders yok", - "addon.block_myoverview.past": "Geçmiş", - "addon.block_myoverview.pluginname": "Derslere genel bakış", - "addon.block_myoverview.shortname": "Kısa adı", - "addon.block_myoverview.title": "Ders adı", - "addon.block_newsitems.pluginname": "Son duyurular", - "addon.block_onlineusers.pluginname": "Çevrimiçi Kullanıcılar", - "addon.block_privatefiles.pluginname": "Kişisel dosyalar", - "addon.block_recentactivity.pluginname": "Son etkinlikler", - "addon.block_recentlyaccessedcourses.nocourses": "Yeni ders yok", - "addon.block_recentlyaccessedcourses.pluginname": "Erişilen son dersler", - "addon.block_recentlyaccesseditems.noitems": "Son ürün yok", - "addon.block_recentlyaccesseditems.pluginname": "Son erişilen öğeler", - "addon.block_rssclient.pluginname": "RSS İstemcisi", - "addon.block_selfcompletion.pluginname": "Kendi kendine tamamlama", - "addon.block_sitemainmenu.pluginname": "Ana menü", - "addon.block_starredcourses.nocourses": "Yıldızlı ders yok", - "addon.block_starredcourses.pluginname": "Yıldızlı dersler", - "addon.block_tags.pluginname": "Etiketler", - "addon.block_timeline.duedate": "Bitiş tarihi", - "addon.block_timeline.next30days": "Gelecek 30 gün", - "addon.block_timeline.next3months": "Gelecek 3 ay", - "addon.block_timeline.next6months": "Gelecek 6 ay", - "addon.block_timeline.next7days": "Gelecek 7 gün", - "addon.block_timeline.nocoursesinprogress": "Devam eden ders yok", - "addon.block_timeline.noevents": "Yaklaşan etkinlik yok", - "addon.block_timeline.overdue": "Süresi dolmuş", - "addon.block_timeline.pluginname": "Zaman çizelgesi", - "addon.block_timeline.sortbycourses": "Derse göre sırala", - "addon.block_timeline.sortbydates": "Tarihe göre sırala", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Blog girdileri", - "addon.blog.linktooriginalentry": "Orijinal blog girdisine bağlantı", - "addon.blog.noentriesyet": "Henüz gösterilecek girdi yok", - "addon.blog.publishtonoone": "Kendinize (taslak)", - "addon.blog.publishtosite": "Bu sitedeki herkese", - "addon.blog.publishtoworld": "Dünyadaki herkese", - "addon.blog.siteblogheading": "Site blog'u", - "addon.calendar.allday": "Tüm gün", - "addon.calendar.calendar": "Takvim", - "addon.calendar.categoryevents": "Kategori etkinlikleri", - "addon.calendar.confirmeventdelete": "\"{{$a}}\" etkinliğini silmek istediğinizden emin misiniz?", - "addon.calendar.confirmeventseriesdelete": "\"{{$a.name}}\" etkinliği tekrarlı etkinliklerin bir parçasıdır. Sadece bu etkinliği mi yoksa serideki tüm {{$a.count}} etkinliği mi silmek istersiniz?", - "addon.calendar.courseevents": "Ders etkinlikleri", - "addon.calendar.daynext": "Sonraki gün", - "addon.calendar.dayprev": "Önceki gün", - "addon.calendar.deleteallevents": "Tüm etkinlikleri sil", - "addon.calendar.deleteevent": "Etkinliği sil", - "addon.calendar.deleteoneevent": "Bu etkinliği sil", - "addon.calendar.durationminutes": "Dakika olarak süre", - "addon.calendar.durationnone": "Süresiz", - "addon.calendar.durationuntil": "Bitiş", - "addon.calendar.editevent": "Etkinlik düzenleniyor", - "addon.calendar.eventcalendareventdeleted": "Takvim etkinliği silindi", - "addon.calendar.eventduration": "Süre", - "addon.calendar.eventendtime": "Bitiş süresi", - "addon.calendar.eventkind": "Etkinlik türü", - "addon.calendar.eventname": "Etkinlik adı", - "addon.calendar.eventstarttime": "Başlangıç zamanı", - "addon.calendar.eventtype": "Etkinlik türü", - "addon.calendar.fri": "Cum", - "addon.calendar.friday": "Cuma", - "addon.calendar.gotoactivity": "Etkinliğe git", - "addon.calendar.groupevents": "Grup olayları", - "addon.calendar.invalidtimedurationminutes": "Dakika olarak girdiğiniz süre geçerli değil. Lütfen, süreyi 0'dan büyük girin veya süre seçmeyin.", - "addon.calendar.invalidtimedurationuntil": "Süre için seçtiğiniz gün ve zaman, olayın başlangıç tarihinden önce olamaz. Devam etmeden önce lütfen süreyi düzeltin.", - "addon.calendar.mon": "Pzt", - "addon.calendar.monday": "Pazartesi", - "addon.calendar.monthlyview": "Aylık Görünüm", - "addon.calendar.newevent": "Yeni olay", - "addon.calendar.noevents": "Hiç bildiriminiz yok", - "addon.calendar.nopermissiontoupdatecalendar": "Üzgünüz, ama şu anda takvim etkinliğini güncellemek için izniniz yok", - "addon.calendar.repeatedevents": "Tekrar eden olaylar", - "addon.calendar.repeateditall": "Bu tekrarlanan serideki değişiklikleri, tüm {{$a}} olaylarına uygula", - "addon.calendar.repeateditthis": "Değişiklikleri sadece bu olaya uygula", - "addon.calendar.repeatevent": "Bu olayı tekrarla", - "addon.calendar.repeatweeksl": "Haftalık tekrarla, toplam gerçekleşme sayısı", - "addon.calendar.sat": "Cmt", - "addon.calendar.saturday": "Cumartesi", - "addon.calendar.siteevents": "Site olayları", - "addon.calendar.sun": "Paz", - "addon.calendar.sunday": "Pazar", - "addon.calendar.thu": "Prş", - "addon.calendar.thursday": "Perşembe", - "addon.calendar.today": "Bugün", - "addon.calendar.tomorrow": "Yarın", - "addon.calendar.tue": "Sal", - "addon.calendar.tuesday": "Salı", - "addon.calendar.typecategory": "Kategori etkinliği", - "addon.calendar.typeclose": "Etkinliği kapat", - "addon.calendar.typecourse": "Ders olayı", - "addon.calendar.typedue": "Son tarihli etkinlik", - "addon.calendar.typegradingdue": "Son tarihli etkinliği puanla", - "addon.calendar.typegroup": "Grup olayı", - "addon.calendar.typeopen": "Etkinliği aç", - "addon.calendar.typesite": "Site olayı", - "addon.calendar.typeuser": "Kullanıcı olayı", - "addon.calendar.upcomingevents": "Yaklaşan olaylar", - "addon.calendar.userevents": "Kullanıcı olayları", - "addon.calendar.wed": "Çrş", - "addon.calendar.wednesday": "Çarşamba", - "addon.calendar.when": "Ne zaman", - "addon.calendar.yesterday": "Dün", - "addon.competency.activities": "Etkinlikler", - "addon.competency.competencies": "Yetkinlikler", - "addon.competency.competenciesmostoftennotproficientincourse": "Bu dersi alan en çok yeterli olmayan yetkinlikler", - "addon.competency.coursecompetencies": "Ders yetkinlikleri", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Bu dersin yetkinlik dereceleri öğrenme planlarını etkilemez.", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Bu dersin yetkinlik dereceleri öğrenme planlarında anında güncellenir.", - "addon.competency.crossreferencedcompetencies": "Çapraz referanslı yetkinlikler", - "addon.competency.duedate": "Bitiş tarihi", - "addon.competency.errornocompetenciesfound": "Yeterlilik bulunamdı", - "addon.competency.evidence": "Öğrenme kanıtı", - "addon.competency.evidence_competencyrule": "Yetkinlik kuralı karşılandı.", - "addon.competency.evidence_coursecompleted": "'{{$a}}' dersi tamamlandı.", - "addon.competency.evidence_coursemodulecompleted": "'{{$a}}' etkinliği tamamlandı.", - "addon.competency.evidence_courserestored": "Değerlendirme, '{{$a}}' dersiyle birlikte geri yüklendi.", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Önceki öğrenim '{{$a}}' ile ilgili kanıt bağlantılıydı.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "'{{$a}}' adlı önceden öğrenilenlerin kanıtları birbirine bağlı değildi.", - "addon.competency.evidence_manualoverride": "Yetkinlik derecesi elle ayarlandı.", - "addon.competency.evidence_manualoverrideincourse": "Yetkinlik derecesi '{{$a}}' dersinde elle ayarlandı.", - "addon.competency.evidence_manualoverrideinplan": "Yetkinlik derecesi '{{$a}}' öğrenme planında elle ayarlandı.", - "addon.competency.learningplancompetencies": "Öğrenme planı yetkinlikleri", - "addon.competency.learningplans": "Öğrenme planları", - "addon.competency.myplans": "Benim öğrenme planlarım", - "addon.competency.noactivities": "Etkinlikler yok", - "addon.competency.nocompetenciesincourse": "Bu derse hiçbir yetkinlik bağlantı kurmamıştır.", - "addon.competency.nocrossreferencedcompetencies": "Bu yetkinliğe çapraz referanslı başka yetkinlik bulunmamaktadır.", - "addon.competency.noevidence": "Öğrenme kanıtı yok", - "addon.competency.noplanswerecreated": "Hiçbir öğrenme planı oluşturulmadı.", - "addon.competency.nouserplanswithcompetency": "Hiçbir öğrenme planı bu yetkinliği içermez.", - "addon.competency.path": "Yol:", - "addon.competency.planstatusactive": "Aktif", - "addon.competency.planstatuscomplete": "Tamamla", - "addon.competency.planstatusdraft": "Taslak", - "addon.competency.planstatusinreview": "İncelemede", - "addon.competency.planstatuswaitingforreview": "İnceleme bekleniyor", - "addon.competency.proficient": "Yeterli", - "addon.competency.progress": "İlerleme", - "addon.competency.rating": "Derecelendirme", - "addon.competency.reviewstatus": "İnceleme durumu", - "addon.competency.status": "Durumlar", - "addon.competency.template": "Öğrenme planı şablonu", - "addon.competency.uponcoursecompletion": "Ders tamamlandığında:", - "addon.competency.usercompetencystatus_idle": "Kullanılmayan", - "addon.competency.usercompetencystatus_inreview": "İncelemede", - "addon.competency.usercompetencystatus_waitingforreview": "İnceleme bekleniyor", - "addon.competency.userplans": "Öğrenme planları", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.y}} yetkinliğinden {{$a.x}} dışarıda yeterli", - "addon.competency.xcompetenciesproficientoutofyincourse": "Bu derste {{$a.y}} yetkinliğin dışında {{$a.x}} yeterliliğe sahibisiniz.", - "addon.coursecompletion.complete": "Tamamla", - "addon.coursecompletion.completecourse": "Dersi tamamla", - "addon.coursecompletion.completed": "Tamamlandı", - "addon.coursecompletion.completiondate": "Tamamlama raporu", - "addon.coursecompletion.completionmenuitem": "Tamamlama", - "addon.coursecompletion.coursecompletion": "Kurs tamamlama", - "addon.coursecompletion.criteria": "Ölçüt", - "addon.coursecompletion.criteriagroup": "Ölçüt Grubu", - "addon.coursecompletion.criteriarequiredall": "Aşağıdaki ölçütlerin tümü gereklidir", - "addon.coursecompletion.criteriarequiredany": "Aşağıdaki herhangi bir kriter gereklidir", - "addon.coursecompletion.inprogress": "Devam ediyor", - "addon.coursecompletion.manualselfcompletion": "Kendi kendine elle tamamlama", - "addon.coursecompletion.nottracked": "Bu kursta henüz tamamlamış olarak kaydedilmediniz", - "addon.coursecompletion.notyetstarted": "Henüz başlamadı", - "addon.coursecompletion.pending": "Bekliyor", - "addon.coursecompletion.required": "Gerekli", - "addon.coursecompletion.requiredcriteria": "Gerekli Ölçüt", - "addon.coursecompletion.requirement": "Gereklilikler", - "addon.coursecompletion.status": "Durum", - "addon.coursecompletion.viewcoursereport": "Kurs raporunu görüntüle", - "addon.files.emptyfilelist": "Gösterilecek dosya yok", - "addon.files.files": "Dosyalar", - "addon.files.privatefiles": "Kişisel dosyalar", - "addon.files.sitefiles": "Site dosyaları", - "addon.messages.addcontact": "Kişi ekle", - "addon.messages.addtoyourcontacts": "İletişimlerinize ekleyin", - "addon.messages.blocknoncontacts": "Tanımadığım kullanıcıları engelle", - "addon.messages.contactblocked": "İletişim engellendi", - "addon.messages.contacts": "Kişiler", - "addon.messages.deleteallconfirm": "Tüm bu sohbeti silmek istediğinizden emin misiniz?", - "addon.messages.message": "Mesaj", - "addon.messages.messagenotsent": "İleti gönderilemedi. Lütfen daha sonra tekrar deneyin.", - "addon.messages.messagepreferences": "İleti tercihleri", - "addon.messages.messages": "Mesajlar", - "addon.messages.newmessage": "Yeni ileti", - "addon.messages.newmessages": "Yeni ileti", - "addon.messages.nocontactsgetstarted": "Kişi yok", - "addon.messages.nofavourites": "Yıldızlı görüşme yok", - "addon.messages.nogroupconversations": "Grup sohbeti yok", - "addon.messages.noindividualconversations": "Özel görüşme yok", - "addon.messages.nomessagesfound": "Mesaj yok", - "addon.messages.noncontacts": "İletişim kurulamadı", - "addon.messages.nousersfound": "Kullanıcı bulunamadı", - "addon.messages.numparticipants": "{{$a}} katılımcı", - "addon.messages.removecontact": "Kişiyi sil", - "addon.messages.removecontactconfirm": "{{$a}} kişisini kişilerinizden kaldırmak istediğinizden emin misiniz?", - "addon.messages.removefromfavourites": "Görüşmenin yıldızını kaldır", - "addon.messages.removefromyourcontacts": "İletişimlerden kaldır", - "addon.messages.requests": "İstekler", - "addon.messages.requirecontacttomessage": "Size mesaj gönderebilmek için {{$a}} kişisini kişi olarak eklemenizi istemeniz gerekir.", - "addon.messages.searchcombined": "İnsan ve mesajları ara", - "addon.messages.selfconversation": "Kişisel alan", - "addon.messages.selfconversationdefaultmessage": "Daha sonra erişmek için taslak mesajları, bağlantıları, notları vb. Kaydedin.", - "addon.messages.sendcontactrequest": "İletişim isteği gönder", - "addon.messages.type_blocked": "Engellendi", - "addon.messages.type_offline": "Çevrimdışı", - "addon.messages.type_online": "Çevrimiçi", - "addon.messages.type_search": "Arama sonuçları", - "addon.messages.type_strangers": "Diğer", - "addon.messages.unabletomessage": "Bu kullanıcıya mesaj gönderemezsiniz", - "addon.messages.unblockuser": "Kullanıcının engelini kaldır", - "addon.messages.unblockuserconfirm": "{{$a}} adlı kişinin engellemesini kaldırmak istediğinizden emin misiniz?", - "addon.messages.unmuteconversation": "Sesi aç", - "addon.messages.useentertosend": "Göndermek için enter tuşunu kullanın", - "addon.messages.userwouldliketocontactyou": "{{$a}} sizinle iletişim kurmak istiyor", - "addon.messages.wouldliketocontactyou": "Seninle iletişime geçmek ister misin", - "addon.messages.you": "Sen:", - "addon.messages.youhaveblockeduser": "Bu kullanıcıyı engellediniz.", - "addon.messages.yourcontactrequestpending": "İletişim isteğiniz {{$a}} ile bekliyor", - "addon.mod_assign.addattempt": "Yeniden denemeye izin ver", - "addon.mod_assign.addnewattempt": "Yeni bir deneme ekle", - "addon.mod_assign.addnewattemptfromprevious": "Önceki gönderim üzerinden yeni bir denemeye izin ver", - "addon.mod_assign.addsubmission": "Gönderim ekle", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Ödev ayrıntıları ve başvuru formu {{$a}} adresinde ulaşılabilir olacak.", - "addon.mod_assign.allowsubmissionsfromdate": "Başvuru izni başlangıcı", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Ödev başvuruları {{$a}} adresinden kabul edilecek.", - "addon.mod_assign.applytoteam": "Notları onayla ve tüm gruba geri bildirimde bulun", - "addon.mod_assign.assignmentisdue": "Ödevin teslim süresi dolmuş", - "addon.mod_assign.attemptnumber": "Deneme numarası", - "addon.mod_assign.attemptreopenmethod": "Denemeler tekrar açıldı", - "addon.mod_assign.attemptreopenmethod_manual": "El ile", - "addon.mod_assign.attemptreopenmethod_untilpass": "Geçene kadar otomatik", - "addon.mod_assign.attemptsettings": "Deneme ayarları", - "addon.mod_assign.confirmsubmission": "Çalışmanızı notlandırılması için göndermek istediğinize emin misiniz? Bundan sonra değişiklik yapamayacaksınız.", - "addon.mod_assign.currentattempt": "{{$a}}. deneme", - "addon.mod_assign.currentattemptof": "Bu, {{$a.attemptnumber}} ({{$a.maxattempts}} deneme izinli) denemesi.", - "addon.mod_assign.currentgrade": "Notdefterindeki şuanki notu", - "addon.mod_assign.cutoffdate": "Kesilme tarihi", - "addon.mod_assign.defaultteam": "Varsayılan grup", - "addon.mod_assign.duedate": "Son teslim tarihi", - "addon.mod_assign.duedateno": "Süresiz", - "addon.mod_assign.duedatereached": "Bu ödevin teslim tarihi geçmiş", - "addon.mod_assign.editingstatus": "Durumu düzenleme", - "addon.mod_assign.editsubmission": "Gönderimi düzenle", - "addon.mod_assign.extensionduedate": "Ek sürenin bitiş tarihi", - "addon.mod_assign.grade": "Başarı notu", - "addon.mod_assign.graded": "Notlandırıldı", - "addon.mod_assign.gradedby": "", - "addon.mod_assign.gradedfollowupsubmit": "Kademeli - takip gönderimi alındı", - "addon.mod_assign.gradedon": "Not verildi", - "addon.mod_assign.gradelocked": "Bu not kilitli veya not defterinde geçersiz kılınmıştır.", - "addon.mod_assign.gradeoutof": "{{$a}} Dışarıdan notu", - "addon.mod_assign.gradingstatus": "Puan durumu", - "addon.mod_assign.groupsubmissionsettings": "Grup gönderimleri ayarları", - "addon.mod_assign.hiddenuser": "Katılımcı", - "addon.mod_assign.latesubmissions": "Gecikmiş gönderimler", - "addon.mod_assign.latesubmissionsaccepted": "Sadece ek süre tanınmış olan öğrenciler ödev göndermeye devam edebilir.", - "addon.mod_assign.markingworkflowstate": "İş akış durumunu işaretleme", - "addon.mod_assign.markingworkflowstateinmarking": "İşaretlemede", - "addon.mod_assign.markingworkflowstateinreview": "İncelemede", - "addon.mod_assign.markingworkflowstatenotmarked": "İşaretli değil", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Yayımlamak için hazır", - "addon.mod_assign.markingworkflowstatereadyforreview": "İşaretleme tamamlandı", - "addon.mod_assign.markingworkflowstatereleased": "Yayımlandı", - "addon.mod_assign.modulenameplural": "Ödevler", - "addon.mod_assign.multipleteams": "Birden fazla grubun üyesi", - "addon.mod_assign.multipleteams_desc": "Ödev, gruplar halinde sunulmayı gerektirir. Birden fazla grubun üyesisiniz. Gönderebilmeniz için yalnızca bir grubun üyesi olmanız gerekir. Grup üyeliğinizi değiştirmek için lütfen öğretmeninizle iletişime geçin.", - "addon.mod_assign.noattempt": "Deneme yok", - "addon.mod_assign.nomoresubmissionsaccepted": "Artık gönderim kabul edilmiyor", - "addon.mod_assign.noonlinesubmissions": "Bu ödev için online gönderimde bulunmanız gerekmiyor.", - "addon.mod_assign.nosubmission": "Bu ödev için gönderimde bulunulmamış.", - "addon.mod_assign.noteam": "Herhangi bir grubun üyesi değil", - "addon.mod_assign.noteam_desc": "Bu ödev, gruplar halinde sunulmayı gerektirir. Herhangi bir grubun üyesi değilsiniz, bu nedenle gönderim oluşturamazsınız. Lütfen bir gruba eklenecek öğretmeninizle iletişime geçin.", - "addon.mod_assign.notgraded": "Puanlanmamış", - "addon.mod_assign.numberofdraftsubmissions": "Taslaklar", - "addon.mod_assign.numberofparticipants": "Katılımcılar", - "addon.mod_assign.numberofsubmissionsneedgrading": "Notlandırılması gereken", - "addon.mod_assign.numberofsubmittedassignments": "Gönderilen", - "addon.mod_assign.numberofteams": "Gruplar", - "addon.mod_assign.numwords": "{{$a}} kelime", - "addon.mod_assign.outof": "{{$a.current}} dışında {{$a.total}}", - "addon.mod_assign.overdue": "Tarafından göderilen geç kalmış ödev{{$a}}", - "addon.mod_assign.submission": "Gönderim", - "addon.mod_assign.submissioneditable": "Öğrenci bu gönderimi düzenleyebilir", - "addon.mod_assign.submissionnoteditable": "Öğrenci bu gönderimi düzenleneyemez", - "addon.mod_assign.submissionslocked": "Bu ödev gönderim kabul etmemektedir", - "addon.mod_assign.submissionstatus": "Gönderim durumu", - "addon.mod_assign.submissionstatus_": "Gönderim yok", - "addon.mod_assign.submissionstatus_draft": "Taslak (gönderilmemiş)", - "addon.mod_assign.submissionstatus_marked": "Notlandırıldı", - "addon.mod_assign.submissionstatus_new": "Yeni gönderim", - "addon.mod_assign.submissionstatus_reopened": "Tekrar açıldı", - "addon.mod_assign.submissionstatus_submitted": "Notlandırılması için gönderildi", - "addon.mod_assign.submissionstatusheading": "Gönderim durumu", - "addon.mod_assign.submissionteam": "Grup", - "addon.mod_assign.submitassignment": "Ödevi gönder", - "addon.mod_assign.submitassignment_help": "Ödevinizi gönderdiğinizde, üzerinde değişiklik yapmanız mümkün olmayacak.", - "addon.mod_assign.submittedearly": "Ödev {{$a}} erken teslim edildi", - "addon.mod_assign.submittedlate": "Ödev {{$a}} geç teslim edildi", - "addon.mod_assign.timemodified": "Son düzenleme", - "addon.mod_assign.timeremaining": "Kalan süre", - "addon.mod_assign.ungroupedusers": "'Gönderim yapmak için grup gerekli' ayarı etkindir ve bazı kullanıcılar herhangi bir gruba üye değildir veya birden fazla gruba üye olduğundan, gönderim yapamazlar.", - "addon.mod_assign.ungroupedusersoptional": "'Öğrenciler gruplar halinde gönderiyor' ayarı etkinleştirildi ve bazı kullanıcılar ya herhangi bir grubun üyesi değil ya da birden fazla grubun üyesi. Bu öğrencilerin 'Varsayılan grup' üyesi olarak göndereceklerini lütfen unutmayın.", - "addon.mod_assign.unlimitedattempts": "Sınırsız", - "addon.mod_assign.userswhoneedtosubmit": "Gönderim yapması gereken kullanıcı: {{$a}}", - "addon.mod_assign.viewsubmission": "Gönderimi görüntüle", - "addon.mod_assign.wordlimit": "Kelime sınırı", - "addon.mod_assign_feedback_comments.pluginname": "Geribildirim yorumları", - "addon.mod_assign_feedback_editpdf.pluginname": "PDF ek açıklama", - "addon.mod_assign_feedback_file.pluginname": "Dos geribildirimi", - "addon.mod_assign_submission_comments.pluginname": "Gönderim yorumları", - "addon.mod_assign_submission_file.pluginname": "Dosya gönderimleri", - "addon.mod_assign_submission_onlinetext.pluginname": "Çevrimiçi metin gönderimleri", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Bu ödev için kelime sınırı {{$a limit->}} kelimeleri ve {{$a.count}} kelimeyi göndermeye çalışıyorsunuz. Lütfen gönderinizi gözden geçirin ve yeniden deneyin.", - "addon.mod_book.errorchapter": "Kitabın bölümünü okurken hata oluştu.", - "addon.mod_book.modulenameplural": "Kitaplar", - "addon.mod_book.navnexttitle": "Sonraki: {{$a}}", - "addon.mod_book.navprevtitle": "Önceki: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "Kitap bölümleri", - "addon.mod_book.toc": "İçindekiler", - "addon.mod_chat.beep": "biip", - "addon.mod_chat.chatreport": "Sohbet oturumları", - "addon.mod_chat.currentusers": "Aktif kullanıcılar", - "addon.mod_chat.enterchat": "Şimdi sohbete katıl", - "addon.mod_chat.entermessage": "Mesajınızı buraya girin", - "addon.mod_chat.messagebeepseveryone": "{{$a}} herkese sesleniyor!", - "addon.mod_chat.messagebeepsyou": "{{$a}} size sesleniyor!", - "addon.mod_chat.messageenter": "{{$a}} odaya girdi", - "addon.mod_chat.messageexit": "{{$a}} odadan ayrıldı", - "addon.mod_chat.messages": "Mesajlar", - "addon.mod_chat.messageyoubeep": "{{$a}} kullanıcısına seslendiniz", - "addon.mod_chat.modulenameplural": "Sohbetler", - "addon.mod_chat.nomessages": "Henüz mesaj yok", - "addon.mod_chat.saidto": "kişiye dedi:", - "addon.mod_chat.send": "Gönder", - "addon.mod_chat.sessionstart": "Bir sonraki görüşme oturumu {{$a.date}}, ({{$a.fromnow}} şu andan itibaren)", - "addon.mod_chat.talk": "Sohbet et", - "addon.mod_chat.viewreport": "Geçmiş sohbet oturumlarına gözat", - "addon.mod_choice.cannotsubmit": "Üzgünüz, seçiminizi gönderirken bir sorun oluştu. Lütfen tekrar deneyin.", - "addon.mod_choice.choiceoptions": "Seçim seçenekleri", - "addon.mod_choice.expired": "Üzgünüz, bu etkinlik {{$a}} tarihinde kapandı ve bu etkinliğe artık ulaşılamaz", - "addon.mod_choice.full": "(Dolu)", - "addon.mod_choice.modulenameplural": "Anketler (Mini)", - "addon.mod_choice.noresultsviewable": "Sonuçlar şu anda görüntülenmemektedir.", - "addon.mod_choice.notopenyet": "Üzgünüz, bu etkinliğe {{$a}} tarihine kadar ulaşılamaz", - "addon.mod_choice.numberofuser": "Yanıt sayısı", - "addon.mod_choice.numberofuserinpercentage": "Yanıtların yüzdesi", - "addon.mod_choice.previewonly": "Bu, bu etkinlik için mevcut seçeneklerin bir önizlemesidir. {{$a}} tarihine kadar seçiminizi yapamazsınız.", - "addon.mod_choice.publishinfoanonafter": "Siz yanıtladıktan sonra anonim sonuçlar yayınlanacaktır.", - "addon.mod_choice.publishinfoanonclose": "Etkinlik kapatıldıktan sonra anonim sonuçlar yayınlanacaktır.", - "addon.mod_choice.publishinfofullafter": "Yanıtladıktan sonra herkesin seçimlerini gösteren tam sonuçlar yayınlanacaktır.", - "addon.mod_choice.publishinfofullclose": "Etkinlik kapatıldıktan sonra herkesin seçimlerini gösteren tam sonuçlar yayınlanacaktır.", - "addon.mod_choice.publishinfonever": "Bu etkinliğin sonuçları, siz yanıtladıktan sonra yayınlanmayacak.", - "addon.mod_choice.removemychoice": "Seçimimi sil", - "addon.mod_choice.responses": "Yanıtlar", - "addon.mod_choice.responsesresultgraphheader": "Grafik ekranı", - "addon.mod_choice.savemychoice": "Seçeneğimi kaydet", - "addon.mod_choice.userchoosethisoption": "Bu seçeneği seçen kullanıcılar", - "addon.mod_choice.yourselection": "Seçiminiz", - "addon.mod_data.addentries": "Kayıtları ekle", - "addon.mod_data.advancedsearch": "Gelişmiş arama", - "addon.mod_data.alttext": "Alternatif metin", - "addon.mod_data.approve": "Onayla", - "addon.mod_data.approved": "Onaylandı", - "addon.mod_data.ascending": "Artan", - "addon.mod_data.authorfirstname": "Yazarın adı", - "addon.mod_data.authorlastname": "Yazarın soyadı", - "addon.mod_data.confirmdeleterecord": "Bu kaydı silmek istediğinizden emin misiniz?", - "addon.mod_data.descending": "Azalan", - "addon.mod_data.disapprove": "Onayı geri al", - "addon.mod_data.emptyaddform": "Hiçbir alanı doldurmadınız!", - "addon.mod_data.entrieslefttoadd": "Bu etkinliği bitirmek için {{$a.entriesleft}} kayıt daha eklemelisiniz.", - "addon.mod_data.entrieslefttoaddtoview": "Diğer katılımcıların kayıtlarını görebilmek için {{$a.entrieslefttoview}} kayıt daha eklemelisiniz.", - "addon.mod_data.errormustsupplyvalue": "Burada bir değer vermelisiniz.", - "addon.mod_data.expired": "Maalesef, bu etkinlik {{$a}} tarihinde kapandı ve artık mevcut değil", - "addon.mod_data.fields": "Alanlar", - "addon.mod_data.foundrecords": "Bulunan kayıtlar: {{$a.num}}/{{$a.max}} (Filtreleri temizle)", - "addon.mod_data.latlongboth": "Enlem ve boylam gereklidir.", - "addon.mod_data.menuchoose": "Seç...", - "addon.mod_data.modulenameplural": "Veritabanları", - "addon.mod_data.more": "Dahası", - "addon.mod_data.nomatch": "Eşleşen kayıt bulunamadı!", - "addon.mod_data.norecords": "Veritabanında kayıt yok", - "addon.mod_data.notapproved": "Kayıt henüz onaylanmamış.", - "addon.mod_data.notopenyet": "Üzgünüz, bu etkinlik {{$a}} kadar kullanılamıyor", - "addon.mod_data.numrecords": "{{$a}} kayıt", - "addon.mod_data.other": "Diğer", - "addon.mod_data.recordapproved": "Kayıt onaylandı", - "addon.mod_data.recorddeleted": "Kayıt silindi", - "addon.mod_data.recorddisapproved": "Giriş onaylanmadı", - "addon.mod_data.resetsettings": "Alanları Temizle", - "addon.mod_data.search": "Ara", - "addon.mod_data.selectedrequired": "Tüm seçililer gereklidir", - "addon.mod_data.single": "Tek görünüm", - "addon.mod_data.tagarea_data_records": "Veri kayıtları", - "addon.mod_data.timeadded": "Ekleme zamanı", - "addon.mod_data.timemodified": "Düzenleme zamanı", - "addon.mod_data.usedate": "Aramaya dahil et.", - "addon.mod_feedback.analysis": "Analiz", - "addon.mod_feedback.anonymous": "Anonim", - "addon.mod_feedback.anonymous_entries": "Anonim kayıtlar ({{$a}})", - "addon.mod_feedback.average": "Ortalama", - "addon.mod_feedback.complete_the_form": "Soruları cevaplayın...", - "addon.mod_feedback.completed_feedbacks": "Gönderilen cevaplar", - "addon.mod_feedback.continue_the_form": "Soruları cevaplamaya devam et...", - "addon.mod_feedback.feedback_is_not_open": "Geribildirim açık değil", - "addon.mod_feedback.feedbackclose": "Şuna yazılan cevaplara izin ver:", - "addon.mod_feedback.feedbackopen": "Şunlardan gelen cevaplara izin ver:", - "addon.mod_feedback.mapcourses": "Geribildirimi derslere eşleştirin", - "addon.mod_feedback.maximal": "azami (en fazla)", - "addon.mod_feedback.minimal": "asgari (en az)", - "addon.mod_feedback.mode": "Mod", - "addon.mod_feedback.modulenameplural": "Anket (Geribildirim)", - "addon.mod_feedback.next_page": "Sonraki sayfa", - "addon.mod_feedback.non_anonymous": "Kullanıcıların adları kaydedilecek ve cevaplarıyla birlikte gösterilecek", - "addon.mod_feedback.non_anonymous_entries": "anonim olmayan kayıtlar({{$a}})", - "addon.mod_feedback.non_respondents_students": "Katılımcı olmayan öğrenciler ({{$a}})", - "addon.mod_feedback.not_selected": "Seçilmedi", - "addon.mod_feedback.not_started": "Başlatılmadı", - "addon.mod_feedback.numberoutofrange": "Aralık dışı numara", - "addon.mod_feedback.overview": "Gözat", - "addon.mod_feedback.page_after_submit": "Tamamlama bildirimi", - "addon.mod_feedback.preview": "Önizleme", - "addon.mod_feedback.previous_page": "Önceki sayfa", - "addon.mod_feedback.questions": "Sorular", - "addon.mod_feedback.response_nr": "Yanıt numarası", - "addon.mod_feedback.responses": "Yanıtlar", - "addon.mod_feedback.save_entries": "Cevaplarınızı gönderin", - "addon.mod_feedback.show_entries": "Yanıtları göster", - "addon.mod_feedback.show_nonrespondents": "Katılımcıları gösterme", - "addon.mod_feedback.started": "Başlatıldı", - "addon.mod_feedback.this_feedback_is_already_submitted": "Bu etkinliği zaten doldurdunuz.", - "addon.mod_folder.emptyfilelist": "Gösterilecek dosya yok", - "addon.mod_folder.modulenameplural": "Klasörler", - "addon.mod_forum.addanewdiscussion": "Yeni tartışma konusu ekle", - "addon.mod_forum.addanewquestion": "Yeni soru ekle", - "addon.mod_forum.addanewtopic": "Yeni konu ekle", - "addon.mod_forum.addtofavourites": "Bu tartışmaya yıldız ekle", - "addon.mod_forum.advanced": "Gelişmiş", - "addon.mod_forum.cannotadddiscussion": "Bu foruma tartışma ekleme, grup üyeliği gerektirir.", - "addon.mod_forum.cannotadddiscussionall": "Tüm katılımcılar için yeni bir tartışma konusu ekleme izniniz yok.", - "addon.mod_forum.cannotcreatediscussion": "Yeni tartışma oluşturulamadı", - "addon.mod_forum.couldnotadd": "Bilinmeyen bir nedenle mesajınız eklenemedi", - "addon.mod_forum.couldnotupdate": "Bilinmeyen bir nedenle mesajınız güncellenemedi", - "addon.mod_forum.cutoffdatereached": "Bu foruma kayıt göndermek için kesme tarihine ulaşıldığından artık foruma mesaj gönderemezsiniz.", - "addon.mod_forum.delete": "Sil", - "addon.mod_forum.deletedpost": "Mesaj silindi", - "addon.mod_forum.deletesure": "Bu mesajı silmek istediğinizden emin misiniz?", - "addon.mod_forum.discussion": "Tartışma", - "addon.mod_forum.discussionlistsortbycreatedasc": "Artan tarihe göre oluşturma tarihine göre sırala", - "addon.mod_forum.discussionlistsortbycreateddesc": "Oluşturma tarihine göre azalan düzende sırala", - "addon.mod_forum.discussionlistsortbylastpostasc": "Son yazı oluşturma tarihine göre artan düzende sırala", - "addon.mod_forum.discussionlistsortbylastpostdesc": "Son gönderi oluşturma tarihine göre azalan düzende sırala", - "addon.mod_forum.discussionlistsortbyrepliesasc": "Artan sırada verilen cevap sayısına göre sırala", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "Cevap sayısına göre azalan düzende sırala", - "addon.mod_forum.discussionlocked": "Bu tartışma kilitli olduğundan artık yanıtlayamıyorsunuz.", - "addon.mod_forum.discussionpinned": "Sabitlenmiş", - "addon.mod_forum.discussionsubscription": "Tartışma aboneliği", - "addon.mod_forum.edit": "Düzelt", - "addon.mod_forum.erroremptymessage": "Yazı mesajı boş olamaz", - "addon.mod_forum.erroremptysubject": "Gönderi konusu boş olamaz.", - "addon.mod_forum.favouriteupdated": "Yıldızlı seçeneğiniz güncellendi.", - "addon.mod_forum.forumnodiscussionsyet": "Bu forum da henuz hiç tartışma başlığı yok", - "addon.mod_forum.group": "Grup", - "addon.mod_forum.lastpost": "Son mesaj", - "addon.mod_forum.lockdiscussion": "Bu tartışmayı kilitle", - "addon.mod_forum.lockupdated": "Kilitleme seçeneği güncellendi.", - "addon.mod_forum.message": "Mesaj", - "addon.mod_forum.modeflatnewestfirst": "Yanıtları yeniler önce olacak şekilde düz göster", - "addon.mod_forum.modeflatoldestfirst": "Yanıtları eskiler önce olacak şekilde düz göster", - "addon.mod_forum.modenested": "Yanıtları içiçe göster", - "addon.mod_forum.modulenameplural": "Forumlar", - "addon.mod_forum.pindiscussion": "Bu tartışmayı sabitle", - "addon.mod_forum.pinupdated": "Sabitleme seçeneği güncellendi.", - "addon.mod_forum.postisprivatereply": "Bu özel bir yanıt. Diğer katılımcılar tarafından görülemez.", - "addon.mod_forum.posttoforum": "Foruma gönder", - "addon.mod_forum.posttomygroups": "Tüm gruplara bir kopyasını gönder", - "addon.mod_forum.privatereply": "Özel olarak yanıtla", - "addon.mod_forum.re": "Ynt:", - "addon.mod_forum.removefromfavourites": "Bu tartışmayı kaldır", - "addon.mod_forum.reply": "Yanıtla", - "addon.mod_forum.replyplaceholder": "Cevap yazınız ...", - "addon.mod_forum.subject": "Konu", - "addon.mod_forum.tagarea_forum_posts": "Forum mesajları", - "addon.mod_forum.unlockdiscussion": "Bu tartışmanın kilidini aç", - "addon.mod_forum.unpindiscussion": "Bu tartışmanın sabitlemesini kaldır", - "addon.mod_forum.unread": "Okunmamış", - "addon.mod_forum.unreadpostsnumber": "{{$a}} okunmamış mesaj", - "addon.mod_forum.yourreply": "Yanıtınız", - "addon.mod_glossary.addentry": "Yeni kayıt ekle", - "addon.mod_glossary.aliases": "Anahtar sözcük(ler)", - "addon.mod_glossary.attachment": "Ek dosya", - "addon.mod_glossary.bysearch": "Ara", - "addon.mod_glossary.cannoteditentry": "Giriş düzenlenemiyor", - "addon.mod_glossary.casesensitive": "Bu girdi harf duyarlıdır", - "addon.mod_glossary.categories": "Kategoriler", - "addon.mod_glossary.concept": "Kavram", - "addon.mod_glossary.definition": "Tanım", - "addon.mod_glossary.entryusedynalink": "Bu kayıt otomatikmen linklensin", - "addon.mod_glossary.errconceptalreadyexists": "Bu kavram zaten var. Bu sözlükte aynı kaydın tekrar girilmesine izin verilmemektedir.", - "addon.mod_glossary.fillfields": "Kavram ve tanımlamalar gerekli alanlardır.", - "addon.mod_glossary.fullmatch": "Sözcüklerin tamamını eşleştir", - "addon.mod_glossary.linking": "Otomatik-linkle", - "addon.mod_glossary.modulenameplural": "Sözlükler", - "addon.mod_glossary.noentriesfound": "Her hangi bir kayıt bulunamadı", - "addon.mod_glossary.tagarea_glossary_entries": "Sözlük girdileri", - "addon.mod_imscp.deploymenterror": "İçerik paketi hatası!", - "addon.mod_imscp.modulenameplural": "IMS İçerik paketleri", - "addon.mod_imscp.showmoduledescription": "Açıklamayı göster", - "addon.mod_imscp.toc": "İçindekiler", - "addon.mod_lesson.answer": "Cevap", - "addon.mod_lesson.attempt": "Uygulama: {{$a}}", - "addon.mod_lesson.attemptheader": "Deneme(Teşebbüs)", - "addon.mod_lesson.attemptsremaining": "{{$a}} uygulama hakkınız kaldı", - "addon.mod_lesson.averagescore": "Ortalama not", - "addon.mod_lesson.averagetime": "Ortalama zaman", - "addon.mod_lesson.branchtable": "İçerik", - "addon.mod_lesson.cannotfindattempt": "Hata: uygulama bulunamadı", - "addon.mod_lesson.cannotfinduser": "Hata: kullanıcılar bulunamadı", - "addon.mod_lesson.clusterjump": "Küme içindeki bakılmamış soru", - "addon.mod_lesson.completed": "Bitirmeli", - "addon.mod_lesson.congratulations": "Tebrikler, dersin sonuna geldiniz", - "addon.mod_lesson.continue": "Devam et", - "addon.mod_lesson.continuetonextpage": "Sonraki sayfaya devam et.", - "addon.mod_lesson.defaultessayresponse": "Yazılınız kurs eğitimcisi tarafından değerlendirilecek.", - "addon.mod_lesson.detailedstats": "Ayrıntılı istatistikler", - "addon.mod_lesson.didnotanswerquestion": "Bu soruyu yanıtlamadı.", - "addon.mod_lesson.displayofgrade": "Not gösterimi (sadece öğrenciler için)", - "addon.mod_lesson.displayscorewithessays": "Otomatik olarak, değerlendirilen sorulardan {{$a.tempmaxgrade}} üzerinden {{$a.score}} aldınız.
                      {{$a.essayquestions}} yazılı sorusu için notunuz hesaplanacak
                      ve sonraki bir tarihte final notunuza eklenecektir.

                      Yazılı notlar olmaksızın şu anki notunuz {{$a.grade}} üzerinden {{$a.score}}'dir", - "addon.mod_lesson.displayscorewithoutessays": "Notunuz {{$a.score}} ({{$a.grade}} üzerinden).", - "addon.mod_lesson.emptypassword": "Şifre boş olamaz", - "addon.mod_lesson.enterpassword": "Lütfen şifreyi giriniz:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Hiç bir soruya cevap vermediniz. Bu ders için 0 aldınız.", - "addon.mod_lesson.finish": "Bitiş", - "addon.mod_lesson.firstwrong": "Cevabınız doğru olmadığı için maalesef bu puanı alamazsınız. Öğrenmenizi daha çok pekiştirmek için tahmin etmeye devam etmek ister misiniz (ama puan verilmez)?", - "addon.mod_lesson.gotoendoflesson": "Dersin sonuna git", - "addon.mod_lesson.grade": "Not", - "addon.mod_lesson.highscore": "Yüksek not", - "addon.mod_lesson.hightime": "Yüksek zaman", - "addon.mod_lesson.leftduringtimed": "Zamanlı bir dersin süresini aştınız.
                      Dersi tekrar başlatmak için Devam Et butonuna tıklayın.", - "addon.mod_lesson.leftduringtimednoretake": "Zamanlı bir dersin süresini aştınız.
                      Dersi tekrar alamaz veya derse devam edemezsiniz.", - "addon.mod_lesson.lessonmenu": "Ders menüsü", - "addon.mod_lesson.lessonstats": "Ders istatististikleri", - "addon.mod_lesson.linkedmedia": "Bağlantılı çokluortam", - "addon.mod_lesson.loginfail": "Giriş hatalı, lütfen tekrar deneyin...", - "addon.mod_lesson.lowscore": "Düşük puan", - "addon.mod_lesson.lowtime": "Düşük zaman", - "addon.mod_lesson.maximumnumberofattemptsreached": "En fazla uygulama sayısına ulaşıldı - Sonraki sayfaya gidiliyor", - "addon.mod_lesson.modattemptsnoteacher": "Öğrenci değerlendirmesi sadece öğrenciler için çalışır.", - "addon.mod_lesson.modulenameplural": "Dersler", - "addon.mod_lesson.noanswer": "Bir veya daha fazla soruya cevap verilmedi. Lütfen geri dönün ve bir cevap verin.", - "addon.mod_lesson.nolessonattempts": "Bu derste uygulama yapılmadı.", - "addon.mod_lesson.nolessonattemptsgroup": "Bu derste {{$a}} grup üyeleri tarafından hiçbir deneme yapılmadı.", - "addon.mod_lesson.notcompleted": "Tamamlanmadı", - "addon.mod_lesson.numberofcorrectanswers": "Doğru cevap sayısı: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Cevaplanan soru sayısı: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Cevaplanan soru sayısı: {{$a.nquestions}}; (En az bu kadar cevaplamalısınız: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Şu ana kadar {{$a.currenthigh}} not üzerinden {{$a.score}} puan aldınız.", - "addon.mod_lesson.ongoingnormal": "{{$a.viewed}} uygulamadan {{$a.correct}} tanesini doğru yanıtladınız.", - "addon.mod_lesson.or": "VEYA", - "addon.mod_lesson.overview": "Gözden geçir", - "addon.mod_lesson.preview": "Önizleme", - "addon.mod_lesson.progressbarteacherwarning2": "Bu dersi düzenlediğiniz için ilerleme çubuğunu göremezsiniz", - "addon.mod_lesson.progresscompleted": "Dersin {{$a}} % tamamladınız", - "addon.mod_lesson.question": "Soru", - "addon.mod_lesson.rawgrade": "Ham not", - "addon.mod_lesson.reports": "Raporlar", - "addon.mod_lesson.response": "Yanıtına karşılık dönüt", - "addon.mod_lesson.review": "Gözden geçir", - "addon.mod_lesson.reviewlesson": "Dersi tekrar gözden geçir", - "addon.mod_lesson.reviewquestionback": "Evet, tekrar deneyeyim", - "addon.mod_lesson.reviewquestioncontinue": "Hayır, sonraki soruya geçeyim", - "addon.mod_lesson.secondpluswrong": "Tamamen değil. Tekrar denemek ister misiniz?", - "addon.mod_lesson.submit": "Gönder", - "addon.mod_lesson.teacherjumpwarning": "Bir {{$a.cluster}} veya {{$a.unseen}} ilerlemesi bu derste kullanılıyor. Bunun yerine Sonraki sayfa ilerlemesi kullanılacak. Bu ilerlemeleri denemek için öğrenci olarak giriş yapın.", - "addon.mod_lesson.teacherongoingwarning": "Sürekli not gösterimi sadece öğrenciler içindir. Bunu denemek için öğrenci olarak giriş yapın", - "addon.mod_lesson.teachertimerwarning": "Sayaç sadece öğrenciler için çalışır. Öğrenci olarak giriş yaparak sayacı deneyin.", - "addon.mod_lesson.thatsthecorrectanswer": "Doğru cevap", - "addon.mod_lesson.thatsthewronganswer": "Yanlış cevap", - "addon.mod_lesson.timeremaining": "Kalan süre", - "addon.mod_lesson.timetaken": "Geçen süre", - "addon.mod_lesson.unseenpageinbranch": "Bir içerik sayfasındaki görülmemiş soru", - "addon.mod_lesson.welldone": "Aferin!", - "addon.mod_lesson.youhaveseen": "Bu dersin birden fazla sayfasını zaten gördünüz.\n
                      Kaldığınız sayfadan devam etmek ister misiniz?", - "addon.mod_lesson.youranswer": "Cevabınız", - "addon.mod_lesson.yourcurrentgradeisoutof": "Şu anki notunuz {{$a.total}} üzerinden {{$a.grade}} dir.", - "addon.mod_lesson.youshouldview": "En az cevaplamanız gereken: {{$a}}", - "addon.mod_page.modulenameplural": "Sayfalar", - "addon.mod_quiz.answercolon": "Yanıt:", - "addon.mod_quiz.attemptfirst": "İlk uygulama", - "addon.mod_quiz.attemptlast": "Son uygulama", - "addon.mod_quiz.attemptnumber": "Uygulama", - "addon.mod_quiz.attemptquiznow": "Sınavı şimdi uygula", - "addon.mod_quiz.attemptstate": "Durum", - "addon.mod_quiz.clearchoice": "Seçimimi temizle", - "addon.mod_quiz.comment": "Yorum", - "addon.mod_quiz.completedon": "Tamamlanma", - "addon.mod_quiz.confirmclose": "Bu uygulamayı gönderdikten sonra, cevaplarınızı bir daha değiştiremezsiniz.", - "addon.mod_quiz.confirmstart": "Testin süresi {{$a}}. Girişimi başlattığınız andan itibaren süre geri sayılır ve süresi dolmadan göndermeniz gerekir. Şimdi başlatmak istediğinizden emin misiniz?", - "addon.mod_quiz.confirmstartheader": "Zamanlanmış sınav", - "addon.mod_quiz.connectionerror": "Ağ bağlantısı kesildi. (Otomatik kaydetme başarısız oldu).\n\nBu sayfada son birkaç dakika içinde girilen cevaplarınız not edin, sonra yeniden bağlanmayı deneyin.\n\nBağlantı yeniden kurulduktan sonra cevaplarınızı kaydedilir ve bu ileti kaybolur.", - "addon.mod_quiz.continueattemptquiz": "Son uygulamadan devam et", - "addon.mod_quiz.continuepreview": "Son gözden geçirmeden itibaren devam et", - "addon.mod_quiz.feedback": "Geribildirim", - "addon.mod_quiz.finishattemptdots": "Uyuluma bitiyor...", - "addon.mod_quiz.grade": "Not", - "addon.mod_quiz.gradeaverage": "Not ortalaması", - "addon.mod_quiz.gradehighest": "En yüksek not", - "addon.mod_quiz.grademethod": "Notlandırma yöntemi", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Puanlar", - "addon.mod_quiz.modulenameplural": "Sınavlar", - "addon.mod_quiz.mustbesubmittedby": "Bu uygulama {{$a}} tarafından gönderilmelidir.", - "addon.mod_quiz.noquestions": "Henüz bir soru eklenmemiş", - "addon.mod_quiz.noreviewattempt": "Bu uygulamayı gözden geçirmek için izniniz yok.", - "addon.mod_quiz.notyetgraded": "Henüz puanlanmadı", - "addon.mod_quiz.outof": "{{$a.maxgrade}} üzerinden {{$a.grade}}", - "addon.mod_quiz.outofpercent": "Maksimum {{$a.maxgrade}} üzerinden {{$a.grade}} (%{{$a.percent}})", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Sonuç geribildirimi", - "addon.mod_quiz.overdue": "Süresi dolmuş", - "addon.mod_quiz.overduemustbesubmittedby": "Bu girişim şimdi gecikti. Zaten göndermiş olmalıydı. Bu sınavın derecelendirilmesini isterseniz, {{$a}} tarafından sunulmalıdır. O zamana kadar göndermezseniz, bu denemeden hiçbir işaret sayılmaz.", - "addon.mod_quiz.preview": "Önizleme", - "addon.mod_quiz.previewquiznow": "Sınavı şimdi gözden geçir", - "addon.mod_quiz.question": "Soru", - "addon.mod_quiz.quiznavigation": "Sınav gezintisi", - "addon.mod_quiz.quizpassword": "Sınav şifresi", - "addon.mod_quiz.reattemptquiz": "Sınavı tekrar uygula", - "addon.mod_quiz.requirepasswordmessage": "Bu sınavı uygulayabilmeniz için sınav parolasını bilmeniz gerekiyor", - "addon.mod_quiz.returnattempt": "Uygulamaya dön", - "addon.mod_quiz.review": "Önizleme", - "addon.mod_quiz.reviewofattempt": "{{$a}}. Uygulamayı incele", - "addon.mod_quiz.reviewofpreview": "Gözden geçirmeleri incele", - "addon.mod_quiz.showall": "Bütün soruları tek sayfada göster", - "addon.mod_quiz.showeachpage": "Bir seferde tek sayfa göster", - "addon.mod_quiz.startattempt": "Uygulamayı başlat", - "addon.mod_quiz.startedon": "Başlangıç", - "addon.mod_quiz.stateabandoned": "Asla gönderilmiş", - "addon.mod_quiz.statefinished": "Bitti", - "addon.mod_quiz.statefinisheddetails": "Gönderildi {{$a}}", - "addon.mod_quiz.stateinprogress": "Devam etmekte", - "addon.mod_quiz.stateoverdue": "Süresi dolmuş", - "addon.mod_quiz.stateoverduedetails": "{{$a}} tarafından teslim edilmelidir", - "addon.mod_quiz.status": "Durum", - "addon.mod_quiz.submitallandfinish": "Tümünü gönder ve bitir", - "addon.mod_quiz.summaryofattempt": "Uygulama özeti", - "addon.mod_quiz.summaryofattempts": "Önceki uygulamalarınızın özeti", - "addon.mod_quiz.timeleft": "Kalan Süre", - "addon.mod_quiz.timetaken": "Geçen süre", - "addon.mod_quiz.yourfinalgradeis": "Bu sınav için final notunuz: {{$a}}.", - "addon.mod_resource.modifieddate": "Değiştirilmiş {{$a}}", - "addon.mod_resource.modulenameplural": "Kaynaklar", - "addon.mod_resource.openthefile": "Dosyayı aç", - "addon.mod_resource.uploadeddate": "{{$a}} öğesine yüklendi", - "addon.mod_scorm.asset": "Öge", - "addon.mod_scorm.assetlaunched": "Öge - Bakıldı", - "addon.mod_scorm.attempts": "Uygulama (lar)", - "addon.mod_scorm.averageattempt": "Uygulama ortalaması", - "addon.mod_scorm.browse": "Önizleme", - "addon.mod_scorm.browsed": "Gözatıldı", - "addon.mod_scorm.browsemode": "Önizleme Modu", - "addon.mod_scorm.completed": "Tamamlandı", - "addon.mod_scorm.contents": "İçerik", - "addon.mod_scorm.enter": "Giriş", - "addon.mod_scorm.exceededmaxattempts": "En fazla uygulama sayısına ulaştınız.", - "addon.mod_scorm.failed": "Hata oluştu", - "addon.mod_scorm.firstattempt": "İlk uygulama", - "addon.mod_scorm.gradeaverage": "Ortalama not", - "addon.mod_scorm.gradeforattempt": "Uygulama notu", - "addon.mod_scorm.gradehighest": "En yüksek not", - "addon.mod_scorm.grademethod": "Not verme yöntemi", - "addon.mod_scorm.gradereported": "Not verme raporu", - "addon.mod_scorm.gradescoes": "Öğrenme Nesneleri", - "addon.mod_scorm.gradesum": "Notları topla", - "addon.mod_scorm.highestattempt": "En yüksek uygulama", - "addon.mod_scorm.incomplete": "Tamamlanmadı", - "addon.mod_scorm.lastattempt": "Son uygulama", - "addon.mod_scorm.modulenameplural": "SCORM/AICC", - "addon.mod_scorm.newattempt": "Yeni bir uygulama başlat", - "addon.mod_scorm.noattemptsallowed": "İzin verilen uygulama sayısı", - "addon.mod_scorm.noattemptsmade": "Yaptığınız uygulama sayısı", - "addon.mod_scorm.notattempted": "Uygulanmadı", - "addon.mod_scorm.organizations": "Düzenlemeler", - "addon.mod_scorm.passed": "Geçti", - "addon.mod_scorm.reviewmode": "Önizleme Modu", - "addon.mod_scorm.score": "Puan", - "addon.mod_scorm.suspended": "Durduruldu", - "addon.mod_scorm.toc": "TOC", - "addon.mod_survey.ifoundthat": "Gerçekte olan", - "addon.mod_survey.ipreferthat": "İstediğim", - "addon.mod_survey.modulenameplural": "Anketler (Hazır ölçekli)", - "addon.mod_survey.responses": "Yanıtlar", - "addon.mod_survey.surveycompletednograph": "Bu anketi tamamladınız.", - "addon.mod_url.modulenameplural": "Adresler", - "addon.mod_wiki.cannoteditpage": "Bu sayfayı düzenleyemezsiniz.", - "addon.mod_wiki.createpage": "Sayfa oluştur", - "addon.mod_wiki.editingpage": "Bu sayfayı düzenleme '{{$a}}'", - "addon.mod_wiki.map": "Harita", - "addon.mod_wiki.modulenameplural": "Wikiler", - "addon.mod_wiki.newpagehdr": "Yeni sayfa", - "addon.mod_wiki.newpagetitle": "Yeni sayfa başlığı", - "addon.mod_wiki.nocontent": "Bu sayfa için içerik yok", - "addon.mod_wiki.notingroup": "Grupta değil", - "addon.mod_wiki.pageexists": "Bu sayfa zaten var.", - "addon.mod_wiki.pagename": "Sayfa adı", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki sayflalrı", - "addon.mod_wiki.wrongversionlock": "Başka bir kullanıcı sizin düzenlemeniz sırasında bu sayfayı düzenledi ve içeriğiniz geçersiz.", - "addon.mod_workshop.alreadygraded": "Önceden notlandırılmış", - "addon.mod_workshop.areainstructauthors": "Gönderim yönergesi", - "addon.mod_workshop.areainstructreviewers": "Değerlendirme yönergesi", - "addon.mod_workshop.assess": "Değerlendir", - "addon.mod_workshop.assessedsubmission": "Değerlendirilmiş gönderim", - "addon.mod_workshop.assessmentform": "Değerlendirme formu", - "addon.mod_workshop.assessmentsettings": "Değerlendirme ayarları", - "addon.mod_workshop.assessmentweight": "Değerlendirme ağırlığı", - "addon.mod_workshop.assignedassessments": "Erişilen ve değerlendirilmiş gönderimler", - "addon.mod_workshop.assignedassessmentsnone": "Değerlendirmeniz için atanmış herhangi bir başvurunuz yok", - "addon.mod_workshop.conclusion": "Sonuç", - "addon.mod_workshop.createsubmission": "Gönderiminizi hazırlamaya başlayın", - "addon.mod_workshop.deletesubmission": "Gönderimi sil", - "addon.mod_workshop.editsubmission": "Gönderimi düzenle", - "addon.mod_workshop.feedbackauthor": "Yazar için geri bildirim", - "addon.mod_workshop.feedbackby": "{{$a}} tarafından geri bildirim", - "addon.mod_workshop.feedbackreviewer": "incelemeci için geri bildirim", - "addon.mod_workshop.givengrades": "Verilen notlar", - "addon.mod_workshop.gradecalculated": "Gönderim için hesaplanan not", - "addon.mod_workshop.gradeinfo": "Not: {{$a.received}} / {{$a.max}}", - "addon.mod_workshop.gradeover": "Gönderim için notu iptal et", - "addon.mod_workshop.gradesreport": "Çalıştay notları raporu", - "addon.mod_workshop.gradinggrade": "Notlandırmayı değerlendirme", - "addon.mod_workshop.gradinggradecalculated": "Değerlendirme için hesaplanan not", - "addon.mod_workshop.gradinggradeof": "Değerlendirme için ({{$a}}) notu", - "addon.mod_workshop.gradinggradeover": "Değerlendirme için notu iptal et", - "addon.mod_workshop.modulenameplural": "Çalıştaylar", - "addon.mod_workshop.nogradeyet": "Henüz bir not yok", - "addon.mod_workshop.notassessed": "Henüz değerlendirilmedi", - "addon.mod_workshop.notoverridden": "Geçersiz kılınmadı", - "addon.mod_workshop.noyoursubmission": "İşinizi henüz göndermediniz", - "addon.mod_workshop.overallfeedback": "Genel geribildirim", - "addon.mod_workshop.publishedsubmissions": "Yayınlanan gönderimler", - "addon.mod_workshop.publishsubmission": "Yayınlanan gönderim", - "addon.mod_workshop.publishsubmission_help": "Yayınlanan gönderimler, çalıştay kapatıldığında diğerleri tarafından kullanılabilir.", - "addon.mod_workshop.reassess": "Yeniden değerlendir", - "addon.mod_workshop.receivedgrades": "Alınan notlar", - "addon.mod_workshop.submissionattachment": "Ek dosya", - "addon.mod_workshop.submissioncontent": "Gönderim içeriği", - "addon.mod_workshop.submissiondeleteconfirm": "Aşağıdaki gönderimi silmek istediğinizden emin misiniz?", - "addon.mod_workshop.submissiongrade": "Gönderim için not", - "addon.mod_workshop.submissiongradeof": "Gönderim için not ({{$a}} 'nın)", - "addon.mod_workshop.submissionrequiredcontent": "Bir miktar metin girmeniz veya dosya eklemeniz gerekiyor", - "addon.mod_workshop.submissionsreport": "Çalıştay başvuruları raporu", - "addon.mod_workshop.submissiontitle": "Başlık", - "addon.mod_workshop.switchphase10": "Kurulum aşamasına geç", - "addon.mod_workshop.switchphase20": "Gönderim aşamasına geç", - "addon.mod_workshop.switchphase30": "Notlandırma aşamasına geç", - "addon.mod_workshop.switchphase40": "Değerlendirme aşamasına geç", - "addon.mod_workshop.switchphase50": "Çalıştayı bitir", - "addon.mod_workshop.userplan": "Çalıştay plancısı", - "addon.mod_workshop.userplancurrentphase": "Geçerli aşama", - "addon.mod_workshop.weightinfo": "Ağırlık :{{$a}}", - "addon.mod_workshop.yourassessment": "Sizin değerlendirmeniz", - "addon.mod_workshop.yourassessmentfor": "{{$a}} için değerlendirmeniz", - "addon.mod_workshop.yourgrades": "Sizin Notunuz", - "addon.mod_workshop.yoursubmission": "Gönderimleriniz", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "{{$a}} için yorum", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "{{$a}} için not", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "görünüm", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Bu özellik için bir not seçmelisiniz", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "{{$a}} için yorum yap", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Görünüm {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "{{$a}} için yorum yap", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "{{$a}} için not", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "İddia {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Kriter {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Bu öğelerden birini seçmek zorundasınız", - "addon.notes.addnewnote": "Yeni not ekle", - "addon.notes.coursenotes": "Ders notları", - "addon.notes.deleteconfirm": "Bu not silinsin mi?", - "addon.notes.eventnotecreated": "Not oluşturuldu", - "addon.notes.eventnotedeleted": "Not silindi", - "addon.notes.nonotes": "Bu tip için yazılmış bir not henüz yok", - "addon.notes.note": "Not", - "addon.notes.notes": "Notlar", - "addon.notes.personalnotes": "Kişisel notlar", - "addon.notes.publishstate": "Durum", - "addon.notes.sitenotes": "Site notları", - "addon.notifications.markallread": "Tümünü okunmuş olarak işaretle", - "addon.notifications.notificationpreferences": "Bildirim tercihleri", - "addon.notifications.notifications": "Bildirimler", - "addon.notifications.playsound": "Sesi çal", - "addon.notifications.therearentnotificationsyet": "Hiç bildiriminiz yok", - "assets.countries.AD": "Andora", - "assets.countries.AE": "Birleşik Arap Emirlikleri", - "assets.countries.AF": "Afganistan", - "assets.countries.AG": "Antigua ve Barbuda", - "assets.countries.AI": "Anguilla", - "assets.countries.AL": "Arnavutluk", - "assets.countries.AM": "Ermenistan", - "assets.countries.AO": "Angola", - "assets.countries.AQ": "Antarktika", - "assets.countries.AR": "Arjantin", - "assets.countries.AS": "Amerikan Samoası", - "assets.countries.AT": "Avusturya", - "assets.countries.AU": "Avustralya", - "assets.countries.AW": "Aruba", - "assets.countries.AX": "Åland Islands", - "assets.countries.AZ": "Azerbaycan", - "assets.countries.BA": "Bosna Hersek", - "assets.countries.BB": "Barbados", - "assets.countries.BD": "Bangladeş", - "assets.countries.BE": "Belçika", - "assets.countries.BF": "Burkina Faso", - "assets.countries.BG": "Bulgaristan", - "assets.countries.BH": "Bahreyn", - "assets.countries.BI": "Burundi", - "assets.countries.BJ": "Benin", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BM": "Bermuda", - "assets.countries.BN": "Brunei", - "assets.countries.BO": "Bolivya", - "assets.countries.BQ": "ABC Adaları", - "assets.countries.BR": "Brezilya", - "assets.countries.BS": "Bahamalar", - "assets.countries.BT": "Bhutan", - "assets.countries.BV": "Bouvet Adası", - "assets.countries.BW": "Botsvana", - "assets.countries.BY": "Beyaz Rusya", - "assets.countries.BZ": "Belize", - "assets.countries.CA": "Kanada", - "assets.countries.CC": "Cocos (Keeling) Adaları", - "assets.countries.CD": "Kongo", - "assets.countries.CF": "Orta Afrika Cumhuriyeti", - "assets.countries.CG": "Kongo", - "assets.countries.CH": "İsviçre", - "assets.countries.CI": "Fildişi Sahilleri", - "assets.countries.CK": "Cook Adaları", - "assets.countries.CL": "Şili", - "assets.countries.CM": "Kamerun", - "assets.countries.CN": "Çin", - "assets.countries.CO": "Kolombiya", - "assets.countries.CR": "Kosta Rika", - "assets.countries.CU": "Küba", - "assets.countries.CV": "Cape Verde", - "assets.countries.CW": "Curaçao", - "assets.countries.CX": "Christmas Adası", - "assets.countries.CY": "Kıbrıs", - "assets.countries.CZ": "Çek Cumhuriyeti", - "assets.countries.DE": "Almanya", - "assets.countries.DJ": "Cibuti", - "assets.countries.DK": "Danimarka", - "assets.countries.DM": "Dominik", - "assets.countries.DO": "Dominik Cumhuriyeti", - "assets.countries.DZ": "Cezayir", - "assets.countries.EC": "Ekvator", - "assets.countries.EE": "Estonya", - "assets.countries.EG": "Mısır", - "assets.countries.EH": "Batı Sahara", - "assets.countries.ER": "Eritre", - "assets.countries.ES": "İspanya", - "assets.countries.ET": "Etiyopya", - "assets.countries.FI": "Finlandiya", - "assets.countries.FJ": "Fiji", - "assets.countries.FK": "Falkland Adaları (Malvinalar)", - "assets.countries.FM": "Mikronezya Federal Eyaletleri", - "assets.countries.FO": "Faroe Adaları", - "assets.countries.FR": "Fransa", - "assets.countries.GA": "Gabon", - "assets.countries.GB": "Birleşik Krallık", - "assets.countries.GD": "Granada", - "assets.countries.GE": "Gürcistan", - "assets.countries.GF": "Fransız Guyanası", - "assets.countries.GG": "Guernsey", - "assets.countries.GH": "Gana", - "assets.countries.GI": "Cebelitarık", - "assets.countries.GL": "Grönland", - "assets.countries.GM": "Gambia", - "assets.countries.GN": "Gine", - "assets.countries.GP": "Guadeloupe", - "assets.countries.GQ": "Ekvator Ginesi", - "assets.countries.GR": "Yunanistan", - "assets.countries.GS": "South Georgia ve The South Sandwich", - "assets.countries.GT": "Guatemala", - "assets.countries.GU": "Guam", - "assets.countries.GW": "Gine-Bissau", - "assets.countries.GY": "Guyana", - "assets.countries.HK": "Hong Kong", - "assets.countries.HM": "Heard Adası ve Mcdonald Adaları", - "assets.countries.HN": "Honduras", - "assets.countries.HR": "Hırvatistan", - "assets.countries.HT": "Haiti", - "assets.countries.HU": "Macaristan", - "assets.countries.ID": "Endonezya", - "assets.countries.IE": "İrlanda", - "assets.countries.IL": "İsrail", - "assets.countries.IM": "Isle Of Man", - "assets.countries.IN": "Hindistan", - "assets.countries.IO": "Hint Okyanusu İngiliz Bölgesi", - "assets.countries.IQ": "Irak", - "assets.countries.IR": "İran", - "assets.countries.IS": "İzlanda", - "assets.countries.IT": "İtalya", - "assets.countries.JE": "Jersey", - "assets.countries.JM": "Jamaika", - "assets.countries.JO": "Ürdün", - "assets.countries.JP": "Japonya", - "assets.countries.KE": "Kenya", - "assets.countries.KG": "Kırgızistan", - "assets.countries.KH": "Kamboçya", - "assets.countries.KI": "Kiribati", - "assets.countries.KM": "Komorlar", - "assets.countries.KN": "Saint Kittler ve Neviler", - "assets.countries.KP": "Kuzey Kore", - "assets.countries.KR": "Güney Kore", - "assets.countries.KW": "Kuveyt", - "assets.countries.KY": "Kayman Adaları", - "assets.countries.KZ": "Kazakistan", - "assets.countries.LA": "Laos", - "assets.countries.LB": "Lübnan", - "assets.countries.LC": "Saint Lucia", - "assets.countries.LI": "Liechtenstein", - "assets.countries.LK": "Sri Lanka", - "assets.countries.LR": "Liberya", - "assets.countries.LS": "Lesotho", - "assets.countries.LT": "Litvanya", - "assets.countries.LU": "Lüksemburg", - "assets.countries.LV": "Letonya", - "assets.countries.LY": "Libya", - "assets.countries.MA": "Fas", - "assets.countries.MC": "Monako", - "assets.countries.MD": "Moldovya Cumhuriyeti", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin", - "assets.countries.MG": "Madagaskar", - "assets.countries.MH": "Marshall Adaları", - "assets.countries.MK": "Makedonya Cumhuriyeti", - "assets.countries.ML": "Mali", - "assets.countries.MM": "Myanmar", - "assets.countries.MN": "Moğolistan", - "assets.countries.MO": "Makao", - "assets.countries.MP": "Kuzey Mariana Adaları", - "assets.countries.MQ": "Martinik", - "assets.countries.MR": "Moritanya", - "assets.countries.MS": "Montserrat", - "assets.countries.MT": "Malta", - "assets.countries.MU": "Mauritius", - "assets.countries.MV": "Maldivler", - "assets.countries.MW": "Malavi", - "assets.countries.MX": "Meksika", - "assets.countries.MY": "Malezya", - "assets.countries.MZ": "Mozambik", - "assets.countries.NA": "Namibya", - "assets.countries.NC": "Yeni Kaledonya", - "assets.countries.NE": "Nijer", - "assets.countries.NF": "Norfolk Adası", - "assets.countries.NG": "Nijerya", - "assets.countries.NI": "Nikaragua", - "assets.countries.NL": "Hollanda", - "assets.countries.NO": "Norveç", - "assets.countries.NP": "Nepal", - "assets.countries.NR": "Nauru Adası", - "assets.countries.NU": "Niue Adaları", - "assets.countries.NZ": "Yeni Zelanda", - "assets.countries.OM": "Umman", - "assets.countries.PA": "Panama", - "assets.countries.PE": "Peru", - "assets.countries.PF": "Fransız Polinezyası", - "assets.countries.PG": "Papua Yeni Gine", - "assets.countries.PH": "Filipinler", - "assets.countries.PK": "Pakistan", - "assets.countries.PL": "Polonya", - "assets.countries.PM": "Saint Pierre ve Miquelon", - "assets.countries.PN": "Pitcairn", - "assets.countries.PR": "Porto Riko", - "assets.countries.PS": "Filistin", - "assets.countries.PT": "Portekiz", - "assets.countries.PW": "Palau", - "assets.countries.PY": "Paraguay", - "assets.countries.QA": "Katar", - "assets.countries.RE": "Reunion", - "assets.countries.RO": "Romanya", - "assets.countries.RS": "Sırbistan", - "assets.countries.RU": "Rusya Federasyonu", - "assets.countries.RW": "Ruanda", - "assets.countries.SA": "Suudi Arabistan", - "assets.countries.SB": "Solomon Adaları", - "assets.countries.SC": "Seyşeller", - "assets.countries.SD": "Sudan", - "assets.countries.SE": "İsveç", - "assets.countries.SG": "Singapur", - "assets.countries.SH": "Saint Helena", - "assets.countries.SI": "Slovenya", - "assets.countries.SJ": "Svalbard ve Jan Mayen", - "assets.countries.SK": "Slovakya", - "assets.countries.SL": "Sierra Leone", - "assets.countries.SM": "San Marino", - "assets.countries.SN": "Senegal", - "assets.countries.SO": "Somali", - "assets.countries.SR": "Surinam", - "assets.countries.SS": "Güney Sudan", - "assets.countries.ST": "Sao Tome ve Principe", - "assets.countries.SV": "El Salvador", - "assets.countries.SX": "Sint Maarten (Hollandaca Bölüm)", - "assets.countries.SY": "Suriye", - "assets.countries.SZ": "Svaziland", - "assets.countries.TC": "Turks ve Caicos Adaları", - "assets.countries.TD": "Çad", - "assets.countries.TF": "Fransız Güney Bölgeleri", - "assets.countries.TG": "Togo", - "assets.countries.TH": "Tayland", - "assets.countries.TJ": "Tacikistan", - "assets.countries.TK": "Tokelau", - "assets.countries.TL": "Timor-Leste", - "assets.countries.TM": "Türkmenistan", - "assets.countries.TN": "Tunus", - "assets.countries.TO": "Tonga", - "assets.countries.TR": "Türkiye", - "assets.countries.TT": "Trinidad ve Tobago", - "assets.countries.TV": "Tuvalu", - "assets.countries.TW": "Tayvan", - "assets.countries.TZ": "Tanzanya", - "assets.countries.UA": "Ukrayna", - "assets.countries.UG": "Uganda", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "Amerika Birleşik Devletleri", - "assets.countries.UY": "Uruguay", - "assets.countries.UZ": "Özbekistan", - "assets.countries.VA": "Kutsal Devlet (Vatikan Şehir Devleti)", - "assets.countries.VC": "Saint Vincent ve Grenadinler", - "assets.countries.VE": "Venezuela", - "assets.countries.VG": "İngiliz Virgin Adaları", - "assets.countries.VI": "Abd Virgin Adaları", - "assets.countries.VN": "Vietnam", - "assets.countries.VU": "Vanuatu", - "assets.countries.WF": "Wallis ve Futuna", - "assets.countries.WS": "Samoa", - "assets.countries.YE": "Yemen", - "assets.countries.YT": "Mayotte", - "assets.countries.ZA": "Güney Afrika", - "assets.countries.ZM": "Zambiya", - "assets.countries.ZW": "Zimbabwe", - "assets.mimetypes.application/epub_zip": "EPUB e-kitap", - "assets.mimetypes.application/msword": "Word belgesi", - "assets.mimetypes.application/pdf": "PDF belgesi", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle yedekleme", - "assets.mimetypes.application/vnd.ms-excel": "Excel çalışma kitabı", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 makro özellikli çalışma kitabı", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint sunumu", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument Elektronik Tablosu", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument Elektronik Tablosu şablonu", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument Metin belgesi", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument Metin şablonu", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument Web sayfası şablonu", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint sunumu", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint slayt gösterisi", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel tablosu", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel şablonu", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word dokümanı", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote sunumu", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numaraları e-tablosu", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Sayfaları belgesi", - "assets.mimetypes.application/x-javascript": "JavaScript kaynağı", - "assets.mimetypes.application/x-mspublisher": "Publisher belgesi", - "assets.mimetypes.application/x-shockwave-flash": "Flash animasyonu", - "assets.mimetypes.application/xhtml_xml": "XHTML belgesi", - "assets.mimetypes.archive": "Arşiv ({{$a.EXT}})", - "assets.mimetypes.audio": "Ses dosyası ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Dosya", - "assets.mimetypes.group:archive": "Arşiv dosyaları", - "assets.mimetypes.group:audio": "Ses dosyaları", - "assets.mimetypes.group:document": "Döküman dosyaları", - "assets.mimetypes.group:html_audio": "Tarayıcılar tarafından yerel olarak desteklenen ses dosyaları", - "assets.mimetypes.group:html_track": "HTML parça dosyaları", - "assets.mimetypes.group:html_video": "Tarayıcılar tarafından yerel olarak desteklenen video dosyaları", - "assets.mimetypes.group:image": "Görüntü dosyaları", - "assets.mimetypes.group:presentation": "Sunum dosyaları", - "assets.mimetypes.group:sourcecode": "Kaynak kodu", - "assets.mimetypes.group:spreadsheet": "Elektronik tablo dosyaları", - "assets.mimetypes.group:video": "Video dosyaları", - "assets.mimetypes.group:web_audio": "Web'de kullanılan ses dosyaları", - "assets.mimetypes.group:web_file": "Web dosyaları", - "assets.mimetypes.group:web_image": "Web'de kullanılan görüntü dosyaları", - "assets.mimetypes.group:web_video": "Web'de kullanılan video dosyaları", - "assets.mimetypes.image": "Görüntü ({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows simgesi", - "assets.mimetypes.text/css": "Basamaklı Stil Sayfası (CSS)", - "assets.mimetypes.text/csv": "Virgülle ayrılmış değerler", - "assets.mimetypes.text/html": "HTML dokümanı", - "assets.mimetypes.text/plain": "Metin dosyası", - "assets.mimetypes.text/rtf": "RTF belgesi", - "assets.mimetypes.text/vtt": "Web Video Metin Müzik Dosyası", - "assets.mimetypes.video": "Video dosyası ({{$a.EXT}})", - "core.accounts": "Hesaplar", - "core.add": "Ekle", - "core.agelocationverification": "Yaş ve konum doğrulama", - "core.ago": "{{$a}} önce", - "core.all": "Tümü", - "core.allgroups": "Bütün grublar", - "core.allparticipants": "Bütün katılımcılar", - "core.answer": "Yanıt", - "core.answered": "Cevap verildi", - "core.areyousure": "Emin misiniz?", - "core.back": "Geri", - "core.block.blocks": "Bloklar", - "core.cancel": "İptal", - "core.cannotconnect": "Bağlantı kurulamıyor: Doğru adres girdiğinizden ve sitenizin Moodle {{$a}} ve sonrası sürüme sahip olduğundan emin olun.", - "core.category": "Kategori", - "core.choose": "Seç", - "core.choosedots": "Seçiniz...", - "core.clearsearch": "Aramayı temizle", - "core.clicktohideshow": "Genişlet/Daralt", - "core.close": "Kapat", - "core.comments": "Yorumlar", - "core.comments.addcomment": "Yorum ekle...", - "core.comments.comments": "Yorumlar", - "core.comments.commentscount": "Yorumlar ({{$a}})", - "core.comments.deletecommentbyon": "{{$a.user}} tarafından {{$a.time}} tarihinde/zamanında gönderilmiş olan yorumu sil", - "core.comments.eventcommentcreated": "Yorum oluşturuldu", - "core.comments.eventcommentdeleted": "Yorum silindi", - "core.comments.nocomments": "Yorum yok", - "core.comments.savecomment": "Yorumu kaydet", - "core.commentscount": "Yorumlar ({{$a}})", - "core.completion-alt-auto-fail": "Tamamlandı (geçer not almayı başaramadı)", - "core.completion-alt-auto-n": "Tamamlanmadı", - "core.completion-alt-auto-n-override": "Tamamlanmadı: {{$a.modname}} ({{$a.overrideuser}} tarafından ayarlandı)", - "core.completion-alt-auto-pass": "Tamamlandı (geçer not almayı başardı)", - "core.completion-alt-auto-y": "Tamamlandı", - "core.completion-alt-auto-y-override": "Tamamlandı: {{$a.modname}} ({{$a.overrideuser}} tarafından ayarlandı)", - "core.completion-alt-manual-n": "Tamamlanmadı; tamamlandı olarak işaretlemek için seçin", - "core.completion-alt-manual-n-override": "Tamamlanmadı: {{$a.modname}} ({{$a.overrideuser}} tarafından ayarlandı). Tamamlandı olarak işaretlemek için seçin.", - "core.completion-alt-manual-y": "Tamamlandı; tamamlanmadı olarak işaretlemek için seçin", - "core.completion-alt-manual-y-override": "Tamamlandı: {{$a.modname}} ({{$a.overrideuser}} tarafından ayarlandı). Tamamlanmadı olarak işaretlemek için seçin.", - "core.confirmdeletefile": "Bu dosyayı silmek istediğinize emin misiniz?", - "core.confirmopeninbrowser": "Tarayıcıda açmak istediğine emin misin?", - "core.considereddigitalminor": "Bu sitede bir hesap oluşturmak için çok gençsiniz.", - "core.content": "İçerik", - "core.contentlinks.chooseaccount": "Hesap seç", - "core.continue": "Devam", - "core.course": "Ders", - "core.course.allsections": "Tüm Bölümler", - "core.course.contents": "İçerik(ler)", - "core.course.coursesummary": "Ders özeti", - "core.course.downloadcourse": "Dersi indir", - "core.course.hiddenfromstudents": "Öğrencilerden gizli", - "core.course.hiddenoncoursepage": "Kullanılabilir ancak ders sayfasında gösterilmez", - "core.course.overriddennotice": "Bu etkinlikteki final notunuz, elle ayarlandı.", - "core.course.sections": "Bölümler", - "core.coursedetails": "Ders ayrıntıları", - "core.courses.addtofavourites": "Dersi favorilere ekle", - "core.courses.allowguests": "Bu derse konuk olarak girilebilir", - "core.courses.availablecourses": "Mevcut dersler", - "core.courses.categories": "Ders Kategorileri", - "core.courses.courses": "Dersler", - "core.courses.frontpage": "Ön sayfa", - "core.courses.hidecourse": "Görünümden kaldır", - "core.courses.ignore": "Yoksay", - "core.courses.mycourses": "Derslerim", - "core.courses.mymoodle": "Kontrol paneli", - "core.courses.nocourses": "Gösterilecek ders bilgisi yok", - "core.courses.nocoursesyet": "Bu kategoride ders yok", - "core.courses.nosearchresults": "Sonuç yok", - "core.courses.notenroled": "Bu derse kayıtlı değilsiniz", - "core.courses.paymentrequired": "Bu derse giriş için ödeme yapmanız gerekir.", - "core.courses.paypalaccepted": "PayPal ödemeleri kabul edilir", - "core.courses.reload": "Tekrar yükle", - "core.courses.removefromfavourites": "Bu dersin yıldızını kaldır", - "core.courses.search": "Ara", - "core.courses.searchcourses": "Dersleri ara", - "core.courses.sendpaymentbutton": "Ödemeyi PayPal ile yap", - "core.courses.show": "Görüntülemek için geri yükle", - "core.date": "Tarih", - "core.day": "gün", - "core.days": "gün", - "core.decsep": ",", - "core.defaultvalue": "Varsayılan ({{$a}})", - "core.delete": "Sil", - "core.deleteduser": "Silinmiş kullanıcı", - "core.description": "Açıklama", - "core.digitalminor": "Dijital küçük", - "core.digitalminor_desc": "Lütfen ebeveyninizden / velinizden iletişim kurmasını isteyin:", - "core.displayoptions": "Görüntüleme seçenekleri", - "core.done": "Tamamlandı", - "core.download": "İndir", - "core.edit": "Düzenle ", - "core.editor.autosavesucceeded": "Taslak kaydedildi", - "core.editor.bold": "Kalın", - "core.editor.clear": "Biçimlendirmeyi temizle", - "core.editor.h3": "Başlık (büyük)", - "core.editor.h4": "Başlık (orta)", - "core.editor.h5": "Başlık küçük", - "core.editor.italic": "Yatık", - "core.editor.orderedlist": "Sıralı liste", - "core.editor.p": "Paragraf", - "core.editor.strike": "Üstü çizili", - "core.editor.textrecovered": "Bu metnin taslak versiyonu yeri yüklendi.", - "core.editor.underline": "Altı çizili", - "core.editor.unorderedlist": "Sırasız liste", - "core.error": "Hata", - "core.errordownloading": "Dosya indirmede hata", - "core.explanationdigitalminor": "Bu bilgi, yaşınızın dijital rıza yaşının üzerinde olup olmadığını belirlemek için gereklidir. Bu, bireyin şartlar ve koşulları kabul edebileceği ve verilerinin yasal olarak saklanıp işlendiği yaştır.", - "core.favourites": "Yıldızlı", - "core.filename": "Dosya adı", - "core.filenotfound": "Dosya yok, üzgünüz.", - "core.fileuploader.addfiletext": "Dosya ekle", - "core.fileuploader.audio": "Ses", - "core.fileuploader.camera": "Kamera", - "core.fileuploader.errorcapturingaudio": "Ses kayıt hatası", - "core.fileuploader.errorcapturingvideo": "Video kayıt hatası", - "core.fileuploader.errormustbeonlinetoupload": "Dosyaları yüklemek için çevrimiçi olmanız gerekiyor", - "core.fileuploader.file": "Dosya", - "core.fileuploader.filesofthesetypes": "Kabul edilen dosya türleri:", - "core.fileuploader.fileuploaded": "Dosya başarıyla yüklendi", - "core.fileuploader.invalidfiletype": "{{$a}} dosya türü kabul edilemez", - "core.fileuploader.more": "Dahası", - "core.fileuploader.photoalbums": "Fotoğraf albümleri", - "core.fileuploader.readingfile": "Dosya okunuyor", - "core.fileuploader.selectafile": "Dosya seç", - "core.fileuploader.uploadafile": "Dosya yükle", - "core.fileuploader.uploading": "Yükleniyor", - "core.fileuploader.video": "Video", - "core.filter": "Filtre", - "core.folder": "Klasör", - "core.forcepasswordchangenotice": "Devam etmek için şifrenizi değiştirmelisiniz.", - "core.fulllistofcourses": "Tüm dersler", - "core.grades.average": "Ortalama", - "core.grades.badgrade": "Sağlanan not geçersiz", - "core.grades.contributiontocoursetotal": "Ders toplamına katkısı", - "core.grades.feedback": "Geribildirim", - "core.grades.grade": "Başarı notu", - "core.grades.gradeitem": "Not ögesi", - "core.grades.grades": "Başarı notları", - "core.grades.lettergrade": "Harf notu", - "core.grades.nogradesreturned": "Hiç not yok", - "core.grades.nooutcome": "Hedef yok", - "core.grades.percentage": "Yüzde", - "core.grades.range": "Aralık", - "core.grades.rank": "sıra", - "core.grades.weight": "Ağırlık", - "core.group": "Grup", - "core.groupsseparate": "Ayrı gruplar", - "core.groupsvisible": "Görünür gruplar", - "core.h5p.additionallicenseinfo": "Lisansla ilgili ek bilgiler", - "core.h5p.author": "Yazar", - "core.h5p.authorcomments": "Yazar yorumları", - "core.h5p.authorname": "Yazarın ismi", - "core.h5p.authorrole": "Yazarın rolü", - "core.h5p.by": "tarafından", - "core.h5p.cancellabel": "İptal et", - "core.h5p.ccattribution": "Atıf (CC BY)", - "core.h5p.ccattributionnc": "Atıf-Ticari Olmayan (CC BY-NC)", - "core.h5p.changedby": "Tarafından değiştirildi", - "core.h5p.changedescription": "Değişimin tanımı", - "core.h5p.changelog": "Değişiklikler", - "core.h5p.changeplaceholder": "Fotoğraf kırpılmış, metin değişmiş vb.", - "core.h5p.close": "Kapat", - "core.h5p.confirmdialogbody": "Lütfen devam etmek istediğinizi onaylayın. Bu işlem geri alınamaz.", - "core.h5p.confirmdialogheader": "İşlemi onayla", - "core.h5p.confirmlabel": "Onayla", - "core.h5p.connectionLost": "Bağlantı koptu. Bağlantı yeniden kurulduğunda sonuçlar kaydedilecek ve gönderilecektir.", - "core.h5p.connectionReestablished": "Bağlantı yeniden kuruldu.", - "core.h5p.contentCopied": "İçerik panoya kopyalandı", - "core.h5p.contentchanged": "Bu içerik en son kullandığınızdan beri değişti.", - "core.h5p.contenttype": "İçerik türü", - "core.h5p.copyright": "Kullanım hakları", - "core.h5p.copyrightinfo": "Telif hakkı bilgisi", - "core.h5p.copyrightstring": "Telif hakkı", - "core.h5p.copyrighttitle": "Bu içeriğin telif hakkı bilgilerini görüntüleyin.", - "core.h5p.creativecommons": "Genel Oluşturucu", - "core.h5p.date": "Tarih", - "core.h5p.disablefullscreen": "Tam ekranı devre dışı bırak", - "core.h5p.download": "İndir", - "core.h5p.downloadtitle": "Bu içeriği bir H5P dosyası olarak indirin.", - "core.h5p.editor": "Editör", - "core.h5p.embed": "Gömülü", - "core.h5p.embedtitle": "Bu içeriğe ilişkin yerleştirme kodunu görüntüleyin.", - "core.h5p.fullscreen": "Tam Ekran", - "core.h5p.gpl": "Genel Kamu Lisansı v3", - "core.h5p.h5ptitle": "Daha fazla içeriğe bakmak için h5p.org adresini ziyaret edin.", - "core.h5p.hideadvanced": "Gelişmişleri gizle", - "core.h5p.license": "Lisans", - "core.h5p.licenseCC010U": "CC0 1.0 Evrensel", - "core.h5p.licenseCC10": "1.0 Genel", - "core.h5p.licenseCC20": "2.0 Genel", - "core.h5p.licenseCC25": "2.5 Genel", - "core.h5p.licenseCC30": "3.0 Yüklenmemiş", - "core.h5p.licenseCC40": "4.0 Uluslararası", - "core.h5p.licenseGPL": "Genel Kamu Lisansı", - "core.h5p.licenseV1": "Versiyon 1", - "core.h5p.licenseV2": "Versiyon 2", - "core.h5p.licenseV3": "Versiyon 3", - "core.h5p.licensee": "Ruhsat sahibi", - "core.h5p.licenseextras": "Ekstra lisanslar", - "core.h5p.licenseversion": "Lisans sürümü", - "core.h5p.nocopyright": "Bu içerik için telif hakkı bilgisi yok.", - "core.help": "Yardım", - "core.hide": "Gizle", - "core.hour": "saat", - "core.hours": "saat", - "core.image": "Resim", - "core.info": "Bilgi", - "core.invalidformdata": "Yanlış form verileri", - "core.labelsep": ":", - "core.lastaccess": "En son erişim", - "core.lastmodified": "En son değiştirme", - "core.lastsync": "Son senkronizasyon", - "core.layoutgrid": "Izgara", - "core.list": "Listele", - "core.listsep": ";", - "core.loading": "Yükleniyor", - "core.location": "Konum", - "core.login.auth_email": "E-Posta temelli kendi kendine kayıt", - "core.login.authenticating": "Kimlik doğrulanıyor", - "core.login.cancel": "İptal", - "core.login.changepassword": "Şifre değiştir", - "core.login.createaccount": "Yeni hesabımı oluştur", - "core.login.createuserandpass": "Kullanıcı adınızı ve şifrenizi seçin", - "core.login.emailconfirmsent": "

                      Bu e-posta adresinize ({{$a}}) bir mesaj gönderildi.

                      \n

                      Bu mesaj, kaydınızı tamamlamanız için basit yönergeleri içermektedir.

                      \n

                      Bu işlemleri yaparken bir zorlukla karşılaşırsanız site yöneticiyle iletişim kurabilirsiniz.

                      ", - "core.login.emailconfirmsentsuccess": "Onaylama e-postası başarıyla gönderildi", - "core.login.emailnotmatch": "E-postalar uyuymuyor", - "core.login.firsttime": "Buraya ilk defa mı geliyorsunuz?", - "core.login.forcepasswordchangenotice": "Devam etmek için şifrenizi değiştirmelisiniz.", - "core.login.forgotten": "Kullanıcı adı veya şifrenizi mi unuttunuz?", - "core.login.help": "Yardım", - "core.login.helpmelogin": "

                      Dünyada binlerce Moodle Sitesi var. Bu uygulama sadece Moodle App girişine izin verilmiş olan Moodle Sitelerinde çalışır

                      Eğer moodle sitenizle bağlantı kuramıyorsanız, Moodle yöneticinize başvurun nereyle bağlantı kuramıyorsanız, yöneticizle iletişime geçin ve şu linkteki açıklamaları okumasını isteyin http://docs.moodle.org/en/Mobile_app

                      Deneme yapmak için Moodle demo sitesinde ilgili alanlara öğretmen veya öğrenciiçinde Site URL seçeneklerini girin ve ekle butonuna tıklayın", - "core.login.instructions": "Yönergeler", - "core.login.invalidaccount": "Lütfen giriş bilgileriniz kontrol ediniz veya Site ayarlarını kontrol etmesi için yöneticinize başvurun.", - "core.login.invaliddate": "Geçersiz tarih", - "core.login.invalidemail": "Geçersiz e-posta adresi", - "core.login.invalidmoodleversion": "Geçersiz Moodle sürümü. Sürümünüzün en az {{$a}} olması gerekir.", - "core.login.invalidsite": "Bu site adresi geçersizdir.", - "core.login.invalidtime": "Geçersiz zaman", - "core.login.invalidurl": "Geçersiz URL belirtildi", - "core.login.invalidvaluemax": "En fazla değer {{$a}}", - "core.login.invalidvaluemin": "En az değer {{$a}}", - "core.login.login": "Giriş yap", - "core.login.loginbutton": "Giriş yap", - "core.login.logininsiterequired": "Bir tarayıcı penceresinde siteye giriş yapmanız gerekiyor.", - "core.login.loginsteps": "Bu siteye tam erişim için önce bir hesap oluşturmalısınız.", - "core.login.missingemail": "E-posta adresi eksik", - "core.login.missingfirstname": "Adı eksik", - "core.login.missinglastname": "Soyadı eksik", - "core.login.mobileservicesnotenabled": "Mobil Hizmetler sitenizde etkin değildir. Mobil erişimin etkin olduğunu düşünüyorsanız, Lütfen Moodle site yöneticinize başvurun.", - "core.login.mustconfirm": "Girişinizi onaylamalısınız", - "core.login.newaccount": "Yeni hesap", - "core.login.notloggedin": "Giriş yapmanız gerekiyor.", - "core.login.password": "Şifre", - "core.login.passwordforgotten": "Unutulan şifre", - "core.login.passwordforgotteninstructions2": "Şifrenizi yeniden oluşturmak için aşağıya kullanıcı adınızı veya şifrenizi giriniz. Sizi veritabanımızda bulabilirsek, tekrar erişim sağlamak için neler yapmanız gerektiğini anlatan bir e-posta göndereceğiz.", - "core.login.passwordrequired": "Şifre zorunludur", - "core.login.policyaccept": "Anladım ve kabul ediyorum", - "core.login.policyagree": "Bu siteyi kullanmaya devam etmek için bu sözleşmeyi kabul etmelisiniz. Kabul ediyor musunuz?", - "core.login.policyagreement": "Site şartları sözleşmesi", - "core.login.policyagreementclick": "Site şartları sözleşmesi bağlantısı", - "core.login.potentialidps": "Şu hesabınızla oturum açın:", - "core.login.profileinvaliddata": "Geçersiz değer", - "core.login.reconnect": "Yeniden bağlantı kur", - "core.login.resendemail": "Elektronik postayı tekrar gönder", - "core.login.security_question": "Güvenlik sorusu", - "core.login.selectacountry": "Bir ülke seç", - "core.login.selectsite": "Lütfen sitenizi seçin:", - "core.login.siteaddress": "Site adresi", - "core.login.siteinmaintenance": "Siteniz bakım modunda", - "core.login.siteurl": "Sİte Adresi", - "core.login.siteurlrequired": "Site adresi gereklidir, örneğin http://www.yourmoodlesite.abc veya https://www.yourmoodlesite.efg gibi", - "core.login.startsignup": "Yeni hesap oluştur", - "core.login.stillcantconnect": "Henüz bağlantı kurulamadı", - "core.login.supplyinfo": "Daha fazla bilgi", - "core.login.username": "Kullanıcı adı", - "core.login.usernameoremail": "Ya kullanıcı adını ya da e-posta adresini girin", - "core.login.usernamerequired": "Kullanıcı adı gerekiyor", - "core.login.usernotaddederror": "Kullanıcısı eklenemedi - hata", - "core.login.webservicesnotenabled": "Sitenizin Web Hizmetleri ektin değildir. Mobil erişimin etkin olduğunu düşünüyrosanız. Moodle site yöneticinize başvurun.", - "core.lostconnection": "Bağlantınızı kaybettik, yeniden bağlanmanız gerekiyor. Verileriniz artık geçerli değil.", - "core.mainmenu.changesite": "Siteyi değiştir", - "core.mainmenu.help": "Yardım", - "core.mainmenu.logout": "Çıkış yap", - "core.mainmenu.website": "Websitesi", - "core.maxsizeandattachments": "Yeni dosyalar için en büyük boyut: {{$a.size}}, en fazla ek: {{$a.attachments}}", - "core.min": "dk", - "core.mins": "dk", - "core.misc": "Çeşitli", - "core.mod_assign": "Ödev", - "core.mod_assignment": "Ödev (2.2)", - "core.mod_book": "Kitap", - "core.mod_chat": "Sohbet", - "core.mod_choice": "Anket (Mini)", - "core.mod_data": "Veritabanı", - "core.mod_database": "Veritabanı", - "core.mod_feedback": "Geribildirim", - "core.mod_file": "Dosya", - "core.mod_folder": "Klasör", - "core.mod_forum": "Forum", - "core.mod_glossary": "Sözlük", - "core.mod_ims": "IMS İçerik Paketi", - "core.mod_imscp": "IMS İçerik Paketi", - "core.mod_label": "Etiket", - "core.mod_lesson": "Ders", - "core.mod_page": "Sayfa", - "core.mod_quiz": "Sınav", - "core.mod_resource": "Kaynak", - "core.mod_scorm": "SCORM paketi", - "core.mod_survey": "Anket (Hazır ölçekli)", - "core.mod_url": "URL", - "core.mod_wiki": "Wiki", - "core.mod_workshop": "Çalıştay", - "core.moduleintro": "Açıklama", - "core.more": "dahası", - "core.mygroups": "Gruplarım", - "core.name": "Ad", - "core.networkerrormsg": "Ağ etkin değil ya da çalışmıyor.", - "core.never": "Hiçbir zaman", - "core.next": "Sonraki", - "core.no": "Hayır", - "core.nocomments": "Yorum yok", - "core.nograde": "Not yok", - "core.none": "Hiçbiri", - "core.nopermissions": "Üzgünüz, şu anda bunu yapmaya yetkiniz yok: {{$a}}", - "core.noresults": "Sonuç yok", - "core.noselection": "Seçim yok", - "core.notenrolledprofile": "Bu kullanıcı bu derse kayıtlı olmadığı için kullanıcı bilgilerine ulaşılamaz.", - "core.notice": "Uyarı", - "core.notingroup": "Üzgünüz, bu etkinliği görebilmeniz için bir grubun üyesi olmanız gerekiyor.", - "core.now": "şimdi", - "core.numwords": "{{$a}} kelime", - "core.offline": "Çevrimdışı", - "core.ok": "Tamam", - "core.online": "Çevrimiçi", - "core.openinbrowser": "Tarayıcıda aç", - "core.othergroups": "Diğer gruplar", - "core.pagea": "Sayfa {{$a}}", - "core.paymentinstant": "Ödeme için aşağıdaki düğmeyi kullanın ve bir kaç dakika içinde kaydolun!", - "core.phone": "Telefon", - "core.pictureof": "{{$a}} 'ın resmi", - "core.previous": "Önceki", - "core.proceed": "İlerlemek", - "core.question.answer": "Cevap", - "core.question.answersaved": "Cevap kaydedildi", - "core.question.certainty": "Kesinlik", - "core.question.complete": "Tamamlandı", - "core.question.correct": "Doğru", - "core.question.feedback": "Geribildirim", - "core.question.incorrect": "Yanlış", - "core.question.information": "Bilgi", - "core.question.invalidanswer": "Tamamlanmamış cevap", - "core.question.notanswered": "Cevaplanmadı", - "core.question.notyetanswered": "Henüz cevaplanmadı", - "core.question.partiallycorrect": "Kısmen doğru", - "core.question.questionno": "Soru {{$a}}", - "core.question.requiresgrading": "Notlandırma gerekir", - "core.quotausage": "Şu anda {{$a.total}} limitinizin {{$a.used}} adresini kullandınız.", - "core.rating.aggregateavg": "Puan ortalaması", - "core.rating.aggregatecount": "Puan sayısı", - "core.rating.aggregatemax": "Maksimum puan", - "core.rating.aggregatemin": "Minimum puan", - "core.rating.aggregatesum": "Puan toplamı", - "core.rating.noratings": "Derecelendirme yapılmadı", - "core.rating.rating": "Puanlama", - "core.rating.ratings": "Dereceler", - "core.refresh": "Yenile", - "core.remove": "Kaldır", - "core.required": "Gerekli", - "core.resourcedisplayopen": "Aç", - "core.resources": "Kaynaklar", - "core.restore": "Geri yükle", - "core.restricted": "Sınırlandırılmış", - "core.save": "Kaydet", - "core.savechanges": "Değişiklikleri kaydet", - "core.search": "Ara", - "core.searching": "Aranıyor", - "core.searchresults": "Arama sonuçları", - "core.sec": "sn", - "core.secs": "sn", - "core.seemoredetail": "Ayrıntıları görmek için tıklayınız", - "core.selectacategory": "Lütfen bir kategori seçin", - "core.selectacourse": "Bir ders seç", - "core.selectagroup": "Bir grup seç", - "core.send": "Gönder", - "core.sending": "Gönderiliyor", - "core.serverconnection": "Sunucuya bağlanma hatası", - "core.settings.about": "Hakkında", - "core.settings.currentlanguage": "Geçerli dil", - "core.settings.debugdisplay": "Hata ayıklama mesajlarını göster", - "core.settings.deletesitefiles": "Bu siteden indirdiğiniz dosyaları silmek istediğinizden eminmisiniz?", - "core.settings.deviceinfo": "Cihaz bilgisi", - "core.settings.deviceos": "Cihaz işletim sistemi", - "core.settings.disableall": "Bildirimleri devre dışı bırak", - "core.settings.disabled": "Devre dışı bırakıldı", - "core.settings.displayformat": "Görüntüleme biçimi", - "core.settings.enablerichtexteditor": "Meten düzenleyiciyi etkinleştir", - "core.settings.estimatedfreespace": "Tahmini boş alan", - "core.settings.fontsizecharacter": "Bir", - "core.settings.general": "Genel", - "core.settings.language": "Dil", - "core.settings.license": "Lisans", - "core.settings.locked": "Kilitli", - "core.settings.loggedin": "Çevrimiçi", - "core.settings.loggedoff": "Çevrim içi değil", - "core.settings.preferences": "Tercihler", - "core.settings.settings": "Ayarlar", - "core.settings.sites": "Siteler", - "core.settings.spaceusage": "Kullanılan alan", - "core.settings.synchronization": "Eşitleme", - "core.settings.synchronizenow": "Şimde eşitle", - "core.settings.syncsettings": "Eşitleme ayarları", - "core.settings.total": "Toplam", - "core.settings.wificonnection": "Wi-Fi Bağlantısı", - "core.sharedfiles.rename": "Yeniden adlandır", - "core.sharedfiles.replace": "Değiştir", - "core.sharedfiles.sharedfiles": "Paylaşılan dosyalar", - "core.show": "Göster", - "core.showless": "Daha az göster...", - "core.showmore": "Daha fazla göster...", - "core.site": "Site", - "core.sitehome.sitehome": "Site ana sayfası", - "core.sitehome.sitenews": "Site duyuruları", - "core.sitemaintenance": "Bu siteye şu anda bakım yapılıyor ve şimdilik erişilemez", - "core.sizeb": "bayt", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.skip": "Atla", - "core.sorry": "Üzgünüz...", - "core.sort": "Sırala", - "core.sortby": "Sıralama ölçütü", - "core.start": "Başlat", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %H:%M", - "core.strftimedatetimeshort": "%d/%m/%y, %H:%M", - "core.strftimedaydate": "%d %B %Y, %A", - "core.strftimedaydatetime": "%d %B %Y, %A, %H:%M", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b, %H:%M", - "core.strftimerecentfull": "%d %b %Y, %a, %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "Gönder", - "core.success": "Başarı", - "core.tablet": "Tablet", - "core.tag.defautltagcoll": "Varsayılan koleksiyon", - "core.tag.inalltagcoll": "Her yer", - "core.tag.itemstaggedwith": "{{$a.tagarea}} etiketlenen ile birlikte \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Sonuç yok: \"{{$a}}\"", - "core.tag.notagsfound": "\"{{$a}}\" ile eşleşen etiket bulunamadı", - "core.tag.searchtags": "Etiketleri ara", - "core.tag.showingfirsttags": "En popüler etiketler {{$a}} gösteriliyor", - "core.tag.tag": "Etiket", - "core.tag.tagarea_course": "Dersler", - "core.tag.tagarea_course_modules": "Faaliyetler ve kaynaklar", - "core.tag.tagarea_post": "Blog girdileri", - "core.tag.tagarea_user": "Kullanıcı ilgi alanları", - "core.tag.tags": "Etiketler", - "core.teachers": "Eğitimciler", - "core.thisdirection": "ltr", - "core.time": "Zaman", - "core.timesup": "Süre doldu!", - "core.today": "Bugün", - "core.unexpectederror": "Beklenmeyen hata. Lütfen uygulamanızı yeniden açın ve tekrar deneyin", - "core.unknown": "Bilinmeyen", - "core.unlimited": "Limitsiz", - "core.upgraderunning": "Site güncellemesi yapılıyor, lütfen daha sonra deneyin.", - "core.user": "Kullanıcı", - "core.user.address": "Adres", - "core.user.city": "Şehir", - "core.user.contact": "Kişi", - "core.user.country": "Ülke", - "core.user.description": "Açıklama", - "core.user.details": "Ayrıntılar", - "core.user.detailsnotavailable": "Bu kişiye ait detayları görünteleyemezsiniz.", - "core.user.editingteacher": "Eğitimci", - "core.user.email": "E-posta adresi", - "core.user.emailagain": "E-posta (tekrar)", - "core.user.firstname": "Adı", - "core.user.interests": "İlgi alanları", - "core.user.lastname": "Soyadı", - "core.user.manager": "Yönetici", - "core.user.newpicture": "Yeni resim", - "core.user.noparticipants": "Bu ders için hiç katılımcı bulunamadı", - "core.user.participants": "Katılımcılar", - "core.user.phone1": "Telefon", - "core.user.phone2": "Cep telefonu", - "core.user.roles": "Roller", - "core.user.sendemail": "Eposta", - "core.user.student": "Öğrenci", - "core.user.teacher": "Düzenleme yapamayan eğitimci", - "core.user.webpage": "Web sayfası", - "core.userdeleted": "Bu kullanıcı hesabı silindi", - "core.userdetails": "Kullanıcı ayrıntıları", - "core.usernotfullysetup": "Kullanıcı tam kurulum yapmadı", - "core.users": "Kullanıcılar", - "core.view": "Görüntüle", - "core.viewprofile": "Profili görüntüle", - "core.whatisyourage": "Yaşınız nedir?", - "core.wheredoyoulive": "Sen hangi ülkede yaşıyorsun?", - "core.whyisthisrequired": "Bu neden gerekli?", - "core.year": "yıl", - "core.years": "yıl", - "core.yes": "Evet" -} \ No newline at end of file diff --git a/src/assets/lang/uk.json b/src/assets/lang/uk.json deleted file mode 100644 index 43716e2f6..000000000 --- a/src/assets/lang/uk.json +++ /dev/null @@ -1,1777 +0,0 @@ -{ - "addon.badges.badgedetails": "Детальніше про відзнаку", - "addon.badges.badges": "Відзнаки", - "addon.badges.contact": "Контакт", - "addon.badges.dateawarded": "Дата отримання", - "addon.badges.expired": "Закінчено", - "addon.badges.expirydate": "Дата завершення", - "addon.badges.issuancedetails": "Відзнака не актуальна", - "addon.badges.issuerdetails": "Деталі присудження", - "addon.badges.issuername": "Ім’я видавця", - "addon.badges.issuerurl": "URL видавця", - "addon.badges.nobadges": "Немає доступних відзнак.", - "addon.badges.recipientdetails": "Деталі отримувача", - "addon.badges.warnexpired": "(Ця відзнака не актуальна!)", - "addon.block_activitymodules.pluginname": "Види діяльності", - "addon.block_activityresults.pluginname": "Результати діяльності", - "addon.block_badges.pluginname": "Останні відзнаки", - "addon.block_blogmenu.pluginname": "Меню блогу", - "addon.block_blogrecent.pluginname": "Нові записи блогу", - "addon.block_blogtags.pluginname": "Мітки блогу", - "addon.block_calendarmonth.pluginname": "Календар", - "addon.block_calendarupcoming.pluginname": "Незабаром", - "addon.block_comments.pluginname": "Коментарі", - "addon.block_completionstatus.pluginname": "Статус завершення курсу", - "addon.block_glossaryrandom.pluginname": "Випадковий запис глосарію", - "addon.block_learningplans.pluginname": "Навчальні плани", - "addon.block_myoverview.future": "Заплановані", - "addon.block_myoverview.inprogress": "В процесі", - "addon.block_myoverview.morecourses": "Більше курсів", - "addon.block_myoverview.nocourses": "Курси відсутні", - "addon.block_myoverview.past": "Минулі", - "addon.block_myoverview.pluginname": "Огляд курсу", - "addon.block_newsitems.pluginname": "Останні новини", - "addon.block_onlineusers.pluginname": "Зараз на сайті", - "addon.block_privatefiles.pluginname": "Особисті файли", - "addon.block_recentactivity.pluginname": "Останні дії", - "addon.block_rssclient.pluginname": "RSS клієнт", - "addon.block_selfcompletion.pluginname": "Моє навчання", - "addon.block_sitemainmenu.pluginname": "Головне меню", - "addon.block_tags.pluginname": "Мітки", - "addon.blog.blog": "Блог", - "addon.blog.blogentries": "Записи блогу", - "addon.blog.linktooriginalentry": "Посилання на оригінальний запис у блозі", - "addon.blog.noentriesyet": "Немає записів для відображення", - "addon.blog.publishtonoone": "Особисте (чорновик)", - "addon.blog.publishtosite": "Для всіх користувачів сайту", - "addon.blog.publishtoworld": "Для всіх", - "addon.blog.siteblogheading": "Блог сайту", - "addon.calendar.allday": "Цілий день", - "addon.calendar.calendar": "Календар", - "addon.calendar.calendarevents": "Події календаря", - "addon.calendar.categoryevents": "Категорія подій", - "addon.calendar.confirmeventdelete": "Ви впевнені, що бажаєте видалити цю подію?", - "addon.calendar.courseevents": "Події курсу", - "addon.calendar.daynext": "Наступного дня", - "addon.calendar.dayprev": "Попередній день", - "addon.calendar.defaultnotificationtime": "Час сповіщень за-замовчуванням", - "addon.calendar.deleteallevents": "Видалити всі події", - "addon.calendar.deleteevent": "Видалити подію", - "addon.calendar.deleteoneevent": "Видалити цю подію", - "addon.calendar.durationminutes": "Тривалість в хвилинах", - "addon.calendar.durationnone": "Не вказувати тривалість події", - "addon.calendar.durationuntil": "Триває до", - "addon.calendar.editevent": "Редагувати подію", - "addon.calendar.errorloadevent": "Помилка завантаження події.", - "addon.calendar.errorloadevents": "Помилка завантаження подій.", - "addon.calendar.eventcalendareventdeleted": "Вилучено календарну подію", - "addon.calendar.eventduration": "Тривалість", - "addon.calendar.eventendtime": "Час завершення", - "addon.calendar.eventkind": "Категорія", - "addon.calendar.eventname": "Назва", - "addon.calendar.eventstarttime": "Час початку", - "addon.calendar.eventtype": "Тип події", - "addon.calendar.fri": "Пт", - "addon.calendar.friday": "П'ятниця", - "addon.calendar.gotoactivity": "Перейти до активності", - "addon.calendar.groupevents": "Події групи", - "addon.calendar.invalidtimedurationminutes": "Неправильний формат. Будь ласка, введіть тривалість у хвилинах більшу ніж 0, або виберіть \"не вказувати тривалість\"", - "addon.calendar.invalidtimedurationuntil": "Подія не може закінчуватися раніше ніж розпочнеться.", - "addon.calendar.mon": "Пн", - "addon.calendar.monday": "Понеділок", - "addon.calendar.monthlyview": "Місяць", - "addon.calendar.newevent": "Створити подію", - "addon.calendar.noevents": "Немає подій", - "addon.calendar.nopermissiontoupdatecalendar": "Наразі, ви не маєте прав оновлювати події календаря", - "addon.calendar.repeatedevents": "Повторювані події", - "addon.calendar.repeateditall": "Застосувати зміни до всіх {{$a}} подій в циклі цих подій", - "addon.calendar.repeateditthis": "Застосувати зміни тільки до цієї події", - "addon.calendar.repeatevent": "Повторювати", - "addon.calendar.repeatweeksl": "Повторювати щотижня", - "addon.calendar.sat": "Сб", - "addon.calendar.saturday": "Субота", - "addon.calendar.siteevents": "Події сайту", - "addon.calendar.sun": "Нд", - "addon.calendar.sunday": "Неділя", - "addon.calendar.thu": "Чт", - "addon.calendar.thursday": "Четвер", - "addon.calendar.today": "Сьогодні", - "addon.calendar.tomorrow": "Завтра", - "addon.calendar.tue": "Вт", - "addon.calendar.tuesday": "Вівторок", - "addon.calendar.typecategory": "Подія категорії", - "addon.calendar.typeclose": "Закрити подію", - "addon.calendar.typecourse": "Подія курсу", - "addon.calendar.typedue": "Відповідна подія", - "addon.calendar.typegradingdue": "Визначення події", - "addon.calendar.typegroup": "Подія групи", - "addon.calendar.typeopen": "Відкрити подію", - "addon.calendar.typesite": "Подія сайту", - "addon.calendar.typeuser": "Подія користувача", - "addon.calendar.upcomingevents": "Майбутні події", - "addon.calendar.userevents": "Події користувача", - "addon.calendar.wed": "Ср", - "addon.calendar.wednesday": "Середа", - "addon.calendar.when": "Коли", - "addon.calendar.yesterday": "Вчора", - "addon.competency.activities": "Діяльності", - "addon.competency.competencies": "Компетентності", - "addon.competency.competenciesmostoftennotproficientincourse": "Компетентності, які найчастіше не досягаються у цьому курсі", - "addon.competency.coursecompetencies": "Компетентності курсу", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "Оцінювання компетентностей цього курсу не впливають на навчальні плани", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "Оцінювання компетентностей цього курсу будуть зразу передані в навчальні плани.", - "addon.competency.crossreferencedcompetencies": "Пов'язані компетентності", - "addon.competency.duedate": "Термін завершення", - "addon.competency.errornocompetenciesfound": "Не знайдено компетенції", - "addon.competency.evidence": "Підтвердження", - "addon.competency.evidence_competencyrule": "Правило для компетентності досягнуте", - "addon.competency.evidence_coursecompleted": "Курс «{{$a}}» завершено.", - "addon.competency.evidence_coursemodulecompleted": "Діяльність «{{$a}}» завершена.", - "addon.competency.evidence_courserestored": "Оцінювання було відновлено з курсом «{{$a}}».", - "addon.competency.evidence_evidenceofpriorlearninglinked": "Зв'язок підтвердження «{{$a}}» встановлено.", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "Зв'язок підтвердження «{{$a}}» видалено.", - "addon.competency.evidence_manualoverride": "Оцінювання компетентності дане вручну.", - "addon.competency.evidence_manualoverrideincourse": "Оцінювання компетентності дане вручну в курсі «{{$a}}».", - "addon.competency.evidence_manualoverrideinplan": "Оцінювання компетентності дане вручну в плані «{{$a}}».", - "addon.competency.learningplancompetencies": "Компетентності навчального плану", - "addon.competency.learningplans": "Навчальний план", - "addon.competency.myplans": "Мої навчальні плани", - "addon.competency.noactivities": "Жодної діяльності", - "addon.competency.nocompetencies": "Немає компетенції", - "addon.competency.nocompetenciesincourse": "Жодна компетентність не пов'язана з цим курсом.", - "addon.competency.nocrossreferencedcompetencies": "Жодна інша компетентність не пов'язана з даною", - "addon.competency.noevidence": "Жодного підтвердження", - "addon.competency.noplanswerecreated": "Жодного навчального плану не було створено", - "addon.competency.path": "Шлях:", - "addon.competency.planstatusactive": "Активний", - "addon.competency.planstatuscomplete": "Завершений", - "addon.competency.planstatusdraft": "Чернетка", - "addon.competency.planstatusinreview": "В процесі підтвердження", - "addon.competency.planstatuswaitingforreview": "В очікуванні підтвердження", - "addon.competency.proficient": "Набута компетентність", - "addon.competency.progress": "Прогрес", - "addon.competency.rating": "Оцінювання", - "addon.competency.reviewstatus": "Статус підтвердження", - "addon.competency.status": "Статус", - "addon.competency.template": "Шаблон навчального плану", - "addon.competency.uponcoursecompletion": "По завершенні курсу:", - "addon.competency.usercompetencystatus_idle": "В очікуванні", - "addon.competency.usercompetencystatus_inreview": "В процесі підтвердження", - "addon.competency.usercompetencystatus_waitingforreview": "В очікування підтвердження", - "addon.competency.userplans": "Навчальний план", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} компетентностей з {{$a.y}} набуті", - "addon.competency.xcompetenciesproficientoutofyincourse": "Ви набули {{$a.x}} компетентностей з {{$a.y}} наявних у цьому курсі", - "addon.coursecompletion.complete": "Завершити", - "addon.coursecompletion.completecourse": "Проходження курсу", - "addon.coursecompletion.completed": "Виконано", - "addon.coursecompletion.completiondate": "Термін закінчення", - "addon.coursecompletion.completionmenuitem": "Відслідковування виконання", - "addon.coursecompletion.couldnotloadreport": "Не вдалося завантажити звіт про закінчення курсу, будь ласка, спробуйте ще раз пізніше.", - "addon.coursecompletion.coursecompletion": "Завершення курсу", - "addon.coursecompletion.criteria": "Критерій", - "addon.coursecompletion.criteriagroup": "Група критеріїв", - "addon.coursecompletion.criteriarequiredall": "Потрібна відповідність всім вказаним критеріям", - "addon.coursecompletion.criteriarequiredany": "Потрібна відповідність будь-якому критерію", - "addon.coursecompletion.inprogress": "В процесі", - "addon.coursecompletion.manualselfcompletion": "Самореєстрація завершення", - "addon.coursecompletion.nottracked": "Наразі відслідковування завершення не проводиться на вашому курсі", - "addon.coursecompletion.notyetstarted": "Ще не почато", - "addon.coursecompletion.pending": "Очікується", - "addon.coursecompletion.required": "Необхідні", - "addon.coursecompletion.requiredcriteria": "Необхідний критерій", - "addon.coursecompletion.requirement": "Вимога", - "addon.coursecompletion.status": "Статус", - "addon.coursecompletion.viewcoursereport": "Переглянути звіт курсу", - "addon.files.couldnotloadfiles": "Список файлів не може бути завантажений.", - "addon.files.emptyfilelist": "Немає файлів для показу.", - "addon.files.erroruploadnotworking": "На жаль, в даний час не представляється можливим завантажувати файли на ваш сайт.", - "addon.files.files": "Файли", - "addon.files.privatefiles": "Особисті файли", - "addon.files.sitefiles": "Файли сайту", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Конфігурація пристроїв", - "addon.messages.addcontact": "Додати контакт", - "addon.messages.blocknoncontacts": "Запобігти повідомленням, що не в моїх контактах", - "addon.messages.contactlistempty": "Список контактів порожній", - "addon.messages.contactname": "Ім'я контакту", - "addon.messages.contacts": "Контакти", - "addon.messages.errordeletemessage": "Помилка при видаленні повідомлення.", - "addon.messages.errorwhileretrievingcontacts": "Помилка при отриманні контактів з сервера.", - "addon.messages.errorwhileretrievingdiscussions": "Помилка при отриманні обговорення з сервера.", - "addon.messages.errorwhileretrievingmessages": "Помилка при отриманні повідомлень від сервера.", - "addon.messages.message": "Повідомлення", - "addon.messages.messagenotsent": "Повідомлення не було відправлено, будь ласка, спробуйте ще раз пізніше.", - "addon.messages.messages": "Повідомлення", - "addon.messages.newmessages": "Нові повідомлення", - "addon.messages.nomessagesfound": "Повідомлень не знайдено", - "addon.messages.nousersfound": "Користувачів не знайдено", - "addon.messages.removecontact": "Видалити контакт", - "addon.messages.removecontactconfirm": "Контакт буде видалено зі списку контактів.", - "addon.messages.searchcombined": "Шукати людей та повідомлення", - "addon.messages.type_blocked": "Заблоковано", - "addon.messages.type_offline": "Офлайн", - "addon.messages.type_online": "Онлайн", - "addon.messages.type_search": "Результати пошуку", - "addon.messages.type_strangers": "Інші", - "addon.messages.warningmessagenotsent": "Неможливо відправити повідомлення до {{user}}. {{error}}", - "addon.mod_assign.acceptsubmissionstatement": "Будь ласка, прийміть заяву-згоду.", - "addon.mod_assign.addattempt": "Дозволити іншу спробу", - "addon.mod_assign.addnewattempt": "Додати нову спробу", - "addon.mod_assign.addnewattemptfromprevious": "Додати нову спробу на основі попередньої", - "addon.mod_assign.addsubmission": "Здати роботу", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "Деталі завдання та можливість здачі робіт будуть відкрити з {{$a}}", - "addon.mod_assign.allowsubmissionsfromdate": "Відповіді приймаються з", - "addon.mod_assign.allowsubmissionsfromdatesummary": "Прийом робіт з цього завдання буде відкрито з {{$a}}", - "addon.mod_assign.applytoteam": "Застосувати оцінки та відгуки для всієї групи", - "addon.mod_assign.assignmentisdue": "Прийом робіт закінчено", - "addon.mod_assign.attemptnumber": "Спроба номер", - "addon.mod_assign.attemptreopenmethod": "Можливість перездачі", - "addon.mod_assign.attemptreopenmethod_manual": "Вручну", - "addon.mod_assign.attemptreopenmethod_untilpass": "Автоматично до виконання", - "addon.mod_assign.attemptsettings": "Налаштування спроби", - "addon.mod_assign.cannoteditduetostatementsubmission": "Ви не можете додати або змінити представлення в додатку, тому що ми не могли отримати заяву з сайту.", - "addon.mod_assign.cannotgradefromapp": "Деякі методи класифікації поки не підтримуються додатком і не можуть бути змінені.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Ви не можете подати запит для сортування в додатку, тому що ми не могли отримати заява представлення з сайту.", - "addon.mod_assign.confirmsubmission": "Відправити вашу роботу на перевірку? Після цього ви не зможете робити в ній ніяких змін.", - "addon.mod_assign.currentattempt": "Це спроба {{$a}}.", - "addon.mod_assign.currentattemptof": "Це спроба {{$a.attemptnumber}} (дозволено спроб - {{$a.maxattempts}}).", - "addon.mod_assign.currentgrade": "Поточна оцінка в журналі", - "addon.mod_assign.cutoffdate": "Термін неприйняття здачі", - "addon.mod_assign.defaultteam": "Типова група", - "addon.mod_assign.duedate": "Кінцевий термін здачі", - "addon.mod_assign.duedateno": "Термін здачі не обмежено", - "addon.mod_assign.duedatereached": "Термін для здачі робіт цього завдання вже минув", - "addon.mod_assign.editingstatus": "Зміна статусу", - "addon.mod_assign.editsubmission": "Редагувати мою відповідь", - "addon.mod_assign.erroreditpluginsnotsupported": "Ви не можете додати або змінити представлення в додатку, тому що деякі плагіни не підтримуються для редагування:", - "addon.mod_assign.errorshowinginformation": "Ми не можемо відобразити інформацію представлення", - "addon.mod_assign.extensionduedate": "Відстрочений кінцевий термін", - "addon.mod_assign.feedbacknotsupported": "Цей зворотний зв'язок не підтримується програмою і не може містити всю інформацію", - "addon.mod_assign.grade": "Оцінка", - "addon.mod_assign.graded": "Оцінено", - "addon.mod_assign.gradedby": "Оцінив", - "addon.mod_assign.gradedon": "Оцінено на", - "addon.mod_assign.gradelocked": "Ця оцінка заблокована або змінена вручну в Журналі оцінок", - "addon.mod_assign.gradenotsynced": "Оцінки не синхронізовані", - "addon.mod_assign.gradeoutof": "Оцінка (макс. {{$a}})", - "addon.mod_assign.gradingstatus": "Статус оцінення", - "addon.mod_assign.groupsubmissionsettings": "Налаштування групової здачі", - "addon.mod_assign.hiddenuser": "Учасник", - "addon.mod_assign.latesubmissions": "Прострочені завдання", - "addon.mod_assign.latesubmissionsaccepted": "Дозволено до {{$a}}", - "addon.mod_assign.markingworkflowstate": "Стан оцінювання", - "addon.mod_assign.markingworkflowstateinmarking": "В оцінюванні", - "addon.mod_assign.markingworkflowstateinreview": "В перегляді", - "addon.mod_assign.markingworkflowstatenotmarked": "Не оцінено", - "addon.mod_assign.markingworkflowstatereadyforrelease": "Готово до показу", - "addon.mod_assign.markingworkflowstatereadyforreview": "Оцінено", - "addon.mod_assign.markingworkflowstatereleased": "Показано", - "addon.mod_assign.modulenameplural": "Завдання", - "addon.mod_assign.multipleteams": "Учасник кількох груп", - "addon.mod_assign.noattempt": "Немає спроб", - "addon.mod_assign.nomoresubmissionsaccepted": "Дозволено лише для учасників, яким продовжено термін виконання", - "addon.mod_assign.noonlinesubmissions": "Завдання не вимагає нічого здавати в онлайні", - "addon.mod_assign.nosubmission": "Нічого не відправлялося", - "addon.mod_assign.notallparticipantsareshown": "Учасники без представлення не показуються", - "addon.mod_assign.noteam": "Ви не включені до жодної групи, зверніться, будь ласка, до викладача.", - "addon.mod_assign.notgraded": "Не оцінено", - "addon.mod_assign.numberofdraftsubmissions": "Чорновики", - "addon.mod_assign.numberofparticipants": "Учасники", - "addon.mod_assign.numberofsubmissionsneedgrading": "Потрібно оцінити", - "addon.mod_assign.numberofsubmittedassignments": "Здано", - "addon.mod_assign.numberofteams": "Групи", - "addon.mod_assign.numwords": "{{$a}} слів", - "addon.mod_assign.outof": "{{$a.current}} з {{$a.total}}", - "addon.mod_assign.overdue": "Термін здачі прострочено на: {{$a}}", - "addon.mod_assign.submission": "Прийнято відповідей", - "addon.mod_assign.submissioneditable": "Студент може редагувати свою роботу", - "addon.mod_assign.submissionnoteditable": "Студент не може редагувати свою роботу", - "addon.mod_assign.submissionnotsupported": "Це подання не підтримується програмою і не може містити всю інформацію", - "addon.mod_assign.submissionslocked": "Це завдання не приймає робіт", - "addon.mod_assign.submissionstatus": "Статус роботи", - "addon.mod_assign.submissionstatus_": "Не здано", - "addon.mod_assign.submissionstatus_draft": "Чорновик (не здано)", - "addon.mod_assign.submissionstatus_marked": "Оцінено", - "addon.mod_assign.submissionstatus_new": "Немає відповіді", - "addon.mod_assign.submissionstatus_reopened": "Дозволено перездати", - "addon.mod_assign.submissionstatus_submitted": "Здано на оцінення", - "addon.mod_assign.submissionstatusheading": "Статус роботи", - "addon.mod_assign.submissionteam": "Група", - "addon.mod_assign.submitassignment": "Відправити на оцінення", - "addon.mod_assign.submitassignment_help": "Як тільки ви відішлете роботу на оцінення, ви не зможете зробити в ній ніяких змін", - "addon.mod_assign.submittedearly": "Завдання здано раніше на {{$a}}", - "addon.mod_assign.submittedlate": "Завдання здано пізніше на {{$a}}", - "addon.mod_assign.timemodified": "Востаннє змінено", - "addon.mod_assign.timeremaining": "Залишилося часу", - "addon.mod_assign.ungroupedusers": "Включено параметр \"Необхідна група щоб здати роботу\" але деякі студенти не записані у жодну групу, це означає, що вони не зможуть здати роботу в цьому завданні.", - "addon.mod_assign.unlimitedattempts": "Необмежено", - "addon.mod_assign.userswhoneedtosubmit": "Користувачі, яким потрібно здати: {{$a}}", - "addon.mod_assign.userwithid": "Користувач з Id {{id}}", - "addon.mod_assign.viewsubmission": "Переглянути роботу", - "addon.mod_assign.warningsubmissiongrademodified": "Ступінь представлення була змінена на сайті.", - "addon.mod_assign.warningsubmissionmodified": "Представлення користувача було змінено на сайті.", - "addon.mod_assign.wordlimit": "Обмеження в кількості слів", - "addon.mod_assign_feedback_comments.pluginname": "Відгук - коментарем", - "addon.mod_assign_feedback_editpdf.pluginname": "Анотація PDF", - "addon.mod_assign_feedback_file.pluginname": "Відгук - файлом", - "addon.mod_assign_submission_comments.pluginname": "Коментарі до відповідей", - "addon.mod_assign_submission_file.pluginname": "Завантаження файлу", - "addon.mod_assign_submission_onlinetext.pluginname": "Відповідь текстом", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "Максимальна кількість слів для цього завдання є {{$a.limit}}, а ваша відповідь складає {{$a.count}} слів. Перевірте свій текст та спробуйте ще раз.", - "addon.mod_book.errorchapter": "Помилка читання розділу книги", - "addon.mod_book.modulenameplural": "Книги", - "addon.mod_book.toc": "Зміст", - "addon.mod_chat.beep": "сигнал", - "addon.mod_chat.chatreport": "Чати", - "addon.mod_chat.currentusers": "Поточні користувачі", - "addon.mod_chat.enterchat": "Увійти в чат", - "addon.mod_chat.entermessage": "Введіть ваше повідомлення", - "addon.mod_chat.errorwhileconnecting": "Помилка при підключенні до чату.", - "addon.mod_chat.errorwhilegettingchatdata": "Помилка отримання даних чату.", - "addon.mod_chat.errorwhilegettingchatusers": "Помилка отримання користувачів чату.", - "addon.mod_chat.errorwhileretrievingmessages": "Помилка отримання повідомлень з серверу.", - "addon.mod_chat.errorwhilesendingmessage": "Помилка відправки повідомлення.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} відправив сигнал всім!", - "addon.mod_chat.messagebeepsyou": "{{$a}} відправив Вам сигнал!", - "addon.mod_chat.messageenter": "{{$a}} з'явився в чаті", - "addon.mod_chat.messageexit": "{{$a}} пішов із чату", - "addon.mod_chat.messages": "Повідомлення", - "addon.mod_chat.messageyoubeep": "Ви відправили сигнал {{$a}}", - "addon.mod_chat.modulenameplural": "Чати", - "addon.mod_chat.mustbeonlinetosendmessages": "Ви повинні бути онлайн для відправки повідомлення", - "addon.mod_chat.nomessages": "Ще немає повідомлень", - "addon.mod_chat.saidto": "сказав до", - "addon.mod_chat.send": "Відіслати", - "addon.mod_chat.sessionstart": "Чат-сесію буде розпочато: {{$a}}", - "addon.mod_chat.talk": "Розмова", - "addon.mod_chat.viewreport": "Переглянути минулі чат-сесії", - "addon.mod_choice.cannotsubmit": "Вибачте, але трапилася проблема під час збереження вашого вибору. Повторіть, будь ласка, ще раз.", - "addon.mod_choice.choiceoptions": "Параметри вибору", - "addon.mod_choice.errorgetchoice": "Помилка при отриманні даних виборки.", - "addon.mod_choice.expired": "На жаль, ця діяльність закрита для {{$a}} та більше недоступна", - "addon.mod_choice.full": "(Все)", - "addon.mod_choice.modulenameplural": "Вибори", - "addon.mod_choice.noresultsviewable": "В даний час результати не можна побачити.", - "addon.mod_choice.notopenyet": "Цей ресурс буде недоступний до {{$a}}", - "addon.mod_choice.numberofuser": "Кількість користувачів", - "addon.mod_choice.numberofuserinpercentage": "Кількість користувачів у відсотках", - "addon.mod_choice.previewonly": "Це лише попередній перегляд доступних варіантів для цієї діяльності. Ви не можете відправити відповідь до {{$a}}.", - "addon.mod_choice.removemychoice": "Видалити мій вибір", - "addon.mod_choice.responses": "Відповіді", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% користувачів вибрали пункт: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "Показувати графік", - "addon.mod_choice.resultsnotsynced": "Результати не включають останні відповіді. Будь ласка, синхронізуйтесь для поновлення.", - "addon.mod_choice.savemychoice": "Зберегти відповідь", - "addon.mod_choice.userchoosethisoption": "Користувачі, які вибрали цю опцію", - "addon.mod_choice.yourselection": "Ваш вибір", - "addon.mod_data.addentries": "Додати записи", - "addon.mod_data.advancedsearch": "Розширений пошук", - "addon.mod_data.alttext": "Альтернативний текст", - "addon.mod_data.approve": "Прийняти", - "addon.mod_data.approved": "Прийнято", - "addon.mod_data.ascending": "за зростанням", - "addon.mod_data.authorfirstname": "Ім’я автора", - "addon.mod_data.authorlastname": "Прізвище автора", - "addon.mod_data.confirmdeleterecord": "Ви впевнені, що хочете видалити цей запис?", - "addon.mod_data.descending": "за спаданням", - "addon.mod_data.disapprove": "Скасувати схвалення", - "addon.mod_data.emptyaddform": "Ви не заповнили жодного поля!", - "addon.mod_data.entrieslefttoadd": "Вам потрібно додати більше чим {{$a}} записів перед тим, як ви зможете побачити записи інших учасників.", - "addon.mod_data.entrieslefttoaddtoview": "Ви повинні ввести більше чим {{$a.entrieslefttoview}} запис(ів) перед тим, як матимете змогу бачити записи інших.", - "addon.mod_data.errormustsupplyvalue": "Ви повинні тут вказати значення.", - "addon.mod_data.expired": "На жаль, ця діяльність закрита на {{$a}} і більше не доступна", - "addon.mod_data.fields": "Поля", - "addon.mod_data.foundrecords": "Знайдено записи: {{$a.num}}/{{$a.max}} (Перевстановити фільтр)", - "addon.mod_data.latlongboth": "Широта і довгота є обов’язковими.", - "addon.mod_data.menuchoose": "Вибрати...", - "addon.mod_data.modulenameplural": "Бази даних", - "addon.mod_data.more": "Детальний перегляд...", - "addon.mod_data.nomatch": "Жодного запису не знайдено!", - "addon.mod_data.norecords": "Немає записів у базі даних", - "addon.mod_data.notapproved": "Запис ще не схвалено", - "addon.mod_data.notopenyet": "ця діяльність не доступна до {{$a}}", - "addon.mod_data.numrecords": "{{$a}} записів", - "addon.mod_data.other": "Інше", - "addon.mod_data.recordapproved": "Запис погоджено", - "addon.mod_data.recorddeleted": "Запис вилучено", - "addon.mod_data.recorddisapproved": "Запис не схвалено", - "addon.mod_data.resetsettings": "Скинути фільтри", - "addon.mod_data.search": "Пошук", - "addon.mod_data.selectedrequired": "Всі відібрані вимоги", - "addon.mod_data.single": "Перегляд одного запису", - "addon.mod_data.timeadded": "Час введення", - "addon.mod_data.timemodified": "Час модифікації", - "addon.mod_data.usedate": "Включити в пошук", - "addon.mod_feedback.analysis": "Аналіз", - "addon.mod_feedback.anonymous": "Анонімно", - "addon.mod_feedback.anonymous_entries": "Анонімні записи", - "addon.mod_feedback.average": "Середнє", - "addon.mod_feedback.captchaofflinewarning": "Зворотній зв'язок із captcha не може бути завершений, якщо не налаштований, в автономному режимі або сервер не працює.", - "addon.mod_feedback.complete_the_form": "Відповідь на питання..", - "addon.mod_feedback.completed_feedbacks": "Надані відповіді", - "addon.mod_feedback.continue_the_form": "Продовжити форму", - "addon.mod_feedback.feedback_is_not_open": "Зворотний зв'язок не відкрито", - "addon.mod_feedback.feedback_submitted_offline": "Цей зворотний зв'язок був збережений, та буде представлений пізніше.", - "addon.mod_feedback.feedbackclose": "Закрити зворотний зв’язок до", - "addon.mod_feedback.feedbackopen": "Відкрити зворотний зв’язок до", - "addon.mod_feedback.mapcourses": "Прив’язка зворотного зв’язку до курсів", - "addon.mod_feedback.maximal": "максимально", - "addon.mod_feedback.minimal": "мінімально", - "addon.mod_feedback.mode": "Режим", - "addon.mod_feedback.modulenameplural": "Зворотний зв’язок", - "addon.mod_feedback.next_page": "Наступна сторінка", - "addon.mod_feedback.non_anonymous": "Ім'я користувача буде записане та показане з відповідями", - "addon.mod_feedback.non_anonymous_entries": "немає анонімних відповідей", - "addon.mod_feedback.non_respondents_students": "немає опитаних студентів", - "addon.mod_feedback.not_selected": "Не вибрано", - "addon.mod_feedback.not_started": "не почато", - "addon.mod_feedback.numberoutofrange": "Значення поза діапазоном", - "addon.mod_feedback.overview": "Перегляд", - "addon.mod_feedback.page_after_submit": "Сторінка після відсилання відповіді", - "addon.mod_feedback.preview": "Попередній перегляд", - "addon.mod_feedback.previous_page": "Попередня сторінка", - "addon.mod_feedback.questions": "Питання", - "addon.mod_feedback.response_nr": "Номер відповіді", - "addon.mod_feedback.responses": "Відповіді", - "addon.mod_feedback.save_entries": "Відправити ваші відповіді", - "addon.mod_feedback.show_entries": "Показати відповіді", - "addon.mod_feedback.show_nonrespondents": "Показати користувачів без відповіді", - "addon.mod_feedback.started": "почато", - "addon.mod_feedback.this_feedback_is_already_submitted": "Ви вже пройшли цю діяльність", - "addon.mod_folder.emptyfilelist": "Немає файлів для показу", - "addon.mod_folder.modulenameplural": "Теки", - "addon.mod_forum.addanewdiscussion": "Додати тему для обговорення", - "addon.mod_forum.addanewquestion": "Додати нове питання", - "addon.mod_forum.addanewtopic": "Додати нову тему", - "addon.mod_forum.advanced": "Додаткові параметри", - "addon.mod_forum.cannotadddiscussion": "Додання тем обговорення на цей форум вимагає членства у групі.", - "addon.mod_forum.cannotadddiscussionall": "Ви не маєте права створювати нові теми дискусії для всіх учасників", - "addon.mod_forum.cannotcreatediscussion": "Не вдається створити нову дискусію", - "addon.mod_forum.couldnotadd": "Повідомлення не додане через невідому помилку", - "addon.mod_forum.couldnotupdate": "Повідомлення не оновлене через невідому помилку", - "addon.mod_forum.delete": "Видалити", - "addon.mod_forum.deletedpost": "Повідомлення було видалено", - "addon.mod_forum.deletesure": "Ви впевнені, що хочете видалити це повідомлення?", - "addon.mod_forum.discussion": "Обговорення", - "addon.mod_forum.discussionpinned": "Прикріплено", - "addon.mod_forum.discussionsubscription": "Підписатися на дискусію", - "addon.mod_forum.edit": "Редагувати", - "addon.mod_forum.erroremptymessage": "Повідомлення не може бути порожнім", - "addon.mod_forum.erroremptysubject": "Тема повідомлення не може бути порожньою", - "addon.mod_forum.errorgetforum": "Помилка отримання даних форуму", - "addon.mod_forum.errorgetgroups": "Помилка отримання групових налаштувань.", - "addon.mod_forum.forumnodiscussionsyet": "Відсутні обговорення в даному форумі.", - "addon.mod_forum.group": "Група", - "addon.mod_forum.lastpost": "Останнє повідомлення", - "addon.mod_forum.message": "Повідомлення", - "addon.mod_forum.modeflatnewestfirst": "Показувати відповіді простим списком, найновіші спочатку", - "addon.mod_forum.modeflatoldestfirst": "Показувати відповіді простим списком, найстаріші спочатку", - "addon.mod_forum.modenested": "Показувати відповіді у формі вкладених повідомлень", - "addon.mod_forum.modulenameplural": "Форуми", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} дискусій", - "addon.mod_forum.numreplies": "{{numreplies}} відповідей", - "addon.mod_forum.posttoforum": "Надіслати до форуму", - "addon.mod_forum.posttomygroups": "Надіслати копію всім групам", - "addon.mod_forum.re": "", - "addon.mod_forum.refreshdiscussions": "Оновити обговорення", - "addon.mod_forum.refreshposts": "Оновити пости з обговоренням", - "addon.mod_forum.reply": "Відповісти", - "addon.mod_forum.subject": "Тема", - "addon.mod_forum.unread": "Непрочитані", - "addon.mod_forum.unreadpostsnumber": "{{$a}} непрочитаних повідомлень", - "addon.mod_forum.yourreply": "Ваша відповідь", - "addon.mod_glossary.addentry": "Додати новий запис", - "addon.mod_glossary.aliases": "Ключові слова", - "addon.mod_glossary.attachment": "Вкладення", - "addon.mod_glossary.browsemode": "Перегляд записів", - "addon.mod_glossary.byalphabet": "По алфавіту", - "addon.mod_glossary.byauthor": "Групувати за автором", - "addon.mod_glossary.bycategory": "Групувати за категорією", - "addon.mod_glossary.bynewestfirst": "Новіші перші", - "addon.mod_glossary.byrecentlyupdated": "Нещодавно оновлені", - "addon.mod_glossary.bysearch": "Пошук", - "addon.mod_glossary.cannoteditentry": "Неможливо редагувати запис", - "addon.mod_glossary.casesensitive": "Це слово чутливе до регістру", - "addon.mod_glossary.categories": "Категорії", - "addon.mod_glossary.concept": "Поняття", - "addon.mod_glossary.definition": "Визначення", - "addon.mod_glossary.entriestobesynced": "Записи будуть синхронізовані", - "addon.mod_glossary.entrypendingapproval": "Цей запис очікує схвалення.", - "addon.mod_glossary.entryusedynalink": "Цей запис повинен автоматично зв'язуватись", - "addon.mod_glossary.errconceptalreadyexists": "Цей термін вже існує. Дублювати записи в глосарії не дозволено.", - "addon.mod_glossary.errorloadingentries": "Сталася помилка під час завантаження записів.", - "addon.mod_glossary.errorloadingentry": "Сталася помилка під час завантаження запису.", - "addon.mod_glossary.errorloadingglossary": "Сталася помилка під час завантаження глосарію.", - "addon.mod_glossary.fillfields": "Поняття та визначення - поля обов’язкові.", - "addon.mod_glossary.fullmatch": "Визначати відповідність тільки повним словам", - "addon.mod_glossary.linking": "Автозв'язування", - "addon.mod_glossary.modulenameplural": "Глосарії", - "addon.mod_glossary.noentriesfound": "Немає записів", - "addon.mod_glossary.searchquery": "Пошуковий запит", - "addon.mod_imscp.deploymenterror": "Помилка пакування вмісту!", - "addon.mod_imscp.modulenameplural": "IMS контент пакети", - "addon.mod_imscp.showmoduledescription": "Показати опис", - "addon.mod_imscp.toc": "Зміст", - "addon.mod_lesson.answer": "Відповідь", - "addon.mod_lesson.attempt": "Спроба: {{$a}}", - "addon.mod_lesson.attemptheader": "Спроба", - "addon.mod_lesson.attemptsremaining": "У Вас залишилося {{$a}} спроб(и)", - "addon.mod_lesson.averagescore": "Середній бал", - "addon.mod_lesson.averagetime": "Середній час", - "addon.mod_lesson.branchtable": "Вміст", - "addon.mod_lesson.cannotfindattempt": "Помилка: не знайдено спробу", - "addon.mod_lesson.cannotfinduser": "Помилка: користувачів не знайдено", - "addon.mod_lesson.clusterjump": "Не переглянута сторінка кластеру", - "addon.mod_lesson.completed": "Завершено", - "addon.mod_lesson.congratulations": "Вітаємо - Ви повністю пройшли цей урок", - "addon.mod_lesson.continue": "Продовжити", - "addon.mod_lesson.continuetonextpage": "Продовжити до наступної сторінки.", - "addon.mod_lesson.defaultessayresponse": "Ваше есе буде оцінене вашим викладачем.", - "addon.mod_lesson.detailedstats": "Детальна статистика", - "addon.mod_lesson.didnotanswerquestion": "Немає відповіді на питання", - "addon.mod_lesson.displayofgrade": "Показати оцінки (тільки для студентів)", - "addon.mod_lesson.displayscorewithessays": "Ви отримали {{$a.score}} балів із {{$a.tempmaxgrade}} за питання, оцінені автоматично.
                      Оцінки за {{$a.essayquestions}} эссе будуть виставленні і враховані при виставленні результативної оцінки.
                      Ваша поточна оцінка (без врахування эсе): {{$a.score}} из {{$a.grade}}.", - "addon.mod_lesson.displayscorewithoutessays": "Ваша оцінка {{$a.score}} (з {{$a.grade}}).", - "addon.mod_lesson.emptypassword": "Пароль не може бути порожнім", - "addon.mod_lesson.enterpassword": "Будь ласка, введіть пароль:", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Ви не дали відповіді на жодне запитання. Ви отримуєте нуль за цей урок.", - "addon.mod_lesson.errorreviewretakenotlast": "Ця спроба не може бути переглянута, тому що ще одна спроба була закінчена.", - "addon.mod_lesson.finish": "Завершити", - "addon.mod_lesson.finishretakeoffline": "Ця спроба була закінчена в автономному режимі.", - "addon.mod_lesson.firstwrong": "На жаль, за неправильної відповіді Ви не можете отримати бал. Ви хочете продовжити вгадування в цілях навчання (але не в цілях отримання балів)?", - "addon.mod_lesson.gotoendoflesson": "Перейти на кінець уроку", - "addon.mod_lesson.grade": "Оцінка", - "addon.mod_lesson.highscore": "Найвищій бал", - "addon.mod_lesson.hightime": "Найбільший час", - "addon.mod_lesson.leftduringtimed": "Ви закінчили урок із заданим часом його проходу.
                      Будь-ласка, клацніть на кнопку «Продовжити», щоб почати урок знову.", - "addon.mod_lesson.leftduringtimednoretake": "Ви покинули урок під час його проходження і не можете пройти його знову або продовжити.", - "addon.mod_lesson.lessonmenu": "Меню уроку", - "addon.mod_lesson.lessonstats": "Статистика уроку", - "addon.mod_lesson.linkedmedia": "Зв'язані медіа", - "addon.mod_lesson.loginfail": "Помилка входу, будь ласка, спробуйте ще раз...", - "addon.mod_lesson.lowscore": "Найнижчий бал", - "addon.mod_lesson.lowtime": "Менший час", - "addon.mod_lesson.maximumnumberofattemptsreached": "Досягнуто максимальної кількості спроб - Перехід до наступної сторінки", - "addon.mod_lesson.modattemptsnoteacher": "Перегляд студентів доступний лише для студентів.", - "addon.mod_lesson.modulenameplural": "Уроки", - "addon.mod_lesson.noanswer": "Одне або більше питань залишилися без відповіді. Будь ласка, поверніться і дайте відповідь.", - "addon.mod_lesson.nolessonattempts": "спроби проходження цього уроку не знайдено.", - "addon.mod_lesson.nolessonattemptsgroup": "Студенти групи {{$a}} не зробили жодної спроби проходження уроку.", - "addon.mod_lesson.notcompleted": "Не завершено", - "addon.mod_lesson.numberofcorrectanswers": "Кількість правильних відповідей: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Кількість запитань, на які було дано відповідь: {{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "Кількість запитань, на які було дано відповідь: {{$a.nquestions}}; (вам потрібно відповісти хоча б на: {{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "Ви отримали {{$a.score}} бал(ів) з {{$a.currenthigh}} можливих.", - "addon.mod_lesson.ongoingnormal": "Ви відповіли правильно на {{$a.correct}} запитань із {{$a.viewed}} переглянутих.", - "addon.mod_lesson.or": "АБО", - "addon.mod_lesson.overview": "Переглянути", - "addon.mod_lesson.preview": "Попередньо переглянути", - "addon.mod_lesson.progressbarteacherwarning2": "Ви не зможете побачити показник виконання, тому що Ви редагуєте цей урок", - "addon.mod_lesson.progresscompleted": "Ви вже пройшли {{$a}}% уроку", - "addon.mod_lesson.question": "Питання", - "addon.mod_lesson.rawgrade": "Попередня оцінка", - "addon.mod_lesson.reports": "Звіти", - "addon.mod_lesson.response": "Відповідь", - "addon.mod_lesson.retakefinishedinsync": "Офлайн спроба була синхронізована. Ви хочете розглянути її?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "Переглянути", - "addon.mod_lesson.reviewlesson": "Переглянути урок", - "addon.mod_lesson.reviewquestionback": "Так, я хочу спробувати знов", - "addon.mod_lesson.reviewquestioncontinue": "Ні, я просто хочу перейти до наступного питання", - "addon.mod_lesson.secondpluswrong": "Недостатньо. Ви хочете спробувати знову?", - "addon.mod_lesson.submit": "Відправити", - "addon.mod_lesson.teacherjumpwarning": "Перехід {{$a.cluster}} або перехід {{$a.unseen}} повинен використовуватися в цьому уроці. Натомість буде використаний перехід на наступну сторінку. Увійдіть під ім'ям якого-небудь студента, щоб протестувати ці переходи.", - "addon.mod_lesson.teacherongoingwarning": "Поточна оцінка показується лише студенту. Зайдіть в систему як студент, щоб перевірити поточні оцінки", - "addon.mod_lesson.teachertimerwarning": "Таймер працює лише для студентів. Перевірте таймер, зайшовши в систему як студент.", - "addon.mod_lesson.thatsthecorrectanswer": "Це правильна відповідь", - "addon.mod_lesson.thatsthewronganswer": "Це неправильна відповідь", - "addon.mod_lesson.timeremaining": "Залишилося", - "addon.mod_lesson.timetaken": "Витрачений час", - "addon.mod_lesson.unseenpageinbranch": "Не переглянута сторінка гілки", - "addon.mod_lesson.warningretakefinished": "Спроба була закінчена на сайті.", - "addon.mod_lesson.welldone": "Відмінно!", - "addon.mod_lesson.youhaveseen": "Ви вже працювали з цим уроком.
                      Хочете продовжити з того місця, на якому Ви зупинились?", - "addon.mod_lesson.youranswer": "Ваша відповідь", - "addon.mod_lesson.yourcurrentgradeisoutof": "Ваша оцінка {{$a.grade}} з {{$a.total}}", - "addon.mod_lesson.youshouldview": "Вам потрібно відповісти хоча б на: {{$a}}", - "addon.mod_lti.errorgetlti": "Помилка при отриманні даних модуля.", - "addon.mod_lti.errorinvalidlaunchurl": "Цей URL не є дійсним.", - "addon.mod_lti.launchactivity": "Запуск діяльності", - "addon.mod_lti.modulenameplural": "Зовнішні засоби", - "addon.mod_page.errorwhileloadingthepage": "Помилка при завантаженні вмісту сторінки.", - "addon.mod_page.modulenameplural": "Сторінки", - "addon.mod_quiz.answercolon": "Відповідь:", - "addon.mod_quiz.attemptfirst": "Перша спроба", - "addon.mod_quiz.attemptlast": "Остання спроба", - "addon.mod_quiz.attemptnumber": "Спроба", - "addon.mod_quiz.attemptquiznow": "Почати тестування", - "addon.mod_quiz.attemptstate": "Стан", - "addon.mod_quiz.cannotsubmitquizdueto": "Ця спроба тесту не може бути представлена з наступних причин:", - "addon.mod_quiz.comment": "Коментар", - "addon.mod_quiz.completedon": "Завершено", - "addon.mod_quiz.confirmclose": "Ви вибрали закінчення тесту (даної спроби). Якщо ви це підтверджуєте, Ви більше не зможете змінювати ваші відповіді.", - "addon.mod_quiz.confirmcontinueoffline": "Ця спроба не була синхронізована, {{$a}}. Якщо ви продовжили цю спробу на іншому пристрої, ви можете втратити дані.", - "addon.mod_quiz.confirmleavequizonerror": "При збереженні відповідей сталася помилка. Ви впевнені, що хочете залишити тест?", - "addon.mod_quiz.confirmstart": "Цей тест має обмеження в часі {{$a}}. Час почне відраховуватися з моменту, коли ви починаєте вашу спробу, і ви повинні будете закінчити вашу спробу допоки не спливе термін. Ви впевнені, що хочете почати зараз?", - "addon.mod_quiz.confirmstartheader": "Тест з обмеженням в часі", - "addon.mod_quiz.connectionerror": "Мережеве підключення втрачено. (Автозбереження не вдалося).\n\nЗапишіть відповіді введені на цій сторінці за останні кілька хвилин, потім спробуйте знову підключити.\n\nПісля того, як з'єднання буде відновлено, ваші відповіді мають бути збережені і це повідомлення зникне.", - "addon.mod_quiz.continueattemptquiz": "Продовжити останню спробу", - "addon.mod_quiz.continuepreview": "Продовжити останній перегляд", - "addon.mod_quiz.errorbehaviournotsupported": "Цей тест не може бути зроблений в додатку, тому що не підтримується додатком:", - "addon.mod_quiz.errordownloading": "Помилка при завантаженні потрібних даних.", - "addon.mod_quiz.errorgetattempt": "Помилка при отриманні даних спроби.", - "addon.mod_quiz.errorgetquestions": "Помилка отримання питань.", - "addon.mod_quiz.errorgetquiz": "Помилка при отриманні даних тесту.", - "addon.mod_quiz.errorparsequestions": "Сталася помилка під час читання питання. Будь ласка, спробуйте цей тест в веб-браузері.", - "addon.mod_quiz.errorquestionsnotsupported": "Цей тест не може бути вирішених в додатку, оскільки він може містити питання, які не підтримуються додатком:", - "addon.mod_quiz.errorrulesnotsupported": "Цей тест не може бути зроблений в додатку, оскільки він має правила доступу, що не підтримується додатком:", - "addon.mod_quiz.errorsaveattempt": "При збереженні даних спроби сталася помилка.", - "addon.mod_quiz.feedback": "Відгук", - "addon.mod_quiz.finishattemptdots": "Завершити спробу...", - "addon.mod_quiz.finishnotsynced": "Готово, але не синхронізовано", - "addon.mod_quiz.grade": "Оцінка", - "addon.mod_quiz.gradeaverage": "Середня оцінка", - "addon.mod_quiz.gradehighest": "Краща оцінка", - "addon.mod_quiz.grademethod": "Метод оцінювання", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}.", - "addon.mod_quiz.marks": "Балів", - "addon.mod_quiz.modulenameplural": "Тести", - "addon.mod_quiz.mustbesubmittedby": "Ця спроба має бути відправлена до {{$a}}.", - "addon.mod_quiz.noquestions": "Жодного питання не було додано", - "addon.mod_quiz.noreviewattempt": "Вам не дозволено переглядати цю спробу.", - "addon.mod_quiz.notyetgraded": "Ще не оцінено", - "addon.mod_quiz.opentoc": "Відкрити навігаційне повідомлення.", - "addon.mod_quiz.outof": "{{$a.grade}} з {{$a.maxgrade}} можливих", - "addon.mod_quiz.outofpercent": "{{$a.grade}} з можливих {{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Розширений відгук", - "addon.mod_quiz.overdue": "Прострочено", - "addon.mod_quiz.overduemustbesubmittedby": "Ця спроба в даний час прострочена. Вона повинна вже були відправлена. Якщо Ви хочете, щоб цей тест було оцінено, Ви повинні відправити його до {{$a}}. Якщо ви не відправите його до вказаного часу, оцінки за цю спробу не будуть зараховані.", - "addon.mod_quiz.preview": "Перегляд", - "addon.mod_quiz.previewquiznow": "Переглянути тест зараз", - "addon.mod_quiz.question": "Питання", - "addon.mod_quiz.quiznavigation": "Перехід по тесту", - "addon.mod_quiz.quizpassword": "Кодове слово тесту", - "addon.mod_quiz.reattemptquiz": "Зробити наступну спробу", - "addon.mod_quiz.requirepasswordmessage": "Щоб почати спробу тесту, ви повинні знати кодове слово тесту", - "addon.mod_quiz.returnattempt": "Повернутися до спроби", - "addon.mod_quiz.review": "Огляд", - "addon.mod_quiz.reviewofattempt": "Огляд спроби {{$a}}", - "addon.mod_quiz.reviewofpreview": "Огляд попереднього перегляду", - "addon.mod_quiz.showall": "Показати всі питання на одній сторінці", - "addon.mod_quiz.showeachpage": "Показати одну сторінку за раз", - "addon.mod_quiz.startattempt": "Почати спробу", - "addon.mod_quiz.startedon": "Розпочато", - "addon.mod_quiz.stateabandoned": "Не здано", - "addon.mod_quiz.statefinished": "Завершено", - "addon.mod_quiz.statefinisheddetails": "Здано {{$a}}", - "addon.mod_quiz.stateinprogress": "В процесі", - "addon.mod_quiz.stateoverdue": "Прострочено", - "addon.mod_quiz.stateoverduedetails": "Повинно бути надіслано {{$a}}", - "addon.mod_quiz.status": "Статус", - "addon.mod_quiz.submitallandfinish": "Відправити все та завершити", - "addon.mod_quiz.summaryofattempt": "Результати спроби", - "addon.mod_quiz.summaryofattempts": "Результати ваших попередніх спроб", - "addon.mod_quiz.timeleft": "Залишилося часу", - "addon.mod_quiz.timetaken": "Витрачено часу", - "addon.mod_quiz.warningattemptfinished": "Offline спроби були відкинуті, так як вони були закінчені на сайті або не знайдені.", - "addon.mod_quiz.warningdatadiscarded": "Деякі відповіді форуму були відкинуті, бо питання були змінені в Інтернеті.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Спроба незакінчена, тому що деякі відповіді форуму були відкинуті. Будь ласка, ознайомтеся з Вашими відповідями і спробуйте ще раз.", - "addon.mod_quiz.yourfinalgradeis": "Ваша підсумкова оцінка за цей тест: {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "Помилка при завантаженні вмісту.", - "addon.mod_resource.modifieddate": "Модифіковано {{$a}}", - "addon.mod_resource.modulenameplural": "Файли", - "addon.mod_resource.openthefile": "Відкрити файл", - "addon.mod_resource.uploadeddate": "Завантажено {{$a}}", - "addon.mod_scorm.asset": "Актив", - "addon.mod_scorm.assetlaunched": "Актив - Переглянуто", - "addon.mod_scorm.attempts": "Спроби", - "addon.mod_scorm.averageattempt": "Середнє по спробах", - "addon.mod_scorm.browse": "Перегляд", - "addon.mod_scorm.browsed": "Переглянуто", - "addon.mod_scorm.browsemode": "Режим перегляду", - "addon.mod_scorm.cannotcalculategrade": "Оцінка не може бути обчислена.", - "addon.mod_scorm.completed": "Завершено", - "addon.mod_scorm.contents": "Зміст", - "addon.mod_scorm.dataattemptshown": "Ці дані належать до спроби № {{number}}.", - "addon.mod_scorm.enter": "Ввести", - "addon.mod_scorm.errorcreateofflineattempt": "При створенні нового форуму сталася помилка. Будь ласка спробуйте ще раз.", - "addon.mod_scorm.errordownloadscorm": "Помилка завантаження SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "Помилка отримання SCORM-даних.", - "addon.mod_scorm.errorinvalidversion": "На жаль, додаток підтримує тільки SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "Завантаження SCORM пакетів відключене у вашому сайті Moodle. Будь ласка, зверніться до адміністратора сайту Moodle.", - "addon.mod_scorm.errornovalidsco": "Це SCORM не має доступних SCO для завантаження.", - "addon.mod_scorm.errorpackagefile": "На жаль, додаток підтримує тільки ZIP пакети.", - "addon.mod_scorm.errorsyncscorm": "Під час синхронізації сталася помилка. Будь ласка спробуйте ще раз.", - "addon.mod_scorm.exceededmaxattempts": "Ви досягли максимальної кількості спроб.", - "addon.mod_scorm.failed": "Невдалі", - "addon.mod_scorm.firstattempt": "Перша спроба", - "addon.mod_scorm.gradeaverage": "Середня оцінка", - "addon.mod_scorm.gradeforattempt": "Оцінка спроби", - "addon.mod_scorm.gradehighest": "Краща оцінка", - "addon.mod_scorm.grademethod": "Метод оцінювання", - "addon.mod_scorm.gradereported": "Звіт оцінювання", - "addon.mod_scorm.gradescoes": "Об’єкти навчання", - "addon.mod_scorm.gradesum": "Сумарна оцінка", - "addon.mod_scorm.highestattempt": "Найкраща спроба", - "addon.mod_scorm.incomplete": "Не завершено", - "addon.mod_scorm.lastattempt": "Остання завершена спроба", - "addon.mod_scorm.modulenameplural": "SCORM пакети", - "addon.mod_scorm.newattempt": "Почати нову спробу", - "addon.mod_scorm.noattemptsallowed": "Кількість дозволених спроб", - "addon.mod_scorm.noattemptsmade": "Кількість спроб, які ви повінні зробити", - "addon.mod_scorm.notattempted": "Не було спроб", - "addon.mod_scorm.offlineattemptnote": "Ця спроба має дані, що не були синхронізовані.", - "addon.mod_scorm.offlineattemptovermax": "Ця спроба не може бути відправлена, так як ви перевищили максимальну кількість спроб.", - "addon.mod_scorm.organizations": "Організації", - "addon.mod_scorm.passed": "Пропущено", - "addon.mod_scorm.reviewmode": "Режим перегляду", - "addon.mod_scorm.score": "Оцінка", - "addon.mod_scorm.scormstatusnotdownloaded": "Цей SCORM не завантажений. Він буде автоматично завантажуватися при його відкритті.", - "addon.mod_scorm.scormstatusoutdated": "Цей SCORM був змінений з моменту останнього завантаження. Він буде автоматично завантажуватися при її відкритті.", - "addon.mod_scorm.suspended": "Заблокований", - "addon.mod_scorm.toc": "Зміст", - "addon.mod_scorm.warningofflinedatadeleted": "Деякі автономні дані про спробу № {{number}} були видалені, тому що вони не можуть бути створені в новій спробі.", - "addon.mod_scorm.warningsynconlineincomplete": "Деякі спроби не можуть бути синхронізовані з сайтом, тому що онлайн спроба не закінчена. Будь ласка закінчіть спробу онлайн спершу.", - "addon.mod_survey.cannotsubmitsurvey": "На жаль, існує проблема вашого представлення. Будь ласка спробуйте ще раз.", - "addon.mod_survey.errorgetsurvey": "Помилка при отриманні даних.", - "addon.mod_survey.ifoundthat": "Я довідався, що", - "addon.mod_survey.ipreferthat": "Я волію, щоб", - "addon.mod_survey.modulenameplural": "Обстеження", - "addon.mod_survey.responses": "Відповіді", - "addon.mod_survey.results": "Результати", - "addon.mod_survey.surveycompletednograph": "Ви завершили цю анкету.", - "addon.mod_url.accessurl": "Доступ до URL", - "addon.mod_url.modulenameplural": "URLs (веб-посилання)", - "addon.mod_url.pointingtourl": "Точки URL цього ресурсу", - "addon.mod_wiki.cannoteditpage": "Ви не можете редагувати цю сторінку", - "addon.mod_wiki.createpage": "Створити сторінку", - "addon.mod_wiki.editingpage": "Редагувати сторінку '{{$a}}", - "addon.mod_wiki.errorloadingpage": "Сталася помилка під час завантаження сторінки.", - "addon.mod_wiki.errornowikiavailable": "Ця вікі ще не має ніякого змісту.", - "addon.mod_wiki.gowikihome": "Перейти на Вікі головну", - "addon.mod_wiki.map": "Мапа", - "addon.mod_wiki.modulenameplural": "Вікі", - "addon.mod_wiki.newpagehdr": "Нова сторінка", - "addon.mod_wiki.newpagetitle": "Заголовок нової сторінки", - "addon.mod_wiki.nocontent": "Немає контенту для цієї сторінки", - "addon.mod_wiki.notingroup": "Не в групі", - "addon.mod_wiki.pageexists": "Ця сторінка вже існує. Перенаправити до неї.", - "addon.mod_wiki.pagename": "Назва сторінки", - "addon.mod_wiki.subwiki": "Субвікі", - "addon.mod_wiki.tagarea_wiki_pages": "Вікі сторінки", - "addon.mod_wiki.titleshouldnotbeempty": "Заголовок не може бути порожнім", - "addon.mod_wiki.viewpage": "Переглянути сторінку", - "addon.mod_wiki.wikipage": "Вікі-сторінка", - "addon.mod_wiki.wrongversionlock": "Інший користувач змінив сторінку, тож версія, яку ви редагуєте - вже застаріла.", - "addon.mod_workshop.alreadygraded": "Вже оцінено", - "addon.mod_workshop.areainstructauthors": "Інструкція до роботи", - "addon.mod_workshop.areainstructreviewers": "Інструкція до оцінювання", - "addon.mod_workshop.assess": "Оцінити", - "addon.mod_workshop.assessedsubmission": "Оцінена робота", - "addon.mod_workshop.assessmentform": "Форма оцінки", - "addon.mod_workshop.assessmentsettings": "Параметри оцінки", - "addon.mod_workshop.assessmentweight": "Вага оцінки", - "addon.mod_workshop.assignedassessments": "Роботи надані для оцінювання", - "addon.mod_workshop.assignedassessmentsnone": "У вас немає поданих до оцінювання робіт", - "addon.mod_workshop.conclusion": "Висновок", - "addon.mod_workshop.createsubmission": "Надіслати", - "addon.mod_workshop.deletesubmission": "Вилучити відповідь", - "addon.mod_workshop.editsubmission": "Редагувати роботу", - "addon.mod_workshop.feedbackauthor": "Відгук для автора", - "addon.mod_workshop.feedbackby": "Відгук на {{$a}}", - "addon.mod_workshop.feedbackreviewer": "Відгук для рецензента", - "addon.mod_workshop.givengrades": "Надані оцінки", - "addon.mod_workshop.gradecalculated": "Розрахунок оцінки за роботу", - "addon.mod_workshop.gradeinfo": "Оцінка: {{$a.received}} з {{$a.max}}", - "addon.mod_workshop.gradeover": "Перевизначення балів за роботу", - "addon.mod_workshop.gradesreport": "Звіт з оцінювання семінарів", - "addon.mod_workshop.gradinggrade": "Бали за оцінювання", - "addon.mod_workshop.gradinggradecalculated": "Розрахунок балів за оцінювання", - "addon.mod_workshop.gradinggradeof": "Бали за оцінювання (з {{$a}})", - "addon.mod_workshop.gradinggradeover": "Перевизначення балів за оцінювання", - "addon.mod_workshop.modulenameplural": "Семінари", - "addon.mod_workshop.nogradeyet": "Ще не оцінено", - "addon.mod_workshop.notassessed": "Ще не оцінювалося", - "addon.mod_workshop.notoverridden": "Не перевизначено", - "addon.mod_workshop.noyoursubmission": "Ви ще не відправляли вашу роботу", - "addon.mod_workshop.overallfeedback": "Загальний відгук", - "addon.mod_workshop.publishedsubmissions": "Опубліковані роботи", - "addon.mod_workshop.publishsubmission": "Публікувати роботу", - "addon.mod_workshop.publishsubmission_help": "Опубліковані роботи доступні іншим після закінчення семінару.", - "addon.mod_workshop.reassess": "Переоцінити", - "addon.mod_workshop.receivedgrades": "Отримані оцінки", - "addon.mod_workshop.submissionattachment": "Прикріплення", - "addon.mod_workshop.submissioncontent": "Відправлення вмісту", - "addon.mod_workshop.submissiondeleteconfirm": "Ви точно хочете видалити цю відповідь?", - "addon.mod_workshop.submissiongrade": "Оцінка за роботу", - "addon.mod_workshop.submissiongradeof": "Оцінка за роботу (від {{$a}})", - "addon.mod_workshop.submissionsreport": "Звіт по роботах семінару", - "addon.mod_workshop.submissiontitle": "Заголовок", - "addon.mod_workshop.userplan": "План семінару", - "addon.mod_workshop.userplancurrentphase": "Поточний етап", - "addon.mod_workshop.weightinfo": "Вага: {{$a}}", - "addon.mod_workshop.yourassessment": "Самооцінка", - "addon.mod_workshop.yourassessmentfor": "Ваша оцінка для {{$a}}", - "addon.mod_workshop.yourgrades": "Ваші оцінки", - "addon.mod_workshop.yoursubmission": "Ваша робота", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "Аспект {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "Ви повинні вибрати оцінку для цього ракурсу", - "addon.mod_workshop_assessment_comments.dimensionnumber": "Аспект {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "Твердження {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "Критерій {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "Ви повинні вибрати один із елементів", - "addon.notes.addnewnote": "Додати нову нотатку", - "addon.notes.coursenotes": "Нотатки курсу", - "addon.notes.deleteconfirm": "Видалити цю нотатку?", - "addon.notes.eventnotecreated": "Створено нотатку", - "addon.notes.eventnotedeleted": "Вилучено нотатку", - "addon.notes.nonotes": "Поки що немає нотаток цього типу", - "addon.notes.note": "Нотатка", - "addon.notes.notes": "Нотатки", - "addon.notes.personalnotes": "Особисті нотатки", - "addon.notes.publishstate": "Контекст", - "addon.notes.sitenotes": "Нотатки сайту", - "addon.notes.userwithid": "Користувач з Id {{id}}", - "addon.notes.warningnotenotsent": "Неможливо додати записку до курсу {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "Помилка отримання сповіщень", - "addon.notifications.markallread": "Позначити все як прочитане", - "addon.notifications.notifications": "Сповіщення", - "addon.notifications.playsound": "Грати звук", - "addon.notifications.therearentnotificationsyet": "Немає сповіщень", - "assets.countries.AD": "Андорра", - "assets.countries.AE": "Об'єднані Арабські Емірати", - "assets.countries.AF": "Афганістан", - "assets.countries.AG": "Антиґуа і Барбуда", - "assets.countries.AI": "Анґілья", - "assets.countries.AL": "Албанія", - "assets.countries.AM": "Вірменія", - "assets.countries.AO": "Анґола", - "assets.countries.AQ": "Антарктика", - "assets.countries.AR": "Аргентина", - "assets.countries.AS": "Американське Самоа", - "assets.countries.AT": "Австрія", - "assets.countries.AU": "Австралія", - "assets.countries.AW": "Аруба", - "assets.countries.AX": "Аландські острови", - "assets.countries.AZ": "Азербайджан", - "assets.countries.BA": "Боснія і Герцеґовина", - "assets.countries.BB": "Барбадос", - "assets.countries.BD": "Банґладеш", - "assets.countries.BE": "Бельгія", - "assets.countries.BF": "Буркіна-Фасо", - "assets.countries.BG": "Болгарія", - "assets.countries.BH": "Бахрейн", - "assets.countries.BI": "Бурунді", - "assets.countries.BJ": "Бенін", - "assets.countries.BL": "СантБарталам'ю", - "assets.countries.BM": "Бермуди", - "assets.countries.BN": "Бруней", - "assets.countries.BO": "Болівія", - "assets.countries.BQ": "Бонайре, Синт-Естатіус і Саба", - "assets.countries.BR": "Бразилія", - "assets.countries.BS": "Багами", - "assets.countries.BT": "Бутан", - "assets.countries.BV": "о. Буве", - "assets.countries.BW": "Ботсвана", - "assets.countries.BY": "Бєларусь", - "assets.countries.BZ": "Беліз", - "assets.countries.CA": "Канада", - "assets.countries.CC": "Кокосові (Кілінґ) Острови", - "assets.countries.CD": "Конго, Демократична Республіка", - "assets.countries.CF": "Центральноафриканська Республіка", - "assets.countries.CG": "Конґо", - "assets.countries.CH": "Швейцарія", - "assets.countries.CI": "Кот-д'Івуар", - "assets.countries.CK": "о-ви Кука", - "assets.countries.CL": "Чилі", - "assets.countries.CM": "Камерун", - "assets.countries.CN": "Китай", - "assets.countries.CO": "Колумбія", - "assets.countries.CR": "Коста-Ріка", - "assets.countries.CU": "Куба", - "assets.countries.CV": "Кабо-Верде", - "assets.countries.CW": "Кюрасао", - "assets.countries.CX": "о. Різдва", - "assets.countries.CY": "Кіпр", - "assets.countries.CZ": "Чехія", - "assets.countries.DE": "Німеччина", - "assets.countries.DJ": "Джібуті", - "assets.countries.DK": "Данія", - "assets.countries.DM": "Домініка", - "assets.countries.DO": "Домініканська Республіка", - "assets.countries.DZ": "Алжир", - "assets.countries.EC": "Еквадор", - "assets.countries.EE": "Естонія", - "assets.countries.EG": "Єгипет", - "assets.countries.EH": "Західна Сахара", - "assets.countries.ER": "Еритрея", - "assets.countries.ES": "Іспанія", - "assets.countries.ET": "Ефіопія", - "assets.countries.FI": "Фінляндія", - "assets.countries.FJ": "Фіджі", - "assets.countries.FK": "Фолклендські (Мальвінські) Острови", - "assets.countries.FM": "Федеративні Штати Мікронезії", - "assets.countries.FO": "Фарерські Острови", - "assets.countries.FR": "Франція", - "assets.countries.GA": "Ґабон", - "assets.countries.GB": "Велика Британія", - "assets.countries.GD": "Ґренада", - "assets.countries.GE": "Грузія", - "assets.countries.GF": "Французька Гвіана", - "assets.countries.GG": "Гернсей", - "assets.countries.GH": "Гана", - "assets.countries.GI": "Ґібралтар", - "assets.countries.GL": "Ґренландія", - "assets.countries.GM": "Ґамбія", - "assets.countries.GN": "Ґвінея", - "assets.countries.GP": "Ґваделупа", - "assets.countries.GQ": "Екваторіальна Ґвінея", - "assets.countries.GR": "Греція", - "assets.countries.GS": "Південна Джорджія", - "assets.countries.GT": "Ґватемала", - "assets.countries.GU": "Ґуам", - "assets.countries.GW": "Ґвінея-Бісау", - "assets.countries.GY": "Ґайана", - "assets.countries.HK": "Ґонконг (Сянґон)", - "assets.countries.HM": "о-ви Герд і Макдональд", - "assets.countries.HN": "Гондурас", - "assets.countries.HR": "Хорватія", - "assets.countries.HT": "Гаїті", - "assets.countries.HU": "Угорщина", - "assets.countries.ID": "Індонезія", - "assets.countries.IE": "Ірландія", - "assets.countries.IL": "Ізраїль", - "assets.countries.IM": "Острів Мен", - "assets.countries.IN": "Індія", - "assets.countries.IO": "Британська територія в Індійському океані", - "assets.countries.IQ": "Ірак", - "assets.countries.IR": "Іран", - "assets.countries.IS": "Ісландія", - "assets.countries.IT": "Італія", - "assets.countries.JE": "Джерсей", - "assets.countries.JM": "Ямайка", - "assets.countries.JO": "Йорданія", - "assets.countries.JP": "Японія", - "assets.countries.KE": "Кенія", - "assets.countries.KG": "Киргизстан", - "assets.countries.KH": "Камбоджа", - "assets.countries.KI": "Кірибаті", - "assets.countries.KM": "Комори", - "assets.countries.KN": "Сент-Кітс і Невіс", - "assets.countries.KP": "Корея Північна", - "assets.countries.KR": "Корея Південна", - "assets.countries.KW": "Кувейт", - "assets.countries.KY": "Кайманові Острови", - "assets.countries.KZ": "Казахстан", - "assets.countries.LA": "Лаос", - "assets.countries.LB": "Ліван", - "assets.countries.LC": "Сент-Люсія", - "assets.countries.LI": "Ліхтенштейн", - "assets.countries.LK": "Шрі-Ланка", - "assets.countries.LR": "Ліберія", - "assets.countries.LS": "Лесото", - "assets.countries.LT": "Литва", - "assets.countries.LU": "Люксембурґ", - "assets.countries.LV": "Латвія", - "assets.countries.LY": "Лівія", - "assets.countries.MA": "Марокко", - "assets.countries.MC": "Монако", - "assets.countries.MD": "Молдова", - "assets.countries.ME": "Чорногорія", - "assets.countries.MF": "СантМартін (Французька частина)", - "assets.countries.MG": "Мадаґаскар", - "assets.countries.MH": "Маршаллові Острови", - "assets.countries.MK": "Македонія", - "assets.countries.ML": "Малі", - "assets.countries.MM": "М'янма", - "assets.countries.MN": "Монголія", - "assets.countries.MO": "Макау", - "assets.countries.MP": "Північні Маріанські Острови", - "assets.countries.MQ": "Мартиніка", - "assets.countries.MR": "Мавританія", - "assets.countries.MS": "Монтсеррат", - "assets.countries.MT": "Мальта", - "assets.countries.MU": "Маврикій", - "assets.countries.MV": "Мальдіви", - "assets.countries.MW": "Малаві", - "assets.countries.MX": "Мексика", - "assets.countries.MY": "Малайзія", - "assets.countries.MZ": "Мозамбік", - "assets.countries.NA": "Намібія", - "assets.countries.NC": "Нова Каледонія", - "assets.countries.NE": "Ніґер", - "assets.countries.NF": "о. Норфолк", - "assets.countries.NG": "Ніґерія", - "assets.countries.NI": "Нікараґуа", - "assets.countries.NL": "Нідерланди", - "assets.countries.NO": "Норвегія", - "assets.countries.NP": "Непал", - "assets.countries.NR": "Науру", - "assets.countries.NU": "Ніуе", - "assets.countries.NZ": "Нова Зеландія", - "assets.countries.OM": "Оман", - "assets.countries.PA": "Панама", - "assets.countries.PE": "Перу", - "assets.countries.PF": "Французька Полінезія", - "assets.countries.PG": "Папуа Нова Ґвінея", - "assets.countries.PH": "Філіппіни", - "assets.countries.PK": "Пакистан", - "assets.countries.PL": "Польща", - "assets.countries.PM": "Сен-П'єр і Мікелон", - "assets.countries.PN": "Піткерн", - "assets.countries.PR": "Пуерто-Рико", - "assets.countries.PS": "Палестина", - "assets.countries.PT": "Португалія", - "assets.countries.PW": "Палау", - "assets.countries.PY": "Параґвай", - "assets.countries.QA": "Катар", - "assets.countries.RE": "Реюньйон", - "assets.countries.RO": "Румунія", - "assets.countries.RS": "Сербія", - "assets.countries.RU": "Росія", - "assets.countries.RW": "Руанда", - "assets.countries.SA": "Саудівська Аравія", - "assets.countries.SB": "Соломонові Острови", - "assets.countries.SC": "Сейшельські Острови", - "assets.countries.SD": "Судан", - "assets.countries.SE": "Швеція", - "assets.countries.SG": "Сінґапур", - "assets.countries.SH": "о. Св. Єлени", - "assets.countries.SI": "Словенія", - "assets.countries.SJ": "о-ви Ян-Маєн і Свалбард", - "assets.countries.SK": "Словаччина", - "assets.countries.SL": "Сьєрра-Леоне", - "assets.countries.SM": "Сан-Марино", - "assets.countries.SN": "Сенеґал", - "assets.countries.SO": "Сомалі", - "assets.countries.SR": "Суринам", - "assets.countries.SS": "Південний Судан", - "assets.countries.ST": "Сан-Томе і Принсіпі", - "assets.countries.SV": "Сальвадор", - "assets.countries.SX": "СантМартін (Датська частина)", - "assets.countries.SY": "Сирія", - "assets.countries.SZ": "Свазіленд", - "assets.countries.TC": "о-ви Теркс і Кайкос", - "assets.countries.TD": "Чад", - "assets.countries.TF": "Французькі Південнополярні території", - "assets.countries.TG": "Тоґо", - "assets.countries.TH": "Таїланд", - "assets.countries.TJ": "Таджикистан", - "assets.countries.TK": "Токелау", - "assets.countries.TL": "Тімор-Лесте", - "assets.countries.TM": "Туркменістан", - "assets.countries.TN": "Туніс", - "assets.countries.TO": "Тонґа", - "assets.countries.TR": "Туреччина", - "assets.countries.TT": "Тринідад і Тобаґо", - "assets.countries.TV": "Тувалу", - "assets.countries.TW": "Тайвань", - "assets.countries.TZ": "Танзанія", - "assets.countries.UA": "Україна", - "assets.countries.UG": "Уґанда", - "assets.countries.UM": "США віддалені острови", - "assets.countries.US": "Сполучені Штати Америки", - "assets.countries.UY": "Уруґвай", - "assets.countries.UZ": "Узбекистан", - "assets.countries.VA": "Ватикан", - "assets.countries.VC": "Сент-Вінсент і Ґренадини", - "assets.countries.VE": "Венесуела", - "assets.countries.VG": "Віргінські Острови (Брит.)", - "assets.countries.VI": "Віргінські Острови (США)", - "assets.countries.VN": "В'єтнам", - "assets.countries.VU": "Вануату", - "assets.countries.WF": "о-ви Волліс і Футуна", - "assets.countries.WS": "Самоа", - "assets.countries.YE": "Ємен", - "assets.countries.YT": "Майотта", - "assets.countries.ZA": "Південна Африка", - "assets.countries.ZM": "Замбія", - "assets.countries.ZW": "Зімбабве", - "assets.mimetypes.application/epub_zip": "Електронна книга EPUB", - "assets.mimetypes.application/msword": "Документ Word", - "assets.mimetypes.application/pdf": "Документ PDF", - "assets.mimetypes.application/vnd.moodle.backup": "Резервна копія Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Електронна таблиця Excel", - "assets.mimetypes.application/vnd.ms-powerpoint": "Презентація Powerpoint", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "Електронна таблиця OpenDocument", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "Шаблон електронної таблиці OpenDocument", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Презентація Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Слайдшою Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Електронна таблиця Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Шаблон Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Документ Word", - "assets.mimetypes.archive": "Архів ({{$a.EXT}})", - "assets.mimetypes.audio": "Аудіо файл ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Файл", - "assets.mimetypes.group:html_audio": "Аудіофайли, які підтримуються браузерами", - "assets.mimetypes.group:html_video": "Відеофайли, які підтримуються браузерами", - "assets.mimetypes.image": "Зображення ({{$a.MIMETYPE2}})", - "assets.mimetypes.text/html": "Документ HTML", - "assets.mimetypes.text/plain": "Текстовий файл", - "assets.mimetypes.text/rtf": "Документ RTF", - "core.accounts": "Облікові записи", - "core.add": "Додати", - "core.agelocationverification": "Перевірка віку та місцезнаходження", - "core.ago": "{{$a}} тому", - "core.all": "Вибрати все", - "core.allgroups": "Всі групи", - "core.allparticipants": "Усі учасники", - "core.answer": "Відповідь", - "core.answered": "Відповів", - "core.areyousure": "Ви впевнені?", - "core.back": "Назад", - "core.block.blocks": "Блоки", - "core.cancel": "Скасувати", - "core.cannotconnect": "Неможливо з'єднатися: Переконайтеся, що ви ввели правильний URL і що сайт використовує Moodle {{$a}} або більш пізню версію.", - "core.cannotdownloadfiles": "Завантаження файлів відключено у вашій мобільній службі. Будь ласка, зверніться до адміністратора сайту.", - "core.category": "Категорія", - "core.choose": "Вибрати", - "core.choosedots": "Вибрати...", - "core.clearsearch": "Очистити пошук", - "core.clicktohideshow": "Натисніть, щоб розгорнути або згорнути", - "core.clicktoseefull": "Натисніть, щоб побачити весь вміст.", - "core.close": "Закрити", - "core.comments": "Коментарі", - "core.comments.addcomment": "Додати коментар...", - "core.comments.comments": "Коментарі", - "core.comments.commentscount": "Коментарі ({{$a}})", - "core.comments.deletecommentbyon": "Видалити коментар, опублікований {{$a.user}} o {{$a.time}}", - "core.comments.eventcommentcreated": "Створено коментар", - "core.comments.eventcommentdeleted": "Вилучено коментар", - "core.comments.nocomments": "Немає коментарів", - "core.comments.savecomment": "Зберегти коментар", - "core.commentscount": "Коментарі ({{$a}})", - "core.completion-alt-auto-fail": "Виконано: {{$a}} (не вдалося досягти прохідного балу)", - "core.completion-alt-auto-n": "Не виконано: {{$a}}", - "core.completion-alt-auto-pass": "Виконано: {{$a}} (перевищено прохідний бал)", - "core.completion-alt-auto-y": "Виконано: {{$a}}", - "core.completion-alt-manual-n": "Не виконано: {{$a}}. Виберіть для позначення виконання", - "core.completion-alt-manual-y": "Виконано: {{$a}}. Виберіть для позначення не виконання", - "core.confirmcanceledit": "Ви впевнені що хочете залишити цю сторінку? Всі зміни будуть втрачені.", - "core.confirmdeletefile": "Ви впевнені, що хочете видалити цей файл?", - "core.confirmloss": "Ви впевнені? Всі зміни будуть втрачені.", - "core.confirmopeninbrowser": "Ви хочете відкрити в браузері?", - "core.content": "Контент", - "core.contenteditingsynced": "Ви редагуєте вміст який був синхронізований.", - "core.contentlinks.chooseaccount": "Виберіть аккаунт", - "core.contentlinks.chooseaccounttoopenlink": "Виберіть обліковий запис, щоб відкрити посилання.", - "core.contentlinks.confirmurlothersite": "Це посилання відноситься до іншого місця. Ви хочете відкрити?", - "core.contentlinks.errornoactions": "Не вдалося знайти дію, яка виконується за цим посиланням.", - "core.contentlinks.errornosites": "Не вдалося знайти сайт, щоб впоратися з цим посиланням.", - "core.continue": "Продовжити", - "core.copiedtoclipboard": "Текст скопійований", - "core.course": "Курс", - "core.course.activitydisabled": "Ваша організація відключила цю активність в мобільному додатку.", - "core.course.activitynotyetviewableremoteaddon": "Ваша організація встановила плагін, який ще не підтримується.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Установка версія Moodle вашої організації повинна бути оновлена.", - "core.course.allsections": "Всі секції", - "core.course.askadmintosupport": "Зверніться до адміністратора сайту і скажіть, що ви хочете використовувати цю діяльність з додатком Moodle Mobile.", - "core.course.confirmdeletemodulefiles": "Ви впевнені, що хочете видалити цей модуль?", - "core.course.confirmdownload": "Ви збираєтеся завантажити {{size}}. Ви впевнені, що хочете продовжити?", - "core.course.confirmdownloadunknownsize": "Ми не змогли розрахувати розмір файлу. Ви впевнені, що ви хочете завантажити?", - "core.course.confirmpartialdownloadsize": "Ви збираєтеся завантажити принаймні {{size}}. Ви впевнені, що хочете продовжити?", - "core.course.contents": "Контент", - "core.course.couldnotloadsectioncontent": "Не вдалося завантажити вміст розділу, будь ласка, спробуйте ще раз пізніше.", - "core.course.couldnotloadsections": "Не вдалося завантажити розділи, будь ласка, спробуйте ще раз пізніше.", - "core.course.coursesummary": "Анотація курсу", - "core.course.errordownloadingsection": "Помилка в завантаженні.", - "core.course.errorgetmodule": "Помилка при отриманні даних модуля.", - "core.course.hiddenfromstudents": "Сховане від студентів", - "core.course.hiddenoncoursepage": "Доступно, але не показано на сторінці курсу", - "core.course.nocontentavailable": "Немає контенту, доступного в даний момент.", - "core.course.overriddennotice": "Ваші фінальні бали з даної активності були відкореговані вручну.", - "core.course.sections": "Секції", - "core.course.useactivityonbrowser": "Ви все ще можете використовувати його за допомогою браузера пристрою.", - "core.coursedetails": "Деталі курсу", - "core.courses.allowguests": "Дозволено гостьовий доступ до курсу", - "core.courses.availablecourses": "Доступні курси", - "core.courses.cannotretrievemorecategories": "Категорії глибші, ніж рівень {{$a}} не можуть бути відновлені.", - "core.courses.categories": "Категорії курсів", - "core.courses.confirmselfenrol": "Ви впевнені, що хочете зареєструвати себе в цьому курсі?", - "core.courses.courses": "Курси", - "core.courses.enrolme": "Відрахувати мене", - "core.courses.errorloadcategories": "Сталася помилка під час завантаження категорій.", - "core.courses.errorloadcourses": "Сталася помилка під час завантаження курсів.", - "core.courses.errorsearching": "Під час пошуку сталася помилка.", - "core.courses.errorselfenrol": "Сталася помилка при самостійному зарахування.", - "core.courses.filtermycourses": "Відфільтрувати мої курси", - "core.courses.frontpage": "Головна сторінка", - "core.courses.ignore": "Ігнорувати", - "core.courses.mycourses": "Мої курси", - "core.courses.mymoodle": "Інформаційна сторінка", - "core.courses.nocourses": "Немає інформації курсу для показу.", - "core.courses.nocoursesyet": "У цій категорії немає курсів", - "core.courses.nosearchresults": "Немає результатів", - "core.courses.notenroled": "Ви не зареєстровані як студент цього курсу", - "core.courses.notenrollable": "Ви не можете зареєструвати себе в цьому курсі.", - "core.courses.password": "Ключ подачі заявок", - "core.courses.paymentrequired": "Цей курс потребує оплати за вхід.", - "core.courses.paypalaccepted": "Налаштовувати варіанти зарахування PayPal", - "core.courses.reload": "Оновити", - "core.courses.search": "Знайти", - "core.courses.searchcourses": "Пошук курсів", - "core.courses.searchcoursesadvice": "Ви можете використовувати кнопку пошуку курсів, щоб отримати доступ в якості гостя або зареєструвати себе в курсах, які дозволяють це.", - "core.courses.selfenrolment": "Самостійна реєстрація", - "core.courses.sendpaymentbutton": "Оплатити з допомогою Paypal", - "core.courses.totalcoursesearchresults": "Всього курсів: {{$a}}", - "core.currentdevice": "Поточний пристрій", - "core.datastoredoffline": "Дані зберігаються в пристрої, оскільки не можуть бути надіслані. Вони будуть автоматично відправлені пізніше.", - "core.date": "Дата", - "core.day": "день", - "core.days": "днів", - "core.decsep": ",", - "core.defaultvalue": "За замовчуванням ({{$a}})", - "core.delete": "Видалити", - "core.deleting": "Видалення", - "core.description": "Опис", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.discard": "Скинути", - "core.dismiss": "Відхилити", - "core.done": "Зроблено", - "core.download": "Завантажити", - "core.downloading": "Завантаження", - "core.edit": "Редагувати ", - "core.editor.autosavesucceeded": "Чорнетку збережено", - "core.editor.bold": "Жирний", - "core.editor.clear": "Скасувати форматування", - "core.editor.h3": "Заголовок (великий)", - "core.editor.h4": "Заголовок (середній)", - "core.editor.h5": "Заголовок (малий)", - "core.editor.italic": "Курсив", - "core.editor.orderedlist": "Упорядкований список", - "core.editor.p": "Абзац", - "core.editor.strike": "Перекреслений", - "core.editor.textrecovered": "Чорновий варіант цього тексту автоматично відновлено.", - "core.editor.underline": "Підкреслений", - "core.editor.unorderedlist": "Невпорядкований список", - "core.emptysplit": "Ця сторінка буде виглядати порожньою, якщо ліва панель порожня або завантажується.", - "core.error": "Помилка", - "core.errorchangecompletion": "При зміні статусу завершення сталася помилка. Будь ласка спробуйте ще раз.", - "core.errordeletefile": "Помилка видалення файлу. Будь ласка спробуйте ще раз.", - "core.errordownloading": "Помилка завантаження файлу.", - "core.errordownloadingsomefiles": "Помилка завантаження модуля файлів. Деякі файли можуть бути відсутні.", - "core.errorfileexistssamename": "Файл з таким ім'ям існує.", - "core.errorinvalidform": "Форма містить невірні дані. Будь ласка, переконайтеся, що всі необхідні поля з даними вірні.", - "core.errorinvalidresponse": "Неправильна відповідь отримана. Будь ласка, зверніться до адміністратора сайту Moodle, якщо роботу не відновлено.", - "core.errorloadingcontent": "Помилка завантаження контенту.", - "core.erroropenfilenoapp": "Помилка відкриття файлу: немає програми, щоб відкрити цей тип файлу.", - "core.erroropenfilenoextension": "Помилка відкриття файлу: файл не має розширення.", - "core.erroropenpopup": "Ця діяльність намагається відкрити спливаюче вікно. Це не підтримується в цьому додатку.", - "core.errorrenamefile": "Помилка перейменування файлу. Будь ласка спробуйте ще раз.", - "core.errorsync": "Під час синхронізації сталася помилка. Будь ласка спробуйте ще раз.", - "core.errorsyncblocked": "{{$a}} не може бути синхронізований прямо зараз через навантаження на сервер. Будь-ласка спробуйте пізніше. Якщо питання залишається невирішеним, спробуйте перезапустити програму.", - "core.filename": "Ім’я файлу", - "core.filenameexist": "Файл вже існує: {{$a}}", - "core.filenotfound": "Файл не знайдено.", - "core.fileuploader.addfiletext": "Додати файл", - "core.fileuploader.audio": "Аудіо", - "core.fileuploader.camera": "Камера", - "core.fileuploader.confirmuploadfile": "Ви збираєтеся завантажити {{size}}. Ви впевнені, що хочете продовжити?", - "core.fileuploader.confirmuploadunknownsize": "Ми не змогли розрахувати розмір завантаження. Ви впевнені, що хочете продовжити?", - "core.fileuploader.errorcapturingaudio": "Помилка захоплення аудіо.", - "core.fileuploader.errorcapturingimage": "Помилка захоплення зображення.", - "core.fileuploader.errorcapturingvideo": "Помилка захоплення відео.", - "core.fileuploader.errorgettingimagealbum": "Помилка отримання зображення з альбома.", - "core.fileuploader.errormustbeonlinetoupload": "Ви повинні бути в мережі для завантаження файлів.", - "core.fileuploader.errornoapp": "У вас немає програми, встановленої для виконання цієї дії.", - "core.fileuploader.errorreadingfile": "Помилка читання файлу.", - "core.fileuploader.errorwhileuploading": "Помилка при завантаженні файлу.", - "core.fileuploader.file": "Файл", - "core.fileuploader.fileuploaded": "Файл був успішно завантажений.", - "core.fileuploader.invalidfiletype": "{{$a}} тип файлу не може бути прийнятим", - "core.fileuploader.maxbytesfile": "Файл {{$a.file}} завеликий. Максимум {{$a.size}}.", - "core.fileuploader.more": "Детальний перегляд...", - "core.fileuploader.photoalbums": "Фото альбом.", - "core.fileuploader.readingfile": "Читання файлу", - "core.fileuploader.selectafile": "Виберіть файл", - "core.fileuploader.uploadafile": "Завантажити файл", - "core.fileuploader.uploading": "Завантаження", - "core.fileuploader.uploadingperc": "Завантаження: {{$a}}%", - "core.fileuploader.video": "Відео", - "core.filter": "Фільтр", - "core.folder": "Тека", - "core.forcepasswordchangenotice": "Щоб продовжити, ви повинні змінити свій пароль.", - "core.fulllistofcourses": "Всі курси", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Середня оцінка", - "core.grades.badgrade": "Проставлена оцінка - недопустима", - "core.grades.contributiontocoursetotal": "Внесок у підсумок курсу", - "core.grades.feedback": "Відгук", - "core.grades.grade": "Оцінка", - "core.grades.gradeitem": "Елемент оцінювання", - "core.grades.grades": "Оцінки", - "core.grades.lettergrade": "Буквена оцінка", - "core.grades.nogradesreturned": "Немає оцінок", - "core.grades.nooutcome": "Без результату", - "core.grades.percentage": "Відсоток", - "core.grades.range": "Інтервал", - "core.grades.rank": "Порядок", - "core.grades.weight": "значимість", - "core.group": "Група", - "core.groupsseparate": "Окремі групи", - "core.groupsvisible": "Доступні групи", - "core.h5p.additionallicenseinfo": "Будь-яка додаткова інформація про ліцензію", - "core.h5p.author": "Автор", - "core.h5p.authorcomments": "Авторський коментар", - "core.h5p.authorcommentsdescription": "Коментарі для редактора змісту. (Цей текст не публікується як частина інформації про авторські права.)", - "core.h5p.authorname": "Ім'я автора", - "core.h5p.authorrole": "Роль автора", - "core.h5p.by": "від", - "core.h5p.cancellabel": "Завершити", - "core.h5p.ccattribution": "Ліцензія(CC BY)", - "core.h5p.ccattributionnc": "Ліцензія-Не комерційне (CC BY-NC)", - "core.h5p.ccattributionncnd": "Ліцензія-Не комерційне-Без похідних (CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "Ліцензія-Не комерційне-Поділитися подібним(CC BY-NC-SA)", - "core.h5p.ccattributionnd": "Ліцензія-Без похідних (CC BY-ND)", - "core.h5p.ccattributionsa": "Ліцензія - Поділитися подібним(CC BY-SA)", - "core.h5p.ccpdd": "Публічна ліцензія (CC0)", - "core.h5p.changedby": "Змінено від", - "core.h5p.changedescription": "Опис змін", - "core.h5p.changelog": "Лог змін", - "core.h5p.changeplaceholder": "Фото обрізано, текст змінено тощо.", - "core.h5p.close": "Закрити", - "core.h5p.confirmdialogbody": "Підтвердьте, що хочете продовжити. Цю дію не можна скасувати.", - "core.h5p.confirmdialogheader": "Підтведити дію", - "core.h5p.confirmlabel": "Підтвердження", - "core.h5p.connectionLost": "Зв'язок втрачений. Результати будуть збережені та надіслані, коли з'єднання буде відновлено.", - "core.h5p.connectionReestablished": "Зновлення налагоджено.", - "core.h5p.contentCopied": "Вміст копіюється в буфер обміну", - "core.h5p.contentchanged": "Цей вміст змінився з моменту останнього його використання.", - "core.h5p.contenttype": "Тип контенту", - "core.h5p.copyright": "Права користування", - "core.h5p.copyrightinfo": "Інформація про авторське право", - "core.h5p.copyrightstring": "Авторське право", - "core.h5p.copyrighttitle": "Перегляньте інформацію про авторські права на цей вміст.", - "core.h5p.date": "Дата", - "core.h5p.disablefullscreen": "Вимкнути повноекранний режим", - "core.h5p.download": "Завантажити", - "core.h5p.downloadtitle": "Завантажити цей контент як H5P файл.", - "core.h5p.editor": "Редактор", - "core.h5p.embed": "Вбудовано", - "core.h5p.embedtitle": "Перегляньте вбудований код цього вмісту.", - "core.h5p.fullscreen": "Повний екран", - "core.h5p.h5ptitle": "Відвідайте h5p.org, щоб переглянути більше вмісту.", - "core.h5p.hideadvanced": "Приховати розширене", - "core.h5p.license": "Ліцензія", - "core.h5p.licenseCC010": "CC0 1.0 Універсальна (CC0 1.0)\nПередання до Суспільного Надбання", - "core.h5p.licenseCC010U": "CC0 1.0 Універсальна", - "core.h5p.licenseCC10": "1.0 Генерована", - "core.h5p.licenseCC20": "2.0 Генерована", - "core.h5p.licenseCC25": "2.5 Генерована", - "core.h5p.licenseCC30": "3.0 Неповідомлено", - "core.h5p.licenseCC40": "4.0 Інтернаціональна", - "core.h5p.licenseGPL": "Загальна публічна ліцензія", - "core.h5p.licenseV1": "Версія 1", - "core.h5p.licenseV2": "Версія 2", - "core.h5p.licenseV3": "Версія 3", - "core.h5p.licensee": "Ліцензія", - "core.h5p.licenseextras": "Ліцензії", - "core.h5p.licenseversion": "Версія ліцензії", - "core.h5p.nocopyright": "Інформація про авторські права для цього вмісту недоступна.", - "core.h5p.offlineDialogBody": "Нам не вдалося надіслати інформацію про ваше виконання цього завдання. Перевірте підключення до Інтернету.", - "core.h5p.offlineDialogHeader": "Ваше зв’язок із сервером було втрачено", - "core.h5p.offlineDialogRetryButtonLabel": "Повторіть спробу зараз", - "core.h5p.offlineDialogRetryMessage": "Повторна спроба в :num....", - "core.h5p.offlineSuccessfulSubmit": "Результати успішно надіслані.", - "core.h5p.originator": "творець", - "core.h5p.pd": "Публічний домен", - "core.h5p.pddl": "Посвідчення публічних доменів та ліцензії", - "core.h5p.pdm": "Знак публічного домену (PDM)", - "core.h5p.resizescript": "Включіть цей скрипт на свій веб-сайт, якщо ви хочете динамічно знати розмір вбудованого вмісту:", - "core.h5p.resubmitScores": "Спроба подати збережені результати.", - "core.h5p.reuse": "Повторно", - "core.h5p.reuseContent": "Повторне використання", - "core.h5p.reuseDescription": "Повторне використання контенту", - "core.h5p.showadvanced": "Розширений показ", - "core.h5p.showless": "Показати менще", - "core.h5p.showmore": "Показати більше", - "core.h5p.size": "Розмір", - "core.h5p.source": "Джерело", - "core.h5p.startingover": "Ви почнете з початку.", - "core.h5p.sublevel": "Підрівень", - "core.h5p.thumbnail": "Ескіз", - "core.h5p.title": "Заголовок", - "core.h5p.undisclosed": "Нерозголошено", - "core.h5p.year": "Рік", - "core.h5p.years": "Рік (роки)", - "core.h5p.yearsfrom": "Роки (з)", - "core.h5p.yearsto": "Роки (до)", - "core.hasdatatosync": "{{$a}} має автономні дані які будуть синхронізовані.", - "core.help": "Допомога", - "core.hide": "Сховати", - "core.hour": "година", - "core.hours": "години", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Зображення", - "core.imageviewer": "Переглядач зображень", - "core.info": "Інформація", - "core.invalidformdata": "Неправильна форма даних", - "core.labelsep": ":", - "core.lastaccess": "Останній вхід на сайт", - "core.lastmodified": "Остання зміна", - "core.lastsync": "Остання синхронізація", - "core.layoutgrid": "Сітка", - "core.list": "Список", - "core.listsep": ";", - "core.loading": "Завантаження", - "core.loadmore": "Завантажити більше", - "core.location": "Розміщення", - "core.login.auth_email": "E-mail - ідентифікація", - "core.login.authenticating": "Аутентифікація", - "core.login.cancel": "Скасувати", - "core.login.changepassword": "Змінити пароль", - "core.login.confirmdeletesite": "Видалити сайт {{sitename}}?", - "core.login.connect": "З'єднано!", - "core.login.connecttomoodle": "Підключитись до Moodle", - "core.login.contactyouradministrator": "Зверніться до адміністратора сайту для подальшої допомоги.", - "core.login.contactyouradministratorissue": "Будь ласка, зверніться до адміністратора, щоб перевірити наступне питання: {{$a}}", - "core.login.createaccount": "Створити запис", - "core.login.createuserandpass": "Створити користувача для входу в систему", - "core.login.credentialsdescription": "Будь ласка, введіть Ваше ім'я користувача та пароль, щоб увійти в систему.", - "core.login.emailconfirmsent": "На зазначену Вами адресу електронної пошти ({{$a}}) було відправлено листа з інструкціями із завершення реєстрації. Якщо у Вас з'являться проблеми з реєстрацією, зв'яжіться з адміністратором сайту.", - "core.login.emailnotmatch": "Email не співпадають", - "core.login.erroraccesscontrolalloworigin": "Cross-Origin дзвінок був відхилений. Будь ласка, перевірте https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "Під час видалення цього сайту сталася помилка. Будь ласка спробуйте ще раз.", - "core.login.errorupdatesite": "При оновленні токена сайту сталася помилка.", - "core.login.firsttime": "Ви вперше на нашому сайті?", - "core.login.forcepasswordchangenotice": "Щоб продовжити, ви повинні змінити свій пароль.", - "core.login.forgotten": "Забули ім'я або пароль?", - "core.login.help": "Допомога", - "core.login.helpmelogin": "

                      Є багато тисяч сайтів Moodle по всьому світу. Ця програма може підключатися тільки до сайтів Moodle, які є з підтримкою мобільного доступу додатків.

                      Якщо ви не можете підключитися до вашого сайту Moodle, то вам необхідно звернутися до адміністратора Moodle в тому місці, де планується отримати доступ і попросіть їх прочитати http://docs.moodle.org/en/Mobile_app

                      Щоб перевірити додаток на Moodle демо сайті в ролі вчителя або студента в полі адреса сайту і натисніть кнопку Підключення .

                      ", - "core.login.instructions": "Інструкції", - "core.login.invalidaccount": "Будь ласка, перевірте ваші реєстраційні дані, або зверніться до адміністратора сайту, щоб перевірити конфігурацію сайту.", - "core.login.invaliddate": "Неправильна дата", - "core.login.invalidemail": "Неправильний формат для ел.пошти", - "core.login.invalidmoodleversion": "Невірна версія Moodle. Мінімальна версія {{$a}}.", - "core.login.invalidsite": "URL сайту недійсний.", - "core.login.invalidtime": "Невірний час", - "core.login.invalidurl": "Вказано неправильну URL адресу", - "core.login.invalidvaluemax": "Максимальне значення {{$a}}", - "core.login.invalidvaluemin": "Мінімальне значення {{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle Mobile Additional Features при перевірці повернуло несподівану відповідь, ви будете проходити перевірку автентичності з використанням стандартного мобільного сервісу.", - "core.login.loggedoutssodescription": "Ви повинні знову перевірити справжність. Вам необхідно увійти на сайт у вікні браузера.", - "core.login.login": "Вхід", - "core.login.loginbutton": "Вхід", - "core.login.logininsiterequired": "Вам необхідно увійти на сайт у вікні браузера.", - "core.login.loginsteps": "Привіт! Для повного доступу до курсів вам необхідно створити для себе новий обліковий запис на цьому веб-сайті. Для кожного окремого курсу також може бути потрібним \"кодове слово\", яке вам повинен повідомити викладач. Покрокова інструкція:
                      1. Заповніть новий обліковий запис (форму, що містить дані про вас).
                      2. На вашу електронну пошту буде відправлено листа.
                      3. Прочитайте лист і зверніться за посиланням, зазначеним у ньому.
                      4. Обліковий запис буде підтверджено й система вас ідентифікує.
                      5. Оберіть курс, для навчання.
                      6. Якщо для запису на курс потрібно знання \"кодового слова\", викладач повідомить його вам.
                      7. Отже, у вас з'явився повний доступ до курсу: з цього моменту ви будете користуватися своїм логіном (іменем користувача) та паролем, щоб потрапити на нього.
                      ", - "core.login.missingemail": "Не вказано адресу електронної пошти", - "core.login.missingfirstname": "Не вказано ім'я", - "core.login.missinglastname": "Не вказано прізвище", - "core.login.mobileservicesnotenabled": "Мобільні послуги не включені в вашому сайті. Будь ласка, зверніться до адміністратора вашого Moodle сайт, якщо ви вважаєте, що мобільний доступ повинен бути включений.", - "core.login.mustconfirm": "Підтвердіть обліковий запис", - "core.login.newaccount": "Новий обліковий запис", - "core.login.notloggedin": "Ви повинні увійти в систему.", - "core.login.password": "Пароль", - "core.login.passwordforgotten": "Забутий пароль", - "core.login.passwordforgotteninstructions2": "Щоб скинути пароль, введіть нижче ваше ім'я користувача (логін) або вашу адресу електронної пошти. Якщо ви є у базі даних, на вашу електронну пошту буде надіслано лист з інструкціями, як відновити доступ до системи.", - "core.login.passwordrequired": "Пароль необхідний", - "core.login.policyaccept": "Я розумію та погоджуюся", - "core.login.policyagree": "Ви повинні погодитися з цими правилами для використання цього сайту. Ви згодні?", - "core.login.policyagreement": "Угода Сайту", - "core.login.policyagreementclick": "Читати Угоду", - "core.login.potentialidps": "Ви зазвичай входите з іншої сторінки для того щоб попасти сюди?
                      Виберіть зі списку, звідки ви звичайно приходите:", - "core.login.profileinvaliddata": "Неправильне значення", - "core.login.recaptchachallengeimage": "виклик reCAPTCHA", - "core.login.reconnect": "Повторне з'єднання", - "core.login.reconnectdescription": "Ваш маркер аутентифікації недійсний або закінчився, ви повинні підключитися до сайту.", - "core.login.reconnectssodescription": "Ваш маркер аутентифікації недійсний або закінчився, ви повинні перепідключитися до сайту. Вам необхідно увійти на сайт у вікні браузера.", - "core.login.security_question": "Контрольне питання", - "core.login.selectacountry": "Країна", - "core.login.signupplugindisabled": "{{$a}} не доступно.", - "core.login.siteaddress": "Адреса сайту", - "core.login.siteinmaintenance": "Сайт в режимі обслуговування", - "core.login.sitepolicynotagreederror": "Політики сайту не погоджені", - "core.login.siteurl": "URL сайту", - "core.login.siteurlrequired": "URL сайту повинно бути схоже на http://www.yourmoodlesite.abc or https://www.yourmoodlesite.efg", - "core.login.startsignup": "Створити новий обліковий запис", - "core.login.stillcantconnect": "До сих пір не можете підключитися?", - "core.login.supplyinfo": "Більше інформації", - "core.login.username": "Ім’я входу", - "core.login.usernameoremail": "Введіть або ім'я користувача, або адресу електронної пошти", - "core.login.usernamerequired": "Ім'я користувача необхідне", - "core.login.usernotaddederror": "Користувач не доданий - помилка", - "core.login.visitchangepassword": "Ви хочете відвідати сайт, щоб змінити пароль?", - "core.login.webservicesnotenabled": "Веб-служба не включені в вашому сайті. Будь ласка, зверніться до адміністратора вашого Moodle сайту, якщо ви вважаєте, що мобільний доступ повинен бути включений.", - "core.lostconnection": "Ваш маркер аутентифікації недійсний або закінчився, вам доведеться підключитися до сайту.", - "core.mainmenu.changesite": "Змінити сайт", - "core.mainmenu.help": "Допомога", - "core.mainmenu.logout": "Вихід", - "core.mainmenu.website": "Веб-сайт", - "core.maxsizeandattachments": "Макс. обсяг для нових файлів: {{$a.size}}, макс. кількість прикріплених файлів: {{$a.attachments}}", - "core.min": "хв", - "core.mins": "хв", - "core.misc": "Різне", - "core.mod_assign": "Завдання", - "core.mod_assignment": "Завдання 2.2 (Відключено)", - "core.mod_book": "Книга", - "core.mod_chat": "Чат", - "core.mod_choice": "Вибір", - "core.mod_data": "База даних", - "core.mod_database": "База даних", - "core.mod_external-tool": "ЗНВ", - "core.mod_feedback": "Зворотний зв’язок", - "core.mod_file": "Файл", - "core.mod_folder": "Тека", - "core.mod_forum": "Форум", - "core.mod_glossary": "Глосарій", - "core.mod_ims": "IMS контент пакет", - "core.mod_imscp": "IMS контент пакет", - "core.mod_label": "Напис", - "core.mod_lesson": "Урок", - "core.mod_lti": "ЗНВ", - "core.mod_page": "Сторінка", - "core.mod_quiz": "Тест", - "core.mod_resource": "Файл", - "core.mod_scorm": "SCORM пакет", - "core.mod_survey": "Обстеження", - "core.mod_url": "URL (веб-посилання)", - "core.mod_wiki": "Вікі", - "core.mod_workshop": "Семінар", - "core.moduleintro": "Опис", - "core.more": "більше", - "core.mygroups": "Мої групи", - "core.name": "Назва", - "core.networkerrormsg": "Мережа не включена або не працює.", - "core.never": "Ніколи", - "core.next": "Далі", - "core.no": "Ні", - "core.nocomments": "Немає коментарів", - "core.nograde": "Без оцінки", - "core.none": "Не вибрано", - "core.nopasswordchangeforced": "Ви не можете продовжити без зміни пароля.", - "core.nopermissions": "Вибачте, але ваші поточні права не дозволяють вам цього робити ({{$a}})", - "core.noresults": "Без результатів", - "core.noselection": "Нічого не вибрано", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Цей профіль недоступний, бо користувач не зареєстрований на цьому курсі.", - "core.notice": "Помітити", - "core.notingroup": "Ви повинні бути у групі для даного перегляду.", - "core.notsent": "Не відправлено", - "core.now": "зараз", - "core.numwords": "{{$a}} слів", - "core.offline": "Поза мережею", - "core.ok": "OK", - "core.online": "В мережі", - "core.openfullimage": "Натисніть тут, щоб побачити зображення в повному розмірі", - "core.openinbrowser": "Відкрити у браузері", - "core.othergroups": "Інші групи", - "core.pagea": "Сторінка {{$a}}", - "core.paymentinstant": "Використайте кнопку нижче, для того щоб заплатити та зареєструватися протягом декількох хвилин!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Телефон", - "core.pictureof": "Фото {{$a}}", - "core.previous": "Назад", - "core.pulltorefresh": "Потягніть щоб оновити", - "core.question.answer": "Відповідь", - "core.question.answersaved": "Відповідь збережено", - "core.question.certainty": "Впевненість", - "core.question.complete": "Завершено", - "core.question.correct": "Правильно", - "core.question.errorattachmentsnotsupported": "Додаток не підтримує прикріплення файлів відповідей", - "core.question.errorinlinefilesnotsupported": "Додаток ще не підтримує редагування вбудованих файлів.", - "core.question.errorquestionnotsupported": "Цей тип питання не підтримується додатком: {{$a}}.", - "core.question.feedback": "Коментар", - "core.question.howtodraganddrop": "Натисніть, щоб вибрати і перемістить.", - "core.question.incorrect": "Неправильно", - "core.question.information": "Інформація", - "core.question.invalidanswer": "Неповна відповідь", - "core.question.notanswered": "Відповіді не було", - "core.question.notyetanswered": "Відповіді ще не було", - "core.question.partiallycorrect": "Частково правильно", - "core.question.questionmessage": "Питання {{$a}}: {{$b}}", - "core.question.questionno": "Питання {{$a}}", - "core.question.requiresgrading": "Потрібно оцінити", - "core.quotausage": "Ви в даний час використовували {$ a-> used}} вашого ліміту {$ a-> total}}.", - "core.rating.aggregateavg": "Середня оцінка", - "core.rating.aggregatecount": "Кількість оцінок", - "core.rating.aggregatemax": "Максимальна оцінка", - "core.rating.aggregatemin": "Мінімальна оцінка", - "core.rating.aggregatesum": "Сума оцінок", - "core.rating.noratings": "Немає оцінок", - "core.rating.rating": "Оцінка", - "core.rating.ratings": "Рейтинги", - "core.redirectingtosite": "Ви будете перенаправлені на сайт.", - "core.refresh": "Оновити", - "core.remove": "Видалити", - "core.required": "Необхідні", - "core.requireduserdatamissing": "Цей користувач не має деяких необхідних даних в профілі. Заповніть, будь ласка, ці дані у вашому профілі Moodle і спробуйте ще раз.
                      {{$a}}", - "core.resourcedisplayopen": "Відкрити", - "core.resources": "Ресурси", - "core.restore": "Відновлення", - "core.restricted": "Обмежений", - "core.retry": "Повторити", - "core.save": "Зберегти", - "core.savechanges": "Зберегти", - "core.search": "Знайти", - "core.searching": "Пошук", - "core.searchresults": "Результати пошуку", - "core.sec": "сек", - "core.secs": "сек", - "core.seemoredetail": "Деталі...", - "core.selectacategory": "Виберіть категорію", - "core.selectacourse": "Виберіть курс", - "core.selectagroup": "Створити групу", - "core.sending": "Відсилання", - "core.serverconnection": "Помилка з’єднання з сервером", - "core.settings.about": "Про додаток", - "core.settings.cannotsyncoffline": "Неможливо синхронізувати в автономному режимі.", - "core.settings.cannotsyncwithoutwifi": "Неможливо синхронізувати, так як поточні налаштування тільки дозволяють синхронізуватись при підключенні до Wi-Fi. Підключіться до мережі Wi-Fi.", - "core.settings.cordovadevicemodel": "Модель пристрою Cordova", - "core.settings.cordovadeviceosversion": "Cordova Device OS version", - "core.settings.cordovadeviceplatform": "Cordova Device platform", - "core.settings.cordovadeviceuuid": "Cordova Device uuid", - "core.settings.cordovaversion": "Cordova version", - "core.settings.currentlanguage": "Обрана мова", - "core.settings.debugdisplay": "Показувати повідомлення налагодження", - "core.settings.deletesitefiles": "Ви впевнені, що хочете видалити завантажені файли з сайту '{{sitename}}'?", - "core.settings.deletesitefilestitle": "Видалити файли сайту", - "core.settings.deviceinfo": "Інформація про пристрій", - "core.settings.deviceos": "ОС пристрою", - "core.settings.disableall": "Тимчасово заборонити повідомлення", - "core.settings.disabled": "Відключено", - "core.settings.displayformat": "Формат відображення", - "core.settings.enabledownloadsection": "Увімкнути секцію завантаження", - "core.settings.enablerichtexteditor": "Включити редактор тексту", - "core.settings.enablerichtexteditordescription": "Якщо цей параметр включений, розширений текстовий редактор буде показаний в тих місцях, які дозволяють це.", - "core.settings.enablesyncwifi": "Тільки Wi-Fi синхронізація", - "core.settings.errordeletesitefiles": "Помилка видалення файлів сайту", - "core.settings.errorsyncsite": "Помилка синхронізації даних сайту, будь ласка, перевірте підключення до Інтернету і спробуйте ще раз.", - "core.settings.estimatedfreespace": "Розрахунковий вільний простір", - "core.settings.filesystemroot": "Коренева файлова система", - "core.settings.general": "Основне", - "core.settings.language": "Мова інтерфейсу", - "core.settings.license": "Ліцензія", - "core.settings.localnotifavailable": "Доступні локальні сповіщення", - "core.settings.locationhref": "Webview URL", - "core.settings.locked": "закрито", - "core.settings.loggedin": "В мережі", - "core.settings.loggedoff": "Поза мережею", - "core.settings.navigatorlanguage": "Мова навігації", - "core.settings.navigatoruseragent": "Навігаційний userAgent", - "core.settings.networkstatus": "Статус інтернет з'єднання", - "core.settings.preferences": "Уподобання", - "core.settings.privacypolicy": "Політика конфіденційності", - "core.settings.reportinbackground": "Відправляти помилки автоматично", - "core.settings.settings": "Налаштування", - "core.settings.sites": "Сайти", - "core.settings.spaceusage": "Використане місце", - "core.settings.synchronization": "Синхронізація", - "core.settings.synchronizenow": "Синхронізувати зараз", - "core.settings.syncsettings": "Налаштування синхронізації", - "core.settings.total": "Підсумок", - "core.settings.wificonnection": "WiFi з'єднання", - "core.sharedfiles.chooseaccountstorefile": "Виберіть обліковий запис, щоб зберегти файл", - "core.sharedfiles.chooseactionrepeatedfile": "Існує файл з таким ім'ям. Ви хочете замінити існуючий файл або перейменувати його в \"{{$a}}\"?", - "core.sharedfiles.errorreceivefilenosites": "Немає сайтів. Будь ласка, додайте сайт, перш ніж відправляє файл з додатком.", - "core.sharedfiles.nosharedfiles": "Немає загальних файлів, що зберігаються на цьому сайті.", - "core.sharedfiles.nosharedfilestoupload": "У вас немає файлів для завантаження. Якщо ви хочете завантажити файл з іншої програми, знайдіть цей файл і натисніть кнопку «Відкрити\".", - "core.sharedfiles.rename": "Перейменувати", - "core.sharedfiles.replace": "Перемістити", - "core.sharedfiles.sharedfiles": "Розшарити файли", - "core.sharedfiles.successstorefile": "Файл успішно збережений. Тепер ви можете вибрати цей файл, щоб завантажити його на ваші особисті файли або прикріпити його в деяких видах діяльності.", - "core.show": "Показати", - "core.showless": "Сховати додаткове...", - "core.showmore": "Показати додаткове...", - "core.site": "Сайт", - "core.sitehome.sitehome": "Головна сторінка", - "core.sitehome.sitenews": "Новини сайту", - "core.sitemaintenance": "Сайт зараз на технічному обслуговуванні і не доступний", - "core.sizeb": "байт", - "core.sizegb": "Гб", - "core.sizekb": "Кб", - "core.sizemb": "Мб", - "core.sizetb": "TB", - "core.skip": "Пропустити", - "core.sorry": "Вибачте...", - "core.sort": "Сортувати", - "core.sortby": "Сортувати за", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d.%m.%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedatetimeshort": "%d.%m.%y, %H:%M", - "core.strftimedaydate": "%A %d %B %Y", - "core.strftimedaydatetime": "%A %d %B %Y %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d %b %H:%M", - "core.strftimerecentfull": "%a %d %b %Y %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.submit": "Прийняти", - "core.success": "Успішно", - "core.tablet": "Планшет", - "core.tag.defautltagcoll": "Типова колекція", - "core.tag.inalltagcoll": "Скрізь", - "core.tag.itemstaggedwith": "Область {{$a.tagarea}} відмічена як \"{{$a.tag}}\"", - "core.tag.noresultsfor": "Немає результатів для \"{{$a}}\"", - "core.tag.notagsfound": "Для \"{{$a}}\" відповідних міток не знайдено", - "core.tag.searchtags": "Пошук міток", - "core.tag.showingfirsttags": "Показати {{$a}} найпопулярніших міток", - "core.tag.tag": "Мітка", - "core.tag.tagarea_course": "Курси", - "core.tag.tagarea_course_modules": "Діяльності та ресурси", - "core.tag.tagarea_post": "Записи блогу", - "core.tag.tagarea_user": "Інтереси користувачів", - "core.tag.tags": "Мітки", - "core.teachers": "Викладачі", - "core.thereisdatatosync": "Офлайн {{$a}} повинні бути синхронізовані.", - "core.thisdirection": "ltr", - "core.time": "Час", - "core.timesup": "Час вичерпано!", - "core.today": "Сьогодні", - "core.tryagain": "Спробувати ще раз", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.uhoh": "Опана...", - "core.unexpectederror": "Неочікувана помилка. Будь ласка, закрийте і знову відкрийте додаток, щоб спробувати ще раз", - "core.unicodenotsupported": "Деякі Emoji не підтримуються на цьому сайті. Такі символи будуть видалені, коли повідомлення буде відправлено.", - "core.unicodenotsupportedcleanerror": "Порожній текст був знайдений при чищенні Unicode символів.", - "core.unknown": "Невідомо", - "core.unlimited": "Не обмежено", - "core.unzipping": "Розпакування", - "core.upgraderunning": "Сайт оновлюється, повторіть спробу пізніше.", - "core.user": "Користувач", - "core.user.address": "Адреса", - "core.user.city": "Місто", - "core.user.contact": "Контакт", - "core.user.country": "Країна", - "core.user.description": "Опис", - "core.user.details": "Деталі", - "core.user.detailsnotavailable": "Деталі цього користувача вам не доступні", - "core.user.editingteacher": "Викладач", - "core.user.email": "Електронна пошта", - "core.user.emailagain": "Електронна пошта (повторно)", - "core.user.firstname": "Ім'я", - "core.user.interests": "Інтереси", - "core.user.lastname": "Прізвище", - "core.user.manager": "Менеджер", - "core.user.newpicture": "Новий малюнок", - "core.user.noparticipants": "Не знайдені учасники для цього курсу", - "core.user.participants": "Учасники", - "core.user.phone1": "Телефон", - "core.user.phone2": "Мобільний телефон", - "core.user.roles": "Ролі", - "core.user.sendemail": "Email", - "core.user.student": "Студент", - "core.user.teacher": "Асистент", - "core.user.webpage": "Веб-сторінка", - "core.userdeleted": "Реєстраційний запис користувача було вилучено", - "core.userdetails": "Детально", - "core.users": "Користувачі", - "core.view": "Перегляд", - "core.viewprofile": "Переглянути профіль", - "core.warningofflinedatadeleted": "Offline дані {{component}} '{{name}}' були видалені. {{error}}", - "core.whoops": "Упс!", - "core.whyisthishappening": "Чому це відбувається?", - "core.wsfunctionnotavailable": "Функція веб-сервіс не доступна.", - "core.year": "рік", - "core.years": "роки", - "core.yes": "Так" -} \ No newline at end of file diff --git a/src/assets/lang/vi.json b/src/assets/lang/vi.json deleted file mode 100644 index e91778f09..000000000 --- a/src/assets/lang/vi.json +++ /dev/null @@ -1,1139 +0,0 @@ -{ - "addon.badges.badgedetails": "Thông tin chi tiết huy hiệu", - "addon.badges.badges": "Các huy hiệu", - "addon.badges.contact": "Liên hệ", - "addon.badges.dateawarded": "Ngày phát hành", - "addon.badges.expired": "Đã hết hạn", - "addon.badges.expirydate": "Ngày tháng năm hết hạn", - "addon.badges.issuancedetails": "Mãn hạn huy hiệu", - "addon.badges.issuerdetails": "Thông tin người trao", - "addon.badges.issuername": "Tên người trao", - "addon.badges.issuerurl": "URL người trao", - "addon.badges.nobadges": "Không có huy hiệu.", - "addon.badges.recipientdetails": "Thông tin chi tiết người nhận", - "addon.badges.warnexpired": "(Huy hiệu này hết hạn!)", - "addon.block_activitymodules.pluginname": "Hoạt động", - "addon.block_badges.pluginname": "Huy hiệu mới nhất của tôi", - "addon.block_blogmenu.pluginname": "Menu blog", - "addon.block_blogrecent.pluginname": "Các mục blog gần đây", - "addon.block_blogtags.pluginname": "Các thẻ blog", - "addon.block_calendarmonth.pluginname": "Lịch", - "addon.block_calendarupcoming.pluginname": "Sự kiện sắp diễn ra", - "addon.block_comments.pluginname": "Bình luận", - "addon.block_completionstatus.pluginname": "Tình trạng hoàn thành khóa học", - "addon.block_glossaryrandom.pluginname": "Thuật ngữ ngẫu nhiên", - "addon.block_newsitems.pluginname": "Tin mới nhất", - "addon.block_onlineusers.pluginname": "Thành viên trực tuyến", - "addon.block_privatefiles.pluginname": "Tệp riêng tư của tôi", - "addon.block_recentactivity.pluginname": "Hoạt động gần đây", - "addon.block_rssclient.pluginname": "Trình đọc tin RSS", - "addon.block_selfcompletion.pluginname": "Tự hoàn thành", - "addon.block_sitemainmenu.pluginname": "Trình đơn chính", - "addon.block_tags.pluginname": "Thẻ", - "addon.blog.blog": "Blog", - "addon.blog.blogentries": "Các mục blog", - "addon.blog.errorloadentries": "Lỗi nạp blog entries.", - "addon.blog.linktooriginalentry": "Liên kết đến mục blog ban đầu", - "addon.blog.noentriesyet": "Không có mục hiện hữu ở đây", - "addon.blog.publishtonoone": "Của bạn (bản nháp)", - "addon.blog.publishtosite": "Bất kỳ ai trên site này", - "addon.blog.publishtoworld": "Bất kỳ ai trên thế giới", - "addon.blog.showonlyyourentries": "Chỉ hiển thị các mục của bạn", - "addon.calendar.allday": "Cả ngày", - "addon.calendar.calendar": "Lịch", - "addon.calendar.calendarevent": "Sự kiện lịch", - "addon.calendar.calendarevents": "Sự kiện lịch", - "addon.calendar.calendarreminders": "Lời nhắc lịch", - "addon.calendar.confirmeventdelete": "Bạn có chắc chắn muốn xoá sự kiện này?", - "addon.calendar.courseevents": "Sự kiện của khoá học", - "addon.calendar.currentmonth": "Tháng hiện tại", - "addon.calendar.defaultnotificationtime": "Thời gian thông báo mặc định", - "addon.calendar.deleteevent": "Xóa sự kiện", - "addon.calendar.durationminutes": "Thời lượng tính bằng phút", - "addon.calendar.durationnone": "Không xác định thời lượng", - "addon.calendar.durationuntil": "Tới", - "addon.calendar.editevent": "Chỉnh sửa sự kiện", - "addon.calendar.errorloadevent": "Lỗi tải sự kiện.", - "addon.calendar.errorloadevents": "Lỗi tải sự kiện.", - "addon.calendar.eventcalendareventdeleted": "Sự kiện lịch bị xóa", - "addon.calendar.eventduration": "Thời lượng", - "addon.calendar.eventendtime": "Kết thúc", - "addon.calendar.eventkind": "Loại sự kiện", - "addon.calendar.eventname": "Tên", - "addon.calendar.eventstarttime": "Bắt đầu", - "addon.calendar.fri": "T6", - "addon.calendar.friday": "Thứ 6", - "addon.calendar.groupevents": "Sự kiện nhóm", - "addon.calendar.invalidtimedurationminutes": "Khoảng thời gian tính bằng phút mà bạn vừa nhập không có hiệu lực. Vui lòng nhập một khoảng thời gian tính bằng phút lớn hơn 0 hoặc không chọn thời gian.", - "addon.calendar.invalidtimedurationuntil": "Khoảng thời gian ngày và giờ bạn chọn diễn ra trước khi sự kiện bắt đầu. Vui lòng điều chỉnh trước khi tiếp tục.", - "addon.calendar.mon": "T2", - "addon.calendar.monday": "Thứ 2", - "addon.calendar.monthlyview": "Xem theo tháng", - "addon.calendar.newevent": "Sự kiện mới", - "addon.calendar.noevents": "Không có sự kiện", - "addon.calendar.nopermissiontoupdatecalendar": "Xin lỗi, nhưng hiện tại bạn không được phép cập nhật sự kiện lịch", - "addon.calendar.reminders": "Nhắc nhở", - "addon.calendar.repeatedevents": "Sự kiện lặp lại", - "addon.calendar.repeateditall": "Áp dụng các thay đổi cho toàn bộ {{$a}} sự kiện trong chuỗi lặp này", - "addon.calendar.repeateditthis": "Áp dụng các thay đổi chỉ cho sự kiện này", - "addon.calendar.repeatevent": "Lặp lại sự kiện này", - "addon.calendar.repeatweeksl": "Lặp lại hàng tuần, tạo ra tất cả", - "addon.calendar.sat": "T7", - "addon.calendar.saturday": "Thứ 7", - "addon.calendar.setnewreminder": "Đặt lời nhắc mới", - "addon.calendar.siteevents": "Các sự kiện trang", - "addon.calendar.sun": "CN", - "addon.calendar.sunday": "Chủ Nhật", - "addon.calendar.thu": "T5", - "addon.calendar.thursday": "Thứ 5", - "addon.calendar.today": "Hôm nay", - "addon.calendar.tomorrow": "Ngày mai", - "addon.calendar.tue": "T3", - "addon.calendar.tuesday": "Thứ 3", - "addon.calendar.typecourse": "Sự kiện khoá học", - "addon.calendar.typegroup": "Sự kiện của nhóm", - "addon.calendar.typesite": "Sự kiện chung", - "addon.calendar.typeuser": "Sự kiện thành viên", - "addon.calendar.upcomingevents": "Sự kiện sắp đến", - "addon.calendar.userevents": "Sự kiện thành viên", - "addon.calendar.wed": "T4", - "addon.calendar.wednesday": "Thứ 4", - "addon.calendar.yesterday": "Hôm qua", - "addon.competency.competencies": "Năng lực", - "addon.competency.errornocompetenciesfound": "Không có năng lực tìm thấy", - "addon.competency.nocompetencies": "Không có năng lực", - "addon.coursecompletion.complete": "Hoàn thành", - "addon.coursecompletion.completecourse": "Hoàn thành khóa học", - "addon.coursecompletion.completed": "Đã hoàn thành", - "addon.coursecompletion.couldnotloadreport": "Không thể nạp báo cáo hoàn thành khóa học. Vui lòng thử lại sau.", - "addon.coursecompletion.coursecompletion": "Hoàn thành khóa học", - "addon.coursecompletion.criteria": "Tiêu chuẩn", - "addon.coursecompletion.criteriagroup": "Nhóm tiêu chuẩn", - "addon.coursecompletion.criteriarequiredall": "Yêu cầu phải thỏa mãn tất cả các tiêu chuẩn dưới đây", - "addon.coursecompletion.criteriarequiredany": "Yêu cầu nào dưới đây cũng phải thỏa mãn", - "addon.coursecompletion.inprogress": "Đang tiến hành", - "addon.coursecompletion.manualselfcompletion": "Tự hoàn thành", - "addon.coursecompletion.nottracked": "Hiện tại bạn không bị theo dõi bởi hoàn thành khóa học", - "addon.coursecompletion.notyetstarted": "Chưa bắt đầu", - "addon.coursecompletion.pending": "Đang ngưng", - "addon.coursecompletion.required": "Bắt buộc", - "addon.coursecompletion.requiredcriteria": "Điều kiện yêu cầu", - "addon.coursecompletion.requirement": "Yêu cầu", - "addon.coursecompletion.status": "Trạng thái", - "addon.coursecompletion.viewcoursereport": "Xem báo cáo khóa học", - "addon.files.couldnotloadfiles": "Danh sách các tệp không thể nạp được.", - "addon.files.emptyfilelist": "Không có tệp nào để hiển thị.", - "addon.files.erroruploadnotworking": "Thật không may là hiện nay không thể tải lên các tập tin vào trang web của bạn.", - "addon.files.files": "Tập tin", - "addon.files.privatefiles": "Tập tin riêng tư", - "addon.files.sitefiles": "Tập tin toàn hệ thống", - "addon.messageoutput_airnotifier.processorsettingsdesc": "Cấu hình thiết bị", - "addon.messages.addcontact": "Thêm liên lạc", - "addon.messages.blocknoncontacts": "Ngăn những người không có liên lạc nhắn tin tôi", - "addon.messages.contactlistempty": "Danh sách liên lạc trống", - "addon.messages.contactname": "Tên liên hệ", - "addon.messages.contacts": "Liên lạc", - "addon.messages.deletemessage": "Xóa tin nhắn", - "addon.messages.deletemessageconfirmation": "Bạn có chắc bạn muốn xóa bỏ thư này không? Nó sẽ chỉ bị xóa khỏi lịch sử nhắn tin của bạn và sẽ vẫn có thể xem được bởi người dùng đã gửi hoặc nhận được tin nhắn.", - "addon.messages.errordeletemessage": "error trong khi xoá thư.", - "addon.messages.errorwhileretrievingcontacts": "error khi truy xuất danh bạ từ máy chủ.", - "addon.messages.errorwhileretrievingdiscussions": "error khi truy xuất các cuộc thảo luận từ máy chủ.", - "addon.messages.errorwhileretrievingmessages": "error khi truy xuất thư từ máy chủ.", - "addon.messages.errorwhileretrievingusers": "error khi truy xuất người dùng từ máy chủ.", - "addon.messages.message": "Tin nhắn", - "addon.messages.messagenotsent": "Thư không được gửi. Vui lòng thử lại sau.", - "addon.messages.messages": "Tin nhắn", - "addon.messages.newmessages": "Tin nhắn mới", - "addon.messages.nomessagesfound": "Không tim thấy tin nhắn", - "addon.messages.nousersfound": "Không tìm thấy người dùng", - "addon.messages.removecontact": "Xóa liên lạc", - "addon.messages.searchcombined": "Tìm người và tin nhắn", - "addon.messages.showdeletemessages": "Hiện xóa thư", - "addon.messages.type_blocked": "Bị chặn", - "addon.messages.type_offline": "Diễn đàn", - "addon.messages.type_online": "Trực tuyến", - "addon.messages.type_search": "Kết quả tìm kiếm", - "addon.messages.type_strangers": "Khác", - "addon.messages.useentertosenddescdesktop": "Nếu bị vô hiệu hoá, bạn có thể sử dụng Ctrl + Enter để gửi tin nhắn.", - "addon.messages.useentertosenddescmac": "Nếu bị vô hiệu hoá, bạn có thể sử dụng CMD + Enter để gửi tin nhắn.", - "addon.messages.warningconversationmessagenotsent": "Không thể gửi tin nhắn đến hội thoại {{conversation}}.{{error}}", - "addon.messages.warningmessagenotsent": "Không thể gửi tin nhắn cho người dùng {{user}}.{{error}}", - "addon.mod_assign.acceptsubmissionstatement": "Xin vui lòng chấp nhận tuyên bố nộp hồ sơ.", - "addon.mod_assign.cannoteditduetostatementsubmission": "Bạn không thể thêm hoặc chỉnh sửa một bài nộp trong ứng dụng vì tuyên bố gửi không thể được lấy từ trang web.", - "addon.mod_assign.cannotgradefromapp": "Một số phương pháp chấm điểm chưa được ứng dụng hỗ trợ và không thể sửa đổi.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "Bạn không thể thực hiện một trình trong ứng dụng vì tuyên bố nộp không thể được lấy từ trang web.", - "addon.mod_assign.duedate": "Hạn chót", - "addon.mod_assign.duedateno": "Vô thời hạn", - "addon.mod_assign.editsubmission": "Sửa bài nộp", - "addon.mod_assign.erroreditpluginsnotsupported": "Bạn không thể thêm hoặc chỉnh sửa một bài nộp trong ứng dụng bởi vì một số plugin chưa được hỗ trợ để chỉnh sửa.", - "addon.mod_assign.errorshowinginformation": "Thông tin gửi không được hiển thị.", - "addon.mod_assign.feedbacknotsupported": "Phản hồi này không được ứng dụng hỗ trợ và có thể không chứa tất cả thông tin.", - "addon.mod_assign.grade": "Điểm", - "addon.mod_assign.graded": "Đã cho điểm", - "addon.mod_assign.gradenotsynced": "Lớp không đồng bộ hóa", - "addon.mod_assign.modulenameplural": "Bài tập", - "addon.mod_assign.notallparticipantsareshown": "Những người tham gia đã không thực hiện một bài nộp không được hiển thị.", - "addon.mod_assign.numwords": "{{$a}} từ", - "addon.mod_assign.submission": "Bài nộp", - "addon.mod_assign.submissionnotsupported": "Việc gửi này không được ứng dụng hỗ trợ và có thể không chứa tất cả thông tin.", - "addon.mod_assign.submissionstatus_marked": "Đã cho điểm", - "addon.mod_assign.submitassignment": "Gửi bài", - "addon.mod_assign.userwithid": "Người dùng có ID {{id}}", - "addon.mod_assign.warningsubmissiongrademodified": "Bài nộp đã được sửa đổi trên trang web.", - "addon.mod_assign.warningsubmissionmodified": "Người gửi bài nộp được sửa đổi trên trang web.", - "addon.mod_chat.beep": "bíp", - "addon.mod_chat.currentusers": "Thành viên đang có mặt", - "addon.mod_chat.enterchat": "Nhấn vào đây để tham gia", - "addon.mod_chat.errorwhileconnecting": "error trong khi kết nối với cuộc trò chuyện.", - "addon.mod_chat.errorwhilegettingchatdata": "error trong khi nhận dữ liệu trò chuyện.", - "addon.mod_chat.errorwhilegettingchatusers": "error trong khi nhận được người dùng trò chuyện.", - "addon.mod_chat.errorwhileretrievingmessages": "error khi truy xuất thư từ máy chủ.", - "addon.mod_chat.errorwhilesendingmessage": "error khi gửi thư.", - "addon.mod_chat.messagebeepseveryone": "{{$a}} gọi tất cả mọi người!", - "addon.mod_chat.messagebeepsyou": "{{$a}} vừa gọi bạn!", - "addon.mod_chat.messageenter": "{{$a}} vừa vào phòng họp", - "addon.mod_chat.messageexit": "{{$a}} vừa ra khỏi phòng họp này", - "addon.mod_chat.messages": "Tin nhắn", - "addon.mod_chat.modulenameplural": "Phòng họp trực tuyến", - "addon.mod_chat.mustbeonlinetosendmessages": "Bạn phải trực tuyến để gửi tin nhắn.", - "addon.mod_chat.nomessages": "Chưa có ai viết gì", - "addon.mod_chat.nosessionsfound": "Không tìm thấy phiên", - "addon.mod_chat.showincompletesessions": "Hiển thị phiên không đầy đủ", - "addon.mod_chat.viewreport": "Xem các phiên họp trước", - "addon.mod_choice.errorgetchoice": "error được lựa chọn dữ liệu.", - "addon.mod_choice.expired": "Xin lỗi, hoạt động này đã kết thúc vào ngày {{$a}}.", - "addon.mod_choice.full": "(đã đủ)", - "addon.mod_choice.modulenameplural": "Câu hỏi thăm dò", - "addon.mod_choice.noresultsviewable": "Hiện không thể xem được kết quả.", - "addon.mod_choice.notopenyet": "Xin lỗi, hoạt động này chỉ được mở vào ngày {{$a}}", - "addon.mod_choice.removemychoice": "Xoá các câu trả lời của tôi", - "addon.mod_choice.responses": "Câu trả lời", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}% của người dùng đã chọn tùy chọn: {{text}}.", - "addon.mod_choice.resultsnotsynced": "Phản hồi cuối cùng của bạn phải được đồng bộ trước khi nó được bao gồm trong kết quả.", - "addon.mod_choice.savemychoice": "Lưu câu trả lời của tôi", - "addon.mod_choice.yourselection": "Lựa chọn của bạn", - "addon.mod_data.addentries": "Thêm các mục", - "addon.mod_data.advancedsearch": "Tìm kiếm nâng cao", - "addon.mod_data.alttext": "Văn bản thay thế", - "addon.mod_data.approve": "Chấp nhận", - "addon.mod_data.approved": "Đã chấp nhận", - "addon.mod_data.ascending": "Tăng dần", - "addon.mod_data.authorfirstname": "Họ của tác giả", - "addon.mod_data.authorlastname": "Tên của tác giả", - "addon.mod_data.descending": "Giảm dần", - "addon.mod_data.edittagsnotsupported": "Xin error, các thẻ chỉnh sửa không được ứng dụng hỗ trợ.", - "addon.mod_data.errorapproving": "error phê duyệt hoặc không phê duyệt mục.", - "addon.mod_data.errordeleting": "error xóa mục nhập.", - "addon.mod_data.gettinglocation": "Nhận vị trí", - "addon.mod_data.locationpermissiondenied": "Quyền truy cập vào vị trí của bạn đã bị từ chối.", - "addon.mod_data.modulenameplural": "Các CSDL", - "addon.mod_data.mylocation": "Vị trí của tôi", - "addon.mod_data.resetsettings": "Đặt lại bộ lọc", - "addon.mod_data.searchbytagsnotsupported": "Xin error, việc tìm kiếm bằng thẻ không được ứng dụng hỗ trợ.", - "addon.mod_feedback.captchaofflinewarning": "Phản hồi với CAPTCHA không thể hoàn tất gián tuyến, hoặc nếu không được cấu hình, hoặc nếu máy chủ xuống.", - "addon.mod_feedback.complete_the_form": "Trả lời các câu hỏi", - "addon.mod_feedback.feedback_submitted_offline": "Phản hồi này đã được lưu để gửi sau.", - "addon.mod_feedback.preview": "Xem thử", - "addon.mod_feedback.questions": "Các câu hỏi", - "addon.mod_folder.emptyfilelist": "Không có tệp nào để hiển thị.", - "addon.mod_forum.addanewdiscussion": "Thêm một chủ đề thảo luận mới", - "addon.mod_forum.addanewtopic": "Thêm một chủ đề mới", - "addon.mod_forum.advanced": "Nâng cao", - "addon.mod_forum.couldnotadd": "Không thể thêm bài viết của bạn bởi vì có một lỗi không rõ", - "addon.mod_forum.couldnotupdate": "Không thể cập nhật bài viết của bạn bởi vì có một lỗi không rõ", - "addon.mod_forum.delete": "Xoá", - "addon.mod_forum.deletedpost": "bài viết bị xóa", - "addon.mod_forum.deletesure": "Bạn chắc chắn muốn xóa bài viết đó ?", - "addon.mod_forum.discussion": "Diễn đàn", - "addon.mod_forum.edit": "Chỉnh sửa", - "addon.mod_forum.errorgetforum": "error nhận dữ liệu diễn đàn.", - "addon.mod_forum.errorgetgroups": "error nhận cài đặt nhóm.", - "addon.mod_forum.errorposttoallgroups": "Không thể tạo ra cuộc thảo luận mới trong tất cả các nhóm.", - "addon.mod_forum.forumnodiscussionsyet": "Không có thảo luận nào được nêu ra trong diễn đàn này.", - "addon.mod_forum.group": "Nhóm", - "addon.mod_forum.lastpost": "Bài viết gần đây nhất", - "addon.mod_forum.message": "Nội dung", - "addon.mod_forum.modeflatnewestfirst": "Lần gửi cuối cùng", - "addon.mod_forum.modeflatoldestfirst": "Lần gửi đầu tiên", - "addon.mod_forum.modenested": "Hiển thị phúc đáp theo kiểu cấu trúc", - "addon.mod_forum.modulenameplural": "Diễn đàn", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} thảo luận", - "addon.mod_forum.numreplies": "{{numreplies}} trả lời", - "addon.mod_forum.posttoforum": "Gửi bài viết lên diễn đàn", - "addon.mod_forum.re": "Trả lời:", - "addon.mod_forum.refreshdiscussions": "Làm mới thảo luận", - "addon.mod_forum.refreshposts": "Làm mới bài viết", - "addon.mod_forum.reply": "Phúc đáp", - "addon.mod_forum.subject": "Tiêu đề", - "addon.mod_forum.unread": "Chưa đọc", - "addon.mod_forum.unreadpostsnumber": "{{$a}} bài gửi chưa đọc", - "addon.mod_forum.yourreply": "Bài phúc đáp của bạn", - "addon.mod_glossary.browsemode": "Duyệt các mục", - "addon.mod_glossary.byalphabet": "Theo chữ cái", - "addon.mod_glossary.byauthor": "Nhóm theo tác giả", - "addon.mod_glossary.bycategory": "Nhóm theo thể loại", - "addon.mod_glossary.bynewestfirst": "Mới nhất trước", - "addon.mod_glossary.byrecentlyupdated": "Cập Nhật gần đây", - "addon.mod_glossary.bysearch": "Tìm", - "addon.mod_glossary.cannoteditentry": "Không thể chỉnh sửa mục", - "addon.mod_glossary.entriestobesynced": "Mục được đồng bộ hóa", - "addon.mod_glossary.entrypendingapproval": "Mục này đang chờ phê duyệt.", - "addon.mod_glossary.errorloadingentries": "error xảy ra khi tải mục.", - "addon.mod_glossary.errorloadingentry": "error xảy ra khi tải mục.", - "addon.mod_glossary.errorloadingglossary": "error đã xảy ra trong khi tải glossary.", - "addon.mod_glossary.noentriesfound": "Không tìm thấy mục nào.", - "addon.mod_glossary.searchquery": "Truy vấn tìm kiếm", - "addon.mod_imscp.showmoduledescription": "Hiện mô tả", - "addon.mod_lesson.answer": "Đáp án", - "addon.mod_lesson.attempt": "Lần thử: {{$a}}", - "addon.mod_lesson.averagescore": "Điểm trung bình", - "addon.mod_lesson.averagetime": "Thời gian trung bình", - "addon.mod_lesson.branchtable": "Bảng phân nhánh", - "addon.mod_lesson.clusterjump": "Unseen Question within a Cluster", - "addon.mod_lesson.completed": "Được hoàn thành", - "addon.mod_lesson.congratulations": "Chúc mừng bạn", - "addon.mod_lesson.continue": "Tiếp tục", - "addon.mod_lesson.detailedstats": "Các thông báo chi tiết", - "addon.mod_lesson.didnotanswerquestion": "Không trả lời câu hỏi này.", - "addon.mod_lesson.displayofgrade": "Hiển thị điểm (chỉ dành cho sinh viên)", - "addon.mod_lesson.displayscorewithessays": "Bạn nhân {{$a.score}} trong số {{$a.tempmaxgrade}} đối với những câu hỏi được tự động cho điểm.
                      Của bạn {{$a.essayquestions}} câu hỏi sẽ được cho điểm và được thêm
                      vào số điểm cuối cùng của bạn .

                      Điểm số hiện tại của bạn không bao gồm câu hỏi có câu trả lời ngắn là {{$a.score}} {{$a.grade}}", - "addon.mod_lesson.displayscorewithoutessays": "Điểm của bạn là {{$a.score}} (trong số {{$a.grade}} ).", - "addon.mod_lesson.enterpassword": "Vui lòng gõ vào mật khẩu :", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "Bạn không trả lời bất kỳ câu hỏi nào. Bạn sẽ nhận được không điểm.", - "addon.mod_lesson.errorprefetchrandombranch": "Bài học này chứa một bước nhảy đến một trang nội dung ngẫu nhiên. Nó có thể ' t được cố gắng trong các ứng dụng cho đến khi nó đã được bắt đầu trong một trình duyệt web.", - "addon.mod_lesson.errorreviewretakenotlast": "Nỗ lực này không còn có thể được đánh giá vì một nỗ lực đã được hoàn tất.", - "addon.mod_lesson.finishretakeoffline": "Nỗ lực này đã hoàn tất ngoại tuyến.", - "addon.mod_lesson.firstwrong": "Rất tiếc bạn không thể nhận điểm bởi vì câu trả lời của bạn sai. Bạn có muốn Would you like to keep guessing, just for the sheer joy of learning (but for no point credit)?", - "addon.mod_lesson.grade": "Điểm", - "addon.mod_lesson.highscore": "Điểm cao", - "addon.mod_lesson.hightime": "High Time", - "addon.mod_lesson.leftduringtimed": "Bạn đã thoát ra trong thời gian bài học.
                      Vui lòng kích vào nút tiếp tục để bắt đầu lại bài học .", - "addon.mod_lesson.leftduringtimednoretake": "Bạn đã thoát ra trong thời gian bài học và vì thế ban
                      không được phép làm lại hoặc tiếp tục bài học .", - "addon.mod_lesson.lessonmenu": "Thực đơn bài học", - "addon.mod_lesson.lessonstats": "Thống kê bài học", - "addon.mod_lesson.loginfail": "Việc đăng nhập bị thất bại, vui lòng thử lại..", - "addon.mod_lesson.lowscore": "Điểm thấp", - "addon.mod_lesson.lowtime": "Low Time", - "addon.mod_lesson.modattemptsnoteacher": "Sinh viên chỉ xem lại các công việc dành cho các sinh viên.", - "addon.mod_lesson.modulenameplural": "Các bài học", - "addon.mod_lesson.noanswer": "Không đưa ra câu trả lời. Vui lòng quay trở lại và đưa ra một câu trả lời.", - "addon.mod_lesson.notcompleted": "Không được hoàn thành", - "addon.mod_lesson.numberofcorrectanswers": "Số câu trả lời đúng: {{$a}}", - "addon.mod_lesson.numberofpagesviewed": "Số lần thử : {{$a}}", - "addon.mod_lesson.ongoingcustom": "Bạn vừa nhận được {{$a.score}} điểm trong số {{$a.currenthigh}} điểm từ trước tới giờ .", - "addon.mod_lesson.ongoingnormal": "Bạn vừa trả lời {{$a.correct}} câu hỏi trong số {{$a.viewed}} các câu hỏi.", - "addon.mod_lesson.or": "hoặc", - "addon.mod_lesson.overview": "Tổng quan", - "addon.mod_lesson.question": "Câu hỏi", - "addon.mod_lesson.response": "Câu trả lời", - "addon.mod_lesson.retakefinishedinsync": "Một nỗ lực offline đã được đồng bộ hóa. Bạn có muốn đánh giá nó không?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.reviewlesson": "Xem lại bài", - "addon.mod_lesson.reviewquestionback": "Có , tôi muốn thử lại", - "addon.mod_lesson.reviewquestioncontinue": "không, tôi muốn đi tới câu hỏi tiếp theo", - "addon.mod_lesson.secondpluswrong": "Không khá lắm. Bạn có muốn thử lại ?", - "addon.mod_lesson.teacherjumpwarning": "Một {{$a.cluster}} chuyển tới hoặc một {{$a.unseen}} sự chuyển thì đang được sử dụng trong bài học này. Thay vì sử dụng chuyển tới trang tiếp theo. Đăng nhập như một sinh viên để kiểm tra những bước chuyển đó.", - "addon.mod_lesson.teacherongoingwarning": "Điểm kế tiếp chỉ được hiển thị đối với sinh viên. Đăng nhập như một sinh viên để kiểm tra điểm tiếp theo", - "addon.mod_lesson.teachertimerwarning": "Thời gian làm bài chỉ dành cho các sinh viên. Kiểm tra thời gian bằng cách đăng nhập như là một sinh viên.", - "addon.mod_lesson.thatsthecorrectanswer": "Đó là một câu trả lời đúng", - "addon.mod_lesson.thatsthewronganswer": "Đó là một câu trả lời sai", - "addon.mod_lesson.timeremaining": "Thời gian còn lại", - "addon.mod_lesson.timetaken": "Thời gian đã sử dụng", - "addon.mod_lesson.unseenpageinbranch": "Không thấy câu hỏi trong một nhánh", - "addon.mod_lesson.warningretakefinished": "Các nỗ lực đã được hoàn thành trên trang web.", - "addon.mod_lesson.welldone": "thực hiện tốt!", - "addon.mod_lesson.youhaveseen": "Bạn vừa nhìn thấy một số trang của bài học này.
                      Bạn có muốn bắt đầu ở trang sau ?", - "addon.mod_lesson.youranswer": "Câu trả lời của bạn", - "addon.mod_lesson.youshouldview": "Bạn nên xem ít nhất: {{$a}}", - "addon.mod_lti.errorgetlti": "error nhận dữ liệu mô-đun.", - "addon.mod_lti.errorinvalidlaunchurl": "URL khởi động không hợp lệ.", - "addon.mod_lti.launchactivity": "Khởi chạy hoạt động", - "addon.mod_page.errorwhileloadingthepage": "error khi tải nội dung trang.", - "addon.mod_quiz.attemptfirst": "Thử nghiệm lần đầu", - "addon.mod_quiz.attemptlast": "Kiểm tra lần cuối", - "addon.mod_quiz.attemptquiznow": "Bắt đầu kiểm tra", - "addon.mod_quiz.canattemptbutnotsubmit": "Bạn có thể thử bài kiểm tra này trong ứng dụng, nhưng bạn sẽ cần phải gửi nỗ lực trong trình duyệt vì những lý do sau:", - "addon.mod_quiz.cannotsubmitquizdueto": "Nỗ lực kiểm tra này không thể được gửi vì những lý do sau:", - "addon.mod_quiz.comment": "Nhận xét", - "addon.mod_quiz.completedon": "Kết thúc lúc", - "addon.mod_quiz.confirmclose": "Bạn chuẩn bị chấm dứt lần thi này. Một khi đã chấm dứt bạn sẽ không thể thay đổi đáp án của mình nữa.", - "addon.mod_quiz.confirmcontinueoffline": "Nỗ lực này chưa được đồng bộ hóa từ {{$a}}. Nếu bạn đã tiếp tục nỗ lực này trong một thiết bị khác kể từ đó, bạn có thể mất dữ liệu.", - "addon.mod_quiz.confirmleavequizonerror": "error đã xảy ra khi lưu câu trả lời. Bạn có chắc chắn muốn rời khỏi bài kiểm tra?", - "addon.mod_quiz.continueattemptquiz": "Tiếp tục lần kiểm tra cuối cùng", - "addon.mod_quiz.errorbehaviournotsupported": "Bài kiểm tra này không thể thực hiện trong ứng dụng vì các câu hỏi hành vi không được hỗ trợ bởi các ứng dụng:", - "addon.mod_quiz.errordownloading": "error tải xuống dữ liệu cần thiết.", - "addon.mod_quiz.errorgetattempt": "error nhận dữ liệu cố gắng.", - "addon.mod_quiz.errorgetquestions": "error nhận được câu hỏi.", - "addon.mod_quiz.errorgetquiz": "error nhận dữ liệu đố.", - "addon.mod_quiz.errorparsequestions": "error xảy ra khi đọc các câu hỏi. Hãy thử bài kiểm tra này trong một trình duyệt web.", - "addon.mod_quiz.errorquestionsnotsupported": "Bài kiểm tra này không thể thực hiện trong ứng dụng vì nó chỉ chứa các câu hỏi không được ứng dụng hỗ trợ:", - "addon.mod_quiz.errorrulesnotsupported": "Bài kiểm tra này không thể thực hiện trong ứng dụng vì nó có các quy tắc truy cập không được ứng dụng hỗ trợ:", - "addon.mod_quiz.errorsaveattempt": "error đã xảy ra khi lưu dữ liệu cố gắng.", - "addon.mod_quiz.feedback": "Phản hồi", - "addon.mod_quiz.finishnotsynced": "Đã hoàn thành nhưng không đồng bộ hóa", - "addon.mod_quiz.grade": "Điểm", - "addon.mod_quiz.gradeaverage": "Điểm trung bình", - "addon.mod_quiz.gradehighest": "Lần cao nhất", - "addon.mod_quiz.grademethod": "Cách tính điểm", - "addon.mod_quiz.marks": "Điểm", - "addon.mod_quiz.modulenameplural": "Các đề thi", - "addon.mod_quiz.noquestions": "Chưa có câu hỏi nào được thêm vào", - "addon.mod_quiz.opentoc": "Mở điều hướng popover", - "addon.mod_quiz.outof": "{{$a.grade}} trên tối đa of {{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "Phản hồi chung", - "addon.mod_quiz.overdue": "Quá hạn", - "addon.mod_quiz.preview": "Xem trước", - "addon.mod_quiz.question": "Câu hỏi", - "addon.mod_quiz.reattemptquiz": "Thực hiện lại đề thi", - "addon.mod_quiz.requirepasswordmessage": "Để thử đề thi này bạn cần biết mật khẩu của đề thi đó", - "addon.mod_quiz.review": "Xem lại", - "addon.mod_quiz.reviewofattempt": "Xem lại lần thử {{$a}}", - "addon.mod_quiz.showall": "Hiển thị các câu hỏi trong 1 trang", - "addon.mod_quiz.startedon": "Bắt đầu vào lúc", - "addon.mod_quiz.submitallandfinish": "Nộp bài và kết thúc", - "addon.mod_quiz.timeleft": "Thời gian còn lại", - "addon.mod_quiz.timetaken": "Thời gian thực hiện", - "addon.mod_quiz.warningattemptfinished": "Cố gắng gián tuyến bị loại bỏ vì nó đã được hoàn thành trên trang web hoặc không tìm thấy.", - "addon.mod_quiz.warningdatadiscarded": "Một số câu trả lời offline đã bị loại bỏ bởi vì các câu hỏi đã được sửa đổi trực tuyến.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "Cố gắng chưa hoàn thành vì một số câu trả lời offline đã bị loại bỏ. Xin vui lòng xem lại câu trả lời của bạn sau đó gửi lại các nỗ lực.", - "addon.mod_quiz.warningquestionsnotsupported": "Bài kiểm tra này chứa các câu hỏi không được ứng dụng hỗ trợ:", - "addon.mod_quiz.yourfinalgradeis": "Điểm số cuối cùng cho đề thi này của bạn là {{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "error khi tải nội dung.", - "addon.mod_resource.openthefile": "Mở tệp", - "addon.mod_scorm.cannotcalculategrade": "Không thể tổng hợp điểm", - "addon.mod_scorm.dataattemptshown": "Dữ liệu này thuộc về số lần thực hiện {{number}}.", - "addon.mod_scorm.errorcreateofflineattempt": "error xảy ra khi tạo một nỗ lực gián tuyến mới. Vui lòng thử lại.", - "addon.mod_scorm.errordownloadscorm": "error tải xuống SCORM: \"{{name}}\".", - "addon.mod_scorm.errorgetscorm": "error nhận dữ liệu SCORM.", - "addon.mod_scorm.errorinvalidversion": "Xin error, các ứng dụng chỉ hỗ trợ SCORM 1,2.", - "addon.mod_scorm.errornotdownloadable": "Tải xuống gói SCORM bị vô hiệu hoá. Vui lòng liên hệ với người quản trị trang web của bạn.", - "addon.mod_scorm.errornovalidsco": "Gói SCORM này không có một SCO có thể nhìn thấy để tải.", - "addon.mod_scorm.errorpackagefile": "Xin error, các ứng dụng chỉ hỗ trợ các gói ZIP.", - "addon.mod_scorm.errorsyncscorm": "error đã xảy ra khi đồng bộ. Vui lòng thử lại.", - "addon.mod_scorm.modulenameplural": "Các gói SCORM", - "addon.mod_scorm.offlineattemptnote": "Dữ liệu của lần thực hiện này không được đồng bộ hóa.", - "addon.mod_scorm.offlineattemptovermax": "Lần thực hiện này không được vì bạn vượt quá số lần thực hiện tối đa.", - "addon.mod_scorm.scormstatusnotdownloaded": "Gói SCORM này không được tải xuống. Nó sẽ được tự động tải về khi bạn mở nó.", - "addon.mod_scorm.scormstatusoutdated": "Gói SCORM này đã được sửa đổi kể từ lần tải xuống cuối cùng. Nó sẽ được tự động tải về khi bạn mở nó.", - "addon.mod_scorm.warningofflinedatadeleted": "Một số dữ liệu ngoại tuyến từ lần thực hiện thứ {{number}} đã bị loại bỏ vì nó không được tính là một lần thực hiện mới", - "addon.mod_scorm.warningsynconlineincomplete": "Một số nỗ lực Không thể được đồng bộ hóa với các trang web vì cố gắng trực tuyến cuối cùng chưa hoàn thành. Hãy hoàn thành những nỗ lực trực tuyến trước.", - "addon.mod_survey.cannotsubmitsurvey": "Xin error, đã có một vấn đề gửi khảo sát của bạn. Vui lòng thử lại.", - "addon.mod_survey.errorgetsurvey": "error nhận dữ liệu khảo sát.", - "addon.mod_survey.ifoundthat": "Tôi nhận thấy rằng", - "addon.mod_survey.ipreferthat": "Tôi muốn rằng", - "addon.mod_survey.modulenameplural": "Cuộc khảo sát", - "addon.mod_survey.results": "Kết quả", - "addon.mod_url.accessurl": "Truy cập URL", - "addon.mod_url.pointingtourl": "URL mà tài nguyên trỏ đến.", - "addon.mod_wiki.errorloadingpage": "error xảy ra khi tải trang.", - "addon.mod_wiki.errornowikiavailable": "Wiki này không có bất kỳ nội dung nào được nêu ra.", - "addon.mod_wiki.gowikihome": "Truy cập trang đầu tiên của wiki", - "addon.mod_wiki.subwiki": "Tiểu wiki", - "addon.mod_wiki.titleshouldnotbeempty": "Tiêu đề không nên trống", - "addon.mod_wiki.viewpage": "Xem trang", - "addon.mod_wiki.wikipage": "Trang wiki", - "addon.mod_workshop.assessmentstrategynotsupported": "Chiến lược đánh giá {{$a}} không được hỗ trợ", - "addon.mod_workshop.modulenameplural": "Các đợt tập huấn", - "addon.mod_workshop.submissionrequiredtitle": "Bạn cần phải nhập tiêu đề.", - "addon.mod_workshop.warningassessmentmodified": "Việc gửi đã được sửa đổi trên trang web.", - "addon.mod_workshop.warningsubmissionmodified": "Việc đánh giá đã được sửa đổi trên trang web.", - "addon.notes.addnewnote": "Thêm ghi chú mới", - "addon.notes.coursenotes": "Ghi chú khóa học", - "addon.notes.deleteconfirm": "Xóa ghi chú này?", - "addon.notes.eventnotecreated": "Ghi chú được tạo", - "addon.notes.eventnotedeleted": "Ghi chú bị xóa", - "addon.notes.note": "Ghi chú", - "addon.notes.notes": "Ghi chú", - "addon.notes.personalnotes": "Ghi chú cá nhân", - "addon.notes.publishstate": "Bối cảnh", - "addon.notes.sitenotes": "Ghi chú trang", - "addon.notes.userwithid": "Người dùng có ID {{id}}", - "addon.notes.warningnotenotsent": "Không thể thêm ghi chú cho khóa học {{course}}.{{error}}", - "addon.notifications.errorgetnotifications": "error nhận được thông báo.", - "addon.notifications.notifications": "Thông báo", - "addon.notifications.playsound": "Phát âm thanh", - "addon.notifications.therearentnotificationsyet": "Không có thông báo nào.", - "addon.storagemanager.deletecourse": "Offload tất cả dữ liệu khóa học", - "addon.storagemanager.deletedatafrom": "Offload dữ liệu từ {{name}}", - "addon.storagemanager.info": "Các tệp được lưu trên thiết bị của bạn làm cho ứng dụng hoạt động nhanh hơn và cho phép sử dụng ứng dụng ngoại tuyến. Bạn có thể an toàn offload tập tin nếu bạn cần để miễn phí không gian lưu trữ.", - "addon.storagemanager.managestorage": "Quản lý bộ nhớ", - "addon.storagemanager.storageused": "Lưu trữ tệp được sử dụng:", - "assets.countries.AE": "Các Tiểu Vương quốc Ả Rập Thống nhất", - "assets.countries.AO": "Ăng gô la", - "assets.countries.AQ": "Châu Nam Cực", - "assets.countries.AT": "Áo", - "assets.countries.AU": "Australia", - "assets.countries.AX": "Quần đảo Ai Len", - "assets.countries.BE": "Bỉ", - "assets.countries.BG": "Bun ga ri", - "assets.countries.BL": "Saint Barthélemy", - "assets.countries.BR": "Bra xin", - "assets.countries.CA": "Ca na đa", - "assets.countries.CD": "Cộng hoà Dân chủ Công gô", - "assets.countries.CF": "Cộng hoà Trung Phi", - "assets.countries.CH": "Thụy Sĩ", - "assets.countries.CI": "Bờ Biển Ngà", - "assets.countries.CK": "Quần đảo Cook", - "assets.countries.CL": "Chi Lê", - "assets.countries.CN": "Trung Quốc", - "assets.countries.CU": "Cu Ba", - "assets.countries.CX": "Đảo Giáng sinh", - "assets.countries.CZ": "Cộng Hoà Séc", - "assets.countries.DE": "Đức", - "assets.countries.DO": "Cộng Hoà Dominican", - "assets.countries.EH": "Tây Sahara", - "assets.countries.FI": "Phần Lan", - "assets.countries.FK": "Qunầ đảo Falkland (Malvinas)", - "assets.countries.FM": "Liên bang Micronesia", - "assets.countries.FO": "Quần đảo Faroe", - "assets.countries.FR": "Pháp", - "assets.countries.GB": "Vương Quốc Anh", - "assets.countries.GF": "Guiana Pháp", - "assets.countries.GG": "Guernsey", - "assets.countries.GQ": "Guinea Xích đạo", - "assets.countries.GR": "Hi Lạp", - "assets.countries.GS": "Nam Georgia và Quần đảo South Sandwich", - "assets.countries.HK": "Hồng Công", - "assets.countries.HM": "Quần đảo Heard và Mc Donald", - "assets.countries.HU": "Hung ga ri", - "assets.countries.IE": "Ai Len", - "assets.countries.IM": "Isle Of Man", - "assets.countries.IN": "Ấn Độ", - "assets.countries.IQ": "I rắc", - "assets.countries.IS": "Iceland", - "assets.countries.IT": "Ý", - "assets.countries.JE": "Jersey", - "assets.countries.JP": "Nhật Bản", - "assets.countries.KH": "Căm pu chia", - "assets.countries.KP": "Triều Tiên", - "assets.countries.KR": "Hàn Quốc", - "assets.countries.KW": "Kuwait", - "assets.countries.KY": "Quần đảo Cayman", - "assets.countries.LA": "Lào", - "assets.countries.MD": "Cộng hoà Moldova", - "assets.countries.ME": "Montenegro", - "assets.countries.MF": "Saint Martin", - "assets.countries.MH": "Quần đảo Marshall", - "assets.countries.MM": "Miến Điện", - "assets.countries.MN": "Mông cổ", - "assets.countries.MP": "Quần đảo Northern Mariana", - "assets.countries.MX": "Mê Hi Cô", - "assets.countries.NC": "Tân Caledonia", - "assets.countries.NL": "Hà Lan", - "assets.countries.NO": "Na Uy", - "assets.countries.NZ": "Tân Tây Lan", - "assets.countries.PK": "Phi líp pin", - "assets.countries.PT": "Bồ Đào Nha", - "assets.countries.RS": "Serbia", - "assets.countries.RU": "Liên bang Nga", - "assets.countries.SA": "Saudi Arabia", - "assets.countries.SB": "Quần đảo Solomon", - "assets.countries.SE": "Thuỵ Điển", - "assets.countries.SG": "Sinh ga po", - "assets.countries.TH": "Thái Lan", - "assets.countries.TL": "Đông Timor", - "assets.countries.TR": "Thổ Nhĩ Kì", - "assets.countries.TW": "Đài Loan", - "assets.countries.UM": "United States Minor Outlying Islands", - "assets.countries.US": "Mỹ", - "assets.countries.VN": "Việt Nam", - "assets.mimetypes.application/epub_zip": "ebook EPUB", - "assets.mimetypes.application/msword": "Tài liệu Word", - "assets.mimetypes.application/pdf": "Tài liêu PĐF", - "assets.mimetypes.application/vnd.moodle.backup": "Bản sao lưu Moodle", - "assets.mimetypes.application/vnd.ms-excel": "Bảng tính Excel", - "assets.mimetypes.application/vnd.ms-powerpoint": "Bản trình bày Powepoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Bản trình bày Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Bản trình chiếu Powerpoint", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Bảng tính Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Mẫu Excel", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Tài liệu Word", - "assets.mimetypes.archive": "Lưu trữ ({{$a.EXT}})", - "assets.mimetypes.audio": "Tệp âm thanh ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "Tệp", - "assets.mimetypes.image": "Ảnh ({{$a.MIMETYPE2}})", - "assets.mimetypes.text/html": "Tài liệu HTML", - "assets.mimetypes.text/plain": "Tệp văn bản", - "assets.mimetypes.text/rtf": "Tài liệu RTF", - "core.accounts": "Tài khoản", - "core.add": "Thêm", - "core.ago": "cách đây {a}}", - "core.all": "Tất cả", - "core.allgroups": "Tất cả các nhóm", - "core.allparticipants": "Tất cả thành viên", - "core.answer": "Trả lời", - "core.areyousure": "Bạn có chắc không?", - "core.back": "Quay lại", - "core.block.blocks": "Khối", - "core.cancel": "Huỷ bỏ", - "core.cannotconnect": "Không thể kết nối: xác minh rằng bạn đã gõ đúng URL và trang web của bạn sử dụng Moodle {{$a}} hoặc mới hơn.", - "core.cannotdownloadfiles": "Tệp tải xuống bị vô hiệu hoá. Vui lòng liên hệ với người quản trị trang web của bạn.", - "core.captureaudio": "Ghi âm", - "core.capturedimage": "Chụp ảnh.", - "core.captureimage": "Chụp ảnh", - "core.capturevideo": "Quay video", - "core.category": "Mục", - "core.choose": "Chọn", - "core.choosedots": "Chọn...", - "core.clearsearch": "Xóa tìm kiếm", - "core.clicktohideshow": "Nhấn để mở rộng hoặc thu gọn lại", - "core.clicktoseefull": "Nhấp để xem toàn bộ nội dung.", - "core.close": "Đóng", - "core.comments": "Bình luận", - "core.comments.addcomment": "Thêm lời bình", - "core.comments.comments": "Bình luận", - "core.comments.commentsnotworking": "Không thể lấy ý kiến", - "core.comments.eventcommentcreated": "Lời bình đã được tạo", - "core.comments.eventcommentdeleted": "Lời bình đã xóa", - "core.comments.nocomments": "Không có lời bình", - "core.comments.savecomment": "Lưu lời bình", - "core.comments.warningcommentsnotsent": "Không thể đồng bộ hoá ý kiến. {{error}}", - "core.completion-alt-auto-fail": "Đã hoàn thành (không đạt điểm vượt qua)", - "core.completion-alt-auto-n": "Chưa hoàn thành", - "core.completion-alt-auto-pass": "Đã hoàn thành (đạt điểm vượt qua)", - "core.completion-alt-auto-y": "Đã hoàn thành", - "core.completion-alt-manual-n": "Chưa hoàn thành; hãy chọn đánh dấu chưa hoàn thành", - "core.completion-alt-manual-y": "Đã hoàn thành; hãy chọn đánh dấu hoàn thành", - "core.confirmcanceledit": "Bạn có chắc bạn muốn rời khỏi trang này không? Mọi thay đổi sẽ bị mất.", - "core.confirmdeletefile": "Bạn có chắc muốn xóa tệp này?", - "core.confirmgotabroot": "Bạn có chắc bạn muốn quay lại {{name}} không?", - "core.confirmgotabrootdefault": "Bạn có chắc chắn muốn đi đến trang đầu tiên của tab hiện tại không?", - "core.confirmloss": "Bạn có chắc không? Mọi thay đổi sẽ bị mất.", - "core.confirmopeninbrowser": "Bạn có muốn mở nó trong trình duyệt web không?", - "core.content": "Nội dung", - "core.contenteditingsynced": "Nội dung bạn đang chỉnh sửa đã được đồng bộ hóa.", - "core.contentlinks.chooseaccount": "Chọn tài khoản", - "core.contentlinks.chooseaccounttoopenlink": "Chọn một tài khoản để mở liên kết.", - "core.contentlinks.confirmurlothersite": "Liên kết này thuộc về một trang web khác. Bạn có muốn mở nó không?", - "core.contentlinks.errornoactions": "Không thể tìm một hành động để thực hiện với liên kết này.", - "core.contentlinks.errornosites": "Không thể tìm thấy bất kỳ trang web để xử lý liên kết này.", - "core.contentlinks.errorredirectothersite": "URL chuyển hướng không thể trỏ đến một trang web khác.", - "core.continue": "Tiếp tục", - "core.copiedtoclipboard": "Văn bản sao chép vào clipboard", - "core.course": "Khoá học", - "core.course.activitydisabled": "Tổ chức của bạn đã vô hiệu hoá hoạt động này trong ứng dụng dành cho thiết bị di động.", - "core.course.activitynotyetviewableremoteaddon": "Tổ chức của bạn đã cài đặt một plugin chưa được hỗ trợ.", - "core.course.activitynotyetviewablesiteupgradeneeded": "Cài đặt Moodle của tổ chức của bạn cần được Cập Nhật.", - "core.course.allsections": "Tất cả các phần", - "core.course.askadmintosupport": "Liên hệ với quản trị viên trang web và cho họ biết bạn muốn sử dụng hoạt động này với ứng dụng Moodle Mobile.", - "core.course.availablespace": "Bạn hiện có khoảng không gian trống {{available}}.", - "core.course.confirmdeletemodulefiles": "Bạn có chắc muốn xóa bỏ những tệp này không?", - "core.course.confirmdownload": "Bạn định tải xuống {{size}}.{{availableSpace}} Bạn có chắc bạn muốn tiếp tục không?", - "core.course.confirmdownloadunknownsize": "Không thể tính toán kích thước của việc tải xuống. {{availableSpace}} Bạn có chắc bạn muốn tiếp tục không?", - "core.course.confirmdownloadzerosize": "Bạn đang về để bắt đầu tải về. {{availableSpace}} Bạn có chắc bạn muốn tiếp tục không?", - "core.course.confirmlimiteddownload": "Bạn hiện không được kết nối với Wi-Fi.", - "core.course.confirmpartialdownloadsize": "Bạn định tải về ít nhất {{size}}.{{availableSpace}} Bạn có chắc bạn muốn tiếp tục không?", - "core.course.contents": "Nội dung", - "core.course.couldnotloadsectioncontent": "Không thể nạp nội dung phần. Vui lòng thử lại sau.", - "core.course.couldnotloadsections": "Không thể nạp các phần. Vui lòng thử lại sau.", - "core.course.coursesummary": "Tóm tắt về khoá học", - "core.course.errordownloadingcourse": "Lỗi tải về khóa học.", - "core.course.errordownloadingsection": "Lỗi tải xuống phần.", - "core.course.errorgetmodule": "Lỗi nhận dữ liệu hoạt động.", - "core.course.hiddenfromstudents": "Ẩn đối với người học", - "core.course.insufficientavailablequota": "Thiết bị của bạn không thể phân bổ không gian để lưu tải xuống này. Nó có thể được không gian để cập nhật ứng dụng và hệ thống. Hãy xóa một số không gian lưu trữ trước.", - "core.course.insufficientavailablespace": "Bạn đang cố gắng tải {{size}}. Điều này sẽ để lại thiết bị của bạn với không gian không đủ để hoạt động bình thường. Hãy xóa một số không gian lưu trữ trước.", - "core.course.manualcompletionnotsynced": "Hoàn thành thủ công không đồng bộ.", - "core.course.nocontentavailable": "Không có nội dung có sẵn tại thời điểm này.", - "core.course.overriddennotice": "Điểm cuối cùng của bạn từ hoạt động này đã được sửa đổi bằng tay.", - "core.course.refreshcourse": "Làm mới khóa học", - "core.course.sections": "Phần", - "core.course.useactivityonbrowser": "Bạn vẫn có thể sử dụng trình duyệt web của thiết bị.", - "core.course.warningmanualcompletionmodified": "Việc hoàn thành thủ công của một hoạt động đã được sửa đổi trên trang web.", - "core.course.warningofflinemanualcompletiondeleted": "Một số hướng dẫn sử dụng ngoại tuyến hoàn tất khóa '{{name}} ' đã bị xoá. {{error}}", - "core.coursenogroups": "Bạn không phải là thành viên của bất kỳ nhóm nào của khóa học này.", - "core.courses.allowguests": "Khoá học này cho phép khách vãng lai tham gia", - "core.courses.availablecourses": "Các khoá học hiện có", - "core.courses.cannotretrievemorecategories": "Danh mục sâu hơn mức {{$a}} không thể truy.", - "core.courses.categories": "Danh mục khoá học", - "core.courses.confirmselfenrol": "Bạn có chắc chắn muốn ghi danh mình trong khóa học này không?", - "core.courses.courses": "Khoá học", - "core.courses.downloadcourses": "Tải các khóa học", - "core.courses.enrolme": "Ghi danh cho tôi", - "core.courses.errorloadcategories": "error xảy ra trong khi tải loại.", - "core.courses.errorloadcourses": "error xảy ra khi đang tải các khóa học.", - "core.courses.errorloadplugins": "Các plugin theo yêu cầu của khóa học này không thể nạp đúng. Vui lòng tải lại ứng dụng để thử lần nữa.", - "core.courses.errorsearching": "error xảy ra khi tìm kiếm.", - "core.courses.errorselfenrol": "error xảy ra khi tự ghi danh.", - "core.courses.filtermycourses": "Lọc các khóa học của tôi", - "core.courses.frontpage": "Trang chủ", - "core.courses.ignore": "Bỏ qua", - "core.courses.mycourses": "Các khoá học của tôi", - "core.courses.mymoodle": "Nhà của tôi", - "core.courses.nocourses": "Không có thông tin khóa học để hiện", - "core.courses.nocoursesyet": "Không có khoá học nào trong mục này", - "core.courses.notenroled": "Bạn chưa ghi danh vào khóa này", - "core.courses.notenrollable": "Bạn không thể ghi danh mình trong khóa học này.", - "core.courses.password": "Khóa tuyển sinh", - "core.courses.paymentrequired": "Khoá học này có thu phí.", - "core.courses.reload": "Nạp lại", - "core.courses.search": "Tìm kiếm", - "core.courses.searchcourses": "Tìm kiếm khoá học", - "core.courses.searchcoursesadvice": "Bạn có thể sử dụng nút khóa học tìm kiếm để tìm các khóa học để truy cập như một khách hoặc ghi danh cho mình trong các khóa học cho phép nó.", - "core.courses.selfenrolment": "Tự tuyển sinh", - "core.courses.totalcoursesearchresults": "Tổng số khóa học: {{$a}}", - "core.currentdevice": "Thiết bị hiện tại", - "core.datastoredoffline": "Dữ liệu được lưu trữ trong thiết bị vì nó không thể gửi. Nó sẽ được gửi tự động sau đó.", - "core.date": "Ngày", - "core.day": "ngày", - "core.days": "ngày", - "core.decsep": ",", - "core.delete": "Xoá", - "core.deletedoffline": "Đã xóa ngoại tuyến", - "core.deleting": "Xóa", - "core.description": "Mô tả", - "core.dfdaymonthyear": "MM-DD-YYYY", - "core.dfdayweekmonth": "DDD, D MMM", - "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", - "core.dflastweekdate": "Ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.discard": "Loại bỏ", - "core.dismiss": "Bỏ", - "core.done": "Thực hiện", - "core.download": "Tải xuống", - "core.downloaded": "Tải về", - "core.downloading": "Tải", - "core.edit": "Chỉnh sửa ", - "core.emptysplit": "Trang này sẽ xuất hiện trống nếu bảng điều khiển bên trái trống hoặc đang tải.", - "core.error": "Lỗi", - "core.errorchangecompletion": "Lỗi xảy ra khi thay đổi trạng thái hoàn thành. Vui lòng thử lại.", - "core.errordeletefile": "Lỗi xóa tệp. Vui lòng thử lại.", - "core.errordownloading": "Lỗi tải xuống tệp.", - "core.errordownloadingsomefiles": "Lỗi tải xuống tệp. Một số tệp có thể bị thiếu.", - "core.errorfileexistssamename": "Tệp với tên này đã tồn tại.", - "core.errorinvalidform": "Biểu mẫu chứa dữ liệu không hợp lệ. Vui lòng kiểm tra tất cả các trường bắt buộc được điền và dữ liệu hợp lệ.", - "core.errorinvalidresponse": "Phản hồi không hợp lệ nhận được. Vui lòng liên hệ với quản trị viên trang web của bạn nếu error vẫn tồn tại.", - "core.errorloadingcontent": "Lỗi tải nội dung.", - "core.errorofflinedisabled": "Duyệt offline bị vô hiệu hoá trên trang web của bạn. Bạn cần kết nối với Internet để sử dụng ứng dụng.", - "core.erroropenfilenoapp": "Lỗi khi mở tệp: không tìm thấy ứng dụng nào để mở loại tệp này.", - "core.erroropenfilenoextension": "Lỗi khi mở tệp: tệp không có phần mở rộng.", - "core.erroropenpopup": "Hoạt động này đang cố gắng mở một popup. Điều này không được hỗ trợ trong ứng dụng.", - "core.errorrenamefile": "Lỗi đổi tên tệp. Vui lòng thử lại.", - "core.errorsomedatanotdownloaded": "Nếu bạn đã tải về hoạt động này, xin vui lòng nhận thấy rằng một số dữ liệu không được tải về trong quá trình tải về cho hiệu suất và dữ liệu sử dụng lý do.", - "core.errorsync": "Lỗi đã xảy ra khi đồng bộ. Vui lòng thử lại.", - "core.errorsyncblocked": "Điều này {{$a}} không thể được đồng bộ ngay bây giờ vì một quá trình liên tục. Vui lòng thử lại sau. Nếu sự cố vẫn tiếp diễn, hãy thử khởi động lại ứng dụng.", - "core.filename": "Tên tệp", - "core.filenameexist": "Tên tệp đã tồn tại: {{$a}}", - "core.fileuploader.audio": "Âm thanh", - "core.fileuploader.camera": "Camera", - "core.fileuploader.confirmuploadfile": "Bạn định tải lên {{size}}. Bạn có chắc bạn muốn tiếp tục không?", - "core.fileuploader.confirmuploadunknownsize": "Không thể tính toán kích thước của quá trình tải lên. Bạn có chắc bạn muốn tiếp tục không?", - "core.fileuploader.errorcapturingaudio": "Lỗi chụp âm thanh.", - "core.fileuploader.errorcapturingimage": "Lỗi chụp ảnh.", - "core.fileuploader.errorcapturingvideo": "Lỗi chụp video.", - "core.fileuploader.errorgettingimagealbum": "Lỗi nhận được hình ảnh từ album.", - "core.fileuploader.errormustbeonlinetoupload": "Bạn phải trực tuyến để tải lên các tập tin.", - "core.fileuploader.errornoapp": "Bạn không có một ứng dụng được cài đặt để thực hiện hành động này.", - "core.fileuploader.errorreadingfile": "Lỗi đọc tệp.", - "core.fileuploader.errorwhileuploading": "Xảy ra error trong quá trình tải lên tệp.", - "core.fileuploader.file": "Tập tin", - "core.fileuploader.fileuploaded": "Tệp đã được tải lên thành công.", - "core.fileuploader.invalidfiletype": "Loại tệp {{$a}} không thể chấp nhận được.", - "core.fileuploader.maxbytesfile": "Tệp {{$a.file}} quá lớn. Kích thước tối đa bạn có thể tải lên là {{$a.size}}.", - "core.fileuploader.photoalbums": "Album ảnh", - "core.fileuploader.readingfile": "Đọc tập tin", - "core.fileuploader.readingfileperc": "Đọc tập tin: {{$a}}%", - "core.fileuploader.selectafile": "Chọn một tập tin", - "core.fileuploader.uploadafile": "Tải tệp lên", - "core.fileuploader.uploading": "Tải lên", - "core.fileuploader.uploadingperc": "Tải lên: {{$a}}%", - "core.fileuploader.video": "Video", - "core.filter": "Bộ lọc", - "core.folder": "Thư mục", - "core.forcepasswordchangenotice": "Bạn cần phải đổi lại mật khẩu trước khi tiếp tục", - "core.fulllistofcourses": "Tất cả các khoá học", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "Trung bình", - "core.grades.badgrade": "Điểm cung cấp không hợp lệ", - "core.grades.feedback": "Phản hồi", - "core.grades.grade": "Điểm", - "core.grades.gradeitem": "Mục điểm", - "core.grades.grades": "Điểm", - "core.grades.nooutcome": "Không đầu ra", - "core.grades.percentage": "Phần trăm", - "core.grades.range": "Khoảng", - "core.grades.rank": "Hạng", - "core.group": "Nhóm", - "core.groupsseparate": "Nhóm tách biệt", - "core.groupsvisible": "Nhóm thấy nhau được", - "core.h5p.play": "Chơi H5P", - "core.hasdatatosync": "{{$a}} này có dữ liệu ngoại tuyến được đồng bộ hóa.", - "core.help": "Trợ giúp", - "core.hide": "Đóng", - "core.hour": "giờ", - "core.hours": "giờ", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "Hình ảnh", - "core.imageviewer": "Trình xem ảnh", - "core.info": "Thông tin", - "core.invalidformdata": "Dữ liệu mẫu đơn sai", - "core.labelsep": ":", - "core.lastaccess": "Truy cập gần nhất", - "core.lastdownloaded": "Tải về lần cuối", - "core.lastmodified": "Sửa lần cuối", - "core.lastsync": "Đồng bộ cuối cùng", - "core.list": "Danh sách", - "core.listsep": ";", - "core.loadmore": "Tải thêm", - "core.location": "Địa chỉ", - "core.login.auth_email": "Chứng thực dựa trên Email", - "core.login.authenticating": "Xác thực", - "core.login.cancel": "Huỷ bỏ", - "core.login.changepassword": "Đổi mật khẩu", - "core.login.changepasswordbutton": "Mở trang thay đổi mật khẩu", - "core.login.changepasswordhelp": "Nếu bạn gặp sự cố thay đổi mật khẩu, hãy liên hệ với người quản trị trang web của bạn. \"Quản trị viên trang web\" là những người quản lý Moodle tại trường học của bạn/trường đại học/công ty hoặc tổ chức học tập. Nếu bạn không biết làm thế nào để liên lạc với họ, xin vui lòng liên hệ với giáo viên của bạn/huấn luyện.", - "core.login.changepasswordinstructions": "Bạn không thể thay đổi mật khẩu trong ứng dụng. Vui lòng nhấp vào nút sau để mở trang web trong trình duyệt web để thay đổi mật khẩu của bạn. Đưa vào tài khoản bạn cần phải đóng trình duyệt sau khi thay đổi mật khẩu như bạn sẽ không được chuyển hướng đến các ứng dụng.", - "core.login.changepasswordlogoutinstructions": "Nếu bạn muốn thay đổi trang web hoặc đăng xuất, vui lòng nhấp vào nút sau:", - "core.login.changepasswordreconnectinstructions": "Bấm vào nút sau để kết nối lại với trang web. (Hãy vào tài khoản mà nếu bạn thay đổi mật khẩu của bạn chưa thành công, bạn sẽ trở lại màn hình trước đó).", - "core.login.confirmdeletesite": "Bạn có chắc bạn muốn xóa bỏ site {{sitename}} không?", - "core.login.connect": "Kết nối!", - "core.login.connecttomoodle": "Kết nối với Moodle", - "core.login.connecttomoodleapp": "Bạn đang cố gắng kết nối với một trang web Moodle thường xuyên. Vui lòng tải ứng dụng Moodle chính thức để truy cập trang web này.", - "core.login.connecttoworkplaceapp": "Bạn đang cố gắng kết nối với một trang web Moodle Workplace. Vui lòng tải ứng dụng Moodle Workplace xuống để truy cập trang web này.", - "core.login.contactyouradministrator": "Liên hệ với quản trị viên trang web của bạn để biết thêm trợ giúp.", - "core.login.contactyouradministratorissue": "Vui lòng hỏi quản trị viên trang web của bạn để kiểm tra vấn đề sau: {{$a}}", - "core.login.createaccount": "Tạo tài khoản mới", - "core.login.createuserandpass": "Chọn kí danh và mật khẩu", - "core.login.credentialsdescription": "Vui lòng cung cấp tên người dùng và mật khẩu của bạn để đăng nhập.", - "core.login.emailconfirmsent": "

                      Một bức thư điện tử đã được gửi đến cho bạn tại địa chỉ sau: {{$a}}

                      \n

                      Trong thư này có các hướng dẫn giúp bạn hoàn tất việc đăng kí thành viên.

                      \n

                      Nếu vẫn gặp khó khăn, hãy liên hệ với Quản trị viên của hệ thống.

                      ", - "core.login.emailconfirmsentnoemail": "

                      Một email phải được gửi đến địa chỉ của bạn.

                      nó chứa các hướng dẫn dễ dàng để hoàn thành đăng ký của bạn.

                      nếu bạn tiếp tục gặp khó khăn, hãy liên hệ với quản trị viên trang web.

                      ", - "core.login.emailnotmatch": "Email không khớp", - "core.login.erroraccesscontrolalloworigin": "Cuộc gọi có nguồn gốc chéo mà bạn đang cố thực hiện đã bị từ chối. Vui lòng kiểm tra https://docs.moodle.org/dev/Moodle_Mobile_development_USE_Chrom_or_Chromium", - "core.login.errordeletesite": "error đã xảy ra trong khi xoá site này. Vui lòng thử lại.", - "core.login.errorupdatesite": "error đã xảy ra khi cập nhật mã thông báo của trang web.", - "core.login.findyoursite": "Tìm trang web của bạn", - "core.login.firsttime": "Bạn chưa đăng kí thành viên?", - "core.login.forcepasswordchangenotice": "Bạn cần phải đổi lại mật khẩu trước khi tiếp tục", - "core.login.forgotten": "Bạn quên kí danh hoặc mật khẩu?", - "core.login.help": "Trợ giúp", - "core.login.helpmelogin": "

                      có rất nhiều hàng ngàn Moodle các trang web trên toàn thế giới. Ứng dụng này chỉ có thể kết nối với Moodle các trang web có kích hoạt đặc biệt truy cập ứng dụng di động.

                      nếu bạn có thể ' t kết nối với trang web Moodle của bạn sau đó bạn cần phải liên hệ với quản trị viên trang web của bạn và yêu cầu họ đọc http://Docs.Moodle.org/en/Mobile_app

                      để kiểm tra các ứng dụng trong một loại trang web giới thiệu Moodle giáo viên hoặc học sinh trong địa chỉ trang web trường và nhấp vào nút kết nối .

                      ", - "core.login.instructions": "Hướng dẫn", - "core.login.invalidaccount": "Vui lòng kiểm tra thông tin đăng nhập của bạn hoặc yêu cầu quản trị viên trang web của bạn kiểm tra cấu hình trang web.", - "core.login.invalidemail": "Địa chỉ thư điện tử không hợp lệ", - "core.login.invalidmoodleversion": "

                      Phiên bản trang web không hợp lệ Moodle. Ứng dụng Moodle chỉ hỗ trợ các hệ thống Moodle {{$a}} trở đi.

                      ", - "core.login.invalidsite": "URL trang web không hợp lệ.", - "core.login.invalidtime": "Thời gian không hợp lệ", - "core.login.invalidvaluemax": "Giá trị tối đa là {{$a}}", - "core.login.invalidvaluemin": "Giá trị tối thiểu là {{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle di động tính năng bổ sung kiểm tra trả lại một phản ứng bất ngờ. Bạn sẽ được xác thực bằng cách sử dụng dịch vụ di động chuẩn.", - "core.login.loggedoutssodescription": "Bạn phải xác nhận lại. Bạn cần phải đăng nhập vào trang web trong một cửa sổ trình duyệt.", - "core.login.login": "Đăng nhập", - "core.login.loginbutton": "Đăng nhập", - "core.login.logininsiterequired": "Bạn cần phải đăng nhập vào trang web trong một cửa sổ trình duyệt.", - "core.login.loginsteps": "Xin chào! Để có thể truy cập vào các khoá học, bạn cần phải đăng kí một tài khoản thành viên.\nMỗi khoá học còn có thể có thêm khóa truy cập riêng, mà sau này bạn sẽ cần đến. Các bước cần làm là như sau:\n
                        \n
                      1. Điền mẫu đăng kí thành viên với đầy đủ các thông tin cần thiết.
                      2. \n
                      3. Hệ thống sẽ gửi một bức thư tới địa chỉ điện thư của bạn.
                      4. \n
                      5. Đọc thư này và mở đường liên kết có trong thư.
                      6. \n
                      7. Tài khoản của bạn sẽ được xác nhận; từ đó bạn có thể đăng nhập vào hệ thống.
                      8. \n
                      9. Tiếp theo, chọn khoá học mà bạn muốn tham gia.
                      10. \n
                      11. Nếu khoá học ấy cần mật khẩu truy cập thì bạn phải khai báo mật khẩu mà giáo viên đã cung cấp. Như thế bạn sẽ được ghi danh vào khoá học.
                      12. \n
                      13. Đến lúc này bạn sẽ có quyền truy cập đầy đủ trong khoá học. Cũng từ khi đó, bạn có thể ghi danh và theo học các khoá học khác trên hệ thống này mà không cần phải đăng kí thành viên nữa. Mỗi lượt truy cập bạn đều phải sử dụng kí danh và mật khẩu đã đăng kí như trên.
                      14. \n
                      ", - "core.login.missingemail": "Xin cho biết địa chỉ điện thư", - "core.login.missingfirstname": "Xin cho biết tên và tên đệm", - "core.login.missinglastname": "Xin cho biết họ", - "core.login.mobileservicesnotenabled": "Truy cập di động không được bật trên trang web của bạn. Vui lòng liên hệ với quản trị viên trang web của bạn nếu bạn nghĩ rằng nó sẽ được kích hoạt.", - "core.login.mustconfirm": "Bạn cần xác nhận việc đăng kí của bạn", - "core.login.newaccount": "Tài khoản mới", - "core.login.notloggedin": "Bạn cần phải đăng nhập.", - "core.login.password": "Mật khẩu", - "core.login.passwordforgotten": "Mật khẩu bị quên", - "core.login.passwordforgotteninstructions2": "Để lấy lại mật khẩu, hãy cung cấp kí danh hay thư điện của bản vào bên dưới. Nếu bạn được tìm thấy trong CSDL, một thư điện sẽ được gửi đến bạn, cùng với những hướng dẫn về cách tái truy cập.", - "core.login.passwordrequired": "Yêu cầu mật khẩu", - "core.login.policyaccept": "Tôi hiểu và đồng ý", - "core.login.policyagree": "Bạn phải đồng ý với bản thoả thuận này trước khi tiếp tục sử dụng hệ thống này. Bạn có đồng ý?", - "core.login.policyagreement": "Bản thoả thuận chính sách trang", - "core.login.policyagreementclick": "Liên kết đến bản thỏa thuận chính sách trang", - "core.login.potentialidps": "Đăng nhập bằng tài khoản của bạn trên:", - "core.login.profileinvaliddata": "Giá trị không hợp lệ", - "core.login.recaptchachallengeimage": "reCAPTCHA thách thức hình ảnh", - "core.login.recaptchaexpired": "Xác minh đã hết hạn. Trả lời câu hỏi bảo mật một lần nữa.", - "core.login.recaptchaincorrect": "Câu trả lời câu hỏi bảo mật không chính xác.", - "core.login.reconnect": "Kết nối", - "core.login.reconnectdescription": "Mã thông báo xác thực của bạn không hợp lệ hoặc đã hết hạn. Bạn phải kết nối lại với trang web.", - "core.login.reconnectssodescription": "Mã thông báo xác thực của bạn không hợp lệ hoặc đã hết hạn. Bạn phải kết nối lại với trang web. Bạn cần phải đăng nhập vào trang web trong một cửa sổ trình duyệt.", - "core.login.searchby": "Tìm theo:", - "core.login.security_question": "Câu hỏi bảo mật", - "core.login.selectacountry": "Chọn quốc gia", - "core.login.selectsite": "Vui lòng chọn trang web của bạn:", - "core.login.signupplugindisabled": "{{$a}} không được phép.", - "core.login.siteaddress": "Địa chỉ trang web", - "core.login.sitehasredirect": "Trang web của bạn chứa ít nhất một chuyển hướng HTTP. Ứng dụng không thể thực hiện theo chuyển hướng, đây có thể là vấn đề của việc ngăn không cho ứng dụng kết nối với trang web của bạn.", - "core.login.siteinmaintenance": "Trang web của bạn đang ở chế độ bảo trì", - "core.login.sitepolicynotagreederror": "Chính sách trang web không đồng ý.", - "core.login.siteurl": "URL trang web", - "core.login.siteurlrequired": "URL trang web yêu cầu tức là http://www.yourmoodlesite.org", - "core.login.startsignup": "Tạo tài khoản mới", - "core.login.stillcantconnect": "Vẫn không thể kết nối?", - "core.login.supplyinfo": "Các thông tin khác", - "core.login.username": "Kí danh", - "core.login.usernameoremail": "Xin vui lòng cho biết kí danh hoặc là địa chỉ điện thư", - "core.login.usernamerequired": "Tên người dùng yêu cầu", - "core.login.usernotaddederror": "Người dùng không được thêm - lỗi", - "core.login.visitchangepassword": "Bạn có muốn truy cập trang web để thay đổi mật khẩu không?", - "core.login.webservicesnotenabled": "Dịch vụ web không được kích hoạt trong trang web của bạn. Vui lòng liên hệ với quản trị viên trang web của bạn nếu bạn nghĩ rằng họ sẽ được kích hoạt.", - "core.lostconnection": "Mã thông báo xác thực của bạn không hợp lệ hoặc đã hết hạn. Bạn sẽ phải kết nối lại với trang web.", - "core.mainmenu.changesite": "Thay đổi trang web", - "core.mainmenu.help": "Trợ giúp", - "core.mainmenu.logout": "Thoát", - "core.mainmenu.website": "Trang web", - "core.maxsizeandattachments": "Kích cỡ tối đa đối với các tập tin mới: {{$a.size}}, đính kèm tối đa: {{$a.attachments}}", - "core.min": "phút", - "core.mins": "phút", - "core.misc": "Nội dung khác", - "core.mod_assignment": "Bài tập (2.2)", - "core.mod_chat": "Phòng họp trực tuyến", - "core.mod_choice": "Câu hỏi thăm dò", - "core.mod_data": "Cơ sở dữ liệu", - "core.mod_database": "Cơ sở dữ liệu", - "core.mod_file": "Tập tin", - "core.mod_forum": "Diễn đàn", - "core.mod_glossary": "Bảng từ", - "core.mod_lesson": "Bài học", - "core.mod_quiz": "Đề thi", - "core.mod_scorm": "Gói SCORM", - "core.mod_survey": "Cuộc khảo sát", - "core.mod_workshop": "Tập huấn", - "core.moduleintro": "Mô tả", - "core.more": "tiếp", - "core.name": "Tên", - "core.networkerroriframemsg": "Nội dung này không khả dụng gián tuyến. Hãy kết nối với Internet và thử lại.", - "core.networkerrormsg": "Đã có một vấn đề kết nối với trang web. Vui lòng kiểm tra kết nối của bạn và thử lại.", - "core.never": "Chưa lần nào", - "core.next": "Tiếp theo", - "core.no": "Không", - "core.nocomments": "Không có lời bình", - "core.nograde": "Chưa có điểm", - "core.none": "Không", - "core.nooptionavailable": "Không có tùy chọn có sẵn", - "core.nopasswordchangeforced": "Bạn không thể tiến hành mà không thay đổi mật khẩu của bạn.", - "core.nopermissionerror": "Xin error, nhưng bạn hiện không có quyền để làm điều đó", - "core.nopermissions": "Xin lỗi, nhưng hiện tại bạn không được phép làm điều đó ({{$a}})", - "core.noresults": "Không có kết quả nào", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "Không mở được hồ sơ này do học viên không được ghi danh vào khoá học.", - "core.notice": "Lưu ý", - "core.notingroup": "Xin lỗi, bạn cần phải là thành viên của nhóm mới có thể tham gia hoạt động này.", - "core.notsent": "Không gửi", - "core.now": "hiện thời", - "core.nummore": "{{$a}} nhiều hơn", - "core.numwords": "{{$a}} từ", - "core.offline": "Không trực tuyến", - "core.ok": "Đồng ý", - "core.online": "Trực tuyến", - "core.openfullimage": "Click vào đây để hiển thị hình ảnh kích thước đầy đủ", - "core.openinbrowser": "Mở trong trình duyệt", - "core.pagea": "Trang {{$a}}", - "core.paymentinstant": "Nhấn nút dưới đây để trả phí và ghi danh vào khoá học!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "Điện thoại", - "core.pictureof": "Hình của {{$a}}", - "core.previous": "Trước", - "core.pulltorefresh": "Kéo để làm mới", - "core.question.answer": "Câu trả lời", - "core.question.answersaved": "Câu trả lời đã được lưu", - "core.question.cannotdeterminestatus": "Không thể xác định trạng thái", - "core.question.complete": "Hoàn thành", - "core.question.correct": "Đúng", - "core.question.errorattachmentsnotsupported": "Ứng dụng không hỗ trợ đính kèm tập tin để câu trả lời nào được nêu ra.", - "core.question.errorinlinefilesnotsupported": "Ứng dụng không hỗ trợ chỉnh sửa tập tin nội tuyến được nêu ra.", - "core.question.errorquestionnotsupported": "Loại câu hỏi này không được ứng dụng hỗ trợ: {{$a}}.", - "core.question.feedback": "Phản hồi", - "core.question.howtodraganddrop": "Nhấn để chọn sau đó nhấn để thả.", - "core.question.incorrect": "Sai", - "core.question.information": "Thông tin", - "core.question.invalidanswer": "Câu trả lời không hoàn chỉnh", - "core.question.notanswered": "Không trả lời", - "core.question.notyetanswered": "Chưa trả lời", - "core.question.partiallycorrect": "Đúng một phần", - "core.question.questionmessage": "Câu hỏi {{$a}}: {{$b}}", - "core.question.questionno": "Câu hỏi {{$a}}", - "core.question.requiresgrading": "Yêu cầu chấm điểm", - "core.rating.aggregateavg": "Trung bình điểm đánh giá", - "core.rating.aggregatecount": "Số đánh giá", - "core.rating.aggregatemax": "Điểm đánh giá tối đa", - "core.rating.aggregatemin": "Điểm đánh giá tối thiểu", - "core.rating.aggregatesum": "Tổng điểm đánh giá", - "core.rating.noratings": "Không có đánh giá được gửi đi", - "core.rating.rating": "Điểm đánh giá", - "core.rating.ratings": "Điểm đánh giá", - "core.redirectingtosite": "Bạn sẽ được chuyển hướng đến trang web.", - "core.refresh": "Tải lại", - "core.remove": "Xoá", - "core.removefiles": "Loại bỏ các tập tin {{$a}}", - "core.required": "Bắt buộc", - "core.requireduserdatamissing": "Người dùng này thiếu một số dữ liệu yêu cầu hồ sơ. Vui lòng nhập dữ liệu vào trang web của bạn và thử lại.
                      {{$a}}", - "core.resourcedisplayopen": "Mở", - "core.resources": "Tài nguyên", - "core.restore": "Phục hồi", - "core.restricted": "Hạn chế", - "core.retry": "Thử lại", - "core.savechanges": "Lưu những thay đổi", - "core.search": "Tìm kiếm", - "core.searching": "Tìm kiếm", - "core.searchresults": "Kết quả tìm kiếm", - "core.sec": "giây", - "core.secs": "giây", - "core.seemoredetail": "Kích vào đây để xem chi tiết hơn.", - "core.selectacategory": "Hãy chọn một chuyên mục", - "core.selectacourse": "Chọn một khóa học", - "core.selectagroup": "Chọn một nhóm", - "core.serverconnection": "Lỗi kết nối máy chủ", - "core.settings.about": "Về", - "core.settings.cannotsyncoffline": "Không thể đồng bộ hóa ngoại tuyến.", - "core.settings.cannotsyncwithoutwifi": "Không thể đồng bộ hóa vì các cài đặt hiện tại chỉ cho phép đồng bộ khi kết nối với Wi-Fi. Vui lòng kết nối với mạng Wi-Fi.", - "core.settings.colorscheme": "Lược đồ màu", - "core.settings.colorscheme-auto": "Tự động (dựa trên cài đặt hệ thống)", - "core.settings.colorscheme-dark": "Tối", - "core.settings.colorscheme-light": "Ánh sáng", - "core.settings.compilationinfo": "Biên soạn thông tin", - "core.settings.cordovadevicemodel": "Mô hình thiết bị Cordova", - "core.settings.cordovadeviceosversion": "Phiên bản hệ điều hành thiết bị Cordova", - "core.settings.cordovadeviceplatform": "Nền tảng thiết bị Cordova", - "core.settings.cordovadeviceuuid": "Thiết bị Cordova UUID", - "core.settings.cordovaversion": "Phiên bản Cordova", - "core.settings.currentlanguage": "Ngôn ngữ hiện thời", - "core.settings.debugdisplay": "Hiện thông điệp gỡ rối", - "core.settings.debugdisplaydescription": "Nếu được kích hoạt, các phương thức lỗi sẽ hiển thị nhiều dữ liệu hơn về lỗi nếu có thể.", - "core.settings.deletesitefiles": "Bạn có chắc chắn muốn xóa bỏ các tệp đã tải xuống và dữ liệu đã lưu trong bộ nhớ đệm từ trang web ' {{sitename}} '? Bạn sẽ không thể sử dụng các ứng dụng trong chế độ offline.", - "core.settings.deletesitefilestitle": "Xóa tập tin trang web", - "core.settings.deviceinfo": "Thông tin thiết bị", - "core.settings.deviceos": "Hệ điều hành thiết bị", - "core.settings.disableall": "Tạm thời vô hiệu thóa thông báo", - "core.settings.displayformat": "Định dạng hiển thị", - "core.settings.enabledownloadsection": "Bật phần tải xuống", - "core.settings.enablefirebaseanalytics": "Bật phân tích Firebase", - "core.settings.enablefirebaseanalyticsdescription": "Nếu được bật, ứng dụng sẽ thu thập sử dụng dữ liệu ẩn danh.", - "core.settings.enablerichtexteditor": "Bật trình soạn thảo văn bản", - "core.settings.enablerichtexteditordescription": "Nếu được bật, một trình soạn thảo văn bản sẽ có sẵn khi nhập nội dung.", - "core.settings.enablesyncwifi": "Chỉ cho phép đồng bộ khi trên Wi-Fi", - "core.settings.entriesincache": "{{$a}} mục trong bộ nhớ cache", - "core.settings.errordeletesitefiles": "Lỗi xóa các tập tin trang web.", - "core.settings.errorsyncsite": "Lỗi đồng bộ hóa dữ liệu trang web. Vui lòng kiểm tra kết nối Internet của bạn và thử lại.", - "core.settings.estimatedfreespace": "Ước tính không gian trống", - "core.settings.filesystemroot": "Gốc hệ thống tập tin", - "core.settings.fontsize": "Cỡ chữ", - "core.settings.general": "Thông tin chung", - "core.settings.language": "Ngôn ngữ", - "core.settings.license": "Giấy phép", - "core.settings.localnotifavailable": "Thông báo địa phương có sẵn", - "core.settings.locationhref": "URL xem web", - "core.settings.locked": "Bị khóa", - "core.settings.loggedin": "Trực tuyến", - "core.settings.loggedoff": "Không trực tuyến", - "core.settings.navigatorlanguage": "Ngôn ngữ Navigator", - "core.settings.navigatoruseragent": "Navigator userAgent", - "core.settings.networkstatus": "Trạng thái kết nối Internet", - "core.settings.preferences": "Tuỳ chọn", - "core.settings.privacypolicy": "Chính sách bảo mật", - "core.settings.pushid": "Thông báo đẩy ID", - "core.settings.reportinbackground": "Tự động báo cáo lỗi", - "core.settings.settings": "Thiết lập", - "core.settings.showdownloadoptions": "Hiển thị tùy chọn tải xuống", - "core.settings.sites": "Các hệ thống", - "core.settings.spaceusage": "Không gian sử dụng", - "core.settings.synchronization": "Synchronization", - "core.settings.synchronizenow": "Đồng bộ ngay bây giờ", - "core.settings.syncsettings": "Cài đặt đồng bộ hóa", - "core.settings.total": "Tổng", - "core.settings.wificonnection": "Kết nối Wi-Fi", - "core.sharedfiles.chooseaccountstorefile": "Chọn một tài khoản để lưu trữ tệp.", - "core.sharedfiles.chooseactionrepeatedfile": "Tệp với tên này đã tồn tại. Bạn có muốn thay thế tệp hiện có hoặc đổi tên thành \"{{$a}}\" không?", - "core.sharedfiles.errorreceivefilenosites": "Không có các trang web được lưu trữ. Xin vui lòng thêm một trang web trước khi chia sẻ một tập tin với các ứng dụng.", - "core.sharedfiles.nosharedfiles": "Không có tệp chia sẻ được lưu trữ trên trang web này.", - "core.sharedfiles.nosharedfilestoupload": "Bạn không có tập tin để tải lên ở đây. Nếu bạn muốn tải lên một tập tin từ một ứng dụng khác, xác định vị trí các tập tin và nhấp vào nút ' mở trong '.", - "core.sharedfiles.rename": "Đổi tên", - "core.sharedfiles.replace": "Thay thế", - "core.sharedfiles.sharedfiles": "Tệp chia sẻ", - "core.sharedfiles.successstorefile": "Tệp đã lưu thành công. Chọn tập tin để tải lên các tập tin riêng tư của bạn hoặc sử dụng trong một hoạt động.", - "core.show": "Mở", - "core.site": "Hệ thống", - "core.sitehome.sitehome": "Trang chủ hê thống", - "core.sitehome.sitenews": "Tin tức chung", - "core.sitemaintenance": "Hệ thống đang được bảo trì nên không thể truy cập được", - "core.sizeb": "byte", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.sorry": "Xin lỗi...", - "core.sort": "Sắp xếp", - "core.sortby": "Sắp xếp theo", - "core.strftimedate": "%d %B %Y", - "core.strftimedatefullshort": "%d/%m/%y", - "core.strftimedateshort": "%d %B", - "core.strftimedatetime": "%d %B %Y, %I:%M %p", - "core.strftimedatetimeshort": "%d/%m/%Y %H:%M", - "core.strftimedaydate": "%A, %d %B %Y", - "core.strftimedaydatetime": "%A, %d %B %Y, %I:%M %p", - "core.strftimedayshort": "%A, %d %B", - "core.strftimedaytime": "%a, %H:%M", - "core.strftimemonthyear": "%B %Y", - "core.strftimerecent": "%d/%m/%Y, %H:%M", - "core.strftimerecentfull": "%a, %d %b %Y, %I:%M %p", - "core.strftimetime": "%I:%M %p", - "core.submit": "Gửi", - "core.success": "Thành công", - "core.tablet": "Máy tính bảng", - "core.tag.errorareanotsupported": "Vùng thẻ này không được ứng dụng hỗ trợ.", - "core.tag.noresultsfor": "Không có kết quả cho \"{{$a}}\"", - "core.tag.searchtags": "Các thẻ tìm kiếm", - "core.tag.tag": "Thẻ từ khoá", - "core.tag.tags": "Thẻ từ khoá", - "core.tag.warningareasnotsupported": "Một số vùng thẻ không được hiển thị vì chúng không được ứng dụng hỗ trợ.", - "core.teachers": "Giáo viên", - "core.thereisdatatosync": "Có ngoại tuyến {{$a}} được đồng bộ hóa.", - "core.thisdirection": "ltr", - "core.time": "Thời gian", - "core.timesup": "Thời gian vượt quá !", - "core.today": "Hôm nay", - "core.tryagain": "Thử lại", - "core.twoparagraphs": "{{p1}}

                      {{p2}}", - "core.uhoh": "Uh oh!", - "core.unexpectederror": "Lỗi không mong muốn. Hãy đóng và mở lại các ứng dụng sau đó thử một lần nữa.", - "core.unicodenotsupported": "Một số biểu tượng cảm xúc không được hỗ trợ trên trang web này. Các ký tự này sẽ bị xóa khi thư được gửi.", - "core.unicodenotsupportedcleanerror": "Văn bản trống đã được tìm thấy khi làm sạch ký tự Unicode.", - "core.unknown": "Biết", - "core.unlimited": "Không giới hạn", - "core.unzipping": "Unzipping", - "core.updaterequired": "Yêu cầu cập nhật ứng dụng", - "core.updaterequireddesc": "Vui lòng cập nhật ứng dụng của bạn lên phiên bản {{$a}}", - "core.upgraderunning": "Trang đang được nâng cấp, hãy thử lại sau.", - "core.user": "Thành viên", - "core.user.address": "Địa chỉ", - "core.user.city": "Tỉnh/Thành phố", - "core.user.contact": "Liên hệ", - "core.user.country": "Quốc gia", - "core.user.description": "Mô tả", - "core.user.detailsnotavailable": "Các chi tiết của người dùng này không có sẵn cho bạn.", - "core.user.editingteacher": "Giáo viên", - "core.user.email": "Thư điện tử", - "core.user.emailagain": "Thư điện tử (xác nhận)", - "core.user.errorloaduser": "Lỗi tải người dùng.", - "core.user.firstname": "Tên đệm và tên", - "core.user.interests": "Sở thích", - "core.user.lastname": "Họ", - "core.user.manager": "Người quản lý", - "core.user.newpicture": "Ảnh mới", - "core.user.noparticipants": "Không tìm thấy người tham gia khóa học này", - "core.user.participants": "Danh sách thành viên", - "core.user.phone1": "Điện thoại", - "core.user.phone2": "Điện thoại di động", - "core.user.roles": "Vai trò", - "core.user.sendemail": "Email", - "core.user.student": "Học viên", - "core.user.teacher": "Giáo viên trợ giảng", - "core.user.webpage": "Trang web", - "core.userdeleted": "Tài khoản thành viên đã được xóa", - "core.userdetails": "Chi tiết người dùng", - "core.users": "Thành viên", - "core.view": "Xem", - "core.viewcode": "Xem mã", - "core.vieweditor": "Xem biên tập", - "core.viewembeddedcontent": "Xem nội dung nhúng", - "core.viewprofile": "Xem hồ sơ", - "core.warningofflinedatadeleted": "Dữ liệu ngoại tuyến từ {{component}} ' {{name}} ' đã bị xóa. {{error}}", - "core.whoops": "Rất tiếc!", - "core.whyisthishappening": "Tại sao điều này xảy ra?", - "core.wsfunctionnotavailable": "Chức năng dịch vụ web không khả dụng.", - "core.year": "năm", - "core.years": "năm", - "core.yes": "Có", - "core.youreoffline": "Bạn đang offline", - "core.youreonline": "Bạn đang trở lại trực tuyến" -} \ No newline at end of file diff --git a/src/assets/lang/zh-cn.json b/src/assets/lang/zh-cn.json deleted file mode 100644 index c9f3f1c50..000000000 --- a/src/assets/lang/zh-cn.json +++ /dev/null @@ -1,2169 +0,0 @@ -{ - "addon.badges.alignment": "对齐", - "addon.badges.badgedetails": "勋章详情", - "addon.badges.badges": "勋章", - "addon.badges.bendorsement": "签注", - "addon.badges.claimcomment": "签注意见", - "addon.badges.claimid": "声明网址", - "addon.badges.contact": "联系", - "addon.badges.dateawarded": "颁发日期", - "addon.badges.expired": "已过期", - "addon.badges.expirydate": "到期日", - "addon.badges.imageauthoremail": "图片作者的电子邮件", - "addon.badges.imageauthorname": "图片作者的名字", - "addon.badges.imageauthorurl": "图片作者的网址", - "addon.badges.imagecaption": "图片说明", - "addon.badges.issuancedetails": "勋章有效期", - "addon.badges.issuerdetails": "颁发人详情", - "addon.badges.issueremail": "电子邮件", - "addon.badges.issuername": "颁发人名称", - "addon.badges.issuerurl": "颁发人URL", - "addon.badges.language": "语言", - "addon.badges.noalignment": "此勋章没有任何指定的外部技能或标准。", - "addon.badges.nobadges": "没有可用的勋章。", - "addon.badges.norelated": "此勋章没有任何关联勋章。", - "addon.badges.recipientdetails": "获得者详情", - "addon.badges.relatedbages": "关联勋章", - "addon.badges.version": "版本", - "addon.badges.warnexpired": "(此勋章已过期!)", - "addon.block_activitymodules.pluginname": "活动", - "addon.block_activityresults.pluginname": "活动结果", - "addon.block_badges.pluginname": "最新勋章", - "addon.block_blogmenu.pluginname": "博客菜单", - "addon.block_blogrecent.pluginname": "最近博客更新", - "addon.block_blogtags.pluginname": "博客标签", - "addon.block_calendarmonth.pluginname": "日历", - "addon.block_calendarupcoming.pluginname": "即将到来的事件", - "addon.block_comments.pluginname": "评论", - "addon.block_completionstatus.pluginname": "课程进度状态", - "addon.block_glossaryrandom.pluginname": "随机词汇条目", - "addon.block_learningplans.pluginname": "学习计划", - "addon.block_myoverview.all": "全部(除隐藏外)", - "addon.block_myoverview.allincludinghidden": "所有", - "addon.block_myoverview.favourites": "标星的", - "addon.block_myoverview.future": "尚未开始的", - "addon.block_myoverview.hiddencourses": "已隐藏的", - "addon.block_myoverview.inprogress": "进行中", - "addon.block_myoverview.lastaccessed": "上次访问", - "addon.block_myoverview.morecourses": "更多课程", - "addon.block_myoverview.nocourses": "没有课程", - "addon.block_myoverview.past": "过去的", - "addon.block_myoverview.pluginname": "课程概览", - "addon.block_myoverview.shortname": "简称", - "addon.block_myoverview.title": "课程名称", - "addon.block_newsitems.pluginname": "最新通告", - "addon.block_onlineusers.pluginname": "在线用户", - "addon.block_privatefiles.pluginname": "私人文件", - "addon.block_recentactivity.pluginname": "课程动态", - "addon.block_recentlyaccessedcourses.nocourses": "没有最近访问的课程", - "addon.block_recentlyaccessedcourses.pluginname": "最近访问的课程", - "addon.block_recentlyaccesseditems.noitems": "没有最近的项目", - "addon.block_recentlyaccesseditems.pluginname": "最近访问的项目", - "addon.block_rssclient.pluginname": "远程RSS种子", - "addon.block_selfcompletion.pluginname": "自设完成", - "addon.block_sitemainmenu.pluginname": "主菜单", - "addon.block_starredcourses.nocourses": "没有标星的课程", - "addon.block_starredcourses.pluginname": "标星的课程", - "addon.block_tags.pluginname": "标签", - "addon.block_timeline.duedate": "到期日", - "addon.block_timeline.next30days": "未来30天", - "addon.block_timeline.next3months": "未来3个月", - "addon.block_timeline.next6months": "未来6个月", - "addon.block_timeline.next7days": "未来7天", - "addon.block_timeline.nocoursesinprogress": "没有正在进行的课程", - "addon.block_timeline.noevents": "没有即将到期的活动", - "addon.block_timeline.overdue": "过期的", - "addon.block_timeline.pluginname": "时间轴", - "addon.block_timeline.sortbycourses": "按课程名称排序", - "addon.block_timeline.sortbydates": "按日期排序", - "addon.blog.blog": "博客", - "addon.blog.blogentries": "博客条目", - "addon.blog.errorloadentries": "加载博客条目出错。", - "addon.blog.linktooriginalentry": "链接到原始的博客条目", - "addon.blog.noentriesyet": "这里没有可见条目", - "addon.blog.publishtonoone": "自己(草稿)", - "addon.blog.publishtosite": "网站上的任何人", - "addon.blog.publishtoworld": "世界上的任何人", - "addon.blog.showonlyyourentries": "只显示您的条目", - "addon.blog.siteblogheading": "站点博客", - "addon.calendar.allday": "整天", - "addon.calendar.calendar": "日程管理", - "addon.calendar.calendarevent": "日历事件", - "addon.calendar.calendarevents": "日历事件", - "addon.calendar.calendarreminders": "日历提醒", - "addon.calendar.categoryevents": "类别事件", - "addon.calendar.confirmeventdelete": "您确定要删除这个\"{{$a}}\"事件么?", - "addon.calendar.confirmeventseriesdelete": "“{{$a.name}}”事件是系列的一部分。您想要删除这个事件,还是要删除系列中的所有{{$a.count}}事件?", - "addon.calendar.courseevents": "课程事件", - "addon.calendar.currentmonth": "本月", - "addon.calendar.daynext": "下一天", - "addon.calendar.dayprev": "上一天", - "addon.calendar.defaultnotificationtime": "默认通知时间", - "addon.calendar.deleteallevents": "删除所有事件", - "addon.calendar.deleteevent": "删除事件", - "addon.calendar.deleteoneevent": "删除此事件", - "addon.calendar.durationminutes": "持续多少分钟", - "addon.calendar.durationnone": "不持续", - "addon.calendar.durationuntil": "到", - "addon.calendar.editevent": "编辑事件", - "addon.calendar.errorloadevent": "加载事件时出错。", - "addon.calendar.errorloadevents": "加载事件时出错。", - "addon.calendar.eventcalendareventdeleted": "日历事件删除", - "addon.calendar.eventduration": "持续时间", - "addon.calendar.eventendtime": "结束时间", - "addon.calendar.eventkind": "事件类型", - "addon.calendar.eventname": "事件标题", - "addon.calendar.eventstarttime": "开始时间", - "addon.calendar.eventtype": "事件类型", - "addon.calendar.fri": "周五", - "addon.calendar.friday": "星期五", - "addon.calendar.gotoactivity": "转到活动", - "addon.calendar.groupevents": "组事件", - "addon.calendar.invalidtimedurationminutes": "您输入的持续时间(以分钟为单位)无效。请输入持续时间(以分钟为单位),或选择不持续时间。", - "addon.calendar.invalidtimedurationuntil": "您为“持续到”选择的时间和日期早于此事件的开始时间。请在继续处理前纠正这个错误。", - "addon.calendar.mon": "周一", - "addon.calendar.monday": "星期一", - "addon.calendar.monthlyview": "月视图", - "addon.calendar.newevent": "新事件", - "addon.calendar.noevents": "没有事件", - "addon.calendar.nopermissiontoupdatecalendar": "对不起,您没有权限更新日历事件。", - "addon.calendar.reminders": "提醒事项", - "addon.calendar.repeatedevents": "重复事件", - "addon.calendar.repeateditall": "应用到此重复系列的其它{{$a}}事件", - "addon.calendar.repeateditthis": "仅应用到此事件", - "addon.calendar.repeatevent": "重复此事件", - "addon.calendar.repeatweeksl": "每周重复", - "addon.calendar.sat": "周六", - "addon.calendar.saturday": "星期六", - "addon.calendar.setnewreminder": "设置新提醒事项", - "addon.calendar.siteevents": "网站事件", - "addon.calendar.sun": "周日", - "addon.calendar.sunday": "星期日", - "addon.calendar.thu": "周四", - "addon.calendar.thursday": "星期四", - "addon.calendar.today": "今天", - "addon.calendar.tomorrow": "明天", - "addon.calendar.tue": "周二", - "addon.calendar.tuesday": "星期二", - "addon.calendar.typecategory": "类别事件", - "addon.calendar.typeclose": "关闭事件", - "addon.calendar.typecourse": "课程事件", - "addon.calendar.typedue": "到期事件", - "addon.calendar.typegradingdue": "评分到期事件", - "addon.calendar.typegroup": "组事件", - "addon.calendar.typeopen": "打开事件", - "addon.calendar.typesite": "全站事件", - "addon.calendar.typeuser": "用户事件", - "addon.calendar.upcomingevents": "事件预告", - "addon.calendar.userevents": "用户事件", - "addon.calendar.wed": "周三", - "addon.calendar.wednesday": "星期三", - "addon.calendar.when": "当", - "addon.calendar.yesterday": "昨天", - "addon.competency.activities": "活动", - "addon.competency.competencies": "能力", - "addon.competency.competenciesmostoftennotproficientincourse": "在这门课程中最经常的不熟练的能力", - "addon.competency.coursecompetencies": "课程能力", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "该课程的能力评价不会影响学习计划。", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "本课程的能力评分会在学习计划中即时更新。", - "addon.competency.crossreferencedcompetencies": "交叉引用能力", - "addon.competency.duedate": "截止日期", - "addon.competency.errornocompetenciesfound": "没有发现能力", - "addon.competency.evidence": "证据", - "addon.competency.evidence_competencyrule": "符合能力的规则。", - "addon.competency.evidence_coursecompleted": "课程“{{$a}}”已完成。", - "addon.competency.evidence_coursemodulecompleted": "活动“{{$a}}”已完成。", - "addon.competency.evidence_courserestored": "评分随课程“{{$a}}”一起恢复。", - "addon.competency.evidence_evidenceofpriorlearninglinked": "先前学习“{{$a}}” 的证据已链接", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "先前学习“{{$a}}” 的证据已取消链接", - "addon.competency.evidence_manualoverride": "能力评价是手动设置的。", - "addon.competency.evidence_manualoverrideincourse": "能力评价是在课程“{{$a}}”中手动设置的。", - "addon.competency.evidence_manualoverrideinplan": "能力评价是在学习计划“{{$a}}”中手动设置的。", - "addon.competency.learningplancompetencies": "学习计划能力", - "addon.competency.learningplans": "学习计划", - "addon.competency.myplans": "我的学习计划", - "addon.competency.noactivities": "没有活动", - "addon.competency.nocompetencies": "没有能力", - "addon.competency.nocompetenciesincourse": "没有能力链接到该课程。", - "addon.competency.nocrossreferencedcompetencies": "没有其他能力交叉引用到这个能力。", - "addon.competency.noevidence": "没有证据", - "addon.competency.noplanswerecreated": "没有创建学习计划", - "addon.competency.nouserplanswithcompetency": "没有任何学习计划包含这个能力。", - "addon.competency.path": "路径:", - "addon.competency.planstatusactive": "有效的", - "addon.competency.planstatuscomplete": "完成", - "addon.competency.planstatusdraft": "草稿", - "addon.competency.planstatusinreview": "审核中", - "addon.competency.planstatuswaitingforreview": "等待审核", - "addon.competency.proficient": "熟练", - "addon.competency.progress": "进步", - "addon.competency.rating": "评价", - "addon.competency.reviewstatus": "检查状态", - "addon.competency.status": "状态", - "addon.competency.template": "学习计划模板", - "addon.competency.uponcoursecompletion": "课程完成之后:", - "addon.competency.usercompetencystatus_idle": "闲置", - "addon.competency.usercompetencystatus_inreview": "审核中", - "addon.competency.usercompetencystatus_waitingforreview": "等待审核", - "addon.competency.userplans": "学习计划", - "addon.competency.xcompetenciesproficientoutofy": "{{$a.x}} / {{$a.y}} 能力是熟练的", - "addon.competency.xcompetenciesproficientoutofyincourse": "您熟练这门课程{{$a.y}}个能力中的{{$a.x}}个。", - "addon.coursecompletion.complete": "完成", - "addon.coursecompletion.completecourse": "完成课程", - "addon.coursecompletion.completed": "已完成", - "addon.coursecompletion.completiondate": "完成日期", - "addon.coursecompletion.completionmenuitem": "进度", - "addon.coursecompletion.couldnotloadreport": "无法载入课程进度报告,请稍后再试。", - "addon.coursecompletion.coursecompletion": "课程进度", - "addon.coursecompletion.criteria": "条件", - "addon.coursecompletion.criteriagroup": "条件组", - "addon.coursecompletion.criteriarequiredall": "必须满足以下条件", - "addon.coursecompletion.criteriarequiredany": "必须满足下列任一条件", - "addon.coursecompletion.inprogress": "处理中", - "addon.coursecompletion.manualselfcompletion": "手动自设进度", - "addon.coursecompletion.nottracked": "在此课程中目前没有关于您的进度跟踪信息", - "addon.coursecompletion.notyetstarted": "还未开始", - "addon.coursecompletion.pending": "等待中", - "addon.coursecompletion.required": "必需的", - "addon.coursecompletion.requiredcriteria": "必备条件", - "addon.coursecompletion.requirement": "前提条件", - "addon.coursecompletion.status": "状态", - "addon.coursecompletion.viewcoursereport": "查看课程报告", - "addon.files.couldnotloadfiles": "文件列表不能加载。", - "addon.files.emptyfilelist": "没有可显示的文件。", - "addon.files.erroruploadnotworking": "很遗憾,目前无法将文件上传到您的站点。", - "addon.files.files": "文件", - "addon.files.privatefiles": "私人文件", - "addon.files.sitefiles": "网站文件", - "addon.messageoutput_airnotifier.processorsettingsdesc": "配置设备", - "addon.messages.acceptandaddcontact": "接受并添加到联系人", - "addon.messages.addcontact": "添加联系人", - "addon.messages.addcontactconfirm": "您确定要将{{$a}}添加到您的联系人吗?", - "addon.messages.addtofavourites": "标星对话", - "addon.messages.addtoyourcontacts": "添加到联系人", - "addon.messages.blocknoncontacts": "禁止不在联系人中的用户给我发消息", - "addon.messages.blockuser": "屏蔽用户", - "addon.messages.blockuserconfirm": "您确定要屏蔽{{$a}}吗?", - "addon.messages.contactableprivacy": "接受消息:", - "addon.messages.contactableprivacy_coursemember": "联系人和课程同伴", - "addon.messages.contactableprivacy_onlycontacts": "仅联系人", - "addon.messages.contactableprivacy_site": "网站上的任何人", - "addon.messages.contactblocked": "被屏蔽的联系人", - "addon.messages.contactlistempty": "您的联系人名单是空的", - "addon.messages.contactname": "联系人姓名", - "addon.messages.contactrequestsent": "联系请求发送", - "addon.messages.contacts": "联系人", - "addon.messages.conversationactions": "对话操作菜单", - "addon.messages.decline": "拒绝", - "addon.messages.deleteallconfirm": "您确定要删除整个对话吗?这不会删除其他对话参与者的对话。", - "addon.messages.deleteallselfconfirm": "您确定要删除整个对话吗?", - "addon.messages.deleteconversation": "删除对话", - "addon.messages.deleteforeveryone": "为我和所有人删除", - "addon.messages.deletemessage": "删除消息", - "addon.messages.deletemessageconfirmation": "您确定要删除此消息吗?它只会从您的消息收发历史记录中删除,发送或接收消息的用户仍然可以看到它。", - "addon.messages.errordeletemessage": "删除消息时出错。", - "addon.messages.errorwhileretrievingcontacts": "从服务器检索联系人时出错。", - "addon.messages.errorwhileretrievingdiscussions": "从服务器检索讨论区时出错。", - "addon.messages.errorwhileretrievingmessages": "从服务器检索消息时出错。", - "addon.messages.errorwhileretrievingusers": "从服务器检索用户时出错。", - "addon.messages.groupconversations": "小组", - "addon.messages.groupinfo": "小组信息", - "addon.messages.individualconversations": "私人", - "addon.messages.info": "用户信息", - "addon.messages.isnotinyourcontacts": "{{$a}}不是您的联系人", - "addon.messages.message": "消息", - "addon.messages.messagenotsent": "这条消息没有发送,请稍后再试。", - "addon.messages.messagepreferences": "信息偏好", - "addon.messages.messages": "消息", - "addon.messages.muteconversation": "静音", - "addon.messages.mutedconversation": "静音对话", - "addon.messages.newmessage": "新消息", - "addon.messages.newmessages": "新消息", - "addon.messages.nocontactrequests": "没有联系请求", - "addon.messages.nocontactsgetstarted": "没有联系人", - "addon.messages.nofavourites": "没有标星的对话", - "addon.messages.nogroupconversations": "没有小组对话", - "addon.messages.noindividualconversations": "没有私人对话", - "addon.messages.nomessagesfound": "没有消息", - "addon.messages.noncontacts": "不是联系人", - "addon.messages.nousersfound": "没有找到用户", - "addon.messages.numparticipants": "{{$a}}参与者", - "addon.messages.removecontact": "删除联系人", - "addon.messages.removecontactconfirm": "您确定要从联系人中删除{{$a}}吗?", - "addon.messages.removefromfavourites": "取消标星对话", - "addon.messages.removefromyourcontacts": "删除联系人", - "addon.messages.requests": "申请", - "addon.messages.requirecontacttomessage": "您需要请求{{$a}}将您添加为联系人,才能给他们发消息。", - "addon.messages.searchcombined": "搜索用户和消息", - "addon.messages.selfconversation": "个人空间", - "addon.messages.selfconversationdefaultmessage": "保存草稿信息,链接,笔记等,以供以后使用。", - "addon.messages.sendcontactrequest": "发送联系请求", - "addon.messages.showdeletemessages": "显示删除消息", - "addon.messages.type_blocked": "已屏蔽", - "addon.messages.type_offline": "离线", - "addon.messages.type_online": "在线", - "addon.messages.type_search": "搜索结果", - "addon.messages.type_strangers": "其他人", - "addon.messages.unabletomessage": "您无法给该用户发送消息", - "addon.messages.unblockuser": "解除屏蔽用户", - "addon.messages.unblockuserconfirm": "您确定要解除屏蔽{{$a}}吗?", - "addon.messages.unmuteconversation": "取消静音", - "addon.messages.useentertosend": "按回车键发送", - "addon.messages.useentertosenddescdesktop": "如果禁用,可以使用Ctrl+Enter发送消息。", - "addon.messages.useentertosenddescmac": "如果禁用,您可以使用Cmd+Enter来发送消息。", - "addon.messages.userwouldliketocontactyou": "{{$a}}想与您联系", - "addon.messages.warningconversationmessagenotsent": "不能向对话 {{conversation}}发送消息。 {{error}}", - "addon.messages.warningmessagenotsent": "不能向用户{{user}}发送消息。 {{error}}", - "addon.messages.wouldliketocontactyou": "想和您联系", - "addon.messages.you": "您:", - "addon.messages.youhaveblockeduser": "您已经屏蔽了该用户。", - "addon.messages.yourcontactrequestpending": "您的联系请求正在等待{{$a}}接受", - "addon.mod_assign.acceptsubmissionstatement": "请接受提交声明。", - "addon.mod_assign.addattempt": "允许重做", - "addon.mod_assign.addnewattempt": "添加新尝试", - "addon.mod_assign.addnewattemptfromprevious": "根据上次提交添加新尝试", - "addon.mod_assign.addsubmission": "添加提交", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "作业细节和提交表单将从{{$a}}中获得", - "addon.mod_assign.allowsubmissionsfromdate": "开启时间", - "addon.mod_assign.allowsubmissionsfromdatesummary": "本作业将从{{$a}}起接受提交", - "addon.mod_assign.applytoteam": "将分数和反馈意见应用到整个组", - "addon.mod_assign.assignmentisdue": "作业到期", - "addon.mod_assign.attemptnumber": "尝试次数", - "addon.mod_assign.attemptreopenmethod": "重试开启", - "addon.mod_assign.attemptreopenmethod_manual": "手工开启", - "addon.mod_assign.attemptreopenmethod_untilpass": "自动开启直到通过", - "addon.mod_assign.attemptsettings": "尝试设置", - "addon.mod_assign.cannoteditduetostatementsubmission": "您不能在应用程序中添加或编辑提交,因为我们无法从站点检索提交声明。", - "addon.mod_assign.cannotgradefromapp": "应用程序还不支持某些评分方法,不能修改。", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "您不能在应用程序中提交,因为我们无法从站点检索提交声明。", - "addon.mod_assign.confirmsubmission": "您确定要把作业交上来评分吗?提交后,不可再进行任何更改。", - "addon.mod_assign.currentattempt": "这是第{{$a}}次尝试。", - "addon.mod_assign.currentattemptof": "这是第{{$a.attemptnumber}}次尝试(允许尝试 {{$a.maxattempts}} 次)", - "addon.mod_assign.currentgrade": "成绩单中的当前成绩", - "addon.mod_assign.cutoffdate": "截止日期", - "addon.mod_assign.defaultteam": "默认分组", - "addon.mod_assign.duedate": "到期日期", - "addon.mod_assign.duedateno": "没有到期日期", - "addon.mod_assign.duedatereached": "此作业的到期日期已经过了", - "addon.mod_assign.editingstatus": "编辑状态", - "addon.mod_assign.editsubmission": "编辑提交", - "addon.mod_assign.erroreditpluginsnotsupported": "你不能在应用中添加或编辑提交,因为有些插件不支持编辑:", - "addon.mod_assign.errorshowinginformation": "无法显示提交信息。", - "addon.mod_assign.extensionduedate": "延长到期日期", - "addon.mod_assign.feedbacknotsupported": "应用程序不支持此反馈,可能不包含所有信息。", - "addon.mod_assign.grade": "成绩", - "addon.mod_assign.graded": "评分", - "addon.mod_assign.gradedby": "评分人", - "addon.mod_assign.gradedfollowupsubmit": "评分 - 继续收到的提交", - "addon.mod_assign.gradedon": "评分时间", - "addon.mod_assign.gradelocked": "该成绩在成绩册中被锁定或覆盖。", - "addon.mod_assign.gradenotsynced": "成绩未同步", - "addon.mod_assign.gradeoutof": "成绩(满分 {{$a}} )", - "addon.mod_assign.gradingstatus": "评分状态", - "addon.mod_assign.groupsubmissionsettings": "小组提交设置", - "addon.mod_assign.hiddenuser": "参与者", - "addon.mod_assign.latesubmissions": "迟交的", - "addon.mod_assign.latesubmissionsaccepted": "允许到{{$a}}", - "addon.mod_assign.markingworkflowstate": "评分工作流程状态", - "addon.mod_assign.markingworkflowstateinmarking": "评分中", - "addon.mod_assign.markingworkflowstateinreview": "检查中", - "addon.mod_assign.markingworkflowstatenotmarked": "未评分", - "addon.mod_assign.markingworkflowstatereadyforrelease": "准备公布", - "addon.mod_assign.markingworkflowstatereadyforreview": "评分完成", - "addon.mod_assign.markingworkflowstatereleased": "已公布", - "addon.mod_assign.modulenameplural": "作业", - "addon.mod_assign.multipleteams": "一个以上组的成员", - "addon.mod_assign.multipleteams_desc": "作业要求小组提交。您是多个组的成员。为了能够提交, 您必须只是一个组的成员。请联系您的教师更改您的组成员身份。", - "addon.mod_assign.noattempt": "没有尝试", - "addon.mod_assign.nomoresubmissionsaccepted": "只适用于已获延期的参与者", - "addon.mod_assign.noonlinesubmissions": "这个作业不需要您在线提交任何东西", - "addon.mod_assign.nosubmission": "这个作业还没有任何提交", - "addon.mod_assign.notallparticipantsareshown": "没有提交的参与者不会显示", - "addon.mod_assign.noteam": "不是任何小组的成员", - "addon.mod_assign.noteam_desc": "此作业要求小组提交。您不是任何组的成员, 因此不能创建提交。请联系您的老师把您添加到一个小组中。", - "addon.mod_assign.notgraded": "未评分", - "addon.mod_assign.numberofdraftsubmissions": "草稿", - "addon.mod_assign.numberofparticipants": "参与人", - "addon.mod_assign.numberofsubmissionsneedgrading": "需要评分", - "addon.mod_assign.numberofsubmittedassignments": "已提交", - "addon.mod_assign.numberofteams": "群组", - "addon.mod_assign.numwords": "{{$a}}字", - "addon.mod_assign.outof": "{{$a.current}},共有{{$a.total}}", - "addon.mod_assign.overdue": "逾期未交:{{$a}}", - "addon.mod_assign.submission": "提交", - "addon.mod_assign.submissioneditable": "学生可以编辑此提交", - "addon.mod_assign.submissionnoteditable": "学生不能编辑此提交", - "addon.mod_assign.submissionnotsupported": "本应用程序不支持此提交,可能不包含所有信息。", - "addon.mod_assign.submissionslocked": "此作业不接受提交", - "addon.mod_assign.submissionstatus": "提交状态", - "addon.mod_assign.submissionstatus_": "未提交", - "addon.mod_assign.submissionstatus_draft": "草稿(未提交)", - "addon.mod_assign.submissionstatus_marked": "评分", - "addon.mod_assign.submissionstatus_new": "未提交", - "addon.mod_assign.submissionstatus_reopened": "重新开始", - "addon.mod_assign.submissionstatus_submitted": "提交了去评分", - "addon.mod_assign.submissionstatusheading": "提交状态", - "addon.mod_assign.submissionteam": "小组", - "addon.mod_assign.submitassignment": "提交作业", - "addon.mod_assign.submitassignment_help": "提交作业后,您将不能再做任何更改。", - "addon.mod_assign.submittedearly": "作业早交{{$a}}", - "addon.mod_assign.submittedlate": "作业迟交{{$a}}", - "addon.mod_assign.timemodified": "最后修改", - "addon.mod_assign.timeremaining": "剩余时间", - "addon.mod_assign.ungroupedusers": "“要求小组提交”的设置启用,某些用户未分配小组或者加入多个小组,将不能完成作业。", - "addon.mod_assign.ungroupedusersoptional": "“学生小组提交”的设置启用,有些用户不是任何组的成员,或者是多个组的成员。请注意,这些学生将作为“默认组”的成员提交。", - "addon.mod_assign.unlimitedattempts": "不限", - "addon.mod_assign.userswhoneedtosubmit": "需要提交的用户:{{$a}}", - "addon.mod_assign.userwithid": "ID为{{id}}的用户", - "addon.mod_assign.viewsubmission": "查看提交", - "addon.mod_assign.warningsubmissiongrademodified": "此提交的成绩在站点中被修改。", - "addon.mod_assign.warningsubmissionmodified": "此用户提交在站点中被修改。", - "addon.mod_assign.wordlimit": "字数限制", - "addon.mod_assign_feedback_comments.pluginname": "反馈意见", - "addon.mod_assign_feedback_editpdf.pluginname": "批注的PDF", - "addon.mod_assign_feedback_file.pluginname": "文件反馈", - "addon.mod_assign_submission_comments.pluginname": "提交评论", - "addon.mod_assign_submission_file.pluginname": "文件提交", - "addon.mod_assign_submission_onlinetext.pluginname": "在线文本提交", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "这个作业的字数限制是{{$a.limit}}字,而您要提交的是{{$a.count}}字。请检查您的提交,然后再试一次。", - "addon.mod_book.errorchapter": "读取图书章节发生错误。", - "addon.mod_book.modulenameplural": "图书", - "addon.mod_book.navnexttitle": "下一个: {{$a}}", - "addon.mod_book.navprevtitle": "上一个: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "图书章节", - "addon.mod_book.toc": "目录", - "addon.mod_chat.beep": "呼叫", - "addon.mod_chat.chatreport": "聊天会话", - "addon.mod_chat.currentusers": "当前用户", - "addon.mod_chat.enterchat": "进入聊天室", - "addon.mod_chat.entermessage": "输入您的消息", - "addon.mod_chat.errorwhileconnecting": "连接聊天时出错。", - "addon.mod_chat.errorwhilegettingchatdata": "获取聊天数据时出错。", - "addon.mod_chat.errorwhilegettingchatusers": "获取聊天用户时出错。", - "addon.mod_chat.errorwhileretrievingmessages": "从服务器检索消息时出错。", - "addon.mod_chat.errorwhilesendingmessage": "发送消息时出错。", - "addon.mod_chat.messagebeepseveryone": "{{$a}}呼叫所有人!", - "addon.mod_chat.messagebeepsyou": "{{$a}}刚刚呼叫您!", - "addon.mod_chat.messageenter": "{{$a}}刚刚进入聊天室", - "addon.mod_chat.messageexit": "{{$a}}已退出聊天室", - "addon.mod_chat.messages": "消息", - "addon.mod_chat.messageyoubeep": "您呼叫了{{$a}}", - "addon.mod_chat.modulenameplural": "聊天", - "addon.mod_chat.mustbeonlinetosendmessages": "您必须在线才能发送消息。", - "addon.mod_chat.nomessages": "无消息", - "addon.mod_chat.nosessionsfound": "没有发现会话", - "addon.mod_chat.saidto": "对", - "addon.mod_chat.send": "发送", - "addon.mod_chat.sessionstart": "下一个聊天会话将在{{$a.date}}开始,({{$a.fromnow}} 从现在起)", - "addon.mod_chat.showincompletesessions": "显示不完整的会话", - "addon.mod_chat.talk": "交谈", - "addon.mod_chat.viewreport": "查看过去的聊天会话", - "addon.mod_choice.cannotsubmit": "对不起,在提交您的投票时出现了问题。请再试一次。", - "addon.mod_choice.choiceoptions": "投票选项", - "addon.mod_choice.errorgetchoice": "获取投票数据时出错。", - "addon.mod_choice.expired": "对不起,该活动已于 {{$a}} 关闭。", - "addon.mod_choice.full": "(已满)", - "addon.mod_choice.modulenameplural": "投票", - "addon.mod_choice.noresultsviewable": "结果目前不能查看。", - "addon.mod_choice.notopenyet": "此活动在{{$a}}之前不可用。", - "addon.mod_choice.numberofuser": "投票数量", - "addon.mod_choice.numberofuserinpercentage": "投票百分比", - "addon.mod_choice.previewonly": "这只是该活动的可用选项的预览。您要等到{{$a}}才可以进行投票。", - "addon.mod_choice.publishinfoanonafter": "匿名结果将在您回答后公布。", - "addon.mod_choice.publishinfoanonclose": "匿名结果将在活动结束后公布。", - "addon.mod_choice.publishinfofullafter": "完整的结果,显示每个人的选择,将在您回答后公布。", - "addon.mod_choice.publishinfofullclose": "完整的结果,显示每个人的选择,将在活动结束后公布。", - "addon.mod_choice.publishinfonever": "此活动结果将不会在您回答后公布。", - "addon.mod_choice.removemychoice": "删除我的投票", - "addon.mod_choice.responses": "投票", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}%的用户选择了这个选项:{{text}}。", - "addon.mod_choice.responsesresultgraphheader": "图形显示", - "addon.mod_choice.resultsnotsynced": "结果不包括您的最后的投票。请同步更新它们。", - "addon.mod_choice.savemychoice": "保存我的投票", - "addon.mod_choice.userchoosethisoption": "选择此选项的用户", - "addon.mod_choice.yourselection": "您的选择", - "addon.mod_data.addentries": "添加条目", - "addon.mod_data.advancedsearch": "高级搜索", - "addon.mod_data.alttext": "替代文本", - "addon.mod_data.approve": "批准", - "addon.mod_data.approved": "已批准", - "addon.mod_data.ascending": "升序", - "addon.mod_data.authorfirstname": "作者的名", - "addon.mod_data.authorlastname": "作者的姓", - "addon.mod_data.confirmdeleterecord": "您确定要删除此条目吗?", - "addon.mod_data.descending": "降序", - "addon.mod_data.disapprove": "撤消批准", - "addon.mod_data.edittagsnotsupported": "对不起,应用程序不支持编辑标签。", - "addon.mod_data.emptyaddform": "您没有填写任何字段!", - "addon.mod_data.entrieslefttoadd": "要完成此活动,必须添加{{$a.entriesleft}}以上条目", - "addon.mod_data.entrieslefttoaddtoview": "您必须添加{{$a.entrieslefttoview}}以上条目才能查看其他参与者的条目。", - "addon.mod_data.errorapproving": "批准或取消批准条目出错。", - "addon.mod_data.errordeleting": "删除条目出错。", - "addon.mod_data.errormustsupplyvalue": "您必须在这里提供一个值。", - "addon.mod_data.expired": "对不起,此活动已于{{$a}}关闭,不再可用", - "addon.mod_data.fields": "字段", - "addon.mod_data.foundrecords": "找到记录:{{$a.num}}/{{$a.max}} (重设过滤器)", - "addon.mod_data.gettinglocation": "获取位置", - "addon.mod_data.latlongboth": "纬度和经度都是必需的。", - "addon.mod_data.locationpermissiondenied": "访问您的位置的权限被拒绝。", - "addon.mod_data.menuchoose": "选择...", - "addon.mod_data.modulenameplural": "数据库", - "addon.mod_data.more": "更多", - "addon.mod_data.mylocation": "我的位置", - "addon.mod_data.nomatch": "未找到匹配的条目!", - "addon.mod_data.norecords": "数据库中无条目", - "addon.mod_data.notapproved": "条目尚未批准。", - "addon.mod_data.notopenyet": "抱歉,此活动在{{$a}}之前不可用", - "addon.mod_data.numrecords": "{{$a}} 条目", - "addon.mod_data.other": "其他", - "addon.mod_data.recordapproved": "已批准条目", - "addon.mod_data.recorddeleted": "已删除条目", - "addon.mod_data.recorddisapproved": "未批准条目", - "addon.mod_data.resetsettings": "重置字段", - "addon.mod_data.search": "搜索", - "addon.mod_data.searchbytagsnotsupported": "对不起,应用程序不支持标签搜索。", - "addon.mod_data.selectedrequired": "全选", - "addon.mod_data.single": "独立视图", - "addon.mod_data.tagarea_data_records": "数据记录", - "addon.mod_data.timeadded": "添加时间", - "addon.mod_data.timemodified": "修改时间", - "addon.mod_data.usedate": "包含到搜索中。", - "addon.mod_feedback.analysis": "分析", - "addon.mod_feedback.anonymous": "匿名", - "addon.mod_feedback.anonymous_entries": "匿名条目 ({{$a}})", - "addon.mod_feedback.average": "平均", - "addon.mod_feedback.captchaofflinewarning": "问卷调查验证不能离线完成,或者没有配置好,或者服务器关闭了。", - "addon.mod_feedback.complete_the_form": "回答问题", - "addon.mod_feedback.completed_feedbacks": "提交答案", - "addon.mod_feedback.continue_the_form": "继续回答问题", - "addon.mod_feedback.feedback_is_not_open": "此问卷调查未开放", - "addon.mod_feedback.feedback_submitted_offline": "此问卷调查已保存以便稍后提交。", - "addon.mod_feedback.feedbackclose": "允许回答到", - "addon.mod_feedback.feedbackopen": "开放回答时间", - "addon.mod_feedback.mapcourses": "映射反馈到课程调查", - "addon.mod_feedback.maximal": "最大", - "addon.mod_feedback.minimal": "最小", - "addon.mod_feedback.mode": "模式", - "addon.mod_feedback.modulenameplural": "问卷调查", - "addon.mod_feedback.next_page": "下一页", - "addon.mod_feedback.non_anonymous": "用户名将被记录并与答案一起显示", - "addon.mod_feedback.non_anonymous_entries": "非匿名条目 ({{$a}})", - "addon.mod_feedback.non_respondents_students": "未反馈的学生 ({{$a}})", - "addon.mod_feedback.not_selected": "未选择的", - "addon.mod_feedback.not_started": "未开始的", - "addon.mod_feedback.numberoutofrange": "超出范围的数", - "addon.mod_feedback.overview": "概述", - "addon.mod_feedback.page_after_submit": "完成消息", - "addon.mod_feedback.preview": "预览", - "addon.mod_feedback.previous_page": "上一页", - "addon.mod_feedback.questions": "问题", - "addon.mod_feedback.response_nr": "反馈编号", - "addon.mod_feedback.responses": "反馈", - "addon.mod_feedback.save_entries": "提交您的回答", - "addon.mod_feedback.show_entries": "显示反馈", - "addon.mod_feedback.show_nonrespondents": "显示未反馈的", - "addon.mod_feedback.started": "开始", - "addon.mod_feedback.this_feedback_is_already_submitted": "您已经完成此活动。", - "addon.mod_folder.emptyfilelist": "没有可显示的文件。", - "addon.mod_folder.modulenameplural": "文件夹", - "addon.mod_forum.addanewdiscussion": "添加一个新论题", - "addon.mod_forum.addanewquestion": "添加一个新问题", - "addon.mod_forum.addanewtopic": "添加一个新话题", - "addon.mod_forum.addtofavourites": "标星此讨论", - "addon.mod_forum.advanced": "高级", - "addon.mod_forum.cannotadddiscussion": "向此讨论区添加讨论需要组成员身份。", - "addon.mod_forum.cannotadddiscussionall": "您没有权限为所有参与者添加新的论题。", - "addon.mod_forum.cannotcreatediscussion": "不能建立新讨论", - "addon.mod_forum.couldnotadd": "由于未知的错误无法添加您的帖子", - "addon.mod_forum.couldnotupdate": "由于未知的错误无法更新您的帖子", - "addon.mod_forum.cutoffdatereached": "此讨论区截止日期已到,您不能再在这儿发布帖子。", - "addon.mod_forum.delete": "删除", - "addon.mod_forum.deletedpost": "帖子已删除", - "addon.mod_forum.deletesure": "您确定要删除该帖吗?", - "addon.mod_forum.discussion": "讨论", - "addon.mod_forum.discussionlistsortbycreatedasc": "按创建日期升序排序", - "addon.mod_forum.discussionlistsortbycreateddesc": "按创建日期降序排序", - "addon.mod_forum.discussionlistsortbylastpostasc": "按最近帖子创建日期升序排序", - "addon.mod_forum.discussionlistsortbylastpostdesc": "按最近帖子创建日期降序排序", - "addon.mod_forum.discussionlistsortbyrepliesasc": "按回复数升序排序", - "addon.mod_forum.discussionlistsortbyrepliesdesc": "按回复数降序排序", - "addon.mod_forum.discussionlocked": "此讨论已被锁定,不再接受新回复。", - "addon.mod_forum.discussionpinned": "已置顶", - "addon.mod_forum.discussionsubscription": "讨论订阅", - "addon.mod_forum.edit": "编辑", - "addon.mod_forum.erroremptymessage": "帖子正文不能为空", - "addon.mod_forum.erroremptysubject": "帖子主题不能为空。", - "addon.mod_forum.errorgetforum": "获取讨论区数据时出错。", - "addon.mod_forum.errorgetgroups": "获取组设置时出错。", - "addon.mod_forum.errorposttoallgroups": "不能在所有组中发起新讨论。", - "addon.mod_forum.favouriteupdated": "您的标星选项已更新。", - "addon.mod_forum.forumnodiscussionsyet": "此讨论区中还没有讨论。", - "addon.mod_forum.group": "组", - "addon.mod_forum.lastpost": "最新帖子", - "addon.mod_forum.lockdiscussion": "锁定此讨论", - "addon.mod_forum.lockupdated": "锁定选项已更新。", - "addon.mod_forum.message": "正文", - "addon.mod_forum.modeflatnewestfirst": "列表显示回帖,新帖在前", - "addon.mod_forum.modeflatoldestfirst": "列表显示回帖,旧帖在前", - "addon.mod_forum.modenested": "嵌套显示回帖", - "addon.mod_forum.modulenameplural": "讨论区", - "addon.mod_forum.numdiscussions": "{{numdiscussions}} 讨论", - "addon.mod_forum.numreplies": "{{numreplies}} 回复", - "addon.mod_forum.pindiscussion": "置顶此讨论", - "addon.mod_forum.pinupdated": "置顶选项已更新", - "addon.mod_forum.postisprivatereply": "这是私人回复。其他参与者不可见。", - "addon.mod_forum.posttoforum": "发到讨论区上", - "addon.mod_forum.posttomygroups": "发布一份副本到所有组", - "addon.mod_forum.privatereply": "私下回复", - "addon.mod_forum.re": "回复:", - "addon.mod_forum.refreshdiscussions": "刷新讨论", - "addon.mod_forum.refreshposts": "刷新帖子", - "addon.mod_forum.removefromfavourites": "取消标星此讨论", - "addon.mod_forum.reply": "回复", - "addon.mod_forum.replyplaceholder": "写您的回复...", - "addon.mod_forum.subject": "主题", - "addon.mod_forum.tagarea_forum_posts": "讨论区帖子", - "addon.mod_forum.thisforumhasduedate": "此讨论区发帖的截止日期为{{$a}}。", - "addon.mod_forum.thisforumisdue": "此讨论区发帖的截止日期为{{$a}}。", - "addon.mod_forum.unlockdiscussion": "解锁此讨论", - "addon.mod_forum.unpindiscussion": "取消置顶此讨论", - "addon.mod_forum.unread": "未读", - "addon.mod_forum.unreadpostsnumber": "{{$a}} 未读帖子", - "addon.mod_forum.yourreply": "您的回复", - "addon.mod_glossary.addentry": "添加新词条", - "addon.mod_glossary.aliases": "关键词", - "addon.mod_glossary.attachment": "附件", - "addon.mod_glossary.browsemode": "浏览词条", - "addon.mod_glossary.byalphabet": "按字母顺序", - "addon.mod_glossary.byauthor": "按作者分组", - "addon.mod_glossary.bycategory": "按类别分组", - "addon.mod_glossary.bynewestfirst": "新的在前", - "addon.mod_glossary.byrecentlyupdated": "最近更新的", - "addon.mod_glossary.bysearch": "搜索", - "addon.mod_glossary.cannoteditentry": "不能编辑词条", - "addon.mod_glossary.casesensitive": "此词条区分大小写", - "addon.mod_glossary.categories": "类别", - "addon.mod_glossary.concept": "词条名", - "addon.mod_glossary.definition": "定义", - "addon.mod_glossary.entriestobesynced": "要同步的词条", - "addon.mod_glossary.entrypendingapproval": "此词条有待批准。", - "addon.mod_glossary.entryusedynalink": "此词条可自动链接", - "addon.mod_glossary.errconceptalreadyexists": "词条名已存在。此词汇表中不允许使用相同词条名。", - "addon.mod_glossary.errorloadingentries": "加载词条时出错。", - "addon.mod_glossary.errorloadingentry": "加载词条时出错。", - "addon.mod_glossary.errorloadingglossary": "加载词汇表时出错。", - "addon.mod_glossary.fillfields": "词条名和定义都是必须填写的字段。", - "addon.mod_glossary.fullmatch": "整词匹配", - "addon.mod_glossary.linking": "自动链接", - "addon.mod_glossary.modulenameplural": "词汇表", - "addon.mod_glossary.noentriesfound": "没有发现词条。", - "addon.mod_glossary.searchquery": "查询词条", - "addon.mod_glossary.tagarea_glossary_entries": "词汇表词条", - "addon.mod_h5pactivity.all_attempts": "所有用户尝试", - "addon.mod_h5pactivity.answer_checked": "已检查答案", - "addon.mod_h5pactivity.answer_correct": "回答正确", - "addon.mod_h5pactivity.answer_fail": "错误答案", - "addon.mod_h5pactivity.answer_incorrect": "您的答案不正确", - "addon.mod_h5pactivity.answer_pass": "正确答案", - "addon.mod_h5pactivity.attempt": "尝试", - "addon.mod_h5pactivity.attempt_completion_no": "此尝试没有被标记为完成", - "addon.mod_h5pactivity.attempt_completion_yes": "此尝试已完成", - "addon.mod_h5pactivity.attempt_success_fail": "不及格", - "addon.mod_h5pactivity.attempt_success_pass": "及格", - "addon.mod_h5pactivity.attempt_success_unknown": "未报告", - "addon.mod_h5pactivity.attempts_none": "此用户没有尝试显示。", - "addon.mod_h5pactivity.completion": "完成", - "addon.mod_h5pactivity.downloadh5pfile": "下载H5P文件", - "addon.mod_h5pactivity.duration": "持续时间", - "addon.mod_h5pactivity.errorgetactivity": "获取H5P活动数据时出错。", - "addon.mod_h5pactivity.filestatenotdownloaded": "该H5P包没有下载。您需要下载才能使用它。", - "addon.mod_h5pactivity.filestateoutdated": "自上次下载以来,H5P包已被修改。您需要再次下载才能使用它。", - "addon.mod_h5pactivity.maxscore": "最高得分", - "addon.mod_h5pactivity.modulenameplural": "H5P", - "addon.mod_h5pactivity.myattempts": "我的尝试", - "addon.mod_h5pactivity.no_compatible_track": "此交互({{$a}})不提供跟踪信息或所提供的跟踪\n与当前活动版本不兼容。", - "addon.mod_h5pactivity.offlinedisabledwarning": "您需要在线才能查看H5P包。", - "addon.mod_h5pactivity.outcome": "成果", - "addon.mod_h5pactivity.previewmode": "此内容在预览模式下显示。没有尝试跟踪将被存储。", - "addon.mod_h5pactivity.result_fill-in": "填写文字", - "addon.mod_h5pactivity.result_other": "未知的交互类型", - "addon.mod_h5pactivity.review_my_attempts": "查看我的尝试", - "addon.mod_h5pactivity.score": "得分", - "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} / {{$a.maxscore}}", - "addon.mod_h5pactivity.startdate": "开始日期", - "addon.mod_h5pactivity.totalscore": "总分", - "addon.mod_h5pactivity.viewattempt": "查看尝试{{$a}}", - "addon.mod_imscp.deploymenterror": "内容包错误!", - "addon.mod_imscp.modulenameplural": "IMS 内容包", - "addon.mod_imscp.showmoduledescription": "显示描述", - "addon.mod_imscp.toc": "目录", - "addon.mod_lesson.answer": "答案", - "addon.mod_lesson.attempt": "尝试:{{$a}}", - "addon.mod_lesson.attemptheader": "尝试", - "addon.mod_lesson.attemptsremaining": "您还有{{$a}}次机会", - "addon.mod_lesson.averagescore": "平均分", - "addon.mod_lesson.averagetime": "平均耗时", - "addon.mod_lesson.branchtable": "内容", - "addon.mod_lesson.cannotfindattempt": "错误:找不到尝试", - "addon.mod_lesson.cannotfinduser": "错误:找不到用户", - "addon.mod_lesson.clusterjump": "隐藏在簇中的题目", - "addon.mod_lesson.completed": "已完成", - "addon.mod_lesson.congratulations": "祝贺你们——课程结束了", - "addon.mod_lesson.continue": "继续", - "addon.mod_lesson.continuetonextpage": "继续到下一页。", - "addon.mod_lesson.defaultessayresponse": "教师将随后对您的问答题评分。", - "addon.mod_lesson.detailedstats": "详细统计", - "addon.mod_lesson.didnotanswerquestion": "未回答此题。", - "addon.mod_lesson.displayofgrade": "成绩展示(只供学生使用)", - "addon.mod_lesson.displayscorewithessays": "您在自动评分的题目中得分{{$a.score}}(满分:{{$a.tempmaxgrade}})。
                      您的{{$a.essayquestions}}个问答题将晚些评分,并添加到您的最终成绩中。

                      您当前不包含问答题的成绩是:{{$a.score}} (满分:{{$a.grade}})", - "addon.mod_lesson.displayscorewithoutessays": "您的得分是{{$a.score}}(总分$a.grade}})。", - "addon.mod_lesson.emptypassword": "密码不能为空", - "addon.mod_lesson.enterpassword": "请输入密码", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "您没有回答任何题目。您在本程序教学中得到了 0 分。", - "addon.mod_lesson.errorprefetchrandombranch": "这个程序教学包含到随机内容页面的跳转。只有在web浏览器中启动后,才能在应用程序中进行尝试。", - "addon.mod_lesson.errorreviewretakenotlast": "由于另一个尝试已经完成,因此不能再检查此尝试。", - "addon.mod_lesson.finish": "结束", - "addon.mod_lesson.finishretakeoffline": "这个尝试是离线完成的。", - "addon.mod_lesson.firstwrong": "您答错了。您想再试一次这个题目吗?(即使您现在正确地回答了这个题目,它也不计入您的最终得分。)", - "addon.mod_lesson.gotoendoflesson": "到程序教学末尾", - "addon.mod_lesson.grade": "成绩", - "addon.mod_lesson.highscore": "高分", - "addon.mod_lesson.hightime": "高耗时", - "addon.mod_lesson.leftduringtimed": "您在计时程序教学的中间离开了。
                      请点击“继续”来重新开始。", - "addon.mod_lesson.leftduringtimednoretake": "您在计时程序教学的中间离开了。
                      此教程不允许重学或继续。", - "addon.mod_lesson.lessonmenu": "程序教学菜单", - "addon.mod_lesson.lessonstats": "程序教学统计", - "addon.mod_lesson.linkedmedia": "链接的媒体", - "addon.mod_lesson.loginfail": "登录错误,请重试……", - "addon.mod_lesson.lowscore": "低分", - "addon.mod_lesson.lowtime": "短耗时", - "addon.mod_lesson.maximumnumberofattemptsreached": "达到最大尝试次数—请进入下一个页面", - "addon.mod_lesson.modattemptsnoteacher": "学生检查只对学生有效。", - "addon.mod_lesson.modulenameplural": "程序教学", - "addon.mod_lesson.noanswer": "一个或多个问题没有给出答案。请返回并提交一个答案。", - "addon.mod_lesson.nolessonattempts": "还没有人尝试此程序教学。", - "addon.mod_lesson.nolessonattemptsgroup": "{{$a}}组成员在此程序教学没有做任何尝试。", - "addon.mod_lesson.notcompleted": "没有完成", - "addon.mod_lesson.numberofcorrectanswers": "正确回答数量:{{$a}}", - "addon.mod_lesson.numberofpagesviewed": "回答的题目数:{{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "回答问题数:{{$a.nquestions}};(您至少要回答{{$a.minquestions}})", - "addon.mod_lesson.ongoingcustom": "到目前为止,您已经从{{$a.currenthigh}}分中获得 {{$a.score}}分。", - "addon.mod_lesson.ongoingnormal": "您已经在{{$a.viewed}} 尝试中正确地回答了{{$a.correct}}。", - "addon.mod_lesson.or": "或", - "addon.mod_lesson.overview": "概览", - "addon.mod_lesson.preview": "预览", - "addon.mod_lesson.progressbarteacherwarning2": "因为您可以编辑此程序教学,所以您看不到进度条", - "addon.mod_lesson.progresscompleted": "您已完成此程序教学的 {{$a}}%", - "addon.mod_lesson.question": "题目", - "addon.mod_lesson.rawgrade": "原始分数", - "addon.mod_lesson.reports": "报表", - "addon.mod_lesson.response": "回应", - "addon.mod_lesson.retakefinishedinsync": "离线尝试同步了。您想检查一下吗?", - "addon.mod_lesson.retakelabelfull": "{{retake}}: {{grade}} {{timestart}} ({{duration}})", - "addon.mod_lesson.retakelabelshort": "{{retake}}: {{grade}} {{timestart}}", - "addon.mod_lesson.review": "检查", - "addon.mod_lesson.reviewlesson": "检查程序教学", - "addon.mod_lesson.reviewquestionback": "是的,我想再试一次", - "addon.mod_lesson.reviewquestioncontinue": "不,我只想继续下一题", - "addon.mod_lesson.secondpluswrong": "不完全正确,您想再试一次吗?", - "addon.mod_lesson.submit": "提交", - "addon.mod_lesson.teacherjumpwarning": "此程序教学使用{{$a.cluster}}跳转或{{$a.unseen}}跳转。将使用“下一页”跳转模式替代。请以学生身份登录来测试这些跳转。", - "addon.mod_lesson.teacherongoingwarning": "即时分数只显示给学生。请以学生身份登录测试即时分数", - "addon.mod_lesson.teachertimerwarning": "只有学生可以使用计时器。要测试计时器,请以学生身份登录。", - "addon.mod_lesson.thatsthecorrectanswer": "这是正确答案", - "addon.mod_lesson.thatsthewronganswer": "这是错误答案", - "addon.mod_lesson.timeremaining": "剩余时间", - "addon.mod_lesson.timetaken": "实际时间", - "addon.mod_lesson.unseenpageinbranch": "内容页中隐藏的题目", - "addon.mod_lesson.warningretakefinished": "这个尝试在网站上完成了。", - "addon.mod_lesson.welldone": "非常好!", - "addon.mod_lesson.youhaveseen": "您已经浏览了本程序教学的多个页面。
                      想从上次结束的页面开始吗?", - "addon.mod_lesson.youranswer": "您的答案", - "addon.mod_lesson.yourcurrentgradeisoutof": "您目前的成绩是{{$a.grade}}(总分{{$a.total}})", - "addon.mod_lesson.youshouldview": "您至少应该回答:{{$a}}", - "addon.mod_lti.errorgetlti": "获取模块数据时出错。", - "addon.mod_lti.errorinvalidlaunchurl": "启动URL无效。", - "addon.mod_lti.launchactivity": "启动活动", - "addon.mod_lti.modulenameplural": "外部工具", - "addon.mod_page.errorwhileloadingthepage": "加载页面内容时出错。", - "addon.mod_page.modulenameplural": "页面", - "addon.mod_quiz.answercolon": "答案:", - "addon.mod_quiz.attemptfirst": "第一次尝试", - "addon.mod_quiz.attemptlast": "最后一次尝试", - "addon.mod_quiz.attemptnumber": "尝试", - "addon.mod_quiz.attemptquiznow": "现在尝试测验", - "addon.mod_quiz.attemptstate": "状态", - "addon.mod_quiz.canattemptbutnotsubmit": "您可以在应用程序中尝试这个测验,但您需要在浏览器中提交尝试,原因如下:", - "addon.mod_quiz.cannotsubmitquizdueto": "这个测验尝试因为如下原因不能提交:", - "addon.mod_quiz.clearchoice": "清除我的选择", - "addon.mod_quiz.comment": "评论", - "addon.mod_quiz.completedon": "完成于", - "addon.mod_quiz.confirmclose": "一旦提交,您将不能再更改这次尝试的答案。", - "addon.mod_quiz.confirmcontinueoffline": "由于{{$a}},此尝试未同步。如果此后在其他设备上继续此尝试,可能会丢失数据。", - "addon.mod_quiz.confirmleavequizonerror": "保存答案时发生错误。您确定要离开测验吗?", - "addon.mod_quiz.confirmstart": "您的尝试将有一个{{$a}}的时间限制。当您开始时,计时器将开始倒数,不能暂停。您必须在它到期之前完成尝试。您确定现在开始吗?", - "addon.mod_quiz.confirmstartheader": "时间限制", - "addon.mod_quiz.connectionerror": "网络连接丢失。(自动保存失败)。\n\n请记下最近几分钟在本页面所输入的答案,然后尝试重新连接。\n\n一旦重新建立连接,您的答案将会被自动保存,同时这个消息将消失。", - "addon.mod_quiz.continueattemptquiz": "继续上次尝试", - "addon.mod_quiz.continuepreview": "继续上次预览", - "addon.mod_quiz.errorbehaviournotsupported": "这个测验不能在应用中进行尝试,因为应用程序不支持试题行为:", - "addon.mod_quiz.errordownloading": "下载所需数据时出错。", - "addon.mod_quiz.errorgetattempt": "获取尝试数据时出错。", - "addon.mod_quiz.errorgetquestions": "获取试题时出错。", - "addon.mod_quiz.errorgetquiz": "获取测验数据时出错。", - "addon.mod_quiz.errorparsequestions": "在读取试题时出现了错误。请在web浏览器中尝试这个测验。", - "addon.mod_quiz.errorquestionsnotsupported": "这个测验无法在应用中进行,因为它只包含应用程序不支持的试题:", - "addon.mod_quiz.errorrulesnotsupported": "这个测验不能在应用中尝试,因为应用程序不支持它的访问规则:", - "addon.mod_quiz.errorsaveattempt": "在保存尝试数据时发生了错误。", - "addon.mod_quiz.feedback": "反馈", - "addon.mod_quiz.finishattemptdots": "结束尝试…", - "addon.mod_quiz.finishnotsynced": "完成但没有同步", - "addon.mod_quiz.grade": "成绩", - "addon.mod_quiz.gradeaverage": "平均分", - "addon.mod_quiz.gradehighest": "最高分", - "addon.mod_quiz.grademethod": "评分方法", - "addon.mod_quiz.gradesofar": "{{$a.method}}: {{$a.mygrade}} / {{$a.quizgrade}}。", - "addon.mod_quiz.marks": "分数", - "addon.mod_quiz.modulenameplural": "测验", - "addon.mod_quiz.mustbesubmittedby": "此尝试必须由{{$a}}提交。", - "addon.mod_quiz.noquestions": "尚未添加试题", - "addon.mod_quiz.noreviewattempt": "不允许您检查此尝试。", - "addon.mod_quiz.notyetgraded": "仍未评分", - "addon.mod_quiz.opentoc": "打开导航弹窗。", - "addon.mod_quiz.outof": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.outofpercent": "{{$a.grade}}/{{$a.maxgrade}} ({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "总体反馈", - "addon.mod_quiz.overdue": "超时", - "addon.mod_quiz.overduemustbesubmittedby": "此次尝试现在已经过期了。它应该已经提交了。如果您想给这个测验评分的话,您必须通过{{$a}}提交。如果您没有那样提交,这次尝试的得分将不计算在内。", - "addon.mod_quiz.preview": "预览", - "addon.mod_quiz.previewquiznow": "现在预览测验", - "addon.mod_quiz.question": "试题", - "addon.mod_quiz.quiznavigation": "测验导航", - "addon.mod_quiz.quizpassword": "测验密码", - "addon.mod_quiz.reattemptquiz": "再次尝试测验", - "addon.mod_quiz.requirepasswordmessage": "要尝试这个测验,您需要知道测验密码", - "addon.mod_quiz.returnattempt": "返回尝试", - "addon.mod_quiz.review": "检查", - "addon.mod_quiz.reviewofattempt": "检查尝试{{$a}}", - "addon.mod_quiz.reviewofpreview": "检查预览", - "addon.mod_quiz.showall": "所有题目显示在一页", - "addon.mod_quiz.showeachpage": "一次显示一页", - "addon.mod_quiz.startattempt": "开始尝试", - "addon.mod_quiz.startedon": "开始时间", - "addon.mod_quiz.stateabandoned": "从未提交", - "addon.mod_quiz.statefinished": "完成", - "addon.mod_quiz.statefinisheddetails": "已提交 {{$a}}", - "addon.mod_quiz.stateinprogress": "进行中", - "addon.mod_quiz.stateoverdue": "超时", - "addon.mod_quiz.stateoverduedetails": "必须由{{$a}}提交", - "addon.mod_quiz.status": "状态", - "addon.mod_quiz.submitallandfinish": "全部提交并结束", - "addon.mod_quiz.summaryofattempt": "尝试概要", - "addon.mod_quiz.summaryofattempts": "您上次尝试的概要", - "addon.mod_quiz.timeleft": "剩余时间", - "addon.mod_quiz.timetaken": "耗时", - "addon.mod_quiz.warningattemptfinished": "离线尝试在站点上完成或未找到,被丢弃。", - "addon.mod_quiz.warningdatadiscarded": "一些离线答案被丢弃,因为这些试题被在线修改了。", - "addon.mod_quiz.warningdatadiscardedfromfinished": "尝试未完成,因为一些离线答案被丢弃了。请检查您的答案,然后重新提交。", - "addon.mod_quiz.warningquestionsnotsupported": "这个测验包含了应用程序不支持的试题:", - "addon.mod_quiz.yourfinalgradeis": "您这次测验的最终成绩是{{$a}}。", - "addon.mod_resource.errorwhileloadingthecontent": "加载内容时出错。", - "addon.mod_resource.modifieddate": "修改 {{$a}}", - "addon.mod_resource.modulenameplural": "文件", - "addon.mod_resource.openthefile": "打开文件", - "addon.mod_resource.uploadeddate": "上传 {{$a}}", - "addon.mod_scorm.asset": "资产", - "addon.mod_scorm.assetlaunched": "资产 - 查看", - "addon.mod_scorm.attempts": "尝试", - "addon.mod_scorm.averageattempt": "平均的尝试", - "addon.mod_scorm.browse": "预览", - "addon.mod_scorm.browsed": "浏览", - "addon.mod_scorm.browsemode": "预览模式", - "addon.mod_scorm.cannotcalculategrade": "成绩无法计算。", - "addon.mod_scorm.completed": "已完成", - "addon.mod_scorm.contents": "内容", - "addon.mod_scorm.dataattemptshown": "该数据属于尝试号{{number}}。", - "addon.mod_scorm.enter": "输入", - "addon.mod_scorm.errorcreateofflineattempt": "在创建新的离线尝试时发生了错误。请再试一次。", - "addon.mod_scorm.errordownloadscorm": "下载SCORM: \"{{name}}\"出错。", - "addon.mod_scorm.errorgetscorm": "获取SCORM数据出错。", - "addon.mod_scorm.errorinvalidversion": "对不起,应用程序只支持SCORM 1.2。", - "addon.mod_scorm.errornotdownloadable": "SCORM包下载被禁用。请联系您的网站管理员。", - "addon.mod_scorm.errornovalidsco": "这个SCORM没有一个可见的SCO加载。", - "addon.mod_scorm.errorpackagefile": "对不起,应用程序只支持ZIP包。", - "addon.mod_scorm.errorsyncscorm": "同步时发生错误。请再试一次。", - "addon.mod_scorm.exceededmaxattempts": "您已经达到最大尝试次数。", - "addon.mod_scorm.failed": "失败", - "addon.mod_scorm.firstattempt": "首次尝试", - "addon.mod_scorm.gradeaverage": "平均分", - "addon.mod_scorm.gradeforattempt": "尝试成绩", - "addon.mod_scorm.gradehighest": "最高分", - "addon.mod_scorm.grademethod": "评分方法", - "addon.mod_scorm.gradereported": "成绩报告", - "addon.mod_scorm.gradescoes": "学习对象", - "addon.mod_scorm.gradesum": "总分", - "addon.mod_scorm.highestattempt": "最高分尝试", - "addon.mod_scorm.incomplete": "不完整", - "addon.mod_scorm.lastattempt": "最后完成的尝试", - "addon.mod_scorm.modulenameplural": "SCORM 包", - "addon.mod_scorm.newattempt": "开始新尝试", - "addon.mod_scorm.noattemptsallowed": "允许尝试的次数", - "addon.mod_scorm.noattemptsmade": "您已经尝试的次数", - "addon.mod_scorm.notattempted": "未尝试", - "addon.mod_scorm.offlineattemptnote": "这个尝试的数据没有同步。", - "addon.mod_scorm.offlineattemptovermax": "由于超过了最大尝试次数,因此无法发送此尝试。", - "addon.mod_scorm.organizations": "组织", - "addon.mod_scorm.passed": "已通过", - "addon.mod_scorm.reviewmode": "检查模式", - "addon.mod_scorm.score": "得分", - "addon.mod_scorm.scormstatusnotdownloaded": "这个SCORM包没有下载。当您打开它时,它会自动下载。", - "addon.mod_scorm.scormstatusoutdated": "这个SCORM包在上次下载后被修改了。当您打开它时,它会自动下载。", - "addon.mod_scorm.suspended": "暂停", - "addon.mod_scorm.toc": "目录", - "addon.mod_scorm.warningofflinedatadeleted": "来自尝试{{number}}的一些离线数据已被丢弃,因为它不能被视为新的尝试。", - "addon.mod_scorm.warningsynconlineincomplete": "有些尝试无法与网站同步,因为最后一次在线尝试尚未完成。请先完成在线尝试。", - "addon.mod_survey.cannotsubmitsurvey": "抱歉,在提交您的问卷调查时出现了问题。请再试一次。", - "addon.mod_survey.errorgetsurvey": "获取问卷调查数据时出错。", - "addon.mod_survey.ifoundthat": "我发现", - "addon.mod_survey.ipreferthat": "我希望", - "addon.mod_survey.modulenameplural": "问卷调查", - "addon.mod_survey.responses": "回复", - "addon.mod_survey.results": "结果", - "addon.mod_survey.surveycompletednograph": "您已经完成了这个问卷调查。", - "addon.mod_url.accessurl": "访问该网页地址", - "addon.mod_url.modulenameplural": "网页地址", - "addon.mod_url.pointingtourl": "资源指向的网页地址。", - "addon.mod_wiki.cannoteditpage": "您不能编辑此页面。", - "addon.mod_wiki.createpage": "建立页面", - "addon.mod_wiki.editingpage": "编辑页面“{{$a}}”", - "addon.mod_wiki.errorloadingpage": "加载页面时发生错误。", - "addon.mod_wiki.errornowikiavailable": "这个wiki还没有任何内容。", - "addon.mod_wiki.gowikihome": "去 Wiki首页", - "addon.mod_wiki.map": "地图", - "addon.mod_wiki.modulenameplural": "Wiki", - "addon.mod_wiki.newpagehdr": "新页面", - "addon.mod_wiki.newpagetitle": "新页面标题", - "addon.mod_wiki.nocontent": "此页面无内容", - "addon.mod_wiki.notingroup": "不在小组内", - "addon.mod_wiki.pageexists": "这个页面已经存在。", - "addon.mod_wiki.pagename": "页名", - "addon.mod_wiki.subwiki": "子wiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki页面", - "addon.mod_wiki.titleshouldnotbeempty": "标题不应该是空的", - "addon.mod_wiki.viewpage": "查看页面", - "addon.mod_wiki.wikipage": "Wiki页面", - "addon.mod_wiki.wrongversionlock": "当您正在编辑时,另一个用户已编辑此页面,而您的内容已过时。", - "addon.mod_workshop.alreadygraded": "已评分", - "addon.mod_workshop.areainstructauthors": "提交说明", - "addon.mod_workshop.areainstructreviewers": "评价说明", - "addon.mod_workshop.assess": "评价", - "addon.mod_workshop.assessedsubmission": "已评价的提交", - "addon.mod_workshop.assessmentform": "评价表单", - "addon.mod_workshop.assessmentsettings": "评价设置", - "addon.mod_workshop.assessmentstrategynotsupported": "不支持评估策略{{$a}}", - "addon.mod_workshop.assessmentweight": "评价权重", - "addon.mod_workshop.assignedassessments": "分配提交进行评价", - "addon.mod_workshop.assignedassessmentsnone": "您没有分配要评价的提交", - "addon.mod_workshop.conclusion": "总结", - "addon.mod_workshop.createsubmission": "添加提交", - "addon.mod_workshop.deletesubmission": "删除提交", - "addon.mod_workshop.editsubmission": "编辑提交", - "addon.mod_workshop.feedbackauthor": "给作者反馈", - "addon.mod_workshop.feedbackby": "{{$a}}的反馈", - "addon.mod_workshop.feedbackreviewer": "给评价人反馈", - "addon.mod_workshop.givengrades": "给出的成绩", - "addon.mod_workshop.gradecalculated": "计算提交分数", - "addon.mod_workshop.gradeinfo": "成绩:{{$a.received}}/{{$a.max}}", - "addon.mod_workshop.gradeover": "覆盖提交成绩", - "addon.mod_workshop.gradesreport": "互动评价成绩报表", - "addon.mod_workshop.gradinggrade": "评价成绩", - "addon.mod_workshop.gradinggradecalculated": "计算评价成绩", - "addon.mod_workshop.gradinggradeof": "评价({{$a}})的成绩", - "addon.mod_workshop.gradinggradeover": "覆盖评价成绩", - "addon.mod_workshop.modulenameplural": "互动评价", - "addon.mod_workshop.nogradeyet": "还没有成绩", - "addon.mod_workshop.notassessed": "还没有评价", - "addon.mod_workshop.notoverridden": "没有覆盖", - "addon.mod_workshop.noyoursubmission": "您还没有提交您的作品", - "addon.mod_workshop.overallfeedback": "总体反馈", - "addon.mod_workshop.publishedsubmissions": "发布提交", - "addon.mod_workshop.publishsubmission": "发布提交", - "addon.mod_workshop.publishsubmission_help": "当互动评价关闭后,其他人仍可以看到已发布的提交。", - "addon.mod_workshop.reassess": "重新评价", - "addon.mod_workshop.receivedgrades": "已获得成绩", - "addon.mod_workshop.submissionattachment": "附件", - "addon.mod_workshop.submissioncontent": "提交内容", - "addon.mod_workshop.submissiondeleteconfirm": "您确定要删除以下提交吗?", - "addon.mod_workshop.submissiongrade": "提交的成绩", - "addon.mod_workshop.submissiongradeof": "提交({{$a}})的成绩", - "addon.mod_workshop.submissionrequiredcontent": "您需要输入一些文本或添加一个文件。", - "addon.mod_workshop.submissionrequiredtitle": "您需要输入一个标题。", - "addon.mod_workshop.submissionsreport": "互动评价提交报告", - "addon.mod_workshop.submissiontitle": "标题", - "addon.mod_workshop.switchphase10": "切换到设置阶段", - "addon.mod_workshop.switchphase20": "切换到提交阶段", - "addon.mod_workshop.switchphase30": "切换到评价阶段", - "addon.mod_workshop.switchphase40": "切换到成绩核定阶段", - "addon.mod_workshop.switchphase50": "关闭互动评价", - "addon.mod_workshop.userplan": "互动评价计划表", - "addon.mod_workshop.userplancurrentphase": "当前阶段", - "addon.mod_workshop.warningassessmentmodified": "该提交在网站上进行了修改。", - "addon.mod_workshop.warningsubmissionmodified": "该评价在网站上进行了修改。", - "addon.mod_workshop.weightinfo": "权重:{{$a}}", - "addon.mod_workshop.yourassessment": "您的评价", - "addon.mod_workshop.yourassessmentfor": "您对{{$a}}的评价", - "addon.mod_workshop.yourgrades": "您的成绩", - "addon.mod_workshop.yoursubmission": "您的提交", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "评论 {{$a}}", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "为 {{$a}}评分", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "采分点 {{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "您必须为此采分点打分", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "评论 {{$a}}", - "addon.mod_workshop_assessment_comments.dimensionnumber": "采分点 {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "评论 {{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "为 {{$a}}评分", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "声明 {{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "标准 {{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "您必须从这些项目中选择一项", - "addon.notes.addnewnote": "添加新备注", - "addon.notes.coursenotes": "课程备注", - "addon.notes.deleteconfirm": "删除该备注吗?", - "addon.notes.eventnotecreated": "创建备注", - "addon.notes.eventnotedeleted": "删除备注", - "addon.notes.nonotes": "没有这种类型的备注", - "addon.notes.note": "备注", - "addon.notes.notes": "备注", - "addon.notes.personalnotes": "个人备注", - "addon.notes.publishstate": "场景", - "addon.notes.sitenotes": "网站备注", - "addon.notes.userwithid": "ID为 {{id}}的用户", - "addon.notes.warningnotenotsent": "不能在课程 {{course}}中添加备注。{{error}}", - "addon.notifications.errorgetnotifications": "获取通知出错。", - "addon.notifications.markallread": "全部标记为已读", - "addon.notifications.notificationpreferences": "通知偏好", - "addon.notifications.notifications": "通知", - "addon.notifications.playsound": "播放声音", - "addon.notifications.therearentnotificationsyet": "没有通知。", - "addon.storagemanager.deletecourse": "卸载所有课程数据", - "addon.storagemanager.deletecourses": "卸载所有课程数据", - "addon.storagemanager.deletedatafrom": "从{{name}}卸载数据", - "addon.storagemanager.info": "存储在设备上的文件可用让应用程序运行得更快,并使应用程序可以离线使用。如果需要释放存储空间,可以安全地卸载文件。", - "addon.storagemanager.managestorage": "管理存储", - "addon.storagemanager.storageused": "文件存储使用:", - "assets.countries.AD": "安道尔", - "assets.countries.AE": "阿拉伯联合酋长国", - "assets.countries.AF": "阿富汗", - "assets.countries.AG": "安提瓜和巴布达", - "assets.countries.AI": "安圭拉岛", - "assets.countries.AL": "阿尔巴尼亚", - "assets.countries.AM": "亚美尼亚", - "assets.countries.AO": "安哥拉", - "assets.countries.AQ": "南极洲", - "assets.countries.AR": "阿根廷", - "assets.countries.AS": "美属萨摩亚群岛", - "assets.countries.AT": "奥地利", - "assets.countries.AU": "澳大利亚", - "assets.countries.AW": "阿鲁巴岛", - "assets.countries.AX": "奥兰群岛", - "assets.countries.AZ": "阿塞拜疆", - "assets.countries.BA": "波斯尼亚和黑塞哥维那", - "assets.countries.BB": "巴巴多斯", - "assets.countries.BD": "孟加拉国", - "assets.countries.BE": "比利时", - "assets.countries.BF": "布基纳法索", - "assets.countries.BG": "保加利亚", - "assets.countries.BH": "巴林岛", - "assets.countries.BI": "布隆迪", - "assets.countries.BJ": "贝宁湾", - "assets.countries.BL": "圣巴泰勒米岛", - "assets.countries.BM": "百慕大群岛", - "assets.countries.BN": "文莱", - "assets.countries.BO": "玻利维亚多民族国", - "assets.countries.BQ": "博内尔,圣尤斯特歇斯和萨巴", - "assets.countries.BR": "巴西", - "assets.countries.BS": "巴哈马", - "assets.countries.BT": "不丹", - "assets.countries.BV": "布韦群岛", - "assets.countries.BW": "博茨瓦纳", - "assets.countries.BY": "白俄罗斯", - "assets.countries.BZ": "伯利兹", - "assets.countries.CA": "加拿大", - "assets.countries.CC": "科科斯群岛", - "assets.countries.CD": "刚果民主共和国", - "assets.countries.CF": "中非共和国", - "assets.countries.CG": "刚果", - "assets.countries.CH": "瑞士", - "assets.countries.CI": "科特迪瓦", - "assets.countries.CK": "库克群岛", - "assets.countries.CL": "智利", - "assets.countries.CM": "喀麦隆", - "assets.countries.CN": "中国", - "assets.countries.CO": "哥伦比亚", - "assets.countries.CR": "哥斯达黎加", - "assets.countries.CU": "古巴", - "assets.countries.CV": "佛得角", - "assets.countries.CW": "库拉索", - "assets.countries.CX": "圣诞岛", - "assets.countries.CY": "塞浦路斯", - "assets.countries.CZ": "捷克", - "assets.countries.DE": "德国", - "assets.countries.DJ": "吉布提", - "assets.countries.DK": "丹麦", - "assets.countries.DM": "多米尼加", - "assets.countries.DO": "多米尼加共和国", - "assets.countries.DZ": "阿尔及利亚", - "assets.countries.EC": "厄瓜多尔", - "assets.countries.EE": "爱沙尼亚", - "assets.countries.EG": "埃及", - "assets.countries.EH": "西撒哈拉", - "assets.countries.ER": "厄立特里亚", - "assets.countries.ES": "西班牙", - "assets.countries.ET": "埃塞俄比亚", - "assets.countries.FI": "芬兰", - "assets.countries.FJ": "斐济", - "assets.countries.FK": "福克兰群岛(马尔维纳斯)", - "assets.countries.FM": "密克罗尼西亚联邦", - "assets.countries.FO": "法罗群岛", - "assets.countries.FR": "法国", - "assets.countries.GA": "加蓬", - "assets.countries.GB": "英国", - "assets.countries.GD": "格林纳达", - "assets.countries.GE": "格鲁吉亚", - "assets.countries.GF": "法属圭亚那", - "assets.countries.GG": "根西岛", - "assets.countries.GH": "加纳", - "assets.countries.GI": "直布罗陀", - "assets.countries.GL": "格陵兰", - "assets.countries.GM": "冈比亚", - "assets.countries.GN": "几内亚", - "assets.countries.GP": "瓜德罗普", - "assets.countries.GQ": "赤道几内亚", - "assets.countries.GR": "希腊", - "assets.countries.GS": "南乔治亚与南三文治群岛", - "assets.countries.GT": "危地马拉", - "assets.countries.GU": "关岛", - "assets.countries.GW": "几内亚比绍", - "assets.countries.GY": "圭亚那", - "assets.countries.HK": "中国香港", - "assets.countries.HM": "赫德和麦克唐纳群岛", - "assets.countries.HN": "洪都拉斯", - "assets.countries.HR": "克罗地亚", - "assets.countries.HT": "海地", - "assets.countries.HU": "匈牙利", - "assets.countries.ID": "印度尼西亚", - "assets.countries.IE": "爱尔兰", - "assets.countries.IL": "以色列", - "assets.countries.IM": "马恩岛", - "assets.countries.IN": "印度", - "assets.countries.IO": "英属印度洋地区", - "assets.countries.IQ": "伊拉克", - "assets.countries.IR": "伊朗", - "assets.countries.IS": "冰岛", - "assets.countries.IT": "意大利", - "assets.countries.JE": "泽西岛", - "assets.countries.JM": "牙买加", - "assets.countries.JO": "约旦王国", - "assets.countries.JP": "日本", - "assets.countries.KE": "肯尼亚", - "assets.countries.KG": "吉尔吉斯斯坦", - "assets.countries.KH": "柬埔寨", - "assets.countries.KI": "吉里巴斯", - "assets.countries.KM": "科摩罗伊斯兰联邦共和国", - "assets.countries.KN": "尼维斯", - "assets.countries.KP": "韩国", - "assets.countries.KR": "朝鲜", - "assets.countries.KW": "科威特", - "assets.countries.KY": "开曼群岛", - "assets.countries.KZ": "哈萨克斯坦", - "assets.countries.LA": "老挝", - "assets.countries.LB": "黎巴嫩", - "assets.countries.LC": "圣卢西亚", - "assets.countries.LI": "列支敦士登", - "assets.countries.LK": "斯里兰卡", - "assets.countries.LR": "利比里亚", - "assets.countries.LS": "赖索托", - "assets.countries.LT": "立陶宛", - "assets.countries.LU": "卢森堡公国", - "assets.countries.LV": "拉脱维亚", - "assets.countries.LY": "利比亚", - "assets.countries.MA": "摩洛哥", - "assets.countries.MC": "摩纳哥", - "assets.countries.MD": "摩尔多瓦", - "assets.countries.ME": "黑山", - "assets.countries.MF": "圣马丁(法属)", - "assets.countries.MG": "马达加斯加", - "assets.countries.MH": "马歇尔群岛", - "assets.countries.MK": "马其顿", - "assets.countries.ML": "马利", - "assets.countries.MM": "缅甸", - "assets.countries.MN": "蒙古", - "assets.countries.MO": "中国澳门", - "assets.countries.MP": "北马里亚纳群岛", - "assets.countries.MQ": "马提尼克岛", - "assets.countries.MR": "茅利塔尼亚", - "assets.countries.MS": "蒙特塞拉特岛", - "assets.countries.MT": "马耳他", - "assets.countries.MU": "毛里求斯", - "assets.countries.MV": "马尔地夫", - "assets.countries.MW": "马拉威", - "assets.countries.MX": "墨西哥", - "assets.countries.MY": "马来西亚", - "assets.countries.MZ": "莫桑比克", - "assets.countries.NA": "纳密比亚", - "assets.countries.NC": "苏格兰", - "assets.countries.NE": "尼日", - "assets.countries.NF": "诺福克群岛", - "assets.countries.NG": "奈及利亚", - "assets.countries.NI": "尼加拉瓜", - "assets.countries.NL": "荷兰", - "assets.countries.NO": "挪威", - "assets.countries.NP": "尼泊尔", - "assets.countries.NR": "诺鲁", - "assets.countries.NU": "纽埃岛", - "assets.countries.NZ": "新西兰", - "assets.countries.OM": "澳门", - "assets.countries.PA": "巴拿马", - "assets.countries.PE": "秘鲁", - "assets.countries.PF": "波利尼西亚", - "assets.countries.PG": "新几内亚", - "assets.countries.PH": "菲律宾", - "assets.countries.PK": "巴基斯坦", - "assets.countries.PL": "波兰", - "assets.countries.PM": "密客隆岛", - "assets.countries.PN": "皮特凯恩", - "assets.countries.PR": "波多黎各", - "assets.countries.PS": "巴勒斯坦", - "assets.countries.PT": "葡萄牙", - "assets.countries.PW": "帕劳群岛", - "assets.countries.PY": "巴拉圭", - "assets.countries.QA": "卡塔尔", - "assets.countries.RE": "留尼旺岛", - "assets.countries.RO": "罗马尼亚", - "assets.countries.RS": "塞尔维亚", - "assets.countries.RU": "俄罗斯", - "assets.countries.RW": "卢旺达", - "assets.countries.SA": "沙特阿拉伯", - "assets.countries.SB": "所罗门群岛", - "assets.countries.SC": "赛席尔群岛", - "assets.countries.SD": "苏丹", - "assets.countries.SE": "瑞典", - "assets.countries.SG": "新加坡", - "assets.countries.SH": "圣赫勒拿岛,阿森松岛和特里斯坦 - 达库尼亚群岛", - "assets.countries.SI": "斯洛文尼亚", - "assets.countries.SJ": "斯瓦尔巴特群岛", - "assets.countries.SK": "斯洛伐克", - "assets.countries.SL": "塞拉里昂", - "assets.countries.SM": "圣马力诺", - "assets.countries.SN": "塞内加尔", - "assets.countries.SO": "索马里", - "assets.countries.SR": "苏利南", - "assets.countries.SS": "南苏丹", - "assets.countries.ST": "普林西比", - "assets.countries.SV": "萨尔瓦多", - "assets.countries.SX": "圣马丁岛(荷兰的部分)", - "assets.countries.SY": "叙利亚", - "assets.countries.SZ": "斯威士兰", - "assets.countries.TC": "土耳其", - "assets.countries.TD": "查德", - "assets.countries.TF": "法国南部", - "assets.countries.TG": "多哥", - "assets.countries.TH": "泰国", - "assets.countries.TJ": "塔吉克斯坦", - "assets.countries.TK": "托克劳群岛", - "assets.countries.TL": "东帝汶", - "assets.countries.TM": "土库曼斯坦", - "assets.countries.TN": "突尼斯", - "assets.countries.TO": "东加王国", - "assets.countries.TR": "土耳其", - "assets.countries.TT": "特立尼达和多巴哥", - "assets.countries.TV": "吐瓦鲁", - "assets.countries.TW": "中国台湾", - "assets.countries.TZ": "坦桑尼亚", - "assets.countries.UA": "乌克兰", - "assets.countries.UG": "乌干达", - "assets.countries.UM": "美国本土外小岛屿", - "assets.countries.US": "美国", - "assets.countries.UY": "乌拉圭", - "assets.countries.UZ": "乌兹别克斯坦", - "assets.countries.VA": "梵蒂冈", - "assets.countries.VC": "格陵兰的", - "assets.countries.VE": "委内瑞拉玻利瓦尔共和国", - "assets.countries.VG": "英属维金岛", - "assets.countries.VI": "美属维金岛", - "assets.countries.VN": "越南", - "assets.countries.VU": "瓦努阿图", - "assets.countries.WF": "沃利斯福特拉群岛", - "assets.countries.WS": "萨摩亚", - "assets.countries.YE": "也门", - "assets.countries.YT": "马约特岛", - "assets.countries.ZA": "南非", - "assets.countries.ZM": "赞比亚", - "assets.countries.ZW": "津巴布韦", - "assets.mimetypes.application/epub_zip": "EPUB 电子书", - "assets.mimetypes.application/msword": "Word 文档", - "assets.mimetypes.application/pdf": "PDF 文档", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle 备份", - "assets.mimetypes.application/vnd.ms-excel": "Excel 数据表", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "启用宏的Excel 2007工作簿", - "assets.mimetypes.application/vnd.ms-powerpoint": "PowerPoint 演示文档", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument电子表格", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument电子表格模板", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument文本文档", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument文本模板", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument Web页面模板", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "PowerPoint 2007 演示文档", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 幻灯片", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 电子表格", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 模板", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 文档", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork主旨演讲", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork数字电子表格", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages 文字处理文档", - "assets.mimetypes.application/x-javascript": "", - "assets.mimetypes.application/x-mspublisher": "公开发布文档", - "assets.mimetypes.application/x-shockwave-flash": "flash 动画", - "assets.mimetypes.application/xhtml_xml": "XHTML文档", - "assets.mimetypes.archive": "存档({{$a.EXT}})", - "assets.mimetypes.audio": "音频文件({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "普通文件", - "assets.mimetypes.group:archive": "存档文件", - "assets.mimetypes.group:audio": "音频文件", - "assets.mimetypes.group:document": "文档文件", - "assets.mimetypes.group:html_audio": "浏览器支持的音频文件", - "assets.mimetypes.group:html_track": "HTML 格式文件", - "assets.mimetypes.group:html_video": "浏览器支持的视频文件", - "assets.mimetypes.group:image": "图像文件", - "assets.mimetypes.group:presentation": "演示文稿", - "assets.mimetypes.group:sourcecode": "源代码", - "assets.mimetypes.group:spreadsheet": "表单文件", - "assets.mimetypes.group:video": "视频文件", - "assets.mimetypes.group:web_audio": "网络上使用的音频文件", - "assets.mimetypes.group:web_file": "网络文件", - "assets.mimetypes.group:web_image": "网络上使用的图像文件", - "assets.mimetypes.group:web_video": "网络上使用的视频文件", - "assets.mimetypes.image": "图像({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "Windows图标", - "assets.mimetypes.text/css": "CSS层叠样式表", - "assets.mimetypes.text/csv": "逗号(英文)分隔的值", - "assets.mimetypes.text/html": "HTML 文档", - "assets.mimetypes.text/plain": "文本文件", - "assets.mimetypes.text/rtf": "RTF 文档", - "assets.mimetypes.text/vtt": "网络视频文字轨道", - "assets.mimetypes.video": "视频文件({{$a.EXT}})", - "core.accounts": "帐户", - "core.add": "添加", - "core.agelocationverification": "核实年龄及地点", - "core.ago": "{{$a}} 前", - "core.all": "所有", - "core.allgroups": "所有组", - "core.allparticipants": "所有成员", - "core.answer": "回答", - "core.answered": "已回答", - "core.areyousure": "您确定吗?", - "core.back": "返回", - "core.block.blocks": "版块", - "core.browser": "浏览器", - "core.cancel": "取消", - "core.cannotconnect": "无法连接", - "core.cannotconnecttrouble": "我们连接到您的网站有困难。", - "core.cannotconnectverify": "请检查地址是否正确。", - "core.cannotdownloadfiles": "文件下载被禁用。请联系您的网站管理员。", - "core.captureaudio": "录制音频", - "core.capturedimage": "拍摄照片。", - "core.captureimage": "拍摄照片", - "core.capturevideo": "录制视频", - "core.category": "类别", - "core.choose": "选择", - "core.choosedots": "选择...", - "core.clearsearch": "清除搜索", - "core.clicktohideshow": "单击以展开或折叠", - "core.clicktoseefull": "点击查看完整内容。", - "core.close": "关闭", - "core.comments": "评论", - "core.comments.addcomment": "添加一条评论...", - "core.comments.comments": "评论", - "core.comments.commentscount": "评论({{$a}})", - "core.comments.commentsnotworking": "无法检索评论", - "core.comments.deletecommentbyon": "删除 {{$a.user}} 在 {{$a.time}} 发表的评论", - "core.comments.eventcommentcreated": "评论了", - "core.comments.eventcommentdeleted": "删除评论", - "core.comments.nocomments": "无评论", - "core.comments.savecomment": "保存评论", - "core.comments.warningcommentsnotsent": "不能同步评论。{{error}}", - "core.commentscount": "评论({{$a}})", - "core.completion-alt-auto-fail": "已完成:{{$a}}(未及格)", - "core.completion-alt-auto-n": "未完成:{{$a}}", - "core.completion-alt-auto-n-override": "未完成: {{$a.modname}} (由 {{$a.overrideuser}}设置)", - "core.completion-alt-auto-pass": "已完成:{{$a}}(及格)", - "core.completion-alt-auto-y": "已完成:{{$a}}", - "core.completion-alt-auto-y-override": "已完成: {{$a.modname}} (由{{$a.overrideuser}}设置)", - "core.completion-alt-manual-n": "未完成:{{$a}}。选择标记为完成。", - "core.completion-alt-manual-n-override": "未完成: {{$a.modname}} (由 {{$a.overrideuser}}设置)。选择标记为完成。", - "core.completion-alt-manual-y": "已完成:{{$a}}。选择标记为未完成。", - "core.completion-alt-manual-y-override": "已完成: {{$a.modname}} (由{{$a.overrideuser}}设置)。选择标记为未完成。", - "core.confirmcanceledit": "您确定要离开这个页面吗?所有的更改都将丢失。", - "core.confirmdeletefile": "您确定要删除此文件吗?", - "core.confirmgotabroot": "您确定要返回{{name}}吗?", - "core.confirmgotabrootdefault": "您确定要转到当前选项卡的初始页吗?", - "core.confirmleaveunknownchanges": "您确定要离开这个页面吗?如果您有未保存的更改,它们将丢失。", - "core.confirmloss": "您确定吗?所有的更改都将丢失。", - "core.confirmopeninbrowser": "您想在web浏览器中打开它吗?", - "core.considereddigitalminor": "您太小了,不能在这个网站上创建帐户。", - "core.content": "内容", - "core.contenteditingsynced": "您正在编辑的内容已经同步。", - "core.contentlinks.chooseaccount": "选择账户", - "core.contentlinks.chooseaccounttoopenlink": "选择一个帐户来打开链接。", - "core.contentlinks.confirmurlothersite": "这个链接属于另一个站点。您想打开吗?", - "core.contentlinks.errornoactions": "找不到要对该链接执行的操作。", - "core.contentlinks.errornosites": "找不到处理此链接的任何网站。", - "core.contentlinks.errorredirectothersite": "重定向URL不能指向其他站点。", - "core.continue": "继续", - "core.copiedtoclipboard": "复制到剪贴板的文本", - "core.copytoclipboard": "复制到剪贴板", - "core.course": "课程", - "core.course.activitydisabled": "您的组织已在移动应用程序中禁用此活动。", - "core.course.activitynotyetviewableremoteaddon": "您的组织安装了一个尚不支持的插件。", - "core.course.activitynotyetviewablesiteupgradeneeded": "您组织的Moodle安装需要更新。", - "core.course.allsections": "所有小节", - "core.course.askadmintosupport": "请联系网站管理员,告诉他们您想在Moodle移动应用中使用这个活动。", - "core.course.availablespace": "您当前大约有 {{available}} 空闲空间。", - "core.course.cannotdeletewhiledownloading": "在下载活动时无法删除文件。请等待下载完成。", - "core.course.confirmdeletemodulefiles": "您确定要删除这些文件吗?", - "core.course.confirmdownload": "您将要下载{{size}}。您确定要继续吗?", - "core.course.confirmdownloadunknownsize": "无法计算下载的大小。您确定要继续吗?", - "core.course.confirmdownloadzerosize": "您即将开始下载。{{availableSpace}}您确定要继续吗?", - "core.course.confirmlimiteddownload": "您目前没有连接到Wi-Fi。", - "core.course.confirmpartialdownloadsize": "您将下载至少{{size}}。{{availableSpace}} 您确定要继续吗?", - "core.course.contents": "内容", - "core.course.couldnotloadsectioncontent": "无法加载该小节内容。请稍后再试。", - "core.course.couldnotloadsections": "无法加载该小节。请稍后再试。", - "core.course.coursesummary": "课程简介", - "core.course.downloadcourse": "下载课程", - "core.course.errordownloadingcourse": "下载课程时出错。", - "core.course.errordownloadingsection": "下载小节时出错。", - "core.course.errorgetmodule": "获取活动数据时出错。", - "core.course.hiddenfromstudents": "对学生隐藏", - "core.course.hiddenoncoursepage": "可用,但不显示在课程页面", - "core.course.insufficientavailablequota": "您的设备无法分配空间来保存此下载。它可能会为应用程序和系统更新预留空间。请先清理一些存储空间。", - "core.course.insufficientavailablespace": "您正在尝试下载{{size}}。这将使您的设备没有足够的空间正常运行。请先清理一些存储空间。", - "core.course.manualcompletionnotsynced": "手动进度没有同步。", - "core.course.nocontentavailable": "目前没有可用的内容。", - "core.course.overriddennotice": "您在此活动中的最终成绩是手动调整的。", - "core.course.refreshcourse": "刷新课程", - "core.course.sections": "小节", - "core.course.useactivityonbrowser": "您仍然可以通过你的设备的网络浏览器使用它。", - "core.course.warningmanualcompletionmodified": "活动的手动进度在站点上进行了修改。", - "core.course.warningofflinemanualcompletiondeleted": "课程“{{name}}”的一些离线手动进度已被删除。{{error}}", - "core.coursedetails": "课程详情", - "core.coursenogroups": "您不是本课程任何小组的成员。", - "core.courses.addtofavourites": "标星课程", - "core.courses.allowguests": "该课程允许访客进入", - "core.courses.availablecourses": "可用的课程", - "core.courses.cannotretrievemorecategories": "不能检索到超过{{$a}}级的类别。", - "core.courses.categories": "课程类别", - "core.courses.confirmselfenrol": "您确定要报名参加这门课程吗?", - "core.courses.courses": "课程", - "core.courses.downloadcourses": "下载课程", - "core.courses.enrolme": "加入我", - "core.courses.errorloadcategories": "载入类别时发生错误。", - "core.courses.errorloadcourses": "载入课程时发生错误。", - "core.courses.errorloadplugins": "本课程所需的插件无法正确加载。请重新加载应用程序再试一次。", - "core.courses.errorsearching": "搜索时出现错误。", - "core.courses.errorselfenrol": "自己报名时发生错误。", - "core.courses.filtermycourses": "筛选我的课程", - "core.courses.frontpage": "首页", - "core.courses.hidecourse": "隐藏", - "core.courses.ignore": "忽略", - "core.courses.mycourses": "我的课程", - "core.courses.mymoodle": "个人主页", - "core.courses.nocourses": "没有课程信息显示。", - "core.courses.nocoursesyet": "此类中无课程", - "core.courses.nosearchresults": "无结果", - "core.courses.notenroled": "您没有加入此课程", - "core.courses.notenrollable": "您不能报名参加这个课程。", - "core.courses.password": "选课密码", - "core.courses.paymentrequired": "此课程需要付费才能进入。", - "core.courses.paypalaccepted": "接受PayPal支付", - "core.courses.reload": "重新载入", - "core.courses.removefromfavourites": "撤销标星", - "core.courses.search": "搜索", - "core.courses.searchcourses": "搜索课程", - "core.courses.searchcoursesadvice": "您可以使用“搜索课程”按钮来发现课程作为访客访问,或者在允许报名的课程中报名。", - "core.courses.selfenrolment": "自助选课", - "core.courses.sendpaymentbutton": "通过 Paypal 支付", - "core.courses.show": "恢复", - "core.courses.totalcoursesearchresults": "总课程:{{$a}}", - "core.currentdevice": "当前设备", - "core.datastoredoffline": "数据存储在设备中,因为它不能被发送。稍后会自动发送。", - "core.date": "日期", - "core.day": "天", - "core.days": "天", - "core.decsep": ".", - "core.defaultvalue": "默认 ({{$a}})", - "core.delete": "删除", - "core.deletedoffline": "删除离线", - "core.deleteduser": "删除用户", - "core.deleting": "删除中", - "core.description": "描述", - "core.desktop": "桌面", - "core.dfdaymonthyear": "月-日-年", - "core.dfdayweekmonth": "日,周 月", - "core.digitalminor": "未达数字承诺年龄", - "core.digitalminor_desc": "请家长/监护人联系:", - "core.discard": "丢弃", - "core.dismiss": "驳回", - "core.displayoptions": "显示选项", - "core.done": "完成", - "core.download": "下载", - "core.downloaded": "下载", - "core.downloading": "下载中", - "core.edit": "编辑", - "core.editor.autosavesucceeded": "已保存草稿。", - "core.editor.bold": "加粗", - "core.editor.clear": "清除格式", - "core.editor.h3": "大标题", - "core.editor.h4": "中标题", - "core.editor.h5": "小标题", - "core.editor.hidetoolbar": "隐藏工具栏", - "core.editor.italic": "斜体", - "core.editor.orderedlist": "有序列表", - "core.editor.p": "段落", - "core.editor.strike": "删除", - "core.editor.textrecovered": "此文本的草稿已自动恢复。", - "core.editor.toggle": "切换编辑器", - "core.editor.underline": "下划线", - "core.editor.unorderedlist": "无序列表", - "core.emptysplit": "如果左边的面板是空的或者正在加载,此页面将显示为空白。", - "core.error": "错误", - "core.errorchangecompletion": "更改进度状态时发生错误。请再试一次。", - "core.errordeletefile": "删除文件出错。请再试一次。", - "core.errordownloading": "下载文件出错。", - "core.errordownloadingsomefiles": "下载文件错误。可能丢失了一些文件。", - "core.errorfileexistssamename": "此名称的文件已经存在。", - "core.errorinvalidform": "表单包含无效数据。请检查所有必需的字段都已填写,数据是否有效。", - "core.errorinvalidresponse": "无效的响应。如果错误持续存在,请与网站管理员联系。", - "core.errorloadingcontent": "加载内容出错。", - "core.errorofflinedisabled": "您的站点禁用脱机浏览。您需要连接到互联网才能使用这个应用程序。", - "core.erroropenfilenoapp": "打开文件错误:没有找到打开该类型文件的应用程序。", - "core.erroropenfilenoextension": "打开文件时出错:文件没有扩展名。", - "core.erroropenpopup": "此活动试图打开一个弹出窗口。应用程序不支持这一功能。", - "core.errorrenamefile": "重命名文件出错。请再试一次。", - "core.errorsomedatanotdownloaded": "如果您下载了此活动,请注意,由于性能和数据使用原因,在下载过程中有些数据没有下载。", - "core.errorsync": "同步时发生错误。请再试一次。", - "core.errorsyncblocked": "这个{{$a}}现在不能同步,因为有一个进程正在进行。请稍后再试。如果问题仍然存在,请尝试重新启动应用程序。", - "core.explanationdigitalminor": "需要这些信息来确定您的年龄是否达到了同意的数字年龄。在这个年龄,个人可以同意条款和条件,他们的数据被合法存储和处理。", - "core.favourites": "标星的", - "core.filename": "文件名", - "core.filenameexist": "文件名已经存在:{{$a}}", - "core.filenotfound": "抱歉,找不到文件。", - "core.fileuploader.addfiletext": "添加文件", - "core.fileuploader.audio": "音频", - "core.fileuploader.camera": "相机", - "core.fileuploader.confirmuploadfile": "您将要上传{{size}}。您确定要继续吗?", - "core.fileuploader.confirmuploadunknownsize": "无法计算上传的大小。您确定要继续吗?", - "core.fileuploader.errorcapturingaudio": "捕捉音频出错。", - "core.fileuploader.errorcapturingimage": "捕捉图像出错。", - "core.fileuploader.errorcapturingvideo": "捕捉视频出错。", - "core.fileuploader.errorgettingimagealbum": "从相册获取图像出错。", - "core.fileuploader.errormustbeonlinetoupload": "您必须在线上传文件。", - "core.fileuploader.errornoapp": "您没有安装执行此操作的应用程序。", - "core.fileuploader.errorreadingfile": "读取文件时出错。", - "core.fileuploader.errorwhileuploading": "上传文件时发生错误。", - "core.fileuploader.file": "文件", - "core.fileuploader.filesofthesetypes": "可接受的文件类型:", - "core.fileuploader.fileuploaded": "文件上传成功。", - "core.fileuploader.invalidfiletype": "不能接受{{$a}}文件类型。", - "core.fileuploader.maxbytesfile": "文件{{$a.file}}太大。您可以上传的最大大小是{{$a.size}}。", - "core.fileuploader.more": "更多", - "core.fileuploader.photoalbums": "相册", - "core.fileuploader.readingfile": "读取文件", - "core.fileuploader.readingfileperc": "读取文件: {{$a}}%", - "core.fileuploader.selectafile": "选择一个文件", - "core.fileuploader.uploadafile": "上传一个文件", - "core.fileuploader.uploading": "上传中", - "core.fileuploader.uploadingperc": "上传: {{$a}}%", - "core.fileuploader.video": "视频", - "core.filter": "筛选", - "core.folder": "文件夹", - "core.forcepasswordchangenotice": "您必须更改密码才能继续。", - "core.fulllistofcourses": "所有课程", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "平均值", - "core.grades.badgrade": "提供的成绩无效", - "core.grades.contributiontocoursetotal": "对课程的总贡献", - "core.grades.feedback": "反馈", - "core.grades.grade": "成绩", - "core.grades.gradeitem": "成绩项", - "core.grades.grades": "成绩", - "core.grades.lettergrade": "分数段", - "core.grades.nogradesreturned": "没有成绩返回", - "core.grades.nooutcome": "无成果", - "core.grades.percentage": "百分比", - "core.grades.range": "范围", - "core.grades.rank": "排名", - "core.grades.weight": "权重", - "core.group": "小组", - "core.groupsseparate": "分隔小组", - "core.groupsvisible": "可视小组", - "core.h5p.additionallicenseinfo": "关于许可证的任何附加信息", - "core.h5p.author": "作者", - "core.h5p.authorcomments": "作者注释", - "core.h5p.authorcommentsdescription": "内容编辑器的注释。(本文将不作为版权信息的一部分发布。)", - "core.h5p.authorname": "作者的名字", - "core.h5p.authorrole": "作者的角色", - "core.h5p.by": "由", - "core.h5p.cancellabel": "取消", - "core.h5p.ccattribution": "归属(CC BY-NC-ND)", - "core.h5p.ccattributionnc": "非商业归属(CC BY-NC)", - "core.h5p.ccattributionncnd": "归属-非商业性-无衍生(CC BY-NC-ND)", - "core.h5p.ccattributionncsa": "署名-非商业性共享(CC BY-NC-SA)", - "core.h5p.ccattributionnd": "归属-无衍生(CC BY-NC-ND)", - "core.h5p.ccattributionsa": "署名-共享(CC BY-NC-SA)", - "core.h5p.ccpdd": "公共领域贡献(CC0)", - "core.h5p.changedby": "修改人", - "core.h5p.changedescription": "修改描述", - "core.h5p.changelog": "修改日志", - "core.h5p.changeplaceholder": "照片裁剪,文字修改等。", - "core.h5p.close": "关闭", - "core.h5p.confirmdialogbody": "请确认您希望继续。此操作无法撤消。", - "core.h5p.confirmdialogheader": "确认操作", - "core.h5p.confirmlabel": "确认", - "core.h5p.connectionLost": "连接丢失。结果将存储并在连接重新建立时发送。", - "core.h5p.connectionReestablished": "连接已重建。", - "core.h5p.contentCopied": "内容被复制到剪贴板", - "core.h5p.contentchanged": "自您上次使用以来,此内容已发生了更改。", - "core.h5p.contenttype": "内容类型", - "core.h5p.copyright": "用户权利", - "core.h5p.copyrightinfo": "版权信息", - "core.h5p.copyrightstring": "版权", - "core.h5p.copyrighttitle": "查看此内容的版权信息。", - "core.h5p.creativecommons": "知识共享", - "core.h5p.date": "日期", - "core.h5p.disablefullscreen": "禁用全屏", - "core.h5p.download": "下载", - "core.h5p.downloadtitle": "以H5P文件下载此内容。", - "core.h5p.editor": "编辑", - "core.h5p.embed": "嵌入", - "core.h5p.embedtitle": "查看此内容的嵌入代码。", - "core.h5p.errorgetemail": "获取用户电子邮件时出错。请检查连接并重试。", - "core.h5p.fullscreen": "全屏", - "core.h5p.gpl": "通用公共许可证v3", - "core.h5p.h5ptitle": "访问h5p.org查看更多内容。", - "core.h5p.hideadvanced": "隐藏高级", - "core.h5p.license": "许可证", - "core.h5p.licenseCC010": "CC0 1.0通用(CC0 1.0)公共领域专用", - "core.h5p.licenseCC010U": "CC0 1.0通用", - "core.h5p.licenseCC10": "1.0 一般", - "core.h5p.licenseCC20": "2.0 一般", - "core.h5p.licenseCC25": "2.5 一般", - "core.h5p.licenseCC30": "3.0 未迁移", - "core.h5p.licenseCC40": "4.0 国际", - "core.h5p.licenseGPL": "通用公共许可证", - "core.h5p.licenseV1": "版本 1", - "core.h5p.licenseV2": "版本 2", - "core.h5p.licenseV3": "版本 3", - "core.h5p.licensee": "被许可方", - "core.h5p.licenseextras": "许可证附加条件", - "core.h5p.licenseversion": "许可证版本", - "core.h5p.nocopyright": "此内容没有版权信息。", - "core.h5p.offlineDialogBody": "我们无法发送您完成此任务的信息。请检查您的网络连接。", - "core.h5p.offlineDialogHeader": "您与服务器的连接丢失", - "core.h5p.offlineDialogRetryButtonLabel": "现在重试", - "core.h5p.offlineDialogRetryMessage": "重试中:num....", - "core.h5p.offlineSuccessfulSubmit": "成功提交结果。", - "core.h5p.offlinedisabled": "该网站不允许下载H5P包。", - "core.h5p.originator": "原创者", - "core.h5p.pd": "公共领域", - "core.h5p.pddl": "公共领域的贡献和许可", - "core.h5p.pdm": "公共领域商标 (PDM)", - "core.h5p.play": "播放H5P", - "core.h5p.resizescript": "如果您想要动态调整嵌入内容的大小,请在您的网站上包括这个脚本:", - "core.h5p.resubmitScores": "尝试提交存储的结果。", - "core.h5p.reuse": "重用", - "core.h5p.reuseContent": "重用内容", - "core.h5p.reuseDescription": "重用此内容。", - "core.h5p.showadvanced": "显示高级", - "core.h5p.showless": "显示更少", - "core.h5p.showmore": "显示更多", - "core.h5p.size": "大小", - "core.h5p.source": "源", - "core.h5p.startingover": "您将重新开始。", - "core.h5p.sublevel": "子级", - "core.h5p.thumbnail": "缩略图", - "core.h5p.title": "标题", - "core.h5p.undisclosed": "未关闭", - "core.h5p.year": "年", - "core.h5p.years": "年", - "core.h5p.yearsfrom": "年(从)", - "core.h5p.yearsto": "年(到)", - "core.hasdatatosync": "这个{{$a}}有要同步的离线数据。", - "core.help": "帮助", - "core.hide": "隐藏", - "core.hour": "小时", - "core.hours": "小时", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "图像", - "core.imageviewer": "图像查看器", - "core.info": "信息", - "core.invalidformdata": "错误的表单数据", - "core.labelsep": ":", - "core.lastaccess": "上次访问", - "core.lastdownloaded": "最后下载", - "core.lastmodified": "最后修改", - "core.lastsync": "最后一次同步", - "core.layoutgrid": "网格", - "core.list": "列表", - "core.listsep": ",", - "core.loading": "载入中", - "core.loadmore": "加载更多", - "core.location": "地点", - "core.login.auth_email": "基于email的自助注册", - "core.login.authenticating": "验证中", - "core.login.cancel": "取消", - "core.login.changepassword": "更改密码", - "core.login.changepasswordbutton": "打开更改密码页面", - "core.login.changepasswordhelp": "如果您在更改密码时遇到问题,请与您的网站管理员联系。“网站管理员”是在您的学校/大学/公司或学习机构管理Moodle的人。如果您不知道如何联系他们,请联系您的老师/培训师。", - "core.login.changepasswordinstructions": "您无法在应用程序中更改密码。请单击下面的按钮在web浏览器中打开站点来更改密码。请注意,您需要在更改密码后关闭浏览器,因为您不会被重定向到应用程序。", - "core.login.changepasswordlogoutinstructions": "切换或退出网站,请点击下面的按钮:", - "core.login.changepasswordreconnectinstructions": "点击下面的按钮重新连接到站点。(请注意,如果您没有成功更改密码,您将返回到上一个屏幕)。", - "core.login.confirmdeletesite": "您确定要删除网站{{sitename}}吗?", - "core.login.connect": "连接!", - "core.login.connecttomoodle": "连接到Moodle", - "core.login.connecttomoodleapp": "您正在尝试连接到一个常规Moodle站点。请下载官方Moodle应用程序访问本网站。", - "core.login.connecttoworkplaceapp": "您正在尝试连接到一个Moodle 工作场所站点。请下载Moodle Workplace应用程序访问本网站。", - "core.login.contactyouradministrator": "请联系您的网站管理员以获得进一步的帮助。", - "core.login.contactyouradministratorissue": "请让您的网站管理员检查以下问题:{{$a}}", - "core.login.createaccount": "创建我的新帐户", - "core.login.createuserandpass": "选择您的用户名和密码", - "core.login.credentialsdescription": "请提供您的用户名和密码,以便登录。", - "core.login.emailconfirmsent": "

                      一封邮件已经发送到您的地址 {{$a}}

                      \n

                      这封邮件简要说明了您如何完成注册。

                      \n

                      如果您还遇到什么困难,请和网站管理员联系。

                      ", - "core.login.emailconfirmsentnoemail": "

                      一封电子邮件应该已经发送到您的地址了。

                      它包含简单的说明,以完成您的注册。

                      如果您仍然有困难,请与网站管理员联系。

                      ", - "core.login.emailconfirmsentsuccess": "确认邮件发送成功", - "core.login.emailnotmatch": "电子邮件不匹配", - "core.login.erroraccesscontrolalloworigin": "您试图执行的Cross-Origin调用被拒绝了。\n请检查https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "删除此网站时发生错误。请再试一次。", - "core.login.errorexampleurl": "URL https://campus.example.edu只是一个示例URL,它不是一个真正的站点。请使用您的学校或组织的网站的URL 。", - "core.login.errorupdatesite": "更新站点的令牌时发生错误。", - "core.login.faqcannotconnectanswer": "请联系您的网站管理员。", - "core.login.faqcannotconnectquestion": "我正确地输入了我的网站地址,但我仍然无法连接。", - "core.login.faqcannotfindmysiteanswer": "您把名字打对了吗?也有可能,您的网站不包括在我们的公共网站目录。如果您仍然找不到它,请输入您的网站地址。", - "core.login.faqcannotfindmysitequestion": "我找不到我的网站。", - "core.login.faqsetupsiteanswer": "访问{{$link}}查看创建自己的Moodle网站的不同选项。", - "core.login.faqsetupsitelinktitle": "开始。", - "core.login.faqsetupsitequestion": "我想建立自己的Moodle网站。", - "core.login.faqtestappanswer": "要在Moodle演示站点中测试该应用程序,请在“您的网站”字段中输入“teacher”或“student”,然后点击“连接到您的网站”按钮。", - "core.login.faqtestappquestion": "我只是想测试这个应用程序,我该怎么做?", - "core.login.faqwhatisurlanswer": "

                      每个组织或学校都有他们自己的定制地址的Moodle网站。

                      要找到您想要连接的Moodle站点的地址,请执行以下操作:

                      1. 打开网络浏览器,进入你的学校或组织的Moodle网站登录页面
                      2. 在页面顶部的地址栏,你可以看到你的Moodle站点的URL。如。“campus.example.edu”。{{$image}}
                      3. 复制地址(不要复制/login和后面的内容),将其粘贴到Moodle应用程序中,然后点击“连接到您的网站”
                      4. 现在您可以使用您的用户名和密码登录到您的站点了
                      5. ", - "core.login.faqwhatisurlquestion": "我的网站地址是什么?我如何找到我的网站URL?", - "core.login.faqwhereisqrcode": "二维码在哪里?", - "core.login.faqwhereisqrcodeanswer": "

                        如果您的组织启用了该功能,您将在您的用户资料页面底部发现一个二维码。

                        {{$image}}", - "core.login.findyoursite": "找到您的网站", - "core.login.firsttime": "这是您第一次来这里吗?", - "core.login.forcepasswordchangenotice": "您必须更改密码才能继续。", - "core.login.forgotten": "忘记用户名或密码了?", - "core.login.help": "帮助", - "core.login.helpmelogin": "

                        世界上有成千上万的Moodle网站。该应用程序只能连接到专门启用了移动应用程序访问的Moodle站点。

                        如果您不能连接到您的Moodle网站,那么您需要联系您的网站管理员,让他们阅读 http://docs.moodle.org/en/Mobile_app

                        在Moodle演示站点上测试该应用程序,在 站点地址 字段输入teacherstudent 然后点击\"连接\" 按钮

                        ", - "core.login.instructions": "使用说明", - "core.login.invalidaccount": "请检查您的登录详细信息或请您的网站管理员检查网站配置。", - "core.login.invaliddate": "无效日期", - "core.login.invalidemail": "无效电邮地址", - "core.login.invalidmoodleversion": "

                        无效的Moodle网站版本。Moodle应用只支持 {{$a}}以上的Moodle系统。

                        \n

                        您可以联系您的网站管理员,让他们更新他们的Moodle系统。

                        \n

                        “网站管理员”是在您的学校/大学/公司或学习组织管理Moodle的人。如果您不知道如何联系他们,请联系您的老师/培训师。

                        ", - "core.login.invalidsite": "网站URL无效。", - "core.login.invalidtime": "无效的时间", - "core.login.invalidurl": "无效的URL指定", - "core.login.invalidvaluemax": "最大值为{{$a}}", - "core.login.invalidvaluemin": "最小值为{{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle移动附加功能检查返回了一个意外的响应,您将使用标准的移动服务进行身份验证。", - "core.login.loggedoutssodescription": "您必须重新验证。您需要在浏览器窗口中登录到该站点。", - "core.login.login": "登录", - "core.login.loginbutton": "登录", - "core.login.logininsiterequired": "您需要在浏览器窗口中登录到该站点。", - "core.login.loginsteps": "要完全访问此站点,您首先需要创建一个帐户。", - "core.login.missingemail": "Email地址没填", - "core.login.missingfirstname": "名没填", - "core.login.missinglastname": "姓没填", - "core.login.mobileservicesnotenabled": "您的站点上没有启用移动访问。如果您认为应该启用它,请联系您的网站管理员。", - "core.login.mustconfirm": "您需要确认您的账户", - "core.login.newaccount": "新帐号", - "core.login.notloggedin": "您需要登录。", - "core.login.onboardingcreatemanagecourses": "创建和管理您的课程", - "core.login.onboardingenrolmanagestudents": "注册和管理您的学生", - "core.login.onboardinggetstarted": "开始使用Moodle", - "core.login.onboardingialreadyhaveasite": "我已经有了一个Moodle网站", - "core.login.onboardingimalearner": "我是一个学习者", - "core.login.onboardingimaneducator": "我是一个教育工作者", - "core.login.onboardingineedasite": "我需要一个Moodle网站", - "core.login.onboardingprovidefeedback": "提供及时反馈", - "core.login.onboardingtoconnect": "要连接到Moodle应用程序,您需要一个Moodle网站", - "core.login.onboardingwelcome": "欢迎来到Moodle应用!", - "core.login.or": "或者", - "core.login.password": "密码", - "core.login.passwordforgotten": "忘记了密码", - "core.login.passwordforgotteninstructions2": "要重设密码,请在下方提交您的用户名或电子邮件地址。如果我们能在数据库中找到您,我们将发送电子邮件到您的电子邮件地址,并说明如何再次访问。", - "core.login.passwordrequired": "需要密码", - "core.login.policyaccept": "我理解并同意", - "core.login.policyagree": "如要继续使用此站,您必须同意此协议。您同意么?", - "core.login.policyagreement": "网站政策协议", - "core.login.policyagreementclick": "链接到网站政策协议", - "core.login.potentialidps": "使用您的帐户登录:", - "core.login.profileinvaliddata": "无效值", - "core.login.recaptchachallengeimage": "reCAPTCHA挑战图像", - "core.login.recaptchaexpired": "验证过期。请再次回答安全问题。", - "core.login.recaptchaincorrect": "安全问题回答错误。", - "core.login.reconnect": "重新连接", - "core.login.reconnectdescription": "您的身份验证令牌无效或过期,您必须重新连接到站点。", - "core.login.reconnectssodescription": "您的身份验证令牌无效或过期,您必须重新连接到站点。您需要在浏览器窗口中登录到站点。", - "core.login.resendemail": "重新发送电子邮件", - "core.login.searchby": "按以下搜索:", - "core.login.security_question": "安全问题", - "core.login.selectacountry": "选择一个国家或地区", - "core.login.selectsite": "请选择您的站点:", - "core.login.signupplugindisabled": "{{$a}}未启用。", - "core.login.siteaddress": "您的网站", - "core.login.sitehasredirect": "您的站点至少包含一个HTTP重定向。应用程序无法跟随重定向,这可能是阻止应用程序连接到您的网站的问题。", - "core.login.siteinmaintenance": "您的站点处于维护模式", - "core.login.sitepolicynotagreederror": "未同意网站政策。", - "core.login.siteurl": "站点 URL", - "core.login.siteurlrequired": "需要填写站点URL,比如http://www.yourmoodlesite.abc 或https://www.yourmoodlesite.efg", - "core.login.startsignup": "注册新帐号", - "core.login.stillcantconnect": "仍然不能连接吗?", - "core.login.supplyinfo": "更多细节", - "core.login.username": "用户名", - "core.login.usernameoremail": "输入用户名或电子邮件地址", - "core.login.usernamerequired": "需要用户名", - "core.login.usernotaddederror": "用户未添加-错误", - "core.login.visitchangepassword": "您想访问该网站更改密码吗?", - "core.login.webservicesnotenabled": "您的主机站点可能没有启用Web服务。请联系您的管理员寻求帮助。", - "core.login.youcanstillconnectwithcredentials": "您仍然可以通过输入您的用户名和密码连接到该网站。", - "core.login.yourenteredsite": "连接到您的网站", - "core.lostconnection": "您的身份验证令牌无效或已过期。 您必须重新连接到该站点。", - "core.mainmenu.changesite": "切换站点", - "core.mainmenu.help": "帮助", - "core.mainmenu.logout": "退出登录", - "core.mainmenu.website": "网站", - "core.maxsizeandattachments": "最大文件大小:{{$a.size}},最大文件数量:{{$a.attachments}}", - "core.min": "分钟", - "core.mins": "分钟", - "core.misc": "杂项", - "core.mod_assign": "作业", - "core.mod_assignment": "作业2.2(已禁用)", - "core.mod_book": "图书", - "core.mod_chat": "聊天", - "core.mod_choice": "投票", - "core.mod_data": "数据库", - "core.mod_database": "数据库", - "core.mod_external-tool": "外部工具", - "core.mod_feedback": "问卷调查", - "core.mod_file": "文件", - "core.mod_folder": "文件夹", - "core.mod_forum": "讨论区", - "core.mod_glossary": "词汇表", - "core.mod_h5pactivity": "H5P", - "core.mod_ims": "IMS 内容包", - "core.mod_imscp": "IMS 内容包", - "core.mod_label": "标签", - "core.mod_lesson": "程序教学", - "core.mod_lti": "外部工具", - "core.mod_page": "页面", - "core.mod_quiz": "测验", - "core.mod_resource": "文件", - "core.mod_scorm": "SCORM 包", - "core.mod_survey": "问卷调查", - "core.mod_url": "网页地址", - "core.mod_wiki": "Wiki协作", - "core.mod_workshop": "互动评价", - "core.moduleintro": "描述", - "core.more": "更多", - "core.mygroups": "我的小组", - "core.name": "名称", - "core.needhelp": "您需要帮助吗?", - "core.networkerroriframemsg": "此内容无法离线使用。请连接互联网再试一次。", - "core.networkerrormsg": "连接到站点时出现了问题。请检查连接并重试。", - "core.never": "从未", - "core.next": "下一个", - "core.no": "否", - "core.nocomments": "无评论", - "core.nograde": "没有成绩", - "core.none": "无", - "core.nooptionavailable": "没有可用的选项", - "core.nopasswordchangeforced": "如果不更改密码,您无法继续操作。", - "core.nopermissionerror": "对不起,您目前没有权限这样做", - "core.nopermissions": "很抱歉,您没有相应权限({{$a}})", - "core.noresults": "没有结果", - "core.noselection": "无选择", - "core.notapplicable": "不适用", - "core.notenrolledprofile": "此个人档案无效,因为该用户没有加入此课程。", - "core.notice": "注意", - "core.notingroup": "抱歉,您需要加入小组才可浏览此页。", - "core.notsent": "未发送", - "core.now": "现在", - "core.nummore": "{{$a}} 更多", - "core.numwords": "{{$a}}字", - "core.offline": "离线", - "core.ok": "同意", - "core.online": "在线", - "core.openfullimage": "点击这里显示完整大小的图像", - "core.openinbrowser": "在浏览器中打开", - "core.openmodinbrowser": "在浏览器中打开{{$a}}", - "core.othergroups": "其他小组", - "core.pagea": "页 {{$a}}", - "core.paymentinstant": "点击下面的按钮便可以快速付费并加入课程!", - "core.percentagenumber": "{{$a}}%", - "core.phone": "电话", - "core.pictureof": "{{$a}}的头像", - "core.previous": "上一个", - "core.proceed": "开始", - "core.pulltorefresh": "下拉刷新", - "core.qrscanner": "二维码扫描器", - "core.question.answer": "答案", - "core.question.answersaved": "保存答案", - "core.question.cannotdeterminestatus": "不能确定状态", - "core.question.certainty": "确定", - "core.question.complete": "完成", - "core.question.correct": "正确", - "core.question.errorattachmentsnotsupported": "应用程序还不支持附加文件到答案。", - "core.question.errorinlinefilesnotsupported": "应用程序还不支持编辑内联文件。", - "core.question.errorquestionnotsupported": "应用程序不支持此题型:{{$a}}。", - "core.question.feedback": "反馈", - "core.question.howtodraganddrop": "点击选择,然后点击放下。", - "core.question.incorrect": "错误", - "core.question.information": "说明", - "core.question.invalidanswer": "不完整的答案", - "core.question.notanswered": "未回答", - "core.question.notyetanswered": "还未回答", - "core.question.partiallycorrect": "部分正确", - "core.question.questionmessage": "试题{{$a}}: {{$b}}", - "core.question.questionno": "题目{{$a}}", - "core.question.requiresgrading": "需要评分", - "core.quotausage": "您已经使用了{{$a.total}}的{{$a. using}}。", - "core.rating.aggregateavg": "平均分", - "core.rating.aggregatecount": "评分数", - "core.rating.aggregatemax": "最高分", - "core.rating.aggregatemin": "最低分", - "core.rating.aggregatesum": "评分总和", - "core.rating.noratings": "没有提交的评分", - "core.rating.rating": "评分", - "core.rating.ratings": "评分", - "core.redirectingtosite": "您将被重定向到该站点。", - "core.refresh": "刷新", - "core.remove": "移除", - "core.removefiles": "删除文件 {{$a}}", - "core.required": "必需的", - "core.requireduserdatamissing": "该用户缺少一些必需的个人资料数据。请在您的站点中输入数据,然后重试。
                        {{$a}}", - "core.resourcedisplayopen": "打开", - "core.resources": "资源", - "core.restore": "恢复", - "core.restricted": "受限的", - "core.retry": "重试", - "core.save": "保存", - "core.savechanges": "保存更改", - "core.scanqr": "扫描二维码", - "core.search": "搜索", - "core.searching": "搜索中", - "core.searchresults": "搜索结果", - "core.sec": "秒", - "core.secs": "秒", - "core.seemoredetail": "点击这里看更多细节", - "core.selectacategory": "请选择一个类别", - "core.selectacourse": "选择一门课程", - "core.selectagroup": "选择一个小组", - "core.send": "发送", - "core.sending": "正在发送", - "core.serverconnection": "连接到服务器时出错", - "core.settings.about": "关于", - "core.settings.appsettings": "应用程序设置", - "core.settings.appversion": "应用程序版本", - "core.settings.cannotsyncoffline": "不能同步离线。", - "core.settings.cannotsyncwithoutwifi": "无法同步,因为目前的设置只允许在连接到Wi-Fi时进行同步。请连接到一个Wi-Fi无线网络。", - "core.settings.colorscheme": "配色方案", - "core.settings.colorscheme-auto": "自动(基于系统设置)", - "core.settings.colorscheme-dark": "深色模式", - "core.settings.colorscheme-light": "亮色模式", - "core.settings.compilationinfo": "编译信息", - "core.settings.copyinfo": "将设备信息复制到剪贴板上", - "core.settings.cordovadevicemodel": "Cordova设备型号", - "core.settings.cordovadeviceosversion": "Cordova设备操作系统版本", - "core.settings.cordovadeviceplatform": "Cordova设备平台", - "core.settings.cordovadeviceuuid": "Cordova设备UUID", - "core.settings.cordovaversion": "Cordova版本", - "core.settings.currentlanguage": "当前语言", - "core.settings.debugdisplay": "显示调试信息", - "core.settings.debugdisplaydescription": "如果启用,错误模式将显示有关错误的更多数据。", - "core.settings.deletesitefiles": "您确定要从站点“{{sitename}}”删除下载的文件和缓存的数据吗?您将不能在离线模式下使用该应用程序。", - "core.settings.deletesitefilestitle": "删除站点文件", - "core.settings.deviceinfo": "设备信息", - "core.settings.deviceos": "设备操作系统", - "core.settings.disableall": "禁用通知", - "core.settings.disabled": "禁用", - "core.settings.displayformat": "显示格式", - "core.settings.enabledownloadsection": "启用下载小节", - "core.settings.enablefirebaseanalytics": "启用重火力点(Firebase)分析", - "core.settings.enablefirebaseanalyticsdescription": "如果启用,该应用程序将收集匿名数据的使用情况。", - "core.settings.enablerichtexteditor": "启用富文本编辑器", - "core.settings.enablerichtexteditordescription": "如启用,则在输入内容时将显示富文本编辑器。如禁用,将显示纯文本编辑器。您可以在编辑时在两者之间切换。", - "core.settings.enablesyncwifi": "仅允许在Wi-Fi上进行同步", - "core.settings.entriesincache": "{{$a}} 条缓存", - "core.settings.errordeletesitefiles": "删除网站文件时出错。", - "core.settings.errorsyncsite": "同步站点数据时出错。请检查你的网络连接,然后再试一次。", - "core.settings.estimatedfreespace": "估计可用空间", - "core.settings.filesystemroot": "文件系统根目录", - "core.settings.fontsize": "字体大小", - "core.settings.fontsizecharacter": "A", - "core.settings.forcedsetting": "此设置是由您的站点配置强制设置的。", - "core.settings.general": "常规项", - "core.settings.language": "语言", - "core.settings.license": "许可证", - "core.settings.localnotifavailable": "本地通知可用", - "core.settings.locationhref": "Web视图URL", - "core.settings.locked": "已锁定", - "core.settings.loggedin": "在线", - "core.settings.loggedoff": "离线", - "core.settings.navigatorlanguage": "浏览器语言", - "core.settings.navigatoruseragent": "浏览器用户代理", - "core.settings.networkstatus": "网络连接状态", - "core.settings.opensourcelicenses": "开源许可证", - "core.settings.preferences": "偏好", - "core.settings.privacypolicy": "隐私策略", - "core.settings.publisher": "发行人", - "core.settings.pushid": "推送通知ID", - "core.settings.reportinbackground": "自动报告错误", - "core.settings.screen": "屏幕信息", - "core.settings.settings": "设置", - "core.settings.showdownloadoptions": "显示下载选项", - "core.settings.siteinfo": "网站信息", - "core.settings.sites": "网站", - "core.settings.spaceusage": "空间使用", - "core.settings.spaceusagehelp": "删除存储的站点信息将删除站点所有离线数据。这些信息允许您离线使用该应用程序。", - "core.settings.synchronization": "同步", - "core.settings.synchronizenow": "现在同步", - "core.settings.synchronizenowhelp": "同步一个网站将发送待定的更改和所有存储在设备中的离线活动,并将同步一些数据,如消息和通知。", - "core.settings.syncsettings": "同步设置", - "core.settings.total": "总计", - "core.settings.wificonnection": "WiFi连接", - "core.sharedfiles.chooseaccountstorefile": "选择存储文件的帐户。", - "core.sharedfiles.chooseactionrepeatedfile": "此名称的文件已经存在。要替换现有文件还是将其重命名为“{{$a}}”?", - "core.sharedfiles.errorreceivefilenosites": "没有存储的站点。 请在与应用共享文件之前添加一个站点。", - "core.sharedfiles.nosharedfiles": "此站点上没有共享的文件。", - "core.sharedfiles.nosharedfilestoupload": "您没有文件可以上传。如果您想从另一个应用程序上传一个文件,请找到这个文件并点击“打开”按钮。", - "core.sharedfiles.rename": "重命名", - "core.sharedfiles.replace": "替换", - "core.sharedfiles.sharedfiles": "共享文件", - "core.sharedfiles.successstorefile": "成功存储文件。选择要上载到您的私人文件或在活动中使用文件。", - "core.show": "显示", - "core.showless": "显示较少...", - "core.showmore": "显示更多...", - "core.site": "网站", - "core.sitehome.sitehome": "网站首页", - "core.sitehome.sitenews": "网站新闻通告", - "core.sitemaintenance": "本站正在维护中,目前不能访问。", - "core.sizeb": "字节", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "跳过", - "core.sorry": "对不起…", - "core.sort": "排序", - "core.sortby": "排序方式", - "core.start": "开始", - "core.storingfiles": "存储文件", - "core.strftimedate": "%Y年%m月%d日", - "core.strftimedatefullshort": "%y/%m/%d", - "core.strftimedateshort": "%m月%d日", - "core.strftimedatetime": "%Y年%m月%d日 %H:%M", - "core.strftimedatetimeshort": "%Y年%m月%d日 %H:%M", - "core.strftimedaydate": "%Y年%m月%d日 %A", - "core.strftimedaydatetime": "%Y年%m月%d日 %A %H:%M", - "core.strftimedayshort": "%m月%d日 %A", - "core.strftimedaytime": "%A %H:%M", - "core.strftimemonthyear": "%Y年%m月", - "core.strftimerecent": "%m月%d日 %H:%M", - "core.strftimerecentfull": "%Y年%m月%d日 %A %H:%M", - "core.strftimetime": "%H:%M", - "core.submit": "提交", - "core.success": "成功", - "core.tablet": "平板电脑", - "core.tag.defautltagcoll": "默认收藏", - "core.tag.errorareanotsupported": "应用程序不支持此标记区域。", - "core.tag.inalltagcoll": "到处", - "core.tag.itemstaggedwith": "{{$a.tagarea}}标记为\"{{$a.tag}}\"", - "core.tag.noresultsfor": "未找到与“{{$a}}”相关的结果", - "core.tag.notagsfound": "没有找到与“{{$a}}”匹配的标签", - "core.tag.searchtags": "搜索标签", - "core.tag.showingfirsttags": "显示最受欢迎标签{{$a}}", - "core.tag.tag": "标签", - "core.tag.tagarea_course": "课程", - "core.tag.tagarea_course_modules": "活动和资源", - "core.tag.tagarea_post": "博客帖子", - "core.tag.tagarea_user": "用户兴趣", - "core.tag.tags": "标签", - "core.tag.warningareasnotsupported": "有些标签区域没有显示,因为应用程序不支持它们。", - "core.teachers": "教师", - "core.thereisdatatosync": "有离线的{{$a}}要同步。", - "core.thisdirection": "ltr", - "core.time": "时间", - "core.timesup": "时间到!", - "core.today": "今天", - "core.tryagain": "再试一次", - "core.twoparagraphs": "{{p1}}

                        {{p2}}", - "core.uhoh": "呃 哦!", - "core.unexpectederror": "意外出错。请关闭并重新打开应用程序,然后重试。", - "core.unicodenotsupported": "本网站不支持某些表情符号。当消息发送时,这些字符将被删除。", - "core.unicodenotsupportedcleanerror": "清除Unicode字符时发现空文本。", - "core.unknown": "未知的", - "core.unlimited": "无限制", - "core.unzipping": "解压中", - "core.updaterequired": "应用程序需要更新", - "core.updaterequireddesc": "请将您的应用程序升级到版本{{$a}}", - "core.upgraderunning": "网站正在升级,请稍后再试。", - "core.user": "用户", - "core.user.address": "地址", - "core.user.city": "市/县", - "core.user.contact": "联系方式", - "core.user.country": "国家和地区", - "core.user.description": "描述", - "core.user.details": "详情", - "core.user.detailsnotavailable": "您无法获得该用户的详细信息。", - "core.user.editingteacher": "教师", - "core.user.email": "Email地址", - "core.user.emailagain": "Email (重复)", - "core.user.errorloaduser": "加载用户错误。", - "core.user.firstname": "名", - "core.user.interests": "兴趣", - "core.user.lastname": "姓", - "core.user.manager": "管理员", - "core.user.newpicture": "新照片", - "core.user.noparticipants": "本课程没有参与者", - "core.user.participants": "参与者", - "core.user.phone1": "电话", - "core.user.phone2": "手机", - "core.user.roles": "角色", - "core.user.sendemail": "电子邮件", - "core.user.student": "学生", - "core.user.teacher": "无编辑权教师", - "core.user.webpage": "网页", - "core.userdeleted": "该用户帐号已被删除", - "core.userdetails": "用户细节", - "core.usernotfullysetup": "用户没有完全设置好", - "core.users": "用户", - "core.view": "查看", - "core.viewcode": "查看代码", - "core.vieweditor": "查看编辑器", - "core.viewembeddedcontent": "查看嵌入的内容", - "core.viewprofile": "查看个人资料", - "core.warningofflinedatadeleted": "来自{{component}} '{{name}}' 的离线数据已经被删除。 {{error}}", - "core.whatisyourage": "您的年龄?", - "core.wheredoyoulive": "您的国籍?", - "core.whoissiteadmin": "“网站管理员”是在您的学校/大学/公司或学习机构管理Moodle的人。如果您不知道如何联系他们,请联系您的老师/培训师。", - "core.whoops": "哎呀!", - "core.whyisthishappening": "为什么会这样?", - "core.whyisthisrequired": "为什么需要这样做?", - "core.wsfunctionnotavailable": "web服务功能不可用。", - "core.year": "年", - "core.years": "年", - "core.yes": "是", - "core.youreoffline": "您离线了", - "core.youreonline": "您又上线了" -} \ No newline at end of file diff --git a/src/assets/lang/zh-tw.json b/src/assets/lang/zh-tw.json deleted file mode 100644 index ff3a49f89..000000000 --- a/src/assets/lang/zh-tw.json +++ /dev/null @@ -1,1857 +0,0 @@ -{ - "addon.badges.badgedetails": "獎章細節", - "addon.badges.badges": "獎章", - "addon.badges.contact": "聯絡", - "addon.badges.dateawarded": "頒發的日期", - "addon.badges.expired": "已經失效", - "addon.badges.expirydate": "失效日期", - "addon.badges.issuancedetails": "獎章到期", - "addon.badges.issuerdetails": "頒授者細節", - "addon.badges.issuername": "頒授者的姓名", - "addon.badges.issuerurl": "頒授者的網址", - "addon.badges.nobadges": "這裡沒有可用的獎章", - "addon.badges.recipientdetails": "收件者細節", - "addon.badges.warnexpired": "(這一獎章已經過期失效!)", - "addon.block_activitymodules.pluginname": "活動", - "addon.block_activityresults.pluginname": "活動結果", - "addon.block_badges.pluginname": "我的最新獎章", - "addon.block_blogmenu.pluginname": "部落格選單", - "addon.block_blogrecent.pluginname": "新近部落格文章", - "addon.block_blogtags.pluginname": "部落格標籤", - "addon.block_calendarmonth.pluginname": "行事曆", - "addon.block_calendarupcoming.pluginname": "未來事件", - "addon.block_comments.pluginname": "評論", - "addon.block_completionstatus.pluginname": "課程完成狀態", - "addon.block_glossaryrandom.pluginname": "隨機詞彙條目", - "addon.block_learningplans.pluginname": "學習計畫", - "addon.block_myoverview.all": "全部(不含隱藏的)", - "addon.block_myoverview.allincludinghidden": "全部", - "addon.block_myoverview.favourites": "星號標記", - "addon.block_myoverview.future": "未來", - "addon.block_myoverview.hiddencourses": "隱藏", - "addon.block_myoverview.inprogress": "進行中", - "addon.block_myoverview.lastaccessed": "最後訪問的", - "addon.block_myoverview.morecourses": "更多課程", - "addon.block_myoverview.nocourses": "沒有課程", - "addon.block_myoverview.past": "過去", - "addon.block_myoverview.pluginname": "課程概觀", - "addon.block_myoverview.title": "課程名稱", - "addon.block_newsitems.pluginname": "最新消息", - "addon.block_onlineusers.pluginname": "線上用戶", - "addon.block_privatefiles.pluginname": "私人檔案", - "addon.block_recentactivity.pluginname": "最近活動紀錄", - "addon.block_recentlyaccessedcourses.nocourses": "沒有最近的課程", - "addon.block_recentlyaccessedcourses.pluginname": "最近訪問的課程", - "addon.block_rssclient.pluginname": "遠端RSS彙集", - "addon.block_selfcompletion.pluginname": "自我完成", - "addon.block_sitemainmenu.pluginname": "主選單", - "addon.block_starredcourses.pluginname": "星號標記的課程", - "addon.block_tags.pluginname": "標籤", - "addon.block_timeline.duedate": "到期日", - "addon.block_timeline.next30days": "往後30天", - "addon.block_timeline.next3months": "往後三個月", - "addon.block_timeline.next6months": "往後六個月", - "addon.block_timeline.next7days": "往後7天", - "addon.block_timeline.nocoursesinprogress": "沒有進行中的課程", - "addon.block_timeline.noevents": "近期沒有活動", - "addon.block_timeline.overdue": "過期的", - "addon.block_timeline.pluginname": "時間軸", - "addon.block_timeline.sortbycourses": "按課程排序", - "addon.block_timeline.sortbydates": "按日期排序", - "addon.blog.blog": "部落格", - "addon.blog.blogentries": "部落格文章", - "addon.blog.linktooriginalentry": "鏈結到原初的部落格文章", - "addon.blog.noentriesyet": "目前沒有內容", - "addon.blog.publishtonoone": "您自己(草稿)", - "addon.blog.publishtosite": "網站上任何人", - "addon.blog.publishtoworld": "世界任何人", - "addon.blog.showonlyyourentries": "只顯示您的文章", - "addon.blog.siteblogheading": "網站部落格", - "addon.calendar.allday": "全部", - "addon.calendar.calendar": "行事曆", - "addon.calendar.calendarevent": "行事曆事件", - "addon.calendar.calendarevents": "行事曆事件", - "addon.calendar.calendarreminders": "行事曆提醒", - "addon.calendar.categoryevents": "類目事件", - "addon.calendar.confirmeventdelete": "您確定要刪除這個\"{{$a}}\" 事件嗎?", - "addon.calendar.confirmeventseriesdelete": "這 \"{{$a.name}}\"事件是一系列事件中的一部分。你只要刪除這一事件,或是在這系列中的全部{{$a.count}} 個事件?", - "addon.calendar.courseevents": "課程事件", - "addon.calendar.currentmonth": "現在月份", - "addon.calendar.daynext": "下一天", - "addon.calendar.dayprev": "前一天", - "addon.calendar.defaultnotificationtime": "預設提醒的時間", - "addon.calendar.deleteallevents": "刪除所有事件", - "addon.calendar.deleteevent": "刪除事件", - "addon.calendar.deleteoneevent": "刪除這一事件", - "addon.calendar.durationminutes": "持續多少分鐘", - "addon.calendar.durationnone": "不持續", - "addon.calendar.durationuntil": "持續直到", - "addon.calendar.editevent": "修改事件", - "addon.calendar.errorloadevent": "載入事件時出現錯誤", - "addon.calendar.errorloadevents": "載入事件時出現錯誤", - "addon.calendar.eventcalendareventdeleted": "刪除行事曆事件。", - "addon.calendar.eventduration": "持續時間", - "addon.calendar.eventendtime": "結束時間", - "addon.calendar.eventkind": "事件類型", - "addon.calendar.eventname": "事件標題", - "addon.calendar.eventstarttime": "開始時間", - "addon.calendar.eventtype": "事件類型", - "addon.calendar.fri": "五", - "addon.calendar.friday": "星期五", - "addon.calendar.gotoactivity": "到活動", - "addon.calendar.groupevents": "群組事件", - "addon.calendar.invalidtimedurationminutes": "您輸入的持續時間分鐘數是無效的。請輸入大於0的數字,或者選擇不持續。", - "addon.calendar.invalidtimedurationuntil": "您為\"持續直到\"選擇的時間和日期早於事件的開始時間。請在繼續處理前,修正這個問題。", - "addon.calendar.mon": "一", - "addon.calendar.monday": "星期一", - "addon.calendar.monthlyview": "逐月檢視", - "addon.calendar.newevent": "新事件", - "addon.calendar.noevents": "沒有事件", - "addon.calendar.nopermissiontoupdatecalendar": "你沒有權限去更新行事曆事件", - "addon.calendar.repeatedevents": "重複事件", - "addon.calendar.repeateditall": "也套用到在此系列事件中的其他{{$a}}個事件", - "addon.calendar.repeateditthis": "只變更這個事件", - "addon.calendar.repeatevent": "重複此事件", - "addon.calendar.repeatweeksl": "每週重複,總共幾次?", - "addon.calendar.sat": "六", - "addon.calendar.saturday": "星期六", - "addon.calendar.siteevents": "網站事件", - "addon.calendar.sun": "日", - "addon.calendar.sunday": "星期日", - "addon.calendar.thu": "四", - "addon.calendar.thursday": "星期四", - "addon.calendar.today": "今天", - "addon.calendar.tomorrow": "明天", - "addon.calendar.tue": "二", - "addon.calendar.tuesday": "星期二", - "addon.calendar.typecategory": "類別事件", - "addon.calendar.typeclose": "已關閉事件", - "addon.calendar.typecourse": "課程事件", - "addon.calendar.typedue": "到期事件", - "addon.calendar.typegradingdue": "評分到期事件", - "addon.calendar.typegroup": "群組事件", - "addon.calendar.typeopen": "開放中事件", - "addon.calendar.typesite": "網站全體事件", - "addon.calendar.typeuser": "用戶事件", - "addon.calendar.upcomingevents": "未來事件", - "addon.calendar.userevents": "用戶事件", - "addon.calendar.wed": "三", - "addon.calendar.wednesday": "星期三", - "addon.calendar.when": "何時", - "addon.calendar.yesterday": "昨天", - "addon.competency.activities": "活動", - "addon.competency.competencies": "能力", - "addon.competency.competenciesmostoftennotproficientincourse": "大多數人在這課程無法達到精熟的核心能力", - "addon.competency.coursecompetencies": "課程核心能力", - "addon.competency.coursecompetencyratingsarenotpushedtouserplans": "在這一課程的核心能力評等不會影響學習計畫", - "addon.competency.coursecompetencyratingsarepushedtouserplans": "在這一課程的核心能力評等在學習計畫上會立即更新", - "addon.competency.crossreferencedcompetencies": "交互參照的核心能力", - "addon.competency.duedate": "截止日期", - "addon.competency.errornocompetenciesfound": "找不到能使用的功能", - "addon.competency.evidence": "證據", - "addon.competency.evidence_competencyrule": "已經符合這核心能力的規則", - "addon.competency.evidence_coursecompleted": "已經完成課程 '{{$a}}'", - "addon.competency.evidence_coursemodulecompleted": "已經完成活動 '{{$a}}'", - "addon.competency.evidence_courserestored": "這一評等已經伴隨著課程'{{$a}}'被回存", - "addon.competency.evidence_evidenceofpriorlearninglinked": "先備學習的證據 '{{$a}}' 已經建立連結", - "addon.competency.evidence_evidenceofpriorlearningunlinked": "先備學習的證據 '{{$a}}' 已經取消連結", - "addon.competency.evidence_manualoverride": "已經手動設定核心能力的評等", - "addon.competency.evidence_manualoverrideincourse": "這核心能力的評等已經在課程 '{{$a}}'上以手動方式設定", - "addon.competency.evidence_manualoverrideinplan": "這核心能力的評等已經在學習計畫 '{{$a}}'上以手動方式設定", - "addon.competency.learningplancompetencies": "學習計畫核心能力", - "addon.competency.learningplans": "學習計畫", - "addon.competency.myplans": "我的學習計畫", - "addon.competency.noactivities": "沒有活動", - "addon.competency.nocompetencies": "沒有功能", - "addon.competency.nocompetenciesincourse": "沒有核心能力被連結到這一課程", - "addon.competency.nocrossreferencedcompetencies": "沒有其他核心能力被交互參照這一核心能力", - "addon.competency.noevidence": "沒有證據", - "addon.competency.noplanswerecreated": "沒有學習計畫被建立", - "addon.competency.path": "路徑:", - "addon.competency.planstatusactive": "啟用中", - "addon.competency.planstatuscomplete": "完成", - "addon.competency.planstatusdraft": "草稿", - "addon.competency.planstatusinreview": "審查中", - "addon.competency.planstatuswaitingforreview": "等待審查", - "addon.competency.proficient": "精熟", - "addon.competency.progress": "進度", - "addon.competency.rating": "評等", - "addon.competency.reviewstatus": "審查狀況", - "addon.competency.status": "狀態", - "addon.competency.template": "學習計畫樣版", - "addon.competency.uponcoursecompletion": "一旦課程完成:", - "addon.competency.usercompetencystatus_idle": "閒置", - "addon.competency.usercompetencystatus_inreview": "正在審查", - "addon.competency.usercompetencystatus_waitingforreview": "等待審查中", - "addon.competency.userplans": "學習計畫", - "addon.competency.xcompetenciesproficientoutofy": "在{{$a.y}}個核心能力中有{{$a.x}}個已經精熟", - "addon.competency.xcompetenciesproficientoutofyincourse": "在此課程中有{{$a.y}}個核心能力,你已經精熟{{$a.x}}個。", - "addon.coursecompletion.complete": "完成", - "addon.coursecompletion.completecourse": "完成的課程", - "addon.coursecompletion.completed": "已完成", - "addon.coursecompletion.completiondate": "完成日期", - "addon.coursecompletion.completionmenuitem": "完成", - "addon.coursecompletion.couldnotloadreport": "無法載入課程完成報表,請稍後再試.", - "addon.coursecompletion.coursecompletion": "課程完成進度", - "addon.coursecompletion.criteria": "規準", - "addon.coursecompletion.criteriagroup": "各種判斷條件", - "addon.coursecompletion.criteriarequiredall": "必須滿足以下所有條件", - "addon.coursecompletion.criteriarequiredany": "必須滿足以下任一條件", - "addon.coursecompletion.inprogress": "處理中", - "addon.coursecompletion.manualselfcompletion": "手動自我完成", - "addon.coursecompletion.nottracked": "你在這一課程上,現在沒被追蹤完成進度", - "addon.coursecompletion.notyetstarted": "尚未開始", - "addon.coursecompletion.pending": "等待中", - "addon.coursecompletion.required": "必須的", - "addon.coursecompletion.requiredcriteria": "必要條件", - "addon.coursecompletion.requirement": "要求", - "addon.coursecompletion.status": "狀態", - "addon.coursecompletion.viewcoursereport": "查看課程報告", - "addon.files.couldnotloadfiles": "這些檔案是不能被載入的", - "addon.files.emptyfilelist": "沒有檔案可以顯示", - "addon.files.erroruploadnotworking": "很抱歉, 目前無法將檔案上傳到您的網站.", - "addon.files.files": "檔案", - "addon.files.privatefiles": "私人檔案", - "addon.files.sitefiles": "網站檔案", - "addon.messageoutput_airnotifier.processorsettingsdesc": "設定裝置", - "addon.messages.acceptandaddcontact": "接受並加入通訊錄", - "addon.messages.addcontact": "新增聯絡人", - "addon.messages.addcontactconfirm": "您確定要將{{$a}}新增至通訊錄?", - "addon.messages.addtofavourites": "將對話標記星號", - "addon.messages.addtoyourcontacts": "加到通訊錄", - "addon.messages.blocknoncontacts": "阻擋非通訊錄內的人向我發送訊息", - "addon.messages.blockuser": "封鎖用戶", - "addon.messages.blockuserconfirm": "您確定要封鎖{{$a}}?", - "addon.messages.contactableprivacy": "接收訊息從:", - "addon.messages.contactableprivacy_coursemember": "我的通訊錄和我的課程中所有人", - "addon.messages.contactableprivacy_onlycontacts": "限通訊錄內", - "addon.messages.contactableprivacy_site": "網站中的所有人", - "addon.messages.contactblocked": "聯絡人被封鎖", - "addon.messages.contactlistempty": "聯絡人清單沒有資料", - "addon.messages.contactname": "聯絡人名稱", - "addon.messages.contactrequestsent": "已發送通訊聯絡請求", - "addon.messages.contacts": "通訊錄", - "addon.messages.conversationactions": "對話操作選單", - "addon.messages.decline": "拒絕", - "addon.messages.deleteallconfirm": "您確定要刪除整個對話嗎? 這不會刪除其他對話參與者的。", - "addon.messages.deleteallselfconfirm": "您確定要刪除整個個人對話嗎?", - "addon.messages.deleteconversation": "刪除對話", - "addon.messages.deleteforeveryone": "為我和其他人刪除", - "addon.messages.deletemessage": "刪除訊息", - "addon.messages.errordeletemessage": "刪除訊息時發生錯誤.", - "addon.messages.errorwhileretrievingcontacts": "從伺服器存取聯絡人時出錯", - "addon.messages.errorwhileretrievingdiscussions": "從伺服器存取討論區時出錯", - "addon.messages.errorwhileretrievingmessages": "從伺服器存取訊息時出錯", - "addon.messages.groupconversations": "群組", - "addon.messages.groupinfo": "群組資訊", - "addon.messages.individualconversations": "私人", - "addon.messages.info": "用戶資訊", - "addon.messages.isnotinyourcontacts": "{{$a}}不在您的通訊錄中", - "addon.messages.message": "訊息", - "addon.messages.messagenotsent": "訊息未發送, 請稍後再試.", - "addon.messages.messagepreferences": "訊息偏好", - "addon.messages.messages": "訊息", - "addon.messages.muteconversation": "靜音", - "addon.messages.mutedconversation": "已靜音的對話", - "addon.messages.newmessage": "新訊息", - "addon.messages.newmessages": "新的訊息", - "addon.messages.nocontactrequests": "沒有通訊聯絡的請求", - "addon.messages.nocontactsgetstarted": "沒有通訊錄", - "addon.messages.nofavourites": "未加星號的對話", - "addon.messages.nogroupconversations": "沒有群組對話", - "addon.messages.noindividualconversations": "沒有私人對話", - "addon.messages.nomessagesfound": "沒有找到訊息", - "addon.messages.noncontacts": "不在通訊錄", - "addon.messages.nousersfound": "沒有使用者", - "addon.messages.numparticipants": "{{$a}}個成員", - "addon.messages.removecontact": "刪除聯絡人", - "addon.messages.removecontactconfirm": "您確定要從通訊錄中刪除{{$a}}嗎?", - "addon.messages.removefromfavourites": "未標記星號的對話", - "addon.messages.removefromyourcontacts": "從您的通訊錄中移除", - "addon.messages.requests": "請求", - "addon.messages.requirecontacttomessage": "您需要將{{$a}}新增至聯絡人才能發送訊息。", - "addon.messages.searchcombined": "搜尋人員和訊息", - "addon.messages.selfconversation": "個人空間", - "addon.messages.selfconversationdefaultmessage": "儲存訊息草稿、連結、備註等,以便日後存取。", - "addon.messages.sendcontactrequest": "發送聯絡請求", - "addon.messages.showdeletemessages": "顯示刪除的訊息", - "addon.messages.type_blocked": "已停止", - "addon.messages.type_offline": "離線", - "addon.messages.type_online": "上線", - "addon.messages.type_search": "搜尋結果", - "addon.messages.type_strangers": "其他", - "addon.messages.unabletomessage": "您無法向此用戶發送訊息", - "addon.messages.unblockuser": "解除封鎖用戶", - "addon.messages.unblockuserconfirm": "您確定要解除{{$a}}的封鎖嗎?", - "addon.messages.unmuteconversation": "取消靜音", - "addon.messages.useentertosend": "使用輸入鍵發送", - "addon.messages.userwouldliketocontactyou": "{{$a}}想要與您聯絡", - "addon.messages.warningmessagenotsent": "無法傳送訊息給使用者 {{user}}. {{error}}", - "addon.messages.wouldliketocontactyou": "想與您聯絡", - "addon.messages.you": "您:", - "addon.messages.youhaveblockeduser": "您已封鎖此用戶", - "addon.messages.yourcontactrequestpending": "正在等待{{$a}}處理您的通訊請求", - "addon.mod_assign.acceptsubmissionstatement": "請接受提交聲明.", - "addon.mod_assign.addattempt": "允許另一次的繳交", - "addon.mod_assign.addnewattempt": "新增一個繳交管道", - "addon.mod_assign.addnewattemptfromprevious": "依據先前的作業新增一個繳交管道", - "addon.mod_assign.addsubmission": "繳交作業", - "addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "作業的詳細說明和繳交的表單將從 {{$a}} 開始可以使用", - "addon.mod_assign.allowsubmissionsfromdate": "開始繳交時間", - "addon.mod_assign.allowsubmissionsfromdatesummary": "這個作業將從 {{$a}} 開始可以繳交", - "addon.mod_assign.applytoteam": "將分數和回饋套用到整個群組", - "addon.mod_assign.assignmentisdue": "作業繳交已截止", - "addon.mod_assign.attemptnumber": "作業提交次數", - "addon.mod_assign.attemptreopenmethod": "重新開啟作業提交", - "addon.mod_assign.attemptreopenmethod_manual": "手動的", - "addon.mod_assign.attemptreopenmethod_untilpass": "自動的,直到通過", - "addon.mod_assign.attemptsettings": "繳交設定", - "addon.mod_assign.cannoteditduetostatementsubmission": "您無法在應用程式中新增或編輯提交內容, 因為我們無法從該網站擷取提交內容.", - "addon.mod_assign.cannotgradefromapp": "應用程式尚未支援部分評分方式, 無法修改.", - "addon.mod_assign.cannotsubmitduetostatementsubmission": "您無法在應用程式中提交評分, 因為我們無法從網站存取提交聲明", - "addon.mod_assign.confirmsubmission": "您確定要繳交作業並請求評分嗎?一旦這麼做,您將不能再修改作業。", - "addon.mod_assign.currentattempt": "這是第{{$a}}次繳交", - "addon.mod_assign.currentattemptof": "這是第{{$a.attemptnumber}}次繳交(允許繳交 {{$a.maxattempts}} 次)", - "addon.mod_assign.currentgrade": "目前成績單中的分數", - "addon.mod_assign.cutoffdate": "拒收作業時間", - "addon.mod_assign.defaultteam": "預設群組", - "addon.mod_assign.duedate": "規定繳交時間", - "addon.mod_assign.duedateno": "沒有規定繳交時間", - "addon.mod_assign.duedatereached": "此作業的規定繳交時間已經過了", - "addon.mod_assign.editingstatus": "編修狀態", - "addon.mod_assign.editsubmission": "修改我已繳交的作業", - "addon.mod_assign.erroreditpluginsnotsupported": "您無法在應用程式中新增或修改提交, 因為某些外掛不支援編輯:", - "addon.mod_assign.errorshowinginformation": "我們無法顯示提交資訊", - "addon.mod_assign.extensionduedate": "展延到期日", - "addon.mod_assign.feedbacknotsupported": "該應用程式不支援此回饋, 並且可能不包含所有資訊", - "addon.mod_assign.grade": "成績", - "addon.mod_assign.graded": "已評分", - "addon.mod_assign.gradedby": "已評分由", - "addon.mod_assign.gradedon": "評分標準", - "addon.mod_assign.gradelocked": "這一分數在這成績簿中是被鎖定或覆蓋的", - "addon.mod_assign.gradeoutof": "得分(配分{{$a}})", - "addon.mod_assign.gradingstatus": "評分狀態", - "addon.mod_assign.groupsubmissionsettings": "群組繳交作業設定", - "addon.mod_assign.hiddenuser": "參與者", - "addon.mod_assign.latesubmissions": "遲交的作業", - "addon.mod_assign.latesubmissionsaccepted": "寬延繳交直到 {{$a}}", - "addon.mod_assign.markingworkflowstate": "評分工作流程狀態", - "addon.mod_assign.markingworkflowstateinmarking": "正在評分中", - "addon.mod_assign.markingworkflowstateinreview": "正在檢查評分結果", - "addon.mod_assign.markingworkflowstatenotmarked": "沒被評分的", - "addon.mod_assign.markingworkflowstatereadyforrelease": "已準備好公布", - "addon.mod_assign.markingworkflowstatereadyforreview": "評分已完成", - "addon.mod_assign.markingworkflowstatereleased": "已經公布", - "addon.mod_assign.modulenameplural": "作業", - "addon.mod_assign.multipleteams": "你同時是屬於不同的群組的成員", - "addon.mod_assign.multipleteams_desc": "這一作業要求以群組方式繳交作業。你同時屬於多個群組的成員,要提交作業你必須只屬於一個群組。請聯絡您的教師,為你更改群組。", - "addon.mod_assign.noattempt": "沒有繳交作業", - "addon.mod_assign.nomoresubmissionsaccepted": "只接受已被寬延期限的學生的繳交作業", - "addon.mod_assign.noonlinesubmissions": "這個作業不需要您在網上繳交任何東西", - "addon.mod_assign.nosubmission": "這個作業還沒人繳交", - "addon.mod_assign.notallparticipantsareshown": "沒有提交的參與者不會被顯示出來", - "addon.mod_assign.noteam": "不屬於任何群組", - "addon.mod_assign.noteam_desc": "這一作業要求以群組方式繳交作業。你不屬於任何群組的成員,因此你無法繳交。請聯絡您的教師,將你指派到一個群組中。", - "addon.mod_assign.notgraded": "尚未評分", - "addon.mod_assign.numberofdraftsubmissions": "草稿", - "addon.mod_assign.numberofparticipants": "參與者", - "addon.mod_assign.numberofsubmissionsneedgrading": "需要評分", - "addon.mod_assign.numberofsubmittedassignments": "已繳交", - "addon.mod_assign.numberofteams": "群組", - "addon.mod_assign.numwords": "{{$a}}字數", - "addon.mod_assign.outof": "{{$a.current}},共有{{$a.total}}", - "addon.mod_assign.overdue": "已經超過應繳交時間: {{$a}}", - "addon.mod_assign.submission": "繳交作業", - "addon.mod_assign.submissioneditable": "學生可以編編輯這一繳交的作業", - "addon.mod_assign.submissionnoteditable": "學生不能編輯這一繳交的作業", - "addon.mod_assign.submissionnotsupported": "該應用程式不支援此提交, 並且可能不包含所有資訊", - "addon.mod_assign.submissionslocked": "此作業不接受繳交", - "addon.mod_assign.submissionstatus": "繳交狀態", - "addon.mod_assign.submissionstatus_": "未繳交", - "addon.mod_assign.submissionstatus_draft": "草稿(尚未繳交)", - "addon.mod_assign.submissionstatus_marked": "已評分", - "addon.mod_assign.submissionstatus_new": "沒有繳交的作業", - "addon.mod_assign.submissionstatus_reopened": "已經重新開啟", - "addon.mod_assign.submissionstatus_submitted": "已繳交,等待評分中", - "addon.mod_assign.submissionstatusheading": "繳交狀態", - "addon.mod_assign.submissionteam": "群組", - "addon.mod_assign.submitassignment": "繳交作業", - "addon.mod_assign.submitassignment_help": "當這項作業繳交後,您將不能再做任何修改。", - "addon.mod_assign.submittedearly": "提早{{$a}}就繳交作業", - "addon.mod_assign.submittedlate": "過期{{$a}}才繳交作業", - "addon.mod_assign.timemodified": "最後修改", - "addon.mod_assign.timeremaining": "剩餘時間", - "addon.mod_assign.ungroupedusers": "\"需要以群組方式提交作業\"的設定已經被啟動,但是仍有某些用戶沒有被指派到群組中,或者同一個人屬於多個群組,因此無法提交作業。", - "addon.mod_assign.unlimitedattempts": "無限制的", - "addon.mod_assign.userswhoneedtosubmit": "需要提交作業的用戶:{{$a}}", - "addon.mod_assign.userwithid": "ID為{{id}}的使用者", - "addon.mod_assign.viewsubmission": "檢視繳交的作業", - "addon.mod_assign.warningsubmissiongrademodified": "在網站上修改了提交成績.", - "addon.mod_assign.warningsubmissionmodified": "使用者的提交已在網站中修改.", - "addon.mod_assign.wordlimit": "字數限制", - "addon.mod_assign_feedback_comments.pluginname": "評語回饋", - "addon.mod_assign_feedback_editpdf.pluginname": "批註的PDF檔", - "addon.mod_assign_feedback_file.pluginname": "檔案回饋", - "addon.mod_assign_submission_comments.pluginname": "作業加備註", - "addon.mod_assign_submission_file.pluginname": "提交檔案", - "addon.mod_assign_submission_onlinetext.pluginname": "提交線上文字", - "addon.mod_assign_submission_onlinetext.wordlimitexceeded": "{{$a.limit}}為作業字數限制。{{$a.count}}為你要提交的字數。請重新檢視您提交之作業,並再試一次。", - "addon.mod_book.errorchapter": "讀取章節發生錯誤", - "addon.mod_book.modulenameplural": "電子書", - "addon.mod_book.navnexttitle": "下一章: {{$a}}", - "addon.mod_book.navprevtitle": "前一章: {{$a}}", - "addon.mod_book.tagarea_book_chapters": "書籍章節", - "addon.mod_book.toc": "目錄", - "addon.mod_chat.beep": "呼叫", - "addon.mod_chat.chatreport": "聊天室對話排程", - "addon.mod_chat.currentusers": "當前用戶", - "addon.mod_chat.enterchat": "點選這裡進入聊天室", - "addon.mod_chat.entermessage": "輸入您的訊息", - "addon.mod_chat.errorwhileconnecting": "連接到聊天時出錯.", - "addon.mod_chat.errorwhilegettingchatdata": "取得聊天資料時發生錯誤.", - "addon.mod_chat.errorwhilegettingchatusers": "取得聊天使用者時發生錯誤.", - "addon.mod_chat.errorwhileretrievingmessages": "從伺服器存取訊息時發生錯誤", - "addon.mod_chat.errorwhilesendingmessage": "當傳送訊息時發生錯誤", - "addon.mod_chat.messagebeepseveryone": "{{$a}} 呼叫所有人", - "addon.mod_chat.messagebeepsyou": "{{$a}} 呼叫您", - "addon.mod_chat.messageenter": "{{$a}} 剛加入這次聊天", - "addon.mod_chat.messageexit": "{{$a}} 已退出這次聊天", - "addon.mod_chat.messages": "訊息", - "addon.mod_chat.messageyoubeep": "您呼叫了{{$a}}", - "addon.mod_chat.modulenameplural": "聊天室", - "addon.mod_chat.mustbeonlinetosendmessages": "您必須上線才能傳送訊息", - "addon.mod_chat.nomessages": "尚無訊息", - "addon.mod_chat.saidto": "對", - "addon.mod_chat.send": "傳送", - "addon.mod_chat.sessionstart": "下一個聊天時段即將在{{$a.date}}開始,(離現在{{$a.fromnow}} )", - "addon.mod_chat.talk": "交談", - "addon.mod_chat.viewreport": "查看過去對話時段的內容", - "addon.mod_choice.cannotsubmit": "抱歉,在提交你的投票時發生問題,請再試一次。", - "addon.mod_choice.choiceoptions": "票選的選項", - "addon.mod_choice.errorgetchoice": "取得選項資料時出錯", - "addon.mod_choice.expired": "抱歉,此項活動已經在{{$a}}關閉,不再開放使用。", - "addon.mod_choice.full": "(已滿)", - "addon.mod_choice.modulenameplural": "票選活動", - "addon.mod_choice.noresultsviewable": "目前無法檢視票選結果", - "addon.mod_choice.notopenyet": "抱歉,這個活動在{{$a}}之前不能使用", - "addon.mod_choice.numberofuser": "回應的人數", - "addon.mod_choice.numberofuserinpercentage": "回應人數的百分比", - "addon.mod_choice.previewonly": "這只是這一活動的可用選項的預覽。你要等到{{$a}}才可以進行投票。", - "addon.mod_choice.removemychoice": "移除我的選擇", - "addon.mod_choice.responses": "答覆", - "addon.mod_choice.responsesresultgraphdescription": "{{number}}%的使用者選擇了選項: {{text}}.", - "addon.mod_choice.responsesresultgraphheader": "顯示圖表", - "addon.mod_choice.resultsnotsynced": "結果不包括您的上次回覆. 請同步更新.", - "addon.mod_choice.savemychoice": "儲存我的選擇", - "addon.mod_choice.userchoosethisoption": "有選擇這一項的用戶", - "addon.mod_choice.yourselection": "您的選擇", - "addon.mod_data.addentries": "新增條目", - "addon.mod_data.advancedsearch": "進階搜尋", - "addon.mod_data.alttext": "替代文字", - "addon.mod_data.approve": "審核", - "addon.mod_data.approved": "已核准", - "addon.mod_data.ascending": "升冪", - "addon.mod_data.authorfirstname": "作者的名字", - "addon.mod_data.authorlastname": "作者的姓氏", - "addon.mod_data.confirmdeleterecord": "您確定要刪除這筆資料嗎?", - "addon.mod_data.descending": "降冪", - "addon.mod_data.disapprove": "取消核准", - "addon.mod_data.emptyaddform": "您沒有填入任何欄位!", - "addon.mod_data.entrieslefttoadd": "在您要瀏覽其他同學提供的資料前,您必須要再新增{{$a.entriesleft}} 筆資料。", - "addon.mod_data.entrieslefttoaddtoview": "您必須新增{{$a.entrieslefttoview}}筆以上資料,才能夠看到其他同學提供的資料。", - "addon.mod_data.errormustsupplyvalue": "這裡你必須提供一個數值。", - "addon.mod_data.expired": "抱歉,這活動已經在 {{$a}}關閉,已經無法使用。", - "addon.mod_data.fields": "欄位", - "addon.mod_data.foundrecords": "找到記錄資料: {{$a.num}}/{{$a.max}} ({{$a.reseturl}}
                        \">重設篩選器)", - "addon.mod_data.latlongboth": "緯度和經度兩者都要填寫", - "addon.mod_data.menuchoose": "選擇...", - "addon.mod_data.modulenameplural": "資料庫", - "addon.mod_data.more": "更多", - "addon.mod_data.nomatch": "找不到符合的資料!", - "addon.mod_data.norecords": "資料庫中沒有資料", - "addon.mod_data.notapproved": "資料尚未審核。", - "addon.mod_data.notopenyet": "抱歉,這一活動要等到{{$a}}才開始", - "addon.mod_data.numrecords": "{{$a}} 筆資料", - "addon.mod_data.other": "其他", - "addon.mod_data.recordapproved": "資料已審核", - "addon.mod_data.recorddeleted": "資料已刪除", - "addon.mod_data.recorddisapproved": "不可進入", - "addon.mod_data.resetsettings": "重設欄位", - "addon.mod_data.search": "搜尋", - "addon.mod_data.selectedrequired": "所有必要的選擇", - "addon.mod_data.single": "單筆", - "addon.mod_data.tagarea_data_records": "資料紀錄", - "addon.mod_data.timeadded": "新增的時間", - "addon.mod_data.timemodified": "修改的時間", - "addon.mod_data.usedate": "包含在搜尋中", - "addon.mod_feedback.analysis": "分析", - "addon.mod_feedback.anonymous": "匿名", - "addon.mod_feedback.anonymous_entries": "匿名輸入({{$a}})", - "addon.mod_feedback.average": "平均數", - "addon.mod_feedback.complete_the_form": "開始填寫回答...", - "addon.mod_feedback.completed_feedbacks": "已回答", - "addon.mod_feedback.continue_the_form": "繼續填表", - "addon.mod_feedback.feedback_is_not_open": "這個回饋單還沒有開放", - "addon.mod_feedback.feedbackclose": "結束填答時間", - "addon.mod_feedback.feedbackopen": "開始填答時間", - "addon.mod_feedback.mapcourses": "對應回饋單到課程", - "addon.mod_feedback.maximal": "最高限制", - "addon.mod_feedback.minimal": "最小限制", - "addon.mod_feedback.mode": "模式", - "addon.mod_feedback.modulenameplural": "回饋單", - "addon.mod_feedback.next_page": "下一頁", - "addon.mod_feedback.non_anonymous": "用戶名稱和回應將被記錄", - "addon.mod_feedback.non_anonymous_entries": "具名輸入({{$a}})", - "addon.mod_feedback.non_respondents_students": "沒有回應的學生({{$a}})", - "addon.mod_feedback.not_selected": "未選", - "addon.mod_feedback.not_started": "沒有開始", - "addon.mod_feedback.numberoutofrange": "超過範圍的人數", - "addon.mod_feedback.overview": "概要", - "addon.mod_feedback.page_after_submit": "完成後的訊息", - "addon.mod_feedback.preview": "預覽", - "addon.mod_feedback.previous_page": "上一頁", - "addon.mod_feedback.questions": "問題", - "addon.mod_feedback.response_nr": "回應編號", - "addon.mod_feedback.responses": "回應", - "addon.mod_feedback.save_entries": "送出並結束", - "addon.mod_feedback.show_entries": "顯示回應", - "addon.mod_feedback.show_nonrespondents": "顯示沒有回應的學生", - "addon.mod_feedback.started": "已經開始", - "addon.mod_feedback.this_feedback_is_already_submitted": "您'已經完成這種活動。", - "addon.mod_folder.emptyfilelist": "沒有檔案可以顯示", - "addon.mod_folder.modulenameplural": "資料夾", - "addon.mod_forum.addanewdiscussion": "新增一個討論主題", - "addon.mod_forum.addanewquestion": "新增一個問題", - "addon.mod_forum.addanewtopic": "新增一個主題", - "addon.mod_forum.advanced": "進階", - "addon.mod_forum.cannotadddiscussion": "必須是群組成員才能在此討論區添加議題", - "addon.mod_forum.cannotadddiscussionall": "您沒有權限新增討論主題給所有參與者。", - "addon.mod_forum.cannotcreatediscussion": "不能建立新的議題", - "addon.mod_forum.couldnotadd": "由於不明錯誤, 無法新增您的貼文", - "addon.mod_forum.couldnotupdate": "由於不明錯誤,無法更新您的貼文", - "addon.mod_forum.delete": "刪除", - "addon.mod_forum.deletedpost": "這一貼文已被刪除", - "addon.mod_forum.deletesure": "您確定要刪除這一貼文嗎?", - "addon.mod_forum.discussion": "議題", - "addon.mod_forum.discussionlocked": "這一議題已經被封鎖,所以你無法回應它", - "addon.mod_forum.discussionpinned": "已經釘選", - "addon.mod_forum.discussionsubscription": "議題訂閱", - "addon.mod_forum.edit": "編輯", - "addon.mod_forum.erroremptymessage": "貼文的訊息不能是空白", - "addon.mod_forum.erroremptysubject": "貼文的主旨不能是空白", - "addon.mod_forum.errorgetforum": "讀取討論區資料發生錯誤", - "addon.mod_forum.errorgetgroups": "讀取群組設定發生錯誤", - "addon.mod_forum.forumnodiscussionsyet": "這個討論區還沒有討論主題", - "addon.mod_forum.group": "群組", - "addon.mod_forum.lastpost": "最新貼文", - "addon.mod_forum.message": "訊息", - "addon.mod_forum.modeflatnewestfirst": "以平舖方式呈現回應的貼文,最晚貼出的在前", - "addon.mod_forum.modeflatoldestfirst": "以平舖方式呈現回應的貼文,最早貼出的在前", - "addon.mod_forum.modenested": "以縮排方式呈現回應的貼文", - "addon.mod_forum.modulenameplural": "討論區", - "addon.mod_forum.numdiscussions": "{{numdiscussions}}篇討論", - "addon.mod_forum.numreplies": "{{numreplies}}個回覆", - "addon.mod_forum.posttoforum": "貼文到討論區中", - "addon.mod_forum.posttomygroups": "張貼一複本到所有群組", - "addon.mod_forum.privatereply": "私下回覆", - "addon.mod_forum.re": "回應:", - "addon.mod_forum.refreshdiscussions": "更新討論區", - "addon.mod_forum.refreshposts": "更新討論區貼文", - "addon.mod_forum.reply": "回覆", - "addon.mod_forum.replyplaceholder": "您的回應...", - "addon.mod_forum.subject": "主旨", - "addon.mod_forum.tagarea_forum_posts": "討論區貼文", - "addon.mod_forum.unread": "未閱讀", - "addon.mod_forum.unreadpostsnumber": "{{$a}}篇未閱讀的貼文", - "addon.mod_forum.yourreply": "您的回應內容", - "addon.mod_glossary.addentry": "新增條目", - "addon.mod_glossary.aliases": "同義字或別名", - "addon.mod_glossary.attachment": "附件", - "addon.mod_glossary.browsemode": "瀏覽條目", - "addon.mod_glossary.byalphabet": "按字母順序排列", - "addon.mod_glossary.byauthor": "以作者為分群", - "addon.mod_glossary.bynewestfirst": "最新的優先", - "addon.mod_glossary.byrecentlyupdated": "最近的更新", - "addon.mod_glossary.bysearch": "搜尋", - "addon.mod_glossary.casesensitive": "要區分字母大小寫", - "addon.mod_glossary.categories": "類別", - "addon.mod_glossary.concept": "概念", - "addon.mod_glossary.definition": "定義", - "addon.mod_glossary.entrypendingapproval": "這個項目已被暫緩核可", - "addon.mod_glossary.entryusedynalink": "這條目將被自動連結", - "addon.mod_glossary.errconceptalreadyexists": "這個概念已經存在,在這個辭彙表中不允許重複。", - "addon.mod_glossary.errorloadingentries": "載入項目時發生錯誤", - "addon.mod_glossary.errorloadingentry": "載入項目時發生錯誤", - "addon.mod_glossary.errorloadingglossary": "載入詞彙表時發生錯誤.", - "addon.mod_glossary.fillfields": "概念和定義是必要的欄位", - "addon.mod_glossary.fullmatch": "要完全符合整個文字", - "addon.mod_glossary.linking": "自動連結", - "addon.mod_glossary.modulenameplural": "詞彙表", - "addon.mod_glossary.noentriesfound": "未找到條目.", - "addon.mod_glossary.searchquery": "搜索查詢", - "addon.mod_glossary.tagarea_glossary_entries": "詞彙條目", - "addon.mod_imscp.deploymenterror": "內容包有錯誤!", - "addon.mod_imscp.modulenameplural": "IMS內容包", - "addon.mod_imscp.showmoduledescription": "顯示說明", - "addon.mod_imscp.toc": "內容列表", - "addon.mod_lesson.answer": "答案", - "addon.mod_lesson.attempt": "作答:{{$a}}", - "addon.mod_lesson.attemptheader": "作答", - "addon.mod_lesson.attemptsremaining": "您還有{{$a}}次作答機會", - "addon.mod_lesson.averagescore": "平均分數", - "addon.mod_lesson.averagetime": "平均時間", - "addon.mod_lesson.branchtable": "目錄(分支表)", - "addon.mod_lesson.cannotfindattempt": "錯誤:找不到作答次", - "addon.mod_lesson.cannotfinduser": "錯誤:找不到用戶", - "addon.mod_lesson.clusterjump": "在群集中隱藏的問題", - "addon.mod_lesson.completed": "已完成", - "addon.mod_lesson.congratulations": "恭喜!編序學習已經完成。", - "addon.mod_lesson.continue": "繼續", - "addon.mod_lesson.continuetonextpage": "繼續到下一頁", - "addon.mod_lesson.defaultessayresponse": "您的申論題將會由你的教師評分", - "addon.mod_lesson.detailedstats": "詳細統計", - "addon.mod_lesson.didnotanswerquestion": "沒有回答這一試題", - "addon.mod_lesson.displayofgrade": "(只向學生)顯示成績", - "addon.mod_lesson.displayscorewithessays": "

                        您在自動評計分的試題中,獲得{{$a.score}}分(此部分配分是{{$a.tempmaxgrade}})。

                        \n

                        您的{{$a.essayquestions}}個問答題將會晚一些被評分,並加入到您的最後成績中。

                        \n

                        若不包含問答題,您目前的分數是{{$a.score}},總配分是{{$a.grade}}

                        ", - "addon.mod_lesson.displayscorewithoutessays": "您的分數是{{$a.score}}(滿分{{$a.grade}})", - "addon.mod_lesson.emptypassword": "密碼不可以是空白", - "addon.mod_lesson.enterpassword": "請輸入密碼", - "addon.mod_lesson.eolstudentoutoftimenoanswers": "您沒有回答任何試題。此課程您將得到0分。", - "addon.mod_lesson.finish": "完成", - "addon.mod_lesson.firstwrong": "你答錯了,你想要再答一次嗎?(若你回答正確,也不會算入最後正式分數)?", - "addon.mod_lesson.gotoendoflesson": "跳到這編序學習的結束", - "addon.mod_lesson.grade": "分數", - "addon.mod_lesson.highscore": "最高分", - "addon.mod_lesson.hightime": "最長時間", - "addon.mod_lesson.leftduringtimed": "您在一個有計時的編序教學中離開了。
                        請按\"繼續\"以重新開始這編序教學", - "addon.mod_lesson.leftduringtimednoretake": "您在一個有計時的編序教學中離開了,而您不被允許重新或繼續這一編序教學", - "addon.mod_lesson.lessonmenu": "編序學習選單", - "addon.mod_lesson.lessonstats": "編序學習統計", - "addon.mod_lesson.linkedmedia": "已連結的媒體", - "addon.mod_lesson.loginfail": "登入錯誤,請重試", - "addon.mod_lesson.lowscore": "最低分數", - "addon.mod_lesson.lowtime": "最短時間", - "addon.mod_lesson.maximumnumberofattemptsreached": "已達到作答次的上限 - 將進入下一頁", - "addon.mod_lesson.modattemptsnoteacher": "學生回顧功能只開放給學生們。", - "addon.mod_lesson.modulenameplural": "編序學習", - "addon.mod_lesson.noanswer": "有一個或以上的問題沒有回答。請回頭並提交一個答案。", - "addon.mod_lesson.nolessonattempts": "這個課程還沒有人嘗試。", - "addon.mod_lesson.nolessonattemptsgroup": "{{$a}}群組成員在這單元上還沒有人作答", - "addon.mod_lesson.notcompleted": "未完成", - "addon.mod_lesson.numberofcorrectanswers": "答對題數:{{$a}}", - "addon.mod_lesson.numberofpagesviewed": "頁面瀏覽數:{{$a}}", - "addon.mod_lesson.numberofpagesviewednotice": "已經回答題數:{{$a.nquestions}}(您至少要回答{{$a.minquestions}}題)", - "addon.mod_lesson.ongoingcustom": "目前的{{$a.currenthigh}}分中,您已獲得{{$a.score}}分。", - "addon.mod_lesson.ongoingnormal": "到目前為止的{{$a.viewed}}問題中,您答對了{{$a.correct}}題。", - "addon.mod_lesson.or": "或", - "addon.mod_lesson.overview": "綜覽", - "addon.mod_lesson.preview": "預覽", - "addon.mod_lesson.progressbarteacherwarning2": "你不能看到這進度列,因為你可以編輯這一編序學習。", - "addon.mod_lesson.progresscompleted": "在這一編序學習,你已經完成{{$a}}%", - "addon.mod_lesson.question": "試題", - "addon.mod_lesson.rawgrade": "原始分數", - "addon.mod_lesson.reports": "報表", - "addon.mod_lesson.response": "回覆", - "addon.mod_lesson.review": "復習", - "addon.mod_lesson.reviewlesson": "復習編序學習", - "addon.mod_lesson.reviewquestionback": "是的,我想要再試", - "addon.mod_lesson.reviewquestioncontinue": "不,我只想要繼續下一個問題", - "addon.mod_lesson.secondpluswrong": "不離開嗎?您想要再試試?", - "addon.mod_lesson.submit": "提交", - "addon.mod_lesson.teacherjumpwarning": "此編序學習正使用群集{{$a.cluster}}跳躍,或{{$a.unseen}}隱藏跳躍。將使用下一頁的跳躍方式來取代。以學生身份登入來測試這些跳躍。", - "addon.mod_lesson.teacherongoingwarning": "正進行中的分數只顯示給學生。以學生身份登入來測試這個進行中的分數。", - "addon.mod_lesson.teachertimerwarning": "計時器只提供給學生。以學生身份登入來測試這個計時器。", - "addon.mod_lesson.thatsthecorrectanswer": "這是正確的答案", - "addon.mod_lesson.thatsthewronganswer": "這是錯誤的答案", - "addon.mod_lesson.timeremaining": "剩餘時間", - "addon.mod_lesson.timetaken": "使用時間", - "addon.mod_lesson.unseenpageinbranch": "在內容頁面中未瀏覽的試題", - "addon.mod_lesson.welldone": "做的好!", - "addon.mod_lesson.youhaveseen": "您已經瀏覽了該課程的多個頁面。
                        想從最後瀏覽的頁面開始嗎?", - "addon.mod_lesson.youranswer": "您的答案", - "addon.mod_lesson.yourcurrentgradeisoutof": "您目前的分數是{{$a.grade}}(總分{{$a.total}})", - "addon.mod_lesson.youshouldview": "您至少應該回答:{{$a}}題", - "addon.mod_lti.errorgetlti": "取得模組資料時出錯", - "addon.mod_lti.errorinvalidlaunchurl": "啟動網址無效", - "addon.mod_lti.launchactivity": "啟動活動", - "addon.mod_lti.modulenameplural": "外部工具", - "addon.mod_page.errorwhileloadingthepage": "載入頁面內容時出錯", - "addon.mod_page.modulenameplural": "頁面", - "addon.mod_quiz.answercolon": "答案:", - "addon.mod_quiz.attemptfirst": "第一次作答", - "addon.mod_quiz.attemptlast": "最後一次作答", - "addon.mod_quiz.attemptnumber": "作答數", - "addon.mod_quiz.attemptquiznow": "開始作答", - "addon.mod_quiz.attemptstate": "作答狀態", - "addon.mod_quiz.cannotsubmitquizdueto": "此測驗嘗試無法提交, 原因如下:", - "addon.mod_quiz.clearchoice": "清除我的選擇", - "addon.mod_quiz.comment": "評論", - "addon.mod_quiz.completedon": "完成於", - "addon.mod_quiz.confirmclose": "一旦您提交答案,您將無法再更改您這次作答的答案。", - "addon.mod_quiz.confirmcontinueoffline": "自{{$ a}}之後, 此嘗試尚未恢復同步. 如果您在此後在其他設備上繼續此嘗試, 您可能會遺失資料.", - "addon.mod_quiz.confirmleavequizonerror": "存入答案時發生錯誤. 您確定要離開測驗嗎?", - "addon.mod_quiz.confirmstart": "這一測驗有 {{$a}} 的時間限制。時間將會從你開始做測驗時倒數計時,而你必須在時限到之前提交答案。你確定你現在就要開始作答?", - "addon.mod_quiz.confirmstartheader": "計時的測驗", - "addon.mod_quiz.connectionerror": "網路連結中斷。(自動儲存失敗)\n\n系統會紀錄最後幾分鐘在這一頁所做的任何回應,然後嘗試重新連結。\n\n一旦重新建立起連結,你的回應將會被儲存,且這一訊息將會消失。", - "addon.mod_quiz.continueattemptquiz": "繼續上一次作答", - "addon.mod_quiz.continuepreview": "繼續上一次的預覽", - "addon.mod_quiz.errorbehaviournotsupported": "此測驗不能在應用程式中嘗試進行, 因為應用程式不支援此行為:", - "addon.mod_quiz.errordownloading": "下載必要資料時出錯", - "addon.mod_quiz.errorgetattempt": "取得嘗試資料時出錯", - "addon.mod_quiz.errorgetquestions": "取得問題時出錯", - "addon.mod_quiz.errorgetquiz": "取得測驗資料時出錯", - "addon.mod_quiz.errorparsequestions": "閱讀問題時發生錯誤. 請在網路瀏覽器中嘗試進行此測驗.", - "addon.mod_quiz.errorquestionsnotsupported": "此測驗不能在應用程式中嘗試進行, 因為它可能包含應用程式不支援的問題:", - "addon.mod_quiz.errorrulesnotsupported": "此測驗不能在應用程式中嘗試,因為它具有應用程序不支持的存取規則:", - "addon.mod_quiz.errorsaveattempt": "存入嘗試資料時發生錯誤.", - "addon.mod_quiz.feedback": "回饋", - "addon.mod_quiz.finishattemptdots": "完成作答....", - "addon.mod_quiz.finishnotsynced": "已完成但未同步", - "addon.mod_quiz.grade": "分數", - "addon.mod_quiz.gradeaverage": "平均分數", - "addon.mod_quiz.gradehighest": "最高分數", - "addon.mod_quiz.grademethod": "評分方式", - "addon.mod_quiz.gradesofar": "{{$a.method}}:{{$a.mygrade}} / {{$a.quizgrade}}。", - "addon.mod_quiz.marks": "得分", - "addon.mod_quiz.modulenameplural": "測驗卷", - "addon.mod_quiz.mustbesubmittedby": "這作答應該由{{$a}}提交", - "addon.mod_quiz.noquestions": "尚未加入題目", - "addon.mod_quiz.noreviewattempt": "你不被允許重新檢視作答結果", - "addon.mod_quiz.notyetgraded": "還沒被計分過", - "addon.mod_quiz.opentoc": "打開導航彈出視窗.", - "addon.mod_quiz.outof": "得{{$a.grade}}分(滿分為{{$a.maxgrade}}分)", - "addon.mod_quiz.outofpercent": "得分{{$a.grade}}/配分{{$a.maxgrade}}({{$a.percent}}%)", - "addon.mod_quiz.outofshort": "{{$a.grade}}/{{$a.maxgrade}}", - "addon.mod_quiz.overallfeedback": "整體回饋", - "addon.mod_quiz.overdue": "過期", - "addon.mod_quiz.overduemustbesubmittedby": "這次作答已經超過時間限制。它應該已經被提交。若你想要這測驗被計分,你應該在{{$a}}提交它。若你這時候沒有提交它,這次作答將不算分數。", - "addon.mod_quiz.preview": "預覽", - "addon.mod_quiz.previewquiznow": "立刻預覽測驗", - "addon.mod_quiz.question": "試題", - "addon.mod_quiz.quiznavigation": "測驗導覽", - "addon.mod_quiz.quizpassword": "測驗密碼", - "addon.mod_quiz.reattemptquiz": "再測驗一次", - "addon.mod_quiz.requirepasswordmessage": "要進行這個測驗,你需要知道這測驗的密碼", - "addon.mod_quiz.returnattempt": "回到作答次", - "addon.mod_quiz.review": "復習", - "addon.mod_quiz.reviewofattempt": "回顧第 {{$a}} 次作答", - "addon.mod_quiz.reviewofpreview": "預覽檢閱", - "addon.mod_quiz.showall": "在一頁中顯示所有題目", - "addon.mod_quiz.showeachpage": "一次顯示一頁", - "addon.mod_quiz.startattempt": "開始作答", - "addon.mod_quiz.startedon": "開始於", - "addon.mod_quiz.stateabandoned": "從未提交", - "addon.mod_quiz.statefinished": "已經完成", - "addon.mod_quiz.statefinisheddetails": "已經提交{{$a}}", - "addon.mod_quiz.stateinprogress": "進行中", - "addon.mod_quiz.stateoverdue": "過期", - "addon.mod_quiz.stateoverduedetails": "必須在{{$a}}之前提交", - "addon.mod_quiz.status": "狀態", - "addon.mod_quiz.submitallandfinish": "全部送出並結束", - "addon.mod_quiz.summaryofattempt": "作答紀錄摘要", - "addon.mod_quiz.summaryofattempts": "你的先前作答記錄摘要", - "addon.mod_quiz.timeleft": "剩餘時間", - "addon.mod_quiz.timetaken": "所用時間", - "addon.mod_quiz.warningattemptfinished": "由於在網站上已完成或找不到, 因此捨棄離線嘗試.", - "addon.mod_quiz.warningdatadiscarded": "一些離線的答案被捨棄, 因為問題在上線時被修改了.", - "addon.mod_quiz.warningdatadiscardedfromfinished": "嘗試未完成, 因為一些離線答案被捨棄. 請檢查您的答案, 然後重新提交嘗試.", - "addon.mod_quiz.yourfinalgradeis": "這個測驗您的最後成績是{{$a}}", - "addon.mod_resource.errorwhileloadingthecontent": "載入內容時發生錯誤.", - "addon.mod_resource.modifieddate": "修改於{{$a}}", - "addon.mod_resource.modulenameplural": "檔案", - "addon.mod_resource.openthefile": "開啟檔案", - "addon.mod_resource.uploadeddate": "上傳於{{$a}}", - "addon.mod_scorm.asset": "資源", - "addon.mod_scorm.assetlaunched": "已閱讀的資源", - "addon.mod_scorm.attempts": "作答次", - "addon.mod_scorm.averageattempt": "平均使用次數", - "addon.mod_scorm.browse": "預覽", - "addon.mod_scorm.browsed": "已瀏覽", - "addon.mod_scorm.browsemode": "預覽模式", - "addon.mod_scorm.cannotcalculategrade": "無法計算成績", - "addon.mod_scorm.completed": "已完成", - "addon.mod_scorm.contents": "目錄", - "addon.mod_scorm.dataattemptshown": "此資料屬於嘗試編號{{number}}.", - "addon.mod_scorm.enter": "進入", - "addon.mod_scorm.errorcreateofflineattempt": "建立新的離線嘗試時發生錯誤. 請再試一次.", - "addon.mod_scorm.errordownloadscorm": "下載SCORM時出錯: “{{name}}”.", - "addon.mod_scorm.errorgetscorm": "取得SCORM資料時出錯", - "addon.mod_scorm.errorinvalidversion": "很抱歉, 本應用程序僅支援SCORM 1.2.", - "addon.mod_scorm.errornotdownloadable": "您的Moodle網站禁止SCORM軟體的下載. 請與您的Moodle網站管理員聯繫.", - "addon.mod_scorm.errornovalidsco": "此SCORM沒有可見的SCO供載入.", - "addon.mod_scorm.errorpackagefile": "很抱歉, 該應用程式只支援ZIP.", - "addon.mod_scorm.errorsyncscorm": "同步時發生錯誤. 請再試一次.", - "addon.mod_scorm.exceededmaxattempts": "你已經達到最大的作答次數", - "addon.mod_scorm.failed": "失敗", - "addon.mod_scorm.firstattempt": "第一次作答", - "addon.mod_scorm.gradeaverage": "平均成績", - "addon.mod_scorm.gradeforattempt": "作答次的分數", - "addon.mod_scorm.gradehighest": "最高成績", - "addon.mod_scorm.grademethod": "成績採計方式", - "addon.mod_scorm.gradereported": "成績報告", - "addon.mod_scorm.gradescoes": "學習目標", - "addon.mod_scorm.gradesum": "加總", - "addon.mod_scorm.highestattempt": "以最高分採計", - "addon.mod_scorm.incomplete": "不完整", - "addon.mod_scorm.lastattempt": "最後完成的作答次", - "addon.mod_scorm.modulenameplural": "SCORM課程包", - "addon.mod_scorm.newattempt": "開始一個新的作答次", - "addon.mod_scorm.noattemptsallowed": "允許作答的次數", - "addon.mod_scorm.noattemptsmade": "你已經作答的次數", - "addon.mod_scorm.notattempted": "尚未作答", - "addon.mod_scorm.offlineattemptnote": "此嘗試包含尚未同步的資料.", - "addon.mod_scorm.offlineattemptovermax": "無法發送此嘗試, 因為您超過了最大嘗試次數.", - "addon.mod_scorm.organizations": "組織", - "addon.mod_scorm.passed": "通過", - "addon.mod_scorm.reviewmode": "複習模式", - "addon.mod_scorm.score": "分數", - "addon.mod_scorm.scormstatusnotdownloaded": "此SCORM未下載. 它會在您打開時自動下載.", - "addon.mod_scorm.scormstatusoutdated": "此SCORM自上次下載以來已被修改. 它會在您打開時自動下載.", - "addon.mod_scorm.suspended": "已休學", - "addon.mod_scorm.toc": "內容目錄", - "addon.mod_scorm.warningofflinedatadeleted": "嘗試{{number}}的某些離線資料已被刪除, 因為無法建立新的嘗試.", - "addon.mod_scorm.warningsynconlineincomplete": "某些嘗試無法與網站同步, 因為上次上線嘗試未完成, 請先完成上線嘗試.", - "addon.mod_survey.cannotsubmitsurvey": "很抱歉, 提交您的問卷調查時發生問題. 請再試一次.", - "addon.mod_survey.errorgetsurvey": "取得調查資料時出錯", - "addon.mod_survey.ifoundthat": "我發現", - "addon.mod_survey.ipreferthat": "我希望的是", - "addon.mod_survey.modulenameplural": "問卷", - "addon.mod_survey.responses": "回應", - "addon.mod_survey.results": "結果", - "addon.mod_survey.surveycompletednograph": "你已經完成這一份問卷。", - "addon.mod_url.accessurl": "存取URL", - "addon.mod_url.modulenameplural": "網址", - "addon.mod_url.pointingtourl": "此資源所指向的網址", - "addon.mod_wiki.cannoteditpage": "你不能編輯這一頁", - "addon.mod_wiki.createpage": "建立頁面", - "addon.mod_wiki.editingpage": "編輯這一頁\"{{$a}}\"", - "addon.mod_wiki.errorloadingpage": "載入頁面時出錯", - "addon.mod_wiki.errornowikiavailable": "這個維基詞條沒有內容", - "addon.mod_wiki.gowikihome": "到維基百科", - "addon.mod_wiki.map": "地圖", - "addon.mod_wiki.modulenameplural": "Wiki共筆", - "addon.mod_wiki.newpagehdr": "新頁面", - "addon.mod_wiki.newpagetitle": "新頁面標題", - "addon.mod_wiki.nocontent": "在這一頁裡沒有內容", - "addon.mod_wiki.notingroup": "不在群體", - "addon.mod_wiki.pageexists": "這頁面已經存在。", - "addon.mod_wiki.pagename": "頁面名稱", - "addon.mod_wiki.subwiki": "Subwiki", - "addon.mod_wiki.tagarea_wiki_pages": "Wiki頁面", - "addon.mod_wiki.titleshouldnotbeempty": "標題不能為空", - "addon.mod_wiki.viewpage": "檢視頁面", - "addon.mod_wiki.wikipage": "維基頁面", - "addon.mod_wiki.wrongversionlock": "當你在編輯此頁時,另一用戶已經編輯完這一頁,你的內容是過時的。", - "addon.mod_workshop.alreadygraded": "已經評分的", - "addon.mod_workshop.areainstructauthors": "對於作業繳交的指示", - "addon.mod_workshop.areainstructreviewers": "關於評價的指示", - "addon.mod_workshop.assess": "評價", - "addon.mod_workshop.assessedsubmission": "已被評價的作業", - "addon.mod_workshop.assessmentform": "評價表格", - "addon.mod_workshop.assessmentsettings": "評價設定", - "addon.mod_workshop.assessmentweight": "評價的權重", - "addon.mod_workshop.assignedassessments": "需要評價的作業", - "addon.mod_workshop.assignedassessmentsnone": "您沒有需要評價的作業", - "addon.mod_workshop.conclusion": "結語", - "addon.mod_workshop.createsubmission": "開始準備你的作業", - "addon.mod_workshop.deletesubmission": "刪除繳交的作業", - "addon.mod_workshop.editsubmission": "編輯繳交的作業", - "addon.mod_workshop.feedbackauthor": "給作者的回饋", - "addon.mod_workshop.feedbackby": "{{$a}}提供的回饋", - "addon.mod_workshop.feedbackreviewer": "給評閱者的回饋", - "addon.mod_workshop.givengrades": "給出的分數", - "addon.mod_workshop.gradecalculated": "計算出的作業表現分數", - "addon.mod_workshop.gradeinfo": "成績:{{$a.received}}/{{$a.max}}", - "addon.mod_workshop.gradeover": "覆蓋作業成績", - "addon.mod_workshop.gradesreport": "工作坊的成績報告", - "addon.mod_workshop.gradinggrade": "評鑑表現的得分", - "addon.mod_workshop.gradinggradecalculated": "計算出的評鑑表現分數", - "addon.mod_workshop.gradinggradeof": "評鑑表現分數(最高分 {{$a}})", - "addon.mod_workshop.gradinggradeover": "覆蓋評價成績", - "addon.mod_workshop.modulenameplural": "工作坊", - "addon.mod_workshop.nogradeyet": "還沒有分數", - "addon.mod_workshop.notassessed": "還沒進行評價", - "addon.mod_workshop.notoverridden": "不可覆蓋", - "addon.mod_workshop.noyoursubmission": "您還沒有繳交作品", - "addon.mod_workshop.overallfeedback": "整體回饋", - "addon.mod_workshop.publishedsubmissions": "已發佈的作業", - "addon.mod_workshop.publishsubmission": "發佈作業", - "addon.mod_workshop.publishsubmission_help": "當工作坊關閉後,其他人仍可以看到已發佈的作業。", - "addon.mod_workshop.reassess": "重新評價", - "addon.mod_workshop.receivedgrades": "收到的分數", - "addon.mod_workshop.submissionattachment": "附件", - "addon.mod_workshop.submissioncontent": "作業內容", - "addon.mod_workshop.submissiondeleteconfirm": "您確定要刪除下列的作業嗎?", - "addon.mod_workshop.submissiongrade": "作業表現的得分", - "addon.mod_workshop.submissiongradeof": "作業的分數( 最高{{$a}} )", - "addon.mod_workshop.submissionsreport": "工作坊作業繳交報表", - "addon.mod_workshop.submissiontitle": "作業標題", - "addon.mod_workshop.userplan": "工作坊計劃表", - "addon.mod_workshop.userplancurrentphase": "當前階段", - "addon.mod_workshop.weightinfo": "權重:{{$a}}", - "addon.mod_workshop.yourassessment": "您的評價", - "addon.mod_workshop.yourassessmentfor": "你對於{{$a}}的評價", - "addon.mod_workshop.yourgrades": "你的成績", - "addon.mod_workshop.yoursubmission": "您的作業", - "addon.mod_workshop_assessment_accumulative.dimensioncommentfor": "{{$a}}的評論", - "addon.mod_workshop_assessment_accumulative.dimensiongradefor": "{{$a}}的分數", - "addon.mod_workshop_assessment_accumulative.dimensionnumber": "判斷規準{{$a}}", - "addon.mod_workshop_assessment_accumulative.mustchoosegrade": "你必須就這判斷規準選擇一分數", - "addon.mod_workshop_assessment_comments.dimensioncommentfor": "{{$a}}的評論", - "addon.mod_workshop_assessment_comments.dimensionnumber": "向度{{$a}}", - "addon.mod_workshop_assessment_numerrors.dimensioncommentfor": "{{$a}}的評論", - "addon.mod_workshop_assessment_numerrors.dimensiongradefor": "{{$a}}的分數", - "addon.mod_workshop_assessment_numerrors.dimensionnumber": "判斷規準{{$a}}", - "addon.mod_workshop_assessment_rubric.dimensionnumber": "規準{{$a}}", - "addon.mod_workshop_assessment_rubric.mustchooseone": "你必須選擇其中一項", - "addon.notes.addnewnote": "新增一則筆記", - "addon.notes.coursenotes": "課程筆記", - "addon.notes.deleteconfirm": "刪除這則筆記嗎?", - "addon.notes.eventnotecreated": "已建立的筆記", - "addon.notes.eventnotedeleted": "已刪除的筆記", - "addon.notes.nonotes": "還沒有這種類型的筆記", - "addon.notes.note": "筆記", - "addon.notes.notes": "筆記", - "addon.notes.personalnotes": "個人的筆記", - "addon.notes.publishstate": "使用的情境", - "addon.notes.sitenotes": "全站筆記", - "addon.notes.userwithid": "使用者編號 {{id}}", - "addon.notes.warningnotenotsent": "無法新增註冊到課程 {{course}}. {{error}}", - "addon.notifications.errorgetnotifications": "取得通知資料發生錯誤", - "addon.notifications.markallread": "把全部標示為已讀", - "addon.notifications.notificationpreferences": "通知提醒的偏好", - "addon.notifications.notifications": "通知", - "addon.notifications.therearentnotificationsyet": "沒有通知訊息", - "addon.storagemanager.info": "把文件檔案儲存在您的裝置可加快APP程序運行,並且可以讓APP離線使用課程。 如果您需要釋放儲存空間,則可以安全地卸載文件檔案。", - "addon.storagemanager.managestorage": "管理儲存空間", - "addon.storagemanager.storageused": "已使用儲存空間:", - "assets.countries.AD": "安道爾", - "assets.countries.AE": "阿聯酋", - "assets.countries.AF": "阿富汗", - "assets.countries.AG": "安堤瓜及巴爾布達島", - "assets.countries.AI": "安圭拉", - "assets.countries.AL": "阿爾巴尼亞", - "assets.countries.AM": "亞美尼亞", - "assets.countries.AO": "安哥拉", - "assets.countries.AQ": "南極洲", - "assets.countries.AR": "阿根廷", - "assets.countries.AS": "薩摩亞", - "assets.countries.AT": "奧地利", - "assets.countries.AU": "澳大利亞", - "assets.countries.AW": "阿魯巴島", - "assets.countries.AX": "奧蘭群島", - "assets.countries.AZ": "亞塞拜然", - "assets.countries.BA": "波斯尼亞和黑塞哥維那", - "assets.countries.BB": "巴貝多", - "assets.countries.BD": "孟加拉", - "assets.countries.BE": "比利時", - "assets.countries.BF": "波契納法索", - "assets.countries.BG": "保加利亞", - "assets.countries.BH": "巴林", - "assets.countries.BI": "布隆迪", - "assets.countries.BJ": "貝南", - "assets.countries.BL": "聖巴特島", - "assets.countries.BM": "百慕達", - "assets.countries.BN": "汶萊", - "assets.countries.BO": "玻利維亞多民族國", - "assets.countries.BQ": "博內爾,聖尤斯特歇斯和薩巴", - "assets.countries.BR": "巴西", - "assets.countries.BS": "巴哈馬", - "assets.countries.BT": "不丹", - "assets.countries.BV": "波沃特島", - "assets.countries.BW": "波札那", - "assets.countries.BY": "白俄羅斯", - "assets.countries.BZ": "貝里斯", - "assets.countries.CA": "加拿大", - "assets.countries.CC": "格陵島", - "assets.countries.CD": "剛果民主共和國", - "assets.countries.CF": "中非", - "assets.countries.CG": "剛果", - "assets.countries.CH": "瑞士", - "assets.countries.CI": "象牙海岸", - "assets.countries.CK": "庫克島", - "assets.countries.CL": "智利", - "assets.countries.CM": "喀麥隆", - "assets.countries.CN": "中國", - "assets.countries.CO": "哥倫比亞", - "assets.countries.CR": "哥斯大黎加", - "assets.countries.CU": "古巴", - "assets.countries.CV": "維德角", - "assets.countries.CW": "庫拉索島", - "assets.countries.CX": "聖誕島", - "assets.countries.CY": "賽普勒斯", - "assets.countries.CZ": "捷克", - "assets.countries.DE": "德國", - "assets.countries.DJ": "吉布提", - "assets.countries.DK": "丹麥", - "assets.countries.DM": "多明尼加", - "assets.countries.DO": "多明尼加共和國", - "assets.countries.DZ": "阿爾及利亞", - "assets.countries.EC": "厄瓜多", - "assets.countries.EE": "愛沙尼亞", - "assets.countries.EG": "埃及", - "assets.countries.EH": "西撒哈拉", - "assets.countries.ER": "厄利垂亞", - "assets.countries.ES": "西班牙", - "assets.countries.ET": "埃塞阿比亞", - "assets.countries.FI": "芬蘭", - "assets.countries.FJ": "斐濟", - "assets.countries.FK": "馬爾維拉", - "assets.countries.FM": "密克羅尼西亞", - "assets.countries.FO": "法羅群島", - "assets.countries.FR": "法國", - "assets.countries.GA": "加蓬", - "assets.countries.GB": "英國", - "assets.countries.GD": "格瑞那達", - "assets.countries.GE": "喬治亞州", - "assets.countries.GF": "圭亞那", - "assets.countries.GG": "格恩西群島", - "assets.countries.GH": "迦納", - "assets.countries.GI": "格林蘭島", - "assets.countries.GL": "格陵蘭", - "assets.countries.GM": "甘比亞", - "assets.countries.GN": "幾內亞", - "assets.countries.GP": "法屬哥德普洛島", - "assets.countries.GQ": "赤道幾內亞", - "assets.countries.GR": "希臘", - "assets.countries.GS": "南喬治亞及南桑威奇群島", - "assets.countries.GT": "關達美拉", - "assets.countries.GU": "關島", - "assets.countries.GW": "幾內亞比索", - "assets.countries.GY": "蓋亞那", - "assets.countries.HK": "香港", - "assets.countries.HM": "賀德麥唐納島", - "assets.countries.HN": "宏都拉斯", - "assets.countries.HR": "克羅埃西亞", - "assets.countries.HT": "海地", - "assets.countries.HU": "匈牙利", - "assets.countries.ID": "印尼", - "assets.countries.IE": "愛爾蘭", - "assets.countries.IL": "以色列", - "assets.countries.IM": "曼島", - "assets.countries.IN": "印度", - "assets.countries.IO": "英屬印度洋領地", - "assets.countries.IQ": "伊拉克", - "assets.countries.IR": "伊朗", - "assets.countries.IS": "冰島", - "assets.countries.IT": "義大利", - "assets.countries.JE": "澤西", - "assets.countries.JM": "牙買加", - "assets.countries.JO": "約旦", - "assets.countries.JP": "日本", - "assets.countries.KE": "肯亞", - "assets.countries.KG": "吉爾吉斯坦", - "assets.countries.KH": "柬埔寨", - "assets.countries.KI": "吉里巴斯", - "assets.countries.KM": "科摩洛", - "assets.countries.KN": "聖基茨和尼維斯", - "assets.countries.KP": "北韓", - "assets.countries.KR": "南韓", - "assets.countries.KW": "科威特", - "assets.countries.KY": "開曼群島", - "assets.countries.KZ": "哈薩克", - "assets.countries.LA": "寮國", - "assets.countries.LB": "黎巴嫩", - "assets.countries.LC": "聖路西亞", - "assets.countries.LI": "列支敦斯登", - "assets.countries.LK": "斯里蘭卡", - "assets.countries.LR": "賴比瑞亞", - "assets.countries.LS": "賴索托", - "assets.countries.LT": "立陶宛", - "assets.countries.LU": "盧森堡", - "assets.countries.LV": "拉脫維亞", - "assets.countries.LY": "利比亞", - "assets.countries.MA": "摩洛哥", - "assets.countries.MC": "摩納哥", - "assets.countries.MD": "摩爾達維亞共和國", - "assets.countries.ME": "蒙特內哥羅", - "assets.countries.MF": "聖馬丁(法國領地)", - "assets.countries.MG": "馬達加斯加", - "assets.countries.MH": "馬歇爾群島", - "assets.countries.MK": "馬其頓,前南斯拉夫共和國", - "assets.countries.ML": "馬利", - "assets.countries.MM": "緬甸", - "assets.countries.MN": "蒙古", - "assets.countries.MO": "澳門", - "assets.countries.MP": "北馬里亞納群島", - "assets.countries.MQ": "馬提尼克島", - "assets.countries.MR": "茅利塔尼亞", - "assets.countries.MS": "蒙的塞拉特", - "assets.countries.MT": "馬爾它", - "assets.countries.MU": "模里西斯", - "assets.countries.MV": "馬爾地夫", - "assets.countries.MW": "馬拉威", - "assets.countries.MX": "墨西哥", - "assets.countries.MY": "馬來西亞", - "assets.countries.MZ": "莫三比克", - "assets.countries.NA": "那米比亞", - "assets.countries.NC": "新喀里多尼亞", - "assets.countries.NE": "尼日", - "assets.countries.NF": "諾福克群島", - "assets.countries.NG": "奈及利亞", - "assets.countries.NI": "尼加拉瓜", - "assets.countries.NL": "荷蘭", - "assets.countries.NO": "挪威", - "assets.countries.NP": "尼泊爾", - "assets.countries.NR": "諾魯", - "assets.countries.NU": "尼烏埃島", - "assets.countries.NZ": "紐西蘭", - "assets.countries.OM": "阿曼", - "assets.countries.PA": "巴拿馬", - "assets.countries.PE": "祕魯", - "assets.countries.PF": "法屬玻里尼西亞", - "assets.countries.PG": "巴布亞新幾內亞", - "assets.countries.PH": "菲律賓", - "assets.countries.PK": "巴基斯坦", - "assets.countries.PL": "波蘭", - "assets.countries.PM": "聖皮埃爾和密克隆群島", - "assets.countries.PN": "皮特凱恩群島", - "assets.countries.PR": "波多黎各", - "assets.countries.PS": "巴勒斯坦", - "assets.countries.PT": "葡萄牙", - "assets.countries.PW": "帛琉", - "assets.countries.PY": "巴拉圭", - "assets.countries.QA": "卡達", - "assets.countries.RE": "留尼旺島", - "assets.countries.RO": "羅馬尼亞", - "assets.countries.RS": "塞爾維亞", - "assets.countries.RU": "俄羅斯聯邦", - "assets.countries.RW": "盧安達", - "assets.countries.SA": "沙烏地阿拉伯", - "assets.countries.SB": "索羅門群島", - "assets.countries.SC": "塞席爾", - "assets.countries.SD": "蘇丹", - "assets.countries.SE": "瑞典", - "assets.countries.SG": "新加坡", - "assets.countries.SH": "聖海倫那", - "assets.countries.SI": "斯洛維尼亞", - "assets.countries.SJ": "斯瓦巴及尖棉", - "assets.countries.SK": "斯洛伐克", - "assets.countries.SL": "獅子山", - "assets.countries.SM": "聖馬利諾", - "assets.countries.SN": "塞內加爾", - "assets.countries.SO": "索馬利亞", - "assets.countries.SR": "蘇利南", - "assets.countries.SS": "南蘇丹", - "assets.countries.ST": "聖多美與普林西比", - "assets.countries.SV": "薩爾瓦多", - "assets.countries.SX": "聖馬丁島(荷蘭的部分)", - "assets.countries.SY": "敘利亞", - "assets.countries.SZ": "史瓦濟蘭", - "assets.countries.TC": "特克斯和開斯科群島", - "assets.countries.TD": "查德", - "assets.countries.TF": "法屬南部屬地", - "assets.countries.TG": "東哥", - "assets.countries.TH": "泰國", - "assets.countries.TJ": "塔吉克", - "assets.countries.TK": "拖克勞群", - "assets.countries.TL": "東帝汶", - "assets.countries.TM": "土庫曼", - "assets.countries.TN": "突尼西亞", - "assets.countries.TO": "東加", - "assets.countries.TR": "土耳其", - "assets.countries.TT": "千里達和托貝哥", - "assets.countries.TV": "吐瓦魯", - "assets.countries.TW": "台灣", - "assets.countries.TZ": "坦尚尼亞", - "assets.countries.UA": "烏克蘭", - "assets.countries.UG": "烏干達", - "assets.countries.UM": "美屬邊疆群島", - "assets.countries.US": "美國", - "assets.countries.UY": "烏拉圭", - "assets.countries.UZ": "烏茲別克", - "assets.countries.VA": "教廷(梵蒂岡城國)", - "assets.countries.VC": "聖文森特和格林納丁斯", - "assets.countries.VE": "委內瑞拉, 玻利瓦爾共和國", - "assets.countries.VG": "英屬維京群島", - "assets.countries.VI": "美屬維京群島", - "assets.countries.VN": "越南", - "assets.countries.VU": "萬那杜", - "assets.countries.WF": "沃里斯與伏塔那島", - "assets.countries.WS": "薩摩亞", - "assets.countries.YE": "葉門", - "assets.countries.YT": "馬約特", - "assets.countries.ZA": "南非", - "assets.countries.ZM": "尚比亞", - "assets.countries.ZW": "辛巴威", - "assets.mimetypes.application/epub_zip": "EPUB 電子書", - "assets.mimetypes.application/msword": "Word 文件", - "assets.mimetypes.application/pdf": "PDF 文件", - "assets.mimetypes.application/vnd.moodle.backup": "Moodle 備份", - "assets.mimetypes.application/vnd.ms-excel": "Excel 試算表", - "assets.mimetypes.application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 啟用巨集的工作簿", - "assets.mimetypes.application/vnd.ms-powerpoint": "Powerpoint 簡報檔", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet": "OpenDocument 試算表", - "assets.mimetypes.application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument 試算表樣版", - "assets.mimetypes.application/vnd.oasis.opendocument.text": "OpenDocument文本文件", - "assets.mimetypes.application/vnd.oasis.opendocument.text-template": "OpenDocument 文本樣版", - "assets.mimetypes.application/vnd.oasis.opendocument.text-web": "OpenDocument 網頁樣版", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 簡報", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 幻燈秀", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 試算表", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 樣板", - "assets.mimetypes.application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 文件", - "assets.mimetypes.application/x-iwork-keynote-sffkey": "iWork Keynote 簡報", - "assets.mimetypes.application/x-iwork-numbers-sffnumbers": "iWork Numbers 試算表", - "assets.mimetypes.application/x-iwork-pages-sffpages": "iWork Pages 文件", - "assets.mimetypes.application/x-javascript": "JavaScript 原始碼", - "assets.mimetypes.application/x-mspublisher": "Publisher 文件", - "assets.mimetypes.application/x-shockwave-flash": "Flash 動畫", - "assets.mimetypes.application/xhtml_xml": "XHTML 文件", - "assets.mimetypes.archive": "檔案庫({{$a.EXT}})", - "assets.mimetypes.audio": "聲音檔 ({{$a.EXT}})", - "assets.mimetypes.default": "{{$a.mimetype}}", - "assets.mimetypes.document/unknown": "檔案", - "assets.mimetypes.group:archive": "歸類檔案", - "assets.mimetypes.group:audio": "聲音檔案", - "assets.mimetypes.group:document": "文件檔案", - "assets.mimetypes.group:html_audio": "瀏覽器預設會支援的聲音檔", - "assets.mimetypes.group:html_track": "HTML追蹤檔案", - "assets.mimetypes.group:html_video": "瀏覽器預設會支援的影片檔", - "assets.mimetypes.group:image": "圖像檔案", - "assets.mimetypes.group:presentation": "簡報檔案", - "assets.mimetypes.group:sourcecode": "資源程式碼", - "assets.mimetypes.group:spreadsheet": "試算表檔案", - "assets.mimetypes.group:video": "影片檔", - "assets.mimetypes.group:web_audio": "用於這網站的影片檔", - "assets.mimetypes.group:web_file": "網頁檔案", - "assets.mimetypes.group:web_image": "用於網頁的圖像檔", - "assets.mimetypes.group:web_video": "用於網頁的影片檔案", - "assets.mimetypes.image": "圖像({{$a.MIMETYPE2}})", - "assets.mimetypes.image/vnd.microsoft.icon": "視窗圖示", - "assets.mimetypes.text/css": "級聯樣式表", - "assets.mimetypes.text/csv": "以逗號隔開的數值", - "assets.mimetypes.text/html": "HTML 文件", - "assets.mimetypes.text/plain": "純文字檔", - "assets.mimetypes.text/rtf": "RTF文件", - "assets.mimetypes.text/vtt": "網頁影片文字軌", - "assets.mimetypes.video": "影片檔案({{$a.EXT}})", - "core.accounts": "帳戶", - "core.add": "新增", - "core.agelocationverification": "驗證年齡與所在位置", - "core.ago": "{{$a}}之前", - "core.all": "所有", - "core.allgroups": "所有群組", - "core.allparticipants": "所有參與者", - "core.answer": "回答", - "core.answered": "已經回答", - "core.areyousure": "你確定嗎?", - "core.back": "返回", - "core.block.blocks": "區塊", - "core.cancel": "取消", - "core.cannotconnect": "無法連接:請確認您輸入之網址正確,且您的位置使用的是Moodle{{$a}}版或更新版本。", - "core.cannotdownloadfiles": "在您的行動服務中禁用檔案下載. 請與您的網站管理員聯繫.", - "core.captureaudio": "錄音", - "core.capturedimage": "拍照", - "core.captureimage": "拍照", - "core.capturevideo": "錄影", - "core.category": "類別", - "core.choose": "選擇", - "core.choosedots": "選擇...", - "core.clearsearch": "清除搜尋", - "core.clicktohideshow": "點按展開或縮合", - "core.clicktoseefull": "點擊以看到詳細內容", - "core.close": "關閉", - "core.comments": "評論", - "core.comments.addcomment": "給予評論...", - "core.comments.comments": "評論", - "core.comments.commentscount": "評論數({{$a}})", - "core.comments.deletecommentbyon": "刪除{{$a.user}}在 {{$a.time}}所貼的評論", - "core.comments.eventcommentcreated": "已建立評論", - "core.comments.eventcommentdeleted": "已刪除評論", - "core.comments.nocomments": "沒有評論", - "core.comments.savecomment": "儲存評論", - "core.commentscount": "評論數({{$a}})", - "core.completion-alt-auto-fail": "已完成:{{$a}} (不及格)", - "core.completion-alt-auto-n": "尚未完成:{{$a}}", - "core.completion-alt-auto-pass": "已經完成:{{$a}} (及格)", - "core.completion-alt-auto-y": "已經完成:{{$a}}", - "core.completion-alt-manual-n": "未完成:{{$a}} 。點選標記為完成。", - "core.completion-alt-manual-y": "已完成:{{$a}} 。點選標記為未完成。", - "core.confirmcanceledit": "您確定要離開此頁面嗎? 所有的修改將會遺失.", - "core.confirmdeletefile": "你確定要刪除這一檔案?", - "core.confirmopeninbrowser": "你想要在瀏覽器內開啟嗎?", - "core.considereddigitalminor": "你太年輕了,不能在這個網站上創建帳戶。", - "core.content": "內容", - "core.contenteditingsynced": "您正在編輯的內容已同步.", - "core.contentlinks.chooseaccount": "選擇帳號", - "core.contentlinks.chooseaccounttoopenlink": "選擇一個帳號以開啟連結", - "core.contentlinks.confirmurlothersite": "這個連結屬於其他的網站. 你確定要開啟嗎?", - "core.contentlinks.errornoactions": "找不到要使用此連結執行的操作", - "core.contentlinks.errornosites": "找不到任何網站來處理此連結", - "core.continue": "繼續", - "core.course": "課程", - "core.course.activitynotyetviewableremoteaddon": "您的組織安裝了尚未支援的外掛", - "core.course.activitynotyetviewablesiteupgradeneeded": "您的組織的Moodle安裝需要更新.", - "core.course.allsections": "全部", - "core.course.askadmintosupport": "請與網站管理員聯繫, 告知他們您要使用Moodle Mobile應用程式使用此活動.", - "core.course.confirmdeletemodulefiles": "您確定要刪除此模組檔案嗎?", - "core.course.confirmdownload": "您即將下載{{size}}.{{availableSpace}}, 您確定要繼續嗎?", - "core.course.confirmdownloadunknownsize": "無法計算下載大小{{availableSpace}},您確定要繼續下載嗎?", - "core.course.confirmdownloadzerosize": "您即將開始下載{{availableSpace}},您確定要繼續嗎?", - "core.course.confirmpartialdownloadsize": "您即將下載至少 {{size}}.{{availableSpace}} ,您確定您要繼續嗎?", - "core.course.contents": "內容", - "core.course.couldnotloadsectioncontent": "無法載入區塊內容, 請稍後再試.", - "core.course.couldnotloadsections": "無法載入區塊, 請稍後再試.", - "core.course.coursesummary": "課程摘要", - "core.course.errordownloadingsection": "載入區塊時發生錯誤", - "core.course.errorgetmodule": "取得模組資料時出現錯誤", - "core.course.hiddenfromstudents": "對學生隱藏", - "core.course.hiddenoncoursepage": "可以使用,但沒有顯示在課程頁面上", - "core.course.insufficientavailablequota": "您的裝置無法分配空間來儲存此下載檔案。 可能是應用程序和系統更新保留了空間。 請先清除出一些儲存空間。", - "core.course.insufficientavailablespace": "您正在嘗試下載{{size}}。 這將使您的設備沒有足夠的空間來正常運行。 請先清除一些儲存空間。", - "core.course.nocontentavailable": "目前無內容可使用", - "core.course.overriddennotice": "您在這活動的最後成績是手動調整的。", - "core.course.sections": "單元", - "core.course.useactivityonbrowser": "你可以以瀏覽器一直使用", - "core.coursedetails": "課程細節", - "core.courses.addtofavourites": "將課程標記星號", - "core.courses.allowguests": "本課程允許無帳號者進入", - "core.courses.availablecourses": "可使用的課程", - "core.courses.categories": "課程類別", - "core.courses.confirmselfenrol": "您確定要註冊本課程嗎?", - "core.courses.courses": "課程", - "core.courses.downloadcourses": "下載課程", - "core.courses.enrolme": "註冊選課", - "core.courses.errorloadcourses": "載入課程時出錯.", - "core.courses.errorsearching": "搜尋時發生錯誤.", - "core.courses.errorselfenrol": "自行註冊時出現錯誤.", - "core.courses.filtermycourses": "過濾我的課程", - "core.courses.frontpage": "首頁", - "core.courses.hidecourse": "隱藏不檢視", - "core.courses.ignore": "忽略", - "core.courses.mycourses": "我的課程", - "core.courses.mymoodle": "儀表板", - "core.courses.nocourses": "沒有課程資訊可以顯示", - "core.courses.nocoursesyet": "此類別中無課程", - "core.courses.nosearchresults": "沒有結果", - "core.courses.notenroled": "您沒有加入此課程", - "core.courses.notenrollable": "您無法註冊本課程.", - "core.courses.password": "選課密碼", - "core.courses.paymentrequired": "這個課程需要付費才能進入。", - "core.courses.paypalaccepted": "PayPal 付款成功", - "core.courses.reload": "重新載入", - "core.courses.removefromfavourites": "取消課程星號", - "core.courses.search": "搜尋", - "core.courses.searchcourses": "搜尋課程", - "core.courses.searchcoursesadvice": "您可以使用搜尋課程按鈕以訪客身份登入, 或在允許它的課程中註冊", - "core.courses.selfenrolment": "自行選課", - "core.courses.sendpaymentbutton": "使用Paypal送出款項", - "core.courses.show": "顯示這個課程", - "core.courses.totalcoursesearchresults": "課程總數: {{$a}}", - "core.currentdevice": "目前的裝置", - "core.datastoredoffline": "儲存在設備中的資料,因為它之前無法發送. 它稍後將自動發送.", - "core.date": "日期", - "core.day": "日", - "core.days": "日", - "core.decsep": ".", - "core.defaultvalue": "預設({{$a}})", - "core.delete": "刪除", - "core.deleteduser": "已刪除的用戶", - "core.deleting": "刪除中", - "core.description": "說明", - "core.dfdaymonthyear": "YYYY-MM-DD", - "core.dfdayweekmonth": "ddd, D MMM", - "core.dflastweekdate": "ddd", - "core.dfmediumdate": "LLL", - "core.dftimedate": "h[:]mm A", - "core.digitalminor": "數位兒童", - "core.digitalminor_desc": "要在這網站上建立一個帳號,請你父母或監護人聯絡以下的人", - "core.discard": "捨棄", - "core.dismiss": "除去", - "core.displayoptions": "顯示的選項", - "core.done": "完成", - "core.download": "下載", - "core.downloaded": "已下載的", - "core.downloading": "下載中", - "core.edit": "編修", - "core.editor.autosavesucceeded": "草稿已存", - "core.editor.bold": "粗體字", - "core.editor.clear": "清除格式", - "core.editor.h3": "標題一(大)", - "core.editor.h4": "標題二(中)", - "core.editor.h5": "標題三(小)", - "core.editor.italic": "斜體字", - "core.editor.orderedlist": "排序的清單", - "core.editor.p": "段落", - "core.editor.strike": "刪除線", - "core.editor.textrecovered": "此項文字的草稿已自動儲存。", - "core.editor.underline": "加底線", - "core.editor.unorderedlist": "無排序清單", - "core.error": "錯誤", - "core.errorchangecompletion": "更改完成狀態時發生錯誤. 請再試一次.", - "core.errordeletefile": "刪除檔案時出錯. 請再試一次.", - "core.errordownloading": "下載檔案時發生錯誤", - "core.errordownloadingsomefiles": "下載檔案時出錯,有些檔案可能會遺失。", - "core.errorfileexistssamename": "已有一個具有此名稱的檔案", - "core.errorinvalidform": "表單包含無效的資料. 請確認填寫所有必填欄位, 並確認資料有效.", - "core.errorinvalidresponse": "接收的回應無效. 如果錯誤仍然存在, 請與您的Moodle網站管理員聯繫.", - "core.erroropenfilenoapp": "打開檔案時發生錯誤: 找不到應用程式打開此類檔案.", - "core.erroropenfilenoextension": "打開檔案時發生錯誤: 檔案沒有副檔名.", - "core.erroropenpopup": "此活動正在嘗試打開一個彈出視窗. 此應用程式不支援這項功能.", - "core.errorrenamefile": "重新命名檔案時發生錯誤. 請再試一次.", - "core.errorsync": "同步時出錯. 請再試一次.", - "core.errorsyncblocked": "由於程序正在進行, 此{{$ a}}目前無法同步. 請稍後再試. 如果問題仍然存在, 請嘗試重新啟動應用程式.", - "core.explanationdigitalminor": "需要此資訊來確定您的年齡是否超過同意的數字年齡。這是個人可以同意條款和條件以及合法儲存和處理其數據的年齡。", - "core.favourites": "星號標記", - "core.filename": "檔案名稱", - "core.filenameexist": "檔案名已存在: {{$ a}}", - "core.filenotfound": "找不到檔案,抱歉。", - "core.fileuploader.addfiletext": "添加檔案", - "core.fileuploader.audio": "聲音", - "core.fileuploader.camera": "相機", - "core.fileuploader.confirmuploadfile": "你已上傳 {{size}}. 你確定想繼續嗎?", - "core.fileuploader.confirmuploadunknownsize": "我們無法去計算上傳檔案的容量.你確定想繼續嗎?", - "core.fileuploader.errorcapturingaudio": "錄製聲音時發生錯誤", - "core.fileuploader.errorcapturingimage": "擷取圖片時發生錯誤.", - "core.fileuploader.errorcapturingvideo": "錄製影片時發生錯誤", - "core.fileuploader.errorgettingimagealbum": "從相簿裡取得圖片發生錯誤.", - "core.fileuploader.errormustbeonlinetoupload": "您必須上線才能上傳檔案.", - "core.fileuploader.errornoapp": "您沒有安裝應用程式來執行此動作.", - "core.fileuploader.errorreadingfile": "讀取檔案發生錯誤.", - "core.fileuploader.errorwhileuploading": "檔案上傳時發生錯誤.", - "core.fileuploader.file": "檔案", - "core.fileuploader.filesofthesetypes": "可接受的檔案類型:", - "core.fileuploader.fileuploaded": "檔案已上傳", - "core.fileuploader.invalidfiletype": "{{$a}}檔案類型無法被接受", - "core.fileuploader.maxbytesfile": "這個檔案大小 {{$a.file}} 超過限制. 最大檔案大小只能上傳 {{$a.size}}.", - "core.fileuploader.more": "更多", - "core.fileuploader.photoalbums": "相簿", - "core.fileuploader.readingfile": "讀取檔案中", - "core.fileuploader.selectafile": "選擇一個檔案", - "core.fileuploader.uploadafile": "上傳一個檔案", - "core.fileuploader.uploading": "上傳中", - "core.fileuploader.uploadingperc": "上傳中: {{$a}}%", - "core.fileuploader.video": "影片", - "core.filter": "過濾器", - "core.folder": "資料夾", - "core.forcepasswordchangenotice": "您必須修改您的密碼才能繼續進行。", - "core.fulllistofcourses": "所有課程", - "core.fullnameandsitename": "{{fullname}} ({{sitename}})", - "core.grades.average": "平均", - "core.grades.badgrade": "提供的成績是無效的", - "core.grades.contributiontocoursetotal": "貢獻到課程總分", - "core.grades.feedback": "回饋", - "core.grades.grade": "成績", - "core.grades.gradeitem": "評分項目", - "core.grades.grades": "成績", - "core.grades.lettergrade": "文字等第", - "core.grades.nogradesreturned": "未找到成績", - "core.grades.nooutcome": "沒有能力指標", - "core.grades.percentage": "百分比", - "core.grades.range": "全距", - "core.grades.rank": "排名", - "core.grades.weight": "加權量", - "core.group": "分組", - "core.groupsseparate": "分隔群組", - "core.groupsvisible": "可視群組", - "core.h5p.author": "作者", - "core.h5p.authorcomments": "作者評論", - "core.h5p.authorname": "作者姓名", - "core.h5p.authorrole": "作者角色", - "core.h5p.by": "由", - "core.h5p.cancellabel": "取消", - "core.h5p.close": "關閉", - "core.h5p.confirmdialogheader": "確認的動作", - "core.h5p.confirmlabel": "確認", - "core.h5p.contenttype": "內容類型", - "core.h5p.copyrightinfo": "版權資訊", - "core.h5p.copyrightstring": "版權", - "core.h5p.date": "日期", - "core.h5p.disablefullscreen": "取消全螢幕", - "core.h5p.download": "下載", - "core.h5p.downloadtitle": "以H5P檔案格式下載這內容。", - "core.h5p.editor": "編輯器", - "core.h5p.embed": "嵌入", - "core.h5p.embedtitle": "檢視這內容的嵌入碼。", - "core.h5p.fullscreen": "全螢幕", - "core.h5p.hideadvanced": "隱藏進階功能", - "core.h5p.license": "授權", - "core.h5p.play": "播放H5P", - "core.h5p.reuse": "取用", - "core.h5p.reuseContent": "取用內容", - "core.h5p.reuseDescription": "取用這內容", - "core.h5p.showadvanced": "顯示進階功能", - "core.h5p.source": "來源", - "core.h5p.thumbnail": "縮圖", - "core.h5p.title": "標題", - "core.h5p.year": "年", - "core.h5p.years": "年", - "core.h5p.yearsfrom": "年(起)", - "core.h5p.yearsto": "年(迄)", - "core.hasdatatosync": "此{{$ a}}有離線資料要同步.", - "core.help": "輔助說明", - "core.hide": "隱藏", - "core.hour": "小時", - "core.hours": "小時", - "core.humanreadablesize": "{{size}} {{unit}}", - "core.image": "圖片", - "core.imageviewer": "影像檢視器", - "core.info": "資訊", - "core.invalidformdata": "表單資料不正確", - "core.labelsep": ":", - "core.lastaccess": "上次訪問", - "core.lastmodified": "最後修改", - "core.lastsync": "最後同步", - "core.layoutgrid": "網格", - "core.list": "清單", - "core.listsep": ",", - "core.loading": "裝載中", - "core.loadmore": "載入更多", - "core.location": "位置", - "core.login.auth_email": "電子郵件確認", - "core.login.authenticating": "認證中", - "core.login.cancel": "取消", - "core.login.changepassword": "更改密碼", - "core.login.confirmdeletesite": "您確定要刪除網站{{sitename}}嗎?", - "core.login.connect": "連線!", - "core.login.connecttomoodle": "連上Moodle平台", - "core.login.contactyouradministrator": "連絡你的網站管理員以取得更多的協助.", - "core.login.contactyouradministratorissue": "請求管理員檢查以下問題: {{$ a}}", - "core.login.createaccount": "建立我的新帳號", - "core.login.createuserandpass": "請選擇您的帳號名稱和密碼", - "core.login.credentialsdescription": "請提供您的帳號和密碼以便登入", - "core.login.emailconfirmsent": "

                        本系統已經送出電子郵件到 {{$a}}

                        \n

                        訊息內容包含如何完成註冊手續。

                        \n

                        請閱覽您的私人郵件 按下確認連結後 便可登入本系統,若是有問題請和系統管理員連絡。

                        ", - "core.login.emailconfirmsentsuccess": "成功寄出確認電郵", - "core.login.emailnotmatch": "電子郵件內容不符", - "core.login.erroraccesscontrolalloworigin": "您嘗試執行的Cross-Origin呼叫已被拒絕. 請檢查https://docs.moodle.org/dev/Moodle_Mobile_development_using_Chrome_or_Chromium", - "core.login.errordeletesite": "刪除此網站時出錯. 請再試一次.", - "core.login.errorupdatesite": "更新網站權杖時出錯.", - "core.login.firsttime": "您第一次來訪嗎?", - "core.login.forcepasswordchangenotice": "您必須修改您的密碼才能繼續進行。", - "core.login.forgotten": "忘記帳號或密碼嗎?", - "core.login.help": "輔助說明", - "core.login.helpmelogin": "

                        要登入前請確認:

                        1. Moodle 入口網站版本要是2.4或更高
                        2. Moodle 入口網站管理員有啟用手機存取.

                        想測試Moodle示範入口網站型態須輸入teacherstudentUsername 欄位並點選Add button.

                        Moodle文件網站有更詳細的資料及協助

                        ", - "core.login.instructions": "使用說明", - "core.login.invalidaccount": "請檢查您的登人資料或請您的網站管理員檢查網站設定。", - "core.login.invaliddate": "錯誤的日期", - "core.login.invalidemail": "無效的電子郵件信箱", - "core.login.invalidmoodleversion": "", - "core.login.invalidsite": "這網站網址無效.", - "core.login.invalidtime": "無效的時間", - "core.login.invalidurl": "所指定的網址無效", - "core.login.invalidvaluemax": "最大值為 {{$a}}", - "core.login.invalidvaluemin": "最小值為 {{$a}}", - "core.login.localmobileunexpectedresponse": "Moodle行動其他功能檢查返回了無預期的回應, 您將使用標準行動服務進行身份驗證.", - "core.login.login": "登入", - "core.login.loginbutton": "登入", - "core.login.logininsiterequired": "您的Moodle網站強制您以系統瀏覽器開啟. 將開啟新瀏覽器, 並重新導向這Moodle網站.", - "core.login.loginsteps": "您好!如果您尚未申請帳號,為了能完整使用本網站課程,您需要先花一兩分鐘時間申請一個新帳號。此外,部分課程在初次選課時,還需要輸入開課教師所設定的選課密碼。\n

                        申請帳號請依下列步驟進行:

                        \n
                          \n
                        1. 請於 申請新帳號 表單中輸入個人相關資料。
                        2. \n
                        3. 一封電子郵件會立刻寄送到您的信箱中。
                        4. \n
                        5. 讀取您的電子郵件,點按信件中的網址連結。
                        6. \n
                        7. 您的帳號將得到確認,並且立刻登入本網站。
                        8. \n
                        9. 然後,請選擇您想要參加的課程。
                        10. \n
                        11. 如果系統提示要選課密鑰,請輸入教師提供給您的選課密碼,這樣才能註冊到該課程中。
                        12. \n
                        13. 從此,您只要輸入個人的帳號與密碼〈在左邊的表單中〉,就可以進入任何一個您已選修的課程中。
                        14. \n
                        ", - "core.login.missingemail": "缺少電子郵件信箱", - "core.login.missingfirstname": "缺少名字資料", - "core.login.missinglastname": "缺少姓氏資料", - "core.login.mobileservicesnotenabled": "您的網站並未啟用行動服務. 如果您想使用此功能, 請連絡您的Moodle網站管理員.", - "core.login.mustconfirm": "您必須確認一下您的帳戶", - "core.login.newaccount": "新帳號", - "core.login.notloggedin": "你必須先登入", - "core.login.password": "密碼", - "core.login.passwordforgotten": "忘記密碼了", - "core.login.passwordforgotteninstructions2": "想重設密碼,請在下面填寫您的用戶名或電子郵件l地址。如果我們找到資料庫中有關於您的記錄,我們會發送一封電子郵件到您的信箱。郵件會指引您如何重新獲得存取權限。", - "core.login.passwordrequired": "需要密碼", - "core.login.policyaccept": "我了解並且同意", - "core.login.policyagree": "您必須同意這協議才能繼續使用這個網站。您同意嗎?", - "core.login.policyagreement": "網站使用協議", - "core.login.policyagreementclick": "連結到網站使用協議", - "core.login.potentialidps": "用其他帳號登入:", - "core.login.profileinvaliddata": "無效的值", - "core.login.recaptchachallengeimage": "reCAPTCHA挑戰的圖片", - "core.login.reconnect": "重新連線", - "core.login.reconnectdescription": "您的授權權杖無效或已過期, 您必須重新連線到這個網站", - "core.login.reconnectssodescription": "您的授權權杖無效或已過期, 您必須重新連線到這個網站進行重新登入.", - "core.login.resendemail": "重發電郵", - "core.login.security_question": "安全性提問", - "core.login.selectacountry": "選擇一個國家", - "core.login.signupplugindisabled": "{{$a}}未啟用.", - "core.login.siteaddress": "您的網站位址", - "core.login.siteinmaintenance": "您的網站處於維護模式", - "core.login.siteurl": "網址", - "core.login.siteurlrequired": "需要網站網址, 例如http://www.yourmoodle.tw", - "core.login.startsignup": "申請一個新帳號", - "core.login.stillcantconnect": "一直無法連線嗎?", - "core.login.supplyinfo": "更多細節", - "core.login.username": "帳號", - "core.login.usernameoremail": "輸入帳號或信箱", - "core.login.usernamerequired": "需要帳號", - "core.login.usernotaddederror": "無法新增用戶- 未知的錯誤", - "core.login.visitchangepassword": "您需要去拜訪這個網站變更密碼嗎?", - "core.login.webservicesnotenabled": "您的網站沒有啟用Web服務。如果您想用行動裝置連線,請聯繫您的Moodle網站管理員。", - "core.lostconnection": "我們已斷了線,您需重新連線。您的口令目前是無效的。", - "core.mainmenu.changesite": "更換網站", - "core.mainmenu.help": "輔助說明", - "core.mainmenu.logout": "登出", - "core.mainmenu.website": "網站", - "core.maxsizeandattachments": "新檔案的最大容量: {{$a.size}} ,最多附件:{{$a.attachments}}", - "core.min": "分鐘", - "core.mins": "分鐘", - "core.misc": "雜項", - "core.mod_assign": "作業", - "core.mod_assignment": "作業2.2(已關閉)", - "core.mod_book": "電子書", - "core.mod_chat": "聊天室", - "core.mod_choice": "票選活動", - "core.mod_data": "資料庫", - "core.mod_database": "資料庫", - "core.mod_external-tool": "學習工具互通", - "core.mod_feedback": "回饋單", - "core.mod_file": "檔案", - "core.mod_folder": "資料夾", - "core.mod_forum": "討論區", - "core.mod_glossary": "辭彙表", - "core.mod_ims": "IMS內容包", - "core.mod_imscp": "IMS內容包", - "core.mod_label": "標籤", - "core.mod_lesson": "編序學習", - "core.mod_lti": "學習工具互通", - "core.mod_page": "頁面", - "core.mod_quiz": "測驗卷", - "core.mod_resource": "檔案", - "core.mod_scorm": "SCORM包裹", - "core.mod_survey": "問卷", - "core.mod_url": "網址", - "core.mod_wiki": "Wiki共筆", - "core.mod_workshop": "工作坊", - "core.moduleintro": "說明", - "core.more": "更多", - "core.mygroups": "我的群組", - "core.name": "名稱", - "core.networkerrormsg": "網路未啟用或未運作。", - "core.never": "從不", - "core.next": "往後", - "core.no": "否", - "core.nocomments": "沒有評論", - "core.nograde": "沒有成績", - "core.none": "無", - "core.nopasswordchangeforced": "你若沒有更新密碼無法進行此處理", - "core.nopermissions": "抱歉,但是您目前沒有權限執行({{$a}})", - "core.noresults": "沒有結果", - "core.noselection": "沒有選擇", - "core.notapplicable": "n/a", - "core.notenrolledprofile": "個人資料無法使用,因為這用戶並沒有選修這門課程。", - "core.notice": "注意", - "core.notingroup": "抱歉,您必須是群組的成員才能看到這個活動", - "core.notsent": "沒有傳送", - "core.now": "現在", - "core.numwords": "{{$a}}字數", - "core.offline": "離線", - "core.ok": "好", - "core.online": "在線上", - "core.openfullimage": "點擊這裡以圖像原尺寸顯示", - "core.openinbrowser": "以瀏覽器開啟", - "core.othergroups": "其他群組", - "core.pagea": "第 {{$a}} 頁", - "core.paymentinstant": "使用以下按鈕立即付款及註冊。", - "core.percentagenumber": "{{$a}}%", - "core.phone": "電話", - "core.pictureof": "{{$a}}的相片", - "core.previous": "向前", - "core.proceed": "繼續", - "core.pulltorefresh": "拖曳更新", - "core.question.answer": "答案", - "core.question.answersaved": "答案已儲存", - "core.question.certainty": "肯定程度", - "core.question.complete": "完成", - "core.question.correct": "答對", - "core.question.errorattachmentsnotsupported": "本應用程序尚不支援將檔案附加到答案.", - "core.question.errorinlinefilesnotsupported": "該應用程序尚不支援線上檔案的編輯.", - "core.question.errorquestionnotsupported": "該應用程式不支援此問題類型: {{$ a}}.", - "core.question.feedback": "回饋", - "core.question.howtodraganddrop": "點一下選擇, 然後再點一下刪除.", - "core.question.incorrect": "答錯", - "core.question.information": "資訊", - "core.question.invalidanswer": "不完整的答案", - "core.question.notanswered": "沒被回答", - "core.question.notyetanswered": "尚未回答", - "core.question.partiallycorrect": "部分答對", - "core.question.questionmessage": "問題 {{$a}}: {{$b}}", - "core.question.questionno": "試題{{$a}}", - "core.question.requiresgrading": "需要計分", - "core.quotausage": "您可以使用的總容量有{{$a.total}} ,目前已經用掉 {{$a.used}} 。", - "core.rating.aggregateavg": "評比平均分數", - "core.rating.aggregatecount": "評比次數", - "core.rating.aggregatemax": "最高", - "core.rating.aggregatemin": "最低", - "core.rating.aggregatesum": "累加總和", - "core.rating.noratings": "沒有評比被提交", - "core.rating.rating": "評比", - "core.rating.ratings": "評比", - "core.redirectingtosite": "您將被重定向到網站.", - "core.refresh": "更新", - "core.remove": "移除", - "core.removefiles": "移除檔案 {{$a}}", - "core.required": "必須的", - "core.requireduserdatamissing": "此使用者缺少一些必需的配置資料. 請在您的Moodle中填寫此數據, 然後重試.
                        {{$ a}}", - "core.resourcedisplayopen": "開啟", - "core.resources": "資源", - "core.restore": "還原", - "core.restricted": "受限制的", - "core.retry": "重試", - "core.save": "儲存", - "core.savechanges": "儲存更改", - "core.search": "搜尋", - "core.searching": "搜尋中", - "core.searchresults": "搜尋結果", - "core.sec": "秒", - "core.secs": "秒", - "core.seemoredetail": "按此以觀看更多細節", - "core.selectacategory": "請選擇一類別", - "core.selectacourse": "選擇一門課程", - "core.selectagroup": "選擇一個分組", - "core.send": "傳送", - "core.sending": "正在傳送", - "core.serverconnection": "連結到伺服器時發生錯誤", - "core.settings.about": "關於", - "core.settings.cannotsyncoffline": "離線狀態無法進行同步", - "core.settings.cannotsyncwithoutwifi": "無法同步, 因為目前的設定只允許在連接到Wi-Fi時同步. 請連接到Wi-Fi網絡.", - "core.settings.cordovadevicemodel": "Cordova裝置型號", - "core.settings.cordovadeviceosversion": "Cordova裝置作業系統版本", - "core.settings.cordovadeviceplatform": "Cordova裝置平臺", - "core.settings.cordovadeviceuuid": "Cordova裝置uuid", - "core.settings.cordovaversion": "Cordova版本", - "core.settings.currentlanguage": "選定語言", - "core.settings.debugdisplay": "顯示除錯訊息", - "core.settings.deletesitefiles": "您確定想從這個網站位置刪除已下載的檔案?", - "core.settings.deletesitefilestitle": "刪除網站檔案", - "core.settings.deviceinfo": "裝置資訊", - "core.settings.deviceos": "裝置作業系統", - "core.settings.disableall": "停用通知", - "core.settings.disabled": "已關閉", - "core.settings.displayformat": "顯示格式", - "core.settings.enabledownloadsection": "啟用下載區塊", - "core.settings.enablerichtexteditor": "啟用文本編輯器", - "core.settings.enablerichtexteditordescription": "如果啟用了, 文本編輯器將會顯示在允許它的地方", - "core.settings.enablesyncwifi": "只允許使用Wi-Fi進行同步", - "core.settings.errordeletesitefiles": "刪除網站檔案時發生錯誤", - "core.settings.errorsyncsite": "同步網站資料時出錯, 請檢查您的網際網路連線, 然後重試.", - "core.settings.estimatedfreespace": "估計剩餘空間", - "core.settings.filesystemroot": "檔案系統根目錄", - "core.settings.general": "一般", - "core.settings.language": "語言", - "core.settings.license": "授權條款", - "core.settings.localnotifavailable": "提供本端通知", - "core.settings.locationhref": "Webview網址", - "core.settings.locked": "被鎖住", - "core.settings.loggedin": "線上", - "core.settings.loggedoff": "離線", - "core.settings.navigatorlanguage": "導覽語言", - "core.settings.navigatoruseragent": "Navigator userAgent", - "core.settings.networkstatus": "網際網路連線狀況", - "core.settings.preferences": "偏好", - "core.settings.reportinbackground": "報表錯誤自動回報", - "core.settings.settings": "設定", - "core.settings.showdownloadoptions": "顯示下載選項", - "core.settings.sites": "網站", - "core.settings.spaceusage": "使用空間", - "core.settings.synchronization": "同步", - "core.settings.synchronizenow": "立即同步", - "core.settings.syncsettings": "同步設定", - "core.settings.total": "總共", - "core.settings.wificonnection": "WiFi連線", - "core.sharedfiles.chooseaccountstorefile": "選擇要存入檔案的帳號", - "core.sharedfiles.chooseactionrepeatedfile": "已有一個同名稱的檔案. 要替換現有檔案還是將重新命名為“{{$ a}}”?", - "core.sharedfiles.errorreceivefilenosites": "沒有已存入的網站. 請在與應用程式共享文件之前新增網站.", - "core.sharedfiles.nosharedfiles": "此網站中沒有存入共享檔案.", - "core.sharedfiles.nosharedfilestoupload": "您沒有要在此處上傳的檔案. 如果您要從其他應用程式上傳檔案, 請找出該檔案, 然後按一下[開啟]按鈕.", - "core.sharedfiles.rename": "重新命名", - "core.sharedfiles.replace": "替代", - "core.sharedfiles.sharedfiles": "分享檔案", - "core.sharedfiles.successstorefile": "檔案已成功儲存.現在您可以選擇檔案進行上傳到您的個人檔案.", - "core.show": "顯示", - "core.showless": "顯示較少的...", - "core.showmore": "顯示較多的...", - "core.site": "網站", - "core.sitehome.sitehome": "網站首頁", - "core.sitehome.sitenews": "網站公告", - "core.sitemaintenance": "這個網站目前正在維護中,暫時不能使用。", - "core.sizeb": "位元組", - "core.sizegb": "GB", - "core.sizekb": "KB", - "core.sizemb": "MB", - "core.sizetb": "TB", - "core.skip": "略過", - "core.sorry": "抱歉...", - "core.sort": "排序", - "core.sortby": "排序依據", - "core.strftimedate": "%Y年 %m月 %d日", - "core.strftimedatefullshort": "%y/%m/%d", - "core.strftimedateshort": "%m月 %d日", - "core.strftimedatetime": "%Y年%m月%d日,%H:%M", - "core.strftimedatetimeshort": "%Y/%m/%d %H:%M", - "core.strftimedaydate": "%Y年 %m月 %d日 %A", - "core.strftimedaydatetime": "%Y年 %m月 %d日(%a) %H:%M", - "core.strftimedayshort": "%m月 %d日 %A", - "core.strftimedaytime": "%a %H:%M", - "core.strftimemonthyear": "%Y年 %m月", - "core.strftimerecent": "%m月 %d日,%H:%M", - "core.strftimerecentfull": "%Y年%m月%d日(%a) %H:%M", - "core.strftimetime": "%p %I:%M", - "core.submit": "送出", - "core.success": "成功", - "core.tablet": "平板", - "core.tag.defautltagcoll": "預設的蒐藏", - "core.tag.inalltagcoll": "每個地方", - "core.tag.itemstaggedwith": "在{{$a.tagarea}} 被標籤為\"{{$a.tag}}\"", - "core.tag.noresultsfor": "沒有與 \"{{$a}}\" 相關的結果", - "core.tag.notagsfound": "沒有找到符合\"{{$a}}\"的標籤", - "core.tag.searchtags": "搜尋標籤", - "core.tag.showingfirsttags": "顯示 {{$a}} 個最普遍的標籤", - "core.tag.tag": "標籤", - "core.tag.tagarea_course": "課程", - "core.tag.tagarea_course_modules": "課程模組", - "core.tag.tagarea_post": "部落格貼文", - "core.tag.tagarea_user": "用戶興趣", - "core.tag.tags": "標籤", - "core.teachers": "教師", - "core.thereisdatatosync": "有離線的{{$ a}}要同步", - "core.thisdirection": "ltr", - "core.time": "時間", - "core.timesup": "時間到", - "core.today": "今日", - "core.tryagain": "再試一次", - "core.twoparagraphs": "{{p1}}

                        {{p2}}", - "core.uhoh": "噢哦!", - "core.unexpectederror": "無法預期的錯誤. 請關閉並重新開啟應用程式, 再試一次.", - "core.unknown": "未知", - "core.unlimited": "無限制", - "core.unzipping": "解壓縮中", - "core.upgraderunning": "網站正在升級中,請稍後再試。", - "core.user": "用戶", - "core.user.address": "地址", - "core.user.city": "縣/市", - "core.user.contact": "連絡人", - "core.user.country": "國家", - "core.user.description": "說明", - "core.user.details": "細節", - "core.user.detailsnotavailable": "這個使用者的詳細資料無法提供給您", - "core.user.editingteacher": "教師", - "core.user.email": "電子郵件信箱", - "core.user.emailagain": "電子郵件(再次確認)", - "core.user.firstname": "名字", - "core.user.interests": "興趣", - "core.user.lastname": "姓氏", - "core.user.manager": "管理員", - "core.user.newpicture": "新照片", - "core.user.noparticipants": "這一課程找不到參與者", - "core.user.participants": "成員", - "core.user.phone1": "電話", - "core.user.phone2": "手機", - "core.user.roles": "角色", - "core.user.student": "學生", - "core.user.teacher": "助理教師", - "core.user.webpage": "網頁", - "core.userdeleted": "該用戶帳號已被刪除", - "core.userdetails": "用戶的詳細資料", - "core.usernotfullysetup": "用戶沒有完全設定好", - "core.users": "用戶", - "core.view": "瀏覽", - "core.viewprofile": "瀏覽個人資料", - "core.warningofflinedatadeleted": "離線資料 {{component}} '{{name}}' 已被刪除. {{error}}", - "core.whatisyourage": "您幾歲?", - "core.wheredoyoulive": "您住在什麼地方?", - "core.whoops": "糟糕!", - "core.whyisthishappening": "為什麼會發生這種情況呢?", - "core.whyisthisrequired": "為什麼需要這個?", - "core.wsfunctionnotavailable": "行動服務功能無法使用", - "core.year": "年", - "core.years": "年", - "core.yes": "是" -} \ No newline at end of file diff --git a/src/assets/mimetoext.json b/src/assets/mimetoext.json deleted file mode 100644 index 438731f8b..000000000 --- a/src/assets/mimetoext.json +++ /dev/null @@ -1,1095 +0,0 @@ -{ -"application/acad": ["dwg"], -"application/andrew-inset": ["ez"], -"application/applixware": ["aw"], -"application/arj": ["arj"], -"application/atom+xml": ["atom"], -"application/atomcat+xml": ["atomcat"], -"application/atomsvc+xml": ["atomsvc"], -"application/base64": ["mm","mme"], -"application/binhex": ["hqx"], -"application/binhex4": ["hqx"], -"application/book": ["boo","book"], -"application/ccxml+xml": ["ccxml"], -"application/cdf": ["cdf"], -"application/cdmi-capability": ["cdmia"], -"application/cdmi-container": ["cdmic"], -"application/cdmi-domain": ["cdmid"], -"application/cdmi-object": ["cdmio"], -"application/cdmi-queue": ["cdmiq"], -"application/clariscad": ["ccad"], -"application/commonground": ["dp"], -"application/cu-seeme": ["cu"], -"application/dash+xml": ["mpd"], -"application/davmount+xml": ["davmount"], -"application/docbook+xml": ["dbk"], -"application/drafting": ["drw"], -"application/dsptype": ["tsp"], -"application/dssc+der": ["dssc"], -"application/dssc+xml": ["xdssc"], -"application/dxf": ["dxf"], -"application/ecmascript": ["ecma","js"], -"application/emma+xml": ["emma"], -"application/envoy": ["evy"], -"application/epub+zip": ["epub"], -"application/excel": ["xl","xlw","xlb","xlc","xld","xlk","xla","xlm","xls","xlt","xlv","xll"], -"application/exi": ["exi"], -"application/font-tdpfr": ["pfr"], -"application/font-woff": ["woff"], -"application/fractals": ["fif"], -"application/freeloader": ["frl"], -"application/futuresplash": ["spl"], -"application/g-zip": ["tgz","gz","gzip"], -"application/gml+xml": ["gml"], -"application/gnutar": ["tgz"], -"application/gpx+xml": ["gpx"], -"application/groupwise": ["vew"], -"application/gxf": ["gxf"], -"application/hlp": ["hlp"], -"application/hta": ["hta"], -"application/hyperstudio": ["stk"], -"application/i-deas": ["unv"], -"application/iges": ["iges","igs"], -"application/inf": ["inf"], -"application/inkml+xml": ["ink","inkml"], -"application/inspiration": ["isf"], -"application/inspiration.template": ["ist"], -"application/ipfix": ["ipfix"], -"application/java": ["class"], -"application/java-archive": ["jar"], -"application/java-byte-code": ["class"], -"application/java-serialized-object": ["ser"], -"application/java-vm": ["class"], -"application/javascript": ["js"], -"application/json": ["json"], -"application/jsonml+json": ["jsonml"], -"application/lha": ["lha"], -"application/lost+xml": ["lostxml"], -"application/lzx": ["lzx"], -"application/mac-binary": ["bin"], -"application/mac-binhex": ["hqx"], -"application/mac-binhex40": ["hqx"], -"application/mac-compactpro": ["cpt"], -"application/macbinary": ["bin"], -"application/mads+xml": ["mads"], -"application/maple": ["mw","mws"], -"application/marc": ["mrc"], -"application/marcxml+xml": ["mrcx"], -"application/mathematica": ["ma","mb","nb"], -"application/mathml+xml": ["mathml"], -"application/mbedlet": ["mbd"], -"application/mbox": ["mbox"], -"application/mcad": ["mcd"], -"application/mediaservercontrol+xml": ["mscml"], -"application/metalink+xml": ["metalink"], -"application/metalink4+xml": ["meta4"], -"application/mets+xml": ["mets"], -"application/mime": ["aps"], -"application/mods+xml": ["mods"], -"application/mp21": ["mp21","m21"], -"application/mp4": ["mp4s"], -"application/msaccess": ["accdb"], -"application/mspowerpoint": ["pot","pps","ppt","ppz"], -"application/msword": ["doc","dot","w6w","wiz","word"], -"application/mswrite": ["wri"], -"application/mxf": ["mxf"], -"application/netmc": ["mcp"], -"application/octet-stream": ["bin","a","arj","asax","zoo","bpk","com","crx","deploy","dist","distz","dmg","dms","dump","elc","exe","fdk","arc","ipa","lha","lhx","lrf","lzh","lzx","mar","o","phar","pkg","psd","safariextz","saveme","so","uu","hta"], -"application/oda": ["oda"], -"application/oebps-package+xml": ["opf"], -"application/ogg": ["ogx"], -"application/omdoc+xml": ["omdoc"], -"application/onenote": ["onepkg","onetmp","onetoc","onetoc2"], -"application/oxps": ["oxps"], -"application/patch-ops-error+xml": ["xer"], -"application/pdf": ["pdf","fdf","xdp","xfd","xfdf"], -"application/pgp-encrypted": ["pgp"], -"application/pgp-signature": ["asc","sig"], -"application/pics-rules": ["prf"], -"application/pkcs-12": ["p12"], -"application/pkcs-crl": ["crl"], -"application/pkcs10": ["p10","csr"], -"application/pkcs7-mime": ["p7c","p7m"], -"application/pkcs7-signature": ["p7s"], -"application/pkcs8": ["p8","key"], -"application/pkix-attr-cert": ["ac"], -"application/pkix-cert": ["cer","crt"], -"application/pkix-crl": ["crl"], -"application/pkix-pkipath": ["pkipath"], -"application/pkixcmp": ["pki"], -"application/plain": ["text"], -"application/pls+xml": ["pls"], -"application/postscript": ["ps","ai","eps"], -"application/powerpoint": ["ppt"], -"application/pro_eng": ["part","prt"], -"application/prs.cww": ["cww"], -"application/pskc+xml": ["pskcxml"], -"application/rdf+xml": ["rdf"], -"application/reginfo+xml": ["rif"], -"application/relax-ng-compact-syntax": ["rnc"], -"application/resource-lists+xml": ["rl"], -"application/resource-lists-diff+xml": ["rld"], -"application/ringing-tones": ["rng"], -"application/rls-services+xml": ["rs"], -"application/rpki-ghostbusters": ["gbr"], -"application/rpki-manifest": ["mft"], -"application/rpki-roa": ["roa"], -"application/rsd+xml": ["rsd"], -"application/rss+xml": ["rss"], -"application/rtf": ["rtf","rtx"], -"application/sbml+xml": ["sbml"], -"application/scvp-cv-request": ["scq"], -"application/scvp-cv-response": ["scs"], -"application/scvp-vp-request": ["spq"], -"application/scvp-vp-response": ["spp"], -"application/sdp": ["sdp"], -"application/sea": ["sea"], -"application/set": ["set"], -"application/set-payment-initiation": ["setpay"], -"application/set-registration-initiation": ["setreg"], -"application/shf+xml": ["shf"], -"application/sla": ["stl"], -"application/smil": ["smil","smi"], -"application/smil+xml": ["smi","smil"], -"application/solids": ["sol"], -"application/sounder": ["sdr"], -"application/sparql-query": ["rq"], -"application/sparql-results+xml": ["srx"], -"application/srgs": ["gram"], -"application/srgs+xml": ["grxml"], -"application/sru+xml": ["sru"], -"application/ssdl+xml": ["ssdl"], -"application/ssml+xml": ["ssml"], -"application/step": ["step","stp"], -"application/streamingmedia": ["ssm"], -"application/tei+xml": ["tei","teicorpus"], -"application/thraud+xml": ["tfi"], -"application/timestamped-data": ["tsd"], -"application/toolbook": ["tbk"], -"application/vda": ["vda"], -"application/vnd.3gpp.pic-bw-large": ["plb"], -"application/vnd.3gpp.pic-bw-small": ["psb"], -"application/vnd.3gpp.pic-bw-var": ["pvb"], -"application/vnd.3gpp2.tcap": ["tcap"], -"application/vnd.3m.post-it-notes": ["pwn"], -"application/vnd.accpac.simply.aso": ["aso"], -"application/vnd.accpac.simply.imp": ["imp"], -"application/vnd.acucobol": ["acu"], -"application/vnd.acucorp": ["atc","acutc"], -"application/vnd.adobe.air-application-installer-package+zip": ["air"], -"application/vnd.adobe.formscentral.fcdt": ["fcdt"], -"application/vnd.adobe.fxp": ["fxp","fxpl"], -"application/vnd.adobe.xdp+xml": ["xdp"], -"application/vnd.adobe.xfdf": ["xfdf"], -"application/vnd.ahead.space": ["ahead"], -"application/vnd.airzip.filesecure.azf": ["azf"], -"application/vnd.airzip.filesecure.azs": ["azs"], -"application/vnd.amazon.ebook": ["azw"], -"application/vnd.americandynamics.acc": ["acc"], -"application/vnd.amiga.ami": ["ami"], -"application/vnd.android.package-archive": ["apk"], -"application/vnd.anser-web-certificate-issue-initiation": ["cii"], -"application/vnd.anser-web-funds-transfer-initiation": ["fti"], -"application/vnd.antix.game-component": ["atx"], -"application/vnd.apple.installer+xml": ["mpkg"], -"application/vnd.apple.mpegurl": ["m3u8"], -"application/vnd.aristanetworks.swi": ["swi"], -"application/vnd.astraea-software.iota": ["iota"], -"application/vnd.audiograph": ["aep"], -"application/vnd.blueice.multipass": ["mpm"], -"application/vnd.bmi": ["bmi"], -"application/vnd.businessobjects": ["rep"], -"application/vnd.chemdraw+xml": ["cdxml"], -"application/vnd.chipnuts.karaoke-mmd": ["mmd"], -"application/vnd.cinderella": ["cdy"], -"application/vnd.claymore": ["cla"], -"application/vnd.cloanto.rp9": ["rp9"], -"application/vnd.clonk.c4group": ["c4d","c4f","c4g","c4p","c4u"], -"application/vnd.cluetrust.cartomobile-config": ["c11amc"], -"application/vnd.cluetrust.cartomobile-config-pkg": ["c11amz"], -"application/vnd.commonspace": ["csp"], -"application/vnd.contact.cmsg": ["cdbcmsg"], -"application/vnd.cosmocaller": ["cmc"], -"application/vnd.crick.clicker": ["clkx"], -"application/vnd.crick.clicker.keyboard": ["clkk"], -"application/vnd.crick.clicker.palette": ["clkp"], -"application/vnd.crick.clicker.template": ["clkt"], -"application/vnd.crick.clicker.wordbank": ["clkw"], -"application/vnd.criticaltools.wbs+xml": ["wbs"], -"application/vnd.ctc-posml": ["pml"], -"application/vnd.cups-ppd": ["ppd"], -"application/vnd.curl.car": ["car"], -"application/vnd.curl.pcurl": ["pcurl"], -"application/vnd.dart": ["dart"], -"application/vnd.data-vision.rdz": ["rdz"], -"application/vnd.dece.data": ["uvd","uvf","uvvd","uvvf"], -"application/vnd.dece.ttml+xml": ["uvt","uvvt"], -"application/vnd.dece.unspecified": ["uvvx","uvx"], -"application/vnd.dece.zip": ["uvvz","uvz"], -"application/vnd.denovo.fcselayout-link": ["fe_launch"], -"application/vnd.dna": ["dna"], -"application/vnd.dolby.mlp": ["mlp"], -"application/vnd.dpgraph": ["dpg"], -"application/vnd.dreamfactory": ["dfac"], -"application/vnd.ds-keypoint": ["kpxx"], -"application/vnd.dvb.ait": ["ait"], -"application/vnd.dvb.service": ["svc"], -"application/vnd.dynageo": ["geo"], -"application/vnd.ecowin.chart": ["mag"], -"application/vnd.enliven": ["nml"], -"application/vnd.epson.esf": ["esf"], -"application/vnd.epson.msf": ["msf"], -"application/vnd.epson.quickanime": ["qam"], -"application/vnd.epson.salt": ["slt"], -"application/vnd.epson.ssf": ["ssf"], -"application/vnd.eszigno3+xml": ["es3","et3"], -"application/vnd.ezpix-album": ["ez2"], -"application/vnd.ezpix-package": ["ez3"], -"application/vnd.fdf": ["fdf"], -"application/vnd.fdsn.mseed": ["mseed"], -"application/vnd.fdsn.seed": ["dataless","seed"], -"application/vnd.flographit": ["gph"], -"application/vnd.fluxtime.clip": ["ftc"], -"application/vnd.framemaker": ["book","fm","frame","maker"], -"application/vnd.frogans.fnc": ["fnc"], -"application/vnd.frogans.ltf": ["ltf"], -"application/vnd.fsc.weblaunch": ["fsc"], -"application/vnd.fujitsu.oasys": ["oas"], -"application/vnd.fujitsu.oasys2": ["oa2"], -"application/vnd.fujitsu.oasys3": ["oa3"], -"application/vnd.fujitsu.oasysgp": ["fg5"], -"application/vnd.fujitsu.oasysprs": ["bh2"], -"application/vnd.fujixerox.ddd": ["ddd"], -"application/vnd.fujixerox.docuworks": ["xdw"], -"application/vnd.fujixerox.docuworks.binder": ["xbd"], -"application/vnd.fuzzysheet": ["fzs"], -"application/vnd.genomatix.tuxedo": ["txd"], -"application/vnd.geogebra.file": ["ggb"], -"application/vnd.geogebra.tool": ["ggt"], -"application/vnd.geometry-explorer": ["gex","gre"], -"application/vnd.geonext": ["gxt"], -"application/vnd.geoplan": ["g2w"], -"application/vnd.geospace": ["g3w"], -"application/vnd.gmx": ["gmx"], -"application/vnd.google-apps.document": ["gdoc"], -"application/vnd.google-apps.presentation": ["gslides"], -"application/vnd.google-apps.spreadsheet": ["gsheet"], -"application/vnd.google-earth.kml+xml": ["kml"], -"application/vnd.google-earth.kmz": ["kmz"], -"application/vnd.grafeq": ["gqf","gqs"], -"application/vnd.groove-account": ["gac"], -"application/vnd.groove-help": ["ghf"], -"application/vnd.groove-identity-message": ["gim"], -"application/vnd.groove-injector": ["grv"], -"application/vnd.groove-tool-message": ["gtm"], -"application/vnd.groove-tool-template": ["tpl"], -"application/vnd.groove-vcard": ["vcg"], -"application/vnd.hal+xml": ["hal"], -"application/vnd.handheld-entertainment+xml": ["zmm"], -"application/vnd.hbci": ["hbci"], -"application/vnd.hhe.lesson-player": ["les"], -"application/vnd.hp-hpgl": ["hpgl","hgl","hpg"], -"application/vnd.hp-hpid": ["hpid"], -"application/vnd.hp-hps": ["hps"], -"application/vnd.hp-jlyt": ["jlt"], -"application/vnd.hp-pcl": ["pcl"], -"application/vnd.hp-pclxl": ["pclxl"], -"application/vnd.hydrostatix.sof-data": ["sfd-hdstx"], -"application/vnd.ibm.minipay": ["mpy"], -"application/vnd.ibm.modcap": ["afp","list3820","listafp"], -"application/vnd.ibm.rights-management": ["irm"], -"application/vnd.ibm.secure-container": ["sc"], -"application/vnd.iccprofile": ["icc","icm"], -"application/vnd.igloader": ["igl"], -"application/vnd.immervision-ivp": ["ivp"], -"application/vnd.immervision-ivu": ["ivu"], -"application/vnd.insors.igm": ["igm"], -"application/vnd.intercon.formnet": ["xpw","xpx"], -"application/vnd.intergeo": ["i2g"], -"application/vnd.intu.qbo": ["qbo"], -"application/vnd.intu.qfx": ["qfx"], -"application/vnd.ipunplugged.rcprofile": ["rcprofile"], -"application/vnd.irepository.package+xml": ["irp"], -"application/vnd.is-xpr": ["xpr"], -"application/vnd.isac.fcs": ["fcs"], -"application/vnd.jam": ["jam"], -"application/vnd.jcp.javame.midlet-rms": ["rms"], -"application/vnd.jisp": ["jisp"], -"application/vnd.joost.joda-archive": ["joda"], -"application/vnd.kahootz": ["ktr","ktz"], -"application/vnd.kde.karbon": ["karbon"], -"application/vnd.kde.kchart": ["chrt"], -"application/vnd.kde.kformula": ["kfo"], -"application/vnd.kde.kivio": ["flw"], -"application/vnd.kde.kontour": ["kon"], -"application/vnd.kde.kpresenter": ["kpr","kpt"], -"application/vnd.kde.kspread": ["ksp"], -"application/vnd.kde.kword": ["kwd","kwt"], -"application/vnd.kenameaapp": ["htke"], -"application/vnd.kidspiration": ["kia"], -"application/vnd.kinar": ["kne","knp"], -"application/vnd.koan": ["skd","skm","skp","skt"], -"application/vnd.kodak-descriptor": ["sse"], -"application/vnd.las.las+xml": ["lasxml"], -"application/vnd.llamagraphics.life-balance.desktop": ["lbd"], -"application/vnd.llamagraphics.life-balance.exchange+xml": ["lbe"], -"application/vnd.lotus-1-2-3": ["123"], -"application/vnd.lotus-approach": ["apr"], -"application/vnd.lotus-freelance": ["pre"], -"application/vnd.lotus-notes": ["nsf"], -"application/vnd.lotus-organizer": ["org"], -"application/vnd.lotus-screencam": ["scm"], -"application/vnd.lotus-wordpro": ["lwp"], -"application/vnd.macports.portpkg": ["portpkg"], -"application/vnd.mcd": ["mcd"], -"application/vnd.medcalcdata": ["mc1"], -"application/vnd.mediastation.cdkey": ["cdkey"], -"application/vnd.mfer": ["mwf"], -"application/vnd.mfmp": ["mfm"], -"application/vnd.micrografx.flo": ["flo"], -"application/vnd.micrografx.igx": ["igx"], -"application/vnd.mif": ["mif"], -"application/vnd.mobius.daf": ["daf"], -"application/vnd.mobius.dis": ["dis"], -"application/vnd.mobius.mbk": ["mbk"], -"application/vnd.mobius.mqy": ["mqy"], -"application/vnd.mobius.msl": ["msl"], -"application/vnd.mobius.plc": ["plc"], -"application/vnd.mobius.txf": ["txf"], -"application/vnd.moodle.backup": ["mbz"], -"application/vnd.moodle.profiling": ["mpr"], -"application/vnd.mophun.application": ["mpn"], -"application/vnd.mophun.certificate": ["mpc"], -"application/vnd.mozilla.xul+xml": ["xul"], -"application/vnd.ms-artgalry": ["cil"], -"application/vnd.ms-cab-compressed": ["cab"], -"application/vnd.ms-excel": ["xls","xla","xlb","xlc","xll","xlm","xlt","xlw"], -"application/vnd.ms-excel.addin.macroenabled.12": ["xlam"], -"application/vnd.ms-excel.sheet.binary.macroenabled.12": ["xlsb"], -"application/vnd.ms-excel.sheet.macroenabled.12": ["xlsm"], -"application/vnd.ms-excel.template.macroenabled.12": ["xltm"], -"application/vnd.ms-fontobject": ["eot"], -"application/vnd.ms-htmlhelp": ["chm"], -"application/vnd.ms-ims": ["ims"], -"application/vnd.ms-lrm": ["lrm"], -"application/vnd.ms-officetheme": ["thmx"], -"application/vnd.ms-pki.certstore": ["sst"], -"application/vnd.ms-pki.pko": ["pko"], -"application/vnd.ms-pki.seccat": ["cat"], -"application/vnd.ms-pki.stl": ["stl"], -"application/vnd.ms-powerpoint": ["ppt","pot","ppa","pps","pwz"], -"application/vnd.ms-powerpoint.addin.macroenabled.12": ["ppam"], -"application/vnd.ms-powerpoint.presentation.macroenabled.12": ["pptm"], -"application/vnd.ms-powerpoint.slide.macroenabled.12": ["sldm"], -"application/vnd.ms-powerpoint.slideshow.macroenabled.12": ["ppsm"], -"application/vnd.ms-powerpoint.template.macroenabled.12": ["potm"], -"application/vnd.ms-project": ["mpp","mpt"], -"application/vnd.ms-word.document.macroenabled.12": ["docm"], -"application/vnd.ms-word.template.macroenabled.12": ["dotm"], -"application/vnd.ms-works": ["wcm","wdb","wks","wps"], -"application/vnd.ms-wpl": ["wpl"], -"application/vnd.ms-xpsdocument": ["xps"], -"application/vnd.mseq": ["mseq"], -"application/vnd.musician": ["mus"], -"application/vnd.muvee.style": ["msty"], -"application/vnd.mynfc": ["taglet"], -"application/vnd.neurolanguage.nlu": ["nlu"], -"application/vnd.nitf": ["nitf","ntf"], -"application/vnd.noblenet-directory": ["nnd"], -"application/vnd.noblenet-sealer": ["nns"], -"application/vnd.noblenet-web": ["nnw"], -"application/vnd.nokia.configuration-message": ["ncm"], -"application/vnd.nokia.n-gage.data": ["ngdat"], -"application/vnd.nokia.n-gage.symbian.install": ["n-gage"], -"application/vnd.nokia.radio-preset": ["rpst"], -"application/vnd.nokia.radio-presets": ["rpss"], -"application/vnd.nokia.ringing-tone": ["rng"], -"application/vnd.novadigm.edm": ["edm"], -"application/vnd.novadigm.edx": ["edx"], -"application/vnd.novadigm.ext": ["ext"], -"application/vnd.oasis.opendocument.chart": ["odc"], -"application/vnd.oasis.opendocument.chart-template": ["otc"], -"application/vnd.oasis.opendocument.database": ["odb"], -"application/vnd.oasis.opendocument.formula": ["odf"], -"application/vnd.oasis.opendocument.formula-template": ["odft"], -"application/vnd.oasis.opendocument.graphics": ["odg"], -"application/vnd.oasis.opendocument.graphics-template": ["otg"], -"application/vnd.oasis.opendocument.image": ["odi"], -"application/vnd.oasis.opendocument.image-template": ["oti"], -"application/vnd.oasis.opendocument.presentation": ["odp"], -"application/vnd.oasis.opendocument.presentation-template": ["otp"], -"application/vnd.oasis.opendocument.spreadsheet": ["ods"], -"application/vnd.oasis.opendocument.spreadsheet-template": ["ots"], -"application/vnd.oasis.opendocument.text": ["odt"], -"application/vnd.oasis.opendocument.text-master": ["odm"], -"application/vnd.oasis.opendocument.text-template": ["ott"], -"application/vnd.oasis.opendocument.text-web": ["oth"], -"application/vnd.olpc-sugar": ["xo"], -"application/vnd.oma.dd2+xml": ["dd2"], -"application/vnd.openofficeorg.extension": ["oxt"], -"application/vnd.openxmlformats-officedocument.presentationml.presentation": ["pptx"], -"application/vnd.openxmlformats-officedocument.presentationml.slide": ["sldx"], -"application/vnd.openxmlformats-officedocument.presentationml.slideshow": ["ppsx"], -"application/vnd.openxmlformats-officedocument.presentationml.template": ["potx"], -"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ["xlsx"], -"application/vnd.openxmlformats-officedocument.spreadsheetml.template": ["xltx"], -"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ["docx"], -"application/vnd.openxmlformats-officedocument.wordprocessingml.template": ["dotx"], -"application/vnd.osgeo.mapguide.package": ["mgp"], -"application/vnd.osgi.dp": ["dp"], -"application/vnd.osgi.subsystem": ["esa"], -"application/vnd.palm": ["oprc","pdb","pqa"], -"application/vnd.pawaafile": ["paw"], -"application/vnd.pg.format": ["str"], -"application/vnd.pg.osasli": ["ei6"], -"application/vnd.picsel": ["efif"], -"application/vnd.pmi.widget": ["wg"], -"application/vnd.pocketlearn": ["plf"], -"application/vnd.powerbuilder6": ["pbd"], -"application/vnd.previewsystems.box": ["box"], -"application/vnd.proteus.magazine": ["mgz"], -"application/vnd.publishare-delta-tree": ["qps"], -"application/vnd.pvi.ptid1": ["ptid"], -"application/vnd.quark.quarkxpress": ["qwd","qwt","qxb","qxd","qxl","qxt"], -"application/vnd.realvnc.bed": ["bed"], -"application/vnd.recordare.musicxml": ["mxl"], -"application/vnd.recordare.musicxml+xml": ["musicxml"], -"application/vnd.rig.cryptonote": ["cryptonote"], -"application/vnd.rim.cod": ["cod"], -"application/vnd.rn-realmedia": ["rm"], -"application/vnd.rn-realmedia-vbr": ["rmvb"], -"application/vnd.rn-realplayer": ["rnx"], -"application/vnd.route66.link66+xml": ["link66"], -"application/vnd.sailingtracker.track": ["st"], -"application/vnd.seemail": ["see"], -"application/vnd.sema": ["sema"], -"application/vnd.semd": ["semd"], -"application/vnd.semf": ["semf"], -"application/vnd.shana.informed.formdata": ["ifm"], -"application/vnd.shana.informed.formtemplate": ["itp"], -"application/vnd.shana.informed.interchange": ["iif"], -"application/vnd.shana.informed.package": ["ipk"], -"application/vnd.simtech-mindmapper": ["twd","twds"], -"application/vnd.smaf": ["mmf"], -"application/vnd.smart.teacher": ["teacher"], -"application/vnd.solent.sdkm+xml": ["sdkd","sdkm"], -"application/vnd.spotfire.dxp": ["dxp"], -"application/vnd.spotfire.sfs": ["sfs"], -"application/vnd.stardivision.calc": ["sdc"], -"application/vnd.stardivision.draw": ["sda"], -"application/vnd.stardivision.impress": ["sdd"], -"application/vnd.stardivision.math": ["smf"], -"application/vnd.stardivision.writer": ["sdw","vor"], -"application/vnd.stardivision.writer-global": ["sgl"], -"application/vnd.stepmania.package": ["smzip"], -"application/vnd.stepmania.stepchart": ["sm"], -"application/vnd.sun.xml.calc": ["sxc"], -"application/vnd.sun.xml.calc.template": ["stc"], -"application/vnd.sun.xml.draw": ["sxd"], -"application/vnd.sun.xml.draw.template": ["std"], -"application/vnd.sun.xml.impress": ["sxi"], -"application/vnd.sun.xml.impress.template": ["sti"], -"application/vnd.sun.xml.math": ["sxm"], -"application/vnd.sun.xml.writer": ["sxw"], -"application/vnd.sun.xml.writer.global": ["sxg"], -"application/vnd.sun.xml.writer.template": ["stw"], -"application/vnd.sus-calendar": ["sus","susp"], -"application/vnd.svd": ["svd"], -"application/vnd.symbian.install": ["sis","sisx"], -"application/vnd.syncml+xml": ["xsm"], -"application/vnd.syncml.dm+wbxml": ["bdm"], -"application/vnd.syncml.dm+xml": ["xdm"], -"application/vnd.tao.intent-module-archive": ["tao"], -"application/vnd.tcpdump.pcap": ["cap","dmp","pcap"], -"application/vnd.tmobile-livetv": ["tmo"], -"application/vnd.trid.tpt": ["tpt"], -"application/vnd.triscape.mxs": ["mxs"], -"application/vnd.trueapp": ["tra"], -"application/vnd.ufdl": ["ufdl","ufd"], -"application/vnd.uiq.theme": ["utz"], -"application/vnd.umajin": ["umj"], -"application/vnd.unity": ["unityweb"], -"application/vnd.uoml+xml": ["uoml"], -"application/vnd.vcx": ["vcx"], -"application/vnd.visio": ["vsd","vss","vst","vsw"], -"application/vnd.visionary": ["vis"], -"application/vnd.vsf": ["vsf"], -"application/vnd.wap.wbxml": ["wbxml"], -"application/vnd.wap.wmlc": ["wmlc"], -"application/vnd.wap.wmlscriptc": ["wmlsc"], -"application/vnd.webturbo": ["wtb"], -"application/vnd.wolfram.player": ["nbp"], -"application/vnd.wordperfect": ["wpd"], -"application/vnd.wqd": ["wqd"], -"application/vnd.wt.stf": ["stf"], -"application/vnd.xara": ["xar","web"], -"application/vnd.xfdl": ["xfdl","xfd"], -"application/vnd.yamaha.hv-dic": ["hvd"], -"application/vnd.yamaha.hv-script": ["hvs"], -"application/vnd.yamaha.hv-voice": ["hvp"], -"application/vnd.yamaha.openscoreformat": ["osf"], -"application/vnd.yamaha.openscoreformat.osfpvg+xml": ["osfpvg"], -"application/vnd.yamaha.smaf-audio": ["saf"], -"application/vnd.yamaha.smaf-phrase": ["spf"], -"application/vnd.yellowriver-custom-menu": ["cmp"], -"application/vnd.zul": ["zir","zirz"], -"application/vnd.zzazz.deck+xml": ["zaz"], -"application/vocaltec-media-desc": ["vmd"], -"application/vocaltec-media-file": ["vmf"], -"application/voicexml+xml": ["vxml"], -"application/widget": ["wgt"], -"application/winhlp": ["hlp"], -"application/wordperfect": ["wp","wp5","wp6","wpd"], -"application/wordperfect6.0": ["w60","wp5"], -"application/wordperfect6.1": ["w61"], -"application/wsdl+xml": ["wsdl"], -"application/wspolicy+xml": ["wspolicy"], -"application/x-123": ["wk1"], -"application/x-7z-compressed": ["7z"], -"application/x-abiword": ["abw"], -"application/x-ace-compressed": ["ace"], -"application/x-aim": ["aim"], -"application/x-apple-diskimage": ["dmg"], -"application/x-authorware-bin": ["aab"], -"application/x-authorware-map": ["aam"], -"application/x-authorware-seg": ["aas"], -"application/x-bcpio": ["bcpio"], -"application/x-binary": ["bin"], -"application/x-binhex40": ["hqx"], -"application/x-bittorrent": ["torrent"], -"application/x-blorb": ["blorb","blb"], -"application/x-bsh": ["bsh","sh","shar"], -"application/x-bytecode.elisp (compiled elisp)": ["elc"], -"application/x-bytecode.python": ["pyc"], -"application/x-bzip": ["bz"], -"application/x-bzip2": ["bz2","boz"], -"application/x-cbr": ["cbr","cb7","cba","cbt","cbz"], -"application/x-cdf": ["cdf"], -"application/x-cdlink": ["vcd"], -"application/x-cfs-compressed": ["cfs"], -"application/x-chat": ["chat","cha"], -"application/x-chess-pgn": ["pgn"], -"application/x-cmu-raster": ["ras"], -"application/x-cocoa": ["cco"], -"application/x-coldfusion": ["cfc","cfm"], -"application/x-compactpro": ["cpt"], -"application/x-compress": ["z"], -"application/x-compressed": ["gz","tgz","z","zip"], -"application/x-conference": ["nsc"], -"application/x-cpio": ["cpio"], -"application/x-cpt": ["cpt"], -"application/x-csh": ["csh","cs"], -"application/x-debian-package": ["deb","udeb"], -"application/x-deepv": ["deepv"], -"application/x-dgc-compressed": ["dgc"], -"application/x-digidoc": ["bdoc","cdoc","ddoc"], -"application/x-director": ["cct","cst","cxt","dcr","dir","dxr","fgd","swa","w3d"], -"application/x-doom": ["wad"], -"application/x-dtbncx+xml": ["ncx"], -"application/x-dtbook+xml": ["dtb"], -"application/x-dtbresource+xml": ["res"], -"application/x-dvi": ["dvi"], -"application/x-elc": ["elc"], -"application/x-envoy": ["evy","env"], -"application/x-esrehber": ["es"], -"application/x-eva": ["eva"], -"application/x-excel": ["xla","xlw","xlc","xld","xlk","xlb","xlm","xls","xlt","xlv","xll"], -"application/x-font-bdf": ["bdf"], -"application/x-font-ghostscript": ["gsf"], -"application/x-font-linux-psf": ["psf"], -"application/x-font-otf": ["otf"], -"application/x-font-pcf": ["pcf"], -"application/x-font-snf": ["snf"], -"application/x-font-ttf": ["ttf","ttc"], -"application/x-font-type1": ["afm","pfa","pfb","pfm"], -"application/x-font-woff": ["woff"], -"application/x-frame": ["mif"], -"application/x-freearc": ["arc"], -"application/x-freelance": ["pre"], -"application/x-futuresplash": ["spl"], -"application/x-gca-compressed": ["gca"], -"application/x-glulx": ["ulx"], -"application/x-gnumeric": ["gnumeric"], -"application/x-gramps-xml": ["gramps"], -"application/x-gsp": ["gsp"], -"application/x-gss": ["gss"], -"application/x-gtar": ["gtar"], -"application/x-gzip": ["gz","gzip","tgz"], -"application/x-hdf": ["hdf"], -"application/x-helpfile": ["help","hlp"], -"application/x-httpd-imap": ["imap"], -"application/x-httpd-phps": ["phps"], -"application/x-ibooks+zip": ["ibooks"], -"application/x-ima": ["ima"], -"application/x-install-instructions": ["install"], -"application/x-internett-signup": ["ins"], -"application/x-inventor": ["iv"], -"application/x-ip2": ["ip"], -"application/x-iso9660-image": ["iso"], -"application/x-java-class": ["class"], -"application/x-java-commerce": ["jcm"], -"application/x-java-jnlp-file": ["jnlp"], -"application/x-javascript": ["js"], -"application/x-koan": ["skd","skm","skp","skt"], -"application/x-ksh": ["ksh"], -"application/x-latex": ["latex","ltx"], -"application/x-lha": ["lha"], -"application/x-lisp": ["lsp"], -"application/x-livescreen": ["ivy"], -"application/x-lotus": ["wq1"], -"application/x-lotusscreencam": ["scm"], -"application/x-lzh": ["lzh"], -"application/x-lzh-compressed": ["lha","lzh"], -"application/x-lzip": ["lz"], -"application/x-lzma": ["lzma"], -"application/x-lzop": ["lzo"], -"application/x-lzx": ["lzx"], -"application/x-mac-binhex40": ["hqx"], -"application/x-macbinary": ["bin"], -"application/x-magic-cap-package-1.0": ["mc$"], -"application/x-mathcad": ["mcd"], -"application/x-meme": ["mm"], -"application/x-midi": ["mid","midi"], -"application/x-mie": ["mie"], -"application/x-mif": ["mif"], -"application/x-mix-transfer": ["nix"], -"application/x-mobipocket-ebook": ["mobi","prc"], -"application/x-mplayer2": ["asx"], -"application/x-ms-application": ["application"], -"application/x-ms-shortcut": ["lnk"], -"application/x-ms-wmd": ["wmd"], -"application/x-ms-wmz": ["wmz"], -"application/x-ms-xbap": ["xbap"], -"application/x-msaccess": ["mdb"], -"application/x-msbinder": ["obd"], -"application/x-mscardfile": ["crd"], -"application/x-msclip": ["clp"], -"application/x-msdownload": ["dll","bat","com","exe","msi"], -"application/x-msexcel": ["xla","xls","xlw"], -"application/x-msmediaview": ["mvb","m13","m14"], -"application/x-msmetafile": ["emf","emz","wmf"], -"application/x-msmoney": ["mny"], -"application/x-mspowerpoint": ["ppt"], -"application/x-mspublisher": ["pub"], -"application/x-msschedule": ["scd"], -"application/x-msterminal": ["trm"], -"application/x-mswrite": ["wri"], -"application/x-navi-animation": ["ani"], -"application/x-navidoc": ["nvd"], -"application/x-navimap": ["map"], -"application/x-navistyle": ["stl"], -"application/x-netcdf": ["cdf","nc"], -"application/x-newton-compatible-pkg": ["pkg"], -"application/x-nokia-9000-communicator-add-on-software": ["aos"], -"application/x-nzb": ["nzb"], -"application/x-omc": ["omc"], -"application/x-omcdatamaker": ["omcd"], -"application/x-omcregerator": ["omcr"], -"application/x-pagemaker": ["pm4","pm5"], -"application/x-pcl": ["pcl"], -"application/x-pem-file": ["pem"], -"application/x-pixclscript": ["plx"], -"application/x-pkcs10": ["p10"], -"application/x-pkcs12": ["p12","pfx"], -"application/x-pkcs7-certificates": ["p7b","spc"], -"application/x-pkcs7-certreqresp": ["p7r"], -"application/x-pkcs7-crl": ["crl"], -"application/x-pkcs7-mime": ["p7c","p7m"], -"application/x-pkcs7-signature": ["p7a"], -"application/x-plist": ["plist"], -"application/x-pointplus": ["css"], -"application/x-portable-anymap": ["pnm"], -"application/x-project": ["mpc","mpt","mpv","mpx"], -"application/x-qpro": ["wb1"], -"application/x-rar-compressed": ["rar"], -"application/x-research-info-systems": ["ris"], -"application/x-rpm": ["rpm"], -"application/x-rtf": ["rtf"], -"application/x-sdp": ["sdp"], -"application/x-sea": ["sea"], -"application/x-seelogo": ["sl"], -"application/x-sh": ["sh"], -"application/x-shar": ["shar","sh"], -"application/x-shockwave-flash": ["swf","swfl"], -"application/x-silverlight-app": ["xap"], -"application/x-sit": ["sit"], -"application/x-smarttech-notebook": ["gallery","gallerycollection","galleryitem","nbk","notebook","xbk"], -"application/x-sprite": ["spr","sprite"], -"application/x-sql": ["sql"], -"application/x-stuffit": ["sit"], -"application/x-stuffitx": ["sitx"], -"application/x-subrip": ["srt"], -"application/x-sv4cpio": ["sv4cpio"], -"application/x-sv4crc": ["sv4crc"], -"application/x-t3vm-image": ["t3"], -"application/x-tads": ["gam"], -"application/x-tar": ["tar"], -"application/x-tbook": ["sbk","tbk"], -"application/x-tcl": ["tcl"], -"application/x-tex": ["tex"], -"application/x-tex-tfm": ["tfm"], -"application/x-texinfo": ["texi","texinfo"], -"application/x-tgif": ["obj"], -"application/x-troff": ["roff","t","tr"], -"application/x-troff-man": ["man"], -"application/x-troff-me": ["me"], -"application/x-troff-ms": ["ms"], -"application/x-troff-msvideo": ["avi"], -"application/x-ustar": ["ustar"], -"application/x-visio": ["vsd","vst","vsw"], -"application/x-vnd.audioexplosion.mzz": ["mzz"], -"application/x-vnd.ls-xpix": ["xpix"], -"application/x-vrml": ["vrml"], -"application/x-wais-source": ["src","wsrc"], -"application/x-winhelp": ["hlp"], -"application/x-wintalk": ["wtk"], -"application/x-world": ["svr","wrl"], -"application/x-wpwin": ["wpd"], -"application/x-wri": ["wri"], -"application/x-x509-ca-cert": ["crt","cer","der"], -"application/x-x509-user-cert": ["crt"], -"application/x-xfig": ["fig"], -"application/x-xliff+xml": ["xlf"], -"application/x-xpinstall": ["xpi"], -"application/x-xz": ["xz"], -"application/x-zip-compressed": ["zip"], -"application/x-zmachine": ["z1","z2","z3","z4","z5","z6","z7","z8"], -"application/xaml+xml": ["xaml"], -"application/xcap-diff+xml": ["xdf"], -"application/xenc+xml": ["xenc"], -"application/xhtml+xml": ["xhtml","xht"], -"application/xml": ["xml","xsl"], -"application/xml-dtd": ["dtd"], -"application/xop+xml": ["xop"], -"application/xproc+xml": ["xpl"], -"application/xslt+xml": ["xslt"], -"application/xspf+xml": ["xspf"], -"application/xv+xml": ["mxml","xhvml","xvm","xvml"], -"application/yang": ["yang"], -"application/yin+xml": ["yin"], -"application/zip": ["zip","h5p"], -"audio/aac": ["aac"], -"audio/adpcm": ["adp"], -"audio/aiff": ["aif","aifc","aiff"], -"audio/amr": ["amr"], -"audio/au": ["au"], -"audio/basic": ["au","snd"], -"audio/flac": ["flac"], -"audio/it": ["it"], -"audio/make": ["funk","my","pfunk"], -"audio/make.my.funk": ["pfunk"], -"audio/mid": ["rmi"], -"audio/midi": ["midi","kar","mid","rmi"], -"audio/mod": ["mod"], -"audio/mp3": ["mp3"], -"audio/mp4": ["mp4a","m4a"], -"audio/mpeg": ["m2a","m3a","mp2","mp2a","mp3","mpa","mpg","mpga"], -"audio/mpeg3": ["mp3"], -"audio/nspaudio": ["la","lma"], -"audio/ogg": ["ogg","oga","spx"], -"audio/s3m": ["s3m"], -"audio/silk": ["sil"], -"audio/tsp-audio": ["tsi"], -"audio/tsplayer": ["tsp"], -"audio/vnd.dece.audio": ["uva","uvva"], -"audio/vnd.digital-winds": ["eol"], -"audio/vnd.dra": ["dra"], -"audio/vnd.dts": ["dts"], -"audio/vnd.dts.hd": ["dtshd"], -"audio/vnd.lucent.voice": ["lvp"], -"audio/vnd.ms-playready.media.pya": ["pya"], -"audio/vnd.nuera.ecelp4800": ["ecelp4800"], -"audio/vnd.nuera.ecelp7470": ["ecelp7470"], -"audio/vnd.nuera.ecelp9600": ["ecelp9600"], -"audio/vnd.qcelp": ["qcp"], -"audio/vnd.rip": ["rip"], -"audio/voc": ["voc"], -"audio/voxware": ["vox"], -"audio/wav": ["wav"], -"audio/webm": ["weba"], -"audio/x-aac": ["aac"], -"audio/x-adpcm": ["snd"], -"audio/x-aiff": ["aiff","aif","aifc"], -"audio/x-au": ["au"], -"audio/x-caf": ["caf"], -"audio/x-flac": ["flac"], -"audio/x-gsm": ["gsd","gsm"], -"audio/x-jam": ["jam"], -"audio/x-liveaudio": ["lam"], -"audio/x-matroska": ["mka"], -"audio/x-mid": ["mid","midi"], -"audio/x-midi": ["mid","midi"], -"audio/x-mod": ["mod"], -"audio/x-mpeg": ["mp2"], -"audio/x-mpeg-3": ["mp3"], -"audio/x-mpegurl": ["m3u","m3u8"], -"audio/x-mpequrl": ["m3u"], -"audio/x-ms-wax": ["wax"], -"audio/x-ms-wma": ["wma"], -"audio/x-nspaudio": ["la","lma"], -"audio/x-pn-realaudio": ["ra","ram","rm","rmm","rmp"], -"audio/x-pn-realaudio-plugin": ["rmp","ra","ram","rm","rpm","rv"], -"audio/x-psid": ["sid"], -"audio/x-realaudio": ["ra"], -"audio/x-realaudio-plugin": ["ra"], -"audio/x-twinvq": ["vqf"], -"audio/x-twinvq-plugin": ["vqe","vql"], -"audio/x-vnd.audioexplosion.mjuicemediafile": ["mjf"], -"audio/x-voc": ["voc"], -"audio/x-wav": ["wav"], -"audio/xm": ["xm"], -"chemical/x-cdx": ["cdx"], -"chemical/x-cif": ["cif"], -"chemical/x-cmdf": ["cmdf"], -"chemical/x-cml": ["cml"], -"chemical/x-csml": ["csml"], -"chemical/x-pdb": ["pdb","xyz"], -"chemical/x-xyz": ["xyz"], -"document/unknown": ["xxx"], -"drawing/x-dwf (old)": ["dwf"], -"i-world/i-vrml": ["ivr"], -"image/bmp": ["bmp","bm"], -"image/cgm": ["cgm"], -"image/cmu-raster": ["ras","rast"], -"image/fif": ["fif"], -"image/florian": ["flo","turbot"], -"image/g3fax": ["g3"], -"image/gif": ["gif"], -"image/ief": ["ief","iefs"], -"image/jpeg": ["jpeg","jfif","jfif-tbnl","jpe","jpg"], -"image/jutvision": ["jut"], -"image/ktx": ["ktx"], -"image/naplps": ["nap","naplps"], -"image/pict": ["pct","pic","pict"], -"image/pjpeg": ["jfif","jpe","jpeg","jpg"], -"image/png": ["png","x-png"], -"image/prs.btif": ["btif"], -"image/sgi": ["sgi"], -"image/svg+xml": ["svg","svgz"], -"image/tiff": ["tiff","tif"], -"image/vasa": ["mcf"], -"image/vnd.adobe.photoshop": ["psd"], -"image/vnd.dece.graphic": ["uvg","uvi","uvvg","uvvi"], -"image/vnd.djvu": ["djvu","djv"], -"image/vnd.dvb.subtitle": ["sub"], -"image/vnd.dwg": ["dwg","dxf","svf"], -"image/vnd.dxf": ["dxf"], -"image/vnd.fastbidsheet": ["fbs"], -"image/vnd.fpx": ["fpx"], -"image/vnd.fst": ["fst"], -"image/vnd.fujixerox.edmics-mmr": ["mmr"], -"image/vnd.fujixerox.edmics-rlc": ["rlc"], -"image/vnd.microsoft.icon": ["ico"], -"image/vnd.ms-modi": ["mdi"], -"image/vnd.ms-photo": ["wdp"], -"image/vnd.net-fpx": ["npx","fpx"], -"image/vnd.rn-realflash": ["rf"], -"image/vnd.rn-realpix": ["rp"], -"image/vnd.wap.wbmp": ["wbmp"], -"image/vnd.xiff": ["xif"], -"image/webp": ["webp"], -"image/x-3ds": ["3ds"], -"image/x-cmu-raster": ["ras"], -"image/x-cmx": ["cmx"], -"image/x-dwg": ["dwg","dxf","svf"], -"image/x-freehand": ["fh","fh4","fh5","fh7","fhc"], -"image/x-icon": ["ico"], -"image/x-jg": ["art"], -"image/x-jps": ["jps"], -"image/x-mrsid-image": ["sid"], -"image/x-niff": ["nif","niff"], -"image/x-pcx": ["pcx"], -"image/x-pict": ["pct","pic","pict"], -"image/x-portable-anymap": ["pnm"], -"image/x-portable-bitmap": ["pbm"], -"image/x-portable-graymap": ["pgm"], -"image/x-portable-greymap": ["pgm"], -"image/x-portable-pixmap": ["ppm"], -"image/x-quicktime": ["qif","qti","qtif"], -"image/x-rgb": ["rgb"], -"image/x-tga": ["tga"], -"image/x-tiff": ["tif","tiff"], -"image/x-windows-bmp": ["bmp"], -"image/x-xbitmap": ["xbm"], -"image/x-xbm": ["xbm"], -"image/x-xpixmap": ["xpm","pm"], -"image/x-xwd": ["xwd"], -"image/x-xwindowdump": ["xwd"], -"image/xbm": ["xbm"], -"image/xpm": ["xpm"], -"message/rfc822": ["eml","mht","mhtml","mime"], -"model/iges": ["iges","igs"], -"model/mesh": ["mesh","msh","silo"], -"model/vnd.collada+xml": ["dae"], -"model/vnd.dwf": ["dwf"], -"model/vnd.gdl": ["gdl"], -"model/vnd.gtw": ["gtw"], -"model/vnd.mts": ["mts"], -"model/vnd.vtu": ["vtu"], -"model/vrml": ["vrml","wrl","wrz"], -"model/x-pov": ["pov"], -"model/x3d+binary": ["x3db","x3dbz"], -"model/x3d+vrml": ["x3dv","x3dvz"], -"model/x3d+xml": ["x3d","x3dz"], -"multipart/x-gzip": ["gzip"], -"multipart/x-ustar": ["ustar"], -"multipart/x-zip": ["zip"], -"music/crescendo": ["mid","midi"], -"music/x-karaoke": ["kar"], -"paleovu/x-pv": ["pvu"], -"shockwave/director": ["cct"], -"text/asp": ["asp"], -"text/cache-manifest": ["appcache"], -"text/calendar": ["ics","ifb"], -"text/css": ["css"], -"text/csv": ["csv"], -"text/ecmascript": ["js"], -"text/html": ["html","acgi","htc","htm","htmls","htx","shtml"], -"text/javascript": ["js"], -"text/mcf": ["mcf"], -"text/n3": ["n3"], -"text/pascal": ["pas"], -"text/plain": ["txt","applescript","asc","ascx","ashx","asm","asmx","asp","aspx","axd","c","c++","cc","com","conf","cpp","cs","cxx","def","f","f90","asa","g","h","hh","hpp","idc","in","ini","jav","java","list","log","lst","m","mar","php","pl","rb","sdml","text","for"], -"text/prs.lines.tag": ["dsc"], -"text/richtext": ["rtx","rt","rtf"], -"text/rtf": ["rtf"], -"text/scriplet": ["wsc"], -"text/sgml": ["sgml","sgm"], -"text/tab-separated-values": ["tsv"], -"text/troff": ["man","me","ms","roff","t","tr"], -"text/turtle": ["ttl"], -"text/uri-list": ["uri","uni","unis","uris","urls"], -"text/vcard": ["vcard"], -"text/vnd.abc": ["abc"], -"text/vnd.curl": ["curl"], -"text/vnd.curl.dcurl": ["dcurl"], -"text/vnd.curl.mcurl": ["mcurl"], -"text/vnd.curl.scurl": ["scurl"], -"text/vnd.dvb.subtitle": ["sub"], -"text/vnd.fly": ["fly"], -"text/vnd.fmi.flexstor": ["flx"], -"text/vnd.graphviz": ["gv"], -"text/vnd.in3d.3dml": ["3dml"], -"text/vnd.in3d.spot": ["spot"], -"text/vnd.rn-realtext": ["rt"], -"text/vnd.sun.j2me.app-descriptor": ["jad"], -"text/vnd.wap.wml": ["wml"], -"text/vnd.wap.wmlscript": ["wmls"], -"text/vtt": ["vtt"], -"text/webviewhtml": ["htt"], -"text/x-asm": ["asm","s"], -"text/x-audiosoft-intra": ["aip"], -"text/x-c": ["c","cc","cpp","cxx","dic","h","hh"], -"text/x-component": ["htc"], -"text/x-fortran": ["f","f77","f90","for"], -"text/x-h": ["h","hh"], -"text/x-java-source": ["jav","java"], -"text/x-la-asf": ["lsx"], -"text/x-m": ["m"], -"text/x-nfo": ["nfo"], -"text/x-opml": ["opml"], -"text/x-pascal": ["p","pas"], -"text/x-sass": ["sass"], -"text/x-script": ["hlb"], -"text/x-script.csh": ["csh"], -"text/x-script.elisp": ["el"], -"text/x-script.guile": ["scm"], -"text/x-script.ksh": ["ksh"], -"text/x-script.lisp": ["lsp"], -"text/x-script.perl": ["pl"], -"text/x-script.perl-module": ["pm"], -"text/x-script.phyton": ["py"], -"text/x-script.rexx": ["rexx"], -"text/x-script.scheme": ["scm"], -"text/x-script.sh": ["sh"], -"text/x-script.tcl": ["tcl"], -"text/x-script.tcsh": ["tcsh"], -"text/x-script.zsh": ["zsh"], -"text/x-scss": ["scss"], -"text/x-server-parsed-html": ["shtml","ssi"], -"text/x-setext": ["etx"], -"text/x-sfv": ["sfv"], -"text/x-sgml": ["sgm","sgml"], -"text/x-speech": ["spc","talk"], -"text/x-styl": ["styl"], -"text/x-uil": ["uil"], -"text/x-uuencode": ["uu","uue"], -"text/x-vcalendar": ["vcs"], -"text/x-vcard": ["vcf"], -"text/xml": ["resx","jcb","jcw","jmt","jmx","jcl","xsl","rhb","sqt","xml","jqz"], -"text/yaml": ["yaml","yml"], -"video/3gpp": ["3gp"], -"video/3gpp2": ["3g2"], -"video/animaflex": ["afl"], -"video/avi": ["avi"], -"video/avs-video": ["avs"], -"video/dl": ["dl"], -"video/fli": ["fli"], -"video/gl": ["gl"], -"video/h261": ["h261"], -"video/h263": ["h263"], -"video/h264": ["h264"], -"video/jpeg": ["jpgv"], -"video/jpm": ["jpgm","jpm"], -"video/mj2": ["mj2","mjp2"], -"video/mp4": ["mp4","f4v","m4v","mp4v","mpg4","fmp4"], -"video/mpeg": ["mpeg","m1v","m2v","mp2","mp3","mpa","mpe","mpg"], -"video/MP2T": ["ts"], -"video/msvideo": ["avi"], -"video/ogg": ["ogv"], -"video/quicktime": ["mov","3gp","moov","qt"], -"video/vdo": ["vdo"], -"video/vivo": ["viv","vivo"], -"video/vnd.dece.hd": ["uvh","uvvh"], -"video/vnd.dece.mobile": ["uvm","uvvm"], -"video/vnd.dece.pd": ["uvp","uvvp"], -"video/vnd.dece.sd": ["uvs","uvvs"], -"video/vnd.dece.video": ["uvv","uvvv"], -"video/vnd.dvb.file": ["dvb"], -"video/vnd.fvt": ["fvt"], -"video/vnd.mpegurl": ["m4u","mxu"], -"video/vnd.ms-playready.media.pyv": ["pyv"], -"video/vnd.rn-realvideo": ["rv"], -"video/vnd.uvvu.mp4": ["uvu","uvvu"], -"video/vnd.vivo": ["viv","vivo"], -"video/vosaic": ["vos"], -"video/webm": ["webm"], -"video/x-amt-demorun": ["xdr"], -"video/x-amt-showrun": ["xsr"], -"video/x-atomic3d-feature": ["fmf"], -"video/x-dl": ["dl"], -"video/x-dv": ["dv","dif"], -"video/x-f4v": ["f4v"], -"video/x-fli": ["fli"], -"video/x-flv": ["flv"], -"video/x-gl": ["gl"], -"video/x-isvideo": ["isu"], -"video/x-m4v": ["m4v"], -"video/x-matroska": ["mkv","mk3d","mks"], -"video/x-mng": ["mng"], -"video/x-motion-jpeg": ["mjpg"], -"video/x-mpeg": ["mp2","mp3"], -"video/x-mpeq2a": ["mp2"], -"video/x-ms-asf": ["asf","asx"], -"video/x-ms-asf-plugin": ["asx"], -"video/x-ms-vob": ["vob"], -"video/x-ms-wm": ["wm","avi"], -"video/x-ms-wmv": ["wmv"], -"video/x-ms-wmx": ["wmx"], -"video/x-ms-wvx": ["wvx"], -"video/x-msvideo": ["avi"], -"video/x-qtc": ["qtc"], -"video/x-scm": ["scm"], -"video/x-sgi-movie": ["movie","mv"], -"video/x-smv": ["smv"], -"windows/metafile": ["wmf"], -"www/mime": ["mime"], -"x-conference/x-cooltalk": ["ice"], -"x-music/x-midi": ["mid","midi"], -"x-world/x-3dmf": ["3dm","3dmf","qd3","qd3d"], -"x-world/x-svr": ["svr"], -"x-world/x-vrml": ["vrml","wrl","wrz"], -"x-world/x-vrt": ["vrt"], -"xgl/drawing": ["xgz"], -"xgl/movie": ["xmz"] -} \ No newline at end of file diff --git a/src/assets/mimetypes/en.json b/src/assets/mimetypes/en.json deleted file mode 100644 index f94347be4..000000000 --- a/src/assets/mimetypes/en.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "application/epub_zip": "EPUB ebook", - "application/msword": "Word document", - "application/pdf": "PDF document", - "application/vnd.moodle.backup": "Moodle backup", - "application/vnd.ms-excel": "Excel spreadsheet", - "application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 macro-enabled workbook", - "application/vnd.ms-powerpoint": "Powerpoint presentation", - "application/vnd.oasis.opendocument.spreadsheet": "OpenDocument Spreadsheet", - "application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument Spreadsheet template", - "application/vnd.oasis.opendocument.text": "OpenDocument Text document", - "application/vnd.oasis.opendocument.text-template": "OpenDocument Text template", - "application/vnd.oasis.opendocument.text-web": "OpenDocument Web page template", - "application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 presentation", - "application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 slideshow", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 spreadsheet", - "application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 template", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 document", - "application/x-iwork-keynote-sffkey": "iWork Keynote presentation", - "application/x-iwork-numbers-sffnumbers": "iWork Numbers spreadsheet", - "application/x-iwork-pages-sffpages": "iWork Pages document", - "application/x-javascript": "JavaScript source", - "application/x-mspublisher": "Publisher document", - "application/x-shockwave-flash": "Flash animation", - "application/xhtml_xml": "XHTML document", - "archive": "Archive ({{$a.EXT}})", - "audio": "Audio file ({{$a.EXT}})", - "default": "{{$a.mimetype}}", - "document/unknown": "File", - "group:archive": "Archive files", - "group:audio": "Audio files", - "group:document": "Document files", - "group:html_audio": "Audio files natively supported by browsers", - "group:html_track": "HTML track files", - "group:html_video": "Video files natively supported by browsers", - "group:image": "Image files", - "group:presentation": "Presentation files", - "group:sourcecode": "Source code", - "group:spreadsheet": "Spreadsheet files", - "group:video": "Video files", - "group:web_audio": "Audio files used on the web", - "group:web_file": "Web files", - "group:web_image": "Image files used on the web", - "group:web_video": "Video files used on the web", - "image": "Image ({{$a.MIMETYPE2}})", - "image/vnd.microsoft.icon": "Windows icon", - "text/css": "Cascading Style-Sheet", - "text/csv": "Comma-separated values", - "text/html": "HTML document", - "text/plain": "Text file", - "text/rtf": "RTF document", - "text/vtt": "Web Video Text Track", - "video": "Video file ({{$a.EXT}})" -} \ No newline at end of file diff --git a/src/classes/animations.ts b/src/classes/animations.ts deleted file mode 100644 index 361335b33..000000000 --- a/src/classes/animations.ts +++ /dev/null @@ -1,65 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { trigger, style, transition, animate, keyframes } from '@angular/animations'; - -export const coreShowHideAnimation = trigger('coreShowHideAnimation', [ - transition(':enter', [ - style({opacity: 0}), - animate('500ms ease-in-out', style({opacity: 1})) - ]), - transition(':leave', [ - style({opacity: 1}), - animate('500ms ease-in-out', style({opacity: 0})) - ]) -]); - -export const coreSlideInOut = trigger('coreSlideInOut', [ - // Enter animation. - transition('void => fromLeft', [ - style({transform: 'translateX(0)', opacity: 1}), - animate(300, keyframes([ - style({opacity: 0, transform: 'translateX(-100%)', offset: 0}), - style({opacity: 1, transform: 'translateX(5%)', offset: 0.7}), - style({opacity: 1, transform: 'translateX(0)', offset: 1.0}) - ])) - ]), - // Leave animation. - transition('fromLeft => void', [ - style({transform: 'translateX(-100%)', opacity: 0}), - animate(300, keyframes([ - style({opacity: 1, transform: 'translateX(0)', offset: 0}), - style({opacity: 1, transform: 'translateX(5%)', offset: 0.3}), - style({opacity: 0, transform: 'translateX(-100%)', offset: 1.0}) - ])) - ]), - // Enter animation. - transition('void => fromRight', [ - style({transform: 'translateX(0)', opacity: 1}), - animate(300, keyframes([ - style({opacity: 0, transform: 'translateX(100%)', offset: 0}), - style({opacity: 1, transform: 'translateX(-5%)', offset: 0.7}), - style({opacity: 1, transform: 'translateX(0)', offset: 1.0}) - ])) - ]), - // Leave animation. - transition('fromRight => void', [ - style({transform: 'translateX(-100%)', opacity: 0}), - animate(300, keyframes([ - style({opacity: 1, transform: 'translateX(0)', offset: 0}), - style({opacity: 1, transform: 'translateX(-5%)', offset: 0.3}), - style({opacity: 0, transform: 'translateX(100%)', offset: 1.0}) - ])) - ]) -]); diff --git a/src/classes/base-sync.ts b/src/classes/base-sync.ts deleted file mode 100644 index 81d7f8ed8..000000000 --- a/src/classes/base-sync.ts +++ /dev/null @@ -1,309 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSyncProvider } from '@providers/sync'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreAppProvider } from '@providers/app'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; - -/** - * Blocked sync error. - */ -export class CoreSyncBlockedError extends Error { - constructor(message: string) { - super(message); - - // Set the prototype explicitly, otherwise instanceof won't work as expected. - Object.setPrototypeOf(this, CoreSyncBlockedError.prototype); - } -} - -/** - * Base class to create sync providers. It provides some common functions. - */ -export class CoreSyncBaseProvider { - - /** - * Logger instance get from CoreLoggerProvider. - */ - protected logger; - - /** - * Component of the sync provider. - */ - component = 'core'; - - /** - * Sync provider's interval. - */ - syncInterval = 300000; - - // Store sync promises. - protected syncPromises: { [siteId: string]: { [uniqueId: string]: Promise } } = {}; - - constructor(component: string, loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, - protected appProvider: CoreAppProvider, protected syncProvider: CoreSyncProvider, - protected textUtils: CoreTextUtilsProvider, protected translate: TranslateService, - protected timeUtils: CoreTimeUtilsProvider) { - - this.logger = loggerProvider.getInstance(component); - this.component = component; - } - - /** - * Add an offline data deleted warning to a list of warnings. - * - * @param warnings List of warnings. - * @param component Component. - * @param name Instance name. - * @param error Specific error message. - */ - protected addOfflineDataDeletedWarning(warnings: string[], component: string, name: string, error: string): void { - const warning = this.translate.instant('core.warningofflinedatadeleted', { - component: component, - name: name, - error: error, - }); - - if (warnings.indexOf(warning) == -1) { - warnings.push(warning); - } - } - - /** - * Add an ongoing sync to the syncPromises list. On finish the promise will be removed. - * - * @param id Unique sync identifier per component. - * @param promise The promise of the sync to add. - * @param siteId Site ID. If not defined, current site. - * @return The sync promise. - */ - addOngoingSync(id: string | number, promise: Promise, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const uniqueId = this.getUniqueSyncId(id); - if (!this.syncPromises[siteId]) { - this.syncPromises[siteId] = {}; - } - - this.syncPromises[siteId][uniqueId] = promise; - - // Promise will be deleted when finish. - return promise.finally(() => { - delete this.syncPromises[siteId][uniqueId]; - }); - } - - /** - * If there's an ongoing sync for a certain identifier return it. - * - * @param id Unique sync identifier per component. - * @param siteId Site ID. If not defined, current site. - * @return Promise of the current sync or undefined if there isn't any. - */ - getOngoingSync(id: string | number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - if (this.isSyncing(id, siteId)) { - // There's already a sync ongoing for this discussion, return the promise. - const uniqueId = this.getUniqueSyncId(id); - - return this.syncPromises[siteId][uniqueId]; - } - } - - /** - * Get the synchronization time in a human readable format. - * - * @param id Unique sync identifier per component. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the readable time. - */ - getReadableSyncTime(id: string | number, siteId?: string): Promise { - return this.getSyncTime(id, siteId).then((time) => { - return this.getReadableTimeFromTimestamp(time); - }); - } - - /** - * Given a timestamp return it in a human readable format. - * - * @param timestamp Timestamp - * @return Human readable time. - */ - getReadableTimeFromTimestamp(timestamp: number): string { - if (!timestamp) { - return this.translate.instant('core.never'); - } else { - return this.timeUtils.userDate(timestamp); - } - } - - /** - * Get the synchronization time. Returns 0 if no time stored. - * - * @param id Unique sync identifier per component. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the time. - */ - getSyncTime(id: string | number, siteId?: string): Promise { - return this.syncProvider.getSyncRecord(this.component, id, siteId).then((entry) => { - return entry.time; - }).catch(() => { - return 0; - }); - } - - /** - * Get the synchronization warnings of an instance. - * - * @param id Unique sync identifier per component. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with the warnings. - */ - getSyncWarnings(id: string | number, siteId?: string): Promise { - return this.syncProvider.getSyncRecord(this.component, id, siteId).then((entry) => { - return this.textUtils.parseJSON(entry.warnings, []); - }).catch(() => { - return []; - }); - } - - /** - * Create a unique identifier from component and id. - * - * @param id Unique sync identifier per component. - * @return Unique identifier from component and id. - */ - protected getUniqueSyncId(id: string | number): string { - return this.component + '#' + id; - } - - /** - * Check if a there's an ongoing syncronization for the given id. - * - * @param id Unique sync identifier per component. - * @param siteId Site ID. If not defined, current site. - * @return Whether it's synchronizing. - */ - isSyncing(id: string | number, siteId?: string): boolean { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - - const uniqueId = this.getUniqueSyncId(id); - - return !!(this.syncPromises[siteId] && this.syncPromises[siteId][uniqueId]); - } - - /** - * Check if a sync is needed: if a certain time has passed since the last time. - * - * @param id Unique sync identifier per component. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved with boolean: whether sync is needed. - */ - isSyncNeeded(id: string | number, siteId?: string): Promise { - return this.getSyncTime(id, siteId).then((time) => { - return Date.now() - this.syncInterval >= time; - }); - } - - /** - * Set the synchronization time. - * - * @param id Unique sync identifier per component. - * @param siteId Site ID. If not defined, current site. - * @param time Time to set. If not defined, current time. - * @return Promise resolved when the time is set. - */ - setSyncTime(id: string | number, siteId?: string, time?: number): Promise { - time = typeof time != 'undefined' ? time : Date.now(); - - return this.syncProvider.insertOrUpdateSyncRecord(this.component, id, { time: time }, siteId); - } - - /** - * Set the synchronization warnings. - * - * @param id Unique sync identifier per component. - * @param warnings Warnings to set. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when done. - */ - setSyncWarnings(id: string | number, warnings: string[], siteId?: string): Promise { - const warningsText = JSON.stringify(warnings || []); - - return this.syncProvider.insertOrUpdateSyncRecord(this.component, id, { warnings: warningsText }, siteId); - } - - /** - * Execute a sync function on selected sites. - * - * @param syncFunctionLog Log message to explain the sync function purpose. - * @param syncFunction Sync function to execute. - * @param params Array that defines the params that admit the funcion. - * @param siteId Site ID to sync. If not defined, sync all sites. - * @return Resolved with siteIds selected. Rejected if offline. - */ - syncOnSites(syncFunctionLog: string, syncFunction: Function, params?: any[], siteId?: string): Promise { - if (!this.appProvider.isOnline()) { - this.logger.debug(`Cannot sync '${syncFunctionLog}' because device is offline.`); - - return Promise.reject(null); - } - - let promise; - if (!siteId) { - // No site ID defined, sync all sites. - this.logger.debug(`Try to sync '${syncFunctionLog}' in all sites.`); - promise = this.sitesProvider.getLoggedInSitesIds(); - } else { - this.logger.debug(`Try to sync '${syncFunctionLog}' in site '${siteId}'.`); - promise = Promise.resolve([siteId]); - } - - params = params || []; - - return promise.then((siteIds) => { - const sitePromises = []; - siteIds.forEach((siteId) => { - // Execute function for every site selected. - sitePromises.push(syncFunction.apply(syncFunction, [siteId].concat(params))); - }); - - return Promise.all(sitePromises); - }); - } - - /** - * If there's an ongoing sync for a certain identifier, wait for it to end. - * If there's no sync ongoing the promise will be resolved right away. - * - * @param id Unique sync identifier per component. - * @param siteId Site ID. If not defined, current site. - * @return Promise resolved when there's no sync going on for the identifier. - */ - waitForSync(id: string | number, siteId?: string): Promise { - const promise = this.getOngoingSync(id, siteId); - if (promise) { - return promise.catch(() => { - // Ignore errors. - }); - } - - return Promise.resolve(); - } -} diff --git a/src/classes/cache.ts b/src/classes/cache.ts deleted file mode 100644 index bc1f905ce..000000000 --- a/src/classes/cache.ts +++ /dev/null @@ -1,102 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * A cache to store values in memory to speed up processes. - * - * The data is organized by "entries" that are identified by an ID. Each entry can have multiple values stored, - * and each value has its own timemodified. - * - * Values expire after a certain time. - */ -export class CoreCache { - protected cacheStore = {}; - - constructor() { - // Nothing to do. - } - - /** - * Clear the cache. - */ - clear(): void { - this.cacheStore = {}; - } - - /** - * Get all the data stored in the cache for a certain id. - * - * @param id The ID to identify the entry. - * @return The data from the cache. Undefined if not found. - */ - getEntry(id: any): any { - if (!this.cacheStore[id]) { - this.cacheStore[id] = {}; - } - - return this.cacheStore[id]; - } - - /** - * Get the status of a module from the "cache". - * - * @param id The ID to identify the entry. - * @param name Name of the value to get. - * @param ignoreInvalidate Whether it should always return the cached data, even if it's expired. - * @return Cached value. Undefined if not cached or expired. - */ - getValue(id: any, name: string, ignoreInvalidate?: boolean): any { - const entry = this.getEntry(id); - - if (entry[name] && typeof entry[name].value != 'undefined') { - const now = Date.now(); - // Invalidate after 5 minutes. - if (ignoreInvalidate || entry[name].timemodified + 300000 >= now) { - return entry[name].value; - } - } - - return undefined; - } - - /** - * Invalidate all the cached data for a certain entry. - * - * @param id The ID to identify the entry. - */ - invalidate(id: any): void { - const entry = this.getEntry(id); - for (const name in entry) { - entry[name].timemodified = 0; - } - } - - /** - * Update the status of a module in the "cache". - * - * @param id The ID to identify the entry. - * @param name Name of the value to set. - * @param value Value to set. - * @return The set value. - */ - setValue(id: any, name: string, value: any): any { - const entry = this.getEntry(id); - entry[name] = { - value: value, - timemodified: Date.now() - }; - - return value; - } -} diff --git a/src/classes/delegate.ts b/src/classes/delegate.ts deleted file mode 100644 index e342c1e62..000000000 --- a/src/classes/delegate.ts +++ /dev/null @@ -1,356 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSite } from '@classes/site'; - -export interface CoreDelegateHandler { - /** - * Name of the handler, or name and sub context (AddonMessages, AddonMessages:blockContact, ...). - * This name will be used to check if the feature is disabled. - */ - name: string; - - /** - * Whether or not the handler is enabled on a site level. - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise; -} - -/** - * Superclass to help creating delegates - */ -export class CoreDelegate { - - /** - * Logger instance get from CoreLoggerProvider. - */ - protected logger; - - /** - * List of registered handlers. - */ - protected handlers: { [s: string]: CoreDelegateHandler } = {}; - - /** - * List of registered handlers enabled for the current site. - */ - protected enabledHandlers: { [s: string]: CoreDelegateHandler } = {}; - - /** - * Default handler - */ - protected defaultHandler: CoreDelegateHandler; - - /** - * Time when last updateHandler functions started. - */ - protected lastUpdateHandlersStart: number; - - /** - * Feature prefix to check is feature is enabled or disabled in site. - * This check is only made if not false. Override on the subclass or override isFeatureDisabled function. - */ - protected featurePrefix: string; - - /** - * Name of the property to be used to index the handlers. By default, the handler's name will be used. - * If your delegate uses a Moodle component name to identify the handlers, please override this property. - * E.g. CoreCourseModuleDelegate uses 'modName' to index the handlers. - */ - protected handlerNameProperty = 'name'; - - /** - * Set of promises to update a handler, to prevent doing the same operation twice. - */ - protected updatePromises: {[siteId: string]: {[name: string]: Promise}} = {}; - - /** - * Whether handlers have been initialized. - */ - protected handlersInitialized = false; - - /** - * Promise to wait for handlers to be initialized. - */ - protected handlersInitPromise: Promise; - - /** - * Function to resolve the handlers init promise. - */ - protected handlersInitResolve: (value?: any) => void; - - /** - * Constructor of the Delegate. - * - * @param delegateName Delegate name used for logging purposes. - * @param loggerProvider CoreLoggerProvider instance, cannot be directly injected. - * @param sitesProvider CoreSitesProvider instance, cannot be directly injected. - * @param eventsProvider CoreEventsProvider instance, cannot be directly injected. - * If not set, no events will be fired. - */ - constructor(delegateName: string, protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, - protected eventsProvider?: CoreEventsProvider) { - this.logger = this.loggerProvider.getInstance(delegateName); - - this.handlersInitPromise = new Promise((resolve): void => { - this.handlersInitResolve = resolve; - }); - - if (eventsProvider) { - // Update handlers on this cases. - eventsProvider.on(CoreEventsProvider.LOGIN, this.updateHandlers.bind(this)); - eventsProvider.on(CoreEventsProvider.SITE_UPDATED, this.updateHandlers.bind(this)); - eventsProvider.on(CoreEventsProvider.SITE_PLUGINS_LOADED, this.updateHandlers.bind(this)); - } - } - - /** - * Execute a certain function in a enabled handler. - * If the handler isn't found or function isn't defined, call the same function in the default handler. - * - * @param handlerName The handler name. - * @param fnName Name of the function to execute. - * @param params Parameters to pass to the function. - * @return Function returned value or default value. - */ - protected executeFunctionOnEnabled(handlerName: string, fnName: string, params?: any[]): any { - return this.execute(this.enabledHandlers[handlerName], fnName, params); - } - - /** - * Execute a certain function in a handler. - * If the handler isn't found or function isn't defined, call the same function in the default handler. - * - * @param handlerName The handler name. - * @param fnName Name of the function to execute. - * @param params Parameters to pass to the function. - * @return Function returned value or default value. - */ - protected executeFunction(handlerName: string, fnName: string, params?: any[]): any { - return this.execute(this.handlers[handlerName], fnName, params); - } - - /** - * Execute a certain function in a handler. - * If the handler isn't found or function isn't defined, call the same function in the default handler. - * - * @param handler The handler. - * @param fnName Name of the function to execute. - * @param params Parameters to pass to the function. - * @return Function returned value or default value. - */ - private execute(handler: any, fnName: string, params?: any[]): any { - if (handler && handler[fnName]) { - return handler[fnName].apply(handler, params); - } else if (this.defaultHandler && this.defaultHandler[fnName]) { - return this.defaultHandler[fnName].apply(this.defaultHandler, params); - } - } - - /** - * Get a handler. - * - * @param handlerName The handler name. - * @param enabled Only enabled, or any. - * @return Handler. - */ - protected getHandler(handlerName: string, enabled: boolean = false): CoreDelegateHandler { - return enabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName]; - } - - /** - * Gets the handler full name for a given name. This is useful when the handlerNameProperty is different than "name". - * E.g. blocks are indexed by blockName. If you call this function passing the blockName it will return the name. - * - * @param name Name used to indentify the handler. - * @return Full name of corresponding handler. - */ - getHandlerName(name: string): string { - const handler = this.getHandler(name, true); - - if (!handler) { - return ''; - } - - return handler.name; - } - - /** - * Check if function exists on a handler. - * - * @param handlerName The handler name. - * @param fnName Name of the function to execute. - * @param onlyEnabled If check only enabled handlers or all. - * @return Function returned value or default value. - */ - protected hasFunction(handlerName: string, fnName: string, onlyEnabled: boolean = true): any { - const handler = onlyEnabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName]; - - return handler && handler[fnName]; - } - - /** - * Check if a handler name has a registered handler (not necessarily enabled). - * - * @param name The handler name. - * @param enabled Only enabled, or any. - * @return If the handler is registered or not. - */ - hasHandler(name: string, enabled: boolean = false): boolean { - return enabled ? typeof this.enabledHandlers[name] !== 'undefined' : typeof this.handlers[name] !== 'undefined'; - } - - /** - * Check if a time belongs to the last update handlers call. - * This is to handle the cases where updateHandlers don't finish in the same order as they're called. - * - * @param time Time to check. - * @return Whether it's the last call. - */ - isLastUpdateCall(time: number): boolean { - if (!this.lastUpdateHandlersStart) { - return true; - } - - return time == this.lastUpdateHandlersStart; - } - - /** - * Register a handler. - * - * @param handler The handler delegate object to register. - * @return True when registered, false if already registered. - */ - registerHandler(handler: CoreDelegateHandler): boolean { - const key = handler[this.handlerNameProperty] || handler.name; - - if (typeof this.handlers[key] !== 'undefined') { - this.logger.log(`Handler '${handler[this.handlerNameProperty]}' already registered`); - - return false; - } - - this.logger.log(`Registered handler '${handler[this.handlerNameProperty]}'`); - this.handlers[key] = handler; - - return true; - } - - /** - * Update the handler for the current site. - * - * @param handler The handler to check. - * @param time Time this update process started. - * @return Resolved when done. - */ - protected updateHandler(handler: CoreDelegateHandler, time: number): Promise { - const siteId = this.sitesProvider.getCurrentSiteId(), - currentSite = this.sitesProvider.getCurrentSite(); - let promise; - - if (this.updatePromises[siteId] && this.updatePromises[siteId][handler.name]) { - // There's already an update ongoing for this handler, return the promise. - return this.updatePromises[siteId][handler.name]; - } else if (!this.updatePromises[siteId]) { - this.updatePromises[siteId] = {}; - } - - if (!this.sitesProvider.isLoggedIn()) { - promise = Promise.reject(null); - } else if (this.isFeatureDisabled(handler, currentSite)) { - promise = Promise.resolve(false); - } else { - promise = Promise.resolve(handler.isEnabled()); - } - - // Checks if the handler is enabled. - this.updatePromises[siteId][handler.name] = promise.catch(() => { - return false; - }).then((enabled: boolean) => { - // Check that site hasn't changed since the check started. - if (this.sitesProvider.getCurrentSiteId() === siteId) { - const key = handler[this.handlerNameProperty] || handler.name; - - if (enabled) { - this.enabledHandlers[key] = handler; - } else { - delete this.enabledHandlers[key]; - } - } - }).finally(() => { - // Update finished, delete the promise. - delete this.updatePromises[siteId][handler.name]; - }); - - return this.updatePromises[siteId][handler.name]; - } - - /** - * Check if feature is enabled or disabled in the site, depending on the feature prefix and the handler name. - * - * @param handler Handler to check. - * @param site Site to check. - * @return Whether is enabled or disabled in site. - */ - protected isFeatureDisabled(handler: CoreDelegateHandler, site: CoreSite): boolean { - return typeof this.featurePrefix != 'undefined' && site.isFeatureDisabled(this.featurePrefix + handler.name); - } - - /** - * Update the handlers for the current site. - * - * @return Resolved when done. - */ - protected updateHandlers(): Promise { - const promises = [], - now = Date.now(); - - this.logger.debug('Updating handlers for current site.'); - - this.lastUpdateHandlersStart = now; - - // Loop over all the handlers. - for (const name in this.handlers) { - promises.push(this.updateHandler(this.handlers[name], now)); - } - - return Promise.all(promises).then(() => { - return true; - }, () => { - // Never reject. - return true; - }).then(() => { - - // Verify that this call is the last one that was started. - if (this.isLastUpdateCall(now)) { - this.handlersInitialized = true; - this.handlersInitResolve(); - - this.updateData(); - } - }); - } - - /** - * Update handlers Data. - * Override this function to update handlers data. - */ - updateData(): any { - // To be overridden. - } -} diff --git a/src/classes/error.ts b/src/classes/error.ts deleted file mode 100644 index 2bbb0b737..000000000 --- a/src/classes/error.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * Base Error class. - * - * The native Error class cannot be extended in Typescript without restoring the prototype chain, extend this - * class instead. - * - * @see https://stackoverflow.com/questions/41102060/typescript-extending-error-class - */ -export class CoreError extends Error { - - constructor(message?: string) { - super(message); - - // Fix prototype chain: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget - this.name = new.target.name; - Object.setPrototypeOf(this, new.target.prototype); - } - -} diff --git a/src/classes/interceptor.ts b/src/classes/interceptor.ts deleted file mode 100644 index 81a1f45c9..000000000 --- a/src/classes/interceptor.ts +++ /dev/null @@ -1,81 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injectable } from '@angular/core'; -import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -/** - * Interceptor for Http calls. Adds the header 'Content-Type'='application/x-www-form-urlencoded' - * and serializes the parameters if needed. - */ -@Injectable() -export class CoreInterceptor implements HttpInterceptor { - - constructor() { - // Nothing to do. - } - - intercept(req: HttpRequest, next: HttpHandler): Observable { - // Add the header and serialize the body if needed. - const newReq = req.clone({ - headers: req.headers.set('Content-Type', 'application/x-www-form-urlencoded'), - body: typeof req.body == 'object' && String(req.body) != '[object File]' ? - CoreInterceptor.serialize(req.body) : req.body - }); - - // Pass on the cloned request instead of the original request. - return next.handle(newReq); - } - - /** - * Serialize an object to be used in a request. - * - * @param obj Object to serialize. - * @param addNull Add null values to the serialized as empty parameters. - * @return Serialization of the object. - */ - static serialize(obj: any, addNull?: boolean): string { - let query = '', - fullSubName, - subValue, - innerObj; - - for (const name in obj) { - const value = obj[name]; - - if (value instanceof Array) { - for (let i = 0; i < value.length; ++i) { - subValue = value[i]; - fullSubName = name + '[' + i + ']'; - innerObj = {}; - innerObj[fullSubName] = subValue; - query += this.serialize(innerObj) + '&'; - } - } else if (value instanceof Object) { - for (const subName in value) { - subValue = value[subName]; - fullSubName = name + '[' + subName + ']'; - innerObj = {}; - innerObj[fullSubName] = subValue; - query += this.serialize(innerObj) + '&'; - } - } else if (addNull || (typeof value != 'undefined' && value !== null)) { - query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&'; - } - } - - return query.length ? query.substr(0, query.length - 1) : query; - } -} diff --git a/src/classes/modal-lateral-transition.ts b/src/classes/modal-lateral-transition.ts deleted file mode 100644 index 33856a157..000000000 --- a/src/classes/modal-lateral-transition.ts +++ /dev/null @@ -1,72 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Animation } from 'ionic-angular/animations/animation'; -import { PageTransition } from 'ionic-angular/transitions/page-transition'; - -/** - * Sliding transition for lateral modals. - */ -export class CoreModalLateralTransition extends PageTransition { - /** - * Animation. - */ - init(): void { - const enteringView = this.enteringView; - const leavingView = this.leavingView; - - const plt = this.plt; - const OFF_RIGHT = plt.isRTL ? '-100%' : '100%'; - - if (enteringView && enteringView.pageRef()) { - const ele = enteringView.pageRef().nativeElement; - const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper')); - const backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop')); - - wrapper.beforeStyles({ transform: 'translateX(' + OFF_RIGHT + ')', opacity: 0.8 }); - wrapper.fromTo('transform', 'translateX(' + OFF_RIGHT + ')', 'translateX(0)'); - wrapper.fromTo('opacity', 0.8, 1); - backdrop.fromTo('opacity', 0.01, 0.4); - - this - .element(enteringView.pageRef()) - .duration(300) - .easing('cubic-bezier(0.36,0.66,0.04,1)') - .add(wrapper) - .add(backdrop); - } - - if (leavingView && leavingView.pageRef()) { - const ele = this.leavingView.pageRef().nativeElement; - const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper')); - const contentWrapper = new Animation(this.plt, ele.querySelector('.wrapper')); - const backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop')); - - wrapper.beforeStyles({ transform: 'translateX(0)', opacity: 1 }); - wrapper.fromTo('transform', 'translateX(0)', 'translateX(' + OFF_RIGHT + ')'); - wrapper.fromTo('opacity', 1, 0.8); - contentWrapper.fromTo('opacity', 1, 0); - backdrop.fromTo('opacity', 0.4, 0); - - this - .element(leavingView.pageRef()) - .duration(300) - .easing('cubic-bezier(0.36,0.66,0.04,1)') - .add(contentWrapper) - .add(wrapper) - .add(backdrop); - - } - } -} diff --git a/src/classes/native-to-angular-http.ts b/src/classes/native-to-angular-http.ts deleted file mode 100644 index e21e7eb7e..000000000 --- a/src/classes/native-to-angular-http.ts +++ /dev/null @@ -1,98 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { HttpResponse as AngularHttpResponse, HttpHeaders } from '@angular/common/http'; -import { HTTPResponse as NativeHttpResponse } from '@ionic-native/http'; - -const HTTP_STATUS_MESSAGES = { - 100: 'Continue', - 101: 'Switching Protocol', - 102: 'Processing', - 103: 'Early Hints', - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 203: 'Non-Authoritative Information', - 204: 'No Content', - 205: 'Reset Content', - 206: 'Partial Content', - 207: 'Multi-Status', - 208: 'Already Reported', - 226: 'IM Used', - 300: 'Multiple Choice', - 301: 'Moved Permanently', - 302: 'Found', - 303: 'See Other', - 304: 'Not Modified', - 305: 'Use Proxy', - 306: 'unused', - 307: 'Temporary Redirect', - 308: 'Permanent Redirect', - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Payload Too Large', - 414: 'URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Range Not Satisfiable', - 417: 'Expectation Failed', - 418: 'I\'m a teapot', - 421: 'Misdirected Request', - 422: 'Unprocessable Entity', - 423: 'Locked', - 424: 'Failed Dependency', - 425: 'Too Early', - 426: 'Upgrade Required', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - 451: 'Unavailable For Legal Reasons', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 506: 'Variant Also Negotiates', - 507: 'Insufficient Storage', - 508: 'Loop Detected', - 510: 'Not Extended', - 511: 'Network Authentication Required', -}; - -/** - * Class that adapts a Cordova plugin http response to an Angular http response. - */ -export class CoreNativeToAngularHttpResponse extends AngularHttpResponse { - - constructor(protected nativeResponse: NativeHttpResponse) { - super({ - body: nativeResponse.data, - headers: new HttpHeaders(nativeResponse.headers), - status: nativeResponse.status, - statusText: HTTP_STATUS_MESSAGES[nativeResponse.status] || '', - url: nativeResponse.url || '' - }); - } -} diff --git a/src/classes/page-transition.ts b/src/classes/page-transition.ts deleted file mode 100644 index 50c2329ff..000000000 --- a/src/classes/page-transition.ts +++ /dev/null @@ -1,143 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Animation } from 'ionic-angular/animations/animation'; -import { isPresent } from 'ionic-angular/util/util'; -import { PageTransition } from 'ionic-angular/transitions/page-transition'; - -const DURATION = 500; -const EASING = 'cubic-bezier(0.36,0.66,0.04,1)'; -const OPACITY = 'opacity'; -const TRANSFORM = 'transform'; -const TRANSLATEX = 'translateX'; -const CENTER = '0%'; -const OFF_OPACITY = 0.8; -const SHOW_BACK_BTN_CSS = 'show-back-button'; - -/** - * This class overrides the default transition to avoid glitches with new tabs and split view. - * Is based on IOSTransition class but it has some changes: - * - The animation is done to the full page not header, footer and content separetely. - * - On the Navbar only the back button is animated (title and other buttons will be done as a whole). Otherwise back button won't - * appear. - */ -export class CorePageTransition extends PageTransition { - init(): void { - super.init(); - const plt = this.plt; - const OFF_RIGHT = plt.isRTL ? '-99.5%' : '99.5%'; - const OFF_LEFT = plt.isRTL ? '33%' : '-33%'; - const enteringView = this.enteringView; - const leavingView = this.leavingView; - const opts = this.opts; - this.duration(isPresent(opts.duration) ? opts.duration : DURATION); - this.easing(isPresent(opts.easing) ? opts.easing : EASING); - const backDirection = (opts.direction === 'back'); - const enteringHasNavbar = (enteringView && enteringView.hasNavbar()); - const leavingHasNavbar = (leavingView && leavingView.hasNavbar()); - if (enteringView) { - // Get the native element for the entering page. - const enteringPageEle = enteringView.pageRef().nativeElement; - // Entering content. - const enteringContent = new Animation(plt, enteringPageEle); - this.add(enteringContent); - if (backDirection) { - // Entering content, back direction. - enteringContent - .fromTo(TRANSLATEX, OFF_LEFT, CENTER, true) - .fromTo(OPACITY, OFF_OPACITY, 1, true); - } - else { - // Entering content, forward direction. - enteringContent - .beforeClearStyles([OPACITY]) - .fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true); - } - if (enteringHasNavbar) { - // Entering page has a navbar. - const enteringNavbarEle = enteringPageEle.querySelector('ion-navbar'); - const enteringNavBar = new Animation(plt, enteringNavbarEle); - this.add(enteringNavBar); - const enteringBackButton = new Animation(plt, enteringNavbarEle.querySelector('.back-button')); - enteringNavBar - .add(enteringBackButton); - // Set properties depending on direction. - if (backDirection) { - // Entering navbar, back direction. - if (enteringView.enableBack()) { - // Back direction, entering page has a back button. - enteringBackButton - .beforeAddClass(SHOW_BACK_BTN_CSS) - .fromTo(OPACITY, 0.01, 1, true); - } - } - else { - // Entering navbar, forward direction. - if (enteringView.enableBack()) { - // Forward direction, entering page has a back button. - enteringBackButton - .beforeAddClass(SHOW_BACK_BTN_CSS) - .fromTo(OPACITY, 0.01, 1, true); - const enteringBackBtnText = new Animation(plt, enteringNavbarEle.querySelector('.back-button-text')); - enteringBackBtnText.fromTo(TRANSLATEX, (plt.isRTL ? '-100px' : '100px'), '0px'); - enteringNavBar.add(enteringBackBtnText); - } - else { - enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS); - } - } - } - } - // Setup leaving view. - if (leavingView && leavingView.pageRef()) { - // Leaving content. - const leavingPageEle = leavingView.pageRef().nativeElement; - const leavingContent = new Animation(plt, leavingPageEle); - this.add(leavingContent); - if (backDirection) { - // Leaving content, back direction. - leavingContent - .beforeClearStyles([OPACITY]) - .fromTo(TRANSLATEX, CENTER, (plt.isRTL ? '-100%' : '100%')); - } - else { - // Leaving content, forward direction. - leavingContent - .fromTo(TRANSLATEX, CENTER, OFF_LEFT) - .fromTo(OPACITY, 1, OFF_OPACITY) - .afterClearStyles([TRANSFORM, OPACITY]); - } - if (leavingHasNavbar) { - // Leaving page has a navbar. - const leavingNavbarEle = leavingPageEle.querySelector('ion-navbar'); - const leavingNavBar = new Animation(plt, leavingNavbarEle); - const leavingBackButton = new Animation(plt, leavingNavbarEle.querySelector('.back-button')); - leavingNavBar - .add(leavingBackButton); - this.add(leavingNavBar); - // Fade out leaving navbar items. - leavingBackButton.fromTo(OPACITY, 0.99, 0); - if (backDirection) { - const leavingBackBtnText = new Animation(plt, leavingNavbarEle.querySelector('.back-button-text')); - leavingBackBtnText.fromTo(TRANSLATEX, CENTER, (plt.isRTL ? -300 : 300) + 'px'); - leavingNavBar.add(leavingBackBtnText); - } - else { - // Leaving navbar, forward direction. - leavingBackButton.afterClearStyles([OPACITY]); - } - } - } - } -} diff --git a/src/classes/queue-runner.ts b/src/classes/queue-runner.ts deleted file mode 100644 index 88849abdd..000000000 --- a/src/classes/queue-runner.ts +++ /dev/null @@ -1,143 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreUtils, PromiseDefer } from '@providers/utils/utils'; - -/** - * Function to add to the queue. - */ -export type CoreQueueRunnerFunction = (...args: any[]) => T | Promise; - -/** - * Queue item. - */ -export type CoreQueueRunnerItem = { - /** - * Item ID. - */ - id: string; - - /** - * Function to execute. - */ - fn: CoreQueueRunnerFunction; - - /** - * Deferred with a promise resolved/rejected with the result of the function. - */ - deferred: PromiseDefer; -}; - -/** - * Options to pass to add item. - */ -export type CoreQueueRunnerAddOptions = { - /** - * Whether to allow having multiple entries with same ID in the queue. - */ - allowRepeated?: boolean; -}; - -/** - * A queue to prevent having too many concurrent executions. - */ -export class CoreQueueRunner { - protected queue: {[id: string]: CoreQueueRunnerItem} = {}; - protected orderedQueue: CoreQueueRunnerItem[] = []; - protected numberRunning = 0; - - constructor(protected maxParallel: number = 1) { } - - /** - * Get unique ID. - * - * @param id ID. - * @return Unique ID. - */ - protected getUniqueId(id: string): string { - let newId = id; - let num = 1; - - do { - newId = id + '-' + num; - num++; - } while (newId in this.queue); - - return newId; - } - - /** - * Process next item in the queue. - * - * @return Promise resolved when next item has been treated. - */ - protected async processNextItem(): Promise { - if (!this.orderedQueue.length || this.numberRunning >= this.maxParallel) { - // Queue is empty or max number of parallel runs reached, stop. - return; - } - - const item = this.orderedQueue.shift(); - this.numberRunning++; - - try { - const result = await item.fn(); - - item.deferred.resolve(result); - } catch (error) { - item.deferred.reject(error); - } finally { - delete this.queue[item.id]; - this.numberRunning--; - - this.processNextItem(); - } - } - - /** - * Add an item to the queue. - * - * @param id ID. - * @param fn Function to call. - * @param options Options. - * @return Promise resolved when the function has been executed. - */ - run(id: string, fn: CoreQueueRunnerFunction, options?: CoreQueueRunnerAddOptions): Promise { - options = options || {}; - - if (id in this.queue) { - if (!options.allowRepeated) { - // Item already in queue, return its promise. - return this.queue[id].deferred.promise; - } - - id = this.getUniqueId(id); - } - - // Add the item in the queue. - const item = { - id, - fn, - deferred: CoreUtils.instance.promiseDefer(), - }; - - this.queue[id] = item; - this.orderedQueue.push(item); - - // Process next item if we haven't reached the max yet. - this.processNextItem(); - - return item.deferred.promise; - } -} diff --git a/src/classes/singletons-factory.ts b/src/classes/singletons-factory.ts deleted file mode 100644 index 791b192f0..000000000 --- a/src/classes/singletons-factory.ts +++ /dev/null @@ -1,76 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injector, Type } from '@angular/core'; - -/** - * Stub class used to type anonymous classes created in CoreSingletonsFactory#makeSingleton method. - */ -class CoreSingleton {} - -/** - * Token that can be used to resolve instances from the injector. - */ -export type CoreInjectionToken = Type | Type | string; - -/** - * Singleton class created using the factory. - */ -export type CoreSingletonClass = typeof CoreSingleton & { instance: Service }; - -/** - * Factory used to create CoreSingleton classes that get instances from an injector. - */ -export class CoreSingletonsFactory { - - /** - * Angular injector used to resolve singleton instances. - */ - private injector: Injector; - - /** - * Set the injector that will be used to resolve instances in the singletons created with this factory. - * - * @param injector Injector. - */ - setInjector(injector: Injector): void { - this.injector = injector; - } - - /** - * Make a singleton that will hold an instance resolved from the factory injector. - * - * @param injectionToken Injection token used to resolve the singleton instance. This is usually the service class if the - * provider was defined using a class or the string used in the `provide` key if it was defined using an object. - */ - makeSingleton(injectionToken: CoreInjectionToken): CoreSingletonClass { - // tslint:disable: no-this-assignment - const factory = this; - - return class { - - private static _instance: Service; - - static get instance(): Service { - // Initialize instances lazily. - if (!this._instance) { - this._instance = factory.injector.get(injectionToken); - } - - return this._instance; - } - - }; - } -} diff --git a/src/classes/site.ts b/src/classes/site.ts deleted file mode 100644 index 10c9c3e27..000000000 --- a/src/classes/site.ts +++ /dev/null @@ -1,2081 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Injector } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { SQLiteDB } from './sqlitedb'; -import { CoreAppProvider } from '@providers/app'; -import { CoreDbProvider } from '@providers/db'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreFileProvider } from '@providers/file'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreWSProvider, CoreWSPreSets, CoreWSFileUploadOptions, CoreWSAjaxPreSets } from '@providers/ws'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils'; -import { CoreConstants } from '@core/constants'; -import { CoreConfigConstants } from '../configconstants'; -import { Md5 } from 'ts-md5/dist/md5'; -import { InAppBrowserObject } from '@ionic-native/in-app-browser'; - -/** - * PreSets accepted by the WS call. - */ -export interface CoreSiteWSPreSets { - /** - * Get the value from the cache if it's still valid. - */ - getFromCache?: boolean; - - /** - * Save the result to the cache. - */ - saveToCache?: boolean; - - /** - * Ignore cache expiration. - */ - omitExpires?: boolean; - - /** - * Use the cache when a request fails. Defaults to true. - */ - emergencyCache?: boolean; - - /** - * If true, the app won't call the WS. If the data isn't cached, the call will fail. - */ - forceOffline?: boolean; - - /** - * Extra key to add to the cache when storing this call, to identify the entry. - */ - cacheKey?: string; - - /** - * Whether it should use cache key to retrieve the cached data instead of the request params. - */ - getCacheUsingCacheKey?: boolean; - - /** - * Same as getCacheUsingCacheKey, but for emergency cache. - */ - getEmergencyCacheUsingCacheKey?: boolean; - - /** - * If true, the cache entry will be deleted if the WS call returns an exception. - */ - deleteCacheIfWSError?: boolean; - - /** - * Whether it should only be 1 entry for this cache key (all entries with same key will be deleted). - */ - uniqueCacheKey?: boolean; - - /** - * Whether to filter WS response (moodlewssettingfilter). Defaults to true. - */ - filter?: boolean; - - /** - * Whether to rewrite URLs (moodlewssettingfileurl). Defaults to true. - */ - rewriteurls?: boolean; - - /** - * Defaults to true. Set to false when the expected response is null. - */ - responseExpected?: boolean; - - /** - * Defaults to 'object'. Use it when you expect a type that's not an object|array. - */ - typeExpected?: string; - - /** - * Wehther a pending request in the queue matching the same function and arguments can be reused instead of adding - * a new request to the queue. Defaults to true for read requests. - */ - reusePending?: boolean; - - /** - * Whether the request will be be sent immediately as a single request. Defaults to false. - */ - skipQueue?: boolean; - - /** - * Cache the response if it returns an errorcode present in this list. - */ - cacheErrors?: string[]; - - /** - * Update frequency. This value determines how often the cached data will be updated. Possible values: - * CoreSite.FREQUENCY_USUALLY, CoreSite.FREQUENCY_OFTEN, CoreSite.FREQUENCY_SOMETIMES, CoreSite.FREQUENCY_RARELY. - * Defaults to CoreSite.FREQUENCY_USUALLY. - */ - updateFrequency?: number; - - /** - * Component name. Optionally included if this request is being made on behalf of a specific - * component (e.g. activity). - */ - component?: string; - - /** - * Component id. Optionally included when 'component' is set. - */ - componentId?: number; -} - -/** - * Response of checking local_mobile status. - */ -export interface LocalMobileResponse { - /** - * Code to identify the authentication method to use. - */ - code: number; - - /** - * Name of the service to use. - */ - service?: string; - - /** - * Code of the warning message. - */ - warning?: string; - - /** - * Whether core SSO is supported. - */ - coreSupported?: boolean; -} - -/** - * Info of a request waiting in the queue. - */ -interface RequestQueueItem { - cacheId: string; - method: string; - data: any; - preSets: CoreSiteWSPreSets; - wsPreSets: CoreWSPreSets; - deferred: PromiseDefer; -} - -/** - * Class that represents a site (combination of site + user). - * It will have all the site data and provide utility functions regarding a site. - * To add tables to the site's database, please use CoreSitesProvider.registerSiteSchema. This will make sure that - * the tables are created in all the sites, not just the current one. - */ -export class CoreSite { - static REQUEST_QUEUE_DELAY = 50; // Maximum number of miliseconds to wait before processing the queue. - static REQUEST_QUEUE_LIMIT = 10; // Maximum number of requests allowed in the queue. - static REQUEST_QUEUE_FORCE_WS = false; // Use "tool_mobile_call_external_functions" even for calling a single function. - - // Constants for cache update frequency. - static FREQUENCY_USUALLY = 0; - static FREQUENCY_OFTEN = 1; - static FREQUENCY_SOMETIMES = 2; - static FREQUENCY_RARELY = 3; - - // List of injected services. This class isn't injectable, so it cannot use DI. - protected appProvider: CoreAppProvider; - protected dbProvider: CoreDbProvider; - protected domUtils: CoreDomUtilsProvider; - protected eventsProvider: CoreEventsProvider; - protected fileProvider: CoreFileProvider; - protected textUtils: CoreTextUtilsProvider; - protected timeUtils: CoreTimeUtilsProvider; - protected translate: TranslateService; - protected utils: CoreUtilsProvider; - protected urlUtils: CoreUrlUtilsProvider; - protected wsProvider: CoreWSProvider; - - // Variables for the database. - static WS_CACHE_TABLE = 'wscache_2'; - static CONFIG_TABLE = 'core_site_config'; - - // Versions of Moodle releases. - protected MOODLE_RELEASES = { - 3.1: 2016052300, - 3.2: 2016120500, - 3.3: 2017051503, - 3.4: 2017111300, - 3.5: 2018051700, - 3.6: 2018120300, - 3.7: 2019052000 - }; - static MINIMUM_MOODLE_VERSION = '3.1'; - - // Possible cache update frequencies. - protected UPDATE_FREQUENCIES = [ - CoreConfigConstants.cache_update_frequency_usually || 420000, - CoreConfigConstants.cache_update_frequency_often || 1200000, - CoreConfigConstants.cache_update_frequency_sometimes || 3600000, - CoreConfigConstants.cache_update_frequency_rarely || 43200000 - ]; - - // Rest of variables. - protected logger; - protected db: SQLiteDB; - protected cleanUnicode = false; - protected lastAutoLogin = 0; - protected offlineDisabled = false; - protected ongoingRequests: { [cacheId: string]: Promise } = {}; - protected requestQueue: RequestQueueItem[] = []; - protected requestQueueTimeout = null; - protected tokenPluginFileWorks: boolean; - protected tokenPluginFileWorksPromise: Promise; - protected oauthId: number; - - /** - * Create a site. - * - * @param injector Angular injector to prevent having to pass all the required services. - * @param id Site ID. - * @param siteUrl Site URL. - * @param token Site's WS token. - * @param info Site info. - * @param privateToken Private token. - * @param config Site public config. - * @param loggedOut Whether user is logged out. - */ - constructor(injector: Injector, public id: string, public siteUrl: string, public token?: string, public infos?: any, - public privateToken?: string, public config?: any, public loggedOut?: boolean) { - // Inject the required services. - const logger = injector.get(CoreLoggerProvider); - this.appProvider = injector.get(CoreAppProvider); - this.dbProvider = injector.get(CoreDbProvider); - this.domUtils = injector.get(CoreDomUtilsProvider); - this.eventsProvider = injector.get(CoreEventsProvider); - this.fileProvider = injector.get(CoreFileProvider); - this.textUtils = injector.get(CoreTextUtilsProvider); - this.timeUtils = injector.get(CoreTimeUtilsProvider); - this.translate = injector.get(TranslateService); - this.utils = injector.get(CoreUtilsProvider); - this.urlUtils = injector.get(CoreUrlUtilsProvider); - this.wsProvider = injector.get(CoreWSProvider); - - this.logger = logger.getInstance('CoreWSProvider'); - this.setInfo(infos); - this.calculateOfflineDisabled(); - - if (this.id) { - this.initDB(); - } - } - - /** - * Initialize the database. - */ - initDB(): void { - this.db = this.dbProvider.getDB('Site-' + this.id); - } - - /** - * Get site ID. - * - * @return Site ID. - */ - getId(): string { - return this.id; - } - - /** - * Get site URL. - * - * @return Site URL. - */ - getURL(): string { - return this.siteUrl; - } - - /** - * Get site token. - * - * @return Site token. - */ - getToken(): string { - return this.token; - } - - /** - * Get site info. - * - * @return Site info. - */ - getInfo(): any { - return this.infos; - } - - /** - * Get site private token. - * - * @return Site private token. - */ - getPrivateToken(): string { - return this.privateToken; - } - - /** - * Get site DB. - * - * @return Site DB. - */ - getDb(): SQLiteDB { - return this.db; - } - - /** - * Get site user's ID. - * - * @return User's ID. - */ - getUserId(): number { - if (typeof this.infos != 'undefined' && typeof this.infos.userid != 'undefined') { - return this.infos.userid; - } - } - - /** - * Get site Course ID for frontpage course. If not declared it will return 1 as default. - * - * @return Site Home ID. - */ - getSiteHomeId(): number { - return this.infos && this.infos.siteid || 1; - } - - /** - * Get site name. - * - * @return Site name. - */ - getSiteName(): string { - if (CoreConfigConstants.sitename) { - // Overridden by config. - return CoreConfigConstants.sitename; - } else { - return this.infos && this.infos.sitename || ''; - } - } - - /** - * Set site ID. - * - * @param New ID. - */ - setId(id: string): void { - this.id = id; - this.initDB(); - } - - /** - * Set site token. - * - * @param New token. - */ - setToken(token: string): void { - this.token = token; - } - - /** - * Set site private token. - * - * @param privateToken New private token. - */ - setPrivateToken(privateToken: string): void { - this.privateToken = privateToken; - } - - /** - * Check if user logged out from the site and needs to authenticate again. - * - * @return Whether is logged out. - */ - isLoggedOut(): boolean { - return !!this.loggedOut; - } - - /** - * Get OAuth ID. - * - * @return OAuth ID. - */ - getOAuthId(): number { - return this.oauthId; - } - - /** - * Set site info. - * - * @param New info. - */ - setInfo(infos: any): void { - this.infos = infos; - - // Index function by name to speed up wsAvailable method. - if (infos && infos.functions) { - infos.functionsByName = {}; - infos.functions.forEach((func) => { - infos.functionsByName[func.name] = func; - }); - } - } - - /** - * Set site config. - * - * @param Config. - */ - setConfig(config: any): void { - if (config) { - config.tool_mobile_disabledfeatures = this.textUtils.treatDisabledFeatures(config.tool_mobile_disabledfeatures); - } - - this.config = config; - this.calculateOfflineDisabled(); - } - - /** - * Set site logged out. - * - * @param loggedOut True if logged out and needs to authenticate again, false otherwise. - */ - setLoggedOut(loggedOut: boolean): void { - this.loggedOut = !!loggedOut; - } - - /** - * Set OAuth ID. - * - * @param oauth OAuth ID. - */ - setOAuthId(oauthId: number): void { - this.oauthId = oauthId; - } - - /** - * Check if the user authenticated in the site using an OAuth method. - * - * @return {boolean} Whether the user authenticated in the site using an OAuth method. - */ - isOAuth(): boolean { - return this.oauthId != null && typeof this.oauthId != 'undefined'; - } - - /** - * Can the user access their private files? - * - * @return Whether can access my files. - */ - canAccessMyFiles(): boolean { - const infos = this.getInfo(); - - return infos && (typeof infos.usercanmanageownfiles === 'undefined' || infos.usercanmanageownfiles); - } - - /** - * Can the user download files? - * - * @return Whether can download files. - */ - canDownloadFiles(): boolean { - const infos = this.getInfo(); - - return infos && infos.downloadfiles; - } - - /** - * Can the user use an advanced feature? - * - * @param feature The name of the feature. - * @param whenUndefined The value to return when the parameter is undefined. - * @return Whether can use advanced feature. - */ - canUseAdvancedFeature(feature: string, whenUndefined: boolean = true): boolean { - const infos = this.getInfo(); - let canUse = true; - - if (typeof infos.advancedfeatures === 'undefined') { - canUse = whenUndefined; - } else { - for (const i in infos.advancedfeatures) { - const item = infos.advancedfeatures[i]; - if (item.name === feature && parseInt(item.value, 10) === 0) { - canUse = false; - } - } - - } - - return canUse; - } - - /** - * Can the user upload files? - * - * @return Whether can upload files. - */ - canUploadFiles(): boolean { - const infos = this.getInfo(); - - return infos && infos.uploadfiles; - } - - /** - * Fetch site info from the Moodle site. - * - * @return A promise to be resolved when the site info is retrieved. - */ - fetchSiteInfo(): Promise { - // The get_site_info WS call won't be cached. - const preSets = { - getFromCache: false, - saveToCache: false, - skipQueue: true - }; - - // Reset clean Unicode to check if it's supported again. - this.cleanUnicode = false; - - return this.read('core_webservice_get_site_info', {}, preSets); - } - - /** - * Read some data from the Moodle site using WS. Requests are cached by default. - * - * @param method WS method to use. - * @param data Data to send to the WS. - * @param preSets Extra options. - * @return Promise resolved with the response, rejected with CoreWSError if it fails. - */ - read(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise { - preSets = preSets || {}; - if (typeof preSets.getFromCache == 'undefined') { - preSets.getFromCache = true; - } - if (typeof preSets.saveToCache == 'undefined') { - preSets.saveToCache = true; - } - if (typeof preSets.reusePending == 'undefined') { - preSets.reusePending = true; - } - - return this.request(method, data, preSets); - } - - /** - * Sends some data to the Moodle site using WS. Requests are NOT cached by default. - * - * @param method WS method to use. - * @param data Data to send to the WS. - * @param preSets Extra options. - * @return Promise resolved with the response, rejected with CoreWSError if it fails. - */ - write(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise { - preSets = preSets || {}; - if (typeof preSets.getFromCache == 'undefined') { - preSets.getFromCache = false; - } - if (typeof preSets.saveToCache == 'undefined') { - preSets.saveToCache = false; - } - if (typeof preSets.emergencyCache == 'undefined') { - preSets.emergencyCache = false; - } - - return this.request(method, data, preSets); - } - - /** - * WS request to the site. - * - * @param method The WebService method to be called. - * @param data Arguments to pass to the method. - * @param preSets Extra options. - * @param retrying True if we're retrying the call for some reason. This is to prevent infinite loops. - * @return Promise resolved with the response, rejected with CoreWSError if it fails. - * @description - * - * Sends a webservice request to the site. This method will automatically add the - * required parameters and pass it on to the low level API in CoreWSProvider.call(). - * - * Caching is also implemented, when enabled this method will returned a cached version of the request if the - * data hasn't expired. - * - * This method is smart which means that it will try to map the method to a compatibility one if need be, usually this - * means that it will fallback on the 'local_mobile_' prefixed function if it is available and the non-prefixed is not. - */ - request(method: string, data: any, preSets: CoreSiteWSPreSets, retrying?: boolean): Promise { - const initialToken = this.token; - data = data || {}; - - if (!this.appProvider.isOnline() && this.offlineDisabled) { - return Promise.reject(this.wsProvider.createFakeWSError('core.errorofflinedisabled', true)); - } - - // Check if the method is available, use a prefixed version if possible. - // We ignore this check when we do not have the site info, as the list of functions is not loaded yet. - if (this.getInfo() && !this.wsAvailable(method, false)) { - const compatibilityMethod = CoreConstants.WS_PREFIX + method; - if (this.wsAvailable(compatibilityMethod, false)) { - this.logger.info(`Using compatibility WS method '${compatibilityMethod}'`); - method = compatibilityMethod; - } else { - this.logger.error(`WS function '${method}' is not available, even in compatibility mode.`); - - return Promise.reject(this.utils.createFakeWSError('core.wsfunctionnotavailable', true)); - } - } - - const wsPreSets: CoreWSPreSets = { - wsToken: this.token, - siteUrl: this.siteUrl, - cleanUnicode: this.cleanUnicode, - typeExpected: preSets.typeExpected, - responseExpected: preSets.responseExpected - }; - - if (wsPreSets.cleanUnicode && this.textUtils.hasUnicodeData(data)) { - // Data will be cleaned, notify the user. - this.domUtils.showToast('core.unicodenotsupported', true, 3000); - } else { - // No need to clean data in this call. - wsPreSets.cleanUnicode = false; - } - - if (this.offlineDisabled) { - // Offline is disabled, don't use cache. - preSets.getFromCache = false; - preSets.saveToCache = false; - preSets.emergencyCache = false; - } - - // Enable text filtering by default. - data.moodlewssettingfilter = preSets.filter === false ? false : true; - data.moodlewssettingfileurl = preSets.rewriteurls === false ? false : true; - - const originalData = data; - - // Convert arguments to strings before starting the cache process. - data = this.wsProvider.convertValuesToString(data, wsPreSets.cleanUnicode); - if (data == null) { - // Empty cleaned text found. - return Promise.reject(this.utils.createFakeWSError('core.unicodenotsupportedcleanerror', true)); - } - - const cacheId = this.getCacheId(method, data); - - // Check for an ongoing identical request if we're not ignoring cache. - if (preSets.getFromCache && this.ongoingRequests[cacheId]) { - return this.ongoingRequests[cacheId].then((response) => { - // Clone the data, this may prevent errors if in the callback the object is modified. - return this.utils.clone(response); - }); - } - - const promise = this.getFromCache(method, data, preSets, false, originalData).catch(() => { - if (preSets.forceOffline) { - // Don't call the WS, just fail. - return Promise.reject(this.wsProvider.createFakeWSError('core.cannotconnect', true, - {$a: CoreSite.MINIMUM_MOODLE_VERSION})); - } - - // Call the WS. - return this.callOrEnqueueRequest(method, data, preSets, wsPreSets).then((response) => { - if (preSets.saveToCache) { - this.saveToCache(method, data, response, preSets); - } - - return response; - }).catch((error) => { - if (error.errorcode == 'invalidtoken' || - (error.errorcode == 'accessexception' && error.message.indexOf('Invalid token - token expired') > -1)) { - if (initialToken !== this.token && !retrying) { - // Token has changed, retry with the new token. - preSets.getFromCache = false; // Don't check cache now. Also, it will skip ongoingRequests. - - return this.request(method, data, preSets, true); - } else if (this.appProvider.isSSOAuthenticationOngoing()) { - // There's an SSO authentication ongoing, wait for it to finish and try again. - return this.appProvider.waitForSSOAuthentication().then(() => { - return this.request(method, data, preSets, true); - }); - } - - // Session expired, trigger event. - this.eventsProvider.trigger(CoreEventsProvider.SESSION_EXPIRED, {}, this.id); - // Change error message. Try to get data from cache, the event will handle the error. - error.message = this.translate.instant('core.lostconnection'); - } else if (error.errorcode === 'userdeleted') { - // User deleted, trigger event. - this.eventsProvider.trigger(CoreEventsProvider.USER_DELETED, { params: data }, this.id); - error.message = this.translate.instant('core.userdeleted'); - - return Promise.reject(error); - } else if (error.errorcode === 'forcepasswordchangenotice') { - // Password Change Forced, trigger event. Try to get data from cache, the event will handle the error. - this.eventsProvider.trigger(CoreEventsProvider.PASSWORD_CHANGE_FORCED, {}, this.id); - error.message = this.translate.instant('core.forcepasswordchangenotice'); - - } else if (error.errorcode === 'usernotfullysetup') { - // User not fully setup, trigger event. Try to get data from cache, the event will handle the error. - this.eventsProvider.trigger(CoreEventsProvider.USER_NOT_FULLY_SETUP, {}, this.id); - error.message = this.translate.instant('core.usernotfullysetup'); - - } else if (error.errorcode === 'sitepolicynotagreed') { - // Site policy not agreed, trigger event. - this.eventsProvider.trigger(CoreEventsProvider.SITE_POLICY_NOT_AGREED, {}, this.id); - error.message = this.translate.instant('core.login.sitepolicynotagreederror'); - - return Promise.reject(error); - } else if (error.errorcode === 'dmlwriteexception' && this.textUtils.hasUnicodeData(data)) { - if (!this.cleanUnicode) { - // Try again cleaning unicode. - this.cleanUnicode = true; - - return this.request(method, data, preSets); - } - // This should not happen. - error.message = this.translate.instant('core.unicodenotsupported'); - - return Promise.reject(error); - } else if (error.exception === 'required_capability_exception' || error.errorcode === 'nopermission' || - error.errorcode === 'notingroup') { - // Translate error messages with missing strings. - if (error.message === 'error/nopermission') { - error.message = this.translate.instant('core.nopermissionerror'); - } else if (error.message === 'error/notingroup') { - error.message = this.translate.instant('core.notingroup'); - } - - // Save the error instead of deleting the cache entry so the same content is displayed in offline. - this.saveToCache(method, data, error, preSets); - - return Promise.reject(error); - } else if (preSets.cacheErrors && preSets.cacheErrors.indexOf(error.errorcode) != -1) { - // Save the error instead of deleting the cache entry so the same content is displayed in offline. - this.saveToCache(method, data, error, preSets); - - return Promise.reject(error); - } else if (typeof preSets.emergencyCache !== 'undefined' && !preSets.emergencyCache) { - this.logger.debug(`WS call '${method}' failed. Emergency cache is forbidden, rejecting.`); - - return Promise.reject(error); - } - - if (preSets.deleteCacheIfWSError && this.utils.isWebServiceError(error)) { - // Delete the cache entry and return the entry. Don't block the user with the delete. - this.deleteFromCache(method, data, preSets).catch(() => { - // Ignore errors. - }); - - return Promise.reject(error); - } - - this.logger.debug(`WS call '${method}' failed. Trying to use the emergency cache.`); - preSets.omitExpires = true; - preSets.getFromCache = true; - - return this.getFromCache(method, data, preSets, true, originalData).catch(() => { - return Promise.reject(error); - }); - }); - }).then((response) => { - // Check if the response is an error, this happens if the error was stored in the cache. - if (response && (typeof response.exception != 'undefined' || typeof response.errorcode != 'undefined')) { - return Promise.reject(response); - } - - return response; - }); - - this.ongoingRequests[cacheId] = promise; - - // Clear ongoing request after setting the promise (just in case it's already resolved). - return promise.finally(() => { - // Make sure we don't clear the promise of a newer request that ignores the cache. - if (this.ongoingRequests[cacheId] === promise) { - delete this.ongoingRequests[cacheId]; - } - }).then((response) => { - // We pass back a clone of the original object, this may prevent errors if in the callback the object is modified. - return this.utils.clone(response); - }); - } - - /** - * Adds a request to the queue or calls it immediately when not using the queue. - * - * @param method The WebService method to be called. - * @param data Arguments to pass to the method. - * @param preSets Extra options related to the site. - * @param wsPreSets Extra options related to the WS call. - * @return Promise resolved with the response when the WS is called. - */ - protected callOrEnqueueRequest(method: string, data: any, preSets: CoreSiteWSPreSets, wsPreSets: CoreWSPreSets): Promise { - if (preSets.skipQueue || !this.wsAvailable('tool_mobile_call_external_functions')) { - return this.wsProvider.call(method, data, wsPreSets); - } - - const cacheId = this.getCacheId(method, data); - - // Check if there is an identical request waiting in the queue (read requests only by default). - if (preSets.reusePending) { - const request = this.requestQueue.find((request) => request.cacheId == cacheId); - if (request) { - return request.deferred.promise; - } - } - - const request: RequestQueueItem = { - cacheId, - method, - data, - preSets, - wsPreSets, - deferred: {} - }; - - request.deferred.promise = new Promise((resolve, reject): void => { - request.deferred.resolve = resolve; - request.deferred.reject = reject; - }); - - return this.enqueueRequest(request); - } - - /** - * Adds a request to the queue. - * - * @param request The request to enqueue. - * @return Promise resolved with the response when the WS is called. - */ - protected enqueueRequest(request: RequestQueueItem): Promise { - - this.requestQueue.push(request); - - if (this.requestQueue.length >= CoreSite.REQUEST_QUEUE_LIMIT) { - this.processRequestQueue(); - } else if (!this.requestQueueTimeout) { - this.requestQueueTimeout = setTimeout(this.processRequestQueue.bind(this), CoreSite.REQUEST_QUEUE_DELAY); - } - - return request.deferred.promise; - } - - /** - * Call the enqueued web service requests. - */ - protected processRequestQueue(): void { - this.logger.debug(`Processing request queue (${this.requestQueue.length} requests)`); - - // Clear timeout if set. - if (this.requestQueueTimeout) { - clearTimeout(this.requestQueueTimeout); - this.requestQueueTimeout = null; - } - - // Extract all requests from the queue. - const requests = this.requestQueue; - this.requestQueue = []; - - if (requests.length == 1 && !CoreSite.REQUEST_QUEUE_FORCE_WS) { - // Only one request, do a regular web service call. - this.wsProvider.call(requests[0].method, requests[0].data, requests[0].wsPreSets).then((data) => { - requests[0].deferred.resolve(data); - }).catch((error) => { - requests[0].deferred.reject(error); - }); - - return; - } - - const data = { - requests: requests.map((request) => { - const args = {}; - const settings = {}; - - // Separate WS settings from function arguments. - Object.keys(request.data).forEach((key) => { - let value = request.data[key]; - const match = /^moodlews(setting.*)$/.exec(key); - if (match) { - if (match[1] == 'settingfilter' || match[1] == 'settingfileurl') { - // Undo special treatment of these settings in CoreWSProvider.convertValuesToString. - value = (value == 'true' ? '1' : '0'); - } - settings[match[1]] = value; - } else { - args[key] = value; - } - }); - - return { - function: request.method, - arguments: JSON.stringify(args), - ...settings - }; - }) - }; - - const wsPresets: CoreWSPreSets = { - siteUrl: this.siteUrl, - wsToken: this.token, - }; - - this.wsProvider.call('tool_mobile_call_external_functions', data, wsPresets).then((data) => { - if (!data || !data.responses) { - return Promise.reject(null); - } - - requests.forEach((request, i) => { - const response = data.responses[i]; - - if (!response) { - // Request not executed, enqueue again. - this.enqueueRequest(request); - } else if (response.error) { - request.deferred.reject(this.textUtils.parseJSON(response.exception)); - } else { - let responseData = this.textUtils.parseJSON(response.data); - // Match the behaviour of CoreWSProvider.call when no response is expected. - const responseExpected = typeof wsPresets.responseExpected == 'undefined' || wsPresets.responseExpected; - if (!responseExpected && (responseData == null || responseData === '')) { - responseData = {}; - } - request.deferred.resolve(responseData); - } - }); - - }).catch((error) => { - // Error not specific to a single request, reject all promises. - requests.forEach((request) => { - request.deferred.reject(error); - }); - }); - } - - /** - * Check if a WS is available in this site. - * - * @param method WS name. - * @param checkPrefix When true also checks with the compatibility prefix. - * @return Whether the WS is available. - */ - wsAvailable(method: string, checkPrefix: boolean = true): boolean { - if (typeof this.infos == 'undefined') { - return false; - } - - if (this.infos.functionsByName[method]) { - return true; - } - - // Let's try again with the compatibility prefix. - if (checkPrefix) { - return this.wsAvailable(CoreConstants.WS_PREFIX + method, false); - } - - return false; - } - - /** - * Get cache ID. - * - * @param method The WebService method. - * @param data Arguments to pass to the method. - * @return Cache ID. - */ - protected getCacheId(method: string, data: any): string { - return Md5.hashAsciiStr(method + ':' + this.utils.sortAndStringify(data)); - } - - /** - * Get the cache ID used in Ionic 1 version of the app. - * - * @param method The WebService method. - * @param data Arguments to pass to the method. - * @return Cache ID. - */ - protected getCacheOldId(method: string, data: any): string { - return Md5.hashAsciiStr(method + ':' + JSON.stringify(data)); - } - - /** - * Get a WS response from cache. - * - * @param method The WebService method to be called. - * @param data Arguments to pass to the method. - * @param preSets Extra options. - * @param emergency Whether it's an "emergency" cache call (WS call failed). - * @param originalData Arguments to pass to the method before being converted to strings. - * @return Promise resolved with the WS response. - */ - protected getFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, emergency?: boolean, originalData?: any) - : Promise { - if (!this.db || !preSets.getFromCache) { - return Promise.reject(null); - } - - const id = this.getCacheId(method, data); - let promise; - - if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) { - promise = this.db.getRecords(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }).then((entries) => { - if (!entries.length) { - // Cache key not found, get by params sent. - return this.db.getRecord(CoreSite.WS_CACHE_TABLE, { id: id }); - } else if (entries.length > 1) { - // More than one entry found. Search the one with same ID as this call. - for (let i = 0, len = entries.length; i < len; i++) { - const entry = entries[i]; - if (entry.id == id) { - return entry; - } - } - } - - return entries[0]; - }); - } else { - promise = this.db.getRecord(CoreSite.WS_CACHE_TABLE, { id: id }).catch(() => { - // Entry not found, try to get it using the old ID. - const oldId = this.getCacheOldId(method, originalData || {}); - - return this.db.getRecord(CoreSite.WS_CACHE_TABLE, { id: oldId }).then((entry) => { - // Update the entry ID to use the new one. - this.db.updateRecords(CoreSite.WS_CACHE_TABLE, {id: id}, {id: oldId}); - - return entry; - }); - }); - } - - return promise.then((entry) => { - const now = Date.now(); - let expirationTime; - - preSets.omitExpires = preSets.omitExpires || preSets.forceOffline || !this.appProvider.isOnline(); - - if (!preSets.omitExpires) { - expirationTime = entry.expirationTime + this.getExpirationDelay(preSets.updateFrequency); - - if (now > expirationTime) { - this.logger.debug('Cached element found, but it is expired'); - - return Promise.reject(null); - } - } - - if (typeof entry != 'undefined' && typeof entry.data != 'undefined') { - if (!expirationTime) { - this.logger.info(`Cached element found, id: ${id}. Expiration time ignored.`); - } else { - const expires = (expirationTime - now) / 1000; - this.logger.info(`Cached element found, id: ${id}. Expires in expires in ${expires} seconds`); - } - - return this.textUtils.parseJSON(entry.data, {}); - } - - return Promise.reject(null); - }); - } - - /** - * Gets the size of cached data for a specific component or component instance. - * - * @param component Component name - * @param componentId Optional component id (if not included, returns sum for whole component) - * @return Promise resolved when we have calculated the size - */ - getComponentCacheSize(component: string, componentId?: number): Promise { - const params: any[] = [component]; - let extraClause = ''; - if (componentId !== undefined && componentId !== null) { - params.push(componentId); - extraClause = ' AND componentId = ?'; - } - - return this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE + - ' WHERE component = ?' + extraClause, params); - } - - /** - * Save a WS response to cache. - * - * @param method The WebService method. - * @param data Arguments to pass to the method. - * @param response The WS response. - * @param preSets Extra options. - * @return Promise resolved when the response is saved. - */ - protected saveToCache(method: string, data: any, response: any, preSets: CoreSiteWSPreSets): Promise { - if (!this.db) { - return Promise.reject(null); - } - - let promise; - - if (preSets.uniqueCacheKey) { - // Cache key must be unique, delete all entries with same cache key. - promise = this.deleteFromCache(method, data, preSets, true).catch(() => { - // Ignore errors. - }); - } else { - promise = Promise.resolve(); - } - - return promise.then(() => { - // Since 3.7, the expiration time contains the time the entry is modified instead of the expiration time. - // We decided to reuse this field to prevent modifying the database table. - const id = this.getCacheId(method, data), - entry: any = { - id: id, - data: JSON.stringify(response), - expirationTime: Date.now() - }; - - if (preSets.cacheKey) { - entry.key = preSets.cacheKey; - } - - if (preSets.component) { - entry.component = preSets.component; - if (preSets.componentId) { - entry.componentId = preSets.componentId; - } - } - - return this.db.insertRecord(CoreSite.WS_CACHE_TABLE, entry); - }); - } - - /** - * Delete a WS cache entry or entries. - * - * @param method The WebService method to be called. - * @param data Arguments to pass to the method. - * @param preSets Extra options. - * @param allCacheKey True to delete all entries with the cache key, false to delete only by ID. - * @return Promise resolved when the entries are deleted. - */ - protected deleteFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, allCacheKey?: boolean): Promise { - if (!this.db) { - return Promise.reject(null); - } - - const id = this.getCacheId(method, data); - - if (allCacheKey) { - return this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }); - } - - return this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, { id: id }); - } - - /** - * Deletes WS cache entries for all methods relating to a specific component (and - * optionally component id). - * - * @param component Component name. - * @param componentId Component id. - * @return Promise resolved when the entries are deleted. - */ - async deleteComponentFromCache(component: string, componentId?: number): Promise { - if (!component) { - return; - } - - if (!this.db) { - throw new Error('Site DB not initialized'); - } - - const params = { - component: component - } as any; - if (componentId) { - params.componentId = componentId; - } - - return this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, params); - } - - /* - * Uploads a file using Cordova File API. - * - * @param filePath File path. - * @param options File upload options. - * @param onProgress Function to call on progress. - * @return Promise resolved when uploaded. - */ - uploadFile(filePath: string, options: CoreWSFileUploadOptions, onProgress?: (event: ProgressEvent) => any): Promise { - if (!options.fileArea) { - options.fileArea = 'draft'; - } - - return this.wsProvider.uploadFile(filePath, options, { - siteUrl: this.siteUrl, - wsToken: this.token - }, onProgress); - } - - /** - * Invalidates all the cache entries. - * - * @return Promise resolved when the cache entries are invalidated. - */ - invalidateWsCache(): Promise { - if (!this.db) { - return Promise.reject(null); - } - - this.logger.debug('Invalidate all the cache for site: ' + this.id); - - return this.db.updateRecords(CoreSite.WS_CACHE_TABLE, { expirationTime: 0 }).finally(() => { - this.eventsProvider.trigger(CoreEventsProvider.WS_CACHE_INVALIDATED, {}, this.getId()); - }); - } - - /** - * Invalidates all the cache entries with a certain key. - * - * @param key Key to search. - * @return Promise resolved when the cache entries are invalidated. - */ - invalidateWsCacheForKey(key: string): Promise { - if (!this.db) { - return Promise.reject(null); - } - if (!key) { - return Promise.resolve(); - } - - this.logger.debug('Invalidate cache for key: ' + key); - - return this.db.updateRecords(CoreSite.WS_CACHE_TABLE, { expirationTime: 0 }, { key: key }); - } - - /** - * Invalidates all the cache entries in an array of keys. - * - * @param keys Keys to search. - * @return Promise resolved when the cache entries are invalidated. - */ - invalidateMultipleWsCacheForKey(keys: string[]): Promise { - if (!this.db) { - return Promise.reject(null); - } - if (!keys || !keys.length) { - return Promise.resolve(); - } - - const promises = []; - - this.logger.debug('Invalidating multiple cache keys'); - keys.forEach((key) => { - promises.push(this.invalidateWsCacheForKey(key)); - }); - - return Promise.all(promises); - } - - /** - * Invalidates all the cache entries whose key starts with a certain value. - * - * @param key Key to search. - * @return Promise resolved when the cache entries are invalidated. - */ - invalidateWsCacheForKeyStartingWith(key: string): Promise { - if (!this.db) { - return Promise.reject(null); - } - if (!key) { - return Promise.resolve(); - } - - this.logger.debug('Invalidate cache for key starting with: ' + key); - - const sql = 'UPDATE ' + CoreSite.WS_CACHE_TABLE + ' SET expirationTime=0 WHERE key LIKE ?'; - - return this.db.execute(sql, [key + '%']); - } - - /** - * Check if tokenpluginfile can be used, and fix the URL afterwards. - * - * @param url The url to be fixed. - * @return Promise resolved with the fixed URL. - */ - checkAndFixPluginfileURL(url: string): Promise { - return this.checkTokenPluginFile(url).then(() => { - return this.fixPluginfileURL(url); - }); - } - - /** - * Generic function for adding the wstoken to Moodle urls and for pointing to the correct script. - * Uses CoreUtilsProvider.fixPluginfileURL, passing site's token. - * - * @param url The url to be fixed. - * @return Fixed URL. - */ - fixPluginfileURL(url: string): string { - const accessKey = this.tokenPluginFileWorks || typeof this.tokenPluginFileWorks == 'undefined' ? - this.infos && this.infos.userprivateaccesskey : undefined; - - return this.urlUtils.fixPluginfileURL(url, this.token, this.siteUrl, accessKey); - } - - /** - * Deletes site's DB. - * - * @return Promise to be resolved when the DB is deleted. - */ - deleteDB(): Promise { - return this.dbProvider.deleteDB('Site-' + this.id); - } - - /** - * Deletes site's folder. - * - * @return Promise to be resolved when the DB is deleted. - */ - deleteFolder(): Promise { - if (this.fileProvider.isAvailable()) { - const siteFolder = this.fileProvider.getSiteFolder(this.id); - - return this.fileProvider.removeDir(siteFolder).catch(() => { - // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists. - }); - } else { - return Promise.resolve(); - } - } - - /** - * Get space usage of the site. - * - * @return Promise resolved with the site space usage (size). - */ - getSpaceUsage(): Promise { - if (this.fileProvider.isAvailable()) { - const siteFolderPath = this.fileProvider.getSiteFolder(this.id); - - return this.fileProvider.getDirectorySize(siteFolderPath).catch(() => { - return 0; - }); - } else { - return Promise.resolve(0); - } - } - - /** - * Gets an approximation of the cache table usage of the site. - * - * Currently this is just the total length of the data fields in the cache table. - * - * @return Promise resolved with the total size of all data in the cache table (bytes) - */ - getCacheUsage(): Promise { - return this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE); - } - - /** - * Gets a total of the file and cache usage. - * - * @return Promise with the total of getSpaceUsage and getCacheUsage - */ - async getTotalUsage(): Promise { - const space = await this.getSpaceUsage(); - const cache = await this.getCacheUsage(); - - return space + cache; - } - - /** - * Returns the URL to the documentation of the app, based on Moodle version and current language. - * - * @param page Docs page to go to. - * @return Promise resolved with the Moodle docs URL. - */ - getDocsUrl(page?: string): Promise { - const release = this.infos.release ? this.infos.release : undefined; - - return this.urlUtils.getDocsUrl(release, page); - } - - /** - * Returns a url to link an specific page on the site. - * - * @param path Path of the url to go to. - * @param params Object with the params to add. - * @param anchor Anchor text if needed. - * @return URL with params. - */ - createSiteUrl(path: string, params?: {[key: string]: any}, anchor?: string): string { - return this.urlUtils.addParamsToUrl(this.siteUrl + path, params, anchor); - } - - /** - * Check if the local_mobile plugin is installed in the Moodle site. - * - * @param retrying True if we're retrying the check. - * @return Promise resolved when the check is done. - */ - async checkLocalMobilePlugin(retrying?: boolean): Promise { - const checkUrl = this.siteUrl + '/local/mobile/check.php', - service = CoreConfigConstants.wsextservice; - - if (!service) { - // External service not defined. - return { code: 0 }; - } - - let data; - - try { - const response = await this.wsProvider.sendHTTPRequest(checkUrl, { - method: 'post', - data: { service: service }, - }); - - data = response.body; - } catch (ex) { - return { code: 0 }; - } - - if (data === null) { - // This probably means that the server was configured to return null for non-existing URLs. Not installed. - return { code: 0 }; - } - - if (typeof data != 'undefined' && data.errorcode === 'requirecorrectaccess') { - if (!retrying) { - this.siteUrl = this.urlUtils.addOrRemoveWWW(this.siteUrl); - - return this.checkLocalMobilePlugin(true); - } else { - throw data.error; - } - } else if (typeof data == 'undefined' || typeof data.code == 'undefined') { - // The local_mobile returned something we didn't expect. Let's assume it's not installed. - return { code: 0, warning: 'core.login.localmobileunexpectedresponse' }; - } - - const code = parseInt(data.code, 10); - if (data.error) { - switch (code) { - case 1: - // Site in maintenance mode. - throw this.translate.instant('core.login.siteinmaintenance'); - case 2: - // Web services not enabled. - throw this.translate.instant('core.login.webservicesnotenabled'); - case 3: - // Extended service not enabled, but the official is enabled. - return { code: 0 }; - case 4: - // Neither extended or official services enabled. - throw this.translate.instant('core.login.mobileservicesnotenabled'); - default: - throw this.translate.instant('core.unexpectederror'); - } - } else { - return { code: code, service: service, coreSupported: !!data.coresupported }; - } - } - - /** - * Check if local_mobile has been installed in Moodle. - * - * @return Whether the App is able to use local_mobile plugin for this site. - */ - checkIfAppUsesLocalMobile(): boolean { - let appUsesLocalMobile = false; - - if (!this.infos || !this.infos.functions) { - return appUsesLocalMobile; - } - - this.infos.functions.forEach((func) => { - if (func.name.indexOf(CoreConstants.WS_PREFIX) != -1) { - appUsesLocalMobile = true; - } - }); - - return appUsesLocalMobile; - } - - /** - * Check if local_mobile has been installed in Moodle but the app is not using it. - * - * @return Promise resolved it local_mobile was added, rejected otherwise. - */ - checkIfLocalMobileInstalledAndNotUsed(): Promise { - const appUsesLocalMobile = this.checkIfAppUsesLocalMobile(); - - if (appUsesLocalMobile) { - // App already uses local_mobile, it wasn't added. - return Promise.reject(null); - } - - return this.checkLocalMobilePlugin().then((data: LocalMobileResponse): any => { - if (typeof data.service == 'undefined') { - // The local_mobile NOT installed. Reject. - return Promise.reject(null); - } - - return data; - }); - } - - /** - * Check if a URL belongs to this site. - * - * @param url URL to check. - * @return Whether the URL belongs to this site. - */ - containsUrl(url: string): boolean { - if (!url) { - return false; - } - - const siteUrl = this.textUtils.addEndingSlash(this.urlUtils.removeProtocolAndWWW(this.siteUrl)); - url = this.textUtils.addEndingSlash(this.urlUtils.removeProtocolAndWWW(url)); - - return url.indexOf(siteUrl) == 0; - } - - /** - * Get the public config of this site. - * - * @return Promise resolved with public config. Rejected with an object if error, see CoreWSProvider.callAjax. - */ - getPublicConfig(): Promise { - const preSets: CoreWSAjaxPreSets = { - siteUrl: this.siteUrl - }; - - return this.wsProvider.callAjax('tool_mobile_get_public_config', {}, preSets).catch((error) => { - - if ((!this.getInfo() || this.isVersionGreaterEqualThan('3.8')) && error && error.errorcode == 'codingerror') { - // This error probably means that there is a redirect in the site. Try to use a GET request. - preSets.noLogin = true; - preSets.useGet = true; - - return this.wsProvider.callAjax('tool_mobile_get_public_config', {}, preSets).catch((error2) => { - if (this.getInfo() && this.isVersionGreaterEqualThan('3.8')) { - // GET is supported, return the second error. - return Promise.reject(error2); - } else { - // GET not supported or we don't know if it's supported. Return first error. - return Promise.reject(error); - } - }); - } - - return Promise.reject(error); - }).then((config) => { - // Use the wwwroot returned by the server. - if (config.httpswwwroot) { - this.siteUrl = config.httpswwwroot; - } - - return config; - }); - } - - /** - * Open a URL in browser using auto-login in the Moodle site if available. - * - * @param url The URL to open. - * @param alertMessage If defined, an alert will be shown before opening the browser. - * @return Promise resolved when done, rejected otherwise. - */ - openInBrowserWithAutoLogin(url: string, alertMessage?: string): Promise { - return this.openWithAutoLogin(false, url, undefined, alertMessage); - } - - /** - * Open a URL in browser using auto-login in the Moodle site if available and the URL belongs to the site. - * - * @param url The URL to open. - * @param alertMessage If defined, an alert will be shown before opening the browser. - * @return Promise resolved when done, rejected otherwise. - */ - openInBrowserWithAutoLoginIfSameSite(url: string, alertMessage?: string): Promise { - return this.openWithAutoLoginIfSameSite(false, url, undefined, alertMessage); - } - - /** - * Open a URL in inappbrowser using auto-login in the Moodle site if available. - * - * @param url The URL to open. - * @param options Override default options passed to InAppBrowser. - * @param alertMessage If defined, an alert will be shown before opening the inappbrowser. - * @return Promise resolved when done. - */ - openInAppWithAutoLogin(url: string, options?: any, alertMessage?: string): Promise { - return this.openWithAutoLogin(true, url, options, alertMessage); - } - - /** - * Open a URL in inappbrowser using auto-login in the Moodle site if available and the URL belongs to the site. - * - * @param url The URL to open. - * @param options Override default options passed to inappbrowser. - * @param alertMessage If defined, an alert will be shown before opening the inappbrowser. - * @return Promise resolved when done. - */ - openInAppWithAutoLoginIfSameSite(url: string, options?: any, alertMessage?: string): Promise { - return this.openWithAutoLoginIfSameSite(true, url, options, alertMessage); - } - - /** - * Open a URL in browser or InAppBrowser using auto-login in the Moodle site if available. - * - * @param inApp True to open it in InAppBrowser, false to open in browser. - * @param url The URL to open. - * @param options Override default options passed to $cordovaInAppBrowser#open. - * @param alertMessage If defined, an alert will be shown before opening the browser/inappbrowser. - * @return Promise resolved when done. Resolve param is returned only if inApp=true. - */ - openWithAutoLogin(inApp: boolean, url: string, options?: any, alertMessage?: string): Promise { - // Get the URL to open. - return this.getAutoLoginUrl(url).then((url) => { - if (!alertMessage) { - // Just open the URL. - if (inApp) { - return this.utils.openInApp(url, options); - } else { - return this.utils.openInBrowser(url); - } - } - - // Show an alert first. - return this.domUtils.showAlert(this.translate.instant('core.notice'), alertMessage, undefined, 3000).then((alert) => { - - return new Promise((resolve, reject): void => { - const subscription = alert.didDismiss.subscribe(() => { - subscription && subscription.unsubscribe(); - - if (inApp) { - resolve(this.utils.openInApp(url, options)); - } else { - resolve(this.utils.openInBrowser(url)); - } - }); - }); - }); - }); - } - - /** - * Open a URL in browser or InAppBrowser using auto-login in the Moodle site if available and the URL belongs to the site. - * - * @param inApp True to open it in InAppBrowser, false to open in browser. - * @param url The URL to open. - * @param options Override default options passed to inappbrowser. - * @param alertMessage If defined, an alert will be shown before opening the browser/inappbrowser. - * @return Promise resolved when done. Resolve param is returned only if inApp=true. - */ - openWithAutoLoginIfSameSite(inApp: boolean, url: string, options?: any, alertMessage?: string) - : Promise { - if (this.containsUrl(url)) { - return this.openWithAutoLogin(inApp, url, options, alertMessage); - } else { - if (inApp) { - this.utils.openInApp(url, options); - } else { - this.utils.openInBrowser(url); - } - - return Promise.resolve(null); - } - } - - /** - * Get the config of this site. - * It is recommended to use getStoredConfig instead since it's faster and doesn't use network. - * - * @param name Name of the setting to get. If not set or false, all settings will be returned. - * @param ignoreCache True if it should ignore cached data. - * @return Promise resolved with site config. - */ - getConfig(name?: string, ignoreCache?: boolean): Promise { - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getConfigCacheKey() - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - return this.read('tool_mobile_get_config', {}, preSets).then((config) => { - if (name) { - // Return the requested setting. - for (const x in config.settings) { - if (config.settings[x].name == name) { - return config.settings[x].value; - } - } - - return Promise.reject(null); - } else { - // Return all settings in the same array. - const settings = {}; - config.settings.forEach((setting) => { - settings[setting.name] = setting.value; - }); - - return settings; - } - }); - } - - /** - * Invalidates config WS call. - * - * @return Promise resolved when the data is invalidated. - */ - invalidateConfig(): Promise { - return this.invalidateWsCacheForKey(this.getConfigCacheKey()); - } - - /** - * Get cache key for getConfig WS calls. - * - * @return Cache key. - */ - protected getConfigCacheKey(): string { - return 'tool_mobile_get_config'; - } - - /** - * Get the stored config of this site. - * - * @param name Name of the setting to get. If not set, all settings will be returned. - * @return Site config or a specific setting. - */ - getStoredConfig(name?: string): any { - if (!this.config) { - return; - } - - if (name) { - return this.config[name]; - } else { - return this.config; - } - } - - /** - * Check if a certain feature is disabled in the site. - * - * @param name Name of the feature to check. - * @return Whether it's disabled. - */ - isFeatureDisabled(name: string): boolean { - const disabledFeatures = this.getStoredConfig('tool_mobile_disabledfeatures'); - if (!disabledFeatures) { - return false; - } - - const regEx = new RegExp('(,|^)' + this.textUtils.escapeForRegex(name) + '(,|$)', 'g'); - - return !!disabledFeatures.match(regEx); - } - - /** - * Calculate if offline is disabled in the site. - */ - calculateOfflineDisabled(): void { - this.offlineDisabled = this.isFeatureDisabled('NoDelegate_CoreOffline'); - } - - /** - * Get whether offline is disabled in the site. - * - * @return Whether it's disabled. - */ - isOfflineDisabled(): boolean { - return this.offlineDisabled; - } - - /** - * Check if the site version is greater than one or several versions. - * This function accepts a string or an array of strings. If array, the last version must be the highest. - * - * @param versions Version or list of versions to check. - * @return Whether it's greater or equal, false otherwise. - * @description - * If a string is supplied (e.g. '3.2.1'), it will check if the site version is greater or equal than this version. - * - * If an array of versions is supplied, it will check if the site version is greater or equal than the last version, - * or if it's higher or equal than any of the other releases supplied but lower than the next major release. The last - * version of the array must be the highest version. - * For example, if the values supplied are ['3.0.5', '3.2.3', '3.3.1'] the function will return true if the site version - * is either: - * - Greater or equal than 3.3.1. - * - Greater or equal than 3.2.3 but lower than 3.3. - * - Greater or equal than 3.0.5 but lower than 3.1. - * - * This function only accepts versions from 2.4.0 and above. If any of the versions supplied isn't found, it will assume - * it's the last released major version. - */ - isVersionGreaterEqualThan(versions: string | string[]): boolean { - const siteVersion = parseInt(this.getInfo().version, 10); - - if (Array.isArray(versions)) { - if (!versions.length) { - return false; - } - - for (let i = 0; i < versions.length; i++) { - const versionNumber = this.getVersionNumber(versions[i]); - if (i == versions.length - 1) { - // It's the last version, check only if site version is greater than this one. - return siteVersion >= versionNumber; - } else { - // Check if site version if bigger than this number but lesser than next major. - if (siteVersion >= versionNumber && siteVersion < this.getNextMajorVersionNumber(versions[i])) { - return true; - } - } - } - } else if (typeof versions == 'string') { - // Compare with this version. - return siteVersion >= this.getVersionNumber(versions); - } - - return false; - } - - /** - * Given a URL, convert it to a URL that will auto-login if supported. - * - * @param url The URL to convert. - * @param showModal Whether to show a loading modal. - * @return Promise resolved with the converted URL. - */ - getAutoLoginUrl(url: string, showModal: boolean = true): Promise { - - if (!this.privateToken || !this.wsAvailable('tool_mobile_get_autologin_key') || - (this.lastAutoLogin && this.timeUtils.timestamp() - this.lastAutoLogin < CoreConstants.SECONDS_MINUTE * 6)) { - // No private token, WS not available or last auto-login was less than 6 minutes ago. Don't change the URL. - - return Promise.resolve(url); - } - - const userId = this.getUserId(), - params = { - privatetoken: this.privateToken - }; - let modal; - - if (showModal) { - modal = this.domUtils.showModalLoading(); - } - - // Use write to not use cache. - return this.write('tool_mobile_get_autologin_key', params).then((data) => { - - if (!data.autologinurl || !data.key) { - // Not valid data, return the same URL. - return url; - } - - this.lastAutoLogin = this.timeUtils.timestamp(); - - return data.autologinurl + '?userid=' + userId + '&key=' + data.key + '&urltogo=' + encodeURIComponent(url); - }).catch(() => { - - // Couldn't get autologin key, return the same URL. - return url; - }).finally(() => { - modal && modal.dismiss(); - }); - } - - /** - * Get a version number from a release version. - * If release version is valid but not found in the list of Moodle releases, it will use the last released major version. - * - * @param version Release version to convert to version number. - * @return Version number, 0 if invalid. - */ - protected getVersionNumber(version: string): number { - const data = this.getMajorAndMinor(version); - - if (!data) { - // Invalid version. - return 0; - } - - if (typeof this.MOODLE_RELEASES[data.major] == 'undefined') { - // Major version not found. Use the last one. - data.major = Object.keys(this.MOODLE_RELEASES).slice(-1); - } - - return this.MOODLE_RELEASES[data.major] + data.minor; - } - - /** - * Given a release version, return the major and minor versions. - * - * @param version Release version (e.g. '3.1.0'). - * @return Object with major and minor. Returns false if invalid version. - */ - protected getMajorAndMinor(version: string): any { - const match = version.match(/(\d)+(?:\.(\d)+)?(?:\.(\d)+)?/); - if (!match || !match[1]) { - // Invalid version. - return false; - } - - return { - major: match[1] + '.' + (match[2] || '0'), - minor: parseInt(match[3], 10) || 0 - }; - } - - /** - * Given a release version, return the next major version number. - * - * @param version Release version (e.g. '3.1.0'). - * @return Next major version number. - */ - protected getNextMajorVersionNumber(version: string): number { - const data = this.getMajorAndMinor(version), - releases = Object.keys(this.MOODLE_RELEASES); - let position; - - if (!data) { - // Invalid version. - return 0; - } - - position = releases.indexOf(data.major); - - if (position == -1 || position == releases.length - 1) { - // Major version not found or it's the last one. Use the last one. - return this.MOODLE_RELEASES[releases[position]]; - } - - return this.MOODLE_RELEASES[releases[position + 1]]; - } - - /** - * Deletes a site setting. - * - * @param name The config name. - * @return Promise resolved when done. - */ - deleteSiteConfig(name: string): Promise { - return this.db.deleteRecords(CoreSite.CONFIG_TABLE, { name: name }); - } - - /** - * Get a site setting on local device. - * - * @param name The config name. - * @param defaultValue Default value to use if the entry is not found. - * @return Resolves upon success along with the config data. Reject on failure. - */ - getLocalSiteConfig(name: string, defaultValue?: any): Promise { - return this.db.getRecord(CoreSite.CONFIG_TABLE, { name: name }).then((entry) => { - return entry.value; - }).catch((error) => { - if (typeof defaultValue != 'undefined') { - return defaultValue; - } - - return Promise.reject(error); - }); - } - - /** - * Set a site setting on local device. - * - * @param name The config name. - * @param value The config value. Can only store number or strings. - * @return Promise resolved when done. - */ - setLocalSiteConfig(name: string, value: number | string): Promise { - return this.db.insertRecord(CoreSite.CONFIG_TABLE, { name: name, value: value }); - } - - /** - * Get a certain cache expiration delay. - * - * @param updateFrequency The update frequency of the entry. - * @return Expiration delay. - */ - getExpirationDelay(updateFrequency?: number): number { - let expirationDelay = this.UPDATE_FREQUENCIES[updateFrequency] || this.UPDATE_FREQUENCIES[CoreSite.FREQUENCY_USUALLY]; - - if (this.appProvider.isNetworkAccessLimited()) { - // Not WiFi, increase the expiration delay a 50% to decrease the data usage in this case. - expirationDelay *= 1.5; - } - - return expirationDelay; - } - - /* - * Check if tokenpluginfile script works in the site. - * - * @param url URL to check. - * @return Promise resolved with boolean: whether it works or not. - */ - checkTokenPluginFile(url: string): Promise { - if (!this.urlUtils.canUseTokenPluginFile(url, this.siteUrl, this.infos && this.infos.userprivateaccesskey)) { - // Cannot use tokenpluginfile. - return Promise.resolve(false); - } else if (typeof this.tokenPluginFileWorks != 'undefined') { - // Already checked. - return Promise.resolve(this.tokenPluginFileWorks); - } else if (this.tokenPluginFileWorksPromise) { - // Check ongoing, use the same promise. - return this.tokenPluginFileWorksPromise; - } else if (!this.appProvider.isOnline()) { - // Not online, cannot check it. Assume it's working, but don't save the result. - return Promise.resolve(true); - } - - url = this.fixPluginfileURL(url); - - this.tokenPluginFileWorksPromise = this.wsProvider.performHead(url).then((result) => { - return result.status >= 200 && result.status < 300; - }).catch((error) => { - // Error performing head request. - return false; - }).then((result) => { - this.tokenPluginFileWorks = result; - - return result; - }); - - return this.tokenPluginFileWorksPromise; - } -} diff --git a/src/classes/sqlitedb.ts b/src/classes/sqlitedb.ts deleted file mode 100644 index 239a5edcf..000000000 --- a/src/classes/sqlitedb.ts +++ /dev/null @@ -1,1037 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { SQLite, SQLiteObject } from '@ionic-native/sqlite'; -import { Platform } from 'ionic-angular'; - -/** - * Schema of a table. - */ -export interface SQLiteDBTableSchema { - /** - * The table name. - */ - name: string; - - /** - * The columns to create in the table. - */ - columns: SQLiteDBColumnSchema[]; - - /** - * Names of columns that are primary key. Use it for compound primary keys. - */ - primaryKeys?: string[]; - - /** - * List of sets of unique columns. E.g: [['section', 'title'], ['author', 'title']]. - */ - uniqueKeys?: string[][]; - - /** - * List of foreign keys. - */ - foreignKeys?: SQLiteDBForeignKeySchema[]; - - /** - * Check constraint for the table. - */ - tableCheck?: string; -} - -/** - * Schema of a column. - */ -export interface SQLiteDBColumnSchema { - /** - * Column's name. - */ - name: string; - - /** - * Column's type. - */ - type?: 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB'; - - /** - * Whether the column is a primary key. Use it only if primary key is a single column. - */ - primaryKey?: boolean; - - /** - * Whether it should be autoincremented. Only if primaryKey is true. - */ - autoIncrement?: boolean; - - /** - * True if column shouldn't be null. - */ - notNull?: boolean; - - /** - * WWhether the column is unique. - */ - unique?: boolean; - - /** - * Check constraint for the column. - */ - check?: string; - - /** - * Default value for the column. - */ - default?: string; -} - -/** - * Schema of a foreign key. - */ -export interface SQLiteDBForeignKeySchema { - /** - * Columns to include in this foreign key. - */ - columns: string[]; - - /** - * The external table referenced by this key. - */ - table: string; - - /** - * List of referenced columns from the referenced table. - */ - foreignColumns?: string[]; - - /** - * Text with the actions to apply to the foreign key. - */ - actions?: string; -} - -/** - * Class to interact with the local database. - * - * @description - * This class allows creating and interacting with a SQLite database. - * - * You need to supply some dependencies when creating the instance: - * this.db = new SQLiteDB('MyDB', sqlite, platform); - */ -export class SQLiteDB { - db: SQLiteObject; - promise: Promise; - - /** - * Create and open the database. - * - * @param name Database name. - * @param sqlite SQLite library. - * @param platform Ionic platform. - */ - constructor(public name: string, private sqlite: SQLite, private platform: Platform) { - this.init(); - } - - /** - * Helper function to create a table if it doesn't exist. - * - * @param name The table name. - * @param columns The columns to create in the table. - * @param primaryKeys Names of columns that are primary key. Use it for compound primary keys. - * @param uniqueKeys List of sets of unique columns. E.g: [['section', 'title'], ['author', 'title']]. - * @param foreignKeys List of foreign keys. - * @param tableCheck Check constraint for the table. - * @return SQL query. - */ - buildCreateTableSql(name: string, columns: SQLiteDBColumnSchema[], primaryKeys?: string[], uniqueKeys?: string[][], - foreignKeys?: SQLiteDBForeignKeySchema[], tableCheck?: string): string { - const columnsSql = []; - let sql = `CREATE TABLE IF NOT EXISTS ${name} (`; - - // First define all the columns. - for (const index in columns) { - const column = columns[index]; - let columnSql: string = column.name || ''; - - if (column.type) { - columnSql += ' ' + column.type; - } - - if (column.primaryKey) { - columnSql += ' PRIMARY KEY'; - if (column.autoIncrement) { - columnSql += ' AUTOINCREMENT'; - } - } - - if (column.notNull) { - columnSql += ' NOT NULL'; - } - - if (column.unique) { - columnSql += ' UNIQUE'; - } - - if (column.check) { - columnSql += ` CHECK (${column.check})`; - } - - if (typeof column.default != 'undefined') { - columnSql += ` DEFAULT ${column.default}`; - } - - columnsSql.push(columnSql); - } - sql += columnsSql.join(', '); - - // Now add the table constraints. - - if (primaryKeys && primaryKeys.length) { - sql += `, PRIMARY KEY (${primaryKeys.join(', ')})`; - } - - if (uniqueKeys && uniqueKeys.length) { - for (const index in uniqueKeys) { - const setOfKeys = uniqueKeys[index]; - if (setOfKeys && setOfKeys.length) { - sql += `, UNIQUE (${setOfKeys.join(', ')})`; - } - } - } - - if (tableCheck) { - sql += `, CHECK (${tableCheck})`; - } - - for (const index in foreignKeys) { - const foreignKey = foreignKeys[index]; - - if (!foreignKey.columns || !!foreignKey.columns.length) { - return; - } - - sql += `, FOREIGN KEY (${foreignKey.columns.join(', ')}) REFERENCES ${foreignKey.table} `; - - if (foreignKey.foreignColumns && foreignKey.foreignColumns.length) { - sql += `(${foreignKey.foreignColumns.join(', ')})`; - } - - if (foreignKey.actions) { - sql += ` ${foreignKey.actions}`; - } - } - - return sql + ')'; - } - - /** - * Close the database. - * - * @return Promise resolved when done. - */ - close(): Promise { - return this.ready().then(() => { - return this.db.close(); - }); - } - - /** - * Count the records in a table where all the given conditions met. - * - * @param table The table to query. - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @return Promise resolved with the count of records returned from the specified criteria. - */ - countRecords(table: string, conditions?: object): Promise { - const selectAndParams = this.whereClause(conditions); - - return this.countRecordsSelect(table, selectAndParams[0], selectAndParams[1]); - } - - /** - * Count the records in a table which match a particular WHERE clause. - * - * @param table The table to query. - * @param select A fragment of SQL to be used in a where clause in the SQL call. - * @param params An array of sql parameters. - * @param countItem The count string to be used in the SQL call. Default is COUNT('x'). - * @return Promise resolved with the count of records returned from the specified criteria. - */ - countRecordsSelect(table: string, select: string = '', params?: any, countItem: string = 'COUNT(\'x\')'): Promise { - if (select) { - select = 'WHERE ' + select; - } - - return this.countRecordsSql(`SELECT ${countItem} FROM ${table} ${select}`, params); - } - - /** - * Get the result of a SQL SELECT COUNT(...) query. - * - * Given a query that counts rows, return that count. - * - * @param sql The SQL string you wish to be executed. - * @param params An array of sql parameters. - * @return Promise resolved with the count. - */ - countRecordsSql(sql: string, params?: any): Promise { - return this.getFieldSql(sql, params).then((count) => { - if (typeof count != 'number' || count < 0) { - return 0; - } - - return count; - }); - } - - /** - * Create a table if it doesn't exist. - * - * @param name The table name. - * @param columns The columns to create in the table. - * @param primaryKeys Names of columns that are primary key. Use it for compound primary keys. - * @param uniqueKeys List of sets of unique columns. E.g: [['section', 'title'], ['author', 'title']]. - * @param foreignKeys List of foreign keys. - * @param tableCheck Check constraint for the table. - * @return Promise resolved when success. - */ - createTable(name: string, columns: SQLiteDBColumnSchema[], primaryKeys?: string[], uniqueKeys?: string[][], - foreignKeys?: SQLiteDBForeignKeySchema[], tableCheck?: string): Promise { - const sql = this.buildCreateTableSql(name, columns, primaryKeys, uniqueKeys, foreignKeys, tableCheck); - - return this.execute(sql); - } - - /** - * Create a table if it doesn't exist from a schema. - * - * @param table Table schema. - * @return Promise resolved when success. - */ - createTableFromSchema(table: SQLiteDBTableSchema): Promise { - return this.createTable(table.name, table.columns, table.primaryKeys, table.uniqueKeys, - table.foreignKeys, table.tableCheck); - } - - /** - * Create several tables if they don't exist from a list of schemas. - * - * @param tables List of table schema. - * @return Promise resolved when success. - */ - createTablesFromSchema(tables: SQLiteDBTableSchema[]): Promise { - const promises = []; - tables.forEach((table) => { - promises.push(this.createTableFromSchema(table)); - }); - - return Promise.all(promises); - } - - /** - * Delete the records from a table where all the given conditions met. - * If conditions not specified, table is truncated. - * - * @param table The table to delete from. - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @return Promise resolved when done. - */ - deleteRecords(table: string, conditions?: object): Promise { - if (conditions === null || typeof conditions == 'undefined') { - // No conditions, delete the whole table. - return this.execute(`DELETE FROM ${table}`); - } - - const selectAndParams = this.whereClause(conditions); - - return this.deleteRecordsSelect(table, selectAndParams[0], selectAndParams[1]); - } - - /** - * Delete the records from a table where one field match one list of values. - * - * @param table The table to delete from. - * @param field The name of a field. - * @param values The values field might take. - * @return Promise resolved when done. - */ - deleteRecordsList(table: string, field: string, values: any[]): Promise { - const selectAndParams = this.whereClauseList(field, values); - - return this.deleteRecordsSelect(table, selectAndParams[0], selectAndParams[1]); - } - - /** - * Delete one or more records from a table which match a particular WHERE clause. - * - * @param table The table to delete from. - * @param select A fragment of SQL to be used in a where clause in the SQL call. - * @param params Array of sql parameters. - * @return Promise resolved when done. - */ - deleteRecordsSelect(table: string, select: string = '', params?: any[]): Promise { - if (select) { - select = 'WHERE ' + select; - } - - return this.execute(`DELETE FROM ${table} ${select}`, params); - } - - /** - * Drop a table if it exists. - * - * @param name The table name. - * @return Promise resolved when success. - */ - dropTable(name: string): Promise { - return this.execute(`DROP TABLE IF EXISTS ${name}`); - } - - /** - * Execute a SQL query. - * IMPORTANT: Use this function only if you cannot use any of the other functions in this API. Please take into account that - * these query will be run in SQLite (Mobile) and Web SQL (desktop), so your query should work in both environments. - * - * @param sql SQL query to execute. - * @param params Query parameters. - * @return Promise resolved with the result. - */ - execute(sql: string, params?: any[]): Promise { - return this.ready().then(() => { - return this.db.executeSql(sql, params); - }); - } - - /** - * Execute a set of SQL queries. This operation is atomic. - * IMPORTANT: Use this function only if you cannot use any of the other functions in this API. Please take into account that - * these query will be run in SQLite (Mobile) and Web SQL (desktop), so your query should work in both environments. - * - * @param sqlStatements SQL statements to execute. - * @return Promise resolved with the result. - */ - executeBatch(sqlStatements: any[]): Promise { - return this.ready().then(() => { - return this.db.sqlBatch(sqlStatements); - }); - } - - /** - * Format the data to insert in the database. Removes undefined entries so they are stored as null instead of 'undefined'. - * - * @param data Data to insert. - */ - protected formatDataToInsert(data: object): void { - if (!data) { - return; - } - - // Remove undefined entries and convert null to "NULL". - for (const name in data) { - const value = data[name]; - if (typeof value == 'undefined') { - delete data[name]; - } - } - } - - /** - * Get all the records from a table. - * - * @param table The table to query. - * @return Promise resolved with the records. - */ - getAllRecords(table: string): Promise { - return this.getRecords(table); - } - - /** - * Get a single field value from a table record where all the given conditions met. - * - * @param table The table to query. - * @param field The field to return the value of. - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @return Promise resolved with the field's value. - */ - getField(table: string, field: string, conditions?: object): Promise { - const selectAndParams = this.whereClause(conditions); - - return this.getFieldSelect(table, field, selectAndParams[0], selectAndParams[1]); - } - - /** - * Get a single field value from a table record which match a particular WHERE clause. - * - * @param table The table to query. - * @param field The field to return the value of. - * @param select A fragment of SQL to be used in a where clause returning one row with one column. - * @param params Array of sql parameters. - * @return Promise resolved with the field's value. - */ - getFieldSelect(table: string, field: string, select: string = '', params?: any[]): Promise { - if (select) { - select = 'WHERE ' + select; - } - - return this.getFieldSql(`SELECT ${field} FROM ${table} ${select}`, params); - } - - /** - * Get a single field value (first field) using a SQL statement. - * - * @param sql The SQL query returning one row with one column. - * @param params An array of sql parameters. - * @return Promise resolved with the field's value. - */ - getFieldSql(sql: string, params?: any[]): Promise { - return this.getRecordSql(sql, params).then((record) => { - if (!record) { - return Promise.reject(null); - } - - // Return the first property. - return record[Object.keys(record)[0]]; - }); - } - - /** - * Constructs 'IN()' or '=' sql fragment - * - * @param items A single value or array of values for the expression. It doesn't accept objects. - * @param equal True means we want to equate to the constructed expression. - * @param onEmptyItems This defines the behavior when the array of items provided is empty. Defaults to false, - * meaning return empty. Other values will become part of the returned SQL fragment. - * @return A list containing the constructed sql fragment and an array of parameters. - */ - getInOrEqual(items: any, equal: boolean = true, onEmptyItems?: any): any[] { - let sql, - params; - - if (typeof onEmptyItems == 'undefined') { - onEmptyItems = false; - } - - // Default behavior, return empty data on empty array. - if (Array.isArray(items) && !items.length && onEmptyItems === false) { - return ['', []]; - } - - // Handle onEmptyItems on empty array of items. - if (Array.isArray(items) && !items.length) { - if (onEmptyItems === null) { // Special case, NULL value. - sql = equal ? ' IS NULL' : ' IS NOT NULL'; - - return [sql, []]; - } else { - items = [onEmptyItems]; // Rest of cases, prepare items for processing. - } - } - - if (!Array.isArray(items) || items.length == 1) { - sql = equal ? '= ?' : '<> ?'; - params = Array.isArray(items) ? items : [items]; - } else { - sql = (equal ? '' : 'NOT ') + 'IN (' + ',?'.repeat(items.length).substr(1) + ')'; - params = items; - } - - return [sql, params]; - } - - /** - * Get the database name. - * - * @return Database name. - */ - getName(): string { - return this.name; - } - - /** - * Get a single database record where all the given conditions met. - * - * @param table The table to query. - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @param fields A comma separated list of fields to return. - * @return Promise resolved with the record, rejected if not found. - */ - getRecord(table: string, conditions?: object, fields: string = '*'): Promise { - const selectAndParams = this.whereClause(conditions); - - return this.getRecordSelect(table, selectAndParams[0], selectAndParams[1], fields); - } - - /** - * Get a single database record as an object which match a particular WHERE clause. - * - * @param table The table to query. - * @param select A fragment of SQL to be used in a where clause in the SQL call. - * @param params An array of sql parameters. - * @param fields A comma separated list of fields to return. - * @return Promise resolved with the record, rejected if not found. - */ - getRecordSelect(table: string, select: string = '', params: any[] = [], fields: string = '*'): Promise { - if (select) { - select = ' WHERE ' + select; - } - - return this.getRecordSql(`SELECT ${fields} FROM ${table} ${select}`, params); - } - - /** - * Get a single database record as an object using a SQL statement. - * - * The SQL statement should normally only return one record. - * It is recommended to use getRecordsSql() if more matches possible! - * - * @param sql The SQL string you wish to be executed, should normally only return one record. - * @param params List of sql parameters - * @return Promise resolved with the records. - */ - getRecordSql(sql: string, params?: any[]): Promise { - return this.getRecordsSql(sql, params, 0, 1).then((result) => { - if (!result || !result.length) { - // Not found, reject. - return Promise.reject(null); - } - - // Return only the first record. - return result[0]; - }); - } - - /** - * Get a number of records where all the given conditions met. - * - * @param table The table to query. - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @param sort An order to sort the results in. - * @param fields A comma separated list of fields to return. - * @param limitFrom Return a subset of records, starting at this point. - * @param limitNum Return a subset comprising this many records in total. - * @return Promise resolved with the records. - */ - getRecords(table: string, conditions?: object, sort: string = '', fields: string = '*', limitFrom: number = 0, - limitNum: number = 0): Promise { - const selectAndParams = this.whereClause(conditions); - - return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); - } - - /** - * Get a number of records where one field match one list of values. - * - * @param table The database table to be checked against. - * @param field The name of a field. - * @param values The values field might take. - * @param sort An order to sort the results in. - * @param fields A comma separated list of fields to return. - * @param limitFrom Return a subset of records, starting at this point. - * @param limitNum Return a subset comprising this many records in total. - * @return Promise resolved with the records. - */ - getRecordsList(table: string, field: string, values: any[], sort: string = '', fields: string = '*', limitFrom: number = 0, - limitNum: number = 0): Promise { - const selectAndParams = this.whereClauseList(field, values); - - return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); - } - - /** - * Get a number of records which match a particular WHERE clause. - * - * @param table The table to query. - * @param select A fragment of SQL to be used in a where clause in the SQL call. - * @param params An array of sql parameters. - * @param sort An order to sort the results in. - * @param fields A comma separated list of fields to return. - * @param limitFrom Return a subset of records, starting at this point. - * @param limitNum Return a subset comprising this many records in total. - * @return Promise resolved with the records. - */ - getRecordsSelect(table: string, select: string = '', params: any[] = [], sort: string = '', fields: string = '*', - limitFrom: number = 0, limitNum: number = 0): Promise { - if (select) { - select = ' WHERE ' + select; - } - if (sort) { - sort = ' ORDER BY ' + sort; - } - - const sql = `SELECT ${fields} FROM ${table} ${select} ${sort}`; - - return this.getRecordsSql(sql, params, limitFrom, limitNum); - } - - /** - * Get a number of records using a SQL statement. - * - * @param sql The SQL select query to execute. - * @param params List of sql parameters - * @param limitFrom Return a subset of records, starting at this point. - * @param limitNum Return a subset comprising this many records. - * @return Promise resolved with the records. - */ - getRecordsSql(sql: string, params?: any[], limitFrom?: number, limitNum?: number): Promise { - const limits = this.normaliseLimitFromNum(limitFrom, limitNum); - - if (limits[0] || limits[1]) { - if (limits[1] < 1) { - limits[1] = Number.MAX_VALUE; - } - sql += ' LIMIT ' + limits[0] + ', ' + limits[1]; - } - - return this.execute(sql, params).then((result) => { - // Retrieve the records. - const records = []; - for (let i = 0; i < result.rows.length; i++) { - records.push(result.rows.item(i)); - } - - return records; - }); - } - - /** - * Given a data object, returns the SQL query and the params to insert that record. - * - * @param table The database table. - * @param data A data object with values for one or more fields in the record. - * @return Array with the SQL query and the params. - */ - protected getSqlInsertQuery(table: string, data: object): any[] { - this.formatDataToInsert(data); - - const keys = Object.keys(data), - fields = keys.join(','), - questionMarks = ',?'.repeat(keys.length).substr(1); - - return [ - `INSERT OR REPLACE INTO ${table} (${fields}) VALUES (${questionMarks})`, - keys.map((key) => data[key]) - ]; - } - - /** - * Initialize the database. - */ - init(): void { - this.promise = this.platform.ready().then(() => { - return this.sqlite.create({ - name: this.name, - location: 'default' - }); - }).then((db: SQLiteObject) => { - this.db = db; - }); - } - - /** - * Insert a record into a table and return the "rowId" field. - * - * @param table The database table to be inserted into. - * @param data A data object with values for one or more fields in the record. - * @return Promise resolved with new rowId. Please notice this rowId is internal from SQLite. - */ - insertRecord(table: string, data: object): Promise { - const sqlAndParams = this.getSqlInsertQuery(table, data); - - return this.execute(sqlAndParams[0], sqlAndParams[1]).then((result) => { - return result.insertId; - }); - } - - /** - * Insert multiple records into database as fast as possible. - * - * @param table The database table to be inserted into. - * @param dataObjects List of objects to be inserted. - * @return Promise resolved when done. - */ - insertRecords(table: string, dataObjects: object[]): Promise { - if (!Array.isArray(dataObjects)) { - return Promise.reject(null); - } - - const statements = []; - - dataObjects.forEach((dataObject) => { - statements.push(this.getSqlInsertQuery(table, dataObject)); - }); - - return this.executeBatch(statements); - } - - /** - * Insert multiple records into database from another table. - * - * @param table The database table to be inserted into. - * @param source The database table to get the records from. - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @param fields A comma separated list of fields to return. - * @return Promise resolved when done. - */ - insertRecordsFrom(table: string, source: string, conditions?: object, fields: string = '*'): Promise { - const selectAndParams = this.whereClause(conditions); - const select = selectAndParams[0] ? 'WHERE ' + selectAndParams[0] : ''; - const params = selectAndParams[1]; - - return this.execute(`INSERT INTO ${table} SELECT ${fields} FROM ${source} ${select}`, params); - } - - /** - * Ensures that limit params are numeric and positive integers, to be passed to the database. - * We explicitly treat null, '' and -1 as 0 in order to provide compatibility with how limit - * values have been passed historically. - * - * @param limitFrom Where to start results from. - * @param limitNum How many results to return. - * @return Normalised limit params in array: [limitFrom, limitNum]. - */ - normaliseLimitFromNum(limitFrom: any, limitNum: any): number[] { - // We explicilty treat these cases as 0. - if (typeof limitFrom == 'undefined' || limitFrom === null || limitFrom === '' || limitFrom === -1) { - limitFrom = 0; - } - if (typeof limitNum == 'undefined' || limitNum === null || limitNum === '' || limitNum === -1) { - limitNum = 0; - } - - limitFrom = parseInt(limitFrom, 10); - limitNum = parseInt(limitNum, 10); - limitFrom = Math.max(0, limitFrom); - limitNum = Math.max(0, limitNum); - - return [limitFrom, limitNum]; - } - - /** - * Open the database. Only needed if it was closed before, a database is automatically opened when created. - * - * @return Promise resolved when open. - */ - open(): Promise { - return this.ready().then(() => { - return this.db.open(); - }); - } - - /** - * Wait for the DB to be ready. - * - * @return Promise resolved when ready. - */ - ready(): Promise { - return this.promise; - } - - /** - * Test whether a record exists in a table where all the given conditions met. - * - * @param table The table to check. - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @return Promise resolved if exists, rejected otherwise. - */ - recordExists(table: string, conditions?: object): Promise { - return this.getRecord(table, conditions).then((record) => { - if (!record) { - return Promise.reject(null); - } - }); - } - - /** - * Test whether any records exists in a table which match a particular WHERE clause. - * - * @param table The table to query. - * @param select A fragment of SQL to be used in a where clause in the SQL call. - * @param params An array of sql parameters. - * @return Promise resolved if exists, rejected otherwise. - */ - recordExistsSelect(table: string, select: string = '', params: any[] = []): Promise { - return this.getRecordSelect(table, select, params).then((record) => { - if (!record) { - return Promise.reject(null); - } - }); - } - - /** - * Test whether a SQL SELECT statement returns any records. - * - * @param sql The SQL query returning one row with one column. - * @param params An array of sql parameters. - * @return Promise resolved if exists, rejected otherwise. - */ - recordExistsSql(sql: string, params?: any[]): Promise { - return this.getRecordSql(sql, params).then((record) => { - if (!record) { - return Promise.reject(null); - } - }); - } - - /** - * Test whether a table exists.. - * - * @param name The table name. - * @return Promise resolved if exists, rejected otherwise. - */ - tableExists(name: string): Promise { - return this.recordExists('sqlite_master', {type: 'table', tbl_name: name}); - } - - /** - * Update one or more records in a table. - * - * @param string table The database table to update. - * @param data An object with the fields to update: fieldname=>fieldvalue. - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @return Promise resolved when updated. - */ - updateRecords(table: string, data: any, conditions?: any): Promise { - - this.formatDataToInsert(data); - - if (!data || !Object.keys(data).length) { - // No fields to update, consider it's done. - return Promise.resolve(); - } - - const whereAndParams = this.whereClause(conditions), - sets = []; - let sql, - params; - - for (const key in data) { - sets.push(`${key} = ?`); - } - - sql = `UPDATE ${table} SET ${sets.join(', ')} WHERE ${whereAndParams[0]}`; - // Create the list of params using the "data" object and the params for the where clause. - params = Object.keys(data).map((key) => data[key]).concat(whereAndParams[1]); - - return this.execute(sql, params); - } - - /** - * Update one or more records in a table. It accepts a WHERE clause as a string. - * - * @param string table The database table to update. - * @param data An object with the fields to update: fieldname=>fieldvalue. - * @param where Where clause. Must not include the "WHERE" word. - * @param whereParams Params for the where clause. - * @return Promise resolved when updated. - */ - updateRecordsWhere(table: string, data: any, where?: string, whereParams?: any[]): Promise { - if (!data || !Object.keys(data).length) { - // No fields to update, consider it's done. - return Promise.resolve(); - } - - const sets = []; - let sql, - params; - - for (const key in data) { - sets.push(`${key} = ?`); - } - - sql = `UPDATE ${table} SET ${sets.join(', ')}`; - if (where) { - sql += ` WHERE ${where}`; - } - - // Create the list of params using the "data" object and the params for the where clause. - params = Object.keys(data).map((key) => data[key]); - if (where && whereParams) { - params = params.concat(whereParams); - } - - return this.execute(sql, params); - } - - /** - * Returns the SQL WHERE conditions. - * - * @param conditions The conditions to build the where clause. Must not contain numeric indexes. - * @return An array list containing sql 'where' part and 'params'. - */ - whereClause(conditions: any = {}): any[] { - if (!conditions || !Object.keys(conditions).length) { - return ['1 = 1', []]; - } - - const where = [], - params = []; - - for (const key in conditions) { - const value = conditions[key]; - - if (typeof value == 'undefined' || value === null) { - where.push(key + ' IS NULL'); - } else { - where.push(key + ' = ?'); - params.push(value); - } - } - - return [where.join(' AND '), params]; - } - - /** - * Returns SQL WHERE conditions for the ..._list group of methods. - * - * @param field The name of a field. - * @param values The values field might take. - * @return An array containing sql 'where' part and 'params'. - */ - whereClauseList(field: string, values: any[]): any[] { - if (!values || !values.length) { - return ['1 = 2', []]; // Fake condition, won't return rows ever. - } - - const params = []; - let select = ''; - - values.forEach((value) => { - if (typeof value == 'boolean') { - value = Number(value); - } - - if (typeof value == 'undefined' || value === null) { - select = field + ' IS NULL'; - } else { - params.push(value); - } - }); - - if (params && params.length) { - if (select !== '') { - select = select + ' OR '; - } - - if (params.length == 1) { - select = select + field + ' = ?'; - } else { - const questionMarks = ',?'.repeat(params.length).substr(1); - select = select + field + ' IN (' + questionMarks + ')'; - } - } - - return [select, params]; - } -} diff --git a/src/components/attachments/attachments.ts b/src/components/attachments/attachments.ts deleted file mode 100644 index 810165c39..000000000 --- a/src/components/attachments/attachments.ts +++ /dev/null @@ -1,138 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper'; - -/** - * Component to render attachments, allow adding more and delete the current ones. - * - * All the changes done will be applied to the "files" input array, no file will be uploaded. The component using this - * component should be the one uploading and moving the files. - * - * All the files added will be copied to the app temporary folder, so they should be deleted after uploading them - * or if the user cancels the action. - * - * - * - */ -@Component({ - selector: 'core-attachments', - templateUrl: 'core-attachments.html' -}) -export class CoreAttachmentsComponent implements OnInit { - @Input() files: any[]; // List of attachments. New attachments will be added to this array. - @Input() maxSize: number; // Max size for attachments. If not defined, 0 or -1, unknown size. - @Input() maxSubmissions: number; // Max number of attachments. If -1 or not defined, unknown limit. - @Input() component: string; // Component the downloaded files will be linked to. - @Input() componentId: string | number; // Component ID. - @Input() allowOffline: boolean | string; // Whether to allow selecting files in offline. - @Input() acceptedTypes: string; // List of supported filetypes. If undefined, all types supported. - @Input() required: boolean; // Whether to display the required mark. - - maxSizeReadable: string; - maxSubmissionsReadable: string; - unlimitedFiles: boolean; - - protected fileTypes: { info: any[], mimetypes: string[] }; - - constructor(protected appProvider: CoreAppProvider, protected domUtils: CoreDomUtilsProvider, - protected textUtils: CoreTextUtilsProvider, protected fileUploaderProvider: CoreFileUploaderProvider, - protected translate: TranslateService, protected fileUploaderHelper: CoreFileUploaderHelperProvider) { } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.maxSize = Number(this.maxSize); // Make sure it's defined and it's a number. - this.maxSize = !isNaN(this.maxSize) && this.maxSize > 0 ? this.maxSize : -1; - - if (this.maxSize == -1) { - this.maxSizeReadable = this.translate.instant('core.unknown'); - } else { - this.maxSizeReadable = this.textUtils.bytesToSize(this.maxSize, 2); - } - - if (typeof this.maxSubmissions == 'undefined' || this.maxSubmissions < 0) { - this.maxSubmissionsReadable = this.translate.instant('core.unknown'); - this.unlimitedFiles = true; - } else { - this.maxSubmissionsReadable = String(this.maxSubmissions); - } - - this.acceptedTypes = this.acceptedTypes && this.acceptedTypes.trim(); - - if (this.acceptedTypes && this.acceptedTypes != '*') { - this.fileTypes = this.fileUploaderProvider.prepareFiletypeList(this.acceptedTypes); - } - } - - /** - * Add a new attachment. - */ - add(): void { - const allowOffline = this.allowOffline && this.allowOffline !== 'false'; - - if (!allowOffline && !this.appProvider.isOnline()) { - this.domUtils.showErrorModal('core.fileuploader.errormustbeonlinetoupload', true); - } else { - const mimetypes = this.fileTypes && this.fileTypes.mimetypes; - - this.fileUploaderHelper.selectFile(this.maxSize, allowOffline, undefined, mimetypes).then((result) => { - this.files.push(result); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error selecting file.'); - }); - } - } - - /** - * Delete a file from the list. - * - * @param index The index of the file. - * @param askConfirm Whether to ask confirm. - */ - delete(index: number, askConfirm?: boolean): void { - let promise; - - if (askConfirm) { - promise = this.domUtils.showDeleteConfirm('core.confirmdeletefile'); - } else { - promise = Promise.resolve(); - } - - promise.then(() => { - // Remove the file from the list. - this.files.splice(index, 1); - }).catch(() => { - // User cancelled. - }); - } - - /** - * A file was renamed. - * - * @param index Index of the file. - * @param data The data received. - */ - renamed(index: number, data: any): void { - this.files[index] = data.file; - } -} diff --git a/src/components/attachments/core-attachments.html b/src/components/attachments/core-attachments.html deleted file mode 100644 index 7a1abcda8..000000000 --- a/src/components/attachments/core-attachments.html +++ /dev/null @@ -1,26 +0,0 @@ - - {{ 'core.maxsizeandattachments' | translate:{$a: {size: maxSizeReadable, attachments: maxSubmissionsReadable} } }} - - - -

                        {{ 'core.fileuploader.filesofthesetypes' | translate }}

                        -
                          -
                        • - {{typeInfo.name}} {{typeInfo.extlist}} -
                        • -
                        -
                        -
                        - - - - - -
                        - - - - - {{ 'core.fileuploader.addfiletext' | translate }} - - \ No newline at end of file diff --git a/src/components/bs-tooltip/bs-tooltip.scss b/src/components/bs-tooltip/bs-tooltip.scss deleted file mode 100644 index 54640d936..000000000 --- a/src/components/bs-tooltip/bs-tooltip.scss +++ /dev/null @@ -1,2 +0,0 @@ -ion-app.app-root core-bs-tooltip { -} diff --git a/src/components/bs-tooltip/bs-tooltip.ts b/src/components/bs-tooltip/bs-tooltip.ts deleted file mode 100644 index 55d413731..000000000 --- a/src/components/bs-tooltip/bs-tooltip.ts +++ /dev/null @@ -1,33 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { NavParams } from 'ionic-angular'; - -/** - * Component to display a Bootstrap Tooltip in a popover. - */ -@Component({ - selector: 'core-bs-tooltip', - templateUrl: 'core-bs-tooltip.html' -}) -export class CoreBSTooltipComponent { - content: string; - html: boolean; - - constructor(navParams: NavParams) { - this.content = navParams.get('content') || ''; - this.html = !!navParams.get('html'); - } -} diff --git a/src/components/bs-tooltip/core-bs-tooltip.html b/src/components/bs-tooltip/core-bs-tooltip.html deleted file mode 100644 index 198b17159..000000000 --- a/src/components/bs-tooltip/core-bs-tooltip.html +++ /dev/null @@ -1,4 +0,0 @@ - -

                        -

                        {{content}}

                        -
                        \ No newline at end of file diff --git a/src/components/chart/chart.scss b/src/components/chart/chart.scss deleted file mode 100644 index ea587bc90..000000000 --- a/src/components/chart/chart.scss +++ /dev/null @@ -1,8 +0,0 @@ -ion-app.app-root core-chart { - display: block; - - canvas { - max-width: 500px; - margin: 0 auto; - } -} \ No newline at end of file diff --git a/src/components/chart/chart.ts b/src/components/chart/chart.ts deleted file mode 100644 index b3715ad43..000000000 --- a/src/components/chart/chart.ts +++ /dev/null @@ -1,189 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnDestroy, OnInit, ElementRef, OnChanges, ViewChild } from '@angular/core'; -import { Chart } from 'chart.js'; -import { CoreFilterProvider } from '@core/filter/providers/filter'; -import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * This component shows a chart using chart.js. - * Documentation can be found at http://www.chartjs.org/docs/. - * It only supports changes on these properties: data and labels. - * - * Example usage: - * - */ -@Component({ - selector: 'core-chart', - templateUrl: 'core-chart.html' -}) -export class CoreChartComponent implements OnDestroy, OnInit, OnChanges { - // The first 6 colors will be the app colors, the following will be randomly generated. - // It will use the same colors in the whole session. - protected static backgroundColors = [ - 'rgba(0,100,210, 0.6)', - 'rgba(203,61,77, 0.6)', - 'rgba(0,121,130, 0.6)', - 'rgba(249,128,18, 0.6)', - 'rgba(94,129,0, 0.6)', - 'rgba(251,173,26, 0.6)' - ]; - - @Input() data: any[]; // Chart data. - @Input() labels = []; // Labels of the data. - @Input() type: string; // Type of chart. - @Input() legend: any; // Legend options. - @Input() height = 300; // Height of the chart element. - @Input() filter?: boolean | string; // Whether to filter labels. If not defined, true if contextLevel and instanceId are set. - @Input() contextLevel?: string; // The context level of the text. - @Input() contextInstanceId?: number; // The instance ID related to the context. - @Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters. - @Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the labels for some reason. - @ViewChild('canvas') canvas: ElementRef; - - chart: any; - - constructor(protected filterProvider: CoreFilterProvider, private utils: CoreUtilsProvider, - private filterHelper: CoreFilterHelperProvider) { } - - /** - * Component being initialized. - */ - ngOnInit(): any { - let legend = {}; - if (typeof this.legend == 'undefined') { - legend = { - display: false, - labels: { - generateLabels: (chart): any => { - const data = chart.data; - if (data.labels.length && data.labels.length) { - const datasets = data.datasets[0]; - - return data.labels.map((label, i): any => { - return { - text: label + ': ' + datasets.data[i], - fillStyle: datasets.backgroundColor[i] - }; - }); - } - - return []; - } - } - }; - } else { - legend = Object.assign({}, this.legend); - } - - if (this.type == 'bar' && this.data.length >= 5) { - this.type = 'horizontalBar'; - } - - // Format labels if needed. - this.formatLabels().then(() => { - - const context = this.canvas.nativeElement.getContext('2d'); - this.chart = new Chart(context, { - type: this.type, - data: { - labels: this.labels, - datasets: [{ - data: this.data, - backgroundColor: this.getRandomColors(this.data.length) - }] - }, - options: {legend: legend} - }); - }); - } - - /** - * Listen to chart changes. - */ - ngOnChanges(): void { - if (this.chart) { - // Format labels if needed. - this.formatLabels().then(() => { - this.chart.data.datasets[0] = { - data: this.data, - backgroundColor: this.getRandomColors(this.data.length) - }; - this.chart.data.labels = this.labels; - this.chart.update(); - }); - } - } - - /** - * Format labels if needed. - * - * @return Promise resolved when done. - */ - protected formatLabels(): Promise { - this.filter = typeof this.filter == 'undefined' ? !!(this.contextLevel && this.contextInstanceId) : !!this.filter; - - if (!this.filter) { - return Promise.resolve(); - } - - const options = { - clean: true, - singleLine: true, - courseId: this.courseId, - wsNotFiltered: this.utils.isTrueOrOne(this.wsNotFiltered) - }; - - return this.filterHelper.getFilters(this.contextLevel, this.contextInstanceId, options).then((filters) => { - const promises = []; - - this.labels.forEach((label, i) => { - promises.push(this.filterProvider.formatText(label, options, filters).then((text) => { - this.labels[i] = text; - })); - }); - - return Promise.all(promises); - }); - } - - /** - * Generate random colors if needed. - * - * @param n Number of colors needed. - * @return Array with the number of background colors requested. - */ - protected getRandomColors(n: number): any[] { - while (CoreChartComponent.backgroundColors.length < n) { - const red = Math.floor(Math.random() * 255), - green = Math.floor(Math.random() * 255), - blue = Math.floor(Math.random() * 255); - CoreChartComponent.backgroundColors.push('rgba(' + red + ', ' + green + ', ' + blue + ', 0.6)'); - } - - return CoreChartComponent.backgroundColors.slice(0, n); - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): any { - if (this.chart) { - this.chart.destroy(); - this.chart = false; - } - } -} diff --git a/src/components/chart/core-chart.html b/src/components/chart/core-chart.html deleted file mode 100644 index c4adca0b8..000000000 --- a/src/components/chart/core-chart.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - {{data.text}} - - diff --git a/src/components/chrono/chrono.ts b/src/components/chrono/chrono.ts deleted file mode 100644 index 6dd993060..000000000 --- a/src/components/chrono/chrono.ts +++ /dev/null @@ -1,116 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnChanges, OnDestroy, Output, EventEmitter, SimpleChange, ChangeDetectorRef } from '@angular/core'; - -/** - * This component shows a chronometer in format HH:MM:SS. - * - * If no startTime is provided, it will start at 00:00:00. - * If an endTime is provided, the chrono will stop and emit an event in the onEnd output when that number of milliseconds is - * reached. E.g. if startTime=60000 and endTime=120000, the chrono will start at 00:01:00 and end when it reaches 00:02:00. - * - * This component has 2 boolean inputs to control the timer: running (to start and stop it) and reset. - * - * Example usage: - * - */ -@Component({ - selector: 'core-chrono', - template: '{{ time / 1000 | coreSecondsToHMS }}' -}) -export class CoreChronoComponent implements OnChanges, OnDestroy { - @Input() running: boolean; // Set it to true to start the chrono. Set it to false to stop it. - @Input() startTime = 0; // Number of milliseconds to put in the chrono before starting. - @Input() endTime?: number; // Number of milliseconds to stop the chrono. - @Input() reset?: boolean; // Set it to true to reset the chrono. - @Output() onEnd?: EventEmitter; // Will emit an event when the endTime is reached. - - time = 0; - protected interval; - - constructor(private cdr: ChangeDetectorRef) { - this.onEnd = new EventEmitter(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.time = this.startTime || 0; - } - - /** - * Component being changed. - */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if (changes && changes.running) { - if (changes.running.currentValue) { - this.start(); - } else { - this.stop(); - } - } - if (changes && changes.reset && changes.reset.currentValue) { - this.resetChrono(); - } - } - - /** - * Reset the chrono, stopping it and setting it to startTime. - */ - protected resetChrono(): void { - this.stop(); - this.time = this.startTime || 0; - } - - /** - * Start the chrono if it isn't running. - */ - protected start(): void { - if (this.interval) { - // Already setup. - return; - } - - let lastExecTime = Date.now(); - - this.interval = setInterval(() => { - // Increase the chrono. - this.time += Date.now() - lastExecTime; - lastExecTime = Date.now(); - - if (typeof this.endTime != 'undefined' && this.time > this.endTime) { - // End time reached, stop the timer and call the end function. - this.stop(); - this.onEnd.emit(); - } - - // Force change detection. Angular doesn't detect these async operations. - this.cdr.detectChanges(); - }, 200); - } - - /** - * Stop the chrono, leaving the same time it has. - */ - protected stop(): void { - clearInterval(this.interval); - delete this.interval; - } - - ngOnDestroy(): void { - this.stop(); - } -} diff --git a/src/components/components.module.ts b/src/components/components.module.ts deleted file mode 100644 index e48f29dd6..000000000 --- a/src/components/components.module.ts +++ /dev/null @@ -1,144 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives/directives.module'; -import { CorePipesModule } from '@pipes/pipes.module'; -import { CoreLoadingComponent } from './loading/loading'; -import { CoreMarkRequiredComponent } from './mark-required/mark-required'; -import { CoreInputErrorsComponent } from './input-errors/input-errors'; -import { CoreShowPasswordComponent } from './show-password/show-password'; -import { CoreSplitViewComponent } from './split-view/split-view'; -import { CoreIframeComponent } from './iframe/iframe'; -import { CoreProgressBarComponent } from './progress-bar/progress-bar'; -import { CoreEmptyBoxComponent } from './empty-box/empty-box'; -import { CoreFileComponent } from './file/file'; -import { CoreFilesComponent } from './files/files'; -import { CoreIconComponent } from './icon/icon'; -import { CoreContextMenuComponent } from './context-menu/context-menu'; -import { CoreContextMenuItemComponent } from './context-menu/context-menu-item'; -import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-popover'; -import { CoreCoursePickerMenuPopoverComponent } from './course-picker-menu/course-picker-menu-popover'; -import { CoreChartComponent } from './chart/chart'; -import { CoreChronoComponent } from './chrono/chrono'; -import { CoreDownloadRefreshComponent } from './download-refresh/download-refresh'; -import { CoreLocalFileComponent } from './local-file/local-file'; -import { CoreSitePickerComponent } from './site-picker/site-picker'; -import { CoreTabsComponent } from './tabs/tabs'; -import { CoreTabComponent } from './tabs/tab'; -import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons'; -import { CoreDynamicComponent } from './dynamic-component/dynamic-component'; -import { CoreSendMessageFormComponent } from './send-message-form/send-message-form'; -import { CoreTimerComponent } from './timer/timer'; -import { CoreRecaptchaComponent } from './recaptcha/recaptcha'; -import { CoreRecaptchaModalComponent } from './recaptcha/recaptchamodal'; -import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar'; -import { CoreAttachmentsComponent } from './attachments/attachments'; -import { CoreIonTabsComponent } from './ion-tabs/ion-tabs'; -import { CoreIonTabComponent } from './ion-tabs/ion-tab'; -import { CoreInfiniteLoadingComponent } from './infinite-loading/infinite-loading'; -import { CoreUserAvatarComponent } from './user-avatar/user-avatar'; -import { CoreStyleComponent } from './style/style'; -import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip'; - -@NgModule({ - declarations: [ - CoreLoadingComponent, - CoreMarkRequiredComponent, - CoreInputErrorsComponent, - CoreShowPasswordComponent, - CoreSplitViewComponent, - CoreIframeComponent, - CoreProgressBarComponent, - CoreEmptyBoxComponent, - CoreFileComponent, - CoreFilesComponent, - CoreIconComponent, - CoreContextMenuComponent, - CoreContextMenuItemComponent, - CoreContextMenuPopoverComponent, - CoreCoursePickerMenuPopoverComponent, - CoreChartComponent, - CoreChronoComponent, - CoreDownloadRefreshComponent, - CoreLocalFileComponent, - CoreSitePickerComponent, - CoreTabsComponent, - CoreTabComponent, - CoreNavBarButtonsComponent, - CoreDynamicComponent, - CoreSendMessageFormComponent, - CoreTimerComponent, - CoreRecaptchaComponent, - CoreRecaptchaModalComponent, - CoreNavigationBarComponent, - CoreAttachmentsComponent, - CoreIonTabsComponent, - CoreIonTabComponent, - CoreInfiniteLoadingComponent, - CoreUserAvatarComponent, - CoreStyleComponent, - CoreBSTooltipComponent - ], - entryComponents: [ - CoreContextMenuPopoverComponent, - CoreCoursePickerMenuPopoverComponent, - CoreRecaptchaModalComponent, - CoreBSTooltipComponent - ], - imports: [ - IonicModule, - TranslateModule.forChild(), - CoreDirectivesModule, - CorePipesModule - ], - exports: [ - CoreLoadingComponent, - CoreMarkRequiredComponent, - CoreInputErrorsComponent, - CoreShowPasswordComponent, - CoreSplitViewComponent, - CoreIframeComponent, - CoreProgressBarComponent, - CoreEmptyBoxComponent, - CoreFileComponent, - CoreFilesComponent, - CoreIconComponent, - CoreContextMenuComponent, - CoreContextMenuItemComponent, - CoreChartComponent, - CoreChronoComponent, - CoreDownloadRefreshComponent, - CoreLocalFileComponent, - CoreSitePickerComponent, - CoreTabsComponent, - CoreTabComponent, - CoreNavBarButtonsComponent, - CoreDynamicComponent, - CoreSendMessageFormComponent, - CoreTimerComponent, - CoreRecaptchaComponent, - CoreNavigationBarComponent, - CoreAttachmentsComponent, - CoreIonTabsComponent, - CoreIonTabComponent, - CoreInfiniteLoadingComponent, - CoreUserAvatarComponent, - CoreStyleComponent, - CoreBSTooltipComponent - ] -}) -export class CoreComponentsModule {} diff --git a/src/components/context-menu/context-menu-item.ts b/src/components/context-menu/context-menu-item.ts deleted file mode 100644 index b15a5f82f..000000000 --- a/src/components/context-menu/context-menu-item.ts +++ /dev/null @@ -1,118 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, OnInit, OnDestroy, EventEmitter, OnChanges, SimpleChange } from '@angular/core'; -import { CoreContextMenuComponent } from './context-menu'; - -/** - * This directive adds a item to the Context Menu popover. - * - * @description - * This directive defines and item to be added to the popover generated in CoreContextMenu. - * - * It is required to place this tag inside a core-context-menu tag. - * - * - * - * - */ -@Component({ - selector: 'core-context-menu-item', - template: '' -}) -export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChanges { - @Input() content: string; // Content of the item. - @Input() iconDescription?: string; // Name of the icon to be shown on the left side of the item. - @Input() iconAction?: string; // Name of the icon to show on the right side of the item. Represents the action to do on click. - // If is "spinner" an spinner will be shown. - // If no icon or spinner is selected, no action or link will work. - // If href but no iconAction is provided arrow-right will be used. - @Input() iconSlash?: boolean; // Display a red slash over the icon. - @Input() ariaDescription?: string; // Aria label to add to iconDescription. - @Input() ariaAction?: string; // Aria label to add to iconAction. If not set, it will be equal to content. - @Input() href?: string; // Link to go if no action provided. - @Input() captureLink?: boolean | string; // Whether the link needs to be captured by the app. - @Input() autoLogin?: string; // Whether the link needs to be opened using auto-login. - @Input() closeOnClick?: boolean | string = true; // Whether to close the popover when the item is clicked. - @Input() priority?: number; // Used to sort items. The highest priority, the highest position. - @Input() badge?: string; // A badge to show in the item. - @Input() badgeClass?: number; // A class to set in the badge. - @Input() hidden?: boolean; // Whether the item should be hidden. - @Output() action?: EventEmitter<() => void>; // Will emit an event when the item clicked. - @Output() onClosed?: EventEmitter<() => void>; // Will emit an event when the popover is closed because the item was clicked. - - protected hasAction = false; - protected destroyed = false; - - constructor(private ctxtMenu: CoreContextMenuComponent) { - this.action = new EventEmitter(); - this.onClosed = new EventEmitter(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Initialize values. - this.priority = this.priority || 1; - this.closeOnClick = this.getBooleanValue(this.closeOnClick, true); - this.hasAction = this.action.observers.length > 0; - this.ariaAction = this.ariaAction || this.content; - - if (this.hasAction) { - this.href = ''; - } - - // Navigation help if href provided. - this.captureLink = this.href && this.captureLink ? this.captureLink : false; - this.autoLogin = this.autoLogin || 'check'; - - if (!this.destroyed) { - this.ctxtMenu.addItem(this); - } - } - - /** - * Get a boolean value from item. - * - * @param value Value to check. - * @param defaultValue Value to use if undefined. - * @return Boolean value. - */ - protected getBooleanValue(value: any, defaultValue: boolean): boolean { - if (typeof value == 'undefined') { - return defaultValue; - } - - return value && value !== 'false'; - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.destroyed = true; - this.ctxtMenu.removeItem(this); - } - - /** - * Detect changes on input properties. - */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if (changes.hidden && !changes.hidden.firstChange) { - this.ctxtMenu.itemsChanged(); - } - } -} diff --git a/src/components/context-menu/context-menu-popover.ts b/src/components/context-menu/context-menu-popover.ts deleted file mode 100644 index 6d43c77cf..000000000 --- a/src/components/context-menu/context-menu-popover.ts +++ /dev/null @@ -1,74 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { NavParams, ViewController } from 'ionic-angular'; -import { CoreContextMenuItemComponent } from './context-menu-item'; -import { CoreLoggerProvider } from '@providers/logger'; - -/** - * Component to display a list of items received by param in a popover. - */ -@Component({ - selector: 'core-context-menu-popover', - templateUrl: 'core-context-menu-popover.html' -}) -export class CoreContextMenuPopoverComponent { - title: string; - uniqueId: string; - items: CoreContextMenuItemComponent[]; - protected logger: any; - - constructor(navParams: NavParams, private viewCtrl: ViewController, logger: CoreLoggerProvider) { - this.title = navParams.get('title'); - this.items = navParams.get('items') || []; - this.uniqueId = navParams.get('id'); - this.logger = logger.getInstance('CoreContextMenuPopoverComponent'); - } - - /** - * Close the popover. - */ - closeMenu(item?: CoreContextMenuItemComponent): void { - this.viewCtrl.dismiss(item); - } - - /** - * Function called when an item is clicked. - * - * @param event Click event. - * @param item Item clicked. - * @return Return true if success, false if error. - */ - itemClicked(event: Event, item: CoreContextMenuItemComponent): boolean { - if (item.action.observers.length > 0) { - event.preventDefault(); - event.stopPropagation(); - - if (item.iconAction == 'spinner') { - return false; - } - - if (item.closeOnClick) { - this.closeMenu(item); - } - - item.action.emit(this.closeMenu.bind(this, item)); - } else if (item.closeOnClick && (item.href || item.onClosed.observers.length > 0)) { - this.closeMenu(item); - } - - return true; - } -} diff --git a/src/components/context-menu/context-menu.scss b/src/components/context-menu/context-menu.scss deleted file mode 100644 index 43c1468e5..000000000 --- a/src/components/context-menu/context-menu.scss +++ /dev/null @@ -1,9 +0,0 @@ -ion-app.app-root core-context-menu-popover { - .list { - margin-bottom: 0; - } - .item-md ion-icon[item-start] + .item-inner, - .item-md ion-icon[item-start] + .item-input { - @include margin-horizontal(5px, null); - } -} diff --git a/src/components/context-menu/context-menu.ts b/src/components/context-menu/context-menu.ts deleted file mode 100644 index 7dd28b31a..000000000 --- a/src/components/context-menu/context-menu.ts +++ /dev/null @@ -1,205 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, OnDestroy, ElementRef, Optional } from '@angular/core'; -import { PopoverController } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreContextMenuItemComponent } from './context-menu-item'; -import { CoreContextMenuPopoverComponent } from './context-menu-popover'; -import { CoreTabComponent } from '@components/tabs/tab'; -import { Subject } from 'rxjs'; - -/** - * This component adds a button (usually in the navigation bar) that displays a context menu popover. - */ -@Component({ - selector: 'core-context-menu', - templateUrl: 'core-context-menu.html' -}) -export class CoreContextMenuComponent implements OnInit, OnDestroy { - @Input() icon?: string; // Icon to be shown on the navigation bar. Default: Kebab menu icon. - @Input() title?: string; // Text to be shown on the top of the popover. - @Input('aria-label') ariaLabel?: string; // Aria label to be shown on the top of the popover. - - hideMenu = true; // It will be unhidden when items are added. - expanded = false; - protected items: CoreContextMenuItemComponent[] = []; - protected itemsMovedToParent: CoreContextMenuItemComponent[] = []; - protected itemsChangedStream: Subject; // Stream to update the hideMenu boolean when items change. - protected instanceId: string; - protected parentContextMenu: CoreContextMenuComponent; - protected uniqueId: string; - - constructor(private translate: TranslateService, private popoverCtrl: PopoverController, elementRef: ElementRef, - private domUtils: CoreDomUtilsProvider, @Optional() public coreTab: CoreTabComponent, utils: CoreUtilsProvider) { - // Create the stream and subscribe to it. We ignore successive changes during 250ms. - this.itemsChangedStream = new Subject(); - this.itemsChangedStream.auditTime(250).subscribe(() => { - // Hide the menu if all items are hidden. - this.hideMenu = !this.items.some((item) => { - return !item.hidden; - }); - - // Sort the items by priority. - this.items.sort((a, b) => { - return a.priority <= b.priority ? 1 : -1; - }); - }); - - // Calculate the unique ID. - this.uniqueId = 'core-context-menu-' + utils.getUniqueId('CoreContextMenuComponent'); - - this.instanceId = this.domUtils.storeInstanceByElement(elementRef.nativeElement, this); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.icon = this.icon || 'more'; - this.ariaLabel = this.ariaLabel || this.title || this.translate.instant('core.displayoptions'); - } - - /** - * Add a context menu item. - * - * @param item The item to add. - */ - addItem(item: CoreContextMenuItemComponent): void { - if (this.parentContextMenu) { - // All items were moved to the "parent" menu. Add the item in there. - this.parentContextMenu.addItem(item); - - if (this.itemsMovedToParent.indexOf(item) == -1) { - this.itemsMovedToParent.push(item); - } - } else if (this.items.indexOf(item) == -1) { - this.items.push(item); - this.itemsChanged(); - } - } - - /** - * Function called when the items change. - */ - itemsChanged(): void { - if (this.parentContextMenu) { - // All items were moved to the "parent" menu, call the function in there. - this.parentContextMenu.itemsChanged(); - } else { - this.itemsChangedStream.next(); - } - } - - /** - * Merge the current context menu with the one passed as parameter. All the items in this menu will be moved to the - * one passed as parameter. - * - * @param contextMenu The context menu where to move the items. - */ - mergeContextMenus(contextMenu: CoreContextMenuComponent): void { - this.parentContextMenu = contextMenu; - - // Add all the items to the other menu. - for (let i = 0; i < this.items.length; i++) { - const item = this.items[i]; - contextMenu.addItem(item); - this.itemsMovedToParent.push(item); - } - - // Remove all items from the current menu. - this.items = []; - this.itemsChanged(); - } - - /** - * Remove an item from the context menu. - * - * @param item The item to remove. - */ - removeItem(item: CoreContextMenuItemComponent): void { - if (this.parentContextMenu) { - // All items were moved to the "parent" menu. Remove the item from there. - this.parentContextMenu.removeItem(item); - - const index = this.itemsMovedToParent.indexOf(item); - if (index >= 0) { - this.itemsMovedToParent.splice(index, 1); - } - } else { - const index = this.items.indexOf(item); - if (index >= 0) { - this.items.splice(index, 1); - } - this.itemsChanged(); - } - } - - /** - * Remove the items that were merged to a parent context menu. - */ - removeMergedItems(): void { - if (this.parentContextMenu) { - for (let i = 0; i < this.itemsMovedToParent.length; i++) { - this.parentContextMenu.removeItem(this.itemsMovedToParent[i]); - } - } - } - - /** - * Restore the items that were merged to a parent context menu. - */ - restoreMergedItems(): void { - if (this.parentContextMenu) { - for (let i = 0; i < this.itemsMovedToParent.length; i++) { - this.parentContextMenu.addItem(this.itemsMovedToParent[i]); - } - } - } - - /** - * Show the context menu. - * - * @param event Event. - */ - showContextMenu(event: MouseEvent): void { - if (!this.expanded) { - const popover = this.popoverCtrl.create(CoreContextMenuPopoverComponent, - { title: this.title, items: this.items, id: this.uniqueId, showBackdrop: true }); - - popover.onDidDismiss((item: CoreContextMenuItemComponent) => { - this.expanded = false; - - if (item) { - item.onClosed.emit(); - } - }); - popover.present({ - ev: event - }); - - this.expanded = true; - } - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.domUtils.removeInstanceById(this.instanceId); - this.removeMergedItems(); - } -} diff --git a/src/components/context-menu/core-context-menu-popover.html b/src/components/context-menu/core-context-menu-popover.html deleted file mode 100644 index 76a6b96d1..000000000 --- a/src/components/context-menu/core-context-menu-popover.html +++ /dev/null @@ -1,10 +0,0 @@ - - {{title}} - - - - - - {{item.badge}} - - \ No newline at end of file diff --git a/src/components/context-menu/core-context-menu.html b/src/components/context-menu/core-context-menu.html deleted file mode 100644 index 0628573be..000000000 --- a/src/components/context-menu/core-context-menu.html +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/src/components/course-picker-menu/core-course-picker-menu-popover.html b/src/components/course-picker-menu/core-course-picker-menu-popover.html deleted file mode 100644 index ebfa0f52a..000000000 --- a/src/components/course-picker-menu/core-course-picker-menu-popover.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/components/course-picker-menu/course-picker-menu-popover.ts b/src/components/course-picker-menu/course-picker-menu-popover.ts deleted file mode 100644 index 01e553660..000000000 --- a/src/components/course-picker-menu/course-picker-menu-popover.ts +++ /dev/null @@ -1,46 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { NavParams, ViewController } from 'ionic-angular'; - -/** - * Component to display a list of courses. - */ -@Component({ - selector: 'core-course-picker-menu-popover', - templateUrl: 'core-course-picker-menu-popover.html' -}) -export class CoreCoursePickerMenuPopoverComponent { - courses: any[]; - courseId = -1; - - constructor(navParams: NavParams, private viewCtrl: ViewController) { - this.courses = navParams.get('courses') || []; - this.courseId = navParams.get('courseId') || -1; - } - - /** - * Function called when a course is clicked. - * - * @param event Click event. - * @param course Course object clicked. - * @return Return true if success, false if error. - */ - coursePicked(event: Event, course: any): boolean { - this.viewCtrl.dismiss(course); - - return true; - } -} diff --git a/src/components/download-refresh/core-download-refresh.html b/src/components/download-refresh/core-download-refresh.html deleted file mode 100644 index c9b23a15a..000000000 --- a/src/components/download-refresh/core-download-refresh.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/components/download-refresh/download-refresh.scss b/src/components/download-refresh/download-refresh.scss deleted file mode 100644 index 2ac72a335..000000000 --- a/src/components/download-refresh/download-refresh.scss +++ /dev/null @@ -1,20 +0,0 @@ -ion-app.app-root core-download-refresh { - font-size: 1.4rem; - display: flex; - flex-flow: row; - align-items: center; - z-index: 1; - justify-content: space-around; - align-content: center; - min-height: 44px; - - button, ion-icon { - cursor: pointer; - pointer-events: auto; - text-align: center; - } - - ion-icon, .core-icon-downloaded { - font-size: 1.8em; - } -} diff --git a/src/components/download-refresh/download-refresh.ts b/src/components/download-refresh/download-refresh.ts deleted file mode 100644 index 67f855e29..000000000 --- a/src/components/download-refresh/download-refresh.ts +++ /dev/null @@ -1,55 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, EventEmitter } from '@angular/core'; -import { CoreConstants } from '@core/constants'; - -/** - * Component to show a download button with refresh option, the spinner and the status of it. - * - * Usage: - * - */ -@Component({ - selector: 'core-download-refresh', - templateUrl: 'core-download-refresh.html' -}) -export class CoreDownloadRefreshComponent { - @Input() status: string; // Download status. - @Input() enabled = false; // Whether the download is enabled. - @Input() loading = true; // Force loading status when is not downloading. - @Input() canTrustDownload = false; // If false, refresh will be shown if downloaded. - @Output() action: EventEmitter; // Will emit an event when the item clicked. - - statusDownloaded = CoreConstants.DOWNLOADED; - statusNotDownloaded = CoreConstants.NOT_DOWNLOADED; - statusOutdated = CoreConstants.OUTDATED; - statusDownloading = CoreConstants.DOWNLOADING; - - constructor() { - this.action = new EventEmitter(); - } - - /** - * Download clicked. - * - * @param e Click event. - * @param refresh Whether it's refreshing. - */ - download(e: Event, refresh: boolean): void { - e.preventDefault(); - e.stopPropagation(); - this.action.emit(refresh); - } -} diff --git a/src/components/dynamic-component/core-dynamic-component.html b/src/components/dynamic-component/core-dynamic-component.html deleted file mode 100644 index 99c89fec9..000000000 --- a/src/components/dynamic-component/core-dynamic-component.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/components/dynamic-component/dynamic-component.ts b/src/components/dynamic-component/dynamic-component.ts deleted file mode 100644 index c2ed305b7..000000000 --- a/src/components/dynamic-component/dynamic-component.ts +++ /dev/null @@ -1,185 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { - Component, Input, ViewChild, OnInit, OnChanges, DoCheck, ViewContainerRef, ComponentFactoryResolver, ComponentRef, - KeyValueDiffers, SimpleChange, ChangeDetectorRef, Optional, ElementRef -} from '@angular/core'; -import { NavController } from 'ionic-angular'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Component to create another component dynamically. - * - * You need to pass the class of the component to this component (the class, not the name), along with the input data. - * - * So you should do something like: - * - * import { MyComponent } from './component'; - * - * ... - * - * this.component = MyComponent; - * - * And in the template: - * - * - *

                        Cannot render the data.

                        - *
                        - * - * Please notice that the component that you pass needs to be declared in entryComponents of the module to be created dynamically. - * - * Alternatively, you can also supply a ComponentRef instead of the class of the component. In this case, the component won't - * be instantiated because it already is, it will be attached to the view and the right data will be passed to it. - * Passing ComponentRef is meant for site plugins, so we'll inject a NavController instance to the component. - * - * The contents of this component will be displayed if no component is supplied or it cannot be created. In the example above, - * if no component is supplied then the template will show the message "Cannot render the data.". - */ -@Component({ - selector: 'core-dynamic-component', - templateUrl: 'core-dynamic-component.html' -}) -export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck { - - @Input() component: any; - @Input() data: any; - - // Get the container where to put the dynamic component. - @ViewChild('dynamicComponent', { read: ViewContainerRef }) set dynamicComponent(el: ViewContainerRef) { - this.container = el; - this.createComponent(); - } - - instance: any; - container: ViewContainerRef; - protected logger: any; - protected differ: any; // To detect changes in the data input. - protected lastComponent: any; - - constructor(logger: CoreLoggerProvider, protected factoryResolver: ComponentFactoryResolver, differs: KeyValueDiffers, - @Optional() protected navCtrl: NavController, protected cdr: ChangeDetectorRef, protected element: ElementRef, - protected domUtils: CoreDomUtilsProvider) { - - this.logger = logger.getInstance('CoreDynamicComponent'); - this.differ = differs.find([]).create(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.createComponent(); - } - - /** - * Detect changes on input properties. - */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - - if (changes.component && !this.component) { - // Component not set, destroy the instance if any. - this.lastComponent = undefined; - this.instance = undefined; - this.container && this.container.clear(); - } else if (changes.component && (!this.instance || this.component != this.lastComponent)) { - this.createComponent(); - } - } - - /** - * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays). - */ - ngDoCheck(): void { - if (this.instance) { - // Check if there's any change in the data object. - const changes = this.differ.diff(this.data); - if (changes) { - this.setInputData(); - if (this.instance.ngOnChanges) { - this.instance.ngOnChanges(this.domUtils.createChangesFromKeyValueDiff(changes)); - } - } - } - } - - /** - * Call a certain function on the component. - * - * @param name Name of the function to call. - * @param params List of params to send to the function. - * @return Result of the call. Undefined if no component instance or the function doesn't exist. - */ - callComponentFunction(name: string, params?: any[]): any { - if (this.instance && typeof this.instance[name] == 'function') { - return this.instance[name].apply(this.instance, params); - } - } - - /** - * Create a component, add it to a container and set the input data. - * - * @return Whether the component was successfully created. - */ - protected createComponent(): boolean { - this.lastComponent = this.component; - - if (!this.component || !this.container) { - // No component to instantiate or container doesn't exist right now. - return false; - } - - if (this.instance) { - // Component already instantiated. - return true; - } - - if (this.component instanceof ComponentRef) { - // A ComponentRef was supplied instead of the component class. Add it to the view. - this.container.insert(this.component.hostView); - this.instance = this.component.instance; - - // This feature is usually meant for site plugins. Inject some properties. - this.instance['ChangeDetectorRef'] = this.cdr; - this.instance['NavController'] = this.navCtrl; - this.instance['componentContainer'] = this.element.nativeElement; - } else { - try { - // Create the component and add it to the container. - const factory = this.factoryResolver.resolveComponentFactory(this.component), - componentRef = this.container.createComponent(factory); - - this.instance = componentRef.instance; - } catch (ex) { - this.logger.error('Error creating component', ex); - - return false; - } - } - - this.setInputData(); - - return true; - } - - /** - * Set the input data for the component. - */ - protected setInputData(): void { - for (const name in this.data) { - this.instance[name] = this.data[name]; - } - } -} diff --git a/src/components/empty-box/core-empty-box.html b/src/components/empty-box/core-empty-box.html deleted file mode 100644 index 09a592094..000000000 --- a/src/components/empty-box/core-empty-box.html +++ /dev/null @@ -1,8 +0,0 @@ -
                        -
                        - - -

                        {{ message }}

                        - -
                        -
                        \ No newline at end of file diff --git a/src/components/empty-box/empty-box.scss b/src/components/empty-box/empty-box.scss deleted file mode 100644 index e5d0c3edb..000000000 --- a/src/components/empty-box/empty-box.scss +++ /dev/null @@ -1,68 +0,0 @@ -ion-app.app-root core-empty-box { - .core-empty-box { - position: absolute; - @include position(0, 0, 0, 0); - display: table; - height: 100%; - width: 100%; - margin: 0; - padding: 0; - clear: both; - pointer-events: none; - z-index: -5; - - .core-empty-box-content { - color: $black; - margin: 0; - display: table-cell; - text-align: center; - vertical-align: middle; - pointer-events: auto; - } - - &.core-empty-box-inline { - position: relative; - z-index: initial; - @include position(initial, initial, null, initial); - height: auto; - } - - .icon { - font-size: 120px; - width: auto; - } - img { - height: 125px; - width: 145px; - margin: 0 auto; - } - p { - font-size: 120%; - } - } - - &.core-empty-box-clickable .core-empty-box { - z-index: 0; - } - - @media (max-width: 350px) { - .core-empty-box { - position: relative; - height: auto; - margin-top: 50px; - - .icon { - font-size: 100px; - } - img { - height: 104px; - width: 121px; - } - } - } -} - -ion-app.app-root core-block-course-blocks core-empty-box .core-empty-box { - position: relative; -} - diff --git a/src/components/empty-box/empty-box.ts b/src/components/empty-box/empty-box.ts deleted file mode 100644 index e9480d0de..000000000 --- a/src/components/empty-box/empty-box.ts +++ /dev/null @@ -1,40 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input } from '@angular/core'; - -/** - * Component to show an empty box message. It will show an optional icon or image and a text centered on page. - * - * Use class="core-empty-box-clickable" if you want to add some clickable elements to the box. - * - * Usage: - * - */ -@Component({ - selector: 'core-empty-box', - templateUrl: 'core-empty-box.html' -}) -export class CoreEmptyBoxComponent { - @Input() message: string; // Message to display. - @Input() icon?: string; // Name of the icon to use. - @Input() image?: string; // Image source. If an icon is provided, image won't be used. - @Input() inline?: boolean; // If this has to be shown inline instead of occupying whole page. - // If image or icon is not supplied, it's true by default. - @Input() flipIconRtl?: boolean; // Whether to flip the icon in RTL. Defaults to false. - - constructor() { - // Nothing to do. - } -} diff --git a/src/components/file/core-file.html b/src/components/file/core-file.html deleted file mode 100644 index 071deec11..000000000 --- a/src/components/file/core-file.html +++ /dev/null @@ -1,14 +0,0 @@ - - -

                        {{fileName}}

                        -

                        {{ fileSizeReadable }}

                        -

                        {{ timemodified * 1000 | coreFormatDate }}

                        - -
                        - - - -
                        -
                        diff --git a/src/components/file/file.scss b/src/components/file/file.scss deleted file mode 100644 index 9671b89e7..000000000 --- a/src/components/file/file.scss +++ /dev/null @@ -1,30 +0,0 @@ -ion-app.app-root { - .card-md core-file + core-file > .item-md.item-block > .item-inner, - core-file + core-file > .item-md.item-block > .item-inner { - border-top: 1px solid $list-md-border-color; - } - - .card-ios core-file + core-file > .item-ios.item-block > .item-inner, - core-file + core-file > .item-ios.item-block > .item-inner { - border-top: $hairlines-width solid $list-ios-border-color; - .buttons { - min-height: 53px; - min-width: 58px; - } - } - - core-file > .item.item-block > .item-inner { - border-bottom: 0; - @include safe-area-padding(null, 0px, null, null); - .buttons { - display: flex; - flex-flow: row; - align-items: center; - z-index: 1; - justify-content: space-around; - align-content: center; - min-height: 52px; - min-width: 53px; - } - } -} \ No newline at end of file diff --git a/src/components/file/file.ts b/src/components/file/file.ts deleted file mode 100644 index 34c89be6a..000000000 --- a/src/components/file/file.ts +++ /dev/null @@ -1,246 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, OnInit, OnDestroy, EventEmitter } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreFileHelperProvider } from '@providers/file-helper'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreConstants } from '@core/constants'; -import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; - -/** - * Component to handle a remote file. Shows the file name, icon (depending on mimetype) and a button - * to download/refresh it. - */ -@Component({ - selector: 'core-file', - templateUrl: 'core-file.html' -}) -export class CoreFileComponent implements OnInit, OnDestroy { - @Input() file: any; // The file. Must have a property 'filename' and a 'fileurl' or 'url' - @Input() component?: string; // Component the file belongs to. - @Input() componentId?: string | number; // Component ID. - @Input() canDelete?: boolean | string; // Whether file can be deleted. - @Input() alwaysDownload?: boolean | string; // Whether it should always display the refresh button when the file is downloaded. - // Use it for files that you cannot determine if they're outdated or not. - @Input() canDownload?: boolean | string = true; // Whether file can be downloaded. - @Input() showSize?: boolean | string = true; // Whether show filesize. - @Input() showTime?: boolean | string = true; // Whether show file time modified. - @Output() onDelete?: EventEmitter; // Will notify when the delete button is clicked. - - isDownloading: boolean; - fileIcon: string; - fileName: string; - fileSizeReadable: string; - - protected fileUrl: string; - protected siteId: string; - protected fileSize: number; - protected state: string; - protected timemodified: number; - protected observer; - - constructor(protected sitesProvider: CoreSitesProvider, - protected utils: CoreUtilsProvider, - protected domUtils: CoreDomUtilsProvider, - protected filepoolProvider: CoreFilepoolProvider, - protected appProvider: CoreAppProvider, - protected fileHelper: CoreFileHelperProvider, - protected mimeUtils: CoreMimetypeUtilsProvider, - protected eventsProvider: CoreEventsProvider, - protected textUtils: CoreTextUtilsProvider, - protected pluginFileDelegate: CorePluginFileDelegate, - protected urlUtils: CoreUrlUtilsProvider) { - this.onDelete = new EventEmitter(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.canDelete = this.utils.isTrueOrOne(this.canDelete); - this.alwaysDownload = this.utils.isTrueOrOne(this.alwaysDownload); - this.canDownload = this.utils.isTrueOrOne(this.canDownload); - - this.fileUrl = this.fileHelper.getFileUrl(this.file); - this.timemodified = this.fileHelper.getFileTimemodified(this.file); - this.siteId = this.sitesProvider.getCurrentSiteId(); - this.fileSize = this.file.filesize; - this.fileName = this.file.filename; - - if (this.utils.isTrueOrOne(this.showSize) && this.fileSize >= 0) { - this.fileSizeReadable = this.textUtils.bytesToSize(this.fileSize, 2); - } - - this.showTime = this.utils.isTrueOrOne(this.showTime) && this.timemodified > 0; - - if (this.file.isexternalfile) { - this.alwaysDownload = true; // Always show the download button in external files. - } - - this.fileIcon = this.mimeUtils.getFileIcon(this.file.filename); - - if (this.canDownload) { - this.calculateState(); - - // Update state when receiving events about this file. - this.filepoolProvider.getFileEventNameByUrl(this.siteId, this.fileUrl).then((eventName) => { - this.observer = this.eventsProvider.on(eventName, () => { - this.calculateState(); - }); - }).catch(() => { - // File not downloadable. - }); - } - } - - /** - * Convenience function to get the file state and set variables based on it. - * - * @return Promise resolved when state has been calculated. - */ - protected calculateState(): Promise { - return this.filepoolProvider.getFileStateByUrl(this.siteId, this.fileUrl, this.timemodified).then((state) => { - this.canDownload = this.sitesProvider.getCurrentSite().canDownloadFiles(); - - this.state = state; - this.isDownloading = this.canDownload && state === CoreConstants.DOWNLOADING; - }); - } - - /** - * Convenience function to open a file, downloading it if needed. - * - * @return Promise resolved when file is opened. - */ - protected openFile(): Promise { - return this.fileHelper.downloadAndOpenFile(this.file, this.component, this.componentId, this.state, (event) => { - if (event && event.calculating) { - // The process is calculating some data required for the download, show the spinner. - this.isDownloading = true; - } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); - }); - } - - /** - * Download a file and, optionally, open it afterwards. - * - * @param e Click event. - * @param openAfterDownload Whether the file should be opened after download. - */ - async download(e?: Event, openAfterDownload: boolean = false): Promise { - e && e.preventDefault(); - e && e.stopPropagation(); - - if (this.isDownloading && !openAfterDownload) { - return; - } - - if (!this.canDownload || !this.state || this.state == CoreConstants.NOT_DOWNLOADABLE) { - // File cannot be downloaded, just open it. - if (this.file.toURL) { - // Local file. - this.utils.openFile(this.file.toURL()); - } else if (this.fileUrl) { - if (this.urlUtils.isLocalFileUrl(this.fileUrl)) { - this.utils.openFile(this.fileUrl); - } else { - this.utils.openOnlineFile(this.urlUtils.unfixPluginfileURL(this.fileUrl)); - } - } - - return; - } - - if (!this.appProvider.isOnline() && (!openAfterDownload || (openAfterDownload && - !this.fileHelper.isStateDownloaded(this.state)))) { - this.domUtils.showErrorModal('core.networkerrormsg', true); - - return; - } - - if (openAfterDownload) { - // File needs to be opened now. - try { - await this.openFile(); - } catch (error) { - this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); - } - } else { - // File doesn't need to be opened (it's a prefetch). - if (!this.fileHelper.isOpenableInApp(this.file)) { - try { - await this.fileHelper.showConfirmOpenUnsupportedFile(true); - } catch (error) { - return; // Cancelled, stop. - } - } - - try { - // Show confirm modal if file size is defined and it's big. - const size = await this.pluginFileDelegate.getFileSize({fileurl: this.fileUrl, filesize: this.fileSize}, - this.siteId); - - if (size) { - await this.domUtils.confirmDownloadSize({ size: size, total: true }); - } - - // User confirmed, add the file to queue. - await this.utils.ignoreErrors(this.filepoolProvider.invalidateFileByUrl(this.siteId, this.fileUrl)); - - this.isDownloading = true; - - try { - await this.filepoolProvider.addToQueueByUrl(this.siteId, this.fileUrl, this.component, - this.componentId, this.timemodified, undefined, undefined, 0, this.file); - } catch (error) { - this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); - this.calculateState(); - } - } catch (error) { - this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); - } - } - } - - /** - * Delete the file. - * - * @param e Click event. - */ - delete(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - if (this.canDelete) { - this.onDelete.emit(); - } - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.observer && this.observer.off(); - } -} diff --git a/src/components/files/core-files.html b/src/components/files/core-files.html deleted file mode 100644 index 239728e29..000000000 --- a/src/components/files/core-files.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/components/files/files.ts b/src/components/files/files.ts deleted file mode 100644 index 551c81137..000000000 --- a/src/components/files/files.ts +++ /dev/null @@ -1,82 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, DoCheck, KeyValueDiffers } from '@angular/core'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Component to render a file list. - * - * - * - */ -@Component({ - selector: 'core-files', - templateUrl: 'core-files.html' -}) -export class CoreFilesComponent implements OnInit, DoCheck { - @Input() files: any[]; // List of files. - @Input() component: string; // Component the downloaded files will be linked to. - @Input() componentId: string | number; // Component ID. - @Input() alwaysDownload?: boolean | string; // Whether it should always display the refresh button when the file is downloaded. - // Use it for files that you cannot determine if they're outdated or not. - @Input() canDownload?: boolean | string = true; // Whether file can be downloaded. - @Input() showSize?: boolean | string = true; // Whether show filesize. - @Input() showTime?: boolean | string = true; // Whether show file time modified. - @Input() showInline = false; // If true, it will reorder and try to show inline files first. - - contentText: string; - - protected differ: any; // To detect changes in the data input. - - constructor(protected mimetypeUtils: CoreMimetypeUtilsProvider, - protected utils: CoreUtilsProvider, - differs: KeyValueDiffers) { - this.differ = differs.find([]).create(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (this.utils.isTrueOrOne(this.showInline)) { - this.renderInlineFiles(); - } - } - - /** - * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays). - */ - ngDoCheck(): void { - if (this.utils.isTrueOrOne(this.showInline)) { - // Check if there's any change in the files array. - const changes = this.differ.diff(this.files); - if (changes) { - this.renderInlineFiles(); - } - } - } - - /** - * Calculate contentText based on fils that can be rendered inline. - */ - protected renderInlineFiles(): void { - this.contentText = this.files.reduce((previous, file) => { - const text = this.mimetypeUtils.getEmbeddedHtml(file); - - return text ? previous + '
                        ' + text : previous; - }, ''); - } -} diff --git a/src/components/icon/core-icon.html b/src/components/icon/core-icon.html deleted file mode 100644 index 7c89b545c..000000000 --- a/src/components/icon/core-icon.html +++ /dev/null @@ -1 +0,0 @@ -
                        diff --git a/src/components/icon/icon.scss b/src/components/icon/icon.scss deleted file mode 100644 index f14ba848a..000000000 --- a/src/components/icon/icon.scss +++ /dev/null @@ -1,63 +0,0 @@ -// Color icons -@each $color-name, $color-base, $color-contrast in get-colors($colors) { - .fa-#{$color-name} { - color: $color-base; - } -} - -@each $color-name, $color-base, $color-contrast in get-colors($colors-dark) { - .fa-#{$color-name} { - @include darkmode() { - color: $color-base !important; - } - } -} - - -[dir=rtl] .icon { - &.core-icon-dir-flip, - &.fa-caret-right, - &.ion-md-send, &.ion-ios-send { - -webkit-transform: scale(-1, 1); - transform: scale(-1, 1); - } -} - -// Center font awesome icons - -.icon.fa::before { - width: 1em; - text-align: center; -} - -// Slash - -@font-face { - font-family: "Moodle Slash Icon"; - font-style: normal; - font-weight: 400; - src: url("#{$font-path}/slash-icon.woff") format("woff"); -} - -.icon-slash { - position: relative; -} - -.icon-slash::after { - content: "/"; - font-family: "Moodle Slash Icon"; - font-size: 0.75em; - margin-top: 0.125em; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - text-align: center; - color: color($colors, danger); -} - -.icon-slash.fa::after { - font-size: 1em; - margin-top: 0; -} diff --git a/src/components/icon/icon.ts b/src/components/icon/icon.ts deleted file mode 100644 index 72ac16416..000000000 --- a/src/components/icon/icon.ts +++ /dev/null @@ -1,141 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnChanges, OnDestroy, ElementRef, SimpleChange } from '@angular/core'; -import { Config } from 'ionic-angular'; - -/** - * Core Icon is a component that enabled a posibility to add fontawesome icon to the html. It's recommended if both fontawesome - * or ionicons can be used in the name attribute. To use fontawesome just place the full icon name with the fa- prefix and - * the component will detect it. - * Check available icons https://fontawesome.com/v4.7.0/icons/. - */ -@Component({ - selector: 'core-icon', - templateUrl: 'core-icon.html', -}) -export class CoreIconComponent implements OnChanges, OnDestroy { - // Common params. - @Input() name: string; - @Input('color') color?: string; - @Input('slash') slash?: boolean; // Display a red slash over the icon. - - // Ionicons params. - @Input('isActive') isActive?: boolean; - @Input('md') md?: string; - @Input('ios') ios?: string; - - // FontAwesome params. - @Input('fixed-width') fixedWidth: string; - - @Input('label') ariaLabel?: string; - @Input() flipRtl?: boolean; // Whether to flip the icon in RTL. Defaults to false. - - protected element: HTMLElement; - protected newElement: HTMLElement; - - constructor(el: ElementRef, private config: Config) { - this.element = el.nativeElement; - } - - /** - * Detect changes on input properties. - */ - ngOnChanges(changes: {[name: string]: SimpleChange}): void { - if (!changes.name || !this.name) { - return; - } - - const oldElement = this.newElement ? this.newElement : this.element; - - // Use a new created element to avoid ion-icon working. - // This is necessary to make the FontAwesome stuff work. - // It is also required to stop Ionic overriding the aria-label attribute. - this.newElement = document.createElement('ion-icon'); - if (this.name.startsWith('fa-')) { - this.newElement.classList.add('icon'); - this.newElement.classList.add('fa'); - this.newElement.classList.add(this.name); - if (this.isTrueProperty(this.fixedWidth)) { - this.newElement.classList.add('fa-fw'); - } - if (this.color) { - this.newElement.classList.add('fa-' + this.color); - } - } else { - const mode = this.config.get('iconMode'); - this.newElement.classList.add('icon'); - this.newElement.classList.add('icon-' + mode); - this.newElement.classList.add('ion-' + mode + '-' + this.name); - } - - !this.ariaLabel && this.newElement.setAttribute('aria-hidden', 'true'); - !this.ariaLabel && this.newElement.setAttribute('role', 'presentation'); - this.ariaLabel && this.newElement.setAttribute('aria-label', this.ariaLabel); - this.ariaLabel && this.newElement.setAttribute('title', this.ariaLabel); - - const attrs = this.element.attributes; - for (let i = attrs.length - 1; i >= 0; i--) { - if (attrs[i].name == 'class') { - // We don't want to override the classes we already added. Add them one by one. - if (attrs[i].value) { - const classes = attrs[i].value.split(' '); - for (let j = 0; j < classes.length; j++) { - if (classes[j]) { - this.newElement.classList.add(classes[j]); - } - } - } - - } else { - this.newElement.setAttribute(attrs[i].name, attrs[i].value); - } - } - - if (this.slash) { - this.newElement.classList.add('icon-slash'); - } - - if (this.flipRtl) { - this.newElement.classList.add('core-icon-dir-flip'); - } - - oldElement.parentElement.replaceChild(this.newElement, oldElement); - } - - /** - * Check if the value is true or on. - * - * @param val value to be checked. - * @return If has a value equivalent to true. - */ - isTrueProperty(val: any): boolean { - if (typeof val === 'string') { - val = val.toLowerCase().trim(); - - return (val === 'true' || val === 'on' || val === ''); - } - - return !!val; - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - if (this.newElement) { - this.newElement.remove(); - } - } -} diff --git a/src/components/iframe/core-iframe.html b/src/components/iframe/core-iframe.html deleted file mode 100644 index 0ba267b63..000000000 --- a/src/components/iframe/core-iframe.html +++ /dev/null @@ -1,8 +0,0 @@ -
                        - - - - - - -
                        \ No newline at end of file diff --git a/src/components/iframe/iframe.scss b/src/components/iframe/iframe.scss deleted file mode 100644 index d99e54aae..000000000 --- a/src/components/iframe/iframe.scss +++ /dev/null @@ -1,31 +0,0 @@ -ion-app.app-root core-iframe { - - > div { - max-width: 100%; - max-height: 100%; - } - iframe { - border: 0; - display: block; - max-width: 100%; - background-color: $gray-light; - } - - .core-loading-container { - position: absolute; - @include position(0, 0, 0, 0); - display: table; - height: 100%; - width: 100%; - z-index: 1; - margin: 0; - padding: 0; - clear: both; - - .core-loading-spinner { - display: table-cell; - text-align: center; - vertical-align: middle; - } - } -} diff --git a/src/components/iframe/iframe.ts b/src/components/iframe/iframe.ts deleted file mode 100644 index 0bc3cabde..000000000 --- a/src/components/iframe/iframe.ts +++ /dev/null @@ -1,137 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { - Component, Input, Output, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, Optional -} from '@angular/core'; -import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; -import { NavController } from 'ionic-angular'; -import { CoreFile } from '@providers/file'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreIframeUtilsProvider } from '@providers/utils/iframe'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreUrl } from '@singletons/url'; -import { CoreApp } from '@providers/app'; -import { WKWebViewCookiesWindow } from 'cordova-plugin-wkwebview-cookies'; - -@Component({ - selector: 'core-iframe', - templateUrl: 'core-iframe.html' -}) -export class CoreIframeComponent implements OnChanges { - - @ViewChild('iframe') iframe: ElementRef; - @Input() src: string; - @Input() iframeWidth: string; - @Input() iframeHeight: string; - @Input() allowFullscreen: boolean | string; - @Output() loaded?: EventEmitter = new EventEmitter(); - loading: boolean; - safeUrl: SafeResourceUrl; - - protected logger; - protected IFRAME_TIMEOUT = 15000; - protected initialized = false; - - constructor(logger: CoreLoggerProvider, - protected iframeUtils: CoreIframeUtilsProvider, - protected domUtils: CoreDomUtilsProvider, - protected sanitizer: DomSanitizer, - protected navCtrl: NavController, - protected urlUtils: CoreUrlUtilsProvider, - protected utils: CoreUtilsProvider, - @Optional() protected svComponent: CoreSplitViewComponent, - ) { - - this.logger = logger.getInstance('CoreIframe'); - this.loaded = new EventEmitter(); - } - - /** - * Init the data. - */ - protected init(): void { - if (this.initialized) { - return; - } - - this.initialized = true; - - const iframe: HTMLIFrameElement = this.iframe && this.iframe.nativeElement; - - this.iframeWidth = this.domUtils.formatPixelsSize(this.iframeWidth) || '100%'; - this.iframeHeight = this.domUtils.formatPixelsSize(this.iframeHeight) || '100%'; - this.allowFullscreen = this.utils.isTrueOrOne(this.allowFullscreen); - - // Show loading only with external URLs. - this.loading = !this.src || !this.urlUtils.isLocalFileUrl(this.src); - - const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - this.iframeUtils.treatFrame(iframe, false, navCtrl); - - iframe.addEventListener('load', () => { - this.loading = false; - this.loaded.emit(iframe); // Notify iframe was loaded. - }); - - iframe.addEventListener('error', () => { - this.loading = false; - this.domUtils.showErrorModal('core.errorloadingcontent', true); - }); - - if (this.loading) { - setTimeout(() => { - this.loading = false; - }, this.IFRAME_TIMEOUT); - } - } - - /** - * Detect changes on input properties. - */ - async ngOnChanges(changes: {[name: string]: SimpleChange }): Promise { - if (changes.src) { - const url = this.urlUtils.getYoutubeEmbedUrl(changes.src.currentValue) || changes.src.currentValue; - - if (CoreApp.instance.isIOS() && url && !this.urlUtils.isLocalFileUrl(url)) { - // Save a "fake" cookie for the iframe's domain to fix a bug in WKWebView. - try { - const win = window; - const urlParts = CoreUrl.parse(url); - - if (urlParts.domain) { - await win.WKWebViewCookies.setCookie({ - name: 'MoodleAppCookieForWKWebView', - value: '1', - domain: urlParts.domain, - }); - } - } catch (err) { - // Ignore errors. - this.logger.error('Error setting cookie', err); - } - } - - this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(CoreFile.instance.convertFileSrc(url)); - - // Now that the URL has been set, initialize the iframe. Wait for the iframe to the added to the DOM. - setTimeout(() => { - this.init(); - }); - } - } -} diff --git a/src/components/infinite-loading/core-infinite-loading.html b/src/components/infinite-loading/core-infinite-loading.html deleted file mode 100644 index 8775a5b60..000000000 --- a/src/components/infinite-loading/core-infinite-loading.html +++ /dev/null @@ -1,29 +0,0 @@ - -
                        - - -
                        -
                        - - - - - - -
                        - - -
                        -
                        - -
                        - -
                        \ No newline at end of file diff --git a/src/components/infinite-loading/infinite-loading.ts b/src/components/infinite-loading/infinite-loading.ts deleted file mode 100644 index 1e636fcd2..000000000 --- a/src/components/infinite-loading/infinite-loading.ts +++ /dev/null @@ -1,134 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, EventEmitter, OnChanges, SimpleChange, Optional, ViewChild, ElementRef } from '@angular/core'; -import { InfiniteScroll, Content } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; - -/** - * Component to show a infinite loading trigger and spinner while more data is being loaded. - * - * Usage: - * - */ -@Component({ - selector: 'core-infinite-loading', - templateUrl: 'core-infinite-loading.html', -}) -export class CoreInfiniteLoadingComponent implements OnChanges { - @Input() enabled: boolean; - @Input() error = false; - @Input() position = 'bottom'; - @Output() action: EventEmitter<() => void>; // Will emit an event when triggered. - - @ViewChild('topbutton') topButton: ElementRef; - @ViewChild('infinitescroll') infiniteEl: ElementRef; - @ViewChild('bottombutton') bottomButton: ElementRef; - @ViewChild('spinnercontainer') spinnerContainer: ElementRef; - - loadingMore = false; // Hide button and avoid loading more. - - protected infiniteScroll: InfiniteScroll; - - constructor(@Optional() private content: Content, private domUtils: CoreDomUtilsProvider) { - this.action = new EventEmitter(); - } - - /** - * Detect changes on input properties. - * - * @param changes Changes. - */ - ngOnChanges(changes: {[name: string]: SimpleChange}): void { - if (changes.enabled && this.enabled && this.position == 'bottom') { - // Infinite scroll enabled. If the list doesn't fill the full height, infinite scroll isn't triggered automatically. - // Send a fake scroll event to make infinite scroll check if it should load more items. - setTimeout(() => { - const event: any = new Event('scroll'); - this.content.ionScroll.emit(event); - }, 400); - } - } - - /** - * Load More items calling the action provided. - * - * @param infiniteScroll Infinite scroll object only if triggered from the scroll. - */ - loadMore(infiniteScroll?: InfiniteScroll): void { - if (this.loadingMore) { - return; - } - - if (infiniteScroll) { - this.infiniteScroll = infiniteScroll; - } - this.loadingMore = true; - - this.action.emit(this.complete.bind(this)); - } - - /** - * Complete loading. - */ - complete(): void { - if (this.position == 'top') { - // Wait a bit before allowing loading more, otherwise it could be re-triggered automatically when it shouldn't. - setTimeout(this.completeLoadMore.bind(this), 400); - } else { - this.completeLoadMore(); - } - } - - /** - * Complete loading. - */ - protected completeLoadMore(): void { - this.loadingMore = false; - this.infiniteScroll && this.infiniteScroll.complete(); - this.infiniteScroll = undefined; - - // More items loaded. If the list doesn't fill the full height, infinite scroll isn't triggered automatically. - // Send a fake scroll event to make infinite scroll check if it should load more items. - setTimeout(() => { - const event: any = new Event('scroll'); - this.content.ionScroll.emit(event); - }); - } - - /** - * Get the height of the element. - * - * @return Height. - */ - getHeight(): number { - return this.getElementHeight(this.topButton) + this.getElementHeight(this.infiniteEl) + - this.getElementHeight(this.bottomButton) + this.getElementHeight(this.spinnerContainer); - } - - /** - * Get the height of an element. - * - * @param element Element ref. - * @return Height. - */ - protected getElementHeight(element: ElementRef): number { - if (element && element.nativeElement) { - return this.domUtils.getElementHeight(element.nativeElement, true, true, true); - } - - return 0; - } - -} diff --git a/src/components/input-errors/core-input-errors.html b/src/components/input-errors/core-input-errors.html deleted file mode 100644 index 62ab3df6a..000000000 --- a/src/components/input-errors/core-input-errors.html +++ /dev/null @@ -1,16 +0,0 @@ - \ No newline at end of file diff --git a/src/components/input-errors/input-errors.scss b/src/components/input-errors/input-errors.scss deleted file mode 100644 index a3712fe5e..000000000 --- a/src/components/input-errors/input-errors.scss +++ /dev/null @@ -1,16 +0,0 @@ -ion-app.app-root core-input-errors { - width: 100%; - - .core-input-error-container { - .core-input-error { - padding: 4px; - color: $red; - font-size: 12px; - display: none; - - &:first-child { - display: block; - } - } - } -} diff --git a/src/components/input-errors/input-errors.ts b/src/components/input-errors/input-errors.ts deleted file mode 100644 index cf12d126d..000000000 --- a/src/components/input-errors/input-errors.ts +++ /dev/null @@ -1,92 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, OnChanges, SimpleChange } from '@angular/core'; -import { FormControl } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Component to show errors if an input isn't valid. - * - * @description - * The purpose of this component is to make easier and consistent the validation of forms. - * - * It should be applied next to the input element (ion-input, ion-select, ...). In case of ion-checkbox, it should be in another - * item, placing it in the same item as the checkbox will cause problems. - * - * Please notice that the inputs need to have a FormControl to make it work. That FormControl needs to be passed to this component. - * - * If this component is placed in the same ion-item as a ion-label or ion-input, then it should have the attribute "item-content", - * otherwise Ionic will remove it. - * - * Example usage: - * - * - * {{ 'core.login.username' | translate }} - * - * - * - */ -@Component({ - selector: 'core-input-errors', - templateUrl: 'core-input-errors.html' -}) -export class CoreInputErrorsComponent implements OnInit, OnChanges { - @Input('control') formControl?: FormControl; - @Input() errorMessages?: any; - @Input() errorText?: string; // Set other non automatic errors. - errorKeys: any[]; - - constructor(private translate: TranslateService) { } - - /** - * Component is being initialized. - */ - ngOnInit(): void { - if (this.formControl) { - this.initErrorMessages(); - - this.errorKeys = Object.keys(this.errorMessages); - } - } - - /** - * Initialize some common errors if they aren't set. - */ - protected initErrorMessages(): void { - this.errorMessages = this.errorMessages || {}; - - this.errorMessages.required = this.errorMessages.required || this.translate.instant('core.required'); - this.errorMessages.email = this.errorMessages.email || this.translate.instant('core.login.invalidemail'); - this.errorMessages.date = this.errorMessages.date || this.translate.instant('core.login.invaliddate'); - this.errorMessages.datetime = this.errorMessages.datetime || this.translate.instant('core.login.invaliddate'); - this.errorMessages.datetimelocal = this.errorMessages.datetimelocal || this.translate.instant('core.login.invaliddate'); - this.errorMessages.time = this.errorMessages.time || this.translate.instant('core.login.invalidtime'); - this.errorMessages.url = this.errorMessages.url || this.translate.instant('core.login.invalidurl'); - - // Set empty values by default, the default error messages will be built in the template when needed. - this.errorMessages.max = this.errorMessages.max || ''; - this.errorMessages.min = this.errorMessages.min || ''; - } - - /** - * Component being changed. - */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if (changes.errorText) { - this.errorText = changes.errorText.currentValue; - } - } - -} diff --git a/src/components/ion-tabs/core-ion-tabs.html b/src/components/ion-tabs/core-ion-tabs.html deleted file mode 100644 index 64b7f007e..000000000 --- a/src/components/ion-tabs/core-ion-tabs.html +++ /dev/null @@ -1,13 +0,0 @@ -
                        - -
                        -
                        - - - -
                        -
                        -
                        - -
                        -
                        diff --git a/src/components/ion-tabs/ion-tab.ts b/src/components/ion-tabs/ion-tab.ts deleted file mode 100644 index 313f55db7..000000000 --- a/src/components/ion-tabs/ion-tab.ts +++ /dev/null @@ -1,93 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { - Component, Optional, ElementRef, NgZone, Renderer, ComponentFactoryResolver, ChangeDetectorRef, ErrorHandler, OnInit, - OnDestroy, ViewEncapsulation -} from '@angular/core'; -import { Tab, App, Config, Platform, GestureController, DeepLinker, DomController, NavOptions } from 'ionic-angular'; -import { TransitionController } from 'ionic-angular/transitions/transition-controller'; -import { CoreIonTabsComponent } from './ion-tabs'; -import { TransitionDoneFn } from 'ionic-angular/navigation/nav-util'; - -/** - * Equivalent to ion-tab, but to be used inside core-ion-tabs. - */ -@Component({ - selector: 'core-ion-tab', - template: '
                        ', - host: { - '[attr.id]': '_tabId', - '[attr.aria-labelledby]': '_btnId', - 'role': 'tabpanel' - }, - encapsulation: ViewEncapsulation.None, -}) -export class CoreIonTabComponent extends Tab implements OnInit, OnDestroy { - - constructor(parent: CoreIonTabsComponent, app: App, config: Config, plt: Platform, elementRef: ElementRef, zone: NgZone, - renderer: Renderer, cfr: ComponentFactoryResolver, _cd: ChangeDetectorRef, gestureCtrl: GestureController, - transCtrl: TransitionController, @Optional() linker: DeepLinker, _dom: DomController, errHandler: ErrorHandler) { - super(parent, app, config, plt, elementRef, zone, renderer, cfr, _cd, gestureCtrl, transCtrl, linker, _dom, errHandler); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - super.ngOnInit(); - - this.parent.add(this, true); - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - super.ngOnDestroy(); - - this.parent.remove(this); - } - - /** - * Push a page to the navigation stack. this similar to parent NavController, but perform some check to make - * sure one page won't open multiple time. - */ - push(page: any, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise { - if (this.isTransitioning()) { - // Try again later, the app is transitioning, this also happen when the page is first loaded. - return new Promise((resolve, reject): void => { - setTimeout(() => { - - return this.push(page, params, opts, done).then(resolve, reject); - }, 250); - }); - } else { - const previousViews = this.getViews(); - if (previousViews.length > 0) { - const previousView = previousViews[previousViews.length - 1]; - const previousParam = previousView.getNavParams().data; - - // If the view we pushing in have same page's name and identical params, then we won't do anything. - // This is Ionic issue when user clicking too fast on old device or slow internet connection. - if (previousView.name === page && JSON.stringify(previousParam) === JSON.stringify(params)) { - - return Promise.resolve(); - } - } - - return super.push(page, params, opts, done); - } - } -} diff --git a/src/components/ion-tabs/ion-tabs.scss b/src/components/ion-tabs/ion-tabs.scss deleted file mode 100644 index a2dec8b91..000000000 --- a/src/components/ion-tabs/ion-tabs.scss +++ /dev/null @@ -1,135 +0,0 @@ -$core-sidetab-size: 60px !default; - -ion-app.app-root core-ion-tabs { - .tabbar { - z-index: 101; // For some reason, the regular z-index isn't enough with our tabs, use a higher one. - - .core-ion-tabs-loading { - height: 100%; - width: 100%; - display: flex; - align-items: center; - justify-content: center; - - .core-ion-tabs-loading-spinner { - .spinner circle, .spinner line { - stroke: $white; - } - } - } - } - - .tab-badge.badge { - background-color: $ion-tabs-badge-color; - } - - &[tabsplacement="bottom"] { - .ion-page > ion-content > .scroll-content { - margin-bottom: $navbar-md-height; - } - .tabshidden .ion-page > ion-content > .scroll-content { - margin-bottom: 0; - } - } - - &[tabsplacement="side"] { - .tabbar { - @include float(start); - width: $core-sidetab-size; - height: 100%; - flex-direction: column; - @include border-end(0.55px, solid, rgba(0, 0, 0, 0.3)); - .tab-button { - width: 100%; - .tab-badge.badge { - @include position(calc(50% - 30px), 2px, null, null); - } - } - } - - .tabbar[hidden] + .tabcontent, &.tabbar-hidden .tabcontent { - width: 100%; - core-ion-tab { - @include position(0, 0, 0, 0); - } - } - - .tabcontent { - width: calc(100% - #{($core-sidetab-size)}); - position: absolute; - @include position(0, 0, 0, 0); - core-ion-tab { - @include position(0, 0, 0, $core-sidetab-size); - position: relative; - } - } - - ion-content:not(.has-footer) > .scroll-content, - ion-content:not(.has-footer) > .fixed-content { - margin-bottom: 0 !important; - } - } -} - -ion-app.app-root.ios core-ion-tabs .core-ion-tabs-loading { - min-height: $tabs-ios-tab-min-height; -} - -ion-app.app-root.md core-ion-tabs .core-ion-tabs-loading { - min-height: $tabs-md-tab-min-height; -} - -// Copy some styles from ion-tabs and ion-tab. -core-ion-tabs, core-ion-tab { - @include position(0, null, null, 0); - - position: absolute; - z-index: $z-index-page-container; - display: block; - - width: 100%; - height: 100%; - overflow: hidden; -} - -core-ion-tab { - display: none; -} - -core-ion-tab.show-tab { - display: block; -} - -@mixin core-ion-tabs-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding, $modal-max-width, $style-title: false) { - - core-ion-tab > .ion-page, - core-ion-tab > .ion-page > ion-header, - core-ion-tabs > .ion-page.tab-subpage > ion-header { - @include toolbar-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding); - - // If we should style the title elements in the toolbar - @if ($style-title) { - @include toolbar-title-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding); - } - } - - @media only screen and (max-width: $modal-max-width) { - .modal-wrapper > .ion-page > ion-header { - @include toolbar-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding); - - // If we should style the title elements in the toolbar - @if ($style-title) { - @include toolbar-title-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding); - } - } - } - -} - -ion-app.app-root.ios { - @include core-ion-tabs-statusbar-padding($toolbar-ios-height, $toolbar-ios-padding, $content-ios-padding, $cordova-ios-statusbar-padding, $cordova-ios-statusbar-padding-modal-max-width, true); -} - -ion-app.app-root.md { - @include core-ion-tabs-statusbar-padding($toolbar-md-height, $toolbar-md-padding, $content-md-padding, $cordova-md-statusbar-padding, $cordova-md-statusbar-padding-modal-max-width); -} diff --git a/src/components/ion-tabs/ion-tabs.ts b/src/components/ion-tabs/ion-tabs.ts deleted file mode 100644 index bd6c6afab..000000000 --- a/src/components/ion-tabs/ion-tabs.ts +++ /dev/null @@ -1,398 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Optional, ElementRef, Renderer, ViewEncapsulation, forwardRef, ViewChild, Input, - OnDestroy } from '@angular/core'; -import { - Tabs, Tab, NavController, ViewController, App, Config, Platform, DeepLinker, Keyboard, RootNode, NavOptions -} from 'ionic-angular'; -import { CoreIonTabComponent } from './ion-tab'; -import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils'; -import { CoreAppProvider } from '@providers/app'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { TranslateService } from '@ngx-translate/core'; - -/** - * Equivalent to ion-tabs. It has several improvements: - * - If a core-ion-tab is added or removed, it will be reflected in the tab bar in the right position. - * - It supports a loaded input to tell when are the tabs ready. - * - When the user clicks the tab again to go to root, a confirm modal is shown. - */ -@Component({ - selector: 'core-ion-tabs', - templateUrl: 'core-ion-tabs.html', - encapsulation: ViewEncapsulation.None, - providers: [{provide: RootNode, useExisting: forwardRef(() => CoreIonTabsComponent) }] -}) -export class CoreIonTabsComponent extends Tabs implements OnDestroy { - - /** - * Whether the tabs have been loaded. If defined, tabs won't be initialized until it's set to true. - */ - @Input() set loaded(val: boolean) { - this._loaded = this.utils.isTrueOrOne(val); - - if (this.viewInit && !this.initialized) { - // Use a setTimeout to make sure the tabs have been loaded. - setTimeout(() => { - this.initTabs(); - }); - } - } - - @Input() selectedDisabled: boolean; // Whether the initial tab selected can be a disabled tab. - - @ViewChild('originalTabs') originalTabsRef: ElementRef; - - _loaded: boolean; // Whether tabs have been loaded. - hidden = false; // Whether to show/hide tabs. - - /** - * List of tabs that haven't been initialized yet. This is required because IonTab calls add() on the constructor, - * but we need it to be called in OnInit to be able to determine the tab position. - */ - protected tabsNotInit: CoreIonTabComponent[] = []; - - protected tabsIds: string[] = []; // An array to keep the order of tab IDs when they're sorted. - protected tabsNotInitIds: string[] = []; // An array to keep the order of tab IDs for non-init tabs. - protected viewInit = false; // Whether the view has been initialized. - protected initialized = false; // Whether tabs have been initialized. - - protected firstSelectedTab: string; - protected unregisterBackButtonAction: any; - protected selectTabPromiseDefer: PromiseDefer; - - constructor(protected utils: CoreUtilsProvider, protected appProvider: CoreAppProvider, @Optional() parent: NavController, - @Optional() viewCtrl: ViewController, _app: App, config: Config, elementRef: ElementRef, _plt: Platform, - renderer: Renderer, _linker: DeepLinker, protected domUtils: CoreDomUtilsProvider, - protected translate: TranslateService, keyboard?: Keyboard) { - super(parent, viewCtrl, _app, config, elementRef, _plt, renderer, _linker, keyboard); - } - - /** - * View has been initialized. - */ - ngAfterViewInit(): void { - this.viewInit = true; - - super.ngAfterViewInit(); - - this.registerBackButtonAction(); - } - - /** - * Add a new tab if it isn't already in the list of tabs. - * - * @param tab The tab to add. - * @param isInit Whether the tab has been initialized. - * @return The tab ID. - */ - add(tab: CoreIonTabComponent, isInit?: boolean): string { - // Check if tab is already in the list of initialized tabs. - let position = this._tabs.indexOf(tab); - - if (position != -1) { - return this.tabsIds[position]; - } - - // Now check if the tab is in the not init list. - position = this.tabsNotInit.indexOf(tab); - if (position != -1) { - if (!isInit) { - return this.tabsNotInitIds[position]; - } - - // The tab wasn't initialized but now it is. Move it from one array to the other. - const tabId = this.tabsNotInitIds[position]; - this.tabsNotInit.splice(position, 1); - this.tabsNotInitIds.splice(position, 1); - - this._tabs.push(tab); - this.tabsIds.push(tabId); - - this.sortTabs(); - - return tabId; - } - - // Tab is new. In this case isInit should always be false, but check it just in case. - const id = this.id + '-' + (++this._ids); - - if (isInit) { - this._tabs.push(tab); - this.tabsIds.push(id); - - this.sortTabs(); - } else { - this.tabsNotInit.push(tab); - this.tabsNotInitIds.push(id); - } - - return id; - } - - /** - * Initialize the tabs. - * - * @return Promise resolved when done. - */ - initTabs(): Promise { - if (!this.initialized && (this._loaded || typeof this._loaded == 'undefined')) { - this.initialized = true; - - return super.initTabs().then(() => { - // Tabs initialized. Force select the tab if it's not enabled. - if (this.selectedDisabled && typeof this.selectedIndex != 'undefined') { - const tab = this.getByIndex(this.selectedIndex); - if (tab && !tab.enabled) { - this.select(tab); - } - } - - this.firstSelectedTab = this._selectHistory[0] || null; - }).finally(() => { - // If there was a select promise pending to be resolved, do it now. - if (this.selectTabPromiseDefer) { - this.selectTabPromiseDefer.resolve(); - delete this.selectTabPromiseDefer; - } - }); - } else { - // Tabs not loaded yet. Set the tab bar position so the tab bar is shown, it'll have a spinner. - this.setTabbarPosition(-1, 0); - - return Promise.resolve(); - } - } - - /** - * Register back button action. - */ - protected registerBackButtonAction(): void { - this.unregisterBackButtonAction = this.appProvider.registerBackButtonAction(() => { - let tab = this.previousTab(true); - - if (tab) { - const selectedTab = this.getSelected(); - - // It can happen when the previous is a phantom tab. - if (tab.id == selectedTab.id) { - tab = this.previousTab(true); - } - - if (tab) { - // Remove curent and previous tabs from history. - this._selectHistory = this._selectHistory.filter((tabId) => { - return selectedTab.id != tabId && tab.id != tabId; - }); - - this.select(tab); - - return true; - } - } else { - const selected = this.getSelected(); - if (selected && this.firstSelectedTab && selected.id != this.firstSelectedTab) { - // All history is gone but we are not in the first selected tab. - this._selectHistory = []; - - tab = this._tabs.find((t) => { return t.id === this.firstSelectedTab; }); - if (tab && tab.enabled) { - this.select(tab); - - return true; - } - } - } - - return false; - }, 250); - } - - /** - * Remove a tab from the list of tabs. - * - * @param tab The tab to remove. - */ - remove(tab: CoreIonTabComponent): void { - // First search in the list of initialized tabs. - let index = this._tabs.indexOf(tab); - - if (index != -1) { - this._tabs.splice(index, 1); - this.tabsIds.splice(index, 1); - } else { - // Not found, search in the list of non-init tabs. - index = this.tabsNotInit.indexOf(tab); - - if (index != -1) { - this.tabsNotInit.splice(index, 1); - this.tabsNotInitIds.splice(index, 1); - } - } - } - - /** - * Sort the tabs, keeping the same order as in the original list. - */ - sortTabs(): void { - if (this.originalTabsRef) { - const newTabs = [], - newTabsIds = [], - originalTabsEl = this.originalTabsRef.nativeElement; - - this._tabs.forEach((tab, index) => { - const originalIndex = Array.prototype.indexOf.call(originalTabsEl.children, tab.getNativeElement()); - if (originalIndex != -1) { - newTabs[originalIndex] = tab; - newTabsIds[originalIndex] = this.tabsIds[index]; - } - }); - - // Remove undefined values. It can happen if the view has some tabs that were destroyed but weren't removed yet. - this._tabs = newTabs.filter((tab) => { - return typeof tab != 'undefined'; - }); - this.tabsIds = newTabsIds.filter((id) => { - return typeof id != 'undefined'; - }); - } - } - - /** - * Select a tab. - * - * @param tabOrIndex Index, or the Tab instance, of the tab to select. - * @param Nav options. - * @param fromUrl Whether to load from a URL. - * @param manualClick Whether the user manually clicked the tab. - * @return Promise resolved when selected. - */ - select(tabOrIndex: number | Tab, opts: NavOptions = {}, fromUrl?: boolean, manualClick?: boolean): Promise { - - if (this.initialized) { - // Tabs have been initialized, select the tab. - if (manualClick) { - // If we'll go to the root of the current tab, ask the user to confirm first. - const tab = typeof tabOrIndex == 'number' ? this.getByIndex(tabOrIndex) : tabOrIndex; - - return this.confirmGoToRoot(tab).then(() => { - return super.select(tabOrIndex, opts, fromUrl); - }, () => { - // User cancelled. - }); - } - - return super.select(tabOrIndex, opts, fromUrl); - } else { - // Tabs not initialized yet. Mark it as "selectedIndex" input so it's treated when the tabs are initialized. - if (typeof tabOrIndex == 'number') { - this.selectedIndex = tabOrIndex; - } else { - this.selectedIndex = this.getIndex(tabOrIndex); - } - - // Don't resolve the Promise until the tab is really selected (tabs are initialized). - this.selectTabPromiseDefer = this.selectTabPromiseDefer || this.utils.promiseDefer(); - - return this.selectTabPromiseDefer.promise; - } - } - - /** - * Select a tab by Index. First it will reset the status of the tab. - * - * @param index Index of the tab. - * @return Promise resolved when selected. - */ - selectTabRootByIndex(index: number): Promise { - if (this.initialized) { - const tab = this.getByIndex(index); - if (tab) { - return this.confirmGoToRoot(tab).then(() => { - // User confirmed, go to root. - return tab.goToRoot({animate: tab.isSelected, updateUrl: true, isNavRoot: true}).then(() => { - // Tab not previously selected. Select it after going to root. - if (!tab.isSelected) { - return this.select(tab, {animate: false, updateUrl: true, isNavRoot: true}); - } - }); - }, () => { - // User cancelled. - }); - } - - // Not found. - return Promise.reject(null); - } else { - // Tabs not initialized yet. Mark it as "selectedIndex" input so it's treated when the tabs are initialized. - this.selectedIndex = index; - - // Don't resolve the Promise until the tab is really selected (tabs are initialized). - this.selectTabPromiseDefer = this.selectTabPromiseDefer || this.utils.promiseDefer(); - - return this.selectTabPromiseDefer.promise; - } - } - - /** - * Change tabs visibility to show/hide them from the view. - * - * @param visible If show or hide the tabs. - */ - changeVisibility(visible: boolean): void { - if (this.hidden == visible) { - // Change needed. - this.hidden = !visible; - - setTimeout(() => { - this.viewCtrl.getContent().resize(); - }); - } - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - // Unregister the custom back button action for this page - this.unregisterBackButtonAction && this.unregisterBackButtonAction(); - } - - /** - * Confirm if the user wants to go to the root of the current tab. - * - * @param tab Tab to go to root. - * @return Promise resolved when confirmed. - */ - confirmGoToRoot(tab: Tab): Promise { - if (!tab || !tab.isSelected || (tab.getActive() && tab.getActive().isFirst())) { - // Tab not selected or is already at root, no need to confirm. - return Promise.resolve(); - } else { - if (tab.tabTitle) { - return this.domUtils.showConfirm(this.translate.instant('core.confirmgotabroot', {name: tab.tabTitle})); - } else { - return this.domUtils.showConfirm(this.translate.instant('core.confirmgotabrootdefault')); - } - } - } - - /** - * @inheritdoc - */ - setTabbarHidden(tabbarHidden: boolean): void { - // Don't hide the tab bar, we'll do it via CSS if needed. - } -} diff --git a/src/components/loading/core-loading.html b/src/components/loading/core-loading.html deleted file mode 100644 index c149e9bf1..000000000 --- a/src/components/loading/core-loading.html +++ /dev/null @@ -1,10 +0,0 @@ -
                        - - -

                        {{message}}

                        -
                        -
                        -
                        - - -
                        \ No newline at end of file diff --git a/src/components/loading/loading.scss b/src/components/loading/loading.scss deleted file mode 100644 index 6b5bbaa21..000000000 --- a/src/components/loading/loading.scss +++ /dev/null @@ -1,67 +0,0 @@ -ion-app.app-root { - core-loading { - @include core-transition(height, 200ms); - - .core-loading-container { - width: 100%; - text-align: center; - padding-top: 10px; - clear: both; - @include darkmode() { - color: $core-dark-text-color; - } - } - - .core-loading-content { - display: inline; - padding-bottom: 1px; /* This makes height be real */ - } - - &.core-loading-noheight .core-loading-content { - height: auto; - } - - &.safe-area-page { - padding-left: 0 !important; - padding-right: 0 !important; - - > .core-loading-content > *:not[padding], - > .core-loading-content-loading > *:not[padding] { - @include safe-area-padding-horizontal(0px, 0px); - } - } - } - - .scroll-content > core-loading, - ion-content > .scroll-content > core-loading, - core-tab core-loading, - .core-loading-center { - position: static !important; - } - - .scroll-content > core-loading, - ion-content > .scroll-content > core-loading, - core-tab core-loading, - .core-loading-center, - core-loading.core-loading-loaded { - position: relative; - - > .core-loading-container { - position: absolute; - @include position(0, 0, 0, 0); - display: table; - height: 100%; - width: 100%; - z-index: 1; - margin: 0; - padding: 0; - clear: both; - - .core-loading-spinner { - display: table-cell; - text-align: center; - vertical-align: middle; - } - } - } -} diff --git a/src/components/loading/loading.ts b/src/components/loading/loading.ts deleted file mode 100644 index 2685a5b48..000000000 --- a/src/components/loading/loading.ts +++ /dev/null @@ -1,106 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, OnChanges, SimpleChange, ViewChild, ElementRef } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { coreShowHideAnimation } from '@classes/animations'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Component to show a loading spinner and message while data is being loaded. - * - * It will show a spinner with a message and hide all the content until 'hideUntil' variable is set to a truthy value (!!hideUntil). - * If 'message' isn't set, default message "Loading" is shown. - * 'message' attribute accepts hardcoded strings, variables, filters, etc. E.g. [message]="'core.loading' | translate". - * - * Usage: - * - * - * - * - * IMPORTANT: Due to how ng-content works in Angular, the content of core-loading will be executed as soon as your view - * is loaded, even if the content hidden. So if you have the following code: - * - * - * The component "my-component" will be initialized immediately, even if dataLoaded is false, but it will be hidden. If you want - * your component to be initialized only if dataLoaded is true, then you should use ngIf: - * - */ -@Component({ - selector: 'core-loading', - templateUrl: 'core-loading.html', - animations: [coreShowHideAnimation] -}) -export class CoreLoadingComponent implements OnInit, OnChanges { - @Input() hideUntil: any; // Determine when should the contents be shown. - @Input() message?: string; // Message to show while loading. - @ViewChild('content') content: ElementRef; - - protected uniqueId: string; - protected element: HTMLElement; // Current element. - - constructor(private translate: TranslateService, element: ElementRef, private eventsProvider: CoreEventsProvider, - utils: CoreUtilsProvider) { - this.element = element.nativeElement; - - // Calculate the unique ID. - this.uniqueId = 'core-loading-content-' + utils.getUniqueId('CoreLoadingComponent'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - if (!this.message) { - // Default loading message. - this.message = this.translate.instant('core.loading'); - } - - // Add class if loaded on init. - if (!!this.hideUntil) { - this.element.classList.add('core-loading-loaded'); - this.content.nativeElement.classList.add('core-loading-content'); - } - } - - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if (changes.hideUntil) { - if (!!this.hideUntil) { - setTimeout(() => { - // Content is loaded so, center the spinner on the content itself. - this.element.classList.add('core-loading-loaded'); - setTimeout(() => { - // Change CSS to force calculate height. - this.content.nativeElement.classList.add('core-loading-content'); - this.content.nativeElement.classList.remove('core-loading-content-loading'); - }, 500); - }); - } else { - this.element.classList.remove('core-loading-loaded'); - this.content.nativeElement.classList.remove('core-loading-content'); - this.content.nativeElement.classList.add('core-loading-content-loading'); - } - - // Trigger the event after a timeout since the elements inside ngIf haven't been added to DOM yet. - setTimeout(() => { - this.eventsProvider.trigger(CoreEventsProvider.CORE_LOADING_CHANGED, { - loaded: !!this.hideUntil, - uniqueId: this.uniqueId - }); - }); - } - } - -} diff --git a/src/components/local-file/core-local-file.html b/src/components/local-file/core-local-file.html deleted file mode 100644 index ffec36361..000000000 --- a/src/components/local-file/core-local-file.html +++ /dev/null @@ -1,28 +0,0 @@ -
                        - - {{fileExtension}} - - -

                        {{fileName}}

                        - -

                        {{ size }}

                        -

                        {{ timemodified }}

                        - - - - -
                        - - - - - -
                        -
                        -
                        diff --git a/src/components/local-file/local-file.ts b/src/components/local-file/local-file.ts deleted file mode 100644 index eba54e903..000000000 --- a/src/components/local-file/local-file.ts +++ /dev/null @@ -1,208 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, OnInit, EventEmitter, ViewChild, ElementRef } from '@angular/core'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreFileProvider } from '@providers/file'; -import { CoreFileHelper } from '@providers/file-helper'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreTimeUtilsProvider } from '@providers/utils/time'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Component to handle a local file. Only files inside the app folder can be managed. - * - * Shows the file name, icon (depending on extension), size and time modified. - * Also, if managing is enabled it will also show buttons to rename and delete the file. - */ -@Component({ - selector: 'core-local-file', - templateUrl: 'core-local-file.html' -}) -export class CoreLocalFileComponent implements OnInit { - @Input() file: any; // A fileEntry retrieved using CoreFileProvider.getFile or similar. - @Input() manage?: boolean | string; // Whether the user can manage the file (edit and delete). - @Input() overrideClick?: boolean | string; // Whether the default item click should be overridden. - @Output() onDelete?: EventEmitter; // Will notify when the file is deleted. - @Output() onRename?: EventEmitter; // Will notify when the file is renamed. Receives the FileEntry as the param. - @Output() onClick?: EventEmitter; // Will notify when the file is clicked. Only if overrideClick is true. - - @ViewChild('nameForm') formElement: ElementRef; - - fileName: string; - fileIcon: string; - fileExtension: string; - size: string; - timemodified: string; - newFileName = ''; - editMode: boolean; - relativePath: string; - - constructor(protected mimeUtils: CoreMimetypeUtilsProvider, - protected utils: CoreUtilsProvider, - protected textUtils: CoreTextUtilsProvider, - protected fileProvider: CoreFileProvider, - protected domUtils: CoreDomUtilsProvider, - protected timeUtils: CoreTimeUtilsProvider, - protected sitesProvider: CoreSitesProvider, - protected eventsProvider: CoreEventsProvider) { - this.onDelete = new EventEmitter(); - this.onRename = new EventEmitter(); - this.onClick = new EventEmitter(); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.manage = this.utils.isTrueOrOne(this.manage); - - this.loadFileBasicData(); - - // Get the size and timemodified. - this.fileProvider.getMetadata(this.file).then((metadata) => { - if (metadata.size >= 0) { - this.size = this.textUtils.bytesToSize(metadata.size, 2); - } - - this.timemodified = this.timeUtils.userDate(metadata.modificationTime.getTime(), 'core.strftimedatetimeshort'); - }); - } - - /** - * Load the basic data for the file. - */ - protected loadFileBasicData(): void { - this.fileName = this.file.name; - this.fileIcon = this.mimeUtils.getFileIcon(this.file.name); - this.fileExtension = this.mimeUtils.getFileExtension(this.file.name); - - // Let's calculate the relative path for the file. - this.relativePath = this.fileProvider.removeBasePath(this.file.toURL()); - if (!this.relativePath) { - // Didn't find basePath, use fullPath but if the user tries to manage the file it'll probably fail. - this.relativePath = this.file.fullPath; - } - } - - /** - * File clicked. - * - * @param e Click event. - */ - async fileClicked(e: Event): Promise { - if (this.editMode) { - return; - } - - e.preventDefault(); - e.stopPropagation(); - - if (this.utils.isTrueOrOne(this.overrideClick) && this.onClick.observers.length) { - this.onClick.emit(); - } else { - if (!CoreFileHelper.instance.isOpenableInApp(this.file)) { - try { - await CoreFileHelper.instance.showConfirmOpenUnsupportedFile(); - } catch (error) { - return; // Cancelled, stop. - } - } - - this.utils.openFile(this.file.toURL()); - } - } - - /** - * Activate the edit mode. - * - * @param e Click event. - */ - activateEdit(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - this.editMode = true; - this.newFileName = this.file.name; - } - - /** - * Rename the file. - * - * @param newName New name. - * @param e Click event. - */ - changeName(newName: string, e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - if (newName == this.file.name) { - // Name hasn't changed, stop. - this.editMode = false; - this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId()); - - return; - } - - const modal = this.domUtils.showModalLoading(), - fileAndDir = this.fileProvider.getFileAndDirectoryFromPath(this.relativePath), - newPath = this.textUtils.concatenatePaths(fileAndDir.directory, newName); - - // Check if there's a file with this name. - this.fileProvider.getFile(newPath).then(() => { - // There's a file with this name, show error and stop. - this.domUtils.showErrorModal('core.errorfileexistssamename', true); - }).catch(() => { - // File doesn't exist, move it. - return this.fileProvider.moveFile(this.relativePath, newPath).then((fileEntry) => { - - this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId()); - - this.editMode = false; - this.file = fileEntry; - this.loadFileBasicData(); - this.onRename.emit({ file: this.file }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.errorrenamefile', true); - }); - }).finally(() => { - modal.dismiss(); - }); - } - - /** - * Delete the file. - * - * @param e Click event. - */ - deleteFile(e: Event): void { - e.preventDefault(); - e.stopPropagation(); - - // Ask confirmation. - this.domUtils.showDeleteConfirm('core.confirmdeletefile').then(() => { - const modal = this.domUtils.showModalLoading('core.deleting', true); - - return this.fileProvider.removeFile(this.relativePath).then(() => { - this.onDelete.emit(); - }).finally(() => { - modal.dismiss(); - }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.errordeletefile', true); - }); - } -} diff --git a/src/components/mark-required/core-mark-required.html b/src/components/mark-required/core-mark-required.html deleted file mode 100644 index faaea1df8..000000000 --- a/src/components/mark-required/core-mark-required.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/components/mark-required/mark-required.scss b/src/components/mark-required/mark-required.scss deleted file mode 100644 index 3b49f99ee..000000000 --- a/src/components/mark-required/mark-required.scss +++ /dev/null @@ -1,11 +0,0 @@ -ion-app.app-root .core-input-required-asterisk, ion-app.app-root .icon.core-input-required-asterisk { - color: $red !important; - font-size: 8px; - @include padding(null, null, null, 4px); - line-height: 100%; - vertical-align: top; - - @include darkmode() { - color: $red-light !important; - } -} diff --git a/src/components/mark-required/mark-required.ts b/src/components/mark-required/mark-required.ts deleted file mode 100644 index 5a8da5d1a..000000000 --- a/src/components/mark-required/mark-required.ts +++ /dev/null @@ -1,70 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, AfterViewInit, ElementRef } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUtilsProvider } from '@providers/utils/utils'; - -/** - * Directive to add a red asterisk for required input fields. - * - * @description - * For forms with required and not required fields, it is recommended to use this directive to mark the required ones. - * - * This directive should be applied in the label. Example: - * - * {{ 'core.login.username' | translate }} - */ -@Component({ - selector: '[core-mark-required]', - templateUrl: 'core-mark-required.html' -}) -export class CoreMarkRequiredComponent implements OnInit, AfterViewInit { - @Input('core-mark-required') coreMarkRequired: boolean | string = true; - protected element: HTMLElement; - requiredLabel: string; - - constructor(element: ElementRef, private translate: TranslateService, private textUtils: CoreTextUtilsProvider, - private utils: CoreUtilsProvider) { - this.element = element.nativeElement; - this.requiredLabel = this.translate.instant('core.required'); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.coreMarkRequired = this.utils.isTrueOrOne(this.coreMarkRequired); - } - - /** - * Called after the view is initialized. - */ - ngAfterViewInit(): void { - if (this.coreMarkRequired) { - // Add the "required" to the aria-label. - const ariaLabel = this.element.getAttribute('aria-label') || this.textUtils.cleanTags(this.element.innerHTML, true); - if (ariaLabel) { - this.element.setAttribute('aria-label', ariaLabel + ' ' + this.requiredLabel); - } - } else { - // Remove the "required" from the aria-label. - const ariaLabel = this.element.getAttribute('aria-label'); - if (ariaLabel) { - this.element.setAttribute('aria-label', ariaLabel.replace(' ' + this.requiredLabel, '')); - } - } - } -} diff --git a/src/components/navbar-buttons/navbar-buttons.scss b/src/components/navbar-buttons/navbar-buttons.scss deleted file mode 100644 index 7cd383039..000000000 --- a/src/components/navbar-buttons/navbar-buttons.scss +++ /dev/null @@ -1,4 +0,0 @@ -ion-app.app-root core-navbar-buttons, -ion-app.app-root .core-navbar-button-hidden { - display: none !important; -} diff --git a/src/components/navbar-buttons/navbar-buttons.ts b/src/components/navbar-buttons/navbar-buttons.ts deleted file mode 100644 index 8c45b8c14..000000000 --- a/src/components/navbar-buttons/navbar-buttons.ts +++ /dev/null @@ -1,269 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit, OnDestroy, ContentChildren, ElementRef, QueryList } from '@angular/core'; -import { Button } from 'ionic-angular'; -import { CoreLoggerProvider } from '@providers/logger'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContextMenuComponent } from '../context-menu/context-menu'; - -/** - * Component to add buttons to the app's header without having to place them inside the header itself. This is meant for - * pages that are loaded inside a sub ion-nav, so they don't have a header. - * - * If this component indicates a position (start/end), the buttons will only be added if the header has some buttons in that - * position. If no start/end is specified, then the buttons will be added to the first found in the header. - * - * If this component has a "prepend" attribute, the buttons will be added before other existing buttons in the header. - * - * You can use the [hidden] input to hide all the inner buttons if a certain condition is met. - * - * IMPORTANT: Do not use *ngIf in the buttons inside this component, it can cause problems. Please use [hidden] instead. - * - * Example usage: - * - * - * - * - */ -@Component({ - selector: 'core-navbar-buttons', - template: '' -}) -export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { - - protected BUTTON_HIDDEN_CLASS = 'core-navbar-button-hidden'; - - // If the hidden input is true, hide all buttons. - @Input('hidden') set hidden(value: boolean) { - this._hidden = value; - this.showHideAllElements(); - } - - // Get all the ion-buttons inside this directive and apply the role bar-button. - @ContentChildren(Button) set buttons(buttons: QueryList -

                        {{ 'core.answered' | translate }}

                        -

                        {{ 'core.login.recaptchaexpired' | translate }}

                        - - - -
                        - - {{ 'core.errorloadingcontent' | translate }} -
                        \ No newline at end of file diff --git a/src/components/recaptcha/core-recaptchamodal.html b/src/components/recaptcha/core-recaptchamodal.html deleted file mode 100644 index 00e25f989..000000000 --- a/src/components/recaptcha/core-recaptchamodal.html +++ /dev/null @@ -1,14 +0,0 @@ - - - {{ 'core.login.security_question' | translate }} - - - - - - - - - \ No newline at end of file diff --git a/src/components/recaptcha/recaptcha.ts b/src/components/recaptcha/recaptcha.ts deleted file mode 100644 index 8710c761a..000000000 --- a/src/components/recaptcha/recaptcha.ts +++ /dev/null @@ -1,72 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input } from '@angular/core'; -import { ModalController } from 'ionic-angular'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreLangProvider } from '@providers/lang'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreRecaptchaModalComponent } from './recaptchamodal'; - -/** - * Component that allows answering a recaptcha. - */ -@Component({ - selector: 'core-recaptcha', - templateUrl: 'core-recaptcha.html' -}) -export class CoreRecaptchaComponent { - @Input() model: any; // The model where to store the recaptcha response. - @Input() publicKey: string; // The site public key. - @Input() modelValueName = 'recaptcharesponse'; // Name of the model property where to store the response. - @Input() siteUrl?: string; // The site URL. If not defined, current site. - - expired = false; - - protected lang: string; - - constructor(private sitesProvider: CoreSitesProvider, langProvider: CoreLangProvider, - private textUtils: CoreTextUtilsProvider, private modalCtrl: ModalController) { - - // Get the current language of the app. - langProvider.getCurrentLanguage().then((lang) => { - this.lang = lang; - }); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.siteUrl = this.siteUrl || this.sitesProvider.getCurrentSite().getURL(); - } - - /** - * Open the recaptcha modal. - */ - answerRecaptcha(): void { - // Set the iframe src. We use an iframe because reCaptcha V2 doesn't work with file:// protocol. - const src = this.textUtils.concatenatePaths(this.siteUrl, 'webservice/recaptcha.php?lang=' + this.lang); - - // Modal to answer the recaptcha. - // This is because the size of the recaptcha is dynamic, so it could cause problems if it was displayed inline. - const modal = this.modalCtrl.create(CoreRecaptchaModalComponent, { src: src }, - { cssClass: 'core-modal-fullscreen'}); - modal.onDidDismiss((data) => { - this.expired = data.expired; - this.model[this.modelValueName] = data.value; - }); - modal.present(); - } -} diff --git a/src/components/recaptcha/recaptchamodal.ts b/src/components/recaptcha/recaptchamodal.ts deleted file mode 100644 index 2cff04c18..000000000 --- a/src/components/recaptcha/recaptchamodal.ts +++ /dev/null @@ -1,119 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnDestroy } from '@angular/core'; -import { ViewController, NavParams } from 'ionic-angular'; - -/** - * Component to display a the recaptcha in a modal. - */ -@Component({ - selector: 'core-recaptcha-modal', - templateUrl: 'core-recaptchamodal.html' -}) -export class CoreRecaptchaModalComponent implements OnDestroy { - expired = false; - value = ''; - src: string; - - protected messageListenerFunction: (event: MessageEvent) => Promise; - - constructor(protected viewCtrl: ViewController, params: NavParams) { - this.src = params.get('src'); - - // Listen for messages from the iframe. - this.messageListenerFunction = this.onIframeMessage.bind(this); - window.addEventListener('message', this.messageListenerFunction); - } - - /** - * Close modal. - */ - closeModal(): void { - this.viewCtrl.dismiss({ - expired: this.expired, - value: this.value - }); - } - - /** - * The iframe with the recaptcha was loaded. - * - * @param iframe Iframe element. - */ - loaded(iframe: HTMLIFrameElement): void { - // Search the iframe content. - const contentWindow = iframe && iframe.contentWindow; - - if (contentWindow) { - try { - // Set the callbacks we're interested in. - contentWindow['recaptchacallback'] = this.onRecaptchaCallback.bind(this); - contentWindow['recaptchaexpiredcallback'] = this.onRecaptchaExpiredCallback.bind(this); - } catch (error) { - // Cannot access the window. - } - } - } - - /** - * Treat an iframe message event. - * - * @param event Event. - * @return Promise resolved when done. - */ - protected async onIframeMessage(event: MessageEvent): Promise { - if (!event.data || event.data.environment != 'moodleapp' || event.data.context != 'recaptcha') { - return; - } - - switch (event.data.action) { - case 'callback': - this.onRecaptchaCallback(event.data.value); - break; - case 'expired': - this.onRecaptchaExpiredCallback(); - break; - - default: - break; - } - } - - /** - * Recapcha callback called. - * - * @param value Value received. - */ - protected onRecaptchaCallback(value: any): void { - this.expired = false; - this.value = value; - this.closeModal(); - } - - /** - * Recapcha expired callback called. - */ - protected onRecaptchaExpiredCallback(): void { - this.expired = true; - this.value = ''; - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - window.removeEventListener('message', this.messageListenerFunction); - } -} diff --git a/src/components/send-message-form/core-send-message-form.html b/src/components/send-message-form/core-send-message-form.html deleted file mode 100644 index bed8756ce..000000000 --- a/src/components/send-message-form/core-send-message-form.html +++ /dev/null @@ -1,8 +0,0 @@ -
                        - - - - -
                        diff --git a/src/components/send-message-form/send-message-form.scss b/src/components/send-message-form/send-message-form.scss deleted file mode 100644 index cec94f20e..000000000 --- a/src/components/send-message-form/send-message-form.scss +++ /dev/null @@ -1,42 +0,0 @@ -$core-send-message-input-background: $gray; -$core-send-message-input-color: $black; - -ion-app.app-root core-send-message-form { - background: $white; - - form { - position: relative; - display: flex; - align-items: center; - width: 100%; - flex-shrink: 1; - width: 100%; - } - - .core-send-message-input { - @include appearance(none); - display: block; - width: 100%; - min-height: 28px; - border: 0; - font-family: inherit; - align-self: self-start; - background: $core-send-message-input-background; - color: $core-send-message-input-color; - border-radius: 5px; - margin: 0 5px; - } - - .core-send-message-button { - @include margin(0); - @include padding(0); - display: none; - min-height: 0; - align-self: self-end; - } - @include darkmode() { - ion-icon.icon { - color: $gray-darker; - } - } -} \ No newline at end of file diff --git a/src/components/send-message-form/send-message-form.ts b/src/components/send-message-form/send-message-form.ts deleted file mode 100644 index ee2e59116..000000000 --- a/src/components/send-message-form/send-message-form.ts +++ /dev/null @@ -1,127 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, EventEmitter, OnInit, ViewChild, ElementRef } from '@angular/core'; -import { CoreAppProvider } from '@providers/app'; -import { CoreConfigProvider } from '@providers/config'; -import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreConstants } from '@core/constants'; - -/** - * Component to display a "send message form". - * - * @description - * This component will display a standalone send message form in order to have a better UX. - * - * Example usage: - * - */ -@Component({ - selector: 'core-send-message-form', - templateUrl: 'core-send-message-form.html' -}) -export class CoreSendMessageFormComponent implements OnInit { - @Input() message: string; // Input text. - @Input() placeholder = ''; // Placeholder for the input area. - @Input() showKeyboard = false; // If keyboard is shown or not. - @Input() sendDisabled = false; // If send is disabled. - @Output() onSubmit: EventEmitter; // Send data when submitting the message form. - @Output() onResize: EventEmitter; // Emit when resizing the textarea. - - @ViewChild('messageForm') formElement: ElementRef; - - protected sendOnEnter: boolean; - - constructor(protected utils: CoreUtilsProvider, - protected textUtils: CoreTextUtilsProvider, - configProvider: CoreConfigProvider, - protected eventsProvider: CoreEventsProvider, - protected sitesProvider: CoreSitesProvider, - protected appProvider: CoreAppProvider, - protected domUtils: CoreDomUtilsProvider) { - - this.onSubmit = new EventEmitter(); - this.onResize = new EventEmitter(); - - configProvider.get(CoreConstants.SETTINGS_SEND_ON_ENTER, !this.appProvider.isMobile()).then((sendOnEnter) => { - this.sendOnEnter = !!sendOnEnter; - }); - - eventsProvider.on(CoreEventsProvider.SEND_ON_ENTER_CHANGED, (newValue) => { - this.sendOnEnter = newValue; - }, sitesProvider.getCurrentSiteId()); - } - - ngOnInit(): void { - this.showKeyboard = this.utils.isTrueOrOne(this.showKeyboard); - } - - /** - * Form submitted. - * - * @param $event Mouse event. - */ - submitForm($event: Event): void { - $event.preventDefault(); - $event.stopPropagation(); - - let value = this.message.trim(); - - if (!value) { - // Silent error. - return; - } - - this.message = ''; // Reset the form. - - this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId()); - - value = this.textUtils.replaceNewLines(value, '
                        '); - this.onSubmit.emit(value); - } - - /** - * Textarea resized. - */ - textareaResized(): void { - this.onResize.emit(); - } - - /** - * Enter key clicked. - * - * @param e Event. - * @param other The name of the other key that was clicked, undefined if no other key. - */ - enterClicked(e: Event, other: string): void { - if (this.sendDisabled) { - return; - } - - if (this.sendOnEnter && !other) { - // Enter clicked, send the message. - this.submitForm(e); - } else if (!this.sendOnEnter && !this.appProvider.isMobile()) { - if ((this.appProvider.isMac() && other == 'meta') || (!this.appProvider.isMac() && other == 'control')) { - // Cmd+Enter or Ctrl+Enter, send message. - this.submitForm(e); - } - } - } -} diff --git a/src/components/show-password/core-show-password.html b/src/components/show-password/core-show-password.html deleted file mode 100644 index ba281d0ed..000000000 --- a/src/components/show-password/core-show-password.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/components/show-password/show-password.scss b/src/components/show-password/show-password.scss deleted file mode 100644 index f930ef34e..000000000 --- a/src/components/show-password/show-password.scss +++ /dev/null @@ -1,38 +0,0 @@ -ion-app.app-root core-show-password { - padding: 0px; - width: 100%; - position: relative; - - ion-input input.text-input { - @include padding(null, 47px, null, null); - } - - .button[icon-only] { - background: transparent; - padding: 0 ($content-padding / 2); - position: absolute; - @include position(null, 0, $content-padding / 2, null); - margin-top: 0; - margin-bottom: 0; - } - - .core-ioninput-password { - padding-top: 0; - padding-bottom: 0; - } -} - -ion-app.app-root.md { - .item-label-stacked core-show-password .button[icon-only] { - bottom: 0; - } -} - -ion-app.app-root.ios { - .item-label-stacked core-show-password .button[icon-only] { - bottom: -5px; - } - core-show-password .button[icon-only] { - bottom: 0; - } -} diff --git a/src/components/show-password/show-password.ts b/src/components/show-password/show-password.ts deleted file mode 100644 index 3449268b0..000000000 --- a/src/components/show-password/show-password.ts +++ /dev/null @@ -1,127 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, OnInit, AfterViewInit, Input, ElementRef } from '@angular/core'; -import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreApp } from '@providers/app'; - -/** - * Component to allow showing and hiding a password. The affected input MUST have a name to identify it. - * - * @description - * This directive needs to surround the input with the password. - * - * You need to supply the name of the input. - * - * Example: - * - * - * - * - */ -@Component({ - selector: 'core-show-password', - templateUrl: 'core-show-password.html' -}) -export class CoreShowPasswordComponent implements OnInit, AfterViewInit { - @Input() name: string; // Name of the input affected. - @Input() initialShown?: boolean | string; // Whether the password should be shown at start. - - shown: boolean; // Whether the password is shown. - label: string; // Label for the button to show/hide. - iconName: string; // Name of the icon of the button to show/hide. - selector = ''; // Selector to identify the input. - - protected input: HTMLInputElement; // Input affected. - protected element: HTMLElement; // Current element. - - constructor( - element: ElementRef, - private utils: CoreUtilsProvider, - private domUtils: CoreDomUtilsProvider - ) { - this.element = element.nativeElement; - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - this.shown = this.utils.isTrueOrOne(this.initialShown); - this.selector = 'input[name="' + this.name + '"]'; - this.setData(); - } - - /** - * View has been initialized. - */ - ngAfterViewInit(): void { - this.searchInput(); - } - - /** - * Search the input to show/hide. - */ - protected searchInput(): void { - // Search the input. - this.input = this.element.querySelector(this.selector); - - if (this.input) { - // Input found. Set the right type. - this.input.type = this.shown ? 'text' : 'password'; - - // By default, don't autocapitalize and autocorrect. - if (!this.input.getAttribute('autocorrect')) { - this.input.setAttribute('autocorrect', 'off'); - } - if (!this.input.getAttribute('autocapitalize')) { - this.input.setAttribute('autocapitalize', 'none'); - } - } - } - - /** - * Set label, icon name and input type. - */ - protected setData(): void { - this.label = this.shown ? 'core.hide' : 'core.show'; - this.iconName = this.shown ? 'eye-off' : 'eye'; - if (this.input) { - this.input.type = this.shown ? 'text' : 'password'; - } - } - - /** - * Toggle show/hide password. - * - * @param event The mouse event. - */ - toggle(event: Event): void { - event.preventDefault(); - event.stopPropagation(); - - const isFocused = document.activeElement === this.input; - - this.shown = !this.shown; - this.setData(); - - if (isFocused && CoreApp.instance.isAndroid()) { - // In Android, the keyboard is closed when the input type changes. Focus it again. - setTimeout(() => { - this.domUtils.focusElement(this.input); - }, 400); - } - } -} diff --git a/src/components/site-picker/core-site-picker.html b/src/components/site-picker/core-site-picker.html deleted file mode 100644 index 9cae3ec3a..000000000 --- a/src/components/site-picker/core-site-picker.html +++ /dev/null @@ -1,6 +0,0 @@ - - {{ 'core.site' | translate }} - - {{ site.fullNameAndSiteName }} - - diff --git a/src/components/site-picker/site-picker.ts b/src/components/site-picker/site-picker.ts deleted file mode 100644 index 60bfd17e5..000000000 --- a/src/components/site-picker/site-picker.ts +++ /dev/null @@ -1,74 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreFilterProvider } from '@core/filter/providers/filter'; - -/** - * Component to display a site selector. It will display a select with the list of sites. If the selected site changes, - * an output will be emitted with the site ID. - * - * Example usage: - * - */ -@Component({ - selector: 'core-site-picker', - templateUrl: 'core-site-picker.html' -}) -export class CoreSitePickerComponent implements OnInit { - @Input() initialSite?: string; // Initial site. If not provided, current site. - @Output() siteSelected: EventEmitter; // Emit an event when a site is selected. Sends the siteId as parameter. - - selectedSite: string; - sites: any[]; - - constructor(private translate: TranslateService, - private sitesProvider: CoreSitesProvider, - private filterProvider: CoreFilterProvider) { - this.siteSelected = new EventEmitter(); - } - - ngOnInit(): void { - this.selectedSite = this.initialSite || this.sitesProvider.getCurrentSiteId(); - - // Load the sites. - this.sitesProvider.getSites().then((sites) => { - const promises = []; - - sites.forEach((site: any) => { - // Format the site name. - promises.push(this.filterProvider.formatText(site.siteName, {clean: true, singleLine: true, filter: false}, [], - site.id).catch(() => { - return site.siteName; - }).then((siteName) => { - site.fullNameAndSiteName = this.translate.instant('core.fullnameandsitename', - { fullname: site.fullName, sitename: siteName }); - })); - }); - - if (!this.selectedSite && sites.length) { - // There is no current site, select the first one. - this.selectedSite = sites[0].id; - this.siteSelected.emit(this.selectedSite); - } - - return Promise.all(promises).then(() => { - this.sites = sites; - }); - }); - } - -} diff --git a/src/components/split-view/core-split-view.html b/src/components/split-view/core-split-view.html deleted file mode 100644 index 1d2d88bcd..000000000 --- a/src/components/split-view/core-split-view.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/components/split-view/placeholder/core-placeholder.html b/src/components/split-view/placeholder/core-placeholder.html deleted file mode 100644 index 83991ff49..000000000 --- a/src/components/split-view/placeholder/core-placeholder.html +++ /dev/null @@ -1,9 +0,0 @@ - - -   - - - - - - diff --git a/src/components/split-view/placeholder/placeholder.module.ts b/src/components/split-view/placeholder/placeholder.module.ts deleted file mode 100644 index cb6b7f0c9..000000000 --- a/src/components/split-view/placeholder/placeholder.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code based on https://github.com/martinpritchardelevate/ionic-split-pane-demo - -import { NgModule } from '@angular/core'; -import { IonicPageModule } from 'ionic-angular'; -import { CoreSplitViewPlaceholderPage } from './placeholder'; -import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '../../components.module'; - -@NgModule({ - declarations: [ - CoreSplitViewPlaceholderPage, - ], - imports: [ - CoreComponentsModule, - IonicPageModule.forChild(CoreSplitViewPlaceholderPage), - TranslateModule.forChild() - ], - exports: [ - CoreSplitViewPlaceholderPage - ] -}) -export class CorePlaceholderPageModule { } diff --git a/src/components/split-view/placeholder/placeholder.scss b/src/components/split-view/placeholder/placeholder.scss deleted file mode 100644 index b854d8617..000000000 --- a/src/components/split-view/placeholder/placeholder.scss +++ /dev/null @@ -1,3 +0,0 @@ -core-placeholder { - -} diff --git a/src/components/split-view/placeholder/placeholder.ts b/src/components/split-view/placeholder/placeholder.ts deleted file mode 100644 index 3152506d4..000000000 --- a/src/components/split-view/placeholder/placeholder.ts +++ /dev/null @@ -1,31 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code based on https://github.com/martinpritchardelevate/ionic-split-pane-demo - -import { Component } from '@angular/core'; -import { IonicPage } from 'ionic-angular'; - -@IonicPage({ segment: 'core-placeholder' }) -@Component({ - selector: 'core-placeholder', - templateUrl: 'core-placeholder.html', -}) -export class CoreSplitViewPlaceholderPage { - - constructor() { - // Nothing to do. - } - -} diff --git a/src/components/split-view/split-view.scss b/src/components/split-view/split-view.scss deleted file mode 100644 index 355e1ef4e..000000000 --- a/src/components/split-view/split-view.scss +++ /dev/null @@ -1,42 +0,0 @@ -ion-app.app-root core-split-view { - ion-menu.split-pane-side { - display: block; - - .menu-inner { - @include position(0, 0, 0, 0); - -webkit-transform: initial; - transform: initial; - width: 100%; - } - } - - .split-pane-main { - display: none; - } - - .split-pane-visible { - .split-pane-main { - display: block; - @include core-split-area-end(); - } - - .split-pane-side { - @include core-split-area-start(); - } - - .split-pane-side .core-split-item-selected { - background-color: $gray-lighter; - @include core-selected-item($core-splitview-selected); - - @include darkmode() { - background-color: $black; - } - } - } - ion-header { - display: none; - } - ion-content.statusbar-padding:first-child .scroll-content { - padding-top: 0 !important; - } -} diff --git a/src/components/split-view/split-view.ts b/src/components/split-view/split-view.ts deleted file mode 100644 index 58d605b64..000000000 --- a/src/components/split-view/split-view.ts +++ /dev/null @@ -1,308 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code based on https://github.com/martinpritchardelevate/ionic-split-pane-demo - -import { Component, ViewChild, Input, ElementRef, OnInit, Optional, OnDestroy } from '@angular/core'; -import { NavController, Nav, ViewController, Platform, Menu } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; -import { Subscription } from 'rxjs'; - -/** - * Directive to create a split view layout. - * - * @description - * To init/change the right pane contents (content pane), inject this component in the master page. - * @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; - * Then use the push function to load. - * - * Accepts the following params: - * - * @param when When the split-pane should be shown. Can be a CSS media query expression, or a shortcut - * expression. Can also be a boolean expression. Check split-pane component documentation for more information. - * - * Example: - * - * - * - * - */ -@Component({ - selector: 'core-split-view', - templateUrl: 'core-split-view.html' -}) -export class CoreSplitViewComponent implements OnInit, OnDestroy { - - @ViewChild('detailNav') detailNav: Nav; - @ViewChild('menu') menu: Menu; - @Input() when?: string | boolean = 'md'; - - protected VIEW_EVENTS = ['willEnter', 'didEnter', 'willLeave', 'willLeave']; - - protected isEnabled; - protected masterPageName = ''; - protected masterPageIndex = 0; - protected loadDetailPage: any = false; - protected element: HTMLElement; // Current element. - protected masterCanLeaveOverridden = false; - protected originalMasterCanLeave: Function; - protected ignoreSplitChanged = false; - protected audioCaptureSubscription: Subscription; - protected languageChangedSubscription: Subscription; - protected pushOngoing: boolean; - protected viewEventsSubscriptions: Subscription[] = []; - - // Empty placeholder for the 'detail' page. - detailPage: any = null; - side: string; - - constructor(@Optional() private masterNav: NavController, element: ElementRef, fileUploaderProvider: CoreFileUploaderProvider, - platform: Platform, translate: TranslateService) { - this.element = element.nativeElement; - - this.audioCaptureSubscription = fileUploaderProvider.onAudioCapture.subscribe((starting) => { - this.ignoreSplitChanged = starting; - }); - - // Change the side when the language changes. - this.languageChangedSubscription = translate.onLangChange.subscribe((event: any) => { - setTimeout(() => { - this.side = platform.isRTL ? 'right' : 'left'; - this.menu.setElementAttribute('side', this.side); - }); - }); - } - - /** - * Component being initialized. - */ - ngOnInit(): void { - // Get the master page name and set an empty page as a placeholder. - this.masterPageName = this.masterNav.getActive().component.name; - this.masterPageIndex = this.masterNav.indexOf(this.masterNav.getActive()); - this.emptyDetails(); - - this.handleViewEvents(); - } - - /** - * Get the details NavController. If split view is not enabled, it will return the master nav. - * - * @return Details NavController. - */ - getDetailsNav(): NavController { - if (this.isEnabled) { - return this.detailNav; - } else { - return this.masterNav; - } - } - - /** - * Get the master NavController. - * - * @return Master NavController. - */ - getMasterNav(): NavController { - return this.masterNav; - } - - /** - * Handle ionViewCanLeave functions in details page. By default, this function isn't captured by Ionic when - * clicking the back button, it only uses the one in the master page. - */ - handleCanLeave(): void { - // Listen for the didEnter event on the details nav to detect everytime a page is loaded. - this.viewEventsSubscriptions.push(this.detailNav.viewDidEnter.subscribe((detailsViewController: ViewController) => { - if (!this.isOn()) { - return; - } - - const masterViewController = this.masterNav.getActive(); - - if (this.masterCanLeaveOverridden) { - // We've overridden the can leave of the master page for a previous details page. Restore it. - masterViewController.instance.ionViewCanLeave = this.originalMasterCanLeave; - this.originalMasterCanLeave = undefined; - this.masterCanLeaveOverridden = false; - } - - if (detailsViewController && detailsViewController.instance && detailsViewController.instance.ionViewCanLeave) { - // The details page defines a canLeave function. Check if the master page also defines one. - if (masterViewController.instance.ionViewCanLeave) { - // Master page also defines a canLeave function, store it because it will be overridden. - this.originalMasterCanLeave = masterViewController.instance.ionViewCanLeave; - } - - // Override the master canLeave function so it also calls the details canLeave. - this.masterCanLeaveOverridden = true; - - masterViewController.instance.ionViewCanLeave = (): Promise => { - // Always return a Promise. - return Promise.resolve().then(() => { - if (this.originalMasterCanLeave) { - // First call the master canLeave. - const result = this.originalMasterCanLeave.apply(masterViewController.instance); - if (typeof result == 'boolean' && !result) { - // User cannot leave, return a rejected promise so the details canLeave isn't executed. - return Promise.reject(null); - } else { - return result; - } - } - }).then(() => { - // User can leave the master page. Check if he can also leave the details page. - return detailsViewController.instance.ionViewCanLeave(); - }); - }; - } - })); - } - - /** - * Handle Ionic Views lifecycle events in the details page. - */ - handleViewEvents(): void { - // Handle affected view events except ionViewCanLeave, propagating them to the details view. - const masterActiveView = this.masterNav.getActive(); - - for (const i in this.VIEW_EVENTS) { - const viewEvent = this.VIEW_EVENTS[i]; - - this.viewEventsSubscriptions.push(masterActiveView[viewEvent].subscribe(() => { - if (!this.isOn()) { - return; - } - - const activeView = this.detailNav.getActive(); - activeView && activeView[`_${viewEvent}`](); - })); - } - - this.handleCanLeave(); - } - - /** - * Check if both panels are shown. It depends on screen width. - * - * @return If split view is enabled. - */ - isOn(): boolean { - return !!this.isEnabled; - } - - /** - * Push a page to the navigation stack. It will decide where to load it depending on the size of the screen. - * - * @param page The component class or deeplink name you want to push onto the navigation stack. - * @param params Any NavParams you want to pass along to the next view. - * @param retrying Whether it's retrying. - */ - push(page: any, params?: any, retrying?: boolean): void { - // Check there's no ongoing push. - if (!this.pushOngoing) { - if (typeof this.isEnabled == 'undefined' && !retrying) { - // Hasn't calculated if it's enabled yet. Wait a bit and try again. - setTimeout(() => { - this.push(page, params, true); - }, 200); - } else { - this.pushOngoing = true; - let promise; - - if (this.isEnabled) { - promise = this.detailNav.setRoot(page, params); - } else { - this.loadDetailPage = { - component: page, - data: params - }; - promise = this.masterNav.push(page, params); - } - promise.finally(() => { - this.pushOngoing = false; - }); - } - } - } - - /** - * Set the details panel to default info. - */ - emptyDetails(): void { - this.loadDetailPage = false; - this.detailNav.setRoot('CoreSplitViewPlaceholderPage'); - } - - /** - * Splitpanel visibility has changed. - * - * @param isOn If it fits both panels at the same time. - */ - onSplitPaneChanged(isOn: boolean): void { - if (this.ignoreSplitChanged) { - return; - } - - this.isEnabled = isOn; - if (this.masterNav && this.detailNav) { - (isOn) ? this.activateSplitView() : this.deactivateSplitView(); - } - } - - /** - * Enable the split view, show both panels and do some magical navigation. - */ - activateSplitView(): void { - const currentView = this.masterNav.getActive(), - currentPageName = currentView.component.name; - if (this.masterNav.getPrevious() && this.masterNav.getPrevious().component.name == this.masterPageName) { - if (currentPageName != this.masterPageName) { - // CurrentView is a 'Detail' page remove it from the 'master' nav stack. - this.masterNav.pop(); - - // And add it to the 'detail' nav stack. - this.detailNav.setRoot(currentView.component, currentView.data); - } else if (this.loadDetailPage) { - // MasterPage is shown, load the last detail page if found. - this.detailNav.setRoot(this.loadDetailPage.component, this.loadDetailPage.data); - } - this.loadDetailPage = false; - } - } - - /** - * Disabled the split view, show only one panel and do some magical navigation. - */ - deactivateSplitView(): void { - const detailView = this.detailNav.getActive(), - currentPageName = detailView.component.name; - if (currentPageName != 'CoreSplitViewPlaceholderPage') { - // Current detail view is a 'Detail' page so, not the placeholder page, push it on 'master' nav stack. - this.masterNav.insert(this.masterPageIndex + 1, detailView.component, detailView.data); - } - } - - /** - * Component being destroyed. - */ - ngOnDestroy(): void { - this.audioCaptureSubscription.unsubscribe(); - this.languageChangedSubscription.unsubscribe(); - for (const i in this.viewEventsSubscriptions) { - this.viewEventsSubscriptions[i].unsubscribe(); - } - } -} diff --git a/src/components/style/style.ts b/src/components/style/style.ts deleted file mode 100644 index a554bceca..000000000 --- a/src/components/style/style.ts +++ /dev/null @@ -1,75 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ElementRef, Input, OnChanges, SimpleChange } from '@angular/core'; - -/** - * Component to add a